wip
This commit is contained in:
64
Cargo.lock
generated
64
Cargo.lock
generated
@@ -134,7 +134,7 @@ dependencies = [
|
|||||||
"leptos_meta",
|
"leptos_meta",
|
||||||
"leptos_router",
|
"leptos_router",
|
||||||
"moonboard-parser",
|
"moonboard-parser",
|
||||||
"rand",
|
"rand 0.9.0",
|
||||||
"redb",
|
"redb",
|
||||||
"ron",
|
"ron",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -1482,7 +1482,7 @@ dependencies = [
|
|||||||
"oco_ref",
|
"oco_ref",
|
||||||
"or_poisoned",
|
"or_poisoned",
|
||||||
"paste",
|
"paste",
|
||||||
"rand",
|
"rand 0.8.5",
|
||||||
"reactive_graph",
|
"reactive_graph",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"send_wrapper",
|
"send_wrapper",
|
||||||
@@ -1998,7 +1998,7 @@ version = "0.2.20"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zerocopy",
|
"zerocopy 0.7.35",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2104,8 +2104,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"rand_chacha",
|
"rand_chacha 0.3.1",
|
||||||
"rand_core",
|
"rand_core 0.6.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
|
||||||
|
dependencies = [
|
||||||
|
"rand_chacha 0.9.0",
|
||||||
|
"rand_core 0.9.0",
|
||||||
|
"zerocopy 0.8.16",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2115,7 +2126,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ppv-lite86",
|
"ppv-lite86",
|
||||||
"rand_core",
|
"rand_core 0.6.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core 0.9.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2127,6 +2148,15 @@ dependencies = [
|
|||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy 0.8.16",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reactive_graph"
|
name = "reactive_graph"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@@ -3326,7 +3356,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"zerocopy-derive",
|
"zerocopy-derive 0.7.35",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.8.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b8c07a70861ce02bad1607b5753ecb2501f67847b9f9ada7c160fff0ec6300c"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive 0.8.16",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3340,6 +3379,17 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.8.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5226bc9a9a9836e7428936cde76bb6b22feea1a8bfdbc0d241136e4d13417e25"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerofrom"
|
name = "zerofrom"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ leptos_axum = { version = "0.7", optional = true }
|
|||||||
leptos_meta = { version = "0.7" }
|
leptos_meta = { version = "0.7" }
|
||||||
leptos_router = { version = "0.7.0" }
|
leptos_router = { version = "0.7.0" }
|
||||||
moonboard-parser = { workspace = true, optional = true }
|
moonboard-parser = { workspace = true, optional = true }
|
||||||
rand = { version = "0.8", optional = true }
|
rand = { version = "0.9", default-features = false, features = ["std_rng"] }
|
||||||
ron = { version = "0.8" }
|
ron = { version = "0.8" }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
server_fn = { version = "0.7.4", features = ["cbor"] }
|
server_fn = { version = "0.7.4", features = ["cbor"] }
|
||||||
@@ -58,7 +58,6 @@ ssr = [
|
|||||||
"dep:redb",
|
"dep:redb",
|
||||||
"dep:bincode",
|
"dep:bincode",
|
||||||
"dep:tokio",
|
"dep:tokio",
|
||||||
"dep:rand",
|
|
||||||
"dep:tower",
|
"dep:tower",
|
||||||
"dep:tower-http",
|
"dep:tower-http",
|
||||||
"dep:leptos_axum",
|
"dep:leptos_axum",
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::codec::ron::Ron;
|
||||||
use crate::codec::ron::RonEncoded;
|
use crate::codec::ron::RonEncoded;
|
||||||
use crate::models;
|
use crate::models;
|
||||||
use crate::pages;
|
use crate::pages;
|
||||||
@@ -5,7 +6,6 @@ use leptos::prelude::*;
|
|||||||
use leptos_router::components::*;
|
use leptos_router::components::*;
|
||||||
use leptos_router::path;
|
use leptos_router::path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use type_toppings::ResultExt;
|
|
||||||
|
|
||||||
pub fn shell(options: LeptosOptions) -> impl IntoView {
|
pub fn shell(options: LeptosOptions) -> impl IntoView {
|
||||||
use leptos_meta::MetaTags;
|
use leptos_meta::MetaTags;
|
||||||
@@ -45,9 +45,9 @@ pub fn App() -> impl leptos::IntoView {
|
|||||||
<Router>
|
<Router>
|
||||||
<Routes fallback=|| "Not found">
|
<Routes fallback=|| "Not found">
|
||||||
<Route path=path!("/") view=Home />
|
<Route path=path!("/") view=Home />
|
||||||
<Route path=path!("/wall/:id") view=pages::wall::Wall />
|
<Route path=path!("/wall/:wall_uid") view=pages::wall::Wall />
|
||||||
<Route path=path!("/wall/:id/edit") view=pages::edit_wall::EditWall />
|
<Route path=path!("/wall/:wall_uid/edit") view=pages::edit_wall::EditWall />
|
||||||
<Route path=path!("/wall/:id/routes") view=pages::routes::Routes />
|
<Route path=path!("/wall/:wall_uid/routes") view=pages::routes::Routes />
|
||||||
</Routes>
|
</Routes>
|
||||||
</Router>
|
</Router>
|
||||||
}
|
}
|
||||||
@@ -86,7 +86,11 @@ pub fn Home() -> impl leptos::IntoView {
|
|||||||
leptos::view! {}
|
leptos::view! {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[server]
|
#[server(
|
||||||
|
input = Ron,
|
||||||
|
output = Ron,
|
||||||
|
custom = RonEncoded
|
||||||
|
)]
|
||||||
#[tracing::instrument(skip_all, err)]
|
#[tracing::instrument(skip_all, err)]
|
||||||
async fn get_walls() -> Result<RonEncoded<Vec<models::Wall>>, ServerFnError> {
|
async fn get_walls() -> Result<RonEncoded<Vec<models::Wall>>, ServerFnError> {
|
||||||
use redb::ReadableTable;
|
use redb::ReadableTable;
|
||||||
@@ -105,8 +109,6 @@ async fn get_walls() -> Result<RonEncoded<Vec<models::Wall>>, ServerFnError> {
|
|||||||
tracing::debug!("opened table");
|
tracing::debug!("opened table");
|
||||||
let walls: Vec<models::Wall> = walls_table.iter()?.map(|r| r.map(|(_, v)| v.value())).collect::<Result<_, _>>()?;
|
let walls: Vec<models::Wall> = walls_table.iter()?.map(|r| r.map(|(_, v)| v.value())).collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
tracing::debug!("got walls {walls:?}");
|
|
||||||
|
|
||||||
Ok(walls)
|
Ok(walls)
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
|
|||||||
@@ -69,35 +69,6 @@ pub mod ron {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl<T> serde::Serialize for RonEncoded<T>
|
|
||||||
// where
|
|
||||||
// T: serde::Serialize,
|
|
||||||
// {
|
|
||||||
// #[tracing::instrument(skip_all, err)]
|
|
||||||
// fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
// where
|
|
||||||
// S: serde::Serializer,
|
|
||||||
// {
|
|
||||||
// let serialized = ron::to_string(&self.0).map_err(serde::ser::Error::custom)?;
|
|
||||||
// serializer.serialize_str(&serialized)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// impl<'de, T> serde::Deserialize<'de> for RonEncoded<T>
|
|
||||||
// where
|
|
||||||
// T: serde::de::DeserializeOwned + 'static,
|
|
||||||
// {
|
|
||||||
// #[tracing::instrument(skip_all, err)]
|
|
||||||
// fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
// where
|
|
||||||
// D: serde::Deserializer<'de>,
|
|
||||||
// {
|
|
||||||
// let s = String::deserialize(deserializer)?;
|
|
||||||
// let t: T = ron::from_str(&s).map_err(serde::de::Error::custom)?;
|
|
||||||
// Ok(Self(t))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// IntoReq
|
// IntoReq
|
||||||
impl<T, Request, Err> IntoReq<Ron, Request, Err> for RonEncoded<T>
|
impl<T, Request, Err> IntoReq<Ron, Request, Err> for RonEncoded<T>
|
||||||
where
|
where
|
||||||
@@ -151,7 +122,6 @@ pub mod ron {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Ron;
|
use super::Ron;
|
||||||
use super::RonEncoded;
|
|
||||||
use codee::Decoder;
|
use codee::Decoder;
|
||||||
use codee::Encoder;
|
use codee::Encoder;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
@@ -163,27 +133,6 @@ pub mod ron {
|
|||||||
value: i32,
|
value: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_ron_wrapper() {
|
|
||||||
// let original = TestStruct {
|
|
||||||
// name: "Test".to_string(),
|
|
||||||
// value: 42,
|
|
||||||
// };
|
|
||||||
|
|
||||||
// // Wrap in RonCodec
|
|
||||||
// let wrapped = RonEncoded::new(original.clone());
|
|
||||||
|
|
||||||
// // Serialize
|
|
||||||
// let serialized = serde_json::to_string(&wrapped).expect("Serialization failed");
|
|
||||||
// println!("Serialized: {}", serialized);
|
|
||||||
|
|
||||||
// // Deserialize
|
|
||||||
// let deserialized: RonEncoded<TestStruct> = serde_json::from_str(&serialized).expect("Deserialization failed");
|
|
||||||
|
|
||||||
// // Compare
|
|
||||||
// assert_eq!(deserialized.into_inner(), original);
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ron_codec() {
|
fn test_ron_codec() {
|
||||||
let original = TestStruct {
|
let original = TestStruct {
|
||||||
|
|||||||
@@ -4,6 +4,12 @@ use web_sys::MouseEvent;
|
|||||||
#[component]
|
#[component]
|
||||||
pub fn Button(#[prop(into)] text: String, onclick: impl Fn(MouseEvent) -> () + 'static) -> impl IntoView {
|
pub fn Button(#[prop(into)] text: String, onclick: impl Fn(MouseEvent) -> () + 'static) -> impl IntoView {
|
||||||
view! {
|
view! {
|
||||||
<button on:click=onclick type="button" class="text-black bg-orange-300 hover:bg-orange-400 focus:ring-4 focus:ring-orange-500 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 focus:outline-none">{ text }</button>
|
<button
|
||||||
|
on:click=onclick
|
||||||
|
type="button"
|
||||||
|
class="text-black bg-orange-300 hover:bg-orange-400 focus:ring-4 focus:ring-orange-500 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 focus:outline-none"
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</button>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ pub mod components {
|
|||||||
|
|
||||||
pub mod codec;
|
pub mod codec;
|
||||||
|
|
||||||
|
pub mod server_functions;
|
||||||
|
|
||||||
#[cfg(feature = "ssr")]
|
#[cfg(feature = "ssr")]
|
||||||
pub mod server;
|
pub mod server;
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::codec::ron::Ron;
|
||||||
use crate::codec::ron::RonEncoded;
|
use crate::codec::ron::RonEncoded;
|
||||||
use crate::components::header::HeaderItem;
|
use crate::components::header::HeaderItem;
|
||||||
use crate::components::header::HeaderItems;
|
use crate::components::header::HeaderItems;
|
||||||
@@ -35,17 +36,17 @@ pub fn EditWall() -> impl leptos::IntoView {
|
|||||||
right: vec![],
|
right: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
leptos::view! {
|
// leptos::view! {
|
||||||
<div class="min-w-screen min-h-screen bg-slate-900">
|
// <div class="min-w-screen min-h-screen bg-slate-900">
|
||||||
<StyledHeader items=header_items />
|
// <StyledHeader items=header_items />
|
||||||
|
|
||||||
<div class="container mx-auto mt-2">
|
// <div class="container mx-auto mt-2">
|
||||||
<Await future=load let:data>
|
// <Await future=load let:data>
|
||||||
<Ready data=data.deref().to_owned() />
|
// <Ready data=data.deref().to_owned() />
|
||||||
</Await>
|
// </Await>
|
||||||
</div>
|
// </div>
|
||||||
</div>
|
// </div>
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
@@ -149,7 +150,12 @@ pub struct Image {
|
|||||||
file_contents: Vec<u8>,
|
file_contents: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[server]
|
#[server(
|
||||||
|
input = Ron,
|
||||||
|
output = Ron,
|
||||||
|
custom = RonEncoded
|
||||||
|
)]
|
||||||
|
#[tracing::instrument(skip_all, err)]
|
||||||
async fn load_initial_data() -> Result<RonEncoded<InitialData>, ServerFnError> {
|
async fn load_initial_data() -> Result<RonEncoded<InitialData>, ServerFnError> {
|
||||||
todo!()
|
todo!()
|
||||||
// let wall = state.persistent.with(|s| s.wall.clone()).await;
|
// let wall = state.persistent.with(|s| s.wall.clone()).await;
|
||||||
@@ -157,7 +163,7 @@ async fn load_initial_data() -> Result<RonEncoded<InitialData>, ServerFnError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[server(name = SetImage, input = Cbor)]
|
#[server(name = SetImage, input = Cbor)]
|
||||||
#[tracing::instrument(skip(image))]
|
#[tracing::instrument(skip(image), err)]
|
||||||
async fn set_image(hold_position: HoldPosition, image: Image) -> Result<models::Hold, ServerFnError> {
|
async fn set_image(hold_position: HoldPosition, image: Image) -> Result<models::Hold, ServerFnError> {
|
||||||
tracing::info!("Setting image, {}, {} bytes", image.file_name, image.file_contents.len());
|
tracing::info!("Setting image, {}, {} bytes", image.file_name, image.file_contents.len());
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,42 @@
|
|||||||
|
use crate::codec::ron::Ron;
|
||||||
use crate::codec::ron::RonEncoded;
|
use crate::codec::ron::RonEncoded;
|
||||||
use crate::components::header::HeaderItem;
|
use crate::components::header::HeaderItem;
|
||||||
use crate::components::header::HeaderItems;
|
use crate::components::header::HeaderItems;
|
||||||
use crate::components::header::StyledHeader;
|
use crate::components::header::StyledHeader;
|
||||||
use crate::models;
|
use crate::models;
|
||||||
|
use crate::models::WallUid;
|
||||||
|
use leptos::Params;
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
|
use leptos_router::params::Params;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
#[derive(Params, PartialEq, Clone)]
|
||||||
|
struct RouteParams {
|
||||||
|
wall_uid: Option<models::WallUid>,
|
||||||
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
pub fn Routes() -> impl leptos::IntoView {
|
pub fn Routes() -> impl leptos::IntoView {
|
||||||
let load = async move {
|
tracing::debug!("Enter");
|
||||||
// TODO: What to do about this unwrap?
|
|
||||||
load_initial_data().await.unwrap()
|
let params = leptos_router::hooks::use_params::<RouteParams>();
|
||||||
};
|
|
||||||
|
let problems = Resource::<Option<models::Wall>, Ron>::new_with_options(
|
||||||
|
move || params.get().map(|p| p.wall_uid),
|
||||||
|
move |wall_uid: Result<Option<WallUid>, _>| async move {
|
||||||
|
if let Ok(Some(wall_uid)) = wall_uid {
|
||||||
|
let wall = crate::server_functions::get_wall(wall_uid).await.unwrap().into_inner();
|
||||||
|
Some(wall)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
let header_items = HeaderItems {
|
let header_items = HeaderItems {
|
||||||
left: vec![HeaderItem {
|
left: vec![HeaderItem {
|
||||||
@@ -57,27 +79,6 @@ fn Ready(data: InitialData) -> impl leptos::IntoView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
||||||
pub struct InitialData {
|
|
||||||
problems: BTreeSet<models::Problem>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[server]
|
|
||||||
async fn load_initial_data() -> Result<RonEncoded<InitialData>, ServerFnError> {
|
|
||||||
todo!()
|
|
||||||
// let state = expect_context::<State>();
|
|
||||||
|
|
||||||
// let problems = state
|
|
||||||
// .persistent
|
|
||||||
// .with(|s| {
|
|
||||||
// let problems = &s.problems.problems;
|
|
||||||
// problems.clone()
|
|
||||||
// })
|
|
||||||
// .await;
|
|
||||||
|
|
||||||
// Ok(RonCodec::new(InitialData { problems }))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[server(name = ImportFromMiniMoonboard)]
|
#[server(name = ImportFromMiniMoonboard)]
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
async fn import_from_mini_moonboard() -> Result<(), ServerFnError> {
|
async fn import_from_mini_moonboard() -> Result<(), ServerFnError> {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use crate::codec::ron::Ron;
|
use crate::codec::ron::Ron;
|
||||||
use crate::codec::ron::RonEncoded;
|
|
||||||
use crate::components::button::Button;
|
use crate::components::button::Button;
|
||||||
use crate::components::header::HeaderItem;
|
use crate::components::header::HeaderItem;
|
||||||
use crate::components::header::HeaderItems;
|
use crate::components::header::HeaderItems;
|
||||||
@@ -11,45 +10,33 @@ use leptos::Params;
|
|||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use leptos::reactive::graph::ReactiveNode;
|
use leptos::reactive::graph::ReactiveNode;
|
||||||
use leptos_router::params::Params;
|
use leptos_router::params::Params;
|
||||||
use std::sync::Arc;
|
use rand::SeedableRng;
|
||||||
|
|
||||||
#[derive(Params, PartialEq, Clone)]
|
#[derive(Params, PartialEq, Clone)]
|
||||||
struct WallParams {
|
struct RouteParams {
|
||||||
id: Option<models::WallUid>,
|
wall_uid: Option<models::WallUid>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub fn Wall() -> impl leptos::IntoView {
|
pub fn Wall() -> impl leptos::IntoView {
|
||||||
tracing::debug!("Enter");
|
tracing::debug!("Enter");
|
||||||
let params = leptos_router::hooks::use_params::<WallParams>();
|
|
||||||
|
|
||||||
// TODO
|
let params = leptos_router::hooks::use_params::<RouteParams>();
|
||||||
let wall = Resource::<models::Wall, Ron>::new_with_options(
|
|
||||||
move || params.get().unwrap().id,
|
let wall = Resource::<Option<models::Wall>, Ron>::new_with_options(
|
||||||
{
|
move || params.get().map(|p| p.wall_uid),
|
||||||
move |wall_uid: Option<WallUid>| async move {
|
move |wall_uid: Result<Option<WallUid>, _>| async move {
|
||||||
let wall_uid = wall_uid.unwrap();
|
if let Ok(Some(wall_uid)) = wall_uid {
|
||||||
let wall = get_wall(wall_uid).await.unwrap().into_inner();
|
let wall = crate::server_functions::get_wall(wall_uid).await.unwrap().into_inner();
|
||||||
wall
|
Some(wall)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
// // TODO
|
|
||||||
// let wall = Resource::new(
|
|
||||||
// move || params.get().unwrap().id,
|
|
||||||
// move |wall_id| async move {
|
|
||||||
// if let Some(wall_id) = wall_id {
|
|
||||||
// let wall = get_wall(wall_id).await.unwrap();
|
|
||||||
// Some(wall)
|
|
||||||
// } else {
|
|
||||||
// None
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// );
|
|
||||||
|
|
||||||
let header_items = HeaderItems {
|
let header_items = HeaderItems {
|
||||||
left: vec![],
|
left: vec![],
|
||||||
middle: vec![HeaderItem {
|
middle: vec![HeaderItem {
|
||||||
@@ -73,13 +60,13 @@ pub fn Wall() -> impl leptos::IntoView {
|
|||||||
<StyledHeader items=header_items />
|
<StyledHeader items=header_items />
|
||||||
|
|
||||||
<div class="m-2">
|
<div class="m-2">
|
||||||
<Suspense fallback=move || view! {<p>"Loading..."</p>}>
|
<Suspense fallback=move || {
|
||||||
{move || Suspend::new(async move{
|
view! { <p>"Loading..."</p> }
|
||||||
let wall: Option<Option<RonEncoded<models::Wall>>> = wall.get();
|
}>
|
||||||
wall.map(|wall|{let wall = wall.unwrap();
|
{move || Suspend::new(async move {
|
||||||
view! {
|
let wall: Option<models::Wall> = wall.get().flatten();
|
||||||
<Ready wall=wall.into_inner() />
|
wall.map(|wall| {
|
||||||
}
|
view! { <Ready wall/> }
|
||||||
})
|
})
|
||||||
})}
|
})}
|
||||||
</Suspense>
|
</Suspense>
|
||||||
@@ -93,14 +80,33 @@ fn Ready(wall: models::Wall) -> impl leptos::IntoView {
|
|||||||
tracing::debug!("ready");
|
tracing::debug!("ready");
|
||||||
|
|
||||||
let (current_problem, current_problem_writer) = signal(None::<models::Problem>);
|
let (current_problem, current_problem_writer) = signal(None::<models::Problem>);
|
||||||
let problem_fetcher = LocalResource::new(move || async move {
|
let problem_fetcher = {
|
||||||
|
LocalResource::new(move || {
|
||||||
|
let wall_uid = wall.uid;
|
||||||
|
let problems = wall.problems.clone();
|
||||||
|
|
||||||
|
async move {
|
||||||
tracing::info!("Loading random problem");
|
tracing::info!("Loading random problem");
|
||||||
let problem = get_random_problem().await.expect("cannot get random problem");
|
|
||||||
if problem.is_none() {
|
// TODO: seed properly
|
||||||
tracing::info!("No problem returned by server in response to request for random problem");
|
use rand::seq::IteratorRandom;
|
||||||
|
let rng = &mut rand::rngs::StdRng::seed_from_u64(0);
|
||||||
|
let random_problem = problems.iter().choose(rng);
|
||||||
|
|
||||||
|
let problem = if let Some(random_problem) = random_problem {
|
||||||
|
crate::server_functions::get_problem(wall_uid, *random_problem)
|
||||||
|
.await
|
||||||
|
.expect("cannot get random problem")
|
||||||
|
.into_inner()
|
||||||
|
} else {
|
||||||
|
tracing::info!("Wall has no problems");
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
current_problem_writer.set(problem);
|
||||||
}
|
}
|
||||||
current_problem_writer.set(problem.into_inner());
|
})
|
||||||
});
|
};
|
||||||
|
|
||||||
let mut cells = vec![];
|
let mut cells = vec![];
|
||||||
for (&hold_position, hold) in &wall.holds {
|
for (&hold_position, hold) in &wall.holds {
|
||||||
@@ -117,17 +123,18 @@ fn Ready(wall: models::Wall) -> impl leptos::IntoView {
|
|||||||
view! {
|
view! {
|
||||||
<div class="grid grid-cols-[auto,1fr] gap-8">
|
<div class="grid grid-cols-[auto,1fr] gap-8">
|
||||||
// Render the wall
|
// Render the wall
|
||||||
<div style="max-height: 90vh; max-width: 90vh;" class=move || { grid_classes.clone() }>{cells}</div>
|
<div style="max-height: 90vh; max-width: 90vh;" class=move || { grid_classes.clone() }>
|
||||||
|
{cells}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>// TODO:
|
||||||
// TODO:
|
|
||||||
// <p>{current_problem.read().as_ref().map(|p| p.name.clone())}</p>
|
// <p>{current_problem.read().as_ref().map(|p| p.name.clone())}</p>
|
||||||
// <p>{current_problem.read().as_ref().map(|p| p.set_by.clone())}</p>
|
// <p>{current_problem.read().as_ref().map(|p| p.set_by.clone())}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button onclick=move |_| problem_fetcher.mark_dirty() text="➤ Next problem" />
|
<Button onclick=move |_| problem_fetcher.mark_dirty() text="➤ Next problem" />
|
||||||
</ div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -161,74 +168,3 @@ fn Hold(hold: models::Hold, #[prop(into)] role: Signal<Option<HoldRole>>) -> imp
|
|||||||
tracing::trace!("view");
|
tracing::trace!("view");
|
||||||
view! { <div class=class>{img}</div> }
|
view! { <div class=class>{img}</div> }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[server(
|
|
||||||
input = Ron,
|
|
||||||
output = Ron,
|
|
||||||
custom = RonEncoded
|
|
||||||
)]
|
|
||||||
#[tracing::instrument(skip_all, err)]
|
|
||||||
async fn get_wall(wall_uid: models::WallUid) -> Result<RonEncoded<models::Wall>, ServerFnError> {
|
|
||||||
tracing::debug!("Enter");
|
|
||||||
|
|
||||||
#[derive(Debug, derive_more::Error, derive_more::Display)]
|
|
||||||
enum Error {
|
|
||||||
#[display("Wall not found: {_0:?}")]
|
|
||||||
NotFound(#[error(not(source))] models::WallUid),
|
|
||||||
}
|
|
||||||
|
|
||||||
let db = expect_context::<Arc<redb::Database>>();
|
|
||||||
|
|
||||||
let wall = tokio::task::spawn_blocking(move || -> Result<models::Wall, ServerFnError> {
|
|
||||||
let read_txn = db.begin_read()?;
|
|
||||||
|
|
||||||
let walls_table = read_txn.open_table(crate::server::db::current::TABLE_WALLS)?;
|
|
||||||
let wall = walls_table.get(wall_uid)?.ok_or(Error::NotFound(wall_uid))?.value();
|
|
||||||
|
|
||||||
Ok(wall)
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
|
|
||||||
tracing::debug!("ok");
|
|
||||||
|
|
||||||
Ok(RonEncoded::new(wall))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[server(
|
|
||||||
input = Ron,
|
|
||||||
output = Ron,
|
|
||||||
custom = RonEncoded
|
|
||||||
)]
|
|
||||||
#[tracing::instrument(skip_all, err)]
|
|
||||||
async fn get_problem(wall_uid: models::WallUid, problem_uid: models::ProblemUid) -> Result<RonEncoded<Option<models::Problem>>, ServerFnError> {
|
|
||||||
tracing::debug!("Enter");
|
|
||||||
|
|
||||||
let db = expect_context::<Arc<redb::Database>>();
|
|
||||||
|
|
||||||
let problem = tokio::task::spawn_blocking(move || -> Result<Option<models::Problem>, ServerFnError> {
|
|
||||||
let read_txn = db.begin_read()?;
|
|
||||||
|
|
||||||
let table = read_txn.open_table(crate::server::db::current::TABLE_PROBLEMS)?;
|
|
||||||
let problem = table.get((wall_uid, problem_uid))?.map(|guard| guard.value());
|
|
||||||
|
|
||||||
Ok(problem)
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
|
|
||||||
// use rand::seq::IteratorRandom;
|
|
||||||
|
|
||||||
// let state = expect_context::<State>();
|
|
||||||
|
|
||||||
// let problem = state
|
|
||||||
// .persistent
|
|
||||||
// .with(|s| {
|
|
||||||
// let problems = &s.problems.problems;
|
|
||||||
// let rng = &mut rand::thread_rng();
|
|
||||||
// problems.iter().choose(rng).cloned()
|
|
||||||
// })
|
|
||||||
// .await;
|
|
||||||
|
|
||||||
// tracing::debug!("Returning randomized problem: {problem:?}");
|
|
||||||
|
|
||||||
Ok(RonEncoded::new(problem))
|
|
||||||
}
|
|
||||||
|
|||||||
66
crates/ascend/src/server_functions.rs
Normal file
66
crates/ascend/src/server_functions.rs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
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;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
#[server(
|
||||||
|
input = Ron,
|
||||||
|
output = Ron,
|
||||||
|
custom = RonEncoded
|
||||||
|
)]
|
||||||
|
#[tracing::instrument(skip_all, err)]
|
||||||
|
pub(crate) async fn get_wall(wall_uid: models::WallUid) -> Result<RonEncoded<models::Wall>, ServerFnError> {
|
||||||
|
tracing::debug!("Enter");
|
||||||
|
|
||||||
|
#[derive(Debug, derive_more::Error, derive_more::Display)]
|
||||||
|
enum Error {
|
||||||
|
#[display("Wall not found: {_0:?}")]
|
||||||
|
NotFound(#[error(not(source))] models::WallUid),
|
||||||
|
}
|
||||||
|
|
||||||
|
let db = expect_context::<Arc<redb::Database>>();
|
||||||
|
|
||||||
|
let wall = tokio::task::spawn_blocking(move || -> Result<models::Wall, ServerFnError> {
|
||||||
|
let read_txn = db.begin_read()?;
|
||||||
|
|
||||||
|
let walls_table = read_txn.open_table(crate::server::db::current::TABLE_WALLS)?;
|
||||||
|
let wall = walls_table.get(wall_uid)?.ok_or(Error::NotFound(wall_uid))?.value();
|
||||||
|
|
||||||
|
Ok(wall)
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
|
||||||
|
tracing::debug!("ok");
|
||||||
|
|
||||||
|
Ok(RonEncoded::new(wall))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[server(
|
||||||
|
input = Ron,
|
||||||
|
output = Ron,
|
||||||
|
custom = RonEncoded
|
||||||
|
)]
|
||||||
|
#[tracing::instrument(skip_all, err)]
|
||||||
|
pub(crate) async fn get_problem(
|
||||||
|
wall_uid: models::WallUid,
|
||||||
|
problem_uid: models::ProblemUid,
|
||||||
|
) -> Result<RonEncoded<Option<models::Problem>>, ServerFnError> {
|
||||||
|
tracing::debug!("Enter");
|
||||||
|
|
||||||
|
let db = expect_context::<Arc<redb::Database>>();
|
||||||
|
|
||||||
|
let problem = tokio::task::spawn_blocking(move || -> Result<Option<models::Problem>, ServerFnError> {
|
||||||
|
let read_txn = db.begin_read()?;
|
||||||
|
|
||||||
|
let table = read_txn.open_table(crate::server::db::current::TABLE_PROBLEMS)?;
|
||||||
|
let problem = table.get((wall_uid, problem_uid))?.map(|guard| guard.value());
|
||||||
|
|
||||||
|
Ok(problem)
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
|
||||||
|
Ok(RonEncoded::new(problem))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user