synpad 0.1.0

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

use crate::components::avatar::Avatar;
use crate::components::modal::Modal;
use crate::state::app_state::AppState;

/// Room preview / peek dialog.
/// Shows room information before joining.
#[component]
pub fn RoomPreviewDialog(
    room_id: String,
    room_name: Option<String>,
    via_servers: Vec<String>,
    on_close: EventHandler<()>,
) -> Element {
    let state = use_context::<Signal<AppState>>();
    let mut preview_name = use_signal(|| room_name.clone().unwrap_or_else(|| room_id.clone()));
    let mut preview_topic = use_signal(|| Option::<String>::None);
    let mut preview_members = use_signal(|| 0u64);
    let mut preview_avatar = use_signal(|| Option::<String>::None);
    let mut loading = use_signal(|| true);
    let mut joining = use_signal(|| false);
    let mut error = use_signal(|| Option::<String>::None);

    let rid = room_id.clone();
    use_effect(move || {
        let rid = rid.clone();
        spawn(async move {
            let client = { state.read().client.clone() };
            if let Some(client) = client {
                // Try to get room summary via the room summary API
                // (available on some homeservers)
                if let Ok(room_id) = matrix_sdk::ruma::OwnedRoomId::try_from(rid.as_str()) {
                    if let Some(room) = client.get_room(&room_id) {
                        // Already known room
                        if let Ok(name) = room.display_name().await {
                            preview_name.set(name.to_string());
                        }
                        preview_topic.set(room.topic());
                        preview_members.set(room.joined_members_count());
                        preview_avatar.set(room.avatar_url().map(|u| u.to_string()));
                    }
                }
            }
            loading.set(false);
        });
    });

    let rid_join = room_id.clone();
    let on_join = move |_| {
        joining.set(true);
        error.set(None);
        let rid = rid_join.clone();
        spawn(async move {
            let client = { state.read().client.clone() };
            if let Some(client) = client {
                if let Ok(room_id) = matrix_sdk::ruma::OwnedRoomId::try_from(rid.as_str()) {
                    match client.join_room_by_id(&room_id).await {
                        Ok(_) => {
                            tracing::info!("Joined room {room_id}");
                            on_close.call(());
                        }
                        Err(e) => {
                            error.set(Some(format!("Failed to join: {e}")));
                        }
                    }
                }
            }
            joining.set(false);
        });
    };

    let name = preview_name.read().clone();
    let topic = preview_topic.read().clone();
    let members = *preview_members.read();
    let avatar = preview_avatar.read().clone();

    rsx! {
        Modal {
            title: "Room Preview".to_string(),
            on_close: move |_| on_close.call(()),

            div {
                class: "room-preview",

                if *loading.read() {
                    div {
                        class: "room-preview__loading",
                        div { class: "spinner" }
                        span { "Loading room info..." }
                    }
                } else {
                    div {
                        class: "room-preview__header",
                        Avatar {
                            name: name.clone(),
                            url: avatar,
                            size: 64,
                        }
                        div {
                            class: "room-preview__info",
                            h3 { class: "room-preview__name", "{name}" }
                            if let Some(ref topic) = topic {
                                p { class: "room-preview__topic", "{topic}" }
                            }
                            span { class: "room-preview__members", "{members} members" }
                        }
                    }

                    if let Some(ref err) = *error.read() {
                        div { class: "room-preview__error", "{err}" }
                    }

                    div {
                        class: "room-preview__actions",
                        button {
                            class: "btn btn--secondary",
                            onclick: move |_| on_close.call(()),
                            "Cancel"
                        }
                        button {
                            class: "btn btn--primary",
                            disabled: *joining.read(),
                            onclick: on_join,
                            if *joining.read() { "Joining..." } else { "Join Room" }
                        }
                    }
                }
            }
        }
    }
}