diff options
-rw-r--r-- | src/view/address.rs | 11 | ||||
-rw-r--r-- | src/view/dates.rs | 11 | ||||
-rw-r--r-- | src/view/main.rs | 66 | ||||
-rw-r--r-- | src/view/mod.rs | 12 | ||||
-rw-r--r-- | src/view/name.rs | 11 | ||||
-rw-r--r-- | src/view/organizational.rs | 83 | ||||
-rw-r--r-- | src/view/telephone.rs | 11 | ||||
-rw-r--r-- | src/viewmodel/address.rs | 2 | ||||
-rw-r--r-- | src/viewmodel/dates.rs | 2 | ||||
-rw-r--r-- | src/viewmodel/mod.rs | 3 | ||||
-rw-r--r-- | src/viewmodel/name.rs | 2 | ||||
-rw-r--r-- | src/viewmodel/organizational.rs | 86 | ||||
-rw-r--r-- | src/viewmodel/telephone.rs | 2 | ||||
-rw-r--r-- | src/viewmodel/vcard.rs | 4 |
14 files changed, 272 insertions, 34 deletions
diff --git a/src/view/address.rs b/src/view/address.rs index 1aedcbd..a945272 100644 --- a/src/view/address.rs +++ b/src/view/address.rs @@ -1,11 +1,13 @@ +use crate::view::InputProps; use yew::prelude::*; use yewtil::NeqAssign; -use super::WeakComponentLink; use crate::viewmodel::address::*; use crate::viewmodel::VCardPropertyInputObject; use super::VCardPropertyInputComponent; use crate::viewmodel::Error; +type Props = InputProps<Address,AddressView>; + /// View Component for a `address` field /// /// # Examples @@ -20,6 +22,7 @@ use crate::viewmodel::Error; /// /> /// }; /// ``` +#[derive(Clone,PartialEq)] pub struct AddressView { props: Props, value: Address, @@ -40,12 +43,6 @@ pub enum Msg { Generate, } -#[derive(Clone, PartialEq, Properties)] -pub struct Props { - pub generated: Callback<Address>, - pub weak_link: WeakComponentLink<AddressView>, -} - impl VCardPropertyInputComponent<Address> for AddressView { fn get_input_object(&self) -> Address { self.value.clone() diff --git a/src/view/dates.rs b/src/view/dates.rs index 1c16680..de3d311 100644 --- a/src/view/dates.rs +++ b/src/view/dates.rs @@ -1,11 +1,14 @@ +use crate::view::InputProps; use yew::prelude::*; use yewtil::NeqAssign; use crate::viewmodel::Error; -use crate::view::WeakComponentLink; use crate::viewmodel::dates::*; use crate::viewmodel::VCardPropertyInputObject; use super::VCardPropertyInputComponent; +type Props = InputProps<Dates,DatesView>; + +#[derive(Clone,PartialEq)] pub struct DatesView { props: Props, value: Dates, @@ -19,12 +22,6 @@ pub enum Msg { Generate, } -#[derive(Clone, PartialEq, Properties)] -pub struct Props { - pub generated: Callback<Dates>, - pub weak_link: WeakComponentLink<DatesView>, -} - impl VCardPropertyInputComponent<Dates> for DatesView { fn get_input_object(&self) -> Dates { self.value.clone() diff --git a/src/view/main.rs b/src/view/main.rs index 0d2cddb..c2af72a 100644 --- a/src/view/main.rs +++ b/src/view/main.rs @@ -1,3 +1,5 @@ +use crate::viewmodel::organizational::Organizational; +use crate::view::organizational::{self,OrganizationalView}; use crate::viewmodel::dates::Dates; use yew::services::ConsoleService; use crate::viewmodel::vcard::VCardData; @@ -35,6 +37,7 @@ pub struct MainView { address_links: Vec<WeakComponentLink<AddressView>>, telephone_links: Vec<WeakComponentLink<TelephoneView>>, dates_links: Vec<WeakComponentLink<DatesView>>, + organizational_links: Vec<WeakComponentLink<OrganizationalView>>, answer_count: usize, } @@ -44,6 +47,7 @@ pub enum Msg { AddAddress, AddTelephone, AddDates, + AddOrganizational, ChangeDownloadOption(DownloadOption), @@ -52,6 +56,7 @@ pub enum Msg { GeneratedAddress(Address), GeneratedTelephone(Telephone), GeneratedDates(Dates), + GeneratedOrganizational(Organizational), GenerationComplete, Nope, @@ -73,6 +78,7 @@ impl Component for MainView { address_links: vec![WeakComponentLink::default()], telephone_links: vec![WeakComponentLink::default()], dates_links: vec![WeakComponentLink::default()], + organizational_links: vec![WeakComponentLink::default()], answer_count: 0, } } @@ -98,6 +104,10 @@ impl Component for MainView { self.dates_links.push(WeakComponentLink::default()); shouldrender = true; }, + Msg::AddOrganizational => { + self.organizational_links.push(WeakComponentLink::default()); + shouldrender = true; + }, Msg::ChangeDownloadOption(option) => { self.selected_option = option; shouldrender = false; @@ -125,6 +135,11 @@ impl Component for MainView { let dates_link = dates_link.borrow().clone().unwrap(); dates_link.send_message(dates::Msg::Generate) } + + for organizational_links in self.organizational_links.iter() { + let organizational_link = organizational_links.borrow().clone().unwrap(); + organizational_link.send_message(organizational::Msg::Generate) + } } /* DownloadOption::PDF => { @@ -189,6 +204,16 @@ impl Component for MainView { shouldrender = true; }, + Msg::GeneratedOrganizational(organizational) => { + self.answer_count += 1; + + match self.vcard_data.get_mut() { + Some(vcard_data) => vcard_data.add_organizational(organizational), + None => ConsoleService::info("Error in GeneratedOrganizational: Couldn't get mutable borrow of VCardData"), + }; + + shouldrender = true; + }, Msg::GenerationComplete => { self.answer_count = 0; @@ -345,6 +370,33 @@ impl Component for MainView { ); } } + + for organizational in vcard_data.organizationals { + + if !organizational.org.is_empty() { + builder = builder.with_org(vec![organizational.org]); + } + + if !organizational.logo.is_empty() { + builder = builder.with_logo(organizational.logo); + } + + if !organizational.title.is_empty() { + builder = builder.with_title(organizational.title); + } + + if !organizational.role.is_empty() { + builder = builder.with_role(organizational.role); + } + + if !organizational.member.is_empty() { + builder = builder.with_member(organizational.member); + } + + if !organizational.related.is_empty() { + builder = builder.with_related(organizational.related); + } + } let rev = Local::now(); @@ -498,6 +550,19 @@ impl Component for MainView { ) } + { + for self.organizational_links.iter().map(|link| + html!{ + <OrganizationalView weak_link=link + generated=self.link.callback( + |o: Organizational| + Msg::GeneratedOrganizational(o) + ) + /> + } + ) + } + <div class="block level-left"> <button onclick=self.link.callback(|_| Msg::Generate) class="button is-primary level-item">{ "Generate" }</button> @@ -641,5 +706,6 @@ impl MainView { + self.address_links.len() + self.telephone_links.len() + self.dates_links.len() + + self.organizational_links.len() } }
\ No newline at end of file diff --git a/src/view/mod.rs b/src/view/mod.rs index 65ef06d..0751c53 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -9,9 +9,19 @@ pub mod name; pub mod address; pub mod telephone; pub mod dates; +pub mod organizational; + +#[derive(Clone, PartialEq, Properties)] +pub struct InputProps<O, C> + where O: VCardPropertyInputObject<C> + Clone, + C: VCardPropertyInputComponent<O> + Clone +{ + pub generated: Callback<O>, + pub weak_link: WeakComponentLink<C>, +} /// Trait for types that represent an input component for a vcard property. -pub trait VCardPropertyInputComponent<T: VCardPropertyInputObject<Self>>: Component { +pub trait VCardPropertyInputComponent<T: VCardPropertyInputObject<Self>>: Component + Clone + PartialEq { /// Returns the object containing the input data. fn get_input_object(&self) -> T; /// Getter function for the title of the component diff --git a/src/view/name.rs b/src/view/name.rs index 27dadf1..4976e7d 100644 --- a/src/view/name.rs +++ b/src/view/name.rs @@ -1,11 +1,13 @@ +use crate::view::InputProps; use yew::prelude::*; use yewtil::NeqAssign; use crate::viewmodel::Error; -use crate::view::WeakComponentLink; use crate::viewmodel::name::*; use crate::viewmodel::VCardPropertyInputObject; use super::VCardPropertyInputComponent; +type Props = InputProps<Name,NameView>; + /// View Component for a `name` field /// /// # Examples @@ -20,6 +22,7 @@ use super::VCardPropertyInputComponent; /// /> /// }; /// ``` +#[derive(Clone,PartialEq)] pub struct NameView { props: Props, value: Name, @@ -36,12 +39,6 @@ pub enum Msg { Generate, } -#[derive(Clone, PartialEq, Properties)] -pub struct Props { - pub generated: Callback<Name>, - pub weak_link: WeakComponentLink<NameView>, -} - impl VCardPropertyInputComponent<Name> for NameView { fn get_input_object(&self) -> Name { self.value.clone() diff --git a/src/view/organizational.rs b/src/view/organizational.rs new file mode 100644 index 0000000..b11c181 --- /dev/null +++ b/src/view/organizational.rs @@ -0,0 +1,83 @@ +use yew::prelude::*; +use yewtil::NeqAssign; +use crate::viewmodel::Error; +use crate::view::InputProps; +use crate::viewmodel::organizational::*; +use crate::viewmodel::VCardPropertyInputObject; +use super::VCardPropertyInputComponent; + +type Props = InputProps<Organizational,OrganizationalView>; + +#[derive(Clone,PartialEq)] +pub struct OrganizationalView { + props: Props, + value: Organizational, + error: Option<Error>, +} + +pub enum Msg { + UpdateOrg(String), + UpdateLogo(String), + UpdateTitle(String), + UpdateRole(String), + UpdateMember(String), + UpdateRelated(String), + + Generate, +} + +impl VCardPropertyInputComponent<Organizational> for OrganizationalView { + fn get_input_object(&self) -> Organizational { + self.value.clone() + } + fn get_title(&self) -> std::string::String { + "Organizational".to_string() + } + fn get_error(&self) -> std::option::Option<Error> { + self.error.clone() + } +} + +impl Component for OrganizationalView { + type Message = Msg; + type Properties = Props; + fn create(props: <Self as yew::Component>::Properties, link: yew::html::Scope<Self>) -> Self { + props.weak_link.borrow_mut().replace(link); + Self { + props, + value: Organizational::new(), + error: None, + } + } + fn update(&mut self, msg: <Self as yew::Component>::Message) -> bool { + match msg { + Msg::UpdateOrg(o) => self.value.org = o, + Msg::UpdateLogo(l) => self.value.logo = l, + Msg::UpdateTitle(t) => self.value.title = t, + Msg::UpdateRole(r) => self.value.role = r, + Msg::UpdateMember(m) => self.value.member = m, + Msg::UpdateRelated(r) => self.value.related = r, + Msg::Generate => { + self.props.generated.emit(self.value.clone()); + }, + }; + true + } + fn change(&mut self, props: <Self as yew::Component>::Properties) -> bool { + self.props.neq_assign(props) + } + fn view(&self) -> yew::virtual_dom::VNode { + let link = self.props.weak_link.borrow().clone().unwrap(); + + html!{ + <div class="box"> + { self.render_error() } + + <h3 class="subtitle">{ self.get_title() }</h3> + + { self.get_input_object().render(&link) } + + </div> + } + } +}
\ No newline at end of file diff --git a/src/view/telephone.rs b/src/view/telephone.rs index 5db8ae1..dc93632 100644 --- a/src/view/telephone.rs +++ b/src/view/telephone.rs @@ -1,11 +1,13 @@ +use crate::view::InputProps; use yew::prelude::*; use yewtil::NeqAssign; -use crate::view::WeakComponentLink; use crate::viewmodel::Error; use crate::viewmodel::telephone::*; use crate::viewmodel::VCardPropertyInputObject; use super::VCardPropertyInputComponent; +type Props = InputProps<Telephone,TelephoneView>; + /// View Component for a `telephone` field /// /// # Examples @@ -20,6 +22,7 @@ use super::VCardPropertyInputComponent; /// /> /// }; /// ``` +#[derive(Clone,PartialEq)] pub struct TelephoneView { props: Props, value: Telephone, @@ -41,12 +44,6 @@ pub enum Msg { Generate, } -#[derive(Clone, PartialEq, Properties)] -pub struct Props { - pub generated: Callback<Telephone>, - pub weak_link: WeakComponentLink<TelephoneView>, -} - impl VCardPropertyInputComponent<Telephone> for TelephoneView { fn get_input_object(&self) -> Telephone { self.value.clone() diff --git a/src/viewmodel/address.rs b/src/viewmodel/address.rs index 26e168a..71cc8d3 100644 --- a/src/viewmodel/address.rs +++ b/src/viewmodel/address.rs @@ -1,7 +1,7 @@ use super::*; use crate::view::address::*; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Address { pub post_office_box: String, pub extension: String, diff --git a/src/viewmodel/dates.rs b/src/viewmodel/dates.rs index 6c7fa37..28e8bad 100644 --- a/src/viewmodel/dates.rs +++ b/src/viewmodel/dates.rs @@ -2,7 +2,7 @@ use crate::view::dates::*; use super::*; /// Type that represents the vcard `anniversary` and `birthday` properties. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Dates { pub anniversary: String, pub birthday: String, diff --git a/src/viewmodel/mod.rs b/src/viewmodel/mod.rs index edd1a8e..75ed1d2 100644 --- a/src/viewmodel/mod.rs +++ b/src/viewmodel/mod.rs @@ -7,10 +7,11 @@ pub mod address; pub mod name; pub mod telephone; pub mod dates; +pub mod organizational; /// Trait for types that represent the data of a vcard property used inside of a `VCardPropertyInputComponent`. -pub trait VCardPropertyInputObject<C: VCardPropertyInputComponent<Self>> +pub trait VCardPropertyInputObject<C: VCardPropertyInputComponent<Self>>: Clone + PartialEq where Self: Sized { /// Function for creating a new (and empty) `VCardPropertyInputObject`. diff --git a/src/viewmodel/name.rs b/src/viewmodel/name.rs index aa6747c..ee4736f 100644 --- a/src/viewmodel/name.rs +++ b/src/viewmodel/name.rs @@ -16,7 +16,7 @@ use crate::view::name::*; /// /// assert_eq!(name.generate_fn(), String::from("Sir Arthur Charles Clarke, CBE FRAS")); /// ``` -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Name { pub prefix: String, pub first_name: String, diff --git a/src/viewmodel/organizational.rs b/src/viewmodel/organizational.rs new file mode 100644 index 0000000..e7a4ae7 --- /dev/null +++ b/src/viewmodel/organizational.rs @@ -0,0 +1,86 @@ +use crate::view::organizational::*; +use super::*; + +#[derive(Clone,Debug,PartialEq)] +pub struct Organizational { + pub org: String, + pub logo: String, + pub title: String, + pub role: String, + pub member: String, + pub related: String, +} + +impl VCardPropertyInputObject<OrganizationalView> for Organizational { + fn new() -> Self { + Self { + org: String::new(), + logo: String::new(), + title: String::new(), + role: String::new(), + member: String::new(), + related: String::new(), + } + } + fn get_input_fields(&self, link: &yew::html::Scope<OrganizationalView>) -> std::vec::Vec<VCardPropertyInputField> { + let typ = String::from("text"); + vec![ + VCardPropertyInputField::Text{ + label: "Organisation".to_string(), + id: Some("org".to_string()), + placeholder: None, + oninput: link.callback(|e: InputData| Msg::UpdateOrg(e.value)), + value: self.org.clone(), + typ: typ.clone(), + }, + VCardPropertyInputField::Text{ // TODO: Add Upload for logo + label: "Logo".to_string(), + id: Some("logo".to_string()), + placeholder: None, + oninput: link.callback(|e: InputData| Msg::UpdateLogo(e.value)), + value: self.logo.clone(), + typ: typ.clone(), + }, + VCardPropertyInputField::Text{ + label: "Title".to_string(), + id: Some("title".to_string()), + placeholder: None, + oninput: link.callback(|e: InputData| Msg::UpdateTitle(e.value)), + value: self.title.clone(), + typ: typ.clone(), + }, + VCardPropertyInputField::Text{ + label: "Role".to_string(), + id: Some("role".to_string()), + placeholder: None, + oninput: link.callback(|e: InputData| Msg::UpdateRole(e.value)), + value: self.role.clone(), + typ: typ.clone(), + }, + VCardPropertyInputField::Text{ + label: "Member".to_string(), + id: Some("member".to_string()), + placeholder: None, + oninput: link.callback(|e: InputData| Msg::UpdateMember(e.value)), + value: self.member.clone(), + typ: typ.clone(), + }, + VCardPropertyInputField::Text{ + label: "Related".to_string(), + id: Some("related".to_string()), + placeholder: None, + oninput: link.callback(|e: InputData| Msg::UpdateRelated(e.value)), + value: self.related.clone(), + typ: typ, + }, + ] + } + fn is_empty(&self) -> bool { + self.org.is_empty() && + self.logo.is_empty() && + self.title.is_empty() && + self.role.is_empty() && + self.member.is_empty() && + self.related.is_empty() + } +}
\ No newline at end of file diff --git a/src/viewmodel/telephone.rs b/src/viewmodel/telephone.rs index 774b63d..ee616c3 100644 --- a/src/viewmodel/telephone.rs +++ b/src/viewmodel/telephone.rs @@ -1,7 +1,7 @@ use super::*; use crate::view::telephone::*; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Telephone { pub number: String, pub work: bool, diff --git a/src/viewmodel/vcard.rs b/src/viewmodel/vcard.rs index 565b1ff..0d2d6c4 100644 --- a/src/viewmodel/vcard.rs +++ b/src/viewmodel/vcard.rs @@ -1,3 +1,4 @@ +use crate::viewmodel::organizational::Organizational; use crate::viewmodel::dates::Dates; use crate::viewmodel::telephone::Telephone; use crate::viewmodel::address::Address; @@ -10,6 +11,7 @@ pub struct VCardData { pub addresses: Vec<Address>, pub telephones: Vec<Telephone>, pub datess: Vec<Dates>, + pub organizationals: Vec<Organizational>, } macro_rules! make_vec_adder_fn { @@ -27,10 +29,12 @@ impl VCardData { addresses: Vec::new(), telephones: Vec::new(), datess: Vec::new(), + organizationals: Vec::new(), } } make_vec_adder_fn!( fn add_name names => name: Name ); make_vec_adder_fn!( fn add_address addresses => address: Address ); make_vec_adder_fn!( fn add_telephone telephones => telephone: Telephone ); make_vec_adder_fn!( fn add_dates datess => dates: Dates ); + make_vec_adder_fn!( fn add_organizational organizationals => organizational: Organizational ); }
\ No newline at end of file |