app: Isolate RecipeInfoView in a separate component

This commit is contained in:
traxys 2023-06-30 12:46:31 +02:00
parent 5b44cc7dde
commit 8cf5803c1e

View file

@ -1,3 +1,5 @@
use std::rc::Rc;
use api::{
AddRecipeIngredientRequest, IngredientInfo, RecipeEditRating, RecipeEditStepsRequest,
RecipeInfo, RecipeIngredientEditRequest, RecipeRenameRequest,
@ -128,7 +130,7 @@ struct RecipeViewerInnerProps {
household: Uuid,
}
async fn fetch_recipe(token: String, household: Uuid, id: i64) -> anyhow::Result<RecipeInfo> {
async fn fetch_recipe(token: String, household: Uuid, id: i64) -> anyhow::Result<Rc<RecipeInfo>> {
let rsp = gloo_net::http::Request::get(api!("household/{household}/recipe/{id}"))
.header("Authorization", &format!("Bearer {token}"))
.send()
@ -139,7 +141,7 @@ async fn fetch_recipe(token: String, household: Uuid, id: i64) -> anyhow::Result
anyhow::bail!("Could not get recipe (code={}): {body}", rsp.status());
}
Ok(rsp.json().await?)
Ok(Rc::new(rsp.json().await?))
}
#[derive(Clone, PartialEq, Properties)]
@ -756,25 +758,23 @@ fn EditRating(props: &EditRatingProps) -> Html {
</>}
}
#[derive(Properties, PartialEq, Clone)]
struct RecipeInfoProps {
token: String,
household: Uuid,
update: Callback<()>,
recipe_id: i64,
info: Rc<RecipeInfo>,
}
#[function_component]
fn RecipeViewerInner(props: &RecipeViewerInnerProps) -> HtmlResult {
let recipe_render = use_state(|| 0u64);
let recipe = use_future_with_deps(
|_| fetch_recipe(props.token.clone(), props.household, props.id),
*recipe_render,
)?;
let update = Callback::from(move |_| {
recipe_render.set((*recipe_render).wrapping_add(1));
});
fn RecipeInfoView(props: &RecipeInfoProps) -> Html {
let error = use_state(|| None::<String>);
let mk_del_ig = |&id| {
let update = update.clone();
let update = props.update.clone();
let token = props.token.clone();
let household = props.household;
let recipe = props.id;
let recipe = props.recipe_id;
let err = error.clone();
Callback::from(move |_| {
let update = update.clone();
@ -794,23 +794,22 @@ fn RecipeViewerInner(props: &RecipeViewerInnerProps) -> HtmlResult {
})
};
Ok(match &*recipe {
Ok(r) => html! {<>
<h1>{&r.name} <RecipeRating rating={r.rating} /> </h1>
html! {<>
<h1>{&props.info.name} <RecipeRating rating={props.info.rating} /> </h1>
<div class="mt-2">
<EditName
token={props.token.clone()}
id={props.id}
id={props.recipe_id}
household={props.household}
name={r.name.clone()}
update={update.clone()}
name={props.info.name.clone()}
update={props.update.clone()}
/>
<EditRating
token={props.token.clone()}
recipe={props.id}
recipe={props.recipe_id}
household={props.household}
rating={r.rating + 1}
update={update.clone()}
rating={props.info.rating + 1}
update={props.update.clone()}
/>
</div>
if let Some(e) = &*error {
@ -822,7 +821,7 @@ fn RecipeViewerInner(props: &RecipeViewerInnerProps) -> HtmlResult {
<div class="text-start">
<h2>{"Ingredients"}</h2>
<ul class="list-group mb-2">
{for r.ingredients.iter().map(|(id, info, amount)| {
{for props.info.ingredients.iter().map(|(id, info, amount)| {
let delete_modal_id = format!("rcpRmIg{id}");
let amount_rounded = amount.round();
html!{
@ -848,11 +847,11 @@ fn RecipeViewerInner(props: &RecipeViewerInnerProps) -> HtmlResult {
</ModalToggleButton>
<EditIngredient
token={props.token.clone()}
recipe={props.id}
recipe={props.recipe_id}
ingredient={*id}
household={props.household}
amount={*amount}
update={update.clone()}
update={props.update.clone()}
/>
</div>
</li>
@ -861,27 +860,53 @@ fn RecipeViewerInner(props: &RecipeViewerInnerProps) -> HtmlResult {
<AddIngredient
token={props.token.clone()}
household={props.household}
recipe={props.id}
update={update.clone()}
recipe={props.recipe_id}
update={props.update.clone()}
/>
</div>
<hr />
<div class="text-start">
<h2>{"Steps"}</h2>
<ul class="list-group list-group-flush">
{for r.steps.split('\n').map(|text| html!{
{for props.info.steps.split('\n').map(|text| html!{
<li class="list-group-item">{text}</li>
})}
</ul>
<EditSteps
token={props.token.clone()}
household={props.household}
recipe={props.id}
steps={r.steps.clone()}
update={update}
recipe={props.recipe_id}
steps={props.info.steps.clone()}
update={props.update.clone()}
/>
</div>
</>},
</>}
}
#[function_component]
fn RecipeViewerInner(props: &RecipeViewerInnerProps) -> HtmlResult {
let recipe_render = use_state(|| 0u64);
let recipe = use_future_with_deps(
|_| fetch_recipe(props.token.clone(), props.household, props.id),
*recipe_render,
)?;
let update = Callback::from(move |_| {
recipe_render.set((*recipe_render).wrapping_add(1));
});
Ok(match &*recipe {
Ok(r) => {
html! {
<RecipeInfoView
token={props.token.clone()}
recipe_id={props.id}
info={r.clone()}
household={props.household}
{update}
/>
}
}
Err(e) => html! {
<div class={classes!("alert", "alert-danger")} role="alert">
{format!("Error fetching recipe: {e}")}