Add reply counts

Changelog-Added: Reply counts
This commit is contained in:
William Casarin
2023-04-04 12:03:31 -07:00
parent 14802e334d
commit bedf7e0648
10 changed files with 94 additions and 31 deletions

View File

@@ -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 */,

View File

@@ -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)
}
}

View File

@@ -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!

View File

@@ -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
}

View File

@@ -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: ""))
}
}

View File

@@ -517,6 +517,7 @@ class HomeModel: ObservableObject {
return
}
damus_state.replies.count_replies(ev)
damus_state.events.insert(ev)
if sub_id == home_subid {

View File

@@ -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)

View File

@@ -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
}
}
}
}

View File

@@ -47,10 +47,15 @@ struct EventActionBar: View {
var body: some View {
HStack {
if damus_state.keypair.privkey != nil {
EventActionButton(img: "bubble.left", col: nil) {
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)
}
}
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)

View File

@@ -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
}