react-native-rectangle-doc-scanner 3.114.0 → 3.115.0
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/dist/FullDocScanner.d.ts +1 -0
- package/dist/FullDocScanner.js +42 -3
- package/package.json +1 -1
- package/src/FullDocScanner.tsx +49 -3
package/dist/FullDocScanner.d.ts
CHANGED
package/dist/FullDocScanner.js
CHANGED
|
@@ -125,14 +125,19 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
125
125
|
const [rotationDegrees, setRotationDegrees] = (0, react_1.useState)(0);
|
|
126
126
|
const [capturedPhotos, setCapturedPhotos] = (0, react_1.useState)([]);
|
|
127
127
|
const [currentPhotoIndex, setCurrentPhotoIndex] = (0, react_1.useState)(0);
|
|
128
|
+
const [previewPhotoIndex, setPreviewPhotoIndex] = (0, react_1.useState)(0);
|
|
128
129
|
const resolvedGridColor = gridColor ?? overlayColor;
|
|
129
130
|
const docScannerRef = (0, react_1.useRef)(null);
|
|
130
131
|
const captureModeRef = (0, react_1.useRef)(null);
|
|
131
132
|
const captureInProgressRef = (0, react_1.useRef)(false);
|
|
132
133
|
const rectangleCaptureTimeoutRef = (0, react_1.useRef)(null);
|
|
133
134
|
const rectangleHintTimeoutRef = (0, react_1.useRef)(null);
|
|
135
|
+
const currentPhotoIndexRef = (0, react_1.useRef)(currentPhotoIndex);
|
|
134
136
|
const isBusinessMode = type === 'business';
|
|
135
137
|
const maxPhotos = isBusinessMode ? 2 : 1;
|
|
138
|
+
(0, react_1.useEffect)(() => {
|
|
139
|
+
currentPhotoIndexRef.current = currentPhotoIndex;
|
|
140
|
+
}, [currentPhotoIndex]);
|
|
136
141
|
const mergedStrings = (0, react_1.useMemo)(() => ({
|
|
137
142
|
captureHint: strings?.captureHint,
|
|
138
143
|
manualHint: strings?.manualHint,
|
|
@@ -145,6 +150,7 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
145
150
|
first: strings?.first ?? 'Front',
|
|
146
151
|
second: strings?.second ?? 'Back',
|
|
147
152
|
secondBtn: strings?.secondBtn ?? 'Capture Back Side?',
|
|
153
|
+
secondPrompt: strings?.secondPrompt ?? strings?.secondBtn ?? 'Capture Back Side?',
|
|
148
154
|
}), [strings]);
|
|
149
155
|
const emitError = (0, react_1.useCallback)((error, fallbackMessage) => {
|
|
150
156
|
console.error('[FullDocScanner] error', error);
|
|
@@ -184,6 +190,7 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
184
190
|
hasBase64: !!croppedImage.data,
|
|
185
191
|
});
|
|
186
192
|
setProcessing(false);
|
|
193
|
+
setPreviewPhotoIndex(currentPhotoIndexRef.current);
|
|
187
194
|
// Show confirmation screen
|
|
188
195
|
setCroppedImageData({
|
|
189
196
|
path: croppedImage.path,
|
|
@@ -230,6 +237,8 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
230
237
|
console.warn('[FullDocScanner] Missing capture mode for capture result, ignoring');
|
|
231
238
|
return;
|
|
232
239
|
}
|
|
240
|
+
const previewIndex = currentPhotoIndexRef.current;
|
|
241
|
+
setPreviewPhotoIndex(previewIndex);
|
|
233
242
|
const normalizedDoc = normalizeCapturedDocument(document);
|
|
234
243
|
if (captureMode === 'no-grid') {
|
|
235
244
|
console.log('[FullDocScanner] No grid at capture button press: opening cropper for manual selection');
|
|
@@ -433,6 +442,20 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
433
442
|
}, [capturedPhotos, onResult]);
|
|
434
443
|
const handleRetake = (0, react_1.useCallback)(() => {
|
|
435
444
|
console.log('[FullDocScanner] Retake - clearing cropped image and resetting scanner');
|
|
445
|
+
if (isBusinessMode) {
|
|
446
|
+
if (currentPhotoIndex === 1 && previewPhotoIndex === 0 && capturedPhotos.length === 1) {
|
|
447
|
+
console.log('[FullDocScanner] Retake detected on front preview after confirmation - reverting to front capture');
|
|
448
|
+
setCapturedPhotos([]);
|
|
449
|
+
setCurrentPhotoIndex(0);
|
|
450
|
+
setPreviewPhotoIndex(0);
|
|
451
|
+
}
|
|
452
|
+
else if (previewPhotoIndex === 1 && capturedPhotos.length === 2) {
|
|
453
|
+
console.log('[FullDocScanner] Retake detected on back preview after final confirmation - removing back photo');
|
|
454
|
+
setCapturedPhotos(capturedPhotos.slice(0, 1));
|
|
455
|
+
setCurrentPhotoIndex(1);
|
|
456
|
+
setPreviewPhotoIndex(1);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
436
459
|
setCroppedImageData(null);
|
|
437
460
|
setRotationDegrees(0);
|
|
438
461
|
setProcessing(false);
|
|
@@ -452,7 +475,7 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
452
475
|
if (docScannerRef.current?.reset) {
|
|
453
476
|
docScannerRef.current.reset();
|
|
454
477
|
}
|
|
455
|
-
}, []);
|
|
478
|
+
}, [capturedPhotos, currentPhotoIndex, isBusinessMode, previewPhotoIndex]);
|
|
456
479
|
const handleRectangleDetect = (0, react_1.useCallback)((event) => {
|
|
457
480
|
const stableCounter = event.stableCounter ?? 0;
|
|
458
481
|
const rectangleCoordinates = event.rectangleOnScreen ?? event.rectangleCoordinates;
|
|
@@ -506,7 +529,7 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
506
529
|
// check_DP: Show confirmation screen
|
|
507
530
|
react_1.default.createElement(react_native_1.View, { style: styles.confirmationContainer },
|
|
508
531
|
isBusinessMode && (react_1.default.createElement(react_native_1.View, { style: styles.photoHeader },
|
|
509
|
-
react_1.default.createElement(react_native_1.Text, { style: styles.photoHeaderText },
|
|
532
|
+
react_1.default.createElement(react_native_1.Text, { style: styles.photoHeaderText }, previewPhotoIndex === 0 ? mergedStrings.first : mergedStrings.second))),
|
|
510
533
|
isImageRotationSupported() ? (react_1.default.createElement(react_native_1.View, { style: styles.rotateButtonsCenter },
|
|
511
534
|
react_1.default.createElement(react_native_1.TouchableOpacity, { style: styles.rotateButtonTop, onPress: () => handleRotateImage(-90), accessibilityLabel: "\uC67C\uCABD\uC73C\uB85C 90\uB3C4 \uD68C\uC804", accessibilityRole: "button" },
|
|
512
535
|
react_1.default.createElement(react_native_1.Text, { style: styles.rotateIconText }, "\u21BA"),
|
|
@@ -518,10 +541,18 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
518
541
|
styles.previewImage,
|
|
519
542
|
{ transform: [{ rotate: `${rotationDegrees}deg` }] }
|
|
520
543
|
], resizeMode: "contain" }),
|
|
544
|
+
isBusinessMode &&
|
|
545
|
+
capturedPhotos.length === 1 &&
|
|
546
|
+
currentPhotoIndex === 1 &&
|
|
547
|
+
previewPhotoIndex === 0 &&
|
|
548
|
+
mergedStrings.secondPrompt ? (react_1.default.createElement(react_native_1.Text, { style: styles.confirmationPromptText }, mergedStrings.secondPrompt)) : null,
|
|
521
549
|
react_1.default.createElement(react_native_1.View, { style: styles.confirmationButtons },
|
|
522
550
|
react_1.default.createElement(react_native_1.TouchableOpacity, { style: [styles.confirmButton, styles.retakeButton], onPress: handleRetake, accessibilityLabel: mergedStrings.retake, accessibilityRole: "button" },
|
|
523
551
|
react_1.default.createElement(react_native_1.Text, { style: styles.confirmButtonText }, mergedStrings.retake)),
|
|
524
|
-
isBusinessMode &&
|
|
552
|
+
isBusinessMode &&
|
|
553
|
+
capturedPhotos.length === 1 &&
|
|
554
|
+
currentPhotoIndex === 1 &&
|
|
555
|
+
previewPhotoIndex === 0 ? (react_1.default.createElement(react_1.default.Fragment, null,
|
|
525
556
|
react_1.default.createElement(react_native_1.TouchableOpacity, { style: [styles.confirmButton, styles.confirmButtonPrimary], onPress: handleCaptureSecondPhoto, accessibilityLabel: mergedStrings.secondBtn, accessibilityRole: "button" },
|
|
526
557
|
react_1.default.createElement(react_native_1.Text, { style: styles.confirmButtonText }, mergedStrings.secondBtn)),
|
|
527
558
|
react_1.default.createElement(react_native_1.TouchableOpacity, { style: [styles.confirmButton, styles.skipButton], onPress: handleSkipSecondPhoto, accessibilityLabel: mergedStrings.confirm, accessibilityRole: "button" },
|
|
@@ -718,6 +749,14 @@ const styles = react_native_1.StyleSheet.create({
|
|
|
718
749
|
width: '100%',
|
|
719
750
|
height: '80%',
|
|
720
751
|
},
|
|
752
|
+
confirmationPromptText: {
|
|
753
|
+
color: '#fff',
|
|
754
|
+
fontSize: 18,
|
|
755
|
+
fontWeight: '600',
|
|
756
|
+
textAlign: 'center',
|
|
757
|
+
paddingHorizontal: 32,
|
|
758
|
+
marginTop: 24,
|
|
759
|
+
},
|
|
721
760
|
confirmationButtons: {
|
|
722
761
|
flexDirection: 'row',
|
|
723
762
|
justifyContent: 'center',
|
package/package.json
CHANGED
package/src/FullDocScanner.tsx
CHANGED
|
@@ -138,6 +138,7 @@ export interface FullDocScannerStrings {
|
|
|
138
138
|
first?: string;
|
|
139
139
|
second?: string;
|
|
140
140
|
secondBtn?: string;
|
|
141
|
+
secondPrompt?: string;
|
|
141
142
|
}
|
|
142
143
|
|
|
143
144
|
export interface FullDocScannerProps {
|
|
@@ -182,16 +183,22 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
182
183
|
const [rotationDegrees, setRotationDegrees] = useState(0);
|
|
183
184
|
const [capturedPhotos, setCapturedPhotos] = useState<FullDocScannerResult[]>([]);
|
|
184
185
|
const [currentPhotoIndex, setCurrentPhotoIndex] = useState(0);
|
|
186
|
+
const [previewPhotoIndex, setPreviewPhotoIndex] = useState(0);
|
|
185
187
|
const resolvedGridColor = gridColor ?? overlayColor;
|
|
186
188
|
const docScannerRef = useRef<DocScannerHandle | null>(null);
|
|
187
189
|
const captureModeRef = useRef<'grid' | 'no-grid' | null>(null);
|
|
188
190
|
const captureInProgressRef = useRef(false);
|
|
189
191
|
const rectangleCaptureTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
190
192
|
const rectangleHintTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
193
|
+
const currentPhotoIndexRef = useRef(currentPhotoIndex);
|
|
191
194
|
|
|
192
195
|
const isBusinessMode = type === 'business';
|
|
193
196
|
const maxPhotos = isBusinessMode ? 2 : 1;
|
|
194
197
|
|
|
198
|
+
useEffect(() => {
|
|
199
|
+
currentPhotoIndexRef.current = currentPhotoIndex;
|
|
200
|
+
}, [currentPhotoIndex]);
|
|
201
|
+
|
|
195
202
|
const mergedStrings = useMemo(
|
|
196
203
|
() => ({
|
|
197
204
|
captureHint: strings?.captureHint,
|
|
@@ -205,6 +212,7 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
205
212
|
first: strings?.first ?? 'Front',
|
|
206
213
|
second: strings?.second ?? 'Back',
|
|
207
214
|
secondBtn: strings?.secondBtn ?? 'Capture Back Side?',
|
|
215
|
+
secondPrompt: strings?.secondPrompt ?? strings?.secondBtn ?? 'Capture Back Side?',
|
|
208
216
|
}),
|
|
209
217
|
[strings],
|
|
210
218
|
);
|
|
@@ -261,6 +269,8 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
261
269
|
|
|
262
270
|
setProcessing(false);
|
|
263
271
|
|
|
272
|
+
setPreviewPhotoIndex(currentPhotoIndexRef.current);
|
|
273
|
+
|
|
264
274
|
// Show confirmation screen
|
|
265
275
|
setCroppedImageData({
|
|
266
276
|
path: croppedImage.path,
|
|
@@ -323,6 +333,9 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
323
333
|
return;
|
|
324
334
|
}
|
|
325
335
|
|
|
336
|
+
const previewIndex = currentPhotoIndexRef.current;
|
|
337
|
+
setPreviewPhotoIndex(previewIndex);
|
|
338
|
+
|
|
326
339
|
const normalizedDoc = normalizeCapturedDocument(document);
|
|
327
340
|
|
|
328
341
|
if (captureMode === 'no-grid') {
|
|
@@ -574,6 +587,21 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
574
587
|
|
|
575
588
|
const handleRetake = useCallback(() => {
|
|
576
589
|
console.log('[FullDocScanner] Retake - clearing cropped image and resetting scanner');
|
|
590
|
+
|
|
591
|
+
if (isBusinessMode) {
|
|
592
|
+
if (currentPhotoIndex === 1 && previewPhotoIndex === 0 && capturedPhotos.length === 1) {
|
|
593
|
+
console.log('[FullDocScanner] Retake detected on front preview after confirmation - reverting to front capture');
|
|
594
|
+
setCapturedPhotos([]);
|
|
595
|
+
setCurrentPhotoIndex(0);
|
|
596
|
+
setPreviewPhotoIndex(0);
|
|
597
|
+
} else if (previewPhotoIndex === 1 && capturedPhotos.length === 2) {
|
|
598
|
+
console.log('[FullDocScanner] Retake detected on back preview after final confirmation - removing back photo');
|
|
599
|
+
setCapturedPhotos(capturedPhotos.slice(0, 1));
|
|
600
|
+
setCurrentPhotoIndex(1);
|
|
601
|
+
setPreviewPhotoIndex(1);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
577
605
|
setCroppedImageData(null);
|
|
578
606
|
setRotationDegrees(0);
|
|
579
607
|
setProcessing(false);
|
|
@@ -593,7 +621,7 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
593
621
|
if (docScannerRef.current?.reset) {
|
|
594
622
|
docScannerRef.current.reset();
|
|
595
623
|
}
|
|
596
|
-
}, []);
|
|
624
|
+
}, [capturedPhotos, currentPhotoIndex, isBusinessMode, previewPhotoIndex]);
|
|
597
625
|
|
|
598
626
|
const handleRectangleDetect = useCallback((event: RectangleDetectEvent) => {
|
|
599
627
|
const stableCounter = event.stableCounter ?? 0;
|
|
@@ -661,7 +689,7 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
661
689
|
{isBusinessMode && (
|
|
662
690
|
<View style={styles.photoHeader}>
|
|
663
691
|
<Text style={styles.photoHeaderText}>
|
|
664
|
-
{
|
|
692
|
+
{previewPhotoIndex === 0 ? mergedStrings.first : mergedStrings.second}
|
|
665
693
|
</Text>
|
|
666
694
|
</View>
|
|
667
695
|
)}
|
|
@@ -697,6 +725,13 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
697
725
|
]}
|
|
698
726
|
resizeMode="contain"
|
|
699
727
|
/>
|
|
728
|
+
{isBusinessMode &&
|
|
729
|
+
capturedPhotos.length === 1 &&
|
|
730
|
+
currentPhotoIndex === 1 &&
|
|
731
|
+
previewPhotoIndex === 0 &&
|
|
732
|
+
mergedStrings.secondPrompt ? (
|
|
733
|
+
<Text style={styles.confirmationPromptText}>{mergedStrings.secondPrompt}</Text>
|
|
734
|
+
) : null}
|
|
700
735
|
<View style={styles.confirmationButtons}>
|
|
701
736
|
<TouchableOpacity
|
|
702
737
|
style={[styles.confirmButton, styles.retakeButton]}
|
|
@@ -708,7 +743,10 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
708
743
|
</TouchableOpacity>
|
|
709
744
|
|
|
710
745
|
{/* Business 모드이고 첫 번째 사진을 찍은 후: 뒷면 촬영 버튼 또는 확인 버튼 */}
|
|
711
|
-
{isBusinessMode &&
|
|
746
|
+
{isBusinessMode &&
|
|
747
|
+
capturedPhotos.length === 1 &&
|
|
748
|
+
currentPhotoIndex === 1 &&
|
|
749
|
+
previewPhotoIndex === 0 ? (
|
|
712
750
|
<>
|
|
713
751
|
<TouchableOpacity
|
|
714
752
|
style={[styles.confirmButton, styles.confirmButtonPrimary]}
|
|
@@ -1008,6 +1046,14 @@ const styles = StyleSheet.create({
|
|
|
1008
1046
|
width: '100%',
|
|
1009
1047
|
height: '80%',
|
|
1010
1048
|
},
|
|
1049
|
+
confirmationPromptText: {
|
|
1050
|
+
color: '#fff',
|
|
1051
|
+
fontSize: 18,
|
|
1052
|
+
fontWeight: '600',
|
|
1053
|
+
textAlign: 'center',
|
|
1054
|
+
paddingHorizontal: 32,
|
|
1055
|
+
marginTop: 24,
|
|
1056
|
+
},
|
|
1011
1057
|
confirmationButtons: {
|
|
1012
1058
|
flexDirection: 'row',
|
|
1013
1059
|
justifyContent: 'center',
|