feat(frontend): dedupe /public/settings via shared usePublicSettings hook (#325)
Pre-dedup: 7 calls to /api/public/settings on a single /admin/login page load — 4 from raw-fetch consumers + 3 from React Query consumers using inconsistent queryKeys. Captured live in Chrome DevTools. Post-dedup: 1 call. Verified by tests/e2e/public-settings-dedup.spec.ts. Adds: - frontend/src/hooks/usePublicSettings.ts — single React Query hook, 60s staleTime, queryKey ['public-settings']. Vitest with mocked api proves multi-mount dedup. - Extended PublicSettings interface with seo_meta_* fields used by RobotsMetaTags and branding_logo_* fields used by GalleryView/AdminHeader. Migrates 19 call sites across 4 risk-ordered rounds: - Round A (high fan-in): GlobalThemeProvider, MaintenanceContext (with refetchInterval to preserve maintenance polling), MaintenanceWrapper (drops the now-redundant per-route ping; axios interceptor already handles 503), AdminHeader. - Round B (gallery/login): GalleryView, GalleryPage, ClientAccessPage, AdminLoginPage, MaintenanceMode. - Round C (decorative): RobotsMetaTags, DynamicFavicon, CMSContentBlock, ReCaptcha, useWatermarkSettings (rips out raw fetch + local state), LegalPage. - Round D (queryKey alignment): useLocalizedDate, UserPhotoUpload, CreateEventPage. EventDetailsPage Round D ships in the follow-up commit that adds presigned-download UI on the same page. App.tsx (AnalyticsBootstrap) and EventDetailsPage are deferred to later commits — both files mix #325 changes with backend feature work.
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { setMaintenanceModeCallback } from '../config/api';
|
||||
import { getApiBaseUrl } from '../utils/url';
|
||||
import { usePublicSettings } from '../hooks/usePublicSettings';
|
||||
|
||||
interface MaintenanceContextType {
|
||||
isMaintenanceMode: boolean;
|
||||
@@ -25,34 +24,18 @@ interface MaintenanceProviderProps {
|
||||
export const MaintenanceProvider: React.FC<MaintenanceProviderProps> = ({ children }) => {
|
||||
const [isMaintenanceMode, setIsMaintenanceMode] = useState(false);
|
||||
|
||||
// Check maintenance mode status on mount
|
||||
const { data: settings } = useQuery({
|
||||
queryKey: ['public-settings-maintenance'],
|
||||
queryFn: async () => {
|
||||
try {
|
||||
const response = await fetch(`${getApiBaseUrl()}/public/settings`);
|
||||
if (response.status === 503) {
|
||||
setIsMaintenanceMode(true);
|
||||
return null;
|
||||
}
|
||||
return response.json();
|
||||
} catch (error) {
|
||||
// If we can't reach the server, don't assume maintenance mode
|
||||
return null;
|
||||
}
|
||||
},
|
||||
staleTime: 30 * 1000, // Check every 30 seconds
|
||||
refetchInterval: 30 * 1000,
|
||||
});
|
||||
// Polls /public/settings every 30s so a maintenance flag flipped server-side propagates
|
||||
// without a refresh. 503 responses are caught by the axios interceptor in config/api.ts
|
||||
// (which calls setMaintenanceModeCallback below), so we only need to read the explicit
|
||||
// maintenance_mode flag here.
|
||||
const { data: settings } = usePublicSettings({ refetchInterval: 30_000 });
|
||||
|
||||
// Update maintenance mode based on settings
|
||||
useEffect(() => {
|
||||
if (settings?.maintenance_mode !== undefined) {
|
||||
setIsMaintenanceMode(settings.maintenance_mode);
|
||||
}
|
||||
}, [settings]);
|
||||
|
||||
// Set up the callback for API interceptor
|
||||
useEffect(() => {
|
||||
setMaintenanceModeCallback((enabled: boolean) => {
|
||||
setIsMaintenanceMode(enabled);
|
||||
|
||||
Reference in New Issue
Block a user