diff options
Diffstat (limited to 'src/view/main.rs')
-rw-r--r-- | src/view/main.rs | 437 |
1 files changed, 314 insertions, 123 deletions
diff --git a/src/view/main.rs b/src/view/main.rs index 684db84..ce8c283 100644 --- a/src/view/main.rs +++ b/src/view/main.rs @@ -1,35 +1,47 @@ -use crate::view::telephone::TelephoneView; +use crate::viewmodel::Error; +use crate::view::telephone::{self,TelephoneView}; use std::collections::HashSet; -use super::name::{NameView}; -use super::address::AddressView; +use super::WeakComponentLink; +use super::name::{self,NameView}; +use super::address::{self,AddressView}; use genpdf::Element as _; use genpdf::{elements, style, fonts}; use qrcodegen::QrCode; use qrcodegen::QrCodeEcc; use yew::prelude::*; +use vcard::properties; use vcard::{VCard, VCardError}; use crate::viewmodel::utility::*; -use crate::viewmodel::name::Name; -use crate::viewmodel::address::Address; -use crate::viewmodel::telephone::Telephone; -use crate::viewmodel::VCardPropertyInputObject; pub struct MainView { link: ComponentLink<Self>, - error: Vec<String>, - name: Name, - address: Address, - telephone: Telephone, + error: Option<Error>, download: Option<Download>, selected_option: DownloadOption, + vcard: Option<VCard>, + + name_links: Vec<WeakComponentLink<NameView>>, + address_links: Vec<WeakComponentLink<AddressView>>, + telephone_links: Vec<WeakComponentLink<TelephoneView>>, + + answer_count: usize, } pub enum Msg { - UpdateName(Name), - UpdateAddress(Address), - UpdateTelephone(Telephone), - Generate(DownloadOption), + AddName, + AddAddress, + AddTelephone, + + ChangeDownloadOption(DownloadOption), + + Generate, + GeneratedFormattedName(Result<properties::FormattedName,()>), + GeneratedName(Result<properties::Name,()>), + GeneratedAddress(Result<properties::Address,()>), + GeneratedTelephone(Result<properties::Telephone,()>), + GenerationComplete, + Nope, } @@ -40,71 +52,59 @@ impl Component for MainView { fn create(_props: Self::Properties, link: ComponentLink<Self>) -> Self { MainView { link, - error: vec![], - name: Name::new(), - address: Address::new(), - telephone: Telephone::new(), + error: None, download: None, - selected_option: DownloadOption::VCard + selected_option: DownloadOption::VCard, + vcard: None, + + name_links: vec![WeakComponentLink::default()], + address_links: vec![WeakComponentLink::default()], + telephone_links: vec![WeakComponentLink::default()], + answer_count: 0, } } fn update(&mut self, msg: Self::Message) -> ShouldRender { - self.error.clear(); + let shouldrender; // let the compiler check if it is always set + self.error = None; + match msg { - Msg::UpdateName(value) => { - self.name = value; - self.link.send_message(Msg::Generate(self.selected_option)); + Msg::AddName => { + self.name_links.push(WeakComponentLink::default()); + shouldrender = true; }, - Msg::UpdateAddress(value) => { - self.address = value; - self.link.send_message(Msg::Generate(self.selected_option)); + Msg::AddAddress => { + self.address_links.push(WeakComponentLink::default()); + shouldrender = true; }, - Msg::UpdateTelephone(value) => { - self.telephone = value; - self.link.send_message(Msg::Generate(self.selected_option)); + Msg::AddTelephone => { + self.telephone_links.push(WeakComponentLink::default()); + shouldrender = true; }, - Msg::Generate(option) => { + Msg::ChangeDownloadOption(option) => { self.selected_option = option; + shouldrender = false; + }, + Msg::Generate => { - let vcard_content = match self.generate_vcard() { - Ok(vcard) => Some(vcard.to_string()), - Err(VCardError::FormatError(err)) => { - self.error.push(err.to_string()); - None - } - Err(VCardError::EmptyFormatName) => { - self.error.push(String::from("At least one of the name fields should be filled out.")); - None + if self.selected_option == DownloadOption::VCard || self.selected_option == DownloadOption::QrCode { + + for name_link in self.name_links.iter() { + let name_link = name_link.borrow().clone().unwrap(); + name_link.send_message(name::Msg::Generate); } - }; - match option { - DownloadOption::VCard => { - if vcard_content.is_some() { - self.download = Some( - Download { - file_name: format!("{}.vcs", self.name.formatted_name()), - content: vcard_content.unwrap().to_string(), - mime_type: MimeType::VCard, - } - ) - } + for address_link in self.address_links.iter() { + let address_link = address_link.borrow().clone().unwrap(); + address_link.send_message(address::Msg::Generate); } - DownloadOption::QrCode => { - if vcard_content.is_some() { - match QrCode::encode_text(vcard_content.as_ref().unwrap(), QrCodeEcc::Low) { - Ok(qr) => self.download = Some( - Download { - file_name: format!("QR-Code VCard {}.svg", self.name.formatted_name()), - content: qr.to_svg_string(4), - mime_type: MimeType::SVG, - } - ), - Err(_) => self.error.push(String::from("Sorry, VCard is too long!")), - }; - } + + for telephone_link in self.telephone_links.iter() { + let telephone_link = telephone_link.borrow().clone().unwrap(); + telephone_link.send_message(telephone::Msg::Generate); } + } + /* DownloadOption::PDF => { match self.generate_pdf() { Ok(pdf) => self.download = Some( @@ -118,13 +118,192 @@ impl Component for MainView { } } } - } - Msg::Nope => return false, + */ + + shouldrender = true; + }, + Msg::GeneratedFormattedName(formatted_name) => { + + self.answer_count += 1; + + match formatted_name { + Ok(formatted_name) => { + match &mut self.vcard { + None => { + match VCard::from_formatted_name(formatted_name) { + Ok(vcard) => self.vcard = Some(vcard), + Err(VCardError::FormatError(err)) => { + self.error = Some(Error{ + msg: err.to_string(), + }); + }, + Err(VCardError::EmptyFormatName) => { + self.error= Some(Error{ + msg: String::from("At least one of the name fields should be filled out."), + }); + }, + }; + }, + Some(vcard) => { + vcard.formatted_names.insert(formatted_name); + }, + }; + }, + Err(_) => (), + }; + + shouldrender = true; + }, + Msg::GeneratedName(name) => { + + self.answer_count += 1; + + match name { + Ok(name) => { + match self.vcard { + Some(vcard) => { + match vcard.names { + Some(names) => { + names.insert(name); + }, + None => { + let names = { + let mut names = HashSet::new(); + names.insert(name); + + names + }; + + vcard.names = Some(vcard::Set::from_hash_set(names).unwrap()); + } + }; + }, + None => (), + }; + }, + Err(_) => (), + }; + + shouldrender = true; + + }, + Msg::GeneratedAddress(address) => { + + self.answer_count += 1; + + match address { + Ok(address) => { + match self.vcard { + Some(vcard) => { + match vcard.addresses { + Some(addresses) => { + addresses.insert(address); + }, + None => { + let addresses = { + let mut addresses = HashSet::new(); + addresses.insert(address); + + addresses + }; + + vcard.addresses = Some(vcard::Set::from_hash_set(addresses).unwrap()); + } + }; + }, + None => (), + }; + }, + Err(_) => (), + }; + + shouldrender = true; + }, + Msg::GeneratedTelephone(telephone) => { + + 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(_) => (), + } + + shouldrender = true; + }, + Msg::GenerationComplete => { + + 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; + }, + None => shouldrender = false, // what TODO here? + } + + }, + Msg::Nope => shouldrender = false, }; - if self.error.len() > 0 { + + if self.answer_count >= self.get_subcomponent_count() { + self.link.send_message(Msg::GenerationComplete); + } + if self.error.is_some() { self.download = None; } - true + + shouldrender } fn change(&mut self, _props: Self::Properties) -> ShouldRender { @@ -136,9 +315,9 @@ impl Component for MainView { let download_options = self.link.callback(|e: ChangeData| match e { ChangeData::Select(v) => match v.value().as_str() { - "vcard" => Msg::Generate(DownloadOption::VCard), - "pdf" => Msg::Generate(DownloadOption::PDF), - "qrcode" => Msg::Generate(DownloadOption::QrCode), + "vcard" => Msg::ChangeDownloadOption(DownloadOption::VCard), + "pdf" => Msg::ChangeDownloadOption(DownloadOption::PDF), + "qrcode" => Msg::ChangeDownloadOption(DownloadOption::QrCode), _ => Msg::Nope, }, _ => Msg::Nope, @@ -160,15 +339,54 @@ impl Component for MainView { <section class="section"> <div class="container is-max-widescreen"> - { self.render_errors() } - - <NameView oninput=self.link.callback(|n: Name| Msg::UpdateName(n)) /> - - <AddressView oninput=self.link.callback(|a: Address| Msg::UpdateAddress(a)) /> - - <TelephoneView oninput=self.link.callback(|t: Telephone| Msg::UpdateTelephone(t)) /> + { self.render_error() } + + { + 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,()>| + Msg::GeneratedName(n) + ) + /> + } + ) + } + + { + for self.address_links.iter().map(|link| + html!{ + <AddressView weak_link=link + generated=self.link.callback( + |a: Result<properties::Address,()>| + Msg::GeneratedAddress(a) + ) + /> + } + ) + } + + { + for self.telephone_links.iter().map(|link| + html!{ + <TelephoneView weak_link=link + generated=self.link.callback( + |t: Result<properties::Telephone,()>| + Msg::GeneratedTelephone(t) + ) + /> + } + ) + } <div class="block level-left"> + <button onclick=self.link.callback(|_| Msg::Generate) class="button is-primary level-item" /> + <div class="select level-item"> <select id="download_options" onchange=download_options> <option value="vcard">{ "VCard (.vcf)" }</option> @@ -202,17 +420,20 @@ impl Component for MainView { } impl MainView { - fn render_errors(&self) -> Html { + fn render_error(&self) -> Html { html!{ <> { - for self.error.iter().map(|err| - html!{ - <div class="notification is-danger is-light"> - { err } - </div> - } - ) + match &self.error { + Some(error) => { + html!{ + <div class="notification is-danger is-light"> + { error.msg.clone() } + </div> + } + }, + None => html!{}, + } } </> } @@ -222,7 +443,7 @@ impl MainView { let download = self.download.as_ref().unwrap(); html!{ - <a href=download.as_data_link() download=download.file_name class="button is-primary level-item" > + <a href=download.as_data_link() download=download.file_name class="button is-success level-item" > { "Download" } </a> } @@ -253,41 +474,6 @@ impl MainView { html!{} } } - fn generate_vcard(&self) -> Result<VCard, VCardError> { - match VCard::from_formatted_name_str(&self.name.formatted_name()) { - Ok(vcard) => { - let mut vcard = vcard; - - let names = { - let mut names = HashSet::new(); - names.insert(self.name.to_vcard_property().unwrap()); - - names - }; - - let addresses = { - let mut addresses = HashSet::new(); - addresses.insert(self.address.to_vcard_property().unwrap()); - - addresses - }; - - let telephones = { - let mut telephones = HashSet::new(); - telephones.insert(self.telephone.to_vcard_property().unwrap()); - - telephones - }; - - vcard.names = Some(vcard::Set::from_hash_set(names).unwrap()); - vcard.addresses = Some(vcard::Set::from_hash_set(addresses).unwrap()); - vcard.telephones = Some(vcard::Set::from_hash_set(telephones).unwrap()); - - Ok(vcard) - } - Err(err) => Err(err), - } - } fn generate_pdf(&self) -> Result<String, ()>{ let regular_bytes = include_bytes!("/usr/share/fonts/liberation/LiberationSans-Regular.ttf"); let regular_font_data = fonts::FontData::new(regular_bytes.to_vec(), Some(printpdf::BuiltinFont::Helvetica)).expect("font data should be correct"); @@ -336,4 +522,9 @@ impl MainView { Err(_) => Err(()), } } + fn get_subcomponent_count(&self) -> usize { + self.name_links.len() + + self.address_links.len() + + self.telephone_links.len() + } }
\ No newline at end of file |