diff options
author | jelemux <jeremias.weber@protonmail.com> | 2021-02-09 22:38:40 +0100 |
---|---|---|
committer | jelemux <jeremias.weber@protonmail.com> | 2021-02-09 22:38:40 +0100 |
commit | 9df3ff8d633a18e934d4e62b0e2e718620760552 (patch) | |
tree | 9d84d5fd3e418807905b3da928c1a2c3664b0272 | |
parent | ad9ba30ed217ec9907d1faf389c321a1dcf5c13a (diff) | |
download | wasm-card-9df3ff8d633a18e934d4e62b0e2e718620760552.tar.gz wasm-card-9df3ff8d633a18e934d4e62b0e2e718620760552.tar.bz2 |
add file input field, include file as data url in vcard
-rw-r--r-- | Cargo.toml | 6 | ||||
-rw-r--r-- | src/view/main.rs | 7 | ||||
-rw-r--r-- | src/view/organizational.rs | 3 | ||||
-rw-r--r-- | src/viewmodel/mod.rs | 95 | ||||
-rw-r--r-- | src/viewmodel/organizational.rs | 47 | ||||
-rw-r--r-- | src/viewmodel/utility.rs | 6 | ||||
-rw-r--r-- | static/fontawesome-solid.min.css | 5 | ||||
-rw-r--r-- | static/index.html | 1 |
8 files changed, 156 insertions, 14 deletions
@@ -29,4 +29,8 @@ features = ["services"] [dependencies.chrono] version = "0.4.19" default-features = false -features = ["wasmbind", "js-sys"]
\ No newline at end of file +features = ["wasmbind", "js-sys"] + +[dependencies.web-sys] +version = "0.3.47" +features = ["FileReaderSync"]
\ No newline at end of file diff --git a/src/view/main.rs b/src/view/main.rs index c2af72a..d6b2cb9 100644 --- a/src/view/main.rs +++ b/src/view/main.rs @@ -377,9 +377,10 @@ impl Component for MainView { builder = builder.with_org(vec![organizational.org]); } - if !organizational.logo.is_empty() { - builder = builder.with_logo(organizational.logo); - } + match organizational.logo { + Some(file) => builder = builder.with_logo(file.content), + None => (), + }; if !organizational.title.is_empty() { builder = builder.with_title(organizational.title); diff --git a/src/view/organizational.rs b/src/view/organizational.rs index b11c181..e6636ec 100644 --- a/src/view/organizational.rs +++ b/src/view/organizational.rs @@ -1,3 +1,4 @@ +use crate::viewmodel::utility::File; use yew::prelude::*; use yewtil::NeqAssign; use crate::viewmodel::Error; @@ -17,7 +18,7 @@ pub struct OrganizationalView { pub enum Msg { UpdateOrg(String), - UpdateLogo(String), + UpdateLogo(Option<File>), UpdateTitle(String), UpdateRole(String), UpdateMember(String), diff --git a/src/viewmodel/mod.rs b/src/viewmodel/mod.rs index 75ed1d2..044dbad 100644 --- a/src/viewmodel/mod.rs +++ b/src/viewmodel/mod.rs @@ -1,3 +1,8 @@ +use wasm_bindgen::closure::Closure; +use web_sys::FileReader; +use wasm_bindgen::JsCast; +use yew::services::ConsoleService; +use crate::viewmodel::utility::File; use yew::prelude::*; use crate::view::VCardPropertyInputComponent; @@ -50,7 +55,13 @@ pub enum VCardPropertyInputField { placeholder: Option<String>, oninput: Callback<InputData>, value: String, - typ: String + typ: String, + }, + File { + label: String, + name: String, + callback: Callback<Option<File>>, + value: Option<File>, }, CheckBox { label: String, @@ -72,6 +83,12 @@ impl VCardPropertyInputField { value: _, typ, } => Self::text_field_input(label, id, placeholder, oninput, typ), + Self::File { + label, + name, + callback, + value, + } => Self::file_field_input(label, name, callback, value), Self::CheckBox { label, id, @@ -99,6 +116,82 @@ 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 { + let callback = callback.clone(); + let onchange = Callback::<()>::default(); + let onchange = onchange.reform(move |c: ChangeData| + if let ChangeData::Files(files) = c { + match files.item(0) { + Some(file) => { + let file_reader = FileReader::new().unwrap(); + match file_reader.read_as_data_url(&file) { + Ok(_) => (), + Err(_) => ConsoleService::warn("Error: Couldn't get file as data url."), + }; + + 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(); + match data_url { + 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!{ + <div class="field column + is-one-fifth-widescreen + is-one-quarter-desktop + is-one-third-tablet + is-half-mobile" > + <label class="label">{ label }</label> + <div class="file has-name control"> + <label class="file-label"> + <input class="file-input" + type="file" + name=name + onchange=onchange + /> + <span class="file-cta"> + <span class="file-icon"> + <i class="fas fa-upload"></i> + </span> + <span class="file-label"> + { "Choose file..." } + </span> + </span> + <span class="file-name"> + { + match file { + Some(file) => file.name.clone(), + None => String::from("No file selected"), + } + } + </span> + </label> + </div> + </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!{ diff --git a/src/viewmodel/organizational.rs b/src/viewmodel/organizational.rs index e7a4ae7..c8f7164 100644 --- a/src/viewmodel/organizational.rs +++ b/src/viewmodel/organizational.rs @@ -4,7 +4,7 @@ use super::*; #[derive(Clone,Debug,PartialEq)] pub struct Organizational { pub org: String, - pub logo: String, + pub logo: Option<File>, pub title: String, pub role: String, pub member: String, @@ -15,7 +15,7 @@ impl VCardPropertyInputObject<OrganizationalView> for Organizational { fn new() -> Self { Self { org: String::new(), - logo: String::new(), + logo: None, title: String::new(), role: String::new(), member: String::new(), @@ -33,13 +33,44 @@ impl VCardPropertyInputObject<OrganizationalView> for Organizational { value: self.org.clone(), typ: typ.clone(), }, - VCardPropertyInputField::Text{ // TODO: Add Upload for logo + VCardPropertyInputField::File{ // TODO: Add Upload for logo label: "Logo".to_string(), - id: Some("logo".to_string()), - placeholder: None, - oninput: link.callback(|e: InputData| Msg::UpdateLogo(e.value)), + 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) + ), value: self.logo.clone(), - typ: typ.clone(), }, VCardPropertyInputField::Text{ label: "Title".to_string(), @@ -77,7 +108,7 @@ impl VCardPropertyInputObject<OrganizationalView> for Organizational { } fn is_empty(&self) -> bool { self.org.is_empty() && - self.logo.is_empty() && + self.logo.is_none() && self.title.is_empty() && self.role.is_empty() && self.member.is_empty() && diff --git a/src/viewmodel/utility.rs b/src/viewmodel/utility.rs index 4a82a42..cb581ac 100644 --- a/src/viewmodel/utility.rs +++ b/src/viewmodel/utility.rs @@ -38,4 +38,10 @@ pub enum DownloadOption { PDF, VCard, QrCode, +} + +#[derive(Clone,Debug,PartialEq)] +pub struct File { + pub name: String, + pub content: String, }
\ No newline at end of file diff --git a/static/fontawesome-solid.min.css b/static/fontawesome-solid.min.css new file mode 100644 index 0000000..d61a2d3 --- /dev/null +++ b/static/fontawesome-solid.min.css @@ -0,0 +1,5 @@ +/*! + * Font Awesome Free 5.15.2 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + */ +@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.eot);src:url(../webfonts/fa-solid-900.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.woff) format("woff"),url(../webfonts/fa-solid-900.ttf) format("truetype"),url(../webfonts/fa-solid-900.svg#fontawesome) format("svg")}.fa,.fas{font-family:"Font Awesome 5 Free";font-weight:900}
\ No newline at end of file diff --git a/static/index.html b/static/index.html index 21f35d4..c067b51 100644 --- a/static/index.html +++ b/static/index.html @@ -5,6 +5,7 @@ <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" /> <title>BCard Wasm Web App</title> </head> |