diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index b532246f48..11d499f63d 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -363,6 +363,13 @@ object ChatModel { } } + suspend fun addSentChatItem(activeCtx: ChatsContext, rhId: Long?, cInfo: ChatInfo, cItem: ChatItem) { + activeCtx.addChatItem(rhId, cInfo, cItem) + if (activeCtx.secondaryContextFilter != null && cInfo.inMainChatList) { + chatsContext.addChatItem(rhId, cInfo, cItem) + } + } + // Spec: spec/state.md#ChatsContext class ChatsContext(val secondaryContextFilter: SecondaryContextFilter?) { val chats = mutableStateOf(SnapshotStateList()) @@ -1821,6 +1828,9 @@ sealed class ChatInfo: SomeChat, NamedChat { val isChannel: Boolean get() = groupInfo_?.useRelays == true + + val inMainChatList: Boolean + get() = groupChatScope() == null || groupInfo_?.membership?.memberPending == true } @Serializable diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt index 6bfbad52ef..0f8ec06979 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt @@ -583,7 +583,7 @@ fun ComposeView( if (!chatItems.isNullOrEmpty()) { chatItems.forEach { aChatItem -> withContext(Dispatchers.Main) { - chatsCtx.addChatItem(chat.remoteHostId, aChatItem.chatInfo, aChatItem.chatItem) + chatModel.addSentChatItem(chatsCtx, chat.remoteHostId, aChatItem.chatInfo, aChatItem.chatItem) } } return chatItems.first().chatItem @@ -725,7 +725,7 @@ fun ComposeView( withContext(Dispatchers.Main) { chatItems?.forEach { chatItem -> - chatsCtx.addChatItem(rhId, chat.chatInfo, chatItem) + chatModel.addSentChatItem(chatsCtx, rhId, chat.chatInfo, chatItem) } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/FramedItemView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/FramedItemView.kt index cbd15aca67..50d9732cdc 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/FramedItemView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/FramedItemView.kt @@ -442,7 +442,7 @@ fun sendCommandMsg(chatsCtx: ChatModel.ChatsContext, chat: Chat, msg: String) { if (!chatItems.isNullOrEmpty()) { chatItems.forEach { aChatItem -> withContext(Dispatchers.Main) { - chatsCtx.addChatItem(chat.remoteHostId, aChatItem.chatInfo, aChatItem.chatItem) + chatModel.addSentChatItem(chatsCtx, chat.remoteHostId, aChatItem.chatInfo, aChatItem.chatItem) } } } diff --git a/plans/chat-list-wrong-chat-preview-fix.md b/plans/chat-list-wrong-chat-preview-fix.md index 2f438cc3b3..b703b46830 100644 --- a/plans/chat-list-wrong-chat-preview-fix.md +++ b/plans/chat-list-wrong-chat-preview-fix.md @@ -70,6 +70,44 @@ This is a byte-for-byte revert of the unintended part of `b97e1e0f1`, with the feature's guard preserved. `chats[i]` get/set is the established idiom used at nine other sites in the same class. +## The revert alone would break #5909 + +A pending invitee's own **sent** support message reaches only the **active** context — +`ComposeView` send/forward and `FramedItemView` command-send all call +`chatsCtx.addChatItem`, and in a member-support chat `chatsCtx` is the *secondary* +context. Before this change that call wrote `chatsContext.chats[i]`, i.e. the primary +list, so the group's main-list preview did update — but only as a side effect of the +bug, at whatever index happened to line up. + +Reverting the four sites therefore removes it: the secondary updates its own list and +the primary is never touched, so the invitee's sent support messages stop appearing as +the group's preview. Confirmed by testing during review. + +So the revert is paired with an explicit dispatch, which does the same thing on purpose +and at the right index: + +```kotlin +suspend fun addSentChatItem(activeCtx: ChatsContext, rhId: Long?, cInfo: ChatInfo, cItem: ChatItem) { + activeCtx.addChatItem(rhId, cInfo, cItem) + if (activeCtx.secondaryContextFilter != null && cInfo.inMainChatList) { + chatsContext.addChatItem(rhId, cInfo, cItem) + } +} +``` + +`ChatInfo.inMainChatList` (`groupChatScope() == null || membership.memberPending`) is +the same rule `addChatItem` already applies to decide whether an item updates the main +list preview, now named once instead of spelled out. + +Cross-context dispatch stays with the **caller**, matching how `processReceivedMsg` +hands received items to both contexts and how `ChatView` handles deletes. No +`ChatsContext` method reaches into the other context — which is the invariant this +whole fix restores. + +Passing the scoped `cInfo` to the primary is safe: `updateChatInfo` strips the scope +before storing it, and `chatItemBelongsToScope` returns false in the primary context +for a scoped `cInfo`, so the item never enters the primary item list. + ## Why it is safe - Behaviour for the **primary** context is unchanged: `chats === chatsContext.chats`