From 35cc74578e969bae4812afd2ff041eba3746142d Mon Sep 17 00:00:00 2001 From: Joris Date: Sat, 19 Mar 2022 22:03:58 +0100 Subject: Allow to repeat an event until a specific date Also provide a shortcut to modify a repetead event from a specific occurence. This set the end repetition date under the hood and create a new repeated event. --- src/gui/form/mod.rs | 115 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 81 insertions(+), 34 deletions(-) (limited to 'src/gui/form/mod.rs') diff --git a/src/gui/form/mod.rs b/src/gui/form/mod.rs index 68e6539..bb43ef5 100644 --- a/src/gui/form/mod.rs +++ b/src/gui/form/mod.rs @@ -1,22 +1,24 @@ mod repetition; +mod utils; use gtk4 as gtk; use anyhow::Result; -use chrono::{NaiveDate, NaiveTime}; +use chrono::{Duration, NaiveDate}; use gtk::glib; use gtk::prelude::*; use rusqlite::Connection; +use std::collections::HashSet; use thiserror::Error; use uuid::Uuid; use crate::{ db, gui::{update, update::Msg, App}, - model::{event, event::Event}, + model::{event, event::Event, repetition::Repetition}, }; -pub async fn repetition_dialog(app: &App, date: NaiveDate, event: Event) { +pub async fn repetition_dialog(app: &App, date: NaiveDate, event: &Event) { let dialog = gtk::Dialog::builder() .transient_for(&*app.window) .modal(true) @@ -38,17 +40,28 @@ pub async fn repetition_dialog(app: &App, date: NaiveDate, event: Event) { lines.append(&button); let tx = app.tx.clone(); button.connect_clicked(glib::clone!(@weak dialog, @strong event => move |_| { - update::send(tx.clone(), Msg::ShowUpdateRepetitionForm { date, event: event.clone() }); + update::send(tx.clone(), Msg::ShowUpdateRepetitionForm { date, event_id: event.id }); dialog.close() })); let button = gtk::Button::builder() .label("Toutes les occurences") + .margin_bottom(10) .build(); lines.append(&button); let tx = app.tx.clone(); button.connect_clicked(glib::clone!(@weak dialog, @strong event => move |_| { - update::send(tx.clone(), Msg::ShowUpdateForm { event: event.clone() }); + update::send(tx.clone(), Msg::ShowUpdateForm { event_id: event.id }); + dialog.close() + })); + + let button = gtk::Button::builder() + .label("À partir de cette occurence") + .build(); + lines.append(&button); + let tx = app.tx.clone(); + button.connect_clicked(glib::clone!(@weak dialog, @strong event => move |_| { + update::send(tx.clone(), Msg::ShowUpdateFromOccurence { date, event_id: event.id }); dialog.close() })); @@ -60,6 +73,7 @@ pub enum Target { New { date: NaiveDate }, Update { event: Event }, UpdateRepetition { event: Event, date: NaiveDate }, + UpdateFromOccurence { event: Event, date: NaiveDate }, } pub async fn show(app: &App, target: Target) { @@ -67,6 +81,7 @@ pub async fn show(app: &App, target: Target) { Target::New { .. } => None, Target::Update { ref event } => Some(event.clone()), Target::UpdateRepetition { ref event, .. } => Some(event.clone()), + Target::UpdateFromOccurence { ref event, .. } => Some(event.clone()), }; let title = if event.is_some() { @@ -91,6 +106,7 @@ pub async fn show(app: &App, target: Target) { let columns = gtk::Box::builder() .orientation(gtk::Orientation::Horizontal) + .spacing(10) .build(); lines.append(&columns); @@ -101,37 +117,42 @@ pub async fn show(app: &App, target: Target) { .build(); columns.append(&column1); - let name = event.as_ref().map(|e| entry(&e.name)).unwrap_or_default(); - column1.append(&label("Événement")); + let name = event + .as_ref() + .map(|e| utils::entry(&e.name)) + .unwrap_or_default(); + column1.append(&utils::label("Événement")); column1.append(&name); let date = match target { Target::New { date } => date, Target::Update { ref event } => event.date, Target::UpdateRepetition { date, .. } => date, + Target::UpdateFromOccurence { date, .. } => date, }; - let date = entry(&date.format(event::DATE_FORMAT).to_string()); - column1.append(&label("Jour")); + let date = utils::entry(&date.format(event::DATE_FORMAT).to_string()); + column1.append(&utils::label("Jour")); column1.append(&date); let start = event .as_ref() - .map(|e| time_entry(e.start)) - .unwrap_or_else(|| entry("")); - column1.append(&label("Début")); + .map(|e| utils::time_entry(e.start)) + .unwrap_or_else(|| utils::entry("")); + column1.append(&utils::label("Début")); column1.append(&start); let end = event .as_ref() - .map(|e| time_entry(e.end)) - .unwrap_or_else(|| entry("")); - column1.append(&label("Fin")); + .map(|e| utils::time_entry(e.end)) + .unwrap_or_else(|| utils::entry("")); + column1.append(&utils::label("Fin")); column1.append(&end); // Second column let repetition = match target { Target::Update { ref event } => event.repetition.as_ref(), + Target::UpdateFromOccurence { ref event, .. } => event.repetition.as_ref(), _ => None, }; let repetition_model = repetition::view(repetition); @@ -143,6 +164,7 @@ pub async fn show(app: &App, target: Target) { Target::New { .. } => "Créer", Target::Update { .. } => "Modifier", Target::UpdateRepetition { .. } => "Modifier l’occurence", + Target::UpdateFromOccurence { .. } => "Modifier à partir de l’occurence", }; let button = gtk::Button::builder() @@ -153,7 +175,13 @@ pub async fn show(app: &App, target: Target) { let conn = app.conn.clone(); let tx = app.tx.clone(); button.connect_clicked(glib::clone!(@weak dialog, @strong target, @strong event => move |_| { - match repetition::validate(&repetition_model) { + let removed_occurences = match &target { + Target::Update { event, .. } => { + event.repetition.as_ref().map(|r| r.removed_occurences.clone()).unwrap_or_default() + }, + _ => HashSet::new(), + }; + match repetition::validate(&repetition_model, removed_occurences) { Ok(repetition) => { let id = match &target { Target::Update {event} => event.id, @@ -200,6 +228,24 @@ pub async fn show(app: &App, target: Target) { Err(err) => eprintln!("Error when updating repetition: {}", err) } } + Target::UpdateFromOccurence { date, event } => { + match update_repetition_until(&conn, *date - Duration::days(1), event) { + Ok(updated) => { + match db::insert(&conn, &new) { + Ok(_) => { + update::send(tx.clone(), Msg::UpdateRepeatedFrom { + old: event.clone(), + updated, + new + }); + dialog.close() + }, + Err(err) => eprintln!("Error when inserting event: {}", err) + } + }, + Err(err) => eprintln!("Error when updating event: {}", err) + } + } } } None => eprintln!("Event is not valid.") @@ -212,6 +258,7 @@ pub async fn show(app: &App, target: Target) { if let Some(event) = event { let label = match target { Target::Update { .. } => "Supprimer", + Target::UpdateFromOccurence { .. } => "Supprimer à partir de l’occurence", _ => "Supprimer l’occurence", }; let button = gtk::Button::builder().label(label).build(); @@ -231,6 +278,15 @@ pub async fn show(app: &App, target: Target) { } } } + Target::UpdateFromOccurence { date, .. } => { + match update_repetition_until(&conn, date - Duration::days(1), &event) { + Ok(updated) => { + update::send(tx.clone(), Msg::UpdateEvent { old: event.clone(), new: updated }); + dialog.close() + }, + Err(err) => eprintln!("Error when updating event: {}", err) + } + } _ => { let operation = db::delete(&conn, &event.id); if operation.is_ok() { @@ -273,22 +329,13 @@ fn delete_repetition_occurence( } } -fn time_entry(time: Option) -> gtk::Entry { - entry( - &time - .map(event::pprint_time) - .unwrap_or_else(|| "".to_string()), - ) -} - -fn entry(text: &str) -> gtk::Entry { - gtk::Entry::builder().text(text).margin_bottom(10).build() -} - -fn label(text: &str) -> gtk::Label { - gtk::Label::builder() - .label(text) - .halign(gtk::Align::Start) - .margin_bottom(5) - .build() +fn update_repetition_until(conn: &Connection, date: NaiveDate, event: &Event) -> Result { + let mut with_repetition_until = event.clone(); + with_repetition_until.repetition = event.repetition.as_ref().map(|r| Repetition { + frequency: r.frequency.clone(), + removed_occurences: r.removed_occurences.clone(), + until: Some(date), + }); + db::update(conn, &with_repetition_until)?; + Ok(with_repetition_until) } -- cgit v1.2.3