Immediately search for events and profiles

Instead of having to click twice

Changelog-Changed: Immediately search for events and profiles
This commit is contained in:
William Casarin
2023-03-05 16:39:00 -05:00
parent df076b03fd
commit dffb60a601
6 changed files with 240 additions and 141 deletions

View File

@@ -147,7 +147,7 @@ struct ContentView: View {
search_open = false
isSideBarOpened = false
}
var timelineNavItem: Text {
switch selected_timeline {
case .home:
@@ -199,7 +199,7 @@ struct ContentView: View {
EmptyView()
}
}
.navigationBarTitle(timelineNavItem, displayMode: .inline)
.navigationBarTitle(timeline_name(selected_timeline), displayMode: .inline)
.toolbar {
ToolbarItem(placement: .principal) {
VStack {
@@ -348,7 +348,7 @@ struct ContentView: View {
active_profile = ref.ref_id
profile_open = true
} else if ref.key == "e" {
find_event(state: damus_state!, evid: ref.ref_id, find_from: nil) { ev in
find_event(state: damus_state!, evid: ref.ref_id, search_type: .event, find_from: nil) { ev in
if let ev {
active_event = ev
}
@@ -767,7 +767,7 @@ func setup_notifications() {
}
func find_event(state: DamusState, evid: String, find_from: [String]?, callback: @escaping (NostrEvent?) -> ()) {
func find_event(state: DamusState, evid: String, search_type: SearchType, find_from: [String]?, callback: @escaping (NostrEvent?) -> ()) {
if let ev = state.events.lookup(evid) {
callback(ev)
return
@@ -776,7 +776,13 @@ func find_event(state: DamusState, evid: String, find_from: [String]?, callback:
let subid = UUID().description
var has_event = false
var filter = NostrFilter.filter_ids([ evid ])
var filter = search_type == .event ? NostrFilter.filter_ids([ evid ]) : NostrFilter.filter_authors([ evid ])
if search_type == .profile {
filter.kinds = [0]
}
filter.limit = 1
var attempts = 0
@@ -808,3 +814,20 @@ func find_event(state: DamusState, evid: String, find_from: [String]?, callback:
}
}
func timeline_name(_ timeline: Timeline?) -> String {
guard let timeline else {
return ""
}
switch timeline {
case .home:
return "Home"
case .notifications:
return "Notifications"
case .search:
return "Universe 🛸"
case .dms:
return "DMs"
}
}

View File

@@ -1,53 +0,0 @@
//
// SearchedEventView.swift
// damus
//
// Created by William Casarin on 2023-03-03.
//
import SwiftUI
enum EventSearchState {
case searching
case not_found
case found(NostrEvent)
}
struct SearchedEventView: View {
let state: DamusState
let event_id: String
@State var search_state: EventSearchState = .searching
var body: some View {
Group {
switch search_state {
case .not_found:
Text("Event could not be found")
case .searching:
Text("Searching...")
case .found(let ev):
let thread = ThreadModel(event: ev, damus_state: state)
let dest = ThreadView(state: state, thread: thread)
NavigationLink(destination: dest) {
EventView(damus: state, event: ev)
}
.buttonStyle(.plain)
}
}
.onAppear {
find_event(state: state, evid: event_id, find_from: nil) { ev in
if let ev {
self.search_state = .found(ev)
} else {
self.search_state = .not_found
}
}
}
}
}
struct SearchedEventView_Previews: PreviewProvider {
static var previews: some View {
SearchedEventView(state: test_damus_state(), event_id: "event_id")
}
}

View File

@@ -0,0 +1,112 @@
//
// SearchingEventView.swift
// damus
//
// Created by William Casarin on 2023-03-05.
//
import SwiftUI
enum SearchState {
case searching
case found(NostrEvent)
case found_profile(String)
case not_found
}
enum SearchType {
case event
case profile
}
struct SearchingEventView: View {
let state: DamusState
let evid: String
let search_type: SearchType
@State var search_state: SearchState = .searching
var bech32_evid: String {
guard let bytes = hex_decode(evid) else {
return evid
}
let noteid = bech32_encode(hrp: "note", bytes)
return abbrev_pubkey(noteid)
}
var search_name: String {
switch search_type {
case .profile:
return "profile"
case .event:
return "note"
}
}
var body: some View {
Group {
switch search_state {
case .searching:
HStack(spacing: 10) {
Text("Looking for \(search_name)...", comment: "Label that appears when searching for note or profile")
ProgressView()
.progressViewStyle(.circular)
}
case .found(let ev):
NavigationLink(destination: ThreadView(state: state, thread: ThreadModel(event: ev, damus_state: state))) {
EventView(damus: state, event: ev)
}
.buttonStyle(PlainButtonStyle())
case .found_profile(let pk):
NavigationLink(destination: ProfileView(damus_state: state, pubkey: pk)) {
FollowUserView(target: .pubkey(pk), damus_state: state)
}
.buttonStyle(PlainButtonStyle())
case .not_found:
Text("\(search_name.capitalized) not found", comment: "When a note or profile is not found when searching for it via its note id")
}
}
.onAppear {
switch search_type {
case .event:
if let ev = state.events.lookup(evid) {
self.search_state = .found(ev)
return
}
case .profile:
if state.profiles.lookup(id: evid) != nil {
self.search_state = .found_profile(evid)
return
}
}
find_event(state: state, evid: evid, search_type: search_type, find_from: nil) { ev in
if let ev {
self.search_state = .found(ev)
} else {
self.search_state = .not_found
}
}
}
}
}
struct SearchingEventView_Previews: PreviewProvider {
static var previews: some View {
let state = test_damus_state()
SearchingEventView(state: state, evid: test_event.id, search_type: .event)
}
}
enum EventSearchState {
case searching
case not_found
case found(NostrEvent)
case found_profile(String)
}

View File

@@ -0,0 +1,20 @@
//
// SearchingProfileView.swift
// damus
//
// Created by William Casarin on 2023-03-05.
//
import SwiftUI
struct SearchingProfileView: View {
var body: some View {
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
}
}
struct SearchingProfileView_Previews: PreviewProvider {
static var previews: some View {
SearchingProfileView()
}
}

View File

@@ -18,6 +18,7 @@ enum Search {
struct SearchResultsView: View {
let damus_state: DamusState
@Binding var search: String
@State var result: Search? = nil
func ProfileSearchResult(pk: String, res: Profile) -> some View {
@@ -43,36 +44,23 @@ struct SearchResultsView: View {
case .profile(let prof):
let decoded = try? bech32_decode(prof)
let hex = hex_encode(decoded!.data)
let prof_view = ProfileView(damus_state: damus_state, pubkey: hex)
NavigationLink(destination: prof_view) {
Text("Goto profile \(prof)", comment: "Navigation link to go to 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)
NavigationLink(destination: prof_view) {
Text("Goto profile \(h)", comment: "Navigation link to go to profile referenced by hex code.")
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)
}
/*
VStack(spacing: 50) {
NavigationLink(destination: ev_view) {
Text("Goto post \(h)", comment: "Navigation link to go to post referenced by hex code.")
}
}
*/
case .note(let nid):
/*
let decoded = try? bech32_decode(nid)
let hex = hex_encode(decoded!.data)
let ev_view = ThreadView(state: state, ev: ev)
*/
Text("Todo: fix this")
/*
NavigationLink(destination: ev_view) {
Text("Goto post \(nid)", comment: "Navigation link to go to post referenced by note ID.")
}
*/
SearchingEventView(state: damus_state, evid: hex, search_type: .event)
case .none:
Text("none", comment: "No search results.")
}
@@ -81,66 +69,14 @@ struct SearchResultsView: View {
}
}
func search_changed(_ new: String) {
guard new.count != 0 else {
return
}
if new.first! == "#" {
let ht = String(new.dropFirst())
self.result = .hashtag(ht)
return
}
if hex_decode(new) != nil, new.count == 64 {
self.result = .hex(new)
return
}
if new.starts(with: "npub") {
if (try? bech32_decode(new)) != nil {
self.result = .profile(new)
return
}
}
if new.starts(with: "note") {
if (try? bech32_decode(new)) != nil {
self.result = .note(new)
return
}
}
let profs = damus_state.profiles.profiles.enumerated()
let results: [(String, Profile)] = 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 lownip05 = damus_state.profiles.is_validated(pk).map { $0.host.lowercased() }
let lowdisp = prof.display_name.map { $0.lowercased() }
let ok = new.count == 1 ?
((lowname?.starts(with: new) ?? false) ||
(lownip05?.starts(with: new) ?? false) ||
(lowdisp?.starts(with: new) ?? false)) : (pk.starts(with: new) || String(new.dropFirst()) == pk
|| lowname?.contains(new) ?? false
|| lownip05?.contains(new) ?? false
|| lowdisp?.contains(new) ?? false)
if ok {
acc.append((pk, prof))
}
}
self.result = .profiles(results)
}
var body: some View {
MainContent
.frame(maxHeight: .infinity)
.onAppear {
search_changed(search)
self.result = search_changed(profiles: damus_state.profiles, search)
}
.onChange(of: search) { new in
search_changed(new)
self.result = search_changed(profiles: damus_state.profiles, new)
}
}
}
@@ -152,3 +88,52 @@ struct SearchResultsView_Previews: PreviewProvider {
}
}
*/
func search_changed(profiles: Profiles, _ new: String) -> Search? {
guard new.count != 0 else {
return nil
}
if new.first! == "#" {
let ht = String(new.dropFirst())
return .hashtag(ht)
}
if hex_decode(new) != nil, new.count == 64 {
return .hex(new)
}
if new.starts(with: "npub") {
if (try? bech32_decode(new)) != nil {
return .profile(new)
}
}
if new.starts(with: "note") {
if (try? bech32_decode(new)) != nil {
return .note(new)
}
}
let profs = profiles.profiles.enumerated()
let results: [(String, Profile)] = 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 lownip05 = profiles.is_validated(pk).map { $0.host.lowercased() }
let lowdisp = prof.display_name.map { $0.lowercased() }
let ok = new.count == 1 ?
((lowname?.starts(with: new) ?? false) ||
(lownip05?.starts(with: new) ?? false) ||
(lowdisp?.starts(with: new) ?? false)) : (pk.starts(with: new) || String(new.dropFirst()) == pk
|| lowname?.contains(new) ?? false
|| lownip05?.contains(new) ?? false
|| lowdisp?.contains(new) ?? false)
if ok {
acc.append((pk, prof))
}
}
return .profiles(results)
}