From bc7a6908b2aeecaf477e3e01e2346e8098c74eca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asger=20Juul=20Brunsh=C3=B8j?= Date: Tue, 18 Feb 2025 00:28:56 +0100 Subject: [PATCH] wip --- crates/ascend/src/pages/edit_wall.rs | 66 ++++++++++++-------------- crates/ascend/src/server/migrations.rs | 59 ++++++++++++++++++++--- crates/ascend/src/server_functions.rs | 5 +- justfile | 20 ++++---- 4 files changed, 98 insertions(+), 52 deletions(-) diff --git a/crates/ascend/src/pages/edit_wall.rs b/crates/ascend/src/pages/edit_wall.rs index f782f1e..dd859a2 100644 --- a/crates/ascend/src/pages/edit_wall.rs +++ b/crates/ascend/src/pages/edit_wall.rs @@ -1,3 +1,5 @@ +use crate::codec::ron::Ron; +use crate::codec::ron::RonEncoded; use crate::components::StyledHeader; use crate::components::header::HeaderItem; use crate::components::header::HeaderItems; @@ -12,9 +14,6 @@ use leptos_router::params::Params; use serde::Deserialize; use serde::Serialize; use server_fn::codec::Cbor; -use std::collections::BTreeMap; -use std::io::Cursor; -use std::path::Path; use wasm_bindgen::JsCast; use wasm_bindgen::prelude::*; use web_sys::FileList; @@ -110,7 +109,7 @@ fn Hold(wall_uid: models::WallUid, hold: models::Hold) -> impl IntoView { let hold = Signal::derive(move || { let refreshed = upload.value().get().map(Result::unwrap); - refreshed.unwrap_or(hold.clone()) + refreshed.map(RonEncoded::into_inner).unwrap_or(hold.clone()) }); // Callback to handle file selection @@ -176,17 +175,28 @@ pub struct Image { file_contents: Vec, } -#[server(name = SetImage, input = Cbor)] +#[server( + name = SetImage, + input = Cbor, + output = Ron, +)] #[tracing::instrument(skip(image), err)] -async fn set_image(wall_uid: WallUid, hold_position: HoldPosition, image: Image) -> Result { +async fn set_image(wall_uid: WallUid, hold_position: HoldPosition, image: Image) -> Result, ServerFnError> { + use image::ImageDecoder; + use std::collections::BTreeMap; + use std::path::Path; + tracing::info!("Setting image, {}, {} bytes", image.file_name, image.file_contents.len()); let db = expect_context::(); let image = tokio::task::spawn_blocking(move || -> Result { - let img = image::ImageReader::new(Cursor::new(image.file_contents)) + let mut decoder = image::ImageReader::new(std::io::Cursor::new(image.file_contents)) .with_guessed_format()? - .decode()?; + .into_decoder()?; + let orientation = decoder.orientation()?; + let mut img = image::DynamicImage::from_decoder(decoder)?; + img.apply_orientation(orientation); let holds_dir = Path::new("datastore/public/holds"); std::fs::create_dir_all(holds_dir)?; @@ -200,7 +210,7 @@ async fn set_image(wall_uid: WallUid, hold_position: HoldPosition, image: Image) let filename = format!("hold_row{}_col{}_{width}x{height}_{uid}.webp", hold_position.row, hold_position.col); let path = holds_dir.join(&filename); - let mut file = std::fs::File::open(&path)?; + let mut file = std::fs::OpenOptions::new().write(true).append(false).create_new(true).open(&path)?; resized.write_to(&mut file, image::ImageFormat::WebP)?; let res = models::ImageResolution { @@ -216,34 +226,20 @@ async fn set_image(wall_uid: WallUid, hold_position: HoldPosition, image: Image) }) .await??; - db.write(move |txn| { - use redb::ReadableTable; - let mut walls = txn.open_table(crate::server::db::current::TABLE_WALLS)?; - let mut wall = walls.get(wall_uid)?.expect("todo").value(); + let hold = db + .write(move |txn| { + use redb::ReadableTable; + let mut walls = txn.open_table(crate::server::db::current::TABLE_WALLS)?; + let mut wall = walls.get(wall_uid)?.expect("todo").value(); - if let Some(hold) = wall.holds.get_mut(&hold_position) { + let hold = wall.holds.get_mut(&hold_position).expect("hold"); hold.image = Some(image); - } + let hold = hold.clone(); + walls.insert(wall_uid, wall)?; - walls.insert(wall_uid, wall); + Ok(hold) + }) + .await?; - Ok(()) - }) - .await?; - - // let state = expect_context::(); - // state - // .persistent - // .update(|s| { - // if let Some(hold) = s.wall.holds.get_mut(&hold_position) { - // hold.image = Some(models::Image { filename }); - // } - // }) - // .await?; - - // // Return updated hold - // let hold = state.persistent.with(|s| s.wall.holds.get(&hold_position).cloned().unwrap()).await; - - let hold = todo!(); - Ok(hold) + Ok(RonEncoded::new(hold)) } diff --git a/crates/ascend/src/server/migrations.rs b/crates/ascend/src/server/migrations.rs index d96e3e4..5ddec99 100644 --- a/crates/ascend/src/server/migrations.rs +++ b/crates/ascend/src/server/migrations.rs @@ -1,9 +1,12 @@ use super::db; use super::db::Database; use crate::models; +use image::ImageDecoder; use redb::ReadableTable; use redb::ReadableTableMetadata; +use std::collections::BTreeMap; use std::collections::BTreeSet; +use std::path::Path; use std::path::PathBuf; use type_toppings::ResultExt; @@ -123,22 +126,66 @@ async fn migrate_to_v2(db: &Database) -> Result<(), Box> let holds = wall .holds .into_iter() - .map(|(hold_position, hold)| { - ( + .map(|(hold_position, hold)| -> Result<_, Box> { + let image = hold + .image + .map(|i| -> Result<_, Box> { + let holds_dir = Path::new("datastore/public/holds"); + + let p = holds_dir.join(i.filename); + tracing::info!("reading {}", p.display()); + let file_contents = std::fs::read(p)?; + + let mut decoder = image::ImageReader::new(std::io::Cursor::new(file_contents)) + .with_guessed_format()? + .into_decoder()?; + let orientation = decoder.orientation()?; + let mut img = image::DynamicImage::from_decoder(decoder)?; + img.apply_orientation(orientation); + + std::fs::create_dir_all(holds_dir)?; + + let targets = [(50, 50), (150, 150), (300, 300), (400, 400)]; + + let uid = models::ImageUid::create(); + let mut resolutions = BTreeMap::new(); + for (width, height) in targets { + tracing::info!("resizing to {width}x{height}"); + let resized = img.resize_to_fill(width, height, image::imageops::FilterType::Lanczos3); + + let filename = format!("hold_row{}_col{}_{width}x{height}_{uid}.webp", hold_position.row, hold_position.col); + let path = holds_dir.join(&filename); + tracing::info!("opening {}", path.display()); + let mut file = std::fs::OpenOptions::new().write(true).append(false).create_new(true).open(&path)?; + resized.write_to(&mut file, image::ImageFormat::WebP)?; + + let res = models::ImageResolution { + width: width.into(), + height: height.into(), + }; + resolutions.insert(res, models::ImageFilename { filename }); + } + + Ok(models::Image { uid, resolutions }) + }) + .transpose()?; + + Ok(( models::v1::HoldPosition { row: hold_position.row, col: hold_position.col, }, - models::v1::Hold { + models::v2::Hold { position: models::v1::HoldPosition { row: hold.position.row, col: hold.position.col, }, - image: hold.image.map(|i| models::v1::Image { filename: i.filename }), + image, }, - ) + )) }) - .collect(); + .collect::>() + .unwrap(); let wall_v2 = models::v2::Wall { uid: wall_uid, diff --git a/crates/ascend/src/server_functions.rs b/crates/ascend/src/server_functions.rs index ea9fa6c..fa2249e 100644 --- a/crates/ascend/src/server_functions.rs +++ b/crates/ascend/src/server_functions.rs @@ -1,7 +1,6 @@ use crate::codec::ron::Ron; use crate::codec::ron::RonEncoded; use crate::models; -use leptos::prelude::expect_context; use leptos::server; use server_fn::ServerFnError; @@ -13,6 +12,7 @@ use server_fn::ServerFnError; #[tracing::instrument(skip_all, err(Debug))] pub async fn get_walls() -> Result>, ServerFnError> { use crate::server::db::Database; + use leptos::prelude::expect_context; use redb::ReadableTable; tracing::debug!("Enter"); @@ -40,6 +40,7 @@ pub async fn get_walls() -> Result>, ServerFnError> pub(crate) async fn get_wall(wall_uid: models::WallUid) -> Result, ServerFnError> { use crate::server::db::Database; use crate::server::db::DatabaseOperationError; + use leptos::prelude::expect_context; tracing::debug!("Enter"); #[derive(Debug, derive_more::Error, derive_more::Display)] @@ -76,6 +77,7 @@ pub(crate) async fn get_wall(wall_uid: models::WallUid) -> Result Result>, ServerFnError> { use crate::server::db::Database; use crate::server::db::DatabaseOperationError; + use leptos::prelude::expect_context; tracing::debug!("Enter"); #[derive(Debug, derive_more::Error, derive_more::Display, derive_more::From)] @@ -134,6 +136,7 @@ pub(crate) async fn get_problems_for_wall(wall_uid: models::WallUid) -> Result Result, ServerFnError> { use crate::server::db::Database; use crate::server::db::DatabaseOperationError; + use leptos::prelude::expect_context; tracing::debug!("Enter"); #[derive(Debug, derive_more::Error, derive_more::Display)] diff --git a/justfile b/justfile index 023d662..8c6650e 100644 --- a/justfile +++ b/justfile @@ -11,17 +11,17 @@ fmt: serve: RUST_BACKTRACE=1 cargo leptos watch -- serve -build-release: - rm -rf dist - mkdir dist - cargo leptos build --release -vv - cp target/release/ascend dist/ - cp -r target/site dist/ +# build-release: +# rm -rf dist +# mkdir dist +# cargo leptos build --release -vv +# cp target/release/ascend dist/ +# cp -r target/site dist/ -run-release: - #!/usr/bin/env bash - cd dist - LEPTOS_SITE_ROOT="site" LEPTOS_SITE_ADDR="127.0.0.1:1337" ./ascend serve +# run-release: +# #!/usr/bin/env bash +# cd dist +# LEPTOS_SITE_ROOT="site" LEPTOS_SITE_ADDR="127.0.0.1:1337" ./ascend serve reset-state: cargo run --features ssr -- reset-state