Refine photo matte implementation

This commit is contained in:
Sam Becker 2024-05-09 19:09:05 -05:00
parent a76fa30331
commit 577371e28f
9 changed files with 53 additions and 68 deletions

View File

@ -96,7 +96,7 @@ Application behavior can be changed by configuring the following environment var
- `NEXT_PUBLIC_PRO_MODE = 1` enables higher quality image storage (results in increased storage usage) - `NEXT_PUBLIC_PRO_MODE = 1` enables higher quality image storage (results in increased storage usage)
- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PAGES = 1` enables static optimization for pages, i.e., renders pages at build time (results in increased project usage)—⚠️ _Experimental_ - `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PAGES = 1` enables static optimization for pages, i.e., renders pages at build time (results in increased project usage)—⚠️ _Experimental_
- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_OG_IMAGES = 1` enables static optimization for OG images, i.e., renders images at build time (results in increased project usage)—⚠️ _Experimental_ - `NEXT_PUBLIC_STATICALLY_OPTIMIZE_OG_IMAGES = 1` enables static optimization for OG images, i.e., renders images at build time (results in increased project usage)—⚠️ _Experimental_
- `NEXT_PUBLIC_MATTE_SETTING = light` constrains the size of each photo, and enables a surrounding border (can be set to `light` or `dark`) - `NEXT_PUBLIC_MATTE_PHOTOS = 1` constrains the size of each photo, and enables a surrounding border (potentially useful for photos with tall aspect ratios)
- `NEXT_PUBLIC_BLUR_DISABLED = 1` prevents image blur data being stored and displayed (potentially useful for limiting Postgres usage) - `NEXT_PUBLIC_BLUR_DISABLED = 1` prevents image blur data being stored and displayed (potentially useful for limiting Postgres usage)
- `NEXT_PUBLIC_GEO_PRIVACY = 1` disables collection/display of location-based data - `NEXT_PUBLIC_GEO_PRIVACY = 1` disables collection/display of location-based data
- `NEXT_PUBLIC_IGNORE_PRIORITY_ORDER = 1` prevents `priority_order` field affecting photo order - `NEXT_PUBLIC_IGNORE_PRIORITY_ORDER = 1` prevents `priority_order` field affecting photo order

View File

@ -70,13 +70,13 @@ export default function CommandKClient({
isUserSignedIn, isUserSignedIn,
setUserEmail, setUserEmail,
isCommandKOpen: isOpen, isCommandKOpen: isOpen,
matteSetting, arePhotosMatted,
setMatteSetting,
shouldShowBaselineGrid, shouldShowBaselineGrid,
shouldDebugBlur, shouldDebugBlur,
setIsCommandKOpen: setIsOpen, setIsCommandKOpen: setIsOpen,
setShouldRespondToKeyboardCommands, setShouldRespondToKeyboardCommands,
setShouldShowBaselineGrid, setShouldShowBaselineGrid,
setArePhotosMatted,
setShouldDebugBlur, setShouldDebugBlur,
} = useAppState(); } = useAppState();
@ -199,15 +199,9 @@ export default function CommandKClient({
heading: 'Debug Tools', heading: 'Debug Tools',
accessory: <RiToolsFill size={16} className="translate-x-[-1px]" />, accessory: <RiToolsFill size={16} className="translate-x-[-1px]" />,
items: [{ items: [{
label: 'Toggle Matte Setting', label: 'Toggle Photo Matting',
action: () => setMatteSetting?.(prev => { action: () => setArePhotosMatted?.(prev => !prev),
if (!prev) { annotation: arePhotosMatted ? <FaCheck size={12} /> : undefined,
return 'light';
} else if (prev === 'light') {
return 'dark';
}
}),
annotation: Boolean(matteSetting) ? <FaCheck size={12} /> : undefined,
}, { }, {
label: 'Toggle Blur Debug', label: 'Toggle Blur Debug',
action: () => setShouldDebugBlur?.(prev => !prev), action: () => setShouldDebugBlur?.(prev => !prev),

View File

@ -75,8 +75,9 @@ export default function ImageBlurFallback(props: ImageProps & {
<div className={clsx( <div className={clsx(
'@container', '@container',
'absolute inset-0', 'absolute inset-0',
'bg-main overflow-hidden', 'overflow-hidden',
'transition-opacity duration-300 ease-in', 'transition-opacity duration-300 ease-in',
!(BLUR_ENABLED && props.blurDataURL) && 'bg-main',
(isLoading || shouldDebugBlur) ? 'opacity-100' : 'opacity-0', (isLoading || shouldDebugBlur) ? 'opacity-100' : 'opacity-0',
)}> )}>
{(BLUR_ENABLED && props.blurDataURL) {(BLUR_ENABLED && props.blurDataURL)

View File

@ -69,7 +69,7 @@ export default function PhotoLarge({
useOnVisible(ref, onVisible); useOnVisible(ref, onVisible);
const { matteSetting } = useAppState(); const { arePhotosMatted } = useAppState();
return ( return (
<SiteGrid <SiteGrid
@ -79,28 +79,30 @@ export default function PhotoLarge({
href={pathForPhoto(photo)} href={pathForPhoto(photo)}
className={clsx( className={clsx(
'active:brightness-75', 'active:brightness-75',
Boolean(matteSetting) && arePhotosMatted &&
'flex items-center justify-center aspect-[3/2]', 'flex items-center aspect-[3/2] bg-gray-100',
matteSetting === 'light' && 'bg-invert',
matteSetting === 'dark' && 'bg-dim',
)} )}
prefetch={prefetch} prefetch={prefetch}
> >
<ImageLarge <div className={clsx(
className={clsx(Boolean(matteSetting) && arePhotosMatted &&
'flex items-center justify-center h-full')} 'flex items-center justify-center w-full',
imgClassName={clsx( arePhotosMatted && photo.aspectRatio >= 1
Boolean(matteSetting) && 'object-scale-down', ? 'h-[80%]'
Boolean(matteSetting) && : 'h-[90%]',
photo.aspectRatio >= 1 ? 'max-h-[80%]' : 'max-h-[90%]' )}>
)} <ImageLarge
alt={altTextForPhoto(photo)} className={clsx(arePhotosMatted && 'h-full')}
src={photo.url} imgClassName={clsx(arePhotosMatted &&
aspectRatio={photo.aspectRatio} 'object-contain w-full h-full')}
blurData={photo.blurData} alt={altTextForPhoto(photo)}
blurCompatibilityMode={doesPhotoNeedBlurCompatibility(photo)} src={photo.url}
priority={priority} aspectRatio={photo.aspectRatio}
/> blurData={photo.blurData}
blurCompatibilityMode={doesPhotoNeedBlurCompatibility(photo)}
priority={priority}
/>
</div>
</Link>} </Link>}
contentSide={ contentSide={
<DivDebugBaselineGrid className={clsx( <DivDebugBaselineGrid className={clsx(

View File

@ -44,7 +44,7 @@ export default function SiteChecklistClient({
isStaticallyOptimized, isStaticallyOptimized,
arePagesStaticallyOptimized, arePagesStaticallyOptimized,
areOGImagesStaticallyOptimized, areOGImagesStaticallyOptimized,
matteSetting, arePhotosMatted,
isBlurEnabled, isBlurEnabled,
isGeoPrivacyEnabled, isGeoPrivacyEnabled,
isPriorityOrderEnabled, isPriorityOrderEnabled,
@ -123,9 +123,9 @@ export default function SiteChecklistClient({
> >
<span className="inline-flex items-center gap-1"> <span className="inline-flex items-center gap-1">
<span className={clsx( <span className={clsx(
'text-xs font-medium tracking-wide', 'text-[11px] font-medium tracking-wide',
'px-0.5 py-0.5', 'px-0.5 py-[0.5px]',
'rounded-sm', 'rounded-[5px]',
'bg-gray-100 dark:bg-gray-800', 'bg-gray-100 dark:bg-gray-800',
)}> )}>
`{variable}` `{variable}`
@ -135,7 +135,7 @@ export default function SiteChecklistClient({
</div>; </div>;
const renderEnvVars = (variables: string[]) => const renderEnvVars = (variables: string[]) =>
<div className="py-1 space-y-1"> <div className="py-0.5">
{variables.map(envVar => renderEnvVar(envVar))} {variables.map(envVar => renderEnvVar(envVar))}
</div>; </div>;
@ -370,26 +370,24 @@ export default function SiteChecklistClient({
{renderSubStatus( {renderSubStatus(
arePagesStaticallyOptimized ? 'checked' : 'optional', arePagesStaticallyOptimized ? 'checked' : 'optional',
renderEnvVars(['NEXT_PUBLIC_STATICALLY_OPTIMIZE_PAGES']), renderEnvVars(['NEXT_PUBLIC_STATICALLY_OPTIMIZE_PAGES']),
'translate-y-[4.5px]', 'translate-y-[3.5px]',
)} )}
{renderSubStatus( {renderSubStatus(
areOGImagesStaticallyOptimized ? 'checked' : 'optional', areOGImagesStaticallyOptimized ? 'checked' : 'optional',
renderEnvVars(['NEXT_PUBLIC_STATICALLY_OPTIMIZE_OG_IMAGES']), renderEnvVars(['NEXT_PUBLIC_STATICALLY_OPTIMIZE_OG_IMAGES']),
'translate-y-[4.5px]', 'translate-y-[3.5px]',
)} )}
</ChecklistRow> </ChecklistRow>
<ChecklistRow <ChecklistRow
title={'Photo Matte' + (matteSetting title="Photo Matting"
? `: ${matteSetting?.toLocaleUpperCase()}` status={arePhotosMatted}
: '')}
status={Boolean(matteSetting)}
isPending={isPendingPage} isPending={isPendingPage}
optional optional
> >
Set environment variable to {'"light"'} or {'"dark"'} to Set environment variable to {'"1"'} to constrain the size
{' '} {' '}
constrain the size of each photo, and enable a surrounding border: of each photo, and enable a surrounding border:
{renderEnvVars(['NEXT_PUBLIC_MATTE_SETTING'])} {renderEnvVars(['NEXT_PUBLIC_MATTE_PHOTOS'])}
</ChecklistRow> </ChecklistRow>
<ChecklistRow <ChecklistRow
title="Image Blur" title="Image Blur"

View File

@ -114,12 +114,8 @@ export const STATICALLY_OPTIMIZED_PAGES =
process.env.NEXT_PUBLIC_STATICALLY_OPTIMIZE_PAGES === '1'; process.env.NEXT_PUBLIC_STATICALLY_OPTIMIZE_PAGES === '1';
export const STATICALLY_OPTIMIZED_OG_IMAGES = export const STATICALLY_OPTIMIZED_OG_IMAGES =
process.env.NEXT_PUBLIC_STATICALLY_OPTIMIZE_OG_IMAGES === '1'; process.env.NEXT_PUBLIC_STATICALLY_OPTIMIZE_OG_IMAGES === '1';
export const MATTE_SETTING = export const MATTE_PHOTOS =
process.env.NEXT_PUBLIC_MATTE_SETTING === 'light' process.env.NEXT_PUBLIC_MATTE_PHOTOS === '1';
? 'light'
: process.env.NEXT_PUBLIC_MATTE_SETTING === 'dark'
? 'dark'
: undefined;
export const BLUR_ENABLED = export const BLUR_ENABLED =
process.env.NEXT_PUBLIC_BLUR_DISABLED !== '1'; process.env.NEXT_PUBLIC_BLUR_DISABLED !== '1';
export const GEO_PRIVACY_ENABLED = export const GEO_PRIVACY_ENABLED =
@ -183,7 +179,7 @@ export const CONFIG_CHECKLIST_STATUS = {
), ),
arePagesStaticallyOptimized: STATICALLY_OPTIMIZED_PAGES, arePagesStaticallyOptimized: STATICALLY_OPTIMIZED_PAGES,
areOGImagesStaticallyOptimized: STATICALLY_OPTIMIZED_OG_IMAGES, areOGImagesStaticallyOptimized: STATICALLY_OPTIMIZED_OG_IMAGES,
matteSetting: MATTE_SETTING, arePhotosMatted: MATTE_PHOTOS,
isBlurEnabled: BLUR_ENABLED, isBlurEnabled: BLUR_ENABLED,
isGeoPrivacyEnabled: GEO_PRIVACY_ENABLED, isGeoPrivacyEnabled: GEO_PRIVACY_ENABLED,
isAiTextGenerationEnabled: AI_TEXT_GENERATION_ENABLED, isAiTextGenerationEnabled: AI_TEXT_GENERATION_ENABLED,

View File

@ -162,10 +162,6 @@
@apply @apply
bg-gray-900 dark:bg-gray-100 bg-gray-900 dark:bg-gray-100
} }
.bg-dim {
@apply
bg-gray-200/40 dark:bg-gray-800/40
}
/* Utilities: Baseline Grid */ /* Utilities: Baseline Grid */
.space-y-baseline { .space-y-baseline {
@apply @apply

View File

@ -1,13 +1,11 @@
import { Dispatch, SetStateAction, createContext, useContext } from 'react'; import { Dispatch, SetStateAction, createContext, useContext } from 'react';
import { AnimationConfig } from '@/components/AnimateItems'; import { AnimationConfig } from '@/components/AnimateItems';
export type MatteSetting = 'light' | 'dark' | undefined;
export interface AppStateContext { export interface AppStateContext {
previousPathname?: string previousPathname?: string
hasLoaded?: boolean hasLoaded?: boolean
matteSetting?: MatteSetting arePhotosMatted?: boolean
setMatteSetting?: Dispatch<SetStateAction<MatteSetting>> setArePhotosMatted?: Dispatch<SetStateAction<boolean>>
swrTimestamp?: number swrTimestamp?: number
invalidateSwr?: () => void invalidateSwr?: () => void
userEmail?: string userEmail?: string

View File

@ -1,12 +1,12 @@
'use client'; 'use client';
import { useState, useEffect, ReactNode, useCallback } from 'react'; import { useState, useEffect, ReactNode, useCallback } from 'react';
import { AppStateContext, MatteSetting } from './AppState'; import { AppStateContext } from './AppState';
import { AnimationConfig } from '@/components/AnimateItems'; import { AnimationConfig } from '@/components/AnimateItems';
import usePathnames from '@/utility/usePathnames'; import usePathnames from '@/utility/usePathnames';
import { getAuthAction } from '@/auth/actions'; import { getAuthAction } from '@/auth/actions';
import useSWR from 'swr'; import useSWR from 'swr';
import { MATTE_SETTING } from '@/site/config'; import { MATTE_PHOTOS } from '@/site/config';
export default function AppStateProvider({ export default function AppStateProvider({
children, children,
@ -17,8 +17,8 @@ export default function AppStateProvider({
const [hasLoaded, setHasLoaded] = const [hasLoaded, setHasLoaded] =
useState(false); useState(false);
const [matteSetting, setMatteSetting] = const [arePhotosMatted, setArePhotosMatted] =
useState<MatteSetting>(MATTE_SETTING); useState(MATTE_PHOTOS);
const [swrTimestamp, setSwrTimestamp] = const [swrTimestamp, setSwrTimestamp] =
useState(Date.now()); useState(Date.now());
const [userEmail, setUserEmail] = const [userEmail, setUserEmail] =
@ -53,8 +53,8 @@ export default function AppStateProvider({
value={{ value={{
previousPathname, previousPathname,
hasLoaded, hasLoaded,
matteSetting, arePhotosMatted,
setMatteSetting, setArePhotosMatted,
swrTimestamp, swrTimestamp,
invalidateSwr, invalidateSwr,
setHasLoaded, setHasLoaded,