damus

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

commit 8eb013f1f70b686e146edfa2efb053de0b996cd7
parent 4f459d128a5761143c4ec71af63b56091c95a46b
Author: William Casarin <jb55@jb55.com>
Date:   Tue,  2 May 2023 08:22:36 -0700

Search hashtags automatically

Changelog-Changed: Search hashtags automatically

Diffstat:
Mdamus/Models/SearchModel.swift | 2++
Mdamus/Views/SearchResultsView.swift | 177+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
2 files changed, 122 insertions(+), 57 deletions(-)

diff --git a/damus/Models/SearchModel.swift b/damus/Models/SearchModel.swift @@ -87,6 +87,8 @@ class SearchModel: ObservableObject { return } + self.loading = false + if sub_id == self.sub_id { load_profiles(profiles_subid: self.profiles_subid, relay_id: relay_id, load: .from_events(self.events.all_events), damus_state: state) } diff --git a/damus/Views/SearchResultsView.swift b/damus/Views/SearchResultsView.swift @@ -7,82 +7,132 @@ import SwiftUI -enum Search { +struct MultiSearch { + let hashtag: String + let profiles: [SearchedUser] +} + +enum Search: Identifiable { case profiles([SearchedUser]) case hashtag(String) case profile(String) case note(String) case nip05(String) case hex(String) + case multi(MultiSearch) + + var id: String { + switch self { + case .profiles: return "profiles" + case .hashtag: return "hashtag" + case .profile: return "profile" + case .note: return "note" + case .nip05: return "nip05" + case .hex: return "hex" + case .multi: return "multi" + } + } } -struct SearchResultsView: View { +struct AnySearchResultsView: View { let damus_state: DamusState - @Binding var search: String + let searches: [Search] - @State var result: Search? = nil + var body: some View { + VStack { + ForEach(searches) { r in + InnerSearchResults(damus_state: damus_state, search: r) + } + } + } +} + +struct InnerSearchResults: View { + let damus_state: DamusState + let search: Search? func ProfileSearchResult(pk: String) -> some View { FollowUserView(target: .pubkey(pk), damus_state: damus_state) } - var MainContent: some View { - ScrollView { - Group { - switch result { - case .profiles(let results): - LazyVStack { - ForEach(results) { prof in - ProfileSearchResult(pk: prof.pubkey) - } - } - case .hashtag(let ht): - let search_model = SearchModel(state: damus_state, search: .filter_hashtag([ht])) - let dst = SearchView(appstate: damus_state, search: search_model) - NavigationLink(destination: dst) { - Text("Search hashtag: #\(ht)", comment: "Navigation link to search hashtag.") - } - - case .nip05(let addr): - SearchingEventView(state: damus_state, evid: addr, search_type: .nip05) - - case .profile(let prof): - let decoded = try? bech32_decode(prof) - let hex = hex_encode(decoded!.data) - - SearchingEventView(state: damus_state, evid: hex, search_type: .profile) - case .hex(let h): - //let prof_view = ProfileView(damus_state: damus_state, pubkey: h) - //let ev_view = ThreadView(damus: damus_state, event_id: h) - - VStack(spacing: 10) { - SearchingEventView(state: damus_state, evid: h, search_type: .event) - - SearchingEventView(state: damus_state, evid: h, search_type: .profile) - } - - case .note(let nid): - let decoded = try? bech32_decode(nid) - let hex = hex_encode(decoded!.data) + func HashtagSearch(_ ht: String) -> some View { + let search_model = SearchModel(state: damus_state, search: .filter_hashtag([ht])) + let dst = SearchView(appstate: damus_state, search: search_model) + return NavigationLink(destination: dst) { + Text("Search hashtag: #\(ht)", comment: "Navigation link to search hashtag.") + } + } + + func ProfilesSearch(_ results: [SearchedUser]) -> some View { + return LazyVStack { + ForEach(results) { prof in + ProfileSearchResult(pk: prof.pubkey) + } + } + } + + var body: some View { + Group { + switch search { + case .profiles(let results): + ProfilesSearch(results) + + case .hashtag(let ht): + HashtagSearch(ht) + + case .nip05(let addr): + SearchingEventView(state: damus_state, evid: addr, search_type: .nip05) + + case .profile(let prof): + let decoded = try? bech32_decode(prof) + let hex = hex_encode(decoded!.data) + + SearchingEventView(state: damus_state, evid: hex, search_type: .profile) + case .hex(let h): + //let prof_view = ProfileView(damus_state: damus_state, pubkey: h) + //let ev_view = ThreadView(damus: damus_state, event_id: h) + + VStack(spacing: 10) { + SearchingEventView(state: damus_state, evid: h, search_type: .event) - SearchingEventView(state: damus_state, evid: hex, search_type: .event) - case .none: - Text("none", comment: "No search results.") + SearchingEventView(state: damus_state, evid: h, search_type: .profile) + } + + case .note(let nid): + let decoded = try? bech32_decode(nid) + let hex = hex_encode(decoded!.data) + + SearchingEventView(state: damus_state, evid: hex, search_type: .event) + case .multi(let multi): + VStack { + HashtagSearch(multi.hashtag) + ProfilesSearch(multi.profiles) } + + case .none: + Text("none", comment: "No search results.") } - .padding() } } +} + +struct SearchResultsView: View { + let damus_state: DamusState + @Binding var search: String + @State var result: Search? = nil var body: some View { - MainContent - .frame(maxHeight: .infinity) - .onAppear { - self.result = search_for_string(profiles: damus_state.profiles, search) - } - .onChange(of: search) { new in - self.result = search_for_string(profiles: damus_state.profiles, new) - } + ScrollView { + InnerSearchResults(damus_state: damus_state, search: result) + .padding() + } + .frame(maxHeight: .infinity) + .onAppear { + self.result = search_for_string(profiles: damus_state.profiles, search) + } + .onChange(of: search) { new in + self.result = search_for_string(profiles: damus_state.profiles, new) + } } } @@ -107,8 +157,7 @@ func search_for_string(profiles: Profiles, _ new: String) -> Search? { } if new.first! == "#" { - let ht = String(new.dropFirst().filter{$0 != " "}) - return .hashtag(ht) + return .hashtag(make_hashtagable(new)) } if hex_decode(new) != nil, new.count == 64 { @@ -127,7 +176,21 @@ func search_for_string(profiles: Profiles, _ new: String) -> Search? { } } - return .profiles(search_profiles(profiles: profiles, search: new)) + let multisearch = MultiSearch(hashtag: make_hashtagable(new), profiles: search_profiles(profiles: profiles, search: new)) + return .multi(multisearch) +} + +func make_hashtagable(_ str: String) -> String { + var new = str + guard str.utf8.count > 0 else { + return str + } + + if new.hasPrefix("#") { + new = String(new.dropFirst()) + } + + return String(new.filter{$0 != " "}) } func search_profiles(profiles: Profiles, search: String) -> [SearchedUser] {