add /config endpoint, add docker proxy setup, and style chat box
This commit is contained in:
+129
-37
@@ -3,9 +3,11 @@
|
||||
use base64::{display::Base64Display, prelude::BASE64_URL_SAFE};
|
||||
use dioxus::prelude::*;
|
||||
use mime_guess::Mime;
|
||||
use mumble_web2_common::GuiConfig;
|
||||
use ordermap::OrderSet;
|
||||
use sir::{css, global_css};
|
||||
use std::collections::HashMap;
|
||||
use tracing::error;
|
||||
|
||||
use crate::{imp, CONFIG};
|
||||
|
||||
@@ -279,6 +281,13 @@ pub fn ChatView() -> Element {
|
||||
let server = STATE.server.read();
|
||||
let mut draft = use_signal(|| "".to_string());
|
||||
|
||||
let chat_panel = css!(
|
||||
"
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
"
|
||||
);
|
||||
|
||||
let chat_history = css!(
|
||||
"
|
||||
overflow-y: auto;
|
||||
@@ -296,18 +305,44 @@ pub fn ChatView() -> Element {
|
||||
"
|
||||
);
|
||||
|
||||
let chat_box_wrapper = css!(
|
||||
"
|
||||
padding: 16px;
|
||||
border-top: solid var(--line-color) var(--line-width);
|
||||
"
|
||||
);
|
||||
|
||||
let chat_box = css!(
|
||||
"
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 16px;
|
||||
gap: 8px;
|
||||
border-top: solid var(--line-color) var(--line-width);
|
||||
gap: 16px;
|
||||
|
||||
background-color: var(--light-bg-color);
|
||||
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
|
||||
padding-left: 8px;
|
||||
padding-right: 16px;
|
||||
|
||||
border-radius: 8px;
|
||||
|
||||
input {
|
||||
color: white;
|
||||
background-color: var(--light-bg-color);
|
||||
|
||||
font-size: larger;
|
||||
|
||||
flex-grow: 1;
|
||||
padding: 8px;
|
||||
|
||||
border: none;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
"
|
||||
);
|
||||
|
||||
@@ -322,49 +357,88 @@ pub fn ChatView() -> Element {
|
||||
|
||||
rsx!(
|
||||
div {
|
||||
class: "{chat_history}",
|
||||
for chat in server.chat.iter() {
|
||||
div {
|
||||
class: "{chat_message}",
|
||||
if let Some(sender) = chat.sender.and_then(|u| server.users.get(&u)) {
|
||||
UserPill {
|
||||
name: sender.name.clone(),
|
||||
icon: UserIcon::None,
|
||||
isself: false,
|
||||
class: "{chat_panel}",
|
||||
div {
|
||||
class: "{chat_history}",
|
||||
for chat in server.chat.iter() {
|
||||
div {
|
||||
class: "{chat_message}",
|
||||
if let Some(sender) = chat.sender.and_then(|u| server.users.get(&u)) {
|
||||
UserPill {
|
||||
name: sender.name.clone(),
|
||||
icon: UserIcon::None,
|
||||
isself: false,
|
||||
}
|
||||
}
|
||||
span {
|
||||
dangerous_inner_html: "{chat.dangerous_html}",
|
||||
}
|
||||
}
|
||||
span {
|
||||
dangerous_inner_html: "{chat.dangerous_html}",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
div {
|
||||
class: "{chat_box}",
|
||||
input {
|
||||
placeholder: "say something",
|
||||
value: "{draft.read()}",
|
||||
oninput: move |evt| draft.set(evt.value().clone()),
|
||||
onkeypress: move |evt: Event<KeyboardData>| {
|
||||
if evt.code() == Code::Enter && evt.modifiers().is_empty() {
|
||||
do_send();
|
||||
div {
|
||||
class: "{chat_box_wrapper}",
|
||||
div {
|
||||
class: "{chat_box}",
|
||||
input {
|
||||
placeholder: "say something",
|
||||
value: "{draft.read()}",
|
||||
oninput: move |evt| draft.set(evt.value().clone()),
|
||||
onkeypress: move |evt: Event<KeyboardData>| {
|
||||
if evt.code() == Code::Enter && evt.modifiers().is_empty() {
|
||||
do_send();
|
||||
}
|
||||
}
|
||||
}
|
||||
div {
|
||||
span {
|
||||
onclick: move |_| pick_and_send_file(&net),
|
||||
class: "material-symbols-outlined",
|
||||
style: "color: rgba(255, 255, 255, 0.5); font-variation-settings: 'FILL' 1, 'wght' 700, 'GRAD' 0, 'opsz' 48; vertical-align: middle; font-size: 35px; user-select: none;",
|
||||
"attach_file",
|
||||
}
|
||||
}
|
||||
div {
|
||||
span {
|
||||
onclick: move |_| do_send(),
|
||||
class: "material-symbols-outlined",
|
||||
style: "color: rgba(255, 255, 255, 0.5); font-variation-settings: 'FILL' 1, 'wght' 700, 'GRAD' 0, 'opsz' 48; vertical-align: middle; font-size: 35px; user-select: none;",
|
||||
"send",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
button {
|
||||
onclick: move |_| pick_and_send_file(&net),
|
||||
"File"
|
||||
}
|
||||
button {
|
||||
onclick: move |_| do_send(),
|
||||
"Send"
|
||||
//button {
|
||||
// onclick: move |_| do_send(),
|
||||
// "Send"
|
||||
//}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// true => rsx!(span { class: "material-symbols-outlined", style: "{button_style}", "mic_off"}),
|
||||
//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"
|
||||
// }
|
||||
// }
|
||||
//},
|
||||
|
||||
#[component]
|
||||
pub fn ControlView() -> Element {
|
||||
let config_future = use_resource(|| CONFIG.get());
|
||||
|
||||
let net: Coroutine<Command> = use_coroutine_handle();
|
||||
let status = &STATE.status;
|
||||
let server = STATE.server.read();
|
||||
@@ -382,7 +456,12 @@ pub fn ControlView() -> Element {
|
||||
};
|
||||
|
||||
let current_channel_name = server.channels[&channel].name.clone();
|
||||
let Some(proxy_url) = &CONFIG.proxy_url else {
|
||||
|
||||
let Some(proxy_url) = config_future
|
||||
.read_unchecked()
|
||||
.as_ref()
|
||||
.and_then(|gui_config| gui_config.proxy_url.clone())
|
||||
else {
|
||||
return rsx!();
|
||||
};
|
||||
|
||||
@@ -675,7 +754,7 @@ pub fn ServerView() -> Element {
|
||||
let chat_box = css!(
|
||||
"
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-direction: row;
|
||||
grid-area: chat;
|
||||
border-left: solid var(--line-color) var(--line-width);
|
||||
|
||||
@@ -727,7 +806,15 @@ pub fn ServerView() -> Element {
|
||||
#[component]
|
||||
pub fn LoginView() -> Element {
|
||||
let net: Coroutine<Command> = use_coroutine_handle();
|
||||
let default_address = CONFIG.proxy_url.as_deref().unwrap_or("");
|
||||
|
||||
let config_future = use_resource(|| CONFIG.get());
|
||||
|
||||
let default_address = &*config_future
|
||||
.read_unchecked()
|
||||
.as_ref()
|
||||
.and_then(|gui_config| gui_config.proxy_url.clone())
|
||||
.unwrap_or("".to_string());
|
||||
|
||||
let mut address = use_signal(|| default_address.to_string());
|
||||
|
||||
let previous_username = imp::load_username();
|
||||
@@ -840,6 +927,11 @@ pub fn LoginView() -> Element {
|
||||
|
||||
pub fn app() -> Element {
|
||||
use_coroutine(|rx: UnboundedReceiver<Command>| super::network_entrypoint(rx));
|
||||
use_future(|| async move {
|
||||
if let Err(err) = imp::load_config().await {
|
||||
error!("{}", err)
|
||||
}
|
||||
});
|
||||
|
||||
global_css!(
|
||||
"
|
||||
|
||||
@@ -224,8 +224,10 @@ pub fn load_username() -> Option<String> {
|
||||
return None;
|
||||
}
|
||||
|
||||
pub fn load_config() -> Option<GuiConfig> {
|
||||
None
|
||||
pub async fn load_config() -> color_eyre::Result<GuiConfig> {
|
||||
color_eyre::eyre::bail!(
|
||||
"there is no config on desktop because desktops cannot be configured as they are tables"
|
||||
)
|
||||
}
|
||||
|
||||
pub fn init_logging() {
|
||||
|
||||
+15
-3
@@ -320,7 +320,8 @@ pub async fn network_connect(
|
||||
)
|
||||
.ey()?;
|
||||
|
||||
if let Some(server_hash) = &CONFIG.cert_hash {
|
||||
if let Some(server_hash) = &CONFIG.try_get().and_then(|cfg| cfg.cert_hash) {
|
||||
error!("{:?}", server_hash);
|
||||
let hash = web_sys::js_sys::Uint8Array::from(server_hash.as_slice());
|
||||
web_sys::js_sys::Reflect::set(&object, &"value".into(), &hash).ey()?;
|
||||
}
|
||||
@@ -394,8 +395,19 @@ fn load_config_from_env() -> Option<GuiConfig> {
|
||||
serde_json::from_str(option_env!("MUMBLE_WEB2_GUI_CONFIG")?).ok()?
|
||||
}
|
||||
|
||||
pub fn load_config() -> Option<GuiConfig> {
|
||||
load_config_from_window().or_else(load_config_from_env)
|
||||
pub async fn load_config() -> color_eyre::Result<()> {
|
||||
let config_url = option_env!("MUMBLE_WEB2_GUI_CONFIG_URL").ok_or(eyre!("foo"))?;
|
||||
|
||||
let config = reqwest::get(config_url)
|
||||
.await
|
||||
.unwrap()
|
||||
.json::<GuiConfig>()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
crate::CONFIG.set(config);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn init_logging() {
|
||||
|
||||
+2
-1
@@ -33,7 +33,8 @@ pub mod app;
|
||||
pub mod imp;
|
||||
mod msghtml;
|
||||
|
||||
pub static CONFIG: Lazy<GuiConfig> = Lazy::new(|| imp::load_config().unwrap_or_default());
|
||||
//pub static CONFIG: Lazy<GuiConfig> = Lazy::new(|| imp::load_config().unwrap_or_default());
|
||||
pub static CONFIG: async_cell::sync::AsyncCell<GuiConfig> = async_cell::sync::AsyncCell::new();
|
||||
|
||||
pub async fn network_entrypoint(mut event_rx: UnboundedReceiver<Command>) {
|
||||
loop {
|
||||
|
||||
Reference in New Issue
Block a user