Vercel/src/admin/select/SelectPhotosProvider.tsx
Sam Becker dbf55badf6
Optimize Next.js 16 behavior (#349)
* Remove unused desktop redirect component

* Tweak useEffect/setState interactions

* Address more next.js 16 linting

* Tweak secret loading

* Finish linting setstate/useeffect interactions

* Disable ref lint warnings
2025-10-27 09:49:16 -05:00

96 lines
3.0 KiB
TypeScript

'use client';
import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react';
import { SelectPhotosContext } from './SelectPhotosState';
import { PARAM_SELECT, PATH_GRID_INFERRED } from '@/app/path';
import { usePathname, useRouter } from 'next/navigation';
import { useAppState } from '@/app/AppState';
import useClientSearchParams from '@/utility/useClientSearchParams';
import { replacePathWithEvent } from '@/utility/url';
import { isElementPartiallyInViewport } from '@/utility/dom';
export const DATA_KEY_PHOTO_GRID = 'data-photo-grid';
export default function SelectPhotosProvider({
children,
}: {
children: ReactNode
}) {
const router = useRouter();
const pathname = usePathname();
const { isUserSignedIn } = useAppState();
const searchParamsSelect = useClientSearchParams(
PARAM_SELECT,
// Only scan urls when admin is signed in
isUserSignedIn,
);
const [canCurrentPageSelectPhotos, setCanCurrentPageSelectPhotos] =
useState(false);
const [selectedPhotoIds, setSelectedPhotoIds] =
useState<string[]>([]);
const [isPerformingSelectEdit, setIsPerformingSelectEdit] =
useState(false);
const getPhotoGridElements = useCallback(() =>
document.querySelectorAll(`[${DATA_KEY_PHOTO_GRID}=true]`)
, []);
useEffect(() => {
if (isUserSignedIn) {
const doesPageHavePhotoGrids = getPhotoGridElements().length > 0;
// eslint-disable-next-line react-hooks/set-state-in-effect
setCanCurrentPageSelectPhotos(doesPageHavePhotoGrids);
}
}, [pathname, isUserSignedIn, getPhotoGridElements]);
const isSelectingPhotos = useMemo(() =>
isUserSignedIn &&
searchParamsSelect === 'true'
, [isUserSignedIn, searchParamsSelect]);
const startSelectingPhotos = useCallback(() =>
canCurrentPageSelectPhotos
// Use replacePathWithEvent because only query params change
? replacePathWithEvent(`${pathname}?${PARAM_SELECT}=true`)
// Redirect to grid if current view does not support photo selection
: router.push(`${PATH_GRID_INFERRED}?${PARAM_SELECT}=true`)
, [router, canCurrentPageSelectPhotos, pathname]);
const stopSelectingPhotos = useCallback(() =>
replacePathWithEvent(pathname)
, [pathname]);
useEffect(() => {
if (isSelectingPhotos) {
const photoGrids = Array.from(getPhotoGridElements());
const isSomePhotoGridVisible = photoGrids
.some(element => isElementPartiallyInViewport(element, -20));
if (!isSomePhotoGridVisible) {
photoGrids[0]?.scrollIntoView({ behavior: 'smooth' });
}
} else {
// eslint-disable-next-line react-hooks/set-state-in-effect
setSelectedPhotoIds([]);
}
}, [isSelectingPhotos, getPhotoGridElements]);
return (
<SelectPhotosContext.Provider value={{
canCurrentPageSelectPhotos,
isSelectingPhotos,
startSelectingPhotos,
stopSelectingPhotos,
selectedPhotoIds,
setSelectedPhotoIds,
isPerformingSelectEdit,
setIsPerformingSelectEdit,
}}>
{children}
</SelectPhotosContext.Provider>
);
}