forked from zelo72/mastodon-ios
109 lines
3.7 KiB
Swift
109 lines
3.7 KiB
Swift
|
//
|
||
|
// MastodonStatusContent+ParseResult.swift
|
||
|
// Mastodon
|
||
|
//
|
||
|
// Created by Cirno MainasuK on 2021-6-20.
|
||
|
//
|
||
|
|
||
|
import Foundation
|
||
|
import ActiveLabel
|
||
|
|
||
|
extension MastodonStatusContent {
|
||
|
struct ParseResult: Hashable {
|
||
|
let document: String
|
||
|
let original: String
|
||
|
let trimmed: String
|
||
|
let activeEntities: [ActiveEntity]
|
||
|
|
||
|
static func == (lhs: MastodonStatusContent.ParseResult, rhs: MastodonStatusContent.ParseResult) -> Bool {
|
||
|
return lhs.document == rhs.document
|
||
|
&& lhs.original == rhs.original
|
||
|
&& lhs.trimmed == rhs.trimmed
|
||
|
&& lhs.activeEntities.count == rhs.activeEntities.count // FIXME:
|
||
|
}
|
||
|
|
||
|
func hash(into hasher: inout Hasher) {
|
||
|
hasher.combine(document)
|
||
|
hasher.combine(original)
|
||
|
hasher.combine(trimmed)
|
||
|
hasher.combine(activeEntities.count) // FIXME:
|
||
|
}
|
||
|
|
||
|
func trimmedAttributedString(appearance: MastodonStatusContent.Appearance) -> NSAttributedString {
|
||
|
let attributedString = NSMutableAttributedString(string: trimmed, attributes: appearance.attributes)
|
||
|
for entity in activeEntities {
|
||
|
switch entity.type {
|
||
|
case .url:
|
||
|
attributedString.addAttributes(appearance.urlAttributes, range: entity.range)
|
||
|
case .hashtag:
|
||
|
attributedString.addAttributes(appearance.hashtagAttributes, range: entity.range)
|
||
|
case .mention:
|
||
|
attributedString.addAttributes(appearance.mentionAttributes, range: entity.range)
|
||
|
default:
|
||
|
break
|
||
|
}
|
||
|
if let uri = entity.type.uri {
|
||
|
attributedString.addAttributes([
|
||
|
.link: uri
|
||
|
], range: entity.range)
|
||
|
}
|
||
|
}
|
||
|
return attributedString
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
extension ActiveEntityType {
|
||
|
|
||
|
static let appScheme = "mastodon"
|
||
|
|
||
|
init?(url: URL) {
|
||
|
guard let scheme = url.scheme?.lowercased() else { return nil }
|
||
|
guard scheme == ActiveEntityType.appScheme else {
|
||
|
self = .url("", trimmed: "", url: url.absoluteString, userInfo: nil)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
|
||
|
let parameters = components.queryItems else { return nil }
|
||
|
|
||
|
if let hashtag = parameters.first(where: { $0.name == "hashtag" }), let encoded = hashtag.value, let value = String(base64Encoded: encoded) {
|
||
|
self = .hashtag(value, userInfo: nil)
|
||
|
return
|
||
|
}
|
||
|
if let mention = parameters.first(where: { $0.name == "mention" }), let encoded = mention.value, let value = String(base64Encoded: encoded) {
|
||
|
self = .mention(value, userInfo: nil)
|
||
|
return
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
var uri: URL? {
|
||
|
switch self {
|
||
|
case .url(_, _, let url, _):
|
||
|
return URL(string: url)
|
||
|
case .hashtag(let hashtag, _):
|
||
|
return URL(string: "\(ActiveEntityType.appScheme)://meta?hashtag=\(hashtag.base64Encoded)")
|
||
|
case .mention(let mention, _):
|
||
|
return URL(string: "\(ActiveEntityType.appScheme)://meta?mention=\(mention.base64Encoded)")
|
||
|
default:
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
extension String {
|
||
|
fileprivate var base64Encoded: String {
|
||
|
return Data(self.utf8).base64EncodedString()
|
||
|
}
|
||
|
|
||
|
init?(base64Encoded: String) {
|
||
|
guard let data = Data(base64Encoded: base64Encoded),
|
||
|
let string = String(data: data, encoding: .utf8) else {
|
||
|
return nil
|
||
|
}
|
||
|
self = string
|
||
|
}
|
||
|
}
|