mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge commit 'be9735f67073496620d695d3e1594d91a3ba2238'
This commit is contained in:
commit
4d5f899a59
@ -133,16 +133,18 @@ public final class BotCheckoutController: ViewController {
|
||||
self?.present(c, in: .window(.root), with: a)
|
||||
}, dismissAnimated: { [weak self] in
|
||||
self?.dismiss()
|
||||
}, completed: self.completed)
|
||||
}, completed: { [weak self] currencyValue, receiptMessageId in
|
||||
self?.complete(currencyValue: currencyValue, receiptMessageId: receiptMessageId)
|
||||
})
|
||||
|
||||
displayNode.dismiss = { [weak self] in
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
}
|
||||
displayNode.pending = { [weak self] in
|
||||
self?.pending()
|
||||
self?.setPending()
|
||||
}
|
||||
displayNode.failed = { [weak self] in
|
||||
self?.failed()
|
||||
self?.fail()
|
||||
}
|
||||
|
||||
self.displayNode = displayNode
|
||||
@ -150,6 +152,12 @@ public final class BotCheckoutController: ViewController {
|
||||
self._ready.set(displayNode.ready)
|
||||
}
|
||||
|
||||
override public func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
|
||||
self.cancel()
|
||||
}
|
||||
|
||||
override public func viewDidAppear(_ animated: Bool) {
|
||||
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())
|
||||
}
|
||||
|
||||
@objc private func cancelPressed() {
|
||||
private var didCancel = false
|
||||
private func cancel() {
|
||||
guard !self.didCancel && !self.didFail && !self.didComplete else {
|
||||
return
|
||||
}
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
@ -444,7 +444,8 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.updateHighlight(animated: true)
|
||||
strongSelf.isButtonHighlighted = highlighted
|
||||
strongSelf.updateHighlight(animated: false)
|
||||
}
|
||||
|
||||
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.placeholderNode.layer.animateAlpha(from: self.placeholderNode.alpha, to: 1.0, duration: 0.2)
|
||||
}
|
||||
self.textNode.visibilityRect = CGRect.infinite
|
||||
|
||||
var contentHeight = textLayout.size.height
|
||||
if contentHeight.isZero {
|
||||
@ -593,7 +595,7 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
func updateHighlight(animated: Bool) {
|
||||
if self.buttonNode.isHighlighted || self.isHighlighted {
|
||||
if self.isButtonHighlighted || self.isHighlighted {
|
||||
self.highlightBackgroundNode.alpha = 1.0
|
||||
} else {
|
||||
if animated {
|
||||
@ -606,8 +608,8 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var isHighlighted = false
|
||||
private var isButtonHighlighted = false
|
||||
private var isHighlighted = false
|
||||
func setHighlighted(_ highlighted: Bool) {
|
||||
guard self.isHighlighted != highlighted else {
|
||||
return
|
||||
|
@ -98,6 +98,16 @@ public func chatMessageGalleryControllerData(context: AccountContext, chatLocati
|
||||
var galleryMedia: Media?
|
||||
var otherMedia: Media?
|
||||
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 {
|
||||
if let action = media as? TelegramMediaAction {
|
||||
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.isUserInteractionEnabled = false
|
||||
self.sceneView.preferredFramesPerSecond = 60
|
||||
self.sceneView.isJitteringEnabled = true
|
||||
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
let animation = CABasicAnimation(keyPath: "scale")
|
||||
animation.duration = 2.0
|
||||
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.autoreverses = true
|
||||
animation.repeatCount = .infinity
|
||||
|
||||
|
||||
node.addAnimation(animation, forKey: "scale")
|
||||
}
|
||||
|
||||
@ -432,23 +433,32 @@ class PremiumStarComponent: Component {
|
||||
self.previousInteractionTimestamp = currentTime
|
||||
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 let particleSystem = particles.particleSystems?.first {
|
||||
particleSystem.particleColorVariation = SCNVector4(0.15, 0.2, 0.15, 0.3)
|
||||
particleSystem.speedFactor = 2.0
|
||||
particleSystem.particleVelocity = 2.2
|
||||
particleSystem.birthRate = 4.0
|
||||
particleSystem.particleLifeSpan = 2.0
|
||||
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 leftParticleSystem = particlesLeft.particleSystems?.first, let rightParticleSystem = particlesRight.particleSystems?.first {
|
||||
leftParticleSystem.speedFactor = 1.3
|
||||
leftParticleSystem.particleVelocity = 2.4
|
||||
leftParticleSystem.birthRate = 24.0
|
||||
leftParticleSystem.particleLifeSpan = 4.0
|
||||
|
||||
rightParticleSystem.speedFactor = 1.3
|
||||
rightParticleSystem.particleVelocity = 2.4
|
||||
rightParticleSystem.birthRate = 24.0
|
||||
rightParticleSystem.particleLifeSpan = 4.0
|
||||
|
||||
node.physicsField?.isActive = true
|
||||
Queue.mainQueue().after(1.0) {
|
||||
node.physicsField?.isActive = false
|
||||
particles.particleSystems?.first?.birthRate = 1.2
|
||||
particleSystem.particleVelocity = 1.0
|
||||
particleSystem.particleLifeSpan = 4.0
|
||||
|
||||
let animation = POPBasicAnimation()
|
||||
animation.property = (POPAnimatableProperty.property(withName: "speedFactor", initializer: { property in
|
||||
leftParticleSystem.birthRate = 9.0
|
||||
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
|
||||
values?.pointee = (particleSystem as! SCNParticleSystem).speedFactor
|
||||
}
|
||||
@ -457,11 +467,27 @@ class PremiumStarComponent: Component {
|
||||
}
|
||||
property?.threshold = 0.01
|
||||
}) as! POPAnimatableProperty)
|
||||
animation.fromValue = 2.0 as NSNumber
|
||||
animation.toValue = 1.0 as NSNumber
|
||||
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
|
||||
animation.duration = 0.5
|
||||
particleSystem.pop_add(animation, forKey: "speedFactor")
|
||||
leftAnimation.fromValue = 1.2 as NSNumber
|
||||
leftAnimation.toValue = 0.85 as NSNumber
|
||||
leftAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
|
||||
leftAnimation.duration = 0.5
|
||||
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 {
|
||||
static var defaultValue: SearchStickersConfiguration {
|
||||
return SearchStickersConfiguration(cacheTimeout: 86400)
|
||||
return SearchStickersConfiguration(cacheTimeout: 86400, normalStickersPerPremium: 2, premiumStickersCount: 0)
|
||||
}
|
||||
|
||||
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.normalStickersPerPremiumCount = normalStickersPerPremium
|
||||
self.premiumStickersCount = premiumStickersCount
|
||||
}
|
||||
|
||||
static func with(appConfiguration: AppConfiguration) -> SearchStickersConfiguration {
|
||||
if let data = appConfiguration.data, let value = data["stickers_emoji_cache_time"] as? Double {
|
||||
return SearchStickersConfiguration(cacheTimeout: Int32(value))
|
||||
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(cacheTimeoutValue), normalStickersPerPremium: Int32(normalStickersPerPremiumValue), premiumStickersCount: Int32(premiumStickersCountValue))
|
||||
} else {
|
||||
return .defaultValue
|
||||
}
|
||||
@ -85,7 +89,7 @@ func _internal_searchStickers(account: Account, query: String, scope: SearchStic
|
||||
if query == "\u{2764}" {
|
||||
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
|
||||
|
||||
var result: [FoundStickerItem] = []
|
||||
@ -198,8 +202,8 @@ func _internal_searchStickers(account: Account, query: String, scope: SearchStic
|
||||
cached = nil
|
||||
}
|
||||
|
||||
return (result, cached, isPremium)
|
||||
} |> mapToSignal { localItems, cached, isPremium -> Signal<[FoundStickerItem], NoError> in
|
||||
return (result, cached, isPremium, searchStickersConfiguration)
|
||||
} |> mapToSignal { localItems, cached, isPremium, searchStickersConfiguration -> Signal<[FoundStickerItem], NoError> in
|
||||
var tempResult: [FoundStickerItem] = localItems
|
||||
if !scope.contains(.remote) {
|
||||
return .single(tempResult)
|
||||
@ -237,19 +241,19 @@ func _internal_searchStickers(account: Account, query: String, scope: SearchStic
|
||||
case let .stickers(hash, stickers):
|
||||
var items: [FoundStickerItem] = []
|
||||
var animatedItems: [FoundStickerItem] = []
|
||||
var premiumItems: [FoundStickerItem] = []
|
||||
|
||||
var result: [FoundStickerItem] = localItems
|
||||
let currentItemIds = Set<MediaId>(result.map { $0.file.fileId })
|
||||
var allItems: [FoundStickerItem] = localItems
|
||||
let currentItemIds = Set<MediaId>(allItems.map { $0.file.fileId })
|
||||
|
||||
var files: [TelegramMediaFile] = []
|
||||
for sticker in stickers {
|
||||
if let file = telegramMediaFileFromApiDocument(sticker), let id = file.id {
|
||||
if file.isPremiumSticker && !isPremium {
|
||||
continue
|
||||
}
|
||||
files.append(file)
|
||||
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: []))
|
||||
} else {
|
||||
items.append(FoundStickerItem(file: file, stringRepresentations: []))
|
||||
@ -257,10 +261,24 @@ func _internal_searchStickers(account: Account, query: String, scope: SearchStic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.append(contentsOf: animatedItems)
|
||||
result.append(contentsOf: items)
|
||||
|
||||
|
||||
allItems.append(contentsOf: animatedItems)
|
||||
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)
|
||||
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)
|
||||
|
@ -62,7 +62,7 @@ extension ImagePlane {
|
||||
func subtract(other: DctCoefficientPlane) {
|
||||
self.data.withUnsafeMutableBytes { bytes 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) {
|
||||
self.data.withUnsafeMutableBytes { bytes 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) {
|
||||
self.data.withUnsafeBytes { bytes 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 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
|
||||
} else if stickersEnabled && message.text.count == 1, let _ = associatedData.animatedEmojiStickers[message.text.basicEmoji.0], (message.textEntitiesAttribute?.entities.isEmpty ?? true) {
|
||||
contentTypeHint = .animatedEmoji
|
||||
|
@ -27,6 +27,7 @@ import LocalMediaResources
|
||||
import AppBundle
|
||||
import LottieMeshSwift
|
||||
import ChatPresentationInterfaceState
|
||||
import TextNodeWithEntities
|
||||
|
||||
private let nameFont = Font.medium(14.0)
|
||||
private let inlineBotPrefixFont = Font.regular(14.0)
|
||||
@ -174,6 +175,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
private var didSetUpAnimationNode = false
|
||||
private var isPlaying = false
|
||||
|
||||
private let textNode: TextNodeWithEntities
|
||||
|
||||
private var additionalAnimationNodes: [ChatMessageTransitionNode.DecorationItemNode] = []
|
||||
private var overlayMeshAnimationNode: ChatMessageTransitionNode.DecorationItemNode?
|
||||
private var enqueuedAdditionalAnimations: [(Int, Double)] = []
|
||||
@ -189,6 +192,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
var telegramFile: TelegramMediaFile?
|
||||
var emojiFile: TelegramMediaFile?
|
||||
var telegramDice: TelegramMediaDice?
|
||||
var emojiString: String?
|
||||
private let disposable = MetaDisposable()
|
||||
private let disposables = DisposableSet()
|
||||
|
||||
@ -232,6 +236,10 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
|
||||
self.messageAccessibilityArea = AccessibilityAreaNode()
|
||||
|
||||
self.textNode = TextNodeWithEntities()
|
||||
self.textNode.textNode.displaysAsynchronously = false
|
||||
self.textNode.textNode.isUserInteractionEnabled = false
|
||||
|
||||
super.init(layerBacked: false)
|
||||
|
||||
self.containerNode.shouldBegin = { [weak self] location in
|
||||
@ -399,6 +407,18 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
if wasVisible != 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
|
||||
|
||||
var emojiFile: TelegramMediaFile?
|
||||
if let entities = item.message.textEntitiesAttribute?.entities, 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
|
||||
var emojiString: String?
|
||||
if let entities = item.message.textEntitiesAttribute?.entities {
|
||||
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
|
||||
}
|
||||
if emojiFile == nil {
|
||||
if emojiFile == nil && emojiString == nil {
|
||||
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
|
||||
if let emojiFile = emojiFile {
|
||||
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 mode: AnimatedStickerMode = .direct(cachePathPrefix: pathPrefix)
|
||||
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) {
|
||||
var displaySize = CGSize(width: 180.0, height: 180.0)
|
||||
let telegramFile = self.telegramFile
|
||||
let emojiFile = self.emojiFile
|
||||
let telegramDice = self.telegramDice
|
||||
let emojiString = self.emojiString
|
||||
let layoutConstants = self.layoutConstants
|
||||
let imageLayout = self.imageNode.asyncLayout()
|
||||
let makeDateAndStatusLayout = self.dateAndStatusNode.asyncLayout()
|
||||
@ -774,6 +803,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
let currentShareButtonNode = self.shareButtonNode
|
||||
let currentForwardInfo = self.appliedForwardInfo
|
||||
|
||||
let textLayout = TextNodeWithEntities.asyncLayout(self.textNode)
|
||||
|
||||
return { item, params, mergedTop, mergedBottom, dateHeaderAtBottom in
|
||||
let accessibilityData = ChatMessageAccessibilityData(item: item, isSelected: nil)
|
||||
let layoutConstants = chatMessageItemLayoutConstants(layoutConstants, params: params, presentationData: item.presentationData)
|
||||
@ -781,6 +812,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
|
||||
var imageSize: CGSize = CGSize(width: 200.0, height: 200.0)
|
||||
var imageVerticalInset: CGFloat = 0.0
|
||||
var imageTopPadding: CGFloat = 0.0
|
||||
var imageBottomPadding: CGFloat = 0.0
|
||||
var imageHorizontalOffset: CGFloat = 0.0
|
||||
if !(telegramFile?.videoThumbnails.isEmpty ?? true) {
|
||||
displaySize = CGSize(width: 240.0, height: 240.0)
|
||||
@ -788,27 +821,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
imageHorizontalOffset = 12.0
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
var textLayoutAndApply: (TextNodeLayout, (TextNodeWithEntities.Arguments) -> TextNodeWithEntities)?
|
||||
var imageInset: CGFloat = 10.0
|
||||
|
||||
let avatarInset: CGFloat
|
||||
var hasAvatar = false
|
||||
@ -909,6 +923,56 @@ 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 = 0.0
|
||||
imageTopPadding = 12.0
|
||||
imageBottomPadding = 20.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)
|
||||
if dateHeaderAtBottom {
|
||||
layoutInsets.top += layoutConstants.timestampHeaderHeight
|
||||
@ -921,10 +985,10 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
|
||||
let displayLeftInset = params.leftInset + layoutConstants.bubble.edgeInset + avatarInset
|
||||
|
||||
let imageInset: CGFloat = 10.0
|
||||
|
||||
var innerImageSize = imageSize
|
||||
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 + imageTopPadding), size: CGSize(width: imageSize.width, height: imageSize.height))
|
||||
if isEmoji {
|
||||
innerImageSize = imageSize
|
||||
}
|
||||
@ -1072,7 +1136,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 + imageTopPadding + imageBottomPadding
|
||||
} else {
|
||||
contentHeight = max(imageSize.height + imageVerticalInset * 2.0, layoutConstants.image.minDimensions.height)
|
||||
}
|
||||
|
||||
var forwardSource: Peer?
|
||||
var forwardAuthorSignature: String?
|
||||
@ -1179,14 +1248,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
|
||||
if isEmoji {
|
||||
if isEmoji && emojiString == nil {
|
||||
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.contextSourceNode.contentRect = contextContentFrame
|
||||
strongSelf.containerNode.targetNodeForActivationProgressContentRect = strongSelf.contextSourceNode.contentRect
|
||||
|
||||
let animationNodeFrame = updatedContentFrame.insetBy(dx: imageInset, dy: imageInset)
|
||||
|
||||
var file: TelegramMediaFile?
|
||||
@ -1231,10 +1323,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
strongSelf.enableSynchronousImageApply = true
|
||||
imageApply()
|
||||
strongSelf.enableSynchronousImageApply = false
|
||||
|
||||
strongSelf.contextSourceNode.contentRect = strongSelf.imageNode.frame
|
||||
strongSelf.containerNode.targetNodeForActivationProgressContentRect = strongSelf.contextSourceNode.contentRect
|
||||
|
||||
|
||||
if let updatedShareButtonNode = updatedShareButtonNode {
|
||||
if updatedShareButtonNode !== strongSelf.shareButtonNode {
|
||||
if let shareButtonNode = strongSelf.shareButtonNode {
|
||||
@ -1245,13 +1334,13 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
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)
|
||||
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 {
|
||||
shareButtonNode.removeFromSupernode()
|
||||
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)
|
||||
dateAndStatusApply(animation)
|
||||
|
||||
@ -1337,7 +1426,6 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
strongSelf.replyInfoNode = nil
|
||||
}
|
||||
|
||||
|
||||
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))
|
||||
|
||||
@ -1367,7 +1455,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
strongSelf.addSubnode(deliveryFailedNode)
|
||||
}
|
||||
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 {
|
||||
deliveryFailedNode.frame = deliveryFailedFrame
|
||||
transition.animatePositionAdditive(node: deliveryFailedNode, offset: CGPoint(x: deliveryFailedInset, y: 0.0))
|
||||
@ -1385,7 +1473,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
if let actionButtonsSizeAndApply = actionButtonsSizeAndApply {
|
||||
let actionButtonsNode = actionButtonsSizeAndApply.1(animation)
|
||||
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
|
||||
if actionButtonsNode !== strongSelf.actionButtonsNode {
|
||||
strongSelf.actionButtonsNode = actionButtonsNode
|
||||
@ -1412,7 +1500,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
|
||||
if let reactionButtonsSizeAndApply = reactionButtonsSizeAndApply {
|
||||
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 {
|
||||
reactionButtonsFrame.origin.x = imageFrame.maxX - reactionButtonsSizeAndApply.0.width
|
||||
}
|
||||
@ -1820,10 +1908,10 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
return .optionalAction({
|
||||
item.controllerInteraction.displayDiceTooltip(dice)
|
||||
})
|
||||
} else if let emojiFile = self.emojiFile, !emojiFile.isCustomEmoji {
|
||||
} else if let emojiFile = self.emojiFile {
|
||||
if let animationNode = self.animationNode as? AnimatedStickerNode, let _ = recognizer {
|
||||
var shouldPlay = false
|
||||
if !animationNode.isPlaying {
|
||||
if !animationNode.isPlaying && !emojiFile.isCustomEmoji {
|
||||
shouldPlay = true
|
||||
}
|
||||
|
||||
@ -1945,6 +2033,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
self.playAdditionalEmojiAnimation(index: index)
|
||||
}
|
||||
}
|
||||
} else if emojiFile.isCustomEmoji {
|
||||
let _ = item.controllerInteraction.openMessage(item.message, .default)
|
||||
}
|
||||
|
||||
if shouldPlay {
|
||||
@ -2494,3 +2584,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
|
||||
|
||||
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 addedContentNodes: [(Message, Bool, ChatMessageBubbleContentNode)]?
|
||||
|
@ -138,7 +138,7 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
|
||||
}
|
||||
if let actions = self.actions, actions.isCopyProtected {
|
||||
self.interfaceInteraction?.displayCopyProtectionTip(self.forwardButton, false)
|
||||
} else {
|
||||
} else if !self.forwardButton.isImplicitlyDisabled {
|
||||
self.interfaceInteraction?.forwardSelectedMessages()
|
||||
}
|
||||
}
|
||||
@ -149,7 +149,7 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
|
||||
}
|
||||
if let actions = self.actions, actions.isCopyProtected {
|
||||
self.interfaceInteraction?.displayCopyProtectionTip(self.shareButton, true)
|
||||
} else {
|
||||
} else if !self.shareButton.isImplicitlyDisabled {
|
||||
self.interfaceInteraction?.shareSelectedMessages()
|
||||
}
|
||||
}
|
||||
|
@ -260,7 +260,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
|
||||
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
|
||||
if item.presentationData.largeEmoji && messageIsElligibleForLargeEmoji(item.message) {
|
||||
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)
|
||||
isEmoji = true
|
||||
|
@ -182,7 +182,7 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode {
|
||||
|
||||
let animationFrame = CGRect(origin: CGPoint(x: floor((size.width - width) / 2.0), y: floor((size.height - width) / 2.0) - bottomInset), size: CGSize(width: width, height: width))
|
||||
|
||||
var colorKeys: [String] = ["__allcolors__"]
|
||||
let colorKeys: [String] = ["__allcolors__"]
|
||||
let animationName: String
|
||||
var animationMode: LottieAnimationComponent.AnimationItem.Mode = .still(position: .end)
|
||||
|
||||
@ -216,14 +216,6 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode {
|
||||
break
|
||||
}
|
||||
|
||||
let emojiColorKeys = [
|
||||
"Ellipse 33.Ellipse 33.Stroke 1",
|
||||
"Ellipse 34.Ellipse 34.Stroke 1",
|
||||
"Oval.Oval.Fill 1",
|
||||
"Oval 2.Oval.Fill 1",
|
||||
"Path 85.Path 85.Stroke 1"
|
||||
]
|
||||
|
||||
if let inputMode = inputMode {
|
||||
switch inputMode {
|
||||
case .keyboard:
|
||||
@ -251,7 +243,7 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode {
|
||||
} else if case .emoji = previousInputMode {
|
||||
animationName = "input_anim_smileToSticker"
|
||||
animationMode = .animating(loop: false)
|
||||
colorKeys = emojiColorKeys
|
||||
// colorKeys = emojiColorKeys
|
||||
} else {
|
||||
animationName = "input_anim_keyToSticker"
|
||||
}
|
||||
@ -266,7 +258,7 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode {
|
||||
} else if case .stickers = previousInputMode {
|
||||
animationName = "input_anim_stickerToSmile"
|
||||
animationMode = .animating(loop: false)
|
||||
colorKeys = emojiColorKeys
|
||||
// colorKeys = emojiColorKeys
|
||||
} else {
|
||||
animationName = "input_anim_keyToSmile"
|
||||
}
|
||||
|
@ -192,7 +192,7 @@ private func matchingEmojiEntry(_ emoji: String) -> (UInt8, UInt8, UInt8)? {
|
||||
}
|
||||
|
||||
func messageIsElligibleForLargeEmoji(_ message: Message) -> Bool {
|
||||
if !message.text.isEmpty && message.text.containsOnlyEmoji && message.text.emojis.count < 4 {
|
||||
if !message.text.isEmpty && message.text.containsOnlyEmoji && message.text.emojis.count < 6 {
|
||||
if !(message.textEntitiesAttribute?.entities.isEmpty ?? true) {
|
||||
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> {
|
||||
var dataSignals: [Signal<MediaResourceData, NoError>] = []
|
||||
for emoji in emoji.emojis {
|
||||
|
Loading…
x
Reference in New Issue
Block a user