Skip to content

Commit e53dd84

Browse files
authored
Fix floating timer blur/edit race (#104)
1 parent e8d91f0 commit e53dd84

2 files changed

Lines changed: 66 additions & 5 deletions

File tree

app/src/components/floating-timer.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ export const FloatingTimer = () => {
5555
retry: 1,
5656
});
5757
const timer = timerResponse?.timer;
58+
const timerForEditDialog = React.useMemo(
59+
() => (timer ? { ...timer, note: userNote } : null),
60+
[timer, userNote],
61+
);
5862

5963
const { mutate: startTimer } = timeTrackingMutations.useStartTimer();
6064
const { mutate: stopTimer, isPending: isStoppingTimer } =
@@ -380,12 +384,12 @@ export const FloatingTimer = () => {
380384
</div>
381385
</div>
382386
)}
383-
{timer && (
387+
{timerForEditDialog && (
384388
<TimerEditDialog
385389
key={`${isEditDialogOpen}`}
386390
open={isEditDialogOpen}
387391
onOpenChange={setIsEditDialogOpen}
388-
timer={timer}
392+
timer={timerForEditDialog}
389393
/>
390394
)}
391395
</>

app/src/lib/api/mutations/time-tracking.ts

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
22
import { api } from "../api";
33
import { DefaultMutationOptions, MutationFnAsync } from "./mutations";
44
import { z } from "zod";
5-
import { timeTrackingQueries } from "../queries/time-tracking";
5+
import {
6+
GetTimerResponse,
7+
TimerResponse,
8+
timeTrackingQueries,
9+
} from "../queries/time-tracking";
610
import { useTimeTrackingActions } from "@/hooks/useTimeTrackingStore";
711

812
export const timeTrackingMutations = {
@@ -127,8 +131,24 @@ function useSaveTimer(options?: DefaultMutationOptions<SaveTimerPayload>) {
127131
});
128132
}
129133

134+
function mergeOptimisticTimerEdit(
135+
timer: TimerResponse,
136+
body: EditTimerPayload,
137+
): TimerResponse {
138+
return {
139+
...timer,
140+
note: body.userNote ?? timer.note,
141+
projectId: body.projectId ?? timer.projectId,
142+
projectName: body.projectName ?? timer.projectName,
143+
activityId: body.activityId ?? timer.activityId,
144+
activityName: body.activityName ?? timer.activityName,
145+
startTime: body.startTime ?? timer.startTime,
146+
};
147+
}
148+
130149
function useEditTimer(options?: DefaultMutationOptions<EditTimerPayload>) {
131150
const queryClient = useQueryClient();
151+
const timerQueryKey = timeTrackingQueries.getTimer().queryKey;
132152

133153
return useMutation({
134154
mutationKey: ["time-tracking", "editTimer"],
@@ -137,11 +157,48 @@ function useEditTimer(options?: DefaultMutationOptions<EditTimerPayload>) {
137157
json: body,
138158
}),
139159
...options,
160+
onMutate: async (vars) => {
161+
await queryClient.cancelQueries({
162+
queryKey: timerQueryKey,
163+
});
164+
165+
const previousTimer =
166+
queryClient.getQueryData<GetTimerResponse>(timerQueryKey);
167+
168+
queryClient.setQueryData<GetTimerResponse | undefined>(
169+
timerQueryKey,
170+
(current) =>
171+
current?.timer
172+
? {
173+
...current,
174+
timer: mergeOptimisticTimerEdit(current.timer, vars),
175+
}
176+
: current,
177+
);
178+
179+
const optionsContext = await options?.onMutate?.(vars);
180+
return { previousTimer, optionsContext } satisfies {
181+
previousTimer: GetTimerResponse | undefined;
182+
optionsContext: unknown;
183+
};
184+
},
140185
onSuccess: (data, v, c) => {
186+
options?.onSuccess?.(data, v, c?.optionsContext);
187+
},
188+
onError: (error, v, c) => {
189+
if (c?.previousTimer !== undefined) {
190+
queryClient.setQueryData<GetTimerResponse>(
191+
timerQueryKey,
192+
c.previousTimer,
193+
);
194+
}
195+
options?.onError?.(error, v, c?.optionsContext);
196+
},
197+
onSettled: (data, error, v, c) => {
141198
queryClient.invalidateQueries({
142-
queryKey: timeTrackingQueries.getTimer().queryKey,
199+
queryKey: timerQueryKey,
143200
});
144-
options?.onSuccess?.(data, v, c);
201+
options?.onSettled?.(data, error, v, c?.optionsContext);
145202
},
146203
});
147204
}

0 commit comments

Comments
 (0)