Update to dioxus 0.4

This commit is contained in:
traxys 2023-08-05 12:54:49 +02:00
parent c36ce14b3b
commit 183f8a75d2
10 changed files with 328 additions and 586 deletions

View file

@ -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> {}
})
}