Theme preview crossfade fixes

This commit is contained in:
Ilya Laktyushin 2021-09-15 02:08:05 +03:00
parent d44cee406c
commit f696edf0a4
9 changed files with 61 additions and 25 deletions

View File

@ -191,6 +191,8 @@ public final class GradientBackgroundNode: ASDisplayNode {
self.parentNode = parentNode
super.init()
self.displaysAsynchronously = false
self.index = parentNode.cloneNodes.add(Weak<CloneNode>(self))
self.image = parentNode.dimmedImage

View File

@ -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()

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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 {

View File

@ -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()
})
}

View File

@ -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

View File

@ -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)