summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs346
-rw-r--r--src/name.rs161
-rw-r--r--src/view.rs287
3 files changed, 506 insertions, 288 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 941067e..67c2eed 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,3 +1,347 @@
#![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 view; \ No newline at end of file
+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<Self>,
+ error: Vec<String>,
+ name: Name,
+ download: Option<Download>,
+}
+
+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>) -> 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!{
+ <>
+ <main>
+ <section class="hero">
+ <div class="hero-body">
+ <div class="container is-max-widescreen">
+ <h1 class="title">{ "A Generator for vCards" }</h1>
+ <h2 class="subtitle">{ "Supports generating vCards (.vcf), print-ready PDF business cards and QR Codes" }</h2>
+ </div>
+ </div>
+ </section>
+
+ <section class="section">
+ <div class="container is-max-widescreen">
+
+ { self.render_errors() }
+
+ <NameView oninput=on_name_input />
+
+ <div class="block">
+ <div class="select">
+ <select id="download_options" onchange=download_options>
+ <option value="vcard">{ "VCard (.vcf)" }</option>
+ <option value="pdf">{ "Print-ready PDF" }</option>
+ <option value="qrcode">{ "QR Code" }</option>
+ </select>
+ </div>
+
+ { self.render_download() }
+ </div>
+
+ <div class="block">
+ { self.render_preview() }
+ </div>
+
+ </div>
+ </section>
+ </main>
+
+ <footer class="footer">
+ <div class="content has-text-centered">
+ <p>
+ <strong>{ "VCard Generator" }</strong> { " by " } <a href="https://jelemux.dev">{ "Jeremias Weber" }</a>{ ". "}
+ { "The source code is licenced " } <a href="http://opensource.org/licenses/mit-license.php">{ "MIT" }</a>{"."}
+ </p>
+ </div>
+ </footer>
+ </>
+ }
+ }
+}
+
+impl MainView {
+ fn render_errors(&self) -> Html {
+ html!{
+ <>
+ {
+ for self.error.iter().map(|err|
+ html!{
+ <div class="notification is-danger is-light">
+ { err }
+ </div>
+ }
+ )
+ }
+ </>
+ }
+ }
+ fn render_download(&self) -> Html {
+ if self.download.is_some() {
+ let download = self.download.as_ref().unwrap();
+
+ html!{
+ <a href=download.as_data_link() download=download.file_name class="button is-primary" >
+ { "Download" }
+ </a>
+ }
+ } 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!{
+ <iframe src=download.as_data_link() alt="PDF Preview" width="400" height="550"/>
+ },
+ MimeType::VCard => html!{
+ <code> { download.content.clone() } </code>
+ },
+ MimeType::SVG => html!{
+ <img src=download.as_data_link() alt="Image Preview" class="image is-square" width="300" height="300"/>
+ },
+ }
+ } else {
+ html!{}
+ }
+ }
+ fn generate_vcard(&self) -> Result<VCard, VCardError> {
+ match VCard::from_formatted_name_str(&self.name.formatted_name()) {
+ Ok(vcard) => Ok(vcard),
+ Err(err) => Err(err),
+ }
+ }
+ 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 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 mut doc = genpdf::Document::new(font_family);
+
+ doc.set_title("BCard test");
+ doc.set_minimal_conformance();
+ doc.set_margins(10);
+ doc.set_line_spacing(1.25);
+
+ doc.push(
+ elements::Paragraph::new("genpdf Demo Document")
+ .aligned(elements::Alignment::Center)
+ .styled(style::Style::new().bold().with_font_size(20)),
+ );
+
+ // TODO fill doc with real data
+
+ let mut buf: Vec<u8> = Vec::new();
+ match doc.render(&mut buf) {
+ Ok(_) => Ok(match String::from_utf8(buf) {
+ Ok(s) => s,
+ Err(_) => return Err(()),
+ }),
+ Err(_) => Err(()),
+ }
+ }
+}
+
+
+#[wasm_bindgen(start)]
+pub fn run_app() {
+ init();
+ App::<MainView>::new().mount_to_body();
+} \ No newline at end of file
diff --git a/src/name.rs b/src/name.rs
new file mode 100644
index 0000000..bf91186
--- /dev/null
+++ b/src/name.rs
@@ -0,0 +1,161 @@
+use yew::prelude::*;
+
+#[derive(Clone)]
+pub struct Name {
+ pub prefix: String,
+ pub first_name: String,
+ pub middle_name: String,
+ pub last_name: String,
+ pub suffix: String,
+}
+
+impl Name {
+ pub fn new() -> Self {
+ Self {
+ prefix: String::new(),
+ first_name: String::new(),
+ middle_name: String::new(),
+ last_name: String::new(),
+ suffix: String::new(),
+ }
+ }
+ pub fn formatted_name(&self) -> String {
+ let mut formatted_name = String::new();
+
+ if !self.prefix.is_empty() {
+ formatted_name.push_str(&self.prefix);
+ }
+ if !self.first_name.is_empty() {
+ formatted_name.push_str(" ");
+ formatted_name.push_str(&self.first_name);
+ }
+ if !self.middle_name.is_empty() {
+ formatted_name.push_str(" ");
+ formatted_name.push_str(&self.middle_name);
+ }
+ if !self.last_name.is_empty() {
+ formatted_name.push_str(" ");
+ formatted_name.push_str(&self.last_name);
+ }
+ if !self.suffix.is_empty() {
+ formatted_name.push_str(", ");
+ formatted_name.push_str(&self.suffix);
+ }
+
+ formatted_name
+ }
+}
+
+pub struct NameView {
+ link: ComponentLink<Self>,
+ value: Name,
+ oninput: Callback<Name>,
+ //errors: Vec<String>,
+}
+
+pub enum Msg {
+ UpdatePrefix(String),
+ UpdateFirstName(String),
+ UpdateMiddleName(String),
+ UpdateLastName(String),
+ UpdateSuffix(String),
+}
+
+#[derive(Clone, PartialEq, Properties)]
+pub struct Props {
+ pub oninput: Callback<Name>,
+ //pub errors: Vec<String>,
+}
+
+impl Component for NameView {
+ type Message = Msg;
+ type Properties = Props;
+ fn create(props: <Self as yew::Component>::Properties, link: yew::html::Scope<Self>) -> Self {
+ Self {
+ link,
+ value: Name::new(),
+ oninput: props.oninput,
+ }
+ }
+ fn update(&mut self, msg: <Self as yew::Component>::Message) -> bool {
+ match msg {
+ Msg::UpdatePrefix(p) => self.value.prefix = p,
+ Msg::UpdateFirstName(f) => self.value.first_name = f,
+ Msg::UpdateMiddleName(m) => self.value.middle_name = m,
+ Msg::UpdateLastName(l) => self.value.last_name = l,
+ Msg::UpdateSuffix(s) => self.value.suffix = s,
+ };
+ self.oninput.emit(self.value.clone());
+ true
+ }
+ fn change(&mut self, props: <Self as yew::Component>::Properties) -> bool {
+ self.oninput = props.oninput;
+ true
+ }
+ fn view(&self) -> yew::virtual_dom::VNode {
+ html!{
+ <>
+ <h3 class="subtitle">{ "Name" }</h3>
+
+ <div class="columns">
+
+ <div class="field column">
+ <label class="label">{ "Prefix" }</label>
+ <div class="control">
+ <input id="prefix"
+ type="text"
+ placeholder="Sir"
+ oninput=self.link.callback(|e: InputData| Msg::UpdatePrefix(e.value))
+ />
+ </div>
+ </div>
+
+ <div class="field column">
+ <label class="label">{ "First name" }</label>
+ <div class="control">
+ <input id="first_name"
+ type="text"
+ placeholder="Arthur"
+ oninput=self.link.callback(|e: InputData| Msg::UpdateFirstName(e.value))
+ />
+ </div>
+ </div>
+
+ <div class="field column">
+ <label class="label">{ "Middle name" }</label>
+ <div class="control">
+ <input id="middle_name"
+ type="text"
+ placeholder="Charles"
+ oninput=self.link.callback(|e: InputData| Msg::UpdateMiddleName(e.value))
+ />
+ </div>
+ </div>
+
+ <div class="field column">
+ <label class="label">{ "Last name" }</label>
+ <div class="control">
+ <input id="last_name"
+ type="text"
+ placeholder="Clarke"
+ oninput=self.link.callback(|e: InputData| Msg::UpdateLastName(e.value))
+ />
+ </div>
+ </div>
+
+ <div class="field column">
+ <label class="label">{ "Suffix" }</label>
+ <div class="control">
+ <input id="suffix"
+ type="text"
+ placeholder="CBE FRAS"
+ oninput=self.link.callback(|e: InputData| Msg::UpdateSuffix(e.value))
+ />
+ </div>
+ </div>
+
+ </div>
+ </>
+ }
+ }
+} \ No newline at end of file
diff --git a/src/view.rs b/src/view.rs
deleted file mode 100644
index 9557dc5..0000000
--- a/src/view.rs
+++ /dev/null
@@ -1,287 +0,0 @@
-extern crate console_error_panic_hook;
-use genpdf::Element as _;
-use genpdf::{elements, style, fonts};
-use qrcodegen::QrCode;
-use qrcodegen::QrCodeEcc;
-use wasm_bindgen::prelude::*;
-use js_sys;
-use yew::prelude::*;
-use vcard::{VCard, VCardError};
-use std::panic;
-
-fn init() {
- panic::set_hook(Box::new(console_error_panic_hook::hook));
-}
-
-struct Download {
- file_name: String,
- content: Box<dyn AsRef<[u8]>>,
- mime_type: MimeType,
-}
-
-impl Download {
- 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)
- }
-}
-
-enum MimeType {
- PDF,
- VCard,
- SVG,
-}
-
-impl MimeType {
- fn as_text(&self) -> &str {
- match self {
- MimeType::PDF => "application/pdf",
- MimeType::VCard => "text/vcard",
- MimeType::SVG => "image/svg+xml",
- }
- }
-}
-
-pub struct Form {
- link: ComponentLink<Self>,
- error: Vec<String>,
- formatted_name: String,
- download: Option<Download>,
-}
-
-pub enum Msg {
- UpdateFormattedName(String),
- GenerateVCard,
- GeneratePdf,
- GenerateQrCode,
- Nope,
-}
-
-impl Component for Form {
- type Message = Msg;
- type Properties = ();
-
- fn create(_props: Self::Properties, link: ComponentLink<Self>) -> Self {
- Self { link, error: vec![], formatted_name: String::new(), download: 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.download = Some(
- Download {
- file_name: format!("{}.vcs",self.formatted_name),
- content: Box::new(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.formatted_name),
- content: Box::new(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.formatted_name),
- content: Box::new(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 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::Select(v) => match v.value().as_str() {
- "vcard" => Msg::GenerateVCard,
- "pdf" => Msg::GeneratePdf,
- "qrcode" => Msg::GenerateQrCode,
- _ => Msg::Nope,
- },
- _ => Msg::Nope,
- }
- );
-
- html!{
- <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() }
- <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="vcard">{ "VCard (.vcf)" }</option>
- <option value="pdf">{ "Print-ready PDF" }</option>
- <option value="qrcode">{ "QR Code" }</option>
- </select>
-
- { self.render_download() }
-
- </div>
- }
- }
-}
-
-impl Form {
- fn generate_vcard(&self) -> Result<VCard, VCardError> {
- match VCard::from_formatted_name_str(&self.formatted_name) {
- Ok(vcard) => Ok(vcard),
- Err(err) => Err(err),
- }
- }
- fn generate_pdf(&self) -> Result<Vec<u8>, ()>{
- 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 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 mut doc = genpdf::Document::new(font_family);
-
- doc.set_title("BCard test");
- doc.set_minimal_conformance();
- doc.set_margins(10);
- doc.set_line_spacing(1.25);
-
- doc.push(
- elements::Paragraph::new("genpdf Demo Document")
- .aligned(elements::Alignment::Center)
- .styled(style::Style::new().bold().with_font_size(20)),
- );
-
- // TODO fill doc with real data
-
- let mut buf: Vec<u8> = Vec::new();
- match doc.render(&mut buf) {
- Ok(_) => Ok(buf),
- Err(_) => Err(()),
- }
- }
- fn render_error(&self) -> Html {
- html!{
- <>
- {
- for self.error.iter().map(|err|
- html!{
- <div class="alert danger">
- <p>{ err }</p>
- </div>
- }
- )
- }
- </>
- }
- }
- fn render_download(&self) -> Html {
- if self.download.is_some() {
- let download = self.download.as_ref().unwrap();
-
- html!{
- <a href=download.as_data_link() download=download.file_name class="button success small" >
- { "Download" }
- </a>
- }
- } else {
- 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!{}
- }
- }*/
-}
-
-
-#[wasm_bindgen(start)]
-pub fn run_app() {
- init();
- App::<Form>::new().mount_to_body();
-} \ No newline at end of file