From 0b5be7edad025801a98a92d5d21a810bf587155e Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 27 Dec 2019 20:57:06 +0400 Subject: [PATCH] Display progress when adding sticker packs --- .../ChatMediaInputTrendingPane.swift | 39 ++++++- .../StickerPaneSearchContentNode.swift | 110 ++++++++++++++---- .../StickerPaneSearchGlobaltem.swift | 15 ++- 3 files changed, 139 insertions(+), 25 deletions(-) diff --git a/submodules/TelegramUI/TelegramUI/ChatMediaInputTrendingPane.swift b/submodules/TelegramUI/TelegramUI/ChatMediaInputTrendingPane.swift index 4a4a20675f..ab0785c6be 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMediaInputTrendingPane.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMediaInputTrendingPane.swift @@ -145,6 +145,8 @@ final class ChatMediaInputTrendingPane: ChatMediaInputPane { var scrollingInitiated: (() -> Void)? + private let installDisposable = MetaDisposable() + init(context: AccountContext, controllerInteraction: ChatControllerInteraction, getItemIsPreviewed: @escaping (StickerPackItem) -> Bool) { self.context = context self.controllerInteraction = controllerInteraction @@ -163,6 +165,7 @@ final class ChatMediaInputTrendingPane: ChatMediaInputPane { deinit { self.disposable?.dispose() + self.installDisposable.dispose() } func activate() { @@ -174,7 +177,7 @@ final class ChatMediaInputTrendingPane: ChatMediaInputPane { let interaction = TrendingPaneInteraction(installPack: { [weak self] info in if let strongSelf = self, let info = info as? StickerPackCollectionInfo { let account = strongSelf.context.account - let _ = (loadedStickerPack(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false) + var installSignal = loadedStickerPack(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false) |> mapToSignal { result -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), NoError> in switch result { case let .result(info, items, installed): @@ -200,7 +203,37 @@ final class ChatMediaInputTrendingPane: ChatMediaInputPane { } return .complete() } - |> deliverOnMainQueue).start(next: { info, items in + |> deliverOnMainQueue + + let context = strongSelf.context + var cancelImpl: (() -> Void)? + let progressSignal = Signal { subscriber in + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: { + cancelImpl?() + })) + self?.controllerInteraction.presentController(controller, nil) + return ActionDisposable { [weak controller] in + Queue.mainQueue().async() { + controller?.dismiss() + } + } + } + |> runOn(Queue.mainQueue()) + |> delay(0.12, queue: Queue.mainQueue()) + let progressDisposable = progressSignal.start() + + installSignal = installSignal + |> afterDisposed { + Queue.mainQueue().async { + progressDisposable.dispose() + } + } + cancelImpl = { + self?.installDisposable.set(nil) + } + + strongSelf.installDisposable.set(installSignal.start(next: { info, items in guard let strongSelf = self else { return } @@ -219,7 +252,7 @@ final class ChatMediaInputTrendingPane: ChatMediaInputPane { strongSelf.controllerInteraction.navigationController()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).0, undo: false, info: info, topItem: items.first, account: strongSelf.context.account), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { _ in return true })) - }) + })) } }, openPack: { [weak self] info in if let strongSelf = self, let info = info as? StickerPackCollectionInfo { diff --git a/submodules/TelegramUI/TelegramUI/StickerPaneSearchContentNode.swift b/submodules/TelegramUI/TelegramUI/StickerPaneSearchContentNode.swift index 99d8a8555e..b0693e8f18 100644 --- a/submodules/TelegramUI/TelegramUI/StickerPaneSearchContentNode.swift +++ b/submodules/TelegramUI/TelegramUI/StickerPaneSearchContentNode.swift @@ -7,20 +7,23 @@ import Postbox import TelegramCore import SyncCore import TelegramPresentationData +import PresentationDataUtils import LegacyComponents import MergeLists import AccountContext import StickerPackPreviewUI import Emoji import AppBundle +import OverlayStatusController +import UndoUI final class StickerPaneSearchInteraction { let open: (StickerPackCollectionInfo) -> Void - let install: (StickerPackCollectionInfo) -> Void + let install: (StickerPackCollectionInfo, [ItemCollectionItem]) -> Void let sendSticker: (FileMediaReference, ASDisplayNode, CGRect) -> Void let getItemIsPreviewed: (StickerPackItem) -> Bool - init(open: @escaping (StickerPackCollectionInfo) -> Void, install: @escaping (StickerPackCollectionInfo) -> Void, sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Void, getItemIsPreviewed: @escaping (StickerPackItem) -> Bool) { + init(open: @escaping (StickerPackCollectionInfo) -> Void, install: @escaping (StickerPackCollectionInfo, [ItemCollectionItem]) -> Void, sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Void, getItemIsPreviewed: @escaping (StickerPackItem) -> Bool) { self.open = open self.install = install self.sendSticker = sendSticker @@ -104,7 +107,7 @@ private enum StickerSearchEntry: Identifiable, Comparable { return StickerPaneSearchGlobalItem(account: account, theme: theme, strings: strings, info: info, topItems: topItems, grid: false, topSeparator: topSeparator, installed: installed, unread: false, open: { interaction.open(info) }, install: { - interaction.install(info) + interaction.install(info, topItems) }, getItemIsPreviewed: { item in return interaction.getItemIsPreviewed(item) }) @@ -171,6 +174,8 @@ final class StickerPaneSearchContentNode: ASDisplayNode, PaneSearchContentNode { var deactivateSearchBar: (() -> Void)? var updateActivity: ((Bool) -> Void)? + private let installDisposable = MetaDisposable() + init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, controllerInteraction: ChatControllerInteraction, inputNodeInteraction: ChatMediaInputNodeInteraction) { self.context = context self.controllerInteraction = controllerInteraction @@ -227,25 +232,87 @@ final class StickerPaneSearchContentNode: ASDisplayNode, PaneSearchContentNode { }) strongSelf.controllerInteraction.presentController(controller, nil) } - }, install: { [weak self] info in - if let strongSelf = self { - let _ = (loadedStickerPack(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false) - |> mapToSignal { result -> Signal in - switch result { - case let .result(info, items, installed): - if installed { - return .complete() - } else { - return addStickerPackInteractively(postbox: strongSelf.context.account.postbox, info: info, items: items) - } - case .fetching: - break - case .none: - break - } - return .complete() - }).start() + }, install: { [weak self] info, items in + guard let strongSelf = self else { + return } + let account = strongSelf.context.account + var installSignal = loadedStickerPack(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false) + |> mapToSignal { result -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), NoError> in + switch result { + case let .result(info, items, installed): + if installed { + return .complete() + } else { + return preloadedStickerPackThumbnail(account: account, info: info, items: items) + |> filter { $0 } + |> ignoreValues + |> then( + addStickerPackInteractively(postbox: strongSelf.context.account.postbox, info: info, items: items) + |> ignoreValues + ) + |> mapToSignal { _ -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), NoError> in + return .complete() + } + |> then(.single((info, items))) + } + case .fetching: + break + case .none: + break + } + return .complete() + } + |> deliverOnMainQueue + + let context = strongSelf.context + var cancelImpl: (() -> Void)? + let progressSignal = Signal { subscriber in + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: { + cancelImpl?() + })) + self?.controllerInteraction.presentController(controller, nil) + return ActionDisposable { [weak controller] in + Queue.mainQueue().async() { + controller?.dismiss() + } + } + } + |> runOn(Queue.mainQueue()) + |> delay(0.12, queue: Queue.mainQueue()) + let progressDisposable = progressSignal.start() + + installSignal = installSignal + |> afterDisposed { + Queue.mainQueue().async { + progressDisposable.dispose() + } + } + cancelImpl = { + self?.installDisposable.set(nil) + } + + strongSelf.installDisposable.set(installSignal.start(next: { info, items in + guard let strongSelf = self else { + return + } + + var animateInAsReplacement = false + if let navigationController = strongSelf.controllerInteraction.navigationController() { + for controller in navigationController.overlayControllers { + if let controller = controller as? UndoOverlayController { + controller.dismissWithCommitActionAndReplacementAnimation() + animateInAsReplacement = true + } + } + } + + let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } + strongSelf.controllerInteraction.navigationController()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).0, undo: false, info: info, topItem: items.first, account: strongSelf.context.account), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { _ in + return true + })) + })) }, sendSticker: { [weak self] file, sourceNode, sourceRect in if let strongSelf = self { let _ = strongSelf.controllerInteraction.sendSticker(file, false, sourceNode, sourceRect) @@ -262,6 +329,7 @@ final class StickerPaneSearchContentNode: ASDisplayNode, PaneSearchContentNode { deinit { self.searchDisposable.dispose() + self.installDisposable.dispose() } func updateText(_ text: String, languageCode: String?) { diff --git a/submodules/TelegramUI/TelegramUI/StickerPaneSearchGlobaltem.swift b/submodules/TelegramUI/TelegramUI/StickerPaneSearchGlobaltem.swift index 94c2669d6d..b6041c4349 100644 --- a/submodules/TelegramUI/TelegramUI/StickerPaneSearchGlobaltem.swift +++ b/submodules/TelegramUI/TelegramUI/StickerPaneSearchGlobaltem.swift @@ -7,6 +7,7 @@ import SyncCore import SwiftSignalKit import Postbox import TelegramPresentationData +import StickerPackPreviewUI final class StickerPaneSearchGlobalSection: GridSection { let height: CGFloat = 0.0 @@ -41,6 +42,7 @@ final class StickerPaneSearchGlobalItem: GridItem { let grid: Bool let topSeparator: Bool let installed: Bool + let installing: Bool let unread: Bool let open: () -> Void let install: () -> Void @@ -51,7 +53,7 @@ final class StickerPaneSearchGlobalItem: GridItem { return self.grid ? nil : (128.0 + (self.topSeparator ? 12.0 : 0.0)) } - init(account: Account, theme: PresentationTheme, strings: PresentationStrings, info: StickerPackCollectionInfo, topItems: [StickerPackItem], grid: Bool, topSeparator: Bool, installed: Bool, unread: Bool, open: @escaping () -> Void, install: @escaping () -> Void, getItemIsPreviewed: @escaping (StickerPackItem) -> Bool) { + init(account: Account, theme: PresentationTheme, strings: PresentationStrings, info: StickerPackCollectionInfo, topItems: [StickerPackItem], grid: Bool, topSeparator: Bool, installed: Bool, installing: Bool = false, unread: Bool, open: @escaping () -> Void, install: @escaping () -> Void, getItemIsPreviewed: @escaping (StickerPackItem) -> Bool) { self.account = account self.theme = theme self.strings = strings @@ -60,6 +62,7 @@ final class StickerPaneSearchGlobalItem: GridItem { self.grid = grid self.topSeparator = topSeparator self.installed = installed + self.installing = installing self.unread = unread self.open = open self.install = install @@ -98,6 +101,9 @@ class StickerPaneSearchGlobalItemNode: GridItemNode { private var item: StickerPaneSearchGlobalItem? private var appliedItem: StickerPaneSearchGlobalItem? private let preloadDisposable = MetaDisposable() + private let preloadedStickerPackThumbnailDisposable = MetaDisposable() + + private var preloadedThumbnail = false override var isVisibleInGrid: Bool { didSet { @@ -105,6 +111,12 @@ class StickerPaneSearchGlobalItemNode: GridItemNode { for node in self.itemNodes { node.visibility = self.isVisibleInGrid } + + if let item = self.item, self.isVisibleInGrid, !self.preloadedThumbnail { + self.preloadedThumbnail = true + + self.preloadedStickerPackThumbnailDisposable.set(preloadedStickerPackThumbnail(account: item.account, info: item.info, items: item.topItems).start()) + } } } } @@ -173,6 +185,7 @@ class StickerPaneSearchGlobalItemNode: GridItemNode { deinit { self.preloadDisposable.dispose() + self.preloadedStickerPackThumbnailDisposable.dispose() } override func didLoad() {