diff --git a/crates/store/src/addressbook/address_object.rs b/crates/store/src/addressbook/address_object.rs index 81e7e5c..7225a87 100644 --- a/crates/store/src/addressbook/address_object.rs +++ b/crates/store/src/addressbook/address_object.rs @@ -49,6 +49,11 @@ impl AddressObject { &self.vcf } + pub fn get_anniversary(&self) -> Option { + let prop = self.vcard.get_property("ANNIVERSARY")?; + CalDateTime::parse_prop(prop, &HashMap::default()).unwrap_or(None) + } + pub fn get_birthday(&self) -> Option { let prop = self.vcard.get_property("BDAY")?; CalDateTime::parse_prop(prop, &HashMap::default()).unwrap_or(None) @@ -59,6 +64,50 @@ impl AddressObject { prop.value.as_ref() } + pub fn get_anniversary_object(&self) -> Result, Error> { + Ok(if let Some(anniversary) = self.get_anniversary() { + let fullname = if let Some(name) = self.get_full_name() { + name + } else { + return Ok(None); + }; + let anniversary = anniversary.date(); + let year = anniversary.year(); + let anniversary_start = anniversary.format(LOCAL_DATE); + let anniversary_end = anniversary + .succ_opt() + .unwrap_or(anniversary) + .format(LOCAL_DATE); + let uid = format!("{}-anniversary", self.get_id()); + + Some(CalendarObject::from_ics( + uid.clone(), + format!( + r#"BEGIN:VCALENDAR +VERSION:2.0 +CALSCALE:GREGORIAN +PRODID:-//github.com/lennart-k/rustical birthday calendar//EN +BEGIN:VEVENT +DTSTART;VALUE=DATE:{anniversary_start} +DTEND;VALUE=DATE:{anniversary_end} +UID:{uid} +RRULE:FREQ=YEARLY +SUMMARY:💍 {fullname} ({year}) +TRANSP:TRANSPARENT +BEGIN:VALARM +TRIGGER;VALUE=DURATION:-PT0M +ACTION:DISPLAY +DESCRIPTION:💍 {fullname} ({year}) +END:VALARM +END:VEVENT +END:VCALENDAR"#, + ), + )?) + } else { + None + }) + } + pub fn get_birthday_object(&self) -> Result, Error> { Ok(if let Some(birthday) = self.get_birthday() { let fullname = if let Some(name) = self.get_full_name() { @@ -70,8 +119,10 @@ impl AddressObject { let year = birthday.year(); let birthday_start = birthday.format(LOCAL_DATE); let birthday_end = birthday.succ_opt().unwrap_or(birthday).format(LOCAL_DATE); + let uid = format!("{}-birthday", self.get_id()); + Some(CalendarObject::from_ics( - self.get_id().to_owned(), + uid.clone(), format!( r#"BEGIN:VCALENDAR VERSION:2.0 @@ -91,11 +142,22 @@ DESCRIPTION:🎂 {fullname} ({year}) END:VALARM END:VEVENT END:VCALENDAR"#, - uid = self.get_id(), ), )?) } else { None }) } + + /// Get significant dates associated with this address object + pub fn get_significant_dates(&self) -> Result, Error> { + let mut out = HashMap::new(); + if let Some(birthday) = self.get_birthday_object()? { + out.insert("birthday", birthday); + } + if let Some(anniversary) = self.get_anniversary_object()? { + out.insert("anniversary", anniversary); + } + Ok(out) + } } diff --git a/crates/store/src/contact_birthday_store.rs b/crates/store/src/contact_birthday_store.rs index 70025ba..9c16545 100644 --- a/crates/store/src/contact_birthday_store.rs +++ b/crates/store/src/contact_birthday_store.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{collections::HashMap, sync::Arc}; use crate::{ calendar::CalendarObjectType, AddressObject, Addressbook, AddressbookStore, Calendar, @@ -99,14 +99,17 @@ impl CalendarStore for ContactBirthdayStore { principal: &str, cal_id: &str, ) -> Result, Error> { - let objects: Result>, Error> = self + let objects: Result>, Error> = self .0 .get_objects(principal, cal_id) .await? .iter() - .map(AddressObject::get_birthday_object) + .map(AddressObject::get_significant_dates) + .collect(); + let objects = objects? + .into_iter() + .flat_map(HashMap::into_values) .collect(); - let objects = objects?.into_iter().flatten().collect(); Ok(objects) } @@ -117,12 +120,14 @@ impl CalendarStore for ContactBirthdayStore { cal_id: &str, object_id: &str, ) -> Result { - Ok(self - .0 - .get_object(principal, cal_id, object_id) + let (addressobject_id, date_type) = object_id.rsplit_once("-").ok_or(Error::NotFound)?; + dbg!(addressobject_id, date_type); + self.0 + .get_object(principal, cal_id, addressobject_id) .await? - .get_birthday_object()? - .ok_or(Error::NotFound)?) + .get_significant_dates()? + .remove(date_type) + .ok_or(Error::NotFound) } async fn put_object(