Add interactive dice success animation

Fix interactive dice tooltip localization
This commit is contained in:
Ilya Laktyushin 2020-04-23 05:41:38 +04:00
parent c3f2fa5bc3
commit 6e7a5af08d
14 changed files with 4089 additions and 4189 deletions

View File

@ -5474,19 +5474,10 @@ Any member of this group will be able to see messages in the channel.";
"MuteFor.Forever" = "Mute Forever"; "MuteFor.Forever" = "Mute Forever";
"Conversation.Dice" = "Send a 🎲 emoji to any chat to get a random number from Telegram."; "Conversation.Dice.u1F3B2" = "Send a dice emoji to any chat to roll a die.";
"Conversation.Dice.u1F3AF" = "Send a dart emoji to try your luck.";
"Conversation.Dice.🎲" = "Send a dice emoji to any chat to roll a die.";
"Conversation.Dice.🎯" = "Send a dart emoji to try your luck.";
"Conversation.SendDice" = "Send"; "Conversation.SendDice" = "Send";
"ForwardedDices_1" = "Forwarded dice";
"ForwardedDices_2" = "2 forwarded dices";
"ForwardedDices_3_10" = "%@ forwarded dices";
"ForwardedDices_any" = "%@ forwarded dices";
"ForwardedDices_many" = "%@ forwarded dices";
"ForwardedDices_0" = "%@ forwarded dices";
"Conversation.ContextMenuDiscuss" = "Discuss"; "Conversation.ContextMenuDiscuss" = "Discuss";
"CreatePoll.ExplanationHeader" = "EXPLANATION"; "CreatePoll.ExplanationHeader" = "EXPLANATION";

View File

@ -1971,6 +1971,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self?.displayPollSolution(solution: solution, sourceNode: sourceNode, isAutomatic: false) self?.displayPollSolution(solution: solution, sourceNode: sourceNode, isAutomatic: false)
}, displayDiceTooltip: { [weak self] dice in }, displayDiceTooltip: { [weak self] dice in
self?.displayDiceTooltip(dice: dice) self?.displayDiceTooltip(dice: dice)
}, animateDiceSuccess: { [weak self] in
self?.chatDisplayNode.animateQuizCorrectOptionSelected()
}, requestMessageUpdate: { [weak self] id in }, requestMessageUpdate: { [weak self] id in
if let strongSelf = self { if let strongSelf = self {
strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(id) strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(id)
@ -6552,20 +6554,23 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
controller.dismissWithCommitAction() controller.dismissWithCommitAction()
} }
}) })
let value: String? let value: String?
if dice.emoji == "🎲" { switch dice.emoji {
value = self.presentationData.strings.Conversation_Dice_🎲 case "🎲":
} else if dice.emoji == "🎯" { value = self.presentationData.strings.Conversation_Dice_u1F3B2
value = self.presentationData.strings.Conversation_Dice_🎯 case "🎯":
} else { value = self.presentationData.strings.Conversation_Dice_u1F3AF
let key = "Conversation.Dice.\(dice.emoji)" default:
if let string = self.presentationData.strings.primaryComponent.dict[key] { let emojiHex = dice.emoji.unicodeScalars.map({ String(format:"%02x", $0.value) }).joined().uppercased()
value = string let key = "Conversation.Dice.u\(emojiHex)"
} else if let string = self.presentationData.strings.secondaryComponent?.dict[key] { if let string = self.presentationData.strings.primaryComponent.dict[key] {
value = string value = string
} else { } else if let string = self.presentationData.strings.secondaryComponent?.dict[key] {
value = nil value = string
} } else {
value = nil
}
} }
if let value = value { 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 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

View File

@ -109,6 +109,7 @@ public final class ChatControllerInteraction {
let openPollCreation: (Bool?) -> Void let openPollCreation: (Bool?) -> Void
let displayPollSolution: (TelegramMediaPollResults.Solution, ASDisplayNode) -> Void let displayPollSolution: (TelegramMediaPollResults.Solution, ASDisplayNode) -> Void
let displayDiceTooltip: (TelegramMediaDice) -> Void let displayDiceTooltip: (TelegramMediaDice) -> Void
let animateDiceSuccess: () -> Void
let requestMessageUpdate: (MessageId) -> Void let requestMessageUpdate: (MessageId) -> Void
let cancelInteractiveKeyboardGestures: () -> Void let cancelInteractiveKeyboardGestures: () -> Void
@ -125,7 +126,7 @@ public final class ChatControllerInteraction {
var searchTextHighightState: (String, [MessageIndex])? var searchTextHighightState: (String, [MessageIndex])?
var seenOneTimeAnimatedMedia = Set<MessageId>() var seenOneTimeAnimatedMedia = Set<MessageId>()
init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void, openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, tapMessage: ((Message) -> Void)?, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?, Message?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openTheme: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, chatControllerNode: @escaping () -> ASDisplayNode?, reactionContainerNode: @escaping () -> ReactionSelectionParentNode?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOptions: @escaping (MessageId, [Data]) -> Void, requestOpenMessagePollResults: @escaping (MessageId, MediaId) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, scheduleCurrentMessage: @escaping () -> Void, sendScheduledMessagesNow: @escaping ([MessageId]) -> Void, editScheduledMessagesTime: @escaping ([MessageId]) -> Void, performTextSelectionAction: @escaping (UInt32, String, TextSelectionAction) -> Void, updateMessageReaction: @escaping (MessageId, String?) -> Void, openMessageReactions: @escaping (MessageId) -> Void, displaySwipeToReplyHint: @escaping () -> Void, dismissReplyMarkupMessage: @escaping (Message) -> Void, openMessagePollResults: @escaping (MessageId, Data) -> Void, openPollCreation: @escaping (Bool?) -> Void, displayPollSolution: @escaping (TelegramMediaPollResults.Solution, ASDisplayNode) -> Void, displayDiceTooltip: @escaping (TelegramMediaDice) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) { init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void, openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, tapMessage: ((Message) -> Void)?, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?, Message?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openTheme: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, chatControllerNode: @escaping () -> ASDisplayNode?, reactionContainerNode: @escaping () -> ReactionSelectionParentNode?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOptions: @escaping (MessageId, [Data]) -> Void, requestOpenMessagePollResults: @escaping (MessageId, MediaId) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, scheduleCurrentMessage: @escaping () -> Void, sendScheduledMessagesNow: @escaping ([MessageId]) -> Void, editScheduledMessagesTime: @escaping ([MessageId]) -> Void, performTextSelectionAction: @escaping (UInt32, String, TextSelectionAction) -> Void, updateMessageReaction: @escaping (MessageId, String?) -> Void, openMessageReactions: @escaping (MessageId) -> Void, displaySwipeToReplyHint: @escaping () -> Void, dismissReplyMarkupMessage: @escaping (Message) -> Void, openMessagePollResults: @escaping (MessageId, Data) -> Void, openPollCreation: @escaping (Bool?) -> Void, displayPollSolution: @escaping (TelegramMediaPollResults.Solution, ASDisplayNode) -> Void, displayDiceTooltip: @escaping (TelegramMediaDice) -> Void, animateDiceSuccess: @escaping () -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) {
self.openMessage = openMessage self.openMessage = openMessage
self.openPeer = openPeer self.openPeer = openPeer
self.openPeerMention = openPeerMention self.openPeerMention = openPeerMention
@ -185,6 +186,7 @@ public final class ChatControllerInteraction {
self.dismissReplyMarkupMessage = dismissReplyMarkupMessage self.dismissReplyMarkupMessage = dismissReplyMarkupMessage
self.openMessagePollResults = openMessagePollResults self.openMessagePollResults = openMessagePollResults
self.displayDiceTooltip = displayDiceTooltip self.displayDiceTooltip = displayDiceTooltip
self.animateDiceSuccess = animateDiceSuccess
self.requestMessageUpdate = requestMessageUpdate self.requestMessageUpdate = requestMessageUpdate
self.cancelInteractiveKeyboardGestures = cancelInteractiveKeyboardGestures self.cancelInteractiveKeyboardGestures = cancelInteractiveKeyboardGestures
@ -228,6 +230,7 @@ public final class ChatControllerInteraction {
}, openPollCreation: { _ in }, openPollCreation: { _ in
}, displayPollSolution: { _, _ in }, displayPollSolution: { _, _ in
}, displayDiceTooltip: { _ in }, displayDiceTooltip: { _ in
}, animateDiceSuccess: {
}, requestMessageUpdate: { _ in }, requestMessageUpdate: { _ in
}, cancelInteractiveKeyboardGestures: { }, cancelInteractiveKeyboardGestures: {
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,

View File

@ -56,26 +56,6 @@ private struct ChatControllerNodeDerivedLayoutState {
var upperInputPositionBound: CGFloat? var upperInputPositionBound: CGFloat?
} }
public struct InteractiveEmojiConfiguration {
static var defaultValue: InteractiveEmojiConfiguration {
return InteractiveEmojiConfiguration(emojis: [])
}
public let emojis: [String]
fileprivate init(emojis: [String]) {
self.emojis = emojis
}
static func with(appConfiguration: AppConfiguration) -> InteractiveEmojiConfiguration {
if let data = appConfiguration.data, let value = data["emojies_send_dice"] as? [String] {
return InteractiveEmojiConfiguration(emojis: value)
} else {
return .defaultValue
}
}
}
class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
let context: AccountContext let context: AccountContext
let chatLocation: ChatLocation let chatLocation: ChatLocation
@ -149,7 +129,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
var chatPresentationInterfaceState: ChatPresentationInterfaceState var chatPresentationInterfaceState: ChatPresentationInterfaceState
var automaticMediaDownloadSettings: MediaAutoDownloadSettings var automaticMediaDownloadSettings: MediaAutoDownloadSettings
private var interactiveEmojis: [String] = [] private var interactiveEmojis: InteractiveEmojiConfiguration?
private var interactiveEmojisDisposable: Disposable? private var interactiveEmojisDisposable: Disposable?
private let selectedMessagesPromise = Promise<Set<MessageId>?>(nil) private let selectedMessagesPromise = Promise<Set<MessageId>?>(nil)
@ -318,10 +298,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
})) }))
self.interactiveEmojisDisposable = (self.context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration]) self.interactiveEmojisDisposable = (self.context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration])
|> map { preferencesView -> [String] in |> map { preferencesView -> InteractiveEmojiConfiguration in
let appConfiguration: AppConfiguration = preferencesView.values[PreferencesKeys.appConfiguration] as? AppConfiguration ?? .defaultValue let appConfiguration: AppConfiguration = preferencesView.values[PreferencesKeys.appConfiguration] as? AppConfiguration ?? .defaultValue
let configuration = InteractiveEmojiConfiguration.with(appConfiguration: appConfiguration) return InteractiveEmojiConfiguration.with(appConfiguration: appConfiguration)
return configuration.emojis
} }
|> deliverOnMainQueue).start(next: { [weak self] emojis in |> deliverOnMainQueue).start(next: { [weak self] emojis in
if let strongSelf = self { if let strongSelf = self {
@ -2299,7 +2278,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
let effectiveInputText = effectivePresentationInterfaceState.interfaceState.composeInputState.inputText let effectiveInputText = effectivePresentationInterfaceState.interfaceState.composeInputState.inputText
let trimmedInputText = effectiveInputText.string.trimmingCharacters(in: .whitespacesAndNewlines) let trimmedInputText = effectiveInputText.string.trimmingCharacters(in: .whitespacesAndNewlines)
if case let .peer(peerId) = effectivePresentationInterfaceState.chatLocation, peerId.namespace != Namespaces.Peer.SecretChat, self.interactiveEmojis.contains(trimmedInputText) { if case let .peer(peerId) = effectivePresentationInterfaceState.chatLocation, peerId.namespace != Namespaces.Peer.SecretChat, let interactiveEmojis = self.interactiveEmojis, interactiveEmojis.emojis.contains(trimmedInputText) {
messages.append(.message(text: "", attributes: [], mediaReference: AnyMediaReference.standalone(media: TelegramMediaDice(emoji: trimmedInputText)), replyToMessageId: self.chatPresentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil)) messages.append(.message(text: "", attributes: [], mediaReference: AnyMediaReference.standalone(media: TelegramMediaDice(emoji: trimmedInputText)), replyToMessageId: self.chatPresentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil))
} else { } else {
let inputText = convertMarkdownToAttributes(effectiveInputText) let inputText = convertMarkdownToAttributes(effectiveInputText)
@ -2400,123 +2379,5 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
func animateQuizCorrectOptionSelected() { func animateQuizCorrectOptionSelected() {
self.view.insertSubview(ConfettiView(frame: self.view.bounds), aboveSubview: self.historyNode.view) self.view.insertSubview(ConfettiView(frame: self.view.bounds), aboveSubview: self.historyNode.view)
/*class ConfettiView: UIView {
private let direction: Bool
private let confettiViewEmitterLayer = CAEmitterLayer()
private let confettiViewEmitterCell = CAEmitterCell()
init(frame: CGRect, direction: Bool) {
self.direction = direction
super.init(frame: frame)
self.isUserInteractionEnabled = false
self.setupConfettiEmitterLayer()
self.confettiViewEmitterLayer.frame = self.bounds
self.confettiViewEmitterLayer.emitterCells = generateConfettiEmitterCells()
self.layer.addSublayer(self.confettiViewEmitterLayer)
let animation = CAKeyframeAnimation(keyPath: #keyPath(CAEmitterLayer.birthRate))
animation.duration = 0.5
animation.timingFunction = CAMediaTimingFunction(name: .easeIn)
animation.fillMode = .forwards
animation.values = [1, 0, 0]
animation.keyTimes = [0, 0.5, 1]
animation.isRemovedOnCompletion = false
self.confettiViewEmitterLayer.beginTime = CACurrentMediaTime()
self.confettiViewEmitterLayer.birthRate = 1.0
CATransaction.begin()
CATransaction.setCompletionBlock { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, delay: 1.0, removeOnCompletion: false, completion: { _ in
self?.removeFromSuperview()
})
}
self.confettiViewEmitterLayer.add(animation, forKey: nil)
CATransaction.commit()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupConfettiEmitterLayer() {
let emitterWidth: CGFloat = self.bounds.width / 4.0
self.confettiViewEmitterLayer.emitterSize = CGSize(width: emitterWidth, height: 2.0)
self.confettiViewEmitterLayer.emitterShape = .line
self.confettiViewEmitterLayer.emitterPosition = CGPoint(x: direction ? 0.0 : (self.bounds.width - emitterWidth * 0.0), y: self.bounds.height)
}
private func generateConfettiEmitterCells() -> [CAEmitterCell] {
var cells = [CAEmitterCell]()
let cellImageCircle = generateFilledCircleImage(diameter: 4.0, color: .white)!.cgImage!
let cellImageLine = generateImage(CGSize(width: 4.0, height: 10.0), opaque: false, rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(UIColor.white.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.width)))
context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: size.height - size.width), size: CGSize(width: size.width, height: size.width)))
context.fill(CGRect(origin: CGPoint(x: 0.0, y: size.width / 2.0), size: CGSize(width: size.width, height: size.height - size.width)))
})!.cgImage!
for index in 0 ..< 4 {
let cell = CAEmitterCell()
cell.color = self.nextColor(i: index).cgColor
cell.contents = index % 2 == 0 ? cellImageCircle : cellImageLine
cell.birthRate = 60.0
cell.lifetime = 14.0
cell.lifetimeRange = 0
if index % 2 == 0 {
cell.scale = 0.8
cell.scaleRange = 0.4
} else {
cell.scale = 0.5
cell.scaleRange = 0.1
}
cell.velocity = -self.randomVelocity
cell.velocityRange = abs(cell.velocity) * 0.3
cell.yAcceleration = 3000.0
cell.emissionLongitude = (self.direction ? -1.0 : 1.0) * (CGFloat.pi * 0.95)
cell.emissionRange = 0.2
cell.spin = 5.5
cell.spinRange = 1.0
cells.append(cell)
}
return cells
}
var randomNumber: Int {
let dimension = 4
return Int(arc4random_uniform(UInt32(dimension)))
}
var randomVelocity: CGFloat {
let velocities: [CGFloat] = [100.0, 120.0, 130.0, 140.0]
return velocities[self.randomNumber] * 12.0
}
private let colors: [UIColor] = ([
0x56CE6B,
0xCD89D0,
0x1E9AFF,
0xFF8724
] as [UInt32]).map(UIColor.init(rgb:))
private func nextColor(i: Int) -> UIColor {
return self.colors[i % self.colors.count]
}
}
self.view.insertSubview(ConfettiView(frame: self.view.bounds, direction: true), aboveSubview: self.historyNode.view)
self.view.insertSubview(ConfettiView(frame: self.view.bounds, direction: false), aboveSubview: self.historyNode.view)*/
} }
} }

View File

@ -209,7 +209,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
} }
if let telegramDice = self.telegramDice { if let telegramDice = self.telegramDice {
let animationNode = ManagedDiceAnimationNode(context: item.context, dice: telegramDice) let animationNode = ManagedDiceAnimationNode(context: item.context, emoji: telegramDice.emoji)
self.animationNode = animationNode self.animationNode = animationNode
} else { } else {
let animationNode = AnimatedStickerNode() let animationNode = AnimatedStickerNode()

View File

@ -425,6 +425,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
}, openPollCreation: { _ in }, openPollCreation: { _ in
}, displayPollSolution: { _, _ in }, displayPollSolution: { _, _ in
}, displayDiceTooltip: { _ in }, displayDiceTooltip: { _ in
}, animateDiceSuccess: {
}, requestMessageUpdate: { _ in }, requestMessageUpdate: { _ in
}, cancelInteractiveKeyboardGestures: { }, cancelInteractiveKeyboardGestures: {
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, }, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings,

View File

@ -61,8 +61,8 @@ func textStringForForwardedMessage(_ message: Message, strings: PresentationStri
return ("", true) return ("", true)
case _ as TelegramMediaPoll: case _ as TelegramMediaPoll:
return (strings.ForwardedPolls(1), true) return (strings.ForwardedPolls(1), true)
case _ as TelegramMediaDice: case let dice as TelegramMediaDice:
return (strings.ForwardedDices(1), true) return (dice.emoji, true)
default: default:
break break
} }

View File

@ -68,20 +68,61 @@ private func rollingAnimationItem(account: Account, emojis: Signal<[TelegramMedi
} }
} }
private struct InteractiveEmojiSuccessParameters {
let value: Int
let frame: Int
}
public struct InteractiveEmojiConfiguration {
static var defaultValue: InteractiveEmojiConfiguration {
return InteractiveEmojiConfiguration(emojis: [], successParameters: [:])
}
public let emojis: [String]
fileprivate let successParameters: [String: InteractiveEmojiSuccessParameters]
fileprivate init(emojis: [String], successParameters: [String: InteractiveEmojiSuccessParameters]) {
self.emojis = emojis
self.successParameters = successParameters
}
static func with(appConfiguration: AppConfiguration) -> InteractiveEmojiConfiguration {
if let data = appConfiguration.data, let emojis = data["emojies_send_dice"] as? [String] {
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[""] {
successParameters[key] = InteractiveEmojiSuccessParameters(value: Int(successValue), frame: Int(successFrame))
}
}
}
return InteractiveEmojiConfiguration(emojis: emojis, successParameters: successParameters)
} else {
return .defaultValue
}
}
}
final class ManagedDiceAnimationNode: ManagedAnimationNode, GenericAnimatedStickerNode { final class ManagedDiceAnimationNode: ManagedAnimationNode, GenericAnimatedStickerNode {
private let context: AccountContext private let context: AccountContext
private let dice: TelegramMediaDice private let emoji: String
private var diceState: ManagedDiceAnimationState? = nil private var diceState: ManagedDiceAnimationState? = nil
private let disposable = MetaDisposable() private let disposable = MetaDisposable()
private let emojis = Promise<[TelegramMediaFile]>() private let emojis = Promise<[TelegramMediaFile]>()
init(context: AccountContext, dice: TelegramMediaDice) { init(context: AccountContext, emoji: String) {
self.context = context self.context = context
self.dice = dice self.emoji = emoji
self.emojis.set(loadedStickerPack(postbox: context.account.postbox, network: context.account.network, reference: .dice(dice.emoji), forceActualized: false) 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 |> mapToSignal { stickerPack -> Signal<[TelegramMediaFile], NoError> in
switch stickerPack { switch stickerPack {
case let .result(_, items, _): case let .result(_, items, _):
@ -114,14 +155,14 @@ final class ManagedDiceAnimationNode: ManagedAnimationNode, GenericAnimatedStick
case .rolling: case .rolling:
switch diceState { switch diceState {
case let .value(value, _): case let .value(value, _):
item = animationItem(account: context.account, emojis: self.emojis.get(), emoji: self.dice.emoji, value: value) item = animationItem(account: context.account, emojis: self.emojis.get(), emoji: self.emoji, value: value)
case .rolling: case .rolling:
break break
} }
case .value: case .value:
switch diceState { switch diceState {
case .rolling: case .rolling:
item = rollingAnimationItem(account: context.account, emojis: self.emojis.get(), emoji: self.dice.emoji) item = rollingAnimationItem(account: context.account, emojis: self.emojis.get(), emoji: self.emoji)
case .value: case .value:
break break
} }
@ -129,9 +170,9 @@ final class ManagedDiceAnimationNode: ManagedAnimationNode, GenericAnimatedStick
} else { } else {
switch diceState { switch diceState {
case let .value(value, immediate): case let .value(value, immediate):
item = animationItem(account: context.account, emojis: self.emojis.get(), emoji: self.dice.emoji, value: value, immediate: immediate, roll: true) item = animationItem(account: context.account, emojis: self.emojis.get(), emoji: self.emoji, value: value, immediate: immediate, roll: true)
case .rolling: case .rolling:
item = rollingAnimationItem(account: context.account, emojis: self.emojis.get(), emoji: self.dice.emoji) item = rollingAnimationItem(account: context.account, emojis: self.emojis.get(), emoji: self.emoji)
} }
} }

View File

@ -124,6 +124,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
}, openPollCreation: { _ in }, openPollCreation: { _ in
}, displayPollSolution: { _, _ in }, displayPollSolution: { _, _ in
}, displayDiceTooltip: { _ in }, displayDiceTooltip: { _ in
}, animateDiceSuccess: {
}, requestMessageUpdate: { _ in }, requestMessageUpdate: { _ in
}, cancelInteractiveKeyboardGestures: { }, cancelInteractiveKeyboardGestures: {
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false)) }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false))

View File

@ -1529,6 +1529,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}, openPollCreation: { _ in }, openPollCreation: { _ in
}, displayPollSolution: { _, _ in }, displayPollSolution: { _, _ in
}, displayDiceTooltip: { _ in }, displayDiceTooltip: { _ in
}, animateDiceSuccess: {
}, requestMessageUpdate: { _ in }, requestMessageUpdate: { _ in
}, cancelInteractiveKeyboardGestures: { }, cancelInteractiveKeyboardGestures: {
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,

View File

@ -429,6 +429,7 @@ public class PeerMediaCollectionController: TelegramBaseController {
}, openPollCreation: { _ in }, openPollCreation: { _ in
}, displayPollSolution: { _, _ in }, displayPollSolution: { _, _ in
}, displayDiceTooltip: { _ in }, displayDiceTooltip: { _ in
}, animateDiceSuccess: {
}, requestMessageUpdate: { _ in }, requestMessageUpdate: { _ in
}, cancelInteractiveKeyboardGestures: { }, cancelInteractiveKeyboardGestures: {
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,

View File

@ -1137,6 +1137,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
}, openPollCreation: { _ in }, openPollCreation: { _ in
}, displayPollSolution: { _, _ in }, displayPollSolution: { _, _ in
}, displayDiceTooltip: { _ in }, displayDiceTooltip: { _ in
}, animateDiceSuccess: {
}, requestMessageUpdate: { _ in }, requestMessageUpdate: { _ in
}, cancelInteractiveKeyboardGestures: { }, cancelInteractiveKeyboardGestures: {
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,