android: implement message screen ui with compose actions
Some checks failed
CI / test (push) Failing after 2m8s
Some checks failed
CI / test (push) Failing after 2m8s
This commit is contained in:
@@ -84,3 +84,10 @@
|
|||||||
- Updated unified realtime handler to write `receive_message`, `message_updated`, `message_deleted` into `messages` Room state.
|
- Updated unified realtime handler to write `receive_message`, `message_updated`, `message_deleted` into `messages` Room state.
|
||||||
- Added delivery/read status updates in Room for message status events.
|
- Added delivery/read status updates in Room for message status events.
|
||||||
- Kept chat list sync updates in the same manager/use-case pipeline for consistency.
|
- Kept chat list sync updates in the same manager/use-case pipeline for consistency.
|
||||||
|
|
||||||
|
### Step 14 - Sprint A / 4) Message UI core
|
||||||
|
- Replaced chat placeholder with a real message screen route + ViewModel.
|
||||||
|
- Added message list rendering with Telegram-like bubble alignment and status hints.
|
||||||
|
- Added input composer with send flow, reply/edit modes, and inline action cancellation.
|
||||||
|
- Added long-press actions (`reply`, `edit`, `delete`) for baseline message operations.
|
||||||
|
- Added manual "load older" pagination trigger and chat back navigation integration.
|
||||||
|
|||||||
@@ -0,0 +1,225 @@
|
|||||||
|
package ru.daemonlord.messenger.ui.chat
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.combinedClickable
|
||||||
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
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.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.Button
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.OutlinedTextField
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.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.message.model.MessageItem
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ChatRoute(
|
||||||
|
onBack: () -> Unit,
|
||||||
|
viewModel: ChatViewModel = hiltViewModel(),
|
||||||
|
) {
|
||||||
|
val state by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
|
ChatScreen(
|
||||||
|
state = state,
|
||||||
|
onBack = onBack,
|
||||||
|
onInputChanged = viewModel::onInputChanged,
|
||||||
|
onSendClick = viewModel::onSendClick,
|
||||||
|
onSelectMessage = viewModel::onSelectMessage,
|
||||||
|
onReplySelected = viewModel::onReplySelected,
|
||||||
|
onEditSelected = viewModel::onEditSelected,
|
||||||
|
onDeleteSelected = viewModel::onDeleteSelected,
|
||||||
|
onCancelComposeAction = viewModel::onCancelComposeAction,
|
||||||
|
onLoadMore = viewModel::loadMore,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ChatScreen(
|
||||||
|
state: MessageUiState,
|
||||||
|
onBack: () -> Unit,
|
||||||
|
onInputChanged: (String) -> Unit,
|
||||||
|
onSendClick: () -> Unit,
|
||||||
|
onSelectMessage: (MessageItem?) -> Unit,
|
||||||
|
onReplySelected: (MessageItem) -> Unit,
|
||||||
|
onEditSelected: (MessageItem) -> Unit,
|
||||||
|
onDeleteSelected: (Boolean) -> Unit,
|
||||||
|
onCancelComposeAction: () -> Unit,
|
||||||
|
onLoadMore: () -> Unit,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(MaterialTheme.colorScheme.surfaceVariant)
|
||||||
|
.padding(horizontal = 12.dp, vertical = 10.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
) {
|
||||||
|
Button(onClick = onBack) { Text("Back") }
|
||||||
|
Text(
|
||||||
|
text = "Chat #${state.chatId}",
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
)
|
||||||
|
Button(onClick = onLoadMore, enabled = !state.isLoadingMore) {
|
||||||
|
Text(if (state.isLoadingMore) "..." else "Load older")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when {
|
||||||
|
state.isLoading -> {
|
||||||
|
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
|
CircularProgressIndicator()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||||
|
) {
|
||||||
|
items(state.messages, key = { it.id }) { message ->
|
||||||
|
MessageBubble(
|
||||||
|
message = message,
|
||||||
|
isSelected = state.selectedMessage?.id == message.id,
|
||||||
|
onLongPress = { onSelectMessage(message) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.selectedMessage != null) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 12.dp, vertical = 6.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
) {
|
||||||
|
Button(onClick = { onReplySelected(state.selectedMessage) }) { Text("Reply") }
|
||||||
|
Button(onClick = { onEditSelected(state.selectedMessage) }) { Text("Edit") }
|
||||||
|
Button(onClick = { onDeleteSelected(false) }) { Text("Delete") }
|
||||||
|
Button(onClick = { onSelectMessage(null) }) { Text("Close") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.replyToMessage != null || state.editingMessage != null) {
|
||||||
|
val header = if (state.editingMessage != null) {
|
||||||
|
"Editing message #${state.editingMessage.id}"
|
||||||
|
} else {
|
||||||
|
"Reply to #${state.replyToMessage?.id}"
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(MaterialTheme.colorScheme.secondaryContainer)
|
||||||
|
.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Text(text = header, style = MaterialTheme.typography.bodySmall)
|
||||||
|
Button(onClick = onCancelComposeAction) { Text("Cancel") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
) {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = state.inputText,
|
||||||
|
onValueChange = onInputChanged,
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
label = { Text("Message") },
|
||||||
|
maxLines = 4,
|
||||||
|
)
|
||||||
|
Button(
|
||||||
|
onClick = onSendClick,
|
||||||
|
enabled = !state.isSending && state.inputText.isNotBlank(),
|
||||||
|
) {
|
||||||
|
Text(if (state.isSending) "..." else "Send")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state.errorMessage.isNullOrBlank()) {
|
||||||
|
Text(
|
||||||
|
text = state.errorMessage,
|
||||||
|
color = MaterialTheme.colorScheme.error,
|
||||||
|
modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
|
private fun MessageBubble(
|
||||||
|
message: MessageItem,
|
||||||
|
isSelected: Boolean,
|
||||||
|
onLongPress: () -> Unit,
|
||||||
|
) {
|
||||||
|
val isOutgoing = message.isOutgoing
|
||||||
|
val bubbleColor = if (isOutgoing) {
|
||||||
|
MaterialTheme.colorScheme.primaryContainer
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.surfaceVariant
|
||||||
|
}
|
||||||
|
val alignment = if (isOutgoing) Alignment.End else Alignment.Start
|
||||||
|
Column(modifier = Modifier.fillMaxWidth(), horizontalAlignment = alignment) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth(0.86f)
|
||||||
|
.background(
|
||||||
|
if (isSelected) MaterialTheme.colorScheme.tertiaryContainer else bubbleColor
|
||||||
|
)
|
||||||
|
.combinedClickable(
|
||||||
|
onClick = {},
|
||||||
|
onLongClick = onLongPress,
|
||||||
|
)
|
||||||
|
.padding(horizontal = 10.dp, vertical = 8.dp),
|
||||||
|
) {
|
||||||
|
if (!isOutgoing && !message.senderDisplayName.isNullOrBlank()) {
|
||||||
|
Text(
|
||||||
|
text = message.senderDisplayName,
|
||||||
|
style = MaterialTheme.typography.labelMedium,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
text = message.text ?: "[${message.type}]",
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
)
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.End,
|
||||||
|
) {
|
||||||
|
val status = if (message.status.isNullOrBlank()) "" else " ${message.status}"
|
||||||
|
Text(
|
||||||
|
text = "${message.id}$status",
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
package ru.daemonlord.messenger.ui.chat
|
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun ChatScreenPlaceholder(
|
|
||||||
chatId: Long,
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
verticalArrangement = Arrangement.Center,
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = "Chat Screen",
|
|
||||||
style = MaterialTheme.typography.headlineSmall,
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = "chatId=$chatId",
|
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
package ru.daemonlord.messenger.ui.chat
|
||||||
|
|
||||||
|
import androidx.lifecycle.SavedStateHandle
|
||||||
|
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.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import ru.daemonlord.messenger.domain.common.AppError
|
||||||
|
import ru.daemonlord.messenger.domain.common.AppResult
|
||||||
|
import ru.daemonlord.messenger.domain.message.model.MessageItem
|
||||||
|
import ru.daemonlord.messenger.domain.message.usecase.DeleteMessageUseCase
|
||||||
|
import ru.daemonlord.messenger.domain.message.usecase.EditMessageUseCase
|
||||||
|
import ru.daemonlord.messenger.domain.message.usecase.LoadMoreMessagesUseCase
|
||||||
|
import ru.daemonlord.messenger.domain.message.usecase.ObserveMessagesUseCase
|
||||||
|
import ru.daemonlord.messenger.domain.message.usecase.SendTextMessageUseCase
|
||||||
|
import ru.daemonlord.messenger.domain.message.usecase.SyncRecentMessagesUseCase
|
||||||
|
import ru.daemonlord.messenger.domain.realtime.usecase.HandleRealtimeEventsUseCase
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class ChatViewModel @Inject constructor(
|
||||||
|
savedStateHandle: SavedStateHandle,
|
||||||
|
private val observeMessagesUseCase: ObserveMessagesUseCase,
|
||||||
|
private val syncRecentMessagesUseCase: SyncRecentMessagesUseCase,
|
||||||
|
private val loadMoreMessagesUseCase: LoadMoreMessagesUseCase,
|
||||||
|
private val sendTextMessageUseCase: SendTextMessageUseCase,
|
||||||
|
private val editMessageUseCase: EditMessageUseCase,
|
||||||
|
private val deleteMessageUseCase: DeleteMessageUseCase,
|
||||||
|
private val handleRealtimeEventsUseCase: HandleRealtimeEventsUseCase,
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
private val chatId: Long = checkNotNull(savedStateHandle["chatId"])
|
||||||
|
private val _uiState = MutableStateFlow(MessageUiState(chatId = chatId))
|
||||||
|
val uiState: StateFlow<MessageUiState> = _uiState.asStateFlow()
|
||||||
|
|
||||||
|
init {
|
||||||
|
handleRealtimeEventsUseCase.start()
|
||||||
|
observeMessages()
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onInputChanged(value: String) {
|
||||||
|
_uiState.update { it.copy(inputText = value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onSelectMessage(message: MessageItem?) {
|
||||||
|
_uiState.update { it.copy(selectedMessage = message) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onReplySelected(message: MessageItem) {
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(
|
||||||
|
replyToMessage = message,
|
||||||
|
editingMessage = null,
|
||||||
|
selectedMessage = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onEditSelected(message: MessageItem) {
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(
|
||||||
|
editingMessage = message,
|
||||||
|
replyToMessage = null,
|
||||||
|
selectedMessage = null,
|
||||||
|
inputText = message.text.orEmpty(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onCancelComposeAction() {
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(
|
||||||
|
replyToMessage = null,
|
||||||
|
editingMessage = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onDeleteSelected(forAll: Boolean = false) {
|
||||||
|
val selected = uiState.value.selectedMessage ?: return
|
||||||
|
viewModelScope.launch {
|
||||||
|
when (val result = deleteMessageUseCase(selected.id, forAll = forAll)) {
|
||||||
|
is AppResult.Success -> _uiState.update { it.copy(selectedMessage = null) }
|
||||||
|
is AppResult.Error -> _uiState.update { it.copy(errorMessage = result.reason.toUiMessage()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onSendClick() {
|
||||||
|
val text = uiState.value.inputText.trim()
|
||||||
|
if (text.isBlank()) return
|
||||||
|
|
||||||
|
viewModelScope.launch {
|
||||||
|
_uiState.update { it.copy(isSending = true, errorMessage = null) }
|
||||||
|
val editing = uiState.value.editingMessage
|
||||||
|
val result = if (editing != null) {
|
||||||
|
editMessageUseCase(messageId = editing.id, newText = text)
|
||||||
|
} else {
|
||||||
|
sendTextMessageUseCase(
|
||||||
|
chatId = chatId,
|
||||||
|
text = text,
|
||||||
|
replyToMessageId = uiState.value.replyToMessage?.id,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (result) {
|
||||||
|
is AppResult.Success -> {
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(
|
||||||
|
isSending = false,
|
||||||
|
inputText = "",
|
||||||
|
editingMessage = null,
|
||||||
|
replyToMessage = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is AppResult.Error -> {
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(
|
||||||
|
isSending = false,
|
||||||
|
errorMessage = result.reason.toUiMessage(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadMore() {
|
||||||
|
val oldest = uiState.value.messages.firstOrNull() ?: return
|
||||||
|
viewModelScope.launch {
|
||||||
|
_uiState.update { it.copy(isLoadingMore = true) }
|
||||||
|
when (val result = loadMoreMessagesUseCase(chatId, beforeMessageId = oldest.id)) {
|
||||||
|
is AppResult.Success -> _uiState.update { it.copy(isLoadingMore = false) }
|
||||||
|
is AppResult.Error -> _uiState.update {
|
||||||
|
it.copy(
|
||||||
|
isLoadingMore = false,
|
||||||
|
errorMessage = result.reason.toUiMessage(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun observeMessages() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
observeMessagesUseCase(chatId).collectLatest { messages ->
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(
|
||||||
|
isLoading = false,
|
||||||
|
messages = messages.sortedBy { msg -> msg.id },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refresh() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_uiState.update { it.copy(isLoading = true, errorMessage = null) }
|
||||||
|
when (val result = syncRecentMessagesUseCase(chatId = chatId)) {
|
||||||
|
is AppResult.Success -> _uiState.update { it.copy(isLoading = false) }
|
||||||
|
is AppResult.Error -> _uiState.update {
|
||||||
|
it.copy(
|
||||||
|
isLoading = false,
|
||||||
|
errorMessage = result.reason.toUiMessage(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun AppError.toUiMessage(): String {
|
||||||
|
return when (this) {
|
||||||
|
AppError.Network -> "Network error."
|
||||||
|
AppError.Unauthorized -> "Session expired."
|
||||||
|
AppError.InvalidCredentials -> "Authorization error."
|
||||||
|
is AppError.Server -> "Server error."
|
||||||
|
is AppError.Unknown -> "Unknown error."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package ru.daemonlord.messenger.ui.chat
|
||||||
|
|
||||||
|
import ru.daemonlord.messenger.domain.message.model.MessageItem
|
||||||
|
|
||||||
|
data class MessageUiState(
|
||||||
|
val chatId: Long = 0L,
|
||||||
|
val isLoading: Boolean = true,
|
||||||
|
val isLoadingMore: Boolean = false,
|
||||||
|
val isSending: Boolean = false,
|
||||||
|
val errorMessage: String? = null,
|
||||||
|
val messages: List<MessageItem> = emptyList(),
|
||||||
|
val inputText: String = "",
|
||||||
|
val replyToMessage: MessageItem? = null,
|
||||||
|
val editingMessage: MessageItem? = null,
|
||||||
|
val selectedMessage: MessageItem? = null,
|
||||||
|
)
|
||||||
@@ -21,7 +21,7 @@ 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.chat.ChatScreenPlaceholder
|
import ru.daemonlord.messenger.ui.chat.ChatRoute
|
||||||
import ru.daemonlord.messenger.ui.chats.ChatListRoute
|
import ru.daemonlord.messenger.ui.chats.ChatListRoute
|
||||||
|
|
||||||
private object Routes {
|
private object Routes {
|
||||||
@@ -95,8 +95,9 @@ fun MessengerNavHost(
|
|||||||
navArgument("chatId") { type = NavType.LongType }
|
navArgument("chatId") { type = NavType.LongType }
|
||||||
),
|
),
|
||||||
) { backStackEntry ->
|
) { backStackEntry ->
|
||||||
val chatId = backStackEntry.arguments?.getLong("chatId") ?: 0L
|
ChatRoute(
|
||||||
ChatScreenPlaceholder(chatId = chatId)
|
onBack = { navController.popBackStack() },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user