From 5257c6f9ca01a40eb3441f8f1b6e654379f65655 Mon Sep 17 00:00:00 2001 From: Stanislav Dmitrenko <7953703+avently@users.noreply.github.com> Date: Tue, 30 Jul 2024 13:33:15 +0700 Subject: [PATCH] android, desktop: fix crash on fast clicking every chat in list (#4536) Co-authored-by: Evgeny Poberezkin --- .../simplex/common/views/chat/ChatView.kt | 4 ++- .../views/chatlist/ChatListNavLinkView.kt | 34 ++++++++++--------- .../views/chatlist/ShareListNavLinkView.kt | 8 +++-- 3 files changed, 26 insertions(+), 20 deletions(-) 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 610d8d95e9..ce91834838 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 @@ -341,7 +341,7 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: suspend (chatId: } }, openDirectChat = { contactId -> - withBGApi { + scope.launch { openDirectChat(chatRh, contactId, chatModel) } }, @@ -1156,6 +1156,8 @@ private fun ScrollToBottom(chatId: ChatId, listState: LazyListState, chatItems: * this coroutine will be canceled with the message "Current mutation had a higher priority" because of animatedScroll. * Which breaks auto-scrolling to bottom. So just ignoring the exception * */ + } catch (e: IllegalArgumentException) { + Log.e(TAG, "Failed to scroll: ${e.stackTraceToString()}") } } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt index dc32bb1318..f6d23c020f 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt @@ -28,7 +28,7 @@ import chat.simplex.common.views.chat.item.ItemAction import chat.simplex.common.views.helpers.* import chat.simplex.common.views.newchat.* import chat.simplex.res.MR -import kotlinx.coroutines.delay +import kotlinx.coroutines.* import kotlinx.datetime.Clock @Composable @@ -56,6 +56,8 @@ fun ChatListNavLinkView(chat: Chat, nextChatSelected: State) { } } + val scope = rememberCoroutineScope() + when (chat.chatInfo) { is ChatInfo.Direct -> { val contactNetworkStatus = chatModel.contactNetworkStatus(chat.chatInfo.contact) @@ -65,7 +67,7 @@ fun ChatListNavLinkView(chat: Chat, nextChatSelected: State) { ChatPreviewView(chat, showChatPreviews, chatModel.draft.value, chatModel.draftChatId.value, chatModel.currentUser.value?.profile?.displayName, contactNetworkStatus, disabled, linkMode, inProgress = false, progressByTimeout = false) } }, - click = { directChatAction(chat.remoteHostId, chat.chatInfo.contact, chatModel) }, + click = { scope.launch { directChatAction(chat.remoteHostId, chat.chatInfo.contact, chatModel) } }, dropdownMenuItems = { tryOrShowError("${chat.id}ChatListNavLinkDropdown", error = {}) { ContactMenuItems(chat, chat.chatInfo.contact, chatModel, showMenu, showMarkRead) @@ -84,7 +86,7 @@ fun ChatListNavLinkView(chat: Chat, nextChatSelected: State) { ChatPreviewView(chat, showChatPreviews, chatModel.draft.value, chatModel.draftChatId.value, chatModel.currentUser.value?.profile?.displayName, null, disabled, linkMode, inProgress.value, progressByTimeout) } }, - click = { if (!inProgress.value) groupChatAction(chat.remoteHostId, chat.chatInfo.groupInfo, chatModel, inProgress) }, + click = { if (!inProgress.value) scope.launch { groupChatAction(chat.remoteHostId, chat.chatInfo.groupInfo, chatModel, inProgress) } }, dropdownMenuItems = { tryOrShowError("${chat.id}ChatListNavLinkDropdown", error = {}) { GroupMenuItems(chat, chat.chatInfo.groupInfo, chatModel, showMenu, inProgress, showMarkRead) @@ -102,7 +104,7 @@ fun ChatListNavLinkView(chat: Chat, nextChatSelected: State) { ChatPreviewView(chat, showChatPreviews, chatModel.draft.value, chatModel.draftChatId.value, chatModel.currentUser.value?.profile?.displayName, null, disabled, linkMode, inProgress = false, progressByTimeout = false) } }, - click = { noteFolderChatAction(chat.remoteHostId, chat.chatInfo.noteFolder) }, + click = { scope.launch { noteFolderChatAction(chat.remoteHostId, chat.chatInfo.noteFolder) } }, dropdownMenuItems = { tryOrShowError("${chat.id}ChatListNavLinkDropdown", error = {}) { NoteFolderMenuItems(chat, showMenu, showMarkRead) @@ -178,42 +180,42 @@ private fun ErrorChatListItem() { } } -fun directChatAction(rhId: Long?, contact: Contact, chatModel: ChatModel) { +suspend fun directChatAction(rhId: Long?, contact: Contact, chatModel: ChatModel) { when { contact.activeConn == null && contact.profile.contactLink != null -> askCurrentOrIncognitoProfileConnectContactViaAddress(chatModel, rhId, contact, close = null, openChat = true) - else -> withBGApi { openChat(rhId, ChatInfo.Direct(contact), chatModel) } + else -> openChat(rhId, ChatInfo.Direct(contact), chatModel) } } -fun groupChatAction(rhId: Long?, groupInfo: GroupInfo, chatModel: ChatModel, inProgress: MutableState? = null) { +suspend fun groupChatAction(rhId: Long?, groupInfo: GroupInfo, chatModel: ChatModel, inProgress: MutableState? = null) { when (groupInfo.membership.memberStatus) { GroupMemberStatus.MemInvited -> acceptGroupInvitationAlertDialog(rhId, groupInfo, chatModel, inProgress) GroupMemberStatus.MemAccepted -> groupInvitationAcceptedAlert(rhId) - else -> withBGApi { openChat(rhId, ChatInfo.Group(groupInfo), chatModel) } + else -> openChat(rhId, ChatInfo.Group(groupInfo), chatModel) } } -fun noteFolderChatAction(rhId: Long?, noteFolder: NoteFolder) { - withBGApi { openChat(rhId, ChatInfo.Local(noteFolder), chatModel) } +suspend fun noteFolderChatAction(rhId: Long?, noteFolder: NoteFolder) { + openChat(rhId, ChatInfo.Local(noteFolder), chatModel) } -suspend fun openDirectChat(rhId: Long?, contactId: Long, chatModel: ChatModel) { +suspend fun openDirectChat(rhId: Long?, contactId: Long, chatModel: ChatModel) = coroutineScope { val chat = chatModel.controller.apiGetChat(rhId, ChatType.Direct, contactId) - if (chat != null) { + if (chat != null && isActive) { openLoadedChat(chat, chatModel) } } -suspend fun openGroupChat(rhId: Long?, groupId: Long, chatModel: ChatModel) { +suspend fun openGroupChat(rhId: Long?, groupId: Long, chatModel: ChatModel) = coroutineScope { val chat = chatModel.controller.apiGetChat(rhId, ChatType.Group, groupId) - if (chat != null) { + if (chat != null && isActive) { openLoadedChat(chat, chatModel) } } -suspend fun openChat(rhId: Long?, chatInfo: ChatInfo, chatModel: ChatModel) { +suspend fun openChat(rhId: Long?, chatInfo: ChatInfo, chatModel: ChatModel) = coroutineScope { val chat = chatModel.controller.apiGetChat(rhId, chatInfo.chatType, chatInfo.apiId) - if (chat != null) { + if (chat != null && isActive) { openLoadedChat(chat, chatModel) } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ShareListNavLinkView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ShareListNavLinkView.kt index 91dffeb7c8..c755ab50a0 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ShareListNavLinkView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ShareListNavLinkView.kt @@ -13,6 +13,7 @@ import chat.simplex.common.model.* import chat.simplex.common.ui.theme.* import chat.simplex.common.views.helpers.* import chat.simplex.res.MR +import kotlinx.coroutines.launch @Composable fun ShareListNavLinkView( @@ -23,6 +24,7 @@ fun ShareListNavLinkView( hasSimplexLink: Boolean ) { val stopped = chatModel.chatRunning.value == false + val scope = rememberCoroutineScope() when (chat.chatInfo) { is ChatInfo.Direct -> { val voiceProhibited = isVoice && !chat.chatInfo.featureEnabled(ChatFeature.Voice) @@ -32,7 +34,7 @@ fun ShareListNavLinkView( if (voiceProhibited) { showForwardProhibitedByPrefAlert() } else { - directChatAction(chat.remoteHostId, chat.chatInfo.contact, chatModel) + scope.launch { directChatAction(chat.remoteHostId, chat.chatInfo.contact, chatModel) } } }, stopped @@ -49,7 +51,7 @@ fun ShareListNavLinkView( if (prohibitedByPref) { showForwardProhibitedByPrefAlert() } else { - groupChatAction(chat.remoteHostId, chat.chatInfo.groupInfo, chatModel) + scope.launch { groupChatAction(chat.remoteHostId, chat.chatInfo.groupInfo, chatModel) } } }, stopped @@ -58,7 +60,7 @@ fun ShareListNavLinkView( is ChatInfo.Local -> ShareListNavLinkLayout( chatLinkPreview = { SharePreviewView(chat, disabled = false) }, - click = { noteFolderChatAction(chat.remoteHostId, chat.chatInfo.noteFolder) }, + click = { scope.launch { noteFolderChatAction(chat.remoteHostId, chat.chatInfo.noteFolder) } }, stopped ) is ChatInfo.ContactRequest, is ChatInfo.ContactConnection, is ChatInfo.InvalidJSON -> {}