New simplified navigation.
Info overlay api testing screen
This commit is contained in:
parent
feca34f892
commit
f8882c1dcc
|
|
@ -7,7 +7,10 @@ import androidx.lifecycle.viewModelScope
|
||||||
import com.example.livingai_lg.api.AuthApiClient
|
import com.example.livingai_lg.api.AuthApiClient
|
||||||
import com.example.livingai_lg.api.TokenManager
|
import com.example.livingai_lg.api.TokenManager
|
||||||
import com.example.livingai_lg.api.UserDetails
|
import com.example.livingai_lg.api.UserDetails
|
||||||
|
import com.example.livingai_lg.ui.navigation.NavEvent
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
@ -343,4 +346,14 @@ class MainViewModel(context: Context) : ViewModel() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used in navigation logic.
|
||||||
|
private val _navEvents = MutableSharedFlow<NavEvent>()
|
||||||
|
val navEvents = _navEvents.asSharedFlow()
|
||||||
|
|
||||||
|
fun emitNavEvent(event: NavEvent) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_navEvents.emit(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,8 @@ fun BuyAnimalCard(
|
||||||
onSavedChange: (Boolean) -> Unit,
|
onSavedChange: (Boolean) -> Unit,
|
||||||
onProductClick: () -> Unit,
|
onProductClick: () -> Unit,
|
||||||
onSellerClick:(sellerId: String)-> Unit,
|
onSellerClick:(sellerId: String)-> Unit,
|
||||||
onBookmarkClick: () -> Unit
|
onBookmarkClick: () -> Unit,
|
||||||
|
onInfoClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -164,7 +165,7 @@ fun BuyAnimalCard(
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Outlined.Info,
|
imageVector = Icons.Outlined.Info,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
Modifier.padding(horizontal = 4.dp).size(16.dp).align(Alignment.CenterVertically),
|
Modifier.padding(horizontal = 4.dp).size(16.dp).align(Alignment.CenterVertically).clickable{onInfoClick()},
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,142 @@
|
||||||
|
package com.example.livingai_lg.ui.components
|
||||||
|
|
||||||
|
import androidx.activity.compose.BackHandler
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.slideInVertically
|
||||||
|
import androidx.compose.animation.slideOutVertically
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.filled.Close
|
||||||
|
import androidx.compose.material.icons.filled.Info
|
||||||
|
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.Alignment
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.shadow
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.zIndex
|
||||||
|
import com.example.livingai_lg.ui.screens.align
|
||||||
|
import com.example.livingai_lg.ui.theme.AppTypography
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun InfoOverlay(
|
||||||
|
visible: Boolean,
|
||||||
|
title: String,
|
||||||
|
text: String,
|
||||||
|
onDismiss: () -> Unit
|
||||||
|
) {
|
||||||
|
BackHandler(enabled = visible) { onDismiss() }
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
|
// Dimmed background
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = visible,
|
||||||
|
enter = fadeIn(),
|
||||||
|
exit = fadeOut()
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(Color.Black.copy(alpha = 0.35f))
|
||||||
|
.clickable(
|
||||||
|
indication = null,
|
||||||
|
interactionSource = remember { MutableInteractionSource() }
|
||||||
|
) { onDismiss() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sliding content
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = visible,
|
||||||
|
enter = slideInVertically { it }, // from bottom
|
||||||
|
exit = slideOutVertically { it },
|
||||||
|
modifier = Modifier.align(Alignment.BottomCenter)
|
||||||
|
) {
|
||||||
|
InfoOverlayContent(
|
||||||
|
title = title,
|
||||||
|
text = text,
|
||||||
|
onDismiss = onDismiss
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun InfoOverlayContent(
|
||||||
|
title: String,
|
||||||
|
text: String,
|
||||||
|
onDismiss: () -> Unit
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(
|
||||||
|
Color.White,
|
||||||
|
RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
|
||||||
|
)
|
||||||
|
.padding(20.dp)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
fontSize = AppTypography.Title,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
color = Color.Black
|
||||||
|
)
|
||||||
|
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Close,
|
||||||
|
contentDescription = "Close",
|
||||||
|
modifier = Modifier
|
||||||
|
.size(24.dp)
|
||||||
|
.clickable { onDismiss() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
fontSize = AppTypography.Body,
|
||||||
|
color = Color(0xFF4A5565),
|
||||||
|
lineHeight = 20.sp
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(18.dp))
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -29,7 +29,7 @@ val sampleAnimals = listOf(
|
||||||
name = "Rita",
|
name = "Rita",
|
||||||
age = 45,
|
age = 45,
|
||||||
breed = "Gir",
|
breed = "Gir",
|
||||||
breedInfo = "The 2nd best in India",
|
breedInfo = "The best in India",
|
||||||
location = "Punjab",
|
location = "Punjab",
|
||||||
distance = 12000,
|
distance = 12000,
|
||||||
imageUrl = listOf("https://external-content.duckduckgo.com/iu/?u=http%3A%2F%2F4.bp.blogspot.com%2F_tecSnxaePMo%2FTLLVknW8dOI%2FAAAAAAAAACo%2F_kd1ZNBXU1o%2Fs1600%2FGIR%2CGujrat.jpg&f=1&nofb=1&ipt=da6ba1d040c396b64d3f08cc99998f66200dcd6c001e4a56def143ab3d1a87ea","https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fcpimg.tistatic.com%2F4478702%2Fb%2F4%2Fgir-cow.jpg&f=1&nofb=1&ipt=19bf391461480585c786d01433d863a383c60048ac2ce063ce91f173e215205d"),
|
imageUrl = listOf("https://external-content.duckduckgo.com/iu/?u=http%3A%2F%2F4.bp.blogspot.com%2F_tecSnxaePMo%2FTLLVknW8dOI%2FAAAAAAAAACo%2F_kd1ZNBXU1o%2Fs1600%2FGIR%2CGujrat.jpg&f=1&nofb=1&ipt=da6ba1d040c396b64d3f08cc99998f66200dcd6c001e4a56def143ab3d1a87ea","https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fcpimg.tistatic.com%2F4478702%2Fb%2F4%2Fgir-cow.jpg&f=1&nofb=1&ipt=19bf391461480585c786d01433d863a383c60048ac2ce063ce91f173e215205d"),
|
||||||
|
|
|
||||||
|
|
@ -1,455 +1,492 @@
|
||||||
package com.example.livingai_lg.ui.navigation
|
package com.example.livingai_lg.ui.navigation
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import android.util.Log
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.navigation.NavController
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.navigation.NavType
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.compose.runtime.remember
|
import androidx.navigation.compose.composable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.navigation.compose.rememberNavController
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.navigation.navArgument
|
||||||
import androidx.compose.ui.Modifier
|
import com.example.farmmarketplace.ui.screens.CallsScreen
|
||||||
import androidx.navigation.NavController
|
import com.example.farmmarketplace.ui.screens.ContactsScreen
|
||||||
import androidx.navigation.NavHostController
|
import com.example.livingai_lg.ui.AuthState
|
||||||
import androidx.navigation.compose.NavHost
|
import com.example.livingai_lg.ui.MainViewModel
|
||||||
import androidx.navigation.compose.composable
|
import com.example.livingai_lg.ui.screens.AccountsScreen
|
||||||
import androidx.navigation.compose.rememberNavController
|
import com.example.livingai_lg.ui.screens.AnimalProfileScreen
|
||||||
import androidx.navigation.NavType
|
import com.example.livingai_lg.ui.screens.BuyScreen
|
||||||
import androidx.navigation.navArgument
|
import com.example.livingai_lg.ui.screens.ChooseServiceScreen
|
||||||
import kotlinx.coroutines.delay
|
import com.example.livingai_lg.ui.screens.CreateProfileScreen
|
||||||
import com.example.livingai_lg.ui.AuthState
|
import com.example.livingai_lg.ui.screens.FilterScreen
|
||||||
import com.example.livingai_lg.ui.screens.AnimalProfileScreen
|
import com.example.livingai_lg.ui.screens.NewListingScreen
|
||||||
import com.example.livingai_lg.ui.screens.BuyScreen
|
import com.example.livingai_lg.ui.screens.PostSaleSurveyScreen
|
||||||
import com.example.livingai_lg.ui.screens.ChooseServiceScreen
|
import com.example.livingai_lg.ui.screens.SaleArchiveScreen
|
||||||
import com.example.livingai_lg.ui.screens.CreateProfileScreen
|
import com.example.livingai_lg.ui.screens.SavedListingsScreen
|
||||||
import com.example.livingai_lg.ui.screens.FilterScreen
|
import com.example.livingai_lg.ui.screens.SellerProfileScreen
|
||||||
import com.example.livingai_lg.ui.screens.NewListingScreen
|
import com.example.livingai_lg.ui.screens.SortScreen
|
||||||
import com.example.livingai_lg.ui.screens.PostSaleSurveyScreen
|
import com.example.livingai_lg.ui.screens.auth.LandingScreen
|
||||||
import com.example.livingai_lg.ui.screens.SaleArchiveScreen
|
import com.example.livingai_lg.ui.screens.auth.OtpScreen
|
||||||
import com.example.livingai_lg.ui.screens.SellerProfileScreen
|
import com.example.livingai_lg.ui.screens.auth.SignInScreen
|
||||||
import com.example.livingai_lg.ui.screens.SortScreen
|
import com.example.livingai_lg.ui.screens.auth.SignUpScreen
|
||||||
import com.example.livingai_lg.ui.screens.auth.LandingScreen
|
import com.example.livingai_lg.ui.screens.chat.ChatScreen
|
||||||
import com.example.livingai_lg.ui.screens.auth.OtpScreen
|
import com.example.livingai_lg.ui.screens.chat.ChatsScreen
|
||||||
import com.example.livingai_lg.ui.screens.auth.SignInScreen
|
import com.example.livingai_lg.ui.screens.testing.ApiTestScreen
|
||||||
import com.example.livingai_lg.ui.screens.auth.SignUpScreen
|
|
||||||
import com.example.livingai_lg.ui.navigation.authNavGraph
|
fun NavController.navigateIfAuthenticated(
|
||||||
import com.example.livingai_lg.ui.navigation.mainNavGraph
|
authState: AuthState,
|
||||||
|
targetRoute: String,
|
||||||
object AppScreen {
|
fallbackRoute: String = AppScreen.LANDING
|
||||||
const val LANDING = "landing"
|
) {
|
||||||
const val SIGN_IN = "sign_in"
|
if (authState is AuthState.Authenticated) {
|
||||||
const val SIGN_UP = "sign_up"
|
navigate(targetRoute) {
|
||||||
|
launchSingleTop = true
|
||||||
const val OTP = "otp"
|
}
|
||||||
|
} else {
|
||||||
const val CHOOSE_SERVICE = "choose_service"
|
navigate(fallbackRoute) {
|
||||||
|
popUpTo(0) { inclusive = true }
|
||||||
const val CREATE_PROFILE = "create_profile"
|
launchSingleTop = true
|
||||||
|
}
|
||||||
const val BUY_ANIMALS = "buy_animals"
|
}
|
||||||
const val ANIMAL_PROFILE = "animal_profile"
|
}
|
||||||
|
|
||||||
const val CREATE_ANIMAL_LISTING = "create_animal_listing"
|
@Composable
|
||||||
|
fun AppNavigation(
|
||||||
const val BUY_ANIMALS_FILTERS = "buy_animals_filters"
|
authState: AuthState,
|
||||||
const val BUY_ANIMALS_SORT = "buy_animals_sort"
|
mainViewModel: MainViewModel
|
||||||
|
) {
|
||||||
const val SELLER_PROFILE = "seller_profile"
|
val navController = rememberNavController()
|
||||||
|
|
||||||
const val SALE_ARCHIVE = "sale_archive"
|
/* ---------------------------------------------------
|
||||||
|
* Collect one-time navigation events (OTP, login, logout)
|
||||||
const val POST_SALE_SURVEY = "post_sale_survey"
|
* --------------------------------------------------- */
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
const val SAVED_LISTINGS = "saved_listings"
|
mainViewModel.navEvents.collect { event ->
|
||||||
|
when (event) {
|
||||||
const val CONTACTS = "contacts"
|
|
||||||
|
is NavEvent.ToCreateProfile -> {
|
||||||
const val CALLS = "calls"
|
navController.navigate(
|
||||||
|
AppScreen.createProfile(event.name)
|
||||||
const val CHAT = "chat"
|
) {
|
||||||
|
popUpTo(0) { inclusive = true }
|
||||||
const val CHATS = "chats"
|
launchSingleTop = true
|
||||||
|
}
|
||||||
const val ACCOUNTS = "accounts"
|
}
|
||||||
|
|
||||||
fun chats(contact: String) =
|
is NavEvent.ToChooseService -> {
|
||||||
"$CHAT/$contact"
|
navController.navigate(
|
||||||
|
AppScreen.chooseService(event.profileId)
|
||||||
fun otp(phone: String, name: String) =
|
) {
|
||||||
"$OTP/$phone/$name"
|
popUpTo(0) { inclusive = true }
|
||||||
|
launchSingleTop = true
|
||||||
fun otpWithSignup(phone: String, name: String, state: String, district: String, village: String) =
|
}
|
||||||
"$OTP/$phone/$name/$state/$district/$village"
|
}
|
||||||
|
|
||||||
fun createProfile(name: String) =
|
NavEvent.ToLanding -> {
|
||||||
"$CREATE_PROFILE/$name"
|
navController.navigate(AppScreen.LANDING) {
|
||||||
|
popUpTo(0) { inclusive = true }
|
||||||
fun chooseService(profileId: String) =
|
launchSingleTop = true
|
||||||
"$CHOOSE_SERVICE/$profileId"
|
}
|
||||||
fun postSaleSurvey(animalId: String) =
|
}
|
||||||
"$POST_SALE_SURVEY/$animalId"
|
}
|
||||||
|
}
|
||||||
fun animalProfile(animalId: String) =
|
}
|
||||||
"$ANIMAL_PROFILE/$animalId"
|
|
||||||
|
val onNavClick: (String) -> Unit = { route ->
|
||||||
fun sellerProfile(sellerId: String) =
|
val currentRoute =
|
||||||
"$SELLER_PROFILE/$sellerId"
|
navController.currentBackStackEntry?.destination?.route
|
||||||
|
Log.d("Current Route:"," $currentRoute $route")
|
||||||
fun saleArchive(saleId: String) =
|
|
||||||
"$SALE_ARCHIVE/$saleId"
|
if (currentRoute != route) {
|
||||||
|
navController.navigate(route) {
|
||||||
}
|
launchSingleTop = true
|
||||||
|
//restoreState = true
|
||||||
object Graph {
|
// popUpTo(navController.graph.startDestinationId) {
|
||||||
const val AUTH = "auth"
|
// saveState = true
|
||||||
const val MAIN = "main"
|
// }
|
||||||
|
}
|
||||||
fun auth(route: String)=
|
}
|
||||||
"$AUTH/$route"
|
}
|
||||||
|
|
||||||
fun main(route: String)=
|
/* ---------------------------------------------------
|
||||||
"$MAIN/$route"
|
* Single NavHost
|
||||||
}
|
* --------------------------------------------------- */
|
||||||
@Composable
|
NavHost(
|
||||||
fun AppNavigation(
|
navController = navController,
|
||||||
authState: AuthState,
|
startDestination = AppScreen.LANDING
|
||||||
mainViewModel: com.example.livingai_lg.ui.MainViewModel
|
) {
|
||||||
) {
|
|
||||||
val navController = rememberNavController()
|
/* ---------------- AUTH ---------------- */
|
||||||
|
|
||||||
// Determine start destination based on initial auth state
|
composable(AppScreen.LANDING) {
|
||||||
// This prevents showing landing screen when user is already logged in
|
LandingScreen(
|
||||||
val startDestination = remember(authState) {
|
onSignUpClick = {
|
||||||
when (authState) {
|
navController.navigate(AppScreen.SIGN_UP)
|
||||||
is AuthState.Authenticated -> Graph.MAIN
|
},
|
||||||
is AuthState.Unauthenticated -> Graph.AUTH
|
onSignInClick = {
|
||||||
is AuthState.Unknown -> Graph.AUTH // Show landing while checking
|
navController.navigate(AppScreen.SIGN_IN)
|
||||||
}
|
},
|
||||||
}
|
onGuestClick = {
|
||||||
|
mainViewModel.emitNavEvent(
|
||||||
NavHost(
|
NavEvent.ToCreateProfile("guest")
|
||||||
navController = navController,
|
)
|
||||||
startDestination = startDestination
|
}
|
||||||
) {
|
)
|
||||||
authNavGraph(navController, mainViewModel)
|
}
|
||||||
mainNavGraph(navController)
|
|
||||||
}
|
composable(AppScreen.SIGN_IN) {
|
||||||
|
SignInScreen(
|
||||||
// Handle navigation based on auth state changes
|
onSignUpClick = {
|
||||||
LaunchedEffect(authState) {
|
navController.navigate(AppScreen.SIGN_UP)
|
||||||
android.util.Log.d("AppNavigation", "LaunchedEffect triggered with authState: $authState")
|
},
|
||||||
when (authState) {
|
onSignInClick = { phone, name ->
|
||||||
is AuthState.Authenticated -> {
|
navController.navigate(
|
||||||
// User is authenticated, navigate to ChooseServiceScreen
|
AppScreen.otp(phone, name)
|
||||||
// Add a small delay to ensure NavHost graphs are fully built
|
)
|
||||||
delay(100)
|
}
|
||||||
|
)
|
||||||
val currentRoute = navController.currentBackStackEntry?.destination?.route
|
}
|
||||||
android.util.Log.d("AppNavigation", "Authenticated - currentRoute: $currentRoute")
|
|
||||||
// Only navigate if we're not already in the MAIN graph or ChooseServiceScreen
|
composable(AppScreen.SIGN_UP) {
|
||||||
if (currentRoute?.startsWith(Graph.MAIN) != true &&
|
SignUpScreen(
|
||||||
currentRoute?.startsWith(AppScreen.CHOOSE_SERVICE) != true) {
|
onSignUpClick = { phone, name, state, district, village ->
|
||||||
android.util.Log.d("AppNavigation", "Navigating to ChooseServiceScreen (default profileId: 1)")
|
navController.navigate(
|
||||||
try {
|
AppScreen.otpWithSignup(
|
||||||
// Navigate directly to the start destination route of MAIN graph
|
phone, name, state, district, village
|
||||||
// This avoids the "Sequence is empty" error when navigating to Graph.MAIN
|
)
|
||||||
navController.navigate(AppScreen.chooseService("1")) {
|
)
|
||||||
// Clear back stack to prevent going back to auth screens
|
},
|
||||||
popUpTo(Graph.AUTH) { inclusive = true }
|
onSignInClick = {
|
||||||
// Prevent multiple navigations
|
navController.navigate(AppScreen.SIGN_IN)
|
||||||
launchSingleTop = true
|
}
|
||||||
}
|
)
|
||||||
} catch (e: Exception) {
|
}
|
||||||
android.util.Log.e("AppNavigation", "Navigation error: ${e.message}", e)
|
|
||||||
// Fallback: try navigating to Graph.MAIN if direct route fails
|
composable(
|
||||||
try {
|
"${AppScreen.OTP}/{phoneNumber}/{name}",
|
||||||
navController.navigate(Graph.MAIN) {
|
arguments = listOf(
|
||||||
popUpTo(Graph.AUTH) { inclusive = true }
|
navArgument("phoneNumber") { type = NavType.StringType },
|
||||||
launchSingleTop = true
|
navArgument("name") { type = NavType.StringType }
|
||||||
}
|
)
|
||||||
} catch (e2: Exception) {
|
) { backStackEntry ->
|
||||||
android.util.Log.e("AppNavigation", "Fallback navigation also failed: ${e2.message}", e2)
|
OtpScreen(
|
||||||
}
|
mainViewModel = mainViewModel,
|
||||||
}
|
phoneNumber = backStackEntry.arguments?.getString("phoneNumber") ?: "",
|
||||||
} else {
|
name = backStackEntry.arguments?.getString("name") ?: "",
|
||||||
android.util.Log.d("AppNavigation", "Already in MAIN graph or ChooseServiceScreen, skipping navigation")
|
onCreateProfile = { name ->
|
||||||
}
|
// navController.navigateIfAuthenticated(
|
||||||
}
|
// authState,
|
||||||
is AuthState.Unauthenticated -> {
|
// AppScreen.createProfile(name),
|
||||||
// User is not authenticated, ensure we're in auth graph (landing screen)
|
// AppScreen.LANDING
|
||||||
val currentRoute = navController.currentBackStackEntry?.destination?.route
|
// )
|
||||||
if (currentRoute?.startsWith(Graph.MAIN) == true ||
|
mainViewModel.emitNavEvent(
|
||||||
currentRoute?.startsWith(Graph.AUTH) != true) {
|
NavEvent.ToCreateProfile(name)
|
||||||
navController.navigate(Graph.AUTH) {
|
)
|
||||||
// Clear back stack to prevent going back to main screens
|
},
|
||||||
popUpTo(0) { inclusive = true }
|
onSuccess = {
|
||||||
launchSingleTop = true
|
// navController.navigateIfAuthenticated(
|
||||||
}
|
// authState,
|
||||||
}
|
// AppScreen.chooseService(""),
|
||||||
}
|
// AppScreen.LANDING
|
||||||
is AuthState.Unknown -> {
|
// )
|
||||||
// Still checking auth status
|
mainViewModel.emitNavEvent(
|
||||||
// If we're on landing screen, stay there
|
NavEvent.ToChooseService()
|
||||||
// If we're on main screen and checking, don't navigate yet
|
)
|
||||||
// This prevents flickering during token validation
|
}
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// MainNavGraph(navController)
|
composable(
|
||||||
// AuthNavGraph(navController)
|
"${AppScreen.OTP}/{phoneNumber}/{name}/{state}/{district}/{village}",
|
||||||
// when (authState) {
|
arguments = listOf(
|
||||||
// is AuthState.Unauthenticated -> {AuthNavGraph()}
|
navArgument("phoneNumber") { type = NavType.StringType },
|
||||||
// is AuthState.Authenticated -> {MainNavGraph()}
|
navArgument("name") { type = NavType.StringType },
|
||||||
// is AuthState.Unknown -> {
|
navArgument("state") { type = NavType.StringType },
|
||||||
// Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
navArgument("district") { type = NavType.StringType },
|
||||||
// CircularProgressIndicator()
|
navArgument("village") { type = NavType.StringType }
|
||||||
// }
|
)
|
||||||
// }
|
) { backStackEntry ->
|
||||||
// AuthState.Loading -> SplashScreen()
|
OtpScreen(
|
||||||
// }
|
mainViewModel = mainViewModel,
|
||||||
|
phoneNumber = backStackEntry.arguments?.getString("phoneNumber") ?: "",
|
||||||
// val onNavClick: (String) -> Unit = { route ->
|
name = backStackEntry.arguments?.getString("name") ?: "",
|
||||||
// val currentRoute =
|
signupState = backStackEntry.arguments?.getString("state"),
|
||||||
// navController.currentBackStackEntry?.destination?.route
|
signupDistrict = backStackEntry.arguments?.getString("district"),
|
||||||
//
|
signupVillage = backStackEntry.arguments?.getString("village"),
|
||||||
// if (currentRoute != route) {
|
onCreateProfile = { name ->
|
||||||
// navController.navigate(route) {
|
mainViewModel.emitNavEvent(
|
||||||
// launchSingleTop = true
|
NavEvent.ToCreateProfile(name)
|
||||||
// restoreState = true
|
)
|
||||||
// popUpTo(navController.graph.startDestinationId) {
|
},
|
||||||
// saveState = true
|
onSuccess = {
|
||||||
// }
|
mainViewModel.emitNavEvent(
|
||||||
// }
|
NavEvent.ToChooseService()
|
||||||
// }
|
)
|
||||||
// }
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// NavHost(
|
/* ---------------- MAIN ---------------- */
|
||||||
// navController = navController,
|
|
||||||
// startDestination = AppScreen.LANDING,
|
composable(
|
||||||
//
|
"${AppScreen.CREATE_PROFILE}/{name}",
|
||||||
// ) {
|
arguments = listOf(
|
||||||
// composable(AppScreen.LANDING) {
|
navArgument("name") { type = NavType.StringType }
|
||||||
// LandingScreen(
|
)
|
||||||
// onSignUpClick = { navController.navigate(AppScreen.SIGN_UP) },
|
) { backStackEntry ->
|
||||||
// onSignInClick = { navController.navigate(AppScreen.SIGN_IN) },
|
CreateProfileScreen(
|
||||||
// onGuestClick = { navController.navigate(AppScreen.CREATE_PROFILE) }
|
name = backStackEntry.arguments?.getString("name") ?: "",
|
||||||
// )
|
onProfileSelected = { profileId ->
|
||||||
// }
|
mainViewModel.emitNavEvent(
|
||||||
//
|
NavEvent.ToChooseService(profileId)
|
||||||
// composable(AppScreen.SIGN_IN) {
|
)
|
||||||
// SignInScreen(
|
}
|
||||||
// onSignUpClick = { navController.navigate(AppScreen.SIGN_UP) },
|
)
|
||||||
// onSignInClick = {
|
}
|
||||||
// navController.navigate(AppScreen.OTP)
|
|
||||||
// }
|
composable(
|
||||||
// )
|
"${AppScreen.CHOOSE_SERVICE}/{profileId}",
|
||||||
// }
|
arguments = listOf(
|
||||||
//
|
navArgument("profileId") { type = NavType.StringType }
|
||||||
// composable(AppScreen.SIGN_UP) {
|
)
|
||||||
// SignUpScreen(
|
) { backStackEntry ->
|
||||||
// onSignUpClick = {
|
ChooseServiceScreen(
|
||||||
// navController.navigate(AppScreen.OTP)
|
profileId = backStackEntry.arguments?.getString("profileId") ?: "",
|
||||||
// },
|
onServiceSelected = {
|
||||||
// onSignInClick = {
|
navController.navigate(AppScreen.BUY_ANIMALS)
|
||||||
// navController.navigate(AppScreen.SIGN_IN) {
|
// navController.navigateIfAuthenticated(
|
||||||
// popUpTo(AppScreen.SIGN_UP) { inclusive = true }
|
// authState,
|
||||||
// }
|
// AppScreen.BUY_ANIMALS
|
||||||
// }
|
// )
|
||||||
// )
|
}
|
||||||
// }
|
)
|
||||||
//
|
}
|
||||||
// composable(AppScreen.OTP) {
|
|
||||||
// OtpScreen(
|
composable(AppScreen.BUY_ANIMALS) {
|
||||||
// onContinue = { navController.navigate(AppScreen.CREATE_PROFILE) }
|
BuyScreen(
|
||||||
// )
|
onBackClick = {
|
||||||
// }
|
navController.popBackStack()
|
||||||
|
},
|
||||||
// composable(AppScreen.CREATE_PROFILE) {
|
onProductClick = { animalId ->
|
||||||
// CreateProfileScreen (
|
navController.navigate(
|
||||||
// onProfileSelected = { profileId ->
|
AppScreen.animalProfile(animalId)
|
||||||
// if (profileId == "buyer_seller") {
|
)
|
||||||
// navController.navigate(AppScreen.BUY_ANIMALS)
|
},
|
||||||
// } else {
|
onNavClick = onNavClick,
|
||||||
// navController.navigate(AppScreen.CHOOSE_SERVICE)
|
onSellerClick = { sellerId ->
|
||||||
// } },
|
navController.navigate(
|
||||||
// )
|
AppScreen.sellerProfile(sellerId)
|
||||||
// }
|
)
|
||||||
//
|
},
|
||||||
// composable(AppScreen.CHOOSE_SERVICE) {
|
)
|
||||||
// ChooseServiceScreen (
|
}
|
||||||
// onServiceSelected = { navController.navigate(AppScreen.LANDING) },
|
|
||||||
// )
|
composable(AppScreen.BUY_ANIMALS_FILTERS) {
|
||||||
// }
|
FilterScreen(
|
||||||
//
|
onBackClick = {
|
||||||
// composable(AppScreen.BUY_ANIMALS) {
|
navController.popBackStack()
|
||||||
// BuyScreen(
|
},
|
||||||
// onBackClick = {
|
onSubmitClick = {navController.navigate(AppScreen.BUY_ANIMALS)},
|
||||||
// navController.popBackStack()
|
onCancelClick = {
|
||||||
// },
|
navController.popBackStack()
|
||||||
// onProductClick = { animalId ->
|
},
|
||||||
// navController.navigate(
|
)
|
||||||
// AppScreen.animalProfile(animalId)
|
}
|
||||||
// )
|
|
||||||
// },
|
composable(AppScreen.BUY_ANIMALS_SORT) {
|
||||||
// onNavClick = onNavClick,
|
SortScreen(
|
||||||
// onFilterClick = {navController.navigate(AppScreen.BUY_ANIMALS_FILTERS)},
|
onApplyClick = {navController.navigate(AppScreen.BUY_ANIMALS)},
|
||||||
// onSortClick = {navController.navigate(AppScreen.BUY_ANIMALS_SORT)},
|
onBackClick = {
|
||||||
// onSellerClick = { sellerId ->
|
navController.popBackStack()
|
||||||
// navController.navigate(
|
},
|
||||||
// AppScreen.sellerProfile(sellerId)
|
onCancelClick = {
|
||||||
// )
|
navController.popBackStack()
|
||||||
// },
|
},
|
||||||
// )
|
|
||||||
// }
|
)
|
||||||
//
|
}
|
||||||
// composable(AppScreen.BUY_ANIMALS_FILTERS) {
|
|
||||||
// FilterScreen(
|
composable(AppScreen.SAVED_LISTINGS) {
|
||||||
// onSubmitClick = {navController.navigate(AppScreen.BUY_ANIMALS)},
|
SavedListingsScreen(
|
||||||
// onBackClick = {
|
onNavClick = onNavClick,
|
||||||
// navController.popBackStack()
|
onBackClick = { navController.popBackStack() })
|
||||||
// },
|
}
|
||||||
// onSkipClick = {
|
|
||||||
// navController.popBackStack()
|
composable(AppScreen.ACCOUNTS) {
|
||||||
// },
|
AccountsScreen(
|
||||||
// onCancelClick = {
|
onBackClick = { navController.popBackStack() },
|
||||||
// navController.popBackStack()
|
onLogout = {
|
||||||
// },
|
// Navigate to auth graph after logout
|
||||||
//
|
navController.navigate(AppScreen.LANDING) {
|
||||||
// )
|
popUpTo(0) { inclusive = true }
|
||||||
// }
|
}
|
||||||
//
|
},
|
||||||
// composable(AppScreen.BUY_ANIMALS_SORT) {
|
onApiTest = {
|
||||||
// SortScreen(
|
navController.navigate(AppScreen.API_TEST)
|
||||||
// onApplyClick = {navController.navigate(AppScreen.BUY_ANIMALS)},
|
}
|
||||||
// onBackClick = {
|
)
|
||||||
// navController.popBackStack()
|
}
|
||||||
// },
|
|
||||||
// onSkipClick = {
|
|
||||||
// navController.popBackStack()
|
composable(AppScreen.CREATE_ANIMAL_LISTING) {
|
||||||
// },
|
NewListingScreen (
|
||||||
// onCancelClick = {
|
onSaveClick = {navController.navigate(
|
||||||
// navController.popBackStack()
|
AppScreen.postSaleSurvey("2")
|
||||||
// },
|
)},
|
||||||
//
|
onBackClick = {
|
||||||
// )
|
navController.navigate(AppScreen.BUY_ANIMALS){
|
||||||
// }
|
popUpTo(AppScreen.BUY_ANIMALS){
|
||||||
//
|
inclusive = true
|
||||||
// composable(AppScreen.CREATE_ANIMAL_LISTING) {
|
}
|
||||||
// NewListingScreen (
|
}
|
||||||
// onSaveClick = {navController.navigate(
|
|
||||||
// AppScreen.postSaleSurvey("2")
|
}
|
||||||
// )},
|
)
|
||||||
// onBackClick = {
|
}
|
||||||
// navController.popBackStack()
|
|
||||||
// }
|
composable(
|
||||||
// )
|
route = "${AppScreen.SALE_ARCHIVE}/{saleId}",
|
||||||
// }
|
arguments = listOf(
|
||||||
//
|
navArgument("saleId") { type = NavType.StringType }
|
||||||
// composable(
|
)
|
||||||
// route = "${AppScreen.SALE_ARCHIVE}/{saleId}",
|
) { backStackEntry ->
|
||||||
// arguments = listOf(
|
|
||||||
// navArgument("saleId") { type = NavType.StringType }
|
val saleId = backStackEntry
|
||||||
// )
|
.arguments
|
||||||
// ) { backStackEntry ->
|
?.getString("saleId")
|
||||||
//
|
?: return@composable
|
||||||
// val saleId = backStackEntry
|
|
||||||
// .arguments
|
SaleArchiveScreen(
|
||||||
// ?.getString("saleId")
|
saleId = saleId,
|
||||||
// ?: return@composable
|
onBackClick = {
|
||||||
//
|
navController.popBackStack()
|
||||||
// SaleArchiveScreen(
|
},
|
||||||
// saleId = saleId,
|
onSaleSurveyClick = { saleId ->
|
||||||
// onBackClick = {
|
navController.navigate(
|
||||||
// navController.popBackStack()
|
AppScreen.sellerProfile(saleId)
|
||||||
// },
|
)
|
||||||
// onSaleSurveyClick = { saleId ->
|
},
|
||||||
// navController.navigate(
|
)
|
||||||
// AppScreen.sellerProfile(saleId)
|
}
|
||||||
// )
|
|
||||||
// },
|
composable(
|
||||||
// )
|
route = "${AppScreen.POST_SALE_SURVEY}/{animalId}",
|
||||||
// }
|
arguments = listOf(
|
||||||
//
|
navArgument("animalId") { type = NavType.StringType }
|
||||||
// composable(
|
)
|
||||||
// route = "${AppScreen.POST_SALE_SURVEY}/{animalId}",
|
) { backStackEntry ->
|
||||||
// arguments = listOf(
|
|
||||||
// navArgument("animalId") { type = NavType.StringType }
|
val animalId = backStackEntry
|
||||||
// )
|
.arguments
|
||||||
// ) { backStackEntry ->
|
?.getString("animalId")
|
||||||
//
|
?: return@composable
|
||||||
// val animalId = backStackEntry
|
|
||||||
// .arguments
|
PostSaleSurveyScreen (
|
||||||
// ?.getString("animalId")
|
animalId = animalId,
|
||||||
// ?: return@composable
|
onBackClick = {
|
||||||
//
|
navController.navigate(AppScreen.CREATE_ANIMAL_LISTING)
|
||||||
// PostSaleSurveyScreen (
|
},
|
||||||
// animalId = animalId,
|
onSubmit = {navController.navigate(
|
||||||
// onBackClick = {
|
AppScreen.saleArchive("2")
|
||||||
// navController.popBackStack()
|
)}
|
||||||
// },
|
)
|
||||||
// onSubmit = {navController.navigate(
|
}
|
||||||
// AppScreen.saleArchive("2")
|
|
||||||
// )}
|
composable(
|
||||||
// )
|
route = "${AppScreen.ANIMAL_PROFILE}/{animalId}",
|
||||||
// }
|
arguments = listOf(
|
||||||
//
|
navArgument("animalId") { type = NavType.StringType }
|
||||||
// composable(
|
)
|
||||||
// route = "${AppScreen.ANIMAL_PROFILE}/{animalId}",
|
) { backStackEntry ->
|
||||||
// arguments = listOf(
|
|
||||||
// navArgument("animalId") { type = NavType.StringType }
|
val animalId = backStackEntry
|
||||||
// )
|
.arguments
|
||||||
// ) { backStackEntry ->
|
?.getString("animalId")
|
||||||
//
|
?: return@composable
|
||||||
// val animalId = backStackEntry
|
|
||||||
// .arguments
|
AnimalProfileScreen(
|
||||||
// ?.getString("animalId")
|
animalId = animalId,
|
||||||
// ?: return@composable
|
onBackClick = {
|
||||||
//
|
navController.popBackStack()
|
||||||
// AnimalProfileScreen(
|
},
|
||||||
// animalId = animalId,
|
onNavClick = onNavClick,
|
||||||
// onBackClick = {
|
onSellerClick = { sellerId ->
|
||||||
// navController.popBackStack()
|
navController.navigate(
|
||||||
// },
|
AppScreen.sellerProfile(sellerId)
|
||||||
// onSellerClick = { sellerId ->
|
)
|
||||||
// navController.navigate(
|
},
|
||||||
// AppScreen.sellerProfile(sellerId)
|
)
|
||||||
// )
|
}
|
||||||
// },
|
|
||||||
// )
|
composable(
|
||||||
// }
|
route = "${AppScreen.SELLER_PROFILE}/{sellerId}",
|
||||||
//
|
arguments = listOf(
|
||||||
// composable(
|
navArgument("sellerId") { type = NavType.StringType }
|
||||||
// route = "${AppScreen.SELLER_PROFILE}/{sellerId}",
|
)
|
||||||
// arguments = listOf(
|
) { backStackEntry ->
|
||||||
// navArgument("sellerId") { type = NavType.StringType }
|
|
||||||
// )
|
val sellerId = backStackEntry
|
||||||
// ) { backStackEntry ->
|
.arguments
|
||||||
//
|
?.getString("sellerId")
|
||||||
// val sellerId = backStackEntry
|
?: return@composable
|
||||||
// .arguments
|
|
||||||
// ?.getString("sellerId")
|
SellerProfileScreen(
|
||||||
// ?: return@composable
|
sellerId = sellerId,
|
||||||
//
|
onBackClick = {
|
||||||
// SellerProfileScreen(
|
navController.popBackStack()
|
||||||
// sellerId = sellerId,
|
}
|
||||||
// onBackClick = {
|
)
|
||||||
// navController.popBackStack()
|
}
|
||||||
// }
|
|
||||||
// )
|
composable(AppScreen.CONTACTS) {
|
||||||
// }
|
ContactsScreen(
|
||||||
// composable(AppScreen.SELLER_PROFILE) {
|
onBackClick = {navController.navigate(AppScreen.BUY_ANIMALS)},//{navController.popBackStack()},
|
||||||
// SellerProfileScreen (
|
onTabClick = onNavClick,
|
||||||
// onBackClick = {
|
)
|
||||||
// navController.popBackStack()
|
}
|
||||||
// }
|
|
||||||
// )
|
composable(AppScreen.CALLS) {
|
||||||
// }
|
CallsScreen(
|
||||||
|
onBackClick = {navController.navigate(AppScreen.BUY_ANIMALS)},//{navController.popBackStack()},
|
||||||
|
onTabClick = onNavClick,
|
||||||
// }
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
composable(AppScreen.CHATS) {
|
||||||
|
ChatsScreen(
|
||||||
|
onBackClick = {navController.navigate(AppScreen.BUY_ANIMALS)},//{navController.popBackStack()},
|
||||||
|
onTabClick = onNavClick,
|
||||||
|
onChatItemClick = {navController.navigate(AppScreen.chats("2"))}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
composable(
|
||||||
|
route = "${AppScreen.CHAT}/{contact}",
|
||||||
|
arguments = listOf(
|
||||||
|
navArgument("contact") { type = NavType.StringType }
|
||||||
|
)
|
||||||
|
) { backStackEntry ->
|
||||||
|
|
||||||
|
val sellerId = backStackEntry
|
||||||
|
.arguments
|
||||||
|
?.getString("contact")
|
||||||
|
?: return@composable
|
||||||
|
|
||||||
|
ChatScreen(
|
||||||
|
sellerId,
|
||||||
|
onBackClick = {
|
||||||
|
navController.navigate(AppScreen.CHATS)
|
||||||
|
//navController.popBackStack()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
//A route for testing
|
||||||
|
composable(AppScreen.API_TEST) {
|
||||||
|
ApiTestScreen(
|
||||||
|
onBackClick = { navController.popBackStack() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
package com.example.livingai_lg.ui.navigation
|
||||||
|
|
||||||
|
object AppScreen {
|
||||||
|
const val LANDING = "landing"
|
||||||
|
const val SIGN_IN = "sign_in"
|
||||||
|
const val SIGN_UP = "sign_up"
|
||||||
|
|
||||||
|
const val OTP = "otp"
|
||||||
|
|
||||||
|
fun otp(phone: String, name: String) =
|
||||||
|
"$OTP/$phone/$name"
|
||||||
|
|
||||||
|
fun otpWithSignup(phone: String, name: String, state: String, district: String, village: String) =
|
||||||
|
"$OTP/$phone/$name/$state/$district/$village"
|
||||||
|
|
||||||
|
const val CHOOSE_SERVICE = "choose_service"
|
||||||
|
fun chooseService(profileId: String?) =
|
||||||
|
"$CHOOSE_SERVICE/$profileId"
|
||||||
|
|
||||||
|
const val CREATE_PROFILE = "create_profile"
|
||||||
|
|
||||||
|
fun createProfile(name: String) =
|
||||||
|
"$CREATE_PROFILE/$name"
|
||||||
|
|
||||||
|
const val BUY_ANIMALS = "buy_animals"
|
||||||
|
const val ANIMAL_PROFILE = "animal_profile"
|
||||||
|
fun animalProfile(animalId: String) =
|
||||||
|
"$ANIMAL_PROFILE/$animalId"
|
||||||
|
|
||||||
|
const val CREATE_ANIMAL_LISTING = "create_animal_listing"
|
||||||
|
|
||||||
|
const val BUY_ANIMALS_FILTERS = "buy_animals_filters"
|
||||||
|
const val BUY_ANIMALS_SORT = "buy_animals_sort"
|
||||||
|
|
||||||
|
const val SELLER_PROFILE = "seller_profile"
|
||||||
|
fun sellerProfile(sellerId: String) =
|
||||||
|
"$SELLER_PROFILE/$sellerId"
|
||||||
|
|
||||||
|
const val SALE_ARCHIVE = "sale_archive"
|
||||||
|
fun saleArchive(saleId: String) =
|
||||||
|
"$SALE_ARCHIVE/$saleId"
|
||||||
|
|
||||||
|
const val POST_SALE_SURVEY = "post_sale_survey"
|
||||||
|
fun postSaleSurvey(animalId: String) =
|
||||||
|
"$POST_SALE_SURVEY/$animalId"
|
||||||
|
|
||||||
|
const val SAVED_LISTINGS = "saved_listings"
|
||||||
|
|
||||||
|
const val CONTACTS = "contacts"
|
||||||
|
|
||||||
|
fun chats(contact: String) =
|
||||||
|
"$CHAT/$contact"
|
||||||
|
|
||||||
|
const val CALLS = "calls"
|
||||||
|
|
||||||
|
const val CHAT = "chat"
|
||||||
|
|
||||||
|
const val CHATS = "chats"
|
||||||
|
|
||||||
|
const val ACCOUNTS = "accounts"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Test screens::
|
||||||
|
const val API_TEST = "api_test"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
package com.example.livingai_lg.ui.navigation
|
||||||
|
sealed class NavEvent {
|
||||||
|
data class ToCreateProfile(val name: String) : NavEvent()
|
||||||
|
data class ToChooseService(val profileId: String = "") : NavEvent()
|
||||||
|
object ToLanding : NavEvent()
|
||||||
|
}
|
||||||
|
|
@ -1,27 +1,25 @@
|
||||||
package com.example.livingai_lg.ui.navigation
|
package com.example.livingai_lg.ui.navigation.navigation_legacy
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import android.util.Log
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.NavGraphBuilder
|
import androidx.navigation.NavGraphBuilder
|
||||||
import androidx.navigation.NavHostController
|
|
||||||
import androidx.navigation.NavType
|
import androidx.navigation.NavType
|
||||||
import androidx.navigation.compose.NavHost
|
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.navigation
|
import androidx.navigation.compose.navigation
|
||||||
import androidx.navigation.compose.rememberNavController
|
|
||||||
import androidx.navigation.navArgument
|
import androidx.navigation.navArgument
|
||||||
|
import com.example.livingai_lg.ui.MainViewModel
|
||||||
|
import com.example.livingai_lg.ui.navigation.AppScreen
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
// Note: CoroutineScope, Dispatchers, delay, launch still used in onCreateProfile callbacks
|
// Note: CoroutineScope, Dispatchers, delay, launch still used in onCreateProfile callbacks
|
||||||
import com.example.livingai_lg.ui.screens.SaleArchiveScreen
|
|
||||||
import com.example.livingai_lg.ui.screens.auth.LandingScreen
|
import com.example.livingai_lg.ui.screens.auth.LandingScreen
|
||||||
import com.example.livingai_lg.ui.screens.auth.OtpScreen
|
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.SignInScreen
|
||||||
import com.example.livingai_lg.ui.screens.auth.SignUpScreen
|
import com.example.livingai_lg.ui.screens.auth.SignUpScreen
|
||||||
|
|
||||||
fun NavGraphBuilder.authNavGraph(navController: NavController, mainViewModel: com.example.livingai_lg.ui.MainViewModel) {
|
fun NavGraphBuilder.authNavGraph(navController: NavController, mainViewModel: MainViewModel) {
|
||||||
navigation(
|
navigation(
|
||||||
route = Graph.AUTH,
|
route = Graph.AUTH,
|
||||||
startDestination = AppScreen.LANDING
|
startDestination = AppScreen.LANDING
|
||||||
|
|
@ -70,7 +68,7 @@ fun NavGraphBuilder.authNavGraph(navController: NavController, mainViewModel: co
|
||||||
name = backStackEntry.arguments?.getString("name") ?: "",
|
name = backStackEntry.arguments?.getString("name") ?: "",
|
||||||
mainViewModel = mainViewModel,
|
mainViewModel = mainViewModel,
|
||||||
onCreateProfile = {name ->
|
onCreateProfile = {name ->
|
||||||
android.util.Log.d("AuthNavGraph", "Navigating to create profile with name: $name")
|
Log.d("AuthNavGraph", "Navigating to create profile with name: $name")
|
||||||
// Navigate to main graph first without popping, then navigate to specific route
|
// Navigate to main graph first without popping, then navigate to specific route
|
||||||
try {
|
try {
|
||||||
// Navigate to main graph (this will use its start destination)
|
// Navigate to main graph (this will use its start destination)
|
||||||
|
|
@ -87,15 +85,15 @@ fun NavGraphBuilder.authNavGraph(navController: NavController, mainViewModel: co
|
||||||
popUpTo(Graph.AUTH) { inclusive = true }
|
popUpTo(Graph.AUTH) { inclusive = true }
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
android.util.Log.e("AuthNavGraph", "Secondary navigation error: ${e.message}", e)
|
Log.e("AuthNavGraph", "Secondary navigation error: ${e.message}", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
android.util.Log.e("AuthNavGraph", "Navigation error: ${e.message}", e)
|
Log.e("AuthNavGraph", "Navigation error: ${e.message}", e)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLanding = {
|
onLanding = {
|
||||||
android.util.Log.d("AuthNavGraph", "Navigating to landing page for new user")
|
Log.d("AuthNavGraph", "Navigating to landing page for new user")
|
||||||
// Navigate to landing page within AUTH graph
|
// Navigate to landing page within AUTH graph
|
||||||
try {
|
try {
|
||||||
navController.navigate(AppScreen.LANDING) {
|
navController.navigate(AppScreen.LANDING) {
|
||||||
|
|
@ -104,11 +102,11 @@ fun NavGraphBuilder.authNavGraph(navController: NavController, mainViewModel: co
|
||||||
launchSingleTop = true
|
launchSingleTop = true
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
android.util.Log.e("AuthNavGraph", "Navigation to landing error: ${e.message}", e)
|
Log.e("AuthNavGraph", "Navigation to landing error: ${e.message}", e)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
android.util.Log.d("AuthNavGraph", "Sign-in successful - navigating to ChooseServiceScreen")
|
Log.d("AuthNavGraph", "Sign-in successful - navigating to ChooseServiceScreen")
|
||||||
// Navigate to MAIN graph which starts at ChooseServiceScreen (startDestination)
|
// Navigate to MAIN graph which starts at ChooseServiceScreen (startDestination)
|
||||||
try {
|
try {
|
||||||
navController.navigate(Graph.MAIN) {
|
navController.navigate(Graph.MAIN) {
|
||||||
|
|
@ -117,7 +115,7 @@ fun NavGraphBuilder.authNavGraph(navController: NavController, mainViewModel: co
|
||||||
launchSingleTop = true
|
launchSingleTop = true
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
android.util.Log.e("AuthNavGraph", "Navigation error: ${e.message}", e)
|
Log.e("AuthNavGraph", "Navigation error: ${e.message}", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -141,7 +139,7 @@ fun NavGraphBuilder.authNavGraph(navController: NavController, mainViewModel: co
|
||||||
signupVillage = backStackEntry.arguments?.getString("village"),
|
signupVillage = backStackEntry.arguments?.getString("village"),
|
||||||
mainViewModel = mainViewModel,
|
mainViewModel = mainViewModel,
|
||||||
onCreateProfile = {name ->
|
onCreateProfile = {name ->
|
||||||
android.util.Log.d("AuthNavGraph", "Navigating to create profile with name: $name")
|
Log.d("AuthNavGraph", "Navigating to create profile with name: $name")
|
||||||
// Navigate to main graph first without popping, then navigate to specific route
|
// Navigate to main graph first without popping, then navigate to specific route
|
||||||
try {
|
try {
|
||||||
// Navigate to main graph (this will use its start destination)
|
// Navigate to main graph (this will use its start destination)
|
||||||
|
|
@ -158,15 +156,15 @@ fun NavGraphBuilder.authNavGraph(navController: NavController, mainViewModel: co
|
||||||
popUpTo(Graph.AUTH) { inclusive = true }
|
popUpTo(Graph.AUTH) { inclusive = true }
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
android.util.Log.e("AuthNavGraph", "Secondary navigation error: ${e.message}", e)
|
Log.e("AuthNavGraph", "Secondary navigation error: ${e.message}", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
android.util.Log.e("AuthNavGraph", "Navigation error: ${e.message}", e)
|
Log.e("AuthNavGraph", "Navigation error: ${e.message}", e)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLanding = {
|
onLanding = {
|
||||||
android.util.Log.d("AuthNavGraph", "Navigating to landing page for new user")
|
Log.d("AuthNavGraph", "Navigating to landing page for new user")
|
||||||
// Navigate to landing page within AUTH graph
|
// Navigate to landing page within AUTH graph
|
||||||
try {
|
try {
|
||||||
navController.navigate(AppScreen.LANDING) {
|
navController.navigate(AppScreen.LANDING) {
|
||||||
|
|
@ -175,11 +173,11 @@ fun NavGraphBuilder.authNavGraph(navController: NavController, mainViewModel: co
|
||||||
launchSingleTop = true
|
launchSingleTop = true
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
android.util.Log.e("AuthNavGraph", "Navigation to landing error: ${e.message}", e)
|
Log.e("AuthNavGraph", "Navigation to landing error: ${e.message}", e)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
android.util.Log.d("AuthNavGraph", "Sign-in successful - navigating to ChooseServiceScreen")
|
Log.d("AuthNavGraph", "Sign-in successful - navigating to ChooseServiceScreen")
|
||||||
// Navigate to MAIN graph which starts at ChooseServiceScreen (startDestination)
|
// Navigate to MAIN graph which starts at ChooseServiceScreen (startDestination)
|
||||||
try {
|
try {
|
||||||
navController.navigate(Graph.MAIN) {
|
navController.navigate(Graph.MAIN) {
|
||||||
|
|
@ -188,7 +186,7 @@ fun NavGraphBuilder.authNavGraph(navController: NavController, mainViewModel: co
|
||||||
launchSingleTop = true
|
launchSingleTop = true
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
android.util.Log.e("AuthNavGraph", "Navigation error: ${e.message}", e)
|
Log.e("AuthNavGraph", "Navigation error: ${e.message}", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -1,18 +1,15 @@
|
||||||
package com.example.livingai_lg.ui.navigation
|
package com.example.livingai_lg.ui.navigation.navigation_legacy
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.NavGraphBuilder
|
import androidx.navigation.NavGraphBuilder
|
||||||
import androidx.navigation.NavType
|
import androidx.navigation.NavType
|
||||||
import androidx.navigation.compose.NavHost
|
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.navigation
|
import androidx.navigation.compose.navigation
|
||||||
import androidx.navigation.navArgument
|
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.CallsScreen
|
||||||
import com.example.farmmarketplace.ui.screens.ContactsScreen
|
import com.example.farmmarketplace.ui.screens.ContactsScreen
|
||||||
import com.example.livingai_lg.ui.models.profileTypes
|
import com.example.livingai_lg.ui.navigation.AppScreen
|
||||||
import com.example.livingai_lg.ui.screens.AccountsScreen
|
import com.example.livingai_lg.ui.screens.AccountsScreen
|
||||||
import com.example.livingai_lg.ui.screens.AnimalProfileScreen
|
import com.example.livingai_lg.ui.screens.AnimalProfileScreen
|
||||||
import com.example.livingai_lg.ui.screens.BuyScreen
|
import com.example.livingai_lg.ui.screens.BuyScreen
|
||||||
|
|
@ -137,7 +134,8 @@ fun NavGraphBuilder.mainNavGraph(navController: NavController) {
|
||||||
navController.navigate(Graph.AUTH) {
|
navController.navigate(Graph.AUTH) {
|
||||||
popUpTo(0) { inclusive = true }
|
popUpTo(0) { inclusive = true }
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
onApiTest = {navController.navigate(AppScreen.API_TEST)}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,430 @@
|
||||||
|
package com.example.livingai_lg.ui.navigation.navigation_legacy
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.navigation.compose.NavHost
|
||||||
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import com.example.livingai_lg.ui.AuthState
|
||||||
|
import com.example.livingai_lg.ui.MainViewModel
|
||||||
|
import com.example.livingai_lg.ui.navigation.AppScreen
|
||||||
|
|
||||||
|
object OldAppScreen {
|
||||||
|
const val LANDING = "landing"
|
||||||
|
const val SIGN_IN = "sign_in"
|
||||||
|
const val SIGN_UP = "sign_up"
|
||||||
|
|
||||||
|
const val OTP = "otp"
|
||||||
|
|
||||||
|
const val CHOOSE_SERVICE = "choose_service"
|
||||||
|
|
||||||
|
const val CREATE_PROFILE = "create_profile"
|
||||||
|
|
||||||
|
const val BUY_ANIMALS = "buy_animals"
|
||||||
|
const val ANIMAL_PROFILE = "animal_profile"
|
||||||
|
|
||||||
|
const val CREATE_ANIMAL_LISTING = "create_animal_listing"
|
||||||
|
|
||||||
|
const val BUY_ANIMALS_FILTERS = "buy_animals_filters"
|
||||||
|
const val BUY_ANIMALS_SORT = "buy_animals_sort"
|
||||||
|
|
||||||
|
const val SELLER_PROFILE = "seller_profile"
|
||||||
|
|
||||||
|
const val SALE_ARCHIVE = "sale_archive"
|
||||||
|
|
||||||
|
const val POST_SALE_SURVEY = "post_sale_survey"
|
||||||
|
|
||||||
|
const val SAVED_LISTINGS = "saved_listings"
|
||||||
|
|
||||||
|
const val CONTACTS = "contacts"
|
||||||
|
|
||||||
|
const val CALLS = "calls"
|
||||||
|
|
||||||
|
const val CHAT = "chat"
|
||||||
|
|
||||||
|
const val CHATS = "chats"
|
||||||
|
|
||||||
|
const val ACCOUNTS = "accounts"
|
||||||
|
|
||||||
|
fun chats(contact: String) =
|
||||||
|
"$CHAT/$contact"
|
||||||
|
|
||||||
|
fun otp(phone: String, name: String) =
|
||||||
|
"$OTP/$phone/$name"
|
||||||
|
|
||||||
|
fun otpWithSignup(phone: String, name: String, state: String, district: String, village: String) =
|
||||||
|
"$OTP/$phone/$name/$state/$district/$village"
|
||||||
|
|
||||||
|
fun createProfile(name: String) =
|
||||||
|
"$CREATE_PROFILE/$name"
|
||||||
|
|
||||||
|
fun chooseService(profileId: String) =
|
||||||
|
"$CHOOSE_SERVICE/$profileId"
|
||||||
|
fun postSaleSurvey(animalId: String) =
|
||||||
|
"$POST_SALE_SURVEY/$animalId"
|
||||||
|
|
||||||
|
fun animalProfile(animalId: String) =
|
||||||
|
"$ANIMAL_PROFILE/$animalId"
|
||||||
|
|
||||||
|
fun sellerProfile(sellerId: String) =
|
||||||
|
"$SELLER_PROFILE/$sellerId"
|
||||||
|
|
||||||
|
fun saleArchive(saleId: String) =
|
||||||
|
"$SALE_ARCHIVE/$saleId"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object Graph {
|
||||||
|
const val AUTH = "auth"
|
||||||
|
const val MAIN = "main"
|
||||||
|
|
||||||
|
fun auth(route: String)=
|
||||||
|
"$AUTH/$route"
|
||||||
|
|
||||||
|
fun main(route: String)=
|
||||||
|
"$MAIN/$route"
|
||||||
|
}
|
||||||
|
@Composable
|
||||||
|
fun OldAppNavigation(
|
||||||
|
authState: AuthState,
|
||||||
|
mainViewModel: MainViewModel
|
||||||
|
) {
|
||||||
|
val navController = rememberNavController()
|
||||||
|
|
||||||
|
// Determine start destination based on initial auth state
|
||||||
|
// This prevents showing landing screen when user is already logged in
|
||||||
|
val startDestination = remember(authState) {
|
||||||
|
when (authState) {
|
||||||
|
is AuthState.Authenticated -> Graph.MAIN
|
||||||
|
is AuthState.Unauthenticated -> Graph.AUTH
|
||||||
|
is AuthState.Unknown -> Graph.AUTH // Show landing while checking
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NavHost(
|
||||||
|
navController = navController,
|
||||||
|
startDestination = startDestination
|
||||||
|
) {
|
||||||
|
authNavGraph(navController, mainViewModel)
|
||||||
|
mainNavGraph(navController)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle navigation based on auth state changes
|
||||||
|
LaunchedEffect(authState) {
|
||||||
|
Log.d("AppNavigation", "LaunchedEffect triggered with authState: $authState")
|
||||||
|
when (authState) {
|
||||||
|
is AuthState.Authenticated -> {
|
||||||
|
// User is authenticated, navigate to ChooseServiceScreen
|
||||||
|
// Add a small delay to ensure NavHost graphs are fully built
|
||||||
|
delay(100)
|
||||||
|
|
||||||
|
val currentRoute = navController.currentBackStackEntry?.destination?.route
|
||||||
|
Log.d("AppNavigation", "Authenticated - currentRoute: $currentRoute")
|
||||||
|
// Only navigate if we're not already in the MAIN graph or ChooseServiceScreen
|
||||||
|
if (currentRoute?.startsWith(Graph.MAIN) != true &&
|
||||||
|
currentRoute?.startsWith(AppScreen.CHOOSE_SERVICE) != true) {
|
||||||
|
Log.d("AppNavigation", "Navigating to ChooseServiceScreen (default profileId: 1)")
|
||||||
|
try {
|
||||||
|
// Navigate directly to the start destination route of MAIN graph
|
||||||
|
// This avoids the "Sequence is empty" error when navigating to Graph.MAIN
|
||||||
|
navController.navigate(AppScreen.chooseService("1")) {
|
||||||
|
// Clear back stack to prevent going back to auth screens
|
||||||
|
popUpTo(Graph.AUTH) { inclusive = true }
|
||||||
|
// Prevent multiple navigations
|
||||||
|
launchSingleTop = true
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("AppNavigation", "Navigation error: ${e.message}", e)
|
||||||
|
// Fallback: try navigating to Graph.MAIN if direct route fails
|
||||||
|
try {
|
||||||
|
navController.navigate(Graph.MAIN) {
|
||||||
|
popUpTo(Graph.AUTH) { inclusive = true }
|
||||||
|
launchSingleTop = true
|
||||||
|
}
|
||||||
|
} catch (e2: Exception) {
|
||||||
|
Log.e("AppNavigation", "Fallback navigation also failed: ${e2.message}", e2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.d("AppNavigation", "Already in MAIN graph or ChooseServiceScreen, skipping navigation")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 ||
|
||||||
|
currentRoute?.startsWith(Graph.AUTH) != true) {
|
||||||
|
navController.navigate(Graph.AUTH) {
|
||||||
|
// Clear back stack to prevent going back to main screens
|
||||||
|
popUpTo(0) { inclusive = true }
|
||||||
|
launchSingleTop = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is AuthState.Unknown -> {
|
||||||
|
// Still checking auth status
|
||||||
|
// If we're on landing screen, stay there
|
||||||
|
// If we're on main screen and checking, don't navigate yet
|
||||||
|
// This prevents flickering during token validation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// MainNavGraph(navController)
|
||||||
|
// AuthNavGraph(navController)
|
||||||
|
// when (authState) {
|
||||||
|
// is AuthState.Unauthenticated -> {AuthNavGraph()}
|
||||||
|
// is AuthState.Authenticated -> {MainNavGraph()}
|
||||||
|
// is AuthState.Unknown -> {
|
||||||
|
// Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
|
// CircularProgressIndicator()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// AuthState.Loading -> SplashScreen()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// val onNavClick: (String) -> Unit = { route ->
|
||||||
|
// val currentRoute =
|
||||||
|
// navController.currentBackStackEntry?.destination?.route
|
||||||
|
//
|
||||||
|
// if (currentRoute != route) {
|
||||||
|
// navController.navigate(route) {
|
||||||
|
// launchSingleTop = true
|
||||||
|
// restoreState = true
|
||||||
|
// popUpTo(navController.graph.startDestinationId) {
|
||||||
|
// saveState = true
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// NavHost(
|
||||||
|
// navController = navController,
|
||||||
|
// startDestination = AppScreen.LANDING,
|
||||||
|
//
|
||||||
|
// ) {
|
||||||
|
// composable(AppScreen.LANDING) {
|
||||||
|
// LandingScreen(
|
||||||
|
// onSignUpClick = { navController.navigate(AppScreen.SIGN_UP) },
|
||||||
|
// onSignInClick = { navController.navigate(AppScreen.SIGN_IN) },
|
||||||
|
// onGuestClick = { navController.navigate(AppScreen.CREATE_PROFILE) }
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// composable(AppScreen.SIGN_IN) {
|
||||||
|
// SignInScreen(
|
||||||
|
// onSignUpClick = { navController.navigate(AppScreen.SIGN_UP) },
|
||||||
|
// onSignInClick = {
|
||||||
|
// navController.navigate(AppScreen.OTP)
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// composable(AppScreen.SIGN_UP) {
|
||||||
|
// SignUpScreen(
|
||||||
|
// onSignUpClick = {
|
||||||
|
// navController.navigate(AppScreen.OTP)
|
||||||
|
// },
|
||||||
|
// onSignInClick = {
|
||||||
|
// navController.navigate(AppScreen.SIGN_IN) {
|
||||||
|
// popUpTo(AppScreen.SIGN_UP) { inclusive = true }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// composable(AppScreen.OTP) {
|
||||||
|
// OtpScreen(
|
||||||
|
// onContinue = { navController.navigate(AppScreen.CREATE_PROFILE) }
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
// composable(AppScreen.CREATE_PROFILE) {
|
||||||
|
// CreateProfileScreen (
|
||||||
|
// onProfileSelected = { profileId ->
|
||||||
|
// if (profileId == "buyer_seller") {
|
||||||
|
// navController.navigate(AppScreen.BUY_ANIMALS)
|
||||||
|
// } else {
|
||||||
|
// navController.navigate(AppScreen.CHOOSE_SERVICE)
|
||||||
|
// } },
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// composable(AppScreen.CHOOSE_SERVICE) {
|
||||||
|
// ChooseServiceScreen (
|
||||||
|
// onServiceSelected = { navController.navigate(AppScreen.LANDING) },
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// composable(AppScreen.BUY_ANIMALS) {
|
||||||
|
// BuyScreen(
|
||||||
|
// onBackClick = {
|
||||||
|
// navController.popBackStack()
|
||||||
|
// },
|
||||||
|
// onProductClick = { animalId ->
|
||||||
|
// navController.navigate(
|
||||||
|
// AppScreen.animalProfile(animalId)
|
||||||
|
// )
|
||||||
|
// },
|
||||||
|
// onNavClick = onNavClick,
|
||||||
|
// onFilterClick = {navController.navigate(AppScreen.BUY_ANIMALS_FILTERS)},
|
||||||
|
// onSortClick = {navController.navigate(AppScreen.BUY_ANIMALS_SORT)},
|
||||||
|
// onSellerClick = { sellerId ->
|
||||||
|
// navController.navigate(
|
||||||
|
// AppScreen.sellerProfile(sellerId)
|
||||||
|
// )
|
||||||
|
// },
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// composable(AppScreen.BUY_ANIMALS_FILTERS) {
|
||||||
|
// FilterScreen(
|
||||||
|
// onSubmitClick = {navController.navigate(AppScreen.BUY_ANIMALS)},
|
||||||
|
// onBackClick = {
|
||||||
|
// navController.popBackStack()
|
||||||
|
// },
|
||||||
|
// onSkipClick = {
|
||||||
|
// navController.popBackStack()
|
||||||
|
// },
|
||||||
|
// onCancelClick = {
|
||||||
|
// navController.popBackStack()
|
||||||
|
// },
|
||||||
|
//
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// composable(AppScreen.BUY_ANIMALS_SORT) {
|
||||||
|
// SortScreen(
|
||||||
|
// onApplyClick = {navController.navigate(AppScreen.BUY_ANIMALS)},
|
||||||
|
// onBackClick = {
|
||||||
|
// navController.popBackStack()
|
||||||
|
// },
|
||||||
|
// onSkipClick = {
|
||||||
|
// navController.popBackStack()
|
||||||
|
// },
|
||||||
|
// onCancelClick = {
|
||||||
|
// navController.popBackStack()
|
||||||
|
// },
|
||||||
|
//
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// composable(AppScreen.CREATE_ANIMAL_LISTING) {
|
||||||
|
// NewListingScreen (
|
||||||
|
// onSaveClick = {navController.navigate(
|
||||||
|
// AppScreen.postSaleSurvey("2")
|
||||||
|
// )},
|
||||||
|
// onBackClick = {
|
||||||
|
// navController.popBackStack()
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// composable(
|
||||||
|
// route = "${AppScreen.SALE_ARCHIVE}/{saleId}",
|
||||||
|
// arguments = listOf(
|
||||||
|
// navArgument("saleId") { type = NavType.StringType }
|
||||||
|
// )
|
||||||
|
// ) { backStackEntry ->
|
||||||
|
//
|
||||||
|
// val saleId = backStackEntry
|
||||||
|
// .arguments
|
||||||
|
// ?.getString("saleId")
|
||||||
|
// ?: return@composable
|
||||||
|
//
|
||||||
|
// SaleArchiveScreen(
|
||||||
|
// saleId = saleId,
|
||||||
|
// onBackClick = {
|
||||||
|
// navController.popBackStack()
|
||||||
|
// },
|
||||||
|
// onSaleSurveyClick = { saleId ->
|
||||||
|
// navController.navigate(
|
||||||
|
// AppScreen.sellerProfile(saleId)
|
||||||
|
// )
|
||||||
|
// },
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// composable(
|
||||||
|
// route = "${AppScreen.POST_SALE_SURVEY}/{animalId}",
|
||||||
|
// arguments = listOf(
|
||||||
|
// navArgument("animalId") { type = NavType.StringType }
|
||||||
|
// )
|
||||||
|
// ) { backStackEntry ->
|
||||||
|
//
|
||||||
|
// val animalId = backStackEntry
|
||||||
|
// .arguments
|
||||||
|
// ?.getString("animalId")
|
||||||
|
// ?: return@composable
|
||||||
|
//
|
||||||
|
// PostSaleSurveyScreen (
|
||||||
|
// animalId = animalId,
|
||||||
|
// onBackClick = {
|
||||||
|
// navController.popBackStack()
|
||||||
|
// },
|
||||||
|
// onSubmit = {navController.navigate(
|
||||||
|
// AppScreen.saleArchive("2")
|
||||||
|
// )}
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// composable(
|
||||||
|
// route = "${AppScreen.ANIMAL_PROFILE}/{animalId}",
|
||||||
|
// arguments = listOf(
|
||||||
|
// navArgument("animalId") { type = NavType.StringType }
|
||||||
|
// )
|
||||||
|
// ) { backStackEntry ->
|
||||||
|
//
|
||||||
|
// val animalId = backStackEntry
|
||||||
|
// .arguments
|
||||||
|
// ?.getString("animalId")
|
||||||
|
// ?: return@composable
|
||||||
|
//
|
||||||
|
// AnimalProfileScreen(
|
||||||
|
// animalId = animalId,
|
||||||
|
// onBackClick = {
|
||||||
|
// navController.popBackStack()
|
||||||
|
// },
|
||||||
|
// onSellerClick = { sellerId ->
|
||||||
|
// navController.navigate(
|
||||||
|
// AppScreen.sellerProfile(sellerId)
|
||||||
|
// )
|
||||||
|
// },
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// composable(
|
||||||
|
// route = "${AppScreen.SELLER_PROFILE}/{sellerId}",
|
||||||
|
// arguments = listOf(
|
||||||
|
// navArgument("sellerId") { type = NavType.StringType }
|
||||||
|
// )
|
||||||
|
// ) { backStackEntry ->
|
||||||
|
//
|
||||||
|
// val sellerId = backStackEntry
|
||||||
|
// .arguments
|
||||||
|
// ?.getString("sellerId")
|
||||||
|
// ?: return@composable
|
||||||
|
//
|
||||||
|
// SellerProfileScreen(
|
||||||
|
// sellerId = sellerId,
|
||||||
|
// onBackClick = {
|
||||||
|
// navController.popBackStack()
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// composable(AppScreen.SELLER_PROFILE) {
|
||||||
|
// SellerProfileScreen (
|
||||||
|
// onBackClick = {
|
||||||
|
// navController.popBackStack()
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -8,6 +8,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.automirrored.filled.Logout
|
import androidx.compose.material.icons.automirrored.filled.Logout
|
||||||
|
import androidx.compose.material.icons.filled.Construction
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
|
@ -34,7 +35,8 @@ import kotlinx.coroutines.launch
|
||||||
@Composable
|
@Composable
|
||||||
fun AccountsScreen(
|
fun AccountsScreen(
|
||||||
onBackClick: () -> Unit,
|
onBackClick: () -> Unit,
|
||||||
onLogout: () -> Unit
|
onLogout: () -> Unit,
|
||||||
|
onApiTest: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
@ -123,6 +125,43 @@ fun AccountsScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth().padding(vertical = 12.dp)
|
||||||
|
.clickable {
|
||||||
|
onApiTest()
|
||||||
|
},
|
||||||
|
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.Default.Construction,
|
||||||
|
contentDescription = "Api test",
|
||||||
|
tint = Color.Gray,
|
||||||
|
modifier = Modifier.size(24.dp)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "Test API",
|
||||||
|
fontSize = 16.sp,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
color = Color.Gray
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ import com.example.livingai_lg.R
|
||||||
import com.example.livingai_lg.ui.components.ActionPopup
|
import com.example.livingai_lg.ui.components.ActionPopup
|
||||||
import com.example.livingai_lg.ui.components.AddressSelectorOverlay
|
import com.example.livingai_lg.ui.components.AddressSelectorOverlay
|
||||||
import com.example.livingai_lg.ui.components.FilterOverlay
|
import com.example.livingai_lg.ui.components.FilterOverlay
|
||||||
|
import com.example.livingai_lg.ui.components.InfoOverlay
|
||||||
import com.example.livingai_lg.ui.components.NotificationsOverlay
|
import com.example.livingai_lg.ui.components.NotificationsOverlay
|
||||||
import com.example.livingai_lg.ui.components.SortOverlay
|
import com.example.livingai_lg.ui.components.SortOverlay
|
||||||
import com.example.livingai_lg.ui.models.sampleNotifications
|
import com.example.livingai_lg.ui.models.sampleNotifications
|
||||||
|
|
@ -59,6 +60,9 @@ fun BuyScreen(
|
||||||
var showSavedPopup by remember { mutableStateOf(false) }
|
var showSavedPopup by remember { mutableStateOf(false) }
|
||||||
var showNotifications by remember { mutableStateOf(false) }
|
var showNotifications by remember { mutableStateOf(false) }
|
||||||
val sampleNotifications = sampleNotifications
|
val sampleNotifications = sampleNotifications
|
||||||
|
var showInfoOverlay by remember { mutableStateOf(false) }
|
||||||
|
var selectedItemId by remember { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -168,7 +172,9 @@ fun BuyScreen(
|
||||||
onSavedChange = { isSaved.value = it },
|
onSavedChange = { isSaved.value = it },
|
||||||
onProductClick = { onProductClick(animal.id)},
|
onProductClick = { onProductClick(animal.id)},
|
||||||
onSellerClick = onSellerClick,
|
onSellerClick = onSellerClick,
|
||||||
onBookmarkClick = { showSavedPopup = true }
|
onBookmarkClick = { showSavedPopup = true },
|
||||||
|
onInfoClick = { showInfoOverlay = true
|
||||||
|
selectedItemId=animal.id},
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
@ -213,6 +219,15 @@ fun BuyScreen(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val animal = sampleAnimals.find { it.id == selectedItemId }
|
||||||
|
|
||||||
|
InfoOverlay(
|
||||||
|
visible = showInfoOverlay,
|
||||||
|
title = animal?.breed ?:"",
|
||||||
|
text = animal?.breedInfo ?: "",
|
||||||
|
onDismiss = { showInfoOverlay = false }
|
||||||
|
)
|
||||||
|
|
||||||
ActionPopup(
|
ActionPopup(
|
||||||
visible = showSavedPopup,
|
visible = showSavedPopup,
|
||||||
text = "Saved",
|
text = "Saved",
|
||||||
|
|
|
||||||
|
|
@ -239,7 +239,7 @@ Column(
|
||||||
val needsProfile = signupResponse.needsProfile == true || verifyResponse.needsProfile
|
val needsProfile = signupResponse.needsProfile == true || verifyResponse.needsProfile
|
||||||
android.util.Log.d("OTPScreen", "Signup successful. needsProfile=$needsProfile, navigating...")
|
android.util.Log.d("OTPScreen", "Signup successful. needsProfile=$needsProfile, navigating...")
|
||||||
// Refresh auth status - this will trigger navigation via AppNavigation's LaunchedEffect
|
// Refresh auth status - this will trigger navigation via AppNavigation's LaunchedEffect
|
||||||
mainViewModel.refreshAuthStatus()
|
// mainViewModel.refreshAuthStatus()
|
||||||
try {
|
try {
|
||||||
if (needsProfile) {
|
if (needsProfile) {
|
||||||
android.util.Log.d("OTPScreen", "Navigating to create profile screen with name: $name")
|
android.util.Log.d("OTPScreen", "Navigating to create profile screen with name: $name")
|
||||||
|
|
@ -247,6 +247,7 @@ Column(
|
||||||
} else {
|
} else {
|
||||||
// Don't manually navigate - let AppNavigation handle it
|
// Don't manually navigate - let AppNavigation handle it
|
||||||
android.util.Log.d("OTPScreen", "Signup successful - auth state updated, navigation will happen automatically")
|
android.util.Log.d("OTPScreen", "Signup successful - auth state updated, navigation will happen automatically")
|
||||||
|
onSuccess()
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
android.util.Log.e("OTPScreen", "Navigation error: ${e.message}", e)
|
android.util.Log.e("OTPScreen", "Navigation error: ${e.message}", e)
|
||||||
|
|
@ -266,6 +267,7 @@ Column(
|
||||||
} else {
|
} else {
|
||||||
// Don't manually navigate - let AppNavigation handle it
|
// Don't manually navigate - let AppNavigation handle it
|
||||||
android.util.Log.d("OTPScreen", "Signup successful - auth state updated, navigation will happen automatically")
|
android.util.Log.d("OTPScreen", "Signup successful - auth state updated, navigation will happen automatically")
|
||||||
|
onSuccess()
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
android.util.Log.e("OTPScreen", "Navigation error: ${e.message}", e)
|
android.util.Log.e("OTPScreen", "Navigation error: ${e.message}", e)
|
||||||
|
|
@ -325,7 +327,12 @@ Column(
|
||||||
// Tokens are now saved (synchronously via commit())
|
// Tokens are now saved (synchronously via commit())
|
||||||
// Refresh auth status - this will optimistically set authState to Authenticated
|
// Refresh auth status - this will optimistically set authState to Authenticated
|
||||||
// The LaunchedEffect in AppNavigation will automatically navigate to ChooseServiceScreen
|
// The LaunchedEffect in AppNavigation will automatically navigate to ChooseServiceScreen
|
||||||
mainViewModel.refreshAuthStatus()
|
//mainViewModel.refreshAuthStatus()
|
||||||
|
if(response.needsProfile) {
|
||||||
|
onCreateProfile(name)
|
||||||
|
} else {
|
||||||
|
onSuccess()
|
||||||
|
}
|
||||||
android.util.Log.d("OTPScreen", "Auth status refreshed - navigation will happen automatically via LaunchedEffect")
|
android.util.Log.d("OTPScreen", "Auth status refreshed - navigation will happen automatically via LaunchedEffect")
|
||||||
}
|
}
|
||||||
.onFailure { error ->
|
.onFailure { error ->
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,156 @@
|
||||||
|
package com.example.livingai_lg.ui.screens.testing
|
||||||
|
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
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.material3.*
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun ApiTestScreen(
|
||||||
|
onBackClick: () -> Unit
|
||||||
|
) {
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
// Default API endpoint
|
||||||
|
var apiEndpoint by remember {
|
||||||
|
mutableStateOf("https://jsonplaceholder.typicode.com/posts/1")
|
||||||
|
}
|
||||||
|
|
||||||
|
var isLoading by remember { mutableStateOf(false) }
|
||||||
|
var responseText by remember { mutableStateOf<String?>(null) }
|
||||||
|
var errorText by remember { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
|
val client = remember { OkHttpClient() }
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(Color(0xFFF7F4EE))
|
||||||
|
) {
|
||||||
|
// Top App Bar
|
||||||
|
TopAppBar(
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
text = "API Test",
|
||||||
|
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)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(16.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
|
) {
|
||||||
|
|
||||||
|
// API endpoint input
|
||||||
|
OutlinedTextField(
|
||||||
|
value = apiEndpoint,
|
||||||
|
onValueChange = { apiEndpoint = it },
|
||||||
|
label = { Text("API Endpoint") },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
singleLine = true
|
||||||
|
)
|
||||||
|
|
||||||
|
// Call API button
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
scope.launch {
|
||||||
|
isLoading = true
|
||||||
|
responseText = null
|
||||||
|
errorText = null
|
||||||
|
|
||||||
|
try {
|
||||||
|
val result = withContext(Dispatchers.IO) {
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url(apiEndpoint)
|
||||||
|
.get()
|
||||||
|
.build()
|
||||||
|
|
||||||
|
client.newCall(request).execute().use { response ->
|
||||||
|
val body = response.body?.string()
|
||||||
|
Pair(response.code, body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val (code, body) = result
|
||||||
|
responseText = "Status: $code\n\n$body"
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
errorText = e.toString()
|
||||||
|
} finally {
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
enabled = !isLoading
|
||||||
|
) {
|
||||||
|
Text(if (isLoading) "Calling API..." else "Call API")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Result display
|
||||||
|
when {
|
||||||
|
responseText != null -> {
|
||||||
|
Surface(
|
||||||
|
shape = RoundedCornerShape(12.dp),
|
||||||
|
color = Color.White,
|
||||||
|
shadowElevation = 2.dp,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = responseText ?: "",
|
||||||
|
modifier = Modifier.padding(16.dp),
|
||||||
|
fontSize = 14.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errorText != null -> {
|
||||||
|
Surface(
|
||||||
|
shape = RoundedCornerShape(12.dp),
|
||||||
|
color = Color(0xFFFFEBEE),
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = errorText ?: "",
|
||||||
|
modifier = Modifier.padding(16.dp),
|
||||||
|
fontSize = 14.sp,
|
||||||
|
color = Color(0xFFE53935)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,7 @@ navigationCompose = "2.7.7"
|
||||||
ktor = "2.3.12"
|
ktor = "2.3.12"
|
||||||
kotlinxSerialization = "1.6.3"
|
kotlinxSerialization = "1.6.3"
|
||||||
securityCrypto = "1.1.0-alpha06"
|
securityCrypto = "1.1.0-alpha06"
|
||||||
|
navigationComposeJvmstubs = "2.9.6"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||||
|
|
@ -42,6 +43,7 @@ kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-
|
||||||
|
|
||||||
# AndroidX Security
|
# AndroidX Security
|
||||||
androidx-security-crypto = { group = "androidx.security", name = "security-crypto", version.ref = "securityCrypto" }
|
androidx-security-crypto = { group = "androidx.security", name = "security-crypto", version.ref = "securityCrypto" }
|
||||||
|
androidx-navigation-compose-jvmstubs = { group = "androidx.navigation", name = "navigation-compose-jvmstubs", version.ref = "navigationComposeJvmstubs" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue