Chat mute UX: dynamic channel toggle, top-bar indicator, no success toast
Some checks failed
Android CI / android (push) Failing after 5m21s
Android Release / release (push) Has started running
CI / test (push) Has been cancelled

This commit is contained in:
2026-03-11 05:45:46 +03:00
parent 10676e34ad
commit 732b21a4e3
5 changed files with 37 additions and 10 deletions

View File

@@ -133,6 +133,7 @@ import androidx.compose.material.icons.filled.RadioButtonChecked
import androidx.compose.material.icons.filled.RadioButtonUnchecked import androidx.compose.material.icons.filled.RadioButtonUnchecked
import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Notifications import androidx.compose.material.icons.filled.Notifications
import androidx.compose.material.icons.filled.NotificationsOff
import androidx.compose.material.icons.filled.Pause import androidx.compose.material.icons.filled.Pause
import androidx.compose.material.icons.filled.Info import androidx.compose.material.icons.filled.Info
import androidx.compose.material.icons.filled.Image import androidx.compose.material.icons.filled.Image
@@ -692,11 +693,24 @@ fun ChatScreen(
maxLines = 1, maxLines = 1,
) )
if (state.chatSubtitle.isNotBlank()) { if (state.chatSubtitle.isNotBlank()) {
Text( Row(
text = state.chatSubtitle, verticalAlignment = Alignment.CenterVertically,
style = MaterialTheme.typography.labelSmall, horizontalArrangement = Arrangement.spacedBy(4.dp),
color = MaterialTheme.colorScheme.onSurfaceVariant, ) {
) Text(
text = state.chatSubtitle,
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
if (state.chatMuted) {
Icon(
imageVector = Icons.Filled.NotificationsOff,
contentDescription = null,
tint = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.size(13.dp),
)
}
}
} }
} }
} }
@@ -719,7 +733,12 @@ fun ChatScreen(
) { ) {
DropdownMenuItem( DropdownMenuItem(
text = { Text(stringResource(id = R.string.chat_menu_notifications)) }, text = { Text(stringResource(id = R.string.chat_menu_notifications)) },
leadingIcon = { Icon(Icons.Filled.Notifications, contentDescription = null) }, leadingIcon = {
Icon(
if (state.chatMuted) Icons.Filled.NotificationsOff else Icons.Filled.Notifications,
contentDescription = null,
)
},
onClick = { onClick = {
showChatMenu = false showChatMenu = false
onToggleChatNotifications() onToggleChatNotifications()
@@ -1426,6 +1445,7 @@ fun ChatScreen(
} else if (isChannelChat && !state.canSendMessages) { } else if (isChannelChat && !state.canSendMessages) {
ChannelReadOnlyBar( ChannelReadOnlyBar(
onToggleNotifications = onToggleChatNotifications, onToggleNotifications = onToggleChatNotifications,
isMuted = state.chatMuted,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.navigationBarsPadding() .navigationBarsPadding()
@@ -2745,6 +2765,7 @@ private fun DaySeparatorChip(label: String) {
@Composable @Composable
private fun ChannelReadOnlyBar( private fun ChannelReadOnlyBar(
onToggleNotifications: () -> Unit, onToggleNotifications: () -> Unit,
isMuted: Boolean,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
Row( Row(
@@ -2760,7 +2781,11 @@ private fun ChannelReadOnlyBar(
.clickable { onToggleNotifications() }, .clickable { onToggleNotifications() },
) { ) {
Text( Text(
text = stringResource(id = R.string.chat_enable_sound), text = if (isMuted) {
stringResource(id = R.string.chat_enable_sound)
} else {
stringResource(id = R.string.chat_disable_sound)
},
modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp), modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp),
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
textAlign = androidx.compose.ui.text.style.TextAlign.Center, textAlign = androidx.compose.ui.text.style.TextAlign.Center,

View File

@@ -456,9 +456,7 @@ class ChatViewModel @Inject constructor(
when (val current = chatRepository.getChatNotifications(chatId = chatId)) { when (val current = chatRepository.getChatNotifications(chatId = chatId)) {
is AppResult.Success -> { is AppResult.Success -> {
when (val updated = chatRepository.updateChatNotifications(chatId = chatId, muted = !current.data.muted)) { when (val updated = chatRepository.updateChatNotifications(chatId = chatId, muted = !current.data.muted)) {
is AppResult.Success -> _uiState.update { is AppResult.Success -> _uiState.update { it.copy(chatMuted = updated.data.muted) }
it.copy(errorMessage = if (updated.data.muted) "Notifications muted." else "Notifications enabled.")
}
is AppResult.Error -> _uiState.update { it.copy(errorMessage = updated.reason.toUiMessage()) } is AppResult.Error -> _uiState.update { it.copy(errorMessage = updated.reason.toUiMessage()) }
} }
} }
@@ -760,6 +758,7 @@ class ChatViewModel @Inject constructor(
it.copy( it.copy(
chatType = chat.type, chatType = chat.type,
chatRole = role, chatRole = role,
chatMuted = chat.muted,
chatTitle = chatTitle, chatTitle = chatTitle,
chatSubtitle = chatSubtitle, chatSubtitle = chatSubtitle,
chatAvatarUrl = chat.avatarUrl ?: chat.counterpartAvatarUrl, chatAvatarUrl = chat.avatarUrl ?: chat.counterpartAvatarUrl,

View File

@@ -18,6 +18,7 @@ data class MessageUiState(
val chatAvatarUrl: String? = null, val chatAvatarUrl: String? = null,
val chatType: String = "", val chatType: String = "",
val chatRole: String? = null, val chatRole: String? = null,
val chatMuted: Boolean = false,
val chatUnreadCount: Int = 0, val chatUnreadCount: Int = 0,
val chatMembers: List<ChatMemberItem> = emptyList(), val chatMembers: List<ChatMemberItem> = emptyList(),
val chatBans: List<ChatBanItem> = emptyList(), val chatBans: List<ChatBanItem> = emptyList(),

View File

@@ -64,6 +64,7 @@
<string name="chat_delete_dialog_confirm">Удалить диалог у вас? История будет очищена.</string> <string name="chat_delete_dialog_confirm">Удалить диалог у вас? История будет очищена.</string>
<string name="chat_leave_delete_confirm">Выйти из чата и убрать его из списка?</string> <string name="chat_leave_delete_confirm">Выйти из чата и убрать его из списка?</string>
<string name="chat_enable_sound">Включить звук</string> <string name="chat_enable_sound">Включить звук</string>
<string name="chat_disable_sound">Выключить звук</string>
<string name="chat_circle_video">Видеокружок</string> <string name="chat_circle_video">Видеокружок</string>
<string name="settings_user_fallback">Пользователь</string> <string name="settings_user_fallback">Пользователь</string>

View File

@@ -64,6 +64,7 @@
<string name="chat_delete_dialog_confirm">Delete dialog for you? History will be cleared.</string> <string name="chat_delete_dialog_confirm">Delete dialog for you? History will be cleared.</string>
<string name="chat_leave_delete_confirm">Leave the chat and remove it from your list?</string> <string name="chat_leave_delete_confirm">Leave the chat and remove it from your list?</string>
<string name="chat_enable_sound">Enable sound</string> <string name="chat_enable_sound">Enable sound</string>
<string name="chat_disable_sound">Disable sound</string>
<string name="chat_circle_video">Circle video</string> <string name="chat_circle_video">Circle video</string>
<string name="settings_user_fallback">User</string> <string name="settings_user_fallback">User</string>