damus

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

commit 4bf8a68c9c13e4af3548a55e7f40a92476a13d4e
parent 0a9ac9cb0db4c04f591c21f7823155ea81fab87e
Author: William Casarin <jb55@jb55.com>
Date:   Sun,  3 Dec 2023 22:13:46 -0800

search: add damus search ui

Diffstat:
Mdamus.xcodeproj/project.pbxproj | 5++++-
Mdamus/ContentView.swift | 16+++++-----------
Adamus/Views/Search/PullDownSearch.swift | 110+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdamus/Views/SearchHomeView.swift | 2+-
4 files changed, 120 insertions(+), 13 deletions(-)

diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj @@ -260,6 +260,7 @@ 4C9B0DF32A65C46800CBDA21 /* ProfileEditButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9B0DF22A65C46800CBDA21 /* ProfileEditButton.swift */; }; 4C9BB83129C0ED4F00FC4E37 /* DisplayName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9BB83029C0ED4F00FC4E37 /* DisplayName.swift */; }; 4C9BB83429C12D9900FC4E37 /* EventProfileName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9BB83329C12D9900FC4E37 /* EventProfileName.swift */; }; + 4C9D6D1B2B1D35D7004E5CD9 /* PullDownSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9D6D1A2B1D35D7004E5CD9 /* PullDownSearch.swift */; }; 4C9F18E229AA9B6C008C55EC /* CustomizeZapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9F18E129AA9B6C008C55EC /* CustomizeZapView.swift */; }; 4C9F18E429ABDE6D008C55EC /* MaybeAnonPfpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9F18E329ABDE6D008C55EC /* MaybeAnonPfpView.swift */; }; 4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */; }; @@ -1057,6 +1058,7 @@ 4C9B0DF22A65C46800CBDA21 /* ProfileEditButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileEditButton.swift; sourceTree = "<group>"; }; 4C9BB83029C0ED4F00FC4E37 /* DisplayName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayName.swift; sourceTree = "<group>"; }; 4C9BB83329C12D9900FC4E37 /* EventProfileName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventProfileName.swift; sourceTree = "<group>"; }; + 4C9D6D1A2B1D35D7004E5CD9 /* PullDownSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PullDownSearch.swift; sourceTree = "<group>"; }; 4C9F18E129AA9B6C008C55EC /* CustomizeZapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomizeZapView.swift; sourceTree = "<group>"; }; 4C9F18E329ABDE6D008C55EC /* MaybeAnonPfpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaybeAnonPfpView.swift; sourceTree = "<group>"; }; 4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineView.swift; sourceTree = "<group>"; }; @@ -2188,6 +2190,7 @@ children = ( 4CCEB7AD29B53D260078AA28 /* SearchingEventView.swift */, 4CCEB7AF29B5415A0078AA28 /* SearchingProfileView.swift */, + 4C9D6D1A2B1D35D7004E5CD9 /* PullDownSearch.swift */, ); path = Search; sourceTree = "<group>"; @@ -2867,7 +2870,6 @@ 4C32B9582A9AD44700DC3548 /* VeriferOptions.swift in Sources */, 4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */, 4C30AC7629A5770900E2BD5A /* NotificationItemView.swift in Sources */, - BA3759972ABCCF360018D73B /* CameraPreview.swift in Sources */, 4C86F7C42A76C44C00EC0817 /* ZappingNotify.swift in Sources */, 4C363A8428233689006E126D /* Parser.swift in Sources */, 3AAA95CA298DF87B00F3D526 /* TranslationService.swift in Sources */, @@ -2952,6 +2954,7 @@ 4CACA9DC280C38C000D9BBE8 /* Profiles.swift in Sources */, 4CE879582996C45300F758CC /* ZapsView.swift in Sources */, 4C30AC7429A5680900E2BD5A /* EventGroupView.swift in Sources */, + 4C9D6D1B2B1D35D7004E5CD9 /* PullDownSearch.swift in Sources */, 4C633352283D419F00B1C9C3 /* SignalModel.swift in Sources */, 4CFF8F6D29CD022E008DB934 /* WideEventView.swift in Sources */, 9609F058296E220800069BF3 /* BannerImageView.swift in Sources */, diff --git a/damus/ContentView.swift b/damus/ContentView.swift @@ -67,7 +67,7 @@ struct ContentView: View { @Environment(\.scenePhase) var scenePhase @State var active_sheet: Sheets? = nil - @State var damus_state: DamusState? = nil + @State var damus_state: DamusState! @SceneStorage("ContentView.selected_timeline") var selected_timeline: Timeline = .home @State var muting: Pubkey? = nil @State var confirm_mute: Bool = false @@ -133,10 +133,8 @@ struct ContentView: View { } func contentTimelineView(filter: (@escaping (NostrEvent) -> Bool)) -> some View { - ZStack { - if let damus = self.damus_state { - TimelineView<AnyView>(events: home.events, loading: .constant(false), damus: damus, show_friend_icon: false, filter: filter) - } + TimelineView(events: home.events, loading: .constant(false), damus: damus_state, show_friend_icon: false, filter: filter) { + PullDownSearchView(state: damus_state, on_cancel: {}) } } @@ -202,12 +200,8 @@ struct ContentView: View { func MaybeReportView(target: ReportTarget) -> some View { Group { - if let damus_state { - if let keypair = damus_state.keypair.to_full() { - ReportView(postbox: damus_state.postbox, target: target, keypair: keypair) - } else { - EmptyView() - } + if let keypair = damus_state.keypair.to_full() { + ReportView(postbox: damus_state.postbox, target: target, keypair: keypair) } else { EmptyView() } diff --git a/damus/Views/Search/PullDownSearch.swift b/damus/Views/Search/PullDownSearch.swift @@ -0,0 +1,110 @@ +// +// PullDownSearch.swift +// damus +// +// Created by William Casarin on 2023-12-03. +// + +import Foundation + +import SwiftUI + +struct PullDownSearchView: View { + @State private var search_text = "" + @State private var results: [NostrEvent] = [] + @State private var is_active: Bool = false + let state: DamusState + let on_cancel: () -> Void + + var body: some View { + VStack(alignment: .leading) { + HStack { + TextField("Search", text: $search_text) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .onChange(of: search_text) { newValue in + Task.detached { + let note_keys = state.ndb.text_search(query: newValue, limit: 16) + var res = [NostrEvent]() + // TODO: fix duplicate results from search + var keyset = Set<NoteKey>() + do { + let txn = NdbTxn(ndb: state.ndb) + for note_key in note_keys { + guard let note = state.ndb.lookup_note_by_key_with_txn(note_key, txn: txn) else { + continue + } + + if !keyset.contains(note_key) { + let owned_note = note.to_owned() + res.append(owned_note) + keyset.insert(note_key) + } + } + } + + let res_ = res + + Task { @MainActor [res_] in + results = res_ + } + } + } + .onTapGesture { + is_active = true + } + + if is_active { + Button(action: { + search_text = "" + end_editing() + on_cancel() + }, label: { + Text("Cancel") + }) + } + } + .padding() + + if results.count > 0 { + HStack { + Image("search") + Text(NSLocalizedString("Top hits", comment: "A label indicating that the notes being displayed below it are all top note search results")) + Spacer() + } + .padding(.horizontal) + .foregroundColor(.secondary) + + ForEach(results, id: \.self) { note in + EventView(damus: state, event: note) + .onTapGesture { + let event = note.get_inner_event(cache: state.events) ?? note + let thread = ThreadModel(event: event, damus_state: state) + state.nav.push(route: Route.Thread(thread: thread)) + } + } + + HStack { + Image("notes.fill") + Text(NSLocalizedString("Notes", comment: "A label indicating that the notes being displayed below it are from a timeline, not search results")) + Spacer() + } + .foregroundColor(.secondary) + .padding(.horizontal) + } else if results.count == 0 && !search_text.isEmpty { + HStack { + Image("search") + Text(NSLocalizedString("No results", comment: "A label indicating that note search resulted in no results")) + Spacer() + } + .padding(.horizontal) + .foregroundColor(.secondary) + } + } + } +} + +struct PullDownSearchView_Previews: PreviewProvider { + static var previews: some View { + PullDownSearchView(state: test_damus_state, on_cancel: {}) + } +} diff --git a/damus/Views/SearchHomeView.swift b/damus/Views/SearchHomeView.swift @@ -79,7 +79,7 @@ struct SearchHomeView: View { AnyView(VStack { SuggestedHashtagsView(damus_state: damus_state, max_items: 5, events: model.events) HStack { - Image(systemName: "bubble.fill") + Image("notes.fill") Text(NSLocalizedString("All recent notes", comment: "A label indicating that the notes being displayed below it are all recent notes")) Spacer() }