Vercel/src/state/AppStateProvider.tsx
2025-03-01 11:18:36 -06:00

231 lines
6.8 KiB
TypeScript

'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<AnimationConfig>();
const [shouldRespondToKeyboardCommands, setShouldRespondToKeyboardCommands] =
useState(true);
// UPLOAD
const uploadInputRef = useRef<HTMLInputElement>(null);
const [uploadState, _setUploadState] =
useState(INITIAL_UPLOAD_STATE);
// MODAL
const [isCommandKOpen, setIsCommandKOpen] =
useState(false);
const [shareModalProps, setShareModalProps] =
useState<ShareModalProps>();
// AUTH
const [userEmail, setUserEmail] =
useState<string>();
const [isUserSignedInEager, setIsUserSignedInEager] =
useState(false);
// ADMIN
const [adminUpdateTimes, setAdminUpdateTimes] =
useState<Date[]>([]);
const [photosCount, setPhotosCount] =
useState<number>();
const [photosCountHidden, setPhotosCountHidden] =
useState<number>();
const [uploadsCount, setUploadsCount] =
useState<number>();
const [tagsCount, setTagsCount] =
useState<number>();
const [selectedPhotoIds, setSelectedPhotoIds] =
useState<string[] | undefined>();
const [isPerformingSelectEdit, setIsPerformingSelectEdit] =
useState(false);
const [insightIndicatorStatus, setInsightIndicatorStatus] =
useState<InsightIndicatorStatus>();
// 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<UploadState>) => {
_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 (
<AppStateContext.Provider
value={{
// CORE
previousPathname,
hasLoaded,
setHasLoaded,
swrTimestamp,
invalidateSwr,
nextPhotoAnimation,
setNextPhotoAnimation,
clearNextPhotoAnimation: () => 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}
</AppStateContext.Provider>
);
};