diff --git a/src/components/FieldSetWithStatus.tsx b/src/components/FieldSetWithStatus.tsx index bdc62717..3bf31245 100644 --- a/src/components/FieldSetWithStatus.tsx +++ b/src/components/FieldSetWithStatus.tsx @@ -1,7 +1,6 @@ 'use client'; import { LegacyRef } from 'react'; -// @ts-ignore import { useFormStatus } from 'react-dom'; import Spinner from './Spinner'; import { cc } from '@/utility/css'; diff --git a/src/components/SubmitButtonWithStatus.tsx b/src/components/SubmitButtonWithStatus.tsx index bb9d661e..60026d46 100644 --- a/src/components/SubmitButtonWithStatus.tsx +++ b/src/components/SubmitButtonWithStatus.tsx @@ -1,7 +1,6 @@ 'use client'; import { HTMLProps } from 'react'; -// @ts-ignore import { useFormStatus } from 'react-dom'; import Spinner from './Spinner'; import { cc } from '@/utility/css'; diff --git a/src/photo/PhotoEditPageClient.tsx b/src/photo/PhotoEditPageClient.tsx index d6be09b2..ffb9f157 100644 --- a/src/photo/PhotoEditPageClient.tsx +++ b/src/photo/PhotoEditPageClient.tsx @@ -5,26 +5,40 @@ import { Photo } from '.'; import { PATH_ADMIN_PHOTOS } from '@/site/paths'; import SubmitButtonWithStatus from '@/components/SubmitButtonWithStatus'; import { BiRefresh } from 'react-icons/bi'; -import { convertPhotoToFormData } from './form'; +import { PhotoFormData, convertPhotoToFormData } from './form'; import PhotoForm from './PhotoForm'; +import { useFormState } from 'react-dom'; +import { getExifDataAction } from './actions'; export default function PhotoEditPageClient({ photo, }: { photo: Photo }) { + const [updatedExifData, action] = useFormState>( + getExifDataAction, + { url: photo.url}, + ); + return ( }> - Refresh EXIF - } + accessory={ +
+ + } + > + Refresh EXIF + +
} >
); diff --git a/src/photo/PhotoForm.tsx b/src/photo/PhotoForm.tsx index c2c7055f..36816bbd 100644 --- a/src/photo/PhotoForm.tsx +++ b/src/photo/PhotoForm.tsx @@ -23,16 +23,26 @@ const THUMBNAIL_HEIGHT = 200; export default function PhotoForm({ initialPhotoForm, + updatedExifData, type = 'create', debugBlur, }: { initialPhotoForm: Partial + updatedExifData?: Partial type?: 'create' | 'edit' debugBlur?: boolean }) { const [formData, setFormData] = useState>(initialPhotoForm); + useEffect(() => { + // Update form when EXIF data is refreshed by parent + setFormData(currentForm => ({ + ...currentForm, + ...updatedExifData, + })); + }, [updatedExifData]); + // Generate local date strings when // none can be harvested from EXIF useEffect(() => { diff --git a/src/photo/actions.ts b/src/photo/actions.ts index 811be878..7223beaf 100644 --- a/src/photo/actions.ts +++ b/src/photo/actions.ts @@ -6,8 +6,14 @@ import { sqlDeletePhotoTagGlobally, sqlUpdatePhoto, sqlRenamePhotoTagGlobally, + getPhoto, } from '@/services/postgres'; -import { convertFormDataToPhoto } from './form'; +import { + PhotoFormData, + convertFormDataToPhotoDbInsert, + convertPhotoFormDataToPhotoDbInsert, + convertPhotoToFormData, +} from './form'; import { redirect } from 'next/navigation'; import { convertUploadToPhoto, @@ -20,9 +26,10 @@ import { revalidatePhotosKey, } from '@/cache'; import { PATH_ADMIN_PHOTOS, PATH_ADMIN_TAGS } from '@/site/paths'; +import { extractFormDataFromUploadPath } from './server'; export async function createPhotoAction(formData: FormData) { - const photo = convertFormDataToPhoto(formData, true); + const photo = convertFormDataToPhotoDbInsert(formData, true); const updatedUrl = await convertUploadToPhoto(photo.url, photo.id); @@ -36,7 +43,7 @@ export async function createPhotoAction(formData: FormData) { } export async function updatePhotoAction(formData: FormData) { - const photo = convertFormDataToPhoto(formData); + const photo = convertFormDataToPhotoDbInsert(formData); await sqlUpdatePhoto(photo); @@ -84,7 +91,40 @@ export async function deleteBlobPhotoAction(formData: FormData) { if (formData.get('redirectToPhotos') === 'true') { redirect(PATH_ADMIN_PHOTOS); } -}; +} + +export async function getExifDataAction( + photoFormPrevious: Partial, +): Promise> { + const { url } = photoFormPrevious; + if (url) { + const { photoForm } = await extractFormDataFromUploadPath(url); + if (photoForm) { + return photoForm; + } + } + return {}; +} + +export async function syncPhotoExifDataAction(formData: FormData) { + const photoId = formData.get('photoId') as string; + if (photoId) { + const photo = await getPhoto(photoId); + if (photo) { + const { + photoForm: photoFormExif, + } = await extractFormDataFromUploadPath(photo.url); + if (photoFormExif) { + const photoFormDbInsert = convertPhotoFormDataToPhotoDbInsert({ + ...convertPhotoToFormData(photo), + ...photoFormExif, + }); + await sqlUpdatePhoto(photoFormDbInsert); + revalidatePhotosKey(); + } + } + } +} export async function syncCacheAction() { revalidateAllKeysAndPaths(); diff --git a/src/photo/form.ts b/src/photo/form.ts index 6c1ea947..860fd610 100644 --- a/src/photo/form.ts +++ b/src/photo/form.ts @@ -123,7 +123,7 @@ export const convertExifToFormData = ( : undefined, }); -export const convertFormDataToPhoto = ( +export const convertFormDataToPhotoDbInsert = ( formData: FormData, generateId?: boolean, ): PhotoDbInsert => { @@ -178,3 +178,20 @@ export const convertFormDataToPhoto = ( hidden: photoForm.hidden === 'true', }; }; + +const convertPhotoFormDataToFormData = ( + photoFormData: PhotoFormData, +) => { + const formData = new FormData(); + for (const key in photoFormData) { + formData.append(key, photoFormData[key as keyof PhotoFormData]); + } + return formData; +}; + +export const convertPhotoFormDataToPhotoDbInsert = ( + photoFormData: PhotoFormData, +) => { + const formData = convertPhotoFormDataToFormData(photoFormData); + return convertFormDataToPhotoDbInsert(formData); +};