diff --git a/Telegram/NotificationService/Sources/NotificationService.swift b/Telegram/NotificationService/Sources/NotificationService.swift index 0bf9ce88c0..a512d4008a 100644 --- a/Telegram/NotificationService/Sources/NotificationService.swift +++ b/Telegram/NotificationService/Sources/NotificationService.swift @@ -1645,7 +1645,7 @@ private final class NotificationServiceHandler { } |> then( stateManager.postbox.transaction { transaction -> (MediaResourceReference, Int64?)? in - guard let state = transaction.getPeerStoryState(peerId: peerId)?.get(Stories.PeerState.self) else { + guard let state = transaction.getPeerStoryState(peerId: peerId)?.entry.get(Stories.PeerState.self) else { return nil } let firstUnseenItem = transaction.getStoryItems(peerId: peerId).first(where: { entry in diff --git a/Telegram/SiriIntents/IntentMessages.swift b/Telegram/SiriIntents/IntentMessages.swift index e5ae56a823..1a2efcdc6d 100644 --- a/Telegram/SiriIntents/IntentMessages.swift +++ b/Telegram/SiriIntents/IntentMessages.swift @@ -36,7 +36,11 @@ func unreadMessages(account: Account) -> Signal<[INMessage], NoError> { |> mapToSignal { view -> Signal<[INMessage], NoError> in var signals: [Signal<[INMessage], NoError>] = [] for entry in view.0.entries { - if case let .MessageEntry(index, _, readState, isMuted, _, _, _, _, _, _, _, _, _) = entry { + if case let .MessageEntry(entryData) = entry { + let index = entryData.index + let readState = entryData.readState + let isMuted = entryData.isRemovedFromTotalUnreadCount + if index.messageIndex.id.peerId.namespace != Namespaces.Peer.CloudUser { continue } diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 71edde0743..b9b1d45a9f 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -178,6 +178,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController private var powerSavingMonitoringDisposable: Disposable? + private var rawStoryArchiveSubscriptions: EngineStorySubscriptions? + private var storyArchiveSubscriptionsDisposable: Disposable? + private var rawStorySubscriptions: EngineStorySubscriptions? private var shouldFixStorySubscriptionOrder: Bool = false private var fixedStorySubscriptionOrder: [EnginePeer.Id] = [] @@ -756,6 +759,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.actionDisposables.dispose() self.powerSavingMonitoringDisposable?.dispose() self.storySubscriptionsDisposable?.dispose() + self.storyArchiveSubscriptionsDisposable?.dispose() self.preloadStorySubscriptionsDisposable?.dispose() self.storyProgressDisposable?.dispose() self.storiesPostingAvailabilityDisposable?.dispose() @@ -1243,10 +1247,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } switch item.content { - case let .groupReference(groupId, _, _, _, _): - let chatListController = ChatListControllerImpl(context: strongSelf.context, location: .chatList(groupId: groupId), controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false) + case let .groupReference(groupReference): + let chatListController = ChatListControllerImpl(context: strongSelf.context, location: .chatList(groupId: groupReference.groupId), controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false) chatListController.navigationPresentation = .master - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: archiveContextMenuItems(context: strongSelf.context, groupId: groupId._asGroup(), chatListController: strongSelf) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) + let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: archiveContextMenuItems(context: strongSelf.context, groupId: groupReference.groupId._asGroup(), chatListController: strongSelf) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) strongSelf.presentInGlobalOverlay(contextController) case let .peer(peerData): let peer = peerData.peer @@ -1306,16 +1310,30 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } } - self.chatListDisplayNode.mainContainerNode.openStories = { [weak self] peerId, itemNode in + self.chatListDisplayNode.mainContainerNode.openStories = { [weak self] subject, itemNode in guard let self else { return } - let storyContent = StoryContentContextImpl(context: self.context, isHidden: false, focusedPeerId: peerId, singlePeer: true) + let isHidden: Bool + let focusedPeerId: EnginePeer.Id? + let singlePeer: Bool + switch subject { + case let .peer(peerId): + isHidden = self.location == .chatList(groupId: .archive) + focusedPeerId = peerId + singlePeer = true + case .archive: + isHidden = true + focusedPeerId = nil + singlePeer = false + } + + let storyContent = StoryContentContextImpl(context: self.context, isHidden: isHidden, focusedPeerId: focusedPeerId, singlePeer: singlePeer) let _ = (storyContent.state |> filter { $0.slice != nil } |> take(1) - |> deliverOnMainQueue).start(next: { [weak self, weak itemNode] _ in + |> deliverOnMainQueue).start(next: { [weak self, weak itemNode] state in guard let self else { return } @@ -1337,10 +1355,30 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController transitionIn: transitionIn, transitionOut: { _, _ in if let itemNode = itemNode as? ChatListItemNode { - let rect = itemNode.avatarNode.view.convert(itemNode.avatarNode.view.bounds, to: itemNode.view) + let transitionView = itemNode.avatarNode.view + let destinationView = itemNode.view + let rect = transitionView.convert(transitionView.bounds, to: destinationView) return StoryContainerScreen.TransitionOut( - destinationView: itemNode.view, - transitionView: nil, + destinationView: destinationView, + transitionView: StoryContainerScreen.TransitionView( + makeView: { [weak transitionView] in + let parentView = UIView() + if let copyView = transitionView?.snapshotContentTree(unhide: true) { + parentView.addSubview(copyView) + } + return parentView + }, + updateView: { copyView, state, transition in + guard let view = copyView.subviews.first else { + return + } + let size = state.sourceSize.interpolate(to: CGSize(width: state.destinationSize.width, height: state.destinationSize.height), amount: state.progress) + let scaleSize = state.sourceSize.interpolate(to: CGSize(width: state.destinationSize.width - 7.0, height: state.destinationSize.height - 7.0), amount: state.progress) + transition.setPosition(view: view, position: CGPoint(x: size.width * 0.5, y: size.height * 0.5)) + transition.setScale(view: view, scale: scaleSize.width / state.destinationSize.width) + }, + insertCloneTransitionView: nil + ), destinationRect: rect, destinationCornerRadius: rect.height * 0.5, destinationIsAvatar: true, @@ -1789,7 +1827,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.displayNodeDidLoad() - if case .chatList(.root) = self.location { + if case .chatList = self.location { let automaticDownloadNetworkType = context.account.networkType |> map { type -> MediaAutoDownloadNetworkType in switch type { @@ -1802,7 +1840,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController |> distinctUntilChanged self.preloadStorySubscriptionsDisposable = (combineLatest(queue: .mainQueue(), - self.context.engine.messages.preloadStorySubscriptions(isHidden: false), + self.context.engine.messages.preloadStorySubscriptions(isHidden: self.location == .chatList(groupId: .archive)), self.context.sharedContext.automaticMediaDownloadSettings, automaticDownloadNetworkType ) @@ -1842,7 +1880,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.preloadStoryResourceDisposables.removeValue(forKey: id) } }) - self.storySubscriptionsDisposable = (self.context.engine.messages.storySubscriptions(isHidden: false) + + self.storySubscriptionsDisposable = (self.context.engine.messages.storySubscriptions(isHidden: self.location == .chatList(groupId: .archive)) |> deliverOnMainQueue).start(next: { [weak self] rawStorySubscriptions in guard let self else { return @@ -1880,7 +1919,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.requestLayout(transition: transition) self.chatListDisplayNode.temporaryContentOffsetChangeTransition = nil - self.chatListDisplayNode.mainContainerNode.currentItemNode.updateState { chatListState in + if rawStorySubscriptions.items.isEmpty { + self.chatListDisplayNode.scrollToTopIfStoriesAreExpanded() + } + + /*self.chatListDisplayNode.mainContainerNode.currentItemNode.updateState { chatListState in var chatListState = chatListState var peerStoryMapping: [EnginePeer.Id: ChatListNodeState.StoryState] = [:] @@ -1896,7 +1939,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController chatListState.peerStoryMapping = peerStoryMapping return chatListState - } + }*/ self.storiesReady.set(.single(true)) @@ -1914,6 +1957,41 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } self.updateStoryUploadProgress(progress) }) + + if case .chatList(.root) = self.location { + self.storyArchiveSubscriptionsDisposable = (self.context.engine.messages.storySubscriptions(isHidden: true) + |> deliverOnMainQueue).start(next: { [weak self] rawStoryArchiveSubscriptions in + guard let self else { + return + } + + self.rawStoryArchiveSubscriptions = rawStoryArchiveSubscriptions + + let hasUnseenArchive: Bool? + if rawStoryArchiveSubscriptions.items.isEmpty { + hasUnseenArchive = nil + } else { + hasUnseenArchive = rawStoryArchiveSubscriptions.items.contains(where: { $0.hasUnseen }) + } + + self.chatListDisplayNode.mainContainerNode.currentItemNode.updateState { chatListState in + var chatListState = chatListState + + chatListState.hasUnseenArchiveStories = hasUnseenArchive + + return chatListState + } + + self.storiesReady.set(.single(true)) + + Queue.mainQueue().after(1.0, { [weak self] in + guard let self else { + return + } + self.maybeDisplayStoryTooltip() + }) + }) + } } } @@ -2540,7 +2618,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.chatListDisplayNode.scrollToTop() } - if case .chatList(.root) = self.location, let componentView = self.chatListHeaderView() { + if case .chatList = self.location, let componentView = self.chatListHeaderView() { componentView.storyPeerAction = { [weak self] peer in guard let self else { return @@ -2703,15 +2781,26 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } }))) - items.append(.action(ContextMenuActionItem(text: "Hide \(peer.compactDisplayTitle)", icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MoveToContacts"), color: theme.contextMenu.primaryColor) + let hideText: String + if self.location == .chatList(groupId: .archive) { + hideText = "Unarchive" + } else { + hideText = "Archive" + } + let iconName = self.location == .chatList(groupId: .archive) ? "Chat/Context Menu/MoveToChats" : "Chat/Context Menu/MoveToContacts" + items.append(.action(ContextMenuActionItem(text: hideText, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: iconName), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in f(.dismissWithoutContent) guard let self else { return } - self.context.engine.peers.updatePeerStoriesHidden(id: peer.id, isHidden: true) + if self.location == .chatList(groupId: .archive) { + self.context.engine.peers.updatePeerStoriesHidden(id: peer.id, isHidden: false) + } else { + self.context.engine.peers.updatePeerStoriesHidden(id: peer.id, isHidden: true) + } guard let parentController = self.parent as? TabBarController, let contactsController = (self.navigationController as? TelegramRootControllerInterface)?.getContactsController(), let sourceFrame = parentController.frameForControllerTab(controller: contactsController) else { return @@ -3442,7 +3531,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.shouldFixStorySubscriptionOrder = true } } - let storyContent = StoryContentContextImpl(context: self.context, isHidden: false, focusedPeerId: peerId, singlePeer: false, fixedOrder: self.fixedStorySubscriptionOrder) + let storyContent = StoryContentContextImpl(context: self.context, isHidden: self.location == .chatList(groupId: .archive), focusedPeerId: peerId, singlePeer: false, fixedOrder: self.fixedStorySubscriptionOrder) let _ = (storyContent.state |> take(1) |> deliverOnMainQueue).start(next: { [weak self] storyContentState in diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index 3c08069541..bff1543784 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -925,10 +925,10 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele return } - if itemNode.listNode.isTracking && !self.currentItemNode.startedScrollingAtUpperBound && self.tempTopInset == 0.0 { + if !self.isInlineMode, itemNode.listNode.isTracking && !self.currentItemNode.startedScrollingAtUpperBound && self.tempTopInset == 0.0 { if case let .known(value) = offset { if value < -1.0 { - if let storySubscriptions = self.controller?.orderedStorySubscriptions, (shouldDisplayStoriesInChatListHeader(storySubscriptions: storySubscriptions) || true) { + if let controller = self.controller, let storySubscriptions = controller.orderedStorySubscriptions, shouldDisplayStoriesInChatListHeader(storySubscriptions: storySubscriptions, isHidden: controller.location == .chatList(groupId: .archive)) { self.currentItemNode.startedScrollingAtUpperBound = true self.tempTopInset = ChatListNavigationBar.storiesScrollHeight } @@ -939,7 +939,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele self.contentOffset = offset self.contentOffsetChanged?(offset) - if self.currentItemNode.startedScrollingAtUpperBound && self.tempTopInset != 0.0 { + if !self.isInlineMode, self.currentItemNode.startedScrollingAtUpperBound && self.tempTopInset != 0.0 { if case let .known(value) = offset { if value > 4.0 { self.currentItemNode.startedScrollingAtUpperBound = false @@ -954,12 +954,12 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele } } itemNode.listNode.didBeginInteractiveDragging = { [weak self] _ in - guard let self else { + guard let self, !self.isInlineMode else { return } let tempTopInset: CGFloat if self.currentItemNode.startedScrollingAtUpperBound && !self.isInlineMode { - if let storySubscriptions = self.controller?.orderedStorySubscriptions, (shouldDisplayStoriesInChatListHeader(storySubscriptions: storySubscriptions) || true) { + if let controller = self.controller, let storySubscriptions = controller.orderedStorySubscriptions, shouldDisplayStoriesInChatListHeader(storySubscriptions: storySubscriptions, isHidden: controller.location == .chatList(groupId: .archive)) { tempTopInset = ChatListNavigationBar.storiesScrollHeight } else { tempTopInset = 0.0 @@ -1001,8 +1001,8 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele itemNode.listNode.activateChatPreview = { [weak self] item, threadId, sourceNode, gesture, location in self?.activateChatPreview?(item, threadId, sourceNode, gesture, location) } - itemNode.listNode.openStories = { [weak self] peerId, itemNode in - self?.openStories?(peerId, itemNode) + itemNode.listNode.openStories = { [weak self] subject, itemNode in + self?.openStories?(subject, itemNode) } itemNode.listNode.addedVisibleChatsWithPeerIds = { [weak self] ids in self?.addedVisibleChatsWithPeerIds?(ids) @@ -1076,7 +1076,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele var endedInteractiveDragging: ((ListView) -> Void)? var shouldStopScrolling: ((ListView, CGFloat) -> Bool)? var activateChatPreview: ((ChatListItem, Int64?, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? - var openStories: ((EnginePeer.Id, ASDisplayNode?) -> Void)? + var openStories: ((ChatListNode.OpenStoriesSubject, ASDisplayNode?) -> Void)? var addedVisibleChatsWithPeerIds: (([EnginePeer.Id]) -> Void)? var didBeginSelectingChats: (() -> Void)? var canExpandHiddenItems: (() -> Bool)? @@ -1802,7 +1802,7 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate { return false } - if let storySubscriptions = controller.orderedStorySubscriptions, (shouldDisplayStoriesInChatListHeader(storySubscriptions: storySubscriptions) || true) { + if let storySubscriptions = controller.orderedStorySubscriptions, shouldDisplayStoriesInChatListHeader(storySubscriptions: storySubscriptions, isHidden: controller.location == .chatList(groupId: .archive)) { if let navigationBarComponentView = self.navigationBarView.view as? ChatListNavigationBar.View { if navigationBarComponentView.storiesUnlocked { return true @@ -1946,7 +1946,7 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate { secondaryContent: headerContent?.secondaryContent, secondaryTransition: self.inlineStackContainerTransitionFraction, storySubscriptions: self.controller?.orderedStorySubscriptions, - storiesIncludeHidden: false, + storiesIncludeHidden: self.location == .chatList(groupId: .archive), uploadProgress: self.controller?.storyUploadProgress, tabsNode: tabsNode, tabsNodeIsSearch: tabsNodeIsSearch, @@ -2534,4 +2534,20 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate { self.mainContainerNode.scrollToTop(animated: true, adjustForTempInset: false) } } + + func scrollToTopIfStoriesAreExpanded() { + if let contentOffset = self.mainContainerNode.contentOffset, case let .known(offset) = contentOffset, offset < 0.0 { + self.mainContainerNode.scrollToTop(animated: true, adjustForTempInset: false) + } + } +} + +func shouldDisplayStoriesInChatListHeader(storySubscriptions: EngineStorySubscriptions, isHidden: Bool) -> Bool { + if !storySubscriptions.items.isEmpty { + return true + } + if !isHidden, let accountItem = storySubscriptions.accountItem, (accountItem.hasUnseen || accountItem.hasPending) { + return true + } + return false } diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index f287ad95e3..70ca54d052 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -3244,8 +3244,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { switch item.content { case let .peer(peerData): return (selectedItemNode.view, bounds, peerData.messages.last?.id ?? peerData.peer.peerId) - case let .groupReference(groupId, _, _, _, _): - return (selectedItemNode.view, bounds, groupId) + case let .groupReference(groupReference): + return (selectedItemNode.view, bounds, groupReference.groupId) } } return nil diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 9da6263f59..35428a72c5 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -66,14 +66,14 @@ public enum ChatListItemContent { } public struct StoryState: Equatable { - public var hasUnseen: Bool + public var stats: EngineChatList.StoryStats public var hasUnseenCloseFriends: Bool public init( - hasUnseen: Bool, + stats: EngineChatList.StoryStats, hasUnseenCloseFriends: Bool ) { - self.hasUnseen = hasUnseen + self.stats = stats self.hasUnseenCloseFriends = hasUnseenCloseFriends } } @@ -138,9 +138,34 @@ public enum ChatListItemContent { self.storyState = storyState } } + + public struct GroupReferenceData { + public var groupId: EngineChatList.Group + public var peers: [EngineChatList.GroupItem.Item] + public var message: EngineMessage? + public var unreadCount: Int + public var hiddenByDefault: Bool + public var storyState: StoryState? + + public init( + groupId: EngineChatList.Group, + peers: [EngineChatList.GroupItem.Item], + message: EngineMessage?, + unreadCount: Int, + hiddenByDefault: Bool, + storyState: StoryState? + ) { + self.groupId = groupId + self.peers = peers + self.message = message + self.unreadCount = unreadCount + self.hiddenByDefault = hiddenByDefault + self.storyState = storyState + } + } case peer(PeerData) - case groupReference(groupId: EngineChatList.Group, peers: [EngineChatList.GroupItem.Item], message: EngineMessage?, unreadCount: Int, hiddenByDefault: Bool) + case groupReference(GroupReferenceData) public var chatLocation: ChatLocation? { switch self { @@ -267,8 +292,8 @@ public class ChatListItem: ListViewItem, ChatListSearchItemNeighbour { } else if let peer = peerData.peer.peers[peerData.peer.peerId] { self.interaction.peerSelected(peer, nil, nil, peerData.promoInfo) } - case let .groupReference(groupId, _, _, _, _): - self.interaction.groupSelected(groupId) + case let .groupReference(groupReferenceData): + self.interaction.groupSelected(groupReferenceData.groupId) } } @@ -1005,9 +1030,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { return nil } switch item.content { - case let .groupReference(_, _, _, unreadCount, _): + case let .groupReference(groupReferenceData): var result = item.presentationData.strings.ChatList_ArchivedChatsTitle - let allCount = unreadCount + let allCount = groupReferenceData.unreadCount if allCount > 0 { result += "\n\(item.presentationData.strings.VoiceOver_Chat_UnreadMessages(Int32(allCount)))" } @@ -1037,7 +1062,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { return nil } switch item.content { - case let .groupReference(_, peers, messageValue, _, _): + case let .groupReference(groupReferenceData): + let peers = groupReferenceData.peers + let messageValue = groupReferenceData.message if let message = messageValue, let peer = peers.first?.peer { let messages = [message] var result = "" @@ -1314,12 +1341,12 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { if peerData.peer.peerId.namespace == Namespaces.Peer.SecretChat { enablePreview = false } - case let .groupReference(_, _, _, _, hiddenByDefault): - if let previousItem = previousItem, case let .groupReference(_, _, _, _, previousHiddenByDefault) = previousItem.content, hiddenByDefault != previousHiddenByDefault { + case let .groupReference(groupReferenceData): + if let previousItem = previousItem, case let .groupReference(previousGroupReferenceData) = previousItem.content, groupReferenceData.hiddenByDefault != previousGroupReferenceData.hiddenByDefault { UIView.transition(with: self.avatarNode.view, duration: 0.3, options: [.transitionCrossDissolve], animations: { }, completion: nil) } - self.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: .archivedChatsIcon(hiddenByDefault: hiddenByDefault), emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads) + self.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: .archivedChatsIcon(hiddenByDefault: groupReferenceData.hiddenByDefault), emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads) } if let peer = peer { @@ -1628,7 +1655,12 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { promoInfo = promoInfoValue displayAsMessage = displayAsMessageValue hasFailedMessages = messagesValue.last?.flags.contains(.Failed) ?? false // hasFailedMessagesValue - case let .groupReference(_, peers, messageValue, unreadCountValue, hiddenByDefault): + case let .groupReference(groupReferenceData): + let peers = groupReferenceData.peers + let messageValue = groupReferenceData.message + let unreadCountValue = groupReferenceData.unreadCount + let hiddenByDefault = groupReferenceData.hiddenByDefault + if let _ = messageValue, !peers.isEmpty { contentPeer = .chat(peers[0].peer) } else { @@ -2150,8 +2182,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { let dateText: String var topIndex: MessageIndex? switch item.content { - case let .groupReference(_, _, message, _, _): - topIndex = message?.index + case let .groupReference(groupReferenceData): + topIndex = groupReferenceData.message?.index case let .peer(peerData): topIndex = peerData.messages.first?.index } @@ -2763,6 +2795,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { var storyState: ChatListItemContent.StoryState? if case let .peer(peerData) = item.content { storyState = peerData.storyState + } else if case let .groupReference(groupReference) = item.content { + storyState = groupReference.storyState } let avatarFrame = CGRect(origin: CGPoint(x: leftInset - avatarLeftInset + editingOffset + 10.0 + revealOffset, y: floor((itemHeight - avatarDiameter) / 2.0)), size: CGSize(width: avatarDiameter, height: avatarDiameter)) @@ -2805,12 +2839,15 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { let _ = avatarStoryIndicator.update( transition: indicatorTransition, component: AnyComponent(AvatarStoryIndicatorComponent( - hasUnseen: storyState.hasUnseen, + hasUnseen: storyState.stats.unseenCount != 0, hasUnseenCloseFriendsItems: storyState.hasUnseenCloseFriends, theme: item.presentationData.theme, activeLineWidth: 2.0, inactiveLineWidth: 1.0 + UIScreenPixel, - counters: nil + counters: AvatarStoryIndicatorComponent.Counters( + totalCount: storyState.stats.totalCount, + unseenCount: storyState.stats.unseenCount + ) )), environment: {}, containerSize: indicatorFrame.size @@ -3504,7 +3541,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { } let separatorInset: CGFloat - if case let .groupReference(_, _, _, _, hiddenByDefault) = item.content, hiddenByDefault { + if case let .groupReference(groupReferenceData) = item.content, groupReferenceData.hiddenByDefault { separatorInset = 0.0 } else if (!nextIsPinned && isPinned) || last { separatorInset = 0.0 @@ -3526,7 +3563,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { backgroundColor = theme.itemSelectedBackgroundColor highlightedBackgroundColor = theme.itemHighlightedBackgroundColor } else if isPinned { - if case let .groupReference(_, _, _, _, hiddenByDefault) = item.content, hiddenByDefault { + if case let .groupReference(groupReferenceData) = item.content, groupReferenceData.hiddenByDefault { backgroundColor = theme.itemBackgroundColor highlightedBackgroundColor = theme.itemHighlightedBackgroundColor } else { @@ -3873,10 +3910,16 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { @objc private func avatarStoryTapGesture(_ recognizer: UITapGestureRecognizer) { if case .ended = recognizer.state { - guard let item = self.item, case let .peer(peerData) = item.content else { + guard let item = self.item else { return } - item.interaction.openStories(peerData.peer.peerId, self) + switch item.content { + case let .peer(peerData): + item.interaction.openStories(.peer(peerData.peer.peerId), self) + case .groupReference: + item.interaction.openStories(.archive, self) + } + } } } diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 0cbd837165..1d7337e6c6 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -100,7 +100,7 @@ public final class ChatListNodeInteraction { let openPremiumIntro: () -> Void let openChatFolderUpdates: () -> Void let hideChatFolderUpdates: () -> Void - let openStories: (EnginePeer.Id, ASDisplayNode?) -> Void + let openStories: (ChatListNode.OpenStoriesSubject, ASDisplayNode?) -> Void public var searchTextHighightState: String? var highlightedChatLocation: ChatListHighlightedLocation? @@ -148,7 +148,7 @@ public final class ChatListNodeInteraction { openPremiumIntro: @escaping () -> Void, openChatFolderUpdates: @escaping () -> Void, hideChatFolderUpdates: @escaping () -> Void, - openStories: @escaping (EnginePeer.Id, ASDisplayNode?) -> Void + openStories: @escaping (ChatListNode.OpenStoriesSubject, ASDisplayNode?) -> Void ) { self.activateSearch = activateSearch self.peerSelected = peerSelected @@ -219,11 +219,11 @@ private func areFoundPeerArraysEqual(_ lhs: [(EnginePeer, EnginePeer?)], _ rhs: public struct ChatListNodeState: Equatable { public struct StoryState: Equatable { - public var hasUnseen: Bool + public var stats: EngineChatList.StoryStats public var hasUnseenCloseFriends: Bool - public init(hasUnseen: Bool, hasUnseenCloseFriends: Bool) { - self.hasUnseen = hasUnseen + public init(stats: EngineChatList.StoryStats, hasUnseenCloseFriends: Bool) { + self.stats = stats self.hasUnseenCloseFriends = hasUnseenCloseFriends } } @@ -251,7 +251,7 @@ public struct ChatListNodeState: Equatable { public var foundPeers: [(EnginePeer, EnginePeer?)] public var selectedPeerMap: [EnginePeer.Id: EnginePeer] public var selectedThreadIds: Set - public var peerStoryMapping: [EnginePeer.Id: ChatListNodeState.StoryState] + public var hasUnseenArchiveStories: Bool? public init( presentationData: ChatListPresentationData, @@ -267,7 +267,7 @@ public struct ChatListNodeState: Equatable { hiddenItemShouldBeTemporaryRevealed: Bool, hiddenPsaPeerId: EnginePeer.Id?, selectedThreadIds: Set, - peerStoryMapping: [EnginePeer.Id: ChatListNodeState.StoryState] + hasUnseenArchiveStories: Bool? ) { self.presentationData = presentationData self.editing = editing @@ -282,7 +282,7 @@ public struct ChatListNodeState: Equatable { self.hiddenItemShouldBeTemporaryRevealed = hiddenItemShouldBeTemporaryRevealed self.hiddenPsaPeerId = hiddenPsaPeerId self.selectedThreadIds = selectedThreadIds - self.peerStoryMapping = peerStoryMapping + self.hasUnseenArchiveStories = hasUnseenArchiveStories } public static func ==(lhs: ChatListNodeState, rhs: ChatListNodeState) -> Bool { @@ -325,7 +325,7 @@ public struct ChatListNodeState: Equatable { if lhs.selectedThreadIds != rhs.selectedThreadIds { return false } - if lhs.peerStoryMapping != rhs.peerStoryMapping { + if lhs.hasUnseenArchiveStories != rhs.hasUnseenArchiveStories { return false } return true @@ -408,7 +408,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL autoremoveTimeout: peerEntry.autoremoveTimeout, storyState: peerEntry.storyState.flatMap { storyState in return ChatListItemContent.StoryState( - hasUnseen: storyState.hasUnseen, + stats: storyState.stats, hasUnseenCloseFriends: storyState.hasUnseenCloseFriends ) } @@ -623,26 +623,32 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL } case let .HoleEntry(_, theme): return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListHoleItem(theme: theme), directionHint: entry.directionHint) - case let .GroupReferenceEntry(index, presentationData, groupId, peers, message, editing, unreadCount, revealed, hiddenByDefault): + case let .GroupReferenceEntry(groupReferenceEntry): return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem( - presentationData: presentationData, + presentationData: groupReferenceEntry.presentationData, context: context, chatListLocation: location, filterData: filterData, - index: index, - content: .groupReference( - groupId: groupId, - peers: peers, - message: message, - unreadCount: unreadCount, - hiddenByDefault: hiddenByDefault - ), - editing: editing, + index: groupReferenceEntry.index, + content: .groupReference(ChatListItemContent.GroupReferenceData( + groupId: groupReferenceEntry.groupId, + peers: groupReferenceEntry.peers, + message: groupReferenceEntry.message, + unreadCount: groupReferenceEntry.unreadCount, + hiddenByDefault: groupReferenceEntry.hiddenByDefault, + storyState: groupReferenceEntry.storyState.flatMap { storyState in + return ChatListItemContent.StoryState( + stats: storyState.stats, + hasUnseenCloseFriends: storyState.hasUnseenCloseFriends + ) + } + )), + editing: groupReferenceEntry.editing, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: true, - hiddenOffset: hiddenByDefault && !revealed, + hiddenOffset: groupReferenceEntry.hiddenByDefault && !groupReferenceEntry.revealed, interaction: nodeInteraction ), directionHint: entry.directionHint) case let .ContactEntry(contactEntry): @@ -759,7 +765,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL autoremoveTimeout: peerEntry.autoremoveTimeout, storyState: peerEntry.storyState.flatMap { storyState in return ChatListItemContent.StoryState( - hasUnseen: storyState.hasUnseen, + stats: storyState.stats, hasUnseenCloseFriends: storyState.hasUnseenCloseFriends ) } @@ -928,26 +934,32 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL } case let .HoleEntry(_, theme): return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListHoleItem(theme: theme), directionHint: entry.directionHint) - case let .GroupReferenceEntry(index, presentationData, groupId, peers, message, editing, unreadCount, revealed, hiddenByDefault): + case let .GroupReferenceEntry(groupReferenceEntry): return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem( - presentationData: presentationData, + presentationData: groupReferenceEntry.presentationData, context: context, chatListLocation: location, filterData: filterData, - index: index, - content: .groupReference( - groupId: groupId, - peers: peers, - message: message, - unreadCount: unreadCount, - hiddenByDefault: hiddenByDefault - ), - editing: editing, + index: groupReferenceEntry.index, + content: .groupReference(ChatListItemContent.GroupReferenceData( + groupId: groupReferenceEntry.groupId, + peers: groupReferenceEntry.peers, + message: groupReferenceEntry.message, + unreadCount: groupReferenceEntry.unreadCount, + hiddenByDefault: groupReferenceEntry.hiddenByDefault, + storyState: groupReferenceEntry.storyState.flatMap { storyState in + return ChatListItemContent.StoryState( + stats: storyState.stats, + hasUnseenCloseFriends: storyState.hasUnseenCloseFriends + ) + } + )), + editing: groupReferenceEntry.editing, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: true, - hiddenOffset: hiddenByDefault && !revealed, + hiddenOffset: groupReferenceEntry.hiddenByDefault && !groupReferenceEntry.revealed, interaction: nodeInteraction ), directionHint: entry.directionHint) case let .ContactEntry(contactEntry): @@ -1069,6 +1081,11 @@ public enum ChatListNodeEmptyState: Equatable { } public final class ChatListNode: ListView { + public enum OpenStoriesSubject { + case peer(EnginePeer.Id) + case archive + } + private let fillPreloadItems: Bool private let context: AccountContext private let location: ChatListControllerLocation @@ -1106,7 +1123,7 @@ public final class ChatListNode: ListView { public var toggleArchivedFolderHiddenByDefault: (() -> Void)? public var hidePsa: ((EnginePeer.Id) -> Void)? public var activateChatPreview: ((ChatListItem, Int64?, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? - public var openStories: ((EnginePeer.Id, ASDisplayNode?) -> Void)? + public var openStories: ((ChatListNode.OpenStoriesSubject, ASDisplayNode?) -> Void)? private var theme: PresentationTheme @@ -1234,7 +1251,7 @@ public final class ChatListNode: ListView { isSelecting = true } - self.currentState = ChatListNodeState(presentationData: ChatListPresentationData(theme: theme, fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, disableAnimations: disableAnimations), editing: isSelecting, peerIdWithRevealedOptions: nil, selectedPeerIds: Set(), foundPeers: [], selectedPeerMap: [:], selectedAdditionalCategoryIds: Set(), peerInputActivities: nil, pendingRemovalItemIds: Set(), pendingClearHistoryPeerIds: Set(), hiddenItemShouldBeTemporaryRevealed: false, hiddenPsaPeerId: nil, selectedThreadIds: Set(), peerStoryMapping: [:]) + self.currentState = ChatListNodeState(presentationData: ChatListPresentationData(theme: theme, fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, disableAnimations: disableAnimations), editing: isSelecting, peerIdWithRevealedOptions: nil, selectedPeerIds: Set(), foundPeers: [], selectedPeerMap: [:], selectedAdditionalCategoryIds: Set(), peerInputActivities: nil, pendingRemovalItemIds: Set(), pendingClearHistoryPeerIds: Set(), hiddenItemShouldBeTemporaryRevealed: false, hiddenPsaPeerId: nil, selectedThreadIds: Set(), hasUnseenArchiveStories: nil) self.statePromise = ValuePromise(self.currentState, ignoreRepeated: true) self.theme = theme @@ -1573,11 +1590,11 @@ public final class ChatListNode: ListView { let _ = self.context.engine.peers.hideChatFolderUpdates(folderId: localFilterId).start() } }) - }, openStories: { [weak self] peerId, itemNode in + }, openStories: { [weak self] subject, itemNode in guard let self else { return } - self.openStories?(peerId, itemNode) + self.openStories?(subject, itemNode) }) nodeInteraction.isInlineMode = isInlineMode @@ -1889,6 +1906,8 @@ public final class ChatListNode: ListView { contacts = .single([]) } + let accountPeerId = context.account.peerId + let chatListNodeViewTransition = combineLatest( queue: viewProcessingQueue, hideArchivedFolderByDefault, @@ -1914,7 +1933,7 @@ public final class ChatListNode: ListView { notice = nil } - let (rawEntries, isLoading) = chatListNodeEntriesForView(update.list, state: state, savedMessagesPeer: savedMessagesPeer, foundPeers: state.foundPeers, hideArchivedFolderByDefault: hideArchivedFolderByDefault, displayArchiveIntro: displayArchiveIntro, notice: notice, mode: mode, chatListLocation: location, contacts: contacts) + let (rawEntries, isLoading) = chatListNodeEntriesForView(view: update.list, state: state, savedMessagesPeer: savedMessagesPeer, foundPeers: state.foundPeers, hideArchivedFolderByDefault: hideArchivedFolderByDefault, displayArchiveIntro: displayArchiveIntro, notice: notice, mode: mode, chatListLocation: location, contacts: contacts, accountPeerId: accountPeerId) var isEmpty = true var entries = rawEntries.filter { entry in switch entry { @@ -2260,8 +2279,8 @@ public final class ChatListNode: ListView { didIncludeRemovingPeerId = true } } - } else if case let .GroupReferenceEntry(_, _, _, _, _, _, _, _, hiddenByDefault) = entry { - didIncludeHiddenByDefaultArchive = hiddenByDefault + } else if case let .GroupReferenceEntry(groupReferenceEntry) = entry { + didIncludeHiddenByDefaultArchive = groupReferenceEntry.hiddenByDefault } else if case .Notice = entry { didIncludeNotice = true } @@ -2296,9 +2315,9 @@ public final class ChatListNode: ListView { doesIncludeRemovingPeerId = true } } - } else if case let .GroupReferenceEntry(_, _, _, _, _, _, _, _, hiddenByDefault) = entry { + } else if case let .GroupReferenceEntry(groupReferenceEntry) = entry { doesIncludeArchive = true - doesIncludeHiddenByDefaultArchive = hiddenByDefault + doesIncludeHiddenByDefaultArchive = groupReferenceEntry.hiddenByDefault } else if case .Notice = entry { doesIncludeNotice = true } @@ -2836,8 +2855,8 @@ public final class ChatListNode: ListView { isHiddenItemVisible = true } } - if case let .groupReference(_, _, _, _, hiddenByDefault) = item.content { - if hiddenByDefault { + if case let .groupReference(groupReference) = item.content { + if groupReference.hiddenByDefault { isHiddenItemVisible = true } } diff --git a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift index 0794bdf045..12b7dab724 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift @@ -302,10 +302,82 @@ enum ChatListNodeEntry: Comparable, Identifiable { } } + struct GroupReferenceEntryData: Equatable { + var index: EngineChatList.Item.Index + var presentationData: ChatListPresentationData + var groupId: EngineChatList.Group + var peers: [EngineChatList.GroupItem.Item] + var message: EngineMessage? + var editing: Bool + var unreadCount: Int + var revealed: Bool + var hiddenByDefault: Bool + var storyState: ChatListNodeState.StoryState? + + init( + index: EngineChatList.Item.Index, + presentationData: ChatListPresentationData, + groupId: EngineChatList.Group, + peers: [EngineChatList.GroupItem.Item], + message: EngineMessage?, + editing: Bool, + unreadCount: Int, + revealed: Bool, + hiddenByDefault: Bool, + storyState: ChatListNodeState.StoryState? + ) { + self.index = index + self.presentationData = presentationData + self.groupId = groupId + self.peers = peers + self.message = message + self.editing = editing + self.unreadCount = unreadCount + self.revealed = revealed + self.hiddenByDefault = hiddenByDefault + self.storyState = storyState + } + + static func ==(lhs: GroupReferenceEntryData, rhs: GroupReferenceEntryData) -> Bool { + if lhs.index != rhs.index { + return false + } + if lhs.presentationData !== rhs.presentationData { + return false + } + if lhs.groupId != rhs.groupId { + return false + } + if lhs.peers != rhs.peers { + return false + } + if lhs.message?.stableId != rhs.message?.stableId { + return false + } + if lhs.editing != rhs.editing { + return false + } + if lhs.unreadCount != rhs.unreadCount { + return false + } + if lhs.revealed != rhs.revealed { + return false + } + if lhs.hiddenByDefault != rhs.hiddenByDefault { + return false + } + if lhs.storyState != rhs.storyState { + return false + } + + return true + } + } + case HeaderEntry case PeerEntry(PeerEntryData) case HoleEntry(EngineMessage.Index, theme: PresentationTheme) - case GroupReferenceEntry(index: EngineChatList.Item.Index, presentationData: ChatListPresentationData, groupId: EngineChatList.Group, peers: [EngineChatList.GroupItem.Item], message: EngineMessage?, editing: Bool, unreadCount: Int, revealed: Bool, hiddenByDefault: Bool) + case GroupReferenceEntry(GroupReferenceEntryData) case ContactEntry(ContactEntryData) case ArchiveIntro(presentationData: ChatListPresentationData) case EmptyIntro(presentationData: ChatListPresentationData) @@ -321,8 +393,8 @@ enum ChatListNodeEntry: Comparable, Identifiable { return .index(peerEntry.index) case let .HoleEntry(holeIndex, _): return .index(.chatList(EngineChatList.Item.Index.ChatList(pinningIndex: nil, messageIndex: holeIndex))) - case let .GroupReferenceEntry(index, _, _, _, _, _, _, _, _): - return .index(index) + case let .GroupReferenceEntry(groupReferenceEntry): + return .index(groupReferenceEntry.index) case let .ContactEntry(contactEntry): return .contact(id: contactEntry.peer.id, presence: contactEntry.presence) case .ArchiveIntro: @@ -351,8 +423,8 @@ enum ChatListNodeEntry: Comparable, Identifiable { } case let .HoleEntry(holeIndex, _): return .Hole(Int64(holeIndex.id.id)) - case let .GroupReferenceEntry(_, _, groupId, _, _, _, _, _, _): - return .GroupId(groupId) + case let .GroupReferenceEntry(groupReferenceEntry): + return .GroupId(groupReferenceEntry.groupId) case let .ContactEntry(contactEntry): return .ContactId(contactEntry.peer.id) case .ArchiveIntro: @@ -393,35 +465,8 @@ enum ChatListNodeEntry: Comparable, Identifiable { default: return false } - case let .GroupReferenceEntry(lhsIndex, lhsPresentationData, lhsGroupId, lhsPeers, lhsMessage, lhsEditing, lhsUnreadState, lhsRevealed, lhsHiddenByDefault): - if case let .GroupReferenceEntry(rhsIndex, rhsPresentationData, rhsGroupId, rhsPeers, rhsMessage, rhsEditing, rhsUnreadState, rhsRevealed, rhsHiddenByDefault) = rhs { - if lhsIndex != rhsIndex { - return false - } - if lhsPresentationData !== rhsPresentationData { - return false - } - if lhsGroupId != rhsGroupId { - return false - } - if lhsPeers != rhsPeers { - return false - } - if lhsMessage?.stableId != rhsMessage?.stableId { - return false - } - if lhsEditing != rhsEditing { - return false - } - if lhsUnreadState != rhsUnreadState { - return false - } - if lhsRevealed != rhsRevealed { - return false - } - if lhsHiddenByDefault != rhsHiddenByDefault { - return false - } + case let .GroupReferenceEntry(groupReferenceEntry): + if case .GroupReferenceEntry(groupReferenceEntry) = rhs { return true } else { return false @@ -523,7 +568,7 @@ struct ChatListContactPeer { } } -func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState, savedMessagesPeer: EnginePeer?, foundPeers: [(EnginePeer, EnginePeer?)], hideArchivedFolderByDefault: Bool, displayArchiveIntro: Bool, notice: ChatListNotice?, mode: ChatListNodeMode, chatListLocation: ChatListControllerLocation, contacts: [ChatListContactPeer]) -> (entries: [ChatListNodeEntry], loading: Bool) { +func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState, savedMessagesPeer: EnginePeer?, foundPeers: [(EnginePeer, EnginePeer?)], hideArchivedFolderByDefault: Bool, displayArchiveIntro: Bool, notice: ChatListNotice?, mode: ChatListNodeMode, chatListLocation: ChatListControllerLocation, contacts: [ChatListContactPeer], accountPeerId: EnginePeer.Id) -> (entries: [ChatListNodeEntry], loading: Bool) { var result: [ChatListNodeEntry] = [] if !view.hasEarlier { @@ -639,7 +684,12 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState forumTopicData: entry.forumTopicData, topForumTopicItems: entry.topForumTopicItems, revealed: threadId == 1 && (state.hiddenItemShouldBeTemporaryRevealed || state.editing), - storyState: state.peerStoryMapping[entry.renderedPeer.peerId] + storyState: entry.renderedPeer.peerId == accountPeerId ? nil : entry.storyStats.flatMap { stats -> ChatListNodeState.StoryState in + return ChatListNodeState.StoryState( + stats: stats, + hasUnseenCloseFriends: false + ) + } )) if let threadInfo, threadInfo.isHidden { @@ -783,7 +833,16 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState if !view.hasLater, case .chatList = mode { for groupReference in view.groupItems { let messageIndex = EngineMessage.Index(id: EngineMessage.Id(peerId: EnginePeer.Id(0), namespace: 0, id: 0), timestamp: 1) - result.append(.GroupReferenceEntry( + var mappedStoryState: ChatListNodeState.StoryState? + if let hasUnseenArchiveStories = state.hasUnseenArchiveStories { + mappedStoryState = ChatListNodeState.StoryState( + stats: EngineChatList.StoryStats( + totalCount: 1, unseenCount: hasUnseenArchiveStories ? 1 : 0 + ), + hasUnseenCloseFriends: false + ) + } + result.append(.GroupReferenceEntry(ChatListNodeEntry.GroupReferenceEntryData( index: .chatList(EngineChatList.Item.Index.ChatList(pinningIndex: pinningIndex, messageIndex: messageIndex)), presentationData: state.presentationData, groupId: groupReference.id, @@ -792,8 +851,9 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState editing: state.editing, unreadCount: groupReference.unreadCount, revealed: state.hiddenItemShouldBeTemporaryRevealed, - hiddenByDefault: hideArchivedFolderByDefault - )) + hiddenByDefault: hideArchivedFolderByDefault, + storyState: mappedStoryState + ))) if pinningIndex != 0 { pinningIndex -= 1 } diff --git a/submodules/ChatListUI/Sources/Node/ChatListNodeLocation.swift b/submodules/ChatListUI/Sources/Node/ChatListNodeLocation.swift index 6737b466dd..945081eedc 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNodeLocation.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNodeLocation.swift @@ -288,7 +288,8 @@ func chatListViewForLocation(chatListLocation: ChatListControllerLocation, locat topForumTopicItems: [], hasFailed: false, isContact: false, - autoremoveTimeout: nil + autoremoveTimeout: nil, + storyStats: nil )) } diff --git a/submodules/ContactListUI/Sources/ContactListNode.swift b/submodules/ContactListUI/Sources/ContactListNode.swift index 32b073ccaa..40e4e026c4 100644 --- a/submodules/ContactListUI/Sources/ContactListNode.swift +++ b/submodules/ContactListUI/Sources/ContactListNode.swift @@ -1236,8 +1236,8 @@ public final class ContactListNode: ASDisplayNode { return context.engine.data.get(EngineDataMap( view.entries.compactMap { entry -> EnginePeer.Id? in switch entry { - case let .MessageEntry(_, _, _, _, _, renderedPeer, _, _, _, _, _, _, _): - if let peer = renderedPeer.peer { + case let .MessageEntry(entryData): + if let peer = entryData.renderedPeer.peer { if let channel = peer as? TelegramChannel, case .group = channel.info { return peer.id } @@ -1252,8 +1252,8 @@ public final class ContactListNode: ASDisplayNode { var peers: [(EnginePeer, Int32)] = [] for entry in view.entries { switch entry { - case let .MessageEntry(_, _, _, _, _, renderedPeer, _, _, _, _, _, _, _): - if let peer = renderedPeer.peer { + case let .MessageEntry(entryData): + if let peer = entryData.renderedPeer.peer { if peer is TelegramGroup { peers.append((EnginePeer(peer), 0)) } else if let channel = peer as? TelegramChannel, case .group = channel.info { diff --git a/submodules/ContactListUI/Sources/ContactsControllerNode.swift b/submodules/ContactListUI/Sources/ContactsControllerNode.swift index 0b46c1cb32..6466c45b61 100644 --- a/submodules/ContactListUI/Sources/ContactsControllerNode.swift +++ b/submodules/ContactListUI/Sources/ContactsControllerNode.swift @@ -233,7 +233,10 @@ final class ContactsControllerNode: ASDisplayNode, UIGestureRecognizerDelegate { return self.contentScrollingEnded(listView: listView) } - self.storySubscriptionsDisposable = (self.context.engine.messages.storySubscriptions(isHidden: true) + self.contactListNode.storySubscriptions.set(.single(nil)) + self.storiesReady.set(.single(true)) + + /*self.storySubscriptionsDisposable = (self.context.engine.messages.storySubscriptions(isHidden: true) |> deliverOnMainQueue).start(next: { [weak self] storySubscriptions in guard let self else { return @@ -243,7 +246,7 @@ final class ContactsControllerNode: ASDisplayNode, UIGestureRecognizerDelegate { self.contactListNode.storySubscriptions.set(.single(storySubscriptions)) self.storiesReady.set(.single(true)) - }) + })*/ self.contactListNode.openStories = { [weak self] peer, sourceNode in guard let self else { diff --git a/submodules/InstantPageUI/Sources/InstantPagePeerReferenceNode.swift b/submodules/InstantPageUI/Sources/InstantPagePeerReferenceNode.swift index 8958c6aef3..af034e9f3f 100644 --- a/submodules/InstantPageUI/Sources/InstantPagePeerReferenceNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPagePeerReferenceNode.swift @@ -145,11 +145,11 @@ final class InstantPagePeerReferenceNode: ASDisplayNode, InstantPageNode { self.joinNode.addTarget(self, action: #selector(self.joinPressed), forControlEvents: .touchUpInside) let account = self.context.account - let context = self.context - let signal: Signal = actualizedPeer(postbox: account.postbox, network: account.network, peer: initialPeer._asPeer()) + let engine = context.engine + let signal: Signal = actualizedPeer(accountPeerId: account.peerId, postbox: account.postbox, network: account.network, peer: initialPeer._asPeer()) |> mapToSignal({ peer -> Signal in if let peer = peer as? TelegramChannel, let username = peer.addressName, peer.accessHash == nil { - return .single(.channel(peer)) |> then(context.engine.peers.resolvePeerByName(name: username) + return .single(.channel(peer)) |> then(engine.peers.resolvePeerByName(name: username) |> mapToSignal({ updatedPeer -> Signal in if let updatedPeer = updatedPeer { return .single(updatedPeer) diff --git a/submodules/PassportUI/Sources/SecureIdAuthController.swift b/submodules/PassportUI/Sources/SecureIdAuthController.swift index b71c8c949e..6acb72a6af 100644 --- a/submodules/PassportUI/Sources/SecureIdAuthController.swift +++ b/submodules/PassportUI/Sources/SecureIdAuthController.swift @@ -214,7 +214,7 @@ public final class SecureIdAuthController: ViewController, StandalonePresentable switch self.mode { case let .form(peerId, scope, publicKey, callbackUrl, _, _): - self.formDisposable = (combineLatest(requestSecureIdForm(postbox: context.account.postbox, network: context.account.network, peerId: peerId, scope: scope, publicKey: publicKey), secureIdConfiguration(postbox: context.account.postbox, network: context.account.network) |> castError(RequestSecureIdFormError.self)) + self.formDisposable = (combineLatest(requestSecureIdForm(accountPeerId: context.account.peerId, postbox: context.account.postbox, network: context.account.network, peerId: peerId, scope: scope, publicKey: publicKey), secureIdConfiguration(postbox: context.account.postbox, network: context.account.network) |> castError(RequestSecureIdFormError.self)) |> mapToSignal { form, configuration -> Signal in return context.engine.data.get( TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId), diff --git a/submodules/Postbox/Sources/ChatListView.swift b/submodules/Postbox/Sources/ChatListView.swift index 1cfc0d7920..c6d7d7ca68 100644 --- a/submodules/Postbox/Sources/ChatListView.swift +++ b/submodules/Postbox/Sources/ChatListView.swift @@ -111,85 +111,144 @@ public struct ChatListForumTopicData: Equatable { } public enum ChatListEntry: Comparable { - case MessageEntry(index: ChatListIndex, messages: [Message], readState: ChatListViewReadState?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: StoredPeerChatInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, summaryInfo: [ChatListEntryMessageTagSummaryKey: ChatListMessageTagSummaryInfo], forumTopicData: ChatListForumTopicData?, topForumTopics: [ChatListForumTopicData], hasFailed: Bool, isContact: Bool, autoremoveTimeout: Int32?) + public struct MessageEntryData: Equatable { + public var index: ChatListIndex + public var messages: [Message] + public var readState: ChatListViewReadState? + public var isRemovedFromTotalUnreadCount: Bool + public var embeddedInterfaceState: StoredPeerChatInterfaceState? + public var renderedPeer: RenderedPeer + public var presence: PeerPresence? + public var summaryInfo: [ChatListEntryMessageTagSummaryKey: ChatListMessageTagSummaryInfo] + public var forumTopicData: ChatListForumTopicData? + public var topForumTopics: [ChatListForumTopicData] + public var hasFailed: Bool + public var isContact: Bool + public var autoremoveTimeout: Int32? + public var storyStats: PeerStoryStats? + + public init( + index: ChatListIndex, + messages: [Message], + readState: ChatListViewReadState?, + isRemovedFromTotalUnreadCount: Bool, + embeddedInterfaceState: StoredPeerChatInterfaceState?, + renderedPeer: RenderedPeer, + presence: PeerPresence?, + summaryInfo: [ChatListEntryMessageTagSummaryKey: ChatListMessageTagSummaryInfo], + forumTopicData: ChatListForumTopicData?, + topForumTopics: [ChatListForumTopicData], + hasFailed: Bool, + isContact: Bool, + autoremoveTimeout: Int32?, + storyStats: PeerStoryStats? + ) { + self.index = index + self.messages = messages + self.readState = readState + self.isRemovedFromTotalUnreadCount = isRemovedFromTotalUnreadCount + self.embeddedInterfaceState = embeddedInterfaceState + self.renderedPeer = renderedPeer + self.presence = presence + self.summaryInfo = summaryInfo + self.forumTopicData = forumTopicData + self.topForumTopics = topForumTopics + self.hasFailed = hasFailed + self.isContact = isContact + self.autoremoveTimeout = autoremoveTimeout + self.storyStats = storyStats + } + + public static func ==(lhs: MessageEntryData, rhs: MessageEntryData) -> Bool { + if lhs.index != rhs.index { + return false + } + if lhs.readState != rhs.readState { + return false + } + if lhs.messages.count != rhs.messages.count { + return false + } + for i in 0 ..< lhs.messages.count { + if lhs.messages[i].stableVersion != rhs.messages[i].stableVersion { + return false + } + if lhs.messages[i].associatedStories != rhs.messages[i].associatedStories { + return false + } + } + if lhs.isRemovedFromTotalUnreadCount != rhs.isRemovedFromTotalUnreadCount { + return false + } + if let lhsEmbeddedState = lhs.embeddedInterfaceState, let rhsEmbeddedState = rhs.embeddedInterfaceState { + if lhsEmbeddedState != rhsEmbeddedState { + return false + } + } else if (lhs.embeddedInterfaceState != nil) != (rhs.embeddedInterfaceState != nil) { + return false + } + if lhs.renderedPeer != rhs.renderedPeer { + return false + } + if let lhsPresence = lhs.presence, let rhsPresence = rhs.presence { + if !lhsPresence.isEqual(to: rhsPresence) { + return false + } + } else if (lhs.presence != nil) != (rhs.presence != nil) { + return false + } + if lhs.summaryInfo != rhs.summaryInfo { + return false + } + if lhs.forumTopicData != rhs.forumTopicData { + return false + } + if lhs.topForumTopics != rhs.topForumTopics { + return false + } + if lhs.hasFailed != rhs.hasFailed { + return false + } + if lhs.isContact != rhs.isContact { + return false + } + if lhs.autoremoveTimeout != rhs.autoremoveTimeout { + return false + } + if lhs.storyStats != rhs.storyStats { + return false + } + + return true + } + } + + case MessageEntry(MessageEntryData) case HoleEntry(ChatListHole) public var index: ChatListIndex { switch self { - case let .MessageEntry(index, _, _, _, _, _, _, _, _, _, _, _, _): - return index - case let .HoleEntry(hole): - return ChatListIndex(pinningIndex: nil, messageIndex: hole.index) + case let .MessageEntry(entryData): + return entryData.index + case let .HoleEntry(hole): + return ChatListIndex(pinningIndex: nil, messageIndex: hole.index) } } public static func ==(lhs: ChatListEntry, rhs: ChatListEntry) -> Bool { switch lhs { - case let .MessageEntry(lhsIndex, lhsMessages, lhsReadState, lhsIsRemovedFromTotalUnreadCount, lhsEmbeddedState, lhsPeer, lhsPresence, lhsInfo, lhsForumTopicData, lhsTopForumTopics, lhsHasFailed, lhsIsContact, lhsAutoremoveTimeout): - switch rhs { - case let .MessageEntry(rhsIndex, rhsMessages, rhsReadState, rhsIsRemovedFromTotalUnreadCount, rhsEmbeddedState, rhsPeer, rhsPresence, rhsInfo, rhsForumTopicData, rhsTopForumTopics, rhsHasFailed, rhsIsContact, rhsAutoremoveTimeout): - if lhsIndex != rhsIndex { - return false - } - if lhsReadState != rhsReadState { - return false - } - if lhsMessages.count != rhsMessages.count { - return false - } - for i in 0 ..< lhsMessages.count { - if lhsMessages[i].stableVersion != rhsMessages[i].stableVersion { - return false - } - } - if lhsIsRemovedFromTotalUnreadCount != rhsIsRemovedFromTotalUnreadCount { - return false - } - if let lhsEmbeddedState = lhsEmbeddedState, let rhsEmbeddedState = rhsEmbeddedState { - if lhsEmbeddedState != rhsEmbeddedState { - return false - } - } else if (lhsEmbeddedState != nil) != (rhsEmbeddedState != nil) { - return false - } - if lhsPeer != rhsPeer { - return false - } - if let lhsPresence = lhsPresence, let rhsPresence = rhsPresence { - if !lhsPresence.isEqual(to: rhsPresence) { - return false - } - } else if (lhsPresence != nil) != (rhsPresence != nil) { - return false - } - if lhsInfo != rhsInfo { - return false - } - if lhsForumTopicData != rhsForumTopicData { - return false - } - if lhsTopForumTopics != rhsTopForumTopics { - return false - } - if lhsHasFailed != rhsHasFailed { - return false - } - if lhsIsContact != rhsIsContact { - return false - } - if lhsAutoremoveTimeout != rhsAutoremoveTimeout { - return false - } - return true - default: - return false - } - case let .HoleEntry(hole): - if case .HoleEntry(hole) = rhs { - return true - } else { - return false - } + case let .MessageEntry(entryData): + if case .MessageEntry(entryData) = rhs { + return true + } else { + return false + } + case let .HoleEntry(hole): + if case .HoleEntry(hole) = rhs { + return true + } else { + return false + } } } @@ -198,9 +257,89 @@ public enum ChatListEntry: Comparable { } } +public struct PeerStoryStats: Equatable { + public var totalCount: Int + public var unseenCount: Int + + public init(totalCount: Int, unseenCount: Int) { + self.totalCount = totalCount + self.unseenCount = unseenCount + } +} + +func fetchPeerStoryStats(postbox: PostboxImpl, peerId: PeerId) -> PeerStoryStats? { + guard let topItems = postbox.storyTopItemsTable.get(peerId: peerId) else { + return nil + } + if topItems.id == 0 { + return nil + } + guard let state = postbox.storyPeerStatesTable.get(key: .peer(peerId)) else { + return nil + } + if topItems.isExact { + let stats = postbox.storyItemsTable.getStats(peerId: peerId, maxSeenId: state.maxSeenId) + return PeerStoryStats(totalCount: stats.total, unseenCount: stats.unseen) + } else { + return PeerStoryStats(totalCount: 1, unseenCount: topItems.id > state.maxSeenId ? 1 : 0) + } +} + enum MutableChatListEntry: Equatable { + struct MessageEntryData { + var index: ChatListIndex + var messages: [Message] + var readState: ChatListViewReadState? + var notificationSettings: PeerNotificationSettings? + var isRemovedFromTotalUnreadCount: Bool + var embeddedInterfaceState: StoredPeerChatInterfaceState? + var renderedPeer: RenderedPeer + var presence: PeerPresence? + var tagSummaryInfo: [ChatListEntryMessageTagSummaryKey: ChatListMessageTagSummaryInfo] + var forumTopicData: ChatListForumTopicData? + var topForumTopics: [ChatListForumTopicData] + var hasFailedMessages: Bool + var isContact: Bool + var autoremoveTimeout: Int32? + var storyStats: PeerStoryStats? + + init( + index: ChatListIndex, + messages: [Message], + readState: ChatListViewReadState?, + notificationSettings: PeerNotificationSettings?, + isRemovedFromTotalUnreadCount: Bool, + embeddedInterfaceState: StoredPeerChatInterfaceState?, + renderedPeer: RenderedPeer, + presence: PeerPresence?, + tagSummaryInfo: [ChatListEntryMessageTagSummaryKey : ChatListMessageTagSummaryInfo], + forumTopicData: ChatListForumTopicData?, + topForumTopics: [ChatListForumTopicData], + hasFailedMessages: Bool, + isContact: Bool, + autoremoveTimeout: Int32?, + storyStats: PeerStoryStats? + ) { + self.index = index + self.messages = messages + self.readState = readState + self.notificationSettings = notificationSettings + self.isRemovedFromTotalUnreadCount = isRemovedFromTotalUnreadCount + self.embeddedInterfaceState = embeddedInterfaceState + self.renderedPeer = renderedPeer + self.presence = presence + self.tagSummaryInfo = tagSummaryInfo + self.forumTopicData = forumTopicData + self.topForumTopics = topForumTopics + self.hasFailedMessages = hasFailedMessages + self.isContact = isContact + self.autoremoveTimeout = autoremoveTimeout + self.storyStats = storyStats + } + } + case IntermediateMessageEntry(index: ChatListIndex, messageIndex: MessageIndex?) - case MessageEntry(index: ChatListIndex, messages: [Message], readState: ChatListViewReadState?, notificationSettings: PeerNotificationSettings?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: StoredPeerChatInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, tagSummaryInfo: [ChatListEntryMessageTagSummaryKey: ChatListMessageTagSummaryInfo], forumTopicData: ChatListForumTopicData?, topForumTopics: [ChatListForumTopicData], hasFailedMessages: Bool, isContact: Bool, autoremoveTimeout: Int32?) + case MessageEntry(MessageEntryData) case HoleEntry(ChatListHole) init(_ intermediateEntry: ChatListIntermediateEntry, cachedDataTable: CachedPeerDataTable, readStateTable: MessageHistoryReadStateTable, messageHistoryTable: MessageHistoryTable) { @@ -214,12 +353,12 @@ enum MutableChatListEntry: Equatable { var index: ChatListIndex { switch self { - case let .IntermediateMessageEntry(index, _): - return index - case let .MessageEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _): - return index - case let .HoleEntry(hole): - return ChatListIndex(pinningIndex: nil, messageIndex: hole.index) + case let .IntermediateMessageEntry(index, _): + return index + case let .MessageEntry(data): + return data.index + case let .HoleEntry(hole): + return ChatListIndex(pinningIndex: nil, messageIndex: hole.index) } } @@ -742,7 +881,25 @@ final class MutableChatListView { autoremoveTimeout = postbox.seedConfiguration.decodeAutoremoveTimeout(cachedData) } - return .MessageEntry(index: index, messages: renderedMessages, readState: readState, notificationSettings: notificationSettings, isRemovedFromTotalUnreadCount: false, embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId), renderedPeer: renderedPeer, presence: presence, tagSummaryInfo: [:], forumTopicData: forumTopicData, topForumTopics: topForumTopics, hasFailedMessages: postbox.messageHistoryFailedTable.contains(peerId: index.messageIndex.id.peerId), isContact: isContact, autoremoveTimeout: autoremoveTimeout) + let storyStats = fetchPeerStoryStats(postbox: postbox, peerId: index.messageIndex.id.peerId) + + return .MessageEntry(MutableChatListEntry.MessageEntryData( + index: index, + messages: renderedMessages, + readState: readState, + notificationSettings: notificationSettings, + isRemovedFromTotalUnreadCount: false, + embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId), + renderedPeer: renderedPeer, + presence: presence, + tagSummaryInfo: [:], + forumTopicData: forumTopicData, + topForumTopics: topForumTopics, + hasFailedMessages: postbox.messageHistoryFailedTable.contains(peerId: index.messageIndex.id.peerId), + isContact: isContact, + autoremoveTimeout: autoremoveTimeout, + storyStats: storyStats + )) default: return nil } @@ -771,8 +928,23 @@ public final class ChatListView { var entries: [ChatListEntry] = [] for entry in mutableView.sampledState.entries { switch entry { - case let .MessageEntry(index, messages, combinedReadState, _, isRemovedFromTotalUnreadCount, embeddedState, peer, peerPresence, summaryInfo, forumTopicData, topForumTopics, hasFailed, isContact, autoremoveTimeout): - entries.append(.MessageEntry(index: index, messages: messages, readState: combinedReadState, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: embeddedState, renderedPeer: peer, presence: peerPresence, summaryInfo: summaryInfo, forumTopicData: forumTopicData, topForumTopics: topForumTopics, hasFailed: hasFailed, isContact: isContact, autoremoveTimeout: autoremoveTimeout)) + case let .MessageEntry(entryData): + entries.append(.MessageEntry(ChatListEntry.MessageEntryData( + index: entryData.index, + messages: entryData.messages, + readState: entryData.readState, + isRemovedFromTotalUnreadCount: entryData.isRemovedFromTotalUnreadCount, + embeddedInterfaceState: entryData.embeddedInterfaceState, + renderedPeer: entryData.renderedPeer, + presence: entryData.presence, + summaryInfo: entryData.tagSummaryInfo, + forumTopicData: entryData.forumTopicData, + topForumTopics: entryData.topForumTopics, + hasFailed: entryData.hasFailedMessages, + isContact: entryData.isContact, + autoremoveTimeout: entryData.autoremoveTimeout, + storyStats: entryData.storyStats + ))) case let .HoleEntry(hole): entries.append(.HoleEntry(hole)) case .IntermediateMessageEntry: @@ -789,9 +961,24 @@ public final class ChatListView { var additionalItemEntries: [ChatListAdditionalItemEntry] = [] for entry in mutableView.additionalItemEntries { switch entry.entry { - case let .MessageEntry(index, messages, combinedReadState, _, isExcludedFromUnreadCount, embeddedState, peer, peerPresence, summaryInfo, forumTopicData, topForumTopics, hasFailed, isContact, autoremoveTimeout): + case let .MessageEntry(entryData): additionalItemEntries.append(ChatListAdditionalItemEntry( - entry: .MessageEntry(index: index, messages: messages, readState: combinedReadState, isRemovedFromTotalUnreadCount: isExcludedFromUnreadCount, embeddedInterfaceState: embeddedState, renderedPeer: peer, presence: peerPresence, summaryInfo: summaryInfo, forumTopicData: forumTopicData, topForumTopics: topForumTopics, hasFailed: hasFailed, isContact: isContact, autoremoveTimeout: autoremoveTimeout), + entry: .MessageEntry(ChatListEntry.MessageEntryData( + index: entryData.index, + messages: entryData.messages, + readState: entryData.readState, + isRemovedFromTotalUnreadCount: entryData.isRemovedFromTotalUnreadCount, + embeddedInterfaceState: entryData.embeddedInterfaceState, + renderedPeer: entryData.renderedPeer, + presence: entryData.presence, + summaryInfo: entryData.tagSummaryInfo, + forumTopicData: entryData.forumTopicData, + topForumTopics: entryData.topForumTopics, + hasFailed: entryData.hasFailedMessages, + isContact: entryData.isContact, + autoremoveTimeout: entryData.autoremoveTimeout, + storyStats: entryData.storyStats + )), info: entry.info )) case .HoleEntry: diff --git a/submodules/Postbox/Sources/ChatListViewState.swift b/submodules/Postbox/Sources/ChatListViewState.swift index fd2085712a..9299a2d8e5 100644 --- a/submodules/Postbox/Sources/ChatListViewState.swift +++ b/submodules/Postbox/Sources/ChatListViewState.swift @@ -533,8 +533,8 @@ private final class ChatListViewSpaceState { let entryPeer: Peer let entryNotificationsPeerId: PeerId switch entry { - case let .MessageEntry(_, _, _, _, _, _, renderedPeer, _, _, _, _, _, _, _): - if let peer = renderedPeer.peer { + case let .MessageEntry(entryData): + if let peer = entryData.renderedPeer.peer { entryPeer = peer entryNotificationsPeerId = peer.notificationSettingsPeerId ?? peer.id } else { @@ -648,13 +648,17 @@ private final class ChatListViewSpaceState { if self.orderedEntries.mutableScan({ entry in switch entry { - case let .MessageEntry(index, messages, readState, _, _, embeddedInterfaceState, renderedPeer, presence, tagSummaryInfo, forumTopicData, topForumTopics, hasFailedMessages, isContact, autoremoveTimeout): - if let peer = renderedPeer.peer { + case let .MessageEntry(entryData): + if let peer = entryData.renderedPeer.peer { let notificationsPeerId = peer.notificationSettingsPeerId ?? peer.id if let (_, updated) = transaction.currentUpdatedPeerNotificationSettings[notificationsPeerId] { let isRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettings, peer: peer, peerSettings: updated) - return .MessageEntry(index: index, messages: messages, readState: readState, notificationSettings: updated, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: embeddedInterfaceState, renderedPeer: renderedPeer, presence: presence, tagSummaryInfo: tagSummaryInfo, forumTopicData: forumTopicData, topForumTopics: topForumTopics, hasFailedMessages: hasFailedMessages, isContact: isContact, autoremoveTimeout: autoremoveTimeout) + var entryData = entryData + entryData.notificationSettings = updated + entryData.isRemovedFromTotalUnreadCount = isRemovedFromTotalUnreadCount + + return .MessageEntry(entryData) } else { return nil } @@ -672,24 +676,11 @@ private final class ChatListViewSpaceState { if !transaction.updatedFailedMessagePeerIds.isEmpty { if self.orderedEntries.mutableScan({ entry in switch entry { - case let .MessageEntry(index, messages, readState, notificationSettings, isRemovedFromTotalUnreadCount, embeddedInterfaceState, renderedPeer, presence, tagSummaryInfo, forumTopicData, topForumTopics, _, isContact, autoremoveTimeout): - if transaction.updatedFailedMessagePeerIds.contains(index.messageIndex.id.peerId) { - return .MessageEntry( - index: index, - messages: messages, - readState: readState, - notificationSettings: notificationSettings, - isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, - embeddedInterfaceState: embeddedInterfaceState, - renderedPeer: renderedPeer, - presence: presence, - tagSummaryInfo: tagSummaryInfo, - forumTopicData: forumTopicData, - topForumTopics: topForumTopics, - hasFailedMessages: postbox.messageHistoryFailedTable.contains(peerId: index.messageIndex.id.peerId), - isContact: isContact, - autoremoveTimeout: autoremoveTimeout - ) + case let .MessageEntry(entryData): + if transaction.updatedFailedMessagePeerIds.contains(entryData.index.messageIndex.id.peerId) { + var entryData = entryData + entryData.hasFailedMessages = postbox.messageHistoryFailedTable.contains(peerId: entryData.index.messageIndex.id.peerId) + return .MessageEntry(entryData) } else { return nil } @@ -704,8 +695,8 @@ private final class ChatListViewSpaceState { if !transaction.currentUpdatedPeers.isEmpty { if self.orderedEntries.mutableScan({ entry in switch entry { - case let .MessageEntry(index, messages, readState, notificationSettings, isRemovedFromTotalUnreadCount, embeddedInterfaceState, entryRenderedPeer, presence, tagSummaryInfo, forumTopicData, topForumTopics, hasFailedMessages, isContact, autoremoveTimeout): - var updatedMessages: [Message] = messages + case let .MessageEntry(entryData): + var updatedMessages: [Message] = entryData.messages var hasUpdatedMessages = false for i in 0 ..< updatedMessages.count { if let updatedMessage = updateMessagePeers(updatedMessages[i], updatedPeers: transaction.currentUpdatedPeers) { @@ -713,25 +704,14 @@ private final class ChatListViewSpaceState { hasUpdatedMessages = true } } - let renderedPeer = updatedRenderedPeer(postbox: postbox, renderedPeer: entryRenderedPeer, updatedPeers: transaction.currentUpdatedPeers) + let renderedPeer = updatedRenderedPeer(postbox: postbox, renderedPeer: entryData.renderedPeer, updatedPeers: transaction.currentUpdatedPeers) + + var entryData = entryData + entryData.messages = updatedMessages + entryData.renderedPeer = renderedPeer ?? entryData.renderedPeer if hasUpdatedMessages || renderedPeer != nil { - return .MessageEntry( - index: index, - messages: updatedMessages, - readState: readState, - notificationSettings: notificationSettings, - isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, - embeddedInterfaceState: embeddedInterfaceState, - renderedPeer: renderedPeer ?? entryRenderedPeer, - presence: presence, - tagSummaryInfo: tagSummaryInfo, - forumTopicData: forumTopicData, - topForumTopics: topForumTopics, - hasFailedMessages: hasFailedMessages, - isContact: isContact, - autoremoveTimeout: autoremoveTimeout - ) + return .MessageEntry(entryData) } else { return nil } @@ -746,28 +726,15 @@ private final class ChatListViewSpaceState { if !transaction.currentUpdatedPeerPresences.isEmpty { if self.orderedEntries.mutableScan({ entry in switch entry { - case let .MessageEntry(index, messages, readState, notificationSettings, isRemovedFromTotalUnreadCount, embeddedInterfaceState, entryRenderedPeer, _, tagSummaryInfo, forumTopicData, topForumTopics, hasFailedMessages, isContact, autoremoveTimeout): - var presencePeerId = entryRenderedPeer.peerId - if let peer = entryRenderedPeer.peers[entryRenderedPeer.peerId], let associatedPeerId = peer.associatedPeerId { + case let .MessageEntry(entryData): + var presencePeerId = entryData.renderedPeer.peerId + if let peer = entryData.renderedPeer.peers[entryData.renderedPeer.peerId], let associatedPeerId = peer.associatedPeerId { presencePeerId = associatedPeerId } if let presence = transaction.currentUpdatedPeerPresences[presencePeerId] { - return .MessageEntry( - index: index, - messages: messages, - readState: readState, - notificationSettings: notificationSettings, - isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, - embeddedInterfaceState: embeddedInterfaceState, - renderedPeer: entryRenderedPeer, - presence: presence, - tagSummaryInfo: tagSummaryInfo, - forumTopicData: forumTopicData, - topForumTopics: topForumTopics, - hasFailedMessages: hasFailedMessages, - isContact: isContact, - autoremoveTimeout: autoremoveTimeout - ) + var entryData = entryData + entryData.presence = presence + return .MessageEntry(entryData) } else { return nil } @@ -785,8 +752,8 @@ private final class ChatListViewSpaceState { let entryPeer: Peer let entryNotificationsPeerId: PeerId switch entry { - case let .MessageEntry(_, _, _, _, _, _, entryRenderedPeer, _, _, _, _, _, _, _): - if let peer = entryRenderedPeer.peer { + case let .MessageEntry(entryData): + if let peer = entryData.renderedPeer.peer { entryPeer = peer entryNotificationsPeerId = peer.notificationSettingsPeerId ?? peer.id } else { @@ -913,17 +880,17 @@ private final class ChatListViewSpaceState { } } - if !transaction.currentUpdatedMessageTagSummaries.isEmpty || !transaction.currentUpdatedMessageActionsSummaries.isEmpty || !transaction.updatedPeerThreadsSummaries.isEmpty || cachedPeerDataUpdated { + if !transaction.currentUpdatedMessageTagSummaries.isEmpty || !transaction.currentUpdatedMessageActionsSummaries.isEmpty || !transaction.updatedPeerThreadsSummaries.isEmpty || cachedPeerDataUpdated || !transaction.currentStoryTopItemEvents.isEmpty || !transaction.storyPeerStatesEvents.isEmpty { if self.orderedEntries.mutableScan({ entry in switch entry { - case let .MessageEntry(index, messages, readState, notificationSettings, isRemovedFromTotalUnreadCount, embeddedInterfaceState, entryRenderedPeer, presence, tagSummaryInfo, forumTopicData, topForumTopics, hasFailedMessages, isContact, autoremoveTimeout): - var updatedChatListMessageTagSummaryInfo: [ChatListEntryMessageTagSummaryKey: ChatListMessageTagSummaryInfo] = tagSummaryInfo + case let .MessageEntry(entryData): + var updatedChatListMessageTagSummaryInfo: [ChatListEntryMessageTagSummaryKey: ChatListMessageTagSummaryInfo] = entryData.tagSummaryInfo var didUpdateSummaryInfo = false for (key, component) in self.summaryComponents.components { var updatedTagSummaryCount: Int32? if let tagSummary = component.tagSummary { - let key = MessageHistoryTagsSummaryKey(tag: key.tag, peerId: index.messageIndex.id.peerId, threadId: nil, namespace: tagSummary.namespace) + let key = MessageHistoryTagsSummaryKey(tag: key.tag, peerId: entryData.index.messageIndex.id.peerId, threadId: nil, namespace: tagSummary.namespace) if let summary = transaction.currentUpdatedMessageTagSummaries[key] { updatedTagSummaryCount = summary.count } @@ -931,7 +898,7 @@ private final class ChatListViewSpaceState { var updatedActionsSummaryCount: Int32? if let actionsSummary = component.actionsSummary { - let key = PendingMessageActionsSummaryKey(type: key.actionType, peerId: index.messageIndex.id.peerId, namespace: actionsSummary.namespace) + let key = PendingMessageActionsSummaryKey(type: key.actionType, peerId: entryData.index.messageIndex.id.peerId, namespace: actionsSummary.namespace) if let count = transaction.currentUpdatedMessageActionsSummaries[key] { updatedActionsSummaryCount = count } @@ -946,8 +913,8 @@ private final class ChatListViewSpaceState { } } - var updatedReadState = readState - if let peer = postbox.peerTable.get(index.messageIndex.id.peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer) { + var updatedReadState = entryData.readState + if let peer = postbox.peerTable.get(entryData.index.messageIndex.id.peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer) { let summary = postbox.peerThreadsSummaryTable.get(peerId: peer.id) var count: Int32 = 0 @@ -961,40 +928,35 @@ private final class ChatListViewSpaceState { updatedReadState = ChatListViewReadState(state: CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 1, maxOutgoingReadId: 0, maxKnownId: 0, count: count, markedUnread: false))]), isMuted: isMuted) } else { - updatedReadState = postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId).flatMap { state in + updatedReadState = postbox.readStateTable.getCombinedState(entryData.index.messageIndex.id.peerId).flatMap { state in return ChatListViewReadState(state: state, isMuted: false) } } - if updatedReadState != readState { + if updatedReadState != entryData.readState { didUpdateSummaryInfo = true } var updatedAutoremoveTimeout: Int32? - if let cachedData = postbox.cachedPeerDataTable.get(index.messageIndex.id.peerId) { + if let cachedData = postbox.cachedPeerDataTable.get(entryData.index.messageIndex.id.peerId) { updatedAutoremoveTimeout = postbox.seedConfiguration.decodeAutoremoveTimeout(cachedData) - if updatedAutoremoveTimeout != autoremoveTimeout { + if updatedAutoremoveTimeout != entryData.autoremoveTimeout { didUpdateSummaryInfo = true } } + let storyStats = fetchPeerStoryStats(postbox: postbox, peerId: entryData.index.messageIndex.id.peerId) + if entryData.storyStats != storyStats { + didUpdateSummaryInfo = true + } + if didUpdateSummaryInfo { - return .MessageEntry( - index: index, - messages: messages, - readState: updatedReadState, - notificationSettings: notificationSettings, - isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, - embeddedInterfaceState: embeddedInterfaceState, - renderedPeer: entryRenderedPeer, - presence: presence, - tagSummaryInfo: updatedChatListMessageTagSummaryInfo, - forumTopicData: forumTopicData, - topForumTopics: topForumTopics, - hasFailedMessages: hasFailedMessages, - isContact: isContact, - autoremoveTimeout: updatedAutoremoveTimeout - ) + var entryData = entryData + entryData.readState = updatedReadState + entryData.tagSummaryInfo = updatedChatListMessageTagSummaryInfo + entryData.autoremoveTimeout = updatedAutoremoveTimeout + entryData.storyStats = storyStats + return .MessageEntry(entryData) } else { return nil } @@ -1127,8 +1089,8 @@ private extension MutableChatListEntry { switch self { case let .IntermediateMessageEntry(index, _): return index.messageIndex.id.peerId - case let .MessageEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _): - return index.messageIndex.id.peerId + case let .MessageEntry(entryData): + return entryData.index.messageIndex.id.peerId case .HoleEntry: return nil } @@ -1138,8 +1100,8 @@ private extension MutableChatListEntry { switch self { case let .IntermediateMessageEntry(index, _): return MutableChatListEntryIndex(index: index, isMessage: true) - case let .MessageEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _): - return MutableChatListEntryIndex(index: index, isMessage: true) + case let .MessageEntry(entryData): + return MutableChatListEntryIndex(index: entryData.index, isMessage: true) case let .HoleEntry(hole): return MutableChatListEntryIndex(index: ChatListIndex(pinningIndex: nil, messageIndex: hole.index), isMessage: false) } @@ -1149,8 +1111,8 @@ private extension MutableChatListEntry { switch self { case let .IntermediateMessageEntry(index, _): return .peer(index.messageIndex.id.peerId) - case let .MessageEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _): - return .peer(index.messageIndex.id.peerId) + case let .MessageEntry(entryData): + return .peer(entryData.index.messageIndex.id.peerId) case let .HoleEntry(hole): return .hole(hole.index) } @@ -1635,7 +1597,25 @@ struct ChatListViewState { autoremoveTimeout = postbox.seedConfiguration.decodeAutoremoveTimeout(cachedData) } - let updatedEntry: MutableChatListEntry = .MessageEntry(index: index, messages: renderedMessages, readState: readState, notificationSettings: notificationSettings, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId), renderedPeer: renderedPeer, presence: presence, tagSummaryInfo: tagSummaryInfo, forumTopicData: forumTopicData, topForumTopics: topForumTopics, hasFailedMessages: false, isContact: postbox.contactsTable.isContact(peerId: index.messageIndex.id.peerId), autoremoveTimeout: autoremoveTimeout) + let storyStats = fetchPeerStoryStats(postbox: postbox, peerId: index.messageIndex.id.peerId) + + let updatedEntry: MutableChatListEntry = .MessageEntry(MutableChatListEntry.MessageEntryData( + index: index, + messages: renderedMessages, + readState: readState, + notificationSettings: notificationSettings, + isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, + embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId), + renderedPeer: renderedPeer, + presence: presence, + tagSummaryInfo: tagSummaryInfo, + forumTopicData: forumTopicData, + topForumTopics: topForumTopics, + hasFailedMessages: false, + isContact: postbox.contactsTable.isContact(peerId: index.messageIndex.id.peerId), + autoremoveTimeout: autoremoveTimeout, + storyStats: storyStats + )) if directionIndex == 0 { self.stateBySpace[space]!.orderedEntries.setLowerOrAtAnchorAtArrayIndex(listIndex, to: updatedEntry) } else { diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index 4dfdfd6062..40bae9cd61 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -1291,12 +1291,12 @@ public final class Transaction { self.postbox!.setLocalStoryState(state: state) } - public func getPeerStoryState(peerId: PeerId) -> CodableEntry? { + public func getPeerStoryState(peerId: PeerId) -> StoredStoryPeerState? { assert(!self.disposed) return self.postbox!.getPeerStoryState(peerId: peerId) } - public func setPeerStoryState(peerId: PeerId, state: CodableEntry?) { + public func setPeerStoryState(peerId: PeerId, state: StoredStoryPeerState?) { assert(!self.disposed) self.postbox!.setPeerStoryState(peerId: peerId, state: state) } @@ -1306,6 +1306,11 @@ public final class Transaction { self.postbox!.setStoryItems(peerId: peerId, items: items) } + public func setStoryItemsInexactMaxId(peerId: PeerId, id: Int32) { + assert(!self.disposed) + self.postbox!.setStoryItemsInexactMaxId(peerId: peerId, id: id) + } + public func getStoryItems(peerId: PeerId) -> [StoryItemsTableEntry] { return self.postbox!.getStoryItems(peerId: peerId) } @@ -1557,9 +1562,11 @@ final class PostboxImpl { private var currentHiddenChatIds: [PeerId: Bag] = [:] private var currentUpdatedHiddenPeerIds: Bool = false - private var currentStoryStatesEvents: [StoryStatesTable.Event] = [] + private var currentStoryGeneralStatesEvents: [StoryGeneralStatesTable.Event] = [] + private var currentStoryPeerStatesEvents: [StoryPeerStatesTable.Event] = [] private var currentStorySubscriptionsEvents: [StorySubscriptionsTable.Event] = [] private var currentStoryItemsEvents: [StoryItemsTable.Event] = [] + private var currentStoryTopItemEvents: [StoryTopItemsTable.Event] = [] private var currentStoryEvents: [StoryTable.Event] = [] var hiddenChatIds: Set { @@ -1680,9 +1687,11 @@ final class PostboxImpl { let messageHistoryHoleIndexTable: MessageHistoryHoleIndexTable let groupMessageStatsTable: GroupMessageStatsTable let peerTimeoutPropertiesTable: PeerTimeoutPropertiesTable - let storyStatesTable: StoryStatesTable + let storyGeneralStatesTable: StoryGeneralStatesTable + let storyPeerStatesTable: StoryPeerStatesTable let storySubscriptionsTable: StorySubscriptionsTable let storyItemsTable: StoryItemsTable + let storyTopItemsTable: StoryTopItemsTable let storyTable: StoryTable //temporary @@ -1773,9 +1782,13 @@ final class PostboxImpl { self.noticeTable = NoticeTable(valueBox: self.valueBox, table: NoticeTable.tableSpec(43), useCaches: useCaches) self.deviceContactImportInfoTable = DeviceContactImportInfoTable(valueBox: self.valueBox, table: DeviceContactImportInfoTable.tableSpec(54), useCaches: useCaches) self.groupMessageStatsTable = GroupMessageStatsTable(valueBox: self.valueBox, table: GroupMessageStatsTable.tableSpec(58), useCaches: useCaches) - self.storyStatesTable = StoryStatesTable(valueBox: self.valueBox, table: StoryStatesTable.tableSpec(65), useCaches: useCaches) + + self.storyGeneralStatesTable = StoryGeneralStatesTable(valueBox: self.valueBox, table: StoryGeneralStatesTable.tableSpec(65), useCaches: useCaches) + self.storyPeerStatesTable = StoryPeerStatesTable(valueBox: self.valueBox, table: StoryPeerStatesTable.tableSpec(79), useCaches: useCaches) + self.storySubscriptionsTable = StorySubscriptionsTable(valueBox: self.valueBox, table: StorySubscriptionsTable.tableSpec(78), useCaches: useCaches) self.storyItemsTable = StoryItemsTable(valueBox: self.valueBox, table: StoryItemsTable.tableSpec(69), useCaches: useCaches) + self.storyTopItemsTable = StoryTopItemsTable(valueBox: self.valueBox, table: StoryTopItemsTable.tableSpec(80), useCaches: useCaches) self.storyTable = StoryTable(valueBox: self.valueBox, table: StoryTable.tableSpec(70), useCaches: useCaches) var tables: [Table] = [] @@ -1844,9 +1857,11 @@ final class PostboxImpl { tables.append(self.messageHistoryHoleIndexTable) tables.append(self.groupMessageStatsTable) tables.append(self.peerTimeoutPropertiesTable) - tables.append(self.storyStatesTable) + tables.append(self.storyGeneralStatesTable) + tables.append(self.storyPeerStatesTable) tables.append(self.storySubscriptionsTable) tables.append(self.storyItemsTable) + tables.append(self.storyTopItemsTable) tables.append(self.storyTable) self.tables = tables @@ -2188,7 +2203,7 @@ final class PostboxImpl { fileprivate func getAllStorySubscriptions(key: PostboxStorySubscriptionsKey) -> (state: CodableEntry?, peerIds: [PeerId]) { return ( - self.storyStatesTable.get(key: .subscriptions(key)), + self.storyGeneralStatesTable.get(key: .subscriptions(key)), self.storySubscriptionsTable.getAll(subscriptionsKey: key) ) } @@ -2198,41 +2213,48 @@ final class PostboxImpl { } fileprivate func replaceAllStorySubscriptions(key: PostboxStorySubscriptionsKey, state: CodableEntry?, peerIds: [PeerId]) { - self.storyStatesTable.set(key: .subscriptions(key), value: state, events: &self.currentStoryStatesEvents) + self.storyGeneralStatesTable.set(key: .subscriptions(key), value: state, events: &self.currentStoryGeneralStatesEvents) self.storySubscriptionsTable.replaceAll(subscriptionsKey: key, peerIds: peerIds, events: &self.currentStorySubscriptionsEvents) } fileprivate func getLocalStoryState() -> CodableEntry? { - return self.storyStatesTable.get(key: .local) + return self.storyGeneralStatesTable.get(key: .local) } fileprivate func getSubscriptionsStoriesState(key: PostboxStorySubscriptionsKey) -> CodableEntry? { - return self.storyStatesTable.get(key: .subscriptions(key)) + return self.storyGeneralStatesTable.get(key: .subscriptions(key)) } fileprivate func setSubscriptionsStoriesState(key: PostboxStorySubscriptionsKey, state: CodableEntry?) { - self.storyStatesTable.set(key: .subscriptions(key), value: state, events: &self.currentStoryStatesEvents) + self.storyGeneralStatesTable.set(key: .subscriptions(key), value: state, events: &self.currentStoryGeneralStatesEvents) } fileprivate func setLocalStoryState(state: CodableEntry?) { - self.storyStatesTable.set(key: .local, value: state, events: &self.currentStoryStatesEvents) + self.storyGeneralStatesTable.set(key: .local, value: state, events: &self.currentStoryGeneralStatesEvents) } - fileprivate func getPeerStoryState(peerId: PeerId) -> CodableEntry? { - return self.storyStatesTable.get(key: .peer(peerId)) + fileprivate func getPeerStoryState(peerId: PeerId) -> StoredStoryPeerState? { + return self.storyPeerStatesTable.get(key: .peer(peerId)) } - fileprivate func setPeerStoryState(peerId: PeerId, state: CodableEntry?) { - self.storyStatesTable.set(key: .peer(peerId), value: state, events: &self.currentStoryStatesEvents) + fileprivate func setPeerStoryState(peerId: PeerId, state: StoredStoryPeerState?) { + self.storyPeerStatesTable.set(key: .peer(peerId), value: state, events: &self.currentStoryPeerStatesEvents) } fileprivate func setStoryItems(peerId: PeerId, items: [StoryItemsTableEntry]) { - self.storyItemsTable.replace(peerId: peerId, entries: items, events: &self.currentStoryItemsEvents) + self.storyItemsTable.replace(peerId: peerId, entries: items, topItemTable: self.storyTopItemsTable, events: &self.currentStoryItemsEvents, topItemEvents: &self.currentStoryTopItemEvents) for item in items { self.storyTable.set(id: StoryId(peerId: peerId, id: item.id), value: item.value, events: &self.currentStoryEvents) } } + fileprivate func setStoryItemsInexactMaxId(peerId: PeerId, id: Int32) { + if let value = self.storyTopItemsTable.get(peerId: peerId), value.id >= id { + } else { + self.storyTopItemsTable.set(peerId: peerId, entry: StoryTopItemsTable.Entry(id: id, isExact: false), events: &self.currentStoryTopItemEvents) + } + } + fileprivate func getStoryItems(peerId: PeerId) -> [StoryItemsTableEntry] { return self.storyItemsTable.get(peerId: peerId) } @@ -2311,7 +2333,7 @@ final class PostboxImpl { let updatedPeerTimeoutAttributes = self.peerTimeoutPropertiesTable.hasUpdates - let transaction = PostboxTransaction(currentUpdatedState: self.currentUpdatedState, currentPeerHoleOperations: self.currentPeerHoleOperations, currentOperationsByPeerId: self.currentOperationsByPeerId, chatListOperations: self.currentChatListOperations, currentUpdatedChatListInclusions: self.currentUpdatedChatListInclusions, currentUpdatedPeers: self.currentUpdatedPeers, currentUpdatedPeerNotificationSettings: self.currentUpdatedPeerNotificationSettings, currentUpdatedPeerNotificationBehaviorTimestamps: self.currentUpdatedPeerNotificationBehaviorTimestamps, currentUpdatedCachedPeerData: self.currentUpdatedCachedPeerData, currentUpdatedPeerPresences: currentUpdatedPeerPresences, currentUpdatedPeerChatListEmbeddedStates: self.currentUpdatedPeerChatListEmbeddedStates, currentUpdatedTotalUnreadStates: self.currentUpdatedTotalUnreadStates, currentUpdatedTotalUnreadSummaries: self.currentUpdatedGroupTotalUnreadSummaries, alteredInitialPeerCombinedReadStates: alteredInitialPeerCombinedReadStates, currentPeerMergedOperationLogOperations: self.currentPeerMergedOperationLogOperations, currentTimestampBasedMessageAttributesOperations: self.currentTimestampBasedMessageAttributesOperations, unsentMessageOperations: self.currentUnsentOperations, updatedSynchronizePeerReadStateOperations: self.currentUpdatedSynchronizeReadStateOperations, currentUpdatedGroupSummarySynchronizeOperations: self.currentUpdatedGroupSummarySynchronizeOperations, currentPreferencesOperations: self.currentPreferencesOperations, currentOrderedItemListOperations: self.currentOrderedItemListOperations, currentItemCollectionItemsOperations: self.currentItemCollectionItemsOperations, currentItemCollectionInfosOperations: self.currentItemCollectionInfosOperations, currentUpdatedPeerChatStates: self.currentUpdatedPeerChatStates, currentGlobalTagsOperations: self.currentGlobalTagsOperations, currentLocalTagsOperations: self.currentLocalTagsOperations, updatedMedia: self.currentUpdatedMedia, replaceRemoteContactCount: self.currentReplaceRemoteContactCount, replaceContactPeerIds: self.currentReplacedContactPeerIds, currentPendingMessageActionsOperations: self.currentPendingMessageActionsOperations, currentUpdatedMessageActionsSummaries: self.currentUpdatedMessageActionsSummaries, currentUpdatedMessageTagSummaries: self.currentUpdatedMessageTagSummaries, currentInvalidateMessageTagSummaries: self.currentInvalidateMessageTagSummaries, currentUpdatedPendingPeerNotificationSettings: self.currentUpdatedPendingPeerNotificationSettings, replacedAdditionalChatListItems: self.currentReplacedAdditionalChatListItems, updatedNoticeEntryKeys: self.currentUpdatedNoticeEntryKeys, updatedCacheEntryKeys: self.currentUpdatedCacheEntryKeys, currentUpdatedMasterClientId: currentUpdatedMasterClientId, updatedFailedMessagePeerIds: self.messageHistoryFailedTable.updatedPeerIds, updatedFailedMessageIds: self.messageHistoryFailedTable.updatedMessageIds, updatedGlobalNotificationSettings: self.currentNeedsReindexUnreadCounters, updatedPeerTimeoutAttributes: updatedPeerTimeoutAttributes, updatedMessageThreadPeerIds: updatedMessageThreadPeerIds, updatedPeerThreadCombinedStates: self.currentUpdatedPeerThreadCombinedStates, updatedPeerThreadsSummaries: Set(alteredInitialPeerThreadsSummaries.keys), updatedPinnedThreads: self.currentUpdatedPinnedThreads, updatedHiddenPeerIds: self.currentUpdatedHiddenPeerIds, storyStatesEvents: self.currentStoryStatesEvents, storySubscriptionsEvents: self.currentStorySubscriptionsEvents, storyItemsEvents: self.currentStoryItemsEvents, storyEvents: self.currentStoryEvents) + let transaction = PostboxTransaction(currentUpdatedState: self.currentUpdatedState, currentPeerHoleOperations: self.currentPeerHoleOperations, currentOperationsByPeerId: self.currentOperationsByPeerId, chatListOperations: self.currentChatListOperations, currentUpdatedChatListInclusions: self.currentUpdatedChatListInclusions, currentUpdatedPeers: self.currentUpdatedPeers, currentUpdatedPeerNotificationSettings: self.currentUpdatedPeerNotificationSettings, currentUpdatedPeerNotificationBehaviorTimestamps: self.currentUpdatedPeerNotificationBehaviorTimestamps, currentUpdatedCachedPeerData: self.currentUpdatedCachedPeerData, currentUpdatedPeerPresences: currentUpdatedPeerPresences, currentUpdatedPeerChatListEmbeddedStates: self.currentUpdatedPeerChatListEmbeddedStates, currentUpdatedTotalUnreadStates: self.currentUpdatedTotalUnreadStates, currentUpdatedTotalUnreadSummaries: self.currentUpdatedGroupTotalUnreadSummaries, alteredInitialPeerCombinedReadStates: alteredInitialPeerCombinedReadStates, currentPeerMergedOperationLogOperations: self.currentPeerMergedOperationLogOperations, currentTimestampBasedMessageAttributesOperations: self.currentTimestampBasedMessageAttributesOperations, unsentMessageOperations: self.currentUnsentOperations, updatedSynchronizePeerReadStateOperations: self.currentUpdatedSynchronizeReadStateOperations, currentUpdatedGroupSummarySynchronizeOperations: self.currentUpdatedGroupSummarySynchronizeOperations, currentPreferencesOperations: self.currentPreferencesOperations, currentOrderedItemListOperations: self.currentOrderedItemListOperations, currentItemCollectionItemsOperations: self.currentItemCollectionItemsOperations, currentItemCollectionInfosOperations: self.currentItemCollectionInfosOperations, currentUpdatedPeerChatStates: self.currentUpdatedPeerChatStates, currentGlobalTagsOperations: self.currentGlobalTagsOperations, currentLocalTagsOperations: self.currentLocalTagsOperations, updatedMedia: self.currentUpdatedMedia, replaceRemoteContactCount: self.currentReplaceRemoteContactCount, replaceContactPeerIds: self.currentReplacedContactPeerIds, currentPendingMessageActionsOperations: self.currentPendingMessageActionsOperations, currentUpdatedMessageActionsSummaries: self.currentUpdatedMessageActionsSummaries, currentUpdatedMessageTagSummaries: self.currentUpdatedMessageTagSummaries, currentInvalidateMessageTagSummaries: self.currentInvalidateMessageTagSummaries, currentUpdatedPendingPeerNotificationSettings: self.currentUpdatedPendingPeerNotificationSettings, replacedAdditionalChatListItems: self.currentReplacedAdditionalChatListItems, updatedNoticeEntryKeys: self.currentUpdatedNoticeEntryKeys, updatedCacheEntryKeys: self.currentUpdatedCacheEntryKeys, currentUpdatedMasterClientId: currentUpdatedMasterClientId, updatedFailedMessagePeerIds: self.messageHistoryFailedTable.updatedPeerIds, updatedFailedMessageIds: self.messageHistoryFailedTable.updatedMessageIds, updatedGlobalNotificationSettings: self.currentNeedsReindexUnreadCounters, updatedPeerTimeoutAttributes: updatedPeerTimeoutAttributes, updatedMessageThreadPeerIds: updatedMessageThreadPeerIds, updatedPeerThreadCombinedStates: self.currentUpdatedPeerThreadCombinedStates, updatedPeerThreadsSummaries: Set(alteredInitialPeerThreadsSummaries.keys), updatedPinnedThreads: self.currentUpdatedPinnedThreads, updatedHiddenPeerIds: self.currentUpdatedHiddenPeerIds, storyGeneralStatesEvents: self.currentStoryGeneralStatesEvents, storyPeerStatesEvents: self.currentStoryPeerStatesEvents, storySubscriptionsEvents: self.currentStorySubscriptionsEvents, storyItemsEvents: self.currentStoryItemsEvents, currentStoryTopItemEvents: self.currentStoryTopItemEvents, storyEvents: self.currentStoryEvents) var updatedTransactionState: Int64? var updatedMasterClientId: Int64? if !transaction.isEmpty { @@ -2367,9 +2389,11 @@ final class PostboxImpl { self.currentUpdatedPinnedThreads.removeAll() self.currentUpdatedHiddenPeerIds = false self.currentNeedsReindexUnreadCounters = false - self.currentStoryStatesEvents.removeAll() + self.currentStoryGeneralStatesEvents.removeAll() + self.currentStoryPeerStatesEvents.removeAll() self.currentStorySubscriptionsEvents.removeAll() self.currentStoryItemsEvents.removeAll() + self.currentStoryTopItemEvents.removeAll() self.currentStoryEvents.removeAll() for table in self.tables { diff --git a/submodules/Postbox/Sources/PostboxTransaction.swift b/submodules/Postbox/Sources/PostboxTransaction.swift index cd556d9738..5729f31e3f 100644 --- a/submodules/Postbox/Sources/PostboxTransaction.swift +++ b/submodules/Postbox/Sources/PostboxTransaction.swift @@ -49,9 +49,11 @@ final class PostboxTransaction { let updatedPeerThreadsSummaries: Set let updatedPinnedThreads: Set let updatedHiddenPeerIds: Bool - let storyStatesEvents: [StoryStatesTable.Event] + let storyGeneralStatesEvents: [StoryGeneralStatesTable.Event] + let storyPeerStatesEvents: [StoryPeerStatesTable.Event] let storySubscriptionsEvents: [StorySubscriptionsTable.Event] let storyItemsEvents: [StoryItemsTable.Event] + let currentStoryTopItemEvents: [StoryTopItemsTable.Event] let storyEvents: [StoryTable.Event] var isEmpty: Bool { @@ -199,7 +201,10 @@ final class PostboxTransaction { if self.updatedHiddenPeerIds { return false } - if !self.storyStatesEvents.isEmpty { + if !self.storyGeneralStatesEvents.isEmpty { + return false + } + if !self.storyPeerStatesEvents.isEmpty { return false } if !self.storySubscriptionsEvents.isEmpty { @@ -208,13 +213,71 @@ final class PostboxTransaction { if !self.storyItemsEvents.isEmpty { return false } + if !self.currentStoryTopItemEvents.isEmpty { + return false + } if !self.storyEvents.isEmpty { return false } return true } - init(currentUpdatedState: PostboxCoding?, currentPeerHoleOperations: [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]] = [:], currentOperationsByPeerId: [PeerId: [MessageHistoryOperation]], chatListOperations: [PeerGroupId: [ChatListOperation]], currentUpdatedChatListInclusions: [PeerId: PeerChatListInclusion], currentUpdatedPeers: [PeerId: Peer], currentUpdatedPeerNotificationSettings: [PeerId: (PeerNotificationSettings?, PeerNotificationSettings)], currentUpdatedPeerNotificationBehaviorTimestamps: [PeerId: PeerNotificationSettingsBehaviorTimestamp], currentUpdatedCachedPeerData: [PeerId: CachedPeerData], currentUpdatedPeerPresences: [PeerId: PeerPresence], currentUpdatedPeerChatListEmbeddedStates: Set, currentUpdatedTotalUnreadStates: [PeerGroupId: ChatListTotalUnreadState], currentUpdatedTotalUnreadSummaries: [PeerGroupId: PeerGroupUnreadCountersCombinedSummary], alteredInitialPeerCombinedReadStates: [PeerId: CombinedPeerReadState], currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation], currentTimestampBasedMessageAttributesOperations: [TimestampBasedMessageAttributesOperation], unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation], updatedSynchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?], currentUpdatedGroupSummarySynchronizeOperations: [PeerGroupAndNamespace: Bool], currentPreferencesOperations: [PreferencesOperation], currentOrderedItemListOperations: [Int32: [OrderedItemListOperation]], currentItemCollectionItemsOperations: [ItemCollectionId: [ItemCollectionItemsOperation]], currentItemCollectionInfosOperations: [ItemCollectionInfosOperation], currentUpdatedPeerChatStates: Set, currentGlobalTagsOperations: [GlobalMessageHistoryTagsOperation], currentLocalTagsOperations: [IntermediateMessageHistoryLocalTagsOperation], updatedMedia: [MediaId: Media?], replaceRemoteContactCount: Int32?, replaceContactPeerIds: Set?, currentPendingMessageActionsOperations: [PendingMessageActionsOperation], currentUpdatedMessageActionsSummaries: [PendingMessageActionsSummaryKey: Int32], currentUpdatedMessageTagSummaries: [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], currentInvalidateMessageTagSummaries: [InvalidatedMessageHistoryTagsSummaryEntryOperation], currentUpdatedPendingPeerNotificationSettings: Set, replacedAdditionalChatListItems: [AdditionalChatListItem]?, updatedNoticeEntryKeys: Set, updatedCacheEntryKeys: Set, currentUpdatedMasterClientId: Int64?, updatedFailedMessagePeerIds: Set, updatedFailedMessageIds: Set, updatedGlobalNotificationSettings: Bool, updatedPeerTimeoutAttributes: Bool, updatedMessageThreadPeerIds: Set, updatedPeerThreadCombinedStates: Set, updatedPeerThreadsSummaries: Set, updatedPinnedThreads: Set, updatedHiddenPeerIds: Bool, storyStatesEvents: [StoryStatesTable.Event], storySubscriptionsEvents: [StorySubscriptionsTable.Event], storyItemsEvents: [StoryItemsTable.Event], storyEvents: [StoryTable.Event]) { + init( + currentUpdatedState: PostboxCoding?, + currentPeerHoleOperations: [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]] = [:], + currentOperationsByPeerId: [PeerId: [MessageHistoryOperation]], + chatListOperations: [PeerGroupId: [ChatListOperation]], + currentUpdatedChatListInclusions: [PeerId: PeerChatListInclusion], + currentUpdatedPeers: [PeerId: Peer], + currentUpdatedPeerNotificationSettings: [PeerId: (PeerNotificationSettings?, + PeerNotificationSettings)], + currentUpdatedPeerNotificationBehaviorTimestamps: [PeerId: PeerNotificationSettingsBehaviorTimestamp], + currentUpdatedCachedPeerData: [PeerId: CachedPeerData], + currentUpdatedPeerPresences: [PeerId: PeerPresence], + currentUpdatedPeerChatListEmbeddedStates: Set, + currentUpdatedTotalUnreadStates: [PeerGroupId: ChatListTotalUnreadState], + currentUpdatedTotalUnreadSummaries: [PeerGroupId: PeerGroupUnreadCountersCombinedSummary], + alteredInitialPeerCombinedReadStates: [PeerId: CombinedPeerReadState], + currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation], + currentTimestampBasedMessageAttributesOperations: [TimestampBasedMessageAttributesOperation], + unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation], + updatedSynchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?], + currentUpdatedGroupSummarySynchronizeOperations: [PeerGroupAndNamespace: Bool], + currentPreferencesOperations: [PreferencesOperation], + currentOrderedItemListOperations: [Int32: [OrderedItemListOperation]], + currentItemCollectionItemsOperations: [ItemCollectionId: [ItemCollectionItemsOperation]], + currentItemCollectionInfosOperations: [ItemCollectionInfosOperation], + currentUpdatedPeerChatStates: Set, + currentGlobalTagsOperations: [GlobalMessageHistoryTagsOperation], + currentLocalTagsOperations: [IntermediateMessageHistoryLocalTagsOperation], + updatedMedia: [MediaId: Media?], + replaceRemoteContactCount: Int32?, + replaceContactPeerIds: Set?, + currentPendingMessageActionsOperations: [PendingMessageActionsOperation], + currentUpdatedMessageActionsSummaries: [PendingMessageActionsSummaryKey: Int32], + currentUpdatedMessageTagSummaries: [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], + currentInvalidateMessageTagSummaries: [InvalidatedMessageHistoryTagsSummaryEntryOperation], + currentUpdatedPendingPeerNotificationSettings: Set, + replacedAdditionalChatListItems: [AdditionalChatListItem]?, + updatedNoticeEntryKeys: Set, + updatedCacheEntryKeys: Set, + currentUpdatedMasterClientId: Int64?, + updatedFailedMessagePeerIds: Set, + updatedFailedMessageIds: Set, + updatedGlobalNotificationSettings: Bool, + updatedPeerTimeoutAttributes: Bool, + updatedMessageThreadPeerIds: Set, + updatedPeerThreadCombinedStates: Set, + updatedPeerThreadsSummaries: Set, + updatedPinnedThreads: Set, + updatedHiddenPeerIds: Bool, + storyGeneralStatesEvents: [StoryGeneralStatesTable.Event], + storyPeerStatesEvents: [StoryPeerStatesTable.Event], + storySubscriptionsEvents: [StorySubscriptionsTable.Event], + storyItemsEvents: [StoryItemsTable.Event], + currentStoryTopItemEvents: [StoryTopItemsTable.Event], + storyEvents: [StoryTable.Event] + ) { self.currentUpdatedState = currentUpdatedState self.currentPeerHoleOperations = currentPeerHoleOperations self.currentOperationsByPeerId = currentOperationsByPeerId @@ -262,9 +325,11 @@ final class PostboxTransaction { self.updatedPeerThreadsSummaries = updatedPeerThreadsSummaries self.updatedPinnedThreads = updatedPinnedThreads self.updatedHiddenPeerIds = updatedHiddenPeerIds - self.storyStatesEvents = storyStatesEvents + self.storyGeneralStatesEvents = storyGeneralStatesEvents + self.storyPeerStatesEvents = storyPeerStatesEvents self.storySubscriptionsEvents = storySubscriptionsEvents self.storyItemsEvents = storyItemsEvents + self.currentStoryTopItemEvents = currentStoryTopItemEvents self.storyEvents = storyEvents } } diff --git a/submodules/Postbox/Sources/StoryItemsTable.swift b/submodules/Postbox/Sources/StoryItemsTable.swift index 098d401bfc..18e55d1c5c 100644 --- a/submodules/Postbox/Sources/StoryItemsTable.swift +++ b/submodules/Postbox/Sources/StoryItemsTable.swift @@ -32,6 +32,75 @@ public final class StoryItemsTableEntry: Equatable { } } +final class StoryTopItemsTable: Table { + struct Entry { + var id: Int32 + var isExact: Bool + } + + enum Event { + case replace(peerId: PeerId) + } + + private struct Key: Hashable { + var peerId: PeerId + } + + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: false) + } + + private let sharedKey = ValueBoxKey(length: 8 + 4) + + private func key(_ key: Key) -> ValueBoxKey { + self.sharedKey.setInt64(0, value: key.peerId.toInt64()) + return self.sharedKey + } + + public func get(peerId: PeerId) -> Entry? { + if let value = self.valueBox.get(self.table, key: self.key(Key(peerId: peerId))) { + let buffer = ReadBuffer(memoryBufferNoCopy: value) + var version: UInt8 = 0 + buffer.read(&version, offset: 0, length: 1) + if version != 100 { + return nil + } + var maxId: Int32 = 0 + buffer.read(&maxId, offset: 0, length: 4) + var isExact: Int8 = 0 + buffer.read(&isExact, offset: 0, length: 1) + + return Entry(id: maxId, isExact: isExact != 0) + } else { + return nil + } + } + + public func set(peerId: PeerId, entry: Entry?, events: inout [Event]) { + if let entry = entry { + let buffer = WriteBuffer() + + var version: UInt8 = 100 + buffer.write(&version, length: 1) + var maxId = entry.id + buffer.write(&maxId, length: 4) + var isExact: Int8 = entry.isExact ? 1 : 0 + buffer.write(&isExact, length: 1) + + self.valueBox.set(self.table, key: self.key(Key(peerId: peerId)), value: buffer.readBufferNoCopy()) + } else { + self.valueBox.remove(self.table, key: self.key(Key(peerId: peerId)), secure: true) + } + } + + override func clearMemoryCache() { + } + + override func beforeCommit() { + } +} + + final class StoryItemsTable: Table { enum Event { case replace(peerId: PeerId) @@ -66,6 +135,24 @@ final class StoryItemsTable: Table { return key.successor } + public func getStats(peerId: PeerId, maxSeenId: Int32) -> (total: Int, unseen: Int) { + var total = 0 + var unseen = 0 + + self.valueBox.range(self.table, start: self.lowerBound(peerId: peerId), end: self.upperBound(peerId: peerId), keys: { key in + let id = key.getInt32(8) + + total += 1 + if id > maxSeenId { + unseen += 1 + } + + return true + }, limit: 10000) + + return (total, unseen) + } + public func get(peerId: PeerId) -> [StoryItemsTableEntry] { var result: [StoryItemsTableEntry] = [] @@ -177,7 +264,7 @@ final class StoryItemsTable: Table { return minValue } - public func replace(peerId: PeerId, entries: [StoryItemsTableEntry], events: inout [Event]) { + public func replace(peerId: PeerId, entries: [StoryItemsTableEntry], topItemTable: StoryTopItemsTable, events: inout [Event], topItemEvents: inout [StoryTopItemsTable.Event]) { var previousKeys: [ValueBoxKey] = [] self.valueBox.range(self.table, start: self.lowerBound(peerId: peerId), end: self.upperBound(peerId: peerId), keys: { key in previousKeys.append(key) @@ -208,6 +295,8 @@ final class StoryItemsTable: Table { } events.append(.replace(peerId: peerId)) + + topItemTable.set(peerId: peerId, entry: StoryTopItemsTable.Entry(id: entries.last?.id ?? 0, isExact: true), events: &topItemEvents) } override func clearMemoryCache() { diff --git a/submodules/Postbox/Sources/StoryStatesTable.swift b/submodules/Postbox/Sources/StoryStatesTable.swift index 4f2307ec80..296f3a285f 100644 --- a/submodules/Postbox/Sources/StoryStatesTable.swift +++ b/submodules/Postbox/Sources/StoryStatesTable.swift @@ -1,6 +1,50 @@ import Foundation -final class StoryStatesTable: Table { +public struct StoredStoryPeerState: Equatable { + public var entry: CodableEntry + public var maxSeenId: Int32 + + public init(entry: CodableEntry, maxSeenId: Int32) { + self.entry = entry + self.maxSeenId = maxSeenId + } +} + +private extension StoredStoryPeerState { + init?(buffer: MemoryBuffer) { + let readBuffer = ReadBuffer(memoryBufferNoCopy: buffer) + var version: UInt8 = 0 + readBuffer.read(&version, offset: 0, length: 1) + if version != 100 { + return nil + } + + var entryLength: Int32 = 0 + readBuffer.read(&entryLength, offset: 0, length: 4) + if entryLength < 0 || readBuffer.offset + Int(entryLength) > readBuffer.length { + return nil + } + self.entry = CodableEntry(data: readBuffer.readData(length: Int(entryLength))) + + var maxSeenId: Int32 = 0 + readBuffer.read(&maxSeenId, offset: 0, length: 4) + self.maxSeenId = maxSeenId + } + + func serialize(buffer: WriteBuffer) { + var version: UInt8 = 100 + buffer.write(&version, length: 1) + + var entryLength: Int32 = Int32(self.entry.data.count) + buffer.write(&entryLength, length: 4) + buffer.write(self.entry.data) + + var maxSeenId: Int32 = self.maxSeenId + buffer.write(&maxSeenId, length: 4) + } +} + +final class StoryGeneralStatesTable: Table { enum Event { case set(Key) } @@ -8,7 +52,6 @@ final class StoryStatesTable: Table { enum Key: Hashable { case local case subscriptions(PostboxStorySubscriptionsKey) - case peer(PeerId) init?(key: ValueBoxKey) { switch key.getUInt8(0) { @@ -22,11 +65,9 @@ final class StoryStatesTable: Table { return nil } self = .subscriptions(subscriptionsKey) - case 2: - self = .peer(PeerId(key.getInt64(1))) default: assertionFailure() - self = .peer(PeerId(namespace: PeerId.Namespace._internalFromInt32Value(0), id: ._internalFromInt64Value(0))) + self = .subscriptions(.hidden) } } @@ -41,11 +82,6 @@ final class StoryStatesTable: Table { key.setUInt8(0, value: 1) key.setInt32(1, value: subscriptionsKey.rawValue) return key - case let .peer(peerId): - let key = ValueBoxKey(length: 1 + 8) - key.setUInt8(0, value: 2) - key.setInt64(1, value: peerId.toInt64()) - return key } } } @@ -54,8 +90,6 @@ final class StoryStatesTable: Table { return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: false) } - private let sharedKey = ValueBoxKey(length: 8 + 4) - func get(key: Key) -> CodableEntry? { return self.valueBox.get(self.table, key: key.asKey()).flatMap { CodableEntry(data: $0.makeData()) } } @@ -75,3 +109,58 @@ final class StoryStatesTable: Table { override func beforeCommit() { } } + +final class StoryPeerStatesTable: Table { + enum Event { + case set(Key) + } + + enum Key: Hashable { + case peer(PeerId) + + init?(key: ValueBoxKey) { + switch key.getUInt8(0) { + case 0: + self = .peer(PeerId(key.getInt64(1))) + default: + assertionFailure() + self = .peer(PeerId(namespace: PeerId.Namespace._internalFromInt32Value(0), id: ._internalFromInt64Value(0))) + } + } + + func asKey() -> ValueBoxKey { + switch self { + case let .peer(peerId): + let key = ValueBoxKey(length: 1 + 8) + key.setUInt8(0, value: 2) + key.setInt64(1, value: peerId.toInt64()) + return key + } + } + } + + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: false) + } + + func get(key: Key) -> StoredStoryPeerState? { + return self.valueBox.get(self.table, key: key.asKey()).flatMap { StoredStoryPeerState(buffer: $0) } + } + + func set(key: Key, value: StoredStoryPeerState?, events: inout [Event]) { + if let value = value { + let buffer = WriteBuffer() + value.serialize(buffer: buffer) + self.valueBox.set(self.table, key: key.asKey(), value: buffer.readBufferNoCopy()) + } else { + self.valueBox.remove(self.table, key: key.asKey(), secure: true) + } + events.append(.set(key)) + } + + override func clearMemoryCache() { + } + + override func beforeCommit() { + } +} diff --git a/submodules/Postbox/Sources/StoryStatesView.swift b/submodules/Postbox/Sources/StoryStatesView.swift index bb1f192542..9279d12b27 100644 --- a/submodules/Postbox/Sources/StoryStatesView.swift +++ b/submodules/Postbox/Sources/StoryStatesView.swift @@ -6,54 +6,60 @@ public enum PostboxStoryStatesKey: Hashable { case peer(PeerId) } -private extension PostboxStoryStatesKey { - init(tableKey: StoryStatesTable.Key) { - switch tableKey { - case .local: - self = .local - case let .subscriptions(key): - self = .subscriptions(key) - case let .peer(peerId): - self = .peer(peerId) - } - } - - var tableKey: StoryStatesTable.Key { - switch self { - case .local: - return .local - case let .subscriptions(key): - return .subscriptions(key) - case let .peer(peerId): - return .peer(peerId) - } - } -} - final class MutableStoryStatesView: MutablePostboxView { let key: PostboxStoryStatesKey var value: CodableEntry? init(postbox: PostboxImpl, key: PostboxStoryStatesKey) { self.key = key - self.value = postbox.storyStatesTable.get(key: key.tableKey) + + let _ = self.refreshDueToExternalTransaction(postbox: postbox) } func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool { var updated = false - if !transaction.storyStatesEvents.isEmpty { - let tableKey = self.key.tableKey - loop: for event in transaction.storyStatesEvents { - switch event { - case .set(tableKey): - let value = postbox.storyStatesTable.get(key: self.key.tableKey) - if value != self.value { - self.value = value - updated = true + switch self.key { + case .local, .subscriptions: + let storyGeneralKey: StoryGeneralStatesTable.Key + switch self.key { + case .local: + storyGeneralKey = .local + case let .subscriptions(value): + storyGeneralKey = .subscriptions(value) + case .peer: + assertionFailure() + return false + } + if !transaction.storyGeneralStatesEvents.isEmpty { + loop: for event in transaction.storyGeneralStatesEvents { + switch event { + case .set(storyGeneralKey): + let value = postbox.storyGeneralStatesTable.get(key: storyGeneralKey) + if value != self.value { + self.value = value + updated = true + } + break loop + default: + break + } + } + } + case let .peer(peerId): + let storyPeerKey: StoryPeerStatesTable.Key = .peer(peerId) + if !transaction.storyPeerStatesEvents.isEmpty { + loop: for event in transaction.storyPeerStatesEvents { + switch event { + case .set(storyPeerKey): + let value = postbox.storyPeerStatesTable.get(key: storyPeerKey)?.entry + if value != self.value { + self.value = value + updated = true + } + break loop + default: + break } - break loop - default: - break } } } @@ -62,7 +68,16 @@ final class MutableStoryStatesView: MutablePostboxView { } func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { - let value = postbox.storyStatesTable.get(key: self.key.tableKey) + let value: CodableEntry? + switch self.key { + case .local: + value = postbox.storyGeneralStatesTable.get(key: .local) + case let .subscriptions(valueKey): + value = postbox.storyGeneralStatesTable.get(key: .subscriptions(valueKey)) + case let .peer(peerId): + value = postbox.storyPeerStatesTable.get(key: .peer(peerId))?.entry + } + if value != self.value { self.value = value return true diff --git a/submodules/ShareController/Sources/ShareController.swift b/submodules/ShareController/Sources/ShareController.swift index 8a27687856..562fd75d7b 100644 --- a/submodules/ShareController/Sources/ShareController.swift +++ b/submodules/ShareController/Sources/ShareController.swift @@ -1534,9 +1534,9 @@ public final class ShareController: ViewController { var peers: [EngineRenderedPeer] = [] for entry in view.0.entries.reversed() { switch entry { - case let .MessageEntry(_, _, _, _, _, renderedPeer, _, _, _, _, _, _, _): - if let peer = renderedPeer.peers[renderedPeer.peerId], peer.id != accountPeer.id, canSendMessagesToPeer(peer) { - peers.append(EngineRenderedPeer(renderedPeer)) + case let .MessageEntry(entryData): + if let peer = entryData.renderedPeer.peers[entryData.renderedPeer.peerId], peer.id != accountPeer.id, canSendMessagesToPeer(peer) { + peers.append(EngineRenderedPeer(entryData.renderedPeer)) } default: break diff --git a/submodules/ShareController/Sources/ShareControllerNode.swift b/submodules/ShareController/Sources/ShareControllerNode.swift index 07ca59222c..78faf245c5 100644 --- a/submodules/ShareController/Sources/ShareControllerNode.swift +++ b/submodules/ShareController/Sources/ShareControllerNode.swift @@ -1363,7 +1363,8 @@ private func threadList(context: AccountContext, peerId: EnginePeer.Id) -> Signa topForumTopicItems: [], hasFailed: false, isContact: false, - autoremoveTimeout: nil + autoremoveTimeout: nil, + storyStats: nil )) } diff --git a/submodules/StatisticsUI/Sources/GroupStatsController.swift b/submodules/StatisticsUI/Sources/GroupStatsController.swift index 600df725fb..32b9a45569 100644 --- a/submodules/StatisticsUI/Sources/GroupStatsController.swift +++ b/submodules/StatisticsUI/Sources/GroupStatsController.swift @@ -727,7 +727,7 @@ public func groupStatsController(context: AccountContext, updatedPresentationDat actionsDisposable.add(context.account.viewTracker.peerView(peerId, updateData: true).start()) - let statsContext = GroupStatsContext(postbox: context.account.postbox, network: context.account.network, datacenterId: datacenterId, peerId: peerId) + let statsContext = GroupStatsContext(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, datacenterId: datacenterId, peerId: peerId) let dataSignal: Signal = statsContext.state |> map { state in return state.stats diff --git a/submodules/TelegramCore/Sources/Account/Account.swift b/submodules/TelegramCore/Sources/Account/Account.swift index 18fb980468..83d34f3138 100644 --- a/submodules/TelegramCore/Sources/Account/Account.swift +++ b/submodules/TelegramCore/Sources/Account/Account.swift @@ -1002,7 +1002,7 @@ public class Account { self.hiddenStorySubscriptionsContext = nil } - self.callSessionManager = CallSessionManager(postbox: postbox, network: network, maxLayer: networkArguments.voipMaxLayer, versions: networkArguments.voipVersions, addUpdates: { [weak self] updates in + self.callSessionManager = CallSessionManager(postbox: postbox, network: network, accountPeerId: peerId, maxLayer: networkArguments.voipMaxLayer, versions: networkArguments.voipVersions, addUpdates: { [weak self] updates in self?.stateManager?.addUpdates(updates) }) diff --git a/submodules/TelegramCore/Sources/ForumChannels.swift b/submodules/TelegramCore/Sources/ForumChannels.swift index de782b320a..4f10dd7311 100644 --- a/submodules/TelegramCore/Sources/ForumChannels.swift +++ b/submodules/TelegramCore/Sources/ForumChannels.swift @@ -240,7 +240,7 @@ func _internal_createForumChannelTopic(account: Account, peerId: PeerId, title: } |> castError(CreateForumChannelTopicError.self) |> mapToSignal { _ -> Signal in - return resolveForumThreads(postbox: account.postbox, network: account.network, ids: [MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId))]) + return resolveForumThreads(accountPeerId: account.peerId, postbox: account.postbox, network: account.network, ids: [MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId))]) |> castError(CreateForumChannelTopicError.self) |> map { _ -> Int64 in return topicId @@ -265,7 +265,7 @@ func _internal_fetchForumChannelTopic(account: Account, peerId: PeerId, threadId if let info = info { return .single(info) } else { - return resolveForumThreads(postbox: account.postbox, network: account.network, ids: [MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId))]) + return resolveForumThreads(accountPeerId: account.peerId, postbox: account.postbox, network: account.network, ids: [MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId))]) |> mapToSignal { _ -> Signal in return account.postbox.transaction { transaction -> EngineMessageHistoryThread.Info? in if let data = transaction.getMessageHistoryThreadInfo(peerId: peerId, threadId: threadId)?.data.get(MessageHistoryThreadData.self) { @@ -706,23 +706,8 @@ func _internal_requestMessageHistoryThreads(accountPeerId: PeerId, postbox: Post func applyLoadMessageHistoryThreadsResults(accountPeerId: PeerId, transaction: Transaction, results: [LoadMessageHistoryThreadsResult]) { for result in results { - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - for chat in result.chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers.append(groupOrChannel) - } - } - for user in result.users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - - updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: result.chats, users: result.users) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) let _ = InternalAccountState.addMessages(transaction: transaction, messages: result.messages, location: .Random) diff --git a/submodules/TelegramCore/Sources/LoadedPeer.swift b/submodules/TelegramCore/Sources/LoadedPeer.swift index 14f8603421..b5b98dd745 100644 --- a/submodules/TelegramCore/Sources/LoadedPeer.swift +++ b/submodules/TelegramCore/Sources/LoadedPeer.swift @@ -3,7 +3,7 @@ import TelegramApi import SwiftSignalKit -public func actualizedPeer(postbox: Postbox, network: Network, peer: Peer) -> Signal { +public func actualizedPeer(accountPeerId: PeerId, postbox: Postbox, network: Network, peer: Peer) -> Signal { return postbox.transaction { transaction -> Signal in var signal: Signal var actualizeChannel: Api.InputChannel? @@ -30,31 +30,30 @@ public func actualizedPeer(postbox: Postbox, network: Network, peer: Peer) -> Si return .single(nil) } |> mapToSignal { result -> Signal in - var remotePeer: Peer? - if let result = result { - let chats: [Api.Chat] - switch result { + return postbox.transaction { transaction -> Signal in + var parsedPeers: AccumulatedPeers? + if let result = result { + let chats: [Api.Chat] + switch result { case let .chats(apiChats): chats = apiChats case let .chatsSlice(_, apiChats): chats = apiChats - } - for chat in chats { - if let parsedPeer = parseTelegramGroupOrChannel(chat: chat), parsedPeer.id == peer.id { - remotePeer = parsedPeer + } + let parsedPeersValue = AccumulatedPeers(transaction: transaction, chats: chats, users: []) + if parsedPeersValue.allIds.contains(peer.id) { + parsedPeers = parsedPeersValue } } - } - if let remotePeer = remotePeer { - return postbox.transaction { transaction -> Peer in - updatePeers(transaction: transaction, peers: [remotePeer], update: { _, updated in - return updated - }) - return remotePeer + if let parsedPeers = parsedPeers { + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) + if let peer = transaction.getPeer(peer.id) { + return .single(peer) + } } - } else { return .complete() } + |> switchToLatest } signal = signal |> then(remote) } diff --git a/submodules/TelegramCore/Sources/LoadedPeerFromMessage.swift b/submodules/TelegramCore/Sources/LoadedPeerFromMessage.swift index ae8c28826a..953f54fd1f 100644 --- a/submodules/TelegramCore/Sources/LoadedPeerFromMessage.swift +++ b/submodules/TelegramCore/Sources/LoadedPeerFromMessage.swift @@ -5,6 +5,7 @@ import SwiftSignalKit public func loadedPeerFromMessage(account: Account, peerId: PeerId, messageId: MessageId) -> Signal { + let accountPeerId = account.peerId return account.postbox.transaction { transaction -> Signal in if let peer = transaction.getPeer(peerId) { if let user = peer as? TelegramUser { @@ -34,27 +35,21 @@ public func loadedPeerFromMessage(account: Account, peerId: PeerId, messageId: M if let result = result { let apiUsers: [Api.User] switch result { - case let .messages(_, _, users): - apiUsers = users - case let .messagesSlice(_, _, _, _, _, _, users): - apiUsers = users - case let .channelMessages(_, _, _, _, _, _, _, users): - apiUsers = users - case .messagesNotModified: - apiUsers = [] + case let .messages(_, _, users): + apiUsers = users + case let .messagesSlice(_, _, _, _, _, _, users): + apiUsers = users + case let .channelMessages(_, _, _, _, _, _, _, users): + apiUsers = users + case .messagesNotModified: + apiUsers = [] } - for user in apiUsers { - let telegramUser = TelegramUser(user: user) - if telegramUser.id == peerId, let accessHash = telegramUser.accessHash, accessHash.value != 0 { - updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: [telegramUser.id: user]) - - updatePeers(transaction: transaction, peers: [telegramUser], update: { _, updated -> Peer in - return updated - }) - - return telegramUser - } + let parsedPeers = AccumulatedPeers(users: apiUsers) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) + + if let peer = transaction.getPeer(peerId) { + return peer } } return nil diff --git a/submodules/TelegramCore/Sources/Network/FetchedMediaResource.swift b/submodules/TelegramCore/Sources/Network/FetchedMediaResource.swift index 0d5c2a058c..ddaa422d09 100644 --- a/submodules/TelegramCore/Sources/Network/FetchedMediaResource.swift +++ b/submodules/TelegramCore/Sources/Network/FetchedMediaResource.swift @@ -421,7 +421,7 @@ final class MediaReferenceRevalidationContext { } } - func message(postbox: Postbox, network: Network, background: Bool, message: MessageReference) -> Signal { + func message(accountPeerId: PeerId, postbox: Postbox, network: Network, background: Bool, message: MessageReference) -> Signal { return self.genericItem(key: .message(message: message), background: background, request: { next, error in let source: Signal if background { @@ -432,7 +432,7 @@ final class MediaReferenceRevalidationContext { } let signal = source |> mapToSignal { source -> Signal in - return fetchRemoteMessage(postbox: postbox, source: source, message: message) + return fetchRemoteMessage(accountPeerId: accountPeerId, postbox: postbox, source: source, message: message) } return signal.start(next: { value in if let value = value { @@ -544,9 +544,9 @@ final class MediaReferenceRevalidationContext { } } - func peer(postbox: Postbox, network: Network, background: Bool, peer: PeerReference) -> Signal { + func peer(accountPeerId: PeerId, postbox: Postbox, network: Network, background: Bool, peer: PeerReference) -> Signal { return self.genericItem(key: .peer(peer: peer), background: background, request: { next, error in - return (_internal_updatedRemotePeer(postbox: postbox, network: network, peer: peer) + return (_internal_updatedRemotePeer(accountPeerId: accountPeerId, postbox: postbox, network: network, peer: peer) |> mapError { _ -> RevalidateMediaReferenceError in return .generic }).start(next: { value in @@ -640,9 +640,9 @@ final class MediaReferenceRevalidationContext { } } - func attachBot(postbox: Postbox, network: Network, background: Bool, peer: PeerReference) -> Signal { + func attachBot(accountPeerId: PeerId, postbox: Postbox, network: Network, background: Bool, peer: PeerReference) -> Signal { return self.genericItem(key: .attachBot(peer: peer), background: background, request: { next, error in - return (_internal_getAttachMenuBot(postbox: postbox, network: network, botId: peer.id, cached: false) + return (_internal_getAttachMenuBot(accountPeerId: accountPeerId, postbox: postbox, network: network, botId: peer.id, cached: false) |> mapError { _ -> RevalidateMediaReferenceError in return .generic }).start(next: { value in @@ -751,7 +751,7 @@ func revalidateMediaResourceReference(accountPeerId: PeerId, postbox: Postbox, n case let .media(media, _): switch media { case let .message(message, previousMedia): - return revalidationContext.message(postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation, message: message) + return revalidationContext.message(accountPeerId: accountPeerId, postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation, message: message) |> mapToSignal { message -> Signal in for media in message.media { if let updatedResource = findUpdatedMediaResource(media: media, previousMedia: previousMedia, resource: resource) { @@ -822,7 +822,7 @@ func revalidateMediaResourceReference(accountPeerId: PeerId, postbox: Postbox, n return .fail(.generic) } case let .attachBot(peer, _): - return revalidationContext.attachBot(postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation, peer: peer) + return revalidationContext.attachBot(accountPeerId: accountPeerId, postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation, peer: peer) |> mapToSignal { attachBot -> Signal in for (_, icon) in attachBot.icons { if let updatedResource = findUpdatedMediaResource(media: icon, previousMedia: nil, resource: resource) { @@ -896,7 +896,7 @@ func revalidateMediaResourceReference(accountPeerId: PeerId, postbox: Postbox, n } } case let .avatar(peer, _): - return revalidationContext.peer(postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation, peer: peer) + return revalidationContext.peer(accountPeerId: accountPeerId, postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation, peer: peer) |> mapToSignal { updatedPeer -> Signal in for representation in updatedPeer.profileImageRepresentations { if let updatedResource = representation.resource as? CloudPeerPhotoSizeMediaResource, let previousResource = resource as? CloudPeerPhotoSizeMediaResource { @@ -921,7 +921,7 @@ func revalidateMediaResourceReference(accountPeerId: PeerId, postbox: Postbox, n return .fail(.generic) } case let .messageAuthorAvatar(message, _): - return revalidationContext.message(postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation, message: message) + return revalidationContext.message(accountPeerId: accountPeerId, postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation, message: message) |> mapToSignal { updatedMessage -> Signal in guard let author = updatedMessage.author, let authorReference = PeerReference(author) else { return .fail(.generic) diff --git a/submodules/TelegramCore/Sources/PeerStatistics.swift b/submodules/TelegramCore/Sources/PeerStatistics.swift index 97e85702a1..8734538939 100644 --- a/submodules/TelegramCore/Sources/PeerStatistics.swift +++ b/submodules/TelegramCore/Sources/PeerStatistics.swift @@ -666,7 +666,7 @@ public struct GroupStatsContextState: Equatable { public var stats: GroupStats? } -private func requestGroupStats(postbox: Postbox, network: Network, datacenterId: Int32, peerId: PeerId, dark: Bool = false) -> Signal { +private func requestGroupStats(accountPeerId: PeerId, postbox: Postbox, network: Network, datacenterId: Int32, peerId: PeerId, dark: Bool = false) -> Signal { return postbox.transaction { transaction -> Peer? in return transaction.getPeer(peerId) } |> mapToSignal { peer -> Signal in @@ -694,13 +694,7 @@ private func requestGroupStats(postbox: Postbox, network: Network, datacenterId: |> mapToSignal { result -> Signal in return postbox.transaction { transaction -> GroupStats? in if case let .megagroupStats(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, users) = result { - var parsedUsers: [Peer] = [] - for user in users { - parsedUsers.append(TelegramUser(user: user)) - } - updatePeers(transaction: transaction, peers: parsedUsers, update: { existing, updated in - return existing ?? updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users)) } return GroupStats(apiMegagroupStats: result) } @@ -713,6 +707,7 @@ private func requestGroupStats(postbox: Postbox, network: Network, datacenterId: private final class GroupStatsContextImpl { private let postbox: Postbox private let network: Network + private let accountPeerId: PeerId private let datacenterId: Int32 private let peerId: PeerId @@ -731,11 +726,12 @@ private final class GroupStatsContextImpl { private let disposable = MetaDisposable() private let disposables = DisposableDict() - init(postbox: Postbox, network: Network, datacenterId: Int32, peerId: PeerId) { + init(postbox: Postbox, network: Network, accountPeerId: PeerId, datacenterId: Int32, peerId: PeerId) { assert(Queue.mainQueue().isCurrent()) self.postbox = postbox self.network = network + self.accountPeerId = accountPeerId self.datacenterId = datacenterId self.peerId = peerId self._state = GroupStatsContextState(stats: nil) @@ -753,7 +749,7 @@ private final class GroupStatsContextImpl { private func load() { assert(Queue.mainQueue().isCurrent()) - self.disposable.set((requestGroupStats(postbox: self.postbox, network: self.network, datacenterId: self.datacenterId, peerId: self.peerId) + self.disposable.set((requestGroupStats(accountPeerId: self.accountPeerId, postbox: self.postbox, network: self.network, datacenterId: self.datacenterId, peerId: self.peerId) |> deliverOnMainQueue).start(next: { [weak self] stats in if let strongSelf = self { strongSelf._state = GroupStatsContextState(stats: stats) @@ -906,9 +902,9 @@ public final class GroupStatsContext { } } - public init(postbox: Postbox, network: Network, datacenterId: Int32, peerId: PeerId) { + public init(postbox: Postbox, network: Network, accountPeerId: PeerId, datacenterId: Int32, peerId: PeerId) { self.impl = QueueLocalObject(queue: Queue.mainQueue(), generate: { - return GroupStatsContextImpl(postbox: postbox, network: network, datacenterId: datacenterId, peerId: peerId) + return GroupStatsContextImpl(postbox: postbox, network: network, accountPeerId: accountPeerId, datacenterId: datacenterId, peerId: peerId) }) } diff --git a/submodules/TelegramCore/Sources/PendingMessages/RequestEditMessage.swift b/submodules/TelegramCore/Sources/PendingMessages/RequestEditMessage.swift index 29edcc91af..3ddeca6417 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/RequestEditMessage.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/RequestEditMessage.swift @@ -204,18 +204,8 @@ private func requestEditMessageInternal(accountPeerId: PeerId, postbox: Postbox, for update in updates { switch update { case .updateEditMessage(let message, _, _), .updateNewMessage(let message, _, _), .updateEditChannelMessage(let message, _, _), .updateNewChannelMessage(let message, _, _): - var peers: [Peer] = [] - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers.append(groupOrChannel) - } - } - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - } - - updatePeers(transaction: transaction, peers: peers, update: { _, updated in updated }) + let peers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: peers) if let message = StoreMessage(apiMessage: message, peerIsForum: peer.isForum), case let .Id(id) = message.id { transaction.updateMessage(id, update: { previousMessage in diff --git a/submodules/TelegramCore/Sources/SecretChats/SetSecretChatMessageAutoremoveTimeoutInteractively.swift b/submodules/TelegramCore/Sources/SecretChats/SetSecretChatMessageAutoremoveTimeoutInteractively.swift index 6c977a050a..efd313df90 100644 --- a/submodules/TelegramCore/Sources/SecretChats/SetSecretChatMessageAutoremoveTimeoutInteractively.swift +++ b/submodules/TelegramCore/Sources/SecretChats/SetSecretChatMessageAutoremoveTimeoutInteractively.swift @@ -14,7 +14,7 @@ func _internal_setSecretChatMessageAutoremoveTimeoutInteractively(transaction: T let updatedPeer = peer.withUpdatedMessageAutoremoveTimeout(timeout) let updatedState = state.withUpdatedMessageAutoremoveTimeout(timeout) if !updatedPeer.isEqual(peer) { - updatePeers(transaction: transaction, peers: [updatedPeer], update: { $1 }) + updatePeersCustom(transaction: transaction, peers: [updatedPeer], update: { $1 }) } if updatedState != state { transaction.setPeerChatState(peerId, state: updatedState) diff --git a/submodules/TelegramCore/Sources/SecretChats/UpdateSecretChat.swift b/submodules/TelegramCore/Sources/SecretChats/UpdateSecretChat.swift index c4c427f30d..1b426f9c56 100644 --- a/submodules/TelegramCore/Sources/SecretChats/UpdateSecretChat.swift +++ b/submodules/TelegramCore/Sources/SecretChats/UpdateSecretChat.swift @@ -58,7 +58,7 @@ func updateSecretChat(encryptionProvider: EncryptionProvider, accountPeerId: Pee updatedState = secretChatAddReportCurrentLayerSupportOperationAndUpdateRequestedLayer(transaction: transaction, peerId: currentPeer.id, state: updatedState) transaction.setPeerChatState(currentPeer.id, state: updatedState) - updatePeers(transaction: transaction, peers: [currentPeer.withUpdatedEmbeddedState(updatedState.embeddedState.peerState)], update: { _, updated in + updatePeersCustom(transaction: transaction, peers: [currentPeer.withUpdatedEmbeddedState(updatedState.embeddedState.peerState)], update: { _, updated in return updated }) } else { @@ -73,7 +73,7 @@ func updateSecretChat(encryptionProvider: EncryptionProvider, accountPeerId: Pee let state = currentState.withUpdatedEmbeddedState(.terminated) let peer = currentPeer.withUpdatedEmbeddedState(state.embeddedState.peerState) - updatePeers(transaction: transaction, peers: [peer], update: { _, updated in return updated }) + updatePeersCustom(transaction: transaction, peers: [peer], update: { _, updated in return updated }) transaction.setPeerChatState(peer.id, state: state) transaction.operationLogRemoveAllEntries(peerId: peer.id, tag: OperationLogTags.SecretOutgoing) @@ -101,7 +101,7 @@ func updateSecretChat(encryptionProvider: EncryptionProvider, accountPeerId: Pee transaction.setPeerChatState(chat.peerId, state: updatedState) let peer = TelegramSecretChat(id: chat.peerId, creationDate: date, regularPeerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(adminId)), accessHash: accessHash, role: updatedState.role, embeddedState: updatedState.embeddedState.peerState, messageAutoremoveTimeout: nil) - updatePeers(transaction: transaction, peers: [peer], update: { _, updated in return updated }) + updatePeersCustom(transaction: transaction, peers: [peer], update: { _, updated in return updated }) if folderId != nil { transaction.updatePeerChatListInclusion(peer.id, inclusion: .ifHasMessagesOrOneOf(groupId: Namespaces.PeerGroup.archive, pinningIndex: nil, minTimestamp: date)) } @@ -126,7 +126,7 @@ func updateSecretChat(encryptionProvider: EncryptionProvider, accountPeerId: Pee if let requestData = requestData, currentPeer == nil && adminId == accountPeerId.id._internalGetInt64Value() { let state = SecretChatState(role: .creator, embeddedState: .handshake(.requested(g: requestData.g, p: requestData.p, a: requestData.a)), keychain: SecretChatKeychain(keys: []), keyFingerprint: nil, messageAutoremoveTimeout: nil) let peer = TelegramSecretChat(id: chat.peerId, creationDate: date, regularPeerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(participantId)), accessHash: accessHash, role: state.role, embeddedState: state.embeddedState.peerState, messageAutoremoveTimeout: nil) - updatePeers(transaction: transaction, peers: [peer], update: { _, updated in return updated }) + updatePeersCustom(transaction: transaction, peers: [peer], update: { _, updated in return updated }) transaction.setPeerChatState(peer.id, state: state) transaction.resetIncomingReadStates([peer.id: [ Namespaces.Message.SecretIncoming: .indexBased(maxIncomingReadIndex: MessageIndex.lowerBound(peerId: peer.id), maxOutgoingReadIndex: MessageIndex.lowerBound(peerId: peer.id), count: 0, markedUnread: false), diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index db16bedfef..40b27b0340 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -1872,7 +1872,7 @@ func resolveForumThreads(postbox: Postbox, network: Network, state: AccountMutab } } -func resolveForumThreads(postbox: Postbox, network: Network, ids: [MessageId]) -> Signal { +func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, network: Network, ids: [MessageId]) -> Signal { let forumThreadIds = Set(ids) if forumThreadIds.isEmpty { @@ -1965,20 +1965,8 @@ func resolveForumThreads(postbox: Postbox, network: Network, ids: [MessageId]) - } } - var peers: [Peer] = [] - for user in users { - if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) { - peers.append(telegramUser) - } - } - for chat in chats { - if let groupOrChannel = mergeGroupOrChannel(lhs: transaction.getPeer(chat.peerId), rhs: chat) { - peers.append(groupOrChannel) - } - } - updatePeers(transaction: transaction, peers: peers, update: { _, updated in - return updated - }) + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) let _ = transaction.addMessages(storeMessages, location: .Random) } @@ -1994,7 +1982,7 @@ func resolveForumThreads(postbox: Postbox, network: Network, fetchedChatList: Fe for message in fetchedChatList.storeMessages { if let threadId = message.threadId { - if let channel = fetchedChatList.peers.first(where: { $0.id == message.id.peerId }) as? TelegramChannel, case .group = channel.info, channel.flags.contains(.isForum) { + if let channel = fetchedChatList.peers.peers.first(where: { $0.key == message.id.peerId })?.value as? TelegramChannel, case .group = channel.info, channel.flags.contains(.isForum) { forumThreadIds.insert(MessageId(peerId: message.id.peerId, namespace: message.id.namespace, id: Int32(clamping: threadId))) } } @@ -2017,7 +2005,7 @@ func resolveForumThreads(postbox: Postbox, network: Network, fetchedChatList: Fe } else { var signals: [Signal<(Peer, Api.messages.ForumTopics)?, NoError>] = [] for (peerId, threadIds) in missingForumThreadIds { - guard let peer = fetchedChatList.peers.first(where: { $0.id == peerId }), let inputChannel = apiInputChannel(peer) else { + guard let peer = fetchedChatList.peers.get(peerId), let inputChannel = apiInputChannel(peer) else { Logger.shared.log("resolveForumThreads", "can't fetch thread infos \(threadIds) for peer \(peerId): can't create inputChannel") continue } @@ -2042,12 +2030,7 @@ func resolveForumThreads(postbox: Postbox, network: Network, fetchedChatList: Fe switch result { case let .forumTopics(_, _, topics, messages, chats, users, _): - fetchedChatList.peers.append(contentsOf: chats.compactMap { chat in - return parseTelegramGroupOrChannel(chat: chat) - }) - fetchedChatList.peers.append(contentsOf: users.compactMap { user in - return TelegramUser(user: user) - }) + fetchedChatList.peers = fetchedChatList.peers.union(with: AccumulatedPeers(chats: chats, users: users)) for message in messages { if let message = StoreMessage(apiMessage: message, peerIsForum: peerIsForum) { @@ -2098,7 +2081,7 @@ func resolveForumThreads(postbox: Postbox, network: Network, fetchedChatList: Fe } } -func resolveStories(postbox: Postbox, source: FetchMessageHistoryHoleSource, accountPeerId: PeerId, storyIds: Set, additionalPeers: [PeerId: Peer], result: T) -> Signal { +func resolveStories(postbox: Postbox, source: FetchMessageHistoryHoleSource, accountPeerId: PeerId, storyIds: Set, additionalPeers: AccumulatedPeers, result: T) -> Signal { var storyBuckets: [PeerId: [Int32]] = [:] for id in storyIds { if storyBuckets[id.peerId] == nil { @@ -2113,7 +2096,7 @@ func resolveStories(postbox: Postbox, source: FetchMessageHistoryHoleSource, while idOffset < allIds.count { let bucketLength = min(100, allIds.count - idOffset) let ids = Array(allIds[idOffset ..< (idOffset + bucketLength)]) - signals.append(_internal_getStoriesById(accountPeerId: accountPeerId, postbox: postbox, source: source, peerId: peerId, peerReference: additionalPeers[peerId].flatMap(PeerReference.init), ids: ids) + signals.append(_internal_getStoriesById(accountPeerId: accountPeerId, postbox: postbox, source: source, peerId: peerId, peerReference: additionalPeers.get(peerId).flatMap(PeerReference.init), ids: ids) |> mapToSignal { result -> Signal in return postbox.transaction { transaction -> Void in for id in ids { @@ -2168,7 +2151,7 @@ func resolveAssociatedStories(postbox: Postbox, network: Network, accountPeerId: } if !missingStoryIds.isEmpty { - return resolveStories(postbox: postbox, source: .network(network), accountPeerId: accountPeerId, storyIds: missingStoryIds, additionalPeers: state.insertedPeers, result: state) + return resolveStories(postbox: postbox, source: .network(network), accountPeerId: accountPeerId, storyIds: missingStoryIds, additionalPeers: AccumulatedPeers(peers: Array(state.insertedPeers.values)), result: state) } else { return .single(state) } @@ -2176,7 +2159,7 @@ func resolveAssociatedStories(postbox: Postbox, network: Network, accountPeerId: |> switchToLatest } -func resolveAssociatedStories(postbox: Postbox, source: FetchMessageHistoryHoleSource, accountPeerId: PeerId, messages: [StoreMessage], additionalPeers: [PeerId: Peer], result: T) -> Signal { +func resolveAssociatedStories(postbox: Postbox, source: FetchMessageHistoryHoleSource, accountPeerId: PeerId, messages: [StoreMessage], additionalPeers: AccumulatedPeers, result: T) -> Signal { return postbox.transaction { transaction -> Signal in var missingStoryIds = Set() @@ -4041,32 +4024,17 @@ func replayFinalState( transaction.globalNotificationSettingsUpdated() } case let .MergeApiChats(chats): - var peers: [Peer] = [] - for chat in chats { - if let groupOrChannel = mergeGroupOrChannel(lhs: transaction.getPeer(chat.peerId), rhs: chat) { - peers.append(groupOrChannel) - } - } - updatePeers(transaction: transaction, peers: peers, update: { _, updated in - return updated - }) + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: []) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) case let .MergeApiUsers(users): - var peers: [Peer] = [] - for user in users { - if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) { - peers.append(telegramUser) - } - } - - if let updatedAccountPeer = peers.first(where: { $0.id == accountPeerId }) as? TelegramUser, let previousAccountPeer = transaction.getPeer(accountPeerId) as? TelegramUser { + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: [], users: users) + if let updatedAccountPeer = parsedPeers.get(accountPeerId) as? TelegramUser, let previousAccountPeer = transaction.getPeer(accountPeerId) as? TelegramUser { if updatedAccountPeer.isPremium != previousAccountPeer.isPremium { isPremiumUpdated = true } } - updatePeers(transaction: transaction, peers: peers, update: { _, updated in - return updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) updateContacts(transaction: transaction, apiUsers: users) case let .UpdatePeer(id, f): if let peer = f(transaction.getPeer(id)) { @@ -4076,7 +4044,7 @@ func replayFinalState( } } - updatePeers(transaction: transaction, peers: [peer], update: { _, updated in + updatePeersCustom(transaction: transaction, peers: [peer], update: { _, updated in return updated }) } @@ -4530,7 +4498,7 @@ func replayFinalState( } var appliedMaxReadId: Int32? - if let currentState = transaction.getPeerStoryState(peerId: peerId)?.get(Stories.PeerState.self) { + if let currentState = transaction.getPeerStoryState(peerId: peerId)?.entry.get(Stories.PeerState.self) { if let appliedMaxReadIdValue = appliedMaxReadId { appliedMaxReadId = max(appliedMaxReadIdValue, currentState.maxReadId) } else { @@ -4539,9 +4507,9 @@ func replayFinalState( } transaction.setStoryItems(peerId: peerId, items: updatedPeerEntries) - transaction.setPeerStoryState(peerId: peerId, state: CodableEntry(Stories.PeerState( + transaction.setPeerStoryState(peerId: peerId, state: Stories.PeerState( maxReadId: appliedMaxReadId ?? 0 - ))) + ).postboxRepresentation) if let parsedItem = Stories.StoredItem(apiStoryItem: story, peerId: peerId, transaction: transaction) { storyUpdates.append(InternalStoryUpdate.added(peerId: peerId, item: parsedItem)) @@ -4550,13 +4518,13 @@ func replayFinalState( } case let .UpdateReadStories(peerId, maxId): var appliedMaxReadId = maxId - if let currentState = transaction.getPeerStoryState(peerId: peerId)?.get(Stories.PeerState.self) { + if let currentState = transaction.getPeerStoryState(peerId: peerId)?.entry.get(Stories.PeerState.self) { appliedMaxReadId = max(appliedMaxReadId, currentState.maxReadId) } - transaction.setPeerStoryState(peerId: peerId, state: CodableEntry(Stories.PeerState( + transaction.setPeerStoryState(peerId: peerId, state: Stories.PeerState( maxReadId: appliedMaxReadId - ))) + ).postboxRepresentation) storyUpdates.append(InternalStoryUpdate.read(peerId: peerId, maxId: maxId)) } diff --git a/submodules/TelegramCore/Sources/State/AccountTaskManager.swift b/submodules/TelegramCore/Sources/State/AccountTaskManager.swift index 40e2e003b4..7f165b1b8f 100644 --- a/submodules/TelegramCore/Sources/State/AccountTaskManager.swift +++ b/submodules/TelegramCore/Sources/State/AccountTaskManager.swift @@ -91,7 +91,7 @@ final class AccountTaskManager { tasks.add(managedSynchronizeEmojiSearchCategories(postbox: self.stateManager.postbox, network: self.stateManager.network, kind: .emoji).start()) tasks.add(managedSynchronizeEmojiSearchCategories(postbox: self.stateManager.postbox, network: self.stateManager.network, kind: .status).start()) tasks.add(managedSynchronizeEmojiSearchCategories(postbox: self.stateManager.postbox, network: self.stateManager.network, kind: .avatar).start()) - tasks.add(managedSynchronizeAttachMenuBots(postbox: self.stateManager.postbox, network: self.stateManager.network, force: true).start()) + tasks.add(managedSynchronizeAttachMenuBots(accountPeerId: self.accountPeerId, postbox: self.stateManager.postbox, network: self.stateManager.network, force: true).start()) tasks.add(managedSynchronizeNotificationSoundList(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) tasks.add(managedChatListFilters(postbox: self.stateManager.postbox, network: self.stateManager.network, accountPeerId: self.stateManager.accountPeerId).start()) tasks.add(managedAnimatedEmojiUpdates(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) @@ -112,12 +112,12 @@ final class AccountTaskManager { //tasks.add(managedVoipConfigurationUpdates(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) tasks.add(managedAppConfigurationUpdates(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) - tasks.add(managedPremiumPromoConfigurationUpdates(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) + tasks.add(managedPremiumPromoConfigurationUpdates(accountPeerId: self.accountPeerId, postbox: self.stateManager.postbox, network: self.stateManager.network).start()) tasks.add(managedAutodownloadSettingsUpdates(accountManager: self.accountManager, network: self.stateManager.network).start()) tasks.add(managedTermsOfServiceUpdates(postbox: self.stateManager.postbox, network: self.stateManager.network, stateManager: self.stateManager).start()) tasks.add(managedAppUpdateInfo(network: self.stateManager.network, stateManager: self.stateManager).start()) tasks.add(managedAppChangelog(postbox: self.stateManager.postbox, network: self.stateManager.network, stateManager: self.stateManager, appVersion: self.networkArguments.appVersion).start()) - tasks.add(managedPromoInfoUpdates(postbox: self.stateManager.postbox, network: self.stateManager.network, viewTracker: self.viewTracker).start()) + tasks.add(managedPromoInfoUpdates(accountPeerId: self.accountPeerId, postbox: self.stateManager.postbox, network: self.stateManager.network, viewTracker: self.viewTracker).start()) tasks.add(managedLocalizationUpdatesOperations(accountManager: self.accountManager, postbox: self.stateManager.postbox, network: self.stateManager.network).start()) tasks.add(managedPendingPeerNotificationSettings(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) tasks.add(managedSynchronizeAppLogEventsOperations(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) diff --git a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift index 600d8624eb..6215a58e30 100644 --- a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift +++ b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift @@ -62,6 +62,7 @@ private func pollMessages(entries: [MessageHistoryEntry]) -> (Set, [M } private func fetchWebpage(account: Account, messageId: MessageId) -> Signal { + let accountPeerId = account.peerId return account.postbox.loadedPeerWithId(messageId.peerId) |> take(1) |> mapToSignal { peer in @@ -105,19 +106,7 @@ private func fetchWebpage(account: Account, messageId: MessageId) -> Signal Void in - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - for chat in chats { - if let groupOrChannel = mergeGroupOrChannel(lhs: transaction.getPeer(chat.peerId), rhs: chat) { - peers.append(groupOrChannel) - } - } - for apiUser in users { - if let user = TelegramUser.merge(transaction.getPeer(apiUser.peerId) as? TelegramUser, rhs: apiUser) { - peers.append(user) - peerPresences[user.id] = apiUser - } - } + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) for message in messages { if let storeMessage = StoreMessage(apiMessage: message, peerIsForum: peer.isForum, namespace: isScheduledMessage ? Namespaces.Message.ScheduledCloud : Namespaces.Message.Cloud) { @@ -145,11 +134,7 @@ private func fetchWebpage(account: Account, messageId: MessageId) -> Signal Peer in - return updated - }) - - updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) } } } else { @@ -688,6 +673,7 @@ public final class AccountViewTracker { self.nextUpdatedViewCountDisposableId += 1 if let account = self.account { + let accountPeerId = account.peerId let signal: Signal<[MessageId: ViewCountContextState], NoError> = (account.postbox.transaction { transaction -> Signal<[MessageId: ViewCountContextState], NoError> in guard let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) else { return .complete() @@ -703,28 +689,11 @@ public final class AccountViewTracker { } return account.postbox.transaction { transaction -> [MessageId: ViewCountContextState] in - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - var resultStates: [MessageId: ViewCountContextState] = [:] - for apiUser in users { - if let user = TelegramUser.merge(transaction.getPeer(apiUser.peerId) as? TelegramUser, rhs: apiUser) { - peers.append(user) - peerPresences[user.id] = apiUser - } - } - for chat in chats { - if let groupOrChannel = mergeGroupOrChannel(lhs: transaction.getPeer(chat.peerId), rhs: chat) { - peers.append(groupOrChannel) - } - } + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - - updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) for i in 0 ..< messageIds.count { if i < viewCounts.count { @@ -1046,6 +1015,7 @@ public final class AccountViewTracker { self.nextUpdatedUnsupportedMediaDisposableId += 1 if let account = self.account { + let accountPeerId = account.peerId let signal = account.postbox.transaction { transaction -> Peer? in if let peer = transaction.getPeer(peerId) { return peer @@ -1091,26 +1061,8 @@ public final class AccountViewTracker { } |> mapToSignal { topPeer, messages, chats, users -> Signal in return account.postbox.transaction { transaction -> Void in - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - - for chat in chats { - if let groupOrChannel = mergeGroupOrChannel(lhs: transaction.getPeer(chat.peerId), rhs: chat) { - peers.append(groupOrChannel) - } - } - for apiUser in users { - if let user = TelegramUser.merge(transaction.getPeer(apiUser.peerId) as? TelegramUser, rhs: apiUser) { - peers.append(user) - peerPresences[user.id] = apiUser - } - } - - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - - updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences) + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) for message in messages { guard let storeMessage = StoreMessage(apiMessage: message, peerIsForum: topPeer.isForum) else { diff --git a/submodules/TelegramCore/Sources/State/CallSessionManager.swift b/submodules/TelegramCore/Sources/State/CallSessionManager.swift index 93fd01e995..51bb27ee04 100644 --- a/submodules/TelegramCore/Sources/State/CallSessionManager.swift +++ b/submodules/TelegramCore/Sources/State/CallSessionManager.swift @@ -363,6 +363,7 @@ private final class CallSessionManagerContext { private let queue: Queue private let postbox: Postbox private let network: Network + private let accountPeerId: PeerId private let maxLayer: Int32 private var versions: [CallSessionManagerImplementationVersion] private let addUpdates: (Api.Updates) -> Void @@ -376,10 +377,11 @@ private final class CallSessionManagerContext { private let disposables = DisposableSet() - init(queue: Queue, postbox: Postbox, network: Network, maxLayer: Int32, versions: [CallSessionManagerImplementationVersion], addUpdates: @escaping (Api.Updates) -> Void) { + init(queue: Queue, postbox: Postbox, network: Network, accountPeerId: PeerId, maxLayer: Int32, versions: [CallSessionManagerImplementationVersion], addUpdates: @escaping (Api.Updates) -> Void) { self.queue = queue self.postbox = postbox self.network = network + self.accountPeerId = accountPeerId self.maxLayer = maxLayer self.versions = versions.reversed() self.addUpdates = addUpdates @@ -706,7 +708,7 @@ private final class CallSessionManagerContext { switch context.state { case let .ringing(id, accessHash, gAHash, b, _): let acceptVersions = self.versions.map({ $0.version }) - context.state = .accepting(id: id, accessHash: accessHash, gAHash: gAHash, b: b, disposable: (acceptCallSession(postbox: self.postbox, network: self.network, stableId: id, accessHash: accessHash, b: b, maxLayer: self.maxLayer, versions: acceptVersions) |> deliverOn(self.queue)).start(next: { [weak self] result in + context.state = .accepting(id: id, accessHash: accessHash, gAHash: gAHash, b: b, disposable: (acceptCallSession(accountPeerId: self.accountPeerId, postbox: self.postbox, network: self.network, stableId: id, accessHash: accessHash, b: b, maxLayer: self.maxLayer, versions: acceptVersions) |> deliverOn(self.queue)).start(next: { [weak self] result in if let strongSelf = self, let context = strongSelf.contexts[internalId] { if case .accepting = context.state { switch result { @@ -1056,9 +1058,9 @@ public final class CallSessionManager { private let queue = Queue() private var contextRef: Unmanaged? - init(postbox: Postbox, network: Network, maxLayer: Int32, versions: [CallSessionManagerImplementationVersion], addUpdates: @escaping (Api.Updates) -> Void) { + init(postbox: Postbox, network: Network, accountPeerId: PeerId, maxLayer: Int32, versions: [CallSessionManagerImplementationVersion], addUpdates: @escaping (Api.Updates) -> Void) { self.queue.async { - let context = CallSessionManagerContext(queue: self.queue, postbox: postbox, network: network, maxLayer: maxLayer, versions: versions, addUpdates: addUpdates) + let context = CallSessionManagerContext(queue: self.queue, postbox: postbox, network: network, accountPeerId: accountPeerId, maxLayer: maxLayer, versions: versions, addUpdates: addUpdates) self.contextRef = Unmanaged.passRetained(context) } } @@ -1193,7 +1195,7 @@ private enum AcceptCallResult { case success(AcceptedCall) } -private func acceptCallSession(postbox: Postbox, network: Network, stableId: CallSessionStableId, accessHash: Int64, b: Data, maxLayer: Int32, versions: [String]) -> Signal { +private func acceptCallSession(accountPeerId: PeerId, postbox: Postbox, network: Network, stableId: CallSessionStableId, accessHash: Int64, b: Data, maxLayer: Int32, versions: [String]) -> Signal { return validatedEncryptionConfig(postbox: postbox, network: network) |> mapToSignal { config -> Signal in var gValue: Int32 = config.g.byteSwapped @@ -1218,13 +1220,7 @@ private func acceptCallSession(postbox: Postbox, network: Network, stableId: Cal return postbox.transaction { transaction -> AcceptCallResult in switch call { case let .phoneCall(phoneCall, users): - var parsedUsers: [Peer] = [] - for user in users { - parsedUsers.append(TelegramUser(user: user)) - } - updatePeers(transaction: transaction, peers: parsedUsers, update: { _, updated in - return updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users)) switch phoneCall { case .phoneCallEmpty, .phoneCallRequested, .phoneCallAccepted, .phoneCallDiscarded: diff --git a/submodules/TelegramCore/Sources/State/ChatHistoryPreloadManager.swift b/submodules/TelegramCore/Sources/State/ChatHistoryPreloadManager.swift index 738327b25c..acd550c523 100644 --- a/submodules/TelegramCore/Sources/State/ChatHistoryPreloadManager.swift +++ b/submodules/TelegramCore/Sources/State/ChatHistoryPreloadManager.swift @@ -354,21 +354,6 @@ final class ChatHistoryPreloadManager { return disposable } - /*let signal = self.postbox.tailChatListView(groupId: .root, count: 20, summaryComponents: ChatListEntrySummaryComponents()) - |> map { view -> [ChatHistoryPreloadItem] in - var result: [ChatHistoryPreloadItem] = [] - for entry in view.0.entries { - if case let .MessageEntry(index, _, readState, isMuted, _, _, _, _, _, _) = entry { - var hasUnread = false - if let readState = readState { - hasUnread = readState.count != 0 - } - result.append(ChatHistoryPreloadItem(index: index, isMuted: isMuted, hasUnread: hasUnread)) - } - } - return result - }*/ - self.automaticChatListDisposable.set((combineLatest(queue: .mainQueue(), self.preloadItemsSignal, additionalPeerIds) |> delay(1.0, queue: .mainQueue()) |> deliverOnMainQueue).start(next: { [weak self] loadItems, additionalPeerIds in diff --git a/submodules/TelegramCore/Sources/State/ContactSyncManager.swift b/submodules/TelegramCore/Sources/State/ContactSyncManager.swift index 95ec7a08f0..47dae1a1fd 100644 --- a/submodules/TelegramCore/Sources/State/ContactSyncManager.swift +++ b/submodules/TelegramCore/Sources/State/ContactSyncManager.swift @@ -192,7 +192,7 @@ private final class ContactSyncManagerImpl { case let .sync(importableContacts): let importSignal: Signal if let importableContacts = importableContacts { - importSignal = pushDeviceContacts(postbox: self.postbox, network: self.network, importableContacts: importableContacts, reimportAttempts: self.reimportAttempts) + importSignal = pushDeviceContacts(accountPeerId: self.accountPeerId, postbox: self.postbox, network: self.network, importableContacts: importableContacts, reimportAttempts: self.reimportAttempts) } else { importSignal = .single(PushDeviceContactsResult(addedReimportAttempts: [:])) } @@ -235,7 +235,7 @@ private struct PushDeviceContactsResult { let addedReimportAttempts: [TelegramDeviceContactImportIdentifier: Double] } -private func pushDeviceContacts(postbox: Postbox, network: Network, importableContacts: [DeviceContactNormalizedPhoneNumber: ImportableDeviceContactData], reimportAttempts: [TelegramDeviceContactImportIdentifier: Double]) -> Signal { +private func pushDeviceContacts(accountPeerId: PeerId, postbox: Postbox, network: Network, importableContacts: [DeviceContactNormalizedPhoneNumber: ImportableDeviceContactData], reimportAttempts: [TelegramDeviceContactImportIdentifier: Double]) -> Signal { return postbox.transaction { transaction -> Signal in var noLongerImportedIdentifiers = Set() var updatedDataIdentifiers = Set() @@ -312,14 +312,14 @@ private func pushDeviceContacts(postbox: Postbox, network: Network, importableCo } } - return pushDeviceContactData(postbox: postbox, network: network, contacts: preparedContactData) + return pushDeviceContactData(accountPeerId: accountPeerId, postbox: postbox, network: network, contacts: preparedContactData) } |> switchToLatest } private let importBatchCount: Int = 500 -private func pushDeviceContactData(postbox: Postbox, network: Network, contacts: [(DeviceContactNormalizedPhoneNumber, ImportableDeviceContactData)]) -> Signal { +private func pushDeviceContactData(accountPeerId: PeerId, postbox: Postbox, network: Network, contacts: [(DeviceContactNormalizedPhoneNumber, ImportableDeviceContactData)]) -> Signal { var batches: Signal = .single(PushDeviceContactsResult(addedReimportAttempts: [:])) for s in stride(from: 0, to: contacts.count, by: importBatchCount) { let batch = Array(contacts[s ..< min(s + importBatchCount, contacts.count)]) @@ -342,10 +342,8 @@ private func pushDeviceContactData(postbox: Postbox, network: Network, contacts: var peerIdByClientId: [Int64: PeerId] = [:] switch result { case let .importedContacts(imported, popularInvites, retryContacts, users): - let peers = users.map { TelegramUser(user: $0) as Peer } - updatePeers(transaction: transaction, peers: peers, update: { _, updated in - return updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users)) + for item in imported { switch item { case let .importedContact(userId, clientId): diff --git a/submodules/TelegramCore/Sources/State/FetchChatList.swift b/submodules/TelegramCore/Sources/State/FetchChatList.swift index 1d4ae4cc37..4b9c07b22d 100644 --- a/submodules/TelegramCore/Sources/State/FetchChatList.swift +++ b/submodules/TelegramCore/Sources/State/FetchChatList.swift @@ -12,8 +12,7 @@ enum FetchChatListLocation { struct ParsedDialogs { let itemIds: [PeerId] - let peers: [Peer] - let peerPresences: [PeerId: Api.User] + let peers: AccumulatedPeers let notificationSettings: [PeerId: PeerNotificationSettings] let readStates: [PeerId: [MessageId.Namespace: PeerReadState]] @@ -62,18 +61,7 @@ private func parseDialogs(apiDialogs: [Api.Dialog], apiMessages: [Api.Message], var referencedFolders: [PeerGroupId: PeerGroupUnreadCountersSummary] = [:] var itemIds: [PeerId] = [] - var peers: [PeerId: Peer] = [:] - var peerPresences: [PeerId: Api.User] = [:] - for chat in apiChats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers[groupOrChannel.id] = groupOrChannel - } - } - for user in apiUsers { - let telegramUser = TelegramUser(user: user) - peers[telegramUser.id] = telegramUser - peerPresences[telegramUser.id] = user - } + let peers = AccumulatedPeers(chats: apiChats, users: apiUsers) for dialog in apiDialogs { let apiPeer: Api.Peer @@ -88,7 +76,7 @@ private func parseDialogs(apiDialogs: [Api.Dialog], apiMessages: [Api.Message], let apiNotificationSettings: Api.PeerNotifySettings switch dialog { case let .dialog(flags, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount, peerNotificationSettings, pts, _, _, ttlPeriod): - if let peer = peers[peer.peerId] { + if let peer = peers.get(peer.peerId) { var isExluded = false if let group = peer as? TelegramGroup { if group.flags.contains(.deactivated) { @@ -154,7 +142,7 @@ private func parseDialogs(apiDialogs: [Api.Dialog], apiMessages: [Api.Message], for message in apiMessages { var peerIsForum = false - if let peerId = message.peerId, let peer = peers[peerId], peer.isForum { + if let peerId = message.peerId, let peer = peers.get(peerId), peer.isForum { peerIsForum = true } if let storeMessage = StoreMessage(apiMessage: message, peerIsForum: peerIsForum) { @@ -179,9 +167,7 @@ private func parseDialogs(apiDialogs: [Api.Dialog], apiMessages: [Api.Message], return ParsedDialogs( itemIds: itemIds, - peers: Array(peers.values), - peerPresences: peerPresences, - + peers: peers, notificationSettings: notificationSettings, readStates: readStates, mentionTagSummaries: mentionTagSummaries, @@ -198,8 +184,7 @@ private func parseDialogs(apiDialogs: [Api.Dialog], apiMessages: [Api.Message], struct FetchedChatList { var chatPeerIds: [PeerId] - var peers: [Peer] - var peerPresences: [PeerId: Api.User] + var peers: AccumulatedPeers var notificationSettings: [PeerId: PeerNotificationSettings] var ttlPeriods: [PeerId: CachedPeerAutoremoveTimeout] var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] @@ -306,8 +291,7 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo return combineLatest(folderSignals) |> mapToSignal { folders -> Signal in - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] + var peers = AccumulatedPeers() var notificationSettings: [PeerId: PeerNotificationSettings] = [:] var ttlPeriods: [PeerId: CachedPeerAutoremoveTimeout] = [:] var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:] @@ -317,8 +301,7 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo var storeMessages: [StoreMessage] = [] var topMessageIds: [PeerId: MessageId] = [:] - peers.append(contentsOf: parsedRemoteChats.peers) - peerPresences.merge(parsedRemoteChats.peerPresences, uniquingKeysWith: { _, updated in updated }) + peers = peers.union(with: parsedRemoteChats.peers) notificationSettings.merge(parsedRemoteChats.notificationSettings, uniquingKeysWith: { _, updated in updated }) ttlPeriods.merge(parsedRemoteChats.ttlPeriods, uniquingKeysWith: { _, updated in updated }) readStates.merge(parsedRemoteChats.readStates, uniquingKeysWith: { _, updated in updated }) @@ -329,8 +312,7 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo topMessageIds.merge(parsedRemoteChats.topMessageIds, uniquingKeysWith: { _, updated in updated }) if let parsedPinnedChats = parsedPinnedChats { - peers.append(contentsOf: parsedPinnedChats.peers) - peerPresences.merge(parsedPinnedChats.peerPresences, uniquingKeysWith: { _, updated in updated }) + peers = peers.union(with: parsedPinnedChats.peers) notificationSettings.merge(parsedPinnedChats.notificationSettings, uniquingKeysWith: { _, updated in updated }) ttlPeriods.merge(parsedPinnedChats.ttlPeriods, uniquingKeysWith: { _, updated in updated }) readStates.merge(parsedPinnedChats.readStates, uniquingKeysWith: { _, updated in updated }) @@ -353,8 +335,7 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo for peerId in folderChats.itemIds { peerGroupIds[peerId] = groupId } - peers.append(contentsOf: folderChats.peers) - peerPresences.merge(folderChats.peerPresences, uniquingKeysWith: { _, updated in updated }) + peers = peers.union(with: folderChats.peers) notificationSettings.merge(folderChats.notificationSettings, uniquingKeysWith: { _, updated in updated }) ttlPeriods.merge(folderChats.ttlPeriods, uniquingKeysWith: { _, updated in updated }) readStates.merge(folderChats.readStates, uniquingKeysWith: { _, updated in updated }) @@ -389,7 +370,6 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo let result: FetchedChatList? = FetchedChatList( chatPeerIds: parsedRemoteChats.itemIds + (pinnedItemIds ?? []), peers: peers, - peerPresences: peerPresences, notificationSettings: notificationSettings, ttlPeriods: ttlPeriods, readStates: readStates, diff --git a/submodules/TelegramCore/Sources/State/Holes.swift b/submodules/TelegramCore/Sources/State/Holes.swift index d3e83ac05d..bb5f62e31c 100644 --- a/submodules/TelegramCore/Sources/State/Holes.swift +++ b/submodules/TelegramCore/Sources/State/Holes.swift @@ -4,6 +4,81 @@ import SwiftSignalKit import TelegramApi import MtProtoKit +struct AccumulatedPeers { + var peers: [PeerId: Peer] = [:] + var users: [PeerId: Api.User] = [:] + + var allIds: Set { + var result = Set() + for (id, _) in self.peers { + result.insert(id) + } + for (id, _) in self.users { + result.insert(id) + } + return result + } + + init() { + } + + init(transaction: Transaction, chats: [Api.Chat], users: [Api.User]) { + for chat in chats { + if let groupOrChannel = mergeGroupOrChannel(lhs: transaction.getPeer(chat.peerId), rhs: chat) { + self.peers[groupOrChannel.id] = groupOrChannel + } + } + for user in users { + self.users[user.peerId] = user + } + } + + init(chats: [Api.Chat], users: [Api.User]) { + for chat in chats { + if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { + self.peers[groupOrChannel.id] = groupOrChannel + } + } + for user in users { + self.users[user.peerId] = user + } + } + + init(users: [Api.User]) { + for user in users { + self.users[user.peerId] = user + } + } + + init(peers: [Peer]) { + for peer in peers { + self.peers[peer.id] = peer + } + } + + func union(with other: AccumulatedPeers) -> AccumulatedPeers { + var result = self + + for (id, peer) in other.peers { + result.peers[id] = peer + } + for (id, user) in other.users { + result.users[id] = user + } + + return result + } + + func get(_ id: PeerId) -> Peer? { + if let peer = self.peers[id] { + return peer + } else if let user = self.users[id] { + return TelegramUser(user: user) + } else { + return nil + } + } +} func messageFilterForTagMask(_ tagMask: MessageTags) -> Api.MessagesFilter? { if tagMask == .photoOrVideo { @@ -100,7 +175,7 @@ func resolveUnknownEmojiFiles(postbox: Postbox, source: FetchMessageHistoryHo } } -func withResolvedAssociatedMessages(postbox: Postbox, source: FetchMessageHistoryHoleSource, accountPeerId: PeerId, peers: [PeerId: Peer], storeMessages: [StoreMessage], _ f: @escaping (Transaction, [Peer], [StoreMessage]) -> T) -> Signal { +func withResolvedAssociatedMessages(postbox: Postbox, source: FetchMessageHistoryHoleSource, accountPeerId: PeerId, parsedPeers: AccumulatedPeers, storeMessages: [StoreMessage], _ f: @escaping (Transaction, AccumulatedPeers, [StoreMessage]) -> T) -> Signal { return postbox.transaction { transaction -> Signal in var storedIds = Set() var referencedReplyIds = ReferencedReplyMessageIds() @@ -129,17 +204,17 @@ func withResolvedAssociatedMessages(postbox: Postbox, source: FetchMessageHis if referencedReplyIds.isEmpty && referencedGeneralIds.isEmpty { return resolveUnknownEmojiFiles(postbox: postbox, source: source, messages: storeMessages, reactions: [], result: Void()) |> mapToSignal { _ -> Signal in - return resolveAssociatedStories(postbox: postbox, source: source, accountPeerId: accountPeerId, messages: storeMessages, additionalPeers: peers, result: Void()) + return resolveAssociatedStories(postbox: postbox, source: source, accountPeerId: accountPeerId, messages: storeMessages, additionalPeers: parsedPeers, result: Void()) |> mapToSignal { _ -> Signal in return postbox.transaction { transaction -> T in - return f(transaction, [], []) + return f(transaction, parsedPeers, []) } } } } else { var signals: [Signal<(Peer, [Api.Message], [Api.Chat], [Api.User]), NoError>] = [] for (peerId, messageIds) in messagesIdsGroupedByPeerId(referencedReplyIds) { - if let peer = transaction.getPeer(peerId) ?? peers[peerId] { + if let peer = transaction.getPeer(peerId) ?? parsedPeers.get(peerId) { var signal: Signal? if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.CloudGroup { signal = source.request(Api.functions.messages.getMessages(id: messageIds.targetIdsBySourceId.values.map({ Api.InputMessage.inputMessageReplyTo(id: $0.id) }))) @@ -170,7 +245,7 @@ func withResolvedAssociatedMessages(postbox: Postbox, source: FetchMessageHis } } for (peerId, messageIds) in messagesIdsGroupedByPeerId(referencedGeneralIds) { - if let peer = transaction.getPeer(peerId) ?? peers[peerId] { + if let peer = transaction.getPeer(peerId) ?? parsedPeers.get(peerId) { var signal: Signal? if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.CloudGroup { signal = source.request(Api.functions.messages.getMessages(id: messageIds.map({ Api.InputMessage.inputMessageID(id: $0.id) }))) @@ -205,42 +280,32 @@ func withResolvedAssociatedMessages(postbox: Postbox, source: FetchMessageHis return fetchMessages |> mapToSignal { results -> Signal in - var additionalPeers: [Peer] = [] - var additionalMessages: [StoreMessage] = [] - for (peer, messages, chats, users) in results { - if !messages.isEmpty { - for message in messages { - if let message = StoreMessage(apiMessage: message, peerIsForum: peer.isForum) { - additionalMessages.append(message) + return postbox.transaction { transaction -> Signal in + var additionalPeers = AccumulatedPeers() + + var additionalMessages: [StoreMessage] = [] + for (peer, messages, chats, users) in results { + if !messages.isEmpty { + for message in messages { + if let message = StoreMessage(apiMessage: message, peerIsForum: peer.isForum) { + additionalMessages.append(message) + } + } + } + additionalPeers = additionalPeers.union(with: AccumulatedPeers(transaction: transaction, chats: chats, users: users)) + } + + return resolveUnknownEmojiFiles(postbox: postbox, source: source, messages: storeMessages + additionalMessages, reactions: [], result: Void()) + |> mapToSignal { _ -> Signal in + return resolveAssociatedStories(postbox: postbox, source: source, accountPeerId: accountPeerId, messages: storeMessages + additionalMessages, additionalPeers: parsedPeers.union(with: additionalPeers), result: Void()) + |> mapToSignal { _ -> Signal in + return postbox.transaction { transaction -> T in + return f(transaction, additionalPeers, additionalMessages) } } } - for chat in chats { - if let peer = parseTelegramGroupOrChannel(chat: chat) { - additionalPeers.append(peer) - } - } - for user in users { - additionalPeers.append(TelegramUser(user: user)) - } - } - - return resolveUnknownEmojiFiles(postbox: postbox, source: source, messages: storeMessages + additionalMessages, reactions: [], result: Void()) - |> mapToSignal { _ -> Signal in - var additionalPeerMap: [PeerId: Peer] = [:] - for (_, peer) in peers { - additionalPeerMap[peer.id] = peer - } - for peer in additionalPeers { - additionalPeerMap[peer.id] = peer - } - return resolveAssociatedStories(postbox: postbox, source: source, accountPeerId: accountPeerId, messages: storeMessages + additionalMessages, additionalPeers: additionalPeerMap, result: Void()) - |> mapToSignal { _ -> Signal in - return postbox.transaction { transaction -> T in - return f(transaction, additionalPeers, additionalMessages) - } - } } + |> switchToLatest } } } @@ -616,7 +681,9 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH } return true }, delayIncrement: 0.1, maxDelay: 2.0, maxRetries: 0, onQueue: .concurrentDefaultQueue()) - |> map(Optional.init) + |> map { result -> Api.messages.Messages? in + return result + } |> `catch` { _ -> Signal in return .single(nil) } @@ -624,160 +691,149 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH guard let result = result else { return .single(nil) } - let messages: [Api.Message] - let chats: [Api.Chat] - let users: [Api.User] - var channelPts: Int32? - switch result { - case let .messages(messages: apiMessages, chats: apiChats, users: apiUsers): - messages = apiMessages - chats = apiChats - users = apiUsers - case let .messagesSlice(_, _, _, _, messages: apiMessages, chats: apiChats, users: apiUsers): - messages = apiMessages - chats = apiChats - users = apiUsers - case let .channelMessages(_, pts, _, _, apiMessages, apiTopics, apiChats, apiUsers): - messages = apiMessages - let _ = apiTopics - chats = apiChats - users = apiUsers - channelPts = pts - case .messagesNotModified: - messages = [] - chats = [] - users = [] - } - - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers.append(groupOrChannel) + return postbox.transaction { transaction -> Signal in + let messages: [Api.Message] + let chats: [Api.Chat] + let users: [Api.User] + var channelPts: Int32? + switch result { + case let .messages(messages: apiMessages, chats: apiChats, users: apiUsers): + messages = apiMessages + chats = apiChats + users = apiUsers + case let .messagesSlice(_, _, _, _, messages: apiMessages, chats: apiChats, users: apiUsers): + messages = apiMessages + chats = apiChats + users = apiUsers + case let .channelMessages(_, pts, _, _, apiMessages, apiTopics, apiChats, apiUsers): + messages = apiMessages + let _ = apiTopics + chats = apiChats + users = apiUsers + channelPts = pts + case .messagesNotModified: + messages = [] + chats = [] + users = [] } - } - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } - - var storeMessages: [StoreMessage] = [] - - for message in messages { - if let storeMessage = StoreMessage(apiMessage: message, peerIsForum: peer.isForum, namespace: namespace) { - if let channelPts = channelPts { - var attributes = storeMessage.attributes - attributes.append(ChannelMessageStateVersionAttribute(pts: channelPts)) - storeMessages.append(storeMessage.withUpdatedAttributes(attributes)) - } else { - storeMessages.append(storeMessage) + + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) + + var storeMessages: [StoreMessage] = [] + + for message in messages { + if let storeMessage = StoreMessage(apiMessage: message, peerIsForum: peer.isForum, namespace: namespace) { + if let channelPts = channelPts { + var attributes = storeMessage.attributes + attributes.append(ChannelMessageStateVersionAttribute(pts: channelPts)) + storeMessages.append(storeMessage.withUpdatedAttributes(attributes)) + } else { + storeMessages.append(storeMessage) + } } } - } - - return withResolvedAssociatedMessages(postbox: postbox, source: source, accountPeerId: accountPeerId, peers: Dictionary(peers.map({ ($0.id, $0) }), uniquingKeysWith: { lhs, _ in lhs }), storeMessages: storeMessages, { transaction, additionalPeers, additionalMessages -> FetchMessageHistoryHoleResult? in - let _ = transaction.addMessages(storeMessages, location: .Random) - let _ = transaction.addMessages(additionalMessages, location: .Random) - var filledRange: ClosedRange - var strictFilledIndices: IndexSet - let ids = storeMessages.compactMap { message -> MessageId.Id? in - switch message.id { - case let .Id(id): - switch space { - case let .tag(tag): - if !message.tags.contains(tag) { - return nil - } else { + + return withResolvedAssociatedMessages(postbox: postbox, source: source, accountPeerId: accountPeerId, parsedPeers: parsedPeers, storeMessages: storeMessages, { transaction, additionalParsedPeers, additionalMessages -> FetchMessageHistoryHoleResult? in + let _ = transaction.addMessages(storeMessages, location: .Random) + let _ = transaction.addMessages(additionalMessages, location: .Random) + var filledRange: ClosedRange + var strictFilledIndices: IndexSet + let ids = storeMessages.compactMap { message -> MessageId.Id? in + switch message.id { + case let .Id(id): + switch space { + case let .tag(tag): + if !message.tags.contains(tag) { + return nil + } else { + return id.id + } + case .everywhere: return id.id } - case .everywhere: - return id.id + case .Partial: + return nil } - case .Partial: - return nil } - } - let fullIds = storeMessages.compactMap { message -> MessageId? in - switch message.id { - case let .Id(id): - switch space { - case let .tag(tag): - if !message.tags.contains(tag) { - return nil - } else { + let fullIds = storeMessages.compactMap { message -> MessageId? in + switch message.id { + case let .Id(id): + switch space { + case let .tag(tag): + if !message.tags.contains(tag) { + return nil + } else { + return id + } + case .everywhere: return id } - case .everywhere: - return id + case .Partial: + return nil } - case .Partial: - return nil } - } - - print("fetchMessageHistoryHole for \(peerInput) space \(space) done") - if peerInput.requestThreadId != nil, case .everywhere = space, case .aroundId = direction { - assert(true) - } - - if ids.count == 0 || implicitelyFillHole { - filledRange = minMaxRange - strictFilledIndices = IndexSet() - } else { - let messageRange = ids.min()! ... ids.max()! - switch direction { - case let .aroundId(aroundId): - filledRange = min(aroundId.id, messageRange.lowerBound) ... max(aroundId.id, messageRange.upperBound) - strictFilledIndices = IndexSet(integersIn: Int(min(aroundId.id, messageRange.lowerBound)) ... Int(max(aroundId.id, messageRange.upperBound))) - if peerInput.requestThreadId != nil { - if ids.count <= count / 2 - 1 { - filledRange = minMaxRange + + print("fetchMessageHistoryHole for \(peerInput) space \(space) done") + if peerInput.requestThreadId != nil, case .everywhere = space, case .aroundId = direction { + assert(true) + } + + if ids.count == 0 || implicitelyFillHole { + filledRange = minMaxRange + strictFilledIndices = IndexSet() + } else { + let messageRange = ids.min()! ... ids.max()! + switch direction { + case let .aroundId(aroundId): + filledRange = min(aroundId.id, messageRange.lowerBound) ... max(aroundId.id, messageRange.upperBound) + strictFilledIndices = IndexSet(integersIn: Int(min(aroundId.id, messageRange.lowerBound)) ... Int(max(aroundId.id, messageRange.upperBound))) + if peerInput.requestThreadId != nil { + if ids.count <= count / 2 - 1 { + filledRange = minMaxRange + } } - } - case let .range(start, end): - if start.id <= end.id { - let minBound = start.id - let maxBound = messageRange.upperBound - filledRange = min(minBound, maxBound) ... max(minBound, maxBound) - - var maxStrictIndex = max(minBound, maxBound) - maxStrictIndex = min(maxStrictIndex, messageRange.upperBound) - strictFilledIndices = IndexSet(integersIn: Int(min(minBound, maxBound)) ... Int(maxStrictIndex)) - } else { - let minBound = messageRange.lowerBound - let maxBound = start.id - filledRange = min(minBound, maxBound) ... max(minBound, maxBound) - - var maxStrictIndex = max(minBound, maxBound) - maxStrictIndex = min(maxStrictIndex, messageRange.upperBound) - strictFilledIndices = IndexSet(integersIn: Int(min(minBound, maxBound)) ... Int(maxStrictIndex)) - } + case let .range(start, end): + if start.id <= end.id { + let minBound = start.id + let maxBound = messageRange.upperBound + filledRange = min(minBound, maxBound) ... max(minBound, maxBound) + + var maxStrictIndex = max(minBound, maxBound) + maxStrictIndex = min(maxStrictIndex, messageRange.upperBound) + strictFilledIndices = IndexSet(integersIn: Int(min(minBound, maxBound)) ... Int(maxStrictIndex)) + } else { + let minBound = messageRange.lowerBound + let maxBound = start.id + filledRange = min(minBound, maxBound) ... max(minBound, maxBound) + + var maxStrictIndex = max(minBound, maxBound) + maxStrictIndex = min(maxStrictIndex, messageRange.upperBound) + strictFilledIndices = IndexSet(integersIn: Int(min(minBound, maxBound)) ... Int(maxStrictIndex)) + } + } } - } - - - switch peerInput { - case let .direct(peerId, threadId): - transaction.removeHole(peerId: peerId, threadId: threadId, namespace: namespace, space: space, range: filledRange) - case .threadFromChannel: - break - } - - updatePeers(transaction: transaction, peers: peers + additionalPeers, update: { _, updated -> Peer in - return updated + + + switch peerInput { + case let .direct(peerId, threadId): + transaction.removeHole(peerId: peerId, threadId: threadId, namespace: namespace, space: space, range: filledRange) + case .threadFromChannel: + break + } + + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers.union(with: additionalParsedPeers)) + + let result = FetchMessageHistoryHoleResult( + removedIndices: IndexSet(integersIn: Int(filledRange.lowerBound) ... Int(filledRange.upperBound)), + strictRemovedIndices: strictFilledIndices, + actualPeerId: storeMessages.first?.id.peerId, + actualThreadId: storeMessages.first?.threadId, + ids: fullIds + ) + return result }) - updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) - - let result = FetchMessageHistoryHoleResult( - removedIndices: IndexSet(integersIn: Int(filledRange.lowerBound) ... Int(filledRange.upperBound)), - strictRemovedIndices: strictFilledIndices, - actualPeerId: storeMessages.first?.id.peerId, - actualThreadId: storeMessages.first?.threadId, - ids: fullIds - ) - return result - }) + } + |> switchToLatest } } } @@ -812,10 +868,8 @@ func fetchChatListHole(postbox: Postbox, network: Network, accountPeerId: PeerId } |> ignoreValues } - return withResolvedAssociatedMessages(postbox: postbox, source: .network(network), accountPeerId: accountPeerId, peers: Dictionary(fetchedChats.peers.map({ ($0.id, $0) }), uniquingKeysWith: { lhs, _ in lhs }), storeMessages: fetchedChats.storeMessages, { transaction, additionalPeers, additionalMessages -> Void in - updatePeers(transaction: transaction, peers: fetchedChats.peers + additionalPeers, update: { _, updated -> Peer in - return updated - }) + return withResolvedAssociatedMessages(postbox: postbox, source: .network(network), accountPeerId: accountPeerId, parsedPeers: fetchedChats.peers, storeMessages: fetchedChats.storeMessages, { transaction, additionalPeers, additionalMessages -> Void in + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: fetchedChats.peers.union(with: additionalPeers)) for (threadMessageId, data) in fetchedChats.threadInfos { if let entry = StoredMessageHistoryThreadInfo(data.data) { @@ -825,7 +879,6 @@ func fetchChatListHole(postbox: Postbox, network: Network, accountPeerId: PeerId 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) transaction.updateCurrentPeerNotificationSettings(fetchedChats.notificationSettings) let _ = transaction.addMessages(fetchedChats.storeMessages, location: .UpperHistoryBlock) let _ = transaction.addMessages(additionalMessages, location: .Random) @@ -927,27 +980,11 @@ func fetchCallListHole(network: Network, postbox: Postbox, accountPeerId: PeerId var storeMessages: [StoreMessage] = [] var topIndex: MessageIndex? - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers.append(groupOrChannel) - } - } - for user in users { - if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) { - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } - } - var peerMap: [PeerId: Peer] = [:] - for peer in peers { - peerMap[peer.id] = peer - } + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) for message in messages { var peerIsForum = false - if let peerId = message.peerId, let peer = peerMap[peerId], peer.isForum { + if let peerId = message.peerId, let peer = parsedPeers.get(peerId), peer.isForum { peerIsForum = true } if let storeMessage = StoreMessage(apiMessage: message, peerIsForum: peerIsForum) { @@ -965,10 +1002,7 @@ func fetchCallListHole(network: Network, postbox: Postbox, accountPeerId: PeerId transaction.replaceGlobalMessageTagsHole(globalTags: [.Calls, .MissedCalls], index: holeIndex, with: updatedIndex, messages: storeMessages) - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) } } return searchResult diff --git a/submodules/TelegramCore/Sources/State/ManagedPeerTimestampAttributeOperations.swift b/submodules/TelegramCore/Sources/State/ManagedPeerTimestampAttributeOperations.swift index 472ceee5ce..f1ee10c3a9 100644 --- a/submodules/TelegramCore/Sources/State/ManagedPeerTimestampAttributeOperations.swift +++ b/submodules/TelegramCore/Sources/State/ManagedPeerTimestampAttributeOperations.swift @@ -87,7 +87,7 @@ func managedPeerTimestampAttributeOperations(network: Network, postbox: Postbox) |> then(postbox.transaction { transaction -> Void in if let peer = transaction.getPeer(entry.peerId) { if let user = peer as? TelegramUser { - updatePeers(transaction: transaction, peers: [user.withUpdatedEmojiStatus(nil)], update: { _, updated in updated }) + updatePeersCustom(transaction: transaction, peers: [user.withUpdatedEmojiStatus(nil)], update: { _, updated in updated }) } } //failsafe diff --git a/submodules/TelegramCore/Sources/State/ManagedPremiumPromoConfigurationUpdates.swift b/submodules/TelegramCore/Sources/State/ManagedPremiumPromoConfigurationUpdates.swift index edff6e5ee0..7c26efb038 100644 --- a/submodules/TelegramCore/Sources/State/ManagedPremiumPromoConfigurationUpdates.swift +++ b/submodules/TelegramCore/Sources/State/ManagedPremiumPromoConfigurationUpdates.swift @@ -5,10 +5,10 @@ import TelegramApi import MtProtoKit public func updatePremiumPromoConfigurationOnce(account: Account) -> Signal { - return updatePremiumPromoConfigurationOnce(postbox: account.postbox, network: account.network) + return updatePremiumPromoConfigurationOnce(accountPeerId: account.peerId, postbox: account.postbox, network: account.network) } -func updatePremiumPromoConfigurationOnce(postbox: Postbox, network: Network) -> Signal { +func updatePremiumPromoConfigurationOnce(accountPeerId: PeerId, postbox: Postbox, network: Network) -> Signal { return network.request(Api.functions.help.getPremiumPromo()) |> map(Optional.init) |> `catch` { _ -> Signal in @@ -20,14 +20,8 @@ func updatePremiumPromoConfigurationOnce(postbox: Postbox, network: Network) -> } return postbox.transaction { transaction -> Void in if case let .premiumPromo(_, _, _, _, _, apiUsers) = result { - let users = apiUsers.map { TelegramUser(user: $0) } - updatePeers(transaction: transaction, peers: users, update: { current, updated -> Peer in - if let updated = updated as? TelegramUser { - return TelegramUser.merge(lhs: current as? TelegramUser, rhs: updated) - } else { - return updated - } - }) + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: [], users: apiUsers) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) } updatePremiumPromoConfiguration(transaction: transaction, { configuration -> PremiumPromoConfiguration in @@ -37,9 +31,9 @@ func updatePremiumPromoConfigurationOnce(postbox: Postbox, network: Network) -> } } -func managedPremiumPromoConfigurationUpdates(postbox: Postbox, network: Network) -> Signal { +func managedPremiumPromoConfigurationUpdates(accountPeerId: PeerId, postbox: Postbox, network: Network) -> Signal { let poll = Signal { subscriber in - return updatePremiumPromoConfigurationOnce(postbox: postbox, network: network).start(completed: { + return updatePremiumPromoConfigurationOnce(accountPeerId: accountPeerId, postbox: postbox, network: network).start(completed: { subscriber.putCompletion() }) } diff --git a/submodules/TelegramCore/Sources/State/ManagedProxyInfoUpdates.swift b/submodules/TelegramCore/Sources/State/ManagedProxyInfoUpdates.swift index 7f7dfd8d33..13c23d8495 100644 --- a/submodules/TelegramCore/Sources/State/ManagedProxyInfoUpdates.swift +++ b/submodules/TelegramCore/Sources/State/ManagedProxyInfoUpdates.swift @@ -72,7 +72,7 @@ public final class PromoChatListItem: AdditionalChatListItem { } } -func managedPromoInfoUpdates(postbox: Postbox, network: Network, viewTracker: AccountViewTracker) -> Signal { +func managedPromoInfoUpdates(accountPeerId: PeerId, postbox: Postbox, network: Network, viewTracker: AccountViewTracker) -> Signal { return Signal { subscriber in let queue = Queue() let update = network.contextProxyId @@ -89,24 +89,9 @@ func managedPromoInfoUpdates(postbox: Postbox, network: Network, viewTracker: Ac case .promoDataEmpty: transaction.replaceAdditionalChatListItems([]) case let .promoData(_, _, peer, chats, users, psaType, psaMessage): - var peers: [Peer] = [] - var peerPresences: [PeerId: PeerPresence] = [:] - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers.append(groupOrChannel) - } - } - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - if let presence = TelegramUserPresence(apiUser: user) { - peerPresences[telegramUser.id] = presence - } - } + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) let kind: PromoChatListItem.Kind if let psaType = psaType { diff --git a/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift b/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift index 6cffff8581..d7fe6ea2be 100644 --- a/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift +++ b/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift @@ -192,7 +192,7 @@ private func initialHandshakeAccept(postbox: Postbox, network: Network, peerId: updatedState = updatedState.withUpdatedEmbeddedState(.terminated) transaction.setPeerChatState(peerId, state: updatedState) if let peer = transaction.getPeer(peerId) as? TelegramSecretChat { - updatePeers(transaction: transaction, peers: [peer.withUpdatedEmbeddedState(updatedState.embeddedState.peerState)], update: { _, updated in + updatePeersCustom(transaction: transaction, peers: [peer.withUpdatedEmbeddedState(updatedState.embeddedState.peerState)], update: { _, updated in return updated }) } @@ -266,7 +266,7 @@ private func initialHandshakeAccept(postbox: Postbox, network: Network, peerId: } transaction.setPeerChatState(peerId, state: updatedState) if let peer = transaction.getPeer(peerId) as? TelegramSecretChat { - updatePeers(transaction: transaction, peers: [peer.withUpdatedEmbeddedState(updatedState.embeddedState.peerState)], update: { _, updated in + updatePeersCustom(transaction: transaction, peers: [peer.withUpdatedEmbeddedState(updatedState.embeddedState.peerState)], update: { _, updated in return updated }) } diff --git a/submodules/TelegramCore/Sources/State/ManagedServiceViews.swift b/submodules/TelegramCore/Sources/State/ManagedServiceViews.swift index c813dce0e7..d7471d4cae 100644 --- a/submodules/TelegramCore/Sources/State/ManagedServiceViews.swift +++ b/submodules/TelegramCore/Sources/State/ManagedServiceViews.swift @@ -11,5 +11,7 @@ func managedServiceViews(accountPeerId: PeerId, network: Network, postbox: Postb disposable.add(managedChatListHoles(network: network, postbox: postbox, accountPeerId: accountPeerId).start()) disposable.add(managedForumTopicListHoles(network: network, postbox: postbox, accountPeerId: accountPeerId).start()) + disposable.add((_internal_refreshSeenStories(postbox: postbox, network: network) |> then(.complete() |> suspendAwareDelay(5 * 60 * 60, queue: .mainQueue())) |> restart).start()) + return (managedHoles.0, disposable) } diff --git a/submodules/TelegramCore/Sources/State/ManagedSynchronizePinnedChatsOperations.swift b/submodules/TelegramCore/Sources/State/ManagedSynchronizePinnedChatsOperations.swift index 3e08c37dec..a400c77eda 100644 --- a/submodules/TelegramCore/Sources/State/ManagedSynchronizePinnedChatsOperations.swift +++ b/submodules/TelegramCore/Sources/State/ManagedSynchronizePinnedChatsOperations.swift @@ -128,169 +128,153 @@ private func synchronizePinnedChats(transaction: Transaction, postbox: Postbox, return network.request(Api.functions.messages.getPinnedDialogs(folderId: groupId.rawValue)) |> retryRequest |> mapToSignal { dialogs -> Signal in - var storeMessages: [StoreMessage] = [] - var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:] - var channelStates: [PeerId: Int32] = [:] - var notificationSettings: [PeerId: PeerNotificationSettings] = [:] - var ttlPeriods: [PeerId: CachedPeerAutoremoveTimeout] = [:] - - var remoteItemIds: [PinnedItemId] = [] - - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - - switch dialogs { - case let .peerDialogs(dialogs, messages, chats, users, _): - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers.append(groupOrChannel) - } - } - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } + return postbox.transaction { transaction -> Signal in + var storeMessages: [StoreMessage] = [] + var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:] + var channelStates: [PeerId: Int32] = [:] + var notificationSettings: [PeerId: PeerNotificationSettings] = [:] + var ttlPeriods: [PeerId: CachedPeerAutoremoveTimeout] = [:] - var peerMap: [PeerId: Peer] = [:] - for peer in peers { - peerMap[peer.id] = peer + var remoteItemIds: [PinnedItemId] = [] + + let parsedPeers: AccumulatedPeers + + switch dialogs { + case let .peerDialogs(dialogs, messages, chats, users, _): + parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) + + loop: for dialog in dialogs { + let apiPeer: Api.Peer + let apiReadInboxMaxId: Int32 + let apiReadOutboxMaxId: Int32 + let apiTopMessage: Int32 + let apiUnreadCount: Int32 + let apiMarkedUnread: Bool + var apiChannelPts: Int32? + let apiTtlPeriod: Int32? + let apiNotificationSettings: Api.PeerNotifySettings + switch dialog { + case let .dialog(flags, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, _, _, peerNotificationSettings, pts, _, _, ttlPeriod): + apiPeer = peer + apiTopMessage = topMessage + apiReadInboxMaxId = readInboxMaxId + apiReadOutboxMaxId = readOutboxMaxId + apiUnreadCount = unreadCount + apiMarkedUnread = (flags & (1 << 3)) != 0 + apiNotificationSettings = peerNotificationSettings + apiChannelPts = pts + apiTtlPeriod = ttlPeriod + case .dialogFolder: + //assertionFailure() + continue loop } - loop: for dialog in dialogs { - let apiPeer: Api.Peer - let apiReadInboxMaxId: Int32 - let apiReadOutboxMaxId: Int32 - let apiTopMessage: Int32 - let apiUnreadCount: Int32 - let apiMarkedUnread: Bool - var apiChannelPts: Int32? - let apiTtlPeriod: Int32? - let apiNotificationSettings: Api.PeerNotifySettings - switch dialog { - case let .dialog(flags, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, _, _, peerNotificationSettings, pts, _, _, ttlPeriod): - apiPeer = peer - apiTopMessage = topMessage - apiReadInboxMaxId = readInboxMaxId - apiReadOutboxMaxId = readOutboxMaxId - apiUnreadCount = unreadCount - apiMarkedUnread = (flags & (1 << 3)) != 0 - apiNotificationSettings = peerNotificationSettings - apiChannelPts = pts - apiTtlPeriod = ttlPeriod - case .dialogFolder: - //assertionFailure() - continue loop - } - - let peerId: PeerId = apiPeer.peerId - - remoteItemIds.append(.peer(peerId)) - - if readStates[peerId] == nil { - readStates[peerId] = [:] - } - readStates[peerId]![Namespaces.Message.Cloud] = .idBased(maxIncomingReadId: apiReadInboxMaxId, maxOutgoingReadId: apiReadOutboxMaxId, maxKnownId: apiTopMessage, count: apiUnreadCount, markedUnread: apiMarkedUnread) - - if let apiChannelPts = apiChannelPts { - channelStates[peerId] = apiChannelPts - } - - notificationSettings[peerId] = TelegramPeerNotificationSettings(apiSettings: apiNotificationSettings) - - ttlPeriods[peerId] = .known(apiTtlPeriod.flatMap(CachedPeerAutoremoveTimeout.Value.init(peerValue:))) + let peerId: PeerId = apiPeer.peerId + + remoteItemIds.append(.peer(peerId)) + + if readStates[peerId] == nil { + readStates[peerId] = [:] } + readStates[peerId]![Namespaces.Message.Cloud] = .idBased(maxIncomingReadId: apiReadInboxMaxId, maxOutgoingReadId: apiReadOutboxMaxId, maxKnownId: apiTopMessage, count: apiUnreadCount, markedUnread: apiMarkedUnread) + + if let apiChannelPts = apiChannelPts { + channelStates[peerId] = apiChannelPts + } + + notificationSettings[peerId] = TelegramPeerNotificationSettings(apiSettings: apiNotificationSettings) + + ttlPeriods[peerId] = .known(apiTtlPeriod.flatMap(CachedPeerAutoremoveTimeout.Value.init(peerValue:))) + } for message in messages { var peerIsForum = false - if let peerId = message.peerId, let peer = peerMap[peerId], peer.isForum { + if let peerId = message.peerId, let peer = parsedPeers.get(peerId), peer.isForum { peerIsForum = true } if let storeMessage = StoreMessage(apiMessage: message, peerIsForum: peerIsForum) { storeMessages.append(storeMessage) } } - } - - var resultingItemIds: [PinnedItemId] - if initialRemoteItemIds == localItemIds { - resultingItemIds = remoteItemIds - } else { - let locallyRemovedFromRemoteItemIds = Set(initialRemoteItemIdsWithoutSecretChats).subtracting(Set(localItemIdsWithoutSecretChats)) - let remotelyRemovedItemIds = Set(initialRemoteItemIdsWithoutSecretChats).subtracting(Set(remoteItemIds)) - - resultingItemIds = localItemIds.filter { !remotelyRemovedItemIds.contains($0) } - resultingItemIds.append(contentsOf: remoteItemIds.filter { !locallyRemovedFromRemoteItemIds.contains($0) && !resultingItemIds.contains($0) }) - } - - return postbox.transaction { transaction -> Signal in - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - - transaction.setPinnedItemIds(groupId: groupId, itemIds: resultingItemIds) - - updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) - - transaction.updateCurrentPeerNotificationSettings(notificationSettings) - - for (peerId, autoremoveValue) in ttlPeriods { - transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in - if peerId.namespace == Namespaces.Peer.CloudUser { - let current = (current as? CachedUserData) ?? CachedUserData() - return current.withUpdatedAutoremoveTimeout(autoremoveValue) - } else if peerId.namespace == Namespaces.Peer.CloudChannel { - let current = (current as? CachedChannelData) ?? CachedChannelData() - return current.withUpdatedAutoremoveTimeout(autoremoveValue) - } else if peerId.namespace == Namespaces.Peer.CloudGroup { - let current = (current as? CachedGroupData) ?? CachedGroupData() - return current.withUpdatedAutoremoveTimeout(autoremoveValue) - } else { - return current - } - }) } - var allPeersWithMessages = Set() - for message in storeMessages { - if !allPeersWithMessages.contains(message.id.peerId) { - allPeersWithMessages.insert(message.id.peerId) - } - } - let _ = transaction.addMessages(storeMessages, location: .UpperHistoryBlock) - - transaction.resetIncomingReadStates(readStates) - - for (peerId, pts) in channelStates { - if let _ = transaction.getPeerChatState(peerId) as? ChannelState { - // skip changing state - } else { - transaction.setPeerChatState(peerId, state: ChannelState(pts: pts, invalidatedPts: nil, synchronizedUntilMessageId: nil)) - } - } - - if remoteItemIds == resultingItemIds { - return .complete() + var resultingItemIds: [PinnedItemId] + if initialRemoteItemIds == localItemIds { + resultingItemIds = remoteItemIds } else { - var inputDialogPeers: [Api.InputDialogPeer] = [] - for itemId in resultingItemIds { - switch itemId { - case let .peer(peerId): - if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { - inputDialogPeers.append(Api.InputDialogPeer.inputDialogPeer(peer: inputPeer)) - } + let locallyRemovedFromRemoteItemIds = Set(initialRemoteItemIdsWithoutSecretChats).subtracting(Set(localItemIdsWithoutSecretChats)) + let remotelyRemovedItemIds = Set(initialRemoteItemIdsWithoutSecretChats).subtracting(Set(remoteItemIds)) + + resultingItemIds = localItemIds.filter { !remotelyRemovedItemIds.contains($0) } + resultingItemIds.append(contentsOf: remoteItemIds.filter { !locallyRemovedFromRemoteItemIds.contains($0) && !resultingItemIds.contains($0) }) + } + + return postbox.transaction { transaction -> Signal in + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) + + transaction.setPinnedItemIds(groupId: groupId, itemIds: resultingItemIds) + + transaction.updateCurrentPeerNotificationSettings(notificationSettings) + + for (peerId, autoremoveValue) in ttlPeriods { + transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in + if peerId.namespace == Namespaces.Peer.CloudUser { + let current = (current as? CachedUserData) ?? CachedUserData() + return current.withUpdatedAutoremoveTimeout(autoremoveValue) + } else if peerId.namespace == Namespaces.Peer.CloudChannel { + let current = (current as? CachedChannelData) ?? CachedChannelData() + return current.withUpdatedAutoremoveTimeout(autoremoveValue) + } else if peerId.namespace == Namespaces.Peer.CloudGroup { + let current = (current as? CachedGroupData) ?? CachedGroupData() + return current.withUpdatedAutoremoveTimeout(autoremoveValue) + } else { + return current + } + }) + } + + var allPeersWithMessages = Set() + for message in storeMessages { + if !allPeersWithMessages.contains(message.id.peerId) { + allPeersWithMessages.insert(message.id.peerId) + } + } + let _ = transaction.addMessages(storeMessages, location: .UpperHistoryBlock) + + transaction.resetIncomingReadStates(readStates) + + for (peerId, pts) in channelStates { + if let _ = transaction.getPeerChatState(peerId) as? ChannelState { + // skip changing state + } else { + transaction.setPeerChatState(peerId, state: ChannelState(pts: pts, invalidatedPts: nil, synchronizedUntilMessageId: nil)) } } - return network.request(Api.functions.messages.reorderPinnedDialogs(flags: 1 << 0, folderId: groupId.rawValue, order: inputDialogPeers)) - |> `catch` { _ -> Signal in - return .single(Api.Bool.boolFalse) - } - |> mapToSignal { result -> Signal in - return postbox.transaction { transaction -> Void in + if remoteItemIds == resultingItemIds { + return .complete() + } else { + var inputDialogPeers: [Api.InputDialogPeer] = [] + for itemId in resultingItemIds { + switch itemId { + case let .peer(peerId): + if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { + inputDialogPeers.append(Api.InputDialogPeer.inputDialogPeer(peer: inputPeer)) + } + } + } + + return network.request(Api.functions.messages.reorderPinnedDialogs(flags: 1 << 0, folderId: groupId.rawValue, order: inputDialogPeers)) + |> `catch` { _ -> Signal in + return .single(Api.Bool.boolFalse) + } + |> mapToSignal { result -> Signal in + return postbox.transaction { transaction -> Void in + } } } } + |> switchToLatest } |> switchToLatest } diff --git a/submodules/TelegramCore/Sources/State/MessageReactions.swift b/submodules/TelegramCore/Sources/State/MessageReactions.swift index 8e9fcc62b4..6d1e4a7a40 100644 --- a/submodules/TelegramCore/Sources/State/MessageReactions.swift +++ b/submodules/TelegramCore/Sources/State/MessageReactions.swift @@ -470,6 +470,7 @@ public final class EngineMessageReactionListContext { self.isLoadingMore = true let account = self.account + let accountPeerId = account.peerId let message = self.message let reaction = self.reaction let currentOffset = self.state.nextOffset @@ -500,24 +501,9 @@ public final class EngineMessageReactionListContext { return account.postbox.transaction { transaction -> InternalState in switch result { case let .messageReactionsList(_, count, reactions, chats, users, nextOffset): - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } - for chat in chats { - if let peer = parseTelegramGroupOrChannel(chat: chat) { - peers.append(peer) - } - } - - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) var items: [EngineMessageReactionListContext.Item] = [] for reaction in reactions { diff --git a/submodules/TelegramCore/Sources/State/ProcessSecretChatIncomingDecryptedOperations.swift b/submodules/TelegramCore/Sources/State/ProcessSecretChatIncomingDecryptedOperations.swift index f7c0b76f6c..c6030ec173 100644 --- a/submodules/TelegramCore/Sources/State/ProcessSecretChatIncomingDecryptedOperations.swift +++ b/submodules/TelegramCore/Sources/State/ProcessSecretChatIncomingDecryptedOperations.swift @@ -384,7 +384,7 @@ func processSecretChatIncomingDecryptedOperations(encryptionProvider: Encryption updatedPeer = updatedPeer.withUpdatedEmbeddedState(updatedState.embeddedState.peerState) } if !peer.isEqual(updatedPeer) { - updatePeers(transaction: transaction, peers: [updatedPeer], update: { _, updated in + updatePeersCustom(transaction: transaction, peers: [updatedPeer], update: { _, updated in return updated }) } diff --git a/submodules/TelegramCore/Sources/State/ResetState.swift b/submodules/TelegramCore/Sources/State/ResetState.swift index 521acded1a..2f27ea17e1 100644 --- a/submodules/TelegramCore/Sources/State/ResetState.swift +++ b/submodules/TelegramCore/Sources/State/ResetState.swift @@ -14,7 +14,7 @@ func _internal_resetAccountState(postbox: Postbox, network: Network, accountPeer guard let fetchedChats = fetchedChats else { return .never() } - return withResolvedAssociatedMessages(postbox: postbox, source: .network(network), accountPeerId: accountPeerId, peers: Dictionary(fetchedChats.peers.map({ ($0.id, $0) }), uniquingKeysWith: { lhs, _ in lhs }), storeMessages: fetchedChats.storeMessages, { transaction, additionalPeers, additionalMessages -> Void in + return withResolvedAssociatedMessages(postbox: postbox, source: .network(network), accountPeerId: accountPeerId, parsedPeers: fetchedChats.peers, storeMessages: fetchedChats.storeMessages, { transaction, additionalPeers, additionalMessages -> Void in for peerId in transaction.chatListGetAllPeerIds() { if peerId.namespace != Namespaces.Peer.SecretChat { transaction.updatePeerChatListInclusion(peerId, inclusion: .notIncluded) @@ -40,9 +40,7 @@ func _internal_resetAccountState(postbox: Postbox, network: Network, accountPeer transaction.removeAllChatListEntries(groupId: .root, exceptPeerNamespace: Namespaces.Peer.SecretChat) transaction.removeAllChatListEntries(groupId: .group(1), exceptPeerNamespace: Namespaces.Peer.SecretChat) - updatePeers(transaction: transaction, peers: fetchedChats.peers + additionalPeers, update: { _, updated -> Peer in - return updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: fetchedChats.peers.union(with: additionalPeers)) for (threadMessageId, data) in fetchedChats.threadInfos { if let entry = StoredMessageHistoryThreadInfo(data.data) { @@ -52,7 +50,6 @@ func _internal_resetAccountState(postbox: Postbox, network: Network, accountPeer 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) transaction.updateCurrentPeerNotificationSettings(fetchedChats.notificationSettings) let _ = transaction.addMessages(fetchedChats.storeMessages, location: .UpperHistoryBlock) let _ = transaction.addMessages(additionalMessages, location: .Random) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/AccountData/ChangeAccountPhoneNumber.swift b/submodules/TelegramCore/Sources/TelegramEngine/AccountData/ChangeAccountPhoneNumber.swift index 8e51debb73..44d473bfd2 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/AccountData/ChangeAccountPhoneNumber.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/AccountData/ChangeAccountPhoneNumber.swift @@ -100,6 +100,7 @@ public enum ChangeAccountPhoneNumberError { } func _internal_requestChangeAccountPhoneNumber(account: Account, phoneNumber: String, phoneCodeHash: String, phoneCode: String) -> Signal { + let accountPeerId = account.peerId return account.network.request(Api.functions.account.changePhone(phoneNumber: phoneNumber, phoneCodeHash: phoneCodeHash, phoneCode: phoneCode), automaticFloodWait: false) |> mapError { error -> ChangeAccountPhoneNumberError in if error.errorDescription.hasPrefix("FLOOD_WAIT") { @@ -114,10 +115,7 @@ func _internal_requestChangeAccountPhoneNumber(account: Account, phoneNumber: St } |> mapToSignal { result -> Signal in return account.postbox.transaction { transaction -> Void in - let user = TelegramUser(user: result) - updatePeers(transaction: transaction, peers: [user], update: { _, updated in - return updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(transaction: transaction, chats: [], users: [result])) } |> mapError { _ -> ChangeAccountPhoneNumberError in } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/AccountData/TelegramEngineAccountData.swift b/submodules/TelegramCore/Sources/TelegramEngine/AccountData/TelegramEngineAccountData.swift index c5cfa9f257..a90da63f66 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/AccountData/TelegramEngineAccountData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/AccountData/TelegramEngineAccountData.swift @@ -89,7 +89,7 @@ public extension TelegramEngine { } if let peer = transaction.getPeer(peerId) as? TelegramUser { - updatePeers(transaction: transaction, peers: [peer.withUpdatedEmojiStatus(file.flatMap({ PeerEmojiStatus(fileId: $0.fileId.id, expirationDate: expirationDate) }))], update: { _, updated in + updatePeersCustom(transaction: transaction, peers: [peer.withUpdatedEmojiStatus(file.flatMap({ PeerEmojiStatus(fileId: $0.fileId.id, expirationDate: expirationDate) }))], update: { _, updated in updated }) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/AccountData/UpdateAccountPeerName.swift b/submodules/TelegramCore/Sources/TelegramEngine/AccountData/UpdateAccountPeerName.swift index 096a27bcae..37c828f05f 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/AccountData/UpdateAccountPeerName.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/AccountData/UpdateAccountPeerName.swift @@ -6,6 +6,7 @@ import MtProtoKit func _internal_updateAccountPeerName(account: Account, firstName: String, lastName: String) -> Signal { + let accountPeerId = account.peerId return account.network.request(Api.functions.account.updateProfile(flags: (1 << 0) | (1 << 1), firstName: firstName, lastName: lastName, about: nil)) |> map { result -> Api.User? in return result @@ -16,9 +17,7 @@ func _internal_updateAccountPeerName(account: Account, firstName: String, lastNa |> mapToSignal { result -> Signal in return account.postbox.transaction { transaction -> Void in if let result = result { - if let peer = TelegramUser.merge(transaction.getPeer(result.peerId) as? TelegramUser, rhs: result) { - updatePeers(transaction: transaction, peers: [peer], update: { $1 }) - } + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(transaction: transaction, chats: [], users: [result])) } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift b/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift index 2de645d41e..699e561906 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift @@ -84,6 +84,7 @@ public enum GetCurrentGroupCallError { } func _internal_getCurrentGroupCall(account: Account, callId: Int64, accessHash: Int64, peerId: PeerId? = nil) -> Signal { + let accountPeerId = account.peerId return account.network.request(Api.functions.phone.getGroupCall(call: .inputGroupCall(id: callId, accessHash: accessHash), limit: 4)) |> mapError { _ -> GetCurrentGroupCallError in return .generic @@ -96,20 +97,7 @@ func _internal_getCurrentGroupCall(account: Account, callId: Int64, accessHash: return nil } - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } - - for chat in chats { - if let peer = parseTelegramGroupOrChannel(chat: chat) { - peers.append(peer) - } - } + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) if let peerId = peerId { transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in if let cachedData = current as? CachedChannelData { @@ -122,10 +110,7 @@ func _internal_getCurrentGroupCall(account: Account, callId: Int64, accessHash: }) } - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) let parsedParticipants = participants.compactMap { GroupCallParticipantsContext.Participant($0, transaction: transaction) } return GroupCallSummary( @@ -340,6 +325,8 @@ public enum GetGroupCallParticipantsError { } func _internal_getGroupCallParticipants(account: Account, callId: Int64, accessHash: Int64, offset: String, ssrcs: [UInt32], limit: Int32, sortAscending: Bool?) -> Signal { + let accountPeerId = account.peerId + let sortAscendingValue: Signal<(Bool, Int32?, Bool, GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?, Bool, Int, Bool), GetGroupCallParticipantsError> sortAscendingValue = _internal_getCurrentGroupCall(account: account, callId: callId, accessHash: accessHash) @@ -380,25 +367,8 @@ func _internal_getGroupCallParticipants(account: Account, callId: Int64, accessH nextParticipantsFetchOffset = nil } - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } - - for chat in chats { - if let peer = parseTelegramGroupOrChannel(chat: chat) { - peers.append(peer) - } - } - - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences) + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) parsedParticipants = participants.compactMap { GroupCallParticipantsContext.Participant($0, transaction: transaction) } } @@ -587,18 +557,7 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, state.sortAscending = parsedCall.sortAscending - let apiUsers: [Api.User] = [] - state.adminIds = Set(peerAdminIds) - - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - - for user in apiUsers { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } let connectionMode: JoinGroupCallResult.ConnectionMode if let clientParamsData = parsedClientParams.data(using: .utf8), let dict = (try? JSONSerialization.jsonObject(with: clientParamsData, options: [])) as? [String: Any] { @@ -681,11 +640,6 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, state.participants.sort(by: { GroupCallParticipantsContext.Participant.compare(lhs: $0, rhs: $1, sortAscending: state.sortAscending) }) - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences) - return JoinGroupCallResult( callInfo: parsedCall, state: state, @@ -805,7 +759,7 @@ func _internal_stopGroupCall(account: Account, peerId: PeerId, callId: Int64, ac flags.remove(.hasVoiceChat) flags.remove(.hasActiveVoiceChat) peer = peer.withUpdatedFlags(flags) - updatePeers(transaction: transaction, peers: [peer], update: { _, updated in + updatePeersCustom(transaction: transaction, peers: [peer], update: { _, updated in return updated }) } @@ -814,7 +768,7 @@ func _internal_stopGroupCall(account: Account, peerId: PeerId, callId: Int64, ac flags.remove(.hasVoiceChat) flags.remove(.hasActiveVoiceChat) peer = peer.updateFlags(flags: flags, version: peer.version) - updatePeers(transaction: transaction, peers: [peer], update: { _, updated in + updatePeersCustom(transaction: transaction, peers: [peer], update: { _, updated in return updated }) } @@ -2275,10 +2229,11 @@ func _internal_editGroupCallTitle(account: Account, callId: Int64, accessHash: I } } -func _internal_groupCallDisplayAsAvailablePeers(network: Network, postbox: Postbox, peerId: PeerId) -> Signal<[FoundPeer], NoError> { +func _internal_groupCallDisplayAsAvailablePeers(accountPeerId: PeerId, network: Network, postbox: Postbox, peerId: PeerId) -> Signal<[FoundPeer], NoError> { return postbox.transaction { transaction -> Api.InputPeer? in return transaction.getPeer(peerId).flatMap(apiInputPeer) - } |> mapToSignal { inputPeer in + } + |> mapToSignal { inputPeer in guard let inputPeer = inputPeer else { return .complete() } @@ -2287,34 +2242,39 @@ func _internal_groupCallDisplayAsAvailablePeers(network: Network, postbox: Postb |> `catch` { _ -> Signal in return .single(nil) } - |> mapToSignal { result in + |> mapToSignal { result -> Signal<[FoundPeer], NoError> in guard let result = result else { return .single([]) } switch result { - case let .joinAsPeers(_, chats, _): - var subscribers: [PeerId: Int32] = [:] - let peers = chats.compactMap(parseTelegramGroupOrChannel) - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - switch chat { - case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _): - if let participantsCount = participantsCount { + case let .joinAsPeers(_, chats, users): + return postbox.transaction { transaction -> [FoundPeer] in + var subscribers: [PeerId: Int32] = [:] + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) + for chat in chats { + if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { + switch chat { + case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _): + if let participantsCount = participantsCount { + subscribers[groupOrChannel.id] = participantsCount + } + case let .chat(_, _, _, _, participantsCount, _, _, _, _, _): subscribers[groupOrChannel.id] = participantsCount + default: + break } - case let .chat(_, _, _, _, participantsCount, _, _, _, _, _): - subscribers[groupOrChannel.id] = participantsCount - default: - break } } - } - return postbox.transaction { transaction -> [Peer] in - updatePeers(transaction: transaction, peers: peers, update: { _, updated in - return updated - }) - return peers - } |> map { peers -> [FoundPeer] in + + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) + + var peers: [Peer] = [] + for chat in chats { + if let peer = transaction.getPeer(chat.peerId) { + peers.append(peer) + } + } + return peers.map { FoundPeer(peer: $0, subscribers: subscribers[$0.id]) } } } @@ -2382,7 +2342,7 @@ func _internal_cachedGroupCallDisplayAsAvailablePeers(account: Account, peerId: if let (cachedPeers, timestamp) = cachedPeersAndTimestamp, currentTimestamp - timestamp < 60 * 3 && !cachedPeers.isEmpty { return .single(cachedPeers) } else { - return _internal_groupCallDisplayAsAvailablePeers(network: account.network, postbox: account.postbox, peerId: peerId) + return _internal_groupCallDisplayAsAvailablePeers(accountPeerId: account.peerId, network: account.network, postbox: account.postbox, peerId: peerId) |> mapToSignal { peers -> Signal<[FoundPeer], NoError> in return account.postbox.transaction { transaction -> [FoundPeer] in let currentTimestamp = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Contacts/ContactManagement.swift b/submodules/TelegramCore/Sources/TelegramEngine/Contacts/ContactManagement.swift index 17d6c2e06f..14d7948a78 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Contacts/ContactManagement.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Contacts/ContactManagement.swift @@ -12,30 +12,21 @@ private func md5(_ data: Data) -> Data { } } -private func updatedRemoteContactPeers(network: Network, hash: Int64) -> Signal<([Peer], [PeerId: PeerPresence], Int32)?, NoError> { +private func updatedRemoteContactPeers(network: Network, hash: Int64) -> Signal<(AccumulatedPeers, Int32)?, NoError> { return network.request(Api.functions.contacts.getContacts(hash: hash), automaticFloodWait: false) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) } - |> map { result -> ([Peer], [PeerId: PeerPresence], Int32)? in + |> map { result -> (AccumulatedPeers, Int32)? in guard let result = result else { return nil } switch result { - case .contactsNotModified: - return nil - case let .contacts(_, savedCount, users): - var peers: [Peer] = [] - var peerPresences: [PeerId: PeerPresence] = [:] - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - if let presence = TelegramUserPresence(apiUser: user) { - peerPresences[telegramUser.id] = presence - } - } - return (peers, peerPresences, savedCount) + case .contactsNotModified: + return nil + case let .contacts(_, savedCount, users): + return (AccumulatedPeers(users: users), savedCount) } } } @@ -60,29 +51,28 @@ func syncContactsOnce(network: Network, postbox: Postbox, accountPeerId: PeerId) } let updatedPeers = initialContactPeerIdsHash - |> mapToSignal { hash -> Signal<([Peer], [PeerId: PeerPresence], Int32)?, NoError> in + |> mapToSignal { hash -> Signal<(AccumulatedPeers, Int32)?, NoError> in return updatedRemoteContactPeers(network: network, hash: hash) } let appliedUpdatedPeers = updatedPeers |> mapToSignal { peersAndPresences -> Signal in - if let (peers, peerPresences, totalCount) = peersAndPresences { + if let (peers, totalCount) = peersAndPresences { return postbox.transaction { transaction -> Signal in let previousIds = transaction.getContactPeerIds() let wasEmpty = previousIds.isEmpty transaction.replaceRemoteContactCount(totalCount) - updatePeerPresencesClean(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) - if wasEmpty { + let users = Array(peers.users.values) var insertSignal: Signal = .complete() - for s in stride(from: 0, to: peers.count, by: 500) { - let partPeers = Array(peers[s ..< min(s + 500, peers.count)]) + for s in stride(from: 0, to: users.count, by: 500) { + let partUsers = Array(users[s ..< min(s + 500, users.count)]) let partSignal = postbox.transaction { transaction -> Void in - updatePeers(transaction: transaction, peers: partPeers, update: { return $1 }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: partUsers)) var updatedIds = transaction.getContactPeerIds() - updatedIds.formUnion(partPeers.map { $0.id }) + updatedIds.formUnion(partUsers.map { $0.peerId }) transaction.replaceContactPeerIds(updatedIds) } |> delay(0.1, queue: Queue.concurrentDefaultQueue()) @@ -91,7 +81,7 @@ func syncContactsOnce(network: Network, postbox: Postbox, accountPeerId: PeerId) return insertSignal } else { - transaction.replaceContactPeerIds(Set(peers.map { $0.id })) + transaction.replaceContactPeerIds(Set(peers.users.keys)) return .complete() } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Contacts/ImportContact.swift b/submodules/TelegramCore/Sources/TelegramEngine/Contacts/ImportContact.swift index 18a63990e4..ce9d96f5a7 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Contacts/ImportContact.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Contacts/ImportContact.swift @@ -4,6 +4,8 @@ import SwiftSignalKit func _internal_importContact(account: Account, firstName: String, lastName: String, phoneNumber: String) -> Signal { + let accountPeerId = account.peerId + let input = Api.InputContact.inputPhoneContact(clientId: 1, phone: phoneNumber, firstName: firstName, lastName: lastName) return account.network.request(Api.functions.contacts.importContacts(contacts: [input])) @@ -17,11 +19,10 @@ func _internal_importContact(account: Account, firstName: String, lastName: Stri switch result { case let .importedContacts(_, _, _, users): if let first = users.first { - let user = TelegramUser(user: first) - let peerId = user.id - updatePeers(transaction: transaction, peers: [user], update: { _, updated in - return updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users)) + + let peerId = first.peerId + var peerIds = transaction.getContactPeerIds() if !peerIds.contains(peerId) { peerIds.insert(peerId) @@ -41,6 +42,8 @@ public enum AddContactError { } func _internal_addContactInteractively(account: Account, peerId: PeerId, firstName: String, lastName: String, phoneNumber: String, addToPrivacyExceptions: Bool) -> Signal { + let accountPeerId = account.peerId + return account.postbox.transaction { transaction -> (Api.InputUser, String)? in if let user = transaction.getPeer(peerId) as? TelegramUser, let inputUser = apiInputUser(user) { return (inputUser, user.phone == nil ? phoneNumber : "") @@ -63,22 +66,16 @@ func _internal_addContactInteractively(account: Account, peerId: PeerId, firstNa } |> mapToSignal { result -> Signal in return account.postbox.transaction { transaction -> Void in - var peers: [Peer] = [] + var peers = AccumulatedPeers() switch result { - case let .updates(_, users, _, _, _): - for user in users { - peers.append(TelegramUser(user: user)) - } - case let .updatesCombined(_, users, _, _, _, _): - for user in users { - peers.append(TelegramUser(user: user)) - } - default: - break + case let .updates(_, users, _, _, _): + peers = AccumulatedPeers(users: users) + case let .updatesCombined(_, users, _, _, _, _): + peers = AccumulatedPeers(users: users) + default: + break } - updatePeers(transaction: transaction, peers: peers, update: { _, updated in - return updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: peers) var peerIds = transaction.getContactPeerIds() if !peerIds.contains(peerId) { peerIds.insert(peerId) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift index 9a3307aedb..3244d7539d 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift @@ -442,6 +442,8 @@ private class AdMessagesHistoryContextImpl { self.queue = queue self.account = account self.peerId = peerId + + let accountPeerId = account.peerId self.stateValue = State(interPostInterval: nil, messages: []) @@ -478,25 +480,8 @@ private class AdMessagesHistoryContextImpl { return account.postbox.transaction { transaction -> (interPostInterval: Int32?, messages: [Message]) in switch result { case let .sponsoredMessages(_, postsBetween, messages, chats, users): - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers.append(groupOrChannel) - } - } - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } - - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - - updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences) + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) var parsedMessages: [CachedMessage] = [] diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AttachMenuBots.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AttachMenuBots.swift index 875ea751dd..8b8876f272 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AttachMenuBots.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AttachMenuBots.swift @@ -269,7 +269,7 @@ private func removeCachedAttachMenuBot(postbox: Postbox, botId: PeerId) -> Signa } } -func managedSynchronizeAttachMenuBots(postbox: Postbox, network: Network, force: Bool = false) -> Signal { +func managedSynchronizeAttachMenuBots(accountPeerId: PeerId, postbox: Postbox, network: Network, force: Bool = false) -> Signal { let poll = Signal { subscriber in let signal: Signal = cachedAttachMenuBots(postbox: postbox) |> mapToSignal { current in @@ -285,14 +285,7 @@ func managedSynchronizeAttachMenuBots(postbox: Postbox, network: Network, force: return postbox.transaction { transaction -> Void in switch result { case let .attachMenuBots(hash, bots, users): - var peers: [Peer] = [] - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - } - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users)) var resultBots: [AttachMenuBots.Bot] = [] for bot in bots { @@ -368,7 +361,7 @@ public enum AddBotToAttachMenuError { } -func _internal_addBotToAttachMenu(postbox: Postbox, network: Network, botId: PeerId, allowWrite: Bool) -> Signal { +func _internal_addBotToAttachMenu(accountPeerId: PeerId, postbox: Postbox, network: Network, botId: PeerId, allowWrite: Bool) -> Signal { return postbox.transaction { transaction -> Signal in guard let peer = transaction.getPeer(botId), let inputUser = apiInputUser(peer) else { return .complete() @@ -391,7 +384,7 @@ func _internal_addBotToAttachMenu(postbox: Postbox, network: Network, botId: Pee } |> mapToSignal { value -> Signal in if value { - return managedSynchronizeAttachMenuBots(postbox: postbox, network: network, force: true) + return managedSynchronizeAttachMenuBots(accountPeerId: accountPeerId, postbox: postbox, network: network, force: true) |> castError(AddBotToAttachMenuError.self) |> take(1) |> map { _ -> Bool in @@ -406,7 +399,7 @@ func _internal_addBotToAttachMenu(postbox: Postbox, network: Network, botId: Pee |> switchToLatest } -func _internal_removeBotFromAttachMenu(postbox: Postbox, network: Network, botId: PeerId) -> Signal { +func _internal_removeBotFromAttachMenu(accountPeerId: PeerId, postbox: Postbox, network: Network, botId: PeerId) -> Signal { return postbox.transaction { transaction -> Signal in guard let peer = transaction.getPeer(botId), let inputUser = apiInputUser(peer) else { return .complete() @@ -424,7 +417,7 @@ func _internal_removeBotFromAttachMenu(postbox: Postbox, network: Network, botId return .single(false) } |> afterCompleted { - let _ = (managedSynchronizeAttachMenuBots(postbox: postbox, network: network, force: true) + let _ = (managedSynchronizeAttachMenuBots(accountPeerId: accountPeerId, postbox: postbox, network: network, force: true) |> take(1)).start(completed: { let _ = removeCachedAttachMenuBot(postbox: postbox, botId: botId) }) @@ -468,7 +461,7 @@ public enum GetAttachMenuBotError { case generic } -func _internal_getAttachMenuBot(postbox: Postbox, network: Network, botId: PeerId, cached: Bool) -> Signal { +func _internal_getAttachMenuBot(accountPeerId: PeerId, postbox: Postbox, network: Network, botId: PeerId, cached: Bool) -> Signal { return postbox.transaction { transaction -> Signal in if cached, let cachedBots = cachedAttachMenuBots(transaction: transaction)?.bots { if let bot = cachedBots.first(where: { $0.peerId == botId }), let peer = transaction.getPeer(bot.peerId) { @@ -487,19 +480,14 @@ func _internal_getAttachMenuBot(postbox: Postbox, network: Network, botId: PeerI return postbox.transaction { transaction -> Signal in switch result { case let .attachMenuBotsBot(bot, users): - var peers: [Peer] = [] var peer: Peer? for user in users { let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - if telegramUser.id == botId { peer = telegramUser } } - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users)) guard let peer = peer else { return .fail(.generic) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift index 8fbfbca7e5..6a765d40c7 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift @@ -7,6 +7,7 @@ public final class EngineChatList: Equatable { } public typealias MessageTagSummaryInfo = ChatListMessageTagSummaryInfo + public typealias StoryStats = PeerStoryStats public enum PinnedItem { public typealias Id = PinnedItemId @@ -127,6 +128,7 @@ public final class EngineChatList: Equatable { public let hasFailed: Bool public let isContact: Bool public let autoremoveTimeout: Int32? + public let storyStats: StoryStats? public init( id: Id, @@ -144,7 +146,8 @@ public final class EngineChatList: Equatable { topForumTopicItems: [EngineChatList.ForumTopicData], hasFailed: Bool, isContact: Bool, - autoremoveTimeout: Int32? + autoremoveTimeout: Int32?, + storyStats: StoryStats? ) { self.id = id self.index = index @@ -162,6 +165,7 @@ public final class EngineChatList: Equatable { self.hasFailed = hasFailed self.isContact = isContact self.autoremoveTimeout = autoremoveTimeout + self.storyStats = storyStats } public static func ==(lhs: Item, rhs: Item) -> Bool { @@ -213,6 +217,9 @@ public final class EngineChatList: Equatable { if lhs.autoremoveTimeout != rhs.autoremoveTimeout { return false } + if lhs.storyStats != rhs.storyStats { + return false + } return true } } @@ -414,7 +421,21 @@ public extension EngineChatList.RelativePosition { extension EngineChatList.Item { convenience init?(_ entry: ChatListEntry) { switch entry { - case let .MessageEntry(index, messages, readState, isRemovedFromTotalUnreadCount, embeddedState, renderedPeer, presence, tagSummaryInfo, forumTopicData, topForumTopics, hasFailed, isContact, autoremoveTimeout): + case let .MessageEntry(entryData): + let index = entryData.index + let messages = entryData.messages + let readState = entryData.readState + let isRemovedFromTotalUnreadCount = entryData.isRemovedFromTotalUnreadCount + let embeddedState = entryData.embeddedInterfaceState + let renderedPeer = entryData.renderedPeer + let presence = entryData.presence + let tagSummaryInfo = entryData.summaryInfo + let forumTopicData = entryData.forumTopicData + let topForumTopics = entryData.topForumTopics + let hasFailed = entryData.hasFailed + let isContact = entryData.isContact + let autoremoveTimeout = entryData.autoremoveTimeout + var draft: EngineChatList.Draft? if let embeddedState = embeddedState, let _ = embeddedState.overrideChatTimestamp { if let opaqueState = _internal_decodeStoredChatInterfaceState(state: embeddedState) { @@ -482,7 +503,8 @@ extension EngineChatList.Item { topForumTopicItems: topForumTopicItems, hasFailed: hasFailed, isContact: isContact, - autoremoveTimeout: autoremoveTimeout + autoremoveTimeout: autoremoveTimeout, + storyStats: entryData.storyStats ) case .HoleEntry: return nil diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ClearCloudDrafts.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ClearCloudDrafts.swift index c37a0516a9..6ac6b5101d 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ClearCloudDrafts.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ClearCloudDrafts.swift @@ -16,18 +16,8 @@ func _internal_clearCloudDraftsInteractively(postbox: Postbox, network: Network, var keys = Set() switch updates { case let .updates(updates, users, chats, _, _): - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers.append(groupOrChannel) - } - } - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) + for update in updates { switch update { case let .updateDraftMessage(_, peer, topMsgId, _): @@ -40,11 +30,8 @@ func _internal_clearCloudDraftsInteractively(postbox: Postbox, network: Network, break } } - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) - updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) var signals: [Signal] = [] for key in keys { _internal_updateChatInputState(transaction: transaction, peerId: key.peerId, threadId: key.threadId, inputState: nil) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/LoadMessagesIfNecessary.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/LoadMessagesIfNecessary.swift index 6e9a57f288..e9cd4967be 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/LoadMessagesIfNecessary.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/LoadMessagesIfNecessary.swift @@ -92,23 +92,8 @@ func _internal_getMessagesLoadIfNecessary(_ messageIds: [MessageId], postbox: Po _ = transaction.addMessages(storeMessages, location: .Random) } - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers.append(groupOrChannel) - } - } - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } - - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) } var loadedMessages:[Message] = [] for messageId in missingMessageIds { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Polls.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Polls.swift index 7786e3a495..1ddb77458b 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Polls.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Polls.swift @@ -250,6 +250,7 @@ private final class PollResultsOptionContext { let messageId = self.messageId let opaqueIdentifier = self.opaqueIdentifier let account = self.account + let accountPeerId = account.peerId let nextOffset = self.nextOffset let populateCache = self.populateCache self.disposable.set((self.account.postbox.transaction { transaction -> Api.InputPeer? in @@ -273,18 +274,8 @@ private final class PollResultsOptionContext { } switch result { case let .votesList(_, count, votes, chats, users, nextOffset): - var peers: [Peer] = [] - for apiChat in chats { - if let peer = parseTelegramGroupOrChannel(chat: apiChat) { - peers.append(peer) - } - } - for apiUser in users { - peers.append(TelegramUser(user: apiUser)) - } - updatePeers(transaction: transaction, peers: peers, update: { _, updated in - return updated - }) + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) var resultPeers: [RenderedPeer] = [] for vote in votes { let peerId: PeerId diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift index 2aaa666cd2..cb822f1204 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift @@ -138,6 +138,8 @@ private class ReplyThreadHistoryContextImpl { } }) + let accountPeerId = account.peerId + let updateInitialState: Signal = account.postbox.transaction { transaction -> Peer? in return transaction.getPeer(data.messageId.peerId) } @@ -176,27 +178,11 @@ private class ReplyThreadHistoryContextImpl { } } - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers.append(groupOrChannel) - } - } - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) let _ = transaction.addMessages(parsedMessages, location: .Random) - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - - updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) let resolvedMaxMessage: MessageId? if let maxId = maxId { @@ -608,6 +594,8 @@ public enum FetchChannelReplyThreadMessageError { } func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: MessageId, atMessageId: MessageId?) -> Signal { + let accountPeerId = account.peerId + return account.postbox.transaction { transaction -> Peer? in return transaction.getPeer(messageId.peerId) } @@ -653,27 +641,11 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa } } - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers.append(groupOrChannel) - } - } - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) let _ = transaction.addMessages(parsedMessages, location: .Random) - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - - updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) let resolvedMaxMessage: MessageId? if let maxId = maxId { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift index 9df0d21579..63bb3ba7a4 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift @@ -545,7 +545,7 @@ func _internal_downloadMessage(postbox: Postbox, network: Network, messageId: Me } } -func fetchRemoteMessage(postbox: Postbox, source: FetchMessageHistoryHoleSource, message: MessageReference) -> Signal { +func fetchRemoteMessage(accountPeerId: PeerId, postbox: Postbox, source: FetchMessageHistoryHoleSource, message: MessageReference) -> Signal { guard case let .message(peer, _, id, _, _, _) = message.content else { return .single(nil) } @@ -597,28 +597,13 @@ func fetchRemoteMessage(postbox: Postbox, source: FetchMessageHistoryHoleSource, } return postbox.transaction { transaction -> Message? in - var peers: [PeerId: Peer] = [:] - - for user in users { - if let user = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) { - peers[user.id] = user - } - } - - for chat in chats { - if let groupOrChannel = mergeGroupOrChannel(lhs: transaction.getPeer(chat.peerId), rhs: chat) { - peers[groupOrChannel.id] = groupOrChannel - } - } - - updatePeers(transaction: transaction, peers: Array(peers.values), update: { _, updated in - return updated - }) + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) var renderedMessages: [Message] = [] for message in messages { var peerIsForum = false - if let peerId = message.peerId, let peer = transaction.getPeer(peerId), peer.isForum { + if let peerId = message.peerId, let peer = transaction.getPeer(peerId) ?? parsedPeers.get(peerId), peer.isForum { peerIsForum = true } if let message = StoreMessage(apiMessage: message, peerIsForum: peerIsForum, namespace: id.namespace), case let .Id(updatedId) = message.id { @@ -633,6 +618,12 @@ func fetchRemoteMessage(postbox: Postbox, source: FetchMessageHistoryHoleSource, } } + var peers: [PeerId: Peer] = [:] + for id in parsedPeers.allIds { + if let peer = transaction.getPeer(id) { + peers[peer.id] = peer + } + } if !addedExisting, let renderedMessage = locallyRenderedMessage(message: message, peers: peers) { renderedMessages.append(renderedMessage) } @@ -756,7 +747,7 @@ public enum UpdatedRemotePeerError { case generic } -func _internal_updatedRemotePeer(postbox: Postbox, network: Network, peer: PeerReference) -> Signal { +func _internal_updatedRemotePeer(accountPeerId: PeerId, postbox: Postbox, network: Network, peer: PeerReference) -> Signal { if let inputUser = peer.inputUser { return network.request(Api.functions.users.getUsers(id: [inputUser])) |> mapError { _ -> UpdatedRemotePeerError in @@ -767,12 +758,10 @@ func _internal_updatedRemotePeer(postbox: Postbox, network: Network, peer: PeerR return .fail(.generic) } return postbox.transaction { transaction -> Peer? in - guard let peer = TelegramUser.merge(transaction.getPeer(apiUser.peerId) as? TelegramUser, rhs: apiUser) else { - return nil - } - updatePeers(transaction: transaction, peers: [peer], update: { _, updated in - return updated - }) + let parsedPeers = AccumulatedPeers(users: [apiUser]) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) + + let peer = transaction.getPeer(apiUser.peerId) return peer } |> castError(UpdatedRemotePeerError.self) @@ -790,25 +779,30 @@ func _internal_updatedRemotePeer(postbox: Postbox, network: Network, peer: PeerR return .generic } |> mapToSignal { result -> Signal in - let chats: [Api.Chat] - switch result { + return postbox.transaction { transaction -> Signal in + let chats: [Api.Chat] + switch result { case let .chats(c): chats = c case let .chatsSlice(_, c): chats = c - } - if let updatedPeer = chats.first.flatMap(parseTelegramGroupOrChannel), updatedPeer.id == peer.id { - return postbox.transaction { transaction -> Peer in - updatePeers(transaction: transaction, peers: [updatedPeer], update: { _, updated in - return updated - }) - return updatedPeer } - |> mapError { _ -> UpdatedRemotePeerError in + + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: []) + + if let firstId = chats.first?.peerId, let updatedPeer = parsedPeers.get(firstId), updatedPeer.id == peer.id { + return postbox.transaction { transaction -> Peer in + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) + return updatedPeer + } + |> mapError { _ -> UpdatedRemotePeerError in + } + } else { + return .fail(.generic) } - } else { - return .fail(.generic) } + |> castError(UpdatedRemotePeerError.self) + |> switchToLatest } } else if let inputChannel = peer.inputChannel { return network.request(Api.functions.channels.getChannels(id: [inputChannel])) @@ -816,25 +810,30 @@ func _internal_updatedRemotePeer(postbox: Postbox, network: Network, peer: PeerR return .generic } |> mapToSignal { result -> Signal in - let chats: [Api.Chat] - switch result { + return postbox.transaction { transaction -> Signal in + let chats: [Api.Chat] + switch result { case let .chats(c): chats = c case let .chatsSlice(_, c): chats = c - } - if let updatedPeer = chats.first.flatMap(parseTelegramGroupOrChannel), updatedPeer.id == peer.id { - return postbox.transaction { transaction -> Peer in - updatePeers(transaction: transaction, peers: [updatedPeer], update: { _, updated in - return updated - }) - return updatedPeer } - |> mapError { _ -> UpdatedRemotePeerError in + + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: []) + + if let firstId = chats.first?.peerId, let updatedPeer = parsedPeers.get(firstId), updatedPeer.id == peer.id { + return postbox.transaction { transaction -> Peer in + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) + return updatedPeer + } + |> mapError { _ -> UpdatedRemotePeerError in + } + } else { + return .fail(.generic) } - } else { - return .fail(.generic) } + |> castError(UpdatedRemotePeerError.self) + |> switchToLatest } } else { return .fail(.generic) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SendAsPeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SendAsPeers.swift index b384bec415..457b179e8d 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SendAsPeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SendAsPeers.swift @@ -77,7 +77,7 @@ func _internal_cachedPeerSendAsAvailablePeers(account: Account, peerId: PeerId) initialSignal = .complete() } return initialSignal - |> then(_internal_peerSendAsAvailablePeers(network: account.network, postbox: account.postbox, peerId: peerId) + |> then(_internal_peerSendAsAvailablePeers(accountPeerId: account.peerId, network: account.network, postbox: account.postbox, peerId: peerId) |> mapToSignal { peers -> Signal<[SendAsPeer], NoError> in return account.postbox.transaction { transaction -> [SendAsPeer] in let currentTimestamp = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) @@ -97,7 +97,7 @@ func _internal_cachedPeerSendAsAvailablePeers(account: Account, peerId: PeerId) } -func _internal_peerSendAsAvailablePeers(network: Network, postbox: Postbox, peerId: PeerId) -> Signal<[SendAsPeer], NoError> { +func _internal_peerSendAsAvailablePeers(accountPeerId: PeerId, network: Network, postbox: Postbox, peerId: PeerId) -> Signal<[SendAsPeer], NoError> { return postbox.transaction { transaction -> Peer? in return transaction.getPeer(peerId) } @@ -119,40 +119,44 @@ func _internal_peerSendAsAvailablePeers(network: Network, postbox: Postbox, peer |> `catch` { _ -> Signal in return .single(nil) } - |> mapToSignal { result in + |> mapToSignal { result -> Signal<[SendAsPeer], NoError> in guard let result = result else { return .single([]) } switch result { case let .sendAsPeers(sendAsPeers, chats, _): - var subscribers: [PeerId: Int32] = [:] - let peers = chats.compactMap(parseTelegramGroupOrChannel) - var premiumRequiredPeerIds = Set() - for sendAsPeer in sendAsPeers { - if case let .sendAsPeer(flags, peer) = sendAsPeer, (flags & (1 << 0)) != 0 { - premiumRequiredPeerIds.insert(peer.peerId) - } - } - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - switch chat { - case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _): - if let participantsCount = participantsCount { - subscribers[groupOrChannel.id] = participantsCount - } - case let .chat(_, _, _, _, participantsCount, _, _, _, _, _): - subscribers[groupOrChannel.id] = participantsCount - default: - break + return postbox.transaction { transaction -> [SendAsPeer] in + var subscribers: [PeerId: Int32] = [:] + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: []) + + var premiumRequiredPeerIds = Set() + for sendAsPeer in sendAsPeers { + if case let .sendAsPeer(flags, peer) = sendAsPeer, (flags & (1 << 0)) != 0 { + premiumRequiredPeerIds.insert(peer.peerId) + } + } + for chat in chats { + if let groupOrChannel = parsedPeers.get(chat.peerId) { + switch chat { + case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _): + if let participantsCount = participantsCount { + subscribers[groupOrChannel.id] = participantsCount + } + case let .chat(_, _, _, _, participantsCount, _, _, _, _, _): + subscribers[groupOrChannel.id] = participantsCount + default: + break + } + } + } + updatePeers(transaction: transaction, accountPeerId: accountPeerId,peers: parsedPeers) + + var peers: [Peer] = [] + for chat in chats { + if let peer = transaction.getPeer(chat.peerId) { + peers.append(peer) } } - } - return postbox.transaction { transaction -> [Peer] in - updatePeers(transaction: transaction, peers: peers, update: { _, updated in - return updated - }) - return peers - } |> map { peers -> [SendAsPeer] in return peers.map { SendAsPeer(peer: $0, subscribers: subscribers[$0.id], isPremiumRequired: premiumRequiredPeerIds.contains($0.id)) } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift index c7a47218e9..4467e1b48d 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift @@ -801,6 +801,7 @@ public final class SparseMessageCalendar { } let account = self.account + let accountPeerId = account.peerId let peerId = self.peerId let messageTag = self.messageTag self.disposable.set((self.account.postbox.transaction { transaction -> Peer? in @@ -830,19 +831,8 @@ public final class SparseMessageCalendar { switch result { case let .searchResultsCalendar(_, _, minDate, minMsgId, _, periods, messages, chats, users): var parsedMessages: [StoreMessage] = [] - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers.append(groupOrChannel) - } - } - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } + + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) for message in messages { if let parsedMessage = StoreMessage(apiMessage: message, peerIsForum: peer.isForum) { @@ -850,8 +840,7 @@ public final class SparseMessageCalendar { } } - updatePeers(transaction: transaction, peers: peers, update: { _, updated in updated }) - updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) let _ = transaction.addMessages(parsedMessages, location: .Random) var minMessageId: Int32? diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift index 7829f42b90..fa142d88ff 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift @@ -410,8 +410,8 @@ public enum Stories { } public final class PeerState: Equatable, Codable { - private enum CodingKeys: CodingKey { - case maxReadId + private enum CodingKeys: String, CodingKey { + case maxReadId = "rid" } public let maxReadId: Int32 @@ -576,6 +576,12 @@ public final class EngineStorySubscriptions: Equatable { } } +extension Stories.PeerState { + var postboxRepresentation: StoredStoryPeerState { + return StoredStoryPeerState(entry: CodableEntry(self)!, maxSeenId: self.maxReadId) + } +} + public enum StoryUploadResult { case progress(Float) case completed(Int32?) @@ -1121,7 +1127,7 @@ func _internal_markStoryAsSeen(account: Account, peerId: PeerId, id: Int32, asPi return .complete() } - #if DEBUG && false + #if DEBUG && true if "".isEmpty { return .complete() } @@ -1135,10 +1141,10 @@ func _internal_markStoryAsSeen(account: Account, peerId: PeerId, id: Int32, asPi } } else { return account.postbox.transaction { transaction -> Api.InputUser? in - if let peerStoryState = transaction.getPeerStoryState(peerId: peerId)?.get(Stories.PeerState.self) { - transaction.setPeerStoryState(peerId: peerId, state: CodableEntry(Stories.PeerState( + if let peerStoryState = transaction.getPeerStoryState(peerId: peerId)?.entry.get(Stories.PeerState.self) { + transaction.setPeerStoryState(peerId: peerId, state: Stories.PeerState( maxReadId: max(peerStoryState.maxReadId, id) - ))) + ).postboxRepresentation) } return transaction.getPeer(peerId).flatMap(apiInputUser) @@ -1352,19 +1358,7 @@ func _internal_getStoriesById(accountPeerId: PeerId, postbox: Postbox, network: return postbox.transaction { transaction -> [Stories.StoredItem] in switch result { case let .stories(_, stories, users): - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } - - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users)) return stories.compactMap { apiStoryItem -> Stories.StoredItem? in return Stories.StoredItem(apiStoryItem: apiStoryItem, peerId: peer.id, transaction: transaction) @@ -1395,19 +1389,7 @@ func _internal_getStoriesById(accountPeerId: PeerId, postbox: Postbox, source: F return postbox.transaction { transaction -> [Stories.StoredItem] in switch result { case let .stories(_, stories, users): - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } - - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users)) return stories.compactMap { apiStoryItem -> Stories.StoredItem? in return Stories.StoredItem(apiStoryItem: apiStoryItem, peerId: peerId, transaction: transaction) @@ -1439,6 +1421,7 @@ public final class StoryViewList { } func _internal_getStoryViewList(account: Account, id: Int32, offsetTimestamp: Int32?, offsetPeerId: PeerId?, limit: Int) -> Signal { + let accountPeerId = account.peerId return account.network.request(Api.functions.stories.getStoryViewsList(id: id, offsetDate: offsetTimestamp ?? 0, offsetId: offsetPeerId?.id._internalGetInt64Value() ?? 0, limit: Int32(limit))) |> map(Optional.init) |> `catch` { _ -> Signal in @@ -1451,19 +1434,7 @@ func _internal_getStoryViewList(account: Account, id: Int32, offsetTimestamp: In return account.postbox.transaction { transaction -> StoryViewList? in switch result { case let .storyViewsList(count, views, users): - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } - - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users)) var items: [StoryViewList.Item] = [] for view in views { @@ -1482,6 +1453,7 @@ func _internal_getStoryViewList(account: Account, id: Int32, offsetTimestamp: In } func _internal_getStoryViews(account: Account, ids: [Int32]) -> Signal<[Int32: Stories.Item.Views], NoError> { + let accountPeerId = account.peerId return account.network.request(Api.functions.stories.getStoriesViews(id: ids)) |> map(Optional.init) |> `catch` { _ -> Signal in @@ -1495,19 +1467,7 @@ func _internal_getStoryViews(account: Account, ids: [Int32]) -> Signal<[Int32: S var parsedViews: [Int32: Stories.Item.Views] = [:] switch result { case let .storyViews(views, users): - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } - - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users)) for i in 0 ..< views.count { if i < ids.count { @@ -1621,6 +1581,7 @@ public final class EngineStoryViewListContext { self.isLoadingMore = true let account = self.account + let accountPeerId = account.peerId let storyId = self.storyId let currentOffset = self.state.nextOffset let limit = self.state.items.isEmpty ? 50 : 100 @@ -1636,19 +1597,7 @@ public final class EngineStoryViewListContext { return account.postbox.transaction { transaction -> InternalState in switch result { case let .storyViewsList(count, views, users): - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } - - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users)) var items: [Item] = [] var nextOffset: NextOffset? @@ -1803,7 +1752,7 @@ func _internal_updatePeerStoriesHidden(account: Account, id: PeerId, isHidden: B guard let user = peer as? TelegramUser else { return nil } - updatePeers(transaction: transaction, peers: [user.withUpdatedStoriesHidden(isHidden)], update: { _, updated in + updatePeersCustom(transaction: transaction, peers: [user.withUpdatedStoriesHidden(isHidden)], update: { _, updated in return updated }) return apiInputUser(peer) @@ -1879,3 +1828,36 @@ func _internal_refreshStories(account: Account, peerId: PeerId, ids: [Int32]) -> |> ignoreValues } } + +func _internal_refreshSeenStories(postbox: Postbox, network: Network) -> Signal { + return network.request(Api.functions.stories.getAllReadUserStories()) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { updates -> Signal in + guard let updates = updates else { + return .complete() + } + return postbox.transaction { transaction -> Void in + for update in updates.allUpdates { + switch update { + case let .updateReadStories(userId, maxId): + let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)) + var update = false + if let value = transaction.getPeerStoryState(peerId: peerId) { + update = value.maxSeenId < maxId + } else { + update = true + } + if update { + transaction.setPeerStoryState(peerId: peerId, state: Stories.PeerState(maxReadId: maxId).postboxRepresentation) + } + default: + break + } + } + } + |> ignoreValues + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift index f7ada43e54..4fccbf704f 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift @@ -317,14 +317,7 @@ public final class StorySubscriptionsContext { case let .allStories(flags, _, state, userStories, users): //TODO:count - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: [], users: users) let hasMore: Bool = (flags & (1 << 0)) != 0 @@ -354,9 +347,9 @@ public final class StorySubscriptionsContext { peerEntries.append(peerId) transaction.setStoryItems(peerId: peerId, items: updatedPeerEntries) - transaction.setPeerStoryState(peerId: peerId, state: CodableEntry(Stories.PeerState( + transaction.setPeerStoryState(peerId: peerId, state: Stories.PeerState( maxReadId: maxReadId ?? 0 - ))) + ).postboxRepresentation) } } @@ -379,10 +372,7 @@ public final class StorySubscriptionsContext { hasMore: hasMore )), peerIds: peerEntries) - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) } } |> deliverOn(self.queue)).start(completed: { [weak self] in @@ -549,6 +539,7 @@ public final class PeerStoryListContext { let peerId = self.peerId let account = self.account + let accountPeerId = account.peerId let isArchived = self.isArchived self.requestDisposable = (self.account.postbox.transaction { transaction -> Api.InputUser? in return transaction.getPeer(peerId).flatMap(apiInputUser) @@ -565,7 +556,9 @@ public final class PeerStoryListContext { signal = account.network.request(Api.functions.stories.getPinnedStories(userId: inputUser, offsetId: Int32(loadMoreToken), limit: 100)) } return signal - |> map(Optional.init) + |> map { result -> Api.stories.Stories? in + return result + } |> `catch` { _ -> Signal in return .single(nil) } @@ -582,19 +575,7 @@ public final class PeerStoryListContext { case let .stories(count, stories, users): totalCount = Int(count) - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } - - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users)) for story in stories { if let storedItem = Stories.StoredItem(apiStoryItem: story, peerId: peerId, transaction: transaction) { @@ -897,16 +878,21 @@ public final class PeerExpiringStoryListContext { return self.statePromise.get() } + private let polledOnce = ValuePromise(false, ignoreRepeated: true) + init(queue: Queue, account: Account, peerId: EnginePeer.Id) { self.queue = queue self.account = account self.peerId = peerId - self.listDisposable = (account.postbox.combinedView(keys: [ - PostboxViewKey.storiesState(key: .peer(peerId)), - PostboxViewKey.storyItems(peerId: peerId) - ]) - |> deliverOn(self.queue)).start(next: { [weak self] views in + self.listDisposable = (combineLatest(queue: self.queue, + account.postbox.combinedView(keys: [ + PostboxViewKey.storiesState(key: .peer(peerId)), + PostboxViewKey.storyItems(peerId: peerId) + ]), + self.polledOnce.get() + ) + |> deliverOn(self.queue)).start(next: { [weak self] views, polledOnce in guard let `self` = self else { return } @@ -961,7 +947,8 @@ public final class PeerExpiringStoryListContext { return State( items: items, isCached: false, - maxReadId: state?.maxReadId ?? 0 + maxReadId: state?.maxReadId ?? 0, + isLoading: items.isEmpty && !polledOnce ) } |> deliverOn(self.queue)).start(next: { [weak self] state in @@ -987,6 +974,7 @@ public final class PeerExpiringStoryListContext { self.pollDisposable?.dispose() let account = self.account + let accountPeerId = account.peerId let peerId = self.peerId self.pollDisposable = (self.account.postbox.transaction { transaction -> Api.InputUser? in return transaction.getPeer(peerId).flatMap(apiInputUser) @@ -1006,14 +994,7 @@ public final class PeerExpiringStoryListContext { updatedPeerEntries.removeAll() if let result = result, case let .userStories(stories, users) = result { - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: [], users: users) switch stories { case let .userStories(_, userId, maxReadId, stories): @@ -1033,15 +1014,12 @@ public final class PeerExpiringStoryListContext { } } - transaction.setPeerStoryState(peerId: peerId, state: CodableEntry(Stories.PeerState( + transaction.setPeerStoryState(peerId: peerId, state: Stories.PeerState( maxReadId: maxReadId ?? 0 - ))) + ).postboxRepresentation) } - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) } transaction.setStoryItems(peerId: peerId, items: updatedPeerEntries) @@ -1052,6 +1030,9 @@ public final class PeerExpiringStoryListContext { guard let `self` = self else { return } + + self.polledOnce.set(true) + self.pollDisposable = (Signal.complete() |> suspendAwareDelay(60.0, queue: self.queue) |> deliverOn(self.queue)).start(completed: { [weak self] in guard let `self` = self else { return @@ -1098,6 +1079,7 @@ public final class PeerExpiringStoryListContext { public let items: [Item] public let isCached: Bool public let maxReadId: Int32 + public let isLoading: Bool public var hasUnseen: Bool { return self.items.contains(where: { $0.id > self.maxReadId }) @@ -1117,10 +1099,11 @@ public final class PeerExpiringStoryListContext { return self.items.contains(where: { $0.id > self.maxReadId && $0.isCloseFriends }) } - public init(items: [Item], isCached: Bool, maxReadId: Int32) { + public init(items: [Item], isCached: Bool, maxReadId: Int32, isLoading: Bool) { self.items = items self.isCached = isCached self.maxReadId = maxReadId + self.isLoading = isLoading } public static func ==(lhs: State, rhs: State) -> Bool { @@ -1133,6 +1116,9 @@ public final class PeerExpiringStoryListContext { if lhs.maxReadId != rhs.maxReadId { return false } + if lhs.isLoading != rhs.isLoading { + return false + } return true } } @@ -1174,14 +1160,7 @@ public func _internal_pollPeerStories(postbox: Postbox, network: Network, accoun updatedPeerEntries.removeAll() if let result = result, case let .userStories(stories, users) = result { - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: [], users: users) switch stories { case let .userStories(_, userId, maxReadId, stories): @@ -1201,15 +1180,12 @@ public func _internal_pollPeerStories(postbox: Postbox, network: Network, accoun } } - transaction.setPeerStoryState(peerId: peerId, state: CodableEntry(Stories.PeerState( + transaction.setPeerStoryState(peerId: peerId, state: Stories.PeerState( maxReadId: maxReadId ?? 0 - ))) + ).postboxRepresentation) } - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) } transaction.setStoryItems(peerId: peerId, items: updatedPeerEntries) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index a22ee2710f..a2b418b7e3 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -498,15 +498,15 @@ public extension TelegramEngine { } public func addBotToAttachMenu(botId: PeerId, allowWrite: Bool) -> Signal { - return _internal_addBotToAttachMenu(postbox: self.account.postbox, network: self.account.network, botId: botId, allowWrite: allowWrite) + return _internal_addBotToAttachMenu(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network, botId: botId, allowWrite: allowWrite) } public func removeBotFromAttachMenu(botId: PeerId) -> Signal { - return _internal_removeBotFromAttachMenu(postbox: self.account.postbox, network: self.account.network, botId: botId) + return _internal_removeBotFromAttachMenu(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network, botId: botId) } public func getAttachMenuBot(botId: PeerId, cached: Bool = false) -> Signal { - return _internal_getAttachMenuBot(postbox: self.account.postbox, network: self.account.network, botId: botId, cached: cached) + return _internal_getAttachMenuBot(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network, botId: botId, cached: cached) } public func attachMenuBots() -> Signal<[AttachMenuBot], NoError> { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift index 04acd3424e..d2e452f107 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift @@ -247,7 +247,7 @@ func _internal_fetchBotPaymentInvoice(postbox: Postbox, network: Network, source } } -func _internal_fetchBotPaymentForm(postbox: Postbox, network: Network, source: BotPaymentInvoiceSource, themeParams: [String: Any]?) -> Signal { +func _internal_fetchBotPaymentForm(accountPeerId: PeerId, postbox: Postbox, network: Network, source: BotPaymentInvoiceSource, themeParams: [String: Any]?) -> Signal { return postbox.transaction { transaction -> Api.InputInvoice? in switch source { case let .message(messageId): @@ -286,14 +286,8 @@ func _internal_fetchBotPaymentForm(postbox: Postbox, network: Network, source: B let _ = description let _ = photo - var peers: [Peer] = [] - for user in apiUsers { - let parsed = TelegramUser(user: user) - peers.append(parsed) - } - updatePeers(transaction: transaction, peers: peers, update: { _, updated in - return updated - }) + let parsedPeers = AccumulatedPeers(users: apiUsers) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) let parsedInvoice = BotPaymentInvoice(apiInvoice: invoice) var parsedNativeProvider: BotPaymentNativeProvider? @@ -577,6 +571,7 @@ public enum RequestBotPaymentReceiptError { } func _internal_requestBotPaymentReceipt(account: Account, messageId: MessageId) -> Signal { + let accountPeerId = account.peerId return account.postbox.transaction { transaction -> Api.InputPeer? in return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer) } @@ -594,11 +589,8 @@ func _internal_requestBotPaymentReceipt(account: Account, messageId: MessageId) return account.postbox.transaction { transaction -> BotPaymentReceipt in switch result { case let .paymentReceipt(_, _, botId, _, title, description, photo, invoice, info, shipping, tipAmount, currency, totalAmount, credentialsTitle, users): - var peers: [Peer] = [] - for user in users { - peers.append(TelegramUser(user: user)) - } - updatePeers(transaction: transaction, peers: peers, update: { _, updated in return updated }) + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: [], users: users) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) let parsedInvoice = BotPaymentInvoice(apiInvoice: invoice) let parsedInfo = info.flatMap(BotPaymentRequestedInfo.init) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift index 5906aa0687..c9773af950 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift @@ -19,7 +19,7 @@ public extension TelegramEngine { } public func fetchBotPaymentForm(source: BotPaymentInvoiceSource, themeParams: [String: Any]?) -> Signal { - return _internal_fetchBotPaymentForm(postbox: self.account.postbox, network: self.account.network, source: source, themeParams: themeParams) + return _internal_fetchBotPaymentForm(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network, source: source, themeParams: themeParams) } public func validateBotPaymentForm(saveInfo: Bool, source: BotPaymentInvoiceSource, formInfo: BotPaymentRequestedInfo) -> Signal { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/AddressNames.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/AddressNames.swift index c344d06f6a..5620190e15 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/AddressNames.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/AddressNames.swift @@ -136,6 +136,7 @@ public enum UpdateAddressNameError { } func _internal_updateAddressName(account: Account, domain: AddressNameDomain, name: String?) -> Signal { + let accountPeerId = account.peerId return account.postbox.transaction { transaction -> Signal in switch domain { case .account: @@ -145,10 +146,7 @@ func _internal_updateAddressName(account: Account, domain: AddressNameDomain, na } |> mapToSignal { result -> Signal in return account.postbox.transaction { transaction -> Void in - let user = TelegramUser(user: result) - updatePeers(transaction: transaction, peers: [user], update: { _, updated in - return updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: [result])) } |> mapError { _ -> UpdateAddressNameError in } } case let .peer(peerId): @@ -165,8 +163,8 @@ func _internal_updateAddressName(account: Account, domain: AddressNameDomain, na if name != nil, let defaultBannedRights = updatedPeer.defaultBannedRights { updatedPeer = updatedPeer.withUpdatedDefaultBannedRights(TelegramChatBannedRights(flags: defaultBannedRights.flags.union([.banPinMessages, .banChangeInfo]), untilDate: Int32.max)) } - updatePeers(transaction: transaction, peers: [updatedPeer], update: { _, updated in - return updated + updatePeersCustom(transaction: transaction, peers: [updatedPeer], update: { _, updated in + updated }) } } @@ -211,7 +209,7 @@ func _internal_deactivateAllAddressNames(account: Account, peerId: EnginePeer.Id updatedNames.append(TelegramPeerUsername(flags: updatedFlags, username: username.username)) } let updatedUser = peer.withUpdatedAddressNames(updatedNames) - updatePeers(transaction: transaction, peers: [updatedUser], update: { _, updated in + updatePeersCustom(transaction: transaction, peers: [updatedUser], update: { _, updated in return updated }) } @@ -289,7 +287,7 @@ func _internal_toggleAddressNameActive(account: Account, domain: AddressNameDoma updatedNames.insert(updatedName, at: updatedIndex) } let updatedUser = peer.withUpdatedUsernames(updatedNames) - updatePeers(transaction: transaction, peers: [updatedUser], update: { _, updated in + updatePeersCustom(transaction: transaction, peers: [updatedUser], update: { _, updated in return updated }) } @@ -347,7 +345,7 @@ func _internal_toggleAddressNameActive(account: Account, domain: AddressNameDoma updatedNames.insert(updatedName, at: updatedIndex) } let updatedPeer = peer.withUpdatedAddressNames(updatedNames) - updatePeers(transaction: transaction, peers: [updatedPeer], update: { _, updated in + updatePeersCustom(transaction: transaction, peers: [updatedPeer], update: { _, updated in return updated }) } @@ -408,7 +406,7 @@ func _internal_toggleAddressNameActive(account: Account, domain: AddressNameDoma updatedNames.insert(updatedName, at: updatedIndex) } let updatedPeer = peer.withUpdatedAddressNames(updatedNames) - updatePeers(transaction: transaction, peers: [updatedPeer], update: { _, updated in + updatePeersCustom(transaction: transaction, peers: [updatedPeer], update: { _, updated in return updated }) } @@ -439,7 +437,7 @@ func _internal_reorderAddressNames(account: Account, domain: AddressNameDomain, return account.postbox.transaction { transaction -> Void in if case .boolTrue = result, let peer = transaction.getPeer(account.peerId) as? TelegramUser { let updatedUser = peer.withUpdatedUsernames(names) - updatePeers(transaction: transaction, peers: [updatedUser], update: { _, updated in + updatePeersCustom(transaction: transaction, peers: [updatedUser], update: { _, updated in return updated }) } @@ -455,7 +453,7 @@ func _internal_reorderAddressNames(account: Account, domain: AddressNameDomain, return account.postbox.transaction { transaction -> Void in if case .boolTrue = result, let peer = transaction.getPeer(peerId) as? TelegramChannel { let updatedPeer = peer.withUpdatedAddressNames(names) - updatePeers(transaction: transaction, peers: [updatedPeer], update: { _, updated in + updatePeersCustom(transaction: transaction, peers: [updatedPeer], update: { _, updated in return updated }) } @@ -474,7 +472,7 @@ func _internal_reorderAddressNames(account: Account, domain: AddressNameDomain, return account.postbox.transaction { transaction -> Void in if case .boolTrue = result, let peer = transaction.getPeer(peerId) as? TelegramChannel { let updatedPeer = peer.withUpdatedAddressNames(names) - updatePeers(transaction: transaction, peers: [updatedPeer], update: { _, updated in + updatePeersCustom(transaction: transaction, peers: [updatedPeer], update: { _, updated in return updated }) } @@ -521,28 +519,28 @@ func _internal_adminedPublicChannels(account: Account, scope: AdminedPublicChann flags |= (1 << 2) } + let accountPeerId = account.peerId + return account.network.request(Api.functions.channels.getAdminedPublicChannels(flags: flags)) |> retryRequest |> mapToSignal { result -> Signal<[Peer], NoError> in - var peers: [Peer] = [] - switch result { - case let .chats(apiChats): - for chat in apiChats { - if let peer = parseTelegramGroupOrChannel(chat: chat) { - peers.append(peer) - } - } - case let .chatsSlice(_, apiChats): - for chat in apiChats { - if let peer = parseTelegramGroupOrChannel(chat: chat) { - peers.append(peer) - } - } - } return account.postbox.transaction { transaction -> [Peer] in - updatePeers(transaction: transaction, peers: peers, update: { _, updated in - return updated - }) + let chats: [Api.Chat] + let parsedPeers: AccumulatedPeers + switch result { + case let .chats(apiChats): + chats = apiChats + case let .chatsSlice(_, apiChats): + chats = apiChats + } + parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: []) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) + var peers: [Peer] = [] + for chat in chats { + if let peer = transaction.getPeer(chat.peerId) { + peers.append(peer) + } + } return peers } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelAdminEventLogContext.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelAdminEventLogContext.swift index 14f66f81a7..6b69465557 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelAdminEventLogContext.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelAdminEventLogContext.swift @@ -81,6 +81,7 @@ public final class ChannelAdminEventLogContext { private let postbox: Postbox private let network: Network + private let accountPeerId: PeerId private let peerId: PeerId private var filter: ChannelAdminEventLogFilter = ChannelAdminEventLogFilter() @@ -98,10 +99,11 @@ public final class ChannelAdminEventLogContext { private let loadMoreDisposable = MetaDisposable() - init(postbox: Postbox, network: Network, peerId: PeerId) { + init(postbox: Postbox, network: Network, peerId: PeerId, accountPeerId: PeerId) { self.postbox = postbox self.network = network self.peerId = peerId + self.accountPeerId = accountPeerId } deinit { @@ -166,7 +168,7 @@ public final class ChannelAdminEventLogContext { } self.loadingMoreEarlier = true - self.loadMoreDisposable.set((channelAdminLogEvents(postbox: self.postbox, network: self.network, peerId: self.peerId, maxId: maxId, minId: AdminLogEventId.min, limit: 100, query: self.filter.query, filter: self.filter.events, admins: self.filter.adminPeerIds) + self.loadMoreDisposable.set((channelAdminLogEvents(accountPeerId: self.accountPeerId, postbox: self.postbox, network: self.network, peerId: self.peerId, maxId: maxId, minId: AdminLogEventId.min, limit: 100, query: self.filter.query, filter: self.filter.events, admins: self.filter.adminPeerIds) |> deliverOn(self.queue)).start(next: { [weak self] result in if let strongSelf = self { var events = result.events.sorted() diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelAdminEventLogs.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelAdminEventLogs.swift index 481d2b9984..91dfaaa75a 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelAdminEventLogs.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelAdminEventLogs.swift @@ -139,7 +139,7 @@ private func boolFromApiValue(_ value: Api.Bool) -> Bool { } } -func channelAdminLogEvents(postbox: Postbox, network: Network, peerId: PeerId, maxId: AdminLogEventId, minId: AdminLogEventId, limit: Int32 = 100, query: String? = nil, filter: AdminLogEventsFlags? = nil, admins: [PeerId]? = nil) -> Signal { +func channelAdminLogEvents(accountPeerId: PeerId, postbox: Postbox, network: Network, peerId: PeerId, maxId: AdminLogEventId, minId: AdminLogEventId, limit: Int32 = 100, query: String? = nil, filter: AdminLogEventsFlags? = nil, admins: [PeerId]? = nil) -> Signal { return postbox.transaction { transaction -> (Peer?, [Peer]?) in return (transaction.getPeer(peerId), admins?.compactMap { transaction.getPeer($0) }) } @@ -157,213 +157,214 @@ func channelAdminLogEvents(postbox: Postbox, network: Network, peerId: PeerId, m if let _ = inputAdmins { flags += Int32(1 << 1) } - return network.request(Api.functions.channels.getAdminLog(flags: flags, channel: inputChannel, q: query ?? "", eventsFilter: eventsFilter, admins: inputAdmins, maxId: maxId, minId: minId, limit: limit)) |> mapToSignal { result in - + return network.request(Api.functions.channels.getAdminLog(flags: flags, channel: inputChannel, q: query ?? "", eventsFilter: eventsFilter, admins: inputAdmins, maxId: maxId, minId: minId, limit: limit)) + |> mapToSignal { result in switch result { case let .adminLogResults(apiEvents, apiChats, apiUsers): - var peers: [PeerId: Peer] = [:] - for apiChat in apiChats { - if let peer = parseTelegramGroupOrChannel(chat: apiChat) { + return postbox.transaction { transaction -> AdminLogEventsResult in + var peers: [PeerId: Peer] = [:] + for apiChat in apiChats { + if let peer = parseTelegramGroupOrChannel(chat: apiChat) { + peers[peer.id] = peer + } + } + for apiUser in apiUsers { + let peer = TelegramUser(user: apiUser) peers[peer.id] = peer } - } - for apiUser in apiUsers { - let peer = TelegramUser(user: apiUser) - peers[peer.id] = peer - } - - var events: [AdminLogEvent] = [] - - for event in apiEvents { - switch event { + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: apiChats, users: apiUsers) + + var events: [AdminLogEvent] = [] + + for event in apiEvents { + switch event { case let .channelAdminLogEvent(id, date, userId, apiAction): var action: AdminLogEventAction? switch apiAction { - case let .channelAdminLogEventActionChangeTitle(prev, new): - action = .changeTitle(prev: prev, new: new) - case let .channelAdminLogEventActionChangeAbout(prev, new): - action = .changeAbout(prev: prev, new: new) - case let .channelAdminLogEventActionChangeUsername(prev, new): - action = .changeUsername(prev: prev, new: new) - case let .channelAdminLogEventActionChangePhoto(prev, new): - let previousImage = telegramMediaImageFromApiPhoto(prev) - let newImage = telegramMediaImageFromApiPhoto(new) - action = .changePhoto(prev: (previousImage?.representations ?? [], previousImage?.videoRepresentations ?? []) , new: (newImage?.representations ?? [], newImage?.videoRepresentations ?? [])) - case let .channelAdminLogEventActionToggleInvites(new): - action = .toggleInvites(boolFromApiValue(new)) - case let .channelAdminLogEventActionToggleSignatures(new): - action = .toggleSignatures(boolFromApiValue(new)) - case let .channelAdminLogEventActionUpdatePinned(new): - switch new { - case .messageEmpty: - action = .updatePinned(nil) - default: - if let message = StoreMessage(apiMessage: new, peerIsForum: peer.isForum), let rendered = locallyRenderedMessage(message: message, peers: peers) { - action = .updatePinned(rendered) - } - } - case let .channelAdminLogEventActionEditMessage(prev, new): - if let prev = StoreMessage(apiMessage: prev, peerIsForum: peer.isForum), let prevRendered = locallyRenderedMessage(message: prev, peers: peers), let new = StoreMessage(apiMessage: new, peerIsForum: peer.isForum), let newRendered = locallyRenderedMessage(message: new, peers: peers) { - action = .editMessage(prev: prevRendered, new: newRendered) - } - case let .channelAdminLogEventActionDeleteMessage(message): - if let message = StoreMessage(apiMessage: message, peerIsForum: peer.isForum), let rendered = locallyRenderedMessage(message: message, peers: peers) { - action = .deleteMessage(rendered) - } - case .channelAdminLogEventActionParticipantJoin: - action = .participantJoin - case .channelAdminLogEventActionParticipantLeave: - action = .participantLeave - case let .channelAdminLogEventActionParticipantInvite(participant): - let participant = ChannelParticipant(apiParticipant: participant) - - if let peer = peers[participant.peerId] { - action = .participantInvite(RenderedChannelParticipant(participant: participant, peer: peer)) - } - case let .channelAdminLogEventActionParticipantToggleBan(prev, new): - let prevParticipant = ChannelParticipant(apiParticipant: prev) - let newParticipant = ChannelParticipant(apiParticipant: new) - - if let prevPeer = peers[prevParticipant.peerId], let newPeer = peers[newParticipant.peerId] { - action = .participantToggleBan(prev: RenderedChannelParticipant(participant: prevParticipant, peer: prevPeer), new: RenderedChannelParticipant(participant: newParticipant, peer: newPeer)) - } - case let .channelAdminLogEventActionParticipantToggleAdmin(prev, new): - let prevParticipant = ChannelParticipant(apiParticipant: prev) - let newParticipant = ChannelParticipant(apiParticipant: new) - - if let prevPeer = peers[prevParticipant.peerId], let newPeer = peers[newParticipant.peerId] { - action = .participantToggleAdmin(prev: RenderedChannelParticipant(participant: prevParticipant, peer: prevPeer), new: RenderedChannelParticipant(participant: newParticipant, peer: newPeer)) - } - case let .channelAdminLogEventActionChangeStickerSet(prevStickerset, newStickerset): - action = .changeStickerPack(prev: StickerPackReference(apiInputSet: prevStickerset), new: StickerPackReference(apiInputSet: newStickerset)) - case let .channelAdminLogEventActionTogglePreHistoryHidden(value): - action = .togglePreHistoryHidden(value == .boolTrue) - case let .channelAdminLogEventActionDefaultBannedRights(prevBannedRights, newBannedRights): - action = .updateDefaultBannedRights(prev: TelegramChatBannedRights(apiBannedRights: prevBannedRights), new: TelegramChatBannedRights(apiBannedRights: newBannedRights)) - case let .channelAdminLogEventActionStopPoll(message): - if let message = StoreMessage(apiMessage: message, peerIsForum: peer.isForum), let rendered = locallyRenderedMessage(message: message, peers: peers) { - action = .pollStopped(rendered) - } - case let .channelAdminLogEventActionChangeLinkedChat(prevValue, newValue): - action = .linkedPeerUpdated(previous: prevValue == 0 ? nil : peers[PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(prevValue))], updated: newValue == 0 ? nil : peers[PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(newValue))]) - case let .channelAdminLogEventActionChangeLocation(prevValue, newValue): - action = .changeGeoLocation(previous: PeerGeoLocation(apiLocation: prevValue), updated: PeerGeoLocation(apiLocation: newValue)) - case let .channelAdminLogEventActionToggleSlowMode(prevValue, newValue): - action = .updateSlowmode(previous: prevValue == 0 ? nil : prevValue, updated: newValue == 0 ? nil : newValue) - case .channelAdminLogEventActionStartGroupCall: - action = .startGroupCall - case .channelAdminLogEventActionDiscardGroupCall: - action = .endGroupCall - case let .channelAdminLogEventActionParticipantMute(participant): - let parsedParticipant = GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate(participant) - action = .groupCallUpdateParticipantMuteStatus(peerId: parsedParticipant.peerId, isMuted: true) - case let .channelAdminLogEventActionParticipantUnmute(participant): - let parsedParticipant = GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate(participant) - action = .groupCallUpdateParticipantMuteStatus(peerId: parsedParticipant.peerId, isMuted: false) - case let .channelAdminLogEventActionToggleGroupCallSetting(joinMuted): - action = .updateGroupCallSettings(joinMuted: joinMuted == .boolTrue) - case let .channelAdminLogEventActionExportedInviteDelete(invite): - action = .deleteExportedInvitation(ExportedInvitation(apiExportedInvite: invite)) - case let .channelAdminLogEventActionExportedInviteRevoke(invite): - action = .revokeExportedInvitation(ExportedInvitation(apiExportedInvite: invite)) - case let .channelAdminLogEventActionExportedInviteEdit(prevInvite, newInvite): - action = .editExportedInvitation(previous: ExportedInvitation(apiExportedInvite: prevInvite), updated: ExportedInvitation(apiExportedInvite: newInvite)) - case let .channelAdminLogEventActionParticipantJoinByInvite(flags, invite): - action = .participantJoinedViaInvite(invitation: ExportedInvitation(apiExportedInvite: invite), joinedViaFolderLink: (flags & (1 << 0)) != 0) - case let .channelAdminLogEventActionParticipantVolume(participant): - let parsedParticipant = GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate(participant) - action = .groupCallUpdateParticipantVolume(peerId: parsedParticipant.peerId, volume: parsedParticipant.volume ?? 10000) - case let .channelAdminLogEventActionChangeHistoryTTL(prevValue, newValue): - action = .changeHistoryTTL(previousValue: prevValue, updatedValue: newValue) - case let .channelAdminLogEventActionParticipantJoinByRequest(invite, approvedBy): - action = .participantJoinByRequest(invitation: ExportedInvitation(apiExportedInvite: invite), approvedBy: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(approvedBy))) - case let .channelAdminLogEventActionToggleNoForwards(new): - action = .toggleCopyProtection(boolFromApiValue(new)) - case let .channelAdminLogEventActionSendMessage(message): - if let message = StoreMessage(apiMessage: message, peerIsForum: peer.isForum), let rendered = locallyRenderedMessage(message: message, peers: peers) { - action = .sendMessage(rendered) - } - case let .channelAdminLogEventActionChangeAvailableReactions(prevValue, newValue): - action = .changeAvailableReactions(previousValue: PeerAllowedReactions(apiReactions: prevValue), updatedValue: PeerAllowedReactions(apiReactions: newValue)) - case let .channelAdminLogEventActionChangeUsernames(prevValue, newValue): - action = .changeUsernames(prev: prevValue, new: newValue) - case let .channelAdminLogEventActionCreateTopic(topic): - switch topic { - case let .forumTopic(_, _, _, title, iconColor, iconEmojiId, _, _, _, _, _, _, _, _, _): - action = .createTopic(info: EngineMessageHistoryThread.Info(title: title, icon: iconEmojiId, iconColor: iconColor)) - case .forumTopicDeleted: - action = .createTopic(info: EngineMessageHistoryThread.Info(title: "", icon: nil, iconColor: 0)) - } - case let .channelAdminLogEventActionDeleteTopic(topic): - switch topic { - case let .forumTopic(_, _, _, title, iconColor, iconEmojiId, _, _, _, _, _, _, _, _, _): - action = .deleteTopic(info: EngineMessageHistoryThread.Info(title: title, icon: iconEmojiId, iconColor: iconColor)) - case .forumTopicDeleted: - action = .deleteTopic(info: EngineMessageHistoryThread.Info(title: "", icon: nil, iconColor: 0)) - } - case let .channelAdminLogEventActionEditTopic(prevTopic, newTopic): - let prevInfo: AdminLogEventAction.ForumTopicInfo - switch prevTopic { - case let .forumTopic(flags, _, _, title, iconColor, iconEmojiId, _, _, _, _, _, _, _, _, _): - prevInfo = AdminLogEventAction.ForumTopicInfo(info: EngineMessageHistoryThread.Info(title: title, icon: iconEmojiId, iconColor: iconColor), isClosed: (flags & (1 << 2)) != 0, isHidden: (flags & (1 << 6)) != 0) - case .forumTopicDeleted: - prevInfo = AdminLogEventAction.ForumTopicInfo(info: EngineMessageHistoryThread.Info(title: "", icon: nil, iconColor: 0), isClosed: false, isHidden: false) + case let .channelAdminLogEventActionChangeTitle(prev, new): + action = .changeTitle(prev: prev, new: new) + case let .channelAdminLogEventActionChangeAbout(prev, new): + action = .changeAbout(prev: prev, new: new) + case let .channelAdminLogEventActionChangeUsername(prev, new): + action = .changeUsername(prev: prev, new: new) + case let .channelAdminLogEventActionChangePhoto(prev, new): + let previousImage = telegramMediaImageFromApiPhoto(prev) + let newImage = telegramMediaImageFromApiPhoto(new) + action = .changePhoto(prev: (previousImage?.representations ?? [], previousImage?.videoRepresentations ?? []) , new: (newImage?.representations ?? [], newImage?.videoRepresentations ?? [])) + case let .channelAdminLogEventActionToggleInvites(new): + action = .toggleInvites(boolFromApiValue(new)) + case let .channelAdminLogEventActionToggleSignatures(new): + action = .toggleSignatures(boolFromApiValue(new)) + case let .channelAdminLogEventActionUpdatePinned(new): + switch new { + case .messageEmpty: + action = .updatePinned(nil) + default: + if let message = StoreMessage(apiMessage: new, peerIsForum: peer.isForum), let rendered = locallyRenderedMessage(message: message, peers: peers) { + action = .updatePinned(rendered) } + } + case let .channelAdminLogEventActionEditMessage(prev, new): + if let prev = StoreMessage(apiMessage: prev, peerIsForum: peer.isForum), let prevRendered = locallyRenderedMessage(message: prev, peers: peers), let new = StoreMessage(apiMessage: new, peerIsForum: peer.isForum), let newRendered = locallyRenderedMessage(message: new, peers: peers) { + action = .editMessage(prev: prevRendered, new: newRendered) + } + case let .channelAdminLogEventActionDeleteMessage(message): + if let message = StoreMessage(apiMessage: message, peerIsForum: peer.isForum), let rendered = locallyRenderedMessage(message: message, peers: peers) { + action = .deleteMessage(rendered) + } + case .channelAdminLogEventActionParticipantJoin: + action = .participantJoin + case .channelAdminLogEventActionParticipantLeave: + action = .participantLeave + case let .channelAdminLogEventActionParticipantInvite(participant): + let participant = ChannelParticipant(apiParticipant: participant) - let newInfo: AdminLogEventAction.ForumTopicInfo - switch newTopic { - case let .forumTopic(flags, _, _, title, iconColor, iconEmojiId, _, _, _, _, _, _, _, _, _): - newInfo = AdminLogEventAction.ForumTopicInfo(info: EngineMessageHistoryThread.Info(title: title, icon: iconEmojiId, iconColor: iconColor), isClosed: (flags & (1 << 2)) != 0, isHidden: (flags & (1 << 6)) != 0) - case .forumTopicDeleted: - newInfo = AdminLogEventAction.ForumTopicInfo(info: EngineMessageHistoryThread.Info(title: "", icon: nil, iconColor: 0), isClosed: false, isHidden: false) - } + if let peer = peers[participant.peerId] { + action = .participantInvite(RenderedChannelParticipant(participant: participant, peer: peer)) + } + case let .channelAdminLogEventActionParticipantToggleBan(prev, new): + let prevParticipant = ChannelParticipant(apiParticipant: prev) + let newParticipant = ChannelParticipant(apiParticipant: new) - action = .editTopic(prevInfo: prevInfo, newInfo: newInfo) - case let .channelAdminLogEventActionPinTopic(_, prevTopic, newTopic): - let prevInfo: EngineMessageHistoryThread.Info? - switch prevTopic { - case let .forumTopic(_, _, _, title, iconColor, iconEmojiId, _, _, _, _, _, _, _, _, _): - prevInfo = EngineMessageHistoryThread.Info(title: title, icon: iconEmojiId, iconColor: iconColor) - case .forumTopicDeleted: - prevInfo = EngineMessageHistoryThread.Info(title: "", icon: nil, iconColor: 0) - case .none: - prevInfo = nil - } + if let prevPeer = peers[prevParticipant.peerId], let newPeer = peers[newParticipant.peerId] { + action = .participantToggleBan(prev: RenderedChannelParticipant(participant: prevParticipant, peer: prevPeer), new: RenderedChannelParticipant(participant: newParticipant, peer: newPeer)) + } + case let .channelAdminLogEventActionParticipantToggleAdmin(prev, new): + let prevParticipant = ChannelParticipant(apiParticipant: prev) + let newParticipant = ChannelParticipant(apiParticipant: new) - let newInfo: EngineMessageHistoryThread.Info? - switch newTopic { - case let .forumTopic(_, _, _, title, iconColor, iconEmojiId, _, _, _, _, _, _, _, _, _): - newInfo = EngineMessageHistoryThread.Info(title: title, icon: iconEmojiId, iconColor: iconColor) - case .forumTopicDeleted: - newInfo = EngineMessageHistoryThread.Info(title: "", icon: nil, iconColor: 0) - case .none: - newInfo = nil - } + if let prevPeer = peers[prevParticipant.peerId], let newPeer = peers[newParticipant.peerId] { + action = .participantToggleAdmin(prev: RenderedChannelParticipant(participant: prevParticipant, peer: prevPeer), new: RenderedChannelParticipant(participant: newParticipant, peer: newPeer)) + } + case let .channelAdminLogEventActionChangeStickerSet(prevStickerset, newStickerset): + action = .changeStickerPack(prev: StickerPackReference(apiInputSet: prevStickerset), new: StickerPackReference(apiInputSet: newStickerset)) + case let .channelAdminLogEventActionTogglePreHistoryHidden(value): + action = .togglePreHistoryHidden(value == .boolTrue) + case let .channelAdminLogEventActionDefaultBannedRights(prevBannedRights, newBannedRights): + action = .updateDefaultBannedRights(prev: TelegramChatBannedRights(apiBannedRights: prevBannedRights), new: TelegramChatBannedRights(apiBannedRights: newBannedRights)) + case let .channelAdminLogEventActionStopPoll(message): + if let message = StoreMessage(apiMessage: message, peerIsForum: peer.isForum), let rendered = locallyRenderedMessage(message: message, peers: peers) { + action = .pollStopped(rendered) + } + case let .channelAdminLogEventActionChangeLinkedChat(prevValue, newValue): + action = .linkedPeerUpdated(previous: prevValue == 0 ? nil : peers[PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(prevValue))], updated: newValue == 0 ? nil : peers[PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(newValue))]) + case let .channelAdminLogEventActionChangeLocation(prevValue, newValue): + action = .changeGeoLocation(previous: PeerGeoLocation(apiLocation: prevValue), updated: PeerGeoLocation(apiLocation: newValue)) + case let .channelAdminLogEventActionToggleSlowMode(prevValue, newValue): + action = .updateSlowmode(previous: prevValue == 0 ? nil : prevValue, updated: newValue == 0 ? nil : newValue) + case .channelAdminLogEventActionStartGroupCall: + action = .startGroupCall + case .channelAdminLogEventActionDiscardGroupCall: + action = .endGroupCall + case let .channelAdminLogEventActionParticipantMute(participant): + let parsedParticipant = GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate(participant) + action = .groupCallUpdateParticipantMuteStatus(peerId: parsedParticipant.peerId, isMuted: true) + case let .channelAdminLogEventActionParticipantUnmute(participant): + let parsedParticipant = GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate(participant) + action = .groupCallUpdateParticipantMuteStatus(peerId: parsedParticipant.peerId, isMuted: false) + case let .channelAdminLogEventActionToggleGroupCallSetting(joinMuted): + action = .updateGroupCallSettings(joinMuted: joinMuted == .boolTrue) + case let .channelAdminLogEventActionExportedInviteDelete(invite): + action = .deleteExportedInvitation(ExportedInvitation(apiExportedInvite: invite)) + case let .channelAdminLogEventActionExportedInviteRevoke(invite): + action = .revokeExportedInvitation(ExportedInvitation(apiExportedInvite: invite)) + case let .channelAdminLogEventActionExportedInviteEdit(prevInvite, newInvite): + action = .editExportedInvitation(previous: ExportedInvitation(apiExportedInvite: prevInvite), updated: ExportedInvitation(apiExportedInvite: newInvite)) + case let .channelAdminLogEventActionParticipantJoinByInvite(flags, invite): + action = .participantJoinedViaInvite(invitation: ExportedInvitation(apiExportedInvite: invite), joinedViaFolderLink: (flags & (1 << 0)) != 0) + case let .channelAdminLogEventActionParticipantVolume(participant): + let parsedParticipant = GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate(participant) + action = .groupCallUpdateParticipantVolume(peerId: parsedParticipant.peerId, volume: parsedParticipant.volume ?? 10000) + case let .channelAdminLogEventActionChangeHistoryTTL(prevValue, newValue): + action = .changeHistoryTTL(previousValue: prevValue, updatedValue: newValue) + case let .channelAdminLogEventActionParticipantJoinByRequest(invite, approvedBy): + action = .participantJoinByRequest(invitation: ExportedInvitation(apiExportedInvite: invite), approvedBy: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(approvedBy))) + case let .channelAdminLogEventActionToggleNoForwards(new): + action = .toggleCopyProtection(boolFromApiValue(new)) + case let .channelAdminLogEventActionSendMessage(message): + if let message = StoreMessage(apiMessage: message, peerIsForum: peer.isForum), let rendered = locallyRenderedMessage(message: message, peers: peers) { + action = .sendMessage(rendered) + } + case let .channelAdminLogEventActionChangeAvailableReactions(prevValue, newValue): + action = .changeAvailableReactions(previousValue: PeerAllowedReactions(apiReactions: prevValue), updatedValue: PeerAllowedReactions(apiReactions: newValue)) + case let .channelAdminLogEventActionChangeUsernames(prevValue, newValue): + action = .changeUsernames(prev: prevValue, new: newValue) + case let .channelAdminLogEventActionCreateTopic(topic): + switch topic { + case let .forumTopic(_, _, _, title, iconColor, iconEmojiId, _, _, _, _, _, _, _, _, _): + action = .createTopic(info: EngineMessageHistoryThread.Info(title: title, icon: iconEmojiId, iconColor: iconColor)) + case .forumTopicDeleted: + action = .createTopic(info: EngineMessageHistoryThread.Info(title: "", icon: nil, iconColor: 0)) + } + case let .channelAdminLogEventActionDeleteTopic(topic): + switch topic { + case let .forumTopic(_, _, _, title, iconColor, iconEmojiId, _, _, _, _, _, _, _, _, _): + action = .deleteTopic(info: EngineMessageHistoryThread.Info(title: title, icon: iconEmojiId, iconColor: iconColor)) + case .forumTopicDeleted: + action = .deleteTopic(info: EngineMessageHistoryThread.Info(title: "", icon: nil, iconColor: 0)) + } + case let .channelAdminLogEventActionEditTopic(prevTopic, newTopic): + let prevInfo: AdminLogEventAction.ForumTopicInfo + switch prevTopic { + case let .forumTopic(flags, _, _, title, iconColor, iconEmojiId, _, _, _, _, _, _, _, _, _): + prevInfo = AdminLogEventAction.ForumTopicInfo(info: EngineMessageHistoryThread.Info(title: title, icon: iconEmojiId, iconColor: iconColor), isClosed: (flags & (1 << 2)) != 0, isHidden: (flags & (1 << 6)) != 0) + case .forumTopicDeleted: + prevInfo = AdminLogEventAction.ForumTopicInfo(info: EngineMessageHistoryThread.Info(title: "", icon: nil, iconColor: 0), isClosed: false, isHidden: false) + } - action = .pinTopic(prevInfo: prevInfo, newInfo: newInfo) - case let .channelAdminLogEventActionToggleForum(newValue): - action = .toggleForum(isForum: newValue == .boolTrue) - case let .channelAdminLogEventActionToggleAntiSpam(newValue): - action = .toggleAntiSpam(isEnabled: newValue == .boolTrue) + let newInfo: AdminLogEventAction.ForumTopicInfo + switch newTopic { + case let .forumTopic(flags, _, _, title, iconColor, iconEmojiId, _, _, _, _, _, _, _, _, _): + newInfo = AdminLogEventAction.ForumTopicInfo(info: EngineMessageHistoryThread.Info(title: title, icon: iconEmojiId, iconColor: iconColor), isClosed: (flags & (1 << 2)) != 0, isHidden: (flags & (1 << 6)) != 0) + case .forumTopicDeleted: + newInfo = AdminLogEventAction.ForumTopicInfo(info: EngineMessageHistoryThread.Info(title: "", icon: nil, iconColor: 0), isClosed: false, isHidden: false) + } + + action = .editTopic(prevInfo: prevInfo, newInfo: newInfo) + case let .channelAdminLogEventActionPinTopic(_, prevTopic, newTopic): + let prevInfo: EngineMessageHistoryThread.Info? + switch prevTopic { + case let .forumTopic(_, _, _, title, iconColor, iconEmojiId, _, _, _, _, _, _, _, _, _): + prevInfo = EngineMessageHistoryThread.Info(title: title, icon: iconEmojiId, iconColor: iconColor) + case .forumTopicDeleted: + prevInfo = EngineMessageHistoryThread.Info(title: "", icon: nil, iconColor: 0) + case .none: + prevInfo = nil + } + + let newInfo: EngineMessageHistoryThread.Info? + switch newTopic { + case let .forumTopic(_, _, _, title, iconColor, iconEmojiId, _, _, _, _, _, _, _, _, _): + newInfo = EngineMessageHistoryThread.Info(title: title, icon: iconEmojiId, iconColor: iconColor) + case .forumTopicDeleted: + newInfo = EngineMessageHistoryThread.Info(title: "", icon: nil, iconColor: 0) + case .none: + newInfo = nil + } + + action = .pinTopic(prevInfo: prevInfo, newInfo: newInfo) + case let .channelAdminLogEventActionToggleForum(newValue): + action = .toggleForum(isForum: newValue == .boolTrue) + case let .channelAdminLogEventActionToggleAntiSpam(newValue): + action = .toggleAntiSpam(isEnabled: newValue == .boolTrue) } let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)) if let action = action { events.append(AdminLogEvent(id: id, peerId: peerId, date: date, action: action)) } + } } - } - - return postbox.transaction { transaction -> AdminLogEventsResult in - updatePeers(transaction: transaction, peers: peers.map { $0.1 }, update: { return $1 }) - var peers = peers + + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) if peers[peerId] == nil, let peer = transaction.getPeer(peerId) { peers[peer.id] = peer } return AdminLogEventsResult(peerId: peerId, peers: peers, events: events) - } |> castError(MTRpcError.self) + } + |> castError(MTRpcError.self) } - - } |> mapError {_ in return .generic} + } + |> mapError {_ in return .generic} } return .complete() diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelBlacklist.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelBlacklist.swift index 29bf8d846a..0813017891 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelBlacklist.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelBlacklist.swift @@ -168,11 +168,11 @@ func _internal_updateDefaultChannelMemberBannedRights(account: Account, peerId: return } if let peer = peer as? TelegramGroup { - updatePeers(transaction: transaction, peers: [peer.updateDefaultBannedRights(rights, version: peer.version)], update: { _, updated in + updatePeersCustom(transaction: transaction, peers: [peer.updateDefaultBannedRights(rights, version: peer.version)], update: { _, updated in return updated }) } else if let peer = peer as? TelegramChannel { - updatePeers(transaction: transaction, peers: [peer.withUpdatedDefaultBannedRights(rights)], update: { _, updated in + updatePeersCustom(transaction: transaction, peers: [peer.withUpdatedDefaultBannedRights(rights)], update: { _, updated in return updated }) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelMembers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelMembers.swift index ca8c3297a3..85899d79ae 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelMembers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelMembers.swift @@ -90,25 +90,17 @@ func _internal_channelMembers(postbox: Postbox, network: Network, accountPeerId: var items: [RenderedChannelParticipant] = [] switch result { case let .channelParticipants(_, participants, chats, users): + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) var peers: [PeerId: Peer] = [:] - var presences: [PeerId: Api.User] = [:] - for user in users { - let peer = TelegramUser(user: user) - peers[peer.id] = peer - presences[peer.id] = user - } - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers[groupOrChannel.id] = groupOrChannel + for id in parsedPeers.allIds { + if let peer = transaction.getPeer(id) { + peers[peer.id] = peer } } - updatePeers(transaction: transaction, peers: Array(peers.values), update: { _, updated in - return updated - }) - updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: presences) for participant in CachedChannelParticipants(apiParticipants: participants).participants { - if let peer = peers[participant.peerId] { + if let peer = parsedPeers.get(participant.peerId) { var renderedPresences: [PeerId: PeerPresence] = [:] if let presence = transaction.getPeerPresence(peerId: participant.peerId) { renderedPresences[participant.peerId] = presence diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChatListFiltering.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChatListFiltering.swift index 5995ab2615..83a7ecae1a 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChatListFiltering.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChatListFiltering.swift @@ -617,13 +617,7 @@ private func requestChatListFilters(accountPeerId: PeerId, postbox: Postbox, net } |> mapToSignal { users -> Signal in return postbox.transaction { transaction -> Void in - var peers: [Peer] = [] - for user in users { - peers.append(TelegramUser(user: user)) - } - updatePeers(transaction: transaction, peers: peers, update: { _, updated in - return updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users)) } |> ignoreValues } @@ -641,18 +635,12 @@ private func requestChatListFilters(accountPeerId: PeerId, postbox: Postbox, net |> mapToSignal { result -> Signal in return postbox.transaction { transaction -> Void in if let result = result { - var peers: [Peer] = [] + let parsedPeers: AccumulatedPeers switch result { case .chats(let chats), .chatsSlice(_, let chats): - for chat in chats { - if let peer = parseTelegramGroupOrChannel(chat: chat) { - peers.append(peer) - } - } + parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: []) } - updatePeers(transaction: transaction, peers: peers, update: { _, updated in - return updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) } } |> ignoreValues @@ -671,18 +659,12 @@ private func requestChatListFilters(accountPeerId: PeerId, postbox: Postbox, net |> mapToSignal { result -> Signal in return postbox.transaction { transaction -> Void in if let result = result { - var peers: [Peer] = [] + let parsedPeers: AccumulatedPeers switch result { case .chats(let chats), .chatsSlice(_, let chats): - for chat in chats { - if let peer = parseTelegramGroupOrChannel(chat: chat) { - peers.append(peer) - } - } + parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: []) } - updatePeers(transaction: transaction, peers: peers, update: { _, updated in - return updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) } } |> ignoreValues @@ -737,24 +719,15 @@ private func loadAndStorePeerChatInfos(accountPeerId: PeerId, postbox: Postbox, } return postbox.transaction { transaction -> Void in - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] var notificationSettings: [PeerId: PeerNotificationSettings] = [:] var ttlPeriods: [PeerId: CachedPeerAutoremoveTimeout] = [:] var channelStates: [PeerId: Int32] = [:] + let parsedPeers: AccumulatedPeers + switch result { case let .peerDialogs(dialogs, messages, chats, users, _): - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers.append(groupOrChannel) - } - } - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } + parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) var topMessageIds = Set() @@ -833,15 +806,10 @@ private func loadAndStorePeerChatInfos(accountPeerId: PeerId, postbox: Postbox, } } - var peerMap: [PeerId: Peer] = [:] - for peer in peers { - peerMap[peer.id] = peer - } - var storeMessages: [StoreMessage] = [] for message in messages { var peerIsForum = false - if let peerId = message.peerId, let peer = peerMap[peerId], peer.isForum { + if let peerId = message.peerId, let peer = parsedPeers.get(peerId), peer.isForum { peerIsForum = true } if let storeMessage = StoreMessage(apiMessage: message, peerIsForum: peerIsForum) { @@ -864,11 +832,7 @@ private func loadAndStorePeerChatInfos(accountPeerId: PeerId, postbox: Postbox, } } - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - - updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) transaction.updateCurrentPeerNotificationSettings(notificationSettings) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/Communities.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/Communities.swift index 4410b438cb..22af420350 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/Communities.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/Communities.swift @@ -153,6 +153,7 @@ func _internal_exportChatFolder(account: Account, filterId: Int32, title: String } func _internal_getExportedChatFolderLinks(account: Account, id: Int32) -> Signal<[ExportedChatFolderLink]?, NoError> { + let accountPeerId = account.peerId return account.network.request(Api.functions.chatlists.getExportedInvites(chatlist: .inputChatlistDialogFilter(filterId: id))) |> map(Optional.init) |> `catch` { _ -> Signal in @@ -165,24 +166,8 @@ func _internal_getExportedChatFolderLinks(account: Account, id: Int32) -> Signal return account.postbox.transaction { transaction -> [ExportedChatFolderLink]? in switch result { case let .exportedInvites(invites, chats, users): - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } - for chat in chats { - if let peer = parseTelegramGroupOrChannel(chat: chat) { - peers.append(peer) - } - } - - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences) + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) var result: [ExportedChatFolderLink] = [] for invite in invites { @@ -280,6 +265,7 @@ public final class ChatFolderLinkContents { } func _internal_checkChatFolderLink(account: Account, slug: String) -> Signal { + let accountPeerId = account.peerId return account.network.request(Api.functions.chatlists.checkChatlistInvite(slug: slug)) |> mapError { _ -> CheckChatFolderLinkError in return .generic @@ -290,19 +276,10 @@ func _internal_checkChatFolderLink(account: Account, slug: String) -> Signal Signal Peer in - return updated - }) - updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) var resultPeers: [EnginePeer] = [] var alreadyMemberPeerIds = Set() @@ -329,19 +303,10 @@ func _internal_checkChatFolderLink(account: Account, slug: String) -> Signal Signal Peer in - return updated - }) - updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) let currentFilters = _internal_currentChatListFilters(transaction: transaction) var currentFilterTitle: String? @@ -602,6 +564,7 @@ private struct FirstTimeFolderUpdatesKey: Hashable { private var firstTimeFolderUpdates = Set() func _internal_pollChatFolderUpdatesOnce(account: Account, folderId: Int32) -> Signal { + let accountPeerId = account.peerId return account.postbox.transaction { transaction -> (ChatListFiltersState, AppConfiguration) in return (_internal_currentChatListFiltersState(transaction: transaction), currentAppConfiguration(transaction: transaction)) } @@ -654,19 +617,10 @@ func _internal_pollChatFolderUpdatesOnce(account: Account, folderId: Int32) -> S switch result { case let .chatlistUpdates(missingPeers, chats, users): return account.postbox.transaction { transaction -> Void in - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) var memberCounts: [ChatListFiltersState.ChatListFilterUpdates.MemberCount] = [] - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } for chat in chats { - if let peer = parseTelegramGroupOrChannel(chat: chat) { - peers.append(peer) - } if case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _) = chat { if let participantsCount = participantsCount { memberCounts.append(ChatListFiltersState.ChatListFilterUpdates.MemberCount(id: chat.peerId, count: participantsCount)) @@ -674,10 +628,7 @@ func _internal_pollChatFolderUpdatesOnce(account: Account, folderId: Int32) -> S } } - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) let _ = updateChatListFiltersState(transaction: transaction, { state in var state = state diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/FindChannelById.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/FindChannelById.swift index 7393db32fa..adac9d6d67 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/FindChannelById.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/FindChannelById.swift @@ -3,35 +3,32 @@ import SwiftSignalKit import Postbox import TelegramApi -func _internal_findChannelById(postbox: Postbox, network: Network, channelId: Int64) -> Signal { +func _internal_findChannelById(accountPeerId: PeerId, postbox: Postbox, network: Network, channelId: Int64) -> Signal { return network.request(Api.functions.channels.getChannels(id: [.inputChannel(channelId: channelId, accessHash: 0)])) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) } |> mapToSignal { result -> Signal in - guard let result = result else { - return .single(nil) - } - let chats: [Api.Chat] - switch result { + return postbox.transaction { transaction -> Peer? in + guard let result = result else { + return nil + } + let chats: [Api.Chat] + switch result { case let .chats(apiChats): chats = apiChats case let .chatsSlice(_, apiChats): chats = apiChats - } - guard let chat = chats.first else { - return .single(nil) - } - guard let peer = parseTelegramGroupOrChannel(chat: chat) else { - return .single(nil) - } - - return postbox.transaction { transaction -> Peer? in - updatePeers(transaction: transaction, peers: [peer], update: { _, updated in - return updated - }) - return peer + } + guard let id = chats.first?.peerId else { + return nil + } + + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: []) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) + + return transaction.getPeer(id) } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/GroupsInCommon.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/GroupsInCommon.swift index a62ae7ea51..6e2833041e 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/GroupsInCommon.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/GroupsInCommon.swift @@ -61,6 +61,7 @@ private final class GroupsInCommonContextImpl { let maxId = self.peers.last?.peerId.id let peerId = self.peerId + let accountPeerId = self.account.peerId let network = self.account.network let postbox = self.account.postbox let signal: Signal<([Peer], Int), NoError> = self.account.postbox.transaction { transaction -> Api.InputUser? in @@ -94,16 +95,15 @@ private final class GroupsInCommonContextImpl { return postbox.transaction { transaction -> ([Peer], Int) in + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: []) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) + var peers: [Peer] = [] for chat in chats { - if let peer = parseTelegramGroupOrChannel(chat: chat) { + if let peer = transaction.getPeer(chat.peerId) { peers.append(peer) } } - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer? in - return updated - }) - return (peers, count ?? 0) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/InvitationLinks.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/InvitationLinks.swift index 5a2ebcea7d..c8c86a29cf 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/InvitationLinks.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/InvitationLinks.swift @@ -124,6 +124,7 @@ public enum EditPeerExportedInvitationError { } func _internal_editPeerExportedInvitation(account: Account, peerId: PeerId, link: String, title: String?, expireDate: Int32?, usageLimit: Int32?, requestNeeded: Bool?) -> Signal { + let accountPeerId = account.peerId return account.postbox.transaction { transaction -> Signal in if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { var flags: Int32 = 0 @@ -144,14 +145,7 @@ func _internal_editPeerExportedInvitation(account: Account, peerId: PeerId, link |> mapToSignal { result -> Signal in return account.postbox.transaction { transaction in if case let .exportedChatInvite(invite, users) = result { - var peers: [Peer] = [] - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - } - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users)) return ExportedInvitation(apiExportedInvite: invite) } else { return nil @@ -176,6 +170,7 @@ public enum RevokeExportedInvitationResult { } func _internal_revokePeerExportedInvitation(account: Account, peerId: PeerId, link: String) -> Signal { + let accountPeerId = account.peerId return account.postbox.transaction { transaction -> Signal in if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { let flags: Int32 = (1 << 2) @@ -184,24 +179,10 @@ func _internal_revokePeerExportedInvitation(account: Account, peerId: PeerId, li |> mapToSignal { result -> Signal in return account.postbox.transaction { transaction in if case let .exportedChatInvite(invite, users) = result { - var peers: [Peer] = [] - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - } - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users)) return .update(ExportedInvitation(apiExportedInvite: invite)) } else if case let .exportedChatInviteReplaced(invite, newInvite, users) = result { - var peers: [Peer] = [] - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - } - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users)) let previous = ExportedInvitation(apiExportedInvite: invite) let new = ExportedInvitation(apiExportedInvite: newInvite) @@ -242,6 +223,7 @@ public struct ExportedInvitations : Equatable { } func _internal_peerExportedInvitations(account: Account, peerId: PeerId, revoked: Bool, adminId: PeerId? = nil, offsetLink: ExportedInvitation? = nil) -> Signal { + let accountPeerId = account.peerId return account.postbox.transaction { transaction -> Signal in if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer), let adminPeer = transaction.getPeer(adminId ?? account.peerId), let adminId = apiInputUser(adminPeer) { var flags: Int32 = 0 @@ -259,16 +241,8 @@ func _internal_peerExportedInvitations(account: Account, peerId: PeerId, revoked |> mapToSignal { result -> Signal in return account.postbox.transaction { transaction -> ExportedInvitations? in if let result = result, case let .exportedChatInvites(count, apiInvites, users) = result { - var peers: [Peer] = [] - var peersMap: [PeerId: Peer] = [:] - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peersMap[telegramUser.id] = telegramUser - } - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: [], users: users) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) let invites = apiInvites.map { ExportedInvitation(apiExportedInvite: $0) } return ExportedInvitations(list: invites, totalCount: count) @@ -447,6 +421,7 @@ private final class PeerExportedInvitationsContextImpl { } self.isLoadingMore = true let account = self.account + let accountPeerId = account.peerId let peerId = self.peerId let adminId = self.adminId let revoked = self.revoked @@ -488,13 +463,7 @@ private final class PeerExportedInvitationsContextImpl { } switch result { case let .exportedChatInvites(count, invites, users): - var peers: [Peer] = [] - for apiUser in users { - peers.append(TelegramUser(user: apiUser)) - } - updatePeers(transaction: transaction, peers: peers, update: { _, updated in - return updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users)) let invitations: [ExportedInvitation] = invites.compactMap { ExportedInvitation(apiExportedInvite: $0) } if populateCache { if let entry = CodableEntry(CachedPeerExportedInvitations(invitations: invitations, canLoadMore: count >= 50, count: count)) { @@ -916,6 +885,7 @@ private final class PeerInvitationImportersContextImpl { } self.isLoadingMore = true let account = self.account + let accountPeerId = account.peerId let peerId = self.peerId let link = self.link let populateCache = self.populateCache @@ -968,13 +938,7 @@ private final class PeerInvitationImportersContextImpl { } switch result { case let .chatInviteImporters(count, importers, users): - var peers: [Peer] = [] - for apiUser in users { - peers.append(TelegramUser(user: apiUser)) - } - updatePeers(transaction: transaction, peers: peers, update: { _, updated in - return updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users)) var resultImporters: [PeerInvitationImportersState.Importer] = [] for importer in importers { let peerId: PeerId @@ -1050,7 +1014,7 @@ private final class PeerInvitationImportersContextImpl { self.updateDisposables.add(self.account.postbox.transaction({ transaction in let peer = transaction.getPeer(self.peerId) if let peer = peer as? TelegramGroup { - updatePeers(transaction: transaction, peers: [peer], update: { current, _ in + updatePeersCustom(transaction: transaction, peers: [peer], update: { current, _ in var updated = current if let current = current as? TelegramGroup { updated = current.updateParticipantCount(current.participantCount + 1) @@ -1167,6 +1131,7 @@ public struct ExportedInvitationCreator : Equatable { } func _internal_peerExportedInvitationsCreators(account: Account, peerId: PeerId) -> Signal<[ExportedInvitationCreator], NoError> { + let accountPeerId = account.peerId return account.postbox.transaction { transaction -> Signal<[ExportedInvitationCreator], NoError> in if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { var isCreator = false @@ -1187,27 +1152,20 @@ func _internal_peerExportedInvitationsCreators(account: Account, peerId: PeerId) return account.postbox.transaction { transaction -> [ExportedInvitationCreator] in if let result = result, case let .chatAdminsWithInvites(admins, users) = result { var creators: [ExportedInvitationCreator] = [] - var peers: [Peer] = [] - var peersMap: [PeerId: Peer] = [:] - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peersMap[telegramUser.id] = telegramUser - } + + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: [], users: users) for admin in admins { switch admin { case let .chatAdminWithInvites(adminId, invitesCount, revokedInvitesCount): let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(adminId)) - if let peer = peersMap[peerId], peerId != account.peerId { + if let peer = parsedPeers.get(peerId), peerId != account.peerId { creators.append(ExportedInvitationCreator(peer: RenderedPeer(peer: peer), count: invitesCount, revokedCount: revokedInvitesCount)) } } } - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) return creators } else { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/JoinLink.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/JoinLink.swift index 2b9d7a39a6..2fc7134648 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/JoinLink.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/JoinLink.swift @@ -89,6 +89,7 @@ func _internal_joinChatInteractively(with hash: String, account: Account) -> Sig } func _internal_joinLinkInformation(_ hash: String, account: Account) -> Signal { + let accountPeerId = account.peerId return account.network.request(Api.functions.messages.checkChatInvite(hash: hash), automaticFloodWait: false) |> map(Optional.init) |> `catch` { error -> Signal in @@ -108,10 +109,8 @@ func _internal_joinLinkInformation(_ hash: String, account: Account) -> Signal ExternalJoiningChatState in - updatePeers(transaction: transaction, peers: [peer], update: { (previous, updated) -> Peer? in - return updated - }) - + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: [chat], users: []) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) return .alreadyJoined(EnginePeer(peer)) }) |> castError(JoinLinkInfoError.self) @@ -120,9 +119,8 @@ func _internal_joinLinkInformation(_ hash: String, account: Account) -> Signal ExternalJoiningChatState in - updatePeers(transaction: transaction, peers: [peer], update: { (previous, updated) -> Peer? in - return updated - }) + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: [chat], users: []) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) return .peek(EnginePeer(peer), expires) }) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ManageChannelDiscussionGroup.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ManageChannelDiscussionGroup.swift index 8f6a35ba9e..c55c93938b 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ManageChannelDiscussionGroup.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ManageChannelDiscussionGroup.swift @@ -8,7 +8,7 @@ public enum AvailableChannelDiscussionGroupError { case generic } -func _internal_availableGroupsForChannelDiscussion(postbox: Postbox, network: Network) -> Signal<[Peer], AvailableChannelDiscussionGroupError> { +func _internal_availableGroupsForChannelDiscussion(accountPeerId: PeerId, postbox: Postbox, network: Network) -> Signal<[Peer], AvailableChannelDiscussionGroupError> { return network.request(Api.functions.channels.getGroupsForDiscussion()) |> mapError { error in return .generic @@ -16,15 +16,17 @@ func _internal_availableGroupsForChannelDiscussion(postbox: Postbox, network: Ne |> mapToSignal { result -> Signal<[Peer], AvailableChannelDiscussionGroupError> in let chats: [Api.Chat] switch result { - case let .chats(c): - chats = c - case let .chatsSlice(_, c): - chats = c + case let .chats(c): + chats = c + case let .chatsSlice(_, c): + chats = c } - let peers = chats.compactMap(parseTelegramGroupOrChannel) - return postbox.transaction { transation -> [Peer] in - updatePeers(transaction: transation, peers: peers, update: { _, updated in updated }) + return postbox.transaction { transaction -> [Peer] in + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: []) + let peers = chats.compactMap(parseTelegramGroupOrChannel) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) + return peers } |> castError(AvailableChannelDiscussionGroupError.self) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/NotificationExceptionsList.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/NotificationExceptionsList.swift index 96ab021bc3..a9262a0490 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/NotificationExceptionsList.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/NotificationExceptionsList.swift @@ -18,7 +18,7 @@ public final class NotificationExceptionsList: Equatable { } } -func _internal_notificationExceptionsList(postbox: Postbox, network: Network) -> Signal { +func _internal_notificationExceptionsList(accountPeerId: PeerId, postbox: Postbox, network: Network) -> Signal { var flags: Int32 = 0 flags |= 1 << 1 flags |= 1 << 2 @@ -29,21 +29,18 @@ func _internal_notificationExceptionsList(postbox: Postbox, network: Network) -> return postbox.transaction { transaction -> NotificationExceptionsList in switch result { case let .updates(updates, users, chats, _, _): - var peers: [PeerId: Peer] = [:] var settings: [PeerId: TelegramPeerNotificationSettings] = [:] - for user in users { - let peer = TelegramUser(user: user) - peers[peer.id] = peer - } - for chat in chats { - if let peer = parseTelegramGroupOrChannel(chat: chat) { + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) + updatePeers(transaction: transaction, accountPeerId: accountPeerId,peers: parsedPeers) + + var peers: [PeerId: Peer] = [:] + for id in parsedPeers.allIds { + if let peer = transaction.getPeer(id) { peers[peer.id] = peer } } - updatePeers(transaction: transaction, peers: Array(peers.values), update: { _, updated in updated }) - for update in updates { switch update { case let .updateNotifySettings(apiPeer, notifySettings): diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/PeerPhotoUpdater.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/PeerPhotoUpdater.swift index 45b0c9e92a..d3c9560b37 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/PeerPhotoUpdater.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/PeerPhotoUpdater.swift @@ -283,7 +283,7 @@ func _internal_updatePeerPhotoInternal(postbox: Postbox, network: Network, state } return postbox.transaction { transaction -> (UpdatePeerPhotoStatus, MediaResource?, MediaResource?) in if let peer = transaction.getPeer(peer.id) { - updatePeers(transaction: transaction, peers: [peer], update: { (_, peer) -> Peer? in + updatePeersCustom(transaction: transaction, peers: [peer], update: { (_, peer) -> Peer? in if let peer = peer as? TelegramUser { if customPeerPhotoMode == .suggest || fallback { return peer @@ -369,7 +369,7 @@ func _internal_updatePeerPhotoInternal(postbox: Postbox, network: Network, state } return postbox.transaction { transaction -> (UpdatePeerPhotoStatus, MediaResource?, MediaResource?) in - updatePeers(transaction: transaction, peers: [groupOrChannel], update: { _, updated in + updatePeersCustom(transaction: transaction, peers: [groupOrChannel], update: { _, updated in return updated }) return (.complete(groupOrChannel.profileImageRepresentations), photoResult.resource, videoResult?.resource) @@ -471,7 +471,7 @@ func _internal_updatePeerPhotoInternal(postbox: Postbox, network: Network, state } return postbox.transaction { transaction -> UpdatePeerPhotoStatus in if let peer = transaction.getPeer(peer.id) { - updatePeers(transaction: transaction, peers: [peer], update: { (_, peer) -> Peer? in + updatePeersCustom(transaction: transaction, peers: [peer], update: { (_, peer) -> Peer? in if let peer = peer as? TelegramUser { if customPeerPhotoMode == .suggest || fallback { return peer @@ -499,7 +499,7 @@ func _internal_updatePeerPhotoInternal(postbox: Postbox, network: Network, state updatedUsers = apiUsers.map { TelegramUser(user: $0) } } return postbox.transaction { transaction -> UpdatePeerPhotoStatus in - updatePeers(transaction: transaction, peers: updatedUsers, update: { (_, updatedPeer) -> Peer? in + updatePeersCustom(transaction: transaction, peers: updatedUsers, update: { (_, updatedPeer) -> Peer? in return updatedPeer }) if fallback { @@ -542,7 +542,7 @@ func _internal_updatePeerPhotoInternal(postbox: Postbox, network: Network, state if chat.peerId == peer.id { if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { return postbox.transaction { transaction -> UpdatePeerPhotoStatus in - updatePeers(transaction: transaction, peers: [groupOrChannel], update: { _, updated in + updatePeersCustom(transaction: transaction, peers: [groupOrChannel], update: { _, updated in return updated }) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/RecentPeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/RecentPeers.swift index b298bc0f0f..7dd5e98da8 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/RecentPeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/RecentPeers.swift @@ -65,18 +65,10 @@ func _internal_managedUpdatedRecentPeers(accountPeerId: PeerId, postbox: Postbox return postbox.transaction { transaction -> Void in switch result { case let .topPeers(_, _, users): - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } - updatePeers(transaction: transaction, peers: peers, update: { return $1 }) - - updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) + let parsedPeers = AccumulatedPeers(users: users) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) - if let entry = CodableEntry(CachedRecentPeers(enabled: true, ids: peers.map { $0.id })) { + if let entry = CodableEntry(CachedRecentPeers(enabled: true, ids: users.map { $0.peerId })) { transaction.putItemCacheEntry(id: cachedRecentPeersEntryId(), entry: entry) } case .topPeersNotModified: @@ -155,59 +147,52 @@ func _internal_updateRecentPeersEnabled(postbox: Postbox, network: Network, enab func _internal_managedRecentlyUsedInlineBots(postbox: Postbox, network: Network, accountPeerId: PeerId) -> Signal { let remotePeers = network.request(Api.functions.contacts.getTopPeers(flags: 1 << 2, offset: 0, limit: 16, hash: 0)) - |> retryRequest - |> map { result -> ([Peer], [PeerId: Api.User], [(PeerId, Double)])? in - switch result { - case .topPeersDisabled: - break - case let .topPeers(categories, _, users): - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } - var peersWithRating: [(PeerId, Double)] = [] - for category in categories { - switch category { - case let .topPeerCategoryPeers(_, _, topPeers): - for topPeer in topPeers { - switch topPeer { - case let .topPeer(apiPeer, rating): - peersWithRating.append((apiPeer.peerId, rating)) - } - } + |> retryRequest + |> map { result -> (AccumulatedPeers, [(PeerId, Double)])? in + switch result { + case .topPeersDisabled: + break + case let .topPeers(categories, _, users): + let parsedPeers = AccumulatedPeers(users: users) + + var peersWithRating: [(PeerId, Double)] = [] + for category in categories { + switch category { + case let .topPeerCategoryPeers(_, _, topPeers): + for topPeer in topPeers { + switch topPeer { + case let .topPeer(apiPeer, rating): + peersWithRating.append((apiPeer.peerId, rating)) } } - return (peers, peerPresences, peersWithRating) - case .topPeersNotModified: - break + } } - return ([], [:], []) + return (parsedPeers, peersWithRating) + case .topPeersNotModified: + break + } + return (AccumulatedPeers(), []) } let updatedRemotePeers = remotePeers - |> mapToSignal { peersAndPresences -> Signal in - if let (peers, peerPresences, peersWithRating) = peersAndPresences { - return postbox.transaction { transaction -> Void in - updatePeers(transaction: transaction, peers: peers, update: { return $1 }) - - updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) - - let sortedPeersWithRating = peersWithRating.sorted(by: { $0.1 > $1.1 }) - - transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudRecentInlineBots, items: sortedPeersWithRating.compactMap { (peerId, rating) in - if let entry = CodableEntry(RecentPeerItem(rating: rating)) { - return OrderedItemListEntry(id: RecentPeerItemId(peerId).rawValue, contents: entry) - } else { - return nil - } - }) - } - } else { - return .complete() + |> mapToSignal { peersAndPresences -> Signal in + if let (parsedPeers, peersWithRating) = peersAndPresences { + return postbox.transaction { transaction -> Void in + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) + + let sortedPeersWithRating = peersWithRating.sorted(by: { $0.1 > $1.1 }) + + transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudRecentInlineBots, items: sortedPeersWithRating.compactMap { (peerId, rating) in + if let entry = CodableEntry(RecentPeerItem(rating: rating)) { + return OrderedItemListEntry(id: RecentPeerItemId(peerId).rawValue, contents: entry) + } else { + return nil + } + }) } + } else { + return .complete() + } } return updatedRemotePeers diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/RemovePeerChat.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/RemovePeerChat.swift index f9f556dca3..9c6d805d88 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/RemovePeerChat.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/RemovePeerChat.swift @@ -15,7 +15,7 @@ func _internal_terminateSecretChat(transaction: Transaction, peerId: PeerId, req if updatedState != state { transaction.setPeerChatState(peerId, state: updatedState) if let peer = transaction.getPeer(peerId) as? TelegramSecretChat { - updatePeers(transaction: transaction, peers: [peer.withUpdatedEmbeddedState(updatedState.embeddedState.peerState)], update: { _, updated in + updatePeersCustom(transaction: transaction, peers: [peer.withUpdatedEmbeddedState(updatedState.embeddedState.peerState)], update: { _, updated in return updated }) } @@ -52,7 +52,7 @@ func _internal_removePeerChat(account: Account, transaction: Transaction, mediaB if updatedState != state { transaction.setPeerChatState(peerId, state: updatedState) if let peer = transaction.getPeer(peerId) as? TelegramSecretChat { - updatePeers(transaction: transaction, peers: [peer.withUpdatedEmbeddedState(updatedState.embeddedState.peerState)], update: { _, updated in + updatePeersCustom(transaction: transaction, peers: [peer.withUpdatedEmbeddedState(updatedState.embeddedState.peerState)], update: { _, updated in return updated }) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ResolvePeerByName.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ResolvePeerByName.swift index fbc388cbc3..beacb44396 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ResolvePeerByName.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ResolvePeerByName.swift @@ -20,6 +20,8 @@ func _internal_resolvePeerByName(account: Account, name: String, ageLimit: Int32 normalizedName = String(normalizedName[name.index(after: name.startIndex)...]) } + let accountPeerId = account.peerId + return account.postbox.transaction { transaction -> CachedResolvedByNamePeer? in return transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.resolvedByNamePeers, key: CachedResolvedByNamePeer.key(name: normalizedName)))?.get(CachedResolvedByNamePeer.self) } |> mapToSignal { cachedEntry -> Signal in @@ -36,28 +38,14 @@ func _internal_resolvePeerByName(account: Account, name: String, ageLimit: Int32 var peerId: PeerId? = nil switch result { - case let .resolvedPeer(apiPeer, chats, users): - var peers: [PeerId: Peer] = [:] - - for user in users { - if let user = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) { - peers[user.id] = user - } - } - - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers[groupOrChannel.id] = groupOrChannel - } - } + case let .resolvedPeer(apiPeer, chats, users): + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) - if let peer = peers[apiPeer.peerId] { - peerId = peer.id - - updatePeers(transaction: transaction, peers: Array(peers.values), update: { _, updated -> Peer in - return updated - }) - } + if let peer = parsedPeers.get(apiPeer.peerId) { + peerId = peer.id + + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) + } } let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) @@ -81,6 +69,8 @@ func _internal_resolvePeerByPhone(account: Account, phone: String, ageLimit: Int normalizedPhone = String(normalizedPhone[normalizedPhone.index(after: normalizedPhone.startIndex)...]) } + let accountPeerId = account.peerId + return account.postbox.transaction { transaction -> CachedResolvedByPhonePeer? in return transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.resolvedByPhonePeers, key: CachedResolvedByPhonePeer.key(name: normalizedPhone)))?.get(CachedResolvedByPhonePeer.self) } |> mapToSignal { cachedEntry -> Signal in @@ -98,26 +88,12 @@ func _internal_resolvePeerByPhone(account: Account, phone: String, ageLimit: Int switch result { case let .resolvedPeer(apiPeer, chats, users): - var peers: [PeerId: Peer] = [:] - - for user in users { - if let user = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) { - peers[user.id] = user - } - } - - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers[groupOrChannel.id] = groupOrChannel - } - } + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) - if let peer = peers[apiPeer.peerId] { + if let peer = parsedPeers.get(apiPeer.peerId) { peerId = peer.id - updatePeers(transaction: transaction, peers: Array(peers.values), update: { _, updated -> Peer in - return updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift index 1157f25495..a4bedb6de6 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift @@ -20,6 +20,8 @@ public struct FoundPeer: Equatable { } func _internal_searchPeers(account: Account, query: String) -> Signal<([FoundPeer], [FoundPeer]), NoError> { + let accountPeerId = account.peerId + let searchResult = account.network.request(Api.functions.contacts.search(q: query, limit: 20), automaticFloodWait: false) |> map(Optional.init) |> `catch` { _ in @@ -31,37 +33,29 @@ func _internal_searchPeers(account: Account, query: String) -> Signal<([FoundPee switch result { case let .found(myResults, results, chats, users): return account.postbox.transaction { transaction -> ([FoundPeer], [FoundPeer]) in - var peers: [PeerId: Peer] = [:] var subscribers: [PeerId: Int32] = [:] - for user in users { - if let user = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) { - peers[user.id] = user - } - } + + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) for chat in chats { if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers[groupOrChannel.id] = groupOrChannel switch chat { - /*feed*/ - case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _): - if let participantsCount = participantsCount { - subscribers[groupOrChannel.id] = participantsCount - } - default: - break + case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _): + if let participantsCount = participantsCount { + subscribers[groupOrChannel.id] = participantsCount + } + default: + break } } } - updatePeers(transaction: transaction, peers: Array(peers.values), update: { _, updated in - return updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) var renderedMyPeers: [FoundPeer] = [] for result in myResults { let peerId: PeerId = result.peerId - if let peer = peers[peerId] { + if let peer = parsedPeers.get(peerId) { if let group = peer as? TelegramGroup, group.migrationReference != nil { continue } @@ -72,7 +66,7 @@ func _internal_searchPeers(account: Account, query: String) -> Signal<([FoundPee var renderedPeers: [FoundPeer] = [] for result in results { let peerId: PeerId = result.peerId - if let peer = peers[peerId] { + if let peer = parsedPeers.get(peerId) { if let group = peer as? TelegramGroup, group.migrationReference != nil { continue } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/SupportPeerId.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/SupportPeerId.swift index 7553f724bf..d234bd6a3a 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/SupportPeerId.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/SupportPeerId.swift @@ -5,6 +5,8 @@ import MtProtoKit func _internal_supportPeerId(account: Account) -> Signal { + let accountPeerId = account.peerId + return account.network.request(Api.functions.help.getSupport()) |> map(Optional.init) |> `catch` { _ in @@ -13,14 +15,12 @@ func _internal_supportPeerId(account: Account) -> Signal { |> mapToSignal { support -> Signal in if let support = support { switch support { - case let .support(phoneNumber: _, user: user): - let user = TelegramUser(user: user) - return account.postbox.transaction { transaction -> PeerId in - updatePeers(transaction: transaction, peers: [user], update: { (previous, updated) -> Peer? in - return updated - }) - return user.id - } + case let .support(_, user): + return account.postbox.transaction { transaction -> PeerId in + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: [], users: [user]) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) + return user.peerId + } } } return .single(nil) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index 44851be0a7..6875dfa3bf 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -93,7 +93,7 @@ public extension TelegramEngine { } public func findChannelById(channelId: Int64) -> Signal { - return _internal_findChannelById(postbox: self.account.postbox, network: self.account.network, channelId: channelId) + return _internal_findChannelById(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network, channelId: channelId) |> map { peer in return peer.flatMap(EnginePeer.init) } @@ -132,7 +132,7 @@ public extension TelegramEngine { } public func updatedRemotePeer(peer: PeerReference) -> Signal { - return _internal_updatedRemotePeer(postbox: self.account.postbox, network: self.account.network, peer: peer) + return _internal_updatedRemotePeer(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network, peer: peer) } public func chatOnlineMembers(peerId: PeerId) -> Signal { @@ -327,7 +327,7 @@ public extension TelegramEngine { } public func channelAdminEventLog(peerId: PeerId) -> ChannelAdminEventLogContext { - return ChannelAdminEventLogContext(postbox: self.account.postbox, network: self.account.network, peerId: peerId) + return ChannelAdminEventLogContext(postbox: self.account.postbox, network: self.account.network, peerId: peerId, accountPeerId: self.account.peerId) } public func updateChannelMemberBannedRights(peerId: PeerId, memberId: PeerId, rights: TelegramChatBannedRights?) -> Signal<(ChannelParticipant?, RenderedChannelParticipant?, Bool), NoError> { @@ -415,7 +415,7 @@ public extension TelegramEngine { } public func availableGroupsForChannelDiscussion() -> Signal<[EnginePeer], AvailableChannelDiscussionGroupError> { - return _internal_availableGroupsForChannelDiscussion(postbox: self.account.postbox, network: self.account.network) + return _internal_availableGroupsForChannelDiscussion(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network) |> map { peers -> [EnginePeer] in return peers.map(EnginePeer.init) } @@ -637,7 +637,7 @@ public extension TelegramEngine { } public func notificationExceptionsList() -> Signal { - return _internal_notificationExceptionsList(postbox: self.account.postbox, network: self.account.network) + return _internal_notificationExceptionsList(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network) } public func fetchAndUpdateCachedPeerData(peerId: PeerId) -> Signal { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateBotInfo.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateBotInfo.swift index d4c6179bc7..3d24832545 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateBotInfo.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateBotInfo.swift @@ -29,7 +29,7 @@ func _internal_updateBotName(account: Account, peerId: PeerId, name: String) -> return current } }) - updatePeers(transaction: transaction, peers: [peer]) { _, peer in + updatePeersCustom(transaction: transaction, peers: [peer]) { _, peer in var updatedPeer = peer if let user = peer as? TelegramUser, user.firstName == previousBotName { updatedPeer = user.withUpdatedNames(firstName: name, lastName: nil) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift index 6015b6f45e..114b06ae4c 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift @@ -78,30 +78,17 @@ func fetchAndUpdateSupplementalCachedPeerData(peerId rawPeerId: PeerId, accountP return network.request(Api.functions.messages.getPeerSettings(peer: inputPeer)) |> retryRequest |> mapToSignal { peerSettings -> Signal in - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - - let peerStatusSettings: PeerStatusSettings - switch peerSettings { + return postbox.transaction { transaction -> Bool in + let parsedPeers: AccumulatedPeers + + let peerStatusSettings: PeerStatusSettings + switch peerSettings { case let .peerSettings(settings, chats, users): peerStatusSettings = PeerStatusSettings(apiSettings: settings) - for chat in chats { - if let peer = parseTelegramGroupOrChannel(chat: chat) { - peers.append(peer) - } - } - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } - } - - return postbox.transaction { transaction -> Bool in - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) + parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) + } + + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) transaction.updatePeerCachedData(peerIds: Set([peer.id]), update: { _, current in switch peer.id.namespace { @@ -220,32 +207,18 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee switch result { case let .userFull(fullUser, chats, users): var accountUser: Api.User? - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - for chat in chats { - if let peer = parseTelegramGroupOrChannel(chat: chat) { - peers.append(peer) - } - } + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - if telegramUser.id == accountPeerId { + if user.peerId == accountPeerId { accountUser = user } } + let _ = accountUser switch fullUser { case let .userFull(_, _, _, _, _, _, _, userFullNotifySettings, _, _, _, _, _, _, _, _, _, _, _, _): - updatePeers(transaction: transaction, peers: peers, update: { previous, updated -> Peer in - if previous?.id == accountPeerId, let accountUser = accountUser, let user = TelegramUser.merge(previous as? TelegramUser, rhs: accountUser) { - return user - } - return updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) transaction.updateCurrentPeerNotificationSettings([peerId: TelegramPeerNotificationSettings(apiSettings: userFullNotifySettings)]) - updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) } transaction.updatePeerCachedData(peerIds: [peerId], update: { peerId, current in let previous: CachedUserData @@ -368,25 +341,8 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee let exportedInvitation = chatFullExportedInvite.flatMap { ExportedInvitation(apiExportedInvite: $0) } let pinnedMessageId = chatFullPinnedMsgId.flatMap({ MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) }) - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers.append(groupOrChannel) - } - } - for user in users { - if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) { - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } - } - - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - - updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) var flags = CachedGroupFlags() if (chatFullFlags & 1 << 7) != 0 { @@ -561,42 +517,16 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee migrationReference = ChannelMigrationReference(maxMessageId: MessageId(peerId: PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(migratedFromChatId)), namespace: Namespaces.Message.Cloud, id: migratedFromMaxId)) } - var peers: [Peer] = [] - var peerPresences: [PeerId: Api.User] = [:] - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers.append(groupOrChannel) - } - } - for user in users { - if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) { - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } - } + var parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) if let participantResult = participantResult { switch participantResult { case let .channelParticipant(_, chats, users): - for user in users { - if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) { - peers.append(telegramUser) - peerPresences[telegramUser.id] = user - } - } - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers.append(groupOrChannel) - } - } + parsedPeers = parsedPeers.union(with: AccumulatedPeers(transaction: transaction, chats: chats, users: users)) } } - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - - updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) let stickerPack: StickerPackCollectionInfo? = stickerSet.flatMap { apiSet -> StickerPackCollectionInfo in let namespace: ItemCollectionId.Namespace diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdatePeerInfo.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdatePeerInfo.swift index 47935d495e..8b7f2b7b8e 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdatePeerInfo.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdatePeerInfo.swift @@ -10,6 +10,7 @@ public enum UpdatePeerTitleError { } func _internal_updatePeerTitle(account: Account, peerId: PeerId, title: String) -> Signal { + let accountPeerId = account.peerId return account.postbox.transaction { transaction -> Signal in if let peer = transaction.getPeer(peerId) { if let peer = peer as? TelegramChannel, let inputChannel = apiInputChannel(peer) { @@ -21,10 +22,9 @@ func _internal_updatePeerTitle(account: Account, peerId: PeerId, title: String) account.stateManager.addUpdates(result) return account.postbox.transaction { transaction -> Void in - if let apiChat = apiUpdatesGroups(result).first, let updatedPeer = parseTelegramGroupOrChannel(chat: apiChat) { - updatePeers(transaction: transaction, peers: [updatedPeer], update: { _, updated in - return updated - }) + if let apiChat = apiUpdatesGroups(result).first { + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: [apiChat], users: []) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) } } |> mapError { _ -> UpdatePeerTitleError in } } @@ -37,10 +37,9 @@ func _internal_updatePeerTitle(account: Account, peerId: PeerId, title: String) account.stateManager.addUpdates(result) return account.postbox.transaction { transaction -> Void in - if let apiChat = apiUpdatesGroups(result).first, let updatedPeer = parseTelegramGroupOrChannel(chat: apiChat) { - updatePeers(transaction: transaction, peers: [updatedPeer], update: { _, updated in - return updated - }) + if let apiChat = apiUpdatesGroups(result).first { + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: [apiChat], users: []) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) } } |> mapError { _ -> UpdatePeerTitleError in } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Privacy/BlockedPeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Privacy/BlockedPeers.swift index 3569596ec9..565c4f96aa 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Privacy/BlockedPeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Privacy/BlockedPeers.swift @@ -6,35 +6,31 @@ import MtProtoKit public func requestBlockedPeers(account: Account) -> Signal<[Peer], NoError> { + let accountPeerId = account.peerId return account.network.request(Api.functions.contacts.getBlocked(offset: 0, limit: 100)) |> retryRequest |> mapToSignal { result -> Signal<[Peer], NoError> in return account.postbox.transaction { transaction -> [Peer] in - var peers: [Peer] = [] let apiUsers: [Api.User] let apiChats: [Api.Chat] switch result { - case let .blocked(_, chats, users): - apiUsers = users - apiChats = chats - case let .blockedSlice(_, _, chats, users): - apiUsers = users - apiChats = chats + case let .blocked(_, chats, users): + apiUsers = users + apiChats = chats + case let .blockedSlice(_, _, chats, users): + apiUsers = users + apiChats = chats } - for chat in apiChats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers.append(groupOrChannel) + + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: apiChats, users: apiUsers) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) + + var peers: [Peer] = [] + for id in parsedPeers.allIds { + if let peer = transaction.getPeer(id) { + peers.append(peer) } } - for user in apiUsers { - let parsed = TelegramUser(user: user) - peers.append(parsed) - } - - updatePeers(transaction: transaction, peers: peers, update: { _, updated in - return updated - }) - return peers } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Privacy/BlockedPeersContext.swift b/submodules/TelegramCore/Sources/TelegramEngine/Privacy/BlockedPeersContext.swift index 0c8254b358..ef2b18f8f5 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Privacy/BlockedPeersContext.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Privacy/BlockedPeersContext.swift @@ -57,53 +57,38 @@ public final class BlockedPeersContext { } self._state = BlockedPeersContextState(isLoadingMore: true, canLoadMore: self._state.canLoadMore, totalCount: self._state.totalCount, peers: self._state.peers) let postbox = self.account.postbox + let accountPeerId = self.account.peerId self.disposable.set((self.account.network.request(Api.functions.contacts.getBlocked(offset: Int32(self._state.peers.count), limit: 64)) |> retryRequest |> mapToSignal { result -> Signal<(peers: [RenderedPeer], canLoadMore: Bool, totalCount: Int?), NoError> in return postbox.transaction { transaction -> (peers: [RenderedPeer], canLoadMore: Bool, totalCount: Int?) in switch result { case let .blocked(blocked, chats, users): - var peers: [Peer] = [] - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers.append(groupOrChannel) - } - } - for user in users { - peers.append(TelegramUser(user: user)) - } - updatePeers(transaction: transaction, peers: peers, update: { _, updated in updated }) - + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) + var renderedPeers: [RenderedPeer] = [] for blockedPeer in blocked { switch blockedPeer { - case let .peerBlocked(peerId, _): - if let peer = transaction.getPeer(peerId.peerId) { - renderedPeers.append(RenderedPeer(peer: peer)) - } + case let .peerBlocked(peerId, _): + if let peer = transaction.getPeer(peerId.peerId) { + renderedPeers.append(RenderedPeer(peer: peer)) + } } } return (renderedPeers, false, nil) case let .blockedSlice(count, blocked, chats, users): - var peers: [Peer] = [] - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers.append(groupOrChannel) - } - } - for user in users { - peers.append(TelegramUser(user: user)) - } - updatePeers(transaction: transaction, peers: peers, update: { _, updated in updated }) + let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) var renderedPeers: [RenderedPeer] = [] for blockedPeer in blocked { switch blockedPeer { - case let .peerBlocked(peerId, _): - if let peer = transaction.getPeer(peerId.peerId) { - renderedPeers.append(RenderedPeer(peer: peer)) - } + case let .peerBlocked(peerId, _): + if let peer = transaction.getPeer(peerId.peerId) { + renderedPeers.append(RenderedPeer(peer: peer)) + } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Privacy/CloseFriends.swift b/submodules/TelegramCore/Sources/TelegramEngine/Privacy/CloseFriends.swift index 37dee8cde2..46d6913bdf 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Privacy/CloseFriends.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Privacy/CloseFriends.swift @@ -27,7 +27,7 @@ public func _internal_updateCloseFriends(account: Account, peerIds: [EnginePeer. } } } - updatePeers(transaction: transaction, peers: updatedPeers, update: { _, updated in + updatePeersCustom(transaction: transaction, peers: updatedPeers, update: { _, updated in return updated }) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Privacy/UpdatedAccountPrivacySettings.swift b/submodules/TelegramCore/Sources/TelegramEngine/Privacy/UpdatedAccountPrivacySettings.swift index 29061e3983..af9bda38e6 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Privacy/UpdatedAccountPrivacySettings.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Privacy/UpdatedAccountPrivacySettings.swift @@ -160,7 +160,7 @@ func _internal_requestAccountPrivacySettings(account: Account) -> Signal AccountPrivacySettings in - updatePeers(transaction: transaction, peers: peers.map { $0.peer }, update: { _, updated in + updatePeersCustom(transaction: transaction, peers: peers.map { $0.peer }, update: { _, updated in return updated }) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/SecureId/RequestSecureIdForm.swift b/submodules/TelegramCore/Sources/TelegramEngine/SecureId/RequestSecureIdForm.swift index a979781b54..5d09194136 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/SecureId/RequestSecureIdForm.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/SecureId/RequestSecureIdForm.swift @@ -240,7 +240,7 @@ public struct EncryptedSecureIdForm { let errors: [Api.SecureValueError] } -public func requestSecureIdForm(postbox: Postbox, network: Network, peerId: PeerId, scope: String, publicKey: String) -> Signal { +public func requestSecureIdForm(accountPeerId: PeerId, postbox: Postbox, network: Network, peerId: PeerId, scope: String, publicKey: String) -> Signal { if peerId.namespace != Namespaces.Peer.CloudUser { return .fail(.serverError("BOT_INVALID")) } @@ -263,14 +263,7 @@ public func requestSecureIdForm(postbox: Postbox, network: Network, peerId: Peer return postbox.transaction { transaction -> EncryptedSecureIdForm in switch result { case let .authorizationForm(_, requiredTypes, values, errors, users, termsUrl): - var peers: [Peer] = [] - for user in users { - let parsed = TelegramUser(user: user) - peers.append(parsed) - } - updatePeers(transaction: transaction, peers: peers, update: { _, updated in - return updated - }) + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users)) return EncryptedSecureIdForm(peerId: peerId, requestedFields: requiredTypes.map { requiredType in switch requiredType { diff --git a/submodules/TelegramCore/Sources/UpdatePeers.swift b/submodules/TelegramCore/Sources/UpdatePeers.swift index 88e4771928..8e3150945f 100644 --- a/submodules/TelegramCore/Sources/UpdatePeers.swift +++ b/submodules/TelegramCore/Sources/UpdatePeers.swift @@ -2,7 +2,6 @@ import Foundation import Postbox import TelegramApi - func updatePeerChatInclusionWithMinTimestamp(transaction: Transaction, id: PeerId, minTimestamp: Int32, forceRootGroupIfNotExists: Bool) { let currentInclusion = transaction.getPeerChatListInclusion(id) var updatedInclusion: PeerChatListInclusion? @@ -39,7 +38,31 @@ func minTimestampForPeerInclusion(_ peer: Peer) -> Int32? { } } -public func updatePeers(transaction: Transaction, peers: [Peer], update: (Peer?, Peer) -> Peer?) { +func updatePeers(transaction: Transaction, accountPeerId: PeerId, peers: AccumulatedPeers) { + var parsedPeers: [Peer] = [] + for (_, user) in peers.users { + if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) { + parsedPeers.append(telegramUser) + + switch user { + case let .user(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, storiesMaxId): + if let storiesMaxId = storiesMaxId { + transaction.setStoryItemsInexactMaxId(peerId: user.peerId, id: storiesMaxId) + } + case .userEmpty: + break + } + } + } + for (_, peer) in peers.peers { + parsedPeers.append(peer) + } + updatePeersCustom(transaction: transaction, peers: parsedPeers, update: { _, updated in updated }) + + updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peers.users) +} + +public func updatePeersCustom(transaction: Transaction, peers: [Peer], update: (Peer?, Peer) -> Peer?) { transaction.updatePeersInternal(peers, update: { previous, updated in let peerId = updated.id diff --git a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift index 04bfc67df7..29d44b0b9a 100644 --- a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift +++ b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift @@ -223,6 +223,62 @@ func locallyRenderedMessage(message: StoreMessage, peers: [PeerId: Peer], associ return Message(stableId: stableId, stableVersion: 0, id: id, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: message.threadId, timestamp: message.timestamp, flags: MessageFlags(message.flags), tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: forwardInfo, author: author, text: message.text, attributes: message.attributes, media: message.media, peers: messagePeers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: associatedThreadInfo, associatedStories: [:]) } +func locallyRenderedMessage(message: StoreMessage, peers: AccumulatedPeers, associatedThreadInfo: Message.AssociatedThreadInfo? = nil) -> Message? { + guard case let .Id(id) = message.id else { + return nil + } + + var messagePeers = SimpleDictionary() + + var author: Peer? + if let authorId = message.authorId { + author = peers.get(authorId) + if let author = author { + messagePeers[author.id] = author + } + } + + if let peer = peers.get(id.peerId) { + messagePeers[peer.id] = peer + + if let group = peer as? TelegramGroup, let migrationReference = group.migrationReference { + if let channelPeer = peers.get(migrationReference.peerId) { + messagePeers[channelPeer.id] = channelPeer + } + } + } + + for media in message.media { + for peerId in media.peerIds { + if let peer = peers.get(peerId) { + messagePeers[peer.id] = peer + } + } + } + + var forwardInfo: MessageForwardInfo? + if let info = message.forwardInfo { + forwardInfo = MessageForwardInfo(author: info.authorId.flatMap({ peers.get($0) }), source: info.sourceId.flatMap({ peers.get($0) }), sourceMessageId: info.sourceMessageId, date: info.date, authorSignature: info.authorSignature, psaType: info.psaType, flags: info.flags) + if let author = forwardInfo?.author { + messagePeers[author.id] = author + } + if let source = forwardInfo?.source { + messagePeers[source.id] = source + } + } + + var hasher = Hasher() + hasher.combine(id.id) + hasher.combine(id.peerId) + + let hashValue = Int64(hasher.finalize()) + let first = UInt32((hashValue >> 32) & 0xffffffff) + let second = UInt32(hashValue & 0xffffffff) + let stableId = first &+ second + + return Message(stableId: stableId, stableVersion: 0, id: id, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: message.threadId, timestamp: message.timestamp, flags: MessageFlags(message.flags), tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: forwardInfo, author: author, text: message.text, attributes: message.attributes, media: message.media, peers: messagePeers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: associatedThreadInfo, associatedStories: [:]) +} + public extension Message { func effectivelyIncoming(_ accountPeerId: PeerId) -> Bool { if self.id.peerId == accountPeerId { diff --git a/submodules/TelegramCore/Sources/Utils/StoredMessageFromSearchPeer.swift b/submodules/TelegramCore/Sources/Utils/StoredMessageFromSearchPeer.swift index 81aa77a175..949b95a37c 100644 --- a/submodules/TelegramCore/Sources/Utils/StoredMessageFromSearchPeer.swift +++ b/submodules/TelegramCore/Sources/Utils/StoredMessageFromSearchPeer.swift @@ -5,7 +5,7 @@ import SwiftSignalKit func _internal_storedMessageFromSearchPeer(account: Account, peer: Peer) -> Signal { return account.postbox.transaction { transaction -> Peer in if transaction.getPeer(peer.id) == nil { - updatePeers(transaction: transaction, peers: [peer], update: { previousPeer, updatedPeer in + updatePeersCustom(transaction: transaction, peers: [peer], update: { _, updatedPeer in return updatedPeer }) } @@ -24,7 +24,7 @@ func _internal_storedMessageFromSearchPeers(account: Account, peers: [Peer]) -> return account.postbox.transaction { transaction -> Void in for peer in peers { if transaction.getPeer(peer.id) == nil { - updatePeers(transaction: transaction, peers: [peer], update: { previousPeer, updatedPeer in + updatePeersCustom(transaction: transaction, peers: [peer], update: { _, updatedPeer in return updatedPeer }) } @@ -37,7 +37,7 @@ func _internal_storeMessageFromSearch(transaction: Transaction, message: Message if transaction.getMessage(message.id) == nil { for (_, peer) in message.peers { if transaction.getPeer(peer.id) == nil { - updatePeers(transaction: transaction, peers: [peer], update: { previousPeer, updatedPeer in + updatePeersCustom(transaction: transaction, peers: [peer], update: { _, updatedPeer in return updatedPeer }) } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift index 80c4f85dea..63ae439779 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift @@ -438,6 +438,7 @@ public final class StoryContentContextImpl: StoryContentContext { } let singlePeerListContext = PeerExpiringStoryListContext(account: context.account, peerId: focusedPeerId) self.singlePeerListContext = singlePeerListContext + self.storySubscriptionsDisposable = (combineLatest( context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: focusedPeerId)), singlePeerListContext.state @@ -447,6 +448,10 @@ public final class StoryContentContextImpl: StoryContentContext { return } + if state.isLoading { + return + } + let storySubscriptions = EngineStorySubscriptions( accountItem: nil, items: [EngineStorySubscriptions.Item( @@ -579,7 +584,7 @@ public final class StoryContentContextImpl: StoryContentContext { } var sortedItems: [EngineStorySubscriptions.Item] = [] - if let accountItem = storySubscriptions.accountItem { + if !isHidden, let accountItem = storySubscriptions.accountItem { if self.fixedSubscriptionOrder.contains(context.account.peerId) { sortedItems.append(accountItem) } else { diff --git a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift index 6f8155058e..51840617ed 100644 --- a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift @@ -12,16 +12,6 @@ import TelegramPresentationData import StoryContainerScreen import EmojiStatusComponent -public func shouldDisplayStoriesInChatListHeader(storySubscriptions: EngineStorySubscriptions) -> Bool { - if !storySubscriptions.items.isEmpty { - return true - } - if let accountItem = storySubscriptions.accountItem, (accountItem.hasUnseen || accountItem.hasPending) { - return true - } - return false -} - private func solveParabolicMotion(from sourcePoint: CGPoint, to targetPosition: CGPoint, progress: CGFloat) -> CGPoint { if sourcePoint.y == targetPosition.y { return sourcePoint.interpolate(to: targetPosition, amount: progress) @@ -858,9 +848,6 @@ public final class StoryPeerListComponent: Component { self.visibleItems[itemSet.peer.id] = visibleItem } - var hasUnseen = false - hasUnseen = itemSet.hasUnseen - var hasUnseenCloseFriendsItems = itemSet.hasUnseenCloseFriends var hasItems = true @@ -921,6 +908,16 @@ public final class StoryPeerListComponent: Component { rightNeighborDistance = CGPoint(x: abs(rightItemFrame.midX - measuredItem.itemFrame.midX), y: rightItemFrame.minY - measuredItem.itemFrame.minY) } + let totalCount: Int + let unseenCount: Int + if peer.id == component.context.account.peerId { + totalCount = 1 + unseenCount = itemSet.unseenCount != 0 ? 1 : 0 + } else { + totalCount = itemSet.storyCount + unseenCount = itemSet.unseenCount + } + let _ = visibleItem.view.update( transition: itemTransition, component: AnyComponent(StoryPeerListItemComponent( @@ -928,7 +925,8 @@ public final class StoryPeerListComponent: Component { theme: component.theme, strings: component.strings, peer: peer, - hasUnseen: hasUnseen, + totalCount: totalCount, + unseenCount: unseenCount, hasUnseenCloseFriendsItems: hasUnseenCloseFriendsItems, hasItems: hasItems, ringAnimation: itemRingAnimation, @@ -1001,9 +999,6 @@ public final class StoryPeerListComponent: Component { self.visibleCollapsableItems[itemSet.peer.id] = visibleItem } - var hasUnseen = false - hasUnseen = itemSet.hasUnseen - var hasUnseenCloseFriendsItems = itemSet.hasUnseenCloseFriends var hasItems = true @@ -1063,7 +1058,8 @@ public final class StoryPeerListComponent: Component { theme: component.theme, strings: component.strings, peer: peer, - hasUnseen: hasUnseen, + totalCount: 1, + unseenCount: itemSet.unseenCount != 0 ? 1 : 0, hasUnseenCloseFriendsItems: hasUnseenCloseFriendsItems, hasItems: hasItems, ringAnimation: itemRingAnimation, @@ -1141,7 +1137,13 @@ public final class StoryPeerListComponent: Component { let collapsedTitleOffset = targetCollapsedTitleOffset - defaultCollapsedTitleOffset let titleMinContentOffset: CGFloat = collapsedTitleOffset.interpolate(to: collapsedTitleOffset + 12.0, amount: collapsedState.minFraction * (1.0 - collapsedState.activityFraction)) - var titleContentOffset: CGFloat = titleMinContentOffset.interpolate(to: ((itemLayout.containerSize.width - collapsedState.titleWidth) * 0.5) as CGFloat, amount: collapsedState.maxFraction * (1.0 - collapsedState.activityFraction)) + + var titleContentOffset: CGFloat + if self.sortedItems.isEmpty { + titleContentOffset = collapsedTitleOffset + } else { + titleContentOffset = titleMinContentOffset.interpolate(to: ((itemLayout.containerSize.width - collapsedState.titleWidth) * 0.5) as CGFloat, amount: collapsedState.maxFraction * (1.0 - collapsedState.activityFraction)) + } var titleIndicatorSize: CGSize? if collapsedState.activityFraction != 0.0 { diff --git a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListItemComponent.swift b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListItemComponent.swift index 6d3d8cbae7..57f2bbfbfa 100644 --- a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListItemComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListItemComponent.swift @@ -56,7 +56,7 @@ private func calculateCircleIntersection(center: CGPoint, otherCenter: CGPoint, return (point1Angle, point2Angle) } -private func calculateMergingCircleShape(center: CGPoint, leftCenter: CGPoint?, rightCenter: CGPoint?, radius: CGFloat) -> CGPath { +private func calculateMergingCircleShape(center: CGPoint, leftCenter: CGPoint?, rightCenter: CGPoint?, radius: CGFloat, totalCount: Int, unseenCount: Int, isSeen: Bool) -> CGPath { let leftAngles = leftCenter.flatMap { calculateCircleIntersection(center: center, otherCenter: $0, radius: radius) } let rightAngles = rightCenter.flatMap { calculateCircleIntersection(center: center, otherCenter: $0, radius: radius) } @@ -70,7 +70,40 @@ private func calculateMergingCircleShape(center: CGPoint, leftCenter: CGPoint?, } else if let angles = leftAngles ?? rightAngles { path.addArc(center: center, radius: radius, startAngle: angles.point1Angle, endAngle: angles.point2Angle, clockwise: true) } else { - path.addEllipse(in: CGRect(origin: CGPoint(x: center.x - radius, y: center.y - radius), size: CGSize(width: radius * 2.0, height: radius * 2.0))) + let segmentCount = max(totalCount, 1) + if segmentCount == 1 { + if unseenCount != 0 { + if !isSeen { + path.addEllipse(in: CGRect(origin: CGPoint(x: center.x - radius, y: center.y - radius), size: CGSize(width: radius * 2.0, height: radius * 2.0))) + } + } else { + if isSeen { + path.addEllipse(in: CGRect(origin: CGPoint(x: center.x - radius, y: center.y - radius), size: CGSize(width: radius * 2.0, height: radius * 2.0))) + } + } + } else { + let segmentSpacing: CGFloat = 4.0 + let segmentSpacingAngle: CGFloat = segmentSpacing / radius + let segmentAngle = (2.0 * CGFloat.pi - segmentSpacingAngle * CGFloat(segmentCount)) / CGFloat(segmentCount) + for i in 0 ..< segmentCount { + if isSeen { + if i <= segmentCount - unseenCount - 1 { + } else { + continue + } + } else { + if i > segmentCount - unseenCount - 1 { + } else { + continue + } + } + + let startAngle = segmentSpacingAngle * 0.5 - CGFloat.pi * 0.5 + CGFloat(i) * (segmentSpacingAngle + segmentAngle) + let endAngle = startAngle + segmentAngle + path.move(to: CGPoint(x: center.x + cos(startAngle) * radius, y: center.y + sin(startAngle) * radius)) + path.addArc(center: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: false) + } + } } return path @@ -265,7 +298,8 @@ public final class StoryPeerListItemComponent: Component { public let theme: PresentationTheme public let strings: PresentationStrings public let peer: EnginePeer - public let hasUnseen: Bool + public let totalCount: Int + public let unseenCount: Int public let hasUnseenCloseFriendsItems: Bool public let hasItems: Bool public let ringAnimation: RingAnimation? @@ -282,7 +316,8 @@ public final class StoryPeerListItemComponent: Component { theme: PresentationTheme, strings: PresentationStrings, peer: EnginePeer, - hasUnseen: Bool, + totalCount: Int, + unseenCount: Int, hasUnseenCloseFriendsItems: Bool, hasItems: Bool, ringAnimation: RingAnimation?, @@ -298,7 +333,8 @@ public final class StoryPeerListItemComponent: Component { self.theme = theme self.strings = strings self.peer = peer - self.hasUnseen = hasUnseen + self.totalCount = totalCount + self.unseenCount = unseenCount self.hasUnseenCloseFriendsItems = hasUnseenCloseFriendsItems self.hasItems = hasItems self.ringAnimation = ringAnimation @@ -324,7 +360,10 @@ public final class StoryPeerListItemComponent: Component { if lhs.peer != rhs.peer { return false } - if lhs.hasUnseen != rhs.hasUnseen { + if lhs.totalCount != rhs.totalCount { + return false + } + if lhs.unseenCount != rhs.unseenCount { return false } if lhs.hasUnseenCloseFriendsItems != rhs.hasUnseenCloseFriendsItems { @@ -370,10 +409,13 @@ public final class StoryPeerListItemComponent: Component { private var avatarNode: AvatarNode? private var avatarAddBadgeView: UIImageView? private let avatarShapeLayer: SimpleShapeLayer - private let indicatorMaskLayer: SimpleLayer - private let indicatorColorLayer: SimpleGradientLayer + private let indicatorMaskSeenLayer: SimpleLayer + private let indicatorMaskUnseenLayer: SimpleLayer + private let indicatorColorSeenLayer: SimpleGradientLayer + private let indicatorColorUnseenLayer: SimpleGradientLayer private var progressLayer: StoryProgressLayer? - private let indicatorShapeLayer: SimpleShapeLayer + private let indicatorShapeSeenLayer: SimpleShapeLayer + private let indicatorShapeUnseenLayer: SimpleShapeLayer private let title = ComponentView() private var component: StoryPeerListItemComponent? @@ -403,13 +445,20 @@ public final class StoryPeerListItemComponent: Component { self.avatarShapeLayer = SimpleShapeLayer() - self.indicatorColorLayer = SimpleGradientLayer() - self.indicatorColorLayer.type = .axial - self.indicatorColorLayer.startPoint = CGPoint(x: 0.5, y: 0.0) - self.indicatorColorLayer.endPoint = CGPoint(x: 0.5, y: 1.0) + self.indicatorColorSeenLayer = SimpleGradientLayer() + self.indicatorColorSeenLayer.type = .axial + self.indicatorColorSeenLayer.startPoint = CGPoint(x: 0.5, y: 0.0) + self.indicatorColorSeenLayer.endPoint = CGPoint(x: 0.5, y: 1.0) - self.indicatorMaskLayer = SimpleLayer() - self.indicatorShapeLayer = SimpleShapeLayer() + self.indicatorColorUnseenLayer = SimpleGradientLayer() + self.indicatorColorUnseenLayer.type = .axial + self.indicatorColorUnseenLayer.startPoint = CGPoint(x: 0.5, y: 0.0) + self.indicatorColorUnseenLayer.endPoint = CGPoint(x: 0.5, y: 1.0) + + self.indicatorMaskSeenLayer = SimpleLayer() + self.indicatorMaskUnseenLayer = SimpleLayer() + self.indicatorShapeSeenLayer = SimpleShapeLayer() + self.indicatorShapeUnseenLayer = SimpleShapeLayer() super.init(frame: frame) @@ -426,16 +475,23 @@ public final class StoryPeerListItemComponent: Component { self.avatarContent.addSubview(self.avatarContainer) self.button.addSubview(self.avatarContent) - self.avatarContent.layer.addSublayer(self.indicatorColorLayer) - self.indicatorMaskLayer.addSublayer(self.indicatorShapeLayer) - self.indicatorColorLayer.mask = self.indicatorMaskLayer + self.avatarContent.layer.addSublayer(self.indicatorColorSeenLayer) + self.avatarContent.layer.addSublayer(self.indicatorColorUnseenLayer) + self.indicatorMaskSeenLayer.addSublayer(self.indicatorShapeSeenLayer) + self.indicatorMaskUnseenLayer.addSublayer(self.indicatorShapeUnseenLayer) + self.indicatorColorSeenLayer.mask = self.indicatorMaskSeenLayer + self.indicatorColorUnseenLayer.mask = self.indicatorMaskUnseenLayer self.avatarShapeLayer.fillColor = UIColor.white.cgColor self.avatarShapeLayer.fillRule = .evenOdd - self.indicatorShapeLayer.fillColor = nil - self.indicatorShapeLayer.strokeColor = UIColor.white.cgColor - self.indicatorShapeLayer.lineCap = .round + self.indicatorShapeSeenLayer.fillColor = nil + self.indicatorShapeSeenLayer.strokeColor = UIColor.white.cgColor + self.indicatorShapeSeenLayer.lineCap = .round + + self.indicatorShapeUnseenLayer.fillColor = nil + self.indicatorShapeUnseenLayer.strokeColor = UIColor.white.cgColor + self.indicatorShapeUnseenLayer.lineCap = .round self.button.highligthedChanged = { [weak self] highlighted in guard let self else { @@ -513,8 +569,6 @@ public final class StoryPeerListItemComponent: Component { self.extractedContainerNode.contentRect = CGRect(origin: CGPoint(x: self.extractedBackgroundView.frame.minX - 2.0, y: self.extractedBackgroundView.frame.minY), size: CGSize(width: self.extractedBackgroundView.frame.width + 4.0, height: self.extractedBackgroundView.frame.height)) self.containerNode.frame = CGRect(origin: CGPoint(), size: size) - let hadUnseen = self.component?.hasUnseen - let hadProgress = self.component?.ringAnimation != nil let themeUpdated = self.component?.theme !== component.theme let previousComponent = self.component @@ -558,14 +612,13 @@ public final class StoryPeerListItemComponent: Component { let indicatorFrame = avatarFrame.insetBy(dx: -6.0, dy: -6.0) - let baseLineWidth: CGFloat + let baseLineUnseenWidth: CGFloat = 2.0 + let baseLineSeenWidth: CGFloat = 1.0 + UIScreenPixel + let minimizedLineWidth: CGFloat = 3.0 - if component.hasUnseen || component.ringAnimation != nil { - baseLineWidth = 2.0 - } else { - baseLineWidth = 1.0 + UIScreenPixel - } - let indicatorLineWidth: CGFloat = baseLineWidth * component.scale + minimizedLineWidth * (1.0 - component.scale) + + let indicatorLineSeenWidth: CGFloat = baseLineSeenWidth * component.scale + minimizedLineWidth * (1.0 - component.scale) + let indicatorLineUnseenWidth: CGFloat = baseLineUnseenWidth * component.scale + minimizedLineWidth * (1.0 - component.scale) avatarNode.setPeer( context: component.context, @@ -589,7 +642,8 @@ public final class StoryPeerListItemComponent: Component { transition.setScale(view: self.avatarBackgroundContainer, scale: scaledAvatarSize / avatarSize.width) if component.peer.id == component.context.account.peerId && !component.hasItems && component.ringAnimation == nil { - self.indicatorColorLayer.isHidden = true + self.indicatorColorSeenLayer.isHidden = true + self.indicatorColorUnseenLayer.isHidden = true let avatarAddBadgeView: UIImageView var avatarAddBadgeTransition = transition @@ -625,9 +679,8 @@ public final class StoryPeerListItemComponent: Component { } avatarAddBadgeTransition.setFrame(view: avatarAddBadgeView, frame: CGRect(origin: CGPoint(x: avatarFrame.width - 1.0 - badgeSize.width, y: avatarFrame.height - 2.0 - badgeSize.height), size: badgeSize)) } else { - if self.indicatorColorLayer.isHidden { - self.indicatorColorLayer.isHidden = false - } + self.indicatorColorSeenLayer.isHidden = false + self.indicatorColorUnseenLayer.isHidden = false if let avatarAddBadgeView = self.avatarAddBadgeView { self.avatarAddBadgeView = nil @@ -635,37 +688,47 @@ public final class StoryPeerListItemComponent: Component { } } - self.avatarBackgroundView.isHidden = component.ringAnimation != nil || self.indicatorColorLayer.isHidden + self.avatarBackgroundView.isHidden = component.ringAnimation != nil || self.indicatorColorSeenLayer.isHidden let baseRadius: CGFloat = 30.0 let collapsedRadius: CGFloat = 32.0 let indicatorRadius: CGFloat = baseRadius * component.scale + collapsedRadius * (1.0 - component.scale) - self.indicatorShapeLayer.lineWidth = indicatorLineWidth + self.indicatorShapeSeenLayer.lineWidth = indicatorLineSeenWidth + self.indicatorShapeUnseenLayer.lineWidth = indicatorLineUnseenWidth - if hadUnseen != component.hasUnseen || themeUpdated || hadProgress != (component.ringAnimation != nil) { - let locations: [CGFloat] = [0.0, 1.0] - let colors: [CGColor] - - if component.hasUnseen || component.ringAnimation != nil { - if component.hasUnseenCloseFriendsItems { - colors = [component.theme.chatList.storyUnseenPrivateColors.topColor.cgColor, component.theme.chatList.storyUnseenPrivateColors.bottomColor.cgColor] - } else { - colors = [component.theme.chatList.storyUnseenColors.topColor.cgColor, component.theme.chatList.storyUnseenColors.bottomColor.cgColor] - } - } else { - colors = [component.theme.chatList.storySeenColors.topColor.cgColor, component.theme.chatList.storySeenColors.bottomColor.cgColor] - } - - self.indicatorColorLayer.locations = locations.map { $0 as NSNumber } - self.indicatorColorLayer.colors = colors + let locations: [CGFloat] = [0.0, 1.0] + let seenColors: [CGColor] + let unseenColors: [CGColor] + + if component.hasUnseenCloseFriendsItems { + unseenColors = [component.theme.chatList.storyUnseenPrivateColors.topColor.cgColor, component.theme.chatList.storyUnseenPrivateColors.bottomColor.cgColor] + } else { + unseenColors = [component.theme.chatList.storyUnseenColors.topColor.cgColor, component.theme.chatList.storyUnseenColors.bottomColor.cgColor] } - transition.setPosition(layer: self.indicatorColorLayer, position: indicatorFrame.offsetBy(dx: -avatarFrame.minX, dy: -avatarFrame.minY).center) - transition.setBounds(layer: self.indicatorColorLayer, bounds: CGRect(origin: CGPoint(), size: indicatorFrame.size)) - transition.setPosition(layer: self.indicatorShapeLayer, position: CGPoint(x: indicatorFrame.width * 0.5, y: indicatorFrame.height * 0.5)) - transition.setBounds(layer: self.indicatorShapeLayer, bounds: CGRect(origin: CGPoint(), size: indicatorFrame.size)) - transition.setScale(layer: self.indicatorColorLayer, scale: effectiveScale) + seenColors = [component.theme.chatList.storySeenColors.topColor.cgColor, component.theme.chatList.storySeenColors.bottomColor.cgColor] + + self.indicatorColorSeenLayer.locations = locations.map { $0 as NSNumber } + self.indicatorColorSeenLayer.colors = seenColors + + self.indicatorColorUnseenLayer.locations = locations.map { $0 as NSNumber } + self.indicatorColorUnseenLayer.colors = unseenColors + + transition.setPosition(layer: self.indicatorColorSeenLayer, position: indicatorFrame.offsetBy(dx: -avatarFrame.minX, dy: -avatarFrame.minY).center) + transition.setPosition(layer: self.indicatorColorUnseenLayer, position: indicatorFrame.offsetBy(dx: -avatarFrame.minX, dy: -avatarFrame.minY).center) + + transition.setBounds(layer: self.indicatorColorSeenLayer, bounds: CGRect(origin: CGPoint(), size: indicatorFrame.size)) + transition.setBounds(layer: self.indicatorColorUnseenLayer, bounds: CGRect(origin: CGPoint(), size: indicatorFrame.size)) + + transition.setPosition(layer: self.indicatorShapeSeenLayer, position: CGPoint(x: indicatorFrame.width * 0.5, y: indicatorFrame.height * 0.5)) + transition.setPosition(layer: self.indicatorShapeUnseenLayer, position: CGPoint(x: indicatorFrame.width * 0.5, y: indicatorFrame.height * 0.5)) + + transition.setBounds(layer: self.indicatorShapeSeenLayer, bounds: CGRect(origin: CGPoint(), size: indicatorFrame.size)) + transition.setBounds(layer: self.indicatorShapeUnseenLayer, bounds: CGRect(origin: CGPoint(), size: indicatorFrame.size)) + + transition.setScale(layer: self.indicatorColorSeenLayer, scale: effectiveScale) + transition.setScale(layer: self.indicatorColorUnseenLayer, scale: effectiveScale) let indicatorCenter = CGRect(origin: CGPoint(), size: indicatorFrame.size).center @@ -685,11 +748,12 @@ public final class StoryPeerListItemComponent: Component { let cutoutSize: CGFloat = 18.0 + UIScreenPixel * 2.0 avatarPath.addEllipse(in: CGRect(origin: CGPoint(x: avatarSize.width - cutoutSize + UIScreenPixel, y: avatarSize.height - 1.0 - cutoutSize + UIScreenPixel), size: CGSize(width: cutoutSize, height: cutoutSize))) } else if let mappedLeftCenter { - avatarPath.addEllipse(in: CGRect(origin: CGPoint(), size: avatarSize).insetBy(dx: -indicatorLineWidth, dy: -indicatorLineWidth).offsetBy(dx: -abs(indicatorCenter.x - mappedLeftCenter.x), dy: -abs(indicatorCenter.y - mappedLeftCenter.y))) + avatarPath.addEllipse(in: CGRect(origin: CGPoint(), size: avatarSize).insetBy(dx: -indicatorLineSeenWidth, dy: -indicatorLineSeenWidth).offsetBy(dx: -abs(indicatorCenter.x - mappedLeftCenter.x), dy: -abs(indicatorCenter.y - mappedLeftCenter.y))) } Transition.immediate.setShapeLayerPath(layer: self.avatarShapeLayer, path: avatarPath) - Transition.immediate.setShapeLayerPath(layer: self.indicatorShapeLayer, path: calculateMergingCircleShape(center: indicatorCenter, leftCenter: mappedLeftCenter, rightCenter: mappedRightCenter, radius: indicatorRadius - indicatorLineWidth * 0.5)) + Transition.immediate.setShapeLayerPath(layer: self.indicatorShapeSeenLayer, path: calculateMergingCircleShape(center: indicatorCenter, leftCenter: mappedLeftCenter, rightCenter: mappedRightCenter, radius: indicatorRadius - indicatorLineUnseenWidth * 0.5, totalCount: component.totalCount, unseenCount: component.unseenCount, isSeen: true)) + Transition.immediate.setShapeLayerPath(layer: self.indicatorShapeUnseenLayer, path: calculateMergingCircleShape(center: indicatorCenter, leftCenter: mappedLeftCenter, rightCenter: mappedRightCenter, radius: indicatorRadius - indicatorLineUnseenWidth * 0.5, totalCount: component.totalCount, unseenCount: component.unseenCount, isSeen: false)) //TODO:localize let titleString: String @@ -723,10 +787,10 @@ public final class StoryPeerListItemComponent: Component { let initialLineWidth: CGFloat = 2.0 let targetLineWidth: CGFloat = 3.0 - self.indicatorShapeLayer.lineWidth = targetLineWidth - self.indicatorShapeLayer.animateShapeLineWidth(from: initialLineWidth, to: targetLineWidth, duration: 0.2, completion: { [weak self] _ in - self?.indicatorShapeLayer.lineWidth = initialLineWidth - self?.indicatorShapeLayer.animateShapeLineWidth(from: targetLineWidth, to: initialLineWidth, duration: 0.15) + self.indicatorShapeSeenLayer.lineWidth = targetLineWidth + self.indicatorShapeSeenLayer.animateShapeLineWidth(from: initialLineWidth, to: targetLineWidth, duration: 0.2, completion: { [weak self] _ in + self?.indicatorShapeSeenLayer.lineWidth = initialLineWidth + self?.indicatorShapeSeenLayer.animateShapeLineWidth(from: targetLineWidth, to: initialLineWidth, duration: 0.15) }) } @@ -752,6 +816,8 @@ public final class StoryPeerListItemComponent: Component { titleTransition.setAlpha(view: titleView, alpha: component.expandedAlphaFraction) } + self.indicatorColorSeenLayer.isHidden = component.ringAnimation != nil + if let ringAnimation = component.ringAnimation { var progressTransition = transition let progressLayer: StoryProgressLayer @@ -761,7 +827,7 @@ public final class StoryPeerListItemComponent: Component { progressTransition = .immediate progressLayer = StoryProgressLayer() self.progressLayer = progressLayer - self.indicatorMaskLayer.addSublayer(progressLayer) + self.indicatorMaskUnseenLayer.addSublayer(progressLayer) } let progressFrame = CGRect(origin: CGPoint(), size: indicatorFrame.size).insetBy(dx: 2.0, dy: 2.0) progressTransition.setFrame(layer: progressLayer, frame: progressFrame) @@ -778,9 +844,11 @@ public final class StoryPeerListItemComponent: Component { case .loading: progressLayer.update(size: progressFrame.size, lineWidth: 4.0, value: .indefinite, transition: transition) } - self.indicatorShapeLayer.opacity = 0.0 + self.indicatorShapeSeenLayer.opacity = 0.0 + self.indicatorShapeUnseenLayer.opacity = 0.0 } else { - self.indicatorShapeLayer.opacity = 1.0 + self.indicatorShapeSeenLayer.opacity = 1.0 + self.indicatorShapeUnseenLayer.opacity = 1.0 if let progressLayer = self.progressLayer { self.progressLayer = nil diff --git a/submodules/WatchBridge/Sources/WatchBridge.swift b/submodules/WatchBridge/Sources/WatchBridge.swift index 502cb623d3..4cceb2c348 100644 --- a/submodules/WatchBridge/Sources/WatchBridge.swift +++ b/submodules/WatchBridge/Sources/WatchBridge.swift @@ -325,7 +325,13 @@ func makeBridgeMedia(message: Message, strings: PresentationStrings, chatPeer: P } func makeBridgeChat(_ entry: ChatListEntry, strings: PresentationStrings) -> (TGBridgeChat, [Int64 : TGBridgeUser])? { - if case let .MessageEntry(index, messages, readState, _, _, renderedPeer, _, _, _, _, hasFailed, _, _) = entry { + if case let .MessageEntry(entryData) = entry { + let index = entryData.index + let messages = entryData.messages + let readState = entryData.readState + let renderedPeer = entryData.renderedPeer + let hasFailed = entryData.hasFailed + guard index.messageIndex.id.peerId.namespace != Namespaces.Peer.SecretChat else { return nil }