search
This commit is contained in:
107
client/src/search/event.rs
Normal file
107
client/src/search/event.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
// src/search/event.rs
|
||||
use crate::search::state::SearchState;
|
||||
use crate::state::app::state::AppState;
|
||||
use crate::services::grpc_client::GrpcClient;
|
||||
use common::proto::komp_ac::search::search_response::Hit;
|
||||
use crossterm::event::KeyCode;
|
||||
use tokio::sync::mpsc;
|
||||
use tracing::{error, info};
|
||||
use std::collections::HashMap;
|
||||
use anyhow::Result;
|
||||
|
||||
pub async fn handle_search_palette_event(
|
||||
key_event: crossterm::event::KeyEvent,
|
||||
app_state: &mut AppState,
|
||||
grpc_client: &mut GrpcClient,
|
||||
search_result_sender: mpsc::UnboundedSender<Vec<Hit>>,
|
||||
) -> Result<Option<String>> {
|
||||
let mut should_close = false;
|
||||
let mut outcome_message = None;
|
||||
let mut trigger_search = false;
|
||||
|
||||
if let Some(search_state) = app_state.search_state.as_mut() {
|
||||
match key_event.code {
|
||||
KeyCode::Esc => {
|
||||
should_close = true;
|
||||
outcome_message = Some("Search cancelled".to_string());
|
||||
}
|
||||
KeyCode::Enter => {
|
||||
// Step 1: Extract the data we need while holding the borrow
|
||||
let maybe_data = search_state
|
||||
.results
|
||||
.get(search_state.selected_index)
|
||||
.map(|hit| (hit.id, hit.content_json.clone()));
|
||||
|
||||
// Step 2: Process outside the borrow
|
||||
if let Some((id, content_json)) = maybe_data {
|
||||
if let Ok(data) = serde_json::from_str::<HashMap<String, String>>(&content_json) {
|
||||
if let Some(fs) = app_state.form_state_mut() {
|
||||
let detached_pos = fs.total_count + 2;
|
||||
fs.update_from_response(&data, detached_pos);
|
||||
}
|
||||
should_close = true;
|
||||
outcome_message = Some(format!("Loaded record ID {}", id));
|
||||
}
|
||||
}
|
||||
}
|
||||
KeyCode::Up => search_state.previous_result(),
|
||||
KeyCode::Down => search_state.next_result(),
|
||||
KeyCode::Char(c) => {
|
||||
search_state.input.insert(search_state.cursor_position, c);
|
||||
search_state.cursor_position += 1;
|
||||
trigger_search = true;
|
||||
}
|
||||
KeyCode::Backspace => {
|
||||
if search_state.cursor_position > 0 {
|
||||
search_state.cursor_position -= 1;
|
||||
search_state.input.remove(search_state.cursor_position);
|
||||
trigger_search = true;
|
||||
}
|
||||
}
|
||||
KeyCode::Left => {
|
||||
search_state.cursor_position =
|
||||
search_state.cursor_position.saturating_sub(1);
|
||||
}
|
||||
KeyCode::Right => {
|
||||
if search_state.cursor_position < search_state.input.len() {
|
||||
search_state.cursor_position += 1;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if trigger_search {
|
||||
if let Some(search_state) = app_state.search_state.as_mut() {
|
||||
search_state.is_loading = true;
|
||||
search_state.results.clear();
|
||||
search_state.selected_index = 0;
|
||||
|
||||
let query = search_state.input.clone();
|
||||
let table_name = search_state.table_name.clone();
|
||||
let sender = search_result_sender.clone();
|
||||
let mut grpc_client = grpc_client.clone();
|
||||
|
||||
info!("Spawning search task for query: '{}'", query);
|
||||
tokio::spawn(async move {
|
||||
match grpc_client.search_table(table_name, query).await {
|
||||
Ok(response) => {
|
||||
let _ = sender.send(response.hits);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Search failed: {:?}", e);
|
||||
let _ = sender.send(vec![]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if should_close {
|
||||
app_state.search_state = None;
|
||||
app_state.ui.show_search_palette = false;
|
||||
app_state.ui.focus_outside_canvas = false;
|
||||
}
|
||||
|
||||
Ok(outcome_message)
|
||||
}
|
||||
Reference in New Issue
Block a user