From 22b04a7ffbe43775f9ace157c990dc26e24865e5 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sat, 23 Jan 2021 22:45:41 +0400 Subject: [PATCH] [WIP] Chat Import --- .../Sources/AnimatedStickerNode.swift | 14 +++++ submodules/ChatImportUI/BUILD | 1 + .../Sources/ChatImportActivityScreen.swift | 50 +++++++++++++++++- .../Sources/ChatListSearchMediaNode.swift | 2 +- .../Sources/Node/ChatListStatusNode.swift | 4 +- .../ChatMessageInteractiveMediaBadge.swift | 2 +- submodules/ConfettiEffect/BUILD | 16 ++++++ .../Sources/ConfettiView.swift | 6 +-- .../Items/ChatAnimationGalleryItem.swift | 4 +- .../Items/ChatDocumentGalleryItem.swift | 4 +- .../Items/ChatExternalFileGalleryItem.swift | 4 +- .../Sources/Items/ChatImageGalleryItem.swift | 4 +- .../Items/UniversalVideoGalleryItem.swift | 4 +- .../Sources/InstantPageImageNode.swift | 2 +- .../InstantPagePlayableVideoNode.swift | 2 +- .../Sources/LegacyDataImportSplash.swift | 2 +- .../Sources/SecureIdValueFormFileItem.swift | 2 +- .../Sources/RadialProgressContentNode.swift | 42 +++++++++------ .../Sources/RadialStatusNode.swift | 16 +++--- .../Sources/Themes/WallpaperGalleryItem.swift | 4 +- .../Sources/ShareLoadingContainerNode.swift | 6 +-- submodules/TelegramUI/BUILD | 1 + .../Resources/Animations/HistoryImport.tgs | Bin 12545 -> 18660 bytes .../Sources/ChatControllerNode.swift | 1 + .../ChatMessageCommentFooterContentNode.swift | 2 +- ...atMessageInteractiveInstantVideoNode.swift | 6 +-- .../ChatMessageInteractiveMediaNode.swift | 4 +- .../ChatPinnedMessageTitlePanelNode.swift | 2 +- .../Sources/EditAccessoryPanelNode.swift | 2 +- .../TelegramUI/Sources/GridMessageItem.swift | 2 +- ...ListContextResultsChatInputPanelItem.swift | 2 +- .../Panes/PeerInfoVisualMediaPaneNode.swift | 2 +- .../Sources/PeerInfo/PeerInfoHeaderNode.swift | 6 +-- .../Sources/ShareExtensionContext.swift | 27 +++++++--- ...ListContextResultsChatInputPanelItem.swift | 2 +- .../Sources/OverlayVideoDecoration.swift | 2 +- .../Sources/WebSearchVideoGalleryItem.swift | 4 +- 37 files changed, 180 insertions(+), 76 deletions(-) create mode 100644 submodules/ConfettiEffect/BUILD rename submodules/{TelegramUI => ConfettiEffect}/Sources/ConfettiView.swift (98%) diff --git a/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift b/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift index e6a842ef79..8abde56c9d 100644 --- a/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift +++ b/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift @@ -757,6 +757,8 @@ public final class AnimatedStickerNode: ASDisplayNode { private var canDisplayFirstFrame: Bool = false private var playbackMode: AnimatedStickerPlaybackMode = .loop + public var stopAtNearestLoop: Bool = false + private let playbackStatus = Promise() public var status: Signal { return self.playbackStatus.get() @@ -966,7 +968,13 @@ public final class AnimatedStickerNode: ASDisplayNode { if frame.isLastFrame { var stopped = false + var stopNow = false if case .once = strongSelf.playbackMode { + stopNow = true + } else if strongSelf.stopAtNearestLoop { + stopNow = true + } + if stopNow { strongSelf.stop() strongSelf.isPlaying = false stopped = true @@ -1043,7 +1051,13 @@ public final class AnimatedStickerNode: ASDisplayNode { if frame.isLastFrame { var stopped = false + var stopNow = false if case .once = strongSelf.playbackMode { + stopNow = true + } else if strongSelf.stopAtNearestLoop { + stopNow = true + } + if stopNow { strongSelf.stop() strongSelf.isPlaying = false stopped = true diff --git a/submodules/ChatImportUI/BUILD b/submodules/ChatImportUI/BUILD index 016041c6fb..ed566db9a2 100644 --- a/submodules/ChatImportUI/BUILD +++ b/submodules/ChatImportUI/BUILD @@ -22,6 +22,7 @@ swift_library( "//submodules/AnimatedStickerNode:AnimatedStickerNode", "//submodules/ChatHistoryImportTasks:ChatHistoryImportTasks", "//submodules/MimeTypes:MimeTypes", + "//submodules/ConfettiEffect:ConfettiEffect", ], visibility = [ "//visibility:public", diff --git a/submodules/ChatImportUI/Sources/ChatImportActivityScreen.swift b/submodules/ChatImportUI/Sources/ChatImportActivityScreen.swift index 7a37202915..3e3e869176 100644 --- a/submodules/ChatImportUI/Sources/ChatImportActivityScreen.swift +++ b/submodules/ChatImportUI/Sources/ChatImportActivityScreen.swift @@ -13,6 +13,7 @@ import AnimatedStickerNode import AppBundle import ZIPFoundation import MimeTypes +import ConfettiEffect public final class ChatImportActivityScreen: ViewController { private final class Node: ViewControllerTracingNode { @@ -22,6 +23,7 @@ public final class ChatImportActivityScreen: ViewController { private var presentationData: PresentationData private let animationNode: AnimatedStickerNode + private let doneAnimationNode: AnimatedStickerNode private let radialStatus: RadialStatusNode private let radialCheck: RadialStatusNode private let radialStatusBackground: ASImageNode @@ -38,6 +40,8 @@ public final class ChatImportActivityScreen: ViewController { private let totalBytes: Int private var isDone: Bool = false + private var feedback: HapticFeedback? + init(controller: ChatImportActivityScreen, context: AccountContext, totalBytes: Int) { self.controller = controller self.context = context @@ -46,6 +50,8 @@ public final class ChatImportActivityScreen: ViewController { self.presentationData = self.context.sharedContext.currentPresentationData.with { $0 } self.animationNode = AnimatedStickerNode() + self.doneAnimationNode = AnimatedStickerNode() + self.doneAnimationNode.isHidden = true self.radialStatus = RadialStatusNode(backgroundNodeColor: .clear) self.radialCheck = RadialStatusNode(backgroundNodeColor: .clear) @@ -89,8 +95,19 @@ public final class ChatImportActivityScreen: ViewController { self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 170 * 2, height: 170 * 2, playbackMode: .loop, mode: .direct(cachePathPrefix: nil)) self.animationNode.visibility = true } + if let path = getAppBundle().path(forResource: "HistoryImportDone", ofType: "tgs") { + self.doneAnimationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 170 * 2, height: 170 * 2, playbackMode: .once, mode: .direct(cachePathPrefix: nil)) + self.doneAnimationNode.started = { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.animationNode.isHidden = true + } + self.doneAnimationNode.visibility = false + } self.addSubnode(self.animationNode) + self.addSubnode(self.doneAnimationNode) self.addSubnode(self.radialStatusBackground) self.addSubnode(self.radialStatus) self.addSubnode(self.radialCheck) @@ -112,6 +129,15 @@ public final class ChatImportActivityScreen: ViewController { } } } + + self.animationNode.completed = { [weak self] stopped in + guard let strongSelf = self, stopped else { + return + } + strongSelf.animationNode.visibility = false + strongSelf.doneAnimationNode.visibility = true + strongSelf.doneAnimationNode.isHidden = false + } } @objc private func statusButtonPressed() { @@ -119,6 +145,7 @@ public final class ChatImportActivityScreen: ViewController { } func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) { + let isFirstLayout = self.validLayout == nil self.validLayout = (layout, navigationHeight) //TODO:localize @@ -156,11 +183,14 @@ public final class ChatImportActivityScreen: ViewController { } transition.updateAlpha(node: self.animationNode, alpha: hideIcon ? 0.0 : 1.0) + transition.updateAlpha(node: self.doneAnimationNode, alpha: hideIcon ? 0.0 : 1.0) let contentOriginY = navigationHeight + floor((layout.size.height - contentHeight) / 2.0) self.animationNode.frame = CGRect(origin: CGPoint(x: floor((layout.size.width - iconSize.width) / 2.0), y: contentOriginY), size: iconSize) self.animationNode.updateLayout(size: iconSize) + self.doneAnimationNode.frame = CGRect(origin: CGPoint(x: floor((layout.size.width - iconSize.width) / 2.0), y: contentOriginY), size: iconSize) + self.doneAnimationNode.updateLayout(size: iconSize) self.radialStatus.frame = CGRect(origin: CGPoint(x: floor((layout.size.width - radialStatusSize.width) / 2.0), y: hideIcon ? contentOriginY : (contentOriginY + iconSize.height + maxIconStatusSpacing)), size: radialStatusSize) let checkSize: CGFloat = 130.0 @@ -184,17 +214,22 @@ public final class ChatImportActivityScreen: ViewController { self.statusButtonText.isHidden = !self.isDone self.statusButton.isHidden = !self.isDone self.progressText.isHidden = self.isDone + + if isFirstLayout { + self.updateProgress(totalProgress: self.totalProgress, isDone: self.isDone, animated: false) + } } func updateProgress(totalProgress: CGFloat, isDone: Bool, animated: Bool) { self.totalProgress = totalProgress + let wasDone = self.isDone self.isDone = isDone if let (layout, navigationHeight) = self.validLayout { self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .immediate) - self.radialStatus.transitionToState(.progress(color: self.presentationData.theme.list.itemAccentColor, lineWidth: 6.0, value: max(0.02, self.totalProgress), cancelEnabled: false), animated: animated, synchronous: true, completion: {}) + self.radialStatus.transitionToState(.progress(color: self.presentationData.theme.list.itemAccentColor, lineWidth: 6.0, value: max(0.02, self.totalProgress), cancelEnabled: false, animateRotation: false), animated: animated, synchronous: true, completion: {}) if isDone { - self.radialCheck.transitionToState(.progress(color: .clear, lineWidth: 6.0, value: self.totalProgress, cancelEnabled: false), animated: false, synchronous: true, completion: {}) + self.radialCheck.transitionToState(.progress(color: .clear, lineWidth: 6.0, value: self.totalProgress, cancelEnabled: false, animateRotation: false), animated: false, synchronous: true, completion: {}) self.radialCheck.transitionToState(.check(self.presentationData.theme.list.itemAccentColor), animated: animated, synchronous: true, completion: {}) self.radialStatus.layer.animateScale(from: 1.0, to: 1.05, duration: 0.07, delay: 0.0, timingFunction: CAMediaTimingFunctionName.linear.rawValue, removeOnCompletion: false, additive: false, completion: { [weak self] _ in guard let strongSelf = self else { @@ -216,6 +251,17 @@ public final class ChatImportActivityScreen: ViewController { transition = .immediate } transition.updateAlpha(node: self.radialStatusText, alpha: 0.0) + + if !wasDone { + self.view.addSubview(ConfettiView(frame: self.view.bounds)) + + if self.feedback == nil { + self.feedback = HapticFeedback() + } + self.feedback?.success() + + self.animationNode.stopAtNearestLoop = true + } } } } diff --git a/submodules/ChatListUI/Sources/ChatListSearchMediaNode.swift b/submodules/ChatListUI/Sources/ChatListSearchMediaNode.swift index d96b9dc0de..d74c1208e9 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchMediaNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchMediaNode.swift @@ -234,7 +234,7 @@ private final class VisualMediaItemNode: ASDisplayNode { switch status { case let .Fetching(_, progress): let adjustedProgress = max(progress, 0.027) - statusState = .progress(color: .white, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true) + statusState = .progress(color: .white, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true, animateRotation: true) case .Local: statusState = .none case .Remote: diff --git a/submodules/ChatListUI/Sources/Node/ChatListStatusNode.swift b/submodules/ChatListUI/Sources/Node/ChatListStatusNode.swift index a4f6cb2a2a..9bac5535c2 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListStatusNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListStatusNode.swift @@ -459,14 +459,14 @@ private class ChatListStatusProgressNode: ChatListStatusContentNode { super.init() - self.statusNode.transitionToState(.progress(color: color, lineWidth: 1.0, value: progress, cancelEnabled: false)) + self.statusNode.transitionToState(.progress(color: color, lineWidth: 1.0, value: progress, cancelEnabled: false, animateRotation: true)) self.addSubnode(self.statusNode) } override func updateWithState(_ state: ChatListStatusNodeState, animated: Bool) { if case let .progress(color, progress) = state { - self.statusNode.transitionToState(.progress(color: color, lineWidth: 1.0, value: progress, cancelEnabled: false), animated: animated, completion: {}) + self.statusNode.transitionToState(.progress(color: color, lineWidth: 1.0, value: progress, cancelEnabled: false, animateRotation: true), animated: animated, completion: {}) } } diff --git a/submodules/ChatMessageInteractiveMediaBadge/Sources/ChatMessageInteractiveMediaBadge.swift b/submodules/ChatMessageInteractiveMediaBadge/Sources/ChatMessageInteractiveMediaBadge.swift index 1c8fd02e91..eb0d2b272e 100644 --- a/submodules/ChatMessageInteractiveMediaBadge/Sources/ChatMessageInteractiveMediaBadge.swift +++ b/submodules/ChatMessageInteractiveMediaBadge/Sources/ChatMessageInteractiveMediaBadge.swift @@ -279,7 +279,7 @@ public final class ChatMessageInteractiveMediaBadge: ASDisplayNode { isCompact = true originY = -1.0 - UIScreenPixel case .compactFetching: - state = .progress(color: .white, lineWidth: nil, value: 0.0, cancelEnabled: true) + state = .progress(color: .white, lineWidth: nil, value: 0.0, cancelEnabled: true, animateRotation: true) isCompact = true originY = -1.0 } diff --git a/submodules/ConfettiEffect/BUILD b/submodules/ConfettiEffect/BUILD new file mode 100644 index 0000000000..6103e15e37 --- /dev/null +++ b/submodules/ConfettiEffect/BUILD @@ -0,0 +1,16 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "ConfettiEffect", + module_name = "ConfettiEffect", + srcs = glob([ + "Sources/**/*.swift", + ]), + deps = [ + "//submodules/AsyncDisplayKit:AsyncDisplayKit", + "//submodules/Display:Display", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Sources/ConfettiView.swift b/submodules/ConfettiEffect/Sources/ConfettiView.swift similarity index 98% rename from submodules/TelegramUI/Sources/ConfettiView.swift rename to submodules/ConfettiEffect/Sources/ConfettiView.swift index c17a9f32a4..9eda1bd814 100644 --- a/submodules/TelegramUI/Sources/ConfettiView.swift +++ b/submodules/ConfettiEffect/Sources/ConfettiView.swift @@ -44,13 +44,13 @@ private final class ParticleLayer: CALayer { } } -final class ConfettiView: UIView { +public final class ConfettiView: UIView { private var particles: [ParticleLayer] = [] private var displayLink: ConstantDisplayLinkAnimator? private var localTime: Float = 0.0 - override init(frame: CGRect) { + override public init(frame: CGRect) { super.init(frame: frame) self.isUserInteractionEnabled = false @@ -142,7 +142,7 @@ final class ConfettiView: UIView { self.displayLink?.isPaused = false } - required init?(coder: NSCoder) { + required public init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/submodules/GalleryUI/Sources/Items/ChatAnimationGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatAnimationGalleryItem.swift index 7d67b546ac..90c12dedaa 100644 --- a/submodules/GalleryUI/Sources/Items/ChatAnimationGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatAnimationGalleryItem.swift @@ -214,10 +214,10 @@ final class ChatAnimationGalleryItemNode: ZoomableContentGalleryItemNode { strongSelf.statusNode.alpha = 1.0 strongSelf.statusNodeContainer.isUserInteractionEnabled = true let adjustedProgress = max(progress, 0.027) - strongSelf.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true), completion: {}) + strongSelf.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true, animateRotation: true), completion: {}) case .Local: if let previousStatus = previousStatus, case .Fetching = previousStatus { - strongSelf.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: 1.0, cancelEnabled: true), completion: { + strongSelf.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: 1.0, cancelEnabled: true, animateRotation: true), completion: { if let strongSelf = self { strongSelf.statusNode.alpha = 0.0 strongSelf.statusNodeContainer.isUserInteractionEnabled = false diff --git a/submodules/GalleryUI/Sources/Items/ChatDocumentGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatDocumentGalleryItem.swift index e1fa63f3f6..1bb838437d 100644 --- a/submodules/GalleryUI/Sources/Items/ChatDocumentGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatDocumentGalleryItem.swift @@ -203,10 +203,10 @@ class ChatDocumentGalleryItemNode: ZoomableContentGalleryItemNode, WKNavigationD strongSelf.statusNode.alpha = 1.0 strongSelf.statusNodeContainer.isUserInteractionEnabled = true let adjustedProgress = max(progress, 0.027) - strongSelf.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true), completion: {}) + strongSelf.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true, animateRotation: true), completion: {}) case .Local: if let previousStatus = previousStatus, case .Fetching = previousStatus { - strongSelf.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: 1.0, cancelEnabled: true), completion: { + strongSelf.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: 1.0, cancelEnabled: true, animateRotation: true), completion: { if let strongSelf = self { strongSelf.statusNode.alpha = 0.0 strongSelf.statusNodeContainer.isUserInteractionEnabled = false diff --git a/submodules/GalleryUI/Sources/Items/ChatExternalFileGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatExternalFileGalleryItem.swift index 1b0964ad77..620eefdffe 100644 --- a/submodules/GalleryUI/Sources/Items/ChatExternalFileGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatExternalFileGalleryItem.swift @@ -201,10 +201,10 @@ class ChatExternalFileGalleryItemNode: GalleryItemNode { strongSelf.statusNode.alpha = 1.0 strongSelf.statusNodeContainer.isUserInteractionEnabled = true let adjustedProgress = max(progress, 0.027) - strongSelf.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true), completion: {}) + strongSelf.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true, animateRotation: true), completion: {}) case .Local: if let previousStatus = previousStatus, case .Fetching = previousStatus { - strongSelf.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: 1.0, cancelEnabled: true), completion: { + strongSelf.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: 1.0, cancelEnabled: true, animateRotation: true), completion: { if let strongSelf = self { strongSelf.statusNode.alpha = 0.0 strongSelf.statusNodeContainer.isUserInteractionEnabled = false diff --git a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift index 5dc494a86f..8e3bab9eb4 100644 --- a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift @@ -426,10 +426,10 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode { strongSelf.statusNode.alpha = 1.0 strongSelf.statusNodeContainer.isUserInteractionEnabled = true let adjustedProgress = max(progress, 0.027) - strongSelf.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true), completion: {}) + strongSelf.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true, animateRotation: true), completion: {}) case .Local: if let previousStatus = previousStatus, case .Fetching = previousStatus { - strongSelf.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: 1.0, cancelEnabled: true), completion: { + strongSelf.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: 1.0, cancelEnabled: true, animateRotation: true), completion: { if let strongSelf = self { strongSelf.statusNode.alpha = 0.0 strongSelf.statusNodeContainer.isUserInteractionEnabled = false diff --git a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift index c2d2ee0b14..310b5bcb7f 100644 --- a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift @@ -723,7 +723,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { var fetching = false if initialBuffering { if displayProgress { - strongSelf.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: nil, cancelEnabled: false), animated: false, completion: {}) + strongSelf.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: nil, cancelEnabled: false, animateRotation: true), animated: false, completion: {}) } else { strongSelf.statusNode.transitionToState(.none, animated: false, completion: {}) } @@ -740,7 +740,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { fetching = true isPaused = true } - state = .progress(color: .white, lineWidth: nil, value: CGFloat(progress), cancelEnabled: true) + state = .progress(color: .white, lineWidth: nil, value: CGFloat(progress), cancelEnabled: true, animateRotation: true) default: break } diff --git a/submodules/InstantPageUI/Sources/InstantPageImageNode.swift b/submodules/InstantPageUI/Sources/InstantPageImageNode.swift index 9e73a273e2..73e634010e 100644 --- a/submodules/InstantPageUI/Sources/InstantPageImageNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPageImageNode.swift @@ -176,7 +176,7 @@ final class InstantPageImageNode: ASDisplayNode, InstantPageNode { switch fetchStatus { case let .Fetching(_, progress): let adjustedProgress = max(progress, 0.027) - state = .progress(color: .white, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true) + state = .progress(color: .white, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true, animateRotation: true) case .Remote: state = .download(.white) default: diff --git a/submodules/InstantPageUI/Sources/InstantPagePlayableVideoNode.swift b/submodules/InstantPageUI/Sources/InstantPagePlayableVideoNode.swift index 12150b846d..b9f1dc66d5 100644 --- a/submodules/InstantPageUI/Sources/InstantPagePlayableVideoNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPagePlayableVideoNode.swift @@ -119,7 +119,7 @@ final class InstantPagePlayableVideoNode: ASDisplayNode, InstantPageNode, Galler switch fetchStatus { case let .Fetching(_, progress): let adjustedProgress = max(progress, 0.027) - state = .progress(color: .white, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true) + state = .progress(color: .white, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true, animateRotation: true) case .Remote: state = .download(.white) default: diff --git a/submodules/LegacyDataImport/Sources/LegacyDataImportSplash.swift b/submodules/LegacyDataImport/Sources/LegacyDataImportSplash.swift index e85659b19c..f121307d0e 100644 --- a/submodules/LegacyDataImport/Sources/LegacyDataImportSplash.swift +++ b/submodules/LegacyDataImport/Sources/LegacyDataImportSplash.swift @@ -28,7 +28,7 @@ private final class LegacyDataImportSplashImpl: WindowCoveringView, LegacyDataIm self.updateLayout(size) } } - self.progressNode.transitionToState(.progress(color: self.theme?.list.itemAccentColor ?? UIColor(rgb: 0x007ee5), lineWidth: 2.0, value: CGFloat(max(0.025, self.progress.1)), cancelEnabled: false), animated: false, completion: {}) + self.progressNode.transitionToState(.progress(color: self.theme?.list.itemAccentColor ?? UIColor(rgb: 0x007ee5), lineWidth: 2.0, value: CGFloat(max(0.025, self.progress.1)), cancelEnabled: false, animateRotation: true), animated: false, completion: {}) } } diff --git a/submodules/PassportUI/Sources/SecureIdValueFormFileItem.swift b/submodules/PassportUI/Sources/SecureIdValueFormFileItem.swift index 4759c97acd..890c145641 100644 --- a/submodules/PassportUI/Sources/SecureIdValueFormFileItem.swift +++ b/submodules/PassportUI/Sources/SecureIdValueFormFileItem.swift @@ -138,7 +138,7 @@ final class SecureIdValueFormFileItemNode: FormEditableBlockItemNode Void)? - init(color: UIColor, lineWidth: CGFloat?, displayCancel: Bool) { + init(color: UIColor, lineWidth: CGFloat?, displayCancel: Bool, animateRotation: Bool) { self.color = color self.displayCancel = displayCancel + self.animateRotation = animateRotation - self.spinnerNode = RadialProgressContentSpinnerNode(color: color, lineWidth: lineWidth) + self.spinnerNode = RadialProgressContentSpinnerNode(color: color, lineWidth: lineWidth, animateRotation: animateRotation) self.cancelNode = RadialProgressContentCancelNode(color: color, displayCancel: displayCancel) super.init() diff --git a/submodules/RadialStatusNode/Sources/RadialStatusNode.swift b/submodules/RadialStatusNode/Sources/RadialStatusNode.swift index 6ac5eef755..9bfbcbb715 100644 --- a/submodules/RadialStatusNode/Sources/RadialStatusNode.swift +++ b/submodules/RadialStatusNode/Sources/RadialStatusNode.swift @@ -7,7 +7,7 @@ public enum RadialStatusNodeState: Equatable { case download(UIColor) case play(UIColor) case pause(UIColor) - case progress(color: UIColor, lineWidth: CGFloat?, value: CGFloat?, cancelEnabled: Bool) + case progress(color: UIColor, lineWidth: CGFloat?, value: CGFloat?, cancelEnabled: Bool, animateRotation: Bool) case cloudProgress(color: UIColor, strokeBackgroundColor: UIColor, lineWidth: CGFloat, value: CGFloat?) case check(UIColor) case customIcon(UIImage) @@ -39,8 +39,8 @@ public enum RadialStatusNodeState: Equatable { } else { return false } - case let .progress(lhsColor, lhsLineWidth, lhsValue, lhsCancelEnabled): - if case let .progress(rhsColor, rhsLineWidth, rhsValue, rhsCancelEnabled) = rhs, lhsColor.isEqual(rhsColor), lhsValue == rhsValue, lhsLineWidth == rhsLineWidth, lhsCancelEnabled == rhsCancelEnabled { + case let .progress(lhsColor, lhsLineWidth, lhsValue, lhsCancelEnabled, lhsAnimateRotation): + if case let .progress(rhsColor, rhsLineWidth, rhsValue, rhsCancelEnabled, rhsAnimateRotation) = rhs, lhsColor.isEqual(rhsColor), lhsValue == rhsValue, lhsLineWidth == rhsLineWidth, lhsCancelEnabled == rhsCancelEnabled, lhsAnimateRotation == rhsAnimateRotation { return true } else { return false @@ -98,8 +98,8 @@ public enum RadialStatusNodeState: Equatable { } else { return false } - case let .progress(lhsColor, lhsLineWidth, lhsValue, lhsCancelEnabled): - if case let .progress(rhsColor, rhsLineWidth, rhsValue, rhsCancelEnabled) = rhs, lhsColor.isEqual(rhsColor), lhsValue == rhsValue, lhsLineWidth == rhsLineWidth, lhsCancelEnabled == rhsCancelEnabled { + case let .progress(lhsColor, lhsLineWidth, lhsValue, lhsCancelEnabled, lhsAnimateRotation): + if case let .progress(rhsColor, rhsLineWidth, rhsValue, rhsCancelEnabled, rhsAnimateRotation) = rhs, lhsColor.isEqual(rhsColor), lhsValue == rhsValue, lhsLineWidth == rhsLineWidth, lhsCancelEnabled == rhsCancelEnabled, lhsAnimateRotation == rhsAnimateRotation { return true } else { return false @@ -154,15 +154,15 @@ public enum RadialStatusNodeState: Equatable { return RadialStatusIconContentNode(icon: .custom(image), synchronous: synchronous) case let .check(color): return RadialCheckContentNode(color: color) - case let .progress(color, lineWidth, value, cancelEnabled): - if let current = current as? RadialProgressContentNode, current.displayCancel == cancelEnabled { + case let .progress(color, lineWidth, value, cancelEnabled, animateRotation): + if let current = current as? RadialProgressContentNode, current.displayCancel == cancelEnabled, current.animateRotation == animateRotation { if !current.color.isEqual(color) { current.color = color } current.progress = value return current } else { - let node = RadialProgressContentNode(color: color, lineWidth: lineWidth, displayCancel: cancelEnabled) + let node = RadialProgressContentNode(color: color, lineWidth: lineWidth, displayCancel: cancelEnabled, animateRotation: animateRotation) node.progress = value return node } diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift index 97bcba4e33..7e93b9ff75 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift @@ -501,12 +501,12 @@ final class WallpaperGalleryItemNode: GalleryItemNode { switch status { case let .Fetching(_, progress): let adjustedProgress = max(progress, 0.027) - state = .progress(color: statusForegroundColor, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: false) + state = .progress(color: statusForegroundColor, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: false, animateRotation: true) case .Local: state = .none local = true case .Remote: - state = .progress(color: statusForegroundColor, lineWidth: nil, value: 0.027, cancelEnabled: false) + state = .progress(color: statusForegroundColor, lineWidth: nil, value: 0.027, cancelEnabled: false, animateRotation: true) } strongSelf.statusNode.transitionToState(state, completion: {}) diff --git a/submodules/ShareController/Sources/ShareLoadingContainerNode.swift b/submodules/ShareController/Sources/ShareLoadingContainerNode.swift index 3fd7bdff58..fcfeea1fd8 100644 --- a/submodules/ShareController/Sources/ShareLoadingContainerNode.swift +++ b/submodules/ShareController/Sources/ShareLoadingContainerNode.swift @@ -31,11 +31,11 @@ public final class ShareLoadingContainerNode: ASDisplayNode, ShareContentContain case let .progress(value): self.activityIndicator.isHidden = true self.statusNode.isHidden = false - self.statusNode.transitionToState(.progress(color: self.theme.actionSheet.controlAccentColor, lineWidth: 2.0, value: max(0.12, CGFloat(value)), cancelEnabled: false), completion: {}) + self.statusNode.transitionToState(.progress(color: self.theme.actionSheet.controlAccentColor, lineWidth: 2.0, value: max(0.12, CGFloat(value)), cancelEnabled: false, animateRotation: true), completion: {}) case .done: self.activityIndicator.isHidden = true self.statusNode.isHidden = false - self.statusNode.transitionToState(.progress(color: self.theme.actionSheet.controlAccentColor, lineWidth: 2.0, value: 1.0, cancelEnabled: false), completion: {}) + self.statusNode.transitionToState(.progress(color: self.theme.actionSheet.controlAccentColor, lineWidth: 2.0, value: 1.0, cancelEnabled: false, animateRotation: true), completion: {}) self.doneStatusNode.transitionToState(.check(self.theme.actionSheet.controlAccentColor), completion: {}) } } @@ -52,7 +52,7 @@ public final class ShareLoadingContainerNode: ASDisplayNode, ShareContentContain self.addSubnode(self.activityIndicator) self.addSubnode(self.statusNode) self.addSubnode(self.doneStatusNode) - self.doneStatusNode.transitionToState(.progress(color: self.theme.actionSheet.controlAccentColor, lineWidth: 2.0, value: 0.0, cancelEnabled: false), completion: {}) + self.doneStatusNode.transitionToState(.progress(color: self.theme.actionSheet.controlAccentColor, lineWidth: 2.0, value: 0.0, cancelEnabled: false, animateRotation: true), completion: {}) } public func activate() { diff --git a/submodules/TelegramUI/BUILD b/submodules/TelegramUI/BUILD index b866dd7f22..7cb9c6e72a 100644 --- a/submodules/TelegramUI/BUILD +++ b/submodules/TelegramUI/BUILD @@ -217,6 +217,7 @@ swift_library( "//submodules/ChatImportUI:ChatImportUI", "//submodules/ChatHistoryImportTasks:ChatHistoryImportTasks", "//submodules/DatePickerNode:DatePickerNode", + "//submodules/ConfettiEffect:ConfettiEffect", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Resources/Animations/HistoryImport.tgs b/submodules/TelegramUI/Resources/Animations/HistoryImport.tgs index e6988086789cb5b09deb80a63a8f7831a4c2b9d0..13b6d7d7364bcc71bcc79cd49d85b93a33b21fb7 100644 GIT binary patch literal 18660 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, ignoreRepeated: true) @@ -462,10 +463,10 @@ public class ShareRootControllerImpl { if let mainFile = mainFile, let mainFileText = try? String(contentsOf: URL(fileURLWithPath: mainFile.path)) { let mainFileHeader: String - if mainFileText.count < 1000 { + if mainFileText.count < 2000 { mainFileHeader = mainFileText } else { - mainFileHeader = String(mainFileText[mainFileText.startIndex ..< mainFileText.index(mainFileText.startIndex, offsetBy: 1000)]) + mainFileHeader = String(mainFileText[mainFileText.startIndex ..< mainFileText.index(mainFileText.startIndex, offsetBy: 2000)]) } final class TempController: ViewController { @@ -476,11 +477,16 @@ public class ShareRootControllerImpl { } } + private let activityIndicator: ActivityIndicator + init(context: AccountContext) { let presentationData = context.sharedContext.currentPresentationData.with { $0 } + self.activityIndicator = ActivityIndicator(type: .custom(presentationData.theme.list.itemAccentColor, 22.0, 1.0, false)) + super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: presentationData)) + //TODO:localize self.title = "Import Chat" self.navigationItem.setLeftBarButton(UIBarButtonItem(title: presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed)), animated: false) } @@ -492,6 +498,19 @@ public class ShareRootControllerImpl { @objc private func cancelPressed() { //self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) } + + override func displayNodeDidLoad() { + super.displayNodeDidLoad() + + self.displayNode.addSubnode(self.activityIndicator) + } + + override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { + super.containerLayoutUpdated(layout, transition: transition) + + let indicatorSize = self.activityIndicator.measure(CGSize(width: 100.0, height: 100.0)) + transition.updateFrame(node: self.activityIndicator, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - indicatorSize.width) / 2.0), y: floor((layout.size.height - indicatorSize.height - 50.0) / 2.0)), size: indicatorSize)) + } } let presentationData = internalContext.sharedContext.currentPresentationData.with { $0 } @@ -620,10 +639,8 @@ public class ShareRootControllerImpl { } navigationController.viewControllers = [controller] - strongSelf.mainWindow?.present(navigationController, on: .root) case let .privateChat(title): let presentationData = internalContext.sharedContext.currentPresentationData.with { $0 } - let navigationController = NavigationController(mode: .single, theme: NavigationControllerTheme(presentationTheme: presentationData.theme)) //TODO:localize var attemptSelectionImpl: ((Peer) -> Void)? @@ -684,7 +701,6 @@ public class ShareRootControllerImpl { } navigationController.viewControllers = [controller] - strongSelf.mainWindow?.present(navigationController, on: .root) case let .unknown(peerTitle): //TODO:localize var attemptSelectionImpl: ((Peer) -> Void)? @@ -839,7 +855,6 @@ public class ShareRootControllerImpl { } navigationController.viewControllers = [controller] - strongSelf.mainWindow?.present(navigationController, on: .root) } }, error: { _ in beginShare() diff --git a/submodules/TelegramUI/Sources/VerticalListContextResultsChatInputPanelItem.swift b/submodules/TelegramUI/Sources/VerticalListContextResultsChatInputPanelItem.swift index 74b62a277a..3a3b06298e 100644 --- a/submodules/TelegramUI/Sources/VerticalListContextResultsChatInputPanelItem.swift +++ b/submodules/TelegramUI/Sources/VerticalListContextResultsChatInputPanelItem.swift @@ -364,7 +364,7 @@ final class VerticalListContextResultsChatInputPanelItemNode: ListViewItemNode { switch status { case let .Fetching(_, progress): - state = RadialStatusNodeState.progress(color: statusForegroundColor, lineWidth: nil, value: CGFloat(max(progress, 0.2)), cancelEnabled: false) + state = RadialStatusNodeState.progress(color: statusForegroundColor, lineWidth: nil, value: CGFloat(max(progress, 0.2)), cancelEnabled: false, animateRotation: true) case .Remote: state = .download(statusForegroundColor) case .Local: diff --git a/submodules/TelegramUniversalVideoContent/Sources/OverlayVideoDecoration.swift b/submodules/TelegramUniversalVideoContent/Sources/OverlayVideoDecoration.swift index 4cacd66bc7..e14bfd80ad 100644 --- a/submodules/TelegramUniversalVideoContent/Sources/OverlayVideoDecoration.swift +++ b/submodules/TelegramUniversalVideoContent/Sources/OverlayVideoDecoration.swift @@ -188,7 +188,7 @@ final class OverlayVideoDecoration: UniversalVideoDecoration { return } if let status = status, case .buffering = status.status { - strongSelf.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: nil, cancelEnabled: false)) + strongSelf.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: nil, cancelEnabled: false, animateRotation: true)) } else { strongSelf.statusNode.transitionToState(.none) } diff --git a/submodules/WebSearchUI/Sources/WebSearchVideoGalleryItem.swift b/submodules/WebSearchUI/Sources/WebSearchVideoGalleryItem.swift index 739053cb79..50482a3db7 100644 --- a/submodules/WebSearchUI/Sources/WebSearchVideoGalleryItem.swift +++ b/submodules/WebSearchUI/Sources/WebSearchVideoGalleryItem.swift @@ -227,7 +227,7 @@ final class WebSearchVideoGalleryItemNode: ZoomableContentGalleryItemNode { var fetching = false if initialBuffering { - strongSelf.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: nil, cancelEnabled: false), animated: false, completion: {}) + strongSelf.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: nil, cancelEnabled: false, animateRotation: true), animated: false, completion: {}) } else { var state: RadialStatusNodeState = .none @@ -237,7 +237,7 @@ final class WebSearchVideoGalleryItemNode: ZoomableContentGalleryItemNode { case let .Fetching(_, progress): fetching = true isPaused = true - state = .progress(color: .white, lineWidth: nil, value: CGFloat(max(0.027, progress)), cancelEnabled: false) + state = .progress(color: .white, lineWidth: nil, value: CGFloat(max(0.027, progress)), cancelEnabled: false, animateRotation: true) default: break }