'use client'; import { useState, useEffect, ReactNode, useCallback, useRef } from 'react'; import { AppStateContext } from './AppState'; import { AnimationConfig } from '@/components/AnimateItems'; import usePathnames from '@/utility/usePathnames'; import { getAuthAction } from '@/auth/actions'; import useSWR from 'swr'; import { HIGH_DENSITY_GRID, IS_DEVELOPMENT, MATTE_PHOTOS, SHOW_ZOOM_CONTROLS, } from '@/app/config'; import { ShareModalProps } from '@/share'; import { storeTimezoneCookie } from '@/utility/timezone'; import { InsightIndicatorStatus } from '@/admin/insights'; import { getAdminDataAction } from '@/admin/actions'; import { storeAuthEmailCookie, clearAuthEmailCookie, hasAuthEmailCookie, } from '@/auth/client'; import { useRouter } from 'next/navigation'; import { PATH_SIGN_IN } from '@/app/paths'; import { INITIAL_UPLOAD_STATE, UploadState } from '@/admin/upload'; export default function AppStateProvider({ children, }: { children: ReactNode }) { const router = useRouter(); const { previousPathname } = usePathnames(); // CORE const [hasLoaded, setHasLoaded] = useState(false); const [swrTimestamp, setSwrTimestamp] = useState(Date.now()); const [nextPhotoAnimation, setNextPhotoAnimation] = useState(); const [shouldRespondToKeyboardCommands, setShouldRespondToKeyboardCommands] = useState(true); // UPLOAD const uploadInputRef = useRef(null); const [uploadState, _setUploadState] = useState(INITIAL_UPLOAD_STATE); // MODAL const [isCommandKOpen, setIsCommandKOpen] = useState(false); const [shareModalProps, setShareModalProps] = useState(); // AUTH const [userEmail, setUserEmail] = useState(); const [isUserSignedInEager, setIsUserSignedInEager] = useState(false); // ADMIN const [adminUpdateTimes, setAdminUpdateTimes] = useState([]); const [photosCount, setPhotosCount] = useState(); const [photosCountHidden, setPhotosCountHidden] = useState(); const [uploadsCount, setUploadsCount] = useState(); const [tagsCount, setTagsCount] = useState(); const [selectedPhotoIds, setSelectedPhotoIds] = useState(); const [isPerformingSelectEdit, setIsPerformingSelectEdit] = useState(false); const [insightIndicatorStatus, setInsightIndicatorStatus] = useState(); // DEBUG const [isGridHighDensity, setIsGridHighDensity] = useState(HIGH_DENSITY_GRID); const [areZoomControlsShown, setAreZoomControlsShown] = useState(SHOW_ZOOM_CONTROLS); const [arePhotosMatted, setArePhotosMatted] = useState(MATTE_PHOTOS); const [shouldDebugImageFallbacks, setShouldDebugImageFallbacks] = useState(false); const [shouldShowBaselineGrid, setShouldShowBaselineGrid] = useState(false); const [shouldDebugInsights, setShouldDebugInsights] = useState(IS_DEVELOPMENT); const [shouldDebugRecipeOverlays, setShouldDebugRecipeOverlays] = useState(false); const photosCountTotal = ( photosCount !== undefined && photosCountHidden !== undefined ) ? photosCount + photosCountHidden : undefined; const startUpload = useCallback((onStart?: () => void) => { if (uploadInputRef.current) { uploadInputRef.current.value = ''; uploadInputRef.current.click(); uploadInputRef.current.oninput = onStart ?? null; uploadInputRef.current.oncancel = onStart ?? null; } }, []); const setUploadState = useCallback((uploadState: Partial) => { _setUploadState(prev => ({ ...prev, ...uploadState })); }, []); const resetUploadState = useCallback(() => { _setUploadState(INITIAL_UPLOAD_STATE); }, []); const invalidateSwr = useCallback(() => setSwrTimestamp(Date.now()), []); const { data: auth, error: authError } = useSWR('getAuth', getAuthAction); useEffect(() => { setIsUserSignedInEager(hasAuthEmailCookie()); if (!authError) { setUserEmail(auth?.user?.email ?? undefined); } }, [auth, authError]); const isUserSignedIn = Boolean(userEmail); const { data: adminData, mutate: refreshAdminData } = useSWR( isUserSignedIn ? 'getAdminData' : null, getAdminDataAction, { refreshInterval: 1000 * 60, }, ); useEffect(() => { if (userEmail) { storeAuthEmailCookie(userEmail); if (adminData) { setPhotosCount(adminData.countPhotos); setPhotosCountHidden(adminData.countHiddenPhotos); setUploadsCount(adminData.countUploads); setTagsCount(adminData.countTags); setInsightIndicatorStatus(adminData.shouldShowInsightsIndicator); } } else { setPhotosCountHidden(0); } }, [adminData, userEmail]); const registerAdminUpdate = useCallback(() => setAdminUpdateTimes(updates => [...updates, new Date()]) , []); useEffect(() => { setHasLoaded?.(true); storeTimezoneCookie(); }, []); const clearAuthStateAndRedirect = useCallback((shouldRedirect = true) => { setUserEmail(undefined); setIsUserSignedInEager(false); clearAuthEmailCookie(); if (shouldRedirect) { router.push(PATH_SIGN_IN); } }, [router]); return ( setNextPhotoAnimation?.(undefined), shouldRespondToKeyboardCommands, setShouldRespondToKeyboardCommands, // UPLOAD uploadInputRef, startUpload, uploadState, setUploadState, resetUploadState, // MODAL isCommandKOpen, setIsCommandKOpen, shareModalProps, setShareModalProps, // AUTH userEmail, setUserEmail, isUserSignedIn, isUserSignedInEager, clearAuthStateAndRedirect, // ADMIN adminUpdateTimes, registerAdminUpdate, refreshAdminData, photosCount, photosCountHidden, photosCountTotal, uploadsCount, tagsCount, selectedPhotoIds, setSelectedPhotoIds, isPerformingSelectEdit, setIsPerformingSelectEdit, insightIndicatorStatus, setInsightIndicatorStatus, // DEBUG isGridHighDensity, setIsGridHighDensity, areZoomControlsShown, setAreZoomControlsShown, arePhotosMatted, setArePhotosMatted, shouldDebugImageFallbacks, setShouldDebugImageFallbacks, shouldShowBaselineGrid, setShouldShowBaselineGrid, shouldDebugInsights, setShouldDebugInsights, shouldDebugRecipeOverlays, setShouldDebugRecipeOverlays, }} > {children} ); };