Fix duplicate photo keys in development

This commit is contained in:
Sam Becker 2025-03-08 15:45:44 -06:00
parent bb58c91977
commit ff6165a19b
9 changed files with 29 additions and 24 deletions

View File

@ -1,5 +1,5 @@
import { clsx } from 'clsx/lite';
import { HTMLAttributes, JSX, RefObject } from 'react';
import { HTMLAttributes, ReactNode, RefObject } from 'react';
/*
MAX WIDTHS
@ -22,8 +22,8 @@ export default function SiteGrid({
}: {
containerRef?: RefObject<HTMLDivElement | null>
className?: string
contentMain: JSX.Element
contentSide?: JSX.Element
contentMain: ReactNode
contentSide?: ReactNode
sideFirstOnMobile?: boolean
sideHiddenOnMobile?: boolean
} & HTMLAttributes<HTMLDivElement>) {

View File

@ -1,6 +1,6 @@
import { clsx } from 'clsx/lite';
import { SHOULD_PREFETCH_ALL_LINKS } from '@/app/config';
import { JSX } from 'react';
import { ReactNode } from 'react';
import Spinner from './Spinner';
import LinkWithLoader from './LinkWithLoader';
@ -15,7 +15,7 @@ export default function SwitcherItem({
noPadding,
prefetch = SHOULD_PREFETCH_ALL_LINKS,
}: {
icon: JSX.Element
icon: ReactNode
title?: string
href?: string
className?: string

View File

@ -11,8 +11,8 @@ import { TAG_HIDDEN } from '@/tag';
import HiddenHeader from '@/tag/HiddenHeader';
import FocalLengthHeader from '@/focal/FocalLengthHeader';
import PhotoHeader from './PhotoHeader';
import { JSX } from 'react';
import RecipeHeader from '@/recipe/RecipeHeader';
import { ReactNode } from 'react';
export default function PhotoDetailPage({
photo,
@ -38,7 +38,7 @@ export default function PhotoDetailPage({
shouldShare?: boolean
includeFavoriteInAdminMenu?: boolean
} & PhotoSetCategory) {
let customHeader: JSX.Element | undefined;
let customHeader: ReactNode | undefined;
if (tag) {
customHeader = tag === TAG_HIDDEN

View File

@ -1,5 +1,4 @@
import {
INFINITE_SCROLL_FEED_INITIAL,
INFINITE_SCROLL_FEED_MULTIPLE,
Photo,
} from '.';
@ -18,7 +17,7 @@ export default function PhotoFeedPage({
<PhotosLarge {...{ photos }} />
{photosCount > photos.length &&
<PhotosLargeInfinite
initialOffset={INFINITE_SCROLL_FEED_INITIAL}
initialOffset={photos.length}
itemsPerPage={INFINITE_SCROLL_FEED_MULTIPLE}
/>}
</div>

View File

@ -8,7 +8,7 @@ import AnimateItems from '@/components/AnimateItems';
import { GRID_ASPECT_RATIO } from '@/app/config';
import { useAppState } from '@/state/AppState';
import SelectTileOverlay from '@/components/SelectTileOverlay';
import { JSX } from 'react';
import { ReactNode } from 'react';
export default function PhotoGrid({
photos,
@ -38,7 +38,7 @@ export default function PhotoGrid({
canStart?: boolean
animateOnFirstLoadOnly?: boolean
staggerOnFirstLoadOnly?: boolean
additionalTile?: JSX.Element
additionalTile?: ReactNode
small?: boolean
canSelect?: boolean
onLastPhotoVisible?: () => void
@ -113,7 +113,7 @@ export default function PhotoGrid({
)}
/>}
</div>;
}).concat(additionalTile ?? [])}
}).concat(additionalTile ? <>{additionalTile}</> : [])}
itemKeys={photos.map(photo => photo.id)
.concat(additionalTile ? ['more'] : [])}
/>

View File

@ -5,7 +5,7 @@ import PhotoGrid from './PhotoGrid';
import PhotoGridInfinite from './PhotoGridInfinite';
import { clsx } from 'clsx/lite';
import AnimateItems from '@/components/AnimateItems';
import { JSX, ComponentProps, useCallback, useState } from 'react';
import { ComponentProps, useCallback, useState, ReactNode } from 'react';
export default function PhotoGridContainer({
cacheKey,
@ -23,8 +23,8 @@ export default function PhotoGridContainer({
}: {
cacheKey: string
count: number
header?: JSX.Element
sidebar?: JSX.Element
header?: ReactNode
sidebar?: ReactNode
} & ComponentProps<typeof PhotoGrid>) {
const [
shouldAnimateDynamicItems,
@ -34,8 +34,6 @@ export default function PhotoGridContainer({
const onAnimationComplete = useCallback(() =>
setShouldAnimateDynamicItems(true), []);
const initialOffset = photos.length;
return (
<SiteGrid
contentMain={<div className={clsx(
@ -59,10 +57,10 @@ export default function PhotoGridContainer({
onAnimationComplete,
canSelect,
}} />
{count > initialOffset &&
{count > photos.length &&
<PhotoGridInfinite {...{
cacheKey,
initialOffset,
initialOffset: photos.length,
canStart: shouldAnimateDynamicItems,
tag,
camera,

View File

@ -356,7 +356,7 @@ export default function PhotoLarge({
'text-medium',
'border-medium rounded-md',
'px-[4px] py-[2.5px] my-[-3px]',
'translate-y-[2px]',
'translate-y-[1.5px]',
'hover:bg-dim active:bg-main',
)}>
{shouldShowRecipe

View File

@ -69,6 +69,7 @@ const createPhotosTable = () =>
const safelyQueryPhotos = async <T>(
callback: () => Promise<T>,
debugMessage: string,
options?: GetPhotosOptions,
): Promise<T> => {
let result: T;
@ -122,7 +123,10 @@ const safelyQueryPhotos = async <T>(
if (ADMIN_SQL_DEBUG_ENABLED && debugMessage) {
const time =
(((new Date()).getTime() - start.getTime()) / 1000).toFixed(2);
console.log(`Executing sql query: ${debugMessage} (${time} seconds)`);
console.log(
`Executing sql query: ${debugMessage} (${time} seconds)`,
options ? { options } : undefined,
);
}
return result;
@ -410,7 +414,11 @@ export const getPhotos = async (options: GetPhotosOptions = {}) =>
return query(sql.join(' '), values)
.then(({ rows }) => rows.map(parsePhotoFromDb));
}, 'getPhotos');
},
'getPhotos',
// Seemingly necessary to pass `options` for expected cache behavior
options,
);
export const getPhotosNearId = async (
photoId: string,

View File

@ -4,7 +4,7 @@ import Modal from '@/components/Modal';
import { TbPhotoShare } from 'react-icons/tb';
import { clsx } from 'clsx/lite';
import { BiCopy } from 'react-icons/bi';
import { JSX, ReactNode, useEffect } from 'react';
import { ReactNode, useEffect } from 'react';
import { shortenUrl } from '@/utility/url';
import { toastSuccess } from '@/toast';
import { PiXLogo } from 'react-icons/pi';
@ -35,7 +35,7 @@ export default function ShareModal({
}, [setShouldRespondToKeyboardCommands]);
const renderIcon = (
icon: JSX.Element,
icon: ReactNode,
action: () => void,
embedded?: boolean,
) =>