diff --git a/app/src/infra/common_component.rs b/app/src/infra/common_component.rs index 8ebc630..99494f8 100644 --- a/app/src/infra/common_component.rs +++ b/app/src/infra/common_component.rs @@ -1,3 +1,26 @@ +//! Common Component module. +//! This is used to factor out some common functionality that is recurrent in modules all over the +//! application. In particular: +//! - error handling +//! - task handling +//! - storing props +//! +//! The pattern used is the +//! [CRTP](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) pattern: The +//! [`CommonComponent`] trait must be implemented with `Self` as the parameter, e.g. +//! +//! ```ignore +//! struct MyComponent; +//! impl CommonComponent for MyComponent { ... } +//! ``` +//! +//! The component should also have a `CommonComponentParts` as a field, usually named +//! `common`. +//! +//! Then the [`yew::prelude::Component::update`] method can delegate to +//! [`CommonComponentParts::update`]. This will in turn call [`CommonComponent::handle_msg`] and +//! take care of error and task handling. + use crate::infra::api::HostService; use anyhow::{Error, Result}; use graphql_client::GraphQLQuery; @@ -7,11 +30,18 @@ use yew::{ }; use yewtil::NeqAssign; +/// Trait required for common components. pub trait CommonComponent>: Component { + /// Handle the incoming message. If an error is returned here, any running task will be + /// cancelled, the error will be written to the [`CommonComponentParts::error`] and the + /// component will be refreshed. fn handle_msg(&mut self, msg: ::Message) -> Result; + /// Get a mutable reference to the inner component parts, necessary for the CRTP. fn mut_common(&mut self) -> &mut CommonComponentParts; } +/// Structure that contains the common parts needed by most components. +/// The fields of [`props`] are directly accessible through a `Deref` implementation. pub struct CommonComponentParts> { link: ComponentLink, pub props: ::Properties, @@ -20,10 +50,12 @@ pub struct CommonComponentParts> { } impl> CommonComponentParts { + /// Whether there is a currently running task in the background. pub fn is_task_running(&self) -> bool { self.task.is_some() } + /// Cancel any background task. pub fn cancel_task(&mut self) { self.task = None; } @@ -37,6 +69,8 @@ impl> CommonComponentParts { } } + /// This should be called from the [`yew::prelude::Component::update`]: it will in turn call + /// [`CommonComponent::handle_msg`] and handle any resulting error. pub fn update(com: &mut C, msg: ::Message) -> ShouldRender { com.mut_common().error = None; match com.handle_msg(msg) { @@ -50,6 +84,7 @@ impl> CommonComponentParts { } } + /// Same as above, but the resulting error is instead passed to the reporting function. pub fn update_and_report_error( com: &mut C, msg: ::Message, @@ -66,6 +101,8 @@ impl> CommonComponentParts { .unwrap_or(should_render) } + /// This can be called from [`yew::prelude::Component::update`]: it will check if the + /// properties have changed and return whether the component should update. pub fn change(&mut self, props: ::Properties) -> ShouldRender where ::Properties: std::cmp::PartialEq, @@ -73,6 +110,7 @@ impl> CommonComponentParts { self.props.neq_assign(props) } + /// Create a callback from the link. pub fn callback(&self, function: F) -> Callback where M: Into, @@ -81,6 +119,8 @@ impl> CommonComponentParts { self.link.callback(function) } + /// Call `method` from the backend with the given `request`, and pass the `callback` for the + /// result. Returns whether _starting the call_ failed. pub fn call_backend( &mut self, method: M, @@ -95,6 +135,9 @@ impl> CommonComponentParts { Ok(()) } + /// Call the backend with a GraphQL query. + /// + /// `EnumCallback` should usually be left as `_`. pub fn call_graphql( &mut self, variables: QueryType::Variables,