damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | LICENSE

commit 040ffdf9f597da3a83a91a8411a5bd6f7a0356f6
parent bd49c8a9d11e2819c7360fdd390fb6963f8dc7c0
Author: William Casarin <jb55@jb55.com>
Date:   Tue, 10 May 2022 14:41:34 -0700

boosts working

Signed-off-by: William Casarin <jb55@jb55.com>

Diffstat:
Mdamus/ContentView.swift | 64+++++++++++++++++++++++++++++++++++++++-------------------------
Mdamus/Models/ActionBarModel.swift | 4+++-
Mdamus/Models/LikeCounter.swift | 8++++----
Mdamus/Models/Liked.swift | 4++--
Mdamus/Models/ThreadModel.swift | 2+-
Mdamus/Nostr/NostrEvent.swift | 26++++++++++++++++++++++++--
Mdamus/Notifications.swift | 8+++++++-
Mdamus/Views/ChatView.swift | 3++-
Mdamus/Views/EventActionBar.swift | 24+++++++++++++++---------
Mdamus/Views/EventView.swift | 38++++++++++++++++++++++++++++++--------
10 files changed, 127 insertions(+), 54 deletions(-)

diff --git a/damus/ContentView.swift b/damus/ContentView.swift @@ -51,7 +51,6 @@ struct ContentView: View { @State var is_profile_open: Bool = false @State var last_event_of_kind: [String: [Int: NostrEvent]] = [:] @State var has_events: [String: ()] = [:] - @State var has_friend_event: [String: ()] = [:] @State var new_notifications: Bool = false @State var event: NostrEvent? = nil @State var events: [NostrEvent] = [] @@ -265,7 +264,7 @@ struct ContentView: View { } .onReceive(handle_notify(.boost)) { notif in let ev = notif.object as! NostrEvent - let boost = make_boost_event(ev, privkey: privkey, pubkey: pubkey) + let boost = make_boost_event(pubkey: pubkey, privkey: privkey, boosted: ev) self.damus?.pool.send(.event(boost)) } .onReceive(handle_notify(.open_thread)) { obj in @@ -277,13 +276,14 @@ struct ContentView: View { let ev = notif.object as! NostrEvent self.active_sheet = .reply(ev) } + .onReceive(handle_notify(.boost)) { boost in + let ev = boost.object as! NostrEvent + let boost_ev = make_boost_event(pubkey: pubkey, privkey: privkey, boosted: ev) + self.damus?.pool.send(.event(boost_ev)) + } .onReceive(handle_notify(.like)) { like in let ev = like.object as! NostrEvent - guard let like_ev = make_like_event(pubkey: pubkey, liked: ev) else { - return - } - like_ev.calculate_id() - like_ev.sign(privkey: privkey) + let like_ev = make_like_event(pubkey: pubkey, privkey: privkey, liked: ev) self.damus?.pool.send(.event(like_ev)) } .onReceive(handle_notify(.broadcast_event)) { obj in @@ -390,8 +390,28 @@ struct ContentView: View { } func handle_boost_event(_ ev: NostrEvent) { + var boost_ev_id = ev.last_refid()?.ref_id + + // CHECK SIGS ON THESE + if var inner_ev = ev.inner_event { + boost_ev_id = inner_ev.id + + if inner_ev.kind == 1 { + handle_text_event(ev) + } + } + + guard let e = boost_ev_id else { + return + } - //damus!.boosts.add_event(ev) + switch damus!.boosts.add_event(ev, target: e) { + case .already_counted: + break + case .success(let n): + let boosted = Counted(event: ev, id: e, total: n) + notify(.boosted, boosted) + } } func handle_like_event(_ ev: NostrEvent) { @@ -403,10 +423,10 @@ struct ContentView: View { // CHECK SIGS ON THESE switch damus!.likes.add_event(ev, target: e.ref_id) { - case .user_already_liked: + case .already_counted: break case .success(let n): - let liked = Liked(like: ev, id: e.ref_id, total: n) + let liked = Counted(event: ev, id: e.ref_id, total: n) notify(.liked, liked) } } @@ -484,12 +504,12 @@ struct ContentView: View { } func handle_friend_event(_ ev: NostrEvent) { - if has_friend_event[ev.id] != nil || !is_friend_event(ev) { + if !is_friend_event(ev) { + return + } + if !insert_uniq_sorted_event(events: &self.friend_events, new_ev: ev, cmp: { $0.created_at > $1.created_at } ) { return } - self.has_friend_event[ev.id] = () - self.friend_events.append(ev) - self.friend_events = self.friend_events.sorted { $0.created_at > $1.created_at } } func handle_text_event(_ ev: NostrEvent) { @@ -497,8 +517,9 @@ struct ContentView: View { return } - self.events.append(ev) - self.events = self.events.sorted { $0.created_at > $1.created_at } + if !insert_uniq_sorted_event(events: &self.events, new_ev: ev, cmp: { $0.created_at > $1.created_at }) { + return + } handle_friend_event(ev) @@ -521,6 +542,8 @@ struct ContentView: View { handle_text_event(ev) } else if ev.kind == 0 { handle_metadata_event(ev) + } else if ev.kind == 6 { + handle_boost_event(ev) } else if ev.kind == 7 { handle_like_event(ev) } else if ev.kind == 3 { @@ -708,15 +731,6 @@ func save_last_notified(_ ev: NostrEvent) { } - -func make_boost_event(_ ev: NostrEvent, privkey: String, pubkey: String) -> NostrEvent { - let boost = NostrEvent(content: "", pubkey: pubkey, kind: 6, tags: [["e", ev.id]]) - boost.calculate_id() - boost.sign(privkey: privkey) - return boost -} - - func get_like_pow() -> [String] { return ["00000"] // 20 bits } diff --git a/damus/Models/ActionBarModel.swift b/damus/Models/ActionBarModel.swift @@ -12,9 +12,11 @@ class ActionBarModel: ObservableObject { @Published var our_like: NostrEvent? @Published var our_boost: NostrEvent? @Published var likes: Int + @Published var boosts: Int - init(likes: Int, our_like: NostrEvent?, our_boost: NostrEvent?) { + init(likes: Int, boosts: Int, our_like: NostrEvent?, our_boost: NostrEvent?) { self.likes = likes + self.boosts = boosts self.our_like = our_like self.our_boost = our_boost } diff --git a/damus/Models/LikeCounter.swift b/damus/Models/LikeCounter.swift @@ -14,8 +14,8 @@ class EventCounter { var our_events: [String: NostrEvent] = [:] var our_pubkey: String - enum LikeResult { - case user_already_liked + enum CountResult { + case already_counted case success(Int) } @@ -23,7 +23,7 @@ class EventCounter { self.our_pubkey = our_pubkey } - func add_event(_ ev: NostrEvent, target: String) -> LikeResult { + func add_event(_ ev: NostrEvent, target: String) -> CountResult { let pubkey = ev.pubkey if self.user_events[pubkey] == nil { @@ -32,7 +32,7 @@ class EventCounter { if user_events[pubkey]!.contains(target) { // don't double count - return .user_already_liked + return .already_counted } user_events[pubkey]!.insert(target) diff --git a/damus/Models/Liked.swift b/damus/Models/Liked.swift @@ -7,8 +7,8 @@ import Foundation -struct Liked { - let like: NostrEvent +struct Counted { + let event: NostrEvent let id: String let total: Int } diff --git a/damus/Models/ThreadModel.swift b/damus/Models/ThreadModel.swift @@ -101,7 +101,7 @@ class ThreadModel: ObservableObject { } func subscribe() { - var ref_events = NostrFilter.filter_kinds([1,5,7]) + var ref_events = NostrFilter.filter_kinds([1,5,6,7]) var events_filter = NostrFilter.filter_kinds([1]) //var likes_filter = NostrFilter.filter_kinds(7]) diff --git a/damus/Nostr/NostrEvent.swift b/damus/Nostr/NostrEvent.swift @@ -37,6 +37,7 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible { var id: String var sig: String var tags: [[String]] + var boosted_by: String? // cached field for pow calc var pow: Int? @@ -53,6 +54,10 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible { return parse_mentions(content: self.content, tags: self.tags) }() + lazy var inner_event: NostrEvent? = { + return event_from_json(dat: self.content) + }() + lazy var event_refs: [EventRef] = { return interpret_event_refs(blocks: self.blocks, tags: self.tags) }() @@ -335,13 +340,26 @@ func get_referenced_ids(tags: [[String]], key: String) -> [ReferencedId] { } } +func make_boost_event(pubkey: String, privkey: String, boosted: NostrEvent) -> NostrEvent { + var tags: [[String]] = boosted.tags.filter { tag in tag.count >= 2 && (tag[0] == "e" || tag[0] == "p") } + tags.append(["e", boosted.id]) + tags.append(["p", boosted.pubkey]) + + let ev = NostrEvent(content: event_to_json(ev: boosted), pubkey: pubkey, kind: 6, tags: tags) + ev.calculate_id() + ev.sign(privkey: privkey) + return ev +} -func make_like_event(pubkey: String, liked: NostrEvent) -> NostrEvent? { +func make_like_event(pubkey: String, privkey: String, liked: NostrEvent) -> NostrEvent { var tags: [[String]] = liked.tags.filter { tag in tag.count >= 2 && (tag[0] == "e" || tag[0] == "p") } tags.append(["e", liked.id]) tags.append(["p", liked.pubkey]) + let ev = NostrEvent(content: "", pubkey: pubkey, kind: 7, tags: tags) + ev.calculate_id() + ev.sign(privkey: privkey) - return NostrEvent(content: "", pubkey: pubkey, kind: 7, tags: tags) + return ev } func gather_reply_ids(our_pubkey: String, from: NostrEvent) -> [ReferencedId] { @@ -355,6 +373,10 @@ func gather_reply_ids(our_pubkey: String, from: NostrEvent) -> [ReferencedId] { return ids } +func event_from_json(dat: String) -> NostrEvent? { + return try? JSONDecoder().decode(NostrEvent.self, from: Data(dat.utf8)) +} + func event_to_json(ev: NostrEvent) -> String { let encoder = JSONEncoder() guard let res = try? encoder.encode(ev) else { diff --git a/damus/Notifications.swift b/damus/Notifications.swift @@ -99,7 +99,13 @@ extension Notification.Name { extension Notification.Name { static var boost: Notification.Name { - return Notification.Name("boost post") + return Notification.Name("boost") + } +} + +extension Notification.Name { + static var boosted: Notification.Name { + return Notification.Name("boosted") } } diff --git a/damus/Views/ChatView.swift b/damus/Views/ChatView.swift @@ -113,10 +113,11 @@ struct ChatView: View { NoteContentView(event: event, profiles: damus.profiles, content: event.content) if is_active || next_ev == nil || next_ev!.pubkey != event.pubkey { + let bar = make_actionbar_model(ev: event, like_counter: damus.likes, boost_counter: damus.boosts) EventActionBar(event: event, our_pubkey: damus.pubkey, profiles: damus.profiles, - bar: make_actionbar_model(ev: event, counter: damus.likes) + bar: bar ) } diff --git a/damus/Views/EventActionBar.swift b/damus/Views/EventActionBar.swift @@ -42,7 +42,7 @@ struct EventActionBar: View { HStack(alignment: .bottom) { Text("\(bar.likes > 0 ? "\(bar.likes)" : "")") .font(.footnote) - .foregroundColor(Color.gray) + .foregroundColor(bar.liked ? Color.red : Color.gray) EventActionButton(img: bar.liked ? "heart.fill" : "heart", col: bar.liked ? Color.red : nil) { if bar.liked { @@ -54,23 +54,29 @@ struct EventActionBar: View { } .padding([.trailing], 40) - EventActionButton(img: "arrow.2.squarepath", col: bar.boosted ? Color.green : nil) { - if bar.boosted { - notify(.delete, bar.our_boost) - } else { - notify(.boost, event) + HStack(alignment: .bottom) { + Text("\(bar.boosts > 0 ? "\(bar.boosts)" : "")") + .font(.footnote) + .foregroundColor(bar.boosted ? Color.green : Color.gray) + + EventActionButton(img: "arrow.2.squarepath", col: bar.boosted ? Color.green : nil) { + if bar.boosted { + notify(.delete, bar.our_boost) + } else { + notify(.boost, event) + } } } } .onReceive(handle_notify(.liked)) { n in - let liked = n.object as! Liked + let liked = n.object as! Counted if liked.id != event.id { return } self.bar.likes = liked.total - if liked.like.pubkey == our_pubkey { - self.bar.our_like = liked.like + if liked.event.pubkey == our_pubkey { + self.bar.our_like = liked.event } } } diff --git a/damus/Views/EventView.swift b/damus/Views/EventView.swift @@ -45,8 +45,28 @@ struct EventView: View { @EnvironmentObject var action_bar: ActionBarModel var body: some View { - let profile = damus.profiles.lookup(id: event.pubkey) - HStack { + return Group { + if event.known_kind == .boost, let inner_ev = event.inner_event { + VStack(alignment: .leading) { + HStack { + Label("", systemImage: "arrow.2.squarepath") + .foregroundColor(Color.gray) + ProfileName(pubkey: event.pubkey, profile: damus.profiles.lookup(id: event.pubkey)) + .foregroundColor(Color.gray) + Text(" Boosted") + .foregroundColor(Color.gray) + } + TextEvent(inner_ev) + } + } else { + TextEvent(event) + } + } + } + + func TextEvent(_ event: NostrEvent) -> some View { + return HStack { + let profile = damus.profiles.lookup(id: event.pubkey) VStack { let pv = ProfileView(damus: damus, profile: ProfileModel(pubkey: event.pubkey, damus: damus)) @@ -79,7 +99,8 @@ struct EventView: View { Spacer() if has_action_bar { - EventActionBar(event: event, our_pubkey: damus.pubkey, profiles: damus.profiles, bar: make_actionbar_model(ev: event, counter: damus.likes)) + let bar = make_actionbar_model(ev: event, like_counter: damus.likes, boost_counter: damus.boosts) + EventActionBar(event: event, our_pubkey: damus.pubkey, profiles: damus.profiles, bar: bar) } Divider() @@ -159,10 +180,11 @@ func reply_others_desc(n: Int, n_pubkeys: Int) -> String { -func make_actionbar_model(ev: NostrEvent, counter: EventCounter) -> ActionBarModel { - let likes = counter.counts[ev.id] - let our_like = counter.our_events[ev.id] - let our_boost: NostrEvent? = nil +func make_actionbar_model(ev: NostrEvent, like_counter: EventCounter, boost_counter: EventCounter) -> ActionBarModel { + let likes = like_counter.counts[ev.id] + let boosts = boost_counter.counts[ev.id] + let our_like = like_counter.our_events[ev.id] + let our_boost = boost_counter.our_events[ev.id] - return ActionBarModel(likes: likes ?? 0, our_like: our_like, our_boost: our_boost) + return ActionBarModel(likes: likes ?? 0, boosts: boosts ?? 0, our_like: our_like, our_boost: our_boost) }