kamerazugriff wieder möglich + anleitung zum erlauben des kamerazugriffs
This commit is contained in:
@@ -53,7 +53,7 @@ interface Task {
|
||||
difficulty?: 'easy' | 'medium' | 'hard';
|
||||
}
|
||||
|
||||
type PermissionState = 'idle' | 'prompt' | 'granted' | 'denied' | 'error' | 'unsupported';
|
||||
type PermissionState = 'idle' | 'prompt' | 'granted' | 'denied' | 'error' | 'unsupported' | 'blocked';
|
||||
type CameraMode = 'preview' | 'countdown' | 'review' | 'uploading';
|
||||
|
||||
type CameraPreferences = {
|
||||
@@ -85,6 +85,21 @@ function getErrorName(error: unknown): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function isCameraBlockedByPolicy(): boolean {
|
||||
if (typeof document === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const policy = (document as { permissionsPolicy?: { allowsFeature?: (feature: string) => boolean } })
|
||||
.permissionsPolicy;
|
||||
|
||||
if (!policy?.allowsFeature) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !policy.allowsFeature('camera');
|
||||
}
|
||||
|
||||
const DEFAULT_PREFS: CameraPreferences = {
|
||||
facingMode: 'environment',
|
||||
countdownSeconds: 3,
|
||||
@@ -449,6 +464,12 @@ const [canUpload, setCanUpload] = useState(true);
|
||||
if (mode === 'uploading') return;
|
||||
|
||||
try {
|
||||
if (isCameraBlockedByPolicy()) {
|
||||
setPermissionState('blocked');
|
||||
setPermissionMessage(t('upload.cameraBlocked.message'));
|
||||
return;
|
||||
}
|
||||
|
||||
setPermissionState('prompt');
|
||||
setPermissionMessage(null);
|
||||
|
||||
@@ -475,6 +496,18 @@ const [canUpload, setCanUpload] = useState(true);
|
||||
}
|
||||
}, [attachStreamToVideo, createConstraint, mode, preferences.facingMode, stopStream, supportsCamera, t]);
|
||||
|
||||
const handleRecheckCamera = useCallback(() => {
|
||||
if (isCameraBlockedByPolicy()) {
|
||||
setPermissionState('blocked');
|
||||
setPermissionMessage(t('upload.cameraBlocked.message'));
|
||||
return;
|
||||
}
|
||||
|
||||
setPermissionState('idle');
|
||||
setPermissionMessage(null);
|
||||
void startCamera();
|
||||
}, [startCamera, t]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loadingTask) return;
|
||||
startCamera();
|
||||
@@ -1078,6 +1111,7 @@ const renderWithDialog = (content: ReactNode, wrapperClassName = 'space-y-6 pb-[
|
||||
denied: t('upload.cameraDenied.title'),
|
||||
error: t('upload.cameraError.title'),
|
||||
unsupported: t('upload.cameraUnsupported.title'),
|
||||
blocked: t('upload.cameraBlocked.title'),
|
||||
};
|
||||
|
||||
const fallbackMessages: Record<PermissionState, string> = {
|
||||
@@ -1087,11 +1121,20 @@ const renderWithDialog = (content: ReactNode, wrapperClassName = 'space-y-6 pb-[
|
||||
denied: t('upload.cameraDenied.explanation'),
|
||||
error: t('upload.cameraError.explanation'),
|
||||
unsupported: t('upload.cameraUnsupported.message'),
|
||||
blocked: t('upload.cameraBlocked.message'),
|
||||
};
|
||||
|
||||
const title = titles[permissionState];
|
||||
const description = permissionMessage ?? fallbackMessages[permissionState];
|
||||
const canRetryCamera = permissionState !== 'unsupported';
|
||||
const canRetryCamera = permissionState !== 'unsupported' && permissionState !== 'blocked';
|
||||
const canRecheckCamera = permissionState === 'blocked';
|
||||
const helpText = permissionState === 'blocked'
|
||||
? t('upload.cameraBlocked.hint')
|
||||
: permissionState === 'denied'
|
||||
? t('upload.cameraDenied.hint')
|
||||
: permissionState === 'error'
|
||||
? t('upload.cameraError.hint')
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -1107,6 +1150,9 @@ const renderWithDialog = (content: ReactNode, wrapperClassName = 'space-y-6 pb-[
|
||||
<p className="text-xs text-slate-600 dark:text-white/70">{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
{helpText ? (
|
||||
<p className="mt-3 text-xs text-slate-600 dark:text-white/70">{helpText}</p>
|
||||
) : null}
|
||||
<div className="mt-4 flex flex-wrap gap-3">
|
||||
{canRetryCamera && (
|
||||
<Button
|
||||
@@ -1117,6 +1163,17 @@ const renderWithDialog = (content: ReactNode, wrapperClassName = 'space-y-6 pb-[
|
||||
{t('upload.buttons.startCamera')}
|
||||
</Button>
|
||||
)}
|
||||
{canRecheckCamera && (
|
||||
<Button
|
||||
onClick={handleRecheckCamera}
|
||||
size="sm"
|
||||
style={buttonStyle === 'outline'
|
||||
? { borderRadius: radius, background: 'transparent', color: linkColor, border: `1px solid ${linkColor}` }
|
||||
: { borderRadius: radius }}
|
||||
>
|
||||
{t('upload.buttons.recheckCamera')}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
@@ -1194,11 +1251,19 @@ const renderWithDialog = (content: ReactNode, wrapperClassName = 'space-y-6 pb-[
|
||||
<span>
|
||||
{permissionState === 'unsupported'
|
||||
? t('upload.cameraUnsupported.title')
|
||||
: permissionState === 'blocked'
|
||||
? t('upload.cameraBlocked.title')
|
||||
: t('upload.cameraDenied.title')}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{permissionState !== 'granted' && (
|
||||
<div className="absolute inset-x-4 top-16 z-30 sm:top-20">
|
||||
{renderPermissionNotice()}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{mode === 'countdown' && (
|
||||
<div className="absolute inset-0 z-40 flex flex-col items-center justify-center bg-black/60 text-white">
|
||||
<div className="text-6xl font-bold">{countdownValue}</div>
|
||||
@@ -1449,7 +1514,6 @@ const renderWithDialog = (content: ReactNode, wrapperClassName = 'space-y-6 pb-[
|
||||
</>
|
||||
)}
|
||||
|
||||
{permissionState !== 'granted' && renderPermissionNotice()}
|
||||
{renderPrimer()}
|
||||
|
||||
<input
|
||||
|
||||
Reference in New Issue
Block a user