forked from shadowfacts/Tusker
parent
71bfd1513a
commit
37f6a0b4c8
|
@ -9,17 +9,16 @@
|
|||
import Foundation
|
||||
|
||||
public class Emoji: Decodable {
|
||||
let shortcode: String
|
||||
let url: URL
|
||||
let staticURL: URL
|
||||
// TODO: missing in pleroma
|
||||
// let visibleInPicker: Bool
|
||||
public let shortcode: String
|
||||
public let url: URL
|
||||
public let staticURL: URL
|
||||
public let visibleInPicker: Bool
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case shortcode
|
||||
case url
|
||||
case staticURL = "static_url"
|
||||
// case visibleInPicker = "visible_in_picker"
|
||||
case visibleInPicker = "visible_in_picker"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ class ImageCache {
|
|||
static let avatars = ImageCache(name: "Avatars", memoryExpiry: .seconds(60 * 60), diskExpiry: .seconds(60 * 60 * 24))
|
||||
static let headers = ImageCache(name: "Headers", memoryExpiry: .seconds(60 * 60), diskExpiry: .seconds(60 * 60 * 24))
|
||||
static let attachments = ImageCache(name: "Attachments", memoryExpiry: .seconds(60 * 2))
|
||||
static let emojis = ImageCache(name: "Emojis", memoryExpiry: .seconds(60 * 5), diskExpiry: .seconds(60 * 60))
|
||||
|
||||
let cache: Cache<Data>
|
||||
|
||||
|
|
|
@ -8,14 +8,74 @@
|
|||
|
||||
import UIKit
|
||||
import SafariServices
|
||||
import TTTAttributedLabel
|
||||
import Pachyderm
|
||||
import SwiftSoup
|
||||
|
||||
class ContentLabel: LinkLabel {
|
||||
|
||||
private static let emojiRegex = try! NSRegularExpression(pattern: ":(\\w+):", options: [])
|
||||
|
||||
var navigationDelegate: TuskerNavigationDelegate?
|
||||
|
||||
// MARK: - Emojis
|
||||
func setEmojis(_ emojis: [Emoji]) {
|
||||
guard !emojis.isEmpty else { return }
|
||||
|
||||
let group = DispatchGroup()
|
||||
|
||||
let mutAttrString = NSMutableAttributedString(attributedString: self.attributedText!)
|
||||
let string = mutAttrString.string
|
||||
let matches = ContentLabel.emojiRegex.matches(in: string, options: [], range: NSRange(location: 0, length: mutAttrString.length))
|
||||
for match in matches.reversed() {
|
||||
let shortcode = (string as NSString).substring(with: match.range(at: 1))
|
||||
guard let emoji = emojis.first(where: { $0.shortcode == shortcode }) else {
|
||||
continue
|
||||
}
|
||||
|
||||
group.enter()
|
||||
ImageCache.emojis.get(emoji.url) { (data) in
|
||||
guard let data = data, let image = UIImage(data: data) else {
|
||||
group.leave()
|
||||
return
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
let attachment = self.createEmojiTextAttachment(image: image, index: match.range.location)
|
||||
mutAttrString.replaceCharacters(in: match.range, with: NSAttributedString(attachment: attachment))
|
||||
|
||||
group.leave()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
group.notify(queue: .main) {
|
||||
self.attributedText = mutAttrString
|
||||
self.setNeedsLayout()
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
// Based on https://github.com/ReticentJohn/Amaroq/blob/7c5b7088eb9fd1611dcb0f47d43bf8df093e142c/DireFloof/InlineImageHelpers.m
|
||||
func createEmojiTextAttachment(image: UIImage, index: Int) -> NSTextAttachment {
|
||||
let font = self.font!
|
||||
|
||||
let adjustedCapHeight = font.capHeight - 1
|
||||
var imageSizeMatchingFontSize = CGSize(width: image.size.width * (adjustedCapHeight / image.size.height), height: adjustedCapHeight)
|
||||
|
||||
let defaultScale: CGFloat = 1.4
|
||||
imageSizeMatchingFontSize = CGSize(width: imageSizeMatchingFontSize.width * defaultScale, height: imageSizeMatchingFontSize.height * defaultScale)
|
||||
let textColor = self.textColor!
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(imageSizeMatchingFontSize, false, 0.0)
|
||||
textColor.set()
|
||||
image.draw(in: CGRect(origin: .zero, size: imageSizeMatchingFontSize))
|
||||
let attachmentImage = UIGraphicsGetImageFromCurrentImageContext()
|
||||
UIGraphicsEndImageContext()
|
||||
|
||||
let attachment = NSTextAttachment()
|
||||
attachment.image = attachmentImage
|
||||
return attachment
|
||||
}
|
||||
|
||||
// MARK: - HTML Parsing
|
||||
func setTextFromHtml(_ html: String) {
|
||||
let doc = try! SwiftSoup.parse(html)
|
||||
|
|
|
@ -16,6 +16,7 @@ class StatusContentLabel: ContentLabel {
|
|||
guard let statusID = statusID else { return }
|
||||
guard let status = MastodonCache.status(for: statusID) else { fatalError("Can't set StatusContentLabel text without cached status \(statusID)") }
|
||||
setTextFromHtml(status.content)
|
||||
setEmojis(status.emojis)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue