#![recursion_limit="1024"] extern crate wee_alloc; extern crate console_error_panic_hook; use name::{NameView,Name}; use genpdf::Element as _; use genpdf::{elements, style, fonts}; use qrcodegen::QrCode; use qrcodegen::QrCodeEcc; use wasm_bindgen::prelude::*; use yew::prelude::*; use vcard::{VCard, VCardError}; use std::panic; mod name; // Use `wee_alloc` as the global allocator. #[global_allocator] static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; fn init() { panic::set_hook(Box::new(console_error_panic_hook::hook)); } pub struct Download { pub file_name: String, pub content: String, pub mime_type: MimeType, } impl Download { pub fn as_data_link(&self) -> String { let data = base64::encode(&*self.content); let uri_component: String = js_sys::encode_uri_component(&data).into(); format!("data:{};base64,{}", self.mime_type.as_text(), uri_component) } } impl Clone for Download { fn clone(&self) -> Self { Self { file_name: self.file_name.clone(), content: self.content.clone(), mime_type: self.mime_type.clone(), } } } pub enum MimeType { PDF, VCard, SVG, } impl MimeType { pub fn as_text(&self) -> &str { match self { MimeType::PDF => "application/pdf", MimeType::VCard => "text/vcard", MimeType::SVG => "image/svg+xml", } } } impl Clone for MimeType { fn clone(&self) -> Self { match self { MimeType::PDF => MimeType::PDF, MimeType::VCard => MimeType::VCard, MimeType::SVG => MimeType::SVG, } } } pub struct MainView { link: ComponentLink, error: Vec, name: Name, download: Option, } pub enum Msg { UpdateName(Name), GenerateVCard, GeneratePdf, GenerateQrCode, Nope, } impl Component for MainView { type Message = Msg; type Properties = (); fn create(_props: Self::Properties, link: ComponentLink) -> Self { MainView { link, error: vec![], name: Name::new(), download: None, } } fn update(&mut self, msg: Self::Message) -> ShouldRender { self.error.clear(); match msg { Msg::UpdateName(value) => self.name = value, Msg::GenerateVCard => { match self.generate_vcard() { Ok(vcard) => self.download = Some( Download { file_name: format!("{}.vcs", self.name.formatted_name()), content: vcard.to_string(), mime_type: MimeType::VCard, } ), Err(VCardError::FormatError(err)) => self.error.push(err.to_string()), Err(VCardError::EmptyFormatName) => self.error.push(String::from("A VCard should have at least one formatted name.")), }; } Msg::GeneratePdf => { 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.")), } } Msg::GenerateQrCode => { let mut vcard_content = None; match self.generate_vcard() { Ok(vcard) => vcard_content = Some(vcard.to_string()), Err(VCardError::FormatError(err)) => self.error.push(err.to_string()), Err(VCardError::EmptyFormatName) => self.error.push(String::from("A VCard should have at least one formatted name.")), }; 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!")), }; } } Msg::Nope => return false, }; if self.error.len() > 0 { self.download = None; } true } fn change(&mut self, _props: Self::Properties) -> ShouldRender { false } fn view(&self) -> Html { let download = self.download.clone(); let on_name_input = self.link.batch_callback(move |n: Name| if download.is_some() { vec![ Msg::UpdateName(n), match download.as_ref().unwrap().mime_type { MimeType::PDF => Msg::GeneratePdf, MimeType::SVG => Msg::GenerateQrCode, MimeType::VCard => Msg::GenerateVCard, } ] } else { vec![Msg::UpdateName(n), Msg::GenerateVCard] } ); let download_options = self.link.callback(|e: ChangeData| match e { ChangeData::Select(v) => match v.value().as_str() { "vcard" => Msg::GenerateVCard, "pdf" => Msg::GeneratePdf, "qrcode" => Msg::GenerateQrCode, _ => Msg::Nope, }, _ => Msg::Nope, } ); html!{ <>

{ "A Generator for vCards" }

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

{ self.render_errors() }
{ self.render_download() }
{ self.render_preview() }
} } } impl MainView { fn render_errors(&self) -> Html { html!{ <> { for self.error.iter().map(|err| html!{
{ err }
} ) } } } 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!{