Update to dioxus 0.4
This commit is contained in:
parent
c36ce14b3b
commit
183f8a75d2
10 changed files with 328 additions and 586 deletions
|
|
@ -10,10 +10,9 @@ ammonia = "3.3.0"
|
|||
anyhow = "1.0.71"
|
||||
api = { version = "0.1.0", path = "../api" }
|
||||
console_log = { version = "1.0.0", features = ["color"] }
|
||||
dioxus = "0.3.2"
|
||||
dioxus-class = "0.3.0"
|
||||
dioxus-router = { version = "0.3.0", features = ["web"] }
|
||||
dioxus-web = "0.3.2"
|
||||
dioxus = "0.4.0"
|
||||
dioxus-router = { version = "0.4.1", features = ["web"] }
|
||||
dioxus-web = "0.4.0"
|
||||
gloo-net = { version = "0.3.0", features = ["json"] }
|
||||
gloo-storage = "0.2.2"
|
||||
gloo-utils = "0.1.7"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
use dioxus::prelude::*;
|
||||
use dioxus_class::prelude::*;
|
||||
|
||||
pub mod bs {
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
|
@ -65,21 +64,30 @@ pub struct ModalProps<'a> {
|
|||
}
|
||||
|
||||
pub fn Modal<'a>(cx: Scope<'a, ModalProps<'a>>) -> Element<'a> {
|
||||
let mut classes = Class::from(vec!["modal"]);
|
||||
let mut classes = vec!["modal"];
|
||||
|
||||
if cx.props.fade {
|
||||
classes.append("fade");
|
||||
classes.push("fade");
|
||||
}
|
||||
|
||||
let mut dialog_class = Class::from(vec!["modal-dialog"]);
|
||||
let classes = classes.join(" ");
|
||||
|
||||
let mut dialog_class = vec!["modal-dialog"];
|
||||
|
||||
if cx.props.centered {
|
||||
dialog_class.append("modal-dialog-centered");
|
||||
dialog_class.push("modal-dialog-centered");
|
||||
}
|
||||
|
||||
let dialog_class = dialog_class.join(" ");
|
||||
|
||||
cx.render(rsx! {
|
||||
div { class: classes, id: cx.props.id.as_str(), tabindex: "-1", "aria-labelledby": cx.props.labeled_by.as_deref(), "aria-hidden": "true",
|
||||
div { class: dialog_class,
|
||||
div {
|
||||
class: "{classes}",
|
||||
id: cx.props.id.as_str(),
|
||||
tabindex: "-1",
|
||||
"aria-labelledby": cx.props.labeled_by.as_deref(),
|
||||
"aria-hidden": "true",
|
||||
div { class: "{dialog_class}",
|
||||
div { class: "modal-content", &cx.props.children }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,16 @@ use std::{
|
|||
};
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_router::use_router;
|
||||
use dioxus_router::prelude::*;
|
||||
use gloo_storage::{errors::StorageError, LocalStorage, Storage};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{HouseholdInfo, LoginInfo, RedirectorProps};
|
||||
use crate::{HouseholdInfo, LoginInfo, Route};
|
||||
|
||||
#[derive(Props)]
|
||||
pub struct RedirectorProps<'a> {
|
||||
children: Element<'a>,
|
||||
}
|
||||
|
||||
pub struct RefreshHandle {
|
||||
run: Box<dyn FnOnce()>,
|
||||
|
|
@ -159,12 +164,12 @@ fn FullContextRedirectInner<'a>(cx: Scope<'a, RedirectorProps<'a>>) -> Element {
|
|||
}
|
||||
|
||||
pub fn FullContextRedirect<'a>(cx: Scope<'a, RedirectorProps<'a>>) -> Element {
|
||||
let router = use_router(cx);
|
||||
let navigator = use_navigator(cx);
|
||||
|
||||
let check_token = match LocalStorage::get::<LoginInfo>("token") {
|
||||
Ok(_) => true,
|
||||
Err(StorageError::KeyNotFound(_)) => {
|
||||
router.navigate_to("/login");
|
||||
navigator.push(Route::Login);
|
||||
false
|
||||
}
|
||||
Err(e) => unreachable!("Could not get token: {e:?}"),
|
||||
|
|
@ -173,7 +178,7 @@ pub fn FullContextRedirect<'a>(cx: Scope<'a, RedirectorProps<'a>>) -> Element {
|
|||
let check_household = match LocalStorage::get::<HouseholdInfo>("household") {
|
||||
Ok(_) => true,
|
||||
Err(StorageError::KeyNotFound(_)) => {
|
||||
router.navigate_to("/household_selection");
|
||||
navigator.push(Route::HouseholdSelection);
|
||||
false
|
||||
}
|
||||
Err(e) => unreachable!("Could not get household: {e:?}"),
|
||||
|
|
|
|||
|
|
@ -240,8 +240,8 @@ pub fn Ingredients(cx: Scope) -> Element {
|
|||
let error = use_error(cx);
|
||||
|
||||
let add_ingredient = move |ev: FormEvent| {
|
||||
let name = ev.values["newIgName"].to_string();
|
||||
let unit = ev.values["newIgUnit"].to_string();
|
||||
let name = ev.values["newIgName"][0].to_string();
|
||||
let unit = ev.values["newIgUnit"][0].to_string();
|
||||
|
||||
if name.is_empty() && unit.is_empty() {
|
||||
return;
|
||||
|
|
|
|||
169
app/src/main.rs
169
app/src/main.rs
|
|
@ -6,16 +6,13 @@ use api::{
|
|||
LoginResponse, UserInfo,
|
||||
};
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_router::{use_route, use_router, Redirect, Route, Router};
|
||||
use dioxus_router::prelude::*;
|
||||
use gloo_storage::{errors::StorageError, LocalStorage, Storage};
|
||||
use itertools::Itertools;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
bootstrap::{bs, FormModal, ModalToggleButton, Spinner},
|
||||
sidebar::Page,
|
||||
};
|
||||
use crate::bootstrap::{bs, FormModal, ModalToggleButton, Spinner};
|
||||
|
||||
mod bootstrap;
|
||||
mod ingredients;
|
||||
|
|
@ -46,33 +43,6 @@ macro_rules! api {
|
|||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! to_owned_props {
|
||||
// Rule matching simple symbols without a path
|
||||
($es:ident $(, $($rest:tt)*)?) => {
|
||||
#[allow(unused_mut)]
|
||||
let mut $es = $es.to_owned();
|
||||
$( to_owned_props![$($rest)*] )?
|
||||
};
|
||||
|
||||
// We need to find the last element in a path, for this we need to unstack the path part by
|
||||
// part using, separating what we have with a '@'
|
||||
($($deref:ident).+ $(, $($rest:tt)*)?) => {
|
||||
to_owned_props![@ $($deref).+ $(, $($rest)*)?]
|
||||
};
|
||||
|
||||
// Take the head of the path and add it to the list of $deref
|
||||
($($deref:ident)* @ $head:ident $( . $tail:ident)+ $(, $($rest:tt)*)?) => {
|
||||
to_owned_props![$($deref)* $head @ $($tail).+ $(, $($rest)*)?]
|
||||
};
|
||||
// We have exhausted the path, use the last as a name
|
||||
($($deref:ident)* @ $last:ident $(, $($rest:tt)*)? ) => {
|
||||
#[allow(unused_mut)]
|
||||
let mut $last = $($deref .)* $last .to_owned();
|
||||
$(to_owned_props![$($rest)*])?
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Props)]
|
||||
pub struct ErrorProps<'a> {
|
||||
error: &'a Option<String>,
|
||||
|
|
@ -158,26 +128,28 @@ pub struct HouseholdInfo {
|
|||
name: String,
|
||||
}
|
||||
|
||||
#[derive(Props)]
|
||||
pub struct RedirectorProps<'a> {
|
||||
children: Element<'a>,
|
||||
}
|
||||
|
||||
pub fn LoginRedirect<'a>(cx: Scope<'a, RedirectorProps<'a>>) -> Element {
|
||||
let router = use_router(cx);
|
||||
pub fn LoginRedirect(cx: Scope) -> Element {
|
||||
let navigator = use_navigator(cx);
|
||||
|
||||
let token = match LocalStorage::get::<LoginInfo>("token") {
|
||||
Ok(v) => v,
|
||||
Ok(v) => Some(v),
|
||||
Err(StorageError::KeyNotFound(_)) => {
|
||||
router.navigate_to("/login");
|
||||
return None;
|
||||
}
|
||||
Err(e) => unreachable!("Could not get token: {e:?}"),
|
||||
};
|
||||
|
||||
use_shared_state_provider(cx, || token);
|
||||
use_shared_state_provider(cx, || token.clone());
|
||||
|
||||
cx.render(rsx! {&cx.props.children})
|
||||
cx.render(match token {
|
||||
Some(_) => rsx! {
|
||||
Outlet::<Route> {}
|
||||
},
|
||||
None => {
|
||||
navigator.push(Route::Login);
|
||||
rsx! {{}}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async fn do_login(username: String, password: String) -> anyhow::Result<()> {
|
||||
|
|
@ -242,20 +214,20 @@ fn Openid(cx: Scope) -> Element {
|
|||
|
||||
fn Login(cx: Scope) -> Element {
|
||||
let error = use_state(cx, || None::<String>);
|
||||
let router = use_router(cx);
|
||||
let navigator = use_navigator(cx);
|
||||
|
||||
let on_submit = move |e: Event<FormData>| {
|
||||
to_owned![error, router];
|
||||
to_owned![error, navigator];
|
||||
cx.spawn(async move {
|
||||
match do_login(
|
||||
e.values["username"].to_string(),
|
||||
e.values["password"].to_string(),
|
||||
e.values["username"][0].to_string(),
|
||||
e.values["password"][0].to_string(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
error.set(None);
|
||||
router.navigate_to("/");
|
||||
navigator.push(Route::Index);
|
||||
}
|
||||
Err(e) => {
|
||||
error.set(Some(format!("Could not log in: {e}")));
|
||||
|
|
@ -357,11 +329,11 @@ fn CreateHousehold(cx: Scope) -> Element {
|
|||
|
||||
let members = use_ref(cx, Vec::<(Uuid, String)>::new);
|
||||
|
||||
let router = use_router(cx);
|
||||
let navigator = use_navigator(cx);
|
||||
|
||||
let token = login.read().token.clone();
|
||||
let on_submit = move |_| {
|
||||
to_owned![members, name, error, token, router];
|
||||
to_owned![members, name, error, token, navigator];
|
||||
|
||||
cx.spawn(async move {
|
||||
match do_new_household(token.clone(), name.to_string()).await {
|
||||
|
|
@ -387,7 +359,7 @@ fn CreateHousehold(cx: Scope) -> Element {
|
|||
let modal = bs::Modal::get_instance("#newHsModal");
|
||||
modal.hide();
|
||||
|
||||
router.navigate_to("/");
|
||||
navigator.push(Route::Index);
|
||||
error.set(None);
|
||||
}
|
||||
Err(e) => {
|
||||
|
|
@ -490,7 +462,7 @@ async fn fetch_households(token: String) -> anyhow::Result<api::Households> {
|
|||
fn HouseholdListSelect(cx: Scope) -> Element {
|
||||
let login = use_login(cx);
|
||||
let households = use_future(cx, (), |_| fetch_households(login.read().token.clone()));
|
||||
let router = use_router(cx);
|
||||
let navigator = use_navigator(cx);
|
||||
|
||||
cx.render(match households.value() {
|
||||
Some(Ok(response)) => {
|
||||
|
|
@ -511,7 +483,7 @@ fn HouseholdListSelect(cx: Scope) -> Element {
|
|||
return;
|
||||
}
|
||||
|
||||
router.navigate_to("/");
|
||||
navigator.push(Route::Index);
|
||||
};
|
||||
rsx! {button { key: "{id}", class: "btn btn-secondary m-1", onclick: onclick, "{info.name}" }}
|
||||
});
|
||||
|
|
@ -542,60 +514,69 @@ fn Index(cx: Scope) -> Element {
|
|||
cx.render(rsx! {"INDEX"})
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Deserialize, PartialEq, Clone)]
|
||||
struct OidcQuery {
|
||||
token: String,
|
||||
username: String,
|
||||
}
|
||||
|
||||
fn OidcRedirect(cx: Scope) -> Element {
|
||||
let auth = use_route(cx).query::<OidcQuery>();
|
||||
#[derive(PartialEq, Props)]
|
||||
struct OidcProps {
|
||||
token: String,
|
||||
username: String,
|
||||
}
|
||||
|
||||
cx.render(match auth {
|
||||
None => rsx! {"No authentication query, internal error."},
|
||||
Some(v) => {
|
||||
match LocalStorage::set(
|
||||
"token",
|
||||
LoginInfo {
|
||||
token: v.token,
|
||||
name: v.username,
|
||||
},
|
||||
) {
|
||||
Ok(_) => {
|
||||
gloo_utils::window().location().replace("/").unwrap();
|
||||
rsx! {{}}
|
||||
}
|
||||
Err(_) => rsx! {"Could not store authentication, try again."},
|
||||
fn OidcRedirect(cx: Scope<OidcProps>) -> Element {
|
||||
cx.render({
|
||||
match LocalStorage::set(
|
||||
"token",
|
||||
LoginInfo {
|
||||
token: cx.props.token.clone(),
|
||||
name: cx.props.username.clone(),
|
||||
},
|
||||
) {
|
||||
Ok(_) => {
|
||||
gloo_utils::window().location().replace("/").unwrap();
|
||||
rsx! {{}}
|
||||
}
|
||||
Err(_) => rsx! {"Could not store authentication, try again."},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
use ingredients::Ingredients;
|
||||
use recipe::{RecipeCreator, RecipeList, RecipeView};
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[derive(Clone, Routable)]
|
||||
enum Route {
|
||||
#[route("/login")]
|
||||
Login,
|
||||
#[route("/login/oidc?:token?:username")]
|
||||
OidcRedirect { token: String, username: String },
|
||||
|
||||
#[layout(LoginRedirect)]
|
||||
#[route("/household_selection")]
|
||||
HouseholdSelection,
|
||||
#[end_layout]
|
||||
|
||||
#[layout(RegaladeSidebar)]
|
||||
#[route("/")]
|
||||
Index,
|
||||
#[route("/ingredients")]
|
||||
Ingredients,
|
||||
#[route("/recipe_creator")]
|
||||
RecipeCreator,
|
||||
#[nest("/recipe")]
|
||||
#[route("/")]
|
||||
RecipeList,
|
||||
#[route("/:id")]
|
||||
RecipeView {id: i64}
|
||||
}
|
||||
|
||||
fn App(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
Router {
|
||||
Route { to: Page::Home.to(),
|
||||
RegaladeSidebar { current: Page::Home, Index {} }
|
||||
}
|
||||
Route { to: Page::Ingredients.to(),
|
||||
RegaladeSidebar { current: Page::Ingredients, ingredients::Ingredients {} }
|
||||
}
|
||||
Route { to: Page::RecipeCreator.to(),
|
||||
RegaladeSidebar { current: Page::RecipeCreator, recipe::RecipeCreator {} }
|
||||
}
|
||||
Route { to: Page::RecipeList.to(),
|
||||
RegaladeSidebar { current: Page::RecipeList, recipe::RecipeList {} }
|
||||
}
|
||||
Route { to: "/recipe/:recipe_id",
|
||||
RegaladeSidebar { current: Page::RecipeList, recipe::RecipeView {} }
|
||||
}
|
||||
Route { to: "/login", Login {} }
|
||||
Route { to: "/login/oidc", OidcRedirect {} }
|
||||
Route { to: "/household_selection",
|
||||
LoginRedirect { HouseholdSelection {} }
|
||||
}
|
||||
Route { to: "", "Not found" }
|
||||
}
|
||||
Router::<Route> {}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::{marker::PhantomData, rc::Rc};
|
|||
|
||||
use api::{CreateRecipeRequest, CreateRecipeResponse, IngredientInfo};
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_router::use_router;
|
||||
use dioxus_router::prelude::*;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
|
|
@ -10,7 +10,7 @@ use crate::{
|
|||
bootstrap::{bs, FormModal, ModalBody, ModalFooter, ModalToggleButton, TitledModal},
|
||||
ingredients::do_add_ingredient,
|
||||
recipe::IngredientSelect,
|
||||
use_error, use_trimmed_context, ErrorView,
|
||||
use_error, use_trimmed_context, ErrorView, Route,
|
||||
};
|
||||
|
||||
use super::RecipeIngredient;
|
||||
|
|
@ -211,7 +211,7 @@ pub fn RecipeCreator(cx: Scope) -> Element {
|
|||
|
||||
let steps = use_state(cx, String::new);
|
||||
|
||||
let router = use_router(cx);
|
||||
let navigator = use_navigator(cx);
|
||||
|
||||
let ingredient_list: Vec<_> =
|
||||
ingredients.with(|ig| {
|
||||
|
|
@ -254,8 +254,9 @@ pub fn RecipeCreator(cx: Scope) -> Element {
|
|||
person_count,
|
||||
steps,
|
||||
error,
|
||||
router
|
||||
navigator
|
||||
];
|
||||
|
||||
cx.spawn(async move {
|
||||
match do_create_recipe(
|
||||
token,
|
||||
|
|
@ -281,7 +282,7 @@ pub fn RecipeCreator(cx: Scope) -> Element {
|
|||
name.set(Default::default());
|
||||
error.set(Default::default());
|
||||
|
||||
router.navigate_to(&format!("/recipe/{id}"));
|
||||
navigator.push(Route::RecipeView{id});
|
||||
}
|
||||
Err(e) => {
|
||||
error.set(Some(format!("Error creating recipe: {e:?}")));
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use dioxus::prelude::*;
|
||||
use dioxus_router::Link;
|
||||
use dioxus_router::prelude::*;
|
||||
use itertools::Itertools;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{api, bootstrap::Spinner, recipe::RecipeRating, use_trimmed_context, ErrorAlert};
|
||||
use crate::{
|
||||
api, bootstrap::Spinner, recipe::RecipeRating, use_trimmed_context, ErrorAlert, Route,
|
||||
};
|
||||
|
||||
async fn get_all_recipes(
|
||||
token: String,
|
||||
|
|
@ -37,7 +39,7 @@ pub fn RecipeList(cx: Scope) -> Element {
|
|||
div { key: "{id}", class: "col",
|
||||
div { class: "p-3 border rounded border-light-subtle h-100",
|
||||
Link {
|
||||
to: "/recipe/{id}",
|
||||
to: Route::RecipeView {id: *id},
|
||||
class: "link-light link-offset-2 link-underline-opacity-25 link-underline-opacity-100-hover",
|
||||
"{name}"
|
||||
RecipeRating { rating: *rating }
|
||||
|
|
|
|||
|
|
@ -5,15 +5,14 @@ use api::{
|
|||
RecipeInfo, RecipeIngredientEditRequest, RecipeRenameRequest,
|
||||
};
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_router::{use_route, use_router};
|
||||
use pulldown_cmark::{Parser, html};
|
||||
use pulldown_cmark::{html, Parser};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
api,
|
||||
bootstrap::{bs, ConfirmDangerModal, FormModal, ModalToggleButton, Spinner},
|
||||
recipe::{IngredientSelect, RecipeRating},
|
||||
to_owned_props, use_error, use_refresh, use_trimmed_context, Callback, ErrorAlert, ErrorView,
|
||||
use_error, use_refresh, use_trimmed_context, Callback, ErrorAlert, ErrorView,
|
||||
};
|
||||
|
||||
async fn do_rename_recipe(
|
||||
|
|
@ -53,7 +52,7 @@ fn EditName(cx: Scope<EditNameProps>) -> Element {
|
|||
error.set(Some("Name can't be empty".into()));
|
||||
}
|
||||
|
||||
to_owned_props![name, error, token, cx.props.refresh, cx.props.recipe];
|
||||
to_owned![name, error, token, cx.props.refresh, cx.props.recipe];
|
||||
|
||||
cx.spawn(async move {
|
||||
match do_rename_recipe(token, household, recipe, name.to_string()).await {
|
||||
|
|
@ -136,7 +135,7 @@ fn EditRating(cx: Scope<EditRatingProps>) -> Element {
|
|||
}
|
||||
};
|
||||
|
||||
to_owned_props![error, token, cx.props.refresh, cx.props.recipe];
|
||||
to_owned![error, token, cx.props.refresh, cx.props.recipe];
|
||||
|
||||
cx.spawn(async move {
|
||||
match do_edit_rating(token, household, recipe, rating - 1).await {
|
||||
|
|
@ -226,7 +225,7 @@ fn EditPersonCount(cx: Scope<EditPersonCountProps>) -> Element {
|
|||
}
|
||||
};
|
||||
|
||||
to_owned_props![error, token, cx.props.refresh, cx.props.recipe];
|
||||
to_owned![error, token, cx.props.refresh, cx.props.recipe];
|
||||
|
||||
cx.spawn(async move {
|
||||
match do_edit_person_count(token, household, recipe, person_count).await {
|
||||
|
|
@ -325,7 +324,7 @@ fn EditIngredient(cx: Scope<EditIngredientProps>) -> Element {
|
|||
}
|
||||
};
|
||||
|
||||
to_owned_props![
|
||||
to_owned![
|
||||
token,
|
||||
cx.props.recipe,
|
||||
cx.props.refresh,
|
||||
|
|
@ -439,7 +438,7 @@ fn AddIngredientToRecipe(cx: Scope<AddIngredientToRecipeProps>) -> Element {
|
|||
return;
|
||||
};
|
||||
|
||||
to_owned_props![
|
||||
to_owned![
|
||||
cx.props.refresh,
|
||||
cx.props.recipe,
|
||||
token,
|
||||
|
|
@ -538,13 +537,7 @@ fn EditSteps(cx: Scope<EditStepsProps>) -> Element {
|
|||
let (token, household) = use_trimmed_context(cx);
|
||||
|
||||
let on_submit = move |_| {
|
||||
to_owned_props![
|
||||
error,
|
||||
token,
|
||||
steps,
|
||||
cx.props.recipe,
|
||||
cx.props.refresh
|
||||
];
|
||||
to_owned![error, token, steps, cx.props.recipe, cx.props.refresh];
|
||||
|
||||
cx.spawn(async move {
|
||||
match do_edit_steps(token, household, recipe, steps.to_string()).await {
|
||||
|
|
@ -600,9 +593,9 @@ fn RecipeViewer(cx: Scope<RecipeViewerProps>) -> Element {
|
|||
};
|
||||
|
||||
let mk_del_ig = |&ingredient_id| {
|
||||
to_owned_props![token];
|
||||
to_owned![token];
|
||||
move |_| {
|
||||
to_owned_props![cx.props.refresh, token, cx.props.id, error];
|
||||
to_owned![cx.props.refresh, token, cx.props.id, error];
|
||||
cx.spawn(async move {
|
||||
match do_delete_ig(token, household, id, ingredient_id).await {
|
||||
Ok(_) => {
|
||||
|
|
@ -711,7 +704,7 @@ fn RecipeViewer(cx: Scope<RecipeViewerProps>) -> Element {
|
|||
}
|
||||
|
||||
#[derive(Props, PartialEq)]
|
||||
struct RecipeFetchProps {
|
||||
pub struct RecipeViewProps {
|
||||
id: i64,
|
||||
}
|
||||
|
||||
|
|
@ -729,7 +722,7 @@ async fn fetch_recipe(token: String, household: Uuid, id: i64) -> anyhow::Result
|
|||
Ok(Rc::new(rsp.json().await?))
|
||||
}
|
||||
|
||||
fn RecipeFetch(cx: Scope<RecipeFetchProps>) -> Element {
|
||||
pub fn RecipeView(cx: Scope<RecipeViewProps>) -> Element {
|
||||
let (token, household) = use_trimmed_context(cx);
|
||||
let id = cx.props.id;
|
||||
let (refresh_dep, do_refresh) = use_refresh(cx);
|
||||
|
|
@ -749,18 +742,3 @@ fn RecipeFetch(cx: Scope<RecipeFetchProps>) -> Element {
|
|||
None => rsx! { Spinner {} },
|
||||
})
|
||||
}
|
||||
|
||||
pub fn RecipeView(cx: Scope) -> Element {
|
||||
let id = use_route(cx).parse_segment_or_404("recipe_id");
|
||||
let router = use_router(cx);
|
||||
|
||||
let id = match id {
|
||||
Some(id) => id,
|
||||
None => {
|
||||
router.navigate_to("/404");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
cx.render(rsx! { RecipeFetch { id: id } })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use api::RenameHouseholdRequest;
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_router::{use_router, Link};
|
||||
use dioxus_router::prelude::*;
|
||||
use gloo_storage::{LocalStorage, Storage};
|
||||
use uuid::Uuid;
|
||||
|
||||
|
|
@ -9,7 +9,7 @@ use crate::{
|
|||
bootstrap::{bs, ConfirmDangerModal, FormModal},
|
||||
do_add_user_to_household, do_resolve_user,
|
||||
full_context::FullContextRedirect,
|
||||
use_error, use_full_context, use_trimmed_context, ErrorView, HouseholdInfo,
|
||||
use_error, use_full_context, use_trimmed_context, ErrorView, HouseholdInfo, Route,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
|
|
@ -31,6 +31,22 @@ impl Page {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Route> for Option<Page> {
|
||||
fn from(value: Route) -> Self {
|
||||
match value {
|
||||
Route::Index => Some(Page::Home),
|
||||
Route::Login => None,
|
||||
Route::OidcRedirect { .. } => None,
|
||||
Route::HouseholdSelection => None,
|
||||
Route::Ingredients => Some(Page::Ingredients),
|
||||
Route::RecipeCreator => Some(Page::RecipeCreator),
|
||||
Route::RecipeList => Some(Page::RecipeList),
|
||||
Route::RecipeView { .. } => Some(Page::RecipeList),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
struct MenuEntry {
|
||||
icon: &'static str,
|
||||
label: &'static str,
|
||||
|
|
@ -166,11 +182,10 @@ fn RenameHousehold<'a>(cx: Scope<'a>, name: &'a str) -> Element {
|
|||
})
|
||||
}
|
||||
|
||||
#[derive(Props)]
|
||||
struct SidebarProps<'a> {
|
||||
#[derive(Props, PartialEq)]
|
||||
struct SidebarProps {
|
||||
entries: Vec<MenuEntry>,
|
||||
current: Page,
|
||||
children: Element<'a>,
|
||||
}
|
||||
|
||||
async fn do_leave(token: String, household: Uuid) -> anyhow::Result<()> {
|
||||
|
|
@ -194,12 +209,12 @@ async fn do_leave(token: String, household: Uuid) -> anyhow::Result<()> {
|
|||
|
||||
fn SidebarDropdown(cx: Scope) -> Element {
|
||||
let ctx = use_full_context(cx);
|
||||
let router = use_router(cx);
|
||||
let navigator = use_navigator(cx);
|
||||
|
||||
let leave = move || {
|
||||
let token = ctx.read().login.token.clone();
|
||||
let household = ctx.read().household.id;
|
||||
to_owned![router];
|
||||
to_owned![navigator];
|
||||
|
||||
cx.spawn(async move {
|
||||
match do_leave(token, household).await {
|
||||
|
|
@ -207,7 +222,7 @@ fn SidebarDropdown(cx: Scope) -> Element {
|
|||
log::error!("Could not leave household: {e:?}");
|
||||
}
|
||||
Ok(_) => {
|
||||
router.navigate_to("/household_selection");
|
||||
navigator.push(Route::HouseholdSelection);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -217,7 +232,7 @@ fn SidebarDropdown(cx: Scope) -> Element {
|
|||
LocalStorage::delete("token");
|
||||
LocalStorage::delete("household");
|
||||
|
||||
router.navigate_to("/login");
|
||||
navigator.push(Route::Login);
|
||||
};
|
||||
|
||||
cx.render(rsx! {
|
||||
|
|
@ -280,7 +295,7 @@ fn SidebarDropdown(cx: Scope) -> Element {
|
|||
})
|
||||
}
|
||||
|
||||
fn Sidebar<'a>(cx: Scope<'a, SidebarProps<'a>>) -> Element {
|
||||
fn Sidebar(cx: Scope<SidebarProps>) -> Element {
|
||||
let entries = cx.props.entries.iter().map(|e| {
|
||||
let active = if e.page == cx.props.current {
|
||||
"active"
|
||||
|
|
@ -318,19 +333,14 @@ fn Sidebar<'a>(cx: Scope<'a, SidebarProps<'a>>) -> Element {
|
|||
SidebarDropdown {}
|
||||
}
|
||||
}
|
||||
div { class: "col py-3 overflow-scroll vh-100", &cx.props.children }
|
||||
div { class: "col py-3 overflow-scroll vh-100", Outlet::<Route> {} }
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Props)]
|
||||
pub struct RegaladeSidebarProps<'a> {
|
||||
current: Page,
|
||||
children: Element<'a>,
|
||||
}
|
||||
|
||||
pub fn RegaladeSidebar<'a>(cx: Scope<'a, RegaladeSidebarProps<'a>>) -> Element {
|
||||
pub fn RegaladeSidebar(cx: Scope) -> Element {
|
||||
let current: Route = use_route(cx).unwrap();
|
||||
let entries = vec![
|
||||
MenuEntry {
|
||||
label: "Home",
|
||||
|
|
@ -355,8 +365,8 @@ pub fn RegaladeSidebar<'a>(cx: Scope<'a, RegaladeSidebarProps<'a>>) -> Element {
|
|||
];
|
||||
|
||||
cx.render(rsx! {
|
||||
FullContextRedirect {
|
||||
Sidebar { current: cx.props.current, entries: entries, &cx.props.children }
|
||||
FullContextRedirect {
|
||||
Sidebar { current: Option::from(current).unwrap(), entries: entries }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue