From a9c595a640b3d6de65bf10fcf16894bbd49998d1 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 18 Mar 2022 22:55:06 +0400 Subject: [PATCH] Another feed experiment --- .bazelrc | 3 +- Telegram/SiriIntents/IntentMessages.swift | 2 +- build-system/Make/ProjectGeneration.py | 1 + build-system/tulsi | 2 +- .../Sources/AccountContext.swift | 5 +- .../AccountContext/Sources/MediaManager.swift | 3 + .../AttachmentTextInputPanelNode.swift | 2 +- .../ChatListUI/Sources/ChatContextMenus.swift | 2 +- .../Sources/ChatListController.swift | 26 +- .../Sources/ChatListSearchContainerNode.swift | 4 +- .../Sources/ChatListSearchListPaneNode.swift | 16 +- .../Sources/Node/ChatListItem.swift | 2 +- .../Sources/Node/ChatListNode.swift | 2 +- .../ChatPresentationInterfaceState.swift | 4 +- .../Sources/ContactContextMenus.swift | 6 +- .../Sources/ContactsController.swift | 2 +- .../Sources/ContactsControllerNode.swift | 2 +- .../Sources/ContactsPeerItem.swift | 2 +- .../GalleryData/Sources/GalleryData.swift | 4 +- .../Items/UniversalVideoGalleryItem.swift | 6 +- submodules/Geocoding/Sources/Geocoding.swift | 7 +- .../Sources/HashtagSearchController.swift | 2 +- .../Sources/HashtagSearchControllerNode.swift | 2 +- .../Sources/InstantPageController.swift | 2 +- .../Sources/InstantPageControllerNode.swift | 4 +- .../Sources/InviteLinkViewController.swift | 2 +- .../Sources/InviteRequestsController.swift | 2 +- .../LegacyComponents/Sources/ActionStage.mm | 4 +- .../Sources/MediaPickerScreen.swift | 4 +- submodules/MtProtoKit/Sources/MTDisposable.m | 2 +- .../MtProtoKit/Sources/MTFileBasedKeychain.m | 2 +- .../Sources/MTNetworkAvailability.m | 2 +- .../Sources/ChannelBlacklistController.swift | 2 +- ...hannelDiscussionGroupSetupController.swift | 2 +- .../ChannelPermissionsController.swift | 2 +- .../Sources/ChannelVisibilityController.swift | 8 +- .../ConvertToSupergroupController.swift | 2 +- .../Sources/DeviceContactInfoController.swift | 2 +- .../GroupStickerPackSetupController.swift | 2 +- .../Sources/PeersNearbyController.swift | 4 +- submodules/Postbox/BUILD | 1 + submodules/Postbox/Sources/ChatLocation.swift | 5 +- .../Sources/MessageHistoryHolesView.swift | 4 +- .../Postbox/Sources/MessageHistoryView.swift | 88 +- .../Sources/MessageHistoryViewState.swift | 217 +++- .../Sources/MessageOfInterestHolesView.swift | 2 +- submodules/Postbox/Sources/Postbox.swift | 25 +- submodules/Postbox/Sources/ViewTracker.swift | 4 +- .../QrCodeUI/Sources/QrCodeScanScreen.swift | 2 +- .../Source/SSignalKit/SBlockDisposable.m | 2 +- .../Source/SSignalKit/SQueueLocalObject.m | 2 +- .../Sources/SearchPeerMembers.swift | 5 + .../Sources/LogoutOptionsController.swift | 2 +- .../Search/SettingsSearchableItems.swift | 4 +- .../InstalledStickerPacksController.swift | 2 +- .../Sources/ThemePickerController.swift | 14 +- .../Themes/ThemeSettingsController.swift | 10 +- .../Sources/ChannelStatsController.swift | 4 +- .../Sources/GroupStatsController.swift | 2 +- .../Sources/MessageStatsController.swift | 2 +- .../StickerPackPreviewController.swift | 2 +- submodules/TelegramApi/Sources/Api0.swift | 10 +- submodules/TelegramApi/Sources/Api2.swift | 108 +- submodules/TelegramApi/Sources/Api4.swift | 135 ++ .../Sources/TelegramBaseController.swift | 2 +- .../Sources/VoiceChatController.swift | 4 +- submodules/TelegramCore/BUILD | 1 + .../Sources/State/AccountViewTracker.swift | 50 +- .../State/HistoryViewStateValidation.swift | 2 +- .../Sources/State/Serialization.swift | 2 +- ...EarliestUnseenPersonalMentionMessage.swift | 4 +- .../TelegramEngine/Messages/FeedHistory.swift | 538 ++++++++ .../Messages/PeerLiveLocationsContext.swift | 2 +- .../Messages/ReplyThreadHistory.swift | 20 +- .../Messages/SparseMessageList.swift | 2 +- .../Peers/UpdateCachedPeerData.swift | 4 +- .../TelegramUI/Sources/AccountContext.swift | 43 +- .../Sources/ApplicationContext.swift | 12 +- .../Sources/AttachmentFileController.swift | 2 +- .../Sources/AttachmentFileSearchItem.swift | 2 +- .../TelegramUI/Sources/ChatController.swift | 1105 +++++++++-------- .../Sources/ChatControllerNode.swift | 2 +- .../TelegramUI/Sources/ChatEmptyNode.swift | 2 +- .../Sources/ChatHistoryListNode.swift | 22 +- .../ChatHistorySearchContainerNode.swift | 2 +- .../Sources/ChatHistoryViewForLocation.swift | 20 +- .../Sources/ChatInfoTitlePanelNode.swift | 14 +- .../ChatInterfaceStateContextMenus.swift | 64 +- .../ChatMessageAnimatedStickerItemNode.swift | 4 + .../Sources/ChatMessageBubbleItemNode.swift | 4 +- ...entLogPreviousDescriptionContentNode.swift | 2 +- ...ssageEventLogPreviousLinkContentNode.swift | 2 +- ...geEventLogPreviousMessageContentNode.swift | 2 +- .../ChatMessageGameBubbleContentNode.swift | 2 +- .../ChatMessageInstantVideoItemNode.swift | 10 +- .../TelegramUI/Sources/ChatMessageItem.swift | 2 +- .../Sources/ChatMessageStickerItemNode.swift | 66 +- .../ChatRecentActionsControllerNode.swift | 6 +- .../ChatRecentActionsHistoryTransition.swift | 86 +- .../ChatSearchNavigationContentNode.swift | 8 +- .../ChatSearchResultsContollerNode.swift | 2 +- .../Sources/ChatTextInputPanelNode.swift | 2 +- .../Sources/ComposeController.swift | 6 +- .../Sources/CreateGroupController.swift | 2 +- .../Sources/NavigateToChatController.swift | 18 +- .../TelegramUI/Sources/OpenChatMessage.swift | 4 +- .../TelegramUI/Sources/OpenResolvedUrl.swift | 8 +- submodules/TelegramUI/Sources/OpenUrl.swift | 4 +- .../OverlayAudioPlayerControllerNode.swift | 4 +- .../PeerInfo/Panes/PeerInfoListPaneNode.swift | 4 +- .../Panes/PeerInfoVisualMediaPaneNode.swift | 2 +- .../Sources/PeerInfo/PeerInfoData.swift | 2 +- .../Sources/PeerInfo/PeerInfoScreen.swift | 52 +- .../Sources/PeerInfoGifPaneNode.swift | 2 +- .../Sources/PeerMessagesMediaPlaylist.swift | 14 +- .../Sources/PeerSelectionControllerNode.swift | 2 +- .../Sources/SharedAccountContext.swift | 4 +- .../TelegramUI/Sources/TextLinkHandling.swift | 4 +- .../UIWindow+OrientationChange.m | 2 +- submodules/Utils/BUILD | 0 submodules/Utils/RangeSet/BUILD | 15 + .../Sources/CollectionExtensions.swift | 269 ++++ .../RangeSet/Sources/DiscontiguousSlice.swift | 140 +++ submodules/Utils/RangeSet/Sources/Pair.swift | 33 + .../Utils/RangeSet/Sources/Partition.swift | 217 ++++ .../Utils/RangeSet/Sources/RangeSet.swift | 564 +++++++++ .../RangeSet/Sources/RangeSetStorage.swift | 181 +++ .../Sources/WatchRequestHandlers.swift | 2 +- 128 files changed, 3524 insertions(+), 957 deletions(-) create mode 100644 submodules/TelegramCore/Sources/TelegramEngine/Messages/FeedHistory.swift create mode 100644 submodules/Utils/BUILD create mode 100644 submodules/Utils/RangeSet/BUILD create mode 100644 submodules/Utils/RangeSet/Sources/CollectionExtensions.swift create mode 100644 submodules/Utils/RangeSet/Sources/DiscontiguousSlice.swift create mode 100644 submodules/Utils/RangeSet/Sources/Pair.swift create mode 100644 submodules/Utils/RangeSet/Sources/Partition.swift create mode 100644 submodules/Utils/RangeSet/Sources/RangeSet.swift create mode 100644 submodules/Utils/RangeSet/Sources/RangeSetStorage.swift diff --git a/.bazelrc b/.bazelrc index 9bda53602d..6f16327e1d 100644 --- a/.bazelrc +++ b/.bazelrc @@ -8,7 +8,8 @@ build --per_file_copt="third-party/webrtc/.*\.cc$","@-std=c++14" build --per_file_copt="third-party/webrtc/.*\.mm$","@-std=c++14" build --per_file_copt="submodules/LottieMeshSwift/LottieMeshBinding/Sources/.*\.mm$","@-std=c++14" -build --swiftcopt=-disallow-use-new-driver +#build --swiftcopt=-disallow-use-new-driver +build --swiftcopt=-whole-module-optimization #build --swiftcopt=-sanitize=address #build --copt=-fsanitize=address diff --git a/Telegram/SiriIntents/IntentMessages.swift b/Telegram/SiriIntents/IntentMessages.swift index f8b551899d..004248e2b1 100644 --- a/Telegram/SiriIntents/IntentMessages.swift +++ b/Telegram/SiriIntents/IntentMessages.swift @@ -49,7 +49,7 @@ func unreadMessages(account: Account) -> Signal<[INMessage], NoError> { } if !isMuted && hasUnread { - signals.append(account.postbox.aroundMessageHistoryViewForLocation(.peer(index.messageIndex.id.peerId), anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: 10, fixedCombinedReadStates: fixedCombinedReadStates, topTaggedMessageIdNamespaces: Set(), tagMask: nil, appendMessagesFromTheSameGroup: false, namespaces: .not(Namespaces.Message.allScheduled), orderStatistics: .combinedLocation) + signals.append(account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId: index.messageIndex.id.peerId), anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: 10, fixedCombinedReadStates: fixedCombinedReadStates, topTaggedMessageIdNamespaces: Set(), tagMask: nil, appendMessagesFromTheSameGroup: false, namespaces: .not(Namespaces.Message.allScheduled), orderStatistics: .combinedLocation) |> take(1) |> map { view -> [INMessage] in var messages: [INMessage] = [] diff --git a/build-system/Make/ProjectGeneration.py b/build-system/Make/ProjectGeneration.py index 2cb9e2e2f9..1154d83a08 100644 --- a/build-system/Make/ProjectGeneration.py +++ b/build-system/Make/ProjectGeneration.py @@ -41,6 +41,7 @@ def generate(build_environment: BuildEnvironment, disable_extensions, disable_pr tulsi_build_command += ['--xcode_version={}'.format(build_environment.xcode_version)] tulsi_build_command += ['--use_top_level_targets_for_symlinks'] tulsi_build_command += ['--verbose_failures'] + tulsi_build_command += ['--swiftcopt=-whole-module-optimization'] call_executable(tulsi_build_command) diff --git a/build-system/tulsi b/build-system/tulsi index ec7dd9ddf4..2b8fbf5a95 160000 --- a/build-system/tulsi +++ b/build-system/tulsi @@ -1 +1 @@ -Subproject commit ec7dd9ddf4b73dedb02df827b7ab3b2cbb1f2ac0 +Subproject commit 2b8fbf5a95d43dd43ba20b5a690c2bf8e1146063 diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 5dbfbe00df..3ac3ef9fca 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -267,8 +267,9 @@ public enum ChatSearchDomain: Equatable { } public enum ChatLocation: Equatable { - case peer(PeerId) - case replyThread(ChatReplyThreadMessage) + case peer(id: PeerId) + case replyThread(message: ChatReplyThreadMessage) + case feed(id: Int32) } public final class NavigateToChatControllerParams { diff --git a/submodules/AccountContext/Sources/MediaManager.swift b/submodules/AccountContext/Sources/MediaManager.swift index 1b4e87c34a..fb6b1d8635 100644 --- a/submodules/AccountContext/Sources/MediaManager.swift +++ b/submodules/AccountContext/Sources/MediaManager.swift @@ -10,6 +10,7 @@ import UniversalMediaPlayer public enum PeerMessagesMediaPlaylistId: Equatable, SharedMediaPlaylistId { case peer(PeerId) case recentActions(PeerId) + case feed(Int32) case custom public func isEqual(to: SharedMediaPlaylistId) -> Bool { @@ -34,6 +35,8 @@ public enum PeerMessagesPlaylistLocation: Equatable, SharedMediaPlaylistLocation return .peer(peerId) case let .replyThread(replyThreaMessage): return .peer(replyThreaMessage.messageId.peerId) + case let .feed(id): + return .feed(id) } case let .singleMessage(id): return .peer(id.peerId) diff --git a/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift b/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift index 6f85142c6b..38b0667d89 100644 --- a/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift +++ b/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift @@ -248,7 +248,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS self.isAttachment = isAttachment var hasSpoilers = true - if presentationInterfaceState.chatLocation.peerId.namespace == Namespaces.Peer.SecretChat { + if presentationInterfaceState.chatLocation.peerId?.namespace == Namespaces.Peer.SecretChat { hasSpoilers = false } self.inputMenu = TextInputMenu(hasSpoilers: hasSpoilers) diff --git a/submodules/ChatListUI/Sources/ChatContextMenus.swift b/submodules/ChatListUI/Sources/ChatContextMenus.swift index a2746c9fcc..c6998bfda4 100644 --- a/submodules/ChatListUI/Sources/ChatContextMenus.swift +++ b/submodules/ChatListUI/Sources/ChatContextMenus.swift @@ -374,7 +374,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch } }, completed: { if let navigationController = (chatListController?.navigationController as? NavigationController) { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId))) } })) f(.default) diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index d8499ba996..c12adf4ead 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -895,7 +895,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController scrollToEndIfExists = true } - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), activateInput: activateInput && !peer.isDeleted, scrollToEndIfExists: scrollToEndIfExists, animated: !scrollToEndIfExists, options: strongSelf.groupId == PeerGroupId.root ? [.removeOnMasterDetails] : [], parentGroupId: strongSelf.groupId, chatListFilter: strongSelf.chatListDisplayNode.containerNode.currentItemNode.chatListFilter?.id, completion: { [weak self] controller in + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peer.id), activateInput: activateInput && !peer.isDeleted, scrollToEndIfExists: scrollToEndIfExists, animated: !scrollToEndIfExists, options: strongSelf.groupId == PeerGroupId.root ? [.removeOnMasterDetails] : [], parentGroupId: strongSelf.groupId, chatListFilter: strongSelf.chatListDisplayNode.containerNode.currentItemNode.chatListFilter?.id, completion: { [weak self] controller in self?.chatListDisplayNode.containerNode.currentItemNode.clearHighlightAnimated(true) if let promoInfo = promoInfo { switch promoInfo { @@ -974,7 +974,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController if let layout = strongSelf.validLayout, case .regular = layout.metrics.widthClass { scrollToEndIfExists = true } - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(actualPeerId), subject: .message(id: .id(messageId), highlight: true, timecode: nil), purposefulAction: { + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: actualPeerId), subject: .message(id: .id(messageId), highlight: true, timecode: nil), purposefulAction: { if deactivateOnAction { self?.deactivateSearch(animated: false) } @@ -1005,7 +1005,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController scrollToEndIfExists = true } if let navigationController = strongSelf.navigationController as? NavigationController { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), purposefulAction: { [weak self] in + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peer.id), purposefulAction: { [weak self] in self?.deactivateSearch(animated: false) }, scrollToEndIfExists: scrollToEndIfExists, options: strongSelf.groupId == PeerGroupId.root ? [.removeOnMasterDetails] : [])) strongSelf.chatListDisplayNode.containerNode.currentItemNode.clearHighlightAnimated(true) @@ -1125,7 +1125,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController 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) strongSelf.presentInGlobalOverlay(contextController) case let .peer(_, peer, _, _, _, _, _, _, _, promoInfo, _, _, _): - let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peer.peerId), subject: nil, botStart: nil, mode: .standard(previewing: true)) + let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peer.peerId), subject: nil, botStart: nil, mode: .standard(previewing: true)) chatController.canReadHistory.set(false) let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.peerId, promoInfo: promoInfo, source: .chatList(filter: strongSelf.chatListDisplayNode.containerNode.currentItemNode.chatListFilter), chatListController: strongSelf, joined: joined) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) strongSelf.presentInGlobalOverlay(contextController) @@ -1146,7 +1146,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController if case let .search(messageId) = source, let id = messageId { subject = .message(id: .id(id), highlight: false, timecode: nil) } - let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peer.id), subject: subject, botStart: nil, mode: .standard(previewing: true)) + let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peer.id), subject: subject, botStart: nil, mode: .standard(previewing: true)) chatController.canReadHistory.set(false) contextContentSource = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)) } @@ -1378,6 +1378,22 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController }))) } + if let id = id { + //TODO:localize + items.append(.action(ContextMenuActionItem(text: "View as Feed", icon: { _ in + return nil + }, action: { c, f in + c.dismiss(completion: { + guard let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController else { + return + } + + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .feed(id: id), subject: nil, purposefulAction: { + }, scrollToEndIfExists: false, options: [])) + }) + }))) + } + let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatListHeaderBarContextExtractedContentSource(controller: strongSelf, sourceNode: sourceNode, keepInPlace: keepInPlace)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) strongSelf.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller) }) diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index 7080ed6394..af9abda560 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -1009,7 +1009,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo private func mediaMessageContextAction(_ message: EngineMessage, node: ASDisplayNode?, rect: CGRect?, gesture anyRecognizer: UIGestureRecognizer?) { let gesture: ContextGesture? = anyRecognizer as? ContextGesture - let _ = (chatMediaListPreviewControllerData(context: self.context, chatLocation: .peer(message.id.peerId), chatLocationContextHolder: Atomic(value: nil), message: message._asMessage(), standalone: true, reverseMessageGalleryOrder: false, navigationController: self.navigationController) + let _ = (chatMediaListPreviewControllerData(context: self.context, chatLocation: .peer(id: message.id.peerId), chatLocationContextHolder: Atomic(value: nil), message: message._asMessage(), standalone: true, reverseMessageGalleryOrder: false, navigationController: self.navigationController) |> deliverOnMainQueue).start(next: { [weak self] previewData in guard let strongSelf = self else { gesture?.cancel() @@ -1351,7 +1351,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo }) |> deliverOnMainQueue).start(completed: { if let strongSelf = self { - let controller = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peerId), subject: nil, botStart: nil, mode: .standard(previewing: false)) + let controller = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, mode: .standard(previewing: false)) controller.purposefulAction = { [weak self] in self?.cancel?() } diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index 07c1b1339b..153e602200 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -576,7 +576,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable { isMedia = true } if isMedia { - return ListMessageItem(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: .builtin(WallpaperSettings())), fontSize: presentationData.fontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: true, largeEmoji: false, chatBubbleCorners: PresentationChatBubbleCorners(mainRadius: 0.0, auxiliaryRadius: 0.0, mergeBubbleCorners: false)), context: context, chatLocation: .peer(peer.peerId), interaction: listInteraction, message: message._asMessage(), selection: selection, displayHeader: enableHeaders && !displayCustomHeader, customHeader: key == .downloads ? header : nil, hintIsLink: tagMask == .webPage, isGlobalSearchResult: key != .downloads, isDownloadList: key == .downloads) + return ListMessageItem(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: .builtin(WallpaperSettings())), fontSize: presentationData.fontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: true, largeEmoji: false, chatBubbleCorners: PresentationChatBubbleCorners(mainRadius: 0.0, auxiliaryRadius: 0.0, mergeBubbleCorners: false)), context: context, chatLocation: .peer(id: peer.peerId), interaction: listInteraction, message: message._asMessage(), selection: selection, displayHeader: enableHeaders && !displayCustomHeader, customHeader: key == .downloads ? header : nil, hintIsLink: tagMask == .webPage, isGlobalSearchResult: key != .downloads, isDownloadList: key == .downloads) } else { return ChatListItem(presentationData: presentationData, context: context, peerGroupId: .root, filterData: nil, index: EngineChatList.Item.Index(pinningIndex: nil, messageIndex: message.index), content: .peer(messages: [message], peer: peer, combinedReadState: readState, isRemovedFromTotalUnreadCount: false, presence: nil, hasUnseenMentions: false, hasUnseenReactions: false, draftState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: true, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: tagMask == nil ? header : nil, enableContextActions: false, hiddenOffset: false, interaction: interaction) } @@ -1500,7 +1500,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { let gallerySource: GalleryControllerItemSource if strongSelf.key == .downloads { - gallerySource = .peerMessagesAtId(messageId: message.id, chatLocation: .peer(message.id.peerId), chatLocationContextHolder: Atomic(value: nil)) + gallerySource = .peerMessagesAtId(messageId: message.id, chatLocation: .peer(id: message.id.peerId), chatLocationContextHolder: Atomic(value: nil)) } else { gallerySource = .custom(messages: foundMessages |> map { message, a, b in return (message.map { $0._asMessage() }, a, b) @@ -1520,7 +1520,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { }) } - return context.sharedContext.openChatMessage(OpenChatMessageParams(context: context, chatLocation: .peer(message.id.peerId), chatLocationContextHolder: nil, message: message, standalone: false, reverseMessageGalleryOrder: true, mode: mode, navigationController: navigationController, dismissInput: { + return context.sharedContext.openChatMessage(OpenChatMessageParams(context: context, chatLocation: .peer(id: message.id.peerId), chatLocationContextHolder: nil, message: message, standalone: false, reverseMessageGalleryOrder: true, mode: mode, navigationController: navigationController, dismissInput: { interaction.dismissInput() }, present: { c, a in interaction.present(c, a) @@ -2183,7 +2183,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { strongSelf.interaction.dismissInput() strongSelf.interaction.present(controller, nil) } else { - let signal = strongSelf.context.sharedContext.messageFromPreloadedChatHistoryViewForLocation(id: id.messageId, location: ChatHistoryLocationInput(content: .InitialSearch(location: .id(id.messageId), count: 60, highlight: true), id: 0), context: strongSelf.context, chatLocation: .peer(id.messageId.peerId), subject: nil, chatLocationContextHolder: Atomic(value: nil), tagMask: EngineMessage.Tags.music) + let signal = strongSelf.context.sharedContext.messageFromPreloadedChatHistoryViewForLocation(id: id.messageId, location: ChatHistoryLocationInput(content: .InitialSearch(location: .id(id.messageId), count: 60, highlight: true), id: 0), context: strongSelf.context, chatLocation: .peer(id: id.messageId.peerId), subject: nil, chatLocationContextHolder: Atomic(value: nil), tagMask: EngineMessage.Tags.music) var cancelImpl: (() -> Void)? let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } @@ -2764,7 +2764,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode { associatedMessageIds: [] ) - return ListMessageItem(presentationData: ChatPresentationData(presentationData: presentationData), context: context, chatLocation: .peer(peer1.id), interaction: ListMessageItemInteraction.default, message: message._asMessage(), selection: hasSelection ? .selectable(selected: false) : .none, displayHeader: false, customHeader: nil, hintIsLink: true, isGlobalSearchResult: true) + return ListMessageItem(presentationData: ChatPresentationData(presentationData: presentationData), context: context, chatLocation: .peer(id: peer1.id), interaction: ListMessageItemInteraction.default, message: message._asMessage(), selection: hasSelection ? .selectable(selected: false) : .none, displayHeader: false, customHeader: nil, hintIsLink: true, isGlobalSearchResult: true) case .files: var media: [EngineMedia] = [] media.append(.file(TelegramMediaFile(fileId: EngineMedia.Id(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: 0, attributes: [.FileName(fileName: "Text.txt")]))) @@ -2791,7 +2791,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode { associatedMessageIds: [] ) - return ListMessageItem(presentationData: ChatPresentationData(presentationData: presentationData), context: context, chatLocation: .peer(peer1.id), interaction: ListMessageItemInteraction.default, message: message._asMessage(), selection: hasSelection ? .selectable(selected: false) : .none, displayHeader: false, customHeader: nil, hintIsLink: false, isGlobalSearchResult: true) + return ListMessageItem(presentationData: ChatPresentationData(presentationData: presentationData), context: context, chatLocation: .peer(id: peer1.id), interaction: ListMessageItemInteraction.default, message: message._asMessage(), selection: hasSelection ? .selectable(selected: false) : .none, displayHeader: false, customHeader: nil, hintIsLink: false, isGlobalSearchResult: true) case .music: var media: [EngineMedia] = [] media.append(.file(TelegramMediaFile(fileId: EngineMedia.Id(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: [.Audio(isVoice: false, duration: 0, title: nil, performer: nil, waveform: Data())]))) @@ -2818,7 +2818,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode { associatedMessageIds: [] ) - return ListMessageItem(presentationData: ChatPresentationData(presentationData: presentationData), context: context, chatLocation: .peer(peer1.id), interaction: ListMessageItemInteraction.default, message: message._asMessage(), selection: hasSelection ? .selectable(selected: false) : .none, displayHeader: false, customHeader: nil, hintIsLink: false, isGlobalSearchResult: true) + return ListMessageItem(presentationData: ChatPresentationData(presentationData: presentationData), context: context, chatLocation: .peer(id: peer1.id), interaction: ListMessageItemInteraction.default, message: message._asMessage(), selection: hasSelection ? .selectable(selected: false) : .none, displayHeader: false, customHeader: nil, hintIsLink: false, isGlobalSearchResult: true) case .voice: var media: [EngineMedia] = [] media.append(.file(TelegramMediaFile(fileId: EngineMedia.Id(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: [.Audio(isVoice: true, duration: 0, title: nil, performer: nil, waveform: Data())]))) @@ -2845,7 +2845,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode { associatedMessageIds: [] ) - return ListMessageItem(presentationData: ChatPresentationData(presentationData: presentationData), context: context, chatLocation: .peer(peer1.id), interaction: ListMessageItemInteraction.default, message: message._asMessage(), selection: hasSelection ? .selectable(selected: false) : .none, displayHeader: false, customHeader: nil, hintIsLink: false, isGlobalSearchResult: true) + return ListMessageItem(presentationData: ChatPresentationData(presentationData: presentationData), context: context, chatLocation: .peer(id: peer1.id), interaction: ListMessageItemInteraction.default, message: message._asMessage(), selection: hasSelection ? .selectable(selected: false) : .none, displayHeader: false, customHeader: nil, hintIsLink: false, isGlobalSearchResult: true) } } diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 0ede787abb..43bcfe1c5e 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -42,7 +42,7 @@ public enum ChatListItemContent { public var chatLocation: ChatLocation? { switch self { case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _, _): - return .peer(peer.peerId) + return .peer(id: peer.peerId) case .groupReference: return nil } diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 9f77b07889..e4b6561d38 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -1938,7 +1938,7 @@ public final class ChatListNode: ListView { } switch chatListView.filteredEntries[entryCount - i - 1] { case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _, _, _, _): - if interaction.highlightedChatLocation?.location == ChatLocation.peer(peer.peerId) { + if interaction.highlightedChatLocation?.location == ChatLocation.peer(id: peer.peerId) { current = (index, peer.peer!, entryCount - i - 1) break outer } diff --git a/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift b/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift index b6b7d5e33f..47bace53ff 100644 --- a/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift +++ b/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift @@ -8,12 +8,14 @@ import AccountContext import ChatInterfaceState public extension ChatLocation { - var peerId: PeerId { + var peerId: PeerId? { switch self { case let .peer(peerId): return peerId case let .replyThread(replyThreadMessage): return replyThreadMessage.messageId.peerId + case .feed: + return nil } } } diff --git a/submodules/ContactListUI/Sources/ContactContextMenus.swift b/submodules/ContactListUI/Sources/ContactContextMenus.swift index 6db9ce42b8..b45cc6842b 100644 --- a/submodules/ContactListUI/Sources/ContactContextMenus.swift +++ b/submodules/ContactListUI/Sources/ContactContextMenus.swift @@ -19,7 +19,7 @@ func contactContextMenuItems(context: AccountContext, peerId: EnginePeer.Id, con items.append(.action(ContextMenuActionItem(text: strings.ContactList_Context_SendMessage, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Message"), color: theme.contextMenu.primaryColor) }, action: { _, f in if let contactsController = contactsController, let navigationController = contactsController.navigationController as? NavigationController { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), peekData: nil)) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId), peekData: nil)) } f(.default) }))) @@ -56,7 +56,7 @@ func contactContextMenuItems(context: AccountContext, peerId: EnginePeer.Id, con |> deliverOnMainQueue).start(next: { currentPeerId in if let currentPeerId = currentPeerId { if let contactsController = contactsController, let navigationController = (contactsController.navigationController as? NavigationController) { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(currentPeerId), peekData: nil)) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: currentPeerId), peekData: nil)) } } else { var createSignal = context.engine.peers.createSecretChat(peerId: peerId) @@ -91,7 +91,7 @@ func contactContextMenuItems(context: AccountContext, peerId: EnginePeer.Id, con createSecretChatDisposable.set((createSignal |> deliverOnMainQueue).start(next: { peerId in if let navigationController = (contactsController?.navigationController as? NavigationController) { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), peekData: nil)) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId), peekData: nil)) } }, error: { error in if let contactsController = contactsController { diff --git a/submodules/ContactListUI/Sources/ContactsController.swift b/submodules/ContactListUI/Sources/ContactsController.swift index 5b57df23f2..d7e77c52d0 100644 --- a/submodules/ContactListUI/Sources/ContactsController.swift +++ b/submodules/ContactListUI/Sources/ContactsController.swift @@ -323,7 +323,7 @@ public class ContactsController: ViewController { scrollToEndIfExists = true } - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), purposefulAction: { [weak self] in + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peer.id), purposefulAction: { [weak self] in if fromSearch { self?.deactivateSearch(animated: false) self?.switchToChatsController?() diff --git a/submodules/ContactListUI/Sources/ContactsControllerNode.swift b/submodules/ContactListUI/Sources/ContactsControllerNode.swift index 5556e6b62f..9803afa2eb 100644 --- a/submodules/ContactListUI/Sources/ContactsControllerNode.swift +++ b/submodules/ContactListUI/Sources/ContactsControllerNode.swift @@ -174,7 +174,7 @@ final class ContactsControllerNode: ASDisplayNode { guard let contactsController = self.controller else { return } - let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .peer(peer.id), subject: nil, botStart: nil, mode: .standard(previewing: true)) + let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .standard(previewing: true)) chatController.canReadHistory.set(false) let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: contactContextMenuItems(context: self.context, peerId: peer.id, contactsController: contactsController) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) contactsController.presentInGlobalOverlay(contextController) diff --git a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift index dcb8c8e25f..9ee76ef24a 100644 --- a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift +++ b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift @@ -478,7 +478,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { switch item.peer { case let .peer(_, chatPeer): if let peer = chatPeer { - if ChatLocation.peer(peer.id) == item.itemHighlighting?.chatLocation { + if ChatLocation.peer(id: peer.id) == item.itemHighlighting?.chatLocation { reallyHighlighted = true } } diff --git a/submodules/GalleryData/Sources/GalleryData.swift b/submodules/GalleryData/Sources/GalleryData.swift index 5c08318248..938fea0246 100644 --- a/submodules/GalleryData/Sources/GalleryData.swift +++ b/submodules/GalleryData/Sources/GalleryData.swift @@ -211,7 +211,7 @@ public func chatMessageGalleryControllerData(context: AccountContext, chatLocati } if internalDocumentItemSupportsMimeType(file.mimeType, fileName: file.fileName ?? "file") { - let gallery = GalleryController(context: context, source: source ?? .peerMessagesAtId(messageId: message.id, chatLocation: chatLocation ?? .peer(message.id.peerId), chatLocationContextHolder: chatLocationContextHolder ?? Atomic(value: nil)), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: autoplayingVideo, landscape: landscape, timecode: timecode, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in + let gallery = GalleryController(context: context, source: source ?? .peerMessagesAtId(messageId: message.id, chatLocation: chatLocation ?? .peer(id: message.id.peerId), chatLocationContextHolder: chatLocationContextHolder ?? Atomic(value: nil)), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: autoplayingVideo, landscape: landscape, timecode: timecode, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in navigationController?.replaceTopController(controller, animated: false, ready: ready) }, baseNavigationController: navigationController, actionInteraction: actionInteraction) return .gallery(.single(gallery)) @@ -239,7 +239,7 @@ public func chatMessageGalleryControllerData(context: AccountContext, chatLocati return .gallery(startState |> deliverOnMainQueue |> map { startState in - let gallery = GalleryController(context: context, source: source ?? (standalone ? .standaloneMessage(message) : .peerMessagesAtId(messageId: message.id, chatLocation: chatLocation ?? .peer(message.id.peerId), chatLocationContextHolder: chatLocationContextHolder ?? Atomic(value: nil))), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: autoplayingVideo, landscape: landscape, timecode: startState.timecode, playbackRate: startState.rate, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in + let gallery = GalleryController(context: context, source: source ?? (standalone ? .standaloneMessage(message) : .peerMessagesAtId(messageId: message.id, chatLocation: chatLocation ?? .peer(id: message.id.peerId), chatLocationContextHolder: chatLocationContextHolder ?? Atomic(value: nil))), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: autoplayingVideo, landscape: landscape, timecode: startState.timecode, playbackRate: startState.rate, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in navigationController?.replaceTopController(controller, animated: false, ready: ready) }, baseNavigationController: navigationController, actionInteraction: actionInteraction) gallery.temporaryDoNotWaitForReady = autoplayingVideo diff --git a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift index 718787c1ca..6e5718b78a 100644 --- a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift @@ -2083,7 +2083,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { switch contentInfo { case let .message(message): - let gallery = GalleryController(context: context, source: .peerMessagesAtId(messageId: message.id, chatLocation: .peer(message.id.peerId), chatLocationContextHolder: Atomic(value: nil)), playbackRate: playbackRate, replaceRootController: { controller, ready in + let gallery = GalleryController(context: context, source: .peerMessagesAtId(messageId: message.id, chatLocation: .peer(id: message.id.peerId), chatLocationContextHolder: Atomic(value: nil)), playbackRate: playbackRate, replaceRootController: { controller, ready in if let baseNavigationController = baseNavigationController { baseNavigationController.replaceTopController(controller, animated: false, ready: ready) } @@ -2207,7 +2207,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { switch contentInfo { case let .message(message): - let gallery = GalleryController(context: context, source: .peerMessagesAtId(messageId: message.id, chatLocation: .peer(message.id.peerId), chatLocationContextHolder: Atomic(value: nil)), playbackRate: playbackRate, replaceRootController: { [weak baseNavigationController] controller, ready in + let gallery = GalleryController(context: context, source: .peerMessagesAtId(messageId: message.id, chatLocation: .peer(id: message.id.peerId), chatLocationContextHolder: Atomic(value: nil)), playbackRate: playbackRate, replaceRootController: { [weak baseNavigationController] controller, ready in if let baseNavigationController = baseNavigationController { baseNavigationController.replaceTopController(controller, animated: false, ready: ready) } @@ -2272,7 +2272,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { switch contentInfo { case let .message(message): - let gallery = GalleryController(context: context, source: .peerMessagesAtId(messageId: message.id, chatLocation: .peer(message.id.peerId), chatLocationContextHolder: Atomic(value: nil)), playbackRate: playbackRate, replaceRootController: { controller, ready in + let gallery = GalleryController(context: context, source: .peerMessagesAtId(messageId: message.id, chatLocation: .peer(id: message.id.peerId), chatLocationContextHolder: Atomic(value: nil)), playbackRate: playbackRate, replaceRootController: { controller, ready in if let baseNavigationController = baseNavigationController { baseNavigationController.replaceTopController(controller, animated: false, ready: ready) } diff --git a/submodules/Geocoding/Sources/Geocoding.swift b/submodules/Geocoding/Sources/Geocoding.swift index a391e26b3c..e573efce2f 100644 --- a/submodules/Geocoding/Sources/Geocoding.swift +++ b/submodules/Geocoding/Sources/Geocoding.swift @@ -71,7 +71,12 @@ public func reverseGeocodeLocation(latitude: Double, longitude: Double) -> Signa let geocoder = CLGeocoder() geocoder.reverseGeocodeLocation(CLLocation(latitude: latitude, longitude: longitude), completionHandler: { placemarks, _ in if let placemarks = placemarks, let placemark = placemarks.first { - let result = ReverseGeocodedPlacemark(street: placemark.thoroughfare, city: placemark.locality, country: placemark.country) + let result: ReverseGeocodedPlacemark + if placemark.thoroughfare == nil && placemark.locality == nil && placemark.country == nil { + result = ReverseGeocodedPlacemark(street: placemark.name, city: nil, country: nil) + } else { + result = ReverseGeocodedPlacemark(street: placemark.thoroughfare, city: placemark.locality, country: placemark.country) + } subscriber.putNext(result) subscriber.putCompletion() } else { diff --git a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift index ce53a3b5bc..7e5bd06193 100644 --- a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift +++ b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift @@ -57,7 +57,7 @@ public final class HashtagSearchController: TelegramBaseController { if let strongSelf = self { strongSelf.openMessageFromSearchDisposable.set((storedMessageFromSearchPeer(account: strongSelf.context.account, peer: peer._asPeer()) |> deliverOnMainQueue).start(next: { actualPeerId in if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(actualPeerId), subject: message.id.peerId == actualPeerId ? .message(id: .id(message.id), highlight: true, timecode: nil) : nil, keepStack: .always)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: actualPeerId), subject: message.id.peerId == actualPeerId ? .message(id: .id(message.id), highlight: true, timecode: nil) : nil, keepStack: .always)) } })) strongSelf.controllerNode.listNode.clearHighlightAnimated(true) diff --git a/submodules/HashtagSearchUI/Sources/HashtagSearchControllerNode.swift b/submodules/HashtagSearchUI/Sources/HashtagSearchControllerNode.swift index bed199dda9..9fdd713186 100644 --- a/submodules/HashtagSearchUI/Sources/HashtagSearchControllerNode.swift +++ b/submodules/HashtagSearchUI/Sources/HashtagSearchControllerNode.swift @@ -47,7 +47,7 @@ final class HashtagSearchControllerNode: ASDisplayNode { self.segmentedControlNode = SegmentedControlNode(theme: SegmentedControlTheme(theme: presentationData.theme), items: items.map { SegmentedControlItem(title: $0) }, selectedIndex: 0) if let peer = peer { - self.chatController = context.sharedContext.makeChatController(context: context, chatLocation: .peer(peer.id), subject: nil, botStart: nil, mode: .inline(navigationController)) + self.chatController = context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .inline(navigationController)) } else { self.chatController = nil } diff --git a/submodules/InstantPageUI/Sources/InstantPageController.swift b/submodules/InstantPageUI/Sources/InstantPageController.swift index c5a2bb408f..7529938848 100644 --- a/submodules/InstantPageUI/Sources/InstantPageController.swift +++ b/submodules/InstantPageUI/Sources/InstantPageController.swift @@ -153,7 +153,7 @@ public final class InstantPageController: ViewController { (self?.navigationController as? NavigationController)?.pushViewController(c) }, openPeer: { [weak self] peerId in if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), animated: true)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId), animated: true)) } }, navigateBack: { [weak self] in if let strongSelf = self, let controllers = strongSelf.navigationController?.viewControllers.reversed() { diff --git a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift index 8d94ea3372..f0a94d550f 100644 --- a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift @@ -1241,11 +1241,11 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { switch navigation { case let .chat(_, subject, peekData): if let navigationController = strongSelf.getNavigationController() { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: subject, peekData: peekData)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId), subject: subject, peekData: peekData)) } case let .withBotStartPayload(botStart): if let navigationController = strongSelf.getNavigationController() { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), botStart: botStart, keepStack: .always)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId), botStart: botStart, keepStack: .always)) } case .info: let _ = (strongSelf.context.account.postbox.loadedPeerWithId(peerId) diff --git a/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift b/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift index 067b5725e4..4db8bcf841 100644 --- a/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift +++ b/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift @@ -473,7 +473,7 @@ public final class InviteLinkViewController: ViewController { self.interaction = InviteLinkViewInteraction(context: context, openPeer: { [weak self] peerId in if let strongSelf = self, let navigationController = strongSelf.controller?.navigationController as? NavigationController { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), keepStack: .always)) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId), keepStack: .always)) } }, copyLink: { [weak self] invite in UIPasteboard.general.string = invite.link diff --git a/submodules/InviteLinksUI/Sources/InviteRequestsController.swift b/submodules/InviteLinksUI/Sources/InviteRequestsController.swift index ac1a55704c..51dcde2325 100644 --- a/submodules/InviteLinksUI/Sources/InviteRequestsController.swift +++ b/submodules/InviteLinksUI/Sources/InviteRequestsController.swift @@ -383,7 +383,7 @@ public func inviteRequestsController(context: AccountContext, updatedPresentatio } navigateToChatImpl = { [weak controller] peer in if let navigationController = controller?.navigationController as? NavigationController { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer.id), keepStack: .always)) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peer.id), keepStack: .always)) } } dismissInputImpl = { [weak controller] in diff --git a/submodules/LegacyComponents/Sources/ActionStage.mm b/submodules/LegacyComponents/Sources/ActionStage.mm index 2827a5da98..daa7759af3 100644 --- a/submodules/LegacyComponents/Sources/ActionStage.mm +++ b/submodules/LegacyComponents/Sources/ActionStage.mm @@ -476,7 +476,7 @@ ActionStage *ActionStageInstance() { TGLegacyLog(@"%s should be called from graph queue", __PRETTY_FUNCTION__); - return nil; + return false; } __block bool result = false; @@ -517,7 +517,7 @@ ActionStage *ActionStageInstance() { TGLegacyLog(@"%s should be called from graph queue", __PRETTY_FUNCTION__); - return nil; + return false; } __block bool result = false; diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index 381c666358..70d9a22581 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -604,7 +604,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { } var hasTimer = false - if controller.chatLocation?.peerId != controller.context.account.peerId && controller.chatLocation?.peerId.namespace == Namespaces.Peer.CloudUser { + if controller.chatLocation?.peerId != controller.context.account.peerId && controller.chatLocation?.peerId?.namespace == Namespaces.Peer.CloudUser { hasTimer = true } @@ -643,7 +643,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { } var hasTimer = false - if controller.chatLocation?.peerId != controller.context.account.peerId && controller.chatLocation?.peerId.namespace == Namespaces.Peer.CloudUser { + if controller.chatLocation?.peerId != controller.context.account.peerId && controller.chatLocation?.peerId?.namespace == Namespaces.Peer.CloudUser { hasTimer = true } diff --git a/submodules/MtProtoKit/Sources/MTDisposable.m b/submodules/MtProtoKit/Sources/MTDisposable.m index ad89f9e0bd..0912bed35d 100644 --- a/submodules/MtProtoKit/Sources/MTDisposable.m +++ b/submodules/MtProtoKit/Sources/MTDisposable.m @@ -31,7 +31,7 @@ { if (block != nil) { - __strong id strongBlock = (__bridge_transfer id)block; + __unused __strong id strongBlock = (__bridge_transfer id)block; strongBlock = nil; } } diff --git a/submodules/MtProtoKit/Sources/MTFileBasedKeychain.m b/submodules/MtProtoKit/Sources/MTFileBasedKeychain.m index abe1a51b03..999c88bdff 100644 --- a/submodules/MtProtoKit/Sources/MTFileBasedKeychain.m +++ b/submodules/MtProtoKit/Sources/MTFileBasedKeychain.m @@ -136,7 +136,7 @@ static NSMutableDictionary *keychains() { uint8_t buf[32]; - int result = 0; + __unused int result = 0; result = SecRandomCopyBytes(kSecRandomDefault, 32, buf); _aesKey = [[NSData alloc] initWithBytes:buf length:32]; diff --git a/submodules/MtProtoKit/Sources/MTNetworkAvailability.m b/submodules/MtProtoKit/Sources/MTNetworkAvailability.m index da33990b8c..500cc281cb 100644 --- a/submodules/MtProtoKit/Sources/MTNetworkAvailability.m +++ b/submodules/MtProtoKit/Sources/MTNetworkAvailability.m @@ -32,7 +32,7 @@ static const void *MTNetworkAvailabilityContextRetain(const void *info) static void MTNetworkAvailabilityContextRelease(const void *info) { void *retainedThing = (__bridge void *)((__bridge id)info); - id unretainedThing = (__bridge_transfer id)retainedThing; + __unused id unretainedThing = (__bridge_transfer id)retainedThing; unretainedThing = nil; } diff --git a/submodules/PeerInfoUI/Sources/ChannelBlacklistController.swift b/submodules/PeerInfoUI/Sources/ChannelBlacklistController.swift index dc51da7a52..9c2870d1db 100644 --- a/submodules/PeerInfoUI/Sources/ChannelBlacklistController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelBlacklistController.swift @@ -369,7 +369,7 @@ public func channelBlacklistController(context: AccountContext, updatedPresentat actionSheet?.dismissAnimated() if participant.peer is TelegramChannel { if let navigationController = getNavigationControllerImpl?() { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(participant.peer.id))) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: participant.peer.id))) } } else if let infoController = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: participant.peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) { pushControllerImpl?(infoController) diff --git a/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSetupController.swift b/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSetupController.swift index 0b6da2eba5..40138e2404 100644 --- a/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSetupController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSetupController.swift @@ -642,7 +642,7 @@ public func channelDiscussionGroupSetupController(context: AccountContext, updat guard let navigationController = controller?.navigationController as? NavigationController else { return } - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(groupId), keepStack: .always)) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: groupId), keepStack: .always)) } return controller } diff --git a/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift b/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift index afd00f384f..90347c8b60 100644 --- a/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift @@ -930,7 +930,7 @@ public func channelPermissionsController(context: AccountContext, updatedPresent } navigateToChatControllerImpl = { [weak controller] peerId in if let controller = controller, let navigationController = controller.navigationController as? NavigationController { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), keepStack: .always)) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId), keepStack: .always)) } } dismissInputImpl = { [weak controller] in diff --git a/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift b/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift index 42c68f01ab..2e89f127f4 100644 --- a/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift @@ -1490,7 +1490,7 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta } }) if filteredPeerIds.isEmpty { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, chatController: nil, context: context, chatLocation: .peer(peerId), keepStack: .never, animated: true)) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, chatController: nil, context: context, chatLocation: .peer(id: peerId), keepStack: .never, animated: true)) } else { selectionController.displayProgress = true let _ = (context.engine.peers.addChannelMembers(peerId: peerId, memberIds: filteredPeerIds) @@ -1499,19 +1499,19 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta return } - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, chatController: nil, context: context, chatLocation: .peer(peerId), keepStack: .never, animated: true)) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, chatController: nil, context: context, chatLocation: .peer(id: peerId), keepStack: .never, animated: true)) }, completed: { [weak selectionController] in guard let selectionController = selectionController, let navigationController = selectionController.navigationController as? NavigationController else { return } - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, chatController: nil, context: context, chatLocation: .peer(peerId), keepStack: .never, animated: true)) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, chatController: nil, context: context, chatLocation: .peer(id: peerId), keepStack: .never, animated: true)) }) } }) } else { if let navigationController = controller.navigationController as? NavigationController { - navigationController.replaceAllButRootController(context.sharedContext.makeChatController(context: context, chatLocation: .peer(peerId), subject: nil, botStart: nil, mode: .standard(previewing: false)), animated: true) + navigationController.replaceAllButRootController(context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, mode: .standard(previewing: false)), animated: true) } } } diff --git a/submodules/PeerInfoUI/Sources/ConvertToSupergroupController.swift b/submodules/PeerInfoUI/Sources/ConvertToSupergroupController.swift index 18fdb87649..fa2162d8ff 100644 --- a/submodules/PeerInfoUI/Sources/ConvertToSupergroupController.swift +++ b/submodules/PeerInfoUI/Sources/ConvertToSupergroupController.swift @@ -150,7 +150,7 @@ public func convertToSupergroupController(context: AccountContext, peerId: PeerI if !alreadyConverting { convertDisposable.set((context.engine.peers.convertGroupToSupergroup(peerId: peerId) |> deliverOnMainQueue).start(next: { createdPeerId in - replaceControllerImpl?(context.sharedContext.makeChatController(context: context, chatLocation: .peer(createdPeerId), subject: nil, botStart: nil, mode: .standard(previewing: false))) + replaceControllerImpl?(context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: createdPeerId), subject: nil, botStart: nil, mode: .standard(previewing: false))) })) } })]), nil) diff --git a/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift b/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift index 4224870378..6e0b80c573 100644 --- a/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift +++ b/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift @@ -1240,7 +1240,7 @@ public func deviceContactInfoController(context: AccountContext, updatedPresenta } openChatImpl = { [weak controller] peerId in if let navigationController = (controller?.navigationController as? NavigationController) { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId))) } } replaceControllerImpl = { [weak controller] value in diff --git a/submodules/PeerInfoUI/Sources/GroupStickerPackSetupController.swift b/submodules/PeerInfoUI/Sources/GroupStickerPackSetupController.swift index f15972186e..09fd24a8b4 100644 --- a/submodules/PeerInfoUI/Sources/GroupStickerPackSetupController.swift +++ b/submodules/PeerInfoUI/Sources/GroupStickerPackSetupController.swift @@ -487,7 +487,7 @@ public func groupStickerPackSetupController(context: AccountContext, updatedPres } navigateToChatControllerImpl = { [weak controller] peerId in if let controller = controller, let navigationController = controller.navigationController as? NavigationController { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId))) } } dismissImpl = { [weak controller] in diff --git a/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift b/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift index 079ac3937c..8a704f5eee 100644 --- a/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift +++ b/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift @@ -490,7 +490,7 @@ public func peersNearbyController(context: AccountContext) -> ViewController { })) }, contextAction: { peer, node, gesture in let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let chatController = context.sharedContext.makeChatController(context: context, chatLocation: .peer(peer.id), subject: nil, botStart: nil, mode: .standard(previewing: true)) + let chatController = context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .standard(previewing: true)) chatController.canReadHistory.set(false) let contextController = ContextController(account: context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: peerNearbyContextMenuItems(context: context, peerId: peer.id, present: { c in presentControllerImpl?(c, nil) @@ -604,7 +604,7 @@ public func peersNearbyController(context: AccountContext) -> ViewController { } navigateToChatImpl = { [weak controller] peer in if let navigationController = controller?.navigationController as? NavigationController { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer.id), keepStack: .always, purposefulAction: {}, peekData: nil)) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peer.id), keepStack: .always, purposefulAction: {}, peekData: nil)) } } pushControllerImpl = { [weak controller] c in diff --git a/submodules/Postbox/BUILD b/submodules/Postbox/BUILD index 5e93f5a24c..b975adf50e 100644 --- a/submodules/Postbox/BUILD +++ b/submodules/Postbox/BUILD @@ -16,6 +16,7 @@ swift_library( "//submodules/MurMurHash32:MurMurHash32", "//submodules/StringTransliteration:StringTransliteration", "//submodules/ManagedFile:ManagedFile", + "//submodules/Utils/RangeSet:RangeSet", ], visibility = [ "//visibility:public", diff --git a/submodules/Postbox/Sources/ChatLocation.swift b/submodules/Postbox/Sources/ChatLocation.swift index 095d1da8b6..b77e478ed1 100644 --- a/submodules/Postbox/Sources/ChatLocation.swift +++ b/submodules/Postbox/Sources/ChatLocation.swift @@ -2,8 +2,9 @@ import Foundation import SwiftSignalKit public enum ChatLocationInput { - case peer(PeerId) - case external(PeerId, Int64, Signal) + case peer(peerId: PeerId) + case thread(peerId: PeerId, threadId: Int64, data: Signal) + case feed(id: Int32, data: Signal) } public enum ResolvedChatLocationInput { diff --git a/submodules/Postbox/Sources/MessageHistoryHolesView.swift b/submodules/Postbox/Sources/MessageHistoryHolesView.swift index 503580e653..0e5bcf7c4f 100644 --- a/submodules/Postbox/Sources/MessageHistoryHolesView.swift +++ b/submodules/Postbox/Sources/MessageHistoryHolesView.swift @@ -5,12 +5,14 @@ public struct MessageHistoryHolesViewEntry: Equatable, Hashable { public let direction: MessageHistoryViewRelativeHoleDirection public let space: MessageHistoryHoleSpace public let count: Int + public let userId: Int64? - public init(hole: MessageHistoryViewHole, direction: MessageHistoryViewRelativeHoleDirection, space: MessageHistoryHoleSpace, count: Int) { + public init(hole: MessageHistoryViewHole, direction: MessageHistoryViewRelativeHoleDirection, space: MessageHistoryHoleSpace, count: Int, userId: Int64?) { self.hole = hole self.direction = direction self.space = space self.count = count + self.userId = userId } } diff --git a/submodules/Postbox/Sources/MessageHistoryView.swift b/submodules/Postbox/Sources/MessageHistoryView.swift index bdecca3d5d..c7694c699e 100644 --- a/submodules/Postbox/Sources/MessageHistoryView.swift +++ b/submodules/Postbox/Sources/MessageHistoryView.swift @@ -248,37 +248,30 @@ public struct MessageHistoryViewOrderStatistics: OptionSet { } public final class MessageHistoryViewExternalInput: Equatable { - public let peerId: PeerId - public let threadId: Int64 + public enum Content: Equatable { + case messages(indices: [MessageIndex], holes: [MessageId.Namespace: IndexSet], userId: Int64?) + case thread(peerId: PeerId, id: Int64, holes: [MessageId.Namespace: IndexSet]) + } + + public let content: Content public let maxReadIncomingMessageId: MessageId? public let maxReadOutgoingMessageId: MessageId? - public let holes: [MessageId.Namespace: IndexSet] public init( - peerId: PeerId, - threadId: Int64, + content: Content, maxReadIncomingMessageId: MessageId?, - maxReadOutgoingMessageId: MessageId?, - holes: [MessageId.Namespace: IndexSet] + maxReadOutgoingMessageId: MessageId? ) { - self.peerId = peerId - self.threadId = threadId + self.content = content self.maxReadIncomingMessageId = maxReadIncomingMessageId self.maxReadOutgoingMessageId = maxReadOutgoingMessageId - self.holes = holes } public static func ==(lhs: MessageHistoryViewExternalInput, rhs: MessageHistoryViewExternalInput) -> Bool { if lhs === rhs { return true } - if lhs.peerId != rhs.peerId { - return false - } - if lhs.threadId != rhs.threadId { - return false - } - if lhs.holes != rhs.holes { + if lhs.content != rhs.content { return false } if lhs.maxReadIncomingMessageId != rhs.maxReadIncomingMessageId { @@ -332,6 +325,8 @@ final class MutableMessageHistoryView { fileprivate var isAddedToChatList: Bool + private var userId: Int64? + init( postbox: PostboxImpl, orderStatistics: MessageHistoryViewOrderStatistics, @@ -370,7 +365,13 @@ final class MutableMessageHistoryView { case let .single(peerId): self.isAddedToChatList = postbox.chatListTable.getPeerChatListIndex(peerId: peerId) != nil case let .external(input): - self.isAddedToChatList = postbox.chatListTable.getPeerChatListIndex(peerId: input.peerId) != nil + switch input.content { + case let .thread(peerId, _, _): + self.isAddedToChatList = postbox.chatListTable.getPeerChatListIndex(peerId: peerId) != nil + case let .messages(_, _, userId): + self.isAddedToChatList = false + self.userId = userId + } } self.state = HistoryViewState(postbox: postbox, inputAnchor: inputAnchor, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: self.orderStatistics, ignoreMessagesInTimestampRange: self.ignoreMessagesInTimestampRange, halfLimit: count + 1, locations: peerIds) @@ -441,16 +442,19 @@ final class MutableMessageHistoryView { var holePeerIdsSet = Set() if !transaction.chatListOperations.isEmpty { - let mainPeerId: PeerId switch peerIds { case let .associated(peerId, _): - mainPeerId = peerId + self.isAddedToChatList = postbox.chatListTable.getPeerChatListIndex(peerId: peerId) != nil case let .single(peerId): - mainPeerId = peerId + self.isAddedToChatList = postbox.chatListTable.getPeerChatListIndex(peerId: peerId) != nil case let .external(input): - mainPeerId = input.peerId + switch input.content { + case let .thread(peerId, _, _): + self.isAddedToChatList = postbox.chatListTable.getPeerChatListIndex(peerId: peerId) != nil + case .messages: + self.isAddedToChatList = false + } } - self.isAddedToChatList = postbox.chatListTable.getPeerChatListIndex(peerId: mainPeerId) != nil } switch self.peerIds { @@ -476,27 +480,43 @@ final class MutableMessageHistoryView { } } case let .external(input): - if let value = transaction.currentOperationsByPeerId[input.peerId] { - operations.append(value) + switch input.content { + case let .thread(peerId, _, _): + if let value = transaction.currentOperationsByPeerId[peerId] { + operations.append(value) + } + case .messages: + //TODO:implement + break } } var hasChanges = false let unwrappedTag: MessageTags = self.tag ?? [] - let threadId: Int64? + let externalThreadId: Int64? + let isExternal: Bool switch self.peerIds { case .single, .associated: - threadId = nil + externalThreadId = nil + isExternal = false case let .external(input): - threadId = input.threadId + isExternal = true + switch input.content { + case let .thread(_, id, _): + externalThreadId = id + case .messages: + externalThreadId = nil + //TODO:implement + break + } } switch self.state { case let .loading(loadingState): for (key, holeOperations) in transaction.currentPeerHoleOperations { var matchesSpace = false - if threadId == nil { + if !isExternal { switch key.space { case .everywhere: matchesSpace = unwrappedTag.isEmpty @@ -557,7 +577,7 @@ final class MutableMessageHistoryView { var matches = false if matchesTag { - if threadId == nil || message.threadId == threadId { + if !isExternal || message.threadId == externalThreadId { if self.namespaces.contains(message.id.namespace) { matches = true if loadedState.add(entry: .IntermediateMessageEntry(message, nil, nil)) { @@ -608,7 +628,7 @@ final class MutableMessageHistoryView { } for (key, holeOperations) in transaction.currentPeerHoleOperations { var matchesSpace = false - if threadId == nil { + if !isExternal { switch key.space { case .everywhere: matchesSpace = unwrappedTag.isEmpty @@ -880,14 +900,14 @@ final class MutableMessageHistoryView { } } - func firstHole() -> (MessageHistoryViewHole, MessageHistoryViewRelativeHoleDirection, Int)? { + func firstHole() -> (MessageHistoryViewHole, MessageHistoryViewRelativeHoleDirection, Int, Int64?)? { switch self.sampledState { case let .loading(loadingSample): switch loadingSample { case .ready: return nil case let .loadHole(peerId, namespace, _, threadId, id): - return (.peer(MessageHistoryViewPeerHole(peerId: peerId, namespace: namespace, threadId: threadId)), .aroundId(MessageId(peerId: peerId, namespace: namespace, id: id)), self.fillCount * 2) + return (.peer(MessageHistoryViewPeerHole(peerId: peerId, namespace: namespace, threadId: threadId)), .aroundId(MessageId(peerId: peerId, namespace: namespace, id: id)), self.fillCount * 2, self.userId) } case let .loaded(loadedSample): if let hole = loadedSample.hole { @@ -897,7 +917,7 @@ final class MutableMessageHistoryView { } else { direction = .aroundId(MessageId(peerId: hole.peerId, namespace: hole.namespace, id: hole.startId)) } - return (.peer(MessageHistoryViewPeerHole(peerId: hole.peerId, namespace: hole.namespace, threadId: hole.threadId)), direction, self.fillCount * 2) + return (.peer(MessageHistoryViewPeerHole(peerId: hole.peerId, namespace: hole.namespace, threadId: hole.threadId)), direction, self.fillCount * 2, self.userId) } else { return nil } diff --git a/submodules/Postbox/Sources/MessageHistoryViewState.swift b/submodules/Postbox/Sources/MessageHistoryViewState.swift index 9ab908095a..c5cde494f1 100644 --- a/submodules/Postbox/Sources/MessageHistoryViewState.swift +++ b/submodules/Postbox/Sources/MessageHistoryViewState.swift @@ -70,7 +70,102 @@ private extension MessageHistoryInput { } return items case let .external(input, tag): - return postbox.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: tag, threadId: input.threadId, from: fromIndex, includeFrom: includeFrom, to: toIndex, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, limit: limit) + switch input.content { + case let .thread(peerId, id, _): + return postbox.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: tag, threadId: id, from: fromIndex, includeFrom: includeFrom, to: toIndex, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, limit: limit) + case let .messages(allIndices, _, _): + if allIndices.isEmpty { + return [] + } + var indices: [MessageIndex] = [] + var startIndex = fromIndex + var localIncludeFrom = includeFrom + while true { + var sliceIndices: [MessageIndex] = [] + if fromIndex < toIndex { + for i in 0 ..< allIndices.count { + var matches = false + if localIncludeFrom { + if allIndices[i] >= startIndex { + matches = true + } + } else { + if allIndices[i] > startIndex { + matches = true + } + } + if matches { + for j in i ..< min(i + limit, allIndices.count) { + sliceIndices.append(allIndices[j]) + } + break + } + } + //sliceIndices = self.threadsTable.laterIndices(threadId: threadId, peerId: peerId, namespace: namespace, index: startIndex, includeFrom: localIncludeFrom, count: limit) + } else { + for i in (0 ..< allIndices.count).reversed() { + var matches = false + if localIncludeFrom { + if allIndices[i] <= startIndex { + matches = true + } + } else { + if allIndices[i] < startIndex { + matches = true + } + } + if matches { + for j in (max(i - limit + 1, 0) ... i).reversed() { + sliceIndices.append(allIndices[j]) + } + break + } + } + + //sliceIndices = self.threadsTable.earlierIndices(threadId: threadId, peerId: peerId, namespace: namespace, index: startIndex, includeFrom: localIncludeFrom, count: limit) + } + if sliceIndices.isEmpty { + break + } + startIndex = sliceIndices[sliceIndices.count - 1] + localIncludeFrom = false + + for index in sliceIndices { + if let ignoreMessagesInTimestampRange = ignoreMessagesInTimestampRange { + if ignoreMessagesInTimestampRange.contains(index.timestamp) { + continue + } + } + indices.append(index) + } + if indices.count >= limit { + break + } + } + var result: [IntermediateMessage] = [] + if fromIndex < toIndex { + assert(indices.sorted() == indices) + } else { + assert(indices.sorted().reversed() == indices) + } + for index in indices { + if fromIndex < toIndex { + if index < fromIndex || index > toIndex { + continue + } + } else { + if index < toIndex || index > fromIndex { + continue + } + } + if let message = postbox.messageHistoryTable.getMessage(index) { + result.append(message) + } else { + assertionFailure() + } + } + return result + } } } @@ -105,8 +200,13 @@ private func canContainHoles(_ peerIdAndNamespace: PeerIdAndNamespace, input: Me return false } return messageNamespaces[peerIdAndNamespace.namespace] != nil - case .external: - return true + case let .external(data, _): + switch data.content { + case .thread: + return true + case .messages: + return false + } } } @@ -395,14 +495,29 @@ private func sampleHoleRanges(input: MessageHistoryInput, orderedEntriesBySpace: case let .automatic(automatic): tag = automatic?.tag case let .external(value, _): - threadId = value.threadId + switch value.content { + case let .thread(_, id, _): + threadId = id + case .messages: + threadId = nil + } } for (space, indices) in holes.holesBySpace { if indices.isEmpty { continue } - assert(canContainHoles(space, input: input, seedConfiguration: seedConfiguration)) + switch input { + case .automatic: + assert(canContainHoles(space, input: input, seedConfiguration: seedConfiguration)) + case let .external(data, _): + switch data.content { + case .thread: + assert(canContainHoles(space, input: input, seedConfiguration: seedConfiguration)) + case .messages: + break + } + } switch anchor { case .lowerBound, .upperBound: break @@ -764,7 +879,7 @@ struct OrderedHistoryViewEntries { if self.lowerOrAtAnchor.count > 1 { for i in 1 ..< self.lowerOrAtAnchor.count { if self.lowerOrAtAnchor[i].index < self.lowerOrAtAnchor[i - 1].index { - //assertionFailure() + assertionFailure() break } } @@ -772,7 +887,7 @@ struct OrderedHistoryViewEntries { if self.higherThanAnchor.count > 1 { for i in 1 ..< self.higherThanAnchor.count { if self.higherThanAnchor[i].index < self.higherThanAnchor[i - 1].index { - //assertionFailure() + assertionFailure() break } } @@ -897,28 +1012,35 @@ final class HistoryViewLoadedState { self.holes = holes var peerIds: [PeerId] = [] + var spaces: [PeerIdAndNamespace] = [] + let input: MessageHistoryInput switch locations { - case let .single(peerId): + case let .single(peerId): + peerIds.append(peerId) + input = .automatic(tag.flatMap { tag in + MessageHistoryInput.Automatic(tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup) + }) + case let .associated(peerId, associatedId): + peerIds.append(peerId) + if let associatedId = associatedId { + peerIds.append(associatedId.peerId) + } + input = .automatic(tag.flatMap { tag in + MessageHistoryInput.Automatic(tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup) + }) + case let .external(external): + switch external.content { + case let .thread(peerId, _, _): peerIds.append(peerId) - input = .automatic(tag.flatMap { tag in - MessageHistoryInput.Automatic(tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup) - }) - case let .associated(peerId, associatedId): - peerIds.append(peerId) - if let associatedId = associatedId { - peerIds.append(associatedId.peerId) - } - input = .automatic(tag.flatMap { tag in - MessageHistoryInput.Automatic(tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup) - }) - case let .external(external): - peerIds.append(external.peerId) input = .external(external, tag) + case .messages: + input = .external(external, tag) + spaces.append(PeerIdAndNamespace(peerId: PeerId(namespace: PeerId.Namespace.max, id: PeerId.Id.max), namespace: 0)) + } } self.input = input - var spaces: [PeerIdAndNamespace] = [] for peerId in peerIds { for namespace in postbox.messageHistoryIndexTable.existingNamespaces(peerId: peerId) { if namespaces.contains(namespace) { @@ -1269,6 +1391,15 @@ final class HistoryViewLoadedState { let combinedSpacesAndIndicesByDirection = sampleEntries(orderedEntriesBySpace: self.orderedEntriesBySpace, anchor: self.anchor, halfLimit: self.halfLimit) let (clipRanges, sampledHole) = sampleHoleRanges(input: self.input, orderedEntriesBySpace: self.orderedEntriesBySpace, holes: self.holes, anchor: self.anchor, halfLimit: self.halfLimit, seedConfiguration: self.seedConfiguration) + /*switch self.input { + case .external: + if sampledHole == nil { + assert(true) + } + default: + break + }*/ + var holesToLower = false var holesToHigher = false var result: [MessageHistoryMessageEntry] = [] @@ -1360,7 +1491,17 @@ private func fetchHoles(postbox: PostboxImpl, locations: MessageHistoryViewInput peerIds.append(associatedId.peerId) } case let .external(input): - peerIds.append(input.peerId) + switch input.content { + case let .thread(peerId, _, _): + peerIds.append(peerId) + case let .messages(_, holes, _): + let key = PeerIdAndNamespace(peerId: PeerId(namespace: PeerId.Namespace.max, id: PeerId.Id.max), namespace: 0) + if let namespaceHoles = holes[0] { + return [key: namespaceHoles] + } else { + return [:] + } + } } switch locations { case .single, .associated: @@ -1382,19 +1523,24 @@ private func fetchHoles(postbox: PostboxImpl, locations: MessageHistoryViewInput } return holesBySpace case let .external(input): - var holesBySpace: [PeerIdAndNamespace: IndexSet] = [:] - for peerId in peerIds { - for (namespace, indices) in input.holes { - if namespaces.contains(namespace) { - if !indices.isEmpty { - let peerIdAndNamespace = PeerIdAndNamespace(peerId: peerId, namespace: namespace) - assert(canContainHoles(peerIdAndNamespace, input: .external(input, tag), seedConfiguration: postbox.seedConfiguration)) - holesBySpace[peerIdAndNamespace] = indices + switch input.content { + case let .thread(_, _, holes): + var holesBySpace: [PeerIdAndNamespace: IndexSet] = [:] + for peerId in peerIds { + for (namespace, indices) in holes { + if namespaces.contains(namespace) { + if !indices.isEmpty { + let peerIdAndNamespace = PeerIdAndNamespace(peerId: peerId, namespace: namespace) + assert(canContainHoles(peerIdAndNamespace, input: .external(input, tag), seedConfiguration: postbox.seedConfiguration)) + holesBySpace[peerIdAndNamespace] = indices + } } } } + return holesBySpace + case .messages: + return [:] } - return holesBySpace } } @@ -1516,7 +1662,12 @@ enum HistoryViewState { var threadId: Int64? switch locations { case let .external(input): - threadId = input.threadId + switch input.content { + case let .thread(_, id, _): + threadId = id + case .messages: + break + } default: break } diff --git a/submodules/Postbox/Sources/MessageOfInterestHolesView.swift b/submodules/Postbox/Sources/MessageOfInterestHolesView.swift index 18d41cf9ba..1c1f068609 100644 --- a/submodules/Postbox/Sources/MessageOfInterestHolesView.swift +++ b/submodules/Postbox/Sources/MessageOfInterestHolesView.swift @@ -66,7 +66,7 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView { private func updateFromView() -> Bool { let closestHole: MessageOfInterestHole? - if let (hole, direction, _) = self.wrappedView.firstHole() { + if let (hole, direction, _, _) = self.wrappedView.firstHole() { closestHole = MessageOfInterestHole(hole: hole, direction: direction) } else { closestHole = nil diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index 4b2b3d5646..d9a64e80f9 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -2549,10 +2549,10 @@ final class PostboxImpl { switch chatLocation { case let .peer(peerId): return .single((.peer(peerId), false)) - case let .external(_, _, input): + case .thread(_, _, let data), .feed(_, let data): return Signal { subscriber in var isHoleFill = false - return (input + return (data |> map { value -> (ResolvedChatLocationInput, Bool) in let wasHoleFill = isHoleFill isHoleFill = true @@ -2812,12 +2812,21 @@ final class PostboxImpl { let initialData: InitialMessageHistoryData switch peerIds { - case let .single(peerId): - initialData = self.initialMessageHistoryData(peerId: peerId, threadId: nil) - case let .associated(peerId, _): - initialData = self.initialMessageHistoryData(peerId: peerId, threadId: nil) - case let .external(input): - initialData = self.initialMessageHistoryData(peerId: input.peerId, threadId: input.threadId) + case let .single(peerId): + initialData = self.initialMessageHistoryData(peerId: peerId, threadId: nil) + case let .associated(peerId, _): + initialData = self.initialMessageHistoryData(peerId: peerId, threadId: nil) + case let .external(input): + switch input.content { + case let .thread(peerId, id, _): + initialData = self.initialMessageHistoryData(peerId: peerId, threadId: id) + case .messages: + initialData = InitialMessageHistoryData( + peer: nil, + storedInterfaceState: nil, + associatedMessages: [:] + ) + } } subscriber.putNext((MessageHistoryView(mutableView), initialUpdateType, initialData)) diff --git a/submodules/Postbox/Sources/ViewTracker.swift b/submodules/Postbox/Sources/ViewTracker.swift index b6c8350238..05b45e0c73 100644 --- a/submodules/Postbox/Sources/ViewTracker.swift +++ b/submodules/Postbox/Sources/ViewTracker.swift @@ -428,14 +428,14 @@ final class ViewTracker { private func updateTrackedHoles() { var firstHolesAndTags = Set() for (view, _) in self.messageHistoryViews.copyItems() { - if let (hole, direction, count) = view.firstHole() { + if let (hole, direction, count, userId) = view.firstHole() { let space: MessageHistoryHoleSpace if let tag = view.tag { space = .tag(tag) } else { space = .everywhere } - firstHolesAndTags.insert(MessageHistoryHolesViewEntry(hole: hole, direction: direction, space: space, count: count)) + firstHolesAndTags.insert(MessageHistoryHolesViewEntry(hole: hole, direction: direction, space: space, count: count, userId: userId)) } } diff --git a/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift index c96a5a07d0..fceb5af6e4 100644 --- a/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift +++ b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift @@ -696,7 +696,7 @@ private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollVie guard let strongSelf = self else { return } - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: nil, keepStack: .always, peekData: nil, completion: { [weak navigationController] _ in + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId), subject: nil, keepStack: .always, peekData: nil, completion: { [weak navigationController] _ in if let navigationController = navigationController { var viewControllers = navigationController.viewControllers viewControllers = viewControllers.filter { controller in diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SBlockDisposable.m b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SBlockDisposable.m index b8d1f989d4..bf4eab6806 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SBlockDisposable.m +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SBlockDisposable.m @@ -31,7 +31,7 @@ { if (block != nil) { - __strong id strongBlock = (__bridge_transfer id)block; + __unused __strong id strongBlock = (__bridge_transfer id)block; strongBlock = nil; } } diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SQueueLocalObject.m b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SQueueLocalObject.m index 0b12201362..24302211e2 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SQueueLocalObject.m +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SQueueLocalObject.m @@ -29,7 +29,7 @@ } -(void)dealloc { - __block id value = self->valueRef; + __unused __block id value = self->valueRef; self->valueRef = nil; [_queue dispatch:^{ value = nil; diff --git a/submodules/SearchPeerMembers/Sources/SearchPeerMembers.swift b/submodules/SearchPeerMembers/Sources/SearchPeerMembers.swift index 351de0f580..4b85ba3552 100644 --- a/submodules/SearchPeerMembers/Sources/SearchPeerMembers.swift +++ b/submodules/SearchPeerMembers/Sources/SearchPeerMembers.swift @@ -81,6 +81,11 @@ public func searchPeerMembers(context: AccountContext, peerId: EnginePeer.Id, ch return ActionDisposable { disposable.dispose() } + case .feed: + subscriber.putNext(([], true)) + + return ActionDisposable { + } } } |> runOn(Queue.mainQueue()) } diff --git a/submodules/SettingsUI/Sources/LogoutOptionsController.swift b/submodules/SettingsUI/Sources/LogoutOptionsController.swift index 6134bfd35c..13c9e68b1e 100644 --- a/submodules/SettingsUI/Sources/LogoutOptionsController.swift +++ b/submodules/SettingsUI/Sources/LogoutOptionsController.swift @@ -196,7 +196,7 @@ public func logoutOptionsController(context: AccountContext, navigationControlle |> deliverOnMainQueue).start(next: { peerId in if let peerId = peerId, let navigationController = navigationController { dismissImpl?() - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId))) } })) }) diff --git a/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift b/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift index a8c4dbe046..1027d7354d 100644 --- a/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift +++ b/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift @@ -834,7 +834,7 @@ func settingsSearchableItems(context: AccountContext, notificationExceptionsList allItems.append(contentsOf: profileItems) let savedMessages = SettingsSearchableItem(id: .savedMessages(0), title: strings.Settings_SavedMessages, alternate: synonyms(strings.SettingsSearch_Synonyms_SavedMessages), icon: .savedMessages, breadcrumbs: [], present: { context, _, present in - present(.push, context.sharedContext.makeChatController(context: context, chatLocation: .peer(context.account.peerId), subject: nil, botStart: nil, mode: .standard(previewing: false))) + present(.push, context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: context.account.peerId), subject: nil, botStart: nil, mode: .standard(previewing: false))) }) allItems.append(savedMessages) @@ -885,7 +885,7 @@ func settingsSearchableItems(context: AccountContext, notificationExceptionsList let _ = (context.engine.peers.supportPeerId() |> deliverOnMainQueue).start(next: { peerId in if let peerId = peerId { - present(.push, context.sharedContext.makeChatController(context: context, chatLocation: .peer(peerId), subject: nil, botStart: nil, mode: .standard(previewing: false))) + present(.push, context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, mode: .standard(previewing: false))) } }) }) diff --git a/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift b/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift index 04ec62c84a..a31f272c52 100644 --- a/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift +++ b/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift @@ -1238,7 +1238,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta } navigateToChatControllerImpl = { [weak controller] peerId in if let controller = controller, let navigationController = controller.navigationController as? NavigationController { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId))) } } dismissImpl = { [weak controller] in diff --git a/submodules/SettingsUI/Sources/ThemePickerController.swift b/submodules/SettingsUI/Sources/ThemePickerController.swift index 947ee21720..f02b48e1eb 100644 --- a/submodules/SettingsUI/Sources/ThemePickerController.swift +++ b/submodules/SettingsUI/Sources/ThemePickerController.swift @@ -417,7 +417,7 @@ public func themePickerController(context: AccountContext, focusOnItemTag: Theme }, editTheme: { theme in let controller = editThemeController(context: context, mode: .edit(theme), navigateToChat: { peerId in if let navigationController = getNavigationControllerImpl?() { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId))) } }) pushControllerImpl?(controller) @@ -439,7 +439,7 @@ public func themePickerController(context: AccountContext, focusOnItemTag: Theme if case let .cloud(cloudTheme) = themeReference, cloudTheme.theme.settings?.isEmpty ?? true { let controller = editThemeController(context: context, mode: .edit(cloudTheme), navigateToChat: { peerId in if let navigationController = getNavigationControllerImpl?() { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId))) } }) pushControllerImpl?(controller) @@ -464,7 +464,7 @@ public func themePickerController(context: AccountContext, focusOnItemTag: Theme |> deliverOnMainQueue).start(next: { themeReference in let controller = editThemeController(context: context, mode: .create(nil, nil), navigateToChat: { peerId in if let navigationController = getNavigationControllerImpl?() { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId))) } }) pushControllerImpl?(controller) @@ -526,7 +526,7 @@ public func themePickerController(context: AccountContext, focusOnItemTag: Theme items.append(.action(ContextMenuActionItem(text: presentationData.strings.Appearance_EditTheme, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ApplyTheme"), color: theme.contextMenu.primaryColor) }, action: { c, f in let controller = editThemeController(context: context, mode: .edit(theme), navigateToChat: { peerId in if let navigationController = getNavigationControllerImpl?() { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId))) } }) @@ -556,7 +556,7 @@ public func themePickerController(context: AccountContext, focusOnItemTag: Theme let controller = editThemeController(context: context, mode: .create(result, settings ), navigateToChat: { peerId in if let navigationController = getNavigationControllerImpl?() { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId))) } }) updateControllersImpl?({ controllers in @@ -762,7 +762,7 @@ public func themePickerController(context: AccountContext, focusOnItemTag: Theme items.append(.action(ContextMenuActionItem(text: presentationData.strings.Appearance_EditTheme, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ApplyTheme"), color: theme.contextMenu.primaryColor) }, action: { c, f in let controller = editThemeController(context: context, mode: .edit(cloudTheme), navigateToChat: { peerId in if let navigationController = getNavigationControllerImpl?() { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId))) } }) @@ -797,7 +797,7 @@ public func themePickerController(context: AccountContext, focusOnItemTag: Theme let controller = ThemeAccentColorController(context: context, mode: .edit(settings: settings, theme: theme, wallpaper: wallpaper, generalThemeReference: effectiveThemeReference.generalThemeReference, defaultThemeReference: nil, create: true, completion: { result, settings in let controller = editThemeController(context: context, mode: .create(hasSettings ? nil : result, hasSettings ? settings : nil), navigateToChat: { peerId in if let navigationController = getNavigationControllerImpl?() { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId))) } }) updateControllersImpl?({ controllers in diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift index 6d1fa05934..557d42377a 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift @@ -501,7 +501,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The }, editTheme: { theme in let controller = editThemeController(context: context, mode: .edit(theme), navigateToChat: { peerId in if let navigationController = getNavigationControllerImpl?() { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId))) } }) pushControllerImpl?(controller) @@ -562,7 +562,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The items.append(.action(ContextMenuActionItem(text: presentationData.strings.Appearance_EditTheme, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ApplyTheme"), color: theme.contextMenu.primaryColor) }, action: { c, f in let controller = editThemeController(context: context, mode: .edit(theme), navigateToChat: { peerId in if let navigationController = getNavigationControllerImpl?() { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId))) } }) @@ -592,7 +592,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The let controller = editThemeController(context: context, mode: .create(result, settings ), navigateToChat: { peerId in if let navigationController = getNavigationControllerImpl?() { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId))) } }) updateControllersImpl?({ controllers in @@ -796,7 +796,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The items.append(.action(ContextMenuActionItem(text: presentationData.strings.Appearance_EditTheme, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ApplyTheme"), color: theme.contextMenu.primaryColor) }, action: { c, f in let controller = editThemeController(context: context, mode: .edit(cloudTheme), navigateToChat: { peerId in if let navigationController = getNavigationControllerImpl?() { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId))) } }) @@ -831,7 +831,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The let controller = ThemeAccentColorController(context: context, mode: .edit(settings: settings, theme: theme, wallpaper: wallpaper, generalThemeReference: effectiveThemeReference.generalThemeReference, defaultThemeReference: nil, create: true, completion: { result, settings in let controller = editThemeController(context: context, mode: .create(hasSettings ? nil : result, hasSettings ? settings : nil), navigateToChat: { peerId in if let navigationController = getNavigationControllerImpl?() { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId))) } }) updateControllersImpl?({ controllers in diff --git a/submodules/StatisticsUI/Sources/ChannelStatsController.swift b/submodules/StatisticsUI/Sources/ChannelStatsController.swift index 9616925a99..f73027f3e2 100644 --- a/submodules/StatisticsUI/Sources/ChannelStatsController.swift +++ b/submodules/StatisticsUI/Sources/ChannelStatsController.swift @@ -450,7 +450,7 @@ public func channelStatsController(context: AccountContext, updatedPresentationD contextActionImpl?(messageId, node, gesture) }) - let messageView = context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId), index: .upperBound, anchorIndex: .upperBound, count: 100, fixedCombinedReadStates: nil) + let messageView = context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: peerId), index: .upperBound, anchorIndex: .upperBound, count: 100, fixedCombinedReadStates: nil) |> map { messageHistoryView, _, _ -> MessageHistoryView? in return messageHistoryView } @@ -518,7 +518,7 @@ public func channelStatsController(context: AccountContext, updatedPresentationD items.append(.action(ContextMenuActionItem(text: presentationData.strings.SharedMedia_ViewInChat, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor) }, action: { [weak controller] c, _ in c.dismiss(completion: { if let navigationController = controller?.navigationController as? NavigationController { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), subject: .message(id: .id(messageId), highlight: true, timecode: nil))) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId), subject: .message(id: .id(messageId), highlight: true, timecode: nil))) } }) }))) diff --git a/submodules/StatisticsUI/Sources/GroupStatsController.swift b/submodules/StatisticsUI/Sources/GroupStatsController.swift index c118eca1be..86aa14cedf 100644 --- a/submodules/StatisticsUI/Sources/GroupStatsController.swift +++ b/submodules/StatisticsUI/Sources/GroupStatsController.swift @@ -884,7 +884,7 @@ public func groupStatsController(context: AccountContext, updatedPresentationDat let _ = (context.account.postbox.loadedPeerWithId(participantPeerId) |> take(1) |> deliverOnMainQueue).start(next: { peer in - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, chatController: nil, context: context, chatLocation: .peer(peerId), subject: nil, botStart: nil, updateTextInputState: nil, activateInput: false, keepStack: .always, useExisting: false, purposefulAction: nil, scrollToEndIfExists: false, activateMessageSearch: (.member(peer), ""), animated: true)) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, chatController: nil, context: context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, updateTextInputState: nil, activateInput: false, keepStack: .always, useExisting: false, purposefulAction: nil, scrollToEndIfExists: false, activateMessageSearch: (.member(peer), ""), animated: true)) }) } } diff --git a/submodules/StatisticsUI/Sources/MessageStatsController.swift b/submodules/StatisticsUI/Sources/MessageStatsController.swift index 5cca507484..398eb7fd21 100644 --- a/submodules/StatisticsUI/Sources/MessageStatsController.swift +++ b/submodules/StatisticsUI/Sources/MessageStatsController.swift @@ -263,7 +263,7 @@ public func messageStatsController(context: AccountContext, messageId: MessageId } navigateToMessageImpl = { [weak controller] messageId in if let navigationController = controller?.navigationController as? NavigationController { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(messageId.peerId), subject: .message(id: .id(messageId), highlight: true, timecode: nil), keepStack: .always, useExisting: false, purposefulAction: {}, peekData: nil)) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: messageId.peerId), subject: .message(id: .id(messageId), highlight: true, timecode: nil), keepStack: .always, useExisting: false, purposefulAction: {}, peekData: nil)) } } return controller diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewController.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewController.swift index 7529e37e44..3c598121cf 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewController.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewController.swift @@ -149,7 +149,7 @@ public final class StickerPackPreviewController: ViewController, StandalonePrese } if let peer = peer, let parentNavigationController = strongSelf.parentNavigationController { strongSelf.dismiss() - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: parentNavigationController, context: strongSelf.context, chatLocation: .peer(peer.id), animated: true)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: parentNavigationController, context: strongSelf.context, chatLocation: .peer(id: peer.id), animated: true)) } })) }, actionPerformed: self.actionPerformed) diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index b93c2512a4..107a679804 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -107,7 +107,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[483104362] = { return Api.RichText.parse_textPhone($0) } dict[136105807] = { return Api.RichText.parse_textImage($0) } dict[894777186] = { return Api.RichText.parse_textAnchor($0) } - dict[-818518751] = { return Api.UserFull.parse_userFull($0) } + dict[-1938625919] = { return Api.UserFull.parse_userFull($0) } dict[-292807034] = { return Api.InputChannel.parse_inputChannelEmpty($0) } dict[-212145112] = { return Api.InputChannel.parse_inputChannel($0) } dict[1536380829] = { return Api.InputChannel.parse_inputChannelFromMessage($0) } @@ -292,6 +292,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1885586395] = { return Api.Update.parse_updatePendingJoinRequests($0) } dict[299870598] = { return Api.Update.parse_updateBotChatInviteRequester($0) } dict[357013699] = { return Api.Update.parse_updateMessageReactions($0) } + dict[1951948721] = { return Api.Update.parse_updateReadFeed($0) } dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) } dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) } dict[-592373577] = { return Api.GroupCallParticipantVideoSourceGroup.parse_groupCallParticipantVideoSourceGroup($0) } @@ -433,6 +434,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1694474197] = { return Api.messages.Chats.parse_chats($0) } dict[-1663561404] = { return Api.messages.Chats.parse_chatsSlice($0) } dict[-2118733814] = { return Api.messages.ChatInviteImporters.parse_chatInviteImporters($0) } + dict[1348066419] = { return Api.FeedPosition.parse_feedPosition($0) } dict[-659913713] = { return Api.InputGroupCall.parse_inputGroupCall($0) } dict[-2091463255] = { return Api.channels.SendAsPeers.parse_sendAsPeers($0) } dict[482797855] = { return Api.InputSingleMedia.parse_inputSingleMedia($0) } @@ -851,6 +853,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-463335103] = { return Api.PrivacyRule.parse_privacyValueDisallowUsers($0) } dict[1796427406] = { return Api.PrivacyRule.parse_privacyValueAllowChatParticipants($0) } dict[1103656293] = { return Api.PrivacyRule.parse_privacyValueDisallowChatParticipants($0) } + dict[-619039485] = { return Api.feed.FeedMessages.parse_feedMessagesNotModified($0) } + dict[-587770695] = { return Api.feed.FeedMessages.parse_feedMessages($0) } dict[-1230047312] = { return Api.MessageAction.parse_messageActionEmpty($0) } dict[-1119368275] = { return Api.MessageAction.parse_messageActionChatCreate($0) } dict[-1247687078] = { return Api.MessageAction.parse_messageActionChatEditTitle($0) } @@ -1255,6 +1259,8 @@ public struct Api { _1.serialize(buffer, boxed) case let _1 as Api.messages.ChatInviteImporters: _1.serialize(buffer, boxed) + case let _1 as Api.FeedPosition: + _1.serialize(buffer, boxed) case let _1 as Api.InputGroupCall: _1.serialize(buffer, boxed) case let _1 as Api.channels.SendAsPeers: @@ -1627,6 +1633,8 @@ public struct Api { _1.serialize(buffer, boxed) case let _1 as Api.PrivacyRule: _1.serialize(buffer, boxed) + case let _1 as Api.feed.FeedMessages: + _1.serialize(buffer, boxed) case let _1 as Api.MessageAction: _1.serialize(buffer, boxed) case let _1 as Api.PhoneCall: diff --git a/submodules/TelegramApi/Sources/Api2.swift b/submodules/TelegramApi/Sources/Api2.swift index b634b7e837..233faf6382 100644 --- a/submodules/TelegramApi/Sources/Api2.swift +++ b/submodules/TelegramApi/Sources/Api2.swift @@ -2944,13 +2944,13 @@ public extension Api { } public enum UserFull: TypeConstructorDescription { - case userFull(flags: Int32, id: Int64, about: String?, settings: Api.PeerSettings, profilePhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, botInfo: Api.BotInfo?, pinnedMsgId: Int32?, commonChatsCount: Int32, folderId: Int32?, ttlPeriod: Int32?, themeEmoticon: String?, privateForwardName: String?) + case userFull(flags: Int32, id: Int64, about: String?, settings: Api.PeerSettings, profilePhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, botInfo: Api.BotInfo?, pinnedMsgId: Int32?, commonChatsCount: Int32, folderId: Int32?, ttlPeriod: Int32?, themeEmoticon: String?, privateForwardName: String?, botGroupAdminRights: Api.ChatAdminRights?, botBroadcastAdminRights: Api.ChatAdminRights?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .userFull(let flags, let id, let about, let settings, let profilePhoto, let notifySettings, let botInfo, let pinnedMsgId, let commonChatsCount, let folderId, let ttlPeriod, let themeEmoticon, let privateForwardName): + case .userFull(let flags, let id, let about, let settings, let profilePhoto, let notifySettings, let botInfo, let pinnedMsgId, let commonChatsCount, let folderId, let ttlPeriod, let themeEmoticon, let privateForwardName, let botGroupAdminRights, let botBroadcastAdminRights): if boxed { - buffer.appendInt32(-818518751) + buffer.appendInt32(-1938625919) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt64(id, buffer: buffer, boxed: false) @@ -2965,14 +2965,16 @@ public extension Api { if Int(flags) & Int(1 << 14) != 0 {serializeInt32(ttlPeriod!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 15) != 0 {serializeString(themeEmoticon!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 16) != 0 {serializeString(privateForwardName!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 17) != 0 {botGroupAdminRights!.serialize(buffer, true)} + if Int(flags) & Int(1 << 18) != 0 {botBroadcastAdminRights!.serialize(buffer, true)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .userFull(let flags, let id, let about, let settings, let profilePhoto, let notifySettings, let botInfo, let pinnedMsgId, let commonChatsCount, let folderId, let ttlPeriod, let themeEmoticon, let privateForwardName): - return ("userFull", [("flags", flags), ("id", id), ("about", about), ("settings", settings), ("profilePhoto", profilePhoto), ("notifySettings", notifySettings), ("botInfo", botInfo), ("pinnedMsgId", pinnedMsgId), ("commonChatsCount", commonChatsCount), ("folderId", folderId), ("ttlPeriod", ttlPeriod), ("themeEmoticon", themeEmoticon), ("privateForwardName", privateForwardName)]) + case .userFull(let flags, let id, let about, let settings, let profilePhoto, let notifySettings, let botInfo, let pinnedMsgId, let commonChatsCount, let folderId, let ttlPeriod, let themeEmoticon, let privateForwardName, let botGroupAdminRights, let botBroadcastAdminRights): + return ("userFull", [("flags", flags), ("id", id), ("about", about), ("settings", settings), ("profilePhoto", profilePhoto), ("notifySettings", notifySettings), ("botInfo", botInfo), ("pinnedMsgId", pinnedMsgId), ("commonChatsCount", commonChatsCount), ("folderId", folderId), ("ttlPeriod", ttlPeriod), ("themeEmoticon", themeEmoticon), ("privateForwardName", privateForwardName), ("botGroupAdminRights", botGroupAdminRights), ("botBroadcastAdminRights", botBroadcastAdminRights)]) } } @@ -3011,6 +3013,14 @@ public extension Api { if Int(_1!) & Int(1 << 15) != 0 {_12 = parseString(reader) } var _13: String? if Int(_1!) & Int(1 << 16) != 0 {_13 = parseString(reader) } + var _14: Api.ChatAdminRights? + if Int(_1!) & Int(1 << 17) != 0 {if let signature = reader.readInt32() { + _14 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights + } } + var _15: Api.ChatAdminRights? + if Int(_1!) & Int(1 << 18) != 0 {if let signature = reader.readInt32() { + _15 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights + } } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil @@ -3024,8 +3034,10 @@ public extension Api { let _c11 = (Int(_1!) & Int(1 << 14) == 0) || _11 != nil let _c12 = (Int(_1!) & Int(1 << 15) == 0) || _12 != nil let _c13 = (Int(_1!) & Int(1 << 16) == 0) || _13 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 { - return Api.UserFull.userFull(flags: _1!, id: _2!, about: _3, settings: _4!, profilePhoto: _5, notifySettings: _6!, botInfo: _7, pinnedMsgId: _8, commonChatsCount: _9!, folderId: _10, ttlPeriod: _11, themeEmoticon: _12, privateForwardName: _13) + let _c14 = (Int(_1!) & Int(1 << 17) == 0) || _14 != nil + let _c15 = (Int(_1!) & Int(1 << 18) == 0) || _15 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 { + return Api.UserFull.userFull(flags: _1!, id: _2!, about: _3, settings: _4!, profilePhoto: _5, notifySettings: _6!, botInfo: _7, pinnedMsgId: _8, commonChatsCount: _9!, folderId: _10, ttlPeriod: _11, themeEmoticon: _12, privateForwardName: _13, botGroupAdminRights: _14, botBroadcastAdminRights: _15) } else { return nil @@ -4924,6 +4936,7 @@ public extension Api { case updatePendingJoinRequests(peer: Api.Peer, requestsPending: Int32, recentRequesters: [Int64]) case updateBotChatInviteRequester(peer: Api.Peer, date: Int32, userId: Int64, about: String, invite: Api.ExportedChatInvite, qts: Int32) case updateMessageReactions(peer: Api.Peer, msgId: Int32, reactions: Api.MessageReactions) + case updateReadFeed(flags: Int32, filterId: Int32, maxPosition: Api.FeedPosition, unreadCount: Int32?, unreadMutedCount: Int32?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -5769,6 +5782,16 @@ public extension Api { serializeInt32(msgId, buffer: buffer, boxed: false) reactions.serialize(buffer, true) break + case .updateReadFeed(let flags, let filterId, let maxPosition, let unreadCount, let unreadMutedCount): + if boxed { + buffer.appendInt32(1951948721) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(filterId, buffer: buffer, boxed: false) + maxPosition.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(unreadCount!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(unreadMutedCount!, buffer: buffer, boxed: false)} + break } } @@ -5966,6 +5989,8 @@ public extension Api { return ("updateBotChatInviteRequester", [("peer", peer), ("date", date), ("userId", userId), ("about", about), ("invite", invite), ("qts", qts)]) case .updateMessageReactions(let peer, let msgId, let reactions): return ("updateMessageReactions", [("peer", peer), ("msgId", msgId), ("reactions", reactions)]) + case .updateReadFeed(let flags, let filterId, let maxPosition, let unreadCount, let unreadMutedCount): + return ("updateReadFeed", [("flags", flags), ("filterId", filterId), ("maxPosition", maxPosition), ("unreadCount", unreadCount), ("unreadMutedCount", unreadMutedCount)]) } } @@ -7697,6 +7722,31 @@ public extension Api { return nil } } + public static func parse_updateReadFeed(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Api.FeedPosition? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.FeedPosition + } + var _4: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_4 = reader.readInt32() } + var _5: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_5 = reader.readInt32() } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil + let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.Update.updateReadFeed(flags: _1!, filterId: _2!, maxPosition: _3!, unreadCount: _4, unreadMutedCount: _5) + } + else { + return nil + } + } } public enum PopularContact: TypeConstructorDescription { @@ -11394,6 +11444,50 @@ public extension Api { } } + } + public enum FeedPosition: TypeConstructorDescription { + case feedPosition(date: Int32, peer: Api.Peer, id: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .feedPosition(let date, let peer, let id): + if boxed { + buffer.appendInt32(1348066419) + } + serializeInt32(date, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(id, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .feedPosition(let date, let peer, let id): + return ("feedPosition", [("date", date), ("peer", peer), ("id", id)]) + } + } + + public static func parse_feedPosition(_ reader: BufferReader) -> FeedPosition? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.Peer? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _3: Int32? + _3 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.FeedPosition.feedPosition(date: _1!, peer: _2!, id: _3!) + } + else { + return nil + } + } + } public enum InputGroupCall: TypeConstructorDescription { case inputGroupCall(id: Int64, accessHash: Int64) diff --git a/submodules/TelegramApi/Sources/Api4.swift b/submodules/TelegramApi/Sources/Api4.swift index e9d42a2dd8..5282f9492c 100644 --- a/submodules/TelegramApi/Sources/Api4.swift +++ b/submodules/TelegramApi/Sources/Api4.swift @@ -621,6 +621,104 @@ public struct upload { } } public extension Api { +public struct feed { + public enum FeedMessages: TypeConstructorDescription { + case feedMessagesNotModified + case feedMessages(flags: Int32, maxPosition: Api.FeedPosition?, minPosition: Api.FeedPosition?, readMaxPosition: Api.FeedPosition?, messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .feedMessagesNotModified: + if boxed { + buffer.appendInt32(-619039485) + } + + break + case .feedMessages(let flags, let maxPosition, let minPosition, let readMaxPosition, let messages, let chats, let users): + if boxed { + buffer.appendInt32(-587770695) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {maxPosition!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {minPosition!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {readMaxPosition!.serialize(buffer, true)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .feedMessagesNotModified: + return ("feedMessagesNotModified", []) + case .feedMessages(let flags, let maxPosition, let minPosition, let readMaxPosition, let messages, let chats, let users): + return ("feedMessages", [("flags", flags), ("maxPosition", maxPosition), ("minPosition", minPosition), ("readMaxPosition", readMaxPosition), ("messages", messages), ("chats", chats), ("users", users)]) + } + } + + public static func parse_feedMessagesNotModified(_ reader: BufferReader) -> FeedMessages? { + return Api.feed.FeedMessages.feedMessagesNotModified + } + public static func parse_feedMessages(_ reader: BufferReader) -> FeedMessages? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.FeedPosition? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.FeedPosition + } } + var _3: Api.FeedPosition? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.FeedPosition + } } + var _4: Api.FeedPosition? + if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.FeedPosition + } } + var _5: [Api.Message]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + } + var _6: [Api.Chat]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _7: [Api.User]? + if let _ = reader.readInt32() { + _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { + return Api.feed.FeedMessages.feedMessages(flags: _1!, maxPosition: _2, minPosition: _3, readMaxPosition: _4, messages: _5!, chats: _6!, users: _7!) + } + else { + return nil + } + } + + } +} +} +public extension Api { public struct stickers { public enum SuggestedShortName: TypeConstructorDescription { case suggestedShortName(shortName: String) @@ -6865,6 +6963,43 @@ public extension Api { }) } } + public struct feed { + public static func getFeed(flags: Int32, filterId: Int32, offsetPosition: Api.FeedPosition?, addOffset: Int32, limit: Int32, maxPosition: Api.FeedPosition?, minPosition: Api.FeedPosition?, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2121717715) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(filterId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {offsetPosition!.serialize(buffer, true)} + serializeInt32(addOffset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {maxPosition!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {minPosition!.serialize(buffer, true)} + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "feed.getFeed", parameters: [("flags", flags), ("filterId", filterId), ("offsetPosition", offsetPosition), ("addOffset", addOffset), ("limit", limit), ("maxPosition", maxPosition), ("minPosition", minPosition), ("hash", hash)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.feed.FeedMessages? in + let reader = BufferReader(buffer) + var result: Api.feed.FeedMessages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.feed.FeedMessages + } + return result + }) + } + + public static func readFeed(filterId: Int32, maxPosition: Api.FeedPosition) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1271479809) + serializeInt32(filterId, buffer: buffer, boxed: false) + maxPosition.serialize(buffer, true) + return (FunctionDescription(name: "feed.readFeed", parameters: [("filterId", filterId), ("maxPosition", maxPosition)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } + } public struct stickers { public static func createStickerSet(flags: Int32, userId: Api.InputUser, title: String, shortName: String, thumb: Api.InputDocument?, stickers: [Api.InputStickerSetItem], software: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() diff --git a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift index 0061b1aa72..4304e24eb9 100644 --- a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift +++ b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift @@ -758,7 +758,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { strongSelf.displayNode.view.window?.endEditing(true) strongSelf.present(controller, in: .window(.root)) } else { - let signal = strongSelf.context.sharedContext.messageFromPreloadedChatHistoryViewForLocation(id: id.messageId, location: ChatHistoryLocationInput(content: .InitialSearch(location: .id(id.messageId), count: 60, highlight: true), id: 0), context: strongSelf.context, chatLocation: .peer(id.messageId.peerId), subject: nil, chatLocationContextHolder: Atomic(value: nil), tagMask: MessageTags.music) + let signal = strongSelf.context.sharedContext.messageFromPreloadedChatHistoryViewForLocation(id: id.messageId, location: ChatHistoryLocationInput(content: .InitialSearch(location: .id(id.messageId), count: 60, highlight: true), id: 0), context: strongSelf.context, chatLocation: .peer(id: id.messageId.peerId), subject: nil, chatLocationContextHolder: Atomic(value: nil), tagMask: MessageTags.music) var cancelImpl: (() -> Void)? let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift index 8ff9352cb3..8c6be115ee 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift @@ -1693,7 +1693,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController let context = strongSelf.context strongSelf.controller?.dismiss(completion: { Queue.mainQueue().after(0.3) { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer.id), keepStack: .always, purposefulAction: {}, peekData: nil)) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peer.id), keepStack: .always, purposefulAction: {}, peekData: nil)) } }) @@ -2653,7 +2653,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController let context = strongSelf.context strongSelf.controller?.dismiss(completion: { Queue.mainQueue().justDispatch { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(context.account.peerId), keepStack: .always, purposefulAction: {}, peekData: nil)) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: context.account.peerId), keepStack: .always, purposefulAction: {}, peekData: nil)) } }) diff --git a/submodules/TelegramCore/BUILD b/submodules/TelegramCore/BUILD index 05e4423fc7..4a708502fb 100644 --- a/submodules/TelegramCore/BUILD +++ b/submodules/TelegramCore/BUILD @@ -20,6 +20,7 @@ swift_library( "//submodules/NetworkLogging:NetworkLogging", "//submodules/Reachability:Reachability", "//submodules/ManagedFile:ManagedFile", + "//submodules/Utils/RangeSet:RangeSet", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift index ce8f00148a..404bd63899 100644 --- a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift +++ b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift @@ -183,18 +183,20 @@ private func fetchPoll(account: Account, messageId: MessageId) -> Signal [AdditionalMessageHistoryViewData] { var result = additionalData switch chatLocation { - case let .peer(peerId): - if peerId.namespace == Namespaces.Peer.CloudChannel { - if result.firstIndex(where: { if case .peerChatState = $0 { return true } else { return false } }) == nil { - result.append(.peerChatState(peerId)) - } + case let .peer(peerId): + if peerId.namespace == Namespaces.Peer.CloudChannel { + if result.firstIndex(where: { if case .peerChatState = $0 { return true } else { return false } }) == nil { + result.append(.peerChatState(peerId)) } - case let .external(peerId, _, _): - if peerId.namespace == Namespaces.Peer.CloudChannel { - if result.firstIndex(where: { if case .peerChatState = $0 { return true } else { return false } }) == nil { - result.append(.peerChatState(peerId)) - } + } + case let .thread(peerId, _, _): + if peerId.namespace == Namespaces.Peer.CloudChannel { + if result.firstIndex(where: { if case .peerChatState = $0 { return true } else { return false } }) == nil { + result.append(.peerChatState(peerId)) } + } + case .feed: + break } return result } @@ -1474,7 +1476,7 @@ public final class AccountViewTracker { strongSelf.updatePolls(viewId: viewId, messageIds: pollMessageIds, messages: pollMessageDict) if case let .peer(peerId) = chatLocation, peerId.namespace == Namespaces.Peer.CloudChannel { strongSelf.historyViewStateValidationContexts.updateView(id: viewId, view: next.0) - } else if case let .external(peerId, _, _) = chatLocation, peerId.namespace == Namespaces.Peer.CloudChannel { + } else if case let .thread(peerId, _, _) = chatLocation, peerId.namespace == Namespaces.Peer.CloudChannel { strongSelf.historyViewStateValidationContexts.updateView(id: viewId, view: next.0, location: chatLocation) } } @@ -1485,27 +1487,31 @@ public final class AccountViewTracker { strongSelf.updatePendingWebpages(viewId: viewId, messageIds: [], localWebpages: [:]) strongSelf.updatePolls(viewId: viewId, messageIds: [], messages: [:]) switch chatLocation { - case let .peer(peerId): - if peerId.namespace == Namespaces.Peer.CloudChannel { - strongSelf.historyViewStateValidationContexts.updateView(id: viewId, view: nil) - } - case let .external(peerId, _, _): - if peerId.namespace == Namespaces.Peer.CloudChannel { - strongSelf.historyViewStateValidationContexts.updateView(id: viewId, view: nil, location: chatLocation) - } + case let .peer(peerId): + if peerId.namespace == Namespaces.Peer.CloudChannel { + strongSelf.historyViewStateValidationContexts.updateView(id: viewId, view: nil) + } + case let .thread(peerId, _, _): + if peerId.namespace == Namespaces.Peer.CloudChannel { + strongSelf.historyViewStateValidationContexts.updateView(id: viewId, view: nil, location: chatLocation) + } + case .feed: + break } } } }) - let peerId: PeerId + let peerId: PeerId? switch chatLocation { case let .peer(peerIdValue): peerId = peerIdValue - case let .external(peerIdValue, _, _): + case let .thread(peerIdValue, _, _): peerId = peerIdValue + case .feed: + peerId = nil } - if peerId.namespace == Namespaces.Peer.CloudChannel { + if let peerId = peerId, peerId.namespace == Namespaces.Peer.CloudChannel { return Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { subscriber in let combinedDisposable = MetaDisposable() self.queue.async { diff --git a/submodules/TelegramCore/Sources/State/HistoryViewStateValidation.swift b/submodules/TelegramCore/Sources/State/HistoryViewStateValidation.swift index d132f483be..d23bf17830 100644 --- a/submodules/TelegramCore/Sources/State/HistoryViewStateValidation.swift +++ b/submodules/TelegramCore/Sources/State/HistoryViewStateValidation.swift @@ -156,7 +156,7 @@ final class HistoryViewStateValidationContexts { } } - if let location = location, case let .external(peerId, threadId, _) = location { + if let location = location, case let .thread(peerId, threadId, _) = location { var rangesToInvalidate: [[MessageId]] = [] let addToRange: (MessageId, inout [[MessageId]]) -> Void = { id, ranges in if ranges.isEmpty { diff --git a/submodules/TelegramCore/Sources/State/Serialization.swift b/submodules/TelegramCore/Sources/State/Serialization.swift index a94c7fa7ca..cee780af81 100644 --- a/submodules/TelegramCore/Sources/State/Serialization.swift +++ b/submodules/TelegramCore/Sources/State/Serialization.swift @@ -210,7 +210,7 @@ public class BoxedMessage: NSObject { public class Serialization: NSObject, MTSerialization { public func currentLayer() -> UInt { - return 139 + return 141 } public func parseMessage(_ data: Data!) -> Any! { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/EarliestUnseenPersonalMentionMessage.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/EarliestUnseenPersonalMentionMessage.swift index 9dc6ef1aef..bee21f4b23 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/EarliestUnseenPersonalMentionMessage.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/EarliestUnseenPersonalMentionMessage.swift @@ -10,7 +10,7 @@ public enum EarliestUnseenPersonalMentionMessageResult: Equatable { } func _internal_earliestUnseenPersonalMentionMessage(account: Account, peerId: PeerId) -> Signal { - return account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId), index: .lowerBound, anchorIndex: .lowerBound, count: 4, fixedCombinedReadStates: nil, tagMask: .unseenPersonalMessage, additionalData: [.peerChatState(peerId)]) + return account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: peerId), index: .lowerBound, anchorIndex: .lowerBound, count: 4, fixedCombinedReadStates: nil, tagMask: .unseenPersonalMessage, additionalData: [.peerChatState(peerId)]) |> mapToSignal { view -> Signal in if view.0.isLoading { return .single(.loading) @@ -77,7 +77,7 @@ func _internal_earliestUnseenPersonalMentionMessage(account: Account, peerId: Pe } func _internal_earliestUnseenPersonalReactionMessage(account: Account, peerId: PeerId) -> Signal { - return account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId), index: .lowerBound, anchorIndex: .lowerBound, count: 4, fixedCombinedReadStates: nil, tagMask: .unseenReaction, additionalData: [.peerChatState(peerId)]) + return account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: peerId), index: .lowerBound, anchorIndex: .lowerBound, count: 4, fixedCombinedReadStates: nil, tagMask: .unseenReaction, additionalData: [.peerChatState(peerId)]) |> mapToSignal { view -> Signal in if view.0.isLoading { return .single(.loading) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/FeedHistory.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/FeedHistory.swift new file mode 100644 index 0000000000..18baaf03f0 --- /dev/null +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/FeedHistory.swift @@ -0,0 +1,538 @@ +import Foundation +import Postbox +import SwiftSignalKit +import TelegramApi + +private class FeedHistoryContextImpl { + private let queue: Queue + private let account: Account + private let feedId: Int32 + private let userId: Int64 + + private var currentHole: (MessageHistoryHolesViewEntry, Disposable)? + + struct State: Equatable { + var messageIndices: [MessageIndex] + var holeIndices: [MessageId.Namespace: IndexSet] + } + + let state = Promise() + private var stateValue: State? { + didSet { + if let stateValue = self.stateValue { + if stateValue != oldValue { + self.state.set(.single(stateValue)) + } + } + } + } + + let maxReadOutgoingMessageId = Promise() + private var maxReadOutgoingMessageIdValue: MessageId? { + didSet { + if self.maxReadOutgoingMessageIdValue != oldValue { + self.maxReadOutgoingMessageId.set(.single(self.maxReadOutgoingMessageIdValue)) + } + } + } + + private var maxReadIncomingMessageIdValue: MessageId? + + let unreadCount = Promise() + private var unreadCountValue: Int = 0 { + didSet { + if self.unreadCountValue != oldValue { + self.unreadCount.set(.single(self.unreadCountValue)) + } + } + } + + private var initialStateDisposable: Disposable? + private var holesDisposable: Disposable? + private var readStateDisposable: Disposable? + private var updateInitialStateDisposable: Disposable? + private let readDisposable = MetaDisposable() + + init(queue: Queue, account: Account, feedId: Int32, userId: Int64) { + self.queue = queue + self.account = account + self.feedId = feedId + self.userId = userId + + self.maxReadOutgoingMessageIdValue = nil + self.maxReadOutgoingMessageId.set(.single(self.maxReadOutgoingMessageIdValue)) + + self.maxReadIncomingMessageIdValue = nil + + self.unreadCountValue = 0 + self.unreadCount.set(.single(self.unreadCountValue)) + + self.initialStateDisposable = (account.postbox.transaction { transaction -> State in + return State(messageIndices: [], holeIndices: [Namespaces.Message.Cloud: IndexSet(integersIn: 2 ... 2)]) + } + |> deliverOn(self.queue)).start(next: { [weak self] state in + guard let strongSelf = self else { + return + } + strongSelf.stateValue = state + strongSelf.state.set(.single(state)) + }) + + /*self.updateInitialStateDisposable = (account.network.request(Api.functions.feed.getFeed(flags: 0, filterId: self.feedId, offsetPosition: nil, addOffset: 0, limit: 100, maxPosition: nil, minPosition: nil, hash: 0)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { result -> Signal<[MessageIndex], NoError> in + return account.postbox.transaction { transaction -> [MessageIndex] in + guard let result = result else { + return [] + } + + let messages: [Api.Message] + let chats: [Api.Chat] + let users: [Api.User] + + switch result { + case let .feedMessages(_, _, _, _, apiMessages, apiChats, apiUsers): + messages = apiMessages + chats = apiChats + users = apiUsers + case .feedMessagesNotModified: + messages = [] + users = [] + chats = [] + } + + 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 + } + } + + var storeMessages: [StoreMessage] = [] + + for message in messages { + if let storeMessage = StoreMessage(apiMessage: message, namespace: Namespaces.Message.Cloud) { + storeMessages.append(storeMessage) + } + } + + updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in + return updated + }) + updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences) + + let _ = transaction.addMessages(storeMessages, location: .Random) + + return storeMessages.compactMap({ message in + return message.index + }).sorted() + } + } + |> deliverOn(self.queue)).start(next: { [weak self] indices in + guard let strongSelf = self else { + return + } + assert(indices.sorted() == indices) + strongSelf.stateValue = State(messageIndices: indices, holeIndices: [:]) + })*/ + + let userId = self.userId + self.holesDisposable = (account.postbox.messageHistoryHolesView() + |> map { view -> MessageHistoryHolesViewEntry? in + for entry in view.entries { + if entry.userId == userId { + return entry + } + } + return nil + } + |> distinctUntilChanged + |> deliverOn(self.queue)).start(next: { [weak self] entry in + guard let strongSelf = self else { + return + } + strongSelf.setCurrentHole(entry: entry) + }) + + /*self.readStateDisposable = (account.stateManager.threadReadStateUpdates + |> deliverOn(self.queue)).start(next: { [weak self] (_, outgoing) in + guard let strongSelf = self else { + return + } + if let value = outgoing[data.messageId] { + strongSelf.maxReadOutgoingMessageIdValue = MessageId(peerId: data.messageId.peerId, namespace: Namespaces.Message.Cloud, id: value) + } + }) + + let updateInitialState: Signal = account.postbox.transaction { transaction -> Api.InputPeer? in + return transaction.getPeer(data.messageId.peerId).flatMap(apiInputPeer) + } + |> castError(FetchChannelReplyThreadMessageError.self) + |> mapToSignal { inputPeer -> Signal in + guard let inputPeer = inputPeer else { + return .fail(.generic) + } + + return account.network.request(Api.functions.messages.getDiscussionMessage(peer: inputPeer, msgId: data.messageId.id)) + |> mapError { _ -> FetchChannelReplyThreadMessageError in + return .generic + } + |> mapToSignal { discussionMessage -> Signal in + return account.postbox.transaction { transaction -> Signal in + switch discussionMessage { + case let .discussionMessage(_, messages, maxId, readInboxMaxId, readOutboxMaxId, unreadCount, chats, users): + let parsedMessages = messages.compactMap { message -> StoreMessage? in + StoreMessage(apiMessage: message) + } + + guard let topMessage = parsedMessages.last, let parsedIndex = topMessage.index else { + return .fail(.generic) + } + + var channelMessageId: MessageId? + var replyThreadAttribute: ReplyThreadMessageAttribute? + for attribute in topMessage.attributes { + if let attribute = attribute as? SourceReferenceMessageAttribute { + channelMessageId = attribute.messageId + } else if let attribute = attribute as? ReplyThreadMessageAttribute { + replyThreadAttribute = attribute + } + } + + 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 _ = transaction.addMessages(parsedMessages, location: .Random) + + updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in + return updated + }) + + updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences) + + let resolvedMaxMessage: MessageId? + if let maxId = maxId { + resolvedMaxMessage = MessageId( + peerId: parsedIndex.id.peerId, + namespace: Namespaces.Message.Cloud, + id: maxId + ) + } else { + resolvedMaxMessage = nil + } + + var isChannelPost = false + for attribute in topMessage.attributes { + if let _ = attribute as? SourceReferenceMessageAttribute { + isChannelPost = true + break + } + } + + let maxReadIncomingMessageId = readInboxMaxId.flatMap { readMaxId in + MessageId(peerId: parsedIndex.id.peerId, namespace: Namespaces.Message.Cloud, id: readMaxId) + } + + if let channelMessageId = channelMessageId, let replyThreadAttribute = replyThreadAttribute { + account.viewTracker.updateReplyInfoForMessageId(channelMessageId, info: AccountViewTracker.UpdatedMessageReplyInfo( + timestamp: Int32(CFAbsoluteTimeGetCurrent()), + commentsPeerId: parsedIndex.id.peerId, + maxReadIncomingMessageId: maxReadIncomingMessageId, + maxMessageId: resolvedMaxMessage + )) + + transaction.updateMessage(channelMessageId, update: { currentMessage in + var attributes = currentMessage.attributes + loop: for j in 0 ..< attributes.count { + if let attribute = attributes[j] as? ReplyThreadMessageAttribute { + attributes[j] = ReplyThreadMessageAttribute( + count: replyThreadAttribute.count, + latestUsers: attribute.latestUsers, + commentsPeerId: attribute.commentsPeerId, + maxMessageId: replyThreadAttribute.maxMessageId, + maxReadMessageId: replyThreadAttribute.maxReadMessageId + ) + } + } + return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + }) + } + + return .single(DiscussionMessage( + messageId: parsedIndex.id, + channelMessageId: channelMessageId, + isChannelPost: isChannelPost, + maxMessage: resolvedMaxMessage, + maxReadIncomingMessageId: maxReadIncomingMessageId, + maxReadOutgoingMessageId: readOutboxMaxId.flatMap { readMaxId in + MessageId(peerId: parsedIndex.id.peerId, namespace: Namespaces.Message.Cloud, id: readMaxId) + }, + unreadCount: Int(unreadCount) + )) + } + } + |> castError(FetchChannelReplyThreadMessageError.self) + |> switchToLatest + } + } + + self.updateInitialStateDisposable = (updateInitialState + |> deliverOnMainQueue).start(next: { [weak self] updatedData in + guard let strongSelf = self else { + return + } + if let maxReadOutgoingMessageId = updatedData.maxReadOutgoingMessageId { + if let current = strongSelf.maxReadOutgoingMessageIdValue { + if maxReadOutgoingMessageId > current { + strongSelf.maxReadOutgoingMessageIdValue = maxReadOutgoingMessageId + } + } else { + strongSelf.maxReadOutgoingMessageIdValue = maxReadOutgoingMessageId + } + } + })*/ + } + + deinit { + self.initialStateDisposable?.dispose() + self.holesDisposable?.dispose() + self.readDisposable.dispose() + self.updateInitialStateDisposable?.dispose() + } + + func setCurrentHole(entry: MessageHistoryHolesViewEntry?) { + if self.currentHole?.0 != entry { + self.currentHole?.1.dispose() + if let entry = entry { + self.currentHole = (entry, self.fetchHole(entry: entry).start(next: { [weak self] updatedState in + guard let strongSelf = self else { + return + } + strongSelf.currentHole = nil + strongSelf.stateValue = updatedState + })) + } else { + self.currentHole = nil + } + } + } + + private func fetchHole(entry: MessageHistoryHolesViewEntry) -> Signal { + //feed.getFeed flags:# filter_id:int offset_to_max_read:flags.3?true offset_position:flags.0?FeedPosition add_offset:int limit:int max_position:flags.1?FeedPosition min_position:flags.2?FeedPosition hash:long = messages.FeedMessages; + + let offsetPosition: Api.FeedPosition? + let addOffset: Int32 = 0 + + switch entry.direction { + case let .range(start, end): + if min(start.id, end.id) == 1 && max(start.id, end.id) == Int32.max - 1 { + offsetPosition = nil + } else { + return .never() + } + case let .aroundId(id): + let _ = id + return .never() + } + + var flags: Int32 = 0 + if let _ = offsetPosition { + flags |= 1 << 0 + } + + let account = self.account + let state = self.stateValue + return self.account.network.request(Api.functions.feed.getFeed( + flags: flags, + filterId: self.feedId, + offsetPosition: offsetPosition, + addOffset: addOffset, + limit: 100, + maxPosition: nil, + minPosition: nil, + hash: 0 + )) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { result -> Signal in + return account.postbox.transaction { transaction -> State in + guard let result = result else { + var updatedState = state ?? State(messageIndices: [], holeIndices: [:]) + updatedState.holeIndices = [:] + return updatedState + } + + let messages: [Api.Message] + let chats: [Api.Chat] + let users: [Api.User] + + switch result { + case let .feedMessages(_, _, _, _, apiMessages, apiChats, apiUsers): + messages = apiMessages + chats = apiChats + users = apiUsers + case .feedMessagesNotModified: + messages = [] + users = [] + chats = [] + } + + 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 + } + } + + var storeMessages: [StoreMessage] = [] + + for message in messages { + if let storeMessage = StoreMessage(apiMessage: message, namespace: Namespaces.Message.Cloud) { + storeMessages.append(storeMessage) + } + } + + updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in + return updated + }) + updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences) + + let _ = transaction.addMessages(storeMessages, location: .Random) + + var updatedState = state ?? State(messageIndices: [], holeIndices: [:]) + var currentSet = Set(updatedState.messageIndices) + + for index in storeMessages.compactMap(\.index) { + if !currentSet.contains(index) { + currentSet.insert(index) + } + updatedState.messageIndices.append(index) + } + + updatedState.messageIndices.sort() + + updatedState.holeIndices = [:] + return updatedState + } + } + } + + func applyMaxReadIndex(messageIndex: MessageIndex) { + } +} + +public class FeedHistoryContext { + fileprivate final class GuardReference { + private let deallocated: () -> Void + + init(deallocated: @escaping () -> Void) { + self.deallocated = deallocated + } + + deinit { + self.deallocated() + } + } + + private let queue = Queue() + private let impl: QueueLocalObject + + private let userId: Int64 = Int64.random(in: 0 ..< Int64.max) + + public var state: Signal { + let userId = self.userId + + return Signal { subscriber in + let disposable = MetaDisposable() + + self.impl.with { impl in + let stateDisposable = impl.state.get().start(next: { state in + subscriber.putNext(MessageHistoryViewExternalInput( + content: .messages(indices: state.messageIndices, holes: state.holeIndices, userId: userId), + maxReadIncomingMessageId: nil, + maxReadOutgoingMessageId: nil + )) + }) + disposable.set(stateDisposable) + } + + return disposable + } + } + + public var maxReadOutgoingMessageId: Signal { + return Signal { subscriber in + let disposable = MetaDisposable() + + self.impl.with { impl in + disposable.set(impl.maxReadOutgoingMessageId.get().start(next: { value in + subscriber.putNext(value) + })) + } + + return disposable + } + } + + public var unreadCount: Signal { + return Signal { subscriber in + let disposable = MetaDisposable() + + self.impl.with { impl in + disposable.set(impl.unreadCount.get().start(next: { value in + subscriber.putNext(value) + })) + } + + return disposable + } + } + + public init(account: Account, feedId: Int32) { + let queue = self.queue + let userId = self.userId + self.impl = QueueLocalObject(queue: queue, generate: { + return FeedHistoryContextImpl(queue: queue, account: account, feedId: feedId, userId: userId) + }) + } + + public func applyMaxReadIndex(messageIndex: MessageIndex) { + self.impl.with { impl in + impl.applyMaxReadIndex(messageIndex: messageIndex) + } + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/PeerLiveLocationsContext.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/PeerLiveLocationsContext.swift index 782815e3b9..744f359985 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/PeerLiveLocationsContext.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/PeerLiveLocationsContext.swift @@ -4,7 +4,7 @@ import SwiftSignalKit import TelegramApi func _internal_topPeerActiveLiveLocationMessages(viewTracker: AccountViewTracker, accountPeerId: PeerId, peerId: PeerId) -> Signal<(Peer?, [Message]), NoError> { - return viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId), index: .upperBound, anchorIndex: .upperBound, count: 50, fixedCombinedReadStates: nil, tagMask: .liveLocation, orderStatistics: [], additionalData: [.peer(accountPeerId)]) + return viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: peerId), index: .upperBound, anchorIndex: .upperBound, count: 50, fixedCombinedReadStates: nil, tagMask: .liveLocation, orderStatistics: [], additionalData: [.peer(accountPeerId)]) |> map { (view, _, _) -> (Peer?, [Message]) in var accountPeer: Peer? for entry in view.additionalData { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift index 5c00200927..c45a9a47ee 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift @@ -477,11 +477,9 @@ public class ReplyThreadHistoryContext { self.impl.with { impl in let stateDisposable = impl.state.get().start(next: { state in subscriber.putNext(MessageHistoryViewExternalInput( - peerId: state.messageId.peerId, - threadId: makeMessageThreadId(state.messageId), + content: .thread(peerId: state.messageId.peerId, id: makeMessageThreadId(state.messageId), holes: state.holeIndices), maxReadIncomingMessageId: state.maxReadIncomingMessageId, - maxReadOutgoingMessageId: state.maxReadOutgoingMessageId, - holes: state.holeIndices + maxReadOutgoingMessageId: state.maxReadOutgoingMessageId )) }) disposable.set(stateDisposable) @@ -804,13 +802,15 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa let testView = transaction.getMessagesHistoryViewState( input: .external(MessageHistoryViewExternalInput( - peerId: commentsPeerId, - threadId: makeMessageThreadId(threadMessageId), + content: .thread( + peerId: commentsPeerId, + id: makeMessageThreadId(threadMessageId), + holes: [ + Namespaces.Message.Cloud: holes + ] + ), maxReadIncomingMessageId: nil, - maxReadOutgoingMessageId: nil, - holes: [ - Namespaces.Message.Cloud: holes - ] + maxReadOutgoingMessageId: nil )), ignoreMessagesInTimestampRange: nil, count: 40, diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift index d4c1c068b0..66a1a5f027 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift @@ -188,7 +188,7 @@ public final class SparseMessageList { #else*/ count = 200 //#endif - self.topItemsDisposable.set((self.account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId), anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: count, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tagMask: self.messageTag, appendMessagesFromTheSameGroup: false, namespaces: .not(Set(Namespaces.Message.allScheduled)), orderStatistics: []) + self.topItemsDisposable.set((self.account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId: peerId), anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: count, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tagMask: self.messageTag, appendMessagesFromTheSameGroup: false, namespaces: .not(Set(Namespaces.Message.allScheduled)), orderStatistics: []) |> deliverOn(self.queue)).start(next: { [weak self] view, updateType, _ in guard let strongSelf = self else { return diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift index b31a717fab..88ecbcc6f0 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift @@ -216,7 +216,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee } switch fullUser { - case let .userFull(_, _, _, _, _, userFullNotifySettings, _, _, _, _, _, _, _): + 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 @@ -234,7 +234,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee previous = CachedUserData() } switch fullUser { - case let .userFull(userFullFlags, _, userFullAbout, userFullSettings, _, _, userFullBotInfo, userFullPinnedMsgId, userFullCommonChatsCount, _, userFullTtlPeriod, userFullThemeEmoticon, _): + case let .userFull(userFullFlags, _, userFullAbout, userFullSettings, _, _, userFullBotInfo, userFullPinnedMsgId, userFullCommonChatsCount, _, userFullTtlPeriod, userFullThemeEmoticon, _, _, _): let botInfo = userFullBotInfo.flatMap(BotInfo.init(apiBotInfo:)) let isBlocked = (userFullFlags & (1 << 0)) != 0 let voiceCallsAvailable = (userFullFlags & (1 << 4)) != 0 diff --git a/submodules/TelegramUI/Sources/AccountContext.swift b/submodules/TelegramUI/Sources/AccountContext.swift index 70d221e193..d325e1e0e1 100644 --- a/submodules/TelegramUI/Sources/AccountContext.swift +++ b/submodules/TelegramUI/Sources/AccountContext.swift @@ -278,10 +278,13 @@ public final class AccountContextImpl: AccountContext { public func chatLocationInput(for location: ChatLocation, contextHolder: Atomic) -> ChatLocationInput { switch location { case let .peer(peerId): - return .peer(peerId) + return .peer(peerId: peerId) case let .replyThread(data): let context = chatLocationContext(holder: contextHolder, account: self.account, data: data) - return .external(data.messageId.peerId, makeMessageThreadId(data.messageId), context.state) + return .thread(peerId: data.messageId.peerId, threadId: makeMessageThreadId(data.messageId), data: context.state) + case let .feed(id): + let context = chatLocationContext(holder: contextHolder, account: self.account, feedId: id) + return .feed(id: id, data: context.state) } } @@ -292,6 +295,9 @@ public final class AccountContextImpl: AccountContext { case let .replyThread(data): let context = chatLocationContext(holder: contextHolder, account: self.account, data: data) return context.maxReadOutgoingMessageId + case let .feed(id): + let context = chatLocationContext(holder: contextHolder, account: self.account, feedId: id) + return context.maxReadOutgoingMessageId } } @@ -314,6 +320,9 @@ public final class AccountContextImpl: AccountContext { case let .replyThread(data): let context = chatLocationContext(holder: contextHolder, account: self.account, data: data) return context.unreadCount + case let .feed(id): + let context = chatLocationContext(holder: contextHolder, account: self.account, feedId: id) + return context.unreadCount } } @@ -324,6 +333,9 @@ public final class AccountContextImpl: AccountContext { case let .replyThread(data): let context = chatLocationContext(holder: contextHolder, account: self.account, data: data) context.applyMaxReadIndex(messageIndex: messageIndex) + case let .feed(id): + let context = chatLocationContext(holder: contextHolder, account: self.account, feedId: id) + context.applyMaxReadIndex(messageIndex: messageIndex) } } @@ -448,16 +460,27 @@ public final class AccountContextImpl: AccountContext { private func chatLocationContext(holder: Atomic, account: Account, data: ChatReplyThreadMessage) -> ReplyThreadHistoryContext { let holder = holder.modify { current in - if let current = current as? ChatLocationContextHolderImpl { + if let current = current as? ChatLocationReplyContextHolderImpl { return current } else { - return ChatLocationContextHolderImpl(account: account, data: data) + return ChatLocationReplyContextHolderImpl(account: account, data: data) } - } as! ChatLocationContextHolderImpl + } as! ChatLocationReplyContextHolderImpl return holder.context } -private final class ChatLocationContextHolderImpl: ChatLocationContextHolder { +private func chatLocationContext(holder: Atomic, account: Account, feedId: Int32) -> FeedHistoryContext { + let holder = holder.modify { current in + if let current = current as? ChatLocationFeedContextHolderImpl { + return current + } else { + return ChatLocationFeedContextHolderImpl(account: account, feedId: feedId) + } + } as! ChatLocationFeedContextHolderImpl + return holder.context +} + +private final class ChatLocationReplyContextHolderImpl: ChatLocationContextHolder { let context: ReplyThreadHistoryContext init(account: Account, data: ChatReplyThreadMessage) { @@ -465,6 +488,14 @@ private final class ChatLocationContextHolderImpl: ChatLocationContextHolder { } } +private final class ChatLocationFeedContextHolderImpl: ChatLocationContextHolder { + let context: FeedHistoryContext + + init(account: Account, feedId: Int32) { + self.context = FeedHistoryContext(account: account, feedId: feedId) + } +} + func getAppConfiguration(transaction: Transaction) -> AppConfiguration { let appConfiguration: AppConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration)?.get(AppConfiguration.self) ?? AppConfiguration.defaultValue return appConfiguration diff --git a/submodules/TelegramUI/Sources/ApplicationContext.swift b/submodules/TelegramUI/Sources/ApplicationContext.swift index ed722fc2c8..ce25113434 100644 --- a/submodules/TelegramUI/Sources/ApplicationContext.swift +++ b/submodules/TelegramUI/Sources/ApplicationContext.swift @@ -414,16 +414,14 @@ final class AuthorizedApplicationContext { } if !processed { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: strongSelf.rootController, context: strongSelf.context, chatLocation: .peer(firstMessage.id.peerId))) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: strongSelf.rootController, context: strongSelf.context, chatLocation: .peer(id: firstMessage.id.peerId))) } } return false }, expandAction: { expandData in if let strongSelf = self { - let chatController = ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(firstMessage.id.peerId), mode: .overlay(strongSelf.rootController)) - //chatController.navigation_setNavigationController(strongSelf.rootController) + let chatController = ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(id: firstMessage.id.peerId), mode: .overlay(strongSelf.rootController)) chatController.presentationArguments = ChatControllerOverlayPresentationData(expandData: expandData()) - //strongSelf.rootController.pushViewController(chatController) (strongSelf.rootController.viewControllers.last as? ViewController)?.present(chatController, in: .window(.root), with: ChatControllerOverlayPresentationData(expandData: expandData())) } })) @@ -466,7 +464,7 @@ final class AuthorizedApplicationContext { if let strongSelf = self, let botName = botName { strongSelf.termsOfServiceProceedToBotDisposable.set((strongSelf.context.engine.peers.resolvePeerByName(name: botName, ageLimit: 10) |> take(1) |> deliverOnMainQueue).start(next: { peer in if let strongSelf = self, let peer = peer { - self?.rootController.pushViewController(ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(peer.id))) + self?.rootController.pushViewController(ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(id: peer.id))) } })) } @@ -758,7 +756,7 @@ final class AuthorizedApplicationContext { } let navigateToMessage = { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: strongSelf.rootController, context: strongSelf.context, chatLocation: .peer(messageId.peerId), subject: .message(id: .id(messageId), highlight: true, timecode: nil))) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: strongSelf.rootController, context: strongSelf.context, chatLocation: .peer(id: messageId.peerId), subject: .message(id: .id(messageId), highlight: true, timecode: nil))) } if chatIsVisible { @@ -837,7 +835,7 @@ final class AuthorizedApplicationContext { if visiblePeerId != peerId || messageId != nil { if self.rootController.rootTabController != nil { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: self.rootController, context: self.context, chatLocation: .peer(peerId), subject: messageId.flatMap { .message(id: .id($0), highlight: true, timecode: nil) }, activateInput: activateInput)) + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: self.rootController, context: self.context, chatLocation: .peer(id: peerId), subject: messageId.flatMap { .message(id: .id($0), highlight: true, timecode: nil) }, activateInput: activateInput)) } else { self.scheduledOpenChatWithPeerId = (peerId, messageId, activateInput) } diff --git a/submodules/TelegramUI/Sources/AttachmentFileController.swift b/submodules/TelegramUI/Sources/AttachmentFileController.swift index dc7d2b2917..494dfcbe5c 100644 --- a/submodules/TelegramUI/Sources/AttachmentFileController.swift +++ b/submodules/TelegramUI/Sources/AttachmentFileController.swift @@ -131,7 +131,7 @@ private enum AttachmentFileEntry: ItemListNodeEntry { let dateTimeFormat = arguments.context.sharedContext.currentPresentationData.with({$0}).dateTimeFormat let chatPresentationData = ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: .color(0)), fontSize: presentationData.fontSize, strings: presentationData.strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, disableAnimations: false, largeEmoji: false, chatBubbleCorners: PresentationChatBubbleCorners(mainRadius: 0, auxiliaryRadius: 0, mergeBubbleCorners: false)) - return ListMessageItem(presentationData: chatPresentationData, context: arguments.context, chatLocation: .peer(PeerId(0)), interaction: interaction, message: message, selection: .none, displayHeader: false, displayFileInfo: false, displayBackground: true, style: .blocks) + return ListMessageItem(presentationData: chatPresentationData, context: arguments.context, chatLocation: .peer(id: PeerId(0)), interaction: interaction, message: message, selection: .none, displayHeader: false, displayFileInfo: false, displayBackground: true, style: .blocks) } } } diff --git a/submodules/TelegramUI/Sources/AttachmentFileSearchItem.swift b/submodules/TelegramUI/Sources/AttachmentFileSearchItem.swift index ff098e5978..5c124c065b 100644 --- a/submodules/TelegramUI/Sources/AttachmentFileSearchItem.swift +++ b/submodules/TelegramUI/Sources/AttachmentFileSearchItem.swift @@ -269,7 +269,7 @@ private final class AttachmentFileSearchEntry: Comparable, Identifiable { interaction.send(message) return false }, openMessageContextMenu: { _, _, _, _, _ in }, toggleMessagesSelection: { _, _ in }, openUrl: { _, _, _, _ in }, openInstantPage: { _, _ in }, longTap: { _, _ in }, getHiddenMedia: { return [:] }) - return ListMessageItem(presentationData: ChatPresentationData(presentationData: interaction.context.sharedContext.currentPresentationData.with({$0})), context: interaction.context, chatLocation: .peer(PeerId(0)), interaction: itemInteraction, message: message, selection: .none, displayHeader: true, displayFileInfo: false, displayBackground: true, style: .plain) + return ListMessageItem(presentationData: ChatPresentationData(presentationData: interaction.context.sharedContext.currentPresentationData.with({$0})), context: interaction.context, chatLocation: .peer(id: PeerId(0)), interaction: itemInteraction, message: message, selection: .none, displayHeader: true, displayFileInfo: false, displayBackground: true, style: .plain) } } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 796bffaece..4b23437e7a 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -95,7 +95,7 @@ public final class ChatControllerOverlayPresentationData { private enum ChatLocationInfoData { case peer(Promise) case replyThread(Promise) - //case group(Promise) + case feed } private enum ChatRecordingActivity { @@ -526,28 +526,32 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var groupCallPanelSource: GroupCallPanelSource switch chatLocation { - case let .peer(peerId): - locationBroadcastPanelSource = .peer(peerId) - switch subject { - case .message, .none: - groupCallPanelSource = .peer(peerId) - default: - groupCallPanelSource = .none - } - self.chatLocationInfoData = .peer(Promise()) - case let .replyThread(replyThreadMessage): - locationBroadcastPanelSource = .none + case let .peer(peerId): + locationBroadcastPanelSource = .peer(peerId) + switch subject { + case .message, .none: + groupCallPanelSource = .peer(peerId) + default: groupCallPanelSource = .none - let promise = Promise() - let key = PostboxViewKey.messages([replyThreadMessage.messageId]) - promise.set(context.account.postbox.combinedView(keys: [key]) - |> map { views -> Message? in - guard let view = views.views[key] as? MessagesView else { - return nil - } - return view.messages[replyThreadMessage.messageId] - }) - self.chatLocationInfoData = .replyThread(promise) + } + self.chatLocationInfoData = .peer(Promise()) + case let .replyThread(replyThreadMessage): + locationBroadcastPanelSource = .none + groupCallPanelSource = .none + let promise = Promise() + let key = PostboxViewKey.messages([replyThreadMessage.messageId]) + promise.set(context.account.postbox.combinedView(keys: [key]) + |> map { views -> Message? in + guard let view = views.views[key] as? MessagesView else { + return nil + } + return view.messages[replyThreadMessage.messageId] + }) + self.chatLocationInfoData = .replyThread(promise) + case .feed: + locationBroadcastPanelSource = .none + groupCallPanelSource = .none + self.chatLocationInfoData = .feed } self.presentationData = context.sharedContext.currentPresentationData.with { $0 } @@ -770,7 +774,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var openChatLocation = strongSelf.chatLocation if case let .replyThread(replyThreadMessage) = openChatLocation { if message.threadId != makeMessageThreadId(replyThreadMessage.messageId) { - openChatLocation = .peer(message.id.peerId) + openChatLocation = .peer(id: message.id.peerId) } } @@ -1923,6 +1927,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } case .replyThread: postAsReply = true + case .feed: + postAsReply = true } if let messageId = messageId, let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(messageId) { @@ -2260,7 +2266,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G actionSheet?.dismissAnimated() if let strongSelf = self { let peerSignal: Signal - peerSignal = strongSelf.context.account.postbox.loadedPeerWithId(strongSelf.chatLocation.peerId) + guard let peerId = strongSelf.chatLocation.peerId else { + return + } + peerSignal = strongSelf.context.account.postbox.loadedPeerWithId(peerId) |> map(Optional.init) let _ = (peerSignal |> deliverOnMainQueue).start(next: { peer in @@ -2506,6 +2515,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case let .replyThread(replyThreadMessage): let peerId = replyThreadMessage.messageId.peerId strongSelf.navigateToMessage(from: nil, to: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: 0), timestamp: timestamp - Int32(NSTimeZone.local.secondsFromGMT()))), scrollPosition: .bottom(0.0), rememberInStack: false, animated: true, completion: nil) + case .feed: + //TODO:implement + break } }, requestRedeliveryOfFailedMessages: { [weak self] id in guard let strongSelf = self else { @@ -3305,7 +3317,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.navigationItem.titleView = self.chatTitleView self.chatTitleView?.pressed = { [weak self] in if let strongSelf = self { - if strongSelf.chatLocation == .peer(strongSelf.context.account.peerId) { + if strongSelf.chatLocation == .peer(id: strongSelf.context.account.peerId) { if let peer = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer, let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: true, requestsContext: nil) { strongSelf.effectiveNavigationController?.pushViewController(infoController) } @@ -3341,59 +3353,61 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let chatInfoButtonItem: UIBarButtonItem switch chatLocation { - case .peer, .replyThread: - let avatarNode = ChatAvatarNavigationNode() - avatarNode.contextAction = { [weak self] node, gesture in - guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer, peer.smallProfileImage != nil else { - return - } - let galleryController = AvatarGalleryController(context: strongSelf.context, peer: peer, remoteEntries: nil, replaceRootController: { controller, ready in - }, synchronousLoad: true) - galleryController.setHintWillBePresentedInPreviewingContext(true) - - let items: Signal<[ContextMenuItem], NoError> = context.account.postbox.transaction { transaction -> [ContextMenuItem] in - var items: [ContextMenuItem] = [ - .action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_LinkDialogOpen, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.actionSheet.primaryTextColor) - }, action: { _, f in - f(.dismissWithoutContent) - self?.navigationButtonAction(.openChatInfo(expandAvatar: true)) - })) - ] - if let cachedData = transaction.getPeerCachedData(peerId: peer.id) as? CachedChannelData, cachedData.flags.contains(.canViewStats) { - items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChannelInfo_Stats, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Statistics"), color: theme.actionSheet.primaryTextColor) - }, action: { _, f in - f(.dismissWithoutContent) - guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer else { - return - } - strongSelf.view.endEditing(true) - - let statsController: ViewController - if let channel = peer as? TelegramChannel, case .group = channel.info { - statsController = groupStatsController(context: context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: peer.id, cachedPeerData: cachedData) - } else { - statsController = channelStatsController(context: context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: peer.id, cachedPeerData: cachedData) - } - strongSelf.push(statsController) - }))) - } - items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_Search, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Search"), color: theme.actionSheet.primaryTextColor) + case .peer, .replyThread: + let avatarNode = ChatAvatarNavigationNode() + avatarNode.contextAction = { [weak self] node, gesture in + guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer, peer.smallProfileImage != nil else { + return + } + let galleryController = AvatarGalleryController(context: strongSelf.context, peer: peer, remoteEntries: nil, replaceRootController: { controller, ready in + }, synchronousLoad: true) + galleryController.setHintWillBePresentedInPreviewingContext(true) + + let items: Signal<[ContextMenuItem], NoError> = context.account.postbox.transaction { transaction -> [ContextMenuItem] in + var items: [ContextMenuItem] = [ + .action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_LinkDialogOpen, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in f(.dismissWithoutContent) - self?.interfaceInteraction?.beginMessageSearch(.everything, "") + self?.navigationButtonAction(.openChatInfo(expandAvatar: true)) + })) + ] + if let cachedData = transaction.getPeerCachedData(peerId: peer.id) as? CachedChannelData, cachedData.flags.contains(.canViewStats) { + items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChannelInfo_Stats, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Statistics"), color: theme.actionSheet.primaryTextColor) + }, action: { _, f in + f(.dismissWithoutContent) + guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer else { + return + } + strongSelf.view.endEditing(true) + + let statsController: ViewController + if let channel = peer as? TelegramChannel, case .group = channel.info { + statsController = groupStatsController(context: context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: peer.id, cachedPeerData: cachedData) + } else { + statsController = channelStatsController(context: context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: peer.id, cachedPeerData: cachedData) + } + strongSelf.push(statsController) }))) - return items } - - strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts() - - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node, passthroughTouches: false)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) - strongSelf.presentInGlobalOverlay(contextController) + items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_Search, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Search"), color: theme.actionSheet.primaryTextColor) + }, action: { _, f in + f(.dismissWithoutContent) + self?.interfaceInteraction?.beginMessageSearch(.everything, "") + }))) + return items } - chatInfoButtonItem = UIBarButtonItem(customDisplayNode: avatarNode)! + + strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts() + + let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node, passthroughTouches: false)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) + strongSelf.presentInGlobalOverlay(contextController) + } + chatInfoButtonItem = UIBarButtonItem(customDisplayNode: avatarNode)! + case .feed: + chatInfoButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil) } chatInfoButtonItem.target = self chatInfoButtonItem.action = #selector(self.rightNavigationButtonAction) @@ -3412,11 +3426,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }) - let chatLocationPeerId: PeerId = chatLocation.peerId + let chatLocationPeerId: PeerId? = chatLocation.peerId do { let peerId = chatLocationPeerId - if case let .peer(peerView) = self.chatLocationInfoData { + if case let .peer(peerView) = self.chatLocationInfoData, let peerId = peerId { peerView.set(context.account.viewTracker.peerView(peerId)) var onlineMemberCount: Signal = .single(nil) var hasScheduledMessages: Signal = .single(false) @@ -3929,7 +3943,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let navigationController = strongSelf.effectiveNavigationController { var viewControllers = navigationController.viewControllers if let index = viewControllers.firstIndex(where: { $0 === strongSelf }) { - viewControllers[index] = ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(upgradedToPeerId)) + viewControllers[index] = ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(id: upgradedToPeerId)) navigationController.setViewControllers(viewControllers, animated: false) } } @@ -3938,7 +3952,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } })) - } else if case let .replyThread(messagePromise) = self.chatLocationInfoData { + } else if case let .replyThread(messagePromise) = self.chatLocationInfoData, let peerId = peerId { self.reportIrrelvantGeoNoticePromise.set(.single(nil)) let replyThreadType: ChatTitleContent.ReplyThreadType @@ -3951,6 +3965,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } else { replyThreadType = .replies } + case .feed: + replyThreadType = .replies } let peerView = context.account.viewTracker.peerView(peerId) @@ -4137,6 +4153,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } })) + } else if case .feed = self.chatLocationInfoData { + self.reportIrrelvantGeoNoticePromise.set(.single(nil)) + self.titleDisposable.set(nil) + + //TODO:localize + self.chatTitleView?.titleContent = .custom("Feed", nil, false) + + if !self.didSetChatLocationInfoReady { + self.didSetChatLocationInfoReady = true + self._chatLocationInfoReady.set(.single(true)) + } } } @@ -4288,52 +4315,56 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.startBot(botStart.payload) } - let activitySpace: PeerActivitySpace + let activitySpace: PeerActivitySpace? switch self.chatLocation { case let .peer(peerId): activitySpace = PeerActivitySpace(peerId: peerId, category: .global) case let .replyThread(replyThreadMessage): activitySpace = PeerActivitySpace(peerId: replyThreadMessage.messageId.peerId, category: .thread(makeMessageThreadId(replyThreadMessage.messageId))) + case .feed: + activitySpace = nil } - self.inputActivityDisposable = (self.typingActivityPromise.get() - |> deliverOnMainQueue).start(next: { [weak self] value in - if let strongSelf = self, strongSelf.presentationInterfaceState.interfaceState.editMessage == nil && strongSelf.presentationInterfaceState.subject != .scheduledMessages && strongSelf.presentationInterfaceState.currentSendAsPeerId == nil { - strongSelf.context.account.updateLocalInputActivity(peerId: activitySpace, activity: .typingText, isPresent: value) - } - }) + if let activitySpace = activitySpace { + self.inputActivityDisposable = (self.typingActivityPromise.get() + |> deliverOnMainQueue).start(next: { [weak self] value in + if let strongSelf = self, strongSelf.presentationInterfaceState.interfaceState.editMessage == nil && strongSelf.presentationInterfaceState.subject != .scheduledMessages && strongSelf.presentationInterfaceState.currentSendAsPeerId == nil { + strongSelf.context.account.updateLocalInputActivity(peerId: activitySpace, activity: .typingText, isPresent: value) + } + }) - self.choosingStickerActivityDisposable = (self.choosingStickerActivityPromise.get() - |> mapToSignal { value -> Signal in - if value { - return .single(true) - } else { - return .single(false) |> delay(2.0, queue: Queue.mainQueue()) - } - } - |> deliverOnMainQueue).start(next: { [weak self] value in - if let strongSelf = self, strongSelf.presentationInterfaceState.interfaceState.editMessage == nil && strongSelf.presentationInterfaceState.subject != .scheduledMessages && strongSelf.presentationInterfaceState.currentSendAsPeerId == nil { + self.choosingStickerActivityDisposable = (self.choosingStickerActivityPromise.get() + |> mapToSignal { value -> Signal in if value { - strongSelf.context.account.updateLocalInputActivity(peerId: activitySpace, activity: .typingText, isPresent: false) - } - strongSelf.context.account.updateLocalInputActivity(peerId: activitySpace, activity: .choosingSticker, isPresent: value) - } - }) - - self.recordingActivityDisposable = (self.recordingActivityPromise.get() - |> deliverOnMainQueue).start(next: { [weak self] value in - if let strongSelf = self, strongSelf.presentationInterfaceState.interfaceState.editMessage == nil && strongSelf.presentationInterfaceState.subject != .scheduledMessages && strongSelf.presentationInterfaceState.currentSendAsPeerId == nil { - strongSelf.acquiredRecordingActivityDisposable?.dispose() - switch value { - case .voice: - strongSelf.acquiredRecordingActivityDisposable = strongSelf.context.account.acquireLocalInputActivity(peerId: activitySpace, activity: .recordingVoice) - case .instantVideo: - strongSelf.acquiredRecordingActivityDisposable = strongSelf.context.account.acquireLocalInputActivity(peerId: activitySpace, activity: .recordingInstantVideo) - case .none: - strongSelf.acquiredRecordingActivityDisposable = nil + return .single(true) + } else { + return .single(false) |> delay(2.0, queue: Queue.mainQueue()) } } - }) + |> deliverOnMainQueue).start(next: { [weak self] value in + if let strongSelf = self, strongSelf.presentationInterfaceState.interfaceState.editMessage == nil && strongSelf.presentationInterfaceState.subject != .scheduledMessages && strongSelf.presentationInterfaceState.currentSendAsPeerId == nil { + if value { + strongSelf.context.account.updateLocalInputActivity(peerId: activitySpace, activity: .typingText, isPresent: false) + } + strongSelf.context.account.updateLocalInputActivity(peerId: activitySpace, activity: .choosingSticker, isPresent: value) + } + }) + + self.recordingActivityDisposable = (self.recordingActivityPromise.get() + |> deliverOnMainQueue).start(next: { [weak self] value in + if let strongSelf = self, strongSelf.presentationInterfaceState.interfaceState.editMessage == nil && strongSelf.presentationInterfaceState.subject != .scheduledMessages && strongSelf.presentationInterfaceState.currentSendAsPeerId == nil { + strongSelf.acquiredRecordingActivityDisposable?.dispose() + switch value { + case .voice: + strongSelf.acquiredRecordingActivityDisposable = strongSelf.context.account.acquireLocalInputActivity(peerId: activitySpace, activity: .recordingVoice) + case .instantVideo: + strongSelf.acquiredRecordingActivityDisposable = strongSelf.context.account.acquireLocalInputActivity(peerId: activitySpace, activity: .recordingInstantVideo) + case .none: + strongSelf.acquiredRecordingActivityDisposable = nil + } + } + }) + } let themeEmoticon: Signal = self.cachedDataPromise.get() |> map { cachedData -> String? in @@ -4739,7 +4770,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G location = .Initial(count: count) } - return (chatHistoryViewForLocation(ChatHistoryLocationInput(content: location, id: 0), ignoreMessagesInTimestampRange: nil, context: context, chatLocation: .peer(peerId), chatLocationContextHolder: Atomic(value: nil), scheduled: false, fixedCombinedReadStates: nil, tagMask: MessageTags.pinned, appendMessagesFromTheSameGroup: false, additionalData: [], orderStatistics: .combinedLocation) + return (chatHistoryViewForLocation(ChatHistoryLocationInput(content: location, id: 0), ignoreMessagesInTimestampRange: nil, context: context, chatLocation: .peer(id: peerId), chatLocationContextHolder: Atomic(value: nil), scheduled: false, fixedCombinedReadStates: nil, tagMask: MessageTags.pinned, appendMessagesFromTheSameGroup: false, additionalData: [], orderStatistics: .combinedLocation) |> castError(Bool.self) |> mapToSignal { update -> Signal in switch update { @@ -4959,7 +4990,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return message } |> distinctUntilChanged - case .replyThread: + case .replyThread, .feed: return .single(nil) } return topPinnedMessage @@ -5026,38 +5057,40 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return FoundPeer(peer: peer, subscribers: nil) } - self.sendAsPeersDisposable = (combineLatest(queue: Queue.mainQueue(), currentAccountPeer, self.context.account.postbox.peerView(id: self.chatLocation.peerId), self.context.engine.peers.sendAsAvailablePeers(peerId: self.chatLocation.peerId))) - .start(next: { [weak self] currentAccountPeer, peerView, peers in - guard let strongSelf = self else { - return - } - var allPeers: [FoundPeer]? - if !peers.isEmpty { - if let channel = peerViewMainPeer(peerView) as? TelegramChannel, case .group = channel.info, channel.hasPermission(.canBeAnonymous) { - allPeers = peers - - var hasAnonymousPeer = false - for peer in peers { - if peer.peer.id == channel.id { - hasAnonymousPeer = true - break - } - } - if !hasAnonymousPeer { - allPeers?.insert(FoundPeer(peer: channel, subscribers: 0), at: 0) - } - } else { - allPeers = peers.filter { $0.peer.id != peerViewMainPeer(peerView)?.id } - allPeers?.insert(currentAccountPeer, at: 0) + if let peerId = self.chatLocation.peerId { + self.sendAsPeersDisposable = (combineLatest(queue: Queue.mainQueue(), currentAccountPeer, self.context.account.postbox.peerView(id: peerId), self.context.engine.peers.sendAsAvailablePeers(peerId: peerId))) + .start(next: { [weak self] currentAccountPeer, peerView, peers in + guard let strongSelf = self else { + return } - } - if allPeers?.count == 1 { - allPeers = nil - } - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { - return $0.updatedSendAsPeers(allPeers) + var allPeers: [FoundPeer]? + if !peers.isEmpty { + if let channel = peerViewMainPeer(peerView) as? TelegramChannel, case .group = channel.info, channel.hasPermission(.canBeAnonymous) { + allPeers = peers + + var hasAnonymousPeer = false + for peer in peers { + if peer.peer.id == channel.id { + hasAnonymousPeer = true + break + } + } + if !hasAnonymousPeer { + allPeers?.insert(FoundPeer(peer: channel, subscribers: 0), at: 0) + } + } else { + allPeers = peers.filter { $0.peer.id != peerViewMainPeer(peerView)?.id } + allPeers?.insert(currentAccountPeer, at: 0) + } + } + if allPeers?.count == 1 { + allPeers = nil + } + strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { + return $0.updatedSendAsPeers(allPeers) + }) }) - }) + } let initialData = self.chatDisplayNode.historyNode.initialData |> take(1) @@ -5204,237 +5237,255 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let hasPendingMessages: Signal let chatLocationPeerId = self.chatLocation.peerId - hasPendingMessages = self.context.account.pendingMessageManager.hasPendingMessages - |> mapToSignal { peerIds -> Signal in - let value = peerIds.contains(chatLocationPeerId) - if value { - return .single(true) - } else { - return .single(false) - |> delay(0.1, queue: .mainQueue()) + + if let chatLocationPeerId = chatLocationPeerId { + hasPendingMessages = self.context.account.pendingMessageManager.hasPendingMessages + |> mapToSignal { peerIds -> Signal in + let value = peerIds.contains(chatLocationPeerId) + if value { + return .single(true) + } else { + return .single(false) + |> delay(0.1, queue: .mainQueue()) + } } + |> distinctUntilChanged + } else { + hasPendingMessages = .single(false) } - |> distinctUntilChanged let isTopReplyThreadMessageShown: Signal = self.chatDisplayNode.historyNode.isTopReplyThreadMessageShown.get() |> distinctUntilChanged let topPinnedMessage: Signal = self.topPinnedMessageSignal(latest: false) - let peerId = self.chatLocation.peerId - let _ = (self.context.account.postbox.transaction { transaction -> CachedPeerData? in - return transaction.getPeerCachedData(peerId: peerId) - }).start(next: { [weak self] cachedData in - if let strongSelf = self { - strongSelf.cachedDataPromise.set(.single(cachedData)) - } - }) - - self.cachedDataDisposable = combineLatest(queue: .mainQueue(), self.chatDisplayNode.historyNode.cachedPeerDataAndMessages, hasPendingMessages, isTopReplyThreadMessageShown, topPinnedMessage).start(next: { [weak self] cachedDataAndMessages, hasPendingMessages, isTopReplyThreadMessageShown, topPinnedMessage in - if let strongSelf = self { - let (cachedData, messages) = cachedDataAndMessages - - if cachedData != nil { + if let peerId = self.chatLocation.peerId { + let _ = (self.context.account.postbox.transaction { transaction -> CachedPeerData? in + return transaction.getPeerCachedData(peerId: peerId) + }).start(next: { [weak self] cachedData in + if let strongSelf = self { strongSelf.cachedDataPromise.set(.single(cachedData)) } - - var pinnedMessageId: MessageId? - var peerIsBlocked: Bool = false - var callsAvailable: Bool = false - var callsPrivate: Bool = false - var slowmodeState: ChatSlowmodeState? - var activeGroupCallInfo: ChatActiveGroupCallInfo? - var inviteRequestsPending: Int32? - if let cachedData = cachedData as? CachedChannelData { - pinnedMessageId = cachedData.pinnedMessageId - if let channel = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isRestrictedBySlowmode, let timeout = cachedData.slowModeTimeout { - if hasPendingMessages { - slowmodeState = ChatSlowmodeState(timeout: timeout, variant: .pendingMessages) - } else if let slowmodeUntilTimestamp = calculateSlowmodeActiveUntilTimestamp(account: strongSelf.context.account, untilTimestamp: cachedData.slowModeValidUntilTimestamp) { - slowmodeState = ChatSlowmodeState(timeout: timeout, variant: .timestamp(slowmodeUntilTimestamp)) + }) + } else { + self.cachedDataPromise.set(.single(nil)) + } + + if let peerId = self.chatLocation.peerId { + self.cachedDataDisposable = combineLatest(queue: .mainQueue(), self.chatDisplayNode.historyNode.cachedPeerDataAndMessages, hasPendingMessages, isTopReplyThreadMessageShown, topPinnedMessage).start(next: { [weak self] cachedDataAndMessages, hasPendingMessages, isTopReplyThreadMessageShown, topPinnedMessage in + if let strongSelf = self { + let (cachedData, messages) = cachedDataAndMessages + + if cachedData != nil { + strongSelf.cachedDataPromise.set(.single(cachedData)) + } + + var pinnedMessageId: MessageId? + var peerIsBlocked: Bool = false + var callsAvailable: Bool = false + var callsPrivate: Bool = false + var slowmodeState: ChatSlowmodeState? + var activeGroupCallInfo: ChatActiveGroupCallInfo? + var inviteRequestsPending: Int32? + if let cachedData = cachedData as? CachedChannelData { + pinnedMessageId = cachedData.pinnedMessageId + if let channel = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isRestrictedBySlowmode, let timeout = cachedData.slowModeTimeout { + if hasPendingMessages { + slowmodeState = ChatSlowmodeState(timeout: timeout, variant: .pendingMessages) + } else if let slowmodeUntilTimestamp = calculateSlowmodeActiveUntilTimestamp(account: strongSelf.context.account, untilTimestamp: cachedData.slowModeValidUntilTimestamp) { + slowmodeState = ChatSlowmodeState(timeout: timeout, variant: .timestamp(slowmodeUntilTimestamp)) + } } + if let activeCall = cachedData.activeCall { + activeGroupCallInfo = ChatActiveGroupCallInfo(activeCall: activeCall) + } + inviteRequestsPending = cachedData.inviteRequestsPending + } else if let cachedData = cachedData as? CachedUserData { + peerIsBlocked = cachedData.isBlocked + callsAvailable = cachedData.voiceCallsAvailable + callsPrivate = cachedData.callsPrivate + pinnedMessageId = cachedData.pinnedMessageId + } else if let cachedData = cachedData as? CachedGroupData { + pinnedMessageId = cachedData.pinnedMessageId + if let activeCall = cachedData.activeCall { + activeGroupCallInfo = ChatActiveGroupCallInfo(activeCall: activeCall) + } + inviteRequestsPending = cachedData.inviteRequestsPending + } else if let _ = cachedData as? CachedSecretChatData { } - if let activeCall = cachedData.activeCall { - activeGroupCallInfo = ChatActiveGroupCallInfo(activeCall: activeCall) - } - inviteRequestsPending = cachedData.inviteRequestsPending - } else if let cachedData = cachedData as? CachedUserData { - peerIsBlocked = cachedData.isBlocked - callsAvailable = cachedData.voiceCallsAvailable - callsPrivate = cachedData.callsPrivate - pinnedMessageId = cachedData.pinnedMessageId - } else if let cachedData = cachedData as? CachedGroupData { - pinnedMessageId = cachedData.pinnedMessageId - if let activeCall = cachedData.activeCall { - activeGroupCallInfo = ChatActiveGroupCallInfo(activeCall: activeCall) - } - inviteRequestsPending = cachedData.inviteRequestsPending - } else if let _ = cachedData as? CachedSecretChatData { - } - - var pinnedMessage: ChatPinnedMessage? - switch strongSelf.chatLocation { - case let .replyThread(replyThreadMessage): - if isTopReplyThreadMessageShown { + + var pinnedMessage: ChatPinnedMessage? + switch strongSelf.chatLocation { + case let .replyThread(replyThreadMessage): + if isTopReplyThreadMessageShown { + pinnedMessageId = nil + } else { + pinnedMessageId = replyThreadMessage.effectiveTopId + } + if let pinnedMessageId = pinnedMessageId { + if let message = messages?[pinnedMessageId] { + pinnedMessage = ChatPinnedMessage(message: message, index: 0, totalCount: 1, topMessageId: message.id) + } + } + case .peer: + pinnedMessageId = topPinnedMessage?.message.id + pinnedMessage = topPinnedMessage + case .feed: pinnedMessageId = nil - } else { - pinnedMessageId = replyThreadMessage.effectiveTopId + pinnedMessage = nil } - if let pinnedMessageId = pinnedMessageId { - if let message = messages?[pinnedMessageId] { - pinnedMessage = ChatPinnedMessage(message: message, index: 0, totalCount: 1, topMessageId: message.id) + + var pinnedMessageUpdated = false + if let current = strongSelf.presentationInterfaceState.pinnedMessage, let updated = pinnedMessage { + if current != updated { + pinnedMessageUpdated = true } - } - case .peer: - pinnedMessageId = topPinnedMessage?.message.id - pinnedMessage = topPinnedMessage - } - - var pinnedMessageUpdated = false - if let current = strongSelf.presentationInterfaceState.pinnedMessage, let updated = pinnedMessage { - if current != updated { + } else if (strongSelf.presentationInterfaceState.pinnedMessage != nil) != (pinnedMessage != nil) { pinnedMessageUpdated = true } - } else if (strongSelf.presentationInterfaceState.pinnedMessage != nil) != (pinnedMessage != nil) { - pinnedMessageUpdated = true - } + + let callsDataUpdated = strongSelf.presentationInterfaceState.callsAvailable != callsAvailable || strongSelf.presentationInterfaceState.callsPrivate != callsPrivate - let callsDataUpdated = strongSelf.presentationInterfaceState.callsAvailable != callsAvailable || strongSelf.presentationInterfaceState.callsPrivate != callsPrivate - - var canManageInvitations = false - if let channel = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isCreator) || (channel.adminRights?.rights.contains(.canInviteUsers) == true) { - canManageInvitations = true - } else if let group = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramGroup { - if case .creator = group.role { - canManageInvitations = true - } else if case let .admin(rights, _) = group.role, rights.rights.contains(.canInviteUsers) { + var canManageInvitations = false + if let channel = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isCreator) || (channel.adminRights?.rights.contains(.canInviteUsers) == true) { canManageInvitations = true + } else if let group = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramGroup { + if case .creator = group.role { + canManageInvitations = true + } else if case let .admin(rights, _) = group.role, rights.rights.contains(.canInviteUsers) { + canManageInvitations = true + } } - } - - if canManageInvitations, let inviteRequestsPending = inviteRequestsPending, inviteRequestsPending >= 0 { - if strongSelf.inviteRequestsContext == nil { - let inviteRequestsContext = strongSelf.context.engine.peers.peerInvitationImporters(peerId: peerId, subject: .requests(query: nil)) - strongSelf.inviteRequestsContext = inviteRequestsContext - - strongSelf.inviteRequestsDisposable.set((combineLatest(queue: Queue.mainQueue(), inviteRequestsContext.state, ApplicationSpecificNotice.dismissedInvitationRequests(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peerId))).start(next: { [weak self] requestsState, dismissedInvitationRequests in - guard let strongSelf = self else { - return - } - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { state in - return state - .updatedTitlePanelContext({ context in - let peers: [EnginePeer] = Array(requestsState.importers.compactMap({ $0.peer.peer.flatMap({ EnginePeer($0) }) }).prefix(3)) - - var peersDismissed = false - if let dismissedInvitationRequests = dismissedInvitationRequests, Set(peers.map({ $0.id.toInt64() })) == Set(dismissedInvitationRequests) { - peersDismissed = true - } - - if requestsState.count > 0 && !peersDismissed { - if !context.contains(where: { - switch $0 { - case .inviteRequests(peers, requestsState.count): - return true - default: - return false - } - }) { - var updatedContexts = context.filter { c in - if case .inviteRequests = c { - return false - } else { - return true + + if canManageInvitations, let inviteRequestsPending = inviteRequestsPending, inviteRequestsPending >= 0 { + if strongSelf.inviteRequestsContext == nil { + let inviteRequestsContext = strongSelf.context.engine.peers.peerInvitationImporters(peerId: peerId, subject: .requests(query: nil)) + strongSelf.inviteRequestsContext = inviteRequestsContext + + strongSelf.inviteRequestsDisposable.set((combineLatest(queue: Queue.mainQueue(), inviteRequestsContext.state, ApplicationSpecificNotice.dismissedInvitationRequests(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peerId))).start(next: { [weak self] requestsState, dismissedInvitationRequests in + guard let strongSelf = self else { + return + } + strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { state in + return state + .updatedTitlePanelContext({ context in + let peers: [EnginePeer] = Array(requestsState.importers.compactMap({ $0.peer.peer.flatMap({ EnginePeer($0) }) }).prefix(3)) + + var peersDismissed = false + if let dismissedInvitationRequests = dismissedInvitationRequests, Set(peers.map({ $0.id.toInt64() })) == Set(dismissedInvitationRequests) { + peersDismissed = true + } + + if requestsState.count > 0 && !peersDismissed { + if !context.contains(where: { + switch $0 { + case .inviteRequests(peers, requestsState.count): + return true + default: + return false } + }) { + var updatedContexts = context.filter { c in + if case .inviteRequests = c { + return false + } else { + return true + } + } + updatedContexts.append(.inviteRequests(peers, requestsState.count)) + return updatedContexts.sorted() + } else { + return context } - updatedContexts.append(.inviteRequests(peers, requestsState.count)) - return updatedContexts.sorted() } else { - return context - } - } else { - if let index = context.firstIndex(where: { - switch $0 { - case .inviteRequests: - return true - default: - return false + if let index = context.firstIndex(where: { + switch $0 { + case .inviteRequests: + return true + default: + return false + } + }) { + var updatedContexts = context + updatedContexts.remove(at: index) + return updatedContexts + } else { + return context } - }) { - var updatedContexts = context - updatedContexts.remove(at: index) - return updatedContexts - } else { - return context } - } + }) + .updatedSlowmodeState(slowmodeState) }) - .updatedSlowmodeState(slowmodeState) + })) + } else if let inviteRequestsContext = strongSelf.inviteRequestsContext { + let _ = (inviteRequestsContext.state + |> take(1) + |> deliverOnMainQueue).start(next: { [weak inviteRequestsContext] state in + if state.count != inviteRequestsPending { + inviteRequestsContext?.loadMore() + } }) - })) - } else if let inviteRequestsContext = strongSelf.inviteRequestsContext { - let _ = (inviteRequestsContext.state - |> take(1) - |> deliverOnMainQueue).start(next: { [weak inviteRequestsContext] state in - if state.count != inviteRequestsPending { - inviteRequestsContext?.loadMore() - } + } + } + + if strongSelf.presentationInterfaceState.pinnedMessageId != pinnedMessageId || strongSelf.presentationInterfaceState.pinnedMessage != pinnedMessage || strongSelf.presentationInterfaceState.peerIsBlocked != peerIsBlocked || pinnedMessageUpdated || callsDataUpdated || strongSelf.presentationInterfaceState.slowmodeState != slowmodeState || strongSelf.presentationInterfaceState.activeGroupCallInfo != activeGroupCallInfo { + strongSelf.updateChatPresentationInterfaceState(animated: strongSelf.willAppear, interactive: strongSelf.willAppear, { state in + return state + .updatedPinnedMessageId(pinnedMessageId) + .updatedActiveGroupCallInfo(activeGroupCallInfo) + .updatedPinnedMessage(pinnedMessage) + .updatedPeerIsBlocked(peerIsBlocked) + .updatedCallsAvailable(callsAvailable) + .updatedCallsPrivate(callsPrivate) + .updatedTitlePanelContext({ context in + if pinnedMessageId != nil { + if !context.contains(where: { + switch $0 { + case .pinnedMessage: + return true + default: + return false + } + }) { + var updatedContexts = context + updatedContexts.append(.pinnedMessage) + return updatedContexts.sorted() + } else { + return context + } + } else { + if let index = context.firstIndex(where: { + switch $0 { + case .pinnedMessage: + return true + default: + return false + } + }) { + var updatedContexts = context + updatedContexts.remove(at: index) + return updatedContexts + } else { + return context + } + } + }) + .updatedSlowmodeState(slowmodeState) }) } + + if !strongSelf.didSetCachedDataReady { + strongSelf.didSetCachedDataReady = true + strongSelf.cachedDataReady.set(.single(true)) + } } - - if strongSelf.presentationInterfaceState.pinnedMessageId != pinnedMessageId || strongSelf.presentationInterfaceState.pinnedMessage != pinnedMessage || strongSelf.presentationInterfaceState.peerIsBlocked != peerIsBlocked || pinnedMessageUpdated || callsDataUpdated || strongSelf.presentationInterfaceState.slowmodeState != slowmodeState || strongSelf.presentationInterfaceState.activeGroupCallInfo != activeGroupCallInfo { - strongSelf.updateChatPresentationInterfaceState(animated: strongSelf.willAppear, interactive: strongSelf.willAppear, { state in - return state - .updatedPinnedMessageId(pinnedMessageId) - .updatedActiveGroupCallInfo(activeGroupCallInfo) - .updatedPinnedMessage(pinnedMessage) - .updatedPeerIsBlocked(peerIsBlocked) - .updatedCallsAvailable(callsAvailable) - .updatedCallsPrivate(callsPrivate) - .updatedTitlePanelContext({ context in - if pinnedMessageId != nil { - if !context.contains(where: { - switch $0 { - case .pinnedMessage: - return true - default: - return false - } - }) { - var updatedContexts = context - updatedContexts.append(.pinnedMessage) - return updatedContexts.sorted() - } else { - return context - } - } else { - if let index = context.firstIndex(where: { - switch $0 { - case .pinnedMessage: - return true - default: - return false - } - }) { - var updatedContexts = context - updatedContexts.remove(at: index) - return updatedContexts - } else { - return context - } - } - }) - .updatedSlowmodeState(slowmodeState) - }) - } - - if !strongSelf.didSetCachedDataReady { - strongSelf.didSetCachedDataReady = true - strongSelf.cachedDataReady.set(.single(true)) - } + }) + } else { + if !self.didSetCachedDataReady { + self.didSetCachedDataReady = true + self.cachedDataReady.set(.single(true)) } - }) + } self.historyStateDisposable = self.chatDisplayNode.historyNode.historyState.get().start(next: { [weak self] state in if let strongSelf = self { @@ -5654,7 +5705,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } self.chatDisplayNode.sendMessages = { [weak self] messages, silentPosting, scheduleTime, isAnyMessageTextPartitioned in - if let strongSelf = self { + if let strongSelf = self, let peerId = strongSelf.chatLocation.peerId { var correlationIds: [Int64] = [] for message in messages { switch message { @@ -5666,7 +5717,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G break } } - let peerId = strongSelf.chatLocation.peerId strongSelf.commitPurposefulAction() if let channel = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isRestrictedBySlowmode { @@ -6350,7 +6400,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G |> distinctUntilChanged } - let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peerId), subject: .forwardedMessages(ids: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds ?? [], options: forwardOptions), botStart: nil, mode: .standard(previewing: true)) + let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: .forwardedMessages(ids: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds ?? [], options: forwardOptions), botStart: nil, mode: .standard(previewing: true)) chatController.canReadHistory.set(false) let messageIds = strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds ?? [] @@ -6756,8 +6806,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.updateItemNodesSearchTextHighlightStates() if let navigateIndex = navigateIndex { switch strongSelf.chatLocation { - case .peer, .replyThread: - strongSelf.navigateToMessage(from: nil, to: .index(navigateIndex), forceInCurrentChat: true) + case .peer, .replyThread, .feed: + strongSelf.navigateToMessage(from: nil, to: .index(navigateIndex), forceInCurrentChat: true) } } } @@ -6790,7 +6840,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } if let navigationController = strongSelf.effectiveNavigationController { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: nil, keepStack: .always)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId), subject: nil, keepStack: .always)) } }, navigateToProfile: { [weak self] peerId in guard let strongSelf = self else { @@ -6800,8 +6850,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }, openPeerInfo: { [weak self] in self?.navigationButtonAction(.openChatInfo(expandAvatar: false)) }, togglePeerNotifications: { [weak self] in - if let strongSelf = self { - let peerId = strongSelf.chatLocation.peerId + if let strongSelf = self, let peerId = strongSelf.chatLocation.peerId { let _ = strongSelf.context.engine.peers.togglePeerMuted(peerId: peerId).start() } }, sendContextResult: { [weak self] results, result, node, rect in @@ -7945,7 +7994,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let navigationController = strongSelf.effectiveNavigationController { let subject: ChatControllerSubject? = sourceMessageId.flatMap { ChatControllerSubject.message(id: .id($0), highlight: true, timecode: nil) } - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .replyThread(replyThreadResult), subject: subject, keepStack: .always)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .replyThread(message: replyThreadResult), subject: subject, keepStack: .always)) } }, activatePinnedListPreview: { [weak self] node, gesture in guard let strongSelf = self else { @@ -7995,7 +8044,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }))) } - let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peerId), subject: .pinnedMessages(id: pinnedMessage.message.id), botStart: nil, mode: .standard(previewing: true)) + let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: .pinnedMessages(id: pinnedMessage.message.id), botStart: nil, mode: .standard(previewing: true)) chatController.canReadHistory.set(false) strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts() @@ -8042,7 +8091,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.push(controller) } }, openSendAsPeer: { [weak self] node, gesture in - guard let strongSelf = self, let node = node as? ContextReferenceContentNode, let peers = strongSelf.presentationInterfaceState.sendAsPeers, let layout = strongSelf.validLayout else { + guard let strongSelf = self, let peerId = strongSelf.chatLocation.peerId, let node = node as? ContextReferenceContentNode, let peers = strongSelf.presentationInterfaceState.sendAsPeers, let layout = strongSelf.validLayout else { return } @@ -8158,7 +8207,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.chatDisplayNode.navigateButtons.unreadCount = Int32(count) }) - if case .peer = self.chatLocation { + if case let .peer(peerId) = self.chatLocation { let unreadCountsKey: PostboxViewKey = .unreadCounts(items: [.peer(peerId), .total(nil)]) let notificationSettingsKey: PostboxViewKey = .peerNotificationSettings(peerIds: Set([peerId])) self.chatUnreadCountDisposable = (self.context.account.postbox.combinedView(keys: [unreadCountsKey, notificationSettingsKey]) @@ -8213,136 +8262,142 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let postbox = self.context.account.postbox let previousPeerCache = Atomic<[PeerId: Peer]>(value: [:]) - let activitySpace: PeerActivitySpace + let activitySpace: PeerActivitySpace? switch self.chatLocation { case let .peer(peerId): activitySpace = PeerActivitySpace(peerId: peerId, category: .global) case let .replyThread(replyThreadMessage): activitySpace = PeerActivitySpace(peerId: replyThreadMessage.messageId.peerId, category: .thread(makeMessageThreadId(replyThreadMessage.messageId))) + case .feed: + activitySpace = nil } - self.peerInputActivitiesDisposable = (self.context.account.peerInputActivities(peerId: activitySpace) - |> mapToSignal { activities -> Signal<[(Peer, PeerInputActivity)], NoError> in - var foundAllPeers = true - var cachedResult: [(Peer, PeerInputActivity)] = [] - previousPeerCache.with { dict -> Void in - for (peerId, activity) in activities { - if let peer = dict[peerId] { - cachedResult.append((peer, activity)) - } else { - foundAllPeers = false - break - } - } - } - if foundAllPeers { - return .single(cachedResult) - } else { - return postbox.transaction { transaction -> [(Peer, PeerInputActivity)] in - var result: [(Peer, PeerInputActivity)] = [] - var peerCache: [PeerId: Peer] = [:] + if let activitySpace = activitySpace, let peerId = peerId { + self.peerInputActivitiesDisposable = (self.context.account.peerInputActivities(peerId: activitySpace) + |> mapToSignal { activities -> Signal<[(Peer, PeerInputActivity)], NoError> in + var foundAllPeers = true + var cachedResult: [(Peer, PeerInputActivity)] = [] + previousPeerCache.with { dict -> Void in for (peerId, activity) in activities { - if let peer = transaction.getPeer(peerId) { - result.append((peer, activity)) - peerCache[peerId] = peer + if let peer = dict[peerId] { + cachedResult.append((peer, activity)) + } else { + foundAllPeers = false + break } } - let _ = previousPeerCache.swap(peerCache) - return result } - } - } - |> deliverOnMainQueue).start(next: { [weak self] activities in - if let strongSelf = self { - let displayActivities = activities.filter({ - switch $0.1 { - case .speakingInGroupCall, .interactingWithEmoji: - return false - default: - return true - } - }) - strongSelf.chatTitleView?.inputActivities = (peerId, displayActivities) - - strongSelf.peerInputActivitiesPromise.set(.single(activities)) - - for activity in activities { - if case let .interactingWithEmoji(emoticon, messageId, maybeInteraction) = activity.1, let interaction = maybeInteraction { - var found = false - strongSelf.chatDisplayNode.historyNode.forEachVisibleItemNode({ itemNode in - if !found, let itemNode = itemNode as? ChatMessageAnimatedStickerItemNode, let item = itemNode.item { - if item.message.id == messageId { - itemNode.playEmojiInteraction(interaction) - found = true - } + if foundAllPeers { + return .single(cachedResult) + } else { + return postbox.transaction { transaction -> [(Peer, PeerInputActivity)] in + var result: [(Peer, PeerInputActivity)] = [] + var peerCache: [PeerId: Peer] = [:] + for (peerId, activity) in activities { + if let peer = transaction.getPeer(peerId) { + result.append((peer, activity)) + peerCache[peerId] = peer } - }) - - if found { - let _ = strongSelf.context.account.updateLocalInputActivity(peerId: activitySpace, activity: .seeingEmojiInteraction(emoticon: emoticon), isPresent: true) } + let _ = previousPeerCache.swap(peerCache) + return result } } } - }) + |> deliverOnMainQueue).start(next: { [weak self] activities in + if let strongSelf = self { + let displayActivities = activities.filter({ + switch $0.1 { + case .speakingInGroupCall, .interactingWithEmoji: + return false + default: + return true + } + }) + strongSelf.chatTitleView?.inputActivities = (peerId, displayActivities) + + strongSelf.peerInputActivitiesPromise.set(.single(activities)) + + for activity in activities { + if case let .interactingWithEmoji(emoticon, messageId, maybeInteraction) = activity.1, let interaction = maybeInteraction { + var found = false + strongSelf.chatDisplayNode.historyNode.forEachVisibleItemNode({ itemNode in + if !found, let itemNode = itemNode as? ChatMessageAnimatedStickerItemNode, let item = itemNode.item { + if item.message.id == messageId { + itemNode.playEmojiInteraction(interaction) + found = true + } + } + }) + + if found { + let _ = strongSelf.context.account.updateLocalInputActivity(peerId: activitySpace, activity: .seeingEmojiInteraction(emoticon: emoticon), isPresent: true) + } + } + } + } + }) + } } - self.sentMessageEventsDisposable.set((self.context.account.pendingMessageManager.deliveredMessageEvents(peerId: peerId) - |> deliverOnMainQueue).start(next: { [weak self] namespace, silent in - if let strongSelf = self { - let inAppNotificationSettings = strongSelf.context.sharedContext.currentInAppNotificationSettings.with { $0 } - if inAppNotificationSettings.playSounds && !silent { - serviceSoundManager.playMessageDeliveredSound() - } - if strongSelf.presentationInterfaceState.subject != .scheduledMessages && namespace == Namespaces.Message.ScheduledCloud { - strongSelf.openScheduledMessages() - } - - if strongSelf.shouldDisplayChecksTooltip { - Queue.mainQueue().after(1.0) { - strongSelf.displayChecksTooltip() + if let peerId = peerId { + self.sentMessageEventsDisposable.set((self.context.account.pendingMessageManager.deliveredMessageEvents(peerId: peerId) + |> deliverOnMainQueue).start(next: { [weak self] namespace, silent in + if let strongSelf = self { + let inAppNotificationSettings = strongSelf.context.sharedContext.currentInAppNotificationSettings.with { $0 } + if inAppNotificationSettings.playSounds && !silent { + serviceSoundManager.playMessageDeliveredSound() + } + if strongSelf.presentationInterfaceState.subject != .scheduledMessages && namespace == Namespaces.Message.ScheduledCloud { + strongSelf.openScheduledMessages() + } + + if strongSelf.shouldDisplayChecksTooltip { + Queue.mainQueue().after(1.0) { + strongSelf.displayChecksTooltip() + } + strongSelf.shouldDisplayChecksTooltip = false + strongSelf.checksTooltipDisposable.set(dismissServerProvidedSuggestion(account: strongSelf.context.account, suggestion: .newcomerTicks).start()) } - strongSelf.shouldDisplayChecksTooltip = false - strongSelf.checksTooltipDisposable.set(dismissServerProvidedSuggestion(account: strongSelf.context.account, suggestion: .newcomerTicks).start()) } - } - })) - - self.failedMessageEventsDisposable.set((self.context.account.pendingMessageManager.failedMessageEvents(peerId: peerId) - |> deliverOnMainQueue).start(next: { [weak self] reason in - if let strongSelf = self, strongSelf.currentFailedMessagesAlertController == nil { - let text: String - let moreInfo: Bool - switch reason { - case .flood: - text = strongSelf.presentationData.strings.Conversation_SendMessageErrorFlood - moreInfo = true - case .publicBan: - text = strongSelf.presentationData.strings.Conversation_SendMessageErrorGroupRestricted - moreInfo = true - case .mediaRestricted: - strongSelf.interfaceInteraction?.displayRestrictedInfo(.mediaRecording, .alert) - return - case .slowmodeActive: - text = strongSelf.presentationData.strings.Chat_SlowmodeSendError - moreInfo = false - case .tooMuchScheduled: - text = strongSelf.presentationData.strings.Conversation_SendMessageErrorTooMuchScheduled - moreInfo = false + })) + + self.failedMessageEventsDisposable.set((self.context.account.pendingMessageManager.failedMessageEvents(peerId: peerId) + |> deliverOnMainQueue).start(next: { [weak self] reason in + if let strongSelf = self, strongSelf.currentFailedMessagesAlertController == nil { + let text: String + let moreInfo: Bool + switch reason { + case .flood: + text = strongSelf.presentationData.strings.Conversation_SendMessageErrorFlood + moreInfo = true + case .publicBan: + text = strongSelf.presentationData.strings.Conversation_SendMessageErrorGroupRestricted + moreInfo = true + case .mediaRestricted: + strongSelf.interfaceInteraction?.displayRestrictedInfo(.mediaRecording, .alert) + return + case .slowmodeActive: + text = strongSelf.presentationData.strings.Chat_SlowmodeSendError + moreInfo = false + case .tooMuchScheduled: + text = strongSelf.presentationData.strings.Conversation_SendMessageErrorTooMuchScheduled + moreInfo = false + } + let actions: [TextAlertAction] + if moreInfo { + actions = [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Generic_ErrorMoreInfo, action: { + self?.openPeerMention("spambot", navigation: .chat(textInputState: nil, subject: nil, peekData: nil)) + }), TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_OK, action: {})] + } else { + actions = [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})] + } + let controller = textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: nil, text: text, actions: actions) + strongSelf.currentFailedMessagesAlertController = controller + strongSelf.present(controller, in: .window(.root)) } - let actions: [TextAlertAction] - if moreInfo { - actions = [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Generic_ErrorMoreInfo, action: { - self?.openPeerMention("spambot", navigation: .chat(textInputState: nil, subject: nil, peekData: nil)) - }), TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_OK, action: {})] - } else { - actions = [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})] - } - let controller = textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: nil, text: text, actions: actions) - strongSelf.currentFailedMessagesAlertController = controller - strongSelf.present(controller, in: .window(.root)) - } - })) + })) + } } self.interfaceInteraction = interfaceInteraction @@ -8472,7 +8527,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G updatedChatNavigationStack.insert(peerId, at: 0) } - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), animated: false, chatListFilter: nextFolderId, chatNavigationStack: updatedChatNavigationStack, completion: { nextController in + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peer.id), animated: false, chatListFilter: nextFolderId, chatNavigationStack: updatedChatNavigationStack, completion: { nextController in (nextController as! ChatControllerImpl).animateFromPreviousController(snapshotState: snapshotState) })) } @@ -8576,11 +8631,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } - /*let snapshotState = strongSelf.chatDisplayNode.prepareSnapshotState( - titleViewSnapshotState: strongSelf.chatTitleView?.prepareSnapshotState(), - avatarSnapshotState: (strongSelf.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.prepareSnapshotState() - )*/ - let nextFolderId: Int32? = strongSelf.currentChatListFilter var updatedChatNavigationStack = strongSelf.chatNavigationStack @@ -8588,9 +8638,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G updatedChatNavigationStack.removeSubrange(0 ..< (index + 1)) } - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), animated: true, chatListFilter: nextFolderId, chatNavigationStack: updatedChatNavigationStack, completion: { nextController in + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peer.id), animated: true, chatListFilter: nextFolderId, chatNavigationStack: updatedChatNavigationStack, completion: { nextController in let _ = nextController - //(nextController as! ChatControllerImpl).animateFromPreviousController(snapshotState: snapshotState) })) }))) } @@ -9020,6 +9069,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case let .replyThread(replyThreadMessage): peerId = replyThreadMessage.messageId.peerId threadId = makeMessageThreadId(replyThreadMessage.messageId) + case .feed: + return } let timestamp = Int32(Date().timeIntervalSince1970) @@ -9903,6 +9954,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G })) case .replyThread: break + case .feed: + break } }) case .search: @@ -10110,6 +10163,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G })) case .replyThread: break + case .feed: + break } case .toggleInfoPanel: self.updateChatPresentationInterfaceState(animated: true, interactive: true, { @@ -10405,10 +10460,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G attachmentController?.dismiss(animated: true) self?.presentICloudFileGallery() }, send: { [weak self] mediaReference in - guard let strongSelf = self else { + guard let strongSelf = self, let peerId = strongSelf.chatLocation.peerId else { return } - let peerId = strongSelf.chatLocation.peerId let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: mediaReference, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil) let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: strongSelf.transformEnqueueMessages([message])) |> deliverOnMainQueue).start(next: { [weak self] _ in @@ -11860,6 +11914,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G break case let .replyThread(replyThreadMessage): defaultReplyMessageId = replyThreadMessage.messageId + case .feed: + break } return messages.map { message in @@ -11904,7 +11960,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } private func sendMessages(_ messages: [EnqueueMessage], commit: Bool = false) { - let peerId = self.chatLocation.peerId + guard let peerId = self.chatLocation.peerId else { + return + } var isScheduledMessages = false if case .scheduledMessages = self.presentationInterfaceState.subject { @@ -12145,8 +12203,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if !canSendMessagesToChat(self.presentationInterfaceState) { return } - - let peerId = self.chatLocation.peerId + + guard let peerId = self.chatLocation.peerId else { + return + } let replyMessageId = self.presentationInterfaceState.interfaceState.replyMessageId @@ -12226,7 +12286,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } private func requestVideoRecorder() { - let peerId = self.chatLocation.peerId + guard let peerId = self.chatLocation.peerId else { + return + } if self.videoRecorderValue == nil { if let currentInputPanelFrame = self.chatDisplayNode.currentInputPanelFrame() { @@ -12456,6 +12518,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G private func sendMediaRecording(silentPosting: Bool? = nil, scheduleTime: Int32? = nil) { self.chatDisplayNode.updateRecordedMediaDeleted(false) + if let recordedMediaPreview = self.presentationInterfaceState.recordedMediaPreview { var isScheduledMessages = false if case .scheduledMessages = self.presentationInterfaceState.subject { @@ -12490,7 +12553,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G transformedMessages = self.transformEnqueueMessages(messages) } - let peerId = self.chatLocation.peerId + guard let peerId = self.chatLocation.peerId else { + return + } + let _ = (enqueueMessages(account: self.context.account, peerId: peerId, messages: transformedMessages) |> deliverOnMainQueue).start(next: { [weak self] _ in if let strongSelf = self, strongSelf.presentationInterfaceState.subject != .scheduledMessages { @@ -12503,6 +12569,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } private func updateSearch(_ interfaceState: ChatPresentationInterfaceState) -> ChatPresentationInterfaceState? { + guard let peerId = self.chatLocation.peerId else { + return nil + } + let limit: Int32 = 100 var derivedSearchState: ChatSearchState? @@ -12524,14 +12594,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G break case let .replyThread(replyThreadMessage): searchTopMsgId = replyThreadMessage.messageId + case .feed: + break } switch search.domain { case .everything: - derivedSearchState = ChatSearchState(query: search.query, location: .peer(peerId: self.chatLocation.peerId, fromId: nil, tags: nil, topMsgId: searchTopMsgId, minDate: nil, maxDate: nil), loadMoreState: loadMoreStateFromResultsState(search.resultsState)) + derivedSearchState = ChatSearchState(query: search.query, location: .peer(peerId: peerId, fromId: nil, tags: nil, topMsgId: searchTopMsgId, minDate: nil, maxDate: nil), loadMoreState: loadMoreStateFromResultsState(search.resultsState)) case .members: derivedSearchState = nil case let .member(peer): - derivedSearchState = ChatSearchState(query: search.query, location: .peer(peerId: self.chatLocation.peerId, fromId: peer.id, tags: nil, topMsgId: searchTopMsgId, minDate: nil, maxDate: nil), loadMoreState: loadMoreStateFromResultsState(search.resultsState)) + derivedSearchState = ChatSearchState(query: search.query, location: .peer(peerId: peerId, fromId: peer.id, tags: nil, topMsgId: searchTopMsgId, minDate: nil, maxDate: nil), loadMoreState: loadMoreStateFromResultsState(search.resultsState)) } } @@ -12601,8 +12673,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) if let navigateIndex = navigateIndex { switch strongSelf.chatLocation { - case .peer, .replyThread: - strongSelf.navigateToMessage(from: nil, to: .index(navigateIndex), forceInCurrentChat: true) + case .peer, .replyThread, .feed: + strongSelf.navigateToMessage(from: nil, to: .index(navigateIndex), forceInCurrentChat: true) } } strongSelf.updateItemNodesSearchTextHighlightStates() @@ -12839,6 +12911,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case let .replyThread(replyThreadMessage): peerId = replyThreadMessage.messageId.peerId threadId = makeMessageThreadId(replyThreadMessage.messageId) + case .feed: + return } strongSelf.messageIndexDisposable.set((strongSelf.context.engine.messages.searchMessageIdByTimestamp(peerId: peerId, threadId: threadId, timestamp: timestamp) |> deliverOnMainQueue).start(next: { messageId in @@ -12874,6 +12948,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case let .replyThread(replyThreadMessage): peerId = replyThreadMessage.messageId.peerId threadId = makeMessageThreadId(replyThreadMessage.messageId) + case .feed: + return } strongSelf.messageIndexDisposable.set((strongSelf.context.engine.messages.searchMessageIdByTimestamp(peerId: peerId, threadId: threadId, timestamp: timestamp) |> deliverOnMainQueue).start(next: { messageId in @@ -12904,7 +12980,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }))) } - let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peerId), subject: .message(id: .timestamp(timestamp), highlight: false, timecode: nil), botStart: nil, mode: .standard(previewing: true)) + let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: .message(id: .timestamp(timestamp), highlight: false, timecode: nil), botStart: nil, mode: .standard(previewing: true)) chatController.canReadHistory.set(false) strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts() @@ -13033,7 +13109,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G statusController?.dismiss() } - let chatLocation: ChatLocation = .replyThread(result.message) + let chatLocation: ChatLocation = .replyThread(message: result.message) let subject: ChatControllerSubject? if let atMessageId = atMessageId { @@ -13102,11 +13178,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if isPinnedMessages, let messageId = messageLocation.messageId { if let navigationController = self.effectiveNavigationController { self.dismiss() - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(messageId.peerId), subject: .message(id: .id(messageId), highlight: true, timecode: nil), keepStack: .always)) + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(id: messageId.peerId), subject: .message(id: .id(messageId), highlight: true, timecode: nil), keepStack: .always)) } } else if case let .peer(peerId) = self.chatLocation, let messageId = messageLocation.messageId, (messageId.peerId != peerId && !forceInCurrentChat) || (isScheduledMessages && messageId.id != 0 && !Namespaces.Message.allScheduled.contains(messageId.namespace)) { if let navigationController = self.effectiveNavigationController { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(messageId.peerId), subject: .message(id: .id(messageId), highlight: true, timecode: nil), keepStack: .always)) + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(id: messageId.peerId), subject: .message(id: .id(messageId), highlight: true, timecode: nil), keepStack: .always)) } } else if forceInCurrentChat { if let _ = fromId, let fromIndex = fromIndex, rememberInStack { @@ -13156,12 +13232,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.loadingMessage.set(.single(statusSubject) |> delay(0.1, queue: .mainQueue())) let searchLocation: ChatHistoryInitialSearchLocation switch messageLocation { - case let .id(id, _): - searchLocation = .id(id) - case let .index(index): - searchLocation = .index(index) - case .upperBound: - searchLocation = .index(MessageIndex.upperBound(peerId: self.chatLocation.peerId)) + case let .id(id, _): + searchLocation = .id(id) + case let .index(index): + searchLocation = .index(index) + case .upperBound: + if let peerId = self.chatLocation.peerId { + searchLocation = .index(MessageIndex.upperBound(peerId: peerId)) + } else { + searchLocation = .index(.absoluteUpperBound()) + } } let historyView = preloadedChatHistoryViewForLocation(ChatHistoryLocationInput(content: .InitialSearch(location: searchLocation, count: 50, highlight: true), id: 0), context: self.context, chatLocation: self.chatLocation, subject: self.subject, chatLocationContextHolder: self.chatLocationContextHolder, fixedCombinedReadStates: nil, tagMask: nil, additionalData: []) @@ -13283,7 +13363,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G completion?() } else { if let navigationController = strongSelf.effectiveNavigationController { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(messageLocation.peerId), subject: messageLocation.messageId.flatMap { .message(id: .id($0), highlight: true, timecode: nil) })) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: messageLocation.peerId), subject: messageLocation.messageId.flatMap { .message(id: .id($0), highlight: true, timecode: nil) })) } completion?() } @@ -13295,7 +13375,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G })) } else { if let navigationController = self.effectiveNavigationController { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(messageLocation.peerId), subject: messageLocation.messageId.flatMap { .message(id: .id($0), highlight: true, timecode: nil) })) + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(id: messageLocation.peerId), subject: messageLocation.messageId.flatMap { .message(id: .id($0), highlight: true, timecode: nil) })) } completion?() } @@ -13489,7 +13569,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: true, text: messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] value in if case .info = value, let strongSelf = self, let navigationController = strongSelf.effectiveNavigationController { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(context.account.peerId), keepStack: .always, purposefulAction: {}, peekData: nil)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: context.account.peerId), keepStack: .always, purposefulAction: {}, peekData: nil)) return true } return false @@ -13558,7 +13638,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } if let navigationController = navigationController { - let chatController = ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(peerId)) + let chatController = ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(id: peerId)) var viewControllers = navigationController.viewControllers viewControllers.insert(chatController, at: viewControllers.count - 1) navigationController.setViewControllers(viewControllers, animated: false) @@ -13647,14 +13727,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) |> deliverOnMainQueue).start(completed: { [weak self] in if let strongSelf = self, let navigationController = strongSelf.effectiveNavigationController { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: subject, updateTextInputState: textInputState, peekData: peekData)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId), subject: subject, updateTextInputState: textInputState, peekData: peekData)) } }) } else { - self.effectiveNavigationController?.pushViewController(ChatControllerImpl(context: self.context, chatLocation: .peer(peerId), subject: subject)) + self.effectiveNavigationController?.pushViewController(ChatControllerImpl(context: self.context, chatLocation: .peer(id: peerId), subject: subject)) } case let .withBotStartPayload(botStart): - self.effectiveNavigationController?.pushViewController(ChatControllerImpl(context: self.context, chatLocation: .peer(peerId), botStart: botStart)) + self.effectiveNavigationController?.pushViewController(ChatControllerImpl(context: self.context, chatLocation: .peer(id: peerId), botStart: botStart)) } } } else { @@ -13686,7 +13766,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withoutSelectionState() }) }) if let navigationController = strongSelf.effectiveNavigationController { - let chatController = ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(peerId)) + let chatController = ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(id: peerId)) var viewControllers = navigationController.viewControllers viewControllers.insert(chatController, at: viewControllers.count - 1) navigationController.setViewControllers(viewControllers, animated: false) @@ -13777,6 +13857,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } private func openHashtag(_ hashtag: String, peerName: String?) { + guard let peerId = self.chatLocation.peerId else { + return + } let _ = self.presentVoiceMessageDiscardAlert(action: { if self.resolvePeerByNameDisposable == nil { self.resolvePeerByNameDisposable = MetaDisposable() @@ -13792,7 +13875,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } } else { - resolveSignal = self.context.account.postbox.loadedPeerWithId(self.chatLocation.peerId) + resolveSignal = self.context.account.postbox.loadedPeerWithId(peerId) |> map(Optional.init) } var cancelImpl: (() -> Void)? @@ -14089,7 +14172,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } } else if let navigationController = strongSelf.effectiveNavigationController { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: subject, keepStack: .always, peekData: peekData)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId), subject: subject, keepStack: .always, peekData: peekData)) } case .info: strongSelf.navigationActionDisposable.set((strongSelf.context.account.postbox.loadedPeerWithId(peerId) @@ -14107,7 +14190,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G $0.updatedBotStartPayload(startPayload.payload) }) } else if let navigationController = strongSelf.effectiveNavigationController { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), botStart: startPayload)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId), botStart: startPayload)) } default: break @@ -14299,7 +14382,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } private func presentBanMessageOptions(accountPeerId: PeerId, author: Peer, messageIds: Set, options: ChatAvailableMessageActionOptions) { - let peerId = self.chatLocation.peerId + guard let peerId = self.chatLocation.peerId else { + return + } do { self.navigationActionDisposable.set((self.context.engine.peers.fetchChannelParticipant(peerId: peerId, participantId: author.id) |> deliverOnMainQueue).start(next: { [weak self] participant in @@ -15019,6 +15104,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } private func performRequestedUnpinAllMessages(count: Int, pinnedMessageId: MessageId) { + guard let peerId = self.chatLocation.peerId else { + return + } self.chatDisplayNode.historyNode.pendingUnpinnedAllMessages = true self.updateChatPresentationInterfaceState(animated: true, interactive: true, { return $0.updatedPendingUnpinnedAllMessages(true) @@ -15041,7 +15129,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G switch action { case .commit: - let _ = (strongSelf.context.engine.messages.requestUnpinAllMessages(peerId: strongSelf.chatLocation.peerId) + let _ = (strongSelf.context.engine.messages.requestUnpinAllMessages(peerId: peerId) |> deliverOnMainQueue).start(error: { _ in }, completed: { guard let strongSelf = self else { @@ -15249,6 +15337,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.themeEmoticonAndDarkAppearancePreviewPromise.set(.single((emoticon, dark))) } }, completion: { [weak self] emoticon in + guard let strongSelf = self, let peerId = peerId else { + return + } strongSelf.themeEmoticonAndDarkAppearancePreviewPromise.set(.single((emoticon ?? "", nil))) let _ = context.engine.themes.setChatTheme(peerId: peerId, emoticon: emoticon).start(completed: { [weak self] in if let strongSelf = self { diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 35cf0b67f8..fa774fe32b 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -2414,7 +2414,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { let effectiveInputText = effectivePresentationInterfaceState.interfaceState.composeInputState.inputText let trimmedInputText = effectiveInputText.string.trimmingCharacters(in: .whitespacesAndNewlines) let peerId = effectivePresentationInterfaceState.chatLocation.peerId - if peerId.namespace != Namespaces.Peer.SecretChat, let interactiveEmojis = self.interactiveEmojis, interactiveEmojis.emojis.contains(trimmedInputText) { + if peerId?.namespace != Namespaces.Peer.SecretChat, let interactiveEmojis = self.interactiveEmojis, interactiveEmojis.emojis.contains(trimmedInputText) { messages.append(.message(text: "", attributes: [], mediaReference: AnyMediaReference.standalone(media: TelegramMediaDice(emoji: trimmedInputText)), replyToMessageId: self.chatPresentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil, correlationId: nil)) } else { let inputText = convertMarkdownToAttributes(effectiveInputText) diff --git a/submodules/TelegramUI/Sources/ChatEmptyNode.swift b/submodules/TelegramUI/Sources/ChatEmptyNode.swift index bdc54d1e79..62be2e4a2c 100644 --- a/submodules/TelegramUI/Sources/ChatEmptyNode.swift +++ b/submodules/TelegramUI/Sources/ChatEmptyNode.swift @@ -42,7 +42,7 @@ private final class ChatEmptyNodeRegularChatContent: ASDisplayNode, ChatEmptyNod let text: String switch interfaceState.chatLocation { - case .peer, .replyThread: + case .peer, .replyThread, .feed: if case .scheduledMessages = interfaceState.subject { text = interfaceState.strings.ScheduledMessages_EmptyPlaceholder } else { diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 006c84467c..967024d88f 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -928,7 +928,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { let customChannelDiscussionReadState: Signal if case let .peer(peerId) = chatLocation, peerId.namespace == Namespaces.Peer.CloudChannel { - let cachedDataKey = PostboxViewKey.cachedPeerData(peerId: chatLocation.peerId) + let cachedDataKey = PostboxViewKey.cachedPeerData(peerId: peerId) let peerKey = PostboxViewKey.basicPeer(peerId) customChannelDiscussionReadState = context.account.postbox.combinedView(keys: [cachedDataKey, peerKey]) |> mapToSignal { views -> Signal in @@ -1062,7 +1062,12 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { case let .id(id): initialSearchLocation = .id(id) case let .timestamp(timestamp): - initialSearchLocation = .index(MessageIndex(id: MessageId(peerId: strongSelf.chatLocation.peerId, namespace: Namespaces.Message.Cloud, id: 1), timestamp: timestamp)) + if let peerId = strongSelf.chatLocation.peerId { + initialSearchLocation = .index(MessageIndex(id: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: 1), timestamp: timestamp)) + } else { + //TODO:implement + initialSearchLocation = .index(.absoluteUpperBound()) + } } strongSelf.chatHistoryLocationValue = ChatHistoryLocationInput(content: .InitialSearch(location: initialSearchLocation, count: 60, highlight: highlight), id: (strongSelf.chatHistoryLocationValue?.id).flatMap({ $0 + 1 }) ?? 0) } else if let subject = subject, case let .pinnedMessages(maybeMessageId) = subject, let messageId = maybeMessageId { @@ -1307,7 +1312,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { } if apply { switch chatLocation { - case .peer, .replyThread: + case .peer, .replyThread, .feed: if !context.sharedContext.immediateExperimentalUISettings.skipReadHistory { context.applyMaxReadIndex(for: chatLocation, contextHolder: chatLocationContextHolder, messageIndex: messageIndex) } @@ -1348,7 +1353,12 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { case let .id(id): initialSearchLocation = .id(id) case let .timestamp(timestamp): - initialSearchLocation = .index(MessageIndex(id: MessageId(peerId: self.chatLocation.peerId, namespace: Namespaces.Message.Cloud, id: 1), timestamp: timestamp)) + if let peerId = self.chatLocation.peerId { + initialSearchLocation = .index(MessageIndex(id: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: 1), timestamp: timestamp)) + } else { + //TODO:implement + initialSearchLocation = .index(MessageIndex.absoluteUpperBound()) + } } self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .InitialSearch(location: initialSearchLocation, count: 60, highlight: highlight), id: 0) } else if let subject = subject, case let .pinnedMessages(maybeMessageId) = subject, let messageId = maybeMessageId { @@ -2108,7 +2118,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { switch self.chatLocation { case .peer: messageIndex = maxIncomingIndex - case .replyThread: + case .replyThread, .feed: messageIndex = maxOverallIndex } @@ -2520,7 +2530,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { switch strongSelf.chatLocation { case .peer: messageIndex = incomingIndex - case .replyThread: + case .replyThread, .feed: messageIndex = overallIndex } diff --git a/submodules/TelegramUI/Sources/ChatHistorySearchContainerNode.swift b/submodules/TelegramUI/Sources/ChatHistorySearchContainerNode.swift index d26c8246e4..ad80ec2233 100644 --- a/submodules/TelegramUI/Sources/ChatHistorySearchContainerNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistorySearchContainerNode.swift @@ -69,7 +69,7 @@ private enum ChatHistorySearchEntry: Comparable, Identifiable { func item(context: AccountContext, peerId: PeerId, interaction: ChatControllerInteraction) -> ListViewItem { switch self { case let .message(message, theme, strings, dateTimeFormat, fontSize): - return ListMessageItem(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: theme, wallpaper: .builtin(WallpaperSettings())), fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, disableAnimations: false, largeEmoji: false, chatBubbleCorners: PresentationChatBubbleCorners(mainRadius: 0.0, auxiliaryRadius: 0.0, mergeBubbleCorners: false)), context: context, chatLocation: .peer(peerId), interaction: ListMessageItemInteraction(controllerInteraction: interaction), message: message, selection: .none, displayHeader: true) + return ListMessageItem(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: theme, wallpaper: .builtin(WallpaperSettings())), fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, disableAnimations: false, largeEmoji: false, chatBubbleCorners: PresentationChatBubbleCorners(mainRadius: 0.0, auxiliaryRadius: 0.0, mergeBubbleCorners: false)), context: context, chatLocation: .peer(id: peerId), interaction: ListMessageItemInteraction(controllerInteraction: interaction), message: message, selection: .none, displayHeader: true) } } } diff --git a/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift b/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift index 3072fadeec..e84fc4ac04 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift @@ -305,15 +305,15 @@ private func extractAdditionalData(view: MessageHistoryView, chatLocation: ChatL cachedDataMessages[message.id] = message } case let .totalUnreadState(totalUnreadState): - switch chatLocation { - case let .peer(peerId): - if let combinedReadStates = view.fixedReadStates { - if case let .peer(readStates) = combinedReadStates, let readState = readStates[peerId] { - readStateData[peerId] = ChatHistoryCombinedInitialReadStateData(unreadCount: readState.count, totalState: totalUnreadState, notificationSettings: notificationSettings) - } - } - case .replyThread: - break + switch chatLocation { + case let .peer(peerId): + if let combinedReadStates = view.fixedReadStates { + if case let .peer(readStates) = combinedReadStates, let readState = readStates[peerId] { + readStateData[peerId] = ChatHistoryCombinedInitialReadStateData(unreadCount: readState.count, totalState: totalUnreadState, notificationSettings: notificationSettings) + } + } + case .replyThread, .feed: + break } default: break @@ -383,7 +383,7 @@ func fetchAndPreloadReplyThreadInfo(context: AccountContext, subject: ReplyThrea let preloadSignal = preloadedChatHistoryViewForLocation( input, context: context, - chatLocation: .replyThread(replyThreadMessage), + chatLocation: .replyThread(message: replyThreadMessage), subject: nil, chatLocationContextHolder: chatLocationContextHolder, fixedCombinedReadStates: nil, diff --git a/submodules/TelegramUI/Sources/ChatInfoTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatInfoTitlePanelNode.swift index e6d6af2b09..9a00dc073e 100644 --- a/submodules/TelegramUI/Sources/ChatInfoTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatInfoTitlePanelNode.swift @@ -151,14 +151,14 @@ final class ChatInfoTitlePanelNode: ChatTitleAccessoryPanelNode { let updatedButtons: [ChatInfoTitleButton] switch interfaceState.chatLocation { - case .peer: - if let peer = interfaceState.renderedPeer?.peer { - updatedButtons = peerButtons(peer, interfaceState: interfaceState) - } else { - updatedButtons = [] - } - case .replyThread: + case .peer: + if let peer = interfaceState.renderedPeer?.peer { + updatedButtons = peerButtons(peer, interfaceState: interfaceState) + } else { updatedButtons = [] + } + case .replyThread, .feed: + updatedButtons = [] } var buttonsUpdated = false diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index b4ef4f108d..afe9766d71 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -259,20 +259,22 @@ func canReplyInChat(_ chatPresentationInterfaceState: ChatPresentationInterfaceS var canReply = false switch chatPresentationInterfaceState.chatLocation { - case .peer: - if let channel = peer as? TelegramChannel { - if case .member = channel.participationStatus { - canReply = channel.hasPermission(.sendMessages) - } - } else if let group = peer as? TelegramGroup { - if case .Member = group.membership { - canReply = true - } - } else { + case .peer: + if let channel = peer as? TelegramChannel { + if case .member = channel.participationStatus { + canReply = channel.hasPermission(.sendMessages) + } + } else if let group = peer as? TelegramGroup { + if case .Member = group.membership { canReply = true } - case .replyThread: + } else { canReply = true + } + case .replyThread: + canReply = true + case .feed: + canReply = false } return canReply } @@ -481,29 +483,29 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState canPin = false } else if messages[0].flags.intersection([.Failed, .Unsent]).isEmpty { switch chatPresentationInterfaceState.chatLocation { - case .peer, .replyThread: - if let channel = messages[0].peers[messages[0].id.peerId] as? TelegramChannel { - if !isAction { - canPin = channel.hasPermission(.pinMessages) - } - } else if let group = messages[0].peers[messages[0].id.peerId] as? TelegramGroup { - if !isAction { - switch group.role { - case .creator, .admin: - canPin = true - default: - if let defaultBannedRights = group.defaultBannedRights { - canPin = !defaultBannedRights.flags.contains(.banPinMessages) - } else { - canPin = true - } + case .peer, .replyThread, .feed: + if let channel = messages[0].peers[messages[0].id.peerId] as? TelegramChannel { + if !isAction { + canPin = channel.hasPermission(.pinMessages) + } + } else if let group = messages[0].peers[messages[0].id.peerId] as? TelegramGroup { + if !isAction { + switch group.role { + case .creator, .admin: + canPin = true + default: + if let defaultBannedRights = group.defaultBannedRights { + canPin = !defaultBannedRights.flags.contains(.banPinMessages) + } else { + canPin = true } } - } else if let _ = messages[0].peers[messages[0].id.peerId] as? TelegramUser, chatPresentationInterfaceState.explicitelyCanPinMessages { - if !isAction { - canPin = true - } } + } else if let _ = messages[0].peers[messages[0].id.peerId] as? TelegramUser, chatPresentationInterfaceState.explicitelyCanPinMessages { + if !isAction { + canPin = true + } + } } } else { canReply = false diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift index c4cce70b20..886e08f889 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -766,6 +766,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { if !isBroadcastChannel { hasAvatar = true + } else if case .feed = item.chatLocation { + hasAvatar = true } } } else if incoming { @@ -790,6 +792,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } else if incoming { hasAvatar = true } + case .feed: + hasAvatar = true } if hasAvatar { diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index f2c4d0b9ca..c0d052e71f 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -1043,7 +1043,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode var hasAvatar = false var allowFullWidth = false - let chatLocationPeerId: PeerId = item.chatLocation.peerId + let chatLocationPeerId: PeerId = item.chatLocation.peerId ?? item.content.firstMessage.id.peerId do { let peerId = chatLocationPeerId @@ -1107,6 +1107,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode if !isBroadcastChannel { hasAvatar = item.content.firstMessage.effectivelyIncoming(item.context.account.peerId) + } else if case .feed = item.chatLocation { + hasAvatar = true } } } else if incoming { diff --git a/submodules/TelegramUI/Sources/ChatMessageEventLogPreviousDescriptionContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageEventLogPreviousDescriptionContentNode.swift index 04791e6eeb..7bc6f4be86 100644 --- a/submodules/TelegramUI/Sources/ChatMessageEventLogPreviousDescriptionContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageEventLogPreviousDescriptionContentNode.swift @@ -43,7 +43,7 @@ final class ChatMessageEventLogPreviousDescriptionContentNode: ChatMessageBubble } let mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? = nil - let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, true, .peer(item.message.id.peerId), title, nil, text, messageEntities, mediaAndFlags, nil, nil, nil, true, layoutConstants, preparePosition, constrainedSize) + let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, true, .peer(id: item.message.id.peerId), title, nil, text, messageEntities, mediaAndFlags, nil, nil, nil, true, layoutConstants, preparePosition, constrainedSize) let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 8.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none) diff --git a/submodules/TelegramUI/Sources/ChatMessageEventLogPreviousLinkContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageEventLogPreviousLinkContentNode.swift index 08706ba0a5..5f81fea974 100644 --- a/submodules/TelegramUI/Sources/ChatMessageEventLogPreviousLinkContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageEventLogPreviousLinkContentNode.swift @@ -38,7 +38,7 @@ final class ChatMessageEventLogPreviousLinkContentNode: ChatMessageBubbleContent let text: String = item.message.text let mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? = nil - let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, true, .peer(item.message.id.peerId), title, nil, text, messageEntities, mediaAndFlags, nil, nil, nil, true, layoutConstants, preparePosition, constrainedSize) + let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, true, .peer(id: item.message.id.peerId), title, nil, text, messageEntities, mediaAndFlags, nil, nil, nil, true, layoutConstants, preparePosition, constrainedSize) let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 8.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none) diff --git a/submodules/TelegramUI/Sources/ChatMessageEventLogPreviousMessageContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageEventLogPreviousMessageContentNode.swift index f958b26b8a..05b3a5f9ec 100644 --- a/submodules/TelegramUI/Sources/ChatMessageEventLogPreviousMessageContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageEventLogPreviousMessageContentNode.swift @@ -43,7 +43,7 @@ final class ChatMessageEventLogPreviousMessageContentNode: ChatMessageBubbleCont } let mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? = nil - let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, true, .peer(item.message.id.peerId), title, nil, text, messageEntities, mediaAndFlags, nil, nil, nil, true, layoutConstants, preparePosition, constrainedSize) + let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, true, .peer(id: item.message.id.peerId), title, nil, text, messageEntities, mediaAndFlags, nil, nil, nil, true, layoutConstants, preparePosition, constrainedSize) let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 8.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none) diff --git a/submodules/TelegramUI/Sources/ChatMessageGameBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageGameBubbleContentNode.swift index 7235e824f7..de88910386 100644 --- a/submodules/TelegramUI/Sources/ChatMessageGameBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageGameBubbleContentNode.swift @@ -77,7 +77,7 @@ final class ChatMessageGameBubbleContentNode: ChatMessageBubbleContentNode { } } - let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, item.read, .peer(item.message.id.peerId), title, nil, item.message.text.isEmpty ? text : item.message.text, item.message.text.isEmpty ? nil : messageEntities, mediaAndFlags, nil, nil, nil, true, layoutConstants, preparePosition, constrainedSize) + let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, item.read, .peer(id: item.message.id.peerId), title, nil, item.message.text.isEmpty ? text : item.message.text, item.message.text.isEmpty ? nil : messageEntities, mediaAndFlags, nil, nil, nil, true, layoutConstants, preparePosition, constrainedSize) let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 8.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none) diff --git a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift index c4a7aac36d..3e98c8ae13 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift @@ -274,13 +274,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD let avatarInset: CGFloat var hasAvatar = false - let messagePeerId: PeerId - switch item.chatLocation { - case let .peer(peerId): - messagePeerId = peerId - case let .replyThread(replyThreadMessage): - messagePeerId = replyThreadMessage.messageId.peerId - } + let messagePeerId = item.chatLocation.peerId ?? item.content.firstMessage.id.peerId do { if messagePeerId != item.context.account.peerId { @@ -296,6 +290,8 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD if !isBroadcastChannel { hasAvatar = true + } else if case .feed = item.chatLocation { + hasAvatar = true } } } else if incoming { diff --git a/submodules/TelegramUI/Sources/ChatMessageItem.swift b/submodules/TelegramUI/Sources/ChatMessageItem.swift index 456a774b11..4bdc1d3eef 100644 --- a/submodules/TelegramUI/Sources/ChatMessageItem.swift +++ b/submodules/TelegramUI/Sources/ChatMessageItem.swift @@ -297,7 +297,7 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible { var effectiveAuthor: Peer? let displayAuthorInfo: Bool - let messagePeerId: PeerId = chatLocation.peerId + let messagePeerId: PeerId = chatLocation.peerId ?? content.firstMessage.id.peerId do { let peerId = messagePeerId diff --git a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift index 66cae8f595..a6618863c5 100644 --- a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift @@ -354,40 +354,44 @@ class ChatMessageStickerItemNode: ChatMessageItemView { var hasAvatar = false switch item.chatLocation { - case let .peer(peerId): - if !peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId) { - if peerId.isGroupOrChannel && item.message.author != nil { - var isBroadcastChannel = false - if let peer = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .broadcast = peer.info { - isBroadcastChannel = true - } - - if !isBroadcastChannel { - hasAvatar = true - } + case let .peer(peerId): + if !peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId) { + if peerId.isGroupOrChannel && item.message.author != nil { + var isBroadcastChannel = false + if let peer = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .broadcast = peer.info { + isBroadcastChannel = true } - } else if incoming { - hasAvatar = true - } - case let .replyThread(replyThreadMessage): - if replyThreadMessage.messageId.peerId != item.context.account.peerId { - if replyThreadMessage.messageId.peerId.isGroupOrChannel && item.message.author != nil { - var isBroadcastChannel = false - if let peer = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .broadcast = peer.info { - isBroadcastChannel = true - } - - if replyThreadMessage.isChannelPost, replyThreadMessage.effectiveTopId == item.message.id { - isBroadcastChannel = true - } - - if !isBroadcastChannel { - hasAvatar = true - } + + if !isBroadcastChannel { + hasAvatar = true + } else if case .feed = item.chatLocation { + hasAvatar = true } - } else if incoming { - hasAvatar = true } + } else if incoming { + hasAvatar = true + } + case let .replyThread(replyThreadMessage): + if replyThreadMessage.messageId.peerId != item.context.account.peerId { + if replyThreadMessage.messageId.peerId.isGroupOrChannel && item.message.author != nil { + var isBroadcastChannel = false + if let peer = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .broadcast = peer.info { + isBroadcastChannel = true + } + + if replyThreadMessage.isChannelPost, replyThreadMessage.effectiveTopId == item.message.id { + isBroadcastChannel = true + } + + if !isBroadcastChannel { + hasAvatar = true + } + } + } else if incoming { + hasAvatar = true + } + case .feed: + hasAvatar = true } if hasAvatar { diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift index dd65022613..105475eb41 100644 --- a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift @@ -767,7 +767,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { self.navigationActionDisposable.set((peerSignal |> take(1) |> deliverOnMainQueue).start(next: { [weak self] peer in if let strongSelf = self, let peer = peer { if peer is TelegramChannel, let navigationController = strongSelf.getNavigationController() { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), peekData: peekData, animated: true)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peer.id), peekData: peekData, animated: true)) } else { if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) { strongSelf.pushController(infoController) @@ -889,11 +889,11 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { break case let .channelMessage(peerId, messageId, timecode): if let navigationController = strongSelf.getNavigationController() { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: .message(id: .id(messageId), highlight: true, timecode: timecode))) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId), subject: .message(id: .id(messageId), highlight: true, timecode: timecode))) } case let .replyThreadMessage(replyThreadMessage, messageId): if let navigationController = strongSelf.getNavigationController() { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .replyThread(replyThreadMessage), subject: .message(id: .id(messageId), highlight: true, timecode: nil))) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .replyThread(message: replyThreadMessage), subject: .message(id: .id(messageId), highlight: true, timecode: nil))) } case let .stickerPack(name): let packReference: StickerPackReference = .name(name) diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsHistoryTransition.swift b/submodules/TelegramUI/Sources/ChatRecentActionsHistoryTransition.swift index cbd9604acf..17f7649a2f 100644 --- a/submodules/TelegramUI/Sources/ChatRecentActionsHistoryTransition.swift +++ b/submodules/TelegramUI/Sources/ChatRecentActionsHistoryTransition.swift @@ -114,7 +114,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.titleUpdated(title: new) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeAbout(prev, new): var peers = SimpleDictionary() var author: Peer? @@ -145,14 +145,14 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case .content: let peers = SimpleDictionary() let attributes: [MessageAttribute] = [] let prevMessage = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: prev, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: new, attributes: attributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.isEmpty ? .eventLogPreviousDescription(prevMessage) : nil) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.isEmpty ? .eventLogPreviousDescription(prevMessage) : nil) } case let .changeUsername(prev, new): var peers = SimpleDictionary() @@ -183,7 +183,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } let action: TelegramMediaActionType = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case .content: var previousAttributes: [MessageAttribute] = [] var attributes: [MessageAttribute] = [] @@ -202,7 +202,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let prevMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: prevText, attributes: previousAttributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: text, attributes: attributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.isEmpty ? .eventLogPreviousLink(prevMessage) : nil) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.isEmpty ? .eventLogPreviousLink(prevMessage) : nil) } case let .changePhoto(_, new): var peers = SimpleDictionary() @@ -221,7 +221,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.photoUpdated(image: photo) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .toggleInvites(value): var peers = SimpleDictionary() var author: Peer? @@ -248,7 +248,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .toggleSignatures(value): var peers = SimpleDictionary() var author: Peer? @@ -275,7 +275,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .updatePinned(message): switch self.id.contentIndex { case .header: @@ -306,7 +306,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case .content: if let message = message { var peers = SimpleDictionary() @@ -324,7 +324,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } } let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) } else { var peers = SimpleDictionary() var author: Peer? @@ -346,7 +346,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 0), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) } } case let .editMessage(prev, message): @@ -391,7 +391,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case .content: var peers = SimpleDictionary() var attributes: [MessageAttribute] = [] @@ -408,7 +408,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } } let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: filterOriginalMessageFlags(message), read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.text.isEmpty || !message.text.isEmpty ? .eventLogPreviousMessage(filterOriginalMessageFlags(prev)) : nil) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: filterOriginalMessageFlags(message), read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.text.isEmpty || !message.text.isEmpty ? .eventLogPreviousMessage(filterOriginalMessageFlags(prev)) : nil) } case let .deleteMessage(message): switch self.id.contentIndex { @@ -434,7 +434,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case .content: var peers = SimpleDictionary() var attributes: [MessageAttribute] = [] @@ -458,7 +458,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } } let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) } case .participantJoin, .participantLeave: var peers = SimpleDictionary() @@ -476,7 +476,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { action = TelegramMediaActionType.removedMembers(peerIds: [self.entry.event.peerId]) } let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .participantInvite(participant): var peers = SimpleDictionary() var author: Peer? @@ -493,7 +493,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action: TelegramMediaActionType action = TelegramMediaActionType.addedMembers(peerIds: [participant.peer.id]) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .participantToggleBan(prev, new): var peers = SimpleDictionary() var attributes: [MessageAttribute] = [] @@ -623,7 +623,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } } let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: text, attributes: attributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .participantToggleAdmin(prev, new): var peers = SimpleDictionary() var attributes: [MessageAttribute] = [] @@ -856,7 +856,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } } let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: text, attributes: attributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeStickerPack(_, new): var peers = SimpleDictionary() var author: Peer? @@ -885,7 +885,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .togglePreHistoryHidden(value): var peers = SimpleDictionary() var author: Peer? @@ -915,7 +915,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .updateDefaultBannedRights(prev, new): var peers = SimpleDictionary() var attributes: [MessageAttribute] = [] @@ -973,7 +973,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } } let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: text, attributes: attributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .pollStopped(message): switch self.id.contentIndex { case .header: @@ -1001,7 +1001,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case .content: var peers = SimpleDictionary() var attributes: [MessageAttribute] = [] @@ -1018,7 +1018,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } } let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: message.author, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: filterOriginalMessageFlags(message), read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: nil) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: filterOriginalMessageFlags(message), read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: nil) } case let .linkedPeerUpdated(previous, updated): var peers = SimpleDictionary() @@ -1074,7 +1074,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeGeoLocation(_, updated): var peers = SimpleDictionary() var author: Peer? @@ -1096,12 +1096,12 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let mediaMap = TelegramMediaMap(latitude: updated.latitude, longitude: updated.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: text, attributes: [], media: [mediaMap], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) } else { let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) } case let .updateSlowmode(_, newValue): var peers = SimpleDictionary() @@ -1132,7 +1132,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case .startGroupCall, .endGroupCall: var peers = SimpleDictionary() var author: Peer? @@ -1169,7 +1169,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .groupCallUpdateParticipantMuteStatus(participantId, isMuted): var peers = SimpleDictionary() var author: Peer? @@ -1203,7 +1203,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .updateGroupCallSettings(joinMuted): var peers = SimpleDictionary() var author: Peer? @@ -1232,7 +1232,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .groupCallUpdateParticipantVolume(participantId, volume): var peers = SimpleDictionary() var author: Peer? @@ -1263,7 +1263,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .deleteExportedInvitation(invite): var peers = SimpleDictionary() var author: Peer? @@ -1289,7 +1289,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .revokeExportedInvitation(invite): var peers = SimpleDictionary() var author: Peer? @@ -1315,7 +1315,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .editExportedInvitation(_, updatedInvite): var peers = SimpleDictionary() var author: Peer? @@ -1341,7 +1341,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .participantJoinedViaInvite(invite): var peers = SimpleDictionary() var author: Peer? @@ -1367,7 +1367,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeHistoryTTL(_, updatedValue): var peers = SimpleDictionary() var author: Peer? @@ -1398,7 +1398,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeAvailableReactions(_, updatedValue): var peers = SimpleDictionary() var author: Peer? @@ -1430,7 +1430,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeTheme(_, updatedValue): var peers = SimpleDictionary() var author: Peer? @@ -1461,7 +1461,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .participantJoinByRequest(invite, approvedBy): var peers = SimpleDictionary() var author: Peer? @@ -1494,7 +1494,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .toggleCopyProtection(value): var peers = SimpleDictionary() var author: Peer? @@ -1521,7 +1521,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .sendMessage(message): switch self.id.contentIndex { case .header: @@ -1546,7 +1546,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { let action = TelegramMediaActionType.customText(text: text, entities: entities) let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case .content: var peers = SimpleDictionary() var attributes: [MessageAttribute] = [] @@ -1563,7 +1563,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } } let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) } } } diff --git a/submodules/TelegramUI/Sources/ChatSearchNavigationContentNode.swift b/submodules/TelegramUI/Sources/ChatSearchNavigationContentNode.swift index 62d0acc049..b590793441 100644 --- a/submodules/TelegramUI/Sources/ChatSearchNavigationContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatSearchNavigationContentNode.swift @@ -32,8 +32,8 @@ final class ChatSearchNavigationContentNode: NavigationBarContentNode { self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: theme, hasBackground: false, hasSeparator: false), strings: strings, fieldStyle: .modern) let placeholderText: String switch chatLocation { - case .peer, .replyThread: - placeholderText = strings.Conversation_SearchPlaceholder + case .peer, .replyThread, .feed: + placeholderText = strings.Conversation_SearchPlaceholder } self.searchBar.placeholderString = NSAttributedString(string: placeholderText, font: searchBarFont, textColor: theme.rootController.navigationSearchBar.inputPlaceholderTextColor) @@ -104,8 +104,8 @@ final class ChatSearchNavigationContentNode: NavigationBarContentNode { self.searchBar.prefixString = nil let placeholderText: String switch self.chatLocation { - case .peer, .replyThread: - placeholderText = self.strings.Conversation_SearchPlaceholder + case .peer, .replyThread, .feed: + placeholderText = self.strings.Conversation_SearchPlaceholder } self.searchBar.placeholderString = NSAttributedString(string: placeholderText, font: searchBarFont, textColor: theme.rootController.navigationSearchBar.inputPlaceholderTextColor) case .members: diff --git a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift index 5c862878df..b5b3e34d4d 100644 --- a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift +++ b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift @@ -233,7 +233,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe switch item.content { case let .peer(messages, peer, _, _, _, _, _, _, _, _, _, _, _): if let message = messages.first { - let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peer.peerId), subject: .message(id: .id(message.id), highlight: true, timecode: nil), botStart: nil, mode: .standard(previewing: true)) + let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peer.peerId), subject: .message(id: .id(message.id), highlight: true, timecode: nil), botStart: nil, mode: .standard(previewing: true)) chatController.canReadHistory.set(false) let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: .single(ContextController.Items(content: .list([]))), gesture: gesture) presentInGlobalOverlay(contextController) diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 0892e0dcde..4bf4dcea28 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -452,7 +452,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { self.presentationInterfaceState = presentationInterfaceState var hasSpoilers = true - if presentationInterfaceState.chatLocation.peerId.namespace == Namespaces.Peer.SecretChat { + if presentationInterfaceState.chatLocation.peerId?.namespace == Namespaces.Peer.SecretChat { hasSpoilers = false } self.inputMenu = TextInputMenu(hasSpoilers: hasSpoilers) diff --git a/submodules/TelegramUI/Sources/ComposeController.swift b/submodules/TelegramUI/Sources/ComposeController.swift index 626dbf77d1..daf64b2804 100644 --- a/submodules/TelegramUI/Sources/ComposeController.swift +++ b/submodules/TelegramUI/Sources/ComposeController.swift @@ -163,7 +163,7 @@ public class ComposeControllerImpl: ViewController, ComposeController { strongSelf.createActionDisposable.set((strongSelf.context.engine.peers.createSecretChat(peerId: peer.id) |> deliverOnMainQueue).start(next: { peerId in if let strongSelf = self, let controller = controller { controller.displayNavigationActivity = false - (controller.navigationController as? NavigationController)?.replaceAllButRootController(ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(peerId)), animated: true) + (controller.navigationController as? NavigationController)?.replaceAllButRootController(ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(id: peerId)), animated: true) } }, error: { error in if let strongSelf = self, let controller = controller { @@ -207,7 +207,7 @@ public class ComposeControllerImpl: ViewController, ComposeController { if let peer = peer { DispatchQueue.main.async { if let navigationController = strongSelf.navigationController as? NavigationController { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id))) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peer.id))) } } } else { @@ -305,7 +305,7 @@ public class ComposeControllerImpl: ViewController, ComposeController { } private func openPeer(peerId: PeerId) { - (self.navigationController as? NavigationController)?.replaceTopController(ChatControllerImpl(context: self.context, chatLocation: .peer(peerId)), animated: true) + (self.navigationController as? NavigationController)?.replaceTopController(ChatControllerImpl(context: self.context, chatLocation: .peer(id: peerId)), animated: true) } @objc private func cancelPressed() { diff --git a/submodules/TelegramUI/Sources/CreateGroupController.swift b/submodules/TelegramUI/Sources/CreateGroupController.swift index b284e650d0..ffc73fa0b3 100644 --- a/submodules/TelegramUI/Sources/CreateGroupController.swift +++ b/submodules/TelegramUI/Sources/CreateGroupController.swift @@ -510,7 +510,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId] dismissImpl?() }) } else { - let controller = ChatControllerImpl(context: context, chatLocation: .peer(peerId)) + let controller = ChatControllerImpl(context: context, chatLocation: .peer(id: peerId)) replaceControllerImpl?(controller) } } diff --git a/submodules/TelegramUI/Sources/NavigateToChatController.swift b/submodules/TelegramUI/Sources/NavigateToChatController.swift index f997a38598..3374284a07 100644 --- a/submodules/TelegramUI/Sources/NavigateToChatController.swift +++ b/submodules/TelegramUI/Sources/NavigateToChatController.swift @@ -136,14 +136,16 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam if let item = item as? ChatMessageNotificationItem { for message in item.messages { switch params.chatLocation { - case let .peer(peerId): - if message.id.peerId == peerId { - return true - } - case let .replyThread(replyThreadMessage): - if message.id.peerId == replyThreadMessage.messageId.peerId { - return true - } + case let .peer(peerId): + if message.id.peerId == peerId { + return true + } + case let .replyThread(replyThreadMessage): + if message.id.peerId == replyThreadMessage.messageId.peerId { + return true + } + case .feed: + break } } } diff --git a/submodules/TelegramUI/Sources/OpenChatMessage.swift b/submodules/TelegramUI/Sources/OpenChatMessage.swift index 94bdfdb06d..fc2cbdafa9 100644 --- a/submodules/TelegramUI/Sources/OpenChatMessage.swift +++ b/submodules/TelegramUI/Sources/OpenChatMessage.swift @@ -137,7 +137,7 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool { } else if params.standalone { location = .recentActions(params.message) } else { - location = .messages(chatLocation: params.chatLocation ?? .peer(params.message.id.peerId), tagMask: .voiceOrInstantVideo, at: params.message.id) + location = .messages(chatLocation: params.chatLocation ?? .peer(id: params.message.id.peerId), tagMask: .voiceOrInstantVideo, at: params.message.id) } playerType = .voice } else if file.isMusic && params.message.tags.contains(.music) { @@ -146,7 +146,7 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool { } else if params.standalone { location = .recentActions(params.message) } else { - location = .messages(chatLocation: params.chatLocation ?? .peer(params.message.id.peerId), tagMask: .music, at: params.message.id) + location = .messages(chatLocation: params.chatLocation ?? .peer(id: params.message.id.peerId), tagMask: .music, at: params.message.id) } playerType = .music } else { diff --git a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift index fc65e751bb..44263f0cd9 100644 --- a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift @@ -86,7 +86,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur let _ = (context.engine.messages.requestStartBotInGroup(botPeerId: botPeerId, groupPeerId: peerId, payload: payload) |> deliverOnMainQueue).start(next: { result in if let navigationController = navigationController { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId))) } switch result { case let .channelParticipant(participant): @@ -243,10 +243,10 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur return currentState.withUpdatedComposeInputState(textInputState) }) |> deliverOnMainQueue).start(completed: { - navigationController?.pushViewController(ChatControllerImpl(context: context, chatLocation: .peer(peerId))) + navigationController?.pushViewController(ChatControllerImpl(context: context, chatLocation: .peer(id: peerId))) }) } else { - navigationController?.pushViewController(ChatControllerImpl(context: context, chatLocation: .peer(peerId))) + navigationController?.pushViewController(ChatControllerImpl(context: context, chatLocation: .peer(id: peerId))) } } @@ -474,7 +474,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur case let .joinVoiceChat(peerId, invite): dismissInput() if let navigationController = navigationController { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), completion: { chatController in + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId), completion: { chatController in guard let chatController = chatController as? ChatControllerImpl else { return } diff --git a/submodules/TelegramUI/Sources/OpenUrl.swift b/submodules/TelegramUI/Sources/OpenUrl.swift index 40d308e359..a992e05e78 100644 --- a/submodules/TelegramUI/Sources/OpenUrl.swift +++ b/submodules/TelegramUI/Sources/OpenUrl.swift @@ -200,12 +200,12 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur case let .chat(_, subject, peekData): context.sharedContext.applicationBindings.dismissNativeController() if let navigationController = navigationController { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), subject: subject, peekData: peekData)) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId), subject: subject, peekData: peekData)) } case let .withBotStartPayload(payload): context.sharedContext.applicationBindings.dismissNativeController() if let navigationController = navigationController { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), botStart: payload)) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId), botStart: payload)) } default: break diff --git a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift index f34f83fb66..9026c3295d 100644 --- a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift +++ b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift @@ -192,7 +192,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu self.isGlobalSearch = false } - self.historyNode = ChatHistoryListNode(context: context, updatedPresentationData: (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: .peer(peerId), chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, source: source, subject: .message(id: .id(initialMessageId), highlight: true, timecode: nil), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch)) + self.historyNode = ChatHistoryListNode(context: context, updatedPresentationData: (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: .peer(id: peerId), chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, source: source, subject: .message(id: .id(initialMessageId), highlight: true, timecode: nil), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch)) self.historyNode.clipsToBounds = true super.init() @@ -534,7 +534,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu } let chatLocationContextHolder = Atomic(value: nil) - let historyNode = ChatHistoryListNode(context: self.context, updatedPresentationData: (self.context.sharedContext.currentPresentationData.with({ $0 }), self.context.sharedContext.presentationData), chatLocation: .peer(self.peerId), chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, subject: .message(id: .id(messageId), highlight: true, timecode: nil), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch)) + let historyNode = ChatHistoryListNode(context: self.context, updatedPresentationData: (self.context.sharedContext.currentPresentationData.with({ $0 }), self.context.sharedContext.presentationData), chatLocation: .peer(id: self.peerId), chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, subject: .message(id: .id(messageId), highlight: true, timecode: nil), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch)) historyNode.clipsToBounds = true historyNode.preloadPages = true historyNode.stackFromBottom = true diff --git a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift index 799abd9e13..c988df1d28 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift @@ -70,7 +70,7 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode { self.selectedMessagesPromise.set(.single(self.selectedMessages)) let chatLocationContextHolder = Atomic(value: nil) - self.listNode = ChatHistoryListNode(context: context, updatedPresentationData: updatedPresentationData ?? (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: .peer(peerId), chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, subject: nil, controllerInteraction: chatControllerInteraction, selectedMessages: self.selectedMessagesPromise.get(), mode: .list(search: false, reversed: false, displayHeaders: .allButLast, hintLinks: tagMask == .webPage, isGlobalSearch: false)) + self.listNode = ChatHistoryListNode(context: context, updatedPresentationData: updatedPresentationData ?? (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: .peer(id: peerId), chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, subject: nil, controllerInteraction: chatControllerInteraction, selectedMessages: self.selectedMessagesPromise.get(), mode: .list(search: false, reversed: false, displayHeaders: .allButLast, hintLinks: tagMask == .webPage, isGlobalSearch: false)) self.listNode.clipsToBounds = true self.listNode.defaultToSynchronousTransactionWhileScrolling = true self.listNode.scroller.bounces = false @@ -330,7 +330,7 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode { } if let id = state.id as? PeerMessagesMediaPlaylistItemId { if type == .music { - let signal = strongSelf.context.sharedContext.messageFromPreloadedChatHistoryViewForLocation(id: id.messageId, location: ChatHistoryLocationInput(content: .InitialSearch(location: .id(id.messageId), count: 60, highlight: true), id: 0), context: strongSelf.context, chatLocation: .peer(id.messageId.peerId), subject: nil, chatLocationContextHolder: Atomic(value: nil), tagMask: MessageTags.music) + let signal = strongSelf.context.sharedContext.messageFromPreloadedChatHistoryViewForLocation(id: id.messageId, location: ChatHistoryLocationInput(content: .InitialSearch(location: .id(id.messageId), count: 60, highlight: true), id: 0), context: strongSelf.context, chatLocation: .peer(id: id.messageId.peerId), subject: nil, chatLocationContextHolder: Atomic(value: nil), tagMask: MessageTags.music) var cancelImpl: (() -> Void)? let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } diff --git a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift index 726a0a5d43..af595e150d 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift @@ -1698,7 +1698,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro self.itemGridBinding = SparseItemGridBindingImpl( context: context, - chatLocation: .peer(peerId), + chatLocation: .peer(id: peerId), useListItems: useListItems, listItemInteraction: listItemInteraction, chatControllerInteraction: chatControllerInteraction, diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift index 17eb17eefc..6c8ed5a5f4 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift @@ -251,7 +251,7 @@ private func peerInfoAvailableMediaPanes(context: AccountContext, peerId: PeerId let loadedOnce = Atomic(value: false) return combineLatest(queue: .mainQueue(), tags.map { tagAndKey -> Signal<(PeerInfoPaneKey, PaneState), NoError> in let (tag, key) = tagAndKey - return context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId), index: .upperBound, anchorIndex: .upperBound, count: 20, clipHoles: false, fixedCombinedReadStates: nil, tagMask: tag) + return context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: peerId), index: .upperBound, anchorIndex: .upperBound, count: 20, clipHoles: false, fixedCombinedReadStates: nil, tagMask: tag) |> map { (view, _, _) -> (PeerInfoPaneKey, PaneState) in if view.entries.isEmpty { if view.isLoading { diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 3aa5a627ac..da083e316e 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -361,7 +361,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode { self.backgroundNode.updateColor(color: presentationData.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate) self.separatorNode.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor - let interfaceState = ChatPresentationInterfaceState(chatWallpaper: .color(0), theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, limitsConfiguration: .defaultValue, fontSize: .regular, bubbleCorners: PresentationChatBubbleCorners(mainRadius: 16.0, auxiliaryRadius: 8.0, mergeBubbleCorners: true), accountPeerId: self.context.account.peerId, mode: .standard(previewing: false), chatLocation: .peer(self.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil) + let interfaceState = ChatPresentationInterfaceState(chatWallpaper: .color(0), theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, limitsConfiguration: .defaultValue, fontSize: .regular, bubbleCorners: PresentationChatBubbleCorners(mainRadius: 16.0, auxiliaryRadius: 8.0, mergeBubbleCorners: true), accountPeerId: self.context.account.peerId, mode: .standard(previewing: false), chatLocation: .peer(id: self.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil) let panelHeight = self.selectionPanel.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: UIEdgeInsets(), maxHeight: 0.0, isSecondary: false, transition: transition, interfaceState: interfaceState, metrics: layout.metrics) transition.updateFrame(node: self.selectionPanel, frame: CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: panelHeight))) @@ -1805,7 +1805,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate c.dismiss(completion: { if let strongSelf = self, let navigationController = strongSelf.controller?.navigationController as? NavigationController { let currentPeerId = strongSelf.peerId - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(currentPeerId), subject: .message(id: .id(message.id), highlight: true, timecode: nil), keepStack: .always, useExisting: false, purposefulAction: { + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: currentPeerId), subject: .message(id: .id(message.id), highlight: true, timecode: nil), keepStack: .always, useExisting: false, purposefulAction: { var viewControllers = navigationController.viewControllers var indexesToRemove = Set() var keptCurrentChatController = false @@ -1938,7 +1938,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate return } - let _ = (chatMediaListPreviewControllerData(context: strongSelf.context, chatLocation: .peer(message.id.peerId), chatLocationContextHolder: Atomic(value: nil), message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: strongSelf.controller?.navigationController as? NavigationController) + let _ = (chatMediaListPreviewControllerData(context: strongSelf.context, chatLocation: .peer(id: message.id.peerId), chatLocationContextHolder: Atomic(value: nil), message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: strongSelf.controller?.navigationController as? NavigationController) |> deliverOnMainQueue).start(next: { previewData in guard let strongSelf = self else { gesture?.cancel() @@ -1955,7 +1955,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate c.dismiss(completion: { if let strongSelf = self, let navigationController = strongSelf.controller?.navigationController as? NavigationController { let currentPeerId = strongSelf.peerId - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(currentPeerId), subject: .message(id: .id(message.id), highlight: true, timecode: nil), keepStack: .always, useExisting: false, purposefulAction: { + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: currentPeerId), subject: .message(id: .id(message.id), highlight: true, timecode: nil), keepStack: .always, useExisting: false, purposefulAction: { var viewControllers = navigationController.viewControllers var indexesToRemove = Set() var keptCurrentChatController = false @@ -2290,7 +2290,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate return } let presentationData = strongSelf.presentationData - let chatController = strongSelf.context.sharedContext.makeChatController(context: context, chatLocation: .peer(peer.id), subject: nil, botStart: nil, mode: .standard(previewing: true)) + let chatController = strongSelf.context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .standard(previewing: true)) chatController.canReadHistory.set(false) let items: [ContextMenuItem] = [ .action(ContextMenuActionItem(text: presentationData.strings.Conversation_LinkDialogOpen, icon: { _ in nil }, action: { _, f in @@ -3273,7 +3273,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate } switch navigation { case let .chat(_, subject, peekData): - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: subject, keepStack: .always, peekData: peekData)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId), subject: subject, keepStack: .always, peekData: peekData)) case .info: strongSelf.navigationActionDisposable.set((strongSelf.context.account.postbox.loadedPeerWithId(peerId) |> take(1) @@ -3285,7 +3285,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate } })) case let .withBotStartPayload(startPayload): - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), botStart: startPayload)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId), botStart: startPayload)) default: break } @@ -3342,11 +3342,11 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate switch navigation { case .default: if let navigationController = self.controller?.navigationController as? NavigationController { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peerId), keepStack: .always)) + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(id: peerId), keepStack: .always)) } case let .chat(_, subject, peekData): if let navigationController = self.controller?.navigationController as? NavigationController { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peerId), subject: subject, keepStack: .always, peekData: peekData)) + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(id: peerId), subject: subject, keepStack: .always, peekData: peekData)) } case .info: self.resolveUrlDisposable.set((self.context.account.postbox.loadedPeerWithId(peerId) @@ -3360,7 +3360,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate })) case let .withBotStartPayload(startPayload): if let navigationController = self.controller?.navigationController as? NavigationController { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peerId), botStart: startPayload)) + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(id: peerId), botStart: startPayload)) } } } @@ -3479,7 +3479,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate switch key { case .message: if let navigationController = controller.navigationController as? NavigationController { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(self.peerId), keepStack: self.nearbyPeerDistance != nil ? .always : .default, peerNearbyData: self.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0) }), completion: { [weak self] _ in + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(id: self.peerId), keepStack: self.nearbyPeerDistance != nil ? .always : .default, peerNearbyData: self.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0) }), completion: { [weak self] _ in if let strongSelf = self, strongSelf.nearbyPeerDistance != nil { var viewControllers = navigationController.viewControllers viewControllers = viewControllers.filter { controller in @@ -3495,7 +3495,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate case .discussion: if let cachedData = self.data?.cachedData as? CachedChannelData, case let .known(maybeLinkedDiscussionPeerId) = cachedData.linkedDiscussionPeerId, let linkedDiscussionPeerId = maybeLinkedDiscussionPeerId { if let navigationController = controller.navigationController as? NavigationController { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(linkedDiscussionPeerId))) + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(id: linkedDiscussionPeerId))) } } case .call: @@ -3891,7 +3891,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate private func openChatWithMessageSearch() { if let navigationController = (self.controller?.navigationController as? NavigationController) { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(self.peerId), keepStack: self.nearbyPeerDistance != nil ? .always : .default, activateMessageSearch: (.everything, ""), peerNearbyData: self.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0) }), completion: { [weak self] _ in + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(id: self.peerId), keepStack: self.nearbyPeerDistance != nil ? .always : .default, activateMessageSearch: (.everything, ""), peerNearbyData: self.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0) }), completion: { [weak self] _ in if let strongSelf = self, strongSelf.nearbyPeerDistance != nil { var viewControllers = navigationController.viewControllers viewControllers = viewControllers.filter { controller in @@ -3908,14 +3908,14 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate private func openChatForReporting(_ reason: ReportReason) { if let navigationController = (self.controller?.navigationController as? NavigationController) { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(self.peerId), keepStack: .default, reportReason: reason, completion: { _ in + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(id: self.peerId), keepStack: .default, reportReason: reason, completion: { _ in })) } } private func openChatForThemeChange() { if let navigationController = (self.controller?.navigationController as? NavigationController) { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(self.peerId), keepStack: .default, changeColors: true, completion: { _ in + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(id: self.peerId), keepStack: .default, changeColors: true, completion: { _ in })) } } @@ -3993,7 +3993,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate return } if let navigationController = (strongSelf.controller?.navigationController as? NavigationController) { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId))) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId))) } }, error: { error in guard let strongSelf = self else { @@ -4353,7 +4353,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate private func openChat() { if let navigationController = self.controller?.navigationController as? NavigationController { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(self.peerId), keepStack: self.nearbyPeerDistance != nil ? .always : .default, peerNearbyData: self.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0) }), completion: { [weak self] _ in + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(id: self.peerId), keepStack: self.nearbyPeerDistance != nil ? .always : .default, peerNearbyData: self.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0) }), completion: { [weak self] _ in if let strongSelf = self, strongSelf.nearbyPeerDistance != nil { var viewControllers = navigationController.viewControllers viewControllers = viewControllers.filter { controller in @@ -4396,7 +4396,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate if !block { let _ = enqueueMessages(account: strongSelf.context.account, peerId: peer.id, messages: [.message(text: "/start", attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)]).start() if let navigationController = strongSelf.controller?.navigationController as? NavigationController { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id))) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peer.id))) } } } else { @@ -4805,7 +4805,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate let _ = enqueueMessages(account: strongSelf.context.account, peerId: peer.id, messages: [.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)]).start() if let navigationController = strongSelf.controller?.navigationController as? NavigationController { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(strongSelf.peerId))) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: strongSelf.peerId))) } }) } @@ -5563,7 +5563,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate self.controller?.push(proxySettingsController(context: self.context)) case .savedMessages: if let controller = self.controller, let navigationController = controller.navigationController as? NavigationController { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(self.context.account.peerId))) + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(id: self.context.account.peerId))) } case .recentCalls: self.controller?.push(CallListController(context: context, mode: .navigation)) @@ -5624,7 +5624,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { [weak self] in self?.supportPeerDisposable.set((supportPeer.get() |> take(1) |> deliverOnMainQueue).start(next: { [weak self] peerId in if let strongSelf = self, let peerId = peerId { - strongSelf.controller?.push(strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peerId), subject: nil, botStart: nil, mode: .standard(previewing: false))) + strongSelf.controller?.push(strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, mode: .standard(previewing: false))) } })) })]), in: .window(.root)) @@ -5702,7 +5702,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate self.tipsPeerDisposable.set((self.context.engine.peers.resolvePeerByName(name: self.presentationData.strings.Settings_TipsUsername) |> deliverOnMainQueue).start(next: { [weak controller] peer in controller?.dismiss() if let peer = peer, let navigationController = navigationController { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer.id))) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peer.id))) } })) } @@ -5987,7 +5987,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone, nil, nil) if let navigationController = strongSelf.controller?.navigationController as? NavigationController { - let chatController = ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(peerId)) + let chatController = ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(id: peerId)) var viewControllers = navigationController.viewControllers viewControllers.insert(chatController, at: viewControllers.count - 1) navigationController.setViewControllers(viewControllers, animated: false) @@ -6354,7 +6354,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate navigationController: navigationController, chatController: nil, context: strongSelf.context, - chatLocation: .peer(strongSelf.peerId), + chatLocation: .peer(id: strongSelf.peerId), subject: .message(id: .id(index.id), highlight: false, timecode: nil), botStart: nil, updateTextInputState: nil, @@ -6377,7 +6377,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate )) }))) - let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(strongSelf.peerId), subject: .message(id: .id(index.id), highlight: false, timecode: nil), botStart: nil, mode: .standard(previewing: true)) + let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: strongSelf.peerId), subject: .message(id: .id(index.id), highlight: false, timecode: nil), botStart: nil, mode: .standard(previewing: true)) chatController.canReadHistory.set(false) let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: sourceNode, sourceRect: sourceRect, passthroughTouches: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) strongSelf.controller?.presentInGlobalOverlay(contextController) @@ -7513,7 +7513,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen { return } - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), animated: true, completion: { _ in + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peer.id), animated: true, completion: { _ in })) }))) } diff --git a/submodules/TelegramUI/Sources/PeerInfoGifPaneNode.swift b/submodules/TelegramUI/Sources/PeerInfoGifPaneNode.swift index 86728907f7..10e451b22b 100644 --- a/submodules/TelegramUI/Sources/PeerInfoGifPaneNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfoGifPaneNode.swift @@ -922,7 +922,7 @@ final class PeerInfoGifPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScrollViewDe return } self.isRequestingView = true - self.listDisposable.set((self.context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(self.peerId), index: .upperBound, anchorIndex: .upperBound, count: self.numberOfItemsToRequest, fixedCombinedReadStates: nil, tagMask: tagMaskForType(self.contentType)) + self.listDisposable.set((self.context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: self.peerId), index: .upperBound, anchorIndex: .upperBound, count: self.numberOfItemsToRequest, fixedCombinedReadStates: nil, tagMask: tagMaskForType(self.contentType)) |> deliverOnMainQueue).start(next: { [weak self] (view, updateType, _) in guard let strongSelf = self else { return diff --git a/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift b/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift index 996269fd3a..22a46e8352 100644 --- a/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift +++ b/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift @@ -559,14 +559,14 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { case let .index(index): switch self.messagesLocation { case let .messages(chatLocation, tagMask, _): - let inputIndex: Signal + let inputIndex: Signal let looping = self.looping switch self.order { case .regular, .reversed: inputIndex = .single(index) case .random: var playbackStack = self.playbackStack - inputIndex = self.context.account.postbox.transaction { transaction -> MessageIndex in + inputIndex = self.context.account.postbox.transaction { transaction -> MessageIndex? in if case let .random(previous) = navigation, previous { let _ = playbackStack.pop() while true { @@ -579,11 +579,19 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { } } } - return transaction.findRandomMessage(peerId: chatLocation.peerId, namespace: Namespaces.Message.Cloud, tag: tagMask, ignoreIds: (playbackStack.ids, playbackStack.set)) ?? index + + if let peerId = chatLocation.peerId { + return transaction.findRandomMessage(peerId: peerId, namespace: Namespaces.Message.Cloud, tag: tagMask, ignoreIds: (playbackStack.ids, playbackStack.set)) ?? index + } else { + return nil + } } } let historySignal = inputIndex |> mapToSignal { inputIndex -> Signal<(Message, [Message])?, NoError> in + guard let inputIndex = inputIndex else { + return .single(nil) + } return self.context.account.postbox.aroundMessageHistoryViewForLocation(self.context.chatLocationInput(for: chatLocation, contextHolder: self.chatLocationContextHolder ?? Atomic(value: nil)), anchor: .index(inputIndex), ignoreMessagesInTimestampRange: nil, count: 10, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tagMask, appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: []) |> mapToSignal { view -> Signal<(Message, [Message])?, NoError> in let position: NavigatedMessageFromViewPosition diff --git a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift index 26941aefaa..c433a90715 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift @@ -91,7 +91,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { self.presentationData = presentationData - self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: .builtin(WallpaperSettings()), theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.context.account.peerId, mode: .standard(previewing: false), chatLocation: .peer(PeerId(0)), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil) + self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: .builtin(WallpaperSettings()), theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.context.account.peerId, mode: .standard(previewing: false), chatLocation: .peer(id: PeerId(0)), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil) self.presentationInterfaceState = self.presentationInterfaceState.updatedInterfaceState { $0.withUpdatedForwardMessageIds(forwardedMessageIds) } diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index fca422eabb..8c00a2f4b8 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -1337,10 +1337,10 @@ public final class SharedAccountContextImpl: SharedAccountContext { let chatLocation: ChatLocation if messages.count > 1 { content = .group(messages: messages.map { ($0, true, .none, entryAttributes, nil) }) - chatLocation = .peer(messages.first!.id.peerId) + chatLocation = .peer(id: messages.first!.id.peerId) } else { content = .message(message: messages.first!, read: true, selection: .none, attributes: entryAttributes, location: nil) - chatLocation = .peer(messages.first!.id.peerId) + chatLocation = .peer(id: messages.first!.id.peerId) } return ChatMessageItem(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameOrder, disableAnimations: false, largeEmoji: false, chatBubbleCorners: chatBubbleCorners, animatedEmojiScale: 1.0, isPreview: true), context: context, chatLocation: chatLocation, associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false, subject: nil, contactsPeerIds: Set(), animatedEmojiStickers: [:], forcedResourceStatus: forcedResourceStatus, availableReactions: availableReactions, defaultReaction: nil), controllerInteraction: controllerInteraction, content: content, disableDate: true, additionalContent: nil) diff --git a/submodules/TelegramUI/Sources/TextLinkHandling.swift b/submodules/TelegramUI/Sources/TextLinkHandling.swift index fe46336d3b..ee5354f0b7 100644 --- a/submodules/TelegramUI/Sources/TextLinkHandling.swift +++ b/submodules/TelegramUI/Sources/TextLinkHandling.swift @@ -27,7 +27,7 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate switch navigation { case let .chat(_, subject, peekData): if let navigationController = controller?.navigationController as? NavigationController { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), subject: subject, keepStack: .always, peekData: peekData)) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId), subject: subject, keepStack: .always, peekData: peekData)) } case .info: let peerSignal: Signal @@ -59,7 +59,7 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate openResolvedPeerImpl(peerId, navigation) case let .channelMessage(peerId, messageId, timecode): if let navigationController = controller.navigationController as? NavigationController { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), subject: .message(id: .id(messageId), highlight: true, timecode: timecode))) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId), subject: .message(id: .id(messageId), highlight: true, timecode: timecode))) } case let .replyThreadMessage(replyThreadMessage, messageId): if let navigationController = controller.navigationController as? NavigationController { diff --git a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIWindow+OrientationChange.m b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIWindow+OrientationChange.m index d2e3aecece..5374f427b6 100644 --- a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIWindow+OrientationChange.m +++ b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIWindow+OrientationChange.m @@ -45,7 +45,7 @@ static bool _isDeviceRotating = false; } CGSize windowSize = CGSizeZero; CGFloat windowRotation = 0.0; - bool landscape = false; + __unused bool landscape = false; switch (orientation) { case UIInterfaceOrientationPortrait: windowSize = screenSize; diff --git a/submodules/Utils/BUILD b/submodules/Utils/BUILD new file mode 100644 index 0000000000..e69de29bb2 diff --git a/submodules/Utils/RangeSet/BUILD b/submodules/Utils/RangeSet/BUILD new file mode 100644 index 0000000000..6e55c96603 --- /dev/null +++ b/submodules/Utils/RangeSet/BUILD @@ -0,0 +1,15 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "RangeSet", + module_name = "RangeSet", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/Utils/RangeSet/Sources/CollectionExtensions.swift b/submodules/Utils/RangeSet/Sources/CollectionExtensions.swift new file mode 100644 index 0000000000..822c418542 --- /dev/null +++ b/submodules/Utils/RangeSet/Sources/CollectionExtensions.swift @@ -0,0 +1,269 @@ +//===----------------------------------------------------------*- swift -*-===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// + +// MARK: Subscripts + +extension Collection { + /// Accesses a view of this collection with the elements at the given + /// indices. + /// + /// - Parameter subranges: The indices of the elements to retrieve from this + /// collection. + /// - Returns: A collection of the elements at the positions in `subranges`. + /// + /// - Complexity: O(1) + public subscript(subranges: RangeSet) -> DiscontiguousSlice { + DiscontiguousSlice(base: self, subranges: subranges) + } +} + +extension MutableCollection { + /// Accesses a mutable view of this collection with the elements at the + /// given indices. + /// + /// - Parameter subranges: The ranges of the elements to retrieve from this + /// collection. + /// - Returns: A collection of the elements at the positions in `subranges`. + /// + /// - Complexity: O(1) to access the elements, O(*m*) to mutate the + /// elements at the positions in `subranges`, where *m* is the number of + /// elements indicated by `subranges`. + public subscript(subranges: RangeSet) -> DiscontiguousSlice { + get { + DiscontiguousSlice(base: self, subranges: subranges) + } + set { + for i in newValue.indices where subranges.contains(i.base) { + self[i.base] = newValue[i] + } + } + } +} + +// MARK: - moveSubranges(_:to:) + +extension MutableCollection { + /// Moves the elements in the given subranges to just before the element at + /// the specified index. + /// + /// This example finds all the uppercase letters in the array and then + /// moves them to between `"i"` and `"j"`. + /// + /// var letters = Array("ABCdeFGhijkLMNOp") + /// let uppercaseRanges = letters.subranges(where: { $0.isUppercase }) + /// let rangeOfUppercase = letters.moveSubranges(uppercaseRanges, to: 10) + /// // String(letters) == "dehiABCFGLMNOjkp" + /// // rangeOfUppercase == 4..<13 + /// + /// - Parameters: + /// - subranges: The subranges of the elements to move. + /// - insertionPoint: The index to use as the destination of the elements. + /// - Returns: The new bounds of the moved elements. + /// + /// - Complexity: O(*n* log *n*) where *n* is the length of the collection. + @discardableResult + public mutating func moveSubranges( + _ subranges: RangeSet, to insertionPoint: Index + ) -> Range { + let lowerCount = distance(from: startIndex, to: insertionPoint) + let upperCount = distance(from: insertionPoint, to: endIndex) + let start = _indexedStablePartition( + count: lowerCount, + range: startIndex.. = ["a", "e", "i", "o", "u"] + /// let vowelIndices = str.subranges(where: { vowels.contains($0) }) + /// + /// str.removeSubranges(vowelIndices) + /// // str == "Th rn n Spn stys mnly n th pln." + /// + /// - Parameter subranges: The indices of the elements to remove. + /// + /// - Complexity: O(*n*), where *n* is the length of the collection. + public mutating func removeSubranges(_ subranges: RangeSet) { + guard !subranges.isEmpty else { + return + } + + let inversion = subranges._inverted(within: self) + var result = Self() + for range in inversion.ranges { + result.append(contentsOf: self[range]) + } + self = result + } +} + +extension MutableCollection where Self: RangeReplaceableCollection { + /// Removes the elements at the given indices. + /// + /// For example, this code sample finds the indices of all the negative + /// numbers in the array, and then removes those values. + /// + /// var numbers = [5, 7, -3, -8, 11, 2, -1, 6] + /// let negativeIndices = numbers.subranges(where: { $0 < 0 }) + /// + /// numbers.removeSubranges(negativeIndices) + /// // numbers == [5, 7, 11, 2, 6] + /// + /// - Parameter subranges: The indices of the elements to remove. + /// + /// - Complexity: O(*n*), where *n* is the length of the collection. + public mutating func removeSubranges(_ subranges: RangeSet) { + guard let firstRange = subranges.ranges.first else { + return + } + + var endOfElementsToKeep = firstRange.lowerBound + var firstUnprocessed = firstRange.upperBound + + // This performs a half-stable partition based on the ranges in + // `indices`. At all times, the collection is divided into three + // regions: + // + // - `self[.. = ["a", "e", "i", "o", "u"] + /// let vowelIndices = str.subranges(where: { vowels.contains($0) }) + /// + /// let disemvoweled = str.removingSubranges(vowelIndices) + /// print(String(disemvoweled)) + /// // Prints "Th rn n Spn stys mnly n th pln." + /// + /// - Parameter subranges: A range set representing the indices of the + /// elements to remove. + /// - Returns: A collection of the elements that are not in `subranges`. + /// + /// - Complexity: O(*n*), where *n* is the length of the collection. + public func removingSubranges( + _ subranges: RangeSet + ) -> DiscontiguousSlice { + let inversion = subranges._inverted(within: self) + return self[inversion] + } +} + +// MARK: - subranges(where:) / subranges(of:) + +extension Collection { + /// Returns the indices of all the elements that match the given predicate. + /// + /// For example, you can use this method to find all the places that a + /// vowel occurs in a string. + /// + /// let str = "Fresh cheese in a breeze" + /// let vowels: Set = ["a", "e", "i", "o", "u"] + /// let allTheVowels = str.subranges(where: { vowels.contains($0) }) + /// // str[allTheVowels].count == 9 + /// + /// - Parameter predicate: A closure that takes an element as its argument + /// and returns a Boolean value that indicates whether the passed element + /// represents a match. + /// - Returns: A set of the indices of the elements for which `predicate` + /// returns `true`. + /// + /// - Complexity: O(*n*), where *n* is the length of the collection. + public func subranges(where predicate: (Element) throws -> Bool) rethrows + -> RangeSet + { + if isEmpty { return RangeSet() } + + var result = RangeSet() + var i = startIndex + while i != endIndex { + let next = index(after: i) + if try predicate(self[i]) { + result._append(i.. RangeSet { + subranges(where: { $0 == element }) + } +} + diff --git a/submodules/Utils/RangeSet/Sources/DiscontiguousSlice.swift b/submodules/Utils/RangeSet/Sources/DiscontiguousSlice.swift new file mode 100644 index 0000000000..bb0e4c628a --- /dev/null +++ b/submodules/Utils/RangeSet/Sources/DiscontiguousSlice.swift @@ -0,0 +1,140 @@ +//===----------------------------------------------------------*- swift -*-===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// + +/// A collection wrapper that provides access to the elements of a collection, +/// indexed by a set of indices. +public struct DiscontiguousSlice { + /// The collection that the indexed collection wraps. + public var base: Base + + /// The set of subranges that are available through this discontiguous slice. + public var subranges: RangeSet +} + +extension DiscontiguousSlice { + /// A position in an `DiscontiguousSlice`. + public struct Index: Comparable { + /// The index of the range that contains `base`. + internal var _rangeOffset: Int + + /// The position of this index in the base collection. + public var base: Base.Index + + public static func < (lhs: Index, rhs: Index) -> Bool { + lhs.base < rhs.base + } + } +} + +extension DiscontiguousSlice.Index: Hashable where Base.Index: Hashable {} + +extension DiscontiguousSlice: Collection { + public typealias SubSequence = Self + + public var startIndex: Index { + subranges.isEmpty + ? endIndex + : Index(_rangeOffset: 0, base: subranges._ranges[0].lowerBound) + } + + public var endIndex: Index { + Index(_rangeOffset: subranges._ranges.endIndex, base: base.endIndex) + } + + public func index(after i: Index) -> Index { + let nextIndex = base.index(after: i.base) + if subranges._ranges[i._rangeOffset].contains(nextIndex) { + return Index(_rangeOffset: i._rangeOffset, base: nextIndex) + } + + let nextOffset = i._rangeOffset + 1 + if nextOffset < subranges._ranges.endIndex { + return Index( + _rangeOffset: nextOffset, + base: subranges._ranges[nextOffset].lowerBound) + } else { + return endIndex + } + } + + public subscript(i: Index) -> Base.Element { + base[i.base] + } + + public subscript(bounds: Range) -> DiscontiguousSlice { + let baseBounds = bounds.lowerBound.base ..< bounds.upperBound.base + let subset = subranges.intersection(RangeSet(baseBounds)) + return DiscontiguousSlice(base: base, subranges: subset) + } +} + +extension DiscontiguousSlice { + public var count: Int { + var c = 0 + for range in subranges._ranges { + c += base.distance(from: range.lowerBound, to: range.upperBound) + } + return c + } + + public __consuming func _copyToContiguousArray() -> ContiguousArray { + var result: ContiguousArray = [] + for range in subranges._ranges { + result.append(contentsOf: base[range]) + } + return result + } +} + +extension DiscontiguousSlice: BidirectionalCollection + where Base: BidirectionalCollection +{ + public func index(before i: Index) -> Index { + precondition(i != startIndex, "Can't move index before startIndex") + + if i == endIndex || i.base == subranges._ranges[i._rangeOffset].lowerBound { + let offset = i._rangeOffset - 1 + return Index( + _rangeOffset: offset, + base: base.index(before: subranges._ranges[offset].upperBound)) + } + + return Index( + _rangeOffset: i._rangeOffset, + base: base.index(before: i.base)) + } +} + +extension DiscontiguousSlice: MutableCollection where Base: MutableCollection { + public subscript(i: Index) -> Base.Element { + get { + base[i.base] + } + set { + base[i.base] = newValue + } + } + + public subscript(bounds: Range) -> DiscontiguousSlice { + get { + let baseBounds = bounds.lowerBound.base ..< bounds.upperBound.base + let subset = subranges.intersection(RangeSet(baseBounds)) + return DiscontiguousSlice(base: base, subranges: subset) + } + set { + let baseBounds = bounds.lowerBound.base ..< bounds.upperBound.base + let subset = subranges.intersection(RangeSet(baseBounds)) + for i in newValue.indices where subset.contains(i.base) { + base[i.base] = newValue[i] + } + } + } +} diff --git a/submodules/Utils/RangeSet/Sources/Pair.swift b/submodules/Utils/RangeSet/Sources/Pair.swift new file mode 100644 index 0000000000..5e6aa13424 --- /dev/null +++ b/submodules/Utils/RangeSet/Sources/Pair.swift @@ -0,0 +1,33 @@ +//===----------------------------------------------------------*- swift -*-===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// + +/// A collection of two elements, to avoid heap allocation when calling +/// `replaceSubrange` with just two elements. +internal struct Pair: RandomAccessCollection { + var pair: (first: Element, second: Element) + + init(_ first: Element, _ second: Element) { + self.pair = (first, second) + } + + var startIndex: Int { 0 } + var endIndex: Int { 2 } + + subscript(position: Int) -> Element { + get { + switch position { + case 0: return pair.first + case 1: return pair.second + default: fatalError("Index '\(position)' is out of range") + } + } + } +} diff --git a/submodules/Utils/RangeSet/Sources/Partition.swift b/submodules/Utils/RangeSet/Sources/Partition.swift new file mode 100644 index 0000000000..17b9cc3847 --- /dev/null +++ b/submodules/Utils/RangeSet/Sources/Partition.swift @@ -0,0 +1,217 @@ +//===----------------------------------------------------------*- swift -*-===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// + +// MARK: _rotate(in:shiftingToStart:) + +extension MutableCollection { + /// Rotates the elements of the collection so that the element at `middle` + /// ends up first. + /// + /// - Returns: The new index of the element that was first pre-rotation. + /// + /// - Complexity: O(*n*) + @discardableResult + internal mutating func _rotate(in subrange: Range, shiftingToStart middle: Index) -> Index { + var m = middle, s = subrange.lowerBound + let e = subrange.upperBound + + // Handle the trivial cases + if s == m { return e } + if m == e { return s } + + // We have two regions of possibly-unequal length that need to be + // exchanged. The return value of this method is going to be the + // position following that of the element that is currently last + // (element j). + // + // [a b c d e f g|h i j] or [a b c|d e f g h i j] + // ^ ^ ^ ^ ^ ^ + // s m e s m e + // + var ret = e // start with a known incorrect result. + while true { + // Exchange the leading elements of each region (up to the + // length of the shorter region). + // + // [a b c d e f g|h i j] or [a b c|d e f g h i j] + // ^^^^^ ^^^^^ ^^^^^ ^^^^^ + // [h i j d e f g|a b c] or [d e f|a b c g h i j] + // ^ ^ ^ ^ ^ ^ ^ ^ + // s s1 m m1/e s s1/m m1 e + // + let (s1, m1) = _swapNonemptySubrangePrefixes(s.., _ rhs: Range + ) -> (Index, Index) { + assert(!lhs.isEmpty) + assert(!rhs.isEmpty) + + var p = lhs.lowerBound + var q = rhs.lowerBound + repeat { + swapAt(p, q) + formIndex(after: &p) + formIndex(after: &q) + } + while p != lhs.upperBound && q != rhs.upperBound + return (p, q) + } +} + +// MARK: - _stablePartition(count:range:by:) + +extension MutableCollection { + /// Moves all elements satisfying `belongsInSecondPartition` into a suffix + /// of the collection, preserving their relative order, and returns the + /// start of the resulting suffix. + /// + /// - Complexity: O(*n* log *n*) where *n* is the number of elements. + /// - Precondition: + /// `n == distance(from: range.lowerBound, to: range.upperBound)` + internal mutating func _stablePartition( + count n: Int, + range: Range, + by belongsInSecondPartition: (Element) throws-> Bool + ) rethrows -> Index { + if n == 0 { return range.lowerBound } + if n == 1 { + return try belongsInSecondPartition(self[range.lowerBound]) + ? range.lowerBound + : range.upperBound + } + let h = n / 2, i = index(range.lowerBound, offsetBy: h) + let j = try _stablePartition( + count: h, + range: range.lowerBound.., + by belongsInSecondPartition: (Index) throws-> Bool + ) rethrows -> Index { + if n == 0 { return range.lowerBound } + if n == 1 { + return try belongsInSecondPartition(range.lowerBound) + ? range.lowerBound + : range.upperBound + } + let h = n / 2, i = index(range.lowerBound, offsetBy: h) + let j = try _indexedStablePartition( + count: h, + range: range.lowerBound.. Bool + ) rethrows -> Index { + var n = count + var l = startIndex + + while n > 0 { + let half = n / 2 + let mid = index(l, offsetBy: half) + if try predicate(self[mid]) { + n = half + } else { + l = index(after: mid) + n -= half + 1 + } + } + return l + } +} + diff --git a/submodules/Utils/RangeSet/Sources/RangeSet.swift b/submodules/Utils/RangeSet/Sources/RangeSet.swift new file mode 100644 index 0000000000..1456b139d8 --- /dev/null +++ b/submodules/Utils/RangeSet/Sources/RangeSet.swift @@ -0,0 +1,564 @@ +//===----------------------------------------------------------*- swift -*-===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// + +/// A set of values of any comparable type, represented by ranges. +/// +/// You can use a range set to efficiently represent a set of `Comparable` +/// values that spans any number of discontiguous ranges. Range sets are +/// commonly used to represent multiple subranges of a collection, by storing +/// ranges of a collection's index type. +/// +/// In this example, `negativeSubranges` is a range set representing the +/// locations of all the negative values in `numbers`: +/// +/// var numbers = [10, 12, -5, 14, -3, -9, 15] +/// let negativeSubranges = numbers.subranges(where: { $0 < 0 }) +/// // numbers[negativeSubranges].count == 3 +/// +/// numbers.moveSubranges(negativeSubranges, to: 0) +/// // numbers == [-5, -3, -9, 10, 12, 14, 15] +public struct RangeSet { + internal var _ranges = _RangeSetStorage() + + /// Creates an empty range set. + public init() {} + + /// Creates a range set containing the given range. + /// + /// - Parameter range: The range to use for the new range set. + public init(_ range: Range) { + if !range.isEmpty { + self._ranges = _RangeSetStorage(range) + } + } + + /// Creates a range set containing the values in the given ranges. + /// + /// Any empty ranges in `ranges` are ignored, and non-empty ranges are merged + /// to eliminate any overlaps. As such, the `ranges` collection in the + /// resulting range set may not be equivalent to the sequence of ranges + /// passed to this initializer. + /// + /// - Parameter ranges: The ranges to use for the new range set. + public init(_ ranges: S) where S.Element == Range { + for range in ranges { + insert(contentsOf: range) + } + } + + /// Checks the invariants of `_ranges`. + /// + /// The ranges stored by a range set are never empty, never overlap, + /// and are always stored in ascending order when comparing their lower + /// or upper bounds. In addition to not overlapping, no two consecutive + /// ranges share an upper and lower bound — `[0..<5, 5..<10]` is ill-formed, + /// and would instead be represented as `[0..<10]`. + internal func _checkInvariants() { + for (a, b) in zip(ranges, ranges.dropFirst()) { + precondition(!a.isEmpty && !b.isEmpty, "Empty range in range set") + precondition( + a.upperBound < b.lowerBound, + "Out of order/overlapping ranges in range set") + } + } + + /// Creates a new range set from `ranges`, which satisfies the range set + /// invariants. + internal init(_orderedRanges ranges: [Range]) { + self._ranges = _RangeSetStorage(ranges) + _checkInvariants() + } + + /// A Boolean value indicating whether the range set is empty. + public var isEmpty: Bool { + _ranges.isEmpty + } + + /// Returns a Boolean value indicating whether the given value is + /// contained by the ranges in the range set. + /// + /// - Parameter value: The value to look for in the range set. + /// - Returns: `true` if `value` is contained by a range in the range set; + /// otherwise, `false`. + /// + /// - Complexity: O(log *n*), where *n* is the number of ranges in the + /// range set. + public func contains(_ value: Bound) -> Bool { + let i = _ranges._partitioningIndex { $0.upperBound > value } + return i == _ranges.endIndex + ? false + : _ranges[i].lowerBound <= value + } + + public func intersects(_ range: Range) -> Bool { + if _ranges.isEmpty { + return false + } + if range.isEmpty { + return false + } + if range.lowerBound > _ranges.last!.upperBound { + return false + } + if range.upperBound < _ranges.first!.lowerBound { + return false + } + return !_indicesOfRange(range).isEmpty + } + + /// Returns a range indicating the existing ranges that `range` overlaps + /// with. + /// + /// For example, if `self` is `[0..<5, 10..<15, 20..<25, 30..<35]`, then: + /// + /// - `_indicesOfRange(12..<14) == 1..<2` + /// - `_indicesOfRange(12..<19) == 1..<2` + /// - `_indicesOfRange(17..<19) == 2..<2` + /// - `_indicesOfRange(12..<22) == 1..<3` + func _indicesOfRange(_ range: Range) -> Range { + precondition(!range.isEmpty) + precondition(!_ranges.isEmpty) + precondition(range.lowerBound <= _ranges.last!.upperBound) + precondition(range.upperBound >= _ranges.first!.lowerBound) + + // The beginning index for the position of `range` is the first range + // with an upper bound larger than `range`'s lower bound. The range + // at this position may or may not overlap `range`. + let beginningIndex = _ranges + ._partitioningIndex { $0.upperBound >= range.lowerBound } + + // The ending index for `range` is the first range with a lower bound + // greater than `range`'s upper bound. If this is the same as + // `beginningIndex`, than `range` doesn't overlap any of the existing + // ranges. If this is `ranges.endIndex`, then `range` overlaps the + // rest of the ranges. Otherwise, `range` overlaps one or + // more ranges in the set. + let endingIndex = _ranges[beginningIndex...] + ._partitioningIndex { $0.lowerBound > range.upperBound } + + return beginningIndex ..< endingIndex + } + + /// Inserts a non-empty range that is known to be greater than all the + /// elements in the set so far. + /// + /// - Precondition: The range set must be empty, or else + /// `ranges.last!.upperBound <= range.lowerBound`. + /// - Precondition: `range` must not be empty. + internal mutating func _append(_ range: Range) { + precondition(_ranges.isEmpty + || _ranges.last!.upperBound <= range.lowerBound) + precondition(!range.isEmpty) + if _ranges.isEmpty { + _ranges.append(range) + } else if _ranges.last!.upperBound == range.lowerBound { + _ranges[_ranges.count - 1] = + _ranges[_ranges.count - 1].lowerBound ..< range.upperBound + } else { + _ranges.append(range) + } + } + + /// Inserts the given range into the range set. + /// + /// - Parameter range: The range to insert into the set. + /// + /// - Complexity: O(*n*), where *n* is the number of ranges in the range + /// set. + public mutating func insert(contentsOf range: Range) { + // Shortcuts for the (literal) edge cases + if range.isEmpty { return } + guard !_ranges.isEmpty else { + _ranges.append(range) + return + } + guard range.lowerBound < _ranges.last!.upperBound else { + _append(range) + return + } + guard range.upperBound >= _ranges.first!.lowerBound else { + _ranges.insert(range, at: 0) + return + } + + let indices = _indicesOfRange(range) + + // Non-overlapping is a simple insertion. + guard !indices.isEmpty else { + _ranges.insert(range, at: indices.lowerBound) + return + } + + // Find the lower and upper bounds of the overlapping ranges. + let newLowerBound = Swift.min( + _ranges[indices.lowerBound].lowerBound, + range.lowerBound) + let newUpperBound = Swift.max( + _ranges[indices.upperBound - 1].upperBound, + range.upperBound) + _ranges.replaceSubrange( + indices, + with: CollectionOfOne(newLowerBound..) { + // Shortcuts for the (literal) edge cases + if range.isEmpty + || _ranges.isEmpty + || range.lowerBound >= _ranges.last!.upperBound + || range.upperBound < _ranges.first!.lowerBound + { return } + + let indices = _indicesOfRange(range) + + // No actual overlap, nothing to remove. + if indices.isEmpty { return } + + let overlapsLowerBound = + range.lowerBound > _ranges[indices.lowerBound].lowerBound + let overlapsUpperBound = + range.upperBound < _ranges[indices.upperBound - 1].upperBound + + switch (overlapsLowerBound, overlapsUpperBound) { + case (false, false): + _ranges.removeSubrange(indices) + case (false, true): + let newRange = + range.upperBound..<_ranges[indices.upperBound - 1].upperBound + _ranges.replaceSubrange(indices, with: CollectionOfOne(newRange)) + case (true, false): + let newRange = _ranges[indices.lowerBound].lowerBound.. + + public var startIndex: Int { _ranges.startIndex } + public var endIndex: Int { _ranges.endIndex } + + public subscript(i: Int) -> Range { + _ranges[i] + } + } + + /// A collection of the ranges that make up the range set. + /// + /// The ranges that you access by using `ranges` never overlap, are never + /// empty, and are always in increasing order. + public var ranges: Ranges { + Ranges(_ranges: _ranges) + } +} + +// MARK: - Collection APIs + +extension RangeSet { + /// Creates a new range set containing ranges that contain only the + /// specified indices in the given collection. + /// + /// - Parameters: + /// - index: The index to include in the range set. `index` must be a + /// valid index of `collection` that isn't the collection's `endIndex`. + /// - collection: The collection that contains `index`. + public init(_ indices: S, within collection: C) + where S: Sequence, C: Collection, S.Element == C.Index, C.Index == Bound + { + for i in indices { + self.insert(i, within: collection) + } + } + + /// Inserts a range that contains only the specified index into the range + /// set. + /// + /// - Parameters: + /// - index: The index to insert into the range set. `index` must be a + /// valid index of `collection` that isn't the collection's `endIndex`. + /// - collection: The collection that contains `index`. + /// + /// - Complexity: O(*n*), where *n* is the number of ranges in the range + /// set. + public mutating func insert(_ index: Bound, within collection: C) + where C: Collection, C.Index == Bound + { + insert(contentsOf: index ..< collection.index(after: index)) + } + + /// Removes the range that contains only the specified index from the range + /// set. + /// + /// - Parameters: + /// - index: The index to remove from the range set. `index` must be a + /// valid index of `collection` that isn't the collection's `endIndex`. + /// - collection: The collection that contains `index`. + /// + /// - Complexity: O(*n*), where *n* is the number of ranges in the range + /// set. + public mutating func remove(_ index: Bound, within collection: C) + where C: Collection, C.Index == Bound + { + remove(contentsOf: index ..< collection.index(after: index)) + } + + /// Returns a range set that represents all the elements in the given + /// collection that aren't represented by this range set. + /// + /// - Parameter collection: The collection that the range set is relative + /// to. + /// - Returns: A new range set that represents the elements in `collection` + /// that aren't represented by this range set. + /// + /// - Complexity: O(*n*), where *n* is the number of ranges in the range + /// set. + internal func _inverted(within collection: C) -> RangeSet + where C: Collection, C.Index == Bound + { + return _gaps( + boundedBy: collection.startIndex..) -> RangeSet { + guard !_ranges.isEmpty else { return RangeSet(bounds) } + guard let start = _ranges.firstIndex(where: { $0.lowerBound >= bounds.lowerBound }) + else { return RangeSet() } + guard let end = _ranges.lastIndex(where: { $0.upperBound <= bounds.upperBound }) + else { return RangeSet() } + + var result = RangeSet() + var low = bounds.lowerBound + for range in _ranges[start...end] { + result.insert(contentsOf: low..) { + for range in other._ranges { + insert(contentsOf: range) + } + } + + /// Removes the contents of this range set that aren't also in the given + /// range set. + /// + /// - Parameter other: A range set to intersect with. + public mutating func formIntersection(_ other: RangeSet) { + self = self.intersection(other) + } + + /// Removes the contents of this range set that are also in the given set + /// and adds the contents of the given set that are not already in this + /// range set. + /// + /// - Parameter other: A range set to perform a symmetric difference against. + public mutating func formSymmetricDifference( + _ other: __owned RangeSet + ) { + self = self.symmetricDifference(other) + } + + /// Removes the contents of the given range set from this range set. + /// + /// - Parameter other: A range set to subtract from this one. + public mutating func subtract(_ other: RangeSet) { + for range in other._ranges { + remove(contentsOf: range) + } + } + + /// Returns a new range set containing the contents of both this set and the + /// given set. + /// + /// - Parameter other: The range set to merge with this one. + /// - Returns: A new range set. + public __consuming func union( + _ other: __owned RangeSet + ) -> RangeSet { + var result = self + result.formUnion(other) + return result + } + + /// Returns a new range set containing the contents of both this set and the + /// given set. + /// + /// - Parameter other: The range set to merge with this one. + /// - Returns: A new range set. + public __consuming func intersection( + _ other: RangeSet + ) -> RangeSet { + var otherRangeIndex = 0 + var result: [Range] = [] + + // Considering these two range sets: + // + // self = [0..<5, 9..<14] + // other = [1..<3, 4..<6, 8..<12] + // + // `self.intersection(other)` looks like this, where x's cover the + // ranges in `self`, y's cover the ranges in `other`, and z's cover the + // resulting ranges: + // + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + // xxxxxxxxxxxxxxxxxxx__ xxxxxxxxxxxxxxxxxxx__ + // yyyyyyy__ yyyyyyy__ yyyyyyyyyyyyyyy__ + // zzzzzzz__ zzz__ zzzzzzzzzzz__ + // + // The same, but for `other.intersection(self)`: + // + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + // xxxxxxx__ xxxxxxx__ xxxxxxxxxxxxxxx__ + // yyyyyyyyyyyyyyyyyyy__ yyyyyyyyyyyyyyyyyyy__ + // zzzzzzz__ zzz__ zzzzzzzzzzz__ + + for currentRange in _ranges { + // Search forward in `other` until finding either an overlapping + // range or one that is strictly higher than this range. + while otherRangeIndex < other._ranges.endIndex && + other._ranges[otherRangeIndex].upperBound <= currentRange.lowerBound + { + otherRangeIndex += 1 + } + + // For each range in `other` that overlaps with the current range + // in `self`, append the intersection to the result. + while otherRangeIndex < other._ranges.endIndex && + other._ranges[otherRangeIndex].lowerBound < currentRange.upperBound + { + let lower = Swift.max( + other._ranges[otherRangeIndex].lowerBound, + currentRange.lowerBound) + let upper = Swift.min( + other._ranges[otherRangeIndex].upperBound, + currentRange.upperBound) + result.append(lower.. other._ranges[otherRangeIndex].upperBound + else { + break + } + otherRangeIndex += 1 + } + } + + return RangeSet(_orderedRanges: result) + } + + /// Returns a new range set representing the values in this range set or the + /// given range set, but not both. + /// + /// - Parameter other: The range set to find a symmetric difference with. + /// - Returns: A new range set. + public __consuming func symmetricDifference( + _ other: __owned RangeSet + ) -> RangeSet { + return union(other).subtracting(intersection(other)) + } + + /// Returns a new set containing the contents of this range set that are not + /// also in the given range set. + /// + /// - Parameter other: The range set to subtract. + /// - Returns: A new range set. + public func subtracting(_ other: RangeSet) -> RangeSet { + var result = self + result.subtract(other) + return result + } + + /// Returns a Boolean value that indicates whether this range set is a + /// subset of the given set. + /// + /// - Parameter other: A range set to compare against. + /// - Returns: `true` if this range set is a subset of `other`; + /// otherwise, `false`. + public func isSubset(of other: RangeSet) -> Bool { + self.intersection(other) == self + } + + /// Returns a Boolean value that indicates whether this range set is a + /// superset of the given set. + /// + /// - Parameter other: A range set to compare against. + /// - Returns: `true` if this range set is a superset of `other`; + /// otherwise, `false`. + public func isSuperset(of other: RangeSet) -> Bool { + other.isSubset(of: self) + } + + /// Returns a Boolean value that indicates whether this range set is a + /// strict subset of the given set. + /// + /// - Parameter other: A range set to compare against. + /// - Returns: `true` if this range set is a strict subset of `other`; + /// otherwise, `false`. + public func isStrictSubset(of other: RangeSet) -> Bool { + self != other && isSubset(of: other) + } + + /// Returns a Boolean value that indicates whether this range set is a + /// strict superset of the given set. + /// + /// - Parameter other: A range set to compare against. + /// - Returns: `true` if this range set is a strict superset of `other`; + /// otherwise, `false`. + public func isStrictSuperset(of other: RangeSet) -> Bool { + other.isStrictSubset(of: self) + } +} + +extension RangeSet: CustomStringConvertible { + public var description: String { + let rangesDescription = _ranges + .map { r in "\(r.lowerBound)..<\(r.upperBound)" } + .joined(separator: ", ") + return "RangeSet(\(rangesDescription))" + } +} diff --git a/submodules/Utils/RangeSet/Sources/RangeSetStorage.swift b/submodules/Utils/RangeSet/Sources/RangeSetStorage.swift new file mode 100644 index 0000000000..ce0d0ea7ac --- /dev/null +++ b/submodules/Utils/RangeSet/Sources/RangeSetStorage.swift @@ -0,0 +1,181 @@ +//===----------------------------------------------------------*- swift -*-===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// + +struct _RangeSetStorage { + fileprivate enum _Storage { + case empty + case singleRange(high: Int, low: Int32) + case variadic([Range]) + } + + fileprivate var _storage: _Storage + + init() { + _storage = .empty + } + + init(_ range: Range) { + if let intRange = range as? Range, + let lowerBound = Int32(exactly: intRange.lowerBound) + { + _storage = .singleRange(high: intRange.upperBound, low: lowerBound) + } else { + _storage = .variadic([range]) + } + } + + init(_ ranges: [Range]) { + _storage = .variadic(ranges) + } + + func unsafeRange(low: Int32, high: Int) -> Range { + unsafeBitCast(Int(low)...self) + } +} + +// _RangeSetStorage has custom Equatable (and therefore Hashable) +// conformance, since the same "value" can be represented by different +// storage structures. For example, `.empty` and `.variadic([])` are +// equivalent, but the synthesized conformance treats them as distinct. +// The same holds with the `singleRange` representation and `variadic` +// with a single-element array. + +extension _RangeSetStorage: Equatable { + static func == (lhs: _RangeSetStorage, rhs: _RangeSetStorage) -> Bool { + switch (lhs._storage, rhs._storage) { + case (.empty, .empty): + return true + case (.empty, .singleRange), (.singleRange, .empty): + return false + case let (.empty, .variadic(ranges)), + let (.variadic(ranges), .empty): + return ranges.isEmpty + + case let (.singleRange(lhsHigh, lhsLow), .singleRange(rhsHigh, rhsLow)): + return (lhsLow, lhsHigh) == (rhsLow, rhsHigh) + + case let (.singleRange(high, low), .variadic(ranges)), + let (.variadic(ranges), .singleRange(high, low)): + return ranges.count == 1 && + (ranges[0] as! Range) == Int(low).. Range { + get { + switch _storage { + case .empty: fatalError("Can't access elements of empty storage") + case let .singleRange(high, low): + assert(T.self == Int.self) + return unsafeRange(low: low, high: high) + case let .variadic(ranges): + return ranges[i] + } + } + set { + switch _storage { + case .empty: fatalError("Can't access elements of empty storage") + case .singleRange: + assert(T.self == Int.self) + let intRange = newValue as! Range + if let lowerBound = Int32(exactly: intRange.lowerBound) { + _storage = .singleRange(high: intRange.upperBound, low: lowerBound) + } else { + _storage = .variadic([newValue]) + } + case .variadic(var ranges): + // Temporarily set `_storage` to empty so that `ranges` + // remains uniquely referenced while mutating. + _storage = .empty + ranges[i] = newValue + _storage = .variadic(ranges) + } + } + } + + var count: Int { + switch _storage { + case .empty: return 0 + case .singleRange: return 1 + case let .variadic(ranges): return ranges.count + } + } +} + +extension _RangeSetStorage: RangeReplaceableCollection { + mutating func replaceSubrange(_ subrange: Range, with newElements: C) where C : Collection, C.Element == Element { + switch _storage { + case .empty: + if !newElements.isEmpty { + _storage = .variadic(Array(newElements)) + } + + case .singleRange(high: let high, low: let low): + switch (subrange.isEmpty, newElements.isEmpty) { + case (false, true): + // Replacing the single range with an empty collection. + _storage = .empty + + case (false, false): + // Replacing the single range with a non-empty collection; + // promote to a variadic container. + _storage = .variadic(Array(newElements)) + + case (true, true): + // Inserting an empty collection; no-op. + break + + case (true, false): + // Inserting a non-empty collection either before or after + // the existing single element. + var ranges: [Range] + if subrange.lowerBound == 0 { + ranges = Array(newElements) + ranges.append(unsafeRange(low: low, high: high)) + } else { + ranges = [unsafeRange(low: low, high: high)] + ranges.append(contentsOf: newElements) + } + _storage = .variadic(ranges) + } + + case .variadic(var ranges): + // Temporarily set `_storage` to empty so that `ranges` + // remains uniquely referenced while mutating. + _storage = .empty + ranges.replaceSubrange(subrange, with: newElements) + _storage = .variadic(ranges) + } + } +} diff --git a/submodules/WatchBridge/Sources/WatchRequestHandlers.swift b/submodules/WatchBridge/Sources/WatchRequestHandlers.swift index f283fa82d1..31111228a6 100644 --- a/submodules/WatchBridge/Sources/WatchRequestHandlers.swift +++ b/submodules/WatchBridge/Sources/WatchRequestHandlers.swift @@ -91,7 +91,7 @@ final class WatchChatMessagesHandler: WatchRequestHandler { |> take(1) |> mapToSignal({ context -> Signal<(MessageHistoryView, Bool, PresentationData), NoError> in if let context = context { - return context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId), index: .upperBound, anchorIndex: .upperBound, count: limit, fixedCombinedReadStates: nil) + return context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: peerId), index: .upperBound, anchorIndex: .upperBound, count: limit, fixedCombinedReadStates: nil) |> map { messageHistoryView, _, _ -> (MessageHistoryView, Bool, PresentationData) in return (messageHistoryView, peerId == context.account.peerId, context.sharedContext.currentPresentationData.with { $0 }) }