wip
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
use crate::codec::ron::Ron;
|
||||||
|
use crate::codec::ron::RonEncoded;
|
||||||
use crate::components::StyledHeader;
|
use crate::components::StyledHeader;
|
||||||
use crate::components::header::HeaderItem;
|
use crate::components::header::HeaderItem;
|
||||||
use crate::components::header::HeaderItems;
|
use crate::components::header::HeaderItems;
|
||||||
@@ -12,9 +14,6 @@ use leptos_router::params::Params;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use server_fn::codec::Cbor;
|
use server_fn::codec::Cbor;
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use std::io::Cursor;
|
|
||||||
use std::path::Path;
|
|
||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
use web_sys::FileList;
|
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 hold = Signal::derive(move || {
|
||||||
let refreshed = upload.value().get().map(Result::unwrap);
|
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
|
// Callback to handle file selection
|
||||||
@@ -176,17 +175,28 @@ pub struct Image {
|
|||||||
file_contents: Vec<u8>,
|
file_contents: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[server(name = SetImage, input = Cbor)]
|
#[server(
|
||||||
|
name = SetImage,
|
||||||
|
input = Cbor,
|
||||||
|
output = Ron,
|
||||||
|
)]
|
||||||
#[tracing::instrument(skip(image), err)]
|
#[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());
|
tracing::info!("Setting image, {}, {} bytes", image.file_name, image.file_contents.len());
|
||||||
|
|
||||||
let db = expect_context::<crate::server::db::Database>();
|
let db = expect_context::<crate::server::db::Database>();
|
||||||
|
|
||||||
let image = tokio::task::spawn_blocking(move || -> Result<models::Image, ServerFnError> {
|
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()?
|
.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");
|
let holds_dir = Path::new("datastore/public/holds");
|
||||||
std::fs::create_dir_all(holds_dir)?;
|
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 filename = format!("hold_row{}_col{}_{width}x{height}_{uid}.webp", hold_position.row, hold_position.col);
|
||||||
let path = holds_dir.join(&filename);
|
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)?;
|
resized.write_to(&mut file, image::ImageFormat::WebP)?;
|
||||||
|
|
||||||
let res = models::ImageResolution {
|
let res = models::ImageResolution {
|
||||||
@@ -216,34 +226,20 @@ async fn set_image(wall_uid: WallUid, hold_position: HoldPosition, image: Image)
|
|||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
db.write(move |txn| {
|
let hold = db
|
||||||
|
.write(move |txn| {
|
||||||
use redb::ReadableTable;
|
use redb::ReadableTable;
|
||||||
let mut walls = txn.open_table(crate::server::db::current::TABLE_WALLS)?;
|
let mut walls = txn.open_table(crate::server::db::current::TABLE_WALLS)?;
|
||||||
let mut wall = walls.get(wall_uid)?.expect("todo").value();
|
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);
|
hold.image = Some(image);
|
||||||
}
|
let hold = hold.clone();
|
||||||
|
walls.insert(wall_uid, wall)?;
|
||||||
|
|
||||||
walls.insert(wall_uid, wall);
|
Ok(hold)
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// let state = expect_context::<State>();
|
Ok(RonEncoded::new(hold))
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
use super::db;
|
use super::db;
|
||||||
use super::db::Database;
|
use super::db::Database;
|
||||||
use crate::models;
|
use crate::models;
|
||||||
|
use image::ImageDecoder;
|
||||||
use redb::ReadableTable;
|
use redb::ReadableTable;
|
||||||
use redb::ReadableTableMetadata;
|
use redb::ReadableTableMetadata;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
use std::path::Path;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use type_toppings::ResultExt;
|
use type_toppings::ResultExt;
|
||||||
|
|
||||||
@@ -123,22 +126,66 @@ async fn migrate_to_v2(db: &Database) -> Result<(), Box<dyn std::error::Error>>
|
|||||||
let holds = wall
|
let holds = wall
|
||||||
.holds
|
.holds
|
||||||
.into_iter()
|
.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 {
|
models::v1::HoldPosition {
|
||||||
row: hold_position.row,
|
row: hold_position.row,
|
||||||
col: hold_position.col,
|
col: hold_position.col,
|
||||||
},
|
},
|
||||||
models::v1::Hold {
|
models::v2::Hold {
|
||||||
position: models::v1::HoldPosition {
|
position: models::v1::HoldPosition {
|
||||||
row: hold.position.row,
|
row: hold.position.row,
|
||||||
col: hold.position.col,
|
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 {
|
let wall_v2 = models::v2::Wall {
|
||||||
uid: wall_uid,
|
uid: wall_uid,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use crate::codec::ron::Ron;
|
use crate::codec::ron::Ron;
|
||||||
use crate::codec::ron::RonEncoded;
|
use crate::codec::ron::RonEncoded;
|
||||||
use crate::models;
|
use crate::models;
|
||||||
use leptos::prelude::expect_context;
|
|
||||||
use leptos::server;
|
use leptos::server;
|
||||||
use server_fn::ServerFnError;
|
use server_fn::ServerFnError;
|
||||||
|
|
||||||
@@ -13,6 +12,7 @@ use server_fn::ServerFnError;
|
|||||||
#[tracing::instrument(skip_all, err(Debug))]
|
#[tracing::instrument(skip_all, err(Debug))]
|
||||||
pub async fn get_walls() -> Result<RonEncoded<Vec<models::Wall>>, ServerFnError> {
|
pub async fn get_walls() -> Result<RonEncoded<Vec<models::Wall>>, ServerFnError> {
|
||||||
use crate::server::db::Database;
|
use crate::server::db::Database;
|
||||||
|
use leptos::prelude::expect_context;
|
||||||
use redb::ReadableTable;
|
use redb::ReadableTable;
|
||||||
tracing::debug!("Enter");
|
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> {
|
pub(crate) async fn get_wall(wall_uid: models::WallUid) -> Result<RonEncoded<models::Wall>, ServerFnError> {
|
||||||
use crate::server::db::Database;
|
use crate::server::db::Database;
|
||||||
use crate::server::db::DatabaseOperationError;
|
use crate::server::db::DatabaseOperationError;
|
||||||
|
use leptos::prelude::expect_context;
|
||||||
tracing::debug!("Enter");
|
tracing::debug!("Enter");
|
||||||
|
|
||||||
#[derive(Debug, derive_more::Error, derive_more::Display)]
|
#[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> {
|
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::Database;
|
||||||
use crate::server::db::DatabaseOperationError;
|
use crate::server::db::DatabaseOperationError;
|
||||||
|
use leptos::prelude::expect_context;
|
||||||
tracing::debug!("Enter");
|
tracing::debug!("Enter");
|
||||||
|
|
||||||
#[derive(Debug, derive_more::Error, derive_more::Display, derive_more::From)]
|
#[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> {
|
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::Database;
|
||||||
use crate::server::db::DatabaseOperationError;
|
use crate::server::db::DatabaseOperationError;
|
||||||
|
use leptos::prelude::expect_context;
|
||||||
tracing::debug!("Enter");
|
tracing::debug!("Enter");
|
||||||
|
|
||||||
#[derive(Debug, derive_more::Error, derive_more::Display)]
|
#[derive(Debug, derive_more::Error, derive_more::Display)]
|
||||||
|
|||||||
20
justfile
20
justfile
@@ -11,17 +11,17 @@ fmt:
|
|||||||
serve:
|
serve:
|
||||||
RUST_BACKTRACE=1 cargo leptos watch -- serve
|
RUST_BACKTRACE=1 cargo leptos watch -- serve
|
||||||
|
|
||||||
build-release:
|
# build-release:
|
||||||
rm -rf dist
|
# rm -rf dist
|
||||||
mkdir dist
|
# mkdir dist
|
||||||
cargo leptos build --release -vv
|
# cargo leptos build --release -vv
|
||||||
cp target/release/ascend dist/
|
# cp target/release/ascend dist/
|
||||||
cp -r target/site dist/
|
# cp -r target/site dist/
|
||||||
|
|
||||||
run-release:
|
# run-release:
|
||||||
#!/usr/bin/env bash
|
# #!/usr/bin/env bash
|
||||||
cd dist
|
# cd dist
|
||||||
LEPTOS_SITE_ROOT="site" LEPTOS_SITE_ADDR="127.0.0.1:1337" ./ascend serve
|
# LEPTOS_SITE_ROOT="site" LEPTOS_SITE_ADDR="127.0.0.1:1337" ./ascend serve
|
||||||
|
|
||||||
reset-state:
|
reset-state:
|
||||||
cargo run --features ssr -- reset-state
|
cargo run --features ssr -- reset-state
|
||||||
|
|||||||
Reference in New Issue
Block a user