Confirm before syncing photo with key command
This commit is contained in:
parent
c8080e9cf2
commit
84ff9885cc
@ -115,8 +115,7 @@ export default function AdminPhotosTable({
|
||||
{canEdit &&
|
||||
<EditButton path={pathForAdminPhotoEdit(photo)} />}
|
||||
<PhotoSyncButton
|
||||
photoId={photo.id}
|
||||
photoTitle={titleForPhoto(photo)}
|
||||
photo={photo}
|
||||
onSyncComplete={invalidateSwr}
|
||||
isSyncingExternal={photoIdsSyncing.includes(photo.id)}
|
||||
hasAiTextGeneration={hasAiTextGeneration}
|
||||
|
||||
@ -6,10 +6,11 @@ import { ComponentProps, useRef, useState } from 'react';
|
||||
import Tooltip from '@/components/Tooltip';
|
||||
import clsx from 'clsx/lite';
|
||||
import useScrollIntoView from '@/utility/useScrollIntoView';
|
||||
import { Photo } from '@/photo';
|
||||
import { syncPhotoConfirmText } from './confirm';
|
||||
|
||||
export default function PhotoSyncButton({
|
||||
photoId,
|
||||
photoTitle,
|
||||
photo,
|
||||
onSyncComplete,
|
||||
className,
|
||||
isSyncingExternal,
|
||||
@ -19,11 +20,10 @@ export default function PhotoSyncButton({
|
||||
shouldToast,
|
||||
shouldScrollIntoViewOnExternalSync,
|
||||
}: {
|
||||
photoId: string
|
||||
photoTitle?: string
|
||||
photo: Photo
|
||||
onSyncComplete?: () => void
|
||||
isSyncingExternal?: boolean
|
||||
hasAiTextGeneration?: boolean
|
||||
hasAiTextGeneration: boolean
|
||||
shouldConfirm?: boolean
|
||||
shouldToast?: boolean
|
||||
shouldScrollIntoViewOnExternalSync?: boolean
|
||||
@ -32,13 +32,6 @@ export default function PhotoSyncButton({
|
||||
|
||||
const [isSyncing, setIsSyncing] = useState(false);
|
||||
|
||||
const confirmText = ['Overwrite'];
|
||||
if (photoTitle) { confirmText.push(`"${photoTitle}"`); }
|
||||
confirmText.push('data from original file?');
|
||||
if (hasAiTextGeneration) { confirmText.push(
|
||||
'AI text will be generated for undefined fields.'); }
|
||||
confirmText.push('This action cannot be undone.');
|
||||
|
||||
useScrollIntoView({
|
||||
ref,
|
||||
shouldScrollIntoView:
|
||||
@ -55,14 +48,17 @@ export default function PhotoSyncButton({
|
||||
className="translate-y-[0.5px] translate-x-[0.5px]"
|
||||
/>}
|
||||
onClick={() => {
|
||||
if (!shouldConfirm || window.confirm(confirmText.join(' '))) {
|
||||
if (
|
||||
!shouldConfirm ||
|
||||
window.confirm(syncPhotoConfirmText(photo, hasAiTextGeneration))
|
||||
) {
|
||||
setIsSyncing(true);
|
||||
syncPhotoAction(photoId)
|
||||
syncPhotoAction(photo.id)
|
||||
.then(() => {
|
||||
onSyncComplete?.();
|
||||
if (shouldToast) {
|
||||
toastSuccess(photoTitle
|
||||
? `"${photoTitle}" data synced`
|
||||
toastSuccess(photo.title
|
||||
? `"${photo.title}" data synced`
|
||||
: 'Data synced');
|
||||
}
|
||||
})
|
||||
|
||||
14
src/admin/confirm.ts
Normal file
14
src/admin/confirm.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { Photo } from '@/photo';
|
||||
|
||||
export const syncPhotoConfirmText = (
|
||||
photo: Photo,
|
||||
hasAiTextGeneration: boolean,
|
||||
) => {
|
||||
const confirmText = ['Sync'];
|
||||
if (photo.title) { confirmText.push(`"${photo.title}"`); }
|
||||
confirmText.push('data from original image file?');
|
||||
if (hasAiTextGeneration) { confirmText.push(
|
||||
'AI text will be generated for undefined fields.'); }
|
||||
confirmText.push('This action cannot be undone.');
|
||||
return confirmText.join(' ');
|
||||
};
|
||||
@ -3,6 +3,7 @@ import PhotoHeader from '@/photo/PhotoHeader';
|
||||
import { Camera, cameraFromPhoto } from '.';
|
||||
import PhotoCamera from './PhotoCamera';
|
||||
import { descriptionForCameraPhotos } from './meta';
|
||||
import { AI_TEXT_GENERATION_ENABLED } from '@/app/config';
|
||||
|
||||
export default function CameraHeader({
|
||||
camera: cameraProp,
|
||||
@ -31,6 +32,7 @@ export default function CameraHeader({
|
||||
indexNumber={indexNumber}
|
||||
count={count}
|
||||
dateRange={dateRange}
|
||||
hasAiTextGeneration={AI_TEXT_GENERATION_ENABLED}
|
||||
includeShareButton
|
||||
/>
|
||||
);
|
||||
|
||||
@ -6,6 +6,7 @@ import PhotoHeader from '@/photo/PhotoHeader';
|
||||
import PhotoFilm from '@/film/PhotoFilm';
|
||||
import { getRecipePropsFromPhotos } from '@/recipe';
|
||||
import { useAppState } from '@/state/AppState';
|
||||
import { AI_TEXT_GENERATION_ENABLED } from '@/app/config';
|
||||
|
||||
export default function FilmHeader({
|
||||
film,
|
||||
@ -47,6 +48,7 @@ export default function FilmHeader({
|
||||
indexNumber={indexNumber}
|
||||
count={count}
|
||||
dateRange={dateRange}
|
||||
hasAiTextGeneration={AI_TEXT_GENERATION_ENABLED}
|
||||
includeShareButton
|
||||
/>
|
||||
);
|
||||
|
||||
@ -2,7 +2,7 @@ import { Photo, PhotoDateRange } from '@/photo';
|
||||
import { descriptionForFocalLengthPhotos } from '.';
|
||||
import PhotoHeader from '@/photo/PhotoHeader';
|
||||
import PhotoFocalLength from './PhotoFocalLength';
|
||||
|
||||
import { AI_TEXT_GENERATION_ENABLED } from '@/app/config';
|
||||
export default function FocalLengthHeader({
|
||||
focal,
|
||||
photos,
|
||||
@ -32,6 +32,7 @@ export default function FocalLengthHeader({
|
||||
indexNumber={indexNumber}
|
||||
count={count}
|
||||
dateRange={dateRange}
|
||||
hasAiTextGeneration={AI_TEXT_GENERATION_ENABLED}
|
||||
includeShareButton
|
||||
/>
|
||||
);
|
||||
|
||||
@ -3,6 +3,7 @@ import PhotoHeader from '@/photo/PhotoHeader';
|
||||
import { Lens, lensFromPhoto } from '.';
|
||||
import PhotoLens from './PhotoLens';
|
||||
import { descriptionForLensPhotos } from './meta';
|
||||
import { AI_TEXT_GENERATION_ENABLED } from '@/app/config';
|
||||
|
||||
export default function LensHeader({
|
||||
lens: lensProp,
|
||||
@ -31,6 +32,7 @@ export default function LensHeader({
|
||||
indexNumber={indexNumber}
|
||||
count={count}
|
||||
dateRange={dateRange}
|
||||
hasAiTextGeneration={AI_TEXT_GENERATION_ENABLED}
|
||||
includeShareButton
|
||||
/>
|
||||
);
|
||||
|
||||
@ -14,6 +14,7 @@ import PhotoHeader from './PhotoHeader';
|
||||
import RecipeHeader from '@/recipe/RecipeHeader';
|
||||
import { ReactNode } from 'react';
|
||||
import LensHeader from '@/lens/LensHeader';
|
||||
import { AI_TEXT_GENERATION_ENABLED } from '@/app/config';
|
||||
|
||||
export default function PhotoDetailPage({
|
||||
photo,
|
||||
@ -113,6 +114,7 @@ export default function PhotoDetailPage({
|
||||
selectedPhoto={photo}
|
||||
photos={photos}
|
||||
recipe={recipe}
|
||||
hasAiTextGeneration={AI_TEXT_GENERATION_ENABLED}
|
||||
/>}
|
||||
/>
|
||||
<AnimateItems
|
||||
|
||||
@ -27,6 +27,7 @@ export default function PhotoHeader({
|
||||
indexNumber,
|
||||
count,
|
||||
dateRange,
|
||||
hasAiTextGeneration,
|
||||
includeShareButton,
|
||||
...categories
|
||||
}: {
|
||||
@ -38,6 +39,7 @@ export default function PhotoHeader({
|
||||
indexNumber?: number
|
||||
count?: number
|
||||
dateRange?: PhotoDateRange
|
||||
hasAiTextGeneration: boolean
|
||||
includeShareButton?: boolean
|
||||
} & PhotoSetCategory) {
|
||||
const { isGridHighDensity } = useAppState();
|
||||
@ -62,6 +64,7 @@ export default function PhotoHeader({
|
||||
<PhotoPrevNextActions {...{
|
||||
photo: selectedPhoto,
|
||||
photos,
|
||||
hasAiTextGeneration,
|
||||
...categories,
|
||||
}} />;
|
||||
|
||||
|
||||
@ -30,6 +30,7 @@ import {
|
||||
import { downloadFileFromBrowser } from '@/utility/url';
|
||||
import useKeydownHandler from '@/utility/useKeydownHandler';
|
||||
import { KEY_COMMANDS } from './key-commands';
|
||||
import { syncPhotoConfirmText } from '@/admin/confirm';
|
||||
|
||||
const ANIMATION_LEFT: AnimationConfig = { type: 'left', duration: 0.3 };
|
||||
const ANIMATION_RIGHT: AnimationConfig = { type: 'right', duration: 0.3 };
|
||||
@ -38,11 +39,13 @@ export default function PhotoPrevNextActions({
|
||||
photo,
|
||||
photos = [],
|
||||
className,
|
||||
hasAiTextGeneration,
|
||||
...categories
|
||||
}: {
|
||||
photo?: Photo
|
||||
photos?: Photo[]
|
||||
className?: string
|
||||
hasAiTextGeneration: boolean
|
||||
} & PhotoSetCategory) {
|
||||
const { setNextPhotoAnimation, isUserSignedIn } = useAppState();
|
||||
|
||||
@ -157,7 +160,11 @@ export default function PhotoPrevNextActions({
|
||||
}
|
||||
break;
|
||||
case KEY_COMMANDS.sync:
|
||||
if (isUserSignedIn) {
|
||||
if (
|
||||
isUserSignedIn &&
|
||||
photo &&
|
||||
window.confirm(syncPhotoConfirmText(photo, hasAiTextGeneration))
|
||||
) {
|
||||
syncPhoto();
|
||||
}
|
||||
break;
|
||||
@ -176,6 +183,7 @@ export default function PhotoPrevNextActions({
|
||||
downloadFileName,
|
||||
syncPhoto,
|
||||
deletePhoto,
|
||||
hasAiTextGeneration,
|
||||
]);
|
||||
useKeydownHandler({ onKeyDown });
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ import PhotoHeader from '@/photo/PhotoHeader';
|
||||
import PhotoRecipe from './PhotoRecipe';
|
||||
import { useAppState } from '@/state/AppState';
|
||||
import { descriptionForRecipePhotos, getRecipePropsFromPhotos } from '.';
|
||||
import { AI_TEXT_GENERATION_ENABLED } from '@/app/config';
|
||||
|
||||
export default function RecipeHeader({
|
||||
recipe,
|
||||
@ -42,6 +43,7 @@ export default function RecipeHeader({
|
||||
indexNumber={indexNumber}
|
||||
count={count}
|
||||
dateRange={dateRange}
|
||||
hasAiTextGeneration={AI_TEXT_GENERATION_ENABLED}
|
||||
includeShareButton
|
||||
/>
|
||||
);
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Photo, photoQuantityText } from '@/photo';
|
||||
import PhotoHeader from '@/photo/PhotoHeader';
|
||||
import HiddenTag from './HiddenTag';
|
||||
import { AI_TEXT_GENERATION_ENABLED } from '@/app/config';
|
||||
|
||||
export default function HiddenHeader({
|
||||
photos,
|
||||
@ -22,6 +23,7 @@ export default function HiddenHeader({
|
||||
selectedPhoto={selectedPhoto}
|
||||
indexNumber={indexNumber}
|
||||
count={count}
|
||||
hasAiTextGeneration={AI_TEXT_GENERATION_ENABLED}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ import PhotoTag from './PhotoTag';
|
||||
import { descriptionForTaggedPhotos, isTagFavs } from '.';
|
||||
import PhotoHeader from '@/photo/PhotoHeader';
|
||||
import FavsTag from './FavsTag';
|
||||
import { AI_TEXT_GENERATION_ENABLED } from '@/app/config';
|
||||
|
||||
export default function TagHeader({
|
||||
tag,
|
||||
@ -32,6 +33,7 @@ export default function TagHeader({
|
||||
indexNumber={indexNumber}
|
||||
count={count}
|
||||
dateRange={dateRange}
|
||||
hasAiTextGeneration={AI_TEXT_GENERATION_ENABLED}
|
||||
includeShareButton
|
||||
/>
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user