From 9ec67698f5912e97f50aee3ca9cd3c324df22a41 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sun, 26 Apr 2020 21:06:57 +0400 Subject: [PATCH] Dice fixes --- .../Sources/ManagedAnimationNode.swift | 16 +++++++-- .../TelegramUI/Sources/ChatController.swift | 4 +-- .../ChatMessageAnimatedStickerItemNode.swift | 7 ++++ .../Sources/ManagedDiceAnimationNode.swift | 36 ++++++++++++------- .../Sources/UndoOverlayController.swift | 2 +- .../Sources/UndoOverlayControllerNode.swift | 10 ++++-- 6 files changed, 54 insertions(+), 21 deletions(-) diff --git a/submodules/ManagedAnimationNode/Sources/ManagedAnimationNode.swift b/submodules/ManagedAnimationNode/Sources/ManagedAnimationNode.swift index 2131982654..ccad59c94a 100644 --- a/submodules/ManagedAnimationNode/Sources/ManagedAnimationNode.swift +++ b/submodules/ManagedAnimationNode/Sources/ManagedAnimationNode.swift @@ -10,7 +10,6 @@ import SwiftSignalKit public final class ManagedAnimationState { public let item: ManagedAnimationItem - private let instance: LottieInstance let frameCount: Int @@ -19,6 +18,8 @@ public final class ManagedAnimationState { var relativeTime: Double = 0.0 public var frameIndex: Int? + public var executedCallbacks = Set() + private let renderContext: DrawingContext public init?(displaySize: CGSize, item: ManagedAnimationItem, current: ManagedAnimationState?) { @@ -109,17 +110,19 @@ public enum ManagedAnimationSource: Equatable { } } -public struct ManagedAnimationItem: Equatable { +public struct ManagedAnimationItem { public let source: ManagedAnimationSource var frames: ManagedAnimationFrameRange? var duration: Double? var loop: Bool + var callbacks: [(Int, () -> Void)] - public init(source: ManagedAnimationSource, frames: ManagedAnimationFrameRange? = nil, duration: Double? = nil, loop: Bool = false) { + public init(source: ManagedAnimationSource, frames: ManagedAnimationFrameRange? = nil, duration: Double? = nil, loop: Bool = false, callbacks: [(Int, () -> Void)] = []) { self.source = source self.frames = frames self.duration = duration self.loop = loop + self.callbacks = callbacks } } @@ -240,6 +243,13 @@ open class ManagedAnimationNode: ASDisplayNode { if let image = state.draw() { self.imageNode.image = image } + + for (callbackFrame, callback) in state.item.callbacks { + if !state.executedCallbacks.contains(callbackFrame) && frameIndex >= callbackFrame { + state.executedCallbacks.insert(callbackFrame) + callback() + } + } } var animationAdvancement: Double = 1.0 / 60.0 diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index d6f7f2bff8..d18351eab9 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -6573,8 +6573,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } if let value = value { - self.present(UndoOverlayController(presentationData: self.presentationData, content: .dice(dice: dice, account: self.context.account, text: value, action: self.presentationData.strings.Conversation_SendDice), elevatedLayout: true, action: { [weak self] action in - if let strongSelf = self, action == .undo { + self.present(UndoOverlayController(presentationData: self.presentationData, content: .dice(dice: dice, account: self.context.account, text: value, action: canSendMessagesToChat(self.presentationInterfaceState) ? self.presentationData.strings.Conversation_SendDice : nil), elevatedLayout: true, action: { [weak self] action in + if let strongSelf = self, canSendMessagesToChat(strongSelf.presentationInterfaceState), action == .undo { strongSelf.sendMessages([.message(text: "", attributes: [], mediaReference: AnyMediaReference.standalone(media: TelegramMediaDice(emoji: dice.emoji)), replyToMessageId: nil, localGroupingKey: nil)]) } return false diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift index bd9ab68b5d..a650b6fffe 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -210,6 +210,13 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { if let telegramDice = self.telegramDice { let animationNode = ManagedDiceAnimationNode(context: item.context, emoji: telegramDice.emoji) + if !item.message.effectivelyIncoming(item.context.account.peerId) { + animationNode.success = { [weak self] in + if let strongSelf = self, let item = strongSelf.item { + item.controllerInteraction.animateDiceSuccess() + } + } + } self.animationNode = animationNode } else { let animationNode = AnimatedStickerNode() diff --git a/submodules/TelegramUI/Sources/ManagedDiceAnimationNode.swift b/submodules/TelegramUI/Sources/ManagedDiceAnimationNode.swift index cd4393649e..fa7e406135 100644 --- a/submodules/TelegramUI/Sources/ManagedDiceAnimationNode.swift +++ b/submodules/TelegramUI/Sources/ManagedDiceAnimationNode.swift @@ -14,19 +14,25 @@ enum ManagedDiceAnimationState: Equatable { case value(Int32, Bool) } -private func animationItem(account: Account, emojis: Signal<[TelegramMediaFile], NoError>, emoji: String, value: Int32?, immediate: Bool = false, roll: Bool = false, loop: Bool = false) -> Signal { - return emojis - |> mapToSignal { diceEmojis -> Signal in +private func animationItem(account: Account, emojis: Signal<[TelegramMediaFile], NoError>, configuration: Signal = .single(nil), emoji: String, value: Int32?, immediate: Bool = false, roll: Bool = false, loop: Bool = false, successCallback: (() -> Void)? = nil) -> Signal { + return combineLatest(emojis, configuration) + |> mapToSignal { diceEmojis, configuration -> Signal in if let value = value, value >= diceEmojis.count { return .complete() } let file = diceEmojis[Int(value ?? 0)] + var callbacks: [(Int, () -> Void)] = [] + + if !loop, let successCallback = successCallback, let value = value, let configuration = configuration, let success = configuration.successParameters[emoji], value == success.value { + callbacks.append((success.frame, successCallback)) + } + if let _ = account.postbox.mediaBox.completedResourcePath(file.resource) { if immediate { return .single(ManagedAnimationItem(source: .resource(account.postbox.mediaBox, file.resource), frames: .still(.end), duration: 0)) } else { - return .single(ManagedAnimationItem(source: .resource(account.postbox.mediaBox, file.resource), loop: loop)) + return .single(ManagedAnimationItem(source: .resource(account.postbox.mediaBox, file.resource), loop: loop, callbacks: callbacks)) } } else { let dimensions = file.dimensions ?? PixelDimensions(width: 512, height: 512) @@ -39,7 +45,7 @@ private func animationItem(account: Account, emojis: Signal<[TelegramMediaFile], |> filter { data in return data.complete }).start(next: { next in - subscriber.putNext(ManagedAnimationItem(source: .resource(account.postbox.mediaBox, file.resource), loop: loop)) + subscriber.putNext(ManagedAnimationItem(source: .resource(account.postbox.mediaBox, file.resource), loop: loop, callbacks: callbacks)) }) return ActionDisposable { @@ -91,7 +97,7 @@ public struct InteractiveEmojiConfiguration { var successParameters: [String: InteractiveEmojiSuccessParameters] = [:] if let success = data["emojies_send_dice_success"] as? [String: [String: Double]] { for (key, dict) in success { - if let successValue = dict[""], let successFrame = dict[""] { + if let successValue = dict["value"], let successFrame = dict["frame_start"] { successParameters[key] = InteractiveEmojiSuccessParameters(value: Int(successValue), frame: Int(successFrame)) } } @@ -110,18 +116,20 @@ final class ManagedDiceAnimationNode: ManagedAnimationNode, GenericAnimatedStick private var diceState: ManagedDiceAnimationState? = nil private let disposable = MetaDisposable() + private let configuration = Promise() private let emojis = Promise<[TelegramMediaFile]>() + var success: (() -> Void)? + init(context: AccountContext, emoji: String) { self.context = context self.emoji = emoji - self.context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration]) - |> map { preferencesView -> InteractiveEmojiConfiguration in + self.configuration.set(self.context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration]) + |> map { preferencesView -> InteractiveEmojiConfiguration? in let appConfiguration: AppConfiguration = preferencesView.values[PreferencesKeys.appConfiguration] as? AppConfiguration ?? .defaultValue return InteractiveEmojiConfiguration.with(appConfiguration: appConfiguration) - } - + }) self.emojis.set(loadedStickerPack(postbox: context.account.postbox, network: context.account.network, reference: .dice(emoji), forceActualized: false) |> mapToSignal { stickerPack -> Signal<[TelegramMediaFile], NoError> in switch stickerPack { @@ -155,7 +163,9 @@ final class ManagedDiceAnimationNode: ManagedAnimationNode, GenericAnimatedStick case .rolling: switch diceState { case let .value(value, _): - item = animationItem(account: context.account, emojis: self.emojis.get(), emoji: self.emoji, value: value) + item = animationItem(account: context.account, emojis: self.emojis.get(), configuration: self.configuration.get(), emoji: self.emoji, value: value, successCallback: { [weak self] in + self?.success?() + }) case .rolling: break } @@ -170,7 +180,9 @@ final class ManagedDiceAnimationNode: ManagedAnimationNode, GenericAnimatedStick } else { switch diceState { case let .value(value, immediate): - item = animationItem(account: context.account, emojis: self.emojis.get(), emoji: self.emoji, value: value, immediate: immediate, roll: true) + item = animationItem(account: context.account, emojis: self.emojis.get(), configuration: self.configuration.get(), emoji: self.emoji, value: value, immediate: immediate, roll: true, successCallback: { [weak self] in + self?.success?() + }) case .rolling: item = rollingAnimationItem(account: context.account, emojis: self.emojis.get(), emoji: self.emoji) } diff --git a/submodules/UndoUI/Sources/UndoOverlayController.swift b/submodules/UndoUI/Sources/UndoOverlayController.swift index 673cfc1eac..419844402d 100644 --- a/submodules/UndoUI/Sources/UndoOverlayController.swift +++ b/submodules/UndoUI/Sources/UndoOverlayController.swift @@ -17,7 +17,7 @@ public enum UndoOverlayContent { case swipeToReply(title: String, text: String) case actionSucceeded(title: String, text: String, cancel: String) case stickersModified(title: String, text: String, undo: Bool, info: StickerPackCollectionInfo, topItem: ItemCollectionItem?, account: Account) - case dice(dice: TelegramMediaDice, account: Account, text: String, action: String) + case dice(dice: TelegramMediaDice, account: Account, text: String, action: String?) } public enum UndoOverlayAction { diff --git a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift index 1031a3806c..1b81aab643 100644 --- a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift +++ b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift @@ -290,15 +290,19 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { 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.textNode.attributedText = attributedText - displayUndo = true - undoText = action + if let action = action { + displayUndo = true + undoText = action + } else { + displayUndo = false + } self.originalRemainingSeconds = 5 self.stickerImageSize = CGSize(width: 42.0, height: 42.0) switch dice.emoji { case "🎲": - self.stickerOffset = CGPoint(x: 0.0, y: -8.0) + self.stickerOffset = CGPoint(x: 0.0, y: -7.0) default: break }