summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml1
-rw-r--r--src/lib.rs2
-rw-r--r--src/view.rs136
3 files changed, 111 insertions, 28 deletions
diff --git a/Cargo.toml b/Cargo.toml
index bda1251..2dc092e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,6 +18,7 @@ printpdf = "0.3.3"
base64 = "0.13.0"
vcard = "0.4.7"
console_error_panic_hook = "0.1.6"
+qrcodegen = "1.6.0"
[dependencies.chrono]
version = "0.4.19"
diff --git a/src/lib.rs b/src/lib.rs
index 2e54410..fd0e53c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,2 +1,4 @@
+#![recursion_limit="1024"]
+
mod view;
mod pdfgen; \ No newline at end of file
diff --git a/src/view.rs b/src/view.rs
index 281cb63..8da1a6c 100644
--- a/src/view.rs
+++ b/src/view.rs
@@ -1,4 +1,6 @@
extern crate console_error_panic_hook;
+use qrcodegen::QrCode;
+use qrcodegen::QrCodeEcc;
use crate::pdfgen;
use wasm_bindgen::prelude::*;
use js_sys;
@@ -12,14 +14,17 @@ fn init() {
pub struct Form {
link: ComponentLink<Self>,
- error: Option<String>,
+ error: Vec<String>,
formatted_name: String,
- generated_vcard: Option<String>,
+ vcard: Option<String>,
+ qr_code: Option<String>,
}
pub enum Msg {
UpdateFormattedName(String),
GenerateVCard,
+ GeneratePdf,
+ GenerateQrCode,
Nope,
}
@@ -28,21 +33,42 @@ impl Component for Form {
type Properties = ();
fn create(_props: Self::Properties, link: ComponentLink<Self>) -> Self {
- Self { link, error: None, formatted_name: String::new(), generated_vcard: None }
+ Self { link, error: vec![], formatted_name: String::new(), vcard: None, qr_code: None }
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
+ self.error.clear();
match msg {
Msg::UpdateFormattedName(value) => self.formatted_name = String::from(value),
Msg::GenerateVCard => {
match self.generate_vcard() {
- Ok(vcard) => self.generated_vcard = Some(vcard.to_string()),
- Err(VCardError::FormatError(err)) => self.error = Some(err.to_string()),
- Err(VCardError::EmptyFormatName) => self.error = Some(String::from("A VCard should have at least one formatted name.")),
+ Ok(vcard) => self.vcard = 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.")),
};
}
- Msg::Nope => (),
+ Msg::GeneratePdf => {
+
+ }
+ Msg::GenerateQrCode => {
+ match self.generate_vcard() {
+ Ok(vcard) => self.vcard = 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 self.vcard.is_some() {
+ match QrCode::encode_text(self.vcard.as_ref().unwrap(), QrCodeEcc::Low) {
+ Ok(qr) => self.qr_code = Some(qr.to_svg_string(4)),
+ Err(_) => self.error.push(String::from("Sorry, VCard is too long!")),
+ };
+ }
+ }
+ Msg::Nope => return false,
};
+ if self.error.len() > 0 {
+ self.vcard = None;
+ self.qr_code = None;
+ }
true
}
@@ -52,26 +78,63 @@ impl Component for Form {
fn view(&self) -> Html {
- let formatted_name_input = self.link.callback(|e: ChangeData|
+ let formatted_name_input = self.link.batch_callback(|e: InputData|
+ vec![Msg::UpdateFormattedName(e.value), Msg::GenerateVCard]
+ );
+
+ let download_options = self.link.callback(|e: ChangeData|
match e {
- ChangeData::Value(v) => Msg::UpdateFormattedName(v),
+ ChangeData::Select(v) => match v.value().as_str() {
+ "vcard" => Msg::GenerateVCard,
+ "pdf" => Msg::GeneratePdf,
+ "qrcode" => Msg::GenerateQrCode,
+ _ => Msg::Nope,
+ },
_ => Msg::Nope,
}
);
html!{
- <div id="form" >
+ <div class="container">
+ <div class="banner row">
+ <div class="span twelve">
+ <h4>{ "A Generator for vCards" }</h4>
+ <h5>{ "Supports generating vCards (.vcf), print-ready PDF business cards and QR Codes" }</h5>
+ </div>
+ </div>
{ self.render_error() }
- <label for="formatted_name">{ "Formatted Name" }</label>
- <input type="text" id="formatted_name" onchange=formatted_name_input/>
- <input id="prefix"/>
- <input id="first_name"/>
- <input id="middle_name"/>
- <input id="last_name"/>
- <input id="suffix"/>
- <button id="generate-vcard" class="button primary small" onclick=self.link.callback(|_| Msg::GenerateVCard)>{ "Generate VCard" }</button>
+ <h4 class="explainer">{ "Name" }</h4>
+ <form class="row">
+ <label for="formatted_name">{ "Formatted name: " }</label>
+ <input id="formatted_name" type="text" oninput=formatted_name_input/>
+ <br/>
+ <label for="prefix">{ "Prefix: " }</label>
+ <input id="prefix" type="text"/>
+ <br/>
+ <label for="first_name">{ "First name: " }</label>
+ <input id="first_name" type="text"/>
+
+ <label for="middle_name">{ "Middle name: " }</label>
+ <input id="middle_name" type="text"/>
+
+ <label for="last_name">{ "Last name: " }</label>
+ <input id="last_name" type="text"/>
+ <br/>
+ <label for="suffix">{ "Suffix: " }</label>
+ <input id="suffix" type="text"/>
+ </form>
+ <select id="download_options" onchange=download_options>
+ <option value="">{ "" }</option>
+ <option value="vcard">{ "VCard (.vcf)" }</option>
+ <option value="pdf">{ "Print-ready PDF" }</option>
+ <option value="qrcode">{ "QR Code" }</option>
+ </select>
{ self.render_pdf() } { self.render_vcard() }
+
+ <div class="row">
+ { self.render_qrcode() }
+ </div>
</div>
}
}
@@ -85,14 +148,18 @@ impl Form {
}
}
fn render_error(&self) -> Html {
- if self.error.is_some() {
- html!{
- <div class="alert danger">
- <p>{ self.error.as_ref().unwrap() }</p>
- </div>
- }
- } else {
- html!{}
+ html!{
+ <>
+ {
+ for self.error.iter().map(|err|
+ html!{
+ <div class="alert danger">
+ <p>{ err }</p>
+ </div>
+ }
+ )
+ }
+ </>
}
}
fn render_pdf(&self) -> Html {
@@ -108,8 +175,8 @@ impl Form {
}
}
fn render_vcard(&self) -> Html {
- if self.generated_vcard.is_some() {
- let data = base64::encode(self.generated_vcard.as_ref().unwrap());
+ if self.vcard.is_some() {
+ let data = base64::encode(self.vcard.as_ref().unwrap());
let uri_component: String = js_sys::encode_uri_component(&data).into();
let href = format!("data:text/vcard;base64,{}", uri_component);
@@ -122,6 +189,19 @@ impl Form {
html!{}
}
}
+ fn render_qrcode(&self) -> Html {
+ if self.qr_code.is_some() {
+ let data = base64::encode(self.qr_code.as_ref().unwrap());
+ let uri_component: String = js_sys::encode_uri_component(&data).into();
+ let src = format!("data:image/svg+xml;base64,{}", uri_component);
+
+ html!{
+ <img src=src alt="QR Code" width="200" height="200"/>
+ }
+ } else {
+ html!{}
+ }
+ }
}