From 7817c9c9071b3708c97b2135061bbb75b481c6d2 Mon Sep 17 00:00:00 2001 From: "s0wlz (Matthias Puchstein)" Date: Thu, 26 Feb 2026 12:23:54 +0100 Subject: [PATCH] feat(countdown): add app state and wire countdown service into Tauri - add `app_state` module with `ClockAnchor` and `AppState` - initialize `AppState` with a default `CountdownService` - register app state in Tauri via `.manage(app_state)` - extend `CountdownSnapshotDto` epoch fields to `Option` - add countdown model helpers for start/target epoch-ms conversion - return optional epoch fields from `CountdownService::snapshot` - ignore generated `dist/` directory in `.gitignore` --- .gitignore | 3 +++ src-tauri/src/app_state.rs | 37 ++++++++++++++++++++++++++++++ src-tauri/src/countdown/dto.rs | 10 ++++---- src-tauri/src/countdown/model.rs | 24 +++++++++++++++++++ src-tauri/src/countdown/service.rs | 7 +++--- src-tauri/src/lib.rs | 5 ++++ 6 files changed, 77 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index b43a731..dcf6569 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,6 @@ pids debug.log TODO.md .aider* + +# dist folder +dist/ \ No newline at end of file diff --git a/src-tauri/src/app_state.rs b/src-tauri/src/app_state.rs index e69de29..2b4104b 100644 --- a/src-tauri/src/app_state.rs +++ b/src-tauri/src/app_state.rs @@ -0,0 +1,37 @@ +use crate::countdown::service::CountdownService; +#[derive(Clone, Debug)] +pub struct ClockAnchor { + pub boot_instant: std::time::Instant, + pub boot_epoch_ms: u128, +} + +impl ClockAnchor { + pub fn new() -> Self { + Self { + boot_instant: std::time::Instant::now(), + boot_epoch_ms: std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis(), + } + } + + pub fn instant_to_epoch_ms(&self, instant: std::time::Instant) -> u128 { + instant.duration_since(self.boot_instant).as_millis() + self.boot_epoch_ms + } +} + +#[derive(Debug)] +pub struct AppState { + pub clock_anchor: ClockAnchor, + pub countdowns: Vec, +} + +impl AppState { + pub fn new() -> Self { + Self { + clock_anchor: ClockAnchor::new(), + countdowns: vec![CountdownService::new()], + } + } +} diff --git a/src-tauri/src/countdown/dto.rs b/src-tauri/src/countdown/dto.rs index e5a7c3e..eb4332f 100644 --- a/src-tauri/src/countdown/dto.rs +++ b/src-tauri/src/countdown/dto.rs @@ -1,4 +1,4 @@ -use tokio::time::{Duration, Instant}; +use tokio::time::Duration; use crate::countdown::model::CountdownState; @@ -8,8 +8,8 @@ pub struct CountdownSnapshotDto { pub label: String, pub duration: Duration, pub state: CountdownState, - pub start_epoch_ms: Option, - pub target_epoch_ms: Option, + pub start_epoch_ms: Option, + pub target_epoch_ms: Option, } impl CountdownSnapshotDto { @@ -18,8 +18,8 @@ impl CountdownSnapshotDto { label: String, duration: Duration, state: CountdownState, - start_epoch_ms: Option, - target_epoch_ms: Option, + start_epoch_ms: Option, + target_epoch_ms: Option, ) -> Self { Self { id, diff --git a/src-tauri/src/countdown/model.rs b/src-tauri/src/countdown/model.rs index 22f96da..c3710be 100644 --- a/src-tauri/src/countdown/model.rs +++ b/src-tauri/src/countdown/model.rs @@ -1,4 +1,5 @@ use serde::{Deserialize, Serialize}; +use std::time::{SystemTime, UNIX_EPOCH}; use tokio::time::{Duration, Instant}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash)] @@ -164,6 +165,29 @@ impl Countdown { self.start_timestamp = None; self.target_timestamp = None; } + + pub fn start_epoch_ms(&self) -> u128 { + let anchor_instant = Instant::now(); + let anchor_epoch_ms = SystemTime::now(); + let anchor_epoch_ms = anchor_epoch_ms.duration_since(UNIX_EPOCH).unwrap(); + anchor_instant + .checked_duration_since(self.start_timestamp.unwrap()) + .unwrap() + .as_millis() + - anchor_epoch_ms.as_millis() + } + + pub fn target_epoch_ms(&self) -> u128 { + let anchor_instant = Instant::now(); + let anchor_epoch_ms = SystemTime::now(); + let anchor_epoch_ms = anchor_epoch_ms.duration_since(UNIX_EPOCH).unwrap(); + self.target_timestamp + .unwrap() + .checked_duration_since(anchor_instant) + .unwrap() + .as_millis() + + anchor_epoch_ms.as_millis() + } } #[cfg(test)] diff --git a/src-tauri/src/countdown/service.rs b/src-tauri/src/countdown/service.rs index 267318d..1adb186 100644 --- a/src-tauri/src/countdown/service.rs +++ b/src-tauri/src/countdown/service.rs @@ -1,10 +1,10 @@ -use chrono::Utc; use tokio::sync::Mutex; use tokio::time::{Duration, Instant}; use crate::countdown::dto::CountdownSnapshotDto; use crate::countdown::model::{Countdown, CountdownError}; +#[derive(Debug)] pub struct CountdownService { countdown: Mutex, next_id: u64, @@ -20,14 +20,13 @@ impl CountdownService { pub async fn snapshot(&self, now: Instant) -> CountdownSnapshotDto { let countdown = self.countdown.lock().await; - let instant_now = Instant::now(); CountdownSnapshotDto { id: countdown.id, label: countdown.label.to_string(), state: countdown.state(), duration: countdown.remaining(), - start_epoch_ms: countdown.start_epoch_ms(), - target_epoch_ms: countdown.target_epoch_ms(), + start_epoch_ms: Some(countdown.start_epoch_ms()), + target_epoch_ms: Some(countdown.target_epoch_ms()), } } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 4a66b4f..7393463 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,5 +1,8 @@ +mod app_state; mod countdown; +pub use app_state::AppState; + // Learn more about Tauri commands at https://tauri.app/develop/calling-rust/ #[tauri::command] fn greet(name: &str) -> String { @@ -8,7 +11,9 @@ fn greet(name: &str) -> String { #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { + let app_state = AppState::new(); tauri::Builder::default() + .manage(app_state) .plugin(tauri_plugin_opener::init()) .invoke_handler(tauri::generate_handler![greet]) .run(tauri::generate_context!())