synpad 0.1.0

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

use crate::utils::media_helpers::fit_dimensions;

/// Full-screen image viewer overlay for viewing images at full resolution.
#[component]
pub fn ImageViewer(
    image_url: String,
    alt_text: String,
    original_width: Option<u32>,
    original_height: Option<u32>,
    on_close: EventHandler<()>,
) -> Element {
    let mut is_zoomed = use_signal(|| false);
    let mut rotation = use_signal(|| 0i32);

    let toggle_zoom = move |_| {
        is_zoomed.set(!is_zoomed());
    };

    let close_viewer = move |_| {
        on_close.call(());
    };

    let rotate_cw = move |_| {
        let r = (*rotation.read() + 90) % 360;
        rotation.set(r);
    };

    let rotate_ccw = move |_| {
        let r = (*rotation.read() + 270) % 360;
        rotation.set(r);
    };

    let current_rotation = *rotation.read();

    let zoom_class = if is_zoomed() {
        "image-viewer__img image-viewer__img--zoomed"
    } else {
        "image-viewer__img"
    };

    // Compute fitted dimensions for the non-zoomed view.
    let (display_w, display_h) = fit_dimensions(
        original_width.unwrap_or(800),
        original_height.unwrap_or(600),
        1200,
        900,
    );

    rsx! {
        div {
            class: "image-viewer",
            onclick: close_viewer,

            div {
                class: "image-viewer__backdrop",
            }

            div {
                class: "image-viewer__toolbar",
                onclick: move |evt| evt.stop_propagation(),

                span {
                    class: "image-viewer__title",
                    "{alt_text}"
                }

                div {
                    class: "image-viewer__actions",

                    button {
                        class: "image-viewer__action-btn",
                        title: "Rotate left",
                        onclick: rotate_ccw,
                        ""
                    }

                    button {
                        class: "image-viewer__action-btn",
                        title: "Rotate right",
                        onclick: rotate_cw,
                        ""
                    }

                    button {
                        class: "image-viewer__action-btn",
                        title: if is_zoomed() { "Zoom out" } else { "Zoom in" },
                        onclick: toggle_zoom,
                        if is_zoomed() { "-" } else { "+" }
                    }

                    a {
                        class: "image-viewer__action-btn",
                        href: "{image_url}",
                        target: "_blank",
                        title: "Open original",
                        "Open"
                    }

                    button {
                        class: "image-viewer__action-btn image-viewer__close-btn",
                        title: "Close",
                        onclick: close_viewer,
                        "X"
                    }
                }
            }

            div {
                class: "image-viewer__content",
                onclick: move |evt| evt.stop_propagation(),

                img {
                    class: "{zoom_class}",
                    src: "{image_url}",
                    alt: "{alt_text}",
                    style: "transform: rotate({current_rotation}deg);",
                    width: if !is_zoomed() { format!("{display_w}") } else { String::new() },
                    height: if !is_zoomed() { format!("{display_h}") } else { String::new() },
                    onclick: toggle_zoom,
                }
            }
        }
    }
}