From 443f35831b95344f849e1669b8b59c64fa7f6034 Mon Sep 17 00:00:00 2001 From: Avently <7953703+avently@users.noreply.github.com> Date: Wed, 8 Jan 2025 17:01:40 +0700 Subject: [PATCH] changes --- .../simplex/common/platform/UI.android.kt | 3 + .../chat/simplex/common/model/ChatModel.kt | 33 +++++------ .../chat/simplex/common/model/SimpleXAPI.kt | 3 + .../chat/simplex/common/views/TerminalView.kt | 2 +- .../simplex/common/views/chat/ChatInfoView.kt | 6 ++ .../common/views/chat/ChatItemsMerger.kt | 6 +- .../simplex/common/views/chat/ChatView.kt | 57 ++++++++++++++----- .../simplex/common/views/chat/ComposeView.kt | 2 +- .../views/chat/group/AddGroupMembersView.kt | 4 ++ .../views/chat/group/GroupChatInfoView.kt | 4 ++ .../views/chat/group/GroupMemberInfoView.kt | 43 +++++++++++--- .../views/chat/group/GroupPreferences.kt | 3 + .../views/chat/group/GroupProfileView.kt | 4 ++ .../views/chat/group/GroupReportsView.kt | 2 + .../views/chat/group/WelcomeMessageView.kt | 4 ++ .../views/chat/item/CIChatFeatureView.kt | 5 +- .../common/views/chat/item/ChatItemView.kt | 19 ++++--- .../views/chat/item/MarkedDeletedItemView.kt | 6 +- .../views/chatlist/ChatListNavLinkView.kt | 25 ++++++-- .../common/views/chatlist/TagListView.kt | 7 +++ 20 files changed, 173 insertions(+), 65 deletions(-) diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/UI.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/UI.android.kt index 1a4d0b72e9..a1698ae28a 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/UI.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/UI.android.kt @@ -81,6 +81,9 @@ actual class GlobalExceptionsHandler: Thread.UncaughtExceptionHandler { chatModel.chatId.value = null chatItems.clearAndNotify() } + withChats { + chatItems.clearAndNotify() + } } } else { // ChatList, nothing to do. Maybe to show other view except ChatList 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 72cb1d2f47..acee4f6181 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 @@ -66,13 +66,12 @@ object ChatModel { val chatId = mutableStateOf(null) val chatsContext = ChatsContext(null) val reportsChatsContext = ChatsContext(MsgContentTag.Report) + // declaration of chatsContext should be before any other variable that is taken from ChatsContext class and used in the model, otherwise, strange crash with NullPointerException for "this" parameter in random functions val chats: State> = chatsContext.chats /** if you modify the items by adding/removing them, use helpers methods like [addAndNotify], [removeLastAndNotify], [removeAllAndNotify], [clearAndNotify] and so on. * If some helper is missing, create it. Notify is needed to track state of items that we added manually (not via api call). See [apiLoadMessages]. * If you use api call to get the items, use just [add] instead of [addAndNotify]. * Never modify underlying list directly because it produces unexpected results in ChatView's LazyColumn (setting by index is ok) */ - // declaration of chatsContext should be after any other variable that is directly attached to ChatsContext class, otherwise, strange crash with NullPointerException for "this" parameter in random functions - val chatItems: State> = chatsContext.chatItems // rhId, chatId val deletedChats = mutableStateOf>>(emptyList()) val chatItemStatuses = mutableMapOf() @@ -173,7 +172,7 @@ object ChatModel { // return true if you handled the click var centerPanelBackgroundClickHandler: (() -> Boolean)? = null - fun chatItemsForContent(contentTag: MsgContentTag?): State> = when(contentTag) { + fun chatItemsForContent(contentTag: MsgContentTag?): State> = when(contentTag) { null -> chatsContext.chatItems MsgContentTag.Report -> reportsChatsContext.chatItems else -> TODO() @@ -619,9 +618,9 @@ object ChatModel { } } - val popChatCollector = PopChatCollector() + val popChatCollector = PopChatCollector(contentTag) - class PopChatCollector { + class PopChatCollector(contentTag: MsgContentTag?) { private val subject = MutableSharedFlow() private var remoteHostId: Long? = null private val chatsToPop = mutableMapOf() @@ -631,7 +630,7 @@ object ChatModel { subject .throttleLatest(2000) .collect { - withChats { + withChats(contentTag) { chats.replaceAll(popCollectedChats()) } } @@ -671,7 +670,7 @@ object ChatModel { fun markChatItemsRead(remoteHostId: Long?, chatInfo: ChatInfo, itemIds: List? = null) { val cInfo = chatInfo - val markedRead = markItemsReadInCurrentChat(chatInfo, itemIds) + val markedRead = markItemsReadInCurrentChat(chatInfo, contentTag, itemIds) // update preview val chatIdx = getChatIndex(remoteHostId, cInfo.id) if (chatIdx >= 0) { @@ -806,7 +805,7 @@ object ChatModel { } fun removeLiveDummy() { - if (chatItems.value.lastOrNull()?.id == ChatItem.TEMP_LIVE_CHAT_ITEM_ID) { + if (chatItemsForContent(null).value.lastOrNull()?.id == ChatItem.TEMP_LIVE_CHAT_ITEM_ID) { withApi { withChats { chatItems.removeLastAndNotify() @@ -815,11 +814,11 @@ object ChatModel { } } - private fun markItemsReadInCurrentChat(chatInfo: ChatInfo, itemIds: List? = null): Int { + private fun markItemsReadInCurrentChat(chatInfo: ChatInfo, contentTag: MsgContentTag?, itemIds: List? = null): Int { val cInfo = chatInfo var markedRead = 0 if (chatId.value == cInfo.id) { - val items = chatItems.value + val items = chatItemsForContent(contentTag).value var i = items.lastIndex val itemIdsFromRange = itemIds?.toMutableSet() ?: mutableSetOf() val markedReadIds = mutableSetOf() @@ -864,19 +863,17 @@ object ChatModel { } } - fun getChatItemIndexOrNull(cItem: ChatItem): Int? { - val reversedChatItems = chatItems.asReversed() + fun getChatItemIndexOrNull(cItem: ChatItem, reversedChatItems: List): Int? { val index = reversedChatItems.indexOfFirst { it.id == cItem.id } return if (index != -1) index else null } // this function analyses "connected" events and assumes that each member will be there only once - fun getConnectedMemberNames(cItem: ChatItem): Pair> { + fun getConnectedMemberNames(cItem: ChatItem, reversedChatItems: List): Pair> { var count = 0 val ns = mutableListOf() - var idx = getChatItemIndexOrNull(cItem) + var idx = getChatItemIndexOrNull(cItem, reversedChatItems) if (cItem.mergeCategory != null && idx != null) { - val reversedChatItems = chatItems.asReversed() while (idx < reversedChatItems.size) { val ci = reversedChatItems[idx] if (ci.mergeCategory != cItem.mergeCategory) break @@ -893,9 +890,8 @@ object ChatModel { // returns the index of the first item in the same merged group (the first hidden item) // and the previous visible item with another merge category - fun getPrevShownChatItem(ciIndex: Int?, ciCategory: CIMergeCategory?): Pair { + fun getPrevShownChatItem(ciIndex: Int?, ciCategory: CIMergeCategory?, reversedChatItems: List): Pair { var i = ciIndex ?: return null to null - val reversedChatItems = chatItems.asReversed() val fst = reversedChatItems.lastIndex while (i < fst) { i++ @@ -908,8 +904,7 @@ object ChatModel { } // returns the previous member in the same merge group and the count of members in this group - fun getPrevHiddenMember(member: GroupMember, range: IntRange): Pair { - val reversedChatItems = chatItems.asReversed() + fun getPrevHiddenMember(member: GroupMember, range: IntRange, reversedChatItems: List): Pair { var prevMember: GroupMember? = null val names: MutableSet = mutableSetOf() for (i in range) { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt index 03812bbbf9..e38b958339 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt @@ -3052,6 +3052,9 @@ object ChatController { withChats { updateGroup(rh, groupInfo) } + withReportsChatsIfOpen { + updateGroup(rh, groupInfo) + } } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/TerminalView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/TerminalView.kt index 02dab5f294..20dec7a25b 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/TerminalView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/TerminalView.kt @@ -56,7 +56,7 @@ private fun sendCommand(chatModel: ChatModel, composeState: MutableState, listState: LazyListState): IntRange { +fun visibleItemIndexesNonReversed(mergedItems: State, reversedItemsSize: Int, listState: LazyListState): IntRange { val zero = 0 .. 0 if (listState.layoutInfo.totalItemsCount == 0) return zero val newest = mergedItems.value.items.getOrNull(listState.firstVisibleItemIndex)?.startIndexInReversedItems val oldest = mergedItems.value.items.getOrNull(listState.layoutInfo.visibleItemsInfo.last().index)?.lastIndexInReversed() if (newest == null || oldest == null) return zero - val size = chatModel.chatItems.value.size - val range = size - oldest .. size - newest + val range = reversedItemsSize - oldest .. reversedItemsSize - newest if (range.first < 0 || range.last < 0) return zero // visible items mapped to their underlying data structure which is chatModel.chatItems 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 29a2c1e80a..4ca9e1601d 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 @@ -77,7 +77,7 @@ fun ChatView( ModalManager.end.closeModals() } } else { - val showArchivedReports = remember { mutableStateOf(false) } + val showArchivedReports = remember { mutableStateOf(if (reportsView) reportsShowArchived else false) } val groupReports = remember { derivedStateOf { val reportsCount = if (activeChatInfo.value is ChatInfo.Group) activeChatStats.value?.reportsCount ?: 0 else 0 GroupReports(reportsCount, reportsView, showArchivedReports.value) } @@ -125,7 +125,10 @@ fun ChatView( } } val clipboard = LocalClipboardManager.current - CompositionLocalProvider(LocalAppBarHandler provides rememberAppBarHandler(chatInfo.id, keyboardCoversBar = false)) { + CompositionLocalProvider( + LocalAppBarHandler provides rememberAppBarHandler(chatInfo.id, keyboardCoversBar = false), + LocalContentTag provides groupReports.value.contentTag + ) { when (chatInfo) { is ChatInfo.Direct, is ChatInfo.Group, is ChatInfo.Local -> { var groupMembersJob: Job = remember { Job() } @@ -511,6 +514,11 @@ fun ChatView( withChats { updateChatItem(cInfo, updatedCI) } + withReportsChatsIfOpen { + if (cItem.content.msgContent is MsgContent.MCReport) { + updateChatItem(cInfo, updatedCI) + } + } } } }, @@ -528,7 +536,9 @@ fun ChatView( groupMembersJob.cancel() groupMembersJob = scope.launch(Dispatchers.Default) { var initialCiInfo = loadChatItemInfo() ?: return@launch - ModalManager.end.closeModals() + if (!ModalManager.end.hasModalOpen(ModalViewId.GROUP_REPORTS)) { + ModalManager.end.closeModals() + } ModalManager.end.showModalCloseable(endButtons = { ShareButton { clipboard.shareText(itemInfoShareText(chatModel, cItem, initialCiInfo, chatModel.controller.appPrefs.developerTools.get())) @@ -1078,6 +1088,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) } +/** Saves current scroll position when [GroupReports] are open and user opens [ChatItemInfoView], for example, and goes back */ +private var reportsListState: LazyListState? = null +private var reportsShowArchived: Boolean = false + @Composable fun BoxScope.ChatItemsList( remoteHostId: Long?, @@ -1130,12 +1144,18 @@ fun BoxScope.ChatItemsList( ) val listState = rememberUpdatedState(rememberSaveable(chatInfo.id, searchValueIsEmpty.value, saver = LazyListState.Saver) { val index = mergedItems.value.items.indexOfLast { it.hasUnread() } - if (index <= 0) { + val reportsState = reportsListState + if (reportsState != null) { + reportsListState = null + reportsShowArchived = false + reportsState + } else if (index <= 0) { LazyListState(0, 0) } else { LazyListState(index + 1, -maxHeightForList.value) } }) + SaveReportsStateOnDispose(groupReports, listState) val maxHeight = remember { derivedStateOf { listState.value.layoutInfo.viewportEndOffset - topPaddingToContentPx.value } } val loadingMoreItems = remember { mutableStateOf(false) } val animatedScrollingInProgress = remember { mutableStateOf(false) } @@ -1146,7 +1166,7 @@ fun BoxScope.ChatItemsList( try { loadingMoreItems.value = true loadMessages(chatId, pagination) { - visibleItemIndexesNonReversed(mergedItems, listState.value) + visibleItemIndexesNonReversed(mergedItems, reversedChatItems.value.size, listState.value) } } finally { loadingMoreItems.value = false @@ -1304,7 +1324,7 @@ fun BoxScope.ChatItemsList( val rangeValue = range.value val (prevMember, memCount) = if (rangeValue != null) { - chatModel.getPrevHiddenMember(member, rangeValue) + chatModel.getPrevHiddenMember(member, rangeValue, reversedChatItems.value) } else { null to 1 } @@ -1702,7 +1722,7 @@ private fun PreloadItemsBefore( var lastIndexToLoadFrom: Int? = findLastIndexToLoadFromInSplits(firstVisibleIndex, lastVisibleIndex, remaining, splits) val items = reversedChatItems.value if (splits.isEmpty() && items.isNotEmpty() && lastVisibleIndex > mergedItems.value.items.size - remaining && items.size >= ChatPagination.INITIAL_COUNT) { - lastIndexToLoadFrom = items.lastIndex + lastIndexToLoadFrom = 0 } if (allowLoad.value && lastIndexToLoadFrom != null) { items.getOrNull(items.lastIndex - lastIndexToLoadFrom)?.id @@ -1910,6 +1930,16 @@ private fun FloatingDate( } } +@Composable +private fun SaveReportsStateOnDispose(groupReports: State, listState: State) { + DisposableEffect(Unit) { + onDispose { + reportsListState = if (groupReports.value.reportsView && ModalManager.end.hasModalOpen(ModalViewId.GROUP_REPORTS)) listState.value else null + reportsShowArchived = if (groupReports.value.reportsView && ModalManager.end.hasModalOpen(ModalViewId.GROUP_REPORTS)) groupReports.value.showArchived else false + } + } +} + @Composable private fun DownloadFilesButton( forwardConfirmation: ForwardConfirmation.FilesNotAccepted, @@ -2051,7 +2081,7 @@ private fun scrollToItem( val oldSize = reversedChatItems.value.size withContext(Dispatchers.Default) { loadMessages(chatInfo.value.id, pagination) { - visibleItemIndexesNonReversed(mergedItems, listState.value) + visibleItemIndexesNonReversed(mergedItems, reversedChatItems.value.size, listState.value) } } var repeatsLeft = 50 @@ -2199,10 +2229,10 @@ private fun selectUnselectChatItem( ) { val itemIds = mutableSetOf() if (!revealed.value) { - val currIndex = chatModel.getChatItemIndexOrNull(ci) + val currIndex = chatModel.getChatItemIndexOrNull(ci, reversedChatItems.value) val ciCategory = ci.mergeCategory if (currIndex != null && ciCategory != null) { - val (prevHidden, _) = chatModel.getPrevShownChatItem(currIndex, ciCategory) + val (prevHidden, _) = chatModel.getPrevShownChatItem(currIndex, ciCategory, reversedChatItems.value) val range = chatViewItemsRange(currIndex, prevHidden) if (range != null) { val reversed = reversedChatItems.value @@ -2251,7 +2281,9 @@ private fun deleteMessages(chatRh: Long?, chatInfo: ChatInfo, itemIds: List, chatInfo: ChatInfo) { chatModel.chatId.value = null chatModel.sharedContent.value = SharedContent.Forward( - chatModel.chatItems.value.filter { chatItemsIds.contains(it.id) }, + chatModel.chatItemsForContent(null).value.filter { chatItemsIds.contains(it.id) }, chatInfo ) } 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 2247a615df..12a8015816 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 @@ -798,7 +798,7 @@ fun ComposeView( fun editPrevMessage() { if (composeState.value.contextItem != ComposeContextItem.NoContextItem || composeState.value.preview != ComposePreview.NoPreview) return - val lastEditable = chatModel.chatItems.value.findLast { it.meta.editable } + val lastEditable = chatModel.chatItemsForContent(null).value.findLast { it.meta.editable } if (lastEditable != null) { composeState.value = ComposeState(editingItem = lastEditable, useLinkPreviews = useLinkPreviews) } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/AddGroupMembersView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/AddGroupMembersView.kt index 6072abfc36..20295a6e70 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/AddGroupMembersView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/AddGroupMembersView.kt @@ -26,6 +26,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import chat.simplex.common.model.* import chat.simplex.common.model.ChatModel.withChats +import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen import chat.simplex.common.ui.theme.* import chat.simplex.common.views.chat.ChatInfoToolbarTitle import chat.simplex.common.views.helpers.* @@ -64,6 +65,9 @@ fun AddGroupMembersView(rhId: Long?, groupInfo: GroupInfo, creatingGroup: Boolea withChats { upsertGroupMember(rhId, groupInfo, member) } + withReportsChatsIfOpen { + upsertGroupMember(rhId, groupInfo, member) + } } else { break } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt index 9852c7f7d7..0689c382f3 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt @@ -30,6 +30,7 @@ import androidx.compose.ui.unit.* import chat.simplex.common.model.* import chat.simplex.common.model.ChatController.appPrefs import chat.simplex.common.model.ChatModel.withChats +import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen import chat.simplex.common.ui.theme.* import chat.simplex.common.views.helpers.* import chat.simplex.common.views.usersettings.* @@ -198,6 +199,9 @@ private fun removeMemberAlert(rhId: Long?, groupInfo: GroupInfo, mem: GroupMembe withChats { upsertGroupMember(rhId, groupInfo, updatedMember) } + withReportsChatsIfOpen { + upsertGroupMember(rhId, groupInfo, updatedMember) + } } } }, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt index 8fe72d761a..ef1c69a5bb 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt @@ -28,6 +28,7 @@ import androidx.compose.ui.unit.* import chat.simplex.common.model.* import chat.simplex.common.model.ChatModel.controller import chat.simplex.common.model.ChatModel.withChats +import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen import chat.simplex.common.ui.theme.* import chat.simplex.common.views.chat.* import chat.simplex.common.views.helpers.* @@ -65,6 +66,9 @@ fun GroupMemberInfoView( withChats { updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second) } + withReportsChatsIfOpen { + updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second) + } close.invoke() } } @@ -142,6 +146,9 @@ fun GroupMemberInfoView( withChats { upsertGroupMember(rhId, groupInfo, mem) } + withReportsChatsIfOpen { + upsertGroupMember(rhId, groupInfo, mem) + } }.onFailure { newRole.value = prevValue } @@ -157,6 +164,9 @@ fun GroupMemberInfoView( withChats { updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second) } + withReportsChatsIfOpen { + updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second) + } close.invoke() } } @@ -171,6 +181,9 @@ fun GroupMemberInfoView( withChats { updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second) } + withReportsChatsIfOpen { + updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second) + } close.invoke() } } @@ -188,6 +201,9 @@ fun GroupMemberInfoView( withChats { updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second) } + withReportsChatsIfOpen { + updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second) + } close.invoke() } } @@ -203,16 +219,16 @@ fun GroupMemberInfoView( verify = { code -> chatModel.controller.apiVerifyGroupMember(rhId, mem.groupId, mem.groupMemberId, code)?.let { r -> val (verified, existingCode) = r - withChats { - upsertGroupMember( - rhId, - groupInfo, - mem.copy( - activeConn = mem.activeConn?.copy( - connectionCode = if (verified) SecurityCode(existingCode, Clock.System.now()) else null - ) - ) + val copy = mem.copy( + activeConn = mem.activeConn?.copy( + connectionCode = if (verified) SecurityCode(existingCode, Clock.System.now()) else null ) + ) + withChats { + upsertGroupMember(rhId, groupInfo, copy) + } + withReportsChatsIfOpen { + upsertGroupMember(rhId, groupInfo, copy) } r } @@ -246,6 +262,9 @@ fun removeMemberDialog(rhId: Long?, groupInfo: GroupInfo, member: GroupMember, c withChats { upsertGroupMember(rhId, groupInfo, removedMember) } + withReportsChatsIfOpen { + upsertGroupMember(rhId, groupInfo, removedMember) + } } close?.invoke() } @@ -753,6 +772,9 @@ fun updateMemberSettings(rhId: Long?, gInfo: GroupInfo, member: GroupMember, mem withChats { upsertGroupMember(rhId, gInfo, member.copy(memberSettings = memberSettings)) } + withReportsChatsIfOpen { + upsertGroupMember(rhId, gInfo, member.copy(memberSettings = memberSettings)) + } } } } @@ -786,6 +808,9 @@ fun blockMemberForAll(rhId: Long?, gInfo: GroupInfo, member: GroupMember, blocke withChats { upsertGroupMember(rhId, gInfo, updatedMember) } + withReportsChatsIfOpen { + upsertGroupMember(rhId, gInfo, updatedMember) + } } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupPreferences.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupPreferences.kt index 0a807e1d63..3d9f42f929 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupPreferences.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupPreferences.kt @@ -45,6 +45,9 @@ fun GroupPreferencesView(m: ChatModel, rhId: Long?, chatId: String, close: () -> updateGroup(rhId, g) currentPreferences = preferences } + withChats { + updateGroup(rhId, g) + } } afterSave() } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupProfileView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupProfileView.kt index e81722f3f0..1084f897eb 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupProfileView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupProfileView.kt @@ -18,6 +18,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import chat.simplex.common.model.* import chat.simplex.common.model.ChatModel.withChats +import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen import chat.simplex.common.platform.* import chat.simplex.common.ui.theme.* import chat.simplex.common.views.* @@ -42,6 +43,9 @@ fun GroupProfileView(rhId: Long?, groupInfo: GroupInfo, chatModel: ChatModel, cl withChats { updateGroup(rhId, gInfo) } + withReportsChatsIfOpen { + updateGroup(rhId, gInfo) + } close.invoke() } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupReportsView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupReportsView.kt index eb5d5815c9..28b0e7c979 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupReportsView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupReportsView.kt @@ -17,6 +17,8 @@ import dev.icerock.moko.resources.compose.stringResource import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch +val LocalContentTag: ProvidableCompositionLocal = staticCompositionLocalOf { null } + data class GroupReports( val reportsCount: Int, val reportsView: Boolean, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/WelcomeMessageView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/WelcomeMessageView.kt index 6ebd4b13c3..2e12b77c49 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/WelcomeMessageView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/WelcomeMessageView.kt @@ -27,6 +27,7 @@ import chat.simplex.common.views.chat.item.MarkdownText import chat.simplex.common.views.helpers.* import chat.simplex.common.model.ChatModel import chat.simplex.common.model.ChatModel.withChats +import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen import chat.simplex.common.model.GroupInfo import chat.simplex.common.platform.ColumnWithScrollBar import chat.simplex.common.platform.chatJsonLength @@ -53,6 +54,9 @@ fun GroupWelcomeView(m: ChatModel, rhId: Long?, groupInfo: GroupInfo, close: () withChats { updateGroup(rhId, res) } + withReportsChatsIfOpen { + updateGroup(rhId, res) + } welcomeText.value = welcome ?: "" } afterSave() diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIChatFeatureView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIChatFeatureView.kt index 9bb3cef1d7..7711ee73af 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIChatFeatureView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIChatFeatureView.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.unit.sp import chat.simplex.common.model.* import chat.simplex.common.model.ChatModel.getChatItemIndexOrNull import chat.simplex.common.platform.onRightClick +import chat.simplex.common.views.chat.group.LocalContentTag @Composable fun CIChatFeatureView( @@ -75,9 +76,9 @@ private fun mergedFeatures(chatItem: ChatItem, chatInfo: ChatInfo): List = arrayListOf() val icons: MutableSet = mutableSetOf() - var i = getChatItemIndexOrNull(chatItem) + val reversedChatItems = m.chatItemsForContent(LocalContentTag.current).value.asReversed() + var i = getChatItemIndexOrNull(chatItem, reversedChatItems) if (i != null) { - val reversedChatItems = m.chatItems.asReversed() while (i < reversedChatItems.size) { val f = featureInfo(reversedChatItems[i], chatInfo) ?: break if (!icons.contains(f.icon)) { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.kt index 647c74da06..016e9e427b 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.kt @@ -28,6 +28,7 @@ import chat.simplex.common.model.ChatModel.currentUser import chat.simplex.common.platform.* import chat.simplex.common.ui.theme.* import chat.simplex.common.views.chat.* +import chat.simplex.common.views.chat.group.LocalContentTag import chat.simplex.common.views.helpers.* import chat.simplex.res.MR import kotlinx.datetime.Clock @@ -500,8 +501,8 @@ fun ChatItemView( DeleteItemMenu() } - fun mergedGroupEventText(chatItem: ChatItem): String? { - val (count, ns) = chatModel.getConnectedMemberNames(chatItem) + fun mergedGroupEventText(chatItem: ChatItem, reversedChatItems: List): String? { + val (count, ns) = chatModel.getConnectedMemberNames(chatItem, reversedChatItems) val members = when { ns.size == 1 -> String.format(generalGetString(MR.strings.rcv_group_event_1_member_connected), ns[0]) ns.size == 2 -> String.format(generalGetString(MR.strings.rcv_group_event_2_members_connected), ns[0], ns[1]) @@ -520,9 +521,9 @@ fun ChatItemView( } } - fun eventItemViewText(): AnnotatedString { + fun eventItemViewText(reversedChatItems: List): AnnotatedString { val memberDisplayName = cItem.memberDisplayName - val t = mergedGroupEventText(cItem) + val t = mergedGroupEventText(cItem, reversedChatItems) return if (!revealed.value && t != null) { chatEventText(t, cItem.timestampText) } else if (memberDisplayName != null) { @@ -536,7 +537,8 @@ fun ChatItemView( } @Composable fun EventItemView() { - CIEventView(eventItemViewText()) + val reversedChatItems = chatModel.chatItemsForContent(LocalContentTag.current).value.asReversed() + CIEventView(eventItemViewText(reversedChatItems)) } @Composable @@ -729,20 +731,21 @@ fun DeleteItemAction( deleteMessage: (Long, CIDeleteMode) -> Unit, deleteMessages: (List) -> Unit, ) { + val contentTag = LocalContentTag.current ItemAction( stringResource(MR.strings.delete_verb), painterResource(MR.images.ic_delete), onClick = { showMenu.value = false if (!revealed.value) { - val currIndex = chatModel.getChatItemIndexOrNull(cItem) + val reversedChatItems = chatModel.chatItemsForContent(contentTag).value.asReversed() + val currIndex = chatModel.getChatItemIndexOrNull(cItem, reversedChatItems) val ciCategory = cItem.mergeCategory if (currIndex != null && ciCategory != null) { - val (prevHidden, _) = chatModel.getPrevShownChatItem(currIndex, ciCategory) + val (prevHidden, _) = chatModel.getPrevShownChatItem(currIndex, ciCategory, reversedChatItems) val range = chatViewItemsRange(currIndex, prevHidden) if (range != null) { val itemIds: ArrayList = arrayListOf() - val reversedChatItems = chatModel.chatItems.asReversed() for (i in range) { itemIds.add(reversedChatItems[i].id) } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/MarkedDeletedItemView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/MarkedDeletedItemView.kt index d2e19a37d6..7b1b523a77 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/MarkedDeletedItemView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/MarkedDeletedItemView.kt @@ -12,8 +12,10 @@ import androidx.compose.runtime.* import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import chat.simplex.common.model.* +import chat.simplex.common.model.ChatController.chatModel import chat.simplex.common.model.ChatModel.getChatItemIndexOrNull import chat.simplex.common.ui.theme.* +import chat.simplex.common.views.chat.group.LocalContentTag import chat.simplex.common.views.helpers.generalGetString import chat.simplex.res.MR import dev.icerock.moko.resources.compose.stringResource @@ -42,10 +44,10 @@ fun MarkedDeletedItemView(ci: ChatItem, timedMessagesTTL: Int?, revealed: State< @Composable private fun MergedMarkedDeletedText(chatItem: ChatItem, revealed: State) { - var i = getChatItemIndexOrNull(chatItem) + val reversedChatItems = chatModel.chatItemsForContent(LocalContentTag.current).value.asReversed() + var i = getChatItemIndexOrNull(chatItem, reversedChatItems) val ciCategory = chatItem.mergeCategory val text = if (!revealed.value && ciCategory != null && i != null) { - val reversedChatItems = ChatModel.chatItems.asReversed() var moderated = 0 var blocked = 0 var blockedByAdmin = 0 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 f825646849..7994cdc08b 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 @@ -22,6 +22,7 @@ import chat.simplex.common.model.* import chat.simplex.common.model.ChatModel.markChatTagRead import chat.simplex.common.model.ChatModel.updateChatTagRead import chat.simplex.common.model.ChatModel.withChats +import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen import chat.simplex.common.platform.* import chat.simplex.common.ui.theme.* import chat.simplex.common.views.chat.* @@ -252,7 +253,7 @@ suspend fun setGroupMembers(rhId: Long?, groupInfo: GroupInfo, chatModel: ChatMo fun ContactMenuItems(chat: Chat, contact: Contact, chatModel: ChatModel, showMenu: MutableState, showMarkRead: Boolean) { if (contact.activeConn != null) { if (showMarkRead) { - MarkReadChatAction(chat, chatModel, showMenu) + MarkReadChatAction(chat, showMenu) } else { MarkUnreadChatAction(chat, chatModel, showMenu) } @@ -292,7 +293,7 @@ fun GroupMenuItems( } else -> { if (showMarkRead) { - MarkReadChatAction(chat, chatModel, showMenu) + MarkReadChatAction(chat, showMenu) } else { MarkUnreadChatAction(chat, chatModel, showMenu) } @@ -313,7 +314,7 @@ fun GroupMenuItems( @Composable fun NoteFolderMenuItems(chat: Chat, showMenu: MutableState, showMarkRead: Boolean) { if (showMarkRead) { - MarkReadChatAction(chat, chatModel, showMenu) + MarkReadChatAction(chat, showMenu) } else { MarkUnreadChatAction(chat, chatModel, showMenu) } @@ -321,12 +322,12 @@ fun NoteFolderMenuItems(chat: Chat, showMenu: MutableState, showMarkRea } @Composable -fun MarkReadChatAction(chat: Chat, chatModel: ChatModel, showMenu: MutableState) { +fun MarkReadChatAction(chat: Chat, showMenu: MutableState) { ItemAction( stringResource(MR.strings.mark_read), painterResource(MR.images.ic_check), onClick = { - markChatRead(chat, chatModel) + markChatRead(chat) ntfManager.cancelNotificationsForChat(chat.id) showMenu.value = false } @@ -563,13 +564,16 @@ private fun InvalidDataView() { } } -fun markChatRead(c: Chat, chatModel: ChatModel) { +fun markChatRead(c: Chat) { var chat = c withApi { if (chat.chatStats.unreadCount > 0) { withChats { markChatItemsRead(chat.remoteHostId, chat.chatInfo) } + withReportsChatsIfOpen { + markChatItemsRead(chat.remoteHostId, chat.chatInfo) + } chatModel.controller.apiChatRead( chat.remoteHostId, chat.chatInfo.chatType, @@ -589,6 +593,9 @@ fun markChatRead(c: Chat, chatModel: ChatModel) { replaceChat(chat.remoteHostId, chat.id, chat.copy(chatStats = chat.chatStats.copy(unreadChat = false))) markChatTagRead(chat) } + withReportsChatsIfOpen { + replaceChat(chat.remoteHostId, chat.id, chat.copy(chatStats = chat.chatStats.copy(unreadChat = false))) + } } } } @@ -611,6 +618,9 @@ fun markChatUnread(chat: Chat, chatModel: ChatModel) { replaceChat(chat.remoteHostId, chat.id, chat.copy(chatStats = chat.chatStats.copy(unreadChat = true))) updateChatTagRead(chat, wasUnread) } + withReportsChatsIfOpen { + replaceChat(chat.remoteHostId, chat.id, chat.copy(chatStats = chat.chatStats.copy(unreadChat = true))) + } } } } @@ -866,6 +876,9 @@ fun updateChatSettings(remoteHostId: Long?, chatInfo: ChatInfo, chatSettings: Ch withChats { updateChatInfo(remoteHostId, newChatInfo) } + withReportsChatsIfOpen { + updateChatInfo(remoteHostId, newChatInfo) + } if (chatSettings.enableNtfs != MsgFilter.All) { ntfManager.cancelNotificationsForChat(chatInfo.id) } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/TagListView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/TagListView.kt index 7bbf4f4aa5..9f73b05625 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/TagListView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/TagListView.kt @@ -32,6 +32,7 @@ import chat.simplex.common.model.ChatController.apiDeleteChatTag import chat.simplex.common.model.ChatController.apiSetChatTags import chat.simplex.common.model.ChatController.appPrefs import chat.simplex.common.model.ChatModel.withChats +import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen import chat.simplex.common.platform.* import chat.simplex.common.ui.theme.* import chat.simplex.common.views.chat.item.ItemAction @@ -425,6 +426,9 @@ private fun setTag(rhId: Long?, tagId: Long?, chat: Chat, close: () -> Unit) { withChats { updateGroup(rhId, group) } + withReportsChatsIfOpen { + updateGroup(rhId, group) + } } else -> {} @@ -462,6 +466,9 @@ private fun deleteTag(rhId: Long?, tag: ChatTag, saving: MutableState) withChats { updateGroup(rhId, group) } + withReportsChatsIfOpen { + updateGroup(rhId, group) + } } else -> {} }