Add server actions to get/override EXIF data

This commit is contained in:
Sam Becker 2023-11-01 00:10:42 -05:00
parent bf78ced898
commit 8bb5c2990b
6 changed files with 90 additions and 11 deletions

View File

@ -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';

View File

@ -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';

View File

@ -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<Partial<PhotoFormData>>(
getExifDataAction,
{ url: photo.url},
);
return (
<AdminChildPage
backPath={PATH_ADMIN_PHOTOS}
backLabel="Photos"
breadcrumb={photo.title || photo.id}
accessory={<SubmitButtonWithStatus icon={<BiRefresh size={18} />}>
Refresh EXIF
</SubmitButtonWithStatus>}
accessory={
<form action={action}>
<input name="photoUrl" value={photo.url} hidden readOnly />
<SubmitButtonWithStatus
icon={<BiRefresh size={18} className="translate-y-[-1.5px]" />}
>
Refresh EXIF
</SubmitButtonWithStatus>
</form>}
>
<PhotoForm
type="edit"
initialPhotoForm={convertPhotoToFormData(photo)}
updatedExifData={updatedExifData}
/>
</AdminChildPage>
);

View File

@ -23,16 +23,26 @@ const THUMBNAIL_HEIGHT = 200;
export default function PhotoForm({
initialPhotoForm,
updatedExifData,
type = 'create',
debugBlur,
}: {
initialPhotoForm: Partial<PhotoFormData>
updatedExifData?: Partial<PhotoFormData>
type?: 'create' | 'edit'
debugBlur?: boolean
}) {
const [formData, setFormData] =
useState<Partial<PhotoFormData>>(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(() => {

View File

@ -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<PhotoFormData>,
): Promise<Partial<PhotoFormData>> {
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();

View File

@ -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);
};