From 5a03734b6767fed04c0913384584d8f59dc597ea Mon Sep 17 00:00:00 2001 From: jelemux Date: Tue, 24 Nov 2020 21:58:51 +0100 Subject: add traits for viewmodel and view --- src/view/input_objects/address.rs | 151 +++++++++++++++++++++++++++++++++ src/view/input_objects/mod.rs | 106 +++++++++++++++++++++++ src/view/input_objects/name.rs | 118 ++++++++++++++++++++++++++ src/view/input_objects/telephone.rs | 162 ++++++++++++++++++++++++++++++++++++ src/view/input_objects/utility.rs | 39 +++++++++ 5 files changed, 576 insertions(+) create mode 100644 src/view/input_objects/address.rs create mode 100644 src/view/input_objects/mod.rs create mode 100644 src/view/input_objects/name.rs create mode 100644 src/view/input_objects/telephone.rs create mode 100644 src/view/input_objects/utility.rs (limited to 'src/view/input_objects') diff --git a/src/view/input_objects/address.rs b/src/view/input_objects/address.rs new file mode 100644 index 0000000..3975200 --- /dev/null +++ b/src/view/input_objects/address.rs @@ -0,0 +1,151 @@ +use vcard::properties; +use vcard::parameters; +use vcard::values::{self, text}; +use std::collections::HashSet; +use super::*; +use super::super::address::*; + +#[derive(Clone)] +pub struct Address { + pub post_office_box: String, + pub extension: String, + pub street: String, + pub locality: String, + pub region: String, + pub code: String, + pub country: String, + pub work: bool, + pub home: bool, +} + +impl VCardPropertyInputObject for Address { + fn new() -> Self { + Self { + post_office_box: String::new(), + extension: String::new(), + street: String::new(), + locality: String::new(), + region: String::new(), + code: String::new(), + country: String::new(), + work: false, + home: false, + } + } + fn get_input_fields(&self, link: &ComponentLink) -> Vec { + vec![ + 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(), + }, + VCardPropertyInputField::Text{ + label: "Extension".to_string(), + id: Some("extension".to_string()), + placeholder: None, + oninput: link.callback(|e: InputData| Msg::UpdateExtension(e.value)), + value: self.extension.clone(), + }, + VCardPropertyInputField::Text{ + label: "Street".to_string(), + id: Some("street".to_string()), + placeholder: None, + oninput: link.callback(|e: InputData| Msg::UpdateStreet(e.value)), + value: self.street.clone(), + }, + VCardPropertyInputField::Text{ + label: "Locality".to_string(), + id: Some("locality".to_string()), + placeholder: None, + oninput: link.callback(|e: InputData| Msg::UpdateLocality(e.value)), + value: self.locality.clone(), + }, + VCardPropertyInputField::Text{ + label: "Region".to_string(), + id: Some("region".to_string()), + placeholder: None, + oninput: link.callback(|e: InputData| Msg::UpdateRegion(e.value)), + value: self.region.clone(), + }, + VCardPropertyInputField::Text{ + label: "Code".to_string(), + id: Some("code".to_string()), + placeholder: None, + oninput: link.callback(|e: InputData| Msg::UpdateCode(e.value)), + value: self.code.clone(), + }, + VCardPropertyInputField::Text{ + label: "Country".to_string(), + id: Some("country".to_string()), + placeholder: None, + oninput: link.callback(|e: InputData| Msg::UpdateCountry(e.value)), + value: self.country.clone(), + }, + VCardPropertyInputField::CheckBox{ + label: "Work".to_string(), + id: Some("work".to_string()), + onclick: link.callback(|_: MouseEvent| Msg::ToggleWork), + value: self.work, + }, + VCardPropertyInputField::CheckBox{ + label: "Home".to_string(), + id: Some("home".to_string()), + onclick: link.callback(|_: MouseEvent| Msg::ToggleHome), + value: self.home, + }, + ] + } + fn to_vcard_property(&self) -> Result { + let address_value = values::address_value::AddressValue::from_components( + match self.post_office_box.is_empty() { + true => None, + false => Some(text::Component::from_str(&self.post_office_box).unwrap()), + }, + match self.extension.is_empty() { + true => None, + false => Some(text::Component::from_str(&self.extension).unwrap()), + }, + match self.street.is_empty() { + true => None, + false => Some(text::Component::from_str(&self.street).unwrap()), + }, + match self.locality.is_empty() { + true => None, + false => Some(text::Component::from_str(&self.locality).unwrap()), + }, + match self.region.is_empty() { + true => None, + false => Some(text::Component::from_str(&self.region).unwrap()), + }, + match self.code.is_empty() { + true => None, + false => Some(text::Component::from_str(&self.code).unwrap()), + }, + match self.country.is_empty() { + true => None, + false => Some(text::Component::from_str(&self.country).unwrap()), + }, + ); + + let mut address = properties::Address::from_address_value(address_value); + + let type_values = { + let mut type_values = HashSet::new(); + + if self.work { + type_values.insert(values::type_value::TypeValue::Work); + } + if self.home { + type_values.insert(values::type_value::TypeValue::Home); + } + + vcard::Set::from_hash_set(type_values).unwrap() + }; + + address.typ = Some(parameters::typ::Type::from_type_values(type_values)); + + Ok(address) + } +} \ No newline at end of file diff --git a/src/view/input_objects/mod.rs b/src/view/input_objects/mod.rs new file mode 100644 index 0000000..7c0fdff --- /dev/null +++ b/src/view/input_objects/mod.rs @@ -0,0 +1,106 @@ +use vcard::properties; +use yew::prelude::*; +use super::VCardPropertyInputComponent; + +pub mod address; +pub mod birthday; +pub mod name; +pub mod photo; +pub mod telephone; +pub mod utility; + +pub trait VCardPropertyInputObject> + where Self: Sized +{ + fn new() -> Self; + fn get_input_fields(&self, link: &ComponentLink) -> Vec; + fn render(&self, link: &ComponentLink) -> Html { + html!{ +
+ { + for self.get_input_fields(link).iter().map(|field| + field.render() + ) + } +
+ } + } + fn to_vcard_property(&self) -> Result; +} + +#[derive(Debug)] +pub struct VCardPropertyInputError { + msg: String, +} + +pub enum VCardPropertyInputField { + Text { + label: String, + id: Option, + placeholder: Option, + oninput: Callback, + value: String, + }, + CheckBox { + label: String, + id: Option, + onclick: Callback, + value: bool, + }, +} + +impl VCardPropertyInputField { + pub fn render(&self) -> Html { + match self { + Self::Text { + label, + id, + placeholder, + oninput, + value: _, + } => Self::text_field_input(label, id, placeholder, oninput), + Self::CheckBox { + label, + id, + onclick, + value, + } => Self::checkbox_field_input(label, id, value, onclick), + } + } + fn text_field_input(label: &str, id: &Option, placeholder: &Option, oninput: &Callback) -> Html { + html!{ +
+ +
+ +
+
+ } + } + fn checkbox_field_input(label: &str, id: &Option, checked: &bool, onclick: &Callback) -> Html { + html!{ +
+ +
+ } + } +} \ No newline at end of file diff --git a/src/view/input_objects/name.rs b/src/view/input_objects/name.rs new file mode 100644 index 0000000..0fe9e64 --- /dev/null +++ b/src/view/input_objects/name.rs @@ -0,0 +1,118 @@ +use vcard::properties; +use vcard::values::{self, text}; +use super::*; +use super::super::name::*; + +#[derive(Clone)] +pub struct Name { + pub prefix: String, + pub first_name: String, + pub middle_name: String, + pub last_name: String, + pub suffix: String, +} + +impl VCardPropertyInputObject for Name { + fn new() -> Self { + Self { + prefix: String::new(), + first_name: String::new(), + middle_name: String::new(), + last_name: String::new(), + suffix: String::new(), + } + } + fn get_input_fields(&self, link: &ComponentLink) -> std::vec::Vec { + vec![ + VCardPropertyInputField::Text{ + label: "Prefix".to_string(), + id: Some("prefix".to_string()), + placeholder: Some("Sir".to_string()), + oninput: link.callback(|e: InputData| Msg::UpdatePrefix(e.value)), + value: self.prefix.clone(), + }, + VCardPropertyInputField::Text{ + label: "First Name".to_string(), + id: Some("first_name".to_string()), + placeholder: Some("Arthur".to_string()), + oninput: link.callback(|e: InputData| Msg::UpdateFirstName(e.value)), + value: self.first_name.clone(), + }, + VCardPropertyInputField::Text{ + label: "Middle Name".to_string(), + id: Some("middle_name".to_string()), + placeholder: Some("Charles".to_string()), + oninput: link.callback(|e: InputData| Msg::UpdateMiddleName(e.value)), + value: self.middle_name.clone(), + }, + VCardPropertyInputField::Text{ + label: "Last Name".to_string(), + id: Some("last_name".to_string()), + placeholder: Some("Clarke".to_string()), + oninput: link.callback(|e: InputData| Msg::UpdateLastName(e.value)), + value: self.last_name.clone(), + }, + VCardPropertyInputField::Text{ + label: "Suffix".to_string(), + id: Some("suffix".to_string()), + placeholder: Some("CBE FRAS".to_string()), + oninput: link.callback(|e: InputData| Msg::UpdateSuffix(e.value)), + value: self.suffix.clone(), + }, + ] + } + fn to_vcard_property(&self) -> std::result::Result { + let name_value = values::name_value::NameValue::from_components( + match self.last_name.is_empty() { + true => None, + false => Some(text::Component::from_str(&self.last_name).unwrap()), + }, + match self.first_name.is_empty() { + true => None, + false => Some(text::Component::from_str(&self.first_name).unwrap()), + }, + match self.middle_name.is_empty() { + true => None, + false => Some(text::Component::from_str(&self.middle_name).unwrap()), + }, + match self.prefix.is_empty() { + true => None, + false => Some(text::Component::from_str(&self.prefix).unwrap()), + }, + match self.suffix.is_empty() { + true => None, + false => Some(text::Component::from_str(&self.suffix).unwrap()), + }, + ); + + Ok(properties::Name::from_name_value(name_value)) + } +} + +impl Name { + 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 + } +} \ No newline at end of file diff --git a/src/view/input_objects/telephone.rs b/src/view/input_objects/telephone.rs new file mode 100644 index 0000000..d7f7194 --- /dev/null +++ b/src/view/input_objects/telephone.rs @@ -0,0 +1,162 @@ +use vcard::properties; +use vcard::parameters; +use vcard::values; +use std::collections::HashSet; +use super::*; +use super::super::telephone::*; + +#[derive(Clone)] +pub struct Telephone { + pub number: String, + pub extension: String, + pub work: bool, + pub home: bool, + pub text: bool, + pub voice: bool, + pub fax: bool, + pub cell: bool, + pub video: bool, + pub pager: bool, + pub text_phone: bool, +} + +impl VCardPropertyInputObject for Telephone { + fn new() -> Self { + Self { + number: String::new(), + extension: String::new(), + work: false, + home: false, + text: false, + voice: false, + fax: false, + cell: false, + video: false, + pager: false, + text_phone: false, + } + } + fn get_input_fields(&self, link: &ComponentLink) -> Vec { + vec![ + VCardPropertyInputField::Text{ + label: "Number".to_string(), + id: Some("number".to_string()), + placeholder: None, + oninput: link.callback(|e: InputData| Msg::UpdateNumber(e.value)), + value: self.number.clone(), + }, + VCardPropertyInputField::Text{ + label: "Extension".to_string(), + id: Some("extension".to_string()), + placeholder: None, + oninput: link.callback(|e: InputData| Msg::UpdateExtension(e.value)), + value: self.extension.clone(), + }, + VCardPropertyInputField::CheckBox{ + label: "Work".to_string(), + id: Some("work".to_string()), + onclick: link.callback(|_: MouseEvent| Msg::ToggleWork), + value: self.work, + }, + VCardPropertyInputField::CheckBox{ + label: "Home".to_string(), + id: Some("home".to_string()), + onclick: link.callback(|_: MouseEvent| Msg::ToggleHome), + value: self.home, + }, + VCardPropertyInputField::CheckBox{ + label: "Text".to_string(), + id: Some("text".to_string()), + onclick: link.callback(|_: MouseEvent| Msg::ToggleText), + value: self.text, + }, + VCardPropertyInputField::CheckBox{ + label: "Voice".to_string(), + id: Some("voice".to_string()), + onclick: link.callback(|_: MouseEvent| Msg::ToggleVoice), + value: self.voice, + }, + VCardPropertyInputField::CheckBox{ + label: "Fax".to_string(), + id: Some("fax".to_string()), + onclick: link.callback(|_: MouseEvent| Msg::ToggleFax), + value: self.fax, + }, + VCardPropertyInputField::CheckBox{ + label: "Cell".to_string(), + id: Some("cell".to_string()), + onclick: link.callback(|_: MouseEvent| Msg::ToggleCell), + value: self.cell, + }, + VCardPropertyInputField::CheckBox{ + label: "Video".to_string(), + id: Some("video".to_string()), + onclick: link.callback(|_: MouseEvent| Msg::ToggleVideo), + value: self.video, + }, + VCardPropertyInputField::CheckBox{ + label: "Pager".to_string(), + id: Some("pager".to_string()), + onclick: link.callback(|_: MouseEvent| Msg::TogglePager), + value: self.pager, + }, + VCardPropertyInputField::CheckBox{ + label: "Text Phone".to_string(), + id: Some("text_phone".to_string()), + onclick: link.callback(|_: MouseEvent| Msg::ToggleTextPhone), + value: self.text_phone, + }, + ] + } + fn to_vcard_property(&self) -> Result { + let mut telephone = properties::Telephone::from_telephone_value( + values::telephone_value::TelephoneValue::from_telephone_number_str( + self.number.clone(), + match self.extension.is_empty() { + true => None::<&str>, + false => Some(&self.extension), + }, + ).unwrap() + ); + + let type_values = { + let mut type_values = HashSet::new(); + + if self.work { + type_values.insert(values::type_value::TypeValueWithTelephoneType::Work); + } + if self.home { + type_values.insert(values::type_value::TypeValueWithTelephoneType::Home); + } + if self.text { + type_values.insert(values::type_value::TypeValueWithTelephoneType::Text); + } + if self.voice { + type_values.insert(values::type_value::TypeValueWithTelephoneType::Voice); + } + if self.fax { + type_values.insert(values::type_value::TypeValueWithTelephoneType::Fax); + } + if self.cell { + type_values.insert(values::type_value::TypeValueWithTelephoneType::Cell); + } + if self.video { + type_values.insert(values::type_value::TypeValueWithTelephoneType::Video); + } + if self.pager { + type_values.insert(values::type_value::TypeValueWithTelephoneType::Pager); + } + if self.text_phone { + type_values.insert(values::type_value::TypeValueWithTelephoneType::TextPhone); + } + + vcard::Set::from_hash_set(type_values).unwrap() + }; + + if let properties::Telephone::TelephoneValue { ref mut typ, .. } = telephone { + *typ = Some(parameters::typ::TypeWithTelType::from_type_values(type_values)); + } + + Ok(telephone) + } +} \ No newline at end of file diff --git a/src/view/input_objects/utility.rs b/src/view/input_objects/utility.rs new file mode 100644 index 0000000..a296c1e --- /dev/null +++ b/src/view/input_objects/utility.rs @@ -0,0 +1,39 @@ +#[derive(Clone)] +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) + } +} + +#[derive(Clone, Copy)] +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", + } + } +} + +#[derive(Clone, Copy)] +pub enum DownloadOption { + PDF, + VCard, + QrCode, +} \ No newline at end of file -- cgit v1.2.3