commit 4db06b015c37ff0abe2b6d71b1757d739097d389
parent 0ad17c05fe1f5fb41ab4e9b0b1901041273d4ddb
Author: William Casarin <jb55@jb55.com>
Date: Sat, 7 May 2022 17:48:00 -0700
event mentions working
Signed-off-by: William Casarin <jb55@jb55.com>
Diffstat:
10 files changed, 174 insertions(+), 26 deletions(-)
diff --git a/damus/ContentView.swift b/damus/ContentView.swift
@@ -59,7 +59,9 @@ struct ContentView: View {
@State var friend_events: [NostrEvent] = []
@State var notifications: [NostrEvent] = []
@State var active_profile: String? = nil
+ @State var active_event_id: String? = nil
@State var profile_open: Bool = false
+ @State var thread_open: Bool = false
// connect retry timer
let timer = Timer.publish(every: 60, on: .main, in: .common).autoconnect()
@@ -151,6 +153,9 @@ struct ContentView: View {
NavigationLink(destination: MaybeProfileView, isActive: $profile_open) {
EmptyView()
}
+ NavigationLink(destination: MaybeThreadView, isActive: $thread_open) {
+ EmptyView()
+ }
switch selected_timeline {
case .home:
PostingTimelineView
@@ -178,6 +183,18 @@ struct ContentView: View {
.navigationViewStyle(.stack)
}
+ var MaybeThreadView: some View {
+ Group {
+ if let evid = self.active_event_id {
+ let thread_model = ThreadModel(evid: evid, pool: damus!.pool)
+ ThreadView(thread: thread_model, damus: damus!)
+ .environmentObject(profiles)
+ } else {
+ EmptyView()
+ }
+ }
+ }
+
var MaybeProfileView: some View {
Group {
if let pk = self.active_profile {
@@ -226,7 +243,8 @@ struct ContentView: View {
active_profile = ref.ref_id
profile_open = true
} else if ref.key == "e" {
- // TODO open event view
+ active_event_id = ref.ref_id
+ thread_open = true
}
case .filter:
break
diff --git a/damus/Models/Mentions.swift b/damus/Models/Mentions.swift
@@ -129,15 +129,71 @@ func parse_mention(_ p: Parser, tags: [[String]]) -> Mention? {
return Mention(index: digit, type: kind, ref: ref)
}
-func post_to_event(post: NostrPost, privkey: String, pubkey: String) -> NostrEvent {
- let new_ev = NostrEvent(content: post.content, pubkey: pubkey)
- for id in post.references {
- var tag = [id.key, id.ref_id]
- if let relay_id = id.relay_id {
- tag.append(relay_id)
+func find_tag_ref(type: String, id: String, tags: [[String]]) -> Int? {
+ var i: Int = 0
+ for tag in tags {
+ if tag.count >= 2 {
+ if tag[0] == type && tag[1] == id {
+ return i
+ }
}
- new_ev.tags.append(tag)
+ i += i
+ }
+
+ return nil
+}
+
+struct PostTags {
+ let blocks: [Block]
+ let tags: [[String]]
+}
+
+func parse_mention_type(_ c: String) -> MentionType? {
+ if c == "e" {
+ return .event
+ } else if c == "p" {
+ return .pubkey
}
+
+ return nil
+}
+
+/// Convert
+func make_post_tags(post_blocks: [PostBlock], tags: [[String]]) -> PostTags {
+ var new_tags = tags
+ var blocks: [Block] = []
+
+ for post_block in post_blocks {
+ switch post_block {
+ case .ref(let ref):
+ guard let mention_type = parse_mention_type(ref.key) else {
+ continue
+ }
+ if let ind = find_tag_ref(type: ref.key, id: ref.ref_id, tags: tags) {
+ let mention = Mention(index: ind, type: mention_type, ref: ref)
+ let block = Block.mention(mention)
+ blocks.append(block)
+ } else {
+ let ind = new_tags.count
+ new_tags.append(refid_to_tag(ref))
+ let mention = Mention(index: ind, type: mention_type, ref: ref)
+ let block = Block.mention(mention)
+ blocks.append(block)
+ }
+ case .text(let txt):
+ blocks.append(Block.text(txt))
+ }
+ }
+
+ return PostTags(blocks: blocks, tags: new_tags)
+}
+
+func post_to_event(post: NostrPost, privkey: String, pubkey: String) -> NostrEvent {
+ let tags = post.references.map(refid_to_tag)
+ let post_blocks = parse_post_blocks(content: post.content)
+ let post_tags = make_post_tags(post_blocks: post_blocks, tags: tags)
+ let content = render_blocks(blocks: post_tags.blocks)
+ let new_ev = NostrEvent(content: content, pubkey: pubkey, kind: 1, tags: post_tags.tags)
new_ev.calculate_id()
new_ev.sign(privkey: privkey)
return new_ev
diff --git a/damus/Models/ThreadModel.swift b/damus/Models/ThreadModel.swift
@@ -7,24 +7,57 @@
import Foundation
+enum InitialEvent {
+ case event(NostrEvent)
+ case event_id(String)
+
+ var id: String {
+ switch self {
+ case .event(let ev):
+ return ev.id
+ case .event_id(let evid):
+ return evid
+ }
+ }
+}
+
/// manages the lifetime of a thread
class ThreadModel: ObservableObject {
- @Published var event: NostrEvent
+ @Published var initial_event: InitialEvent
@Published var events: [NostrEvent] = []
@Published var event_map: [String: Int] = [:]
var replies: ReplyMap = ReplyMap()
+ var event: NostrEvent? {
+ switch initial_event {
+ case .event(let ev):
+ return ev
+ case .event_id(let evid):
+ for event in events {
+ if event.id == evid {
+ return event
+ }
+ }
+ return nil
+ }
+ }
+
let pool: RelayPool
var sub_id = UUID().description
+
+ init(evid: String, pool: RelayPool) {
+ self.pool = pool
+ self.initial_event = .event_id(evid)
+ }
- init(ev: NostrEvent, pool: RelayPool) {
- self.event = ev
+ init(event: NostrEvent, pool: RelayPool) {
self.pool = pool
+ self.initial_event = .event(event)
}
func unsubscribe() {
self.pool.unsubscribe(sub_id: sub_id)
- print("unsubscribing from thread \(event.id) with sub_id \(sub_id)")
+ print("unsubscribing from thread \(initial_event.id) with sub_id \(sub_id)")
}
func reset_events() {
@@ -50,11 +83,10 @@ class ThreadModel: ObservableObject {
func set_active_event(_ ev: NostrEvent) {
if should_resubscribe(ev) {
unsubscribe()
- self.event = ev
- add_event(ev)
+ self.initial_event = .event(ev)
subscribe()
} else {
- self.event = ev
+ self.initial_event = .event(ev)
if events.count == 0 {
add_event(ev)
}
@@ -68,13 +100,20 @@ class ThreadModel: ObservableObject {
//var likes_filter = NostrFilter.filter_kinds(7])
// TODO: add referenced relays
- ref_events.referenced_ids = event.referenced_ids.map { $0.ref_id }
- ref_events.referenced_ids!.append(event.id)
+ switch self.initial_event {
+ case .event(let ev):
+ ref_events.referenced_ids = ev.referenced_ids.map { $0.ref_id }
+ ref_events.referenced_ids?.append(ev.id)
+ events_filter.ids = ref_events.referenced_ids!
+ events_filter.ids?.append(ev.id)
+ case .event_id(let evid):
+ events_filter.ids = [evid]
+ ref_events.referenced_ids = [evid]
+ }
//likes_filter.ids = ref_events.referenced_ids!
- events_filter.ids = ref_events.referenced_ids!
- print("subscribing to thread \(event.id) with sub_id \(sub_id)")
+ print("subscribing to thread \(initial_event.id) with sub_id \(sub_id)")
pool.register_handler(sub_id: sub_id, handler: handle_event)
pool.send(.subscribe(.init(filters: [ref_events, events_filter], sub_id: sub_id)))
}
@@ -97,6 +136,8 @@ class ThreadModel: ObservableObject {
self.events.append(ev)
self.events = self.events.sorted { $0.created_at < $1.created_at }
+ objectWillChange.send()
+
var i: Int = 0
for ev in events {
self.event_map[ev.id] = i
diff --git a/damus/Nostr/NostrEvent.swift b/damus/Nostr/NostrEvent.swift
@@ -359,6 +359,14 @@ func random_bytes(count: Int) -> Data {
return data
}
+func refid_to_tag(_ ref: ReferencedId) -> [String] {
+ var tag = [ref.key, ref.ref_id]
+ if let relay_id = ref.relay_id {
+ tag.append(relay_id)
+ }
+ return tag
+}
+
func tag_to_refid(_ tag: [String]) -> ReferencedId? {
if tag.count == 0 {
return nil
diff --git a/damus/Views/ChatView.swift b/damus/Views/ChatView.swift
@@ -42,7 +42,7 @@ struct ChatView: View {
}
var is_active: Bool {
- return thread.event.id == event.id
+ return thread.initial_event.id == event.id
}
func prev_reply_is_same() -> String? {
diff --git a/damus/Views/ChatroomView.swift b/damus/Views/ChatroomView.swift
@@ -24,7 +24,7 @@ struct ChatroomView: View {
damus: damus
)
.onTapGesture {
- if thread.event.id == ev.id {
+ if thread.initial_event.id == ev.id {
//dismiss()
toggle_thread_view()
} else {
@@ -37,13 +37,13 @@ struct ChatroomView: View {
}
.onReceive(NotificationCenter.default.publisher(for: .select_quote)) { notif in
let ev = notif.object as! NostrEvent
- if ev.id != thread.event.id {
+ if ev.id != thread.initial_event.id {
thread.set_active_event(ev)
}
scroll_to_event(scroller: scroller, id: ev.id, delay: 0, animate: true, anchor: .top)
}
.onAppear() {
- scroll_to_event(scroller: scroller, id: thread.event.id, delay: 0.3, animate: true, anchor: .bottom)
+ scroll_to_event(scroller: scroller, id: thread.initial_event.id, delay: 0.3, animate: true, anchor: .bottom)
}
}
}
diff --git a/damus/Views/EventDetailView.swift b/damus/Views/EventDetailView.swift
@@ -72,7 +72,7 @@ struct EventDetailView: View {
case .event(let ev, let highlight):
EventView(event: ev, highlight: highlight, has_action_bar: true, damus: damus)
.onTapGesture {
- if thread.event.id == ev.id {
+ if thread.initial_event.id == ev.id {
toggle_thread_view()
} else {
thread.set_active_event(ev)
diff --git a/damus/Views/EventView.swift b/damus/Views/EventView.swift
@@ -102,7 +102,7 @@ struct EventView: View {
}
Button {
- UIPasteboard.general.string = event.id
+ UIPasteboard.general.string = "&" + event.id
} label: {
Label("Copy ID", systemImage: "tag")
}
diff --git a/damus/Views/TimelineView.swift b/damus/Views/TimelineView.swift
@@ -37,7 +37,7 @@ struct TimelineView: View {
.environmentObject(profiles)
*/
- let tv = ThreadView(thread: ThreadModel(ev: ev, pool: damus.pool), damus: damus)
+ let tv = ThreadView(thread: ThreadModel(event: ev, pool: damus.pool), damus: damus)
.environmentObject(profiles)
NavigationLink(destination: tv) {
diff --git a/damusTests/damusTests.swift b/damusTests/damusTests.swift
@@ -48,6 +48,31 @@ class damusTests: XCTestCase {
XCTAssertEqual(parsed.count, 0)
}
+ func testPostWithMentions() throws {
+ let evid = "0000000000000000000000000000000000000000000000000000000000000005"
+ let pk = "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"
+ let content = "this is a @\(pk) mention"
+ let reply_ref = ReferencedId(ref_id: evid, relay_id: nil, key: "e")
+ let post = NostrPost(content: content, references: [reply_ref])
+ let ev = post_to_event(post: post, privkey: evid, pubkey: pk)
+
+ XCTAssertEqual(ev.tags.count, 2)
+ XCTAssertEqual(ev.content, "this is a #[1] mention")
+ }
+
+ func testPostTags() throws {
+ let pk = "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"
+ let content = "this is a @\(pk) mention"
+ let parsed = parse_post_blocks(content: content)
+ let post_tags = make_post_tags(post_blocks: parsed, tags: [])
+
+ XCTAssertEqual(post_tags.blocks.count, 3)
+ XCTAssertEqual(post_tags.tags.count, 1)
+ XCTAssertEqual(post_tags.tags[0].count, 2)
+ XCTAssertEqual(post_tags.tags[0][0], "p")
+ XCTAssertEqual(post_tags.tags[0][1], pk)
+ }
+
func testInvalidPostReference() throws {
let pk = "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e24"
let content = "this is a @\(pk) mention"