damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

commit 552bd9cae5a04c1a876d6ea54384491c65c03287
parent b6ea17a0eb6f1f6708b719dc7a262eedb61935cf
Author: William Casarin <jb55@jb55.com>
Date:   Sat, 28 Jan 2023 08:30:06 -0800

Implement NIP-21 URI handling

Changelog-Added: Added nostr: uri handling

Diffstat:
Mdamus.xcodeproj/project.pbxproj | 4++++
Mdamus/Nostr/NostrEvent.swift | 2+-
Mdamus/Nostr/NostrFilter.swift | 2+-
Mdamus/Nostr/NostrLink.swift | 32++++++++++++++++++++++++++++++--
Adamus/Util/Bech32Object.swift | 31+++++++++++++++++++++++++++++++
Mdamus/Util/Lists.swift | 2+-
Mdamus/Views/EventDetailView.swift | 2+-
MdamusTests/damusTests.swift | 9+++++++++
8 files changed, 78 insertions(+), 6 deletions(-)

diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj @@ -170,6 +170,7 @@ 4CF0ABE929844AF100D66079 /* AnyCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABE829844AF100D66079 /* AnyCodable.swift */; }; 4CF0ABEC29844B4700D66079 /* AnyDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABEB29844B4700D66079 /* AnyDecodable.swift */; }; 4CF0ABEE29844B5500D66079 /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABED29844B5500D66079 /* AnyEncodable.swift */; }; + 4CF0ABF029857E9200D66079 /* Bech32Object.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABEF29857E9200D66079 /* Bech32Object.swift */; }; 4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE60CDC295E1C5E00105A1F /* Wallet.swift */; }; 6439E014296790CF0020672B /* ProfileZoomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6439E013296790CF0020672B /* ProfileZoomView.swift */; }; 647D9A8D2968520300A295DE /* SideMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647D9A8C2968520300A295DE /* SideMenuView.swift */; }; @@ -424,6 +425,7 @@ 4CF0ABE829844AF100D66079 /* AnyCodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyCodable.swift; sourceTree = "<group>"; }; 4CF0ABEB29844B4700D66079 /* AnyDecodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyDecodable.swift; sourceTree = "<group>"; }; 4CF0ABED29844B5500D66079 /* AnyEncodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyEncodable.swift; sourceTree = "<group>"; }; + 4CF0ABEF29857E9200D66079 /* Bech32Object.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32Object.swift; sourceTree = "<group>"; }; 4FE60CDC295E1C5E00105A1F /* Wallet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wallet.swift; sourceTree = "<group>"; }; 6439E013296790CF0020672B /* ProfileZoomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileZoomView.swift; sourceTree = "<group>"; }; 647D9A8C2968520300A295DE /* SideMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideMenuView.swift; sourceTree = "<group>"; }; @@ -683,6 +685,7 @@ 64FBD06E296255C400D9D3B2 /* Theme.swift */, 4CB8838529656C8B00DC99E7 /* NIP05.swift */, 4CF0ABD72981980C00D66079 /* Lists.swift */, + 4CF0ABEF29857E9200D66079 /* Bech32Object.swift */, ); path = Util; sourceTree = "<group>"; @@ -1041,6 +1044,7 @@ F7F0BA272978E54D009531F3 /* ParicipantsView.swift in Sources */, 4C0A3F91280F6528000448DE /* ChatView.swift in Sources */, 4CF0ABE32981BC7D00D66079 /* UserView.swift in Sources */, + 4CF0ABF029857E9200D66079 /* Bech32Object.swift in Sources */, 4C216F362870A9A700040376 /* InputDismissKeyboard.swift in Sources */, 4C216F382871EDE300040376 /* DirectMessageModel.swift in Sources */, 4C75EFA627FF87A20006080F /* Nostr.swift in Sources */, diff --git a/damus/Nostr/NostrEvent.swift b/damus/Nostr/NostrEvent.swift @@ -27,7 +27,7 @@ struct KeyEvent { let relay_url: String } -struct ReferencedId: Identifiable, Hashable { +struct ReferencedId: Identifiable, Hashable, Equatable { let ref_id: String let relay_id: String? let key: String diff --git a/damus/Nostr/NostrFilter.swift b/damus/Nostr/NostrFilter.swift @@ -7,7 +7,7 @@ import Foundation -struct NostrFilter: Codable { +struct NostrFilter: Codable, Equatable { var ids: [String]? var kinds: [Int]? var referenced_ids: [String]? diff --git a/damus/Nostr/NostrLink.swift b/damus/Nostr/NostrLink.swift @@ -8,7 +8,7 @@ import Foundation -enum NostrLink { +enum NostrLink: Equatable { case ref(ReferencedId) case filter(NostrFilter) } @@ -101,6 +101,24 @@ func decode_universal_link(_ s: String) -> NostrLink? { return nil } +func decode_nostr_bech32_uri(_ s: String) -> NostrLink? { + guard let obj = Bech32Object.parse(s) else { + return nil + } + + switch obj { + case .nsec(let privkey): + guard let pubkey = privkey_to_pubkey(privkey: privkey) else { + return nil + } + return .ref(ReferencedId(ref_id: pubkey, relay_id: nil, key: "p")) + case .npub(let pubkey): + return .ref(ReferencedId(ref_id: pubkey, relay_id: nil, key: "p")) + case .note(let id): + return .ref(ReferencedId(ref_id: id, relay_id: nil, key: "e")) + } +} + func decode_nostr_uri(_ s: String) -> NostrLink? { if s.starts(with: "https://damus.io/") { return decode_universal_link(s) @@ -122,5 +140,15 @@ func decode_nostr_uri(_ s: String) -> NostrLink? { return .filter(NostrFilter.filter_hashtag([parts[1].lowercased()])) } - return tag_to_refid(parts).map { .ref($0) } + if let rid = tag_to_refid(parts) { + return .ref(rid) + } + + guard parts.count == 1 else { + return nil + } + + let part = parts[0] + + return decode_nostr_bech32_uri(part) } diff --git a/damus/Util/Bech32Object.swift b/damus/Util/Bech32Object.swift @@ -0,0 +1,31 @@ +// +// Bech32Object.swift +// damus +// +// Created by William Casarin on 2023-01-28. +// + +import Foundation + + +enum Bech32Object { + case nsec(String) + case npub(String) + case note(String) + + static func parse(_ str: String) -> Bech32Object? { + guard let decoded = try? bech32_decode(str) else { + return nil + } + + if decoded.hrp == "npub" { + return .npub(hex_encode(decoded.data)) + } else if decoded.hrp == "nsec" { + return .nsec(hex_encode(decoded.data)) + } else if decoded.hrp == "note" { + return .note(hex_encode(decoded.data)) + } + + return nil + } +} diff --git a/damus/Util/Lists.swift b/damus/Util/Lists.swift @@ -24,7 +24,7 @@ func create_or_update_list_event(keypair: FullKeypair, mprev: NostrEvent?, to_ad } } - var tags = [["d", list_name], [list_type, to_add]] + let tags = [["d", list_name], [list_type, to_add]] let ev = NostrEvent(content: "", pubkey: pubkey, kind: 30000, tags: tags) ev.tags = tags diff --git a/damus/Views/EventDetailView.swift b/damus/Views/EventDetailView.swift @@ -71,7 +71,7 @@ struct EventDetailView: View { } toggle_thread_view() } - case .event(let ev, let _): + case .event(let ev, _): EventView(damus: damus, event: ev, has_action_bar: true) .onTapGesture { if thread.initial_event.id == ev.id { diff --git a/damusTests/damusTests.swift b/damusTests/damusTests.swift @@ -79,6 +79,15 @@ class damusTests: XCTestCase { XCTAssertEqual(parsed[1].is_url?.absoluteString, "HTTPS://jb55.COM") } + func testBech32Url() { + let parsed = decode_nostr_uri("nostr:npub1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzsevkk5s") + + let hexpk = "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245" + let expected: NostrLink = .ref(ReferencedId(ref_id: hexpk, relay_id: nil, key: "p")) + + XCTAssertEqual(parsed, expected) + } + func testParseUrl() { let parsed = parse_mentions(content: "a https://jb55.com b", tags: [])