From 4bd4884887bfff1b06b408eb045605a4544fa6bb Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sun, 28 Jan 2024 18:59:00 +0100 Subject: [PATCH] Saved messages view as chats --- .../Sources/ChatListSearchListPaneNode.swift | 6 +- .../Sources/ChatListShimmerNode.swift | 3 +- .../Sources/Node/ChatListItem.swift | 17 ++- .../Sources/Node/ChatListNode.swift | 43 ++------ .../Sources/Node/ChatListNodeEntries.swift | 20 +++- .../Sources/Node/ChatListNodeLocation.swift | 14 ++- .../Display/Source/NavigationButtonNode.swift | 2 +- submodules/Postbox/Sources/ChatListView.swift | 29 +++++ .../Postbox/Sources/SeedConfiguration.swift | 5 +- .../TextSizeSelectionController.swift | 3 +- .../Themes/ThemePreviewControllerNode.swift | 3 +- .../Sources/ShareControllerNode.swift | 3 +- .../TelegramCore/Sources/ForumChannels.swift | 3 +- .../SyncCore/SyncCore_Namespaces.swift | 7 ++ ...yncCore_StandaloneAccountTransaction.swift | 3 +- .../TelegramEngine/Data/PeersData.swift | 31 ++++++ .../TelegramEngine/Messages/ChatList.swift | 24 +++-- .../Messages/TelegramEngineMessages.swift | 3 +- .../Peers/TelegramEnginePeers.swift | 8 ++ .../Sources/MoreHeaderButton.swift | 22 ++-- .../Sources/PeerInfoChatListPaneNode.swift | 1 + .../Components/PeerInfo/PeerInfoScreen/BUILD | 1 + .../PeerInfoScreen/Sources/PeerInfoData.swift | 22 ++-- .../PeerInfoHeaderNavigationButton.swift | 4 + ...oHeaderNavigationButtonContainerNode.swift | 19 ++-- .../Sources/PeerInfoHeaderNode.swift | 40 +++++++ .../Sources/PeerInfoScreen.swift | 100 ++++++++++++++++-- .../ThemeAccentColorControllerNode.swift | 3 +- ...UpdateChatPresentationInterfaceState.swift | 45 +++++++- .../TelegramUI/Sources/ChatController.swift | 17 ++- .../ChatInterfaceStateNavigationButtons.swift | 8 ++ .../ChatSearchResultsContollerNode.swift | 3 +- .../Sources/NavigateToChatController.swift | 15 +++ 33 files changed, 421 insertions(+), 106 deletions(-) diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index c43de99600..e589429076 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -881,7 +881,8 @@ public enum ChatListSearchEntry: Comparable, Identifiable { hasUnseenCloseFriends: stats.hasUnseenCloseFriends ) }, - requiresPremiumForMessaging: requiresPremiumForMessaging + requiresPremiumForMessaging: requiresPremiumForMessaging, + displayAsTopicList: false )), editing: false, hasActiveRevealControls: false, selected: false, header: tagMask == nil ? header : nil, enableContextActions: false, hiddenOffset: false, interaction: interaction) } case let .addContact(phoneNumber, theme, strings): @@ -3743,7 +3744,8 @@ public final class ChatListSearchShimmerNode: ASDisplayNode { topForumTopicItems: [], autoremoveTimeout: nil, storyState: nil, - requiresPremiumForMessaging: false + requiresPremiumForMessaging: false, + displayAsTopicList: false )), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction) case .media: return nil diff --git a/submodules/ChatListUI/Sources/ChatListShimmerNode.swift b/submodules/ChatListUI/Sources/ChatListShimmerNode.swift index 53f89b3dea..865bdef36e 100644 --- a/submodules/ChatListUI/Sources/ChatListShimmerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListShimmerNode.swift @@ -208,7 +208,8 @@ public final class ChatListShimmerNode: ASDisplayNode { topForumTopicItems: [], autoremoveTimeout: nil, storyState: nil, - requiresPremiumForMessaging: false + requiresPremiumForMessaging: false, + displayAsTopicList: false )), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction) } diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index e2eb846261..7b6b145eab 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -97,6 +97,7 @@ public enum ChatListItemContent { public var autoremoveTimeout: Int32? public var storyState: StoryState? public var requiresPremiumForMessaging: Bool + public var displayAsTopicList: Bool public init( messages: [EngineMessage], @@ -117,7 +118,8 @@ public enum ChatListItemContent { topForumTopicItems: [EngineChatList.ForumTopicData], autoremoveTimeout: Int32?, storyState: StoryState?, - requiresPremiumForMessaging: Bool + requiresPremiumForMessaging: Bool, + displayAsTopicList: Bool ) { self.messages = messages self.peer = peer @@ -138,6 +140,7 @@ public enum ChatListItemContent { self.autoremoveTimeout = autoremoveTimeout self.storyState = storyState self.requiresPremiumForMessaging = requiresPremiumForMessaging + self.displayAsTopicList = displayAsTopicList } } @@ -1417,11 +1420,17 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { } else if peer.isDeleted { overrideImage = .deletedIcon } - var isForum = false + var isForumAvatar = false if case let .channel(channel) = peer, channel.flags.contains(.isForum) { - isForum = true + isForumAvatar = true } - self.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, clipStyle: isForum ? .roundedRect : .round, synchronousLoad: synchronousLoads, displayDimensions: CGSize(width: 60.0, height: 60.0)) + if case let .peer(data) = item.content { + if data.displayAsTopicList { + isForumAvatar = true + } + } + + self.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, clipStyle: isForumAvatar ? .roundedRect : .round, synchronousLoad: synchronousLoads, displayDimensions: CGSize(width: 60.0, height: 60.0)) if peer.isPremium && peer.id != item.context.account.peerId { let context = item.context diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 01fc0c5016..2c11365dd2 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -427,7 +427,8 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL hasUnseenCloseFriends: storyState.hasUnseenCloseFriends ) }, - requiresPremiumForMessaging: peerEntry.requiresPremiumForMessaging + requiresPremiumForMessaging: peerEntry.requiresPremiumForMessaging, + displayAsTopicList: peerEntry.displayAsTopicList )), editing: editing, hasActiveRevealControls: hasActiveRevealControls, @@ -796,7 +797,8 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL hasUnseenCloseFriends: storyState.hasUnseenCloseFriends ) }, - requiresPremiumForMessaging: peerEntry.requiresPremiumForMessaging + requiresPremiumForMessaging: peerEntry.requiresPremiumForMessaging, + displayAsTopicList: peerEntry.displayAsTopicList )), editing: editing, hasActiveRevealControls: hasActiveRevealControls, @@ -1278,6 +1280,8 @@ public final class ChatListNode: ListView { public let isMainTab = ValuePromise(false, ignoreRepeated: true) private let suggestedChatListNotice = Promise(nil) + public var synchronousDrawingWhenNotAnimated: Bool = false + public init(context: AccountContext, location: ChatListControllerLocation, chatListFilter: ChatListFilter? = nil, previewing: Bool, fillPreloadItems: Bool, mode: ChatListNodeMode, isPeerEnabled: ((EnginePeer) -> Bool)? = nil, theme: PresentationTheme, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, disableAnimations: Bool, isInlineMode: Bool, autoSetReady: Bool, isMainTab: Bool?) { self.context = context self.location = location @@ -1977,39 +1981,6 @@ public final class ChatListNode: ListView { } let currentPeerId: EnginePeer.Id = context.account.peerId - - /*let contactList: Signal - if case let .chatList(appendContacts) = mode, appendContacts { - contactList = self.context.engine.data.subscribe(TelegramEngine.EngineData.Item.Contacts.List(includePresences: true)) - |> map(Optional.init) - } else { - contactList = .single(nil) - } - let _ = contactList*/ - - - /*let emptyInitialView = ChatListNodeView( - originalList: EngineChatList( - items: [], - groupItems: [], - additionalItems: [], - hasEarlier: false, - hasLater: false, - isLoading: false - ), - filteredEntries: [ChatListNodeEntry.HeaderEntry], - isLoading: false, - filter: nil - ) - let _ = previousView.swap(emptyInitialView) - - let _ = (preparedChatListNodeViewTransition(from: nil, to: emptyInitialView, reason: .initial, previewing: previewing, disableAnimations: disableAnimations, account: context.account, scrollPosition: nil, searchMode: false) - |> map { mappedChatListNodeViewListTransition(context: context, nodeInteraction: nodeInteraction, location: location, filterData: nil, mode: mode, isPeerEnabled: nil, transition: $0) }).start(next: { [weak self] value in - guard let self else { - return - } - let _ = self.enqueueTransition(value).start() - })*/ let contacts: Signal<[ChatListContactPeer], NoError> if case .chatList(groupId: .root) = location, chatListFilter == nil, case .chatList = mode { @@ -3414,7 +3385,7 @@ public final class ChatListNode: ListView { var options = transition.options //options.insert(.Synchronous) - if self.view.window != nil { + if self.view.window != nil || self.synchronousDrawingWhenNotAnimated { if !options.contains(.AnimateInsertion) { options.insert(.PreferSynchronousDrawing) options.insert(.PreferSynchronousResourceLoading) diff --git a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift index 5bd0ba108b..d8fc0cac03 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift @@ -115,6 +115,7 @@ enum ChatListNodeEntry: Comparable, Identifiable { var revealed: Bool var storyState: ChatListNodeState.StoryState? var requiresPremiumForMessaging: Bool + var displayAsTopicList: Bool init( index: EngineChatList.Item.Index, @@ -140,7 +141,8 @@ enum ChatListNodeEntry: Comparable, Identifiable { topForumTopicItems: [EngineChatList.ForumTopicData], revealed: Bool, storyState: ChatListNodeState.StoryState?, - requiresPremiumForMessaging: Bool + requiresPremiumForMessaging: Bool, + displayAsTopicList: Bool ) { self.index = index self.presentationData = presentationData @@ -166,6 +168,7 @@ enum ChatListNodeEntry: Comparable, Identifiable { self.revealed = revealed self.storyState = storyState self.requiresPremiumForMessaging = requiresPremiumForMessaging + self.displayAsTopicList = displayAsTopicList } static func ==(lhs: PeerEntryData, rhs: PeerEntryData) -> Bool { @@ -281,6 +284,9 @@ enum ChatListNodeEntry: Comparable, Identifiable { if lhs.requiresPremiumForMessaging != rhs.requiresPremiumForMessaging { return false } + if lhs.displayAsTopicList != rhs.displayAsTopicList { + return false + } return true } } @@ -708,7 +714,8 @@ func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState, hasUnseenCloseFriends: stats.hasUnseenCloseFriends ) }, - requiresPremiumForMessaging: false + requiresPremiumForMessaging: false, + displayAsTopicList: entry.displayAsTopicList )) if let threadInfo, threadInfo.isHidden { @@ -759,7 +766,8 @@ func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState, topForumTopicItems: [], revealed: false, storyState: nil, - requiresPremiumForMessaging: false + requiresPremiumForMessaging: false, + displayAsTopicList: false ))) if foundPinningIndex != 0 { foundPinningIndex -= 1 @@ -791,7 +799,8 @@ func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState, topForumTopicItems: [], revealed: false, storyState: nil, - requiresPremiumForMessaging: false + requiresPremiumForMessaging: false, + displayAsTopicList: false ))) } else { if !filteredAdditionalItemEntries.isEmpty { @@ -843,7 +852,8 @@ func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState, topForumTopicItems: item.item.topForumTopicItems, revealed: state.hiddenItemShouldBeTemporaryRevealed || state.editing, storyState: nil, - requiresPremiumForMessaging: false + requiresPremiumForMessaging: false, + displayAsTopicList: false ))) if pinningIndex != 0 { pinningIndex -= 1 diff --git a/submodules/ChatListUI/Sources/Node/ChatListNodeLocation.swift b/submodules/ChatListUI/Sources/Node/ChatListNodeLocation.swift index 1e7e66a198..dd0101c727 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNodeLocation.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNodeLocation.swift @@ -114,6 +114,8 @@ public func chatListFilterPredicate(filter: ChatListFilterData, accountPeerId: E } func chatListViewForLocation(chatListLocation: ChatListControllerLocation, location: ChatListNodeLocation, account: Account) -> Signal { + let accountPeerId = account.peerId + switch chatListLocation { case let .chatList(groupId): let filterPredicate: ChatListFilterPredicate? @@ -129,7 +131,7 @@ func chatListViewForLocation(chatListLocation: ChatListControllerLocation, locat signal = account.viewTracker.tailChatListView(groupId: groupId._asGroup(), filterPredicate: filterPredicate, count: count) return signal |> map { view, updateType -> ChatListNodeViewUpdate in - return ChatListNodeViewUpdate(list: EngineChatList(view), type: updateType, scrollPosition: nil) + return ChatListNodeViewUpdate(list: EngineChatList(view, accountPeerId: accountPeerId), type: updateType, scrollPosition: nil) } case let .navigation(index, _): guard case let .chatList(index) = index else { @@ -145,7 +147,7 @@ func chatListViewForLocation(chatListLocation: ChatListControllerLocation, locat } else { genericType = updateType } - return ChatListNodeViewUpdate(list: EngineChatList(view), type: genericType, scrollPosition: nil) + return ChatListNodeViewUpdate(list: EngineChatList(view, accountPeerId: accountPeerId), type: genericType, scrollPosition: nil) } case let .scroll(index, sourceIndex, scrollPosition, animated, _): guard case let .chatList(index) = index else { @@ -165,7 +167,7 @@ func chatListViewForLocation(chatListLocation: ChatListControllerLocation, locat } else { genericType = updateType } - return ChatListNodeViewUpdate(list: EngineChatList(view), type: genericType, scrollPosition: scrollPosition) + return ChatListNodeViewUpdate(list: EngineChatList(view, accountPeerId: accountPeerId), type: genericType, scrollPosition: scrollPosition) } } case let .forum(peerId): @@ -292,7 +294,8 @@ func chatListViewForLocation(chatListLocation: ChatListControllerLocation, locat hasFailed: false, isContact: false, autoremoveTimeout: nil, - storyStats: nil + storyStats: nil, + displayAsTopicList: false )) } @@ -356,7 +359,8 @@ func chatListViewForLocation(chatListLocation: ChatListControllerLocation, locat hasFailed: false, isContact: false, autoremoveTimeout: nil, - storyStats: nil + storyStats: nil, + displayAsTopicList: false )) } diff --git a/submodules/Display/Source/NavigationButtonNode.swift b/submodules/Display/Source/NavigationButtonNode.swift index fe281a6211..ea05895c76 100644 --- a/submodules/Display/Source/NavigationButtonNode.swift +++ b/submodules/Display/Source/NavigationButtonNode.swift @@ -500,7 +500,7 @@ public final class NavigationButtonNode: ContextControllerSourceNode { var totalHeight: CGFloat = 0.0 for i in 0 ..< self.nodes.count { if i != 0 { - nodeOrigin.x += 10.0 + nodeOrigin.x += 15.0 } let node = self.nodes[i] diff --git a/submodules/Postbox/Sources/ChatListView.swift b/submodules/Postbox/Sources/ChatListView.swift index 3a37963f16..aa2b834f6e 100644 --- a/submodules/Postbox/Sources/ChatListView.swift +++ b/submodules/Postbox/Sources/ChatListView.swift @@ -566,6 +566,9 @@ final class MutableChatListView { private var currentHiddenPeerIds = Set() + private let displaySavedMessagesAsTopicListPreferencesKey: ValueBoxKey + private(set) var displaySavedMessagesAsTopicList: PreferencesEntry? + init(postbox: PostboxImpl, currentTransaction: Transaction, groupId: PeerGroupId, filterPredicate: ChatListFilterPredicate?, aroundIndex: ChatListIndex, count: Int, summaryComponents: ChatListEntrySummaryComponents) { self.groupId = groupId self.filterPredicate = filterPredicate @@ -574,6 +577,8 @@ final class MutableChatListView { self.currentHiddenPeerIds = postbox.hiddenChatIds + self.displaySavedMessagesAsTopicListPreferencesKey = postbox.seedConfiguration.displaySavedMessagesAsTopicListPreferencesKey + var spaces: [ChatListViewSpace] = [ .group(groupId: self.groupId, pinned: .notPinned, predicate: filterPredicate) ] @@ -612,6 +617,8 @@ final class MutableChatListView { } else { self.groupEntries = [] } + + self.displaySavedMessagesAsTopicList = postbox.preferencesTable.get(key: self.displaySavedMessagesAsTopicListPreferencesKey) } private func reloadGroups(postbox: PostboxImpl) { @@ -689,6 +696,8 @@ final class MutableChatListView { } } } + + self.displaySavedMessagesAsTopicList = postbox.preferencesTable.get(key: self.displaySavedMessagesAsTopicListPreferencesKey) } func refreshDueToExternalTransaction(postbox: PostboxImpl, currentTransaction: Transaction) -> Bool { @@ -699,12 +708,16 @@ final class MutableChatListView { updated = true let currentGroupEntries = self.groupEntries + let currentDisplaySavedMessagesAsTopicList = self.displaySavedMessagesAsTopicList self.reloadGroups(postbox: postbox) if self.groupEntries != currentGroupEntries { updated = true } + if self.displaySavedMessagesAsTopicList != currentDisplaySavedMessagesAsTopicList { + updated = true + } return updated } @@ -734,6 +747,20 @@ final class MutableChatListView { } } + if !transaction.currentPreferencesOperations.isEmpty { + for operation in transaction.currentPreferencesOperations { + switch operation { + case let .update(key, value): + if key == self.displaySavedMessagesAsTopicListPreferencesKey { + if self.displaySavedMessagesAsTopicList != value { + self.displaySavedMessagesAsTopicList = value + hasChanges = true + } + } + } + } + } + if case .root = self.groupId, self.filterPredicate == nil { var invalidatedGroups = false for (groupId, groupOperations) in operations { @@ -938,6 +965,7 @@ public final class ChatListView { public let groupEntries: [ChatListGroupReferenceEntry] public let earlierIndex: ChatListIndex? public let laterIndex: ChatListIndex? + public let displaySavedMessagesAsTopicList: PreferencesEntry? init(_ mutableView: MutableChatListView) { self.groupId = mutableView.groupId @@ -1006,5 +1034,6 @@ public final class ChatListView { } self.additionalItemEntries = additionalItemEntries + self.displaySavedMessagesAsTopicList = mutableView.displaySavedMessagesAsTopicList } } diff --git a/submodules/Postbox/Sources/SeedConfiguration.swift b/submodules/Postbox/Sources/SeedConfiguration.swift index f389647aa2..66e0d82094 100644 --- a/submodules/Postbox/Sources/SeedConfiguration.swift +++ b/submodules/Postbox/Sources/SeedConfiguration.swift @@ -80,6 +80,7 @@ public final class SeedConfiguration { public let isPeerUpgradeMessage: (Message) -> Bool public let automaticThreadIndexInfo: (PeerId, Int64) -> StoredMessageHistoryThreadInfo? public let customTagsFromAttributes: ([MessageAttribute]) -> [MemoryBuffer] + public let displaySavedMessagesAsTopicListPreferencesKey: ValueBoxKey public init( globalMessageIdsPeerIdNamespaces: Set, @@ -109,7 +110,8 @@ public final class SeedConfiguration { decodeDisplayPeerAsRegularChat: @escaping (CachedPeerData) -> Bool, isPeerUpgradeMessage: @escaping (Message) -> Bool, automaticThreadIndexInfo: @escaping (PeerId, Int64) -> StoredMessageHistoryThreadInfo?, - customTagsFromAttributes: @escaping ([MessageAttribute]) -> [MemoryBuffer] + customTagsFromAttributes: @escaping ([MessageAttribute]) -> [MemoryBuffer], + displaySavedMessagesAsTopicListPreferencesKey: ValueBoxKey ) { self.globalMessageIdsPeerIdNamespaces = globalMessageIdsPeerIdNamespaces self.initializeChatListWithHole = initializeChatListWithHole @@ -135,5 +137,6 @@ public final class SeedConfiguration { self.isPeerUpgradeMessage = isPeerUpgradeMessage self.automaticThreadIndexInfo = automaticThreadIndexInfo self.customTagsFromAttributes = customTagsFromAttributes + self.displaySavedMessagesAsTopicListPreferencesKey = displaySavedMessagesAsTopicListPreferencesKey } } diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index f9e693c813..3d329a577c 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -295,7 +295,8 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView topForumTopicItems: [], autoremoveTimeout: nil, storyState: nil, - requiresPremiumForMessaging: false + requiresPremiumForMessaging: false, + displayAsTopicList: false )), editing: false, hasActiveRevealControls: false, diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index 29a2264c0d..4e664b9040 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -442,7 +442,8 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { topForumTopicItems: [], autoremoveTimeout: nil, storyState: nil, - requiresPremiumForMessaging: false + requiresPremiumForMessaging: false, + displayAsTopicList: false )), editing: false, hasActiveRevealControls: false, diff --git a/submodules/ShareController/Sources/ShareControllerNode.swift b/submodules/ShareController/Sources/ShareControllerNode.swift index 7c33a21936..90c8435a2f 100644 --- a/submodules/ShareController/Sources/ShareControllerNode.swift +++ b/submodules/ShareController/Sources/ShareControllerNode.swift @@ -1458,7 +1458,8 @@ private func threadList(accountPeerId: EnginePeer.Id, postbox: Postbox, peerId: hasFailed: false, isContact: false, autoremoveTimeout: nil, - storyStats: nil + storyStats: nil, + displayAsTopicList: false )) } diff --git a/submodules/TelegramCore/Sources/ForumChannels.swift b/submodules/TelegramCore/Sources/ForumChannels.swift index c0040a2c68..79351361b7 100644 --- a/submodules/TelegramCore/Sources/ForumChannels.swift +++ b/submodules/TelegramCore/Sources/ForumChannels.swift @@ -1136,7 +1136,8 @@ public func _internal_searchForumTopics(account: Account, peerId: EnginePeer.Id, hasFailed: false, isContact: false, autoremoveTimeout: nil, - storyStats: nil + storyStats: nil, + displayAsTopicList: false )) } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift index 720a5ee8f9..2f0e5193ce 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift @@ -278,6 +278,7 @@ private enum PreferencesKeyValues: Int32 { case storiesConfiguration = 32 case audioTranscriptionTrialState = 33 case didCacheSavedMessageTagsPrefix = 34 + case displaySavedChatsAsTopics = 35 } public func applicationSpecificPreferencesKey(_ value: Int32) -> ValueBoxKey { @@ -455,6 +456,12 @@ public struct PreferencesKeys { key.setInt64(4, value: threadId ?? 0) return key } + + public static func displaySavedChatsAsTopics() -> ValueBoxKey { + let key = ValueBoxKey(length: 4) + key.setInt32(0, value: PreferencesKeyValues.displaySavedChatsAsTopics.rawValue) + return key + } } private enum SharedDataKeyValues: Int32 { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_StandaloneAccountTransaction.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_StandaloneAccountTransaction.swift index 2932141570..94bc0cd63d 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_StandaloneAccountTransaction.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_StandaloneAccountTransaction.swift @@ -235,7 +235,8 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = { } return result - } + }, + displaySavedMessagesAsTopicListPreferencesKey: PreferencesKeys.displaySavedChatsAsTopics() ) }() diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift index d24b4d1840..9b3049aac8 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift @@ -18,6 +18,14 @@ public enum EnginePeerCachedInfoItem { } } +public struct EngineDisplaySavedChatsAsTopics: Codable, Equatable { + public var value: Bool + + public init(value: Bool) { + self.value = value + } +} + extension EnginePeerCachedInfoItem: Equatable where T: Equatable { public static func ==(lhs: EnginePeerCachedInfoItem, rhs: EnginePeerCachedInfoItem) -> Bool { switch lhs { @@ -1245,5 +1253,28 @@ public extension TelegramEngine.EngineData.Item { } } + public struct DisplaySavedChatsAsTopics: TelegramEngineDataItem, PostboxViewDataItem { + public typealias Result = Bool + + public init() { + } + + var key: PostboxViewKey { + return .preferences(keys: Set([PreferencesKeys.displaySavedChatsAsTopics()])) + } + + func extract(view: PostboxView) -> Result { + guard let view = view as? PreferencesView else { + preconditionFailure() + } + + if let value = view.values[PreferencesKeys.displaySavedChatsAsTopics()]?.get(EngineDisplaySavedChatsAsTopics.self) { + return value.value + } else { + return false + } + } + } + } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift index 6a765d40c7..a0544481ee 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift @@ -129,6 +129,7 @@ public final class EngineChatList: Equatable { public let isContact: Bool public let autoremoveTimeout: Int32? public let storyStats: StoryStats? + public let displayAsTopicList: Bool public init( id: Id, @@ -147,7 +148,8 @@ public final class EngineChatList: Equatable { hasFailed: Bool, isContact: Bool, autoremoveTimeout: Int32?, - storyStats: StoryStats? + storyStats: StoryStats?, + displayAsTopicList: Bool ) { self.id = id self.index = index @@ -166,6 +168,7 @@ public final class EngineChatList: Equatable { self.isContact = isContact self.autoremoveTimeout = autoremoveTimeout self.storyStats = storyStats + self.displayAsTopicList = displayAsTopicList } public static func ==(lhs: Item, rhs: Item) -> Bool { @@ -220,6 +223,9 @@ public final class EngineChatList: Equatable { if lhs.storyStats != rhs.storyStats { return false } + if lhs.displayAsTopicList != rhs.displayAsTopicList { + return false + } return true } } @@ -419,7 +425,7 @@ public extension EngineChatList.RelativePosition { } extension EngineChatList.Item { - convenience init?(_ entry: ChatListEntry) { + convenience init?(_ entry: ChatListEntry, displayAsTopicList: Bool) { switch entry { case let .MessageEntry(entryData): let index = entryData.index @@ -504,7 +510,8 @@ extension EngineChatList.Item { hasFailed: hasFailed, isContact: isContact, autoremoveTimeout: autoremoveTimeout, - storyStats: entryData.storyStats + storyStats: entryData.storyStats, + displayAsTopicList: displayAsTopicList ) case .HoleEntry: return nil @@ -544,7 +551,7 @@ extension EngineChatList.AdditionalItem.PromoInfo { extension EngineChatList.AdditionalItem { convenience init?(_ entry: ChatListAdditionalItemEntry) { - guard let item = EngineChatList.Item(entry.entry) else { + guard let item = EngineChatList.Item(entry.entry, displayAsTopicList: false) else { return nil } guard let promoInfo = (entry.info as? PromoChatListItem).flatMap(EngineChatList.AdditionalItem.PromoInfo.init) else { @@ -555,14 +562,19 @@ extension EngineChatList.AdditionalItem { } public extension EngineChatList { - convenience init(_ view: ChatListView) { + convenience init(_ view: ChatListView, accountPeerId: PeerId) { var isLoading = false + + var displaySavedMessagesAsTopicList = false + if let value = view.displaySavedMessagesAsTopicList?.get(EngineDisplaySavedChatsAsTopics.self) { + displaySavedMessagesAsTopicList = value.value + } var items: [EngineChatList.Item] = [] loop: for entry in view.entries { switch entry { case .MessageEntry: - if let item = EngineChatList.Item(entry) { + if let item = EngineChatList.Item(entry, displayAsTopicList: entry.index.messageIndex.id.peerId == accountPeerId ? displaySavedMessagesAsTopicList : false) { items.append(item) } case .HoleEntry: diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index 70c5e6c3b9..ec3fc8a859 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -328,9 +328,10 @@ public extension TelegramEngine { } public func chatList(group: EngineChatList.Group, count: Int) -> Signal { + let accountPeerId = self.account.peerId return self.account.postbox.tailChatListView(groupId: group._asGroup(), count: count, summaryComponents: ChatListEntrySummaryComponents()) |> map { view -> EngineChatList in - return EngineChatList(view.0) + return EngineChatList(view.0, accountPeerId: accountPeerId) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index beed309046..e11a85ab14 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -1346,6 +1346,14 @@ public extension TelegramEngine { |> distinctUntilChanged } } + + public func updateSavedMessagesViewAsTopics(value: Bool) { + let _ = (self.account.postbox.transaction { transaction -> Void in + transaction.updatePreferencesEntry(key: PreferencesKeys.displaySavedChatsAsTopics(), { _ in + return PreferencesEntry(EngineDisplaySavedChatsAsTopics(value: value)) + }) + }).start() + } } } diff --git a/submodules/TelegramUI/Components/MoreHeaderButton/Sources/MoreHeaderButton.swift b/submodules/TelegramUI/Components/MoreHeaderButton/Sources/MoreHeaderButton.swift index 89afc9f9d3..e559c37311 100644 --- a/submodules/TelegramUI/Components/MoreHeaderButton/Sources/MoreHeaderButton.swift +++ b/submodules/TelegramUI/Components/MoreHeaderButton/Sources/MoreHeaderButton.swift @@ -52,10 +52,10 @@ public final class MoreHeaderButton: HighlightableButtonNode { strongSelf.contextAction?(strongSelf.containerNode, gesture) } - self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 26.0, height: 44.0)) + self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 30.0, height: 44.0)) self.referenceNode.frame = self.containerNode.bounds - self.iconNode.image = MoreHeaderButton.optionsCircleImage(color: color) + //self.iconNode.image = MoreHeaderButton.optionsCircleImage(color: color) if let image = self.iconNode.image { self.iconNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - image.size.width) / 2.0), y: floor((self.containerNode.bounds.height - image.size.height) / 2.0)), size: image.size) } @@ -72,11 +72,13 @@ public final class MoreHeaderButton: HighlightableButtonNode { private var content: Content? public func setContent(_ content: Content, animated: Bool = false) { if case .more = content { - let animationSize = CGSize(width: 22.0, height: 22.0) + let animationSize = CGSize(width: 30.0, height: 30.0) let _ = self.animationView.update( transition: .immediate, component: AnyComponent(LottieComponent( - content: LottieComponent.AppBundleContent(name: "anim_profilemore"), + content: LottieComponent.AppBundleContent( + name: "anim_moredots" + ), color: self.color )), environment: {}, @@ -119,13 +121,13 @@ public final class MoreHeaderButton: HighlightableButtonNode { if let animationComponentView = self.animationView.view { animationComponentView.isHidden = true } - case let .more(image): - if let image = image { + case .more: + /*if let image = image { self.iconNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - image.size.width) / 2.0), y: floor((self.containerNode.bounds.height - image.size.height) / 2.0)), size: image.size) } self.iconNode.image = image - self.iconNode.isHidden = false + self.iconNode.isHidden = false*/ if let animationComponentView = self.animationView.view { animationComponentView.isHidden = false } @@ -143,13 +145,13 @@ public final class MoreHeaderButton: HighlightableButtonNode { if let animationComponentView = self.animationView.view { animationComponentView.isHidden = true } - case let .more(image): - if let image = image { + case .more: + /*if let image = image { self.iconNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - image.size.width) / 2.0), y: floor((self.containerNode.bounds.height - image.size.height) / 2.0)), size: image.size) } self.iconNode.image = image - self.iconNode.isHidden = false + self.iconNode.isHidden = false*/ if let animationComponentView = self.animationView.view { animationComponentView.isHidden = false } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift index 32d33aeb28..c6ee786758 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift @@ -79,6 +79,7 @@ public final class PeerInfoChatListPaneNode: ASDisplayNode, PeerInfoPaneNode, UI autoSetReady: false, isMainTab: nil ) + self.chatListNode.synchronousDrawingWhenNotAnimated = true super.init() diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD index f78e733852..1123166542 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD @@ -137,6 +137,7 @@ swift_library( "//submodules/MediaPickerUI", "//submodules/AttachmentUI", "//submodules/TelegramUI/Components/Settings/BoostLevelIconComponent", + "//submodules/Components/MultilineTextComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift index ba6816923c..a59cf60724 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift @@ -860,13 +860,23 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen let hasSavedMessageTags: Signal if let peerId = chatLocation.peerId { - hasSavedMessageTags = context.engine.data.subscribe( - TelegramEngine.EngineData.Item.Messages.SavedMessageTagStats(peerId: context.account.peerId, threadId: peerId.toInt64()) - ) - |> map { tags -> Bool in - return !tags.isEmpty + if case .peer = chatLocation { + hasSavedMessageTags = context.engine.data.subscribe( + TelegramEngine.EngineData.Item.Messages.SavedMessageTagStats(peerId: context.account.peerId, threadId: nil) + ) + |> map { tags -> Bool in + return !tags.isEmpty + } + |> distinctUntilChanged + } else { + hasSavedMessageTags = context.engine.data.subscribe( + TelegramEngine.EngineData.Item.Messages.SavedMessageTagStats(peerId: context.account.peerId, threadId: peerId.toInt64()) + ) + |> map { tags -> Bool in + return !tags.isEmpty + } + |> distinctUntilChanged } - |> distinctUntilChanged } else { hasSavedMessageTags = .single(false) } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift index 0f54c0fc1f..619f89e4a6 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift @@ -250,6 +250,10 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { icon = nil isAnimation = true animationState = .search + case .standaloneSearch: + text = "" + accessibilityText = presentationData.strings.Common_Search + icon = PresentationResourcesRootController.navigationCompactSearchIcon(presentationData.theme) case .searchWithTags: text = "" accessibilityText = presentationData.strings.Common_Search diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift index b8b94f3582..cc71f68e12 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift @@ -14,6 +14,7 @@ enum PeerInfoHeaderNavigationButtonKey { case selectionDone case search case searchWithTags + case standaloneSearch case editPhoto case editVideo case more @@ -52,11 +53,11 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode { } for (_, button) in self.rightButtonNodes { button.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, canBeExpanded: canBeExpanded, transition: transition) - transition.updateSublayerTransformOffset(layer: button.layer, offset: CGPoint(x: canBeExpanded ? 8.0 : 0.0, y: 0.0)) + transition.updateSublayerTransformOffset(layer: button.layer, offset: CGPoint(x: canBeExpanded ? 16.0 : 0.0, y: 0.0)) } } - func update(size: CGSize, presentationData: PresentationData, leftButtons: [PeerInfoHeaderNavigationButtonSpec], rightButtons: [PeerInfoHeaderNavigationButtonSpec], expandFraction: CGFloat, transition: ContainedViewLayoutTransition) { + func update(size: CGSize, presentationData: PresentationData, leftButtons: [PeerInfoHeaderNavigationButtonSpec], rightButtons: [PeerInfoHeaderNavigationButtonSpec], expandFraction: CGFloat, shouldAnimateIn: Bool, transition: ContainedViewLayoutTransition) { let sideInset: CGFloat = 24.0 let maximumExpandOffset: CGFloat = 14.0 @@ -201,7 +202,7 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode { if case .postStory = spec.key { buttonFrame.origin.x -= 12.0 } - nextButtonOrigin -= buttonSize.width + 4.0 + nextButtonOrigin -= buttonSize.width + 15.0 if spec.isForExpandedView { nextExpandedButtonOrigin = nextButtonOrigin } else { @@ -211,15 +212,17 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode { if wasAdded { buttonNode.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, canBeExpanded: self.canBeExpanded, transition: .immediate) - if key == .moreToSearch || key == .searchWithTags { - buttonNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2) + if shouldAnimateIn { + if key == .moreToSearch || key == .searchWithTags || key == .standaloneSearch { + buttonNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2) + } } buttonNode.frame = buttonFrame buttonNode.alpha = 0.0 transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor) - transition.updateSublayerTransformOffset(layer: buttonNode.layer, offset: CGPoint(x: canBeExpanded ? 8.0 : 0.0, y: 0.0)) + transition.updateSublayerTransformOffset(layer: buttonNode.layer, offset: CGPoint(x: canBeExpanded ? 16.0 : 0.0, y: 0.0)) } else { transition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame) transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor) @@ -237,7 +240,7 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode { } for key in removeKeys { if let buttonNode = self.rightButtonNodes.removeValue(forKey: key) { - if key == .moreToSearch || key == .searchWithTags { + if key == .moreToSearch || key == .searchWithTags || key == .standaloneSearch { buttonNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak buttonNode] _ in buttonNode?.removeFromSupernode() }) @@ -264,7 +267,7 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode { if case .postStory = spec.key { buttonFrame.origin.x -= 12.0 } - nextButtonOrigin -= buttonSize.width + 4.0 + nextButtonOrigin -= buttonSize.width + 15.0 if spec.isForExpandedView { nextExpandedButtonOrigin = nextButtonOrigin } else { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift index c68191623a..8a7ee03cc9 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift @@ -37,6 +37,7 @@ import ChatAvatarNavigationNode import MultiScaleTextNode import PeerInfoCoverComponent import PeerInfoPaneNode +import MultilineTextComponent final class PeerInfoHeaderNavigationTransition { let sourceNavigationBar: NavigationBar @@ -108,6 +109,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { let titleNodeContainer: ASDisplayNode let titleNodeRawContainer: ASDisplayNode let titleNode: MultiScaleTextNode + var standardTitle: ComponentView? let titleCredibilityIconView: ComponentHostView var credibilityIconSize: CGSize? @@ -991,12 +993,15 @@ final class PeerInfoHeaderNode: ASDisplayNode { let titleShadowColor: UIColor? = nil + var displayStandardTitle = false + if let peer = peer { var title: String if peer.id == self.context.account.peerId && !self.isSettings { if case .replyThread = self.chatLocation { title = presentationData.strings.Conversation_MyNotes } else { + displayStandardTitle = true title = presentationData.strings.Conversation_SavedMessages } } else if peer.id.isAnonymousSavedMessages { @@ -1789,6 +1794,41 @@ final class PeerInfoHeaderNode: ASDisplayNode { } } + if displayStandardTitle { + self.titleNode.isHidden = true + + let standardTitle: ComponentView + if let current = self.standardTitle { + standardTitle = current + } else { + standardTitle = ComponentView() + self.standardTitle = standardTitle + } + + let titleSize = standardTitle.update( + transition: .immediate, + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: titleStringText, font: Font.semibold(17.0), textColor: navigationContentsPrimaryColor)) + )), + environment: {}, + containerSize: CGSize(width: width, height: navigationHeight) + ) + if let standardTitleView = standardTitle.view { + if standardTitleView.superview == nil { + self.regularContentNode.view.addSubview(standardTitleView) + } + let standardTitleFrame = titleSize.centered(in: self.titleNodeContainer.frame).offsetBy(dx: 2.0, dy: 0.0) + standardTitleView.frame = standardTitleFrame + } + } else { + if let standardTitle = self.standardTitle { + self.standardTitle = nil + standardTitle.view?.removeFromSuperview() + + self.titleNode.isHidden = false + } + } + let buttonsTransitionDistance: CGFloat = -min(0.0, apparentBackgroundHeight - backgroundHeight) let buttonsTransitionDistanceNorm: CGFloat = 40.0 diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index f5371f0ffd..2e8eb8674b 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -3758,11 +3758,17 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } strongSelf.chatInterfaceInteraction.selectionState = strongSelf.state.selectedMessageIds.flatMap { ChatInterfaceSelectionState(selectedIds: $0) } strongSelf.paneContainerNode.updateSelectedMessageIds(strongSelf.state.selectedMessageIds, animated: true) - case .search, .searchWithTags: + case .search, .searchWithTags, .standaloneSearch: strongSelf.activateSearch() case .more: - if let source = source { - strongSelf.displayMediaGalleryContextMenu(source: source, gesture: gesture) + if let currentPaneKey = strongSelf.paneContainerNode.currentPaneKey, case .savedMessagesChats = currentPaneKey { + if let controller = strongSelf.controller, let source { + PeerInfoScreenImpl.openSavedMessagesMoreMenu(context: strongSelf.context, sourceController: controller, isViewingAsTopics: true, sourceView: source.view, gesture: gesture) + } + } else { + if let source = source { + strongSelf.displayMediaGalleryContextMenu(source: source, gesture: gesture) + } } case .qrCode: strongSelf.openQrCode() @@ -10177,7 +10183,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } self.ignoreScrolling = false - self.updateNavigation(transition: transition, additive: additive, animateHeader: true) + self.updateNavigation(transition: transition, additive: additive, animateHeader: self.controller?.didAppear ?? false) if !self.didSetReady && self.data != nil { self.didSetReady = true @@ -10287,8 +10293,15 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if self.state.selectedMessageIds == nil { if let currentPaneKey = self.paneContainerNode.currentPaneKey { switch currentPaneKey { - case .files, .music, .links, .members, .savedMessagesChats: + case .files, .music, .links, .members: rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .search, isForExpandedView: true)) + case .savedMessagesChats: + if let data = self.data, data.hasSavedMessageTags { + rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .searchWithTags, isForExpandedView: true)) + } else { + rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .standaloneSearch, isForExpandedView: true)) + } + rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .more, isForExpandedView: true)) case .savedMessages: if let data = self.data, data.hasSavedMessageTags { rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .searchWithTags, isForExpandedView: true)) @@ -10318,7 +10331,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro leftNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .back, isForExpandedView: false)) } } - self.headerNode.navigationButtonContainer.update(size: CGSize(width: layout.size.width - layout.safeInsets.left * 2.0, height: navigationBarHeight), presentationData: self.presentationData, leftButtons: leftNavigationButtons, rightButtons: rightNavigationButtons, expandFraction: effectiveAreaExpansionFraction, transition: transition) + self.headerNode.navigationButtonContainer.update(size: CGSize(width: layout.size.width - layout.safeInsets.left * 2.0, height: navigationBarHeight), presentationData: self.presentationData, leftButtons: leftNavigationButtons, rightButtons: rightNavigationButtons, expandFraction: effectiveAreaExpansionFraction, shouldAnimateIn: animateHeader, transition: transition) } } @@ -10622,6 +10635,8 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc } } + var didAppear: Bool = false + private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)? public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], isSettings: Bool = false, hintGroupInCommon: PeerId? = nil, requestsContext: PeerInvitationImportersContext? = nil, forumTopicThread: ChatReplyThreadMessage? = nil, switchToRecommendedChannels: Bool = false) { @@ -11145,6 +11160,10 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc override public func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) + DispatchQueue.main.async { [weak self] in + self?.didAppear = true + } + var chatNavigationStack: [ChatNavigationStackItem] = [] if !self.isSettings, let summary = self.customNavigationDataSummary as? ChatControllerNavigationDataSummary { chatNavigationStack.removeAll() @@ -11266,6 +11285,61 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc ] } } + + public static func openSavedMessagesMoreMenu(context: AccountContext, sourceController: ViewController, isViewingAsTopics: Bool, sourceView: UIView, gesture: ContextGesture?) { + let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)) + |> deliverOnMainQueue).startStandalone(next: { peer in + guard let peer else { + return + } + + let strings = context.sharedContext.currentPresentationData.with { $0 }.strings + + var items: [ContextMenuItem] = [] + + //TODO:localize + items.append(.action(ContextMenuActionItem(text: "View as Chats", icon: { theme in + if !isViewingAsTopics { + return nil + } + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) + }, iconPosition: .left, action: { [weak sourceController] _, a in + a(.default) + + guard let sourceController = sourceController, let navigationController = sourceController.navigationController as? NavigationController else { + return + } + + if let infoController = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) { + navigationController.replaceController(sourceController, with: infoController, animated: false) + } + + context.engine.peers.updateSavedMessagesViewAsTopics(value: true) + }))) + items.append(.action(ContextMenuActionItem(text: strings.Chat_ContextViewAsMessages, icon: { theme in + if isViewingAsTopics { + return nil + } + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) + }, iconPosition: .left, action: { [weak sourceController] _, a in + a(.default) + + guard let sourceController = sourceController, let navigationController = sourceController.navigationController as? NavigationController else { + return + } + + let chatController = context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: context.account.peerId), subject: nil, botStart: nil, mode: .standard(.default)) + + navigationController.replaceController(sourceController, with: chatController, animated: false) + + context.engine.peers.updateSavedMessagesViewAsTopics(value: false) + }))) + + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: sourceController, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + sourceController.presentInGlobalOverlay(contextController) + }) + } } private final class SettingsTabBarContextExtractedContentSource: ContextExtractedContentSource { @@ -12432,3 +12506,17 @@ private final class PeerInfoControllerContextReferenceContentSource: ContextRefe return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds.inset(by: self.insets), insets: self.contentInsets) } } + +private final class HeaderContextReferenceContentSource: ContextReferenceContentSource { + private let controller: ViewController + private let sourceView: UIView + + init(controller: ViewController, sourceView: UIView) { + self.controller = controller + self.sourceView = sourceView + } + + func transitionInfo() -> ContextControllerReferenceViewInfo? { + return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds) + } +} diff --git a/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift b/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift index 78a4cc1605..89dee442cb 100644 --- a/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift +++ b/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift @@ -937,7 +937,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate topForumTopicItems: [], autoremoveTimeout: nil, storyState: nil, - requiresPremiumForMessaging: false + requiresPremiumForMessaging: false, + displayAsTopicList: false )), editing: false, hasActiveRevealControls: false, diff --git a/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift b/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift index 6fa8d2433f..f9d64a470d 100644 --- a/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift +++ b/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift @@ -452,23 +452,58 @@ func updateChatPresentationInterfaceStateImpl( selfController.leftNavigationButton = nil } + var buttonsAnimated = transition.isAnimated if let button = rightNavigationButtonForChatInterfaceState(context: selfController.context, presentationInterfaceState: updatedChatPresentationInterfaceState, strings: updatedChatPresentationInterfaceState.strings, currentButton: selfController.rightNavigationButton, target: selfController, selector: #selector(selfController.rightNavigationButtonAction), chatInfoNavigationButton: selfController.chatInfoNavigationButton, moreInfoNavigationButton: selfController.moreInfoNavigationButton) { if selfController.rightNavigationButton != button { - var animated = transition.isAnimated if let currentButton = selfController.rightNavigationButton?.action, currentButton == button.action { - animated = false + buttonsAnimated = false } if case .replyThread = selfController.chatLocation { - animated = false + buttonsAnimated = false } - selfController.navigationItem.setRightBarButton(button.buttonItem, animated: animated) selfController.rightNavigationButton = button } } else if let _ = selfController.rightNavigationButton { - selfController.navigationItem.setRightBarButton(nil, animated: transition.isAnimated) selfController.rightNavigationButton = nil } + if let button = secondaryRightNavigationButtonForChatInterfaceState(context: selfController.context, presentationInterfaceState: updatedChatPresentationInterfaceState, strings: updatedChatPresentationInterfaceState.strings, currentButton: selfController.secondaryRightNavigationButton, target: selfController, selector: #selector(selfController.secondaryRightNavigationButtonAction), chatInfoNavigationButton: selfController.chatInfoNavigationButton, moreInfoNavigationButton: selfController.moreInfoNavigationButton) { + if selfController.secondaryRightNavigationButton != button { + if let currentButton = selfController.secondaryRightNavigationButton?.action, currentButton == button.action { + buttonsAnimated = false + } + if case .replyThread = selfController.chatLocation { + buttonsAnimated = false + } + selfController.secondaryRightNavigationButton = button + } + } else if let _ = selfController.secondaryRightNavigationButton { + selfController.secondaryRightNavigationButton = nil + } + + var rightBarButtons: [UIBarButtonItem] = [] + if let rightNavigationButton = selfController.rightNavigationButton { + rightBarButtons.append(rightNavigationButton.buttonItem) + } + if let secondaryRightNavigationButton = selfController.secondaryRightNavigationButton { + rightBarButtons.append(secondaryRightNavigationButton.buttonItem) + } + var rightBarButtonsUpdated = false + let currentRightBarButtons = selfController.navigationItem.rightBarButtonItems ?? [] + if rightBarButtons.count != currentRightBarButtons.count { + rightBarButtonsUpdated = true + } else { + for i in 0 ..< rightBarButtons.count { + if rightBarButtons[i] !== currentRightBarButtons[i] { + rightBarButtonsUpdated = true + break + } + } + } + if rightBarButtonsUpdated { + selfController.navigationItem.setRightBarButtonItems(rightBarButtons, animated: buttonsAnimated) + } + if let controllerInteraction = selfController.controllerInteraction { if updatedChatPresentationInterfaceState.interfaceState.selectionState != controllerInteraction.selectionState { controllerInteraction.selectionState = updatedChatPresentationInterfaceState.interfaceState.selectionState diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 29da10983f..cf7f13f48f 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -122,6 +122,7 @@ import WallpaperGalleryScreen import WallpaperGridScreen import VideoMessageCameraScreen import TopMessageReactions +import PeerInfoScreen public enum ChatControllerPeekActions { case standard @@ -267,6 +268,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var chatTitleView: ChatTitleView? var leftNavigationButton: ChatNavigationButton? var rightNavigationButton: ChatNavigationButton? + var secondaryRightNavigationButton: ChatNavigationButton? var chatInfoNavigationButton: ChatNavigationButton? var moreBarButton: MoreHeaderButton @@ -4725,13 +4727,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.moreBarButton.setContent(.more(MoreHeaderButton.optionsCircleImage(color: self.presentationData.theme.rootController.navigationBar.buttonColor))) self.moreInfoNavigationButton = ChatNavigationButton(action: .toggleInfoPanel, buttonItem: UIBarButtonItem(customDisplayNode: self.moreBarButton)!) self.moreBarButton.contextAction = { [weak self] sourceNode, gesture in - guard let self = self else { + guard let self else { return } guard case let .peer(peerId) = self.chatLocation else { return } - ChatListControllerImpl.openMoreMenu(context: self.context, peerId: peerId, sourceController: self, isViewingAsTopics: false, sourceView: sourceNode.view, gesture: gesture) + + if peerId == self.context.account.peerId { + PeerInfoScreenImpl.openSavedMessagesMoreMenu(context: self.context, sourceController: self, isViewingAsTopics: false, sourceView: sourceNode.view, gesture: gesture) + } else { + ChatListControllerImpl.openMoreMenu(context: self.context, peerId: peerId, sourceController: self, isViewingAsTopics: false, sourceView: sourceNode.view, gesture: gesture) + } } self.moreBarButton.addTarget(self, action: #selector(self.moreButtonPressed), forControlEvents: .touchUpInside) @@ -12087,6 +12094,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } + @objc func secondaryRightNavigationButtonAction() { + if let button = self.secondaryRightNavigationButton { + self.navigationButtonAction(button.action) + } + } + @objc func moreButtonPressed() { self.moreBarButton.play() self.moreBarButton.contextAction?(self.moreBarButton.containerNode, nil) diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift index 46ef536683..a7364265f0 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift @@ -182,3 +182,11 @@ func rightNavigationButtonForChatInterfaceState(context: AccountContext, present return chatInfoNavigationButton } + +func secondaryRightNavigationButtonForChatInterfaceState(context: AccountContext, presentationInterfaceState: ChatPresentationInterfaceState, strings: PresentationStrings, currentButton: ChatNavigationButton?, target: Any?, selector: Selector?, chatInfoNavigationButton: ChatNavigationButton?, moreInfoNavigationButton: ChatNavigationButton?) -> ChatNavigationButton? { + if case .peer(context.account.peerId) = presentationInterfaceState.chatLocation { + return moreInfoNavigationButton + } + + return nil +} diff --git a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift index 69b0505429..70ce72f365 100644 --- a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift +++ b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift @@ -104,7 +104,8 @@ private enum ChatListSearchEntry: Comparable, Identifiable { topForumTopicItems: [], autoremoveTimeout: nil, storyState: nil, - requiresPremiumForMessaging: false + requiresPremiumForMessaging: false, + displayAsTopicList: false )), editing: false, hasActiveRevealControls: false, diff --git a/submodules/TelegramUI/Sources/NavigateToChatController.swift b/submodules/TelegramUI/Sources/NavigateToChatController.swift index cddcdac827..e130eb1341 100644 --- a/submodules/TelegramUI/Sources/NavigateToChatController.swift +++ b/submodules/TelegramUI/Sources/NavigateToChatController.swift @@ -41,6 +41,13 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam return false } } + } else if case let .peer(peer) = params.chatLocation, peer.id == params.context.account.peerId { + viewForumAsMessages = params.context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.DisplaySavedChatsAsTopics() + ) + |> map { value in + return !value + } } let _ = (viewForumAsMessages @@ -93,6 +100,14 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam return } + if !viewForumAsMessages, params.subject == nil, case let .peer(peer) = params.chatLocation, peer.id == params.context.account.peerId { + if let controller = params.context.sharedContext.makePeerInfoController(context: params.context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) { + params.navigationController.pushViewController(controller, animated: params.animated, completion: { + }) + return + } + } + var found = false var isFirst = true if params.useExisting {