Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
6fe0d2c
chore: update AUR checksum
vjousse Nov 28, 2025
af1b58b
feat: parse session file content
vjousse Jun 26, 2025
948a684
fix: use `;` for the separator
vjousse Jun 26, 2025
ba4848a
refactor: use anyhow and add tests
vjousse Jun 27, 2025
7c7384d
feat: add option to display session label
vjousse Jun 27, 2025
a9622b9
chore: bump min rust version
vjousse Jun 27, 2025
5a9c361
chore: detect session start/end
vjousse Jun 27, 2025
0ecfc76
fix: simplify file handling, don’t allow to change default pomodorod …
vjousse Aug 11, 2025
c06d8db
fix: clippy
vjousse Aug 11, 2025
0ed2e5b
chore: create session file on play
vjousse Aug 13, 2025
d33af7c
fix: create cache dir if doesn’t exist
vjousse Aug 13, 2025
4fd2b5c
feat: delete session file on reset
vjousse Aug 15, 2025
bfa731d
chore: remove session file on reset
vjousse Aug 15, 2025
3703696
refactor: session file content
vjousse Aug 15, 2025
d330491
feat: auto start when file created
vjousse Aug 17, 2025
2e1aa77
chore: add debug
vjousse Sep 26, 2025
a08ceed
chore: clippy
vjousse Oct 7, 2025
b901842
fix: current time computation
vjousse Oct 10, 2025
fa49cc9
chore: get pomodoro from session file
vjousse Nov 7, 2025
3590759
refactor: move pomodoro code
vjousse Nov 7, 2025
8a24224
fix: don’t use harcoded path in tests
vjousse Nov 7, 2025
f1108b7
chore: downgrade rust version for flatpak
vjousse Nov 7, 2025
7b3c2ca
test: add session file tests
vjousse Nov 7, 2025
32fb317
feat(wip): control gui with session file
vjousse Nov 7, 2025
81a9b59
chore: clippy
vjousse Nov 7, 2025
1321fa8
chore: parse status from file
vjousse Nov 10, 2025
c99e1ed
chore(wip): pause management
vjousse Nov 18, 2025
0f74678
refactor: use elapsed time in session file
vjousse Nov 21, 2025
80e7548
chore: lint
vjousse Nov 21, 2025
4102ad8
chore: session file tests
vjousse Feb 17, 2026
581200f
chore: remove dead code
vjousse Feb 17, 2026
089ac48
chore: write session file
vjousse Feb 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion aur/PKGBUILD
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ license=('MIT')
depends=('alsa-lib' 'gtk3' 'hicolor-icon-theme' 'glibc' 'webkit2gtk-4.1' 'libsoup' 'cairo' 'glib2' 'pango' 'gcc-libs' 'gdk-pixbuf2' 'libayatana-appindicator')
provides=('pomodorolm')
source=("https://github.com/vjousse/pomodorolm/releases/download/app-v$pkgver/pomodorolm_${pkgver}_amd64.deb")
sha256sums=('8042ccb3d1be79c96ff8b5107ec5e1aee44cf09853d991dba8bd462e3fa14eb8')
sha256sums=('2cc41dbd3937998db8c10387a581af8779fc5d5cbbf2972c6dbe786df9f2f5ce')

package() {
bsdtar -xf "$srcdir/data.tar.gz" -C "$pkgdir"
Expand Down
6 changes: 5 additions & 1 deletion src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ description = "A Tauri App"
authors = ["Vincent Jousse"]
license = ""
repository = ""
edition = "2021"
rust-version = "1.60"
edition = "2024"
rust-version = "1.89"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand All @@ -33,6 +33,8 @@ rustls-pemfile = "2.2.0"
rodio = "0.19.0"
clap = { version = "4.0.32", features = ["derive"] }
dirs = "6.0.0"
anyhow = "1.0.98"
chrono = { version = "0.4.42", features = ["serde"] }
[features]
# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.
# If you use cargo directly instead of tauri's cli you can use this feature flag to switch between tauri's `dev` and `build` modes.
Expand Down
92 changes: 45 additions & 47 deletions src-tauri/src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,84 +1,82 @@
extern crate dirs;
use crate::config::Config;
use crate::config::{Config, pomodoro_state_from_config};
use crate::pomodoro::{SessionStatus, get_session_info, tick_with_file_session_info};

use anyhow::{Context, Result};
use std::fs;
use std::path::Path;
use std::time::{Duration, SystemTime};
use std::time::Duration;
use tokio::time::interval;

pub fn run(config_dir_name: &str) {
pub fn run(config_dir_name: &str, display_label: bool) -> Result<()> {
let config_dir = dirs::config_dir()
.expect("Error while getting the config directory")
.join(config_dir_name);

let config =
Config::get_or_create_from_disk(&config_dir, None).expect("Unable to get config file");
Config::get_or_create_from_disk(&config_dir, None).context("Unable to get config file")?;

// Initialize the Tokio runtime
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(run_pomodoro_checker(config));
rt.block_on(run_pomodoro_checker(config, display_label))
}

async fn run_pomodoro_checker(config: Config) {
let cache_dir = dirs::cache_dir().expect("Error while getting the cache directory");
async fn run_pomodoro_checker(config: Config, display_label: bool) -> Result<()> {
let mut pomodoro = pomodoro_state_from_config(&config);

let file_path = cache_dir.join("pomodoro_session");
let mut interval = interval(Duration::from_secs(1));

loop {
interval.tick().await;

if file_exists(&file_path).await {
if let Some(remaining_time) =
get_remaining_time(&file_path, config.focus_duration as u64).await
{
let total_seconds = config.focus_duration as u64; // Total time for Pomodoro in seconds
let remaining_seconds = remaining_time.as_secs();
let elapsed_seconds = total_seconds - remaining_seconds;

// Create the progress bar
let progress_bar = create_progress_bar(total_seconds, elapsed_seconds);
let formatted_time = format_time(remaining_seconds);

// Check if remaining time is zero
if remaining_seconds == 0 {
// Delete the session file
if let Err(e) = fs::remove_file(&file_path) {
eprintln!("Failed to delete session file: {e}");
}
continue;
}
let next_pomodoro = tick_with_file_session_info(
&pomodoro,
get_session_info(&pomodoro.config.session_file),
)?;

if next_pomodoro.current_session.elapsed_seconds == 1 {
println!("-> New pomodoro created");
}

// Create the progress bar
let progress_bar = create_progress_bar(
next_pomodoro.config.focus_duration.into(),
next_pomodoro.current_session.elapsed_seconds.into(),
);
let remaining_seconds = (next_pomodoro.config.focus_duration
- next_pomodoro.current_session.elapsed_seconds) as u64;

let formatted_time = format_time(remaining_seconds);

if next_pomodoro.current_session.status != SessionStatus::NotStarted {
if let Some(ref label) = next_pomodoro.current_session.label
&& display_label
{
println!("{progress_bar} {formatted_time} {}", label);
} else {
println!("{progress_bar} {formatted_time}");
}
} else {
println!("P -");
}

// Check if remaining time is zero
if remaining_seconds == 0 && file_exists(&pomodoro.config.session_file) {
// Delete the session file
fs::remove_file(&pomodoro.config.session_file)
.context("Failed to delete session file")?;
println!("-> Pomodoro ended normally");
continue;
}

pomodoro = next_pomodoro;
}
}

async fn file_exists(path: &Path) -> bool {
fn file_exists(path: &Path) -> bool {
fs::metadata(path).is_ok()
}

async fn get_remaining_time(path: &Path, duration: u64) -> Option<Duration> {
if let Ok(metadata) = fs::metadata(path) {
if let Ok(modified_time) = metadata.modified() {
let now = SystemTime::now();
let elapsed = now.duration_since(modified_time).ok()?;
let total_duration = Duration::from_secs(duration);

if elapsed >= total_duration {
return Some(Duration::from_secs(0)); // Return zero if the time is up
}

let remaining = total_duration - elapsed;
return Some(remaining);
}
}
None
}

fn create_progress_bar(total_seconds: u64, elapsed_seconds: u64) -> String {
let total_hashes = 10; // Total number of '#' in the progress bar
let filled_length =
Expand Down
31 changes: 29 additions & 2 deletions src-tauri/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::pomodoro;
use crate::pomodoro::{self, default_session_file};
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::fs;
use std::fs::OpenOptions;
Expand Down Expand Up @@ -32,6 +33,8 @@ pub struct Config {
pub minimize_to_tray_on_close: bool,
#[serde(default)]
pub muted: bool,
#[serde(default = "default_session_file")]
pub session_file: PathBuf,
pub short_break_audio: Option<String>,
pub short_break_duration: u16,
#[serde(default)]
Expand Down Expand Up @@ -70,7 +73,7 @@ impl Config {
pub fn get_or_create_from_disk(
config_dir: &Path,
config_file_name: Option<String>,
) -> Result<Self, Box<dyn std::error::Error>> {
) -> Result<Self> {
let config_file_path = Self::get_config_file_path(config_dir, config_file_name);

// Create the config dir and the themes one if they don’t exist
Expand Down Expand Up @@ -121,6 +124,7 @@ impl Default for Config {
minimize_to_tray: true,
minimize_to_tray_on_close: true,
muted: false,
session_file: default_session_file(),
short_break_audio: None,
short_break_duration: 5 * 60,
start_minimized: false,
Expand All @@ -132,3 +136,26 @@ impl Default for Config {
}
}
}

pub fn pomodoro_config(config: &Config) -> pomodoro::Config {
pomodoro::Config {
auto_start_long_break_timer: config.auto_start_break_timer,
auto_start_short_break_timer: config.auto_start_break_timer,
auto_start_focus_timer: config.auto_start_work_timer,
default_focus_label: config.default_focus_label.clone(),
default_short_break_label: config.default_short_break_label.clone(),
default_long_break_label: config.default_long_break_label.clone(),
focus_duration: config.focus_duration,
long_break_duration: config.long_break_duration,
max_focus_rounds: config.max_round_number,
session_file: config.session_file.clone(),
short_break_duration: config.short_break_duration,
}
}

pub fn pomodoro_state_from_config(config: &Config) -> pomodoro::Pomodoro {
pomodoro::Pomodoro {
config: pomodoro_config(config),
..pomodoro::Pomodoro::default()
}
}
Loading
Loading