@switchlabs/verify-ai-react-native 2.4.5 → 2.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/VerifyAIScanner.js +32 -3
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +1 -1
- package/src/components/VerifyAIScanner.tsx +59 -4
- package/src/version.ts +1 -1
|
@@ -93,6 +93,24 @@ export function VerifyAIScanner({ onCapture, onResult, onError, overlay, style,
|
|
|
93
93
|
const { width: windowWidth, height: windowHeight } = useWindowDimensions();
|
|
94
94
|
const isLandscape = windowWidth > windowHeight;
|
|
95
95
|
const prevDimensionsRef = useRef({ width: windowWidth, height: windowHeight });
|
|
96
|
+
// Track physical device orientation independently of interface orientation.
|
|
97
|
+
// When the host app is orientation-locked, window dimensions don't change
|
|
98
|
+
// on rotation — this fires via expo-camera's responsive-orientation callback
|
|
99
|
+
// so we can rotate the overlay UI to stay readable from the user's viewpoint.
|
|
100
|
+
const [physicalOrientation, setPhysicalOrientation] = useState('portrait');
|
|
101
|
+
const overlayRotationDeg = (() => {
|
|
102
|
+
switch (physicalOrientation) {
|
|
103
|
+
case 'landscapeLeft':
|
|
104
|
+
return 90;
|
|
105
|
+
case 'landscapeRight':
|
|
106
|
+
return -90;
|
|
107
|
+
case 'portraitUpsideDown':
|
|
108
|
+
return 180;
|
|
109
|
+
case 'portrait':
|
|
110
|
+
default:
|
|
111
|
+
return 0;
|
|
112
|
+
}
|
|
113
|
+
})();
|
|
96
114
|
// Detect orientation changes and remount camera after rotation settles.
|
|
97
115
|
// On iOS, AVCaptureVideoPreviewLayer distorts if remounted during the rotation
|
|
98
116
|
// animation — the native preview layer initializes with transitional bounds.
|
|
@@ -369,13 +387,21 @@ export function VerifyAIScanner({ onCapture, onResult, onError, overlay, style,
|
|
|
369
387
|
return (_jsxs(View, { style: [styles.container, styles.permissionContainer, style], children: [_jsx(Text, { style: styles.permissionText, children: "Camera access is required for photo verification" }), _jsx(TouchableOpacity, { style: styles.permissionButton, onPress: requestPermission, children: _jsx(Text, { style: styles.permissionButtonText, children: "Grant Camera Access" }) })] }));
|
|
370
388
|
}
|
|
371
389
|
const showBottomCard = status === 'success' || status === 'error';
|
|
372
|
-
return (_jsx(View, { style: [styles.container, style], children: _jsx(CameraView, { ref: cameraRef, style: styles.camera, facing: "back", enableTorch: !terminated && enableTorch, onCameraReady: onCameraReady, onMountError: onMountError,
|
|
390
|
+
return (_jsx(View, { style: [styles.container, style], children: _jsx(CameraView, { ref: cameraRef, style: styles.camera, facing: "back", enableTorch: !terminated && enableTorch, onCameraReady: onCameraReady, onMountError: onMountError, responsiveOrientationWhenOrientationLocked: true, onResponsiveOrientationChanged: (event) => {
|
|
391
|
+
setPhysicalOrientation(event.orientation);
|
|
392
|
+
}, children: _jsxs(View, { style: styles.overlay, children: [overlay?.title && (_jsx(View, { style: [styles.topBar, isLandscape && styles.topBarLandscape], children: _jsx(Text, { style: [
|
|
393
|
+
styles.titleText,
|
|
394
|
+
{ transform: [{ rotate: `${overlayRotationDeg}deg` }] },
|
|
395
|
+
], children: overlay.title }) })), overlay?.showGuideFrame && (_jsxs(View, { style: [styles.guideContainer, isLandscape && styles.guideContainerLandscape], children: [_jsxs(View, { style: [
|
|
373
396
|
styles.guideFrame,
|
|
374
397
|
isLandscape && styles.guideFrameLandscape,
|
|
375
398
|
overlay.guideFrameAspectRatio
|
|
376
399
|
? { aspectRatio: overlay.guideFrameAspectRatio }
|
|
377
400
|
: undefined,
|
|
378
|
-
], children: [overlay.guideOverlayContent && (_jsx(View, { style: [StyleSheet.absoluteFill, { opacity: overlay.guideOverlayOpacity ?? 0.3 }], children: overlay.guideOverlayContent })), _jsx(View, { style: [styles.corner, styles.cornerTopLeft, overlay.theme?.cornerColor ? { borderColor: overlay.theme.cornerColor } : undefined] }), _jsx(View, { style: [styles.corner, styles.cornerTopRight, overlay.theme?.cornerColor ? { borderColor: overlay.theme.cornerColor } : undefined] }), _jsx(View, { style: [styles.corner, styles.cornerBottomLeft, overlay.theme?.cornerColor ? { borderColor: overlay.theme.cornerColor } : undefined] }), _jsx(View, { style: [styles.corner, styles.cornerBottomRight, overlay.theme?.cornerColor ? { borderColor: overlay.theme.cornerColor } : undefined] })] }), overlay.guideCaption && (_jsx(Text, { style:
|
|
401
|
+
], children: [overlay.guideOverlayContent && (_jsx(View, { style: [StyleSheet.absoluteFill, { opacity: overlay.guideOverlayOpacity ?? 0.3 }], children: overlay.guideOverlayContent })), _jsx(View, { style: [styles.corner, styles.cornerTopLeft, overlay.theme?.cornerColor ? { borderColor: overlay.theme.cornerColor } : undefined] }), _jsx(View, { style: [styles.corner, styles.cornerTopRight, overlay.theme?.cornerColor ? { borderColor: overlay.theme.cornerColor } : undefined] }), _jsx(View, { style: [styles.corner, styles.cornerBottomLeft, overlay.theme?.cornerColor ? { borderColor: overlay.theme.cornerColor } : undefined] }), _jsx(View, { style: [styles.corner, styles.cornerBottomRight, overlay.theme?.cornerColor ? { borderColor: overlay.theme.cornerColor } : undefined] })] }), overlay.guideCaption && (_jsx(Text, { style: [
|
|
402
|
+
styles.guideCaptionText,
|
|
403
|
+
{ transform: [{ rotate: `${overlayRotationDeg}deg` }] },
|
|
404
|
+
], children: overlay.guideCaption }))] })), status === 'processing' && (_jsxs(View, { style: styles.processingOverlay, children: [_jsx(ActivityIndicator, { size: "large", color: "#fff" }), _jsx(Text, { style: styles.statusText, children: overlay?.processingMessage || 'Analyzing photo...' })] })), showBottomCard && _jsx(View, { style: styles.cardBackdrop }), _jsxs(View, { style: [styles.bottomArea, isLandscape && styles.bottomAreaLandscape], children: [status === 'success' && result && (_jsxs(View, { style: styles.resultCard, children: [_jsxs(View, { style: styles.resultCardHeader, children: [_jsx(View, { style: [
|
|
379
405
|
styles.resultIconCircle,
|
|
380
406
|
exhausted
|
|
381
407
|
? styles.resultIconExhausted
|
|
@@ -419,7 +445,10 @@ export function VerifyAIScanner({ onCapture, onResult, onError, overlay, style,
|
|
|
419
445
|
errorMessage = display.message;
|
|
420
446
|
}
|
|
421
447
|
return (_jsxs(View, { style: styles.resultCard, children: [_jsxs(View, { style: styles.resultCardHeader, children: [_jsx(View, { style: [styles.resultIconCircle, styles.resultIconError, overlay?.theme?.failureColor ? { backgroundColor: overlay.theme.failureColor } : undefined], children: _jsx(Text, { style: styles.resultIcon, children: "!" }) }), _jsx(Text, { style: [styles.resultLabel, styles.resultLabelError, overlay?.theme?.failureColor ? { color: overlay.theme.failureColor } : undefined], children: errorTitle })] }), _jsx(Text, { style: styles.feedbackText, children: errorMessage })] }));
|
|
422
|
-
})(), !showBottomCard && (_jsxs(_Fragment, { children: [overlay?.instructions && status === 'idle' && (_jsx(Text, { style:
|
|
448
|
+
})(), !showBottomCard && (_jsxs(_Fragment, { children: [overlay?.instructions && status === 'idle' && (_jsx(Text, { style: [
|
|
449
|
+
styles.instructionsText,
|
|
450
|
+
{ transform: [{ rotate: `${overlayRotationDeg}deg` }] },
|
|
451
|
+
], children: overlay.instructions })), showCaptureButton && (_jsx(View, { style: styles.captureButtonRow, children: _jsx(TouchableOpacity, { style: [
|
|
423
452
|
styles.captureButton,
|
|
424
453
|
overlay?.theme?.captureButtonColor ? { borderColor: overlay.theme.captureButtonColor } : undefined,
|
|
425
454
|
(!cameraReady || status === 'capturing' || status === 'processing') &&
|
package/lib/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const SDK_VERSION = "2.4.
|
|
1
|
+
export declare const SDK_VERSION = "2.4.8";
|
package/lib/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const SDK_VERSION = '2.4.
|
|
1
|
+
export const SDK_VERSION = '2.4.8';
|
package/package.json
CHANGED
|
@@ -159,6 +159,28 @@ export function VerifyAIScanner({
|
|
|
159
159
|
const isLandscape = windowWidth > windowHeight;
|
|
160
160
|
const prevDimensionsRef = useRef({ width: windowWidth, height: windowHeight });
|
|
161
161
|
|
|
162
|
+
// Track physical device orientation independently of interface orientation.
|
|
163
|
+
// When the host app is orientation-locked, window dimensions don't change
|
|
164
|
+
// on rotation — this fires via expo-camera's responsive-orientation callback
|
|
165
|
+
// so we can rotate the overlay UI to stay readable from the user's viewpoint.
|
|
166
|
+
const [physicalOrientation, setPhysicalOrientation] = useState<
|
|
167
|
+
'portrait' | 'portraitUpsideDown' | 'landscapeLeft' | 'landscapeRight'
|
|
168
|
+
>('portrait');
|
|
169
|
+
|
|
170
|
+
const overlayRotationDeg = (() => {
|
|
171
|
+
switch (physicalOrientation) {
|
|
172
|
+
case 'landscapeLeft':
|
|
173
|
+
return 90;
|
|
174
|
+
case 'landscapeRight':
|
|
175
|
+
return -90;
|
|
176
|
+
case 'portraitUpsideDown':
|
|
177
|
+
return 180;
|
|
178
|
+
case 'portrait':
|
|
179
|
+
default:
|
|
180
|
+
return 0;
|
|
181
|
+
}
|
|
182
|
+
})();
|
|
183
|
+
|
|
162
184
|
// Detect orientation changes and remount camera after rotation settles.
|
|
163
185
|
// On iOS, AVCaptureVideoPreviewLayer distorts if remounted during the rotation
|
|
164
186
|
// animation — the native preview layer initializes with transitional bounds.
|
|
@@ -486,12 +508,31 @@ export function VerifyAIScanner({
|
|
|
486
508
|
|
|
487
509
|
return (
|
|
488
510
|
<View style={[styles.container, style]}>
|
|
489
|
-
<CameraView
|
|
511
|
+
<CameraView
|
|
512
|
+
key={cameraKey}
|
|
513
|
+
ref={cameraRef}
|
|
514
|
+
style={styles.camera}
|
|
515
|
+
facing="back"
|
|
516
|
+
enableTorch={!terminated && enableTorch}
|
|
517
|
+
onCameraReady={onCameraReady}
|
|
518
|
+
onMountError={onMountError}
|
|
519
|
+
responsiveOrientationWhenOrientationLocked
|
|
520
|
+
onResponsiveOrientationChanged={(event) => {
|
|
521
|
+
setPhysicalOrientation(event.orientation);
|
|
522
|
+
}}
|
|
523
|
+
>
|
|
490
524
|
{/* Overlay */}
|
|
491
525
|
<View style={styles.overlay}>
|
|
492
526
|
{overlay?.title && (
|
|
493
527
|
<View style={[styles.topBar, isLandscape && styles.topBarLandscape]}>
|
|
494
|
-
<Text
|
|
528
|
+
<Text
|
|
529
|
+
style={[
|
|
530
|
+
styles.titleText,
|
|
531
|
+
{ transform: [{ rotate: `${overlayRotationDeg}deg` }] },
|
|
532
|
+
]}
|
|
533
|
+
>
|
|
534
|
+
{overlay.title}
|
|
535
|
+
</Text>
|
|
495
536
|
</View>
|
|
496
537
|
)}
|
|
497
538
|
|
|
@@ -519,7 +560,14 @@ export function VerifyAIScanner({
|
|
|
519
560
|
<View style={[styles.corner, styles.cornerBottomRight, overlay.theme?.cornerColor ? { borderColor: overlay.theme.cornerColor } : undefined]} />
|
|
520
561
|
</View>
|
|
521
562
|
{overlay.guideCaption && (
|
|
522
|
-
<Text
|
|
563
|
+
<Text
|
|
564
|
+
style={[
|
|
565
|
+
styles.guideCaptionText,
|
|
566
|
+
{ transform: [{ rotate: `${overlayRotationDeg}deg` }] },
|
|
567
|
+
]}
|
|
568
|
+
>
|
|
569
|
+
{overlay.guideCaption}
|
|
570
|
+
</Text>
|
|
523
571
|
)}
|
|
524
572
|
</View>
|
|
525
573
|
)}
|
|
@@ -621,7 +669,14 @@ export function VerifyAIScanner({
|
|
|
621
669
|
{!showBottomCard && (
|
|
622
670
|
<>
|
|
623
671
|
{overlay?.instructions && status === 'idle' && (
|
|
624
|
-
<Text
|
|
672
|
+
<Text
|
|
673
|
+
style={[
|
|
674
|
+
styles.instructionsText,
|
|
675
|
+
{ transform: [{ rotate: `${overlayRotationDeg}deg` }] },
|
|
676
|
+
]}
|
|
677
|
+
>
|
|
678
|
+
{overlay.instructions}
|
|
679
|
+
</Text>
|
|
625
680
|
)}
|
|
626
681
|
{showCaptureButton && (
|
|
627
682
|
<View style={styles.captureButtonRow}>
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const SDK_VERSION = '2.4.
|
|
1
|
+
export const SDK_VERSION = '2.4.8';
|