diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/view/address.rs | 29 | ||||
-rw-r--r-- | src/view/main.rs | 281 | ||||
-rw-r--r-- | src/view/name.rs | 57 | ||||
-rw-r--r-- | src/view/telephone.rs | 29 | ||||
-rw-r--r-- | src/viewmodel/address.rs | 2 | ||||
-rw-r--r-- | src/viewmodel/mod.rs | 2 | ||||
-rw-r--r-- | src/viewmodel/name.rs | 4 | ||||
-rw-r--r-- | src/viewmodel/telephone.rs | 15 | ||||
-rw-r--r-- | src/viewmodel/vcard.rs | 32 |
9 files changed, 294 insertions, 157 deletions
diff --git a/src/view/address.rs b/src/view/address.rs index 23b697a..a16d93a 100644 --- a/src/view/address.rs +++ b/src/view/address.rs @@ -1,5 +1,9 @@ +use yew::services::ConsoleService; +use yewtil::ptr::Irc; +use yewtil::ptr::Mrc; use super::WeakComponentLink; use yew::prelude::*; +use yewtil::NeqAssign; use crate::viewmodel::address::*; use crate::viewmodel::VCardPropertyInputObject; use super::VCardPropertyInputComponent; @@ -8,7 +12,7 @@ use crate::viewmodel::Error; pub struct AddressView { props: Props, - value: Address, + value: Mrc<Address>, error: Option<Error>, } @@ -28,13 +32,19 @@ pub enum Msg { #[derive(Clone, PartialEq, Properties)] pub struct Props { - pub generated: Callback<Address>, + pub generated: Callback<Irc<Address>>, pub weak_link: WeakComponentLink<AddressView>, } impl VCardPropertyInputComponent<Address> for AddressView { fn get_input_object(&self) -> Address { - self.value.clone() + match self.value.irc().try_unwrap() { + Ok(address) => address, + Err(_) => { + ConsoleService::error("Couldn't unwrap address"); + Address::new() + }, + } } fn get_title(&self) -> String { "Address".to_string() @@ -51,7 +61,7 @@ impl Component for AddressView { props.weak_link.borrow_mut().replace(link); Self { props, - value: Address::new(), + value: Mrc::new(Address::new()), error: None, } } @@ -66,19 +76,14 @@ impl Component for AddressView { Msg::UpdateCountry(c) => self.value.country = c, Msg::ToggleWork => self.value.work = !self.value.work, Msg::ToggleHome => self.value.home = !self.value.home, - Generate => { - self.props.generated.emit(self.value); + Msg::Generate => { + self.props.generated.emit(self.value.irc()); }, }; true } fn change(&mut self, props: <Self as yew::Component>::Properties) -> bool { - if self.props != props { - self.props = props; - true - } else { - false - } + self.props.neq_assign(props) } fn view(&self) -> yew::virtual_dom::VNode { let link = self.props.weak_link.borrow().clone().unwrap(); diff --git a/src/view/main.rs b/src/view/main.rs index e926504..b1c0dda 100644 --- a/src/view/main.rs +++ b/src/view/main.rs @@ -1,6 +1,8 @@ +use yew::services::ConsoleService; +use yewtil::ptr::Irc; +use crate::viewmodel::vcard::VCardData; use crate::viewmodel::Error; use crate::view::telephone::{self,TelephoneView}; -use std::collections::HashSet; use super::WeakComponentLink; use super::name::{self,NameView}; use super::address::{self,AddressView}; @@ -9,13 +11,15 @@ use genpdf::{elements, style, fonts}; use qrcodegen::QrCode; use qrcodegen::QrCodeEcc; use yew::prelude::*; -use vobject::Vcard; +use yewtil::ptr::Mrc; use vobject::vcard::VcardBuilder; use vobject::parameters; +use chrono::prelude::*; use crate::viewmodel::name::Name; use crate::viewmodel::address::Address; use crate::viewmodel::telephone::Telephone; use crate::viewmodel::utility::*; +use boolinator::Boolinator; pub struct MainView { @@ -23,7 +27,7 @@ pub struct MainView { error: Option<Error>, download: Option<Download>, selected_option: DownloadOption, - vcard_builder: VcardBuilder, + vcard_data: Mrc<VCardData>, name_links: Vec<WeakComponentLink<NameView>>, address_links: Vec<WeakComponentLink<AddressView>>, @@ -40,10 +44,9 @@ pub enum Msg { ChangeDownloadOption(DownloadOption), Generate, - GeneratedFormattedName(String), - GeneratedName(Name), - GeneratedAddress(Address), - GeneratedTelephone(Telephone), + GeneratedName(Irc<Name>), + GeneratedAddress(Irc<Address>), + GeneratedTelephone(Irc<Telephone>), GenerationComplete, Nope, @@ -59,7 +62,7 @@ impl Component for MainView { error: None, download: None, selected_option: DownloadOption::VCard, - vcard_builder: VcardBuilder::new(), + vcard_data: Mrc::new(VCardData::new()), name_links: vec![WeakComponentLink::default()], address_links: vec![WeakComponentLink::default()], @@ -126,26 +129,14 @@ impl Component for MainView { shouldrender = true; }, - Msg::GeneratedFormattedName(formatted_name) => { - - self.answer_count += 1; - - self.vcard_builder = self.vcard_builder.with_fullname(formatted_name); - - shouldrender = true; - }, Msg::GeneratedName(name) => { self.answer_count += 1; - self.vcard_builder = self.vcard_builder.with_name( - parameters!(), - name.last_name.is_empty().then(|| name.last_name), - name.first_name.is_empty().then(|| name.first_name), - name.middle_name.is_empty().then(|| name.middle_name), - name.prefix.is_empty().then(|| name.prefix), - name.suffix.is_empty().then(|| name.suffix) - ); + match self.vcard_data.get_mut() { + Some(vcard_data) => vcard_data.add_name(name), + None => ConsoleService::info("Error in GeneratedName: Couldn't get mutable borrow of VCardData"), + }; shouldrender = true; @@ -154,9 +145,10 @@ impl Component for MainView { self.answer_count += 1; - self.vcard_builder = self.vcard_builder.with_adr( - parameters!(), - ); + match self.vcard_data.get_mut() { + Some(vcard_data) => vcard_data.add_address(address), + None => ConsoleService::info("Error in GeneratedAddress: Couldn't get mutable borrow of VCardData"), + }; shouldrender = true; }, @@ -164,31 +156,10 @@ impl Component for MainView { self.answer_count += 1; - match telephone { - Ok(telephone) => { - match self.vcard { - Some(vcard) => { - match vcard.telephones { - Some(telephones) => { - telephones.insert(telephone); - }, - None => { - let telephones = { - let mut telephones = HashSet::new(); - telephones.insert(telephone); - - telephones - }; - - vcard.telephones = Some(vcard::Set::from_hash_set(telephones).unwrap()); - } - }; - }, - None => (), - }; - }, - Err(_) => (), - } + match self.vcard_data.get_mut() { + Some(vcard_data) => vcard_data.add_telephone(telephone), + None => ConsoleService::info("Error in GeneratedTelephone: Couldn't get mutable borrow of VCardData"), + }; shouldrender = true; }, @@ -196,43 +167,167 @@ impl Component for MainView { self.answer_count = 0; - match self.vcard.clone() { - Some(vcard) => { - match self.selected_option { - DownloadOption::VCard => { - self.download = Some( - Download { - file_name: String::from("VCard.vcs"), - content: vcard.to_string(), - mime_type: MimeType::VCard, - } - ); - }, - DownloadOption::QrCode => { - match QrCode::encode_text(&vcard.to_string(), QrCodeEcc::Low) { - Ok(qr) => self.download = Some( - Download { - file_name: String::from("QR-Code VCard.svg"), - content: qr.to_svg_string(4), - mime_type: MimeType::SVG, - } - ), - Err(_) => self.error = Some( - Error{ - msg: String::from("Sorry, VCard is too long!"), - } - ), - }; - }, - _ => (), - }; - - self.vcard = None; - shouldrender = true; + let vcard_data = match self.vcard_data.irc().try_unwrap() { + Ok(data) => data, + Err(err) => { + ConsoleService::error(&format!("Error when unwrapping VCardData: {:?}", err)); + VCardData::new() }, - None => shouldrender = false, // what TODO here? + }; + + let mut builder = VcardBuilder::new(); + + for name in vcard_data.names { + + builder = builder + .with_fullname( + name.generate_fn() + ) + .with_name( + parameters!(), + name.last_name.is_empty().as_some(name.last_name.clone()), + name.first_name.is_empty().as_some(name.first_name.clone()), + name.middle_name.is_empty().as_some(name.middle_name.clone()), + name.prefix.is_empty().as_some(name.prefix.clone()), + name.suffix.is_empty().as_some(name.suffix.clone()) + ); + } + + for address in vcard_data.addresses { + let mut types = String::new(); + if address.work { + types.push_str("WORK"); + } + if address.home { + if types.is_empty() { + types.push(','); + } + types.push_str("HOME") + } + + builder = builder.with_adr( + parameters!("TYPE" => types), + address.post_office_box.is_empty().as_some(address.post_office_box.clone()), + address.extension.is_empty().as_some(address.extension.clone()), + address.street.is_empty().as_some(address.street.clone()), + address.locality.is_empty().as_some(address.locality.clone()), + address.region.is_empty().as_some(address.region.clone()), + address.code.is_empty().as_some(address.code.clone()), + address.country.is_empty().as_some(address.country.clone()), + ); } + for telephone in vcard_data.telephones { + let mut types = String::new(); + if telephone.work { + types.push_str("WORK"); + } + if telephone.home { + if types.is_empty() { + types.push(','); + } + types.push_str("HOME") + } + if telephone.text { + if types.is_empty() { + types.push(','); + } + types.push_str("TEXT") + } + if telephone.voice { + if types.is_empty() { + types.push(','); + } + types.push_str("VOICE") + } + if telephone.fax { + if types.is_empty() { + types.push(','); + } + types.push_str("FAX") + } + if telephone.cell { + if types.is_empty() { + types.push(','); + } + types.push_str("CELL") + } + if telephone.video { + if types.is_empty() { + types.push(','); + } + types.push_str("VIDEO") + } + if telephone.pager { + if types.is_empty() { + types.push(','); + } + types.push_str("PAGER") + } + if telephone.text_phone { + if types.is_empty() { + types.push(','); + } + types.push_str("TEXTPHONE") + } + + builder = builder.with_tel( + parameters!("TYPE" => types), + telephone.number.clone(), + ); + } + + + let rev = Local::now(); + + match builder + .with_version("4.0".to_string()) + .with_rev(format!("{}", rev)) + .build() { + Ok(vcard) => { + match self.selected_option { + DownloadOption::VCard => { + self.download = Some( + Download { + file_name: String::from("VCard.vcs"), + content: vobject::write_component(&vcard), + mime_type: MimeType::VCard, + } + ); + }, + DownloadOption::QrCode => { + match QrCode::encode_text(&vobject::write_component(&vcard), QrCodeEcc::Low) { + Ok(qr) => self.download = Some( + Download { + file_name: String::from("QR-Code VCard.svg"), + content: qr.to_svg_string(4), + mime_type: MimeType::SVG, + } + ), + Err(_) => self.error = Some( + Error{ + msg: String::from("Sorry, VCard is too long!"), + } + ), + }; + }, + _ => (), + }; + }, + Err(err) => self.error = Some( + Error{ + msg: err.to_string(), + } + ), + }; + + match self.vcard_data.get_mut() { + Some(vcard_data) => *vcard_data = VCardData::new(), + None => ConsoleService::info("Couldn't reset VCardData"), + }; + + shouldrender = true; + }, Msg::Nope => shouldrender = false, }; @@ -286,12 +381,8 @@ impl Component for MainView { for self.name_links.iter().map(|link| html!{ <NameView weak_link=link - generated_fn=self.link.callback( - |fmn: Result<properties::FormattedName,()>| - Msg::GeneratedFormattedName(fmn) - ) - generated_name=self.link.callback( - |n: Result<properties::Name,()>| + generated=self.link.callback( + |n: Irc<Name>| Msg::GeneratedName(n) ) /> @@ -304,7 +395,7 @@ impl Component for MainView { html!{ <AddressView weak_link=link generated=self.link.callback( - |a: Result<properties::Address,()>| + |a: Irc<Address>| Msg::GeneratedAddress(a) ) /> @@ -317,7 +408,7 @@ impl Component for MainView { html!{ <TelephoneView weak_link=link generated=self.link.callback( - |t: Result<properties::Telephone,()>| + |t: Irc<Telephone>| Msg::GeneratedTelephone(t) ) /> @@ -326,7 +417,7 @@ impl Component for MainView { } <div class="block level-left"> - <button onclick=self.link.callback(|_| Msg::Generate) class="button is-primary level-item" /> + <button onclick=self.link.callback(|_| Msg::Generate) class="button is-primary level-item">{ "Generate" }</button> <div class="select level-item"> <select id="download_options" onchange=download_options> diff --git a/src/view/name.rs b/src/view/name.rs index bee9256..5b64bae 100644 --- a/src/view/name.rs +++ b/src/view/name.rs @@ -1,14 +1,17 @@ +use yew::services::ConsoleService; +use yewtil::ptr::Irc; +use yewtil::ptr::Mrc; use crate::viewmodel::Error; use crate::view::WeakComponentLink; use yew::prelude::*; -use vobject::Property; +use yewtil::NeqAssign; use crate::viewmodel::name::*; use crate::viewmodel::VCardPropertyInputObject; use super::VCardPropertyInputComponent; pub struct NameView { props: Props, - value: Name, + value: Mrc<Name>, error: Option<Error>, } @@ -24,14 +27,19 @@ pub enum Msg { #[derive(Clone, PartialEq, Properties)] pub struct Props { - pub generated_name: Callback<Name>, - pub generated_fn: Callback<String>, + pub generated: Callback<Irc<Name>>, pub weak_link: WeakComponentLink<NameView>, } impl VCardPropertyInputComponent<Name> for NameView { fn get_input_object(&self) -> Name { - self.value.clone() + match self.value.clone().try_unwrap() { + Ok(name) => name, + Err(_) => { + ConsoleService::error("Couldn't unwrap name"); + Name::new() + }, + } } fn get_title(&self) -> String { "Name".to_string() @@ -48,31 +56,40 @@ impl Component for NameView { props.weak_link.borrow_mut().replace(link); Self { props, - value: Name::new(), + value: Mrc::new(Name::new()), error: None, } } fn update(&mut self, msg: <Self as yew::Component>::Message) -> bool { match msg { - Msg::UpdatePrefix(p) => self.value.prefix = p, - Msg::UpdateFirstName(f) => self.value.first_name = f, - Msg::UpdateMiddleName(m) => self.value.middle_name = m, - Msg::UpdateLastName(l) => self.value.last_name = l, - Msg::UpdateSuffix(s) => self.value.suffix = s, - Generate => { - self.props.generated_fn.emit(self.value.generate_fn()); - self.props.generated_name.emit(self.value); + Msg::UpdatePrefix(p) => match self.value.get_mut() { + Some(value) => value.prefix = p, + None => ConsoleService::info("Couldn't get mutable reference to name"), + }, + Msg::UpdateFirstName(f) => match self.value.get_mut() { + Some(value) => value.first_name = f, + None => ConsoleService::info("Couldn't get mutable reference to name"), + }, + Msg::UpdateMiddleName(m) => match self.value.get_mut() { + Some(value) => value.middle_name = m, + None => ConsoleService::info("Couldn't get mutable reference to name"), + }, + Msg::UpdateLastName(l) => match self.value.get_mut() { + Some(value) => value.last_name = l, + None => ConsoleService::info("Couldn't get mutable reference to name"), + }, + Msg::UpdateSuffix(s) => match self.value.get_mut() { + Some(value) => value.suffix = s, + None => ConsoleService::info("Couldn't get mutable reference to name"), + }, + Msg::Generate => { + self.props.generated.emit(self.value.irc()); }, }; true } fn change(&mut self, props: <Self as yew::Component>::Properties) -> bool { - if self.props != props { - self.props = props; - true - } else { - false - } + self.props.neq_assign(props) } fn view(&self) -> yew::virtual_dom::VNode { let link = self.props.weak_link.borrow().clone().unwrap(); diff --git a/src/view/telephone.rs b/src/view/telephone.rs index 26d7806..ca58538 100644 --- a/src/view/telephone.rs +++ b/src/view/telephone.rs @@ -1,19 +1,22 @@ +use yew::services::ConsoleService; +use yewtil::ptr::Irc; +use yewtil::ptr::Mrc; use crate::view::WeakComponentLink; use crate::viewmodel::Error; use yew::prelude::*; +use yewtil::NeqAssign; use crate::viewmodel::telephone::*; use crate::viewmodel::VCardPropertyInputObject; use super::VCardPropertyInputComponent; pub struct TelephoneView { props: Props, - value: Telephone, + value: Mrc<Telephone>, error: Option<Error>, } pub enum Msg { UpdateNumber(String), - UpdateExtension(String), ToggleWork, ToggleHome, ToggleText, @@ -29,13 +32,19 @@ pub enum Msg { #[derive(Clone, PartialEq, Properties)] pub struct Props { - pub generated: Callback<Telephone>, + pub generated: Callback<Irc<Telephone>>, pub weak_link: WeakComponentLink<TelephoneView>, } impl VCardPropertyInputComponent<Telephone> for TelephoneView { fn get_input_object(&self) -> Telephone { - self.value.clone() + match self.value.irc().try_unwrap() { + Ok(telephone) => telephone, + Err(_) => { + ConsoleService::error("Couldn't unwrap telephone"); + Telephone::new() + }, + } } fn get_title(&self) -> String { "Telephone".to_string() @@ -52,14 +61,13 @@ impl Component for TelephoneView { props.weak_link.borrow_mut().replace(link); Self { props, - value: Telephone::new(), + value: Mrc::new(Telephone::new()), error: None, } } fn update(&mut self, msg: <Self as yew::Component>::Message) -> bool { match msg { Msg::UpdateNumber(n) => self.value.number = n, - Msg::UpdateExtension(e) => self.value.extension = e, Msg::ToggleWork => self.value.work = !self.value.work, Msg::ToggleHome => self.value.home = !self.value.home, Msg::ToggleText => self.value.text = !self.value.text, @@ -70,18 +78,13 @@ impl Component for TelephoneView { Msg::TogglePager => self.value.pager = !self.value.pager, Msg::ToggleTextPhone => self.value.text_phone = !self.value.text_phone, Msg::Generate => { - self.props.generated.emit(self.value); + self.props.generated.emit(self.value.irc()); } }; true } fn change(&mut self, props: <Self as yew::Component>::Properties) -> bool { - if self.props != props { - self.props = props; - true - } else { - false - } + self.props.neq_assign(props) } fn view(&self) -> yew::virtual_dom::VNode { let link = self.props.weak_link.borrow().clone().unwrap(); diff --git a/src/viewmodel/address.rs b/src/viewmodel/address.rs index 61bd464..6fd3173 100644 --- a/src/viewmodel/address.rs +++ b/src/viewmodel/address.rs @@ -1,7 +1,7 @@ use super::*; use crate::view::address::*; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Address { pub post_office_box: String, pub extension: String, diff --git a/src/viewmodel/mod.rs b/src/viewmodel/mod.rs index cc25d68..de433de 100644 --- a/src/viewmodel/mod.rs +++ b/src/viewmodel/mod.rs @@ -1,7 +1,7 @@ use yew::prelude::*; -use vobject::Property; use crate::view::VCardPropertyInputComponent; +pub mod vcard; pub mod address; pub mod name; pub mod telephone; diff --git a/src/viewmodel/name.rs b/src/viewmodel/name.rs index 26beaa1..a88e07f 100644 --- a/src/viewmodel/name.rs +++ b/src/viewmodel/name.rs @@ -1,7 +1,7 @@ use super::*; use crate::view::name::*; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Name { pub prefix: String, pub first_name: String, @@ -70,7 +70,7 @@ impl VCardPropertyInputObject<NameView> for Name { impl Name { pub fn generate_fn(&self) -> String { - let full_name = String::new(); + let mut full_name = String::new(); full_name.push_str(&self.prefix); if !self.first_name.is_empty() { diff --git a/src/viewmodel/telephone.rs b/src/viewmodel/telephone.rs index e5f63f0..6c930a2 100644 --- a/src/viewmodel/telephone.rs +++ b/src/viewmodel/telephone.rs @@ -1,11 +1,9 @@ -use std::collections::HashSet; use super::*; use crate::view::telephone::*; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Telephone { pub number: String, - pub extension: String, pub work: bool, pub home: bool, pub text: bool, @@ -21,7 +19,6 @@ impl VCardPropertyInputObject<TelephoneView> for Telephone { fn new() -> Self { Self { number: String::new(), - extension: String::new(), work: false, home: false, text: false, @@ -42,13 +39,6 @@ impl VCardPropertyInputObject<TelephoneView> for Telephone { oninput: link.callback(|e: InputData| Msg::UpdateNumber(e.value)), value: self.number.clone(), }, - VCardPropertyInputField::Text{ - label: "Extension".to_string(), - id: Some("extension".to_string()), - placeholder: None, - oninput: link.callback(|e: InputData| Msg::UpdateExtension(e.value)), - value: self.extension.clone(), - }, VCardPropertyInputField::CheckBox{ label: "Work".to_string(), id: Some("work".to_string()), @@ -106,7 +96,6 @@ impl VCardPropertyInputObject<TelephoneView> for Telephone { ] } fn is_empty(&self) -> bool { - self.number.is_empty() && - self.extension.is_empty() + self.number.is_empty() } }
\ No newline at end of file diff --git a/src/viewmodel/vcard.rs b/src/viewmodel/vcard.rs new file mode 100644 index 0000000..2d415b7 --- /dev/null +++ b/src/viewmodel/vcard.rs @@ -0,0 +1,32 @@ +use yewtil::ptr::Irc; +use crate::viewmodel::telephone::Telephone; +use crate::viewmodel::address::Address; +use crate::viewmodel::name::Name; + +#[derive(Clone, Debug)] +pub struct VCardData { + pub names: Vec<Irc<Name>>, + pub addresses: Vec<Irc<Address>>, + pub telephones: Vec<Irc<Telephone>>, +} + +macro_rules! make_vec_adder_fn { + ( $fnname:ident $property:ident $($arg_name:ident : $arg_type:ty),* ) => { + pub fn $fnname(&mut self, $( $arg_name : $arg_type ),*) { + $(self.$property.push($arg_name);)* + } + }; +} + +impl VCardData { + pub fn new() -> Self { + Self { + names: Vec::new(), + addresses: Vec::new(), + telephones: Vec::new(), + } + } + make_vec_adder_fn!( add_name names name: Irc<Name> ); + make_vec_adder_fn!( add_address addresses address: Irc<Address> ); + make_vec_adder_fn!( add_telephone telephones telephone: Irc<Telephone> ); +}
\ No newline at end of file |