Optimize EXIF capture on edit
This commit is contained in:
parent
2520170639
commit
d6da955bf4
@ -27,6 +27,7 @@
|
||||
"date-fns": "^4.1.0",
|
||||
"date-fns-tz": "^3.2.0",
|
||||
"exifr": "^7.1.3",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"framer-motion": "^12.4.10",
|
||||
"nanoid": "^5.1.2",
|
||||
"next": "15.2.1",
|
||||
|
||||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@ -62,6 +62,9 @@ importers:
|
||||
exifr:
|
||||
specifier: ^7.1.3
|
||||
version: 7.1.3
|
||||
fast-deep-equal:
|
||||
specifier: ^3.1.3
|
||||
version: 3.1.3
|
||||
framer-motion:
|
||||
specifier: ^12.4.10
|
||||
version: 12.4.10(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
|
||||
@ -4,11 +4,11 @@ import LoaderButton from '@/components/primitives/LoaderButton';
|
||||
import SubmitButtonWithStatus from '@/components/SubmitButtonWithStatus';
|
||||
import { getExifDataAction } from '@/photo/actions';
|
||||
import { PhotoFormData } from '@/photo/form';
|
||||
import IconGrSync from '@/app/IconGrSync';
|
||||
import { clsx } from 'clsx/lite';
|
||||
import { ComponentProps, useState } from 'react';
|
||||
import { LuDatabaseBackup } from 'react-icons/lu';
|
||||
|
||||
export default function ExifSyncButton({
|
||||
export default function ExifCaptureButton({
|
||||
photoUrl,
|
||||
onSync,
|
||||
}: {
|
||||
@ -27,7 +27,8 @@ export default function ExifSyncButton({
|
||||
.then(onSync)
|
||||
.finally(() => setIsLoading(false));
|
||||
}}
|
||||
icon={<IconGrSync
|
||||
icon={<LuDatabaseBackup
|
||||
size={16}
|
||||
className={clsx(
|
||||
'translate-y-[0.5px] translate-x-[0.5px]',
|
||||
'sm:translate-x-[-0.5px]',
|
||||
@ -8,7 +8,7 @@ import PhotoForm from './form/PhotoForm';
|
||||
import { Tags } from '@/tag';
|
||||
import AiButton from './ai/AiButton';
|
||||
import usePhotoFormParent from './form/usePhotoFormParent';
|
||||
import ExifSyncButton from '@/admin/ExifSyncButton';
|
||||
import ExifCaptureButton from '@/admin/ExifCaptureButton';
|
||||
import { useState } from 'react';
|
||||
import { Recipes } from '@/recipe';
|
||||
|
||||
@ -57,7 +57,7 @@ export default function PhotoEditPageClient({
|
||||
<div className="flex gap-2">
|
||||
{hasAiTextGeneration &&
|
||||
<AiButton {...{ aiContent, shouldConfirm: hasTextContent }} />}
|
||||
<ExifSyncButton
|
||||
<ExifCaptureButton
|
||||
photoUrl={photo.url}
|
||||
onSync={setUpdatedExifData}
|
||||
/>
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
FIELDS_WITH_JSON,
|
||||
FORM_METADATA_ENTRIES,
|
||||
PhotoFormData,
|
||||
convertFormKeysToLabels,
|
||||
@ -31,6 +32,7 @@ import { BLUR_ENABLED, IS_PREVIEW } from '@/app/config';
|
||||
import { PhotoDbInsert } from '..';
|
||||
import ErrorNote from '@/components/ErrorNote';
|
||||
import { convertRecipesForForm, Recipes } from '@/recipe';
|
||||
import deepEqual from 'fast-deep-equal/es6/react';
|
||||
|
||||
const THUMBNAIL_SIZE = 300;
|
||||
|
||||
@ -89,9 +91,16 @@ export default function PhotoForm({
|
||||
const changedKeys: (keyof PhotoFormData)[] = [];
|
||||
|
||||
setFormData(currentForm => {
|
||||
Object.entries(updatedExifData ?? {})
|
||||
(Object.entries(updatedExifData ?? {}) as
|
||||
[keyof PhotoFormData, string][])
|
||||
.forEach(([key, value]) => {
|
||||
if (currentForm[key as keyof PhotoFormData] !== value) {
|
||||
let a = currentForm[key];
|
||||
let b = value;
|
||||
if (FIELDS_WITH_JSON.includes(key)) {
|
||||
a = JSON.parse(a ?? '');
|
||||
b = JSON.parse(b ?? '');
|
||||
}
|
||||
if (!deepEqual(a, b)) {
|
||||
changedKeys.push(key as keyof PhotoFormData);
|
||||
}
|
||||
});
|
||||
|
||||
@ -63,6 +63,7 @@ type FormMeta = {
|
||||
tagOptionsLimit?: number
|
||||
tagOptionsLimitValidationMessage?: string
|
||||
shouldNotOverwriteWithNullDataOnSync?: boolean
|
||||
isJson?: boolean
|
||||
};
|
||||
|
||||
const STRING_MAX_LENGTH_SHORT = 255;
|
||||
@ -130,6 +131,7 @@ const FORM_METADATA = (
|
||||
capitalize: false,
|
||||
shouldHide: ({ make }) => make !== MAKE_FUJIFILM,
|
||||
shouldNotOverwriteWithNullDataOnSync: true,
|
||||
isJson: true,
|
||||
validate: value => {
|
||||
let validationMessage = undefined;
|
||||
if (value) {
|
||||
@ -166,6 +168,10 @@ const FORM_METADATA = (
|
||||
hidden: { label: 'hidden', type: 'checkbox' },
|
||||
});
|
||||
|
||||
export const FIELDS_WITH_JSON = Object.entries(FORM_METADATA())
|
||||
.filter(([_, meta]) => meta.isJson)
|
||||
.map(([key]) => key as keyof PhotoFormData);
|
||||
|
||||
export const FIELDS_TO_NOT_OVERWRITE_WITH_NULL_DATA_ON_SYNC =
|
||||
Object.entries(FORM_METADATA())
|
||||
.filter(([_, meta]) => meta.shouldNotOverwriteWithNullDataOnSync)
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
type SimpleObject = Record<string, string>;
|
||||
|
||||
export const areSimpleObjectsEqual = (
|
||||
obj1: SimpleObject,
|
||||
obj2: SimpleObject,
|
||||
): boolean => {
|
||||
const obj1Keys = Object.keys(obj1);
|
||||
const obj2Keys = Object.keys(obj2);
|
||||
|
||||
return obj1Keys.length === obj2Keys.length
|
||||
? obj1Keys.every((key) => obj1[key] === obj2[key])
|
||||
: false;
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user