damus

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

commit 200cbe3728a47df7bcd7fefc177c03223c6afdbb
parent e1c4f59e9a37cf07012bd712739a286e675bc117
Author: William Casarin <jb55@jb55.com>
Date:   Thu, 26 May 2022 08:35:19 -0700

followers

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

Diffstat:
Mdamus.xcodeproj/project.pbxproj | 4++++
Mdamus/ContentView.swift | 27+++------------------------
Adamus/Models/FollowersModel.swift | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdamus/Models/FollowingModel.swift | 2+-
Mdamus/Views/EventView.swift | 2+-
Mdamus/Views/FollowingView.swift | 23++++++++++++++++++++---
Mdamus/Views/ProfileView.swift | 31++++++++++++++++++++++++-------
7 files changed, 119 insertions(+), 36 deletions(-)

diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj @@ -71,6 +71,7 @@ 4C90BD18283A9EE5008EE7EF /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C90BD17283A9EE5008EE7EF /* LoginView.swift */; }; 4C90BD1A283AA67F008EE7EF /* Bech32.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C90BD19283AA67F008EE7EF /* Bech32.swift */; }; 4C90BD1C283AC38E008EE7EF /* Bech32Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C90BD1B283AC38E008EE7EF /* Bech32Tests.swift */; }; + 4C987B57283FD07F0042CE38 /* FollowersModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C987B56283FD07F0042CE38 /* FollowersModel.swift */; }; 4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */; }; 4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */; }; 4CACA9DC280C38C000D9BBE8 /* Profiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9DB280C38C000D9BBE8 /* Profiles.swift */; }; @@ -177,6 +178,7 @@ 4C90BD17283A9EE5008EE7EF /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = "<group>"; }; 4C90BD19283AA67F008EE7EF /* Bech32.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32.swift; sourceTree = "<group>"; }; 4C90BD1B283AC38E008EE7EF /* Bech32Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32Tests.swift; sourceTree = "<group>"; }; + 4C987B56283FD07F0042CE38 /* FollowersModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersModel.swift; sourceTree = "<group>"; }; 4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineView.swift; sourceTree = "<group>"; }; 4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyView.swift; sourceTree = "<group>"; }; 4CACA9DB280C38C000D9BBE8 /* Profiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Profiles.swift; sourceTree = "<group>"; }; @@ -253,6 +255,7 @@ 4C5F9113283D694D0052CD1C /* FollowTarget.swift */, 4C5F9115283D855D0052CD1C /* EventsModel.swift */, 4C5F9117283D88E40052CD1C /* FollowingModel.swift */, + 4C987B56283FD07F0042CE38 /* FollowersModel.swift */, ); path = Models; sourceTree = "<group>"; @@ -558,6 +561,7 @@ 4C75EFB92804A2740006080F /* EventView.swift in Sources */, 4C7FF7D52823313F009601DB /* Mentions.swift in Sources */, 4C633350283D40E500B1C9C3 /* HomeModel.swift in Sources */, + 4C987B57283FD07F0042CE38 /* FollowersModel.swift in Sources */, 4C363A9828283441006E126D /* TestingPrivate.swift in Sources */, 4C363A9028247A1D006E126D /* NostrLink.swift in Sources */, 4C0A3F8C280F5FCA000448DE /* ChatroomView.swift in Sources */, diff --git a/damus/ContentView.swift b/damus/ContentView.swift @@ -148,7 +148,8 @@ struct ContentView: View { Group { if let pk = self.active_profile { let profile_model = ProfileModel(pubkey: pk, damus: damus_state!) - ProfileView(damus_state: damus_state!, profile: profile_model) + let followers = FollowersModel(damus_state: damus_state!, target: pk) + ProfileView(damus_state: damus_state!, profile: profile_model, followers: followers) } else { EmptyView() } @@ -355,13 +356,11 @@ struct ContentView: View { } -/* struct ContentView_Previews: PreviewProvider { static var previews: some View { - ContentView() + ContentView(keypair: Keypair(pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681", privkey: nil)) } } - */ func get_since_time(last_event: NostrEvent?) -> Int64? { @@ -372,26 +371,6 @@ func get_since_time(last_event: NostrEvent?) -> Int64? { return nil } -/* -func fetch_profiles(relay: URL, pubkeys: [String]) { - return NostrFilter(ids: nil, kinds: 3, event_ids: nil, pubkeys: pubkeys, since: nil, until: nil, authors: pubkeys) -} - - -func nostr_req(relays: [URL], filter: NostrFilter) { - if relays.count == 0 { - return - } - let conn = NostrConnection(url: relay) { - } -} - - -func get_profiles() - -*/ - - func ws_nostr_event(relay: String, ev: WebSocketEvent) -> NostrEvent? { switch ev { case .binary(let dat): diff --git a/damus/Models/FollowersModel.swift b/damus/Models/FollowersModel.swift @@ -0,0 +1,66 @@ +// +// FollowersModel.swift +// damus +// +// Created by William Casarin on 2022-05-26. +// + +import Foundation + +class FollowersModel: ObservableObject { + let damus_state: DamusState + let target: String + var needs_sub: Bool = true + + @Published var contacts: [String] = [] + var has_contact: Set<String> = Set() + + let sub_id: String = UUID().description + + init(damus_state: DamusState, target: String) { + self.damus_state = damus_state + self.target = target + } + + func get_filter() -> NostrFilter { + var filter = NostrFilter.filter_contacts + filter.pubkeys = [target] + return filter + } + + func subscribe() { + let filter = get_filter() + let filters = [filter] + print_filters(relay_id: "following", filters: [filters]) + self.damus_state.pool.subscribe(sub_id: sub_id, filters: filters, handler: handle_event) + } + + func unsubscribe() { + self.damus_state.pool.unsubscribe(sub_id: sub_id) + } + + func handle_contact_event(_ ev: NostrEvent) { + if has_contact.contains(ev.pubkey) { + return + } + process_contact_event(contacts: damus_state.contacts, pubkey: damus_state.pubkey, ev: ev) + contacts.append(ev.pubkey) + has_contact.insert(ev.pubkey) + } + + func handle_event(relay_id: String, ev: NostrConnectionEvent) { + switch ev { + case .ws_event: + break + case .nostr_event(let nev): + switch nev { + case .event(_, let ev): + if ev.kind == 3 { + handle_contact_event(ev) + } + case .notice(let msg): + print("followingmodel notice: \(msg)") + } + } + } +} diff --git a/damus/Models/FollowingModel.swift b/damus/Models/FollowingModel.swift @@ -7,7 +7,7 @@ import Foundation -class FollowingModel: ObservableObject { +class FollowingModel { let damus_state: DamusState var needs_sub: Bool = true diff --git a/damus/Views/EventView.swift b/damus/Views/EventView.swift @@ -69,7 +69,7 @@ struct EventView: View { let profile = damus.profiles.lookup(id: event.pubkey) VStack { let pmodel = ProfileModel(pubkey: event.pubkey, damus: damus) - let pv = ProfileView(damus_state: damus, profile: pmodel) + let pv = ProfileView(damus_state: damus, profile: pmodel, followers: FollowersModel(damus_state: damus, target: event.pubkey)) NavigationLink(destination: pv) { ProfilePicView(pubkey: event.pubkey, size: PFP_SIZE, highlight: highlight, image_cache: damus.image_cache, profiles: damus.profiles) diff --git a/damus/Views/FollowingView.swift b/damus/Views/FollowingView.swift @@ -14,7 +14,8 @@ struct FollowUserView: View { var body: some View { HStack(alignment: .top) { let pmodel = ProfileModel(pubkey: target.pubkey, damus: damus_state) - let pv = ProfileView(damus_state: damus_state, profile: pmodel) + let followers = FollowersModel(damus_state: damus_state, target: target.pubkey) + let pv = ProfileView(damus_state: damus_state, profile: pmodel, followers: followers) NavigationLink(destination: pv) { ProfilePicView(pubkey: target.pubkey, size: PFP_SIZE, highlight: .none, image_cache: damus_state.image_cache, profiles: damus_state.profiles) @@ -35,11 +36,27 @@ struct FollowUserView: View { } } -struct FollowingView: View { +struct FollowersView: View { let damus_state: DamusState - @StateObject var following: FollowingModel + @EnvironmentObject var followers: FollowersModel + + var body: some View { + ScrollView { + LazyVStack(alignment: .leading) { + ForEach(followers.contacts, id: \.self) { pk in + FollowUserView(target: .pubkey(pk), damus_state: damus_state) + } + } + } + } + +} + +struct FollowingView: View { + let damus_state: DamusState + let following: FollowingModel var body: some View { ScrollView { diff --git a/damus/Views/ProfileView.swift b/damus/Views/ProfileView.swift @@ -50,6 +50,7 @@ struct ProfileView: View { @State private var selected_tab: ProfileTab = .posts @StateObject var profile: ProfileModel + @StateObject var followers: FollowersModel //@EnvironmentObject var profile: ProfileModel @@ -88,14 +89,27 @@ struct ProfileView: View { let contacts = contact.referenced_pubkeys.map { $0.ref_id } let following_model = FollowingModel(damus_state: damus_state, contacts: contacts) - NavigationLink(destination: FollowingView(damus_state: damus_state, following: following_model)) { - HStack { - Text("\(profile.following)") - Text("Following") - .foregroundColor(.gray) + HStack { + NavigationLink(destination: FollowingView(damus_state: damus_state, following: following_model)) { + HStack { + Text("\(profile.following)") + Text("Following") + .foregroundColor(.gray) + } + } + .buttonStyle(PlainButtonStyle()) + + let fview = FollowersView(damus_state: damus_state) + .environmentObject(followers) + NavigationLink(destination: fview) { + HStack { + Text("\(followers.contacts.count)") + Text("Followers") + .foregroundColor(.gray) + } } + .buttonStyle(PlainButtonStyle()) } - .buttonStyle(PlainButtonStyle()) } } } @@ -117,9 +131,11 @@ struct ProfileView: View { .navigationBarTitle("Profile") .onAppear() { profile.subscribe() + followers.subscribe() } .onDisappear { profile.unsubscribe() + followers.unsubscribe() // our profilemodel needs a bit more help } } @@ -128,8 +144,9 @@ struct ProfileView: View { struct ProfileView_Previews: PreviewProvider { static var previews: some View { let ds = test_damus_state() + let followers = FollowersModel(damus_state: ds, target: ds.pubkey) let profile_model = ProfileModel(pubkey: ds.pubkey, damus: ds) - ProfileView(damus_state: ds, profile: profile_model) + ProfileView(damus_state: ds, profile: profile_model, followers: followers) } }