Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2025-05-22 15:23:34 +02:00
commit e7772bb581
15 changed files with 391 additions and 133 deletions

View File

@ -1763,7 +1763,7 @@ private final class NotificationServiceHandler {
|> mapToSignal { content, _ -> Signal<(NotificationContent, Media?), NoError> in |> mapToSignal { content, _ -> Signal<(NotificationContent, Media?), NoError> in
return stateManager.postbox.transaction { transaction -> (NotificationContent, Media?) in return stateManager.postbox.transaction { transaction -> (NotificationContent, Media?) in
var parsedMedia: Media? 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 { if let media = message.media.first {
parsedMedia = media parsedMedia = media
} }

View File

@ -1105,7 +1105,7 @@ public struct PresentationResourcesChat {
public static func chatFreeNavigateButtonIcon(_ theme: PresentationTheme, wallpaper: TelegramWallpaper) -> UIImage? { public static func chatFreeNavigateButtonIcon(_ theme: PresentationTheme, wallpaper: TelegramWallpaper) -> UIImage? {
return theme.image(PresentationResourceKey.chatFreeNavigateButtonIcon.rawValue, { _ in 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))
}) })
} }

View File

@ -1926,7 +1926,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
inlineBotNameString = attribute.title inlineBotNameString = attribute.title
} }
} else if let attribute = attribute as? ReplyMessageAttribute { } 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 { } else {
replyMessage = firstMessage.associatedMessages[attribute.messageId] replyMessage = firstMessage.associatedMessages[attribute.messageId]
} }

View File

@ -531,7 +531,11 @@ public class ChatMessageForwardInfoNode: ASDisplayNode {
avatarNode.frame = CGRect(origin: CGPoint(x: leftOffset, y: titleLayout.size.height + titleAuthorSpacing), size: avatarSize) avatarNode.frame = CGRect(origin: CGPoint(x: leftOffset, y: titleLayout.size.height + titleAuthorSpacing), size: avatarSize)
avatarNode.updateSize(size: avatarSize) avatarNode.updateSize(size: avatarSize)
if let peer { 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 { } else if let authorName, !authorName.isEmpty {
avatarNode.setCustomLetters([String(authorName[authorName.startIndex])]) avatarNode.setCustomLetters([String(authorName[authorName.startIndex])])
} else { } else {

View File

@ -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 let channel = content.firstMessage.peers[content.firstMessage.id.peerId] as? TelegramChannel, channel.isForumOrMonoForum {
if case .replyThread = chatLocation { if case .replyThread = chatLocation {
displayAuthorInfo = false if channel.isMonoForum && chatLocation.threadId != context.account.peerId.toInt64() {
displayAuthorInfo = false
}
} else { } else {
if channel.isMonoForum { if channel.isMonoForum {
if let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = content.firstMessage.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.adminRights != nil { if let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = content.firstMessage.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.adminRights != nil {

View File

@ -38,7 +38,7 @@ public class ChatUnreadItem: ListViewItem {
Queue.mainQueue().async { Queue.mainQueue().async {
completion(node, { completion(node, {
return (nil, { _ in return (nil, { _ in
apply() apply(.None)
}) })
}) })
} }
@ -56,7 +56,7 @@ public class ChatUnreadItem: ListViewItem {
let (layout, apply) = nodeLayout(self, params, dateAtBottom) let (layout, apply) = nodeLayout(self, params, dateAtBottom)
Queue.mainQueue().async { Queue.mainQueue().async {
completion(layout, { _ in completion(layout, { _ in
apply() apply(animation)
}) })
} }
} }
@ -122,18 +122,18 @@ public class ChatUnreadItemNode: ListViewItemNode {
if let item = item as? ChatUnreadItem { if let item = item as? ChatUnreadItem {
let dateAtBottom = !chatItemsHaveCommonDateHeader(item, nextItem) let dateAtBottom = !chatItemsHaveCommonDateHeader(item, nextItem)
let (layout, apply) = self.asyncLayout()(item, params, dateAtBottom) let (layout, apply) = self.asyncLayout()(item, params, dateAtBottom)
apply() apply(.None)
self.contentSize = layout.contentSize self.contentSize = layout.contentSize
self.insets = layout.insets 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 labelLayout = TextNode.asyncLayout(self.labelNode)
let layoutConstants = self.layoutConstants let layoutConstants = self.layoutConstants
let currentTheme = self.theme let currentTheme = self.theme
return { item, params, dateAtBottom in return { [weak self] item, params, dateAtBottom in
var updatedBackgroundImage: UIImage? var updatedBackgroundImage: UIImage?
if currentTheme != item.presentationData.theme { if currentTheme != item.presentationData.theme {
updatedBackgroundImage = PresentationResourcesChat.chatUnreadBarBackgroundImage(item.presentationData.theme.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) 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 { if let strongSelf = self {
strongSelf.item = item strongSelf.item = item
strongSelf.theme = item.presentationData.theme strongSelf.theme = item.presentationData.theme
@ -159,7 +159,10 @@ public class ChatUnreadItemNode: ListViewItemNode {
strongSelf.activateArea.accessibilityLabel = string strongSelf.activateArea.accessibilityLabel = string
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: backgroundSize) 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 item.controllerInteraction.presentationContext.backgroundNode?.hasExtraBubbleBackground() == true {
if strongSelf.backgroundContent == nil, let backgroundContent = item.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) { if strongSelf.backgroundContent == nil, let backgroundContent = item.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) {

View File

@ -1148,7 +1148,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
self.superview?.insertSubview(snapshotState.snapshotView, belowSubview: self) self.superview?.insertSubview(snapshotState.snapshotView, belowSubview: self)
let snapshotView = snapshotState.snapshotView 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?.removeFromSuperview()
}) })
snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: -offset.x, y: -offset.y), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true) snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: -offset.x, y: -offset.y), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)

View File

@ -127,7 +127,7 @@ import PostSuggestionsSettingsScreen
import ChatSendStarsScreen import ChatSendStarsScreen
extension ChatControllerImpl { extension ChatControllerImpl {
func reloadChatLocation(chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, historyNode: ChatHistoryListNodeImpl, isReady: Promise<Bool>?) { func reloadChatLocation(chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, historyNode: ChatHistoryListNodeImpl, apply: @escaping ((Bool) -> Void) -> Void) {
self.contentDataReady.set(false) self.contentDataReady.set(false)
self.contentDataDisposable?.dispose() self.contentDataDisposable?.dispose()
@ -159,29 +159,42 @@ extension ChatControllerImpl {
self.contentDataDisposable = (contentData.isReady.get() self.contentDataDisposable = (contentData.isReady.get()
|> filter { $0 } |> filter { $0 }
|> take(1) |> 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 { guard let self, let contentData, self.pendingContentData?.contentData === contentData else {
return return
} }
self.contentData = contentData
self.pendingContentData = nil
self.contentDataUpdated(synchronous: true, previousState: contentData.state)
self.chatThemeEmoticonPromise.set(contentData.chatThemeEmoticonPromise.get()) apply({ [weak self, weak contentData] forceAnimation in
self.chatWallpaperPromise.set(contentData.chatWallpaperPromise.get()) guard let self, let contentData, self.pendingContentData?.contentData === contentData else {
self.contentDataReady.set(true)
contentData.onUpdated = { [weak self, weak contentData] previousState in
guard let self, let contentData, self.contentData === contentData else {
return 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 { guard let contentData = self.contentData else {
return return
} }
@ -293,19 +306,23 @@ extension ChatControllerImpl {
if self.presentationInterfaceState.search != nil && contentData.state.hasSearchTags { if self.presentationInterfaceState.search != nil && contentData.state.hasSearchTags {
displayActionsPanel = true displayActionsPanel = true
} }
if displayActionsPanel != didDisplayActionsPanel { if displayActionsPanel != didDisplayActionsPanel {
animated = true animated = true
} }
if previousState.pinnedMessage != contentData.state.pinnedMessage { if previousState.pinnedMessage != contentData.state.pinnedMessage {
animated = true animated = true
} }
if forceAnimation {
animated = true
}
self.updateChatPresentationInterfaceState(animated: animated && self.willAppear, interactive: false, { presentationInterfaceState in self.updateChatPresentationInterfaceState(animated: animated && self.willAppear, interactive: false, { presentationInterfaceState in
var presentationInterfaceState = presentationInterfaceState var presentationInterfaceState = presentationInterfaceState
presentationInterfaceState = presentationInterfaceState.updatedPeer({ _ in presentationInterfaceState = presentationInterfaceState.updatedPeer({ _ in
return contentData.state.renderedPeer return contentData.state.renderedPeer
}) })
presentationInterfaceState = presentationInterfaceState.updatedChatLocation(contentData.chatLocation)
presentationInterfaceState = presentationInterfaceState.updatedIsNotAccessible(contentData.state.isNotAccessible) presentationInterfaceState = presentationInterfaceState.updatedIsNotAccessible(contentData.state.isNotAccessible)
presentationInterfaceState = presentationInterfaceState.updatedContactStatus(contentData.state.contactStatus) presentationInterfaceState = presentationInterfaceState.updatedContactStatus(contentData.state.contactStatus)
presentationInterfaceState = presentationInterfaceState.updatedHasBots(contentData.state.hasBots) presentationInterfaceState = presentationInterfaceState.updatedHasBots(contentData.state.hasBots)
@ -749,7 +766,7 @@ extension ChatControllerImpl {
})).startStandalone() })).startStandalone()
} }
self.setupChatHistoryNode() self.setupChatHistoryNode(historyNode: self.chatDisplayNode.historyNode)
self.chatDisplayNode.requestLayout = { [weak self] transition in self.chatDisplayNode.requestLayout = { [weak self] transition in
self?.requestLayout(transition: transition) self?.requestLayout(transition: transition)
@ -1206,7 +1223,7 @@ extension ChatControllerImpl {
self.scrollToEndOfHistory() self.scrollToEndOfHistory()
} }
} else { } 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) self.navigateToMessage(from: nil, to: .id(messageId.id, NavigateToMessageParams(timestamp: nil, quote: nil)), rememberInStack: false)
} else { } else {
if case .known = self.chatDisplayNode.historyNode.visibleContentOffset() { if case .known = self.chatDisplayNode.historyNode.visibleContentOffset() {
@ -4295,7 +4312,7 @@ extension ChatControllerImpl {
self.displayNodeDidLoad() self.displayNodeDidLoad()
} }
func setupChatHistoryNode() { func setupChatHistoryNode(historyNode: ChatHistoryListNodeImpl) {
do { do {
let peerId = self.chatLocation.peerId let peerId = self.chatLocation.peerId
if let subject = self.subject, case .scheduledMessages = subject { if let subject = self.subject, case .scheduledMessages = subject {
@ -4582,8 +4599,10 @@ extension ChatControllerImpl {
} }
} }
self.chatDisplayNode.historyNode.contentPositionChanged = { [weak self] offset in historyNode.contentPositionChanged = { [weak self, weak historyNode] offset in
guard let strongSelf = self else { return } guard let strongSelf = self, let historyNode, strongSelf.chatDisplayNode.historyNode === historyNode else {
return
}
var minOffsetForNavigation: CGFloat = 40.0 var minOffsetForNavigation: CGFloat = 40.0
strongSelf.chatDisplayNode.historyNode.enumerateItemNodes { itemNode in strongSelf.chatDisplayNode.historyNode.enumerateItemNodes { itemNode in
@ -4632,7 +4651,7 @@ extension ChatControllerImpl {
strongSelf.chatDisplayNode.updatePlainInputSeparatorAlpha(plainInputSeparatorAlpha, transition: .animated(duration: 0.2, curve: .easeInOut)) 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 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 case let .message(messageSubject, _, _, _) = strongSelf.subject, initial, case let .id(messageId) = messageSubject, messageId != index.id {
if messageId.peerId == index.id.peerId { 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 { guard let strongSelf = self else {
return return
} }
strongSelf.contentData?.scrolledToMessageIdValue = nil strongSelf.contentData?.scrolledToMessageIdValue = nil
} }
self.chatDisplayNode.historyNode.maxVisibleMessageIndexUpdated = { [weak self] index in historyNode.maxVisibleMessageIndexUpdated = { [weak self] index in
if let strongSelf = self, !strongSelf.historyNavigationStack.isEmpty { if let strongSelf = self, let contentData = strongSelf.contentData, !contentData.historyNavigationStack.isEmpty {
strongSelf.historyNavigationStack.filterOutIndicesLessThan(index) contentData.historyNavigationStack.filterOutIndicesLessThan(index)
} }
} }
@ -4723,7 +4742,7 @@ extension ChatControllerImpl {
hasActiveCalls = .single(false) 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<Bool, NoError> in |> mapToSignal { [weak self] isPlaybackActive, hasVisiblePlayableItemNodes, hasActiveCalls -> Signal<Bool, NoError> in
if hasVisiblePlayableItemNodes && !isPlaybackActive && !hasActiveCalls { if hasVisiblePlayableItemNodes && !isPlaybackActive && !hasActiveCalls {
return Signal<Bool, NoError> { [weak self] subscriber in return Signal<Bool, NoError> { [weak self] subscriber in
@ -4776,7 +4795,7 @@ extension ChatControllerImpl {
downPressed: buttonAction 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 { guard let strongSelf = self else {
return return
} }
@ -4831,7 +4850,7 @@ extension ChatControllerImpl {
} }
} }
self.chatDisplayNode.historyNode.beganDragging = { [weak self] in historyNode.beganDragging = { [weak self] in
guard let self else { guard let self else {
return 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 { guard let strongSelf = self else {
return return
} }
@ -4876,23 +4895,22 @@ extension ChatControllerImpl {
strongSelf.chatDisplayNode.loadingPlaceholderNode?.addContentOffset(offset: offset, transition: transition) strongSelf.chatDisplayNode.loadingPlaceholderNode?.addContentOffset(offset: offset, transition: transition)
} }
strongSelf.chatDisplayNode.messageTransitionNode.addExternalOffset(offset: offset, transition: transition, itemNode: itemNode, isRotated: strongSelf.chatDisplayNode.historyNode.rotated) 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 { if let strongSelf = self {
strongSelf.updateChatPresentationInterfaceState(interactive: false, { $0.updatedHasAtLeast3Messages(hasAtLeast3Messages) }) 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 { if let strongSelf = self {
strongSelf.updateChatPresentationInterfaceState(interactive: false, { $0.updatedHasPlentyOfMessages(hasPlentyOfMessages) }) strongSelf.updateChatPresentationInterfaceState(interactive: false, { $0.updatedHasPlentyOfMessages(hasPlentyOfMessages) })
} }
} }
if self.didAppear { if self.didAppear {
self.chatDisplayNode.historyNode.canReadHistory.set(self.computedCanReadHistoryPromise.get()) historyNode.canReadHistory.set(self.computedCanReadHistoryPromise.get())
} }
} }
} }

View File

@ -317,7 +317,7 @@ extension ChatControllerImpl {
}) })
} else if forceInCurrentChat { } else if forceInCurrentChat {
if let _ = fromId, let fromIndex = fromIndex, rememberInStack { if let _ = fromId, let fromIndex = fromIndex, rememberInStack {
self.historyNavigationStack.add(fromIndex) self.contentData?.historyNavigationStack.add(fromIndex)
} }
let scrollFromIndex: MessageIndex? let scrollFromIndex: MessageIndex?
@ -505,7 +505,7 @@ extension ChatControllerImpl {
return return
} }
if let _ = fromId, rememberInStack { if let _ = fromId, rememberInStack {
self.historyNavigationStack.add(fromIndex) self.contentData?.historyNavigationStack.add(fromIndex)
} }
self.loadingMessage.set(.single(statusSubject) |> delay(0.1, queue: .mainQueue())) self.loadingMessage.set(.single(statusSubject) |> delay(0.1, queue: .mainQueue()))

View File

@ -23,8 +23,6 @@ func updateChatPresentationInterfaceStateImpl(
_ f: (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState, _ f: (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState,
completion externalCompletion: @escaping (ContainedViewLayoutTransition) -> Void completion externalCompletion: @escaping (ContainedViewLayoutTransition) -> Void
) { ) {
let previousChatLocation = selfController.chatDisplayNode.historyNode.chatLocation
var completion = externalCompletion var completion = externalCompletion
var temporaryChatPresentationInterfaceState = f(selfController.presentationInterfaceState) var temporaryChatPresentationInterfaceState = f(selfController.presentationInterfaceState)
@ -436,13 +434,13 @@ func updateChatPresentationInterfaceStateImpl(
selfController.presentationInterfaceState = updatedChatPresentationInterfaceState 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 let defaultDirection: ChatControllerAnimateInnerChatSwitchDirection? = selfController.chatDisplayNode.chatLocationTabSwitchDirection(from: selfController.chatLocation.threadId, to: selfController.presentationInterfaceState.chatLocation.threadId).flatMap { direction -> ChatControllerAnimateInnerChatSwitchDirection in
return direction ? .right : .left return direction ? .right : .left
} }
let tabSwitchDirection = selfController.currentChatSwitchDirection ?? defaultDirection let tabSwitchDirection = selfController.currentChatSwitchDirection ?? defaultDirection
selfController.chatDisplayNode.updateChatLocation(chatLocation: selfController.presentationInterfaceState.chatLocation, transition: transition, tabSwitchDirection: tabSwitchDirection) selfController.chatDisplayNode.updateChatLocation(chatLocation: selfController.presentationInterfaceState.chatLocation, transition: transition, tabSwitchDirection: tabSwitchDirection)
} }*/
selfController.updateSlowmodeStatus() selfController.updateSlowmodeStatus()
@ -603,12 +601,6 @@ func updateChatPresentationInterfaceStateImpl(
} }
} }
if previousChatLocation != selfController.presentationInterfaceState.chatLocation {
selfController.chatLocation = selfController.presentationInterfaceState.chatLocation
selfController.reloadCachedData()
selfController.setupChatHistoryNode()
}
selfController.updateDownButtonVisibility() selfController.updateDownButtonVisibility()
if selfController.presentationInterfaceState.hasBirthdayToday { if selfController.presentationInterfaceState.hasBirthdayToday {

View File

@ -389,8 +389,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
var searchDisposable: MetaDisposable? var searchDisposable: MetaDisposable?
var historyNavigationStack = ChatHistoryNavigationStack()
public let canReadHistory = ValuePromise<Bool>(true, ignoreRepeated: true) public let canReadHistory = ValuePromise<Bool>(true, ignoreRepeated: true)
public let hasBrowserOrAppInFront = Promise<Bool>(false) public let hasBrowserOrAppInFront = Promise<Bool>(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() self.botCallbackAlertMessageDisposable = (self.botCallbackAlertMessage.get()
|> deliverOnMainQueue).startStrict(next: { [weak self] message in |> deliverOnMainQueue).startStrict(next: { [weak self] message in
@ -9711,10 +9709,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
var currentChatSwitchDirection: ChatControllerAnimateInnerChatSwitchDirection? var currentChatSwitchDirection: ChatControllerAnimateInnerChatSwitchDirection?
public func updateChatLocationThread(threadId: Int64?, animationDirection: ChatControllerAnimateInnerChatSwitchDirection? = nil) { public func updateChatLocationThread(threadId: Int64?, animationDirection: ChatControllerAnimateInnerChatSwitchDirection? = nil) {
/*if self.isUpdatingChatLocationThread { if self.isUpdatingChatLocationThread {
return return
} }
guard let peerId = self.chatLocation.peerId else { guard let peerId = self.chatLocation.peerId else {
return return
} }
@ -9725,16 +9722,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return return
} }
let navigationSnapshot = self.chatTitleView?.prepareSnapshotState() self.saveInterfaceState()
//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))
}
let updatedChatLocation: ChatLocation let updatedChatLocation: ChatLocation
if let threadId { if let threadId {
@ -9762,6 +9750,77 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
updatedChatLocation = .peer(id: peerId) updatedChatLocation = .peer(id: peerId)
} }
let navigationSnapshot = self.chatTitleView?.prepareSnapshotState()
let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(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<Bool>() let isReady = Promise<Bool>()
let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil) let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
self.reloadChatLocation(chatLocation: updatedChatLocation, chatLocationContextHolder: chatLocationContextHolder, historyNode: historyNode, isReady: isReady) self.reloadChatLocation(chatLocation: updatedChatLocation, chatLocationContextHolder: chatLocationContextHolder, historyNode: historyNode, isReady: isReady)

View File

@ -144,6 +144,7 @@ extension ChatControllerImpl {
private let isChatLocationInfoReady = ValuePromise<Bool>(false, ignoreRepeated: true) private let isChatLocationInfoReady = ValuePromise<Bool>(false, ignoreRepeated: true)
private let isCachedDataReady = ValuePromise<Bool>(false, ignoreRepeated: true) private let isCachedDataReady = ValuePromise<Bool>(false, ignoreRepeated: true)
let chatLocation: ChatLocation
let chatLocationInfoData: ChatLocationInfoData let chatLocationInfoData: ChatLocationInfoData
private(set) var state: State = State() private(set) var state: State = State()
@ -172,6 +173,8 @@ extension ChatControllerImpl {
} }
} }
var historyNavigationStack = ChatHistoryNavigationStack()
let chatThemeEmoticonPromise = Promise<String?>() let chatThemeEmoticonPromise = Promise<String?>()
let chatWallpaperPromise = Promise<TelegramWallpaper?>() let chatWallpaperPromise = Promise<TelegramWallpaper?>()
@ -191,6 +194,7 @@ extension ChatControllerImpl {
presentationData: PresentationData, presentationData: PresentationData,
historyNode: ChatHistoryListNodeImpl historyNode: ChatHistoryListNodeImpl
) { ) {
self.chatLocation = chatLocation
self.presentationData = presentationData self.presentationData = presentationData
let strings = self.presentationData.strings let strings = self.presentationData.strings

View File

@ -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 { class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
let context: AccountContext let context: AccountContext
private(set) var chatLocation: ChatLocation private(set) var chatLocation: ChatLocation
@ -162,7 +175,10 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
var historyNode: ChatHistoryListNodeImpl var historyNode: ChatHistoryListNodeImpl
var blurredHistoryNode: ASImageNode? var blurredHistoryNode: ASImageNode?
let historyNodeContainer: HistoryNodeContainer 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? private(set) var loadingPlaceholderNode: ChatLoadingPlaceholderNode?
var alwaysShowSearchResultsAsList: Bool = false var alwaysShowSearchResultsAsList: Bool = false
@ -316,8 +332,8 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
let adMessagesContext: AdMessagesHistoryContext? let adMessagesContext: AdMessagesHistoryContext?
private var isLoadingValue: Bool = false private var pendingSwitchToChatLocation: PendingSwitchToChatLocation?
private var isLoadingEarlier: Bool = false
private func updateIsLoading(isLoading: Bool, earlier: Bool, animated: Bool) { 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 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 { 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) loadingPlaceholderNode.setup(self.historyNode, updating: false)
if let (layout, navigationHeight) = self.validLayout { let contentBounds = self.loadingNode.frame
self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: .immediate, listViewTransaction: { _, _, _, _ in loadingPlaceholderNode.frame = contentBounds
}, updateExtraNavigationBarBackgroundHeight: { _, _, _, _ in 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 loadingPlaceholderNode.alpha = 1.0
@ -371,12 +388,17 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
} else { } else {
if useLoadingPlaceholder { if useLoadingPlaceholder {
if let loadingPlaceholderNode = self.loadingPlaceholderNode { if let loadingPlaceholderNode = self.loadingPlaceholderNode {
loadingPlaceholderNode.animateOut(self.historyNode, completion: { [weak self] in if animated {
if let strongSelf = self { loadingPlaceholderNode.animateOut(self.historyNode, completion: { [weak self] in
strongSelf.loadingPlaceholderNode?.removeFromSupernode() if let strongSelf = self {
strongSelf.loadingPlaceholderNode = nil strongSelf.loadingPlaceholderNode?.removeFromSupernode()
} strongSelf.loadingPlaceholderNode = nil
}) }
})
} else {
self.loadingPlaceholderNode = nil
loadingPlaceholderNode.removeFromSupernode()
}
} }
} else { } else {
self.loadingNode.alpha = 0.0 self.loadingNode.alpha = 0.0
@ -1295,8 +1317,6 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
} }
self.containerLayoutAndNavigationBarHeight = (layout, navigationBarHeight) self.containerLayoutAndNavigationBarHeight = (layout, navigationBarHeight)
var extraTransition = transition
var dismissedTitleTopicsAccessoryPanelNode: ChatTopicListTitleAccessoryPanelNode? var dismissedTitleTopicsAccessoryPanelNode: ChatTopicListTitleAccessoryPanelNode?
var immediatelyLayoutTitleTopicsAccessoryPanelNodeAndAnimateAppearance = false var immediatelyLayoutTitleTopicsAccessoryPanelNodeAndAnimateAppearance = false
var titleTopicsAccessoryPanelHeight: CGFloat? var titleTopicsAccessoryPanelHeight: CGFloat?
@ -1357,9 +1377,6 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
self.titleAccessoryPanelContainer.addSubnode(titleAccessoryPanelNode) self.titleAccessoryPanelContainer.addSubnode(titleAccessoryPanelNode)
titleAccessoryPanelNode.clipsToBounds = true 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) 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)
@ -1367,9 +1384,11 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
titleAccessoryPanelBackgroundHeight = layoutResult.backgroundHeight titleAccessoryPanelBackgroundHeight = layoutResult.backgroundHeight
titleAccessoryPanelHitTestSlop = layoutResult.hitTestSlop titleAccessoryPanelHitTestSlop = layoutResult.hitTestSlop
if immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance { 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) 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 { } else if let titleAccessoryPanelNode = self.titleAccessoryPanelNode {
dismissedTitleAccessoryPanelNode = titleAccessoryPanelNode dismissedTitleAccessoryPanelNode = titleAccessoryPanelNode
@ -1412,9 +1431,6 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
self.titleAccessoryPanelContainer.addSubnode(translationPanelNode) self.titleAccessoryPanelContainer.addSubnode(translationPanelNode)
translationPanelNode.clipsToBounds = true 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) 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)
@ -1422,7 +1438,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
if immediatelyLayoutTranslationPanelNodeAndAnimateAppearance { if immediatelyLayoutTranslationPanelNodeAndAnimateAppearance {
translationPanelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) translationPanelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
translationPanelNode.subnodeTransform = CATransform3DMakeTranslation(0.0, -height, 0.0) 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 { } else if let chatTranslationPanel = self.chatTranslationPanel {
dismissedTranslationPanelNode = chatTranslationPanel dismissedTranslationPanelNode = chatTranslationPanel
@ -1803,18 +1819,6 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
wrappingInsets.top += statusBarHeight 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))) 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) self.titleAccessoryPanelContainer.hitTestExcludeInsets = UIEdgeInsets(top: 0.0, left: leftPanelSize?.width ?? 0.0, bottom: 0.0, right: 0.0)
@ -1839,6 +1843,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
} }
var titleAccessoryPanelFrame: CGRect? var titleAccessoryPanelFrame: CGRect?
let titleAccessoryPanelBaseY = titlePanelsContentOffset
if let _ = self.titleAccessoryPanelNode, let panelHeight = titleAccessoryPanelHeight { if let _ = self.titleAccessoryPanelNode, let panelHeight = titleAccessoryPanelHeight {
titleAccessoryPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: titlePanelsContentOffset), size: CGSize(width: layout.size.width, height: panelHeight)) titleAccessoryPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: titlePanelsContentOffset), size: CGSize(width: layout.size.width, height: panelHeight))
insets.top += panelHeight insets.top += panelHeight
@ -1881,7 +1886,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
extraNavigationBarLeftCutout = CGSize(width: 0.0, height: navigationBarHeight) 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) 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)
@ -1908,13 +1913,154 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
transition.updateBounds(node: self.historyNodeContainer, bounds: contentBounds) transition.updateBounds(node: self.historyNodeContainer, bounds: contentBounds)
transition.updatePosition(node: self.historyNodeContainer, position: contentBounds.center) transition.updatePosition(node: self.historyNodeContainer, position: contentBounds.center)
transition.updateBounds(node: self.historyNode, bounds: CGRect(origin: CGPoint(), size: contentBounds.size)) if let pendingSwitchToChatLocation = self.pendingSwitchToChatLocation {
transition.updatePosition(node: self.historyNode, position: CGPoint(x: contentBounds.size.width / 2.0, y: contentBounds.size.height / 2.0)) 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 { if let blurredHistoryNode = self.blurredHistoryNode {
transition.updateFrame(node: blurredHistoryNode, frame: contentBounds) 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) transition.updateFrame(node: self.loadingNode, frame: contentBounds)
if let loadingPlaceholderNode = self.loadingPlaceholderNode { if let loadingPlaceholderNode = self.loadingPlaceholderNode {
@ -2552,7 +2698,13 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
if let dismissedTitleAccessoryPanelNode { if let dismissedTitleAccessoryPanelNode {
var dismissedPanelFrame = dismissedTitleAccessoryPanelNode.frame 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 transition.updateFrame(node: dismissedTitleAccessoryPanelNode, frame: dismissedPanelFrame, completion: { [weak dismissedTitleAccessoryPanelNode] _ in
dismissedTitleAccessoryPanelNode?.removeFromSupernode() dismissedTitleAccessoryPanelNode?.removeFromSupernode()
}) })
@ -5078,9 +5230,27 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
return nil 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 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?) { func updateChatLocation(chatLocation: ChatLocation, transition: ContainedViewLayoutTransition, tabSwitchDirection: ChatControllerAnimateInnerChatSwitchDirection?) {
if chatLocation == self.chatLocation { if chatLocation == self.chatLocation {
return return

View File

@ -667,7 +667,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
public var contentPositionChanged: (ListViewVisibleContentOffset) -> Void = { _ in } public var contentPositionChanged: (ListViewVisibleContentOffset) -> Void = { _ in }
public private(set) var loadState: ChatHistoryNodeLoadState? 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] = [] private var additionalLoadStateUpdated: [(ChatHistoryNodeLoadState, Bool) -> Void] = []
public private(set) var hasAtLeast3Messages: Bool = false public private(set) var hasAtLeast3Messages: Bool = false
@ -1830,11 +1830,6 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
let initialData: ChatHistoryCombinedInitialData? let initialData: ChatHistoryCombinedInitialData?
switch update.0 { switch update.0 {
case let .Loading(combinedInitialData, type): case let .Loading(combinedInitialData, type):
if case .Generic(.FillHole) = type {
applyHole()
return
}
initialData = combinedInitialData initialData = combinedInitialData
if resetScrolling, let previousViewValue = previousView.with({ $0 })?.0 { if resetScrolling, let previousViewValue = previousView.with({ $0 })?.0 {
@ -1891,13 +1886,6 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
Queue.mainQueue().async { Queue.mainQueue().async {
if let strongSelf = self { 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 cachedData = initialData?.cachedData
let cachedDataMessages = initialData?.cachedDataMessages let cachedDataMessages = initialData?.cachedDataMessages
@ -1917,8 +1905,30 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
strongSelf.currentHistoryState = historyState strongSelf.currentHistoryState = historyState
strongSelf.historyState.set(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 return
case let .HistoryView(view, type, scrollPosition, flashIndicators, originalScrollPosition, data, id): case let .HistoryView(view, type, scrollPosition, flashIndicators, originalScrollPosition, data, id):
if case .Generic(.FillHole) = type { if case .Generic(.FillHole) = type {

View File

@ -230,10 +230,6 @@ func titlePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceStat
} }
func titleTopicsPanelForChatPresentationInterfaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentPanel: ChatTitleAccessoryPanelNode?, controllerInteraction: ChatControllerInteraction?, interfaceInteraction: ChatPanelInterfaceInteraction?, force: Bool) -> ChatTopicListTitleAccessoryPanelNode? { 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 { 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 let topicListDisplayMode = chatPresentationInterfaceState.topicListDisplayMode ?? .top
if case .top = topicListDisplayMode, let peerId = chatPresentationInterfaceState.chatLocation.peerId { if case .top = topicListDisplayMode, let peerId = chatPresentationInterfaceState.chatLocation.peerId {