aboutsummaryrefslogtreecommitdiff
path: root/src/app.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/app.rs')
-rw-r--r--src/app.rs151
1 files changed, 113 insertions, 38 deletions
diff --git a/src/app.rs b/src/app.rs
index 5717c12..0eb2b1e 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -1,15 +1,18 @@
use gtk4 as gtk;
use async_channel::{Receiver, Sender};
-use chrono::{Datelike, NaiveDate, Weekday};
+use chrono::{Datelike, NaiveDate, NaiveTime, Weekday};
use gtk::gdk::Display;
-use gtk::glib;
use gtk::glib::signal::Inhibit;
+use gtk::glib;
use gtk::prelude::*;
+use rusqlite::Connection;
use std::future::Future;
use std::rc::Rc;
-use crate::model::event::{Event, Time};
+use crate::model::event;
+use crate::model::event::Event;
+use crate::db;
/// Spawns a task on the default executor, without waiting for it to complete
pub fn spawn<F>(future: F)
@@ -35,24 +38,25 @@ static MONTHES: [&str; 12] = [
"Jan", "Fév", "Mar", "Avr", "Mai", "Juin", "Juil", "Aoû", "Sep", "Oct", "Nov", "Déc",
];
-pub fn run(events: Vec<Event>) {
- let application = gtk::Application::new(Some("me.guyonvarch.calendar"), Default::default());
- application.connect_startup(move |app| build_ui(app, &events));
- application.run();
+pub fn run(conn: Connection) {
+ let conn = Rc::new(conn);
+ let app = gtk::Application::new(Some("me.guyonvarch.calendar"), Default::default());
+ app.connect_startup(|_| load_style());
+ app.connect_activate(move |app| build_ui(conn.clone(), app));
+ app.run();
}
-fn build_ui(app: &gtk::Application, events: &Vec<Event>) {
- load_style();
+fn build_ui(conn: Rc<Connection>, app: &gtk::Application) {
let (tx, rx) = async_channel::unbounded();
- let app = App::new(app, tx.clone(), &events);
- spawn(event_handler(rx, tx, app))
+ let app = App::new(conn.clone(), app, tx.clone());
+ spawn(event_handler(conn, rx, tx, app))
}
-async fn event_handler(rx: Receiver<Msg>, tx: Sender<Msg>, mut app: App) {
+async fn event_handler(conn: Rc<Connection>, rx: Receiver<Msg>, tx: Sender<Msg>, mut app: App) {
while let Ok(msg) = rx.recv().await {
match msg {
Msg::ShowAddForm { date } => {
- spawn(add_event_dialog(tx.clone(), Rc::clone(&app.window), date));
+ add_event_dialog(Rc::clone(&conn), tx.clone(), Rc::clone(&app.window), date).await;
}
Msg::AddEvent { event } => {
let date = event.date.clone();
@@ -85,7 +89,7 @@ struct App {
}
impl App {
- fn new(app: &gtk::Application, tx: Sender<Msg>, events: &Vec<Event>) -> Self {
+ fn new(conn: Rc<Connection>, app: &gtk::Application, tx: Sender<Msg>) -> Self {
let window = Rc::new(
gtk::ApplicationWindow::builder()
.application(app)
@@ -107,6 +111,8 @@ impl App {
let start_date =
NaiveDate::from_isoywd(today.year(), today.iso_week().week(), Weekday::Mon);
+
+ let events = db::list(&conn).unwrap_or(vec!());
show_days(tx, &grid, &start_date, &today, &events);
window.connect_close_request(move |window| {
@@ -196,7 +202,7 @@ fn day_entry(
.iter()
.filter(|e| e.date == *date)
.collect::<Vec<&Event>>();
- events.sort_by_key(|e| e.time);
+ events.sort_by_key(|e| e.start);
if !events.is_empty() {
vbox.append(&day_events(events));
@@ -263,34 +269,103 @@ fn day_events(events: Vec<&Event>) -> gtk::Box {
vbox
}
-async fn add_event_dialog(tx: Sender<Msg>, window: Rc<gtk::ApplicationWindow>, date: NaiveDate) {
+static DATE_FORMAT: &str = "%d/%m/%Y";
+
+async fn add_event_dialog(conn: Rc<Connection>, tx: Sender<Msg>, window: Rc<gtk::ApplicationWindow>, date: NaiveDate) {
let dialog = gtk::Dialog::builder()
.transient_for(&*window)
.modal(true)
- .title("Ajouter un évènement")
+ .title("Ajouter")
+ .css_classes(vec!["g-Form".to_string()])
.build();
let content_area = dialog.content_area();
- let label = gtk::Label::builder().label(&format!("{:?}", date)).build();
- content_area.append(&label);
-
- let entry = gtk::Entry::builder().build();
- content_area.append(&entry);
-
- dialog.add_buttons(&[
- ("Annuler", gtk::ResponseType::Cancel),
- ("Créer", gtk::ResponseType::Ok),
- ]);
-
- let answer = dialog.run_future().await;
- if answer == gtk::ResponseType::Ok {
- let event = Event {
- date,
- time: Time::AllDay,
- name: entry.buffer().text(),
- };
-
- send_message(tx, Msg::AddEvent { event: event });
+
+ let vbox = gtk::Box::builder()
+ .orientation(gtk::Orientation::Vertical)
+ .build();
+ vbox.add_css_class("g-Form__Inputs");
+ content_area.append(&vbox);
+
+ let name = entry("");
+ vbox.append(&label("Événement"));
+ vbox.append(&name);
+
+ let date = entry(&date.format(DATE_FORMAT).to_string());
+ vbox.append(&label("Jour"));
+ vbox.append(&date);
+
+ let start = entry("");
+ vbox.append(&label("Début"));
+ vbox.append(&start);
+
+ let end = entry("");
+ vbox.append(&label("Fin"));
+ vbox.append(&end);
+
+ let button = gtk::Button::with_label("Créer");
+ vbox.append(&button);
+ button.connect_clicked(glib::clone!(@weak dialog => move |_| {
+ match validate_event(date.buffer().text(), name.buffer().text(), start.buffer().text(), end.buffer().text()) {
+ Some(event) => {
+ match db::insert(&conn, &event) {
+ Ok(_) => {
+ send_message(tx.clone(), Msg::AddEvent { event: event });
+ dialog.close()
+ },
+ Err(_) => ()
+ }
+ },
+ None => ()
+ }
+ }));
+
+ dialog.run_future().await;
+}
+
+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 validate_event(date: String, name: String, start: String, end: String) -> Option<Event> {
+ let start = validate_time(start)?;
+ let end = validate_time(end)?;
+
+ match (start, end) {
+ (Some(s), Some(e)) if s > e => None?,
+ _ => (),
+ }
+
+ Some(Event {
+ date: NaiveDate::parse_from_str(&date, DATE_FORMAT).ok()?,
+ name: validate_name(name)?,
+ start,
+ end,
+ })
+}
+
+fn validate_time(time: String) -> Option<Option<NaiveTime>> {
+ let time = time.trim();
+ if time.is_empty() {
+ Some(None)
+ } else {
+ event::parse_time(time).map(|t| Some(t))
+ }
+}
+
+fn validate_name(name: String) -> Option<String> {
+ let name = name.trim();
+ if name.is_empty() {
+ None
+ } else {
+ Some(name.to_string())
}
- dialog.close();
}