Merge commit 'c22ca26aae8630b165c1648952f360dae03f5cac'

This commit is contained in:
Ali 2021-09-12 22:42:23 +04:00
commit fde7516cf4
7 changed files with 131 additions and 54 deletions

View File

@ -6721,6 +6721,8 @@ Sorry for the inconvenience.";
"Conversation.Theme.DontSetTheme" = "Do Not Set Theme";
"Conversation.Theme.SwitchToDark" = "Switch to dark appearance";
"Conversation.Theme.SwitchToLight" = "Switch to light appearance";
"Conversation.Theme.DismissAlert" = "Do you want to apply the selected theme to the chat?";
"Conversation.Theme.DismissAlertApply" = "Apply";
"Notification.ChangedTheme" = "%1$@ changed chat theme to %2$@";
"Notification.DisabledTheme" = "%@ disabled chat theme";

View File

@ -24,8 +24,11 @@ public final class AnimationNode : ASDisplayNode {
return self.animationView()?.isAnimationPlaying ?? false
}
private var currentParams: (String?, [String: UIColor]?)?
public init(animation: String? = nil, colors: [String: UIColor]? = nil, scale: CGFloat = 1.0) {
self.scale = scale
self.currentParams = (animation, colors)
super.init()
@ -80,11 +83,23 @@ public final class AnimationNode : ASDisplayNode {
})
}
public func makeCopy(colors: [String: UIColor]? = nil, progress: CGFloat? = nil) -> AnimationNode? {
guard let (animation, currentColors) = self.currentParams else {
return nil
}
let animationNode = AnimationNode(animation: animation, colors: colors ?? currentColors, scale: 1.0)
animationNode.animationView()?.play(fromProgress: progress ?? (self.animationView()?.animationProgress ?? 0.0), toProgress: 1.0, withCompletion: { [weak animationNode] _ in
animationNode?.completion?()
})
return animationNode
}
public func seekToEnd() {
self.animationView()?.animationProgress = 1.0
}
public func setAnimation(name: String, colors: [String: UIColor]? = nil) {
self.currentParams = (name, colors)
if let url = getAppBundle().url(forResource: name, withExtension: "json"), let composition = LOTComposition(filePath: url.path) {
self.didPlay = false
self.animationView()?.sceneModel = composition

View File

@ -310,6 +310,13 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
private final var actionsForVSync: [() -> ()] = []
private final var inVSync = false
private var tapGestureRecognizer: UITapGestureRecognizer?
public final var tapped: (() -> Void)? {
didSet {
self.tapGestureRecognizer?.isEnabled = self.tapped != nil
}
}
private let frictionSlider = UISlider()
private let springSlider = UISlider()
private let freeResistanceSlider = UISlider()
@ -386,7 +393,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
self.view.addSubview(self.scroller)
self.scroller.panGestureRecognizer.cancelsTouchesInView = true
self.view.addGestureRecognizer(self.scroller.panGestureRecognizer)
let trackingRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.trackingGesture(_:)))
trackingRecognizer.delegate = self
trackingRecognizer.cancelsTouchesInView = false
@ -420,6 +427,12 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
self?.updateReordering(offset: offset)
}))
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))
tapGestureRecognizer.isEnabled = false
tapGestureRecognizer.delegate = self
self.view.addGestureRecognizer(tapGestureRecognizer)
self.tapGestureRecognizer = tapGestureRecognizer
self.displayLink = CADisplayLink(target: DisplayLinkProxy(target: self), selector: #selector(DisplayLinkProxy.displayLinkEvent))
self.displayLink.add(to: RunLoop.main, forMode: RunLoop.Mode.common)
@ -449,6 +462,10 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
self.reorderFeedbackDisposable?.dispose()
}
@objc private func tapGesture(_ gestureRecognizer: UITapGestureRecognizer) {
self.tapped?()
}
private func displayLinkEvent() {
self.updateAnimations()
}

View File

@ -1126,6 +1126,7 @@ public class Account {
self.managedOperationsDisposable.add(managedSynchronizeAppLogEventsOperations(postbox: self.postbox, network: self.network).start())
self.managedOperationsDisposable.add(managedNotificationSettingsBehaviors(postbox: self.postbox).start())
self.managedOperationsDisposable.add(managedThemesUpdates(accountManager: accountManager, postbox: self.postbox, network: self.network).start())
self.managedOperationsDisposable.add(managedChatThemesUpdates(accountManager: accountManager, network: self.network).start())
if !self.supplementary {
self.managedOperationsDisposable.add(managedAnimatedEmojiUpdates(postbox: self.postbox, network: self.network).start())

View File

@ -101,7 +101,7 @@ func _internal_getChatThemes(accountManager: AccountManager<TelegramAccountManag
}
}
|> mapToSignal { current, hash -> Signal<[ChatTheme], NoError> in
if onlyCached {
if onlyCached && !current.isEmpty {
return .single(current)
} else {
return .single(current)
@ -151,3 +151,11 @@ extension ChatTheme {
}
}
}
func managedChatThemesUpdates(accountManager: AccountManager<TelegramAccountManagerTypes>, network: Network) -> Signal<Void, NoError> {
let poll = _internal_getChatThemes(accountManager: accountManager, network: network)
|> mapToSignal { _ -> Signal<Void, NoError> in
return .complete()
}
return (poll |> then(.complete() |> suspendAwareDelay(1.0 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue()))) |> restart
}

View File

@ -3924,7 +3924,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let accountManager = context.sharedContext.accountManager
let currentThemeEmoticon = Atomic<(String?, Bool)?>(value: nil)
self.presentationDataDisposable = combineLatest(queue: Queue.mainQueue(), context.sharedContext.presentationData, themeSettings, context.engine.themes.getChatThemes(accountManager: accountManager, onlyCached: false), themeEmoticon, self.themeEmoticonAndDarkAppearancePreviewPromise.get()).start(next: { [weak self] presentationData, themeSettings, chatThemes, themeEmoticon, themeEmoticonAndDarkAppearance in
self.presentationDataDisposable = combineLatest(queue: Queue.mainQueue(), context.sharedContext.presentationData, themeSettings, context.engine.themes.getChatThemes(accountManager: accountManager, onlyCached: true), themeEmoticon, self.themeEmoticonAndDarkAppearancePreviewPromise.get()).start(next: { [weak self] presentationData, themeSettings, chatThemes, themeEmoticon, themeEmoticonAndDarkAppearance in
if let strongSelf = self {
let (themeEmoticonPreview, darkAppearancePreview) = themeEmoticonAndDarkAppearance
@ -13460,6 +13460,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return nil
}
}
controller.dismissed = { [weak self] in
if let strongSelf = self {
strongSelf.chatDisplayNode.historyNode.tapped = nil
}
}
strongSelf.chatDisplayNode.historyNode.tapped = { [weak controller] in
controller?.dimTapped()
}
strongSelf.present(controller, in: .window(.root))
strongSelf.themeSceen = controller
})

View File

@ -529,6 +529,8 @@ final class ChatThemeScreen: ViewController {
private var presentationData: PresentationData
private var presentationDataDisposable: Disposable?
var dismissed: (() -> Void)?
var passthroughHitTestImpl: ((CGPoint) -> UIView?)? {
didSet {
if self.isNodeLoaded {
@ -572,7 +574,7 @@ final class ChatThemeScreen: ViewController {
}
override public func loadDisplayNode() {
self.displayNode = ChatThemeScreenNode(context: self.context, presentationData: self.presentationData, animatedEmojiStickers: self.animatedEmojiStickers, initiallySelectedEmoticon: self.initiallySelectedEmoticon, dismissByTapOutside: self.dismissByTapOutside)
self.displayNode = ChatThemeScreenNode(context: self.context, presentationData: self.presentationData, controller: self, animatedEmojiStickers: self.animatedEmojiStickers, initiallySelectedEmoticon: self.initiallySelectedEmoticon, dismissByTapOutside: self.dismissByTapOutside)
self.controllerNode.passthroughHitTestImpl = self.passthroughHitTestImpl
self.controllerNode.previewTheme = { [weak self] emoticon, dark in
guard let strongSelf = self else {
@ -629,6 +631,8 @@ final class ChatThemeScreen: ViewController {
})
self.controllerNode.animateOut(completion: completion)
self.dismissed?()
}
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
@ -636,6 +640,10 @@ final class ChatThemeScreen: ViewController {
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
}
func dimTapped() {
self.controllerNode.dimTapped()
}
}
private func iconColors(theme: PresentationTheme) -> [String: UIColor] {
@ -658,6 +666,7 @@ private func iconColors(theme: PresentationTheme) -> [String: UIColor] {
private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelegate {
private let context: AccountContext
private var presentationData: PresentationData
private weak var controller: ChatThemeScreen?
private let dismissByTapOutside: Bool
private let dimNode: ASDisplayNode
@ -671,7 +680,8 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
private let textNode: ImmediateTextNode
private let cancelButton: HighlightableButtonNode
private let switchThemeButton: HighlightTrackingButtonNode
private let animationNode: AnimationNode
private let animationContainerNode: ASDisplayNode
private var animationNode: AnimationNode
private let doneButton: SolidRoundedButtonNode
private let listNode: ListView
@ -704,8 +714,9 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
var dismiss: (() -> Void)?
var cancel: (() -> Void)?
init(context: AccountContext, presentationData: PresentationData, animatedEmojiStickers: [String: [StickerPackItem]], initiallySelectedEmoticon: String?, dismissByTapOutside: Bool) {
init(context: AccountContext, presentationData: PresentationData, controller: ChatThemeScreen, animatedEmojiStickers: [String: [StickerPackItem]], initiallySelectedEmoticon: String?, dismissByTapOutside: Bool) {
self.context = context
self.controller = controller
self.initiallySelectedEmoticon = initiallySelectedEmoticon
self.selectedEmoticon = initiallySelectedEmoticon
self.selectedEmoticonPromise = ValuePromise(initiallySelectedEmoticon)
@ -754,6 +765,9 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
self.cancelButton.setImage(closeButtonImage(theme: self.presentationData.theme), for: .normal)
self.switchThemeButton = HighlightTrackingButtonNode()
self.animationContainerNode = ASDisplayNode()
self.animationContainerNode.isUserInteractionEnabled = false
self.animationNode = AnimationNode(animation: self.isDarkAppearance ? "anim_sun_reverse" : "anim_sun", colors: iconColors(theme: self.presentationData.theme), scale: 1.0)
self.animationNode.isUserInteractionEnabled = false
@ -768,7 +782,6 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
self.backgroundColor = nil
self.isOpaque = false
self.dimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:))))
self.addSubnode(self.dimNode)
self.wrappingScrollNode.view.delegate = self
@ -784,7 +797,8 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
self.contentContainerNode.addSubnode(self.textNode)
self.contentContainerNode.addSubnode(self.doneButton)
self.topContentContainerNode.addSubnode(self.animationNode)
self.topContentContainerNode.addSubnode(self.animationContainerNode)
self.animationContainerNode.addSubnode(self.animationNode)
self.topContentContainerNode.addSubnode(self.switchThemeButton)
self.topContentContainerNode.addSubnode(self.listNode)
self.topContentContainerNode.addSubnode(self.cancelButton)
@ -820,7 +834,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
let action: (String?) -> Void = { [weak self] emoticon in
if let strongSelf = self, strongSelf.selectedEmoticon != emoticon {
strongSelf.animateCrossfade(animateBackground: strongSelf.presentationData.theme.overallDarkAppearance, updateSunIcon: true)
strongSelf.animateCrossfade(animateIcon: false)
strongSelf.previewTheme?(emoticon, strongSelf.isDarkAppearance)
strongSelf.selectedEmoticon = emoticon
@ -928,11 +942,6 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
let previousTheme = self.presentationData.theme
self.presentationData = presentationData
if let effectView = self.effectNode.view as? UIVisualEffectView {
effectView.effect = UIBlurEffect(style: presentationData.theme.actionSheet.backgroundType == .light ? .light : .dark)
}
self.contentBackgroundNode.backgroundColor = self.presentationData.theme.actionSheet.itemBackgroundColor
self.titleNode.attributedText = NSAttributedString(string: self.titleNode.attributedText?.string ?? "", font: Font.bold(17.0), textColor: self.presentationData.theme.actionSheet.primaryTextColor)
if previousTheme !== presentationData.theme, let (layout, navigationBarHeight) = self.containerLayout {
@ -943,7 +952,19 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
self.doneButton.updateTheme(SolidRoundedButtonTheme(theme: self.presentationData.theme))
if self.animationNode.isPlaying {
if let animationNode = self.animationNode.makeCopy(colors: iconColors(theme: self.presentationData.theme), progress: 0.25) {
let previousAnimationNode = self.animationNode
self.animationNode = animationNode
animationNode.completion = { [weak previousAnimationNode] in
previousAnimationNode?.removeFromSupernode()
}
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)
animationNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
} else {
self.animationNode.setAnimation(name: self.isDarkAppearance ? "anim_sun_reverse" : "anim_sun", colors: iconColors(theme: self.presentationData.theme))
}
@ -963,8 +984,21 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
self.cancel?()
}
func dimTapped() {
if self.selectedEmoticon == self.initiallySelectedEmoticon {
self.cancelButtonPressed()
} else {
let alertController = textAlertController(context: self.context, updatedPresentationData: (self.presentationData, .single(self.presentationData)), title: nil, text: self.presentationData.strings.Conversation_Theme_DismissAlert, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Conversation_Theme_DismissAlertApply, action: { [weak self] in
if let strongSelf = self {
strongSelf.completion?(strongSelf.selectedEmoticon)
}
})], actionLayout: .horizontal, dismissOnOutsideTap: true)
self.present?(alertController)
}
}
@objc func switchThemePressed() {
self.animateCrossfade(animateBackground: 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()
@ -975,16 +1009,10 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
let _ = ApplicationSpecificNotice.incrementChatSpecificThemesDarkPreviewTip(accountManager: self.context.sharedContext.accountManager, count: 3).start()
}
@objc func dimTapGesture(_ recognizer: UITapGestureRecognizer) {
if self.dismissByTapOutside, case .ended = recognizer.state {
self.cancelButtonPressed()
}
}
private func animateCrossfade(animateBackground: Bool = true, updateSunIcon: Bool = false) {
private func animateCrossfade(animateIcon: Bool = true) {
let delay: Double = 0.2
if let snapshotView = self.animationNode.view.snapshotView(afterScreenUpdates: false) {
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)
@ -993,15 +1021,19 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
})
}
if animateBackground, let snapshotView = self.backgroundNode.view.snapshotView(afterScreenUpdates: false) {
snapshotView.frame = self.backgroundNode.frame
self.backgroundNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.backgroundNode.view)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, delay: delay, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
Queue.mainQueue().after(delay) {
if let effectView = self.effectNode.view as? UIVisualEffectView {
UIView.animate(withDuration: 0.3, delay: 0.0, options: .curveEaseInOut) {
effectView.effect = UIBlurEffect(style: self.presentationData.theme.actionSheet.backgroundType == .light ? .light : .dark)
} completion: { _ in
}
}
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)
}
if let snapshotView = self.contentContainerNode.view.snapshotView(afterScreenUpdates: false) {
snapshotView.frame = self.contentContainerNode.frame
self.contentContainerNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.contentContainerNode.view)
@ -1055,33 +1087,26 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
func animateOut(completion: (() -> Void)? = nil) {
self.animatedOut = true
var dimCompleted = false
var offsetCompleted = false
let internalCompletion: () -> Void = { [weak self] in
if let strongSelf = self, dimCompleted && offsetCompleted {
strongSelf.dismiss?()
}
completion?()
}
self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in
dimCompleted = true
internalCompletion()
})
let offset = self.bounds.size.height - self.contentBackgroundNode.frame.minY
let dimPosition = self.dimNode.layer.position
self.dimNode.layer.animatePosition(from: dimPosition, to: CGPoint(x: dimPosition.x, y: dimPosition.y - offset), duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false)
self.layer.animateBoundsOriginYAdditive(from: 0.0, to: -offset, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, completion: { _ in
offsetCompleted = true
internalCompletion()
self.wrappingScrollNode.layer.animateBoundsOriginYAdditive(from: 0.0, to: -offset, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, completion: { [weak self] _ in
if let strongSelf = self {
strongSelf.dismiss?()
completion?()
}
})
}
var passthroughHitTestImpl: ((CGPoint) -> UIView?)?
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if self.bounds.contains(point) {
var presentingAlertController = false
self.controller?.forEachController({ c in
if c is AlertController {
presentingAlertController = true
}
return true
})
if !presentingAlertController && self.bounds.contains(point) {
if !self.contentBackgroundNode.bounds.contains(self.convert(point, to: self.contentBackgroundNode)) {
if let result = self.passthroughHitTestImpl?(point) {
return result
@ -1136,7 +1161,8 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
let switchThemeSize = CGSize(width: 44.0, height: 44.0)
let switchThemeFrame = CGRect(origin: CGPoint(x: 3.0, y: 6.0), size: switchThemeSize)
transition.updateFrame(node: self.switchThemeButton, frame: switchThemeFrame)
transition.updateFrame(node: self.animationNode, frame: switchThemeFrame.insetBy(dx: 9.0, dy: 9.0))
transition.updateFrame(node: self.animationContainerNode, frame: switchThemeFrame.insetBy(dx: 9.0, dy: 9.0))
transition.updateFrame(node: self.animationNode, frame: CGRect(origin: CGPoint(), size: self.animationContainerNode.frame.size))
let cancelSize = CGSize(width: 44.0, height: 44.0)
let cancelFrame = CGRect(origin: CGPoint(x: contentFrame.width - cancelSize.width - 3.0, y: 6.0), size: cancelSize)