diff options
| author | Joris | 2022-02-20 09:33:55 +0100 | 
|---|---|---|
| committer | Joris | 2022-02-20 09:33:55 +0100 | 
| commit | 1445e23a26c6581ad0c3f5b5016e47e95d224e9f (patch) | |
| tree | 18bd4288fbcf52279a69de50be5bad6cc7db3c75 /src/app/form | |
| parent | 6c47403b11e7aaf1a22778bdc7615051779cb7bd (diff) | |
Save repetition in events
But don’t show repetead events for now.
Diffstat (limited to 'src/app/form')
| -rw-r--r-- | src/app/form/mod.rs | 124 | ||||
| -rw-r--r-- | src/app/form/repetition.rs | 151 | 
2 files changed, 275 insertions, 0 deletions
| diff --git a/src/app/form/mod.rs b/src/app/form/mod.rs new file mode 100644 index 0000000..5c60bc5 --- /dev/null +++ b/src/app/form/mod.rs @@ -0,0 +1,124 @@ +mod repetition; + +use gtk4 as gtk; + +use gtk::glib; +use gtk::prelude::*; + +use crate::{ +    app::{update, update::Msg, App}, +    db, +    model::{event, event::Event}, +}; + +pub async fn show(app: &App, event: Event, is_new: bool) { +    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()]) +        .build(); + +    let content_area = dialog.content_area(); + +    let lines = gtk::Box::builder() +        .orientation(gtk::Orientation::Vertical) +        .build(); +    content_area.append(&lines); + +    let columns = gtk::Box::builder() +        .orientation(gtk::Orientation::Horizontal) +        .build(); +    columns.add_css_class("g-Form__Columns"); +    lines.append(&columns); + +    // First column + +    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); +    column1.append(&label("Événement")); +    column1.append(&name); + +    let date = entry(&event.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("".to_string()), +    ); +    column1.append(&label("Début")); +    column1.append(&start); + +    let end = entry(&event.end.map(event::pprint_time).unwrap_or("".to_string())); +    column1.append(&label("Fin")); +    column1.append(&end); + +    // Second column + +    let repetition_model = repetition::view(&event); +    columns.append(&repetition_model.view); + +    // Buttons + +    let button = gtk::Button::builder() +        .label(if is_new { "Créer" } else { "Modifier" }) +        .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 |_| { +        let repetition = repetition::validate(&repetition_model).clone(); +        match event::validate(event.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) => println!("Error when upserting event: {err}") +                } +            }, +            None => println!("Event is not valid: {event:?}") +        } +    })); + +    if !is_new { +        let button = gtk::Button::builder().label("Supprimer").build(); +        lines.append(&button); +        let conn = app.conn.clone(); +        let tx = app.tx.clone(); +        button.connect_clicked(glib::clone!(@weak dialog => move |_| { +            match db::delete(&conn, &event.id) { +                Ok(_) => { +                    update::send(tx.clone(), Msg::DeleteEvent { event: event.clone() }); +                    dialog.close() +                }, +                Err(_) => () +            } +        })); +    } + +    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() +} diff --git a/src/app/form/repetition.rs b/src/app/form/repetition.rs new file mode 100644 index 0000000..ac56479 --- /dev/null +++ b/src/app/form/repetition.rs @@ -0,0 +1,151 @@ +use gtk4 as gtk; + +use chrono::{Weekday, Weekday::*}; +use gtk::prelude::*; + +use crate::{ +    model::event::Event, +    model::{ +        repetition, +        repetition::{MonthFrequency, Repetition}, +    }, +}; + +static WEEKDAYS_STR: [&str; 7] = [ +    "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche", +]; + +static WEEKDAYS: [Weekday; 7] = [Mon, Tue, Wed, Thu, Fri, Sat, Sun]; + +pub struct Model { +    pub view: gtk::Box, +    pub no_radio: gtk::CheckButton, +    pub day_interval_radio: gtk::CheckButton, +    pub day_interval_entry: gtk::Entry, +    pub monthly_radio: gtk::CheckButton, +    pub monthly_entry: gtk::Entry, +    pub first_day_radio: gtk::CheckButton, +    pub first_day_dropdown: gtk::DropDown, +    pub yearly_radio: gtk::CheckButton, +} + +pub fn view(event: &Event) -> Model { +    let view = gtk::Box::builder() +        .orientation(gtk::Orientation::Vertical) +        .build(); +    view.add_css_class("g-Form__Inputs"); + +    view.append(&label("Répétition")); + +    let no_radio = gtk::CheckButton::builder() +        .label("Non") +        .active(event.repetition.is_none()) +        .build(); +    view.append(&no_radio); + +    let default = match event.repetition { +        Some(Repetition::Daily { frequency }) => frequency.to_string(), +        _ => "".to_string(), +    }; +    let day_interval_entry = gtk::Entry::builder().text(&default).build(); +    let (day_interval_box, day_interval_radio) = radio_input( +        &no_radio, +        !default.is_empty(), +        &day_interval_entry, +        "Interval de jours", +    ); +    view.append(&day_interval_box); + +    let default = match event.repetition { +        Some(Repetition::Monthly { +            frequency: MonthFrequency::Day { day }, +        }) => day.to_string(), +        _ => "".to_string(), +    }; +    let monthly_entry = gtk::Entry::builder().text(&default).build(); +    let (monthly_box, monthly_radio) = +        radio_input(&no_radio, !default.is_empty(), &monthly_entry, "Mensuel"); +    view.append(&monthly_box); + +    let (active, default) = match event.repetition { +        Some(Repetition::Monthly { +            frequency: MonthFrequency::FirstDay { day }, +        }) => (true, day), +        _ => (false, Mon), +    }; +    let first_day_dropdown = gtk::DropDown::from_strings(&WEEKDAYS_STR); +    first_day_dropdown +        .set_selected(WEEKDAYS.iter().position(|d| d == &default).unwrap_or(0) as u32); +    let (first_day_of_month_box, first_day_radio) = +        radio_input(&no_radio, active, &first_day_dropdown, "1er jour du mois"); +    view.append(&first_day_of_month_box); + +    let yearly_radio = gtk::CheckButton::builder() +        .group(&no_radio) +        .label("Annuel") +        .active(event.repetition == Some(Repetition::Yearly)) +        .build(); +    view.append(&yearly_radio); + +    Model { +        view, +        no_radio, +        day_interval_radio, +        day_interval_entry, +        monthly_radio, +        monthly_entry, +        first_day_radio, +        first_day_dropdown, +        yearly_radio, +    } +} + +fn radio_input( +    radio_group: &impl IsA<gtk::CheckButton>, +    active: bool, +    input: &impl IsA<gtk::Widget>, +    text: &str, +) -> (gtk::Box, gtk::CheckButton) { +    let radio_box = gtk::Box::builder().build(); +    let radio = gtk::CheckButton::builder() +        .group(radio_group) +        .label(text) +        .active(active) +        .build(); +    radio_box.append(&radio); +    input.add_css_class("g-Form__RadioInput"); +    radio_box.append(input); +    (radio_box, radio) +} + +fn label(text: &str) -> gtk::Label { +    gtk::Label::builder() +        .label(text) +        .halign(gtk::Align::Start) +        .margin_bottom(5) +        .build() +} + +pub fn validate(model: &Model) -> Option<Repetition> { +    if model.no_radio.is_active() { +        None +    } else if model.day_interval_radio.is_active() { +        repetition::validate_day(&model.day_interval_entry.buffer().text()) +            .map(|d| Repetition::Daily { frequency: d }) +    } else if model.monthly_radio.is_active() { +        repetition::validate_day(&model.monthly_entry.buffer().text()).map(|d| { +            Repetition::Monthly { +                frequency: MonthFrequency::Day { day: d }, +            } +        }) +    } else if model.first_day_radio.is_active() { +        let day = WEEKDAYS[model.first_day_dropdown.selected() as usize]; +        Some(Repetition::Monthly { +            frequency: MonthFrequency::FirstDay { day }, +        }) +    } else if model.yearly_radio.is_active() { +        Some(Repetition::Yearly) +    } else { +        None +    } +} | 
