From e43307d945884d532e095e62b48c90fd7f7ca367 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Thu, 22 May 2025 12:29:31 +0800 Subject: [PATCH 1/3] Fix saved message navigation icon --- .../Sources/Resources/PresentationResourcesChat.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift index bb868a76cc..0d7dac3104 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift @@ -1105,7 +1105,7 @@ public struct PresentationResourcesChat { public static func chatFreeNavigateButtonIcon(_ theme: PresentationTheme, wallpaper: TelegramWallpaper) -> UIImage? { return theme.image(PresentationResourceKey.chatFreeNavigateButtonIcon.rawValue, { _ in - return generateTintedImage(image: UIImage(bundleImageName: "Settings/TextArrowRight"), color: bubbleVariableColor(variableColor: theme.chat.message.shareButtonForegroundColor, wallpaper: wallpaper)) + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/NavigateToMessageIcon"), color: bubbleVariableColor(variableColor: theme.chat.message.shareButtonForegroundColor, wallpaper: wallpaper)) }) } From 56532e3ab89c5904d5cd8f028c3e0fdf2d07d97d Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Thu, 22 May 2025 19:39:52 +0800 Subject: [PATCH 2/3] Monoforums --- .../Sources/ChatMessageBubbleItemNode.swift | 2 +- .../Sources/ChatMessageForwardInfoNode.swift | 6 +- .../Sources/ChatMessageItemImpl.swift | 4 +- .../Sources/ChatUnreadItem.swift | 17 +- .../Chat/ChatControllerLoadDisplayNode.swift | 86 ++++--- .../ChatControllerNavigateToMessage.swift | 4 +- ...UpdateChatPresentationInterfaceState.swift | 12 +- .../TelegramUI/Sources/ChatController.swift | 89 +++++-- .../Sources/ChatControllerContentData.swift | 4 + .../Sources/ChatControllerNode.swift | 227 +++++++++++++++--- .../Sources/ChatHistoryListNode.swift | 36 ++- .../ChatInterfaceTitlePanelNodes.swift | 4 - 12 files changed, 374 insertions(+), 117 deletions(-) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index ec521589b0..87780d0847 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -1926,7 +1926,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI inlineBotNameString = attribute.title } } else if let attribute = attribute as? ReplyMessageAttribute { - if case let .replyThread(replyThreadMessage) = item.chatLocation, Int32(clamping: replyThreadMessage.threadId) == attribute.messageId.id { + if let threadId = firstMessage.threadId, Int32(clamping: threadId) == attribute.messageId.id { } else { replyMessage = firstMessage.associatedMessages[attribute.messageId] } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageForwardInfoNode/Sources/ChatMessageForwardInfoNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageForwardInfoNode/Sources/ChatMessageForwardInfoNode.swift index 33121fb337..4ed2ba5005 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageForwardInfoNode/Sources/ChatMessageForwardInfoNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageForwardInfoNode/Sources/ChatMessageForwardInfoNode.swift @@ -531,7 +531,11 @@ public class ChatMessageForwardInfoNode: ASDisplayNode { avatarNode.frame = CGRect(origin: CGPoint(x: leftOffset, y: titleLayout.size.height + titleAuthorSpacing), size: avatarSize) avatarNode.updateSize(size: avatarSize) if let peer { - avatarNode.setPeer(context: context, theme: presentationData.theme.theme, peer: EnginePeer(peer), displayDimensions: avatarSize) + if peer.smallProfileImage != nil { + avatarNode.setPeerV2(context: context, theme: presentationData.theme.theme, peer: EnginePeer(peer), displayDimensions: avatarSize) + } else { + avatarNode.setPeer(context: context, theme: presentationData.theme.theme, peer: EnginePeer(peer), displayDimensions: avatarSize) + } } else if let authorName, !authorName.isEmpty { avatarNode.setCustomLetters([String(authorName[authorName.startIndex])]) } else { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift index 4dc0005076..0a7faa26da 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageItemImpl.swift @@ -322,7 +322,9 @@ public final class ChatMessageItemImpl: ChatMessageItem, CustomStringConvertible if let channel = content.firstMessage.peers[content.firstMessage.id.peerId] as? TelegramChannel, channel.isForumOrMonoForum { if case .replyThread = chatLocation { - displayAuthorInfo = false + if channel.isMonoForum && chatLocation.threadId != context.account.peerId.toInt64() { + displayAuthorInfo = false + } } else { if channel.isMonoForum { if let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = content.firstMessage.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.adminRights != nil { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatUnreadItem.swift b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatUnreadItem.swift index bd516b177b..65c004db87 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatUnreadItem.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatUnreadItem.swift @@ -38,7 +38,7 @@ public class ChatUnreadItem: ListViewItem { Queue.mainQueue().async { completion(node, { return (nil, { _ in - apply() + apply(.None) }) }) } @@ -56,7 +56,7 @@ public class ChatUnreadItem: ListViewItem { let (layout, apply) = nodeLayout(self, params, dateAtBottom) Queue.mainQueue().async { completion(layout, { _ in - apply() + apply(animation) }) } } @@ -122,18 +122,18 @@ public class ChatUnreadItemNode: ListViewItemNode { if let item = item as? ChatUnreadItem { let dateAtBottom = !chatItemsHaveCommonDateHeader(item, nextItem) let (layout, apply) = self.asyncLayout()(item, params, dateAtBottom) - apply() + apply(.None) self.contentSize = layout.contentSize self.insets = layout.insets } } - public func asyncLayout() -> (_ item: ChatUnreadItem, _ params: ListViewItemLayoutParams, _ dateAtBottom: Bool) -> (ListViewItemNodeLayout, () -> Void) { + public func asyncLayout() -> (_ item: ChatUnreadItem, _ params: ListViewItemLayoutParams, _ dateAtBottom: Bool) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation) -> Void) { let labelLayout = TextNode.asyncLayout(self.labelNode) let layoutConstants = self.layoutConstants let currentTheme = self.theme - return { item, params, dateAtBottom in + return { [weak self] item, params, dateAtBottom in var updatedBackgroundImage: UIImage? if currentTheme != item.presentationData.theme { updatedBackgroundImage = PresentationResourcesChat.chatUnreadBarBackgroundImage(item.presentationData.theme.theme) @@ -144,7 +144,7 @@ public class ChatUnreadItemNode: ListViewItemNode { let backgroundSize = CGSize(width: params.width, height: 25.0) - return (ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: 25.0), insets: UIEdgeInsets(top: 6.0 + (dateAtBottom ? layoutConstants.timestampHeaderHeight : 0.0), left: 0.0, bottom: 5.0, right: 0.0)), { [weak self] in + return (ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: 25.0), insets: UIEdgeInsets(top: 6.0 + (dateAtBottom ? layoutConstants.timestampHeaderHeight : 0.0), left: 0.0, bottom: 5.0, right: 0.0)), { animation in if let strongSelf = self { strongSelf.item = item strongSelf.theme = item.presentationData.theme @@ -159,7 +159,10 @@ public class ChatUnreadItemNode: ListViewItemNode { strongSelf.activateArea.accessibilityLabel = string strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: backgroundSize) - strongSelf.labelNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundSize.width - size.size.width) / 2.0), y: floorToScreenPixels((backgroundSize.height - size.size.height) / 2.0)), size: size.size) + + let labelFrame = CGRect(origin: CGPoint(x: params.leftInset + floorToScreenPixels((backgroundSize.width - params.leftInset - params.rightInset - size.size.width) / 2.0), y: floorToScreenPixels((backgroundSize.height - size.size.height) / 2.0)), size: size.size) + animation.animator.updatePosition(layer: strongSelf.labelNode.layer, position: labelFrame.center, completion: nil) + strongSelf.labelNode.bounds = CGRect(origin: CGPoint(), size: labelFrame.size) if item.controllerInteraction.presentationContext.backgroundNode?.hasExtraBubbleBackground() == true { if strongSelf.backgroundContent == nil, let backgroundContent = item.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) { diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift index ff3a2ebd31..81fb4d4912 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift @@ -127,7 +127,7 @@ import PostSuggestionsSettingsScreen import ChatSendStarsScreen extension ChatControllerImpl { - func reloadChatLocation(chatLocation: ChatLocation, chatLocationContextHolder: Atomic, historyNode: ChatHistoryListNodeImpl, isReady: Promise?) { + func reloadChatLocation(chatLocation: ChatLocation, chatLocationContextHolder: Atomic, historyNode: ChatHistoryListNodeImpl, apply: @escaping ((Bool) -> Void) -> Void) { self.contentDataReady.set(false) self.contentDataDisposable?.dispose() @@ -159,29 +159,42 @@ extension ChatControllerImpl { self.contentDataDisposable = (contentData.isReady.get() |> filter { $0 } |> take(1) - |> deliverOnMainQueue).startStrict(next: { [weak self, weak contentData] _ in + |> deliverOnMainQueue).startStrict(next: { [weak self, weak contentData, weak historyNode] _ in guard let self, let contentData, self.pendingContentData?.contentData === contentData else { return } - self.contentData = contentData - self.pendingContentData = nil - self.contentDataUpdated(synchronous: true, previousState: contentData.state) - self.chatThemeEmoticonPromise.set(contentData.chatThemeEmoticonPromise.get()) - self.chatWallpaperPromise.set(contentData.chatWallpaperPromise.get()) - - self.contentDataReady.set(true) - - contentData.onUpdated = { [weak self, weak contentData] previousState in - guard let self, let contentData, self.contentData === contentData else { + apply({ [weak self, weak contentData] forceAnimation in + guard let self, let contentData, self.pendingContentData?.contentData === contentData else { return } - self.contentDataUpdated(synchronous: false, previousState: previousState) - } + + self.contentData = contentData + self.pendingContentData = nil + self.contentDataUpdated(synchronous: true, forceAnimation: forceAnimation, previousState: contentData.state) + + self.chatThemeEmoticonPromise.set(contentData.chatThemeEmoticonPromise.get()) + self.chatWallpaperPromise.set(contentData.chatWallpaperPromise.get()) + + if let historyNode { + self.setupChatHistoryNode(historyNode: historyNode) + + historyNode.contentPositionChanged(historyNode.visibleContentOffset()) + } + + self.contentDataReady.set(true) + + contentData.onUpdated = { [weak self, weak contentData] previousState in + guard let self, let contentData, self.contentData === contentData else { + return + } + self.contentDataUpdated(synchronous: false, forceAnimation: false, previousState: previousState) + } + }) }) } - func contentDataUpdated(synchronous: Bool, previousState: ContentData.State) { + func contentDataUpdated(synchronous: Bool, forceAnimation: Bool, previousState: ContentData.State) { guard let contentData = self.contentData else { return } @@ -293,19 +306,23 @@ extension ChatControllerImpl { if self.presentationInterfaceState.search != nil && contentData.state.hasSearchTags { displayActionsPanel = true } + if displayActionsPanel != didDisplayActionsPanel { animated = true } - if previousState.pinnedMessage != contentData.state.pinnedMessage { animated = true } + if forceAnimation { + animated = true + } self.updateChatPresentationInterfaceState(animated: animated && self.willAppear, interactive: false, { presentationInterfaceState in var presentationInterfaceState = presentationInterfaceState presentationInterfaceState = presentationInterfaceState.updatedPeer({ _ in return contentData.state.renderedPeer }) + presentationInterfaceState = presentationInterfaceState.updatedChatLocation(contentData.chatLocation) presentationInterfaceState = presentationInterfaceState.updatedIsNotAccessible(contentData.state.isNotAccessible) presentationInterfaceState = presentationInterfaceState.updatedContactStatus(contentData.state.contactStatus) presentationInterfaceState = presentationInterfaceState.updatedHasBots(contentData.state.hasBots) @@ -749,7 +766,7 @@ extension ChatControllerImpl { })).startStandalone() } - self.setupChatHistoryNode() + self.setupChatHistoryNode(historyNode: self.chatDisplayNode.historyNode) self.chatDisplayNode.requestLayout = { [weak self] transition in self?.requestLayout(transition: transition) @@ -1206,7 +1223,7 @@ extension ChatControllerImpl { self.scrollToEndOfHistory() } } else { - if let messageId = self.historyNavigationStack.removeLast() { + if let messageId = self.contentData?.historyNavigationStack.removeLast() { self.navigateToMessage(from: nil, to: .id(messageId.id, NavigateToMessageParams(timestamp: nil, quote: nil)), rememberInStack: false) } else { if case .known = self.chatDisplayNode.historyNode.visibleContentOffset() { @@ -4295,7 +4312,7 @@ extension ChatControllerImpl { self.displayNodeDidLoad() } - func setupChatHistoryNode() { + func setupChatHistoryNode(historyNode: ChatHistoryListNodeImpl) { do { let peerId = self.chatLocation.peerId if let subject = self.subject, case .scheduledMessages = subject { @@ -4582,8 +4599,10 @@ extension ChatControllerImpl { } } - self.chatDisplayNode.historyNode.contentPositionChanged = { [weak self] offset in - guard let strongSelf = self else { return } + historyNode.contentPositionChanged = { [weak self, weak historyNode] offset in + guard let strongSelf = self, let historyNode, strongSelf.chatDisplayNode.historyNode === historyNode else { + return + } var minOffsetForNavigation: CGFloat = 40.0 strongSelf.chatDisplayNode.historyNode.enumerateItemNodes { itemNode in @@ -4632,7 +4651,7 @@ extension ChatControllerImpl { strongSelf.chatDisplayNode.updatePlainInputSeparatorAlpha(plainInputSeparatorAlpha, transition: .animated(duration: 0.2, curve: .easeInOut)) } - self.chatDisplayNode.historyNode.scrolledToIndex = { [weak self] toSubject, initial in + historyNode.scrolledToIndex = { [weak self] toSubject, initial in if let strongSelf = self, case let .message(index) = toSubject.index { if case let .message(messageSubject, _, _, _) = strongSelf.subject, initial, case let .id(messageId) = messageSubject, messageId != index.id { if messageId.peerId == index.id.peerId { @@ -4693,16 +4712,16 @@ extension ChatControllerImpl { } } - self.chatDisplayNode.historyNode.scrolledToSomeIndex = { [weak self] in + historyNode.scrolledToSomeIndex = { [weak self] in guard let strongSelf = self else { return } strongSelf.contentData?.scrolledToMessageIdValue = nil } - self.chatDisplayNode.historyNode.maxVisibleMessageIndexUpdated = { [weak self] index in - if let strongSelf = self, !strongSelf.historyNavigationStack.isEmpty { - strongSelf.historyNavigationStack.filterOutIndicesLessThan(index) + historyNode.maxVisibleMessageIndexUpdated = { [weak self] index in + if let strongSelf = self, let contentData = strongSelf.contentData, !contentData.historyNavigationStack.isEmpty { + contentData.historyNavigationStack.filterOutIndicesLessThan(index) } } @@ -4723,7 +4742,7 @@ extension ChatControllerImpl { hasActiveCalls = .single(false) } - let shouldBeActive = combineLatest(self.context.sharedContext.mediaManager.audioSession.isPlaybackActive() |> deliverOnMainQueue, self.chatDisplayNode.historyNode.hasVisiblePlayableItemNodes, hasActiveCalls) + let shouldBeActive = combineLatest(self.context.sharedContext.mediaManager.audioSession.isPlaybackActive() |> deliverOnMainQueue, historyNode.hasVisiblePlayableItemNodes, hasActiveCalls) |> mapToSignal { [weak self] isPlaybackActive, hasVisiblePlayableItemNodes, hasActiveCalls -> Signal in if hasVisiblePlayableItemNodes && !isPlaybackActive && !hasActiveCalls { return Signal { [weak self] subscriber in @@ -4776,7 +4795,7 @@ extension ChatControllerImpl { downPressed: buttonAction ) - self.chatDisplayNode.historyNode.openNextChannelToRead = { [weak self] peer, threadData, location in + historyNode.openNextChannelToRead = { [weak self] peer, threadData, location in guard let strongSelf = self else { return } @@ -4831,7 +4850,7 @@ extension ChatControllerImpl { } } - self.chatDisplayNode.historyNode.beganDragging = { [weak self] in + historyNode.beganDragging = { [weak self] in guard let self else { return } @@ -4846,7 +4865,7 @@ extension ChatControllerImpl { } } - self.chatDisplayNode.historyNode.didScrollWithOffset = { [weak self] offset, transition, itemNode, isTracking in + historyNode.didScrollWithOffset = { [weak self] offset, transition, itemNode, isTracking in guard let strongSelf = self else { return } @@ -4876,23 +4895,22 @@ extension ChatControllerImpl { strongSelf.chatDisplayNode.loadingPlaceholderNode?.addContentOffset(offset: offset, transition: transition) } strongSelf.chatDisplayNode.messageTransitionNode.addExternalOffset(offset: offset, transition: transition, itemNode: itemNode, isRotated: strongSelf.chatDisplayNode.historyNode.rotated) - } - self.chatDisplayNode.historyNode.hasAtLeast3MessagesUpdated = { [weak self] hasAtLeast3Messages in + historyNode.hasAtLeast3MessagesUpdated = { [weak self] hasAtLeast3Messages in if let strongSelf = self { strongSelf.updateChatPresentationInterfaceState(interactive: false, { $0.updatedHasAtLeast3Messages(hasAtLeast3Messages) }) } } - self.chatDisplayNode.historyNode.hasPlentyOfMessagesUpdated = { [weak self] hasPlentyOfMessages in + historyNode.hasPlentyOfMessagesUpdated = { [weak self] hasPlentyOfMessages in if let strongSelf = self { strongSelf.updateChatPresentationInterfaceState(interactive: false, { $0.updatedHasPlentyOfMessages(hasPlentyOfMessages) }) } } if self.didAppear { - self.chatDisplayNode.historyNode.canReadHistory.set(self.computedCanReadHistoryPromise.get()) + historyNode.canReadHistory.set(self.computedCanReadHistoryPromise.get()) } } } diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerNavigateToMessage.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerNavigateToMessage.swift index 832c7fe53f..9b11b9cd17 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerNavigateToMessage.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerNavigateToMessage.swift @@ -317,7 +317,7 @@ extension ChatControllerImpl { }) } else if forceInCurrentChat { if let _ = fromId, let fromIndex = fromIndex, rememberInStack { - self.historyNavigationStack.add(fromIndex) + self.contentData?.historyNavigationStack.add(fromIndex) } let scrollFromIndex: MessageIndex? @@ -505,7 +505,7 @@ extension ChatControllerImpl { return } if let _ = fromId, rememberInStack { - self.historyNavigationStack.add(fromIndex) + self.contentData?.historyNavigationStack.add(fromIndex) } self.loadingMessage.set(.single(statusSubject) |> delay(0.1, queue: .mainQueue())) diff --git a/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift b/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift index 5f4dae9e2a..a8fcd18da8 100644 --- a/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift +++ b/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift @@ -23,8 +23,6 @@ func updateChatPresentationInterfaceStateImpl( _ f: (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState, completion externalCompletion: @escaping (ContainedViewLayoutTransition) -> Void ) { - let previousChatLocation = selfController.chatDisplayNode.historyNode.chatLocation - var completion = externalCompletion var temporaryChatPresentationInterfaceState = f(selfController.presentationInterfaceState) @@ -436,13 +434,13 @@ func updateChatPresentationInterfaceStateImpl( selfController.presentationInterfaceState = updatedChatPresentationInterfaceState - if selfController.chatDisplayNode.chatLocation != selfController.presentationInterfaceState.chatLocation { + /*if selfController.chatDisplayNode.chatLocation != selfController.presentationInterfaceState.chatLocation { let defaultDirection: ChatControllerAnimateInnerChatSwitchDirection? = selfController.chatDisplayNode.chatLocationTabSwitchDirection(from: selfController.chatLocation.threadId, to: selfController.presentationInterfaceState.chatLocation.threadId).flatMap { direction -> ChatControllerAnimateInnerChatSwitchDirection in return direction ? .right : .left } let tabSwitchDirection = selfController.currentChatSwitchDirection ?? defaultDirection selfController.chatDisplayNode.updateChatLocation(chatLocation: selfController.presentationInterfaceState.chatLocation, transition: transition, tabSwitchDirection: tabSwitchDirection) - } + }*/ selfController.updateSlowmodeStatus() @@ -603,12 +601,6 @@ func updateChatPresentationInterfaceStateImpl( } } - if previousChatLocation != selfController.presentationInterfaceState.chatLocation { - selfController.chatLocation = selfController.presentationInterfaceState.chatLocation - selfController.reloadCachedData() - selfController.setupChatHistoryNode() - } - selfController.updateDownButtonVisibility() if selfController.presentationInterfaceState.hasBirthdayToday { diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 8dd7ae9ec8..9ccc51a8d3 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -389,8 +389,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var searchDisposable: MetaDisposable? - var historyNavigationStack = ChatHistoryNavigationStack() - public let canReadHistory = ValuePromise(true, ignoreRepeated: true) public let hasBrowserOrAppInFront = Promise(false) @@ -5230,7 +5228,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) } - self.reloadChatLocation(chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder, historyNode: self.chatDisplayNode.historyNode, isReady: nil) + self.reloadChatLocation(chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder, historyNode: self.chatDisplayNode.historyNode, apply: { $0(false) }) self.botCallbackAlertMessageDisposable = (self.botCallbackAlertMessage.get() |> deliverOnMainQueue).startStrict(next: { [weak self] message in @@ -9711,10 +9709,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var currentChatSwitchDirection: ChatControllerAnimateInnerChatSwitchDirection? public func updateChatLocationThread(threadId: Int64?, animationDirection: ChatControllerAnimateInnerChatSwitchDirection? = nil) { - /*if self.isUpdatingChatLocationThread { + if self.isUpdatingChatLocationThread { return } - guard let peerId = self.chatLocation.peerId else { return } @@ -9725,16 +9722,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } - let navigationSnapshot = self.chatTitleView?.prepareSnapshotState() - - //let historyNode = self.chatDisplayNode.createHistoryNodeForChatLocation(chatLocation: self.chatLocation, chatLocationContextHolder: ) - - let rightBarButtonItemSnapshots: [(UIView, CGRect)] = (self.navigationItem.rightBarButtonItems ?? []).compactMap { item -> (UIView, CGRect)? in - guard let view = item.customDisplayNode?.view, let snapshotView = view.snapshotView(afterScreenUpdates: false) else { - return nil - } - return (snapshotView, view.convert(view.bounds, to: self.view)) - } + self.saveInterfaceState() let updatedChatLocation: ChatLocation if let threadId { @@ -9762,6 +9750,77 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G updatedChatLocation = .peer(id: peerId) } + let navigationSnapshot = self.chatTitleView?.prepareSnapshotState() + + let chatLocationContextHolder = Atomic(value: nil) + let historyNode = self.chatDisplayNode.createHistoryNodeForChatLocation(chatLocation: updatedChatLocation, chatLocationContextHolder: chatLocationContextHolder) + self.isUpdatingChatLocationThread = true + self.reloadChatLocation(chatLocation: updatedChatLocation, chatLocationContextHolder: chatLocationContextHolder, historyNode: historyNode, apply: { [weak self, weak historyNode] apply in + guard let self, let historyNode else { + return + } + + self.currentChatSwitchDirection = animationDirection + self.chatLocation = updatedChatLocation + self.chatDisplayNode.prepareSwitchToChatLocation(historyNode: historyNode, animationDirection: animationDirection) + + apply(true) + + if let navigationSnapshot, let animationDirection { + let mappedAnimationDirection: ChatTitleView.AnimateFromSnapshotDirection + switch animationDirection { + case .up: + mappedAnimationDirection = .up + case .down: + mappedAnimationDirection = .down + case .left: + mappedAnimationDirection = .left + case .right: + mappedAnimationDirection = .right + } + + self.chatTitleView?.animateFromSnapshot(navigationSnapshot, direction: mappedAnimationDirection) + /*if let rightBarButtonItems = self.navigationItem.rightBarButtonItems { + for i in 0 ..< rightBarButtonItems.count { + let item = rightBarButtonItems[i] + if let customDisplayNode = item.customDisplayNode { + customDisplayNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + //customDisplayNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring) + + let _ = rightBarButtonItemSnapshots + /*if i < rightBarButtonItemSnapshots.count { + let (snapshotItem, snapshotFrame) = rightBarButtonItemSnapshots[i] + if let targetSuperview = customDisplayNode.view.superview { + snapshotItem.frame = targetSuperview.convert(snapshotFrame, from: self.view) + targetSuperview.addSubview(snapshotItem) + + snapshotItem.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.18, completion: { [weak snapshotItem] _ in + snapshotItem?.removeFromSuperview() + }) + snapshotItem.layer.animateScale(from: 1.0, to: 0.1, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring) + } + }*/ + } + } + }*/ + } + + self.currentChatSwitchDirection = nil + + self.isUpdatingChatLocationThread = false + }) + + /* + + // + + let rightBarButtonItemSnapshots: [(UIView, CGRect)] = (self.navigationItem.rightBarButtonItems ?? []).compactMap { item -> (UIView, CGRect)? in + guard let view = item.customDisplayNode?.view, let snapshotView = view.snapshotView(afterScreenUpdates: false) else { + return nil + } + return (snapshotView, view.convert(view.bounds, to: self.view)) + } + let isReady = Promise() let chatLocationContextHolder = Atomic(value: nil) self.reloadChatLocation(chatLocation: updatedChatLocation, chatLocationContextHolder: chatLocationContextHolder, historyNode: historyNode, isReady: isReady) diff --git a/submodules/TelegramUI/Sources/ChatControllerContentData.swift b/submodules/TelegramUI/Sources/ChatControllerContentData.swift index 48046d104a..c69a8d4719 100644 --- a/submodules/TelegramUI/Sources/ChatControllerContentData.swift +++ b/submodules/TelegramUI/Sources/ChatControllerContentData.swift @@ -144,6 +144,7 @@ extension ChatControllerImpl { private let isChatLocationInfoReady = ValuePromise(false, ignoreRepeated: true) private let isCachedDataReady = ValuePromise(false, ignoreRepeated: true) + let chatLocation: ChatLocation let chatLocationInfoData: ChatLocationInfoData private(set) var state: State = State() @@ -172,6 +173,8 @@ extension ChatControllerImpl { } } + var historyNavigationStack = ChatHistoryNavigationStack() + let chatThemeEmoticonPromise = Promise() let chatWallpaperPromise = Promise() @@ -191,6 +194,7 @@ extension ChatControllerImpl { presentationData: PresentationData, historyNode: ChatHistoryListNodeImpl ) { + self.chatLocation = chatLocation self.presentationData = presentationData let strings = self.presentationData.strings diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 06f9eaef63..6a21248cb3 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -133,6 +133,19 @@ class HistoryNodeContainer: ASDisplayNode { } } +private final class PendingSwitchToChatLocation { + let historyNode: ChatHistoryListNodeImpl + let animationDirection: ChatControllerAnimateInnerChatSwitchDirection? + + init( + historyNode: ChatHistoryListNodeImpl, + animationDirection: ChatControllerAnimateInnerChatSwitchDirection? + ) { + self.historyNode = historyNode + self.animationDirection = animationDirection + } +} + class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { let context: AccountContext private(set) var chatLocation: ChatLocation @@ -162,7 +175,10 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { var historyNode: ChatHistoryListNodeImpl var blurredHistoryNode: ASImageNode? let historyNodeContainer: HistoryNodeContainer - let loadingNode: ChatLoadingNode + private(set) var loadingNode: ChatLoadingNode + + private var isLoadingValue: Bool = false + private var isLoadingEarlier: Bool = false private(set) var loadingPlaceholderNode: ChatLoadingPlaceholderNode? var alwaysShowSearchResultsAsList: Bool = false @@ -316,8 +332,8 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { let adMessagesContext: AdMessagesHistoryContext? - private var isLoadingValue: Bool = false - private var isLoadingEarlier: Bool = false + private var pendingSwitchToChatLocation: PendingSwitchToChatLocation? + private func updateIsLoading(isLoading: Bool, earlier: Bool, animated: Bool) { var useLoadingPlaceholder = self.chatLocation.peerId?.namespace != Namespaces.Peer.CloudUser && self.chatLocation.peerId?.namespace != Namespaces.Peer.SecretChat if case let .replyThread(message) = self.chatLocation, message.peerId == self.context.account.peerId { @@ -351,10 +367,11 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { loadingPlaceholderNode.setup(self.historyNode, updating: false) - if let (layout, navigationHeight) = self.validLayout { - self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: .immediate, listViewTransaction: { _, _, _, _ in - }, updateExtraNavigationBarBackgroundHeight: { _, _, _, _ in - }) + let contentBounds = self.loadingNode.frame + loadingPlaceholderNode.frame = contentBounds + if let loadingPlaceholderNode = self.loadingPlaceholderNode, let validLayout = self.validLayout { + loadingPlaceholderNode.updateLayout(size: contentBounds.size, insets: self.visibleAreaInset, metrics: validLayout.0.metrics, transition: .immediate) + loadingPlaceholderNode.update(rect: contentBounds, within: contentBounds.size, transition: .immediate) } } loadingPlaceholderNode.alpha = 1.0 @@ -371,12 +388,17 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { } else { if useLoadingPlaceholder { if let loadingPlaceholderNode = self.loadingPlaceholderNode { - loadingPlaceholderNode.animateOut(self.historyNode, completion: { [weak self] in - if let strongSelf = self { - strongSelf.loadingPlaceholderNode?.removeFromSupernode() - strongSelf.loadingPlaceholderNode = nil - } - }) + if animated { + loadingPlaceholderNode.animateOut(self.historyNode, completion: { [weak self] in + if let strongSelf = self { + strongSelf.loadingPlaceholderNode?.removeFromSupernode() + strongSelf.loadingPlaceholderNode = nil + } + }) + } else { + self.loadingPlaceholderNode = nil + loadingPlaceholderNode.removeFromSupernode() + } } } else { self.loadingNode.alpha = 0.0 @@ -1803,18 +1825,6 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { wrappingInsets.top += statusBarHeight } } - - var isSelectionEnabled = true - if previewing { - isSelectionEnabled = false - } else if case .pinnedMessages = self.chatPresentationInterfaceState.subject { - isSelectionEnabled = false - } else if self.chatPresentationInterfaceState.inputTextPanelState.mediaRecordingState != nil { - isSelectionEnabled = false - } else if case .customChatContents = self.chatLocation { - isSelectionEnabled = false - } - self.historyNode.isSelectionGestureEnabled = isSelectionEnabled transition.updateFrame(node: self.titleAccessoryPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: 200.0))) self.titleAccessoryPanelContainer.hitTestExcludeInsets = UIEdgeInsets(top: 0.0, left: leftPanelSize?.width ?? 0.0, bottom: 0.0, right: 0.0) @@ -1908,13 +1918,154 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { transition.updateBounds(node: self.historyNodeContainer, bounds: contentBounds) transition.updatePosition(node: self.historyNodeContainer, position: contentBounds.center) - transition.updateBounds(node: self.historyNode, bounds: CGRect(origin: CGPoint(), size: contentBounds.size)) - transition.updatePosition(node: self.historyNode, position: CGPoint(x: contentBounds.size.width / 2.0, y: contentBounds.size.height / 2.0)) + if let pendingSwitchToChatLocation = self.pendingSwitchToChatLocation { + self.pendingSwitchToChatLocation = nil + + let previousHistoryNode = self.historyNode + self.historyNode = pendingSwitchToChatLocation.historyNode + + self.historyNode.position = previousHistoryNode.position + self.historyNode.bounds = previousHistoryNode.bounds + self.historyNode.transform = previousHistoryNode.transform + + self.historyNode.messageTransitionNode = { [weak self] in + guard let self else { + return nil + } + return self.messageTransitionNode + } + + transition.updateBounds(node: self.historyNode, bounds: CGRect(origin: CGPoint(), size: contentBounds.size)) + transition.updatePosition(node: self.historyNode, position: CGPoint(x: contentBounds.size.width / 2.0, y: contentBounds.size.height / 2.0)) + + previousHistoryNode.supernode?.insertSubnode(self.historyNode, aboveSubnode: previousHistoryNode) + + let messageTransitionNode = ChatMessageTransitionNodeImpl(listNode: self.historyNode, getContentAreaInScreenSpace: { [weak self] in + guard let self else { + return CGRect() + } + return self.view.convert(self.frameForVisibleArea(), to: nil) + }, onTransitionEvent: { [weak self] transition in + guard let self else { + return + } + if (self.context.sharedContext.currentPresentationData.with({ $0 })).reduceMotion { + return + } + if self.context.sharedContext.energyUsageSettings.fullTranslucency { + self.backgroundNode.animateEvent(transition: transition, extendAnimation: false) + } + }) + + let previousMessageTransitionNode = self.messageTransitionNode + self.messageTransitionNode = messageTransitionNode + + messageTransitionNode.position = previousMessageTransitionNode.position + messageTransitionNode.bounds = previousMessageTransitionNode.bounds + messageTransitionNode.transform = previousMessageTransitionNode.transform + + self.wrappingNode.contentNode.insertSubnode(self.messageTransitionNode, aboveSubnode: previousMessageTransitionNode) + + self.emptyType = nil + self.isLoadingValue = false + self.isLoadingEarlier = false + + let previousLoadingNode = self.loadingNode + self.loadingNode = ChatLoadingNode(context: self.context, theme: self.chatPresentationInterfaceState.theme, chatWallpaper: self.chatPresentationInterfaceState.chatWallpaper, bubbleCorners: self.chatPresentationInterfaceState.bubbleCorners) + self.loadingNode.frame = previousLoadingNode.frame + self.loadingNode.isHidden = previousLoadingNode.isHidden + self.loadingNode.alpha = previousLoadingNode.alpha + previousLoadingNode.supernode?.insertSubnode(self.loadingNode, aboveSubnode: previousLoadingNode) + + let previousLoadingPlaceholderNode = self.loadingPlaceholderNode + self.loadingPlaceholderNode = nil + + let previousEmptyNode = self.emptyNode + self.emptyNode = nil + + self.setupHistoryNode() + self.historyNode.loadStateUpdated?(self.historyNode.loadState ?? .messages, false) + + if let animationDirection = pendingSwitchToChatLocation.animationDirection { + var offsetMultiplier = CGPoint() + switch animationDirection { + case .up: + offsetMultiplier.y = -1.0 + case .down: + offsetMultiplier.y = 1.0 + case .left: + offsetMultiplier.x = -1.0 + case .right: + offsetMultiplier.x = 1.0 + } + + previousHistoryNode.clipsToBounds = true + self.historyNode.clipsToBounds = true + + transition.animatePosition(layer: self.historyNode.layer, from: CGPoint(x: offsetMultiplier.x * layout.size.width, y: offsetMultiplier.y * layout.size.height), to: CGPoint(), removeOnCompletion: true, additive: true, completion: { [weak self] _ in + guard let self else { + return + } + self.historyNode.clipsToBounds = false + }) + transition.animatePosition(layer: previousHistoryNode.layer, from: CGPoint(), to: CGPoint(x: -offsetMultiplier.x * layout.size.width, y: -offsetMultiplier.y * layout.size.height), removeOnCompletion: false, additive: true, completion: { [weak previousHistoryNode] _ in + previousHistoryNode?.removeFromSupernode() + }) + + transition.animatePosition(layer: self.messageTransitionNode.layer, from: CGPoint(x: offsetMultiplier.x * layout.size.width, y: offsetMultiplier.y * layout.size.height), to: CGPoint(), removeOnCompletion: true, additive: true) + transition.animatePosition(layer: previousMessageTransitionNode.layer, from: CGPoint(), to: CGPoint(x: -offsetMultiplier.x * layout.size.width, y: -offsetMultiplier.y * layout.size.height), removeOnCompletion: false, additive: true, completion: { [weak previousMessageTransitionNode] _ in + previousMessageTransitionNode?.removeFromSupernode() + }) + + transition.animatePosition(layer: self.loadingNode.layer, from: CGPoint(x: offsetMultiplier.x * layout.size.width, y: offsetMultiplier.y * layout.size.height), to: CGPoint(), removeOnCompletion: true, additive: true) + transition.animatePosition(layer: previousLoadingNode.layer, from: CGPoint(), to: CGPoint(x: -offsetMultiplier.x * layout.size.width, y: -offsetMultiplier.y * layout.size.height), removeOnCompletion: false, additive: true, completion: { [weak previousLoadingNode] _ in + previousLoadingNode?.removeFromSupernode() + }) + + if let loadingPlaceholderNode = self.loadingPlaceholderNode { + transition.animatePosition(layer: loadingPlaceholderNode.layer, from: CGPoint(x: offsetMultiplier.x * layout.size.width, y: offsetMultiplier.y * layout.size.height), to: CGPoint(), removeOnCompletion: true, additive: true) + } + if let previousLoadingPlaceholderNode { + transition.animatePosition(layer: previousLoadingPlaceholderNode.layer, from: CGPoint(), to: CGPoint(x: -offsetMultiplier.x * layout.size.width, y: -offsetMultiplier.y * layout.size.height), removeOnCompletion: false, additive: true, completion: { [weak previousLoadingPlaceholderNode] _ in + previousLoadingPlaceholderNode?.removeFromSupernode() + }) + } + + if let emptyNode = self.emptyNode { + transition.animatePosition(layer: emptyNode.layer, from: CGPoint(x: offsetMultiplier.x * layout.size.width, y: offsetMultiplier.y * layout.size.height), to: CGPoint(), removeOnCompletion: true, additive: true) + } + if let previousEmptyNode { + transition.animatePosition(layer: previousEmptyNode.layer, from: CGPoint(), to: CGPoint(x: -offsetMultiplier.x * layout.size.width, y: -offsetMultiplier.y * layout.size.height), removeOnCompletion: false, additive: true, completion: { [weak previousEmptyNode] _ in + previousEmptyNode?.removeFromSupernode() + }) + } + } else { + previousHistoryNode.removeFromSupernode() + previousMessageTransitionNode.removeFromSupernode() + previousLoadingNode.removeFromSupernode() + previousLoadingPlaceholderNode?.removeFromSupernode() + previousEmptyNode?.removeFromSupernode() + } + } else { + transition.updateBounds(node: self.historyNode, bounds: CGRect(origin: CGPoint(), size: contentBounds.size)) + transition.updatePosition(node: self.historyNode, position: CGPoint(x: contentBounds.size.width / 2.0, y: contentBounds.size.height / 2.0)) + } + if let blurredHistoryNode = self.blurredHistoryNode { transition.updateFrame(node: blurredHistoryNode, frame: contentBounds) } - - //transition.updateFrame(node: self.historyScrollingArea, frame: contentBounds) + + var isSelectionEnabled = true + if previewing { + isSelectionEnabled = false + } else if case .pinnedMessages = self.chatPresentationInterfaceState.subject { + isSelectionEnabled = false + } else if self.chatPresentationInterfaceState.inputTextPanelState.mediaRecordingState != nil { + isSelectionEnabled = false + } else if case .customChatContents = self.chatLocation { + isSelectionEnabled = false + } + self.historyNode.isSelectionGestureEnabled = isSelectionEnabled transition.updateFrame(node: self.loadingNode, frame: contentBounds) if let loadingPlaceholderNode = self.loadingPlaceholderNode { @@ -5078,9 +5229,27 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { return nil } ) + + historyNode.position = self.historyNode.position + historyNode.bounds = self.historyNode.bounds + historyNode.transform = self.historyNode.transform + + if let currentListViewLayout = self.currentListViewLayout { + let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: currentListViewLayout.size, insets: currentListViewLayout.insets, scrollIndicatorInsets: currentListViewLayout.scrollIndicatorInsets, duration: 0.0, curve: .Default(duration: nil), ensureTopInsetForOverlayHighlightedItems: nil, customAnimationTransition: nil) + historyNode.updateLayout(transition: .immediate, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: 0.0, scrollToTop: false, completion: {}) + } + return historyNode } + func prepareSwitchToChatLocation(historyNode: ChatHistoryListNodeImpl, animationDirection: ChatControllerAnimateInnerChatSwitchDirection?) { + self.chatLocation = historyNode.chatLocation + self.pendingSwitchToChatLocation = PendingSwitchToChatLocation( + historyNode: historyNode, + animationDirection: animationDirection + ) + } + func updateChatLocation(chatLocation: ChatLocation, transition: ContainedViewLayoutTransition, tabSwitchDirection: ChatControllerAnimateInnerChatSwitchDirection?) { if chatLocation == self.chatLocation { return diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index e7928e43d4..57c3f0e91e 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -667,7 +667,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto public var contentPositionChanged: (ListViewVisibleContentOffset) -> Void = { _ in } public private(set) var loadState: ChatHistoryNodeLoadState? - private var loadStateUpdated: ((ChatHistoryNodeLoadState, Bool) -> Void)? + public private(set) var loadStateUpdated: ((ChatHistoryNodeLoadState, Bool) -> Void)? private var additionalLoadStateUpdated: [(ChatHistoryNodeLoadState, Bool) -> Void] = [] public private(set) var hasAtLeast3Messages: Bool = false @@ -1830,11 +1830,6 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto let initialData: ChatHistoryCombinedInitialData? switch update.0 { case let .Loading(combinedInitialData, type): - if case .Generic(.FillHole) = type { - applyHole() - return - } - initialData = combinedInitialData if resetScrolling, let previousViewValue = previousView.with({ $0 })?.0 { @@ -1891,13 +1886,6 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto Queue.mainQueue().async { if let strongSelf = self { - if !strongSelf.didSetInitialData { - strongSelf.didSetInitialData = true - var combinedInitialData = combinedInitialData - combinedInitialData?.cachedData = nil - strongSelf._initialData.set(.single(combinedInitialData)) - } - let cachedData = initialData?.cachedData let cachedDataMessages = initialData?.cachedDataMessages @@ -1917,8 +1905,30 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto strongSelf.currentHistoryState = historyState strongSelf.historyState.set(historyState) } + + if !strongSelf.didSetInitialData { + strongSelf.didSetInitialData = true + var combinedInitialData = combinedInitialData + combinedInitialData?.cachedData = nil + strongSelf._initialData.set(.single(combinedInitialData)) + } + + strongSelf._isReady.set(true) + if !strongSelf.didSetReady { + strongSelf.didSetReady = true + #if DEBUG + let deltaTime = (CFAbsoluteTimeGetCurrent() - strongSelf.initTimestamp) * 1000.0 + print("Chat init to dequeue time: \(deltaTime) ms") + #endif + } } } + + if case .Generic(.FillHole) = type { + applyHole() + return + } + return case let .HistoryView(view, type, scrollPosition, flashIndicators, originalScrollPosition, data, id): if case .Generic(.FillHole) = type { diff --git a/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift b/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift index f4ada44683..47249ca94e 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift @@ -230,10 +230,6 @@ func titlePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceStat } func titleTopicsPanelForChatPresentationInterfaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentPanel: ChatTitleAccessoryPanelNode?, controllerInteraction: ChatControllerInteraction?, interfaceInteraction: ChatPanelInterfaceInteraction?, force: Bool) -> ChatTopicListTitleAccessoryPanelNode? { - //TODO:release - if "".isEmpty { - return nil - } if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum, let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = chatPresentationInterfaceState.renderedPeer?.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.adminRights != nil, chatPresentationInterfaceState.search == nil { let topicListDisplayMode = chatPresentationInterfaceState.topicListDisplayMode ?? .top if case .top = topicListDisplayMode, let peerId = chatPresentationInterfaceState.chatLocation.peerId { From c2052b559aee203d20788aa4526fd0e8d8735520 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Thu, 22 May 2025 21:15:58 +0800 Subject: [PATCH 3/3] Monoforums --- .../Sources/NotificationService.swift | 2 +- .../ChatTitleView/Sources/ChatTitleView.swift | 2 +- .../Sources/ChatControllerNode.swift | 27 ++++++++++--------- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/Telegram/NotificationService/Sources/NotificationService.swift b/Telegram/NotificationService/Sources/NotificationService.swift index 49eccbd317..8c92b1ef94 100644 --- a/Telegram/NotificationService/Sources/NotificationService.swift +++ b/Telegram/NotificationService/Sources/NotificationService.swift @@ -1763,7 +1763,7 @@ private final class NotificationServiceHandler { |> mapToSignal { content, _ -> Signal<(NotificationContent, Media?), NoError> in return stateManager.postbox.transaction { transaction -> (NotificationContent, Media?) in var parsedMedia: Media? - if let messageId, let message = transaction.getMessage(messageId), !message.containsSecretMedia { + if let messageId, let message = transaction.getMessage(messageId), !message.containsSecretMedia, !message.attributes.contains(where: { $0 is MediaSpoilerMessageAttribute }) { if let media = message.media.first { parsedMedia = media } diff --git a/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift b/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift index f7a7d09dcd..ed7c2cf28b 100644 --- a/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift +++ b/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift @@ -1148,7 +1148,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { self.superview?.insertSubview(snapshotState.snapshotView, belowSubview: self) let snapshotView = snapshotState.snapshotView - snapshotState.snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.12, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotState.snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.14, removeOnCompletion: false, completion: { [weak snapshotView] _ in snapshotView?.removeFromSuperview() }) snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: -offset.x, y: -offset.y), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true) diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 6a21248cb3..d9565af13f 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -1317,8 +1317,6 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { } self.containerLayoutAndNavigationBarHeight = (layout, navigationBarHeight) - var extraTransition = transition - var dismissedTitleTopicsAccessoryPanelNode: ChatTopicListTitleAccessoryPanelNode? var immediatelyLayoutTitleTopicsAccessoryPanelNodeAndAnimateAppearance = false var titleTopicsAccessoryPanelHeight: CGFloat? @@ -1379,9 +1377,6 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { self.titleAccessoryPanelContainer.addSubnode(titleAccessoryPanelNode) titleAccessoryPanelNode.clipsToBounds = true - if transition.isAnimated { - extraTransition = .animated(duration: 0.2, curve: .easeInOut) - } } let layoutResult = titleAccessoryPanelNode.updateLayout(width: layout.size.width, leftInset: leftPanelSize?.width ?? layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState) @@ -1389,9 +1384,11 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { titleAccessoryPanelBackgroundHeight = layoutResult.backgroundHeight titleAccessoryPanelHitTestSlop = layoutResult.hitTestSlop if immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance { - titleAccessoryPanelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + if transition.isAnimated { + titleAccessoryPanelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } titleAccessoryPanelNode.subnodeTransform = CATransform3DMakeTranslation(0.0, -layoutResult.backgroundHeight, 0.0) - extraTransition.updateSublayerTransformOffset(layer: titleAccessoryPanelNode.layer, offset: CGPoint()) + transition.updateSublayerTransformOffset(layer: titleAccessoryPanelNode.layer, offset: CGPoint()) } } else if let titleAccessoryPanelNode = self.titleAccessoryPanelNode { dismissedTitleAccessoryPanelNode = titleAccessoryPanelNode @@ -1434,9 +1431,6 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { self.titleAccessoryPanelContainer.addSubnode(translationPanelNode) translationPanelNode.clipsToBounds = true - if transition.isAnimated { - extraTransition = .animated(duration: 0.2, curve: .easeInOut) - } } let height = translationPanelNode.updateLayout(width: layout.size.width, leftInset: leftPanelSize?.width ?? layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState) @@ -1444,7 +1438,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { if immediatelyLayoutTranslationPanelNodeAndAnimateAppearance { translationPanelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) translationPanelNode.subnodeTransform = CATransform3DMakeTranslation(0.0, -height, 0.0) - extraTransition.updateSublayerTransformOffset(layer: translationPanelNode.layer, offset: CGPoint()) + transition.updateSublayerTransformOffset(layer: translationPanelNode.layer, offset: CGPoint()) } } else if let chatTranslationPanel = self.chatTranslationPanel { dismissedTranslationPanelNode = chatTranslationPanel @@ -1849,6 +1843,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { } var titleAccessoryPanelFrame: CGRect? + let titleAccessoryPanelBaseY = titlePanelsContentOffset if let _ = self.titleAccessoryPanelNode, let panelHeight = titleAccessoryPanelHeight { titleAccessoryPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: titlePanelsContentOffset), size: CGSize(width: layout.size.width, height: panelHeight)) insets.top += panelHeight @@ -1891,7 +1886,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { extraNavigationBarLeftCutout = CGSize(width: 0.0, height: navigationBarHeight) } - updateExtraNavigationBarBackgroundHeight(extraNavigationBarHeight, extraNavigationBarHitTestSlop, extraNavigationBarLeftCutout, extraTransition) + updateExtraNavigationBarBackgroundHeight(extraNavigationBarHeight, extraNavigationBarHitTestSlop, extraNavigationBarLeftCutout, transition) let contentBounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width - wrappingInsets.left - wrappingInsets.right, height: layout.size.height - wrappingInsets.top - wrappingInsets.bottom) @@ -2703,7 +2698,13 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { if let dismissedTitleAccessoryPanelNode { var dismissedPanelFrame = dismissedTitleAccessoryPanelNode.frame - dismissedPanelFrame.origin.y = -dismissedPanelFrame.size.height + transition.updateSublayerTransformOffset(layer: dismissedTitleAccessoryPanelNode.layer, offset: CGPoint(x: 0.0, y: -dismissedPanelFrame.height)) + dismissedPanelFrame.origin.y = titleAccessoryPanelBaseY + dismissedTitleAccessoryPanelNode.clipsToBounds = true + dismissedPanelFrame.size.height = 0.0 + if transition.isAnimated { + dismissedTitleAccessoryPanelNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) + } transition.updateFrame(node: dismissedTitleAccessoryPanelNode, frame: dismissedPanelFrame, completion: { [weak dismissedTitleAccessoryPanelNode] _ in dismissedTitleAccessoryPanelNode?.removeFromSupernode() })