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<u128>` - add countdown model helpers for start/target epoch-ms conversion - return optional epoch fields from `CountdownService::snapshot` - ignore generated `dist/` directory in `.gitignore`
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -53,3 +53,6 @@ pids
|
||||
debug.log
|
||||
TODO.md
|
||||
.aider*
|
||||
|
||||
# dist folder
|
||||
dist/
|
||||
@@ -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<CountdownService>,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
clock_anchor: ClockAnchor::new(),
|
||||
countdowns: vec![CountdownService::new()],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<u64>,
|
||||
pub target_epoch_ms: Option<u64>,
|
||||
pub start_epoch_ms: Option<u128>,
|
||||
pub target_epoch_ms: Option<u128>,
|
||||
}
|
||||
|
||||
impl CountdownSnapshotDto {
|
||||
@@ -18,8 +18,8 @@ impl CountdownSnapshotDto {
|
||||
label: String,
|
||||
duration: Duration,
|
||||
state: CountdownState,
|
||||
start_epoch_ms: Option<u64>,
|
||||
target_epoch_ms: Option<u64>,
|
||||
start_epoch_ms: Option<u128>,
|
||||
target_epoch_ms: Option<u128>,
|
||||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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<Countdown>,
|
||||
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()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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!())
|
||||
|
||||
Reference in New Issue
Block a user