272 lines
12 KiB
Kotlin
272 lines
12 KiB
Kotlin
package navigation
|
|
|
|
import androidx.compose.foundation.layout.Box
|
|
import androidx.compose.foundation.layout.PaddingValues
|
|
import androidx.compose.foundation.layout.padding
|
|
import androidx.compose.material3.Scaffold
|
|
import androidx.compose.runtime.*
|
|
import androidx.compose.ui.Modifier
|
|
import androidx.compose.ui.unit.dp
|
|
import androidx.hilt.navigation.compose.hiltViewModel
|
|
import androidx.navigation.NavHostController
|
|
import androidx.navigation.NavType
|
|
import androidx.navigation.compose.NavHost
|
|
import androidx.navigation.compose.composable
|
|
import androidx.navigation.compose.currentBackStackEntryAsState
|
|
import androidx.navigation.compose.rememberNavController
|
|
import androidx.navigation.navArgument
|
|
import auth.presentation.LoginScreen
|
|
import auth.presentation.RegisterScreen
|
|
import auth.presentation.AuthViewModel
|
|
import core.presentation.settings.SettingsScreen
|
|
import core.presentation.settings.SettingsViewModel
|
|
import chats.presentation.chat_list.ChatListScreen
|
|
import chats.presentation.chat_list.ChatListViewModel
|
|
import chats.presentation.chat_detail.ChatDetailScreen
|
|
import chats.presentation.chat_detail.ChatDetailViewModel
|
|
import contacts.presentation.ContactListScreen
|
|
import contacts.presentation.ContactListViewModel
|
|
import core.presentation.components.GlassNavigationBar
|
|
import profiles.presentation.ProfileScreen
|
|
import profiles.presentation.EditProfileScreen
|
|
import profiles.presentation.viewmodel.ProfileViewModel
|
|
import stories.presentation.StoryViewModel
|
|
|
|
sealed class Screen(val route: String) {
|
|
object Login : Screen("login")
|
|
object Register : Screen("register")
|
|
object ChatList : Screen("chat_list")
|
|
object ChatDetail : Screen("chat_detail/{chatId}/{chatName}") {
|
|
fun createRoute(chatId: String, chatName: String) = "chat_detail/$chatId/$chatName"
|
|
}
|
|
object Profile : Screen("profile?profileId={profileId}") {
|
|
fun createRoute(profileId: String? = null) = if (profileId != null) "profile?profileId=$profileId" else "profile"
|
|
}
|
|
object EditProfile : Screen("edit_profile")
|
|
object Settings : Screen("settings")
|
|
object Contacts : Screen("contacts")
|
|
}
|
|
|
|
@Composable
|
|
fun AppNavigation(
|
|
navController: NavHostController = rememberNavController(),
|
|
navigationManager: core.utils.NavigationManager? = null
|
|
) {
|
|
val authViewModel: AuthViewModel = hiltViewModel()
|
|
val authState by authViewModel.state.collectAsState()
|
|
val startDestination = if (authState.isAuthenticated) Screen.ChatList.route else Screen.Login.route
|
|
|
|
androidx.compose.runtime.LaunchedEffect(navigationManager) {
|
|
navigationManager?.events?.collect { event ->
|
|
when (event) {
|
|
is core.utils.NavEvent.OpenChat -> {
|
|
if (authState.isAuthenticated) {
|
|
navController.navigate(Screen.ChatDetail.createRoute(event.chatId, "Chat")) {
|
|
launchSingleTop = true
|
|
}
|
|
}
|
|
}
|
|
core.utils.NavEvent.Logout -> {
|
|
navController.navigate(Screen.Login.route) {
|
|
popUpTo(0) { inclusive = true }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
|
val currentRoute = navBackStackEntry?.destination?.route
|
|
|
|
// Определяем, нужно ли показывать нижнюю панель
|
|
val showBottomBar = currentRoute in listOf(
|
|
Screen.ChatList.route,
|
|
Screen.Contacts.route,
|
|
Screen.Settings.route,
|
|
Screen.Profile.route
|
|
)
|
|
|
|
val profileViewModel: ProfileViewModel = hiltViewModel()
|
|
val profileState by profileViewModel.state.collectAsState()
|
|
|
|
androidx.compose.runtime.LaunchedEffect(authState.isAuthenticated) {
|
|
if (authState.isAuthenticated) {
|
|
profileViewModel.loadProfile()
|
|
}
|
|
}
|
|
|
|
Scaffold(
|
|
bottomBar = {
|
|
if (showBottomBar) {
|
|
GlassNavigationBar(
|
|
currentRoute = currentRoute ?: "",
|
|
onNavigate = { route ->
|
|
navController.navigate(route) {
|
|
popUpTo(Screen.ChatList.route) { saveState = true }
|
|
launchSingleTop = true
|
|
restoreState = true
|
|
}
|
|
},
|
|
userAvatarUrl = profileState.profile?.avatarUrl,
|
|
username = profileState.profile?.displayName ?: profileState.profile?.username ?: ""
|
|
)
|
|
}
|
|
}
|
|
) { paddingValues ->
|
|
Box(modifier = Modifier.padding(if (showBottomBar) paddingValues else PaddingValues(0.dp))) {
|
|
NavHost(
|
|
navController = navController,
|
|
startDestination = startDestination
|
|
) {
|
|
composable(Screen.Login.route) {
|
|
val viewModel: AuthViewModel = hiltViewModel()
|
|
LoginScreen(
|
|
viewModel = viewModel,
|
|
onNavigateToRegister = { navController.navigate(Screen.Register.route) },
|
|
onNavigateToSettings = { navController.navigate(Screen.Settings.route) },
|
|
onLoginSuccess = {
|
|
navController.navigate(Screen.ChatList.route) {
|
|
popUpTo(Screen.Login.route) { inclusive = true }
|
|
}
|
|
}
|
|
)
|
|
}
|
|
composable(Screen.Register.route) {
|
|
val viewModel: AuthViewModel = hiltViewModel()
|
|
RegisterScreen(
|
|
viewModel = viewModel,
|
|
onNavigateToLogin = { navController.popBackStack() },
|
|
onRegisterSuccess = {
|
|
navController.navigate(Screen.ChatList.route) {
|
|
popUpTo(Screen.Login.route) { inclusive = true }
|
|
}
|
|
}
|
|
)
|
|
}
|
|
composable(Screen.ChatList.route) {
|
|
val viewModel: ChatListViewModel = hiltViewModel()
|
|
val storyViewModel: StoryViewModel = hiltViewModel()
|
|
ChatListScreen(
|
|
viewModel = viewModel,
|
|
storyViewModel = storyViewModel,
|
|
onChatClick = { chatId ->
|
|
navController.navigate(Screen.ChatDetail.createRoute(chatId, "Chat"))
|
|
},
|
|
onStoryClick = { /* Story click logic */ }
|
|
)
|
|
}
|
|
composable(
|
|
route = Screen.ChatDetail.route,
|
|
arguments = listOf(
|
|
navArgument("chatId") { type = NavType.StringType },
|
|
navArgument("chatName") { type = NavType.StringType }
|
|
)
|
|
) { backStackEntry ->
|
|
val chatId = backStackEntry.arguments?.getString("chatId") ?: ""
|
|
val chatName = backStackEntry.arguments?.getString("chatName") ?: "Chat"
|
|
val viewModel: ChatDetailViewModel = hiltViewModel()
|
|
|
|
ChatDetailScreen(
|
|
chatId = chatId,
|
|
chatName = chatName,
|
|
viewModel = viewModel,
|
|
onBack = { navController.popBackStack() }
|
|
)
|
|
}
|
|
composable(Screen.Contacts.route) {
|
|
val viewModel: ContactListViewModel = hiltViewModel()
|
|
val state by viewModel.state.collectAsState()
|
|
ContactListScreen(
|
|
contacts = state.contacts,
|
|
requests = state.requests,
|
|
isLoading = state.isLoading,
|
|
onContactClick = { id -> navController.navigate(Screen.Profile.createRoute(id)) },
|
|
onSearchChange = { viewModel.onSearchChange(it) },
|
|
onAddContact = { id -> viewModel.addContact(id) },
|
|
onStartChat = { id -> viewModel.startChat(id) },
|
|
onAcceptRequest = { id -> viewModel.acceptRequest(id) },
|
|
onDeclineRequest = { id -> viewModel.declineRequest(id) }
|
|
)
|
|
}
|
|
composable(
|
|
route = Screen.Profile.route,
|
|
arguments = listOf(
|
|
navArgument("profileId") {
|
|
type = NavType.StringType
|
|
nullable = true
|
|
}
|
|
)
|
|
) { backStackEntry ->
|
|
val profileId = backStackEntry.arguments?.getString("profileId")
|
|
val viewModel: ProfileViewModel = hiltViewModel()
|
|
|
|
ProfileScreen(
|
|
profileId = profileId,
|
|
viewModel = viewModel,
|
|
onEditProfile = { navController.navigate(Screen.EditProfile.route) },
|
|
onBack = { navController.popBackStack() },
|
|
onSendMessage = { id -> navController.navigate(Screen.ChatDetail.createRoute(id, "Chat")) }
|
|
)
|
|
}
|
|
composable(Screen.EditProfile.route) {
|
|
// Используем ViewModel от предыдущего экрана (Profile), чтобы данные не терялись
|
|
val profileEntry = remember(it) {
|
|
navController.getBackStackEntry(Screen.Profile.route)
|
|
}
|
|
val viewModel: ProfileViewModel = hiltViewModel(profileEntry)
|
|
val state by viewModel.state.collectAsState()
|
|
|
|
// Получаем profileId из родительского backStackEntry
|
|
val profileId = profileEntry.arguments?.getString("profileId")
|
|
|
|
androidx.compose.runtime.LaunchedEffect(state.profile) {
|
|
// Если профиль загружен (или обновился), мы можем инициализировать/обновить поля
|
|
}
|
|
|
|
val isSaving = state.isSaving
|
|
val error = state.error
|
|
|
|
// Автоматически возвращаемся назад после успешного сохранения
|
|
var wasSaving by remember { mutableStateOf(false) }
|
|
androidx.compose.runtime.LaunchedEffect(isSaving) {
|
|
if (isSaving) wasSaving = true
|
|
if (wasSaving && !isSaving && error == null) {
|
|
// Перезагружаем профиль перед возвратом
|
|
viewModel.loadProfile(profileId)
|
|
// Небольшая задержка чтобы данные обновились
|
|
kotlinx.coroutines.delay(300)
|
|
navController.popBackStack()
|
|
}
|
|
}
|
|
|
|
EditProfileScreen(
|
|
currentDisplayName = state.profile?.displayName,
|
|
currentUsername = state.profile?.username ?: "",
|
|
currentBio = state.profile?.bio,
|
|
currentBirthday = state.profile?.birthday,
|
|
currentAvatarUrl = state.profile?.avatarUrl,
|
|
isSaving = isSaving,
|
|
error = error,
|
|
onSave = { name, bio, birthday, uri ->
|
|
viewModel.updateProfile(name, bio, birthday, uri)
|
|
},
|
|
onBack = { navController.popBackStack() }
|
|
)
|
|
}
|
|
composable(Screen.Settings.route) {
|
|
val viewModel: SettingsViewModel = hiltViewModel()
|
|
SettingsScreen(
|
|
viewModel = viewModel,
|
|
onBack = { navController.popBackStack() },
|
|
onLogout = {
|
|
navController.navigate(Screen.Login.route) {
|
|
popUpTo(0) { inclusive = true }
|
|
}
|
|
}
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|