800dee31b2
- Added boto3 as a dependency in pyproject.toml and uv.lock. - Introduced multiple new exercise images in various formats (jpg, webp, avif, png). - Added new machine images to enhance the workout assets library.
104 lines
3.3 KiB
TypeScript
104 lines
3.3 KiB
TypeScript
import type { QueryClient } from "@tanstack/react-query";
|
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
|
|
import { ApiError } from "../../api";
|
|
import type { CatalogKind, WorkoutSetInput } from "../../types";
|
|
import { useAuth } from "../auth/AuthContext";
|
|
import { workoutApi } from "./api";
|
|
|
|
export async function invalidateWorkoutQueries(queryClient: QueryClient) {
|
|
await Promise.all([
|
|
queryClient.invalidateQueries({ queryKey: ["workout", "active"] }),
|
|
queryClient.invalidateQueries({ queryKey: ["workouts"] }),
|
|
queryClient.invalidateQueries({ queryKey: ["calories"] }),
|
|
queryClient.invalidateQueries({ queryKey: ["progression"] }),
|
|
]);
|
|
}
|
|
|
|
export function useActiveWorkout() {
|
|
const { auth } = useAuth();
|
|
return useQuery({ queryKey: ["workout", "active"], queryFn: () => workoutApi.active(auth.accessToken) });
|
|
}
|
|
|
|
export function useWorkoutMutations(options: { onStartConflict?: () => void; onFinish?: () => void; onDiscard?: () => void } = {}) {
|
|
const { auth } = useAuth();
|
|
const token = auth.accessToken;
|
|
const queryClient = useQueryClient();
|
|
|
|
const refresh = () => invalidateWorkoutQueries(queryClient);
|
|
|
|
const startWorkout = useMutation({
|
|
mutationFn: () => workoutApi.start(token),
|
|
onSuccess: refresh,
|
|
onError: async (error) => {
|
|
if (error instanceof ApiError && error.status === 409) {
|
|
await refresh();
|
|
options.onStartConflict?.();
|
|
}
|
|
},
|
|
});
|
|
|
|
const addWorkoutItem = useMutation({
|
|
mutationFn: ({ workoutId, sourceId }: { workoutId: string; sourceId: string; kind: CatalogKind }) =>
|
|
workoutApi.addItem(token, workoutId, {
|
|
activity_source_id: sourceId,
|
|
exercise_id: null,
|
|
equipment_id: null,
|
|
}),
|
|
onSuccess: refresh,
|
|
});
|
|
|
|
const recordWorkoutSet = useMutation({
|
|
mutationFn: ({ itemId, payload }: { itemId: string; payload: WorkoutSetInput }) => workoutApi.addSet(token, itemId, payload),
|
|
onSuccess: refresh,
|
|
});
|
|
|
|
const recordWorkoutSetsBatch = useMutation({
|
|
mutationFn: ({ itemId, sets }: { itemId: string; sets: WorkoutSetInput[] }) => workoutApi.addSetBatch(token, itemId, sets),
|
|
onSuccess: refresh,
|
|
});
|
|
|
|
const removeWorkoutItem = useMutation({
|
|
mutationFn: (itemId: string) => workoutApi.removeItem(token, itemId),
|
|
onSuccess: refresh,
|
|
});
|
|
|
|
const removeWorkoutSet = useMutation({
|
|
mutationFn: ({ itemId, setId }: { itemId: string; setId: string }) => workoutApi.removeSet(token, itemId, setId),
|
|
onSuccess: refresh,
|
|
});
|
|
|
|
const updateWorkoutSet = useMutation({
|
|
mutationFn: ({ setId, payload }: { setId: string; payload: Partial<WorkoutSetInput> }) => workoutApi.updateSet(token, setId, payload),
|
|
onSuccess: refresh,
|
|
});
|
|
|
|
const finishWorkout = useMutation({
|
|
mutationFn: ({ workoutId, notes }: { workoutId: string; notes?: string }) => workoutApi.finish(token, workoutId, notes),
|
|
onSuccess: async () => {
|
|
await refresh();
|
|
options.onFinish?.();
|
|
},
|
|
});
|
|
|
|
const discardWorkout = useMutation({
|
|
mutationFn: (workoutId: string) => workoutApi.discard(token, workoutId),
|
|
onSuccess: async () => {
|
|
await refresh();
|
|
options.onDiscard?.();
|
|
},
|
|
});
|
|
|
|
return {
|
|
startWorkout,
|
|
addWorkoutItem,
|
|
recordWorkoutSet,
|
|
recordWorkoutSetsBatch,
|
|
removeWorkoutItem,
|
|
removeWorkoutSet,
|
|
updateWorkoutSet,
|
|
finishWorkout,
|
|
discardWorkout,
|
|
};
|
|
}
|