Updated FIxed routes and Logout
This commit is contained in:
parent
95c6c598a0
commit
682ed78491
|
|
@ -29,7 +29,7 @@ class MainActivity : ComponentActivity() {
|
|||
val mainViewModel: MainViewModel = viewModel(factory = MainViewModelFactory(LocalContext.current))
|
||||
val authState by mainViewModel.authState.collectAsState()
|
||||
|
||||
AppNavigation(authState)
|
||||
AppNavigation(authState = authState, mainViewModel = mainViewModel)
|
||||
// when (authState) {
|
||||
// is AuthState.Unknown -> {
|
||||
// Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
|
|
|
|||
|
|
@ -111,6 +111,20 @@ class AuthApiClient(private val context: Context) {
|
|||
client.get("users/me").body()
|
||||
}
|
||||
|
||||
suspend fun refreshToken(): Result<RefreshResponse> = runCatching {
|
||||
val refreshToken = tokenManager.getRefreshToken()
|
||||
?: throw IllegalStateException("No refresh token found")
|
||||
|
||||
val response: RefreshResponse = client.post("auth/refresh") {
|
||||
contentType(ContentType.Application.Json)
|
||||
setBody(RefreshRequest(refreshToken))
|
||||
}.body()
|
||||
|
||||
// Save the new tokens (refresh token rotates)
|
||||
tokenManager.saveTokens(response.accessToken, response.refreshToken)
|
||||
response
|
||||
}
|
||||
|
||||
suspend fun logout(): Result<LogoutResponse> = runCatching {
|
||||
val refreshToken = tokenManager.getRefreshToken() ?: throw IllegalStateException("No refresh token found")
|
||||
val response: LogoutResponse = client.post("auth/logout") {
|
||||
|
|
|
|||
|
|
@ -39,6 +39,10 @@ class AuthManager(
|
|||
return apiClient.updateProfile(name, userType)
|
||||
}
|
||||
|
||||
suspend fun logout(): Result<com.example.livingai_lg.api.LogoutResponse> {
|
||||
return apiClient.logout()
|
||||
}
|
||||
|
||||
private fun getDeviceId(): String {
|
||||
return Settings.Secure.getString(
|
||||
context.contentResolver,
|
||||
|
|
|
|||
|
|
@ -88,8 +88,8 @@ data class User(
|
|||
val id: String,
|
||||
@SerialName("phone_number") val phoneNumber: String,
|
||||
val name: String?,
|
||||
val role: String,
|
||||
@SerialName("user_type") val userType: String?,
|
||||
val role: String? = null, // Optional field - can be missing from JSON, defaults to null
|
||||
@SerialName("user_type") val userType: String? = null, // Optional field - can be missing from JSON, defaults to null
|
||||
@SerialName("created_at") val createdAt: String? = null,
|
||||
@SerialName("country_code") val countryCode: String? = null
|
||||
)
|
||||
|
|
|
|||
|
|
@ -40,27 +40,80 @@ class MainViewModel(context: Context) : ViewModel() {
|
|||
checkAuthStatus()
|
||||
}
|
||||
|
||||
/**
|
||||
* Public method to refresh auth status after login/signup
|
||||
* Call this after tokens are saved to update the auth state
|
||||
*/
|
||||
fun refreshAuthStatus() {
|
||||
checkAuthStatus()
|
||||
}
|
||||
|
||||
private fun checkAuthStatus() {
|
||||
viewModelScope.launch {
|
||||
if (tokenManager.getAccessToken() != null) {
|
||||
_authState.value = AuthState.Authenticated
|
||||
fetchUserDetails()
|
||||
val accessToken = tokenManager.getAccessToken()
|
||||
val refreshToken = tokenManager.getRefreshToken()
|
||||
|
||||
if (accessToken != null && refreshToken != null) {
|
||||
// Tokens exist, validate them by fetching user details
|
||||
// The Ktor Auth plugin will automatically refresh if access token is expired
|
||||
validateTokens()
|
||||
} else {
|
||||
// No tokens, user is not authenticated
|
||||
_authState.value = AuthState.Unauthenticated
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun validateTokens() {
|
||||
viewModelScope.launch {
|
||||
// Try to fetch user details - this will validate the access token
|
||||
// If access token is expired, Ktor's Auth plugin will auto-refresh
|
||||
authApiClient.getUserDetails()
|
||||
.onSuccess { userDetails ->
|
||||
// Tokens are valid, user is authenticated
|
||||
_authState.value = AuthState.Authenticated
|
||||
_userState.value = UserState.Success(userDetails)
|
||||
}
|
||||
.onFailure { error ->
|
||||
// If fetching user details failed, try manual refresh
|
||||
Log.d(TAG, "Failed to fetch user details, attempting token refresh: ${error.message}")
|
||||
attemptTokenRefresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun attemptTokenRefresh() {
|
||||
viewModelScope.launch {
|
||||
authApiClient.refreshToken()
|
||||
.onSuccess { refreshResponse ->
|
||||
// Refresh successful, tokens are valid
|
||||
Log.d(TAG, "Token refresh successful")
|
||||
_authState.value = AuthState.Authenticated
|
||||
// Fetch user details with new token
|
||||
fetchUserDetails()
|
||||
}
|
||||
.onFailure { error ->
|
||||
// Refresh failed, tokens are invalid - clear and logout
|
||||
Log.d(TAG, "Token refresh failed: ${error.message}")
|
||||
tokenManager.clearTokens()
|
||||
_authState.value = AuthState.Unauthenticated
|
||||
_userState.value = UserState.Error("Session expired. Please sign in again.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchUserDetails() {
|
||||
viewModelScope.launch {
|
||||
_userState.value = UserState.Loading
|
||||
authApiClient.getUserDetails()
|
||||
.onSuccess {
|
||||
_userState.value = UserState.Success(it)
|
||||
_authState.value = AuthState.Authenticated
|
||||
}
|
||||
.onFailure {
|
||||
_userState.value = UserState.Error(it.message ?: "Unknown error")
|
||||
_authState.value = AuthState.Unauthenticated
|
||||
// Don't automatically set to Unauthenticated here - let the caller decide
|
||||
// or try refresh if needed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@ fun UserLocationHeader(
|
|||
user: UserProfile,
|
||||
selectedAddressId: String,
|
||||
modifier: Modifier = Modifier,
|
||||
onOpenAddressOverlay: () -> Unit
|
||||
onOpenAddressOverlay: () -> Unit,
|
||||
onProfileClick: () -> Unit = {} // New callback for profile icon click
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier.wrapContentWidth(),
|
||||
|
|
@ -41,12 +42,18 @@ fun UserLocationHeader(
|
|||
) {
|
||||
val selectedAddress = user.addresses.find { it.id == selectedAddressId } ?: user.addresses.first()
|
||||
|
||||
// Profile image
|
||||
// Profile image - make it clickable
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.clip(CircleShape)
|
||||
.background(Color.Black),
|
||||
.background(Color.Black)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) {
|
||||
onProfileClick()
|
||||
},
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
if (user.profileImageUrl != null) {
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ object AppScreen {
|
|||
|
||||
const val CHATS = "chats"
|
||||
|
||||
const val ACCOUNTS = "accounts"
|
||||
|
||||
fun chats(contact: String) =
|
||||
"$CHAT/$contact"
|
||||
|
||||
|
|
@ -100,7 +102,7 @@ object AppScreen {
|
|||
|
||||
object Graph {
|
||||
const val AUTH = "auth"
|
||||
const val MAIN = "auth"
|
||||
const val MAIN = "main"
|
||||
|
||||
fun auth(route: String)=
|
||||
"$AUTH/$route"
|
||||
|
|
@ -110,7 +112,8 @@ object Graph {
|
|||
}
|
||||
@Composable
|
||||
fun AppNavigation(
|
||||
authState: AuthState
|
||||
authState: AuthState,
|
||||
mainViewModel: com.example.livingai_lg.ui.MainViewModel
|
||||
) {
|
||||
val navController = rememberNavController()
|
||||
|
||||
|
|
@ -120,21 +123,39 @@ fun AppNavigation(
|
|||
navController = navController,
|
||||
startDestination = Graph.AUTH
|
||||
) {
|
||||
authNavGraph(navController)
|
||||
authNavGraph(navController, mainViewModel)
|
||||
mainNavGraph(navController)
|
||||
}
|
||||
|
||||
// Navigate to MAIN graph if user is authenticated
|
||||
// Handle navigation based on auth state
|
||||
LaunchedEffect(authState) {
|
||||
if (authState is AuthState.Authenticated) {
|
||||
val currentRoute = navController.currentBackStackEntry?.destination?.route
|
||||
// Only navigate if we're not already in the MAIN graph
|
||||
if (currentRoute?.startsWith(Graph.MAIN) != true) {
|
||||
navController.navigate(Graph.MAIN) {
|
||||
// Clear back stack to prevent going back to auth screens
|
||||
popUpTo(Graph.AUTH) { inclusive = true }
|
||||
when (authState) {
|
||||
is AuthState.Authenticated -> {
|
||||
// User is authenticated, navigate to main graph
|
||||
val currentRoute = navController.currentBackStackEntry?.destination?.route
|
||||
// Only navigate if we're not already in the MAIN graph
|
||||
if (currentRoute?.startsWith(Graph.MAIN) != true &&
|
||||
currentRoute?.startsWith(Graph.AUTH) == true) {
|
||||
navController.navigate(Graph.MAIN) {
|
||||
// Clear back stack to prevent going back to auth screens
|
||||
popUpTo(Graph.AUTH) { inclusive = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
is AuthState.Unauthenticated -> {
|
||||
// User is not authenticated, ensure we're in auth graph (landing screen)
|
||||
val currentRoute = navController.currentBackStackEntry?.destination?.route
|
||||
if (currentRoute?.startsWith(Graph.MAIN) == true) {
|
||||
navController.navigate(Graph.AUTH) {
|
||||
// Clear back stack to prevent going back to main screens
|
||||
popUpTo(0) { inclusive = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
is AuthState.Unknown -> {
|
||||
// Still checking auth status, stay on landing screen
|
||||
// Don't navigate anywhere yet
|
||||
}
|
||||
}
|
||||
}
|
||||
// MainNavGraph(navController)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import com.example.livingai_lg.ui.screens.auth.OtpScreen
|
|||
import com.example.livingai_lg.ui.screens.auth.SignInScreen
|
||||
import com.example.livingai_lg.ui.screens.auth.SignUpScreen
|
||||
|
||||
fun NavGraphBuilder.authNavGraph(navController: NavController) {
|
||||
fun NavGraphBuilder.authNavGraph(navController: NavController, mainViewModel: com.example.livingai_lg.ui.MainViewModel) {
|
||||
navigation(
|
||||
route = Graph.AUTH,
|
||||
startDestination = AppScreen.LANDING
|
||||
|
|
@ -67,6 +67,7 @@ fun NavGraphBuilder.authNavGraph(navController: NavController) {
|
|||
OtpScreen(
|
||||
phoneNumber = backStackEntry.arguments?.getString("phoneNumber") ?: "",
|
||||
name = backStackEntry.arguments?.getString("name") ?: "",
|
||||
mainViewModel = mainViewModel,
|
||||
onCreateProfile = {name ->
|
||||
android.util.Log.d("AuthNavGraph", "Navigating to create profile with name: $name")
|
||||
// Navigate to main graph first without popping, then navigate to specific route
|
||||
|
|
@ -92,6 +93,19 @@ fun NavGraphBuilder.authNavGraph(navController: NavController) {
|
|||
android.util.Log.e("AuthNavGraph", "Navigation error: ${e.message}", e)
|
||||
}
|
||||
},
|
||||
onLanding = {
|
||||
android.util.Log.d("AuthNavGraph", "Navigating to landing page for new user")
|
||||
// Navigate to landing page within AUTH graph
|
||||
try {
|
||||
navController.navigate(AppScreen.LANDING) {
|
||||
// Pop all screens up to and including the current OTP screen
|
||||
popUpTo(Graph.AUTH) { inclusive = false }
|
||||
launchSingleTop = true
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("AuthNavGraph", "Navigation to landing error: ${e.message}", e)
|
||||
}
|
||||
},
|
||||
onSuccess = {
|
||||
android.util.Log.d("AuthNavGraph", "Navigating to choose service")
|
||||
// Navigate to main graph first without popping, then navigate to specific route
|
||||
|
|
@ -136,6 +150,7 @@ fun NavGraphBuilder.authNavGraph(navController: NavController) {
|
|||
signupState = backStackEntry.arguments?.getString("state"),
|
||||
signupDistrict = backStackEntry.arguments?.getString("district"),
|
||||
signupVillage = backStackEntry.arguments?.getString("village"),
|
||||
mainViewModel = mainViewModel,
|
||||
onCreateProfile = {name ->
|
||||
android.util.Log.d("AuthNavGraph", "Navigating to create profile with name: $name")
|
||||
// Navigate to main graph first without popping, then navigate to specific route
|
||||
|
|
@ -161,6 +176,19 @@ fun NavGraphBuilder.authNavGraph(navController: NavController) {
|
|||
android.util.Log.e("AuthNavGraph", "Navigation error: ${e.message}", e)
|
||||
}
|
||||
},
|
||||
onLanding = {
|
||||
android.util.Log.d("AuthNavGraph", "Navigating to landing page for new user")
|
||||
// Navigate to landing page within AUTH graph
|
||||
try {
|
||||
navController.navigate(AppScreen.LANDING) {
|
||||
// Pop all screens up to and including the current OTP screen
|
||||
popUpTo(Graph.AUTH) { inclusive = false }
|
||||
launchSingleTop = true
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("AuthNavGraph", "Navigation to landing error: ${e.message}", e)
|
||||
}
|
||||
},
|
||||
onSuccess = {
|
||||
android.util.Log.d("AuthNavGraph", "Navigating to choose service")
|
||||
// Navigate to main graph first without popping, then navigate to specific route
|
||||
|
|
|
|||
|
|
@ -8,9 +8,12 @@ import androidx.navigation.compose.NavHost
|
|||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.navigation
|
||||
import androidx.navigation.navArgument
|
||||
import com.example.livingai_lg.ui.navigation.AppScreen
|
||||
import com.example.livingai_lg.ui.navigation.Graph
|
||||
import com.example.farmmarketplace.ui.screens.CallsScreen
|
||||
import com.example.farmmarketplace.ui.screens.ContactsScreen
|
||||
import com.example.livingai_lg.ui.models.profileTypes
|
||||
import com.example.livingai_lg.ui.screens.AccountsScreen
|
||||
import com.example.livingai_lg.ui.screens.AnimalProfileScreen
|
||||
import com.example.livingai_lg.ui.screens.BuyScreen
|
||||
import com.example.livingai_lg.ui.screens.ChooseServiceScreen
|
||||
|
|
@ -126,6 +129,18 @@ fun NavGraphBuilder.mainNavGraph(navController: NavController) {
|
|||
onBackClick = { navController.popBackStack() })
|
||||
}
|
||||
|
||||
composable(AppScreen.ACCOUNTS) {
|
||||
AccountsScreen(
|
||||
onBackClick = { navController.popBackStack() },
|
||||
onLogout = {
|
||||
// Navigate to auth graph after logout
|
||||
navController.navigate(Graph.AUTH) {
|
||||
popUpTo(0) { inclusive = true }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
composable(AppScreen.CREATE_ANIMAL_LISTING) {
|
||||
NewListingScreen (
|
||||
|
|
|
|||
|
|
@ -0,0 +1,129 @@
|
|||
package com.example.livingai_lg.ui.screens
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.filled.Logout
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.example.livingai_lg.api.AuthApiClient
|
||||
import com.example.livingai_lg.api.AuthManager
|
||||
import com.example.livingai_lg.api.TokenManager
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AccountsScreen(
|
||||
onBackClick: () -> Unit,
|
||||
onLogout: () -> Unit
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val authManager = remember { AuthManager(context, AuthApiClient(context), TokenManager(context)) }
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color(0xFFF7F4EE))
|
||||
) {
|
||||
// Top App Bar
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
text = "Accounts",
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = Color.Black
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onBackClick) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = "Back",
|
||||
tint = Color.Black
|
||||
)
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = Color(0xFFF7F4EE)
|
||||
)
|
||||
)
|
||||
|
||||
// Content
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 16.dp, vertical = 24.dp)
|
||||
) {
|
||||
// Logout option
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
scope.launch {
|
||||
authManager.logout()
|
||||
.onSuccess { _: com.example.livingai_lg.api.LogoutResponse ->
|
||||
Toast.makeText(context, "Logged out successfully", Toast.LENGTH_SHORT).show()
|
||||
onLogout()
|
||||
}
|
||||
.onFailure { error: Throwable ->
|
||||
Toast.makeText(context, "Logout failed: ${error.message}", Toast.LENGTH_SHORT).show()
|
||||
// Still call onLogout to navigate away even if API call fails
|
||||
onLogout()
|
||||
}
|
||||
}
|
||||
},
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
color = Color.White,
|
||||
shadowElevation = 2.dp
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 20.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.Logout,
|
||||
contentDescription = "Logout",
|
||||
tint = Color(0xFFE53935),
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
Text(
|
||||
text = "Log Out",
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFFE53935)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -94,6 +94,9 @@ fun BuyScreen(
|
|||
user = userProfile,
|
||||
onOpenAddressOverlay = { showAddressSelector = true },
|
||||
selectedAddressId = selectedAddressId?:"1",
|
||||
onProfileClick = {
|
||||
onNavClick(AppScreen.ACCOUNTS)
|
||||
}
|
||||
)
|
||||
|
||||
// Right-side actions (notifications, etc.)
|
||||
|
|
|
|||
|
|
@ -48,8 +48,10 @@ import androidx.compose.ui.input.key.onPreviewKeyEvent
|
|||
fun OtpScreen(
|
||||
phoneNumber: String,
|
||||
name: String,
|
||||
mainViewModel: com.example.livingai_lg.ui.MainViewModel,
|
||||
onSuccess: () -> Unit = {},
|
||||
onCreateProfile: (name: String) -> Unit = {},
|
||||
onLanding: () -> Unit = {}, // New callback for navigating to landing page
|
||||
// Optional signup data for signup flow
|
||||
signupState: String? = null,
|
||||
signupDistrict: String? = null,
|
||||
|
|
@ -164,6 +166,8 @@ Column(
|
|||
.onSuccess { verifyResponse ->
|
||||
android.util.Log.d("OTPScreen", "OTP verified successfully. Calling signup API...")
|
||||
// OTP verified successfully - user is now logged in
|
||||
// Refresh auth status in MainViewModel so AppNavigation knows user is authenticated
|
||||
mainViewModel.refreshAuthStatus()
|
||||
// Now call signup API to update user with name and location
|
||||
val signupRequest = SignupRequest(
|
||||
name = name,
|
||||
|
|
@ -174,57 +178,76 @@ Column(
|
|||
)
|
||||
authManager.signup(signupRequest)
|
||||
.onSuccess { signupResponse ->
|
||||
android.util.Log.d("OTPScreen", "Signup API response: success=${signupResponse.success}, userExists=${signupResponse.userExists}, needsProfile=${signupResponse.needsProfile}")
|
||||
// Signup API response - check if successful or user exists
|
||||
if (signupResponse.success || signupResponse.userExists == true) {
|
||||
// Success - user is created/updated and logged in
|
||||
// Check if profile needs completion
|
||||
val needsProfile = signupResponse.needsProfile == true || verifyResponse.needsProfile
|
||||
android.util.Log.d("OTPScreen", "Signup successful. needsProfile=$needsProfile, navigating...")
|
||||
android.util.Log.d("OTPScreen", "Signup API response: success=${signupResponse.success}, userExists=${signupResponse.userExists}, needsProfile=${signupResponse.needsProfile}, isNewAccount=${signupResponse.isNewAccount}")
|
||||
// Check if this is a new user account
|
||||
val isNewUser = signupResponse.isNewAccount == true
|
||||
|
||||
if (isNewUser) {
|
||||
// New user - navigate to landing page
|
||||
android.util.Log.d("OTPScreen", "New user detected - navigating to landing page")
|
||||
try {
|
||||
if (needsProfile) {
|
||||
android.util.Log.d("OTPScreen", "Navigating to create profile screen with name: $name")
|
||||
onCreateProfile(name)
|
||||
} else {
|
||||
android.util.Log.d("OTPScreen", "Navigating to success screen")
|
||||
onSuccess()
|
||||
}
|
||||
onLanding()
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("OTPScreen", "Navigation error: ${e.message}", e)
|
||||
Toast.makeText(context, "Navigation error: ${e.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
} else {
|
||||
// Signup failed but OTP was verified - user is logged in
|
||||
// Navigate to success anyway since verify-otp succeeded
|
||||
android.util.Log.d("OTPScreen", "Signup API returned false, but OTP verified. Navigating anyway...")
|
||||
val needsProfile = verifyResponse.needsProfile
|
||||
try {
|
||||
if (needsProfile) {
|
||||
android.util.Log.d("OTPScreen", "Navigating to create profile screen with name: $name")
|
||||
onCreateProfile(name)
|
||||
} else {
|
||||
android.util.Log.d("OTPScreen", "Navigating to success screen")
|
||||
onSuccess()
|
||||
// Existing user or signup update - use existing logic
|
||||
if (signupResponse.success || signupResponse.userExists == true) {
|
||||
// Success - user is created/updated and logged in
|
||||
// Check if profile needs completion
|
||||
val needsProfile = signupResponse.needsProfile == true || verifyResponse.needsProfile
|
||||
android.util.Log.d("OTPScreen", "Signup successful. needsProfile=$needsProfile, navigating...")
|
||||
try {
|
||||
if (needsProfile) {
|
||||
android.util.Log.d("OTPScreen", "Navigating to create profile screen with name: $name")
|
||||
onCreateProfile(name)
|
||||
} else {
|
||||
android.util.Log.d("OTPScreen", "Navigating to success screen")
|
||||
onSuccess()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("OTPScreen", "Navigation error: ${e.message}", e)
|
||||
Toast.makeText(context, "Navigation error: ${e.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
} else {
|
||||
// Signup failed but OTP was verified - user is logged in
|
||||
// Navigate to success anyway since verify-otp succeeded
|
||||
android.util.Log.d("OTPScreen", "Signup API returned false, but OTP verified. Navigating anyway...")
|
||||
val needsProfile = verifyResponse.needsProfile
|
||||
try {
|
||||
if (needsProfile) {
|
||||
android.util.Log.d("OTPScreen", "Navigating to create profile screen with name: $name")
|
||||
onCreateProfile(name)
|
||||
} else {
|
||||
android.util.Log.d("OTPScreen", "Navigating to success screen")
|
||||
onSuccess()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("OTPScreen", "Navigation error: ${e.message}", e)
|
||||
Toast.makeText(context, "Navigation error: ${e.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
// Show warning if signup update failed
|
||||
val errorMsg = signupResponse.message
|
||||
if (errorMsg != null) {
|
||||
Toast.makeText(context, "Profile update: $errorMsg", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("OTPScreen", "Navigation error: ${e.message}", e)
|
||||
Toast.makeText(context, "Navigation error: ${e.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
// Show warning if signup update failed
|
||||
val errorMsg = signupResponse.message
|
||||
if (errorMsg != null) {
|
||||
Toast.makeText(context, "Profile update: $errorMsg", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
.onFailure { signupError ->
|
||||
android.util.Log.e("OTPScreen", "Signup API failed: ${signupError.message}", signupError)
|
||||
// Signup API failed but OTP was verified - user is logged in
|
||||
// Navigate to success anyway since verify-otp succeeded
|
||||
// For new users, navigate to landing page
|
||||
// For existing users, use existing logic
|
||||
val needsProfile = verifyResponse.needsProfile
|
||||
android.util.Log.d("OTPScreen", "Navigating despite signup failure. needsProfile=$needsProfile")
|
||||
try {
|
||||
if (needsProfile) {
|
||||
// If this is a signup flow and signup failed, treat as new user
|
||||
if (isSignupFlow) {
|
||||
android.util.Log.d("OTPScreen", "Signup flow failed - navigating to landing page")
|
||||
onLanding()
|
||||
} else if (needsProfile) {
|
||||
android.util.Log.d("OTPScreen", "Navigating to create profile screen with name: $name")
|
||||
onCreateProfile(name)
|
||||
} else {
|
||||
|
|
@ -253,6 +276,8 @@ Column(
|
|||
authManager.login(phoneNumber, otp.value)
|
||||
.onSuccess { response ->
|
||||
android.util.Log.d("OTPScreen", "Sign-in OTP verified. needsProfile=${response.needsProfile}")
|
||||
// Refresh auth status in MainViewModel so AppNavigation knows user is authenticated
|
||||
mainViewModel.refreshAuthStatus()
|
||||
try {
|
||||
if (isSignInFlow) {
|
||||
// For existing users, always go to the success screen.
|
||||
|
|
|
|||
Loading…
Reference in New Issue