feat: prepare to edit wall
This commit is contained in:
parent
5f9ffcde27
commit
4daec21e2d
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -108,6 +108,7 @@ dependencies = [
|
||||
"leptos_meta",
|
||||
"leptos_router",
|
||||
"moonboard-parser",
|
||||
"rand",
|
||||
"ron",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -32,6 +32,7 @@ tracing-subscriber = { version = "0.3.18", features = [
|
||||
"env-filter",
|
||||
], optional = true }
|
||||
ron = { version = "0.8" }
|
||||
rand = { version = "0.8", optional = true }
|
||||
|
||||
[dev-dependencies.serde_json]
|
||||
version = "1"
|
||||
@ -41,6 +42,7 @@ hydrate = ["leptos/hydrate"]
|
||||
ssr = [
|
||||
"dep:axum",
|
||||
"dep:tokio",
|
||||
"dep:rand",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:leptos_axum",
|
||||
|
@ -1,4 +1,8 @@
|
||||
use crate::pages::edit_wall::EditWall;
|
||||
use crate::pages::wall::Wall;
|
||||
use leptos::prelude::*;
|
||||
use leptos_router::components::*;
|
||||
use leptos_router::path;
|
||||
|
||||
pub fn shell(options: LeptosOptions) -> impl IntoView {
|
||||
use leptos_meta::MetaTags;
|
||||
@ -13,7 +17,7 @@ pub fn shell(options: LeptosOptions) -> impl IntoView {
|
||||
<HydrationScripts options />
|
||||
<MetaTags />
|
||||
</head>
|
||||
<body>
|
||||
<body class="bg-slate-950">
|
||||
<App />
|
||||
</body>
|
||||
</html>
|
||||
@ -35,18 +39,20 @@ pub fn App() -> impl leptos::IntoView {
|
||||
leptos::view! {
|
||||
<Stylesheet id="leptos" href="/pkg/ascend.css" />
|
||||
|
||||
// sets the document title
|
||||
<Title text="Ascend" />
|
||||
|
||||
<main>
|
||||
<Ascend />
|
||||
</main>
|
||||
<Router>
|
||||
// <nav class="shadow-md mb-2 bg-white border-gray-200 px-4 lg:px-6 py-2.5">
|
||||
// <div class="flex flex-wrap justify-start items-center gap-4 max-w-screen-xl">
|
||||
// <HeaderItem text="Home" link="/"/>
|
||||
// </div>
|
||||
// </nav>
|
||||
<main>
|
||||
<Routes fallback=|| "Not found">
|
||||
<Route path=path!("/wall") view=Wall />
|
||||
<Route path=path!("/wall/edit") view=EditWall/>
|
||||
</Routes>
|
||||
</main>
|
||||
</Router>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn Ascend() -> impl leptos::IntoView {
|
||||
use crate::pages::wall::Wall;
|
||||
|
||||
leptos::view! { <Wall /> }
|
||||
}
|
||||
|
6
crates/ascend/src/components/header.rs
Normal file
6
crates/ascend/src/components/header.rs
Normal file
@ -0,0 +1,6 @@
|
||||
use leptos::prelude::*;
|
||||
|
||||
#[component]
|
||||
pub fn Header() -> impl leptos::IntoView {
|
||||
leptos::view! { <div>"header"</div> }
|
||||
}
|
@ -1,8 +1,11 @@
|
||||
pub mod app;
|
||||
pub mod pages {
|
||||
pub mod edit_wall;
|
||||
pub mod wall;
|
||||
}
|
||||
pub mod components {}
|
||||
pub mod components {
|
||||
pub mod header;
|
||||
}
|
||||
|
||||
pub mod codec;
|
||||
|
||||
|
69
crates/ascend/src/pages/edit_wall.rs
Normal file
69
crates/ascend/src/pages/edit_wall.rs
Normal file
@ -0,0 +1,69 @@
|
||||
use crate::components::header::Header;
|
||||
use crate::models::HoldPosition;
|
||||
use crate::models::HoldRole;
|
||||
use crate::models::Wall;
|
||||
use leptos::prelude::*;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[component]
|
||||
pub fn EditWall() -> impl leptos::IntoView {
|
||||
let load = async move {
|
||||
// TODO: What to do about this unwrap?
|
||||
load_initial_data().await.unwrap()
|
||||
};
|
||||
|
||||
leptos::view! {
|
||||
<div class="min-w-screen min-h-screen bg-slate-900">
|
||||
<div class="container mx-auto">
|
||||
<Header />
|
||||
<Await future=load let:data>
|
||||
<Ready data=data.to_owned() />
|
||||
</Await>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn Ready(data: InitialData) -> impl leptos::IntoView {
|
||||
leptos::logging::log!("ready");
|
||||
let mut hold_positions = vec![];
|
||||
for row in 0..(data.wall.rows) {
|
||||
for col in 0..(data.wall.cols) {
|
||||
hold_positions.push(HoldPosition { row, col });
|
||||
}
|
||||
}
|
||||
|
||||
let mut cells = vec![];
|
||||
for &_hold_position in &hold_positions {
|
||||
let cell = view! { <Hold /> };
|
||||
cells.push(cell);
|
||||
}
|
||||
|
||||
let grid_classes = format!("grid grid-rows-{} grid-cols-{} gap-4", data.wall.rows, data.wall.cols);
|
||||
|
||||
view! {
|
||||
<div class=move || { grid_classes.clone() }>{cells}</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn Hold() -> impl leptos::IntoView {
|
||||
view! { <div class="bg-indigo-100 aspect-square rounded"></div> }
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct InitialData {
|
||||
wall: Wall,
|
||||
}
|
||||
|
||||
#[server]
|
||||
async fn load_initial_data() -> Result<InitialData, ServerFnError> {
|
||||
use crate::server::state::State;
|
||||
|
||||
let state = expect_context::<State>();
|
||||
|
||||
let wall = state.persistent.with(|s| s.wall.clone()).await;
|
||||
Ok(InitialData { wall })
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
use crate::codec::ron::RonCodec;
|
||||
use crate::components::header::Header;
|
||||
use crate::models;
|
||||
use crate::models::HoldPosition;
|
||||
use crate::models::HoldRole;
|
||||
use leptos::prelude::*;
|
||||
use leptos::reactive::graph::ReactiveNode;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
@ -14,14 +16,19 @@ pub fn Wall() -> impl leptos::IntoView {
|
||||
};
|
||||
|
||||
leptos::view! {
|
||||
<Await future=load let:data>
|
||||
<WallReady data=data.to_owned() />
|
||||
</Await>
|
||||
<div class="min-w-screen min-h-screen bg-slate-900">
|
||||
<div class="container mx-auto">
|
||||
<Header />
|
||||
<Await future=load let:data>
|
||||
<Ready data=data.to_owned() />
|
||||
</Await>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn WallReady(data: InitialData) -> impl leptos::IntoView {
|
||||
fn Ready(data: InitialData) -> impl leptos::IntoView {
|
||||
let mut hold_positions = vec![];
|
||||
for row in 0..(data.wall.rows) {
|
||||
for col in 0..(data.wall.cols) {
|
||||
@ -30,7 +37,7 @@ fn WallReady(data: InitialData) -> impl leptos::IntoView {
|
||||
}
|
||||
|
||||
let (current_problem, current_problem_writer) = signal(None::<models::Problem>);
|
||||
LocalResource::new(move || async move {
|
||||
let problem_fetcher = LocalResource::new(move || async move {
|
||||
leptos::logging::log!("Loading random problem");
|
||||
let problem = get_random_problem().await.expect("cannot get random problem");
|
||||
current_problem_writer.set(Some(problem.into_inner()));
|
||||
@ -38,40 +45,32 @@ fn WallReady(data: InitialData) -> impl leptos::IntoView {
|
||||
|
||||
let mut cells = vec![];
|
||||
for &hold_position in &hold_positions {
|
||||
let role = move || {
|
||||
let x = current_problem
|
||||
.get()
|
||||
.map(|problem| {
|
||||
let role = problem.holds.get(&hold_position)?;
|
||||
// leptos::logging::log!("{hold_position:?}: {role:?}");
|
||||
Some(*role)
|
||||
})
|
||||
.flatten();
|
||||
x
|
||||
};
|
||||
let role = move || current_problem.get().map(|problem| problem.holds.get(&hold_position).copied()).flatten();
|
||||
let role = Signal::derive(role);
|
||||
|
||||
let cell = view! { <Cell role/> };
|
||||
let cell = view! { <Hold role /> };
|
||||
cells.push(cell);
|
||||
}
|
||||
|
||||
let grid_classes = format!("grid grid-rows-{} grid-cols-{} gap-4", data.wall.rows, data.wall.cols);
|
||||
|
||||
view! {
|
||||
<div class="container mx-auto border">
|
||||
<div class="grid grid-rows-4 grid-cols-12 gap-4">{cells}</div>
|
||||
</div>
|
||||
<div class=move || { grid_classes.clone() }>{cells}</div>
|
||||
<button on:click=move |_| problem_fetcher.mark_dirty()>"Random problem"</button>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn Cell(#[prop(into)] role: Signal<Option<HoldRole>>) -> impl leptos::IntoView {
|
||||
let classes = move || {
|
||||
let role_classes = role.get().map(|role| match role {
|
||||
HoldRole::Start => "outline outline-offset-2 outline-green-500",
|
||||
HoldRole::Normal => "outline outline-offset-2 outline-blue-500",
|
||||
HoldRole::Zone => "outline outline-offset-2 outline-amber-500",
|
||||
HoldRole::End => "outline outline-offset-2 outline-red-500",
|
||||
});
|
||||
let mut s = "aspect-square rounded border-2 border-dashed border-sky-500 bg-indigo-100".to_string();
|
||||
fn Hold(#[prop(into)] role: Signal<Option<HoldRole>>) -> impl leptos::IntoView {
|
||||
let class = move || {
|
||||
let role_classes = match role.get() {
|
||||
Some(HoldRole::Start) => Some("outline outline-offset-2 outline-green-500"),
|
||||
Some(HoldRole::Normal) => Some("outline outline-offset-2 outline-blue-500"),
|
||||
Some(HoldRole::Zone) => Some("outline outline-offset-2 outline-amber-500"),
|
||||
Some(HoldRole::End) => Some("outline outline-offset-2 outline-red-500"),
|
||||
None => Some("brightness-50"),
|
||||
};
|
||||
let mut s = "bg-indigo-100 aspect-square rounded".to_string();
|
||||
if let Some(c) = role_classes {
|
||||
s.push_str(" ");
|
||||
s.push_str(c);
|
||||
@ -79,29 +78,7 @@ fn Cell(#[prop(into)] role: Signal<Option<HoldRole>>) -> impl leptos::IntoView {
|
||||
s
|
||||
};
|
||||
|
||||
// let is_start = move || matches!(role.get(), Some(HoldRole::Start));
|
||||
// let is_normal = move || matches!(role.get(), Some(HoldRole::Normal));
|
||||
// let is_end = move || matches!(role.get(), Some(HoldRole::End));
|
||||
|
||||
// let classes_start = "outline outline-offset-2 outline-green-500";
|
||||
|
||||
view! {
|
||||
<div
|
||||
class=move || classes()
|
||||
|
||||
// class="aspect-square rounded border-2 border-dashed border-sky-500 bg-indigo-100"
|
||||
// class=move || (is_start().then_some("outline outline-offset-2 outline-green-500"))
|
||||
// class=(["outline", "outline-offset-2", "outline-green-500"], is_start)
|
||||
// class=(["outline", "outline-offset-2", "outline-blue-500"], is_normal)
|
||||
// class=(["outline", "outline-offset-2", "outline-red-500"], is_end)
|
||||
>
|
||||
"o"
|
||||
</div>
|
||||
}
|
||||
|
||||
// view! {
|
||||
// {format!("{:?}", role)}
|
||||
// }
|
||||
view! { <div class=class></div> }
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
@ -122,11 +99,18 @@ async fn load_initial_data() -> Result<InitialData, ServerFnError> {
|
||||
#[server]
|
||||
async fn get_random_problem() -> Result<RonCodec<models::Problem>, ServerFnError> {
|
||||
use crate::server::state::State;
|
||||
use rand::seq::IteratorRandom;
|
||||
|
||||
let state = expect_context::<State>();
|
||||
|
||||
// TODO: Actually randomize
|
||||
let problem = state.persistent.with(|s| s.problems.problems.iter().next().unwrap().clone()).await;
|
||||
let problem = state
|
||||
.persistent
|
||||
.with(|s| {
|
||||
let problems = &s.problems.problems;
|
||||
let rng = &mut rand::thread_rng();
|
||||
problems.iter().choose(rng).unwrap().clone()
|
||||
})
|
||||
.await;
|
||||
|
||||
tracing::debug!("Returning randomized problem: {problem:?}");
|
||||
|
||||
|
@ -4,9 +4,18 @@
|
||||
relative: true,
|
||||
files: ["*.html", "./src/**/*.rs"],
|
||||
},
|
||||
// https://tailwindcss.com/docs/content-configuration#using-regular-expressions
|
||||
safelist: [
|
||||
{
|
||||
pattern: /grid-cols-.+/,
|
||||
},
|
||||
{
|
||||
pattern: /grid-rows-.+/,
|
||||
},
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user