aboutsummaryrefslogtreecommitdiff
path: root/src/gui/form/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/form/mod.rs')
-rw-r--r--src/gui/form/mod.rs238
1 files changed, 201 insertions, 37 deletions
diff --git a/src/gui/form/mod.rs b/src/gui/form/mod.rs
index 57ccac7..68e6539 100644
--- a/src/gui/form/mod.rs
+++ b/src/gui/form/mod.rs
@@ -2,8 +2,13 @@ mod repetition;
use gtk4 as gtk;
+use anyhow::Result;
+use chrono::{NaiveDate, NaiveTime};
use gtk::glib;
use gtk::prelude::*;
+use rusqlite::Connection;
+use thiserror::Error;
+use uuid::Uuid;
use crate::{
db,
@@ -11,12 +16,70 @@ use crate::{
model::{event, event::Event},
};
-pub async fn show(app: &App, event: Event, is_new: bool) {
+pub async fn repetition_dialog(app: &App, date: NaiveDate, event: Event) {
let dialog = gtk::Dialog::builder()
.transient_for(&*app.window)
.modal(true)
- .title(if is_new { "Ajouter" } else { "Modifier" })
- .css_classes(vec!["g-Form".to_string()])
+ .title("Modifier")
+ .css_classes(vec!["g-Dialog".to_string()])
+ .build();
+
+ let content_area = dialog.content_area();
+
+ let lines = gtk::Box::builder()
+ .orientation(gtk::Orientation::Vertical)
+ .build();
+ content_area.append(&lines);
+
+ let button = gtk::Button::builder()
+ .label("Cette occurence")
+ .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::ShowUpdateRepetitionForm { date, event: event.clone() });
+ dialog.close()
+ }));
+
+ let button = gtk::Button::builder()
+ .label("Toutes les occurences")
+ .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() });
+ dialog.close()
+ }));
+
+ dialog.run_future().await;
+}
+
+#[derive(Clone)]
+pub enum Target {
+ New { date: NaiveDate },
+ Update { event: Event },
+ UpdateRepetition { event: Event, date: NaiveDate },
+}
+
+pub async fn show(app: &App, target: Target) {
+ let event = match target {
+ Target::New { .. } => None,
+ Target::Update { ref event } => Some(event.clone()),
+ Target::UpdateRepetition { ref event, .. } => Some(event.clone()),
+ };
+
+ let title = if event.is_some() {
+ "Modifier"
+ } else {
+ "Ajouter"
+ };
+
+ let dialog = gtk::Dialog::builder()
+ .transient_for(&*app.window)
+ .modal(true)
+ .title(title)
+ .css_classes(vec!["g-Dialog".to_string()])
.build();
let content_area = dialog.content_area();
@@ -29,7 +92,6 @@ pub async fn show(app: &App, event: Event, is_new: bool) {
let columns = gtk::Box::builder()
.orientation(gtk::Orientation::Horizontal)
.build();
- columns.add_css_class("g-Form__Columns");
lines.append(&columns);
// First column
@@ -37,79 +99,145 @@ pub async fn show(app: &App, event: Event, is_new: bool) {
let column1 = gtk::Box::builder()
.orientation(gtk::Orientation::Vertical)
.build();
- column1.add_css_class("g-Form__Inputs");
columns.append(&column1);
- let name = entry(&event.name);
+ let name = event.as_ref().map(|e| entry(&e.name)).unwrap_or_default();
column1.append(&label("Événement"));
column1.append(&name);
- let date = entry(&event.date.format(event::DATE_FORMAT).to_string());
+ let date = match target {
+ Target::New { date } => date,
+ Target::Update { ref event } => event.date,
+ Target::UpdateRepetition { date, .. } => date,
+ };
+ let date = entry(&date.format(event::DATE_FORMAT).to_string());
column1.append(&label("Jour"));
column1.append(&date);
- let start = entry(
- &event
- .start
- .map(event::pprint_time)
- .unwrap_or_else(|| "".to_string()),
- );
+ let start = event
+ .as_ref()
+ .map(|e| time_entry(e.start))
+ .unwrap_or_else(|| entry(""));
column1.append(&label("Début"));
column1.append(&start);
- let end = entry(
- &event
- .end
- .map(event::pprint_time)
- .unwrap_or_else(|| "".to_string()),
- );
+ let end = event
+ .as_ref()
+ .map(|e| time_entry(e.end))
+ .unwrap_or_else(|| entry(""));
column1.append(&label("Fin"));
column1.append(&end);
// Second column
- let repetition_model = repetition::view(&event);
+ let repetition = match target {
+ Target::Update { ref event } => event.repetition.as_ref(),
+ _ => None,
+ };
+ let repetition_model = repetition::view(repetition);
columns.append(&repetition_model.view);
// Buttons
+ let button_title = match target {
+ Target::New { .. } => "Créer",
+ Target::Update { .. } => "Modifier",
+ Target::UpdateRepetition { .. } => "Modifier l’occurence",
+ };
+
let button = gtk::Button::builder()
- .label(if is_new { "Créer" } else { "Modifier" })
+ .label(button_title)
.margin_bottom(10)
.build();
lines.append(&button);
let conn = app.conn.clone();
let tx = app.tx.clone();
- button.connect_clicked(glib::clone!(@weak dialog, @strong event => move |_| {
+ button.connect_clicked(glib::clone!(@weak dialog, @strong target, @strong event => move |_| {
match repetition::validate(&repetition_model) {
Ok(repetition) => {
- match event::validate(event.id, date.buffer().text(), name.buffer().text(), start.buffer().text(), end.buffer().text(), repetition) {
+ let id = match &target {
+ Target::Update {event} => event.id,
+ _ => Uuid::new_v4(),
+ };
+ match event::validate(id, date.buffer().text(), name.buffer().text(), start.buffer().text(), end.buffer().text(), repetition) {
Some(new) => {
- match if is_new { db::insert(&conn, &new) } else { db::update(&conn, &new) } {
- Ok(_) => {
- let msg = if is_new { Msg::AddEvent { new } } else { Msg::UpdateEvent { old: event.clone(), new } };
- update::send(tx.clone(), msg);
- dialog.close()
- },
- Err(err) => eprintln!("Error when upserting event: {err}")
+ match &target {
+ Target::New {..} => {
+ match db::insert(&conn, &new) {
+ Ok(_) => {
+ update::send(tx.clone(), Msg::AddEvent { new });
+ dialog.close()
+ },
+ Err(err) => eprintln!("Error when inserting event: {}", err)
+ }
+ }
+ Target::Update {event} => {
+ match db::update(&conn, &new) {
+ Ok(_) => {
+ update::send(tx.clone(), Msg::UpdateEvent { old: event.clone(), new });
+ dialog.close()
+ },
+ Err(err) => eprintln!("Error when updating event: {}", err)
+ }
+ }
+ Target::UpdateRepetition { event, date } => {
+ // TODO: improve intermediate error state
+ match delete_repetition_occurence(&conn, event, *date) {
+ Ok(occurence) => {
+ match db::insert(&conn, &new) {
+ Ok(_) => {
+ update::send(tx.clone(), Msg::UpdateEventOccurence {
+ event: event.clone(),
+ occurence,
+ date: *date,
+ new
+ })
+ }
+ Err(err) => eprintln!("Error when updating repetition: {}", err)
+ };
+ dialog.close()
+ },
+ Err(err) => eprintln!("Error when updating repetition: {}", err)
+ }
+ }
}
}
- None => eprintln!("Event is not valid: {event:?}")
+ None => eprintln!("Event is not valid.")
}
},
- Err(message) => eprintln!("{message}")
+ Err(message) => eprintln!("{}", message)
}
}));
- if !is_new {
- let button = gtk::Button::builder().label("Supprimer").build();
+ if let Some(event) = event {
+ let label = match target {
+ Target::Update { .. } => "Supprimer",
+ _ => "Supprimer l’occurence",
+ };
+ let button = gtk::Button::builder().label(label).build();
lines.append(&button);
let conn = app.conn.clone();
let tx = app.tx.clone();
button.connect_clicked(glib::clone!(@weak dialog => move |_| {
- if db::delete(&conn, &event.id).is_ok() {
- update::send(tx.clone(), Msg::DeleteEvent { event: event.clone() });
- dialog.close()
+ match target {
+ Target::UpdateRepetition { date, .. } => {
+ match delete_repetition_occurence(&conn, &event, date) {
+ Ok(occurence) => {
+ update::send(tx.clone(), Msg::DeleteOccurence { event: event.clone(), date, occurence });
+ dialog.close()
+ }
+ Err(err) => {
+ eprintln!("{:?}", err);
+ }
+ }
+ }
+ _ => {
+ let operation = db::delete(&conn, &event.id);
+ if operation.is_ok() {
+ update::send(tx.clone(), Msg::DeleteEvent { event: event.clone() });
+ dialog.close()
+ }
+ }
}
}));
}
@@ -117,6 +245,42 @@ pub async fn show(app: &App, event: Event, is_new: bool) {
dialog.run_future().await;
}
+#[derive(Error, Debug)]
+enum DeleteError {
+ #[error("Repetition not found")]
+ RepetitionNotFound,
+ #[error("Occurence not found")]
+ OccurenceNotFound,
+}
+
+fn delete_repetition_occurence(
+ conn: &Connection,
+ event: &Event,
+ occurence_date: NaiveDate,
+) -> Result<usize> {
+ if let Some(ref repetition) = event.repetition {
+ if let Some(occurence) = repetition.occurence_index(event.date, occurence_date) {
+ let mut event = event.clone();
+ let mut repetition = repetition.clone();
+ repetition.removed_occurences.insert(occurence);
+ event.repetition = Some(repetition);
+ db::update(conn, &event).map(|_| occurence)
+ } else {
+ Err(anyhow::Error::new(DeleteError::OccurenceNotFound))
+ }
+ } else {
+ Err(anyhow::Error::new(DeleteError::RepetitionNotFound))
+ }
+}
+
+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()
}