[PATCH damus] nip10: add marker nip10 support when reading notes

0 views
Skip to first unread message

William Casarin

unread,
Apr 25, 2024, 5:07:50 PMApr 25
to pat...@damus.io, William Casarin, Daniel D’Aquino
We still need to add these when writing notes

Changelog-Added: Add marker nip10 support when reading notes
Signed-off-by: William Casarin <jb...@jb55.com>
---
damus.xcodeproj/project.pbxproj | 4 +
damus/ContentParsing.swift | 32 +++---
damus/Models/EventRef.swift | 9 ++
damusTests/NIP10Tests.swift | 171 ++++++++++++++++++++++++++++++++
4 files changed, 203 insertions(+), 13 deletions(-)
create mode 100644 damusTests/NIP10Tests.swift

diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj
index b79b8a611cbf..cb96aa938c86 100644
--- a/damus.xcodeproj/project.pbxproj
+++ b/damus.xcodeproj/project.pbxproj
@@ -98,6 +98,7 @@
4C2B10282A7B0F5C008AA43E /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C2B10272A7B0F5C008AA43E /* Log.swift */; };
4C2B7BF22A71B6540049DEE7 /* Id.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C2B7BF12A71B6540049DEE7 /* Id.swift */; };
4C2CDDF7299D4A5E00879FD5 /* Debouncer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C2CDDF6299D4A5E00879FD5 /* Debouncer.swift */; };
+ 4C2D34412BDAF1B300F9FB44 /* NIP10Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C2D34402BDAF1B300F9FB44 /* NIP10Tests.swift */; };
4C30AC7229A5677A00E2BD5A /* NotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C30AC7129A5677A00E2BD5A /* NotificationsView.swift */; };
4C30AC7429A5680900E2BD5A /* EventGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C30AC7329A5680900E2BD5A /* EventGroupView.swift */; };
4C30AC7629A5770900E2BD5A /* NotificationItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C30AC7529A5770900E2BD5A /* NotificationItemView.swift */; };
@@ -885,6 +886,7 @@
4C2B10272A7B0F5C008AA43E /* Log.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = "<group>"; };
4C2B7BF12A71B6540049DEE7 /* Id.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Id.swift; sourceTree = "<group>"; };
4C2CDDF6299D4A5E00879FD5 /* Debouncer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Debouncer.swift; sourceTree = "<group>"; };
+ 4C2D34402BDAF1B300F9FB44 /* NIP10Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NIP10Tests.swift; sourceTree = "<group>"; };
4C30AC7129A5677A00E2BD5A /* NotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsView.swift; sourceTree = "<group>"; };
4C30AC7329A5680900E2BD5A /* EventGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventGroupView.swift; sourceTree = "<group>"; };
4C30AC7529A5770900E2BD5A /* NotificationItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationItemView.swift; sourceTree = "<group>"; };
@@ -2555,6 +2557,7 @@
D7CBD1D52B8D509800BFD889 /* DamusPurpleImpendingExpirationTests.swift */,
D72927AC2BAB515C00F93E90 /* RelayURLTests.swift */,
D7831AF72BBE11E2005DA780 /* VideoCacheTests.swift */,
+ 4C2D34402BDAF1B300F9FB44 /* NIP10Tests.swift */,
);
path = damusTests;
sourceTree = "<group>";
@@ -3517,6 +3520,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 4C2D34412BDAF1B300F9FB44 /* NIP10Tests.swift in Sources */,
4C684A572A7FFAE6005E6031 /* UrlTests.swift in Sources */,
3A90B1832A4EA3C600000D94 /* UserSearchCacheTests.swift in Sources */,
4C9B0DEE2A65A75F00CBDA21 /* AttrStringTestExtensions.swift in Sources */,
diff --git a/damus/ContentParsing.swift b/damus/ContentParsing.swift
index 96de6162305f..0b7f03d7fb37 100644
--- a/damus/ContentParsing.swift
+++ b/damus/ContentParsing.swift
@@ -76,27 +76,33 @@ func interpret_event_refs_ndb(blocks: [Block], tags: TagsSequence) -> [EventRef]
}

func interp_event_refs_without_mentions_ndb(_ ev_tags: References<NoteRef>) -> [EventRef] {
-
- var count = 0
var evrefs: [EventRef] = []
var first: Bool = true
- var first_ref: NoteRef? = nil
+ var root_id: NoteRef? = nil

for ref in ev_tags {
- if first {
- first_ref = ref
- evrefs.append(.thread_id(ref))
- first = false
+ if let marker = ref.marker {
+ switch marker {
+ case .root: root_id = ref
+ case .reply: evrefs.append(.reply(ref))
+ case .mention: evrefs.append(.mention(.noteref(ref)))
+ }
} else {
-
- evrefs.append(.reply(ref))
+ if first {
+ root_id = ref
+ first = false
+ } else {
+ evrefs.append(.reply(ref))
+ }
}
- count += 1
}

- if let first_ref, count == 1 {
- let r = first_ref
- return [.reply_to_root(r)]
+ if let root_id {
+ if evrefs.count == 0 {
+ return [.reply_to_root(root_id)]
+ } else {
+ evrefs.append(.thread_id(root_id))
+ }
}

return evrefs
diff --git a/damus/Models/EventRef.swift b/damus/Models/EventRef.swift
index 50d9b0a333a7..12c36df6e707 100644
--- a/damus/Models/EventRef.swift
+++ b/damus/Models/EventRef.swift
@@ -13,6 +13,15 @@ enum EventRef: Equatable {
case reply(NoteRef)
case reply_to_root(NoteRef)

+ var note_ref: NoteRef {
+ switch self {
+ case .mention(let mnref): return mnref.ref
+ case .thread_id(let ref): return ref
+ case .reply(let ref): return ref
+ case .reply_to_root(let ref): return ref
+ }
+ }
+
var is_mention: NoteRef? {
if case .mention(let m) = self { return m.ref }
return nil
diff --git a/damusTests/NIP10Tests.swift b/damusTests/NIP10Tests.swift
new file mode 100644
index 000000000000..7a112b8f098c
--- /dev/null
+++ b/damusTests/NIP10Tests.swift
@@ -0,0 +1,171 @@
+//
+// NIP10Tests.swift
+// damusTests
+//
+// Created by William Casarin on 2024-04-25.
+//
+
+import XCTest
+@testable import damus
+
+final class NIP10Tests: XCTestCase {
+
+ override func setUpWithError() throws {
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+ }
+
+ override func tearDownWithError() throws {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ }
+
+ func testExample() throws {
+ // This is an example of a functional test case.
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
+ // Any test you write for XCTest can be annotated as throws and async.
+ // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
+ // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
+ }
+
+ func test_new_nip10() {
+ let root_note_id_hex = "7c7d37bc8c04d2ec65cbc7d9275253e6b5cc34b5d10439f158194a3feefa8d52"
+ let direct_reply_hex = "7c7d37bc8c04d2ec65cbc7d9275253e6b5cc34b5d10439f158194a3feefa8d51"
+ let reply_hex = "7c7d37bc8c04d2ec65cbc7d9275253e6b5cc34b5d10439f158194a3feefa8d53"
+
+ let tags = [
+ ["e", direct_reply_hex, "", "reply"],
+ ["e", root_note_id_hex, "", "root"],
+ ["e", reply_hex, "", "reply"],
+ ["e", "7c7d37bc8c04d2ec65cbc7d9275253e6b5cc34b5d10439f158194a3feefa8d54", "", "mention"],
+ ]
+
+ let root_note_id = NoteId(hex: root_note_id_hex)!
+ let direct_reply_id = NoteId(hex: direct_reply_hex)!
+ let reply_id = NoteId(hex: reply_hex)!
+
+ let note = NdbNote(content: "hi", keypair: test_keypair, kind: 1, tags: tags)!
+ let refs = interp_event_refs_without_mentions_ndb(note.referenced_noterefs)
+
+ XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
+ if let note_id = r.is_thread_id?.note_id { xs.append(note_id) }
+ }), [root_note_id])
+
+ XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
+ if let note_id = r.is_direct_reply?.note_id { xs.append(note_id) }
+ }), [direct_reply_id, reply_id])
+
+ XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
+ if let note_id = r.is_reply?.note_id { xs.append(note_id) }
+ }), [direct_reply_id, reply_id])
+ }
+
+ func test_repost_root() {
+ let mention_hex = "7c7d37bc8c04d2ec65cbc7d9275253e6b5cc34b5d10439f158194a3feefa8d52"
+ let tags = [
+ ["e", mention_hex, "", "mention"],
+ ]
+
+ let mention_id = NoteId(hex: mention_hex)!
+ let note = NdbNote(content: "hi", keypair: test_keypair, kind: 1, tags: tags)!
+ let refs = interp_event_refs_without_mentions_ndb(note.referenced_noterefs)
+
+ XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
+ if let note_id = r.is_thread_id?.note_id { xs.append(note_id) }
+ }), [])
+
+ XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
+ if let note_id = r.is_direct_reply?.note_id { xs.append(note_id) }
+ }), [])
+
+ XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
+ if let note_id = r.is_reply?.note_id { xs.append(note_id) }
+ }), [])
+ }
+
+ func test_direct_reply_old_nip10() {
+ let root_note_id_hex = "7c7d37bc8c04d2ec65cbc7d9275253e6b5cc34b5d10439f158194a3feefa8d52"
+ let tags = [
+ ["e", root_note_id_hex],
+ ]
+
+ let root_note_id = NoteId(hex: root_note_id_hex)!
+
+ let note = NdbNote(content: "hi", keypair: test_keypair, kind: 1, tags: tags)!
+ let refs = interp_event_refs_without_mentions_ndb(note.referenced_noterefs)
+
+ XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
+ if let note_id = r.is_thread_id?.note_id { xs.append(note_id) }
+ }), [root_note_id])
+
+ XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
+ if let note_id = r.is_direct_reply?.note_id { xs.append(note_id) }
+ }), [root_note_id])
+
+ XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
+ if let note_id = r.is_reply?.note_id { xs.append(note_id) }
+ }), [root_note_id])
+ }
+
+ func test_direct_reply_new_nip10() {
+ let root_note_id_hex = "7c7d37bc8c04d2ec65cbc7d9275253e6b5cc34b5d10439f158194a3feefa8d52"
+ let tags = [
+ ["e", root_note_id_hex, "", "root"],
+ ]
+
+ let root_note_id = NoteId(hex: root_note_id_hex)!
+
+ let note = NdbNote(content: "hi", keypair: test_keypair, kind: 1, tags: tags)!
+ let refs = interp_event_refs_without_mentions_ndb(note.referenced_noterefs)
+
+ XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
+ if let note_id = r.is_thread_id?.note_id { xs.append(note_id) }
+ }), [root_note_id])
+
+ XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
+ if let note_id = r.is_direct_reply?.note_id { xs.append(note_id) }
+ }), [root_note_id])
+
+ XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
+ if let note_id = r.is_reply?.note_id { xs.append(note_id) }
+ }), [root_note_id])
+ }
+
+ func test_deprecated_nip10() {
+ let root_note_id_hex = "7c7d37bc8c04d2ec65cbc7d9275253e6b5cc34b5d10439f158194a3feefa8d52"
+ let direct_reply_hex = "7c7d37bc8c04d2ec65cbc7d9275253e6b5cc34b5d10439f158194a3feefa8d51"
+ let reply_hex = "7c7d37bc8c04d2ec65cbc7d9275253e6b5cc34b5d10439f158194a3feefa8d53"
+ let tags = [
+ ["e", root_note_id_hex],
+ ["e", direct_reply_hex],
+ ["e", reply_hex],
+ ]
+
+ let root_note_id = NoteId(hex: root_note_id_hex)!
+ let direct_reply_id = NoteId(hex: direct_reply_hex)!
+ let reply_id = NoteId(hex: reply_hex)!
+
+ let note = NdbNote(content: "hi", keypair: test_keypair, kind: 1, tags: tags)!
+ let refs = interp_event_refs_without_mentions_ndb(note.referenced_noterefs)
+
+ XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
+ if let note_id = r.is_thread_id?.note_id { xs.append(note_id) }
+ }), [root_note_id])
+
+ XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
+ if let note_id = r.is_direct_reply?.note_id { xs.append(note_id) }
+ }), [direct_reply_id, reply_id])
+
+ XCTAssertEqual(refs.reduce(into: Array<NoteId>(), { xs, r in
+ if let note_id = r.is_reply?.note_id { xs.append(note_id) }
+ }), [direct_reply_id, reply_id])
+ }
+
+
+
+ func testPerformanceExample() throws {
+ // This is an example of a performance test case.
+ self.measure {
+ // Put the code you want to measure the time of here.
+ }
+ }
+
+}

base-commit: dc0a09d6f5dc86dda0aceba57c44c4e1fb855a1f
--
2.41.0

Daniel D'Aquino

unread,
May 8, 2024, 7:55:30 PMMay 8
to William Casarin, sembene_truestar via patches
Hi Will, I did some testing on this:

========
Testing
========

Procedure:
1. Go through the notifications tab and try to navigate within several different threads (Go to different threads, Navigate to previous replies, to root note, etc for 10–15 minutes)
2. Go to the home feed and try to navigate a few threads
3. Compare new version with a baseline

Test conditions:
Damus: Tip of 1.8 release branch (cd9b4672)
Device: iPhone 13 Mini
iOS: 17.4.1

Baseline check conditions:
Damus: Tip of 1.8 release branch~2 (b4b5af69)
Device: iPhone 13 Mini
iOS: 17.4.1

Results: No new issues encountered, but I also did not notice much difference in the experience between the new version and the previous one.
========


I will use the tip of 1.8 as my daily driver and see if I encounter any issues with this. But are there any specific threads or things I can check where I can better test these changes?









Reply all
Reply to author
Forward
0 new messages