synpad 0.1.0

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

use crate::components::modal::Modal;
use crate::platform::file_dialog::{FileFilter, pick_file, pick_save_path};
use crate::state::app_state::AppState;

/// Key export/import dialog for E2E encryption keys.
#[component]
pub fn KeyExportDialog(on_close: EventHandler<()>) -> Element {
    let state = use_context::<Signal<AppState>>();
    let mut passphrase = use_signal(|| String::new());
    let mut confirm_passphrase = use_signal(|| String::new());
    let mut import_passphrase = use_signal(|| String::new());
    let mut status = use_signal(|| Option::<String>::None);
    let mut is_working = use_signal(|| false);
    let mut mode = use_signal(|| "export".to_string());

    rsx! {
        Modal {
            title: "E2E Key Export / Import".to_string(),
            on_close: move |_| on_close.call(()),

            div {
                class: "key-export",

                // Mode tabs
                div {
                    class: "key-export__tabs",
                    button {
                        class: if *mode.read() == "export" { "key-export__tab key-export__tab--active" } else { "key-export__tab" },
                        onclick: move |_| mode.set("export".to_string()),
                        "Export Keys"
                    }
                    button {
                        class: if *mode.read() == "import" { "key-export__tab key-export__tab--active" } else { "key-export__tab" },
                        onclick: move |_| mode.set("import".to_string()),
                        "Import Keys"
                    }
                }

                if let Some(ref msg) = *status.read() {
                    div {
                        class: "key-export__status",
                        "{msg}"
                    }
                }

                if *mode.read() == "export" {
                    div {
                        class: "key-export__section",
                        p {
                            class: "key-export__info",
                            "Export your E2E encryption room keys so you can import them on another device or client. The exported file will be encrypted with the passphrase you provide."
                        }
                        div {
                            class: "key-export__field",
                            label { "Passphrase" }
                            input {
                                r#type: "password",
                                placeholder: "Enter a passphrase to protect the export",
                                value: "{passphrase}",
                                oninput: move |evt| passphrase.set(evt.value()),
                            }
                        }
                        div {
                            class: "key-export__field",
                            label { "Confirm Passphrase" }
                            input {
                                r#type: "password",
                                placeholder: "Confirm passphrase",
                                value: "{confirm_passphrase}",
                                oninput: move |evt| confirm_passphrase.set(evt.value()),
                            }
                        }
                        button {
                            class: "btn btn--primary",
                            disabled: *is_working.read() || passphrase.read().is_empty() || *passphrase.read() != *confirm_passphrase.read(),
                            onclick: move |_| {
                                let pp = passphrase.read().clone();
                                is_working.set(true);
                                status.set(Some("Exporting keys...".to_string()));
                                spawn(async move {
                                    let client = { state.read().client.clone() };
                                    if let Some(client) = client {
                                        // Pick save location first
                                        match pick_save_path("Save E2E key export", "element-keys.txt").await {
                                            Ok(Some(path)) => {
                                                let encryption = client.encryption();
                                                match encryption.export_room_keys(path, &pp, |_| true).await {
                                                    Ok(()) => {
                                                        status.set(Some("Keys exported successfully!".to_string()));
                                                    }
                                                    Err(e) => {
                                                        status.set(Some(format!("Export failed: {e}")));
                                                    }
                                                }
                                            }
                                            Ok(None) => {
                                                status.set(None);
                                            }
                                            Err(err) => {
                                                status.set(Some(err));
                                            }
                                        }
                                    }
                                    is_working.set(false);
                                });
                            },
                            if *is_working.read() { "Exporting..." } else { "Export" }
                        }
                    }
                }

                if *mode.read() == "import" {
                    div {
                        class: "key-export__section",
                        p {
                            class: "key-export__info",
                            "Import E2E encryption room keys from a previously exported file. You'll need the passphrase used during export."
                        }
                        div {
                            class: "key-export__field",
                            label { "Passphrase" }
                            input {
                                r#type: "password",
                                placeholder: "Enter the export passphrase",
                                value: "{import_passphrase}",
                                oninput: move |evt| import_passphrase.set(evt.value()),
                            }
                        }
                        button {
                            class: "btn btn--primary",
                            disabled: *is_working.read() || import_passphrase.read().is_empty(),
                            onclick: move |_| {
                                is_working.set(true);
                                status.set(Some("Select a key file...".to_string()));
                                spawn(async move {
                                    match pick_file(
                                        "Select E2E key file",
                                        &[FileFilter {
                                            name: "Key files",
                                            extensions: &["txt", "json"],
                                        }],
                                    )
                                    .await
                                    {
                                        Ok(Some(handle)) => {
                                            let content = String::from_utf8_lossy(&handle.bytes);
                                            status.set(Some(format!("Read {} bytes. Import processing requires SDK key import API.", content.len())));
                                        }
                                        Ok(None) => {
                                            status.set(None);
                                        }
                                        Err(err) => {
                                            status.set(Some(err));
                                        }
                                    }
                                    is_working.set(false);
                                });
                            },
                            if *is_working.read() { "Importing..." } else { "Select File & Import" }
                        }
                    }
                }
            }
        }
    }
}