diff --git a/app/src/components/floating-timer.tsx b/app/src/components/floating-timer.tsx
index 01d40c60..a1b90f9d 100644
--- a/app/src/components/floating-timer.tsx
+++ b/app/src/components/floating-timer.tsx
@@ -55,6 +55,10 @@ export const FloatingTimer = () => {
retry: 1,
});
const timer = timerResponse?.timer;
+ const timerForEditDialog = React.useMemo(
+ () => (timer ? { ...timer, note: userNote } : null),
+ [timer, userNote],
+ );
const { mutate: startTimer } = timeTrackingMutations.useStartTimer();
const { mutate: stopTimer, isPending: isStoppingTimer } =
@@ -380,12 +384,12 @@ export const FloatingTimer = () => {
)}
- {timer && (
+ {timerForEditDialog && (
)}
>
diff --git a/app/src/lib/api/mutations/time-tracking.ts b/app/src/lib/api/mutations/time-tracking.ts
index 7b358b93..1476f022 100644
--- a/app/src/lib/api/mutations/time-tracking.ts
+++ b/app/src/lib/api/mutations/time-tracking.ts
@@ -2,7 +2,11 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
import { api } from "../api";
import { DefaultMutationOptions, MutationFnAsync } from "./mutations";
import { z } from "zod";
-import { timeTrackingQueries } from "../queries/time-tracking";
+import {
+ GetTimerResponse,
+ TimerResponse,
+ timeTrackingQueries,
+} from "../queries/time-tracking";
import { useTimeTrackingActions } from "@/hooks/useTimeTrackingStore";
export const timeTrackingMutations = {
@@ -127,8 +131,24 @@ function useSaveTimer(options?: DefaultMutationOptions) {
});
}
+function mergeOptimisticTimerEdit(
+ timer: TimerResponse,
+ body: EditTimerPayload,
+): TimerResponse {
+ return {
+ ...timer,
+ note: body.userNote ?? timer.note,
+ projectId: body.projectId ?? timer.projectId,
+ projectName: body.projectName ?? timer.projectName,
+ activityId: body.activityId ?? timer.activityId,
+ activityName: body.activityName ?? timer.activityName,
+ startTime: body.startTime ?? timer.startTime,
+ };
+}
+
function useEditTimer(options?: DefaultMutationOptions) {
const queryClient = useQueryClient();
+ const timerQueryKey = timeTrackingQueries.getTimer().queryKey;
return useMutation({
mutationKey: ["time-tracking", "editTimer"],
@@ -137,11 +157,48 @@ function useEditTimer(options?: DefaultMutationOptions) {
json: body,
}),
...options,
+ onMutate: async (vars) => {
+ await queryClient.cancelQueries({
+ queryKey: timerQueryKey,
+ });
+
+ const previousTimer =
+ queryClient.getQueryData(timerQueryKey);
+
+ queryClient.setQueryData(
+ timerQueryKey,
+ (current) =>
+ current?.timer
+ ? {
+ ...current,
+ timer: mergeOptimisticTimerEdit(current.timer, vars),
+ }
+ : current,
+ );
+
+ const optionsContext = await options?.onMutate?.(vars);
+ return { previousTimer, optionsContext } satisfies {
+ previousTimer: GetTimerResponse | undefined;
+ optionsContext: unknown;
+ };
+ },
onSuccess: (data, v, c) => {
+ options?.onSuccess?.(data, v, c?.optionsContext);
+ },
+ onError: (error, v, c) => {
+ if (c?.previousTimer !== undefined) {
+ queryClient.setQueryData(
+ timerQueryKey,
+ c.previousTimer,
+ );
+ }
+ options?.onError?.(error, v, c?.optionsContext);
+ },
+ onSettled: (data, error, v, c) => {
queryClient.invalidateQueries({
- queryKey: timeTrackingQueries.getTimer().queryKey,
+ queryKey: timerQueryKey,
});
- options?.onSuccess?.(data, v, c);
+ options?.onSettled?.(data, error, v, c?.optionsContext);
},
});
}