synpad 0.1.0

A full-featured Matrix chat client built with Dioxus
use dioxus::prelude::*;

use crate::state::app_state::AppState;

/// Global spotlight search overlay (Ctrl+K).
/// Searches across rooms, users, and messages.
#[component]
pub fn SpotlightSearch() -> Element {
    let mut state = use_context::<Signal<AppState>>();
    let mut query = use_signal(|| String::new());
    let mut selected_idx = use_signal(|| 0usize);

    // Collect matching rooms
    let q = query.read().to_lowercase();
    let room_results: Vec<(String, String, bool)> = if q.is_empty() {
        // Show recent rooms (breadcrumbs) when no query
        let s = state.read();
        s.breadcrumbs.iter().take(8).filter_map(|rid| {
            s.rooms.get(rid).map(|r| (rid.to_string(), r.display_name.clone(), r.is_direct))
        }).collect()
    } else {
        let s = state.read();
        s.rooms.values()
            .filter(|r| r.display_name.to_lowercase().contains(&q))
            .take(10)
            .map(|r| (r.room_id.to_string(), r.display_name.clone(), r.is_direct))
            .collect()
    };

    let result_count = room_results.len();

    let mut on_close = move |_: ()| {
        state.write().spotlight_search_open = false;
    };

    rsx! {
        div {
            class: "spotlight-overlay",
            onclick: move |_| on_close(()),

            div {
                class: "spotlight-dialog",
                onclick: move |evt| evt.stop_propagation(),

                // Search input
                div {
                    class: "spotlight-dialog__input-row",
                    span { class: "spotlight-dialog__search-icon", "🔍" }
                    input {
                        r#type: "text",
                        class: "spotlight-dialog__input",
                        placeholder: "Search rooms, people, messages...",
                        value: "{query}",
                        oninput: move |evt| {
                            query.set(evt.value());
                            selected_idx.set(0);
                        },
                        onkeydown: move |evt: Event<KeyboardData>| {
                            match evt.key() {
                                Key::Escape => on_close(()),
                                Key::ArrowDown => {
                                    let idx = *selected_idx.read();
                                    if idx + 1 < result_count {
                                        selected_idx.set(idx + 1);
                                    }
                                }
                                Key::ArrowUp => {
                                    let idx = *selected_idx.read();
                                    if idx > 0 {
                                        selected_idx.set(idx - 1);
                                    }
                                }
                                Key::Enter => {
                                    let idx = *selected_idx.read();
                                    if idx < result_count {
                                        if let Some((rid, _, _)) = room_results.get(idx) {
                                            if let Ok(room_id) = matrix_sdk::ruma::OwnedRoomId::try_from(rid.as_str()) {
                                                let mut s = state.write();
                                                s.active_room_id = Some(room_id);
                                                s.spotlight_search_open = false;
                                            }
                                        }
                                    }
                                }
                                _ => {}
                            }
                        },
                        autofocus: true,
                    }
                    span {
                        class: "spotlight-dialog__shortcut",
                        "Esc"
                    }
                }

                // Results
                div {
                    class: "spotlight-dialog__results",

                    if room_results.is_empty() && !q.is_empty() {
                        div {
                            class: "spotlight-dialog__empty",
                            "No results found"
                        }
                    }

                    if !room_results.is_empty() {
                        div {
                            class: "spotlight-dialog__section-header",
                            if q.is_empty() { "Recent" } else { "Rooms" }
                        }
                    }

                    for (idx, (rid, name, is_dm)) in room_results.iter().enumerate() {
                        {
                            let rid = rid.clone();
                            let name = name.clone();
                            let is_selected = idx == *selected_idx.read();
                            let icon = if *is_dm { "👤" } else { "#" };
                            let selected_class = if is_selected {
                                "spotlight-dialog__result spotlight-dialog__result--selected"
                            } else {
                                "spotlight-dialog__result"
                            };
                            rsx! {
                                button {
                                    class: selected_class,
                                    onclick: move |_| {
                                        if let Ok(room_id) = matrix_sdk::ruma::OwnedRoomId::try_from(rid.as_str()) {
                                            let mut s = state.write();
                                            s.active_room_id = Some(room_id);
                                            s.spotlight_search_open = false;
                                        }
                                    },
                                    span { class: "spotlight-dialog__result-icon", "{icon}" }
                                    span { class: "spotlight-dialog__result-name", "{name}" }
                                }
                            }
                        }
                    }
                }

                // Footer
                div {
                    class: "spotlight-dialog__footer",
                    span { "↑↓ Navigate" }
                    span { "↵ Select" }
                    span { "Esc Close" }
                }
            }
        }
    }
}