use crate::{ components::user_details::User, infra::common_component::{CommonComponent, CommonComponentParts}, }; use anyhow::{bail, Error, Result}; use graphql_client::GraphQLQuery; use validator_derive::Validate; use yew::prelude::*; use yew_form_derive::Model; /// The fields of the form, with the editable details and the constraints. #[derive(Model, Validate, PartialEq, Clone)] pub struct UserModel { #[validate(email)] email: String, #[validate(length(min = 1, message = "Display name is required"))] display_name: String, first_name: String, last_name: String, } /// The GraphQL query sent to the server to update the user details. #[derive(GraphQLQuery)] #[graphql( schema_path = "../schema.graphql", query_path = "queries/update_user.graphql", response_derives = "Debug", variables_derives = "Clone,PartialEq", custom_scalars_module = "crate::infra::graphql" )] pub struct UpdateUser; /// A [yew::Component] to display the user details, with a form allowing to edit them. pub struct UserDetailsForm { common: CommonComponentParts, form: yew_form::Form, /// True if we just successfully updated the user, to display a success message. just_updated: bool, } pub enum Msg { /// A form field changed. Update, /// The "Submit" button was clicked. SubmitClicked, /// We got the response from the server about our update message. UserUpdated(Result), } #[derive(yew::Properties, Clone, PartialEq)] pub struct Props { /// The current user details. pub user: User, /// Callback to report errors (e.g. server error). pub on_error: Callback, } impl CommonComponent for UserDetailsForm { fn handle_msg(&mut self, msg: ::Message) -> Result { match msg { Msg::Update => Ok(true), Msg::SubmitClicked => self.submit_user_update_form(), Msg::UserUpdated(response) => self.user_update_finished(response), } } fn mut_common(&mut self) -> &mut CommonComponentParts { &mut self.common } } impl Component for UserDetailsForm { type Message = Msg; type Properties = Props; fn create(props: Self::Properties, link: ComponentLink) -> Self { let model = UserModel { email: props.user.email.clone(), display_name: props.user.display_name.clone(), first_name: props.user.first_name.clone(), last_name: props.user.last_name.clone(), }; Self { common: CommonComponentParts::::create(props, link), form: yew_form::Form::new(model), just_updated: false, } } fn update(&mut self, msg: Self::Message) -> ShouldRender { self.just_updated = false; CommonComponentParts::::update_and_report_error( self, msg, self.common.on_error.clone(), ) } fn change(&mut self, props: Self::Properties) -> ShouldRender { self.common.change(props) } fn view(&self) -> Html { type Field = yew_form::Field; html! {
{&self.common.user.id}
{&self.form.field_message("email")}
{&self.form.field_message("display_name")}
{&self.form.field_message("first_name")}
{&self.form.field_message("last_name")}
{&self.common.user.creation_date.date().naive_local()}
{&self.common.user.uuid}
} } } impl UserDetailsForm { fn submit_user_update_form(&mut self) -> Result { if !self.form.validate() { bail!("Invalid inputs"); } let base_user = &self.common.user; let mut user_input = update_user::UpdateUserInput { id: self.common.user.id.clone(), email: None, displayName: None, firstName: None, lastName: None, avatar: None, }; let default_user_input = user_input.clone(); let model = self.form.model(); let email = model.email; if base_user.email != email { user_input.email = Some(email); } if base_user.display_name != model.display_name { user_input.displayName = Some(model.display_name); } if base_user.first_name != model.first_name { user_input.firstName = Some(model.first_name); } if base_user.last_name != model.last_name { user_input.lastName = Some(model.last_name); } // Nothing changed. if user_input == default_user_input { return Ok(false); } let req = update_user::Variables { user: user_input }; self.common.call_graphql::( req, Msg::UserUpdated, "Error trying to update user", ); Ok(false) } fn user_update_finished(&mut self, r: Result) -> Result { self.common.cancel_task(); match r { Err(e) => return Err(e), Ok(_) => { let model = self.form.model(); self.common.user = User { id: self.common.user.id.clone(), email: model.email, display_name: model.display_name, first_name: model.first_name, last_name: model.last_name, creation_date: self.common.user.creation_date, uuid: self.common.user.uuid.clone(), groups: self.common.user.groups.clone(), }; self.just_updated = true; } }; Ok(true) } }