UMBRA/frontend/src/hooks/useCalendars.ts
Kyle Pope 2ab7121e42 Phase 4: Frontend performance optimizations
- AW-2: Scope calendar events fetch to visible date range via start/end
  query params, leveraging existing backend support
- AW-3: Reduce calendar events poll from 5s to 30s (personal organiser
  doesn't need 12 API calls/min)
- AS-4: Gate shared-calendar polling on hasSharedCalendars — saves 12
  wasted API calls/min for personal-only users
- AS-2: Lazy-load all route components with React.lazy() — only
  AdminPortal was previously lazy, now all 10 routes are code-split
- AS-1: Add Vite manualChunks to split FullCalendar (~400KB), React,
  TanStack Query, and UI libs into separate cacheable chunks
- AS-3: Extract clockNow into isolated ClockDisplay memo component —
  prevents all 8 dashboard widgets from re-rendering every minute

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

53 lines
1.8 KiB
TypeScript

import { useMemo, useRef } 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.
// Use a ref to latch "has sharing" once discovered, so polling doesn't flicker.
const hasSharingRef = useRef(false);
const ownsShared = (ownedQuery.data ?? []).some((c) => c.is_shared);
if (ownsShared) hasSharingRef.current = true;
const sharedQuery = useQuery({
queryKey: ['calendars', 'shared'],
queryFn: async () => {
const { data } = await api.get<SharedCalendarMembership[]>('/shared-calendars');
return data;
},
refetchInterval: pollingEnabled && hasSharingRef.current ? 5_000 : false,
staleTime: 3_000,
});
// Also latch if user is a member of others' shared calendars
if ((sharedQuery.data ?? []).length > 0) hasSharingRef.current = 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,
};
}