notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

commit 58b15d99d7be25d868d50a298d3fabad88de7ae3
parent 64d3a0842e641282cd017e1773987e0f55c218ed
Author: kernelkind <kernelkind@gmail.com>
Date:   Sun, 25 May 2025 18:27:20 -0400

add `SearchType`

Signed-off-by: kernelkind <kernelkind@gmail.com>

Diffstat:
Mcrates/notedeck_columns/src/ui/search/mod.rs | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 119 insertions(+), 2 deletions(-)

diff --git a/crates/notedeck_columns/src/ui/search/mod.rs b/crates/notedeck_columns/src/ui/search/mod.rs @@ -1,9 +1,9 @@ use egui::{vec2, Align, Color32, CornerRadius, RichText, Stroke, TextEdit}; -use enostr::KeypairUnowned; +use enostr::{KeypairUnowned, NoteId, Pubkey}; use crate::ui::timeline::TimelineTabView; use egui_winit::clipboard::Clipboard; -use nostrdb::{Filter, Transaction}; +use nostrdb::{Filter, Ndb, Transaction}; use notedeck::{MuteFun, NoteAction, NoteContext, NoteRef}; use notedeck_ui::{icons::search_icon, jobs::JobsCache, padding, NoteOptions}; use std::time::{Duration, Instant}; @@ -221,3 +221,120 @@ fn search_box(query: &mut SearchQueryState, ui: &mut egui::Ui, clipboard: &mut C }) .inner } + +#[derive(Debug, Eq, PartialEq)] +pub enum SearchType { + String, + NoteId(NoteId), + Profile(Pubkey), + Hashtag(String), +} + +impl SearchType { + fn get_type(query: &str) -> Self { + if query.len() == 63 && query.starts_with("note1") { + if let Some(noteid) = NoteId::from_bech(query) { + return SearchType::NoteId(noteid); + } + } else if query.len() == 63 && query.starts_with("npub1") { + if let Ok(pk) = Pubkey::try_from_bech32_string(query, false) { + return SearchType::Profile(pk); + } + } else if query.chars().nth(0).is_some_and(|c| c == '#') { + if let Some(hashtag) = query.get(1..) { + return SearchType::Hashtag(hashtag.to_string()); + } + } + + SearchType::String + } + + fn search( + &self, + raw_query: &String, + ndb: &Ndb, + txn: &Transaction, + max_results: u64, + ) -> Option<Vec<NoteRef>> { + match self { + SearchType::String => search_string(raw_query, ndb, txn, max_results), + SearchType::NoteId(noteid) => search_note(noteid, ndb, txn).map(|n| vec![n]), + SearchType::Profile(pk) => search_pk(pk, ndb, txn, max_results), + SearchType::Hashtag(hashtag) => search_hashtag(hashtag, ndb, txn, max_results), + } + } +} + +fn search_string( + query: &String, + ndb: &Ndb, + txn: &Transaction, + max_results: u64, +) -> Option<Vec<NoteRef>> { + let filter = Filter::new() + .search(query) + .kinds([1]) + .limit(max_results) + .build(); + + // TODO: execute in thread + + let before = Instant::now(); + let qrs = ndb.query(txn, &[filter], max_results as i32); + let after = Instant::now(); + let duration = after - before; + + if duration > Duration::from_millis(20) { + warn!( + "query took {:?}... let's update this to use a thread!", + after - before + ); + } + + match qrs { + Ok(qrs) => { + info!("queried '{}' and got {} results", query, qrs.len()); + + return Some(qrs.into_iter().map(NoteRef::from_query_result).collect()); + } + + Err(err) => { + error!("fulltext query failed: {err}") + } + } + + None +} + +fn search_note(noteid: &NoteId, ndb: &Ndb, txn: &Transaction) -> Option<NoteRef> { + ndb.get_note_by_id(txn, noteid.bytes()) + .ok() + .map(|n| NoteRef::from_note(&n)) +} + +fn search_pk(pk: &Pubkey, ndb: &Ndb, txn: &Transaction, max_results: u64) -> Option<Vec<NoteRef>> { + let filter = Filter::new() + .authors([pk.bytes()]) + .kinds([1]) + .limit(max_results) + .build(); + + let qrs = ndb.query(txn, &[filter], max_results as i32).ok()?; + Some(qrs.into_iter().map(NoteRef::from_query_result).collect()) +} + +fn search_hashtag( + hashtag_name: &str, + ndb: &Ndb, + txn: &Transaction, + max_results: u64, +) -> Option<Vec<NoteRef>> { + let filter = Filter::new() + .kinds([1]) + .limit(max_results) + .tags([hashtag_name], 't') + .build(); + + let qrs = ndb.query(txn, &[filter], max_results as i32).ok()?; + Some(qrs.into_iter().map(NoteRef::from_query_result).collect()) +}