Next.js 16 (#347)
* Upgrade to Next.js 16, resolve/suppress linting errors * Update usage of revalidateTag() * Rename proxy.ts export * Refactor infinite scroll data handling
This commit is contained in:
parent
e56b386a20
commit
5591635a1e
@ -1,27 +1,26 @@
|
|||||||
import { dirname } from 'path';
|
import { defineConfig, globalIgnores } from 'eslint/config';
|
||||||
import { fileURLToPath } from 'url';
|
import nextVitals from 'eslint-config-next/core-web-vitals';
|
||||||
import { FlatCompat } from '@eslint/eslintrc';
|
import nextTs from 'eslint-config-next/typescript';
|
||||||
import stylistic from '@stylistic/eslint-plugin';
|
import stylistic from '@stylistic/eslint-plugin';
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const eslintConfig = defineConfig([
|
||||||
const __dirname = dirname(__filename);
|
...nextVitals,
|
||||||
|
...nextTs,
|
||||||
const compat = new FlatCompat({
|
// Override default ignores of eslint-config-next.
|
||||||
baseDirectory: __dirname,
|
globalIgnores([
|
||||||
});
|
// Default ignores of eslint-config-next:
|
||||||
|
'.next/**',
|
||||||
const eslintConfig = [{
|
'out/**',
|
||||||
ignores: [
|
'build/**',
|
||||||
'.*',
|
|
||||||
'node_modules',
|
|
||||||
'next-env.d.ts',
|
'next-env.d.ts',
|
||||||
],
|
]), {
|
||||||
},
|
|
||||||
...compat.extends('next/core-web-vitals', 'next/typescript'), {
|
|
||||||
plugins: {
|
plugins: {
|
||||||
'@stylistic': stylistic,
|
'@stylistic': stylistic,
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
|
// Temporarily disable during Next.js 16 migration
|
||||||
|
'react-hooks/refs': 'off',
|
||||||
|
'react-hooks/set-state-in-effect': 'off',
|
||||||
'@next/next/no-img-element': 'off',
|
'@next/next/no-img-element': 'off',
|
||||||
'@typescript-eslint/no-explicit-any': 'off',
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
'@typescript-eslint/no-require-imports': 'off',
|
'@typescript-eslint/no-require-imports': 'off',
|
||||||
@ -55,6 +54,8 @@ const eslintConfig = [{
|
|||||||
{ 'code': 80 },
|
{ 'code': 80 },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}];
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
export default eslintConfig;
|
export default eslintConfig;
|
||||||
|
|
||||||
|
|||||||
52
package.json
52
package.json
@ -1,30 +1,30 @@
|
|||||||
{
|
{
|
||||||
"name": "exif-photo-blog",
|
"name": "exif-photo-blog",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev --turbo",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"test": "jest --watch --transformIgnorePatterns 'node_modules/(?!my-library-dir)/'",
|
"test": "jest --watch --transformIgnorePatterns 'node_modules/(?!my-library-dir)/'",
|
||||||
"analyze": "ANALYZE=true next build"
|
"analyze": "ANALYZE=true next build"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.18.2",
|
"packageManager": "pnpm@10.19.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/openai": "^2.0.40",
|
"@ai-sdk/openai": "^2.0.53",
|
||||||
"@ai-sdk/rsc": "^1.0.59",
|
"@ai-sdk/rsc": "^1.0.79",
|
||||||
"@aws-sdk/client-s3": "3.899.0",
|
"@aws-sdk/client-s3": "3.917.0",
|
||||||
"@aws-sdk/s3-request-presigner": "3.899.0",
|
"@aws-sdk/s3-request-presigner": "3.917.0",
|
||||||
"@radix-ui/react-dialog": "^1.1.15",
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||||
"@radix-ui/react-tooltip": "^1.2.8",
|
"@radix-ui/react-tooltip": "^1.2.8",
|
||||||
"@radix-ui/react-visually-hidden": "^1.2.3",
|
"@radix-ui/react-visually-hidden": "^1.2.3",
|
||||||
"@types/piexifjs": "^1.0.0",
|
"@types/piexifjs": "^1.0.0",
|
||||||
"@upstash/ratelimit": "^2.0.6",
|
"@upstash/ratelimit": "^2.0.6",
|
||||||
"@upstash/redis": "^1.35.4",
|
"@upstash/redis": "^1.35.6",
|
||||||
"@vercel/analytics": "^1.5.0",
|
"@vercel/analytics": "^1.5.0",
|
||||||
"@vercel/blob": "^2.0.0",
|
"@vercel/blob": "^2.0.0",
|
||||||
"@vercel/speed-insights": "^1.2.0",
|
"@vercel/speed-insights": "^1.2.0",
|
||||||
"ai": "^5.0.59",
|
"ai": "^5.0.79",
|
||||||
"camelcase-keys": "^10.0.0",
|
"camelcase-keys": "^10.0.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "^1.1.1",
|
"cmdk": "^1.1.1",
|
||||||
@ -35,16 +35,16 @@
|
|||||||
"extract-colors": "^4.2.1",
|
"extract-colors": "^4.2.1",
|
||||||
"fast-average-color": "^9.5.0",
|
"fast-average-color": "^9.5.0",
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
"framer-motion": "^12.23.22",
|
"framer-motion": "^12.23.24",
|
||||||
"nanoid": "^5.1.6",
|
"nanoid": "^5.1.6",
|
||||||
"next": "15.5.4",
|
"next": "16.0.0",
|
||||||
"next-auth": "5.0.0-beta.29",
|
"next-auth": "5.0.0-beta.29",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"ol": "^10.6.1",
|
"ol": "^10.6.1",
|
||||||
"pg": "^8.16.3",
|
"pg": "^8.16.3",
|
||||||
"piexifjs": "^1.0.6",
|
"piexifjs": "^1.0.6",
|
||||||
"react": "19.1.1",
|
"react": "19.2.0",
|
||||||
"react-dom": "19.1.1",
|
"react-dom": "19.2.0",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
"react-openlayers": "^10.5.1",
|
"react-openlayers": "^10.5.1",
|
||||||
"sanitize-html": "^2.17.0",
|
"sanitize-html": "^2.17.0",
|
||||||
@ -54,34 +54,34 @@
|
|||||||
"ts-exif-parser": "^0.2.2",
|
"ts-exif-parser": "^0.2.2",
|
||||||
"use-debounce": "^10.0.6",
|
"use-debounce": "^10.0.6",
|
||||||
"viewerjs": "^1.11.7",
|
"viewerjs": "^1.11.7",
|
||||||
"zod": "^4.1.11"
|
"zod": "^4.1.12"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3.3.1",
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
"@next/bundle-analyzer": "15.5.4",
|
"@next/bundle-analyzer": "16.0.0",
|
||||||
"@next/eslint-plugin-next": "15.5.4",
|
"@next/eslint-plugin-next": "16.0.0",
|
||||||
"@stylistic/eslint-plugin": "^5.4.0",
|
"@stylistic/eslint-plugin": "^5.5.0",
|
||||||
"@tailwindcss/postcss": "^4.1.13",
|
"@tailwindcss/postcss": "^4.1.16",
|
||||||
"@testing-library/dom": "^10.4.1",
|
"@testing-library/dom": "^10.4.1",
|
||||||
"@testing-library/jest-dom": "^6.8.0",
|
"@testing-library/jest-dom": "^6.9.1",
|
||||||
"@testing-library/react": "^16.3.0",
|
"@testing-library/react": "^16.3.0",
|
||||||
"@types/culori": "^4.0.1",
|
"@types/culori": "^4.0.1",
|
||||||
"@types/jest": "^30.0.0",
|
"@types/jest": "^30.0.0",
|
||||||
"@types/node": "^24.6.0",
|
"@types/node": "^24.9.1",
|
||||||
"@types/pg": "^8.15.5",
|
"@types/pg": "^8.15.5",
|
||||||
"@types/react": "19.1.15",
|
"@types/react": "19.2.2",
|
||||||
"@types/react-dom": "19.1.9",
|
"@types/react-dom": "19.2.2",
|
||||||
"@types/sanitize-html": "^2.16.0",
|
"@types/sanitize-html": "^2.16.0",
|
||||||
"cross-fetch": "^4.1.0",
|
"cross-fetch": "^4.1.0",
|
||||||
"eslint": "9.36.0",
|
"eslint": "9.38.0",
|
||||||
"eslint-config-next": "15.5.4",
|
"eslint-config-next": "16.0.0",
|
||||||
"eslint-plugin-react-hooks": "^5.2.0",
|
"eslint-plugin-react-hooks": "^7.0.1",
|
||||||
"jest": "^30.2.0",
|
"jest": "^30.2.0",
|
||||||
"jest-environment-jsdom": "^30.2.0",
|
"jest-environment-jsdom": "^30.2.0",
|
||||||
"postcss": "8.5.6",
|
"postcss": "8.5.6",
|
||||||
"tailwindcss": "4.1.13",
|
"tailwindcss": "4.1.16",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "5.9.2"
|
"typescript": "5.9.3"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"onlyBuiltDependencies": [
|
"onlyBuiltDependencies": [
|
||||||
|
|||||||
3508
pnpm-lock.yaml
generated
3508
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,7 @@ import {
|
|||||||
PREFIX_TAG,
|
PREFIX_TAG,
|
||||||
} from './src/app/path';
|
} from './src/app/path';
|
||||||
|
|
||||||
export default function middleware(req: NextRequest, res:NextResponse) {
|
export function proxy(req: NextRequest, res:NextResponse) {
|
||||||
const pathname = req.nextUrl.pathname;
|
const pathname = req.nextUrl.pathname;
|
||||||
|
|
||||||
if (pathname === PATH_ADMIN) {
|
if (pathname === PATH_ADMIN) {
|
||||||
@ -25,8 +25,9 @@ export default function AdminPhotosTableInfinite({
|
|||||||
sortBy="createdAt"
|
sortBy="createdAt"
|
||||||
includeHiddenPhotos
|
includeHiddenPhotos
|
||||||
>
|
>
|
||||||
{({ photos, onLastPhotoVisible, revalidatePhoto }) =>
|
{({ key, photos, onLastPhotoVisible, revalidatePhoto }) =>
|
||||||
<AdminPhotosTable
|
<AdminPhotosTable
|
||||||
|
key={key}
|
||||||
photos={photos}
|
photos={photos}
|
||||||
onLastPhotoVisible={onLastPhotoVisible}
|
onLastPhotoVisible={onLastPhotoVisible}
|
||||||
revalidatePhoto={revalidatePhoto}
|
revalidatePhoto={revalidatePhoto}
|
||||||
|
|||||||
@ -62,12 +62,12 @@ export default function AppStateProvider({
|
|||||||
useState(false);
|
useState(false);
|
||||||
const [nextPhotoAnimation, _setNextPhotoAnimation] =
|
const [nextPhotoAnimation, _setNextPhotoAnimation] =
|
||||||
useState<AnimationConfig>();
|
useState<AnimationConfig>();
|
||||||
|
const [nextPhotoAnimationId, setNextPhotoAnimationId] =
|
||||||
|
useState<string>();
|
||||||
const setNextPhotoAnimation = useCallback((animation?: AnimationConfig) => {
|
const setNextPhotoAnimation = useCallback((animation?: AnimationConfig) => {
|
||||||
_setNextPhotoAnimation(animation);
|
_setNextPhotoAnimation(animation);
|
||||||
setNextPhotoAnimationId(undefined);
|
setNextPhotoAnimationId(undefined);
|
||||||
}, []);
|
}, []);
|
||||||
const [nextPhotoAnimationId, setNextPhotoAnimationId] =
|
|
||||||
useState<string>();
|
|
||||||
const getNextPhotoAnimationId = useCallback(() => {
|
const getNextPhotoAnimationId = useCallback(() => {
|
||||||
const id = nanoid();
|
const id = nanoid();
|
||||||
setNextPhotoAnimationId(id);
|
setNextPhotoAnimationId(id);
|
||||||
|
|||||||
@ -83,6 +83,8 @@ export default function AppViewSwitcher({
|
|||||||
const refHrefFull = useRef<HTMLAnchorElement>(null);
|
const refHrefFull = useRef<HTMLAnchorElement>(null);
|
||||||
const refHrefGrid = useRef<HTMLAnchorElement>(null);
|
const refHrefGrid = useRef<HTMLAnchorElement>(null);
|
||||||
|
|
||||||
|
const [isAdminMenuOpen, setIsAdminMenuOpen] = useState(false);
|
||||||
|
|
||||||
const onKeyDown = useCallback((e: KeyboardEvent) => {
|
const onKeyDown = useCallback((e: KeyboardEvent) => {
|
||||||
if (!e.metaKey) {
|
if (!e.metaKey) {
|
||||||
switch (e.key.toLocaleUpperCase()) {
|
switch (e.key.toLocaleUpperCase()) {
|
||||||
@ -101,7 +103,6 @@ export default function AppViewSwitcher({
|
|||||||
useKeydownHandler({ onKeyDown });
|
useKeydownHandler({ onKeyDown });
|
||||||
|
|
||||||
const [isSortMenuOpen, setIsSortMenuOpen] = useState(false);
|
const [isSortMenuOpen, setIsSortMenuOpen] = useState(false);
|
||||||
const [isAdminMenuOpen, setIsAdminMenuOpen] = useState(false);
|
|
||||||
|
|
||||||
const renderItemFull =
|
const renderItemFull =
|
||||||
<SwitcherItem
|
<SwitcherItem
|
||||||
@ -135,7 +136,7 @@ export default function AppViewSwitcher({
|
|||||||
className={clsx(
|
className={clsx(
|
||||||
GAP_CLASS_RIGHT,
|
GAP_CLASS_RIGHT,
|
||||||
// Apply offset due to outline strategy
|
// Apply offset due to outline strategy
|
||||||
'translate-x-[1px]',
|
'translate-x-px',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{GRID_HOMEPAGE_ENABLED ? renderItemGrid : renderItemFull}
|
{GRID_HOMEPAGE_ENABLED ? renderItemGrid : renderItemFull}
|
||||||
@ -209,7 +210,7 @@ export default function AppViewSwitcher({
|
|||||||
href={pathSortToggle}
|
href={pathSortToggle}
|
||||||
icon={<IconSort
|
icon={<IconSort
|
||||||
sort={isAscending ? 'asc' : 'desc'}
|
sort={isAscending ? 'asc' : 'desc'}
|
||||||
className="translate-x-[0.5px] translate-y-[1px]"
|
className="translate-x-[0.5px] translate-y-px"
|
||||||
/>}
|
/>}
|
||||||
tooltip={{...SHOW_KEYBOARD_SHORTCUT_TOOLTIPS && {
|
tooltip={{...SHOW_KEYBOARD_SHORTCUT_TOOLTIPS && {
|
||||||
content: isAscending
|
content: isAscending
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { ReactNode, useRef } from 'react';
|
import { ReactNode, useRef } from 'react';
|
||||||
import { Variant, motion } from 'framer-motion';
|
import { Variant, motion, stagger } from 'framer-motion';
|
||||||
import { useAppState } from '@/app/AppState';
|
import { useAppState } from '@/app/AppState';
|
||||||
import usePrefersReducedMotion from '@/utility/usePrefersReducedMotion';
|
import usePrefersReducedMotion from '@/utility/usePrefersReducedMotion';
|
||||||
|
|
||||||
@ -73,7 +73,8 @@ function AnimateItems({
|
|||||||
? (nextPhotoAnimationInitial.current?.duration ?? duration)
|
? (nextPhotoAnimationInitial.current?.duration ?? duration)
|
||||||
: duration;
|
: duration;
|
||||||
|
|
||||||
const getInitialVariant = (): Variant => {
|
const hidden: Variant =
|
||||||
|
(() => {
|
||||||
switch (typeResolved) {
|
switch (typeResolved) {
|
||||||
case 'left': return {
|
case 'left': return {
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
@ -91,8 +92,7 @@ function AnimateItems({
|
|||||||
opacity: 0,
|
opacity: 0,
|
||||||
transform: `translateY(${distanceOffset}px) scale(${scaleOffset})`,
|
transform: `translateY(${distanceOffset}px) scale(${scaleOffset})`,
|
||||||
};
|
};
|
||||||
}
|
}})();
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
@ -103,7 +103,7 @@ function AnimateItems({
|
|||||||
? {
|
? {
|
||||||
show: {
|
show: {
|
||||||
transition: {
|
transition: {
|
||||||
staggerChildren: staggerDelay,
|
delayChildren: stagger(staggerDelay),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} : undefined}
|
} : undefined}
|
||||||
@ -122,7 +122,7 @@ function AnimateItems({
|
|||||||
key={itemKeys ? itemKeys[index] : index}
|
key={itemKeys ? itemKeys[index] : index}
|
||||||
className={classNameItem}
|
className={classNameItem}
|
||||||
variants={{
|
variants={{
|
||||||
hidden: getInitialVariant(),
|
hidden,
|
||||||
show: {
|
show: {
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
transform: 'translateX(0) translateY(0) scale(1)',
|
transform: 'translateX(0) translateY(0) scale(1)',
|
||||||
|
|||||||
@ -59,11 +59,11 @@ export default function useMaskedScroll({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const ref = containerRef?.current;
|
const ref = containerRef?.current;
|
||||||
if (ref && updateMaskOnEvents) {
|
if (ref && updateMaskOnEvents) {
|
||||||
ref.onscroll = updateMask;
|
ref.addEventListener('scroll', updateMask);
|
||||||
ref.onresize = updateMask;
|
ref.addEventListener('resize', updateMask);
|
||||||
return () => {
|
return () => {
|
||||||
ref.onscroll = null;
|
ref.removeEventListener('scroll', updateMask);
|
||||||
ref.onresize = null;
|
ref.removeEventListener('resize', updateMask);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, [containerRef, updateMask, updateMaskOnEvents]);
|
}, [containerRef, updateMask, updateMaskOnEvents]);
|
||||||
|
|||||||
@ -58,6 +58,7 @@ export default function InfinitePhotoScroll({
|
|||||||
useCachedPhotos?: boolean
|
useCachedPhotos?: boolean
|
||||||
includeHiddenPhotos?: boolean
|
includeHiddenPhotos?: boolean
|
||||||
children: (props: {
|
children: (props: {
|
||||||
|
key: string
|
||||||
photos: Photo[]
|
photos: Photo[]
|
||||||
onLastPhotoVisible: () => void
|
onLastPhotoVisible: () => void
|
||||||
revalidatePhoto?: RevalidatePhoto
|
revalidatePhoto?: RevalidatePhoto
|
||||||
@ -140,8 +141,6 @@ export default function InfinitePhotoScroll({
|
|||||||
}
|
}
|
||||||
}, [isFinished, isLoadingOrValidating, setSize]);
|
}, [isFinished, isLoadingOrValidating, setSize]);
|
||||||
|
|
||||||
const photos = useMemo(() => (data ?? [])?.flat(), [data]);
|
|
||||||
|
|
||||||
const revalidatePhoto: RevalidatePhoto = useCallback((
|
const revalidatePhoto: RevalidatePhoto = useCallback((
|
||||||
photoId: string,
|
photoId: string,
|
||||||
revalidateRemainingPhotos?: boolean,
|
revalidateRemainingPhotos?: boolean,
|
||||||
@ -159,7 +158,7 @@ export default function InfinitePhotoScroll({
|
|||||||
}
|
}
|
||||||
}});
|
}});
|
||||||
|
|
||||||
const renderMoreButton = () =>
|
const renderMoreButton =
|
||||||
<div ref={buttonContainerRef}>
|
<div ref={buttonContainerRef}>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -179,15 +178,20 @@ export default function InfinitePhotoScroll({
|
|||||||
</div>;
|
</div>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<>
|
||||||
{children({
|
{data?.map((photos, index) => (
|
||||||
|
children({
|
||||||
|
key: `${cacheKey}-${index}`,
|
||||||
photos,
|
photos,
|
||||||
onLastPhotoVisible: advance,
|
onLastPhotoVisible: advance,
|
||||||
revalidatePhoto,
|
revalidatePhoto,
|
||||||
})}
|
})
|
||||||
{!isFinished && (wrapMoreButtonInGrid
|
))}
|
||||||
? <AppGrid contentMain={renderMoreButton()} />
|
{!isFinished && <div className="mt-4">
|
||||||
: renderMoreButton())}
|
{wrapMoreButtonInGrid
|
||||||
</div>
|
? <AppGrid contentMain={renderMoreButton} />
|
||||||
|
: renderMoreButton}
|
||||||
|
</div>}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,8 +32,8 @@ export default function PhotoGridInfinite({
|
|||||||
excludeFromFeeds={excludeFromFeeds}
|
excludeFromFeeds={excludeFromFeeds}
|
||||||
{...categories}
|
{...categories}
|
||||||
>
|
>
|
||||||
{({ photos, onLastPhotoVisible }) =>
|
{({ key, photos, onLastPhotoVisible }) =>
|
||||||
<PhotoGrid {...{
|
<PhotoGrid key={key} {...{
|
||||||
photos,
|
photos,
|
||||||
...categories,
|
...categories,
|
||||||
canStart,
|
canStart,
|
||||||
|
|||||||
@ -65,11 +65,11 @@ export default function PhotoPrevNextActions({
|
|||||||
|
|
||||||
const toggleFavorite = useCallback(() => {
|
const toggleFavorite = useCallback(() => {
|
||||||
if (photo?.id) { return toggleFavoritePhotoAction(photo.id); }
|
if (photo?.id) { return toggleFavoritePhotoAction(photo.id); }
|
||||||
}, [photo?.id]);
|
}, [photo]);
|
||||||
|
|
||||||
const toggleHidden = useCallback(() => {
|
const toggleHidden = useCallback(() => {
|
||||||
if (photo?.id) { return togglePrivatePhotoAction(photo.id); }
|
if (photo?.id) { return togglePrivatePhotoAction(photo.id); }
|
||||||
}, [photo?.id]);
|
}, [photo]);
|
||||||
|
|
||||||
const navigateToPhotoEdit = useNavigateOrRunActionWithToast({
|
const navigateToPhotoEdit = useNavigateOrRunActionWithToast({
|
||||||
pathOrAction: photo ? pathForAdminPhotoEdit(photo) : undefined,
|
pathOrAction: photo ? pathForAdminPhotoEdit(photo) : undefined,
|
||||||
@ -99,7 +99,7 @@ export default function PhotoPrevNextActions({
|
|||||||
const syncPhoto = useNavigateOrRunActionWithToast({
|
const syncPhoto = useNavigateOrRunActionWithToast({
|
||||||
pathOrAction: useCallback(() => {
|
pathOrAction: useCallback(() => {
|
||||||
if (photo?.id) { return syncPhotoAction(photo.id); }
|
if (photo?.id) { return syncPhotoAction(photo.id); }
|
||||||
}, [photo?.id]),
|
}, [photo]),
|
||||||
toastMessage: `Syncing ${photoTitle} ...`,
|
toastMessage: `Syncing ${photoTitle} ...`,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -108,7 +108,7 @@ export default function PhotoPrevNextActions({
|
|||||||
if (photo?.id && photo.url) {
|
if (photo?.id && photo.url) {
|
||||||
return deletePhotoAction(photo.id, photo.url, true);
|
return deletePhotoAction(photo.id, photo.url, true);
|
||||||
}
|
}
|
||||||
}, [photo?.id, photo?.url]),
|
}, [photo]),
|
||||||
toastMessage: `Deleting ${photoTitle} ...`,
|
toastMessage: `Deleting ${photoTitle} ...`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -26,8 +26,9 @@ export default function PhotosLargeInfinite({
|
|||||||
excludeFromFeeds={excludeFromFeeds}
|
excludeFromFeeds={excludeFromFeeds}
|
||||||
wrapMoreButtonInGrid
|
wrapMoreButtonInGrid
|
||||||
>
|
>
|
||||||
{({ photos, onLastPhotoVisible, revalidatePhoto }) =>
|
{({ key, photos, onLastPhotoVisible, revalidatePhoto }) =>
|
||||||
<PhotosLarge
|
<PhotosLarge
|
||||||
|
key={key}
|
||||||
photos={photos}
|
photos={photos}
|
||||||
onLastPhotoVisible={onLastPhotoVisible}
|
onLastPhotoVisible={onLastPhotoVisible}
|
||||||
revalidatePhoto={revalidatePhoto}
|
revalidatePhoto={revalidatePhoto}
|
||||||
|
|||||||
@ -17,8 +17,9 @@ export default function StaggeredOgPhotosInfinite({
|
|||||||
initialOffset={initialOffset}
|
initialOffset={initialOffset}
|
||||||
itemsPerPage={itemsPerPage}
|
itemsPerPage={itemsPerPage}
|
||||||
>
|
>
|
||||||
{({ photos, onLastPhotoVisible }) =>
|
{({ key, photos, onLastPhotoVisible }) =>
|
||||||
<StaggeredOgPhotos
|
<StaggeredOgPhotos
|
||||||
|
key={key}
|
||||||
photos={photos}
|
photos={photos}
|
||||||
onLastPhotoVisible={onLastPhotoVisible}
|
onLastPhotoVisible={onLastPhotoVisible}
|
||||||
/>}
|
/>}
|
||||||
|
|||||||
@ -102,31 +102,31 @@ const getPhotosCacheKeys = (options: PhotoQueryOptions = {}) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const revalidatePhotosKey = () =>
|
export const revalidatePhotosKey = () =>
|
||||||
revalidateTag(KEY_PHOTOS);
|
revalidateTag(KEY_PHOTOS, 'max');
|
||||||
|
|
||||||
export const revalidateAlbumsKey = () =>
|
export const revalidateAlbumsKey = () =>
|
||||||
revalidateTag(KEY_ALBUMS);
|
revalidateTag(KEY_ALBUMS, 'max');
|
||||||
|
|
||||||
export const revalidateTagsKey = () =>
|
export const revalidateTagsKey = () =>
|
||||||
revalidateTag(KEY_TAGS);
|
revalidateTag(KEY_TAGS, 'max');
|
||||||
|
|
||||||
export const revalidateRecipesKey = () =>
|
export const revalidateRecipesKey = () =>
|
||||||
revalidateTag(KEY_RECIPES);
|
revalidateTag(KEY_RECIPES, 'max');
|
||||||
|
|
||||||
export const revalidateCamerasKey = () =>
|
export const revalidateCamerasKey = () =>
|
||||||
revalidateTag(KEY_CAMERAS);
|
revalidateTag(KEY_CAMERAS, 'max');
|
||||||
|
|
||||||
export const revalidateLensesKey = () =>
|
export const revalidateLensesKey = () =>
|
||||||
revalidateTag(KEY_LENSES);
|
revalidateTag(KEY_LENSES, 'max');
|
||||||
|
|
||||||
export const revalidateFilmsKey = () =>
|
export const revalidateFilmsKey = () =>
|
||||||
revalidateTag(KEY_FILMS);
|
revalidateTag(KEY_FILMS, 'max');
|
||||||
|
|
||||||
export const revalidateFocalLengthsKey = () =>
|
export const revalidateFocalLengthsKey = () =>
|
||||||
revalidateTag(KEY_FOCAL_LENGTHS);
|
revalidateTag(KEY_FOCAL_LENGTHS, 'max');
|
||||||
|
|
||||||
export const revalidateYearsKey = () =>
|
export const revalidateYearsKey = () =>
|
||||||
revalidateTag(KEY_YEARS);
|
revalidateTag(KEY_YEARS, 'max');
|
||||||
|
|
||||||
export const revalidateAllKeys = () => {
|
export const revalidateAllKeys = () => {
|
||||||
revalidatePhotosKey();
|
revalidatePhotosKey();
|
||||||
@ -151,7 +151,7 @@ export const revalidateAllKeysAndPaths = () => {
|
|||||||
|
|
||||||
export const revalidatePhoto = (photoId: string) => {
|
export const revalidatePhoto = (photoId: string) => {
|
||||||
// Tags
|
// Tags
|
||||||
revalidateTag(photoId);
|
revalidateTag(photoId, 'max');
|
||||||
revalidateYearsKey();
|
revalidateYearsKey();
|
||||||
revalidateCamerasKey();
|
revalidateCamerasKey();
|
||||||
revalidateLensesKey();
|
revalidateLensesKey();
|
||||||
|
|||||||
@ -334,7 +334,7 @@ export default function PhotoForm({
|
|||||||
? 'true'
|
? 'true'
|
||||||
: 'false',
|
: 'false',
|
||||||
}));
|
}));
|
||||||
}, []);
|
}, [setFormData]);
|
||||||
|
|
||||||
const formContent = useMemo(() =>
|
const formContent = useMemo(() =>
|
||||||
FORM_METADATA_ENTRIES_BY_SECTION(
|
FORM_METADATA_ENTRIES_BY_SECTION(
|
||||||
@ -371,7 +371,7 @@ export default function PhotoForm({
|
|||||||
blurCompatibilityLevel="none"
|
blurCompatibilityLevel="none"
|
||||||
width={thumbnailDimensions.width}
|
width={thumbnailDimensions.width}
|
||||||
height={thumbnailDimensions.height}
|
height={thumbnailDimensions.height}
|
||||||
priority
|
preload
|
||||||
/>;
|
/>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,7 +1,11 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2019",
|
"target": "ES2019",
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"esnext"
|
||||||
|
],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
@ -11,7 +15,7 @@
|
|||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"jsx": "preserve",
|
"jsx": "react-jsx",
|
||||||
"incremental": true,
|
"incremental": true,
|
||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
@ -19,9 +23,19 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"]
|
"@/*": [
|
||||||
|
"./src/*"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
"include": [
|
||||||
"exclude": ["node_modules"]
|
"next-env.d.ts",
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx",
|
||||||
|
".next/types/**/*.ts",
|
||||||
|
".next/dev/types/**/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user