// // BaseEmojiLabel.swift // Tusker // // Created by Shadowfacts on 10/18/20. // Copyright © 2020 Shadowfacts. All rights reserved. // import UIKit import Pachyderm private let emojiRegex = try! NSRegularExpression(pattern: ":(\\w+):", options: []) protocol BaseEmojiLabel: class { var emojiIdentifier: String? { get set } var emojiRequests: [ImageCache.Request] { get set } var emojiFont: UIFont { get } var emojiTextColor: UIColor { get } } extension BaseEmojiLabel { func replaceEmojis(in string: String, emojis: [Emoji], identifier: String, completion: @escaping (NSAttributedString) -> Void) { let matches = emojiRegex.matches(in: string, options: [], range: NSRange(location: 0, length: string.utf16.count)) guard !matches.isEmpty else { completion(NSAttributedString(string: string)) return } let emojiImages = MultiThreadDictionary(name: "BaseEmojiLabel Emoji Images") var foundEmojis = false let group = DispatchGroup() for emoji in emojis { // only make requests for emojis that are present in the text to avoid making unnecessary network requests guard matches.contains(where: { (match) in let matchShortcode = (string as NSString).substring(with: match.range(at: 1)) return emoji.shortcode == matchShortcode }) else { continue } foundEmojis = true group.enter() let request = ImageCache.emojis.get(emoji.url) { (_, image) in defer { group.leave() } guard let image = image, let transformedImage = ImageGrayscalifier.convertIfNecessary(url: emoji.url, image: image) else { return } emojiImages[emoji.shortcode] = transformedImage } if let request = request { emojiRequests.append(request) } } guard foundEmojis else { completion(NSAttributedString(string: string)) return } group.notify(queue: .main) { [weak self] in // if e.g. the account changes before all emojis are loaded, don't bother trying to set them guard let self = self, self.emojiIdentifier == identifier else { return } let mutAttrString = NSMutableAttributedString(string: string) // replaces the emojis starting from the end of the string as to not alter the indices of preceeding emojis for match in matches.reversed() { let shortcode = (string as NSString).substring(with: match.range(at: 1)) guard let emojiImage = emojiImages[shortcode] else { continue } let attachment = NSTextAttachment(emojiImage: emojiImage, in: self.emojiFont, with: self.emojiTextColor) let attachmentStr = NSAttributedString(attachment: attachment) mutAttrString.replaceCharacters(in: match.range, with: attachmentStr) } completion(mutAttrString) } } }