Merge commit 'be9735f67073496620d695d3e1594d91a3ba2238'

This commit is contained in:
Ali 2022-08-02 21:40:18 +04:00
commit 4d5f899a59
20 changed files with 351 additions and 113 deletions

View File

@ -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()
}
}

View File

@ -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

View File

@ -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.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Binary file not shown.

View File

@ -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")
}
}
}

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)
}

View File

@ -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)]?

View File

@ -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()
}
}

View File

@ -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

View File

@ -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"
}

View File

@ -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 {