use async_channel::{Receiver, Sender}; use chrono::{Duration, NaiveDate}; use std::collections::HashSet; use std::iter::FromIterator; use uuid::Uuid; use crate::{ db, gui::{calendar, form, utils, App}, model::{event, event::Event}, }; pub fn send(tx: Sender, msg: Msg) { utils::spawn(async move { let _ = tx.send(msg).await; }) } pub enum Msg { ShowAddForm { date: NaiveDate, }, ShowRepetitionDialog { event_id: Uuid, date: NaiveDate, }, ShowUpdateForm { event_id: Uuid, }, ShowUpdateRepetitionForm { event_id: Uuid, date: NaiveDate, }, ShowUpdateFromOccurence { event_id: Uuid, date: NaiveDate, }, AddEvent { new: Event, }, UpdateEvent { old: Event, new: Event, }, UpdateEventOccurence { event: Event, occurence: usize, date: NaiveDate, new: Event, }, UpdateRepeatedFrom { old: Event, updated: Event, new: Event, }, DeleteEvent { event: Event, }, DeleteOccurence { event: Event, date: NaiveDate, occurence: usize, }, SelectPreviousWeek, SelectNextWeek, } pub async fn event_handler(rx: Receiver, mut app: App) { while let Ok(msg) = rx.recv().await { match msg { Msg::ShowAddForm { date } => form::show(&app, form::Target::New { date }).await, Msg::ShowRepetitionDialog { date, event_id } => { match app.recurring_events.iter().position(|e| e.id == event_id) { Some(index) => { form::repetition_dialog(&app, date, &app.recurring_events[index]).await } None => eprintln!("Event not found with id: {}", event_id), } } Msg::ShowUpdateForm { event_id } => { match app.recurring_events.iter().position(|e| e.id == event_id) { Some(index) => { form::show( &app, form::Target::Update { event: app.recurring_events[index].clone(), }, ) .await } None => match app.events.iter().position(|e| e.id == event_id) { Some(index) => { form::show( &app, form::Target::Update { event: app.events[index].clone(), }, ) .await } None => eprintln!("Event not found with id: {}", event_id), }, } } Msg::ShowUpdateRepetitionForm { event_id, date } => { match app.recurring_events.iter().position(|e| e.id == event_id) { Some(index) => { form::show( &app, form::Target::UpdateRepetition { date, event: app.recurring_events[index].clone(), }, ) .await } None => eprintln!("Event not found with id: {}", event_id), } } Msg::ShowUpdateFromOccurence { event_id, date } => { match app.recurring_events.iter().position(|e| e.id == event_id) { Some(index) => { let event = app.recurring_events[index].clone(); form::show(&app, form::Target::UpdateFromOccurence { date, event }).await } None => eprintln!("Event not found with id: {}", event_id), } } Msg::AddEvent { new } => { let refresh_dates = add(&mut app, &new); refresh(&app, &refresh_dates) } Msg::UpdateEvent { old, new } => { let mut refresh_dates = remove(&mut app, &old); refresh_dates.extend(add(&mut app, &new)); refresh(&app, &refresh_dates); } Msg::UpdateEventOccurence { event, occurence, date, new, } => { remove_occurence(&mut app, &event, occurence); let mut refresh_dates = add(&mut app, &new); refresh_dates.insert(date); refresh(&app, &refresh_dates) } Msg::UpdateRepeatedFrom { old, updated, new } => { let mut refresh_dates = remove(&mut app, &old); refresh_dates.extend(add(&mut app, &updated)); refresh_dates.extend(add(&mut app, &new)); refresh(&app, &refresh_dates); } Msg::DeleteEvent { event } => { let refresh_dates = remove(&mut app, &event); refresh(&app, &refresh_dates) } Msg::DeleteOccurence { event, date, occurence, } => { remove_occurence(&mut app, &event, occurence); refresh(&app, &HashSet::from([date])) } Msg::SelectPreviousWeek => { app.start_date -= Duration::days(7); app.end_date -= Duration::days(7); match db::events::list_non_recurring_between( &app.conn, app.start_date, app.end_date, ) { Ok(events) => app.events = events, Err(err) => eprintln!("{}", err), }; refresh(&app, &HashSet::from_iter(visible_dates(&app))); } Msg::SelectNextWeek => { app.start_date += Duration::days(7); app.end_date += Duration::days(7); match db::events::list_non_recurring_between( &app.conn, app.start_date, app.end_date, ) { Ok(events) => app.events = events, Err(err) => eprintln!("{}", err), }; refresh(&app, &HashSet::from_iter(visible_dates(&app))); } } } } /// Remove event and return dates that should be refreshed. fn remove(app: &mut App, event: &Event) -> HashSet { if event.repetition.is_some() { match app.recurring_events.iter().position(|e| e.id == event.id) { Some(index) => { app.recurring_events.remove(index); repetition_dates(app, event) } None => { eprintln!("Event not found when trying to delete {:?}", event); HashSet::new() } } } else { match app.events.iter().position(|e| e.id == event.id) { Some(index) => { app.events.remove(index); HashSet::from([event.date]) } None => { eprintln!("Event not found when trying to delete {:?}", event); HashSet::new() } } } } /// Remove event repetition /// TODO: Completely remove the event if it’s the last remaining occurence fn remove_occurence(app: &mut App, event: &Event, occurence: usize) { match app.recurring_events.iter().position(|e| e.id == event.id) { Some(index) => { let event = app.recurring_events.get_mut(index).unwrap(); match event.repetition.clone() { Some(mut repetition) => { repetition.removed_occurences.insert(occurence); event.repetition = Some(repetition.clone()) } None => eprintln!( "Repetition not found when trying to delete repetition of {:?}", event ), } } None => { eprintln!( "Event not found when trying to delete repetition of {:?}", event ) } } } /// Add event and return dates that should be refreshed. fn add(app: &mut App, event: &Event) -> HashSet { if event.repetition.is_some() { app.recurring_events.push(event.clone()); let mut dates = repetition_dates(app, event); dates.insert(event.date); dates } else { app.events.push(event.clone()); HashSet::from([event.date]) } } /// Repetition dates of a repetead event. fn repetition_dates(app: &App, event: &Event) -> HashSet { let event_reps = event::repetitions_between(&[event.clone()], app.start_date, app.end_date); HashSet::from_iter(event_reps.keys().copied()) } /// Refresh app for the given dates. fn refresh(app: &App, dates: &HashSet) { let repetitions = event::repetitions_between(&app.recurring_events, app.start_date, app.end_date); for date in dates { if date >= &app.start_date && date <= &app.end_date { calendar::refresh_date( app, *date, &repetitions, &app.categories, &app.default_color, ) } } } /// List visible dates in calendar. fn visible_dates(app: &App) -> Vec { let mut date = app.start_date; let mut res = vec![date]; while date <= app.end_date { date += Duration::days(1); res.push(date); } res }