diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index ae8a6f445e..52500595ca 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -665,7 +665,7 @@ final class ChatModel: ObservableObject { im.reversedChatItems.first?.isLiveDummy == true } - func markChatItemsRead(_ cInfo: ChatInfo) { + func markAllChatItemsRead(_ cInfo: ChatInfo) { // update preview _updateChat(cInfo.id) { chat in self.decreaseUnreadCounter(user: self.currentUser!, chat: chat) @@ -674,54 +674,14 @@ final class ChatModel: ObservableObject { } // update current chat if chatId == cInfo.id { - markCurrentChatRead() - } - } - - private func markCurrentChatRead(fromIndex i: Int = 0) { - var j = i - while j < im.reversedChatItems.count { - markChatItemRead_(j) - j += 1 - } - } - - func markChatItemsRead(_ cInfo: ChatInfo, aboveItem: ChatItem? = nil) { - if let cItem = aboveItem { - if chatId == cInfo.id, let i = getChatItemIndex(cItem) { - markCurrentChatRead(fromIndex: i) - _updateChat(cInfo.id) { chat in - var unreadBelow = 0 - var unreadMentionsBelow = 0 - var j = i - 1 - while j >= 0 { - let meta = self.im.reversedChatItems[j].meta - if case .rcvNew = meta.itemStatus { - unreadBelow += 1 - if meta.userMention { - unreadMentionsBelow += 1 - } - } - j -= 1 - } - // update preview - let markedCount = chat.chatStats.unreadCount - unreadBelow - let markedMentionsCount = chat.chatStats.unreadMentions - unreadMentionsBelow - if markedCount > 0 || markedMentionsCount > 0 { - let wasUnread = chat.unreadTag - chat.chatStats.unreadCount -= markedCount - chat.chatStats.unreadMentions -= markedMentionsCount - ChatTagsModel.shared.updateChatTagRead(chat, wasUnread: wasUnread) - let by = chat.chatInfo.chatSettings?.enableNtfs == .mentions ? markedMentionsCount : markedCount - self.decreaseUnreadCounter(user: self.currentUser!, by: by) - } - } + var i = 0 + while i < im.reversedChatItems.count { + markChatItemRead_(i) + i += 1 } - } else { - markChatItemsRead(cInfo) + im.chatItemsChangesListener.read(nil, im.reversedChatItems.reversed()) } } - func markChatUnread(_ cInfo: ChatInfo, unreadChat: Bool = true) { _updateChat(cInfo.id) { chat in let wasUnread = chat.unreadTag @@ -750,13 +710,16 @@ final class ChatModel: ObservableObject { func markChatItemsRead(_ cInfo: ChatInfo, _ itemIds: [ChatItem.ID], _ mentionsRead: Int) { if self.chatId == cInfo.id { var unreadItemIds: Set = [] - for itemId in itemIds { - if let i = im.reversedChatItems.firstIndex(where: { $0.id == itemId }) { - if im.reversedChatItems[i].isRcvNew { - unreadItemIds.insert(itemId) - } + var i = 0 + var ids = Set(itemIds) + while i < im.reversedChatItems.count && !ids.isEmpty { + let item = im.reversedChatItems[i] + if ids.contains(item.id) && item.isRcvNew { markChatItemRead_(i) + unreadItemIds.insert(item.id) + ids.remove(item.id) } + i += 1 } im.chatItemsChangesListener.read(unreadItemIds, im.reversedChatItems.reversed()) } diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 056340dcaf..51d2a8444d 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -1513,7 +1513,7 @@ func markChatRead(_ chat: Chat) async { let cInfo = chat.chatInfo try await apiChatRead(type: cInfo.chatType, id: cInfo.apiId) await MainActor.run { - withAnimation { ChatModel.shared.markChatItemsRead(cInfo) } + withAnimation { ChatModel.shared.markAllChatItemsRead(cInfo) } } } if chat.chatStats.unreadChat { diff --git a/apps/ios/Shared/Views/Chat/ChatItemsMerger.swift b/apps/ios/Shared/Views/Chat/ChatItemsMerger.swift index fac359076f..2e9dac166a 100644 --- a/apps/ios/Shared/Views/Chat/ChatItemsMerger.swift +++ b/apps/ios/Shared/Views/Chat/ChatItemsMerger.swift @@ -23,11 +23,13 @@ struct MergedItems: Hashable, Equatable { hasher.combine("\(items.hashValue)") } - static func create(_ items: [ChatItem], _ unreadCount: Int, _ revealedItems: Set, _ chatState: ActiveChatState) -> MergedItems { + static func create(_ items: [ChatItem], _ revealedItems: Set, _ chatState: ActiveChatState) -> MergedItems { if items.isEmpty { return MergedItems(items: [], splits: [], indexInParentItems: [:]) } + let unreadCount = chatState.unreadTotal + let unreadAfterItemId = chatState.unreadAfterItemId let itemSplits = chatState.splits var mergedItems: [MergedItem] = [] diff --git a/apps/ios/Shared/Views/Chat/ChatView.swift b/apps/ios/Shared/Views/Chat/ChatView.swift index a621df3b45..396493d6c5 100644 --- a/apps/ios/Shared/Views/Chat/ChatView.swift +++ b/apps/ios/Shared/Views/Chat/ChatView.swift @@ -515,7 +515,7 @@ struct ChatView: View { } .onAppear { Task { - mergedItems.boxedValue = MergedItems.create(im.reversedChatItems, chat.chatStats.unreadCount, revealedItems, ItemsModel.shared.chatState) + mergedItems.boxedValue = MergedItems.create(im.reversedChatItems, revealedItems, im.chatState) let unreadIndex = mergedItems.boxedValue.items.lastIndex(where: { $0.hasUnread() }) let unreadItemId: Int64? = if let unreadIndex { mergedItems.boxedValue.items[unreadIndex].newest().item.id } else { nil } await MainActor.run { @@ -539,8 +539,7 @@ struct ChatView: View { updateMergedItemsTask?.cancel() if useItemsUpdateTask { updateMergedItemsTask = Task { - let start = Date.now.timeIntervalSince1970 - let items = MergedItems.create(items, chat.chatStats.unreadCount, revealedItems, ItemsModel.shared.chatState) + let items = MergedItems.create(items, revealedItems, im.chatState) if Task.isCancelled { return } @@ -550,18 +549,13 @@ struct ChatView: View { } } } else { - mergedItems.boxedValue = MergedItems.create(items, chat.chatStats.unreadCount, revealedItems, ItemsModel.shared.chatState) + mergedItems.boxedValue = MergedItems.create(items, revealedItems, im.chatState) scrollView.updateItems(mergedItems.boxedValue.items) } } .onChange(of: revealedItems) { revealed in updateMergedItemsTask?.cancel() - mergedItems.boxedValue = MergedItems.create(im.reversedChatItems, chat.chatStats.unreadCount, revealed, ItemsModel.shared.chatState) - scrollView.updateItems(mergedItems.boxedValue.items) - } - .onChange(of: chat.chatStats.unreadCount) { unreadCount in - updateMergedItemsTask?.cancel() - mergedItems.boxedValue = MergedItems.create(im.reversedChatItems, unreadCount, revealedItems, ItemsModel.shared.chatState) + mergedItems.boxedValue = MergedItems.create(im.reversedChatItems, revealed, im.chatState) scrollView.updateItems(mergedItems.boxedValue.items) } .onChange(of: chat.id) { _ in @@ -614,6 +608,7 @@ struct ChatView: View { } class FloatingButtonModel: ObservableObject { + @Published var unreadAbove: Int = 0 @Published var unreadBelow: Int = 0 @Published var isNearBottom: Bool = true @Published var date: Date? = nil @@ -627,6 +622,7 @@ struct ChatView: View { } else { 0 } + let unreadAbove = ItemsModel.shared.chatState.unreadTotal - unreadBelow let date: Date? = if let lastVisible = listState.visibleItems.last { Calendar.current.startOfDay(for: lastVisible.item.oldest().item.meta.itemTs) @@ -638,6 +634,7 @@ struct ChatView: View { DispatchQueue.main.async { [weak self] in guard let it = self else { return } it.setDate(visibility: true) + it.unreadAbove = unreadAbove it.unreadBelow = unreadBelow it.date = date } @@ -701,13 +698,12 @@ struct ChatView: View { .padding(.vertical, 4) } VStack { - let unreadAbove = ItemsModel.shared.chatState.unreadTotal - model.unreadBelow - if unreadAbove > 0 { + if model.unreadAbove > 0 { if loadingMoreItems { circleButton { ProgressView() } } else { circleButton { - unreadCountText(unreadAbove) + unreadCountText(model.unreadAbove) .font(.callout) .foregroundColor(theme.colors.primary) }