From 74fe399cdcf81edcd095451f1d895eb37be9613a Mon Sep 17 00:00:00 2001 From: restitux Date: Sun, 6 Apr 2025 18:08:09 -0600 Subject: [PATCH] user control box with some styling --- Cargo.toml | 6 +- gui/Cargo.toml | 18 ++- gui/src/app.rs | 345 +++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 307 insertions(+), 62 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c168cd0..2905e2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = [ "common","gui", "proxy"] +members = ["common", "gui", "proxy"] [workspace.dependencies] serde = { version = "1.0.214", features = ["derive"] } @@ -11,9 +11,7 @@ mumble-web2-common = { path = "common" } version = "0.5.0" package = "mumble-protocol-2x" default-features = false -features = [ - "asynchronous-codec", -] +features = ["asynchronous-codec"] [profile] diff --git a/gui/Cargo.toml b/gui/Cargo.toml index dcd84ec..652df26 100644 --- a/gui/Cargo.toml +++ b/gui/Cargo.toml @@ -53,13 +53,13 @@ web-sys = { version = "0.3.72", features = [ "AudioDataCopyToOptions", "AudioSampleFormat", "Storage", -], optional = true} +], optional = true } gloo-timers = { version = "0.3.0", features = ["futures"], optional = true } tracing-web = { version = "0.1.3", optional = true } # Desktop Dependecies # =================== -dioxus-desktop = { version = "0.6.3", optional = true} +dioxus-desktop = { version = "0.6.3", optional = true } tokio = { version = "1.41.1", features = ["net", "rt"], optional = true } tokio-rustls = { version = "0.26.0", optional = true } opus = { version = "0.3.0", optional = true } @@ -82,7 +82,9 @@ ordermap = "0.5.3" html-purifier = "0.3.0" markdown = "0.3.0" futures-channel = "0.3.30" -sir = { git = "https://gitlab.com/samsartor/sir", features = ["dioxus"] } # dioxus 0.6 +sir = { git = "https://gitlab.com/samsartor/sir", features = [ + "dioxus", +] } # dioxus 0.6 mumble-web2-common = { workspace = true } serde = { workspace = true } tracing-subscriber = { version = "0.3.18", features = ["ansi"] } @@ -107,4 +109,12 @@ web = [ "gloo-timers", "tracing-web", ] -desktop = ["dioxus/desktop", "tokio", "tokio-rustls", "tracing-subscriber/env-filter", "opus", "cpal", "dasp_ring_buffer"] +desktop = [ + "dioxus/desktop", + "tokio", + "tokio-rustls", + "tracing-subscriber/env-filter", + "opus", + "cpal", + "dasp_ring_buffer", +] diff --git a/gui/src/app.rs b/gui/src/app.rs index 11c575a..9c149eb 100644 --- a/gui/src/app.rs +++ b/gui/src/app.rs @@ -363,6 +363,269 @@ pub fn ChatView() -> Element { ) } +#[component] +pub fn ControlView() -> Element { + let net: Coroutine = use_coroutine_handle(); + let status = &STATE.status; + let server = STATE.server.read(); + let Some(&UserState { + deaf, + self_deaf, + mute, + self_mute, + ref name, + channel, + .. + }) = server.this_user() + else { + return rsx!(); + }; + + let current_channel_name = server.channels[&channel].name.clone(); + let Some(proxy_url) = &CONFIG.proxy_url else { + return rsx!(); + }; + + let button_row = css!( + r#" + display: flex; + gap: 10px; + "# + ); + + let spacer = css!( + r#" + flex-grow: 1; + "# + ); + + let toggle_button = css!( + r#" + padding: 8px; + height: 100%; + aspect-ratio: 1 / 1; + + background-color: unset; + + border: solid rgb(255 255 255 / 0.1) 3px; + border-radius: 10px; + color: rgb(255 255 255 / 50%); + + transition: all 0.5s ease-in-out; + "# + ); + + let toggle_button_on = css!( + r#" + padding: 8px; + height: 100%; + aspect-ratio: 1 / 1; + + background-color: oklch(0.5 0.1381 21.71 / 20.12%); + + border: solid rgb(255 255 255 / 0) 3px; + border-radius: 10px; + color: oklch(0.53 0.1505 21.71 / 89.38%); + + transition: all 0.25s ease-in-out; + "# + ); + + let button_style = r#" + font-variation-settings: 'FILL' 1, 'wght' 700, 'GRAD' 0, 'opsz' 48; + vertical-align: middle; + font-size: 35px; + "#; + + let connecting_status = css!( + r#" + color: yellow; + "# + ); + + let connected_status = css!( + r#" + color: oklch(0.55 0.1184 141.35); + "# + ); + + let disconnected_status = css!( + r#" + color: gray; + "# + ); + + let failed_status = css!( + r#" + color: red; + "# + ); + + let connection_info = css!( + r#" + color: gray; + "# + ); + + let user_edit_button = css!( + r#" + background-color: oklch(0.53 0.1431 264.18); + border-radius: 50%; + aspect-ratio: 1 / 1; + "# + ); + + let connection_status = match &*status.read() { + Connecting => rsx! { + div { + class: "{connecting_status}", + span { + class: "material-symbols-outlined", + style: "vertical-align: middle; font-size: 30px;", + "signal_cellular_alt_2_bar" + } + span { + style: "width: 5px; display: inline-block;" + } + span { + style: "vertical-align: middle; font-size: 30px;", + "Connecting" + } + } + }, + Connected => rsx! { + div { + div { + class: "{connected_status}", + span { + class: "material-symbols-outlined", + style: "vertical-align: middle; font-size: 30px;", + "signal_cellular_alt" + } + span { + style: "width: 5px; display: inline-block;" + } + span { + style: "vertical-align: middle; font-size: 25px;", + "Connected" + } + } + div { + class: "{connection_info}", + span { style: "width: 3px; display: inline-block;"} + span { "{current_channel_name}" } + span { " — " } + span { "{proxy_url}" } + } + } + }, + Disconnected => rsx! { + div { + class: "{disconnected_status}", + span { + class: "material-symbols-outlined", + style: "vertical-align: middle;", + "signal_disconnected" + } + span { + style: "width: 5px; display: inline-block;" + } + span { + style: "vertical-align: middle;", + "Disconnected" + } + } + }, + Failed(_) => rsx! { + div { + class: "{failed_status}", + span { + class: "material-symbols-outlined", + style: "vertical-align: middle;", + "error" + } + span { + style: "width: 5px; display: inline-block;" + } + span { + style: "vertical-align: middle;", + "Failed" + } + } + }, + }; + + rsx!( + // Server control + div { + class: "{button_row}", + div { + {connection_status} + } + span { class: "{spacer}" } + button { + class: "{toggle_button}", + onclick: move |_| net.send(Disconnect), + span { + class: "material-symbols-outlined", + style: "{button_style}", + "signal_disconnected" + } + } + } + hr { style: "width: 100%;" } + // User control + div { + class: "{button_row}", + button { + class: "{user_edit_button}", + span { + class: "material-symbols-outlined", + style: "color: oklch(0.65 0.2245 28.06); font-size: 45px; font-variation-settings: 'FILL' 1, 'wght' 700, 'GRAD' 0, 'opsz' 48;", + "person_edit" + } + } + div { + div { + span { style: "font-size: 25px;", "{name}" } + } + div { + span { style: "font-size: 20px; color: gray;", "some data" } + } + } + span { class: "{spacer}" } + button { + class: match mute || self_mute { + true => toggle_button_on, + false => toggle_button, + }, + role: "switch", + aria_checked: mute || self_mute, + disabled: mute, + onclick: move |_| net.send(SetMute { mute: !self_mute }), + match mute || self_mute { + true => rsx!(span { class: "material-symbols-outlined", style: "{button_style}", "mic_off"}), + false => rsx!(span { class: "material-symbols-outlined", style: "{button_style}", "mic"}), + } + } + button { + class: match deaf || self_deaf { + true => toggle_button_on, + false => toggle_button, + }, + role: "switch", + aria_checked: deaf || self_deaf, + disabled: deaf, + onclick: move |_| net.send(SetDeaf { deaf: !self_deaf }), + match deaf || self_deaf { + true => rsx!(span { class: "material-symbols-outlined", style: "{button_style}", "volume_off"}), + false => rsx!(span { class: "material-symbols-outlined", style: "{button_style}", "volume_up"}), + } + } + } + ) +} + #[component] pub fn ServerView() -> Element { let net: Coroutine = use_coroutine_handle(); @@ -381,21 +644,21 @@ pub fn ServerView() -> Element { let grid = css!( r#" display: grid; - height: 100%; + height: 100%; background-color: var(--bg-color); - grid-template-rows: auto 1fr; - grid-template-columns: 1fr 1fr; + grid-template-rows: 1fr auto; + grid-template-columns: 1fr 2fr; grid-template-areas: - "bar bar" - "tree chat"; + "tree chat" + "control chat"; @media screen and (max-width: 720px) { grid-template-rows: auto 1fr 1fr; grid-template-columns: 1fr; grid-template-areas: - "bar" "tree" + "control" "chat"; } "# @@ -423,60 +686,24 @@ pub fn ServerView() -> Element { " ); - let top_bar = css!( + let control_box = css!( " padding: 16px; - grid-area: bar; - background-color: var(--login-bg-color); + margin: 16px; + background-color: var(--light-bg-color); + border-radius: 10px; + overflow: hidden; + grid-area: control; display: flex; - flex-direction: row; - gap: 16px; - align-items: center; - - button { - padding: 8px; - - img { - height: 1em; - vertical-align: text-bottom; - } - } + gap: 10px; + flex-direction: column; " ); rsx!( div { class: "{grid}", - div { - class: "{top_bar}", - button { - onclick: move |_| net.send(Disconnect), - "Disconnect" - } - button { - role: "switch", - aria_checked: mute || self_mute, - disabled: mute, - onclick: move |_| net.send(SetMute { mute: !self_mute }), - match mute || self_mute { - true => rsx!(img { src: asset!("assets/mic-off-svgrepo-com.svg") }), - false => rsx!(img { src: asset!("assets/mic-svgrepo-com.svg") }), - } - "\u{00A0}Mute" - } - button { - role: "switch", - aria_checked: deaf || self_deaf, - disabled: deaf, - onclick: move |_| net.send(SetDeaf { deaf: !self_deaf }), - match deaf || self_deaf { - true => rsx!(img { src: asset!("assets/speaker-muted-svgrepo-com.svg") }), - false => rsx!(img { src: asset!("assets/speaker-medium-svgrepo-com.svg") }), - } - "\u{00A0}Deafen" - } - } div { class: "{channel_box}", for (id, state) in server.channels.iter() { @@ -489,6 +716,10 @@ pub fn ServerView() -> Element { class: "{chat_box}", ChatView {} } + div { + class: "{control_box}", + ControlView {} + } } ) } @@ -613,8 +844,9 @@ pub fn app() -> Element { global_css!( " :root { - --txt-color: white; - --bg-color: #372f3a; + --txt-color: oklch(0.9 0 99); + --bg-color: oklch(0.15 0.01 338.64); + --light-bg-color: oklch(0.25 0.01 338.64); --login-bg-color: #5d7680; --primary-btn-color: #7bad9f; --accent-normal: #7bad9f; @@ -637,8 +869,10 @@ pub fn app() -> Element { overflow: auto; color: var(--txt-color); - font-family: sans-serif; - font-size: large; + font-family: Nunito; + font-size: 15pt; + font-weight: 600; + } button { @@ -675,6 +909,9 @@ pub fn app() -> Element { ); rsx!( + document::Link{ rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap" } + document::Link{ rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" } + sir::AppStyle { } match *STATE.status.read() { Connected => rsx!(ServerView {}),