use yew::services::ConsoleService; use crate::viewmodel::vcard::VCardData; use crate::viewmodel::Error; use crate::view::telephone::{self,TelephoneView}; use crate::viewmodel::VCardPropertyInputObject; 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 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 { link: ComponentLink, error: Option, download: Option, selected_option: DownloadOption, vcard_data: Mrc, name_links: Vec>, address_links: Vec>, telephone_links: Vec>, answer_count: usize, } pub enum Msg { AddName, AddAddress, AddTelephone, ChangeDownloadOption(DownloadOption), Generate, GeneratedName(Name), GeneratedAddress(Address), GeneratedTelephone(Telephone), GenerationComplete, Nope, } impl Component for MainView { type Message = Msg; type Properties = (); fn create(_props: Self::Properties, link: ComponentLink) -> Self { MainView { link, error: None, download: None, selected_option: DownloadOption::VCard, vcard_data: Mrc::new(VCardData::new()), 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 { let shouldrender; // let the compiler check if it is always set self.error = None; match msg { Msg::AddName => { self.name_links.push(WeakComponentLink::default()); shouldrender = true; }, Msg::AddAddress => { self.address_links.push(WeakComponentLink::default()); shouldrender = true; }, Msg::AddTelephone => { self.telephone_links.push(WeakComponentLink::default()); shouldrender = true; }, Msg::ChangeDownloadOption(option) => { self.selected_option = option; shouldrender = false; }, Msg::Generate => { 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); } for address_link in self.address_links.iter() { let address_link = address_link.borrow().clone().unwrap(); address_link.send_message(address::Msg::Generate); } 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( Download { file_name: format!("Visitenkarten {}.pdf", self.name.formatted_name()), content: pdf, mime_type: MimeType::PDF, } ), Err(_) => self.error.push(String::from("Unexpected error while generating the PDF. Please contact me about it.")), } } } */ shouldrender = true; }, Msg::GeneratedName(name) => { self.answer_count += 1; 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; }, Msg::GeneratedAddress(address) => { self.answer_count += 1; 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; }, Msg::GeneratedTelephone(telephone) => { self.answer_count += 1; 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; }, Msg::GenerationComplete => { self.answer_count = 0; let vcard_data = self.vcard_data.clone_inner(); let mut builder = VcardBuilder::new(); for name in vcard_data.names { if !name.is_empty() { 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 { if !address.is_empty() { 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") } let params = if types.is_empty() { parameters!() } else { parameters!("TYPE" => types) }; builder = builder.with_adr( params, (!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 { if !telephone.is_empty() { 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") } let params = if types.is_empty() { parameters!() } else { parameters!("TYPE" => types) }; builder = builder.with_tel( params, 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, }; if self.answer_count >= self.get_subcomponent_count() { self.link.send_message(Msg::GenerationComplete); } if self.error.is_some() { self.download = None; } shouldrender } fn change(&mut self, _props: Self::Properties) -> ShouldRender { false } fn view(&self) -> Html { let download_options = self.link.callback(|e: ChangeData| match e { ChangeData::Select(v) => match v.value().as_str() { "vcard" => Msg::ChangeDownloadOption(DownloadOption::VCard), "pdf" => Msg::ChangeDownloadOption(DownloadOption::PDF), "qrcode" => Msg::ChangeDownloadOption(DownloadOption::QrCode), _ => Msg::Nope, }, _ => Msg::Nope, } ); html!{ <>

{ "A Generator for vCards" }

{ "Supports generating vCards (.vcf), print-ready PDF business cards and QR Codes" }

{ self.render_error() } { for self.name_links.iter().map(|link| html!{ } ) } { for self.address_links.iter().map(|link| html!{ } ) } { for self.telephone_links.iter().map(|link| html!{ } ) }
{ self.render_download() }
{ self.render_preview() }
} } } impl MainView { fn render_error(&self) -> Html { html!{ <> { match &self.error { Some(error) => { html!{
{ error.msg.clone() }
} }, None => html!{}, } } } } fn render_download(&self) -> Html { if self.download.is_some() { let download = self.download.as_ref().unwrap(); html!{ { "Download" } } } else { html!{} } } fn render_preview(&self) -> Html { if self.download.is_some() { let download = self.download.as_ref().unwrap(); match download.mime_type { MimeType::PDF => html!{