feat(auth): implement platform selection screen with API URL input

Add platform selection UI with PepChat and Revolt options
Implement clickable platform cards that set the API URL
Create custom styled text field for API URL input
Add confirm button with localized string resource
Fix vertical alignment of placeholder text in text field
This commit is contained in:
AbronStudio 2025-07-26 12:18:06 +03:30
parent f49b25b4e3
commit 80db00ee2a
6 changed files with 69 additions and 58 deletions

View File

@ -1,5 +1,40 @@
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173"> <code_scheme name="Project" version="173">
<JavaCodeStyleSettings>
<option name="IMPORT_LAYOUT_TABLE">
<value>
<package name="" withSubpackages="true" static="false" module="true" />
<package name="android" withSubpackages="true" static="true" />
<package name="androidx" withSubpackages="true" static="true" />
<package name="com" withSubpackages="true" static="true" />
<package name="junit" withSubpackages="true" static="true" />
<package name="net" withSubpackages="true" static="true" />
<package name="org" withSubpackages="true" static="true" />
<package name="java" withSubpackages="true" static="true" />
<package name="javax" withSubpackages="true" static="true" />
<package name="" withSubpackages="true" static="true" />
<emptyLine />
<package name="android" withSubpackages="true" static="false" />
<emptyLine />
<package name="androidx" withSubpackages="true" static="false" />
<emptyLine />
<package name="com" withSubpackages="true" static="false" />
<emptyLine />
<package name="junit" withSubpackages="true" static="false" />
<emptyLine />
<package name="net" withSubpackages="true" static="false" />
<emptyLine />
<package name="org" withSubpackages="true" static="false" />
<emptyLine />
<package name="java" withSubpackages="true" static="false" />
<emptyLine />
<package name="javax" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="false" />
<emptyLine />
</value>
</option>
</JavaCodeStyleSettings>
<JetCodeStyleSettings> <JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings> </JetCodeStyleSettings>

View File

@ -96,6 +96,7 @@ import chat.revolt.screens.about.AboutScreen
import chat.revolt.screens.about.AttributionScreen import chat.revolt.screens.about.AttributionScreen
import chat.revolt.screens.chat.ChatRouterScreen import chat.revolt.screens.chat.ChatRouterScreen
import chat.revolt.screens.chat.views.channel.ChannelScreen import chat.revolt.screens.chat.views.channel.ChannelScreen
import chat.revolt.screens.chooseplatform.ChoosePlatformScreen
import chat.revolt.screens.create.CreateGroupScreen import chat.revolt.screens.create.CreateGroupScreen
import chat.revolt.screens.labs.LabsRootScreen import chat.revolt.screens.labs.LabsRootScreen
import chat.revolt.screens.login.LoginGreetingScreen import chat.revolt.screens.login.LoginGreetingScreen
@ -200,7 +201,7 @@ class MainActivityViewModel @Inject constructor(
Log.d("MainActivity", "We can reach Revolt, checking if we're logged in") Log.d("MainActivity", "We can reach Revolt, checking if we're logged in")
val token = kvStorage.get("sessionToken") val token = kvStorage.get("sessionToken")
?: return@launch startWithDestination("login/greeting") ?: return@launch startWithDestination("choose-platform")
val id = kvStorage.get("sessionId") ?: "" val id = kvStorage.get("sessionId") ?: ""
Log.d( Log.d(
@ -563,6 +564,7 @@ fun AppEntrypoint(
) )
} }
composable("choose-platform") { ChoosePlatformScreen(navController) }
composable("login/greeting") { LoginGreetingScreen(navController) } composable("login/greeting") { LoginGreetingScreen(navController) }
composable("login/login") { LoginScreen(navController) } composable("login/login") { LoginScreen(navController) }
composable("login/mfa/{mfaTicket}/{allowedAuthTypes}") { backStackEntry -> composable("login/mfa/{mfaTicket}/{allowedAuthTypes}") { backStackEntry ->

View File

@ -22,6 +22,9 @@ fun FormTextField(
type: KeyboardType = KeyboardType.Text, type: KeyboardType = KeyboardType.Text,
action: ImeAction = ImeAction.Done, action: ImeAction = ImeAction.Done,
supportingText: @Composable (() -> Unit)? = null, supportingText: @Composable (() -> Unit)? = null,
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
placeholder: @Composable (() -> Unit)? = null,
singleLine: Boolean = true, singleLine: Boolean = true,
enabled: Boolean = true enabled: Boolean = true
) { ) {
@ -29,10 +32,13 @@ fun FormTextField(
value = value, value = value,
onValueChange = onChange, onValueChange = onChange,
singleLine = singleLine, singleLine = singleLine,
placeholder = placeholder,
keyboardOptions = KeyboardOptions(keyboardType = type, imeAction = action), keyboardOptions = KeyboardOptions(keyboardType = type, imeAction = action),
visualTransformation = if (type == KeyboardType.Password) PasswordVisualTransformation() else VisualTransformation.None, visualTransformation = if (type == KeyboardType.Password) PasswordVisualTransformation() else VisualTransformation.None,
label = { Text(label) }, label = { Text(label) },
supportingText = supportingText, supportingText = supportingText,
leadingIcon = leadingIcon,
trailingIcon = trailingIcon,
enabled = enabled, enabled = enabled,
modifier = modifier modifier = modifier
) )

View File

@ -14,16 +14,13 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawingPadding import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ElevatedButton import androidx.compose.material3.ElevatedButton
import androidx.compose.material3.LocalContentColor import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -31,7 +28,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag import androidx.compose.ui.platform.testTag
@ -42,12 +38,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.navigation.NavController import androidx.navigation.NavController
import chat.revolt.BuildConfig
import chat.revolt.R import chat.revolt.R
import chat.revolt.api.REVOLT_MARKETING
import chat.revolt.composables.generic.AnyLink
import chat.revolt.composables.generic.Weblink
import com.chuckerteam.chucker.api.Chucker
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
@ -59,7 +50,7 @@ fun LoginGreetingScreen(navController: NavController) {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(vertical = 20.dp, horizontal = 0.dp) .padding(vertical = 20.dp, horizontal = 16.dp)
.safeDrawingPadding(), .safeDrawingPadding(),
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
@ -71,13 +62,11 @@ fun LoginGreetingScreen(navController: NavController) {
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
Image( Image(
painter = painterResource(id = R.drawable.revolt_logo_wide), painter = painterResource(id = R.drawable.onboarding_fly_image),
colorFilter = ColorFilter.tint(LocalContentColor.current),
contentDescription = "Revolt Logo", contentDescription = "Revolt Logo",
contentScale = ContentScale.Fit, contentScale = ContentScale.Fit,
modifier = Modifier modifier = Modifier
.height(55.dp) .padding(bottom = 16.dp)
.padding(bottom = 10.dp)
.combinedClickable( .combinedClickable(
interactionSource = remember(::MutableInteractionSource), interactionSource = remember(::MutableInteractionSource),
indication = null, indication = null,
@ -100,7 +89,14 @@ fun LoginGreetingScreen(navController: NavController) {
} }
) )
) )
}
Column(
modifier = Modifier
.fillMaxWidth(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text( Text(
text = stringResource(R.string.login_onboarding_heading), text = stringResource(R.string.login_onboarding_heading),
style = MaterialTheme.typography.displaySmall.copy( style = MaterialTheme.typography.displaySmall.copy(
@ -109,7 +105,7 @@ fun LoginGreetingScreen(navController: NavController) {
textAlign = TextAlign.Center textAlign = TextAlign.Center
), ),
modifier = Modifier modifier = Modifier
.padding(horizontal = 20.dp, vertical = 10.dp) .padding(bottom = 4.dp)
.fillMaxWidth() .fillMaxWidth()
) )
@ -124,17 +120,12 @@ fun LoginGreetingScreen(navController: NavController) {
fontWeight = FontWeight.Normal fontWeight = FontWeight.Normal
), ),
modifier = Modifier modifier = Modifier
.padding(horizontal = 20.dp, vertical = 10.dp) .padding(horizontal = 20.dp)
.fillMaxWidth() .fillMaxWidth()
) )
}
Column( Spacer(modifier = Modifier.height(32.dp))
modifier = Modifier
.width(200.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Button( Button(
onClick = { navController.navigate("login/login") }, onClick = { navController.navigate("login/login") },
modifier = Modifier modifier = Modifier
@ -177,33 +168,6 @@ fun LoginGreetingScreen(navController: NavController) {
} }
Spacer(modifier = Modifier.height(40.dp)) Spacer(modifier = Modifier.height(40.dp))
CompositionLocalProvider(
LocalTextStyle provides LocalTextStyle.current.copy(textAlign = TextAlign.Center)
) {
Weblink(
text = stringResource(R.string.terms_of_service),
url = "$REVOLT_MARKETING/terms"
)
Weblink(
text = stringResource(R.string.privacy_policy),
url = "$REVOLT_MARKETING/privacy"
)
Weblink(
text = stringResource(R.string.community_guidelines),
url = "$REVOLT_MARKETING/aup"
)
if (BuildConfig.DEBUG) {
AnyLink(
text = "Debug: Chucker",
action = {
Chucker.getLaunchIntent(context).apply {
context.startActivity(this)
}
}
)
}
}
} }
} }
} }

View File

@ -1,9 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp" android:width="72dp"
android:height="108dp" android:height="72dp"
android:viewportWidth="108" android:viewportWidth="72"
android:viewportHeight="108"> android:viewportHeight="72">
<path <path
android:pathData="M42.7,71.38C42.7,60.74 42.7,53.76 42.7,44.7L35.91,36.62H57.87C60.48,36.62 62.77,37.09 64.72,38.03C66.68,38.97 68.2,40.32 69.29,42.08C70.37,43.85 70.92,45.97 70.92,48.43C70.92,50.92 70.36,53.02 69.24,54.73C68.13,56.44 66.56,57.73 64.55,58.6C62.55,59.47 60.21,59.91 57.53,59.91H48.47V52.57H55.61C56.73,52.57 57.69,52.44 58.47,52.17C59.28,51.88 59.89,51.44 60.31,50.83C60.75,50.21 60.96,49.42 60.96,48.43C60.96,47.44 60.75,46.63 60.31,46C59.89,45.37 59.28,44.91 58.47,44.61C57.69,44.31 56.73,44.15 55.61,44.15H52.24V71.38H42.7ZM63.3,55.43L72.09,71.38H61.72L53.14,55.43H63.3Z" android:pathData="M32.888,21.721V31.138H26.569V21.721H32.888ZM51.649,21.721V31.138H45.33V21.721H51.649Z"
android:fillColor="#FF005C"/> android:fillColor="#662D91"/>
<path
android:pathData="M51.648,43.635H45.369V49.953H26.569V43.635H45.33V37.318H51.648V43.635ZM26.569,37.318V43.635H20.25V37.318H26.569ZM26.569,31.138H20.25V21.721H26.569V31.138ZM45.35,31.138H39.03V21.721H45.35V31.138Z"
android:fillColor="#FFDE17"/>
</vector> </vector>

View File

@ -7,6 +7,7 @@
<string name="cancel">Cancel</string> <string name="cancel">Cancel</string>
<string name="share">Share</string> <string name="share">Share</string>
<string name="lets_go">Let\'s go</string> <string name="lets_go">Let\'s go</string>
<string name="confirm">Confirm</string>
<string name="loading">Fetching some info, hang in there…</string> <string name="loading">Fetching some info, hang in there…</string>
<string name="rate_limit_toast">Hold your horses! You\'re doing that too often in a short amount of time.</string> <string name="rate_limit_toast">Hold your horses! You\'re doing that too often in a short amount of time.</string>