[WIP] Topics

This commit is contained in:
Ali 2022-10-11 18:38:58 +04:00
parent 9a08911483
commit db4b73ae6b
92 changed files with 1900 additions and 909 deletions

View File

@ -49,7 +49,7 @@ func unreadMessages(account: Account) -> Signal<[INMessage], NoError> {
} }
if !isMuted && hasUnread { if !isMuted && hasUnread {
signals.append(account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId: index.messageIndex.id.peerId), anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: 10, fixedCombinedReadStates: fixedCombinedReadStates, topTaggedMessageIdNamespaces: Set(), tagMask: nil, appendMessagesFromTheSameGroup: false, namespaces: .not(Namespaces.Message.allScheduled), orderStatistics: .combinedLocation) signals.append(account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId: index.messageIndex.id.peerId, threadId: nil), anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: 10, fixedCombinedReadStates: fixedCombinedReadStates, topTaggedMessageIdNamespaces: Set(), tagMask: nil, appendMessagesFromTheSameGroup: false, namespaces: .not(Namespaces.Message.allScheduled), orderStatistics: .combinedLocation)
|> take(1) |> take(1)
|> map { view -> [INMessage] in |> map { view -> [INMessage] in
var messages: [INMessage] = [] var messages: [INMessage] = []

View File

@ -8114,3 +8114,5 @@ Sorry for the inconvenience.";
"ChatList.StartAction" = "Start"; "ChatList.StartAction" = "Start";
"ChatList.CloseAction" = "Close"; "ChatList.CloseAction" = "Close";
"Channel.EditAdmin.PermissionCreateTopics" = "Create Topics";

View File

@ -159,7 +159,7 @@ private func getCommonTimeline(friends: [Friend]?, in context: TimelineProviderC
if let readState = transaction.getCombinedPeerReadState(peer.id), readState.count > 0 { if let readState = transaction.getCombinedPeerReadState(peer.id), readState.count > 0 {
var isMuted = false var isMuted = false
if let notificationSettings = transaction.getPeerNotificationSettings(peer.id) as? TelegramPeerNotificationSettings { if let notificationSettings = transaction.getPeerNotificationSettings(id: peer.id) as? TelegramPeerNotificationSettings {
isMuted = notificationSettings.isRemovedFromTotalUnreadCount(default: false) isMuted = notificationSettings.isRemovedFromTotalUnreadCount(default: false)
} }
badge = WidgetDataPeer.Badge( badge = WidgetDataPeer.Badge(

View File

@ -397,7 +397,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
isMuted = true isMuted = true
} }
items.append(.action(ContextMenuActionItem(text: isMuted ? strings.ChatList_Context_Unmute : strings.ChatList_Context_Mute, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor) }, action: { _, f in items.append(.action(ContextMenuActionItem(text: isMuted ? strings.ChatList_Context_Unmute : strings.ChatList_Context_Mute, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor) }, action: { _, f in
let _ = (context.engine.peers.togglePeerMuted(peerId: peerId) let _ = (context.engine.peers.togglePeerMuted(peerId: peerId, threadId: nil)
|> deliverOnMainQueue).start(completed: { |> deliverOnMainQueue).start(completed: {
f(.default) f(.default)
}) })

View File

@ -492,7 +492,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
return return
} }
chatTitleView.titleContent = .peer(peerView: peerView, customTitle: nil, onlineMemberCount: onlineMemberCount, isScheduledMessages: false) chatTitleView.titleContent = .peer(peerView: peerView, customTitle: nil, onlineMemberCount: onlineMemberCount, isScheduledMessages: false, isMuted: nil)
strongSelf.infoReady.set(.single(true)) strongSelf.infoReady.set(.single(true))
if let channel = peerView.peers[peerView.peerId] as? TelegramChannel, !channel.flags.contains(.isForum) { if let channel = peerView.peers[peerView.peerId] as? TelegramChannel, !channel.flags.contains(.isForum) {
@ -2468,112 +2468,122 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
} }
public static func openMoreMenu(context: AccountContext, peerId: EnginePeer.Id, sourceController: ViewController, isViewingAsTopics: Bool, sourceView: UIView, gesture: ContextGesture?) { public static func openMoreMenu(context: AccountContext, peerId: EnginePeer.Id, sourceController: ViewController, isViewingAsTopics: Bool, sourceView: UIView, gesture: ContextGesture?) {
let strings = context.sharedContext.currentPresentationData.with { $0 }.strings let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|> deliverOnMainQueue).start(next: { peer in
var items: [ContextMenuItem] = [] guard case let .channel(channel) = peer else {
items.append(.action(ContextMenuActionItem(text: "View as Topics", icon: { theme in
if !isViewingAsTopics {
return nil
}
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
}, action: { [weak sourceController] _, a in
a(.default)
guard let sourceController = sourceController, let navigationController = sourceController.navigationController as? NavigationController else {
return return
} }
let chatController = context.sharedContext.makeChatListController(context: context, location: .forum(peerId: peerId), controlsHistoryPreload: false, hideNetworkActivityStatus: false, previewing: false, enableDebugActions: false) let strings = context.sharedContext.currentPresentationData.with { $0 }.strings
navigationController.replaceController(sourceController, with: chatController, animated: false)
})))
items.append(.action(ContextMenuActionItem(text: "View as Messages", icon: { theme in
if isViewingAsTopics {
return nil
}
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
}, action: { [weak sourceController] _, a in
a(.default)
guard let sourceController = sourceController, let navigationController = sourceController.navigationController as? NavigationController else {
return
}
let chatController = context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, mode: .standard(previewing: false)) var items: [ContextMenuItem] = []
navigationController.replaceController(sourceController, with: chatController, animated: false)
})))
items.append(.separator)
//TODO:localize
items.append(.action(ContextMenuActionItem(text: "Group Info", icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Contact List/CreateGroupActionIcon"), color: theme.contextMenu.primaryColor)
}, action: { [weak sourceController] _, f in
f(.default)
let _ = (context.engine.data.get( items.append(.action(ContextMenuActionItem(text: "View as Topics", icon: { theme in
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId) if !isViewingAsTopics {
) return nil
|> deliverOnMainQueue).start(next: { peer in
guard let sourceController = sourceController, let peer = peer, let controller = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) else {
return
} }
(sourceController.navigationController as? NavigationController)?.pushViewController(controller) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
}) }, action: { [weak sourceController] _, a in
}))) a(.default)
//TODO:localize
items.append(.action(ContextMenuActionItem(text: "Add Member", icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: theme.contextMenu.primaryColor)
}, action: { [weak sourceController] _, f in
f(.default)
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|> deliverOnMainQueue).start(next: { peer in
guard let sourceController = sourceController, let peer = peer else {
return
}
let selectAddMemberDisposable = MetaDisposable()
let addMemberDisposable = MetaDisposable()
context.sharedContext.openAddPeerMembers(context: context, updatedPresentationData: nil, parentController: sourceController, groupPeer: peer._asPeer(), selectAddMemberDisposable: selectAddMemberDisposable, addMemberDisposable: addMemberDisposable)
})
})))
items.append(.separator)
if let sourceController = sourceController as? ChatController {
items.append(.action(ContextMenuActionItem(text: strings.Conversation_Search, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Search"), color: theme.contextMenu.primaryColor)
}, action: { [weak sourceController] action in
action.dismissWithResult(.default)
sourceController?.beginMessageSearch("") guard let sourceController = sourceController, let navigationController = sourceController.navigationController as? NavigationController else {
return
}
let chatController = context.sharedContext.makeChatListController(context: context, location: .forum(peerId: peerId), controlsHistoryPreload: false, hideNetworkActivityStatus: false, previewing: false, enableDebugActions: false)
navigationController.replaceController(sourceController, with: chatController, animated: false)
}))) })))
} else { items.append(.action(ContextMenuActionItem(text: "View as Messages", icon: { theme in
if isViewingAsTopics {
return nil
}
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
}, action: { [weak sourceController] _, a in
a(.default)
guard let sourceController = sourceController, let navigationController = sourceController.navigationController as? NavigationController else {
return
}
let chatController = context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, mode: .standard(previewing: false))
navigationController.replaceController(sourceController, with: chatController, animated: false)
})))
items.append(.separator)
//TODO:localize //TODO:localize
items.append(.action(ContextMenuActionItem(text: "New Topic", icon: { theme in items.append(.action(ContextMenuActionItem(text: "Group Info", icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) return generateTintedImage(image: UIImage(bundleImageName: "Contact List/CreateGroupActionIcon"), color: theme.contextMenu.primaryColor)
}, action: { action in }, action: { [weak sourceController] _, f in
action.dismissWithResult(.default) f(.default)
let controller = ForumCreateTopicScreen(context: context, peerId: peerId, mode: .create) let _ = (context.engine.data.get(
controller.navigationPresentation = .modal TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)
controller.completion = { title, fileId in )
let availableColors: [Int32] = [0x6FB9F0, 0xFFD67E, 0xCB86DB, 0x8EEE98, 0xFF93B2, 0xFB6F5F] |> deliverOnMainQueue).start(next: { peer in
guard let sourceController = sourceController, let peer = peer, let controller = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) else {
let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: availableColors.randomElement()!, iconFileId: fileId) return
|> deliverOnMainQueue).start(next: { topicId in }
if let navigationController = (sourceController.navigationController as? NavigationController) { (sourceController.navigationController as? NavigationController)?.pushViewController(controller)
let _ = context.sharedContext.navigateToForumThread(context: context, peerId: peerId, threadId: topicId, navigationController: navigationController, activateInput: .text).start() })
}
})
}
sourceController.push(controller)
}))) })))
}
if channel.hasPermission(.inviteMembers) {
//TODO:localize
items.append(.action(ContextMenuActionItem(text: "Add Member", icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: theme.contextMenu.primaryColor)
}, action: { [weak sourceController] _, f in
f(.default)
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|> deliverOnMainQueue).start(next: { peer in
guard let sourceController = sourceController, let peer = peer else {
return
}
let selectAddMemberDisposable = MetaDisposable()
let addMemberDisposable = MetaDisposable()
context.sharedContext.openAddPeerMembers(context: context, updatedPresentationData: nil, parentController: sourceController, groupPeer: peer._asPeer(), selectAddMemberDisposable: selectAddMemberDisposable, addMemberDisposable: addMemberDisposable)
})
})))
}
if let sourceController = sourceController as? ChatController {
items.append(.separator)
items.append(.action(ContextMenuActionItem(text: strings.Conversation_Search, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Search"), color: theme.contextMenu.primaryColor)
}, action: { [weak sourceController] action in
action.dismissWithResult(.default)
sourceController?.beginMessageSearch("")
})))
} else if channel.hasPermission(.pinMessages) {
items.append(.separator)
//TODO:localize
items.append(.action(ContextMenuActionItem(text: "New Topic", icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
}, action: { action in
action.dismissWithResult(.default)
let controller = ForumCreateTopicScreen(context: context, peerId: peerId, mode: .create)
controller.navigationPresentation = .modal
controller.completion = { title, fileId in
let availableColors: [Int32] = [0x6FB9F0, 0xFFD67E, 0xCB86DB, 0x8EEE98, 0xFF93B2, 0xFB6F5F]
let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: availableColors.randomElement()!, iconFileId: fileId)
|> deliverOnMainQueue).start(next: { topicId in
if let navigationController = (sourceController.navigationController as? NavigationController) {
let _ = context.sharedContext.navigateToForumThread(context: context, peerId: peerId, threadId: topicId, navigationController: navigationController, activateInput: .text).start()
}
})
}
sourceController.push(controller)
})))
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: sourceController, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) let contextController = ContextController(account: context.account, presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: sourceController, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
sourceController.presentInGlobalOverlay(contextController) sourceController.presentInGlobalOverlay(contextController)
})
} }
private var initializedFilters = false private var initializedFilters = false

View File

@ -183,7 +183,7 @@ private final class ChatListShimmerNode: ASDisplayNode {
let timestamp1: Int32 = 100000 let timestamp1: Int32 = 100000
let peers: [EnginePeer.Id: EnginePeer] = [:] let peers: [EnginePeer.Id: EnginePeer] = [:]
let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture, _ in }, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture, _ in
gesture?.cancel() gesture?.cancel()
}, present: { _ in }) }, present: { _ in })

View File

@ -1741,6 +1741,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
}, setPeerIdWithRevealedOptions: { _, _ in }, setPeerIdWithRevealedOptions: { _, _ in
}, setItemPinned: { _, _ in }, setItemPinned: { _, _ in
}, setPeerMuted: { _, _ in }, setPeerMuted: { _, _ in
}, setPeerThreadMuted: { _, _, _ in
}, deletePeer: { _, _ in }, deletePeer: { _, _ in
}, deletePeerThread: { _, _ in }, deletePeerThread: { _, _ in
}, updatePeerGrouping: { _, _ in }, updatePeerGrouping: { _, _ in
@ -2952,7 +2953,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
var peers: [EnginePeer.Id: EnginePeer] = [:] var peers: [EnginePeer.Id: EnginePeer] = [:]
peers[peer1.id] = peer1 peers[peer1.id] = peer1
let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture, _ in }, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture, _ in
gesture?.cancel() gesture?.cancel()
}, present: { _ in }) }, present: { _ in })

View File

@ -2688,6 +2688,12 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
switch option.key { switch option.key {
case RevealOptionKey.delete.rawValue: case RevealOptionKey.delete.rawValue:
item.interaction.deletePeerThread(peerId, threadId) item.interaction.deletePeerThread(peerId, threadId)
case RevealOptionKey.mute.rawValue:
item.interaction.setPeerThreadMuted(peerId, threadId, true)
close = false
case RevealOptionKey.unmute.rawValue:
item.interaction.setPeerThreadMuted(peerId, threadId, false)
close = false
default: default:
break break
} }

View File

@ -67,6 +67,7 @@ public final class ChatListNodeInteraction {
let setPeerIdWithRevealedOptions: (EnginePeer.Id?, EnginePeer.Id?) -> Void let setPeerIdWithRevealedOptions: (EnginePeer.Id?, EnginePeer.Id?) -> Void
let setItemPinned: (EngineChatList.PinnedItem.Id, Bool) -> Void let setItemPinned: (EngineChatList.PinnedItem.Id, Bool) -> Void
let setPeerMuted: (EnginePeer.Id, Bool) -> Void let setPeerMuted: (EnginePeer.Id, Bool) -> Void
let setPeerThreadMuted: (EnginePeer.Id, Int64?, Bool) -> Void
let deletePeer: (EnginePeer.Id, Bool) -> Void let deletePeer: (EnginePeer.Id, Bool) -> Void
let deletePeerThread: (EnginePeer.Id, Int64) -> Void let deletePeerThread: (EnginePeer.Id, Int64) -> Void
let updatePeerGrouping: (EnginePeer.Id, Bool) -> Void let updatePeerGrouping: (EnginePeer.Id, Bool) -> Void
@ -98,6 +99,7 @@ public final class ChatListNodeInteraction {
setPeerIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void, setPeerIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void,
setItemPinned: @escaping (EngineChatList.PinnedItem.Id, Bool) -> Void, setItemPinned: @escaping (EngineChatList.PinnedItem.Id, Bool) -> Void,
setPeerMuted: @escaping (EnginePeer.Id, Bool) -> Void, setPeerMuted: @escaping (EnginePeer.Id, Bool) -> Void,
setPeerThreadMuted: @escaping (EnginePeer.Id, Int64?, Bool) -> Void,
deletePeer: @escaping (EnginePeer.Id, Bool) -> Void, deletePeer: @escaping (EnginePeer.Id, Bool) -> Void,
deletePeerThread: @escaping (EnginePeer.Id, Int64) -> Void, deletePeerThread: @escaping (EnginePeer.Id, Int64) -> Void,
updatePeerGrouping: @escaping (EnginePeer.Id, Bool) -> Void, updatePeerGrouping: @escaping (EnginePeer.Id, Bool) -> Void,
@ -119,6 +121,7 @@ public final class ChatListNodeInteraction {
self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions
self.setItemPinned = setItemPinned self.setItemPinned = setItemPinned
self.setPeerMuted = setPeerMuted self.setPeerMuted = setPeerMuted
self.setPeerThreadMuted = setPeerThreadMuted
self.deletePeer = deletePeer self.deletePeer = deletePeer
self.deletePeerThread = deletePeerThread self.deletePeerThread = deletePeerThread
self.updatePeerGrouping = updatePeerGrouping self.updatePeerGrouping = updatePeerGrouping
@ -946,7 +949,7 @@ public final class ChatListNode: ListView {
return return
} }
strongSelf.setCurrentRemovingPeerId(peerId) strongSelf.setCurrentRemovingPeerId(peerId)
let _ = (context.engine.peers.togglePeerMuted(peerId: peerId) let _ = (context.engine.peers.togglePeerMuted(peerId: peerId, threadId: nil)
|> deliverOnMainQueue).start(completed: { |> deliverOnMainQueue).start(completed: {
self?.updateState { state in self?.updateState { state in
var state = state var state = state
@ -955,6 +958,17 @@ public final class ChatListNode: ListView {
} }
self?.setCurrentRemovingPeerId(nil) self?.setCurrentRemovingPeerId(nil)
}) })
}, setPeerThreadMuted: { [weak self] peerId, threadId, _ in
//self?.setCurrentRemovingPeerId(peerId)
let _ = (context.engine.peers.togglePeerMuted(peerId: peerId, threadId: threadId)
|> deliverOnMainQueue).start(completed: {
self?.updateState { state in
var state = state
state.peerIdWithRevealedOptions = nil
return state
}
//self?.setCurrentRemovingPeerId(nil)
})
}, deletePeer: { [weak self] peerId, joined in }, deletePeer: { [weak self] peerId, joined in
self?.deletePeerChat?(peerId, joined) self?.deletePeerChat?(peerId, joined)
}, deletePeerThread: { [weak self] peerId, threadId in }, deletePeerThread: { [weak self] peerId, threadId in
@ -1799,8 +1813,11 @@ public final class ChatListNode: ListView {
if let combinedReadState = combinedReadState { if let combinedReadState = combinedReadState {
hasUnread = combinedReadState.count > 0 hasUnread = combinedReadState.count > 0
} }
if case let .chatList(index) = index { switch index {
preloadItems.append(ChatHistoryPreloadItem(index: index, isMuted: isMuted, hasUnread: hasUnread)) case let .chatList(index):
preloadItems.append(ChatHistoryPreloadItem(index: index, threadId: nil, isMuted: isMuted, hasUnread: hasUnread))
case .forum:
break
} }
} }
default: default:

View File

@ -166,7 +166,27 @@ func chatListViewForLocation(chatListLocation: ChatListControllerLocation, locat
} }
} }
case let .forum(peerId): case let .forum(peerId):
let viewKey: PostboxViewKey = .messageHistoryThreadIndex(id: peerId) let viewKey: PostboxViewKey = .messageHistoryThreadIndex(
id: peerId,
summaryComponents: ChatListEntrySummaryComponents(
components: [
ChatListEntryMessageTagSummaryKey(
tag: .unseenPersonalMessage,
actionType: PendingMessageActionType.consumeUnseenPersonalMessage
): ChatListEntrySummaryComponents.Component(
tagSummary: ChatListEntryMessageTagSummaryComponent(namespace: Namespaces.Message.Cloud),
actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent(namespace: Namespaces.Message.Cloud)
),
ChatListEntryMessageTagSummaryKey(
tag: .unseenReaction,
actionType: PendingMessageActionType.readReaction
): ChatListEntrySummaryComponents.Component(
tagSummary: ChatListEntryMessageTagSummaryComponent(namespace: Namespaces.Message.Cloud),
actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent(namespace: Namespaces.Message.Cloud)
)
]
)
)
var isFirst = false var isFirst = false
return account.postbox.combinedView(keys: [viewKey]) return account.postbox.combinedView(keys: [viewKey])
|> map { views -> ChatListNodeViewUpdate in |> map { views -> ChatListNodeViewUpdate in
@ -182,18 +202,41 @@ func chatListViewForLocation(chatListLocation: ChatListControllerLocation, locat
guard let data = item.info.get(MessageHistoryThreadData.self) else { guard let data = item.info.get(MessageHistoryThreadData.self) else {
continue continue
} }
var hasUnseenMentions = false
var isMuted = false
if case .muted = data.notificationSettings.muteState {
isMuted = true
}
if let info = item.tagSummaryInfo[ChatListEntryMessageTagSummaryKey(
tag: .unseenPersonalMessage,
actionType: PendingMessageActionType.consumeUnseenPersonalMessage
)] {
hasUnseenMentions = (info.tagSummaryCount ?? 0) > (info.actionsSummaryCount ?? 0)
}
var hasUnseenReactions = false
if let info = item.tagSummaryInfo[ChatListEntryMessageTagSummaryKey(
tag: .unseenReaction,
actionType: PendingMessageActionType.readReaction
)] {
hasUnseenReactions = (info.tagSummaryCount ?? 0) != 0// > (info.actionsSummaryCount ?? 0)
}
items.append(EngineChatList.Item( items.append(EngineChatList.Item(
id: .forum(item.id), id: .forum(item.id),
index: .forum(timestamp: item.index.timestamp, threadId: item.id, namespace: item.index.id.namespace, id: item.index.id.id), index: .forum(timestamp: item.index.timestamp, threadId: item.id, namespace: item.index.id.namespace, id: item.index.id.id),
messages: item.topMessage.flatMap { [EngineMessage($0)] } ?? [], messages: item.topMessage.flatMap { [EngineMessage($0)] } ?? [],
readCounters: EnginePeerReadCounters(state: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, .idBased(maxIncomingReadId: 1, maxOutgoingReadId: 1, maxKnownId: 1, count: data.incomingUnreadCount, markedUnread: false))])), readCounters: EnginePeerReadCounters(state: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, .idBased(maxIncomingReadId: 1, maxOutgoingReadId: 1, maxKnownId: 1, count: data.incomingUnreadCount, markedUnread: false))])),
isMuted: false, isMuted: isMuted,
draft: nil, draft: nil,
threadInfo: data.info, threadInfo: data.info,
renderedPeer: EngineRenderedPeer(peer: EnginePeer(peer)), renderedPeer: EngineRenderedPeer(peer: EnginePeer(peer)),
presence: nil, presence: nil,
hasUnseenMentions: false, hasUnseenMentions: hasUnseenMentions,
hasUnseenReactions: false, hasUnseenReactions: hasUnseenReactions,
forumTopicTitle: nil, forumTopicTitle: nil,
hasFailed: false, hasFailed: false,
isContact: false isContact: false

View File

@ -18,6 +18,17 @@ public extension ChatLocation {
return nil return nil
} }
} }
var threadId: Int64? {
switch self {
case .peer:
return nil
case let .replyThread(replyThreadMessage):
return Int64(replyThreadMessage.messageId.id)
case .feed:
return nil
}
}
} }
public enum ChatPresentationInputQueryKind: Int32 { public enum ChatPresentationInputQueryKind: Int32 {

View File

@ -76,6 +76,7 @@ public final class HashtagSearchController: TelegramBaseController {
}, setPeerIdWithRevealedOptions: { _, _ in }, setPeerIdWithRevealedOptions: { _, _ in
}, setItemPinned: { _, _ in }, setItemPinned: { _, _ in
}, setPeerMuted: { _, _ in }, setPeerMuted: { _, _ in
}, setPeerThreadMuted: { _, _, _ in
}, deletePeer: { _, _ in }, deletePeer: { _, _ in
}, deletePeerThread: { _, _ in }, deletePeerThread: { _, _ in
}, updatePeerGrouping: { _, _ in }, updatePeerGrouping: { _, _ in

View File

@ -416,7 +416,7 @@ private struct ChannelAdminControllerState: Equatable {
} }
} }
private func stringForRight(strings: PresentationStrings, right: TelegramChatAdminRightsFlags, isGroup: Bool, isChannel: Bool, defaultBannedRights: TelegramChatBannedRights?) -> String { private func stringForRight(strings: PresentationStrings, right: TelegramChatAdminRightsFlags, isGroup: Bool, isChannel: Bool, isForum: Bool, defaultBannedRights: TelegramChatBannedRights?) -> String {
if right.contains(.canChangeInfo) { if right.contains(.canChangeInfo) {
return isGroup ? strings.Group_EditAdmin_PermissionChangeInfo : strings.Channel_EditAdmin_PermissionChangeInfo return isGroup ? strings.Group_EditAdmin_PermissionChangeInfo : strings.Channel_EditAdmin_PermissionChangeInfo
} else if right.contains(.canPostMessages) { } else if right.contains(.canPostMessages) {
@ -438,7 +438,11 @@ private func stringForRight(strings: PresentationStrings, right: TelegramChatAdm
return strings.Channel_EditAdmin_PermissionInviteSubscribers return strings.Channel_EditAdmin_PermissionInviteSubscribers
} }
} else if right.contains(.canPinMessages) { } else if right.contains(.canPinMessages) {
return strings.Channel_EditAdmin_PermissionPinMessages if isForum {
return strings.Channel_EditAdmin_PermissionCreateTopics
} else {
return strings.Channel_EditAdmin_PermissionPinMessages
}
} else if right.contains(.canAddAdmins) { } else if right.contains(.canAddAdmins) {
return strings.Channel_EditAdmin_PermissionAddAdmins return strings.Channel_EditAdmin_PermissionAddAdmins
} else if right.contains(.canBeAnonymous) { } else if right.contains(.canBeAnonymous) {
@ -612,7 +616,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
var index = 0 var index = 0
for right in rightsOrder { for right in rightsOrder {
if accountUserRightsFlags.contains(right) { if accountUserRightsFlags.contains(right) {
entries.append(.rightItem(presentationData.theme, index, stringForRight(strings: presentationData.strings, right: right, isGroup: isGroup, isChannel: isChannel, defaultBannedRights: channel.defaultBannedRights), right, currentRightsFlags, currentRightsFlags.contains(right), right == .canBeAnonymous)) entries.append(.rightItem(presentationData.theme, index, stringForRight(strings: presentationData.strings, right: right, isGroup: isGroup, isChannel: isChannel, isForum: channel.flags.contains(.isForum), defaultBannedRights: channel.defaultBannedRights), right, currentRightsFlags, currentRightsFlags.contains(right), right == .canBeAnonymous))
index += 1 index += 1
} }
} }
@ -651,7 +655,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
var index = 0 var index = 0
for right in rightsOrder { for right in rightsOrder {
if accountUserRightsFlags.contains(right) { if accountUserRightsFlags.contains(right) {
entries.append(.rightItem(presentationData.theme, index, stringForRight(strings: presentationData.strings, right: right, isGroup: isGroup, isChannel: isChannel, defaultBannedRights: channel.defaultBannedRights), right, currentRightsFlags, currentRightsFlags.contains(right), !state.updating && admin.id != accountPeerId && !rightEnabledByDefault(channelPeer: channel, right: right))) entries.append(.rightItem(presentationData.theme, index, stringForRight(strings: presentationData.strings, right: right, isGroup: isGroup, isChannel: isChannel, isForum: channel.flags.contains(.isForum), defaultBannedRights: channel.defaultBannedRights), right, currentRightsFlags, currentRightsFlags.contains(right), !state.updating && admin.id != accountPeerId && !rightEnabledByDefault(channelPeer: channel, right: right)))
index += 1 index += 1
} }
} }
@ -683,7 +687,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
} else if let initialParticipant = initialParticipant, case let .member(_, _, maybeAdminInfo, _, _) = initialParticipant, let adminInfo = maybeAdminInfo { } else if let initialParticipant = initialParticipant, case let .member(_, _, maybeAdminInfo, _, _) = initialParticipant, let adminInfo = maybeAdminInfo {
var index = 0 var index = 0
for right in rightsOrder { for right in rightsOrder {
entries.append(.rightItem(presentationData.theme, index, stringForRight(strings: presentationData.strings, right: right, isGroup: isGroup, isChannel: isChannel, defaultBannedRights: channel.defaultBannedRights), right, adminInfo.rights.rights, adminInfo.rights.rights.contains(right), false)) entries.append(.rightItem(presentationData.theme, index, stringForRight(strings: presentationData.strings, right: right, isGroup: isGroup, isChannel: isChannel, isForum: channel.flags.contains(.isForum), defaultBannedRights: channel.defaultBannedRights), right, adminInfo.rights.rights, adminInfo.rights.rights.contains(right), false))
index += 1 index += 1
} }
} }
@ -782,7 +786,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
var index = 0 var index = 0
for right in rightsOrder { for right in rightsOrder {
if accountUserRightsFlags.contains(right) { if accountUserRightsFlags.contains(right) {
entries.append(.rightItem(presentationData.theme, index, stringForRight(strings: presentationData.strings, right: right, isGroup: isGroup, isChannel: isChannel, defaultBannedRights: group.defaultBannedRights), right, currentRightsFlags, currentRightsFlags.contains(right), !state.updating && accountIsCreator)) entries.append(.rightItem(presentationData.theme, index, stringForRight(strings: presentationData.strings, right: right, isGroup: isGroup, isChannel: isChannel, isForum: false, defaultBannedRights: group.defaultBannedRights), right, currentRightsFlags, currentRightsFlags.contains(right), !state.updating && accountIsCreator))
index += 1 index += 1
} }
} }

View File

@ -293,7 +293,7 @@ private func channelBannedMemberControllerEntries(presentationData: Presentation
var index = 0 var index = 0
for (right, _) in allGroupPermissionList { for (right, _) in allGroupPermissionList {
let defaultEnabled = !defaultBannedRights.flags.contains(right) && channel.hasPermission(.banMembers) let defaultEnabled = !defaultBannedRights.flags.contains(right) && channel.hasPermission(.banMembers)
entries.append(.rightItem(presentationData.theme, index, stringForGroupPermission(strings: presentationData.strings, right: right), right, defaultEnabled && !currentRightsFlags.contains(right), defaultEnabled && !state.updating)) entries.append(.rightItem(presentationData.theme, index, stringForGroupPermission(strings: presentationData.strings, right: right, isForum: channel.flags.contains(.isForum)), right, defaultEnabled && !currentRightsFlags.contains(right), defaultEnabled && !state.updating))
index += 1 index += 1
} }
@ -339,7 +339,7 @@ private func channelBannedMemberControllerEntries(presentationData: Presentation
var index = 0 var index = 0
for (right, _) in allGroupPermissionList { for (right, _) in allGroupPermissionList {
let defaultEnabled = !defaultBannedRightsFlags.contains(right) let defaultEnabled = !defaultBannedRightsFlags.contains(right)
entries.append(.rightItem(presentationData.theme, index, stringForGroupPermission(strings: presentationData.strings, right: right), right, defaultEnabled && !currentRightsFlags.contains(right), defaultEnabled && !state.updating)) entries.append(.rightItem(presentationData.theme, index, stringForGroupPermission(strings: presentationData.strings, right: right, isForum: false), right, defaultEnabled && !currentRightsFlags.contains(right), defaultEnabled && !state.updating))
index += 1 index += 1
} }

View File

@ -336,7 +336,7 @@ private struct ChannelPermissionsControllerState: Equatable {
var modifiedSlowmodeTimeout: Int32? var modifiedSlowmodeTimeout: Int32?
} }
func stringForGroupPermission(strings: PresentationStrings, right: TelegramChatBannedRightsFlags) -> String { func stringForGroupPermission(strings: PresentationStrings, right: TelegramChatBannedRightsFlags, isForum: Bool) -> String {
if right.contains(.banSendMessages) { if right.contains(.banSendMessages) {
return strings.Channel_BanUser_PermissionSendMessages return strings.Channel_BanUser_PermissionSendMessages
} else if right.contains(.banSendMedia) { } else if right.contains(.banSendMedia) {
@ -352,7 +352,11 @@ func stringForGroupPermission(strings: PresentationStrings, right: TelegramChatB
} else if right.contains(.banAddMembers) { } else if right.contains(.banAddMembers) {
return strings.Channel_BanUser_PermissionAddMembers return strings.Channel_BanUser_PermissionAddMembers
} else if right.contains(.banPinMessages) { } else if right.contains(.banPinMessages) {
return strings.Channel_EditAdmin_PermissionPinMessages if isForum {
return strings.Channel_EditAdmin_PermissionCreateTopics
} else {
return strings.Channel_EditAdmin_PermissionPinMessages
}
} else { } else {
return "" return ""
} }
@ -442,7 +446,7 @@ private func channelPermissionsControllerEntries(context: AccountContext, presen
if !channel.hasPermission(correspondingAdminRight) { if !channel.hasPermission(correspondingAdminRight) {
enabled = false enabled = false
} }
entries.append(.permission(presentationData.theme, rightIndex, stringForGroupPermission(strings: presentationData.strings, right: rights), !effectiveRightsFlags.contains(rights), rights, enabled)) entries.append(.permission(presentationData.theme, rightIndex, stringForGroupPermission(strings: presentationData.strings, right: rights, isForum: channel.flags.contains(.isForum)), !effectiveRightsFlags.contains(rights), rights, enabled))
rightIndex += 1 rightIndex += 1
} }
@ -479,7 +483,7 @@ private func channelPermissionsControllerEntries(context: AccountContext, presen
entries.append(.permissionsHeader(presentationData.theme, presentationData.strings.GroupInfo_Permissions_SectionTitle)) entries.append(.permissionsHeader(presentationData.theme, presentationData.strings.GroupInfo_Permissions_SectionTitle))
var rightIndex: Int = 0 var rightIndex: Int = 0
for (rights, _) in allGroupPermissionList { for (rights, _) in allGroupPermissionList {
entries.append(.permission(presentationData.theme, rightIndex, stringForGroupPermission(strings: presentationData.strings, right: rights), !effectiveRightsFlags.contains(rights), rights, true)) entries.append(.permission(presentationData.theme, rightIndex, stringForGroupPermission(strings: presentationData.strings, right: rights, isForum: false), !effectiveRightsFlags.contains(rights), rights, true))
rightIndex += 1 rightIndex += 1
} }

View File

@ -260,7 +260,7 @@ final class ChatListTable: Table {
let isRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettings, peer: peer, peerSettings: postbox.peerNotificationSettingsTable.getEffective(messageIndex.id.peerId)) let isRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettings, peer: peer, peerSettings: postbox.peerNotificationSettingsTable.getEffective(messageIndex.id.peerId))
let messageTagSummaryResult = resolveChatListMessageTagSummaryResultCalculation(postbox: postbox, peerId: peer.id, calculation: filterPredicate.messageTagSummary) let messageTagSummaryResult = resolveChatListMessageTagSummaryResultCalculation(postbox: postbox, peerId: peer.id, threadId: nil, calculation: filterPredicate.messageTagSummary)
if filterPredicate.pinnedPeerIds.contains(peer.id) { if filterPredicate.pinnedPeerIds.contains(peer.id) {
passFilter = true passFilter = true
@ -895,7 +895,7 @@ final class ChatListTable: Table {
} }
var tagSummary: MessageHistoryTagNamespaceSummary? var tagSummary: MessageHistoryTagNamespaceSummary?
if let summaryTag = summaryTag { if let summaryTag = summaryTag {
tagSummary = summaryTable.get(MessageHistoryTagsSummaryKey(tag: summaryTag, peerId: peerIndex.messageIndex.id.peerId, namespace: namespace)) tagSummary = summaryTable.get(MessageHistoryTagsSummaryKey(tag: summaryTag, peerId: peerIndex.messageIndex.id.peerId, threadId: nil, namespace: namespace))
} }
var topMessageAttributes: [MessageAttribute] = [] var topMessageAttributes: [MessageAttribute] = []
if let topMessage = topMessage { if let topMessage = topMessage {

View File

@ -10,7 +10,7 @@ public struct ChatListEntryMessageTagSummaryKey: Hashable {
} }
} }
public struct ChatListEntryMessageTagSummaryComponent { public struct ChatListEntryMessageTagSummaryComponent: Equatable {
public let namespace: MessageId.Namespace public let namespace: MessageId.Namespace
public init(namespace: MessageId.Namespace) { public init(namespace: MessageId.Namespace) {
@ -18,7 +18,7 @@ public struct ChatListEntryMessageTagSummaryComponent {
} }
} }
public struct ChatListEntryPendingMessageActionsSummaryComponent { public struct ChatListEntryPendingMessageActionsSummaryComponent: Equatable {
public let namespace: MessageId.Namespace public let namespace: MessageId.Namespace
public init(namespace: MessageId.Namespace) { public init(namespace: MessageId.Namespace) {
@ -26,8 +26,8 @@ public struct ChatListEntryPendingMessageActionsSummaryComponent {
} }
} }
public struct ChatListEntrySummaryComponents { public struct ChatListEntrySummaryComponents: Equatable {
public struct Component { public struct Component: Equatable {
public let tagSummary: ChatListEntryMessageTagSummaryComponent? public let tagSummary: ChatListEntryMessageTagSummaryComponent?
public let actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent? public let actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent?

View File

@ -63,7 +63,7 @@ private func mappedChatListFilterPredicate(postbox: PostboxImpl, currentTransact
let notificationsPeerId = peer.notificationSettingsPeerId ?? peer.id let notificationsPeerId = peer.notificationSettingsPeerId ?? peer.id
let isContact = postbox.contactsTable.isContact(peerId: notificationsPeerId) let isContact = postbox.contactsTable.isContact(peerId: notificationsPeerId)
let isRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettings, peer: peer, peerSettings: postbox.peerNotificationSettingsTable.getEffective(notificationsPeerId)) let isRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettings, peer: peer, peerSettings: postbox.peerNotificationSettingsTable.getEffective(notificationsPeerId))
let messageTagSummaryResult = resolveChatListMessageTagSummaryResultCalculation(postbox: postbox, peerId: peer.id, calculation: predicate.messageTagSummary) let messageTagSummaryResult = resolveChatListMessageTagSummaryResultCalculation(postbox: postbox, peerId: peer.id, threadId: nil, calculation: predicate.messageTagSummary)
if predicate.includes(peer: peer, groupId: groupId, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, isUnread: isUnread, isContact: isContact, messageTagSummaryResult: messageTagSummaryResult) { if predicate.includes(peer: peer, groupId: groupId, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, isUnread: isUnread, isContact: isContact, messageTagSummaryResult: messageTagSummaryResult) {
return true return true
@ -402,7 +402,7 @@ private final class ChatListViewSpaceState {
let isRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettingsValue, peer: peer, peerSettings: postbox.peerNotificationSettingsTable.getEffective(notificationsPeerId)) let isRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettingsValue, peer: peer, peerSettings: postbox.peerNotificationSettingsTable.getEffective(notificationsPeerId))
let messageTagSummaryResult = resolveChatListMessageTagSummaryResultCalculation(postbox: postbox, peerId: peer.id, calculation: filterPredicate.messageTagSummary) let messageTagSummaryResult = resolveChatListMessageTagSummaryResultCalculation(postbox: postbox, peerId: peer.id, threadId: nil, calculation: filterPredicate.messageTagSummary)
if !filterPredicate.includes(peer: peer, groupId: groupId, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, isUnread: postbox.readStateTable.getCombinedState(peer.id)?.isUnread ?? false, isContact: postbox.contactsTable.isContact(peerId: notificationsPeerId), messageTagSummaryResult: messageTagSummaryResult) { if !filterPredicate.includes(peer: peer, groupId: groupId, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, isUnread: postbox.readStateTable.getCombinedState(peer.id)?.isUnread ?? false, isContact: postbox.contactsTable.isContact(peerId: notificationsPeerId), messageTagSummaryResult: messageTagSummaryResult) {
continue inner continue inner
@ -498,7 +498,7 @@ private final class ChatListViewSpaceState {
if !transaction.currentUpdatedPeerNotificationSettings.isEmpty, case let .group(groupId, pinned, maybeFilterPredicate) = self.space, let filterPredicate = maybeFilterPredicate { if !transaction.currentUpdatedPeerNotificationSettings.isEmpty, case let .group(groupId, pinned, maybeFilterPredicate) = self.space, let filterPredicate = maybeFilterPredicate {
var removeEntryIndices: [MutableChatListEntryIndex] = [] var removeEntryIndices: [MutableChatListEntryIndex] = []
let _ = self.orderedEntries.mutableScan { entry in let _ = self.orderedEntries.mutableScan { entry -> MutableChatListEntry? in
let entryPeer: Peer let entryPeer: Peer
let entryNotificationsPeerId: PeerId let entryNotificationsPeerId: PeerId
switch entry { switch entry {
@ -532,7 +532,7 @@ private final class ChatListViewSpaceState {
let nowRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettingsValue, peer: entryPeer, peerSettings: settingsChange.1) let nowRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettingsValue, peer: entryPeer, peerSettings: settingsChange.1)
let messageTagSummaryResult = resolveChatListMessageTagSummaryResultCalculation(postbox: postbox, peerId: entryPeer.id, calculation: filterPredicate.messageTagSummary) let messageTagSummaryResult = resolveChatListMessageTagSummaryResultCalculation(postbox: postbox, peerId: entryPeer.id, threadId: nil, calculation: filterPredicate.messageTagSummary)
let isIncluded = filterPredicate.includes(peer: entryPeer, groupId: groupId, isRemovedFromTotalUnreadCount: nowRemovedFromTotalUnreadCount, isUnread: isUnread, isContact: postbox.contactsTable.isContact(peerId: entryNotificationsPeerId), messageTagSummaryResult: messageTagSummaryResult) let isIncluded = filterPredicate.includes(peer: entryPeer, groupId: groupId, isRemovedFromTotalUnreadCount: nowRemovedFromTotalUnreadCount, isUnread: isUnread, isContact: postbox.contactsTable.isContact(peerId: entryNotificationsPeerId), messageTagSummaryResult: messageTagSummaryResult)
if !isIncluded { if !isIncluded {
@ -570,7 +570,7 @@ private final class ChatListViewSpaceState {
let nowRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettingsValue, peer: mainPeer, peerSettings: settingsChange.1) let nowRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettingsValue, peer: mainPeer, peerSettings: settingsChange.1)
let messageTagSummaryResult = resolveChatListMessageTagSummaryResultCalculation(postbox: postbox, peerId: peerId, calculation: filterPredicate.messageTagSummary) let messageTagSummaryResult = resolveChatListMessageTagSummaryResultCalculation(postbox: postbox, peerId: peerId, threadId: nil, calculation: filterPredicate.messageTagSummary)
let isIncluded = filterPredicate.includes(peer: mainPeer, groupId: groupId, isRemovedFromTotalUnreadCount: nowRemovedFromTotalUnreadCount, isUnread: isUnread, isContact: postbox.contactsTable.isContact(peerId: peerId), messageTagSummaryResult: messageTagSummaryResult) let isIncluded = filterPredicate.includes(peer: mainPeer, groupId: groupId, isRemovedFromTotalUnreadCount: nowRemovedFromTotalUnreadCount, isUnread: isUnread, isContact: postbox.contactsTable.isContact(peerId: peerId), messageTagSummaryResult: messageTagSummaryResult)
if isIncluded && self.orderedEntries.indicesForPeerId(mainPeer.id) == nil { if isIncluded && self.orderedEntries.indicesForPeerId(mainPeer.id) == nil {
@ -730,7 +730,7 @@ private final class ChatListViewSpaceState {
if !transaction.currentUpdatedMessageTagSummaries.isEmpty || !transaction.currentUpdatedMessageActionsSummaries.isEmpty, case let .group(groupId, pinned, maybeFilterPredicate) = self.space, let filterPredicate = maybeFilterPredicate, let filterMessageTagSummary = filterPredicate.messageTagSummary { if !transaction.currentUpdatedMessageTagSummaries.isEmpty || !transaction.currentUpdatedMessageActionsSummaries.isEmpty, case let .group(groupId, pinned, maybeFilterPredicate) = self.space, let filterPredicate = maybeFilterPredicate, let filterMessageTagSummary = filterPredicate.messageTagSummary {
var removeEntryIndices: [MutableChatListEntryIndex] = [] var removeEntryIndices: [MutableChatListEntryIndex] = []
let _ = self.orderedEntries.mutableScan { entry in let _ = self.orderedEntries.mutableScan { entry -> MutableChatListEntry? in
let entryPeer: Peer let entryPeer: Peer
let entryNotificationsPeerId: PeerId let entryNotificationsPeerId: PeerId
switch entry { switch entry {
@ -752,7 +752,7 @@ private final class ChatListViewSpaceState {
return nil return nil
} }
let updatedMessageSummary = transaction.currentUpdatedMessageTagSummaries[MessageHistoryTagsSummaryKey(tag: filterMessageTagSummary.addCount.tag, peerId: entryPeer.id, namespace: filterMessageTagSummary.addCount.namespace)] let updatedMessageSummary = transaction.currentUpdatedMessageTagSummaries[MessageHistoryTagsSummaryKey(tag: filterMessageTagSummary.addCount.tag, peerId: entryPeer.id, threadId: nil, namespace: filterMessageTagSummary.addCount.namespace)]
let updatedActionsSummary = transaction.currentUpdatedMessageActionsSummaries[PendingMessageActionsSummaryKey(type: filterMessageTagSummary.subtractCount.type, peerId: entryPeer.id, namespace: filterMessageTagSummary.subtractCount.namespace)] let updatedActionsSummary = transaction.currentUpdatedMessageActionsSummaries[PendingMessageActionsSummaryKey(type: filterMessageTagSummary.subtractCount.type, peerId: entryPeer.id, namespace: filterMessageTagSummary.subtractCount.namespace)]
if updatedMessageSummary != nil || updatedActionsSummary != nil { if updatedMessageSummary != nil || updatedActionsSummary != nil {
@ -768,7 +768,7 @@ private final class ChatListViewSpaceState {
let nowRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettingsValue, peer: entryPeer, peerSettings: postbox.peerNotificationSettingsTable.getEffective(entryPeer.id)) let nowRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettingsValue, peer: entryPeer, peerSettings: postbox.peerNotificationSettingsTable.getEffective(entryPeer.id))
let messageTagSummaryResult = resolveChatListMessageTagSummaryResultCalculation(postbox: postbox, peerId: entryPeer.id, calculation: filterPredicate.messageTagSummary) let messageTagSummaryResult = resolveChatListMessageTagSummaryResultCalculation(postbox: postbox, peerId: entryPeer.id, threadId: nil, calculation: filterPredicate.messageTagSummary)
let isIncluded = filterPredicate.includes(peer: entryPeer, groupId: groupId, isRemovedFromTotalUnreadCount: nowRemovedFromTotalUnreadCount, isUnread: isUnread, isContact: postbox.contactsTable.isContact(peerId: entryNotificationsPeerId), messageTagSummaryResult: messageTagSummaryResult) let isIncluded = filterPredicate.includes(peer: entryPeer, groupId: groupId, isRemovedFromTotalUnreadCount: nowRemovedFromTotalUnreadCount, isUnread: isUnread, isContact: postbox.contactsTable.isContact(peerId: entryNotificationsPeerId), messageTagSummaryResult: messageTagSummaryResult)
if !isIncluded { if !isIncluded {
@ -813,7 +813,7 @@ private final class ChatListViewSpaceState {
let nowRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettingsValue, peer: mainPeer, peerSettings: postbox.peerNotificationSettingsTable.getEffective(mainPeer.id)) let nowRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettingsValue, peer: mainPeer, peerSettings: postbox.peerNotificationSettingsTable.getEffective(mainPeer.id))
let messageTagSummaryResult = resolveChatListMessageTagSummaryResultCalculation(postbox: postbox, peerId: peerId, calculation: filterPredicate.messageTagSummary) let messageTagSummaryResult = resolveChatListMessageTagSummaryResultCalculation(postbox: postbox, peerId: peerId, threadId: nil, calculation: filterPredicate.messageTagSummary)
let isIncluded = filterPredicate.includes(peer: mainPeer, groupId: groupId, isRemovedFromTotalUnreadCount: nowRemovedFromTotalUnreadCount, isUnread: isUnread, isContact: postbox.contactsTable.isContact(peerId: peerId), messageTagSummaryResult: messageTagSummaryResult) let isIncluded = filterPredicate.includes(peer: mainPeer, groupId: groupId, isRemovedFromTotalUnreadCount: nowRemovedFromTotalUnreadCount, isUnread: isUnread, isContact: postbox.contactsTable.isContact(peerId: peerId), messageTagSummaryResult: messageTagSummaryResult)
if isIncluded && self.orderedEntries.indicesForPeerId(mainPeer.id) == nil { if isIncluded && self.orderedEntries.indicesForPeerId(mainPeer.id) == nil {
@ -852,7 +852,7 @@ private final class ChatListViewSpaceState {
for (key, component) in self.summaryComponents.components { for (key, component) in self.summaryComponents.components {
var updatedTagSummaryCount: Int32? var updatedTagSummaryCount: Int32?
if let tagSummary = component.tagSummary { if let tagSummary = component.tagSummary {
let key = MessageHistoryTagsSummaryKey(tag: key.tag, peerId: index.messageIndex.id.peerId, namespace: tagSummary.namespace) let key = MessageHistoryTagsSummaryKey(tag: key.tag, peerId: index.messageIndex.id.peerId, threadId: nil, namespace: tagSummary.namespace)
if let summary = transaction.currentUpdatedMessageTagSummaries[key] { if let summary = transaction.currentUpdatedMessageTagSummaries[key] {
updatedTagSummaryCount = summary.count updatedTagSummaryCount = summary.count
} }
@ -1428,7 +1428,7 @@ struct ChatListViewState {
var actionsSummaryCount: Int32? var actionsSummaryCount: Int32?
if let tagSummary = component.tagSummary { if let tagSummary = component.tagSummary {
let key = MessageHistoryTagsSummaryKey(tag: key.tag, peerId: index.messageIndex.id.peerId, namespace: tagSummary.namespace) let key = MessageHistoryTagsSummaryKey(tag: key.tag, peerId: index.messageIndex.id.peerId, threadId: nil, namespace: tagSummary.namespace)
if let summary = postbox.messageHistoryTagsSummaryTable.get(key) { if let summary = postbox.messageHistoryTagsSummaryTable.get(key) {
tagSummaryCount = summary.count tagSummaryCount = summary.count
} }

View File

@ -2,12 +2,36 @@ import Foundation
import SwiftSignalKit import SwiftSignalKit
public enum ChatLocationInput { public enum ChatLocationInput {
case peer(peerId: PeerId) case peer(peerId: PeerId, threadId: Int64?)
case thread(peerId: PeerId, threadId: Int64, data: Signal<MessageHistoryViewExternalInput, NoError>) case thread(peerId: PeerId, threadId: Int64, data: Signal<MessageHistoryViewExternalInput, NoError>)
case feed(id: Int32, data: Signal<MessageHistoryViewExternalInput, NoError>) case feed(id: Int32, data: Signal<MessageHistoryViewExternalInput, NoError>)
} }
public extension ChatLocationInput {
var peerId: PeerId? {
switch self {
case let .peer(peerId, _):
return peerId
case let .thread(peerId, _, _):
return peerId
case .feed:
return nil
}
}
var threadId: Int64? {
switch self {
case let .peer(_, threadId):
return threadId
case let .thread(_, threadId, _):
return threadId
case .feed:
return nil
}
}
}
public enum ResolvedChatLocationInput { public enum ResolvedChatLocationInput {
case peer(PeerId) case peer(peerId: PeerId, threadId: Int64?)
case external(MessageHistoryViewExternalInput) case external(MessageHistoryViewExternalInput)
} }

View File

@ -3,7 +3,15 @@ import Foundation
struct MessageHistoryIndexHoleOperationKey: Hashable { struct MessageHistoryIndexHoleOperationKey: Hashable {
let peerId: PeerId let peerId: PeerId
let namespace: MessageId.Namespace let namespace: MessageId.Namespace
let threadId: Int64?
let space: MessageHistoryHoleSpace let space: MessageHistoryHoleSpace
init(peerId: PeerId, namespace: MessageId.Namespace, threadId: Int64?, space: MessageHistoryHoleSpace) {
self.peerId = peerId
self.namespace = namespace
self.threadId = threadId
self.space = space
}
} }
enum MessageHistoryIndexHoleOperation { enum MessageHistoryIndexHoleOperation {
@ -25,8 +33,8 @@ public enum MessageHistoryHoleSpace: Equatable, Hashable, CustomStringConvertibl
} }
} }
private func addOperation(_ operation: MessageHistoryIndexHoleOperation, peerId: PeerId, namespace: MessageId.Namespace, space: MessageHistoryHoleSpace, to operations: inout [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]]) { func addMessageHistoryHoleOperation(_ operation: MessageHistoryIndexHoleOperation, peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace, space: MessageHistoryHoleSpace, to operations: inout [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]]) {
let key = MessageHistoryIndexHoleOperationKey(peerId: peerId, namespace: namespace, space: space) let key = MessageHistoryIndexHoleOperationKey(peerId: peerId, namespace: namespace, threadId: threadId, space: space)
if operations[key] == nil { if operations[key] == nil {
operations[key] = [] operations[key] = []
} }
@ -365,7 +373,7 @@ final class MessageHistoryHoleIndexTable: Table {
self.valueBox.set(self.table, key: self.key(id: MessageId(peerId: peerId, namespace: namespace, id: closedRange.upperBound), space: space), value: MemoryBuffer(memory: &lowerBound, capacity: 4, length: 4, freeWhenDone: false)) self.valueBox.set(self.table, key: self.key(id: MessageId(peerId: peerId, namespace: namespace, id: closedRange.upperBound), space: space), value: MemoryBuffer(memory: &lowerBound, capacity: 4, length: 4, freeWhenDone: false))
} }
addOperation(.insert(clippedRange), peerId: peerId, namespace: namespace, space: space, to: &operations) addMessageHistoryHoleOperation(.insert(clippedRange), peerId: peerId, threadId: nil, namespace: namespace, space: space, to: &operations)
} }
func remove(peerId: PeerId, namespace: MessageId.Namespace, space: MessageHistoryHoleSpace, range: ClosedRange<MessageId.Id>, operations: inout [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]]) { func remove(peerId: PeerId, namespace: MessageId.Namespace, space: MessageHistoryHoleSpace, range: ClosedRange<MessageId.Id>, operations: inout [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]]) {
@ -431,7 +439,7 @@ final class MessageHistoryHoleIndexTable: Table {
} }
if !removeKeys.isEmpty { if !removeKeys.isEmpty {
addOperation(.remove(range), peerId: peerId, namespace: namespace, space: space, to: &operations) addMessageHistoryHoleOperation(.remove(range), peerId: peerId, threadId: nil, namespace: namespace, space: space, to: &operations)
} }
} }

View File

@ -81,6 +81,7 @@ final class MessageHistoryTable: Table {
let failedTable: MessageHistoryFailedTable let failedTable: MessageHistoryFailedTable
let tagsTable: MessageHistoryTagsTable let tagsTable: MessageHistoryTagsTable
let threadsTable: MessageHistoryThreadsTable let threadsTable: MessageHistoryThreadsTable
let threadTagsTable: MessageHistoryThreadTagsTable
let globalTagsTable: GlobalMessageHistoryTagsTable let globalTagsTable: GlobalMessageHistoryTagsTable
let localTagsTable: LocalMessageHistoryTagsTable let localTagsTable: LocalMessageHistoryTagsTable
let timeBasedAttributesTable: TimestampBasedMessageAttributesTable let timeBasedAttributesTable: TimestampBasedMessageAttributesTable
@ -90,7 +91,7 @@ final class MessageHistoryTable: Table {
let summaryTable: MessageHistoryTagsSummaryTable let summaryTable: MessageHistoryTagsSummaryTable
let pendingActionsTable: PendingMessageActionsTable let pendingActionsTable: PendingMessageActionsTable
init(valueBox: ValueBox, table: ValueBoxTable, useCaches: Bool, seedConfiguration: SeedConfiguration, messageHistoryIndexTable: MessageHistoryIndexTable, messageHistoryHoleIndexTable: MessageHistoryHoleIndexTable, messageMediaTable: MessageMediaTable, historyMetadataTable: MessageHistoryMetadataTable, globallyUniqueMessageIdsTable: MessageGloballyUniqueIdTable, unsentTable: MessageHistoryUnsentTable, failedTable: MessageHistoryFailedTable, tagsTable: MessageHistoryTagsTable, threadsTable: MessageHistoryThreadsTable, globalTagsTable: GlobalMessageHistoryTagsTable, localTagsTable: LocalMessageHistoryTagsTable, timeBasedAttributesTable: TimestampBasedMessageAttributesTable, readStateTable: MessageHistoryReadStateTable, synchronizeReadStateTable: MessageHistorySynchronizeReadStateTable, textIndexTable: MessageHistoryTextIndexTable, summaryTable: MessageHistoryTagsSummaryTable, pendingActionsTable: PendingMessageActionsTable) { init(valueBox: ValueBox, table: ValueBoxTable, useCaches: Bool, seedConfiguration: SeedConfiguration, messageHistoryIndexTable: MessageHistoryIndexTable, messageHistoryHoleIndexTable: MessageHistoryHoleIndexTable, messageMediaTable: MessageMediaTable, historyMetadataTable: MessageHistoryMetadataTable, globallyUniqueMessageIdsTable: MessageGloballyUniqueIdTable, unsentTable: MessageHistoryUnsentTable, failedTable: MessageHistoryFailedTable, tagsTable: MessageHistoryTagsTable, threadsTable: MessageHistoryThreadsTable, threadTagsTable: MessageHistoryThreadTagsTable, globalTagsTable: GlobalMessageHistoryTagsTable, localTagsTable: LocalMessageHistoryTagsTable, timeBasedAttributesTable: TimestampBasedMessageAttributesTable, readStateTable: MessageHistoryReadStateTable, synchronizeReadStateTable: MessageHistorySynchronizeReadStateTable, textIndexTable: MessageHistoryTextIndexTable, summaryTable: MessageHistoryTagsSummaryTable, pendingActionsTable: PendingMessageActionsTable) {
self.seedConfiguration = seedConfiguration self.seedConfiguration = seedConfiguration
self.messageHistoryIndexTable = messageHistoryIndexTable self.messageHistoryIndexTable = messageHistoryIndexTable
self.messageHistoryHoleIndexTable = messageHistoryHoleIndexTable self.messageHistoryHoleIndexTable = messageHistoryHoleIndexTable
@ -101,6 +102,7 @@ final class MessageHistoryTable: Table {
self.failedTable = failedTable self.failedTable = failedTable
self.tagsTable = tagsTable self.tagsTable = tagsTable
self.threadsTable = threadsTable self.threadsTable = threadsTable
self.threadTagsTable = threadTagsTable
self.globalTagsTable = globalTagsTable self.globalTagsTable = globalTagsTable
self.localTagsTable = localTagsTable self.localTagsTable = localTagsTable
self.timeBasedAttributesTable = timeBasedAttributesTable self.timeBasedAttributesTable = timeBasedAttributesTable
@ -278,6 +280,9 @@ final class MessageHistoryTable: Table {
if (currentTags & 1) != 0 { if (currentTags & 1) != 0 {
let tag = MessageTags(rawValue: 1 << UInt32(i)) let tag = MessageTags(rawValue: 1 << UInt32(i))
self.tagsTable.add(tags: tag, index: message.index, isNewlyAdded: true, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) self.tagsTable.add(tags: tag, index: message.index, isNewlyAdded: true, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries)
if let threadId = message.threadId {
self.threadTagsTable.add(tags: tag, threadId: threadId, index: message.index, isNewlyAdded: true, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries)
}
} }
} }
} }
@ -434,13 +439,34 @@ final class MessageHistoryTable: Table {
return globallyUniqueIdToMessageId return globallyUniqueIdToMessageId
} }
func removeMessages(_ messageIds: [MessageId], operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], forEachMedia: (Media) -> Void) { func removeMessages(_ messageIds: [MessageId], operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], forEachMedia: ((Media) -> Void)?) {
for (peerId, messageIds) in self.messageIdsByPeerId(messageIds) { for (peerId, messageIds) in self.messageIdsByPeerId(messageIds) {
var operations: [MessageHistoryIndexOperation] = [] var operations: [MessageHistoryIndexOperation] = []
for id in messageIds { for id in messageIds {
self.messageHistoryIndexTable.removeMessage(id, operations: &operations) self.messageHistoryIndexTable.removeMessage(id, operations: &operations)
} }
if let forEachMedia = forEachMedia {
for operation in operations {
if case let .Remove(index) = operation {
if let message = self.getMessage(index) {
for media in self.renderMessageMedia(referencedMedia: message.referencedMedia, embeddedMediaData: message.embeddedMediaData) {
forEachMedia(media)
}
}
}
}
}
self.processIndexOperations(peerId, operations: operations, processedOperationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: &timestampBasedMessageAttributesOperations)
}
}
func removeMessagesInRange(peerId: PeerId, namespace: MessageId.Namespace, minId: MessageId.Id, maxId: MessageId.Id, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], forEachMedia: ((Media) -> Void)?) {
var operations: [MessageHistoryIndexOperation] = []
self.messageHistoryIndexTable.removeMessagesInRange(peerId: peerId, namespace: namespace, minId: minId, maxId: maxId, operations: &operations)
if let forEachMedia = forEachMedia {
for operation in operations { for operation in operations {
if case let .Remove(index) = operation { if case let .Remove(index) = operation {
if let message = self.getMessage(index) { if let message = self.getMessage(index) {
@ -450,27 +476,12 @@ final class MessageHistoryTable: Table {
} }
} }
} }
self.processIndexOperations(peerId, operations: operations, processedOperationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: &timestampBasedMessageAttributesOperations)
}
}
func removeMessagesInRange(peerId: PeerId, namespace: MessageId.Namespace, minId: MessageId.Id, maxId: MessageId.Id, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], forEachMedia: (Media) -> Void) {
var operations: [MessageHistoryIndexOperation] = []
self.messageHistoryIndexTable.removeMessagesInRange(peerId: peerId, namespace: namespace, minId: minId, maxId: maxId, operations: &operations)
for operation in operations {
if case let .Remove(index) = operation {
if let message = self.getMessage(index) {
for media in self.renderMessageMedia(referencedMedia: message.referencedMedia, embeddedMediaData: message.embeddedMediaData) {
forEachMedia(media)
}
}
}
} }
self.processIndexOperations(peerId, operations: operations, processedOperationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: &timestampBasedMessageAttributesOperations) self.processIndexOperations(peerId, operations: operations, processedOperationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: &timestampBasedMessageAttributesOperations)
} }
func clearHistoryInRange(peerId: PeerId, threadId: Int64?, minTimestamp: Int32, maxTimestamp: Int32, namespaces: MessageIdNamespaces, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], forEachMedia: (Media) -> Void) { func clearHistoryInRange(peerId: PeerId, threadId: Int64?, minTimestamp: Int32, maxTimestamp: Int32, namespaces: MessageIdNamespaces, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], forEachMedia: ((Media) -> Void)?) {
var indices = self.allMessageIndices(peerId: peerId).filter { namespaces.contains($0.id.namespace) } var indices = self.allMessageIndices(peerId: peerId).filter { namespaces.contains($0.id.namespace) }
if let threadId = threadId { if let threadId = threadId {
indices = indices.filter { index in indices = indices.filter { index in
@ -491,7 +502,7 @@ final class MessageHistoryTable: Table {
self.removeMessages(indices.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: &timestampBasedMessageAttributesOperations, forEachMedia: forEachMedia) self.removeMessages(indices.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: &timestampBasedMessageAttributesOperations, forEachMedia: forEachMedia)
} }
func clearHistory(peerId: PeerId, threadId: Int64?, namespaces: MessageIdNamespaces, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], forEachMedia: (Media) -> Void) { func clearHistory(peerId: PeerId, threadId: Int64?, namespaces: MessageIdNamespaces, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], forEachMedia: ((Media) -> Void)?) {
var indices = self.allMessageIndices(peerId: peerId).filter { namespaces.contains($0.id.namespace) } var indices = self.allMessageIndices(peerId: peerId).filter { namespaces.contains($0.id.namespace) }
if let threadId = threadId { if let threadId = threadId {
indices = indices.filter { index in indices = indices.filter { index in
@ -509,12 +520,12 @@ final class MessageHistoryTable: Table {
self.removeMessages(indices.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: &timestampBasedMessageAttributesOperations, forEachMedia: forEachMedia) self.removeMessages(indices.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: &timestampBasedMessageAttributesOperations, forEachMedia: forEachMedia)
} }
func removeAllMessagesWithAuthor(peerId: PeerId, authorId: PeerId, namespace: MessageId.Namespace, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], forEachMedia: (Media) -> Void) { func removeAllMessagesWithAuthor(peerId: PeerId, authorId: PeerId, namespace: MessageId.Namespace, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], forEachMedia: ((Media) -> Void)?) {
let indices = self.allIndicesWithAuthor(peerId: peerId, authorId: authorId, namespace: namespace) let indices = self.allIndicesWithAuthor(peerId: peerId, authorId: authorId, namespace: namespace)
self.removeMessages(indices.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: &timestampBasedMessageAttributesOperations, forEachMedia: forEachMedia) self.removeMessages(indices.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: &timestampBasedMessageAttributesOperations, forEachMedia: forEachMedia)
} }
func removeAllMessagesWithGlobalTag(tag: GlobalMessageTags, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], forEachMedia: (Media) -> Void) { func removeAllMessagesWithGlobalTag(tag: GlobalMessageTags, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], forEachMedia: ((Media) -> Void)?) {
var indices: [MessageIndex] = [] var indices: [MessageIndex] = []
for entry in self.allIndicesWithGlobalTag(tag: tag) { for entry in self.allIndicesWithGlobalTag(tag: tag) {
switch entry { switch entry {
@ -527,7 +538,7 @@ final class MessageHistoryTable: Table {
self.removeMessages(indices.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: &timestampBasedMessageAttributesOperations, forEachMedia: forEachMedia) self.removeMessages(indices.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: &timestampBasedMessageAttributesOperations, forEachMedia: forEachMedia)
} }
func removeAllMessagesWithForwardAuthor(peerId: PeerId, forwardAuthorId: PeerId, namespace: MessageId.Namespace, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], forEachMedia: (Media) -> Void) { func removeAllMessagesWithForwardAuthor(peerId: PeerId, forwardAuthorId: PeerId, namespace: MessageId.Namespace, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], forEachMedia: ((Media) -> Void)?) {
let indices = self.allIndicesWithForwardAuthor(peerId: peerId, forwardAuthorId: forwardAuthorId, namespace: namespace) let indices = self.allIndicesWithForwardAuthor(peerId: peerId, forwardAuthorId: forwardAuthorId, namespace: namespace)
self.removeMessages(indices.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: &timestampBasedMessageAttributesOperations, forEachMedia: forEachMedia) self.removeMessages(indices.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: &timestampBasedMessageAttributesOperations, forEachMedia: forEachMedia)
} }
@ -1335,6 +1346,9 @@ final class MessageHistoryTable: Table {
for tag in message.tags { for tag in message.tags {
self.tagsTable.remove(tags: tag, index: index, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) self.tagsTable.remove(tags: tag, index: index, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries)
if let threadId = message.threadId {
self.threadTagsTable.remove(tags: tag, threadId: threadId, index: index, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries)
}
} }
if let threadId = message.threadId { if let threadId = message.threadId {
self.threadsTable.remove(threadId: threadId, index: index) self.threadsTable.remove(threadId: threadId, index: index)
@ -1539,10 +1553,17 @@ final class MessageHistoryTable: Table {
if previousMessage.tags != message.tags || index != updatedIndex { if previousMessage.tags != message.tags || index != updatedIndex {
if !previousMessage.tags.isEmpty { if !previousMessage.tags.isEmpty {
self.tagsTable.remove(tags: previousMessage.tags, index: index, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) self.tagsTable.remove(tags: previousMessage.tags, index: index, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries)
if let threadId = previousMessage.threadId {
self.threadTagsTable.remove(tags: previousMessage.tags, threadId: threadId, index: index, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries)
}
} }
if !message.tags.isEmpty { if !message.tags.isEmpty {
//let isNewlyAdded = previousMessage.tags.isEmpty //let isNewlyAdded = previousMessage.tags.isEmpty
self.tagsTable.add(tags: message.tags, index: message.index, isNewlyAdded: false, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries) self.tagsTable.add(tags: message.tags, index: message.index, isNewlyAdded: false, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries)
if let threadId = message.threadId {
self.threadTagsTable.add(tags: message.tags, threadId: threadId, index: message.index, isNewlyAdded: false, updatedSummaries: &updatedMessageTagSummaries, invalidateSummaries: &invalidateMessageTagSummaries)
}
} }
} }
if previousMessage.threadId != message.threadId || index != message.index { if previousMessage.threadId != message.threadId || index != message.index {
@ -2977,54 +2998,85 @@ final class MessageHistoryTable: Table {
precondition(fromIndex.id.namespace == toIndex.id.namespace) precondition(fromIndex.id.namespace == toIndex.id.namespace)
var result: [IntermediateMessage] = [] var result: [IntermediateMessage] = []
if let threadId = threadId { if let threadId = threadId {
var indices: [MessageIndex] = [] if let tag = tag {
var startIndex = fromIndex let indices: [MessageIndex]
var localIncludeFrom = includeFrom
while true {
let sliceIndices: [MessageIndex]
if fromIndex < toIndex { if fromIndex < toIndex {
sliceIndices = self.threadsTable.laterIndices(threadId: threadId, peerId: peerId, namespace: namespace, index: startIndex, includeFrom: localIncludeFrom, count: limit) indices = self.threadTagsTable.laterIndices(tag: tag, threadId: threadId, peerId: peerId, namespace: namespace, index: fromIndex, includeFrom: includeFrom, count: limit)
} else { } else {
sliceIndices = self.threadsTable.earlierIndices(threadId: threadId, peerId: peerId, namespace: namespace, index: startIndex, includeFrom: localIncludeFrom, count: limit) indices = self.threadTagsTable.earlierIndices(tag: tag, threadId: threadId, peerId: peerId, namespace: namespace, index: fromIndex, includeFrom: includeFrom, count: limit)
} }
if sliceIndices.isEmpty { for index in indices {
break
}
startIndex = sliceIndices[sliceIndices.count - 1]
localIncludeFrom = false
for index in sliceIndices {
if let ignoreMessagesInTimestampRange = ignoreMessagesInTimestampRange { if let ignoreMessagesInTimestampRange = ignoreMessagesInTimestampRange {
if ignoreMessagesInTimestampRange.contains(index.timestamp) { if ignoreMessagesInTimestampRange.contains(index.timestamp) {
continue continue
} }
} }
if let tag = tag { if fromIndex < toIndex {
if self.tagsTable.entryExists(tag: tag, index: index) { if index < fromIndex || index > toIndex {
indices.append(index) continue
} }
} else { } else {
indices.append(index) if index < toIndex || index > fromIndex {
continue
}
}
if let message = self.getMessage(index) {
result.append(message)
} else {
assertionFailure()
} }
} }
if indices.count >= limit { } else {
break var indices: [MessageIndex] = []
} var startIndex = fromIndex
} var localIncludeFrom = includeFrom
for index in indices { while true {
if fromIndex < toIndex { let sliceIndices: [MessageIndex]
if index < fromIndex || index > toIndex { if fromIndex < toIndex {
continue sliceIndices = self.threadsTable.laterIndices(threadId: threadId, peerId: peerId, namespace: namespace, index: startIndex, includeFrom: localIncludeFrom, count: limit)
} else {
sliceIndices = self.threadsTable.earlierIndices(threadId: threadId, peerId: peerId, namespace: namespace, index: startIndex, includeFrom: localIncludeFrom, count: limit)
} }
} else { if sliceIndices.isEmpty {
if index < toIndex || index > fromIndex { break
continue }
startIndex = sliceIndices[sliceIndices.count - 1]
localIncludeFrom = false
for index in sliceIndices {
if let ignoreMessagesInTimestampRange = ignoreMessagesInTimestampRange {
if ignoreMessagesInTimestampRange.contains(index.timestamp) {
continue
}
}
if let tag = tag {
if self.tagsTable.entryExists(tag: tag, index: index) {
indices.append(index)
}
} else {
indices.append(index)
}
}
if indices.count >= limit {
break
} }
} }
if let message = self.getMessage(index) { for index in indices {
result.append(message) if fromIndex < toIndex {
} else { if index < fromIndex || index > toIndex {
assertionFailure() continue
}
} else {
if index < toIndex || index > fromIndex {
continue
}
}
if let message = self.getMessage(index) {
result.append(message)
} else {
assertionFailure()
}
} }
} }
} else if let tag = tag { } else if let tag = tag {

View File

@ -3,22 +3,24 @@ import Foundation
final class MutableMessageHistoryTagSummaryView: MutablePostboxView { final class MutableMessageHistoryTagSummaryView: MutablePostboxView {
private let tag: MessageTags private let tag: MessageTags
private let peerId: PeerId private let peerId: PeerId
private let threadId: Int64?
private let namespace: MessageId.Namespace private let namespace: MessageId.Namespace
fileprivate var count: Int32? fileprivate var count: Int32?
init(postbox: PostboxImpl, tag: MessageTags, peerId: PeerId, namespace: MessageId.Namespace) { init(postbox: PostboxImpl, tag: MessageTags, peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace) {
self.tag = tag self.tag = tag
self.peerId = peerId self.peerId = peerId
self.threadId = threadId
self.namespace = namespace self.namespace = namespace
self.count = postbox.messageHistoryTagsSummaryTable.get(MessageHistoryTagsSummaryKey(tag: tag, peerId: peerId, namespace: namespace))?.count self.count = postbox.messageHistoryTagsSummaryTable.get(MessageHistoryTagsSummaryKey(tag: tag, peerId: peerId, threadId: threadId, namespace: namespace))?.count
} }
func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool { func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool {
var hasChanges = false var hasChanges = false
if let summary = transaction.currentUpdatedMessageTagSummaries[MessageHistoryTagsSummaryKey(tag: self.tag, peerId: self.peerId, namespace: self.namespace)] { if let summary = transaction.currentUpdatedMessageTagSummaries[MessageHistoryTagsSummaryKey(tag: self.tag, peerId: self.peerId, threadId: self.threadId, namespace: self.namespace)] {
self.count = summary.count self.count = summary.count
hasChanges = true hasChanges = true
} }

View File

@ -43,6 +43,7 @@ public struct MessageHistoryTagNamespaceSummary: Equatable, CustomStringConverti
struct MessageHistoryTagsSummaryKey: Equatable, Hashable { struct MessageHistoryTagsSummaryKey: Equatable, Hashable {
let tag: MessageTags let tag: MessageTags
let peerId: PeerId let peerId: PeerId
let threadId: Int64?
let namespace: MessageId.Namespace let namespace: MessageId.Namespace
} }
@ -80,7 +81,8 @@ class MessageHistoryTagsSummaryTable: Table {
private var cachedSummaries: [MessageHistoryTagsSummaryKey: CachedEntry] = [:] private var cachedSummaries: [MessageHistoryTagsSummaryKey: CachedEntry] = [:]
private var updatedKeys = Set<MessageHistoryTagsSummaryKey>() private var updatedKeys = Set<MessageHistoryTagsSummaryKey>()
private let sharedKey = ValueBoxKey(length: 4 + 8 + 4) private let sharedSimpleKey = ValueBoxKey(length: 4 + 8 + 4)
private let sharedThreadKey = ValueBoxKey(length: 4 + 8 + 4 + 8)
init(valueBox: ValueBox, table: ValueBoxTable, useCaches: Bool, invalidateTable: InvalidatedMessageHistoryTagsSummaryTable) { init(valueBox: ValueBox, table: ValueBoxTable, useCaches: Bool, invalidateTable: InvalidatedMessageHistoryTagsSummaryTable) {
self.invalidateTable = invalidateTable self.invalidateTable = invalidateTable
@ -88,17 +90,25 @@ class MessageHistoryTagsSummaryTable: Table {
super.init(valueBox: valueBox, table: table, useCaches: useCaches) super.init(valueBox: valueBox, table: table, useCaches: useCaches)
} }
private func key(key: MessageHistoryTagsSummaryKey, sharedKey: ValueBoxKey = ValueBoxKey(length: 4 + 8 + 4)) -> ValueBoxKey { private func keyShared(key: MessageHistoryTagsSummaryKey) -> ValueBoxKey {
sharedKey.setUInt32(0, value: key.tag.rawValue) if let threadId = key.threadId {
sharedKey.setInt64(4, value: key.peerId.toInt64()) self.sharedThreadKey.setUInt32(0, value: key.tag.rawValue)
sharedKey.setInt32(4 + 8, value: key.namespace) self.sharedThreadKey.setInt64(4, value: key.peerId.toInt64())
return sharedKey self.sharedThreadKey.setInt32(4 + 8, value: key.namespace)
self.sharedThreadKey.setInt64(4 + 8 + 4, value: threadId)
return self.sharedSimpleKey
} else {
self.sharedSimpleKey.setUInt32(0, value: key.tag.rawValue)
self.sharedSimpleKey.setInt64(4, value: key.peerId.toInt64())
self.sharedSimpleKey.setInt32(4 + 8, value: key.namespace)
return self.sharedSimpleKey
}
} }
func get(_ key: MessageHistoryTagsSummaryKey) -> MessageHistoryTagNamespaceSummary? { func get(_ key: MessageHistoryTagsSummaryKey) -> MessageHistoryTagNamespaceSummary? {
if let cached = self.cachedSummaries[key] { if let cached = self.cachedSummaries[key] {
return cached.summary return cached.summary
} else if let value = self.valueBox.get(self.table, key: self.key(key: key, sharedKey: self.sharedKey)) { } else if let value = self.valueBox.get(self.table, key: self.keyShared(key: key)) {
let entry = readSummary(value) let entry = readSummary(value)
self.cachedSummaries[key] = CachedEntry(summary: entry) self.cachedSummaries[key] = CachedEntry(summary: entry)
return entry return entry
@ -164,10 +174,10 @@ class MessageHistoryTagsSummaryTable: Table {
if let summary = cached.summary { if let summary = cached.summary {
buffer.reset() buffer.reset()
writeSummary(summary, to: buffer) writeSummary(summary, to: buffer)
self.valueBox.set(self.table, key: self.key(key: key, sharedKey: self.sharedKey), value: buffer) self.valueBox.set(self.table, key: self.keyShared(key: key), value: buffer)
} else { } else {
assertionFailure() assertionFailure()
self.valueBox.remove(self.table, key: self.key(key: key, sharedKey: self.sharedKey), secure: false) self.valueBox.remove(self.table, key: self.keyShared(key: key), secure: false)
} }
} else { } else {
assertionFailure() assertionFailure()

View File

@ -46,7 +46,7 @@ class MessageHistoryTagsTable: Table {
for tag in tags { for tag in tags {
self.valueBox.set(self.table, key: self.key(tag: tag, index: index, key: self.sharedKey), value: MemoryBuffer()) self.valueBox.set(self.table, key: self.key(tag: tag, index: index, key: self.sharedKey), value: MemoryBuffer())
if self.summaryTags.contains(tag) { if self.summaryTags.contains(tag) {
self.summaryTable.addMessage(key: MessageHistoryTagsSummaryKey(tag: tag, peerId: index.id.peerId, namespace: index.id.namespace), id: index.id.id, isNewlyAdded: isNewlyAdded, updatedSummaries: &updatedSummaries, invalidateSummaries: &invalidateSummaries) self.summaryTable.addMessage(key: MessageHistoryTagsSummaryKey(tag: tag, peerId: index.id.peerId, threadId: nil, namespace: index.id.namespace), id: index.id.id, isNewlyAdded: isNewlyAdded, updatedSummaries: &updatedSummaries, invalidateSummaries: &invalidateSummaries)
} }
} }
} }
@ -56,7 +56,7 @@ class MessageHistoryTagsTable: Table {
self.valueBox.remove(self.table, key: self.key(tag: tag, index: index, key: self.sharedKey), secure: false) self.valueBox.remove(self.table, key: self.key(tag: tag, index: index, key: self.sharedKey), secure: false)
if self.summaryTags.contains(tag) { if self.summaryTags.contains(tag) {
self.summaryTable.removeMessage(key: MessageHistoryTagsSummaryKey(tag: tag, peerId: index.id.peerId, namespace: index.id.namespace), id: index.id.id, updatedSummaries: &updatedSummaries, invalidateSummaries: &invalidateSummaries) self.summaryTable.removeMessage(key: MessageHistoryTagsSummaryKey(tag: tag, peerId: index.id.peerId, threadId: nil, namespace: index.id.namespace), id: index.id.id, updatedSummaries: &updatedSummaries, invalidateSummaries: &invalidateSummaries)
} }
} }
} }

View File

@ -329,7 +329,7 @@ final class MessageHistoryThreadHoleIndexTable: Table {
self.valueBox.set(self.table, key: self.key(threadId: threadId, id: MessageId(peerId: peerId, namespace: namespace, id: closedRange.upperBound), space: space), value: MemoryBuffer(memory: &lowerBound, capacity: 4, length: 4, freeWhenDone: false)) self.valueBox.set(self.table, key: self.key(threadId: threadId, id: MessageId(peerId: peerId, namespace: namespace, id: closedRange.upperBound), space: space), value: MemoryBuffer(memory: &lowerBound, capacity: 4, length: 4, freeWhenDone: false))
} }
//addOperation(.insert(clippedRange), peerId: peerId, namespace: namespace, space: space, to: &operations) addMessageHistoryHoleOperation(.insert(clippedRange), peerId: peerId, threadId: threadId, namespace: namespace, space: space, to: &operations)
} }
func remove(peerId: PeerId, threadId: Int64, namespace: MessageId.Namespace, space: MessageHistoryHoleSpace, range: ClosedRange<MessageId.Id>, operations: inout [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]]) { func remove(peerId: PeerId, threadId: Int64, namespace: MessageId.Namespace, space: MessageHistoryHoleSpace, range: ClosedRange<MessageId.Id>, operations: inout [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]]) {
@ -337,7 +337,7 @@ final class MessageHistoryThreadHoleIndexTable: Table {
self.removeInternal(peerId: peerId, threadId: threadId, namespace: namespace, space: space, range: range, operations: &operations) self.removeInternal(peerId: peerId, threadId: threadId, namespace: namespace, space: space, range: range, operations: &operations)
/*switch space { switch space {
case .everywhere: case .everywhere:
if let namespaceHoleTags = self.seedConfiguration.messageHoles[peerId.namespace]?[namespace] { if let namespaceHoleTags = self.seedConfiguration.messageHoles[peerId.namespace]?[namespace] {
for tag in namespaceHoleTags { for tag in namespaceHoleTags {
@ -346,7 +346,7 @@ final class MessageHistoryThreadHoleIndexTable: Table {
} }
case .tag: case .tag:
break break
}*/ }
} }
private func removeInternal(peerId: PeerId, threadId: Int64, namespace: MessageId.Namespace, space: MessageHistoryHoleSpace, range: ClosedRange<MessageId.Id>, operations: inout [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]]) { private func removeInternal(peerId: PeerId, threadId: Int64, namespace: MessageId.Namespace, space: MessageHistoryHoleSpace, range: ClosedRange<MessageId.Id>, operations: inout [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]]) {
@ -393,9 +393,9 @@ final class MessageHistoryThreadHoleIndexTable: Table {
self.valueBox.set(self.table, key: self.key(threadId: threadId, id: MessageId(peerId: peerId, namespace: namespace, id: closedRange.upperBound), space: space), value: MemoryBuffer(memory: &lowerBound, capacity: 4, length: 4, freeWhenDone: false)) self.valueBox.set(self.table, key: self.key(threadId: threadId, id: MessageId(peerId: peerId, namespace: namespace, id: closedRange.upperBound), space: space), value: MemoryBuffer(memory: &lowerBound, capacity: 4, length: 4, freeWhenDone: false))
} }
/*if !removeKeys.isEmpty { if !removeKeys.isEmpty {
addOperation(.remove(range), peerId: peerId, namespace: namespace, space: space, to: &operations) addMessageHistoryHoleOperation(.remove(range), peerId: peerId, threadId: threadId, namespace: namespace, space: space, to: &operations)
}*/ }
} }
func debugList(peerId: PeerId, threadId: Int64, namespace: MessageId.Namespace, space: MessageHistoryHoleSpace) -> [ClosedRange<MessageId.Id>] { func debugList(peerId: PeerId, threadId: Int64, namespace: MessageId.Namespace, space: MessageHistoryHoleSpace) -> [ClosedRange<MessageId.Id>] {

View File

@ -5,27 +5,32 @@ final class MutableMessageHistoryThreadIndexView: MutablePostboxView {
let id: Int64 let id: Int64
let index: MessageIndex let index: MessageIndex
var info: CodableEntry var info: CodableEntry
var tagSummaryInfo: [ChatListEntryMessageTagSummaryKey: ChatListMessageTagSummaryInfo]
var topMessage: Message? var topMessage: Message?
init( init(
id: Int64, id: Int64,
index: MessageIndex, index: MessageIndex,
info: CodableEntry, info: CodableEntry,
tagSummaryInfo: [ChatListEntryMessageTagSummaryKey: ChatListMessageTagSummaryInfo],
topMessage: Message? topMessage: Message?
) { ) {
self.id = id self.id = id
self.index = index self.index = index
self.info = info self.info = info
self.tagSummaryInfo = tagSummaryInfo
self.topMessage = topMessage self.topMessage = topMessage
} }
} }
fileprivate let peerId: PeerId fileprivate let peerId: PeerId
fileprivate let summaryComponents: ChatListEntrySummaryComponents
fileprivate var peer: Peer? fileprivate var peer: Peer?
fileprivate var items: [Item] = [] fileprivate var items: [Item] = []
init(postbox: PostboxImpl, peerId: PeerId) { init(postbox: PostboxImpl, peerId: PeerId, summaryComponents: ChatListEntrySummaryComponents) {
self.peerId = peerId self.peerId = peerId
self.summaryComponents = summaryComponents
self.reload(postbox: postbox) self.reload(postbox: postbox)
} }
@ -36,10 +41,34 @@ final class MutableMessageHistoryThreadIndexView: MutablePostboxView {
self.peer = postbox.peerTable.get(self.peerId) self.peer = postbox.peerTable.get(self.peerId)
for item in postbox.messageHistoryThreadIndexTable.getAll(peerId: self.peerId) { for item in postbox.messageHistoryThreadIndexTable.getAll(peerId: self.peerId) {
var tagSummaryInfo: [ChatListEntryMessageTagSummaryKey: ChatListMessageTagSummaryInfo] = [:]
for (key, component) in self.summaryComponents.components {
var tagSummaryCount: Int32?
var actionsSummaryCount: Int32?
if let tagSummary = component.tagSummary {
let key = MessageHistoryTagsSummaryKey(tag: key.tag, peerId: self.peerId, threadId: item.threadId, namespace: tagSummary.namespace)
if let summary = postbox.messageHistoryTagsSummaryTable.get(key) {
tagSummaryCount = summary.count
}
}
if let actionsSummary = component.actionsSummary {
let key = PendingMessageActionsSummaryKey(type: key.actionType, peerId: self.peerId, namespace: actionsSummary.namespace)
actionsSummaryCount = postbox.pendingMessageActionsMetadataTable.getCount(.peerNamespaceAction(key.peerId, key.namespace, key.type))
}
tagSummaryInfo[key] = ChatListMessageTagSummaryInfo(
tagSummaryCount: tagSummaryCount,
actionsSummaryCount: actionsSummaryCount
)
}
self.items.append(Item( self.items.append(Item(
id: item.threadId, id: item.threadId,
index: item.index, index: item.index,
info: item.info, info: item.info,
tagSummaryInfo: tagSummaryInfo,
topMessage: postbox.getMessage(item.index.id) topMessage: postbox.getMessage(item.index.id)
)) ))
} }
@ -48,7 +77,7 @@ final class MutableMessageHistoryThreadIndexView: MutablePostboxView {
func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool { func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool {
var updated = false var updated = false
if transaction.updatedMessageThreadPeerIds.contains(self.peerId) { if transaction.updatedMessageThreadPeerIds.contains(self.peerId) || transaction.currentUpdatedMessageTagSummaries.contains(where: { $0.key.peerId == self.peerId }) || transaction.currentUpdatedMessageActionsSummaries.contains(where: { $0.key.peerId == self.peerId }) {
self.reload(postbox: postbox) self.reload(postbox: postbox)
updated = true updated = true
} }
@ -70,17 +99,20 @@ public final class EngineMessageHistoryThread {
public let id: Int64 public let id: Int64
public let index: MessageIndex public let index: MessageIndex
public let info: CodableEntry public let info: CodableEntry
public let tagSummaryInfo: [ChatListEntryMessageTagSummaryKey: ChatListMessageTagSummaryInfo]
public let topMessage: Message? public let topMessage: Message?
public init( public init(
id: Int64, id: Int64,
index: MessageIndex, index: MessageIndex,
info: CodableEntry, info: CodableEntry,
tagSummaryInfo: [ChatListEntryMessageTagSummaryKey: ChatListMessageTagSummaryInfo],
topMessage: Message? topMessage: Message?
) { ) {
self.id = id self.id = id
self.index = index self.index = index
self.info = info self.info = info
self.tagSummaryInfo = tagSummaryInfo
self.topMessage = topMessage self.topMessage = topMessage
} }
@ -94,6 +126,9 @@ public final class EngineMessageHistoryThread {
if lhs.info != rhs.info { if lhs.info != rhs.info {
return false return false
} }
if lhs.tagSummaryInfo != rhs.tagSummaryInfo {
return false
}
if let lhsMessage = lhs.topMessage, let rhsMessage = rhs.topMessage { if let lhsMessage = lhs.topMessage, let rhsMessage = rhs.topMessage {
if lhsMessage.index != rhsMessage.index { if lhsMessage.index != rhsMessage.index {
return false return false
@ -123,6 +158,7 @@ public final class MessageHistoryThreadIndexView: PostboxView {
id: item.id, id: item.id,
index: item.index, index: item.index,
info: item.info, info: item.info,
tagSummaryInfo: item.tagSummaryInfo,
topMessage: item.topMessage topMessage: item.topMessage
)) ))
} }

View File

@ -1,9 +1,5 @@
import Foundation import Foundation
private func extractKey(_ key: ValueBoxKey) -> MessageIndex {
return MessageIndex(id: MessageId(peerId: PeerId(key.getInt64(0)), namespace: key.getInt32(8 + 8), id: key.getInt32(8 + 8 + 4 + 4)), timestamp: key.getInt32(8 + 8 + 4))
}
class MessageHistoryThreadsTable: Table { class MessageHistoryThreadsTable: Table {
struct ItemId: Hashable { struct ItemId: Hashable {
var peerId: PeerId var peerId: PeerId
@ -31,6 +27,10 @@ class MessageHistoryThreadsTable: Table {
return key return key
} }
private func extractKey(_ key: ValueBoxKey) -> MessageIndex {
return MessageIndex(id: MessageId(peerId: PeerId(key.getInt64(0)), namespace: key.getInt32(8 + 8), id: key.getInt32(8 + 8 + 4 + 4)), timestamp: key.getInt32(8 + 8 + 4))
}
private func lowerBound(threadId: Int64, peerId: PeerId, namespace: MessageId.Namespace) -> ValueBoxKey { private func lowerBound(threadId: Int64, peerId: PeerId, namespace: MessageId.Namespace) -> ValueBoxKey {
let key = ValueBoxKey(length: 8 + 8 + 4) let key = ValueBoxKey(length: 8 + 8 + 4)
key.setInt64(0, value: peerId.toInt64()) key.setInt64(0, value: peerId.toInt64())
@ -146,3 +146,199 @@ class MessageHistoryThreadsTable: Table {
self.updatedIds.removeAll() self.updatedIds.removeAll()
} }
} }
class MessageHistoryThreadTagsTable: Table {
static func tableSpec(_ id: Int32) -> ValueBoxTable {
return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: true)
}
private let summaryTable: MessageHistoryTagsSummaryTable
private let summaryTags: MessageTags
private let sharedKey = ValueBoxKey(length: 8 + 8 + 4 + 4 + 4 + 4)
init(valueBox: ValueBox, table: ValueBoxTable, useCaches: Bool, seedConfiguration: SeedConfiguration, summaryTable: MessageHistoryTagsSummaryTable) {
self.summaryTable = summaryTable
self.summaryTags = seedConfiguration.messageTagsWithSummary
super.init(valueBox: valueBox, table: table, useCaches: useCaches)
}
private func key(tag: MessageTags, threadId: Int64, index: MessageIndex, key: ValueBoxKey = ValueBoxKey(length: 8 + 8 + 4 + 4 + 4 + 4)) -> ValueBoxKey {
key.setInt64(0, value: index.id.peerId.toInt64())
key.setInt64(8, value: threadId)
key.setUInt32(8 + 8, value: tag.rawValue)
key.setInt32(8 + 8 + 4, value: index.id.namespace)
key.setInt32(8 + 8 + 4 + 4, value: index.timestamp)
key.setInt32(8 + 8 + 4 + 4 + 4, value: index.id.id)
return key
}
private func extractKey(_ key: ValueBoxKey) -> MessageIndex {
return MessageIndex(id: MessageId(peerId: PeerId(key.getInt64(0)), namespace: key.getInt32(8 + 8 + 4), id: key.getInt32(8 + 8 + 4 + 4 + 4)), timestamp: key.getInt32(8 + 8 + 4 + 4))
}
private func lowerBound(tag: MessageTags, threadId: Int64, peerId: PeerId, namespace: MessageId.Namespace) -> ValueBoxKey {
let key = ValueBoxKey(length: 8 + 8 + 4 + 4)
key.setInt64(0, value: peerId.toInt64())
key.setInt64(8, value: threadId)
key.setUInt32(8 + 8, value: tag.rawValue)
key.setInt32(8 + 8 + 4, value: namespace)
return key
}
private func upperBound(tag: MessageTags, threadId: Int64, peerId: PeerId, namespace: MessageId.Namespace) -> ValueBoxKey {
return self.lowerBound(tag: tag, threadId: threadId, peerId: peerId, namespace: namespace).successor
}
func add(tags: MessageTags, threadId: Int64, index: MessageIndex, isNewlyAdded: Bool, updatedSummaries: inout[MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation]) {
for tag in tags {
self.valueBox.set(self.table, key: self.key(tag: tag, threadId: threadId, index: index, key: self.sharedKey), value: MemoryBuffer())
if self.summaryTags.contains(tag) {
self.summaryTable.addMessage(key: MessageHistoryTagsSummaryKey(tag: tag, peerId: index.id.peerId, threadId: threadId, namespace: index.id.namespace), id: index.id.id, isNewlyAdded: isNewlyAdded, updatedSummaries: &updatedSummaries, invalidateSummaries: &invalidateSummaries)
}
}
}
func remove(tags: MessageTags, threadId: Int64, index: MessageIndex, updatedSummaries: inout[MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation]) {
for tag in tags {
self.valueBox.remove(self.table, key: self.key(tag: tag, threadId: threadId, index: index, key: self.sharedKey), secure: false)
if self.summaryTags.contains(tag) {
self.summaryTable.removeMessage(key: MessageHistoryTagsSummaryKey(tag: tag, peerId: index.id.peerId, threadId: threadId, namespace: index.id.namespace), id: index.id.id, updatedSummaries: &updatedSummaries, invalidateSummaries: &invalidateSummaries)
}
}
}
func entryExists(tag: MessageTags, threadId: Int64, index: MessageIndex) -> Bool {
return self.valueBox.exists(self.table, key: self.key(tag: tag, threadId: threadId, index: index, key: self.sharedKey))
}
func entryLocation(at index: MessageIndex, threadId: Int64, tag: MessageTags) -> MessageHistoryEntryLocation? {
if let _ = self.valueBox.get(self.table, key: self.key(tag: tag, threadId: threadId, index: index)) {
var greaterCount = 0
self.valueBox.range(self.table, start: self.key(tag: tag, threadId: threadId, index: index), end: self.upperBound(tag: tag, threadId: threadId, peerId: index.id.peerId, namespace: index.id.namespace), keys: { _ in
greaterCount += 1
return true
}, limit: 0)
var lowerCount = 0
self.valueBox.range(self.table, start: self.key(tag: tag, threadId: threadId, index: index), end: self.lowerBound(tag: tag, threadId: threadId, peerId: index.id.peerId, namespace: index.id.namespace), keys: { _ in
lowerCount += 1
return true
}, limit: 0)
return MessageHistoryEntryLocation(index: lowerCount, count: greaterCount + lowerCount + 1)
}
return nil
}
func earlierIndices(tag: MessageTags, threadId: Int64, peerId: PeerId, namespace: MessageId.Namespace, index: MessageIndex?, includeFrom: Bool, minIndex: MessageIndex? = nil, count: Int) -> [MessageIndex] {
var indices: [MessageIndex] = []
let key: ValueBoxKey
if let index = index {
if includeFrom {
key = self.key(tag: tag, threadId: threadId, index: index).successor
} else {
key = self.key(tag: tag, threadId: threadId, index: index)
}
} else {
key = self.upperBound(tag: tag, threadId: threadId, peerId: peerId, namespace: namespace)
}
let endKey: ValueBoxKey
if let minIndex = minIndex {
endKey = self.key(tag: tag, threadId: threadId, index: minIndex)
} else {
endKey = self.lowerBound(tag: tag, threadId: threadId, peerId: peerId, namespace: namespace)
}
self.valueBox.range(self.table, start: key, end: endKey, keys: { key in
indices.append(self.extractKey(key))
return true
}, limit: count)
return indices
}
func laterIndices(tag: MessageTags, threadId: Int64, peerId: PeerId, namespace: MessageId.Namespace, index: MessageIndex?, includeFrom: Bool, count: Int) -> [MessageIndex] {
var indices: [MessageIndex] = []
let key: ValueBoxKey
if let index = index {
if includeFrom {
key = self.key(tag: tag, threadId: threadId, index: index).predecessor
} else {
key = self.key(tag: tag, threadId: threadId, index: index)
}
} else {
key = self.lowerBound(tag: tag, threadId: threadId, peerId: peerId, namespace: namespace)
}
self.valueBox.range(self.table, start: key, end: self.upperBound(tag: tag, threadId: threadId, peerId: peerId, namespace: namespace), keys: { key in
indices.append(self.extractKey(key))
return true
}, limit: count)
return indices
}
func getMessageCountInRange(tag: MessageTags, threadId: Int64, peerId: PeerId, namespace: MessageId.Namespace, lowerBound: MessageIndex, upperBound: MessageIndex) -> Int {
precondition(lowerBound.id.namespace == namespace)
precondition(upperBound.id.namespace == namespace)
var lowerBoundKey = self.key(tag: tag, threadId: threadId, index: lowerBound)
if lowerBound.timestamp > 1 {
lowerBoundKey = lowerBoundKey.predecessor
}
var upperBoundKey = self.key(tag: tag, threadId: threadId, index: upperBound)
if upperBound.timestamp < Int32.max - 1 {
upperBoundKey = upperBoundKey.successor
}
return Int(self.valueBox.count(self.table, start: lowerBoundKey, end: upperBoundKey))
}
func latestIndex(tag: MessageTags, threadId: Int64, peerId: PeerId, namespace: MessageId.Namespace) -> MessageIndex? {
var result: MessageIndex?
self.valueBox.range(self.table, start: self.lowerBound(tag: tag, threadId: threadId, peerId: peerId, namespace: namespace), end: self.upperBound(tag: tag, threadId: threadId, peerId: peerId, namespace: namespace), keys: { key in
result = extractKey(key)
return true
}, limit: 1)
return result
}
func findRandomIndex(peerId: PeerId, threadId: Int64, namespace: MessageId.Namespace, tag: MessageTags, ignoreIds: ([MessageId], Set<MessageId>), isMessage: (MessageIndex) -> Bool) -> MessageIndex? {
var indices: [MessageIndex] = []
self.valueBox.range(self.table, start: self.lowerBound(tag: tag, threadId: threadId, peerId: peerId, namespace: namespace), end: self.upperBound(tag: tag, threadId: threadId, peerId: peerId, namespace: namespace), keys: { key in
indices.append(extractKey(key))
return true
}, limit: 0)
var checkedIndices = Set<Int>()
while checkedIndices.count < indices.count {
let i = Int(arc4random_uniform(UInt32(indices.count)))
if checkedIndices.contains(i) {
continue
}
checkedIndices.insert(i)
let index = indices[i]
if isMessage(index) && !ignoreIds.1.contains(index.id) {
return index
}
}
checkedIndices.removeAll()
let lastId = ignoreIds.0.last
while checkedIndices.count < indices.count {
let i = Int(arc4random_uniform(UInt32(indices.count)))
if checkedIndices.contains(i) {
continue
}
checkedIndices.insert(i)
let index = indices[i]
if isMessage(index) && lastId != index.id {
return index
}
}
return nil
}
func debugGetAllIndices() -> [MessageIndex] {
var indices: [MessageIndex] = []
self.valueBox.scan(self.table, values: { key, value in
indices.append(extractKey(key))
return true
})
return indices
}
}

View File

@ -285,7 +285,7 @@ public final class MessageHistoryViewExternalInput: Equatable {
} }
public enum MessageHistoryViewInput: Equatable { public enum MessageHistoryViewInput: Equatable {
case single(PeerId) case single(peerId: PeerId, threadId: Int64?)
case associated(PeerId, MessageId?) case associated(PeerId, MessageId?)
case external(MessageHistoryViewExternalInput) case external(MessageHistoryViewExternalInput)
} }
@ -362,7 +362,7 @@ final class MutableMessageHistoryView {
switch peerIds { switch peerIds {
case let .associated(peerId, _): case let .associated(peerId, _):
self.isAddedToChatList = postbox.chatListTable.getPeerChatListIndex(peerId: peerId) != nil self.isAddedToChatList = postbox.chatListTable.getPeerChatListIndex(peerId: peerId) != nil
case let .single(peerId): case let .single(peerId, _):
self.isAddedToChatList = postbox.chatListTable.getPeerChatListIndex(peerId: peerId) != nil self.isAddedToChatList = postbox.chatListTable.getPeerChatListIndex(peerId: peerId) != nil
case let .external(input): case let .external(input):
switch input.content { switch input.content {
@ -420,7 +420,7 @@ final class MutableMessageHistoryView {
func updatePeerIds(transaction: PostboxTransaction) { func updatePeerIds(transaction: PostboxTransaction) {
switch self.peerIds { switch self.peerIds {
case let .single(peerId): case let .single(peerId, _):
if let updatedData = transaction.currentUpdatedCachedPeerData[peerId] { if let updatedData = transaction.currentUpdatedCachedPeerData[peerId] {
if updatedData.associatedHistoryMessageId != nil { if updatedData.associatedHistoryMessageId != nil {
self.peerIds = .associated(peerId, updatedData.associatedHistoryMessageId) self.peerIds = .associated(peerId, updatedData.associatedHistoryMessageId)
@ -445,7 +445,7 @@ final class MutableMessageHistoryView {
switch peerIds { switch peerIds {
case let .associated(peerId, _): case let .associated(peerId, _):
self.isAddedToChatList = postbox.chatListTable.getPeerChatListIndex(peerId: peerId) != nil self.isAddedToChatList = postbox.chatListTable.getPeerChatListIndex(peerId: peerId) != nil
case let .single(peerId): case let .single(peerId, _):
self.isAddedToChatList = postbox.chatListTable.getPeerChatListIndex(peerId: peerId) != nil self.isAddedToChatList = postbox.chatListTable.getPeerChatListIndex(peerId: peerId) != nil
case let .external(input): case let .external(input):
switch input.content { switch input.content {
@ -458,7 +458,7 @@ final class MutableMessageHistoryView {
} }
switch self.peerIds { switch self.peerIds {
case let .single(peerId): case let .single(peerId, _):
holePeerIdsSet.insert(peerId) holePeerIdsSet.insert(peerId)
if let value = transaction.currentOperationsByPeerId[peerId] { if let value = transaction.currentOperationsByPeerId[peerId] {
operations.append(value) operations.append(value)
@ -497,7 +497,10 @@ final class MutableMessageHistoryView {
let externalThreadId: Int64? let externalThreadId: Int64?
let isExternal: Bool let isExternal: Bool
switch self.peerIds { switch self.peerIds {
case .single, .associated: case let .single(_, threadId):
externalThreadId = threadId
isExternal = false
case .associated:
externalThreadId = nil externalThreadId = nil
isExternal = false isExternal = false
case let .external(input): case let .external(input):
@ -527,7 +530,7 @@ final class MutableMessageHistoryView {
} }
} }
if matchesSpace { if matchesSpace {
if holePeerIdsSet.contains(key.peerId) { if holePeerIdsSet.contains(key.peerId) && key.threadId == externalThreadId {
for operation in holeOperations { for operation in holeOperations {
switch operation { switch operation {
case let .insert(range): case let .insert(range):
@ -865,9 +868,11 @@ final class MutableMessageHistoryView {
if !transaction.currentPeerHoleOperations.isEmpty { if !transaction.currentPeerHoleOperations.isEmpty {
var holePeerIdsSet: [PeerId] = [] var holePeerIdsSet: [PeerId] = []
var threadId: Int64?
switch self.peerIds { switch self.peerIds {
case let .single(peerId): case let .single(peerId, threadIdValue):
holePeerIdsSet.append(peerId) holePeerIdsSet.append(peerId)
threadId = threadIdValue
case let .associated(peerId, associatedId): case let .associated(peerId, associatedId):
holePeerIdsSet.append(peerId) holePeerIdsSet.append(peerId)
if let associatedId = associatedId { if let associatedId = associatedId {
@ -878,7 +883,7 @@ final class MutableMessageHistoryView {
} }
let space: MessageHistoryHoleSpace = self.tag.flatMap(MessageHistoryHoleSpace.tag) ?? .everywhere let space: MessageHistoryHoleSpace = self.tag.flatMap(MessageHistoryHoleSpace.tag) ?? .everywhere
for key in transaction.currentPeerHoleOperations.keys { for key in transaction.currentPeerHoleOperations.keys {
if holePeerIdsSet.contains(key.peerId) && key.space == space { if holePeerIdsSet.contains(key.peerId) && threadId == key.threadId && key.space == space {
hasChanges = true hasChanges = true
} }
} }
@ -1108,7 +1113,7 @@ public final class MessageHistoryView {
} }
} }
} }
if let maxNamespaceIndex = maxNamespaceIndex , maxIndex == nil || maxIndex! < maxNamespaceIndex { if let maxNamespaceIndex = maxNamespaceIndex, maxIndex == nil || maxIndex! < maxNamespaceIndex {
maxIndex = maxNamespaceIndex maxIndex = maxNamespaceIndex
} }
} }
@ -1177,4 +1182,83 @@ public final class MessageHistoryView {
self.entries = entries self.entries = entries
} }
public init(base: MessageHistoryView, fixed combinedReadStates: MessageHistoryViewReadState?, transient transientReadStates: MessageHistoryViewReadState?) {
self.tagMask = base.tagMask
self.namespaces = base.namespaces
self.anchorIndex = base.anchorIndex
self.earlierId = base.earlierId
self.laterId = base.laterId
self.holeEarlier = base.holeEarlier
self.holeLater = base.holeLater
self.entries = base.entries
self.fixedReadStates = combinedReadStates
self.transientReadStates = transientReadStates
self.topTaggedMessages = base.topTaggedMessages
self.additionalData = base.additionalData
self.isLoading = base.isLoading
self.isAddedToChatList = base.isAddedToChatList
if let combinedReadStates = combinedReadStates {
switch combinedReadStates {
case let .peer(states):
var hasUnread = false
for (_, readState) in states {
if readState.count > 0 {
hasUnread = true
break
}
}
var maxIndex: MessageIndex?
if hasUnread {
var peerIds = Set<PeerId>()
for entry in entries {
peerIds.insert(entry.index.id.peerId)
}
for peerId in peerIds {
if let combinedReadState = states[peerId] {
for (namespace, state) in combinedReadState.states {
var maxNamespaceIndex: MessageIndex?
var index = entries.count - 1
for entry in entries.reversed() {
if entry.index.id.peerId == peerId && entry.index.id.namespace == namespace && state.isIncomingMessageIndexRead(entry.index) {
maxNamespaceIndex = entry.index
break
}
index -= 1
}
if maxNamespaceIndex == nil && index == -1 && entries.count != 0 {
index = 0
for entry in entries {
if entry.index.id.peerId == peerId && entry.index.id.namespace == namespace {
maxNamespaceIndex = entry.index.peerLocalPredecessor()
break
}
index += 1
}
}
if let _ = maxNamespaceIndex , index + 1 < entries.count {
for i in index + 1 ..< entries.count {
if entries[i].message.flags.intersection(.IsIncomingMask).isEmpty {
maxNamespaceIndex = entries[i].message.index
} else {
break
}
}
}
if let maxNamespaceIndex = maxNamespaceIndex, maxIndex == nil || maxIndex! < maxNamespaceIndex {
maxIndex = maxNamespaceIndex
}
}
}
}
}
self.maxReadIndex = maxIndex
}
} else {
self.maxReadIndex = nil
}
}
} }

View File

@ -11,7 +11,7 @@ public enum MessageHistoryInput: Equatable, Hashable {
} }
} }
case automatic(Automatic?) case automatic(threadId: Int64?, info: Automatic?)
case external(MessageHistoryViewExternalInput, MessageTags?) case external(MessageHistoryViewExternalInput, MessageTags?)
public func hash(into hasher: inout Hasher) { public func hash(into hasher: inout Hasher) {
@ -27,8 +27,8 @@ public enum MessageHistoryInput: Equatable, Hashable {
private extension MessageHistoryInput { private extension MessageHistoryInput {
func fetch(postbox: PostboxImpl, peerId: PeerId, namespace: MessageId.Namespace, from fromIndex: MessageIndex, includeFrom: Bool, to toIndex: MessageIndex, ignoreMessagesInTimestampRange: ClosedRange<Int32>?, limit: Int) -> [IntermediateMessage] { func fetch(postbox: PostboxImpl, peerId: PeerId, namespace: MessageId.Namespace, from fromIndex: MessageIndex, includeFrom: Bool, to toIndex: MessageIndex, ignoreMessagesInTimestampRange: ClosedRange<Int32>?, limit: Int) -> [IntermediateMessage] {
switch self { switch self {
case let .automatic(automatic): case let .automatic(threadId, automatic):
var items = postbox.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: automatic?.tag, threadId: nil, from: fromIndex, includeFrom: includeFrom, to: toIndex, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, limit: limit) var items = postbox.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: automatic?.tag, threadId: threadId, from: fromIndex, includeFrom: includeFrom, to: toIndex, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, limit: limit)
if let automatic = automatic, automatic.appendMessagesFromTheSameGroup { if let automatic = automatic, automatic.appendMessagesFromTheSameGroup {
enum Direction { enum Direction {
case lowToHigh case lowToHigh
@ -171,11 +171,19 @@ private extension MessageHistoryInput {
func getMessageCountInRange(postbox: PostboxImpl, peerId: PeerId, namespace: MessageId.Namespace, lowerBound: MessageIndex, upperBound: MessageIndex) -> Int { func getMessageCountInRange(postbox: PostboxImpl, peerId: PeerId, namespace: MessageId.Namespace, lowerBound: MessageIndex, upperBound: MessageIndex) -> Int {
switch self { switch self {
case let .automatic(automatic): case let .automatic(threadId, automatic):
if let automatic = automatic { if let automatic = automatic {
return postbox.messageHistoryTagsTable.getMessageCountInRange(tag: automatic.tag, peerId: peerId, namespace: namespace, lowerBound: lowerBound, upperBound: upperBound) if let threadId = threadId {
return postbox.messageHistoryThreadTagsTable.getMessageCountInRange(tag: automatic.tag, threadId: threadId, peerId: peerId, namespace: namespace, lowerBound: lowerBound, upperBound: upperBound)
} else {
return postbox.messageHistoryTagsTable.getMessageCountInRange(tag: automatic.tag, peerId: peerId, namespace: namespace, lowerBound: lowerBound, upperBound: upperBound)
}
} else { } else {
return postbox.messageHistoryTable.getMessageCountInRange(peerId: peerId, namespace: namespace, tag: nil, lowerBound: lowerBound, upperBound: upperBound) if let threadId = threadId {
return postbox.messageHistoryThreadsTable.getMessageCountInRange(threadId: threadId, peerId: peerId, namespace: namespace, lowerBound: lowerBound, upperBound: upperBound)
} else {
return postbox.messageHistoryTable.getMessageCountInRange(peerId: peerId, namespace: namespace, tag: nil, lowerBound: lowerBound, upperBound: upperBound)
}
} }
case .external: case .external:
return 0 return 0
@ -492,8 +500,9 @@ private func sampleHoleRanges(input: MessageHistoryInput, orderedEntriesBySpace:
var tag: MessageTags? var tag: MessageTags?
var threadId: Int64? var threadId: Int64?
switch input { switch input {
case let .automatic(automatic): case let .automatic(threadIdValue, automatic):
tag = automatic?.tag tag = automatic?.tag
threadId = threadIdValue
case let .external(value, _): case let .external(value, _):
switch value.content { switch value.content {
case let .thread(_, id, _): case let .thread(_, id, _):
@ -1016,9 +1025,9 @@ final class HistoryViewLoadedState {
let input: MessageHistoryInput let input: MessageHistoryInput
switch locations { switch locations {
case let .single(peerId): case let .single(peerId, threadId):
peerIds.append(peerId) peerIds.append(peerId)
input = .automatic(tag.flatMap { tag in input = .automatic(threadId: threadId, info: tag.flatMap { tag in
MessageHistoryInput.Automatic(tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup) MessageHistoryInput.Automatic(tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup)
}) })
case let .associated(peerId, associatedId): case let .associated(peerId, associatedId):
@ -1026,7 +1035,7 @@ final class HistoryViewLoadedState {
if let associatedId = associatedId { if let associatedId = associatedId {
peerIds.append(associatedId.peerId) peerIds.append(associatedId.peerId)
} }
input = .automatic(tag.flatMap { tag in input = .automatic(threadId: nil, info: tag.flatMap { tag in
MessageHistoryInput.Automatic(tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup) MessageHistoryInput.Automatic(tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup)
}) })
case let .external(external): case let .external(external):
@ -1482,9 +1491,11 @@ final class HistoryViewLoadedState {
private func fetchHoles(postbox: PostboxImpl, locations: MessageHistoryViewInput, tag: MessageTags?, namespaces: MessageIdNamespaces) -> [PeerIdAndNamespace: IndexSet] { private func fetchHoles(postbox: PostboxImpl, locations: MessageHistoryViewInput, tag: MessageTags?, namespaces: MessageIdNamespaces) -> [PeerIdAndNamespace: IndexSet] {
var peerIds: [PeerId] = [] var peerIds: [PeerId] = []
var threadId: Int64?
switch locations { switch locations {
case let .single(peerId): case let .single(peerId, threadIdValue):
peerIds.append(peerId) peerIds.append(peerId)
threadId = threadIdValue
case let .associated(peerId, associatedId): case let .associated(peerId, associatedId):
peerIds.append(peerId) peerIds.append(peerId)
if let associatedId = associatedId { if let associatedId = associatedId {
@ -1508,15 +1519,30 @@ private func fetchHoles(postbox: PostboxImpl, locations: MessageHistoryViewInput
var holesBySpace: [PeerIdAndNamespace: IndexSet] = [:] var holesBySpace: [PeerIdAndNamespace: IndexSet] = [:]
let holeSpace = tag.flatMap(MessageHistoryHoleSpace.tag) ?? .everywhere let holeSpace = tag.flatMap(MessageHistoryHoleSpace.tag) ?? .everywhere
for peerId in peerIds { for peerId in peerIds {
for namespace in postbox.messageHistoryHoleIndexTable.existingNamespaces(peerId: peerId, holeSpace: holeSpace) { if let threadId = threadId {
if namespaces.contains(namespace) { for namespace in postbox.messageHistoryThreadHoleIndexTable.existingNamespaces(peerId: peerId, threadId: threadId, holeSpace: holeSpace) {
let indices = postbox.messageHistoryHoleIndexTable.closest(peerId: peerId, namespace: namespace, space: holeSpace, range: 1 ... (Int32.max - 1)) if namespaces.contains(namespace) {
if !indices.isEmpty { let indices = postbox.messageHistoryThreadHoleIndexTable.closest(peerId: peerId, threadId: threadId, namespace: namespace, space: holeSpace, range: 1 ... (Int32.max - 1))
let peerIdAndNamespace = PeerIdAndNamespace(peerId: peerId, namespace: namespace) if !indices.isEmpty {
assert(canContainHoles(peerIdAndNamespace, input: .automatic(tag.flatMap { tag in let peerIdAndNamespace = PeerIdAndNamespace(peerId: peerId, namespace: namespace)
MessageHistoryInput.Automatic(tag: tag, appendMessagesFromTheSameGroup: false) assert(canContainHoles(peerIdAndNamespace, input: .automatic(threadId: threadId, info: tag.flatMap { tag in
}), seedConfiguration: postbox.seedConfiguration)) MessageHistoryInput.Automatic(tag: tag, appendMessagesFromTheSameGroup: false)
holesBySpace[peerIdAndNamespace] = indices }), seedConfiguration: postbox.seedConfiguration))
holesBySpace[peerIdAndNamespace] = indices
}
}
}
} else {
for namespace in postbox.messageHistoryHoleIndexTable.existingNamespaces(peerId: peerId, holeSpace: holeSpace) {
if namespaces.contains(namespace) {
let indices = postbox.messageHistoryHoleIndexTable.closest(peerId: peerId, namespace: namespace, space: holeSpace, range: 1 ... (Int32.max - 1))
if !indices.isEmpty {
let peerIdAndNamespace = PeerIdAndNamespace(peerId: peerId, namespace: namespace)
assert(canContainHoles(peerIdAndNamespace, input: .automatic(threadId: nil, info: tag.flatMap { tag in
MessageHistoryInput.Automatic(tag: tag, appendMessagesFromTheSameGroup: false)
}), seedConfiguration: postbox.seedConfiguration))
holesBySpace[peerIdAndNamespace] = indices
}
} }
} }
} }
@ -1612,8 +1638,12 @@ enum HistoryViewState {
case .unread: case .unread:
let anchorPeerId: PeerId let anchorPeerId: PeerId
switch locations { switch locations {
case let .single(peerId): case let .single(peerId, threadId):
anchorPeerId = peerId anchorPeerId = peerId
if threadId != nil {
self = .loaded(HistoryViewLoadedState(anchor: .upperBound, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag, namespaces: namespaces))))
return
}
case let .associated(peerId, _): case let .associated(peerId, _):
anchorPeerId = peerId anchorPeerId = peerId
case .external: case .external:
@ -1661,6 +1691,8 @@ enum HistoryViewState {
case let .message(messageId): case let .message(messageId):
var threadId: Int64? var threadId: Int64?
switch locations { switch locations {
case let .single(_, threadIdValue):
threadId = threadIdValue
case let .external(input): case let .external(input):
switch input.content { switch input.content {
case let .thread(_, id, _): case let .thread(_, id, _):

View File

@ -25,7 +25,7 @@ public struct MessageOfInterestHole: Hashable, Equatable, CustomStringConvertibl
} }
public enum MessageOfInterestViewLocation: Hashable { public enum MessageOfInterestViewLocation: Hashable {
case peer(PeerId) case peer(peerId: PeerId, threadId: Int64?)
} }
final class MutableMessageOfInterestHolesView: MutablePostboxView { final class MutableMessageOfInterestHolesView: MutablePostboxView {
@ -45,9 +45,9 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView {
let mainPeerId: PeerId let mainPeerId: PeerId
let peerIds: MessageHistoryViewInput let peerIds: MessageHistoryViewInput
switch self.location { switch self.location {
case let .peer(id): case let .peer(id, threadId):
mainPeerId = id mainPeerId = id
peerIds = postbox.peerIdsForLocation(.peer(id), ignoreRelatedChats: false) peerIds = postbox.peerIdsForLocation(.peer(peerId: id, threadId: threadId), ignoreRelatedChats: false)
} }
self.peerIds = peerIds self.peerIds = peerIds
var anchor: HistoryViewInputAnchor = .upperBound var anchor: HistoryViewInputAnchor = .upperBound
@ -107,12 +107,14 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView {
func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool { func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool {
var peerId: PeerId var peerId: PeerId
var threadId: Int64?
switch self.location { switch self.location {
case let .peer(id): case let .peer(id, threadIdValue):
peerId = id peerId = id
threadId = threadIdValue
} }
var anchor: HistoryViewInputAnchor = self.anchor var anchor: HistoryViewInputAnchor = self.anchor
if transaction.alteredInitialPeerCombinedReadStates[peerId] != nil { if threadId == nil, transaction.alteredInitialPeerCombinedReadStates[peerId] != nil {
let updatedAnchor: HistoryViewInputAnchor = .upperBound let updatedAnchor: HistoryViewInputAnchor = .upperBound
if let combinedState = postbox.readStateTable.getCombinedState(peerId), let state = combinedState.states.first, state.1.count != 0 { if let combinedState = postbox.readStateTable.getCombinedState(peerId), let state = combinedState.states.first, state.1.count != 0 {
switch state.1 { switch state.1 {
@ -129,8 +131,8 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView {
self.anchor = anchor self.anchor = anchor
let peerIds: MessageHistoryViewInput let peerIds: MessageHistoryViewInput
switch self.location { switch self.location {
case let .peer(id): case let .peer(id, threadId):
peerIds = postbox.peerIdsForLocation(.peer(id), ignoreRelatedChats: false) peerIds = postbox.peerIdsForLocation(.peer(peerId: id, threadId: threadId), ignoreRelatedChats: false)
} }
self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], clipHoles: true, peerIds: peerIds, ignoreMessagesInTimestampRange: nil, anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: [], getMessageCountInRange: { _, _ in return 0}) self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], clipHoles: true, peerIds: peerIds, ignoreMessagesInTimestampRange: nil, anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: [], getMessageCountInRange: { _, _ in return 0})
return self.updateFromView() return self.updateFromView()
@ -138,9 +140,11 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView {
var reloadView = false var reloadView = false
if !transaction.currentPeerHoleOperations.isEmpty { if !transaction.currentPeerHoleOperations.isEmpty {
var allPeerIds: [PeerId] var allPeerIds: [PeerId]
var threadId: Int64?
switch peerIds { switch peerIds {
case let .single(peerId): case let .single(peerId, threadIdValue):
allPeerIds = [peerId] allPeerIds = [peerId]
threadId = threadIdValue
case let .associated(peerId, attachedMessageId): case let .associated(peerId, attachedMessageId):
allPeerIds = [peerId] allPeerIds = [peerId]
if let attachedMessageId = attachedMessageId { if let attachedMessageId = attachedMessageId {
@ -151,7 +155,7 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView {
break break
} }
for (key, _) in transaction.currentPeerHoleOperations { for (key, _) in transaction.currentPeerHoleOperations {
if allPeerIds.contains(key.peerId) { if allPeerIds.contains(key.peerId) && key.threadId == threadId {
reloadView = true reloadView = true
break break
} }
@ -160,8 +164,8 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView {
if reloadView { if reloadView {
let peerIds: MessageHistoryViewInput let peerIds: MessageHistoryViewInput
switch self.location { switch self.location {
case let .peer(id): case let .peer(id, threadId):
peerIds = postbox.peerIdsForLocation(.peer(id), ignoreRelatedChats: false) peerIds = postbox.peerIdsForLocation(.peer(peerId: id, threadId: threadId), ignoreRelatedChats: false)
} }
self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], clipHoles: true, peerIds: peerIds, ignoreMessagesInTimestampRange: nil, anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: [], getMessageCountInRange: { _, _ in return 0}) self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], clipHoles: true, peerIds: peerIds, ignoreMessagesInTimestampRange: nil, anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: [], getMessageCountInRange: { _, _ in return 0})
} }

View File

@ -67,14 +67,14 @@ public final class Transaction {
} }
} }
public func addHole(peerId: PeerId, namespace: MessageId.Namespace, space: MessageHistoryHoleSpace, range: ClosedRange<MessageId.Id>) { public func addHole(peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace, space: MessageHistoryHoleSpace, range: ClosedRange<MessageId.Id>) {
assert(!self.disposed) assert(!self.disposed)
self.postbox?.addHole(peerId: peerId, namespace: namespace, space: space, range: range) self.postbox?.addHole(peerId: peerId, threadId: threadId, namespace: namespace, space: space, range: range)
} }
public func removeHole(peerId: PeerId, namespace: MessageId.Namespace, space: MessageHistoryHoleSpace, range: ClosedRange<MessageId.Id>) { public func removeHole(peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace, space: MessageHistoryHoleSpace, range: ClosedRange<MessageId.Id>) {
assert(!self.disposed) assert(!self.disposed)
self.postbox?.removeHole(peerId: peerId, namespace: namespace, space: space, range: range) self.postbox?.removeHole(peerId: peerId, threadId: threadId, namespace: namespace, space: space, range: range)
} }
public func getHole(containing id: MessageId) -> [MessageHistoryHoleSpace: ClosedRange<MessageId.Id>] { public func getHole(containing id: MessageId) -> [MessageHistoryHoleSpace: ClosedRange<MessageId.Id>] {
@ -87,16 +87,6 @@ public final class Transaction {
return self.postbox?.messageHistoryHoleIndexTable.closest(peerId: peerId, namespace: namespace, space: .everywhere, range: 1 ... (Int32.max - 1)) ?? IndexSet() return self.postbox?.messageHistoryHoleIndexTable.closest(peerId: peerId, namespace: namespace, space: .everywhere, range: 1 ... (Int32.max - 1)) ?? IndexSet()
} }
public func addThreadIndexHole(peerId: PeerId, threadId: Int64, namespace: MessageId.Namespace, space: MessageHistoryHoleSpace, range: ClosedRange<MessageId.Id>) {
assert(!self.disposed)
self.postbox?.addThreadIndexHole(peerId: peerId, threadId: threadId, namespace: namespace, space: space, range: range)
}
public func removeThreadIndexHole(peerId: PeerId, threadId: Int64, namespace: MessageId.Namespace, space: MessageHistoryHoleSpace, range: ClosedRange<MessageId.Id>) {
assert(!self.disposed)
self.postbox?.removeThreadIndexHole(peerId: peerId, threadId: threadId, namespace: namespace, space: space, range: range)
}
public func getThreadIndexHoles(peerId: PeerId, threadId: Int64, namespace: MessageId.Namespace) -> IndexSet { public func getThreadIndexHoles(peerId: PeerId, threadId: Int64, namespace: MessageId.Namespace) -> IndexSet {
assert(!self.disposed) assert(!self.disposed)
return self.postbox!.messageHistoryThreadHoleIndexTable.closest(peerId: peerId, threadId: threadId, namespace: namespace, space: .everywhere, range: 1 ... (Int32.max - 1)) return self.postbox!.messageHistoryThreadHoleIndexTable.closest(peerId: peerId, threadId: threadId, namespace: namespace, space: .everywhere, range: 1 ... (Int32.max - 1))
@ -137,12 +127,12 @@ public final class Transaction {
self.postbox?.replaceChatListHole(groupId: groupId, index: index, hole: hole) self.postbox?.replaceChatListHole(groupId: groupId, index: index, hole: hole)
} }
public func deleteMessages(_ messageIds: [MessageId], forEachMedia: (Media) -> Void) { public func deleteMessages(_ messageIds: [MessageId], forEachMedia: ((Media) -> Void)?) {
assert(!self.disposed) assert(!self.disposed)
self.postbox?.deleteMessages(messageIds, forEachMedia: forEachMedia) self.postbox?.deleteMessages(messageIds, forEachMedia: forEachMedia)
} }
public func deleteMessagesInRange(peerId: PeerId, namespace: MessageId.Namespace, minId: MessageId.Id, maxId: MessageId.Id, forEachMedia: (Media) -> Void) { public func deleteMessagesInRange(peerId: PeerId, namespace: MessageId.Namespace, minId: MessageId.Id, maxId: MessageId.Id, forEachMedia: ((Media) -> Void)?) {
assert(!self.disposed) assert(!self.disposed)
self.postbox?.deleteMessagesInRange(peerId: peerId, namespace: namespace, minId: minId, maxId: maxId, forEachMedia: forEachMedia) self.postbox?.deleteMessagesInRange(peerId: peerId, namespace: namespace, minId: minId, maxId: maxId, forEachMedia: forEachMedia)
} }
@ -151,12 +141,12 @@ public final class Transaction {
self.postbox?.withAllMessages(peerId: peerId, namespace: namespace, f) self.postbox?.withAllMessages(peerId: peerId, namespace: namespace, f)
} }
public func clearHistory(_ peerId: PeerId, threadId: Int64?, minTimestamp: Int32?, maxTimestamp: Int32?, namespaces: MessageIdNamespaces, forEachMedia: (Media) -> Void) { public func clearHistory(_ peerId: PeerId, threadId: Int64?, minTimestamp: Int32?, maxTimestamp: Int32?, namespaces: MessageIdNamespaces, forEachMedia: ((Media) -> Void)?) {
assert(!self.disposed) assert(!self.disposed)
self.postbox?.clearHistory(peerId, threadId: threadId, minTimestamp: minTimestamp, maxTimestamp: maxTimestamp, namespaces: namespaces, forEachMedia: forEachMedia) self.postbox?.clearHistory(peerId, threadId: threadId, minTimestamp: minTimestamp, maxTimestamp: maxTimestamp, namespaces: namespaces, forEachMedia: forEachMedia)
} }
public func removeAllMessagesWithAuthor(_ peerId: PeerId, authorId: PeerId, namespace: MessageId.Namespace, forEachMedia: (Media) -> Void) { public func removeAllMessagesWithAuthor(_ peerId: PeerId, authorId: PeerId, namespace: MessageId.Namespace, forEachMedia: ((Media) -> Void)?) {
assert(!self.disposed) assert(!self.disposed)
self.postbox?.removeAllMessagesWithAuthor(peerId, authorId: authorId, namespace: namespace, forEachMedia: forEachMedia) self.postbox?.removeAllMessagesWithAuthor(peerId, authorId: authorId, namespace: namespace, forEachMedia: forEachMedia)
} }
@ -166,7 +156,7 @@ public final class Transaction {
self.postbox?.removeAllMessagesWithGlobalTag(tag: tag) self.postbox?.removeAllMessagesWithGlobalTag(tag: tag)
} }
public func removeAllMessagesWithForwardAuthor(_ peerId: PeerId, forwardAuthorId: PeerId, namespace: MessageId.Namespace, forEachMedia: (Media) -> Void) { public func removeAllMessagesWithForwardAuthor(_ peerId: PeerId, forwardAuthorId: PeerId, namespace: MessageId.Namespace, forEachMedia: ((Media) -> Void)?) {
assert(!self.disposed) assert(!self.disposed)
self.postbox?.removeAllMessagesWithForwardAuthor(peerId, forwardAuthorId: forwardAuthorId, namespace: namespace, forEachMedia: forEachMedia) self.postbox?.removeAllMessagesWithForwardAuthor(peerId, forwardAuthorId: forwardAuthorId, namespace: namespace, forEachMedia: forEachMedia)
} }
@ -188,7 +178,7 @@ public final class Transaction {
} }
} }
public func deleteMessagesWithGlobalIds(_ ids: [Int32], forEachMedia: (Media) -> Void) { public func deleteMessagesWithGlobalIds(_ ids: [Int32], forEachMedia: ((Media) -> Void)?) {
assert(!self.disposed) assert(!self.disposed)
if let postbox = self.postbox { if let postbox = self.postbox {
let messageIds = postbox.messageIdsForGlobalIds(ids) let messageIds = postbox.messageIdsForGlobalIds(ids)
@ -229,7 +219,7 @@ public final class Transaction {
public func applyInteractiveReadMaxIndex(_ messageIndex: MessageIndex) -> [MessageId] { public func applyInteractiveReadMaxIndex(_ messageIndex: MessageIndex) -> [MessageId] {
assert(!self.disposed) assert(!self.disposed)
if let postbox = self.postbox { if let postbox = self.postbox {
return postbox.applyInteractiveReadMaxIndex(messageIndex) return postbox.applyInteractiveReadMaxIndex(messageIndex: messageIndex)
} else { } else {
return [] return []
} }
@ -328,7 +318,7 @@ public final class Transaction {
return self.postbox?.readStateTable.getCombinedState(id) return self.postbox?.readStateTable.getCombinedState(id)
} }
public func getPeerNotificationSettings(_ id: PeerId) -> PeerNotificationSettings? { public func getPeerNotificationSettings(id: PeerId) -> PeerNotificationSettings? {
assert(!self.disposed) assert(!self.disposed)
return self.postbox?.peerNotificationSettingsTable.getEffective(id) return self.postbox?.peerNotificationSettingsTable.getEffective(id)
} }
@ -894,7 +884,7 @@ public final class Transaction {
let notificationsPeerId = peer.notificationSettingsPeerId ?? peerId let notificationsPeerId = peer.notificationSettingsPeerId ?? peerId
let isContact = postbox.contactsTable.isContact(peerId: notificationsPeerId) let isContact = postbox.contactsTable.isContact(peerId: notificationsPeerId)
let isRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettings, peer: peer, peerSettings: postbox.peerNotificationSettingsTable.getEffective(notificationsPeerId)) let isRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettings, peer: peer, peerSettings: postbox.peerNotificationSettingsTable.getEffective(notificationsPeerId))
let messageTagSummaryResult = resolveChatListMessageTagSummaryResultCalculation(postbox: postbox, peerId: peer.id, calculation: predicate.messageTagSummary) let messageTagSummaryResult = resolveChatListMessageTagSummaryResultCalculation(postbox: postbox, peerId: peer.id, threadId: nil, calculation: predicate.messageTagSummary)
if predicate.includes(peer: peer, groupId: groupId, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, isUnread: isUnread, isContact: isContact, messageTagSummaryResult: messageTagSummaryResult) { if predicate.includes(peer: peer, groupId: groupId, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, isUnread: isUnread, isContact: isContact, messageTagSummaryResult: messageTagSummaryResult) {
includedPeerIds[peer.id] = true includedPeerIds[peer.id] = true
@ -1002,14 +992,14 @@ public final class Transaction {
return self.postbox?.getPendingMessageAction(type: type, id: id) return self.postbox?.getPendingMessageAction(type: type, id: id)
} }
public func getMessageTagSummary(peerId: PeerId, tagMask: MessageTags, namespace: MessageId.Namespace) -> MessageHistoryTagNamespaceSummary? { public func getMessageTagSummary(peerId: PeerId, threadId: Int64?, tagMask: MessageTags, namespace: MessageId.Namespace) -> MessageHistoryTagNamespaceSummary? {
assert(!self.disposed) assert(!self.disposed)
return self.postbox?.messageHistoryTagsSummaryTable.get(MessageHistoryTagsSummaryKey(tag: tagMask, peerId: peerId, namespace: namespace)) return self.postbox?.messageHistoryTagsSummaryTable.get(MessageHistoryTagsSummaryKey(tag: tagMask, peerId: peerId, threadId: threadId, namespace: namespace))
} }
public func replaceMessageTagSummary(peerId: PeerId, tagMask: MessageTags, namespace: MessageId.Namespace, count: Int32, maxId: MessageId.Id) { public func replaceMessageTagSummary(peerId: PeerId, threadId: Int64?, tagMask: MessageTags, namespace: MessageId.Namespace, count: Int32, maxId: MessageId.Id) {
assert(!self.disposed) assert(!self.disposed)
self.postbox?.replaceMessageTagSummary(peerId: peerId, tagMask: tagMask, namespace: namespace, count: count, maxId: maxId) self.postbox?.replaceMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: tagMask, namespace: namespace, count: count, maxId: maxId)
} }
public func getPendingMessageActionsSummary(peerId: PeerId, type: PendingMessageActionType, namespace: MessageId.Namespace) -> Int32? { public func getPendingMessageActionsSummary(peerId: PeerId, type: PendingMessageActionType, namespace: MessageId.Namespace) -> Int32? {
@ -1017,12 +1007,16 @@ public final class Transaction {
return self.postbox?.pendingMessageActionsMetadataTable.getCount(.peerNamespaceAction(peerId, namespace, type)) return self.postbox?.pendingMessageActionsMetadataTable.getCount(.peerNamespaceAction(peerId, namespace, type))
} }
public func getMessageIndicesWithTag(peerId: PeerId, namespace: MessageId.Namespace, tag: MessageTags) -> [MessageIndex] { public func getMessageIndicesWithTag(peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace, tag: MessageTags) -> [MessageIndex] {
assert(!self.disposed) assert(!self.disposed)
guard let postbox = self.postbox else { guard let postbox = self.postbox else {
return [] return []
} }
return postbox.messageHistoryTagsTable.earlierIndices(tag: tag, peerId: peerId, namespace: namespace, index: nil, includeFrom: false, count: 1000) if let threadId = threadId {
return postbox.messageHistoryThreadTagsTable.earlierIndices(tag: tag, threadId: threadId, peerId: peerId, namespace: namespace, index: nil, includeFrom: false, count: 1000)
} else {
return postbox.messageHistoryTagsTable.earlierIndices(tag: tag, peerId: peerId, namespace: namespace, index: nil, includeFrom: false, count: 1000)
}
} }
public func getMessagesWithThreadId(peerId: PeerId, namespace: MessageId.Namespace, threadId: Int64, from: MessageIndex, includeFrom: Bool, to: MessageIndex, limit: Int) -> [Message] { public func getMessagesWithThreadId(peerId: PeerId, namespace: MessageId.Namespace, threadId: Int64, from: MessageIndex, includeFrom: Bool, to: MessageIndex, limit: Int) -> [Message] {
@ -1447,6 +1441,7 @@ final class PostboxImpl {
let messageHistoryFailedTable: MessageHistoryFailedTable let messageHistoryFailedTable: MessageHistoryFailedTable
let messageHistoryTagsTable: MessageHistoryTagsTable let messageHistoryTagsTable: MessageHistoryTagsTable
let messageHistoryThreadsTable: MessageHistoryThreadsTable let messageHistoryThreadsTable: MessageHistoryThreadsTable
let messageHistoryThreadTagsTable: MessageHistoryThreadTagsTable
let messageHistoryThreadHoleIndexTable: MessageHistoryThreadHoleIndexTable let messageHistoryThreadHoleIndexTable: MessageHistoryThreadHoleIndexTable
let messageHistoryThreadReverseIndexTable: MessageHistoryThreadReverseIndexTable let messageHistoryThreadReverseIndexTable: MessageHistoryThreadReverseIndexTable
let messageHistoryThreadIndexTable: MessageHistoryThreadIndexTable let messageHistoryThreadIndexTable: MessageHistoryThreadIndexTable
@ -1524,6 +1519,7 @@ final class PostboxImpl {
self.pendingMessageActionsTable = PendingMessageActionsTable(valueBox: self.valueBox, table: PendingMessageActionsTable.tableSpec(46), useCaches: useCaches, metadataTable: self.pendingMessageActionsMetadataTable) self.pendingMessageActionsTable = PendingMessageActionsTable(valueBox: self.valueBox, table: PendingMessageActionsTable.tableSpec(46), useCaches: useCaches, metadataTable: self.pendingMessageActionsMetadataTable)
self.messageHistoryTagsTable = MessageHistoryTagsTable(valueBox: self.valueBox, table: MessageHistoryTagsTable.tableSpec(12), useCaches: useCaches, seedConfiguration: self.seedConfiguration, summaryTable: self.messageHistoryTagsSummaryTable) self.messageHistoryTagsTable = MessageHistoryTagsTable(valueBox: self.valueBox, table: MessageHistoryTagsTable.tableSpec(12), useCaches: useCaches, seedConfiguration: self.seedConfiguration, summaryTable: self.messageHistoryTagsSummaryTable)
self.messageHistoryThreadsTable = MessageHistoryThreadsTable(valueBox: self.valueBox, table: MessageHistoryThreadsTable.tableSpec(62), useCaches: useCaches) self.messageHistoryThreadsTable = MessageHistoryThreadsTable(valueBox: self.valueBox, table: MessageHistoryThreadsTable.tableSpec(62), useCaches: useCaches)
self.messageHistoryThreadTagsTable = MessageHistoryThreadTagsTable(valueBox: self.valueBox, table: MessageHistoryThreadTagsTable.tableSpec(71), useCaches: useCaches, seedConfiguration: self.seedConfiguration, summaryTable: self.messageHistoryTagsSummaryTable)
self.messageHistoryThreadHoleIndexTable = MessageHistoryThreadHoleIndexTable(valueBox: self.valueBox, table: MessageHistoryThreadHoleIndexTable.tableSpec(63), useCaches: useCaches, metadataTable: self.messageHistoryMetadataTable, seedConfiguration: self.seedConfiguration) self.messageHistoryThreadHoleIndexTable = MessageHistoryThreadHoleIndexTable(valueBox: self.valueBox, table: MessageHistoryThreadHoleIndexTable.tableSpec(63), useCaches: useCaches, metadataTable: self.messageHistoryMetadataTable, seedConfiguration: self.seedConfiguration)
self.messageHistoryThreadReverseIndexTable = MessageHistoryThreadReverseIndexTable(valueBox: self.valueBox, table: MessageHistoryThreadReverseIndexTable.tableSpec(69), useCaches: useCaches) self.messageHistoryThreadReverseIndexTable = MessageHistoryThreadReverseIndexTable(valueBox: self.valueBox, table: MessageHistoryThreadReverseIndexTable.tableSpec(69), useCaches: useCaches)
self.messageHistoryThreadIndexTable = MessageHistoryThreadIndexTable(valueBox: self.valueBox, table: MessageHistoryThreadIndexTable.tableSpec(70), reverseIndexTable: self.messageHistoryThreadReverseIndexTable, useCaches: useCaches) self.messageHistoryThreadIndexTable = MessageHistoryThreadIndexTable(valueBox: self.valueBox, table: MessageHistoryThreadIndexTable.tableSpec(70), reverseIndexTable: self.messageHistoryThreadReverseIndexTable, useCaches: useCaches)
@ -1538,7 +1534,7 @@ final class PostboxImpl {
self.timestampBasedMessageAttributesTable = TimestampBasedMessageAttributesTable(valueBox: self.valueBox, table: TimestampBasedMessageAttributesTable.tableSpec(34), useCaches: useCaches, indexTable: self.timestampBasedMessageAttributesIndexTable) self.timestampBasedMessageAttributesTable = TimestampBasedMessageAttributesTable(valueBox: self.valueBox, table: TimestampBasedMessageAttributesTable.tableSpec(34), useCaches: useCaches, indexTable: self.timestampBasedMessageAttributesIndexTable)
self.textIndexTable = MessageHistoryTextIndexTable(valueBox: self.valueBox, table: MessageHistoryTextIndexTable.tableSpec(41)) self.textIndexTable = MessageHistoryTextIndexTable(valueBox: self.valueBox, table: MessageHistoryTextIndexTable.tableSpec(41))
self.additionalChatListItemsTable = AdditionalChatListItemsTable(valueBox: self.valueBox, table: AdditionalChatListItemsTable.tableSpec(55), useCaches: useCaches) self.additionalChatListItemsTable = AdditionalChatListItemsTable(valueBox: self.valueBox, table: AdditionalChatListItemsTable.tableSpec(55), useCaches: useCaches)
self.messageHistoryTable = MessageHistoryTable(valueBox: self.valueBox, table: MessageHistoryTable.tableSpec(7), useCaches: useCaches, seedConfiguration: seedConfiguration, messageHistoryIndexTable: self.messageHistoryIndexTable, messageHistoryHoleIndexTable: self.messageHistoryHoleIndexTable, messageMediaTable: self.mediaTable, historyMetadataTable: self.messageHistoryMetadataTable, globallyUniqueMessageIdsTable: self.globallyUniqueMessageIdsTable, unsentTable: self.messageHistoryUnsentTable, failedTable: self.messageHistoryFailedTable, tagsTable: self.messageHistoryTagsTable, threadsTable: self.messageHistoryThreadsTable, globalTagsTable: self.globalMessageHistoryTagsTable, localTagsTable: self.localMessageHistoryTagsTable, timeBasedAttributesTable: self.timestampBasedMessageAttributesTable, readStateTable: self.readStateTable, synchronizeReadStateTable: self.synchronizeReadStateTable, textIndexTable: self.textIndexTable, summaryTable: self.messageHistoryTagsSummaryTable, pendingActionsTable: self.pendingMessageActionsTable) self.messageHistoryTable = MessageHistoryTable(valueBox: self.valueBox, table: MessageHistoryTable.tableSpec(7), useCaches: useCaches, seedConfiguration: seedConfiguration, messageHistoryIndexTable: self.messageHistoryIndexTable, messageHistoryHoleIndexTable: self.messageHistoryHoleIndexTable, messageMediaTable: self.mediaTable, historyMetadataTable: self.messageHistoryMetadataTable, globallyUniqueMessageIdsTable: self.globallyUniqueMessageIdsTable, unsentTable: self.messageHistoryUnsentTable, failedTable: self.messageHistoryFailedTable, tagsTable: self.messageHistoryTagsTable, threadsTable: self.messageHistoryThreadsTable, threadTagsTable: self.messageHistoryThreadTagsTable, globalTagsTable: self.globalMessageHistoryTagsTable, localTagsTable: self.localMessageHistoryTagsTable, timeBasedAttributesTable: self.timestampBasedMessageAttributesTable, readStateTable: self.readStateTable, synchronizeReadStateTable: self.synchronizeReadStateTable, textIndexTable: self.textIndexTable, summaryTable: self.messageHistoryTagsSummaryTable, pendingActionsTable: self.pendingMessageActionsTable)
self.peerChatStateTable = PeerChatStateTable(valueBox: self.valueBox, table: PeerChatStateTable.tableSpec(13), useCaches: useCaches) self.peerChatStateTable = PeerChatStateTable(valueBox: self.valueBox, table: PeerChatStateTable.tableSpec(13), useCaches: useCaches)
self.peerNameTokenIndexTable = ReverseIndexReferenceTable<PeerIdReverseIndexReference>(valueBox: self.valueBox, table: ReverseIndexReferenceTable<PeerIdReverseIndexReference>.tableSpec(26), useCaches: useCaches) self.peerNameTokenIndexTable = ReverseIndexReferenceTable<PeerIdReverseIndexReference>(valueBox: self.valueBox, table: ReverseIndexReferenceTable<PeerIdReverseIndexReference>.tableSpec(26), useCaches: useCaches)
self.peerNameIndexTable = PeerNameIndexTable(valueBox: self.valueBox, table: PeerNameIndexTable.tableSpec(27), useCaches: useCaches, peerTable: self.peerTable, peerNameTokenIndexTable: self.peerNameTokenIndexTable) self.peerNameIndexTable = PeerNameIndexTable(valueBox: self.valueBox, table: PeerNameIndexTable.tableSpec(27), useCaches: useCaches, peerTable: self.peerTable, peerNameTokenIndexTable: self.peerNameTokenIndexTable)
@ -1582,6 +1578,7 @@ final class PostboxImpl {
tables.append(self.messageHistoryFailedTable) tables.append(self.messageHistoryFailedTable)
tables.append(self.messageHistoryTagsTable) tables.append(self.messageHistoryTagsTable)
tables.append(self.messageHistoryThreadsTable) tables.append(self.messageHistoryThreadsTable)
tables.append(self.messageHistoryThreadTagsTable)
tables.append(self.messageHistoryThreadHoleIndexTable) tables.append(self.messageHistoryThreadHoleIndexTable)
tables.append(self.messageHistoryThreadReverseIndexTable) tables.append(self.messageHistoryThreadReverseIndexTable)
tables.append(self.messageHistoryThreadIndexTable) tables.append(self.messageHistoryThreadIndexTable)
@ -1818,20 +1815,20 @@ final class PostboxImpl {
} }
} }
fileprivate func addHole(peerId: PeerId, namespace: MessageId.Namespace, space: MessageHistoryHoleSpace, range: ClosedRange<MessageId.Id>) { fileprivate func addHole(peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace, space: MessageHistoryHoleSpace, range: ClosedRange<MessageId.Id>) {
self.messageHistoryHoleIndexTable.add(peerId: peerId, namespace: namespace, space: space, range: range, operations: &self.currentPeerHoleOperations) if let threadId = threadId {
self.messageHistoryThreadHoleIndexTable.add(peerId: peerId, threadId: threadId, namespace: namespace, space: space, range: range, operations: &self.currentPeerHoleOperations)
} else {
self.messageHistoryHoleIndexTable.add(peerId: peerId, namespace: namespace, space: space, range: range, operations: &self.currentPeerHoleOperations)
}
} }
fileprivate func removeHole(peerId: PeerId, namespace: MessageId.Namespace, space: MessageHistoryHoleSpace, range: ClosedRange<MessageId.Id>) { fileprivate func removeHole(peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace, space: MessageHistoryHoleSpace, range: ClosedRange<MessageId.Id>) {
self.messageHistoryHoleIndexTable.remove(peerId: peerId, namespace: namespace, space: space, range: range, operations: &self.currentPeerHoleOperations) if let threadId = threadId {
} self.messageHistoryThreadHoleIndexTable.remove(peerId: peerId, threadId: threadId, namespace: namespace, space: space, range: range, operations: &self.currentPeerHoleOperations)
} else {
fileprivate func addThreadIndexHole(peerId: PeerId, threadId: Int64, namespace: MessageId.Namespace, space: MessageHistoryHoleSpace, range: ClosedRange<MessageId.Id>) { self.messageHistoryHoleIndexTable.remove(peerId: peerId, namespace: namespace, space: space, range: range, operations: &self.currentPeerHoleOperations)
self.messageHistoryThreadHoleIndexTable.add(peerId: peerId, threadId: threadId, namespace: namespace, space: space, range: range, operations: &self.currentPeerHoleOperations) }
}
fileprivate func removeThreadIndexHole(peerId: PeerId, threadId: Int64, namespace: MessageId.Namespace, space: MessageHistoryHoleSpace, range: ClosedRange<MessageId.Id>) {
self.messageHistoryThreadHoleIndexTable.remove(peerId: peerId, threadId: threadId, namespace: namespace, space: space, range: range, operations: &self.currentPeerHoleOperations)
} }
fileprivate func recalculateChatListGroupStats(groupId: PeerGroupId) { fileprivate func recalculateChatListGroupStats(groupId: PeerGroupId) {
@ -1844,11 +1841,11 @@ final class PostboxImpl {
self.chatListTable.replaceHole(groupId: groupId, index: index, hole: hole, operations: &self.currentChatListOperations) self.chatListTable.replaceHole(groupId: groupId, index: index, hole: hole, operations: &self.currentChatListOperations)
} }
fileprivate func deleteMessages(_ messageIds: [MessageId], forEachMedia: (Media) -> Void) { fileprivate func deleteMessages(_ messageIds: [MessageId], forEachMedia: ((Media) -> Void)?) {
self.messageHistoryTable.removeMessages(messageIds, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: &currentUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, timestampBasedMessageAttributesOperations: &self.currentTimestampBasedMessageAttributesOperations, forEachMedia: forEachMedia) self.messageHistoryTable.removeMessages(messageIds, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: &currentUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, timestampBasedMessageAttributesOperations: &self.currentTimestampBasedMessageAttributesOperations, forEachMedia: forEachMedia)
} }
fileprivate func deleteMessagesInRange(peerId: PeerId, namespace: MessageId.Namespace, minId: MessageId.Id, maxId: MessageId.Id, forEachMedia: (Media) -> Void) { fileprivate func deleteMessagesInRange(peerId: PeerId, namespace: MessageId.Namespace, minId: MessageId.Id, maxId: MessageId.Id, forEachMedia: ((Media) -> Void)?) {
self.messageHistoryTable.removeMessagesInRange(peerId: peerId, namespace: namespace, minId: minId, maxId: maxId, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: &currentUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, timestampBasedMessageAttributesOperations: &self.currentTimestampBasedMessageAttributesOperations, forEachMedia: forEachMedia) self.messageHistoryTable.removeMessagesInRange(peerId: peerId, namespace: namespace, minId: minId, maxId: maxId, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: &currentUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, timestampBasedMessageAttributesOperations: &self.currentTimestampBasedMessageAttributesOperations, forEachMedia: forEachMedia)
} }
@ -1864,7 +1861,7 @@ final class PostboxImpl {
} }
} }
fileprivate func clearHistory(_ peerId: PeerId, threadId: Int64?, minTimestamp: Int32?, maxTimestamp: Int32?, namespaces: MessageIdNamespaces, forEachMedia: (Media) -> Void) { fileprivate func clearHistory(_ peerId: PeerId, threadId: Int64?, minTimestamp: Int32?, maxTimestamp: Int32?, namespaces: MessageIdNamespaces, forEachMedia: ((Media) -> Void)?) {
if let minTimestamp = minTimestamp, let maxTimestamp = maxTimestamp { if let minTimestamp = minTimestamp, let maxTimestamp = maxTimestamp {
self.messageHistoryTable.clearHistoryInRange(peerId: peerId, threadId: threadId, minTimestamp: minTimestamp, maxTimestamp: maxTimestamp, namespaces: namespaces, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: &currentUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, timestampBasedMessageAttributesOperations: &self.currentTimestampBasedMessageAttributesOperations, forEachMedia: forEachMedia) self.messageHistoryTable.clearHistoryInRange(peerId: peerId, threadId: threadId, minTimestamp: minTimestamp, maxTimestamp: maxTimestamp, namespaces: namespaces, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: &currentUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, timestampBasedMessageAttributesOperations: &self.currentTimestampBasedMessageAttributesOperations, forEachMedia: forEachMedia)
} else { } else {
@ -1875,7 +1872,7 @@ final class PostboxImpl {
} }
} }
fileprivate func removeAllMessagesWithAuthor(_ peerId: PeerId, authorId: PeerId, namespace: MessageId.Namespace, forEachMedia: (Media) -> Void) { fileprivate func removeAllMessagesWithAuthor(_ peerId: PeerId, authorId: PeerId, namespace: MessageId.Namespace, forEachMedia: ((Media) -> Void)?) {
self.messageHistoryTable.removeAllMessagesWithAuthor(peerId: peerId, authorId: authorId, namespace: namespace, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: &currentUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, timestampBasedMessageAttributesOperations: &self.currentTimestampBasedMessageAttributesOperations, forEachMedia: forEachMedia) self.messageHistoryTable.removeAllMessagesWithAuthor(peerId: peerId, authorId: authorId, namespace: namespace, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: &currentUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, timestampBasedMessageAttributesOperations: &self.currentTimestampBasedMessageAttributesOperations, forEachMedia: forEachMedia)
} }
@ -1883,7 +1880,7 @@ final class PostboxImpl {
self.messageHistoryTable.removeAllMessagesWithGlobalTag(tag: tag, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: &currentUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, timestampBasedMessageAttributesOperations: &self.currentTimestampBasedMessageAttributesOperations, forEachMedia: { _ in }) self.messageHistoryTable.removeAllMessagesWithGlobalTag(tag: tag, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: &currentUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, timestampBasedMessageAttributesOperations: &self.currentTimestampBasedMessageAttributesOperations, forEachMedia: { _ in })
} }
fileprivate func removeAllMessagesWithForwardAuthor(_ peerId: PeerId, forwardAuthorId: PeerId, namespace: MessageId.Namespace, forEachMedia: (Media) -> Void) { fileprivate func removeAllMessagesWithForwardAuthor(_ peerId: PeerId, forwardAuthorId: PeerId, namespace: MessageId.Namespace, forEachMedia: ((Media) -> Void)?) {
self.messageHistoryTable.removeAllMessagesWithForwardAuthor(peerId: peerId, forwardAuthorId: forwardAuthorId, namespace: namespace, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: &currentUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, timestampBasedMessageAttributesOperations: &self.currentTimestampBasedMessageAttributesOperations, forEachMedia: forEachMedia) self.messageHistoryTable.removeAllMessagesWithForwardAuthor(peerId: peerId, forwardAuthorId: forwardAuthorId, namespace: namespace, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia, unsentMessageOperations: &currentUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations, globalTagsOperations: &self.currentGlobalTagsOperations, pendingActionsOperations: &self.currentPendingMessageActionsOperations, updatedMessageActionsSummaries: &self.currentUpdatedMessageActionsSummaries, updatedMessageTagSummaries: &self.currentUpdatedMessageTagSummaries, invalidateMessageTagSummaries: &self.currentInvalidateMessageTagSummaries, localTagsOperations: &self.currentLocalTagsOperations, timestampBasedMessageAttributesOperations: &self.currentTimestampBasedMessageAttributesOperations, forEachMedia: forEachMedia)
} }
@ -1907,17 +1904,17 @@ final class PostboxImpl {
self.messageHistoryTable.applyOutgoingReadMaxId(messageId, operationsByPeerId: &self.currentOperationsByPeerId, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations) self.messageHistoryTable.applyOutgoingReadMaxId(messageId, operationsByPeerId: &self.currentOperationsByPeerId, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations)
} }
fileprivate func applyInteractiveReadMaxIndex(_ messageIndex: MessageIndex) -> [MessageId] { fileprivate func applyInteractiveReadMaxIndex(messageIndex: MessageIndex) -> [MessageId] {
let peerIds = self.peerIdsForLocation(.peer(messageIndex.id.peerId), ignoreRelatedChats: false) let peerIds = self.peerIdsForLocation(.peer(peerId: messageIndex.id.peerId, threadId: nil), ignoreRelatedChats: false)
switch peerIds { switch peerIds {
case let .associated(_, messageId): case let .associated(_, messageId):
if let messageId = messageId, let readState = self.readStateTable.getCombinedState(messageId.peerId), readState.count != 0 { if let messageId = messageId, let readState = self.readStateTable.getCombinedState(messageId.peerId), readState.count != 0 {
if let topMessage = self.messageHistoryTable.topMessage(peerId: messageId.peerId) { if let topMessage = self.messageHistoryTable.topMessage(peerId: messageId.peerId) {
let _ = self.messageHistoryTable.applyInteractiveMaxReadIndex(postbox: self, messageIndex: topMessage.index, operationsByPeerId: &self.currentOperationsByPeerId, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations) let _ = self.messageHistoryTable.applyInteractiveMaxReadIndex(postbox: self, messageIndex: topMessage.index, operationsByPeerId: &self.currentOperationsByPeerId, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations)
}
} }
default: }
break default:
break
} }
let initialCombinedStates = self.readStateTable.getCombinedState(messageIndex.id.peerId) let initialCombinedStates = self.readStateTable.getCombinedState(messageIndex.id.peerId)
var resultIds = self.messageHistoryTable.applyInteractiveMaxReadIndex(postbox: self, messageIndex: messageIndex, operationsByPeerId: &self.currentOperationsByPeerId, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations) var resultIds = self.messageHistoryTable.applyInteractiveMaxReadIndex(postbox: self, messageIndex: messageIndex, operationsByPeerId: &self.currentOperationsByPeerId, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations)
@ -2487,8 +2484,8 @@ final class PostboxImpl {
return self.pendingMessageActionsTable.getAction(id: id, type: type) return self.pendingMessageActionsTable.getAction(id: id, type: type)
} }
fileprivate func replaceMessageTagSummary(peerId: PeerId, tagMask: MessageTags, namespace: MessageId.Namespace, count: Int32, maxId: MessageId.Id) { fileprivate func replaceMessageTagSummary(peerId: PeerId, threadId: Int64?, tagMask: MessageTags, namespace: MessageId.Namespace, count: Int32, maxId: MessageId.Id) {
let key = MessageHistoryTagsSummaryKey(tag: tagMask, peerId: peerId, namespace: namespace) let key = MessageHistoryTagsSummaryKey(tag: tagMask, peerId: peerId, threadId: threadId, namespace: namespace)
self.messageHistoryTagsSummaryTable.replace(key: key, count: count, maxId: maxId, updatedSummaries: &self.currentUpdatedMessageTagSummaries) self.messageHistoryTagsSummaryTable.replace(key: key, count: count, maxId: maxId, updatedSummaries: &self.currentUpdatedMessageTagSummaries)
} }
@ -2611,8 +2608,8 @@ final class PostboxImpl {
func resolvedChatLocationInput(chatLocation: ChatLocationInput) -> Signal<(ResolvedChatLocationInput, Bool), NoError> { func resolvedChatLocationInput(chatLocation: ChatLocationInput) -> Signal<(ResolvedChatLocationInput, Bool), NoError> {
switch chatLocation { switch chatLocation {
case let .peer(peerId): case let .peer(peerId, threadId):
return .single((.peer(peerId), false)) return .single((.peer(peerId: peerId, threadId: threadId), false))
case .thread(_, _, let data), .feed(_, let data): case .thread(_, _, let data), .feed(_, let data):
return Signal { subscriber in return Signal { subscriber in
var isHoleFill = false var isHoleFill = false
@ -2629,9 +2626,9 @@ final class PostboxImpl {
func peerIdsForLocation(_ chatLocation: ResolvedChatLocationInput, ignoreRelatedChats: Bool) -> MessageHistoryViewInput { func peerIdsForLocation(_ chatLocation: ResolvedChatLocationInput, ignoreRelatedChats: Bool) -> MessageHistoryViewInput {
var peerIds: MessageHistoryViewInput var peerIds: MessageHistoryViewInput
switch chatLocation { switch chatLocation {
case let .peer(peerId): case let .peer(peerId, threadId):
peerIds = .single(peerId) peerIds = .single(peerId: peerId, threadId: threadId)
if !ignoreRelatedChats, let associatedMessageId = self.cachedPeerDataTable.get(peerId)?.associatedHistoryMessageId, associatedMessageId.peerId != peerId { if !ignoreRelatedChats, threadId == nil, let associatedMessageId = self.cachedPeerDataTable.get(peerId)?.associatedHistoryMessageId, associatedMessageId.peerId != peerId {
peerIds = .associated(peerId, associatedMessageId) peerIds = .associated(peerId, associatedMessageId)
} }
case let .external(input): case let .external(input):
@ -2640,7 +2637,7 @@ final class PostboxImpl {
return peerIds return peerIds
} }
public func aroundMessageOfInterestHistoryViewForChatLocation(_ chatLocation: ChatLocationInput, ignoreMessagesInTimestampRange: ClosedRange<Int32>?, count: Int, clipHoles: Bool = true, topTaggedMessageIdNamespaces: Set<MessageId.Namespace>, tagMask: MessageTags?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData]) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { public func aroundMessageOfInterestHistoryViewForChatLocation(_ chatLocation: ChatLocationInput, ignoreMessagesInTimestampRange: ClosedRange<Int32>?, count: Int, clipHoles: Bool = true, topTaggedMessageIdNamespaces: Set<MessageId.Namespace>, tagMask: MessageTags?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, customUnreadMessageId: MessageId?, additionalData: [AdditionalMessageHistoryViewData]) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> {
return self.resolvedChatLocationInput(chatLocation: chatLocation) return self.resolvedChatLocationInput(chatLocation: chatLocation)
|> mapToSignal { chatLocationData in |> mapToSignal { chatLocationData in
let (chatLocation, isHoleFill) = chatLocationData let (chatLocation, isHoleFill) = chatLocationData
@ -2650,8 +2647,10 @@ final class PostboxImpl {
var anchor: HistoryViewInputAnchor = .upperBound var anchor: HistoryViewInputAnchor = .upperBound
switch peerIds { switch peerIds {
case let .single(peerId): case let .single(peerId, threadId):
if self.chatListTable.getPeerChatListIndex(peerId: peerId) != nil { if let customUnreadMessageId = customUnreadMessageId {
anchor = .message(customUnreadMessageId)
} else if threadId == nil, self.chatListTable.getPeerChatListIndex(peerId: peerId) != nil {
if let combinedState = self.readStateTable.getCombinedState(peerId), let state = combinedState.states.first, state.1.count != 0 { if let combinedState = self.readStateTable.getCombinedState(peerId), let state = combinedState.states.first, state.1.count != 0 {
switch state.1 { switch state.1 {
case let .idBased(maxIncomingReadId, _, _, _, _): case let .idBased(maxIncomingReadId, _, _, _, _):
@ -2770,8 +2769,10 @@ final class PostboxImpl {
var topTaggedMessages: [MessageId.Namespace: MessageHistoryTopTaggedMessage?] = [:] var topTaggedMessages: [MessageId.Namespace: MessageHistoryTopTaggedMessage?] = [:]
var mainPeerIdForTopTaggedMessages: PeerId? var mainPeerIdForTopTaggedMessages: PeerId?
switch peerIds { switch peerIds {
case let .single(id): case let .single(id, threadId):
mainPeerIdForTopTaggedMessages = id if threadId == nil {
mainPeerIdForTopTaggedMessages = id
}
case let .associated(id, _): case let .associated(id, _):
mainPeerIdForTopTaggedMessages = id mainPeerIdForTopTaggedMessages = id
case .external: case .external:
@ -2844,8 +2845,8 @@ final class PostboxImpl {
var readStates: MessageHistoryViewReadState? var readStates: MessageHistoryViewReadState?
var transientReadStates: MessageHistoryViewReadState? var transientReadStates: MessageHistoryViewReadState?
switch peerIds { switch peerIds {
case let .single(peerId): case let .single(peerId, threadId):
if let readState = self.readStateTable.getCombinedState(peerId) { if threadId == nil, let readState = self.readStateTable.getCombinedState(peerId) {
transientReadStates = .peer([peerId: readState]) transientReadStates = .peer([peerId: readState])
} }
case let .associated(peerId, _): case let .associated(peerId, _):
@ -2876,8 +2877,8 @@ final class PostboxImpl {
let initialData: InitialMessageHistoryData let initialData: InitialMessageHistoryData
switch peerIds { switch peerIds {
case let .single(peerId): case let .single(peerId, threadId):
initialData = self.initialMessageHistoryData(peerId: peerId, threadId: nil) initialData = self.initialMessageHistoryData(peerId: peerId, threadId: threadId)
case let .associated(peerId, _): case let .associated(peerId, _):
initialData = self.initialMessageHistoryData(peerId: peerId, threadId: nil) initialData = self.initialMessageHistoryData(peerId: peerId, threadId: nil)
case let .external(input): case let .external(input):
@ -3671,7 +3672,7 @@ final class PostboxImpl {
fileprivate func addHolesEverywhere(peerNamespaces: [PeerId.Namespace], holeNamespace: MessageId.Namespace) { fileprivate func addHolesEverywhere(peerNamespaces: [PeerId.Namespace], holeNamespace: MessageId.Namespace) {
for peerId in self.chatListIndexTable.getAllPeerIds() { for peerId in self.chatListIndexTable.getAllPeerIds() {
if peerNamespaces.contains(peerId.namespace) && self.messageHistoryMetadataTable.isInitialized(peerId) { if peerNamespaces.contains(peerId.namespace) && self.messageHistoryMetadataTable.isInitialized(peerId) {
self.addHole(peerId: peerId, namespace: holeNamespace, space: .everywhere, range: 1 ... Int32.max - 1) self.addHole(peerId: peerId, threadId: nil, namespace: holeNamespace, space: .everywhere, range: 1 ... Int32.max - 1)
} }
} }
} }
@ -3818,6 +3819,7 @@ public class Postbox {
appendMessagesFromTheSameGroup: Bool, appendMessagesFromTheSameGroup: Bool,
namespaces: MessageIdNamespaces, namespaces: MessageIdNamespaces,
orderStatistics: MessageHistoryViewOrderStatistics, orderStatistics: MessageHistoryViewOrderStatistics,
customUnreadMessageId: MessageId?,
additionalData: [AdditionalMessageHistoryViewData] additionalData: [AdditionalMessageHistoryViewData]
) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { ) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> {
return Signal { subscriber in return Signal { subscriber in
@ -3834,6 +3836,7 @@ public class Postbox {
appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup,
namespaces: namespaces, namespaces: namespaces,
orderStatistics: orderStatistics, orderStatistics: orderStatistics,
customUnreadMessageId: customUnreadMessageId,
additionalData: additionalData additionalData: additionalData
).start(next: subscriber.putNext, error: subscriber.putError, completed: subscriber.putCompletion)) ).start(next: subscriber.putNext, error: subscriber.putError, completed: subscriber.putCompletion))
} }

View File

@ -45,11 +45,11 @@ func resolveChatListMessageTagSummaryResultCalculation(addSummary: MessageHistor
return count > 0 return count > 0
} }
func resolveChatListMessageTagSummaryResultCalculation(postbox: PostboxImpl, peerId: PeerId, calculation: ChatListMessageTagSummaryResultCalculation?) -> Bool? { func resolveChatListMessageTagSummaryResultCalculation(postbox: PostboxImpl, peerId: PeerId, threadId: Int64?, calculation: ChatListMessageTagSummaryResultCalculation?) -> Bool? {
guard let calculation = calculation else { guard let calculation = calculation else {
return nil return nil
} }
let addSummary = postbox.messageHistoryTagsSummaryTable.get(MessageHistoryTagsSummaryKey(tag: calculation.addCount.tag, peerId: peerId, namespace: calculation.addCount.namespace)) let addSummary = postbox.messageHistoryTagsSummaryTable.get(MessageHistoryTagsSummaryKey(tag: calculation.addCount.tag, peerId: peerId, threadId: threadId, namespace: calculation.addCount.namespace))
let subtractSummary = postbox.pendingMessageActionsMetadataTable.getCount(.peerNamespaceAction(peerId, calculation.subtractCount.namespace, calculation.subtractCount.type)) let subtractSummary = postbox.pendingMessageActionsMetadataTable.getCount(.peerNamespaceAction(peerId, calculation.subtractCount.namespace, calculation.subtractCount.type))
let count = (addSummary?.count ?? 0) - subtractSummary let count = (addSummary?.count ?? 0) - subtractSummary
return count > 0 return count > 0

View File

@ -269,9 +269,9 @@ final class ViewTracker {
var updateType: ViewUpdateType = .Generic var updateType: ViewUpdateType = .Generic
switch mutableView.peerIds { switch mutableView.peerIds {
case let .single(peerId): case let .single(peerId, threadId):
for key in transaction.currentPeerHoleOperations.keys { for key in transaction.currentPeerHoleOperations.keys {
if key.peerId == peerId { if key.peerId == peerId && key.threadId == threadId {
updateType = .FillHole updateType = .FillHole
break break
} }

View File

@ -12,7 +12,7 @@ public enum PostboxViewKey: Hashable {
case pendingMessageActions(type: PendingMessageActionType) case pendingMessageActions(type: PendingMessageActionType)
case invalidatedMessageHistoryTagSummaries(tagMask: MessageTags, namespace: MessageId.Namespace) case invalidatedMessageHistoryTagSummaries(tagMask: MessageTags, namespace: MessageId.Namespace)
case pendingMessageActionsSummary(type: PendingMessageActionType, peerId: PeerId, namespace: MessageId.Namespace) case pendingMessageActionsSummary(type: PendingMessageActionType, peerId: PeerId, namespace: MessageId.Namespace)
case historyTagSummaryView(tag: MessageTags, peerId: PeerId, namespace: MessageId.Namespace) case historyTagSummaryView(tag: MessageTags, peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace)
case cachedPeerData(peerId: PeerId) case cachedPeerData(peerId: PeerId)
case unreadCounts(items: [UnreadMessageCountsItem]) case unreadCounts(items: [UnreadMessageCountsItem])
case combinedReadState(peerId: PeerId) case combinedReadState(peerId: PeerId)
@ -38,7 +38,7 @@ public enum PostboxViewKey: Hashable {
case isContact(id: PeerId) case isContact(id: PeerId)
case chatListIndex(id: PeerId) case chatListIndex(id: PeerId)
case peerTimeoutAttributes case peerTimeoutAttributes
case messageHistoryThreadIndex(id: PeerId) case messageHistoryThreadIndex(id: PeerId, summaryComponents: ChatListEntrySummaryComponents)
case messageHistoryThreadInfo(peerId: PeerId, threadId: Int64) case messageHistoryThreadInfo(peerId: PeerId, threadId: Int64)
public func hash(into hasher: inout Hasher) { public func hash(into hasher: inout Hasher) {
@ -68,9 +68,10 @@ public enum PostboxViewKey: Hashable {
hasher.combine(type) hasher.combine(type)
hasher.combine(peerId) hasher.combine(peerId)
hasher.combine(namespace) hasher.combine(namespace)
case let .historyTagSummaryView(tag, peerId, namespace): case let .historyTagSummaryView(tag, peerId, threadId, namespace):
hasher.combine(tag) hasher.combine(tag)
hasher.combine(peerId) hasher.combine(peerId)
hasher.combine(threadId)
hasher.combine(namespace) hasher.combine(namespace)
case let .cachedPeerData(peerId): case let .cachedPeerData(peerId):
hasher.combine(peerId) hasher.combine(peerId)
@ -126,7 +127,7 @@ public enum PostboxViewKey: Hashable {
hasher.combine(id) hasher.combine(id)
case .peerTimeoutAttributes: case .peerTimeoutAttributes:
hasher.combine(17) hasher.combine(17)
case let .messageHistoryThreadIndex(id): case let .messageHistoryThreadIndex(id, _):
hasher.combine(id) hasher.combine(id)
case let .messageHistoryThreadInfo(peerId, threadId): case let .messageHistoryThreadInfo(peerId, threadId):
hasher.combine(peerId) hasher.combine(peerId)
@ -202,8 +203,8 @@ public enum PostboxViewKey: Hashable {
} else { } else {
return false return false
} }
case let .historyTagSummaryView(tag, peerId, namespace): case let .historyTagSummaryView(tag, peerId, threadId, namespace):
if case .historyTagSummaryView(tag, peerId, namespace) = rhs { if case .historyTagSummaryView(tag, peerId, threadId, namespace) = rhs {
return true return true
} else { } else {
return false return false
@ -358,8 +359,8 @@ public enum PostboxViewKey: Hashable {
} else { } else {
return false return false
} }
case let .messageHistoryThreadIndex(id): case let .messageHistoryThreadIndex(id, summaryComponents):
if case .messageHistoryThreadIndex(id) = rhs { if case .messageHistoryThreadIndex(id, summaryComponents) = rhs {
return true return true
} else { } else {
return false return false
@ -398,8 +399,8 @@ func postboxViewForKey(postbox: PostboxImpl, key: PostboxViewKey) -> MutablePost
return MutableInvalidatedMessageHistoryTagSummariesView(postbox: postbox, tagMask: tagMask, namespace: namespace) return MutableInvalidatedMessageHistoryTagSummariesView(postbox: postbox, tagMask: tagMask, namespace: namespace)
case let .pendingMessageActionsSummary(type, peerId, namespace): case let .pendingMessageActionsSummary(type, peerId, namespace):
return MutablePendingMessageActionsSummaryView(postbox: postbox, type: type, peerId: peerId, namespace: namespace) return MutablePendingMessageActionsSummaryView(postbox: postbox, type: type, peerId: peerId, namespace: namespace)
case let .historyTagSummaryView(tag, peerId, namespace): case let .historyTagSummaryView(tag, peerId, threadId, namespace):
return MutableMessageHistoryTagSummaryView(postbox: postbox, tag: tag, peerId: peerId, namespace: namespace) return MutableMessageHistoryTagSummaryView(postbox: postbox, tag: tag, peerId: peerId, threadId: threadId, namespace: namespace)
case let .cachedPeerData(peerId): case let .cachedPeerData(peerId):
return MutableCachedPeerDataView(postbox: postbox, peerId: peerId) return MutableCachedPeerDataView(postbox: postbox, peerId: peerId)
case let .unreadCounts(items): case let .unreadCounts(items):
@ -450,8 +451,8 @@ func postboxViewForKey(postbox: PostboxImpl, key: PostboxViewKey) -> MutablePost
return MutableChatListIndexView(postbox: postbox, id: id) return MutableChatListIndexView(postbox: postbox, id: id)
case .peerTimeoutAttributes: case .peerTimeoutAttributes:
return MutablePeerTimeoutAttributesView(postbox: postbox) return MutablePeerTimeoutAttributesView(postbox: postbox)
case let .messageHistoryThreadIndex(id): case let .messageHistoryThreadIndex(id, summaryComponents):
return MutableMessageHistoryThreadIndexView(postbox: postbox, peerId: id) return MutableMessageHistoryThreadIndexView(postbox: postbox, peerId: id, summaryComponents: summaryComponents)
case let .messageHistoryThreadInfo(peerId, threadId): case let .messageHistoryThreadInfo(peerId, threadId):
return MutableMessageHistoryThreadInfoView(postbox: postbox, peerId: peerId, threadId: threadId) return MutableMessageHistoryThreadInfoView(postbox: postbox, peerId: peerId, threadId: threadId)
} }

View File

@ -781,16 +781,16 @@ final class NotificationExceptionsControllerNode: ViewControllerTracingNode {
let presentationData = context.sharedContext.currentPresentationData.modify {$0} let presentationData = context.sharedContext.currentPresentationData.modify {$0}
let updatePeerSound: (PeerId, PeerMessageSound) -> Signal<Void, NoError> = { peerId, sound in let updatePeerSound: (PeerId, PeerMessageSound) -> Signal<Void, NoError> = { peerId, sound in
return context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, sound: sound) |> deliverOnMainQueue return context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, threadId: nil, sound: sound) |> deliverOnMainQueue
} }
let updatePeerNotificationInterval: (PeerId, Int32?) -> Signal<Void, NoError> = { peerId, muteInterval in let updatePeerNotificationInterval: (PeerId, Int32?) -> Signal<Void, NoError> = { peerId, muteInterval in
return context.engine.peers.updatePeerMuteSetting(peerId: peerId, muteInterval: muteInterval) |> deliverOnMainQueue return context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: nil, muteInterval: muteInterval) |> deliverOnMainQueue
} }
let updatePeerDisplayPreviews:(PeerId, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = { let updatePeerDisplayPreviews:(PeerId, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = {
peerId, displayPreviews in peerId, displayPreviews in
return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, displayPreviews: displayPreviews) |> deliverOnMainQueue return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: nil, displayPreviews: displayPreviews) |> deliverOnMainQueue
} }
self.backgroundColor = presentationData.theme.list.blocksBackgroundColor self.backgroundColor = presentationData.theme.list.blocksBackgroundColor
@ -814,7 +814,7 @@ final class NotificationExceptionsControllerNode: ViewControllerTracingNode {
let mode = stateValue.with { $0.mode } let mode = stateValue.with { $0.mode }
dismissInputImpl?() dismissInputImpl?()
presentControllerImpl?(notificationPeerExceptionController(context: context, peer: peer._asPeer(), mode: mode, updatePeerSound: { peerId, sound in presentControllerImpl?(notificationPeerExceptionController(context: context, peer: peer._asPeer(), threadId: nil, mode: mode, updatePeerSound: { peerId, sound in
_ = updatePeerSound(peer.id, sound).start(next: { _ in _ = updatePeerSound(peer.id, sound).start(next: { _ in
updateNotificationsDisposable.set(nil) updateNotificationsDisposable.set(nil)
_ = combineLatest(updatePeerSound(peer.id, sound), context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { _, peer in _ = combineLatest(updatePeerSound(peer.id, sound), context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { _, peer in

View File

@ -365,7 +365,7 @@ private struct NotificationExceptionPeerState : Equatable {
} }
} }
public func notificationPeerExceptionController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: Peer, mode: NotificationExceptionMode, edit: Bool = false, updatePeerSound: @escaping(PeerId, PeerMessageSound) -> Void, updatePeerNotificationInterval: @escaping(PeerId, Int32?) -> Void, updatePeerDisplayPreviews: @escaping(PeerId, PeerNotificationDisplayPreviews) -> Void, removePeerFromExceptions: @escaping () -> Void, modifiedPeer: @escaping () -> Void) -> ViewController { public func notificationPeerExceptionController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: Peer, threadId: Int64?, mode: NotificationExceptionMode, edit: Bool = false, updatePeerSound: @escaping(PeerId, PeerMessageSound) -> Void, updatePeerNotificationInterval: @escaping(PeerId, Int32?) -> Void, updatePeerDisplayPreviews: @escaping(PeerId, PeerNotificationDisplayPreviews) -> Void, removePeerFromExceptions: @escaping () -> Void, modifiedPeer: @escaping () -> Void) -> ViewController {
let initialState = NotificationExceptionPeerState(canRemove: false) let initialState = NotificationExceptionPeerState(canRemove: false)
let statePromise = Promise(initialState) let statePromise = Promise(initialState)
let stateValue = Atomic(value: initialState) let stateValue = Atomic(value: initialState)
@ -420,10 +420,13 @@ public func notificationPeerExceptionController(context: AccountContext, updated
statePromise.set(context.engine.data.get( statePromise.set(context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: peer.id), TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: peer.id),
EngineDataOptional(threadId.flatMap { TelegramEngine.EngineData.Item.Peer.ThreadNotificationSettings(id: peer.id, threadId: $0) }),
TelegramEngine.EngineData.Item.NotificationSettings.Global() TelegramEngine.EngineData.Item.NotificationSettings.Global()
) )
|> map { notificationSettings, globalNotificationSettings -> NotificationExceptionPeerState in |> map { peerNotificationSettings, threadNotificationSettings, globalNotificationSettings -> NotificationExceptionPeerState in
var state = NotificationExceptionPeerState(canRemove: mode.peerIds.contains(peer.id), notifications: notificationSettings._asNotificationSettings()) let effectiveSettings = threadNotificationSettings ?? peerNotificationSettings
var state = NotificationExceptionPeerState(canRemove: mode.peerIds.contains(peer.id), notifications: effectiveSettings._asNotificationSettings())
let globalSettings = globalNotificationSettings let globalSettings = globalNotificationSettings
switch mode { switch mode {
case .channels: case .channels:

View File

@ -438,16 +438,16 @@ public func notificationsPeerCategoryController(context: AccountContext, categor
} }
let updatePeerSound: (PeerId, PeerMessageSound) -> Signal<Void, NoError> = { peerId, sound in let updatePeerSound: (PeerId, PeerMessageSound) -> Signal<Void, NoError> = { peerId, sound in
return context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, sound: sound) |> deliverOnMainQueue return context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, threadId: nil, sound: sound) |> deliverOnMainQueue
} }
let updatePeerNotificationInterval: (PeerId, Int32?) -> Signal<Void, NoError> = { peerId, muteInterval in let updatePeerNotificationInterval: (PeerId, Int32?) -> Signal<Void, NoError> = { peerId, muteInterval in
return context.engine.peers.updatePeerMuteSetting(peerId: peerId, muteInterval: muteInterval) |> deliverOnMainQueue return context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: nil, muteInterval: muteInterval) |> deliverOnMainQueue
} }
let updatePeerDisplayPreviews:(PeerId, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = { let updatePeerDisplayPreviews:(PeerId, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = {
peerId, displayPreviews in peerId, displayPreviews in
return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, displayPreviews: displayPreviews) |> deliverOnMainQueue return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: nil, displayPreviews: displayPreviews) |> deliverOnMainQueue
} }
var peerIds: Set<PeerId> = Set(mode.peerIds) var peerIds: Set<PeerId> = Set(mode.peerIds)
@ -502,7 +502,7 @@ public func notificationsPeerCategoryController(context: AccountContext, categor
} }
let mode = stateValue.with { $0.mode } let mode = stateValue.with { $0.mode }
pushControllerImpl?(notificationPeerExceptionController(context: context, peer: peer._asPeer(), mode: mode, updatePeerSound: { peerId, sound in pushControllerImpl?(notificationPeerExceptionController(context: context, peer: peer._asPeer(), threadId: nil, mode: mode, updatePeerSound: { peerId, sound in
_ = updatePeerSound(peer.id, sound).start(next: { _ in _ = updatePeerSound(peer.id, sound).start(next: { _ in
updateNotificationsDisposable.set(nil) updateNotificationsDisposable.set(nil)
_ = combineLatest(updatePeerSound(peer.id, sound), context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { _, peer in _ = combineLatest(updatePeerSound(peer.id, sound), context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { _, peer in

View File

@ -219,7 +219,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
var items: [ChatListItem] = [] var items: [ChatListItem] = []
let interaction = ChatListNodeInteraction(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in let interaction = ChatListNodeInteraction(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in
}, activateChatPreview: { _, _, gesture, _ in }, activateChatPreview: { _, _, gesture, _ in
gesture?.cancel() gesture?.cancel()
}, present: { _ in }) }, present: { _ in })

View File

@ -839,7 +839,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
var items: [ChatListItem] = [] var items: [ChatListItem] = []
let interaction = ChatListNodeInteraction(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in let interaction = ChatListNodeInteraction(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in
}, activateChatPreview: { _, _, gesture, _ in }, activateChatPreview: { _, _, gesture, _ in
gesture?.cancel() gesture?.cancel()
}, present: { _ in }, present: { _ in

View File

@ -363,7 +363,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
var items: [ChatListItem] = [] var items: [ChatListItem] = []
let interaction = ChatListNodeInteraction(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in let interaction = ChatListNodeInteraction(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in
}, activateChatPreview: { _, _, gesture, _ in }, activateChatPreview: { _, _, gesture, _ in
gesture?.cancel() gesture?.cancel()
}, present: { _ in }, present: { _ in

View File

@ -447,7 +447,7 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
contextActionImpl?(messageId, node, gesture) contextActionImpl?(messageId, node, gesture)
}) })
let messageView = context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: peerId), index: .upperBound, anchorIndex: .upperBound, count: 100, fixedCombinedReadStates: nil) let messageView = context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: peerId, threadId: nil), index: .upperBound, anchorIndex: .upperBound, count: 100, fixedCombinedReadStates: nil)
|> map { messageHistoryView, _, _ -> MessageHistoryView? in |> map { messageHistoryView, _, _ -> MessageHistoryView? in
return messageHistoryView return messageHistoryView
} }

View File

@ -222,7 +222,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-207944868] = { return Api.FileHash.parse_fileHash($0) } dict[-207944868] = { return Api.FileHash.parse_fileHash($0) }
dict[-11252123] = { return Api.Folder.parse_folder($0) } dict[-11252123] = { return Api.Folder.parse_folder($0) }
dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) } dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) }
dict[413771876] = { return Api.ForumTopic.parse_forumTopic($0) } dict[1495324380] = { return Api.ForumTopic.parse_forumTopic($0) }
dict[-1107729093] = { return Api.Game.parse_game($0) } dict[-1107729093] = { return Api.Game.parse_game($0) }
dict[-1297942941] = { return Api.GeoPoint.parse_geoPoint($0) } dict[-1297942941] = { return Api.GeoPoint.parse_geoPoint($0) }
dict[286776671] = { return Api.GeoPoint.parse_geoPointEmpty($0) } dict[286776671] = { return Api.GeoPoint.parse_geoPointEmpty($0) }
@ -315,6 +315,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1160215659] = { return Api.InputMessage.parse_inputMessageReplyTo($0) } dict[-1160215659] = { return Api.InputMessage.parse_inputMessageReplyTo($0) }
dict[-1311015810] = { return Api.InputNotifyPeer.parse_inputNotifyBroadcasts($0) } dict[-1311015810] = { return Api.InputNotifyPeer.parse_inputNotifyBroadcasts($0) }
dict[1251338318] = { return Api.InputNotifyPeer.parse_inputNotifyChats($0) } dict[1251338318] = { return Api.InputNotifyPeer.parse_inputNotifyChats($0) }
dict[1548122514] = { return Api.InputNotifyPeer.parse_inputNotifyForumTopic($0) }
dict[-1195615476] = { return Api.InputNotifyPeer.parse_inputNotifyPeer($0) } dict[-1195615476] = { return Api.InputNotifyPeer.parse_inputNotifyPeer($0) }
dict[423314455] = { return Api.InputNotifyPeer.parse_inputNotifyUsers($0) } dict[423314455] = { return Api.InputNotifyPeer.parse_inputNotifyUsers($0) }
dict[873977640] = { return Api.InputPaymentCredentials.parse_inputPaymentCredentials($0) } dict[873977640] = { return Api.InputPaymentCredentials.parse_inputPaymentCredentials($0) }

View File

@ -4985,16 +4985,18 @@ public extension Api.functions.messages {
} }
} }
public extension Api.functions.messages { public extension Api.functions.messages {
static func getSearchCounters(peer: Api.InputPeer, filters: [Api.MessagesFilter]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.messages.SearchCounter]>) { static func getSearchCounters(flags: Int32, peer: Api.InputPeer, topMsgId: Int32?, filters: [Api.MessagesFilter]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.messages.SearchCounter]>) {
let buffer = Buffer() let buffer = Buffer()
buffer.appendInt32(1932455680) buffer.appendInt32(11435201)
serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true) peer.serialize(buffer, true)
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)}
buffer.appendInt32(481674261) buffer.appendInt32(481674261)
buffer.appendInt32(Int32(filters.count)) buffer.appendInt32(Int32(filters.count))
for item in filters { for item in filters {
item.serialize(buffer, true) item.serialize(buffer, true)
} }
return (FunctionDescription(name: "messages.getSearchCounters", parameters: [("peer", String(describing: peer)), ("filters", String(describing: filters))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.messages.SearchCounter]? in return (FunctionDescription(name: "messages.getSearchCounters", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("topMsgId", String(describing: topMsgId)), ("filters", String(describing: filters))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.messages.SearchCounter]? in
let reader = BufferReader(buffer) let reader = BufferReader(buffer)
var result: [Api.messages.SearchCounter]? var result: [Api.messages.SearchCounter]?
if let _ = reader.readInt32() { if let _ = reader.readInt32() {
@ -5470,11 +5472,13 @@ public extension Api.functions.messages {
} }
} }
public extension Api.functions.messages { public extension Api.functions.messages {
static func readReactions(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.AffectedHistory>) { static func readReactions(flags: Int32, peer: Api.InputPeer, topMsgId: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.AffectedHistory>) {
let buffer = Buffer() let buffer = Buffer()
buffer.appendInt32(-2099097129) buffer.appendInt32(1420459918)
serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true) peer.serialize(buffer, true)
return (FunctionDescription(name: "messages.readReactions", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedHistory? in if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)}
return (FunctionDescription(name: "messages.readReactions", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("topMsgId", String(describing: topMsgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedHistory? in
let reader = BufferReader(buffer) let reader = BufferReader(buffer)
var result: Api.messages.AffectedHistory? var result: Api.messages.AffectedHistory?
if let signature = reader.readInt32() { if let signature = reader.readInt32() {

View File

@ -1094,13 +1094,13 @@ public extension Api {
} }
public extension Api { public extension Api {
enum ForumTopic: TypeConstructorDescription { enum ForumTopic: TypeConstructorDescription {
case forumTopic(flags: Int32, id: Int32, date: Int32, title: String, iconColor: Int32, iconEmojiId: Int64?, topMessage: Int32, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, unreadMentionsCount: Int32, unreadReactionsCount: Int32, fromId: Api.Peer) case forumTopic(flags: Int32, id: Int32, date: Int32, title: String, iconColor: Int32, iconEmojiId: Int64?, topMessage: Int32, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, unreadMentionsCount: Int32, unreadReactionsCount: Int32, fromId: Api.Peer, notifySettings: Api.PeerNotifySettings)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
case .forumTopic(let flags, let id, let date, let title, let iconColor, let iconEmojiId, let topMessage, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let unreadMentionsCount, let unreadReactionsCount, let fromId): case .forumTopic(let flags, let id, let date, let title, let iconColor, let iconEmojiId, let topMessage, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let unreadMentionsCount, let unreadReactionsCount, let fromId, let notifySettings):
if boxed { if boxed {
buffer.appendInt32(413771876) buffer.appendInt32(1495324380)
} }
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(id, buffer: buffer, boxed: false) serializeInt32(id, buffer: buffer, boxed: false)
@ -1115,14 +1115,15 @@ public extension Api {
serializeInt32(unreadMentionsCount, buffer: buffer, boxed: false) serializeInt32(unreadMentionsCount, buffer: buffer, boxed: false)
serializeInt32(unreadReactionsCount, buffer: buffer, boxed: false) serializeInt32(unreadReactionsCount, buffer: buffer, boxed: false)
fromId.serialize(buffer, true) fromId.serialize(buffer, true)
notifySettings.serialize(buffer, true)
break break
} }
} }
public func descriptionFields() -> (String, [(String, Any)]) { public func descriptionFields() -> (String, [(String, Any)]) {
switch self { switch self {
case .forumTopic(let flags, let id, let date, let title, let iconColor, let iconEmojiId, let topMessage, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let unreadMentionsCount, let unreadReactionsCount, let fromId): case .forumTopic(let flags, let id, let date, let title, let iconColor, let iconEmojiId, let topMessage, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let unreadMentionsCount, let unreadReactionsCount, let fromId, let notifySettings):
return ("forumTopic", [("flags", String(describing: flags)), ("id", String(describing: id)), ("date", String(describing: date)), ("title", String(describing: title)), ("iconColor", String(describing: iconColor)), ("iconEmojiId", String(describing: iconEmojiId)), ("topMessage", String(describing: topMessage)), ("readInboxMaxId", String(describing: readInboxMaxId)), ("readOutboxMaxId", String(describing: readOutboxMaxId)), ("unreadCount", String(describing: unreadCount)), ("unreadMentionsCount", String(describing: unreadMentionsCount)), ("unreadReactionsCount", String(describing: unreadReactionsCount)), ("fromId", String(describing: fromId))]) return ("forumTopic", [("flags", String(describing: flags)), ("id", String(describing: id)), ("date", String(describing: date)), ("title", String(describing: title)), ("iconColor", String(describing: iconColor)), ("iconEmojiId", String(describing: iconEmojiId)), ("topMessage", String(describing: topMessage)), ("readInboxMaxId", String(describing: readInboxMaxId)), ("readOutboxMaxId", String(describing: readOutboxMaxId)), ("unreadCount", String(describing: unreadCount)), ("unreadMentionsCount", String(describing: unreadMentionsCount)), ("unreadReactionsCount", String(describing: unreadReactionsCount)), ("fromId", String(describing: fromId)), ("notifySettings", String(describing: notifySettings))])
} }
} }
@ -1155,6 +1156,10 @@ public extension Api {
if let signature = reader.readInt32() { if let signature = reader.readInt32() {
_13 = Api.parse(reader, signature: signature) as? Api.Peer _13 = Api.parse(reader, signature: signature) as? Api.Peer
} }
var _14: Api.PeerNotifySettings?
if let signature = reader.readInt32() {
_14 = Api.parse(reader, signature: signature) as? Api.PeerNotifySettings
}
let _c1 = _1 != nil let _c1 = _1 != nil
let _c2 = _2 != nil let _c2 = _2 != nil
let _c3 = _3 != nil let _c3 = _3 != nil
@ -1168,8 +1173,9 @@ public extension Api {
let _c11 = _11 != nil let _c11 = _11 != nil
let _c12 = _12 != nil let _c12 = _12 != nil
let _c13 = _13 != nil let _c13 = _13 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 { let _c14 = _14 != nil
return Api.ForumTopic.forumTopic(flags: _1!, id: _2!, date: _3!, title: _4!, iconColor: _5!, iconEmojiId: _6, topMessage: _7!, readInboxMaxId: _8!, readOutboxMaxId: _9!, unreadCount: _10!, unreadMentionsCount: _11!, unreadReactionsCount: _12!, fromId: _13!) if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 {
return Api.ForumTopic.forumTopic(flags: _1!, id: _2!, date: _3!, title: _4!, iconColor: _5!, iconEmojiId: _6, topMessage: _7!, readInboxMaxId: _8!, readOutboxMaxId: _9!, unreadCount: _10!, unreadMentionsCount: _11!, unreadReactionsCount: _12!, fromId: _13!, notifySettings: _14!)
} }
else { else {
return nil return nil

View File

@ -860,6 +860,7 @@ public extension Api {
indirect enum InputNotifyPeer: TypeConstructorDescription { indirect enum InputNotifyPeer: TypeConstructorDescription {
case inputNotifyBroadcasts case inputNotifyBroadcasts
case inputNotifyChats case inputNotifyChats
case inputNotifyForumTopic(peer: Api.InputPeer, topMsgId: Int32)
case inputNotifyPeer(peer: Api.InputPeer) case inputNotifyPeer(peer: Api.InputPeer)
case inputNotifyUsers case inputNotifyUsers
@ -876,6 +877,13 @@ public extension Api {
buffer.appendInt32(1251338318) buffer.appendInt32(1251338318)
} }
break
case .inputNotifyForumTopic(let peer, let topMsgId):
if boxed {
buffer.appendInt32(1548122514)
}
peer.serialize(buffer, true)
serializeInt32(topMsgId, buffer: buffer, boxed: false)
break break
case .inputNotifyPeer(let peer): case .inputNotifyPeer(let peer):
if boxed { if boxed {
@ -898,6 +906,8 @@ public extension Api {
return ("inputNotifyBroadcasts", []) return ("inputNotifyBroadcasts", [])
case .inputNotifyChats: case .inputNotifyChats:
return ("inputNotifyChats", []) return ("inputNotifyChats", [])
case .inputNotifyForumTopic(let peer, let topMsgId):
return ("inputNotifyForumTopic", [("peer", String(describing: peer)), ("topMsgId", String(describing: topMsgId))])
case .inputNotifyPeer(let peer): case .inputNotifyPeer(let peer):
return ("inputNotifyPeer", [("peer", String(describing: peer))]) return ("inputNotifyPeer", [("peer", String(describing: peer))])
case .inputNotifyUsers: case .inputNotifyUsers:
@ -911,6 +921,22 @@ public extension Api {
public static func parse_inputNotifyChats(_ reader: BufferReader) -> InputNotifyPeer? { public static func parse_inputNotifyChats(_ reader: BufferReader) -> InputNotifyPeer? {
return Api.InputNotifyPeer.inputNotifyChats return Api.InputNotifyPeer.inputNotifyChats
} }
public static func parse_inputNotifyForumTopic(_ reader: BufferReader) -> InputNotifyPeer? {
var _1: Api.InputPeer?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.InputPeer
}
var _2: Int32?
_2 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.InputNotifyPeer.inputNotifyForumTopic(peer: _1!, topMsgId: _2!)
}
else {
return nil
}
}
public static func parse_inputNotifyPeer(_ reader: BufferReader) -> InputNotifyPeer? { public static func parse_inputNotifyPeer(_ reader: BufferReader) -> InputNotifyPeer? {
var _1: Api.InputPeer? var _1: Api.InputPeer?
if let signature = reader.readInt32() { if let signature = reader.readInt32() {

View File

@ -117,7 +117,7 @@ enum AccountStateMutationOperation {
case UpdateAudioTranscription(messageId: MessageId, id: Int64, isPending: Bool, text: String) case UpdateAudioTranscription(messageId: MessageId, id: Int64, isPending: Bool, text: String)
case UpdateConfig case UpdateConfig
case UpdateExtendedMedia(MessageId, Api.MessageExtendedMedia) case UpdateExtendedMedia(MessageId, Api.MessageExtendedMedia)
case ResetForumTopic(topicId: MessageId, data: MessageHistoryThreadData, pts: Int32) case ResetForumTopic(topicId: MessageId, data: StoreMessageHistoryThreadData, pts: Int32)
} }
struct HoleFromPreviousState { struct HoleFromPreviousState {

View File

@ -62,8 +62,14 @@ public struct MessageHistoryThreadData: Codable, Equatable {
public var maxIncomingReadId: Int32 public var maxIncomingReadId: Int32
public var maxKnownMessageId: Int32 public var maxKnownMessageId: Int32
public var maxOutgoingReadId: Int32 public var maxOutgoingReadId: Int32
public var unreadMentionCount: Int32 public var notificationSettings: TelegramPeerNotificationSettings
public var unreadReactionCount: Int32 }
struct StoreMessageHistoryThreadData {
var data: MessageHistoryThreadData
var topMessageId: Int32
var unreadMentionCount: Int32
var unreadReactionCount: Int32
} }
public enum CreateForumChannelTopicError { public enum CreateForumChannelTopicError {
@ -247,7 +253,7 @@ func _internal_loadMessageHistoryThreads(account: Account, peerId: PeerId) -> Si
updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences) updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences)
let _ = transaction.addMessages(messages.compactMap { message -> StoreMessage? in let _ = InternalAccountState.addMessages(transaction: transaction, messages: messages.compactMap { message -> StoreMessage? in
return StoreMessage(apiMessage: message) return StoreMessage(apiMessage: message)
}, location: .Random) }, location: .Random)
@ -261,7 +267,7 @@ func _internal_loadMessageHistoryThreads(account: Account, peerId: PeerId) -> Si
for topic in topics { for topic in topics {
switch topic { switch topic {
case let .forumTopic(_, id, date, title, iconColor, iconEmojiId, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount, fromId): case let .forumTopic(_, id, date, title, iconColor, iconEmojiId, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount, fromId, notifySettings):
let data = MessageHistoryThreadData( let data = MessageHistoryThreadData(
creationDate: date, creationDate: date,
author: fromId.peerId, author: fromId.peerId,
@ -274,13 +280,15 @@ func _internal_loadMessageHistoryThreads(account: Account, peerId: PeerId) -> Si
maxIncomingReadId: readInboxMaxId, maxIncomingReadId: readInboxMaxId,
maxKnownMessageId: topMessage, maxKnownMessageId: topMessage,
maxOutgoingReadId: readOutboxMaxId, maxOutgoingReadId: readOutboxMaxId,
unreadMentionCount: unreadMentionsCount, notificationSettings: TelegramPeerNotificationSettings(apiSettings: notifySettings)
unreadReactionCount: unreadReactionsCount
) )
guard let info = CodableEntry(data) else { guard let info = CodableEntry(data) else {
continue continue
} }
transaction.setMessageHistoryThreadInfo(peerId: peerId, threadId: Int64(id), info: info) transaction.setMessageHistoryThreadInfo(peerId: peerId, threadId: Int64(id), info: info)
transaction.replaceMessageTagSummary(peerId: peerId, threadId: Int64(id), tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: unreadMentionsCount, maxId: topMessage)
transaction.replaceMessageTagSummary(peerId: peerId, threadId: Int64(id), tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: unreadReactionsCount, maxId: topMessage)
} }
} }
} }
@ -316,25 +324,6 @@ public final class ForumChannelTopics {
let _ = _internal_loadMessageHistoryThreads(account: self.account, peerId: peerId).start() let _ = _internal_loadMessageHistoryThreads(account: self.account, peerId: peerId).start()
let viewKey: PostboxViewKey = .messageHistoryThreadIndex(id: self.peerId)
self.statePromise.set(self.account.postbox.combinedView(keys: [viewKey])
|> map { views -> State in
guard let view = views.views[viewKey] as? MessageHistoryThreadIndexView else {
preconditionFailure()
}
return State(items: view.items.compactMap { item -> ForumChannelTopics.Item? in
guard let data = item.info.get(MessageHistoryThreadData.self) else {
return nil
}
return ForumChannelTopics.Item(
id: item.id,
info: data.info,
index: item.index,
topMessage: item.topMessage.flatMap(EngineMessage.init)
)
})
})
self.updateDisposable.set(account.viewTracker.polledChannel(peerId: peerId).start()) self.updateDisposable.set(account.viewTracker.polledChannel(peerId: peerId).start())
} }

View File

@ -65,5 +65,5 @@ public func unarchiveAutomaticallyArchivedPeer(account: Account, peerId: PeerId)
} }
|> deliverOnMainQueue).start() |> deliverOnMainQueue).start()
let _ = _internal_updatePeerMuteSetting(account: account, peerId: peerId, muteInterval: nil).start() let _ = _internal_updatePeerMuteSetting(account: account, peerId: peerId, threadId: nil, muteInterval: nil).start()
} }

View File

@ -75,7 +75,7 @@ private func activeChannelsFromUpdateGroups(_ groups: [UpdateGroup]) -> Set<Peer
switch chat { switch chat {
case .channel: case .channel:
if let channel = parseTelegramGroupOrChannel(chat: chat) as? TelegramChannel { if let channel = parseTelegramGroupOrChannel(chat: chat) as? TelegramChannel {
if channel.participationStatus == .member { if channel.participationStatus == .member, case .personal = channel.accessHash {
peerIds.insert(channel.id) peerIds.insert(channel.id)
} }
} }
@ -469,13 +469,13 @@ private func initialStateWithPeerIds(_ transaction: Transaction, peerIds: Set<Pe
hasValidInclusion = false hasValidInclusion = false
} }
if hasValidInclusion { if hasValidInclusion {
if let notificationSettings = transaction.getPeerNotificationSettings(peerId) { if let notificationSettings = transaction.getPeerNotificationSettings(id: peerId) {
peerChatInfos[peerId] = PeerChatInfo(notificationSettings: notificationSettings) peerChatInfos[peerId] = PeerChatInfo(notificationSettings: notificationSettings)
} }
} else { } else {
if let peer = transaction.getPeer(peerId) { if let peer = transaction.getPeer(peerId) {
if let channel = peer as? TelegramChannel, channel.participationStatus != .member { if let channel = peer as? TelegramChannel, channel.participationStatus != .member {
if let notificationSettings = transaction.getPeerNotificationSettings(peerId) { if let notificationSettings = transaction.getPeerNotificationSettings(id: peerId) {
peerChatInfos[peerId] = PeerChatInfo(notificationSettings: notificationSettings) peerChatInfos[peerId] = PeerChatInfo(notificationSettings: notificationSettings)
Logger.shared.log("State", "Peer \(peerId) (\(peer.debugDisplayTitle) has no stored inclusion, using synthesized one") Logger.shared.log("State", "Peer \(peerId) (\(peer.debugDisplayTitle) has no stored inclusion, using synthesized one")
} }
@ -1694,21 +1694,25 @@ func resolveForumThreads(postbox: Postbox, network: Network, state: AccountMutab
for topic in topics { for topic in topics {
switch topic { switch topic {
case let .forumTopic(_, id, date, title, iconColor, iconEmojiId, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount, fromId): case let .forumTopic(_, id, date, title, iconColor, iconEmojiId, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount, fromId, notifySettings):
state.operations.append(.ResetForumTopic( state.operations.append(.ResetForumTopic(
topicId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: id), topicId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: id),
data: MessageHistoryThreadData( data: StoreMessageHistoryThreadData(
creationDate: date, data: MessageHistoryThreadData(
author: fromId.peerId, creationDate: date,
info: EngineMessageHistoryThread.Info( author: fromId.peerId,
title: title, info: EngineMessageHistoryThread.Info(
icon: iconEmojiId == 0 ? nil : iconEmojiId, title: title,
iconColor: iconColor icon: iconEmojiId == 0 ? nil : iconEmojiId,
iconColor: iconColor
),
incomingUnreadCount: unreadCount,
maxIncomingReadId: readInboxMaxId,
maxKnownMessageId: topMessage,
maxOutgoingReadId: readOutboxMaxId,
notificationSettings: TelegramPeerNotificationSettings(apiSettings: notifySettings)
), ),
incomingUnreadCount: unreadCount, topMessageId: topMessage,
maxIncomingReadId: readInboxMaxId,
maxKnownMessageId: topMessage,
maxOutgoingReadId: readOutboxMaxId,
unreadMentionCount: unreadMentionsCount, unreadMentionCount: unreadMentionsCount,
unreadReactionCount: unreadReactionsCount unreadReactionCount: unreadReactionsCount
), ),
@ -1786,7 +1790,7 @@ func resolveForumThreads(postbox: Postbox, network: Network, ids: [MessageId]) -
for topic in topics { for topic in topics {
switch topic { switch topic {
case let .forumTopic(_, id, date, title, iconColor, iconEmojiId, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount, fromId): case let .forumTopic(_, id, date, title, iconColor, iconEmojiId, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount, fromId, notifySettings):
let data = MessageHistoryThreadData( let data = MessageHistoryThreadData(
creationDate: date, creationDate: date,
author: fromId.peerId, author: fromId.peerId,
@ -1799,12 +1803,14 @@ func resolveForumThreads(postbox: Postbox, network: Network, ids: [MessageId]) -
maxIncomingReadId: readInboxMaxId, maxIncomingReadId: readInboxMaxId,
maxKnownMessageId: topMessage, maxKnownMessageId: topMessage,
maxOutgoingReadId: readOutboxMaxId, maxOutgoingReadId: readOutboxMaxId,
unreadMentionCount: unreadMentionsCount, notificationSettings: TelegramPeerNotificationSettings(apiSettings: notifySettings)
unreadReactionCount: unreadReactionsCount
) )
if let entry = CodableEntry(data) { if let entry = CodableEntry(data) {
transaction.setMessageHistoryThreadInfo(peerId: peerId, threadId: Int64(id), info: entry) transaction.setMessageHistoryThreadInfo(peerId: peerId, threadId: Int64(id), info: entry)
} }
transaction.replaceMessageTagSummary(peerId: peerId, threadId: Int64(id), tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: unreadMentionsCount, maxId: topMessage)
transaction.replaceMessageTagSummary(peerId: peerId, threadId: Int64(id), tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: unreadReactionsCount, maxId: topMessage)
} }
} }
} }
@ -1900,19 +1906,23 @@ func resolveForumThreads(postbox: Postbox, network: Network, fetchedChatList: Fe
for topic in topics { for topic in topics {
switch topic { switch topic {
case let .forumTopic(_, id, date, title, iconColor, iconEmojiId, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount, fromId): case let .forumTopic(_, id, date, title, iconColor, iconEmojiId, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount, fromId, notifySettings):
fetchedChatList.threadInfos[MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: id)] = MessageHistoryThreadData( fetchedChatList.threadInfos[MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: id)] = StoreMessageHistoryThreadData(
creationDate: date, data: MessageHistoryThreadData(
author: fromId.peerId, creationDate: date,
info: EngineMessageHistoryThread.Info( author: fromId.peerId,
title: title, info: EngineMessageHistoryThread.Info(
icon: iconEmojiId == 0 ? nil : iconEmojiId, title: title,
iconColor: iconColor icon: iconEmojiId == 0 ? nil : iconEmojiId,
iconColor: iconColor
),
incomingUnreadCount: unreadCount,
maxIncomingReadId: readInboxMaxId,
maxKnownMessageId: topMessage,
maxOutgoingReadId: readOutboxMaxId,
notificationSettings: TelegramPeerNotificationSettings(apiSettings: notifySettings)
), ),
incomingUnreadCount: unreadCount, topMessageId: topMessage,
maxIncomingReadId: readInboxMaxId,
maxKnownMessageId: topMessage,
maxOutgoingReadId: readOutboxMaxId,
unreadMentionCount: unreadMentionsCount, unreadMentionCount: unreadMentionsCount,
unreadReactionCount: unreadReactionsCount unreadReactionCount: unreadReactionsCount
) )
@ -2209,7 +2219,7 @@ func pollChannelOnce(postbox: Postbox, network: Network, peerId: PeerId, stateMa
hasValidInclusion = false hasValidInclusion = false
} }
if hasValidInclusion { if hasValidInclusion {
if let notificationSettings = transaction.getPeerNotificationSettings(peerId) as? TelegramPeerNotificationSettings { if let notificationSettings = transaction.getPeerNotificationSettings(id: peerId) as? TelegramPeerNotificationSettings {
peerChatInfos[peerId] = PeerChatInfo(notificationSettings: notificationSettings) peerChatInfos[peerId] = PeerChatInfo(notificationSettings: notificationSettings)
} }
} }
@ -2263,7 +2273,7 @@ public func standalonePollChannelOnce(postbox: Postbox, network: Network, peerId
hasValidInclusion = false hasValidInclusion = false
} }
if hasValidInclusion { if hasValidInclusion {
if let notificationSettings = transaction.getPeerNotificationSettings(peerId) as? TelegramPeerNotificationSettings { if let notificationSettings = transaction.getPeerNotificationSettings(id: peerId) as? TelegramPeerNotificationSettings {
peerChatInfos[peerId] = PeerChatInfo(notificationSettings: notificationSettings) peerChatInfos[peerId] = PeerChatInfo(notificationSettings: notificationSettings)
} }
} }
@ -3099,7 +3109,7 @@ func replayFinalState(
case .groupCreated, .channelMigratedFromGroup: case .groupCreated, .channelMigratedFromGroup:
let holesAtHistoryStart = transaction.getHole(containing: MessageId(peerId: chatPeerId, namespace: Namespaces.Message.Cloud, id: id.id - 1)) let holesAtHistoryStart = transaction.getHole(containing: MessageId(peerId: chatPeerId, namespace: Namespaces.Message.Cloud, id: id.id - 1))
for (space, _) in holesAtHistoryStart { for (space, _) in holesAtHistoryStart {
transaction.removeHole(peerId: chatPeerId, namespace: Namespaces.Message.Cloud, space: space, range: 1 ... id.id) transaction.removeHole(peerId: chatPeerId, threadId: nil, namespace: Namespaces.Message.Cloud, space: space, range: 1 ... id.id)
} }
default: default:
break break
@ -3422,11 +3432,11 @@ func replayFinalState(
case let .UpdatePeerChatUnreadMark(peerId, namespace, value): case let .UpdatePeerChatUnreadMark(peerId, namespace, value):
transaction.applyMarkUnread(peerId: peerId, namespace: namespace, value: value, interactive: false) transaction.applyMarkUnread(peerId: peerId, namespace: namespace, value: value, interactive: false)
case let .ResetMessageTagSummary(peerId, tag, namespace, count, range): case let .ResetMessageTagSummary(peerId, tag, namespace, count, range):
transaction.replaceMessageTagSummary(peerId: peerId, tagMask: tag, namespace: namespace, count: count, maxId: range.maxId) transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: tag, namespace: namespace, count: count, maxId: range.maxId)
if count == 0 { if count == 0 {
transaction.removeHole(peerId: peerId, namespace: namespace, space: .tag(tag), range: 1 ... (Int32.max - 1)) transaction.removeHole(peerId: peerId, threadId: nil, namespace: namespace, space: .tag(tag), range: 1 ... (Int32.max - 1))
if tag == .unseenPersonalMessage { if tag == .unseenPersonalMessage {
let ids = transaction.getMessageIndicesWithTag(peerId: peerId, namespace: namespace, tag: tag).map({ $0.id }) let ids = transaction.getMessageIndicesWithTag(peerId: peerId, threadId: nil, namespace: namespace, tag: tag).map({ $0.id })
Logger.shared.log("State", "will call markUnseenPersonalMessage for \(ids.count) messages") Logger.shared.log("State", "will call markUnseenPersonalMessage for \(ids.count) messages")
for id in ids { for id in ids {
markUnseenPersonalMessage(transaction: transaction, id: id, addSynchronizeAction: false) markUnseenPersonalMessage(transaction: transaction, id: id, addSynchronizeAction: false)
@ -3942,11 +3952,13 @@ func replayFinalState(
case let .ResetForumTopic(topicId, data, pts): case let .ResetForumTopic(topicId, data, pts):
if finalState.state.resetForumTopicLists[topicId.peerId] == nil { if finalState.state.resetForumTopicLists[topicId.peerId] == nil {
let _ = pts let _ = pts
if let entry = CodableEntry(data) { if let entry = CodableEntry(data.data) {
transaction.setMessageHistoryThreadInfo(peerId: topicId.peerId, threadId: Int64(topicId.id), info: entry) transaction.setMessageHistoryThreadInfo(peerId: topicId.peerId, threadId: Int64(topicId.id), info: entry)
} else { } else {
assertionFailure() assertionFailure()
} }
transaction.replaceMessageTagSummary(peerId: topicId.peerId, threadId: Int64(topicId.id), tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: data.unreadMentionCount, maxId: data.topMessageId)
transaction.replaceMessageTagSummary(peerId: topicId.peerId, threadId: Int64(topicId.id), tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: data.unreadReactionCount, maxId: data.topMessageId)
} }
} }
} }
@ -3959,9 +3971,9 @@ func replayFinalState(
upperId = Int32.max upperId = Int32.max
} }
if upperId >= messageId.id { if upperId >= messageId.id {
transaction.addHole(peerId: messageId.peerId, namespace: messageId.namespace, space: .everywhere, range: messageId.id ... upperId) transaction.addHole(peerId: messageId.peerId, threadId: nil, namespace: messageId.namespace, space: .everywhere, range: messageId.id ... upperId)
transaction.addHole(peerId: messageId.peerId, namespace: messageId.namespace, space: .tag(.pinned), range: 1 ... upperId) transaction.addHole(peerId: messageId.peerId, threadId: nil, namespace: messageId.namespace, space: .tag(.pinned), range: 1 ... upperId)
Logger.shared.log("State", "adding hole for peer \(messageId.peerId), \(messageId.id) ... \(upperId)") Logger.shared.log("State", "adding hole for peer \(messageId.peerId), \(messageId.id) ... \(upperId)")
} else { } else {
@ -4269,7 +4281,7 @@ func replayFinalState(
let currentInclusion = transaction.getPeerChatListInclusion(peerId) let currentInclusion = transaction.getPeerChatListInclusion(peerId)
if let groupId = currentInclusion.groupId, groupId == Namespaces.PeerGroup.archive { if let groupId = currentInclusion.groupId, groupId == Namespaces.PeerGroup.archive {
if let peer = transaction.getPeer(peerId) as? TelegramSecretChat { if let peer = transaction.getPeer(peerId) as? TelegramSecretChat {
let isRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: transaction.getGlobalNotificationSettings(), peer: peer, peerSettings: transaction.getPeerNotificationSettings(peer.regularPeerId)) let isRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: transaction.getGlobalNotificationSettings(), peer: peer, peerSettings: transaction.getPeerNotificationSettings(id: peer.regularPeerId))
if !isRemovedFromTotalUnreadCount { if !isRemovedFromTotalUnreadCount {
transaction.updatePeerChatListInclusion(peerId, inclusion: currentInclusion.withGroupId(groupId: .root)) transaction.updatePeerChatListInclusion(peerId, inclusion: currentInclusion.withGroupId(groupId: .root))

View File

@ -1358,7 +1358,7 @@ public func messagesForNotification(transaction: Transaction, id: MessageId, alw
notificationPeerId = author.id notificationPeerId = author.id
} }
if let notificationSettings = transaction.getPeerNotificationSettings(notificationPeerId) as? TelegramPeerNotificationSettings { if let notificationSettings = transaction.getPeerNotificationSettings(id: notificationPeerId) as? TelegramPeerNotificationSettings {
var defaultSound: PeerMessageSound = defaultCloudPeerNotificationSound var defaultSound: PeerMessageSound = defaultCloudPeerNotificationSound
var defaultNotify: Bool = true var defaultNotify: Bool = true
if let globalNotificationSettings = transaction.getPreferencesEntry(key: PreferencesKeys.globalNotifications)?.get(GlobalNotificationSettings.self) { if let globalNotificationSettings = transaction.getPreferencesEntry(key: PreferencesKeys.globalNotifications)?.get(GlobalNotificationSettings.self) {

View File

@ -181,7 +181,7 @@ private func fetchPoll(account: Account, messageId: MessageId) -> Signal<Void, N
private func wrappedHistoryViewAdditionalData(chatLocation: ChatLocationInput, additionalData: [AdditionalMessageHistoryViewData]) -> [AdditionalMessageHistoryViewData] { private func wrappedHistoryViewAdditionalData(chatLocation: ChatLocationInput, additionalData: [AdditionalMessageHistoryViewData]) -> [AdditionalMessageHistoryViewData] {
var result = additionalData var result = additionalData
switch chatLocation { switch chatLocation {
case let .peer(peerId): case let .peer(peerId, _):
if peerId.namespace == Namespaces.Peer.CloudChannel { if peerId.namespace == Namespaces.Peer.CloudChannel {
if result.firstIndex(where: { if case .peerChatState = $0 { return true } else { return false } }) == nil { if result.firstIndex(where: { if case .peerChatState = $0 { return true } else { return false } }) == nil {
result.append(.peerChatState(peerId)) result.append(.peerChatState(peerId))
@ -1220,13 +1220,13 @@ public final class AccountViewTracker {
} }
} }
public func updateMarkAllMentionsSeen(peerId: PeerId) { public func updateMarkAllMentionsSeen(peerId: PeerId, threadId: Int64?) {
self.queue.async { self.queue.async {
guard let account = self.account else { guard let account = self.account else {
return return
} }
let _ = (account.postbox.transaction { transaction -> Set<MessageId> in let _ = (account.postbox.transaction { transaction -> Set<MessageId> in
let ids = Set(transaction.getMessageIndicesWithTag(peerId: peerId, namespace: Namespaces.Message.Cloud, tag: .unseenPersonalMessage).map({ $0.id })) let ids = Set(transaction.getMessageIndicesWithTag(peerId: peerId, threadId: threadId, namespace: Namespaces.Message.Cloud, tag: .unseenPersonalMessage).map({ $0.id }))
for id in ids { for id in ids {
transaction.updateMessage(id, update: { currentMessage in transaction.updateMessage(id, update: { currentMessage in
@ -1244,13 +1244,13 @@ public final class AccountViewTracker {
}) })
} }
if let summary = transaction.getMessageTagSummary(peerId: peerId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud), summary.count > 0 { if let summary = transaction.getMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud), summary.count > 0 {
var maxId: Int32 = summary.range.maxId var maxId: Int32 = summary.range.maxId
if let index = transaction.getTopPeerMessageIndex(peerId: peerId, namespace: Namespaces.Message.Cloud) { if let index = transaction.getTopPeerMessageIndex(peerId: peerId, namespace: Namespaces.Message.Cloud) {
maxId = index.id.id maxId = index.id.id
} }
transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: 0, maxId: maxId) transaction.replaceMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: 0, maxId: maxId)
addSynchronizeMarkAllUnseenPersonalMessagesOperation(transaction: transaction, peerId: peerId, maxId: summary.range.maxId) addSynchronizeMarkAllUnseenPersonalMessagesOperation(transaction: transaction, peerId: peerId, maxId: summary.range.maxId)
} }
@ -1303,13 +1303,13 @@ public final class AccountViewTracker {
} }
} }
public func updateMarkAllReactionsSeen(peerId: PeerId) { public func updateMarkAllReactionsSeen(peerId: PeerId, threadId: Int64?) {
self.queue.async { self.queue.async {
guard let account = self.account else { guard let account = self.account else {
return return
} }
let _ = (account.postbox.transaction { transaction -> Set<MessageId> in let _ = (account.postbox.transaction { transaction -> Set<MessageId> in
let ids = Set(transaction.getMessageIndicesWithTag(peerId: peerId, namespace: Namespaces.Message.Cloud, tag: .unseenReaction).map({ $0.id })) let ids = Set(transaction.getMessageIndicesWithTag(peerId: peerId, threadId: threadId, namespace: Namespaces.Message.Cloud, tag: .unseenReaction).map({ $0.id }))
for id in ids { for id in ids {
transaction.updateMessage(id, update: { currentMessage in transaction.updateMessage(id, update: { currentMessage in
@ -1327,13 +1327,13 @@ public final class AccountViewTracker {
}) })
} }
if let summary = transaction.getMessageTagSummary(peerId: peerId, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud) { if let summary = transaction.getMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud) {
var maxId: Int32 = summary.range.maxId var maxId: Int32 = summary.range.maxId
if let index = transaction.getTopPeerMessageIndex(peerId: peerId, namespace: Namespaces.Message.Cloud) { if let index = transaction.getTopPeerMessageIndex(peerId: peerId, namespace: Namespaces.Message.Cloud) {
maxId = index.id.id maxId = index.id.id
} }
transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: 0, maxId: maxId) transaction.replaceMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: 0, maxId: maxId)
addSynchronizeMarkAllUnseenReactionsOperation(transaction: transaction, peerId: peerId, maxId: summary.range.maxId) addSynchronizeMarkAllUnseenReactionsOperation(transaction: transaction, peerId: peerId, maxId: summary.range.maxId)
} }
@ -1513,6 +1513,35 @@ public final class AccountViewTracker {
} }
func wrappedMessageHistorySignal(chatLocation: ChatLocationInput, signal: Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError>, addHoleIfNeeded: Bool) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { func wrappedMessageHistorySignal(chatLocation: ChatLocationInput, signal: Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError>, addHoleIfNeeded: Bool) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> {
var signal = signal
if let postbox = self.account?.postbox, let peerId = chatLocation.peerId, let threadId = chatLocation.threadId {
let viewKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: peerId, threadId: threadId)
let fixedReadStates = Atomic<MessageHistoryViewReadState?>(value: nil)
signal = combineLatest(signal, postbox.combinedView(keys: [viewKey]))
|> map { view, additionalViews -> (MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?) in
var view = view
if let threadInfo = additionalViews.views[viewKey] as? MessageHistoryThreadInfoView, let data = threadInfo.info?.get(MessageHistoryThreadData.self) {
let readState = CombinedPeerReadState(states: [(Namespaces.Message.Cloud, .idBased(maxIncomingReadId: data.maxIncomingReadId, maxOutgoingReadId: data.maxOutgoingReadId, maxKnownId: data.maxKnownMessageId, count: data.incomingUnreadCount, markedUnread: false))])
let fixed = fixedReadStates.modify { current in
if let current = current {
return current
} else {
return view.0.fixedReadStates ?? .peer([peerId: readState])
}
}
view.0 = MessageHistoryView(
base: view.0,
fixed: fixed,
transient: .peer([peerId: readState])
)
}
return view
}
}
let history = withState(signal, { [weak self] () -> Int32 in let history = withState(signal, { [weak self] () -> Int32 in
if let strongSelf = self { if let strongSelf = self {
return OSAtomicIncrement32(&strongSelf.nextViewId) return OSAtomicIncrement32(&strongSelf.nextViewId)
@ -1526,7 +1555,7 @@ public final class AccountViewTracker {
strongSelf.updatePendingWebpages(viewId: viewId, messageIds: messageIds, localWebpages: localWebpages) strongSelf.updatePendingWebpages(viewId: viewId, messageIds: messageIds, localWebpages: localWebpages)
let (pollMessageIds, pollMessageDict) = pollMessages(entries: next.0.entries) let (pollMessageIds, pollMessageDict) = pollMessages(entries: next.0.entries)
strongSelf.updatePolls(viewId: viewId, messageIds: pollMessageIds, messages: pollMessageDict) strongSelf.updatePolls(viewId: viewId, messageIds: pollMessageIds, messages: pollMessageDict)
if case let .peer(peerId) = chatLocation, peerId.namespace == Namespaces.Peer.CloudChannel { if case let .peer(peerId, _) = chatLocation, peerId.namespace == Namespaces.Peer.CloudChannel {
strongSelf.historyViewStateValidationContexts.updateView(id: viewId, view: next.0) strongSelf.historyViewStateValidationContexts.updateView(id: viewId, view: next.0)
} else if case let .thread(peerId, _, _) = chatLocation, peerId.namespace == Namespaces.Peer.CloudChannel { } else if case let .thread(peerId, _, _) = chatLocation, peerId.namespace == Namespaces.Peer.CloudChannel {
strongSelf.historyViewStateValidationContexts.updateView(id: viewId, view: next.0, location: chatLocation) strongSelf.historyViewStateValidationContexts.updateView(id: viewId, view: next.0, location: chatLocation)
@ -1539,7 +1568,7 @@ public final class AccountViewTracker {
strongSelf.updatePendingWebpages(viewId: viewId, messageIds: [], localWebpages: [:]) strongSelf.updatePendingWebpages(viewId: viewId, messageIds: [], localWebpages: [:])
strongSelf.updatePolls(viewId: viewId, messageIds: [], messages: [:]) strongSelf.updatePolls(viewId: viewId, messageIds: [], messages: [:])
switch chatLocation { switch chatLocation {
case let .peer(peerId): case let .peer(peerId, _):
if peerId.namespace == Namespaces.Peer.CloudChannel { if peerId.namespace == Namespaces.Peer.CloudChannel {
strongSelf.historyViewStateValidationContexts.updateView(id: viewId, view: nil) strongSelf.historyViewStateValidationContexts.updateView(id: viewId, view: nil)
} }
@ -1556,7 +1585,7 @@ public final class AccountViewTracker {
let peerId: PeerId? let peerId: PeerId?
switch chatLocation { switch chatLocation {
case let .peer(peerIdValue): case let .peer(peerIdValue, _):
peerId = peerIdValue peerId = peerIdValue
case let .thread(peerIdValue, _, _): case let .thread(peerIdValue, _, _):
peerId = peerIdValue peerId = peerIdValue
@ -1583,7 +1612,7 @@ public final class AccountViewTracker {
let isAutomaticallyTracked = self.account!.postbox.transaction { transaction -> Bool in let isAutomaticallyTracked = self.account!.postbox.transaction { transaction -> Bool in
if transaction.getPeerChatListIndex(peerId) == nil { if transaction.getPeerChatListIndex(peerId) == nil {
if addHole { if addHole {
transaction.addHole(peerId: peerId, namespace: Namespaces.Message.Cloud, space: .everywhere, range: 1 ... (Int32.max - 1)) transaction.addHole(peerId: peerId, threadId: nil, namespace: Namespaces.Message.Cloud, space: .everywhere, range: 1 ... (Int32.max - 1))
} }
return false return false
} else { } else {
@ -1670,7 +1699,40 @@ public final class AccountViewTracker {
public func aroundMessageOfInterestHistoryViewForLocation(_ chatLocation: ChatLocationInput, ignoreMessagesInTimestampRange: ClosedRange<Int32>? = nil, count: Int, tagMask: MessageTags? = nil, appendMessagesFromTheSameGroup: Bool = false, orderStatistics: MessageHistoryViewOrderStatistics = [], additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { public func aroundMessageOfInterestHistoryViewForLocation(_ chatLocation: ChatLocationInput, ignoreMessagesInTimestampRange: ClosedRange<Int32>? = nil, count: Int, tagMask: MessageTags? = nil, appendMessagesFromTheSameGroup: Bool = false, orderStatistics: MessageHistoryViewOrderStatistics = [], additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> {
if let account = self.account { if let account = self.account {
let signal = account.postbox.aroundMessageOfInterestHistoryViewForChatLocation(chatLocation, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, count: count, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: .not(Namespaces.Message.allScheduled), orderStatistics: orderStatistics, additionalData: wrappedHistoryViewAdditionalData(chatLocation: chatLocation, additionalData: additionalData)) let signal: Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError>
if let peerId = chatLocation.peerId, let threadId = chatLocation.threadId, tagMask == nil {
return account.postbox.transaction { transaction -> MessageHistoryThreadData? in
return transaction.getMessageHistoryThreadInfo(peerId: peerId, threadId: threadId)?.get(MessageHistoryThreadData.self)
}
|> mapToSignal { threadInfo -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> in
if let threadInfo = threadInfo {
let anchor: HistoryViewInputAnchor
if threadInfo.incomingUnreadCount > 0 && tagMask == nil {
let customUnreadMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: threadInfo.maxIncomingReadId)
anchor = .message(customUnreadMessageId)
} else {
anchor = .upperBound
}
return account.postbox.aroundMessageHistoryViewForLocation(
chatLocation,
anchor: anchor,
ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange,
count: count,
fixedCombinedReadStates: nil,
topTaggedMessageIdNamespaces: [],
tagMask: tagMask,
appendMessagesFromTheSameGroup: false,
namespaces: .not(Namespaces.Message.allScheduled),
orderStatistics: orderStatistics
)
}
return account.postbox.aroundMessageOfInterestHistoryViewForChatLocation(chatLocation, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, count: count, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: .not(Namespaces.Message.allScheduled), orderStatistics: orderStatistics, customUnreadMessageId: nil, additionalData: wrappedHistoryViewAdditionalData(chatLocation: chatLocation, additionalData: additionalData))
}
} else {
signal = account.postbox.aroundMessageOfInterestHistoryViewForChatLocation(chatLocation, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, count: count, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tagMask: tagMask, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: .not(Namespaces.Message.allScheduled), orderStatistics: orderStatistics, customUnreadMessageId: nil, additionalData: wrappedHistoryViewAdditionalData(chatLocation: chatLocation, additionalData: additionalData))
}
return wrappedMessageHistorySignal(chatLocation: chatLocation, signal: signal, addHoleIfNeeded: true) return wrappedMessageHistorySignal(chatLocation: chatLocation, signal: signal, addHoleIfNeeded: true)
} else { } else {
return .never() return .never()
@ -1961,13 +2023,13 @@ public final class AccountViewTracker {
} }
} }
public func unseenPersonalMessagesAndReactionCount(peerId: PeerId) -> Signal<(mentionCount: Int32, reactionCount: Int32), NoError> { public func unseenPersonalMessagesAndReactionCount(peerId: PeerId, threadId: Int64?) -> Signal<(mentionCount: Int32, reactionCount: Int32), NoError> {
if let account = self.account { if let account = self.account {
let pendingMentionsKey: PostboxViewKey = .pendingMessageActionsSummary(type: .consumeUnseenPersonalMessage, peerId: peerId, namespace: Namespaces.Message.Cloud) let pendingMentionsKey: PostboxViewKey = .pendingMessageActionsSummary(type: .consumeUnseenPersonalMessage, peerId: peerId, namespace: Namespaces.Message.Cloud)
let summaryMentionsKey: PostboxViewKey = .historyTagSummaryView(tag: .unseenPersonalMessage, peerId: peerId, namespace: Namespaces.Message.Cloud) let summaryMentionsKey: PostboxViewKey = .historyTagSummaryView(tag: .unseenPersonalMessage, peerId: peerId, threadId: threadId, namespace: Namespaces.Message.Cloud)
let pendingReactionsKey: PostboxViewKey = .pendingMessageActionsSummary(type: .readReaction, peerId: peerId, namespace: Namespaces.Message.Cloud) let pendingReactionsKey: PostboxViewKey = .pendingMessageActionsSummary(type: .readReaction, peerId: peerId, namespace: Namespaces.Message.Cloud)
let summaryReactionsKey: PostboxViewKey = .historyTagSummaryView(tag: .unseenReaction, peerId: peerId, namespace: Namespaces.Message.Cloud) let summaryReactionsKey: PostboxViewKey = .historyTagSummaryView(tag: .unseenReaction, peerId: peerId, threadId: threadId, namespace: Namespaces.Message.Cloud)
return account.postbox.combinedView(keys: [pendingMentionsKey, summaryMentionsKey, pendingReactionsKey, summaryReactionsKey]) return account.postbox.combinedView(keys: [pendingMentionsKey, summaryMentionsKey, pendingReactionsKey, summaryReactionsKey])
|> map { views -> (mentionCount: Int32, reactionCount: Int32) in |> map { views -> (mentionCount: Int32, reactionCount: Int32) in

View File

@ -5,12 +5,14 @@ import SwiftSignalKit
public struct HistoryPreloadIndex: Hashable, Comparable, CustomStringConvertible { public struct HistoryPreloadIndex: Hashable, Comparable, CustomStringConvertible {
public let index: ChatListIndex? public let index: ChatListIndex?
public let threadId: Int64?
public let hasUnread: Bool public let hasUnread: Bool
public let isMuted: Bool public let isMuted: Bool
public let isPriority: Bool public let isPriority: Bool
public init(index: ChatListIndex?, hasUnread: Bool, isMuted: Bool, isPriority: Bool) { public init(index: ChatListIndex?, threadId: Int64?, hasUnread: Bool, isMuted: Bool, isPriority: Bool) {
self.index = index self.index = index
self.threadId = threadId
self.hasUnread = hasUnread self.hasUnread = hasUnread
self.isMuted = isMuted self.isMuted = isMuted
self.isPriority = isPriority self.isPriority = isPriority
@ -38,6 +40,15 @@ public struct HistoryPreloadIndex: Hashable, Comparable, CustomStringConvertible
return false return false
} }
} }
if lhs.index == rhs.index {
if let lhsThreadId = lhs.threadId, let rhsThreadId = rhs.threadId {
if lhsThreadId != rhsThreadId {
return lhsThreadId < rhsThreadId
}
}
}
if let lhsIndex = lhs.index, let rhsIndex = rhs.index { if let lhsIndex = lhs.index, let rhsIndex = rhs.index {
return lhsIndex > rhsIndex return lhsIndex > rhsIndex
} else if lhs.index != nil { } else if lhs.index != nil {
@ -117,6 +128,7 @@ private final class HistoryPreloadEntry: Comparable {
private final class HistoryPreloadViewContext { private final class HistoryPreloadViewContext {
var index: ChatListIndex? var index: ChatListIndex?
var threadId: Int64?
var hasUnread: Bool? var hasUnread: Bool?
var isMuted: Bool? var isMuted: Bool?
var isPriority: Bool var isPriority: Bool
@ -125,7 +137,7 @@ private final class HistoryPreloadViewContext {
var media: [HolesViewMedia] = [] var media: [HolesViewMedia] = []
var preloadIndex: HistoryPreloadIndex { var preloadIndex: HistoryPreloadIndex {
return HistoryPreloadIndex(index: self.index, hasUnread: self.hasUnread ?? false, isMuted: self.isMuted ?? true, isPriority: self.isPriority) return HistoryPreloadIndex(index: self.index, threadId: self.threadId, hasUnread: self.hasUnread ?? false, isMuted: self.isMuted ?? true, isPriority: self.isPriority)
} }
var currentHole: HistoryPreloadHole? { var currentHole: HistoryPreloadHole? {
@ -136,8 +148,9 @@ private final class HistoryPreloadViewContext {
} }
} }
init(index: ChatListIndex?, hasUnread: Bool?, isMuted: Bool?, isPriority: Bool) { init(index: ChatListIndex?, threadId: Int64?, hasUnread: Bool?, isMuted: Bool?, isPriority: Bool) {
self.index = index self.index = index
self.threadId = threadId
self.hasUnread = hasUnread self.hasUnread = hasUnread
self.isMuted = isMuted self.isMuted = isMuted
self.isPriority = isPriority self.isPriority = isPriority
@ -149,7 +162,7 @@ private final class HistoryPreloadViewContext {
} }
private enum ChatHistoryPreloadEntity: Hashable { private enum ChatHistoryPreloadEntity: Hashable {
case peer(PeerId) case peer(peerId: PeerId, threadId: Int64?)
} }
private struct ChatHistoryPreloadIndex { private struct ChatHistoryPreloadIndex {
@ -236,11 +249,13 @@ private final class AdditionalPreloadPeerIdsContext {
public struct ChatHistoryPreloadItem : Equatable { public struct ChatHistoryPreloadItem : Equatable {
public let index: ChatListIndex public let index: ChatListIndex
public let threadId: Int64?
public let isMuted: Bool public let isMuted: Bool
public let hasUnread: Bool public let hasUnread: Bool
public init(index: ChatListIndex, isMuted: Bool, hasUnread: Bool) { public init(index: ChatListIndex, threadId: Int64?, isMuted: Bool, hasUnread: Bool) {
self.index = index self.index = index
self.threadId = threadId
self.isMuted = isMuted self.isMuted = isMuted
self.hasUnread = hasUnread self.hasUnread = hasUnread
} }
@ -365,7 +380,7 @@ final class ChatHistoryPreloadManager {
var indices: [(ChatHistoryPreloadIndex, Bool, Bool)] = [] var indices: [(ChatHistoryPreloadIndex, Bool, Bool)] = []
for item in loadItems { for item in loadItems {
indices.append((ChatHistoryPreloadIndex(index: item.index, entity: .peer(item.index.messageIndex.id.peerId)), item.hasUnread, item.isMuted)) indices.append((ChatHistoryPreloadIndex(index: item.index, entity: .peer(peerId: item.index.messageIndex.id.peerId, threadId: item.threadId)), item.hasUnread, item.isMuted))
} }
strongSelf.update(indices: indices, additionalPeerIds: additionalPeerIds) strongSelf.update(indices: indices, additionalPeerIds: additionalPeerIds)
@ -376,7 +391,7 @@ final class ChatHistoryPreloadManager {
self.queue.async { self.queue.async {
var validEntityIds = Set(indices.map { $0.0.entity }) var validEntityIds = Set(indices.map { $0.0.entity })
for peerId in additionalPeerIds { for peerId in additionalPeerIds {
validEntityIds.insert(.peer(peerId)) validEntityIds.insert(.peer(peerId: peerId, threadId: nil))
} }
var removedEntityIds: [ChatHistoryPreloadEntity] = [] var removedEntityIds: [ChatHistoryPreloadEntity] = []
@ -400,7 +415,7 @@ final class ChatHistoryPreloadManager {
} }
for peerId in additionalPeerIds { for peerId in additionalPeerIds {
if !existingPeerIds.contains(peerId) { if !existingPeerIds.contains(peerId) {
combinedIndices.append((ChatHistoryPreloadIndex(index: ChatListIndex.absoluteLowerBound, entity: .peer(peerId)), false, true, true)) combinedIndices.append((ChatHistoryPreloadIndex(index: ChatListIndex.absoluteLowerBound, entity: .peer(peerId: peerId, threadId: nil)), false, true, true))
} }
} }
@ -418,12 +433,17 @@ final class ChatHistoryPreloadManager {
} }
} }
} else { } else {
let view = HistoryPreloadViewContext(index: index.index, hasUnread: hasUnread, isMuted: isMuted, isPriority: isPriority) var threadId: Int64?
switch index.entity {
case let .peer(_, threadIdValue):
threadId = threadIdValue
}
let view = HistoryPreloadViewContext(index: index.index, threadId: threadId, hasUnread: hasUnread, isMuted: isMuted, isPriority: isPriority)
self.views[index.entity] = view self.views[index.entity] = view
let key: PostboxViewKey let key: PostboxViewKey
switch index.entity { switch index.entity {
case let .peer(peerId): case let .peer(peerId, threadId):
key = .messageOfInterestHole(location: .peer(peerId), namespace: Namespaces.Message.Cloud, count: 70) key = .messageOfInterestHole(location: .peer(peerId: peerId, threadId: threadId), namespace: Namespaces.Message.Cloud, count: 70)
} }
view.disposable.set((self.postbox.combinedView(keys: [key]) view.disposable.set((self.postbox.combinedView(keys: [key])
|> deliverOn(self.queue)).start(next: { [weak self] next in |> deliverOn(self.queue)).start(next: { [weak self] next in
@ -453,8 +473,8 @@ final class ChatHistoryPreloadManager {
let holeIsUpdated = previousHole != updatedHole let holeIsUpdated = previousHole != updatedHole
switch index.entity { switch index.entity {
case let .peer(peerId): case let .peer(peerId, threadId):
Logger.shared.log("HistoryPreload", "view \(peerId) hole \(String(describing: updatedHole)) isUpdated: \(holeIsUpdated)") Logger.shared.log("HistoryPreload", "view \(peerId) (threadId: \(String(describing: threadId)) hole \(String(describing: updatedHole)) isUpdated: \(holeIsUpdated)")
} }
if previousHole != updatedHole { if previousHole != updatedHole {

View File

@ -203,7 +203,7 @@ struct FetchedChatList {
var pinnedItemIds: [PeerId]? var pinnedItemIds: [PeerId]?
var folderSummaries: [PeerGroupId: PeerGroupUnreadCountersSummary] var folderSummaries: [PeerGroupId: PeerGroupUnreadCountersSummary]
var peerGroupIds: [PeerId: PeerGroupId] var peerGroupIds: [PeerId: PeerGroupId]
var threadInfos: [MessageId: MessageHistoryThreadData] var threadInfos: [MessageId: StoreMessageHistoryThreadData]
} }
func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLocation, upperBound: MessageIndex, hash: Int64, limit: Int32) -> Signal<FetchedChatList?, NoError> { func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLocation, upperBound: MessageIndex, hash: Int64, limit: Int32) -> Signal<FetchedChatList?, NoError> {

View File

@ -1,3 +1,16 @@
import Foundation import Foundation
import Postbox
enum InternalAccountState {
static func addMessages(transaction: Transaction, messages: [StoreMessage], location: AddMessagesLocation) -> [Int64 : MessageId] {
return transaction.addMessages(messages, location: location)
}
static func deleteMessages(transaction: Transaction, ids: [MessageId], forEachMedia: ((Media) -> Void)?) {
transaction.deleteMessages(ids, forEachMedia: forEachMedia)
}
static func invalidateChannelState(peerId: PeerId) {
}
}

View File

@ -156,7 +156,7 @@ final class HistoryViewStateValidationContexts {
} }
} }
if let location = location, case let .thread(peerId, threadId, _) = location { if let location = location, let peerId = location.peerId, let threadId = location.threadId {
var rangesToInvalidate: [[MessageId]] = [] var rangesToInvalidate: [[MessageId]] = []
let addToRange: (MessageId, inout [[MessageId]]) -> Void = { id, ranges in let addToRange: (MessageId, inout [[MessageId]]) -> Void = { id, ranges in
if ranges.isEmpty { if ranges.isEmpty {
@ -219,7 +219,7 @@ final class HistoryViewStateValidationContexts {
context.batchReferences[messageId] = batch context.batchReferences[messageId] = batch
} }
disposable.set((validateReplyThreadMessagesBatch(postbox: self.postbox, network: self.network, accountPeerId: self.accountPeerId, peerId: peerId, threadMessageId: makeThreadIdMessageId(peerId: peerId, threadId: threadId).id, messageIds: messages) disposable.set((validateReplyThreadMessagesBatch(postbox: self.postbox, network: self.network, accountPeerId: self.accountPeerId, peerId: peerId, threadMessageId: makeThreadIdMessageId(peerId: peerId, threadId: threadId).id, tag: view.tagMask, messageIds: messages)
|> deliverOn(self.queue)).start(completed: { [weak self, weak batch] in |> deliverOn(self.queue)).start(completed: { [weak self, weak batch] in
if let strongSelf = self, let context = strongSelf.contexts[id], let batch = batch { if let strongSelf = self, let context = strongSelf.contexts[id], let batch = batch {
var completedMessageIds: [MessageId] = [] var completedMessageIds: [MessageId] = []
@ -355,7 +355,7 @@ final class HistoryViewStateValidationContexts {
} }
} else if view.namespaces.contains(Namespaces.Message.ScheduledCloud) { } else if view.namespaces.contains(Namespaces.Message.ScheduledCloud) {
if let _ = self.contexts[id] { if let _ = self.contexts[id] {
} else if let location = location, case let .peer(peerId) = location { } else if let location = location, case let .peer(peerId, _) = location {
let timestamp = self.network.context.globalTime() let timestamp = self.network.context.globalTime()
if let previousTimestamp = self.previousPeerValidationTimestamps[peerId], timestamp < previousTimestamp + 60 { if let previousTimestamp = self.previousPeerValidationTimestamps[peerId], timestamp < previousTimestamp + 60 {
} else { } else {
@ -543,7 +543,7 @@ private func validateChannelMessagesBatch(postbox: Postbox, network: Network, ac
} |> switchToLatest } |> switchToLatest
} }
private func validateReplyThreadMessagesBatch(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, threadMessageId: Int32, messageIds: [MessageId]) -> Signal<Void, NoError> { private func validateReplyThreadMessagesBatch(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, threadMessageId: Int32, tag: MessageTags?, messageIds: [MessageId]) -> Signal<Void, NoError> {
return postbox.transaction { transaction -> Signal<Void, NoError> in return postbox.transaction { transaction -> Signal<Void, NoError> in
var previousMessages: [Message] = [] var previousMessages: [Message] = []
var previous: [MessageId: Message] = [:] var previous: [MessageId: Message] = [:]
@ -556,10 +556,22 @@ private func validateReplyThreadMessagesBatch(postbox: Postbox, network: Network
var signal: Signal<ValidatedMessages, MTRpcError> var signal: Signal<ValidatedMessages, MTRpcError>
let hash = hashForMessages(previousMessages, withChannelIds: false) let hash = hashForMessages(previousMessages, withChannelIds: false)
Logger.shared.log("HistoryValidation", "validate reply thread batch for \(peerId): \(previousMessages.map({ $0.id }))") Logger.shared.log("HistoryValidation", "validate reply thread batch (tag: \(String(describing: tag?.rawValue)) for \(peerId): \(previousMessages.map({ $0.id }))")
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
let requestSignal: Signal<Api.messages.Messages, MTRpcError> let requestSignal: Signal<Api.messages.Messages, MTRpcError>
requestSignal = network.request(Api.functions.messages.getReplies(peer: inputPeer, msgId: threadMessageId, offsetId: messageIds[messageIds.count - 1].id, offsetDate: 0, addOffset: -1, limit: Int32(messageIds.count), maxId: messageIds[messageIds.count - 1].id + 1, minId: messageIds[0].id - 1, hash: hash))
if let tag = tag {
if let filter = messageFilterForTagMask(tag) {
var flags: Int32 = 0
flags |= (1 << 1)
requestSignal = network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: "", fromId: nil, topMsgId: threadMessageId, filter: filter, minDate: 0, maxDate: 0, offsetId: messageIds[messageIds.count - 1].id + 1, addOffset: 0, limit: Int32(messageIds.count), maxId: messageIds[messageIds.count - 1].id + 1, minId: messageIds[0].id - 1, hash: hash))
} else {
return .complete()
}
} else {
requestSignal = network.request(Api.functions.messages.getReplies(peer: inputPeer, msgId: threadMessageId, offsetId: messageIds[messageIds.count - 1].id, offsetDate: 0, addOffset: -1, limit: Int32(messageIds.count), maxId: messageIds[messageIds.count - 1].id + 1, minId: messageIds[0].id - 1, hash: hash))
}
signal = requestSignal signal = requestSignal
|> map { result -> ValidatedMessages in |> map { result -> ValidatedMessages in

View File

@ -406,7 +406,14 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
minMaxRange = 1 ... Int32.max - 1 minMaxRange = 1 ... Int32.max - 1
} }
request = source.request(Api.functions.messages.getUnreadMentions(flags: 0, peer: inputPeer, topMsgId: nil, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId)) var flags: Int32 = 0
var topMsgId: Int32?
if let threadId = peerInput.requestThreadId {
flags |= (1 << 1)
topMsgId = threadId.id
}
request = source.request(Api.functions.messages.getUnreadMentions(flags: flags, peer: inputPeer, topMsgId: topMsgId, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId))
} else if tag == .unseenReaction { } else if tag == .unseenReaction {
let offsetId: Int32 let offsetId: Int32
let addOffset: Int32 let addOffset: Int32
@ -454,7 +461,14 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
minMaxRange = 1 ... Int32.max - 1 minMaxRange = 1 ... Int32.max - 1
} }
request = source.request(Api.functions.messages.getUnreadReactions(flags: 0, peer: inputPeer, topMsgId: nil, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId)) var flags: Int32 = 0
var topMsgId: Int32?
if let threadId = peerInput.requestThreadId {
flags |= (1 << 0)
topMsgId = threadId.id
}
request = source.request(Api.functions.messages.getUnreadReactions(flags: flags, peer: inputPeer, topMsgId: topMsgId, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId))
} else if tag == .liveLocation { } else if tag == .liveLocation {
let selectedLimit = count let selectedLimit = count
@ -511,7 +525,14 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
minMaxRange = 1 ... (Int32.max - 1) minMaxRange = 1 ... (Int32.max - 1)
} }
request = source.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, topMsgId: nil, filter: filter, minDate: 0, maxDate: 0, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: 0)) var flags: Int32 = 0
var topMsgId: Int32?
if let threadId = peerInput.requestThreadId {
flags |= (1 << 1)
topMsgId = threadId.id
}
request = source.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: "", fromId: nil, topMsgId: topMsgId, filter: filter, minDate: 0, maxDate: 0, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: 0))
} else { } else {
assertionFailure() assertionFailure()
minMaxRange = 1 ... 1 minMaxRange = 1 ... 1
@ -669,13 +690,7 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
switch peerInput { switch peerInput {
case let .direct(peerId, threadId): case let .direct(peerId, threadId):
if let threadId = threadId { transaction.removeHole(peerId: peerId, threadId: threadId, namespace: namespace, space: space, range: filledRange)
for range in strictFilledIndices.rangeView {
transaction.removeThreadIndexHole(peerId: peerId, threadId: threadId, namespace: namespace, space: space, range: Int32(range.lowerBound) ... Int32(range.upperBound))
}
} else {
transaction.removeHole(peerId: peerId, namespace: namespace, space: space, range: filledRange)
}
case .threadFromChannel: case .threadFromChannel:
break break
} }
@ -736,9 +751,11 @@ func fetchChatListHole(postbox: Postbox, network: Network, accountPeerId: PeerId
}) })
for (threadMessageId, data) in fetchedChats.threadInfos { for (threadMessageId, data) in fetchedChats.threadInfos {
if let entry = CodableEntry(data) { if let entry = CodableEntry(data.data) {
transaction.setMessageHistoryThreadInfo(peerId: threadMessageId.peerId, threadId: Int64(threadMessageId.id), info: entry) transaction.setMessageHistoryThreadInfo(peerId: threadMessageId.peerId, threadId: Int64(threadMessageId.id), info: entry)
} }
transaction.replaceMessageTagSummary(peerId: threadMessageId.peerId, threadId: Int64(threadMessageId.id), tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: data.unreadMentionCount, maxId: data.topMessageId)
transaction.replaceMessageTagSummary(peerId: threadMessageId.peerId, threadId: Int64(threadMessageId.id), tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: data.unreadReactionCount, maxId: data.topMessageId)
} }
updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: fetchedChats.peerPresences) updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: fetchedChats.peerPresences)
@ -778,10 +795,10 @@ func fetchChatListHole(postbox: Postbox, network: Network, accountPeerId: PeerId
} }
for (peerId, summary) in fetchedChats.mentionTagSummaries { for (peerId, summary) in fetchedChats.mentionTagSummaries {
transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: summary.count, maxId: summary.range.maxId) transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: summary.count, maxId: summary.range.maxId)
} }
for (peerId, summary) in fetchedChats.reactionTagSummaries { for (peerId, summary) in fetchedChats.reactionTagSummaries {
transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: summary.count, maxId: summary.range.maxId) transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: summary.count, maxId: summary.range.maxId)
} }
for (groupId, summary) in fetchedChats.folderSummaries { for (groupId, summary) in fetchedChats.folderSummaries {

View File

@ -377,7 +377,7 @@ private func synchronizeUnseenPersonalMentionsTag(postbox: Postbox, network: Net
} }
return postbox.transaction { transaction -> Void in return postbox.transaction { transaction -> Void in
transaction.replaceMessageTagSummary(peerId: entry.key.peerId, tagMask: entry.key.tagMask, namespace: entry.key.namespace, count: apiUnreadMentionsCount, maxId: apiTopMessage) transaction.replaceMessageTagSummary(peerId: entry.key.peerId, threadId: nil, tagMask: entry.key.tagMask, namespace: entry.key.namespace, count: apiUnreadMentionsCount, maxId: apiTopMessage)
} }
} else { } else {
return .complete() return .complete()
@ -419,7 +419,7 @@ private func synchronizeUnseenReactionsTag(postbox: Postbox, network: Network, e
} }
return postbox.transaction { transaction -> Void in return postbox.transaction { transaction -> Void in
transaction.replaceMessageTagSummary(peerId: entry.key.peerId, tagMask: entry.key.tagMask, namespace: entry.key.namespace, count: apiUnreadReactionsCount, maxId: apiTopMessage) transaction.replaceMessageTagSummary(peerId: entry.key.peerId, threadId: nil, tagMask: entry.key.tagMask, namespace: entry.key.namespace, count: apiUnreadReactionsCount, maxId: apiTopMessage)
} }
} else { } else {
return .complete() return .complete()

View File

@ -24,13 +24,11 @@ private final class ManagedMessageHistoryHolesState {
for entry in entries { for entry in entries {
switch entry.hole { switch entry.hole {
case let .peer(hole): case .peer:
if hole.threadId == nil { if self.holeDisposables[entry] == nil {
if self.holeDisposables[entry] == nil { let disposable = MetaDisposable()
let disposable = MetaDisposable() self.holeDisposables[entry] = disposable
self.holeDisposables[entry] = disposable added[entry] = disposable
added[entry] = disposable
}
} }
} }
} }
@ -55,7 +53,7 @@ func managedMessageHistoryHoles(accountPeerId: PeerId, network: Network, postbox
for (entry, disposable) in added { for (entry, disposable) in added {
switch entry.hole { switch entry.hole {
case let .peer(hole): case let .peer(hole):
disposable.set(fetchMessageHistoryHole(accountPeerId: accountPeerId, source: .network(network), postbox: postbox, peerInput: .direct(peerId: hole.peerId, threadId: nil), namespace: hole.namespace, direction: entry.direction, space: entry.space, count: entry.count).start()) disposable.set(fetchMessageHistoryHole(accountPeerId: accountPeerId, source: .network(network), postbox: postbox, peerInput: .direct(peerId: hole.peerId, threadId: hole.threadId), namespace: hole.namespace, direction: entry.direction, space: entry.space, count: entry.count).start())
} }
} }
}) })

View File

@ -72,7 +72,7 @@ func managedPendingPeerNotificationSettings(postbox: Postbox, network: Network)
} }
for (peerId, settings, disposable) in beginOperations { for (peerId, settings, disposable) in beginOperations {
let signal = pushPeerNotificationSettings(postbox: postbox, network: network, peerId: peerId, settings: settings) let signal = pushPeerNotificationSettings(postbox: postbox, network: network, peerId: peerId, threadId: nil, settings: settings)
disposable.set(signal.start()) disposable.set(signal.start())
} }
}) })
@ -89,62 +89,109 @@ func managedPendingPeerNotificationSettings(postbox: Postbox, network: Network)
} }
} }
private func pushPeerNotificationSettings(postbox: Postbox, network: Network, peerId: PeerId, settings: PeerNotificationSettings) -> Signal<Void, NoError> { func pushPeerNotificationSettings(postbox: Postbox, network: Network, peerId: PeerId, threadId: Int64?, settings: PeerNotificationSettings) -> Signal<Void, NoError> {
return postbox.transaction { transaction -> Signal<Void, NoError> in return postbox.transaction { transaction -> Signal<Void, NoError> in
if let peer = transaction.getPeer(peerId) { if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
var notificationPeerId = peerId var notificationPeerId = peerId
if let associatedPeerId = peer.associatedPeerId { if let associatedPeerId = peer.associatedPeerId {
notificationPeerId = associatedPeerId notificationPeerId = associatedPeerId
} }
if let notificationPeer = transaction.getPeer(notificationPeerId), let inputPeer = apiInputPeer(notificationPeer), let settings = settings as? TelegramPeerNotificationSettings { if let threadId = threadId {
let showPreviews: Api.Bool? if let data = transaction.getMessageHistoryThreadInfo(peerId: peerId, threadId: threadId)?.get(MessageHistoryThreadData.self) {
switch settings.displayPreviews { let settings = data.notificationSettings
let showPreviews: Api.Bool?
switch settings.displayPreviews {
case .default: case .default:
showPreviews = nil showPreviews = nil
case .show: case .show:
showPreviews = .boolTrue showPreviews = .boolTrue
case .hide: case .hide:
showPreviews = .boolFalse showPreviews = .boolFalse
} }
let muteUntil: Int32? let muteUntil: Int32?
switch settings.muteState { switch settings.muteState {
case let .muted(until): case let .muted(until):
muteUntil = until muteUntil = until
case .unmuted: case .unmuted:
muteUntil = 0 muteUntil = 0
case .default: case .default:
muteUntil = nil muteUntil = nil
} }
let sound: Api.NotificationSound? = settings.messageSound.apiSound let sound: Api.NotificationSound? = settings.messageSound.apiSound
var flags: Int32 = 0 var flags: Int32 = 0
if showPreviews != nil { if showPreviews != nil {
flags |= (1 << 0) flags |= (1 << 0)
} }
if muteUntil != nil { if muteUntil != nil {
flags |= (1 << 2) flags |= (1 << 2)
} }
if sound != nil { if sound != nil {
flags |= (1 << 3) flags |= (1 << 3)
} }
let inputSettings = Api.InputPeerNotifySettings.inputPeerNotifySettings(flags: flags, showPreviews: showPreviews, silent: nil, muteUntil: muteUntil, sound: sound) let inputSettings = Api.InputPeerNotifySettings.inputPeerNotifySettings(flags: flags, showPreviews: showPreviews, silent: nil, muteUntil: muteUntil, sound: sound)
return network.request(Api.functions.account.updateNotifySettings(peer: .inputNotifyPeer(peer: inputPeer), settings: inputSettings)) return network.request(Api.functions.account.updateNotifySettings(peer: .inputNotifyForumTopic(peer: inputPeer, topMsgId: Int32(clamping: threadId)), settings: inputSettings))
|> `catch` { _ -> Signal<Api.Bool, NoError> in |> `catch` { _ -> Signal<Api.Bool, NoError> in
return .single(.boolFalse) return .single(.boolFalse)
} }
|> mapToSignal { result -> Signal<Void, NoError> in |> mapToSignal { result -> Signal<Void, NoError> in
return postbox.transaction { transaction -> Void in return postbox.transaction { transaction -> Void in
transaction.updateCurrentPeerNotificationSettings([notificationPeerId: settings])
if let pending = transaction.getPendingPeerNotificationSettings(peerId), pending.isEqual(to: settings) {
transaction.updatePendingPeerNotificationSettings(peerId: peerId, settings: nil)
} }
} }
} else {
return .complete()
} }
} else { } else {
if let pending = transaction.getPendingPeerNotificationSettings(peerId), pending.isEqual(to: settings) { if let notificationPeer = transaction.getPeer(notificationPeerId), let inputPeer = apiInputPeer(notificationPeer), let settings = settings as? TelegramPeerNotificationSettings {
transaction.updatePendingPeerNotificationSettings(peerId: peerId, settings: nil) let showPreviews: Api.Bool?
switch settings.displayPreviews {
case .default:
showPreviews = nil
case .show:
showPreviews = .boolTrue
case .hide:
showPreviews = .boolFalse
}
let muteUntil: Int32?
switch settings.muteState {
case let .muted(until):
muteUntil = until
case .unmuted:
muteUntil = 0
case .default:
muteUntil = nil
}
let sound: Api.NotificationSound? = settings.messageSound.apiSound
var flags: Int32 = 0
if showPreviews != nil {
flags |= (1 << 0)
}
if muteUntil != nil {
flags |= (1 << 2)
}
if sound != nil {
flags |= (1 << 3)
}
let inputSettings = Api.InputPeerNotifySettings.inputPeerNotifySettings(flags: flags, showPreviews: showPreviews, silent: nil, muteUntil: muteUntil, sound: sound)
return network.request(Api.functions.account.updateNotifySettings(peer: .inputNotifyPeer(peer: inputPeer), settings: inputSettings))
|> `catch` { _ -> Signal<Api.Bool, NoError> in
return .single(.boolFalse)
}
|> mapToSignal { result -> Signal<Void, NoError> in
return postbox.transaction { transaction -> Void in
transaction.updateCurrentPeerNotificationSettings([notificationPeerId: settings])
if let pending = transaction.getPendingPeerNotificationSettings(peerId), pending.isEqual(to: settings) {
transaction.updatePendingPeerNotificationSettings(peerId: peerId, settings: nil)
}
}
}
} else {
if let pending = transaction.getPendingPeerNotificationSettings(peerId), pending.isEqual(to: settings) {
transaction.updatePendingPeerNotificationSettings(peerId: peerId, settings: nil)
}
return .complete()
} }
return .complete()
} }
} else { } else {
if let pending = transaction.getPendingPeerNotificationSettings(peerId), pending.isEqual(to: settings) { if let pending = transaction.getPendingPeerNotificationSettings(peerId), pending.isEqual(to: settings) {

View File

@ -284,7 +284,7 @@ private func synchronizeMarkAllUnseenReactions(transaction: Transaction, postbox
return .complete() return .complete()
} }
let signal = network.request(Api.functions.messages.readReactions(peer: inputPeer)) let signal = network.request(Api.functions.messages.readReactions(flags: 0, peer: inputPeer, topMsgId: nil))
|> map(Optional.init) |> map(Optional.init)
|> `catch` { _ -> Signal<Api.messages.AffectedHistory?, Bool> in |> `catch` { _ -> Signal<Api.messages.AffectedHistory?, Bool> in
return .fail(true) return .fail(true)
@ -308,90 +308,6 @@ private func synchronizeMarkAllUnseenReactions(transaction: Transaction, postbox
|> `catch` { _ -> Signal<Void, NoError> in |> `catch` { _ -> Signal<Void, NoError> in
return .complete() return .complete()
} }
/*let inputChannel = transaction.getPeer(peerId).flatMap(apiInputChannel)
let limit: Int32 = 100
let oneOperation: (Int32) -> Signal<Int32?, MTRpcError> = { maxId in
return network.request(Api.functions.messages.getUnreadReactions(peer: inputPeer, offsetId: maxId, addOffset: maxId == 0 ? 0 : -1, limit: limit, maxId: maxId == 0 ? 0 : (maxId + 1), minId: 1))
|> mapToSignal { result -> Signal<[MessageId], MTRpcError> in
switch result {
case let .messages(messages, _, _):
return .single(messages.compactMap({ $0.id() }))
case let .channelMessages(_, _, _, _, messages, _, _):
return .single(messages.compactMap({ $0.id() }))
case .messagesNotModified:
return .single([])
case let .messagesSlice(_, _, _, _, messages, _, _):
return .single(messages.compactMap({ $0.id() }))
}
}
|> mapToSignal { ids -> Signal<Int32?, MTRpcError> in
let filteredIds = ids.filter { $0.id <= operation.maxId }
if filteredIds.isEmpty {
return .single(ids.min()?.id)
}
if peerId.namespace == Namespaces.Peer.CloudChannel {
guard let inputChannel = inputChannel else {
return .single(nil)
}
return network.request(Api.functions.channels.readMessageContents(channel: inputChannel, id: filteredIds.map { $0.id }))
|> map { result -> Int32? in
if ids.count < limit {
return nil
} else {
return ids.min()?.id
}
}
} else {
return network.request(Api.functions.messages.readMessageContents(id: filteredIds.map { $0.id }))
|> map { result -> Int32? in
switch result {
case let .affectedMessages(pts, ptsCount):
stateManager.addUpdateGroups([.updatePts(pts: pts, ptsCount: ptsCount)])
}
if ids.count < limit {
return nil
} else {
return ids.min()?.id
}
}
}
}
}
let currentMaxId = Atomic<Int32>(value: 0)
let loopOperations: Signal<Void, GetUnseenIdsError> = (
(
deferred {
return oneOperation(currentMaxId.with { $0 })
}
|> `catch` { error -> Signal<Int32?, GetUnseenIdsError> in
return .fail(.error(error))
}
)
|> mapToSignal { resultId -> Signal<Void, GetUnseenIdsError> in
if let resultId = resultId {
let previous = currentMaxId.swap(resultId)
if previous == resultId {
return .fail(.done)
} else {
return .complete()
}
} else {
return .fail(.done)
}
}
|> `catch` { error -> Signal<Void, GetUnseenIdsError> in
switch error {
case .done, .error:
return .fail(error)
}
}
|> restart
)
return loopOperations
|> `catch` { _ -> Signal<Void, NoError> in
return .complete()
}*/
} }
func markUnseenReactionMessage(transaction: Transaction, id: MessageId, addSynchronizeAction: Bool) { func markUnseenReactionMessage(transaction: Transaction, id: MessageId, addSynchronizeAction: Bool) {

View File

@ -68,11 +68,7 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = {
if channel.flags.contains(.isForum) { if channel.flags.contains(.isForum) {
return [] return []
} }
if channel.username != nil { return .group
return .group
} else {
return .group
}
} }
} else { } else {
assertionFailure() assertionFailure()

View File

@ -73,11 +73,41 @@ public func getCloudLegacySound(id: Int64) -> (id: Int32, category: CloudSoundBu
return nil return nil
} }
public enum PeerMuteState: Equatable { public enum PeerMuteState: Codable, Equatable {
case `default` case `default`
case unmuted case unmuted
case muted(until: Int32) case muted(until: Int32)
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
switch try container.decode(Int32.self, forKey: "m.v") {
case 0:
self = .default
case 1:
self = .muted(until: try container.decode(Int32.self, forKey: "m.u"))
case 2:
self = .unmuted
default:
assertionFailure()
self = .default
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
switch self {
case .default:
try container.encode(0 as Int32, forKey: "m.v")
case let .muted(until):
try container.encode(1 as Int32, forKey: "m.v")
try container.encode(until, forKey: "m.u")
case .unmuted:
try container.encode(2 as Int32, forKey: "m.v")
}
}
fileprivate static func decodeInline(_ decoder: PostboxDecoder) -> PeerMuteState { fileprivate static func decodeInline(_ decoder: PostboxDecoder) -> PeerMuteState {
switch decoder.decodeInt32ForKey("m.v", orElse: 0) { switch decoder.decodeInt32ForKey("m.v", orElse: 0) {
case 0: case 0:
@ -104,15 +134,15 @@ public enum PeerMuteState: Equatable {
} }
} }
private enum PeerMessageSoundValue: Int32 { private enum PeerMessageSoundValue: Int32, Codable {
case none case none = 0
case bundledModern case bundledModern = 1
case bundledClassic case bundledClassic = 2
case `default` case `default` = 3
case cloud case cloud = 4
} }
public enum PeerMessageSound: Equatable { public enum PeerMessageSound: Equatable, Codable {
public enum Id: Hashable { public enum Id: Hashable {
case none case none
case `default` case `default`
@ -142,6 +172,30 @@ public enum PeerMessageSound: Equatable {
} }
} }
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
switch try container.decode(Int32.self, forKey: "s.v") {
case PeerMessageSoundValue.none.rawValue:
self = .none
case PeerMessageSoundValue.bundledModern.rawValue:
self = .cloud(fileId: getCloudSoundOrDefault(id: (try? container.decode(Int32.self, forKey: "s.i")) ?? 0, isModern: true))
case PeerMessageSoundValue.bundledClassic.rawValue:
self = .cloud(fileId: getCloudSoundOrDefault(id: (try? container.decode(Int32.self, forKey: "s.i")) ?? 0, isModern: false))
case PeerMessageSoundValue.default.rawValue:
self = .default
case PeerMessageSoundValue.cloud.rawValue:
do {
self = .cloud(fileId: try container.decode(Int64.self, forKey: "s.cloud.fileId"))
} catch {
self = .default
}
default:
assertionFailure()
self = defaultCloudPeerNotificationSound
}
}
static func decodeInline(_ container: KeyedDecodingContainer<StringCodingKey>) throws -> PeerMessageSound { static func decodeInline(_ container: KeyedDecodingContainer<StringCodingKey>) throws -> PeerMessageSound {
switch try container.decode(Int32.self, forKey: "s.v") { switch try container.decode(Int32.self, forKey: "s.v") {
case PeerMessageSoundValue.none.rawValue: case PeerMessageSoundValue.none.rawValue:
@ -181,6 +235,26 @@ public enum PeerMessageSound: Equatable {
return defaultCloudPeerNotificationSound return defaultCloudPeerNotificationSound
} }
} }
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
switch self {
case .none:
try container.encode(PeerMessageSoundValue.none.rawValue, forKey: "s.v")
case let .bundledModern(id):
try container.encode(PeerMessageSoundValue.bundledModern.rawValue, forKey: "s.v")
try container.encode(id, forKey: "s.i")
case let .bundledClassic(id):
try container.encode(PeerMessageSoundValue.bundledClassic.rawValue, forKey: "s.v")
try container.encode(id, forKey: "s.i")
case let .cloud(fileId):
try container.encode(PeerMessageSoundValue.cloud.rawValue, forKey: "s.v")
try container.encode(fileId, forKey: "s.cloud.fileId")
case .default:
try container.encode(PeerMessageSoundValue.default.rawValue, forKey: "s.v")
}
}
func encodeInline(_ container: inout KeyedEncodingContainer<StringCodingKey>) throws { func encodeInline(_ container: inout KeyedEncodingContainer<StringCodingKey>) throws {
switch self { switch self {
@ -254,11 +328,40 @@ public enum PeerMessageSound: Equatable {
} }
} }
public enum PeerNotificationDisplayPreviews { public enum PeerNotificationDisplayPreviews: Equatable, Codable {
case `default` case `default`
case show case show
case hide case hide
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
switch try container.decode(Int32.self, forKey: "p.v") {
case 0:
self = .default
case 1:
self = .show
case 2:
self = .hide
default:
assertionFailure()
self = .default
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
switch self {
case .default:
try container.encode(0 as Int32, forKey: "p.v")
case .show:
try container.encode(1 as Int32, forKey: "p.v")
case .hide:
try container.encode(2 as Int32, forKey: "p.v")
}
}
static func decodeInline(_ decoder: PostboxDecoder) -> PeerNotificationDisplayPreviews { static func decodeInline(_ decoder: PostboxDecoder) -> PeerNotificationDisplayPreviews {
switch decoder.decodeInt32ForKey("p.v", orElse: 0) { switch decoder.decodeInt32ForKey("p.v", orElse: 0) {
case 0: case 0:
@ -285,7 +388,7 @@ public enum PeerNotificationDisplayPreviews {
} }
} }
public final class TelegramPeerNotificationSettings: PeerNotificationSettings, Equatable { public final class TelegramPeerNotificationSettings: PeerNotificationSettings, Codable, Equatable {
public let muteState: PeerMuteState public let muteState: PeerMuteState
public let messageSound: PeerMessageSound public let messageSound: PeerMessageSound
public let displayPreviews: PeerNotificationDisplayPreviews public let displayPreviews: PeerNotificationDisplayPreviews
@ -325,6 +428,22 @@ public final class TelegramPeerNotificationSettings: PeerNotificationSettings, E
self.displayPreviews = PeerNotificationDisplayPreviews.decodeInline(decoder) self.displayPreviews = PeerNotificationDisplayPreviews.decodeInline(decoder)
} }
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.muteState = try container.decode(PeerMuteState.self, forKey: "muteState")
self.messageSound = try container.decode(PeerMessageSound.self, forKey: "messageSound")
self.displayPreviews = try container.decode(PeerNotificationDisplayPreviews.self, forKey: "displayPreviews")
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode(self.muteState, forKey: "muteState")
try container.encode(self.messageSound, forKey: "messageSound")
try container.encode(self.displayPreviews, forKey: "displayPreviews")
}
public func encode(_ encoder: PostboxEncoder) { public func encode(_ encoder: PostboxEncoder) {
self.muteState.encodeInline(encoder) self.muteState.encodeInline(encoder)
self.messageSound.encodeInline(encoder) self.messageSound.encodeInline(encoder)

View File

@ -275,23 +275,26 @@ public extension TelegramEngine.EngineData.Item {
public struct ItemKey: Hashable { public struct ItemKey: Hashable {
public var peerId: EnginePeer.Id public var peerId: EnginePeer.Id
public var tag: MessageTags public var tag: MessageTags
public var threadId: Int64?
} }
public typealias Result = Int? public typealias Result = Int?
fileprivate var peerId: EnginePeer.Id fileprivate var peerId: EnginePeer.Id
fileprivate var tag: MessageTags fileprivate var tag: MessageTags
fileprivate var threadId: Int64?
public var mapKey: ItemKey { public var mapKey: ItemKey {
return ItemKey(peerId: self.peerId, tag: self.tag) return ItemKey(peerId: self.peerId, tag: self.tag, threadId: self.threadId)
} }
public init(peerId: EnginePeer.Id, tag: MessageTags) { public init(peerId: EnginePeer.Id, threadId: Int64?, tag: MessageTags) {
self.peerId = peerId self.peerId = peerId
self.threadId = threadId
self.tag = tag self.tag = tag
} }
var key: PostboxViewKey { var key: PostboxViewKey {
return .historyTagSummaryView(tag: tag, peerId: peerId, namespace: Namespaces.Message.Cloud) return .historyTagSummaryView(tag: self.tag, peerId: self.peerId, threadId: self.threadId, namespace: Namespaces.Message.Cloud)
} }
func extract(view: PostboxView) -> Result { func extract(view: PostboxView) -> Result {

View File

@ -258,6 +258,32 @@ public extension TelegramEngine.EngineData.Item {
return EnginePeer.NotificationSettings(notificationSettings) return EnginePeer.NotificationSettings(notificationSettings)
} }
} }
public struct ThreadNotificationSettings: TelegramEngineDataItem, PostboxViewDataItem {
public typealias Result = EnginePeer.NotificationSettings
fileprivate var id: EnginePeer.Id
fileprivate var threadId: Int64
public init(id: EnginePeer.Id, threadId: Int64) {
self.id = id
self.threadId = threadId
}
var key: PostboxViewKey {
return .messageHistoryThreadInfo(peerId: self.id, threadId: self.threadId)
}
func extract(view: PostboxView) -> Result {
guard let view = view as? MessageHistoryThreadInfoView else {
preconditionFailure()
}
guard let data = view.info?.get(MessageHistoryThreadData.self) else {
return EnginePeer.NotificationSettings(TelegramPeerNotificationSettings.defaultSettings)
}
return EnginePeer.NotificationSettings(data.notificationSettings)
}
}
public struct ParticipantCount: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { public struct ParticipantCount: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem {
public typealias Result = Optional<Int> public typealias Result = Optional<Int>

View File

@ -115,14 +115,14 @@ public final class EngineDataOptional<Item: TelegramEngineDataItem>: TelegramEng
} }
func _extract(views: [PostboxViewKey: PostboxView]) -> Any { func _extract(views: [PostboxViewKey: PostboxView]) -> Any {
var result: [Item.Result] = [] var result: Item.Result?
if let item = self.item { if let item = self.item {
let itemResult = (item as! AnyPostboxViewDataItem)._extract(views: views) let itemResult = (item as! AnyPostboxViewDataItem)._extract(views: views)
result.append(itemResult as! Item.Result) result = (itemResult as! Item.Result)
} }
return result return result as Any
} }
} }

View File

@ -133,7 +133,7 @@ func _internal_togglePeerUnreadMarkInteractively(transaction: Transaction, viewT
} }
if !hasUnread && peerId.namespace == Namespaces.Peer.SecretChat { if !hasUnread && peerId.namespace == Namespaces.Peer.SecretChat {
let unseenSummary = transaction.getMessageTagSummary(peerId: peerId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud) let unseenSummary = transaction.getMessageTagSummary(peerId: peerId, threadId: nil, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud)
let actionSummary = transaction.getPendingMessageActionsSummary(peerId: peerId, type: PendingMessageActionType.consumeUnseenPersonalMessage, namespace: Namespaces.Message.Cloud) let actionSummary = transaction.getPendingMessageActionsSummary(peerId: peerId, type: PendingMessageActionType.consumeUnseenPersonalMessage, namespace: Namespaces.Message.Cloud)
if (unseenSummary?.count ?? 0) - (actionSummary ?? 0) > 0 { if (unseenSummary?.count ?? 0) - (actionSummary ?? 0) > 0 {
hasUnread = true hasUnread = true
@ -147,7 +147,7 @@ func _internal_togglePeerUnreadMarkInteractively(transaction: Transaction, viewT
} else { } else {
transaction.applyMarkUnread(peerId: peerId, namespace: principalNamespace, value: false, interactive: true) transaction.applyMarkUnread(peerId: peerId, namespace: principalNamespace, value: false, interactive: true)
} }
viewTracker.updateMarkAllMentionsSeen(peerId: peerId) viewTracker.updateMarkAllMentionsSeen(peerId: peerId, threadId: nil)
} }
} else { } else {
if setToValue == nil || setToValue! { if setToValue == nil || setToValue! {
@ -156,22 +156,22 @@ func _internal_togglePeerUnreadMarkInteractively(transaction: Transaction, viewT
} }
} }
public func clearPeerUnseenPersonalMessagesInteractively(account: Account, peerId: PeerId) -> Signal<Never, NoError> { public func clearPeerUnseenPersonalMessagesInteractively(account: Account, peerId: PeerId, threadId: Int64?) -> Signal<Never, NoError> {
return account.postbox.transaction { transaction -> Void in return account.postbox.transaction { transaction -> Void in
if peerId.namespace == Namespaces.Peer.SecretChat { if peerId.namespace == Namespaces.Peer.SecretChat {
return return
} }
account.viewTracker.updateMarkAllMentionsSeen(peerId: peerId) account.viewTracker.updateMarkAllMentionsSeen(peerId: peerId, threadId: threadId)
} }
|> ignoreValues |> ignoreValues
} }
public func clearPeerUnseenReactionsInteractively(account: Account, peerId: PeerId) -> Signal<Never, NoError> { public func clearPeerUnseenReactionsInteractively(account: Account, peerId: PeerId, threadId: Int64?) -> Signal<Never, NoError> {
return account.postbox.transaction { transaction -> Void in return account.postbox.transaction { transaction -> Void in
if peerId.namespace == Namespaces.Peer.SecretChat { if peerId.namespace == Namespaces.Peer.SecretChat {
return return
} }
account.viewTracker.updateMarkAllReactionsSeen(peerId: peerId) account.viewTracker.updateMarkAllReactionsSeen(peerId: peerId, threadId: threadId)
} }
|> ignoreValues |> ignoreValues
} }

View File

@ -9,14 +9,14 @@ public enum EarliestUnseenPersonalMentionMessageResult: Equatable {
case result(MessageId?) case result(MessageId?)
} }
func _internal_earliestUnseenPersonalMentionMessage(account: Account, peerId: PeerId) -> Signal<EarliestUnseenPersonalMentionMessageResult, NoError> { func _internal_earliestUnseenPersonalMentionMessage(account: Account, peerId: PeerId, threadId: Int64?) -> Signal<EarliestUnseenPersonalMentionMessageResult, NoError> {
return account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: peerId), index: .lowerBound, anchorIndex: .lowerBound, count: 4, fixedCombinedReadStates: nil, tagMask: .unseenPersonalMessage, additionalData: [.peerChatState(peerId)]) return account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: peerId, threadId: threadId), index: .lowerBound, anchorIndex: .lowerBound, count: 4, fixedCombinedReadStates: nil, tagMask: .unseenPersonalMessage, additionalData: [.peerChatState(peerId)])
|> mapToSignal { view -> Signal<EarliestUnseenPersonalMentionMessageResult, NoError> in |> mapToSignal { view -> Signal<EarliestUnseenPersonalMentionMessageResult, NoError> in
if view.0.isLoading { if view.0.isLoading {
return .single(.loading) return .single(.loading)
} }
if case .FillHole = view.1 { if case .FillHole = view.1 {
return _internal_earliestUnseenPersonalMentionMessage(account: account, peerId: peerId) return _internal_earliestUnseenPersonalMentionMessage(account: account, peerId: peerId, threadId: threadId)
} }
if let message = view.0.entries.first?.message { if let message = view.0.entries.first?.message {
if peerId.namespace == Namespaces.Peer.CloudChannel { if peerId.namespace == Namespaces.Peer.CloudChannel {
@ -53,10 +53,10 @@ func _internal_earliestUnseenPersonalMentionMessage(account: Account, peerId: Pe
} else { } else {
return account.postbox.transaction { transaction -> EarliestUnseenPersonalMentionMessageResult in return account.postbox.transaction { transaction -> EarliestUnseenPersonalMentionMessageResult in
if let topId = transaction.getTopPeerMessageId(peerId: peerId, namespace: Namespaces.Message.Cloud) { if let topId = transaction.getTopPeerMessageId(peerId: peerId, namespace: Namespaces.Message.Cloud) {
transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: 0, maxId: topId.id) transaction.replaceMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: 0, maxId: topId.id)
transaction.removeHole(peerId: peerId, namespace: Namespaces.Message.Cloud, space: .tag(.unseenPersonalMessage), range: 1 ... (Int32.max - 1)) transaction.removeHole(peerId: peerId, threadId: threadId, namespace: Namespaces.Message.Cloud, space: .tag(.unseenPersonalMessage), range: 1 ... (Int32.max - 1))
let ids = transaction.getMessageIndicesWithTag(peerId: peerId, namespace: Namespaces.Message.Cloud, tag: .unseenPersonalMessage).map({ $0.id }) let ids = transaction.getMessageIndicesWithTag(peerId: peerId, threadId: threadId, namespace: Namespaces.Message.Cloud, tag: .unseenPersonalMessage).map({ $0.id })
for id in ids { for id in ids {
markUnseenPersonalMessage(transaction: transaction, id: id, addSynchronizeAction: false) markUnseenPersonalMessage(transaction: transaction, id: id, addSynchronizeAction: false)
} }
@ -76,14 +76,14 @@ func _internal_earliestUnseenPersonalMentionMessage(account: Account, peerId: Pe
}) })
} }
func _internal_earliestUnseenPersonalReactionMessage(account: Account, peerId: PeerId) -> Signal<EarliestUnseenPersonalMentionMessageResult, NoError> { func _internal_earliestUnseenPersonalReactionMessage(account: Account, peerId: PeerId, threadId: Int64?) -> Signal<EarliestUnseenPersonalMentionMessageResult, NoError> {
return account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: peerId), index: .lowerBound, anchorIndex: .lowerBound, count: 4, fixedCombinedReadStates: nil, tagMask: .unseenReaction, additionalData: [.peerChatState(peerId)]) return account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: peerId, threadId: threadId), index: .lowerBound, anchorIndex: .lowerBound, count: 4, fixedCombinedReadStates: nil, tagMask: .unseenReaction, additionalData: [.peerChatState(peerId)])
|> mapToSignal { view -> Signal<EarliestUnseenPersonalMentionMessageResult, NoError> in |> mapToSignal { view -> Signal<EarliestUnseenPersonalMentionMessageResult, NoError> in
if view.0.isLoading { if view.0.isLoading {
return .single(.loading) return .single(.loading)
} }
if case .FillHole = view.1 { if case .FillHole = view.1 {
return _internal_earliestUnseenPersonalReactionMessage(account: account, peerId: peerId) return _internal_earliestUnseenPersonalReactionMessage(account: account, peerId: peerId, threadId: threadId)
} }
if let message = view.0.entries.first?.message { if let message = view.0.entries.first?.message {
if peerId.namespace == Namespaces.Peer.CloudChannel { if peerId.namespace == Namespaces.Peer.CloudChannel {
@ -120,10 +120,10 @@ func _internal_earliestUnseenPersonalReactionMessage(account: Account, peerId: P
} else { } else {
return account.postbox.transaction { transaction -> EarliestUnseenPersonalMentionMessageResult in return account.postbox.transaction { transaction -> EarliestUnseenPersonalMentionMessageResult in
if let topId = transaction.getTopPeerMessageId(peerId: peerId, namespace: Namespaces.Message.Cloud) { if let topId = transaction.getTopPeerMessageId(peerId: peerId, namespace: Namespaces.Message.Cloud) {
transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: 0, maxId: topId.id) transaction.replaceMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: 0, maxId: topId.id)
transaction.removeHole(peerId: peerId, namespace: Namespaces.Message.Cloud, space: .tag(.unseenReaction), range: 1 ... (Int32.max - 1)) transaction.removeHole(peerId: peerId, threadId: threadId, namespace: Namespaces.Message.Cloud, space: .tag(.unseenReaction), range: 1 ... (Int32.max - 1))
let ids = transaction.getMessageIndicesWithTag(peerId: peerId, namespace: Namespaces.Message.Cloud, tag: .unseenReaction).map({ $0.id }) let ids = transaction.getMessageIndicesWithTag(peerId: peerId, threadId: threadId, namespace: Namespaces.Message.Cloud, tag: .unseenReaction).map({ $0.id })
for id in ids { for id in ids {
markUnseenReactionMessage(transaction: transaction, id: id, addSynchronizeAction: false) markUnseenReactionMessage(transaction: transaction, id: id, addSynchronizeAction: false)
} }

View File

@ -4,7 +4,7 @@ import SwiftSignalKit
import TelegramApi import TelegramApi
func _internal_topPeerActiveLiveLocationMessages(viewTracker: AccountViewTracker, accountPeerId: PeerId, peerId: PeerId) -> Signal<(Peer?, [Message]), NoError> { func _internal_topPeerActiveLiveLocationMessages(viewTracker: AccountViewTracker, accountPeerId: PeerId, peerId: PeerId) -> Signal<(Peer?, [Message]), NoError> {
return viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: peerId), index: .upperBound, anchorIndex: .upperBound, count: 50, fixedCombinedReadStates: nil, tagMask: .liveLocation, orderStatistics: [], additionalData: [.peer(accountPeerId)]) return viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: peerId, threadId: nil), index: .upperBound, anchorIndex: .upperBound, count: 50, fixedCombinedReadStates: nil, tagMask: .liveLocation, orderStatistics: [], additionalData: [.peer(accountPeerId)])
|> map { (view, _, _) -> (Peer?, [Message]) in |> map { (view, _, _) -> (Peer?, [Message]) in
var accountPeer: Peer? var accountPeer: Peer?
for entry in view.additionalData { for entry in view.additionalData {

View File

@ -325,10 +325,6 @@ private class ReplyThreadHistoryContextImpl {
return return
} }
guard let _ = self.stateValue else {
return
}
let fromIdExclusive: Int32? let fromIdExclusive: Int32?
let toIndex = messageIndex let toIndex = messageIndex
if let maxReadIncomingMessageId = self.maxReadIncomingMessageIdValue { if let maxReadIncomingMessageId = self.maxReadIncomingMessageIdValue {
@ -345,6 +341,7 @@ private class ReplyThreadHistoryContextImpl {
if messageIndex.id.id >= data.maxIncomingReadId { if messageIndex.id.id >= data.maxIncomingReadId {
if let count = transaction.getThreadMessageCount(peerId: messageId.peerId, threadId: Int64(messageId.id), namespace: messageId.namespace, fromIdExclusive: data.maxIncomingReadId, toIndex: messageIndex) { if let count = transaction.getThreadMessageCount(peerId: messageId.peerId, threadId: Int64(messageId.id), namespace: messageId.namespace, fromIdExclusive: data.maxIncomingReadId, toIndex: messageIndex) {
data.incomingUnreadCount = max(0, data.incomingUnreadCount - Int32(count)) data.incomingUnreadCount = max(0, data.incomingUnreadCount - Int32(count))
data.maxIncomingReadId = messageIndex.id.id
} }
data.maxKnownMessageId = max(data.maxKnownMessageId, messageIndex.id.id) data.maxKnownMessageId = max(data.maxKnownMessageId, messageIndex.id.id)
@ -937,7 +934,7 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa
return account.postbox.transaction { transaction -> Signal<ChatReplyThreadMessage, FetchChannelReplyThreadMessageError> in return account.postbox.transaction { transaction -> Signal<ChatReplyThreadMessage, FetchChannelReplyThreadMessageError> in
if let initialFilledHoles = initialFilledHoles { if let initialFilledHoles = initialFilledHoles {
for range in initialFilledHoles.strictRemovedIndices.rangeView { for range in initialFilledHoles.strictRemovedIndices.rangeView {
transaction.removeThreadIndexHole(peerId: discussionMessage.messageId.peerId, threadId: makeMessageThreadId(discussionMessage.messageId), namespace: Namespaces.Message.Cloud, space: .everywhere, range: Int32(range.lowerBound) ... Int32(range.upperBound)) transaction.removeHole(peerId: discussionMessage.messageId.peerId, threadId: makeMessageThreadId(discussionMessage.messageId), namespace: Namespaces.Message.Cloud, space: .everywhere, range: Int32(range.lowerBound) ... Int32(range.upperBound))
} }
} }

View File

@ -189,12 +189,7 @@ public final class SparseMessageList {
let count: Int let count: Int
count = 200 count = 200
let location: ChatLocationInput let location: ChatLocationInput = .peer(peerId: self.peerId, threadId: self.threadId)
if let threadId = self.threadId {
location = .thread(peerId: self.peerId, threadId: threadId, data: .single(MessageHistoryViewExternalInput(content: .thread(peerId: self.peerId, id: threadId, holes: [:]), maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil)))
} else {
location = .peer(peerId: self.peerId)
}
self.topItemsDisposable.set((self.account.postbox.aroundMessageHistoryViewForLocation(location, anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: count, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tagMask: self.messageTag, appendMessagesFromTheSameGroup: false, namespaces: .not(Set(Namespaces.Message.allScheduled)), orderStatistics: []) self.topItemsDisposable.set((self.account.postbox.aroundMessageHistoryViewForLocation(location, anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: count, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tagMask: self.messageTag, appendMessagesFromTheSameGroup: false, namespaces: .not(Set(Namespaces.Message.allScheduled)), orderStatistics: [])
|> deliverOn(self.queue)).start(next: { [weak self] view, updateType, _ in |> deliverOn(self.queue)).start(next: { [weak self] view, updateType, _ in

View File

@ -154,32 +154,32 @@ public extension TelegramEngine {
return PollResultsContext(account: self.account, messageId: messageId, poll: poll) return PollResultsContext(account: self.account, messageId: messageId, poll: poll)
} }
public func earliestUnseenPersonalMentionMessage(peerId: PeerId) -> Signal<EarliestUnseenPersonalMentionMessageResult, NoError> { public func earliestUnseenPersonalMentionMessage(peerId: PeerId, threadId: Int64?) -> Signal<EarliestUnseenPersonalMentionMessageResult, NoError> {
let account = self.account let account = self.account
return _internal_earliestUnseenPersonalMentionMessage(account: self.account, peerId: peerId) return _internal_earliestUnseenPersonalMentionMessage(account: self.account, peerId: peerId, threadId: threadId)
|> mapToSignal { result -> Signal<EarliestUnseenPersonalMentionMessageResult, NoError> in |> mapToSignal { result -> Signal<EarliestUnseenPersonalMentionMessageResult, NoError> in
switch result { switch result {
case .loading: case .loading:
return .single(result) return .single(result)
case let .result(messageId): case let .result(messageId):
if messageId == nil { if messageId == nil {
let _ = clearPeerUnseenPersonalMessagesInteractively(account: account, peerId: peerId).start() let _ = clearPeerUnseenPersonalMessagesInteractively(account: account, peerId: peerId, threadId: threadId).start()
} }
return .single(result) return .single(result)
} }
} }
} }
public func earliestUnseenPersonalReactionMessage(peerId: PeerId) -> Signal<EarliestUnseenPersonalMentionMessageResult, NoError> { public func earliestUnseenPersonalReactionMessage(peerId: PeerId, threadId: Int64?) -> Signal<EarliestUnseenPersonalMentionMessageResult, NoError> {
let account = self.account let account = self.account
return _internal_earliestUnseenPersonalReactionMessage(account: self.account, peerId: peerId) return _internal_earliestUnseenPersonalReactionMessage(account: self.account, peerId: peerId, threadId: threadId)
|> mapToSignal { result -> Signal<EarliestUnseenPersonalMentionMessageResult, NoError> in |> mapToSignal { result -> Signal<EarliestUnseenPersonalMentionMessageResult, NoError> in
switch result { switch result {
case .loading: case .loading:
return .single(result) return .single(result)
case let .result(messageId): case let .result(messageId):
if messageId == nil { if messageId == nil {
let _ = clearPeerUnseenReactionsInteractively(account: account, peerId: peerId).start() let _ = clearPeerUnseenReactionsInteractively(account: account, peerId: peerId, threadId: threadId).start()
} }
return .single(result) return .single(result)
} }
@ -297,7 +297,7 @@ public extension TelegramEngine {
return SparseMessageScrollingContext(account: self.account, peerId: peerId) return SparseMessageScrollingContext(account: self.account, peerId: peerId)
} }
public func refreshMessageTagStats(peerId: EnginePeer.Id, tags: [EngineMessage.Tags]) -> Signal<Never, NoError> { public func refreshMessageTagStats(peerId: EnginePeer.Id, threadId: Int64?, tags: [EngineMessage.Tags]) -> Signal<Never, NoError> {
let account = self.account let account = self.account
return self.account.postbox.transaction { transaction -> Api.InputPeer? in return self.account.postbox.transaction { transaction -> Api.InputPeer? in
return transaction.getPeer(peerId).flatMap(apiInputPeer) return transaction.getPeer(peerId).flatMap(apiInputPeer)
@ -312,7 +312,15 @@ public extension TelegramEngine {
signals.append(.single((nil, nil))) signals.append(.single((nil, nil)))
continue continue
} }
signals.append(self.account.network.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, topMsgId: nil, filter: filter, minDate: 0, maxDate: 0, offsetId: 0, addOffset: 0, limit: 1, maxId: 0, minId: 0, hash: 0))
var flags: Int32 = 0
var topMsgId: Int32?
if let threadId = threadId {
flags |= (1 << 1)
topMsgId = Int32(clamping: threadId)
}
signals.append(self.account.network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: "", fromId: nil, topMsgId: topMsgId, filter: filter, minDate: 0, maxDate: 0, offsetId: 0, addOffset: 0, limit: 1, maxId: 0, minId: 0, hash: 0))
|> map { result -> (count: Int32?, topId: Int32?) in |> map { result -> (count: Int32?, topId: Int32?) in
switch result { switch result {
case let .messagesSlice(_, count, _, _, messages, _, _): case let .messagesSlice(_, count, _, _, messages, _, _):
@ -335,7 +343,7 @@ public extension TelegramEngine {
for i in 0 ..< tags.count { for i in 0 ..< tags.count {
let (count, maxId) = counts[i] let (count, maxId) = counts[i]
if let count = count { if let count = count {
transaction.replaceMessageTagSummary(peerId: peerId, tagMask: tags[i], namespace: Namespaces.Message.Cloud, count: count, maxId: maxId ?? 1) transaction.replaceMessageTagSummary(peerId: peerId, threadId: threadId, tagMask: tags[i], namespace: Namespaces.Message.Cloud, count: count, maxId: maxId ?? 1)
} }
} }
} }

View File

@ -170,7 +170,7 @@ func _internal_requestUnpinAllMessages(account: Account, peerId: PeerId) -> Sign
}, delayIncrement: 0.0, maxDelay: 0.0, maxRetries: 100, onQueue: .concurrentDefaultQueue()) }, delayIncrement: 0.0, maxDelay: 0.0, maxRetries: 100, onQueue: .concurrentDefaultQueue())
|> mapToSignal { _ -> Signal<Never, InternalError> in |> mapToSignal { _ -> Signal<Never, InternalError> in
let signal: Signal<Never, InternalError> = account.postbox.transaction { transaction -> Void in let signal: Signal<Never, InternalError> = account.postbox.transaction { transaction -> Void in
for index in transaction.getMessageIndicesWithTag(peerId: peerId, namespace: Namespaces.Message.Cloud, tag: .pinned) { for index in transaction.getMessageIndicesWithTag(peerId: peerId, threadId: nil, namespace: Namespaces.Message.Cloud, tag: .pinned) {
transaction.updateMessage(index.id, update: { currentMessage in transaction.updateMessage(index.id, update: { currentMessage in
var storeForwardInfo: StoreMessageForwardInfo? var storeForwardInfo: StoreMessageForwardInfo?
if let forwardInfo = currentMessage.forwardInfo { if let forwardInfo = currentMessage.forwardInfo {

View File

@ -1,17 +1,41 @@
import Foundation import Foundation
import Postbox import Postbox
import SwiftSignalKit import SwiftSignalKit
import TelegramApi
func _internal_togglePeerMuted(account: Account, peerId: PeerId, threadId: Int64?) -> Signal<Void, NoError> {
func _internal_togglePeerMuted(account: Account, peerId: PeerId) -> Signal<Void, NoError> {
return account.postbox.transaction { transaction -> Void in return account.postbox.transaction { transaction -> Void in
if let peer = transaction.getPeer(peerId) { guard let peer = transaction.getPeer(peerId) else {
var notificationPeerId = peerId return
if let associatedPeerId = peer.associatedPeerId { }
notificationPeerId = associatedPeerId
var notificationPeerId = peerId
if let associatedPeerId = peer.associatedPeerId {
notificationPeerId = associatedPeerId
}
if let threadId = threadId {
if var data = transaction.getMessageHistoryThreadInfo(peerId: peerId, threadId: threadId)?.get(MessageHistoryThreadData.self) {
var updatedSettings: TelegramPeerNotificationSettings
switch data.notificationSettings.muteState {
case .default:
updatedSettings = data.notificationSettings.withUpdatedMuteState(.muted(until: Int32.max))
case .unmuted:
updatedSettings = data.notificationSettings.withUpdatedMuteState(.muted(until: Int32.max))
case .muted:
updatedSettings = data.notificationSettings.withUpdatedMuteState(.unmuted)
}
data.notificationSettings = updatedSettings
if let entry = CodableEntry(data) {
transaction.setMessageHistoryThreadInfo(peerId: peerId, threadId: threadId, info: entry)
//TODO:loc
let _ = pushPeerNotificationSettings(postbox: account.postbox, network: account.network, peerId: peerId, threadId: threadId, settings: TelegramPeerNotificationSettings.defaultSettings).start()
}
} }
} else {
let currentSettings = transaction.getPeerNotificationSettings(notificationPeerId) as? TelegramPeerNotificationSettings let currentSettings = transaction.getPeerNotificationSettings(id: notificationPeerId) as? TelegramPeerNotificationSettings
let previousSettings: TelegramPeerNotificationSettings let previousSettings: TelegramPeerNotificationSettings
if let currentSettings = currentSettings { if let currentSettings = currentSettings {
previousSettings = currentSettings previousSettings = currentSettings
@ -38,97 +62,159 @@ func _internal_togglePeerMuted(account: Account, peerId: PeerId) -> Signal<Void,
} }
} }
func _internal_updatePeerMuteSetting(account: Account, peerId: PeerId, muteInterval: Int32?) -> Signal<Void, NoError> { func _internal_updatePeerMuteSetting(account: Account, peerId: PeerId, threadId: Int64?, muteInterval: Int32?) -> Signal<Void, NoError> {
return account.postbox.transaction { transaction -> Void in return account.postbox.transaction { transaction -> Void in
updatePeerMuteSetting(transaction: transaction, peerId: peerId, muteInterval: muteInterval) _internal_updatePeerMuteSetting(account: account, transaction: transaction, peerId: peerId, threadId: threadId, muteInterval: muteInterval)
} }
} }
func updatePeerMuteSetting(transaction: Transaction, peerId: PeerId, muteInterval: Int32?) { func _internal_updatePeerMuteSetting(account: Account, transaction: Transaction, peerId: PeerId, threadId: Int64?, muteInterval: Int32?) {
if let peer = transaction.getPeer(peerId) { if let peer = transaction.getPeer(peerId) {
var notificationPeerId = peerId if let threadId = threadId {
if let associatedPeerId = peer.associatedPeerId { if var data = transaction.getMessageHistoryThreadInfo(peerId: peerId, threadId: threadId)?.get(MessageHistoryThreadData.self) {
notificationPeerId = associatedPeerId let previousSettings: TelegramPeerNotificationSettings = data.notificationSettings
}
let muteState: PeerMuteState
let currentSettings = transaction.getPeerNotificationSettings(notificationPeerId) as? TelegramPeerNotificationSettings if let muteInterval = muteInterval {
let previousSettings: TelegramPeerNotificationSettings if muteInterval == 0 {
if let currentSettings = currentSettings { muteState = .unmuted
previousSettings = currentSettings } else {
} else { let absoluteUntil: Int32
previousSettings = TelegramPeerNotificationSettings.defaultSettings if muteInterval == Int32.max {
} absoluteUntil = Int32.max
} else {
let muteState: PeerMuteState absoluteUntil = Int32(Date().timeIntervalSince1970) + muteInterval
if let muteInterval = muteInterval { }
if muteInterval == 0 { muteState = .muted(until: absoluteUntil)
muteState = .unmuted }
} else {
let absoluteUntil: Int32
if muteInterval == Int32.max {
absoluteUntil = Int32.max
} else { } else {
absoluteUntil = Int32(Date().timeIntervalSince1970) + muteInterval muteState = .default
} }
muteState = .muted(until: absoluteUntil)
data.notificationSettings = previousSettings.withUpdatedMuteState(muteState)
if let entry = CodableEntry(data) {
transaction.setMessageHistoryThreadInfo(peerId: peerId, threadId: threadId, info: entry)
}
//TODO:loc
let _ = pushPeerNotificationSettings(postbox: account.postbox, network: account.network, peerId: peerId, threadId: threadId, settings: TelegramPeerNotificationSettings.defaultSettings).start()
} }
} else { } else {
muteState = .default var notificationPeerId = peerId
if let associatedPeerId = peer.associatedPeerId {
notificationPeerId = associatedPeerId
}
let currentSettings = transaction.getPeerNotificationSettings(id: notificationPeerId) as? TelegramPeerNotificationSettings
let previousSettings: TelegramPeerNotificationSettings
if let currentSettings = currentSettings {
previousSettings = currentSettings
} else {
previousSettings = TelegramPeerNotificationSettings.defaultSettings
}
let muteState: PeerMuteState
if let muteInterval = muteInterval {
if muteInterval == 0 {
muteState = .unmuted
} else {
let absoluteUntil: Int32
if muteInterval == Int32.max {
absoluteUntil = Int32.max
} else {
absoluteUntil = Int32(Date().timeIntervalSince1970) + muteInterval
}
muteState = .muted(until: absoluteUntil)
}
} else {
muteState = .default
}
let updatedSettings = previousSettings.withUpdatedMuteState(muteState)
transaction.updatePendingPeerNotificationSettings(peerId: peerId, settings: updatedSettings)
} }
let updatedSettings = previousSettings.withUpdatedMuteState(muteState)
transaction.updatePendingPeerNotificationSettings(peerId: peerId, settings: updatedSettings)
} }
} }
func _internal_updatePeerDisplayPreviewsSetting(account: Account, peerId: PeerId, displayPreviews: PeerNotificationDisplayPreviews) -> Signal<Void, NoError> { func _internal_updatePeerDisplayPreviewsSetting(account: Account, peerId: PeerId, threadId: Int64?, displayPreviews: PeerNotificationDisplayPreviews) -> Signal<Void, NoError> {
return account.postbox.transaction { transaction -> Void in return account.postbox.transaction { transaction -> Void in
updatePeerDisplayPreviewsSetting(transaction: transaction, peerId: peerId, displayPreviews: displayPreviews) _internal_updatePeerDisplayPreviewsSetting(account: account, transaction: transaction, peerId: peerId, threadId: threadId, displayPreviews: displayPreviews)
} }
} }
func updatePeerDisplayPreviewsSetting(transaction: Transaction, peerId: PeerId, displayPreviews: PeerNotificationDisplayPreviews) { func _internal_updatePeerDisplayPreviewsSetting(account: Account, transaction: Transaction, peerId: PeerId, threadId: Int64?, displayPreviews: PeerNotificationDisplayPreviews) {
if let peer = transaction.getPeer(peerId) { if let peer = transaction.getPeer(peerId) {
var notificationPeerId = peerId if let threadId = threadId {
if let associatedPeerId = peer.associatedPeerId { if var data = transaction.getMessageHistoryThreadInfo(peerId: peerId, threadId: threadId)?.get(MessageHistoryThreadData.self) {
notificationPeerId = associatedPeerId let previousSettings: TelegramPeerNotificationSettings = data.notificationSettings
}
data.notificationSettings = previousSettings.withUpdatedDisplayPreviews(displayPreviews)
let currentSettings = transaction.getPeerNotificationSettings(notificationPeerId) as? TelegramPeerNotificationSettings
let previousSettings: TelegramPeerNotificationSettings if let entry = CodableEntry(data) {
if let currentSettings = currentSettings { transaction.setMessageHistoryThreadInfo(peerId: peerId, threadId: threadId, info: entry)
previousSettings = currentSettings }
//TODO:loc
let _ = pushPeerNotificationSettings(postbox: account.postbox, network: account.network, peerId: peerId, threadId: threadId, settings: TelegramPeerNotificationSettings.defaultSettings).start()
}
} else { } else {
previousSettings = TelegramPeerNotificationSettings.defaultSettings var notificationPeerId = peerId
if let associatedPeerId = peer.associatedPeerId {
notificationPeerId = associatedPeerId
}
let currentSettings = transaction.getPeerNotificationSettings(id: notificationPeerId) as? TelegramPeerNotificationSettings
let previousSettings: TelegramPeerNotificationSettings
if let currentSettings = currentSettings {
previousSettings = currentSettings
} else {
previousSettings = TelegramPeerNotificationSettings.defaultSettings
}
let updatedSettings = previousSettings.withUpdatedDisplayPreviews(displayPreviews)
transaction.updatePendingPeerNotificationSettings(peerId: peerId, settings: updatedSettings)
} }
let updatedSettings = previousSettings.withUpdatedDisplayPreviews(displayPreviews)
transaction.updatePendingPeerNotificationSettings(peerId: peerId, settings: updatedSettings)
} }
} }
func _internal_updatePeerNotificationSoundInteractive(account: Account, peerId: PeerId, sound: PeerMessageSound) -> Signal<Void, NoError> { func _internal_updatePeerNotificationSoundInteractive(account: Account, peerId: PeerId, threadId: Int64?, sound: PeerMessageSound) -> Signal<Void, NoError> {
return account.postbox.transaction { transaction -> Void in return account.postbox.transaction { transaction -> Void in
updatePeerNotificationSoundInteractive(transaction: transaction, peerId: peerId, sound: sound) _internal_updatePeerNotificationSoundInteractive(account: account, transaction: transaction, peerId: peerId, threadId: threadId, sound: sound)
} }
} }
func updatePeerNotificationSoundInteractive(transaction: Transaction, peerId: PeerId, sound: PeerMessageSound) { func _internal_updatePeerNotificationSoundInteractive(account: Account, transaction: Transaction, peerId: PeerId, threadId: Int64?, sound: PeerMessageSound) {
if let peer = transaction.getPeer(peerId) { if let peer = transaction.getPeer(peerId) {
var notificationPeerId = peerId if let threadId = threadId {
if let associatedPeerId = peer.associatedPeerId { if var data = transaction.getMessageHistoryThreadInfo(peerId: peerId, threadId: threadId)?.get(MessageHistoryThreadData.self) {
notificationPeerId = associatedPeerId let previousSettings: TelegramPeerNotificationSettings = data.notificationSettings
}
data.notificationSettings = previousSettings.withUpdatedMessageSound(sound)
let currentSettings = transaction.getPeerNotificationSettings(notificationPeerId) as? TelegramPeerNotificationSettings
let previousSettings: TelegramPeerNotificationSettings if let entry = CodableEntry(data) {
if let currentSettings = currentSettings { transaction.setMessageHistoryThreadInfo(peerId: peerId, threadId: threadId, info: entry)
previousSettings = currentSettings }
//TODO:loc
let _ = pushPeerNotificationSettings(postbox: account.postbox, network: account.network, peerId: peerId, threadId: threadId, settings: TelegramPeerNotificationSettings.defaultSettings).start()
}
} else { } else {
previousSettings = TelegramPeerNotificationSettings.defaultSettings var notificationPeerId = peerId
if let associatedPeerId = peer.associatedPeerId {
notificationPeerId = associatedPeerId
}
let currentSettings = transaction.getPeerNotificationSettings(id: notificationPeerId) as? TelegramPeerNotificationSettings
let previousSettings: TelegramPeerNotificationSettings
if let currentSettings = currentSettings {
previousSettings = currentSettings
} else {
previousSettings = TelegramPeerNotificationSettings.defaultSettings
}
let updatedSettings = previousSettings.withUpdatedMessageSound(sound)
transaction.updatePendingPeerNotificationSettings(peerId: peerId, settings: updatedSettings)
} }
let updatedSettings = previousSettings.withUpdatedMessageSound(sound)
transaction.updatePendingPeerNotificationSettings(peerId: peerId, settings: updatedSettings)
} }
} }

View File

@ -710,8 +710,8 @@ private func loadAndStorePeerChatInfos(accountPeerId: PeerId, postbox: Postbox,
transaction.resetIncomingReadStates([peerId: [Namespaces.Message.Cloud: .idBased(maxIncomingReadId: readInboxMaxId, maxOutgoingReadId: readOutboxMaxId, maxKnownId: topMessage, count: unreadCount, markedUnread: false)]]) transaction.resetIncomingReadStates([peerId: [Namespaces.Message.Cloud: .idBased(maxIncomingReadId: readInboxMaxId, maxOutgoingReadId: readOutboxMaxId, maxKnownId: topMessage, count: unreadCount, markedUnread: false)]])
transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: unreadMentionsCount, maxId: topMessage) transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: unreadMentionsCount, maxId: topMessage)
transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: unreadReactionsCount, maxId: topMessage) transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, count: unreadReactionsCount, maxId: topMessage)
if let pts = pts { if let pts = pts {
if transaction.getPeerChatState(peerId) == nil { if transaction.getPeerChatState(peerId) == nil {

View File

@ -73,7 +73,7 @@ func _internal_joinChannel(account: Account, peerId: PeerId, hash: String?) -> S
} }
if let channel = transaction.getPeer(peerId) as? TelegramChannel, case .broadcast = channel.info { if let channel = transaction.getPeer(peerId) as? TelegramChannel, case .broadcast = channel.info {
let notificationSettings = transaction.getPeerNotificationSettings(peerId) as? TelegramPeerNotificationSettings ?? TelegramPeerNotificationSettings.defaultSettings let notificationSettings = transaction.getPeerNotificationSettings(id: peerId) as? TelegramPeerNotificationSettings ?? TelegramPeerNotificationSettings.defaultSettings
transaction.updateCurrentPeerNotificationSettings([peerId: notificationSettings.withUpdatedMuteState(.muted(until: Int32.max))]) transaction.updateCurrentPeerNotificationSettings([peerId: notificationSettings.withUpdatedMuteState(.muted(until: Int32.max))])
} }

View File

@ -183,28 +183,28 @@ public extension TelegramEngine {
return _internal_reportRepliesMessage(account: self.account, messageId: messageId, deleteMessage: deleteMessage, deleteHistory: deleteHistory, reportSpam: reportSpam) return _internal_reportRepliesMessage(account: self.account, messageId: messageId, deleteMessage: deleteMessage, deleteHistory: deleteHistory, reportSpam: reportSpam)
} }
public func togglePeerMuted(peerId: PeerId) -> Signal<Void, NoError> { public func togglePeerMuted(peerId: PeerId, threadId: Int64?) -> Signal<Void, NoError> {
return _internal_togglePeerMuted(account: self.account, peerId: peerId) return _internal_togglePeerMuted(account: self.account, peerId: peerId, threadId: threadId)
} }
public func updatePeerMuteSetting(peerId: PeerId, muteInterval: Int32?) -> Signal<Void, NoError> { public func updatePeerMuteSetting(peerId: PeerId, threadId: Int64?, muteInterval: Int32?) -> Signal<Void, NoError> {
return _internal_updatePeerMuteSetting(account: self.account, peerId: peerId, muteInterval: muteInterval) return _internal_updatePeerMuteSetting(account: self.account, peerId: peerId, threadId: threadId, muteInterval: muteInterval)
} }
public func updatePeerDisplayPreviewsSetting(peerId: PeerId, displayPreviews: PeerNotificationDisplayPreviews) -> Signal<Void, NoError> { public func updatePeerDisplayPreviewsSetting(peerId: PeerId, threadId: Int64?, displayPreviews: PeerNotificationDisplayPreviews) -> Signal<Void, NoError> {
return _internal_updatePeerDisplayPreviewsSetting(account: self.account, peerId: peerId, displayPreviews: displayPreviews) return _internal_updatePeerDisplayPreviewsSetting(account: self.account, peerId: peerId, threadId: threadId, displayPreviews: displayPreviews)
} }
public func updatePeerNotificationSoundInteractive(peerId: PeerId, sound: PeerMessageSound) -> Signal<Void, NoError> { public func updatePeerNotificationSoundInteractive(peerId: PeerId, threadId: Int64?, sound: PeerMessageSound) -> Signal<Void, NoError> {
return _internal_updatePeerNotificationSoundInteractive(account: self.account, peerId: peerId, sound: sound) return _internal_updatePeerNotificationSoundInteractive(account: self.account, peerId: peerId, threadId: threadId, sound: sound)
} }
public func removeCustomNotificationSettings(peerIds: [PeerId]) -> Signal<Never, NoError> { public func removeCustomNotificationSettings(peerIds: [PeerId]) -> Signal<Never, NoError> {
return self.account.postbox.transaction { transaction -> Void in return self.account.postbox.transaction { transaction -> Void in
for peerId in peerIds { for peerId in peerIds {
TelegramCore.updatePeerNotificationSoundInteractive(transaction: transaction, peerId: peerId, sound: .default) _internal_updatePeerNotificationSoundInteractive(account: self.account, transaction: transaction, peerId: peerId, threadId: nil, sound: .default)
TelegramCore.updatePeerMuteSetting(transaction: transaction, peerId: peerId, muteInterval: nil) _internal_updatePeerMuteSetting(account: self.account, transaction: transaction, peerId: peerId, threadId: nil, muteInterval: nil)
TelegramCore.updatePeerDisplayPreviewsSetting(transaction: transaction, peerId: peerId, displayPreviews: .default) _internal_updatePeerDisplayPreviewsSetting(account: self.account, transaction: transaction, peerId: peerId, threadId: nil, displayPreviews: .default)
} }
} }
|> ignoreValues |> ignoreValues

View File

@ -71,7 +71,8 @@ public func updatePeers(transaction: Transaction, peers: [Peer], update: (Peer?,
} }
case Namespaces.Peer.CloudChannel: case Namespaces.Peer.CloudChannel:
if let channel = updated as? TelegramChannel { if let channel = updated as? TelegramChannel {
switch channel.participationStatus { if case .personal = channel.accessHash {
switch channel.participationStatus {
case .member: case .member:
updatePeerChatInclusionWithMinTimestamp(transaction: transaction, id: peerId, minTimestamp: channel.creationDate, forceRootGroupIfNotExists: true) updatePeerChatInclusionWithMinTimestamp(transaction: transaction, id: peerId, minTimestamp: channel.creationDate, forceRootGroupIfNotExists: true)
case .left: case .left:
@ -80,6 +81,7 @@ public func updatePeers(transaction: Transaction, peers: [Peer], update: (Peer?,
transaction.updatePeerChatListInclusion(peerId, inclusion: .notIncluded) transaction.updatePeerChatListInclusion(peerId, inclusion: .notIncluded)
default: default:
transaction.updatePeerChatListInclusion(peerId, inclusion: .notIncluded) transaction.updatePeerChatListInclusion(peerId, inclusion: .notIncluded)
}
} }
} else { } else {
assertionFailure() assertionFailure()

View File

@ -31,7 +31,7 @@ public enum ChatTitleContent {
case replies case replies
} }
case peer(peerView: PeerView, customTitle: String?, onlineMemberCount: Int32?, isScheduledMessages: Bool) case peer(peerView: PeerView, customTitle: String?, onlineMemberCount: Int32?, isScheduledMessages: Bool, isMuted: Bool?)
case replyThread(type: ReplyThreadType, count: Int) case replyThread(type: ReplyThreadType, count: Int)
case custom(String, String?, Bool) case custom(String, String?, Bool)
} }
@ -123,7 +123,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
var titleCredibilityIcon: ChatTitleCredibilityIcon = .none var titleCredibilityIcon: ChatTitleCredibilityIcon = .none
var isEnabled = true var isEnabled = true
switch titleContent { switch titleContent {
case let .peer(peerView, customTitle, _, isScheduledMessages): case let .peer(peerView, customTitle, _, isScheduledMessages, isMuted):
if peerView.peerId.isReplies { if peerView.peerId.isReplies {
let typeText: String = self.strings.DialogList_Replies let typeText: String = self.strings.DialogList_Replies
segments = [.text(0, NSAttributedString(string: typeText, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))] segments = [.text(0, NSAttributedString(string: typeText, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))]
@ -166,10 +166,16 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
if peerView.peerId.namespace == Namespaces.Peer.SecretChat { if peerView.peerId.namespace == Namespaces.Peer.SecretChat {
titleLeftIcon = .lock titleLeftIcon = .lock
} }
if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings { if let isMuted {
if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) { if isMuted {
if titleCredibilityIcon != .verified { titleRightIcon = .mute
titleRightIcon = .mute }
} else {
if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings {
if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) {
if titleCredibilityIcon != .verified {
titleRightIcon = .mute
}
} }
} }
} }
@ -313,7 +319,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
var inputActivitiesAllowed = true var inputActivitiesAllowed = true
if let titleContent = self.titleContent { if let titleContent = self.titleContent {
switch titleContent { switch titleContent {
case let .peer(peerView, _, _, isScheduledMessages): case let .peer(peerView, _, _, isScheduledMessages, _):
if let peer = peerViewMainPeer(peerView) { if let peer = peerViewMainPeer(peerView) {
if peer.id == self.context.account.peerId || isScheduledMessages || peer.id.isReplies { if peer.id == self.context.account.peerId || isScheduledMessages || peer.id.isReplies {
inputActivitiesAllowed = false inputActivitiesAllowed = false
@ -414,7 +420,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
} else { } else {
if let titleContent = self.titleContent { if let titleContent = self.titleContent {
switch titleContent { switch titleContent {
case let .peer(peerView, _, onlineMemberCount, isScheduledMessages): case let .peer(peerView, _, onlineMemberCount, isScheduledMessages, _):
if let peer = peerViewMainPeer(peerView) { if let peer = peerViewMainPeer(peerView) {
let servicePeer = isServicePeer(peer) let servicePeer = isServicePeer(peer)
if peer.id == self.context.account.peerId || isScheduledMessages || peer.id.isReplies { if peer.id == self.context.account.peerId || isScheduledMessages || peer.id.isReplies {

View File

@ -342,10 +342,14 @@ public final class AccountContextImpl: AccountContext {
public func chatLocationInput(for location: ChatLocation, contextHolder: Atomic<ChatLocationContextHolder?>) -> ChatLocationInput { public func chatLocationInput(for location: ChatLocation, contextHolder: Atomic<ChatLocationContextHolder?>) -> ChatLocationInput {
switch location { switch location {
case let .peer(peerId): case let .peer(peerId):
return .peer(peerId: peerId) return .peer(peerId: peerId, threadId: nil)
case let .replyThread(data): case let .replyThread(data):
let context = chatLocationContext(holder: contextHolder, account: self.account, data: data) if data.isForumPost {
return .thread(peerId: data.messageId.peerId, threadId: makeMessageThreadId(data.messageId), data: context.state) return .peer(peerId: data.messageId.peerId, threadId: Int64(data.messageId.id))
} else {
let context = chatLocationContext(holder: contextHolder, account: self.account, data: data)
return .thread(peerId: data.messageId.peerId, threadId: makeMessageThreadId(data.messageId), data: context.state)
}
case let .feed(id): case let .feed(id):
let context = chatLocationContext(holder: contextHolder, account: self.account, feedId: id) let context = chatLocationContext(holder: contextHolder, account: self.account, feedId: id)
return .feed(id: id, data: context.state) return .feed(id: id, data: context.state)
@ -357,8 +361,20 @@ public final class AccountContextImpl: AccountContext {
case .peer: case .peer:
return .single(nil) return .single(nil)
case let .replyThread(data): case let .replyThread(data):
let context = chatLocationContext(holder: contextHolder, account: self.account, data: data) if data.isForumPost, let peerId = location.peerId {
return context.maxReadOutgoingMessageId let viewKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: data.messageId.peerId, threadId: Int64(data.messageId.id))
return self.account.postbox.combinedView(keys: [viewKey])
|> map { views -> MessageId? in
if let threadInfo = views.views[viewKey] as? MessageHistoryThreadInfoView, let data = threadInfo.info?.get(MessageHistoryThreadData.self) {
return MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: data.maxOutgoingReadId)
} else {
return nil
}
}
} else {
let context = chatLocationContext(holder: contextHolder, account: self.account, data: data)
return context.maxReadOutgoingMessageId
}
case let .feed(id): case let .feed(id):
let context = chatLocationContext(holder: contextHolder, account: self.account, feedId: id) let context = chatLocationContext(holder: contextHolder, account: self.account, feedId: id)
return context.maxReadOutgoingMessageId return context.maxReadOutgoingMessageId
@ -372,18 +388,30 @@ public final class AccountContextImpl: AccountContext {
return self.account.postbox.combinedView(keys: [unreadCountsKey]) return self.account.postbox.combinedView(keys: [unreadCountsKey])
|> map { views in |> map { views in
var unreadCount: Int32 = 0 var unreadCount: Int32 = 0
if let view = views.views[unreadCountsKey] as? UnreadMessageCountsView { if let view = views.views[unreadCountsKey] as? UnreadMessageCountsView {
if let count = view.count(for: .peer(peerId)) { if let count = view.count(for: .peer(peerId)) {
unreadCount = count unreadCount = count
} }
} }
return Int(unreadCount) return Int(unreadCount)
} }
case let .replyThread(data): case let .replyThread(data):
let context = chatLocationContext(holder: contextHolder, account: self.account, data: data) if data.isForumPost {
return context.unreadCount let viewKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: data.messageId.peerId, threadId: Int64(data.messageId.id))
return self.account.postbox.combinedView(keys: [viewKey])
|> map { views -> Int in
if let threadInfo = views.views[viewKey] as? MessageHistoryThreadInfoView, let data = threadInfo.info?.get(MessageHistoryThreadData.self) {
return Int(data.incomingUnreadCount)
} else {
return 0
}
}
} else {
let context = chatLocationContext(holder: contextHolder, account: self.account, data: data)
return context.unreadCount
}
case let .feed(id): case let .feed(id):
let context = chatLocationContext(holder: contextHolder, account: self.account, feedId: id) let context = chatLocationContext(holder: contextHolder, account: self.account, feedId: id)
return context.unreadCount return context.unreadCount

View File

@ -252,7 +252,7 @@ final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
break break
case .muteNotifications, .unmuteNotifications: case .muteNotifications, .unmuteNotifications:
if let context = self.context, let presentationInterfaceState = self.presentationInterfaceState, let peer = presentationInterfaceState.renderedPeer?.peer { if let context = self.context, let presentationInterfaceState = self.presentationInterfaceState, let peer = presentationInterfaceState.renderedPeer?.peer {
self.actionDisposable.set(context.engine.peers.togglePeerMuted(peerId: peer.id).start()) self.actionDisposable.set(context.engine.peers.togglePeerMuted(peerId: peer.id, threadId: nil).start())
} }
case .hidePinnedMessages, .unpinMessages: case .hidePinnedMessages, .unpinMessages:
self.interfaceInteraction?.unpinAllMessages() self.interfaceInteraction?.unpinAllMessages()

View File

@ -4384,7 +4384,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if case .pinnedMessages = presentationInterfaceState.subject { if case .pinnedMessages = presentationInterfaceState.subject {
strongSelf.chatTitleView?.titleContent = .custom(presentationInterfaceState.strings.Chat_TitlePinnedMessages(Int32(displayedCount ?? 1)), nil, false) strongSelf.chatTitleView?.titleContent = .custom(presentationInterfaceState.strings.Chat_TitlePinnedMessages(Int32(displayedCount ?? 1)), nil, false)
} else { } else {
strongSelf.chatTitleView?.titleContent = .peer(peerView: peerView, customTitle: nil, onlineMemberCount: onlineMemberCount, isScheduledMessages: isScheduledMessages) strongSelf.chatTitleView?.titleContent = .peer(peerView: peerView, customTitle: nil, onlineMemberCount: onlineMemberCount, isScheduledMessages: isScheduledMessages, isMuted: nil)
let imageOverride: AvatarNodeImageOverride? let imageOverride: AvatarNodeImageOverride?
if strongSelf.context.account.peerId == peer.id { if strongSelf.context.account.peerId == peer.id {
imageOverride = .savedMessagesIcon imageOverride = .savedMessagesIcon
@ -4741,18 +4741,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let peerView = context.account.viewTracker.peerView(peerId) let peerView = context.account.viewTracker.peerView(peerId)
let messageAndTopic = messagePromise.get() let messageAndTopic = messagePromise.get()
|> mapToSignal { message -> Signal<(message: Message?, threadInfo: EngineMessageHistoryThread.Info?), NoError> in |> mapToSignal { message -> Signal<(message: Message?, threadData: MessageHistoryThreadData?), NoError> in
guard let message = message else { guard let message = message else {
return .single((nil, nil)) return .single((nil, nil))
} }
let viewKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: peerId, threadId: Int64(message.id.id)) let viewKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: peerId, threadId: Int64(message.id.id))
return context.account.postbox.combinedView(keys: [viewKey]) return context.account.postbox.combinedView(keys: [viewKey])
|> map { views -> (message: Message?, threadInfo: EngineMessageHistoryThread.Info?) in |> map { views -> (message: Message?, threadData: MessageHistoryThreadData?) in
guard let view = views.views[viewKey] as? MessageHistoryThreadInfoView else { guard let view = views.views[viewKey] as? MessageHistoryThreadInfoView else {
return (message, nil) return (message, nil)
} }
return (message, view.info?.get(MessageHistoryThreadData.self)?.info) return (message, view.info?.get(MessageHistoryThreadData.self))
} }
} }
@ -4809,8 +4809,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
} }
if let threadInfo = messageAndTopic.threadInfo { var peerIsMuted = false
strongSelf.chatTitleView?.titleContent = .peer(peerView: peerView, customTitle: threadInfo.title, onlineMemberCount: onlineMemberCount, isScheduledMessages: false) if let threadData = messageAndTopic.threadData {
if case let .muted(until) = threadData.notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) {
peerIsMuted = true
}
} else if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings {
if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) {
peerIsMuted = true
}
}
if let threadInfo = messageAndTopic.threadData?.info {
strongSelf.chatTitleView?.titleContent = .peer(peerView: peerView, customTitle: threadInfo.title, onlineMemberCount: onlineMemberCount, isScheduledMessages: false, isMuted: peerIsMuted)
let avatarContent: EmojiStatusComponent.Content let avatarContent: EmojiStatusComponent.Content
if let fileId = threadInfo.icon { if let fileId = threadInfo.icon {
@ -4829,12 +4840,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if strongSelf.isNodeLoaded { if strongSelf.isNodeLoaded {
strongSelf.chatDisplayNode.peerView = peerView strongSelf.chatDisplayNode.peerView = peerView
} }
var peerIsMuted = false
if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings {
if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) {
peerIsMuted = true
}
}
var peerDiscussionId: PeerId? var peerDiscussionId: PeerId?
var peerGeoLocation: PeerGeoLocation? var peerGeoLocation: PeerGeoLocation?
var currentSendAsPeerId: PeerId? var currentSendAsPeerId: PeerId?
@ -6813,8 +6819,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
self.chatDisplayNode.navigateButtons.mentionsPressed = { [weak self] in self.chatDisplayNode.navigateButtons.mentionsPressed = { [weak self] in
if let strongSelf = self, strongSelf.isNodeLoaded, case let .peer(peerId) = strongSelf.chatLocation { if let strongSelf = self, strongSelf.isNodeLoaded, let peerId = strongSelf.chatLocation.peerId {
let signal = strongSelf.context.engine.messages.earliestUnseenPersonalMentionMessage(peerId: peerId) let signal = strongSelf.context.engine.messages.earliestUnseenPersonalMentionMessage(peerId: peerId, threadId: strongSelf.chatLocation.threadId)
strongSelf.navigationActionDisposable.set((signal |> deliverOnMainQueue).start(next: { result in strongSelf.navigationActionDisposable.set((signal |> deliverOnMainQueue).start(next: { result in
if let strongSelf = self { if let strongSelf = self {
switch result { switch result {
@ -6850,10 +6856,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
action: { _, f in action: { _, f in
f(.dismissWithoutContent) f(.dismissWithoutContent)
guard let strongSelf = self, case let .peer(peerId) = strongSelf.chatLocation else { guard let strongSelf = self, let peerId = strongSelf.chatLocation.peerId else {
return return
} }
let _ = clearPeerUnseenPersonalMessagesInteractively(account: strongSelf.context.account, peerId: peerId).start() let _ = clearPeerUnseenPersonalMessagesInteractively(account: strongSelf.context.account, peerId: peerId, threadId: strongSelf.chatLocation.threadId).start()
} }
))) )))
let items = ContextController.Items(content: .list(menuItems)) let items = ContextController.Items(content: .list(menuItems))
@ -6870,8 +6876,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
self.chatDisplayNode.navigateButtons.reactionsPressed = { [weak self] in self.chatDisplayNode.navigateButtons.reactionsPressed = { [weak self] in
if let strongSelf = self, strongSelf.isNodeLoaded, case let .peer(peerId) = strongSelf.chatLocation { if let strongSelf = self, strongSelf.isNodeLoaded, let peerId = strongSelf.chatLocation.peerId {
let signal = strongSelf.context.engine.messages.earliestUnseenPersonalReactionMessage(peerId: peerId) let signal = strongSelf.context.engine.messages.earliestUnseenPersonalReactionMessage(peerId: peerId, threadId: strongSelf.chatLocation.threadId)
strongSelf.navigationActionDisposable.set((signal |> deliverOnMainQueue).start(next: { result in strongSelf.navigationActionDisposable.set((signal |> deliverOnMainQueue).start(next: { result in
if let strongSelf = self { if let strongSelf = self {
switch result { switch result {
@ -7017,10 +7023,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
action: { _, f in action: { _, f in
f(.dismissWithoutContent) f(.dismissWithoutContent)
guard let strongSelf = self, case let .peer(peerId) = strongSelf.chatLocation else { guard let strongSelf = self, let peerId = strongSelf.chatLocation.peerId else {
return return
} }
let _ = clearPeerUnseenReactionsInteractively(account: strongSelf.context.account, peerId: peerId).start() let _ = clearPeerUnseenReactionsInteractively(account: strongSelf.context.account, peerId: peerId, threadId: strongSelf.chatLocation.threadId).start()
} }
))) )))
let items = ContextController.Items(content: .list(menuItems)) let items = ContextController.Items(content: .list(menuItems))
@ -7851,7 +7857,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self?.navigationButtonAction(.openChatInfo(expandAvatar: false)) self?.navigationButtonAction(.openChatInfo(expandAvatar: false))
}, togglePeerNotifications: { [weak self] in }, togglePeerNotifications: { [weak self] in
if let strongSelf = self, let peerId = strongSelf.chatLocation.peerId { if let strongSelf = self, let peerId = strongSelf.chatLocation.peerId {
let _ = strongSelf.context.engine.peers.togglePeerMuted(peerId: peerId).start() let _ = strongSelf.context.engine.peers.togglePeerMuted(peerId: peerId, threadId: strongSelf.chatLocation.threadId).start()
} }
}, sendContextResult: { [weak self] results, result, node, rect in }, sendContextResult: { [weak self] results, result, node, rect in
guard let strongSelf = self else { guard let strongSelf = self else {
@ -9360,7 +9366,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
}) })
self.chatUnreadMentionCountDisposable = (self.context.account.viewTracker.unseenPersonalMessagesAndReactionCount(peerId: peerId) |> deliverOnMainQueue).start(next: { [weak self] mentionCount, reactionCount in self.chatUnreadMentionCountDisposable = (self.context.account.viewTracker.unseenPersonalMessagesAndReactionCount(peerId: peerId, threadId: nil) |> deliverOnMainQueue).start(next: { [weak self] mentionCount, reactionCount in
if let strongSelf = self {
if case let .standard(previewing) = strongSelf.presentationInterfaceState.mode, previewing {
strongSelf.chatDisplayNode.navigateButtons.mentionCount = 0
strongSelf.chatDisplayNode.navigateButtons.reactionsCount = 0
} else {
strongSelf.chatDisplayNode.navigateButtons.mentionCount = mentionCount
strongSelf.chatDisplayNode.navigateButtons.reactionsCount = reactionCount
}
}
})
} else if let peerId = self.chatLocation.peerId, let threadId = self.chatLocation.threadId {
self.chatUnreadMentionCountDisposable = (self.context.account.viewTracker.unseenPersonalMessagesAndReactionCount(peerId: peerId, threadId: threadId) |> deliverOnMainQueue).start(next: { [weak self] mentionCount, reactionCount in
if let strongSelf = self { if let strongSelf = self {
if case let .standard(previewing) = strongSelf.presentationInterfaceState.mode, previewing { if case let .standard(previewing) = strongSelf.presentationInterfaceState.mode, previewing {
strongSelf.chatDisplayNode.navigateButtons.mentionCount = 0 strongSelf.chatDisplayNode.navigateButtons.mentionCount = 0

View File

@ -86,6 +86,14 @@ func chatHistoryEntriesForView(
continue continue
} }
if case let .replyThread(replyThreadMessage) = location, replyThreadMessage.isForumPost {
for media in message.media {
if let action = media as? TelegramMediaAction, case .topicCreated = action.action {
continue loop
}
}
}
if let maybeJoinMessage = joinMessage { if let maybeJoinMessage = joinMessage {
if message.timestamp > maybeJoinMessage.timestamp, (!view.holeEarlier || count > 0) { if message.timestamp > maybeJoinMessage.timestamp, (!view.holeEarlier || count > 0) {
entries.append(.MessageEntry(maybeJoinMessage, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false))) entries.append(.MessageEntry(maybeJoinMessage, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false)))

View File

@ -127,7 +127,7 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode {
return true return true
} }
init(context: AccountContext, peerId: PeerId, tagMask: MessageTags, interfaceInteraction: ChatControllerInteraction) { init(context: AccountContext, peerId: PeerId, threadId: Int64?, tagMask: MessageTags, interfaceInteraction: ChatControllerInteraction) {
self.context = context self.context = context
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = context.sharedContext.currentPresentationData.with { $0 }
@ -174,7 +174,7 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode {
if let strongSelf = self { if let strongSelf = self {
let signal: Signal<([ChatHistorySearchEntry], [MessageId: Message])?, NoError> let signal: Signal<([ChatHistorySearchEntry], [MessageId: Message])?, NoError>
if let query = query, !query.isEmpty { if let query = query, !query.isEmpty {
let foundRemoteMessages: Signal<[Message], NoError> = context.engine.messages.searchMessages(location: .peer(peerId: peerId, fromId: nil, tags: tagMask, topMsgId: nil, minDate: nil, maxDate: nil), query: query, state: nil) let foundRemoteMessages: Signal<[Message], NoError> = context.engine.messages.searchMessages(location: .peer(peerId: peerId, fromId: nil, tags: tagMask, topMsgId: threadId.flatMap { makeThreadIdMessageId(peerId: peerId, threadId: $0) }, minDate: nil, maxDate: nil), query: query, state: nil)
|> map { $0.0.messages } |> map { $0.0.messages }
|> delay(0.2, queue: Queue.concurrentDefaultQueue()) |> delay(0.2, queue: Queue.concurrentDefaultQueue())

View File

@ -229,6 +229,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
}, setPeerIdWithRevealedOptions: { _, _ in }, setPeerIdWithRevealedOptions: { _, _ in
}, setItemPinned: { _, _ in }, setItemPinned: { _, _ in
}, setPeerMuted: { _, _ in }, setPeerMuted: { _, _ in
}, setPeerThreadMuted: { _, _, _ in
}, deletePeer: { _, _ in }, deletePeer: { _, _ in
}, deletePeerThread: { _, _ in }, deletePeerThread: { _, _ in
}, updatePeerGrouping: { _, _ in }, updatePeerGrouping: { _, _ in

View File

@ -147,7 +147,7 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode {
} }
self.statusPromise.set(context.engine.data.subscribe( self.statusPromise.set(context.engine.data.subscribe(
TelegramEngine.EngineData.Item.Messages.MessageCount(peerId: peerId, tag: tagMask) TelegramEngine.EngineData.Item.Messages.MessageCount(peerId: peerId, threadId: chatLocation.threadId, tag: tagMask)
) )
|> map { count -> PeerInfoStatusData? in |> map { count -> PeerInfoStatusData? in
let count: Int = count ?? 0 let count: Int = count ?? 0

View File

@ -2031,7 +2031,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
} }
return context.engine.data.subscribe(EngineDataMap( return context.engine.data.subscribe(EngineDataMap(
summaries.map { TelegramEngine.EngineData.Item.Messages.MessageCount(peerId: peerId, tag: $0) } summaries.map { TelegramEngine.EngineData.Item.Messages.MessageCount(peerId: peerId, threadId: chatLocation.threadId, tag: $0) }
)) ))
|> map { summaries -> (ContentType, [MessageTags: Int32]) in |> map { summaries -> (ContentType, [MessageTags: Int32]) in
var result: [MessageTags: Int32] = [:] var result: [MessageTags: Int32] = [:]

View File

@ -187,7 +187,7 @@ final class PeerInfoScreenData {
let invitations: PeerExportedInvitationsState? let invitations: PeerExportedInvitationsState?
let requests: PeerInvitationImportersState? let requests: PeerInvitationImportersState?
let requestsContext: PeerInvitationImportersContext? let requestsContext: PeerInvitationImportersContext?
let threadInfo: EngineMessageHistoryThread.Info? let threadData: MessageHistoryThreadData?
init( init(
peer: Peer?, peer: Peer?,
@ -206,7 +206,7 @@ final class PeerInfoScreenData {
invitations: PeerExportedInvitationsState?, invitations: PeerExportedInvitationsState?,
requests: PeerInvitationImportersState?, requests: PeerInvitationImportersState?,
requestsContext: PeerInvitationImportersContext?, requestsContext: PeerInvitationImportersContext?,
threadInfo: EngineMessageHistoryThread.Info? threadData: MessageHistoryThreadData?
) { ) {
self.peer = peer self.peer = peer
self.chatPeer = chatPeer self.chatPeer = chatPeer
@ -224,7 +224,7 @@ final class PeerInfoScreenData {
self.invitations = invitations self.invitations = invitations
self.requests = requests self.requests = requests
self.requestsContext = requestsContext self.requestsContext = requestsContext
self.threadInfo = threadInfo self.threadData = threadData
} }
} }
@ -477,7 +477,7 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id,
invitations: nil, invitations: nil,
requests: nil, requests: nil,
requestsContext: nil, requestsContext: nil,
threadInfo: nil threadData: nil
) )
} }
} }
@ -504,7 +504,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
invitations: nil, invitations: nil,
requests: nil, requests: nil,
requestsContext: nil, requestsContext: nil,
threadInfo: nil threadData: nil
)) ))
case let .user(userPeerId, secretChatId, kind): case let .user(userPeerId, secretChatId, kind):
let groupsInCommon: GroupsInCommonContext? let groupsInCommon: GroupsInCommonContext?
@ -635,7 +635,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
invitations: nil, invitations: nil,
requests: nil, requests: nil,
requestsContext: nil, requestsContext: nil,
threadInfo: nil threadData: nil
) )
} }
case .channel: case .channel:
@ -711,7 +711,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
invitations: invitations, invitations: invitations,
requests: requests, requests: requests,
requestsContext: currentRequestsContext, requestsContext: currentRequestsContext,
threadInfo: nil threadData: nil
) )
} }
case let .group(groupId): case let .group(groupId):
@ -815,19 +815,19 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
let requestsContextPromise = Promise<PeerInvitationImportersContext?>(nil) let requestsContextPromise = Promise<PeerInvitationImportersContext?>(nil)
let requestsStatePromise = Promise<PeerInvitationImportersState?>(nil) let requestsStatePromise = Promise<PeerInvitationImportersState?>(nil)
let threadInfo: Signal<EngineMessageHistoryThread.Info?, NoError> let threadData: Signal<MessageHistoryThreadData?, NoError>
if case let .replyThread(message) = chatLocation { if case let .replyThread(message) = chatLocation {
let threadId = Int64(message.messageId.id) let threadId = Int64(message.messageId.id)
let viewKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: peerId, threadId: threadId) let viewKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: peerId, threadId: threadId)
threadInfo = context.account.postbox.combinedView(keys: [viewKey]) threadData = context.account.postbox.combinedView(keys: [viewKey])
|> map { views -> EngineMessageHistoryThread.Info? in |> map { views -> MessageHistoryThreadData? in
guard let view = views.views[viewKey] as? MessageHistoryThreadInfoView else { guard let view = views.views[viewKey] as? MessageHistoryThreadInfoView else {
return nil return nil
} }
return view.info?.get(MessageHistoryThreadData.self)?.info return view.info?.get(MessageHistoryThreadData.self)
} }
} else { } else {
threadInfo = .single(nil) threadData = .single(nil)
} }
return combineLatest(queue: .mainQueue(), return combineLatest(queue: .mainQueue(),
@ -840,9 +840,9 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
invitationsStatePromise.get(), invitationsStatePromise.get(),
requestsContextPromise.get(), requestsContextPromise.get(),
requestsStatePromise.get(), requestsStatePromise.get(),
threadInfo threadData
) )
|> map { peerView, availablePanes, globalNotificationSettings, status, membersData, currentInvitationsContext, invitations, currentRequestsContext, requests, threadInfo -> PeerInfoScreenData in |> map { peerView, availablePanes, globalNotificationSettings, status, membersData, currentInvitationsContext, invitations, currentRequestsContext, requests, threadData -> PeerInfoScreenData in
var discussionPeer: Peer? var discussionPeer: Peer?
if case let .known(maybeLinkedDiscussionPeerId) = (peerView.cachedData as? CachedChannelData)?.linkedDiscussionPeerId, let linkedDiscussionPeerId = maybeLinkedDiscussionPeerId, let peer = peerView.peers[linkedDiscussionPeerId] { if case let .known(maybeLinkedDiscussionPeerId) = (peerView.cachedData as? CachedChannelData)?.linkedDiscussionPeerId, let linkedDiscussionPeerId = maybeLinkedDiscussionPeerId, let peer = peerView.peers[linkedDiscussionPeerId] {
discussionPeer = peer discussionPeer = peer
@ -882,13 +882,20 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
requestsStatePromise.set(requestsContext.state |> map(Optional.init)) requestsStatePromise.set(requestsContext.state |> map(Optional.init))
} }
} }
var notificationSettings: TelegramPeerNotificationSettings?
if let threadData = threadData {
notificationSettings = threadData.notificationSettings
} else {
notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings
}
return PeerInfoScreenData( return PeerInfoScreenData(
peer: peerView.peers[groupId], peer: peerView.peers[groupId],
chatPeer: peerView.peers[groupId], chatPeer: peerView.peers[groupId],
cachedData: peerView.cachedData, cachedData: peerView.cachedData,
status: status, status: status,
notificationSettings: peerView.notificationSettings as? TelegramPeerNotificationSettings, notificationSettings: notificationSettings,
globalNotificationSettings: globalNotificationSettings, globalNotificationSettings: globalNotificationSettings,
isContact: peerView.peerIsContact, isContact: peerView.peerIsContact,
availablePanes: availablePanes ?? [], availablePanes: availablePanes ?? [],
@ -900,20 +907,29 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
invitations: invitations, invitations: invitations,
requests: requests, requests: requests,
requestsContext: currentRequestsContext, requestsContext: currentRequestsContext,
threadInfo: threadInfo threadData: threadData
) )
} }
} }
} }
} }
func canEditPeerInfo(context: AccountContext, peer: Peer?) -> Bool { func canEditPeerInfo(context: AccountContext, peer: Peer?, threadData: MessageHistoryThreadData?) -> Bool {
if context.account.peerId == peer?.id { if context.account.peerId == peer?.id {
return true return true
} }
if let channel = peer as? TelegramChannel { if let channel = peer as? TelegramChannel {
if channel.hasPermission(.changeInfo) { if let threadData = threadData {
return true if channel.hasPermission(.pinMessages) {
return true
}
if threadData.author == context.account.peerId {
return true
}
} else {
if channel.hasPermission(.changeInfo) {
return true
}
} }
} else if let group = peer as? TelegramGroup { } else if let group = peer as? TelegramGroup {
switch group.role { switch group.role {

View File

@ -634,7 +634,7 @@ final class PeerInfoEditingAvatarOverlayNode: ASDisplayNode {
transition.updateAlpha(node: self, alpha: 1.0 - fraction) transition.updateAlpha(node: self, alpha: 1.0 - fraction)
} }
func update(peer: Peer?, item: PeerInfoAvatarListItem?, updatingAvatar: PeerInfoUpdatingAvatar?, uploadProgress: CGFloat?, theme: PresentationTheme, avatarSize: CGFloat, isEditing: Bool) { func update(peer: Peer?, threadData: MessageHistoryThreadData?, item: PeerInfoAvatarListItem?, updatingAvatar: PeerInfoUpdatingAvatar?, uploadProgress: CGFloat?, theme: PresentationTheme, avatarSize: CGFloat, isEditing: Bool) {
guard let peer = peer else { guard let peer = peer else {
return return
} }
@ -644,7 +644,7 @@ final class PeerInfoEditingAvatarOverlayNode: ASDisplayNode {
let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .linear) let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .linear)
if canEditPeerInfo(context: self.context, peer: peer) { if canEditPeerInfo(context: self.context, peer: peer, threadData: threadData) {
var overlayHidden = true var overlayHidden = true
if let updatingAvatar = updatingAvatar { if let updatingAvatar = updatingAvatar {
overlayHidden = false overlayHidden = false
@ -730,12 +730,12 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode {
} }
var removedPhotoResourceIds = Set<String>() var removedPhotoResourceIds = Set<String>()
func update(peer: Peer?, item: PeerInfoAvatarListItem?, updatingAvatar: PeerInfoUpdatingAvatar?, uploadProgress: CGFloat?, theme: PresentationTheme, avatarSize: CGFloat, isEditing: Bool) { func update(peer: Peer?, threadData: MessageHistoryThreadData?, item: PeerInfoAvatarListItem?, updatingAvatar: PeerInfoUpdatingAvatar?, uploadProgress: CGFloat?, theme: PresentationTheme, avatarSize: CGFloat, isEditing: Bool) {
guard let peer = peer else { guard let peer = peer else {
return return
} }
let canEdit = canEditPeerInfo(context: self.context, peer: peer) let canEdit = canEditPeerInfo(context: self.context, peer: peer, threadData: threadData)
let previousItem = self.item let previousItem = self.item
var item = item var item = item
@ -1906,14 +1906,14 @@ final class PeerInfoHeaderEditingContentNode: ASDisplayNode {
self.itemNodes[key]?.layer.addShakeAnimation() self.itemNodes[key]?.layer.addShakeAnimation()
} }
func update(width: CGFloat, safeInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, peer: Peer?, cachedData: CachedPeerData?, isContact: Bool, isSettings: Bool, presentationData: PresentationData, transition: ContainedViewLayoutTransition) -> CGFloat { func update(width: CGFloat, safeInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, peer: Peer?, threadData: MessageHistoryThreadData?, cachedData: CachedPeerData?, isContact: Bool, isSettings: Bool, presentationData: PresentationData, transition: ContainedViewLayoutTransition) -> CGFloat {
let avatarSize: CGFloat = isModalOverlay ? 200.0 : 100.0 let avatarSize: CGFloat = isModalOverlay ? 200.0 : 100.0
let avatarFrame = CGRect(origin: CGPoint(x: floor((width - avatarSize) / 2.0), y: statusBarHeight + 13.0), size: CGSize(width: avatarSize, height: avatarSize)) let avatarFrame = CGRect(origin: CGPoint(x: floor((width - avatarSize) / 2.0), y: statusBarHeight + 13.0), size: CGSize(width: avatarSize, height: avatarSize))
transition.updateFrameAdditiveToCenter(node: self.avatarNode, frame: CGRect(origin: avatarFrame.center, size: CGSize())) transition.updateFrameAdditiveToCenter(node: self.avatarNode, frame: CGRect(origin: avatarFrame.center, size: CGSize()))
var contentHeight: CGFloat = statusBarHeight + 10.0 + avatarSize + 20.0 var contentHeight: CGFloat = statusBarHeight + 10.0 + avatarSize + 20.0
if canEditPeerInfo(context: self.context, peer: peer) { if canEditPeerInfo(context: self.context, peer: peer, threadData: threadData) {
if self.avatarButtonNode.supernode == nil { if self.avatarButtonNode.supernode == nil {
self.addSubnode(self.avatarButtonNode) self.addSubnode(self.avatarButtonNode)
} }
@ -1936,12 +1936,12 @@ final class PeerInfoHeaderEditingContentNode: ASDisplayNode {
} }
} else if let _ = peer as? TelegramGroup { } else if let _ = peer as? TelegramGroup {
fieldKeys.append(.title) fieldKeys.append(.title)
if canEditPeerInfo(context: self.context, peer: peer) { if canEditPeerInfo(context: self.context, peer: peer, threadData: threadData) {
fieldKeys.append(.description) fieldKeys.append(.description)
} }
} else if let _ = peer as? TelegramChannel { } else if let _ = peer as? TelegramChannel {
fieldKeys.append(.title) fieldKeys.append(.title)
if canEditPeerInfo(context: self.context, peer: peer) { if canEditPeerInfo(context: self.context, peer: peer, threadData: threadData) {
fieldKeys.append(.description) fieldKeys.append(.description)
} }
} }
@ -1995,10 +1995,10 @@ final class PeerInfoHeaderEditingContentNode: ASDisplayNode {
} else { } else {
placeholder = presentationData.strings.GroupInfo_GroupNamePlaceholder placeholder = presentationData.strings.GroupInfo_GroupNamePlaceholder
} }
isEnabled = canEditPeerInfo(context: self.context, peer: peer) isEnabled = canEditPeerInfo(context: self.context, peer: peer, threadData: threadData)
case .description: case .description:
placeholder = presentationData.strings.Channel_Edit_AboutItem placeholder = presentationData.strings.Channel_Edit_AboutItem
isEnabled = canEditPeerInfo(context: self.context, peer: peer) isEnabled = canEditPeerInfo(context: self.context, peer: peer, threadData: threadData)
} }
let itemHeight = itemNode.update(width: width, safeInset: safeInset, isSettings: isSettings, hasPrevious: hasPrevious, hasNext: key != fieldKeys.last, placeholder: placeholder, isEnabled: isEnabled, presentationData: presentationData, updateText: updateText) let itemHeight = itemNode.update(width: width, safeInset: safeInset, isSettings: isSettings, hasPrevious: hasPrevious, hasNext: key != fieldKeys.last, placeholder: placeholder, isEnabled: isEnabled, presentationData: presentationData, updateText: updateText)
transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: width, height: itemHeight))) transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: width, height: itemHeight)))
@ -2029,6 +2029,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
private var presentationData: PresentationData? private var presentationData: PresentationData?
private var state: PeerInfoState? private var state: PeerInfoState?
private var peer: Peer? private var peer: Peer?
private var threadData: MessageHistoryThreadData?
private var avatarSize: CGFloat? private var avatarSize: CGFloat?
private let isOpenedFromChat: Bool private let isOpenedFromChat: Bool
@ -2225,7 +2226,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
guard let strongSelf = self, let state = strongSelf.state, let peer = strongSelf.peer, let presentationData = strongSelf.presentationData, let avatarSize = strongSelf.avatarSize else { guard let strongSelf = self, let state = strongSelf.state, let peer = strongSelf.peer, let presentationData = strongSelf.presentationData, let avatarSize = strongSelf.avatarSize else {
return return
} }
strongSelf.editingContentNode.avatarNode.update(peer: peer, item: strongSelf.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing) strongSelf.editingContentNode.avatarNode.update(peer: peer, threadData: strongSelf.threadData, item: strongSelf.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing)
} }
self.avatarListNode.animateOverlaysFadeIn = { [weak self] in self.avatarListNode.animateOverlaysFadeIn = { [weak self] in
@ -2344,9 +2345,10 @@ final class PeerInfoHeaderNode: ASDisplayNode {
private var currentCredibilityIcon: CredibilityIcon? private var currentCredibilityIcon: CredibilityIcon?
private var currentPanelStatusData: PeerInfoStatusData? private var currentPanelStatusData: PeerInfoStatusData?
func update(width: CGFloat, containerHeight: CGFloat, containerInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, isMediaOnly: Bool, contentOffset: CGFloat, paneContainerY: CGFloat, presentationData: PresentationData, peer: Peer?, cachedData: CachedPeerData?, threadInfo: EngineMessageHistoryThread.Info?, notificationSettings: TelegramPeerNotificationSettings?, statusData: PeerInfoStatusData?, panelStatusData: (PeerInfoStatusData?, PeerInfoStatusData?, CGFloat?), isSecretChat: Bool, isContact: Bool, isSettings: Bool, state: PeerInfoState, metrics: LayoutMetrics, transition: ContainedViewLayoutTransition, additive: Bool) -> CGFloat { func update(width: CGFloat, containerHeight: CGFloat, containerInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, isMediaOnly: Bool, contentOffset: CGFloat, paneContainerY: CGFloat, presentationData: PresentationData, peer: Peer?, cachedData: CachedPeerData?, threadData: MessageHistoryThreadData?, notificationSettings: TelegramPeerNotificationSettings?, statusData: PeerInfoStatusData?, panelStatusData: (PeerInfoStatusData?, PeerInfoStatusData?, CGFloat?), isSecretChat: Bool, isContact: Bool, isSettings: Bool, state: PeerInfoState, metrics: LayoutMetrics, transition: ContainedViewLayoutTransition, additive: Bool) -> CGFloat {
self.state = state self.state = state
self.peer = peer self.peer = peer
self.threadData = threadData
self.avatarListNode.listContainerNode.peer = peer self.avatarListNode.listContainerNode.peer = peer
let previousPanelStatusData = self.currentPanelStatusData let previousPanelStatusData = self.currentPanelStatusData
@ -2503,7 +2505,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.buttonsContainerNode.alpha = self.regularContentNode.alpha self.buttonsContainerNode.alpha = self.regularContentNode.alpha
self.editingContentNode.alpha = state.isEditing ? 1.0 : 0.0 self.editingContentNode.alpha = state.isEditing ? 1.0 : 0.0
let editingContentHeight = self.editingContentNode.update(width: width, safeInset: containerInset, statusBarHeight: statusBarHeight, navigationHeight: navigationHeight, isModalOverlay: isModalOverlay, peer: state.isEditing ? peer : nil, cachedData: cachedData, isContact: isContact, isSettings: isSettings, presentationData: presentationData, transition: transition) let editingContentHeight = self.editingContentNode.update(width: width, safeInset: containerInset, statusBarHeight: statusBarHeight, navigationHeight: navigationHeight, isModalOverlay: isModalOverlay, peer: state.isEditing ? peer : nil, threadData: threadData, cachedData: cachedData, isContact: isContact, isSettings: isSettings, presentationData: presentationData, transition: transition)
transition.updateFrame(node: self.editingContentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -contentOffset), size: CGSize(width: width, height: editingContentHeight))) transition.updateFrame(node: self.editingContentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -contentOffset), size: CGSize(width: width, height: editingContentHeight)))
let avatarOverlayFarme = self.editingContentNode.convert(self.editingContentNode.avatarNode.frame, to: self) let avatarOverlayFarme = self.editingContentNode.convert(self.editingContentNode.avatarNode.frame, to: self)
@ -2567,7 +2569,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let expandedAvatarListHeight = min(width, containerHeight - expandedAvatarControlsHeight) let expandedAvatarListHeight = min(width, containerHeight - expandedAvatarControlsHeight)
let expandedAvatarListSize = CGSize(width: width, height: expandedAvatarListHeight) let expandedAvatarListSize = CGSize(width: width, height: expandedAvatarListHeight)
let buttonKeys: [PeerInfoHeaderButtonKey] = self.isSettings ? [] : peerInfoHeaderButtons(peer: peer, cachedData: cachedData, isOpenedFromChat: self.isOpenedFromChat, isExpanded: true, videoCallsEnabled: width > 320.0 && self.videoCallsEnabled, isSecretChat: isSecretChat, isContact: isContact, threadInfo: threadInfo) let buttonKeys: [PeerInfoHeaderButtonKey] = self.isSettings ? [] : peerInfoHeaderButtons(peer: peer, cachedData: cachedData, isOpenedFromChat: self.isOpenedFromChat, isExpanded: true, videoCallsEnabled: width > 320.0 && self.videoCallsEnabled, isSecretChat: isSecretChat, isContact: isContact, threadInfo: threadData?.info)
var isPremium = false var isPremium = false
var isVerified = false var isVerified = false
@ -2591,8 +2593,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
title = presentationData.strings.Conversation_SavedMessages title = presentationData.strings.Conversation_SavedMessages
} else if peer.id == self.context.account.peerId && !self.isSettings { } else if peer.id == self.context.account.peerId && !self.isSettings {
title = presentationData.strings.DialogList_Replies title = presentationData.strings.DialogList_Replies
} else if let threadInfo = threadInfo { } else if let threadData = threadData {
title = threadInfo.title title = threadData.info.title
} else { } else {
title = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) title = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
} }
@ -2619,7 +2621,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
smallSubtitleString = NSAttributedString(string: subtitle, font: Font.regular(15.0), textColor: UIColor(rgb: 0xffffff, alpha: 0.7)) smallSubtitleString = NSAttributedString(string: subtitle, font: Font.regular(15.0), textColor: UIColor(rgb: 0xffffff, alpha: 0.7))
subtitleString = NSAttributedString(string: subtitle, font: Font.regular(17.0), textColor: presentationData.theme.list.itemSecondaryTextColor) subtitleString = NSAttributedString(string: subtitle, font: Font.regular(17.0), textColor: presentationData.theme.list.itemSecondaryTextColor)
usernameString = NSAttributedString(string: "", font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor) usernameString = NSAttributedString(string: "", font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor)
} else if let _ = threadInfo { } else if let _ = threadData {
let subtitleColor: UIColor = presentationData.theme.list.itemSecondaryTextColor let subtitleColor: UIColor = presentationData.theme.list.itemSecondaryTextColor
//TODO:localize //TODO:localize
@ -2909,9 +2911,9 @@ final class PeerInfoHeaderNode: ASDisplayNode {
}) })
} }
self.avatarListNode.update(size: CGSize(), avatarSize: avatarSize, isExpanded: self.isAvatarExpanded, peer: peer, threadInfo: threadInfo, theme: presentationData.theme, transition: transition) self.avatarListNode.update(size: CGSize(), avatarSize: avatarSize, isExpanded: self.isAvatarExpanded, peer: peer, threadInfo: threadData?.info, theme: presentationData.theme, transition: transition)
self.editingContentNode.avatarNode.update(peer: peer, item: self.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing) self.editingContentNode.avatarNode.update(peer: peer, threadData: threadData, item: self.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing)
self.avatarOverlayNode.update(peer: peer, item: self.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing) self.avatarOverlayNode.update(peer: peer, threadData: threadData, item: self.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing)
if additive { if additive {
transition.updateSublayerTransformScaleAdditive(node: self.avatarListNode.avatarContainerNode, scale: avatarScale) transition.updateSublayerTransformScaleAdditive(node: self.avatarListNode.avatarContainerNode, scale: avatarScale)
transition.updateSublayerTransformScaleAdditive(node: self.avatarOverlayNode, scale: avatarScale) transition.updateSublayerTransformScaleAdditive(node: self.avatarOverlayNode, scale: avatarScale)

View File

@ -1078,7 +1078,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
let ItemMembers = 6 let ItemMembers = 6
let ItemMemberRequests = 7 let ItemMemberRequests = 7
if let _ = data.threadInfo { if let _ = data.threadData {
let mainUsername: String let mainUsername: String
if let addressName = channel.addressName { if let addressName = channel.addressName {
mainUsername = addressName mainUsername = addressName
@ -1599,7 +1599,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
} }
} }
if cachedData.flags.contains(.canSetStickerSet) && canEditPeerInfo(context: context, peer: channel) { if cachedData.flags.contains(.canSetStickerSet) && canEditPeerInfo(context: context, peer: channel, threadData: data.threadData) {
items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemStickerPack, label: .text(cachedData.stickerPack?.title ?? presentationData.strings.GroupInfo_SharedMediaNone), text: presentationData.strings.Stickers_GroupStickers, icon: UIImage(bundleImageName: "Settings/Menu/Stickers"), action: { items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemStickerPack, label: .text(cachedData.stickerPack?.title ?? presentationData.strings.GroupInfo_SharedMediaNone), text: presentationData.strings.Stickers_GroupStickers, icon: UIImage(bundleImageName: "Settings/Menu/Stickers"), action: {
interaction.editingOpenStickerPackSetup() interaction.editingOpenStickerPackSetup()
})) }))
@ -2817,7 +2817,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
case .edit: case .edit:
if case let .replyThread(message) = strongSelf.chatLocation { if case let .replyThread(message) = strongSelf.chatLocation {
let threadId = Int64(message.messageId.id) let threadId = Int64(message.messageId.id)
if let threadInfo = strongSelf.data?.threadInfo { if let threadInfo = strongSelf.data?.threadData?.info {
let controller = ForumCreateTopicScreen(context: strongSelf.context, peerId: strongSelf.peerId, mode: .edit(topic: threadInfo)) let controller = ForumCreateTopicScreen(context: strongSelf.context, peerId: strongSelf.peerId, mode: .edit(topic: threadInfo))
controller.navigationPresentation = .modal controller.navigationPresentation = .modal
let context = strongSelf.context let context = strongSelf.context
@ -2990,7 +2990,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
} else { } else {
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel, nil, nil) strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel, nil, nil)
} }
} else if let group = data.peer as? TelegramGroup, canEditPeerInfo(context: strongSelf.context, peer: group) { } else if let group = data.peer as? TelegramGroup, canEditPeerInfo(context: strongSelf.context, peer: group, threadData: data.threadData) {
let title = strongSelf.headerNode.editingContentNode.editingTextForKey(.title) ?? "" let title = strongSelf.headerNode.editingContentNode.editingTextForKey(.title) ?? ""
let description = strongSelf.headerNode.editingContentNode.editingTextForKey(.description) ?? "" let description = strongSelf.headerNode.editingContentNode.editingTextForKey(.description) ?? ""
@ -3049,7 +3049,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel, nil, nil) strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel, nil, nil)
})) }))
} }
} else if let channel = data.peer as? TelegramChannel, canEditPeerInfo(context: strongSelf.context, peer: channel) { } else if let channel = data.peer as? TelegramChannel, canEditPeerInfo(context: strongSelf.context, peer: channel, threadData: data.threadData) {
let title = strongSelf.headerNode.editingContentNode.editingTextForKey(.title) ?? "" let title = strongSelf.headerNode.editingContentNode.editingTextForKey(.title) ?? ""
let description = strongSelf.headerNode.editingContentNode.editingTextForKey(.description) ?? "" let description = strongSelf.headerNode.editingContentNode.editingTextForKey(.description) ?? ""
@ -3383,7 +3383,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
} }
}) })
self.refreshMessageTagStatsDisposable = context.engine.messages.refreshMessageTagStats(peerId: peerId, tags: [.video, .photo, .gif, .music, .voiceOrInstantVideo, .webPage, .file]).start() self.refreshMessageTagStatsDisposable = context.engine.messages.refreshMessageTagStats(peerId: peerId, threadId: chatLocation.threadId, tags: [.video, .photo, .gif, .music, .voiceOrInstantVideo, .webPage, .file]).start()
} }
deinit { deinit {
@ -3557,7 +3557,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
} }
self.view.endEditing(true) self.view.endEditing(true)
return self.context.sharedContext.openChatMessage(OpenChatMessageParams(context: self.context, chatLocation: nil, chatLocationContextHolder: nil, message: galleryMessage, standalone: false, reverseMessageGalleryOrder: true, navigationController: navigationController, dismissInput: { [weak self] in return self.context.sharedContext.openChatMessage(OpenChatMessageParams(context: self.context, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder, message: galleryMessage, standalone: false, reverseMessageGalleryOrder: true, navigationController: navigationController, dismissInput: { [weak self] in
self?.view.endEditing(true) self?.view.endEditing(true)
}, present: { [weak self] c, a in }, present: { [weak self] c, a in
self?.controller?.present(c, in: .window(.root), with: a, blockInteraction: true) self?.controller?.present(c, in: .window(.root), with: a, blockInteraction: true)
@ -3919,7 +3919,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
self.requestCall(isVideo: false, gesture: gesture) self.requestCall(isVideo: false, gesture: gesture)
case .mute: case .mute:
if let notificationSettings = self.data?.notificationSettings, case .muted = notificationSettings.muteState { if let notificationSettings = self.data?.notificationSettings, case .muted = notificationSettings.muteState {
let _ = self.context.engine.peers.updatePeerMuteSetting(peerId: self.peerId, muteInterval: nil).start() let _ = self.context.engine.peers.updatePeerMuteSetting(peerId: self.peerId, threadId: self.chatLocation.threadId, muteInterval: nil).start()
let iconColor: UIColor = .white let iconColor: UIColor = .white
self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .universal(animation: "anim_profileunmute", scale: 0.075, colors: [ self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .universal(animation: "anim_profileunmute", scale: 0.075, colors: [
@ -3968,7 +3968,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
let _ = strongSelf.context.engine.peers.updatePeerMuteSetting(peerId: strongSelf.peerId, muteInterval: value).start() let _ = strongSelf.context.engine.peers.updatePeerMuteSetting(peerId: strongSelf.peerId, threadId: strongSelf.chatLocation.threadId, muteInterval: value).start()
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_mute_for", scale: 0.066, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedFor(mutedForTimeIntervalString(strings: strongSelf.presentationData.strings, value: value)).string), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_mute_for", scale: 0.066, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedFor(mutedForTimeIntervalString(strings: strongSelf.presentationData.strings, value: value)).string), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
}))) })))
@ -4006,7 +4006,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
let _ = strongSelf.context.engine.peers.updatePeerNotificationSoundInteractive(peerId: strongSelf.peerId, sound: .default).start() let _ = strongSelf.context.engine.peers.updatePeerNotificationSoundInteractive(peerId: strongSelf.peerId, threadId: strongSelf.chatLocation.threadId, sound: .default).start()
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_sound_on", scale: 0.056, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipSoundEnabled), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_sound_on", scale: 0.056, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipSoundEnabled), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
}))) })))
@ -4019,7 +4019,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
let _ = strongSelf.context.engine.peers.updatePeerNotificationSoundInteractive(peerId: strongSelf.peerId, sound: .none).start() let _ = strongSelf.context.engine.peers.updatePeerNotificationSoundInteractive(peerId: strongSelf.peerId, threadId: strongSelf.chatLocation.threadId, sound: .none).start()
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_sound_off", scale: 0.056, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipSoundDisabled), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_sound_off", scale: 0.056, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipSoundDisabled), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
}))) })))
@ -4033,19 +4033,20 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
guard let strongSelf = self, let peer = strongSelf.data?.peer else { guard let strongSelf = self, let peer = strongSelf.data?.peer else {
return return
} }
let threadId = strongSelf.chatLocation.threadId
let context = strongSelf.context let context = strongSelf.context
let updatePeerSound: (PeerId, PeerMessageSound) -> Signal<Void, NoError> = { peerId, sound in let updatePeerSound: (PeerId, PeerMessageSound) -> Signal<Void, NoError> = { peerId, sound in
return context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, sound: sound) |> deliverOnMainQueue return context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, threadId: threadId, sound: sound) |> deliverOnMainQueue
} }
let updatePeerNotificationInterval: (PeerId, Int32?) -> Signal<Void, NoError> = { peerId, muteInterval in let updatePeerNotificationInterval: (PeerId, Int32?) -> Signal<Void, NoError> = { peerId, muteInterval in
return context.engine.peers.updatePeerMuteSetting(peerId: peerId, muteInterval: muteInterval) |> deliverOnMainQueue return context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: threadId, muteInterval: muteInterval) |> deliverOnMainQueue
} }
let updatePeerDisplayPreviews:(PeerId, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = { let updatePeerDisplayPreviews: (PeerId, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = {
peerId, displayPreviews in peerId, displayPreviews in
return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, displayPreviews: displayPreviews) |> deliverOnMainQueue return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: threadId, displayPreviews: displayPreviews) |> deliverOnMainQueue
} }
let mode: NotificationExceptionMode let mode: NotificationExceptionMode
@ -4063,7 +4064,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
mode = .groups([:]) mode = .groups([:])
} }
let exceptionController = notificationPeerExceptionController(context: context, updatedPresentationData: strongSelf.controller?.updatedPresentationData, peer: peer, mode: mode, edit: true, updatePeerSound: { peerId, sound in let exceptionController = notificationPeerExceptionController(context: context, updatedPresentationData: strongSelf.controller?.updatedPresentationData, peer: peer, threadId: threadId, mode: mode, edit: true, updatePeerSound: { peerId, sound in
let _ = (updatePeerSound(peer.id, sound) let _ = (updatePeerSound(peer.id, sound)
|> deliverOnMainQueue).start(next: { _ in |> deliverOnMainQueue).start(next: { _ in
@ -4107,7 +4108,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
return return
} }
let _ = strongSelf.context.engine.peers.updatePeerMuteSetting(peerId: strongSelf.peerId, muteInterval: Int32.max).start() let _ = strongSelf.context.engine.peers.updatePeerMuteSetting(peerId: strongSelf.peerId, threadId: strongSelf.chatLocation.threadId, muteInterval: Int32.max).start()
let iconColor: UIColor = .white let iconColor: UIColor = .white
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_profilemute", scale: 0.075, colors: [ strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_profilemute", scale: 0.075, colors: [
@ -4151,8 +4152,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
return .single(items) return .single(items)
} }
let allHeaderButtons = Set(peerInfoHeaderButtons(peer: peer, cachedData: data.cachedData, isOpenedFromChat: strongSelf.isOpenedFromChat, isExpanded: false, videoCallsEnabled: strongSelf.videoCallsEnabled, isSecretChat: strongSelf.peerId.namespace == Namespaces.Peer.SecretChat, isContact: strongSelf.data?.isContact ?? false, threadInfo: data.threadInfo)) let allHeaderButtons = Set(peerInfoHeaderButtons(peer: peer, cachedData: data.cachedData, isOpenedFromChat: strongSelf.isOpenedFromChat, isExpanded: false, videoCallsEnabled: strongSelf.videoCallsEnabled, isSecretChat: strongSelf.peerId.namespace == Namespaces.Peer.SecretChat, isContact: strongSelf.data?.isContact ?? false, threadInfo: data.threadData?.info))
let headerButtons = Set(peerInfoHeaderButtons(peer: peer, cachedData: data.cachedData, isOpenedFromChat: strongSelf.isOpenedFromChat, isExpanded: true, videoCallsEnabled: strongSelf.videoCallsEnabled, isSecretChat: strongSelf.peerId.namespace == Namespaces.Peer.SecretChat, isContact: strongSelf.data?.isContact ?? false, threadInfo: strongSelf.data?.threadInfo)) let headerButtons = Set(peerInfoHeaderButtons(peer: peer, cachedData: data.cachedData, isOpenedFromChat: strongSelf.isOpenedFromChat, isExpanded: true, videoCallsEnabled: strongSelf.videoCallsEnabled, isSecretChat: strongSelf.peerId.namespace == Namespaces.Peer.SecretChat, isContact: strongSelf.data?.isContact ?? false, threadInfo: strongSelf.data?.threadData?.info))
let filteredButtons = allHeaderButtons.subtracting(headerButtons) let filteredButtons = allHeaderButtons.subtracting(headerButtons)
@ -4820,9 +4821,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
return return
} }
if value <= 0 { if value <= 0 {
let _ = strongSelf.context.engine.peers.updatePeerMuteSetting(peerId: strongSelf.peerId, muteInterval: nil).start() let _ = strongSelf.context.engine.peers.updatePeerMuteSetting(peerId: strongSelf.peerId, threadId: strongSelf.chatLocation.threadId, muteInterval: nil).start()
} else { } else {
let _ = strongSelf.context.engine.peers.updatePeerMuteSetting(peerId: strongSelf.peerId, muteInterval: value).start() let _ = strongSelf.context.engine.peers.updatePeerMuteSetting(peerId: strongSelf.peerId, threadId: strongSelf.chatLocation.threadId, muteInterval: value).start()
let timeString = stringForPreciseRelativeTimestamp(strings: strongSelf.presentationData.strings, relativeTimestamp: Int32(Date().timeIntervalSince1970) + value, relativeTo: Int32(Date().timeIntervalSince1970), dateTimeFormat: strongSelf.presentationData.dateTimeFormat) let timeString = stringForPreciseRelativeTimestamp(strings: strongSelf.presentationData.strings, relativeTimestamp: Int32(Date().timeIntervalSince1970) + value, relativeTo: Int32(Date().timeIntervalSince1970), dateTimeFormat: strongSelf.presentationData.dateTimeFormat)
@ -5269,7 +5270,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
let _ = strongSelf.context.engine.peers.updatePeerNotificationSoundInteractive(peerId: strongSelf.peerId, sound: sound).start() let _ = strongSelf.context.engine.peers.updatePeerNotificationSoundInteractive(peerId: strongSelf.peerId, threadId: strongSelf.chatLocation.threadId, sound: sound).start()
}) })
soundController.navigationPresentation = .modal soundController.navigationPresentation = .modal
strongSelf.controller?.push(soundController) strongSelf.controller?.push(soundController)
@ -5277,7 +5278,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
let _ = strongSelf.context.engine.peers.updatePeerMuteSetting(peerId: strongSelf.peerId, muteInterval: value).start() let _ = strongSelf.context.engine.peers.updatePeerMuteSetting(peerId: strongSelf.peerId, threadId: strongSelf.chatLocation.threadId, muteInterval: value).start()
}) })
strongSelf.view.endEditing(true) strongSelf.view.endEditing(true)
strongSelf.controller?.present(muteSettingsController, in: .window(.root)) strongSelf.controller?.present(muteSettingsController, in: .window(.root))
@ -5298,7 +5299,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
let _ = strongSelf.context.engine.peers.updatePeerNotificationSoundInteractive(peerId: strongSelf.peerId, sound: sound).start() let _ = strongSelf.context.engine.peers.updatePeerNotificationSoundInteractive(peerId: strongSelf.peerId, threadId: strongSelf.chatLocation.threadId, sound: sound).start()
}) })
strongSelf.controller?.push(soundController) strongSelf.controller?.push(soundController)
}) })
@ -5310,7 +5311,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
guard let strongSelf = self, let peer = peer else { guard let strongSelf = self, let peer = peer else {
return return
} }
let _ = strongSelf.context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peer.id, displayPreviews: value ? .show : .hide).start() let _ = strongSelf.context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peer.id, threadId: strongSelf.chatLocation.threadId, displayPreviews: value ? .show : .hide).start()
}) })
} }
@ -6526,7 +6527,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
} }
private func openAvatarForEditing(fromGallery: Bool = false, completion: @escaping () -> Void = {}) { private func openAvatarForEditing(fromGallery: Bool = false, completion: @escaping () -> Void = {}) {
guard let peer = self.data?.peer, canEditPeerInfo(context: self.context, peer: peer) else { guard let peer = self.data?.peer, canEditPeerInfo(context: self.context, peer: peer, threadData: self.data?.threadData) else {
return return
} }
@ -7308,7 +7309,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
} }
} }
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, mode: .list, placeholder: self.presentationData.strings.Common_Search, hasBackground: true, contentNode: ChatHistorySearchContainerNode(context: self.context, peerId: self.peerId, tagMask: tagMask, interfaceInteraction: self.chatInterfaceInteraction), cancel: { [weak self] in self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, mode: .list, placeholder: self.presentationData.strings.Common_Search, hasBackground: true, contentNode: ChatHistorySearchContainerNode(context: self.context, peerId: self.peerId, threadId: self.chatLocation.threadId, tagMask: tagMask, interfaceInteraction: self.chatInterfaceInteraction), cancel: { [weak self] in
self?.deactivateSearch() self?.deactivateSearch()
}) })
} }
@ -7364,8 +7365,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
let peerId = self.peerId let peerId = self.peerId
let _ = (self.context.engine.data.get(EngineDataMap([ let _ = (self.context.engine.data.get(EngineDataMap([
TelegramEngine.EngineData.Item.Messages.MessageCount(peerId: peerId, tag: .photo), TelegramEngine.EngineData.Item.Messages.MessageCount(peerId: peerId, threadId: self.chatLocation.threadId, tag: .photo),
TelegramEngine.EngineData.Item.Messages.MessageCount(peerId: peerId, tag: .video) TelegramEngine.EngineData.Item.Messages.MessageCount(peerId: peerId, threadId: self.chatLocation.threadId, tag: .video)
])) ]))
|> deliverOnMainQueue).start(next: { [weak self] messageCounts in |> deliverOnMainQueue).start(next: { [weak self] messageCounts in
guard let strongSelf = self else { guard let strongSelf = self else {
@ -7417,17 +7418,19 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
items.append(.action(generateAction(false))) items.append(.action(generateAction(false)))
var ignoreNextActions = false var ignoreNextActions = false
items.append(.action(ContextMenuActionItem(text: strings.SharedMedia_ShowCalendar, icon: { theme in if strongSelf.chatLocation.threadId == nil {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Calendar"), color: theme.contextMenu.primaryColor) items.append(.action(ContextMenuActionItem(text: strings.SharedMedia_ShowCalendar, icon: { theme in
}, action: { _, a in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Calendar"), color: theme.contextMenu.primaryColor)
if ignoreNextActions { }, action: { _, a in
return if ignoreNextActions {
} return
ignoreNextActions = true }
a(.default) ignoreNextActions = true
a(.default)
self?.openMediaCalendar()
}))) self?.openMediaCalendar()
})))
}
if photoCount != 0 && videoCount != 0 { if photoCount != 0 && videoCount != 0 {
items.append(.separator) items.append(.separator)
@ -7665,7 +7668,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
} }
let headerInset = sectionInset let headerInset = sectionInset
var headerHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : self.scrollNode.view.contentOffset.y, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, threadInfo: self.data?.threadInfo, notificationSettings: self.data?.notificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, metrics: layout.metrics, transition: transition, additive: additive) var headerHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : self.scrollNode.view.contentOffset.y, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, threadData: self.data?.threadData, notificationSettings: self.data?.notificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, metrics: layout.metrics, transition: transition, additive: additive)
if !self.isSettings && !self.state.isEditing { if !self.isSettings && !self.state.isEditing {
headerHeight += 71.0 headerHeight += 71.0
} }
@ -8025,7 +8028,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
} }
let headerInset = sectionInset let headerInset = sectionInset
let _ = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : offsetY, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, threadInfo: self.data?.threadInfo, notificationSettings: self.data?.notificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, metrics: layout.metrics, transition: transition, additive: additive) let _ = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : offsetY, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, threadData: self.data?.threadData, notificationSettings: self.data?.notificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, metrics: layout.metrics, transition: transition, additive: additive)
} }
let paneAreaExpansionDistance: CGFloat = 32.0 let paneAreaExpansionDistance: CGFloat = 32.0
@ -9042,7 +9045,7 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig
} }
let headerInset = sectionInset let headerInset = sectionInset
topHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: topNavigationBar.bounds.height, isModalOverlay: layout.isModalOverlay, isMediaOnly: false, contentOffset: 0.0, paneContainerY: 0.0, presentationData: self.presentationData, peer: self.screenNode.data?.peer, cachedData: self.screenNode.data?.cachedData, threadInfo: self.screenNode.data?.threadInfo, notificationSettings: self.screenNode.data?.notificationSettings, statusData: self.screenNode.data?.status, panelStatusData: (nil, nil, nil), isSecretChat: self.screenNode.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.screenNode.data?.isContact ?? false, isSettings: self.screenNode.isSettings, state: self.screenNode.state, metrics: layout.metrics, transition: transition, additive: false) topHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: topNavigationBar.bounds.height, isModalOverlay: layout.isModalOverlay, isMediaOnly: false, contentOffset: 0.0, paneContainerY: 0.0, presentationData: self.presentationData, peer: self.screenNode.data?.peer, cachedData: self.screenNode.data?.cachedData, threadData: self.screenNode.data?.threadData, notificationSettings: self.screenNode.data?.notificationSettings, statusData: self.screenNode.data?.status, panelStatusData: (nil, nil, nil), isSecretChat: self.screenNode.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.screenNode.data?.isContact ?? false, isSettings: self.screenNode.isSettings, state: self.screenNode.state, metrics: layout.metrics, transition: transition, additive: false)
} }
let titleScale = (fraction * previousTitleNode.view.bounds.height + (1.0 - fraction) * self.headerNode.titleNodeRawContainer.bounds.height) / previousTitleNode.view.bounds.height let titleScale = (fraction * previousTitleNode.view.bounds.height + (1.0 - fraction) * self.headerNode.titleNodeRawContainer.bounds.height) / previousTitleNode.view.bounds.height

View File

@ -880,7 +880,7 @@ final class PeerInfoGifPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScrollViewDe
animationTimer.start() animationTimer.start()
self.statusPromise.set(context.engine.data.subscribe( self.statusPromise.set(context.engine.data.subscribe(
TelegramEngine.EngineData.Item.Messages.MessageCount(peerId: peerId, tag: tagMaskForType(self.contentType)) TelegramEngine.EngineData.Item.Messages.MessageCount(peerId: peerId, threadId: chatLocation.threadId, tag: tagMaskForType(self.contentType))
) )
|> map { count -> PeerInfoStatusData? in |> map { count -> PeerInfoStatusData? in
let count: Int = count ?? 0 let count: Int = count ?? 0
@ -925,7 +925,7 @@ final class PeerInfoGifPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScrollViewDe
return return
} }
self.isRequestingView = true self.isRequestingView = true
self.listDisposable.set((self.context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: self.peerId), index: .upperBound, anchorIndex: .upperBound, count: self.numberOfItemsToRequest, fixedCombinedReadStates: nil, tagMask: tagMaskForType(self.contentType)) self.listDisposable.set((self.context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: self.peerId, threadId: self.chatLocation.threadId), index: .upperBound, anchorIndex: .upperBound, count: self.numberOfItemsToRequest, fixedCombinedReadStates: nil, tagMask: tagMaskForType(self.contentType))
|> deliverOnMainQueue).start(next: { [weak self] (view, updateType, _) in |> deliverOnMainQueue).start(next: { [weak self] (view, updateType, _) in
guard let strongSelf = self else { guard let strongSelf = self else {
return return

View File

@ -213,7 +213,7 @@ private final class PrefetchManagerInnerImpl {
context = PrefetchMediaContext() context = PrefetchMediaContext()
self.contexts[id] = context self.contexts[id] = context
let priority: FetchManagerPriority = .backgroundPrefetch(locationOrder: HistoryPreloadIndex(index: nil, hasUnread: false, isMuted: false, isPriority: true), localOrder: MessageIndex(id: MessageId(peerId: PeerId(0), namespace: 0, id: order), timestamp: 0)) let priority: FetchManagerPriority = .backgroundPrefetch(locationOrder: HistoryPreloadIndex(index: nil, threadId: nil, hasUnread: false, isMuted: false, isPriority: true), localOrder: MessageIndex(id: MessageId(peerId: PeerId(0), namespace: 0, id: order), timestamp: 0))
if case .full = automaticDownload { if case .full = automaticDownload {
let fetchSignal = freeMediaFileInteractiveFetched(fetchManager: self.fetchManager, fileReference: .standalone(media: media), priority: priority) let fetchSignal = freeMediaFileInteractiveFetched(fetchManager: self.fetchManager, fileReference: .standalone(media: media), priority: priority)

View File

@ -91,7 +91,7 @@ final class WatchChatMessagesHandler: WatchRequestHandler {
|> take(1) |> take(1)
|> mapToSignal({ context -> Signal<(MessageHistoryView, Bool, PresentationData), NoError> in |> mapToSignal({ context -> Signal<(MessageHistoryView, Bool, PresentationData), NoError> in
if let context = context { if let context = context {
return context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: peerId), index: .upperBound, anchorIndex: .upperBound, count: limit, fixedCombinedReadStates: nil) return context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: peerId, threadId: nil), index: .upperBound, anchorIndex: .upperBound, count: limit, fixedCombinedReadStates: nil)
|> map { messageHistoryView, _, _ -> (MessageHistoryView, Bool, PresentationData) in |> map { messageHistoryView, _, _ -> (MessageHistoryView, Bool, PresentationData) in
return (messageHistoryView, peerId == context.account.peerId, context.sharedContext.currentPresentationData.with { $0 }) return (messageHistoryView, peerId == context.account.peerId, context.sharedContext.currentPresentationData.with { $0 })
} }
@ -833,7 +833,7 @@ final class WatchPeerSettingsHandler: WatchRequestHandler {
var signal: Signal<Void, NoError>? var signal: Signal<Void, NoError>?
if let args = subscription as? TGBridgePeerUpdateNotificationSettingsSubscription, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) { if let args = subscription as? TGBridgePeerUpdateNotificationSettingsSubscription, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) {
signal = context.engine.peers.togglePeerMuted(peerId: peerId) signal = context.engine.peers.togglePeerMuted(peerId: peerId, threadId: nil)
} else if let args = subscription as? TGBridgePeerUpdateBlockStatusSubscription, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) { } else if let args = subscription as? TGBridgePeerUpdateBlockStatusSubscription, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) {
signal = context.engine.privacy.requestUpdatePeerIsBlocked(peerId: peerId, isBlocked: args.blocked) signal = context.engine.privacy.requestUpdatePeerIsBlocked(peerId: peerId, isBlocked: args.blocked)
} }