synpad 0.1.0

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

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

/// Location sharing dialog - allows entering coordinates manually.
#[component]
pub fn LocationPicker(
    room_id: String,
    on_close: EventHandler<()>,
) -> Element {
    let state = use_context::<Signal<AppState>>();
    let mut latitude = use_signal(|| String::new());
    let mut longitude = use_signal(|| String::new());
    let mut description = use_signal(|| String::new());
    let mut is_sending = use_signal(|| false);
    let mut error_msg = use_signal(|| Option::<String>::None);

    let can_send = !latitude.read().is_empty() && !longitude.read().is_empty();

    rsx! {
        Modal {
            title: "Share Location".to_string(),
            on_close: move |_| on_close.call(()),
            div {
                class: "location-picker",

                p {
                    class: "location-picker__hint",
                    "Enter coordinates to share a location."
                }

                div {
                    class: "location-picker__field",
                    label { "Latitude" }
                    input {
                        r#type: "text",
                        placeholder: "e.g. 40.7128",
                        value: "{latitude}",
                        oninput: move |evt| latitude.set(evt.value()),
                    }
                }

                div {
                    class: "location-picker__field",
                    label { "Longitude" }
                    input {
                        r#type: "text",
                        placeholder: "e.g. -74.0060",
                        value: "{longitude}",
                        oninput: move |evt| longitude.set(evt.value()),
                    }
                }

                div {
                    class: "location-picker__field",
                    label { "Description (optional)" }
                    input {
                        r#type: "text",
                        placeholder: "e.g. Meeting point",
                        value: "{description}",
                        oninput: move |evt| description.set(evt.value()),
                    }
                }

                if let Some(ref err) = *error_msg.read() {
                    div { class: "location-picker__error", "{err}" }
                }

                div {
                    class: "location-picker__actions",
                    button {
                        class: "btn btn--secondary",
                        onclick: move |_| on_close.call(()),
                        "Cancel"
                    }
                    button {
                        class: "btn btn--primary",
                        disabled: !can_send || *is_sending.read(),
                        onclick: move |_| {
                            let lat = latitude.read().clone();
                            let lon = longitude.read().clone();
                            let desc = description.read().clone();
                            let rid = room_id.clone();
                            is_sending.set(true);
                            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) {
                                            let geo_uri = format!("geo:{},{}", lat, lon);
                                            let body = if desc.is_empty() {
                                                format!("Location: {}, {}", lat, lon)
                                            } else {
                                                format!("{} ({}, {})", desc, lat, lon)
                                            };

                                            use matrix_sdk::ruma::events::room::message::RoomMessageEventContent;
                                            use matrix_sdk::ruma::events::room::message::LocationMessageEventContent;

                                            let location_content = LocationMessageEventContent::new(body, geo_uri);
                                            let content = RoomMessageEventContent::new(
                                                matrix_sdk::ruma::events::room::message::MessageType::Location(location_content),
                                            );
                                            match room.send(content).await {
                                                Ok(_) => {
                                                    tracing::info!("Location sent");
                                                    on_close.call(());
                                                }
                                                Err(e) => {
                                                    error_msg.set(Some(format!("Failed to send: {e}")));
                                                }
                                            }
                                        }
                                    }
                                }
                                is_sending.set(false);
                            });
                        },
                        if *is_sending.read() { "Sending..." } else { "Share Location" }
                    }
                }
            }
        }
    }
}