improved timer system and overlay

This commit is contained in:
2026-01-26 19:19:46 +00:00
parent 72835324e1
commit 11c808ab69
20 changed files with 445 additions and 19 deletions

View File

@@ -1,4 +1,5 @@
import type {
IssueByIdQuery,
IssueCreateRequest,
IssueRecord,
IssueResponse,
@@ -21,6 +22,14 @@ export function useIssues(projectId?: number | null) {
});
}
export function useIssueById(issueId?: IssueByIdQuery["issueId"] | null) {
return useQuery<IssueResponse>({
queryKey: queryKeys.issues.byId(issueId ?? 0),
queryFn: () => issue.byId(issueId ?? 0),
enabled: Boolean(issueId),
});
}
export function useIssueStatusCount(organisationId?: number | null, status?: string | null) {
return useQuery<StatusCountResponse>({
queryKey: queryKeys.issues.statusCount(organisationId ?? 0, status ?? ""),

View File

@@ -1,15 +1,28 @@
import type { TimerEndRequest, TimerState, TimerToggleRequest } from "@sprint/shared";
import type { TimerEndRequest, TimerListItem, TimerState, TimerToggleRequest } from "@sprint/shared";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { queryKeys } from "@/lib/query/keys";
import { timer } from "@/lib/server";
const activeTimersQueryFn = () => timer.list({ activeOnly: true });
export function useActiveTimers(options?: { refetchInterval?: number; enabled?: boolean }) {
return useQuery<TimerListItem[]>({
queryKey: queryKeys.timers.list(),
queryFn: activeTimersQueryFn,
enabled: options?.enabled ?? true,
refetchInterval: options?.refetchInterval,
refetchIntervalInBackground: false,
});
}
export function useTimerState(issueId?: number | null, options?: { refetchInterval?: number }) {
return useQuery<TimerState>({
queryKey: queryKeys.timers.active(issueId ?? 0),
queryFn: () => timer.get(issueId ?? 0),
return useQuery<TimerListItem[], Error, TimerState>({
queryKey: queryKeys.timers.list(),
queryFn: activeTimersQueryFn,
enabled: Boolean(issueId),
refetchInterval: options?.refetchInterval,
refetchIntervalInBackground: false,
select: (timers) => timers.find((timer) => timer.issueId === issueId) ?? null,
});
}
@@ -29,9 +42,9 @@ export function useToggleTimer() {
return useMutation<TimerState, Error, TimerToggleRequest>({
mutationKey: ["timers", "toggle"],
mutationFn: timer.toggle,
onSuccess: (data, variables) => {
queryClient.setQueryData(queryKeys.timers.active(variables.issueId), data);
onSuccess: (_data, variables) => {
queryClient.invalidateQueries({ queryKey: queryKeys.timers.inactive(variables.issueId) });
queryClient.invalidateQueries({ queryKey: queryKeys.timers.list() });
},
});
}
@@ -42,9 +55,9 @@ export function useEndTimer() {
return useMutation<TimerState, Error, TimerEndRequest>({
mutationKey: ["timers", "end"],
mutationFn: timer.end,
onSuccess: (data, variables) => {
queryClient.setQueryData(queryKeys.timers.active(variables.issueId), data);
onSuccess: (_data, variables) => {
queryClient.invalidateQueries({ queryKey: queryKeys.timers.inactive(variables.issueId) });
queryClient.invalidateQueries({ queryKey: queryKeys.timers.list() });
},
});
}

View File

@@ -13,6 +13,7 @@ export const queryKeys = {
issues: {
all: ["issues"] as const,
byProject: (projectId: number) => [...queryKeys.issues.all, "by-project", projectId] as const,
byId: (issueId: number) => [...queryKeys.issues.all, "by-id", issueId] as const,
statusCount: (organisationId: number, status: string) =>
[...queryKeys.issues.all, "status-count", organisationId, status] as const,
typeCount: (organisationId: number, type: string) =>
@@ -30,7 +31,7 @@ export const queryKeys = {
all: ["timers"] as const,
active: (issueId: number) => [...queryKeys.timers.all, "active", issueId] as const,
inactive: (issueId: number) => [...queryKeys.timers.all, "inactive", issueId] as const,
list: (issueId: number) => [...queryKeys.timers.all, "list", issueId] as const,
list: () => [...queryKeys.timers.all, "list"] as const,
},
users: {
all: ["users"] as const,