Skip to content

Commit

Permalink
feat: switch to callback timer with system time
Browse files Browse the repository at this point in the history
  • Loading branch information
G07cha committed Jun 29, 2023
1 parent 8feb6cc commit 9ac1620
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 65 deletions.
5 changes: 3 additions & 2 deletions src-tauri/Cargo.lock

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

2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ tauri = { version = "1.4.1", features = [ "shell-open", "test", "global-shortcut
window-vibrancy = "0.3.2"
tauri-plugin-autostart = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "dev" }
ts-rs = "6.2.1"
ticking-timer = { git = "https://github.com/G07cha/ticking-timer", version = "0.1.1" }
ticking-timer = { git = "https://github.com/g07cha/ticking-timer", branch = "main" }
anyhow = "1.0.71"

[dev-dependencies]
Expand Down
63 changes: 58 additions & 5 deletions src-tauri/src/commands/timer.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,67 @@
use tauri::{command, State, Window};

use crate::{PomodoroState, SettingsState, TimerState, TimerStatePayload};
use crate::{
state::{Pomodoro, TimerMode},
PomodoroState, SettingsState, TimerState, TimerStatePayload,
};

fn update_pomodoro_state(state: &Pomodoro) -> Pomodoro {
match state.mode {
TimerMode::Relax => Pomodoro {
mode: TimerMode::Work,
cycles: state.cycles + 1,
},
TimerMode::Work => Pomodoro {
mode: TimerMode::Relax,
cycles: state.cycles,
},
}
}

#[command]
pub fn toggle_timer(window: Window, timer: State<'_, TimerState>) {
timer.toggle();
pub fn toggle_timer(window: Window, timer: State<'_, TimerState>) -> Result<(), String> {
timer
.toggle()
.map_err(|_| "Failed to toggle timer".to_string())?;

window
.emit("timer-running-change", *timer.is_running())
.map_err(|_| "Failed to communicate running state".to_string())
}

#[command]
pub fn reset_timer(
window: Window,
timer: State<'_, TimerState>,
pomodoro_state: State<'_, PomodoroState>,
settings: State<'_, SettingsState>,
) -> Result<(), String> {
let mut pomodoro_state = pomodoro_state.lock().unwrap();

*pomodoro_state = update_pomodoro_state(&pomodoro_state);
let new_duration = pomodoro_state.duration(&settings.read().unwrap());
timer
.reset(new_duration)
.map_err(|_| "Failed to reset timer".to_string())?;
timer
.resume()
.map_err(|_| "Failed to resume timer".to_string())?;

window
.emit::<TimerStatePayload>(
"timer-state",
TimerStatePayload {
mode: pomodoro_state.mode,
cycle: pomodoro_state.cycles,
is_ended: false,
duration_secs: new_duration.as_secs() as u32,
},
)
.map_err(|_| "Failed to communicate new state".to_string())?;

window
.emit("timer-running-change", timer.is_running())
.unwrap()
.emit("timer-running-change", *timer.is_running())
.map_err(|_| "Failed to communicate running state".to_string())
}

#[command]
Expand Down
4 changes: 2 additions & 2 deletions src-tauri/src/helpers/shortcuts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ pub fn register_toggle_shortcut<R: Runtime>(
let app_handle = app_handle.clone();
move || {
let timer = app_handle.state::<TimerState>();
timer.toggle();
timer.toggle().expect("Failed to toggle timer");
app_handle
.get_window(MAIN_WINDOW_LABEL)
.expect("Failed to find main window")
.emit("timer-running-change", timer.is_running())
.emit("timer-running-change", *timer.is_running())
.unwrap();
}
})?
Expand Down
46 changes: 9 additions & 37 deletions src-tauri/src/helpers/timer.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,14 @@
use tauri::{AppHandle, Manager};
use std::time::Duration;

use crate::{
state::{Pomodoro, TimerMode},
PomodoroState, SettingsState, TimerState, TimerStatePayload, MAIN_WINDOW_LABEL,
};
use tauri::{AppHandle, Manager};

fn update_pomodoro_state(state: &Pomodoro) -> Pomodoro {
match state.mode {
TimerMode::Relax => Pomodoro {
mode: TimerMode::Work,
cycles: state.cycles + 1,
},
TimerMode::Work => Pomodoro {
mode: TimerMode::Relax,
cycles: state.cycles,
},
}
}
use crate::{PomodoroState, TimerStatePayload, MAIN_WINDOW_LABEL};

pub fn setup_timer_listener(app_handle: &AppHandle) -> impl Fn() {
let window = app_handle
.get_window(MAIN_WINDOW_LABEL)
.expect("Unable to retrieve main window");
let update_receiver = app_handle.state::<TimerState>().update_receiver.clone();
pub fn create_timer_listener(app_handle: &AppHandle) -> impl Fn(Duration) {
let app_handle = app_handle.clone();
let main_window = app_handle.get_window(MAIN_WINDOW_LABEL).unwrap();

move || loop {
let remaining = update_receiver
.recv()
.expect("Failed to receive timer tick");
move |remaining: Duration| {
let remaining_secs = remaining.as_secs();

#[cfg(target_os = "macos")]
Expand All @@ -47,25 +27,17 @@ pub fn setup_timer_listener(app_handle: &AppHandle) -> impl Fn() {
app_handle.emit_all("timer-tick", &remaining_secs).unwrap();

if remaining.is_zero() {
let settings = app_handle.state::<SettingsState>();
let pomodoro_state = app_handle.state::<PomodoroState>();
let mut pomodoro_state = pomodoro_state.lock().unwrap();

*pomodoro_state = update_pomodoro_state(&pomodoro_state);
let new_duration = pomodoro_state.duration(&settings.read().unwrap());
app_handle
.state::<TimerState>()
.reset(new_duration)
.unwrap();
let pomodoro_state = pomodoro_state.lock().unwrap();

window
main_window
.emit::<TimerStatePayload>(
"timer-state",
TimerStatePayload {
mode: pomodoro_state.mode,
cycle: pomodoro_state.cycles,
is_ended: true,
duration_secs: new_duration.as_secs() as u32,
duration_secs: remaining.as_secs() as u32,
},
)
.unwrap();
Expand Down
29 changes: 14 additions & 15 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@
)]

use std::sync::{Arc, Mutex, RwLock};
use std::thread;
use std::time::Duration;

use commands::settings::*;
use commands::timer::*;
use helpers::fs::load_settings;
use helpers::shortcuts::setup_shortcuts;
use helpers::timer::setup_timer_listener;
use helpers::timer::create_timer_listener;
use serde::Serialize;
use state::{Pomodoro, Settings};
use tauri::{Manager, RunEvent};
Expand Down Expand Up @@ -48,14 +47,25 @@ pub type PomodoroState = Mutex<Pomodoro>;
fn main() {
tauri::Builder::default()
.menu(tauri::Menu::new())
.manage::<TimerState>(Arc::new(Timer::new(Duration::from_millis(100))))
.manage::<SettingsState>(RwLock::new(Settings::default()))
.manage::<PomodoroState>(Mutex::new(Pomodoro {
cycles: 0,
mode: state::TimerMode::Work,
}))
.setup(|app| {
let app_handle = app.handle();
let main_window = setup_main_window(&app_handle).unwrap();
decorate_window(&main_window);

#[cfg(debug_assertions)]
main_window.open_devtools();

let timer = Timer::new(
Duration::from_millis(100),
create_timer_listener(&app_handle),
);

app_handle.manage::<TimerState>(Arc::new(timer));

{
match load_settings(&app_handle) {
Expand All @@ -70,20 +80,8 @@ fn main() {
}
}

{
let main_window = setup_main_window(&app_handle).unwrap();
decorate_window(&main_window);

#[cfg(debug_assertions)]
main_window.open_devtools();
}

setup_tray(app);

thread::Builder::new()
.name("Timer listener".into())
.spawn(setup_timer_listener(&app_handle))?;

Ok(())
})
.plugin(tauri_plugin_autostart::init(
Expand All @@ -92,6 +90,7 @@ fn main() {
))
.invoke_handler(tauri::generate_handler![
toggle_timer,
reset_timer,
get_timer_state,
get_settings,
set_settings
Expand Down
5 changes: 3 additions & 2 deletions src-tauri/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
use std::time::Duration;

use serde::{Deserialize, Serialize};
use ts_rs::TS;

use crate::commands::settings::SettingsPayload;

#[derive(Clone, Copy, serde::Serialize, serde::Deserialize, Debug, TS)]
#[derive(Clone, Copy, Serialize, Deserialize, Debug, TS)]
#[ts(export)]
pub enum TimerMode {
Work,
Relax,
}

#[derive(Clone, serde::Serialize, serde::Deserialize, Debug)]
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Settings {
pub work_duration: Duration,
pub relax_duration: Duration,
Expand Down
2 changes: 1 addition & 1 deletion src-ui/pages/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ timerService.onEnd(() => {
timerUI.showIcon(TimerIcon.Play);
timerUI.setText('');
timerUI.setCycle(0);
message('Timer is done', { type: 'info' }).then(() => timerService.toggle());
message('Timer is done', { type: 'info' }).then(() => timerService.reset());
});

timerService.onPause(() => {
Expand Down
2 changes: 2 additions & 0 deletions src-ui/pages/main/timer.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ export class TimerService {

toggle = () => invoke('toggle_timer');

reset = () => invoke('reset_timer');

get duration() {
return this._duration;
}
Expand Down

0 comments on commit 9ac1620

Please sign in to comment.