summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/model/address.rs2
-rw-r--r--src/model/dates.rs69
-rw-r--r--src/model/mod.rs65
-rw-r--r--src/model/name.rs2
-rw-r--r--src/model/organizational.rs2
-rw-r--r--src/model/other_identification.rs116
-rw-r--r--src/model/telephone.rs2
-rw-r--r--src/model/vcard.rs8
-rw-r--r--src/view/main.rs93
-rw-r--r--src/view/property_group.rs8
10 files changed, 237 insertions, 130 deletions
diff --git a/src/model/address.rs b/src/model/address.rs
index 2c42a98..2321f82 100644
--- a/src/model/address.rs
+++ b/src/model/address.rs
@@ -28,7 +28,7 @@ pub enum AddressMsg {
Generate,
}
-impl VCardPropertyInputObject<AddressMsg> for Address {
+impl VCardPropertyInputGroupObject<AddressMsg> for Address {
fn new() -> Self {
Self {
post_office_box: String::new(),
diff --git a/src/model/dates.rs b/src/model/dates.rs
deleted file mode 100644
index 192f986..0000000
--- a/src/model/dates.rs
+++ /dev/null
@@ -1,69 +0,0 @@
-use super::*;
-
-/// Type that represents the vcard `anniversary` and `birthday` properties.
-#[derive(Clone, Debug, PartialEq)]
-pub struct Dates {
- pub anniversary: String,
- pub birthday: String,
-}
-
-#[derive(Clone, PartialEq)]
-pub enum DatesMsg {
- UpdateAnniversary(String),
- UpdateBirthday(String),
-
- Generate,
-}
-
-impl VCardPropertyInputObject<DatesMsg> for Dates {
- fn new() -> Self {
- Self {
- anniversary: String::new(),
- birthday: String::new(),
- }
- }
- fn get_title(&self) -> std::string::String {
- "Dates".to_string()
- }
- fn get_input_fields(
- &self,
- link: &yew::html::Scope<PropertyGroupInputComponent<Self, DatesMsg>>,
- ) -> std::vec::Vec<VCardPropertyInputField> {
- let typ = String::from("date");
- vec![
- VCardPropertyInputField::Text {
- label: "Anniversary".to_string(),
- id: Some("anniversary".to_string()),
- placeholder: None,
- oninput: link.callback(|e: InputData| DatesMsg::UpdateAnniversary(e.value)),
- value: self.anniversary.clone(),
- typ: typ.clone(),
- },
- VCardPropertyInputField::Text {
- label: "Birthday".to_string(),
- id: Some("birthday".to_string()),
- placeholder: None,
- oninput: link.callback(|e: InputData| DatesMsg::UpdateBirthday(e.value)),
- value: self.birthday.clone(),
- typ,
- },
- ]
- }
- fn update(
- &mut self,
- props: InputProps<Self, DatesMsg>,
- msg: <PropertyGroupInputComponent<Self, DatesMsg> as yew::Component>::Message,
- ) -> bool {
- match msg {
- DatesMsg::UpdateAnniversary(a) => self.anniversary = a,
- DatesMsg::UpdateBirthday(b) => self.birthday = b,
- DatesMsg::Generate => {
- props.generated.emit(self.clone());
- }
- };
- true
- }
- fn is_empty(&self) -> bool {
- self.anniversary.is_empty() && self.birthday.is_empty()
- }
-}
diff --git a/src/model/mod.rs b/src/model/mod.rs
index 45c91f1..b8dcfff 100644
--- a/src/model/mod.rs
+++ b/src/model/mod.rs
@@ -7,23 +7,23 @@ use yew::prelude::*;
use yew::services::ConsoleService;
pub mod address;
-pub mod dates;
pub mod name;
pub mod organizational;
+pub mod other_identification;
pub mod telephone;
pub mod utility;
pub mod vcard;
/// Trait for types that represent the data of a vcard property used inside of a `VCardPropertyInputComponent`.
-pub trait VCardPropertyInputObject<M: 'static + PartialEq + Clone>: Clone + PartialEq
+pub trait VCardPropertyInputGroupObject<M: 'static + PartialEq + Clone>: Clone + PartialEq
where
Self: Sized,
{
- /// Function for creating a new (and empty) `VCardPropertyInputObject`.
+ /// Function for creating a new (and empty) `VCardPropertyInputGroupObject`.
fn new() -> Self;
/// Getter function for the title of the component
fn get_title(&self) -> String;
- /// Converts each field of the `VCardPropertyInputObject` to a VCardPropertyInputField and returns them as a vector.
+ /// Converts each field of the `VCardPropertyInputGroupObject` to a VCardPropertyInputField and returns them as a vector.
fn get_input_fields(
&self,
link: &ComponentLink<PropertyGroupInputComponent<Self, M>>,
@@ -33,7 +33,7 @@ where
props: InputProps<Self, M>,
msg: <PropertyGroupInputComponent<Self, M> as yew::Component>::Message,
) -> bool;
- /// Returns a `Html` representation of the `VCardPropertyInputObject`.
+ /// Returns a `Html` representation of the `VCardPropertyInputGroupObject`.
fn render(&self, link: &ComponentLink<PropertyGroupInputComponent<Self, M>>) -> Html {
html! {
<div class="columns is-mobile is-multiline">
@@ -45,7 +45,7 @@ where
</div>
}
}
- /// Convenience function for checking if the `VCardPropertyInputObject` is empty.
+ /// Convenience function for checking if the `VCardPropertyInputGroupObject` is empty.
fn is_empty(&self) -> bool;
}
@@ -73,6 +73,13 @@ pub enum VCardPropertyInputField {
callback: Callback<Option<File>>,
value: Option<File>,
},
+ Select {
+ label: String,
+ id: Option<String>,
+ options: Vec<(String, String)>,
+ onchange: Callback<ChangeData>,
+ value: String,
+ },
CheckBox {
label: String,
id: Option<String>,
@@ -99,6 +106,13 @@ impl VCardPropertyInputField {
callback,
value,
} => Self::file_field_input(label, name, callback, value),
+ Self::Select {
+ label,
+ id,
+ options,
+ onchange,
+ value,
+ } => Self::select_field_input(label, id, options, onchange, value),
Self::CheckBox {
label,
id,
@@ -180,10 +194,10 @@ impl VCardPropertyInputField {
});
html! {
<div class="field column
- is-one-fifth-widescreen
- is-one-quarter-desktop
- is-one-third-tablet
- is-half-mobile" >
+ is-one-third-widescreen
+ is-two-quarters-desktop
+ is-two-thirds-tablet
+ is-full-mobile" >
<label class="label">{ label }</label>
<div class="file has-name control">
<label class="file-label">
@@ -213,6 +227,37 @@ impl VCardPropertyInputField {
</div>
}
}
+ fn select_field_input(
+ label: &str,
+ id: &Option<String>,
+ options: &Vec<(String, String)>,
+ onchange: &Callback<ChangeData>,
+ value: &str,
+ ) -> Html {
+ 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="select">
+ <select id=id.as_ref().unwrap_or(&"".to_string())
+ value=value
+ onchange=onchange >
+ {
+ for options.iter().map(|option|
+ html! {
+ <option value=option.0>{ option.1.clone() }</option>
+ }
+ )
+ }
+ </select>
+ </div>
+
+ </div>
+ }
+ }
/// Returns an `Html` representation of a checkbox input field with the given parameters.
fn checkbox_field_input(
label: &str,
diff --git a/src/model/name.rs b/src/model/name.rs
index 915579d..8cc889c 100644
--- a/src/model/name.rs
+++ b/src/model/name.rs
@@ -35,7 +35,7 @@ pub enum NameMsg {
Generate,
}
-impl VCardPropertyInputObject<NameMsg> for Name {
+impl VCardPropertyInputGroupObject<NameMsg> for Name {
fn new() -> Self {
Self {
prefix: String::new(),
diff --git a/src/model/organizational.rs b/src/model/organizational.rs
index f82f5d7..cfdb44d 100644
--- a/src/model/organizational.rs
+++ b/src/model/organizational.rs
@@ -22,7 +22,7 @@ pub enum OrganizationalMsg {
Generate,
}
-impl VCardPropertyInputObject<OrganizationalMsg> for Organizational {
+impl VCardPropertyInputGroupObject<OrganizationalMsg> for Organizational {
fn new() -> Self {
Self {
org: String::new(),
diff --git a/src/model/other_identification.rs b/src/model/other_identification.rs
new file mode 100644
index 0000000..a01b818
--- /dev/null
+++ b/src/model/other_identification.rs
@@ -0,0 +1,116 @@
+use super::*;
+
+/// Type that represents the vcard `anniversary` and `birthday` properties.
+#[derive(Clone, Debug, PartialEq)]
+pub struct OtherIdentification {
+ pub nickname: String,
+ pub photo: Option<File>,
+ pub birthday: String,
+ pub anniversary: String,
+ pub gender: String,
+}
+
+#[derive(Clone, PartialEq)]
+pub enum OtherIdentificationMsg {
+ UpdateNickname(String),
+ UpdatePhoto(Option<File>),
+ UpdateBirthday(String),
+ UpdateAnniversary(String),
+ UpdateGender(String),
+
+ Generate,
+
+ Nope,
+}
+
+impl VCardPropertyInputGroupObject<OtherIdentificationMsg> for OtherIdentification {
+ fn new() -> Self {
+ Self {
+ nickname: String::new(),
+ photo: None,
+ birthday: String::new(),
+ anniversary: String::new(),
+ gender: String::new(),
+ }
+ }
+ fn get_title(&self) -> std::string::String {
+ "Other Identification Properties".to_string()
+ }
+ fn get_input_fields(
+ &self,
+ link: &yew::html::Scope<PropertyGroupInputComponent<Self, OtherIdentificationMsg>>,
+ ) -> std::vec::Vec<VCardPropertyInputField> {
+ vec![
+ VCardPropertyInputField::Text {
+ label: String::from("Nickname"),
+ id: Some(String::from("nickname")),
+ placeholder: None,
+ oninput: link
+ .callback(|e: InputData| OtherIdentificationMsg::UpdateNickname(e.value)),
+ value: self.nickname.clone(),
+ typ: String::from("text"),
+ },
+ VCardPropertyInputField::File {
+ label: String::from("Photo"),
+ name: String::from("photo"),
+ callback: link
+ .callback(|file: Option<File>| OtherIdentificationMsg::UpdatePhoto(file)),
+ value: self.photo.clone(),
+ },
+ VCardPropertyInputField::Text {
+ label: "Birthday".to_string(),
+ id: Some("birthday".to_string()),
+ placeholder: None,
+ oninput: link
+ .callback(|e: InputData| OtherIdentificationMsg::UpdateBirthday(e.value)),
+ value: self.birthday.clone(),
+ typ: String::from("date"),
+ },
+ VCardPropertyInputField::Text {
+ label: "Anniversary".to_string(),
+ id: Some("anniversary".to_string()),
+ placeholder: None,
+ oninput: link
+ .callback(|e: InputData| OtherIdentificationMsg::UpdateAnniversary(e.value)),
+ value: self.anniversary.clone(),
+ typ: String::from("date"),
+ },
+ VCardPropertyInputField::Select {
+ label: String::from("Gender"),
+ id: Some(String::from("gender")),
+ options: vec![
+ (String::from(""), String::from("Select one")),
+ (String::from("M"), String::from("Male")),
+ (String::from("F"), String::from("Female")),
+ (String::from("O"), String::from("Other")),
+ (String::from("N"), String::from("Not applicable")),
+ (String::from("U"), String::from("Unknown")),
+ ],
+ onchange: link.callback(|c: ChangeData| match c {
+ ChangeData::Select(v) => OtherIdentificationMsg::UpdateGender(v.value()),
+ _ => OtherIdentificationMsg::Nope,
+ }),
+ value: self.gender.clone(),
+ },
+ ]
+ }
+ fn update(
+ &mut self,
+ props: InputProps<Self, OtherIdentificationMsg>,
+ msg: <PropertyGroupInputComponent<Self, OtherIdentificationMsg> as yew::Component>::Message,
+ ) -> bool {
+ match msg {
+ OtherIdentificationMsg::UpdateNickname(n) => self.nickname = n,
+ OtherIdentificationMsg::UpdatePhoto(p) => self.photo = p,
+ OtherIdentificationMsg::UpdateBirthday(b) => self.birthday = b,
+ OtherIdentificationMsg::UpdateAnniversary(a) => self.anniversary = a,
+ OtherIdentificationMsg::UpdateGender(g) => self.gender = g,
+ OtherIdentificationMsg::Generate => props.generated.emit(self.clone()),
+ OtherIdentificationMsg::Nope => (),
+ };
+ true
+ }
+ fn is_empty(&self) -> bool {
+ self.anniversary.is_empty() && self.birthday.is_empty()
+ }
+}
diff --git a/src/model/telephone.rs b/src/model/telephone.rs
index 946da33..b571aa4 100644
--- a/src/model/telephone.rs
+++ b/src/model/telephone.rs
@@ -30,7 +30,7 @@ pub enum TelephoneMsg {
Generate,
}
-impl VCardPropertyInputObject<TelephoneMsg> for Telephone {
+impl VCardPropertyInputGroupObject<TelephoneMsg> for Telephone {
fn new() -> Self {
Self {
number: String::new(),
diff --git a/src/model/vcard.rs b/src/model/vcard.rs
index ee7e28e..185ae71 100644
--- a/src/model/vcard.rs
+++ b/src/model/vcard.rs
@@ -1,7 +1,7 @@
use crate::model::address::Address;
-use crate::model::dates::Dates;
use crate::model::name::Name;
use crate::model::organizational::Organizational;
+use crate::model::other_identification::OtherIdentification;
use crate::model::telephone::Telephone;
/// Type that represents the data structure of a vcard.
@@ -10,7 +10,7 @@ pub struct VCardData {
pub names: Vec<Name>,
pub addresses: Vec<Address>,
pub telephones: Vec<Telephone>,
- pub datess: Vec<Dates>,
+ pub other_identifications: Vec<OtherIdentification>,
pub organizationals: Vec<Organizational>,
}
@@ -28,13 +28,13 @@ impl VCardData {
names: Vec::new(),
addresses: Vec::new(),
telephones: Vec::new(),
- datess: Vec::new(),
+ other_identifications: Vec::new(),
organizationals: Vec::new(),
}
}
make_vec_adder_fn!( fn add_name names => name: Name );
make_vec_adder_fn!( fn add_address addresses => address: Address );
make_vec_adder_fn!( fn add_telephone telephones => telephone: Telephone );
- make_vec_adder_fn!( fn add_dates datess => dates: Dates );
+ make_vec_adder_fn!( fn add_other_identification other_identifications => other_identification: OtherIdentification );
make_vec_adder_fn!( fn add_organizational organizationals => organizational: Organizational );
}
diff --git a/src/view/main.rs b/src/view/main.rs
index d35fb59..808b160 100644
--- a/src/view/main.rs
+++ b/src/view/main.rs
@@ -1,14 +1,14 @@
-use crate::view::property_group::PropertyGroupInputComponent;
-use crate::view::weak_links::WeakComponentLink;
use crate::model::address::*;
-use crate::model::dates::*;
use crate::model::name::*;
use crate::model::organizational::*;
+use crate::model::other_identification::*;
use crate::model::telephone::*;
use crate::model::utility::*;
use crate::model::vcard::VCardData;
use crate::model::Error;
-use crate::model::VCardPropertyInputObject;
+use crate::model::VCardPropertyInputGroupObject;
+use crate::view::property_group::PropertyGroupInputComponent;
+use crate::view::weak_links::WeakComponentLink;
use boolinator::Boolinator;
use chrono::prelude::*;
use genpdf::Element as _;
@@ -23,7 +23,8 @@ use yewtil::ptr::Mrc;
type NameView = PropertyGroupInputComponent<Name, NameMsg>;
type AddressView = PropertyGroupInputComponent<Address, AddressMsg>;
-type DatesView = PropertyGroupInputComponent<Dates, DatesMsg>;
+type OtherIdentificationView =
+ PropertyGroupInputComponent<OtherIdentification, OtherIdentificationMsg>;
type OrganizationalView = PropertyGroupInputComponent<Organizational, OrganizationalMsg>;
type TelephoneView = PropertyGroupInputComponent<Telephone, TelephoneMsg>;
@@ -37,7 +38,7 @@ pub struct MainView {
name_links: Vec<WeakComponentLink<NameView>>,
address_links: Vec<WeakComponentLink<AddressView>>,
telephone_links: Vec<WeakComponentLink<TelephoneView>>,
- dates_links: Vec<WeakComponentLink<DatesView>>,
+ other_identifications_links: Vec<WeakComponentLink<OtherIdentificationView>>,
organizational_links: Vec<WeakComponentLink<OrganizationalView>>,
answer_count: usize,
@@ -54,9 +55,9 @@ pub enum Msg {
Generate,
GeneratedName(Name),
+ GeneratedOtherIdentification(OtherIdentification),
GeneratedAddress(Address),
GeneratedTelephone(Telephone),
- GeneratedDates(Dates),
GeneratedOrganizational(Organizational),
GenerationComplete,
@@ -78,7 +79,7 @@ impl Component for MainView {
name_links: vec![WeakComponentLink::default()],
address_links: vec![WeakComponentLink::default()],
telephone_links: vec![WeakComponentLink::default()],
- dates_links: vec![WeakComponentLink::default()],
+ other_identifications_links: vec![WeakComponentLink::default()],
organizational_links: vec![WeakComponentLink::default()],
answer_count: 0,
}
@@ -102,7 +103,7 @@ impl Component for MainView {
shouldrender = true;
}
Msg::AddDates => {
- self.dates_links.push(WeakComponentLink::default());
+ self.other_identifications_links.push(WeakComponentLink::default());
shouldrender = true;
}
Msg::AddOrganizational => {
@@ -132,9 +133,9 @@ impl Component for MainView {
telephone_link.send_message(TelephoneMsg::Generate);
}
- for dates_link in self.dates_links.iter() {
+ for dates_link in self.other_identifications_links.iter() {
let dates_link = dates_link.borrow().clone().unwrap();
- dates_link.send_message(DatesMsg::Generate)
+ dates_link.send_message(OtherIdentificationMsg::Generate)
}
for organizational_links in self.organizational_links.iter() {
@@ -172,37 +173,37 @@ impl Component for MainView {
shouldrender = true;
}
- Msg::GeneratedAddress(address) => {
+ Msg::GeneratedOtherIdentification(other_identification) => {
self.answer_count += 1;
match self.vcard_data.get_mut() {
- Some(vcard_data) => vcard_data.add_address(address),
+ Some(vcard_data) => vcard_data.add_other_identification(other_identification),
None => ConsoleService::info(
- "Error in GeneratedAddress: Couldn't get mutable borrow of VCardData",
+ "Error in GeneratedOtherIdentification: Couldn't get mutable borrow of VCardData",
),
};
shouldrender = true;
}
- Msg::GeneratedTelephone(telephone) => {
+ Msg::GeneratedAddress(address) => {
self.answer_count += 1;
match self.vcard_data.get_mut() {
- Some(vcard_data) => vcard_data.add_telephone(telephone),
+ Some(vcard_data) => vcard_data.add_address(address),
None => ConsoleService::info(
- "Error in GeneratedTelephone: Couldn't get mutable borrow of VCardData",
+ "Error in GeneratedAddress: Couldn't get mutable borrow of VCardData",
),
};
shouldrender = true;
}
- Msg::GeneratedDates(dates) => {
+ Msg::GeneratedTelephone(telephone) => {
self.answer_count += 1;
match self.vcard_data.get_mut() {
- Some(vcard_data) => vcard_data.add_dates(dates),
+ Some(vcard_data) => vcard_data.add_telephone(telephone),
None => ConsoleService::info(
- "Error in GeneratedDates: Couldn't get mutable borrow of VCardData",
+ "Error in GeneratedTelephone: Couldn't get mutable borrow of VCardData",
),
};
@@ -336,13 +337,27 @@ impl Component for MainView {
}
}
- for dates in vcard_data.datess {
- if !dates.anniversary.is_empty() {
- builder = builder.with_anniversary(dates.anniversary);
+ for other_identification in vcard_data.other_identifications {
+ if !other_identification.nickname.is_empty() {
+ builder =
+ builder.with_nickname(parameters!(), other_identification.nickname);
+ }
+
+ match other_identification.photo {
+ Some(file) => builder = builder.with_photo(parameters!(), file.content),
+ None => (),
+ };
+
+ if !other_identification.anniversary.is_empty() {
+ builder = builder.with_anniversary(other_identification.anniversary);
}
- if !dates.birthday.is_empty() {
- builder = builder.with_bday(parameters!(), dates.birthday);
+ if !other_identification.birthday.is_empty() {
+ builder = builder.with_bday(parameters!(), other_identification.birthday);
+ }
+
+ if !other_identification.gender.is_empty() {
+ builder = builder.with_gender(parameters!(), other_identification.gender);
}
}
@@ -484,6 +499,19 @@ impl Component for MainView {
}
{
+ for self.other_identifications_links.iter().map(|link|
+ html!{
+ <OtherIdentificationView weak_link=link
+ generated=self.link.callback(
+ |d: OtherIdentification|
+ Msg::GeneratedOtherIdentification(d)
+ )
+ />
+ }
+ )
+ }
+
+ {
for self.address_links.iter().map(|link|
html!{
<AddressView weak_link=link
@@ -510,19 +538,6 @@ impl Component for MainView {
}
{
- for self.dates_links.iter().map(|link|
- html!{
- <DatesView weak_link=link
- generated=self.link.callback(
- |d: Dates|
- Msg::GeneratedDates(d)
- )
- />
- }
- )
- }
-
- {
for self.organizational_links.iter().map(|link|
html!{
<OrganizationalView weak_link=link
@@ -695,7 +710,7 @@ impl MainView {
self.name_links.len()
+ self.address_links.len()
+ self.telephone_links.len()
- + self.dates_links.len()
+ + self.other_identifications_links.len()
+ self.organizational_links.len()
}
}
diff --git a/src/view/property_group.rs b/src/view/property_group.rs
index 9c86e80..f4b62a4 100644
--- a/src/view/property_group.rs
+++ b/src/view/property_group.rs
@@ -5,7 +5,7 @@ use yewtil::NeqAssign;
#[derive(Clone, PartialEq, Properties)]
pub struct InputProps<
- O: 'static + VCardPropertyInputObject<M> + Clone,
+ O: 'static + VCardPropertyInputGroupObject<M> + Clone,
M: 'static + PartialEq + Clone,
> {
pub generated: Callback<O>,
@@ -14,7 +14,7 @@ pub struct InputProps<
#[derive(Clone, PartialEq)]
pub struct PropertyGroupInputComponent<
- O: 'static + VCardPropertyInputObject<M>,
+ O: 'static + VCardPropertyInputGroupObject<M>,
M: 'static + PartialEq + Clone,
> {
pub props: InputProps<O, M>,
@@ -22,7 +22,7 @@ pub struct PropertyGroupInputComponent<
pub error: Option<Error>,
}
-impl<O: 'static + VCardPropertyInputObject<M>, M: 'static + PartialEq + Clone> Component
+impl<O: 'static + VCardPropertyInputGroupObject<M>, M: 'static + PartialEq + Clone> Component
for PropertyGroupInputComponent<O, M>
{
type Message = M;
@@ -57,7 +57,7 @@ impl<O: 'static + VCardPropertyInputObject<M>, M: 'static + PartialEq + Clone> C
}
}
-impl<O: VCardPropertyInputObject<M>, M: 'static + PartialEq + Clone>
+impl<O: VCardPropertyInputGroupObject<M>, M: 'static + PartialEq + Clone>
PropertyGroupInputComponent<O, M>
{
/// Returns the error as `Html`