@switchlabs/verify-ai-react-native 2.4.7 → 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 +49 -3
- 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, responsiveOrientationWhenOrientationLocked: true,
|
|
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.
|
|
@@ -495,12 +517,22 @@ export function VerifyAIScanner({
|
|
|
495
517
|
onCameraReady={onCameraReady}
|
|
496
518
|
onMountError={onMountError}
|
|
497
519
|
responsiveOrientationWhenOrientationLocked
|
|
520
|
+
onResponsiveOrientationChanged={(event) => {
|
|
521
|
+
setPhysicalOrientation(event.orientation);
|
|
522
|
+
}}
|
|
498
523
|
>
|
|
499
524
|
{/* Overlay */}
|
|
500
525
|
<View style={styles.overlay}>
|
|
501
526
|
{overlay?.title && (
|
|
502
527
|
<View style={[styles.topBar, isLandscape && styles.topBarLandscape]}>
|
|
503
|
-
<Text
|
|
528
|
+
<Text
|
|
529
|
+
style={[
|
|
530
|
+
styles.titleText,
|
|
531
|
+
{ transform: [{ rotate: `${overlayRotationDeg}deg` }] },
|
|
532
|
+
]}
|
|
533
|
+
>
|
|
534
|
+
{overlay.title}
|
|
535
|
+
</Text>
|
|
504
536
|
</View>
|
|
505
537
|
)}
|
|
506
538
|
|
|
@@ -528,7 +560,14 @@ export function VerifyAIScanner({
|
|
|
528
560
|
<View style={[styles.corner, styles.cornerBottomRight, overlay.theme?.cornerColor ? { borderColor: overlay.theme.cornerColor } : undefined]} />
|
|
529
561
|
</View>
|
|
530
562
|
{overlay.guideCaption && (
|
|
531
|
-
<Text
|
|
563
|
+
<Text
|
|
564
|
+
style={[
|
|
565
|
+
styles.guideCaptionText,
|
|
566
|
+
{ transform: [{ rotate: `${overlayRotationDeg}deg` }] },
|
|
567
|
+
]}
|
|
568
|
+
>
|
|
569
|
+
{overlay.guideCaption}
|
|
570
|
+
</Text>
|
|
532
571
|
)}
|
|
533
572
|
</View>
|
|
534
573
|
)}
|
|
@@ -630,7 +669,14 @@ export function VerifyAIScanner({
|
|
|
630
669
|
{!showBottomCard && (
|
|
631
670
|
<>
|
|
632
671
|
{overlay?.instructions && status === 'idle' && (
|
|
633
|
-
<Text
|
|
672
|
+
<Text
|
|
673
|
+
style={[
|
|
674
|
+
styles.instructionsText,
|
|
675
|
+
{ transform: [{ rotate: `${overlayRotationDeg}deg` }] },
|
|
676
|
+
]}
|
|
677
|
+
>
|
|
678
|
+
{overlay.instructions}
|
|
679
|
+
</Text>
|
|
634
680
|
)}
|
|
635
681
|
{showCaptureButton && (
|
|
636
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';
|