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:
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: [])