mirror of
https://github.com/Astatin3/unshell.git
synced 2026-06-09 06:47:59 -06:00
Start to make dynamic interfaces work
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
use egui::{Align2, Area, Color32, Frame, Order, Sense, UiKind, Vec2, mutex::Mutex};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
use serde_json::json;
|
||||
use std::sync::Arc;
|
||||
use wasm_bindgen::prelude::Closure;
|
||||
|
||||
use unshell_lib::Result;
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
pub struct Auth {
|
||||
// Auth Stuff
|
||||
@@ -150,10 +152,10 @@ impl Auth {
|
||||
// });
|
||||
}
|
||||
|
||||
pub fn get<T, F>(&self, path: &str, ret: F)
|
||||
pub fn get<R, F>(&self, path: &str, ret: F) -> Result<()>
|
||||
where
|
||||
F: FnOnce(T) + 'static,
|
||||
T: DeserializeOwned,
|
||||
F: FnOnce(R) + 'static,
|
||||
R: DeserializeOwned,
|
||||
{
|
||||
if let Some(ref token) = self.token {
|
||||
let state = self.auth_state.clone();
|
||||
@@ -162,7 +164,7 @@ impl Auth {
|
||||
format!("Bearer {}", token.token),
|
||||
Closure::once_into_js(move |ok: bool, response: String| {
|
||||
if ok {
|
||||
if let Ok(value) = serde_json::from_str::<T>(&response) {
|
||||
if let Ok(value) = serde_json::from_str::<R>(&response) {
|
||||
ret(value)
|
||||
} else {
|
||||
*(state.lock()) = AuthState::Error("Malformed Response".into());
|
||||
@@ -173,6 +175,37 @@ impl Auth {
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn post<R, T, F>(&self, path: &str, data: &T, ret: F) -> Result<()>
|
||||
where
|
||||
R: DeserializeOwned,
|
||||
T: Serialize,
|
||||
F: FnOnce(R) + 'static,
|
||||
{
|
||||
if let Some(ref token) = self.token {
|
||||
let state = self.auth_state.clone();
|
||||
crate::httpPostAuth(
|
||||
path,
|
||||
format!("Bearer {}", token.token),
|
||||
&serde_json::to_string(data)?,
|
||||
Closure::once_into_js(move |ok: bool, response: String| {
|
||||
if ok {
|
||||
if let Ok(value) = serde_json::from_str::<R>(&response) {
|
||||
ret(value)
|
||||
} else {
|
||||
*(state.lock()) = AuthState::Error("Malformed Response".into());
|
||||
}
|
||||
} else {
|
||||
*(state.lock()) = AuthState::Error(response);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
#[derive(Clone, serde::Deserialize, serde::Serialize)]
|
||||
pub enum Tree2Repr {
|
||||
File(String),
|
||||
Folder(Vec<String>),
|
||||
}
|
||||
|
||||
// #[derive(Clone, serde::Deserialize, serde::Serialize)]
|
||||
// pub struct InterfaceWrapper {
|
||||
// pub name: String,
|
||||
// pub interface: Interface,
|
||||
// }
|
||||
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
||||
pub enum ConfigStructField {
|
||||
Header(String),
|
||||
Text(String),
|
||||
String {
|
||||
// Default value of string edit in struct
|
||||
#[serde(default)]
|
||||
default: String,
|
||||
max_length: Option<usize>,
|
||||
// Display string edit as password
|
||||
#[serde(default)]
|
||||
protected: Option<bool>,
|
||||
},
|
||||
Integer {
|
||||
// Default value of integer in struct
|
||||
#[serde(default)]
|
||||
default: i32,
|
||||
min: Option<i32>,
|
||||
max: Option<i32>,
|
||||
},
|
||||
// Checkbox
|
||||
// Dropdown
|
||||
// Collapsing header
|
||||
// Slider
|
||||
// ...
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
mod config;
|
||||
mod render_interface;
|
||||
|
||||
use std::{
|
||||
path::PathBuf,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use crate::{auth::Auth, interface::config::Tree2Repr};
|
||||
use unshell_lib::Result;
|
||||
use unshell_lib::config::TreeMessage;
|
||||
|
||||
use crate::auth::Auth;
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
pub struct InterfaceWindow {
|
||||
@@ -18,7 +21,7 @@ pub struct InterfaceWindow {
|
||||
pub struct InterfaceWindowState {
|
||||
is_request: bool,
|
||||
is_error: bool,
|
||||
branch: Option<Tree2Repr>,
|
||||
branch: Option<TreeMessage>,
|
||||
}
|
||||
|
||||
impl InterfaceWindow {
|
||||
@@ -63,7 +66,7 @@ impl InterfaceWindow {
|
||||
let state_clone = self.state.clone();
|
||||
auth.get(
|
||||
&format!("/api/interface{}", self.path.display()),
|
||||
move |response: Result<Tree2Repr, String>| {
|
||||
move |response: Result<TreeMessage>| {
|
||||
let mut state_lock = state_clone.lock().unwrap();
|
||||
|
||||
match response {
|
||||
@@ -79,20 +82,21 @@ impl InterfaceWindow {
|
||||
|
||||
drop(state_lock);
|
||||
},
|
||||
);
|
||||
)
|
||||
.unwrap();
|
||||
} else {
|
||||
let state_clone = self.state.clone();
|
||||
|
||||
let mut state_lock = state_clone.lock().unwrap();
|
||||
|
||||
let branch = (state_lock.branch).as_ref().unwrap();
|
||||
let mut branch = (state_lock.branch).as_mut().unwrap();
|
||||
|
||||
let clear = match branch {
|
||||
Tree2Repr::File(file) => {
|
||||
ui.label(&format!("File {}", file));
|
||||
TreeMessage::InterfaceAndValue(interface_struct, interface_data) => {
|
||||
render_interface::render(ui, interface_struct, interface_data);
|
||||
false
|
||||
}
|
||||
Tree2Repr::Folder(items) => {
|
||||
TreeMessage::Folder(items) => {
|
||||
let mut clear = false;
|
||||
for item in items {
|
||||
if ui.button(&format!("Item {}", item)).clicked() {
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
use egui::{Color32, TextEdit};
|
||||
use unshell_lib::config::{InterfaceData, InterfaceStruct, config_struct};
|
||||
|
||||
pub fn render(
|
||||
ui: &mut egui::Ui,
|
||||
interface_struct: &InterfaceStruct,
|
||||
interface_data: &mut InterfaceData,
|
||||
) {
|
||||
match (interface_struct, interface_data) {
|
||||
(InterfaceStruct::ConfigStruct(interface), InterfaceData::ConfigStruct(data)) => {
|
||||
render_config_struct(ui, interface, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn render_config_struct(
|
||||
ui: &mut egui::Ui,
|
||||
interface: &config_struct::ConfigStructKeys,
|
||||
data: &mut config_struct::ConfigStructValues,
|
||||
) {
|
||||
for (interface, data) in interface.iter().zip(data) {
|
||||
match (interface, data) {
|
||||
(config_struct::ConfigStructField::Header(text), serde_json::Value::Null) => {
|
||||
ui.heading(text);
|
||||
}
|
||||
|
||||
(config_struct::ConfigStructField::Text(text), serde_json::Value::Null) => {
|
||||
ui.label(text);
|
||||
}
|
||||
|
||||
(
|
||||
config_struct::ConfigStructField::String {
|
||||
default: _,
|
||||
max_length,
|
||||
protected,
|
||||
},
|
||||
serde_json::Value::String(value),
|
||||
) => {
|
||||
let mut widget = TextEdit::singleline(value);
|
||||
|
||||
if let Some(limit) = &max_length {
|
||||
widget = widget.char_limit(*limit);
|
||||
}
|
||||
|
||||
if let Some(protected) = &protected {
|
||||
widget = widget.password(*protected);
|
||||
}
|
||||
|
||||
ui.add(widget);
|
||||
}
|
||||
(
|
||||
config_struct::ConfigStructField::Integer { default, min, max },
|
||||
serde_json::Value::Number(number),
|
||||
) => todo!(),
|
||||
|
||||
(interface, data) => {
|
||||
ui.colored_label(
|
||||
Color32::RED,
|
||||
&format!("Incorrect type and value! {interface:?} and {data:?}"),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -192,7 +192,8 @@ impl LogViewer {
|
||||
state_lock.requested_data = true;
|
||||
|
||||
// crate::log(&format!("{e:?}"));
|
||||
});
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user