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>  | 
