diff options
-rw-r--r-- | Makefile.toml | 71 | ||||
-rw-r--r-- | Readme.md | 25 | ||||
-rw-r--r-- | index.html (renamed from static/index.html) | 8 | ||||
-rw-r--r-- | src/lib.rs | 10 | ||||
-rw-r--r-- | src/view/address.rs | 28 | ||||
-rw-r--r-- | src/view/dates.rs | 18 | ||||
-rw-r--r-- | src/view/main.rs | 345 | ||||
-rw-r--r-- | src/view/mod.rs | 25 | ||||
-rw-r--r-- | src/view/name.rs | 28 | ||||
-rw-r--r-- | src/view/organizational.rs | 20 | ||||
-rw-r--r-- | src/view/telephone.rs | 26 | ||||
-rw-r--r-- | src/viewmodel/address.rs | 36 | ||||
-rw-r--r-- | src/viewmodel/dates.rs | 16 | ||||
-rw-r--r-- | src/viewmodel/mod.rs | 103 | ||||
-rw-r--r-- | src/viewmodel/name.rs | 31 | ||||
-rw-r--r-- | src/viewmodel/organizational.rs | 71 | ||||
-rw-r--r-- | src/viewmodel/telephone.rs | 27 | ||||
-rw-r--r-- | src/viewmodel/utility.rs | 6 | ||||
-rw-r--r-- | src/viewmodel/vcard.rs | 8 |
19 files changed, 483 insertions, 419 deletions
diff --git a/Makefile.toml b/Makefile.toml new file mode 100644 index 0000000..00ea9b9 --- /dev/null +++ b/Makefile.toml @@ -0,0 +1,71 @@ +[env] +PORT = "8000" + +[config] +skip_core_tasks = true + +# ---- BASIC ---- + +[tasks.watch] +description = "Watch files and recompile the project on change" +run_task = [ + { name = "build" }, +] +watch = true + +[tasks.serve] +description = "Start server" +install_crate = { crate_name = "microserver", binary = "microserver", test_arg = "-h" } +command = "microserver" +args = ["--port", "${PORT}"] + +[tasks.verify] +description = "Format, lint with Clippy and run tests" +dependencies = ["fmt", "clippy", "test_h_firefox"] + +# ---- BUILD ---- + +[tasks.build] +description = "Build with wasm-pack" +install_crate = { crate_name = "wasm-pack", binary = "wasm-pack", test_arg = "-V" } +command = "wasm-pack" +args = ["build", "--target", "web", "--out-name", "app", "--dev", "--debug"] + +[tasks.build_release] +description = "Build with wasm-pack in release mode" +install_crate = { crate_name = "wasm-pack", binary = "wasm-pack", test_arg = "-V" } +command = "wasm-pack" +args = ["build", "--target", "web", "--out-name", "app", "--release"] + +# ---- LINT ---- + +[tasks.clippy] +description = "Lint with Clippy" +install_crate = { rustup_component_name = "clippy", binary = "cargo-clippy", test_arg = "--help" } +command = "cargo" +args = ["clippy", "--all-features", "--", "--deny", "warnings", "--deny", "clippy::pedantic", "--deny", "clippy::nursery"] + +[tasks.fmt] +description = "Format with rustfmt" +install_crate = { rustup_component_name = "rustfmt", binary = "rustfmt", test_arg = "-V" } +command = "cargo" +args = ["fmt"] + + +# ---- TEST ---- + +[tasks.test_h] +description = "Run headless tests. Ex: 'cargo make test_h firefox'. Test envs: [chrome, firefox, safari]" +extend = "test" +args = ["test", "--headless", "--${@}"] + +[tasks.test_h_firefox] +description = "Run headless tests with Firefox." +extend = "test" +args = ["test", "--headless", "--firefox"] + +[tasks.test] +description = "Run tests. Ex: 'cargo make test firefox'. Test envs: [chrome, firefox, safari]" +install_crate = { crate_name = "wasm-pack", binary = "wasm-pack", test_arg = "-V" } +command = "wasm-pack" +args = ["test", "--${@}"]
\ No newline at end of file @@ -32,23 +32,32 @@ Supports generating vCards (.vcf), print-ready PDF business cards and QR Codes. * [Standard rust toolchain](https://www.rust-lang.org/tools/install) * [wasm-pack](https://rustwasm.github.io/wasm-pack/installer/) -* HTTP Server (python has one!) +* cargo-make: `cargo install cargo-make` -### 📦 Build with `wasm-pack build` - +### 📦 Build +Debug: +``` +cargo make build +``` +Release: ``` -wasm-pack build --target web +cargo make build_release ``` -### 🔍 Use the debug flag for tracing errors +### 🍲 Serve ``` -wasm-pack build --debug --target web +cargo make serve ``` -### 🎬 Run on python server +### 🔍 Linting + +``` +cargo make clippy +``` +### 📏 Format ``` -python -m http.server 8000 +cargo make fmt ```
\ No newline at end of file diff --git a/static/index.html b/index.html index c067b51..7c91ad5 100644 --- a/static/index.html +++ b/index.html @@ -3,16 +3,16 @@ <head> <meta charset="utf-8"> - <link rel="icon" href="./favicon.ico" type="image/png" /> - <link rel="stylesheet" href="./bulma.min.css" /> - <link rel="stylesheet" href="./fontawesome-solid.min.css" /> + <link rel="icon" href="./static/favicon.ico" type="image/png" /> + <link rel="stylesheet" href="./static/bulma.min.css" /> + <link rel="stylesheet" href="./static/fontawesome-solid.min.css" /> <title>BCard Wasm Web App</title> </head> <body> <script type="module"> Error.stackTraceLimit = Infinity; - import init from "../pkg/bcard_wasm_webapp.js"; + import init from "./pkg/app.js"; init(); </script> <noscript>This page contains webassembly and javascript content, please enable javascript in your @@ -1,10 +1,10 @@ -#![recursion_limit="1024"] -extern crate wee_alloc; +#![recursion_limit = "1024"] extern crate console_error_panic_hook; -use wasm_bindgen::prelude::*; -use yew::prelude::App; +extern crate wee_alloc; use std::panic; use view::main::MainView; +use wasm_bindgen::prelude::*; +use yew::prelude::App; // Use `wee_alloc` as the global allocator. #[global_allocator] @@ -21,4 +21,4 @@ fn init() { pub fn run_app() { init(); App::<MainView>::new().mount_to_body(); -}
\ No newline at end of file +} diff --git a/src/view/address.rs b/src/view/address.rs index a945272..57ea7e4 100644 --- a/src/view/address.rs +++ b/src/view/address.rs @@ -1,28 +1,28 @@ +use super::VCardPropertyInputComponent; use crate::view::InputProps; -use yew::prelude::*; -use yewtil::NeqAssign; use crate::viewmodel::address::*; -use crate::viewmodel::VCardPropertyInputObject; -use super::VCardPropertyInputComponent; use crate::viewmodel::Error; +use crate::viewmodel::VCardPropertyInputObject; +use yew::prelude::*; +use yewtil::NeqAssign; -type Props = InputProps<Address,AddressView>; +type Props = InputProps<Address, AddressView>; /// View Component for a `address` field -/// +/// /// # Examples -/// +/// /// ```compile_fail /// let html = html!{ -/// <AddressView weak_link=some_weak_component_link +/// <AddressView weak_link=some_weak_component_link /// generated=self.link.callback( -/// |n: Irc<Address>| +/// |n: Irc<Address>| /// Msg::GeneratedAddress(some_address) /// ) /// /> /// }; /// ``` -#[derive(Clone,PartialEq)] +#[derive(Clone, PartialEq)] pub struct AddressView { props: Props, value: Address, @@ -79,17 +79,17 @@ impl Component for AddressView { Msg::ToggleHome => self.value.home = !self.value.home, Msg::Generate => { self.props.generated.emit(self.value.clone()); - }, + } }; true } - fn change(&mut self, props: <Self as yew::Component>::Properties) -> bool { + fn change(&mut self, props: <Self as yew::Component>::Properties) -> bool { self.props.neq_assign(props) } fn view(&self) -> yew::virtual_dom::VNode { let link = self.props.weak_link.borrow().clone().unwrap(); - html!{ + html! { <div class="box"> { self.render_error() } @@ -100,4 +100,4 @@ impl Component for AddressView { </div> } } -}
\ No newline at end of file +} diff --git a/src/view/dates.rs b/src/view/dates.rs index de3d311..b2a6dd3 100644 --- a/src/view/dates.rs +++ b/src/view/dates.rs @@ -1,14 +1,14 @@ +use super::VCardPropertyInputComponent; use crate::view::InputProps; -use yew::prelude::*; -use yewtil::NeqAssign; -use crate::viewmodel::Error; use crate::viewmodel::dates::*; +use crate::viewmodel::Error; use crate::viewmodel::VCardPropertyInputObject; -use super::VCardPropertyInputComponent; +use yew::prelude::*; +use yewtil::NeqAssign; -type Props = InputProps<Dates,DatesView>; +type Props = InputProps<Dates, DatesView>; -#[derive(Clone,PartialEq)] +#[derive(Clone, PartialEq)] pub struct DatesView { props: Props, value: Dates, @@ -51,7 +51,7 @@ impl Component for DatesView { Msg::UpdateBirthday(b) => self.value.birthday = b, Msg::Generate => { self.props.generated.emit(self.value.clone()); - }, + } }; true } @@ -61,7 +61,7 @@ impl Component for DatesView { fn view(&self) -> yew::virtual_dom::VNode { let link = self.props.weak_link.borrow().clone().unwrap(); - html!{ + html! { <div class="box"> { self.render_error() } @@ -72,4 +72,4 @@ impl Component for DatesView { </div> } } -}
\ No newline at end of file +} diff --git a/src/view/main.rs b/src/view/main.rs index d6b2cb9..196afa0 100644 --- a/src/view/main.rs +++ b/src/view/main.rs @@ -1,30 +1,29 @@ -use crate::viewmodel::organizational::Organizational; -use crate::view::organizational::{self,OrganizationalView}; +use super::address::{self, AddressView}; +use super::dates::{self, DatesView}; +use super::name::{self, NameView}; +use super::WeakComponentLink; +use crate::view::organizational::{self, OrganizationalView}; +use crate::view::telephone::{self, TelephoneView}; +use crate::viewmodel::address::Address; use crate::viewmodel::dates::Dates; -use yew::services::ConsoleService; +use crate::viewmodel::name::Name; +use crate::viewmodel::organizational::Organizational; +use crate::viewmodel::telephone::Telephone; +use crate::viewmodel::utility::*; 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 super::dates::{self,DatesView}; +use boolinator::Boolinator; +use chrono::prelude::*; use genpdf::Element as _; -use genpdf::{elements, style, fonts}; +use genpdf::{elements, fonts, style}; use qrcodegen::QrCode; use qrcodegen::QrCodeEcc; +use vobject::parameters; +use vobject::vcard::VcardBuilder; use yew::prelude::*; +use yew::services::ConsoleService; 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<Self>, @@ -67,10 +66,10 @@ impl Component for MainView { type Properties = (); fn create(_props: Self::Properties, link: ComponentLink<Self>) -> Self { - MainView { - link, - error: None, - download: None, + MainView { + link, + error: None, + download: None, selected_option: DownloadOption::VCard, vcard_data: Mrc::new(VCardData::new()), @@ -91,31 +90,31 @@ impl Component for MainView { 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::AddDates => { self.dates_links.push(WeakComponentLink::default()); shouldrender = true; - }, + } Msg::AddOrganizational => { self.organizational_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 { - + 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); @@ -158,52 +157,55 @@ impl Component for MainView { */ 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"), + 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"), + 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"), + None => ConsoleService::info( + "Error in GeneratedTelephone: Couldn't get mutable borrow of VCardData", + ), }; shouldrender = true; - }, + } Msg::GeneratedDates(dates) => { - self.answer_count += 1; match self.vcard_data.get_mut() { Some(vcard_data) => vcard_data.add_dates(dates), - None => ConsoleService::info("Error in GeneratedDates: Couldn't get mutable borrow of VCardData"), + None => ConsoleService::info( + "Error in GeneratedDates: Couldn't get mutable borrow of VCardData", + ), }; shouldrender = true; - }, + } Msg::GeneratedOrganizational(organizational) => { self.answer_count += 1; @@ -213,9 +215,8 @@ impl Component for MainView { }; shouldrender = true; - }, + } Msg::GenerationComplete => { - self.answer_count = 0; let vcard_data = self.vcard_data.clone_inner(); @@ -223,33 +224,20 @@ impl Component for MainView { let mut builder = VcardBuilder::new(); for name in vcard_data.names { - if !name.is_empty() { - - builder = builder - .with_fullname( - name.generate_fn() - ) - .with_name( + 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()) + (!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"); @@ -260,37 +248,29 @@ impl Component for MainView { } 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()), + (!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"); @@ -343,36 +323,28 @@ impl Component for MainView { } types.push_str("TEXTPHONE") } - + let params = if types.is_empty() { parameters!() } else { parameters!("TYPE" => types) }; - - builder = builder.with_tel( - params, - telephone.number.clone(), - ); + + builder = builder.with_tel(params, telephone.number.clone()); } } for dates in vcard_data.datess { - if !dates.anniversary.is_empty() { builder = builder.with_anniversary(dates.anniversary); } if !dates.birthday.is_empty() { - builder = builder.with_bday( - parameters!(), - dates.birthday - ); + builder = builder.with_bday(parameters!(), dates.birthday); } } for organizational in vcard_data.organizationals { - if !organizational.org.is_empty() { builder = builder.with_org(vec![organizational.org]); } @@ -398,50 +370,51 @@ impl Component for MainView { builder = builder.with_related(organizational.related); } } - 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(), + .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(), @@ -449,8 +422,7 @@ impl Component for MainView { }; shouldrender = true; - - }, + } Msg::Nope => shouldrender = false, }; @@ -460,7 +432,7 @@ impl Component for MainView { if self.error.is_some() { self.download = None; } - + shouldrender } @@ -469,20 +441,17 @@ impl Component for MainView { } 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, - }, + 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!{ + html! { <> <main> <section class="hero"> @@ -502,9 +471,9 @@ impl Component for MainView { { for self.name_links.iter().map(|link| html!{ - <NameView weak_link=link + <NameView weak_link=link generated=self.link.callback( - |n: Name| + |n: Name| Msg::GeneratedName(n) ) /> @@ -574,7 +543,7 @@ impl Component for MainView { <option value="qrcode">{ "QR Code" }</option> </select> </div> - + { self.render_download() } </div> @@ -601,7 +570,7 @@ impl Component for MainView { impl MainView { fn render_error(&self) -> Html { - html!{ + html! { <> { match &self.error { @@ -622,13 +591,13 @@ impl MainView { if self.download.is_some() { let download = self.download.as_ref().unwrap(); - html!{ + html! { <a href=download.as_data_link() download=download.file_name class="button is-success level-item" > { "Download" } </a> } } else { - html!{} + html! {} } } fn render_preview(&self) -> Html { @@ -636,53 +605,71 @@ impl MainView { let download = self.download.as_ref().unwrap(); match download.mime_type { - MimeType::PDF => html!{ + MimeType::PDF => html! { <iframe src=download.as_data_link() alt="PDF Preview" width="400" height="550"/> }, MimeType::VCard => { - html!{ + html! { <pre> <code> { download.content.clone() } </code> </pre> } } - MimeType::SVG => html!{ + MimeType::SVG => html! { <img src=download.as_data_link() alt="Image Preview" class="image is-square" width="300" height="300"/> }, } } else { - html!{} + html! {} } } - 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"); + 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"); let bold_bytes = include_bytes!("/usr/share/fonts/liberation/LiberationSans-Bold.ttf"); - let bold_font_data = fonts::FontData::new(bold_bytes.to_vec(), Some(printpdf::BuiltinFont::HelveticaBold)).expect("font data should be correct"); + let bold_font_data = fonts::FontData::new( + bold_bytes.to_vec(), + Some(printpdf::BuiltinFont::HelveticaBold), + ) + .expect("font data should be correct"); let italic_bytes = include_bytes!("/usr/share/fonts/liberation/LiberationSans-Italic.ttf"); - let italic_font_data = fonts::FontData::new(italic_bytes.to_vec(), Some(printpdf::BuiltinFont::HelveticaOblique)).expect("font data should be correct"); - - let bold_italic_bytes = include_bytes!("/usr/share/fonts/liberation/LiberationSans-BoldItalic.ttf"); - let bold_italic_font_data = fonts::FontData::new(bold_italic_bytes.to_vec(), Some(printpdf::BuiltinFont::HelveticaBoldOblique)).expect("font data should be correct"); - - let font_family = fonts::FontFamily{ - regular: regular_font_data, - bold: bold_font_data, - italic: italic_font_data, - bold_italic: bold_italic_font_data + let italic_font_data = fonts::FontData::new( + italic_bytes.to_vec(), + Some(printpdf::BuiltinFont::HelveticaOblique), + ) + .expect("font data should be correct"); + + let bold_italic_bytes = + include_bytes!("/usr/share/fonts/liberation/LiberationSans-BoldItalic.ttf"); + let bold_italic_font_data = fonts::FontData::new( + bold_italic_bytes.to_vec(), + Some(printpdf::BuiltinFont::HelveticaBoldOblique), + ) + .expect("font data should be correct"); + + let font_family = fonts::FontFamily { + regular: regular_font_data, + bold: bold_font_data, + italic: italic_font_data, + bold_italic: bold_italic_font_data, }; let mut doc = genpdf::Document::new(font_family); doc.set_title("BCard test"); doc.set_minimal_conformance(); - + let mut decorator = genpdf::SimplePageDecorator::new(); decorator.set_margins(10); doc.set_page_decorator(decorator); - + doc.set_line_spacing(1.25); doc.push( @@ -704,9 +691,9 @@ impl MainView { } fn get_subcomponent_count(&self) -> usize { self.name_links.len() - + self.address_links.len() - + self.telephone_links.len() - + self.dates_links.len() - + self.organizational_links.len() + + self.address_links.len() + + self.telephone_links.len() + + self.dates_links.len() + + self.organizational_links.len() } -}
\ No newline at end of file +} diff --git a/src/view/mod.rs b/src/view/mod.rs index 0751c53..cbff0fd 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -1,27 +1,30 @@ -use yew::prelude::*; +use crate::viewmodel::*; use std::cell::RefCell; use std::ops::Deref; use std::rc::Rc; -use crate::viewmodel::*; +use yew::prelude::*; -pub mod main; -pub mod name; pub mod address; -pub mod telephone; pub mod dates; +pub mod main; +pub mod name; pub mod organizational; +pub mod telephone; #[derive(Clone, PartialEq, Properties)] -pub struct InputProps<O, C> - where O: VCardPropertyInputObject<C> + Clone, - C: VCardPropertyInputComponent<O> + Clone +pub struct InputProps<O, C> +where + O: VCardPropertyInputObject<C> + Clone, + C: VCardPropertyInputComponent<O> + Clone, { pub generated: Callback<O>, pub weak_link: WeakComponentLink<C>, } /// Trait for types that represent an input component for a vcard property. -pub trait VCardPropertyInputComponent<T: VCardPropertyInputObject<Self>>: Component + Clone + PartialEq { +pub trait VCardPropertyInputComponent<T: VCardPropertyInputObject<Self>>: + Component + Clone + PartialEq +{ /// Returns the object containing the input data. fn get_input_object(&self) -> T; /// Getter function for the title of the component @@ -30,7 +33,7 @@ pub trait VCardPropertyInputComponent<T: VCardPropertyInputObject<Self>>: Compon fn get_error(&self) -> Option<Error>; /// Returns the error as `Html` fn render_error(&self) -> Html { - html!{ + html! { <> { if self.get_error().is_some() { @@ -75,4 +78,4 @@ impl<COMP: Component> PartialEq for WeakComponentLink<COMP> { fn eq(&self, other: &Self) -> bool { Rc::ptr_eq(&self.0, &other.0) } -}
\ No newline at end of file +} diff --git a/src/view/name.rs b/src/view/name.rs index 4976e7d..4b7089b 100644 --- a/src/view/name.rs +++ b/src/view/name.rs @@ -1,28 +1,28 @@ +use super::VCardPropertyInputComponent; use crate::view::InputProps; -use yew::prelude::*; -use yewtil::NeqAssign; -use crate::viewmodel::Error; use crate::viewmodel::name::*; +use crate::viewmodel::Error; use crate::viewmodel::VCardPropertyInputObject; -use super::VCardPropertyInputComponent; +use yew::prelude::*; +use yewtil::NeqAssign; -type Props = InputProps<Name,NameView>; +type Props = InputProps<Name, NameView>; /// View Component for a `name` field -/// +/// /// # Examples -/// +/// /// ```compile_fail /// let html = html!{ -/// <NameView weak_link=some_weak_component_link +/// <NameView weak_link=some_weak_component_link /// generated=self.link.callback( -/// |n: Irc<Name>| +/// |n: Irc<Name>| /// Msg::GeneratedName(some_name) /// ) /// /> /// }; /// ``` -#[derive(Clone,PartialEq)] +#[derive(Clone, PartialEq)] pub struct NameView { props: Props, value: Name, @@ -71,17 +71,17 @@ impl Component for NameView { Msg::UpdateSuffix(s) => self.value.suffix = s, Msg::Generate => { self.props.generated.emit(self.value.clone()); - }, + } }; true } - fn change(&mut self, props: <Self as yew::Component>::Properties) -> bool { + fn change(&mut self, props: <Self as yew::Component>::Properties) -> bool { self.props.neq_assign(props) } fn view(&self) -> yew::virtual_dom::VNode { let link = self.props.weak_link.borrow().clone().unwrap(); - html!{ + html! { <div class="box"> { self.render_error() } @@ -92,4 +92,4 @@ impl Component for NameView { </div> } } -}
\ No newline at end of file +} diff --git a/src/view/organizational.rs b/src/view/organizational.rs index e6636ec..7f2ca69 100644 --- a/src/view/organizational.rs +++ b/src/view/organizational.rs @@ -1,15 +1,15 @@ -use crate::viewmodel::utility::File; -use yew::prelude::*; -use yewtil::NeqAssign; -use crate::viewmodel::Error; +use super::VCardPropertyInputComponent; use crate::view::InputProps; use crate::viewmodel::organizational::*; +use crate::viewmodel::utility::File; +use crate::viewmodel::Error; use crate::viewmodel::VCardPropertyInputObject; -use super::VCardPropertyInputComponent; +use yew::prelude::*; +use yewtil::NeqAssign; -type Props = InputProps<Organizational,OrganizationalView>; +type Props = InputProps<Organizational, OrganizationalView>; -#[derive(Clone,PartialEq)] +#[derive(Clone, PartialEq)] pub struct OrganizationalView { props: Props, value: Organizational, @@ -60,7 +60,7 @@ impl Component for OrganizationalView { Msg::UpdateRelated(r) => self.value.related = r, Msg::Generate => { self.props.generated.emit(self.value.clone()); - }, + } }; true } @@ -70,7 +70,7 @@ impl Component for OrganizationalView { fn view(&self) -> yew::virtual_dom::VNode { let link = self.props.weak_link.borrow().clone().unwrap(); - html!{ + html! { <div class="box"> { self.render_error() } @@ -81,4 +81,4 @@ impl Component for OrganizationalView { </div> } } -}
\ No newline at end of file +} diff --git a/src/view/telephone.rs b/src/view/telephone.rs index dc93632..68389ba 100644 --- a/src/view/telephone.rs +++ b/src/view/telephone.rs @@ -1,28 +1,28 @@ +use super::VCardPropertyInputComponent; use crate::view::InputProps; -use yew::prelude::*; -use yewtil::NeqAssign; -use crate::viewmodel::Error; use crate::viewmodel::telephone::*; +use crate::viewmodel::Error; use crate::viewmodel::VCardPropertyInputObject; -use super::VCardPropertyInputComponent; +use yew::prelude::*; +use yewtil::NeqAssign; -type Props = InputProps<Telephone,TelephoneView>; +type Props = InputProps<Telephone, TelephoneView>; /// View Component for a `telephone` field -/// +/// /// # Examples -/// +/// /// ```compile_fail /// let html = html!{ -/// <TelephoneView weak_link=some_weak_component_link +/// <TelephoneView weak_link=some_weak_component_link /// generated=self.link.callback( -/// |n: Irc<Telephone>| +/// |n: Irc<Telephone>| /// Msg::GeneratedTelephone(some_telephone) /// ) /// /> /// }; /// ``` -#[derive(Clone,PartialEq)] +#[derive(Clone, PartialEq)] pub struct TelephoneView { props: Props, value: Telephone, @@ -85,13 +85,13 @@ impl Component for TelephoneView { }; true } - fn change(&mut self, props: <Self as yew::Component>::Properties) -> bool { + fn change(&mut self, props: <Self as yew::Component>::Properties) -> bool { self.props.neq_assign(props) } fn view(&self) -> yew::virtual_dom::VNode { let link = self.props.weak_link.borrow().clone().unwrap(); - html!{ + html! { <div class="box"> { self.render_error() } @@ -102,4 +102,4 @@ impl Component for TelephoneView { </div> } } -}
\ No newline at end of file +} diff --git a/src/viewmodel/address.rs b/src/viewmodel/address.rs index 71cc8d3..9542675 100644 --- a/src/viewmodel/address.rs +++ b/src/viewmodel/address.rs @@ -31,15 +31,15 @@ impl VCardPropertyInputObject<AddressView> for Address { fn get_input_fields(&self, link: &ComponentLink<AddressView>) -> Vec<VCardPropertyInputField> { let typ = String::from("text"); vec![ - VCardPropertyInputField::Text{ + VCardPropertyInputField::Text { label: "Post Office Box".to_string(), id: Some("post_office_box".to_string()), placeholder: None, oninput: link.callback(|e: InputData| Msg::UpdatePostOfficeBox(e.value)), value: self.post_office_box.clone(), - typ: typ.clone(), + typ: typ.clone(), }, - VCardPropertyInputField::Text{ + VCardPropertyInputField::Text { label: "Extension".to_string(), id: Some("extension".to_string()), placeholder: None, @@ -47,7 +47,7 @@ impl VCardPropertyInputObject<AddressView> for Address { value: self.extension.clone(), typ: typ.clone(), }, - VCardPropertyInputField::Text{ + VCardPropertyInputField::Text { label: "Street".to_string(), id: Some("street".to_string()), placeholder: None, @@ -55,7 +55,7 @@ impl VCardPropertyInputObject<AddressView> for Address { value: self.street.clone(), typ: typ.clone(), }, - VCardPropertyInputField::Text{ + VCardPropertyInputField::Text { label: "Locality".to_string(), id: Some("locality".to_string()), placeholder: None, @@ -63,7 +63,7 @@ impl VCardPropertyInputObject<AddressView> for Address { value: self.locality.clone(), typ: typ.clone(), }, - VCardPropertyInputField::Text{ + VCardPropertyInputField::Text { label: "Region".to_string(), id: Some("region".to_string()), placeholder: None, @@ -71,7 +71,7 @@ impl VCardPropertyInputObject<AddressView> for Address { value: self.region.clone(), typ: typ.clone(), }, - VCardPropertyInputField::Text{ + VCardPropertyInputField::Text { label: "Code".to_string(), id: Some("code".to_string()), placeholder: None, @@ -79,7 +79,7 @@ impl VCardPropertyInputObject<AddressView> for Address { value: self.code.clone(), typ: typ.clone(), }, - VCardPropertyInputField::Text{ + VCardPropertyInputField::Text { label: "Country".to_string(), id: Some("country".to_string()), placeholder: None, @@ -87,13 +87,13 @@ impl VCardPropertyInputObject<AddressView> for Address { value: self.country.clone(), typ, }, - VCardPropertyInputField::CheckBox{ + VCardPropertyInputField::CheckBox { label: "Work".to_string(), id: Some("work".to_string()), onclick: link.callback(|_: MouseEvent| Msg::ToggleWork), value: self.work, }, - VCardPropertyInputField::CheckBox{ + VCardPropertyInputField::CheckBox { label: "Home".to_string(), id: Some("home".to_string()), onclick: link.callback(|_: MouseEvent| Msg::ToggleHome), @@ -102,12 +102,12 @@ impl VCardPropertyInputObject<AddressView> for Address { ] } fn is_empty(&self) -> bool { - self.post_office_box.is_empty() && - self.extension.is_empty() && - self.street.is_empty() && - self.locality.is_empty() && - self.region.is_empty() && - self.code.is_empty() && - self.country.is_empty() + self.post_office_box.is_empty() + && self.extension.is_empty() + && self.street.is_empty() + && self.locality.is_empty() + && self.region.is_empty() + && self.code.is_empty() + && self.country.is_empty() } -}
\ No newline at end of file +} diff --git a/src/viewmodel/dates.rs b/src/viewmodel/dates.rs index 28e8bad..7d8d394 100644 --- a/src/viewmodel/dates.rs +++ b/src/viewmodel/dates.rs @@ -1,5 +1,5 @@ -use crate::view::dates::*; use super::*; +use crate::view::dates::*; /// Type that represents the vcard `anniversary` and `birthday` properties. #[derive(Clone, Debug, PartialEq)] @@ -15,10 +15,13 @@ impl VCardPropertyInputObject<DatesView> for Dates { birthday: String::new(), } } - fn get_input_fields(&self, link: &yew::html::Scope<DatesView>) -> std::vec::Vec<VCardPropertyInputField> { + fn get_input_fields( + &self, + link: &yew::html::Scope<DatesView>, + ) -> std::vec::Vec<VCardPropertyInputField> { let typ = String::from("date"); vec![ - VCardPropertyInputField::Text{ + VCardPropertyInputField::Text { label: "Anniversary".to_string(), id: Some("anniversary".to_string()), placeholder: None, @@ -26,7 +29,7 @@ impl VCardPropertyInputObject<DatesView> for Dates { value: self.anniversary.clone(), typ: typ.clone(), }, - VCardPropertyInputField::Text{ + VCardPropertyInputField::Text { label: "Birthday".to_string(), id: Some("birthday".to_string()), placeholder: None, @@ -37,7 +40,6 @@ impl VCardPropertyInputObject<DatesView> for Dates { ] } fn is_empty(&self) -> bool { - self.anniversary.is_empty() - && self.birthday.is_empty() + self.anniversary.is_empty() && self.birthday.is_empty() } -}
\ No newline at end of file +} diff --git a/src/viewmodel/mod.rs b/src/viewmodel/mod.rs index 044dbad..0385c61 100644 --- a/src/viewmodel/mod.rs +++ b/src/viewmodel/mod.rs @@ -1,23 +1,24 @@ +use crate::view::VCardPropertyInputComponent; +use crate::viewmodel::utility::File; use wasm_bindgen::closure::Closure; -use web_sys::FileReader; use wasm_bindgen::JsCast; -use yew::services::ConsoleService; -use crate::viewmodel::utility::File; +use web_sys::FileReader; use yew::prelude::*; -use crate::view::VCardPropertyInputComponent; +use yew::services::ConsoleService; -pub mod vcard; -pub mod utility; pub mod address; -pub mod name; -pub mod telephone; pub mod dates; +pub mod name; pub mod organizational; - +pub mod telephone; +pub mod utility; +pub mod vcard; /// Trait for types that represent the data of a vcard property used inside of a `VCardPropertyInputComponent`. -pub trait VCardPropertyInputObject<C: VCardPropertyInputComponent<Self>>: Clone + PartialEq - where Self: Sized +pub trait VCardPropertyInputObject<C: VCardPropertyInputComponent<Self>>: + Clone + PartialEq +where + Self: Sized, { /// Function for creating a new (and empty) `VCardPropertyInputObject`. fn new() -> Self; @@ -25,12 +26,12 @@ pub trait VCardPropertyInputObject<C: VCardPropertyInputComponent<Self>>: Clone fn get_input_fields(&self, link: &ComponentLink<C>) -> Vec<VCardPropertyInputField>; /// Returns a `Html` representation of the `VCardPropertyInputObject`. fn render(&self, link: &ComponentLink<C>) -> Html { - html!{ + html! { <div class="columns is-mobile is-multiline"> - { + { for self.get_input_fields(link).iter().map(|field| field.render() - ) + ) } </div> } @@ -40,9 +41,9 @@ pub trait VCardPropertyInputObject<C: VCardPropertyInputComponent<Self>>: Clone } /// Type for saving error messages. -/// +/// /// More of a placeholder for something better later on. -#[derive(Debug,Clone,PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub struct Error { pub msg: String, } @@ -97,10 +98,16 @@ impl VCardPropertyInputField { } => Self::checkbox_field_input(label, id, value, onclick), } } - /// Returns an `Html` representation of a text input field with the given parameters. - fn text_field_input(label: &str, id: &Option<String>, placeholder: &Option<String>, oninput: &Callback<InputData>, typ: &str) -> Html { - html!{ - <div class="field column + /// Returns an `Html` representation of a text input field with the given parameters. + fn text_field_input( + label: &str, + id: &Option<String>, + placeholder: &Option<String>, + oninput: &Callback<InputData>, + typ: &str, + ) -> Html { + html! { + <div class="field column is-one-fifth-widescreen is-one-quarter-desktop is-one-third-tablet @@ -116,14 +123,19 @@ impl VCardPropertyInputField { </div> } } - /// Returns an `Html` representation of a file input field with the given parameters. - fn file_field_input(label: &str, name: &str, callback: &Callback<Option<File>>, file: &Option<File>) -> Html { + /// Returns an `Html` representation of a file input field with the given parameters. + fn file_field_input( + label: &str, + name: &str, + callback: &Callback<Option<File>>, + file: &Option<File>, + ) -> Html { let callback = callback.clone(); let onchange = Callback::<()>::default(); - let onchange = onchange.reform(move |c: ChangeData| + let onchange = onchange.reform(move |c: ChangeData| { if let ChangeData::Files(files) = c { match files.item(0) { - Some(file) => { + Some(file) => { let file_reader = FileReader::new().unwrap(); match file_reader.read_as_data_url(&file) { Ok(_) => (), @@ -131,33 +143,33 @@ impl VCardPropertyInputField { }; let callback = callback.clone(); - let onload = Closure::wrap(Box::new(move |event: Event|{ - let file_reader: FileReader = event.target().unwrap().dyn_into().unwrap(); - let data_url: Option<String> = file_reader.result().unwrap().as_string(); + let onload = Closure::wrap(Box::new(move |event: Event| { + let file_reader: FileReader = + event.target().unwrap().dyn_into().unwrap(); + let data_url: Option<String> = + file_reader.result().unwrap().as_string(); match data_url { - Some(content) => callback.emit( - Some(File { - name: file.name(), - content, - }) - ), + Some(content) => callback.emit(Some(File { + name: file.name(), + content, + })), None => { ConsoleService::warn("Couldn't get data url as string."); callback.emit(None); - }, + } }; }) as Box<dyn FnMut(_)>); file_reader.set_onload(Some(onload.as_ref().unchecked_ref())); onload.forget(); - }, + } None => callback.emit(None), } } else { callback.emit(None); } - ); - html!{ + }); + html! { <div class="field column is-one-fifth-widescreen is-one-quarter-desktop @@ -192,17 +204,22 @@ impl VCardPropertyInputField { </div> } } - /// Returns an `Html` representation of a checkbox input field with the given parameters. - fn checkbox_field_input(label: &str, id: &Option<String>, checked: &bool, onclick: &Callback<MouseEvent>) -> Html { - html!{ - <div class="field column + /// Returns an `Html` representation of a checkbox input field with the given parameters. + fn checkbox_field_input( + label: &str, + id: &Option<String>, + checked: &bool, + onclick: &Callback<MouseEvent>, + ) -> Html { + html! { + <div class="field column is-one-fifth-widescreen is-one-quarter-desktop is-one-third-tablet is-half-mobile" > <label class="checkbox"> <input id=id.as_ref().unwrap_or(&"".to_string()) - type="checkbox" + type="checkbox" checked=*checked onclick=onclick /> @@ -211,4 +228,4 @@ impl VCardPropertyInputField { </div> } } -}
\ No newline at end of file +} diff --git a/src/viewmodel/name.rs b/src/viewmodel/name.rs index ee4736f..de0aa81 100644 --- a/src/viewmodel/name.rs +++ b/src/viewmodel/name.rs @@ -2,7 +2,7 @@ use super::*; use crate::view::name::*; /// Type that represents a vcard `name` property -/// +/// /// # Examples /// ``` /// # use bcard_wasm_webapp::viewmodel::name::Name; @@ -13,7 +13,7 @@ use crate::view::name::*; /// name.middle_name = String::from("Charles"); /// name.last_name = String::from("Clarke"); /// name.suffix = String::from("CBE FRAS"); -/// +/// /// assert_eq!(name.generate_fn(), String::from("Sir Arthur Charles Clarke, CBE FRAS")); /// ``` #[derive(Clone, Debug, PartialEq)] @@ -35,10 +35,13 @@ impl VCardPropertyInputObject<NameView> for Name { suffix: String::new(), } } - fn get_input_fields(&self, link: &ComponentLink<NameView>) -> std::vec::Vec<VCardPropertyInputField> { + fn get_input_fields( + &self, + link: &ComponentLink<NameView>, + ) -> std::vec::Vec<VCardPropertyInputField> { let typ = String::from("text"); vec![ - VCardPropertyInputField::Text{ + VCardPropertyInputField::Text { label: "Prefix".to_string(), id: Some("prefix".to_string()), placeholder: Some("Sir".to_string()), @@ -46,7 +49,7 @@ impl VCardPropertyInputObject<NameView> for Name { value: self.prefix.clone(), typ: typ.clone(), }, - VCardPropertyInputField::Text{ + VCardPropertyInputField::Text { label: "First Name".to_string(), id: Some("first_name".to_string()), placeholder: Some("Arthur".to_string()), @@ -54,7 +57,7 @@ impl VCardPropertyInputObject<NameView> for Name { value: self.first_name.clone(), typ: typ.clone(), }, - VCardPropertyInputField::Text{ + VCardPropertyInputField::Text { label: "Middle Name".to_string(), id: Some("middle_name".to_string()), placeholder: Some("Charles".to_string()), @@ -62,7 +65,7 @@ impl VCardPropertyInputObject<NameView> for Name { value: self.middle_name.clone(), typ: typ.clone(), }, - VCardPropertyInputField::Text{ + VCardPropertyInputField::Text { label: "Last Name".to_string(), id: Some("last_name".to_string()), placeholder: Some("Clarke".to_string()), @@ -70,7 +73,7 @@ impl VCardPropertyInputObject<NameView> for Name { value: self.last_name.clone(), typ: typ.clone(), }, - VCardPropertyInputField::Text{ + VCardPropertyInputField::Text { label: "Suffix".to_string(), id: Some("suffix".to_string()), placeholder: Some("CBE FRAS".to_string()), @@ -81,11 +84,11 @@ impl VCardPropertyInputObject<NameView> for Name { ] } fn is_empty(&self) -> bool { - self.prefix.is_empty() && - self.first_name.is_empty() && - self.middle_name.is_empty() && - self.last_name.is_empty() && - self.suffix.is_empty() + self.prefix.is_empty() + && self.first_name.is_empty() + && self.middle_name.is_empty() + && self.last_name.is_empty() + && self.suffix.is_empty() } } @@ -113,4 +116,4 @@ impl Name { full_name } -}
\ No newline at end of file +} diff --git a/src/viewmodel/organizational.rs b/src/viewmodel/organizational.rs index c8f7164..72b19d2 100644 --- a/src/viewmodel/organizational.rs +++ b/src/viewmodel/organizational.rs @@ -1,7 +1,7 @@ -use crate::view::organizational::*; use super::*; +use crate::view::organizational::*; -#[derive(Clone,Debug,PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct Organizational { pub org: String, pub logo: Option<File>, @@ -22,10 +22,13 @@ impl VCardPropertyInputObject<OrganizationalView> for Organizational { related: String::new(), } } - fn get_input_fields(&self, link: &yew::html::Scope<OrganizationalView>) -> std::vec::Vec<VCardPropertyInputField> { + fn get_input_fields( + &self, + link: &yew::html::Scope<OrganizationalView>, + ) -> std::vec::Vec<VCardPropertyInputField> { let typ = String::from("text"); vec![ - VCardPropertyInputField::Text{ + VCardPropertyInputField::Text { label: "Organisation".to_string(), id: Some("org".to_string()), placeholder: None, @@ -33,46 +36,14 @@ impl VCardPropertyInputObject<OrganizationalView> for Organizational { value: self.org.clone(), typ: typ.clone(), }, - VCardPropertyInputField::File{ // TODO: Add Upload for logo + VCardPropertyInputField::File { + // TODO: Add Upload for logo label: "Logo".to_string(), name: "logo".to_string(), - callback: link.callback(|file: Option<File>| - /* - if let ChangeData::Files(files) = c { - match files.item(0) { - Some(file) => { - let filereader = match FileReaderSync::new() { - Ok(reader) => reader, - Err(_) => { - ConsoleService::warn("Couldn't create new filereader."); - return Msg::UpdateLogo(None) - }, - }; - let content = match filereader.read_as_data_url(&file) { - Ok(content) => content, - Err(_) => { - ConsoleService::warn("Error: Couldn't get file as data url."); - return Msg::UpdateLogo(None) - }, - }; - Msg::UpdateLogo( - Some(File { - name: file.name(), - content, - }) - ) - }, - None => Msg::UpdateLogo(None), - } - } else { - Msg::UpdateLogo(None) - } - */ - Msg::UpdateLogo(file) - ), + callback: link.callback(|file: Option<File>| Msg::UpdateLogo(file)), value: self.logo.clone(), }, - VCardPropertyInputField::Text{ + VCardPropertyInputField::Text { label: "Title".to_string(), id: Some("title".to_string()), placeholder: None, @@ -80,7 +51,7 @@ impl VCardPropertyInputObject<OrganizationalView> for Organizational { value: self.title.clone(), typ: typ.clone(), }, - VCardPropertyInputField::Text{ + VCardPropertyInputField::Text { label: "Role".to_string(), id: Some("role".to_string()), placeholder: None, @@ -88,7 +59,7 @@ impl VCardPropertyInputObject<OrganizationalView> for Organizational { value: self.role.clone(), typ: typ.clone(), }, - VCardPropertyInputField::Text{ + VCardPropertyInputField::Text { label: "Member".to_string(), id: Some("member".to_string()), placeholder: None, @@ -96,7 +67,7 @@ impl VCardPropertyInputObject<OrganizationalView> for Organizational { value: self.member.clone(), typ: typ.clone(), }, - VCardPropertyInputField::Text{ + VCardPropertyInputField::Text { label: "Related".to_string(), id: Some("related".to_string()), placeholder: None, @@ -107,11 +78,11 @@ impl VCardPropertyInputObject<OrganizationalView> for Organizational { ] } fn is_empty(&self) -> bool { - self.org.is_empty() && - self.logo.is_none() && - self.title.is_empty() && - self.role.is_empty() && - self.member.is_empty() && - self.related.is_empty() + self.org.is_empty() + && self.logo.is_none() + && self.title.is_empty() + && self.role.is_empty() + && self.member.is_empty() + && self.related.is_empty() } -}
\ No newline at end of file +} diff --git a/src/viewmodel/telephone.rs b/src/viewmodel/telephone.rs index ee616c3..44b938f 100644 --- a/src/viewmodel/telephone.rs +++ b/src/viewmodel/telephone.rs @@ -30,10 +30,13 @@ impl VCardPropertyInputObject<TelephoneView> for Telephone { text_phone: false, } } - fn get_input_fields(&self, link: &ComponentLink<TelephoneView>) -> Vec<VCardPropertyInputField> { + fn get_input_fields( + &self, + link: &ComponentLink<TelephoneView>, + ) -> Vec<VCardPropertyInputField> { let typ = String::from("tel"); vec![ - VCardPropertyInputField::Text{ + VCardPropertyInputField::Text { label: "Number".to_string(), id: Some("number".to_string()), placeholder: None, @@ -41,55 +44,55 @@ impl VCardPropertyInputObject<TelephoneView> for Telephone { value: self.number.clone(), typ, }, - VCardPropertyInputField::CheckBox{ + VCardPropertyInputField::CheckBox { label: "Work".to_string(), id: Some("work".to_string()), onclick: link.callback(|_: MouseEvent| Msg::ToggleWork), value: self.work, }, - VCardPropertyInputField::CheckBox{ + VCardPropertyInputField::CheckBox { label: "Home".to_string(), id: Some("home".to_string()), onclick: link.callback(|_: MouseEvent| Msg::ToggleHome), value: self.home, }, - VCardPropertyInputField::CheckBox{ + VCardPropertyInputField::CheckBox { label: "Text".to_string(), id: Some("text".to_string()), onclick: link.callback(|_: MouseEvent| Msg::ToggleText), value: self.text, }, - VCardPropertyInputField::CheckBox{ + VCardPropertyInputField::CheckBox { label: "Voice".to_string(), id: Some("voice".to_string()), onclick: link.callback(|_: MouseEvent| Msg::ToggleVoice), value: self.voice, }, - VCardPropertyInputField::CheckBox{ + VCardPropertyInputField::CheckBox { label: "Fax".to_string(), id: Some("fax".to_string()), onclick: link.callback(|_: MouseEvent| Msg::ToggleFax), value: self.fax, }, - VCardPropertyInputField::CheckBox{ + VCardPropertyInputField::CheckBox { label: "Cell".to_string(), id: Some("cell".to_string()), onclick: link.callback(|_: MouseEvent| Msg::ToggleCell), value: self.cell, }, - VCardPropertyInputField::CheckBox{ + VCardPropertyInputField::CheckBox { label: "Video".to_string(), id: Some("video".to_string()), onclick: link.callback(|_: MouseEvent| Msg::ToggleVideo), value: self.video, }, - VCardPropertyInputField::CheckBox{ + VCardPropertyInputField::CheckBox { label: "Pager".to_string(), id: Some("pager".to_string()), onclick: link.callback(|_: MouseEvent| Msg::TogglePager), value: self.pager, }, - VCardPropertyInputField::CheckBox{ + VCardPropertyInputField::CheckBox { label: "Text Phone".to_string(), id: Some("text_phone".to_string()), onclick: link.callback(|_: MouseEvent| Msg::ToggleTextPhone), @@ -100,4 +103,4 @@ impl VCardPropertyInputObject<TelephoneView> for Telephone { fn is_empty(&self) -> bool { self.number.is_empty() } -}
\ No newline at end of file +} diff --git a/src/viewmodel/utility.rs b/src/viewmodel/utility.rs index cb581ac..617ee45 100644 --- a/src/viewmodel/utility.rs +++ b/src/viewmodel/utility.rs @@ -1,5 +1,3 @@ - - #[derive(Clone)] pub struct Download { pub file_name: String, @@ -40,8 +38,8 @@ pub enum DownloadOption { QrCode, } -#[derive(Clone,Debug,PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct File { pub name: String, pub content: String, -}
\ No newline at end of file +} diff --git a/src/viewmodel/vcard.rs b/src/viewmodel/vcard.rs index 0d2d6c4..18ce43a 100644 --- a/src/viewmodel/vcard.rs +++ b/src/viewmodel/vcard.rs @@ -1,8 +1,8 @@ -use crate::viewmodel::organizational::Organizational; -use crate::viewmodel::dates::Dates; -use crate::viewmodel::telephone::Telephone; use crate::viewmodel::address::Address; +use crate::viewmodel::dates::Dates; use crate::viewmodel::name::Name; +use crate::viewmodel::organizational::Organizational; +use crate::viewmodel::telephone::Telephone; /// Type that represents the data structure of a vcard. #[derive(Clone, Debug)] @@ -37,4 +37,4 @@ impl VCardData { make_vec_adder_fn!( fn add_telephone telephones => telephone: Telephone ); make_vec_adder_fn!( fn add_dates datess => dates: Dates ); make_vec_adder_fn!( fn add_organizational organizationals => organizational: Organizational ); -}
\ No newline at end of file +} |