diff --git a/src/admin/AdminPhotoMenu.tsx b/src/admin/AdminPhotoMenu.tsx index 7ce6a06b..c8c69221 100644 --- a/src/admin/AdminPhotoMenu.tsx +++ b/src/admin/AdminPhotoMenu.tsx @@ -21,7 +21,6 @@ import { RevalidatePhoto } from '@/photo/InfinitePhotoScroll'; import { MdOutlineFileDownload } from 'react-icons/md'; import MoreMenuItem from '@/components/more/MoreMenuItem'; import IconGrSync from '@/components/icons/IconGrSync'; -import { isPhotoOutdated } from '@/photo/outdated'; import InsightsIndicatorDot from './insights/InsightsIndicatorDot'; import IconFavs from '@/components/icons/IconFavs'; import IconEdit from '@/components/icons/IconEdit'; @@ -79,7 +78,7 @@ export default function AdminPhotoMenu({ label: 'Sync', labelComplex: Sync - {isPhotoOutdated(photo) && + {photo.needsSync && { exposureCompensationFormatted?: string takenAtNaiveFormatted: string recipeData?: FujifilmRecipe + needsSync?: boolean } export const parsePhotoFromDb = (photoDbRaw: PhotoDb): Photo => { const photoDb = camelcaseKeys( photoDbRaw as unknown as Record, ) as unknown as PhotoDb; - return { + const photo: Photo ={ ...photoDb, tags: photoDb.tags ?? [], focalLengthFormatted: @@ -130,15 +132,17 @@ export const parsePhotoFromDb = (photoDbRaw: PhotoDb): Photo => { formatExposureTime(photoDb.exposureTime), exposureCompensationFormatted: formatExposureCompensation(photoDb.exposureCompensation), + takenAtNaiveFormatted: + formatDateFromPostgresString(photoDb.takenAtNaive), recipeData: photoDb.recipeData // Legacy check on escaped, string-based JSON ? typeof photoDb.recipeData === 'string' ? JSON.parse(photoDb.recipeData) : photoDb.recipeData : undefined, - takenAtNaiveFormatted: - formatDateFromPostgresString(photoDb.takenAtNaive), }; + photo.needsSync = doesPhotoNeedSync(photo); + return photo; }; export const parseCachedPhotoDates = (photo: Photo) => ({ diff --git a/src/photo/outdated.ts b/src/photo/outdated.ts deleted file mode 100644 index 31d1fdfe..00000000 --- a/src/photo/outdated.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MAKE_FUJIFILM } from '@/platforms/fujifilm'; -import { Photo } from '.'; - -export const UPDATED_BEFORE_01 = new Date('2024-06-16'); -// UTC 2025-02-24 05:30:00 -export const UPDATED_BEFORE_02 = new Date(Date.UTC(2025, 1, 24, 5, 30, 0)); - -export const isPhotoOutdated = (photo: Photo) => { - return photo.updatedAt < UPDATED_BEFORE_01 || ( - photo.updatedAt < UPDATED_BEFORE_02 && - photo.make === MAKE_FUJIFILM - ); -}; diff --git a/src/photo/sync.ts b/src/photo/sync.ts new file mode 100644 index 00000000..f4a9d311 --- /dev/null +++ b/src/photo/sync.ts @@ -0,0 +1,28 @@ +import { MAKE_FUJIFILM } from '@/platforms/fujifilm'; +import { Photo } from '.'; +import { AI_TEXT_AUTO_GENERATED_FIELDS } from '@/app/config'; + +export const UPDATED_BEFORE_01 = new Date('2024-06-16'); +// UTC 2025-02-24 05:30:00 +export const UPDATED_BEFORE_02 = new Date(Date.UTC(2025, 1, 24, 5, 30, 0)); + +const isPhotoOutdated = (photo: Photo) => + photo.updatedAt < UPDATED_BEFORE_01 || ( + photo.updatedAt < UPDATED_BEFORE_02 && + photo.make === MAKE_FUJIFILM + ); + +const doesPhotoNeedAiText = ({ + title, + caption, + tags = [], + semanticDescription, +}: Photo) => + (AI_TEXT_AUTO_GENERATED_FIELDS.includes('title') && !title) || + (AI_TEXT_AUTO_GENERATED_FIELDS.includes('caption') && !caption) || + (AI_TEXT_AUTO_GENERATED_FIELDS.includes('tags') && tags.length === 0) || + (AI_TEXT_AUTO_GENERATED_FIELDS.includes('semantic') && !semanticDescription); + +export const doesPhotoNeedSync = (photo: Photo) => + isPhotoOutdated(photo) || + doesPhotoNeedAiText(photo);