synpad 0.1.0

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

use crate::room::call::call_state::{CallEndReason, CallState, CallStatus, CallType};
use crate::state::app_state::AppState;

pub const ELEMENT_CALL_BASE_URL: &str = "https://call.element.io";

pub fn build_element_call_url(client: &matrix_sdk::Client, room_id: &matrix_sdk::ruma::RoomId) -> String {
    let call_url = format!(
        "{}/room?roomId={}",
        ELEMENT_CALL_BASE_URL,
        urlencoding::encode(room_id.as_str()),
    );

    format!(
        "{}&homeserver={}",
        call_url,
        urlencoding::encode(client.homeserver().as_str()),
    )
}

#[derive(Clone, Debug, PartialEq)]
enum ElementCallState {
    Idle,
    Active,
    Ended,
}

#[component]
pub fn ElementCallWidget(room_id: String) -> Element {
    let mut state = use_context::<Signal<AppState>>();
    let mut link_copied = use_signal(|| false);

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

    let call_snapshot = state.read().call_state.clone();
    let current_call_state = match call_snapshot.status {
        CallStatus::InCall => ElementCallState::Active,
        CallStatus::Ended(_) => ElementCallState::Ended,
        _ => ElementCallState::Idle,
    };
    let muted = call_snapshot.is_muted;
    let camera = call_snapshot.is_video_enabled;
    let sharing = call_snapshot.is_screen_sharing;
    let copied = *link_copied.read();
    let on_hold = call_snapshot.is_on_hold;

    let full_url = call_snapshot
        .widget_url
        .clone()
        .or_else(|| {
            let client = state.read().client.clone()?;
            let room_id = matrix_sdk::ruma::OwnedRoomId::try_from(room_id.as_str()).ok()?;
            Some(build_element_call_url(&client, room_id.as_ref()))
        })
        .unwrap_or_else(|| ELEMENT_CALL_BASE_URL.to_string());

    let url_for_browser = full_url.clone();
    let url_for_active_browser = full_url.clone();
    let url_for_active_display = full_url.clone();
    let url_for_copy = full_url.clone();

    let rid = room_id.clone();
    let on_join_call = move |_| {
        let mut s = state.write();
        if let Ok(parsed_room_id) = matrix_sdk::ruma::OwnedRoomId::try_from(rid.as_str()) {
            let call_type = s.call_state.call_type.clone();
            let widget_url = s.call_state.widget_url.clone();
            s.call_state = CallState {
                status: CallStatus::InCall,
                room_id: Some(parsed_room_id),
                call_type: call_type.clone(),
                is_muted: false,
                is_video_enabled: matches!(call_type, CallType::Video),
                is_screen_sharing: false,
                duration_secs: s.call_state.duration_secs,
                call_id: s.call_state.call_id.clone(),
                participants: s.call_state.participants.clone(),
                is_on_hold: false,
                is_group_call: true,
                widget_url,
            };
        }
    };

    let on_leave_call = move |_| {
        let mut s = state.write();
        s.call_state.status = CallStatus::Ended(CallEndReason::HungUp);
        s.call_state.is_on_hold = false;
    };

    let on_toggle_mute = move |_| {
        let mut s = state.write();
        s.call_state.is_muted = !s.call_state.is_muted;
    };

    let on_toggle_camera = move |_| {
        let mut s = state.write();
        s.call_state.is_video_enabled = !s.call_state.is_video_enabled;
    };

    let on_toggle_screen = move |_| {
        let mut s = state.write();
        s.call_state.is_screen_sharing = !s.call_state.is_screen_sharing;
    };

    let on_toggle_hold = move |_| {
        let mut s = state.write();
        let next_hold = !s.call_state.is_on_hold;
        s.call_state.is_on_hold = next_hold;
        if next_hold {
            s.call_state.is_muted = true;
            s.call_state.is_video_enabled = false;
        } else if matches!(s.call_state.call_type, CallType::Video) {
            s.call_state.is_video_enabled = true;
        }
    };

    let on_copy_link = move |_| {
        tracing::info!("Element Call link copied: {}", url_for_copy);
        link_copied.set(true);
        spawn(async move {
            tokio::time::sleep(std::time::Duration::from_secs(2)).await;
            link_copied.set(false);
        });
    };

    rsx! {
        div {
            class: "element-call",

            div {
                class: "element-call__header",
                div {
                    class: "element-call__branding",
                    span { class: "element-call__logo", "Element Call" }
                }
                span {
                    class: if matches!(current_call_state, ElementCallState::Active) {
                        "element-call__status element-call__status--active"
                    } else {
                        "element-call__status"
                    },
                    if on_hold {
                        "On hold"
                    } else if matches!(current_call_state, ElementCallState::Active) {
                        "Connected"
                    } else {
                        "Ready"
                    }
                }
            }

            match current_call_state {
                ElementCallState::Idle => rsx! {
                    div {
                        class: "element-call__join-screen",
                        div {
                            class: "element-call__join-info",
                            h3 { "Start a Call" }
                            p { "Open Element Call for this room. Other room members can join from the same room." }
                        }

                        div {
                            class: "element-call__preview",
                            div {
                                class: "element-call__preview-placeholder",
                                "Browser-based call"
                            }
                        }

                        div {
                            class: "element-call__join-actions",
                            button {
                                class: "btn btn--primary element-call__join-btn",
                                onclick: on_join_call,
                                "Join Call"
                            }
                            a {
                                class: "element-call__browser-link",
                                href: "{url_for_browser}",
                                target: "_blank",
                                rel: "noopener noreferrer",
                                "Open in browser"
                            }
                        }
                    }
                },

                ElementCallState::Active => rsx! {
                    div {
                        class: "element-call__active-banner",
                        div {
                            class: "element-call__active-banner-icon",
                            span { class: "element-call__pulse-dot" }
                        }
                        div {
                            class: "element-call__active-banner-text",
                            h3 { class: "element-call__active-title", "{room_name}" }
                            p {
                                class: "element-call__active-subtitle",
                                if on_hold {
                                    "This call is on hold in the desktop client. Resume it before rejoining in the browser."
                                } else {
                                    "Use the browser session below for the live media stream."
                                }
                            }
                        }
                    }

                    div {
                        class: "element-call__url-section",
                        div { class: "element-call__url-label", "Call URL" }
                        div {
                            class: "element-call__url-row",
                            code {
                                class: "element-call__url-code",
                                "{url_for_active_display}"
                            }
                            button {
                                class: if copied {
                                    "element-call__copy-btn element-call__copy-btn--copied"
                                } else {
                                    "element-call__copy-btn"
                                },
                                onclick: on_copy_link,
                                if copied { "Copied!" } else { "Copy Link" }
                            }
                            a {
                                class: "element-call__open-btn",
                                href: "{url_for_active_browser}",
                                target: "_blank",
                                rel: "noopener noreferrer",
                                "Open in browser"
                            }
                        }
                    }

                    div {
                        class: "element-call__participants-area",
                        div {
                            class: "element-call__self-view",
                            if on_hold {
                                "Call on hold"
                            } else if camera {
                                "Camera enabled"
                            } else {
                                "Camera off"
                            }
                        }
                    }

                    div {
                        class: "element-call__controls",

                        button {
                            class: if muted {
                                "element-call__control-btn element-call__control-btn--active"
                            } else {
                                "element-call__control-btn"
                            },
                            title: if muted { "Unmute" } else { "Mute" },
                            onclick: on_toggle_mute,
                            if muted { "Unmute" } else { "Mute" }
                        }

                        button {
                            class: if !camera {
                                "element-call__control-btn element-call__control-btn--active"
                            } else {
                                "element-call__control-btn"
                            },
                            title: if camera { "Turn off camera" } else { "Turn on camera" },
                            onclick: on_toggle_camera,
                            if camera { "Camera Off" } else { "Camera On" }
                        }

                        button {
                            class: if sharing {
                                "element-call__control-btn element-call__control-btn--active"
                            } else {
                                "element-call__control-btn"
                            },
                            title: if sharing { "Stop sharing screen" } else { "Share screen" },
                            onclick: on_toggle_screen,
                            if sharing { "Stop Share" } else { "Share Screen" }
                        }

                        button {
                            class: if on_hold {
                                "element-call__control-btn element-call__control-btn--active"
                            } else {
                                "element-call__control-btn"
                            },
                            title: if on_hold { "Resume call" } else { "Hold call" },
                            onclick: on_toggle_hold,
                            if on_hold { "Resume" } else { "Hold" }
                        }

                        button {
                            class: "element-call__control-btn element-call__control-btn--hangup",
                            title: "Leave call",
                            onclick: on_leave_call,
                            "Leave"
                        }
                    }
                },

                ElementCallState::Ended => rsx! {
                    div {
                        class: "element-call__ended",
                        h3 { "Call Ended" }
                        p { "You have left the Element Call session." }
                        div {
                            class: "element-call__ended-actions",
                            button {
                                class: "btn btn--primary",
                                onclick: move |_| {
                                    let mut s = state.write();
                                    s.call_state.status = CallStatus::Connecting;
                                    s.call_state.is_on_hold = false;
                                },
                                "Rejoin"
                            }
                        }
                    }
                },
            }
        }
    }
}