diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 2271813bfb..967372502a 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -1332,8 +1332,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, let (_, items) = countAndFilterItems var filterItems: [ChatListFilterTabEntry] = [] filterItems.append(.all(unreadCount: 0)) - for (filter, unreadCount) in items { - filterItems.append(.filter(id: filter.id, text: filter.title, unreadCount: unreadCount)) + for (filter, unreadCount, hasUnmutedUnread) in items { + filterItems.append(.filter(id: filter.id, text: filter.title, unread: ChatListFilterTabEntryUnreadCount(value: unreadCount, hasUnmuted: hasUnmutedUnread))) } var resolvedItems = filterItems diff --git a/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift b/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift index 87f91df2d3..da99a6402c 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift @@ -68,7 +68,9 @@ private final class ItemNode: ASDisplayNode { private let shortTitleNode: ImmediateTextNode private let badgeContainerNode: ASDisplayNode private let badgeTextNode: ImmediateTextNode - private let badgeBackgroundNode: ASImageNode + private let badgeBackgroundActiveNode: ASImageNode + private let badgeBackgroundInactiveNode: ASImageNode + private var deleteButtonNode: ItemNodeDeleteButtonNode? private let buttonNode: HighlightTrackingButtonNode @@ -101,9 +103,14 @@ private final class ItemNode: ASDisplayNode { self.badgeTextNode = ImmediateTextNode() self.badgeTextNode.displaysAsynchronously = false - self.badgeBackgroundNode = ASImageNode() - self.badgeBackgroundNode.displaysAsynchronously = false - self.badgeBackgroundNode.displayWithoutProcessing = true + self.badgeBackgroundActiveNode = ASImageNode() + self.badgeBackgroundActiveNode.displaysAsynchronously = false + self.badgeBackgroundActiveNode.displayWithoutProcessing = true + + self.badgeBackgroundInactiveNode = ASImageNode() + self.badgeBackgroundInactiveNode.displaysAsynchronously = false + self.badgeBackgroundInactiveNode.displayWithoutProcessing = true + self.badgeBackgroundInactiveNode.isHidden = true self.buttonNode = HighlightTrackingButtonNode() @@ -112,7 +119,8 @@ private final class ItemNode: ASDisplayNode { self.extractedContainerNode.contentNode.addSubnode(self.extractedBackgroundNode) self.extractedContainerNode.contentNode.addSubnode(self.titleNode) self.extractedContainerNode.contentNode.addSubnode(self.shortTitleNode) - self.badgeContainerNode.addSubnode(self.badgeBackgroundNode) + self.badgeContainerNode.addSubnode(self.badgeBackgroundActiveNode) + self.badgeContainerNode.addSubnode(self.badgeBackgroundInactiveNode) self.badgeContainerNode.addSubnode(self.badgeTextNode) self.extractedContainerNode.contentNode.addSubnode(self.badgeContainerNode) self.extractedContainerNode.contentNode.addSubnode(self.buttonNode) @@ -150,11 +158,12 @@ private final class ItemNode: ASDisplayNode { self.pressed() } - func updateText(title: String, shortTitle: String, unreadCount: Int, isNoFilter: Bool, isSelected: Bool, isEditing: Bool, isAllChats: Bool, isReordering: Bool, presentationData: PresentationData, transition: ContainedViewLayoutTransition) { + func updateText(title: String, shortTitle: String, unreadCount: Int, unreadHasUnmuted: Bool, isNoFilter: Bool, isSelected: Bool, isEditing: Bool, isAllChats: Bool, isReordering: Bool, presentationData: PresentationData, transition: ContainedViewLayoutTransition) { if self.theme !== presentationData.theme { self.theme = presentationData.theme - self.badgeBackgroundNode.image = generateStretchableFilledCircleImage(diameter: 18.0, color: presentationData.theme.list.itemCheckColors.fillColor) + self.badgeBackgroundActiveNode.image = generateStretchableFilledCircleImage(diameter: 18.0, color: presentationData.theme.chatList.unreadBadgeActiveBackgroundColor) + self.badgeBackgroundInactiveNode.image = generateStretchableFilledCircleImage(diameter: 18.0, color: presentationData.theme.chatList.unreadBadgeInactiveBackgroundColor) } self.containerNode.isGestureEnabled = !isNoFilter && !isEditing && !isReordering @@ -191,6 +200,8 @@ private final class ItemNode: ASDisplayNode { self.shortTitleNode.attributedText = NSAttributedString(string: shortTitle, font: Font.medium(14.0), textColor: isSelected ? presentationData.theme.list.itemAccentColor : presentationData.theme.list.itemSecondaryTextColor) if unreadCount != 0 { self.badgeTextNode.attributedText = NSAttributedString(string: "\(unreadCount)", font: Font.regular(14.0), textColor: presentationData.theme.list.itemCheckColors.foregroundColor) + self.badgeBackgroundActiveNode.isHidden = !unreadHasUnmuted + self.badgeBackgroundInactiveNode.isHidden = unreadHasUnmuted } if self.isReordering != isReordering { @@ -222,7 +233,8 @@ private final class ItemNode: ASDisplayNode { let badgeInset: CGFloat = 4.0 let badgeBackgroundFrame = CGRect(origin: CGPoint(x: titleSize.width + 5.0, y: floor((height - 18.0) / 2.0)), size: CGSize(width: max(18.0, badgeSize.width + badgeInset * 2.0), height: 18.0)) self.badgeContainerNode.frame = badgeBackgroundFrame - self.badgeBackgroundNode.frame = CGRect(origin: CGPoint(), size: badgeBackgroundFrame.size) + self.badgeBackgroundActiveNode.frame = CGRect(origin: CGPoint(), size: badgeBackgroundFrame.size) + self.badgeBackgroundInactiveNode.frame = CGRect(origin: CGPoint(), size: badgeBackgroundFrame.size) self.badgeTextNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((badgeBackgroundFrame.width - badgeSize.width) / 2.0), y: floor((badgeBackgroundFrame.height - badgeSize.height) / 2.0)), size: badgeSize) let width: CGFloat @@ -340,9 +352,14 @@ enum ChatListFilterTabEntryId: Hashable { case filter(Int32) } +struct ChatListFilterTabEntryUnreadCount: Equatable { + let value: Int + let hasUnmuted: Bool +} + enum ChatListFilterTabEntry: Equatable { case all(unreadCount: Int) - case filter(id: Int32, text: String, unreadCount: Int) + case filter(id: Int32, text: String, unread: ChatListFilterTabEntryUnreadCount) var id: ChatListFilterTabEntryId { switch self { @@ -637,18 +654,21 @@ final class ChatListFilterTabContainerNode: ASDisplayNode { self.itemNodes[filter.id] = itemNode } let unreadCount: Int + let unreadHasUnmuted: Bool var isNoFilter: Bool = false switch filter { case let .all(count): unreadCount = count + unreadHasUnmuted = true isNoFilter = true case let .filter(filter): - unreadCount = filter.unreadCount + unreadCount = filter.unread.value + unreadHasUnmuted = filter.unread.hasUnmuted } if !wasAdded && (itemNode.unreadCount != 0) != (unreadCount != 0) { badgeAnimations[filter.id] = (unreadCount != 0) ? .in : .out } - itemNode.updateText(title: filter.title(strings: presentationData.strings), shortTitle: filter.shortTitle(strings: presentationData.strings), unreadCount: unreadCount, isNoFilter: isNoFilter, isSelected: selectedFilter == filter.id, isEditing: false, isAllChats: isNoFilter, isReordering: isEditing || isReordering, presentationData: presentationData, transition: itemNodeTransition) + itemNode.updateText(title: filter.title(strings: presentationData.strings), shortTitle: filter.shortTitle(strings: presentationData.strings), unreadCount: unreadCount, unreadHasUnmuted: unreadHasUnmuted, isNoFilter: isNoFilter, isSelected: selectedFilter == filter.id, isEditing: false, isAllChats: isNoFilter, isReordering: isEditing || isReordering, presentationData: presentationData, transition: itemNodeTransition) } var removeKeys: [ChatListFilterTabEntryId] = [] for (id, _) in self.itemNodes { diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index 87ab517a47..301dda49d3 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -174,7 +174,7 @@ private enum ChatListRecentEntry: Comparable, Identifiable { var isMuted = false if let notificationSettings = peer.notificationSettings { - isMuted = notificationSettings.isRemovedFromTotalUnreadCount + isMuted = notificationSettings.isRemovedFromTotalUnreadCount(default: false) } var badge: ContactsPeerItemBadge? if peer.unreadCount > 0 { diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index b63019d418..21bcdd0daa 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -20,7 +20,7 @@ import ChatListSearchItemNode import ContextUI public enum ChatListItemContent { - case peer(message: Message?, peer: RenderedPeer, combinedReadState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, embeddedState: PeerChatListEmbeddedInterfaceState?, inputActivities: [(Peer, PeerInputActivity)]?, isAd: Bool, ignoreUnreadBadge: Bool, displayAsMessage: Bool, hasFailedMessages: Bool) + case peer(message: Message?, peer: RenderedPeer, combinedReadState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, embeddedState: PeerChatListEmbeddedInterfaceState?, inputActivities: [(Peer, PeerInputActivity)]?, isAd: Bool, ignoreUnreadBadge: Bool, displayAsMessage: Bool, hasFailedMessages: Bool) case groupReference(groupId: PeerGroupId, peers: [ChatListGroupReferencePeer], message: Message?, unreadState: PeerGroupUnreadCountersCombinedSummary, hiddenByDefault: Bool) public var chatLocation: ChatLocation? { @@ -685,7 +685,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { contentPeer = .chat(peerValue) combinedReadState = combinedReadStateValue if let combinedReadState = combinedReadState, !isAdValue && !ignoreUnreadBadge { - unreadCount = (combinedReadState.count, combinedReadState.isUnread, notificationSettingsValue?.isRemovedFromTotalUnreadCount ?? false, nil) + unreadCount = (combinedReadState.count, combinedReadState.isUnread, notificationSettingsValue?.isRemovedFromTotalUnreadCount(default: false) ?? false, nil) } else { unreadCount = (0, false, false, nil) } diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 76e06b45d8..63ac101c88 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -374,11 +374,6 @@ public enum ChatListGlobalScrollOption { case unread } -private struct ChatListVisibleUnreadCounts: Equatable { - var raw: Int32 = 0 - var filtered: Int32 = 0 -} - public enum ChatListNodeScrollPosition { case auto case autoUp @@ -478,15 +473,6 @@ public final class ChatListNode: ListView { public var contentOffsetChanged: ((ListViewVisibleContentOffset) -> Void)? public var contentScrollingEnded: ((ListView) -> Bool)? - private let visibleUnreadCounts = ValuePromise(ChatListVisibleUnreadCounts()) - private var visibleUnreadCountsValue = ChatListVisibleUnreadCounts() { - didSet { - if self.visibleUnreadCountsValue != oldValue { - self.visibleUnreadCounts.set(self.visibleUnreadCountsValue) - } - } - } - var isEmptyUpdated: ((ChatListNodeEmptyState, Bool, ContainedViewLayoutTransition) -> Void)? private var currentIsEmptyState: ChatListNodeEmptyState? @@ -900,8 +886,6 @@ public final class ChatListNode: ListView { strongSelf.enqueueHistoryPreloadUpdate() } - var rawUnreadCount: Int32 = 0 - var filteredUnreadCount: Int32 = 0 var archiveVisible = false if let range = range.visibleRange { let entryCount = chatListView.filteredEntries.count @@ -911,14 +895,8 @@ public final class ChatListNode: ListView { continue } switch chatListView.filteredEntries[entryCount - i - 1] { - case let .PeerEntry(_, _, _, readState, notificationSettings, _, _, _, _, _, _, _, _, _, _, _): - if let readState = readState { - let count = readState.count - rawUnreadCount += count - if let notificationSettings = notificationSettings, !notificationSettings.isRemovedFromTotalUnreadCount { - filteredUnreadCount += count - } - } + case .PeerEntry: + break case .GroupReferenceEntry: archiveVisible = true default: @@ -926,10 +904,6 @@ public final class ChatListNode: ListView { } } } - var visibleUnreadCountsValue = strongSelf.visibleUnreadCountsValue - visibleUnreadCountsValue.raw = rawUnreadCount - visibleUnreadCountsValue.filtered = filteredUnreadCount - strongSelf.visibleUnreadCountsValue = visibleUnreadCountsValue if !archiveVisible && strongSelf.currentState.archiveShouldBeTemporaryRevealed { strongSelf.updateState { state in var state = state @@ -1161,25 +1135,10 @@ public final class ChatListNode: ListView { self.scrollToTopOptionPromise.set(combineLatest( renderedTotalUnreadCount(accountManager: self.context.sharedContext.accountManager, postbox: self.context.account.postbox) |> deliverOnMainQueue, - self.visibleUnreadCounts.get(), self.scrolledAtTop.get() - ) |> map { badge, visibleUnreadCounts, scrolledAtTop -> ChatListGlobalScrollOption in + ) |> map { badge, scrolledAtTop -> ChatListGlobalScrollOption in if scrolledAtTop { - if badge.0 != 0 { - switch badge.1 { - case .raw: - if visibleUnreadCounts.raw < badge.0 { - return .unread - } - case .filtered: - if visibleUnreadCounts.filtered < badge.0 { - return .unread - } - } - return .none - } else { - return .none - } + return .none } else { return .top } diff --git a/submodules/ChatListUI/Sources/TabBarChatListFilterController.swift b/submodules/ChatListUI/Sources/TabBarChatListFilterController.swift index 2af6a41583..2718a45b30 100644 --- a/submodules/ChatListUI/Sources/TabBarChatListFilterController.swift +++ b/submodules/ChatListUI/Sources/TabBarChatListFilterController.swift @@ -10,21 +10,29 @@ import Postbox import TelegramUIPreferences import TelegramCore -func chatListFilterItems(context: AccountContext) -> Signal<(Int, [(ChatListFilter, Int)]), NoError> { +func chatListFilterItems(context: AccountContext) -> Signal<(Int, [(ChatListFilter, Int, Bool)]), NoError> { return updatedChatListFilters(postbox: context.account.postbox) |> distinctUntilChanged - |> mapToSignal { filters -> Signal<(Int, [(ChatListFilter, Int)]), NoError> in + |> mapToSignal { filters -> Signal<(Int, [(ChatListFilter, Int, Bool)]), NoError> in var unreadCountItems: [UnreadMessageCountsItem] = [] - unreadCountItems.append(.total(nil)) + unreadCountItems.append(.totalInGroup(.root)) var additionalPeerIds = Set() + var additionalGroupIds = Set() for filter in filters { additionalPeerIds.formUnion(filter.data.includePeers) + additionalPeerIds.formUnion(filter.data.excludePeers) + if !filter.data.excludeArchived { + additionalGroupIds.insert(Namespaces.PeerGroup.archive) + } } if !additionalPeerIds.isEmpty { for peerId in additionalPeerIds { unreadCountItems.append(.peer(peerId)) } } + for groupId in additionalGroupIds { + unreadCountItems.append(.totalInGroup(groupId)) + } let unreadKey: PostboxViewKey = .unreadCounts(items: unreadCountItems) var keys: [PostboxViewKey] = [] keys.append(unreadKey) @@ -36,34 +44,36 @@ func chatListFilterItems(context: AccountContext) -> Signal<(Int, [(ChatListFilt context.account.postbox.combinedView(keys: keys), Signal.single(true) ) - |> map { view, _ -> (Int, [(ChatListFilter, Int)]) in + |> map { view, _ -> (Int, [(ChatListFilter, Int, Bool)]) in guard let unreadCounts = view.views[unreadKey] as? UnreadMessageCountsView else { return (0, []) } - var result: [(ChatListFilter, Int)] = [] + var result: [(ChatListFilter, Int, Bool)] = [] - var peerTagAndCount: [PeerId: (PeerSummaryCounterTags, Int)] = [:] + var peerTagAndCount: [PeerId: (PeerSummaryCounterTags, Int, Bool)] = [:] - var totalState: ChatListTotalUnreadState? + var totalStates: [PeerGroupId: ChatListTotalUnreadState] = [:] for entry in unreadCounts.entries { switch entry { - case let .total(_, totalStateValue): - totalState = totalStateValue - case let .totalInGroup(groupId, totalGroupState): - break + case let .total(_, state): + totalStates[.root] = state + case let .totalInGroup(groupId, state): + totalStates[groupId] = state case let .peer(peerId, state): if let state = state, state.isUnread { if let peerView = view.views[.basicPeer(peerId)] as? BasicPeerView, let peer = peerView.peer { let tag = context.account.postbox.seedConfiguration.peerSummaryCounterTags(peer, peerView.isContact) + + var peerCount = Int(state.count) + if state.isUnread { + peerCount = max(1, peerCount) + } + if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings, case .muted = notificationSettings.muteState { - peerTagAndCount[peerId] = (tag, 0) + peerTagAndCount[peerId] = (tag, peerCount, false) } else { - var peerCount = Int(state.count) - if state.isUnread { - peerCount = max(1, peerCount) - } - peerTagAndCount[peerId] = (tag, peerCount) + peerTagAndCount[peerId] = (tag, peerCount, true) } } } @@ -89,25 +99,57 @@ func chatListFilterItems(context: AccountContext) -> Signal<(Int, [(ChatListFilt if filter.data.categories.contains(.channels) { tags.append(.channel) } - + var count = 0 - if let totalState = totalState { + var hasUnmutedUnread = false + if let totalState = totalStates[.root] { for tag in tags { - if let value = totalState.filteredCounters[tag] { + if let value = totalState.absoluteCounters[tag] { count += Int(value.chatCount) } - } - } - for peerId in filter.data.includePeers { - if let (tag, peerCount) = peerTagAndCount[peerId] { - if !tags.contains(tag) { - if peerCount != 0 { - count += 1 + if let value = totalState.filteredCounters[tag] { + if value.chatCount != 0 { + hasUnmutedUnread = true } } } } - result.append((filter, count)) + if !filter.data.excludeArchived { + if let totalState = totalStates[Namespaces.PeerGroup.archive] { + for tag in tags { + if let value = totalState.absoluteCounters[tag] { + count += Int(value.chatCount) + } + if let value = totalState.filteredCounters[tag] { + if value.chatCount != 0 { + hasUnmutedUnread = true + } + } + } + } + } + for peerId in filter.data.includePeers { + if let (tag, peerCount, hasUnmuted) = peerTagAndCount[peerId] { + if !tags.contains(tag) { + if peerCount != 0 { + count += 1 + if hasUnmuted { + hasUnmutedUnread = true + } + } + } + } + } + for peerId in filter.data.excludePeers { + if let (tag, peerCount, _) = peerTagAndCount[peerId] { + if tags.contains(tag) { + if peerCount != 0 { + count -= 1 + } + } + } + } + result.append((filter, count, hasUnmutedUnread)) } return (totalBadge, result) diff --git a/submodules/Display/Source/NavigationBar.swift b/submodules/Display/Source/NavigationBar.swift index 8987abd847..f59718cc6e 100644 --- a/submodules/Display/Source/NavigationBar.swift +++ b/submodules/Display/Source/NavigationBar.swift @@ -799,10 +799,8 @@ open class NavigationBar: ASDisplayNode { } private func requestLayout() { - if self.transitionState == nil { - self.requestedLayout = true - self.setNeedsLayout() - } + self.requestedLayout = true + self.setNeedsLayout() } override open func layout() { diff --git a/submodules/Postbox/Sources/ChatListIndexTable.swift b/submodules/Postbox/Sources/ChatListIndexTable.swift index 93165b6523..7b677cfecd 100644 --- a/submodules/Postbox/Sources/ChatListIndexTable.swift +++ b/submodules/Postbox/Sources/ChatListIndexTable.swift @@ -1,9 +1,5 @@ import Foundation -func shouldPeerParticipateInUnreadCountStats(peer: Peer) -> Bool { - return true -} - struct ChatListPeerInclusionIndex { let topMessageIndex: MessageIndex? let inclusion: PeerChatListInclusion @@ -175,7 +171,7 @@ final class ChatListIndexTable: Table { assert(self.updatedPreviousPeerCachedIndices.isEmpty) } - func commitWithTransaction(postbox: Postbox, alteredInitialPeerCombinedReadStates: [PeerId: CombinedPeerReadState], updatedPeers: [((Peer, Bool)?, (Peer, Bool))], transactionParticipationInTotalUnreadCountUpdates: (added: Set, removed: Set), updatedRootUnreadState: inout ChatListTotalUnreadState?, updatedGroupTotalUnreadSummaries: inout [PeerGroupId: PeerGroupUnreadCountersCombinedSummary], currentUpdatedGroupSummarySynchronizeOperations: inout [PeerGroupAndNamespace: Bool]) { + func commitWithTransaction(postbox: Postbox, alteredInitialPeerCombinedReadStates: [PeerId: CombinedPeerReadState], updatedPeers: [((Peer, Bool)?, (Peer, Bool))], transactionParticipationInTotalUnreadCountUpdates: (added: Set, removed: Set), updatedTotalUnreadStates: inout [PeerGroupId: ChatListTotalUnreadState], updatedGroupTotalUnreadSummaries: inout [PeerGroupId: PeerGroupUnreadCountersCombinedSummary], currentUpdatedGroupSummarySynchronizeOperations: inout [PeerGroupAndNamespace: Bool]) { var updatedPeerTags: [PeerId: (previous: PeerSummaryCounterTags, updated: PeerSummaryCounterTags)] = [:] for (previous, updated) in updatedPeers { let previousTags: PeerSummaryCounterTags @@ -341,9 +337,11 @@ final class ChatListIndexTable: Table { } } - var updatedRootState: ChatListTotalUnreadState? + var updatedTotalStates: [PeerGroupId: ChatListTotalUnreadState] = [:] var updatedTotalUnreadSummaries: [PeerGroupId: PeerGroupUnreadCountersCombinedSummary] = [:] + let globalNotificationSettings = postbox.getGlobalNotificationSettings() + for peerId in alteredPeerIds { guard let peer = postbox.peerTable.get(peerId) else { continue @@ -369,14 +367,12 @@ final class ChatListIndexTable: Table { } for groupId in groupIds { - var totalRootUnreadState: ChatListTotalUnreadState? + var totalGroupUnreadState: ChatListTotalUnreadState var summary: PeerGroupUnreadCountersCombinedSummary - if case .root = groupId { - if let current = updatedRootState { - totalRootUnreadState = current - } else { - totalRootUnreadState = postbox.messageHistoryMetadataTable.getChatListTotalUnreadState() - } + if let current = updatedTotalStates[groupId] { + totalGroupUnreadState = current + } else { + totalGroupUnreadState = postbox.messageHistoryMetadataTable.getTotalUnreadState(groupId: groupId) } if let current = updatedTotalUnreadSummaries[groupId] { summary = current @@ -416,113 +412,110 @@ final class ChatListIndexTable: Table { var initialFilteredValue: (Int32, Bool, Bool) = initialValue var currentFilteredValue: (Int32, Bool, Bool) = currentValue - var initialFilteredStates: CombinedPeerReadState = initialStates - var currentFilteredStates: CombinedPeerReadState = currentStates - if transactionParticipationInTotalUnreadCountUpdates.added.contains(peerId) || transactionParticipationInTotalUnreadCountUpdates.added.contains(notificationPeerId) { initialFilteredValue = (0, false, false) - initialFilteredStates = CombinedPeerReadState(states: []) } else if transactionParticipationInTotalUnreadCountUpdates.removed.contains(peerId) || transactionParticipationInTotalUnreadCountUpdates.removed.contains(notificationPeerId) { currentFilteredValue = (0, false, false) - currentFilteredStates = CombinedPeerReadState(states: []) } else { - if let notificationSettings = postbox.peerNotificationSettingsTable.getEffective(notificationPeerId), !notificationSettings.isRemovedFromTotalUnreadCount { + let isRemovedFromTotalUnreadCount: Bool + if let notificationSettings = postbox.peerNotificationSettingsTable.getEffective(notificationPeerId) { + isRemovedFromTotalUnreadCount = notificationSettings.isRemovedFromTotalUnreadCount(default: !globalNotificationSettings.defaultIncludePeer(peer: peer)) } else { + isRemovedFromTotalUnreadCount = !globalNotificationSettings.defaultIncludePeer(peer: peer) + } + + if isRemovedFromTotalUnreadCount { initialFilteredValue = (0, false, false) currentFilteredValue = (0, false, false) - initialFilteredStates = CombinedPeerReadState(states: []) - currentFilteredStates = CombinedPeerReadState(states: []) } } - if var currentTotalRootUnreadState = totalRootUnreadState { - var keptTags: PeerSummaryCounterTags = postbox.seedConfiguration.peerSummaryCounterTags(peer, isContact) - if let (removedTags, addedTags) = updatedPeerTags[peerId] { - keptTags.remove(removedTags) - keptTags.remove(addedTags) - - for tag in removedTags { - alterTags(¤tTotalRootUnreadState, peerId, tag, { absolute, filtered in - var absolute = absolute - var filtered = filtered - absolute.messageCount -= initialValue.0 - if initialValue.1 { - absolute.chatCount -= 1 - } - if initialValue.2 && initialValue.0 == 0 { - absolute.messageCount -= 1 - } - filtered.messageCount -= initialFilteredValue.0 - if initialFilteredValue.1 { - filtered.chatCount -= 1 - } - if initialFilteredValue.2 && initialFilteredValue.0 == 0 { - filtered.messageCount -= 1 - } - return (absolute, filtered) - }) - } - for tag in addedTags { - alterTags(¤tTotalRootUnreadState, peerId, tag, { absolute, filtered in - var absolute = absolute - var filtered = filtered - absolute.messageCount += currentValue.0 - if currentValue.2 && currentValue.0 == 0 { - absolute.messageCount += 1 - } - if currentValue.1 { - absolute.chatCount += 1 - } - filtered.messageCount += currentFilteredValue.0 - if currentFilteredValue.1 { - filtered.chatCount += 1 - } - if currentFilteredValue.2 && currentFilteredValue.0 == 0 { - filtered.messageCount += 1 - } - return (absolute, filtered) - }) - } - } + var keptTags: PeerSummaryCounterTags = postbox.seedConfiguration.peerSummaryCounterTags(peer, isContact) + if let (removedTags, addedTags) = updatedPeerTags[peerId] { + keptTags.remove(removedTags) + keptTags.remove(addedTags) - for tag in keptTags { - alterTags(¤tTotalRootUnreadState, peerId, tag, { absolute, filtered in + for tag in removedTags { + alterTags(&totalGroupUnreadState, peerId, tag, { absolute, filtered in var absolute = absolute var filtered = filtered - - let chatDifference: Int32 - if initialValue.1 != currentValue.1 { - chatDifference = initialValue.1 ? -1 : 1 - } else { - chatDifference = 0 + absolute.messageCount -= initialValue.0 + if initialValue.1 { + absolute.chatCount -= 1 } - - let currentUnreadMark: Int32 = currentValue.2 ? 1 : 0 - let initialUnreadMark: Int32 = initialValue.2 ? 1 : 0 - let messageDifference = max(currentValue.0, currentUnreadMark) - max(initialValue.0, initialUnreadMark) - - let chatFilteredDifference: Int32 - if initialFilteredValue.1 != currentFilteredValue.1 { - chatFilteredDifference = initialFilteredValue.1 ? -1 : 1 - } else { - chatFilteredDifference = 0 + if initialValue.2 && initialValue.0 == 0 { + absolute.messageCount -= 1 + } + filtered.messageCount -= initialFilteredValue.0 + if initialFilteredValue.1 { + filtered.chatCount -= 1 + } + if initialFilteredValue.2 && initialFilteredValue.0 == 0 { + filtered.messageCount -= 1 + } + return (absolute, filtered) + }) + } + for tag in addedTags { + alterTags(&totalGroupUnreadState, peerId, tag, { absolute, filtered in + var absolute = absolute + var filtered = filtered + absolute.messageCount += currentValue.0 + if currentValue.2 && currentValue.0 == 0 { + absolute.messageCount += 1 + } + if currentValue.1 { + absolute.chatCount += 1 + } + filtered.messageCount += currentFilteredValue.0 + if currentFilteredValue.1 { + filtered.chatCount += 1 + } + if currentFilteredValue.2 && currentFilteredValue.0 == 0 { + filtered.messageCount += 1 } - let currentFilteredUnreadMark: Int32 = currentFilteredValue.2 ? 1 : 0 - let initialFilteredUnreadMark: Int32 = initialFilteredValue.2 ? 1 : 0 - let messageFilteredDifference = max(currentFilteredValue.0, currentFilteredUnreadMark) - max(initialFilteredValue.0, initialFilteredUnreadMark) - - absolute.messageCount += messageDifference - absolute.chatCount += chatDifference - filtered.messageCount += messageFilteredDifference - filtered.chatCount += chatFilteredDifference - return (absolute, filtered) }) } - - updatedRootState = currentTotalRootUnreadState } + for tag in keptTags { + alterTags(&totalGroupUnreadState, peerId, tag, { absolute, filtered in + var absolute = absolute + var filtered = filtered + + let chatDifference: Int32 + if initialValue.1 != currentValue.1 { + chatDifference = initialValue.1 ? -1 : 1 + } else { + chatDifference = 0 + } + + let currentUnreadMark: Int32 = currentValue.2 ? 1 : 0 + let initialUnreadMark: Int32 = initialValue.2 ? 1 : 0 + let messageDifference = max(currentValue.0, currentUnreadMark) - max(initialValue.0, initialUnreadMark) + + let chatFilteredDifference: Int32 + if initialFilteredValue.1 != currentFilteredValue.1 { + chatFilteredDifference = initialFilteredValue.1 ? -1 : 1 + } else { + chatFilteredDifference = 0 + } + let currentFilteredUnreadMark: Int32 = currentFilteredValue.2 ? 1 : 0 + let initialFilteredUnreadMark: Int32 = initialFilteredValue.2 ? 1 : 0 + let messageFilteredDifference = max(currentFilteredValue.0, currentFilteredUnreadMark) - max(initialFilteredValue.0, initialFilteredUnreadMark) + + absolute.messageCount += messageDifference + absolute.chatCount += chatDifference + filtered.messageCount += messageFilteredDifference + filtered.chatCount += chatFilteredDifference + + return (absolute, filtered) + }) + } + + updatedTotalStates[groupId] = totalGroupUnreadState + var namespaces: [MessageId.Namespace] = [] for (namespace, _) in initialStates.states { namespaces.append(namespace) @@ -549,10 +542,10 @@ final class ChatListIndexTable: Table { } } - if let updatedRootState = updatedRootState { - if postbox.messageHistoryMetadataTable.getChatListTotalUnreadState() != updatedRootState { - postbox.messageHistoryMetadataTable.setChatListTotalUnreadState(updatedRootState) - updatedRootUnreadState = updatedRootState + for (groupId, state) in updatedTotalStates { + if postbox.messageHistoryMetadataTable.getTotalUnreadState(groupId: groupId) != state { + postbox.messageHistoryMetadataTable.setTotalUnreadState(groupId: groupId, state: state) + updatedTotalUnreadStates[groupId] = state } } @@ -569,13 +562,15 @@ final class ChatListIndexTable: Table { assert(self.updatedPreviousPeerCachedIndices.isEmpty) } - func debugReindexUnreadCounts(postbox: Postbox) -> (ChatListTotalUnreadState, [PeerGroupId: PeerGroupUnreadCountersCombinedSummary]) { + func debugReindexUnreadCounts(postbox: Postbox) -> ([PeerGroupId: ChatListTotalUnreadState], [PeerGroupId: PeerGroupUnreadCountersCombinedSummary]) { + let globalNotificationSettings = postbox.getGlobalNotificationSettings() + var peerIds: [PeerId] = [] for groupId in postbox.chatListTable.existingGroups() + [.root] { let groupPeerIds = postbox.chatListTable.allPeerIds(groupId: groupId) peerIds.append(contentsOf: groupPeerIds) } - var rootState = ChatListTotalUnreadState(absoluteCounters: [:], filteredCounters: [:]) + var totalStates: [PeerGroupId: ChatListTotalUnreadState] = [:] var summaries: [PeerGroupId: PeerGroupUnreadCountersCombinedSummary] = [:] for peerId in peerIds { guard let peer = postbox.peerTable.get(peerId) else { @@ -589,46 +584,55 @@ final class ChatListIndexTable: Table { let notificationSettings = postbox.peerNotificationSettingsTable.getEffective(notificationPeerId) let inclusion = self.get(peerId: peerId) if let (groupId, _) = inclusion.includedIndex(peerId: peerId) { - if case .root = groupId { - let peerMessageCount = combinedState.count - - let summaryTags = postbox.seedConfiguration.peerSummaryCounterTags(peer, isContact) + if totalStates[groupId] == nil { + totalStates[groupId] = ChatListTotalUnreadState(absoluteCounters: [:], filteredCounters: [:]) + } + + let peerMessageCount = combinedState.count + + let summaryTags = postbox.seedConfiguration.peerSummaryCounterTags(peer, isContact) + for tag in summaryTags { + if totalStates[groupId]!.absoluteCounters[tag] == nil { + totalStates[groupId]!.absoluteCounters[tag] = ChatListTotalUnreadCounters(messageCount: 0, chatCount: 0) + } + var messageCount = totalStates[groupId]!.absoluteCounters[tag]!.messageCount + messageCount = messageCount &+ peerMessageCount + if messageCount < 0 { + messageCount = 0 + } + if combinedState.isUnread { + totalStates[groupId]!.absoluteCounters[tag]!.chatCount += 1 + } + if combinedState.markedUnread { + messageCount = max(1, messageCount) + } + totalStates[groupId]!.absoluteCounters[tag]!.messageCount = messageCount + } + + let isRemovedFromTotalUnreadCount: Bool + if let notificationSettings = postbox.peerNotificationSettingsTable.getEffective(notificationPeerId) { + isRemovedFromTotalUnreadCount = notificationSettings.isRemovedFromTotalUnreadCount(default: !globalNotificationSettings.defaultIncludePeer(peer: peer)) + } else { + isRemovedFromTotalUnreadCount = !globalNotificationSettings.defaultIncludePeer(peer: peer) + } + + if !isRemovedFromTotalUnreadCount { for tag in summaryTags { - if rootState.absoluteCounters[tag] == nil { - rootState.absoluteCounters[tag] = ChatListTotalUnreadCounters(messageCount: 0, chatCount: 0) + if totalStates[groupId]!.filteredCounters[tag] == nil { + totalStates[groupId]!.filteredCounters[tag] = ChatListTotalUnreadCounters(messageCount: 0, chatCount: 0) } - var messageCount = rootState.absoluteCounters[tag]!.messageCount + var messageCount = totalStates[groupId]!.filteredCounters[tag]!.messageCount messageCount = messageCount &+ peerMessageCount if messageCount < 0 { messageCount = 0 } if combinedState.isUnread { - rootState.absoluteCounters[tag]!.chatCount += 1 + totalStates[groupId]!.filteredCounters[tag]!.chatCount += 1 } if combinedState.markedUnread { messageCount = max(1, messageCount) } - rootState.absoluteCounters[tag]!.messageCount = messageCount - } - - if let notificationSettings = notificationSettings, !notificationSettings.isRemovedFromTotalUnreadCount { - for tag in summaryTags { - if rootState.filteredCounters[tag] == nil { - rootState.filteredCounters[tag] = ChatListTotalUnreadCounters(messageCount: 0, chatCount: 0) - } - var messageCount = rootState.filteredCounters[tag]!.messageCount - messageCount = messageCount &+ peerMessageCount - if messageCount < 0 { - messageCount = 0 - } - if combinedState.isUnread { - rootState.filteredCounters[tag]!.chatCount += 1 - } - if combinedState.markedUnread { - messageCount = max(1, messageCount) - } - rootState.filteredCounters[tag]!.messageCount = messageCount - } + totalStates[groupId]!.filteredCounters[tag]!.messageCount = messageCount } } @@ -647,7 +651,7 @@ final class ChatListIndexTable: Table { } } - return (rootState, summaries) + return (totalStates, summaries) } func reindexPeerGroupUnreadCounts(postbox: Postbox, groupId: PeerGroupId) -> PeerGroupUnreadCountersCombinedSummary { diff --git a/submodules/Postbox/Sources/ChatListTable.swift b/submodules/Postbox/Sources/ChatListTable.swift index dc3ad0e0a4..7d6a284cc8 100644 --- a/submodules/Postbox/Sources/ChatListTable.swift +++ b/submodules/Postbox/Sources/ChatListTable.swift @@ -727,7 +727,7 @@ final class ChatListTable: Table { func allPeerIds(groupId: PeerGroupId) -> [PeerId] { var peerIds: [PeerId] = [] - self.valueBox.range(self.table, start: self.upperBound(groupId: groupId), end: self.lowerBound(groupId: groupId), keys: { key in + self.valueBox.range(self.table, start: self.lowerBound(groupId: groupId), end: self.upperBound(groupId: groupId), keys: { key in let (_, _, messageIndex, type) = extractKey(key) if type == ChatListEntryType.message.rawValue { peerIds.append(messageIndex.id.peerId) @@ -792,6 +792,8 @@ final class ChatListTable: Table { let lower: ValueBoxKey let upper: ValueBoxKey + let globalNotificationSettings = postbox.getGlobalNotificationSettings() + switch position { case let .earlier(index): upper = self.upperBound(groupId: groupId) @@ -825,10 +827,17 @@ final class ChatListTable: Table { } else { notificationSettings = postbox.peerNotificationSettingsTable.getEffective(peerId) } - } - if let notificationSettings = notificationSettings, !notificationSettings.isRemovedFromTotalUnreadCount { - result = index - return false + let isRemovedFromTotalUnreadCount: Bool + if let notificationSettings = notificationSettings { + isRemovedFromTotalUnreadCount = notificationSettings.isRemovedFromTotalUnreadCount(default: !globalNotificationSettings.defaultIncludePeer(peer: peer)) + } else { + isRemovedFromTotalUnreadCount = !globalNotificationSettings.defaultIncludePeer(peer: peer) + } + + if !isRemovedFromTotalUnreadCount { + result = index + return false + } } } else { result = index diff --git a/submodules/Postbox/Sources/ChatListView.swift b/submodules/Postbox/Sources/ChatListView.swift index c9b32485c6..b0fd599a85 100644 --- a/submodules/Postbox/Sources/ChatListView.swift +++ b/submodules/Postbox/Sources/ChatListView.swift @@ -88,7 +88,7 @@ public struct ChatListGroupReferenceEntry: Equatable { } public enum ChatListEntry: Comparable { - case MessageEntry(ChatListIndex, Message?, CombinedPeerReadState?, PeerNotificationSettings?, PeerChatListEmbeddedInterfaceState?, RenderedPeer, PeerPresence?, ChatListMessageTagSummaryInfo, Bool, Bool) + case MessageEntry(index: ChatListIndex, message: Message?, readState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: PeerChatListEmbeddedInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, hasFailed: Bool, isContact: Bool) case HoleEntry(ChatListHole) public var index: ChatListIndex { @@ -102,9 +102,9 @@ public enum ChatListEntry: Comparable { public static func ==(lhs: ChatListEntry, rhs: ChatListEntry) -> Bool { switch lhs { - case let .MessageEntry(lhsIndex, lhsMessage, lhsReadState, lhsSettings, lhsEmbeddedState, lhsPeer, lhsPresence, lhsInfo, lhsHasFailed, lhsIsContact): + case let .MessageEntry(lhsIndex, lhsMessage, lhsReadState, lhsIsRemovedFromTotalUnreadCount, lhsEmbeddedState, lhsPeer, lhsPresence, lhsInfo, lhsHasFailed, lhsIsContact): switch rhs { - case let .MessageEntry(rhsIndex, rhsMessage, rhsReadState, rhsSettings, rhsEmbeddedState, rhsPeer, rhsPresence, rhsInfo, rhsHasFailed, rhsIsContact): + case let .MessageEntry(rhsIndex, rhsMessage, rhsReadState, rhsIsRemovedFromTotalUnreadCount, rhsEmbeddedState, rhsPeer, rhsPresence, rhsInfo, rhsHasFailed, rhsIsContact): if lhsIndex != rhsIndex { return false } @@ -114,11 +114,7 @@ public enum ChatListEntry: Comparable { if lhsMessage?.stableVersion != rhsMessage?.stableVersion { return false } - if let lhsSettings = lhsSettings, let rhsSettings = rhsSettings { - if !lhsSettings.isEqual(to: rhsSettings) { - return false - } - } else if (lhsSettings != nil) != (rhsSettings != nil) { + if lhsIsRemovedFromTotalUnreadCount != rhsIsRemovedFromTotalUnreadCount { return false } if let lhsEmbeddedState = lhsEmbeddedState, let rhsEmbeddedState = rhsEmbeddedState { @@ -184,7 +180,7 @@ public enum ChatListEntry: Comparable { enum MutableChatListEntry: Equatable { case IntermediateMessageEntry(index: ChatListIndex, messageIndex: MessageIndex?) - case MessageEntry(index: ChatListIndex, message: Message?, readState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, embeddedInterfaceState: PeerChatListEmbeddedInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, tagSummaryInfo: ChatListMessageTagSummaryInfo, hasFailedMessages: Bool, isContact: Bool) + case MessageEntry(index: ChatListIndex, message: Message?, readState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: PeerChatListEmbeddedInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, tagSummaryInfo: ChatListMessageTagSummaryInfo, hasFailedMessages: Bool, isContact: Bool) case HoleEntry(ChatListHole) init(_ intermediateEntry: ChatListIntermediateEntry, cachedDataTable: CachedPeerDataTable, readStateTable: MessageHistoryReadStateTable, messageHistoryTable: MessageHistoryTable) { @@ -200,7 +196,7 @@ enum MutableChatListEntry: Equatable { switch self { case let .IntermediateMessageEntry(intermediateMessageEntry): return intermediateMessageEntry.index - case let .MessageEntry(index, _, _, _, _, _, _, _, _, _): + case let .MessageEntry(index, _, _, _, _, _, _, _, _, _, _): return index case let .HoleEntry(hole): return ChatListIndex(pinningIndex: nil, messageIndex: hole.index) @@ -258,16 +254,16 @@ public struct ChatListFilterPredicate { public var includePeerIds: Set public var excludePeerIds: Set public var includeAdditionalPeerGroupIds: [PeerGroupId] - public var include: (Peer, PeerNotificationSettings?, Bool, Bool) -> Bool + public var include: (Peer, Bool, Bool, Bool) -> Bool - public init(includePeerIds: Set, excludePeerIds: Set, includeAdditionalPeerGroupIds: [PeerGroupId], include: @escaping (Peer, PeerNotificationSettings?, Bool, Bool) -> Bool) { + public init(includePeerIds: Set, excludePeerIds: Set, includeAdditionalPeerGroupIds: [PeerGroupId], include: @escaping (Peer, Bool, Bool, Bool) -> Bool) { self.includePeerIds = includePeerIds self.excludePeerIds = excludePeerIds self.includeAdditionalPeerGroupIds = includeAdditionalPeerGroupIds self.include = include } - func includes(peer: Peer, groupId: PeerGroupId, notificationSettings: PeerNotificationSettings?, isUnread: Bool, isContact: Bool) -> Bool { + func includes(peer: Peer, groupId: PeerGroupId, isRemovedFromTotalUnreadCount: Bool, isUnread: Bool, isContact: Bool) -> Bool { let includePeerId = peer.associatedPeerId ?? peer.id if self.excludePeerIds.contains(includePeerId) { return false @@ -280,7 +276,7 @@ public struct ChatListFilterPredicate { return false } } - return self.include(peer, notificationSettings, isUnread, isContact) + return self.include(peer, isRemovedFromTotalUnreadCount, isUnread, isContact) } } @@ -620,8 +616,8 @@ public final class ChatListView { var entries: [ChatListEntry] = [] for entry in mutableView.sampledState.entries { switch entry { - case let .MessageEntry(index, message, combinedReadState, notificationSettings, embeddedState, peer, peerPresence, summaryInfo, hasFailed, isContact): - entries.append(.MessageEntry(index, message, combinedReadState, notificationSettings, embeddedState, peer, peerPresence, summaryInfo, hasFailed, isContact)) + case let .MessageEntry(index, message, combinedReadState, _, isRemovedFromTotalUnreadCount, embeddedState, peer, peerPresence, summaryInfo, hasFailed, isContact): + entries.append(.MessageEntry(index: index, message: message, readState: combinedReadState, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: embeddedState, renderedPeer: peer, presence: peerPresence, summaryInfo: summaryInfo, hasFailed: hasFailed, isContact: isContact)) case let .HoleEntry(hole): entries.append(.HoleEntry(hole)) case .IntermediateMessageEntry: diff --git a/submodules/Postbox/Sources/ChatListViewState.swift b/submodules/Postbox/Sources/ChatListViewState.swift index 319343ebbb..9cd1c443ea 100644 --- a/submodules/Postbox/Sources/ChatListViewState.swift +++ b/submodules/Postbox/Sources/ChatListViewState.swift @@ -362,7 +362,7 @@ private final class ChatListViewSpaceState { if let peer = messageEntry.renderedPeer.peer { let notificationsPeerId = peer.notificationSettingsPeerId ?? peer.id if let (_, updated) = transaction.currentUpdatedPeerNotificationSettings[notificationsPeerId] { - return .MessageEntry(index: messageEntry.index, message: messageEntry.message, readState: messageEntry.readState, notificationSettings: updated, embeddedInterfaceState: messageEntry.embeddedInterfaceState, renderedPeer: messageEntry.renderedPeer, presence: messageEntry.presence, tagSummaryInfo: messageEntry.tagSummaryInfo, hasFailedMessages: messageEntry.hasFailedMessages, isContact: messageEntry.isContact) + return .MessageEntry(index: messageEntry.index, message: messageEntry.message, readState: messageEntry.readState, notificationSettings: updated, isRemovedFromTotalUnreadCount: messageEntry.isRemovedFromTotalUnreadCount, embeddedInterfaceState: messageEntry.embeddedInterfaceState, renderedPeer: messageEntry.renderedPeer, presence: messageEntry.presence, tagSummaryInfo: messageEntry.tagSummaryInfo, hasFailedMessages: messageEntry.hasFailedMessages, isContact: messageEntry.isContact) } else { return nil } @@ -382,7 +382,7 @@ private final class ChatListViewSpaceState { switch entry { case let .MessageEntry(messageEntry): if transaction.updatedFailedMessagePeerIds.contains(messageEntry.index.messageIndex.id.peerId) { - return .MessageEntry(index: messageEntry.index, message: messageEntry.message, readState: messageEntry.readState, notificationSettings: messageEntry.notificationSettings, embeddedInterfaceState: messageEntry.embeddedInterfaceState, renderedPeer: messageEntry.renderedPeer, presence: messageEntry.presence, tagSummaryInfo: messageEntry.tagSummaryInfo, hasFailedMessages: postbox.messageHistoryFailedTable.contains(peerId: messageEntry.index.messageIndex.id.peerId), isContact: messageEntry.isContact) + return .MessageEntry(index: messageEntry.index, message: messageEntry.message, readState: messageEntry.readState, notificationSettings: messageEntry.notificationSettings, isRemovedFromTotalUnreadCount: messageEntry.isRemovedFromTotalUnreadCount, embeddedInterfaceState: messageEntry.embeddedInterfaceState, renderedPeer: messageEntry.renderedPeer, presence: messageEntry.presence, tagSummaryInfo: messageEntry.tagSummaryInfo, hasFailedMessages: postbox.messageHistoryFailedTable.contains(peerId: messageEntry.index.messageIndex.id.peerId), isContact: messageEntry.isContact) } else { return nil } @@ -405,7 +405,7 @@ private final class ChatListViewSpaceState { let renderedPeer = updatedRenderedPeer(messageEntry.renderedPeer, updatedPeers: transaction.currentUpdatedPeers) if updatedMessage != nil || renderedPeer != nil { - return .MessageEntry(index: messageEntry.index, message: updatedMessage ?? messageEntry.message, readState: messageEntry.readState, notificationSettings: messageEntry.notificationSettings, embeddedInterfaceState: messageEntry.embeddedInterfaceState, renderedPeer: renderedPeer ?? messageEntry.renderedPeer, presence: messageEntry.presence, tagSummaryInfo: messageEntry.tagSummaryInfo, hasFailedMessages: messageEntry.hasFailedMessages, isContact: messageEntry.isContact) + return .MessageEntry(index: messageEntry.index, message: updatedMessage ?? messageEntry.message, readState: messageEntry.readState, notificationSettings: messageEntry.notificationSettings, isRemovedFromTotalUnreadCount: messageEntry.isRemovedFromTotalUnreadCount, embeddedInterfaceState: messageEntry.embeddedInterfaceState, renderedPeer: renderedPeer ?? messageEntry.renderedPeer, presence: messageEntry.presence, tagSummaryInfo: messageEntry.tagSummaryInfo, hasFailedMessages: messageEntry.hasFailedMessages, isContact: messageEntry.isContact) } else { return nil } @@ -426,7 +426,7 @@ private final class ChatListViewSpaceState { presencePeerId = associatedPeerId } if let presence = transaction.currentUpdatedPeerPresences[presencePeerId] { - return .MessageEntry(index: messageEntry.index, message: messageEntry.message, readState: messageEntry.readState, notificationSettings: messageEntry.notificationSettings, embeddedInterfaceState: messageEntry.embeddedInterfaceState, renderedPeer: messageEntry.renderedPeer, presence: presence, tagSummaryInfo: messageEntry.tagSummaryInfo, hasFailedMessages: messageEntry.hasFailedMessages, isContact: messageEntry.isContact) + return .MessageEntry(index: messageEntry.index, message: messageEntry.message, readState: messageEntry.readState, notificationSettings: messageEntry.notificationSettings, isRemovedFromTotalUnreadCount: messageEntry.isRemovedFromTotalUnreadCount, embeddedInterfaceState: messageEntry.embeddedInterfaceState, renderedPeer: messageEntry.renderedPeer, presence: presence, tagSummaryInfo: messageEntry.tagSummaryInfo, hasFailedMessages: messageEntry.hasFailedMessages, isContact: messageEntry.isContact) } else { return nil } @@ -462,7 +462,7 @@ private final class ChatListViewSpaceState { if updatedTagSummaryCount != nil || updatedActionsSummaryCount != nil { let summaryInfo = ChatListMessageTagSummaryInfo(tagSummaryCount: updatedTagSummaryCount ?? messageEntry.tagSummaryInfo.tagSummaryCount, actionsSummaryCount: updatedActionsSummaryCount ?? messageEntry.tagSummaryInfo.actionsSummaryCount) - return .MessageEntry(index: messageEntry.index, message: messageEntry.message, readState: messageEntry.readState, notificationSettings: messageEntry.notificationSettings, embeddedInterfaceState: messageEntry.embeddedInterfaceState, renderedPeer: messageEntry.renderedPeer, presence: messageEntry.presence, tagSummaryInfo: summaryInfo, hasFailedMessages: messageEntry.hasFailedMessages, isContact: messageEntry.isContact) + return .MessageEntry(index: messageEntry.index, message: messageEntry.message, readState: messageEntry.readState, notificationSettings: messageEntry.notificationSettings, isRemovedFromTotalUnreadCount: messageEntry.isRemovedFromTotalUnreadCount, embeddedInterfaceState: messageEntry.embeddedInterfaceState, renderedPeer: messageEntry.renderedPeer, presence: messageEntry.presence, tagSummaryInfo: summaryInfo, hasFailedMessages: messageEntry.hasFailedMessages, isContact: messageEntry.isContact) } else { return nil } @@ -958,7 +958,17 @@ struct ChatListViewState { let tagSummaryInfo = ChatListMessageTagSummaryInfo(tagSummaryCount: tagSummaryCount, actionsSummaryCount: actionsSummaryCount) - let updatedEntry: MutableChatListEntry = .MessageEntry(index: index, message: messageIndex.flatMap(postbox.messageHistoryTable.getMessage).flatMap(postbox.renderIntermediateMessage), readState: postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId), notificationSettings: postbox.peerNotificationSettingsTable.getEffective(notificationsPeerId), embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId)?.chatListEmbeddedState, renderedPeer: renderedPeer, presence: postbox.peerPresenceTable.get(index.messageIndex.id.peerId), tagSummaryInfo: tagSummaryInfo, hasFailedMessages: false, isContact: postbox.contactsTable.isContact(peerId: index.messageIndex.id.peerId)) + var isRemovedFromTotalUnreadCount: Bool = false + let notificationSettings = postbox.peerNotificationSettingsTable.getEffective(notificationsPeerId) + if let peer = renderedPeer.peers[notificationsPeerId] { + if let notificationSettings = notificationSettings { + isRemovedFromTotalUnreadCount = notificationSettings.isRemovedFromTotalUnreadCount(default: !postbox.getGlobalNotificationSettings().defaultIncludePeer(peer: peer)) + } else { + isRemovedFromTotalUnreadCount = !postbox.getGlobalNotificationSettings().defaultIncludePeer(peer: peer) + } + } + + let updatedEntry: MutableChatListEntry = .MessageEntry(index: index, message: messageIndex.flatMap(postbox.messageHistoryTable.getMessage).flatMap(postbox.renderIntermediateMessage), readState: postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId), notificationSettings: notificationSettings, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId)?.chatListEmbeddedState, renderedPeer: renderedPeer, presence: postbox.peerPresenceTable.get(index.messageIndex.id.peerId), tagSummaryInfo: tagSummaryInfo, hasFailedMessages: false, isContact: postbox.contactsTable.isContact(peerId: index.messageIndex.id.peerId)) if directionIndex == 0 { self.stateBySpace[space]!.orderedEntries.setLowerOrAtAnchorAtArrayIndex(listIndex, to: updatedEntry) } else { diff --git a/submodules/Postbox/Sources/MessageHistoryMetadataTable.swift b/submodules/Postbox/Sources/MessageHistoryMetadataTable.swift index 7032f2bce6..34d3d3ca10 100644 --- a/submodules/Postbox/Sources/MessageHistoryMetadataTable.swift +++ b/submodules/Postbox/Sources/MessageHistoryMetadataTable.swift @@ -11,6 +11,7 @@ private enum MetadataPrefix: Int8 { case ShouldReindexUnreadCounts = 8 case PeerHistoryInitialized = 9 case ShouldReindexUnreadCountsState = 10 + case TotalUnreadCountStates = 11 } public struct ChatListTotalUnreadCounters: PostboxCoding, Equatable { @@ -58,8 +59,8 @@ final class MessageHistoryMetadataTable: Table { private var nextMessageStableId: UInt32? private var nextMessageStableIdUpdated = false - private var chatListTotalUnreadState: ChatListTotalUnreadState? - private var chatListTotalUnreadStateUpdated = false + private var chatListTotalUnreadStates: [PeerGroupId: ChatListTotalUnreadState] = [:] + private var updatedChatListTotalUnreadStates = Set() private var nextPeerOperationLogIndex: UInt32? private var nextPeerOperationLogIndexUpdated = false @@ -99,6 +100,23 @@ final class MessageHistoryMetadataTable: Table { return key } + private func totalUnreadCountStateKey(groupId: PeerGroupId) -> ValueBoxKey { + let key = ValueBoxKey(length: 1 + 4) + key.setInt8(0, value: MetadataPrefix.TotalUnreadCountStates.rawValue) + key.setInt32(1, value: groupId.rawValue) + return key + } + + private func totalUnreadCountLowerBound() -> ValueBoxKey { + let key = ValueBoxKey(length: 1) + key.setInt8(0, value: MetadataPrefix.TotalUnreadCountStates.rawValue) + return key + } + + private func totalUnreadCountUpperBound() -> ValueBoxKey { + return self.totalUnreadCountLowerBound().successor + } + func setInitializedChatList(groupId: PeerGroupId) { switch groupId { case .root: @@ -283,28 +301,38 @@ final class MessageHistoryMetadataTable: Table { } } - func getChatListTotalUnreadState() -> ChatListTotalUnreadState { - if let cached = self.chatListTotalUnreadState { + func removeAllTotalUnreadStates() { + var groupIds: [PeerGroupId] = [] + self.valueBox.range(self.table, start: self.totalUnreadCountLowerBound(), end: self.totalUnreadCountUpperBound(), keys: { key in + let groupId = key.getInt32(1) + groupIds.append(PeerGroupId(rawValue: groupId)) + return true + }, limit: 0) + for groupId in groupIds { + self.setTotalUnreadState(groupId: groupId, state: ChatListTotalUnreadState(absoluteCounters: [:], filteredCounters: [:])) + } + } + + func getTotalUnreadState(groupId: PeerGroupId) -> ChatListTotalUnreadState { + if let cached = self.chatListTotalUnreadStates[groupId] { return cached } else { - if let value = self.valueBox.get(self.table, key: self.key(.ChatListTotalUnreadState)), let state = PostboxDecoder(buffer: value).decodeObjectForKey("_", decoder: { - ChatListTotalUnreadState(decoder: $0) - }) as? ChatListTotalUnreadState { - self.chatListTotalUnreadState = state + if let value = self.valueBox.get(self.table, key: self.totalUnreadCountStateKey(groupId: groupId)), let state = PostboxDecoder(buffer: value).decodeObjectForKey("_", decoder: { ChatListTotalUnreadState(decoder: $0) }) as? ChatListTotalUnreadState { + self.chatListTotalUnreadStates[groupId] = state return state } else { let state = ChatListTotalUnreadState(absoluteCounters: [:], filteredCounters: [:]) - self.chatListTotalUnreadState = state + self.chatListTotalUnreadStates[groupId] = state return state } } } - func setChatListTotalUnreadState(_ state: ChatListTotalUnreadState) { - let current = self.getChatListTotalUnreadState() + func setTotalUnreadState(groupId: PeerGroupId, state: ChatListTotalUnreadState) { + let current = self.getTotalUnreadState(groupId: groupId) if current != state { - self.chatListTotalUnreadState = state - self.chatListTotalUnreadStateUpdated = true + self.chatListTotalUnreadStates[groupId] = state + self.updatedChatListTotalUnreadStates.insert(groupId) } } @@ -315,8 +343,8 @@ final class MessageHistoryMetadataTable: Table { self.updatedPeerNextMessageIdByNamespace.removeAll() self.nextMessageStableId = nil self.nextMessageStableIdUpdated = false - self.chatListTotalUnreadState = nil - self.chatListTotalUnreadStateUpdated = false + self.chatListTotalUnreadStates.removeAll() + self.updatedChatListTotalUnreadStates.removeAll() } override func beforeCommit() { @@ -351,13 +379,13 @@ final class MessageHistoryMetadataTable: Table { } } - if self.chatListTotalUnreadStateUpdated { - if let state = self.chatListTotalUnreadState { + for groupId in self.updatedChatListTotalUnreadStates { + if let state = self.chatListTotalUnreadStates[groupId] { let buffer = PostboxEncoder() buffer.encodeObject(state, forKey: "_") - self.valueBox.set(self.table, key: self.key(.ChatListTotalUnreadState), value: buffer.readBufferNoCopy()) + self.valueBox.set(self.table, key: self.totalUnreadCountStateKey(groupId: groupId), value: buffer.readBufferNoCopy()) } - self.chatListTotalUnreadStateUpdated = false + self.updatedChatListTotalUnreadStates.removeAll() } } } diff --git a/submodules/Postbox/Sources/PeerNotificationSettings.swift b/submodules/Postbox/Sources/PeerNotificationSettings.swift index f5ef31781d..25557657a3 100644 --- a/submodules/Postbox/Sources/PeerNotificationSettings.swift +++ b/submodules/Postbox/Sources/PeerNotificationSettings.swift @@ -33,8 +33,14 @@ public enum PeerNotificationSettingsBehavior: PostboxCoding { } public protocol PeerNotificationSettings: PostboxCoding { - var isRemovedFromTotalUnreadCount: Bool { get } + func isRemovedFromTotalUnreadCount(`default`: Bool) -> Bool var behavior: PeerNotificationSettingsBehavior { get } func isEqual(to: PeerNotificationSettings) -> Bool } + +public protocol PostboxGlobalNotificationSettings: PostboxCoding { + func defaultIncludePeer(peer: Peer) -> Bool + + func isEqualInDefaultPeerInclusion(other: PostboxGlobalNotificationSettings) -> Bool +} diff --git a/submodules/Postbox/Sources/PeerNotificationSettingsTable.swift b/submodules/Postbox/Sources/PeerNotificationSettingsTable.swift index 7fea4ea0d4..06d6312e98 100644 --- a/submodules/Postbox/Sources/PeerNotificationSettingsTable.swift +++ b/submodules/Postbox/Sources/PeerNotificationSettingsTable.swift @@ -199,19 +199,24 @@ final class PeerNotificationSettingsTable: Table { var added = Set() var removed = Set() + let globalNotificationSettings = postbox.getGlobalNotificationSettings() + for (peerId, initialSettings) in self.updatedInitialSettings { guard let peer = postbox.peerTable.get(peerId) else { continue } - var wasParticipating = false - let include = shouldPeerParticipateInUnreadCountStats(peer: peer) - - if include, let initialEffective = initialSettings.effective { - wasParticipating = !initialEffective.isRemovedFromTotalUnreadCount + let wasParticipating: Bool + if let initialEffective = initialSettings.effective { + wasParticipating = !initialEffective.isRemovedFromTotalUnreadCount(default: !globalNotificationSettings.defaultIncludePeer(peer: peer)) + } else { + wasParticipating = globalNotificationSettings.defaultIncludePeer(peer: peer) } - var isParticipating = false - if include, let resultEffective = self.cachedSettings[peerId]?.effective { - isParticipating = !resultEffective.isRemovedFromTotalUnreadCount + + let isParticipating: Bool + if let resultEffective = self.cachedSettings[peerId]?.effective { + isParticipating = !resultEffective.isRemovedFromTotalUnreadCount(default: !globalNotificationSettings.defaultIncludePeer(peer: peer)) + } else { + isParticipating = false } if wasParticipating != isParticipating { if isParticipating { diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index 0b7c6f2dab..c24d373fc2 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -212,6 +212,15 @@ public final class Transaction { self.postbox?.setPeerChatState(id, state: state) } + public func getGlobalNotificationSettings() -> PostboxGlobalNotificationSettings { + assert(!self.disposed) + if let postbox = self.postbox { + return postbox.getGlobalNotificationSettings() + } else { + preconditionFailure() + } + } + /*public func getPeerGroupState(_ id: PeerGroupId) -> PeerGroupState? { assert(!self.disposed) return self.postbox?.peerGroupStateTable.get(id) @@ -754,10 +763,10 @@ public final class Transaction { } } - public func getTotalUnreadState() -> ChatListTotalUnreadState { + public func getTotalUnreadState(groupId: PeerGroupId) -> ChatListTotalUnreadState { assert(!self.disposed) if let postbox = self.postbox { - return postbox.messageHistoryMetadataTable.getChatListTotalUnreadState() + return postbox.messageHistoryMetadataTable.getTotalUnreadState(groupId: groupId) } else { return ChatListTotalUnreadState(absoluteCounters: [:], filteredCounters: [:]) } @@ -1123,7 +1132,7 @@ public final class Postbox { private var currentUpdatedCachedPeerData: [PeerId: CachedPeerData] = [:] private var currentUpdatedPeerPresences: [PeerId: PeerPresence] = [:] private var currentUpdatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?] = [:] - private var currentUpdatedTotalUnreadState: ChatListTotalUnreadState? + private var currentUpdatedTotalUnreadStates: [PeerGroupId: ChatListTotalUnreadState] = [:] private var currentUpdatedGroupTotalUnreadSummaries: [PeerGroupId: PeerGroupUnreadCountersCombinedSummary] = [:] private var currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation] = [] private var currentTimestampBasedMessageAttributesOperations: [TimestampBasedMessageAttributesOperation] = [] @@ -1148,6 +1157,8 @@ public final class Postbox { private var currentUpdatedNoticeEntryKeys = Set() private var currentUpdatedCacheEntryKeys = Set() + private var currentNeedsReindexUnreadCounters: Bool = false + private let statePipe: ValuePipe = ValuePipe() private var masterClientId = Promise() @@ -1384,8 +1395,6 @@ public final class Postbox { return self.cachedPeerDataTable.get(peerId) }, getPeerPresence: { peerId in return self.peerPresenceTable.get(peerId) - }, getTotalUnreadState: { - return self.messageHistoryMetadataTable.getChatListTotalUnreadState() }, getPeerReadState: { peerId in return self.readStateTable.getCombinedState(peerId) }, operationLogGetOperations: { tag, fromIndex, limit in @@ -1415,9 +1424,12 @@ public final class Postbox { if self.messageHistoryMetadataTable.shouldReindexUnreadCounts() { self.groupMessageStatsTable.removeAll() let startTime = CFAbsoluteTimeGetCurrent() - let (rootState, summaries) = self.chatListIndexTable.debugReindexUnreadCounts(postbox: self) + let (totalStates, summaries) = self.chatListIndexTable.debugReindexUnreadCounts(postbox: self) - self.messageHistoryMetadataTable.setChatListTotalUnreadState(rootState) + self.messageHistoryMetadataTable.removeAllTotalUnreadStates() + for (groupId, state) in totalStates { + self.messageHistoryMetadataTable.setTotalUnreadState(groupId: groupId, state: state) + } for (groupId, summary) in summaries { self.groupMessageStatsTable.set(groupId: groupId, summary: summary) } @@ -1746,9 +1758,13 @@ public final class Postbox { } } let transactionParticipationInTotalUnreadCountUpdates = self.peerNotificationSettingsTable.transactionParticipationInTotalUnreadCountUpdates(postbox: self) - self.chatListIndexTable.commitWithTransaction(postbox: self, alteredInitialPeerCombinedReadStates: alteredInitialPeerCombinedReadStates, updatedPeers: updatedPeers, transactionParticipationInTotalUnreadCountUpdates: transactionParticipationInTotalUnreadCountUpdates, updatedRootUnreadState: &self.currentUpdatedTotalUnreadState, updatedGroupTotalUnreadSummaries: &self.currentUpdatedGroupTotalUnreadSummaries, currentUpdatedGroupSummarySynchronizeOperations: &self.currentUpdatedGroupSummarySynchronizeOperations) + self.chatListIndexTable.commitWithTransaction(postbox: self, alteredInitialPeerCombinedReadStates: alteredInitialPeerCombinedReadStates, updatedPeers: updatedPeers, transactionParticipationInTotalUnreadCountUpdates: transactionParticipationInTotalUnreadCountUpdates, updatedTotalUnreadStates: &self.currentUpdatedTotalUnreadStates, updatedGroupTotalUnreadSummaries: &self.currentUpdatedGroupTotalUnreadSummaries, currentUpdatedGroupSummarySynchronizeOperations: &self.currentUpdatedGroupSummarySynchronizeOperations) - let transaction = PostboxTransaction(currentUpdatedState: self.currentUpdatedState, currentPeerHoleOperations: self.currentPeerHoleOperations, currentOperationsByPeerId: self.currentOperationsByPeerId, chatListOperations: self.currentChatListOperations, currentUpdatedChatListInclusions: self.currentUpdatedChatListInclusions, currentUpdatedPeers: self.currentUpdatedPeers, currentUpdatedPeerNotificationSettings: self.currentUpdatedPeerNotificationSettings, currentUpdatedPeerNotificationBehaviorTimestamps: self.currentUpdatedPeerNotificationBehaviorTimestamps, currentUpdatedCachedPeerData: self.currentUpdatedCachedPeerData, currentUpdatedPeerPresences: currentUpdatedPeerPresences, currentUpdatedPeerChatListEmbeddedStates: self.currentUpdatedPeerChatListEmbeddedStates, currentUpdatedTotalUnreadState: self.currentUpdatedTotalUnreadState, currentUpdatedTotalUnreadSummaries: self.currentUpdatedGroupTotalUnreadSummaries, alteredInitialPeerCombinedReadStates: alteredInitialPeerCombinedReadStates, currentPeerMergedOperationLogOperations: self.currentPeerMergedOperationLogOperations, currentTimestampBasedMessageAttributesOperations: self.currentTimestampBasedMessageAttributesOperations, unsentMessageOperations: self.currentUnsentOperations, updatedSynchronizePeerReadStateOperations: self.currentUpdatedSynchronizeReadStateOperations, currentUpdatedGroupSummarySynchronizeOperations: self.currentUpdatedGroupSummarySynchronizeOperations, currentPreferencesOperations: self.currentPreferencesOperations, currentOrderedItemListOperations: self.currentOrderedItemListOperations, currentItemCollectionItemsOperations: self.currentItemCollectionItemsOperations, currentItemCollectionInfosOperations: self.currentItemCollectionInfosOperations, currentUpdatedPeerChatStates: self.currentUpdatedPeerChatStates, currentGlobalTagsOperations: self.currentGlobalTagsOperations, currentLocalTagsOperations: self.currentLocalTagsOperations, updatedMedia: self.currentUpdatedMedia, replaceRemoteContactCount: self.currentReplaceRemoteContactCount, replaceContactPeerIds: self.currentReplacedContactPeerIds, currentPendingMessageActionsOperations: self.currentPendingMessageActionsOperations, currentUpdatedMessageActionsSummaries: self.currentUpdatedMessageActionsSummaries, currentUpdatedMessageTagSummaries: self.currentUpdatedMessageTagSummaries, currentInvalidateMessageTagSummaries: self.currentInvalidateMessageTagSummaries, currentUpdatedPendingPeerNotificationSettings: self.currentUpdatedPendingPeerNotificationSettings, replacedAdditionalChatListItems: self.currentReplacedAdditionalChatListItems, updatedNoticeEntryKeys: self.currentUpdatedNoticeEntryKeys, updatedCacheEntryKeys: self.currentUpdatedCacheEntryKeys, currentUpdatedMasterClientId: currentUpdatedMasterClientId, updatedFailedMessagePeerIds: self.messageHistoryFailedTable.updatedPeerIds, updatedFailedMessageIds: self.messageHistoryFailedTable.updatedMessageIds) + if self.currentNeedsReindexUnreadCounters { + self.reindexUnreadCounters() + } + + let transaction = PostboxTransaction(currentUpdatedState: self.currentUpdatedState, currentPeerHoleOperations: self.currentPeerHoleOperations, currentOperationsByPeerId: self.currentOperationsByPeerId, chatListOperations: self.currentChatListOperations, currentUpdatedChatListInclusions: self.currentUpdatedChatListInclusions, currentUpdatedPeers: self.currentUpdatedPeers, currentUpdatedPeerNotificationSettings: self.currentUpdatedPeerNotificationSettings, currentUpdatedPeerNotificationBehaviorTimestamps: self.currentUpdatedPeerNotificationBehaviorTimestamps, currentUpdatedCachedPeerData: self.currentUpdatedCachedPeerData, currentUpdatedPeerPresences: currentUpdatedPeerPresences, currentUpdatedPeerChatListEmbeddedStates: self.currentUpdatedPeerChatListEmbeddedStates, currentUpdatedTotalUnreadStates: self.currentUpdatedTotalUnreadStates, currentUpdatedTotalUnreadSummaries: self.currentUpdatedGroupTotalUnreadSummaries, alteredInitialPeerCombinedReadStates: alteredInitialPeerCombinedReadStates, currentPeerMergedOperationLogOperations: self.currentPeerMergedOperationLogOperations, currentTimestampBasedMessageAttributesOperations: self.currentTimestampBasedMessageAttributesOperations, unsentMessageOperations: self.currentUnsentOperations, updatedSynchronizePeerReadStateOperations: self.currentUpdatedSynchronizeReadStateOperations, currentUpdatedGroupSummarySynchronizeOperations: self.currentUpdatedGroupSummarySynchronizeOperations, currentPreferencesOperations: self.currentPreferencesOperations, currentOrderedItemListOperations: self.currentOrderedItemListOperations, currentItemCollectionItemsOperations: self.currentItemCollectionItemsOperations, currentItemCollectionInfosOperations: self.currentItemCollectionInfosOperations, currentUpdatedPeerChatStates: self.currentUpdatedPeerChatStates, currentGlobalTagsOperations: self.currentGlobalTagsOperations, currentLocalTagsOperations: self.currentLocalTagsOperations, updatedMedia: self.currentUpdatedMedia, replaceRemoteContactCount: self.currentReplaceRemoteContactCount, replaceContactPeerIds: self.currentReplacedContactPeerIds, currentPendingMessageActionsOperations: self.currentPendingMessageActionsOperations, currentUpdatedMessageActionsSummaries: self.currentUpdatedMessageActionsSummaries, currentUpdatedMessageTagSummaries: self.currentUpdatedMessageTagSummaries, currentInvalidateMessageTagSummaries: self.currentInvalidateMessageTagSummaries, currentUpdatedPendingPeerNotificationSettings: self.currentUpdatedPendingPeerNotificationSettings, replacedAdditionalChatListItems: self.currentReplacedAdditionalChatListItems, updatedNoticeEntryKeys: self.currentUpdatedNoticeEntryKeys, updatedCacheEntryKeys: self.currentUpdatedCacheEntryKeys, currentUpdatedMasterClientId: currentUpdatedMasterClientId, updatedFailedMessagePeerIds: self.messageHistoryFailedTable.updatedPeerIds, updatedFailedMessageIds: self.messageHistoryFailedTable.updatedMessageIds) var updatedTransactionState: Int64? var updatedMasterClientId: Int64? if !transaction.isEmpty { @@ -1785,7 +1801,7 @@ public final class Postbox { self.currentUpdatedCachedPeerData.removeAll() self.currentUpdatedPeerPresences.removeAll() self.currentUpdatedPeerChatListEmbeddedStates.removeAll() - self.currentUpdatedTotalUnreadState = nil + self.currentUpdatedTotalUnreadStates.removeAll() self.currentUpdatedGroupTotalUnreadSummaries.removeAll() self.currentPeerMergedOperationLogOperations.removeAll() self.currentTimestampBasedMessageAttributesOperations.removeAll() @@ -1800,6 +1816,7 @@ public final class Postbox { self.currentInvalidateMessageTagSummaries.removeAll() self.currentUpdatedPendingPeerNotificationSettings.removeAll() self.currentGroupIdsWithUpdatedReadStats.removeAll() + self.currentNeedsReindexUnreadCounters = false for table in self.tables { table.beforeCommit() @@ -2421,7 +2438,7 @@ public final class Postbox { case let .peerChatState(peerId): additionalDataEntries.append(.peerChatState(peerId, self.peerChatStateTable.get(peerId) as? PeerChatState)) case .totalUnreadState: - additionalDataEntries.append(.totalUnreadState(self.messageHistoryMetadataTable.getChatListTotalUnreadState())) + additionalDataEntries.append(.totalUnreadState(self.messageHistoryMetadataTable.getTotalUnreadState(groupId: .root))) case let .peerNotificationSettings(peerId): var notificationPeerId = peerId if let peer = self.peerTable.get(peerId), let associatedPeerId = peer.associatedPeerId { @@ -3063,6 +3080,14 @@ public final class Postbox { } fileprivate func setPreferencesEntry(key: ValueBoxKey, value: PreferencesEntry?) { + if key == self.seedConfiguration.globalNotificationSettingsPreferencesKey { + let current = self.getGlobalNotificationSettings() + let updated = value as? PostboxGlobalNotificationSettings ?? self.seedConfiguration.defaultGlobalNotificationSettings + if !current.isEqualInDefaultPeerInclusion(other: updated) { + self.currentNeedsReindexUnreadCounters = true + } + } + self.preferencesTable.set(key: key, value: value, operations: &self.currentPreferencesOperations) } @@ -3193,6 +3218,10 @@ public final class Postbox { } } + func getGlobalNotificationSettings() -> PostboxGlobalNotificationSettings { + return self.preferencesTable.get(key: self.seedConfiguration.globalNotificationSettingsPreferencesKey) as? PostboxGlobalNotificationSettings ?? self.seedConfiguration.defaultGlobalNotificationSettings + } + public func isMasterClient() -> Signal { return self.transaction { transaction -> Signal in let sessionClientId = self.sessionClientId @@ -3238,10 +3267,14 @@ public final class Postbox { fileprivate func reindexUnreadCounters() { self.groupMessageStatsTable.removeAll() let _ = CFAbsoluteTimeGetCurrent() - let (rootState, summaries) = self.chatListIndexTable.debugReindexUnreadCounts(postbox: self) + let (totalStates, summaries) = self.chatListIndexTable.debugReindexUnreadCounts(postbox: self) + + self.messageHistoryMetadataTable.removeAllTotalUnreadStates() + for (groupId, state) in totalStates { + self.messageHistoryMetadataTable.setTotalUnreadState(groupId: groupId, state: state) + } + self.currentUpdatedTotalUnreadStates = totalStates - self.messageHistoryMetadataTable.setChatListTotalUnreadState(rootState) - self.currentUpdatedTotalUnreadState = rootState for (groupId, summary) in summaries { self.groupMessageStatsTable.set(groupId: groupId, summary: summary) self.currentUpdatedGroupTotalUnreadSummaries[groupId] = summary diff --git a/submodules/Postbox/Sources/PostboxTransaction.swift b/submodules/Postbox/Sources/PostboxTransaction.swift index 207ed2a45a..b2b45d5658 100644 --- a/submodules/Postbox/Sources/PostboxTransaction.swift +++ b/submodules/Postbox/Sources/PostboxTransaction.swift @@ -12,7 +12,7 @@ final class PostboxTransaction { let currentUpdatedCachedPeerData: [PeerId: CachedPeerData] let currentUpdatedPeerPresences: [PeerId: PeerPresence] let currentUpdatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?] - let currentUpdatedTotalUnreadState: ChatListTotalUnreadState? + let currentUpdatedTotalUnreadStates: [PeerGroupId: ChatListTotalUnreadState] let currentUpdatedTotalUnreadSummaries: [PeerGroupId: PeerGroupUnreadCountersCombinedSummary] let alteredInitialPeerCombinedReadStates: [PeerId: CombinedPeerReadState] let currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation] @@ -100,7 +100,7 @@ final class PostboxTransaction { if currentUpdatedMasterClientId != nil { return false } - if currentUpdatedTotalUnreadState != nil { + if !currentUpdatedTotalUnreadStates.isEmpty { return false } if !currentUpdatedTotalUnreadSummaries.isEmpty { @@ -172,7 +172,7 @@ final class PostboxTransaction { return true } - init(currentUpdatedState: PostboxCoding?, currentPeerHoleOperations: [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]] = [:], currentOperationsByPeerId: [PeerId: [MessageHistoryOperation]], chatListOperations: [PeerGroupId: [ChatListOperation]], currentUpdatedChatListInclusions: [PeerId: PeerChatListInclusion], currentUpdatedPeers: [PeerId: Peer], currentUpdatedPeerNotificationSettings: [PeerId: (PeerNotificationSettings?, PeerNotificationSettings)], currentUpdatedPeerNotificationBehaviorTimestamps: [PeerId: PeerNotificationSettingsBehaviorTimestamp], currentUpdatedCachedPeerData: [PeerId: CachedPeerData], currentUpdatedPeerPresences: [PeerId: PeerPresence], currentUpdatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?], currentUpdatedTotalUnreadState: ChatListTotalUnreadState?, currentUpdatedTotalUnreadSummaries: [PeerGroupId: PeerGroupUnreadCountersCombinedSummary], alteredInitialPeerCombinedReadStates: [PeerId: CombinedPeerReadState], currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation], currentTimestampBasedMessageAttributesOperations: [TimestampBasedMessageAttributesOperation], unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation], updatedSynchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?], currentUpdatedGroupSummarySynchronizeOperations: [PeerGroupAndNamespace: Bool], currentPreferencesOperations: [PreferencesOperation], currentOrderedItemListOperations: [Int32: [OrderedItemListOperation]], currentItemCollectionItemsOperations: [ItemCollectionId: [ItemCollectionItemsOperation]], currentItemCollectionInfosOperations: [ItemCollectionInfosOperation], currentUpdatedPeerChatStates: Set, currentGlobalTagsOperations: [GlobalMessageHistoryTagsOperation], currentLocalTagsOperations: [IntermediateMessageHistoryLocalTagsOperation], updatedMedia: [MediaId: Media?], replaceRemoteContactCount: Int32?, replaceContactPeerIds: Set?, currentPendingMessageActionsOperations: [PendingMessageActionsOperation], currentUpdatedMessageActionsSummaries: [PendingMessageActionsSummaryKey: Int32], currentUpdatedMessageTagSummaries: [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], currentInvalidateMessageTagSummaries: [InvalidatedMessageHistoryTagsSummaryEntryOperation], currentUpdatedPendingPeerNotificationSettings: Set, replacedAdditionalChatListItems: [PeerId]?, updatedNoticeEntryKeys: Set, updatedCacheEntryKeys: Set, currentUpdatedMasterClientId: Int64?, updatedFailedMessagePeerIds: Set, updatedFailedMessageIds: Set) { + init(currentUpdatedState: PostboxCoding?, currentPeerHoleOperations: [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]] = [:], currentOperationsByPeerId: [PeerId: [MessageHistoryOperation]], chatListOperations: [PeerGroupId: [ChatListOperation]], currentUpdatedChatListInclusions: [PeerId: PeerChatListInclusion], currentUpdatedPeers: [PeerId: Peer], currentUpdatedPeerNotificationSettings: [PeerId: (PeerNotificationSettings?, PeerNotificationSettings)], currentUpdatedPeerNotificationBehaviorTimestamps: [PeerId: PeerNotificationSettingsBehaviorTimestamp], currentUpdatedCachedPeerData: [PeerId: CachedPeerData], currentUpdatedPeerPresences: [PeerId: PeerPresence], currentUpdatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?], currentUpdatedTotalUnreadStates: [PeerGroupId: ChatListTotalUnreadState], currentUpdatedTotalUnreadSummaries: [PeerGroupId: PeerGroupUnreadCountersCombinedSummary], alteredInitialPeerCombinedReadStates: [PeerId: CombinedPeerReadState], currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation], currentTimestampBasedMessageAttributesOperations: [TimestampBasedMessageAttributesOperation], unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation], updatedSynchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?], currentUpdatedGroupSummarySynchronizeOperations: [PeerGroupAndNamespace: Bool], currentPreferencesOperations: [PreferencesOperation], currentOrderedItemListOperations: [Int32: [OrderedItemListOperation]], currentItemCollectionItemsOperations: [ItemCollectionId: [ItemCollectionItemsOperation]], currentItemCollectionInfosOperations: [ItemCollectionInfosOperation], currentUpdatedPeerChatStates: Set, currentGlobalTagsOperations: [GlobalMessageHistoryTagsOperation], currentLocalTagsOperations: [IntermediateMessageHistoryLocalTagsOperation], updatedMedia: [MediaId: Media?], replaceRemoteContactCount: Int32?, replaceContactPeerIds: Set?, currentPendingMessageActionsOperations: [PendingMessageActionsOperation], currentUpdatedMessageActionsSummaries: [PendingMessageActionsSummaryKey: Int32], currentUpdatedMessageTagSummaries: [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], currentInvalidateMessageTagSummaries: [InvalidatedMessageHistoryTagsSummaryEntryOperation], currentUpdatedPendingPeerNotificationSettings: Set, replacedAdditionalChatListItems: [PeerId]?, updatedNoticeEntryKeys: Set, updatedCacheEntryKeys: Set, currentUpdatedMasterClientId: Int64?, updatedFailedMessagePeerIds: Set, updatedFailedMessageIds: Set) { self.currentUpdatedState = currentUpdatedState self.currentPeerHoleOperations = currentPeerHoleOperations self.currentOperationsByPeerId = currentOperationsByPeerId @@ -184,7 +184,7 @@ final class PostboxTransaction { self.currentUpdatedCachedPeerData = currentUpdatedCachedPeerData self.currentUpdatedPeerPresences = currentUpdatedPeerPresences self.currentUpdatedPeerChatListEmbeddedStates = currentUpdatedPeerChatListEmbeddedStates - self.currentUpdatedTotalUnreadState = currentUpdatedTotalUnreadState + self.currentUpdatedTotalUnreadStates = currentUpdatedTotalUnreadStates self.currentUpdatedTotalUnreadSummaries = currentUpdatedTotalUnreadSummaries self.alteredInitialPeerCombinedReadStates = alteredInitialPeerCombinedReadStates self.currentPeerMergedOperationLogOperations = currentPeerMergedOperationLogOperations diff --git a/submodules/Postbox/Sources/PostboxUpgrade_15to16.swift b/submodules/Postbox/Sources/PostboxUpgrade_15to16.swift index d3a6a95905..62a4714b43 100644 --- a/submodules/Postbox/Sources/PostboxUpgrade_15to16.swift +++ b/submodules/Postbox/Sources/PostboxUpgrade_15to16.swift @@ -137,9 +137,9 @@ private func parseNotificationSettings(valueBox: ValueBox, table: ValueBoxTable, value.skip(Int(length)) } if let pending = pending { - return !pending.isRemovedFromTotalUnreadCount + return !pending.isRemovedFromTotalUnreadCount(default: false) } else if let current = current { - return !current.isRemovedFromTotalUnreadCount + return !current.isRemovedFromTotalUnreadCount(default: false) } else { return false } @@ -154,12 +154,10 @@ private func getReadStateCount(valueBox: ValueBox, table: ValueBoxTable, peerId: if let value = valueBox.get(table, key: key) { var count: Int32 = 0 value.read(&count, offset: 0, length: 4) - var stateByNamespace: [MessageId.Namespace: PeerReadState] = [:] for _ in 0 ..< count { var namespaceId: Int32 = 0 value.read(&namespaceId, offset: 0, length: 4) - let state: PeerReadState var kind: Int8 = 0 value.read(&kind, offset: 0, length: 1) if kind == 0 { diff --git a/submodules/Postbox/Sources/PostboxUpgrade_16to17.swift b/submodules/Postbox/Sources/PostboxUpgrade_16to17.swift index 120b40a155..3bdbfea670 100644 --- a/submodules/Postbox/Sources/PostboxUpgrade_16to17.swift +++ b/submodules/Postbox/Sources/PostboxUpgrade_16to17.swift @@ -137,9 +137,9 @@ private func parseNotificationSettings(valueBox: ValueBox, table: ValueBoxTable, value.skip(Int(length)) } if let pending = pending { - return !pending.isRemovedFromTotalUnreadCount + return !pending.isRemovedFromTotalUnreadCount(default: false) } else if let current = current { - return !current.isRemovedFromTotalUnreadCount + return !current.isRemovedFromTotalUnreadCount(default: false) } else { return false } diff --git a/submodules/Postbox/Sources/SeedConfiguration.swift b/submodules/Postbox/Sources/SeedConfiguration.swift index 2ae0cfc1d7..a0eb801070 100644 --- a/submodules/Postbox/Sources/SeedConfiguration.swift +++ b/submodules/Postbox/Sources/SeedConfiguration.swift @@ -22,8 +22,10 @@ public final class SeedConfiguration { public let messageNamespacesRequiringGroupStatsValidation: Set public let defaultMessageNamespaceReadStates: [MessageId.Namespace: PeerReadState] public let chatMessagesNamespaces: Set + public let globalNotificationSettingsPreferencesKey: ValueBoxKey + public let defaultGlobalNotificationSettings: PostboxGlobalNotificationSettings - public init(globalMessageIdsPeerIdNamespaces: Set, initializeChatListWithHole: (topLevel: ChatListHole?, groups: ChatListHole?), messageHoles: [PeerId.Namespace: [MessageId.Namespace: Set]], existingMessageTags: MessageTags, messageTagsWithSummary: MessageTags, existingGlobalMessageTags: GlobalMessageTags, peerNamespacesRequiringMessageTextIndex: [PeerId.Namespace], peerSummaryCounterTags: @escaping (Peer, Bool) -> PeerSummaryCounterTags, additionalChatListIndexNamespace: MessageId.Namespace?, messageNamespacesRequiringGroupStatsValidation: Set, defaultMessageNamespaceReadStates: [MessageId.Namespace: PeerReadState], chatMessagesNamespaces: Set) { + public init(globalMessageIdsPeerIdNamespaces: Set, initializeChatListWithHole: (topLevel: ChatListHole?, groups: ChatListHole?), messageHoles: [PeerId.Namespace: [MessageId.Namespace: Set]], existingMessageTags: MessageTags, messageTagsWithSummary: MessageTags, existingGlobalMessageTags: GlobalMessageTags, peerNamespacesRequiringMessageTextIndex: [PeerId.Namespace], peerSummaryCounterTags: @escaping (Peer, Bool) -> PeerSummaryCounterTags, additionalChatListIndexNamespace: MessageId.Namespace?, messageNamespacesRequiringGroupStatsValidation: Set, defaultMessageNamespaceReadStates: [MessageId.Namespace: PeerReadState], chatMessagesNamespaces: Set, globalNotificationSettingsPreferencesKey: ValueBoxKey, defaultGlobalNotificationSettings: PostboxGlobalNotificationSettings) { self.globalMessageIdsPeerIdNamespaces = globalMessageIdsPeerIdNamespaces self.initializeChatListWithHole = initializeChatListWithHole self.messageHoles = messageHoles @@ -35,5 +37,7 @@ public final class SeedConfiguration { self.messageNamespacesRequiringGroupStatsValidation = messageNamespacesRequiringGroupStatsValidation self.defaultMessageNamespaceReadStates = defaultMessageNamespaceReadStates self.chatMessagesNamespaces = chatMessagesNamespaces + self.globalNotificationSettingsPreferencesKey = globalNotificationSettingsPreferencesKey + self.defaultGlobalNotificationSettings = defaultGlobalNotificationSettings } } diff --git a/submodules/Postbox/Sources/UnreadMessageCountsView.swift b/submodules/Postbox/Sources/UnreadMessageCountsView.swift index 7246860341..f0d8bf360e 100644 --- a/submodules/Postbox/Sources/UnreadMessageCountsView.swift +++ b/submodules/Postbox/Sources/UnreadMessageCountsView.swift @@ -25,9 +25,9 @@ final class MutableUnreadMessageCountsView: MutablePostboxView { self.entries = items.map { item in switch item { case let .total(preferencesKey): - return .total(preferencesKey.flatMap({ ($0, postbox.preferencesTable.get(key: $0)) }), postbox.messageHistoryMetadataTable.getChatListTotalUnreadState()) + return .total(preferencesKey.flatMap({ ($0, postbox.preferencesTable.get(key: $0)) }), postbox.messageHistoryMetadataTable.getTotalUnreadState(groupId: .root)) case let .totalInGroup(groupId): - return .totalInGroup(groupId, ChatListTotalUnreadState(absoluteCounters: [:], filteredCounters: [:])) + return .totalInGroup(groupId, postbox.messageHistoryMetadataTable.getTotalUnreadState(groupId: groupId)) case let .peer(peerId): return .peer(peerId, postbox.readStateTable.getCombinedState(peerId)) } @@ -52,18 +52,23 @@ final class MutableUnreadMessageCountsView: MutablePostboxView { } } - if transaction.currentUpdatedTotalUnreadState != nil || !transaction.alteredInitialPeerCombinedReadStates.isEmpty || updatedPreferencesEntry != nil { + if !transaction.currentUpdatedTotalUnreadStates.isEmpty || !transaction.alteredInitialPeerCombinedReadStates.isEmpty || updatedPreferencesEntry != nil { for i in 0 ..< self.entries.count { switch self.entries[i] { case let .total(keyAndEntry, state): - if let updatedState = transaction.currentUpdatedTotalUnreadState { + if let updatedState = transaction.currentUpdatedTotalUnreadStates[.root] { if updatedState != state { self.entries[i] = .total(keyAndEntry.flatMap({ ($0.0, updatedPreferencesEntry ?? $0.1) }), updatedState) updated = true } } case let .totalInGroup(groupId, state): - break + if let updatedState = transaction.currentUpdatedTotalUnreadStates[groupId] { + if updatedState != state { + self.entries[i] = .totalInGroup(groupId, updatedState) + updated = true + } + } case let .peer(peerId, _): if transaction.alteredInitialPeerCombinedReadStates[peerId] != nil { self.entries[i] = .peer(peerId, postbox.readStateTable.getCombinedState(peerId)) diff --git a/submodules/Postbox/Sources/ViewTracker.swift b/submodules/Postbox/Sources/ViewTracker.swift index 408fd8d842..1b92f90044 100644 --- a/submodules/Postbox/Sources/ViewTracker.swift +++ b/submodules/Postbox/Sources/ViewTracker.swift @@ -16,7 +16,6 @@ final class ViewTracker { private let getPeerNotificationSettings: (PeerId) -> PeerNotificationSettings? private let getCachedPeerData: (PeerId) -> CachedPeerData? private let getPeerPresence: (PeerId) -> PeerPresence? - private let getTotalUnreadState: () -> ChatListTotalUnreadState private let getPeerReadState: (PeerId) -> CombinedPeerReadState? private let operationLogGetOperations: (PeerOperationLogTag, Int32, Int) -> [PeerMergedOperationLogEntry] private let operationLogGetTailIndex: (PeerOperationLogTag) -> Int32? @@ -59,14 +58,13 @@ final class ViewTracker { private var failedMessageIdsViews = Bag<(MutableFailedMessageIdsView, ValuePipe)>() - init(queue: Queue, renderMessage: @escaping (IntermediateMessage) -> Message, getPeer: @escaping (PeerId) -> Peer?, getPeerNotificationSettings: @escaping (PeerId) -> PeerNotificationSettings?, getCachedPeerData: @escaping (PeerId) -> CachedPeerData?, getPeerPresence: @escaping (PeerId) -> PeerPresence?, getTotalUnreadState: @escaping () -> ChatListTotalUnreadState, getPeerReadState: @escaping (PeerId) -> CombinedPeerReadState?, operationLogGetOperations: @escaping (PeerOperationLogTag, Int32, Int) -> [PeerMergedOperationLogEntry], operationLogGetTailIndex: @escaping (PeerOperationLogTag) -> Int32?, getTimestampBasedMessageAttributesHead: @escaping (UInt16) -> TimestampBasedMessageAttributesEntry?, getPreferencesEntry: @escaping (ValueBoxKey) -> PreferencesEntry?, unsentMessageIds: [MessageId], synchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation]) { + init(queue: Queue, renderMessage: @escaping (IntermediateMessage) -> Message, getPeer: @escaping (PeerId) -> Peer?, getPeerNotificationSettings: @escaping (PeerId) -> PeerNotificationSettings?, getCachedPeerData: @escaping (PeerId) -> CachedPeerData?, getPeerPresence: @escaping (PeerId) -> PeerPresence?, getPeerReadState: @escaping (PeerId) -> CombinedPeerReadState?, operationLogGetOperations: @escaping (PeerOperationLogTag, Int32, Int) -> [PeerMergedOperationLogEntry], operationLogGetTailIndex: @escaping (PeerOperationLogTag) -> Int32?, getTimestampBasedMessageAttributesHead: @escaping (UInt16) -> TimestampBasedMessageAttributesEntry?, getPreferencesEntry: @escaping (ValueBoxKey) -> PreferencesEntry?, unsentMessageIds: [MessageId], synchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation]) { self.queue = queue self.renderMessage = renderMessage self.getPeer = getPeer self.getPeerNotificationSettings = getPeerNotificationSettings self.getCachedPeerData = getCachedPeerData self.getPeerPresence = getPeerPresence - self.getTotalUnreadState = getTotalUnreadState self.getPeerReadState = getPeerReadState self.operationLogGetOperations = operationLogGetOperations self.operationLogGetTailIndex = operationLogGetTailIndex diff --git a/submodules/SyncCore/Sources/GlobalNotificationSettings.swift b/submodules/SyncCore/Sources/GlobalNotificationSettings.swift index 2a368bf52a..fb162cad7b 100644 --- a/submodules/SyncCore/Sources/GlobalNotificationSettings.swift +++ b/submodules/SyncCore/Sources/GlobalNotificationSettings.swift @@ -60,7 +60,7 @@ public struct GlobalNotificationSettingsSet: PostboxCoding, Equatable { } } -public struct GlobalNotificationSettings: PreferencesEntry, Equatable { +public struct GlobalNotificationSettings: PreferencesEntry, Equatable, PostboxGlobalNotificationSettings { public var toBeSynchronized: GlobalNotificationSettingsSet? public var remote: GlobalNotificationSettingsSet @@ -74,6 +74,44 @@ public struct GlobalNotificationSettings: PreferencesEntry, Equatable { } } + public func defaultIncludePeer(peer: Peer) -> Bool { + let settings = self.effective + if peer is TelegramUser || peer is TelegramSecretChat { + return settings.privateChats.enabled + } else if peer is TelegramGroup { + return settings.groupChats.enabled + } else if let channel = peer as? TelegramChannel { + switch channel.info { + case .group: + return settings.groupChats.enabled + case .broadcast: + return settings.channels.enabled + } + } else { + return false + } + } + + public func isEqualInDefaultPeerInclusion(other: PostboxGlobalNotificationSettings) -> Bool { + guard let other = other as? GlobalNotificationSettings else { + return false + } + let settings = self.effective + let otherSettings = other.effective + + if settings.privateChats.enabled != otherSettings.privateChats.enabled { + return false + } + if settings.groupChats.enabled != otherSettings.groupChats.enabled { + return false + } + if settings.channels.enabled != otherSettings.channels.enabled { + return false + } + + return true + } + public init(toBeSynchronized: GlobalNotificationSettingsSet?, remote: GlobalNotificationSettingsSet) { self.toBeSynchronized = toBeSynchronized self.remote = remote diff --git a/submodules/SyncCore/Sources/StandaloneAccountTransaction.swift b/submodules/SyncCore/Sources/StandaloneAccountTransaction.swift index fee9097baa..3072e46c17 100644 --- a/submodules/SyncCore/Sources/StandaloneAccountTransaction.swift +++ b/submodules/SyncCore/Sources/StandaloneAccountTransaction.swift @@ -46,7 +46,7 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = { assertionFailure() return .nonContact } - }, additionalChatListIndexNamespace: Namespaces.Message.Cloud, messageNamespacesRequiringGroupStatsValidation: [Namespaces.Message.Cloud], defaultMessageNamespaceReadStates: [Namespaces.Message.Local: .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 0, markedUnread: false)], chatMessagesNamespaces: Set([Namespaces.Message.Cloud, Namespaces.Message.Local, Namespaces.Message.SecretIncoming])) + }, additionalChatListIndexNamespace: Namespaces.Message.Cloud, messageNamespacesRequiringGroupStatsValidation: [Namespaces.Message.Cloud], defaultMessageNamespaceReadStates: [Namespaces.Message.Local: .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: 0, markedUnread: false)], chatMessagesNamespaces: Set([Namespaces.Message.Cloud, Namespaces.Message.Local, Namespaces.Message.SecretIncoming]), globalNotificationSettingsPreferencesKey: PreferencesKeys.globalNotifications, defaultGlobalNotificationSettings: GlobalNotificationSettings.defaultSettings) }() public func accountTransaction(rootPath: String, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters, transaction: @escaping (Transaction) -> T) -> Signal { diff --git a/submodules/SyncCore/Sources/TelegramPeerNotificationSettings.swift b/submodules/SyncCore/Sources/TelegramPeerNotificationSettings.swift index 59c5bbf091..2a7fbe065c 100644 --- a/submodules/SyncCore/Sources/TelegramPeerNotificationSettings.swift +++ b/submodules/SyncCore/Sources/TelegramPeerNotificationSettings.swift @@ -145,14 +145,14 @@ public final class TelegramPeerNotificationSettings: PeerNotificationSettings, E return TelegramPeerNotificationSettings(muteState: .unmuted, messageSound: .default, displayPreviews: .default) } - public var isRemovedFromTotalUnreadCount: Bool { + public func isRemovedFromTotalUnreadCount(`default`: Bool) -> Bool { switch self.muteState { case .unmuted: return false case .muted: return true case .default: - return false + return `default` } } diff --git a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift index 0eb6714638..6c50744e73 100644 --- a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift @@ -2948,7 +2948,7 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP let currentInclusion = transaction.getPeerChatListInclusion(peerId) if let groupId = currentInclusion.groupId, groupId == Namespaces.PeerGroup.archive { if let peer = transaction.getPeer(peerId) as? TelegramSecretChat { - if let notificationSettings = transaction.getPeerNotificationSettings(peer.regularPeerId) as? TelegramPeerNotificationSettings, !notificationSettings.isRemovedFromTotalUnreadCount { + if let notificationSettings = transaction.getPeerNotificationSettings(peer.regularPeerId) as? TelegramPeerNotificationSettings, !notificationSettings.isRemovedFromTotalUnreadCount(default: !transaction.getGlobalNotificationSettings().defaultIncludePeer(peer: peer)) { transaction.updatePeerChatListInclusion(peerId, inclusion: currentInclusion.withGroupId(groupId: .root)) } } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index f4b697cfa5..7e99dabd0e 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -2727,7 +2727,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let (count, _) = renderedTotalUnreadCount(inAppSettings: inAppSettings, totalUnreadState: peerReadStateData.totalState ?? ChatListTotalUnreadState(absoluteCounters: [:], filteredCounters: [:])) var globalRemainingUnreadChatCount = count - if !notificationSettings.isRemovedFromTotalUnreadCount && peerReadStateData.unreadCount > 0 { + if !notificationSettings.isRemovedFromTotalUnreadCount(default: false) && peerReadStateData.unreadCount > 0 { if case .messages = inAppSettings.totalUnreadCountDisplayCategory { globalRemainingUnreadChatCount -= peerReadStateData.unreadCount } else { @@ -4423,7 +4423,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let view = views.views[notificationSettingsKey] as? PeerNotificationSettingsView, let notificationSettings = view.notificationSettings[peerId] { var globalRemainingUnreadChatCount = totalChatCount - if !notificationSettings.isRemovedFromTotalUnreadCount && unreadCount > 0 { + if !notificationSettings.isRemovedFromTotalUnreadCount(default: false) && unreadCount > 0 { if case .messages = inAppSettings.totalUnreadCountDisplayCategory { globalRemainingUnreadChatCount -= unreadCount } else { diff --git a/submodules/TelegramUIPreferences/Sources/RenderedTotalUnreadCount.swift b/submodules/TelegramUIPreferences/Sources/RenderedTotalUnreadCount.swift index e5347a6a4d..2e2fab0bd1 100644 --- a/submodules/TelegramUIPreferences/Sources/RenderedTotalUnreadCount.swift +++ b/submodules/TelegramUIPreferences/Sources/RenderedTotalUnreadCount.swift @@ -8,7 +8,7 @@ public enum RenderedTotalUnreadCountType { } public func renderedTotalUnreadCount(inAppNotificationSettings: InAppNotificationSettings, transaction: Transaction) -> (Int32, RenderedTotalUnreadCountType) { - let totalUnreadState = transaction.getTotalUnreadState() + let totalUnreadState = transaction.getTotalUnreadState(groupId: .root) return renderedTotalUnreadCount(inAppSettings: inAppNotificationSettings, totalUnreadState: totalUnreadState) }