Added support for calendar colors

This commit is contained in:
daladim 2021-07-08 15:57:45 +02:00
parent 2b339a7aff
commit 17716575d8
11 changed files with 198 additions and 27 deletions

View file

@ -9,6 +9,7 @@ use std::ffi::OsStr;
use serde::{Deserialize, Serialize};
use async_trait::async_trait;
use csscolorparser::Color;
use crate::traits::CalDavSource;
use crate::traits::BaseCalendar;
@ -208,12 +209,12 @@ impl CalDavSource<CachedCalendar> for Cache {
self.get_calendar_sync(id)
}
async fn create_calendar(&mut self, id: CalendarId, name: String, supported_components: SupportedComponents) -> Result<Arc<Mutex<CachedCalendar>>, Box<dyn Error>> {
async fn create_calendar(&mut self, id: CalendarId, name: String, supported_components: SupportedComponents, color: Option<Color>) -> Result<Arc<Mutex<CachedCalendar>>, Box<dyn Error>> {
log::debug!("Inserting local calendar {}", id);
#[cfg(feature = "local_calendar_mocks_remote_calendars")]
self.mock_behaviour.as_ref().map_or(Ok(()), |b| b.lock().unwrap().can_create_calendar())?;
let new_calendar = CachedCalendar::new(name, id.clone(), supported_components);
let new_calendar = CachedCalendar::new(name, id.clone(), supported_components, color);
let arc = Arc::new(Mutex::new(new_calendar));
#[cfg(feature = "local_calendar_mocks_remote_calendars")]
@ -244,12 +245,14 @@ mod tests {
Url::parse("https://caldav.com/shopping").unwrap(),
"My shopping list".to_string(),
SupportedComponents::TODO,
Some(csscolorparser::parse("lime").unwrap()),
).await.unwrap();
let bucket_list = cache.create_calendar(
Url::parse("https://caldav.com/bucket-list").unwrap(),
"My bucket list".to_string(),
SupportedComponents::TODO,
Some(csscolorparser::parse("#ff8000").unwrap()),
).await.unwrap();
{
@ -288,11 +291,12 @@ mod tests {
let cache_path = PathBuf::from(String::from("test_cache/sanity_tests"));
let mut cache = populate_cache(&cache_path).await;
// We should not be able to add twice the same calendar
// We should not be able to add a second calendar with the same id
let second_addition_same_calendar = cache.create_calendar(
Url::parse("https://caldav.com/shopping").unwrap(),
"My shopping list".to_string(),
SupportedComponents::TODO,
None,
).await;
assert!(second_addition_same_calendar.is_err());
}

View file

@ -3,6 +3,7 @@ use std::error::Error;
use serde::{Deserialize, Serialize};
use async_trait::async_trait;
use csscolorparser::Color;
use crate::item::SyncStatus;
use crate::traits::{BaseCalendar, CompleteCalendar};
@ -24,6 +25,7 @@ pub struct CachedCalendar {
name: String,
id: CalendarId,
supported_components: SupportedComponents,
color: Option<Color>,
#[cfg(feature = "local_calendar_mocks_remote_calendars")]
#[serde(skip)]
mock_behaviour: Option<Arc<Mutex<MockBehaviour>>>,
@ -85,7 +87,9 @@ impl CachedCalendar {
pub async fn has_same_observable_content_as(&self, other: &CachedCalendar) -> Result<bool, Box<dyn Error>> {
if self.name != other.name
|| self.id != other.id
|| self.supported_components != other.supported_components {
|| self.supported_components != other.supported_components
|| self.color != other.color
{
log::debug!("Calendar properties mismatch");
return Ok(false);
}
@ -156,6 +160,10 @@ impl BaseCalendar for CachedCalendar {
self.supported_components
}
fn color(&self) -> Option<&Color> {
self.color.as_ref()
}
async fn add_item(&mut self, item: Item) -> Result<SyncStatus, Box<dyn Error>> {
if self.items.contains_key(item.id()) {
return Err(format!("Item {:?} cannot be added, it exists already", item.id()).into());
@ -181,9 +189,9 @@ impl BaseCalendar for CachedCalendar {
#[async_trait]
impl CompleteCalendar for CachedCalendar {
fn new(name: String, id: CalendarId, supported_components: SupportedComponents) -> Self {
fn new(name: String, id: CalendarId, supported_components: SupportedComponents, color: Option<Color>) -> Self {
Self {
name, id, supported_components,
name, id, supported_components, color,
#[cfg(feature = "local_calendar_mocks_remote_calendars")]
mock_behaviour: None,
items: HashMap::new(),
@ -253,8 +261,8 @@ use crate::{item::VersionTag,
#[cfg(feature = "local_calendar_mocks_remote_calendars")]
#[async_trait]
impl DavCalendar for CachedCalendar {
fn new(name: String, resource: Resource, supported_components: SupportedComponents) -> Self {
crate::traits::CompleteCalendar::new(name, resource.url().clone(), supported_components)
fn new(name: String, resource: Resource, supported_components: SupportedComponents, color: Option<Color>) -> Self {
crate::traits::CompleteCalendar::new(name, resource.url().clone(), supported_components, color)
}
async fn get_item_version_tags(&self) -> Result<HashMap<ItemId, VersionTag>, Box<dyn Error>> {

View file

@ -4,6 +4,7 @@ use std::sync::Mutex;
use async_trait::async_trait;
use reqwest::{header::CONTENT_TYPE, header::CONTENT_LENGTH};
use csscolorparser::Color;
use crate::traits::BaseCalendar;
use crate::traits::DavCalendar;
@ -37,6 +38,7 @@ pub struct RemoteCalendar {
name: String,
resource: Resource,
supported_components: SupportedComponents,
color: Option<Color>,
cached_version_tags: Mutex<Option<HashMap<ItemId, VersionTag>>>,
}
@ -48,6 +50,9 @@ impl BaseCalendar for RemoteCalendar {
fn supported_components(&self) -> crate::calendar::SupportedComponents {
self.supported_components
}
fn color(&self) -> Option<&Color> {
self.color.as_ref()
}
async fn add_item(&mut self, item: Item) -> Result<SyncStatus, Box<dyn Error>> {
let ical_text = crate::ical::build_from(&item)?;
@ -114,9 +119,9 @@ impl BaseCalendar for RemoteCalendar {
#[async_trait]
impl DavCalendar for RemoteCalendar {
fn new(name: String, resource: Resource, supported_components: SupportedComponents) -> Self {
fn new(name: String, resource: Resource, supported_components: SupportedComponents, color: Option<Color>) -> Self {
Self {
name, resource, supported_components,
name, resource, supported_components, color,
cached_version_tags: Mutex::new(None),
}
}

View file

@ -10,6 +10,7 @@ use reqwest::{Method, StatusCode};
use reqwest::header::CONTENT_TYPE;
use minidom::Element;
use url::Url;
use csscolorparser::Color;
use crate::resource::Resource;
use crate::utils::{find_elem, find_elems};
@ -42,6 +43,7 @@ static CAL_BODY: &str = r#"
<d:propfind xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav" >
<d:prop>
<d:displayname />
<E:calendar-color xmlns:E="http://apple.com/ns/ical/"/>
<d:resourcetype />
<c:supported-calendar-component-set />
</d:prop>
@ -205,7 +207,14 @@ impl Client {
},
Ok(sc) => sc,
};
let this_calendar = RemoteCalendar::new(display_name, this_calendar_url, supported_components);
let this_calendar_color = find_elem(&rep, "calendar-color")
.and_then(|col| {
col.texts().next()
.and_then(|t| csscolorparser::parse(t).ok())
});
let this_calendar = RemoteCalendar::new(display_name, this_calendar_url, supported_components, this_calendar_color);
log::info!("Found calendar {}", this_calendar.name());
calendars.insert(this_calendar.id().clone(), Arc::new(Mutex::new(this_calendar)));
}
@ -243,7 +252,7 @@ impl CalDavSource<RemoteCalendar> for Client {
.map(|cal| cal.clone())
}
async fn create_calendar(&mut self, id: CalendarId, name: String, supported_components: SupportedComponents) -> Result<Arc<Mutex<RemoteCalendar>>, Box<dyn Error>> {
async fn create_calendar(&mut self, id: CalendarId, name: String, supported_components: SupportedComponents, color: Option<Color>) -> Result<Arc<Mutex<RemoteCalendar>>, Box<dyn Error>> {
self.populate_calendars().await?;
match self.cached_replies.lock().unwrap().calendars.as_ref() {

View file

@ -390,10 +390,12 @@ where
let src = needle.lock().unwrap();
let name = src.name().to_string();
let supported_comps = src.supported_components();
let color = src.color();
if let Err(err) = haystack.create_calendar(
cal_id.clone(),
name,
supported_comps,
color.cloned(),
).await{
return Err(err);
}

View file

@ -3,6 +3,7 @@ use std::collections::{HashMap, HashSet};
use std::sync::{Arc, Mutex};
use async_trait::async_trait;
use csscolorparser::Color;
use crate::item::SyncStatus;
use crate::item::Item;
@ -21,7 +22,7 @@ pub trait CalDavSource<T: BaseCalendar> {
/// Returns the calendar matching the ID
async fn get_calendar(&self, id: &CalendarId) -> Option<Arc<Mutex<T>>>;
/// Create a calendar if it did not exist, and return it
async fn create_calendar(&mut self, id: CalendarId, name: String, supported_components: SupportedComponents)
async fn create_calendar(&mut self, id: CalendarId, name: String, supported_components: SupportedComponents, color: Option<Color>)
-> Result<Arc<Mutex<T>>, Box<dyn Error>>;
// Removing a calendar is not supported yet
@ -39,6 +40,9 @@ pub trait BaseCalendar {
/// Returns the supported kinds of components for this calendar
fn supported_components(&self) -> crate::calendar::SupportedComponents;
/// Returns the user-defined color of this calendar
fn color(&self) -> Option<&Color>;
/// Add an item into this calendar, and return its new sync status.
/// For local calendars, the sync status is not modified.
/// For remote calendars, the sync status is updated by the server
@ -64,7 +68,7 @@ pub trait BaseCalendar {
#[async_trait]
pub trait DavCalendar : BaseCalendar {
/// Create a new calendar
fn new(name: String, resource: Resource, supported_components: SupportedComponents) -> Self;
fn new(name: String, resource: Resource, supported_components: SupportedComponents, color: Option<Color>) -> Self;
/// Get the IDs and the version tags of every item in this calendar
async fn get_item_version_tags(&self) -> Result<HashMap<ItemId, VersionTag>, Box<dyn Error>>;
@ -94,7 +98,7 @@ pub trait DavCalendar : BaseCalendar {
#[async_trait]
pub trait CompleteCalendar : BaseCalendar {
/// Create a new calendar
fn new(name: String, id: CalendarId, supported_components: SupportedComponents) -> Self;
fn new(name: String, id: CalendarId, supported_components: SupportedComponents, color: Option<Color>) -> Self;
/// Get the IDs of all current items in this calendar
async fn get_item_ids(&self) -> Result<HashSet<ItemId>, Box<dyn Error>>;