summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Readme.md3
-rw-r--r--src/view/dates.rs78
-rw-r--r--src/view/main.rs54
-rw-r--r--src/view/mod.rs1
-rw-r--r--src/viewmodel/address.rs8
-rw-r--r--src/viewmodel/dates.rs43
-rw-r--r--src/viewmodel/mod.rs11
-rw-r--r--src/viewmodel/name.rs6
-rw-r--r--src/viewmodel/telephone.rs2
-rw-r--r--src/viewmodel/vcard.rs4
10 files changed, 205 insertions, 5 deletions
diff --git a/Readme.md b/Readme.md
index 60dbd32..cb0e50a 100644
--- a/Readme.md
+++ b/Readme.md
@@ -13,7 +13,7 @@ Supports generating vCards (.vcf), print-ready PDF business cards and QR Codes.
## 🎮 Features
-* Supported properties as of yet: name, address, telephone
+* Supported properties as of yet: name, address, telephone, birthday, anniversary
* Output and Download as vCard (.vcf)
* Output and Download as QR Code
* Generates a dummy PDF
@@ -24,6 +24,7 @@ Supports generating vCards (.vcf), print-ready PDF business cards and QR Codes.
* Button for adding more of one type of property (Support is already there)
* Put generation and download both in download button
* Generate PDF
+* Replace more code with macros to make it even more unreadable 😉
## 🚲 Usage
diff --git a/src/view/dates.rs b/src/view/dates.rs
new file mode 100644
index 0000000..1c16680
--- /dev/null
+++ b/src/view/dates.rs
@@ -0,0 +1,78 @@
+use yew::prelude::*;
+use yewtil::NeqAssign;
+use crate::viewmodel::Error;
+use crate::view::WeakComponentLink;
+use crate::viewmodel::dates::*;
+use crate::viewmodel::VCardPropertyInputObject;
+use super::VCardPropertyInputComponent;
+
+pub struct DatesView {
+ props: Props,
+ value: Dates,
+ error: Option<Error>,
+}
+
+pub enum Msg {
+ UpdateAnniversary(String),
+ UpdateBirthday(String),
+
+ Generate,
+}
+
+#[derive(Clone, PartialEq, Properties)]
+pub struct Props {
+ pub generated: Callback<Dates>,
+ pub weak_link: WeakComponentLink<DatesView>,
+}
+
+impl VCardPropertyInputComponent<Dates> for DatesView {
+ fn get_input_object(&self) -> Dates {
+ self.value.clone()
+ }
+ fn get_title(&self) -> std::string::String {
+ "Dates".to_string()
+ }
+ fn get_error(&self) -> std::option::Option<Error> {
+ self.error.clone()
+ }
+}
+
+impl Component for DatesView {
+ type Message = Msg;
+ type Properties = Props;
+ fn create(props: <Self as yew::Component>::Properties, link: yew::html::Scope<Self>) -> Self {
+ props.weak_link.borrow_mut().replace(link);
+ Self {
+ props,
+ value: Dates::new(),
+ error: None,
+ }
+ }
+ fn update(&mut self, msg: <Self as yew::Component>::Message) -> bool {
+ match msg {
+ Msg::UpdateAnniversary(a) => self.value.anniversary = a,
+ Msg::UpdateBirthday(b) => self.value.birthday = b,
+ Msg::Generate => {
+ self.props.generated.emit(self.value.clone());
+ },
+ };
+ true
+ }
+ fn change(&mut self, props: <Self as yew::Component>::Properties) -> bool {
+ self.props.neq_assign(props)
+ }
+ fn view(&self) -> yew::virtual_dom::VNode {
+ let link = self.props.weak_link.borrow().clone().unwrap();
+
+ html!{
+ <div class="box">
+ { self.render_error() }
+
+ <h3 class="subtitle">{ self.get_title() }</h3>
+
+ { self.get_input_object().render(&link) }
+
+ </div>
+ }
+ }
+} \ No newline at end of file
diff --git a/src/view/main.rs b/src/view/main.rs
index 83acc3f..0d2cddb 100644
--- a/src/view/main.rs
+++ b/src/view/main.rs
@@ -1,3 +1,4 @@
+use crate::viewmodel::dates::Dates;
use yew::services::ConsoleService;
use crate::viewmodel::vcard::VCardData;
use crate::viewmodel::Error;
@@ -6,6 +7,7 @@ use crate::viewmodel::VCardPropertyInputObject;
use super::WeakComponentLink;
use super::name::{self,NameView};
use super::address::{self,AddressView};
+use super::dates::{self,DatesView};
use genpdf::Element as _;
use genpdf::{elements, style, fonts};
use qrcodegen::QrCode;
@@ -32,6 +34,7 @@ pub struct MainView {
name_links: Vec<WeakComponentLink<NameView>>,
address_links: Vec<WeakComponentLink<AddressView>>,
telephone_links: Vec<WeakComponentLink<TelephoneView>>,
+ dates_links: Vec<WeakComponentLink<DatesView>>,
answer_count: usize,
}
@@ -40,6 +43,7 @@ pub enum Msg {
AddName,
AddAddress,
AddTelephone,
+ AddDates,
ChangeDownloadOption(DownloadOption),
@@ -47,6 +51,7 @@ pub enum Msg {
GeneratedName(Name),
GeneratedAddress(Address),
GeneratedTelephone(Telephone),
+ GeneratedDates(Dates),
GenerationComplete,
Nope,
@@ -67,6 +72,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()],
answer_count: 0,
}
}
@@ -88,6 +94,10 @@ impl Component for MainView {
self.telephone_links.push(WeakComponentLink::default());
shouldrender = true;
},
+ Msg::AddDates => {
+ self.dates_links.push(WeakComponentLink::default());
+ shouldrender = true;
+ },
Msg::ChangeDownloadOption(option) => {
self.selected_option = option;
shouldrender = false;
@@ -110,6 +120,11 @@ impl Component for MainView {
let telephone_link = telephone_link.borrow().clone().unwrap();
telephone_link.send_message(telephone::Msg::Generate);
}
+
+ for dates_link in self.dates_links.iter() {
+ let dates_link = dates_link.borrow().clone().unwrap();
+ dates_link.send_message(dates::Msg::Generate)
+ }
}
/*
DownloadOption::PDF => {
@@ -163,6 +178,17 @@ impl Component for MainView {
shouldrender = true;
},
+ Msg::GeneratedDates(dates) => {
+
+ self.answer_count += 1;
+
+ match self.vcard_data.get_mut() {
+ Some(vcard_data) => vcard_data.add_dates(dates),
+ None => ConsoleService::info("Error in GeneratedDates: Couldn't get mutable borrow of VCardData"),
+ };
+
+ shouldrender = true;
+ },
Msg::GenerationComplete => {
self.answer_count = 0;
@@ -305,6 +331,20 @@ impl Component for MainView {
);
}
}
+
+ for dates in vcard_data.datess {
+
+ if !dates.anniversary.is_empty() {
+ builder = builder.with_anniversary(dates.anniversary);
+ }
+
+ if !dates.birthday.is_empty() {
+ builder = builder.with_bday(
+ parameters!(),
+ dates.birthday
+ );
+ }
+ }
let rev = Local::now();
@@ -445,6 +485,19 @@ 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)
+ )
+ />
+ }
+ )
+ }
+
<div class="block level-left">
<button onclick=self.link.callback(|_| Msg::Generate) class="button is-primary level-item">{ "Generate" }</button>
@@ -587,5 +640,6 @@ impl MainView {
self.name_links.len()
+ self.address_links.len()
+ self.telephone_links.len()
+ + self.dates_links.len()
}
} \ No newline at end of file
diff --git a/src/view/mod.rs b/src/view/mod.rs
index d97c6e4..65ef06d 100644
--- a/src/view/mod.rs
+++ b/src/view/mod.rs
@@ -8,6 +8,7 @@ pub mod main;
pub mod name;
pub mod address;
pub mod telephone;
+pub mod dates;
/// Trait for types that represent an input component for a vcard property.
pub trait VCardPropertyInputComponent<T: VCardPropertyInputObject<Self>>: Component {
diff --git a/src/viewmodel/address.rs b/src/viewmodel/address.rs
index 6fd3173..26e168a 100644
--- a/src/viewmodel/address.rs
+++ b/src/viewmodel/address.rs
@@ -29,6 +29,7 @@ impl VCardPropertyInputObject<AddressView> for Address {
}
}
fn get_input_fields(&self, link: &ComponentLink<AddressView>) -> Vec<VCardPropertyInputField> {
+ let typ = String::from("text");
vec![
VCardPropertyInputField::Text{
label: "Post Office Box".to_string(),
@@ -36,6 +37,7 @@ impl VCardPropertyInputObject<AddressView> for Address {
placeholder: None,
oninput: link.callback(|e: InputData| Msg::UpdatePostOfficeBox(e.value)),
value: self.post_office_box.clone(),
+ typ: typ.clone(),
},
VCardPropertyInputField::Text{
label: "Extension".to_string(),
@@ -43,6 +45,7 @@ impl VCardPropertyInputObject<AddressView> for Address {
placeholder: None,
oninput: link.callback(|e: InputData| Msg::UpdateExtension(e.value)),
value: self.extension.clone(),
+ typ: typ.clone(),
},
VCardPropertyInputField::Text{
label: "Street".to_string(),
@@ -50,6 +53,7 @@ impl VCardPropertyInputObject<AddressView> for Address {
placeholder: None,
oninput: link.callback(|e: InputData| Msg::UpdateStreet(e.value)),
value: self.street.clone(),
+ typ: typ.clone(),
},
VCardPropertyInputField::Text{
label: "Locality".to_string(),
@@ -57,6 +61,7 @@ impl VCardPropertyInputObject<AddressView> for Address {
placeholder: None,
oninput: link.callback(|e: InputData| Msg::UpdateLocality(e.value)),
value: self.locality.clone(),
+ typ: typ.clone(),
},
VCardPropertyInputField::Text{
label: "Region".to_string(),
@@ -64,6 +69,7 @@ impl VCardPropertyInputObject<AddressView> for Address {
placeholder: None,
oninput: link.callback(|e: InputData| Msg::UpdateRegion(e.value)),
value: self.region.clone(),
+ typ: typ.clone(),
},
VCardPropertyInputField::Text{
label: "Code".to_string(),
@@ -71,6 +77,7 @@ impl VCardPropertyInputObject<AddressView> for Address {
placeholder: None,
oninput: link.callback(|e: InputData| Msg::UpdateCode(e.value)),
value: self.code.clone(),
+ typ: typ.clone(),
},
VCardPropertyInputField::Text{
label: "Country".to_string(),
@@ -78,6 +85,7 @@ impl VCardPropertyInputObject<AddressView> for Address {
placeholder: None,
oninput: link.callback(|e: InputData| Msg::UpdateCountry(e.value)),
value: self.country.clone(),
+ typ,
},
VCardPropertyInputField::CheckBox{
label: "Work".to_string(),
diff --git a/src/viewmodel/dates.rs b/src/viewmodel/dates.rs
new file mode 100644
index 0000000..6c7fa37
--- /dev/null
+++ b/src/viewmodel/dates.rs
@@ -0,0 +1,43 @@
+use crate::view::dates::*;
+use super::*;
+
+/// Type that represents the vcard `anniversary` and `birthday` properties.
+#[derive(Clone, Debug)]
+pub struct Dates {
+ pub anniversary: String,
+ pub birthday: String,
+}
+
+impl VCardPropertyInputObject<DatesView> for Dates {
+ fn new() -> Self {
+ Self {
+ anniversary: String::new(),
+ birthday: String::new(),
+ }
+ }
+ fn get_input_fields(&self, link: &yew::html::Scope<DatesView>) -> 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| Msg::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| Msg::UpdateBirthday(e.value)),
+ value: self.birthday.clone(),
+ typ,
+ },
+ ]
+ }
+ fn is_empty(&self) -> bool {
+ self.anniversary.is_empty()
+ && self.birthday.is_empty()
+ }
+} \ No newline at end of file
diff --git a/src/viewmodel/mod.rs b/src/viewmodel/mod.rs
index d5d0de3..edd1a8e 100644
--- a/src/viewmodel/mod.rs
+++ b/src/viewmodel/mod.rs
@@ -2,10 +2,11 @@ use yew::prelude::*;
use crate::view::VCardPropertyInputComponent;
pub mod vcard;
+pub mod utility;
pub mod address;
pub mod name;
pub mod telephone;
-pub mod utility;
+pub mod dates;
/// Trait for types that represent the data of a vcard property used inside of a `VCardPropertyInputComponent`.
@@ -48,6 +49,7 @@ pub enum VCardPropertyInputField {
placeholder: Option<String>,
oninput: Callback<InputData>,
value: String,
+ typ: String
},
CheckBox {
label: String,
@@ -67,7 +69,8 @@ impl VCardPropertyInputField {
placeholder,
oninput,
value: _,
- } => Self::text_field_input(label, id, placeholder, oninput),
+ typ,
+ } => Self::text_field_input(label, id, placeholder, oninput, typ),
Self::CheckBox {
label,
id,
@@ -77,7 +80,7 @@ impl VCardPropertyInputField {
}
}
/// Returns an `Html` representation of a text input field with the given parameters.
- fn text_field_input(label: &str, id: &Option<String>, placeholder: &Option<String>, oninput: &Callback<InputData>) -> Html {
+ fn text_field_input(label: &str, id: &Option<String>, placeholder: &Option<String>, oninput: &Callback<InputData>, typ: &str) -> Html {
html!{
<div class="field column
is-one-fifth-widescreen
@@ -87,7 +90,7 @@ impl VCardPropertyInputField {
<label class="label">{ label }</label>
<div class="control">
<input id=id.as_ref().unwrap_or(&"".to_string())
- type="text"
+ type=typ
placeholder=placeholder.as_ref().unwrap_or(&"".to_string())
oninput=oninput
/>
diff --git a/src/viewmodel/name.rs b/src/viewmodel/name.rs
index b6ded60..aa6747c 100644
--- a/src/viewmodel/name.rs
+++ b/src/viewmodel/name.rs
@@ -36,6 +36,7 @@ impl VCardPropertyInputObject<NameView> for Name {
}
}
fn get_input_fields(&self, link: &ComponentLink<NameView>) -> std::vec::Vec<VCardPropertyInputField> {
+ let typ = String::from("text");
vec![
VCardPropertyInputField::Text{
label: "Prefix".to_string(),
@@ -43,6 +44,7 @@ impl VCardPropertyInputObject<NameView> for Name {
placeholder: Some("Sir".to_string()),
oninput: link.callback(|e: InputData| Msg::UpdatePrefix(e.value)),
value: self.prefix.clone(),
+ typ: typ.clone(),
},
VCardPropertyInputField::Text{
label: "First Name".to_string(),
@@ -50,6 +52,7 @@ impl VCardPropertyInputObject<NameView> for Name {
placeholder: Some("Arthur".to_string()),
oninput: link.callback(|e: InputData| Msg::UpdateFirstName(e.value)),
value: self.first_name.clone(),
+ typ: typ.clone(),
},
VCardPropertyInputField::Text{
label: "Middle Name".to_string(),
@@ -57,6 +60,7 @@ impl VCardPropertyInputObject<NameView> for Name {
placeholder: Some("Charles".to_string()),
oninput: link.callback(|e: InputData| Msg::UpdateMiddleName(e.value)),
value: self.middle_name.clone(),
+ typ: typ.clone(),
},
VCardPropertyInputField::Text{
label: "Last Name".to_string(),
@@ -64,6 +68,7 @@ impl VCardPropertyInputObject<NameView> for Name {
placeholder: Some("Clarke".to_string()),
oninput: link.callback(|e: InputData| Msg::UpdateLastName(e.value)),
value: self.last_name.clone(),
+ typ: typ.clone(),
},
VCardPropertyInputField::Text{
label: "Suffix".to_string(),
@@ -71,6 +76,7 @@ impl VCardPropertyInputObject<NameView> for Name {
placeholder: Some("CBE FRAS".to_string()),
oninput: link.callback(|e: InputData| Msg::UpdateSuffix(e.value)),
value: self.suffix.clone(),
+ typ,
},
]
}
diff --git a/src/viewmodel/telephone.rs b/src/viewmodel/telephone.rs
index 6c930a2..774b63d 100644
--- a/src/viewmodel/telephone.rs
+++ b/src/viewmodel/telephone.rs
@@ -31,6 +31,7 @@ impl VCardPropertyInputObject<TelephoneView> for Telephone {
}
}
fn get_input_fields(&self, link: &ComponentLink<TelephoneView>) -> Vec<VCardPropertyInputField> {
+ let typ = String::from("tel");
vec![
VCardPropertyInputField::Text{
label: "Number".to_string(),
@@ -38,6 +39,7 @@ impl VCardPropertyInputObject<TelephoneView> for Telephone {
placeholder: None,
oninput: link.callback(|e: InputData| Msg::UpdateNumber(e.value)),
value: self.number.clone(),
+ typ,
},
VCardPropertyInputField::CheckBox{
label: "Work".to_string(),
diff --git a/src/viewmodel/vcard.rs b/src/viewmodel/vcard.rs
index 2b81fd8..565b1ff 100644
--- a/src/viewmodel/vcard.rs
+++ b/src/viewmodel/vcard.rs
@@ -1,3 +1,4 @@
+use crate::viewmodel::dates::Dates;
use crate::viewmodel::telephone::Telephone;
use crate::viewmodel::address::Address;
use crate::viewmodel::name::Name;
@@ -8,6 +9,7 @@ pub struct VCardData {
pub names: Vec<Name>,
pub addresses: Vec<Address>,
pub telephones: Vec<Telephone>,
+ pub datess: Vec<Dates>,
}
macro_rules! make_vec_adder_fn {
@@ -24,9 +26,11 @@ impl VCardData {
names: Vec::new(),
addresses: Vec::new(),
telephones: Vec::new(),
+ datess: 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 );
} \ No newline at end of file