synpad 0.1.0

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

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

/// Video room component.
/// Video rooms are persistent rooms where a call is always active.
/// They use Element Call (or Jitsi) widget embedded in the room.
#[component]
pub fn VideoRoom(room_id: String) -> Element {
    let state = use_context::<Signal<AppState>>();
    let mut is_joined = use_signal(|| false);
    let participants = use_signal(Vec::<VideoParticipant>::new);

    let room_name = state
        .read()
        .rooms
        .values()
        .find(|r| r.room_id.to_string() == room_id)
        .map(|r| r.display_name.clone())
        .unwrap_or_else(|| "Video Room".to_string());

    rsx! {
        div {
            class: "video-room",

            // Video room header
            div {
                class: "video-room__header",
                h3 { "{room_name}" }
                span {
                    class: "video-room__status",
                    if *is_joined.read() { "In call" } else { "Not joined" }
                }
            }

            if !*is_joined.read() {
                // Join prompt
                div {
                    class: "video-room__join-prompt",
                    div {
                        class: "video-room__preview",
                        Avatar {
                            name: room_name.clone(),
                            url: None::<String>,
                            size: 80,
                        }
                        h4 { "{room_name}" }
                        p { "This is a video room. Join to participate in the ongoing call." }
                    }
                    div {
                        class: "video-room__join-actions",
                        button {
                            class: "btn btn--primary",
                            onclick: move |_| is_joined.set(true),
                            "Join with video"
                        }
                        button {
                            class: "btn btn--secondary",
                            onclick: move |_| is_joined.set(true),
                            "Join without video"
                        }
                    }
                }
            } else {
                // Video grid
                div {
                    class: "video-room__grid",

                    // Self video tile
                    div {
                        class: "video-room__tile video-room__tile--self",
                        div {
                            class: "video-room__tile-video",
                            // Camera placeholder
                            Avatar {
                                name: "You".to_string(),
                                url: None::<String>,
                                size: 64,
                            }
                        }
                        span { class: "video-room__tile-name", "You" }
                    }

                    // Participant tiles
                    for p in participants.read().iter() {
                        {
                            let name = p.display_name.clone();
                            let avatar = p.avatar_url.clone();
                            rsx! {
                                div {
                                    class: "video-room__tile",
                                    div {
                                        class: "video-room__tile-video",
                                        Avatar {
                                            name: name.clone(),
                                            url: avatar,
                                            size: 64,
                                        }
                                    }
                                    span { class: "video-room__tile-name", "{name}" }
                                }
                            }
                        }
                    }
                }

                // Call controls
                div {
                    class: "video-room__controls",
                    button {
                        class: "call-view__btn",
                        title: "Toggle microphone",
                        "🎤"
                    }
                    button {
                        class: "call-view__btn",
                        title: "Toggle camera",
                        "📹"
                    }
                    button {
                        class: "call-view__btn",
                        title: "Share screen",
                        "🖥"
                    }
                    button {
                        class: "call-view__btn call-view__btn--hangup",
                        onclick: move |_| is_joined.set(false),
                        "Leave"
                    }
                }
            }
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
struct VideoParticipant {
    user_id: String,
    display_name: String,
    avatar_url: Option<String>,
    has_video: bool,
    is_muted: bool,
}