react-native-rectangle-doc-scanner 3.112.0 → 3.114.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 +5 -1
- package/dist/FullDocScanner.js +97 -8
- package/package.json +1 -1
- package/src/FullDocScanner.tsx +150 -14
package/dist/FullDocScanner.d.ts
CHANGED
|
@@ -20,9 +20,12 @@ export interface FullDocScannerStrings {
|
|
|
20
20
|
retake?: string;
|
|
21
21
|
confirm?: string;
|
|
22
22
|
cropTitle?: string;
|
|
23
|
+
first?: string;
|
|
24
|
+
second?: string;
|
|
25
|
+
secondBtn?: string;
|
|
23
26
|
}
|
|
24
27
|
export interface FullDocScannerProps {
|
|
25
|
-
onResult: (
|
|
28
|
+
onResult: (results: FullDocScannerResult[]) => void;
|
|
26
29
|
onClose?: () => void;
|
|
27
30
|
detectionConfig?: DetectionConfig;
|
|
28
31
|
overlayColor?: string;
|
|
@@ -35,5 +38,6 @@ export interface FullDocScannerProps {
|
|
|
35
38
|
enableGallery?: boolean;
|
|
36
39
|
cropWidth?: number;
|
|
37
40
|
cropHeight?: number;
|
|
41
|
+
type?: 'business';
|
|
38
42
|
}
|
|
39
43
|
export declare const FullDocScanner: React.FC<FullDocScannerProps>;
|
package/dist/FullDocScanner.js
CHANGED
|
@@ -115,7 +115,7 @@ const normalizeCapturedDocument = (document) => {
|
|
|
115
115
|
croppedPath: document.croppedPath ? stripFileUri(document.croppedPath) : null,
|
|
116
116
|
};
|
|
117
117
|
};
|
|
118
|
-
const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3170f3', gridColor, gridLineWidth, showGrid, strings, minStableFrames, onError, enableGallery = true, cropWidth = 1200, cropHeight = 1600, }) => {
|
|
118
|
+
const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3170f3', gridColor, gridLineWidth, showGrid, strings, minStableFrames, onError, enableGallery = true, cropWidth = 1200, cropHeight = 1600, type, }) => {
|
|
119
119
|
const [processing, setProcessing] = (0, react_1.useState)(false);
|
|
120
120
|
const [croppedImageData, setCroppedImageData] = (0, react_1.useState)(null);
|
|
121
121
|
const [isGalleryOpen, setIsGalleryOpen] = (0, react_1.useState)(false);
|
|
@@ -123,12 +123,16 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
123
123
|
const [rectangleHint, setRectangleHint] = (0, react_1.useState)(false);
|
|
124
124
|
const [flashEnabled, setFlashEnabled] = (0, react_1.useState)(false);
|
|
125
125
|
const [rotationDegrees, setRotationDegrees] = (0, react_1.useState)(0);
|
|
126
|
+
const [capturedPhotos, setCapturedPhotos] = (0, react_1.useState)([]);
|
|
127
|
+
const [currentPhotoIndex, setCurrentPhotoIndex] = (0, react_1.useState)(0);
|
|
126
128
|
const resolvedGridColor = gridColor ?? overlayColor;
|
|
127
129
|
const docScannerRef = (0, react_1.useRef)(null);
|
|
128
130
|
const captureModeRef = (0, react_1.useRef)(null);
|
|
129
131
|
const captureInProgressRef = (0, react_1.useRef)(false);
|
|
130
132
|
const rectangleCaptureTimeoutRef = (0, react_1.useRef)(null);
|
|
131
133
|
const rectangleHintTimeoutRef = (0, react_1.useRef)(null);
|
|
134
|
+
const isBusinessMode = type === 'business';
|
|
135
|
+
const maxPhotos = isBusinessMode ? 2 : 1;
|
|
132
136
|
const mergedStrings = (0, react_1.useMemo)(() => ({
|
|
133
137
|
captureHint: strings?.captureHint,
|
|
134
138
|
manualHint: strings?.manualHint,
|
|
@@ -138,6 +142,9 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
138
142
|
retake: strings?.retake ?? 'Retake',
|
|
139
143
|
confirm: strings?.confirm ?? 'Confirm',
|
|
140
144
|
cropTitle: strings?.cropTitle ?? 'Crop Document',
|
|
145
|
+
first: strings?.first ?? 'Front',
|
|
146
|
+
second: strings?.second ?? 'Back',
|
|
147
|
+
secondBtn: strings?.secondBtn ?? 'Capture Back Side?',
|
|
141
148
|
}), [strings]);
|
|
142
149
|
const emitError = (0, react_1.useCallback)((error, fallbackMessage) => {
|
|
143
150
|
console.error('[FullDocScanner] error', error);
|
|
@@ -377,14 +384,53 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
377
384
|
// 회전 각도 정규화 (0, 90, 180, 270)
|
|
378
385
|
const rotationNormalized = ((rotationDegrees % 360) + 360) % 360;
|
|
379
386
|
console.log('[FullDocScanner] Confirm - rotation degrees:', rotationDegrees, 'normalized:', rotationNormalized);
|
|
380
|
-
//
|
|
381
|
-
|
|
382
|
-
onResult({
|
|
387
|
+
// 현재 사진을 capturedPhotos에 추가
|
|
388
|
+
const currentPhoto = {
|
|
383
389
|
path: croppedImageData.path,
|
|
384
390
|
base64: croppedImageData.base64,
|
|
385
391
|
rotationDegrees: rotationNormalized,
|
|
386
|
-
}
|
|
387
|
-
|
|
392
|
+
};
|
|
393
|
+
const updatedPhotos = [...capturedPhotos, currentPhoto];
|
|
394
|
+
console.log('[FullDocScanner] Photos captured:', updatedPhotos.length, 'of', maxPhotos);
|
|
395
|
+
// Business 모드이고 아직 첫 번째 사진만 찍은 경우
|
|
396
|
+
if (isBusinessMode && updatedPhotos.length === 1) {
|
|
397
|
+
// 두 번째 사진 촬영 여부를 물어봄 (UI에서 버튼으로 표시)
|
|
398
|
+
setCapturedPhotos(updatedPhotos);
|
|
399
|
+
setCurrentPhotoIndex(1);
|
|
400
|
+
// 확인 화면을 유지하고 "뒷면 촬영" 버튼을 표시
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
// 모든 사진 촬영 완료 - 결과 반환
|
|
404
|
+
console.log('[FullDocScanner] All photos captured, returning results');
|
|
405
|
+
onResult(updatedPhotos);
|
|
406
|
+
}, [croppedImageData, rotationDegrees, capturedPhotos, isBusinessMode, maxPhotos, onResult]);
|
|
407
|
+
const handleCaptureSecondPhoto = (0, react_1.useCallback)(() => {
|
|
408
|
+
console.log('[FullDocScanner] Capturing second photo');
|
|
409
|
+
// 확인 화면을 닫고 카메라로 돌아감
|
|
410
|
+
setCroppedImageData(null);
|
|
411
|
+
setRotationDegrees(0);
|
|
412
|
+
setProcessing(false);
|
|
413
|
+
setRectangleDetected(false);
|
|
414
|
+
setRectangleHint(false);
|
|
415
|
+
captureModeRef.current = null;
|
|
416
|
+
captureInProgressRef.current = false;
|
|
417
|
+
if (rectangleCaptureTimeoutRef.current) {
|
|
418
|
+
clearTimeout(rectangleCaptureTimeoutRef.current);
|
|
419
|
+
rectangleCaptureTimeoutRef.current = null;
|
|
420
|
+
}
|
|
421
|
+
if (rectangleHintTimeoutRef.current) {
|
|
422
|
+
clearTimeout(rectangleHintTimeoutRef.current);
|
|
423
|
+
rectangleHintTimeoutRef.current = null;
|
|
424
|
+
}
|
|
425
|
+
if (docScannerRef.current?.reset) {
|
|
426
|
+
docScannerRef.current.reset();
|
|
427
|
+
}
|
|
428
|
+
}, []);
|
|
429
|
+
const handleSkipSecondPhoto = (0, react_1.useCallback)(() => {
|
|
430
|
+
console.log('[FullDocScanner] Skipping second photo');
|
|
431
|
+
// 첫 번째 사진만 반환
|
|
432
|
+
onResult(capturedPhotos);
|
|
433
|
+
}, [capturedPhotos, onResult]);
|
|
388
434
|
const handleRetake = (0, react_1.useCallback)(() => {
|
|
389
435
|
console.log('[FullDocScanner] Retake - clearing cropped image and resetting scanner');
|
|
390
436
|
setCroppedImageData(null);
|
|
@@ -459,6 +505,8 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
459
505
|
croppedImageData ? (
|
|
460
506
|
// check_DP: Show confirmation screen
|
|
461
507
|
react_1.default.createElement(react_native_1.View, { style: styles.confirmationContainer },
|
|
508
|
+
isBusinessMode && (react_1.default.createElement(react_native_1.View, { style: styles.photoHeader },
|
|
509
|
+
react_1.default.createElement(react_native_1.Text, { style: styles.photoHeaderText }, currentPhotoIndex === 0 ? mergedStrings.first : mergedStrings.second))),
|
|
462
510
|
isImageRotationSupported() ? (react_1.default.createElement(react_native_1.View, { style: styles.rotateButtonsCenter },
|
|
463
511
|
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" },
|
|
464
512
|
react_1.default.createElement(react_native_1.Text, { style: styles.rotateIconText }, "\u21BA"),
|
|
@@ -473,8 +521,12 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
473
521
|
react_1.default.createElement(react_native_1.View, { style: styles.confirmationButtons },
|
|
474
522
|
react_1.default.createElement(react_native_1.TouchableOpacity, { style: [styles.confirmButton, styles.retakeButton], onPress: handleRetake, accessibilityLabel: mergedStrings.retake, accessibilityRole: "button" },
|
|
475
523
|
react_1.default.createElement(react_native_1.Text, { style: styles.confirmButtonText }, mergedStrings.retake)),
|
|
476
|
-
|
|
477
|
-
react_1.default.createElement(react_native_1.
|
|
524
|
+
isBusinessMode && capturedPhotos.length === 1 && currentPhotoIndex === 1 ? (react_1.default.createElement(react_1.default.Fragment, null,
|
|
525
|
+
react_1.default.createElement(react_native_1.TouchableOpacity, { style: [styles.confirmButton, styles.confirmButtonPrimary], onPress: handleCaptureSecondPhoto, accessibilityLabel: mergedStrings.secondBtn, accessibilityRole: "button" },
|
|
526
|
+
react_1.default.createElement(react_native_1.Text, { style: styles.confirmButtonText }, mergedStrings.secondBtn)),
|
|
527
|
+
react_1.default.createElement(react_native_1.TouchableOpacity, { style: [styles.confirmButton, styles.skipButton], onPress: handleSkipSecondPhoto, accessibilityLabel: mergedStrings.confirm, accessibilityRole: "button" },
|
|
528
|
+
react_1.default.createElement(react_native_1.Text, { style: styles.confirmButtonText }, mergedStrings.confirm)))) : (react_1.default.createElement(react_native_1.TouchableOpacity, { style: [styles.confirmButton, styles.confirmButtonPrimary], onPress: handleConfirm, accessibilityLabel: mergedStrings.confirm, accessibilityRole: "button" },
|
|
529
|
+
react_1.default.createElement(react_native_1.Text, { style: styles.confirmButtonText }, mergedStrings.confirm)))))) : (react_1.default.createElement(react_native_1.View, { style: styles.flex },
|
|
478
530
|
react_1.default.createElement(DocScanner_1.DocScanner, { ref: docScannerRef, autoCapture: false, overlayColor: overlayColor, showGrid: showGrid, gridColor: resolvedGridColor, gridLineWidth: gridLineWidth, minStableFrames: minStableFrames ?? 6, detectionConfig: detectionConfig, onCapture: handleCapture, onRectangleDetect: handleRectangleDetect, showManualCaptureButton: false, enableTorch: flashEnabled },
|
|
479
531
|
react_1.default.createElement(react_native_1.View, { style: styles.overlayTop, pointerEvents: "box-none" },
|
|
480
532
|
react_1.default.createElement(react_native_1.TouchableOpacity, { style: [
|
|
@@ -484,6 +536,8 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
484
536
|
], onPress: handleFlashToggle, disabled: processing, accessibilityLabel: "Toggle flash", accessibilityRole: "button" },
|
|
485
537
|
react_1.default.createElement(react_native_1.View, { style: styles.iconContainer },
|
|
486
538
|
react_1.default.createElement(react_native_1.Text, { style: styles.iconText }, "\u26A1\uFE0F"))),
|
|
539
|
+
isBusinessMode && (react_1.default.createElement(react_native_1.View, { style: styles.cameraHeaderContainer },
|
|
540
|
+
react_1.default.createElement(react_native_1.Text, { style: styles.cameraHeaderText }, currentPhotoIndex === 0 ? mergedStrings.first : mergedStrings.second))),
|
|
487
541
|
react_1.default.createElement(react_native_1.TouchableOpacity, { style: styles.iconButton, onPress: handleClose, accessibilityLabel: mergedStrings.cancel, accessibilityRole: "button" },
|
|
488
542
|
react_1.default.createElement(react_native_1.View, { style: styles.iconContainer },
|
|
489
543
|
react_1.default.createElement(react_native_1.Text, { style: styles.closeIconText }, "\u00D7")))),
|
|
@@ -687,9 +741,44 @@ const styles = react_native_1.StyleSheet.create({
|
|
|
687
741
|
confirmButtonPrimary: {
|
|
688
742
|
backgroundColor: '#3170f3',
|
|
689
743
|
},
|
|
744
|
+
skipButton: {
|
|
745
|
+
backgroundColor: 'rgba(100,100,100,0.8)',
|
|
746
|
+
},
|
|
690
747
|
confirmButtonText: {
|
|
691
748
|
color: '#fff',
|
|
692
749
|
fontSize: 18,
|
|
693
750
|
fontWeight: '600',
|
|
694
751
|
},
|
|
752
|
+
photoHeader: {
|
|
753
|
+
position: 'absolute',
|
|
754
|
+
top: 20,
|
|
755
|
+
left: 0,
|
|
756
|
+
right: 0,
|
|
757
|
+
alignItems: 'center',
|
|
758
|
+
zIndex: 5,
|
|
759
|
+
},
|
|
760
|
+
photoHeaderText: {
|
|
761
|
+
color: '#fff',
|
|
762
|
+
fontSize: 20,
|
|
763
|
+
fontWeight: 'bold',
|
|
764
|
+
backgroundColor: 'rgba(0,0,0,0.6)',
|
|
765
|
+
paddingHorizontal: 24,
|
|
766
|
+
paddingVertical: 8,
|
|
767
|
+
borderRadius: 20,
|
|
768
|
+
},
|
|
769
|
+
cameraHeaderContainer: {
|
|
770
|
+
position: 'absolute',
|
|
771
|
+
left: 0,
|
|
772
|
+
right: 0,
|
|
773
|
+
alignItems: 'center',
|
|
774
|
+
},
|
|
775
|
+
cameraHeaderText: {
|
|
776
|
+
color: '#fff',
|
|
777
|
+
fontSize: 18,
|
|
778
|
+
fontWeight: 'bold',
|
|
779
|
+
backgroundColor: 'rgba(0,0,0,0.7)',
|
|
780
|
+
paddingHorizontal: 20,
|
|
781
|
+
paddingVertical: 6,
|
|
782
|
+
borderRadius: 16,
|
|
783
|
+
},
|
|
695
784
|
});
|
package/package.json
CHANGED
package/src/FullDocScanner.tsx
CHANGED
|
@@ -135,10 +135,13 @@ export interface FullDocScannerStrings {
|
|
|
135
135
|
retake?: string;
|
|
136
136
|
confirm?: string;
|
|
137
137
|
cropTitle?: string;
|
|
138
|
+
first?: string;
|
|
139
|
+
second?: string;
|
|
140
|
+
secondBtn?: string;
|
|
138
141
|
}
|
|
139
142
|
|
|
140
143
|
export interface FullDocScannerProps {
|
|
141
|
-
onResult: (
|
|
144
|
+
onResult: (results: FullDocScannerResult[]) => void;
|
|
142
145
|
onClose?: () => void;
|
|
143
146
|
detectionConfig?: DetectionConfig;
|
|
144
147
|
overlayColor?: string;
|
|
@@ -151,6 +154,7 @@ export interface FullDocScannerProps {
|
|
|
151
154
|
enableGallery?: boolean;
|
|
152
155
|
cropWidth?: number;
|
|
153
156
|
cropHeight?: number;
|
|
157
|
+
type?: 'business';
|
|
154
158
|
}
|
|
155
159
|
|
|
156
160
|
export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
@@ -167,6 +171,7 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
167
171
|
enableGallery = true,
|
|
168
172
|
cropWidth = 1200,
|
|
169
173
|
cropHeight = 1600,
|
|
174
|
+
type,
|
|
170
175
|
}) => {
|
|
171
176
|
const [processing, setProcessing] = useState(false);
|
|
172
177
|
const [croppedImageData, setCroppedImageData] = useState<{path: string; base64?: string} | null>(null);
|
|
@@ -175,6 +180,8 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
175
180
|
const [rectangleHint, setRectangleHint] = useState(false);
|
|
176
181
|
const [flashEnabled, setFlashEnabled] = useState(false);
|
|
177
182
|
const [rotationDegrees, setRotationDegrees] = useState(0);
|
|
183
|
+
const [capturedPhotos, setCapturedPhotos] = useState<FullDocScannerResult[]>([]);
|
|
184
|
+
const [currentPhotoIndex, setCurrentPhotoIndex] = useState(0);
|
|
178
185
|
const resolvedGridColor = gridColor ?? overlayColor;
|
|
179
186
|
const docScannerRef = useRef<DocScannerHandle | null>(null);
|
|
180
187
|
const captureModeRef = useRef<'grid' | 'no-grid' | null>(null);
|
|
@@ -182,6 +189,9 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
182
189
|
const rectangleCaptureTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
183
190
|
const rectangleHintTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
184
191
|
|
|
192
|
+
const isBusinessMode = type === 'business';
|
|
193
|
+
const maxPhotos = isBusinessMode ? 2 : 1;
|
|
194
|
+
|
|
185
195
|
const mergedStrings = useMemo(
|
|
186
196
|
() => ({
|
|
187
197
|
captureHint: strings?.captureHint,
|
|
@@ -192,6 +202,9 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
192
202
|
retake: strings?.retake ?? 'Retake',
|
|
193
203
|
confirm: strings?.confirm ?? 'Confirm',
|
|
194
204
|
cropTitle: strings?.cropTitle ?? 'Crop Document',
|
|
205
|
+
first: strings?.first ?? 'Front',
|
|
206
|
+
second: strings?.second ?? 'Back',
|
|
207
|
+
secondBtn: strings?.secondBtn ?? 'Capture Back Side?',
|
|
195
208
|
}),
|
|
196
209
|
[strings],
|
|
197
210
|
);
|
|
@@ -506,14 +519,58 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
506
519
|
const rotationNormalized = ((rotationDegrees % 360) + 360) % 360;
|
|
507
520
|
console.log('[FullDocScanner] Confirm - rotation degrees:', rotationDegrees, 'normalized:', rotationNormalized);
|
|
508
521
|
|
|
509
|
-
//
|
|
510
|
-
|
|
511
|
-
onResult({
|
|
522
|
+
// 현재 사진을 capturedPhotos에 추가
|
|
523
|
+
const currentPhoto: FullDocScannerResult = {
|
|
512
524
|
path: croppedImageData.path,
|
|
513
525
|
base64: croppedImageData.base64,
|
|
514
526
|
rotationDegrees: rotationNormalized,
|
|
515
|
-
}
|
|
516
|
-
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
const updatedPhotos = [...capturedPhotos, currentPhoto];
|
|
530
|
+
console.log('[FullDocScanner] Photos captured:', updatedPhotos.length, 'of', maxPhotos);
|
|
531
|
+
|
|
532
|
+
// Business 모드이고 아직 첫 번째 사진만 찍은 경우
|
|
533
|
+
if (isBusinessMode && updatedPhotos.length === 1) {
|
|
534
|
+
// 두 번째 사진 촬영 여부를 물어봄 (UI에서 버튼으로 표시)
|
|
535
|
+
setCapturedPhotos(updatedPhotos);
|
|
536
|
+
setCurrentPhotoIndex(1);
|
|
537
|
+
// 확인 화면을 유지하고 "뒷면 촬영" 버튼을 표시
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// 모든 사진 촬영 완료 - 결과 반환
|
|
542
|
+
console.log('[FullDocScanner] All photos captured, returning results');
|
|
543
|
+
onResult(updatedPhotos);
|
|
544
|
+
}, [croppedImageData, rotationDegrees, capturedPhotos, isBusinessMode, maxPhotos, onResult]);
|
|
545
|
+
|
|
546
|
+
const handleCaptureSecondPhoto = useCallback(() => {
|
|
547
|
+
console.log('[FullDocScanner] Capturing second photo');
|
|
548
|
+
// 확인 화면을 닫고 카메라로 돌아감
|
|
549
|
+
setCroppedImageData(null);
|
|
550
|
+
setRotationDegrees(0);
|
|
551
|
+
setProcessing(false);
|
|
552
|
+
setRectangleDetected(false);
|
|
553
|
+
setRectangleHint(false);
|
|
554
|
+
captureModeRef.current = null;
|
|
555
|
+
captureInProgressRef.current = false;
|
|
556
|
+
if (rectangleCaptureTimeoutRef.current) {
|
|
557
|
+
clearTimeout(rectangleCaptureTimeoutRef.current);
|
|
558
|
+
rectangleCaptureTimeoutRef.current = null;
|
|
559
|
+
}
|
|
560
|
+
if (rectangleHintTimeoutRef.current) {
|
|
561
|
+
clearTimeout(rectangleHintTimeoutRef.current);
|
|
562
|
+
rectangleHintTimeoutRef.current = null;
|
|
563
|
+
}
|
|
564
|
+
if (docScannerRef.current?.reset) {
|
|
565
|
+
docScannerRef.current.reset();
|
|
566
|
+
}
|
|
567
|
+
}, []);
|
|
568
|
+
|
|
569
|
+
const handleSkipSecondPhoto = useCallback(() => {
|
|
570
|
+
console.log('[FullDocScanner] Skipping second photo');
|
|
571
|
+
// 첫 번째 사진만 반환
|
|
572
|
+
onResult(capturedPhotos);
|
|
573
|
+
}, [capturedPhotos, onResult]);
|
|
517
574
|
|
|
518
575
|
const handleRetake = useCallback(() => {
|
|
519
576
|
console.log('[FullDocScanner] Retake - clearing cropped image and resetting scanner');
|
|
@@ -600,6 +657,15 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
600
657
|
{croppedImageData ? (
|
|
601
658
|
// check_DP: Show confirmation screen
|
|
602
659
|
<View style={styles.confirmationContainer}>
|
|
660
|
+
{/* 헤더 - 앞면/뒷면 표시 */}
|
|
661
|
+
{isBusinessMode && (
|
|
662
|
+
<View style={styles.photoHeader}>
|
|
663
|
+
<Text style={styles.photoHeaderText}>
|
|
664
|
+
{currentPhotoIndex === 0 ? mergedStrings.first : mergedStrings.second}
|
|
665
|
+
</Text>
|
|
666
|
+
</View>
|
|
667
|
+
)}
|
|
668
|
+
|
|
603
669
|
{/* 회전 버튼들 - 가운데 정렬 */}
|
|
604
670
|
{isImageRotationSupported() ? (
|
|
605
671
|
<View style={styles.rotateButtonsCenter}>
|
|
@@ -640,14 +706,37 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
640
706
|
>
|
|
641
707
|
<Text style={styles.confirmButtonText}>{mergedStrings.retake}</Text>
|
|
642
708
|
</TouchableOpacity>
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
709
|
+
|
|
710
|
+
{/* Business 모드이고 첫 번째 사진을 찍은 후: 뒷면 촬영 버튼 또는 확인 버튼 */}
|
|
711
|
+
{isBusinessMode && capturedPhotos.length === 1 && currentPhotoIndex === 1 ? (
|
|
712
|
+
<>
|
|
713
|
+
<TouchableOpacity
|
|
714
|
+
style={[styles.confirmButton, styles.confirmButtonPrimary]}
|
|
715
|
+
onPress={handleCaptureSecondPhoto}
|
|
716
|
+
accessibilityLabel={mergedStrings.secondBtn}
|
|
717
|
+
accessibilityRole="button"
|
|
718
|
+
>
|
|
719
|
+
<Text style={styles.confirmButtonText}>{mergedStrings.secondBtn}</Text>
|
|
720
|
+
</TouchableOpacity>
|
|
721
|
+
<TouchableOpacity
|
|
722
|
+
style={[styles.confirmButton, styles.skipButton]}
|
|
723
|
+
onPress={handleSkipSecondPhoto}
|
|
724
|
+
accessibilityLabel={mergedStrings.confirm}
|
|
725
|
+
accessibilityRole="button"
|
|
726
|
+
>
|
|
727
|
+
<Text style={styles.confirmButtonText}>{mergedStrings.confirm}</Text>
|
|
728
|
+
</TouchableOpacity>
|
|
729
|
+
</>
|
|
730
|
+
) : (
|
|
731
|
+
<TouchableOpacity
|
|
732
|
+
style={[styles.confirmButton, styles.confirmButtonPrimary]}
|
|
733
|
+
onPress={handleConfirm}
|
|
734
|
+
accessibilityLabel={mergedStrings.confirm}
|
|
735
|
+
accessibilityRole="button"
|
|
736
|
+
>
|
|
737
|
+
<Text style={styles.confirmButtonText}>{mergedStrings.confirm}</Text>
|
|
738
|
+
</TouchableOpacity>
|
|
739
|
+
)}
|
|
651
740
|
</View>
|
|
652
741
|
</View>
|
|
653
742
|
) : (
|
|
@@ -667,6 +756,7 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
667
756
|
enableTorch={flashEnabled}
|
|
668
757
|
>
|
|
669
758
|
<View style={styles.overlayTop} pointerEvents="box-none">
|
|
759
|
+
{/* 좌측: 플래시 버튼 */}
|
|
670
760
|
<TouchableOpacity
|
|
671
761
|
style={[
|
|
672
762
|
styles.iconButton,
|
|
@@ -682,6 +772,17 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
682
772
|
<Text style={styles.iconText}>⚡️</Text>
|
|
683
773
|
</View>
|
|
684
774
|
</TouchableOpacity>
|
|
775
|
+
|
|
776
|
+
{/* 중앙: 앞면/뒷면 헤더 */}
|
|
777
|
+
{isBusinessMode && (
|
|
778
|
+
<View style={styles.cameraHeaderContainer}>
|
|
779
|
+
<Text style={styles.cameraHeaderText}>
|
|
780
|
+
{currentPhotoIndex === 0 ? mergedStrings.first : mergedStrings.second}
|
|
781
|
+
</Text>
|
|
782
|
+
</View>
|
|
783
|
+
)}
|
|
784
|
+
|
|
785
|
+
{/* 우측: 닫기 버튼 */}
|
|
685
786
|
<TouchableOpacity
|
|
686
787
|
style={styles.iconButton}
|
|
687
788
|
onPress={handleClose}
|
|
@@ -930,9 +1031,44 @@ const styles = StyleSheet.create({
|
|
|
930
1031
|
confirmButtonPrimary: {
|
|
931
1032
|
backgroundColor: '#3170f3',
|
|
932
1033
|
},
|
|
1034
|
+
skipButton: {
|
|
1035
|
+
backgroundColor: 'rgba(100,100,100,0.8)',
|
|
1036
|
+
},
|
|
933
1037
|
confirmButtonText: {
|
|
934
1038
|
color: '#fff',
|
|
935
1039
|
fontSize: 18,
|
|
936
1040
|
fontWeight: '600',
|
|
937
1041
|
},
|
|
1042
|
+
photoHeader: {
|
|
1043
|
+
position: 'absolute',
|
|
1044
|
+
top: 20,
|
|
1045
|
+
left: 0,
|
|
1046
|
+
right: 0,
|
|
1047
|
+
alignItems: 'center',
|
|
1048
|
+
zIndex: 5,
|
|
1049
|
+
},
|
|
1050
|
+
photoHeaderText: {
|
|
1051
|
+
color: '#fff',
|
|
1052
|
+
fontSize: 20,
|
|
1053
|
+
fontWeight: 'bold',
|
|
1054
|
+
backgroundColor: 'rgba(0,0,0,0.6)',
|
|
1055
|
+
paddingHorizontal: 24,
|
|
1056
|
+
paddingVertical: 8,
|
|
1057
|
+
borderRadius: 20,
|
|
1058
|
+
},
|
|
1059
|
+
cameraHeaderContainer: {
|
|
1060
|
+
position: 'absolute',
|
|
1061
|
+
left: 0,
|
|
1062
|
+
right: 0,
|
|
1063
|
+
alignItems: 'center',
|
|
1064
|
+
},
|
|
1065
|
+
cameraHeaderText: {
|
|
1066
|
+
color: '#fff',
|
|
1067
|
+
fontSize: 18,
|
|
1068
|
+
fontWeight: 'bold',
|
|
1069
|
+
backgroundColor: 'rgba(0,0,0,0.7)',
|
|
1070
|
+
paddingHorizontal: 20,
|
|
1071
|
+
paddingVertical: 6,
|
|
1072
|
+
borderRadius: 16,
|
|
1073
|
+
},
|
|
938
1074
|
});
|