damus

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

commit 715d4aa35d4dc1be61e89f9298d8a8e6a2759f7a
parent 4b54278378f56e8901d12f87a5fcb8722948ae21
Author: William Casarin <jb55@jb55.com>
Date:   Fri, 10 Feb 2023 10:50:49 -0800

List zaps on posts

Diffstat:
Mdamus.xcodeproj/project.pbxproj | 16++++++++++++++++
Mdamus/Models/HomeModel.swift | 15++++++++-------
Adamus/Models/ZapsModel.swift | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdamus/Views/ActionBar/EventDetailBar.swift | 9+++++++--
Mdamus/Views/Events/SelectedEventView.swift | 2+-
Adamus/Views/Zaps/ZapsView.swift | 42++++++++++++++++++++++++++++++++++++++++++
6 files changed, 143 insertions(+), 10 deletions(-)

diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj @@ -173,6 +173,8 @@ 4CE879502996B2BD00F758CC /* RelayStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8794F2996B2BD00F758CC /* RelayStatus.swift */; }; 4CE879522996B68900F758CC /* RelayType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE879512996B68900F758CC /* RelayType.swift */; }; 4CE879552996BAB900F758CC /* RelayPaidDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE879542996BAB900F758CC /* RelayPaidDetail.swift */; }; + 4CE879582996C45300F758CC /* ZapsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE879572996C45300F758CC /* ZapsView.swift */; }; + 4CE8795B2996C47A00F758CC /* ZapsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8795A2996C47A00F758CC /* ZapsModel.swift */; }; 4CEE2AED2805B22500AB5EEF /* NostrRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AEC2805B22500AB5EEF /* NostrRequest.swift */; }; 4CEE2AF1280B216B00AB5EEF /* EventDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */; }; 4CEE2AF3280B25C500AB5EEF /* ProfilePicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF2280B25C500AB5EEF /* ProfilePicView.swift */; }; @@ -472,6 +474,8 @@ 4CE8794F2996B2BD00F758CC /* RelayStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayStatus.swift; sourceTree = "<group>"; }; 4CE879512996B68900F758CC /* RelayType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayType.swift; sourceTree = "<group>"; }; 4CE879542996BAB900F758CC /* RelayPaidDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayPaidDetail.swift; sourceTree = "<group>"; }; + 4CE879572996C45300F758CC /* ZapsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapsView.swift; sourceTree = "<group>"; }; + 4CE8795A2996C47A00F758CC /* ZapsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapsModel.swift; sourceTree = "<group>"; }; 4CEE2AE72804F57C00AB5EEF /* libsecp256k1.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libsecp256k1.a; sourceTree = "<group>"; }; 4CEE2AEC2805B22500AB5EEF /* NostrRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostrRequest.swift; sourceTree = "<group>"; }; 4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventDetailView.swift; sourceTree = "<group>"; }; @@ -655,6 +659,7 @@ 3AE45AF5297BB2E700C1D842 /* LibreTranslateServer.swift */, 3AAA95C9298DF87B00F3D526 /* TranslationService.swift */, 3AAA95CB298E07E900F3D526 /* DeepLPlan.swift */, + 4CE8795A2996C47A00F758CC /* ZapsModel.swift */, ); path = Models; sourceTree = "<group>"; @@ -662,6 +667,7 @@ 4C75EFA227FA576C0006080F /* Views */ = { isa = PBXGroup; children = ( + 4CE879562996C44A00F758CC /* Zaps */, 4CB9D4A52992D01900A9A7E4 /* Profile */, 4CAAD8AE29888A9B00060CEA /* Relays */, 4CF0ABF42985CD4200D66079 /* Posting */, @@ -951,6 +957,14 @@ path = Detail; sourceTree = "<group>"; }; + 4CE879562996C44A00F758CC /* Zaps */ = { + isa = PBXGroup; + children = ( + 4CE879572996C45300F758CC /* ZapsView.swift */, + ); + path = Zaps; + sourceTree = "<group>"; + }; 4CEE2AE62804F57B00AB5EEF /* Frameworks */ = { isa = PBXGroup; children = ( @@ -1245,12 +1259,14 @@ 4C3EA64928FF597700C48A62 /* bech32.c in Sources */, 4CE879522996B68900F758CC /* RelayType.swift in Sources */, 4C90BD162839DB54008EE7EF /* NostrMetadata.swift in Sources */, + 4CE8795B2996C47A00F758CC /* ZapsModel.swift in Sources */, 4C3A1D3729637E0500558C0F /* PreviewCache.swift in Sources */, 4C3EA67528FF7A5A00C48A62 /* take.c in Sources */, 4C3AC7A12835A81400E1F516 /* SetupView.swift in Sources */, 4C06670128FC7C5900038D2A /* RelayView.swift in Sources */, 4C285C8C28398BC7008A31F1 /* Keys.swift in Sources */, 4CACA9DC280C38C000D9BBE8 /* Profiles.swift in Sources */, + 4CE879582996C45300F758CC /* ZapsView.swift in Sources */, 4C633352283D419F00B1C9C3 /* SignalModel.swift in Sources */, 9609F058296E220800069BF3 /* BannerImageView.swift in Sources */, 4C363A94282704FA006E126D /* Post.swift in Sources */, diff --git a/damus/Models/HomeModel.swift b/damus/Models/HomeModel.swift @@ -117,13 +117,17 @@ class HomeModel: ObservableObject { } } - func handle_zap_event_with_zapper(_ ev: NostrEvent, zapper: String) { + func handle_zap_event_with_zapper(_ ev: NostrEvent, our_pubkey: String, zapper: String) { guard let zap = Zap.from_zap_event(zap_ev: ev, zapper: zapper) else { return } damus_state.zaps.add_zap(zap: zap) + guard zap.target.pubkey == our_pubkey else { + return + } + if !insert_uniq_sorted_event(events: &notifications, new_ev: ev, cmp: { $0.created_at > $1.created_at }) { return } @@ -138,12 +142,8 @@ class HomeModel: ObservableObject { return } - guard ptag == damus_state.pubkey else { - return - } - if let local_zapper = damus_state.profiles.lookup_zapper(pubkey: damus_state.pubkey) { - handle_zap_event_with_zapper(ev, zapper: local_zapper) + handle_zap_event_with_zapper(ev, our_pubkey: damus_state.pubkey, zapper: local_zapper) return } @@ -161,7 +161,8 @@ class HomeModel: ObservableObject { } DispatchQueue.main.async { - self.handle_zap_event_with_zapper(ev, zapper: zapper) + self.damus_state.profiles.zappers[ptag] = zapper + self.handle_zap_event_with_zapper(ev, our_pubkey: self.damus_state.pubkey, zapper: zapper) } } diff --git a/damus/Models/ZapsModel.swift b/damus/Models/ZapsModel.swift @@ -0,0 +1,69 @@ +// +// ZapsModel.swift +// damus +// +// Created by William Casarin on 2023-02-10. +// + +import Foundation + +class ZapsModel: ObservableObject { + let profiles: Profiles + let pool: RelayPool + let target: ZapTarget + var zaps: [Zap] + + let zaps_subid = UUID().description + + init(profiles: Profiles, pool: RelayPool, target: ZapTarget) { + self.target = target + self.profiles = profiles + self.pool = pool + self.zaps = [] + } + + func subscribe() { + var filter = NostrFilter.filter_kinds([9735]) + switch target { + case .profile(let profile_id): + filter.pubkeys = [profile_id] + case .note(let note_target): + filter.referenced_ids = [note_target.note_id] + } + pool.subscribe(sub_id: zaps_subid, filters: [filter], handler: handle_event) + } + + func unsubscribe() { + pool.unsubscribe(sub_id: zaps_subid) + } + + func handle_event(relay_id: String, conn_ev: NostrConnectionEvent) { + guard case .nostr_event(let resp) = conn_ev else { + return + } + + guard resp.subid == zaps_subid else { + return + } + + guard case .event(_, let ev) = resp else { + return + } + + guard ev.kind == 9735 else { + return + } + + guard let zapper = profiles.lookup_zapper(pubkey: target.pubkey) else { + return + } + + guard let zap = Zap.from_zap_event(zap_ev: ev, zapper: zapper) else { + return + } + + if insert_uniq_sorted_zap(zaps: &zaps, new_zap: zap) { + objectWillChange.send() + } + } +} diff --git a/damus/Views/ActionBar/EventDetailBar.swift b/damus/Views/ActionBar/EventDetailBar.swift @@ -10,6 +10,7 @@ import SwiftUI struct EventDetailBar: View { let state: DamusState let target: String + let target_pk: String @ObservedObject var bar: ActionBarModel var body: some View { @@ -29,7 +30,11 @@ struct EventDetailBar: View { } if bar.zaps > 0 { - Text("\(Text("\(bar.zaps)", comment: "Number of zap payments on a post.").font(.body.bold())) \(Text(String(format: NSLocalizedString("zaps_count", comment: "Part of a larger sentence to describe how many zap payments there are on a post."), bar.boosts)).foregroundColor(.gray))", comment: "Sentence composed of 2 variables to describe how many zap payments there are on a post. In source English, the first variable is the number of zap payments, and the second variable is 'Zap' or 'Zaps'.") + let dst = ZapsView(state: state, target: .note(id: target, author: target_pk)) + NavigationLink(destination: dst) { + Text("\(Text("\(bar.zaps)", comment: "Number of zap payments on a post.").font(.body.bold())) \(Text(String(format: NSLocalizedString("zaps_count", comment: "Part of a larger sentence to describe how many zap payments there are on a post."), bar.boosts)).foregroundColor(.gray))", comment: "Sentence composed of 2 variables to describe how many zap payments there are on a post. In source English, the first variable is the number of zap payments, and the second variable is 'Zap' or 'Zaps'.") + } + .buttonStyle(PlainButtonStyle()) } } } @@ -37,6 +42,6 @@ struct EventDetailBar: View { struct EventDetailBar_Previews: PreviewProvider { static var previews: some View { - EventDetailBar(state: test_damus_state(), target: "", bar: ActionBarModel.empty()) + EventDetailBar(state: test_damus_state(), target: "", target_pk: "", bar: ActionBarModel.empty()) } } diff --git a/damus/Views/Events/SelectedEventView.swift b/damus/Views/Events/SelectedEventView.swift @@ -38,7 +38,7 @@ struct SelectedEventView: View { let bar = make_actionbar_model(ev: event, damus: damus) if !bar.is_empty { - EventDetailBar(state: damus, target: event.id, bar: bar) + EventDetailBar(state: damus, target: event.id, target_pk: event.pubkey, bar: bar) Divider() } diff --git a/damus/Views/Zaps/ZapsView.swift b/damus/Views/Zaps/ZapsView.swift @@ -0,0 +1,42 @@ +// +// ZapsView.swift +// damus +// +// Created by William Casarin on 2023-02-10. +// + +import SwiftUI + +struct ZapsView: View { + let state: DamusState + @StateObject var model: ZapsModel + + init(state: DamusState, target: ZapTarget) { + self.state = state + self._model = StateObject(wrappedValue: ZapsModel(profiles: state.profiles, pool: state.pool, target: target)) + } + + var body: some View { + ScrollView { + LazyVStack { + ForEach(model.zaps, id: \.event.id) { zap in + ZapEvent(damus: state, zap: zap) + .padding() + } + } + } + .navigationBarTitle(NSLocalizedString("Zaps", comment: "Navigation bar title for the Zaps view.")) + .onAppear { + model.subscribe() + } + .onDisappear { + model.unsubscribe() + } + } +} + +struct ZapsView_Previews: PreviewProvider { + static var previews: some View { + ZapsView(state: test_damus_state(), target: .profile("pk")) + } +}