aboutsummaryrefslogtreecommitdiff
path: root/src/gui/form
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/form')
-rw-r--r--src/gui/form/mod.rs115
-rw-r--r--src/gui/form/repetition.rs40
-rw-r--r--src/gui/form/utils.rs21
3 files changed, 137 insertions, 39 deletions
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<NaiveTime>) -> 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<Event> {
+ 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)
}
diff --git a/src/gui/form/repetition.rs b/src/gui/form/repetition.rs
index 1d36765..4da65ac 100644
--- a/src/gui/form/repetition.rs
+++ b/src/gui/form/repetition.rs
@@ -1,13 +1,15 @@
use gtk4 as gtk;
-use chrono::{Weekday, Weekday::*};
+use chrono::{NaiveDate, Weekday, Weekday::*};
use gtk::prelude::*;
use std::collections::HashSet;
+use crate::gui::form::utils;
use crate::model::{
- repetition,
+ event, repetition,
repetition::{DayOfMonth, Frequency, Repetition},
};
+use crate::validation;
static WEEKDAYS_STR: [&str; 7] = [
"Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche",
@@ -25,6 +27,7 @@ pub struct Model {
first_day_radio: gtk::CheckButton,
first_day_dropdown: gtk::DropDown,
yearly_radio: gtk::CheckButton,
+ until: gtk::Entry,
}
pub fn view(repetition: Option<&Repetition>) -> Model {
@@ -52,7 +55,7 @@ pub fn view(repetition: Option<&Repetition>) -> Model {
&no_radio,
!default.is_empty(),
&day_interval_entry,
- "Interval de jours",
+ "Intervalle de jours",
);
view.append(&day_interval_box);
@@ -84,9 +87,18 @@ pub fn view(repetition: Option<&Repetition>) -> Model {
.group(&no_radio)
.label("Annuel")
.active(frequency == Some(Frequency::Yearly))
+ .margin_bottom(10)
.build();
view.append(&yearly_radio);
+ let until = repetition
+ .as_ref()
+ .and_then(|r| r.until)
+ .map(|u| utils::entry(&u.format(event::DATE_FORMAT).to_string()))
+ .unwrap_or_default();
+ view.append(&utils::label("Répéter jusqu’au"));
+ view.append(&until);
+
Model {
view,
no_radio,
@@ -97,6 +109,7 @@ pub fn view(repetition: Option<&Repetition>) -> Model {
first_day_radio,
first_day_dropdown,
yearly_radio,
+ until,
}
}
@@ -126,7 +139,10 @@ fn label(text: &str) -> gtk::Label {
.build()
}
-pub fn validate(model: &Model) -> Result<Option<Repetition>, String> {
+pub fn validate(
+ model: &Model,
+ removed_occurences: HashSet<usize>,
+) -> Result<Option<Repetition>, String> {
let frequency = if model.no_radio.is_active() {
Ok(None)
} else if model.day_interval_radio.is_active() {
@@ -148,8 +164,22 @@ pub fn validate(model: &Model) -> Result<Option<Repetition>, String> {
Err("Aucune option n’a été sélectionnée".to_string())
}?;
+ // Check until
+ let until = (if frequency.is_some() {
+ match validation::non_empty(model.until.buffer().text()) {
+ Some(until) => match NaiveDate::parse_from_str(&until, event::DATE_FORMAT) {
+ Ok(until) => Ok(Some(until)),
+ Err(_) => Err(format!("Can’t parse date from {}", until)),
+ },
+ None => Ok(None),
+ }
+ } else {
+ Ok(None)
+ })?;
+
Ok(frequency.map(|frequency| Repetition {
frequency,
- removed_occurences: HashSet::new(),
+ removed_occurences,
+ until,
}))
}
diff --git a/src/gui/form/utils.rs b/src/gui/form/utils.rs
new file mode 100644
index 0000000..5cf59e3
--- /dev/null
+++ b/src/gui/form/utils.rs
@@ -0,0 +1,21 @@
+use gtk4 as gtk;
+
+use chrono::NaiveTime;
+
+use crate::model::time;
+
+pub fn time_entry(time: Option<NaiveTime>) -> gtk::Entry {
+ entry(&time.map(time::pprint).unwrap_or_else(|| "".to_string()))
+}
+
+pub fn entry(text: &str) -> gtk::Entry {
+ gtk::Entry::builder().text(text).margin_bottom(10).build()
+}
+
+pub fn label(text: &str) -> gtk::Label {
+ gtk::Label::builder()
+ .label(text)
+ .halign(gtk::Align::Start)
+ .margin_bottom(5)
+ .build()
+}