diff --git a/frontend/src/components/common/AuthenticatedImage.tsx b/frontend/src/components/common/AuthenticatedImage.tsx index bd3bc8fe..08215896 100644 --- a/frontend/src/components/common/AuthenticatedImage.tsx +++ b/frontend/src/components/common/AuthenticatedImage.tsx @@ -11,25 +11,12 @@ import { interface AuthenticatedImageProps extends Omit, 'onLoad'> { src: string; fallbackSrc?: string; - useWatermark?: boolean; isGallery?: boolean; - protectFromDownload?: boolean; slug?: string; - photoId?: number; - requiresToken?: boolean; - secureUrlTemplate?: string; - downloadUrlTemplate?: string; - onProtectionViolation?: (violationType: string) => void; - watermarkText?: string; - overlayProtection?: boolean; - fragmentGrid?: boolean; - scrambleFragments?: boolean; useCanvasRendering?: boolean; - blockKeyboardShortcuts?: boolean; - detectPrintScreen?: boolean; - detectDevTools?: boolean; - protectionLevel?: 'basic' | 'standard' | 'enhanced' | 'maximum'; - useEnhancedProtection?: boolean; + /** Fired when the canvas branch blocks a context-menu attempt. The only + * protection callback this component actually implements (#1297). */ + onProtectionViolation?: (violationType: string) => void; onLoad?: () => void; /** * Priority in the shared fetch queue (#1287). NOT the native `fetchPriority` @@ -43,51 +30,44 @@ interface AuthenticatedImageProps extends Omit + * onProtectionViolation fires from the canvas context-menu handler below + * + * `useWatermark` was removed too. #1297 did not list it — it sat outside the + * `unusedProps` block — but it was equally inert: declared, defaulted, never + * read. + */ export const AuthenticatedImage: React.FC = ({ src, fallbackSrc, alt, - useWatermark = false, isGallery = false, - protectFromDownload, slug, - photoId, - requiresToken, - secureUrlTemplate, - downloadUrlTemplate, - onProtectionViolation, - watermarkText, - overlayProtection, - fragmentGrid, - scrambleFragments, useCanvasRendering, - blockKeyboardShortcuts, - detectPrintScreen, - detectDevTools, - protectionLevel, - useEnhancedProtection, + onProtectionViolation, onLoad, queuePriority = 'normal', ...props }) => { - const unusedProps = { - protectFromDownload, - photoId, - requiresToken, - secureUrlTemplate, - downloadUrlTemplate, - onProtectionViolation, - watermarkText, - overlayProtection, - fragmentGrid, - scrambleFragments, - blockKeyboardShortcuts, - detectPrintScreen, - detectDevTools, - protectionLevel, - useEnhancedProtection - }; - void unusedProps; const [imageSrc, setImageSrc] = useState(''); const [error, setError] = useState(false); diff --git a/frontend/src/components/gallery/GalleryFolderTiles.tsx b/frontend/src/components/gallery/GalleryFolderTiles.tsx index 0f05efc7..3ac15763 100644 --- a/frontend/src/components/gallery/GalleryFolderTiles.tsx +++ b/frontend/src/components/gallery/GalleryFolderTiles.tsx @@ -41,9 +41,7 @@ export const GalleryFolderTiles: React.FC = ({ compact = false, slug, protectionLevel, - useEnhancedProtection, useCanvasRendering, - allowDownloads = true, }) => { const { t } = useTranslation(); @@ -96,12 +94,6 @@ export const GalleryFolderTiles: React.FC = ({ className="w-full h-full object-cover group-hover:scale-[1.02] transition-transform" isGallery slug={slug} - photoId={coverPhoto.id} - requiresToken={coverPhoto.requires_token} - secureUrlTemplate={coverPhoto.secure_url_template} - protectFromDownload={!allowDownloads || useEnhancedProtection} - protectionLevel={protectionLevel} - useEnhancedProtection={useEnhancedProtection} // Same rule as every other gallery image path: maximum // protection implies canvas rendering even when the separate // toggle is off (its default), otherwise a cover silently diff --git a/frontend/src/components/gallery/HeroHeader.tsx b/frontend/src/components/gallery/HeroHeader.tsx index 891ff638..6f183799 100644 --- a/frontend/src/components/gallery/HeroHeader.tsx +++ b/frontend/src/components/gallery/HeroHeader.tsx @@ -43,9 +43,7 @@ export const HeroHeader: React.FC = ({ heroLogoSize = 'medium', heroLogoPosition = 'top', dividerStyle = 'wave', - allowDownloads = true, protectionLevel = 'standard', - useEnhancedProtection = false, useCanvasRendering = false, onScrollToContent, heroImageAnchor = 'center' @@ -144,10 +142,6 @@ export const HeroHeader: React.FC = ({ style={{ objectPosition: heroImageAnchor }} isGallery={true} slug={slug} - photoId={heroPhoto.id} - protectFromDownload={!allowDownloads || useEnhancedProtection} - protectionLevel={protectionLevel} - useEnhancedProtection={useEnhancedProtection} useCanvasRendering={useCanvasRendering || protectionLevel === 'maximum'} /> diff --git a/frontend/src/components/gallery/PeopleSheet.tsx b/frontend/src/components/gallery/PeopleSheet.tsx index af339142..c0ab98b0 100644 --- a/frontend/src/components/gallery/PeopleSheet.tsx +++ b/frontend/src/components/gallery/PeopleSheet.tsx @@ -144,9 +144,6 @@ export const PeopleSheet: React.FC = ({ alt="" isGallery slug={slug} - photoId={photo.id} - requiresToken={photo.requires_token} - secureUrlTemplate={photo.secure_url_template} // Crop to the face, exactly as the strip does. Without // this a group photo shows whoever is centred — often // not the person being labelled, and identical for two diff --git a/frontend/src/components/gallery/PeopleStrip.tsx b/frontend/src/components/gallery/PeopleStrip.tsx index a85e05fd..d2bacdc7 100644 --- a/frontend/src/components/gallery/PeopleStrip.tsx +++ b/frontend/src/components/gallery/PeopleStrip.tsx @@ -86,9 +86,6 @@ const PersonAvatar: React.FC = ({ alt="" isGallery slug={slug} - photoId={photo.id} - requiresToken={photo.requires_token} - secureUrlTemplate={photo.secure_url_template} style={cropStyle || { width: '100%', height: '100%', objectFit: 'cover' }} /> ) : ( diff --git a/frontend/src/components/gallery/PhotoGrid.tsx b/frontend/src/components/gallery/PhotoGrid.tsx index 2379cb94..154e8858 100644 --- a/frontend/src/components/gallery/PhotoGrid.tsx +++ b/frontend/src/components/gallery/PhotoGrid.tsx @@ -244,7 +244,6 @@ const PhotoThumbnail: React.FC = ({ onDownload, allowDownloads = true, protectionLevel = 'standard', - useEnhancedProtection = false, useCanvasRendering = false, slug, feedbackEnabled = false @@ -269,18 +268,7 @@ const PhotoThumbnail: React.FC = ({ loading="lazy" isGallery={true} slug={slug} - photoId={photo.id} - requiresToken={photo.requires_token} - secureUrlTemplate={photo.secure_url_template} - protectFromDownload={!allowDownloads || useEnhancedProtection} - protectionLevel={protectionLevel} - useEnhancedProtection={useEnhancedProtection} useCanvasRendering={useCanvasRendering || protectionLevel === 'maximum'} - fragmentGrid={protectionLevel === 'enhanced' || protectionLevel === 'maximum'} - blockKeyboardShortcuts={useEnhancedProtection} - detectPrintScreen={useEnhancedProtection} - detectDevTools={protectionLevel === 'maximum'} - watermarkText={useEnhancedProtection ? 'Protected' : undefined} onProtectionViolation={(violationType) => { // Track analytics if (typeof window !== 'undefined' && (window as any).umami) { diff --git a/frontend/src/components/gallery/PhotoLightbox.tsx b/frontend/src/components/gallery/PhotoLightbox.tsx index d5c475c0..6daf8686 100644 --- a/frontend/src/components/gallery/PhotoLightbox.tsx +++ b/frontend/src/components/gallery/PhotoLightbox.tsx @@ -1159,9 +1159,6 @@ export const PhotoLightbox: React.FC = ({ draggable={false} isGallery={true} slug={slug} - photoId={photo.id} - requiresToken={photo.requires_token} - secureUrlTemplate={photo.secure_url_template} /> )} @@ -1189,21 +1186,9 @@ export const PhotoLightbox: React.FC = ({ transition: isDragging ? 'none' : 'transform 0.2s', }} draggable={false} - useWatermark={useEnhancedProtection} - watermarkText={useEnhancedProtection ? `${photo.filename} - Protected` : undefined} isGallery={true} slug={slug} - photoId={photo.id} - requiresToken={photo.requires_token} - secureUrlTemplate={photo.secure_url_template} - protectFromDownload={!allowDownloads || useEnhancedProtection} - protectionLevel={protectionLevel} - useEnhancedProtection={useEnhancedProtection} useCanvasRendering={useCanvasRendering || protectionLevel === 'maximum'} - fragmentGrid={protectionLevel === 'enhanced' || protectionLevel === 'maximum'} - blockKeyboardShortcuts={useEnhancedProtection} - detectPrintScreen={useEnhancedProtection} - detectDevTools={protectionLevel === 'enhanced' || protectionLevel === 'maximum'} onProtectionViolation={(violationType) => { console.warn(`Protection violation in lightbox for photo ${photo.id}: ${violationType}`); diff --git a/frontend/src/components/gallery/layouts/CarouselGalleryLayout.tsx b/frontend/src/components/gallery/layouts/CarouselGalleryLayout.tsx index f341101d..d0da929e 100644 --- a/frontend/src/components/gallery/layouts/CarouselGalleryLayout.tsx +++ b/frontend/src/components/gallery/layouts/CarouselGalleryLayout.tsx @@ -89,7 +89,6 @@ export const CarouselGalleryLayout: React.FC = ({ alt={currentPhoto.filename} className="w-full h-full object-contain" isGallery={true} - protectFromDownload={!allowDownloads} /> {/* Colour labels for the photo in view (#1189). Bottom-left because it @@ -267,7 +266,6 @@ export const CarouselGalleryLayout: React.FC = ({ className="w-full h-full object-cover" loading="lazy" isGallery={true} - protectFromDownload={!allowDownloads} /> {/* The strip is the only place this layout shows more than one photo at a time, so it is the only place a label can diff --git a/frontend/src/components/gallery/layouts/GalleryPremiumLayout.tsx b/frontend/src/components/gallery/layouts/GalleryPremiumLayout.tsx index e2dedbb9..e6c99b34 100644 --- a/frontend/src/components/gallery/layouts/GalleryPremiumLayout.tsx +++ b/frontend/src/components/gallery/layouts/GalleryPremiumLayout.tsx @@ -68,9 +68,7 @@ const PhotoCard: React.FC = ({ isSelectionMode, isLiked, slug, - allowDownloads = true, protectionLevel = 'standard', - useEnhancedProtection = false, useCanvasRendering = false, feedbackEnabled = false, allowLikes = false, @@ -124,12 +122,6 @@ const PhotoCard: React.FC = ({ loading="lazy" isGallery={true} slug={slug} - photoId={photo.id} - requiresToken={photo.requires_token} - secureUrlTemplate={photo.secure_url_template} - protectFromDownload={!allowDownloads || useEnhancedProtection} - protectionLevel={protectionLevel} - useEnhancedProtection={useEnhancedProtection} useCanvasRendering={useCanvasRendering || protectionLevel === 'maximum'} /> diff --git a/frontend/src/components/gallery/layouts/GridGalleryLayout.tsx b/frontend/src/components/gallery/layouts/GridGalleryLayout.tsx index 544154ca..9fe14320 100644 --- a/frontend/src/components/gallery/layouts/GridGalleryLayout.tsx +++ b/frontend/src/components/gallery/layouts/GridGalleryLayout.tsx @@ -48,7 +48,6 @@ const GridPhoto: React.FC = ({ allowDownloads = true, slug, protectionLevel = 'standard', - useEnhancedProtection = false, useCanvasRendering = false, feedbackEnabled = false, feedbackOptions, @@ -93,18 +92,7 @@ const GridPhoto: React.FC = ({ loading: 'lazy', isGallery: true, slug, - photoId: photo.id, - requiresToken: photo.requires_token, - secureUrlTemplate: photo.secure_url_template, - protectFromDownload: !allowDownloads || useEnhancedProtection, - protectionLevel, - useEnhancedProtection, useCanvasRendering: useCanvasRendering || protectionLevel === 'maximum', - fragmentGrid: protectionLevel === 'enhanced' || protectionLevel === 'maximum', - blockKeyboardShortcuts: useEnhancedProtection, - detectPrintScreen: useEnhancedProtection, - detectDevTools: protectionLevel === 'maximum', - watermarkText: useEnhancedProtection ? 'Protected' : undefined, onProtectionViolation: (violationType: string) => { console.warn(`Protection violation on grid photo ${photo.id}: ${violationType}`); }, diff --git a/frontend/src/components/gallery/layouts/JustifiedGalleryLayout.tsx b/frontend/src/components/gallery/layouts/JustifiedGalleryLayout.tsx index a99193fe..8e0d5113 100644 --- a/frontend/src/components/gallery/layouts/JustifiedGalleryLayout.tsx +++ b/frontend/src/components/gallery/layouts/JustifiedGalleryLayout.tsx @@ -70,7 +70,6 @@ const JustifiedPhoto: React.FC = ({ allowDownloads = true, slug, protectionLevel = 'standard', - useEnhancedProtection = false, useCanvasRendering = false, feedbackEnabled = false, feedbackOptions, @@ -134,18 +133,7 @@ const JustifiedPhoto: React.FC = ({ loading: 'lazy', isGallery: true, slug, - photoId: photo.id, - requiresToken: photo.requires_token, - secureUrlTemplate: photo.secure_url_template, - protectFromDownload: !allowDownloads || useEnhancedProtection, - protectionLevel, - useEnhancedProtection, useCanvasRendering: useCanvasRendering || protectionLevel === 'maximum', - fragmentGrid: protectionLevel === 'enhanced' || protectionLevel === 'maximum', - blockKeyboardShortcuts: useEnhancedProtection, - detectPrintScreen: useEnhancedProtection, - detectDevTools: protectionLevel === 'maximum', - watermarkText: useEnhancedProtection ? 'Protected' : undefined, onProtectionViolation: (violationType: string) => { console.warn(`Protection violation on justified photo ${photo.id}: ${violationType}`); }, @@ -411,10 +399,6 @@ export const JustifiedGalleryLayout: React.FC = ({ className="w-full h-full object-cover" isGallery={true} slug={slug} - photoId={heroPhoto.id} - protectFromDownload={!allowDownloads || useEnhancedProtection} - protectionLevel={protectionLevel} - useEnhancedProtection={useEnhancedProtection} useCanvasRendering={useCanvasRendering || protectionLevel === 'maximum'} /> diff --git a/frontend/src/components/gallery/layouts/MasonryGalleryLayout.tsx b/frontend/src/components/gallery/layouts/MasonryGalleryLayout.tsx index 8c9ac998..4328d9b0 100644 --- a/frontend/src/components/gallery/layouts/MasonryGalleryLayout.tsx +++ b/frontend/src/components/gallery/layouts/MasonryGalleryLayout.tsx @@ -120,7 +120,6 @@ const MasonryPhoto: React.FC = ({ className: 'w-full h-full object-cover rounded-lg', loading: 'lazy', isGallery: true, - protectFromDownload: !allowDownloads, }} overlayBaseClassName="absolute inset-0 bg-black/40 transition-opacity duration-200 rounded-lg flex items-center justify-center gap-2" allowDownloads={allowDownloads} @@ -376,7 +375,6 @@ export const MasonryGalleryLayout: React.FC = ({ className: 'w-full h-full object-cover rounded-lg transition-transform duration-300 group-hover:scale-[1.02]', loading: 'lazy', isGallery: true, - protectFromDownload: !allowDownloads, }} overlayBaseClassName="absolute inset-0 bg-black/40 transition-opacity duration-200 rounded-lg flex items-center justify-center gap-2" actionVariant="dark" @@ -434,7 +432,6 @@ export const MasonryGalleryLayout: React.FC = ({ className: 'w-full h-full object-cover rounded-lg transition-transform duration-300 group-hover:scale-[1.02]', loading: 'lazy', isGallery: true, - protectFromDownload: !allowDownloads, }} overlayBaseClassName="absolute inset-0 bg-black/40 transition-opacity duration-200 rounded-lg flex items-center justify-center gap-2" actionVariant="dark" @@ -501,7 +498,6 @@ export const MasonryGalleryLayout: React.FC = ({ className: 'w-full h-full object-cover transition-transform duration-300 group-hover:scale-105', loading: 'lazy', isGallery: true, - protectFromDownload: !allowDownloads, }} overlayBaseClassName="absolute inset-0 bg-black/40 transition-opacity duration-200 flex items-center justify-center gap-2" actionVariant="dark" diff --git a/frontend/src/components/gallery/layouts/MosaicGalleryLayout.tsx b/frontend/src/components/gallery/layouts/MosaicGalleryLayout.tsx index 6a7b2915..05d14d01 100644 --- a/frontend/src/components/gallery/layouts/MosaicGalleryLayout.tsx +++ b/frontend/src/components/gallery/layouts/MosaicGalleryLayout.tsx @@ -83,7 +83,6 @@ const MosaicPhoto: React.FC = ({ className: 'w-full h-full object-cover transition-transform duration-300 group-hover:scale-105', loading: 'lazy', isGallery: true, - protectFromDownload: !allowDownloads, }} overlayBaseClassName="absolute inset-0 bg-black/40 transition-opacity duration-200 flex items-center justify-center gap-2" allowDownloads={allowDownloads} diff --git a/frontend/src/components/gallery/layouts/TimelineGalleryLayout.tsx b/frontend/src/components/gallery/layouts/TimelineGalleryLayout.tsx index cac34ec8..b7bccf79 100644 --- a/frontend/src/components/gallery/layouts/TimelineGalleryLayout.tsx +++ b/frontend/src/components/gallery/layouts/TimelineGalleryLayout.tsx @@ -130,7 +130,6 @@ export const TimelineGalleryLayout: React.FC = ({ className: 'w-full h-full object-cover rounded-lg', loading: 'lazy', isGallery: true, - protectFromDownload: !allowDownloads, }} overlayBaseClassName="absolute inset-0 bg-black/40 transition-opacity duration-200 rounded-lg flex items-center justify-center gap-2" allowDownloads={allowDownloads} diff --git a/frontend/src/components/gallery/layouts/story/StoryHero.tsx b/frontend/src/components/gallery/layouts/story/StoryHero.tsx index 3c6e23b3..66293623 100644 --- a/frontend/src/components/gallery/layouts/story/StoryHero.tsx +++ b/frontend/src/components/gallery/layouts/story/StoryHero.tsx @@ -22,9 +22,7 @@ export const StoryHero: React.FC = ({ stats, photo, slug, - allowDownloads = true, protectionLevel = 'standard', - useEnhancedProtection = false, useCanvasRendering = false }) => { const formattedDate = date @@ -51,12 +49,6 @@ export const StoryHero: React.FC = ({ className="w-full h-full object-cover" isGallery={true} slug={slug} - photoId={photo.id} - requiresToken={photo.requires_token} - secureUrlTemplate={photo.secure_url_template} - protectFromDownload={!allowDownloads || useEnhancedProtection} - protectionLevel={protectionLevel} - useEnhancedProtection={useEnhancedProtection} useCanvasRendering={useCanvasRendering || protectionLevel === 'maximum'} /> ) : ( diff --git a/frontend/src/components/gallery/layouts/story/StoryPhotoCard.tsx b/frontend/src/components/gallery/layouts/story/StoryPhotoCard.tsx index b9fa9c76..a5eb5643 100644 --- a/frontend/src/components/gallery/layouts/story/StoryPhotoCard.tsx +++ b/frontend/src/components/gallery/layouts/story/StoryPhotoCard.tsx @@ -28,9 +28,7 @@ export const StoryPhotoCard: React.FC = ({ onToggleFavorite, onClick, slug, - allowDownloads = true, protectionLevel = 'standard', - useEnhancedProtection = false, useCanvasRendering = false, featured = false, galleryId: _galleryId @@ -105,12 +103,6 @@ export const StoryPhotoCard: React.FC = ({ }`} isGallery={true} slug={slug} - photoId={photo.id} - requiresToken={photo.requires_token} - secureUrlTemplate={photo.secure_url_template} - protectFromDownload={!allowDownloads || useEnhancedProtection} - protectionLevel={protectionLevel} - useEnhancedProtection={useEnhancedProtection} useCanvasRendering={useCanvasRendering || protectionLevel === 'maximum'} /> )}