synpad 0.1.0

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

use crate::app::Route;
use crate::client::session;
use crate::components::modal::Modal;
use crate::state::app_state::{AppState, AuthStatus};

/// General/account settings panel.
#[component]
pub fn GeneralSettings() -> Element {
    let mut state = use_context::<Signal<AppState>>();
    let navigator = use_navigator();

    let user_profile = state.read().user_profile.clone();
    let display_name = user_profile
        .as_ref()
        .and_then(|p| p.display_name.clone())
        .unwrap_or_default();
    let user_id = user_profile
        .as_ref()
        .and_then(|p| p.user_id.as_ref().map(|u| u.to_string()))
        .unwrap_or_default();

    let status_msg = user_profile
        .as_ref()
        .and_then(|p| p.status_message.clone())
        .unwrap_or_default();

    let mut new_display_name = use_signal(|| display_name.clone());
    let mut new_status_message = use_signal(|| status_msg);
    let mut is_saving = use_signal(|| false);
    let mut show_logout_confirm = use_signal(|| false);

    let on_logout = move |_| {
        spawn(async move {
            if let Some(client) = state.read().client.clone() {
                if let Err(e) = session::logout(&client).await {
                    tracing::error!("Logout failed: {e}");
                }
            }
            {
                let mut w = state.write();
                w.auth_status = AuthStatus::LoggedOut;
                w.client = None;
                w.user_profile = None;
                w.rooms.clear();
                w.sorted_room_ids.clear();
            }
            navigator.push(Route::LoginPage {});
        });
    };

    rsx! {
        div {
            class: "general-settings",

            h3 { "Account" }

            div {
                class: "settings-section",

                // User ID (read-only)
                div {
                    class: "settings-field",
                    label { "User ID" }
                    span { class: "settings-field__value", "{user_id}" }
                }

                // Display name
                div {
                    class: "settings-field",
                    label { r#for: "display-name", "Display Name" }
                    div {
                        class: "settings-field__input-row",
                        input {
                            id: "display-name",
                            r#type: "text",
                            value: "{new_display_name}",
                            oninput: move |evt| new_display_name.set(evt.value()),
                        }
                        button {
                            class: "btn btn--primary btn--sm",
                            disabled: *is_saving.read(),
                            onclick: move |_| {
                                let name = new_display_name.read().clone();
                                is_saving.set(true);
                                spawn(async move {
                                    let client = { state.read().client.clone() };
                                    if let Some(client) = client {
                                        match client.account().set_display_name(Some(name.as_str())).await {
                                            Ok(_) => {
                                                tracing::info!("Display name updated to: {name}");
                                                if let Some(ref mut profile) = state.write().user_profile {
                                                    profile.display_name = Some(name);
                                                }
                                            }
                                            Err(e) => {
                                                tracing::error!("Failed to update display name: {e}");
                                            }
                                        }
                                    }
                                    is_saving.set(false);
                                });
                            },
                            "Save"
                        }
                    }
                }

                // Avatar
                div {
                    class: "settings-field",
                    label { "Avatar" }
                    div {
                        class: "settings-field__avatar-row",
                        div {
                            class: "settings-field__avatar-preview",
                            "{display_name.chars().next().unwrap_or('?')}"
                        }
                        button {
                            class: "btn btn--secondary btn--sm",
                            "Change Avatar"
                        }
                    }
                }

                // Status message
                div {
                    class: "settings-field",
                    label { r#for: "status-message", "Status Message" }
                    div {
                        class: "settings-field__input-row",
                        input {
                            id: "status-message",
                            r#type: "text",
                            placeholder: "Set a status message",
                            value: "{new_status_message}",
                            oninput: move |evt| new_status_message.set(evt.value()),
                        }
                        button {
                            class: "btn btn--secondary btn--sm",
                            onclick: move |_| {
                                let msg = new_status_message.read().clone();
                                spawn(async move {
                                    let status_val = if msg.is_empty() { None } else { Some(msg) };
                                    {
                                        let mut w = state.write();
                                        if let Some(ref mut profile) = w.user_profile {
                                            profile.status_message = status_val.clone();
                                        }
                                        w.custom_status = status_val;
                                    }
                                    tracing::info!("Status message updated");
                                });
                            },
                            "Set"
                        }
                    }
                }
            }

            // Email & Phone (3PID) Management (#59)
            div {
                class: "settings-section",
                h3 { "Email & Phone" }
                p { class: "settings-description", "Manage email addresses and phone numbers linked to your account." }

                {
                    let mut threepids = use_signal(Vec::<(String, String)>::new);
                    let mut threepids_loaded = use_signal(|| false);
                    let mut new_email = use_signal(|| String::new());
                    let mut adding_email = use_signal(|| false);

                    if !*threepids_loaded.read() {
                        threepids_loaded.set(true);
                        spawn(async move {
                            let client = { state.read().client.clone() };
                            if let Some(client) = client {
                                match client.account().get_3pids().await {
                                    Ok(response) => {
                                        let items: Vec<(String, String)> = response.threepids.iter()
                                            .map(|tp| (tp.medium.to_string(), tp.address.clone()))
                                            .collect();
                                        threepids.set(items);
                                    }
                                    Err(e) => tracing::error!("Failed to load 3PIDs: {e}"),
                                }
                            }
                        });
                    }

                    rsx! {
                        div {
                            class: "threepid-list",
                            if threepids.read().is_empty() {
                                p { class: "settings-description", "No email or phone linked." }
                            }
                            for (medium, address) in threepids.read().iter() {
                                {
                                    let medium_display = medium.clone();
                                    let addr = address.clone();
                                    let icon = if medium_display == "email" { "📧" } else { "📱" };
                                    rsx! {
                                        div {
                                            class: "threepid-item",
                                            style: "display: flex; align-items: center; gap: 8px; padding: 8px 0; border-bottom: 1px solid var(--border-color, #333);",
                                            span { "{icon}" }
                                            span { style: "flex: 1;", "{addr}" }
                                            span { class: "threepid-item__medium", "{medium_display}" }
                                        }
                                    }
                                }
                            }

                            div {
                                style: "margin-top: 12px; display: flex; gap: 8px; align-items: center;",
                                input {
                                    r#type: "email",
                                    placeholder: "Add email address",
                                    value: "{new_email}",
                                    oninput: move |evt| new_email.set(evt.value()),
                                    style: "flex: 1; padding: 6px 10px;",
                                }
                                button {
                                    class: "btn btn--primary btn--sm",
                                    disabled: new_email.read().trim().is_empty() || *adding_email.read(),
                                    onclick: move |_| {
                                        let email = new_email.read().clone();
                                        adding_email.set(true);
                                        spawn(async move {
                                            let client = { state.read().client.clone() };
                                            if let Some(_client) = client {
                                                // Request email token - this sends a verification email
                                                tracing::info!("Requesting to add email: {email}");
                                                // The actual flow requires requesting a validation token,
                                                // then adding the threepid after verification
                                                // For now, show a message
                                            }
                                            adding_email.set(false);
                                            new_email.set(String::new());
                                        });
                                    },
                                    "Add"
                                }
                            }
                        }
                    }
                }
            }

            // Danger zone
            div {
                class: "settings-section settings-section--danger",
                h3 { "Sign Out" }
                p { "Sign out of your account on this device." }
                button {
                    class: "btn btn--danger",
                    onclick: move |_| show_logout_confirm.set(true),
                    "Sign Out"
                }
            }

            // Logout confirmation dialog
            if *show_logout_confirm.read() {
                Modal {
                    title: "Sign Out".to_string(),
                    on_close: move |_| show_logout_confirm.set(false),
                    div {
                        class: "logout-confirm",
                        p {
                            class: "logout-confirm__message",
                            "Are you sure you want to sign out? You will need to sign in again to access your messages."
                        }
                        p {
                            class: "logout-confirm__warning",
                            "Make sure you have backed up your encryption keys before signing out, or you may lose access to encrypted messages."
                        }
                        div {
                            class: "logout-confirm__actions",
                            button {
                                class: "btn btn--secondary",
                                onclick: move |_| show_logout_confirm.set(false),
                                "Cancel"
                            }
                            button {
                                class: "btn btn--danger",
                                onclick: move |evt| {
                                    show_logout_confirm.set(false);
                                    on_logout(evt);
                                },
                                "Sign Out"
                            }
                        }
                    }
                }
            }
        }
    }
}