app: Migrate AddGroupMember to CommonComponent

This commit is contained in:
Valentin Tolmer 2021-10-31 21:31:15 +09:00 committed by nitnelave
parent 540ac5d241
commit 232a41d053
2 changed files with 74 additions and 64 deletions

View File

@ -1,14 +1,11 @@
use crate::{ use crate::{
components::select::{Select, SelectOption, SelectOptionProps}, components::select::{Select, SelectOption, SelectOptionProps},
infra::api::HostService, infra::common_component::{CommonComponent, CommonComponentParts},
}; };
use anyhow::{Error, Result}; use anyhow::{Error, Result};
use graphql_client::GraphQLQuery; use graphql_client::GraphQLQuery;
use std::collections::HashSet; use std::collections::HashSet;
use yew::{ use yew::prelude::*;
prelude::*,
services::{fetch::FetchTask, ConsoleService},
};
use yewtil::NeqAssign; use yewtil::NeqAssign;
#[derive(GraphQLQuery)] #[derive(GraphQLQuery)]
@ -33,14 +30,11 @@ pub struct ListUserNames;
pub type User = list_user_names::ListUserNamesUsers; pub type User = list_user_names::ListUserNamesUsers;
pub struct AddGroupMemberComponent { pub struct AddGroupMemberComponent {
link: ComponentLink<Self>, common: CommonComponentParts<Self>,
props: Props,
/// The list of existing users, initially not loaded. /// The list of existing users, initially not loaded.
user_list: Option<Vec<User>>, user_list: Option<Vec<User>>,
/// The currently selected user. /// The currently selected user.
selected_user: Option<User>, selected_user: Option<User>,
// Used to keep the request alive long enough.
task: Option<FetchTask>,
} }
pub enum Msg { pub enum Msg {
@ -58,58 +52,24 @@ pub struct Props {
pub on_error: Callback<Error>, pub on_error: Callback<Error>,
} }
impl AddGroupMemberComponent { impl CommonComponent<AddGroupMemberComponent> for AddGroupMemberComponent {
fn get_user_list(&mut self) {
self.task = HostService::graphql_query::<ListUserNames>(
list_user_names::Variables { filters: None },
self.link.callback(Msg::UserListResponse),
"Error trying to fetch user list",
)
.map_err(|e| {
ConsoleService::log(&e.to_string());
e
})
.ok();
}
fn submit_add_member(&mut self) -> Result<bool> {
let user_id = match self.selected_user.clone() {
None => return Ok(false),
Some(user) => user.id,
};
self.task = HostService::graphql_query::<AddUserToGroup>(
add_user_to_group::Variables {
user: user_id,
group: self.props.group_id,
},
self.link.callback(Msg::AddMemberResponse),
"Error trying to initiate adding the user to a group",
)
.map_err(|e| {
ConsoleService::log(&e.to_string());
e
})
.ok();
Ok(true)
}
fn handle_msg(&mut self, msg: <Self as Component>::Message) -> Result<bool> { fn handle_msg(&mut self, msg: <Self as Component>::Message) -> Result<bool> {
match msg { match msg {
Msg::UserListResponse(response) => { Msg::UserListResponse(response) => {
self.user_list = Some(response?.users); self.user_list = Some(response?.users);
self.task = None; self.common.cancel_task();
} }
Msg::SubmitAddMember => return self.submit_add_member(), Msg::SubmitAddMember => return self.submit_add_member(),
Msg::AddMemberResponse(response) => { Msg::AddMemberResponse(response) => {
response?; response?;
self.task = None; self.common.cancel_task();
let user = self let user = self
.selected_user .selected_user
.as_ref() .as_ref()
.expect("Could not get selected user") .expect("Could not get selected user")
.clone(); .clone();
// Remove the user from the dropdown. // Remove the user from the dropdown.
self.props.on_user_added_to_group.emit(user); self.common.props.on_user_added_to_group.emit(user);
} }
Msg::SelectionChanged(option_props) => { Msg::SelectionChanged(option_props) => {
let was_some = self.selected_user.is_some(); let was_some = self.selected_user.is_some();
@ -123,8 +83,38 @@ impl AddGroupMemberComponent {
Ok(true) Ok(true)
} }
fn mut_common(&mut self) -> &mut CommonComponentParts<Self> {
&mut self.common
}
}
impl AddGroupMemberComponent {
fn get_user_list(&mut self) {
self.common.call_graphql::<ListUserNames, _>(
list_user_names::Variables { filters: None },
Msg::UserListResponse,
"Error trying to fetch user list",
);
}
fn submit_add_member(&mut self) -> Result<bool> {
let user_id = match self.selected_user.clone() {
None => return Ok(false),
Some(user) => user.id,
};
self.common.call_graphql::<AddUserToGroup, _>(
add_user_to_group::Variables {
user: user_id,
group: self.common.props.group_id,
},
Msg::AddMemberResponse,
"Error trying to initiate adding the user to a group",
);
Ok(true)
}
fn get_selectable_user_list(&self, user_list: &[User]) -> Vec<User> { fn get_selectable_user_list(&self, user_list: &[User]) -> Vec<User> {
let user_groups = self.props.users.iter().collect::<HashSet<_>>(); let user_groups = self.common.props.users.iter().collect::<HashSet<_>>();
user_list user_list
.iter() .iter()
.filter(|u| !user_groups.contains(u)) .filter(|u| !user_groups.contains(u))
@ -136,32 +126,27 @@ impl AddGroupMemberComponent {
impl Component for AddGroupMemberComponent { impl Component for AddGroupMemberComponent {
type Message = Msg; type Message = Msg;
type Properties = Props; type Properties = Props;
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self { fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
let mut res = Self { let mut res = Self {
link, common: CommonComponentParts::<Self>::create(props, link),
props,
user_list: None, user_list: None,
selected_user: None, selected_user: None,
task: None,
}; };
res.get_user_list(); res.get_user_list();
res res
} }
fn update(&mut self, msg: Self::Message) -> ShouldRender { fn update(&mut self, msg: Self::Message) -> ShouldRender {
match self.handle_msg(msg) { CommonComponentParts::<Self>::update_and_report_error(
Err(e) => { self,
ConsoleService::error(&e.to_string()); msg,
self.props.on_error.emit(e); self.common.props.on_error.clone(),
self.task = None; )
true
}
Ok(b) => b,
}
} }
fn change(&mut self, props: Self::Properties) -> ShouldRender { fn change(&mut self, props: Self::Properties) -> ShouldRender {
self.props.neq_assign(props) self.common.props.neq_assign(props)
} }
fn view(&self) -> Html { fn view(&self) -> Html {
@ -176,7 +161,7 @@ impl Component for AddGroupMemberComponent {
html! { html! {
<div class="row"> <div class="row">
<div class="col-sm-3"> <div class="col-sm-3">
<Select on_selection_change=self.link.callback(Msg::SelectionChanged)> <Select on_selection_change=self.common.callback(Msg::SelectionChanged)>
{ {
to_add_user_list to_add_user_list
.into_iter() .into_iter()
@ -188,8 +173,8 @@ impl Component for AddGroupMemberComponent {
<div class="col-sm-1"> <div class="col-sm-1">
<button <button
class="btn btn-success" class="btn btn-success"
disabled=self.selected_user.is_none() || self.task.is_some() disabled=self.selected_user.is_none() || self.common.is_task_running()
onclick=self.link.callback(|_| Msg::SubmitAddMember)> onclick=self.common.callback(|_| Msg::SubmitAddMember)>
{"Add"} {"Add"}
</button> </button>
</div> </div>

View File

@ -5,6 +5,7 @@ use yew::{
prelude::*, prelude::*,
services::{fetch::FetchTask, ConsoleService}, services::{fetch::FetchTask, ConsoleService},
}; };
use yewtil::NeqAssign;
pub trait CommonComponent<C: Component + CommonComponent<C>>: Component { pub trait CommonComponent<C: Component + CommonComponent<C>>: Component {
fn handle_msg(&mut self, msg: <Self as Component>::Message) -> Result<bool>; fn handle_msg(&mut self, msg: <Self as Component>::Message) -> Result<bool>;
@ -42,12 +43,36 @@ impl<C: CommonComponent<C>> CommonComponentParts<C> {
Err(e) => { Err(e) => {
ConsoleService::error(&e.to_string()); ConsoleService::error(&e.to_string());
com.mut_common().error = Some(e); com.mut_common().error = Some(e);
com.mut_common().cancel_task();
true true
} }
Ok(b) => b, Ok(b) => b,
} }
} }
pub fn update_and_report_error(
com: &mut C,
msg: <C as Component>::Message,
report_fn: Callback<Error>,
) -> ShouldRender {
let should_render = Self::update(com, msg);
com.mut_common()
.error
.take()
.map(|e| {
report_fn.emit(e);
true
})
.unwrap_or(should_render)
}
pub fn change(&mut self, props: <C as Component>::Properties) -> ShouldRender
where
<C as yew::Component>::Properties: std::cmp::PartialEq,
{
self.props.neq_assign(props)
}
pub fn callback<F, IN, M>(&self, function: F) -> Callback<IN> pub fn callback<F, IN, M>(&self, function: F) -> Callback<IN>
where where
M: Into<C::Message>, M: Into<C::Message>,