// // EmojiLabel.swift // Tusker // // Created by Shadowfacts on 3/1/20. // Copyright © 2020 Shadowfacts. All rights reserved. // import UIKit import Pachyderm private let emojiRegex = try! NSRegularExpression(pattern: ":(\\w+):", options: []) class EmojiLabel: UILabel { private var emojiIdentifier: String? private var emojiRequests: [ImageCache.Request] = [] func setEmojis(_ emojis: [Emoji], identifier: String) { guard emojis.count > 0, let attributedText = attributedText else { return } self.emojiIdentifier = identifier emojiRequests.forEach { $0.cancel() } emojiRequests = [] let matches = emojiRegex.matches(in: attributedText.string, options: [], range: attributedText.fullRange) let emojiImages = CachedDictionary(name: "EmojiLabel Emoji Images") 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 = (attributedText.string as NSString).substring(with: match.range(at: 1)) return emoji.shortcode == matchShortcode }) else { continue } group.enter() let request = ImageCache.emojis.get(emoji.url) { (data) in defer { group.leave() } guard let data = data, let image = UIImage(data: data) else { return } emojiImages[emoji.shortcode] = image } if let request = request { emojiRequests.append(request) } } 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(attributedString: attributedText) // replaces the emojis starting from the end of the string as to not alter the indicies of preceeding emojis for match in matches.reversed() { let shortcode = (mutAttrString.string as NSString).substring(with: match.range(at: 1)) guard let emojiImage = emojiImages[shortcode] else { continue } let attachment = NSTextAttachment(emojiImage: emojiImage, in: self.font, with: self.textColor) let attachmentStr = NSAttributedString(attachment: attachment) mutAttrString.replaceCharacters(in: match.range, with: attachmentStr) } self.attributedText = mutAttrString self.setNeedsLayout() self.setNeedsDisplay() } } func removeEmojis() { emojiIdentifier = nil emojiRequests.forEach { $0.cancel() } emojiRequests = [] } } extension EmojiLabel { func updateForAccountDisplayName(account: Account) { if Preferences.shared.hideCustomEmojiInUsernames { self.text = account.displayName self.removeEmojis() } else { self.text = account.displayName self.setEmojis(account.emojis, identifier: account.id) } } func updateForAccountDisplayName(account: AccountMO) { if Preferences.shared.hideCustomEmojiInUsernames { self.text = account.displayNameWithoutCustomEmoji self.removeEmojis() } else { self.text = account.displayOrUserName self.setEmojis(account.emojis, identifier: account.id) } } }