commit bedf7e0648d3725241f141694076710ae903f031
parent 14802e334d6e66442a2be48056d44879992d1b0b
Author: William Casarin <jb55@jb55.com>
Date: Tue, 4 Apr 2023 12:03:31 -0700
Add reply counts
Changelog-Added: Reply counts
Diffstat:
10 files changed, 94 insertions(+), 31 deletions(-)
diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj
@@ -37,6 +37,7 @@
4C0A3F8F280F640A000448DE /* ThreadModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F8E280F640A000448DE /* ThreadModel.swift */; };
4C0A3F91280F6528000448DE /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F90280F6528000448DE /* ChatView.swift */; };
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F92280F66F5000448DE /* ReplyMap.swift */; };
+ 4C1A9A1A29DCA17E00516EAC /* ReplyCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C1A9A1929DCA17E00516EAC /* ReplyCounter.swift */; };
4C216F32286E388800040376 /* DMChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F31286E388800040376 /* DMChatView.swift */; };
4C216F34286F5ACD00040376 /* DMView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F33286F5ACD00040376 /* DMView.swift */; };
4C216F362870A9A700040376 /* InputDismissKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F352870A9A700040376 /* InputDismissKeyboard.swift */; };
@@ -399,6 +400,7 @@
4C0A3F8E280F640A000448DE /* ThreadModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadModel.swift; sourceTree = "<group>"; };
4C0A3F90280F6528000448DE /* ChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatView.swift; sourceTree = "<group>"; };
4C0A3F92280F66F5000448DE /* ReplyMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyMap.swift; sourceTree = "<group>"; };
+ 4C1A9A1929DCA17E00516EAC /* ReplyCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyCounter.swift; sourceTree = "<group>"; };
4C216F31286E388800040376 /* DMChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DMChatView.swift; sourceTree = "<group>"; };
4C216F33286F5ACD00040376 /* DMView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DMView.swift; sourceTree = "<group>"; };
4C216F352870A9A700040376 /* InputDismissKeyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputDismissKeyboard.swift; sourceTree = "<group>"; };
@@ -955,6 +957,7 @@
4C30AC7729A577AB00E2BD5A /* EventCache.swift */,
4C9BB83029C0ED4F00FC4E37 /* DisplayName.swift */,
4CE4F0F129D4FCFA005914DB /* DebouncedOnChange.swift */,
+ 4C1A9A1929DCA17E00516EAC /* ReplyCounter.swift */,
);
path = Util;
sourceTree = "<group>";
@@ -1622,6 +1625,7 @@
4CE4F0F229D4FCFA005914DB /* DebouncedOnChange.swift in Sources */,
4CF0ABEC29844B4700D66079 /* AnyDecodable.swift in Sources */,
4C5F9118283D88E40052CD1C /* FollowingModel.swift in Sources */,
+ 4C1A9A1A29DCA17E00516EAC /* ReplyCounter.swift in Sources */,
643EA5C8296B764E005081BB /* RelayFilterView.swift in Sources */,
4C3EA67D28FFBBA300C48A62 /* InvoicesView.swift in Sources */,
4C363A8E28236FE4006E126D /* NoteContentView.swift in Sources */,
diff --git a/damus/Components/ZapButton.swift b/damus/Components/ZapButton.swift
@@ -134,7 +134,7 @@ struct ZapButton: View {
struct ZapButton_Previews: PreviewProvider {
static var previews: some View {
- let bar = ActionBarModel(likes: 0, boosts: 0, zaps: 10, zap_total: 15623414, our_like: nil, our_boost: nil, our_zap: nil)
+ let bar = ActionBarModel(likes: 0, boosts: 0, zaps: 10, zap_total: 15623414, replies: 2, our_like: nil, our_boost: nil, our_zap: nil, our_reply: nil)
ZapButton(damus_state: test_damus_state(), event: test_event, lnurl: "lnurl", bar: bar)
}
}
diff --git a/damus/ContentView.swift b/damus/ContentView.swift
@@ -627,7 +627,8 @@ struct ContentView: View {
events: EventCache(),
bookmarks: BookmarksManager(pubkey: pubkey),
postbox: PostBox(pool: pool),
- bootstrap_relays: bootstrap_relays
+ bootstrap_relays: bootstrap_relays,
+ replies: ReplyCounter(our_pubkey: pubkey)
)
home.damus_state = self.damus_state!
diff --git a/damus/Models/ActionBarModel.swift b/damus/Models/ActionBarModel.swift
@@ -11,34 +11,40 @@ import Foundation
class ActionBarModel: ObservableObject {
@Published var our_like: NostrEvent?
@Published var our_boost: NostrEvent?
+ @Published var our_reply: NostrEvent?
@Published var our_zap: Zap?
@Published var likes: Int
@Published var boosts: Int
@Published var zaps: Int
@Published var zap_total: Int64
+ @Published var replies: Int
static func empty() -> ActionBarModel {
- return ActionBarModel(likes: 0, boosts: 0, zaps: 0, zap_total: 0, our_like: nil, our_boost: nil, our_zap: nil)
+ return ActionBarModel(likes: 0, boosts: 0, zaps: 0, zap_total: 0, replies: 0, our_like: nil, our_boost: nil, our_zap: nil, our_reply: nil)
}
- init(likes: Int, boosts: Int, zaps: Int, zap_total: Int64, our_like: NostrEvent?, our_boost: NostrEvent?, our_zap: Zap?) {
+ init(likes: Int, boosts: Int, zaps: Int, zap_total: Int64, replies: Int, our_like: NostrEvent?, our_boost: NostrEvent?, our_zap: Zap?, our_reply: NostrEvent?) {
self.likes = likes
self.boosts = boosts
self.zaps = zaps
+ self.replies = replies
self.zap_total = zap_total
self.our_like = our_like
self.our_boost = our_boost
self.our_zap = our_zap
+ self.our_reply = our_reply
}
func update(damus: DamusState, evid: String) {
self.likes = damus.likes.counts[evid] ?? 0
self.boosts = damus.boosts.counts[evid] ?? 0
self.zaps = damus.zaps.event_counts[evid] ?? 0
+ self.replies = damus.replies.get_replies(evid)
self.zap_total = damus.zaps.event_totals[evid] ?? 0
self.our_like = damus.likes.our_events[evid]
self.our_boost = damus.boosts.our_events[evid]
self.our_zap = damus.zaps.our_zaps[evid]?.first
+ self.our_reply = damus.replies.our_reply(evid)
self.objectWillChange.send()
}
@@ -54,6 +60,10 @@ class ActionBarModel: ObservableObject {
return our_like != nil
}
+ var replied: Bool {
+ return our_reply != nil
+ }
+
var boosted: Bool {
return our_boost != nil
}
diff --git a/damus/Models/DamusState.swift b/damus/Models/DamusState.swift
@@ -27,8 +27,8 @@ struct DamusState {
let events: EventCache
let bookmarks: BookmarksManager
let postbox: PostBox
-
let bootstrap_relays: [String]
+ let replies: ReplyCounter
var pubkey: String {
return keypair.pubkey
@@ -39,6 +39,6 @@ struct DamusState {
}
static var empty: DamusState {
- return DamusState.init(pool: RelayPool(), keypair: Keypair(pubkey: "", privkey: ""), likes: EventCounter(our_pubkey: ""), boosts: EventCounter(our_pubkey: ""), contacts: Contacts(our_pubkey: ""), tips: TipCounter(our_pubkey: ""), profiles: Profiles(), dms: DirectMessagesModel(our_pubkey: ""), previews: PreviewCache(), zaps: Zaps(our_pubkey: ""), lnurls: LNUrls(), settings: UserSettingsStore(), relay_filters: RelayFilters(our_pubkey: ""), relay_metadata: RelayMetadatas(), drafts: Drafts(), events: EventCache(), bookmarks: BookmarksManager(pubkey: ""), postbox: PostBox(pool: RelayPool()), bootstrap_relays: [])
+ return DamusState.init(pool: RelayPool(), keypair: Keypair(pubkey: "", privkey: ""), likes: EventCounter(our_pubkey: ""), boosts: EventCounter(our_pubkey: ""), contacts: Contacts(our_pubkey: ""), tips: TipCounter(our_pubkey: ""), profiles: Profiles(), dms: DirectMessagesModel(our_pubkey: ""), previews: PreviewCache(), zaps: Zaps(our_pubkey: ""), lnurls: LNUrls(), settings: UserSettingsStore(), relay_filters: RelayFilters(our_pubkey: ""), relay_metadata: RelayMetadatas(), drafts: Drafts(), events: EventCache(), bookmarks: BookmarksManager(pubkey: ""), postbox: PostBox(pool: RelayPool()), bootstrap_relays: [], replies: ReplyCounter(our_pubkey: ""))
}
}
diff --git a/damus/Models/HomeModel.swift b/damus/Models/HomeModel.swift
@@ -517,6 +517,7 @@ class HomeModel: ObservableObject {
return
}
+ damus_state.replies.count_replies(ev)
damus_state.events.insert(ev)
if sub_id == home_subid {
diff --git a/damus/Models/ThreadModel.swift b/damus/Models/ThreadModel.swift
@@ -114,6 +114,7 @@ class ThreadModel: ObservableObject {
}
let the_ev = damus_state.events.upsert(ev)
+ damus_state.replies.count_replies(the_ev)
damus_state.events.add_replies(ev: the_ev)
event_map.insert(ev)
diff --git a/damus/Util/ReplyCounter.swift b/damus/Util/ReplyCounter.swift
@@ -0,0 +1,54 @@
+//
+// ReplyCounter.swift
+// damus
+//
+// Created by William Casarin on 2023-04-04.
+//
+
+import Foundation
+
+class ReplyCounter {
+ private var replies: [String: Int]
+ private var counted: Set<String>
+ private var our_replies: [String: NostrEvent]
+ private let our_pubkey: String
+
+ init(our_pubkey: String) {
+ self.our_pubkey = our_pubkey
+ replies = [:]
+ counted = Set()
+ our_replies = [:]
+ }
+
+ func our_reply(_ evid: String) -> NostrEvent? {
+ return our_replies[evid]
+ }
+
+ func get_replies(_ evid: String) -> Int {
+ return replies[evid] ?? 0
+ }
+
+ func count_replies(_ event: NostrEvent) {
+ guard event.is_textlike else {
+ return
+ }
+
+ if counted.contains(event.id) {
+ return
+ }
+
+ counted.insert(event.id)
+
+ for reply in event.direct_replies(nil) {
+ if event.pubkey == our_pubkey {
+ self.our_replies[reply.ref_id] = event
+ }
+
+ if replies[reply.ref_id] != nil {
+ replies[reply.ref_id] = replies[reply.ref_id]! + 1
+ } else {
+ replies[reply.ref_id] = 1
+ }
+ }
+ }
+}
diff --git a/damus/Views/ActionBar/EventActionBar.swift b/damus/Views/ActionBar/EventActionBar.swift
@@ -47,10 +47,15 @@ struct EventActionBar: View {
var body: some View {
HStack {
if damus_state.keypair.privkey != nil {
- EventActionButton(img: "bubble.left", col: nil) {
- notify(.reply, event)
+ HStack(spacing: 4) {
+ EventActionButton(img: "bubble.left", col: bar.replied ? Color.pink : Color.gray) {
+ notify(.reply, event)
+ }
+ .accessibilityLabel(NSLocalizedString("Reply", comment: "Accessibility label for reply button"))
+ Text(verbatim: "\(bar.replies > 0 ? "\(bar.replies)" : "")")
+ .font(.footnote.weight(.medium))
+ .foregroundColor(bar.replied ? Color.pink : Color.gray)
}
- .accessibilityLabel(NSLocalizedString("Reply", comment: "Accessibility label for reply button"))
}
Spacer()
HStack(spacing: 4) {
@@ -225,12 +230,12 @@ struct EventActionBar_Previews: PreviewProvider {
let ev = NostrEvent(content: "hi", pubkey: pk)
let bar = ActionBarModel.empty()
- let likedbar = ActionBarModel(likes: 10, boosts: 0, zaps: 0, zap_total: 0, our_like: nil, our_boost: nil, our_zap: nil)
- let likedbar_ours = ActionBarModel(likes: 10, boosts: 0, zaps: 0, zap_total: 0, our_like: NostrEvent(id: "", content: "", pubkey: ""), our_boost: nil, our_zap: nil)
- let maxed_bar = ActionBarModel(likes: 999, boosts: 999, zaps: 999, zap_total: 99999999, our_like: NostrEvent(id: "", content: "", pubkey: ""), our_boost: NostrEvent(id: "", content: "", pubkey: ""), our_zap: nil)
- let extra_max_bar = ActionBarModel(likes: 9999, boosts: 9999, zaps: 9999, zap_total: 99999999, our_like: NostrEvent(id: "", content: "", pubkey: ""), our_boost: NostrEvent(id: "", content: "", pubkey: ""), our_zap: nil)
- let mega_max_bar = ActionBarModel(likes: 9999999, boosts: 99999, zaps: 9999, zap_total: 99999999, our_like: NostrEvent(id: "", content: "", pubkey: ""), our_boost: NostrEvent(id: "", content: "", pubkey: ""), our_zap: nil)
- let zapbar = ActionBarModel(likes: 0, boosts: 0, zaps: 5, zap_total: 10000000, our_like: nil, our_boost: nil, our_zap: nil)
+ let likedbar = ActionBarModel(likes: 10, boosts: 0, zaps: 0, zap_total: 0, replies: 0, our_like: nil, our_boost: nil, our_zap: nil, our_reply: nil)
+ let likedbar_ours = ActionBarModel(likes: 10, boosts: 0, zaps: 0, zap_total: 0, replies: 0, our_like: test_event, our_boost: nil, our_zap: nil, our_reply: nil)
+ let maxed_bar = ActionBarModel(likes: 999, boosts: 999, zaps: 999, zap_total: 99999999, replies: 999, our_like: test_event, our_boost: test_event, our_zap: nil, our_reply: nil)
+ let extra_max_bar = ActionBarModel(likes: 9999, boosts: 9999, zaps: 9999, zap_total: 99999999, replies: 9999, our_like: test_event, our_boost: test_event, our_zap: nil, our_reply: test_event)
+ let mega_max_bar = ActionBarModel(likes: 9999999, boosts: 99999, zaps: 9999, zap_total: 99999999, replies: 9999999, our_like: test_event, our_boost: test_event, our_zap: test_zap, our_reply: test_event)
+ let zapbar = ActionBarModel(likes: 0, boosts: 0, zaps: 5, zap_total: 10000000, replies: 0, our_like: nil, our_boost: nil, our_zap: nil, our_reply: nil)
VStack(spacing: 50) {
EventActionBar(damus_state: ds, event: ev, bar: bar)
diff --git a/damus/Views/EventView.swift b/damus/Views/EventView.swift
@@ -151,22 +151,9 @@ func format_date(_ created_at: Int64) -> String {
}
func make_actionbar_model(ev: String, damus: DamusState) -> ActionBarModel {
- let likes = damus.likes.counts[ev]
- let boosts = damus.boosts.counts[ev]
- let zaps = damus.zaps.event_counts[ev]
- let zap_total = damus.zaps.event_totals[ev]
- let our_like = damus.likes.our_events[ev]
- let our_boost = damus.boosts.our_events[ev]
- let our_zap = damus.zaps.our_zaps[ev]
-
- return ActionBarModel(likes: likes ?? 0,
- boosts: boosts ?? 0,
- zaps: zaps ?? 0,
- zap_total: zap_total ?? 0,
- our_like: our_like,
- our_boost: our_boost,
- our_zap: our_zap?.first
- )
+ let model = ActionBarModel.empty()
+ model.update(damus: damus, evid: ev)
+ return model
}