#![recursion_limit="1024"] extern crate wee_alloc; extern crate console_error_panic_hook; use std::collections::HashSet; use name::{NameView,Name}; use address::{AddressView,Address,AddressType}; 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; mod address; // 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)); } #[derive(Clone)] 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) } } #[derive(Clone, Copy)] 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", } } } #[derive(Clone, Copy)] pub enum DownloadOption { PDF, VCard, QrCode, } pub struct MainView { link: ComponentLink, error: Vec, name: Name, work_address: Address, home_address: Address, download: Option, selected_option: DownloadOption, } pub enum Msg { UpdateName(Name), UpdateHomeAddress(Address), UpdateWorkAddress(Address), Generate(DownloadOption), 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(), work_address: Address::new_with_type(AddressType::Work), home_address: Address::new_with_type(AddressType::Home), download: None, selected_option: DownloadOption::VCard } } fn update(&mut self, msg: Self::Message) -> ShouldRender { self.error.clear(); match msg { Msg::UpdateName(value) => { self.name = value; self.link.send_message(Msg::Generate(self.selected_option)); }, Msg::UpdateHomeAddress(value) => { self.home_address = value; self.link.send_message(Msg::Generate(self.selected_option)); }, Msg::UpdateWorkAddress(value) => { self.work_address = value; self.link.send_message(Msg::Generate(self.selected_option)); }, Msg::Generate(option) => { self.selected_option = option; 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 } }; 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, } ) } } 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!")), }; } } 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.")), } } } } 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_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), _ => 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!{