This commit is contained in:
2026-03-04 14:16:51 +01:00
parent 1cca200eda
commit 765aa83fb6
24 changed files with 1370 additions and 424 deletions

View File

@@ -0,0 +1,66 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import type { NormalizedTimeline } from "@/functions/get-timeline";
import { updateTimelineItem } from "@/functions/update-timeline-item";
type EntryResizedVars = {
entryId: string;
newStart: string;
newEnd: string | null;
groupId: string;
lane: number;
};
export function useEntryResizedMutation(timelineId: string) {
const queryClient = useQueryClient();
const queryKey = ["timeline", timelineId];
return useMutation({
mutationFn: (vars: EntryResizedVars) =>
updateTimelineItem({
data: {
id: vars.entryId,
start: vars.newStart,
end: vars.newEnd,
timelineGroupId: vars.groupId,
lane: vars.lane,
},
}),
onMutate: async (vars) => {
// Cancel in-flight fetches so they don't overwrite our optimistic update
await queryClient.cancelQueries({ queryKey });
const previous = queryClient.getQueryData<NormalizedTimeline>(queryKey);
queryClient.setQueryData<NormalizedTimeline>(queryKey, (old) => {
if (!old) return old;
return {
...old,
items: {
...old.items,
[vars.entryId]: {
...old.items[vars.entryId],
start: new Date(vars.newStart),
end: vars.newEnd ? new Date(vars.newEnd) : null,
lane: vars.lane,
},
},
};
});
return { previous };
},
onError: (_err, _vars, context) => {
// Roll back to the previous cache state on server error
if (context?.previous) {
queryClient.setQueryData(queryKey, context.previous);
}
},
onSettled: () => {
// Re-fetch from server to ensure consistency
queryClient.invalidateQueries({ queryKey });
},
});
}

View File

@@ -55,4 +55,14 @@ export type FlutterEvent =
newGroupId: string;
newLane: number;
};
}
| {
type: "entry_resized";
payload: {
entryId: string;
newStart: string;
newEnd: string | null;
groupId: string;
lane: number;
};
};

View File

@@ -5,6 +5,7 @@ import type { FlutterEvent, FlutterTimelineState } from "@/lib/flutter-bridge";
import { timelineQueryOptions } from "@/functions/get-timeline";
import { FlutterView } from "@/components/flutter-view";
import { useEntryMovedMutation } from "@/hooks/use-entry-moved-mutation";
import { useEntryResizedMutation } from "@/hooks/use-entry-resized-mutation";
import { useTheme } from "@/lib/theme";
export const Route = createFileRoute("/timeline/$timelineId")({
@@ -22,6 +23,7 @@ function RouteComponent() {
const [selectedItemId, setSelectedItemId] = useState<string | null>(null);
const [flutterHeight, setFlutterHeight] = useState<number | undefined>();
const entryMoved = useEntryMovedMutation(timelineId);
const entryResized = useEntryResizedMutation(timelineId);
const { theme } = useTheme();
const flutterState: FlutterTimelineState = useMemo(
@@ -64,9 +66,12 @@ function RouteComponent() {
case "entry_moved":
entryMoved.mutate(event.payload);
break;
case "entry_resized":
entryResized.mutate(event.payload);
break;
}
},
[entryMoved]
[entryMoved, entryResized]
);
return (