synpad 0.1.0

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

use crate::components::avatar::Avatar;
use crate::room_list::create_space_dialog::CreateSpaceDialog;
use crate::room_list::space_hierarchy::SpaceHierarchyDialog;
use crate::room_list::space_settings_dialog::SpaceSettingsDialog;
use crate::state::app_state::AppState;
use crate::state::room_state::RoomFilter;

/// Spaces bar on the far-left sidebar.
#[component]
pub fn SpacesBar() -> Element {
    let mut state = use_context::<Signal<AppState>>();
    let mut show_create_space = use_signal(|| false);
    let mut show_space_settings = use_signal(|| Option::<(String, String)>::None);
    let mut show_space_hierarchy = use_signal(|| Option::<(String, String)>::None);
    let current_filter = state.read().room_filter.clone();

    // Collect spaces from the room list (rooms that are spaces)
    let spaces: Vec<(String, String)> = {
        let state_read = state.read();
        if let Some(client) = &state_read.client {
            client
                .rooms()
                .iter()
                .filter(|room| room.is_space())
                .filter_map(|room| {
                    let room_id = room.room_id().to_string();
                    let name = room.cached_display_name()
                        .map(|n| n.to_string())
                        .unwrap_or_else(|| room_id.clone());
                    Some((room_id, name))
                })
                .collect()
        } else {
            Vec::new()
        }
    };

    rsx! {
        div {
            class: "spaces-bar",

            // Home button (all rooms)
            button {
                class: if matches!(current_filter, RoomFilter::All) {
                    "spaces-bar__item spaces-bar__item--active"
                } else {
                    "spaces-bar__item"
                },
                onclick: move |_| {
                    let mut w = state.write();
                    w.room_filter = RoomFilter::All;
                    w.update_sorted_rooms();
                },
                title: "Home",
                div {
                    class: "spaces-bar__home-icon",
                    "H"
                }
            }

            // Divider
            div { class: "spaces-bar__divider" }

            // DMs filter button
            button {
                class: if matches!(current_filter, RoomFilter::DirectMessages) {
                    "spaces-bar__item spaces-bar__item--active"
                } else {
                    "spaces-bar__item"
                },
                onclick: move |_| {
                    let mut w = state.write();
                    w.room_filter = RoomFilter::DirectMessages;
                    w.update_sorted_rooms();
                },
                title: "People",
                div {
                    class: "spaces-bar__dm-icon",
                    "P"
                }
            }

            // Favorites filter button
            button {
                class: if matches!(current_filter, RoomFilter::Favorites) {
                    "spaces-bar__item spaces-bar__item--active"
                } else {
                    "spaces-bar__item"
                },
                onclick: move |_| {
                    let mut w = state.write();
                    w.room_filter = RoomFilter::Favorites;
                    w.update_sorted_rooms();
                },
                title: "Favorites",
                div {
                    class: "spaces-bar__fav-icon",
                    ""
                }
            }

            if !spaces.is_empty() {
                div { class: "spaces-bar__divider" }
            }

            // Space list from SDK
            for (space_id, space_name) in spaces.iter() {
                {
                    let sid = space_id.clone();
                    let sid_for_settings = space_id.clone();
                    let sname = space_name.clone();
                    let sname_for_settings = space_name.clone();
                    let is_active = if let RoomFilter::Space(ref active_id) = current_filter {
                        active_id.to_string() == sid
                    } else {
                        false
                    };
                    rsx! {
                        button {
                            class: if is_active { "spaces-bar__item spaces-bar__item--active" } else { "spaces-bar__item" },
                            onclick: move |_| {
                                if let Ok(rid) = matrix_sdk::ruma::OwnedRoomId::try_from(sid.as_str()) {
                                    let mut w = state.write();
                                    w.room_filter = RoomFilter::Space(rid);
                                    w.update_sorted_rooms();
                                }
                            },
                            oncontextmenu: move |evt| {
                                evt.prevent_default();
                                show_space_settings.set(Some((sid_for_settings.clone(), sname_for_settings.clone())));
                            },
                            title: "{sname} (right-click for settings)",
                            Avatar {
                                name: sname.clone(),
                                url: None::<String>,
                                size: 36,
                            }
                        }
                    }
                }
            }

            // Create space button
            button {
                class: "spaces-bar__item spaces-bar__create",
                title: "Create space",
                onclick: move |_| show_create_space.set(true),
                "+"
            }

            // Create space dialog
            if *show_create_space.read() {
                CreateSpaceDialog {
                    on_close: move |_| show_create_space.set(false),
                }
            }

            // Explore space button (shows when a space is active)
            {
                let active_space_info = if let RoomFilter::Space(ref rid) = current_filter {
                    spaces.iter()
                        .find(|(sid, _)| sid == &rid.to_string())
                        .map(|(sid, sname)| (sid.clone(), sname.clone()))
                } else {
                    None
                };
                if let Some((sid, sname)) = active_space_info {
                    rsx! {
                        button {
                            class: "spaces-bar__item spaces-bar__explore",
                            title: "Explore space rooms",
                            onclick: move |_| {
                                show_space_hierarchy.set(Some((sid.clone(), sname.clone())));
                            },
                            "🔍"
                        }
                    }
                } else {
                    rsx! {}
                }
            }

            // Space hierarchy dialog
            if let Some(ref hierarchy) = *show_space_hierarchy.read() {
                {
                    let sid = hierarchy.0.clone();
                    let sname = hierarchy.1.clone();
                    rsx! {
                        SpaceHierarchyDialog {
                            space_id: sid,
                            space_name: sname,
                            on_close: move |_| show_space_hierarchy.set(None),
                        }
                    }
                }
            }

            // Space settings dialog
            if let Some(ref settings) = *show_space_settings.read() {
                {
                    let sid = settings.0.clone();
                    let sname = settings.1.clone();
                    rsx! {
                        SpaceSettingsDialog {
                            space_id: sid,
                            space_name: sname,
                            on_close: move |_| show_space_settings.set(None),
                        }
                    }
                }
            }
        }
    }
}