mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-06 17:00:13 +00:00
Various improvements
This commit is contained in:
parent
095b9d5058
commit
519370cb3c
@ -133,16 +133,18 @@ public final class BotCheckoutController: ViewController {
|
|||||||
self?.present(c, in: .window(.root), with: a)
|
self?.present(c, in: .window(.root), with: a)
|
||||||
}, dismissAnimated: { [weak self] in
|
}, dismissAnimated: { [weak self] in
|
||||||
self?.dismiss()
|
self?.dismiss()
|
||||||
}, completed: self.completed)
|
}, completed: { [weak self] currencyValue, receiptMessageId in
|
||||||
|
self?.complete(currencyValue: currencyValue, receiptMessageId: receiptMessageId)
|
||||||
|
})
|
||||||
|
|
||||||
displayNode.dismiss = { [weak self] in
|
displayNode.dismiss = { [weak self] in
|
||||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||||
}
|
}
|
||||||
displayNode.pending = { [weak self] in
|
displayNode.pending = { [weak self] in
|
||||||
self?.pending()
|
self?.setPending()
|
||||||
}
|
}
|
||||||
displayNode.failed = { [weak self] in
|
displayNode.failed = { [weak self] in
|
||||||
self?.failed()
|
self?.fail()
|
||||||
}
|
}
|
||||||
|
|
||||||
self.displayNode = displayNode
|
self.displayNode = displayNode
|
||||||
@ -150,6 +152,12 @@ public final class BotCheckoutController: ViewController {
|
|||||||
self._ready.set(displayNode.ready)
|
self._ready.set(displayNode.ready)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override public func viewWillDisappear(_ animated: Bool) {
|
||||||
|
super.viewWillDisappear(animated)
|
||||||
|
|
||||||
|
self.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
override public func viewDidAppear(_ animated: Bool) {
|
override public func viewDidAppear(_ animated: Bool) {
|
||||||
super.viewDidAppear(animated)
|
super.viewDidAppear(animated)
|
||||||
|
|
||||||
@ -167,8 +175,40 @@ public final class BotCheckoutController: ViewController {
|
|||||||
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition, additionalInsets: UIEdgeInsets())
|
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition, additionalInsets: UIEdgeInsets())
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func cancelPressed() {
|
private var didCancel = false
|
||||||
|
private func cancel() {
|
||||||
|
guard !self.didCancel && !self.didFail && !self.didComplete else {
|
||||||
|
return
|
||||||
|
}
|
||||||
self.cancelled()
|
self.cancelled()
|
||||||
|
}
|
||||||
|
|
||||||
|
private var didFail = false
|
||||||
|
private func fail() {
|
||||||
|
guard !self.didCancel && !self.didFail && !self.didComplete else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.failed()
|
||||||
|
}
|
||||||
|
|
||||||
|
private var didComplete = false
|
||||||
|
private func complete(currencyValue: String, receiptMessageId: EngineMessage.Id?) {
|
||||||
|
guard !self.didCancel && !self.didFail && !self.didComplete else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.completed(currencyValue, receiptMessageId)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var isPending = false
|
||||||
|
private func setPending() {
|
||||||
|
guard !self.isPending && !self.didCancel && !self.didFail && !self.didComplete else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.pending()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func cancelPressed() {
|
||||||
|
self.cancel()
|
||||||
self.dismiss()
|
self.dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -444,7 +444,8 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.updateHighlight(animated: true)
|
strongSelf.isButtonHighlighted = highlighted
|
||||||
|
strongSelf.updateHighlight(animated: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.buttonNode.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside)
|
self.buttonNode.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside)
|
||||||
@ -545,6 +546,7 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
|||||||
self.textNode.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
self.textNode.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
self.placeholderNode.layer.animateAlpha(from: self.placeholderNode.alpha, to: 1.0, duration: 0.2)
|
self.placeholderNode.layer.animateAlpha(from: self.placeholderNode.alpha, to: 1.0, duration: 0.2)
|
||||||
}
|
}
|
||||||
|
self.textNode.visibilityRect = CGRect.infinite
|
||||||
|
|
||||||
var contentHeight = textLayout.size.height
|
var contentHeight = textLayout.size.height
|
||||||
if contentHeight.isZero {
|
if contentHeight.isZero {
|
||||||
@ -593,7 +595,7 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateHighlight(animated: Bool) {
|
func updateHighlight(animated: Bool) {
|
||||||
if self.buttonNode.isHighlighted || self.isHighlighted {
|
if self.isButtonHighlighted || self.isHighlighted {
|
||||||
self.highlightBackgroundNode.alpha = 1.0
|
self.highlightBackgroundNode.alpha = 1.0
|
||||||
} else {
|
} else {
|
||||||
if animated {
|
if animated {
|
||||||
@ -606,8 +608,8 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var isButtonHighlighted = false
|
||||||
var isHighlighted = false
|
private var isHighlighted = false
|
||||||
func setHighlighted(_ highlighted: Bool) {
|
func setHighlighted(_ highlighted: Bool) {
|
||||||
guard self.isHighlighted != highlighted else {
|
guard self.isHighlighted != highlighted else {
|
||||||
return
|
return
|
||||||
|
|||||||
@ -98,6 +98,16 @@ public func chatMessageGalleryControllerData(context: AccountContext, chatLocati
|
|||||||
var galleryMedia: Media?
|
var galleryMedia: Media?
|
||||||
var otherMedia: Media?
|
var otherMedia: Media?
|
||||||
var instantPageMedia: (TelegramMediaWebpage, [InstantPageGalleryEntry])?
|
var instantPageMedia: (TelegramMediaWebpage, [InstantPageGalleryEntry])?
|
||||||
|
if message.media.isEmpty, let entities = message.textEntitiesAttribute?.entities, entities.count == 1, let firstEntity = entities.first, case let .CustomEmoji(_, fileId) = firstEntity.type, let file = message.associatedMedia[MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)] as? TelegramMediaFile {
|
||||||
|
for attribute in file.attributes {
|
||||||
|
if case let .CustomEmoji(_, _, reference) = attribute {
|
||||||
|
if let reference = reference {
|
||||||
|
return .stickerPack(reference)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
for media in message.media {
|
for media in message.media {
|
||||||
if let action = media as? TelegramMediaAction {
|
if let action = media as? TelegramMediaAction {
|
||||||
switch action.action {
|
switch action.action {
|
||||||
|
|||||||
Binary file not shown.
BIN
submodules/PremiumUI/Resources/particles2.png
Normal file
BIN
submodules/PremiumUI/Resources/particles2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
BIN
submodules/PremiumUI/Resources/star.scn
Normal file
BIN
submodules/PremiumUI/Resources/star.scn
Normal file
Binary file not shown.
Binary file not shown.
@ -90,6 +90,7 @@ class PremiumStarComponent: Component {
|
|||||||
self.sceneView.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
|
self.sceneView.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
|
||||||
self.sceneView.isUserInteractionEnabled = false
|
self.sceneView.isUserInteractionEnabled = false
|
||||||
self.sceneView.preferredFramesPerSecond = 60
|
self.sceneView.preferredFramesPerSecond = 60
|
||||||
|
self.sceneView.isJitteringEnabled = true
|
||||||
|
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
|
||||||
@ -367,7 +368,7 @@ class PremiumStarComponent: Component {
|
|||||||
guard let scene = self.sceneView.scene, let node = scene.rootNode.childNode(withName: "star", recursively: false) else {
|
guard let scene = self.sceneView.scene, let node = scene.rootNode.childNode(withName: "star", recursively: false) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let animation = CABasicAnimation(keyPath: "scale")
|
let animation = CABasicAnimation(keyPath: "scale")
|
||||||
animation.duration = 2.0
|
animation.duration = 2.0
|
||||||
animation.fromValue = NSValue(scnVector3: SCNVector3(x: 0.1, y: 0.1, z: 0.1))
|
animation.fromValue = NSValue(scnVector3: SCNVector3(x: 0.1, y: 0.1, z: 0.1))
|
||||||
@ -375,7 +376,7 @@ class PremiumStarComponent: Component {
|
|||||||
animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
|
animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
|
||||||
animation.autoreverses = true
|
animation.autoreverses = true
|
||||||
animation.repeatCount = .infinity
|
animation.repeatCount = .infinity
|
||||||
|
|
||||||
node.addAnimation(animation, forKey: "scale")
|
node.addAnimation(animation, forKey: "scale")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,23 +433,32 @@ class PremiumStarComponent: Component {
|
|||||||
self.previousInteractionTimestamp = currentTime
|
self.previousInteractionTimestamp = currentTime
|
||||||
self.delayTapsTill = currentTime + 0.85
|
self.delayTapsTill = currentTime + 0.85
|
||||||
|
|
||||||
if explode, let node = scene.rootNode.childNode(withName: "swirl", recursively: false), let particles = scene.rootNode.childNode(withName: "particles", recursively: false) {
|
if explode, let node = scene.rootNode.childNode(withName: "swirl", recursively: false), let particlesLeft = scene.rootNode.childNode(withName: "particles_left", recursively: false), let particlesRight = scene.rootNode.childNode(withName: "particles_right", recursively: false) {
|
||||||
if let particleSystem = particles.particleSystems?.first {
|
if let leftParticleSystem = particlesLeft.particleSystems?.first, let rightParticleSystem = particlesRight.particleSystems?.first {
|
||||||
particleSystem.particleColorVariation = SCNVector4(0.15, 0.2, 0.15, 0.3)
|
leftParticleSystem.speedFactor = 1.3
|
||||||
particleSystem.speedFactor = 2.0
|
leftParticleSystem.particleVelocity = 2.4
|
||||||
particleSystem.particleVelocity = 2.2
|
leftParticleSystem.birthRate = 24.0
|
||||||
particleSystem.birthRate = 4.0
|
leftParticleSystem.particleLifeSpan = 4.0
|
||||||
particleSystem.particleLifeSpan = 2.0
|
|
||||||
|
rightParticleSystem.speedFactor = 1.3
|
||||||
|
rightParticleSystem.particleVelocity = 2.4
|
||||||
|
rightParticleSystem.birthRate = 24.0
|
||||||
|
rightParticleSystem.particleLifeSpan = 4.0
|
||||||
|
|
||||||
node.physicsField?.isActive = true
|
node.physicsField?.isActive = true
|
||||||
Queue.mainQueue().after(1.0) {
|
Queue.mainQueue().after(1.0) {
|
||||||
node.physicsField?.isActive = false
|
node.physicsField?.isActive = false
|
||||||
particles.particleSystems?.first?.birthRate = 1.2
|
|
||||||
particleSystem.particleVelocity = 1.0
|
|
||||||
particleSystem.particleLifeSpan = 4.0
|
|
||||||
|
|
||||||
let animation = POPBasicAnimation()
|
leftParticleSystem.birthRate = 9.0
|
||||||
animation.property = (POPAnimatableProperty.property(withName: "speedFactor", initializer: { property in
|
leftParticleSystem.particleVelocity = 1.2
|
||||||
|
leftParticleSystem.particleLifeSpan = 3.0
|
||||||
|
|
||||||
|
rightParticleSystem.birthRate = 9.0
|
||||||
|
rightParticleSystem.particleVelocity = 1.2
|
||||||
|
rightParticleSystem.particleLifeSpan = 3.0
|
||||||
|
|
||||||
|
let leftAnimation = POPBasicAnimation()
|
||||||
|
leftAnimation.property = (POPAnimatableProperty.property(withName: "speedFactor", initializer: { property in
|
||||||
property?.readBlock = { particleSystem, values in
|
property?.readBlock = { particleSystem, values in
|
||||||
values?.pointee = (particleSystem as! SCNParticleSystem).speedFactor
|
values?.pointee = (particleSystem as! SCNParticleSystem).speedFactor
|
||||||
}
|
}
|
||||||
@ -457,11 +467,27 @@ class PremiumStarComponent: Component {
|
|||||||
}
|
}
|
||||||
property?.threshold = 0.01
|
property?.threshold = 0.01
|
||||||
}) as! POPAnimatableProperty)
|
}) as! POPAnimatableProperty)
|
||||||
animation.fromValue = 2.0 as NSNumber
|
leftAnimation.fromValue = 1.2 as NSNumber
|
||||||
animation.toValue = 1.0 as NSNumber
|
leftAnimation.toValue = 0.85 as NSNumber
|
||||||
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
|
leftAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
|
||||||
animation.duration = 0.5
|
leftAnimation.duration = 0.5
|
||||||
particleSystem.pop_add(animation, forKey: "speedFactor")
|
leftParticleSystem.pop_add(leftAnimation, forKey: "speedFactor")
|
||||||
|
|
||||||
|
let rightAnimation = POPBasicAnimation()
|
||||||
|
rightAnimation.property = (POPAnimatableProperty.property(withName: "speedFactor", initializer: { property in
|
||||||
|
property?.readBlock = { particleSystem, values in
|
||||||
|
values?.pointee = (particleSystem as! SCNParticleSystem).speedFactor
|
||||||
|
}
|
||||||
|
property?.writeBlock = { particleSystem, values in
|
||||||
|
(particleSystem as! SCNParticleSystem).speedFactor = values!.pointee
|
||||||
|
}
|
||||||
|
property?.threshold = 0.01
|
||||||
|
}) as! POPAnimatableProperty)
|
||||||
|
rightAnimation.fromValue = 1.2 as NSNumber
|
||||||
|
rightAnimation.toValue = 0.85 as NSNumber
|
||||||
|
rightAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
|
||||||
|
rightAnimation.duration = 0.5
|
||||||
|
rightParticleSystem.pop_add(rightAnimation, forKey: "speedFactor")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,18 +5,22 @@ import SwiftSignalKit
|
|||||||
|
|
||||||
private struct SearchStickersConfiguration {
|
private struct SearchStickersConfiguration {
|
||||||
static var defaultValue: SearchStickersConfiguration {
|
static var defaultValue: SearchStickersConfiguration {
|
||||||
return SearchStickersConfiguration(cacheTimeout: 86400)
|
return SearchStickersConfiguration(cacheTimeout: 86400, normalStickersPerPremium: 2, premiumStickersCount: 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
public let cacheTimeout: Int32
|
public let cacheTimeout: Int32
|
||||||
|
public let normalStickersPerPremiumCount: Int32
|
||||||
|
public let premiumStickersCount: Int32
|
||||||
|
|
||||||
fileprivate init(cacheTimeout: Int32) {
|
fileprivate init(cacheTimeout: Int32, normalStickersPerPremium: Int32, premiumStickersCount: Int32) {
|
||||||
self.cacheTimeout = cacheTimeout
|
self.cacheTimeout = cacheTimeout
|
||||||
|
self.normalStickersPerPremiumCount = normalStickersPerPremium
|
||||||
|
self.premiumStickersCount = premiumStickersCount
|
||||||
}
|
}
|
||||||
|
|
||||||
static func with(appConfiguration: AppConfiguration) -> SearchStickersConfiguration {
|
static func with(appConfiguration: AppConfiguration) -> SearchStickersConfiguration {
|
||||||
if let data = appConfiguration.data, let value = data["stickers_emoji_cache_time"] as? Double {
|
if let data = appConfiguration.data, let cacheTimeoutValue = data["stickers_emoji_cache_time"] as? Double, let normalStickersPerPremiumValue = data["stickers_normal_by_emoji_per_premium_num"] as? Double, let premiumStickersCountValue = data["stickers_premium_by_emoji_num "] as? Double {
|
||||||
return SearchStickersConfiguration(cacheTimeout: Int32(value))
|
return SearchStickersConfiguration(cacheTimeout: Int32(cacheTimeoutValue), normalStickersPerPremium: Int32(normalStickersPerPremiumValue), premiumStickersCount: Int32(premiumStickersCountValue))
|
||||||
} else {
|
} else {
|
||||||
return .defaultValue
|
return .defaultValue
|
||||||
}
|
}
|
||||||
@ -85,7 +89,7 @@ func _internal_searchStickers(account: Account, query: String, scope: SearchStic
|
|||||||
if query == "\u{2764}" {
|
if query == "\u{2764}" {
|
||||||
query = "\u{2764}\u{FE0F}"
|
query = "\u{2764}\u{FE0F}"
|
||||||
}
|
}
|
||||||
return account.postbox.transaction { transaction -> ([FoundStickerItem], CachedStickerQueryResult?, Bool) in
|
return account.postbox.transaction { transaction -> ([FoundStickerItem], CachedStickerQueryResult?, Bool, SearchStickersConfiguration) in
|
||||||
let isPremium = transaction.getPeer(account.peerId)?.isPremium ?? false
|
let isPremium = transaction.getPeer(account.peerId)?.isPremium ?? false
|
||||||
|
|
||||||
var result: [FoundStickerItem] = []
|
var result: [FoundStickerItem] = []
|
||||||
@ -198,8 +202,8 @@ func _internal_searchStickers(account: Account, query: String, scope: SearchStic
|
|||||||
cached = nil
|
cached = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return (result, cached, isPremium)
|
return (result, cached, isPremium, searchStickersConfiguration)
|
||||||
} |> mapToSignal { localItems, cached, isPremium -> Signal<[FoundStickerItem], NoError> in
|
} |> mapToSignal { localItems, cached, isPremium, searchStickersConfiguration -> Signal<[FoundStickerItem], NoError> in
|
||||||
var tempResult: [FoundStickerItem] = localItems
|
var tempResult: [FoundStickerItem] = localItems
|
||||||
if !scope.contains(.remote) {
|
if !scope.contains(.remote) {
|
||||||
return .single(tempResult)
|
return .single(tempResult)
|
||||||
@ -237,19 +241,19 @@ func _internal_searchStickers(account: Account, query: String, scope: SearchStic
|
|||||||
case let .stickers(hash, stickers):
|
case let .stickers(hash, stickers):
|
||||||
var items: [FoundStickerItem] = []
|
var items: [FoundStickerItem] = []
|
||||||
var animatedItems: [FoundStickerItem] = []
|
var animatedItems: [FoundStickerItem] = []
|
||||||
|
var premiumItems: [FoundStickerItem] = []
|
||||||
|
|
||||||
var result: [FoundStickerItem] = localItems
|
var allItems: [FoundStickerItem] = localItems
|
||||||
let currentItemIds = Set<MediaId>(result.map { $0.file.fileId })
|
let currentItemIds = Set<MediaId>(allItems.map { $0.file.fileId })
|
||||||
|
|
||||||
var files: [TelegramMediaFile] = []
|
var files: [TelegramMediaFile] = []
|
||||||
for sticker in stickers {
|
for sticker in stickers {
|
||||||
if let file = telegramMediaFileFromApiDocument(sticker), let id = file.id {
|
if let file = telegramMediaFileFromApiDocument(sticker), let id = file.id {
|
||||||
if file.isPremiumSticker && !isPremium {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
files.append(file)
|
files.append(file)
|
||||||
if !currentItemIds.contains(id) {
|
if !currentItemIds.contains(id) {
|
||||||
if file.isAnimatedSticker || file.isVideoSticker {
|
if file.isPremiumSticker {
|
||||||
|
premiumItems.append(FoundStickerItem(file: file, stringRepresentations: []))
|
||||||
|
} else if file.isAnimatedSticker || file.isVideoSticker {
|
||||||
animatedItems.append(FoundStickerItem(file: file, stringRepresentations: []))
|
animatedItems.append(FoundStickerItem(file: file, stringRepresentations: []))
|
||||||
} else {
|
} else {
|
||||||
items.append(FoundStickerItem(file: file, stringRepresentations: []))
|
items.append(FoundStickerItem(file: file, stringRepresentations: []))
|
||||||
@ -257,10 +261,24 @@ func _internal_searchStickers(account: Account, query: String, scope: SearchStic
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result.append(contentsOf: animatedItems)
|
allItems.append(contentsOf: animatedItems)
|
||||||
result.append(contentsOf: items)
|
allItems.append(contentsOf: items)
|
||||||
|
|
||||||
|
var result: [FoundStickerItem] = []
|
||||||
|
if isPremium {
|
||||||
|
if searchStickersConfiguration.normalStickersPerPremiumCount == 0 {
|
||||||
|
result.append(contentsOf: premiumItems)
|
||||||
|
result.append(contentsOf: allItems)
|
||||||
|
} else {
|
||||||
|
result.append(contentsOf: premiumItems)
|
||||||
|
result.append(contentsOf: allItems)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.append(contentsOf: allItems)
|
||||||
|
result.append(contentsOf: premiumItems.prefix(max(0, Int(searchStickersConfiguration.premiumStickersCount))))
|
||||||
|
}
|
||||||
|
|
||||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||||
if let entry = CodableEntry(CachedStickerQueryResult(items: files, hash: hash, timestamp: currentTime)) {
|
if let entry = CodableEntry(CachedStickerQueryResult(items: files, hash: hash, timestamp: currentTime)) {
|
||||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedStickerQueryResults, key: CachedStickerQueryResult.cacheKey(query)), entry: entry)
|
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedStickerQueryResults, key: CachedStickerQueryResult.cacheKey(query)), entry: entry)
|
||||||
|
|||||||
@ -62,7 +62,7 @@ extension ImagePlane {
|
|||||||
func subtract(other: DctCoefficientPlane) {
|
func subtract(other: DctCoefficientPlane) {
|
||||||
self.data.withUnsafeMutableBytes { bytes in
|
self.data.withUnsafeMutableBytes { bytes in
|
||||||
other.data.withUnsafeBytes { otherBytes in
|
other.data.withUnsafeBytes { otherBytes in
|
||||||
subtractArraysUInt8Int16(bytes.baseAddress!.assumingMemoryBound(to: UInt8.self), otherBytes.baseAddress!.assumingMemoryBound(to: Int16.self), bytes.baseAddress!.assumingMemoryBound(to: Int8.self), Int32(bytes.count))
|
subtractArraysUInt8Int16(bytes.baseAddress!.assumingMemoryBound(to: UInt8.self), otherBytes.baseAddress!.assumingMemoryBound(to: Int16.self), bytes.baseAddress!.assumingMemoryBound(to: UInt8.self), Int32(bytes.count))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,7 +70,7 @@ extension ImagePlane {
|
|||||||
func add(other: DctCoefficientPlane) {
|
func add(other: DctCoefficientPlane) {
|
||||||
self.data.withUnsafeMutableBytes { bytes in
|
self.data.withUnsafeMutableBytes { bytes in
|
||||||
other.data.withUnsafeBytes { otherBytes in
|
other.data.withUnsafeBytes { otherBytes in
|
||||||
addArraysUInt8Int16(bytes.baseAddress!.assumingMemoryBound(to: UInt8.self), otherBytes.baseAddress!.assumingMemoryBound(to: Int16.self), bytes.baseAddress!.assumingMemoryBound(to: Int8.self), Int32(bytes.count))
|
addArraysUInt8Int16(bytes.baseAddress!.assumingMemoryBound(to: UInt8.self), otherBytes.baseAddress!.assumingMemoryBound(to: Int16.self), bytes.baseAddress!.assumingMemoryBound(to: UInt8.self), Int32(bytes.count))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -160,7 +160,7 @@ extension DctCoefficientPlane {
|
|||||||
func toUInt8(target: ImagePlane) {
|
func toUInt8(target: ImagePlane) {
|
||||||
self.data.withUnsafeBytes { bytes in
|
self.data.withUnsafeBytes { bytes in
|
||||||
target.data.withUnsafeMutableBytes { otherBytes in
|
target.data.withUnsafeMutableBytes { otherBytes in
|
||||||
convertInt16toUInt8(bytes.baseAddress!.assumingMemoryBound(to: UInt16.self), otherBytes.baseAddress!.assumingMemoryBound(to: UInt8.self), Int32(bytes.count / 2))
|
convertInt16toUInt8(bytes.baseAddress!.assumingMemoryBound(to: Int16.self), otherBytes.baseAddress!.assumingMemoryBound(to: UInt8.self), Int32(bytes.count / 2))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -135,7 +135,7 @@ func chatHistoryEntriesForView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if presentationData.largeEmoji, message.media.isEmpty {
|
if presentationData.largeEmoji, message.media.isEmpty {
|
||||||
if stickersEnabled && message.text.count == 1, let entities = message.textEntitiesAttribute?.entities, entities.count == 1, case let .CustomEmoji(_, fileId) = entities[0].type, let file = message.associatedMedia[MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)] as? TelegramMediaFile, !file.isVideoEmoji {
|
if stickersEnabled && messageIsElligibleForLargeCustomEmoji(message) {
|
||||||
contentTypeHint = .animatedEmoji
|
contentTypeHint = .animatedEmoji
|
||||||
} else if stickersEnabled && message.text.count == 1, let _ = associatedData.animatedEmojiStickers[message.text.basicEmoji.0], (message.textEntitiesAttribute?.entities.isEmpty ?? true) {
|
} else if stickersEnabled && message.text.count == 1, let _ = associatedData.animatedEmojiStickers[message.text.basicEmoji.0], (message.textEntitiesAttribute?.entities.isEmpty ?? true) {
|
||||||
contentTypeHint = .animatedEmoji
|
contentTypeHint = .animatedEmoji
|
||||||
|
|||||||
@ -27,6 +27,7 @@ import LocalMediaResources
|
|||||||
import AppBundle
|
import AppBundle
|
||||||
import LottieMeshSwift
|
import LottieMeshSwift
|
||||||
import ChatPresentationInterfaceState
|
import ChatPresentationInterfaceState
|
||||||
|
import TextNodeWithEntities
|
||||||
|
|
||||||
private let nameFont = Font.medium(14.0)
|
private let nameFont = Font.medium(14.0)
|
||||||
private let inlineBotPrefixFont = Font.regular(14.0)
|
private let inlineBotPrefixFont = Font.regular(14.0)
|
||||||
@ -174,6 +175,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
private var didSetUpAnimationNode = false
|
private var didSetUpAnimationNode = false
|
||||||
private var isPlaying = false
|
private var isPlaying = false
|
||||||
|
|
||||||
|
private let textNode: TextNodeWithEntities
|
||||||
|
|
||||||
private var additionalAnimationNodes: [ChatMessageTransitionNode.DecorationItemNode] = []
|
private var additionalAnimationNodes: [ChatMessageTransitionNode.DecorationItemNode] = []
|
||||||
private var overlayMeshAnimationNode: ChatMessageTransitionNode.DecorationItemNode?
|
private var overlayMeshAnimationNode: ChatMessageTransitionNode.DecorationItemNode?
|
||||||
private var enqueuedAdditionalAnimations: [(Int, Double)] = []
|
private var enqueuedAdditionalAnimations: [(Int, Double)] = []
|
||||||
@ -189,6 +192,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
var telegramFile: TelegramMediaFile?
|
var telegramFile: TelegramMediaFile?
|
||||||
var emojiFile: TelegramMediaFile?
|
var emojiFile: TelegramMediaFile?
|
||||||
var telegramDice: TelegramMediaDice?
|
var telegramDice: TelegramMediaDice?
|
||||||
|
var emojiString: String?
|
||||||
private let disposable = MetaDisposable()
|
private let disposable = MetaDisposable()
|
||||||
private let disposables = DisposableSet()
|
private let disposables = DisposableSet()
|
||||||
|
|
||||||
@ -232,6 +236,10 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
|
|
||||||
self.messageAccessibilityArea = AccessibilityAreaNode()
|
self.messageAccessibilityArea = AccessibilityAreaNode()
|
||||||
|
|
||||||
|
self.textNode = TextNodeWithEntities()
|
||||||
|
self.textNode.textNode.displaysAsynchronously = false
|
||||||
|
self.textNode.textNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
super.init(layerBacked: false)
|
super.init(layerBacked: false)
|
||||||
|
|
||||||
self.containerNode.shouldBegin = { [weak self] location in
|
self.containerNode.shouldBegin = { [weak self] location in
|
||||||
@ -399,6 +407,18 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
if wasVisible != isVisible {
|
if wasVisible != isVisible {
|
||||||
self.visibilityStatus = isVisible
|
self.visibilityStatus = isVisible
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if oldValue != self.visibility {
|
||||||
|
switch self.visibility {
|
||||||
|
case .none:
|
||||||
|
self.textNode.visibilityRect = nil
|
||||||
|
case let .visible(_, subRect):
|
||||||
|
var subRect = subRect
|
||||||
|
subRect.origin.x = 0.0
|
||||||
|
subRect.size.width = 10000.0
|
||||||
|
self.textNode.visibilityRect = subRect
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -522,20 +542,28 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
let (emoji, fitz) = item.message.text.basicEmoji
|
let (emoji, fitz) = item.message.text.basicEmoji
|
||||||
|
|
||||||
var emojiFile: TelegramMediaFile?
|
var emojiFile: TelegramMediaFile?
|
||||||
if let entities = item.message.textEntitiesAttribute?.entities, entities.count == 1, case let .CustomEmoji(_, fileId) = entities[0].type {
|
var emojiString: String?
|
||||||
if let file = item.message.associatedMedia[MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)] as? TelegramMediaFile {
|
if let entities = item.message.textEntitiesAttribute?.entities {
|
||||||
emojiFile = file
|
if entities.count == 1, case let .CustomEmoji(_, fileId) = entities[0].type {
|
||||||
|
if let file = item.message.associatedMedia[MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)] as? TelegramMediaFile {
|
||||||
|
emojiFile = file
|
||||||
|
}
|
||||||
|
} else if messageIsElligibleForLargeCustomEmoji(item.message) {
|
||||||
|
emojiString = item.message.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if emojiFile == nil {
|
|
||||||
|
if emojiFile == nil && emojiString == nil {
|
||||||
emojiFile = item.associatedData.animatedEmojiStickers[emoji]?.first?.file
|
emojiFile = item.associatedData.animatedEmojiStickers[emoji]?.first?.file
|
||||||
}
|
}
|
||||||
if emojiFile == nil {
|
if emojiFile == nil && emojiString == nil {
|
||||||
emojiFile = item.associatedData.animatedEmojiStickers[emoji.strippedEmoji]?.first?.file
|
emojiFile = item.associatedData.animatedEmojiStickers[emoji.strippedEmoji]?.first?.file
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.emojiFile?.id != emojiFile?.id {
|
if self.emojiString != emojiString {
|
||||||
|
self.emojiString = emojiString
|
||||||
|
} else if self.emojiFile?.id != emojiFile?.id {
|
||||||
self.emojiFile = emojiFile
|
self.emojiFile = emojiFile
|
||||||
if let emojiFile = emojiFile {
|
if let emojiFile = emojiFile {
|
||||||
let dimensions = emojiFile.dimensions ?? PixelDimensions(width: 512, height: 512)
|
let dimensions = emojiFile.dimensions ?? PixelDimensions(width: 512, height: 512)
|
||||||
@ -645,7 +673,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
let pathPrefix = item.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(file.resource.id)
|
let pathPrefix = item.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(file.resource.id)
|
||||||
let mode: AnimatedStickerMode = .direct(cachePathPrefix: pathPrefix)
|
let mode: AnimatedStickerMode = .direct(cachePathPrefix: pathPrefix)
|
||||||
self.animationSize = fittedSize
|
self.animationSize = fittedSize
|
||||||
animationNode.setup(source: AnimatedStickerResourceSource(account: item.context.account, resource: file.resource, fitzModifier: fitzModifier, isVideo: file.isVideoSticker), width: Int(fittedSize.width), height: Int(fittedSize.height), playbackMode: playbackMode, mode: mode)
|
animationNode.setup(source: AnimatedStickerResourceSource(account: item.context.account, resource: file.resource, fitzModifier: fitzModifier, isVideo: file.mimeType == "video/webm"), width: Int(fittedSize.width), height: Int(fittedSize.height), playbackMode: playbackMode, mode: mode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -755,12 +783,13 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func asyncLayout() -> (_ item: ChatMessageItem, _ params: ListViewItemLayoutParams, _ mergedTop: ChatMessageMerge, _ mergedBottom: ChatMessageMerge, _ dateHeaderAtBottom: Bool) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation, ListViewItemApply, Bool) -> Void) {
|
override func asyncLayout() -> (_ item: ChatMessageItem, _ params: ListViewItemLayoutParams, _ mergedTop: ChatMessageMerge, _ mergedBottom: ChatMessageMerge, _ dateHeaderAtBottom: Bool) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation, ListViewItemApply, Bool) -> Void) {
|
||||||
var displaySize = CGSize(width: 180.0, height: 180.0)
|
var displaySize = CGSize(width: 180.0, height: 180.0)
|
||||||
let telegramFile = self.telegramFile
|
let telegramFile = self.telegramFile
|
||||||
let emojiFile = self.emojiFile
|
let emojiFile = self.emojiFile
|
||||||
let telegramDice = self.telegramDice
|
let telegramDice = self.telegramDice
|
||||||
|
let emojiString = self.emojiString
|
||||||
let layoutConstants = self.layoutConstants
|
let layoutConstants = self.layoutConstants
|
||||||
let imageLayout = self.imageNode.asyncLayout()
|
let imageLayout = self.imageNode.asyncLayout()
|
||||||
let makeDateAndStatusLayout = self.dateAndStatusNode.asyncLayout()
|
let makeDateAndStatusLayout = self.dateAndStatusNode.asyncLayout()
|
||||||
@ -774,6 +803,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
let currentShareButtonNode = self.shareButtonNode
|
let currentShareButtonNode = self.shareButtonNode
|
||||||
let currentForwardInfo = self.appliedForwardInfo
|
let currentForwardInfo = self.appliedForwardInfo
|
||||||
|
|
||||||
|
let textLayout = TextNodeWithEntities.asyncLayout(self.textNode)
|
||||||
|
|
||||||
return { item, params, mergedTop, mergedBottom, dateHeaderAtBottom in
|
return { item, params, mergedTop, mergedBottom, dateHeaderAtBottom in
|
||||||
let accessibilityData = ChatMessageAccessibilityData(item: item, isSelected: nil)
|
let accessibilityData = ChatMessageAccessibilityData(item: item, isSelected: nil)
|
||||||
let layoutConstants = chatMessageItemLayoutConstants(layoutConstants, params: params, presentationData: item.presentationData)
|
let layoutConstants = chatMessageItemLayoutConstants(layoutConstants, params: params, presentationData: item.presentationData)
|
||||||
@ -781,6 +812,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
|
|
||||||
var imageSize: CGSize = CGSize(width: 200.0, height: 200.0)
|
var imageSize: CGSize = CGSize(width: 200.0, height: 200.0)
|
||||||
var imageVerticalInset: CGFloat = 0.0
|
var imageVerticalInset: CGFloat = 0.0
|
||||||
|
var imageBottomPadding: CGFloat = 0.0
|
||||||
var imageHorizontalOffset: CGFloat = 0.0
|
var imageHorizontalOffset: CGFloat = 0.0
|
||||||
if !(telegramFile?.videoThumbnails.isEmpty ?? true) {
|
if !(telegramFile?.videoThumbnails.isEmpty ?? true) {
|
||||||
displaySize = CGSize(width: 240.0, height: 240.0)
|
displaySize = CGSize(width: 240.0, height: 240.0)
|
||||||
@ -788,27 +820,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
imageHorizontalOffset = 12.0
|
imageHorizontalOffset = 12.0
|
||||||
}
|
}
|
||||||
|
|
||||||
var isEmoji = false
|
var textLayoutAndApply: (TextNodeLayout, (TextNodeWithEntities.Arguments) -> TextNodeWithEntities)?
|
||||||
if let _ = telegramDice {
|
var imageInset: CGFloat = 10.0
|
||||||
imageSize = displaySize
|
|
||||||
} else if let telegramFile = telegramFile {
|
|
||||||
if let dimensions = telegramFile.dimensions {
|
|
||||||
imageSize = dimensions.cgSize.aspectFitted(displaySize)
|
|
||||||
} else if let thumbnailSize = telegramFile.previewRepresentations.first?.dimensions {
|
|
||||||
imageSize = thumbnailSize.cgSize.aspectFitted(displaySize)
|
|
||||||
} else {
|
|
||||||
imageSize = displaySize
|
|
||||||
}
|
|
||||||
} else if let emojiFile = emojiFile {
|
|
||||||
isEmoji = true
|
|
||||||
|
|
||||||
let displaySize = CGSize(width: floor(displaySize.width * item.presentationData.animatedEmojiScale), height: floor(displaySize.height * item.presentationData.animatedEmojiScale))
|
|
||||||
if let dimensions = emojiFile.dimensions {
|
|
||||||
imageSize = CGSize(width: displaySize.width * CGFloat(dimensions.width) / 512.0, height: displaySize.height * CGFloat(dimensions.height) / 512.0)
|
|
||||||
} else if let thumbnailSize = emojiFile.previewRepresentations.first?.dimensions {
|
|
||||||
imageSize = thumbnailSize.cgSize.aspectFitted(displaySize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let avatarInset: CGFloat
|
let avatarInset: CGFloat
|
||||||
var hasAvatar = false
|
var hasAvatar = false
|
||||||
@ -909,6 +922,55 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isEmoji = false
|
||||||
|
if let _ = telegramDice {
|
||||||
|
imageSize = displaySize
|
||||||
|
} else if let telegramFile = telegramFile {
|
||||||
|
if let dimensions = telegramFile.dimensions {
|
||||||
|
imageSize = dimensions.cgSize.aspectFitted(displaySize)
|
||||||
|
} else if let thumbnailSize = telegramFile.previewRepresentations.first?.dimensions {
|
||||||
|
imageSize = thumbnailSize.cgSize.aspectFitted(displaySize)
|
||||||
|
} else {
|
||||||
|
imageSize = displaySize
|
||||||
|
}
|
||||||
|
} else if let emojiFile = emojiFile {
|
||||||
|
isEmoji = true
|
||||||
|
|
||||||
|
let displaySize = CGSize(width: floor(displaySize.width * item.presentationData.animatedEmojiScale), height: floor(displaySize.height * item.presentationData.animatedEmojiScale))
|
||||||
|
if let dimensions = emojiFile.dimensions {
|
||||||
|
imageSize = CGSize(width: displaySize.width * CGFloat(dimensions.width) / 512.0, height: displaySize.height * CGFloat(dimensions.height) / 512.0)
|
||||||
|
} else if let thumbnailSize = emojiFile.previewRepresentations.first?.dimensions {
|
||||||
|
imageSize = thumbnailSize.cgSize.aspectFitted(displaySize)
|
||||||
|
}
|
||||||
|
} else if let _ = emojiString {
|
||||||
|
imageVerticalInset = 4.0
|
||||||
|
imageBottomPadding = 24.0
|
||||||
|
|
||||||
|
let baseWidth = params.width
|
||||||
|
var tmpWidth = layoutConstants.bubble.maximumWidthFill.widthFor(baseWidth)
|
||||||
|
if needsShareButton && tmpWidth + 32.0 > baseWidth {
|
||||||
|
tmpWidth = baseWidth - 32.0
|
||||||
|
}
|
||||||
|
|
||||||
|
var deliveryFailedInset: CGFloat = 0.0
|
||||||
|
if isFailed {
|
||||||
|
deliveryFailedInset += 24.0
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpWidth -= deliveryFailedInset
|
||||||
|
|
||||||
|
let maximumContentWidth = floor(tmpWidth - layoutConstants.bubble.edgeInset - layoutConstants.bubble.edgeInset - layoutConstants.bubble.contentInsets.left - layoutConstants.bubble.contentInsets.right - avatarInset)
|
||||||
|
|
||||||
|
let font = Font.regular(fontSizeForEmojiString(item.message.text))
|
||||||
|
let attributedText = stringWithAppliedEntities(item.message.text, entities: item.message.textEntitiesAttribute?.entities ?? [], baseColor: .black, linkColor: .black, baseFont: font, linkFont: font, boldFont: font, italicFont: font, boldItalicFont: font, fixedFont: font, blockQuoteFont: font, message: item.message)
|
||||||
|
textLayoutAndApply = textLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: maximumContentWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural))
|
||||||
|
|
||||||
|
imageSize = CGSize(width: textLayoutAndApply!.0.size.width, height: textLayoutAndApply!.0.size.height)
|
||||||
|
isEmoji = true
|
||||||
|
|
||||||
|
imageInset = 0.0
|
||||||
|
}
|
||||||
|
|
||||||
var layoutInsets = UIEdgeInsets(top: mergedTop.merged ? layoutConstants.bubble.mergedSpacing : layoutConstants.bubble.defaultSpacing, left: 0.0, bottom: mergedBottom.merged ? layoutConstants.bubble.mergedSpacing : layoutConstants.bubble.defaultSpacing, right: 0.0)
|
var layoutInsets = UIEdgeInsets(top: mergedTop.merged ? layoutConstants.bubble.mergedSpacing : layoutConstants.bubble.defaultSpacing, left: 0.0, bottom: mergedBottom.merged ? layoutConstants.bubble.mergedSpacing : layoutConstants.bubble.defaultSpacing, right: 0.0)
|
||||||
if dateHeaderAtBottom {
|
if dateHeaderAtBottom {
|
||||||
layoutInsets.top += layoutConstants.timestampHeaderHeight
|
layoutInsets.top += layoutConstants.timestampHeaderHeight
|
||||||
@ -921,7 +983,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
|
|
||||||
let displayLeftInset = params.leftInset + layoutConstants.bubble.edgeInset + avatarInset
|
let displayLeftInset = params.leftInset + layoutConstants.bubble.edgeInset + avatarInset
|
||||||
|
|
||||||
let imageInset: CGFloat = 10.0
|
|
||||||
var innerImageSize = imageSize
|
var innerImageSize = imageSize
|
||||||
imageSize = CGSize(width: imageSize.width + imageInset * 2.0, height: imageSize.height + imageInset * 2.0)
|
imageSize = CGSize(width: imageSize.width + imageInset * 2.0, height: imageSize.height + imageInset * 2.0)
|
||||||
let imageFrame = CGRect(origin: CGPoint(x: 0.0 + (incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + avatarInset + layoutConstants.bubble.contentInsets.left) : (params.width - params.rightInset - imageSize.width - layoutConstants.bubble.edgeInset - layoutConstants.bubble.contentInsets.left - deliveryFailedInset - imageHorizontalOffset)), y: imageVerticalInset), size: CGSize(width: imageSize.width, height: imageSize.height))
|
let imageFrame = CGRect(origin: CGPoint(x: 0.0 + (incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + avatarInset + layoutConstants.bubble.contentInsets.left) : (params.width - params.rightInset - imageSize.width - layoutConstants.bubble.edgeInset - layoutConstants.bubble.contentInsets.left - deliveryFailedInset - imageHorizontalOffset)), y: imageVerticalInset), size: CGSize(width: imageSize.width, height: imageSize.height))
|
||||||
@ -1072,7 +1134,12 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let contentHeight = max(imageSize.height + imageVerticalInset * 2.0, layoutConstants.image.minDimensions.height)
|
let contentHeight: CGFloat
|
||||||
|
if let _ = emojiString {
|
||||||
|
contentHeight = imageSize.height + imageVerticalInset * 2.0 + imageBottomPadding
|
||||||
|
} else {
|
||||||
|
contentHeight = max(imageSize.height + imageVerticalInset * 2.0, layoutConstants.image.minDimensions.height)
|
||||||
|
}
|
||||||
|
|
||||||
var forwardSource: Peer?
|
var forwardSource: Peer?
|
||||||
var forwardAuthorSignature: String?
|
var forwardAuthorSignature: String?
|
||||||
@ -1179,14 +1246,37 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let updatedImageFrame = imageFrame.offsetBy(dx: 0.0, dy: floor((contentHeight - imageSize.height) / 2.0))
|
let updatedImageFrame: CGRect
|
||||||
|
var contextContentFrame: CGRect
|
||||||
|
if let _ = emojiString {
|
||||||
|
updatedImageFrame = imageFrame
|
||||||
|
contextContentFrame = updatedImageFrame.inset(by: UIEdgeInsets(top: 0.0, left: 0.0, bottom: -imageBottomPadding, right: 0.0))
|
||||||
|
} else {
|
||||||
|
updatedImageFrame = imageFrame.offsetBy(dx: 0.0, dy: floor((contentHeight - imageSize.height) / 2.0))
|
||||||
|
contextContentFrame = updatedImageFrame
|
||||||
|
}
|
||||||
var updatedContentFrame = updatedImageFrame
|
var updatedContentFrame = updatedImageFrame
|
||||||
if isEmoji {
|
if isEmoji && emojiString == nil {
|
||||||
updatedContentFrame = updatedContentFrame.insetBy(dx: -imageInset, dy: -imageInset)
|
updatedContentFrame = updatedContentFrame.insetBy(dx: -imageInset, dy: -imageInset)
|
||||||
|
contextContentFrame = updatedContentFrame
|
||||||
|
}
|
||||||
|
|
||||||
|
if let (_, textApply) = textLayoutAndApply {
|
||||||
|
let placeholderColor = bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.stickerPlaceholderColor, wallpaper: item.presentationData.theme.wallpaper)
|
||||||
|
let _ = textApply(TextNodeWithEntities.Arguments(context: item.context, cache: item.controllerInteraction.presentationContext.animationCache, renderer: item.controllerInteraction.presentationContext.animationRenderer, placeholderColor: placeholderColor, attemptSynchronous: synchronousLoads))
|
||||||
|
|
||||||
|
if strongSelf.textNode.textNode.supernode == nil {
|
||||||
|
strongSelf.contextSourceNode.contentNode.insertSubnode(strongSelf.textNode.textNode, aboveSubnode: strongSelf.imageNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.textNode.textNode.frame = imageFrame
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.imageNode.frame = updatedContentFrame
|
strongSelf.imageNode.frame = updatedContentFrame
|
||||||
|
|
||||||
|
strongSelf.contextSourceNode.contentRect = contextContentFrame
|
||||||
|
strongSelf.containerNode.targetNodeForActivationProgressContentRect = strongSelf.contextSourceNode.contentRect
|
||||||
|
|
||||||
let animationNodeFrame = updatedContentFrame.insetBy(dx: imageInset, dy: imageInset)
|
let animationNodeFrame = updatedContentFrame.insetBy(dx: imageInset, dy: imageInset)
|
||||||
|
|
||||||
var file: TelegramMediaFile?
|
var file: TelegramMediaFile?
|
||||||
@ -1231,10 +1321,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
strongSelf.enableSynchronousImageApply = true
|
strongSelf.enableSynchronousImageApply = true
|
||||||
imageApply()
|
imageApply()
|
||||||
strongSelf.enableSynchronousImageApply = false
|
strongSelf.enableSynchronousImageApply = false
|
||||||
|
|
||||||
strongSelf.contextSourceNode.contentRect = strongSelf.imageNode.frame
|
|
||||||
strongSelf.containerNode.targetNodeForActivationProgressContentRect = strongSelf.contextSourceNode.contentRect
|
|
||||||
|
|
||||||
if let updatedShareButtonNode = updatedShareButtonNode {
|
if let updatedShareButtonNode = updatedShareButtonNode {
|
||||||
if updatedShareButtonNode !== strongSelf.shareButtonNode {
|
if updatedShareButtonNode !== strongSelf.shareButtonNode {
|
||||||
if let shareButtonNode = strongSelf.shareButtonNode {
|
if let shareButtonNode = strongSelf.shareButtonNode {
|
||||||
@ -1245,13 +1332,13 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
updatedShareButtonNode.addTarget(strongSelf, action: #selector(strongSelf.shareButtonPressed), forControlEvents: .touchUpInside)
|
updatedShareButtonNode.addTarget(strongSelf, action: #selector(strongSelf.shareButtonPressed), forControlEvents: .touchUpInside)
|
||||||
}
|
}
|
||||||
let buttonSize = updatedShareButtonNode.update(presentationData: item.presentationData, chatLocation: item.chatLocation, subject: item.associatedData.subject, message: item.message, account: item.context.account)
|
let buttonSize = updatedShareButtonNode.update(presentationData: item.presentationData, chatLocation: item.chatLocation, subject: item.associatedData.subject, message: item.message, account: item.context.account)
|
||||||
updatedShareButtonNode.frame = CGRect(origin: CGPoint(x: updatedImageFrame.maxX + 8.0, y: updatedImageFrame.maxY - buttonSize.height - 4.0), size: buttonSize)
|
updatedShareButtonNode.frame = CGRect(origin: CGPoint(x: updatedImageFrame.maxX + 8.0, y: updatedImageFrame.maxY - buttonSize.height - 4.0 + imageBottomPadding), size: buttonSize)
|
||||||
} else if let shareButtonNode = strongSelf.shareButtonNode {
|
} else if let shareButtonNode = strongSelf.shareButtonNode {
|
||||||
shareButtonNode.removeFromSupernode()
|
shareButtonNode.removeFromSupernode()
|
||||||
strongSelf.shareButtonNode = nil
|
strongSelf.shareButtonNode = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let dateAndStatusFrame = CGRect(origin: CGPoint(x: max(displayLeftInset, updatedImageFrame.maxX - dateAndStatusSize.width - 4.0), y: updatedImageFrame.maxY - dateAndStatusSize.height - 4.0), size: dateAndStatusSize)
|
let dateAndStatusFrame = CGRect(origin: CGPoint(x: max(displayLeftInset, updatedImageFrame.maxX - dateAndStatusSize.width - 4.0), y: updatedImageFrame.maxY - dateAndStatusSize.height - 4.0 + imageBottomPadding), size: dateAndStatusSize)
|
||||||
animation.animator.updateFrame(layer: strongSelf.dateAndStatusNode.layer, frame: dateAndStatusFrame, completion: nil)
|
animation.animator.updateFrame(layer: strongSelf.dateAndStatusNode.layer, frame: dateAndStatusFrame, completion: nil)
|
||||||
dateAndStatusApply(animation)
|
dateAndStatusApply(animation)
|
||||||
|
|
||||||
@ -1337,7 +1424,6 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
strongSelf.replyInfoNode = nil
|
strongSelf.replyInfoNode = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if let replyBackgroundNode = strongSelf.replyBackgroundNode {
|
if let replyBackgroundNode = strongSelf.replyBackgroundNode {
|
||||||
replyBackgroundNode.frame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - messageInfoSize.width - layoutConstants.bubble.edgeInset - 10.0)) - 4.0, y: 6.0), size: CGSize(width: messageInfoSize.width + 8.0, height: messageInfoSize.height + 5.0))
|
replyBackgroundNode.frame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - messageInfoSize.width - layoutConstants.bubble.edgeInset - 10.0)) - 4.0, y: 6.0), size: CGSize(width: messageInfoSize.width + 8.0, height: messageInfoSize.height + 5.0))
|
||||||
|
|
||||||
@ -1367,7 +1453,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
strongSelf.addSubnode(deliveryFailedNode)
|
strongSelf.addSubnode(deliveryFailedNode)
|
||||||
}
|
}
|
||||||
let deliveryFailedSize = deliveryFailedNode.updateLayout(theme: item.presentationData.theme.theme)
|
let deliveryFailedSize = deliveryFailedNode.updateLayout(theme: item.presentationData.theme.theme)
|
||||||
let deliveryFailedFrame = CGRect(origin: CGPoint(x: imageFrame.maxX + deliveryFailedInset - deliveryFailedSize.width, y: imageFrame.maxY - deliveryFailedSize.height - imageInset + imageVerticalInset), size: deliveryFailedSize)
|
let deliveryFailedFrame = CGRect(origin: CGPoint(x: imageFrame.maxX + deliveryFailedInset - deliveryFailedSize.width, y: imageFrame.maxY - deliveryFailedSize.height - imageInset + imageVerticalInset + imageBottomPadding), size: deliveryFailedSize)
|
||||||
if isAppearing {
|
if isAppearing {
|
||||||
deliveryFailedNode.frame = deliveryFailedFrame
|
deliveryFailedNode.frame = deliveryFailedFrame
|
||||||
transition.animatePositionAdditive(node: deliveryFailedNode, offset: CGPoint(x: deliveryFailedInset, y: 0.0))
|
transition.animatePositionAdditive(node: deliveryFailedNode, offset: CGPoint(x: deliveryFailedInset, y: 0.0))
|
||||||
@ -1385,7 +1471,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
if let actionButtonsSizeAndApply = actionButtonsSizeAndApply {
|
if let actionButtonsSizeAndApply = actionButtonsSizeAndApply {
|
||||||
let actionButtonsNode = actionButtonsSizeAndApply.1(animation)
|
let actionButtonsNode = actionButtonsSizeAndApply.1(animation)
|
||||||
let previousFrame = actionButtonsNode.frame
|
let previousFrame = actionButtonsNode.frame
|
||||||
let actionButtonsFrame = CGRect(origin: CGPoint(x: imageFrame.minX, y: imageFrame.maxY + imageVerticalInset), size: actionButtonsSizeAndApply.0)
|
let actionButtonsFrame = CGRect(origin: CGPoint(x: imageFrame.minX, y: imageFrame.maxY + imageVerticalInset + imageBottomPadding), size: actionButtonsSizeAndApply.0)
|
||||||
actionButtonsNode.frame = actionButtonsFrame
|
actionButtonsNode.frame = actionButtonsFrame
|
||||||
if actionButtonsNode !== strongSelf.actionButtonsNode {
|
if actionButtonsNode !== strongSelf.actionButtonsNode {
|
||||||
strongSelf.actionButtonsNode = actionButtonsNode
|
strongSelf.actionButtonsNode = actionButtonsNode
|
||||||
@ -1412,7 +1498,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
|
|
||||||
if let reactionButtonsSizeAndApply = reactionButtonsSizeAndApply {
|
if let reactionButtonsSizeAndApply = reactionButtonsSizeAndApply {
|
||||||
let reactionButtonsNode = reactionButtonsSizeAndApply.1(animation)
|
let reactionButtonsNode = reactionButtonsSizeAndApply.1(animation)
|
||||||
var reactionButtonsFrame = CGRect(origin: CGPoint(x: imageFrame.minX, y: imageFrame.maxY + imageVerticalInset), size: reactionButtonsSizeAndApply.0)
|
var reactionButtonsFrame = CGRect(origin: CGPoint(x: imageFrame.minX, y: imageFrame.maxY + imageVerticalInset + imageBottomPadding), size: reactionButtonsSizeAndApply.0)
|
||||||
if !incoming {
|
if !incoming {
|
||||||
reactionButtonsFrame.origin.x = imageFrame.maxX - reactionButtonsSizeAndApply.0.width
|
reactionButtonsFrame.origin.x = imageFrame.maxX - reactionButtonsSizeAndApply.0.width
|
||||||
}
|
}
|
||||||
@ -1820,7 +1906,12 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
return .optionalAction({
|
return .optionalAction({
|
||||||
item.controllerInteraction.displayDiceTooltip(dice)
|
item.controllerInteraction.displayDiceTooltip(dice)
|
||||||
})
|
})
|
||||||
} else if let emojiFile = self.emojiFile, !emojiFile.isCustomEmoji {
|
} else if let _ = self.emojiFile {
|
||||||
|
// if let emojiFile = self.emojiFile, emojiFile.isCustomEmoji {
|
||||||
|
// return .optionalAction({
|
||||||
|
// let _ = item.controllerInteraction.openMessage(item.message, .default)
|
||||||
|
// })
|
||||||
|
// } else
|
||||||
if let animationNode = self.animationNode as? AnimatedStickerNode, let _ = recognizer {
|
if let animationNode = self.animationNode as? AnimatedStickerNode, let _ = recognizer {
|
||||||
var shouldPlay = false
|
var shouldPlay = false
|
||||||
if !animationNode.isPlaying {
|
if !animationNode.isPlaying {
|
||||||
@ -2494,3 +2585,40 @@ struct AnimatedEmojiSoundsConfiguration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func fontSizeForEmojiString(_ string: String) -> CGFloat {
|
||||||
|
let lines = string.components(separatedBy: "\n")
|
||||||
|
|
||||||
|
var maxLineLength = 0
|
||||||
|
for line in lines {
|
||||||
|
maxLineLength = max(maxLineLength, line.count)
|
||||||
|
}
|
||||||
|
|
||||||
|
let linesCount = lines.count
|
||||||
|
|
||||||
|
let length = max(maxLineLength, linesCount)
|
||||||
|
|
||||||
|
let basicSize: CGFloat = 94.0
|
||||||
|
let multiplier: CGFloat
|
||||||
|
switch length {
|
||||||
|
case 2:
|
||||||
|
multiplier = 0.7
|
||||||
|
case 3:
|
||||||
|
multiplier = 0.52
|
||||||
|
case 4:
|
||||||
|
multiplier = 0.37
|
||||||
|
case 5:
|
||||||
|
multiplier = 0.28
|
||||||
|
case 6:
|
||||||
|
multiplier = 0.25
|
||||||
|
case 7:
|
||||||
|
multiplier = 0.23
|
||||||
|
case 8:
|
||||||
|
multiplier = 0.21
|
||||||
|
case 9:
|
||||||
|
multiplier = 0.19
|
||||||
|
default:
|
||||||
|
multiplier = 0.19
|
||||||
|
}
|
||||||
|
return floor(basicSize * multiplier)
|
||||||
|
}
|
||||||
|
|||||||
@ -1295,7 +1295,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
|||||||
|
|
||||||
tmpWidth -= deliveryFailedInset
|
tmpWidth -= deliveryFailedInset
|
||||||
|
|
||||||
let maximumContentWidth = floor(tmpWidth - layoutConstants.bubble.edgeInset - layoutConstants.bubble.edgeInset - layoutConstants.bubble.contentInsets.left - layoutConstants.bubble.contentInsets.right - avatarInset)
|
let maximumContentWidth = floor(tmpWidth - layoutConstants.bubble.edgeInset * 3.0 - layoutConstants.bubble.contentInsets.left - layoutConstants.bubble.contentInsets.right - avatarInset)
|
||||||
|
|
||||||
var contentPropertiesAndPrepareLayouts: [(Message, Bool, ChatMessageEntryAttributes, BubbleItemAttributes, (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize) -> (ChatMessageBubbleContentProperties, CGSize?, CGFloat, (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))))] = []
|
var contentPropertiesAndPrepareLayouts: [(Message, Bool, ChatMessageEntryAttributes, BubbleItemAttributes, (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize) -> (ChatMessageBubbleContentProperties, CGSize?, CGFloat, (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))))] = []
|
||||||
var addedContentNodes: [(Message, Bool, ChatMessageBubbleContentNode)]?
|
var addedContentNodes: [(Message, Bool, ChatMessageBubbleContentNode)]?
|
||||||
|
|||||||
@ -138,7 +138,7 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
|
|||||||
}
|
}
|
||||||
if let actions = self.actions, actions.isCopyProtected {
|
if let actions = self.actions, actions.isCopyProtected {
|
||||||
self.interfaceInteraction?.displayCopyProtectionTip(self.forwardButton, false)
|
self.interfaceInteraction?.displayCopyProtectionTip(self.forwardButton, false)
|
||||||
} else {
|
} else if !self.forwardButton.isImplicitlyDisabled {
|
||||||
self.interfaceInteraction?.forwardSelectedMessages()
|
self.interfaceInteraction?.forwardSelectedMessages()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,7 +149,7 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
|
|||||||
}
|
}
|
||||||
if let actions = self.actions, actions.isCopyProtected {
|
if let actions = self.actions, actions.isCopyProtected {
|
||||||
self.interfaceInteraction?.displayCopyProtectionTip(self.shareButton, true)
|
self.interfaceInteraction?.displayCopyProtectionTip(self.shareButton, true)
|
||||||
} else {
|
} else if !self.shareButton.isImplicitlyDisabled {
|
||||||
self.interfaceInteraction?.shareSelectedMessages()
|
self.interfaceInteraction?.shareSelectedMessages()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -260,7 +260,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if self.telegramFile == nil && item.presentationData.largeEmoji && messageIsElligibleForLargeEmoji(item.message) {
|
if self.telegramFile == nil && item.presentationData.largeEmoji && messageIsElligibleForLargeEmoji(item.message) {
|
||||||
self.imageNode.setSignal(largeEmoji(postbox: item.context.account.postbox, emoji: item.message.text))
|
self.imageNode.setSignal(largeEmoji(postbox: item.context.account.postbox, emoji: item.message.text, outline: false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,7 +363,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
var isEmoji = false
|
var isEmoji = false
|
||||||
if item.presentationData.largeEmoji && messageIsElligibleForLargeEmoji(item.message) {
|
if item.presentationData.largeEmoji && messageIsElligibleForLargeEmoji(item.message) {
|
||||||
let attributedText = NSAttributedString(string: item.message.text, font: item.presentationData.messageEmojiFont, textColor: .black)
|
let attributedText = NSAttributedString(string: item.message.text, font: item.presentationData.messageEmojiFont, textColor: .black)
|
||||||
textLayoutAndApply = textLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: 180.0, height: 90.0), alignment: .natural))
|
textLayoutAndApply = textLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width, height: 90.0), alignment: .natural))
|
||||||
|
|
||||||
imageSize = CGSize(width: textLayoutAndApply!.0.size.width, height: textLayoutAndApply!.0.size.height)
|
imageSize = CGSize(width: textLayoutAndApply!.0.size.width, height: textLayoutAndApply!.0.size.height)
|
||||||
isEmoji = true
|
isEmoji = true
|
||||||
|
|||||||
@ -192,7 +192,7 @@ private func matchingEmojiEntry(_ emoji: String) -> (UInt8, UInt8, UInt8)? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func messageIsElligibleForLargeEmoji(_ message: Message) -> Bool {
|
func messageIsElligibleForLargeEmoji(_ message: Message) -> Bool {
|
||||||
if !message.text.isEmpty && message.text.containsOnlyEmoji && message.text.emojis.count < 4 {
|
if !message.text.isEmpty && message.text.containsOnlyEmoji {
|
||||||
if !(message.textEntitiesAttribute?.entities.isEmpty ?? true) {
|
if !(message.textEntitiesAttribute?.entities.isEmpty ?? true) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -209,6 +209,29 @@ func messageIsElligibleForLargeEmoji(_ message: Message) -> Bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func messageIsElligibleForLargeCustomEmoji(_ message: Message) -> Bool {
|
||||||
|
let text = message.text.replacingOccurrences(of: "\n", with: "").replacingOccurrences(of: " ", with: "")
|
||||||
|
guard !text.isEmpty && text.containsOnlyEmoji else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
let entities = message.textEntitiesAttribute?.entities ?? []
|
||||||
|
guard entities.count > 0 else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for entity in entities {
|
||||||
|
if case let .CustomEmoji(_, fileId) = entity.type {
|
||||||
|
if let _ = message.associatedMedia[MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)] as? TelegramMediaFile {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func largeEmoji(postbox: Postbox, emoji: String, outline: Bool = true) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
func largeEmoji(postbox: Postbox, emoji: String, outline: Bool = true) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||||
var dataSignals: [Signal<MediaResourceData, NoError>] = []
|
var dataSignals: [Signal<MediaResourceData, NoError>] = []
|
||||||
for emoji in emoji.emojis {
|
for emoji in emoji.emojis {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user