use err ctx

This commit is contained in:
Asger Juul Brunshøj 2023-06-22 21:30:00 +02:00
parent 5b49586da3
commit d39596da77
9 changed files with 70 additions and 31 deletions

View File

@ -1,10 +1,13 @@
use crate::components::error::error_provider::ErrorContext;
use crate::services::rest::RestService;
use common::Achievement;
use common::ToggleAchievement;
use std::rc::Rc;
use wasm_bindgen_futures::spawn_local;
use yew::classes;
use yew::function_component;
use yew::html;
use yew::use_context;
use yew::Callback;
use yew::Html;
use yew::Properties;
@ -17,6 +20,8 @@ pub struct Props {
#[function_component]
pub fn AchievementComponent(props: &Props) -> Html {
let err_ctx = Rc::new(use_context::<ErrorContext>());
let Achievement {
goal,
completed,
@ -28,10 +33,15 @@ pub fn AchievementComponent(props: &Props) -> Html {
let onclick_toggle = Callback::from(move |_| {
log::info!("button click, toggling achievement");
let err_ctx = Rc::clone(&err_ctx);
spawn_local(async move {
match RestService::toggle_achievement(ToggleAchievement { uuid }).await {
Ok(_response) => {}
Err(_err) => {}
Err(err) => {
if let Some(err_ctx) = &*err_ctx {
err_ctx.dispatch(err.to_string());
}
}
}
});
});

View File

@ -1,8 +1,10 @@
use crate::components::error::error_provider::ErrorContext;
use crate::services::confirm::ConfirmService;
use crate::services::rest::RestService;
use common::DeleteAchievement;
use common::DeleteMilestone;
use common::UpdateAchievementTimeOfReveal;
use std::rc::Rc;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::spawn_local;
use yew::function_component;
@ -27,8 +29,9 @@ pub fn Admin() -> Html {
.enumerate()
.map(|(idx, a)| (idx + 1, a))
.map(|(n, a)| {
let uuid = a.uuid.to_string();
html! {
<Achievement number={n} achievement={a} />
<Achievement key={uuid} number={n} achievement={a} />
}
})
.collect::<Html>();
@ -48,7 +51,10 @@ pub fn Admin() -> Html {
milestones.sort_by_key(|m| m.goal);
let milestones = milestones
.into_iter()
.map(|m| html! { <Milestone milestone={m}/> })
.map(|m| {
let uuid = m.uuid.to_string();
html! { <Milestone key={uuid} milestone={m}/> }
})
.collect::<Html>();
html! {
@ -93,6 +99,7 @@ struct AchievementProps {
#[function_component]
fn Achievement(props: &AchievementProps) -> Html {
let err_ctx = Rc::new(use_context::<ErrorContext>());
let achievement = &props.achievement;
let uuid = achievement.uuid;
@ -108,6 +115,7 @@ fn Achievement(props: &AchievementProps) -> Html {
let awaiting_response = awaiting_response.clone();
let timed_reveal_enabled = timed_reveal_enabled.clone();
let input_time = input_time.clone();
let err_ctx = Rc::clone(&err_ctx);
Callback::from(move |e: web_sys::SubmitEvent| {
e.prevent_default();
@ -129,8 +137,16 @@ fn Achievement(props: &AchievementProps) -> Html {
};
awaiting_response.set(true);
let awaiting_response = awaiting_response.clone();
let err_ctx = Rc::clone(&err_ctx);
spawn_local(async move {
let res = RestService::update_time_of_reveal(payload).await;
match RestService::update_time_of_reveal(payload).await {
Ok(_response) => {}
Err(err) => {
if let Some(err_ctx) = &*err_ctx {
err_ctx.dispatch(err.to_string());
}
}
}
awaiting_response.set(false);
});
})
@ -163,10 +179,15 @@ fn Achievement(props: &AchievementProps) -> Html {
}
log::info!("Delete achievement confirmed.");
let err_ctx = Rc::clone(&err_ctx);
spawn_local(async move {
match RestService::delete_achievement(DeleteAchievement { uuid }).await {
Ok(_response) => {}
Err(_err) => {}
Err(err) => {
if let Some(err_ctx) = &*err_ctx {
err_ctx.dispatch(err.to_string());
}
}
}
});
});
@ -192,7 +213,7 @@ fn Achievement(props: &AchievementProps) -> Html {
</div>
// Timed reveal form
<form onsubmit={onsubmit_timed_reveal}>
<form onsubmit={onsubmit_timed_reveal} style="margin-left: 30px">
// Timed reveal: Enable checkbox
<label>
<span class="label-body" style="margin-right: 0.5rem; font-weight: bold"><i class="fas fa-clock" style="padding-right: 0.5em"/>{"Enable Timed Reveal:"}</span>
@ -215,23 +236,27 @@ fn Achievement(props: &AchievementProps) -> Html {
</div>
</form>
<hr />
</div>
}
}
#[function_component]
fn Milestone(props: &MilestoneProps) -> Html {
let err_ctx = Rc::new(use_context::<ErrorContext>());
let uuid = props.milestone.uuid;
let onclick_delete = Callback::from(move |_| {
if !ConfirmService::confirm("Are you sure you want to delete?") {
return;
}
let err_ctx = Rc::clone(&err_ctx);
spawn_local(async move {
match RestService::delete_milestone(DeleteMilestone { uuid }).await {
Ok(_response) => {}
Err(_err) => {}
Err(err) => {
if let Some(err_ctx) = &*err_ctx {
err_ctx.dispatch(err.to_string());
}
}
}
});
});

View File

@ -1,4 +1,4 @@
use super::error::error_provider::ErrorContext;
use super::error::error_provider::get_error_context;
use crate::services::rest::RestService;
use crate::services::rest::RestServiceError;
use common::CreateAchievement;
@ -66,14 +66,12 @@ impl Component for CreateAchievementComponent {
match result {
Ok(_response) => {
let nav = ctx.link().navigator().unwrap();
nav.push(&crate::Route::Root);
nav.push(&crate::Route::Admin);
}
Err(err) => {
let (error_context, _context_listener) = ctx
.link()
.context(Callback::from(|_: ErrorContext| ()))
.expect("No Error Context Provided");
error_context.dispatch(err.to_string());
if let Some(err_ctx) = get_error_context(ctx) {
err_ctx.dispatch(err.to_string());
}
}
};
self.awaiting_response = false;

View File

@ -1,4 +1,4 @@
use super::error::error_provider::ErrorContext;
use super::error::error_provider::get_error_context;
use crate::services::rest::RestService;
use crate::services::rest::RestServiceError;
use common::CreateMilestone;
@ -67,14 +67,12 @@ impl Component for CreateMilestoneComponent {
match result {
Ok(_response) => {
let nav = ctx.link().navigator().unwrap();
nav.push(&crate::Route::Root);
nav.push(&crate::Route::Admin)
}
Err(err) => {
let (error_context, _context_listener) = ctx
.link()
.context(Callback::from(|_: ErrorContext| ()))
.expect("No Error Context Provided");
error_context.dispatch(err.to_string());
if let Some(err_ctx) = get_error_context(ctx) {
err_ctx.dispatch(err.to_string());
}
}
};
self.awaiting_response = false;
@ -92,7 +90,7 @@ impl Component for CreateMilestoneComponent {
let nav = ctx.link().navigator().unwrap();
let onclick_go_back = Callback::from(move |_: web_sys::MouseEvent| {
nav.push(&crate::Route::Root);
nav.push(&crate::Route::Admin);
});
let onsubmit = link.callback(|e: web_sys::SubmitEvent| {

View File

@ -1,3 +1,5 @@
//! This is the UI component that subscribes to and displays errors.
use super::error_provider::ErrorContext;
use yew::prelude::*;

View File

@ -41,3 +41,10 @@ pub fn ErrorProvider(props: &ErrorProviderProps) -> Html {
</ContextProvider<ErrorContext>>
}
}
/// Helper for struct components to get the error context
pub fn get_error_context(ctx: &Context<impl yew::Component>) -> Option<ErrorContext> {
ctx.link()
.context(Callback::from(|_: ErrorContext| ()))
.map(|(error_context, _context_handle)| error_context)
}

View File

@ -3,11 +3,9 @@ use crate::components::milestone::MilestoneComponent;
use crate::util::group_achievements::group_achievements;
use yew::functional::*;
use yew::prelude::*;
use yew_router::prelude::*;
#[function_component]
pub fn Root() -> Html {
let nav = use_navigator().expect("cannot get navigator");
let app_state = use_context::<crate::AppState>().expect("no app state ctx found");
let current_time: chrono::NaiveTime = chrono::Local::now().time();
@ -21,7 +19,7 @@ pub fn Root() -> Html {
if let Some(time_of_reveal) = time_of_reveal {
let s = if time_of_reveal > current_time {
format!(
"{} more revealed at {}!",
"Next {} revealed at {}!",
group.len(),
time_of_reveal.format("%H:%M")
)
@ -38,8 +36,9 @@ pub fn Root() -> Html {
}
for a in group {
n += 1;
let uuid = a.uuid.to_string();
let html = html! {
<AchievementComponent number={n} achievement={a} />
<AchievementComponent key={uuid} number={n} achievement={a} />
};
achievements_html.push(html);
}
@ -55,7 +54,7 @@ pub fn Root() -> Html {
let mut milestones = app_state.state.milestones.clone();
milestones.sort_by_key(|m| m.goal);
let milestones = milestones.into_iter().map(|m| html! { <MilestoneComponent milestone={m} completed_achievements={completed_achievements} /> }).collect::<Html>();
let milestones = milestones.into_iter().map(|m| {let uuid = m.uuid.to_string(); html! { <MilestoneComponent key={uuid} milestone={m} completed_achievements={completed_achievements} /> }}).collect::<Html>();
html! {
<>

View File

@ -41,7 +41,7 @@ impl RestService {
})?;
if let Ok(rest_response) = response.json::<RestResponse<T>>().await {
return rest_response.map_err(Into::into);
return rest_response.map_err(RestServiceError::RestError);
}
let err = RestServiceError::UnexpectedResponse(response);

View File

@ -38,7 +38,7 @@ mod tests {
let time1 = NaiveTime::from_hms_opt(12, 0, 0).unwrap();
let time2 = NaiveTime::from_hms_opt(13, 0, 0).unwrap();
let mut achievements = vec![
let achievements = vec![
Achievement {
goal: "Goal 1".to_string(),
completed: true,