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:
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())
+}