damus

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

commit c046c7cf457643706ef67b11cb37051003b05dc5
parent 5daaec35a8c0ba46dcdc53ddc0c809d7680df91a
Author: Terry Yiu <963907+tyiu@users.noreply.github.com>
Date:   Sun, 22 Jan 2023 23:24:10 -0500

Add Reposts view

Changelog-Added: Reposts view
Closes: #376

Diffstat:
Mdamus.xcodeproj/project.pbxproj | 20++++++++++++++++++++
Adamus/Models/RepostsModel.swift | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdamus/Views/ActionBar/EventDetailBar.swift | 5++++-
Adamus/Views/Reposts/RepostView.swift | 24++++++++++++++++++++++++
Adamus/Views/RepostsView.swift | 38++++++++++++++++++++++++++++++++++++++
5 files changed, 163 insertions(+), 1 deletion(-)

diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj @@ -12,6 +12,9 @@ 3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAEC294FCCFC00EE4006 /* Constants.swift */; }; 31D2E847295218AF006D67F8 /* Shimmer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D2E846295218AF006D67F8 /* Shimmer.swift */; }; 3A4325A82961E11400BFCD9D /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 3A4325AA2961E11400BFCD9D /* Localizable.stringsdict */; }; + 3AA247FD297E3CFF0090C62D /* RepostsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA247FC297E3CFF0090C62D /* RepostsModel.swift */; }; + 3AA247FF297E3D900090C62D /* RepostsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA247FE297E3D900090C62D /* RepostsView.swift */; }; + 3AA24802297E3DC20090C62D /* RepostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA24801297E3DC20090C62D /* RepostView.swift */; }; 3ACB685C297633BC00C46468 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3ACB685A297633BC00C46468 /* InfoPlist.strings */; }; 3ACB685F297633BC00C46468 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3ACB685D297633BC00C46468 /* Localizable.strings */; }; 3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */; }; @@ -217,6 +220,9 @@ 3A929C20297F2CF80090925E /* it-IT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "it-IT"; path = "it-IT.lproj/InfoPlist.strings"; sourceTree = "<group>"; }; 3A929C21297F2CF80090925E /* it-IT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "it-IT"; path = "it-IT.lproj/Localizable.strings"; sourceTree = "<group>"; }; 3A929C22297F2CF80090925E /* it-IT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "it-IT"; path = "it-IT.lproj/Localizable.stringsdict"; sourceTree = "<group>"; }; + 3AA247FC297E3CFF0090C62D /* RepostsModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RepostsModel.swift; sourceTree = "<group>"; }; + 3AA247FE297E3D900090C62D /* RepostsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepostsView.swift; sourceTree = "<group>"; }; + 3AA24801297E3DC20090C62D /* RepostView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepostView.swift; sourceTree = "<group>"; }; 3ACB685B297633BC00C46468 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-419"; path = "es-419.lproj/InfoPlist.strings"; sourceTree = "<group>"; }; 3ACB685E297633BC00C46468 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-419"; path = "es-419.lproj/Localizable.strings"; sourceTree = "<group>"; }; 3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeAgoTests.swift; sourceTree = "<group>"; }; @@ -466,6 +472,14 @@ path = "Empty Views"; sourceTree = "<group>"; }; + 3AA24800297E3DAE0090C62D /* Reposts */ = { + isa = PBXGroup; + children = ( + 3AA24801297E3DC20090C62D /* RepostView.swift */, + ); + path = Reposts; + sourceTree = "<group>"; + }; 4C06670728FDE62900038D2A /* damus-c */ = { isa = PBXGroup; children = ( @@ -523,6 +537,7 @@ 4C0A3F8D280F63FF000448DE /* Models */ = { isa = PBXGroup; children = ( + 3AA247FC297E3CFF0090C62D /* RepostsModel.swift */, 4C0A3F8E280F640A000448DE /* ThreadModel.swift */, 4C0A3F92280F66F5000448DE /* ReplyMap.swift */, 4C3BEFD12819DB9B00B3DE84 /* ProfileModel.swift */, @@ -565,6 +580,7 @@ children = ( 4CF0ABDF2981A83000D66079 /* Muting */, 4CC7AAEE297F11B300430951 /* Events */, + 3AA24800297E3DAE0090C62D /* Reposts */, 4CB88394296F7F8100DC99E7 /* Reactions */, 4CB88387296AF97C00DC99E7 /* ActionBar */, 4CE4F9E228528C5200C00DD9 /* AddRelayView.swift */, @@ -616,6 +632,7 @@ 6439E013296790CF0020672B /* ProfileZoomView.swift */, 4CF0ABD529817F5B00D66079 /* ReportView.swift */, 4CF0ABE42981EE0C00D66079 /* EULAView.swift */, + 3AA247FE297E3D900090C62D /* RepostsView.swift */, ); path = Views; sourceTree = "<group>"; @@ -992,6 +1009,7 @@ 4CC7AAED297F0B9E00430951 /* Highlight.swift in Sources */, 4C285C8A2838B985008A31F1 /* ProfilePictureSelector.swift in Sources */, 4C75EFB92804A2740006080F /* EventView.swift in Sources */, + 3AA247FD297E3CFF0090C62D /* RepostsModel.swift in Sources */, 4C7FF7D52823313F009601DB /* Mentions.swift in Sources */, 4C633350283D40E500B1C9C3 /* HomeModel.swift in Sources */, 4C987B57283FD07F0042CE38 /* FollowersModel.swift in Sources */, @@ -1060,6 +1078,7 @@ 4C75EFAF28049D350006080F /* NostrFilter.swift in Sources */, 4C3EA64C28FF59AC00C48A62 /* bech32_util.c in Sources */, 4C363A9C282838B9006E126D /* EventRef.swift in Sources */, + 3AA24802297E3DC20090C62D /* RepostView.swift in Sources */, 4CD7641B28A1641400B6928F /* EndBlock.swift in Sources */, 4C3EA66528FF5F6800C48A62 /* mem.c in Sources */, 4CF0ABE52981EE0C00D66079 /* EULAView.swift in Sources */, @@ -1110,6 +1129,7 @@ 4C0A3F97280F8E02000448DE /* ThreadView.swift in Sources */, 4C06670B28FDE64700038D2A /* damus.c in Sources */, 4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */, + 3AA247FF297E3D900090C62D /* RepostsView.swift in Sources */, 4C99737B28C92A9200E53835 /* ChatroomMetadata.swift in Sources */, 4CC7AAF4297F18B400430951 /* ReplyDescription.swift in Sources */, 4C75EFA427FA577B0006080F /* PostView.swift in Sources */, diff --git a/damus/Models/RepostsModel.swift b/damus/Models/RepostsModel.swift @@ -0,0 +1,77 @@ +// +// RepostsModel.swift +// damus +// +// Created by Terry Yiu on 1/22/23. +// + +import Foundation + +class RepostsModel: ObservableObject { + let state: DamusState + let target: String + let sub_id: String + let profiles_id: String + + @Published var reposts: [NostrEvent] + + init (state: DamusState, target: String) { + self.state = state + self.target = target + self.sub_id = UUID().description + self.profiles_id = UUID().description + self.reposts = [] + } + + func get_filter() -> NostrFilter { + var filter = NostrFilter.filter_kinds([NostrKind.boost.rawValue]) + filter.referenced_ids = [target] + filter.limit = 500 + return filter + } + + func subscribe() { + let filter = get_filter() + let filters = [filter] + self.state.pool.subscribe(sub_id: sub_id, filters: filters, handler: handle_nostr_event) + } + + func unsubscribe() { + self.state.pool.unsubscribe(sub_id: sub_id) + } + + func handle_event(relay_id: String, ev: NostrEvent) { + guard ev.kind == NostrKind.boost.rawValue else { + return + } + + guard let reposted_event = last_etag(tags: ev.tags) else { + return + } + + guard reposted_event == self.target else { + return + } + + if insert_uniq_sorted_event(events: &self.reposts, new_ev: ev, cmp: { a, b in a.created_at < b.created_at } ) { + objectWillChange.send() + } + } + + func handle_nostr_event(relay_id: String, ev: NostrConnectionEvent) { + guard case .nostr_event(let nev) = ev else { + return + } + + switch nev { + case .event(_, let ev): + handle_event(relay_id: relay_id, ev: ev) + + case .notice(_): + break + case .eose(_): + load_profiles(profiles_subid: profiles_id, relay_id: relay_id, events: reposts, damus_state: state) + break + } + } +} diff --git a/damus/Views/ActionBar/EventDetailBar.swift b/damus/Views/ActionBar/EventDetailBar.swift @@ -15,7 +15,10 @@ struct EventDetailBar: View { var body: some View { HStack { if bar.boosts > 0 { - Text("\(Text("\(bar.boosts)", comment: "Number of reposts.").font(.body.bold())) \(Text(String(format: NSLocalizedString("reposts_count", comment: "Part of a larger sentence to describe how many reposts there are."), bar.boosts)).foregroundColor(.gray))", comment: "Sentence composed of 2 variables to describe how many reposts. In source English, the first variable is the number of reposts, and the second variable is 'Repost' or 'Reposts'.") + NavigationLink(destination: RepostsView(damus_state: state, model: RepostsModel(state: state, target: target))) { + Text("\(Text("\(bar.boosts)", comment: "Number of reposts.").font(.body.bold())) \(Text(String(format: NSLocalizedString("reposts_count", comment: "Part of a larger sentence to describe how many reposts there are."), bar.boosts)).foregroundColor(.gray))", comment: "Sentence composed of 2 variables to describe how many reposts. In source English, the first variable is the number of reposts, and the second variable is 'Repost' or 'Reposts'.") + } + .buttonStyle(PlainButtonStyle()) } if bar.likes > 0 { diff --git a/damus/Views/Reposts/RepostView.swift b/damus/Views/Reposts/RepostView.swift @@ -0,0 +1,24 @@ +// +// RepostView.swift +// damus +// +// Created by Terry Yiu on 1/22/23. +// + +import SwiftUI + +struct RepostView: View { + let damus_state: DamusState + let repost: NostrEvent + + var body: some View { + FollowUserView(target: .pubkey(repost.pubkey), damus_state: damus_state) + } +} + +struct RepostView_Previews: PreviewProvider { + static var previews: some View { + RepostView(damus_state: test_damus_state(), repost: NostrEvent(id: "", content: "", pubkey: "")) + } +} + diff --git a/damus/Views/RepostsView.swift b/damus/Views/RepostsView.swift @@ -0,0 +1,38 @@ +// +// RepostsView.swift +// damus +// +// Created by Terry Yiu on 1/22/23. +// + +import SwiftUI + +struct RepostsView: View { + let damus_state: DamusState + @StateObject var model: RepostsModel + + var body: some View { + ScrollView { + LazyVStack { + ForEach(model.reposts, id: \.id) { ev in + RepostView(damus_state: damus_state, repost: ev) + } + } + .padding() + } + .navigationBarTitle(NSLocalizedString("Reposts", comment: "Navigation bar title for Reposts view.")) + .onAppear { + model.subscribe() + } + .onDisappear { + model.unsubscribe() + } + } +} + +struct RepostsView_Previews: PreviewProvider { + static var previews: some View { + let state = test_damus_state() + RepostsView(damus_state: state, model: RepostsModel(state: state, target: "pubkey")) + } +}