diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index 423f16adaa..dbcf1c94cf 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -661,9 +661,8 @@ final class ChatModel: ObservableObject { chats[i].chatItems = switch cInfo { case .group: if let currentPreviewItem = chats[i].chatItems.first { - // For a pending invitee, surface the latest support message in the preview (its - // broker-clock itemTs isn't comparable to a locally-stamped group event), but don't - // let a no-content group event re-cover a message already shown ("reviewed by admins"). + // Pending invitee: surface the latest support message (broker vs local itemTs + // aren't comparable); don't let a no-content event re-cover an already-shown message. if cInfo.groupInfo?.membership.memberPending ?? false { (cItem.content.hasMsgContent || !currentPreviewItem.content.hasMsgContent) ? [cItem] : [currentPreviewItem] } else if cItem.meta.itemTs >= currentPreviewItem.meta.itemTs { @@ -722,8 +721,7 @@ final class ChatModel: ObservableObject { func upsertChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem) -> Bool { // update chat list var itemAdded: Bool = false - // memberPending: a pending invitee's support item may be the main-list preview, so keep it in - // sync on edit/status change too (matching addChatItem) + // memberPending: a support item may be the main-list preview, keep it synced on edit/status (like addChatItem) if cInfo.groupChatScope() == nil || cInfo.groupInfo?.membership.memberPending ?? false { if let chat = getChat(cInfo.id) { if let pItem = chat.chatItems.last { @@ -795,8 +793,7 @@ final class ChatModel: ObservableObject { func removeChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem) { // update chat list - // memberPending: a pending invitee's support item may be the main-list preview, so clear it - // when deleted/moderated too (matching addChatItem) + // memberPending: a support item may be the main-list preview, clear it on delete/moderate (like addChatItem) if cInfo.groupChatScope() == nil || cInfo.groupInfo?.membership.memberPending ?? false { if cItem.isRcvNew { unreadCollector.changeUnreadCounter(cInfo.id, by: -1, unreadMentions: cItem.meta.userMention ? -1 : 0) diff --git a/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift b/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift index 1647f8055b..57e21c227a 100644 --- a/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift @@ -351,9 +351,8 @@ struct ChatPreviewView: View { let (t, hasSecrets) = messageDraft(draft) chatPreviewLayout(t, draft: true, hasFilePreview: hasFilePreview, hasSecrets: hasSecrets) } else if cItem?.content.msgContent == nil, let previewText = chatPreviewInfoText() { - // only a no-content item (a group/connection event) shows the status text; a message — - // including caption-less media — falls through to render its content (thumbnail shown - // separately), e.g. a pending invitee's photo shows "image" instead of "reviewed by admins" + // status text only for no-content events; any message (incl. caption-less media) renders + // its content (thumbnail shown separately) - e.g. a pending invitee's photo shows "image" chatPreviewInfoTextLayout(previewText) } else if let cItem = cItem { let (t, hasSecrets) = chatItemPreview(cItem) 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 644a62e949..199fcc133b 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 @@ -520,9 +520,8 @@ object ChatModel { } suspend fun addChatItem(rhId: Long?, chatInfo: ChatInfo, cItem: ChatItem) { - // A message sent in a member-support scope ("chat with admins") is added only to that secondary - // context; received items reach both. Mirror it so the send still updates the main chat list - // preview (e.g. a pending invitee's own message) - #5909. + // A member-support send reaches only the active (secondary) context; mirror it to the primary + // so the main-list preview updates (e.g. a pending invitee's own message) - #5909. if (secondaryContextFilter is SecondaryContextFilter.GroupChatScopeContext && cItem.chatDir.sent) { chatsContext.addChatItem(rhId, chatInfo, cItem) } @@ -546,9 +545,8 @@ object ChatModel { is ChatInfo.Group -> { val currentPreviewItem = chat.chatItems.firstOrNull() if (currentPreviewItem != null) { - // For a pending invitee, surface the latest support message in the preview (its - // broker-clock itemTs isn't comparable to a locally-stamped group event), but don't - // let a no-content group event re-cover a message already shown ("reviewed by admins"). + // Pending invitee: surface the latest support message (broker vs local itemTs aren't + // comparable); don't let a no-content event re-cover an already-shown message. if (cInfo.groupInfo_?.membership?.memberPending == true) { if (cItem.content.hasMsgContent || !currentPreviewItem.content.hasMsgContent) cItem else currentPreviewItem } else if (cItem.meta.itemTs >= currentPreviewItem.meta.itemTs) { @@ -622,8 +620,7 @@ object ChatModel { suspend fun upsertChatItem(rhId: Long?, cInfo: ChatInfo, cItem: ChatItem): Boolean { var itemAdded = false // update chat list - // memberPending: a pending invitee's support item may be the main-list preview, so keep it in - // sync on edit/status change too (matching addChatItem) + // memberPending: a support item may be the main-list preview, keep it synced on edit/status (like addChatItem) if (cInfo.groupChatScope() == null || cInfo.groupInfo_?.membership?.memberPending == true) { val i = getChatIndex(rhId, cInfo.id) val chat: Chat @@ -682,8 +679,7 @@ object ChatModel { fun removeChatItem(rhId: Long?, cInfo: ChatInfo, cItem: ChatItem) { // update chat list - // memberPending: a pending invitee's support item may be the main-list preview, so clear it - // when deleted/moderated too (matching addChatItem) + // memberPending: a support item may be the main-list preview, clear it on delete/moderate (like addChatItem) if (cInfo.groupChatScope() == null || cInfo.groupInfo_?.membership?.memberPending == true) { if (cItem.isRcvNew) { decreaseCounterInPrimaryContext(rhId, cInfo.id) 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 e30a451203..552e5bc43f 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 @@ -760,8 +760,7 @@ fun ComposeView( if (updatedItem != null) { withContext(Dispatchers.Main) { chatsCtx.upsertChatItem(chat.remoteHostId, cInfo, updatedItem.chatItem) - // an edit in a member-support scope is added only to the active (secondary) context; also - // update the primary context so the main chat list preview reflects the edit (like the send path) + // a member-support edit reaches only the active context; also update primary so the preview reflects it if (chatsCtx.secondaryContextFilter is SecondaryContextFilter.GroupChatScopeContext) { chatModel.chatsContext.upsertChatItem(chat.remoteHostId, cInfo, updatedItem.chatItem) } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatPreviewView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatPreviewView.kt index 59c869926b..5c6aa2e4d3 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatPreviewView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatPreviewView.kt @@ -239,9 +239,8 @@ fun ChatPreviewView( modifier = Modifier.fillMaxWidth() ) } else if (ci?.content?.msgContent == null && previewText != null) { - // only a no-content item (a group/connection event) shows the status text; a message — - // including caption-less media — falls through to render its content (the media thumbnail is - // shown separately), e.g. a pending invitee's photo shows "image" instead of "reviewed by admins" + // status text only for no-content events; any message (incl. caption-less media) renders its + // content (thumbnail shown separately) - e.g. a pending invitee's photo shows "image", not the status Text(previewText.first, color = previewText.second) } else if (ci != null && showChatPreviews) { val (text: CharSequence, inlineTextContent) = when {