fix(gallery): remove the inert image-protection prop surface from AuthenticatedImage

AuthenticatedImage accepted the whole image-protection prop surface and
discarded it in a `void unusedProps` block. Callers computed those props
from the event's protection level and passed them in good faith, so
raising the level produced canvas rendering (via the layouts' own OR on
`protectionLevel === 'maximum'`) and nothing else the level implies.

Removes them from the interface and from every call site, so the props
state what the component actually does. Two survive because they are
real: `useCanvasRendering`, and `onProtectionViolation` — which #1297
listed as inert but which does fire, from the canvas context-menu
handler. `useWatermark` is removed as well; #1297 did not list it (it sat
outside the `unusedProps` block) but it was equally dead.

Removal rather than implementation is deliberate. The implementation
these props describe already exists in `ProtectedImage`, which is
exported from the barrel and rendered nowhere. Wiring it in is a product
decision about what protection level should mean, not a side effect of a
cleanup.

Analytics payloads inside the surviving onProtectionViolation handlers
keep their photoId/protectionLevel fields.

Refs #1297
This commit is contained in:
Paul Nothaft
2026-09-05 06:31:47 +02:00
parent c71ffae912
commit e734e41c41
16 changed files with 30 additions and 157 deletions
@@ -11,25 +11,12 @@ import {
interface AuthenticatedImageProps extends Omit<React.ImgHTMLAttributes<HTMLImageElement>, '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<React.ImgHTMLAttributes<HTMLImage
queuePriority?: 'high' | 'prefetch' | 'normal';
}
/**
* Fetches an image with the gallery's bearer token and renders it.
*
* IMAGE PROTECTION IS NOT IMPLEMENTED HERE (#1297). This component used to
* accept the whole protection prop surface — protectFromDownload,
* watermarkText, fragmentGrid, blockKeyboardShortcuts, detectPrintScreen,
* detectDevTools, protectionLevel and the rest — and discard every one of
* them in a `void unusedProps` block. Callers computed them from the event's
* protection level and passed them in good faith, so raising that level
* produced canvas rendering (via the layouts' own OR on
* `protectionLevel === 'maximum'`) and nothing else it implies.
*
* They are removed rather than implemented, so the interface states what the
* component actually does. The implementation those props describe already
* exists in `ProtectedImage` — which is exported and currently rendered
* nowhere. Wiring that in is a deliberate product decision about what
* protection level should mean, not a silent side effect of a cleanup.
*
* Two props survive because they are real:
* useCanvasRendering draws to a canvas instead of an <img>
* 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<AuthenticatedImageProps> = ({
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<string>('');
const [error, setError] = useState(false);