support set interval
This commit is contained in:
@@ -10,9 +10,15 @@ use tauri_plugin_store::StoreExt;
|
||||
|
||||
pub struct AppState {
|
||||
pub is_paused: Arc<AtomicBool>,
|
||||
pub capture_interval_secs: std::sync::atomic::AtomicU64,
|
||||
pub toggle_menu_item: std::sync::Mutex<Option<tauri::menu::MenuItem<tauri::Wry>>>,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn update_interval(state: tauri::State<'_, AppState>, seconds: u64) {
|
||||
state.capture_interval_secs.store(seconds, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn toggle_pause(app: tauri::AppHandle, state: tauri::State<'_, AppState>) -> bool {
|
||||
let current = state.is_paused.load(Ordering::SeqCst);
|
||||
@@ -78,26 +84,27 @@ pub fn get_timeline(date: String, base_dir: String) -> Vec<serde_json::Value> {
|
||||
}
|
||||
|
||||
pub fn start_engine(app: AppHandle) {
|
||||
let state = app.state::<AppState>();
|
||||
let is_paused = state.is_paused.clone();
|
||||
|
||||
// Start Cleanup routine
|
||||
let app_clone = app.clone();
|
||||
let app_cleanup = app.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
// Run once on startup, then every 24 hours
|
||||
let mut ticker = interval(Duration::from_secs(60 * 60 * 24));
|
||||
loop {
|
||||
ticker.tick().await;
|
||||
if let Err(e) = run_cleanup(&app_clone).await {
|
||||
if let Err(e) = run_cleanup(&app_cleanup).await {
|
||||
eprintln!("Cleanup error: {}", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let app_capture = app.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
// Check every 500ms to be precise about the 0th second
|
||||
let state = app_capture.state::<AppState>();
|
||||
let is_paused = state.is_paused.clone();
|
||||
|
||||
// Check frequently to catch the aligned second
|
||||
let mut ticker = interval(Duration::from_millis(500));
|
||||
let mut last_minute = -1;
|
||||
let mut last_capture_time = 0;
|
||||
|
||||
loop {
|
||||
ticker.tick().await;
|
||||
@@ -106,16 +113,20 @@ pub fn start_engine(app: AppHandle) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let now = Local::now();
|
||||
let current_minute = now.format("%M").to_string().parse::<i32>().unwrap_or(0);
|
||||
let current_second = now.format("%S").to_string().parse::<i32>().unwrap_or(0);
|
||||
let interval_secs = state.capture_interval_secs.load(Ordering::SeqCst);
|
||||
if interval_secs == 0 { continue; }
|
||||
|
||||
// Trigger only if it's the 0th second and we haven't captured this minute yet
|
||||
if current_second == 0 && current_minute != last_minute {
|
||||
last_minute = current_minute;
|
||||
println!("Tick: capturing screen at {:02}:{:02}:00...", now.format("%H"), current_minute);
|
||||
if let Err(e) = capture_screens(&app).await {
|
||||
let now = Local::now();
|
||||
let timestamp = now.timestamp() as u64;
|
||||
|
||||
// Trigger if aligned to interval and we haven't captured this exact second yet
|
||||
if timestamp % interval_secs == 0 && timestamp != last_capture_time {
|
||||
last_capture_time = timestamp;
|
||||
println!("Tick: capturing screen at {}...", now.format("%H:%M:%S"));
|
||||
if let Err(e) = capture_screens(&app_capture).await {
|
||||
eprintln!("Failed to capture screens: {}", e);
|
||||
} else {
|
||||
let _ = app_capture.emit("refresh-timeline", ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,11 +20,13 @@ pub fn run() {
|
||||
engine::toggle_pause,
|
||||
engine::get_pause_state,
|
||||
engine::get_timeline,
|
||||
engine::get_image_base64
|
||||
engine::get_image_base64,
|
||||
engine::update_interval
|
||||
])
|
||||
.setup(|app| {
|
||||
app.manage(engine::AppState {
|
||||
is_paused: Arc::new(AtomicBool::new(false)),
|
||||
capture_interval_secs: std::sync::atomic::AtomicU64::new(30),
|
||||
toggle_menu_item: std::sync::Mutex::new(None),
|
||||
});
|
||||
tray::create_tray(app.handle())?;
|
||||
|
||||
19
src/App.vue
19
src/App.vue
@@ -29,6 +29,7 @@ const isFullscreen = ref(false);
|
||||
const isSettingsOpen = ref(false);
|
||||
const mergeScreens = ref(false);
|
||||
const retainDays = ref(30);
|
||||
const captureInterval = ref(30);
|
||||
|
||||
const hoveredTime = ref<string | null>(null);
|
||||
const timelineRef = ref<HTMLElement | null>(null);
|
||||
@@ -45,6 +46,11 @@ onMounted(async () => {
|
||||
isSetupComplete.value = true;
|
||||
mergeScreens.value = (await store.get("mergeScreens")) as boolean || false;
|
||||
retainDays.value = (await store.get("retainDays")) as number || 30;
|
||||
captureInterval.value = (await store.get("captureInterval")) as number || 30;
|
||||
|
||||
// Sync initial interval to Rust
|
||||
await invoke("update_interval", { seconds: captureInterval.value });
|
||||
|
||||
isPaused.value = await invoke("get_pause_state");
|
||||
await loadTimeline();
|
||||
}
|
||||
@@ -77,7 +83,11 @@ const selectFolder = async () => {
|
||||
const updateSettings = async () => {
|
||||
await store.set("mergeScreens", mergeScreens.value);
|
||||
await store.set("retainDays", retainDays.value);
|
||||
await store.set("captureInterval", captureInterval.value);
|
||||
await store.save();
|
||||
|
||||
// Sync interval to Rust
|
||||
await invoke("update_interval", { seconds: captureInterval.value });
|
||||
};
|
||||
|
||||
const togglePauseState = async () => {
|
||||
@@ -325,6 +335,15 @@ const handleTimelineClick = (e: MouseEvent) => {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div class="flex justify-between items-end">
|
||||
<label class="text-sm font-bold text-[#86868B] uppercase tracking-widest ml-1">Capture Interval</label>
|
||||
<span class="text-lg font-black text-[#007AFF]">{{ captureInterval }} Seconds</span>
|
||||
</div>
|
||||
<input type="range" v-model.number="captureInterval" min="10" max="600" step="10" @change="updateSettings" class="w-full h-2 bg-[#E5E5E7] rounded-full appearance-none cursor-pointer accent-[#007AFF]" />
|
||||
<p class="text-[11px] text-[#86868B] font-medium leading-normal text-center">Frequency of snapshots (10s to 10m).</p>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div class="flex justify-between items-end">
|
||||
<label class="text-sm font-bold text-[#86868B] uppercase tracking-widest ml-1">Retention Policy</label>
|
||||
|
||||
Reference in New Issue
Block a user