Emoji fixes

This commit is contained in:
Ali 2022-07-16 02:02:39 +02:00
parent 89ecb672c7
commit 2a5b45883d
6 changed files with 126 additions and 45 deletions

View File

@ -9,13 +9,14 @@ private func rtfStringWithAppliedEntities(_ text: String, entities: [MessageText
let sourceString = stringWithAppliedEntities(text, entities: entities, baseColor: .black, linkColor: .black, baseFont: Font.regular(14.0), linkFont: Font.regular(14.0), boldFont: Font.semibold(14.0), italicFont: Font.italic(14.0), boldItalicFont: Font.semiboldItalic(14.0), fixedFont: Font.monospace(14.0), blockQuoteFont: Font.regular(14.0), underlineLinks: false, external: true, message: nil)
let test = NSMutableAttributedString(attributedString: sourceString)
if #available(iOS 15.0, *) {
test.enumerateAttribute(ChatTextInputAttributes.customEmoji, in: NSRange(location: 0, length: sourceString.length), using: { value, range, _ in
if let value = value as? ChatTextInputTextCustomEmojiAttribute {
test.addAttribute(NSAttributedString.Key.link, value: URL(string: "tg://emoji?\(value.fileId)")!, range: range)
}
})
}
var index = 0
test.enumerateAttribute(ChatTextInputAttributes.customEmoji, in: NSRange(location: 0, length: sourceString.length), using: { value, range, _ in
if let value = value as? ChatTextInputTextCustomEmojiAttribute {
test.addAttribute(NSAttributedString.Key.link, value: URL(string: "tg://emoji?id=\(value.fileId)&t=\(index)")!, range: range)
index += 1
}
})
test.removeAttribute(ChatTextInputAttributes.customEmoji, range: NSRange(location: 0, length: test.length))
if let data = try? test.data(from: NSRange(location: 0, length: test.length), documentAttributes: [NSAttributedString.DocumentAttributeKey.documentType: NSAttributedString.DocumentType.rtf]) {
if var rtf = String(data: data, encoding: .windowsCP1252) {
@ -71,10 +72,18 @@ public func chatInputStateStringFromRTF(_ data: Data, type: NSAttributedString.D
if let attributedString = try? NSAttributedString(data: data, options: [NSAttributedString.DocumentReadingOptionKey.documentType: type], documentAttributes: nil) {
let updatedString = NSMutableAttributedString(attributedString: attributedString)
updatedString.enumerateAttribute(NSAttributedString.Key.link, in: NSRange(location: 0, length: attributedString.length), using: { value, range, _ in
if let url = value as? URL, url.scheme == "tg", url.host == "emoji", let query = url.query {
if let fileId = Int64(query) {
if let url = value as? URL, url.scheme == "tg", url.host == "emoji" {
var emojiId: Int64?
if let queryItems = URLComponents(string: url.absoluteString)?.queryItems {
for item in queryItems {
if item.name == "id" {
emojiId = item.value.flatMap(Int64.init)
}
}
}
if let emojiId = emojiId {
updatedString.removeAttribute(NSAttributedString.Key.link, range: range)
updatedString.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(stickerPack: nil, fileId: fileId, file: nil), range: range)
updatedString.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(stickerPack: nil, fileId: emojiId, file: nil), range: range)
}
}
})

View File

@ -306,31 +306,28 @@ public final class GifPagerContentComponent: Component {
}
}
}
private(set) var displayPlaceholder: Bool = false {
didSet {
if self.displayPlaceholder != oldValue {
self.onUpdateDisplayPlaceholder(self.displayPlaceholder)
}
}
}
let onUpdateDisplayPlaceholder: (Bool) -> Void
private(set) var displayPlaceholder: Bool = false
let onUpdateDisplayPlaceholder: (Bool, Double) -> Void
init(
item: Item?,
context: AccountContext,
groupId: String,
attemptSynchronousLoad: Bool,
onUpdateDisplayPlaceholder: @escaping (Bool) -> Void
onUpdateDisplayPlaceholder: @escaping (Bool, Double) -> Void
) {
self.item = item
self.onUpdateDisplayPlaceholder = onUpdateDisplayPlaceholder
super.init(context: context, file: item?.file, synchronousLoad: attemptSynchronousLoad)
self.updateDisplayPlaceholder(displayPlaceholder: true)
if item == nil {
self.updateDisplayPlaceholder(displayPlaceholder: true, duration: 0.0)
}
self.started = { [weak self] in
self?.updateDisplayPlaceholder(displayPlaceholder: false)
let _ = self
//self?.updateDisplayPlaceholder(displayPlaceholder: false, duration: 0.2)
}
}
@ -359,8 +356,12 @@ public final class GifPagerContentComponent: Component {
self.shouldBeAnimating = shouldBePlaying
}
func updateDisplayPlaceholder(displayPlaceholder: Bool) {
func updateDisplayPlaceholder(displayPlaceholder: Bool, duration: Double) {
if self.displayPlaceholder == displayPlaceholder {
return
}
self.displayPlaceholder = displayPlaceholder
self.onUpdateDisplayPlaceholder(displayPlaceholder, duration)
}
}
@ -402,6 +403,7 @@ public final class GifPagerContentComponent: Component {
private let scrollView: ContentScrollView
private let placeholdersContainerView: UIView
private var visibleItemPlaceholderViews: [ItemKey: ItemPlaceholderView] = [:]
private var visibleItemLayers: [ItemKey: ItemLayer] = [:]
private var ignoreScrolling: Bool = false
@ -417,6 +419,8 @@ public final class GifPagerContentComponent: Component {
self.shimmerHostView = PortalSourceView()
self.standaloneShimmerEffect = StandaloneShimmerEffect()
self.placeholdersContainerView = UIView()
self.scrollView = ContentScrollView()
super.init(frame: frame)
@ -436,6 +440,8 @@ public final class GifPagerContentComponent: Component {
self.scrollView.delegate = self
self.addSubview(self.scrollView)
self.scrollView.addSubview(self.placeholdersContainerView)
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
self.useSublayerTransformForActivation = false
@ -621,6 +627,12 @@ public final class GifPagerContentComponent: Component {
continue
}
if !component.isLoading {
if let placeholderView = self.visibleItemPlaceholderViews.removeValue(forKey: .placeholder(index)) {
self.visibleItemPlaceholderViews[itemId] = placeholderView
}
}
validIds.insert(itemId)
let itemFrame = itemLayout.frame(at: index)
@ -639,7 +651,7 @@ public final class GifPagerContentComponent: Component {
context: component.context,
groupId: "savedGif",
attemptSynchronousLoad: attemptSynchronousLoads,
onUpdateDisplayPlaceholder: { [weak self] displayPlaceholder in
onUpdateDisplayPlaceholder: { [weak self] displayPlaceholder, duration in
guard let strongSelf = self else {
return
}
@ -651,7 +663,7 @@ public final class GifPagerContentComponent: Component {
} else {
placeholderView = ItemPlaceholderView(shimmerView: strongSelf.shimmerHostView)
strongSelf.visibleItemPlaceholderViews[itemId] = placeholderView
strongSelf.scrollView.insertSubview(placeholderView, at: 0)
strongSelf.placeholdersContainerView.addSubview(placeholderView)
}
placeholderView.frame = itemLayer.frame
placeholderView.update(size: placeholderView.bounds.size)
@ -661,9 +673,20 @@ public final class GifPagerContentComponent: Component {
} else {
if let placeholderView = strongSelf.visibleItemPlaceholderViews[itemId] {
strongSelf.visibleItemPlaceholderViews.removeValue(forKey: itemId)
placeholderView.removeFromSuperview()
if duration > 0.0 {
if let itemLayer = strongSelf.visibleItemLayers[itemId] {
itemLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18)
}
strongSelf.updateShimmerIfNeeded()
placeholderView.alpha = 0.0
placeholderView.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, completion: { [weak self, weak placeholderView] _ in
placeholderView?.removeFromSuperview()
self?.updateShimmerIfNeeded()
})
} else {
placeholderView.removeFromSuperview()
strongSelf.updateShimmerIfNeeded()
}
}
}
}
@ -683,9 +706,13 @@ public final class GifPagerContentComponent: Component {
itemTransition.setFrame(view: placeholderView, frame: itemFrame)
placeholderView.update(size: itemFrame.size)
}
} else if updateItemLayerPlaceholder {
}
if updateItemLayerPlaceholder {
if itemLayer.displayPlaceholder {
itemLayer.onUpdateDisplayPlaceholder(true)
itemLayer.onUpdateDisplayPlaceholder(true, 0.0)
} else {
itemLayer.onUpdateDisplayPlaceholder(false, 0.2)
}
}
}
@ -708,7 +735,7 @@ public final class GifPagerContentComponent: Component {
}
private func updateShimmerIfNeeded() {
if self.visibleItemPlaceholderViews.isEmpty {
if self.placeholdersContainerView.subviews.isEmpty {
self.standaloneShimmerEffect.layer = nil
} else {
self.standaloneShimmerEffect.layer = self.shimmerHostView.layer

View File

@ -112,13 +112,19 @@ public final class TextNodeWithEntities {
if let sourceString = arguments.attributedString {
let string = NSMutableAttributedString(attributedString: sourceString)
let fullRange = NSRange(location: 0, length: string.length)
string.enumerateAttribute(ChatTextInputAttributes.customEmoji, in: fullRange, options: [], using: { value, range, _ in
if let value = value as? ChatTextInputTextCustomEmojiAttribute {
if let font = string.attribute(.font, at: range.location, effectiveRange: nil) as? UIFont {
string.addAttribute(NSAttributedString.Key("Attribute__EmbeddedItem"), value: InlineStickerItem(emoji: value, file: value.file, fontSize: font.pointSize), range: range)
var fullRange = NSRange(location: 0, length: string.length)
while true {
var found = false
string.enumerateAttribute(ChatTextInputAttributes.customEmoji, in: fullRange, options: [], using: { value, range, stop in
if let value = value as? ChatTextInputTextCustomEmojiAttribute, let font = string.attribute(.font, at: range.location, effectiveRange: nil) as? UIFont {
let updatedSubstring = NSMutableAttributedString(string: ".")
let itemSize = font.pointSize * 24.0 / 17.0 / CGFloat(min(2, range.length))
let replacementRange = NSRange(location: 0, length: updatedSubstring.length)
updatedSubstring.addAttributes(string.attributes(at: range.location, effectiveRange: nil), range: replacementRange)
updatedSubstring.addAttribute(NSAttributedString.Key("Attribute__EmbeddedItem"), value: InlineStickerItem(emoji: value, file: value.file, fontSize: font.pointSize), range: replacementRange)
updatedSubstring.addAttribute(originalTextAttributeKey, value: string.attributedSubstring(from: range).string, range: replacementRange)
let itemSize = font.pointSize * 24.0 / 17.0
let runDelegateData = RunDelegateData(
ascent: font.ascender,
@ -126,7 +132,7 @@ public final class TextNodeWithEntities {
width: itemSize
)
var callbacks = CTRunDelegateCallbacks(
version: kCTRunDelegateVersion1,
version: kCTRunDelegateCurrentVersion,
dealloc: { dataRef in
Unmanaged<RunDelegateData>.fromOpaque(dataRef).release()
},
@ -143,12 +149,23 @@ public final class TextNodeWithEntities {
return data.takeUnretainedValue().width
}
)
if let runDelegate = CTRunDelegateCreate(&callbacks, Unmanaged.passRetained(runDelegateData).toOpaque()) {
string.addAttribute(NSAttributedString.Key(kCTRunDelegateAttributeName as String), value: runDelegate, range: range)
updatedSubstring.addAttribute(NSAttributedString.Key(kCTRunDelegateAttributeName as String), value: runDelegate, range: replacementRange)
}
string.replaceCharacters(in: range, with: updatedSubstring)
let updatedRange = NSRange(location: range.location, length: updatedSubstring.length)
found = true
stop.pointee = ObjCBool(true)
fullRange = NSRange(location: updatedRange.upperBound, length: fullRange.upperBound - range.upperBound)
}
})
if !found {
break
}
})
}
updatedString = string
}

View File

@ -23,6 +23,8 @@ public struct ChatTextInputAttributes {
public static let allAttributes = [ChatTextInputAttributes.bold, ChatTextInputAttributes.italic, ChatTextInputAttributes.monospace, ChatTextInputAttributes.strikethrough, ChatTextInputAttributes.underline, ChatTextInputAttributes.textMention, ChatTextInputAttributes.textUrl, ChatTextInputAttributes.spoiler, ChatTextInputAttributes.customEmoji]
}
public let originalTextAttributeKey = NSAttributedString.Key(rawValue: "Attribute__OriginalText")
public func stateAttributedStringForText(_ text: NSAttributedString) -> NSAttributedString {
let sourceString = NSMutableAttributedString(attributedString: text)
while true {
@ -205,7 +207,8 @@ public final class ChatTextInputTextCustomEmojiAttribute: NSObject {
override public func isEqual(_ object: Any?) -> Bool {
if let other = object as? ChatTextInputTextCustomEmojiAttribute {
return self.stickerPack == other.stickerPack && self.fileId == other.fileId && self.file?.fileId == other.file?.fileId
return self === other
//return self.stickerPack == other.stickerPack && self.fileId == other.fileId && self.file?.fileId == other.file?.fileId
} else {
return false
}

View File

@ -13,6 +13,7 @@ swift_library(
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
"//submodules/Display:Display",
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/TextFormat:TextFormat",
],
visibility = [
"//visibility:public",

View File

@ -4,6 +4,7 @@ import UIKit.UIGestureRecognizerSubclass
import AsyncDisplayKit
import Display
import TelegramPresentationData
import TextFormat
private func findScrollView(view: UIView?) -> UIScrollView? {
if let view = view {
@ -494,20 +495,43 @@ public final class TextSelectionNode: ASDisplayNode {
}
completeRect = completeRect.insetBy(dx: 0.0, dy: -12.0)
let attributedText = attributedString.attributedSubstring(from: range)
let string = NSMutableAttributedString(attributedString: attributedString.attributedSubstring(from: range))
var fullRange = NSRange(location: 0, length: string.length)
while true {
var found = false
string.enumerateAttribute(originalTextAttributeKey, in: fullRange, options: [], using: { value, range, stop in
if let value = value as? String {
let updatedSubstring = NSMutableAttributedString(string: value)
let replacementRange = NSRange(location: 0, length: updatedSubstring.length)
updatedSubstring.addAttributes(string.attributes(at: range.location, effectiveRange: nil), range: replacementRange)
string.replaceCharacters(in: range, with: updatedSubstring)
let updatedRange = NSRange(location: range.location, length: updatedSubstring.length)
found = true
stop.pointee = ObjCBool(true)
fullRange = NSRange(location: updatedRange.upperBound, length: fullRange.upperBound - range.upperBound)
}
})
if !found {
break
}
}
var actions: [ContextMenuAction] = []
actions.append(ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuCopy, accessibilityLabel: self.strings.Conversation_ContextMenuCopy), action: { [weak self] in
self?.performAction(attributedText, .copy)
self?.performAction(string, .copy)
self?.dismissSelection()
}))
actions.append(ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuLookUp, accessibilityLabel: self.strings.Conversation_ContextMenuLookUp), action: { [weak self] in
self?.performAction(attributedText, .lookup)
self?.performAction(string, .lookup)
self?.dismissSelection()
}))
if #available(iOS 15.0, *) {
actions.append(ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuTranslate, accessibilityLabel: self.strings.Conversation_ContextMenuTranslate), action: { [weak self] in
self?.performAction(attributedText, .translate)
self?.performAction(string, .translate)
self?.dismissSelection()
}))
}
@ -518,7 +542,7 @@ public final class TextSelectionNode: ASDisplayNode {
// }))
// }
actions.append(ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuShare, accessibilityLabel: self.strings.Conversation_ContextMenuShare), action: { [weak self] in
self?.performAction(attributedText, .share)
self?.performAction(string, .share)
self?.dismissSelection()
}))