use crate::model::property_groups::address::*; use crate::model::property_groups::communication::*; use crate::model::property_groups::name::*; use crate::model::property_groups::organizational::*; use crate::model::property_groups::other_identification::*; use crate::model::property_groups::telephone::*; use crate::model::utility::*; use crate::model::vcard::VCardData; use crate::model::Error; use crate::model::VCardPropertyInputGroupObject; use crate::view::property_group::PropertyGroupInputComponent; use crate::view::weak_links::WeakComponentLink; use boolinator::Boolinator; use chrono::prelude::*; use genpdf::Element as _; use genpdf::{elements, fonts, style}; use qrcodegen::QrCode; use qrcodegen::QrCodeEcc; use uuid::Uuid; use vobject::parameters; use vobject::vcard::VcardBuilder; use yew::prelude::*; use yew::services::ConsoleService; use yewtil::ptr::Mrc; type NameView = PropertyGroupInputComponent; type AddressView = PropertyGroupInputComponent; type OtherIdentificationView = PropertyGroupInputComponent; type OrganizationalView = PropertyGroupInputComponent; type TelephoneView = PropertyGroupInputComponent; type CommunicationView = PropertyGroupInputComponent; pub struct MainView { link: ComponentLink, error: Option, download: Option, selected_option: DownloadOption, vcard_data: Mrc, name_links: Vec>, address_links: Vec>, telephone_links: Vec>, communcation_links: Vec>, other_identifications_links: Vec>, organizational_links: Vec>, answer_count: usize, } pub enum Msg { AddName, DelName(usize), AddAddress, DelAddress(usize), AddTelephone, DelTelephone(usize), AddOtherIdentification, DelOtherIdentification(usize), AddOrganizational, DelOrganizational(usize), AddCommunication, DelCommunication(usize), ChangeDownloadOption(DownloadOption), Generate, GeneratedName(Name), GeneratedOtherIdentification(OtherIdentification), GeneratedAddress(Address), GeneratedTelephone(Telephone), GeneratedOrganizational(Organizational), GeneratedCommunication(Communication), 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()], communcation_links: vec![WeakComponentLink::default()], other_identifications_links: vec![WeakComponentLink::default()], organizational_links: vec![WeakComponentLink::default()], answer_count: 0, } } fn update(&mut self, msg: Self::Message) -> ShouldRender { self.error = None; let shouldrender = match msg { Msg::AddName => { self.name_links.push(WeakComponentLink::default()); true } Msg::DelName(idx) => { self.name_links.remove(idx); true } Msg::AddAddress => { self.address_links.push(WeakComponentLink::default()); true } Msg::DelAddress(idx) => { self.address_links.remove(idx); true } Msg::AddTelephone => { self.telephone_links.push(WeakComponentLink::default()); true } Msg::DelTelephone(idx) => { self.telephone_links.remove(idx); true } Msg::AddOtherIdentification => { self.other_identifications_links .push(WeakComponentLink::default()); true } Msg::DelOtherIdentification(idx) => { self.other_identifications_links.remove(idx); true } Msg::AddOrganizational => { self.organizational_links.push(WeakComponentLink::default()); true } Msg::DelOrganizational(idx) => { self.organizational_links.remove(idx); true } Msg::AddCommunication => { self.communcation_links.push(WeakComponentLink::default()); true } Msg::DelCommunication(idx) => { self.communcation_links.remove(idx); true } Msg::ChangeDownloadOption(option) => { self.selected_option = option; 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(NameMsg::Generate); } for address_link in self.address_links.iter() { let address_link = address_link.borrow().clone().unwrap(); address_link.send_message(AddressMsg::Generate); } for telephone_link in self.telephone_links.iter() { let telephone_link = telephone_link.borrow().clone().unwrap(); telephone_link.send_message(TelephoneMsg::Generate); } for other_identifications_link in self.other_identifications_links.iter() { let other_identifications_link = other_identifications_link.borrow().clone().unwrap(); other_identifications_link.send_message(OtherIdentificationMsg::Generate) } for organizational_link in self.organizational_links.iter() { let organizational_link = organizational_link.borrow().clone().unwrap(); organizational_link.send_message(OrganizationalMsg::Generate) } for communcation_link in self.communcation_links.iter() { let communcation_link = communcation_link.borrow().clone().unwrap(); communcation_link.send_message(CommunicationMsg::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.")), } } } */ 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", ), }; true } Msg::GeneratedOtherIdentification(other_identification) => { self.answer_count += 1; match self.vcard_data.get_mut() { Some(vcard_data) => vcard_data.add_other_identification(other_identification), None => ConsoleService::info( "Error in GeneratedOtherIdentification: Couldn't get mutable borrow of VCardData", ), }; 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", ), }; 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", ), }; true } Msg::GeneratedCommunication(communication) => { self.answer_count += 1; match self.vcard_data.get_mut() { Some(vcard_data) => vcard_data.add_communication(communication), None => ConsoleService::info( "Error in GeneratedCommunication: Couldn't get mutable borrow of VCardData", ), }; 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"), }; 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()); } } for communication in vcard_data.communications { if !communication.email_address.is_empty() { builder = builder.with_email(communication.email_address.clone()); } if !communication.impp.is_empty() { builder = builder.with_impp(communication.impp.clone()); } } for other_identification in vcard_data.other_identifications { if !other_identification.nickname.is_empty() { builder = builder.with_nickname(parameters!(), other_identification.nickname); } match other_identification.photo { Some(file) => builder = builder.with_photo(parameters!(), file.content), None => (), }; if !other_identification.anniversary.is_empty() { builder = builder.with_anniversary(other_identification.anniversary); } if !other_identification.birthday.is_empty() { builder = builder.with_bday(parameters!(), other_identification.birthday); } if !other_identification.gender.is_empty() { builder = builder.with_gender(parameters!(), other_identification.gender); } } for organizational in vcard_data.organizationals { if !organizational.org.is_empty() { builder = builder.with_org(vec![organizational.org]); } match organizational.logo { Some(file) => builder = builder.with_logo(file.content), None => (), }; 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 uid = format!("urn:uuid:{}", Uuid::new_v4()); let rev = Local::now().to_string(); match builder .with_version("4.0".to_string()) .with_rev(rev) .with_uid(uid) .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"), }; true } Msg::Nope => 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() }

{ "Names" }

{ for self.name_links.iter().enumerate().map(move |(idx, link)| html!{ } ) }

{ "Other Identification Properties" }

{ for self.other_identifications_links.iter().enumerate().map(move |(idx, link)| html!{ } ) }

{ "Addresses" }

{ for self.address_links.iter().enumerate().map(move |(idx, link)| html!{ } ) }

{ "Telephone Numbers" }

{ for self.telephone_links.iter().enumerate().map(move |(idx, link)| html!{ } ) }

{ "Communication" }

{ for self.communcation_links.iter().enumerate().map(move |(idx, link)| html!{ } ) }

{ "Organisations" }

{ for self.organizational_links.iter().enumerate().map(move |(idx, 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! {