diff --git a/src/app/AppState.ts b/src/app/AppState.ts index b3583a6c..54783a1c 100644 --- a/src/app/AppState.ts +++ b/src/app/AppState.ts @@ -14,13 +14,14 @@ import { INITIAL_UPLOAD_STATE, UploadState } from '@/admin/upload'; import { AdminData } from '@/admin/actions'; import { RecipeProps } from '@/recipe'; import { getCountsForCategoriesCachedAction } from '@/category/actions'; +import { SWRKey } from '@/swr'; export type AppStateContextType = { // CORE previousPathname?: string hasLoaded?: boolean hasLoadedWithAnimations?: boolean - invalidateSwr?: () => void + invalidateSwr?: (key?: SWRKey, revalidate?: boolean) => void nextPhotoAnimation?: AnimationConfig setNextPhotoAnimation?: (animationConfig?: AnimationConfig) => void getNextPhotoAnimationId?: () => string diff --git a/src/app/AppStateProvider.tsx b/src/app/AppStateProvider.tsx index 3f2ccd54..bbdacce1 100644 --- a/src/app/AppStateProvider.tsx +++ b/src/app/AppStateProvider.tsx @@ -36,9 +36,8 @@ import { getCountsForCategoriesCachedAction } from '@/category/actions'; import { canKeyBePurged, canKeyBePurgedAndRevalidated, - SWR_KEY_GET_ADMIN_DATA, - SWR_KEY_GET_AUTH, - SWR_KEY_GET_COUNTS_FOR_CATEGORIES, + SWR_KEYS, + SWRKey, } from '@/swr'; export default function AppStateProvider({ @@ -129,13 +128,19 @@ export default function AppStateProvider({ }, []); const { mutate } = useSWRConfig(); - const invalidateSwr = useCallback(() => { - mutate(canKeyBePurged, undefined, { revalidate: false }); - mutate(canKeyBePurgedAndRevalidated, undefined, { revalidate: true }); + const invalidateSwr = useCallback((key?: SWRKey, revalidate?: boolean) => { + if (key) { + // Mutate specific key + mutate((k: string) => k?.startsWith(key), undefined, { revalidate }); + } else { + // Mutate all keys that can be purged + mutate(canKeyBePurged, undefined, { revalidate: false }); + mutate(canKeyBePurgedAndRevalidated, undefined, { revalidate: true }); + } }, [mutate]); const { data: categoriesWithCounts } = useSWR( - SWR_KEY_GET_COUNTS_FOR_CATEGORIES, + SWR_KEYS.GET_COUNTS_FOR_CATEGORIES, getCountsForCategoriesCachedAction, ); @@ -143,7 +148,7 @@ export default function AppStateProvider({ data: auth, error: authError, isLoading: isCheckingAuth, - } = useSWR(SWR_KEY_GET_AUTH, getAuthAction); + } = useSWR(SWR_KEYS.GET_AUTH, getAuthAction); useEffect(() => { if (auth === null || authError) { setUserEmail(undefined); @@ -161,7 +166,7 @@ export default function AppStateProvider({ mutate: refreshAdminData, isLoading: isLoadingAdminData, } = useSWR( - isUserSignedIn ? SWR_KEY_GET_ADMIN_DATA : null, + isUserSignedIn ? SWR_KEYS.GET_ADMIN_DATA : null, getAdminDataAction, ); const updateAdminData = useCallback( diff --git a/src/app/AppViewSwitcher.tsx b/src/app/AppViewSwitcher.tsx index bd81c132..0476515f 100644 --- a/src/app/AppViewSwitcher.tsx +++ b/src/app/AppViewSwitcher.tsx @@ -63,7 +63,7 @@ export default function AppViewSwitcher({ useEffect(() => { if (hasLoadedRef.current) { // After initial load, invalidate cache every time sort changes - invalidateSwr?.(); + invalidateSwr?.('INFINITE_PHOTO_SCROLL'); } hasLoadedRef.current = true; }, [invalidateSwr, sortBy]); diff --git a/src/components/entity/EntityHover.tsx b/src/components/entity/EntityHover.tsx index cbd36705..7f8635c0 100644 --- a/src/components/entity/EntityHover.tsx +++ b/src/components/entity/EntityHover.tsx @@ -8,7 +8,7 @@ import PhotoMedium from '@/photo/PhotoMedium'; import Spinner from '../Spinner'; import clsx from 'clsx'; import { useAppText } from '@/i18n/state/client'; -import { SWR_KEY_SHARED_HOVER } from '@/swr'; +import { SWR_KEYS } from '@/swr'; const { width, height } = getDimensionsFromSize(300, 16 / 9); @@ -37,7 +37,7 @@ export default function EntityHover({ data: photos, isLoading, } = useSWR( - isHovering ? `${SWR_KEY_SHARED_HOVER}-${hoverKey}` : null, + isHovering ? `${SWR_KEYS.SHARED_HOVER}-${hoverKey}` : null, getPhotos, { revalidateIfStale: false, revalidateOnFocus: false, diff --git a/src/photo/InfinitePhotoScroll.tsx b/src/photo/InfinitePhotoScroll.tsx index c5bcb762..f8380be5 100644 --- a/src/photo/InfinitePhotoScroll.tsx +++ b/src/photo/InfinitePhotoScroll.tsx @@ -18,7 +18,7 @@ import { useAppState } from '@/app/AppState'; import useVisible from '@/utility/useVisible'; import { ADMIN_DB_OPTIMIZE_ENABLED } from '@/app/config'; import { SortBy } from './db/sort'; -import { SWR_KEY_INFINITE_PHOTO_SCROLL } from '@/swr'; +import { SWR_KEYS } from '@/swr'; const SIZE_KEY_SEPARATOR = '__'; const getSizeFromKey = (key: string) => @@ -65,7 +65,8 @@ export default function InfinitePhotoScroll({ const keyGenerator = useCallback( (size: number, prev: Photo[]) => prev && prev.length === 0 ? null - : `${SWR_KEY_INFINITE_PHOTO_SCROLL}-${cacheKey}__${size}` + // eslint-disable-next-line max-len + : `${SWR_KEYS.INFINITE_PHOTO_SCROLL}-${cacheKey}${SIZE_KEY_SEPARATOR}${size}` , [cacheKey]); const fetcher = useCallback(( diff --git a/src/swr/index.ts b/src/swr/index.ts index bda00829..00bed8d8 100644 --- a/src/swr/index.ts +++ b/src/swr/index.ts @@ -1,18 +1,22 @@ -export const SWR_KEY_GET_AUTH = 'getAuth'; -export const SWR_KEY_GET_ADMIN_DATA = 'getAdminData'; -export const SWR_KEY_GET_COUNTS_FOR_CATEGORIES = 'getCountsForCategories'; -export const SWR_KEY_SHARED_HOVER = 'sharedHover'; -export const SWR_KEY_INFINITE_PHOTO_SCROLL = 'infinitePhotoScroll'; +export const SWR_KEYS = { + GET_AUTH: 'GET_AUTH', + GET_ADMIN_DATA: 'GET_ADMIN_DATA', + GET_COUNTS_FOR_CATEGORIES: 'GET_COUNTS_FOR_CATEGORIES', + SHARED_HOVER: 'SHARED_HOVER', + INFINITE_PHOTO_SCROLL: 'INFINITE_PHOTO_SCROLL', +} as const; const KEYS_THAT_CAN_BE_PURGED = [ - SWR_KEY_SHARED_HOVER, - SWR_KEY_INFINITE_PHOTO_SCROLL, -]; + SWR_KEYS.SHARED_HOVER, + SWR_KEYS.INFINITE_PHOTO_SCROLL, +] as const; const KEYS_THAT_CAN_BE_PURGED_AND_REVALIDATED = [ - SWR_KEY_GET_ADMIN_DATA, - SWR_KEY_GET_COUNTS_FOR_CATEGORIES, -]; + SWR_KEYS.GET_ADMIN_DATA, + SWR_KEYS.GET_COUNTS_FOR_CATEGORIES, +] as const; + +export type SWRKey = typeof SWR_KEYS[keyof typeof SWR_KEYS]; export const canKeyBePurged = (key: string) => KEYS_THAT_CAN_BE_PURGED.some(k => key.startsWith(k));