react-native-rectangle-doc-scanner 3.121.0 → 3.123.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.js +42 -62
- package/package.json +1 -1
- package/src/FullDocScanner.tsx +53 -62
package/dist/FullDocScanner.js
CHANGED
|
@@ -125,6 +125,7 @@ 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 [scannerSession, setScannerSession] = (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);
|
|
@@ -132,6 +133,29 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
132
133
|
const rectangleCaptureTimeoutRef = (0, react_1.useRef)(null);
|
|
133
134
|
const rectangleHintTimeoutRef = (0, react_1.useRef)(null);
|
|
134
135
|
const isBusinessMode = type === 'business';
|
|
136
|
+
const resetScannerView = (0, react_1.useCallback)((options) => {
|
|
137
|
+
setProcessing(false);
|
|
138
|
+
setCroppedImageData(null);
|
|
139
|
+
setRotationDegrees(0);
|
|
140
|
+
setRectangleDetected(false);
|
|
141
|
+
setRectangleHint(false);
|
|
142
|
+
captureModeRef.current = null;
|
|
143
|
+
captureInProgressRef.current = false;
|
|
144
|
+
if (rectangleCaptureTimeoutRef.current) {
|
|
145
|
+
clearTimeout(rectangleCaptureTimeoutRef.current);
|
|
146
|
+
rectangleCaptureTimeoutRef.current = null;
|
|
147
|
+
}
|
|
148
|
+
if (rectangleHintTimeoutRef.current) {
|
|
149
|
+
clearTimeout(rectangleHintTimeoutRef.current);
|
|
150
|
+
rectangleHintTimeoutRef.current = null;
|
|
151
|
+
}
|
|
152
|
+
if (docScannerRef.current?.reset) {
|
|
153
|
+
docScannerRef.current.reset();
|
|
154
|
+
}
|
|
155
|
+
if (options?.remount) {
|
|
156
|
+
setScannerSession((prev) => prev + 1);
|
|
157
|
+
}
|
|
158
|
+
}, []);
|
|
135
159
|
const mergedStrings = (0, react_1.useMemo)(() => ({
|
|
136
160
|
captureHint: strings?.captureHint,
|
|
137
161
|
manualHint: strings?.manualHint,
|
|
@@ -191,37 +215,28 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
191
215
|
});
|
|
192
216
|
}
|
|
193
217
|
catch (error) {
|
|
194
|
-
|
|
195
|
-
setProcessing(false);
|
|
196
|
-
// Reset capture state when cropper fails or is cancelled
|
|
197
|
-
captureInProgressRef.current = false;
|
|
198
|
-
captureModeRef.current = null;
|
|
199
|
-
setRectangleDetected(false);
|
|
200
|
-
setRectangleHint(false);
|
|
201
|
-
if (docScannerRef.current?.reset) {
|
|
202
|
-
docScannerRef.current.reset();
|
|
203
|
-
}
|
|
218
|
+
resetScannerView({ remount: true });
|
|
204
219
|
const errorCode = error?.code;
|
|
205
|
-
const
|
|
220
|
+
const errorMessageRaw = error?.message ?? String(error);
|
|
221
|
+
const errorMessage = typeof errorMessageRaw === 'string' ? errorMessageRaw : String(errorMessageRaw);
|
|
222
|
+
const normalizedMessage = errorMessage.toLowerCase();
|
|
223
|
+
const isUserCancelled = errorCode === 'E_PICKER_CANCELLED' ||
|
|
224
|
+
normalizedMessage === 'user cancelled image selection' ||
|
|
225
|
+
normalizedMessage.includes('cancel');
|
|
226
|
+
if (isUserCancelled) {
|
|
227
|
+
console.log('[FullDocScanner] User cancelled cropper');
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
console.error('[FullDocScanner] openCropper error:', error);
|
|
206
231
|
if (errorCode === CROPPER_TIMEOUT_CODE || errorMessage === CROPPER_TIMEOUT_CODE) {
|
|
207
232
|
console.error('[FullDocScanner] Cropper timed out waiting for presentation');
|
|
208
233
|
emitError(error instanceof Error ? error : new Error('Cropper timed out'), 'Failed to open crop editor. Please try again.');
|
|
209
234
|
}
|
|
210
|
-
else if (errorCode === 'E_PICKER_CANCELLED' ||
|
|
211
|
-
errorMessage === 'User cancelled image selection' ||
|
|
212
|
-
errorMessage.includes('cancelled') ||
|
|
213
|
-
errorMessage.includes('cancel')) {
|
|
214
|
-
console.log('[FullDocScanner] User cancelled cropper');
|
|
215
|
-
// DocScanner 상태를 리셋하여 카메라가 다시 작동하도록 함
|
|
216
|
-
if (docScannerRef.current?.reset) {
|
|
217
|
-
docScannerRef.current.reset();
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
235
|
else {
|
|
221
236
|
emitError(error instanceof Error ? error : new Error(errorMessage), 'Failed to crop image. Please try again.');
|
|
222
237
|
}
|
|
223
238
|
}
|
|
224
|
-
}, [cropWidth, cropHeight, emitError]);
|
|
239
|
+
}, [cropWidth, cropHeight, emitError, resetScannerView]);
|
|
225
240
|
const handleCapture = (0, react_1.useCallback)(async (document) => {
|
|
226
241
|
console.log('[FullDocScanner] handleCapture called:', {
|
|
227
242
|
origin: document.origin,
|
|
@@ -420,25 +435,8 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
420
435
|
setCapturedPhotos([currentPhoto]);
|
|
421
436
|
setCurrentPhotoIndex(1);
|
|
422
437
|
// 확인 화면을 닫고 카메라로 돌아감
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
setProcessing(false);
|
|
426
|
-
setRectangleDetected(false);
|
|
427
|
-
setRectangleHint(false);
|
|
428
|
-
captureModeRef.current = null;
|
|
429
|
-
captureInProgressRef.current = false;
|
|
430
|
-
if (rectangleCaptureTimeoutRef.current) {
|
|
431
|
-
clearTimeout(rectangleCaptureTimeoutRef.current);
|
|
432
|
-
rectangleCaptureTimeoutRef.current = null;
|
|
433
|
-
}
|
|
434
|
-
if (rectangleHintTimeoutRef.current) {
|
|
435
|
-
clearTimeout(rectangleHintTimeoutRef.current);
|
|
436
|
-
rectangleHintTimeoutRef.current = null;
|
|
437
|
-
}
|
|
438
|
-
if (docScannerRef.current?.reset) {
|
|
439
|
-
docScannerRef.current.reset();
|
|
440
|
-
}
|
|
441
|
-
}, [croppedImageData, rotationDegrees]);
|
|
438
|
+
resetScannerView({ remount: true });
|
|
439
|
+
}, [croppedImageData, resetScannerView, rotationDegrees]);
|
|
442
440
|
const handleRetake = (0, react_1.useCallback)(() => {
|
|
443
441
|
console.log('[FullDocScanner] Retake - clearing cropped image and resetting scanner');
|
|
444
442
|
// Business 모드에서 두 번째 사진을 다시 찍는 경우, 첫 번째 사진 유지
|
|
@@ -452,26 +450,8 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
452
450
|
setCapturedPhotos([]);
|
|
453
451
|
setCurrentPhotoIndex(0);
|
|
454
452
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
setProcessing(false);
|
|
458
|
-
setRectangleDetected(false);
|
|
459
|
-
setRectangleHint(false);
|
|
460
|
-
captureModeRef.current = null;
|
|
461
|
-
captureInProgressRef.current = false;
|
|
462
|
-
if (rectangleCaptureTimeoutRef.current) {
|
|
463
|
-
clearTimeout(rectangleCaptureTimeoutRef.current);
|
|
464
|
-
rectangleCaptureTimeoutRef.current = null;
|
|
465
|
-
}
|
|
466
|
-
if (rectangleHintTimeoutRef.current) {
|
|
467
|
-
clearTimeout(rectangleHintTimeoutRef.current);
|
|
468
|
-
rectangleHintTimeoutRef.current = null;
|
|
469
|
-
}
|
|
470
|
-
// Reset DocScanner state
|
|
471
|
-
if (docScannerRef.current?.reset) {
|
|
472
|
-
docScannerRef.current.reset();
|
|
473
|
-
}
|
|
474
|
-
}, [capturedPhotos.length, isBusinessMode]);
|
|
453
|
+
resetScannerView({ remount: true });
|
|
454
|
+
}, [capturedPhotos.length, isBusinessMode, resetScannerView]);
|
|
475
455
|
const handleRectangleDetect = (0, react_1.useCallback)((event) => {
|
|
476
456
|
const stableCounter = event.stableCounter ?? 0;
|
|
477
457
|
const rectangleCoordinates = event.rectangleOnScreen ?? event.rectangleCoordinates;
|
|
@@ -549,7 +529,7 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
549
529
|
react_1.default.createElement(react_native_1.Text, { style: styles.confirmButtonText }, mergedStrings.retake)),
|
|
550
530
|
react_1.default.createElement(react_native_1.TouchableOpacity, { style: [styles.confirmButton, styles.confirmButtonPrimary], onPress: handleConfirm, accessibilityLabel: mergedStrings.confirm, accessibilityRole: "button" },
|
|
551
531
|
react_1.default.createElement(react_native_1.Text, { style: styles.confirmButtonText }, mergedStrings.confirm))))) : (react_1.default.createElement(react_native_1.View, { style: styles.flex },
|
|
552
|
-
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 },
|
|
532
|
+
react_1.default.createElement(DocScanner_1.DocScanner, { key: scannerSession, 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 },
|
|
553
533
|
react_1.default.createElement(react_native_1.View, { style: styles.overlayTop, pointerEvents: "box-none" },
|
|
554
534
|
react_1.default.createElement(react_native_1.TouchableOpacity, { style: [
|
|
555
535
|
styles.iconButton,
|
package/package.json
CHANGED
package/src/FullDocScanner.tsx
CHANGED
|
@@ -183,6 +183,7 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
183
183
|
const [rotationDegrees, setRotationDegrees] = useState(0);
|
|
184
184
|
const [capturedPhotos, setCapturedPhotos] = useState<FullDocScannerResult[]>([]);
|
|
185
185
|
const [currentPhotoIndex, setCurrentPhotoIndex] = useState(0);
|
|
186
|
+
const [scannerSession, setScannerSession] = useState(0);
|
|
186
187
|
const resolvedGridColor = gridColor ?? overlayColor;
|
|
187
188
|
const docScannerRef = useRef<DocScannerHandle | null>(null);
|
|
188
189
|
const captureModeRef = useRef<'grid' | 'no-grid' | null>(null);
|
|
@@ -192,6 +193,37 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
192
193
|
|
|
193
194
|
const isBusinessMode = type === 'business';
|
|
194
195
|
|
|
196
|
+
const resetScannerView = useCallback(
|
|
197
|
+
(options?: { remount?: boolean }) => {
|
|
198
|
+
setProcessing(false);
|
|
199
|
+
setCroppedImageData(null);
|
|
200
|
+
setRotationDegrees(0);
|
|
201
|
+
setRectangleDetected(false);
|
|
202
|
+
setRectangleHint(false);
|
|
203
|
+
captureModeRef.current = null;
|
|
204
|
+
captureInProgressRef.current = false;
|
|
205
|
+
|
|
206
|
+
if (rectangleCaptureTimeoutRef.current) {
|
|
207
|
+
clearTimeout(rectangleCaptureTimeoutRef.current);
|
|
208
|
+
rectangleCaptureTimeoutRef.current = null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (rectangleHintTimeoutRef.current) {
|
|
212
|
+
clearTimeout(rectangleHintTimeoutRef.current);
|
|
213
|
+
rectangleHintTimeoutRef.current = null;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (docScannerRef.current?.reset) {
|
|
217
|
+
docScannerRef.current.reset();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (options?.remount) {
|
|
221
|
+
setScannerSession((prev) => prev + 1);
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
[],
|
|
225
|
+
);
|
|
226
|
+
|
|
195
227
|
const mergedStrings = useMemo(
|
|
196
228
|
() => ({
|
|
197
229
|
captureHint: strings?.captureHint,
|
|
@@ -268,20 +300,24 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
268
300
|
base64: croppedImage.data ?? undefined,
|
|
269
301
|
});
|
|
270
302
|
} catch (error) {
|
|
271
|
-
|
|
272
|
-
setProcessing(false);
|
|
303
|
+
resetScannerView({ remount: true });
|
|
273
304
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
305
|
+
const errorCode = (error as any)?.code;
|
|
306
|
+
const errorMessageRaw = (error as any)?.message ?? String(error);
|
|
307
|
+
const errorMessage =
|
|
308
|
+
typeof errorMessageRaw === 'string' ? errorMessageRaw : String(errorMessageRaw);
|
|
309
|
+
const normalizedMessage = errorMessage.toLowerCase();
|
|
310
|
+
const isUserCancelled =
|
|
311
|
+
errorCode === 'E_PICKER_CANCELLED' ||
|
|
312
|
+
normalizedMessage === 'user cancelled image selection' ||
|
|
313
|
+
normalizedMessage.includes('cancel');
|
|
314
|
+
|
|
315
|
+
if (isUserCancelled) {
|
|
316
|
+
console.log('[FullDocScanner] User cancelled cropper');
|
|
317
|
+
return;
|
|
281
318
|
}
|
|
282
319
|
|
|
283
|
-
|
|
284
|
-
const errorMessage = (error as any)?.message || String(error);
|
|
320
|
+
console.error('[FullDocScanner] openCropper error:', error);
|
|
285
321
|
|
|
286
322
|
if (errorCode === CROPPER_TIMEOUT_CODE || errorMessage === CROPPER_TIMEOUT_CODE) {
|
|
287
323
|
console.error('[FullDocScanner] Cropper timed out waiting for presentation');
|
|
@@ -289,17 +325,6 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
289
325
|
error instanceof Error ? error : new Error('Cropper timed out'),
|
|
290
326
|
'Failed to open crop editor. Please try again.',
|
|
291
327
|
);
|
|
292
|
-
} else if (
|
|
293
|
-
errorCode === 'E_PICKER_CANCELLED' ||
|
|
294
|
-
errorMessage === 'User cancelled image selection' ||
|
|
295
|
-
errorMessage.includes('cancelled') ||
|
|
296
|
-
errorMessage.includes('cancel')
|
|
297
|
-
) {
|
|
298
|
-
console.log('[FullDocScanner] User cancelled cropper');
|
|
299
|
-
// DocScanner 상태를 리셋하여 카메라가 다시 작동하도록 함
|
|
300
|
-
if (docScannerRef.current?.reset) {
|
|
301
|
-
docScannerRef.current.reset();
|
|
302
|
-
}
|
|
303
328
|
} else {
|
|
304
329
|
emitError(
|
|
305
330
|
error instanceof Error ? error : new Error(errorMessage),
|
|
@@ -308,7 +333,7 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
308
333
|
}
|
|
309
334
|
}
|
|
310
335
|
},
|
|
311
|
-
[cropWidth, cropHeight, emitError],
|
|
336
|
+
[cropWidth, cropHeight, emitError, resetScannerView],
|
|
312
337
|
);
|
|
313
338
|
|
|
314
339
|
const handleCapture = useCallback(
|
|
@@ -563,25 +588,8 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
563
588
|
setCurrentPhotoIndex(1);
|
|
564
589
|
|
|
565
590
|
// 확인 화면을 닫고 카메라로 돌아감
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
setProcessing(false);
|
|
569
|
-
setRectangleDetected(false);
|
|
570
|
-
setRectangleHint(false);
|
|
571
|
-
captureModeRef.current = null;
|
|
572
|
-
captureInProgressRef.current = false;
|
|
573
|
-
if (rectangleCaptureTimeoutRef.current) {
|
|
574
|
-
clearTimeout(rectangleCaptureTimeoutRef.current);
|
|
575
|
-
rectangleCaptureTimeoutRef.current = null;
|
|
576
|
-
}
|
|
577
|
-
if (rectangleHintTimeoutRef.current) {
|
|
578
|
-
clearTimeout(rectangleHintTimeoutRef.current);
|
|
579
|
-
rectangleHintTimeoutRef.current = null;
|
|
580
|
-
}
|
|
581
|
-
if (docScannerRef.current?.reset) {
|
|
582
|
-
docScannerRef.current.reset();
|
|
583
|
-
}
|
|
584
|
-
}, [croppedImageData, rotationDegrees]);
|
|
591
|
+
resetScannerView({ remount: true });
|
|
592
|
+
}, [croppedImageData, resetScannerView, rotationDegrees]);
|
|
585
593
|
|
|
586
594
|
|
|
587
595
|
const handleRetake = useCallback(() => {
|
|
@@ -598,26 +606,8 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
598
606
|
setCurrentPhotoIndex(0);
|
|
599
607
|
}
|
|
600
608
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
setProcessing(false);
|
|
604
|
-
setRectangleDetected(false);
|
|
605
|
-
setRectangleHint(false);
|
|
606
|
-
captureModeRef.current = null;
|
|
607
|
-
captureInProgressRef.current = false;
|
|
608
|
-
if (rectangleCaptureTimeoutRef.current) {
|
|
609
|
-
clearTimeout(rectangleCaptureTimeoutRef.current);
|
|
610
|
-
rectangleCaptureTimeoutRef.current = null;
|
|
611
|
-
}
|
|
612
|
-
if (rectangleHintTimeoutRef.current) {
|
|
613
|
-
clearTimeout(rectangleHintTimeoutRef.current);
|
|
614
|
-
rectangleHintTimeoutRef.current = null;
|
|
615
|
-
}
|
|
616
|
-
// Reset DocScanner state
|
|
617
|
-
if (docScannerRef.current?.reset) {
|
|
618
|
-
docScannerRef.current.reset();
|
|
619
|
-
}
|
|
620
|
-
}, [capturedPhotos.length, isBusinessMode]);
|
|
609
|
+
resetScannerView({ remount: true });
|
|
610
|
+
}, [capturedPhotos.length, isBusinessMode, resetScannerView]);
|
|
621
611
|
|
|
622
612
|
const handleRectangleDetect = useCallback((event: RectangleDetectEvent) => {
|
|
623
613
|
const stableCounter = event.stableCounter ?? 0;
|
|
@@ -780,6 +770,7 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
780
770
|
) : (
|
|
781
771
|
<View style={styles.flex}>
|
|
782
772
|
<DocScanner
|
|
773
|
+
key={scannerSession}
|
|
783
774
|
ref={docScannerRef}
|
|
784
775
|
autoCapture={false}
|
|
785
776
|
overlayColor={overlayColor}
|