From f696edf0a43d0dfe0229f81a8acdf46c392eff56 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 15 Sep 2021 02:08:05 +0300 Subject: [PATCH] Theme preview crossfade fixes --- .../Sources/SoftwareGradientBackground.swift | 2 ++ .../Sources/PresentationTheme.swift | 1 + .../TelegramUI/Sources/ChatController.swift | 23 ++++++++++---- .../Sources/ChatHistoryListNode.swift | 4 +++ .../Sources/ChatMessageBubbleItemNode.swift | 5 +++- .../ChatMessageTextBubbleContentNode.swift | 2 +- .../TelegramUI/Sources/ChatThemeScreen.swift | 30 ++++++++++++------- .../Sources/PeerInfo/PeerInfoScreen.swift | 17 ++++++----- .../Sources/WallpaperBackgroundNode.swift | 2 ++ 9 files changed, 61 insertions(+), 25 deletions(-) diff --git a/submodules/GradientBackground/Sources/SoftwareGradientBackground.swift b/submodules/GradientBackground/Sources/SoftwareGradientBackground.swift index 4b4eb1ae2c..d9fa846ecf 100644 --- a/submodules/GradientBackground/Sources/SoftwareGradientBackground.swift +++ b/submodules/GradientBackground/Sources/SoftwareGradientBackground.swift @@ -191,6 +191,8 @@ public final class GradientBackgroundNode: ASDisplayNode { self.parentNode = parentNode super.init() + + self.displaysAsynchronously = false self.index = parentNode.cloneNodes.add(Weak(self)) self.image = parentNode.dimmedImage diff --git a/submodules/TelegramPresentationData/Sources/PresentationTheme.swift b/submodules/TelegramPresentationData/Sources/PresentationTheme.swift index 6c795a2d9b..c0d0a57389 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationTheme.swift @@ -1270,6 +1270,7 @@ public final class PresentationTheme: Equatable { public let inAppNotification: PresentationThemeInAppNotification public let chart: PresentationThemeChart public let preview: Bool + public var forceSync: Bool = false public let resourceCache: PresentationsResourceCache = PresentationsResourceCache() diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index c6a6826aae..051989afe3 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -3964,7 +3964,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } let customTheme = useDarkAppearance ? theme.darkTheme : theme.theme if let settings = customTheme.settings, let theme = makePresentationTheme(settings: settings) { + theme.forceSync = true presentationData = presentationData.withUpdated(theme: theme).withUpdated(chatWallpaper: theme.chat.defaultWallpaper) + + Queue.mainQueue().after(1.0, { + theme.forceSync = false + }) } } else if let darkAppearancePreview = darkAppearancePreview { useDarkAppearance = darkAppearancePreview @@ -3988,7 +3993,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let themeSpecificWallpaper = themeSpecificWallpaper { lightWallpaper = themeSpecificWallpaper } else { - let theme = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: themeSettings.theme, accentColor: currentColors?.color, bubbleColors: currentColors?.customBubbleColors ?? [], wallpaper: currentColors?.wallpaper, baseColor: currentColors?.baseColor) ?? defaultPresentationTheme + let theme = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: themeSettings.theme, accentColor: currentColors?.color, bubbleColors: currentColors?.customBubbleColors ?? [], wallpaper: currentColors?.wallpaper, baseColor: currentColors?.baseColor, preview: true) ?? defaultPresentationTheme lightWallpaper = theme.chat.defaultWallpaper } @@ -4022,8 +4027,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } if darkAppearancePreview { + darkTheme.forceSync = true + Queue.mainQueue().after(1.0, { + darkTheme.forceSync = false + }) presentationData = presentationData.withUpdated(theme: darkTheme).withUpdated(chatWallpaper: darkWallpaper) } else { + lightTheme.forceSync = true + Queue.mainQueue().after(1.0, { + lightTheme.forceSync = false + }) presentationData = presentationData.withUpdated(theme: lightTheme).withUpdated(chatWallpaper: lightWallpaper) } } @@ -4040,7 +4053,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.presentationDataPromise.set(.single(strongSelf.presentationData)) if !isFirstTime && (previousThemeEmoticon?.0 != themeEmoticon || previousThemeEmoticon?.1 != useDarkAppearance) { - strongSelf.presentCrossfadeSnapshot(delay: 0.2) + strongSelf.presentCrossfadeSnapshot() } } strongSelf.presentationReady.set(.single(true)) @@ -13389,14 +13402,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } private var crossfading = false - private func presentCrossfadeSnapshot(delay: Double) { + private func presentCrossfadeSnapshot() { guard !self.crossfading, let snapshotView = self.view.snapshotView(afterScreenUpdates: false) else { return } self.crossfading = true self.view.addSubview(snapshotView) - snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, delay: delay, removeOnCompletion: false, completion: { [weak self, weak snapshotView] _ in + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: ChatThemeScreen.themeCrossfadeDuration, delay: ChatThemeScreen.themeCrossfadeDelay, timingFunction: CAMediaTimingFunctionName.linear.rawValue, removeOnCompletion: false, completion: { [weak self, weak snapshotView] _ in self?.crossfading = false snapshotView?.removeFromSuperview() }) @@ -13454,7 +13467,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let controller = ChatThemeScreen(context: context, updatedPresentationData: strongSelf.updatedPresentationData, animatedEmojiStickers: animatedEmojiStickers, initiallySelectedEmoticon: selectedEmoticon, peerName: strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer?.compactDisplayTitle ?? "", previewTheme: { [weak self] emoticon, dark in if let strongSelf = self { - strongSelf.presentCrossfadeSnapshot(delay: 0.2) + strongSelf.presentCrossfadeSnapshot() strongSelf.themeEmoticonAndDarkAppearancePreviewPromise.set(.single((emoticon, dark))) } }, completion: { [weak self] emoticon in diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 309b05363d..40a4aa4a06 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -1122,6 +1122,10 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { let rawTransition = preparedChatHistoryViewTransition(from: previous, to: processedView, reason: reason, reverse: reverse, chatLocation: chatLocation, controllerInteraction: controllerInteraction, scrollPosition: updatedScrollPosition, scrollAnimationCurve: scrollAnimationCurve, initialData: initialData?.initialData, keyboardButtonsMessage: view.topTaggedMessages.first, cachedData: initialData?.cachedData, cachedDataMessages: initialData?.cachedDataMessages, readStateData: initialData?.readStateData, flashIndicators: flashIndicators, updatedMessageSelection: previousSelectedMessages != selectedMessages, messageTransitionNode: messageTransitionNode(), allUpdated: updateAllOnEachVersion) var mappedTransition = mappedChatHistoryViewListTransition(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, lastHeaderId: lastHeaderId, transition: rawTransition) + + disableAnimations = true + mappedTransition.options.insert(.LowLatency) + mappedTransition.options.insert(.PreferSynchronousDrawing) if disableAnimations { mappedTransition.options.remove(.AnimateInsertion) mappedTransition.options.remove(.AnimateAlpha) diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index 51920cb736..195bcff4d4 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -2312,6 +2312,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode backgroundType = .incoming(mergeType) } let hasWallpaper = item.presentationData.theme.wallpaper.hasWallpaper + if item.presentationData.theme.theme.forceSync { + transition = .immediate + } strongSelf.backgroundNode.setType(type: backgroundType, highlighted: strongSelf.highlightedState, graphics: graphics, maskMode: strongSelf.backgroundMaskMode, hasWallpaper: hasWallpaper, transition: transition, backgroundNode: presentationContext.backgroundNode) strongSelf.backgroundWallpaperNode.setType(type: backgroundType, theme: item.presentationData.theme, essentialGraphics: graphics, maskMode: strongSelf.backgroundMaskMode, backgroundNode: presentationContext.backgroundNode) strongSelf.shadowNode.setType(type: backgroundType, hasWallpaper: hasWallpaper, graphics: graphics) @@ -2364,7 +2367,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode strongSelf.clippingNode.addSubnode(nameNode) } nameNode.frame = CGRect(origin: CGPoint(x: contentOrigin.x + layoutConstants.text.bubbleInsets.left, y: layoutConstants.bubble.contentInsets.top + nameNodeOriginY), size: nameNodeSizeApply.0) - nameNode.displaysAsynchronously = !item.presentationData.isPreview + nameNode.displaysAsynchronously = !item.presentationData.isPreview && !item.presentationData.theme.theme.forceSync if let credibilityIconImage = currentCredibilityIconImage { let credibilityIconNode: ASImageNode diff --git a/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift index 026435bc42..098a079fd0 100644 --- a/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift @@ -381,7 +381,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { } } - strongSelf.textNode.displaysAsynchronously = !item.presentationData.isPreview + strongSelf.textNode.displaysAsynchronously = !item.presentationData.isPreview && !item.presentationData.theme.theme.forceSync let _ = textApply() if let statusApply = statusApply, let adjustedStatusFrame = adjustedStatusFrame { diff --git a/submodules/TelegramUI/Sources/ChatThemeScreen.swift b/submodules/TelegramUI/Sources/ChatThemeScreen.swift index a8eba54a7e..d30975abd9 100644 --- a/submodules/TelegramUI/Sources/ChatThemeScreen.swift +++ b/submodules/TelegramUI/Sources/ChatThemeScreen.swift @@ -283,9 +283,11 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode { self.textNode = TextNode() self.textNode.isUserInteractionEnabled = false + self.textNode.displaysAsynchronously = false self.emojiNode = TextNode() self.emojiNode.isUserInteractionEnabled = false + self.emojiNode.displaysAsynchronously = false self.emojiImageNode = TransformImageNode() @@ -495,7 +497,7 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode { snapshotView.frame = self.containerNode.view.frame self.view.insertSubview(snapshotView, aboveSubview: self.containerNode.view) - snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, delay: 0.2, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: ChatThemeScreen.themeCrossfadeDuration, delay: ChatThemeScreen.themeCrossfadeDelay, timingFunction: CAMediaTimingFunctionName.linear.rawValue, removeOnCompletion: false, completion: { [weak snapshotView] _ in snapshotView?.removeFromSuperview() }) } @@ -521,6 +523,9 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode { } final class ChatThemeScreen: ViewController { + static let themeCrossfadeDuration: Double = 0.3 + static let themeCrossfadeDelay: Double = 0.25 + private var controllerNode: ChatThemeScreenNode { return self.displayNode as! ChatThemeScreenNode } @@ -839,7 +844,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega let action: (String?) -> Void = { [weak self] emoticon in if let strongSelf = self, strongSelf.selectedEmoticon != emoticon { - strongSelf.animateCrossfade(animateIcon: false) + strongSelf.animateCrossfade(animateIcon: true) strongSelf.previewTheme?(emoticon, strongSelf.isDarkAppearance) strongSelf.selectedEmoticon = emoticon @@ -973,7 +978,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega animationNode.isUserInteractionEnabled = false animationNode.frame = previousAnimationNode.frame previousAnimationNode.supernode?.insertSubnode(animationNode, belowSubnode: previousAnimationNode) - previousAnimationNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false) + previousAnimationNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: ChatThemeScreen.themeCrossfadeDuration, removeOnCompletion: false) animationNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) } } else { @@ -1009,6 +1014,11 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega } @objc func switchThemePressed() { + self.switchThemeButton.isUserInteractionEnabled = false + Queue.mainQueue().after(0.5) { + self.switchThemeButton.isUserInteractionEnabled = true + } + self.animateCrossfade(animateIcon: false) self.animationNode.setAnimation(name: self.isDarkAppearance ? "anim_sun_reverse" : "anim_sun", colors: iconColors(theme: self.presentationData.theme)) self.animationNode.playOnce() @@ -1024,21 +1034,19 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega } } - private func animateCrossfade(animateIcon: Bool = true) { - let delay: Double = 0.2 - + private func animateCrossfade(animateIcon: Bool) { if animateIcon, let snapshotView = self.animationNode.view.snapshotView(afterScreenUpdates: false) { snapshotView.frame = self.animationNode.frame self.animationNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.animationNode.view) - snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, delay: delay, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: ChatThemeScreen.themeCrossfadeDuration, delay: ChatThemeScreen.themeCrossfadeDelay, timingFunction: CAMediaTimingFunctionName.linear.rawValue, removeOnCompletion: false, completion: { [weak snapshotView] _ in snapshotView?.removeFromSuperview() }) } - Queue.mainQueue().after(delay) { + Queue.mainQueue().after(ChatThemeScreen.themeCrossfadeDelay) { if let effectView = self.effectNode.view as? UIVisualEffectView { - UIView.animate(withDuration: 0.3, delay: 0.0, options: .curveEaseInOut) { + UIView.animate(withDuration: ChatThemeScreen.themeCrossfadeDuration, delay: 0.0, options: .curveLinear) { effectView.effect = UIBlurEffect(style: self.presentationData.theme.actionSheet.backgroundType == .light ? .light : .dark) } completion: { _ in } @@ -1046,14 +1054,14 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega let previousColor = self.contentBackgroundNode.backgroundColor ?? .clear self.contentBackgroundNode.backgroundColor = self.presentationData.theme.actionSheet.itemBackgroundColor - self.contentBackgroundNode.layer.animate(from: previousColor.cgColor, to: (self.contentBackgroundNode.backgroundColor ?? .clear).cgColor, keyPath: "backgroundColor", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.3) + self.contentBackgroundNode.layer.animate(from: previousColor.cgColor, to: (self.contentBackgroundNode.backgroundColor ?? .clear).cgColor, keyPath: "backgroundColor", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: ChatThemeScreen.themeCrossfadeDuration) } if let snapshotView = self.contentContainerNode.view.snapshotView(afterScreenUpdates: false) { snapshotView.frame = self.contentContainerNode.frame self.contentContainerNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.contentContainerNode.view) - snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, delay: delay, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: ChatThemeScreen.themeCrossfadeDuration, delay: ChatThemeScreen.themeCrossfadeDelay, timingFunction: CAMediaTimingFunctionName.linear.rawValue, removeOnCompletion: false, completion: { [weak snapshotView] _ in snapshotView?.removeFromSuperview() }) } diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 74fb02ca84..738612a34f 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -3484,16 +3484,19 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD } var mainItemsImpl: (() -> Signal<[ContextMenuItem], NoError>)? - mainItemsImpl = { + mainItemsImpl = { [weak self] in var items: [ContextMenuItem] = [] + guard let strongSelf = self else { + return .single(items) + } - let allHeaderButtons = Set(peerInfoHeaderButtons(peer: peer, cachedData: data.cachedData, isOpenedFromChat: self.isOpenedFromChat, isExpanded: false, videoCallsEnabled: self.videoCallsEnabled, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false)) - let headerButtons = Set(peerInfoHeaderButtons(peer: peer, cachedData: data.cachedData, isOpenedFromChat: self.isOpenedFromChat, isExpanded: self.headerNode.isAvatarExpanded, videoCallsEnabled: self.videoCallsEnabled, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false)) + let allHeaderButtons = Set(peerInfoHeaderButtons(peer: peer, cachedData: data.cachedData, isOpenedFromChat: strongSelf.isOpenedFromChat, isExpanded: false, videoCallsEnabled: strongSelf.videoCallsEnabled, isSecretChat: strongSelf.peerId.namespace == Namespaces.Peer.SecretChat, isContact: strongSelf.data?.isContact ?? false)) + let headerButtons = Set(peerInfoHeaderButtons(peer: peer, cachedData: data.cachedData, isOpenedFromChat: strongSelf.isOpenedFromChat, isExpanded: strongSelf.headerNode.isAvatarExpanded, videoCallsEnabled: strongSelf.videoCallsEnabled, isSecretChat: strongSelf.peerId.namespace == Namespaces.Peer.SecretChat, isContact: strongSelf.data?.isContact ?? false)) let filteredButtons = allHeaderButtons.subtracting(headerButtons) var canChangeColors = false - if peer is TelegramUser, self.data?.encryptionKeyFingerprint == nil { + if peer is TelegramUser, strongSelf.data?.encryptionKeyFingerprint == nil { canChangeColors = true } @@ -3633,7 +3636,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD }))) } - if self.peerId.namespace == Namespaces.Peer.CloudUser && user.botInfo == nil && !user.flags.contains(.isSupport) { + if strongSelf.peerId.namespace == Namespaces.Peer.CloudUser && user.botInfo == nil && !user.flags.contains(.isSupport) { items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_StartSecretChat, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Timer"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in @@ -3653,7 +3656,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD }))) } } - } else if self.peerId.namespace == Namespaces.Peer.SecretChat && data.isContact { + } else if strongSelf.peerId.namespace == Namespaces.Peer.SecretChat && data.isContact { if let cachedData = data.cachedData as? CachedUserData, cachedData.isBlocked { } else { items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_BlockUser, icon: { theme in @@ -3666,7 +3669,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD } } } else if let channel = peer as? TelegramChannel { - if let cachedData = self.data?.cachedData as? CachedChannelData, cachedData.flags.contains(.canViewStats) { + if let cachedData = strongSelf.data?.cachedData as? CachedChannelData, cachedData.flags.contains(.canViewStats) { items.append(.action(ContextMenuActionItem(text: presentationData.strings.ChannelInfo_Stats, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Statistics"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in diff --git a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift index 985d1b2095..5afc0c891d 100644 --- a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift +++ b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift @@ -65,6 +65,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode { self.bubbleType = bubbleType self.contentNode = ASImageNode() + self.contentNode.displaysAsynchronously = false self.contentNode.isUserInteractionEnabled = false super.init() @@ -165,6 +166,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode { if needsWallpaperBackground { if self.cleanWallpaperNode == nil { let cleanWallpaperNode = ASImageNode() + cleanWallpaperNode.displaysAsynchronously = false self.cleanWallpaperNode = cleanWallpaperNode cleanWallpaperNode.frame = self.bounds self.insertSubnode(cleanWallpaperNode, at: 0)