synpad 0.1.0

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

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

/// Preview bar shown when an invited room is selected - shows Accept/Decline.
#[component]
pub fn InvitePreviewBar(
    room_id: String,
    room_name: String,
    avatar_url: Option<String>,
    inviter: Option<String>,
) -> Element {
    let mut state = use_context::<Signal<AppState>>();
    let mut is_accepting = use_signal(|| false);
    let mut is_declining = use_signal(|| false);
    let mut error_msg = use_signal(|| Option::<String>::None);

    let accept_room_id = room_id.clone();
    let on_accept = move |_| {
        let rid = accept_room_id.clone();
        is_accepting.set(true);
        error_msg.set(None);
        spawn(async move {
            let client = { state.read().client.clone() };
            if let Some(client) = client {
                if let Ok(room_id) = OwnedRoomId::try_from(rid.as_str()) {
                    if let Some(room) = client.get_room(&room_id) {
                        match room.join().await {
                            Ok(_) => {
                                tracing::info!("Accepted invite for {room_id}");
                                state.write().active_room_id = Some(room_id);
                            }
                            Err(e) => {
                                tracing::error!("Failed to accept invite: {e}");
                                error_msg.set(Some(format!("Failed to join: {e}")));
                            }
                        }
                    }
                }
            }
            is_accepting.set(false);
        });
    };

    let decline_room_id = room_id.clone();
    let on_decline = move |_| {
        let rid = decline_room_id.clone();
        is_declining.set(true);
        error_msg.set(None);
        spawn(async move {
            let client = { state.read().client.clone() };
            if let Some(client) = client {
                if let Ok(room_id) = OwnedRoomId::try_from(rid.as_str()) {
                    if let Some(room) = client.get_room(&room_id) {
                        match room.leave().await {
                            Ok(_) => {
                                tracing::info!("Declined invite for {room_id}");
                                state.write().active_room_id = None;
                            }
                            Err(e) => {
                                tracing::error!("Failed to decline invite: {e}");
                                error_msg.set(Some(format!("Failed to decline: {e}")));
                            }
                        }
                    }
                }
            }
            is_declining.set(false);
        });
    };

    let inviter_text = inviter
        .map(|i| format!("{i} invited you"))
        .unwrap_or_else(|| "You've been invited".to_string());

    let has_error = error_msg.read().is_some();
    let err_text = error_msg.read().clone().unwrap_or_default();

    rsx! {
        div {
            class: "invite-preview",

            div {
                class: "invite-preview__info",
                Avatar {
                    name: room_name.clone(),
                    url: avatar_url,
                    size: 48,
                }
                div {
                    class: "invite-preview__text",
                    h3 { class: "invite-preview__room-name", "{room_name}" }
                    p { class: "invite-preview__inviter", "{inviter_text}" }
                }
            }

            if has_error {
                div {
                    class: "invite-preview__error",
                    "{err_text}"
                }
            }

            div {
                class: "invite-preview__actions",
                button {
                    class: "btn btn--secondary",
                    disabled: *is_declining.read() || *is_accepting.read(),
                    onclick: on_decline,
                    if *is_declining.read() { "Declining..." } else { "Decline" }
                }
                button {
                    class: "btn btn--primary",
                    disabled: *is_accepting.read() || *is_declining.read(),
                    onclick: on_accept,
                    if *is_accepting.read() { "Accepting..." } else { "Accept" }
                }
            }
        }
    }
}