summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/TimerView.tsx23
-rw-r--r--src/hooks/useTimerEvents.ts44
-rw-r--r--src/store/audioStore.ts14
-rw-r--r--src/store/taskStore.ts7
-rw-r--r--src/store/timerStore.ts25
5 files changed, 68 insertions, 45 deletions
diff --git a/src/components/TimerView.tsx b/src/components/TimerView.tsx
index 896c03a..b43973c 100644
--- a/src/components/TimerView.tsx
+++ b/src/components/TimerView.tsx
@@ -54,6 +54,7 @@ export function TimerView() {
const { phase, remainingSecs, totalSecs, running, sessionCount, currentTaskId } =
useTimerStore();
const tasks = useTaskStore((s) => s.tasks);
+ const syncFromBackend = useTimerStore((s) => s.syncFromBackend);
const currentTask = tasks.find((t) => t.id === currentTaskId) ?? null;
@@ -61,10 +62,22 @@ export function TimerView() {
const dashOffset = CIRCUMFERENCE * (1 - progress);
const arcColor = phaseColor(phase);
- const handleStart = () => invoke('start_timer');
- const handlePause = () => invoke('pause_timer');
- const handleSkip = () => invoke('skip_phase');
- const handleReset = () => invoke('reset_timer');
+ const handleStart = async () => {
+ await invoke('start_timer');
+ await syncFromBackend();
+ };
+ const handlePause = async () => {
+ await invoke('pause_timer');
+ await syncFromBackend();
+ };
+ const handleSkip = async () => {
+ await invoke('skip_phase');
+ await syncFromBackend();
+ };
+ const handleReset = async () => {
+ await invoke('reset_timer');
+ await syncFromBackend();
+ };
return (
<div
@@ -200,7 +213,7 @@ function GhostButton({
onClick,
children,
}: {
- onClick: () => void;
+ onClick: () => Promise<void>;
children: React.ReactNode;
}) {
return (
diff --git a/src/hooks/useTimerEvents.ts b/src/hooks/useTimerEvents.ts
index aebc780..a0d5154 100644
--- a/src/hooks/useTimerEvents.ts
+++ b/src/hooks/useTimerEvents.ts
@@ -1,6 +1,5 @@
import { useEffect, useRef } from 'react';
import { listen } from '@tauri-apps/api/event';
-import { invoke } from '@tauri-apps/api/core';
import { useTimerStore, TimerTickPayload } from '../store/timerStore';
import { useTaskStore } from '../store/taskStore';
@@ -9,15 +8,6 @@ interface PhaseChangedPayload {
session_count: number;
}
-interface TimerStatus {
- phase: 'work' | 'short_break' | 'long_break';
- remaining_secs: number;
- total_secs: number;
- running: boolean;
- session_count: number;
- current_task_id: string | null;
-}
-
interface CompletedPayload {
task_id: string | null;
}
@@ -25,7 +15,7 @@ interface CompletedPayload {
export function useTimerEvents(
onCompleted: (taskId: string | null) => void,
) {
- const setTimerTick = useTimerStore((s) => s.setTimerTick);
+ const syncFromBackend = useTimerStore((s) => s.syncFromBackend);
const setRunning = useTimerStore((s) => s.setRunning);
const fetchTasks = useTaskStore((s) => s.fetchTasks);
@@ -41,16 +31,8 @@ export function useTimerEvents(
async function setup() {
// Bootstrap initial state from backend
try {
- const status = await invoke<TimerStatus>('get_timer_status');
+ await syncFromBackend();
if (cancelled) return;
- setTimerTick({
- phase: status.phase,
- remaining_secs: status.remaining_secs,
- total_secs: status.total_secs,
- session_count: status.session_count,
- current_task_id: status.current_task_id,
- });
- setRunning(status.running);
} catch (e) {
console.error('Failed to get timer status:', e);
}
@@ -61,24 +43,24 @@ export function useTimerEvents(
try {
const [unlistenTick, unlistenCompleted, unlistenPhaseChanged] = await Promise.all([
listen<TimerTickPayload>('timer-tick', (event) => {
- setTimerTick(event.payload);
- setRunning(true);
+ useTimerStore.setState({
+ phase: event.payload.phase,
+ remainingSecs: event.payload.remaining_secs,
+ totalSecs: event.payload.total_secs,
+ running: true,
+ sessionCount: event.payload.session_count,
+ currentTaskId: event.payload.current_task_id,
+ });
}),
listen<CompletedPayload>('timer-completed', async (event) => {
setRunning(false);
onCompletedRef.current(event.payload.task_id ?? null);
await fetchTasks();
+ await syncFromBackend();
}),
listen<PhaseChangedPayload>('timer-phase-changed', async (_event) => {
try {
- const status = await invoke<TimerStatus>('get_timer_status');
- setTimerTick({
- phase: status.phase,
- remaining_secs: status.remaining_secs,
- total_secs: status.total_secs,
- session_count: status.session_count,
- current_task_id: status.current_task_id,
- });
+ await syncFromBackend();
} catch (e) {
console.error('Failed to re-sync timer status:', e);
}
@@ -104,5 +86,5 @@ export function useTimerEvents(
cancelled = true;
unlisteners.forEach((fn) => fn());
};
- }, [setTimerTick, setRunning, fetchTasks]); // onCompleted excluded — updated via ref
+ }, [syncFromBackend, setRunning, fetchTasks]); // onCompleted excluded — updated via ref
}
diff --git a/src/store/audioStore.ts b/src/store/audioStore.ts
index 39cbf8b..36d593d 100644
--- a/src/store/audioStore.ts
+++ b/src/store/audioStore.ts
@@ -53,15 +53,15 @@ export const useAudioStore = create<AudioStore>((set, get) => ({
try {
if (sound === 'none') {
await invoke('stop_ambient');
- set({ playing: false, sound: 'none' });
+ await get().fetchStatus();
return;
}
await invoke('play_ambient', { sound });
- set({ available: true, playing: true, sound });
+ await get().fetchStatus();
} catch (error) {
console.error('play_ambient error:', error);
- set({ available: false, playing: false, sound: 'none' });
+ await get().fetchStatus();
}
},
@@ -71,14 +71,10 @@ export const useAudioStore = create<AudioStore>((set, get) => ({
try {
await invoke('set_ambient_volume', { volume: nextVolume });
- set({ available: true });
+ await get().fetchStatus();
} catch (error) {
console.error('set_ambient_volume error:', error);
- set({
- available: false,
- playing: false,
- sound: get().sound,
- });
+ await get().fetchStatus();
}
},
}));
diff --git a/src/store/taskStore.ts b/src/store/taskStore.ts
index 94386ac..2a3a8be 100644
--- a/src/store/taskStore.ts
+++ b/src/store/taskStore.ts
@@ -1,5 +1,6 @@
import { create } from 'zustand';
import { invoke } from '@tauri-apps/api/core';
+import { useTimerStore } from './timerStore';
export interface Task {
id: string;
@@ -79,5 +80,11 @@ export const useTaskStore = create<TaskStore>((set) => ({
setCurrentTask: async (id) => {
await invoke('set_current_task', { taskId: id });
+ useTimerStore.getState().setCurrentTaskId(id);
+ try {
+ await useTimerStore.getState().syncFromBackend();
+ } catch (e) {
+ console.error('syncFromBackend after set_current_task error:', e);
+ }
},
}));
diff --git a/src/store/timerStore.ts b/src/store/timerStore.ts
index 6a81e20..79170a4 100644
--- a/src/store/timerStore.ts
+++ b/src/store/timerStore.ts
@@ -1,3 +1,4 @@
+import { invoke } from '@tauri-apps/api/core';
import { create } from 'zustand';
export type TimerPhase = 'work' | 'short_break' | 'long_break';
@@ -10,6 +11,15 @@ export interface TimerTickPayload {
current_task_id: string | null;
}
+export interface TimerStatus {
+ phase: TimerPhase;
+ remaining_secs: number;
+ total_secs: number;
+ running: boolean;
+ session_count: number;
+ current_task_id: string | null;
+}
+
interface TimerState {
phase: TimerPhase;
remainingSecs: number;
@@ -17,8 +27,10 @@ interface TimerState {
running: boolean;
sessionCount: number;
currentTaskId: string | null;
+ syncFromBackend: () => Promise<void>;
setTimerTick: (payload: TimerTickPayload) => void;
setRunning: (running: boolean) => void;
+ setCurrentTaskId: (currentTaskId: string | null) => void;
}
export const useTimerStore = create<TimerState>((set) => ({
@@ -29,6 +41,18 @@ export const useTimerStore = create<TimerState>((set) => ({
sessionCount: 0,
currentTaskId: null,
+ syncFromBackend: async () => {
+ const status = await invoke<TimerStatus>('get_timer_status');
+ set({
+ phase: status.phase,
+ remainingSecs: status.remaining_secs,
+ totalSecs: status.total_secs,
+ running: status.running,
+ sessionCount: status.session_count,
+ currentTaskId: status.current_task_id,
+ });
+ },
+
setTimerTick: (payload) =>
set({
phase: payload.phase,
@@ -39,4 +63,5 @@ export const useTimerStore = create<TimerState>((set) => ({
}),
setRunning: (running) => set({ running }),
+ setCurrentTaskId: (currentTaskId) => set({ currentTaskId }),
}));