damus

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

commit dff12702f5bde31b753f9c60bb8302eea9f28fb3
parent 2fc6e7ef221c01faa13c8c3e7ed75f677ebcebef
Author: William Casarin <jb55@jb55.com>
Date:   Mon,  6 Jun 2022 19:26:44 -0700

SearchHomeView working

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

Diffstat:
Mdamus.xcodeproj/project.pbxproj | 8++++++++
Mdamus/ContentView.swift | 2+-
Adamus/Models/SearchHomeModel.swift | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdamus/Views/SearchHomeView.swift | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Adamus/Views/SearchResultsView.swift | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdamus/Views/SearchView.swift | 9++++++---
6 files changed, 187 insertions(+), 6 deletions(-)

diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj @@ -49,6 +49,8 @@ 4C3BEFDC281DCE6100B3DE84 /* Liked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFDB281DCE6100B3DE84 /* Liked.swift */; }; 4C3BEFE0281DE1ED00B3DE84 /* DamusState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFDF281DE1ED00B3DE84 /* DamusState.swift */; }; 4C477C9E282C3A4800033AA3 /* TipCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C477C9D282C3A4800033AA3 /* TipCounter.swift */; }; + 4C5C7E68284ED36500A22DF5 /* SearchHomeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5C7E67284ED36500A22DF5 /* SearchHomeModel.swift */; }; + 4C5C7E6A284EDE2E00A22DF5 /* SearchResultsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5C7E69284EDE2E00A22DF5 /* SearchResultsView.swift */; }; 4C5F9114283D694D0052CD1C /* FollowTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5F9113283D694D0052CD1C /* FollowTarget.swift */; }; 4C5F9116283D855D0052CD1C /* EventsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5F9115283D855D0052CD1C /* EventsModel.swift */; }; 4C5F9118283D88E40052CD1C /* FollowingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5F9117283D88E40052CD1C /* FollowingModel.swift */; }; @@ -154,6 +156,8 @@ 4C3BEFDB281DCE6100B3DE84 /* Liked.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Liked.swift; sourceTree = "<group>"; }; 4C3BEFDF281DE1ED00B3DE84 /* DamusState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusState.swift; sourceTree = "<group>"; }; 4C477C9D282C3A4800033AA3 /* TipCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TipCounter.swift; sourceTree = "<group>"; }; + 4C5C7E67284ED36500A22DF5 /* SearchHomeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchHomeModel.swift; sourceTree = "<group>"; }; + 4C5C7E69284EDE2E00A22DF5 /* SearchResultsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultsView.swift; sourceTree = "<group>"; }; 4C5F9113283D694D0052CD1C /* FollowTarget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowTarget.swift; sourceTree = "<group>"; }; 4C5F9115283D855D0052CD1C /* EventsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventsModel.swift; sourceTree = "<group>"; }; 4C5F9117283D88E40052CD1C /* FollowingModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowingModel.swift; sourceTree = "<group>"; }; @@ -254,6 +258,7 @@ 4C5F9115283D855D0052CD1C /* EventsModel.swift */, 4C5F9117283D88E40052CD1C /* FollowingModel.swift */, 4C987B56283FD07F0042CE38 /* FollowersModel.swift */, + 4C5C7E67284ED36500A22DF5 /* SearchHomeModel.swift */, ); path = Models; sourceTree = "<group>"; @@ -291,6 +296,7 @@ 4C285C892838B985008A31F1 /* ProfilePictureSelector.swift */, 4C285C8D28399BFD008A31F1 /* SaveKeysView.swift */, 4C90BD17283A9EE5008EE7EF /* LoginView.swift */, + 4C5C7E69284EDE2E00A22DF5 /* SearchResultsView.swift */, ); path = Views; sourceTree = "<group>"; @@ -549,6 +555,7 @@ 4C285C86283892E7008A31F1 /* CreateAccountModel.swift in Sources */, 4C363A8C28236B92006E126D /* PubkeyView.swift in Sources */, 4C363A8628234FDE006E126D /* ImageCache.swift in Sources */, + 4C5C7E68284ED36500A22DF5 /* SearchHomeModel.swift in Sources */, 4C75EFB728049D990006080F /* RelayPool.swift in Sources */, 4CE6DEE927F7A08100C66700 /* ContentView.swift in Sources */, 4CEE2AF5280B29E600AB5EEF /* TimeAgo.swift in Sources */, @@ -591,6 +598,7 @@ 4C363A9C282838B9006E126D /* EventRef.swift in Sources */, 4C8682872814DE470026224F /* ProfileView.swift in Sources */, 4C5F9114283D694D0052CD1C /* FollowTarget.swift in Sources */, + 4C5C7E6A284EDE2E00A22DF5 /* SearchResultsView.swift in Sources */, 4CE6DF1627F8DEBF00C66700 /* RelayConnection.swift in Sources */, 4C3BEFD6281D995700B3DE84 /* ActionBarModel.swift in Sources */, 4C363AA428296DEE006E126D /* SearchModel.swift in Sources */, diff --git a/damus/ContentView.swift b/damus/ContentView.swift @@ -104,7 +104,7 @@ struct ContentView: View { } switch selected_timeline { case .search: - SearchHomeView() + SearchHomeView(damus_state: damus_state!, model: SearchHomeModel(pool: damus_state!.pool) ) case .home: PostingTimelineView diff --git a/damus/Models/SearchHomeModel.swift b/damus/Models/SearchHomeModel.swift @@ -0,0 +1,60 @@ +// +// SearchHomeModel.swift +// damus +// +// Created by William Casarin on 2022-06-06. +// + +import Foundation + + +/// The data model for the SearchHome view, typically something global-like +class SearchHomeModel: ObservableObject { + @Published var events: [NostrEvent] = [] + let pool: RelayPool + let sub_id = UUID().description + let limit: UInt32 = 1000 + + init(pool: RelayPool) { + self.pool = pool + } + + func get_base_filter() -> NostrFilter { + var filter = NostrFilter.filter_text + filter.limit = self.limit + filter.until = Int64(Date.now.timeIntervalSince1970) + return filter + } + + func subscribe() { + pool.subscribe(sub_id: sub_id, filters: [get_base_filter()], handler: handle_event) + } + + func unsubscribe() { + pool.unsubscribe(sub_id: sub_id) + } + + func handle_event(relay_id: String, conn_ev: NostrConnectionEvent) { + switch conn_ev { + case .ws_event: + break + case .nostr_event(let event): + switch event { + case .event(let sub_id, let ev): + guard sub_id == self.sub_id else { + return + } + guard self.events.count <= limit else { + return + } + if ev.kind == NostrKind.text.rawValue { + let _ = insert_uniq_sorted_event(events: &events, new_ev: ev) { + $0.created_at > $1.created_at + } + } + case .notice(let msg): + print("search home notice: \(msg)") + } + } + } +} diff --git a/damus/Views/SearchHomeView.swift b/damus/Views/SearchHomeView.swift @@ -6,15 +6,68 @@ // import SwiftUI +import CryptoKit struct SearchHomeView: View { + let damus_state: DamusState + @StateObject var model: SearchHomeModel + @State var search: String = "" + + var SearchInput: some View { + ZStack(alignment: .leading) { + TextField("", text: $search) + .padding(5) + .padding(.leading, 35) + .textInputAutocapitalization(.never) + Label("", systemImage: "magnifyingglass") + .padding(.leading, 10) + } + .background { + RoundedRectangle(cornerRadius: 20) + .foregroundColor(.gray.opacity(0.2)) + } + .padding() + } + + var GlobalContent: some View { + TimelineView(events: $model.events, damus: damus_state) + } + + var SearchContent: some View { + SearchResultsView(damus_state: damus_state, search: $search) + } + + var MainContent: some View { + Group { + if search.isEmpty { + GlobalContent + } else { + SearchContent + } + } + } + var body: some View { - Text("Search Home") + VStack { + SearchInput + + MainContent + } + .onAppear { + model.subscribe() + } + .onDisappear { + model.unsubscribe() + } } } struct SearchHomeView_Previews: PreviewProvider { static var previews: some View { - SearchHomeView() + let state = test_damus_state() + SearchHomeView( + damus_state: state, + model: SearchHomeModel(pool: state.pool) + ) } } diff --git a/damus/Views/SearchResultsView.swift b/damus/Views/SearchResultsView.swift @@ -0,0 +1,57 @@ +// +// SearchResultsView.swift +// damus +// +// Created by William Casarin on 2022-06-06. +// + +import SwiftUI + +struct SearchResultsView: View { + let damus_state: DamusState + @Binding var search: String + @State var results: [(String, Profile)] = [] + + func ProfileSearchResult(pk: String, res: Profile) -> some View { + FollowUserView(target: .pubkey(pk), damus_state: damus_state) + } + + var MainContent: some View { + VStack { + ForEach(results, id: \.0) { prof in + ProfileSearchResult(pk: prof.0, res: prof.1) + } + Spacer() + } + } + + func search_changed(_ new: String) { + let profs = damus_state.profiles.profiles.enumerated() + self.results = profs.reduce(into: []) { acc, els in + let pk = els.element.key + let prof = els.element.value.profile + let lowname = prof.name.map { $0.lowercased() } + let lowdisp = prof.display_name.map { $0.lowercased() } + let ok = new == pk || String(new.dropFirst()) == pk + || lowname?.contains(new) ?? false + || lowdisp?.contains(new) ?? false + if ok { + acc.append((pk, prof)) + } + } + + } + + var body: some View { + MainContent + .frame(maxHeight: .infinity) + .onChange(of: search) { new in search_changed(new) } + } +} + +struct SearchResultsView_Previews: PreviewProvider { + static var previews: some View { + let search = Binding<String>.init(get: { "jb55" }, set: { _ in }) + SearchResultsView(damus_state: test_damus_state(), search: search) + } +} diff --git a/damus/Views/SearchView.swift b/damus/Views/SearchView.swift @@ -37,10 +37,13 @@ func describe_search(_ filter: NostrFilter) -> String { return "Search" } -/* struct SearchView_Previews: PreviewProvider { static var previews: some View { - SearchView() + let test_state = test_damus_state() + let filter = NostrFilter.filter_hashtag(["bitcoin"]) + let pool = test_state.pool + let model = SearchModel(pool: pool, search: filter) + + SearchView(appstate: test_state, search: model) } } - */