diff --git a/submodules/AccountContext/Sources/ChatController.swift b/submodules/AccountContext/Sources/ChatController.swift index de3b414bc7..ea25f5ff52 100644 --- a/submodules/AccountContext/Sources/ChatController.swift +++ b/submodules/AccountContext/Sources/ChatController.swift @@ -912,6 +912,24 @@ public extension Peer { } } +public struct ChatControllerCustomNavigationPanelNodeLayoutResult { + public var backgroundHeight: CGFloat + public var insetHeight: CGFloat + public var hitTestSlop: CGFloat + + public init(backgroundHeight: CGFloat, insetHeight: CGFloat, hitTestSlop: CGFloat) { + self.backgroundHeight = backgroundHeight + self.insetHeight = insetHeight + self.hitTestSlop = hitTestSlop + } +} + +public protocol ChatControllerCustomNavigationPanelNode: ASDisplayNode { + typealias LayoutResult = ChatControllerCustomNavigationPanelNodeLayoutResult + + func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, chatController: ChatController) -> LayoutResult +} + public protocol ChatController: ViewController { var chatLocation: ChatLocation { get } var canReadHistory: ValuePromise { get } @@ -919,9 +937,14 @@ public protocol ChatController: ViewController { var purposefulAction: (() -> Void)? { get set } + var stateUpdated: ((ContainedViewLayoutTransition) -> Void)? { get set } + var selectedMessageIds: Set? { get } var presentationInterfaceStateSignal: Signal { get } + var customNavigationBarContentNode: NavigationBarContentNode? { get } + var customNavigationPanelNode: ChatControllerCustomNavigationPanelNode? { get } + func updatePresentationMode(_ mode: ChatControllerPresentationMode) func beginMessageSearch(_ query: String) func displayPromoAnnouncement(text: String) diff --git a/submodules/Display/Source/ContainedViewLayoutTransition.swift b/submodules/Display/Source/ContainedViewLayoutTransition.swift index a5b8eefe16..1b0c8927d8 100644 --- a/submodules/Display/Source/ContainedViewLayoutTransition.swift +++ b/submodules/Display/Source/ContainedViewLayoutTransition.swift @@ -994,6 +994,35 @@ public extension ContainedViewLayoutTransition { } } + func updateTintColor(view: UIView, color: UIColor, completion: ((Bool) -> Void)? = nil) { + if let current = view.tintColor, current == color { + completion?(true) + return + } + + switch self { + case .immediate: + view.tintColor = color + completion?(true) + case let .animated(duration, curve): + let previousColor: CGColor = view.layer.layerTintColor ?? UIColor.clear.cgColor + view.tintColor = color + + view.layer.animate( + from: previousColor, + to: color.cgColor, + keyPath: "contentsMultiplyColor", + timingFunction: curve.timingFunction, + duration: duration, + delay: 0.0, + mediaTimingFunction: curve.mediaTimingFunction, + removeOnCompletion: true, + additive: false, + completion: completion + ) + } + } + func updateContentsRect(layer: CALayer, contentsRect: CGRect, completion: ((Bool) -> Void)? = nil) { if layer.contentsRect == contentsRect { if let completion = completion { diff --git a/submodules/Display/Source/NavigationBar.swift b/submodules/Display/Source/NavigationBar.swift index 1ddb6767cf..a6d3392043 100644 --- a/submodules/Display/Source/NavigationBar.swift +++ b/submodules/Display/Source/NavigationBar.swift @@ -493,6 +493,8 @@ open class NavigationBar: ASDisplayNode { public var makeCustomTransitionNode: ((NavigationBar, Bool) -> CustomNavigationTransitionNode?)? public var allowsCustomTransition: (() -> Bool)? + public var customSetContentNode: ((NavigationBarContentNode?, Bool) -> Void)? + private var collapsed: Bool { get { return self.frame.size.height.isLess(than: 44.0) @@ -1649,6 +1651,11 @@ open class NavigationBar: ASDisplayNode { } public func setContentNode(_ contentNode: NavigationBarContentNode?, animated: Bool) { + if let customSetContentNode = self.customSetContentNode { + customSetContentNode(contentNode, animated) + return + } + if self.contentNode !== contentNode { if let previous = self.contentNode { if animated { diff --git a/submodules/Postbox/Sources/MessageHistoryViewState.swift b/submodules/Postbox/Sources/MessageHistoryViewState.swift index 4e7bd89a52..c5cb3a70c2 100644 --- a/submodules/Postbox/Sources/MessageHistoryViewState.swift +++ b/submodules/Postbox/Sources/MessageHistoryViewState.swift @@ -110,7 +110,7 @@ private extension MessageHistoryInput { assert(Set(items.map({ $0.stableId })).count == items.count) if items.count > limit { - let overLimit = limit - items.count + let overLimit = items.count - limit switch direction { case .lowToHigh: items.removeFirst(overLimit) diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift index d88a54c12e..2bb670a015 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift @@ -21,6 +21,7 @@ public enum PresentationResourceKey: Int32 { case navigationSearchIcon case navigationCompactSearchIcon case navigationCompactTagsSearchIcon + case navigationCompactTagsSearchWhiteIcon case navigationCalendarIcon case navigationMoreIcon case navigationMoreCircledIcon diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift index ab1f46fa75..74bc35d63e 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift @@ -79,6 +79,12 @@ public struct PresentationResourcesRootController { return generateTintedImage(image: UIImage(bundleImageName: "Chat/NavigationSearchTagsIcon"), color: theme.rootController.navigationBar.accentTextColor) }) } + + public static func navigationCompactTagsSearchWhiteIcon(_ theme: PresentationTheme) -> UIImage? { + return theme.image(PresentationResourceKey.navigationCompactTagsSearchIcon.rawValue, { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/NavigationSearchTagsIcon"), color: .white) + }) + } public static func navigationCalendarIcon(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.navigationCalendarIcon.rawValue, { theme in diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageSelectionInputPanelNode/Sources/ChatMessageSelectionInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageSelectionInputPanelNode/Sources/ChatMessageSelectionInputPanelNode.swift index f0afff8d6e..5f71bfcce7 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageSelectionInputPanelNode/Sources/ChatMessageSelectionInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageSelectionInputPanelNode/Sources/ChatMessageSelectionInputPanelNode.swift @@ -427,15 +427,15 @@ public final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode { if let tagButton { buttons = [ self.deleteButton, - self.forwardButton, tagButton, + self.forwardButton, self.shareButton ] } else { buttons = [ self.deleteButton, - self.forwardButton, - self.shareButton + self.shareButton, + self.forwardButton ] } } else if !self.deleteButton.isHidden { @@ -459,17 +459,17 @@ public final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode { if let tagButton { buttons = [ self.deleteButton, - self.forwardButton, self.reportButton, tagButton, - self.shareButton + self.shareButton, + self.forwardButton ] } else { buttons = [ self.deleteButton, - self.forwardButton, self.reportButton, - self.shareButton + self.shareButton, + self.forwardButton ] } } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatPaneNode/Sources/PeerInfoChatPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatPaneNode/Sources/PeerInfoChatPaneNode.swift index 89d0a2a7f0..a6c28575a1 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatPaneNode/Sources/PeerInfoChatPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatPaneNode/Sources/PeerInfoChatPaneNode.swift @@ -11,6 +11,83 @@ import TelegramUIPreferences import AppBundle import PeerInfoPaneNode +private final class SearchNavigationContentNode: ASDisplayNode, PeerInfoPanelNodeNavigationContentNode { + private struct Params: Equatable { + var width: CGFloat + var defaultHeight: CGFloat + var insets: UIEdgeInsets + + init(width: CGFloat, defaultHeight: CGFloat, insets: UIEdgeInsets) { + self.width = width + self.defaultHeight = defaultHeight + self.insets = insets + } + } + + weak var chatController: ChatController? + let contentNode: NavigationBarContentNode + + var panelNode: ChatControllerCustomNavigationPanelNode? + private var appliedPanelNode: ChatControllerCustomNavigationPanelNode? + + private var params: Params? + + init(chatController: ChatController, contentNode: NavigationBarContentNode) { + self.chatController = chatController + self.contentNode = contentNode + + super.init() + + self.addSubnode(self.contentNode) + } + + func update(transition: ContainedViewLayoutTransition) { + if let params = self.params { + let _ = self.update(width: params.width, defaultHeight: params.defaultHeight, insets: params.insets, transition: transition) + } + } + + func update(width: CGFloat, defaultHeight: CGFloat, insets: UIEdgeInsets, transition: ContainedViewLayoutTransition) -> CGFloat { + self.params = Params(width: width, defaultHeight: defaultHeight, insets: insets) + + let size = CGSize(width: width, height: defaultHeight) + transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 10.0), size: size)) + self.contentNode.updateLayout(size: size, leftInset: insets.left, rightInset: insets.right, transition: transition) + + var contentHeight: CGFloat = size.height + 10.0 + + if self.appliedPanelNode !== self.panelNode { + if let previous = self.appliedPanelNode { + transition.updateAlpha(node: previous, alpha: 0.0, completion: { [weak previous] _ in + previous?.removeFromSupernode() + }) + } + + self.appliedPanelNode = self.panelNode + if let panelNode = self.panelNode, let chatController = self.chatController { + self.addSubnode(panelNode) + let panelLayout = panelNode.updateLayout(width: width, leftInset: insets.left, rightInset: insets.right, transition: .immediate, chatController: chatController) + let panelHeight = panelLayout.backgroundHeight + let panelFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: width, height: panelHeight)) + panelNode.frame = panelFrame + panelNode.alpha = 0.0 + transition.updateAlpha(node: panelNode, alpha: 1.0) + + contentHeight += panelHeight - 1.0 + } + } else if let panelNode = self.panelNode, let chatController = self.chatController { + let panelLayout = panelNode.updateLayout(width: width, leftInset: insets.left, rightInset: insets.right, transition: transition, chatController: chatController) + let panelHeight = panelLayout.backgroundHeight + let panelFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: width, height: panelHeight)) + transition.updateFrame(node: panelNode, frame: panelFrame) + + contentHeight += panelHeight - 1.0 + } + + return contentHeight + } +} + public final class PeerInfoChatPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScrollViewDelegate, UIGestureRecognizerDelegate { private let context: AccountContext private let peerId: EnginePeer.Id @@ -51,6 +128,12 @@ public final class PeerInfoChatPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro public var tabBarOffset: CGFloat { return 0.0 } + + private var searchNavigationContentNode: SearchNavigationContentNode? + public var navigationContentNode: PeerInfoPanelNodeNavigationContentNode? { + return self.searchNavigationContentNode + } + public var externalDataUpdated: ((ContainedViewLayoutTransition) -> Void)? private var presentationData: PresentationData private var presentationDataDisposable: Disposable? @@ -77,6 +160,29 @@ public final class PeerInfoChatPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro self.addSubnode(self.chatController.displayNode) self.chatController.displayNode.clipsToBounds = true + + self.chatController.stateUpdated = { [weak self] transition in + guard let self else { + return + } + if let contentNode = self.chatController.customNavigationBarContentNode { + if self.searchNavigationContentNode?.contentNode !== contentNode { + self.searchNavigationContentNode = SearchNavigationContentNode(chatController: self.chatController, contentNode: contentNode) + self.searchNavigationContentNode?.panelNode = self.chatController.customNavigationPanelNode + self.externalDataUpdated?(transition) + } else if self.searchNavigationContentNode?.panelNode !== self.chatController.customNavigationPanelNode { + self.searchNavigationContentNode?.panelNode = self.chatController.customNavigationPanelNode + self.externalDataUpdated?(transition) + } else { + self.searchNavigationContentNode?.update(transition: transition) + } + } else { + if self.searchNavigationContentNode !== nil { + self.searchNavigationContentNode = nil + self.externalDataUpdated?(transition) + } + } + } } deinit { @@ -124,6 +230,9 @@ public final class PeerInfoChatPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro super.didLoad() } + public func activateSearch() { + self.chatController.activateSearch(domain: .everything, query: "") + } override public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { return true diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode/Sources/PeerInfoPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode/Sources/PeerInfoPaneNode.swift index 2cd86e88ac..b5a80db509 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode/Sources/PeerInfoPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoPaneNode/Sources/PeerInfoPaneNode.swift @@ -39,6 +39,10 @@ public struct PeerInfoStatusData: Equatable { } } +public protocol PeerInfoPanelNodeNavigationContentNode: ASDisplayNode { + func update(width: CGFloat, defaultHeight: CGFloat, insets: UIEdgeInsets, transition: ContainedViewLayoutTransition) -> CGFloat +} + public protocol PeerInfoPaneNode: ASDisplayNode { var isReady: Signal { get } @@ -48,6 +52,9 @@ public protocol PeerInfoPaneNode: ASDisplayNode { var tabBarOffsetUpdated: ((ContainedViewLayoutTransition) -> Void)? { get set } var tabBarOffset: CGFloat { get } + var navigationContentNode: PeerInfoPanelNodeNavigationContentNode? { get } + var externalDataUpdated: ((ContainedViewLayoutTransition) -> Void)? { get set } + func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) func scrollToTop() -> Bool func transferVelocity(_ velocity: CGFloat) @@ -59,3 +66,15 @@ public protocol PeerInfoPaneNode: ASDisplayNode { func updateSelectedMessages(animated: Bool) func ensureMessageIsVisible(id: MessageId) } + +public extension PeerInfoPaneNode { + var navigationContentNode: PeerInfoPanelNodeNavigationContentNode? { + return nil + } + var externalDataUpdated: ((ContainedViewLayoutTransition) -> Void)? { + get { + return nil + } set(value) { + } + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift index 03bbb12e0d..ba6816923c 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift @@ -207,6 +207,7 @@ final class PeerInfoScreenData { let appConfiguration: AppConfiguration? let isPowerSavingEnabled: Bool? let accountIsPremium: Bool + let hasSavedMessageTags: Bool let _isContact: Bool var forceIsContact: Bool = false @@ -242,7 +243,8 @@ final class PeerInfoScreenData { threadData: MessageHistoryThreadData?, appConfiguration: AppConfiguration?, isPowerSavingEnabled: Bool?, - accountIsPremium: Bool + accountIsPremium: Bool, + hasSavedMessageTags: Bool ) { self.peer = peer self.chatPeer = chatPeer @@ -267,6 +269,7 @@ final class PeerInfoScreenData { self.appConfiguration = appConfiguration self.isPowerSavingEnabled = isPowerSavingEnabled self.accountIsPremium = accountIsPremium + self.hasSavedMessageTags = hasSavedMessageTags } } @@ -662,7 +665,8 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id, threadData: nil, appConfiguration: appConfiguration, isPowerSavingEnabled: isPowerSavingEnabled, - accountIsPremium: peer?.isPremium ?? false + accountIsPremium: peer?.isPremium ?? false, + hasSavedMessageTags: false ) } } @@ -697,7 +701,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen threadData: nil, appConfiguration: nil, isPowerSavingEnabled: nil, - accountIsPremium: false + accountIsPremium: false, + hasSavedMessageTags: false )) case let .user(userPeerId, secretChatId, kind): let groupsInCommon: GroupsInCommonContext? @@ -853,6 +858,19 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen hasSavedMessagesChats = .single(false) } + 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 + } + |> distinctUntilChanged + } else { + hasSavedMessageTags = .single(false) + } + return combineLatest( context.account.viewTracker.peerView(peerId, updateData: true), peerInfoAvailableMediaPanes(context: context, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder), @@ -863,9 +881,10 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen accountIsPremium, savedMessagesPeer, hasSavedMessagesChats, - hasSavedMessages + hasSavedMessages, + hasSavedMessageTags ) - |> map { peerView, availablePanes, globalNotificationSettings, encryptionKeyFingerprint, status, hasStories, accountIsPremium, savedMessagesPeer, hasSavedMessagesChats, hasSavedMessages -> PeerInfoScreenData in + |> map { peerView, availablePanes, globalNotificationSettings, encryptionKeyFingerprint, status, hasStories, accountIsPremium, savedMessagesPeer, hasSavedMessagesChats, hasSavedMessages, hasSavedMessageTags -> PeerInfoScreenData in var availablePanes = availablePanes if let hasStories { @@ -924,7 +943,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen threadData: nil, appConfiguration: nil, isPowerSavingEnabled: nil, - accountIsPremium: accountIsPremium + accountIsPremium: accountIsPremium, + hasSavedMessageTags: hasSavedMessageTags ) } case .channel: @@ -985,6 +1005,19 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen hasSavedMessagesChats = .single(false) } + 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 + } + |> distinctUntilChanged + } else { + hasSavedMessageTags = .single(false) + } + return combineLatest( context.account.viewTracker.peerView(peerId, updateData: true), peerInfoAvailableMediaPanes(context: context, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder), @@ -998,9 +1031,10 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen accountIsPremium, context.engine.peers.recommendedChannels(peerId: peerId), hasSavedMessages, - hasSavedMessagesChats + hasSavedMessagesChats, + hasSavedMessageTags ) - |> map { peerView, availablePanes, globalNotificationSettings, status, currentInvitationsContext, invitations, currentRequestsContext, requests, hasStories, accountIsPremium, recommendedChannels, hasSavedMessages, hasSavedMessagesChats -> PeerInfoScreenData in + |> map { peerView, availablePanes, globalNotificationSettings, status, currentInvitationsContext, invitations, currentRequestsContext, requests, hasStories, accountIsPremium, recommendedChannels, hasSavedMessages, hasSavedMessagesChats, hasSavedMessageTags -> PeerInfoScreenData in var availablePanes = availablePanes if let hasStories { if hasStories { @@ -1072,7 +1106,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen threadData: nil, appConfiguration: nil, isPowerSavingEnabled: nil, - accountIsPremium: accountIsPremium + accountIsPremium: accountIsPremium, + hasSavedMessageTags: hasSavedMessageTags ) } case let .group(groupId): @@ -1219,6 +1254,19 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen hasSavedMessagesChats = .single(false) } + 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 + } + |> distinctUntilChanged + } else { + hasSavedMessageTags = .single(false) + } + return combineLatest(queue: .mainQueue(), context.account.viewTracker.peerView(groupId, updateData: true), peerInfoAvailableMediaPanes(context: context, peerId: groupId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder), @@ -1233,9 +1281,10 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration]), accountIsPremium, hasSavedMessages, - hasSavedMessagesChats + hasSavedMessagesChats, + hasSavedMessageTags ) - |> mapToSignal { peerView, availablePanes, globalNotificationSettings, status, membersData, currentInvitationsContext, invitations, currentRequestsContext, requests, threadData, preferencesView, accountIsPremium, hasSavedMessages, hasSavedMessagesChats -> Signal in + |> mapToSignal { peerView, availablePanes, globalNotificationSettings, status, membersData, currentInvitationsContext, invitations, currentRequestsContext, requests, threadData, preferencesView, accountIsPremium, hasSavedMessages, hasSavedMessagesChats, hasSavedMessageTags -> Signal in var discussionPeer: Peer? if case let .known(maybeLinkedDiscussionPeerId) = (peerView.cachedData as? CachedChannelData)?.linkedDiscussionPeerId, let linkedDiscussionPeerId = maybeLinkedDiscussionPeerId, let peer = peerView.peers[linkedDiscussionPeerId] { discussionPeer = peer @@ -1320,7 +1369,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen threadData: threadData, appConfiguration: appConfiguration, isPowerSavingEnabled: nil, - accountIsPremium: accountIsPremium + accountIsPremium: accountIsPremium, + hasSavedMessageTags: hasSavedMessageTags )) } } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift index 566be83523..506ac2135f 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift @@ -178,7 +178,7 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { self.backgroundNode.updateColor(color: backgroundColor, transition: transition) transition.updateTintColor(layer: self.textNode.layer, color: self.contentsColor) - transition.updateTintColor(layer: self.iconNode.layer, color: self.contentsColor) + transition.updateTintColor(view: self.iconNode.view, color: self.contentsColor) transition.updateStrokeColor(layer: self.backIconLayer, strokeColor: self.contentsColor) switch self.key { @@ -247,9 +247,13 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { case .search: text = "" accessibilityText = presentationData.strings.Common_Search - icon = nil// PresentationResourcesRootController.navigationCompactSearchIcon(presentationData.theme) + icon = nil isAnimation = true animationState = .search + case .searchWithTags: + text = "" + accessibilityText = presentationData.strings.Common_Search + icon = PresentationResourcesRootController.navigationCompactTagsSearchWhiteIcon(presentationData.theme) case .editPhoto: text = presentationData.strings.Settings_EditPhoto accessibilityText = text diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift index 7d6e435dfa..b8b94f3582 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift @@ -13,6 +13,7 @@ enum PeerInfoHeaderNavigationButtonKey { case select case selectionDone case search + case searchWithTags case editPhoto case editVideo case more @@ -210,7 +211,7 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode { if wasAdded { buttonNode.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, canBeExpanded: self.canBeExpanded, transition: .immediate) - if key == .moreToSearch { + if key == .moreToSearch || key == .searchWithTags { buttonNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2) } @@ -236,7 +237,7 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode { } for key in removeKeys { if let buttonNode = self.rightButtonNodes.removeValue(forKey: key) { - if key == .moreToSearch { + if key == .moreToSearch || key == .searchWithTags { buttonNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak buttonNode] _ in buttonNode?.removeFromSupernode() }) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift index 983445d9ce..c68191623a 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift @@ -169,6 +169,9 @@ final class PeerInfoHeaderNode: ASDisplayNode { var emojiStatusPackDisposable = MetaDisposable() var emojiStatusFileAndPackTitle = Promise<(TelegramMediaFile, LoadedStickerPack)?>() + var customNavigationContentNode: PeerInfoPanelNodeNavigationContentNode? + private var appliedCustomNavigationContentNode: PeerInfoPanelNodeNavigationContentNode? + private var validLayout: (width: CGFloat, deviceMetrics: DeviceMetrics)? init(context: AccountContext, controller: PeerInfoScreenImpl, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, isMediaOnly: Bool, isSettings: Bool, forumTopicThreadId: Int64?, chatLocation: ChatLocation) { @@ -451,6 +454,24 @@ final class PeerInfoHeaderNode: ASDisplayNode { private var currentPanelStatusData: PeerInfoStatusData? func update(width: CGFloat, containerHeight: CGFloat, containerInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, isMediaOnly: Bool, contentOffset: CGFloat, paneContainerY: CGFloat, presentationData: PresentationData, peer: Peer?, cachedData: CachedPeerData?, threadData: MessageHistoryThreadData?, peerNotificationSettings: TelegramPeerNotificationSettings?, threadNotificationSettings: TelegramPeerNotificationSettings?, globalNotificationSettings: EngineGlobalNotificationSettings?, statusData: PeerInfoStatusData?, panelStatusData: (PeerInfoStatusData?, PeerInfoStatusData?, CGFloat?), isSecretChat: Bool, isContact: Bool, isSettings: Bool, state: PeerInfoState, metrics: LayoutMetrics, deviceMetrics: DeviceMetrics, transition: ContainedViewLayoutTransition, additive: Bool, animateHeader: Bool) -> CGFloat { + if self.appliedCustomNavigationContentNode !== self.customNavigationContentNode { + if let previous = self.appliedCustomNavigationContentNode { + transition.updateAlpha(node: previous, alpha: 0.0, completion: { [weak previous] _ in + previous?.removeFromSupernode() + }) + } + + self.appliedCustomNavigationContentNode = self.customNavigationContentNode + if let customNavigationContentNode = self.customNavigationContentNode { + self.addSubnode(customNavigationContentNode) + customNavigationContentNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: navigationHeight)) + customNavigationContentNode.alpha = 0.0 + transition.updateAlpha(node: customNavigationContentNode, alpha: 1.0) + } + } else if let customNavigationContentNode = self.customNavigationContentNode { + transition.updateFrame(node: customNavigationContentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: navigationHeight))) + } + var threadData = threadData if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.peerId == self.context.account.peerId { threadData = nil @@ -516,7 +537,9 @@ final class PeerInfoHeaderNode: ASDisplayNode { isForum = true } - self.regularContentNode.alpha = state.isEditing ? 0.0 : 1.0 + transition.updateAlpha(node: self.regularContentNode, alpha: (state.isEditing || self.customNavigationContentNode != nil) ? 0.0 : 1.0) + transition.updateAlpha(node: self.navigationButtonContainer, alpha: self.customNavigationContentNode != nil ? 0.0 : 1.0) + self.editingContentNode.alpha = state.isEditing ? 1.0 : 0.0 let editingContentHeight = self.editingContentNode.update(width: width, safeInset: containerInset, statusBarHeight: statusBarHeight, navigationHeight: navigationHeight, isModalOverlay: isModalOverlay, peer: state.isEditing ? peer : nil, threadData: threadData, chatLocation: self.chatLocation, cachedData: cachedData, isContact: isContact, isSettings: isSettings, presentationData: presentationData, transition: transition) @@ -1362,6 +1385,9 @@ final class PeerInfoHeaderNode: ASDisplayNode { if self.navigationTransition == nil && !self.isSettings && effectiveSeparatorAlpha == 1.0 && secondarySeparatorAlpha < 1.0 { effectiveSeparatorAlpha = secondarySeparatorAlpha } + if self.customNavigationContentNode != nil { + effectiveSeparatorAlpha = 0.0 + } transition.updateAlpha(node: self.separatorNode, alpha: effectiveSeparatorAlpha) self.titleNode.update(stateFractions: [ @@ -1984,6 +2010,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { } transition.updateFrame(node: self.regularContentNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: resolvedHeight))) + transition.updateFrameAdditive(node: self.buttonsContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: apparentBackgroundHeight - backgroundHeight), size: CGSize(width: width, height: 1000.0))) navigationTransition.updateAlpha(node: self.buttonsContainerNode, alpha: backgroundBannerAlpha) @@ -2078,6 +2105,13 @@ final class PeerInfoHeaderNode: ASDisplayNode { return nil } + if let customNavigationContentNode = self.customNavigationContentNode { + if let result = customNavigationContentNode.view.hitTest(self.view.convert(point, to: customNavigationContentNode.view), with: event) { + return result + } + return self.view + } + let setByFrame = self.avatarListNode.listContainerNode.setByYouNode.view.convert(self.avatarListNode.listContainerNode.setByYouNode.bounds, to: self.view).insetBy(dx: -44.0, dy: 0.0) if self.avatarListNode.listContainerNode.setByYouNode.alpha > 0.0, setByFrame.contains(point) { return self.avatarListNode.listContainerNode.setByYouNode.view diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift index b24aa644b6..0c82b24c55 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift @@ -366,7 +366,8 @@ private final class PeerInfoPendingPane { parentController: ViewController?, openMediaCalendar: @escaping () -> Void, paneDidScroll: @escaping () -> Void, - ensureRectVisible: @escaping (UIView, CGRect) -> Void + ensureRectVisible: @escaping (UIView, CGRect) -> Void, + externalDataUpdated: @escaping (ContainedViewLayoutTransition) -> Void ) { let captureProtected = data.peer?.isCopyProtectionEnabled ?? false let paneNode: PeerInfoPaneNode @@ -425,6 +426,7 @@ private final class PeerInfoPendingPane { case .savedMessages: paneNode = PeerInfoChatPaneNode(context: context, peerId: peerId, navigationController: chatControllerInteraction.navigationController) } + paneNode.externalDataUpdated = externalDataUpdated paneNode.parentController = parentController self.pane = PeerInfoPaneWrapper(key: key, node: paneNode) self.disposable = (paneNode.isReady @@ -458,7 +460,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat let isReady = Promise() var didSetIsReady = false - private var currentParams: (size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, expansionFraction: CGFloat, presentationData: PresentationData, data: PeerInfoScreenData?)? + private var currentParams: (size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, expansionFraction: CGFloat, presentationData: PresentationData, data: PeerInfoScreenData?, areTabsHidden: Bool)? private(set) var currentPaneKey: PeerInfoPaneKey? var pendingSwitchToPaneKey: PeerInfoPaneKey? @@ -495,6 +497,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat var currentPaneUpdated: ((Bool) -> Void)? var requestExpandTabs: (() -> Bool)? + var requestUpdate: ((ContainedViewLayoutTransition) -> Void)? var openMediaCalendar: (() -> Void)? var paneDidScroll: (() -> Void)? @@ -550,8 +553,8 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat if strongSelf.currentPanes[key] != nil { strongSelf.currentPaneKey = key - if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data) = strongSelf.currentParams { - strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: .animated(duration: 0.4, curve: .spring)) + if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden) = strongSelf.currentParams { + strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, transition: .animated(duration: 0.4, curve: .spring)) strongSelf.currentPaneUpdated?(true) @@ -563,8 +566,8 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat strongSelf.pendingSwitchToPaneKey = key strongSelf.expandOnSwitch = true - if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data) = strongSelf.currentParams { - strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: .animated(duration: 0.4, curve: .spring)) + if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden) = strongSelf.currentParams { + strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, transition: .animated(duration: 0.4, curve: .spring)) } } } @@ -586,6 +589,9 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat } return [.leftCenter, .rightCenter] } + if strongSelf.currentPane?.node.navigationContentNode != nil { + return [] + } if index == 0 { return .left } @@ -629,7 +635,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat cancelContextGestures(view: self.view) case .changed: - if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data) = self.currentParams, let availablePanes = data?.availablePanes, availablePanes.count > 1, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) { + if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden) = self.currentParams, let availablePanes = data?.availablePanes, availablePanes.count > 1, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) { let translation = recognizer.translation(in: self.view) var transitionFraction = translation.x / size.width if currentIndex <= 0 { @@ -644,11 +650,11 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat // print(transitionFraction) self.paneTransitionPromise.set(transitionFraction) - self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: .immediate) + self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, transition: .immediate) self.currentPaneUpdated?(false) } case .cancelled, .ended: - if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data) = self.currentParams, let availablePanes = data?.availablePanes, availablePanes.count > 1, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) { + if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden) = self.currentParams, let availablePanes = data?.availablePanes, availablePanes.count > 1, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) { let translation = recognizer.translation(in: self.view) let velocity = recognizer.velocity(in: self.view) var directionIsToRight: Bool? @@ -672,7 +678,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat } } self.transitionFraction = 0.0 - self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: .animated(duration: 0.35, curve: .spring)) + self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, transition: .animated(duration: 0.35, curve: .spring)) self.currentPaneUpdated?(false) self.currentPaneStatusPromise.set(self.currentPane?.node.status ?? .single(nil)) @@ -711,7 +717,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat } } - func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, expansionFraction: CGFloat, presentationData: PresentationData, data: PeerInfoScreenData?, transition: ContainedViewLayoutTransition) { + func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, expansionFraction: CGFloat, presentationData: PresentationData, data: PeerInfoScreenData?, areTabsHidden: Bool, transition: ContainedViewLayoutTransition) { let previousAvailablePanes = self.currentAvailablePanes let availablePanes = data?.availablePanes ?? [] self.currentAvailablePanes = data?.availablePanes @@ -755,7 +761,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat currentIndex = nil } - self.currentParams = (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data) + self.currentParams = (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden) transition.updateAlpha(node: self.coveringBackgroundNode, alpha: expansionFraction) @@ -770,6 +776,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat let isScrollingLockedAtTop = expansionFraction < 1.0 - CGFloat.ulpOfOne let tabsHeight: CGFloat = 48.0 + let effectiveTabsHeight: CGFloat = areTabsHidden ? 0.0 : tabsHeight let paneFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)) @@ -825,12 +832,12 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat guard let strongSelf = self else { return } - if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data) = strongSelf.currentParams { + if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden) = strongSelf.currentParams { var transition: ContainedViewLayoutTransition = .immediate if strongSelf.pendingSwitchToPaneKey == key && strongSelf.currentPaneKey != nil { transition = .animated(duration: 0.4, curve: .spring) } - strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: transition) + strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, transition: transition) } } if leftScope { @@ -849,18 +856,24 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat return } self.ensurePaneRectVisible?(self.view, sourceView.convert(rect, to: self.view)) + }, + externalDataUpdated: { [weak self] transition in + guard let self else { + return + } + self.requestUpdate?(transition) } ) self.pendingPanes[key] = pane pane.pane.node.frame = paneFrame - pane.pane.update(size: paneFrame.size, topInset: tabsHeight, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expansionFraction, presentationData: presentationData, synchronous: true, transition: .immediate) + pane.pane.update(size: paneFrame.size, topInset: effectiveTabsHeight, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expansionFraction, presentationData: presentationData, synchronous: true, transition: .immediate) let paneNode = pane.pane.node pane.pane.node.tabBarOffsetUpdated = { [weak self, weak paneNode] transition in guard let strongSelf = self, let paneNode = paneNode, let currentPane = strongSelf.currentPane, paneNode === currentPane.node else { return } - if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data) = strongSelf.currentParams { - strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: transition) + if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden) = strongSelf.currentParams { + strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, transition: transition) } } leftScope = true @@ -869,7 +882,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat for (key, pane) in self.pendingPanes { pane.pane.node.frame = paneFrame - pane.pane.update(size: paneFrame.size, topInset: tabsHeight, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expansionFraction, presentationData: presentationData, synchronous: self.currentPaneKey == nil, transition: .immediate) + pane.pane.update(size: paneFrame.size, topInset: effectiveTabsHeight, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expansionFraction, presentationData: presentationData, synchronous: self.currentPaneKey == nil, transition: .immediate) if pane.isReady { self.pendingPanes.removeValue(forKey: key) @@ -930,7 +943,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat return } pane.isAnimatingOut = false - if let (_, _, _, _, _, _, _, data) = strongSelf.currentParams { + if let (_, _, _, _, _, _, _, data, _) = strongSelf.currentParams { if let availablePanes = data?.availablePanes, let currentPaneKey = strongSelf.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey), let paneIndex = availablePanes.firstIndex(of: key), abs(paneIndex - currentIndex) <= 1 { } else { if let pane = strongSelf.currentPanes.removeValue(forKey: key) { @@ -961,7 +974,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat paneCompletion() }) } - pane.update(size: paneFrame.size, topInset: tabsHeight, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expansionFraction, presentationData: presentationData, synchronous: paneWasAdded, transition: paneTransition) + pane.update(size: paneFrame.size, topInset: effectiveTabsHeight, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expansionFraction, presentationData: presentationData, synchronous: paneWasAdded, transition: paneTransition) } } @@ -973,7 +986,14 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat if isScrollingLockedAtTop || self.isMediaOnly { tabsOffset = 0.0 } - var tabsAlpha = 1.0 - tabsOffset / tabsHeight + + var tabsAlpha: CGFloat + if areTabsHidden { + tabsAlpha = 0.0 + tabsOffset = tabsHeight + } else { + tabsAlpha = 1.0 - tabsOffset / tabsHeight + } tabsAlpha *= tabsAlpha transition.updateFrame(node: self.tabsContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -tabsOffset), size: CGSize(width: size.width, height: tabsHeight))) transition.updateAlpha(node: self.tabsContainerNode, alpha: tabsAlpha) @@ -1019,7 +1039,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat for (_, pane) in self.pendingPanes { let paneTransition: ContainedViewLayoutTransition = .immediate paneTransition.updateFrame(node: pane.pane.node, frame: paneFrame) - pane.pane.update(size: paneFrame.size, topInset: tabsHeight, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expansionFraction, presentationData: presentationData, synchronous: true, transition: paneTransition) + pane.pane.update(size: paneFrame.size, topInset: effectiveTabsHeight, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expansionFraction, presentationData: presentationData, synchronous: true, transition: paneTransition) } var removeKeys: [PeerInfoPaneKey] = [] diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index a76afc5ec4..f5371f0ffd 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -102,6 +102,7 @@ import PeerInfoPaneNode import MediaPickerUI import AttachmentUI import BoostLevelIconComponent +import PeerInfoChatPaneNode public enum PeerInfoAvatarEditingMode { case generic @@ -3145,6 +3146,15 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } + self.paneContainerNode.requestUpdate = { [weak self] transition in + guard let self else { + return + } + if let (layout, navigationHeight) = self.validLayout { + self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: transition, additive: false) + } + } + self.paneContainerNode.ensurePaneRectVisible = { [weak self] sourceView, rect in guard let self else { return @@ -3748,8 +3758,7 @@ 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: - strongSelf.headerNode.navigationButtonContainer.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue) + case .search, .searchWithTags: strongSelf.activateSearch() case .more: if let source = source { @@ -9269,6 +9278,13 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro return } + if let currentPaneKey = self.paneContainerNode.currentPaneKey, case .savedMessages = currentPaneKey, let paneNode = self.paneContainerNode.currentPane?.node as? PeerInfoChatPaneNode { + paneNode.activateSearch() + return + } + + self.headerNode.navigationButtonContainer.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue) + if self.isSettings { (self.controller?.parent as? TabBarController)?.updateIsTabBarHidden(true, transition: .animated(duration: 0.3, curve: .linear)) @@ -9803,9 +9819,31 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } + private func updateNavigationHeight(width: CGFloat, defaultHeight: CGFloat, insets: UIEdgeInsets, transition: ContainedViewLayoutTransition) -> CGFloat { + var navigationHeight = defaultHeight + if let customNavigationContentNode = self.headerNode.customNavigationContentNode { + var mappedTransition = transition + if customNavigationContentNode.supernode == nil { + mappedTransition = .immediate + } + let contentHeight = customNavigationContentNode.update(width: width, defaultHeight: defaultHeight, insets: insets, transition: mappedTransition) + navigationHeight = contentHeight + } + return navigationHeight + } + func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition, additive: Bool = false) { self.validLayout = (layout, navigationHeight) + self.headerNode.customNavigationContentNode = self.paneContainerNode.currentPane?.node.navigationContentNode + + let isScrollEnabled = !self.isMediaOnly && self.headerNode.customNavigationContentNode == nil + if self.scrollNode.view.isScrollEnabled != isScrollEnabled { + self.scrollNode.view.isScrollEnabled = isScrollEnabled + } + + let navigationHeight = self.updateNavigationHeight(width: layout.size.width, defaultHeight: navigationHeight, insets: UIEdgeInsets(top: 0.0, left: layout.safeInsets.left, bottom: 0.0, right: layout.safeInsets.right), transition: transition) + if self.headerNode.isAvatarExpanded && layout.size.width > layout.size.height { self.headerNode.updateIsAvatarExpanded(false, transition: transition) self.updateNavigationExpansionPresentation(isExpanded: false, animated: true) @@ -10188,6 +10226,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } if let (layout, navigationHeight) = self.validLayout { + let navigationHeight = self.updateNavigationHeight(width: layout.size.width, defaultHeight: navigationHeight, insets: UIEdgeInsets(top: 0.0, left: layout.safeInsets.left, bottom: 0.0, right: layout.safeInsets.right), transition: transition) + if !additive { let sectionInset: CGFloat if layout.size.width >= 375.0 { @@ -10222,7 +10262,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } let navigationBarHeight: CGFloat = !self.isSettings && layout.isModalOverlay ? 56.0 : 44.0 - self.paneContainerNode.update(size: self.paneContainerNode.bounds.size, sideInset: layout.safeInsets.left, bottomInset: bottomInset, deviceMetrics: layout.deviceMetrics, visibleHeight: visibleHeight, expansionFraction: effectiveAreaExpansionFraction, presentationData: self.presentationData, data: self.data, transition: transition) + self.paneContainerNode.update(size: self.paneContainerNode.bounds.size, sideInset: layout.safeInsets.left, bottomInset: bottomInset, deviceMetrics: layout.deviceMetrics, visibleHeight: visibleHeight, expansionFraction: effectiveAreaExpansionFraction, presentationData: self.presentationData, data: self.data, areTabsHidden: self.headerNode.customNavigationContentNode != nil, transition: transition) transition.updateFrame(node: self.headerNode.navigationButtonContainer, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left, y: layout.statusBarHeight ?? 0.0), size: CGSize(width: layout.size.width - layout.safeInsets.left * 2.0, height: navigationBarHeight))) @@ -10249,6 +10289,12 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro switch currentPaneKey { case .files, .music, .links, .members, .savedMessagesChats: rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .search, isForExpandedView: true)) + case .savedMessages: + if let data = self.data, data.hasSavedMessageTags { + rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .searchWithTags, isForExpandedView: true)) + } else { + rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .search, isForExpandedView: true)) + } case .media: rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .more, isForExpandedView: true)) default: diff --git a/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift b/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift index 1e78b16722..8c4cfe769a 100644 --- a/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift +++ b/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift @@ -524,4 +524,14 @@ func updateChatPresentationInterfaceStateImpl( } selfController.updateDownButtonVisibility() + + if case .standard(.embedded) = selfController.presentationInterfaceState.mode, let controllerInteraction = selfController.controllerInteraction, let interfaceInteraction = selfController.interfaceInteraction { + if let titleAccessoryPanelNode = titlePanelForChatPresentationInterfaceState(selfController.presentationInterfaceState, context: selfController.context, currentPanel: selfController.customNavigationPanelNode as? ChatTitleAccessoryPanelNode, controllerInteraction: controllerInteraction, interfaceInteraction: interfaceInteraction) { + selfController.customNavigationPanelNode = titleAccessoryPanelNode as? ChatControllerCustomNavigationPanelNode + } else { + selfController.customNavigationPanelNode = nil + } + } + + selfController.stateUpdated?(transition) } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 1a3f79504e..29da10983f 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -506,6 +506,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G weak var currentWebAppController: ViewController? weak var currentImportMessageTooltip: UndoOverlayController? + + public var customNavigationBarContentNode: NavigationBarContentNode? + public var customNavigationPanelNode: ChatControllerCustomNavigationPanelNode? + public var stateUpdated: ((ContainedViewLayoutTransition) -> Void)? public override var customData: Any? { return self.chatLocation diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 64547e1e83..3bb8354d95 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -1656,13 +1656,15 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { transition.updateFrame(node: backgroundEffectNode, frame: CGRect(origin: CGPoint(), size: layout.size)) } - transition.updateFrame(node: self.backgroundNode, frame: contentBounds) + let wallpaperBounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width - wrappingInsets.left - wrappingInsets.right, height: layout.size.height) + + transition.updateFrame(node: self.backgroundNode, frame: wallpaperBounds) var displayMode: WallpaperDisplayMode = .aspectFill if case .regular = layout.metrics.widthClass, layout.size.height == layout.deviceMetrics.screenSize.width { displayMode = .aspectFit } - self.backgroundNode.updateLayout(size: contentBounds.size, displayMode: displayMode, transition: transition) + self.backgroundNode.updateLayout(size: wallpaperBounds.size, displayMode: displayMode, transition: transition) transition.updateBounds(node: self.historyNodeContainer, bounds: contentBounds) transition.updatePosition(node: self.historyNodeContainer, position: contentBounds.center) @@ -1796,6 +1798,11 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { if self.dismissedAsOverlay { inputBackgroundFrame.origin.y = layout.size.height } + if case .standard(.embedded) = self.chatPresentationInterfaceState.mode { + if self.inputPanelNode == nil { + inputBackgroundFrame.origin.y = layout.size.height + } + } let additionalScrollDistance: CGFloat = 0.0 var scrollToTop = false @@ -2033,11 +2040,6 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { inputPanelUpdateTransition = .immediate } - if case .standard(.embedded) = self.chatPresentationInterfaceState.mode { - self.inputPanelBackgroundNode.isHidden = true - self.inputPanelBackgroundSeparatorNode.isHidden = true - self.inputPanelBottomBackgroundSeparatorNode.isHidden = true - } self.inputPanelBackgroundNode.update(size: CGSize(width: intrinsicInputPanelBackgroundNodeSize.width, height: intrinsicInputPanelBackgroundNodeSize.height + inputPanelBackgroundExtension), transition: inputPanelUpdateTransition, beginWithCurrentState: true) self.inputPanelBottomBackgroundSeparatorBaseOffset = intrinsicInputPanelBackgroundNodeSize.height inputPanelUpdateTransition.updateFrame(node: self.inputPanelBottomBackgroundSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: intrinsicInputPanelBackgroundNodeSize.height + inputPanelBackgroundExtension), size: CGSize(width: intrinsicInputPanelBackgroundNodeSize.width, height: UIScreenPixel)), beginWithCurrentState: true) @@ -2661,13 +2663,18 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } self.searchNavigationNode = ChatSearchNavigationContentNode(context: self.context, theme: self.chatPresentationInterfaceState.theme, strings: self.chatPresentationInterfaceState.strings, chatLocation: self.chatPresentationInterfaceState.chatLocation, interaction: interfaceInteraction, presentationInterfaceState: self.chatPresentationInterfaceState) } - self.navigationBar?.setContentNode(self.searchNavigationNode, animated: transitionIsAnimated) + if let navigationBar = self.navigationBar { + navigationBar.setContentNode(self.searchNavigationNode, animated: transitionIsAnimated) + } else { + self.controller?.customNavigationBarContentNode = self.searchNavigationNode + } self.searchNavigationNode?.update(presentationInterfaceState: self.chatPresentationInterfaceState) if activate { self.searchNavigationNode?.activate() } } else if let _ = self.searchNavigationNode { self.searchNavigationNode = nil + self.controller?.customNavigationBarContentNode = nil self.navigationBar?.setContentNode(nil, animated: transitionIsAnimated) } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift index d961ac3504..dc3a9629e6 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift @@ -10,10 +10,6 @@ import ChatChannelSubscriberInputPanelNode import ChatMessageSelectionInputPanelNode func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentPanel: ChatInputPanelNode?, currentSecondaryPanel: ChatInputPanelNode?, textInputPanelNode: ChatTextInputPanelNode?, interfaceInteraction: ChatPanelInterfaceInteraction?) -> (primary: ChatInputPanelNode?, secondary: ChatInputPanelNode?) { - if case .standard(.embedded) = chatPresentationInterfaceState.mode { - return (nil, nil) - } - if let renderedPeer = chatPresentationInterfaceState.renderedPeer, renderedPeer.peer?.restrictionText(platform: "ios", contentSettings: context.currentContentSettings.with { $0 }) != nil { return (nil, nil) } @@ -65,6 +61,10 @@ func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState } } + if case .standard(.embedded) = chatPresentationInterfaceState.mode { + return (nil, nil) + } + if let selectionState = chatPresentationInterfaceState.interfaceState.selectionState { if let _ = chatPresentationInterfaceState.reportReason { if let currentPanel = (currentPanel as? ChatMessageReportInputPanelNode) ?? (currentSecondaryPanel as? ChatMessageReportInputPanelNode) { diff --git a/submodules/TelegramUI/Sources/ChatSearchTitleAccessoryPanelNode.swift b/submodules/TelegramUI/Sources/ChatSearchTitleAccessoryPanelNode.swift index 688c33a1b5..6951efb955 100644 --- a/submodules/TelegramUI/Sources/ChatSearchTitleAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatSearchTitleAccessoryPanelNode.swift @@ -25,7 +25,7 @@ private let backgroundTagImage: UIImage? = { } }() -final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, UIScrollViewDelegate { +final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, ChatControllerCustomNavigationPanelNode, UIScrollViewDelegate { private struct Params: Equatable { var width: CGFloat var leftInset: CGFloat @@ -551,6 +551,10 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, UISc return LayoutResult(backgroundHeight: panelHeight, insetHeight: panelHeight, hitTestSlop: 0.0) } + func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, chatController: ChatController) -> LayoutResult { + return self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, transition: transition, interfaceState: (chatController as! ChatControllerImpl).presentationInterfaceState) + } + private func update(params: Params, transition: ContainedViewLayoutTransition) { let panelHeight: CGFloat = 39.0 diff --git a/submodules/TelegramUI/Sources/ChatTitleAccessoryPanelNode.swift b/submodules/TelegramUI/Sources/ChatTitleAccessoryPanelNode.swift index f7973f8c49..8fd917a657 100644 --- a/submodules/TelegramUI/Sources/ChatTitleAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTitleAccessoryPanelNode.swift @@ -3,13 +3,10 @@ import UIKit import Display import AsyncDisplayKit import ChatPresentationInterfaceState +import AccountContext class ChatTitleAccessoryPanelNode: ASDisplayNode { - struct LayoutResult { - var backgroundHeight: CGFloat - var insetHeight: CGFloat - var hitTestSlop: CGFloat - } + typealias LayoutResult = ChatControllerCustomNavigationPanelNode.LayoutResult var interfaceInteraction: ChatPanelInterfaceInteraction?