android: add chat list compose screen and chat placeholder navigation
This commit is contained in:
@@ -44,3 +44,10 @@
|
|||||||
- Added realtime event parser for `receive_message`, `message_updated`, `message_deleted`, `chat_updated`, `chat_deleted`, `user_online`, `user_offline`.
|
- Added realtime event parser for `receive_message`, `message_updated`, `message_deleted`, `chat_updated`, `chat_deleted`, `user_online`, `user_offline`.
|
||||||
- Added use-case level realtime event handling that updates Room and triggers repository refreshes when needed.
|
- Added use-case level realtime event handling that updates Room and triggers repository refreshes when needed.
|
||||||
- Wired realtime manager into DI.
|
- Wired realtime manager into DI.
|
||||||
|
|
||||||
|
### Step 8 - Chat list UI and navigation
|
||||||
|
- Added Chat List screen with tabs (`All` / `Archived`), local search filter, pull-to-refresh, and state handling (loading/empty/error).
|
||||||
|
- Added chat row rendering for unread badge, mention badge (`@`), pinned/muted marks, and message preview by media type.
|
||||||
|
- Added private chat presence display (`online` / `last seen recently` fallback).
|
||||||
|
- Connected Chat List to ViewModel/use-cases with no business logic inside composables.
|
||||||
|
- Added chat click navigation to placeholder `ChatScreen(chatId)`.
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation("androidx.core:core-ktx:1.15.0")
|
implementation("androidx.core:core-ktx:1.15.0")
|
||||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7")
|
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7")
|
||||||
|
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.7")
|
||||||
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7")
|
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7")
|
||||||
implementation("androidx.activity:activity-compose:1.10.1")
|
implementation("androidx.activity:activity-compose:1.10.1")
|
||||||
implementation("androidx.navigation:navigation-compose:2.8.5")
|
implementation("androidx.navigation:navigation-compose:2.8.5")
|
||||||
|
|||||||
@@ -1,31 +1,29 @@
|
|||||||
package ru.daemonlord.messenger.ui.chats
|
package ru.daemonlord.messenger.ui.chat
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ChatsPlaceholderScreen() {
|
fun ChatScreenPlaceholder(
|
||||||
|
chatId: Long,
|
||||||
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier.fillMaxSize(),
|
||||||
.fillMaxSize()
|
|
||||||
.padding(24.dp),
|
|
||||||
verticalArrangement = Arrangement.Center,
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "Chats",
|
text = "Chat Screen",
|
||||||
style = MaterialTheme.typography.headlineMedium,
|
style = MaterialTheme.typography.headlineSmall,
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = "Phase 1 placeholder. Chats list comes next.",
|
text = "chatId=$chatId",
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,241 @@
|
|||||||
|
package ru.daemonlord.messenger.ui.chats
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
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.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material3.AssistChip
|
||||||
|
import androidx.compose.material3.AssistChipDefaults
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.OutlinedTextField
|
||||||
|
import androidx.compose.material3.Tab
|
||||||
|
import androidx.compose.material3.TabRow
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import ru.daemonlord.messenger.domain.chat.model.ChatItem
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ChatListRoute(
|
||||||
|
onOpenChat: (Long) -> Unit,
|
||||||
|
viewModel: ChatListViewModel = hiltViewModel(),
|
||||||
|
) {
|
||||||
|
val state by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
|
ChatListScreen(
|
||||||
|
state = state,
|
||||||
|
onTabSelected = viewModel::onTabSelected,
|
||||||
|
onSearchChanged = viewModel::onSearchChanged,
|
||||||
|
onRefresh = viewModel::onPullToRefresh,
|
||||||
|
onOpenChat = onOpenChat,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ChatListScreen(
|
||||||
|
state: ChatListUiState,
|
||||||
|
onTabSelected: (ChatTab) -> Unit,
|
||||||
|
onSearchChanged: (String) -> Unit,
|
||||||
|
onRefresh: () -> Unit,
|
||||||
|
onOpenChat: (Long) -> Unit,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
) {
|
||||||
|
TabRow(
|
||||||
|
selectedTabIndex = if (state.selectedTab == ChatTab.ALL) 0 else 1,
|
||||||
|
) {
|
||||||
|
Tab(
|
||||||
|
selected = state.selectedTab == ChatTab.ALL,
|
||||||
|
onClick = { onTabSelected(ChatTab.ALL) },
|
||||||
|
text = { Text(text = "All") },
|
||||||
|
)
|
||||||
|
Tab(
|
||||||
|
selected = state.selectedTab == ChatTab.ARCHIVED,
|
||||||
|
onClick = { onTabSelected(ChatTab.ARCHIVED) },
|
||||||
|
text = { Text(text = "Archived") },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = state.searchQuery,
|
||||||
|
onValueChange = onSearchChanged,
|
||||||
|
label = { Text(text = "Search title / username / handle") },
|
||||||
|
singleLine = true,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||||
|
)
|
||||||
|
|
||||||
|
PullToRefreshBox(
|
||||||
|
isRefreshing = state.isRefreshing,
|
||||||
|
onRefresh = onRefresh,
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
) {
|
||||||
|
when {
|
||||||
|
state.isLoading -> {
|
||||||
|
CenterState(text = "Loading chats...", loading = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
!state.errorMessage.isNullOrBlank() -> {
|
||||||
|
CenterState(text = state.errorMessage, loading = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
state.chats.isEmpty() -> {
|
||||||
|
CenterState(text = "No chats found", loading = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
) {
|
||||||
|
items(
|
||||||
|
items = state.chats,
|
||||||
|
key = { it.id },
|
||||||
|
) { chat ->
|
||||||
|
ChatRow(
|
||||||
|
chat = chat,
|
||||||
|
onClick = { onOpenChat(chat.id) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ChatRow(
|
||||||
|
chat: ChatItem,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable(onClick = onClick)
|
||||||
|
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
) {
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Text(
|
||||||
|
text = chat.displayTitle,
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
fontWeight = if (chat.unreadCount > 0) FontWeight.SemiBold else FontWeight.Normal,
|
||||||
|
)
|
||||||
|
if (chat.pinned) {
|
||||||
|
Text(
|
||||||
|
text = " [PIN]",
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (chat.muted) {
|
||||||
|
Text(
|
||||||
|
text = " [MUTE]",
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
||||||
|
) {
|
||||||
|
if (chat.unreadMentionsCount > 0) {
|
||||||
|
BadgeChip(label = "@")
|
||||||
|
}
|
||||||
|
if (chat.unreadCount > 0) {
|
||||||
|
BadgeChip(label = chat.unreadCount.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val preview = chat.previewText()
|
||||||
|
if (preview.isNotBlank()) {
|
||||||
|
Text(
|
||||||
|
text = preview,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.padding(top = 2.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chat.type == "private") {
|
||||||
|
val presence = if (chat.counterpartIsOnline == true) {
|
||||||
|
"online"
|
||||||
|
} else {
|
||||||
|
chat.counterpartLastSeenAt?.let { "last seen recently" } ?: "offline"
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
text = presence,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
modifier = Modifier.padding(top = 2.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun BadgeChip(label: String) {
|
||||||
|
AssistChip(
|
||||||
|
onClick = {},
|
||||||
|
enabled = false,
|
||||||
|
label = { Text(text = label) },
|
||||||
|
colors = AssistChipDefaults.assistChipColors(
|
||||||
|
disabledContainerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||||
|
disabledLabelColor = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun CenterState(
|
||||||
|
text: String?,
|
||||||
|
loading: Boolean,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
|
if (loading) {
|
||||||
|
CircularProgressIndicator()
|
||||||
|
Spacer(modifier = Modifier.padding(8.dp))
|
||||||
|
}
|
||||||
|
if (!text.isNullOrBlank()) {
|
||||||
|
Text(text = text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ChatItem.previewText(): String {
|
||||||
|
val raw = lastMessageText.orEmpty().trim()
|
||||||
|
if (raw.isNotEmpty()) return raw
|
||||||
|
return when (lastMessageType) {
|
||||||
|
"image" -> "Photo"
|
||||||
|
"video" -> "Video"
|
||||||
|
"audio" -> "Audio"
|
||||||
|
"voice" -> "Voice message"
|
||||||
|
"file" -> "File"
|
||||||
|
"circle_video" -> "Video message"
|
||||||
|
null, "text" -> ""
|
||||||
|
else -> "Media"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package ru.daemonlord.messenger.ui.chats
|
||||||
|
|
||||||
|
import ru.daemonlord.messenger.domain.chat.model.ChatItem
|
||||||
|
|
||||||
|
data class ChatListUiState(
|
||||||
|
val selectedTab: ChatTab = ChatTab.ALL,
|
||||||
|
val searchQuery: String = "",
|
||||||
|
val isLoading: Boolean = true,
|
||||||
|
val isRefreshing: Boolean = false,
|
||||||
|
val errorMessage: String? = null,
|
||||||
|
val chats: List<ChatItem> = emptyList(),
|
||||||
|
)
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
package ru.daemonlord.messenger.ui.chats
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import ru.daemonlord.messenger.domain.chat.model.ChatItem
|
||||||
|
import ru.daemonlord.messenger.domain.chat.usecase.ObserveChatsUseCase
|
||||||
|
import ru.daemonlord.messenger.domain.chat.usecase.RefreshChatsUseCase
|
||||||
|
import ru.daemonlord.messenger.domain.common.AppError
|
||||||
|
import ru.daemonlord.messenger.domain.common.AppResult
|
||||||
|
import ru.daemonlord.messenger.domain.realtime.usecase.HandleRealtimeEventsUseCase
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class ChatListViewModel @Inject constructor(
|
||||||
|
private val observeChatsUseCase: ObserveChatsUseCase,
|
||||||
|
private val refreshChatsUseCase: RefreshChatsUseCase,
|
||||||
|
private val handleRealtimeEventsUseCase: HandleRealtimeEventsUseCase,
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
private val selectedTab = MutableStateFlow(ChatTab.ALL)
|
||||||
|
private val searchQuery = MutableStateFlow("")
|
||||||
|
private val _uiState = MutableStateFlow(ChatListUiState())
|
||||||
|
val uiState: StateFlow<ChatListUiState> = _uiState.asStateFlow()
|
||||||
|
|
||||||
|
init {
|
||||||
|
handleRealtimeEventsUseCase.start()
|
||||||
|
observeChatStream()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onTabSelected(tab: ChatTab) {
|
||||||
|
if (selectedTab.value == tab) return
|
||||||
|
selectedTab.value = tab
|
||||||
|
_uiState.update { it.copy(selectedTab = tab, isLoading = true, errorMessage = null) }
|
||||||
|
refreshCurrentTab()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onSearchChanged(value: String) {
|
||||||
|
searchQuery.value = value
|
||||||
|
_uiState.update { it.copy(searchQuery = value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onPullToRefresh() {
|
||||||
|
refreshCurrentTab(forceRefresh = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun observeChatStream() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
selectedTab
|
||||||
|
.flatMapLatest { tab ->
|
||||||
|
observeChatsUseCase(archived = tab == ChatTab.ARCHIVED)
|
||||||
|
}
|
||||||
|
.combine(searchQuery.distinctUntilChanged()) { chats, query ->
|
||||||
|
chats.filterByQuery(query)
|
||||||
|
}
|
||||||
|
.collectLatest { filtered ->
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(
|
||||||
|
isLoading = false,
|
||||||
|
isRefreshing = false,
|
||||||
|
errorMessage = null,
|
||||||
|
chats = filtered,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refreshCurrentTab(forceRefresh: Boolean = false) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(
|
||||||
|
isRefreshing = forceRefresh,
|
||||||
|
errorMessage = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val result = refreshChatsUseCase(archived = selectedTab.value == ChatTab.ARCHIVED)
|
||||||
|
if (result is AppResult.Error) {
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(
|
||||||
|
isRefreshing = false,
|
||||||
|
isLoading = false,
|
||||||
|
errorMessage = result.reason.toUiMessage(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun List<ChatItem>.filterByQuery(query: String): List<ChatItem> {
|
||||||
|
val normalized = query.trim().lowercase()
|
||||||
|
if (normalized.isBlank()) return this
|
||||||
|
return filter { chat ->
|
||||||
|
chat.displayTitle.lowercase().contains(normalized) ||
|
||||||
|
(chat.counterpartUsername?.lowercase()?.contains(normalized) == true) ||
|
||||||
|
(chat.handle?.lowercase()?.contains(normalized) == true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun AppError.toUiMessage(): String {
|
||||||
|
return when (this) {
|
||||||
|
AppError.Network -> "Network error while syncing chats."
|
||||||
|
AppError.Unauthorized -> "Session expired. Please log in again."
|
||||||
|
AppError.InvalidCredentials -> "Authorization failed."
|
||||||
|
is AppError.Server -> "Server error while loading chats."
|
||||||
|
is AppError.Unknown -> "Unknown error while loading chats."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCleared() {
|
||||||
|
handleRealtimeEventsUseCase.stop()
|
||||||
|
super.onCleared()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package ru.daemonlord.messenger.ui.chats
|
||||||
|
|
||||||
|
enum class ChatTab {
|
||||||
|
ALL,
|
||||||
|
ARCHIVED,
|
||||||
|
}
|
||||||
@@ -11,7 +11,9 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import androidx.navigation.NavType
|
||||||
import androidx.navigation.NavGraph.Companion.findStartDestination
|
import androidx.navigation.NavGraph.Companion.findStartDestination
|
||||||
|
import androidx.navigation.navArgument
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
@@ -19,12 +21,14 @@ import androidx.navigation.compose.navigation
|
|||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import ru.daemonlord.messenger.ui.auth.AuthViewModel
|
import ru.daemonlord.messenger.ui.auth.AuthViewModel
|
||||||
import ru.daemonlord.messenger.ui.auth.LoginScreen
|
import ru.daemonlord.messenger.ui.auth.LoginScreen
|
||||||
import ru.daemonlord.messenger.ui.chats.ChatsPlaceholderScreen
|
import ru.daemonlord.messenger.ui.chat.ChatScreenPlaceholder
|
||||||
|
import ru.daemonlord.messenger.ui.chats.ChatListRoute
|
||||||
|
|
||||||
private object Routes {
|
private object Routes {
|
||||||
const val AuthGraph = "auth_graph"
|
const val AuthGraph = "auth_graph"
|
||||||
const val Login = "login"
|
const val Login = "login"
|
||||||
const val Chats = "chats"
|
const val Chats = "chats"
|
||||||
|
const val Chat = "chat"
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -78,7 +82,21 @@ fun MessengerNavHost(
|
|||||||
}
|
}
|
||||||
|
|
||||||
composable(route = Routes.Chats) {
|
composable(route = Routes.Chats) {
|
||||||
ChatsPlaceholderScreen()
|
ChatListRoute(
|
||||||
|
onOpenChat = { chatId ->
|
||||||
|
navController.navigate("${Routes.Chat}/$chatId")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
composable(
|
||||||
|
route = "${Routes.Chat}/{chatId}",
|
||||||
|
arguments = listOf(
|
||||||
|
navArgument("chatId") { type = NavType.LongType }
|
||||||
|
),
|
||||||
|
) { backStackEntry ->
|
||||||
|
val chatId = backStackEntry.arguments?.getLong("chatId") ?: 0L
|
||||||
|
ChatScreenPlaceholder(chatId = chatId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user