'use client'; import { Photo } from '@/photo'; import AdminPhotosTable from '@/admin/AdminPhotosTable'; import IconGrSync from '@/components/icons/IconGrSync'; import Note from '@/components/Note'; import AdminChildPage from '@/components/AdminChildPage'; import { PATH_ADMIN_PHOTOS } from '@/app/paths'; import { useMemo, useRef, useState } from 'react'; import { syncPhotosAction } from '@/photo/actions'; import { useRouter } from 'next/navigation'; import ResponsiveText from '@/components/primitives/ResponsiveText'; import { LiaBroomSolid } from 'react-icons/lia'; import ProgressButton from '@/components/primitives/ProgressButton'; import ErrorNote from '@/components/ErrorNote'; import { getPhotosSyncStatusText } from '@/photo/sync'; const SYNC_BATCH_SIZE_MAX = 3; export default function AdminPhotosSyncClient({ photos, hasAiTextGeneration, }: { photos: Photo[] hasAiTextGeneration: boolean }) { // Use refs for non-reactive while loop state const photoIdsToSync = useRef(photos.map(photo => photo.id)); const errorRef = useRef(undefined); // Use state for updating progress button and error UI const [photoIdsSyncing, setPhotoIdsSyncing] = useState([]); const [error, setError] = useState(); const [progress, setProgress] = useState(0); const arePhotoIdsSyncing = photoIdsSyncing.length > 0; const router = useRouter(); const statusText = useMemo(() => getPhotosSyncStatusText(photos), [photos]); return ( Updates ({photos.length}) } accessory={} hideTextOnMobile={false} progress={progress} tooltip={photos.length === 1 ? 'Sync data for 1 photo' : `Sync data for all ${photos.length} photos`} onClick={async () => { if (window.confirm([ 'Are you sure you want to sync', photos.length === 1 ? '1 photo?' : `all ${photos.length} photos?`, 'Browser must remain open while syncing.', 'This action cannot be undone.', ].join(' '))) { errorRef.current = undefined; setError(undefined); while (photoIdsToSync.current.length > 0) { const photoIds = photoIdsToSync.current .slice(0, SYNC_BATCH_SIZE_MAX); setPhotoIdsSyncing(photoIds); await syncPhotosAction(photoIds) .then(() => { photoIdsToSync.current = photoIdsToSync.current.filter( id => !photoIds.includes(id), ); setProgress( (photos.length - photoIdsToSync.current.length) / photos.length, ); router.refresh(); }) .catch(e => { errorRef.current = e; setError(e); }); if (errorRef.current) { break; } } if (!errorRef.current) { router.push(PATH_ADMIN_PHOTOS); } else { setProgress(0); setPhotoIdsSyncing([]); router.refresh(); } } }} isLoading={arePhotoIdsSyncing} disabled={photoIdsSyncing.length > 0} > {arePhotoIdsSyncing ? 'Syncing ...' : 'Sync All'} } >
{error && Issue syncing: {' '} {error.message} } } >
Photo updates: {statusText}
Sync to capture new EXIF fields, improve blur data, {' '} use AI to generate missing text (if configured)
); }