diff --git a/Telegram-iOS/Telegram-iOS-AppStoreLLC.entitlements b/Telegram-iOS/Telegram-iOS-AppStoreLLC.entitlements
index cf2fe70016..896fe795c9 100644
--- a/Telegram-iOS/Telegram-iOS-AppStoreLLC.entitlements
+++ b/Telegram-iOS/Telegram-iOS-AppStoreLLC.entitlements
@@ -2,15 +2,15 @@
- com.apple.developer.icloud-services
-
- CloudDocuments
- CloudKit
-
- com.apple.developer.icloud-container-identifiers
-
- iCloud.$(CFBundleIdentifier)
-
+ com.apple.developer.icloud-services
+
+ CloudDocuments
+ CloudKit
+
+ com.apple.developer.icloud-container-identifiers
+
+ iCloud.$(CFBundleIdentifier)
+
aps-environment
production
com.apple.developer.associated-domains
@@ -33,7 +33,5 @@
merchant.privatbank.test.telergramios
merchant.privatbank.prod.telergram
- com.apple.developer.carplay-messaging
- com.apple.developer.carplay-calling
diff --git a/submodules/Display/Display/ImmediateTextNode.swift b/submodules/Display/Display/ImmediateTextNode.swift
index 66ef62b0ef..1493c5ff8b 100644
--- a/submodules/Display/Display/ImmediateTextNode.swift
+++ b/submodules/Display/Display/ImmediateTextNode.swift
@@ -13,6 +13,7 @@ public class ImmediateTextNode: TextNode {
public var maximumNumberOfLines: Int = 1
public var lineSpacing: CGFloat = 0.0
public var insets: UIEdgeInsets = UIEdgeInsets()
+ public var textShadowColor: UIColor?
private var tapRecognizer: TapLongTapOrDoubleTapGestureRecognizer?
private var linkHighlightingNode: LinkHighlightingNode?
@@ -34,7 +35,7 @@ public class ImmediateTextNode: TextNode {
public func updateLayout(_ constrainedSize: CGSize) -> CGSize {
let makeLayout = TextNode.asyncLayout(self)
- let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, lineSpacing: self.lineSpacing, cutout: nil, insets: self.insets))
+ let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, lineSpacing: self.lineSpacing, cutout: nil, insets: self.insets, textShadowColor: self.textShadowColor))
let _ = apply()
if layout.numberOfLines > 1 {
self.trailingLineWidth = layout.trailingLineWidth
diff --git a/submodules/Display/Display/RuntimeUtils.h b/submodules/Display/Display/RuntimeUtils.h
index 654d3504d4..945d11ae6d 100644
--- a/submodules/Display/Display/RuntimeUtils.h
+++ b/submodules/Display/Display/RuntimeUtils.h
@@ -1,4 +1,5 @@
#import
+#import
typedef enum {
NSObjectAssociationPolicyRetain = 0,
@@ -10,6 +11,7 @@ typedef enum {
+ (void)swizzleInstanceMethodOfClass:(Class)targetClass currentSelector:(SEL)currentSelector newSelector:(SEL)newSelector;
+ (void)swizzleInstanceMethodOfClass:(Class)targetClass currentSelector:(SEL)currentSelector withAnotherClass:(Class)anotherClass newSelector:(SEL)newSelector;
+ (void)swizzleClassMethodOfClass:(Class)targetClass currentSelector:(SEL)currentSelector newSelector:(SEL)newSelector;
++ (CALayer * _Nonnull)makeLayerHostCopy:(CALayer * _Nonnull)another;
@end
diff --git a/submodules/Display/Display/RuntimeUtils.m b/submodules/Display/Display/RuntimeUtils.m
index 639e5848eb..9dd6b0c91d 100644
--- a/submodules/Display/Display/RuntimeUtils.m
+++ b/submodules/Display/Display/RuntimeUtils.m
@@ -2,8 +2,21 @@
#import
+@interface CALayer ()
+
+- (unsigned int)contextId;
+- (void)setContextId:(unsigned int)contextId;
+
+@end
+
@implementation RuntimeUtils
++ (CALayer * _Nonnull)makeLayerHostCopy:(CALayer * _Nonnull)another {
+ CALayer *result = [[NSClassFromString(@"CALayerHost") alloc] init];
+ [result setContextId:[another contextId]];
+ return result;
+}
+
+ (void)swizzleInstanceMethodOfClass:(Class)targetClass currentSelector:(SEL)currentSelector newSelector:(SEL)newSelector {
Method origMethod = nil, newMethod = nil;
diff --git a/submodules/Display/Display/TextNode.swift b/submodules/Display/Display/TextNode.swift
index cf44a8e18c..4f46c270b1 100644
--- a/submodules/Display/Display/TextNode.swift
+++ b/submodules/Display/Display/TextNode.swift
@@ -92,8 +92,9 @@ public final class TextNodeLayoutArguments {
public let cutout: TextNodeCutout?
public let insets: UIEdgeInsets
public let lineColor: UIColor?
+ public let textShadowColor: UIColor?
- public init(attributedString: NSAttributedString?, backgroundColor: UIColor? = nil, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, alignment: NSTextAlignment = .natural, lineSpacing: CGFloat = 0.12, cutout: TextNodeCutout? = nil, insets: UIEdgeInsets = UIEdgeInsets(), lineColor: UIColor? = nil) {
+ public init(attributedString: NSAttributedString?, backgroundColor: UIColor? = nil, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, alignment: NSTextAlignment = .natural, lineSpacing: CGFloat = 0.12, cutout: TextNodeCutout? = nil, insets: UIEdgeInsets = UIEdgeInsets(), lineColor: UIColor? = nil, textShadowColor: UIColor? = nil) {
self.attributedString = attributedString
self.backgroundColor = backgroundColor
self.maximumNumberOfLines = maximumNumberOfLines
@@ -104,6 +105,7 @@ public final class TextNodeLayoutArguments {
self.cutout = cutout
self.insets = insets
self.lineColor = lineColor
+ self.textShadowColor = textShadowColor
}
}
@@ -123,9 +125,10 @@ public final class TextNodeLayout: NSObject {
fileprivate let lines: [TextNodeLine]
fileprivate let blockQuotes: [TextNodeBlockQuote]
fileprivate let lineColor: UIColor?
+ fileprivate let textShadowColor: UIColor?
public let hasRTL: Bool
- fileprivate init(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, alignment: NSTextAlignment, lineSpacing: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, size: CGSize, truncated: Bool, firstLineOffset: CGFloat, lines: [TextNodeLine], blockQuotes: [TextNodeBlockQuote], backgroundColor: UIColor?, lineColor: UIColor?) {
+ fileprivate init(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, alignment: NSTextAlignment, lineSpacing: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, size: CGSize, truncated: Bool, firstLineOffset: CGFloat, lines: [TextNodeLine], blockQuotes: [TextNodeBlockQuote], backgroundColor: UIColor?, lineColor: UIColor?, textShadowColor: UIColor?) {
self.attributedString = attributedString
self.maximumNumberOfLines = maximumNumberOfLines
self.truncationType = truncationType
@@ -141,6 +144,7 @@ public final class TextNodeLayout: NSObject {
self.blockQuotes = blockQuotes
self.backgroundColor = backgroundColor
self.lineColor = lineColor
+ self.textShadowColor = textShadowColor
var hasRTL = false
for line in lines {
if line.isRTL {
@@ -575,7 +579,7 @@ public class TextNode: ASDisplayNode {
}
}
- private class func calculateLayout(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, backgroundColor: UIColor?, constrainedSize: CGSize, alignment: NSTextAlignment, lineSpacingFactor: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, lineColor: UIColor?) -> TextNodeLayout {
+ private class func calculateLayout(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, backgroundColor: UIColor?, constrainedSize: CGSize, alignment: NSTextAlignment, lineSpacingFactor: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, lineColor: UIColor?, textShadowColor: UIColor?) -> TextNodeLayout {
if let attributedString = attributedString {
let stringLength = attributedString.length
@@ -601,7 +605,7 @@ public class TextNode: ASDisplayNode {
var maybeTypesetter: CTTypesetter?
maybeTypesetter = CTTypesetterCreateWithAttributedString(attributedString as CFAttributedString)
if maybeTypesetter == nil {
- return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor)
+ return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor)
}
let typesetter = maybeTypesetter!
@@ -793,9 +797,9 @@ public class TextNode: ASDisplayNode {
}
}
- return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(width: ceil(layoutSize.width) + insets.left + insets.right, height: ceil(layoutSize.height) + insets.top + insets.bottom), truncated: truncated, firstLineOffset: firstLineOffset, lines: lines, blockQuotes: blockQuotes, backgroundColor: backgroundColor, lineColor: lineColor)
+ return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(width: ceil(layoutSize.width) + insets.left + insets.right, height: ceil(layoutSize.height) + insets.top + insets.bottom), truncated: truncated, firstLineOffset: firstLineOffset, lines: lines, blockQuotes: blockQuotes, backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor)
} else {
- return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor)
+ return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor)
}
}
@@ -828,11 +832,15 @@ public class TextNode: ASDisplayNode {
context.fill(bounds)
}
+ if let textShadowColor = layout.textShadowColor {
+ context.setTextDrawingMode(.fill)
+ context.setShadow(offset: CGSize(width: 0.0, height: 1.0), blur: 0.0, color: textShadowColor.cgColor)
+ }
+
let textMatrix = context.textMatrix
let textPosition = context.textPosition
context.textMatrix = CGAffineTransform(scaleX: 1.0, y: -1.0)
-
let alignment = layout.alignment
let offset = CGPoint(x: layout.insets.left, y: layout.insets.top)
@@ -927,11 +935,11 @@ public class TextNode: ASDisplayNode {
if stringMatch {
layout = existingLayout
} else {
- layout = TextNode.calculateLayout(attributedString: arguments.attributedString, maximumNumberOfLines: arguments.maximumNumberOfLines, truncationType: arguments.truncationType, backgroundColor: arguments.backgroundColor, constrainedSize: arguments.constrainedSize, alignment: arguments.alignment, lineSpacingFactor: arguments.lineSpacing, cutout: arguments.cutout, insets: arguments.insets, lineColor: arguments.lineColor)
+ layout = TextNode.calculateLayout(attributedString: arguments.attributedString, maximumNumberOfLines: arguments.maximumNumberOfLines, truncationType: arguments.truncationType, backgroundColor: arguments.backgroundColor, constrainedSize: arguments.constrainedSize, alignment: arguments.alignment, lineSpacingFactor: arguments.lineSpacing, cutout: arguments.cutout, insets: arguments.insets, lineColor: arguments.lineColor, textShadowColor: arguments.textShadowColor)
updated = true
}
} else {
- layout = TextNode.calculateLayout(attributedString: arguments.attributedString, maximumNumberOfLines: arguments.maximumNumberOfLines, truncationType: arguments.truncationType, backgroundColor: arguments.backgroundColor, constrainedSize: arguments.constrainedSize, alignment: arguments.alignment, lineSpacingFactor: arguments.lineSpacing, cutout: arguments.cutout, insets: arguments.insets, lineColor: arguments.lineColor)
+ layout = TextNode.calculateLayout(attributedString: arguments.attributedString, maximumNumberOfLines: arguments.maximumNumberOfLines, truncationType: arguments.truncationType, backgroundColor: arguments.backgroundColor, constrainedSize: arguments.constrainedSize, alignment: arguments.alignment, lineSpacingFactor: arguments.lineSpacing, cutout: arguments.cutout, insets: arguments.insets, lineColor: arguments.lineColor, textShadowColor: arguments.textShadowColor)
updated = true
}
diff --git a/submodules/MediaPlayer/Sources/MediaPlayerNode.swift b/submodules/MediaPlayer/Sources/MediaPlayerNode.swift
index bb310b7a9f..40be380263 100644
--- a/submodules/MediaPlayer/Sources/MediaPlayerNode.swift
+++ b/submodules/MediaPlayer/Sources/MediaPlayerNode.swift
@@ -285,6 +285,11 @@ public final class MediaPlayerNode: ASDisplayNode {
strongSelf.updateLayout()
strongSelf.layer.addSublayer(videoLayer)
+
+ /*let testLayer = RuntimeUtils.makeLayerHostCopy(videoLayer.sublayers![0].sublayers![0])*/
+ //testLayer.frame = CGRect(origin: CGPoint(x: -500.0, y: -300.0), size: CGSize(width: 60.0, height: 60.0))
+ //strongSelf.layer.addSublayer(testLayer)
+
strongSelf.updateState()
}
}
diff --git a/submodules/MediaResources/Sources/CachedResourceRepresentations.swift b/submodules/MediaResources/Sources/CachedResourceRepresentations.swift
index 426255d1f0..4c4b6b8270 100644
--- a/submodules/MediaResources/Sources/CachedResourceRepresentations.swift
+++ b/submodules/MediaResources/Sources/CachedResourceRepresentations.swift
@@ -240,19 +240,51 @@ public final class CachedEmojiRepresentation: CachedMediaResourceRepresentation
}
}
+public enum EmojiFitzModifier: Int32, Equatable {
+ case type12
+ case type3
+ case type4
+ case type5
+ case type6
+
+ public init?(emoji: String) {
+ switch emoji.unicodeScalars.first?.value {
+ case 0x1f3fb:
+ self = .type12
+ case 0x1f3fc:
+ self = .type3
+ case 0x1f3fd:
+ self = .type4
+ case 0x1f3fe:
+ self = .type5
+ case 0x1f3ff:
+ self = .type6
+ default:
+ return nil
+ }
+ }
+}
+
public final class CachedAnimatedStickerFirstFrameRepresentation: CachedMediaResourceRepresentation {
public let keepDuration: CachedMediaRepresentationKeepDuration = .general
public let width: Int32
public let height: Int32
+ public let fitzModifier: EmojiFitzModifier?
- public init(width: Int32, height: Int32) {
+ public init(width: Int32, height: Int32, fitzModifier: EmojiFitzModifier? = nil) {
self.width = width
self.height = height
+ self.fitzModifier = fitzModifier
}
public var uniqueId: String {
- return "animated-sticker-first-frame-\(self.width)x\(self.height)-v1"
+ let version: Int = 1
+ if let fitzModifier = self.fitzModifier {
+ return "animated-sticker-first-frame-\(self.width)x\(self.height)-fitz\(fitzModifier.rawValue)-v\(version)"
+ } else {
+ return "animated-sticker-first-frame-\(self.width)x\(self.height)-v\(version)"
+ }
}
public func isEqual(to: CachedMediaResourceRepresentation) -> Bool {
@@ -263,6 +295,9 @@ public final class CachedAnimatedStickerFirstFrameRepresentation: CachedMediaRes
if other.height != self.height {
return false
}
+ if other.fitzModifier != self.fitzModifier {
+ return false
+ }
return true
} else {
return false
@@ -275,14 +310,21 @@ public final class CachedAnimatedStickerRepresentation: CachedMediaResourceRepre
public let width: Int32
public let height: Int32
+ public let fitzModifier: EmojiFitzModifier?
public var uniqueId: String {
- return "animated-sticker-\(self.width)x\(self.height)-v8"
+ let version: Int = 8
+ if let fitzModifier = self.fitzModifier {
+ return "animated-sticker-\(self.width)x\(self.height)-fitz\(fitzModifier.rawValue)-v\(version)"
+ } else {
+ return "animated-sticker-\(self.width)x\(self.height)-v\(version)"
+ }
}
- public init(width: Int32, height: Int32) {
+ public init(width: Int32, height: Int32, fitzModifier: EmojiFitzModifier? = nil) {
self.width = width
self.height = height
+ self.fitzModifier = fitzModifier
}
public func isEqual(to: CachedMediaResourceRepresentation) -> Bool {
@@ -293,6 +335,9 @@ public final class CachedAnimatedStickerRepresentation: CachedMediaResourceRepre
if other.height != self.height {
return false
}
+ if other.fitzModifier != self.fitzModifier {
+ return false
+ }
return true
} else {
return false
diff --git a/submodules/StickerResources/Sources/StickerResources.swift b/submodules/StickerResources/Sources/StickerResources.swift
index 59b4c7ed9f..7b76c26aaa 100644
--- a/submodules/StickerResources/Sources/StickerResources.swift
+++ b/submodules/StickerResources/Sources/StickerResources.swift
@@ -98,11 +98,12 @@ private func chatMessageStickerDatas(postbox: Postbox, file: TelegramMediaFile,
}
}
-public func chatMessageAnimatedStickerDatas(postbox: Postbox, file: TelegramMediaFile, small: Bool, size: CGSize, fetched: Bool, onlyFullSize: Bool, synchronousLoad: Bool) -> Signal, NoError> {
+public func chatMessageAnimatedStickerDatas(postbox: Postbox, file: TelegramMediaFile, small: Bool, size: CGSize, fitzModifier: EmojiFitzModifier? = nil, fetched: Bool, onlyFullSize: Bool, synchronousLoad: Bool) -> Signal, NoError> {
let thumbnailResource = chatMessageStickerResource(file: file, small: true)
let resource = chatMessageStickerResource(file: file, small: small)
- let maybeFetched = postbox.mediaBox.cachedResourceRepresentation(resource, representation: CachedAnimatedStickerFirstFrameRepresentation(width: Int32(size.width), height: Int32(size.height)), complete: false, fetch: false, attemptSynchronously: synchronousLoad)
+ let firstFrameRepresentation = CachedAnimatedStickerFirstFrameRepresentation(width: Int32(size.width), height: Int32(size.height), fitzModifier: fitzModifier)
+ let maybeFetched = postbox.mediaBox.cachedResourceRepresentation(resource, representation: firstFrameRepresentation, complete: false, fetch: false, attemptSynchronously: synchronousLoad)
return maybeFetched
|> take(1)
@@ -113,7 +114,7 @@ public func chatMessageAnimatedStickerDatas(postbox: Postbox, file: TelegramMedi
return .single(Tuple(nil, loadedData, true))
} else {
let thumbnailData = postbox.mediaBox.cachedResourceRepresentation(thumbnailResource, representation: CachedStickerAJpegRepresentation(size: nil), complete: false)
- let fullSizeData = postbox.mediaBox.cachedResourceRepresentation(resource, representation: CachedAnimatedStickerFirstFrameRepresentation(width: Int32(size.width), height: Int32(size.height)), complete: onlyFullSize)
+ let fullSizeData = postbox.mediaBox.cachedResourceRepresentation(resource, representation: firstFrameRepresentation, complete: onlyFullSize)
|> map { next in
return (next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: .mappedIfSafe), next.complete)
}
@@ -164,7 +165,7 @@ private func chatMessageStickerThumbnailData(postbox: Postbox, file: TelegramMed
let thumbnailData = postbox.mediaBox.cachedResourceRepresentation(thumbnailResource, representation: CachedStickerAJpegRepresentation(size: nil), complete: false)
return Signal { subscriber in
- var fetchThumbnail = fetchedMediaResource(mediaBox: postbox.mediaBox, reference: stickerPackFileReference(file).resourceReference(thumbnailResource)).start()
+ let fetchThumbnail = fetchedMediaResource(mediaBox: postbox.mediaBox, reference: stickerPackFileReference(file).resourceReference(thumbnailResource)).start()
let disposable = (thumbnailData
|> map { thumbnailData -> Data? in
@@ -227,8 +228,9 @@ private func chatMessageStickerPackThumbnailData(postbox: Postbox, resource: Med
}
}
-public func chatMessageAnimationData(postbox: Postbox, resource: MediaResource, width: Int, height: Int, synchronousLoad: Bool) -> Signal {
- let maybeFetched = postbox.mediaBox.cachedResourceRepresentation(resource, representation: CachedAnimatedStickerRepresentation(width: Int32(width), height: Int32(height)), complete: false, fetch: false, attemptSynchronously: synchronousLoad)
+public func chatMessageAnimationData(postbox: Postbox, resource: MediaResource, fitzModifier: EmojiFitzModifier? = nil, width: Int, height: Int, synchronousLoad: Bool) -> Signal {
+ let representation = CachedAnimatedStickerRepresentation(width: Int32(width), height: Int32(height), fitzModifier: fitzModifier)
+ let maybeFetched = postbox.mediaBox.cachedResourceRepresentation(resource, representation: representation, complete: false, fetch: false, attemptSynchronously: synchronousLoad)
return maybeFetched
|> take(1)
@@ -236,12 +238,12 @@ public func chatMessageAnimationData(postbox: Postbox, resource: MediaResource,
if maybeData.complete {
return .single(maybeData)
} else {
- return postbox.mediaBox.cachedResourceRepresentation(resource, representation: CachedAnimatedStickerRepresentation(width: Int32(width), height: Int32(height)), complete: false)
+ return postbox.mediaBox.cachedResourceRepresentation(resource, representation: representation, complete: false)
}
}
}
-public func chatMessageAnimatedStrickerBackingData(postbox: Postbox, fileReference: FileMediaReference, synchronousLoad: Bool) -> Signal, NoError> {
+public func chatMessageAnimatedStickerBackingData(postbox: Postbox, fileReference: FileMediaReference, synchronousLoad: Bool) -> Signal, NoError> {
let resource = fileReference.media.resource
let maybeFetched = postbox.mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: synchronousLoad)
@@ -466,7 +468,7 @@ public func chatMessageSticker(postbox: Postbox, file: TelegramMediaFile, small:
}
}
-public func chatMessageAnimatedSticker(postbox: Postbox, file: TelegramMediaFile, small: Bool, size: CGSize, fetched: Bool = false, onlyFullSize: Bool = false, thumbnail: Bool = false, synchronousLoad: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
+public func chatMessageAnimatedSticker(postbox: Postbox, file: TelegramMediaFile, small: Bool, size: CGSize, fitzModifier: EmojiFitzModifier? = nil, fetched: Bool = false, onlyFullSize: Bool = false, thumbnail: Bool = false, synchronousLoad: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
let signal: Signal, NoError>
if thumbnail {
signal = chatMessageStickerThumbnailData(postbox: postbox, file: file, synchronousLoad: synchronousLoad)
@@ -474,7 +476,7 @@ public func chatMessageAnimatedSticker(postbox: Postbox, file: TelegramMediaFile
return Tuple(data, nil, false)
}
} else {
- signal = chatMessageAnimatedStickerDatas(postbox: postbox, file: file, small: small, size: size, fetched: fetched, onlyFullSize: onlyFullSize, synchronousLoad: synchronousLoad)
+ signal = chatMessageAnimatedStickerDatas(postbox: postbox, file: file, small: small, size: size, fitzModifier: fitzModifier, fetched: fetched, onlyFullSize: onlyFullSize, synchronousLoad: synchronousLoad)
}
return signal
|> map { value in
@@ -496,7 +498,7 @@ public func chatMessageAnimatedSticker(postbox: Postbox, file: TelegramMediaFile
}
var thumbnailImage: (UIImage, UIImage)?
- if fullSizeImage == nil, let thumbnailData = thumbnailData {
+ if fullSizeImage == nil, let thumbnailData = thumbnailData, fitzModifier == nil {
if let image = imageFromAJpeg(data: thumbnailData) {
thumbnailImage = image
}
diff --git a/submodules/TelegramCore/TelegramCore/AccountStateManagementUtils.swift b/submodules/TelegramCore/TelegramCore/AccountStateManagementUtils.swift
index cc01b1c903..51e9cbf389 100644
--- a/submodules/TelegramCore/TelegramCore/AccountStateManagementUtils.swift
+++ b/submodules/TelegramCore/TelegramCore/AccountStateManagementUtils.swift
@@ -924,8 +924,11 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
}
updatedState.addMessages([message], location: .UpperHistoryBlock)
}
- case let .updateServiceNotification(_, date, type, text, media, entities):
- if let date = date {
+ case let .updateServiceNotification(flags, date, type, text, media, entities):
+ let popup = (flags & (1 << 0)) != 0
+ if popup {
+ updatedState.addDisplayAlert(text, isDropAuth: type.hasPrefix("AUTH_KEY_DROP_"))
+ } else if let date = date {
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: 777000)
if updatedState.peers[peerId] == nil {
@@ -969,8 +972,6 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
let message = StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: nil, groupingKey: nil, timestamp: date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: peerId, text: messageText, attributes: attributes, media: medias)
updatedState.addMessages([message], location: .UpperHistoryBlock)
}
- } else {
- updatedState.addDisplayAlert(text, isDropAuth: type.hasPrefix("AUTH_KEY_DROP_"))
}
case let .updateReadChannelInbox(_, folderId, channelId, maxId, stillUnreadCount, pts):
updatedState.resetIncomingReadState(groupId: PeerGroupId(rawValue: folderId ?? 0), peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId), namespace: Namespaces.Message.Cloud, maxIncomingReadId: maxId, count: stillUnreadCount, pts: pts)
diff --git a/submodules/TelegramCore/TelegramCore/ChatHistoryPreloadManager.swift b/submodules/TelegramCore/TelegramCore/ChatHistoryPreloadManager.swift
index a88a5ed0c7..4534620ab3 100644
--- a/submodules/TelegramCore/TelegramCore/ChatHistoryPreloadManager.swift
+++ b/submodules/TelegramCore/TelegramCore/ChatHistoryPreloadManager.swift
@@ -13,6 +13,13 @@ public struct HistoryPreloadIndex: Comparable {
public let isMuted: Bool
public let isPriority: Bool
+ public init(index: ChatListIndex?, hasUnread: Bool, isMuted: Bool, isPriority: Bool) {
+ self.index = index
+ self.hasUnread = hasUnread
+ self.isMuted = isMuted
+ self.isPriority = isPriority
+ }
+
public static func <(lhs: HistoryPreloadIndex, rhs: HistoryPreloadIndex) -> Bool {
if lhs.isPriority != rhs.isPriority {
if lhs.isPriority {
diff --git a/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift b/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift
index 2783aeda28..4375d8269f 100644
--- a/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift
+++ b/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift
@@ -215,6 +215,10 @@ public final class PrincipalThemeEssentialGraphics {
if preview {
self.chatMessageBackgroundIncomingMaskImage = messageBubbleImage(incoming: true, fillColor: UIColor.black, strokeColor: UIColor.clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true)
self.chatMessageBackgroundIncomingImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout)
+ self.chatMessageBackgroundOutgoingMaskImage = messageBubbleImage(incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true)
+ self.chatMessageBackgroundOutgoingImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout)
+ self.checkBubbleFullImage = generateCheckImage(partial: false, color: theme.message.outgoingCheckColor)!
+ self.checkBubblePartialImage = generateCheckImage(partial: true, color: theme.message.outgoingCheckColor)!
self.chatMessageBackgroundIncomingHighlightedImage = emptyImage
self.chatMessageBackgroundIncomingMergedTopMaskImage = emptyImage
self.chatMessageBackgroundIncomingMergedTopImage = emptyImage
@@ -231,8 +235,6 @@ public final class PrincipalThemeEssentialGraphics {
self.chatMessageBackgroundIncomingMergedSideMaskImage = emptyImage
self.chatMessageBackgroundIncomingMergedSideImage = emptyImage
self.chatMessageBackgroundIncomingMergedSideHighlightedImage = emptyImage
- self.chatMessageBackgroundOutgoingMaskImage = messageBubbleImage(incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true)
- self.chatMessageBackgroundOutgoingImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout)
self.chatMessageBackgroundOutgoingHighlightedImage = emptyImage
self.chatMessageBackgroundOutgoingMergedTopMaskImage = emptyImage
self.chatMessageBackgroundOutgoingMergedTopImage = emptyImage
@@ -249,8 +251,6 @@ public final class PrincipalThemeEssentialGraphics {
self.chatMessageBackgroundOutgoingMergedSideMaskImage = emptyImage
self.chatMessageBackgroundOutgoingMergedSideImage = emptyImage
self.chatMessageBackgroundOutgoingMergedSideHighlightedImage = emptyImage
- self.checkBubbleFullImage = emptyImage
- self.checkBubblePartialImage = emptyImage
self.checkMediaFullImage = emptyImage
self.checkMediaPartialImage = emptyImage
self.checkFreeFullImage = emptyImage
diff --git a/submodules/TelegramUI/TelegramUI/AnimatedStickerNode.swift b/submodules/TelegramUI/TelegramUI/AnimatedStickerNode.swift
index 9a94725591..d84515da75 100644
--- a/submodules/TelegramUI/TelegramUI/AnimatedStickerNode.swift
+++ b/submodules/TelegramUI/TelegramUI/AnimatedStickerNode.swift
@@ -8,6 +8,7 @@ import AsyncDisplayKit
import RLottie
import GZip
import Tuples
+import MediaResources
import StickerResources
private final class AnimationFrameCache {
@@ -404,7 +405,7 @@ final class AnimatedStickerNode: ASDisplayNode {
self.addSubnode(self.renderer!)
}
- func setup(account: Account, resource: MediaResource, width: Int, height: Int, playbackMode: AnimatedStickerPlaybackMode = .loop, mode: AnimatedStickerMode) {
+ func setup(account: Account, resource: MediaResource, fitzModifier: EmojiFitzModifier? = nil, width: Int, height: Int, playbackMode: AnimatedStickerPlaybackMode = .loop, mode: AnimatedStickerMode) {
if width < 2 || height < 2 {
return
}
@@ -412,27 +413,27 @@ final class AnimatedStickerNode: ASDisplayNode {
switch mode {
case .direct:
self.disposable.set((account.postbox.mediaBox.resourceData(resource)
- |> deliverOnMainQueue).start(next: { [weak self] data in
- guard let strongSelf = self, data.complete else {
- return
- }
- if let directData = try? Data(contentsOf: URL(fileURLWithPath: data.path), options: [.mappedRead]) {
- strongSelf.directData = Tuple(directData, data.path, width, height)
- }
+ |> deliverOnMainQueue).start(next: { [weak self] data in
+ guard let strongSelf = self, data.complete else {
+ return
+ }
+ if let directData = try? Data(contentsOf: URL(fileURLWithPath: data.path), options: [.mappedRead]) {
+ strongSelf.directData = Tuple(directData, data.path, width, height)
+ }
+ if strongSelf.isPlaying {
+ strongSelf.play()
+ }
+ }))
+ case .cached:
+ self.disposable.set((chatMessageAnimationData(postbox: account.postbox, resource: resource, fitzModifier: fitzModifier, width: width, height: height, synchronousLoad: false)
+ |> deliverOnMainQueue).start(next: { [weak self] data in
+ if let strongSelf = self, data.complete {
+ strongSelf.cachedData = try? Data(contentsOf: URL(fileURLWithPath: data.path), options: [.mappedRead])
if strongSelf.isPlaying {
strongSelf.play()
}
- }))
- case .cached:
- self.disposable.set((chatMessageAnimationData(postbox: account.postbox, resource: resource, width: width, height: height, synchronousLoad: false)
- |> deliverOnMainQueue).start(next: { [weak self] data in
- if let strongSelf = self, data.complete {
- strongSelf.cachedData = try? Data(contentsOf: URL(fileURLWithPath: data.path), options: [.mappedRead])
- if strongSelf.isPlaying {
- strongSelf.play()
- }
- }
- }))
+ }
+ }))
}
}
diff --git a/submodules/TelegramUI/TelegramUI/AnimatedStickerUtils.swift b/submodules/TelegramUI/TelegramUI/AnimatedStickerUtils.swift
index 1d15924e11..7696fc6983 100644
--- a/submodules/TelegramUI/TelegramUI/AnimatedStickerUtils.swift
+++ b/submodules/TelegramUI/TelegramUI/AnimatedStickerUtils.swift
@@ -11,6 +11,7 @@ import Compression
import GZip
import RLottie
import MobileCoreServices
+import MediaResources
public struct LocalBundleResourceId: MediaResourceId {
public let name: String
@@ -65,7 +66,85 @@ public class LocalBundleResource: TelegramMediaResource {
}
}
-func fetchCompressedLottieFirstFrameAJpeg(data: Data, size: CGSize, cacheKey: String) -> Signal {
+let colorKeyRegex = try? NSRegularExpression(pattern: "\"k\":\\[[\\d\\.]+\\,[\\d\\.]+\\,[\\d\\.]+\\,[\\d\\.]+\\]")
+
+private func transformedWithFitzModifier(data: Data, fitzModifier: EmojiFitzModifier?) -> Data {
+ if let fitzModifier = fitzModifier, var string = String(data: data, encoding: .utf8) {
+ let color1: UIColor
+ let color2: UIColor
+ let color3: UIColor
+ let color4: UIColor
+
+ var colors: [UIColor] = [0xf77e41, 0xffb139, 0xffd140, 0xffdf79].map { UIColor(rgb: $0) }
+ let replacementColors: [UIColor]
+ switch fitzModifier {
+ case .type12:
+ replacementColors = [0xca907a, 0xedc5a5, 0xf7e3c3, 0xfbefd6].map { UIColor(rgb: $0) }
+ case .type3:
+ replacementColors = [0xaa7c60, 0xc8a987, 0xddc89f, 0xe6d6b2].map { UIColor(rgb: $0) }
+ case .type4:
+ replacementColors = [0x8c6148, 0xad8562, 0xc49e76, 0xd4b188].map { UIColor(rgb: $0) }
+ case .type5:
+ replacementColors = [0x6e3c2c, 0x925a34, 0xa16e46, 0xac7a52].map { UIColor(rgb: $0) }
+ case .type6:
+ replacementColors = [0x291c12, 0x472a22, 0x573b30, 0x68493c].map { UIColor(rgb: $0) }
+ }
+
+ func colorToString(_ color: UIColor) -> String {
+ var r: CGFloat = 0.0
+ var g: CGFloat = 0.0
+ var b: CGFloat = 0.0
+ if color.getRed(&r, green: &g, blue: &b, alpha: nil) {
+ return "\"k\":[\(r),\(g),\(b),1]"
+ }
+ return ""
+ }
+
+ func match(_ a: Double, _ b: Double, eps: Double) -> Bool {
+ return abs(a - b) < eps
+ }
+
+ var replacements: [(NSTextCheckingResult, String)] = []
+
+ if let colorKeyRegex = colorKeyRegex {
+ let results = colorKeyRegex.matches(in: string, range: NSRange(string.startIndex..., in: string))
+ for result in results.reversed() {
+ if let range = Range(result.range, in: string) {
+ let substring = String(string[range])
+ let color = substring[substring.index(string.startIndex, offsetBy: "\"k\":[".count) ..< substring.index(before: substring.endIndex)]
+ let components = color.split(separator: ",")
+ if components.count == 4, let r = Double(components[0]), let g = Double(components[1]), let b = Double(components[2]), let a = Double(components[3]) {
+ if match(a, 1.0, eps: 0.01) {
+ for i in 0 ..< colors.count {
+ let color = colors[i]
+ var cr: CGFloat = 0.0
+ var cg: CGFloat = 0.0
+ var cb: CGFloat = 0.0
+ if color.getRed(&cr, green: &cg, blue: &cb, alpha: nil) {
+ if match(r, Double(cr), eps: 0.01) && match(g, Double(cg), eps: 0.01) && match(b, Double(cb), eps: 0.01) {
+ replacements.append((result, colorToString(replacementColors[i])))
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ for (result, text) in replacements {
+ if let range = Range(result.range, in: string) {
+ string = string.replacingCharacters(in: range, with: text)
+ }
+ }
+
+ return string.data(using: .utf8) ?? data
+ } else {
+ return data
+ }
+}
+
+func fetchCompressedLottieFirstFrameAJpeg(data: Data, size: CGSize, fitzModifier: EmojiFitzModifier? = nil, cacheKey: String) -> Signal {
return Signal({ subscriber in
let queue = Queue()
@@ -77,72 +156,75 @@ func fetchCompressedLottieFirstFrameAJpeg(data: Data, size: CGSize, cacheKey: St
}
let decompressedData = TGGUnzipData(data, 8 * 1024 * 1024)
- if let decompressedData = decompressedData, let player = LottieInstance(data: decompressedData, cacheKey: cacheKey) {
- if cancelled.with({ $0 }) {
- return
- }
-
- let context = DrawingContext(size: size, scale: 1.0, clear: true)
- player.renderFrame(with: 0, into: context.bytes.assumingMemoryBound(to: UInt8.self), width: Int32(size.width), height: Int32(size.height), bytesPerRow: Int32(context.bytesPerRow))
-
- let yuvaPixelsPerAlphaRow = (Int(size.width) + 1) & (~1)
- assert(yuvaPixelsPerAlphaRow % 2 == 0)
-
- let yuvaLength = Int(size.width) * Int(size.height) * 2 + yuvaPixelsPerAlphaRow * Int(size.height) / 2
- var yuvaFrameData = malloc(yuvaLength)!
- memset(yuvaFrameData, 0, yuvaLength)
-
- defer {
- free(yuvaFrameData)
- }
-
- encodeRGBAToYUVA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), context.bytes.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height), Int32(context.bytesPerRow))
- decodeYUVAToRGBA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), context.bytes.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height), Int32(context.bytesPerRow))
-
- if let colorSourceImage = context.generateImage(), let alphaImage = generateGrayscaleAlphaMaskImage(image: colorSourceImage) {
- let colorContext = DrawingContext(size: size, scale: 1.0, clear: false)
- colorContext.withFlippedContext { c in
- c.setFillColor(UIColor.black.cgColor)
- c.fill(CGRect(origin: CGPoint(), size: size))
- c.draw(colorSourceImage.cgImage!, in: CGRect(origin: CGPoint(), size: size))
- }
- guard let colorImage = colorContext.generateImage() else {
+ if let decompressedData = decompressedData {
+ let decompressedData = transformedWithFitzModifier(data: decompressedData, fitzModifier: fitzModifier)
+ if let player = LottieInstance(data: decompressedData, cacheKey: cacheKey) {
+ if cancelled.with({ $0 }) {
return
}
- let colorData = NSMutableData()
- let alphaData = NSMutableData()
+ let context = DrawingContext(size: size, scale: 1.0, clear: true)
+ player.renderFrame(with: 0, into: context.bytes.assumingMemoryBound(to: UInt8.self), width: Int32(size.width), height: Int32(size.height), bytesPerRow: Int32(context.bytesPerRow))
- if let colorDestination = CGImageDestinationCreateWithData(colorData as CFMutableData, kUTTypeJPEG, 1, nil), let alphaDestination = CGImageDestinationCreateWithData(alphaData as CFMutableData, kUTTypeJPEG, 1, nil) {
- CGImageDestinationSetProperties(colorDestination, [:] as CFDictionary)
- CGImageDestinationSetProperties(alphaDestination, [:] as CFDictionary)
+ let yuvaPixelsPerAlphaRow = (Int(size.width) + 1) & (~1)
+ assert(yuvaPixelsPerAlphaRow % 2 == 0)
+
+ let yuvaLength = Int(size.width) * Int(size.height) * 2 + yuvaPixelsPerAlphaRow * Int(size.height) / 2
+ var yuvaFrameData = malloc(yuvaLength)!
+ memset(yuvaFrameData, 0, yuvaLength)
+
+ defer {
+ free(yuvaFrameData)
+ }
+
+ encodeRGBAToYUVA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), context.bytes.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height), Int32(context.bytesPerRow))
+ decodeYUVAToRGBA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), context.bytes.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height), Int32(context.bytesPerRow))
+
+ if let colorSourceImage = context.generateImage(), let alphaImage = generateGrayscaleAlphaMaskImage(image: colorSourceImage) {
+ let colorContext = DrawingContext(size: size, scale: 1.0, clear: false)
+ colorContext.withFlippedContext { c in
+ c.setFillColor(UIColor.black.cgColor)
+ c.fill(CGRect(origin: CGPoint(), size: size))
+ c.draw(colorSourceImage.cgImage!, in: CGRect(origin: CGPoint(), size: size))
+ }
+ guard let colorImage = colorContext.generateImage() else {
+ return
+ }
- let colorQuality: Float
- let alphaQuality: Float
- colorQuality = 0.5
- alphaQuality = 0.4
+ let colorData = NSMutableData()
+ let alphaData = NSMutableData()
- let options = NSMutableDictionary()
- options.setObject(colorQuality as NSNumber, forKey: kCGImageDestinationLossyCompressionQuality as NSString)
-
- let optionsAlpha = NSMutableDictionary()
- optionsAlpha.setObject(alphaQuality as NSNumber, forKey: kCGImageDestinationLossyCompressionQuality as NSString)
-
- CGImageDestinationAddImage(colorDestination, colorImage.cgImage!, options as CFDictionary)
- CGImageDestinationAddImage(alphaDestination, alphaImage.cgImage!, optionsAlpha as CFDictionary)
- if CGImageDestinationFinalize(colorDestination) && CGImageDestinationFinalize(alphaDestination) {
- let finalData = NSMutableData()
- var colorSize: Int32 = Int32(colorData.length)
- finalData.append(&colorSize, length: 4)
- finalData.append(colorData as Data)
- var alphaSize: Int32 = Int32(alphaData.length)
- finalData.append(&alphaSize, length: 4)
- finalData.append(alphaData as Data)
+ if let colorDestination = CGImageDestinationCreateWithData(colorData as CFMutableData, kUTTypeJPEG, 1, nil), let alphaDestination = CGImageDestinationCreateWithData(alphaData as CFMutableData, kUTTypeJPEG, 1, nil) {
+ CGImageDestinationSetProperties(colorDestination, [:] as CFDictionary)
+ CGImageDestinationSetProperties(alphaDestination, [:] as CFDictionary)
- let tempFile = TempBox.shared.tempFile(fileName: "image.ajpg")
- let _ = try? finalData.write(to: URL(fileURLWithPath: tempFile.path), options: [])
- subscriber.putNext(tempFile)
- subscriber.putCompletion()
+ let colorQuality: Float
+ let alphaQuality: Float
+ colorQuality = 0.5
+ alphaQuality = 0.4
+
+ let options = NSMutableDictionary()
+ options.setObject(colorQuality as NSNumber, forKey: kCGImageDestinationLossyCompressionQuality as NSString)
+
+ let optionsAlpha = NSMutableDictionary()
+ optionsAlpha.setObject(alphaQuality as NSNumber, forKey: kCGImageDestinationLossyCompressionQuality as NSString)
+
+ CGImageDestinationAddImage(colorDestination, colorImage.cgImage!, options as CFDictionary)
+ CGImageDestinationAddImage(alphaDestination, alphaImage.cgImage!, optionsAlpha as CFDictionary)
+ if CGImageDestinationFinalize(colorDestination) && CGImageDestinationFinalize(alphaDestination) {
+ let finalData = NSMutableData()
+ var colorSize: Int32 = Int32(colorData.length)
+ finalData.append(&colorSize, length: 4)
+ finalData.append(colorData as Data)
+ var alphaSize: Int32 = Int32(alphaData.length)
+ finalData.append(&alphaSize, length: 4)
+ finalData.append(alphaData as Data)
+
+ let tempFile = TempBox.shared.tempFile(fileName: "image.ajpg")
+ let _ = try? finalData.write(to: URL(fileURLWithPath: tempFile.path), options: [])
+ subscriber.putNext(tempFile)
+ subscriber.putCompletion()
+ }
}
}
}
@@ -159,7 +241,7 @@ private let threadPool: ThreadPool = {
}()
@available(iOS 9.0, *)
-func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: CGSize, cacheKey: String) -> Signal {
+func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: CGSize, fitzModifier: EmojiFitzModifier? = nil, cacheKey: String) -> Signal {
return Signal({ subscriber in
let cancelled = Atomic(value: false)
@@ -174,127 +256,130 @@ func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: CGSize,
var appendingTime: Double = 0
var deltaTime: Double = 0
var compressionTime: Double = 0
-
+
let decompressedData = TGGUnzipData(data, 8 * 1024 * 1024)
- if let decompressedData = decompressedData, let player = LottieInstance(data: decompressedData, cacheKey: cacheKey) {
- let endFrame = Int(player.frameCount)
-
- if cancelled.with({ $0 }) {
- //print("cancelled 2")
- return
- }
-
- var randomId: Int64 = 0
- arc4random_buf(&randomId, 8)
- let path = NSTemporaryDirectory() + "\(randomId).lz4v"
- guard let fileContext = ManagedFile(queue: nil, path: path, mode: .readwrite) else {
- return
- }
-
- let bytesPerRow = (4 * Int(size.width) + 15) & (~15)
-
- var currentFrame: Int32 = 0
-
- var fps: Int32 = player.frameRate
- var frameCount: Int32 = player.frameCount
- let _ = fileContext.write(&fps, count: 4)
- let _ = fileContext.write(&frameCount, count: 4)
- var widthValue: Int32 = Int32(size.width)
- var heightValue: Int32 = Int32(size.height)
- var bytesPerRowValue: Int32 = Int32(bytesPerRow)
- let _ = fileContext.write(&widthValue, count: 4)
- let _ = fileContext.write(&heightValue, count: 4)
- let _ = fileContext.write(&bytesPerRowValue, count: 4)
-
- let frameLength = bytesPerRow * Int(size.height)
- assert(frameLength % 16 == 0)
-
- let currentFrameData = malloc(frameLength)!
- memset(currentFrameData, 0, frameLength)
-
- let yuvaPixelsPerAlphaRow = (Int(size.width) + 1) & (~1)
- assert(yuvaPixelsPerAlphaRow % 2 == 0)
-
- let yuvaLength = Int(size.width) * Int(size.height) * 2 + yuvaPixelsPerAlphaRow * Int(size.height) / 2
- var yuvaFrameData = malloc(yuvaLength)!
- memset(yuvaFrameData, 0, yuvaLength)
-
- var previousYuvaFrameData = malloc(yuvaLength)!
- memset(previousYuvaFrameData, 0, yuvaLength)
-
- defer {
- free(currentFrameData)
- free(previousYuvaFrameData)
- free(yuvaFrameData)
- }
-
- var compressedFrameData = Data(count: frameLength)
- let compressedFrameDataLength = compressedFrameData.count
-
- let scratchData = malloc(compression_encode_scratch_buffer_size(COMPRESSION_LZFSE))!
- defer {
- free(scratchData)
- }
-
- while currentFrame < endFrame {
+ if let decompressedData = decompressedData {
+ let decompressedData = transformedWithFitzModifier(data: decompressedData, fitzModifier: fitzModifier)
+ if let player = LottieInstance(data: decompressedData, cacheKey: cacheKey) {
+ let endFrame = Int(player.frameCount)
+
if cancelled.with({ $0 }) {
- //print("cancelled 3")
+ //print("cancelled 2")
return
}
- let drawStartTime = CACurrentMediaTime()
+ var randomId: Int64 = 0
+ arc4random_buf(&randomId, 8)
+ let path = NSTemporaryDirectory() + "\(randomId).lz4v"
+ guard let fileContext = ManagedFile(queue: nil, path: path, mode: .readwrite) else {
+ return
+ }
+
+ let bytesPerRow = (4 * Int(size.width) + 15) & (~15)
+
+ var currentFrame: Int32 = 0
+
+ var fps: Int32 = player.frameRate
+ var frameCount: Int32 = player.frameCount
+ let _ = fileContext.write(&fps, count: 4)
+ let _ = fileContext.write(&frameCount, count: 4)
+ var widthValue: Int32 = Int32(size.width)
+ var heightValue: Int32 = Int32(size.height)
+ var bytesPerRowValue: Int32 = Int32(bytesPerRow)
+ let _ = fileContext.write(&widthValue, count: 4)
+ let _ = fileContext.write(&heightValue, count: 4)
+ let _ = fileContext.write(&bytesPerRowValue, count: 4)
+
+ let frameLength = bytesPerRow * Int(size.height)
+ assert(frameLength % 16 == 0)
+
+ let currentFrameData = malloc(frameLength)!
memset(currentFrameData, 0, frameLength)
- player.renderFrame(with: Int32(currentFrame), into: currentFrameData.assumingMemoryBound(to: UInt8.self), width: Int32(size.width), height: Int32(size.height), bytesPerRow: Int32(bytesPerRow))
- drawingTime += CACurrentMediaTime() - drawStartTime
- let appendStartTime = CACurrentMediaTime()
+ let yuvaPixelsPerAlphaRow = (Int(size.width) + 1) & (~1)
+ assert(yuvaPixelsPerAlphaRow % 2 == 0)
- encodeRGBAToYUVA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), currentFrameData.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height), Int32(bytesPerRow))
+ let yuvaLength = Int(size.width) * Int(size.height) * 2 + yuvaPixelsPerAlphaRow * Int(size.height) / 2
+ var yuvaFrameData = malloc(yuvaLength)!
+ memset(yuvaFrameData, 0, yuvaLength)
- appendingTime += CACurrentMediaTime() - appendStartTime
+ var previousYuvaFrameData = malloc(yuvaLength)!
+ memset(previousYuvaFrameData, 0, yuvaLength)
- let deltaStartTime = CACurrentMediaTime()
- var lhs = previousYuvaFrameData.assumingMemoryBound(to: UInt64.self)
- var rhs = yuvaFrameData.assumingMemoryBound(to: UInt64.self)
- for _ in 0 ..< yuvaLength / 8 {
- lhs.pointee = rhs.pointee ^ lhs.pointee
- lhs = lhs.advanced(by: 1)
- rhs = rhs.advanced(by: 1)
- }
- var lhsRest = previousYuvaFrameData.assumingMemoryBound(to: UInt8.self).advanced(by: (yuvaLength / 8) * 8)
- var rhsRest = yuvaFrameData.assumingMemoryBound(to: UInt8.self).advanced(by: (yuvaLength / 8) * 8)
- for _ in (yuvaLength / 8) * 8 ..< yuvaLength {
- lhsRest.pointee = rhsRest.pointee ^ lhsRest.pointee
- lhsRest = lhsRest.advanced(by: 1)
- rhsRest = rhsRest.advanced(by: 1)
- }
- deltaTime += CACurrentMediaTime() - deltaStartTime
-
- let compressionStartTime = CACurrentMediaTime()
- compressedFrameData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in
- let length = compression_encode_buffer(bytes, compressedFrameDataLength, previousYuvaFrameData.assumingMemoryBound(to: UInt8.self), yuvaLength, scratchData, COMPRESSION_LZFSE)
- var frameLengthValue: Int32 = Int32(length)
- let _ = fileContext.write(&frameLengthValue, count: 4)
- let _ = fileContext.write(bytes, count: length)
+ defer {
+ free(currentFrameData)
+ free(previousYuvaFrameData)
+ free(yuvaFrameData)
}
- let tmp = previousYuvaFrameData
- previousYuvaFrameData = yuvaFrameData
- yuvaFrameData = tmp
+ var compressedFrameData = Data(count: frameLength)
+ let compressedFrameDataLength = compressedFrameData.count
- compressionTime += CACurrentMediaTime() - compressionStartTime
+ let scratchData = malloc(compression_encode_scratch_buffer_size(COMPRESSION_LZFSE))!
+ defer {
+ free(scratchData)
+ }
- currentFrame += 1
+ while currentFrame < endFrame {
+ if cancelled.with({ $0 }) {
+ //print("cancelled 3")
+ return
+ }
+
+ let drawStartTime = CACurrentMediaTime()
+ memset(currentFrameData, 0, frameLength)
+ player.renderFrame(with: Int32(currentFrame), into: currentFrameData.assumingMemoryBound(to: UInt8.self), width: Int32(size.width), height: Int32(size.height), bytesPerRow: Int32(bytesPerRow))
+ drawingTime += CACurrentMediaTime() - drawStartTime
+
+ let appendStartTime = CACurrentMediaTime()
+
+ encodeRGBAToYUVA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), currentFrameData.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height), Int32(bytesPerRow))
+
+ appendingTime += CACurrentMediaTime() - appendStartTime
+
+ let deltaStartTime = CACurrentMediaTime()
+ var lhs = previousYuvaFrameData.assumingMemoryBound(to: UInt64.self)
+ var rhs = yuvaFrameData.assumingMemoryBound(to: UInt64.self)
+ for _ in 0 ..< yuvaLength / 8 {
+ lhs.pointee = rhs.pointee ^ lhs.pointee
+ lhs = lhs.advanced(by: 1)
+ rhs = rhs.advanced(by: 1)
+ }
+ var lhsRest = previousYuvaFrameData.assumingMemoryBound(to: UInt8.self).advanced(by: (yuvaLength / 8) * 8)
+ var rhsRest = yuvaFrameData.assumingMemoryBound(to: UInt8.self).advanced(by: (yuvaLength / 8) * 8)
+ for _ in (yuvaLength / 8) * 8 ..< yuvaLength {
+ lhsRest.pointee = rhsRest.pointee ^ lhsRest.pointee
+ lhsRest = lhsRest.advanced(by: 1)
+ rhsRest = rhsRest.advanced(by: 1)
+ }
+ deltaTime += CACurrentMediaTime() - deltaStartTime
+
+ let compressionStartTime = CACurrentMediaTime()
+ compressedFrameData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in
+ let length = compression_encode_buffer(bytes, compressedFrameDataLength, previousYuvaFrameData.assumingMemoryBound(to: UInt8.self), yuvaLength, scratchData, COMPRESSION_LZFSE)
+ var frameLengthValue: Int32 = Int32(length)
+ let _ = fileContext.write(&frameLengthValue, count: 4)
+ let _ = fileContext.write(bytes, count: length)
+ }
+
+ let tmp = previousYuvaFrameData
+ previousYuvaFrameData = yuvaFrameData
+ yuvaFrameData = tmp
+
+ compressionTime += CACurrentMediaTime() - compressionStartTime
+
+ currentFrame += 1
+ }
+
+ subscriber.putNext(path)
+ subscriber.putCompletion()
+ print("animation render time \(CACurrentMediaTime() - startTime)")
+ print("of which drawing time \(drawingTime)")
+ print("of which appending time \(appendingTime)")
+ print("of which delta time \(deltaTime)")
+
+ print("of which compression time \(compressionTime)")
}
-
- subscriber.putNext(path)
- subscriber.putCompletion()
- print("animation render time \(CACurrentMediaTime() - startTime)")
- print("of which drawing time \(drawingTime)")
- print("of which appending time \(appendingTime)")
- print("of which delta time \(deltaTime)")
-
- print("of which compression time \(compressionTime)")
}
}))
return ActionDisposable {
diff --git a/submodules/TelegramUI/TelegramUI/ChatAnimationGalleryItem.swift b/submodules/TelegramUI/TelegramUI/ChatAnimationGalleryItem.swift
index be9f81d49b..f179391722 100644
--- a/submodules/TelegramUI/TelegramUI/ChatAnimationGalleryItem.swift
+++ b/submodules/TelegramUI/TelegramUI/ChatAnimationGalleryItem.swift
@@ -137,7 +137,7 @@ final class ChatAnimationGalleryItemNode: ZoomableContentGalleryItemNode {
func setFile(context: AccountContext, fileReference: FileMediaReference) {
if self.contextAndMedia == nil || !self.contextAndMedia!.1.media.isEqual(to: fileReference.media) {
- let signal = chatMessageAnimatedStrickerBackingData(postbox: context.account.postbox, fileReference: fileReference, synchronousLoad: false)
+ let signal = chatMessageAnimatedStickerBackingData(postbox: context.account.postbox, fileReference: fileReference, synchronousLoad: false)
|> mapToSignal { value -> Signal in
if value._1, let data = value._0 {
return .single(data)
diff --git a/submodules/TelegramUI/TelegramUI/ChatHistoryEntriesForView.swift b/submodules/TelegramUI/TelegramUI/ChatHistoryEntriesForView.swift
index 4c4d62bd62..48e2834d17 100644
--- a/submodules/TelegramUI/TelegramUI/ChatHistoryEntriesForView.swift
+++ b/submodules/TelegramUI/TelegramUI/ChatHistoryEntriesForView.swift
@@ -40,7 +40,7 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
var contentTypeHint: ChatMessageEntryContentType = .generic
if presentationData.largeEmoji {
- if let _ = associatedData.animatedEmojiStickers[entry.message.text.trimmedEmoji] {
+ if let _ = associatedData.animatedEmojiStickers[entry.message.text.basicEmoji.0] {
contentTypeHint = .animatedEmoji
} else if messageIsElligibleForLargeEmoji(entry.message) {
contentTypeHint = .largeEmoji
diff --git a/submodules/TelegramUI/TelegramUI/ChatHistoryListNode.swift b/submodules/TelegramUI/TelegramUI/ChatHistoryListNode.swift
index 927b56aca1..747e417e1b 100644
--- a/submodules/TelegramUI/TelegramUI/ChatHistoryListNode.swift
+++ b/submodules/TelegramUI/TelegramUI/ChatHistoryListNode.swift
@@ -548,7 +548,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
var animatedEmojiStickers: [String: StickerPackItem] = [:]
for case let item as StickerPackItem in items {
if let emoji = item.getStringRepresentationsOfIndexKeys().first {
- animatedEmojiStickers[emoji.trimmedEmoji] = item
+ animatedEmojiStickers[emoji.basicEmoji.0] = item
}
}
return animatedEmojiStickers
diff --git a/submodules/TelegramUI/TelegramUI/ChatInterfaceInputContexts.swift b/submodules/TelegramUI/TelegramUI/ChatInterfaceInputContexts.swift
index bdfdebc011..6470bf59eb 100644
--- a/submodules/TelegramUI/TelegramUI/ChatInterfaceInputContexts.swift
+++ b/submodules/TelegramUI/TelegramUI/ChatInterfaceInputContexts.swift
@@ -177,7 +177,7 @@ func inputContextQueriesForChatPresentationIntefaceState(_ chatPresentationInter
for (possibleQueryRange, possibleTypes, additionalStringRange) in textInputStateContextQueryRangeAndType(inputState) {
let query = inputString.substring(with: possibleQueryRange)
if possibleTypes == [.emoji] {
- result.append(.emoji(query.basicEmoji))
+ result.append(.emoji(query.basicEmoji.0))
} else if possibleTypes == [.hashtag] {
result.append(.hashtag(query))
} else if possibleTypes == [.mention] {
diff --git a/submodules/TelegramUI/TelegramUI/ChatInterfaceStateContextQueries.swift b/submodules/TelegramUI/TelegramUI/ChatInterfaceStateContextQueries.swift
index fe61e57bbd..54c653cda7 100644
--- a/submodules/TelegramUI/TelegramUI/ChatInterfaceStateContextQueries.swift
+++ b/submodules/TelegramUI/TelegramUI/ChatInterfaceStateContextQueries.swift
@@ -90,7 +90,7 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, pee
case .installed:
scope = [.installed]
}
- return searchStickers(account: context.account, query: query.trimmedEmoji, scope: scope)
+ return searchStickers(account: context.account, query: query.basicEmoji.0, scope: scope)
|> introduceError(ChatContextQueryError.self)
}
|> map { stickers -> (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult? in
diff --git a/submodules/TelegramUI/TelegramUI/ChatItemGalleryFooterContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatItemGalleryFooterContentNode.swift
index aee828460d..6bb4ed3cd7 100644
--- a/submodules/TelegramUI/TelegramUI/ChatItemGalleryFooterContentNode.swift
+++ b/submodules/TelegramUI/TelegramUI/ChatItemGalleryFooterContentNode.swift
@@ -172,7 +172,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
private let messageContextDisposable = MetaDisposable()
- private var videoFramePreviewNode: ASImageNode?
+ private var videoFramePreviewNode: (ASImageNode, ImmediateTextNode)?
private var validLayout: (CGSize, LayoutMetrics, CGFloat, CGFloat, CGFloat, CGFloat)?
@@ -235,6 +235,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
}
private var scrubbingHandleRelativePosition: CGFloat = 0.0
+ private var scrubbingVisualTimestamp: Double?
var scrubberView: ChatVideoGalleryItemScrubberView? = nil {
willSet {
@@ -245,6 +246,23 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
didSet {
if let scrubberView = self.scrubberView {
self.view.addSubview(scrubberView)
+ scrubberView.updateScrubbingVisual = { [weak self] value in
+ guard let strongSelf = self else {
+ return
+ }
+ if let value = value {
+ strongSelf.scrubbingVisualTimestamp = value
+ if let (videoFramePreviewNode, videoFrameTextNode) = strongSelf.videoFramePreviewNode {
+ videoFrameTextNode.attributedText = NSAttributedString(string: stringForDuration(Int32(value)), font: Font.regular(13.0), textColor: .white)
+ let textSize = videoFrameTextNode.updateLayout(CGSize(width: 100.0, height: 100.0))
+ let imageFrame = videoFramePreviewNode.frame
+ let textOffset = (Int((imageFrame.size.width - videoFrameTextNode.bounds.width) / 2) / 2) * 2
+ videoFrameTextNode.frame = CGRect(origin: CGPoint(x: CGFloat(textOffset), y: imageFrame.size.height - videoFrameTextNode.bounds.height - 5.0), size: textSize)
+ }
+ } else {
+ strongSelf.scrubbingVisualTimestamp = nil
+ }
+ }
scrubberView.updateScrubbingHandlePosition = { [weak self] value in
guard let strongSelf = self else {
return
@@ -643,15 +661,31 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
self.dateNode.frame = CGRect(origin: CGPoint(x: floor((width - dateSize.width) / 2.0), y: panelHeight - bottomInset - 44.0 + floor((44.0 - dateSize.height - authorNameSize.height - labelsSpacing) / 2.0) + authorNameSize.height + labelsSpacing), size: dateSize)
}
- if let videoFramePreviewNode = self.videoFramePreviewNode {
+ if let (videoFramePreviewNode, videoFrameTextNode) = self.videoFramePreviewNode {
let intrinsicImageSize = videoFramePreviewNode.image?.size ?? CGSize(width: 320.0, height: 240.0)
- let imageSize = intrinsicImageSize.aspectFitted(CGSize(width: 200.0, height: 200.0))
- var imageFrame = CGRect(origin: CGPoint(x: leftInset + floor(self.scrubbingHandleRelativePosition * (width - leftInset - rightInset) - imageSize.width / 2.0), y: self.scrollNode.frame.minY - 10.0 - imageSize.height), size: imageSize)
+ let fitSize: CGSize
+ if intrinsicImageSize.width < intrinsicImageSize.height {
+ fitSize = CGSize(width: 90.0, height: 160.0)
+ } else {
+ fitSize = CGSize(width: 160.0, height: 90.0)
+ }
+ let scrubberInset: CGFloat
+ if size.width > size.height {
+ scrubberInset = 58.0
+ } else {
+ scrubberInset = 13.0
+ }
+
+ let imageSize = intrinsicImageSize.aspectFitted(fitSize)
+ var imageFrame = CGRect(origin: CGPoint(x: leftInset + scrubberInset + floor(self.scrubbingHandleRelativePosition * (width - leftInset - rightInset - scrubberInset * 2.0) - imageSize.width / 2.0), y: self.scrollNode.frame.minY - 6.0 - imageSize.height), size: imageSize)
imageFrame.origin.x = min(imageFrame.origin.x, width - rightInset - 10.0 - imageSize.width)
imageFrame.origin.x = max(imageFrame.origin.x, leftInset + 10.0)
videoFramePreviewNode.frame = imageFrame
videoFramePreviewNode.subnodes?.first?.frame = CGRect(origin: CGPoint(), size: imageFrame.size)
+
+ let textOffset = (Int((imageFrame.size.width - videoFrameTextNode.bounds.width) / 2) / 2) * 2
+ videoFrameTextNode.frame = CGRect(origin: CGPoint(x: CGFloat(textOffset), y: imageFrame.size.height - videoFrameTextNode.bounds.height - 5.0), size: videoFrameTextNode.bounds.size)
}
return panelHeight
@@ -1028,7 +1062,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
}
func setFramePreviewImageIsLoading() {
- if self.videoFramePreviewNode?.image != nil {
+ if self.videoFramePreviewNode?.0.image != nil {
//self.videoFramePreviewNode?.subnodes?.first?.alpha = 1.0
}
}
@@ -1036,17 +1070,34 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
func setFramePreviewImage(image: UIImage?) {
if let image = image {
let videoFramePreviewNode: ASImageNode
+ let videoFrameTextNode: ImmediateTextNode
var animateIn = false
if let current = self.videoFramePreviewNode {
- videoFramePreviewNode = current
+ videoFramePreviewNode = current.0
+ videoFrameTextNode = current.1
} else {
videoFramePreviewNode = ASImageNode()
videoFramePreviewNode.displaysAsynchronously = false
videoFramePreviewNode.displayWithoutProcessing = true
+ videoFramePreviewNode.clipsToBounds = true
+ videoFramePreviewNode.cornerRadius = 6.0
+
let dimNode = ASDisplayNode()
dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
videoFramePreviewNode.addSubnode(dimNode)
- self.videoFramePreviewNode = videoFramePreviewNode
+
+ videoFrameTextNode = ImmediateTextNode()
+ videoFrameTextNode.displaysAsynchronously = false
+ videoFrameTextNode.maximumNumberOfLines = 1
+ videoFrameTextNode.textShadowColor = .black
+ if let scrubbingVisualTimestamp = self.scrubbingVisualTimestamp {
+ videoFrameTextNode.attributedText = NSAttributedString(string: stringForDuration(Int32(scrubbingVisualTimestamp)), font: Font.regular(13.0), textColor: .white)
+ }
+ let textSize = videoFrameTextNode.updateLayout(CGSize(width: 100.0, height: 100.0))
+ videoFrameTextNode.frame = CGRect(origin: CGPoint(), size: textSize)
+ videoFramePreviewNode.addSubnode(videoFrameTextNode)
+
+ self.videoFramePreviewNode = (videoFramePreviewNode, videoFrameTextNode)
self.addSubnode(videoFramePreviewNode)
animateIn = true
}
@@ -1059,7 +1110,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
if animateIn {
videoFramePreviewNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
}
- } else if let videoFramePreviewNode = self.videoFramePreviewNode {
+ } else if let (videoFramePreviewNode, _) = self.videoFramePreviewNode {
self.videoFramePreviewNode = nil
videoFramePreviewNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false, completion: { [weak videoFramePreviewNode] _ in
videoFramePreviewNode?.removeFromSupernode()
diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift
index e6f5443e94..8cbf70678b 100644
--- a/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift
+++ b/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift
@@ -10,6 +10,7 @@ import TelegramPresentationData
import Compression
import TextFormat
import AccountContext
+import MediaResources
import StickerResources
import ContextUI
@@ -238,12 +239,17 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
}
}
- if self.telegramFile == nil, let emojiFile = item.associatedData.animatedEmojiStickers[item.message.text.trimmedEmoji]?.file {
+ let (emoji, fitz) = item.message.text.basicEmoji
+ if self.telegramFile == nil, let emojiFile = item.associatedData.animatedEmojiStickers[emoji]?.file {
if self.emojiFile?.id != emojiFile.id {
self.emojiFile = emojiFile
let dimensions = emojiFile.dimensions ?? CGSize(width: 512.0, height: 512.0)
- self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.context.account.postbox, file: emojiFile, small: false, size: dimensions.aspectFilled(CGSize(width: 384.0, height: 384.0)), thumbnail: false))
- self.disposable.set(freeMediaFileInteractiveFetched(account: item.context.account, fileReference: .message(message: MessageReference(item.message), media: emojiFile)).start())
+ var fitzModifier: EmojiFitzModifier?
+ if let fitz = fitz {
+ fitzModifier = EmojiFitzModifier(emoji: fitz)
+ }
+ self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.context.account.postbox, file: emojiFile, small: false, size: dimensions.aspectFilled(CGSize(width: 384.0, height: 384.0)), fitzModifier: fitzModifier, thumbnail: false))
+ self.disposable.set(freeMediaFileInteractiveFetched(account: item.context.account, fileReference: .standalone(media: emojiFile)).start())
self.updateVisibility()
}
}
@@ -276,7 +282,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
var file: TelegramMediaFile?
var playbackMode: AnimatedStickerPlaybackMode = .loop
var isEmoji = false
-
+ var fitzModifier: EmojiFitzModifier?
+
if let telegramFile = self.telegramFile {
file = telegramFile
if !item.controllerInteraction.stickerSettings.loopAnimatedStickers {
@@ -286,12 +293,16 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
isEmoji = true
file = emojiFile
playbackMode = .once
+ let (_, fitz) = item.message.text.basicEmoji
+ if let fitz = fitz {
+ fitzModifier = EmojiFitzModifier(emoji: fitz)
+ }
}
if let file = file {
let dimensions = file.dimensions ?? CGSize(width: 512.0, height: 512.0)
let fittedSize = isEmoji ? dimensions.aspectFilled(CGSize(width: 384.0, height: 384.0)) : dimensions.aspectFitted(CGSize(width: 384.0, height: 384.0))
- self.animationNode.setup(account: item.context.account, resource: file.resource, width: Int(fittedSize.width), height: Int(fittedSize.height), playbackMode: playbackMode, mode: .cached)
+ self.animationNode.setup(account: item.context.account, resource: file.resource, fitzModifier: fitzModifier, width: Int(fittedSize.width), height: Int(fittedSize.height), playbackMode: playbackMode, mode: .cached)
}
}
}
diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageItem.swift b/submodules/TelegramUI/TelegramUI/ChatMessageItem.swift
index 155a64cb8f..dbcc75eb01 100644
--- a/submodules/TelegramUI/TelegramUI/ChatMessageItem.swift
+++ b/submodules/TelegramUI/TelegramUI/ChatMessageItem.swift
@@ -388,7 +388,7 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
}
if viewClassName == ChatMessageBubbleItemNode.self && self.presentationData.largeEmoji && messageIsElligibleForLargeEmoji(self.message) {
- if let _ = self.associatedData.animatedEmojiStickers[self.message.text.trimmedEmoji] {
+ if let _ = self.associatedData.animatedEmojiStickers[self.message.text.basicEmoji.0] {
viewClassName = ChatMessageAnimatedStickerItemNode.self
} else {
viewClassName = ChatMessageStickerItemNode.self
diff --git a/submodules/TelegramUI/TelegramUI/ChatVideoGalleryItemScrubberView.swift b/submodules/TelegramUI/TelegramUI/ChatVideoGalleryItemScrubberView.swift
index ab588236ca..9efbcc2e9a 100644
--- a/submodules/TelegramUI/TelegramUI/ChatVideoGalleryItemScrubberView.swift
+++ b/submodules/TelegramUI/TelegramUI/ChatVideoGalleryItemScrubberView.swift
@@ -43,6 +43,7 @@ final class ChatVideoGalleryItemScrubberView: UIView {
}
var updateScrubbing: (Double?) -> Void = { _ in }
+ var updateScrubbingVisual: (Double?) -> Void = { _ in }
var updateScrubbingHandlePosition: (CGFloat) -> Void = { _ in }
var seek: (Double) -> Void = { _ in }
@@ -67,6 +68,7 @@ final class ChatVideoGalleryItemScrubberView: UIView {
self.scrubberNode.update = { [weak self] timestamp, position in
self?.updateScrubbing(timestamp)
+ self?.updateScrubbingVisual(timestamp)
self?.updateScrubbingHandlePosition(position)
}
diff --git a/submodules/TelegramUI/TelegramUI/EmojiUtils.swift b/submodules/TelegramUI/TelegramUI/EmojiUtils.swift
index 37388e101e..32c3535f45 100644
--- a/submodules/TelegramUI/TelegramUI/EmojiUtils.swift
+++ b/submodules/TelegramUI/TelegramUI/EmojiUtils.swift
@@ -129,7 +129,7 @@ extension String {
return string
}
- var basicEmoji: String {
+ var basicEmoji: (String, String?) {
let fitzCodes: [UInt32] = [
0x1f3fb,
0x1f3fc,
@@ -139,13 +139,18 @@ extension String {
]
var string = ""
+ var fitzModifier: String?
for scalar in self.unicodeScalars {
if fitzCodes.contains(scalar.value) {
+ fitzModifier = String(scalar)
continue
}
string.unicodeScalars.append(scalar)
+ if scalar.value == 0x2764, self.unicodeScalars.count > 1, self.emojis.count == 1 {
+ break
+ }
}
- return string
+ return (string, fitzModifier)
}
var trimmedEmoji: String {
diff --git a/submodules/TelegramUI/TelegramUI/FetchCachedRepresentations.swift b/submodules/TelegramUI/TelegramUI/FetchCachedRepresentations.swift
index 8acfe5e9db..6186056d38 100644
--- a/submodules/TelegramUI/TelegramUI/FetchCachedRepresentations.swift
+++ b/submodules/TelegramUI/TelegramUI/FetchCachedRepresentations.swift
@@ -113,19 +113,7 @@ public func fetchCachedResourceRepresentation(account: Account, resource: MediaR
} else if let representation = representation as? CachedEmojiRepresentation {
return fetchEmojiRepresentation(account: account, resource: resource, representation: representation)
} else if let representation = representation as? CachedAnimatedStickerRepresentation {
- let data: Signal
-// if let resource = resource as? LocalBundleResource {
-// data = Signal { subscriber in
-// if let path = frameworkBundle.path(forResource: resource.name, ofType: resource.ext), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: [.mappedRead]) {
-// subscriber.putNext(MediaResourceData(path: path, offset: 0, size: data.count, complete: true))
-// subscriber.putCompletion()
-// }
-// return EmptyDisposable
-// }
-// } else {
- data = account.postbox.mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false))
-// }
- return data
+ return account.postbox.mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false))
|> mapToSignal { data -> Signal in
if !data.complete {
return .complete()
@@ -909,7 +897,7 @@ private func fetchEmojiRepresentation(account: Account, resource: MediaResource,
private func fetchAnimatedStickerFirstFrameRepresentation(account: Account, resource: MediaResource, resourceData: MediaResourceData, representation: CachedAnimatedStickerFirstFrameRepresentation) -> Signal {
return Signal({ subscriber in
if let data = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.mappedIfSafe]) {
- return fetchCompressedLottieFirstFrameAJpeg(data: data, size: CGSize(width: CGFloat(representation.width), height: CGFloat(representation.height)), cacheKey: "\(resource.id.uniqueId)-\(representation.uniqueId)").start(next: { file in
+ return fetchCompressedLottieFirstFrameAJpeg(data: data, size: CGSize(width: CGFloat(representation.width), height: CGFloat(representation.height)), fitzModifier: representation.fitzModifier, cacheKey: "\(resource.id.uniqueId)-\(representation.uniqueId)").start(next: { file in
subscriber.putNext(.tempFile(file))
subscriber.putCompletion()
})
@@ -924,7 +912,7 @@ private func fetchAnimatedStickerRepresentation(account: Account, resource: Medi
return Signal({ subscriber in
if let data = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.mappedIfSafe]) {
if #available(iOS 9.0, *) {
- return experimentalConvertCompressedLottieToCombinedMp4(data: data, size: CGSize(width: CGFloat(representation.width), height: CGFloat(representation.height)), cacheKey: "\(resource.id.uniqueId)-\(representation.uniqueId)").start(next: { path in
+ return experimentalConvertCompressedLottieToCombinedMp4(data: data, size: CGSize(width: CGFloat(representation.width), height: CGFloat(representation.height)), fitzModifier: representation.fitzModifier, cacheKey: "\(resource.id.uniqueId)-\(representation.uniqueId)").start(next: { path in
subscriber.putNext(.temporaryPath(path))
subscriber.putCompletion()
})
diff --git a/submodules/TelegramUI/TelegramUI/FetchMediaUtils.swift b/submodules/TelegramUI/TelegramUI/FetchMediaUtils.swift
index 3cb2be962a..cef5730ee3 100644
--- a/submodules/TelegramUI/TelegramUI/FetchMediaUtils.swift
+++ b/submodules/TelegramUI/TelegramUI/FetchMediaUtils.swift
@@ -10,6 +10,12 @@ public func freeMediaFileInteractiveFetched(account: Account, fileReference: Fil
return fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: fileReference.resourceReference(fileReference.media.resource))
}
+func freeMediaFileInteractiveFetched(fetchManager: FetchManager, fileReference: FileMediaReference, priority: FetchManagerPriority) -> Signal {
+ let file = fileReference.media
+ let mediaReference = AnyMediaReference.standalone(media: fileReference.media)
+ return fetchManager.interactivelyFetched(category: fetchCategoryForFile(file), location: .chat(PeerId(namespace: 0, id: 0)), locationKey: .free, mediaReference: mediaReference, resourceReference: mediaReference.resourceReference(file.resource), ranges: IndexSet(integersIn: 0 ..< Int(Int32.max) as Range), statsCategory: statsCategoryForFileWithAttributes(file.attributes), elevatedPriority: false, userInitiated: false, priority: priority, storeToDownloadsPeerType: nil)
+}
+
func freeMediaFileResourceInteractiveFetched(account: Account, fileReference: FileMediaReference, resource: MediaResource) -> Signal {
return fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: fileReference.resourceReference(resource))
}
diff --git a/submodules/TelegramUI/TelegramUI/PrefetchManager.swift b/submodules/TelegramUI/TelegramUI/PrefetchManager.swift
index a54017ce66..6f7a8bb41c 100644
--- a/submodules/TelegramUI/TelegramUI/PrefetchManager.swift
+++ b/submodules/TelegramUI/TelegramUI/PrefetchManager.swift
@@ -12,6 +12,11 @@ private final class PrefetchMediaContext {
}
}
+public enum PrefetchMediaItem {
+ case chatHistory(ChatHistoryPreloadMediaItem)
+ case animatedEmojiSticker(TelegramMediaFile)
+}
+
private final class PrefetchManagerImpl {
private let queue: Queue
private let account: Account
@@ -37,7 +42,37 @@ private final class PrefetchManagerImpl {
}
|> distinctUntilChanged
- self.listDisposable = (combineLatest(account.viewTracker.orderedPreloadMedia, sharedContext.automaticMediaDownloadSettings, networkType)
+ let orderedPreloadMedia = account.viewTracker.orderedPreloadMedia
+ |> mapToSignal { orderedPreloadMedia in
+ return loadedStickerPack(postbox: account.postbox, network: account.network, reference: .animatedEmoji, forceActualized: false)
+ |> map { result -> [PrefetchMediaItem] in
+ let chatHistoryMediaItems = orderedPreloadMedia.map { PrefetchMediaItem.chatHistory($0) }
+ switch result {
+ case let .result(_, items, _):
+ var animatedEmojiStickers: [String: StickerPackItem] = [:]
+ for case let item as StickerPackItem in items {
+ if let emoji = item.getStringRepresentationsOfIndexKeys().first {
+ animatedEmojiStickers[emoji.basicEmoji.0] = item
+ }
+ }
+ var stickerItems: [PrefetchMediaItem] = []
+ let popularEmoji = ["\u{2764}", "👍", "😳", "😒", "🥳"]
+ for emoji in popularEmoji {
+ if let sticker = animatedEmojiStickers[emoji] {
+ if let _ = account.postbox.mediaBox.completedResourcePath(sticker.file.resource) {
+ } else {
+ stickerItems.append(.animatedEmojiSticker(sticker.file))
+ }
+ }
+ }
+ return stickerItems + chatHistoryMediaItems
+ default:
+ return chatHistoryMediaItems
+ }
+ }
+ }
+
+ self.listDisposable = (combineLatest(orderedPreloadMedia, sharedContext.automaticMediaDownloadSettings, networkType)
|> deliverOn(self.queue)).start(next: { [weak self] orderedPreloadMedia, automaticDownloadSettings, networkType in
self?.updateOrderedPreloadMedia(orderedPreloadMedia, automaticDownloadSettings: automaticDownloadSettings, networkType: networkType)
})
@@ -48,79 +83,119 @@ private final class PrefetchManagerImpl {
self.listDisposable?.dispose()
}
- private func updateOrderedPreloadMedia(_ orderedPreloadMedia: [ChatHistoryPreloadMediaItem], automaticDownloadSettings: MediaAutoDownloadSettings, networkType: MediaAutoDownloadNetworkType) {
+ private func updateOrderedPreloadMedia(_ items: [PrefetchMediaItem], automaticDownloadSettings: MediaAutoDownloadSettings, networkType: MediaAutoDownloadNetworkType) {
var validIds = Set()
- for mediaItem in orderedPreloadMedia {
- guard let id = mediaItem.media.media.id else {
- continue
- }
- if validIds.contains(id) {
- continue
- }
-
- var automaticDownload: InteractiveMediaNodeAutodownloadMode = .none
- let peerType: MediaAutoDownloadPeerType
- if mediaItem.media.authorIsContact {
- peerType = .contact
- } else if let channel = mediaItem.media.peer as? TelegramChannel {
- if case .group = channel.info {
- peerType = .group
- } else {
- peerType = .channel
- }
- } else if mediaItem.media.peer is TelegramGroup {
- peerType = .group
- } else {
- peerType = .otherPrivate
- }
- var mediaResource: MediaResource?
-
- if let telegramImage = mediaItem.media.media as? TelegramMediaImage {
- mediaResource = largestRepresentationForPhoto(telegramImage)?.resource
- if shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: peerType, networkType: networkType, authorPeerId: nil, contactsPeerIds: [], media: telegramImage) {
- automaticDownload = .full
- }
- } else if let telegramFile = mediaItem.media.media as? TelegramMediaFile {
- mediaResource = telegramFile.resource
- if shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: peerType, networkType: networkType, authorPeerId: nil, contactsPeerIds: [], media: telegramFile) {
- automaticDownload = .full
- } else if shouldPredownloadMedia(settings: automaticDownloadSettings, peerType: peerType, networkType: networkType, media: telegramFile) {
- automaticDownload = .prefetch
- }
- }
-
- if case .none = automaticDownload {
- continue
- }
- guard let resource = mediaResource else {
- continue
- }
-
- validIds.insert(id)
- let context: PrefetchMediaContext
- if let current = self.contexts[id] {
- context = current
- } else {
- context = PrefetchMediaContext()
- self.contexts[id] = context
-
- let media = mediaItem.media.media
-
- let priority: FetchManagerPriority = .backgroundPrefetch(locationOrder: mediaItem.preloadIndex, localOrder: mediaItem.media.index)
-
- if case .full = automaticDownload {
- if let image = media as? TelegramMediaImage {
- context.fetchDisposable.set(messageMediaImageInteractiveFetched(fetchManager: self.fetchManager, messageId: mediaItem.media.index.id, messageReference: MessageReference(peer: mediaItem.media.peer, id: mediaItem.media.index.id, timestamp: mediaItem.media.index.timestamp, incoming: true, secret: false), image: image, resource: resource, userInitiated: false, priority: priority, storeToDownloadsPeerType: nil).start())
- } else if let _ = media as? TelegramMediaWebFile {
- //strongSelf.fetchDisposable.set(chatMessageWebFileInteractiveFetched(account: context.account, image: image).start())
- } else if let file = media as? TelegramMediaFile {
- let fetchSignal = messageMediaFileInteractiveFetched(fetchManager: self.fetchManager, messageId: mediaItem.media.index.id, messageReference: MessageReference(peer: mediaItem.media.peer, id: mediaItem.media.index.id, timestamp: mediaItem.media.index.timestamp, incoming: true, secret: false), file: file, userInitiated: false, priority: priority)
- context.fetchDisposable.set(fetchSignal.start())
+ var order: Int32 = 0
+ for mediaItem in items {
+ switch mediaItem {
+ case let .chatHistory(mediaItem):
+ guard let id = mediaItem.media.media.id else {
+ continue
}
- } else if case .prefetch = automaticDownload, mediaItem.media.peer.id.namespace != Namespaces.Peer.SecretChat {
- if let file = media as? TelegramMediaFile, let _ = file.size {
- context.fetchDisposable.set(preloadVideoResource(postbox: self.account.postbox, resourceReference: FileMediaReference.message(message: MessageReference(peer: mediaItem.media.peer, id: mediaItem.media.index.id, timestamp: mediaItem.media.index.timestamp, incoming: true, secret: false), media: file).resourceReference(file.resource), duration: 4.0).start())
+ if validIds.contains(id) {
+ continue
}
+
+ var automaticDownload: InteractiveMediaNodeAutodownloadMode = .none
+ let peerType: MediaAutoDownloadPeerType
+ if mediaItem.media.authorIsContact {
+ peerType = .contact
+ } else if let channel = mediaItem.media.peer as? TelegramChannel {
+ if case .group = channel.info {
+ peerType = .group
+ } else {
+ peerType = .channel
+ }
+ } else if mediaItem.media.peer is TelegramGroup {
+ peerType = .group
+ } else {
+ peerType = .otherPrivate
+ }
+ var mediaResource: MediaResource?
+
+ if let telegramImage = mediaItem.media.media as? TelegramMediaImage {
+ mediaResource = largestRepresentationForPhoto(telegramImage)?.resource
+ if shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: peerType, networkType: networkType, authorPeerId: nil, contactsPeerIds: [], media: telegramImage) {
+ automaticDownload = .full
+ }
+ } else if let telegramFile = mediaItem.media.media as? TelegramMediaFile {
+ mediaResource = telegramFile.resource
+ if shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: peerType, networkType: networkType, authorPeerId: nil, contactsPeerIds: [], media: telegramFile) {
+ automaticDownload = .full
+ } else if shouldPredownloadMedia(settings: automaticDownloadSettings, peerType: peerType, networkType: networkType, media: telegramFile) {
+ automaticDownload = .prefetch
+ }
+ }
+
+ if case .none = automaticDownload {
+ continue
+ }
+ guard let resource = mediaResource else {
+ continue
+ }
+
+ validIds.insert(id)
+ let context: PrefetchMediaContext
+ if let current = self.contexts[id] {
+ context = current
+ } else {
+ context = PrefetchMediaContext()
+ self.contexts[id] = context
+
+ let media = mediaItem.media.media
+
+ let priority: FetchManagerPriority = .backgroundPrefetch(locationOrder: mediaItem.preloadIndex, localOrder: mediaItem.media.index)
+
+ if case .full = automaticDownload {
+ if let image = media as? TelegramMediaImage {
+ context.fetchDisposable.set(messageMediaImageInteractiveFetched(fetchManager: self.fetchManager, messageId: mediaItem.media.index.id, messageReference: MessageReference(peer: mediaItem.media.peer, id: mediaItem.media.index.id, timestamp: mediaItem.media.index.timestamp, incoming: true, secret: false), image: image, resource: resource, userInitiated: false, priority: priority, storeToDownloadsPeerType: nil).start())
+ } else if let _ = media as? TelegramMediaWebFile {
+ //strongSelf.fetchDisposable.set(chatMessageWebFileInteractiveFetched(account: context.account, image: image).start())
+ } else if let file = media as? TelegramMediaFile {
+ let fetchSignal = messageMediaFileInteractiveFetched(fetchManager: self.fetchManager, messageId: mediaItem.media.index.id, messageReference: MessageReference(peer: mediaItem.media.peer, id: mediaItem.media.index.id, timestamp: mediaItem.media.index.timestamp, incoming: true, secret: false), file: file, userInitiated: false, priority: priority)
+ context.fetchDisposable.set(fetchSignal.start())
+ }
+ } else if case .prefetch = automaticDownload, mediaItem.media.peer.id.namespace != Namespaces.Peer.SecretChat {
+ if let file = media as? TelegramMediaFile, let _ = file.size {
+ context.fetchDisposable.set(preloadVideoResource(postbox: self.account.postbox, resourceReference: FileMediaReference.message(message: MessageReference(peer: mediaItem.media.peer, id: mediaItem.media.index.id, timestamp: mediaItem.media.index.timestamp, incoming: true, secret: false), media: file).resourceReference(file.resource), duration: 4.0).start())
+ }
+ }
+ }
+ case let .animatedEmojiSticker(media):
+ guard let id = media.id else {
+ continue
+ }
+ if validIds.contains(id) {
+ continue
+ }
+
+ var automaticDownload: InteractiveMediaNodeAutodownloadMode = .none
+ let peerType = MediaAutoDownloadPeerType.contact
+
+ if shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: peerType, networkType: networkType, authorPeerId: nil, contactsPeerIds: [], media: media) {
+ automaticDownload = .full
+ }
+
+ if case .none = automaticDownload {
+ continue
+ }
+
+ validIds.insert(id)
+ let context: PrefetchMediaContext
+ if let current = self.contexts[id] {
+ context = current
+ } else {
+ context = PrefetchMediaContext()
+ self.contexts[id] = context
+
+ let priority: FetchManagerPriority = .backgroundPrefetch(locationOrder: HistoryPreloadIndex(index: nil, hasUnread: false, isMuted: false, isPriority: true), localOrder: MessageIndex(id: MessageId(peerId: PeerId(namespace: 0, id: 0), namespace: 0, id: order), timestamp: 0))
+
+ if case .full = automaticDownload {
+ let fetchSignal = freeMediaFileInteractiveFetched(fetchManager: self.fetchManager, fileReference: .standalone(media: media), priority: priority)
+ context.fetchDisposable.set(fetchSignal.start())
+ }
+
+ order += 1
}
}
}
diff --git a/submodules/TelegramUI/TelegramUI/StickerPaneSearchContentNode.swift b/submodules/TelegramUI/TelegramUI/StickerPaneSearchContentNode.swift
index f1ae5895c9..a93d6abf0a 100644
--- a/submodules/TelegramUI/TelegramUI/StickerPaneSearchContentNode.swift
+++ b/submodules/TelegramUI/TelegramUI/StickerPaneSearchContentNode.swift
@@ -271,8 +271,7 @@ final class StickerPaneSearchContentNode: ASDisplayNode, PaneSearchContentNode {
let query = text.trimmingCharacters(in: .whitespacesAndNewlines)
if query.isSingleEmoji {
- signals = .single([searchStickers(account: account, query: text.trimmedEmoji)
- //|> take(1)
+ signals = .single([searchStickers(account: account, query: text.basicEmoji.0)
|> map { (nil, $0) }])
} else if query.count > 1, let languageCode = languageCode, !languageCode.isEmpty && languageCode != "emoji" {
var signal = searchEmojiKeywords(postbox: account.postbox, inputLanguageCode: languageCode, query: query.lowercased(), completeMatch: query.count < 3)
@@ -294,7 +293,7 @@ final class StickerPaneSearchContentNode: ASDisplayNode, PaneSearchContentNode {
var signals: [Signal<(String?, [FoundStickerItem]), NoError>] = []
let emoticons = keywords.flatMap { $0.emoticons }
for emoji in emoticons {
- signals.append(searchStickers(account: self.context.account, query: emoji.trimmedEmoji)
+ signals.append(searchStickers(account: self.context.account, query: emoji.basicEmoji.0)
|> take(1)
|> map { (emoji, $0) })
}