android, desktop: fix chatView state propagation (#4676)

This commit is contained in:
Stanislav Dmitrenko
2024-08-13 19:11:04 +01:00
committed by GitHub
parent 2ae5a8bffd
commit 915bbed400
@@ -10,7 +10,6 @@ import androidx.compose.foundation.lazy.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.mapSaver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.*
import androidx.compose.ui.draw.drawWithCache
@@ -49,26 +48,17 @@ import kotlin.math.sign
// staleChatId means the id that was before chatModel.chatId becomes null. It's needed for Android only to make transition from chat
// to chat list smooth. Otherwise, chat view will become blank right before the transition starts
fun ChatView(staleChatId: State<String?>, onComposed: suspend (chatId: String) -> Unit) {
val shouldReturn = remember { mutableStateOf(false) }
val remoteHostId = remember { derivedStateOf { chatModel.chats.value.firstOrNull { chat -> chat.chatInfo.id == staleChatId.value }?.remoteHostId } }
val showSearch = rememberSaveable { mutableStateOf(false) }
val activeChatInfo = remember {
derivedStateOf {
val info = chatModel.chats.value.firstOrNull { chat -> chat.chatInfo.id == staleChatId.value }?.chatInfo
if (info == null) {
shouldReturn.value = true
}
return@derivedStateOf info ?: ChatInfo.Direct.sampleData
}
}
val activeChatInfo = remember { derivedStateOf { chatModel.chats.value.firstOrNull { chat -> chat.chatInfo.id == staleChatId.value }?.chatInfo } }
val user = chatModel.currentUser.value
if (shouldReturn.value || user == null) {
val chatInfo = activeChatInfo.value
if (chatInfo == null || user == null) {
LaunchedEffect(Unit) {
chatModel.chatId.value = null
ModalManager.end.closeModals()
}
} else {
val chatInfo = activeChatInfo.value
val searchText = rememberSaveable { mutableStateOf("") }
val useLinkPreviews = chatModel.controller.appPrefs.privacyLinkPreviews.get()
val composeState = rememberSaveable(saver = ComposeState.saver()) {
@@ -268,8 +258,8 @@ fun ChatView(staleChatId: State<String?>, onComposed: suspend (chatId: String) -
}
},
loadPrevMessages = {
if (chatModel.chatId.value != activeChatInfo.value.id) return@ChatLayout
val c = chatModel.getChat(chatModel.chatId.value ?: return@ChatLayout)
val c = chatModel.getChat(chatInfo.id)
if (chatModel.chatId.value != chatInfo.id) return@ChatLayout
val firstId = chatModel.chatItems.value.firstOrNull()?.id
if (c != null && firstId != null) {
withBGApi {
@@ -279,7 +269,6 @@ fun ChatView(staleChatId: State<String?>, onComposed: suspend (chatId: String) -
},
deleteMessage = { itemId, mode ->
withBGApi {
val cInfo = chatInfo
val toDeleteItem = chatModel.chatItems.value.firstOrNull { it.id == itemId }
val toModerate = toDeleteItem?.memberToModerate(chatInfo)
val groupInfo = toModerate?.first
@@ -295,8 +284,8 @@ fun ChatView(staleChatId: State<String?>, onComposed: suspend (chatId: String) -
} else {
chatModel.controller.apiDeleteChatItems(
chatRh,
type = cInfo.chatType,
id = cInfo.apiId,
type = chatInfo.chatType,
id = chatInfo.apiId,
itemIds = listOf(itemId),
mode = mode
)
@@ -307,9 +296,9 @@ fun ChatView(staleChatId: State<String?>, onComposed: suspend (chatId: String) -
toChatItem = deleted.toChatItem?.chatItem
withChats {
if (toChatItem != null) {
upsertChatItem(chatRh, cInfo, toChatItem)
upsertChatItem(chatRh, chatInfo, toChatItem)
} else {
removeChatItem(chatRh, cInfo, deletedChatItem)
removeChatItem(chatRh, chatInfo, deletedChatItem)
}
}
}
@@ -489,8 +478,8 @@ fun ChatView(staleChatId: State<String?>, onComposed: suspend (chatId: String) -
changeNtfsState = { enabled, currentValue -> toggleNotifications(chatRh, chatInfo, enabled, chatModel, currentValue) },
onSearchValueChanged = { value ->
if (searchText.value == value) return@ChatLayout
if (chatModel.chatId.value != activeChatInfo.value.id) return@ChatLayout
val c = chatModel.getChat(chatModel.chatId.value ?: return@ChatLayout) ?: return@ChatLayout
val c = chatModel.getChat(chatInfo.id) ?: return@ChatLayout
if (chatModel.chatId.value != chatInfo.id) return@ChatLayout
withBGApi {
apiFindMessages(c, chatModel, value)
searchText.value = value
@@ -559,7 +548,7 @@ fun startChatCall(remoteHostId: Long?, chatInfo: ChatInfo, media: CallMediaType)
@Composable
fun ChatLayout(
remoteHostId: State<Long?>,
chatInfo: State<ChatInfo>,
chatInfo: State<ChatInfo?>,
unreadCount: State<Int>,
composeState: MutableState<ComposeState>,
composeView: (@Composable () -> Unit),
@@ -609,7 +598,7 @@ fun ChatLayout(
Modifier
.fillMaxWidth()
.desktopOnExternalDrag(
enabled = !attachmentDisabled.value && rememberUpdatedState(chatInfo.value).value.userCanSend,
enabled = !attachmentDisabled.value && rememberUpdatedState(chatInfo.value).value?.userCanSend == true,
onFiles = { paths -> composeState.onFilesAttached(paths.map { it.toURI() }) },
onImage = {
// TODO: file is not saved anywhere?!
@@ -647,7 +636,10 @@ fun ChatLayout(
Scaffold(
topBar = {
if (selectedChatItems.value == null) {
ChatInfoToolbar(chatInfo, back, info, startCall, endCall, addMembers, openGroupLink, changeNtfsState, onSearchValueChanged, showSearch)
val chatInfo = chatInfo.value
if (chatInfo != null) {
ChatInfoToolbar(chatInfo, back, info, startCall, endCall, addMembers, openGroupLink, changeNtfsState, onSearchValueChanged, showSearch)
}
} else {
SelectedItemsTopToolbar(selectedChatItems)
}
@@ -672,13 +664,17 @@ fun ChatLayout(
Modifier)
.padding(contentPadding)
) {
ChatItemsList(
remoteHostId, chatInfo, unreadCount, composeState, searchValue,
useLinkPreviews, linkMode, selectedChatItems, showMemberInfo, loadPrevMessages, deleteMessage, deleteMessages,
receiveFile, cancelFile, joinGroup, acceptCall, acceptFeature, openDirectChat, forwardItem,
updateContactStats, updateMemberStats, syncContactConnection, syncMemberConnection, findModelChat, findModelMember,
setReaction, showItemDetails, markRead, setFloatingButton, onComposed, developerTools, showViaProxy,
)
val remoteHostId = remember { remoteHostId }.value
val chatInfo = remember { chatInfo }.value
if (chatInfo != null) {
ChatItemsList(
remoteHostId, chatInfo, unreadCount, composeState, searchValue,
useLinkPreviews, linkMode, selectedChatItems, showMemberInfo, loadPrevMessages, deleteMessage, deleteMessages,
receiveFile, cancelFile, joinGroup, acceptCall, acceptFeature, openDirectChat, forwardItem,
updateContactStats, updateMemberStats, syncContactConnection, syncMemberConnection, findModelChat, findModelMember,
setReaction, showItemDetails, markRead, setFloatingButton, onComposed, developerTools, showViaProxy,
)
}
}
}
}
@@ -688,7 +684,7 @@ fun ChatLayout(
@Composable
fun ChatInfoToolbar(
chatInfo: State<ChatInfo>,
chatInfo: ChatInfo,
back: () -> Unit,
info: () -> Unit,
startCall: (CallMediaType) -> Unit,
@@ -713,7 +709,6 @@ fun ChatInfoToolbar(
if (appPlatform.isAndroid) {
BackHandler(onBack = onBackClicked)
}
val chatInfo = chatInfo.value
val barButtons = arrayListOf<@Composable RowScope.() -> Unit>()
val menuItems = arrayListOf<@Composable () -> Unit>()
val activeCall by remember { chatModel.activeCall }
@@ -918,22 +913,10 @@ private fun ContactVerifiedShield() {
Icon(painterResource(MR.images.ic_verified_user), null, Modifier.size(18.dp * fontSizeSqrtMultiplier).padding(end = 3.dp, top = 1.dp), tint = MaterialTheme.colors.secondary)
}
data class CIListState(val scrolled: Boolean, val itemCount: Int, val keyboardState: KeyboardState)
val CIListStateSaver = run {
val scrolledKey = "scrolled"
val countKey = "itemCount"
val keyboardKey = "keyboardState"
mapSaver(
save = { mapOf(scrolledKey to it.scrolled, countKey to it.itemCount, keyboardKey to it.keyboardState) },
restore = { CIListState(it[scrolledKey] as Boolean, it[countKey] as Int, it[keyboardKey] as KeyboardState) }
)
}
@Composable
fun BoxWithConstraintsScope.ChatItemsList(
remoteHostId: State<Long?>,
chatInfo: State<ChatInfo>,
remoteHostId: Long?,
chatInfo: ChatInfo,
unreadCount: State<Int>,
composeState: MutableState<ComposeState>,
searchValue: State<String>,
@@ -967,8 +950,6 @@ fun BoxWithConstraintsScope.ChatItemsList(
) {
val listState = rememberLazyListState()
val scope = rememberCoroutineScope()
val remoteHostId = remember { remoteHostId }.value
val chatInfo = remember { chatInfo }.value
ScrollToBottom(chatInfo.id, listState, chatModel.chatItems)
var prevSearchEmptiness by rememberSaveable { mutableStateOf(searchValue.value.isEmpty()) }
// Scroll to bottom when search value changes from something to nothing and back