UMBRA/frontend/src/hooks/useCalendars.ts
Kyle Pope e270a2f63d Fix team review findings: reactive shared-calendar gate + ReorderItem hardening
- Convert hasSharingRef from useRef to useState in useCalendars so
  refetchInterval reacts immediately when sharing is detected (P-01)
- Add extra="forbid" to ReorderItem schema to prevent mass-assignment (S-03)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 00:50:02 +08:00

54 lines
1.8 KiB
TypeScript

import { useState, useMemo } from 'react';
import { useQuery } from '@tanstack/react-query';
import api from '@/lib/api';
import type { Calendar, SharedCalendarMembership } from '@/types';
interface UseCalendarsOptions {
pollingEnabled?: boolean;
}
export function useCalendars({ pollingEnabled = false }: UseCalendarsOptions = {}) {
const ownedQuery = useQuery({
queryKey: ['calendars'],
queryFn: async () => {
const { data } = await api.get<Calendar[]>('/calendars');
return data;
},
});
// AS-4: Gate shared-calendar polling on whether user participates in sharing.
// Saves ~12 API calls/min for personal-only users.
// Uses useState (not useRef) so changes trigger a re-render and the
// refetchInterval picks up the new value immediately.
const [hasSharing, setHasSharing] = useState(false);
const ownsShared = (ownedQuery.data ?? []).some((c) => c.is_shared);
if (ownsShared && !hasSharing) setHasSharing(true);
const sharedQuery = useQuery({
queryKey: ['calendars', 'shared'],
queryFn: async () => {
const { data } = await api.get<SharedCalendarMembership[]>('/shared-calendars');
return data;
},
refetchInterval: pollingEnabled && hasSharing ? 5_000 : false,
staleTime: 3_000,
});
// Also latch if user is a member of others' shared calendars
if ((sharedQuery.data ?? []).length > 0 && !hasSharing) setHasSharing(true);
const allCalendarIds = useMemo(() => {
const owned = (ownedQuery.data ?? []).map((c) => c.id);
const shared = (sharedQuery.data ?? []).map((m) => m.calendar_id);
return new Set([...owned, ...shared]);
}, [ownedQuery.data, sharedQuery.data]);
return {
data: ownedQuery.data ?? [],
sharedData: sharedQuery.data ?? [],
allCalendarIds,
isLoading: ownedQuery.isLoading,
isLoadingShared: sharedQuery.isLoading,
};
}