From 51bf8e89ce07864b70d7138bbc3958faf499cc67 Mon Sep 17 00:00:00 2001 From: jelemux Date: Wed, 3 Feb 2021 07:08:49 +0100 Subject: now it compiles --- Cargo.toml | 3 +- snippets | 103 +++++++++++++++++ src/view/address.rs | 29 +++-- src/view/main.rs | 281 ++++++++++++++++++++++++++++++--------------- src/view/name.rs | 57 +++++---- src/view/telephone.rs | 29 ++--- src/viewmodel/address.rs | 2 +- src/viewmodel/mod.rs | 2 +- src/viewmodel/name.rs | 4 +- src/viewmodel/telephone.rs | 15 +-- src/viewmodel/vcard.rs | 32 ++++++ 11 files changed, 399 insertions(+), 158 deletions(-) create mode 100644 snippets create mode 100644 src/viewmodel/vcard.rs diff --git a/Cargo.toml b/Cargo.toml index 34eb65f..34281a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,12 +14,13 @@ wasm-bindgen = "0.2.68" js-sys = "0.3.45" console_error_panic_hook = "0.1.6" wee_alloc = "0.4.5" -yew-state = "^0.4" +yewtil = "0.3.2" printpdf = "0.3.3" base64 = "0.13.0" vobject = "^0.7" genpdf = { path = "../genpdf-rs" } qrcodegen = "1.6.0" +boolinator = "2.4.0" [dependencies.yew] version = "0.17.4" diff --git a/snippets b/snippets new file mode 100644 index 0000000..dc4e601 --- /dev/null +++ b/snippets @@ -0,0 +1,103 @@ + + + match self.vcard_data.get_mut() { + Some(vcard_builder) => *vcard_builder = 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) + ), + None => (), + }; + + -------------------------------------------------------------- + + 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") + } + + match self.vcard_data.get_mut() { + Some(vcard_builder) => *vcard_builder = vcard_builder.with_adr( + parameters!("TYPE" => types), + address.post_office_box.is_empty().then(|| address.post_office_box), + address.extension.is_empty().then(|| address.extension), + address.street.is_empty().then(|| address.street), + address.locality.is_empty().then(|| address.locality), + address.region.is_empty().then(|| address.region), + address.code.is_empty().then(|| address.code), + address.country.is_empty().then(|| address.country), + ), + None => (), + }; + + ---------------------------------------------------------------- + + 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") + } + + match self.vcard_data.get_mut() { + Some(vcard_builder) => *vcard_builder = vcard_builder.with_tel( + parameters!("TYPE" => types), + telephone.number, + ), + None => (), + }; \ No newline at end of file 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
, error: Option, } @@ -28,13 +32,19 @@ pub enum Msg { #[derive(Clone, PartialEq, Properties)] pub struct Props { - pub generated: Callback
, + pub generated: Callback>, pub weak_link: WeakComponentLink, } impl VCardPropertyInputComponent
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: ::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, download: Option, selected_option: DownloadOption, - vcard_builder: VcardBuilder, + vcard_data: Mrc, name_links: Vec>, address_links: Vec>, @@ -40,10 +44,9 @@ pub enum Msg { ChangeDownloadOption(DownloadOption), Generate, - GeneratedFormattedName(String), - GeneratedName(Name), - GeneratedAddress(Address), - GeneratedTelephone(Telephone), + GeneratedName(Irc), + GeneratedAddress(Irc
), + GeneratedTelephone(Irc), 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!{ | - Msg::GeneratedFormattedName(fmn) - ) - generated_name=self.link.callback( - |n: Result| + generated=self.link.callback( + |n: Irc| Msg::GeneratedName(n) ) /> @@ -304,7 +395,7 @@ impl Component for MainView { html!{ | + |a: Irc
| Msg::GeneratedAddress(a) ) /> @@ -317,7 +408,7 @@ impl Component for MainView { html!{ | + |t: Irc| Msg::GeneratedTelephone(t) ) /> @@ -326,7 +417,7 @@ impl Component for MainView { }
-