Various fixes

This commit is contained in:
Ilya Laktyushin 2021-09-13 05:16:12 +03:00
parent c22ca26aae
commit bb7772544c
18 changed files with 235 additions and 66 deletions

View File

@ -22,10 +22,11 @@ public final class ChatMessageItemAssociatedData: Equatable {
public let contactsPeerIds: Set<EnginePeer.Id>
public let channelDiscussionGroup: ChannelDiscussionGroupStatus
public let animatedEmojiStickers: [String: [StickerPackItem]]
public let additionalAnimatedEmojiStickers: [String: [Int: StickerPackItem]]
public let forcedResourceStatus: FileMediaResourceStatus?
public let currentlyPlayingMessageId: EngineMessage.Index?
public init(automaticDownloadPeerType: MediaAutoDownloadPeerType, automaticDownloadNetworkType: MediaAutoDownloadNetworkType, isRecentActions: Bool = false, subject: ChatControllerSubject? = nil, contactsPeerIds: Set<EnginePeer.Id> = Set(), channelDiscussionGroup: ChannelDiscussionGroupStatus = .unknown, animatedEmojiStickers: [String: [StickerPackItem]] = [:], forcedResourceStatus: FileMediaResourceStatus? = nil, currentlyPlayingMessageId: EngineMessage.Index? = nil) {
public init(automaticDownloadPeerType: MediaAutoDownloadPeerType, automaticDownloadNetworkType: MediaAutoDownloadNetworkType, isRecentActions: Bool = false, subject: ChatControllerSubject? = nil, contactsPeerIds: Set<EnginePeer.Id> = Set(), channelDiscussionGroup: ChannelDiscussionGroupStatus = .unknown, animatedEmojiStickers: [String: [StickerPackItem]] = [:], additionalAnimatedEmojiStickers: [String: [Int: StickerPackItem]] = [:], forcedResourceStatus: FileMediaResourceStatus? = nil, currentlyPlayingMessageId: EngineMessage.Index? = nil) {
self.automaticDownloadPeerType = automaticDownloadPeerType
self.automaticDownloadNetworkType = automaticDownloadNetworkType
self.isRecentActions = isRecentActions
@ -33,6 +34,7 @@ public final class ChatMessageItemAssociatedData: Equatable {
self.contactsPeerIds = contactsPeerIds
self.channelDiscussionGroup = channelDiscussionGroup
self.animatedEmojiStickers = animatedEmojiStickers
self.additionalAnimatedEmojiStickers = additionalAnimatedEmojiStickers
self.forcedResourceStatus = forcedResourceStatus
self.currentlyPlayingMessageId = currentlyPlayingMessageId
}
@ -59,6 +61,9 @@ public final class ChatMessageItemAssociatedData: Equatable {
if lhs.animatedEmojiStickers != rhs.animatedEmojiStickers {
return false
}
if lhs.additionalAnimatedEmojiStickers != rhs.additionalAnimatedEmojiStickers {
return false
}
if lhs.forcedResourceStatus != rhs.forcedResourceStatus {
return false
}

View File

@ -166,6 +166,12 @@ public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13
}, initialValues: [:], queue: queue)
}
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>, _ s9: Signal<T9, E>, _ s10: Signal<T10, E>, _ s11: Signal<T11, E>, _ s12: Signal<T12, E>, _ s13: Signal<T13, E>, _ s14: Signal<T14, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9), signalOfAny(s10), signalOfAny(s11), signalOfAny(s12), signalOfAny(s13), signalOfAny(s14)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8, values[8] as! T9, values[9] as! T10, values[10] as! T11, values[11] as! T12, values[12] as! T13, values[13] as! T14)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T, E>(queue: Queue? = nil, _ signals: [Signal<T, E>]) -> Signal<[T], E> {
if signals.count == 0 {
return single([T](), E.self)

View File

@ -669,6 +669,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-2044933984] = { return Api.InputStickerSet.parse_inputStickerSetShortName($0) }
dict[42402760] = { return Api.InputStickerSet.parse_inputStickerSetAnimatedEmoji($0) }
dict[-427863538] = { return Api.InputStickerSet.parse_inputStickerSetDice($0) }
dict[215889721] = { return Api.InputStickerSet.parse_inputStickerSetAnimatedEmojiAnimations($0) }
dict[-1231326505] = { return Api.messages.ChatAdminsWithInvites.parse_chatAdminsWithInvites($0) }
dict[460632885] = { return Api.BotInfo.parse_botInfo($0) }
dict[-2046910401] = { return Api.stickers.SuggestedShortName.parse_suggestedShortName($0) }

View File

@ -16939,6 +16939,7 @@ public extension Api {
case inputStickerSetShortName(shortName: String)
case inputStickerSetAnimatedEmoji
case inputStickerSetDice(emoticon: String)
case inputStickerSetAnimatedEmojiAnimations
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -16972,6 +16973,12 @@ public extension Api {
buffer.appendInt32(-427863538)
}
serializeString(emoticon, buffer: buffer, boxed: false)
break
case .inputStickerSetAnimatedEmojiAnimations:
if boxed {
buffer.appendInt32(215889721)
}
break
}
}
@ -16988,6 +16995,8 @@ public extension Api {
return ("inputStickerSetAnimatedEmoji", [])
case .inputStickerSetDice(let emoticon):
return ("inputStickerSetDice", [("emoticon", emoticon)])
case .inputStickerSetAnimatedEmojiAnimations:
return ("inputStickerSetAnimatedEmojiAnimations", [])
}
}
@ -17033,6 +17042,9 @@ public extension Api {
return nil
}
}
public static func parse_inputStickerSetAnimatedEmojiAnimations(_ reader: BufferReader) -> InputStickerSet? {
return Api.InputStickerSet.inputStickerSetAnimatedEmojiAnimations
}
}
public enum BotInfo: TypeConstructorDescription {

View File

@ -1130,6 +1130,7 @@ public class Account {
if !self.supplementary {
self.managedOperationsDisposable.add(managedAnimatedEmojiUpdates(postbox: self.postbox, network: self.network).start())
self.managedOperationsDisposable.add(managedAnimatedEmojiAnimationsUpdates(postbox: self.postbox, network: self.network).start())
}
self.managedOperationsDisposable.add(managedGreetingStickers(postbox: self.postbox, network: self.network).start())

View File

@ -60,6 +60,8 @@ extension StickerPackReference {
self = .animatedEmoji
case let .inputStickerSetDice(emoticon):
self = .dice(emoticon)
case .inputStickerSetAnimatedEmojiAnimations:
self = .animatedEmojiAnimations
}
}
}

View File

@ -11,3 +11,11 @@ func managedAnimatedEmojiUpdates(postbox: Postbox, network: Network) -> Signal<V
}
return (poll |> then(.complete() |> suspendAwareDelay(2.0 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue()))) |> restart
}
func managedAnimatedEmojiAnimationsUpdates(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
let poll = _internal_loadedStickerPack(postbox: postbox, network: network, reference: .animatedEmojiAnimations, forceActualized: true)
|> mapToSignal { _ -> Signal<Void, NoError> in
return .complete()
}
return (poll |> then(.complete() |> suspendAwareDelay(2.0 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue()))) |> restart
}

View File

@ -2,10 +2,20 @@ import Foundation
import TelegramApi
public struct EmojiInteraction: Equatable {
public let animation: Int
public struct Animation: Equatable {
public let index: Int
public let timeOffset: Float
public init(index: Int, timeOffset: Float) {
self.index = index
self.timeOffset = timeOffset
}
}
public init(animation: Int) {
self.animation = animation
public let animations: [Animation]
public init(animations: [Animation]) {
self.animations = animations
}
public init?(apiDataJson: Api.DataJSON) {
@ -15,10 +25,18 @@ public struct EmojiInteraction: Equatable {
guard let item = decodedData as? [String: Any] else {
return nil
}
guard let animation = item["animation"] as? Int else {
guard let animationsArray = item["a"] as? [Any] else {
return nil
}
self.animation = animation
var animations: [EmojiInteraction.Animation] = []
for animationDict in animationsArray {
if let animationDict = animationDict as? [String: Any] {
if let index = animationDict["i"] as? Int, let timeOffset = animationDict["t"] as? Float {
animations.append(EmojiInteraction.Animation(index: index, timeOffset: timeOffset))
}
}
}
self.animations = animations
} catch {
return nil
}
@ -28,7 +46,7 @@ public struct EmojiInteraction: Equatable {
}
public var apiDataJson: Api.DataJSON {
let dict = ["animation": animation]
let dict = ["v": 1, "a": self.animations.map({ ["i": $0.index, "t": $0.timeOffset] })] as [String : Any]
if let data = try? JSONSerialization.data(withJSONObject: dict, options: []), let dataString = String(data: data, encoding: .utf8) {
return .dataJSON(data: dataString)
} else {

View File

@ -61,7 +61,7 @@ public func addSavedSticker(postbox: Postbox, network: Network, file: TelegramMe
if !found {
fetchReference = packReference
}
case .animatedEmoji, .dice:
case .animatedEmoji, .animatedEmojiAnimations, .dice:
break
}
if let fetchReference = fetchReference {

View File

@ -44,6 +44,7 @@ public struct Namespaces {
public static let EmojiKeywords: Int32 = 2
public static let CloudAnimatedEmoji: Int32 = 3
public static let CloudDice: Int32 = 4
public static let CloudAnimatedEmojiAnimations: Int32 = 5
}
public struct OrderedItemList {

View File

@ -16,6 +16,7 @@ public enum StickerPackReference: PostboxCoding, Hashable, Equatable {
case name(String)
case animatedEmoji
case dice(String)
case animatedEmojiAnimations
public init(decoder: PostboxDecoder) {
switch decoder.decodeInt32ForKey("r", orElse: 0) {
@ -27,6 +28,8 @@ public enum StickerPackReference: PostboxCoding, Hashable, Equatable {
self = .animatedEmoji
case 3:
self = .dice(decoder.decodeStringForKey("e", orElse: "🎲"))
case 4:
self = .animatedEmojiAnimations
default:
self = .name("")
assertionFailure()
@ -47,6 +50,8 @@ public enum StickerPackReference: PostboxCoding, Hashable, Equatable {
case let .dice(emoji):
encoder.encodeInt32(3, forKey: "r")
encoder.encodeString(emoji, forKey: "e")
case .animatedEmojiAnimations:
encoder.encodeInt32(4, forKey: "r")
}
}
@ -76,6 +81,12 @@ public enum StickerPackReference: PostboxCoding, Hashable, Equatable {
} else {
return false
}
case .animatedEmojiAnimations:
if case .animatedEmojiAnimations = rhs {
return true
} else {
return false
}
}
}
}

View File

@ -23,6 +23,9 @@ func cacheStickerPack(transaction: Transaction, info: StickerPackCollectionInfo,
case .animatedEmoji:
namespace = Namespaces.ItemCollection.CloudAnimatedEmoji
id = 0
case .animatedEmojiAnimations:
namespace = Namespaces.ItemCollection.CloudAnimatedEmojiAnimations
id = 0
case let .dice(emoji):
namespace = Namespaces.ItemCollection.CloudDice
id = Int64(murMurHashString32(emoji))
@ -104,6 +107,20 @@ func _internal_cachedStickerPack(postbox: Postbox, network: Network, reference:
} else {
return (.fetching, true, nil)
}
case .animatedEmojiAnimations:
let namespace = Namespaces.ItemCollection.CloudAnimatedEmojiAnimations
let id: ItemCollectionId.Id = 0
if let cached = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedStickerPacks, key: CachedStickerPack.cacheKey(ItemCollectionId(namespace: namespace, id: id)))) as? CachedStickerPack, let info = cached.info {
previousHash = cached.hash
let current: CachedStickerPackResult = .result(info, cached.items, false)
if cached.hash != info.hash {
return (current, true, previousHash)
} else {
return (current, false, previousHash)
}
} else {
return (.fetching, true, nil)
}
}
}
|> mapToSignal { result, loadRemote, previousHash in
@ -187,7 +204,19 @@ func cachedStickerPack(transaction: Transaction, reference: StickerPackReference
}
if let cached = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedStickerPacks, key: CachedStickerPack.cacheKey(ItemCollectionId(namespace: namespace, id: id)))) as? CachedStickerPack, let info = cached.info {
return (info, cached.items, false)
}
}
case .animatedEmojiAnimations:
let namespace = Namespaces.ItemCollection.CloudAnimatedEmojiAnimations
let id: ItemCollectionId.Id = 0
if let currentInfo = transaction.getItemCollectionInfo(collectionId: ItemCollectionId(namespace: namespace, id: id)) as? StickerPackCollectionInfo {
let items = transaction.getItemCollectionItems(collectionId: ItemCollectionId(namespace: namespace, id: id))
if !items.isEmpty {
return (currentInfo, items, true)
}
}
if let cached = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedStickerPacks, key: CachedStickerPack.cacheKey(ItemCollectionId(namespace: namespace, id: id)))) as? CachedStickerPack, let info = cached.info {
return (info, cached.items, false)
}
}
return nil
}

View File

@ -19,6 +19,8 @@ extension StickerPackReference {
return .inputStickerSetAnimatedEmoji
case let .dice(emoji):
return .inputStickerSetDice(emoticon: emoji)
case .animatedEmojiAnimations:
return .inputStickerSetAnimatedEmojiAnimations
}
}
}

View File

@ -40,6 +40,9 @@ func _internal_requestStickerSet(postbox: Postbox, network: Network, reference:
case let .dice(emoji):
collectionId = nil
input = .inputStickerSetDice(emoticon: emoji)
case .animatedEmojiAnimations:
collectionId = nil
input = .inputStickerSetAnimatedEmojiAnimations
}
let localSignal: (ItemCollectionId) -> Signal<(ItemCollectionInfo, [ItemCollectionItem])?, NoError> = { collectionId in

View File

@ -7435,7 +7435,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
strongSelf.chatDisplayNode.historyNode.forEachVisibleItemNode({ itemNode in
if !found, let itemNode = itemNode as? ChatMessageAnimatedStickerItemNode, let item = itemNode.item {
if item.message.text.strippedEmoji == emoticon {
itemNode.playAdditionalAnimation(index: interaction.animation, incoming: true)
for animation in interaction.animations {
if animation.timeOffset > 0.0 {
Queue.mainQueue().after(Double(animation.timeOffset)) {
itemNode.playAdditionalAnimation(index: animation.index)
}
} else {
itemNode.playAdditionalAnimation(index: animation.index)
}
}
found = true
}
}

View File

@ -311,7 +311,7 @@ private final class ChatHistoryTransactionOpaqueState {
}
}
private func extractAssociatedData(chatLocation: ChatLocation, view: MessageHistoryView, automaticDownloadNetworkType: MediaAutoDownloadNetworkType, animatedEmojiStickers: [String: [StickerPackItem]], subject: ChatControllerSubject?, currentlyPlayingMessageId: MessageIndex?) -> ChatMessageItemAssociatedData {
private func extractAssociatedData(chatLocation: ChatLocation, view: MessageHistoryView, automaticDownloadNetworkType: MediaAutoDownloadNetworkType, animatedEmojiStickers: [String: [StickerPackItem]], additionalAnimatedEmojiStickers: [String: [Int: StickerPackItem]], subject: ChatControllerSubject?, currentlyPlayingMessageId: MessageIndex?) -> ChatMessageItemAssociatedData {
var automaticMediaDownloadPeerType: MediaAutoDownloadPeerType = .channel
var contactsPeerIds: Set<PeerId> = Set()
var channelDiscussionGroup: ChatMessageItemAssociatedData.ChannelDiscussionGroupStatus = .unknown
@ -360,7 +360,7 @@ private func extractAssociatedData(chatLocation: ChatLocation, view: MessageHist
}
}
return ChatMessageItemAssociatedData(automaticDownloadPeerType: automaticMediaDownloadPeerType, automaticDownloadNetworkType: automaticDownloadNetworkType, isRecentActions: false, subject: subject, contactsPeerIds: contactsPeerIds, channelDiscussionGroup: channelDiscussionGroup, animatedEmojiStickers: animatedEmojiStickers, currentlyPlayingMessageId: currentlyPlayingMessageId)
return ChatMessageItemAssociatedData(automaticDownloadPeerType: automaticMediaDownloadPeerType, automaticDownloadNetworkType: automaticDownloadNetworkType, isRecentActions: false, subject: subject, contactsPeerIds: contactsPeerIds, channelDiscussionGroup: channelDiscussionGroup, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, currentlyPlayingMessageId: currentlyPlayingMessageId)
}
private extension ChatHistoryLocationInput {
@ -786,6 +786,32 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
return animatedEmojiStickers
}
let additionalAnimatedEmojiStickers = context.engine.stickers.loadedStickerPack(reference: .animatedEmojiAnimations, forceActualized: false)
|> map { animatedEmoji -> [String: [Int: StickerPackItem]] in
let sequence = "0⃣1⃣2⃣3⃣4⃣5⃣6⃣7⃣8⃣9"
var animatedEmojiStickers: [String: [Int: StickerPackItem]] = [:]
switch animatedEmoji {
case let .result(_, items, _):
for case let item as StickerPackItem in items {
let indexKeys = item.getStringRepresentationsOfIndexKeys()
if indexKeys.count > 1, let emoji = indexKeys.first, let indexEmoji = indexKeys.last?.first {
if let strIndex = sequence.firstIndex(of: indexEmoji) {
let emoji = emoji.strippedEmoji
let index = sequence.distance(from: sequence.startIndex, to: strIndex)
if animatedEmojiStickers[emoji] != nil {
animatedEmojiStickers[emoji]![index] = item
} else {
animatedEmojiStickers[emoji] = [index: item]
}
}
}
}
default:
break
}
return animatedEmojiStickers
}
let previousHistoryAppearsCleared = Atomic<Bool?>(value: nil)
let updatingMedia = context.account.pendingUpdateMessageManager.updatingMessageMedia
@ -869,11 +895,12 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
self.pendingUnpinnedAllMessagesPromise.get(),
self.pendingRemovedMessagesPromise.get(),
animatedEmojiStickers,
additionalAnimatedEmojiStickers,
customChannelDiscussionReadState,
customThreadOutgoingReadState,
self.currentlyPlayingMessageIdPromise.get(),
adMessages
).start(next: { [weak self] update, chatPresentationData, selectedMessages, updatingMedia, networkType, historyAppearsCleared, pendingUnpinnedAllMessages, pendingRemovedMessages, animatedEmojiStickers, customChannelDiscussionReadState, customThreadOutgoingReadState, currentlyPlayingMessageId, adMessages in
).start(next: { [weak self] update, chatPresentationData, selectedMessages, updatingMedia, networkType, historyAppearsCleared, pendingUnpinnedAllMessages, pendingRemovedMessages, animatedEmojiStickers, additionalAnimatedEmojiStickers, customChannelDiscussionReadState, customThreadOutgoingReadState, currentlyPlayingMessageId, adMessages in
func applyHole() {
Queue.mainQueue().async {
if let strongSelf = self {
@ -956,7 +983,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
reverse = reverseValue
}
let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType, animatedEmojiStickers: animatedEmojiStickers, subject: subject, currentlyPlayingMessageId: currentlyPlayingMessageId)
let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, subject: subject, currentlyPlayingMessageId: currentlyPlayingMessageId)
let filteredEntries = chatHistoryEntriesForView(
location: chatLocation,

View File

@ -168,9 +168,12 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
private(set) var placeholderNode: StickerShimmerEffectNode
private(set) var animationNode: GenericAnimatedStickerNode?
private var animationSize: CGSize?
private var additionalAnimationNodes: [ChatMessageTransitionNode.DecorationItemNode] = []
private var didSetUpAnimationNode = false
private var isPlaying = false
private var additionalAnimationNodes: [ChatMessageTransitionNode.DecorationItemNode] = []
private var enqueuedAdditionalAnimations: [(Int, Double)] = []
private var additionalAnimationsCommitTimer: SwiftSignalKit.Timer?
private var swipeToReplyNode: ChatMessageSwipeToReplyNode?
private var swipeToReplyFeedback: HapticFeedback?
@ -183,6 +186,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
var emojiFile: TelegramMediaFile?
var telegramDice: TelegramMediaDice?
private let disposable = MetaDisposable()
private let disposables = DisposableSet()
private var forwardInfoNode: ChatMessageForwardInfoNode?
private var forwardBackgroundNode: NavigationBackgroundNode?
@ -305,7 +309,9 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
deinit {
self.disposable.dispose()
self.disposables.dispose()
self.mediaStatusDisposable.set(nil)
self.additionalAnimationsCommitTimer?.invalidate()
}
required init?(coder aDecoder: NSCoder) {
@ -515,6 +521,12 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
self.disposable.set(freeMediaFileInteractiveFetched(account: item.context.account, fileReference: .standalone(media: emojiFile)).start())
}
self.updateVisibility()
if let animationItems = item.associatedData.additionalAnimatedEmojiStickers[item.message.text.strippedEmoji] {
for (_, animationItem) in animationItems {
self.disposables.add(freeMediaFileInteractiveFetched(account: item.context.account, fileReference: .standalone(media: animationItem.file)).start())
}
}
}
}
}
@ -1306,32 +1318,51 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
}
}
func playAdditionalAnimation(index: Int, incoming: Bool) {
private func startAdditionalAnimationsCommitTimer() {
guard self.additionalAnimationsCommitTimer == nil else {
return
}
let timer = SwiftSignalKit.Timer(timeout: 1.0, repeat: false, completion: { [weak self] in
self?.commitEnqueuedAnimations()
self?.additionalAnimationsCommitTimer?.invalidate()
self?.additionalAnimationsCommitTimer = nil
}, queue: Queue.mainQueue())
self.additionalAnimationsCommitTimer = timer
timer.start()
}
private func commitEnqueuedAnimations() {
guard let item = self.item, !self.enqueuedAdditionalAnimations.isEmpty else {
return
}
let textEmoji = item.message.text.strippedEmoji
let enqueuedAnimations = self.enqueuedAdditionalAnimations
self.enqueuedAdditionalAnimations.removeAll()
guard let startTimestamp = enqueuedAnimations.first?.1 else {
return
}
var animations: [EmojiInteraction.Animation] = []
for (index, timestamp) in enqueuedAnimations {
animations.append(EmojiInteraction.Animation(index: index, timeOffset: Float(max(0.0, timestamp - startTimestamp))))
}
item.context.account.updateLocalInputActivity(peerId: PeerActivitySpace(peerId: item.message.id.peerId, category: .global), activity: .interactingWithEmoji(emoticon: textEmoji, interaction: EmojiInteraction(animations: animations)), isPresent: true)
}
func playAdditionalAnimation(index: Int) {
guard let item = self.item else {
return
}
let textEmoji = item.message.text.strippedEmoji
let animationName: String?
switch textEmoji {
case "":
if index == 2 {
animationName = "TestHearts2"
} else {
animationName = "TestHearts"
}
case "🎆":
animationName = "TestFireworks"
default:
animationName = nil
}
guard let animationName = animationName else {
guard let animationItems = item.associatedData.additionalAnimatedEmojiStickers[textEmoji], index < 10, let file = animationItems[index]?.file else {
return
}
let source = AnimatedStickerNodeLocalFileSource(name: animationName)
guard let path = source.path, let animationSize = self.animationSize, let animationNode = self.animationNode, self.additionalAnimationNodes.count < 4 else {
let source = AnimatedStickerResourceSource(account: item.context.account, resource: file.resource, fitzModifier: nil)
guard let animationSize = self.animationSize, let animationNode = self.animationNode, self.additionalAnimationNodes.count < 4 else {
return
}
@ -1340,12 +1371,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
}
let incomingMessage = item.message.effectivelyIncoming(item.context.account.peerId)
self.supernode?.view.bringSubviewToFront(self.view)
let resource = BundleResource(name: animationName, path: path)
let pathPrefix = item.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(resource.id)
let pathPrefix = item.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(file.resource.id)
let additionalAnimationNode = AnimatedStickerNode()
additionalAnimationNode.setup(source: source, width: Int(animationSize.width * 2.0), height: Int(animationSize.height * 2.0), playbackMode: .once, mode: .direct(cachePathPrefix: pathPrefix))
var animationFrame = animationNode.frame.insetBy(dx: -animationNode.frame.width, dy: -animationNode.frame.height)
@ -1380,10 +1407,6 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
self.additionalAnimationNodes.append(decorationNode)
additionalAnimationNode.play()
if !incoming {
item.context.account.updateLocalInputActivity(peerId: PeerActivitySpace(peerId: item.message.id.peerId, category: .global), activity: .interactingWithEmoji(emoticon: textEmoji, interaction: EmojiInteraction(animation: index)), isPresent: true)
}
}
private func gestureRecognized(gesture: TapLongTapOrDoubleTapGesture, location: CGPoint, recognizer: TapLongTapOrDoubleTapGestureRecognizer?) -> InternalBubbleTapAction? {
@ -1484,29 +1507,51 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
let heart = 0x2764
let peach = 0x1F351
let coffin = 0x26B0
let fireworks = 0x1F386
let appConfiguration = item.context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration])
|> take(1)
|> map { view in
return view.values[PreferencesKeys.appConfiguration] as? AppConfiguration ?? .defaultValue
}
if let text = self.item?.message.text, var firstScalar = text.unicodeScalars.first {
let text = item.message.text
if var firstScalar = text.unicodeScalars.first {
var textEmoji = text.strippedEmoji
let originalTextEmoji = textEmoji
if beatingHearts.contains(firstScalar.value) {
textEmoji = "❤️"
firstScalar = UnicodeScalar(heart)!
}
return .optionalAction({
if firstScalar.value == heart {
if self.additionalAnimationNodes.count % 2 == 0 {
self.playAdditionalAnimation(index: 1, incoming: false)
} else {
self.playAdditionalAnimation(index: 2, incoming: false)
if let animationItems = item.associatedData.additionalAnimatedEmojiStickers[originalTextEmoji] {
self.startAdditionalAnimationsCommitTimer()
let timestamp = CACurrentMediaTime()
let previousAnimation = self.enqueuedAdditionalAnimations.last
var availableAnimations = animationItems
var delay: Double = 0.0
if availableAnimations.count > 1, let (previousIndex, _) = previousAnimation {
availableAnimations.removeValue(forKey: previousIndex)
}
if let (_, previousTimestamp) = previousAnimation {
delay = min(0.2, max(0.0, previousTimestamp + 0.2 - timestamp))
}
if let index = availableAnimations.randomElement()?.0 {
if delay > 0.0 {
Queue.mainQueue().after(delay) {
self.enqueuedAdditionalAnimations.append((index, timestamp + delay))
self.playAdditionalAnimation(index: index)
if self.additionalAnimationsCommitTimer == nil {
self.startAdditionalAnimationsCommitTimer()
}
}
} else {
self.enqueuedAdditionalAnimations.append((index, timestamp))
self.playAdditionalAnimation(index: index)
}
}
} else if firstScalar.value == fireworks {
self.playAdditionalAnimation(index: 1, incoming: false)
}
if shouldPlay {

View File

@ -392,12 +392,7 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode {
let text = NSAttributedString(string: item.strings.Conversation_Theme_NoTheme, font: Font.semibold(15.0), textColor: item.theme.actionSheet.controlAccentColor)
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: text, backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
var emoticon = item.emoticon
if emoticon == "🦁" {
emoticon = "🌳"
} else if emoticon == "🔮" {
emoticon = "🎆"
}
let emoticon = item.emoticon
let title = NSAttributedString(string: emoticon != nil ? "" : "", font: Font.regular(22.0), textColor: .black)
let (_, emojiApply) = makeEmojiLayout(TextNodeLayoutArguments(attributedString: title, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
@ -823,12 +818,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
var entries: [ThemeSettingsThemeEntry] = []
entries.append(ThemeSettingsThemeEntry(index: 0, emoticon: nil, emojiFile: nil, themeReference: nil, selected: selectedEmoticon == nil, theme: presentationData.theme, strings: presentationData.strings, wallpaper: nil))
for theme in themes {
var emoticon = theme.emoji
if emoticon == "🦁" {
emoticon = "🌳"
} else if emoticon == "🔮" {
emoticon = "🎆"
}
let emoticon = theme.emoji
entries.append(ThemeSettingsThemeEntry(index: entries.count, emoticon: theme.emoji, emojiFile: animatedEmojiStickers[emoticon]?.first?.file, themeReference: .cloud(PresentationCloudTheme(theme: isDarkAppearance ? theme.darkTheme : theme.theme, resolvedWallpaper: nil, creatorAccountId: nil)), selected: selectedEmoticon == theme.emoji, theme: presentationData.theme, strings: presentationData.strings, wallpaper: nil))
}