This commit is contained in:
2025-02-18 00:28:56 +01:00
parent aba6b4a329
commit bc7a6908b2
4 changed files with 98 additions and 52 deletions

View File

@@ -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<u8>,
}
#[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<models::Hold, ServerFnError> {
async fn set_image(wall_uid: WallUid, hold_position: HoldPosition, image: Image) -> Result<RonEncoded<models::Hold>, 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::<crate::server::db::Database>();
let image = tokio::task::spawn_blocking(move || -> Result<models::Image, ServerFnError> {
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>();
// 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))
}

View File

@@ -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<dyn std::error::Error>>
let holds = wall
.holds
.into_iter()
.map(|(hold_position, hold)| {
(
.map(|(hold_position, hold)| -> Result<_, Box<dyn std::error::Error + Send + Sync>> {
let image = hold
.image
.map(|i| -> Result<_, Box<dyn std::error::Error + Send + Sync>> {
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::<Result<_, _>>()
.unwrap();
let wall_v2 = models::v2::Wall {
uid: wall_uid,

View File

@@ -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<RonEncoded<Vec<models::Wall>>, 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<RonEncoded<Vec<models::Wall>>, ServerFnError>
pub(crate) async fn get_wall(wall_uid: models::WallUid) -> Result<RonEncoded<models::Wall>, 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<RonEncoded<mod
pub(crate) async fn get_problems_for_wall(wall_uid: models::WallUid) -> Result<RonEncoded<Vec<models::Problem>>, 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<R
pub(crate) async fn get_problem(wall_uid: models::WallUid, problem_uid: models::ProblemUid) -> Result<RonEncoded<models::Problem>, 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)]