This commit is contained in:
simon.franken
2026-02-16 11:01:07 +01:00
parent 7d678c1c4d
commit d3b8df3deb
8 changed files with 476 additions and 563 deletions

View File

@@ -1,14 +1,7 @@
import {
createContext,
useContext,
useState,
useEffect,
useCallback,
type ReactNode,
} from 'react';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { authApi } from '@/api/auth';
import type { User } from '@/types';
import { createContext, useContext, useCallback, type ReactNode } from "react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { authApi } from "@/api/auth";
import type { User } from "@/types";
interface AuthContextType {
user: User | null;
@@ -23,9 +16,9 @@ const AuthContext = createContext<AuthContextType | undefined>(undefined);
export function AuthProvider({ children }: { children: ReactNode }) {
const queryClient = useQueryClient();
const { data: user, isLoading } = useQuery({
queryKey: ['currentUser'],
queryKey: ["currentUser"],
queryFn: authApi.getCurrentUser,
staleTime: 5 * 60 * 1000, // 5 minutes
});
@@ -36,12 +29,12 @@ export function AuthProvider({ children }: { children: ReactNode }) {
const logout = useCallback(async () => {
await authApi.logout();
queryClient.setQueryData(['currentUser'], null);
queryClient.setQueryData(["currentUser"], null);
queryClient.clear();
}, [queryClient]);
const refetchUser = useCallback(async () => {
await queryClient.invalidateQueries({ queryKey: ['currentUser'] });
await queryClient.invalidateQueries({ queryKey: ["currentUser"] });
}, [queryClient]);
return (
@@ -63,7 +56,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
export function useAuth() {
const context = useContext(AuthContext);
if (context === undefined) {
throw new Error('useAuth must be used within an AuthProvider');
throw new Error("useAuth must be used within an AuthProvider");
}
return context;
}
}

View File

@@ -5,10 +5,10 @@ import {
useEffect,
useCallback,
type ReactNode,
} from 'react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { timerApi } from '@/api/timer';
import type { OngoingTimer, TimeEntry } from '@/types';
} from "react";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { timerApi } from "@/api/timer";
import type { OngoingTimer, TimeEntry } from "@/types";
interface TimerContextType {
ongoingTimer: OngoingTimer | null;
@@ -24,10 +24,12 @@ const TimerContext = createContext<TimerContextType | undefined>(undefined);
export function TimerProvider({ children }: { children: ReactNode }) {
const queryClient = useQueryClient();
const [elapsedSeconds, setElapsedSeconds] = useState(0);
const [elapsedInterval, setElapsedInterval] = useState<NodeJS.Timeout | null>(null);
const [elapsedInterval, setElapsedInterval] = useState<ReturnType<
typeof setInterval
> | null>(null);
const { data: ongoingTimer, isLoading } = useQuery({
queryKey: ['ongoingTimer'],
queryKey: ["ongoingTimer"],
queryFn: timerApi.getOngoing,
refetchInterval: 60000, // Refetch every minute to sync with server
});
@@ -64,7 +66,7 @@ export function TimerProvider({ children }: { children: ReactNode }) {
const startMutation = useMutation({
mutationFn: timerApi.start,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['ongoingTimer'] });
queryClient.invalidateQueries({ queryKey: ["ongoingTimer"] });
},
});
@@ -72,7 +74,7 @@ export function TimerProvider({ children }: { children: ReactNode }) {
const updateMutation = useMutation({
mutationFn: timerApi.update,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['ongoingTimer'] });
queryClient.invalidateQueries({ queryKey: ["ongoingTimer"] });
},
});
@@ -80,8 +82,8 @@ export function TimerProvider({ children }: { children: ReactNode }) {
const stopMutation = useMutation({
mutationFn: timerApi.stop,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['ongoingTimer'] });
queryClient.invalidateQueries({ queryKey: ['timeEntries'] });
queryClient.invalidateQueries({ queryKey: ["ongoingTimer"] });
queryClient.invalidateQueries({ queryKey: ["timeEntries"] });
},
});
@@ -89,14 +91,14 @@ export function TimerProvider({ children }: { children: ReactNode }) {
async (projectId?: string) => {
await startMutation.mutateAsync(projectId);
},
[startMutation]
[startMutation],
);
const updateTimerProject = useCallback(
async (projectId?: string | null) => {
await updateMutation.mutateAsync(projectId);
},
[updateMutation]
[updateMutation],
);
const stopTimer = useCallback(
@@ -108,7 +110,7 @@ export function TimerProvider({ children }: { children: ReactNode }) {
return null;
}
},
[stopMutation]
[stopMutation],
);
return (
@@ -130,7 +132,7 @@ export function TimerProvider({ children }: { children: ReactNode }) {
export function useTimer() {
const context = useContext(TimerContext);
if (context === undefined) {
throw new Error('useTimer must be used within a TimerProvider');
throw new Error("useTimer must be used within a TimerProvider");
}
return context;
}
}