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  | 
