diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj index 7341958..77c2375 100644 --- a/damus.xcodeproj/project.pbxproj +++ b/damus.xcodeproj/project.pbxproj @@ -24,6 +24,8 @@ 4C363A94282704FA006E126D /* Post.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363A93282704FA006E126D /* Post.swift */; }; 4C363A962827096D006E126D /* PostBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363A952827096D006E126D /* PostBlock.swift */; }; 4C363A9828283441006E126D /* TestingPrivate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363A9728283441006E126D /* TestingPrivate.swift */; }; + 4C363A9A28283854006E126D /* Reply.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363A9928283854006E126D /* Reply.swift */; }; + 4C363A9C282838B9006E126D /* EventRef.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363A9B282838B9006E126D /* EventRef.swift */; }; 4C3BEFD22819DB9B00B3DE84 /* ProfileModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFD12819DB9B00B3DE84 /* ProfileModel.swift */; }; 4C3BEFD42819DE8F00B3DE84 /* NostrKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFD32819DE8F00B3DE84 /* NostrKind.swift */; }; 4C3BEFD6281D995700B3DE84 /* ActionBarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFD5281D995700B3DE84 /* ActionBarModel.swift */; }; @@ -101,6 +103,8 @@ 4C363A93282704FA006E126D /* Post.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Post.swift; sourceTree = ""; }; 4C363A952827096D006E126D /* PostBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostBlock.swift; sourceTree = ""; }; 4C363A9728283441006E126D /* TestingPrivate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestingPrivate.swift; sourceTree = ""; }; + 4C363A9928283854006E126D /* Reply.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Reply.swift; sourceTree = ""; }; + 4C363A9B282838B9006E126D /* EventRef.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventRef.swift; sourceTree = ""; }; 4C3BEFD12819DB9B00B3DE84 /* ProfileModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileModel.swift; sourceTree = ""; }; 4C3BEFD32819DE8F00B3DE84 /* NostrKind.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostrKind.swift; sourceTree = ""; }; 4C3BEFD5281D995700B3DE84 /* ActionBarModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionBarModel.swift; sourceTree = ""; }; @@ -188,6 +192,8 @@ 4C363A912825FCF2006E126D /* ProfileUpdate.swift */, 4C363A93282704FA006E126D /* Post.swift */, 4C363A952827096D006E126D /* PostBlock.swift */, + 4C363A9928283854006E126D /* Reply.swift */, + 4C363A9B282838B9006E126D /* EventRef.swift */, ); path = Models; sourceTree = ""; @@ -475,6 +481,7 @@ 4C75EFB328049D640006080F /* NostrEvent.swift in Sources */, 4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */, 4C363A8428233689006E126D /* Parser.swift in Sources */, + 4C363A9A28283854006E126D /* Reply.swift in Sources */, 4C3BEFDC281DCE6100B3DE84 /* Liked.swift in Sources */, 4C75EFB128049D510006080F /* NostrResponse.swift in Sources */, 4CEE2AF7280B2DEA00AB5EEF /* ProfileName.swift in Sources */, @@ -488,6 +495,7 @@ 4C363A94282704FA006E126D /* Post.swift in Sources */, 4C363A8828236948006E126D /* BlocksView.swift in Sources */, 4C75EFAF28049D350006080F /* NostrFilter.swift in Sources */, + 4C363A9C282838B9006E126D /* EventRef.swift in Sources */, 4C8682872814DE470026224F /* ProfileView.swift in Sources */, 4CE6DF1627F8DEBF00C66700 /* RelayConnection.swift in Sources */, 4C3BEFD6281D995700B3DE84 /* ActionBarModel.swift in Sources */, diff --git a/damus/Models/EventRef.swift b/damus/Models/EventRef.swift new file mode 100644 index 0000000..475545e --- /dev/null +++ b/damus/Models/EventRef.swift @@ -0,0 +1,153 @@ +// +// EventRef.swift +// damus +// +// Created by William Casarin on 2022-05-08. +// + +import Foundation + +enum EventRef { + case mention(Mention) + case thread_id(ReferencedId) + case reply(ReferencedId) + case reply_to_root(ReferencedId) + + var is_mention: Mention? { + if case .mention(let m) = self { + return m + } + return nil + } + + var is_direct_reply: ReferencedId? { + switch self { + case .mention: + return nil + case .thread_id: + return nil + case .reply(let refid): + return refid + case .reply_to_root(let refid): + return refid + } + } + + var is_thread_id: ReferencedId? { + switch self { + case .mention(let mention): + return nil + case .thread_id(let referencedId): + return referencedId + case .reply(let referencedId): + return nil + case .reply_to_root(let referencedId): + return referencedId + } + } + + var is_reply: ReferencedId? { + switch self { + case .mention: + return nil + case .thread_id: + return nil + case .reply(let refid): + return refid + case .reply_to_root(let refid): + return refid + } + } +} + +func has_any_e_refs(_ tags: [[String]]) -> Bool { + for tag in tags { + if tag.count >= 2 && tag[0] == "e" { + return true + } + } + return false +} + +func build_mention_indices(_ blocks: [Block], type: MentionType) -> Set { + return blocks.reduce(into: []) { acc, block in + switch block { + case .mention(let m): + if m.type == type { + acc.insert(m.index) + } + case .text: + return + } + } +} + +func interp_event_refs_without_mentions(_ refs: [ReferencedId]) -> [EventRef] { + if refs.count == 0 { + return [] + } + + if refs.count == 1 { + return [.reply_to_root(refs[0])] + } + + var evrefs: [EventRef] = [] + var first: Bool = true + for ref in refs { + if first { + evrefs.append(.thread_id(ref)) + first = false + } else { + evrefs.append(.reply(ref)) + } + } + return evrefs +} + +func interp_event_refs_with_mentions(tags: [[String]], mention_indices: Set) -> [EventRef] { + var mentions: [EventRef] = [] + var ev_refs: [ReferencedId] = [] + var i: Int = 0 + + for tag in tags { + if tag.count >= 2 && tag[0] == "e" { + let ref = tag_to_refid(tag)! + if mention_indices.contains(i) { + let mention = Mention(index: i, type: .event, ref: ref) + mentions.append(.mention(mention)) + } else { + ev_refs.append(ref) + } + } + i += 1 + } + + var replies = interp_event_refs_without_mentions(ev_refs) + replies.append(contentsOf: mentions) + return replies +} + +func interpret_event_refs(blocks: [Block], tags: [[String]]) -> [EventRef] { + if tags.count == 0 { + return [] + } + + /// build a set of indices for each event mention + let mention_indices = build_mention_indices(blocks, type: .event) + + /// simpler case with no mentions + if mention_indices.count == 0 { + let ev_refs = get_referenced_ids(tags: tags, key: "e") + return interp_event_refs_without_mentions(ev_refs) + } + + return interp_event_refs_with_mentions(tags: tags, mention_indices: mention_indices) +} + + +func event_is_reply(_ ev: NostrEvent) -> Bool { + return ev.event_refs.contains { evref in + return evref.is_reply != nil + } +} + diff --git a/damus/Models/Reply.swift b/damus/Models/Reply.swift new file mode 100644 index 0000000..518006c --- /dev/null +++ b/damus/Models/Reply.swift @@ -0,0 +1,32 @@ +// +// Reply.swift +// damus +// +// Created by William Casarin on 2022-05-08. +// + +import Foundation + +struct ReplyDesc { + let pubkeys: [String] + let others: Int +} + +func make_reply_description(_ tags: [[String]]) -> ReplyDesc { + var c = 0 + var ns: [String] = [] + var i = tags.count - 1 + + while i >= 0 { + let tag = tags[i] + if tag.count >= 2 && tag[0] == "p" { + c += 1 + if ns.count < 2 { + ns.append(tag[1]) + } + } + i -= 1 + } + + return ReplyDesc(pubkeys: ns, others: c) +} diff --git a/damus/Nostr/NostrEvent.swift b/damus/Nostr/NostrEvent.swift index 0239ccc..b95fad8 100644 --- a/damus/Nostr/NostrEvent.swift +++ b/damus/Nostr/NostrEvent.swift @@ -128,25 +128,6 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible { return get_referenced_ids(key: "e") } - public var reply_description: ([String], Int) { - var c = 0 - var ns: [String] = [] - var i = tags.count - 1 - - while i >= 0 { - let tag = tags[i] - if tag.count >= 2 && tag[0] == "p" { - c += 1 - if ns.count < 2 { - ns.append(tag[1]) - } - } - i -= 1 - } - - return (ns, c) - } - public func count_ids() -> Int { return count_refs("e") } @@ -396,147 +377,3 @@ func gather_reply_ids(our_pubkey: String, from: NostrEvent) -> [ReferencedId] { } return ids } - -enum EventRef { - case mention(Mention) - case thread_id(ReferencedId) - case reply(ReferencedId) - case reply_to_root(ReferencedId) - - var is_mention: Mention? { - if case .mention(let m) = self { - return m - } - return nil - } - - var is_direct_reply: ReferencedId? { - switch self { - case .mention: - return nil - case .thread_id: - return nil - case .reply(let refid): - return refid - case .reply_to_root(let refid): - return refid - } - } - - var is_thread_id: ReferencedId? { - switch self { - case .mention(let mention): - return nil - case .thread_id(let referencedId): - return referencedId - case .reply(let referencedId): - return nil - case .reply_to_root(let referencedId): - return referencedId - } - } - - var is_reply: ReferencedId? { - switch self { - case .mention: - return nil - case .thread_id: - return nil - case .reply(let refid): - return refid - case .reply_to_root(let refid): - return refid - } - } -} - -func has_any_e_refs(_ tags: [[String]]) -> Bool { - for tag in tags { - if tag.count >= 2 && tag[0] == "e" { - return true - } - } - return false -} - -func build_mention_indices(_ blocks: [Block], type: MentionType) -> Set { - return blocks.reduce(into: []) { acc, block in - switch block { - case .mention(let m): - if m.type == type { - acc.insert(m.index) - } - case .text: - return - } - } -} - -func interp_event_refs_without_mentions(_ refs: [ReferencedId]) -> [EventRef] { - if refs.count == 0 { - return [] - } - - if refs.count == 1 { - return [.reply_to_root(refs[0])] - } - - var evrefs: [EventRef] = [] - var first: Bool = true - for ref in refs { - if first { - evrefs.append(.thread_id(ref)) - first = false - } else { - evrefs.append(.reply(ref)) - } - } - return evrefs -} - -func interp_event_refs_with_mentions(tags: [[String]], mention_indices: Set) -> [EventRef] { - var mentions: [EventRef] = [] - var ev_refs: [ReferencedId] = [] - var i: Int = 0 - - for tag in tags { - if tag.count >= 2 && tag[0] == "e" { - let ref = tag_to_refid(tag)! - if mention_indices.contains(i) { - let mention = Mention(index: i, type: .event, ref: ref) - mentions.append(.mention(mention)) - } else { - ev_refs.append(ref) - } - } - i += 1 - } - - var replies = interp_event_refs_without_mentions(ev_refs) - replies.append(contentsOf: mentions) - return replies -} - -func interpret_event_refs(blocks: [Block], tags: [[String]]) -> [EventRef] { - if tags.count == 0 { - return [] - } - - /// build a set of indices for each event mention - let mention_indices = build_mention_indices(blocks, type: .event) - - /// simpler case with no mentions - if mention_indices.count == 0 { - let ev_refs = get_referenced_ids(tags: tags, key: "e") - return interp_event_refs_without_mentions(ev_refs) - } - - return interp_event_refs_with_mentions(tags: tags, mention_indices: mention_indices) -} - - -func event_is_reply(_ ev: NostrEvent) -> Bool { - return ev.event_refs.contains { evref in - return evref.is_reply != nil - } -} diff --git a/damus/Views/EventView.swift b/damus/Views/EventView.swift index 208f86f..364892d 100644 --- a/damus/Views/EventView.swift +++ b/damus/Views/EventView.swift @@ -123,8 +123,11 @@ func format_relative_time(_ created_at: Int64) -> String } func reply_desc(profiles: Profiles, event: NostrEvent) -> String { - let (pubkeys, n) = event.reply_description - if pubkeys.count == 0 { + let desc = make_reply_description(event.tags) + let pubkeys = desc.pubkeys + let n = desc.others + + if desc.pubkeys.count == 0 { return "Reply to self" }