From d0a9063631e71928484a698f6c585ebb3915e8a2 Mon Sep 17 00:00:00 2001 From: Joris Date: Sun, 9 Jan 2022 14:37:35 +0100 Subject: Allow to update events --- src/app/app.rs | 2 ++ src/app/calendar.rs | 19 +++++++++++-------- src/app/form.rs | 46 ++++++++++++++++++++++++---------------------- src/app/mod.rs | 2 +- src/app/update.rs | 35 ++++++++++++++++++++++++++--------- src/db/mod.rs | 10 ++++++++++ src/model/event.rs | 12 +++++++++++- 7 files changed, 85 insertions(+), 41 deletions(-) diff --git a/src/app/app.rs b/src/app/app.rs index d93b544..b1ee395 100644 --- a/src/app/app.rs +++ b/src/app/app.rs @@ -12,6 +12,7 @@ use crate::app::update::Msg; use crate::{db, model::event::Event}; pub struct App { + pub conn: Rc, pub window: Rc, pub grid: gtk::Grid, pub events: Vec, @@ -48,6 +49,7 @@ impl App { }); Self { + conn, window, grid, events, diff --git a/src/app/calendar.rs b/src/app/calendar.rs index 250101f..fa4ebe6 100644 --- a/src/app/calendar.rs +++ b/src/app/calendar.rs @@ -87,7 +87,7 @@ pub fn day_entry( vbox.add_css_class("g-Calendar__Day"); let gesture = gtk::GestureClick::new(); - gesture.connect_pressed(glib::clone!(@strong date => move |_, n, _, _| { + gesture.connect_pressed(glib::clone!(@strong date, @strong tx => move |_, n, _, _| { if n == 2 { update::send(tx.clone(), Msg::ShowAddForm { date }); } @@ -107,7 +107,7 @@ pub fn day_entry( events.sort_by_key(|e| e.start); if !events.is_empty() { - vbox.append(&day_events(events)); + vbox.append(&day_events(tx, events)); } let scrolled_window = gtk::ScrolledWindow::builder() @@ -135,7 +135,7 @@ fn day_label(date: &NaiveDate) -> gtk::Label { label } -fn day_events(events: Vec<&Event>) -> gtk::Box { +fn day_events(tx: Sender, events: Vec<&Event>) -> gtk::Box { let vbox = gtk::Box::builder() .orientation(gtk::Orientation::Vertical) .build(); @@ -147,11 +147,14 @@ fn day_events(events: Vec<&Event>) -> gtk::Box { .build(); let gesture = gtk::GestureClick::new(); - let click_event = event.clone(); - gesture.connect_pressed(move |gesture, _, _, _| { - gesture.set_state(gtk::EventSequenceState::Claimed); - println!("Click: {:?}", click_event); - }); + gesture.connect_pressed( + glib::clone!(@strong event, @strong tx => move |gesture, n, _, _| { + gesture.set_state(gtk::EventSequenceState::Claimed); + if n == 2 { + update::send(tx.clone(), Msg::ShowUpdateForm { event: event.clone() }); + } + }), + ); hbox.add_controller(&gesture); hbox.add_css_class("g-Calendar__DayEvent"); diff --git a/src/app/form.rs b/src/app/form.rs index 08a2af1..6c42cd0 100644 --- a/src/app/form.rs +++ b/src/app/form.rs @@ -1,25 +1,19 @@ use gtk4 as gtk; -use async_channel::Sender; -use chrono::NaiveDate; use gtk::glib; use gtk::prelude::*; -use rusqlite::Connection; -use std::rc::Rc; -use uuid::Uuid; -use crate::{app::update, app::update::Msg, db, model::event}; +use crate::{ + app::{update, update::Msg, App}, + db, + model::{event, event::Event}, +}; -pub async fn show( - conn: Rc, - tx: Sender, - window: Rc, - date: NaiveDate, -) { +pub async fn show(app: &App, event: Event, is_new: bool) { let dialog = gtk::Dialog::builder() - .transient_for(&*window) + .transient_for(&*app.window) .modal(true) - .title("Ajouter") + .title(if is_new { "Ajouter" } else { "Modifier" }) .css_classes(vec!["g-Form".to_string()]) .build(); @@ -31,30 +25,38 @@ pub async fn show( vbox.add_css_class("g-Form__Inputs"); content_area.append(&vbox); - let name = entry(""); + let name = entry(&event.name); vbox.append(&label("Événement")); vbox.append(&name); - let date = entry(&date.format(event::DATE_FORMAT).to_string()); + let date = entry(&event.date.format(event::DATE_FORMAT).to_string()); vbox.append(&label("Jour")); vbox.append(&date); - let start = entry(""); + let start = entry( + &event + .start + .map(event::pprint_time) + .unwrap_or("".to_string()), + ); vbox.append(&label("Début")); vbox.append(&start); - let end = entry(""); + let end = entry(&event.end.map(event::pprint_time).unwrap_or("".to_string())); vbox.append(&label("Fin")); vbox.append(&end); let button = gtk::Button::with_label("Créer"); vbox.append(&button); + let conn = app.conn.clone(); + let tx = app.tx.clone(); button.connect_clicked(glib::clone!(@weak dialog => move |_| { - match event::validate(Uuid::new_v4(), date.buffer().text(), name.buffer().text(), start.buffer().text(), end.buffer().text()) { - Some(event) => { - match db::insert(&conn, &event) { + match event::validate(event.id, date.buffer().text(), name.buffer().text(), start.buffer().text(), end.buffer().text()) { + Some(new) => { + match if is_new { db::insert(&conn, &new) } else { db::update(&conn, &new) } { Ok(_) => { - update::send(tx.clone(), Msg::AddEvent { event: event }); + let msg = if is_new { Msg::AddEvent { new } } else { Msg::UpdateEvent { old: event.clone(), new } }; + update::send(tx.clone(), msg); dialog.close() }, Err(_) => () diff --git a/src/app/mod.rs b/src/app/mod.rs index 30b59af..c9a7f83 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -24,7 +24,7 @@ pub fn run(conn: Connection) { fn build_ui(conn: Rc, app: >k::Application) { let (tx, rx) = async_channel::unbounded(); let app = App::new(conn.clone(), app, tx.clone()); - utils::spawn(update::event_handler(conn, rx, tx, app)) + utils::spawn(update::event_handler(rx, app)) } fn load_style() { diff --git a/src/app/update.rs b/src/app/update.rs index e7bf7af..f1576b5 100644 --- a/src/app/update.rs +++ b/src/app/update.rs @@ -1,11 +1,9 @@ use async_channel::{Receiver, Sender}; use chrono::NaiveDate; -use rusqlite::Connection; -use std::rc::Rc; use crate::{ app::{calendar, form, utils, App}, - model::event::Event, + model::{event, event::Event}, }; pub fn send(tx: Sender, msg: Msg) { @@ -16,20 +14,39 @@ pub fn send(tx: Sender, msg: Msg) { pub enum Msg { ShowAddForm { date: NaiveDate }, - AddEvent { event: Event }, + ShowUpdateForm { event: Event }, + AddEvent { new: Event }, + UpdateEvent { old: Event, new: Event }, } -pub async fn event_handler(conn: Rc, rx: Receiver, tx: Sender, mut app: App) { +pub async fn event_handler(rx: Receiver, mut app: App) { while let Ok(msg) = rx.recv().await { match msg { Msg::ShowAddForm { date } => { - form::show(Rc::clone(&conn), tx.clone(), Rc::clone(&app.window), date).await; + form::show(&app, event::init(date), true).await; } - Msg::AddEvent { event } => { - let date = event.date.clone(); - app.events.push(event); + Msg::ShowUpdateForm { event } => { + form::show(&app, event, false).await; + } + Msg::AddEvent { new } => { + let date = new.date.clone(); + app.events.push(new); calendar::refresh_date(&app, date); } + Msg::UpdateEvent { old, new } => { + let new_date = new.date.clone(); + match app.events.iter().position(|e| e.id == new.id) { + Some(index) => { + app.events.remove(index); + app.events.push(new); + calendar::refresh_date(&app, new_date); + if old.date != new_date { + calendar::refresh_date(&app, old.date.clone()) + } + } + None => println!("Event not found when updating from {:?} to {:?}", old, new), + } + } } } } diff --git a/src/db/mod.rs b/src/db/mod.rs index 23cec7e..03692e9 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -21,6 +21,16 @@ pub fn insert(conn: &Connection, event: &Event) -> Result<()> { Ok(()) } +pub fn update(conn: &Connection, event: &Event) -> Result<()> { + conn.execute( + "UPDATE events SET date = ?, start = ?, end = ?, name = ?, updated = datetime() where id = ?", + params![event.date, event.start, event.end, event.name, event.id.to_hyphenated().to_string()] + )?; + + Ok(()) +} + +// TODO: Don’t use unwrap pub fn list(conn: &Connection) -> Result> { let mut stmt = conn.prepare("SELECT id, date, start, end, name FROM events")?; diff --git a/src/model/event.rs b/src/model/event.rs index 27587bc..7ab0244 100644 --- a/src/model/event.rs +++ b/src/model/event.rs @@ -13,6 +13,16 @@ pub struct Event { pub name: String, } +pub fn init(date: NaiveDate) -> Event { + Event { + id: Uuid::new_v4(), + date, + start: None, + end: None, + name: "".to_string(), + } +} + impl Event { pub fn pprint(&self) -> String { let start = self.start.map(pprint_time).unwrap_or_default(); @@ -29,7 +39,7 @@ impl Event { } } -fn pprint_time(t: NaiveTime) -> String { +pub fn pprint_time(t: NaiveTime) -> String { if t.minute() == 0 { format!("{}h", t.hour()) } else { -- cgit v1.2.3