From a055783c26ae4a8f94bf76393ed2d85a2f9da908 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Fri, 23 Feb 2024 12:06:02 -0600 Subject: [PATCH 01/11] Fade placeholder blurs --- src/components/ImageBlurFallback.tsx | 84 +++++++++++++++++++++++----- src/photo/PhotoGrid.tsx | 7 +-- src/photo/PhotoSmall.tsx | 17 +----- src/site/index.ts | 2 +- 4 files changed, 75 insertions(+), 35 deletions(-) diff --git a/src/components/ImageBlurFallback.tsx b/src/components/ImageBlurFallback.tsx index 1b1162e8..118d2643 100644 --- a/src/components/ImageBlurFallback.tsx +++ b/src/components/ImageBlurFallback.tsx @@ -1,22 +1,80 @@ +'use client'; + +/* eslint-disable jsx-a11y/alt-text */ import { BLUR_ENABLED } from '@/site/config'; import { clsx} from 'clsx/lite'; import Image, { ImageProps } from 'next/image'; +import { useEffect, useRef, useState } from 'react'; export default function ImageBlurFallback(props: ImageProps) { + const { + className, + priority, + blurDataURL, + ...rest + } = props; + + const [wasCached, setWasCached] = useState(true); + const [isLoading, setIsLoading] = useState(true); + const [didError, setDidError] = useState(false); + + const [hideBluePlaceholder, setHideBluePlaceholder] = useState(false); + + const imageClassName = 'object-cover h-full'; + + const imgRef = useRef(null); + + useEffect(() => { + const timeout = setTimeout( + () => setWasCached(imgRef.current?.complete ?? false), + 100, + ); + return () => clearTimeout(timeout); + }, []); + + useEffect(() => { + if (!isLoading && !didError) { + const timeout = setTimeout(() => { + setHideBluePlaceholder(true); + }, 1000); + return () => clearTimeout(timeout); + } + }, [isLoading, didError]); + + const showPlaceholder = + BLUR_ENABLED && + props.blurDataURL && + !wasCached && + !hideBluePlaceholder; + return ( - // eslint-disable-next-line jsx-a11y/alt-text - + {showPlaceholder && + } + + onLoad: () => setIsLoading(false), + onError: () => setDidError(true), + }} /> + ); } diff --git a/src/photo/PhotoGrid.tsx b/src/photo/PhotoGrid.tsx index fefb0b4e..3927ed01 100644 --- a/src/photo/PhotoGrid.tsx +++ b/src/photo/PhotoGrid.tsx @@ -56,12 +56,7 @@ export default function PhotoGrid({
*]:flex [&>*]:w-full [&>*]:h-full', - '[&>*>*]:object-cover [&>*>*]:min-h-full', - ) + ? 'aspect-square overflow-hidden' : undefined} style={{ ...GRID_ASPECT_RATIO !== 0 && { diff --git a/src/photo/PhotoSmall.tsx b/src/photo/PhotoSmall.tsx index b560de25..c0408f72 100644 --- a/src/photo/PhotoSmall.tsx +++ b/src/photo/PhotoSmall.tsx @@ -5,8 +5,6 @@ import { clsx } from 'clsx/lite'; import { pathForPhoto } from '@/site/paths'; import { Camera } from '@/camera'; import { FilmSimulation } from '@/simulation'; -import AdminPhotoMenu from '@/admin/AdminPhotoMenu'; -import { Suspense } from 'react'; export default function PhotoSmall({ photo, @@ -14,34 +12,23 @@ export default function PhotoSmall({ camera, simulation, selected, - showAdminMenu, }: { photo: Photo tag?: string camera?: Camera simulation?: FilmSimulation selected?: boolean - showAdminMenu?: boolean }) { return ( - - {showAdminMenu && - } - Date: Fri, 23 Feb 2024 12:51:10 -0600 Subject: [PATCH 02/11] Fix poorly blurred placeholder data generated by Safari --- src/components/ImageBlurFallback.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/ImageBlurFallback.tsx b/src/components/ImageBlurFallback.tsx index 118d2643..e8e0842c 100644 --- a/src/components/ImageBlurFallback.tsx +++ b/src/components/ImageBlurFallback.tsx @@ -51,7 +51,7 @@ export default function ImageBlurFallback(props: ImageProps) {
@@ -63,6 +63,8 @@ export default function ImageBlurFallback(props: ImageProps) { imageClassName, 'absolute', 'transition-opacity duration-300 ease-in', + // Fix poorly blurred placeholder data generated by Safari + 'blur-md scale-105', isLoading ? 'opacity-100' : 'opacity-0', ), }} />} From ac4a6995197d24735de2cbe0f48b4ee932204836 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Fri, 23 Feb 2024 13:55:32 -0600 Subject: [PATCH 03/11] Fade in image even without placeholder blur data --- src/components/ImageBlurFallback.tsx | 34 +++++++++++++++------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/components/ImageBlurFallback.tsx b/src/components/ImageBlurFallback.tsx index e8e0842c..b7c48158 100644 --- a/src/components/ImageBlurFallback.tsx +++ b/src/components/ImageBlurFallback.tsx @@ -18,7 +18,7 @@ export default function ImageBlurFallback(props: ImageProps) { const [isLoading, setIsLoading] = useState(true); const [didError, setDidError] = useState(false); - const [hideBluePlaceholder, setHideBluePlaceholder] = useState(false); + const [hideBlurPlaceholder, setHideBlurPlaceholder] = useState(false); const imageClassName = 'object-cover h-full'; @@ -35,7 +35,7 @@ export default function ImageBlurFallback(props: ImageProps) { useEffect(() => { if (!isLoading && !didError) { const timeout = setTimeout(() => { - setHideBluePlaceholder(true); + setHideBlurPlaceholder(true); }, 1000); return () => clearTimeout(timeout); } @@ -44,8 +44,7 @@ export default function ImageBlurFallback(props: ImageProps) { const showPlaceholder = BLUR_ENABLED && props.blurDataURL && - !wasCached && - !hideBluePlaceholder; + !hideBlurPlaceholder; return (
+ setIsLoading(false), + onError: () => setDidError(true), + }} /> {showPlaceholder && } - setIsLoading(false), - onError: () => setDidError(true), - }} />
); } From 6994d0e3f2f0a186cb0bd10612d277219438dd7f Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Fri, 23 Feb 2024 15:31:58 -0600 Subject: [PATCH 04/11] Refine image blur state management --- src/components/ImageBlurFallback.tsx | 35 ++++++++++++++-------------- src/components/ImageSmall.tsx | 1 - src/components/ImageTiny.tsx | 5 +--- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/components/ImageBlurFallback.tsx b/src/components/ImageBlurFallback.tsx index b7c48158..f9c3213d 100644 --- a/src/components/ImageBlurFallback.tsx +++ b/src/components/ImageBlurFallback.tsx @@ -14,10 +14,8 @@ export default function ImageBlurFallback(props: ImageProps) { ...rest } = props; - const [wasCached, setWasCached] = useState(true); - const [isLoading, setIsLoading] = useState(true); - const [didError, setDidError] = useState(false); - + const [isLoading, setIsLoading] = useState(false); + const [didLoad, setDidLoad] = useState(false); const [hideBlurPlaceholder, setHideBlurPlaceholder] = useState(false); const imageClassName = 'object-cover h-full'; @@ -25,21 +23,20 @@ export default function ImageBlurFallback(props: ImageProps) { const imgRef = useRef(null); useEffect(() => { - const timeout = setTimeout( - () => setWasCached(imgRef.current?.complete ?? false), - 100, - ); + const timeout = setTimeout(() => + setIsLoading(imgRef.current?.complete !== true) + , 100); return () => clearTimeout(timeout); }, []); useEffect(() => { - if (!isLoading && !didError) { - const timeout = setTimeout(() => { - setHideBlurPlaceholder(true); - }, 1000); + if (didLoad) { + const timeout = setTimeout(() => + setHideBlurPlaceholder(true) + , 500); return () => clearTimeout(timeout); } - }, [isLoading, didError]); + }, [didLoad]); const showPlaceholder = BLUR_ENABLED && @@ -62,11 +59,15 @@ export default function ImageBlurFallback(props: ImageProps) { imageClassName, 'z-10', 'transition-opacity duration-300 ease-in', - (wasCached || !isLoading) ? 'opacity-100' : 'opacity-0', + isLoading ? 'opacity-0' : 'opacity-100', ), - placeholder: 'empty', - onLoad: () => setIsLoading(false), - onError: () => setDidError(true), + onLoad: () => { + setIsLoading(false); + setDidLoad(true); + }, + onError: () => { + setIsLoading(false); + }, }} /> {showPlaceholder && diff --git a/src/components/ImageTiny.tsx b/src/components/ImageTiny.tsx index ac1cbff3..5e976775 100644 --- a/src/components/ImageTiny.tsx +++ b/src/components/ImageTiny.tsx @@ -19,10 +19,7 @@ export default function ImageTiny({ className, src, alt, - ...blurData && { - blurDataURL: blurData, - placeholder: 'blur', - }, + blurDataURL: blurData, width: IMAGE_TINY_WIDTH, height: Math.round(IMAGE_TINY_WIDTH / aspectRatio), }} /> From f9625f18b556ee1fefa36b11de87132039001340 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Fri, 23 Feb 2024 16:36:12 -0600 Subject: [PATCH 05/11] Tweak placeholder z-order --- src/components/ImageBlurFallback.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ImageBlurFallback.tsx b/src/components/ImageBlurFallback.tsx index f9c3213d..273cb9ab 100644 --- a/src/components/ImageBlurFallback.tsx +++ b/src/components/ImageBlurFallback.tsx @@ -57,7 +57,7 @@ export default function ImageBlurFallback(props: ImageProps) { priority, className: clsx( imageClassName, - 'z-10', + 'relative z-10', 'transition-opacity duration-300 ease-in', isLoading ? 'opacity-0' : 'opacity-100', ), @@ -75,7 +75,7 @@ export default function ImageBlurFallback(props: ImageProps) { src: blurDataURL, className: clsx( imageClassName, - 'absolute', + 'absolute z-[1] top-0 left-0', // Fix poorly blurred placeholder data generated by Safari 'blur-md scale-110', ), From 7b192b003cc9fc6f79aaca7c1bf66cdd89d60568 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Fri, 23 Feb 2024 16:52:17 -0600 Subject: [PATCH 06/11] Add guard for loaded blur images --- src/components/ImageBlurFallback.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ImageBlurFallback.tsx b/src/components/ImageBlurFallback.tsx index 273cb9ab..c652657d 100644 --- a/src/components/ImageBlurFallback.tsx +++ b/src/components/ImageBlurFallback.tsx @@ -59,7 +59,7 @@ export default function ImageBlurFallback(props: ImageProps) { imageClassName, 'relative z-10', 'transition-opacity duration-300 ease-in', - isLoading ? 'opacity-0' : 'opacity-100', + !isLoading || !showPlaceholder ? 'opacity-100' : 'opacity-0', ), onLoad: () => { setIsLoading(false); From f62aa939db54e25f1161a8edb4556ef27e93f78b Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Fri, 23 Feb 2024 17:23:31 -0600 Subject: [PATCH 07/11] Finalize blur loading logic --- src/components/ImageBlurFallback.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/components/ImageBlurFallback.tsx b/src/components/ImageBlurFallback.tsx index c652657d..3f05275b 100644 --- a/src/components/ImageBlurFallback.tsx +++ b/src/components/ImageBlurFallback.tsx @@ -23,15 +23,19 @@ export default function ImageBlurFallback(props: ImageProps) { const imgRef = useRef(null); useEffect(() => { - const timeout = setTimeout(() => - setIsLoading(imgRef.current?.complete !== true) - , 100); + const timeout = setTimeout(() => { + // Check if image has already loaded/is cached + // in order to not show blur placeholder on every load + setIsLoading(imgRef.current?.complete !== true); + setDidLoad(imgRef.current?.complete === true); + }, 100); return () => clearTimeout(timeout); }, []); useEffect(() => { if (didLoad) { const timeout = setTimeout(() => + // Hide blurred placeholder after image has faded in after 300ms setHideBlurPlaceholder(true) , 500); return () => clearTimeout(timeout); @@ -59,7 +63,7 @@ export default function ImageBlurFallback(props: ImageProps) { imageClassName, 'relative z-10', 'transition-opacity duration-300 ease-in', - !isLoading || !showPlaceholder ? 'opacity-100' : 'opacity-0', + (!isLoading || !showPlaceholder) ? 'opacity-100' : 'opacity-0', ), onLoad: () => { setIsLoading(false); From d49331e421c4f3fcc1703aeb75e95204e14177dd Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Fri, 23 Feb 2024 18:49:53 -0600 Subject: [PATCH 08/11] Revert to original fallback logic --- src/components/ImageBlurFallback.tsx | 64 ++++++++++++---------------- 1 file changed, 28 insertions(+), 36 deletions(-) diff --git a/src/components/ImageBlurFallback.tsx b/src/components/ImageBlurFallback.tsx index 3f05275b..83292b9b 100644 --- a/src/components/ImageBlurFallback.tsx +++ b/src/components/ImageBlurFallback.tsx @@ -14,76 +14,68 @@ export default function ImageBlurFallback(props: ImageProps) { ...rest } = props; - const [isLoading, setIsLoading] = useState(false); - const [didLoad, setDidLoad] = useState(false); - const [hideBlurPlaceholder, setHideBlurPlaceholder] = useState(false); + const [wasCached, setWasCached] = useState(true); + const [isLoading, setIsLoading] = useState(true); + const [didError, setDidError] = useState(false); + + const [hideBluePlaceholder, setHideBluePlaceholder] = useState(false); const imageClassName = 'object-cover h-full'; const imgRef = useRef(null); useEffect(() => { - const timeout = setTimeout(() => { - // Check if image has already loaded/is cached - // in order to not show blur placeholder on every load - setIsLoading(imgRef.current?.complete !== true); - setDidLoad(imgRef.current?.complete === true); - }, 100); + const timeout = setTimeout( + () => setWasCached(imgRef.current?.complete ?? false), + 100, + ); return () => clearTimeout(timeout); }, []); useEffect(() => { - if (didLoad) { - const timeout = setTimeout(() => - // Hide blurred placeholder after image has faded in after 300ms - setHideBlurPlaceholder(true) - , 500); + if (!isLoading && !didError) { + const timeout = setTimeout(() => { + setHideBluePlaceholder(true); + }, 1000); return () => clearTimeout(timeout); } - }, [didLoad]); + }, [isLoading, didError]); const showPlaceholder = BLUR_ENABLED && props.blurDataURL && - !hideBlurPlaceholder; + !wasCached && + !hideBluePlaceholder; return (
- { - setIsLoading(false); - setDidLoad(true); - }, - onError: () => { - setIsLoading(false); - }, - }} /> {showPlaceholder && } + setIsLoading(false), + onError: () => setDidError(true), + }} />
); } From 941af6af09d9c38de217477f9c3fc68b48fe789c Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Fri, 23 Feb 2024 18:57:58 -0600 Subject: [PATCH 09/11] Fix blur overflow --- src/components/ImageBlurFallback.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ImageBlurFallback.tsx b/src/components/ImageBlurFallback.tsx index 83292b9b..27771b05 100644 --- a/src/components/ImageBlurFallback.tsx +++ b/src/components/ImageBlurFallback.tsx @@ -51,7 +51,7 @@ export default function ImageBlurFallback(props: ImageProps) {
From 2c4bdd8003f9dd7bb7b43c2ca58c7f11186273b0 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Fri, 23 Feb 2024 19:33:05 -0600 Subject: [PATCH 10/11] Hybrid approach for placeholder with/without blur data --- src/components/ImageBlurFallback.tsx | 44 ++++++++++++++++------------ 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/components/ImageBlurFallback.tsx b/src/components/ImageBlurFallback.tsx index 27771b05..64d1c4ef 100644 --- a/src/components/ImageBlurFallback.tsx +++ b/src/components/ImageBlurFallback.tsx @@ -18,7 +18,7 @@ export default function ImageBlurFallback(props: ImageProps) { const [isLoading, setIsLoading] = useState(true); const [didError, setDidError] = useState(false); - const [hideBluePlaceholder, setHideBluePlaceholder] = useState(false); + const [hideBlurPlaceholder, setHideBlurPlaceholder] = useState(false); const imageClassName = 'object-cover h-full'; @@ -35,39 +35,45 @@ export default function ImageBlurFallback(props: ImageProps) { useEffect(() => { if (!isLoading && !didError) { const timeout = setTimeout(() => { - setHideBluePlaceholder(true); + setHideBlurPlaceholder(true); }, 1000); return () => clearTimeout(timeout); } }, [isLoading, didError]); const showPlaceholder = - BLUR_ENABLED && - props.blurDataURL && !wasCached && - !hideBluePlaceholder; + !hideBlurPlaceholder; return (
{showPlaceholder && - } +
+ {(BLUR_ENABLED && props.blurDataURL) + ? + :
} +
} Date: Fri, 23 Feb 2024 19:51:08 -0600 Subject: [PATCH 11/11] Bump next-auth --- package.json | 2 +- pnpm-lock.yaml | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 52d750a1..a819d072 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "jest-environment-jsdom": "^29.7.0", "nanoid": "^5.0.6", "next": "14.1.0", - "next-auth": "5.0.0-beta.9", + "next-auth": "5.0.0-beta.13", "next-themes": "^0.2.1", "postcss": "8.4.35", "react": "18.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1ce8f7a2..ad97bb96 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -96,8 +96,8 @@ dependencies: specifier: 14.1.0 version: 14.1.0(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0) next-auth: - specifier: 5.0.0-beta.9 - version: 5.0.0-beta.9(next@14.1.0)(react@18.2.0) + specifier: 5.0.0-beta.13 + version: 5.0.0-beta.13(next@14.1.0)(react@18.2.0) next-themes: specifier: ^0.2.1 version: 0.2.1(next@14.1.0)(react-dom@18.2.0)(react@18.2.0) @@ -153,11 +153,11 @@ packages: '@jridgewell/trace-mapping': 0.3.22 dev: false - /@auth/core@0.26.3: - resolution: {integrity: sha512-Ka6rMjWMdiQCvLW/CnYZxj4Rq2bhQ/ZtU32NLxmtyAaixGb0mRXQ9MxJUBZA7GHovbghdzu55p2Cb54qNlVFzw==} + /@auth/core@0.27.0: + resolution: {integrity: sha512-3bydnRJIM/Al6mkYmb53MsC+6G8ojw3lLPzwgVnX4dCo6N2lrib6Wq6r0vxZIhuHGjLObqqtUfpeaEj5aeTHFg==} peerDependencies: '@simplewebauthn/browser': ^9.0.1 - '@simplewebauthn/server': ^9.0.1 + '@simplewebauthn/server': ^9.0.2 nodemailer: ^6.8.0 peerDependenciesMeta: '@simplewebauthn/browser': @@ -6189,11 +6189,11 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: false - /next-auth@5.0.0-beta.9(next@14.1.0)(react@18.2.0): - resolution: {integrity: sha512-BWFiwJ/wzfxWpHnGpAoFsXHSlVofWgFns6tjtIGeDrXfEf3D+afnBpmzCNyek2RNYDVgMHi8Q5uXzFoNBd2l5g==} + /next-auth@5.0.0-beta.13(next@14.1.0)(react@18.2.0): + resolution: {integrity: sha512-2m2Gq69WQ0YXcHCCpHn2y5z1bxSlqD/XOuAgrdtz49/VIAdTFFeYZz97RYqf6xMF8VGmoG32VUnJ6LzaHk6Fwg==} peerDependencies: '@simplewebauthn/browser': ^9.0.1 - '@simplewebauthn/server': ^9.0.1 + '@simplewebauthn/server': ^9.0.2 next: ^14 nodemailer: ^6.6.5 react: ^18.2.0 @@ -6205,7 +6205,7 @@ packages: nodemailer: optional: true dependencies: - '@auth/core': 0.26.3 + '@auth/core': 0.27.0 next: 14.1.0(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0) react: 18.2.0 dev: false