android: add verify and reset auth flows with deep link routing
This commit is contained in:
@@ -28,6 +28,14 @@
|
|||||||
android:host="chat.daemonlord.ru"
|
android:host="chat.daemonlord.ru"
|
||||||
android:pathPrefix="/join"
|
android:pathPrefix="/join"
|
||||||
android:scheme="https" />
|
android:scheme="https" />
|
||||||
|
<data
|
||||||
|
android:host="chat.daemonlord.ru"
|
||||||
|
android:pathPrefix="/verify-email"
|
||||||
|
android:scheme="https" />
|
||||||
|
<data
|
||||||
|
android:host="chat.daemonlord.ru"
|
||||||
|
android:pathPrefix="/reset-password"
|
||||||
|
android:scheme="https" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<service
|
<service
|
||||||
|
|||||||
@@ -20,12 +20,16 @@ import ru.daemonlord.messenger.ui.navigation.MessengerNavHost
|
|||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
private var pendingInviteToken by mutableStateOf<String?>(null)
|
private var pendingInviteToken by mutableStateOf<String?>(null)
|
||||||
|
private var pendingVerifyEmailToken by mutableStateOf<String?>(null)
|
||||||
|
private var pendingResetPasswordToken by mutableStateOf<String?>(null)
|
||||||
private var pendingNotificationChatId by mutableStateOf<Long?>(null)
|
private var pendingNotificationChatId by mutableStateOf<Long?>(null)
|
||||||
private var pendingNotificationMessageId by mutableStateOf<Long?>(null)
|
private var pendingNotificationMessageId by mutableStateOf<Long?>(null)
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
pendingInviteToken = intent.extractInviteToken()
|
pendingInviteToken = intent.extractInviteToken()
|
||||||
|
pendingVerifyEmailToken = intent.extractVerifyEmailToken()
|
||||||
|
pendingResetPasswordToken = intent.extractResetPasswordToken()
|
||||||
val notificationPayload = intent.extractNotificationOpenPayload()
|
val notificationPayload = intent.extractNotificationOpenPayload()
|
||||||
pendingNotificationChatId = notificationPayload?.first
|
pendingNotificationChatId = notificationPayload?.first
|
||||||
pendingNotificationMessageId = notificationPayload?.second
|
pendingNotificationMessageId = notificationPayload?.second
|
||||||
@@ -36,6 +40,10 @@ class MainActivity : ComponentActivity() {
|
|||||||
AppRoot(
|
AppRoot(
|
||||||
inviteToken = pendingInviteToken,
|
inviteToken = pendingInviteToken,
|
||||||
onInviteTokenConsumed = { pendingInviteToken = null },
|
onInviteTokenConsumed = { pendingInviteToken = null },
|
||||||
|
verifyEmailToken = pendingVerifyEmailToken,
|
||||||
|
onVerifyEmailTokenConsumed = { pendingVerifyEmailToken = null },
|
||||||
|
resetPasswordToken = pendingResetPasswordToken,
|
||||||
|
onResetPasswordTokenConsumed = { pendingResetPasswordToken = null },
|
||||||
notificationChatId = pendingNotificationChatId,
|
notificationChatId = pendingNotificationChatId,
|
||||||
notificationMessageId = pendingNotificationMessageId,
|
notificationMessageId = pendingNotificationMessageId,
|
||||||
onNotificationConsumed = {
|
onNotificationConsumed = {
|
||||||
@@ -52,6 +60,8 @@ class MainActivity : ComponentActivity() {
|
|||||||
super.onNewIntent(intent)
|
super.onNewIntent(intent)
|
||||||
setIntent(intent)
|
setIntent(intent)
|
||||||
pendingInviteToken = intent.extractInviteToken() ?: pendingInviteToken
|
pendingInviteToken = intent.extractInviteToken() ?: pendingInviteToken
|
||||||
|
pendingVerifyEmailToken = intent.extractVerifyEmailToken() ?: pendingVerifyEmailToken
|
||||||
|
pendingResetPasswordToken = intent.extractResetPasswordToken() ?: pendingResetPasswordToken
|
||||||
val notificationPayload = intent.extractNotificationOpenPayload()
|
val notificationPayload = intent.extractNotificationOpenPayload()
|
||||||
if (notificationPayload != null) {
|
if (notificationPayload != null) {
|
||||||
pendingNotificationChatId = notificationPayload.first
|
pendingNotificationChatId = notificationPayload.first
|
||||||
@@ -64,6 +74,10 @@ class MainActivity : ComponentActivity() {
|
|||||||
private fun AppRoot(
|
private fun AppRoot(
|
||||||
inviteToken: String?,
|
inviteToken: String?,
|
||||||
onInviteTokenConsumed: () -> Unit,
|
onInviteTokenConsumed: () -> Unit,
|
||||||
|
verifyEmailToken: String?,
|
||||||
|
onVerifyEmailTokenConsumed: () -> Unit,
|
||||||
|
resetPasswordToken: String?,
|
||||||
|
onResetPasswordTokenConsumed: () -> Unit,
|
||||||
notificationChatId: Long?,
|
notificationChatId: Long?,
|
||||||
notificationMessageId: Long?,
|
notificationMessageId: Long?,
|
||||||
onNotificationConsumed: () -> Unit,
|
onNotificationConsumed: () -> Unit,
|
||||||
@@ -71,12 +85,30 @@ private fun AppRoot(
|
|||||||
MessengerNavHost(
|
MessengerNavHost(
|
||||||
inviteToken = inviteToken,
|
inviteToken = inviteToken,
|
||||||
onInviteTokenConsumed = onInviteTokenConsumed,
|
onInviteTokenConsumed = onInviteTokenConsumed,
|
||||||
|
verifyEmailToken = verifyEmailToken,
|
||||||
|
onVerifyEmailTokenConsumed = onVerifyEmailTokenConsumed,
|
||||||
|
resetPasswordToken = resetPasswordToken,
|
||||||
|
onResetPasswordTokenConsumed = onResetPasswordTokenConsumed,
|
||||||
notificationChatId = notificationChatId,
|
notificationChatId = notificationChatId,
|
||||||
notificationMessageId = notificationMessageId,
|
notificationMessageId = notificationMessageId,
|
||||||
onNotificationConsumed = onNotificationConsumed,
|
onNotificationConsumed = onNotificationConsumed,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Intent?.extractVerifyEmailToken(): String? {
|
||||||
|
val uri = this?.data ?: return null
|
||||||
|
val isVerifyPath = uri.pathSegments.contains("verify-email") || uri.path.equals("/verify-email", ignoreCase = true)
|
||||||
|
if (!isVerifyPath) return null
|
||||||
|
return uri.getQueryParameter("token")?.trim()?.takeIf { it.isNotBlank() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Intent?.extractResetPasswordToken(): String? {
|
||||||
|
val uri = this?.data ?: return null
|
||||||
|
val isResetPath = uri.pathSegments.contains("reset-password") || uri.path.equals("/reset-password", ignoreCase = true)
|
||||||
|
if (!isResetPath) return null
|
||||||
|
return uri.getQueryParameter("token")?.trim()?.takeIf { it.isNotBlank() }
|
||||||
|
}
|
||||||
|
|
||||||
private fun Intent?.extractInviteToken(): String? {
|
private fun Intent?.extractInviteToken(): String? {
|
||||||
val uri = this?.data ?: return null
|
val uri = this?.data ?: return null
|
||||||
val queryToken = uri.getQueryParameter("token")?.trim().orEmpty()
|
val queryToken = uri.getQueryParameter("token")?.trim().orEmpty()
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ fun LoginScreen(
|
|||||||
onEmailChanged: (String) -> Unit,
|
onEmailChanged: (String) -> Unit,
|
||||||
onPasswordChanged: (String) -> Unit,
|
onPasswordChanged: (String) -> Unit,
|
||||||
onLoginClick: () -> Unit,
|
onLoginClick: () -> Unit,
|
||||||
|
onOpenVerifyEmail: () -> Unit,
|
||||||
|
onOpenResetPassword: () -> Unit,
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -86,14 +88,19 @@ fun LoginScreen(
|
|||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
modifier = Modifier.padding(top = 12.dp),
|
modifier = Modifier.padding(top = 12.dp),
|
||||||
)
|
)
|
||||||
} else {
|
}
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = {},
|
onClick = onOpenVerifyEmail,
|
||||||
enabled = false,
|
enabled = !state.isLoading,
|
||||||
modifier = Modifier.padding(top = 8.dp),
|
modifier = Modifier.padding(top = 8.dp),
|
||||||
) {
|
) {
|
||||||
Text(text = "Use your existing backend account")
|
Text(text = "Verify email by token")
|
||||||
}
|
}
|
||||||
|
TextButton(
|
||||||
|
onClick = onOpenResetPassword,
|
||||||
|
enabled = !state.isLoading,
|
||||||
|
) {
|
||||||
|
Text(text = "Forgot password")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package ru.daemonlord.messenger.ui.auth.reset
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.safeDrawing
|
||||||
|
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.OutlinedTextField
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import ru.daemonlord.messenger.ui.account.AccountViewModel
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ResetPasswordRoute(
|
||||||
|
token: String?,
|
||||||
|
onBackToLogin: () -> Unit,
|
||||||
|
viewModel: AccountViewModel = hiltViewModel(),
|
||||||
|
) {
|
||||||
|
val state by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
|
var email by remember { mutableStateOf("") }
|
||||||
|
var password by remember { mutableStateOf("") }
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.windowInsetsPadding(WindowInsets.safeDrawing)
|
||||||
|
.padding(16.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||||
|
) {
|
||||||
|
Text("Password reset", style = MaterialTheme.typography.headlineSmall)
|
||||||
|
OutlinedTextField(
|
||||||
|
value = email,
|
||||||
|
onValueChange = { email = it },
|
||||||
|
label = { Text("Email") },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
Button(
|
||||||
|
onClick = { viewModel.requestPasswordReset(email) },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
enabled = !state.isSaving && email.isNotBlank(),
|
||||||
|
) {
|
||||||
|
Text("Send reset link")
|
||||||
|
}
|
||||||
|
OutlinedTextField(
|
||||||
|
value = password,
|
||||||
|
onValueChange = { password = it },
|
||||||
|
label = { Text("New password") },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
if (!token.isNullOrBlank()) {
|
||||||
|
viewModel.resetPassword(token = token, password = password)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
enabled = !state.isSaving && !token.isNullOrBlank() && password.length >= 8,
|
||||||
|
) {
|
||||||
|
Text("Reset with token")
|
||||||
|
}
|
||||||
|
if (state.isSaving) {
|
||||||
|
CircularProgressIndicator()
|
||||||
|
}
|
||||||
|
if (!state.message.isNullOrBlank()) {
|
||||||
|
Text(state.message!!, color = MaterialTheme.colorScheme.primary)
|
||||||
|
}
|
||||||
|
if (!state.errorMessage.isNullOrBlank()) {
|
||||||
|
Text(state.errorMessage!!, color = MaterialTheme.colorScheme.error)
|
||||||
|
}
|
||||||
|
Button(onClick = onBackToLogin, modifier = Modifier.fillMaxWidth()) {
|
||||||
|
Text("Back to login")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package ru.daemonlord.messenger.ui.auth.verify
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.safeDrawing
|
||||||
|
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.OutlinedTextField
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import ru.daemonlord.messenger.ui.account.AccountViewModel
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun VerifyEmailRoute(
|
||||||
|
token: String?,
|
||||||
|
onBackToLogin: () -> Unit,
|
||||||
|
viewModel: AccountViewModel = hiltViewModel(),
|
||||||
|
) {
|
||||||
|
val state by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
|
var editableToken by remember(token) { mutableStateOf(token.orEmpty()) }
|
||||||
|
|
||||||
|
LaunchedEffect(token) {
|
||||||
|
if (!token.isNullOrBlank()) {
|
||||||
|
viewModel.verifyEmail(token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.windowInsetsPadding(WindowInsets.safeDrawing)
|
||||||
|
.padding(16.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||||
|
) {
|
||||||
|
Text("Verify email", style = MaterialTheme.typography.headlineSmall)
|
||||||
|
OutlinedTextField(
|
||||||
|
value = editableToken,
|
||||||
|
onValueChange = { editableToken = it },
|
||||||
|
label = { Text("Verification token") },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
Button(
|
||||||
|
onClick = { viewModel.verifyEmail(editableToken) },
|
||||||
|
enabled = !state.isSaving && editableToken.isNotBlank(),
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
) {
|
||||||
|
Text("Verify")
|
||||||
|
}
|
||||||
|
if (state.isSaving) {
|
||||||
|
CircularProgressIndicator()
|
||||||
|
}
|
||||||
|
if (!state.message.isNullOrBlank()) {
|
||||||
|
Text(state.message!!, color = MaterialTheme.colorScheme.primary)
|
||||||
|
}
|
||||||
|
if (!state.errorMessage.isNullOrBlank()) {
|
||||||
|
Text(state.errorMessage!!, color = MaterialTheme.colorScheme.error)
|
||||||
|
}
|
||||||
|
Button(onClick = onBackToLogin, modifier = Modifier.fillMaxWidth()) {
|
||||||
|
Text("Back to login")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,6 +31,8 @@ import androidx.navigation.compose.navigation
|
|||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import ru.daemonlord.messenger.ui.auth.AuthViewModel
|
import ru.daemonlord.messenger.ui.auth.AuthViewModel
|
||||||
import ru.daemonlord.messenger.ui.auth.LoginScreen
|
import ru.daemonlord.messenger.ui.auth.LoginScreen
|
||||||
|
import ru.daemonlord.messenger.ui.auth.reset.ResetPasswordRoute
|
||||||
|
import ru.daemonlord.messenger.ui.auth.verify.VerifyEmailRoute
|
||||||
import ru.daemonlord.messenger.ui.chat.ChatRoute
|
import ru.daemonlord.messenger.ui.chat.ChatRoute
|
||||||
import ru.daemonlord.messenger.ui.chats.ChatListRoute
|
import ru.daemonlord.messenger.ui.chats.ChatListRoute
|
||||||
import ru.daemonlord.messenger.ui.profile.ProfileRoute
|
import ru.daemonlord.messenger.ui.profile.ProfileRoute
|
||||||
@@ -39,6 +41,8 @@ import ru.daemonlord.messenger.ui.settings.SettingsRoute
|
|||||||
private object Routes {
|
private object Routes {
|
||||||
const val AuthGraph = "auth_graph"
|
const val AuthGraph = "auth_graph"
|
||||||
const val Login = "login"
|
const val Login = "login"
|
||||||
|
const val VerifyEmail = "verify_email"
|
||||||
|
const val ResetPassword = "reset_password"
|
||||||
const val Chats = "chats"
|
const val Chats = "chats"
|
||||||
const val Settings = "settings"
|
const val Settings = "settings"
|
||||||
const val Profile = "profile"
|
const val Profile = "profile"
|
||||||
@@ -51,6 +55,10 @@ fun MessengerNavHost(
|
|||||||
viewModel: AuthViewModel = hiltViewModel(),
|
viewModel: AuthViewModel = hiltViewModel(),
|
||||||
inviteToken: String? = null,
|
inviteToken: String? = null,
|
||||||
onInviteTokenConsumed: () -> Unit = {},
|
onInviteTokenConsumed: () -> Unit = {},
|
||||||
|
verifyEmailToken: String? = null,
|
||||||
|
onVerifyEmailTokenConsumed: () -> Unit = {},
|
||||||
|
resetPasswordToken: String? = null,
|
||||||
|
onResetPasswordTokenConsumed: () -> Unit = {},
|
||||||
notificationChatId: Long? = null,
|
notificationChatId: Long? = null,
|
||||||
notificationMessageId: Long? = null,
|
notificationMessageId: Long? = null,
|
||||||
onNotificationConsumed: () -> Unit = {},
|
onNotificationConsumed: () -> Unit = {},
|
||||||
@@ -72,10 +80,26 @@ fun MessengerNavHost(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(uiState.isCheckingSession, uiState.isAuthenticated, notificationChatId, notificationMessageId) {
|
LaunchedEffect(uiState.isCheckingSession, uiState.isAuthenticated, verifyEmailToken, resetPasswordToken, notificationChatId, notificationMessageId) {
|
||||||
if (uiState.isCheckingSession) {
|
if (uiState.isCheckingSession) {
|
||||||
return@LaunchedEffect
|
return@LaunchedEffect
|
||||||
}
|
}
|
||||||
|
if (!uiState.isAuthenticated && !verifyEmailToken.isNullOrBlank()) {
|
||||||
|
navController.navigate("${Routes.VerifyEmail}?token=$verifyEmailToken") {
|
||||||
|
popUpTo(navController.graph.findStartDestination().id) { inclusive = true }
|
||||||
|
launchSingleTop = true
|
||||||
|
}
|
||||||
|
onVerifyEmailTokenConsumed()
|
||||||
|
return@LaunchedEffect
|
||||||
|
}
|
||||||
|
if (!uiState.isAuthenticated && !resetPasswordToken.isNullOrBlank()) {
|
||||||
|
navController.navigate("${Routes.ResetPassword}?token=$resetPasswordToken") {
|
||||||
|
popUpTo(navController.graph.findStartDestination().id) { inclusive = true }
|
||||||
|
launchSingleTop = true
|
||||||
|
}
|
||||||
|
onResetPasswordTokenConsumed()
|
||||||
|
return@LaunchedEffect
|
||||||
|
}
|
||||||
if (uiState.isAuthenticated) {
|
if (uiState.isAuthenticated) {
|
||||||
if (notificationChatId != null) {
|
if (notificationChatId != null) {
|
||||||
navController.navigate("${Routes.Chat}/$notificationChatId") {
|
navController.navigate("${Routes.Chat}/$notificationChatId") {
|
||||||
@@ -120,11 +144,45 @@ fun MessengerNavHost(
|
|||||||
onEmailChanged = viewModel::onEmailChanged,
|
onEmailChanged = viewModel::onEmailChanged,
|
||||||
onPasswordChanged = viewModel::onPasswordChanged,
|
onPasswordChanged = viewModel::onPasswordChanged,
|
||||||
onLoginClick = viewModel::login,
|
onLoginClick = viewModel::login,
|
||||||
|
onOpenVerifyEmail = { navController.navigate(Routes.VerifyEmail) },
|
||||||
|
onOpenResetPassword = { navController.navigate(Routes.ResetPassword) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
composable(
|
||||||
|
route = "${Routes.VerifyEmail}?token={token}",
|
||||||
|
arguments = listOf(
|
||||||
|
navArgument("token") {
|
||||||
|
type = NavType.StringType
|
||||||
|
nullable = true
|
||||||
|
defaultValue = null
|
||||||
|
}
|
||||||
|
),
|
||||||
|
) { entry ->
|
||||||
|
VerifyEmailRoute(
|
||||||
|
token = entry.arguments?.getString("token"),
|
||||||
|
onBackToLogin = { navController.navigate(Routes.Login) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
composable(
|
||||||
|
route = "${Routes.ResetPassword}?token={token}",
|
||||||
|
arguments = listOf(
|
||||||
|
navArgument("token") {
|
||||||
|
type = NavType.StringType
|
||||||
|
nullable = true
|
||||||
|
defaultValue = null
|
||||||
|
}
|
||||||
|
),
|
||||||
|
) { entry ->
|
||||||
|
ResetPasswordRoute(
|
||||||
|
token = entry.arguments?.getString("token"),
|
||||||
|
onBackToLogin = { navController.navigate(Routes.Login) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
composable(route = Routes.Chats) {
|
composable(route = Routes.Chats) {
|
||||||
ChatListRoute(
|
ChatListRoute(
|
||||||
inviteToken = inviteToken,
|
inviteToken = inviteToken,
|
||||||
|
|||||||
Reference in New Issue
Block a user