Support colored emoji

This commit is contained in:
Ali 2022-12-10 00:22:38 +04:00
parent 71bed257bb
commit 2cb2717ce7
28 changed files with 411 additions and 119 deletions

View File

@ -29,7 +29,17 @@ public func reactionStaticImage(context: AccountContext, animation: TelegramMedi
} else {
type = .still
}
let fetchFrame = animationCacheFetchFile(context: context, resource: MediaResourceReference.standalone(resource: animation.resource), type: type, keyframeOnly: true)
var customColor: UIColor?
for attribute in animation.attributes {
if case let .CustomEmoji(_, isSingleColor, _, _) = attribute {
if isSingleColor {
customColor = nil
}
}
}
let fetchFrame = animationCacheFetchFile(context: context, resource: MediaResourceReference.standalone(resource: animation.resource), type: type, keyframeOnly: true, customColor: customColor)
class AnimationCacheItemWriterImpl: AnimationCacheItemWriter {
let queue: Queue

View File

@ -212,11 +212,13 @@ public final class TextNodeLayout: NSObject {
public let range: NSRange
public let rect: CGRect
public let value: AnyHashable
public let textColor: UIColor
public init(range: NSRange, rect: CGRect, value: AnyHashable) {
public init(range: NSRange, rect: CGRect, value: AnyHashable, textColor: UIColor) {
self.range = range
self.rect = rect
self.value = value
self.textColor = textColor
}
public static func ==(lhs: EmbeddedItem, rhs: EmbeddedItem) -> Bool {
@ -229,6 +231,9 @@ public final class TextNodeLayout: NSObject {
if lhs.value != rhs.value {
return false
}
if lhs.textColor != rhs.textColor {
return false
}
return true
}
}
@ -301,7 +306,18 @@ public final class TextNodeLayout: NSObject {
spoilers.append(contentsOf: line.spoilers.map { ( $0.range, $0.frame.offsetBy(dx: lineFrame.minX, dy: lineFrame.minY)) })
spoilerWords.append(contentsOf: line.spoilerWords.map { ( $0.range, $0.frame.offsetBy(dx: lineFrame.minX, dy: lineFrame.minY)) })
for embeddedItem in line.embeddedItems {
embeddedItems.append(TextNodeLayout.EmbeddedItem(range: embeddedItem.range, rect: embeddedItem.frame.offsetBy(dx: lineFrame.minX, dy: lineFrame.minY), value: embeddedItem.item))
var textColor: UIColor?
if let attributedString = attributedString, embeddedItem.range.location < attributedString.length {
if let color = attributedString.attribute(.foregroundColor, at: embeddedItem.range.location, effectiveRange: nil) as? UIColor {
textColor = color
}
if textColor == nil {
if let color = attributedString.attribute(.foregroundColor, at: 0, effectiveRange: nil) as? UIColor {
textColor = color
}
}
}
embeddedItems.append(TextNodeLayout.EmbeddedItem(range: embeddedItem.range, rect: embeddedItem.frame.offsetBy(dx: lineFrame.minX, dy: lineFrame.minY), value: embeddedItem.item, textColor: textColor ?? .black))
}
}
self.hasRTL = hasRTL

View File

@ -100,7 +100,7 @@ public func chatMessageGalleryControllerData(context: AccountContext, chatLocati
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 case let .CustomEmoji(_, _, _, reference) = attribute {
if let reference = reference {
return .stickerPack(reference)
}

View File

@ -2285,7 +2285,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
var packReference: StickerPackReference?
if let file = file {
for attribute in file.attributes {
if case let .CustomEmoji(_, _, reference) = attribute {
if case let .CustomEmoji(_, _, _, reference) = attribute {
packReference = reference
}
}
@ -2357,7 +2357,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
tapAction: { [weak state, weak environment] _, _ in
if let emojiFile = state?.emojiFile, let controller = environment?.controller() as? PremiumIntroScreen, let navigationController = controller.navigationController as? NavigationController {
for attribute in emojiFile.attributes {
if case let .CustomEmoji(_, _, packReference) = attribute, let packReference = packReference {
if case let .CustomEmoji(_, _, _, packReference) = attribute, let packReference = packReference {
let controller = accountContext.sharedContext.makeStickerPackScreen(context: accountContext, updatedPresentationData: nil, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: loadedEmojiPack.flatMap { [$0] } ?? [], parentNavigationController: navigationController, sendSticker: { _, _, _ in
return false
})

View File

@ -425,7 +425,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
for item in featuredEmojiPack.topItems {
for attribute in item.file.attributes {
switch attribute {
case let .CustomEmoji(_, alt, _):
case let .CustomEmoji(_, _, alt, _):
if filterList.contains(alt) {
filteredFiles.append(item.file)
}
@ -1395,7 +1395,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
}
for attribute in item.file.attributes {
switch attribute {
case let .CustomEmoji(_, alt, _):
case let .CustomEmoji(_, _, alt, _):
if !item.file.isPremiumEmoji || hasPremium {
if !alt.isEmpty, let keyword = allEmoticons[alt] {
result.append((alt, item.file, keyword))
@ -1424,7 +1424,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
content: .animation(animationData),
itemFile: itemFile, subgroupId: nil,
icon: .none,
accentTint: false
tintMode: animationData.isTemplate ? .primary : .none
)
items.append(item)
}
@ -1742,7 +1742,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
if additionalAnimation == nil && itemNode.item.isCustom {
outer: for attribute in itemNode.item.stillAnimation.attributes {
if case let .CustomEmoji(_, alt, _) = attribute {
if case let .CustomEmoji(_, _, alt, _) = attribute {
if let availableReactions = self.availableReactions {
for availableReaction in availableReactions.reactions {
if availableReaction.value == .builtin(alt) {

View File

@ -203,7 +203,7 @@ final class StickerPackEmojisItemNode: GridItemNode {
var emojiAttribute: ChatTextInputTextCustomEmojiAttribute?
loop: for attribute in file.attributes {
switch attribute {
case let .CustomEmoji(_, displayText, _):
case let .CustomEmoji(_, _, displayText, _):
text = displayText
emojiAttribute = ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: file.fileId.id, file: file)
break loop
@ -229,7 +229,7 @@ final class StickerPackEmojisItemNode: GridItemNode {
var emojiAttribute: ChatTextInputTextCustomEmojiAttribute?
loop: for attribute in file.attributes {
switch attribute {
case let .CustomEmoji(_, displayText, _):
case let .CustomEmoji(_, _, displayText, _):
text = displayText
emojiAttribute = ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: file.fileId.id, file: file)
break loop
@ -354,7 +354,7 @@ final class StickerPackEmojisItemNode: GridItemNode {
} else {
updateItemLayerPlaceholder = true
itemTransition = .immediate
let animationData = EntityKeyboardAnimationData(file: item.file)
itemLayer = EmojiPagerContentComponent.View.ItemLayer(
item: EmojiPagerContentComponent.Item(
@ -363,7 +363,7 @@ final class StickerPackEmojisItemNode: GridItemNode {
itemFile: item.file,
subgroupId: nil,
icon: .none,
accentTint: false
tintMode: animationData.isTemplate ? .primary : .none
),
context: context,
attemptSynchronousLoad: attemptSynchronousLoads,
@ -425,6 +425,15 @@ final class StickerPackEmojisItemNode: GridItemNode {
self.visibleItemLayers[itemId] = itemLayer
}
switch itemLayer.item.tintMode {
case .none:
break
case .accent:
itemLayer.layerTintColor = theme.list.itemAccentColor.cgColor
case .primary:
itemLayer.layerTintColor = theme.list.itemPrimaryTextColor.cgColor
}
var itemFrame = itemLayout.frame(itemIndex: index)
itemFrame.origin.x += floor((itemFrame.width - itemVisibleFitSize.width) / 2.0)

View File

@ -66,7 +66,7 @@ private struct StickerPackPreviewGridTransaction {
let scrollToItem: GridNodeScrollToItem?
init(previousList: [StickerPackPreviewGridEntry], list: [StickerPackPreviewGridEntry], context: AccountContext, interaction: StickerPackPreviewInteraction, theme: PresentationTheme, strings: PresentationStrings, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, scrollToItem: GridNodeScrollToItem?) {
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: previousList, rightList: list)
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: previousList, rightList: list)
self.deletions = deleteIndices
self.insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(context: context, interaction: interaction, theme: theme, strings: strings, animationCache: animationCache, animationRenderer: animationRenderer), previousIndex: $0.2) }

View File

@ -112,7 +112,8 @@ func telegramMediaFileAttributesFromApiAttributes(_ attributes: [Api.DocumentAtt
result.append(.Audio(isVoice: isVoice, duration: Int(duration), title: title, performer: performer, waveform: waveformBuffer))
case let .documentAttributeCustomEmoji(flags, alt, stickerSet):
let isFree = (flags & (1 << 0)) != 0
result.append(.CustomEmoji(isPremium: !isFree, alt: alt, packReference: StickerPackReference(apiInputSet: stickerSet)))
let isSingleColor = (flags & (1 << 1)) != 0
result.append(.CustomEmoji(isPremium: !isFree, isSingleColor: isSingleColor, alt: alt, packReference: StickerPackReference(apiInputSet: stickerSet)))
}
}
return result

View File

@ -190,7 +190,7 @@ public struct TelegramMediaVideoFlags: OptionSet {
public static let supportsStreaming = TelegramMediaVideoFlags(rawValue: 1 << 1)
}
public struct StickerMaskCoords: PostboxCoding {
public struct StickerMaskCoords: PostboxCoding, Equatable {
public let n: Int32
public let x: Double
public let y: Double
@ -218,7 +218,7 @@ public struct StickerMaskCoords: PostboxCoding {
}
}
public enum TelegramMediaFileAttribute: PostboxCoding {
public enum TelegramMediaFileAttribute: PostboxCoding, Equatable {
case FileName(fileName: String)
case Sticker(displayText: String, packReference: StickerPackReference?, maskData: StickerMaskCoords?)
case ImageSize(size: PixelDimensions)
@ -229,7 +229,7 @@ public enum TelegramMediaFileAttribute: PostboxCoding {
case hintFileIsLarge
case hintIsValidated
case NoPremium
case CustomEmoji(isPremium: Bool, alt: String, packReference: StickerPackReference?)
case CustomEmoji(isPremium: Bool, isSingleColor: Bool, alt: String, packReference: StickerPackReference?)
public init(decoder: PostboxDecoder) {
let type: Int32 = decoder.decodeInt32ForKey("t", orElse: 0)
@ -260,7 +260,7 @@ public enum TelegramMediaFileAttribute: PostboxCoding {
case typeNoPremium:
self = .NoPremium
case typeCustomEmoji:
self = .CustomEmoji(isPremium: decoder.decodeBoolForKey("ip", orElse: true), alt: decoder.decodeStringForKey("dt", orElse: ""), packReference: decoder.decodeObjectForKey("pr", decoder: { StickerPackReference(decoder: $0) }) as? StickerPackReference)
self = .CustomEmoji(isPremium: decoder.decodeBoolForKey("ip", orElse: true), isSingleColor: decoder.decodeBoolForKey("sc", orElse: false), alt: decoder.decodeStringForKey("dt", orElse: ""), packReference: decoder.decodeObjectForKey("pr", decoder: { StickerPackReference(decoder: $0) }) as? StickerPackReference)
default:
preconditionFailure()
}
@ -317,9 +317,10 @@ public enum TelegramMediaFileAttribute: PostboxCoding {
encoder.encodeInt32(typeHintIsValidated, forKey: "t")
case .NoPremium:
encoder.encodeInt32(typeNoPremium, forKey: "t")
case let .CustomEmoji(isPremium, alt, packReference):
case let .CustomEmoji(isPremium, isSingleColor, alt, packReference):
encoder.encodeInt32(typeCustomEmoji, forKey: "t")
encoder.encodeBool(isPremium, forKey: "ip")
encoder.encodeBool(isSingleColor, forKey: "sc")
encoder.encodeString(alt, forKey: "dt")
if let packReference = packReference {
encoder.encodeObject(packReference, forKey: "pr")
@ -652,7 +653,7 @@ public final class TelegramMediaFile: Media, Equatable, Codable {
public var isPremiumEmoji: Bool {
for attribute in self.attributes {
if case let .CustomEmoji(isPremium, _, _) = attribute {
if case let .CustomEmoji(isPremium, _, _, _) = attribute {
return isPremium
}
}
@ -737,6 +738,10 @@ public final class TelegramMediaFile: Media, Equatable, Codable {
return false
}
if self.attributes != other.attributes {
return false
}
return true
}

View File

@ -442,7 +442,10 @@ public final class EmojiStatusComponent: Component {
var accentTint = false
if let _ = emojiThemeColor {
for attribute in emojiFile.attributes {
if case let .CustomEmoji(_, _, packReference) = attribute {
if case let .CustomEmoji(_, isSingleColor, _, packReference) = attribute {
if isSingleColor {
accentTint = true
}
switch packReference {
case let .id(id, _):
if id == 773947703670341676 || id == 2964141614563343 {
@ -456,8 +459,10 @@ public final class EmojiStatusComponent: Component {
}
if accentTint {
animationLayer.contentTintColor = emojiThemeColor
animationLayer.dynamicColor = emojiThemeColor
} else {
animationLayer.contentTintColor = nil
animationLayer.dynamicColor = nil
}
animationLayer.frame = CGRect(origin: CGPoint(), size: size)

View File

@ -329,7 +329,7 @@ public final class EmojiStatusSelectionController: ViewController {
for item in featuredEmojiPack.topItems {
for attribute in item.file.attributes {
switch attribute {
case let .CustomEmoji(_, alt, _):
case let .CustomEmoji(_, _, alt, _):
if filterList.contains(alt) {
filteredFiles.append(item.file)
}
@ -487,7 +487,7 @@ public final class EmojiStatusSelectionController: ViewController {
}
for attribute in item.file.attributes {
switch attribute {
case let .CustomEmoji(_, alt, _):
case let .CustomEmoji(_, _, alt, _):
if !item.file.isPremiumEmoji || hasPremium {
if !alt.isEmpty, let keyword = allEmoticons[alt] {
result.append((alt, item.file, keyword))
@ -516,7 +516,7 @@ public final class EmojiStatusSelectionController: ViewController {
content: .animation(animationData),
itemFile: itemFile, subgroupId: nil,
icon: .none,
accentTint: false
tintMode: animationData.isTemplate ? .primary : .none
)
items.append(item)
}
@ -645,7 +645,10 @@ public final class EmojiStatusSelectionController: ViewController {
} else if let itemFile = item.itemFile {
var useCleanEffect = false
for attribute in itemFile.attributes {
if case let .CustomEmoji(_, _, packReference) = attribute {
if case let .CustomEmoji(_, isSingleColor, _, packReference) = attribute {
if isSingleColor {
useCleanEffect = true
}
switch packReference {
case let .id(id, _):
if id == 773947703670341676 || id == 2964141614563343 {
@ -692,8 +695,13 @@ public final class EmojiStatusSelectionController: ViewController {
placeholderColor: UIColor(white: 0.0, alpha: 0.0),
pointSize: CGSize(width: 32.0, height: 32.0)
)
if item.accentTint {
switch item.tintMode {
case .accent:
baseItemLayer.contentTintColor = self.presentationData.theme.list.itemAccentColor
case .primary:
baseItemLayer.contentTintColor = self.presentationData.theme.list.itemPrimaryTextColor
case .none:
break
}
if let sublayers = animationLayer.sublayers {
@ -1001,7 +1009,7 @@ public final class EmojiStatusSelectionController: ViewController {
if let itemFile = previewItem.item.itemFile {
attributeLoop: for attribute in itemFile.attributes {
switch attribute {
case let .CustomEmoji(_, alt, _):
case let .CustomEmoji(_, _, alt, _):
emojiString = alt
break attributeLoop
default:
@ -1165,7 +1173,7 @@ public final class EmojiStatusSelectionController: ViewController {
if let itemFile = item.itemFile {
attributeLoop: for attribute in itemFile.attributes {
switch attribute {
case let .CustomEmoji(_, alt, _):
case let .CustomEmoji(_, _, alt, _):
emojiString = alt
break attributeLoop
default:

View File

@ -59,7 +59,7 @@ public final class EmojiSuggestionsComponent: Component {
}
for attribute in item.file.attributes {
switch attribute {
case let .CustomEmoji(_, alt, _):
case let .CustomEmoji(_, _, alt, _):
if alt == query || (!normalizedQuery.isEmpty && alt == normalizedQuery) {
if !item.file.isPremiumEmoji || hasPremium {
if !existingIds.contains(item.file.fileId) {
@ -78,7 +78,7 @@ public final class EmojiSuggestionsComponent: Component {
for item in featuredPack.topItems {
for attribute in item.file.attributes {
switch attribute {
case let .CustomEmoji(_, alt, _):
case let .CustomEmoji(_, _, alt, _):
if alt == query || (!normalizedQuery.isEmpty && alt == normalizedQuery) {
if !item.file.isPremiumEmoji || hasPremium {
if !existingIds.contains(item.file.fileId) {

View File

@ -96,7 +96,7 @@ public extension AnimationCacheAnimationType {
}
}
public func animationCacheFetchFile(context: AccountContext, resource: MediaResourceReference, type: AnimationCacheAnimationType, keyframeOnly: Bool) -> (AnimationCacheFetchOptions) -> Disposable {
public func animationCacheFetchFile(context: AccountContext, resource: MediaResourceReference, type: AnimationCacheAnimationType, keyframeOnly: Bool, customColor: UIColor?) -> (AnimationCacheFetchOptions) -> Disposable {
return { options in
let source = AnimatedStickerResourceSource(account: context.account, resource: resource.resource, fitzModifier: nil, isVideo: false)
@ -107,15 +107,15 @@ public func animationCacheFetchFile(context: AccountContext, resource: MediaReso
switch type {
case .video:
cacheVideoAnimation(path: result, width: Int(options.size.width), height: Int(options.size.height), writer: options.writer, firstFrameOnly: options.firstFrameOnly)
cacheVideoAnimation(path: result, width: Int(options.size.width), height: Int(options.size.height), writer: options.writer, firstFrameOnly: options.firstFrameOnly, customColor: customColor)
case .lottie:
guard let data = try? Data(contentsOf: URL(fileURLWithPath: result)) else {
options.writer.finish()
return
}
cacheLottieAnimation(data: data, width: Int(options.size.width), height: Int(options.size.height), keyframeOnly: keyframeOnly, writer: options.writer, firstFrameOnly: options.firstFrameOnly)
cacheLottieAnimation(data: data, width: Int(options.size.width), height: Int(options.size.height), keyframeOnly: keyframeOnly, writer: options.writer, firstFrameOnly: options.firstFrameOnly, customColor: customColor)
case .still:
cacheStillSticker(path: result, width: Int(options.size.width), height: Int(options.size.height), writer: options.writer)
cacheStillSticker(path: result, width: Int(options.size.width), height: Int(options.size.height), writer: options.writer, customColor: customColor)
}
})
@ -153,6 +153,7 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget {
private let pixelSize: CGSize
private var isDisplayingPlaceholder: Bool = false
private var didProcessTintColor: Bool = false
public private(set) var file: TelegramMediaFile?
private var infoDisposable: Disposable?
@ -168,6 +169,14 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget {
}
}
public var dynamicColor: UIColor? {
didSet {
if self.dynamicColor != oldValue {
self.updateTintColor()
}
}
}
private var currentLoopCount: Int = 0
private var isInHierarchyValue: Bool = false
@ -179,13 +188,14 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget {
}
}
public init(context: AccountContext, attemptSynchronousLoad: Bool, emoji: ChatTextInputTextCustomEmojiAttribute, file: TelegramMediaFile?, cache: AnimationCache, renderer: MultiAnimationRenderer, unique: Bool = false, placeholderColor: UIColor, pointSize: CGSize, loopCount: Int? = nil) {
public init(context: AccountContext, attemptSynchronousLoad: Bool, emoji: ChatTextInputTextCustomEmojiAttribute, file: TelegramMediaFile?, cache: AnimationCache, renderer: MultiAnimationRenderer, unique: Bool = false, placeholderColor: UIColor, pointSize: CGSize, dynamicColor: UIColor? = nil, loopCount: Int? = nil) {
self.context = context
self.emoji = emoji
self.cache = cache
self.renderer = renderer
self.unique = unique
self.placeholderColor = placeholderColor
self.dynamicColor = dynamicColor
self.loopCount = loopCount
let scale = min(2.0, UIScreenScale)
@ -239,7 +249,18 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget {
private func updateTintColor() {
if !self.isDisplayingPlaceholder {
self.layerTintColor = self.contentTintColor?.cgColor
var customColor = self.contentTintColor
if let file = self.file {
for attribute in file.attributes {
if case let .CustomEmoji(_, isSingleColor, _, _) = attribute {
if isSingleColor {
customColor = self.dynamicColor
}
}
}
}
self.layerTintColor = customColor?.cgColor
} else {
self.layerTintColor = nil
}
@ -311,10 +332,17 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget {
self.loadAnimation()
} else {
var isTemplate = false
for attribute in file.attributes {
if case let .CustomEmoji(_, isSingleColor, _, _) = attribute {
isTemplate = isSingleColor
}
}
let pointSize = self.pointSize
let placeholderColor = self.placeholderColor
let isThumbnailCancelled = Atomic<Bool>(value: false)
self.loadDisposable = self.renderer.loadFirstFrame(target: self, cache: self.cache, itemId: file.resource.id.stringRepresentation, size: self.pixelSize, fetch: animationCacheFetchFile(context: self.context, resource: .media(media: .standalone(media: file), resource: file.resource), type: AnimationCacheAnimationType(file: file), keyframeOnly: true), completion: { [weak self] result, isFinal in
self.loadDisposable = self.renderer.loadFirstFrame(target: self, cache: self.cache, itemId: file.resource.id.stringRepresentation, size: self.pixelSize, fetch: animationCacheFetchFile(context: self.context, resource: .media(media: .standalone(media: file), resource: file.resource), type: AnimationCacheAnimationType(file: file), keyframeOnly: true, customColor: isTemplate ? .white : nil), completion: { [weak self] result, isFinal in
if !result {
MultiAnimationRendererImpl.firstFrameQueue.async {
let image = generateStickerPlaceholderImage(data: file.immediateThumbnailData, size: pointSize, scale: min(2.0, UIScreenScale), imageSize: file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0), backgroundColor: nil, foregroundColor: placeholderColor)
@ -350,11 +378,18 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget {
return
}
var isTemplate = false
for attribute in file.attributes {
if case let .CustomEmoji(_, isSingleColor, _, _) = attribute {
isTemplate = isSingleColor
}
}
let context = self.context
if file.isAnimatedSticker || file.isVideoEmoji {
let keyframeOnly = self.pixelSize.width >= 120.0
self.disposable = renderer.add(target: self, cache: self.cache, itemId: file.resource.id.stringRepresentation, unique: self.unique, size: self.pixelSize, fetch: animationCacheFetchFile(context: context, resource: .media(media: .standalone(media: file), resource: file.resource), type: AnimationCacheAnimationType(file: file), keyframeOnly: keyframeOnly))
self.disposable = renderer.add(target: self, cache: self.cache, itemId: file.resource.id.stringRepresentation, unique: self.unique, size: self.pixelSize, fetch: animationCacheFetchFile(context: context, resource: .media(media: .standalone(media: file), resource: file.resource), type: AnimationCacheAnimationType(file: file), keyframeOnly: keyframeOnly, customColor: isTemplate ? .white : nil))
} else {
self.disposable = renderer.add(target: self, cache: self.cache, itemId: file.resource.id.stringRepresentation, unique: self.unique, size: self.pixelSize, fetch: { options in
let dataDisposable = context.account.postbox.mediaBox.resourceData(file.resource).start(next: { result in
@ -362,7 +397,7 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget {
return
}
cacheStillSticker(path: result.path, width: Int(options.size.width), height: Int(options.size.height), writer: options.writer)
cacheStillSticker(path: result.path, width: Int(options.size.width), height: Int(options.size.height), writer: options.writer, customColor: isTemplate ? .white : nil)
})
let fetchDisposable = freeMediaFileResourceInteractiveFetched(account: context.account, fileReference: .customEmoji(media: file), resource: file.resource).start()
@ -405,6 +440,10 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget {
self.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
} else {
if !self.didProcessTintColor {
//self.didProcessTintColor = true
self.updateTintColor()
}
self.contents = contents
}
@ -433,6 +472,10 @@ public final class EmojiTextAttachmentView: UIView {
fatalError("init(coder:) has not been implemented")
}
public func updateTextColor(_ textColor: UIColor) {
self.contentLayer.dynamicColor = textColor
}
override public func layoutSubviews() {
super.layoutSubviews()

View File

@ -222,14 +222,16 @@ public final class EntityKeyboardAnimationData: Equatable {
public let dimensions: CGSize
public let immediateThumbnailData: Data?
public let isReaction: Bool
public let isTemplate: Bool
public init(id: Id, type: ItemType, resource: MediaResourceReference, dimensions: CGSize, immediateThumbnailData: Data?, isReaction: Bool) {
public init(id: Id, type: ItemType, resource: MediaResourceReference, dimensions: CGSize, immediateThumbnailData: Data?, isReaction: Bool, isTemplate: Bool) {
self.id = id
self.type = type
self.resource = resource
self.dimensions = dimensions
self.immediateThumbnailData = immediateThumbnailData
self.isReaction = isReaction
self.isTemplate = isTemplate
}
public convenience init(file: TelegramMediaFile, isReaction: Bool = false) {
@ -241,7 +243,13 @@ public final class EntityKeyboardAnimationData: Equatable {
} else {
type = .still
}
self.init(id: .file(file.fileId), type: type, resource: .standalone(resource: file.resource), dimensions: file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0), immediateThumbnailData: file.immediateThumbnailData, isReaction: isReaction)
var isTemplate = false
for attribute in file.attributes {
if case let .CustomEmoji(_, isSingleColor, _, _) = attribute {
isTemplate = isSingleColor
}
}
self.init(id: .file(file.fileId), type: type, resource: .standalone(resource: file.resource), dimensions: file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0), immediateThumbnailData: file.immediateThumbnailData, isReaction: isReaction, isTemplate: isTemplate)
}
public static func ==(lhs: EntityKeyboardAnimationData, rhs: EntityKeyboardAnimationData) -> Bool {
@ -2201,12 +2209,18 @@ public final class EmojiPagerContentComponent: Component {
case premium
}
public enum TintMode {
case none
case accent
case primary
}
public let animationData: EntityKeyboardAnimationData?
public let content: ItemContent
public let itemFile: TelegramMediaFile?
public let subgroupId: Int32?
public let icon: Icon
public let accentTint: Bool
public let tintMode: TintMode
public init(
animationData: EntityKeyboardAnimationData?,
@ -2214,14 +2228,14 @@ public final class EmojiPagerContentComponent: Component {
itemFile: TelegramMediaFile?,
subgroupId: Int32?,
icon: Icon,
accentTint: Bool
tintMode: TintMode
) {
self.animationData = animationData
self.content = content
self.itemFile = itemFile
self.subgroupId = subgroupId
self.icon = icon
self.accentTint = accentTint
self.tintMode = tintMode
}
public static func ==(lhs: Item, rhs: Item) -> Bool {
@ -2243,7 +2257,7 @@ public final class EmojiPagerContentComponent: Component {
if lhs.icon != rhs.icon {
return false
}
if lhs.accentTint != rhs.accentTint {
if lhs.tintMode != rhs.tintMode {
return false
}
@ -2894,14 +2908,14 @@ public final class EmojiPagerContentComponent: Component {
return
}
strongSelf.disposable = renderer.add(target: strongSelf, cache: cache, itemId: animationData.resource.resource.id.stringRepresentation, unique: false, size: pixelSize, fetch: animationCacheFetchFile(context: context, resource: animationData.resource, type: animationData.type.animationCacheAnimationType, keyframeOnly: pixelSize.width >= 120.0))
strongSelf.disposable = renderer.add(target: strongSelf, cache: cache, itemId: animationData.resource.resource.id.stringRepresentation, unique: false, size: pixelSize, fetch: animationCacheFetchFile(context: context, resource: animationData.resource, type: animationData.type.animationCacheAnimationType, keyframeOnly: pixelSize.width >= 120.0, customColor: animationData.isTemplate ? .white : nil))
}
if attemptSynchronousLoad {
if !renderer.loadFirstFrameSynchronously(target: self, cache: cache, itemId: animationData.resource.resource.id.stringRepresentation, size: pixelSize) {
self.updateDisplayPlaceholder(displayPlaceholder: true)
self.fetchDisposable = renderer.loadFirstFrame(target: self, cache: cache, itemId: animationData.resource.resource.id.stringRepresentation, size: pixelSize, fetch: animationCacheFetchFile(context: context, resource: animationData.resource, type: animationData.type.animationCacheAnimationType, keyframeOnly: true), completion: { [weak self] success, isFinal in
self.fetchDisposable = renderer.loadFirstFrame(target: self, cache: cache, itemId: animationData.resource.resource.id.stringRepresentation, size: pixelSize, fetch: animationCacheFetchFile(context: context, resource: animationData.resource, type: animationData.type.animationCacheAnimationType, keyframeOnly: true, customColor: animationData.isTemplate ? .white : nil), completion: { [weak self] success, isFinal in
if !isFinal {
if !success {
Queue.mainQueue().async {
@ -2931,7 +2945,7 @@ public final class EmojiPagerContentComponent: Component {
loadAnimation()
}
} else {
self.fetchDisposable = renderer.loadFirstFrame(target: self, cache: cache, itemId: animationData.resource.resource.id.stringRepresentation, size: pixelSize, fetch: animationCacheFetchFile(context: context, resource: animationData.resource, type: animationData.type.animationCacheAnimationType, keyframeOnly: true), completion: { [weak self] success, isFinal in
self.fetchDisposable = renderer.loadFirstFrame(target: self, cache: cache, itemId: animationData.resource.resource.id.stringRepresentation, size: pixelSize, fetch: animationCacheFetchFile(context: context, resource: animationData.resource, type: animationData.type.animationCacheAnimationType, keyframeOnly: true, customColor: animationData.isTemplate ? .white : nil), completion: { [weak self] success, isFinal in
if !isFinal {
if !success {
Queue.mainQueue().async {
@ -5337,9 +5351,12 @@ public final class EmojiPagerContentComponent: Component {
itemLayer.update(transition: transition, size: itemFrame.size, badge: badge, blurredBadgeColor: UIColor(white: 0.0, alpha: 0.1), blurredBadgeBackgroundColor: keyboardChildEnvironment.theme.list.plainBackgroundColor)
if item.accentTint {
switch item.tintMode {
case .accent:
itemLayer.layerTintColor = keyboardChildEnvironment.theme.list.itemAccentColor.cgColor
} else {
case .primary:
itemLayer.layerTintColor = keyboardChildEnvironment.theme.list.itemPrimaryTextColor.cgColor
case .none:
itemLayer.layerTintColor = nil
}
@ -5373,7 +5390,7 @@ public final class EmojiPagerContentComponent: Component {
self.visibleItemSelectionLayers[itemId] = itemSelectionLayer
}
if item.accentTint {
if case .accent = item.tintMode {
itemSelectionLayer.backgroundColor = keyboardChildEnvironment.theme.list.itemAccentColor.withMultipliedAlpha(0.1).cgColor
itemSelectionLayer.tintContainerLayer.backgroundColor = UIColor.clear.cgColor
} else {
@ -6392,7 +6409,7 @@ public final class EmojiPagerContentComponent: Component {
itemFile: nil,
subgroupId: nil,
icon: .none,
accentTint: false
tintMode: .none
)
let groupId = "recent"
@ -6411,13 +6428,16 @@ public final class EmojiPagerContentComponent: Component {
}
existingIds.insert(file.fileId)
var accentTint = false
var tintMode: Item.TintMode = .none
for attribute in file.attributes {
if case let .CustomEmoji(_, _, packReference) = attribute {
if case let .CustomEmoji(_, isSingleColor, _, packReference) = attribute {
if isSingleColor {
tintMode = .accent
}
switch packReference {
case let .id(id, _):
if id == 773947703670341676 || id == 2964141614563343 {
accentTint = true
tintMode = .accent
}
default:
break
@ -6434,7 +6454,7 @@ public final class EmojiPagerContentComponent: Component {
itemFile: file,
subgroupId: nil,
icon: .none,
accentTint: accentTint
tintMode: tintMode
)
if let groupIndex = itemGroupIndexById[groupId] {
@ -6448,7 +6468,7 @@ public final class EmojiPagerContentComponent: Component {
itemFile: nil,
subgroupId: nil,
icon: .none,
accentTint: false
tintMode: .none
)
let groupId = "recent"
@ -6467,13 +6487,16 @@ public final class EmojiPagerContentComponent: Component {
}
existingIds.insert(file.fileId)
var accentTint = false
var tintMode: Item.TintMode = .none
for attribute in file.attributes {
if case let .CustomEmoji(_, _, packReference) = attribute {
if case let .CustomEmoji(_, isSingleColor, _, packReference) = attribute {
if isSingleColor {
tintMode = .accent
}
switch packReference {
case let .id(id, _):
if id == 773947703670341676 || id == 2964141614563343 {
accentTint = true
tintMode = .accent
}
default:
break
@ -6490,7 +6513,7 @@ public final class EmojiPagerContentComponent: Component {
itemFile: file,
subgroupId: nil,
icon: .none,
accentTint: accentTint
tintMode: tintMode
)
if let groupIndex = itemGroupIndexById[groupId] {
@ -6510,13 +6533,16 @@ public final class EmojiPagerContentComponent: Component {
}
existingIds.insert(file.fileId)
var accentTint = false
var tintMode: Item.TintMode = .none
for attribute in file.attributes {
if case let .CustomEmoji(_, _, packReference) = attribute {
if case let .CustomEmoji(_, isSingleColor, _, packReference) = attribute {
if isSingleColor {
tintMode = .accent
}
switch packReference {
case let .id(id, _):
if id == 773947703670341676 || id == 2964141614563343 {
accentTint = true
tintMode = .accent
}
default:
break
@ -6533,7 +6559,7 @@ public final class EmojiPagerContentComponent: Component {
itemFile: file,
subgroupId: nil,
icon: .none,
accentTint: accentTint
tintMode: tintMode
)
if let groupIndex = itemGroupIndexById[groupId] {
@ -6559,13 +6585,16 @@ public final class EmojiPagerContentComponent: Component {
let resultItem: EmojiPagerContentComponent.Item
var accentTint = false
var tintMode: Item.TintMode = .none
for attribute in file.attributes {
if case let .CustomEmoji(_, _, packReference) = attribute {
if case let .CustomEmoji(_, isSingleColor, _, packReference) = attribute {
if isSingleColor {
tintMode = .accent
}
switch packReference {
case let .id(id, _):
if id == 773947703670341676 || id == 2964141614563343 {
accentTint = true
tintMode = .accent
}
default:
break
@ -6580,7 +6609,7 @@ public final class EmojiPagerContentComponent: Component {
itemFile: file,
subgroupId: nil,
icon: .none,
accentTint: accentTint
tintMode: tintMode
)
if let groupIndex = itemGroupIndexById[groupId] {
@ -6637,6 +6666,15 @@ public final class EmojiPagerContentComponent: Component {
icon = .none
}
var tintMode: Item.TintMode = .none
for attribute in reactionItem.file.attributes {
if case let .CustomEmoji(_, isSingleColor, _, _) = attribute {
if isSingleColor {
tintMode = .primary
}
}
}
let animationFile = reactionItem.file
let animationData = EntityKeyboardAnimationData(file: animationFile, isReaction: true)
let resultItem = EmojiPagerContentComponent.Item(
@ -6645,7 +6683,7 @@ public final class EmojiPagerContentComponent: Component {
itemFile: animationFile,
subgroupId: nil,
icon: icon,
accentTint: false
tintMode: tintMode
)
let groupId = "recent"
@ -6692,6 +6730,15 @@ public final class EmojiPagerContentComponent: Component {
icon = .none
}
var tintMode: Item.TintMode = .none
for attribute in reactionItem.selectAnimation.attributes {
if case let .CustomEmoji(_, isSingleColor, _, _) = attribute {
if isSingleColor {
tintMode = .primary
}
}
}
let animationFile = reactionItem.selectAnimation
let animationData = EntityKeyboardAnimationData(file: animationFile, isReaction: true)
let resultItem = EmojiPagerContentComponent.Item(
@ -6700,7 +6747,7 @@ public final class EmojiPagerContentComponent: Component {
itemFile: animationFile,
subgroupId: nil,
icon: icon,
accentTint: false
tintMode: tintMode
)
if hasPremium {
@ -6768,6 +6815,15 @@ public final class EmojiPagerContentComponent: Component {
}
}
var tintMode: Item.TintMode = .none
for attribute in animationFile.attributes {
if case let .CustomEmoji(_, isSingleColor, _, _) = attribute {
if isSingleColor {
tintMode = .primary
}
}
}
let animationData = EntityKeyboardAnimationData(file: animationFile, isReaction: true)
let resultItem = EmojiPagerContentComponent.Item(
animationData: animationData,
@ -6775,7 +6831,7 @@ public final class EmojiPagerContentComponent: Component {
itemFile: animationFile,
subgroupId: nil,
icon: icon,
accentTint: false
tintMode: tintMode
)
let groupId = "popular"
@ -6811,6 +6867,15 @@ public final class EmojiPagerContentComponent: Component {
let resultItem: EmojiPagerContentComponent.Item
switch item.content {
case let .file(file):
var tintMode: Item.TintMode = .none
for attribute in file.attributes {
if case let .CustomEmoji(_, isSingleColor, _, _) = attribute {
if isSingleColor {
tintMode = .primary
}
}
}
let animationData = EntityKeyboardAnimationData(file: file)
resultItem = EmojiPagerContentComponent.Item(
animationData: animationData,
@ -6818,7 +6883,7 @@ public final class EmojiPagerContentComponent: Component {
itemFile: file,
subgroupId: nil,
icon: .none,
accentTint: false
tintMode: tintMode
)
case let .text(text):
resultItem = EmojiPagerContentComponent.Item(
@ -6827,7 +6892,7 @@ public final class EmojiPagerContentComponent: Component {
itemFile: nil,
subgroupId: nil,
icon: .none,
accentTint: false
tintMode: .none
)
}
@ -6857,6 +6922,19 @@ public final class EmojiPagerContentComponent: Component {
icon = .locked
}
var tintMode: Item.TintMode = .none
for attribute in item.file.attributes {
if case let .CustomEmoji(_, isSingleColor, _, _) = attribute {
if isSingleColor {
if isStatusSelection {
tintMode = .accent
} else {
tintMode = .primary
}
}
}
}
let animationData = EntityKeyboardAnimationData(file: item.file)
let resultItem = EmojiPagerContentComponent.Item(
animationData: animationData,
@ -6864,7 +6942,7 @@ public final class EmojiPagerContentComponent: Component {
itemFile: item.file,
subgroupId: nil,
icon: icon,
accentTint: false
tintMode: tintMode
)
let supergroupId = entry.index.collectionId
@ -6900,7 +6978,8 @@ public final class EmojiPagerContentComponent: Component {
resource: .stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource),
dimensions: thumbnail.dimensions.cgSize,
immediateThumbnailData: info.immediateThumbnailData,
isReaction: false
isReaction: false,
isTemplate: false
)
}
@ -6918,6 +6997,19 @@ public final class EmojiPagerContentComponent: Component {
}
for item in featuredEmojiPack.topItems {
var tintMode: Item.TintMode = .none
for attribute in item.file.attributes {
if case let .CustomEmoji(_, isSingleColor, _, _) = attribute {
if isSingleColor {
if isStatusSelection {
tintMode = .accent
} else {
tintMode = .primary
}
}
}
}
let animationData = EntityKeyboardAnimationData(file: item.file)
let resultItem = EmojiPagerContentComponent.Item(
animationData: animationData,
@ -6925,7 +7017,7 @@ public final class EmojiPagerContentComponent: Component {
itemFile: item.file,
subgroupId: nil,
icon: .none,
accentTint: false
tintMode: tintMode
)
let supergroupId = featuredEmojiPack.info.id
@ -6959,7 +7051,8 @@ public final class EmojiPagerContentComponent: Component {
resource: .stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource),
dimensions: thumbnail.dimensions.cgSize,
immediateThumbnailData: info.immediateThumbnailData,
isReaction: false
isReaction: false,
isTemplate: false
)
}
@ -6980,7 +7073,7 @@ public final class EmojiPagerContentComponent: Component {
itemFile: nil,
subgroupId: subgroupId.rawValue,
icon: .none,
accentTint: false
tintMode: .none
)
if let groupIndex = itemGroupIndexById[groupId] {
@ -7190,19 +7283,29 @@ public final class EmojiPagerContentComponent: Component {
resource: .stickerPackThumbnail(stickerPack: .id(id: featuredStickerPack.info.id.id, accessHash: featuredStickerPack.info.accessHash), resource: thumbnail.resource),
dimensions: thumbnail.dimensions.cgSize,
immediateThumbnailData: featuredStickerPack.info.immediateThumbnailData,
isReaction: false
isReaction: false,
isTemplate: false
)
} else {
animationData = EntityKeyboardAnimationData(file: item.file)
}
var tintMode: Item.TintMode = .none
for attribute in item.file.attributes {
if case let .CustomEmoji(_, isSingleColor, _, _) = attribute {
if isSingleColor {
tintMode = .primary
}
}
}
let resultItem = EmojiPagerContentComponent.Item(
animationData: animationData,
content: .animation(animationData),
itemFile: item.file,
subgroupId: nil,
icon: .none,
accentTint: false
tintMode: tintMode
)
let supergroupId = "featuredTop"
@ -7233,6 +7336,15 @@ public final class EmojiPagerContentComponent: Component {
continue
}
var tintMode: Item.TintMode = .none
for attribute in item.file.attributes {
if case let .CustomEmoji(_, isSingleColor, _, _) = attribute {
if isSingleColor {
tintMode = .primary
}
}
}
let animationData = EntityKeyboardAnimationData(file: item.file)
let resultItem = EmojiPagerContentComponent.Item(
animationData: animationData,
@ -7240,7 +7352,7 @@ public final class EmojiPagerContentComponent: Component {
itemFile: item.file,
subgroupId: nil,
icon: .none,
accentTint: false
tintMode: tintMode
)
let groupId = "saved"
@ -7262,6 +7374,15 @@ public final class EmojiPagerContentComponent: Component {
continue
}
var tintMode: Item.TintMode = .none
for attribute in item.media.attributes {
if case let .CustomEmoji(_, isSingleColor, _, _) = attribute {
if isSingleColor {
tintMode = .primary
}
}
}
let animationData = EntityKeyboardAnimationData(file: item.media)
let resultItem = EmojiPagerContentComponent.Item(
animationData: animationData,
@ -7269,7 +7390,7 @@ public final class EmojiPagerContentComponent: Component {
itemFile: item.media,
subgroupId: nil,
icon: .none,
accentTint: false
tintMode: tintMode
)
let groupId = "recent"
@ -7314,6 +7435,15 @@ public final class EmojiPagerContentComponent: Component {
}
processedIds.insert(item.file.fileId)
var tintMode: Item.TintMode = .none
for attribute in item.file.attributes {
if case let .CustomEmoji(_, isSingleColor, _, _) = attribute {
if isSingleColor {
tintMode = .primary
}
}
}
let animationData = EntityKeyboardAnimationData(file: item.file)
let resultItem = EmojiPagerContentComponent.Item(
animationData: animationData,
@ -7321,7 +7451,7 @@ public final class EmojiPagerContentComponent: Component {
itemFile: item.file,
subgroupId: nil,
icon: .none,
accentTint: false
tintMode: tintMode
)
let groupId = "premium"
@ -7348,6 +7478,15 @@ public final class EmojiPagerContentComponent: Component {
}
processedIds.insert(item.file.fileId)
var tintMode: Item.TintMode = .none
for attribute in item.file.attributes {
if case let .CustomEmoji(_, isSingleColor, _, _) = attribute {
if isSingleColor {
tintMode = .primary
}
}
}
let animationData = EntityKeyboardAnimationData(file: item.file)
let resultItem = EmojiPagerContentComponent.Item(
animationData: animationData,
@ -7355,7 +7494,7 @@ public final class EmojiPagerContentComponent: Component {
itemFile: item.file,
subgroupId: nil,
icon: .none,
accentTint: false
tintMode: tintMode
)
let groupId = "peerSpecific"
@ -7372,6 +7511,16 @@ public final class EmojiPagerContentComponent: Component {
guard let item = entry.item as? StickerPackItem else {
continue
}
var tintMode: Item.TintMode = .none
for attribute in item.file.attributes {
if case let .CustomEmoji(_, isSingleColor, _, _) = attribute {
if isSingleColor {
tintMode = .primary
}
}
}
let animationData = EntityKeyboardAnimationData(file: item.file)
let resultItem = EmojiPagerContentComponent.Item(
animationData: animationData,
@ -7379,7 +7528,7 @@ public final class EmojiPagerContentComponent: Component {
itemFile: item.file,
subgroupId: nil,
icon: .none,
accentTint: false
tintMode: tintMode
)
let groupId = entry.index.collectionId
if let groupIndex = itemGroupIndexById[groupId] {
@ -7409,7 +7558,8 @@ public final class EmojiPagerContentComponent: Component {
resource: .stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource),
dimensions: thumbnail.dimensions.cgSize,
immediateThumbnailData: info.immediateThumbnailData,
isReaction: false
isReaction: false,
isTemplate: false
)
}
@ -7426,6 +7576,15 @@ public final class EmojiPagerContentComponent: Component {
}
for item in featuredStickerPack.topItems {
var tintMode: Item.TintMode = .none
for attribute in item.file.attributes {
if case let .CustomEmoji(_, isSingleColor, _, _) = attribute {
if isSingleColor {
tintMode = .primary
}
}
}
let animationData = EntityKeyboardAnimationData(file: item.file)
let resultItem = EmojiPagerContentComponent.Item(
animationData: animationData,
@ -7433,7 +7592,7 @@ public final class EmojiPagerContentComponent: Component {
itemFile: item.file,
subgroupId: nil,
icon: .none,
accentTint: false
tintMode: tintMode
)
let supergroupId = featuredStickerPack.info.id
@ -7469,7 +7628,8 @@ public final class EmojiPagerContentComponent: Component {
resource: .stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource),
dimensions: thumbnail.dimensions.cgSize,
immediateThumbnailData: info.immediateThumbnailData,
isReaction: false
isReaction: false,
isTemplate: false
)
}

View File

@ -116,7 +116,7 @@ final class EntityKeyboardAnimationTopPanelComponent: Component {
itemFile: nil,
subgroupId: nil,
icon: .none,
accentTint: false
tintMode: component.item.isTemplate ? .primary : .none
),
context: component.context,
attemptSynchronousLoad: false,
@ -158,6 +158,15 @@ final class EntityKeyboardAnimationTopPanelComponent: Component {
}
itemLayer.update(transition: transition, size: iconFrame.size, badge: badge, blurredBadgeColor: UIColor(white: 0.0, alpha: 0.1), blurredBadgeBackgroundColor: component.theme.list.plainBackgroundColor)
switch itemLayer.item.tintMode {
case .none:
break
case .primary:
itemLayer.layerTintColor = component.theme.list.itemPrimaryTextColor.cgColor
case .accent:
itemLayer.layerTintColor = component.theme.list.itemAccentColor.cgColor
}
itemLayer.isVisibleForAnimations = true
}

View File

@ -6,7 +6,7 @@ import RLottieBinding
import GZip
import WebPBinding
public func cacheLottieAnimation(data: Data, width: Int, height: Int, keyframeOnly: Bool, writer: AnimationCacheItemWriter, firstFrameOnly: Bool) {
public func cacheLottieAnimation(data: Data, width: Int, height: Int, keyframeOnly: Bool, writer: AnimationCacheItemWriter, firstFrameOnly: Bool, customColor: UIColor?) {
let work: () -> Void = {
let decompressedData = TGGUnzipData(data, 2 * 1024 * 1024) ?? data
guard let animation = LottieInstance(data: decompressedData, fitzModifier: .none, colorReplacements: nil, cacheKey: "") else {
@ -32,6 +32,19 @@ public func cacheLottieAnimation(data: Data, width: Int, height: Int, keyframeOn
}
writer.add(with: { surface in
animation.renderFrame(with: i, into: surface.argb, width: Int32(surface.width), height: Int32(surface.height), bytesPerRow: Int32(surface.bytesPerRow))
if customColor != nil {
for y in 0 ..< surface.height {
for x in 0 ..< surface.width {
let pixel = surface.argb.advanced(by: y * surface.bytesPerRow + x * 4)
let a = pixel.advanced(by: 3).pointee
pixel.advanced(by: 0).pointee = a
pixel.advanced(by: 1).pointee = a
pixel.advanced(by: 2).pointee = a
pixel.advanced(by: 3).pointee = a
}
}
}
return frameDuration
}, proposedWidth: width, proposedHeight: height, insertKeyframe: i == 0 || keyframeOnly)
@ -46,7 +59,7 @@ public func cacheLottieAnimation(data: Data, width: Int, height: Int, keyframeOn
writer.queue.async(work)
}
public func cacheStillSticker(path: String, width: Int, height: Int, writer: AnimationCacheItemWriter) {
public func cacheStillSticker(path: String, width: Int, height: Int, writer: AnimationCacheItemWriter, customColor: UIColor?) {
let work: () -> Void = {
if let data = try? Data(contentsOf: URL(fileURLWithPath: path)), let image = WebP.convert(fromWebP: data) {
writer.add(with: { surface in
@ -55,6 +68,12 @@ public func cacheStillSticker(path: String, width: Int, height: Int, writer: Ani
}
context.withFlippedContext { c in
UIGraphicsPushContext(c)
if let customColor = customColor {
c.setFillColor(customColor.cgColor)
c.setBlendMode(.sourceIn)
}
c.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: context.size))
UIGraphicsPopContext()
}

View File

@ -243,9 +243,10 @@ public final class TextNodeWithEntities {
let itemLayer: InlineStickerItemLayer
if let current = self.inlineStickerItemLayers[id] {
itemLayer = current
itemLayer.dynamicColor = item.textColor
} else {
let pointSize = floor(itemSize * 1.3)
itemLayer = InlineStickerItemLayer(context: context, attemptSynchronousLoad: attemptSynchronousLoad, emoji: stickerItem.emoji, file: stickerItem.file, cache: cache, renderer: renderer, placeholderColor: placeholderColor, pointSize: CGSize(width: pointSize, height: pointSize))
itemLayer = InlineStickerItemLayer(context: context, attemptSynchronousLoad: attemptSynchronousLoad, emoji: stickerItem.emoji, file: stickerItem.file, cache: cache, renderer: renderer, placeholderColor: placeholderColor, pointSize: CGSize(width: pointSize, height: pointSize), dynamicColor: item.textColor)
self.inlineStickerItemLayers[id] = itemLayer
self.textNode.layer.addSublayer(itemLayer)
@ -407,9 +408,10 @@ public class ImmediateTextNodeWithEntities: TextNode {
let itemLayer: InlineStickerItemLayer
if let current = self.inlineStickerItemLayers[id] {
itemLayer = current
itemLayer.dynamicColor = item.textColor
} else {
let pointSize = floor(itemSize * 1.3)
itemLayer = InlineStickerItemLayer(context: context, attemptSynchronousLoad: false, emoji: stickerItem.emoji, file: stickerItem.file, cache: cache, renderer: renderer, placeholderColor: placeholderColor, pointSize: CGSize(width: pointSize, height: pointSize))
itemLayer = InlineStickerItemLayer(context: context, attemptSynchronousLoad: false, emoji: stickerItem.emoji, file: stickerItem.file, cache: cache, renderer: renderer, placeholderColor: placeholderColor, pointSize: CGSize(width: pointSize, height: pointSize), dynamicColor: item.textColor)
self.inlineStickerItemLayers[id] = itemLayer
self.layer.addSublayer(itemLayer)

View File

@ -18,7 +18,7 @@ private func roundUp(_ numToRound: Int, multiple: Int) -> Int {
return numToRound + multiple - remainder
}
public func cacheVideoAnimation(path: String, width: Int, height: Int, writer: AnimationCacheItemWriter, firstFrameOnly: Bool) {
public func cacheVideoAnimation(path: String, width: Int, height: Int, writer: AnimationCacheItemWriter, firstFrameOnly: Bool, customColor: UIColor?) {
let work: () -> Void = {
guard let frameSource = makeVideoStickerDirectFrameSource(queue: writer.queue, path: path, width: roundUp(width, multiple: 16), height: roundUp(height, multiple: 16), cachePathPrefix: nil, unpremultiplyAlpha: false) else {
return

View File

@ -1177,7 +1177,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
var existingIds = Set<Int64>()
for (_, file) in files {
loop: for attribute in file.attributes {
if case let .CustomEmoji(_, _, packReference) = attribute, let packReference = packReference {
if case let .CustomEmoji(_, _, _, packReference) = attribute, let packReference = packReference {
if case let .id(id, _) = packReference, !existingIds.contains(id) {
packReferences.append(packReference)
existingIds.insert(id)
@ -1451,7 +1451,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
var existingIds = Set<Int64>()
for (_, file) in customEmoji {
loop: for attribute in file.attributes {
if case let .CustomEmoji(_, _, packReference) = attribute, let packReference = packReference {
if case let .CustomEmoji(_, _, _, packReference) = attribute, let packReference = packReference {
if case let .id(id, _) = packReference, !existingIds.contains(id) {
packReferences.append(packReference)
existingIds.insert(id)
@ -13676,7 +13676,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
var stickerPackReference: StickerPackReference?
for attribute in file.attributes {
if case let .CustomEmoji(_, _, packReference) = attribute {
if case let .CustomEmoji(_, _, _, packReference) = attribute {
stickerPackReference = packReference
break
}

View File

@ -526,7 +526,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
var emojiAttribute: ChatTextInputTextCustomEmojiAttribute?
loop: for attribute in file.attributes {
switch attribute {
case let .CustomEmoji(_, displayText, _):
case let .CustomEmoji(_, _, displayText, _):
text = displayText
var packId: ItemCollectionId?
@ -781,7 +781,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
}
for attribute in item.file.attributes {
switch attribute {
case let .CustomEmoji(_, alt, _):
case let .CustomEmoji(_, _, alt, _):
if !item.file.isPremiumEmoji || hasPremium {
if !alt.isEmpty, let keyword = allEmoticons[alt] {
result.append((alt, item.file, keyword))
@ -811,7 +811,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
itemFile: itemFile,
subgroupId: nil,
icon: .none,
accentTint: false
tintMode: animationData.isTemplate ? .primary : .none
)
items.append(item)
}
@ -824,8 +824,8 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
itemFile: nil,
subgroupId: nil,
icon: .none,
accentTint: false)
)
tintMode: .none
))
}
return [EmojiPagerContentComponent.ItemGroup(
@ -1731,7 +1731,7 @@ final class EntityInputView: UIView, AttachmentTextInputPanelInputView, UIInputV
var emojiAttribute: ChatTextInputTextCustomEmojiAttribute?
loop: for attribute in file.attributes {
switch attribute {
case let .CustomEmoji(_, displayText, _):
case let .CustomEmoji(_, _, displayText, _):
text = displayText
var packId: ItemCollectionId?
if let id = groupId.base as? ItemCollectionId {
@ -2072,7 +2072,7 @@ private final class EmojiContentPeekBehaviorImpl: EmojiContentPeekBehavior {
loop: for attribute in file.attributes {
switch attribute {
case let .CustomEmoji(_, _, packReference), let .Sticker(_, packReference, _):
case let .CustomEmoji(_, _, _, packReference), let .Sticker(_, packReference, _):
if let packReference = packReference {
let controller = strongSelf.context.sharedContext.makeStickerPackScreen(context: context, updatedPresentationData: nil, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], parentNavigationController: strongSelf.controllerInteraction.navigationController(), sendSticker: { file, sourceView, sourceRect in
sendSticker(file, false, false, nil, false, sourceView, sourceRect, nil)

View File

@ -1165,7 +1165,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
}
}
if resourceAvailable, !message.containsSecretMedia {
if resourceAvailable, !message.containsSecretMedia, !chatPresentationInterfaceState.copyProtectionEnabled, !message.isCopyProtected() {
var mediaReference: AnyMediaReference?
var isVideo = false
for media in message.media {
@ -2413,7 +2413,7 @@ private final class ChatReadReportContextItemNode: ASDisplayNode, ContextMenuCus
var firstCustomEmojiReaction: TelegramMediaFile?
for (_, file) in customEmoji {
loop: for attribute in file.attributes {
if case let .CustomEmoji(_, _, packReference) = attribute, let packReference = packReference {
if case let .CustomEmoji(_, _, _, packReference) = attribute, let packReference = packReference {
if case let .id(id, _) = packReference, !existingIds.contains(id) {
if firstCustomEmojiReaction == nil {
firstCustomEmojiReaction = file

View File

@ -351,7 +351,7 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, pee
}
for attribute in item.file.attributes {
switch attribute {
case let .CustomEmoji(_, alt, _):
case let .CustomEmoji(_, _, alt, _):
if alt == query {
if !item.file.isPremiumEmoji || hasPremium {
result.append((alt, item.file, alt))
@ -415,7 +415,7 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, pee
}
for attribute in item.file.attributes {
switch attribute {
case let .CustomEmoji(_, alt, _):
case let .CustomEmoji(_, _, alt, _):
if !alt.isEmpty, let keyword = allEmoticons[alt] {
if !item.file.isPremiumEmoji || hasPremium {
result.append((alt, item.file, keyword))

View File

@ -1069,7 +1069,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
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)
let textColor = item.presentationData.theme.theme.list.itemPrimaryTextColor
let attributedText = stringWithAppliedEntities(item.message.text, entities: item.message.textEntitiesAttribute?.entities ?? [], baseColor: textColor, linkColor: textColor, 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)

View File

@ -334,7 +334,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
let currentDict = updatedString.attributes(at: range.lowerBound, effectiveRange: nil)
var updatedAttributes: [NSAttributedString.Key: Any] = currentDict
updatedAttributes[NSAttributedString.Key.foregroundColor] = UIColor.clear.cgColor
//updatedAttributes[NSAttributedString.Key.foregroundColor] = UIColor.clear.cgColor
updatedAttributes[ChatTextInputAttributes.customEmoji] = ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: fileId, file: item.message.associatedMedia[MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)] as? TelegramMediaFile)
let insertString = NSAttributedString(string: updatedString.attributedSubstring(from: range).string, attributes: updatedAttributes)

View File

@ -473,7 +473,7 @@ final class CustomEmojiContainerView: UIView {
preconditionFailure()
}
func update(fontSize: CGFloat, emojiRects: [(CGRect, ChatTextInputTextCustomEmojiAttribute)]) {
func update(fontSize: CGFloat, textColor: UIColor, emojiRects: [(CGRect, ChatTextInputTextCustomEmojiAttribute)]) {
var nextIndexById: [Int64: Int] = [:]
var validKeys = Set<InlineStickerItemLayer.Key>()
@ -499,6 +499,10 @@ final class CustomEmojiContainerView: UIView {
continue
}
if let view = view as? EmojiTextAttachmentView {
view.updateTextColor(textColor)
}
let itemSize: CGFloat = floor(24.0 * fontSize / 17.0)
let size = CGSize(width: itemSize, height: itemSize)
@ -2352,7 +2356,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
self.customEmojiContainerView = customEmojiContainerView
}
customEmojiContainerView.update(fontSize: fontSize, emojiRects: customEmojiRects)
customEmojiContainerView.update(fontSize: fontSize, textColor: textColor, emojiRects: customEmojiRects)
} else if let customEmojiContainerView = self.customEmojiContainerView {
customEmojiContainerView.removeFromSuperview()
self.customEmojiContainerView = nil
@ -2599,7 +2603,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
var emojiAttribute: ChatTextInputTextCustomEmojiAttribute?
loop: for attribute in file.attributes {
switch attribute {
case let .CustomEmoji(_, displayText, _):
case let .CustomEmoji(_, _, displayText, _):
text = displayText
emojiAttribute = ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: file.fileId.id, file: file)
break loop

View File

@ -206,7 +206,7 @@ final class EmojisChatInputContextPanelNode: ChatInputContextPanelNode {
if let file = file {
loop: for attribute in file.attributes {
switch attribute {
case let .CustomEmoji(_, displayText, _):
case let .CustomEmoji(_, _, displayText, _):
text = displayText
emojiAttribute = ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: file.fileId.id, file: file)
break loop

View File

@ -2505,7 +2505,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
strongSelf.emojiStatusFileAndPackTitle.set(.never())
for attribute in emojiFile.attributes {
if case let .CustomEmoji(_, _, packReference) = attribute, let packReference = packReference {
if case let .CustomEmoji(_, _, _, packReference) = attribute, let packReference = packReference {
strongSelf.emojiStatusPackDisposable.set((strongSelf.context.engine.stickers.loadedStickerPack(reference: packReference, forceActualized: false)
|> filter { result in
if case .result = result {

View File

@ -3514,7 +3514,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
if let file = files.first?.value {
var stickerPackReference: StickerPackReference?
for attribute in file.attributes {
if case let .CustomEmoji(_, _, packReference) = attribute {
if case let .CustomEmoji(_, _, _, packReference) = attribute {
stickerPackReference = packReference
break
}