From af0b004a79b06c801c579dea1834483a347abb75 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Mon, 6 May 2024 21:34:04 -0500 Subject: [PATCH] Auto-generate blur data when editing photos --- src/admin/EditButton.tsx | 19 ++++++------------- src/app/admin/photos/[photoId]/edit/page.tsx | 11 ++++++++--- src/photo/PhotoEditPageClient.tsx | 3 +++ src/photo/form/PhotoForm.tsx | 17 +++++++++++++---- src/services/next-image.ts | 5 +++++ 5 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/admin/EditButton.tsx b/src/admin/EditButton.tsx index 113aec65..112e5fd9 100644 --- a/src/admin/EditButton.tsx +++ b/src/admin/EditButton.tsx @@ -1,24 +1,17 @@ -import Link from 'next/link'; +import PathLoaderButton from '@/components/PathLoaderButton'; import { FaRegEdit } from 'react-icons/fa'; export default function EditButton ({ href, - label = 'Edit', }: { href: string, - label?: string, }) { return ( - } > - - - {label} - - + Edit + ); } diff --git a/src/app/admin/photos/[photoId]/edit/page.tsx b/src/app/admin/photos/[photoId]/edit/page.tsx index b89549d5..38d9fa0c 100644 --- a/src/app/admin/photos/[photoId]/edit/page.tsx +++ b/src/app/admin/photos/[photoId]/edit/page.tsx @@ -3,8 +3,8 @@ import { getPhotoNoStore, getUniqueTagsCached } from '@/photo/cache'; import { PATH_ADMIN } from '@/site/paths'; import PhotoEditPageClient from '@/photo/PhotoEditPageClient'; import { AI_TEXT_GENERATION_ENABLED } from '@/site/config'; -import { resizeImageFromUrl } from '@/photo/server'; -import { getNextImageUrlForRequest } from '@/services/next-image'; +import { blurImageFromUrl, resizeImageFromUrl } from '@/photo/server'; +import { getNextImageUrlForManipulation } from '@/services/next-image'; export default async function PhotoEditPage({ params: { photoId }, @@ -21,15 +21,20 @@ export default async function PhotoEditPage({ // Only generate image thumbnails when AI generation is enabled const imageThumbnailBase64 = AI_TEXT_GENERATION_ENABLED - ? await resizeImageFromUrl(getNextImageUrlForRequest(photo.url, 640)) + ? await resizeImageFromUrl(getNextImageUrlForManipulation(photo.url)) : ''; + const blurData = await blurImageFromUrl( + getNextImageUrlForManipulation(photo.url) + ); + return ( ); }; diff --git a/src/photo/PhotoEditPageClient.tsx b/src/photo/PhotoEditPageClient.tsx index 94561417..77b83492 100644 --- a/src/photo/PhotoEditPageClient.tsx +++ b/src/photo/PhotoEditPageClient.tsx @@ -22,11 +22,13 @@ export default function PhotoEditPageClient({ uniqueTags, hasAiTextGeneration, imageThumbnailBase64, + blurData, }: { photo: Photo uniqueTags: TagsWithMeta hasAiTextGeneration: boolean imageThumbnailBase64: string + blurData: string }) { const seedExifData = { url: photo.url }; @@ -86,6 +88,7 @@ export default function PhotoEditPageClient({ updatedExifData={hasExifDataBeenFound ? updatedExifData : undefined} + updatedBlurData={blurData} uniqueTags={uniqueTags} aiContent={hasAiTextGeneration ? aiContent : undefined} onTitleChange={setUpdatedTitle} diff --git a/src/photo/form/PhotoForm.tsx b/src/photo/form/PhotoForm.tsx index f1b81c2e..674c8ff1 100644 --- a/src/photo/form/PhotoForm.tsx +++ b/src/photo/form/PhotoForm.tsx @@ -27,22 +27,25 @@ import Spinner from '@/components/Spinner'; import usePreventNavigation from '@/utility/usePreventNavigation'; import { useAppState } from '@/state/AppState'; import UpdateBlurDataButton from '../UpdateBlurDataButton'; +import { getNextImageUrlForManipulation } from '@/services/next-image'; const THUMBNAIL_SIZE = 300; export default function PhotoForm({ + type = 'create', initialPhotoForm, updatedExifData, - type = 'create', + updatedBlurData, uniqueTags, aiContent, onTitleChange, onTextContentChange, onFormStatusChange, }: { + type?: 'create' | 'edit' initialPhotoForm: Partial updatedExifData?: Partial - type?: 'create' | 'edit' + updatedBlurData?: string uniqueTags?: TagsWithMeta aiContent?: AiContent onTitleChange?: (updatedTitle: string) => void @@ -110,6 +113,12 @@ export default function PhotoForm({ const url = formData.url ?? ''; + useEffect(() => + setFormData(data => updatedBlurData + ? { ...data, blurData: updatedBlurData } + : data) + , [updatedBlurData]); + useEffect(() => setFormData(data => aiContent?.title ? { ...data, title: aiContent?.title } @@ -184,9 +193,9 @@ export default function PhotoForm({ shouldConfirm={Boolean(formData.semanticDescription)} />; case 'blurData': - return type === 'edit' + return shouldDebugBlur && type === 'edit' && formData.url ? setFormData(data => ({ ...data, blurData }))} /> diff --git a/src/services/next-image.ts b/src/services/next-image.ts index 9b8cc116..13ce7ffe 100644 --- a/src/services/next-image.ts +++ b/src/services/next-image.ts @@ -23,3 +23,8 @@ export const getNextImageUrlForRequest = ( return url.toString(); }; + +// Generate small, low-bandwidth images for quick manipulations such as +// generating blur data or image thumbnails for AI text generation +export const getNextImageUrlForManipulation = (imageUrl: string) => + getNextImageUrlForRequest(imageUrl, 640, 90);