From 65eae502c0e7990d9c92a620282c9b094c7bfe05 Mon Sep 17 00:00:00 2001 From: Avently <7953703+avently@users.noreply.github.com> Date: Wed, 6 Sep 2023 22:20:37 +0800 Subject: [PATCH] android: changing a chat on user change --- .../kotlin/chat/simplex/common/App.kt | 22 ++++++++++--------- .../simplex/common/views/chat/ChatView.kt | 21 +++++++++--------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/App.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/App.kt index cb386be7a3..7154c54243 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/App.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/App.kt @@ -32,8 +32,7 @@ import chat.simplex.res.MR import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.stringResource import kotlinx.coroutines.* -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.* data class SettingsViewState( val userPickerState: MutableStateFlow, @@ -194,24 +193,27 @@ fun AndroidScreen(settingsState: SettingsViewState) { StartPartOfScreen(settingsState) } val scope = rememberCoroutineScope() - val onComposed: () -> Unit = { + val onComposed: suspend (chatId: String?) -> Unit = { chatId -> + // coroutine, scope and join() because: + // - it should be run from coroutine to wait until this function finishes + // - without using scope.launch it throws CancellationException when changing user + // - join allows to wait until completion scope.launch { offset.animateTo( - if (chatModel.chatId.value == null) 0f else maxWidth.value, + if (chatId == null) 0f else maxWidth.value, chatListAnimationSpec() ) - if (offset.value == 0f) { - currentChatId = null - } - } + }.join() } LaunchedEffect(Unit) { launch { snapshotFlow { chatModel.chatId.value } .distinctUntilChanged() .collect { - if (it != null) currentChatId = it - else onComposed() + if (it == null) { + onComposed(null) + } + currentChatId = it } } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt index 3370d34e73..e8afdfeb21 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt @@ -43,7 +43,7 @@ import java.net.URI import kotlin.math.sign @Composable -fun ChatView(chatId: String, chatModel: ChatModel, onComposed: () -> Unit) { +fun ChatView(chatId: String, chatModel: ChatModel, onComposed: suspend (chatId: String) -> Unit) { val activeChat = remember { mutableStateOf(chatModel.chats.firstOrNull { chat -> chat.chatInfo.id == chatId }) } val searchText = rememberSaveable { mutableStateOf("") } val user = chatModel.currentUser.value @@ -66,12 +66,11 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: () -> Unit) { launch { snapshotFlow { chatModel.chatId.value } .distinctUntilChanged() + .filter { it != null && activeChat.value?.id != it } .collect { chatId -> - if (activeChat.value?.id != chatId && chatId != null) { - // Redisplay the whole hierarchy if the chat is different to make going from groups to direct chat working correctly - // Also for situation when chatId changes after clicking in notification, etc - activeChat.value = chatModel.getChat(chatId) - } + // Redisplay the whole hierarchy if the chat is different to make going from groups to direct chat working correctly + // Also for situation when chatId changes after clicking in notification, etc + activeChat.value = chatModel.getChat(chatId!!) markUnreadChatAsRead(activeChat, chatModel) } } @@ -91,7 +90,7 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: () -> Unit) { } .distinctUntilChanged() // Only changed chatInfo is important thing. Other properties can be skipped for reducing recompositions - .filter { it?.chatInfo != activeChat.value?.chatInfo && it != null } + .filter { it != null && it?.chatInfo != activeChat.value?.chatInfo } .collect { activeChat.value = it } } } @@ -422,7 +421,7 @@ fun ChatLayout( markRead: (CC.ItemRange, unreadCountAfter: Int?) -> Unit, changeNtfsState: (Boolean, currentValue: MutableState) -> Unit, onSearchValueChanged: (String) -> Unit, - onComposed: () -> Unit, + onComposed: suspend (chatId: String) -> Unit, ) { val scope = rememberCoroutineScope() val attachmentDisabled = remember { derivedStateOf { composeState.value.attachmentDisabled } } @@ -672,7 +671,7 @@ fun BoxWithConstraintsScope.ChatItemsList( showItemDetails: (ChatInfo, ChatItem) -> Unit, markRead: (CC.ItemRange, unreadCountAfter: Int?) -> Unit, setFloatingButton: (@Composable () -> Unit) -> Unit, - onComposed: () -> Unit, + onComposed: suspend (chatId: String) -> Unit, ) { val listState = rememberLazyListState() val scope = rememberCoroutineScope() @@ -703,13 +702,13 @@ fun BoxWithConstraintsScope.ChatItemsList( scope.launch { listState.animateScrollToItem(kotlin.math.min(reversedChatItems.lastIndex, index + 1), -maxHeightRounded) } } } - LaunchedEffect(Unit) { + LaunchedEffect(chat.id) { var stopListening = false snapshotFlow { listState.layoutInfo.visibleItemsInfo.lastIndex } .distinctUntilChanged() .filter { !stopListening } .collect { - onComposed() + onComposed(chat.id) stopListening = true } }