113 lines
4.3 KiB
Rust
113 lines
4.3 KiB
Rust
// src/search/event.rs
|
|
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) {
|
|
// Use current view path to access the active form
|
|
if let (Some(profile), Some(table)) = (
|
|
app_state.current_view_profile_name.clone(),
|
|
app_state.current_view_table_name.clone(),
|
|
) {
|
|
let path = format!("{}/{}", profile, table);
|
|
if let Some(fs) = app_state.form_state_for_path(&path) {
|
|
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;
|
|
}
|
|
|
|
Ok(outcome_message)
|
|
}
|