feat: upload images

This commit is contained in:
2025-01-17 23:14:19 +01:00
parent a3f1cc1b77
commit 51372e4886
8 changed files with 352 additions and 42 deletions

290
Cargo.lock generated
View File

@@ -94,6 +94,12 @@ version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
[[package]]
name = "anymap2"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c"
[[package]]
name = "ascend"
version = "0.0.0"
@@ -103,7 +109,7 @@ dependencies = [
"clap",
"console_error_panic_hook",
"derive_more",
"http",
"http 1.2.0",
"leptos",
"leptos_axum",
"leptos_meta",
@@ -113,11 +119,13 @@ dependencies = [
"ron",
"serde",
"serde_json",
"server_fn",
"tokio",
"tower 0.4.13",
"tower-http 0.5.2",
"tracing",
"tracing-subscriber",
"tracing-subscriber-wasm",
"type-toppings",
"wasm-bindgen",
"web-sys",
@@ -191,7 +199,7 @@ dependencies = [
"axum-core",
"bytes",
"futures-util",
"http",
"http 1.2.0",
"http-body",
"http-body-util",
"hyper",
@@ -225,7 +233,7 @@ dependencies = [
"async-trait",
"bytes",
"futures-util",
"http",
"http 1.2.0",
"http-body",
"http-body-util",
"mime",
@@ -264,6 +272,15 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]]
name = "bitflags"
version = "2.8.0"
@@ -303,6 +320,33 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "ciborium"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
dependencies = [
"ciborium-io",
"ciborium-ll",
"serde",
]
[[package]]
name = "ciborium-io"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
[[package]]
name = "ciborium-ll"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
dependencies = [
"ciborium-io",
"half",
]
[[package]]
name = "clap"
version = "4.5.26"
@@ -439,6 +483,12 @@ version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crunchy"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "dashmap"
version = "6.1.0"
@@ -683,6 +733,107 @@ version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "gloo"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28999cda5ef6916ffd33fb4a7b87e1de633c47c0dc6d97905fee1cdaa142b94d"
dependencies = [
"gloo-console",
"gloo-dialogs",
"gloo-events",
"gloo-file",
"gloo-history",
"gloo-net 0.3.1",
"gloo-render",
"gloo-storage",
"gloo-timers",
"gloo-utils 0.1.7",
"gloo-worker",
]
[[package]]
name = "gloo-console"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82b7ce3c05debe147233596904981848862b068862e9ec3e34be446077190d3f"
dependencies = [
"gloo-utils 0.1.7",
"js-sys",
"serde",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "gloo-dialogs"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67062364ac72d27f08445a46cab428188e2e224ec9e37efdba48ae8c289002e6"
dependencies = [
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "gloo-events"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68b107f8abed8105e4182de63845afcc7b69c098b7852a813ea7462a320992fc"
dependencies = [
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "gloo-file"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8d5564e570a38b43d78bdc063374a0c3098c4f0d64005b12f9bbe87e869b6d7"
dependencies = [
"gloo-events",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "gloo-history"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85725d90bf0ed47063b3930ef28e863658a7905989e9929a8708aab74a1d5e7f"
dependencies = [
"gloo-events",
"gloo-utils 0.1.7",
"serde",
"serde-wasm-bindgen",
"serde_urlencoded",
"thiserror 1.0.69",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "gloo-net"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a66b4e3c7d9ed8d315fd6b97c8b1f74a7c6ecbbc2320e65ae7ed38b7068cc620"
dependencies = [
"futures-channel",
"futures-core",
"futures-sink",
"gloo-utils 0.1.7",
"http 0.2.12",
"js-sys",
"pin-project",
"serde",
"serde_json",
"thiserror 1.0.69",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "gloo-net"
version = "0.6.0"
@@ -692,8 +843,8 @@ dependencies = [
"futures-channel",
"futures-core",
"futures-sink",
"gloo-utils",
"http",
"gloo-utils 0.2.0",
"http 1.2.0",
"js-sys",
"pin-project",
"serde",
@@ -704,6 +855,54 @@ dependencies = [
"web-sys",
]
[[package]]
name = "gloo-render"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd9306aef67cfd4449823aadcd14e3958e0800aa2183955a309112a84ec7764"
dependencies = [
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "gloo-storage"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480"
dependencies = [
"gloo-utils 0.1.7",
"js-sys",
"serde",
"serde_json",
"thiserror 1.0.69",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "gloo-timers"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "gloo-utils"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e"
dependencies = [
"js-sys",
"serde",
"serde_json",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "gloo-utils"
version = "0.2.0"
@@ -717,12 +916,39 @@ dependencies = [
"web-sys",
]
[[package]]
name = "gloo-worker"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13471584da78061a28306d1359dd0178d8d6fc1c7c80e5e35d27260346e0516a"
dependencies = [
"anymap2",
"bincode",
"gloo-console",
"gloo-utils 0.1.7",
"js-sys",
"serde",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "guardian"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "493913a18c0d7bebb75127a26a432162c59edbe06f6cf712001e3e769345e8b5"
[[package]]
name = "half"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
dependencies = [
"cfg-if",
"crunchy",
]
[[package]]
name = "hashbrown"
version = "0.14.5"
@@ -756,6 +982,17 @@ dependencies = [
"utf8-width",
]
[[package]]
name = "http"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "http"
version = "1.2.0"
@@ -774,7 +1011,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
dependencies = [
"bytes",
"http",
"http 1.2.0",
]
[[package]]
@@ -785,7 +1022,7 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
dependencies = [
"bytes",
"futures-util",
"http",
"http 1.2.0",
"http-body",
"pin-project-lite",
]
@@ -833,7 +1070,7 @@ dependencies = [
"bytes",
"futures-channel",
"futures-util",
"http",
"http 1.2.0",
"http-body",
"httparse",
"httpdate",
@@ -851,7 +1088,7 @@ checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
dependencies = [
"bytes",
"futures-util",
"http",
"http 1.2.0",
"http-body",
"hyper",
"pin-project-lite",
@@ -1233,7 +1470,7 @@ dependencies = [
"any_spawner",
"either_of",
"futures",
"gloo-net",
"gloo-net 0.6.0",
"js-sys",
"leptos",
"leptos_router_macro",
@@ -1421,7 +1658,7 @@ dependencies = [
"bytes",
"encoding_rs",
"futures-util",
"http",
"http 1.2.0",
"httparse",
"memchr",
"mime",
@@ -1926,6 +2163,17 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "serde-wasm-bindgen"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e"
dependencies = [
"js-sys",
"serde",
"wasm-bindgen",
]
[[package]]
name = "serde_derive"
version = "1.0.217"
@@ -1999,11 +2247,12 @@ checksum = "f5dd7fcccd3ef2081da086c1f8595b506627abbbbc9f64be0141d2251219570e"
dependencies = [
"axum",
"bytes",
"ciborium",
"const_format",
"dashmap",
"futures",
"gloo-net",
"http",
"gloo-net 0.6.0",
"http 1.2.0",
"http-body-util",
"hyper",
"inventory",
@@ -2364,7 +2613,7 @@ dependencies = [
"bitflags",
"bytes",
"futures-util",
"http",
"http 1.2.0",
"http-body",
"http-body-util",
"http-range-header",
@@ -2389,7 +2638,7 @@ dependencies = [
"bitflags",
"bytes",
"futures-util",
"http",
"http 1.2.0",
"http-body",
"http-body-util",
"http-range-header",
@@ -2479,6 +2728,17 @@ dependencies = [
"tracing-log",
]
[[package]]
name = "tracing-subscriber-wasm"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79804e80980173c6c8e53d98508eb24a2dbc4ee17a3e8d2ca8e5bad6bf13a898"
dependencies = [
"gloo",
"tracing",
"tracing-subscriber",
]
[[package]]
name = "type-toppings"
version = "0.2.1"

View File

@@ -12,7 +12,8 @@ crate-type = ["cdylib", "rlib"]
moonboard-parser = { workspace = true, optional = true }
axum = { version = "0.7", optional = true }
console_error_panic_hook = "0.1"
leptos = { version = "0.7.3" }
leptos = { version = "0.7.4", features = ["tracing"] }
server_fn = { version = "0.7.4", features = ["cbor"] }
leptos_axum = { version = "0.7", optional = true }
leptos_meta = { version = "0.7" }
leptos_router = { version = "0.7.0" }
@@ -27,10 +28,9 @@ clap = { version = "4.5.7", features = ["derive"] }
camino = { version = "1.1", optional = true }
type-toppings = { version = "0.2.1", features = ["result"] }
# Tracing
tracing = { version = "0.1", optional = true }
tracing-subscriber = { version = "0.3.18", features = [
"env-filter",
], optional = true }
tracing = { version = "0.1" }
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
tracing-subscriber-wasm = "0.1.0"
ron = { version = "0.8" }
rand = { version = "0.8", optional = true }
web-sys = { version = "0.3.76", features = ["File", "FileList"] }
@@ -53,9 +53,9 @@ ssr = [
"leptos_meta/ssr",
"leptos_router/ssr",
"tracing",
# "tracing",
]
tracing = ["leptos/tracing", "dep:tracing", "dep:tracing-subscriber"]
# tracing = ["leptos/tracing", "dep:tracing", "dep:tracing-subscriber"]
[package.metadata.leptos]
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name

View File

@@ -29,8 +29,6 @@ pub fn App() -> impl leptos::IntoView {
use leptos_meta::Stylesheet;
use leptos_meta::Title;
// TODO: look at tracing-subscriber-wasm
#[cfg(feature = "ssr")]
tracing::debug!("Rendering root component");
// Provides context that manages stylesheets, titles, meta tags, etc.

View File

@@ -18,6 +18,19 @@ pub mod models;
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn hydrate() {
use crate::app::*;
console_error_panic_hook::set_once();
tracing_subscriber::fmt()
.with_writer(
// To avoide trace events in the browser from showing their JS backtrace
tracing_subscriber_wasm::MakeConsoleWriter::default().map_trace_level_to(tracing::Level::DEBUG),
)
// For some reason, if we don't do this in the browser, we get a runtime error.
.without_time()
.with_ansi(false)
.init();
tracing::warn!("test");
leptos::mount::hydrate_body(App);
}

View File

@@ -48,4 +48,6 @@ pub struct Hold {
}
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct Image {}
pub struct Image {
pub filename: String,
}

View File

@@ -1,4 +1,5 @@
use crate::components::header::Header;
use crate::models;
use crate::models::HoldPosition;
use crate::models::Wall;
use leptos::ev::Event;
@@ -6,6 +7,7 @@ use leptos::html::Input;
use leptos::prelude::*;
use serde::Deserialize;
use serde::Serialize;
use server_fn::codec::Cbor;
use wasm_bindgen::JsCast;
use wasm_bindgen::prelude::*;
use web_sys::FileList;
@@ -31,7 +33,7 @@ pub fn EditWall() -> impl leptos::IntoView {
#[component]
fn Ready(data: InitialData) -> impl leptos::IntoView {
leptos::logging::log!("ready");
tracing::debug!("ready");
let mut hold_positions = vec![];
for row in 0..(data.wall.rows) {
for col in 0..(data.wall.cols) {
@@ -44,7 +46,7 @@ fn Ready(data: InitialData) -> impl leptos::IntoView {
holds.push(view! { <Hold hold_position /> });
}
let grid_classes = format!("grid grid-rows-{} grid-cols-{} gap-4", data.wall.rows, data.wall.cols);
let grid_classes = format!("grid grid-rows-{} grid-cols-{} gap-2", data.wall.rows, data.wall.cols);
view! { <div class=move || { grid_classes.clone() }>{holds}</div> }
}
@@ -59,32 +61,36 @@ fn Hold(hold_position: HoldPosition) -> impl leptos::IntoView {
}
};
let upload = Action::from(ServerAction::<SetImage>::new());
// Callback to handle file selection
let on_file_input = move |event: Event| {
let files: FileList = event.target().unwrap().unchecked_ref::<web_sys::HtmlInputElement>().files().unwrap();
leptos::logging::log!("{:?}", &files);
let file = files.item(0).unwrap();
leptos::logging::log!("{:?}", &file);
let file_reader = web_sys::FileReader::new().unwrap();
file_reader.read_as_array_buffer(&file).unwrap();
leptos::logging::log!("foo");
let onload = Closure::wrap(Box::new(move |event: Event| {
leptos::logging::log!("onload");
let on_load = Closure::wrap(Box::new(move |event: Event| {
let file_reader: web_sys::FileReader = event.target().unwrap().dyn_into().unwrap();
let file = file_reader.result().unwrap();
let file = web_sys::js_sys::Uint8Array::new(&file);
let mut file_buffer = vec![0; file.length() as usize];
file.copy_to(&mut file_buffer);
let mut file_contents = vec![0; file.length() as usize];
file.copy_to(&mut file_contents);
leptos::logging::log!("bytes: {:?}", &file_buffer.len());
tracing::debug!("bytes: {:?}", &file_contents.len());
let image = Image {
file_name: "foo".to_string(),
file_contents,
};
upload.dispatch(SetImage { hold_position, image });
}) as Box<dyn FnMut(_)>);
file_reader.set_onload(Some(onload.as_ref().unchecked_ref()));
onload.forget();
file_reader.set_onload(Some(on_load.as_ref().unchecked_ref()));
on_load.forget();
};
view! {
@@ -103,11 +109,17 @@ fn Hold(hold_position: HoldPosition) -> impl leptos::IntoView {
}
}
#[derive(Serialize, Deserialize, Clone)]
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct InitialData {
wall: Wall,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Image {
file_name: String,
file_contents: Vec<u8>,
}
#[server]
async fn load_initial_data() -> Result<InitialData, ServerFnError> {
use crate::server::state::State;
@@ -117,3 +129,28 @@ async fn load_initial_data() -> Result<InitialData, ServerFnError> {
let wall = state.persistent.with(|s| s.wall.clone()).await;
Ok(InitialData { wall })
}
#[server(name = SetImage, input = Cbor)]
#[tracing::instrument(skip(image))]
async fn set_image(hold_position: HoldPosition, image: Image) -> Result<(), ServerFnError> {
tracing::info!("Setting image, {}, {} bytes", image.file_name, image.file_contents.len());
use crate::server::state::State;
// TODO: Fix file extension presumption, and possibly use uuid
let filename = format!("row{}_col{}.jpg", hold_position.row, hold_position.col);
tokio::fs::create_dir_all("datastore").await?;
tokio::fs::write(format!("datastore/{filename}"), image.file_contents).await?;
let state = expect_context::<State>();
state
.persistent
.update(|s| {
if let Some(hold) = s.wall.holds.get_mut(&hold_position) {
hold.image = Some(models::Image { filename });
}
})
.await?;
Ok(())
}

View File

@@ -38,7 +38,7 @@ fn Ready(data: InitialData) -> impl leptos::IntoView {
let (current_problem, current_problem_writer) = signal(None::<models::Problem>);
let problem_fetcher = LocalResource::new(move || async move {
leptos::logging::log!("Loading random problem");
tracing::info!("Loading random problem");
let problem = get_random_problem().await.expect("cannot get random problem");
current_problem_writer.set(Some(problem.into_inner()));
});
@@ -52,7 +52,7 @@ fn Ready(data: InitialData) -> impl leptos::IntoView {
cells.push(cell);
}
let grid_classes = format!("grid grid-rows-{} grid-cols-{} gap-4", data.wall.rows, data.wall.cols);
let grid_classes = format!("grid grid-rows-{} grid-cols-{} gap-2", data.wall.rows, data.wall.cols);
view! {
<div class=move || { grid_classes.clone() }>{cells}</div>

File diff suppressed because one or more lines are too long