use std::{fmt::Display, time::Duration};
use egui::{Button, Color32};
use notify_rust::Notification;
#[derive(serde::Deserialize, serde::Serialize)]
#[serde(default)] pub struct EpomoApp {
interval_period: i64,
long_break_period: i64,
short_break_period: i64,
session_count: usize,
show_notifs: bool,
#[serde(skip)]
current_mode: PomodoroMode,
#[serde(skip)]
ends_at: Option<chrono::DateTime<chrono::Utc>>,
}
impl Default for EpomoApp {
fn default() -> Self {
Self {
interval_period: 25,
long_break_period: 15,
short_break_period: 5,
session_count: 0,
show_notifs: true,
ends_at: None,
current_mode: PomodoroMode::Work, }
}
}
impl EpomoApp {
pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
if let Some(storage) = cc.storage {
return eframe::get_value(storage, eframe::APP_KEY).unwrap_or_default();
}
Default::default()
}
}
#[derive(Copy, Clone, PartialEq)]
enum PomodoroMode {
LongBreak,
ShortBreak,
Work,
}
impl PomodoroMode {
fn get_notif_str(&self) -> &'static str {
match *self {
PomodoroMode::LongBreak => "Time for a long break!",
PomodoroMode::ShortBreak => "Time for a short break!",
PomodoroMode::Work => "Time to work!",
}
}
}
impl Display for PomodoroMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
PomodoroMode::LongBreak => write!(f, "Long break"),
PomodoroMode::ShortBreak => write!(f, "Short break"),
PomodoroMode::Work => write!(f, "Work"),
}
}
}
impl From<PomodoroMode> for egui::Color32 {
fn from(val: PomodoroMode) -> Self {
match val {
PomodoroMode::LongBreak => Color32::from_rgb(240, 140, 58),
PomodoroMode::ShortBreak => Color32::from_rgb(240, 231, 58),
PomodoroMode::Work => Color32::from_rgb(58, 191, 240),
}
}
}
fn format_duration(duration: chrono::Duration, mode: PomodoroMode) -> String {
format!(
"{:02}:{:02}:{:02} {}",
duration.num_hours(),
duration.num_minutes(),
duration.num_seconds() % 60,
mode
)
}
fn get_mode(cur_mode: PomodoroMode, session_count: usize) -> PomodoroMode {
match cur_mode {
PomodoroMode::Work => {
if session_count % 4 == 0 {
PomodoroMode::LongBreak
} else {
PomodoroMode::ShortBreak
}
}
_ => PomodoroMode::Work,
}
}
impl eframe::App for EpomoApp {
fn save(&mut self, storage: &mut dyn eframe::Storage) {
eframe::set_value(storage, eframe::APP_KEY, self);
}
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
let Self {
interval_period,
ends_at,
long_break_period,
short_break_period,
current_mode,
session_count,
show_notifs,
} = self;
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading("Pomodoro");
ui.vertical(|ui| {
ui.label("Interval time in minutes");
ui.add_enabled(
ends_at.is_none() || *current_mode != PomodoroMode::Work,
egui::Slider::new(interval_period, 1..=120).suffix("m"),
);
});
ui.vertical(|ui| {
ui.label("Short break time in minutes");
ui.add_enabled(
*current_mode != PomodoroMode::ShortBreak,
egui::Slider::new(short_break_period, 1..=30).suffix("m"),
);
});
ui.vertical(|ui| {
ui.label("Long break time in minutes");
ui.add_enabled(
*current_mode != PomodoroMode::LongBreak,
egui::Slider::new(long_break_period, 1..=120).suffix("m"),
);
});
ui.add(egui::Checkbox::new(show_notifs, "Show notifications"));
ui.horizontal(|ui| {
if ui
.add_enabled(ends_at.is_none(), Button::new("Start"))
.clicked()
{
*ends_at =
Some(chrono::Utc::now() + chrono::Duration::minutes(*interval_period));
};
if ui
.add_enabled(ends_at.is_some(), Button::new("Stop"))
.clicked()
{
*ends_at = None;
*session_count = 0;
};
});
if ends_at.is_some() {
let now = chrono::Utc::now();
let time_left = ends_at.unwrap() - now;
if time_left < chrono::Duration::seconds(0) {
if *current_mode == PomodoroMode::Work {
*session_count += 1;
}
*current_mode = get_mode(*current_mode, *session_count);
match current_mode {
PomodoroMode::LongBreak => {
*ends_at = Some(
chrono::Utc::now() + chrono::Duration::minutes(*long_break_period),
)
}
PomodoroMode::ShortBreak => {
*ends_at = Some(
chrono::Utc::now() + chrono::Duration::minutes(*short_break_period),
)
}
PomodoroMode::Work => {
*ends_at = Some(
chrono::Utc::now() + chrono::Duration::minutes(*interval_period),
)
}
}
if *show_notifs {
Notification::new()
.summary("epomo")
.body(current_mode.get_notif_str())
.show()
.unwrap();
}
ctx.request_repaint();
}
ui.label(
egui::RichText::new(format_duration(time_left, *current_mode))
.heading()
.color(Into::<Color32>::into(*current_mode)),
);
ui.label(format!("Completed session count {}", *session_count));
}
ctx.request_repaint_after(Duration::from_secs(1));
});
}
}