Files
damus/damus/Nostr/ReferencedId.swift
Daniel D’Aquino f9271da11c Add support for rendering highlights with comments
This commit implements rendering comments from the `["comment",
<COMMENT_TEXT>]` tag in a highlight note.

Comment contents get rendered like a kind 1 note's "content" field

This commit also adds the `r` "reference" tag as a standard tag reference type

Changelog-Added: Add support for rendering highlights with comments
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2024-08-17 15:58:43 -07:00

191 lines
4.7 KiB
Swift

//
// ReferencedId.swift
// damus
//
// Created by William Casarin on 2023-07-22.
//
import Foundation
func tagref_should_be_id(_ tag: NdbTagElem) -> Bool {
return !(tag.matches_char("t") || tag.matches_char("d"))
}
struct References<T: TagConvertible>: Sequence, IteratorProtocol {
let tags: TagsSequence
var tags_iter: TagsIterator
init(tags: TagsSequence) {
self.tags = tags
self.tags_iter = tags.makeIterator()
}
mutating func next() -> T? {
while let tag = tags_iter.next() {
guard let evref = T.from_tag(tag: tag) else { continue }
return evref
}
return nil
}
}
extension References {
var first: T? {
self.first(where: { _ in true })
}
var last: T? {
var last: T? = nil
for t in self {
last = t
}
return last
}
}
// NdbTagElem transition helpers
extension String {
func string() -> String {
return self
}
func first_char() -> AsciiCharacter? {
self.first.flatMap { chr in AsciiCharacter(chr) }
}
func matches_char(_ c: AsciiCharacter) -> Bool {
return self.first == c.character
}
func matches_str(_ str: String) -> Bool {
return self == str
}
}
enum FollowRef: TagKeys, Hashable, TagConvertible, Equatable {
// NOTE: When adding cases make sure to update key and from_tag
case pubkey(Pubkey)
case hashtag(String)
var key: FollowKeys {
switch self {
case .hashtag: return .t
case .pubkey: return .p
}
}
enum FollowKeys: AsciiCharacter, TagKey, CustomStringConvertible {
case p, t
var keychar: AsciiCharacter { self.rawValue }
var description: String { self.rawValue.description }
}
static func from_tag(tag: TagSequence) -> FollowRef? {
guard tag.count >= 2 else { return nil }
var i = tag.makeIterator()
guard let t0 = i.next(),
let c = t0.single_char,
let fkey = FollowKeys(rawValue: c),
let t1 = i.next()
else {
return nil
}
switch fkey {
case .p: return t1.id().map({ .pubkey(Pubkey($0)) })
case .t: return .hashtag(t1.string())
}
}
var tag: [String] {
[key.description, self.description]
}
var description: String {
switch self {
case .pubkey(let pubkey): return pubkey.description
case .hashtag(let string): return string
}
}
}
enum RefId: TagConvertible, TagKeys, Equatable, Hashable {
case event(NoteId)
case pubkey(Pubkey)
case quote(QuoteId)
case hashtag(Hashtag)
case param(TagElem)
case naddr(NAddr)
case reference(String)
var key: RefKey {
switch self {
case .event: return .e
case .pubkey: return .p
case .quote: return .q
case .hashtag: return .t
case .param: return .d
case .naddr: return .a
case .reference: return .r
}
}
enum RefKey: AsciiCharacter, TagKey, CustomStringConvertible {
case e, p, t, d, q, a, r
var keychar: AsciiCharacter {
self.rawValue
}
var description: String {
self.keychar.description
}
}
var tag: [String] {
[self.key.description, self.description]
}
var description: String {
switch self {
case .event(let noteId): return noteId.hex()
case .pubkey(let pubkey): return pubkey.hex()
case .quote(let quote): return quote.hex()
case .hashtag(let string): return string.hashtag
case .param(let string): return string.string()
case .naddr(let naddr):
return naddr.kind.description + ":" + naddr.author.hex() + ":" + naddr.identifier
case .reference(let string):
return string
}
}
static func from_tag(tag: TagSequence) -> RefId? {
var i = tag.makeIterator()
guard tag.count >= 2,
let t0 = i.next(),
let key = t0.single_char,
let rkey = RefKey(rawValue: key),
let t1 = i.next()
else { return nil }
switch rkey {
case .e: return t1.id().map({ .event(NoteId($0)) })
case .p: return t1.id().map({ .pubkey(Pubkey($0)) })
case .q: return t1.id().map({ .quote(QuoteId($0)) })
case .t: return .hashtag(Hashtag(hashtag: t1.string()))
case .d: return .param(t1)
case .a: return .naddr(NAddr(identifier: "", author: Pubkey(Data()), relays: [], kind: 0))
case .r: return .reference(t1.string())
}
}
}