Display a toast after sticker list changes

This commit is contained in:
Ali 2019-12-24 13:52:38 +04:00
parent 7dbf4cd90d
commit 4ed2893065
10 changed files with 347 additions and 79 deletions

View File

@ -1198,11 +1198,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
let text = strongSelf.presentationData.strings.ChatList_DeletedChats(Int32(peerIds.count)) let text = strongSelf.presentationData.strings.ChatList_DeletedChats(Int32(peerIds.count))
strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(text: text), elevatedLayout: false, animateInAsReplacement: true, action: { shouldCommit in strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(text: text), elevatedLayout: false, animateInAsReplacement: true, action: { value in
guard let strongSelf = self else { guard let strongSelf = self else {
return return false
} }
if shouldCommit { if value == .commit {
let context = strongSelf.context let context = strongSelf.context
let presentationData = strongSelf.presentationData let presentationData = strongSelf.presentationData
let progressSignal = Signal<Never, NoError> { subscriber in let progressSignal = Signal<Never, NoError> { subscriber in
@ -1230,7 +1230,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
} }
let _ = (signal let _ = (signal
|> deliverOnMainQueue).start() |> deliverOnMainQueue).start()
} else { return true
} else if value == .undo {
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerIds.first!) strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerIds.first!)
strongSelf.chatListDisplayNode.chatListNode.updateState({ state in strongSelf.chatListDisplayNode.chatListNode.updateState({ state in
var state = state var state = state
@ -1240,7 +1241,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
return state return state
}) })
self?.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerIds.first!) self?.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerIds.first!)
return true
} }
return false
}), in: .current) }), in: .current)
strongSelf.donePressed() strongSelf.donePressed()
@ -1310,11 +1313,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
}) })
if value { if value {
strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .hidArchive(title: strongSelf.presentationData.strings.ChatList_UndoArchiveHiddenTitle, text: strongSelf.presentationData.strings.ChatList_UndoArchiveHiddenText, undo: false), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] shouldCommit in strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .hidArchive(title: strongSelf.presentationData.strings.ChatList_UndoArchiveHiddenTitle, text: strongSelf.presentationData.strings.ChatList_UndoArchiveHiddenText, undo: false), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] value in
guard let strongSelf = self else { guard let strongSelf = self else {
return return false
} }
if !shouldCommit { if value == .undo {
let _ = (strongSelf.context.account.postbox.transaction { transaction -> Bool in let _ = (strongSelf.context.account.postbox.transaction { transaction -> Bool in
var updatedValue = false var updatedValue = false
updateChatArchiveSettings(transaction: transaction, { settings in updateChatArchiveSettings(transaction: transaction, { settings in
@ -1325,10 +1328,12 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
}) })
return updatedValue return updatedValue
}).start() }).start()
return true
} }
return false
}), in: .current) }), in: .current)
} else { } else {
strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .revealedArchive(title: strongSelf.presentationData.strings.ChatList_UndoArchiveRevealedTitle, text: strongSelf.presentationData.strings.ChatList_UndoArchiveRevealedText, undo: false), elevatedLayout: false, animateInAsReplacement: true, action: { _ in strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .revealedArchive(title: strongSelf.presentationData.strings.ChatList_UndoArchiveRevealedTitle, text: strongSelf.presentationData.strings.ChatList_UndoArchiveRevealedText, undo: false), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false
}), in: .current) }), in: .current)
} }
}) })
@ -1422,11 +1427,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
return true return true
}) })
strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(text: strongSelf.presentationData.strings.Undo_ChatCleared), elevatedLayout: false, animateInAsReplacement: true, action: { shouldCommit in strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(text: strongSelf.presentationData.strings.Undo_ChatCleared), elevatedLayout: false, animateInAsReplacement: true, action: { value in
guard let strongSelf = self else { guard let strongSelf = self else {
return return false
} }
if shouldCommit { if value == .commit {
let _ = clearHistoryInteractively(postbox: strongSelf.context.account.postbox, peerId: peerId, type: type).start(completed: { let _ = clearHistoryInteractively(postbox: strongSelf.context.account.postbox, peerId: peerId, type: type).start(completed: {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
@ -1437,13 +1442,16 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
return state return state
}) })
}) })
} else { return true
} else if value == .undo {
strongSelf.chatListDisplayNode.chatListNode.updateState({ state in strongSelf.chatListDisplayNode.chatListNode.updateState({ state in
var state = state var state = state
state.pendingClearHistoryPeerIds.remove(peer.peerId) state.pendingClearHistoryPeerIds.remove(peer.peerId)
return state return state
}) })
return true
} }
return false
}), in: .current) }), in: .current)
} }
@ -1618,11 +1626,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
deleteSendMessageIntents(peerId: peerId) deleteSendMessageIntents(peerId: peerId)
} }
let action: (Bool) -> Void = { shouldCommit in let action: (UndoOverlayAction) -> Bool = { value in
guard let strongSelf = self else { guard let strongSelf = self else {
return return false
} }
if !shouldCommit { if value == .undo {
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerIds[0]) strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerIds[0])
let _ = (postbox.transaction { transaction -> Void in let _ = (postbox.transaction { transaction -> Void in
for peerId in peerIds { for peerId in peerIds {
@ -1635,6 +1643,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
} }
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(nil) strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(nil)
}) })
return true
} else {
return false
} }
} }
@ -1721,11 +1732,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
return true return true
}) })
self.present(UndoOverlayController(presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(text: statusText), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] shouldCommit in self.present(UndoOverlayController(presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(text: statusText), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] value in
guard let strongSelf = self else { guard let strongSelf = self else {
return return false
} }
if shouldCommit { if value == .commit {
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId) strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId)
if let channel = chatPeer as? TelegramChannel { if let channel = chatPeer as? TelegramChannel {
strongSelf.context.peerChannelMemberCategoriesContextsManager.externallyRemoved(peerId: channel.id, memberId: strongSelf.context.account.peerId) strongSelf.context.peerChannelMemberCategoriesContextsManager.externallyRemoved(peerId: channel.id, memberId: strongSelf.context.account.peerId)
@ -1744,7 +1755,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
deleteSendMessageIntents(peerId: peerId) deleteSendMessageIntents(peerId: peerId)
}) })
completion() completion()
} else { return true
} else if value == .undo {
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId) strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId)
strongSelf.chatListDisplayNode.chatListNode.updateState({ state in strongSelf.chatListDisplayNode.chatListNode.updateState({ state in
var state = state var state = state
@ -1752,7 +1764,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
return state return state
}) })
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(nil) strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(nil)
return true
} }
return false
}), in: .current) }), in: .current)
} }

View File

@ -13,6 +13,7 @@ import TextFormat
import AccountContext import AccountContext
import StickerPackPreviewUI import StickerPackPreviewUI
import ItemListStickerPackItem import ItemListStickerPackItem
import UndoUI
private final class InstalledStickerPacksControllerArguments { private final class InstalledStickerPacksControllerArguments {
let account: Account let account: Account
@ -494,6 +495,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
let archivedPromise = Promise<[ArchivedStickerPackItem]?>() let archivedPromise = Promise<[ArchivedStickerPackItem]?>()
var presentStickerPackController: ((StickerPackCollectionInfo) -> Void)? var presentStickerPackController: ((StickerPackCollectionInfo) -> Void)?
var navigationControllerImpl: (() -> NavigationController?)?
let arguments = InstalledStickerPacksControllerArguments(account: context.account, openStickerPack: { info in let arguments = InstalledStickerPacksControllerArguments(account: context.account, openStickerPack: { info in
presentStickerPackController?(info) presentStickerPackController?(info)
@ -511,6 +513,31 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
let dismissAction: () -> Void = { [weak controller] in let dismissAction: () -> Void = { [weak controller] in
controller?.dismissAnimated() controller?.dismissAnimated()
} }
let removeAction: (RemoveStickerPackOption) -> Void = { action in
let _ = (removeStickerPackInteractively(postbox: context.account.postbox, id: archivedItem.info.id, option: .archive)
|> deliverOnMainQueue).start(next: { indexAndItems in
guard let (positionInList, items) = indexAndItems else {
return
}
var animateInAsReplacement = false
if let navigationController = navigationControllerImpl?() {
for controller in navigationController.overlayControllers {
if let controller = controller as? UndoOverlayController {
controller.dismissWithCommitActionAndReplacementAnimation()
animateInAsReplacement = true
}
}
}
navigationControllerImpl?()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_RemovedTitle, text: presentationData.strings.StickerPackActionInfo_RemovedText(archivedItem.info.title).0, undo: true, info: archivedItem.info, topItem: archivedItem.topItems.first, account: context.account), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { action in
if case .undo = action {
let _ = addStickerPackInteractively(postbox: context.account.postbox, info: archivedItem.info, items: items, positionInList: positionInList).start()
}
return true
}))
})
}
controller.setItemGroups([ controller.setItemGroups([
ActionSheetItemGroup(items: [ ActionSheetItemGroup(items: [
ActionSheetTextItem(title: presentationData.strings.StickerSettings_ContextInfo), ActionSheetTextItem(title: presentationData.strings.StickerSettings_ContextInfo),
@ -524,12 +551,12 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
archivedPromise.set(.single(packs)) archivedPromise.set(.single(packs))
updatedPacks(packs) updatedPacks(packs)
}) })
let _ = removeStickerPackInteractively(postbox: context.account.postbox, id: archivedItem.info.id, option: .archive).start() removeAction(.archive)
}), }),
ActionSheetButtonItem(title: presentationData.strings.Common_Delete, color: .destructive, action: { ActionSheetButtonItem(title: presentationData.strings.Common_Delete, color: .destructive, action: {
dismissAction() dismissAction()
let _ = removeStickerPackInteractively(postbox: context.account.postbox, id: archivedItem.info.id, option: .delete).start() removeAction(.delete)
}) })
]), ]),
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
@ -800,10 +827,37 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
packs.insert(packReference, at: 0) packs.insert(packReference, at: 0)
} }
if let mainStickerPack = mainStickerPack { if let mainStickerPack = mainStickerPack {
presentControllerImpl?(StickerPackScreen(context: context, mainStickerPack: mainStickerPack, stickerPacks: packs, parentNavigationController: controller?.navigationController as? NavigationController), nil) presentControllerImpl?(StickerPackScreen(context: context, mainStickerPack: mainStickerPack, stickerPacks: packs, parentNavigationController: controller?.navigationController as? NavigationController, actionPerformed: { info, items, action in
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
var animateInAsReplacement = false
if let navigationController = navigationControllerImpl?() {
for controller in navigationController.overlayControllers {
if let controller = controller as? UndoOverlayController {
controller.dismissWithCommitActionAndReplacementAnimation()
animateInAsReplacement = true
}
}
}
switch action {
case .add:
navigationControllerImpl?()?.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: context.account), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { _ in
return true
}))
case let .remove(positionInList):
navigationControllerImpl?()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_RemovedTitle, text: presentationData.strings.StickerPackActionInfo_RemovedText(info.title).0, undo: true, info: info, topItem: items.first, account: context.account), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { action in
if case .undo = action {
let _ = addStickerPackInteractively(postbox: context.account.postbox, info: info, items: items, positionInList: positionInList).start()
}
return true
}))
}
}), nil)
} }
}) })
} }
navigationControllerImpl = { [weak controller] in
return controller?.navigationController as? NavigationController
}
pushControllerImpl = { [weak controller] c in pushControllerImpl = { [weak controller] c in
(controller?.navigationController as? NavigationController)?.pushViewController(c) (controller?.navigationController as? NavigationController)?.pushViewController(c)
} }

View File

@ -63,10 +63,13 @@ public final class StickerPackPreviewController: ViewController, StandalonePrese
} }
} }
public init(context: AccountContext, stickerPack: StickerPackReference, mode: StickerPackPreviewControllerMode = .default, parentNavigationController: NavigationController?) { private let actionPerformed: ((StickerPackCollectionInfo, [ItemCollectionItem], StickerPackScreenPerformedAction) -> Void)?
public init(context: AccountContext, stickerPack: StickerPackReference, mode: StickerPackPreviewControllerMode = .default, parentNavigationController: NavigationController?, actionPerformed: ((StickerPackCollectionInfo, [ItemCollectionItem], StickerPackScreenPerformedAction) -> Void)? = nil) {
self.context = context self.context = context
self.mode = mode self.mode = mode
self.parentNavigationController = parentNavigationController self.parentNavigationController = parentNavigationController
self.actionPerformed = actionPerformed
self.stickerPack = stickerPack self.stickerPack = stickerPack
@ -133,7 +136,7 @@ public final class StickerPackPreviewController: ViewController, StandalonePrese
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: parentNavigationController, context: strongSelf.context, chatLocation: .peer(peer.id), animated: true)) strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: parentNavigationController, context: strongSelf.context, chatLocation: .peer(peer.id), animated: true))
} }
})) }))
}) }, actionPerformed: self.actionPerformed)
self.controllerNode.dismiss = { [weak self] in self.controllerNode.dismiss = { [weak self] in
self?.presentingViewController?.dismiss(animated: false, completion: nil) self?.presentingViewController?.dismiss(animated: false, completion: nil)
} }

View File

@ -74,6 +74,7 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
var dismiss: (() -> Void)? var dismiss: (() -> Void)?
var cancel: (() -> Void)? var cancel: (() -> Void)?
var sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)? var sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?
private let actionPerformed: ((StickerPackCollectionInfo, [ItemCollectionItem], StickerPackScreenPerformedAction) -> Void)?
let ready = Promise<Bool>() let ready = Promise<Bool>()
private var didSetReady = false private var didSetReady = false
@ -87,10 +88,11 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
private var hapticFeedback: HapticFeedback? private var hapticFeedback: HapticFeedback?
init(context: AccountContext, openShare: (() -> Void)?, openMention: @escaping (String) -> Void) { init(context: AccountContext, openShare: (() -> Void)?, openMention: @escaping (String) -> Void, actionPerformed: ((StickerPackCollectionInfo, [ItemCollectionItem], StickerPackScreenPerformedAction) -> Void)?) {
self.context = context self.context = context
self.openShare = openShare self.openShare = openShare
self.presentationData = context.sharedContext.currentPresentationData.with { $0 } self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.actionPerformed = actionPerformed
self.wrappingScrollNode = ASScrollNode() self.wrappingScrollNode = ASScrollNode()
self.wrappingScrollNode.view.alwaysBounceVertical = true self.wrappingScrollNode.view.alwaysBounceVertical = true
@ -509,23 +511,27 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
} }
@objc func installActionButtonPressed() { @objc func installActionButtonPressed() {
let dismissOnAction: Bool let dismissOnAction = true
if let initiallyInstalled = self.stickerPackInitiallyInstalled, initiallyInstalled {
dismissOnAction = false
} else {
dismissOnAction = true
}
if let stickerPack = self.stickerPack, let stickerSettings = self.stickerSettings { if let stickerPack = self.stickerPack, let stickerSettings = self.stickerSettings {
switch stickerPack { switch stickerPack {
case let .result(info, items, installed): case let .result(info, items, installed):
if installed { if installed {
let _ = removeStickerPackInteractively(postbox: self.context.account.postbox, id: info.id, option: .delete).start() let _ = (removeStickerPackInteractively(postbox: self.context.account.postbox, id: info.id, option: .delete)
self.updateStickerPack(.result(info: info, items: items, installed: false), stickerSettings: stickerSettings) |> deliverOnMainQueue).start(next: { [weak self] indexAndItems in
guard let strongSelf = self, let (positionInList, _) = indexAndItems else {
return
}
strongSelf.actionPerformed?(info, items, .remove(positionInList: positionInList))
})
if !dismissOnAction {
self.updateStickerPack(.result(info: info, items: items, installed: false), stickerSettings: stickerSettings)
}
} else { } else {
let _ = addStickerPackInteractively(postbox: self.context.account.postbox, info: info, items: items).start() let _ = addStickerPackInteractively(postbox: self.context.account.postbox, info: info, items: items).start()
if !dismissOnAction { if !dismissOnAction {
self.updateStickerPack(.result(info: info, items: items, installed: true), stickerSettings: stickerSettings) self.updateStickerPack(.result(info: info, items: items, installed: true), stickerSettings: stickerSettings)
} }
self.actionPerformed?(info, items, .add)
} }
if dismissOnAction { if dismissOnAction {
self.cancelButtonPressed() self.cancelButtonPressed()

View File

@ -842,8 +842,13 @@ public final class StickerPackScreenImpl: ViewController {
} }
} }
public func StickerPackScreen(context: AccountContext, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], parentNavigationController: NavigationController? = nil, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)? = nil) -> ViewController { public enum StickerPackScreenPerformedAction {
let controller = StickerPackPreviewController(context: context, stickerPack: mainStickerPack, mode: .default, parentNavigationController: parentNavigationController) case add
case remove(positionInList: Int)
}
public func StickerPackScreen(context: AccountContext, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], parentNavigationController: NavigationController? = nil, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)? = nil, actionPerformed: ((StickerPackCollectionInfo, [ItemCollectionItem], StickerPackScreenPerformedAction) -> Void)? = nil) -> ViewController {
let controller = StickerPackPreviewController(context: context, stickerPack: mainStickerPack, mode: .default, parentNavigationController: parentNavigationController, actionPerformed: actionPerformed)
controller.sendSticker = sendSticker controller.sendSticker = sendSticker
return controller return controller
} }

View File

@ -4,7 +4,7 @@ import SwiftSignalKit
import SyncCore import SyncCore
public func addStickerPackInteractively(postbox: Postbox, info: StickerPackCollectionInfo, items: [ItemCollectionItem]) -> Signal<Void, NoError> { public func addStickerPackInteractively(postbox: Postbox, info: StickerPackCollectionInfo, items: [ItemCollectionItem], positionInList: Int? = nil) -> Signal<Void, NoError> {
return postbox.transaction { transaction -> Void in return postbox.transaction { transaction -> Void in
let namespace: SynchronizeInstalledStickerPacksOperationNamespace? let namespace: SynchronizeInstalledStickerPacksOperationNamespace?
switch info.id.namespace { switch info.id.namespace {
@ -23,7 +23,11 @@ public func addStickerPackInteractively(postbox: Postbox, info: StickerPackColle
updatedInfos.remove(at: index) updatedInfos.remove(at: index)
updatedInfos.insert(currentInfo, at: 0) updatedInfos.insert(currentInfo, at: 0)
} else { } else {
updatedInfos.insert(info, at: 0) if let positionInList = positionInList, positionInList <= updatedInfos.count {
updatedInfos.insert(info, at: positionInList)
} else {
updatedInfos.insert(info, at: 0)
}
transaction.replaceItemCollectionItems(collectionId: info.id, items: items) transaction.replaceItemCollectionItems(collectionId: info.id, items: items)
} }
transaction.replaceItemCollectionInfos(namespace: info.id.namespace, itemCollectionInfos: updatedInfos.map { ($0.id, $0) }) transaction.replaceItemCollectionInfos(namespace: info.id.namespace, itemCollectionInfos: updatedInfos.map { ($0.id, $0) })
@ -36,8 +40,8 @@ public enum RemoveStickerPackOption {
case archive case archive
} }
public func removeStickerPackInteractively(postbox: Postbox, id: ItemCollectionId, option: RemoveStickerPackOption) -> Signal<Void, NoError> { public func removeStickerPackInteractively(postbox: Postbox, id: ItemCollectionId, option: RemoveStickerPackOption) -> Signal<(Int, [ItemCollectionItem])?, NoError> {
return postbox.transaction { transaction -> Void in return postbox.transaction { transaction -> (Int, [ItemCollectionItem])? in
let namespace: SynchronizeInstalledStickerPacksOperationNamespace? let namespace: SynchronizeInstalledStickerPacksOperationNamespace?
switch id.namespace { switch id.namespace {
case Namespaces.ItemCollection.CloudStickerPacks: case Namespaces.ItemCollection.CloudStickerPacks:
@ -55,8 +59,14 @@ public func removeStickerPackInteractively(postbox: Postbox, id: ItemCollectionI
case .archive: case .archive:
content = .archive([id]) content = .archive([id])
} }
let index = transaction.getItemCollectionsInfos(namespace: id.namespace).index(where: { $0.0 == id })
let items = transaction.getItemCollectionItems(collectionId: id)
addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: namespace, content: content) addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: namespace, content: content)
transaction.removeItemCollection(collectionId: id) transaction.removeItemCollection(collectionId: id)
return index.flatMap { ($0, items) }
} else {
return nil
} }
} }
} }

View File

@ -1727,7 +1727,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}) })
}, displaySwipeToReplyHint: { [weak self] in }, displaySwipeToReplyHint: { [weak self] in
if let strongSelf = self, let validLayout = strongSelf.validLayout, min(validLayout.size.width, validLayout.size.height) > 320.0 { if let strongSelf = self, let validLayout = strongSelf.validLayout, min(validLayout.size.width, validLayout.size.height) > 320.0 {
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .swipeToReply(title: strongSelf.presentationData.strings.Conversation_SwipeToReplyHintTitle, text: strongSelf.presentationData.strings.Conversation_SwipeToReplyHintText), elevatedLayout: true, action: { _ in }), in: .window(.root)) strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .swipeToReply(title: strongSelf.presentationData.strings.Conversation_SwipeToReplyHintTitle, text: strongSelf.presentationData.strings.Conversation_SwipeToReplyHintText), elevatedLayout: true, action: { _ in return false }), in: .window(.root))
} }
}, dismissReplyMarkupMessage: { [weak self] message in }, dismissReplyMarkupMessage: { [weak self] message in
guard let strongSelf = self, strongSelf.presentationInterfaceState.keyboardButtonsMessage?.id == message.id else { guard let strongSelf = self, strongSelf.presentationInterfaceState.keyboardButtonsMessage?.id == message.id else {

View File

@ -20,6 +20,7 @@ import SettingsUI
import AlertUI import AlertUI
import PresentationDataUtils import PresentationDataUtils
import ShareController import ShareController
import UndoUI
private enum ChatMessageGalleryControllerData { private enum ChatMessageGalleryControllerData {
case url(String) case url(String)
@ -304,7 +305,31 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
params.navigationController?.pushViewController(controller) params.navigationController?.pushViewController(controller)
return true return true
case let .stickerPack(reference): case let .stickerPack(reference):
let controller = StickerPackScreen(context: params.context, mainStickerPack: reference, stickerPacks: [reference], sendSticker: params.sendSticker) let controller = StickerPackScreen(context: params.context, mainStickerPack: reference, stickerPacks: [reference], sendSticker: params.sendSticker, actionPerformed: { info, items, action in
let presentationData = params.context.sharedContext.currentPresentationData.with { $0 }
var animateInAsReplacement = false
if let navigationController = params.navigationController {
for controller in navigationController.overlayControllers {
if let controller = controller as? UndoOverlayController {
controller.dismissWithCommitActionAndReplacementAnimation()
animateInAsReplacement = true
}
}
}
switch action {
case .add:
params.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: params.context.account), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { _ in
return true
}))
case let .remove(positionInList):
params.navigationController?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_RemovedTitle, text: presentationData.strings.StickerPackActionInfo_RemovedText(info.title).0, undo: true, info: info, topItem: items.first, account: params.context.account), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { action in
if case .undo = action {
let _ = addStickerPackInteractively(postbox: params.context.account.postbox, info: info, items: items, positionInList: positionInList).start()
}
return true
}))
}
})
params.dismissInput() params.dismissInput()
params.present(controller, nil) params.present(controller, nil)
return true return true

View File

@ -2,6 +2,9 @@ import Foundation
import UIKit import UIKit
import Display import Display
import TelegramPresentationData import TelegramPresentationData
import SyncCore
import Postbox
import TelegramCore
public enum UndoOverlayContent { public enum UndoOverlayContent {
case removedChat(text: String) case removedChat(text: String)
@ -12,6 +15,13 @@ public enum UndoOverlayContent {
case emoji(path: String, text: String) case emoji(path: String, text: String)
case swipeToReply(title: String, text: String) case swipeToReply(title: String, text: String)
case actionSucceeded(title: String, text: String, cancel: String) case actionSucceeded(title: String, text: String, cancel: String)
case stickersModified(title: String, text: String, undo: Bool, info: StickerPackCollectionInfo, topItem: ItemCollectionItem?, account: Account)
}
public enum UndoOverlayAction {
case info
case undo
case commit
} }
public final class UndoOverlayController: ViewController { public final class UndoOverlayController: ViewController {
@ -19,12 +29,12 @@ public final class UndoOverlayController: ViewController {
public let content: UndoOverlayContent public let content: UndoOverlayContent
private let elevatedLayout: Bool private let elevatedLayout: Bool
private let animateInAsReplacement: Bool private let animateInAsReplacement: Bool
private var action: (Bool) -> Void private var action: (UndoOverlayAction) -> Bool
private var didPlayPresentationAnimation = false private var didPlayPresentationAnimation = false
private var dismissed = false private var dismissed = false
public init(presentationData: PresentationData, content: UndoOverlayContent, elevatedLayout: Bool, animateInAsReplacement: Bool = false, action: @escaping (Bool) -> Void) { public init(presentationData: PresentationData, content: UndoOverlayContent, elevatedLayout: Bool, animateInAsReplacement: Bool = false, action: @escaping (UndoOverlayAction) -> Bool) {
self.presentationData = presentationData self.presentationData = presentationData
self.content = content self.content = content
self.elevatedLayout = elevatedLayout self.elevatedLayout = elevatedLayout
@ -42,7 +52,7 @@ public final class UndoOverlayController: ViewController {
override public func loadDisplayNode() { override public func loadDisplayNode() {
self.displayNode = UndoOverlayControllerNode(presentationData: self.presentationData, content: self.content, elevatedLayout: self.elevatedLayout, action: { [weak self] value in self.displayNode = UndoOverlayControllerNode(presentationData: self.presentationData, content: self.content, elevatedLayout: self.elevatedLayout, action: { [weak self] value in
self?.action(value) return self?.action(value) ?? false
}, dismiss: { [weak self] in }, dismiss: { [weak self] in
self?.dismiss() self?.dismiss()
}) })
@ -50,12 +60,12 @@ public final class UndoOverlayController: ViewController {
} }
public func dismissWithCommitAction() { public func dismissWithCommitAction() {
self.action(true) self.action(.commit)
self.dismiss() self.dismiss()
} }
public func dismissWithCommitActionAndReplacementAnimation() { public func dismissWithCommitActionAndReplacementAnimation() {
self.action(true) self.action(.commit)
(self.displayNode as! UndoOverlayControllerNode).animateOutWithReplacement(completion: { [weak self] in (self.displayNode as! UndoOverlayControllerNode).animateOutWithReplacement(completion: { [weak self] in
self?.presentingViewController?.dismiss(animated: false, completion: nil) self?.presentingViewController?.dismiss(animated: false, completion: nil)
}) })

View File

@ -9,23 +9,31 @@ import Markdown
import RadialStatusNode import RadialStatusNode
import AppBundle import AppBundle
import AnimatedStickerNode import AnimatedStickerNode
import TelegramAnimatedStickerNode
import AnimationUI import AnimationUI
import SyncCore
import Postbox
import TelegramCore
import StickerResources
final class UndoOverlayControllerNode: ViewControllerTracingNode { final class UndoOverlayControllerNode: ViewControllerTracingNode {
private let elevatedLayout: Bool private let elevatedLayout: Bool
private let statusNode: RadialStatusNode private var statusNode: RadialStatusNode?
private let timerTextNode: ImmediateTextNode private let timerTextNode: ImmediateTextNode
private let iconNode: ASImageNode? private let iconNode: ASImageNode?
private let iconCheckNode: RadialStatusNode? private let iconCheckNode: RadialStatusNode?
private let animationNode: AnimationNode? private let animationNode: AnimationNode?
private let animatedStickerNode: AnimatedStickerNode? private var animatedStickerNode: AnimatedStickerNode?
private var stillStickerNode: TransformImageNode?
private var stickerImageSize: CGSize?
private let titleNode: ImmediateTextNode private let titleNode: ImmediateTextNode
private let textNode: ImmediateTextNode private let textNode: ImmediateTextNode
private let buttonTextNode: ImmediateTextNode
private let buttonNode: HighlightTrackingButtonNode private let buttonNode: HighlightTrackingButtonNode
private let undoButtonTextNode: ImmediateTextNode
private let undoButtonNode: HighlightTrackingButtonNode
private let panelNode: ASDisplayNode private let panelNode: ASDisplayNode
private let panelWrapperNode: ASDisplayNode private let panelWrapperNode: ASDisplayNode
private let action: (Bool) -> Void private let action: (UndoOverlayAction) -> Bool
private let dismiss: () -> Void private let dismiss: () -> Void
private let effectView: UIView private let effectView: UIView
@ -38,7 +46,9 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
private var validLayout: ContainerViewLayout? private var validLayout: ContainerViewLayout?
init(presentationData: PresentationData, content: UndoOverlayContent, elevatedLayout: Bool, action: @escaping (Bool) -> Void, dismiss: @escaping () -> Void) { private var fetchResourceDisposable: Disposable?
init(presentationData: PresentationData, content: UndoOverlayContent, elevatedLayout: Bool, action: @escaping (UndoOverlayAction) -> Bool, dismiss: @escaping () -> Void) {
self.elevatedLayout = elevatedLayout self.elevatedLayout = elevatedLayout
self.action = action self.action = action
@ -55,6 +65,8 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.textNode.displaysAsynchronously = false self.textNode.displaysAsynchronously = false
self.textNode.maximumNumberOfLines = 0 self.textNode.maximumNumberOfLines = 0
self.buttonNode = HighlightTrackingButtonNode()
var displayUndo = true var displayUndo = true
var undoText = presentationData.strings.Undo_Undo var undoText = presentationData.strings.Undo_Undo
var undoTextColor = UIColor(rgb: 0x5ac8fa) var undoTextColor = UIColor(rgb: 0x5ac8fa)
@ -74,6 +86,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white) self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white)
displayUndo = true displayUndo = true
self.originalRemainingSeconds = 5 self.originalRemainingSeconds = 5
self.statusNode = RadialStatusNode(backgroundNodeColor: .clear)
case let .archivedChat(_, title, text, undo): case let .archivedChat(_, title, text, undo):
if undo { if undo {
self.iconNode = ASImageNode() self.iconNode = ASImageNode()
@ -129,12 +142,17 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil self.iconCheckNode = nil
self.animationNode = AnimationNode(animation: "anim_success", colors: ["info1.info1.stroke": self.animationBackgroundColor, "info2.info2.Fill": self.animationBackgroundColor], scale: 1.0) self.animationNode = AnimationNode(animation: "anim_success", colors: ["info1.info1.stroke": self.animationBackgroundColor, "info2.info2.Fill": self.animationBackgroundColor], scale: 1.0)
self.animatedStickerNode = nil self.animatedStickerNode = nil
undoTextColor = UIColor(rgb: 0xff7b74)
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
let link = MarkdownAttributeSet(font: Font.regular(14.0), textColor: undoTextColor)
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { _ in return nil }), textAlignment: .natural)
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white) self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white)
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white) self.textNode.attributedText = attributedText
displayUndo = true displayUndo = true
undoText = cancel undoText = cancel
undoTextColor = UIColor(rgb: 0xff7b74)
self.originalRemainingSeconds = 3 self.originalRemainingSeconds = 3
case let .emoji(path, text): case let .emoji(path, text):
self.iconNode = nil self.iconNode = nil
@ -161,17 +179,102 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.textNode.maximumNumberOfLines = 2 self.textNode.maximumNumberOfLines = 2
displayUndo = false displayUndo = false
self.originalRemainingSeconds = 5 self.originalRemainingSeconds = 5
case let .stickersModified(title, text, undo, info, topItem, account):
self.iconNode = nil
self.iconCheckNode = nil
self.animationNode = nil
let stillStickerNode = TransformImageNode()
self.stillStickerNode = stillStickerNode
enum StickerPackThumbnailItem {
case still(TelegramMediaImageRepresentation)
case animated(MediaResource)
}
var thumbnailItem: StickerPackThumbnailItem?
var resourceReference: MediaResourceReference?
if let thumbnail = info.thumbnail {
if info.flags.contains(.isAnimated) {
thumbnailItem = .animated(thumbnail.resource)
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)
} else {
thumbnailItem = .still(thumbnail)
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)
}
} else if let item = topItem as? StickerPackItem {
if item.file.isAnimatedSticker {
thumbnailItem = .animated(item.file.resource)
resourceReference = MediaResourceReference.media(media: .standalone(media: item.file), resource: item.file.resource)
} else if let dimensions = item.file.dimensions, let resource = chatMessageStickerResource(file: item.file, small: true) as? TelegramMediaResource {
thumbnailItem = .still(TelegramMediaImageRepresentation(dimensions: dimensions, resource: resource))
resourceReference = MediaResourceReference.media(media: .standalone(media: item.file), resource: resource)
}
}
var updatedImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>?
var updatedFetchSignal: Signal<FetchResourceSourceType, FetchResourceError>?
let imageBoundingSize = CGSize(width: 34.0, height: 34.0)
if let thumbnailItem = thumbnailItem {
switch thumbnailItem {
case let .still(representation):
let stillImageSize = representation.dimensions.cgSize.aspectFitted(imageBoundingSize)
self.stickerImageSize = stillImageSize
updatedImageSignal = chatMessageStickerPackThumbnail(postbox: account.postbox, resource: representation.resource)
case let .animated(resource):
self.stickerImageSize = imageBoundingSize
updatedImageSignal = chatMessageStickerPackThumbnail(postbox: account.postbox, resource: resource, animated: true)
}
if let resourceReference = resourceReference {
updatedFetchSignal = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: resourceReference)
}
} else {
updatedImageSignal = .single({ _ in return nil })
updatedFetchSignal = .complete()
}
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white)
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .natural)
self.textNode.attributedText = attributedText
self.textNode.maximumNumberOfLines = 2
displayUndo = undo
self.originalRemainingSeconds = 2
if let updatedFetchSignal = updatedFetchSignal {
self.fetchResourceDisposable = updatedFetchSignal.start()
}
if let updatedImageSignal = updatedImageSignal {
stillStickerNode.setSignal(updatedImageSignal)
}
if let thumbnailItem = thumbnailItem {
switch thumbnailItem {
case .still:
break
case let .animated(resource):
let animatedStickerNode = AnimatedStickerNode()
self.animatedStickerNode = animatedStickerNode
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: resource), width: 80, height: 80, mode: .cached)
}
}
} }
self.remainingSeconds = self.originalRemainingSeconds self.remainingSeconds = self.originalRemainingSeconds
self.statusNode = RadialStatusNode(backgroundNodeColor: .clear) self.undoButtonTextNode = ImmediateTextNode()
self.undoButtonTextNode.displaysAsynchronously = false
self.undoButtonTextNode.attributedText = NSAttributedString(string: undoText, font: Font.regular(17.0), textColor: undoTextColor)
self.buttonTextNode = ImmediateTextNode() self.undoButtonNode = HighlightTrackingButtonNode()
self.buttonTextNode.displaysAsynchronously = false
self.buttonTextNode.attributedText = NSAttributedString(string: undoText, font: Font.regular(17.0), textColor: undoTextColor)
self.buttonNode = HighlightTrackingButtonNode()
self.panelNode = ASDisplayNode() self.panelNode = ASDisplayNode()
if presentationData.theme.overallDarkAppearance { if presentationData.theme.overallDarkAppearance {
@ -191,35 +294,46 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
switch content { switch content {
case .removedChat: case .removedChat:
self.panelWrapperNode.addSubnode(self.timerTextNode) self.panelWrapperNode.addSubnode(self.timerTextNode)
self.panelWrapperNode.addSubnode(self.statusNode) case .archivedChat, .hidArchive, .revealedArchive, .succeed, .emoji, .swipeToReply, .actionSucceeded, .stickersModified:
case .archivedChat, .hidArchive, .revealedArchive, .succeed, .emoji, .swipeToReply, .actionSucceeded:
break break
} }
self.statusNode.flatMap(self.panelWrapperNode.addSubnode)
self.iconNode.flatMap(self.panelWrapperNode.addSubnode) self.iconNode.flatMap(self.panelWrapperNode.addSubnode)
self.iconCheckNode.flatMap(self.panelWrapperNode.addSubnode) self.iconCheckNode.flatMap(self.panelWrapperNode.addSubnode)
self.animationNode.flatMap(self.panelWrapperNode.addSubnode) self.animationNode.flatMap(self.panelWrapperNode.addSubnode)
self.stillStickerNode.flatMap(self.panelWrapperNode.addSubnode)
self.animatedStickerNode.flatMap(self.panelWrapperNode.addSubnode) self.animatedStickerNode.flatMap(self.panelWrapperNode.addSubnode)
self.panelWrapperNode.addSubnode(self.titleNode) self.panelWrapperNode.addSubnode(self.titleNode)
self.panelWrapperNode.addSubnode(self.textNode) self.panelWrapperNode.addSubnode(self.textNode)
self.panelWrapperNode.addSubnode(self.buttonNode)
if displayUndo { if displayUndo {
self.panelWrapperNode.addSubnode(self.buttonTextNode) self.panelWrapperNode.addSubnode(self.undoButtonTextNode)
self.panelWrapperNode.addSubnode(self.buttonNode) self.panelWrapperNode.addSubnode(self.undoButtonNode)
} }
self.addSubnode(self.panelNode) self.addSubnode(self.panelNode)
self.addSubnode(self.panelWrapperNode) self.addSubnode(self.panelWrapperNode)
self.buttonNode.highligthedChanged = { [weak self] highlighted in self.undoButtonNode.highligthedChanged = { [weak self] highlighted in
if let strongSelf = self { if let strongSelf = self {
if highlighted { if highlighted {
strongSelf.buttonTextNode.layer.removeAnimation(forKey: "opacity") strongSelf.undoButtonTextNode.layer.removeAnimation(forKey: "opacity")
strongSelf.buttonTextNode.alpha = 0.4 strongSelf.undoButtonTextNode.alpha = 0.4
} else { } else {
strongSelf.buttonTextNode.alpha = 1.0 strongSelf.undoButtonTextNode.alpha = 1.0
strongSelf.buttonTextNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) strongSelf.undoButtonTextNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
} }
} }
} }
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside) self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
self.undoButtonNode.addTarget(self, action: #selector(self.undoButtonPressed), forControlEvents: .touchUpInside)
self.animatedStickerNode?.started = { [weak self] in
self?.stillStickerNode?.isHidden = true
}
}
deinit {
self.fetchResourceDisposable?.dispose()
} }
override func didLoad() { override func didLoad() {
@ -230,7 +344,13 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
} }
@objc private func buttonPressed() { @objc private func buttonPressed() {
self.action(false) if self.action(.info) {
self.dismiss()
}
}
@objc private func undoButtonPressed() {
self.action(.undo)
self.dismiss() self.dismiss()
} }
@ -239,7 +359,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.remainingSeconds -= 1 self.remainingSeconds -= 1
} }
if self.remainingSeconds == 0 { if self.remainingSeconds == 0 {
self.action(true) self.action(.commit)
self.dismiss() self.dismiss()
} else { } else {
if !self.timerTextNode.bounds.size.width.isZero, let snapshot = self.timerTextNode.view.snapshotContentTree() { if !self.timerTextNode.bounds.size.width.isZero, let snapshot = self.timerTextNode.view.snapshotContentTree() {
@ -286,9 +406,9 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
let margin: CGFloat = 16.0 let margin: CGFloat = 16.0
let buttonTextSize = self.buttonTextNode.updateLayout(CGSize(width: 200.0, height: .greatestFiniteMagnitude)) let buttonTextSize = self.undoButtonTextNode.updateLayout(CGSize(width: 200.0, height: .greatestFiniteMagnitude))
let buttonMinX: CGFloat let buttonMinX: CGFloat
if self.buttonNode.supernode != nil { if self.undoButtonNode.supernode != nil {
buttonMinX = layout.size.width - layout.safeInsets.left - rightInset - buttonTextSize.width - margin * 2.0 buttonMinX = layout.size.width - layout.safeInsets.left - rightInset - buttonTextSize.width - margin * 2.0
} else { } else {
buttonMinX = layout.size.width - layout.safeInsets.left - rightInset buttonMinX = layout.size.width - layout.safeInsets.left - rightInset
@ -316,8 +436,12 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.effectView.frame = CGRect(x: 0.0, y: 0.0, width: layout.size.width - margin * 2.0 - layout.safeInsets.left - layout.safeInsets.right, height: contentHeight) self.effectView.frame = CGRect(x: 0.0, y: 0.0, width: layout.size.width - margin * 2.0 - layout.safeInsets.left - layout.safeInsets.right, height: contentHeight)
let buttonTextFrame = CGRect(origin: CGPoint(x: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - rightInset - buttonTextSize.width - margin * 2.0, y: floor((contentHeight - buttonTextSize.height) / 2.0)), size: buttonTextSize) let buttonTextFrame = CGRect(origin: CGPoint(x: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - rightInset - buttonTextSize.width - margin * 2.0, y: floor((contentHeight - buttonTextSize.height) / 2.0)), size: buttonTextSize)
transition.updateFrame(node: self.buttonTextNode, frame: buttonTextFrame) transition.updateFrame(node: self.undoButtonTextNode, frame: buttonTextFrame)
self.buttonNode.frame = CGRect(origin: CGPoint(x: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - rightInset - buttonTextSize.width - 8.0 - margin * 2.0, y: 0.0), size: CGSize(width: layout.safeInsets.right + rightInset + buttonTextSize.width + 8.0 + margin, height: contentHeight))
let undoButtonFrame = CGRect(origin: CGPoint(x: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - rightInset - buttonTextSize.width - 8.0 - margin * 2.0, y: 0.0), size: CGSize(width: layout.safeInsets.right + rightInset + buttonTextSize.width + 8.0 + margin, height: contentHeight))
self.undoButtonNode.frame = undoButtonFrame
self.buttonNode.frame = CGRect(origin: CGPoint(x: layout.safeInsets.left, y: 0.0), size: CGSize(width: undoButtonFrame.minX - layout.safeInsets.left, height: contentHeight))
var textContentHeight = textSize.height var textContentHeight = textSize.height
var textOffset: CGFloat = 0.0 var textOffset: CGFloat = 0.0
@ -351,7 +475,22 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
transition.updateFrame(node: animationNode, frame: iconFrame) transition.updateFrame(node: animationNode, frame: iconFrame)
} }
if let animatedStickerNode = self.animatedStickerNode { if let stickerImageSize = self.stickerImageSize {
let iconSize = stickerImageSize
let iconFrame = CGRect(origin: CGPoint(x: floor((leftInset - iconSize.width) / 2.0), y: floor((contentHeight - iconSize.height) / 2.0)), size: iconSize)
if let stillStickerNode = self.stillStickerNode {
let makeImageLayout = stillStickerNode.asyncLayout()
let imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: stickerImageSize, boundingSize: stickerImageSize, intrinsicInsets: UIEdgeInsets()))
let _ = imageApply()
transition.updateFrame(node: stillStickerNode, frame: iconFrame)
}
if let animatedStickerNode = self.animatedStickerNode {
animatedStickerNode.updateLayout(size: iconFrame.size)
transition.updateFrame(node: animatedStickerNode, frame: iconFrame)
}
} else if let animatedStickerNode = self.animatedStickerNode {
let iconSize = CGSize(width: 32.0, height: 32.0) let iconSize = CGSize(width: 32.0, height: 32.0)
let iconFrame = CGRect(origin: CGPoint(x: floor((leftInset - iconSize.width) / 2.0), y: floor((contentHeight - iconSize.height) / 2.0)), size: iconSize) let iconFrame = CGRect(origin: CGPoint(x: floor((leftInset - iconSize.width) / 2.0), y: floor((contentHeight - iconSize.height) / 2.0)), size: iconSize)
animatedStickerNode.updateLayout(size: iconFrame.size) animatedStickerNode.updateLayout(size: iconFrame.size)
@ -361,9 +500,11 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
let timerTextSize = self.timerTextNode.updateLayout(CGSize(width: 100.0, height: 100.0)) let timerTextSize = self.timerTextNode.updateLayout(CGSize(width: 100.0, height: 100.0))
transition.updateFrame(node: self.timerTextNode, frame: CGRect(origin: CGPoint(x: floor((leftInset - timerTextSize.width) / 2.0), y: floor((contentHeight - timerTextSize.height) / 2.0)), size: timerTextSize)) transition.updateFrame(node: self.timerTextNode, frame: CGRect(origin: CGPoint(x: floor((leftInset - timerTextSize.width) / 2.0), y: floor((contentHeight - timerTextSize.height) / 2.0)), size: timerTextSize))
let statusSize: CGFloat = 30.0 let statusSize: CGFloat = 30.0
transition.updateFrame(node: self.statusNode, frame: CGRect(origin: CGPoint(x: floor((leftInset - statusSize) / 2.0), y: floor((contentHeight - statusSize) / 2.0)), size: CGSize(width: statusSize, height: statusSize))) if let statusNode = self.statusNode {
if firstLayout { transition.updateFrame(node: statusNode, frame: CGRect(origin: CGPoint(x: floor((leftInset - statusSize) / 2.0), y: floor((contentHeight - statusSize) / 2.0)), size: CGSize(width: statusSize, height: statusSize)))
self.statusNode.transitionToState(.secretTimeout(color: .white, icon: nil, beginTime: CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970, timeout: Double(self.remainingSeconds), sparks: false), completion: {}) if firstLayout {
statusNode.transitionToState(.secretTimeout(color: .white, icon: nil, beginTime: CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970, timeout: Double(self.remainingSeconds), sparks: false), completion: {})
}
} }
} }