mirror of
git://jb55.com/damus
synced 2024-10-01 17:30:44 +00:00
nip19: add bech32 TLV url parsing
Create shortened URLs for bech32 with TLV data strings. Additionally, upon clicking on an nevent URL the user is directed to the note. Lightning-url: LNURL1DP68GURN8GHJ7EM9W3SKCCNE9E3K7MF0D3H82UNVWQHKWUN9V4HXGCTHDC6RZVGR8SW3G Signed-off-by: kernelkind <kernelkind@gmail.com> Reviewed-by: William Casarin <jb55@jb55.com> Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
parent
af75eed83a
commit
d07ad67778
@ -492,21 +492,15 @@ struct ContentView: View {
|
|||||||
open_profile(pubkey: pubkey)
|
open_profile(pubkey: pubkey)
|
||||||
|
|
||||||
case .note(let noteId):
|
case .note(let noteId):
|
||||||
guard let target = damus_state.events.lookup(noteId) else {
|
openEvent(noteId: noteId, notificationType: local.type)
|
||||||
return
|
case .nevent(let nevent):
|
||||||
}
|
openEvent(noteId: nevent.noteid, notificationType: local.type)
|
||||||
|
case .nprofile(let nprofile):
|
||||||
switch local.type {
|
open_profile(pubkey: nprofile.author)
|
||||||
case .dm:
|
case .nrelay(_):
|
||||||
selected_timeline = .dms
|
break
|
||||||
damus_state.dms.set_active_dm(target.pubkey)
|
case .naddr(let naddr):
|
||||||
navigationCoordinator.push(route: Route.DMChat(dms: damus_state.dms.active_model))
|
break
|
||||||
case .like, .zap, .mention, .repost:
|
|
||||||
open_event(ev: target)
|
|
||||||
case .profile_zap:
|
|
||||||
// Handled separately above.
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -725,6 +719,22 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func openEvent(noteId: NoteId, notificationType: LocalNotificationType) {
|
||||||
|
guard let target = damus_state.events.lookup(noteId) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch notificationType {
|
||||||
|
case .dm:
|
||||||
|
selected_timeline = .dms
|
||||||
|
damus_state.dms.set_active_dm(target.pubkey)
|
||||||
|
navigationCoordinator.push(route: Route.DMChat(dms: damus_state.dms.active_model))
|
||||||
|
case .like, .zap, .mention, .repost:
|
||||||
|
open_event(ev: target)
|
||||||
|
case .profile_zap:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ContentView_Previews: PreviewProvider {
|
struct ContentView_Previews: PreviewProvider {
|
||||||
@ -1082,6 +1092,8 @@ func on_open_url(state: DamusState, url: URL, result: @escaping (OpenResult?) ->
|
|||||||
case .param, .quote:
|
case .param, .quote:
|
||||||
// doesn't really make sense here
|
// doesn't really make sense here
|
||||||
break
|
break
|
||||||
|
case .naddr(let naddr):
|
||||||
|
break // TODO: fix
|
||||||
}
|
}
|
||||||
case .filter(let filt):
|
case .filter(let filt):
|
||||||
result(.filter(filt))
|
result(.filter(filt))
|
||||||
|
@ -113,7 +113,7 @@ func is_already_following(contacts: NostrEvent, follow: FollowRef) -> Bool {
|
|||||||
case let (.pubkey(pk), .pubkey(follow_pk)):
|
case let (.pubkey(pk), .pubkey(follow_pk)):
|
||||||
return pk == follow_pk
|
return pk == follow_pk
|
||||||
case (.hashtag, .pubkey), (.pubkey, .hashtag),
|
case (.hashtag, .pubkey), (.pubkey, .hashtag),
|
||||||
(.event, _), (.quote, _), (.param, _):
|
(.event, _), (.quote, _), (.param, _), (.naddr, _):
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,8 @@ import Foundation
|
|||||||
enum MentionType: AsciiCharacter, TagKey {
|
enum MentionType: AsciiCharacter, TagKey {
|
||||||
case p
|
case p
|
||||||
case e
|
case e
|
||||||
|
case a
|
||||||
|
case r
|
||||||
|
|
||||||
var keychar: AsciiCharacter {
|
var keychar: AsciiCharacter {
|
||||||
self.rawValue
|
self.rawValue
|
||||||
@ -17,21 +19,26 @@ enum MentionType: AsciiCharacter, TagKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum MentionRef: TagKeys, TagConvertible, Equatable, Hashable {
|
enum MentionRef: TagKeys, TagConvertible, Equatable, Hashable {
|
||||||
case pubkey(Pubkey) // TODO: handle nprofile
|
case pubkey(Pubkey)
|
||||||
case note(NoteId)
|
case note(NoteId)
|
||||||
|
case nevent(NEvent)
|
||||||
|
case nprofile(NProfile)
|
||||||
|
case nrelay(String)
|
||||||
|
case naddr(NAddr)
|
||||||
|
|
||||||
var key: MentionType {
|
var key: MentionType {
|
||||||
switch self {
|
switch self {
|
||||||
case .pubkey: return .p
|
case .pubkey: return .p
|
||||||
case .note: return .e
|
case .note: return .e
|
||||||
|
case .nevent: return .e
|
||||||
|
case .nprofile: return .p
|
||||||
|
case .nrelay: return .r
|
||||||
|
case .naddr: return .a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var bech32: String {
|
var bech32: String {
|
||||||
switch self {
|
return Bech32Object.encode(toBech32Object())
|
||||||
case .pubkey(let pubkey): return bech32_pubkey(pubkey)
|
|
||||||
case .note(let noteId): return bech32_note_id(noteId)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static func from_bech32(str: String) -> MentionRef? {
|
static func from_bech32(str: String) -> MentionRef? {
|
||||||
@ -46,6 +53,10 @@ enum MentionRef: TagKeys, TagConvertible, Equatable, Hashable {
|
|||||||
switch self {
|
switch self {
|
||||||
case .pubkey(let pubkey): return pubkey
|
case .pubkey(let pubkey): return pubkey
|
||||||
case .note: return nil
|
case .note: return nil
|
||||||
|
case .nevent(let nevent): return nevent.author
|
||||||
|
case .nprofile(let nprofile): return nprofile.author
|
||||||
|
case .nrelay: return nil
|
||||||
|
case .naddr: return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,6 +64,10 @@ enum MentionRef: TagKeys, TagConvertible, Equatable, Hashable {
|
|||||||
switch self {
|
switch self {
|
||||||
case .pubkey(let pubkey): return ["p", pubkey.hex()]
|
case .pubkey(let pubkey): return ["p", pubkey.hex()]
|
||||||
case .note(let noteId): return ["e", noteId.hex()]
|
case .note(let noteId): return ["e", noteId.hex()]
|
||||||
|
case .nevent(let nevent): return ["e", nevent.noteid.hex()]
|
||||||
|
case .nprofile(let nprofile): return ["p", nprofile.author.hex()]
|
||||||
|
case .nrelay(let url): return ["r", url]
|
||||||
|
case .naddr(let naddr): return ["a", naddr.kind.description + ":" + naddr.author.hex() + ":" + naddr.identifier.string()]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,14 +79,45 @@ enum MentionRef: TagKeys, TagConvertible, Equatable, Hashable {
|
|||||||
guard let t0 = i.next(),
|
guard let t0 = i.next(),
|
||||||
let chr = t0.single_char,
|
let chr = t0.single_char,
|
||||||
let mention_type = MentionType(rawValue: chr),
|
let mention_type = MentionType(rawValue: chr),
|
||||||
let id = i.next()?.id()
|
let element = i.next()
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch mention_type {
|
switch mention_type {
|
||||||
case .p: return .pubkey(Pubkey(id))
|
case .p:
|
||||||
case .e: return .note(NoteId(id))
|
guard let data = element.id() else { return nil }
|
||||||
|
return .pubkey(Pubkey(data))
|
||||||
|
case .e:
|
||||||
|
guard let data = element.id() else { return nil }
|
||||||
|
return .note(NoteId(data))
|
||||||
|
case .a:
|
||||||
|
let str = element.string()
|
||||||
|
let data = str.split(separator: ":")
|
||||||
|
if(data.count != 3) { return nil }
|
||||||
|
|
||||||
|
guard let pubkey = Pubkey(hex: String(data[1])) else { return nil }
|
||||||
|
guard let kind = UInt32(data[0]) else { return nil }
|
||||||
|
|
||||||
|
return .naddr(NAddr(identifier: String(data[2]), author: pubkey, relays: [], kind: kind))
|
||||||
|
case .r: return .nrelay(element.string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toBech32Object() -> Bech32Object {
|
||||||
|
switch self {
|
||||||
|
case .pubkey(let pk):
|
||||||
|
return .npub(pk)
|
||||||
|
case .note(let noteid):
|
||||||
|
return .note(noteid)
|
||||||
|
case .naddr(let naddr):
|
||||||
|
return .naddr(naddr)
|
||||||
|
case .nevent(let nevent):
|
||||||
|
return .nevent(nevent)
|
||||||
|
case .nprofile(let nprofile):
|
||||||
|
return .nprofile(nprofile)
|
||||||
|
case .nrelay(let url):
|
||||||
|
return .nrelay(url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -251,4 +297,3 @@ func post_to_event(post: NostrPost, keypair: FullKeypair) -> NostrEvent? {
|
|||||||
.joined(separator: "")
|
.joined(separator: "")
|
||||||
return NostrEvent(content: content, keypair: keypair.to_keypair(), kind: post.kind.rawValue, tags: post_tags.tags)
|
return NostrEvent(content: content, keypair: keypair.to_keypair(), kind: post.kind.rawValue, tags: post_tags.tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,26 +182,31 @@ func attributed_string_attach_icon(_ astr: inout AttributedString, img: UIImage)
|
|||||||
astr.append(wrapped)
|
astr.append(wrapped)
|
||||||
}
|
}
|
||||||
|
|
||||||
func mention_str(_ m: Mention<MentionRef>, profiles: Profiles) -> CompatibleText {
|
func getDisplayName(pk: Pubkey, profiles: Profiles) -> String {
|
||||||
switch m.ref {
|
let profile_txn = profiles.lookup(id: pk)
|
||||||
case .pubkey(let pk):
|
let profile = profile_txn?.unsafeUnownedValue
|
||||||
let npub = bech32_pubkey(pk)
|
return Profile.displayName(profile: profile, pubkey: pk).username.truncate(maxLength: 50)
|
||||||
let profile_txn = profiles.lookup(id: pk)
|
}
|
||||||
let profile = profile_txn?.unsafeUnownedValue
|
|
||||||
let disp = Profile.displayName(profile: profile, pubkey: pk).username.truncate(maxLength: 50)
|
|
||||||
var attributedString = AttributedString(stringLiteral: "@\(disp)")
|
|
||||||
attributedString.link = URL(string: "damus:nostr:\(npub)")
|
|
||||||
attributedString.foregroundColor = DamusColors.purple
|
|
||||||
|
|
||||||
return CompatibleText(attributed: attributedString)
|
|
||||||
case .note(let note_id):
|
|
||||||
let bevid = bech32_note_id(note_id)
|
|
||||||
var attributedString = AttributedString(stringLiteral: "@\(abbrev_pubkey(bevid))")
|
|
||||||
attributedString.link = URL(string: "damus:nostr:\(bevid)")
|
|
||||||
attributedString.foregroundColor = DamusColors.purple
|
|
||||||
|
|
||||||
return CompatibleText(attributed: attributedString)
|
func mention_str(_ m: Mention<MentionRef>, profiles: Profiles) -> CompatibleText {
|
||||||
}
|
let bech32String = Bech32Object.encode(m.ref.toBech32Object())
|
||||||
|
|
||||||
|
let attributedStringLiteral: String = {
|
||||||
|
switch m.ref {
|
||||||
|
case .pubkey(let pk): return getDisplayName(pk: pk, profiles: profiles)
|
||||||
|
case .note: return "@\(abbrev_pubkey(bech32String))"
|
||||||
|
case .nevent: return "@\(abbrev_pubkey(bech32String))"
|
||||||
|
case .nprofile(let nprofile): return getDisplayName(pk: nprofile.author, profiles: profiles)
|
||||||
|
case .nrelay(let url): return url
|
||||||
|
case .naddr: return "@\(abbrev_pubkey(bech32String))"
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
var attributedString = AttributedString(stringLiteral: attributedStringLiteral)
|
||||||
|
attributedString.link = URL(string: "damus:nostr:\(bech32String)")
|
||||||
|
attributedString.foregroundColor = DamusColors.purple
|
||||||
|
|
||||||
|
return CompatibleText(attributed: attributedString)
|
||||||
}
|
}
|
||||||
|
|
||||||
// trim suffix whitespace and newlines
|
// trim suffix whitespace and newlines
|
||||||
|
@ -121,7 +121,8 @@ enum RefId: TagConvertible, TagKeys, Equatable, Hashable {
|
|||||||
case quote(QuoteId)
|
case quote(QuoteId)
|
||||||
case hashtag(TagElem)
|
case hashtag(TagElem)
|
||||||
case param(TagElem)
|
case param(TagElem)
|
||||||
|
case naddr(NAddr)
|
||||||
|
|
||||||
var key: RefKey {
|
var key: RefKey {
|
||||||
switch self {
|
switch self {
|
||||||
case .event: return .e
|
case .event: return .e
|
||||||
@ -129,11 +130,12 @@ enum RefId: TagConvertible, TagKeys, Equatable, Hashable {
|
|||||||
case .quote: return .q
|
case .quote: return .q
|
||||||
case .hashtag: return .t
|
case .hashtag: return .t
|
||||||
case .param: return .d
|
case .param: return .d
|
||||||
|
case .naddr: return .a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum RefKey: AsciiCharacter, TagKey, CustomStringConvertible {
|
enum RefKey: AsciiCharacter, TagKey, CustomStringConvertible {
|
||||||
case e, p, t, d, q
|
case e, p, t, d, q, a
|
||||||
|
|
||||||
var keychar: AsciiCharacter {
|
var keychar: AsciiCharacter {
|
||||||
self.rawValue
|
self.rawValue
|
||||||
@ -155,6 +157,8 @@ enum RefId: TagConvertible, TagKeys, Equatable, Hashable {
|
|||||||
case .quote(let quote): return quote.hex()
|
case .quote(let quote): return quote.hex()
|
||||||
case .hashtag(let string): return string.string()
|
case .hashtag(let string): return string.string()
|
||||||
case .param(let string): return string.string()
|
case .param(let string): return string.string()
|
||||||
|
case .naddr(let naddr):
|
||||||
|
return naddr.kind.description + ":" + naddr.author.hex() + ":" + naddr.identifier
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,6 +178,7 @@ enum RefId: TagConvertible, TagKeys, Equatable, Hashable {
|
|||||||
case .q: return t1.id().map({ .quote(QuoteId($0)) })
|
case .q: return t1.id().map({ .quote(QuoteId($0)) })
|
||||||
case .t: return .hashtag(t1)
|
case .t: return .hashtag(t1)
|
||||||
case .d: return .param(t1)
|
case .d: return .param(t1)
|
||||||
|
case .a: return .naddr(NAddr(identifier: "", author: Pubkey(Data()), relays: [], kind: 0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,47 +150,18 @@ fileprivate extension Block {
|
|||||||
self = .invoice(Invoice(description: description, amount: amount, string: invstr, expiry: b11.expiry, payment_hash: payment_hash, created_at: created_at))
|
self = .invoice(Invoice(description: description, amount: amount, string: invstr, expiry: b11.expiry, payment_hash: payment_hash, created_at: created_at))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate extension Block {
|
fileprivate extension Block {
|
||||||
/// Failable initializer for the C-backed type `mention_bech32_block_t`. This initializer will inspect the
|
/// Failable initializer for the C-backed type `mention_bech32_block_t`. This initializer will inspect the
|
||||||
/// bech32 type code and build the appropriate enum type.
|
/// bech32 type code and build the appropriate enum type.
|
||||||
init?(bech32 b: mention_bech32_block_t) {
|
init?(bech32 b: mention_bech32_block_t) {
|
||||||
switch b.bech32.type {
|
guard let decoded = decodeCBech32(b.bech32) else {
|
||||||
case NOSTR_BECH32_NOTE:
|
|
||||||
let note = b.bech32.data.note;
|
|
||||||
let note_id = NoteId(Data(bytes: note.event_id, count: 32))
|
|
||||||
self = .mention(.any(.note(note_id)))
|
|
||||||
case NOSTR_BECH32_NEVENT:
|
|
||||||
let nevent = b.bech32.data.nevent;
|
|
||||||
let note_id = NoteId(Data(bytes: nevent.event_id, count: 32))
|
|
||||||
self = .mention(.any(.note(note_id)))
|
|
||||||
case NOSTR_BECH32_NPUB:
|
|
||||||
let npub = b.bech32.data.npub
|
|
||||||
let pubkey = Pubkey(Data(bytes: npub.pubkey, count: 32))
|
|
||||||
self = .mention(.any(.pubkey(pubkey)))
|
|
||||||
case NOSTR_BECH32_NSEC:
|
|
||||||
let nsec = b.bech32.data.nsec
|
|
||||||
let privkey = Privkey(Data(bytes: nsec.nsec, count: 32))
|
|
||||||
guard let pubkey = privkey_to_pubkey(privkey: privkey) else { return nil }
|
|
||||||
self = .mention(.any(.pubkey(pubkey)))
|
|
||||||
case NOSTR_BECH32_NPROFILE:
|
|
||||||
let nprofile = b.bech32.data.nprofile
|
|
||||||
let pubkey = Pubkey(Data(bytes: nprofile.pubkey, count: 32))
|
|
||||||
self = .mention(.any(.pubkey(pubkey)))
|
|
||||||
case NOSTR_BECH32_NRELAY:
|
|
||||||
let nrelay = b.bech32.data.nrelay
|
|
||||||
guard let relay_str = String(nrelay.relay) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
self = .relay(relay_str)
|
|
||||||
case NOSTR_BECH32_NADDR:
|
|
||||||
// TODO: wtf do I do with this
|
|
||||||
guard let naddr = String(b.str) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
self = .text("nostr:" + naddr)
|
|
||||||
default:
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
guard let ref = decoded.toMentionRef() else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
self = .mention(.any(ref))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
extension Block {
|
extension Block {
|
||||||
@ -201,10 +172,7 @@ extension Block {
|
|||||||
return "#[\(idx)]"
|
return "#[\(idx)]"
|
||||||
}
|
}
|
||||||
|
|
||||||
switch m.ref {
|
return "nostr:" + Bech32Object.encode(m.ref.toBech32Object())
|
||||||
case .pubkey(let pk): return "nostr:\(pk.npub)"
|
|
||||||
case .note(let note_id): return "nostr:\(note_id.bech32)"
|
|
||||||
}
|
|
||||||
case .relay(let relay):
|
case .relay(let relay):
|
||||||
return relay
|
return relay
|
||||||
case .text(let txt):
|
case .text(let txt):
|
||||||
|
@ -16,7 +16,7 @@ fileprivate extension String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct NEvent : Equatable {
|
struct NEvent : Equatable, Hashable {
|
||||||
let noteid: NoteId
|
let noteid: NoteId
|
||||||
let relays: [String]
|
let relays: [String]
|
||||||
let author: Pubkey?
|
let author: Pubkey?
|
||||||
@ -49,12 +49,12 @@ struct NEvent : Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct NProfile : Equatable {
|
struct NProfile : Equatable, Hashable {
|
||||||
let author: Pubkey
|
let author: Pubkey
|
||||||
let relays: [String]
|
let relays: [String]
|
||||||
}
|
}
|
||||||
|
|
||||||
struct NAddr : Equatable {
|
struct NAddr : Equatable, Hashable {
|
||||||
let identifier: String
|
let identifier: String
|
||||||
let author: Pubkey
|
let author: Pubkey
|
||||||
let relays: [String]
|
let relays: [String]
|
||||||
@ -107,6 +107,29 @@ enum Bech32Object : Equatable {
|
|||||||
return bech32_encode(hrp: "nscript", data)
|
return bech32_encode(hrp: "nscript", data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toMentionRef() -> MentionRef? {
|
||||||
|
switch self {
|
||||||
|
case .nsec(let privkey):
|
||||||
|
guard let pubkey = privkey_to_pubkey(privkey: privkey) else { return nil }
|
||||||
|
return .pubkey(pubkey)
|
||||||
|
case .npub(let pubkey):
|
||||||
|
return .pubkey(pubkey)
|
||||||
|
case .note(let noteid):
|
||||||
|
return .note(noteid)
|
||||||
|
case .nscript(_):
|
||||||
|
return nil
|
||||||
|
case .nevent(let nevent):
|
||||||
|
return .nevent(nevent)
|
||||||
|
case .nprofile(let nprofile):
|
||||||
|
return .nprofile(nprofile)
|
||||||
|
case .nrelay(let relayURL):
|
||||||
|
return .nrelay(relayURL)
|
||||||
|
case .naddr(let naddr):
|
||||||
|
return .naddr(naddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeCBech32(_ b: nostr_bech32_t) -> Bech32Object? {
|
func decodeCBech32(_ b: nostr_bech32_t) -> Bech32Object? {
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import XCTest
|
import XCTest
|
||||||
|
import SwiftUI
|
||||||
@testable import damus
|
@testable import damus
|
||||||
|
|
||||||
class NoteContentViewTests: XCTestCase {
|
class NoteContentViewTests: XCTestCase {
|
||||||
@ -35,5 +36,101 @@ class NoteContentViewTests: XCTestCase {
|
|||||||
|
|
||||||
XCTAssertTrue((parsed.blocks[0].asURL != nil), "NoteContentView does not correctly parse an image block when url in JSON content contains optional escaped slashes.")
|
XCTAssertTrue((parsed.blocks[0].asURL != nil), "NoteContentView does not correctly parse an image block when url in JSON content contains optional escaped slashes.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testMentionStr_Pubkey_ContainsAbbreviated() throws {
|
||||||
|
let compatibleText = createCompatibleText(test_pubkey.npub)
|
||||||
|
|
||||||
|
assertCompatibleTextHasExpectedString(compatibleText: compatibleText, expected: "17ldvg64:nq5mhr77")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMentionStr_Pubkey_ContainsFullBech32() {
|
||||||
|
let compatableText = createCompatibleText(test_pubkey.npub)
|
||||||
|
|
||||||
|
assertCompatibleTextHasExpectedString(compatibleText: compatableText, expected: test_pubkey.npub)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMentionStr_Nprofile_ContainsAbbreviated() throws {
|
||||||
|
let compatibleText = createCompatibleText("nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p")
|
||||||
|
|
||||||
|
assertCompatibleTextHasExpectedString(compatibleText: compatibleText, expected: "180cvv07:wsyjh6w6")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMentionStr_Nprofile_ContainsFullBech32() throws {
|
||||||
|
let bech = "nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p"
|
||||||
|
let compatibleText = createCompatibleText(bech)
|
||||||
|
|
||||||
|
assertCompatibleTextHasExpectedString(compatibleText: compatibleText, expected: bech)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMentionStr_Note_ContainsAbbreviated() {
|
||||||
|
let compatibleText = createCompatibleText(test_note.id.bech32)
|
||||||
|
|
||||||
|
assertCompatibleTextHasExpectedString(compatibleText: compatibleText, expected: "note1qqq:qqn2l0z3")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMentionStr_Note_ContainsFullBech32() {
|
||||||
|
let compatableText = createCompatibleText(test_note.id.bech32)
|
||||||
|
|
||||||
|
assertCompatibleTextHasExpectedString(compatibleText: compatableText, expected: test_note.id.bech32)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMentionStr_Nevent_ContainsAbbreviated() {
|
||||||
|
let bech = "nevent1qqstna2yrezu5wghjvswqqculvvwxsrcvu7uc0f78gan4xqhvz49d9spr3mhxue69uhkummnw3ez6un9d3shjtn4de6x2argwghx6egpr4mhxue69uhkummnw3ez6ur4vgh8wetvd3hhyer9wghxuet5nxnepm"
|
||||||
|
let compatibleText = createCompatibleText(bech)
|
||||||
|
|
||||||
|
assertCompatibleTextHasExpectedString(compatibleText: compatibleText, expected: "nevent1q:t5nxnepm")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMentionStr_Nevent_ContainsFullBech32() throws {
|
||||||
|
let bech = "nevent1qqstna2yrezu5wghjvswqqculvvwxsrcvu7uc0f78gan4xqhvz49d9spr3mhxue69uhkummnw3ez6un9d3shjtn4de6x2argwghx6egpr4mhxue69uhkummnw3ez6ur4vgh8wetvd3hhyer9wghxuet5nxnepm"
|
||||||
|
let compatibleText = createCompatibleText(bech)
|
||||||
|
|
||||||
|
assertCompatibleTextHasExpectedString(compatibleText: compatibleText, expected: bech)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMentionStr_Nrelay_ContainsAbbreviated() {
|
||||||
|
let bech = "nrelay1qqt8wumn8ghj7un9d3shjtnwdaehgu3wvfskueq4r295t"
|
||||||
|
let compatibleText = createCompatibleText(bech)
|
||||||
|
|
||||||
|
assertCompatibleTextHasExpectedString(compatibleText: compatibleText, expected: "wss://relay.nostr.band")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMentionStr_Nrelay_ContainsFullBech32() {
|
||||||
|
let bech = "nrelay1qqt8wumn8ghj7un9d3shjtnwdaehgu3wvfskueq4r295t"
|
||||||
|
let compatibleText = createCompatibleText(bech)
|
||||||
|
|
||||||
|
assertCompatibleTextHasExpectedString(compatibleText: compatibleText, expected: bech)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMentionStr_Naddr_ContainsAbbreviated() {
|
||||||
|
let bech = "naddr1qqxnzdesxqmnxvpexqunzvpcqyt8wumn8ghj7un9d3shjtnwdaehgu3wvfskueqzypve7elhmamff3sr5mgxxms4a0rppkmhmn7504h96pfcdkpplvl2jqcyqqq823cnmhuld"
|
||||||
|
let compatibleText = createCompatibleText(bech)
|
||||||
|
|
||||||
|
assertCompatibleTextHasExpectedString(compatibleText: compatibleText, expected: "naddr1qq:3cnmhuld")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMentionStr_Naddr_ContainsFullBech32() {
|
||||||
|
let bech = "naddr1qqxnzdesxqmnxvpexqunzvpcqyt8wumn8ghj7un9d3shjtnwdaehgu3wvfskueqzypve7elhmamff3sr5mgxxms4a0rppkmhmn7504h96pfcdkpplvl2jqcyqqq823cnmhuld"
|
||||||
|
let compatibleText = createCompatibleText(bech)
|
||||||
|
|
||||||
|
assertCompatibleTextHasExpectedString(compatibleText: compatibleText, expected: bech)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func assertCompatibleTextHasExpectedString(compatibleText: CompatibleText, expected: String) {
|
||||||
|
guard let hasExpected = compatibleText.items.first?.attributed_string()?.description.contains(expected) else {
|
||||||
|
XCTFail()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(hasExpected)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func createCompatibleText(_ bechString: String) -> CompatibleText {
|
||||||
|
guard let mentionRef = Bech32Object.parse(bechString)?.toMentionRef() else {
|
||||||
|
XCTFail("Failed to create MentionRef from Bech32 string")
|
||||||
|
return CompatibleText()
|
||||||
|
}
|
||||||
|
return mention_str(.any(mentionRef), profiles: test_damus_state.profiles)
|
||||||
|
}
|
||||||
|
@ -228,5 +228,47 @@ class damusTests: XCTestCase {
|
|||||||
|
|
||||||
XCTAssertEqual(txt, "there is no mention here")
|
XCTAssertEqual(txt, "there is no mention here")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testTagGeneration_Nevent_ContainsETag() {
|
||||||
|
let ev = createEventFromContentString("nevent1qqstna2yrezu5wghjvswqqculvvwxsrcvu7uc0f78gan4xqhvz49d9spr3mhxue69uhkummnw3ez6un9d3shjtn4de6x2argwghx6egpr4mhxue69uhkummnw3ez6ur4vgh8wetvd3hhyer9wghxuet5nxnepm")
|
||||||
|
|
||||||
|
XCTAssertEqual(ev.tags.count, 1)
|
||||||
|
XCTAssertEqual(ev.tags[0][0].string(), "e")
|
||||||
|
XCTAssertEqual(ev.tags[0][1].string(), "b9f5441e45ca39179320e0031cfb18e34078673dcc3d3e3a3b3a981760aa5696")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTagGeneration_Nprofile_ContainsPTag() {
|
||||||
|
let ev = createEventFromContentString("nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p")
|
||||||
|
|
||||||
|
XCTAssertEqual(ev.tags.count, 1)
|
||||||
|
XCTAssertEqual(ev.tags[0][0].string(), "p")
|
||||||
|
XCTAssertEqual(ev.tags[0][1].string(), "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTagGeneration_Nrelay_ContainsRTag() {
|
||||||
|
let ev = createEventFromContentString("nrelay1qqt8wumn8ghj7un9d3shjtnwdaehgu3wvfskueq4r295t")
|
||||||
|
|
||||||
|
XCTAssertEqual(ev.tags.count, 1)
|
||||||
|
XCTAssertEqual(ev.tags[0][0].string(), "r")
|
||||||
|
XCTAssertEqual(ev.tags[0][1].string(), "wss://relay.nostr.band")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTagGeneration_Naddr_ContainsATag(){
|
||||||
|
let ev = createEventFromContentString("naddr1qqxnzdesxqmnxvpexqunzvpcqyt8wumn8ghj7un9d3shjtnwdaehgu3wvfskueqzypve7elhmamff3sr5mgxxms4a0rppkmhmn7504h96pfcdkpplvl2jqcyqqq823cnmhuld")
|
||||||
|
|
||||||
|
XCTAssertEqual(ev.tags.count, 1)
|
||||||
|
XCTAssertEqual(ev.tags[0][0].string(), "a")
|
||||||
|
XCTAssertEqual(ev.tags[0][1].string(), "30023:599f67f7df7694c603a6d0636e15ebc610db77dcfd47d6e5d05386d821fb3ea9:1700730909108")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func createEventFromContentString(_ content: String) -> NostrEvent {
|
||||||
|
let post = NostrPost(content: content, references: [])
|
||||||
|
guard let ev = post_to_event(post: post, keypair: test_keypair_full) else {
|
||||||
|
XCTFail("Could not create event")
|
||||||
|
return test_note
|
||||||
|
}
|
||||||
|
|
||||||
|
return ev
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user