react-native-rectangle-doc-scanner 3.69.0 → 3.71.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
CHANGED
|
@@ -58,11 +58,13 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
58
58
|
const [croppedImageData, setCroppedImageData] = (0, react_1.useState)(null);
|
|
59
59
|
const [isGalleryOpen, setIsGalleryOpen] = (0, react_1.useState)(false);
|
|
60
60
|
const [rectangleDetected, setRectangleDetected] = (0, react_1.useState)(false);
|
|
61
|
+
const [rectangleHint, setRectangleHint] = (0, react_1.useState)(false);
|
|
61
62
|
const resolvedGridColor = gridColor ?? overlayColor;
|
|
62
63
|
const docScannerRef = (0, react_1.useRef)(null);
|
|
63
64
|
const captureModeRef = (0, react_1.useRef)(null);
|
|
64
65
|
const captureInProgressRef = (0, react_1.useRef)(false);
|
|
65
|
-
const
|
|
66
|
+
const rectangleCaptureTimeoutRef = (0, react_1.useRef)(null);
|
|
67
|
+
const rectangleHintTimeoutRef = (0, react_1.useRef)(null);
|
|
66
68
|
const mergedStrings = (0, react_1.useMemo)(() => ({
|
|
67
69
|
captureHint: strings?.captureHint,
|
|
68
70
|
manualHint: strings?.manualHint,
|
|
@@ -166,6 +168,7 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
166
168
|
processing,
|
|
167
169
|
hasRef: hasScanner,
|
|
168
170
|
rectangleDetected,
|
|
171
|
+
rectangleHint,
|
|
169
172
|
currentCaptureMode: captureModeRef.current,
|
|
170
173
|
captureInProgress: captureInProgressRef.current,
|
|
171
174
|
});
|
|
@@ -214,7 +217,7 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
214
217
|
emitError(error, 'Failed to capture image. Please try again.');
|
|
215
218
|
}
|
|
216
219
|
});
|
|
217
|
-
}, [processing, rectangleDetected, emitError]);
|
|
220
|
+
}, [processing, rectangleDetected, rectangleHint, emitError]);
|
|
218
221
|
const handleGalleryPick = (0, react_1.useCallback)(async () => {
|
|
219
222
|
console.log('[FullDocScanner] handleGalleryPick called');
|
|
220
223
|
if (processing || isGalleryOpen) {
|
|
@@ -266,11 +269,16 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
266
269
|
setCroppedImageData(null);
|
|
267
270
|
setProcessing(false);
|
|
268
271
|
setRectangleDetected(false);
|
|
272
|
+
setRectangleHint(false);
|
|
269
273
|
captureModeRef.current = null;
|
|
270
274
|
captureInProgressRef.current = false;
|
|
271
|
-
if (
|
|
272
|
-
clearTimeout(
|
|
273
|
-
|
|
275
|
+
if (rectangleCaptureTimeoutRef.current) {
|
|
276
|
+
clearTimeout(rectangleCaptureTimeoutRef.current);
|
|
277
|
+
rectangleCaptureTimeoutRef.current = null;
|
|
278
|
+
}
|
|
279
|
+
if (rectangleHintTimeoutRef.current) {
|
|
280
|
+
clearTimeout(rectangleHintTimeoutRef.current);
|
|
281
|
+
rectangleHintTimeoutRef.current = null;
|
|
274
282
|
}
|
|
275
283
|
// Reset DocScanner state
|
|
276
284
|
if (docScannerRef.current?.reset) {
|
|
@@ -281,47 +289,60 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
281
289
|
const stableCounter = event.stableCounter ?? 0;
|
|
282
290
|
const rectangleCoordinates = event.rectangleOnScreen ?? event.rectangleCoordinates;
|
|
283
291
|
const hasRectangle = Boolean(rectangleCoordinates);
|
|
284
|
-
const
|
|
285
|
-
const
|
|
286
|
-
if (
|
|
287
|
-
clearTimeout(
|
|
292
|
+
const captureReady = hasRectangle && event.lastDetectionType === 0 && stableCounter >= 1;
|
|
293
|
+
const scheduleClear = (ref, clearFn) => {
|
|
294
|
+
if (ref.current) {
|
|
295
|
+
clearTimeout(ref.current);
|
|
288
296
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
if (!prev) {
|
|
293
|
-
return prev;
|
|
294
|
-
}
|
|
295
|
-
console.log('[FullDocScanner] Rectangle timeout - clearing detection');
|
|
296
|
-
return false;
|
|
297
|
-
});
|
|
297
|
+
ref.current = setTimeout(() => {
|
|
298
|
+
ref.current = null;
|
|
299
|
+
clearFn();
|
|
298
300
|
}, 350);
|
|
299
301
|
};
|
|
300
|
-
if (
|
|
301
|
-
|
|
302
|
-
|
|
302
|
+
if (hasRectangle) {
|
|
303
|
+
scheduleClear(rectangleHintTimeoutRef, () => setRectangleHint(false));
|
|
304
|
+
setRectangleHint(true);
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
if (rectangleHintTimeoutRef.current) {
|
|
308
|
+
clearTimeout(rectangleHintTimeoutRef.current);
|
|
309
|
+
rectangleHintTimeoutRef.current = null;
|
|
310
|
+
}
|
|
311
|
+
setRectangleHint(false);
|
|
312
|
+
}
|
|
313
|
+
if (captureReady) {
|
|
314
|
+
scheduleClear(rectangleCaptureTimeoutRef, () => {
|
|
315
|
+
console.log('[FullDocScanner] Rectangle timeout - clearing detection');
|
|
316
|
+
setRectangleDetected(false);
|
|
317
|
+
});
|
|
318
|
+
setRectangleDetected(true);
|
|
303
319
|
}
|
|
304
320
|
else if (!hasRectangle) {
|
|
305
|
-
if (
|
|
306
|
-
clearTimeout(
|
|
307
|
-
|
|
321
|
+
if (rectangleCaptureTimeoutRef.current) {
|
|
322
|
+
clearTimeout(rectangleCaptureTimeoutRef.current);
|
|
323
|
+
rectangleCaptureTimeoutRef.current = null;
|
|
308
324
|
}
|
|
309
325
|
setRectangleDetected(false);
|
|
310
326
|
}
|
|
311
|
-
else {
|
|
312
|
-
|
|
313
|
-
|
|
327
|
+
else if (rectangleDetected) {
|
|
328
|
+
scheduleClear(rectangleCaptureTimeoutRef, () => {
|
|
329
|
+
console.log('[FullDocScanner] Rectangle timeout - clearing detection');
|
|
330
|
+
setRectangleDetected(false);
|
|
331
|
+
});
|
|
314
332
|
}
|
|
315
333
|
console.log('[FullDocScanner] Rectangle detection update', {
|
|
316
334
|
lastDetectionType: event.lastDetectionType,
|
|
317
335
|
stableCounter,
|
|
318
336
|
hasRectangle,
|
|
319
|
-
|
|
337
|
+
captureReady,
|
|
320
338
|
});
|
|
321
|
-
}, []);
|
|
339
|
+
}, [rectangleDetected]);
|
|
322
340
|
(0, react_1.useEffect)(() => () => {
|
|
323
|
-
if (
|
|
324
|
-
clearTimeout(
|
|
341
|
+
if (rectangleCaptureTimeoutRef.current) {
|
|
342
|
+
clearTimeout(rectangleCaptureTimeoutRef.current);
|
|
343
|
+
}
|
|
344
|
+
if (rectangleHintTimeoutRef.current) {
|
|
345
|
+
clearTimeout(rectangleHintTimeoutRef.current);
|
|
325
346
|
}
|
|
326
347
|
}, []);
|
|
327
348
|
return (react_1.default.createElement(react_native_1.View, { style: styles.container },
|
|
@@ -348,7 +369,7 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
348
369
|
react_1.default.createElement(react_native_1.TouchableOpacity, { style: [styles.shutterButton, processing && styles.buttonDisabled], onPress: triggerManualCapture, disabled: processing, accessibilityLabel: mergedStrings.manualHint, accessibilityRole: "button" },
|
|
349
370
|
react_1.default.createElement(react_native_1.View, { style: [
|
|
350
371
|
styles.shutterInner,
|
|
351
|
-
|
|
372
|
+
rectangleHint && { backgroundColor: overlayColor }
|
|
352
373
|
] })))))),
|
|
353
374
|
processing && (react_1.default.createElement(react_native_1.View, { style: styles.processingOverlay },
|
|
354
375
|
react_1.default.createElement(react_native_1.ActivityIndicator, { size: "large", color: overlayColor }),
|
package/package.json
CHANGED
package/src/FullDocScanner.tsx
CHANGED
|
@@ -86,11 +86,13 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
86
86
|
const [croppedImageData, setCroppedImageData] = useState<{path: string; base64?: string} | null>(null);
|
|
87
87
|
const [isGalleryOpen, setIsGalleryOpen] = useState(false);
|
|
88
88
|
const [rectangleDetected, setRectangleDetected] = useState(false);
|
|
89
|
+
const [rectangleHint, setRectangleHint] = useState(false);
|
|
89
90
|
const resolvedGridColor = gridColor ?? overlayColor;
|
|
90
91
|
const docScannerRef = useRef<DocScannerHandle | null>(null);
|
|
91
92
|
const captureModeRef = useRef<'grid' | 'no-grid' | null>(null);
|
|
92
93
|
const captureInProgressRef = useRef(false);
|
|
93
|
-
const
|
|
94
|
+
const rectangleCaptureTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
95
|
+
const rectangleHintTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
94
96
|
|
|
95
97
|
const mergedStrings = useMemo(
|
|
96
98
|
() => ({
|
|
@@ -226,6 +228,7 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
226
228
|
processing,
|
|
227
229
|
hasRef: hasScanner,
|
|
228
230
|
rectangleDetected,
|
|
231
|
+
rectangleHint,
|
|
229
232
|
currentCaptureMode: captureModeRef.current,
|
|
230
233
|
captureInProgress: captureInProgressRef.current,
|
|
231
234
|
});
|
|
@@ -288,7 +291,7 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
288
291
|
);
|
|
289
292
|
}
|
|
290
293
|
});
|
|
291
|
-
}, [processing, rectangleDetected, emitError]);
|
|
294
|
+
}, [processing, rectangleDetected, rectangleHint, emitError]);
|
|
292
295
|
|
|
293
296
|
const handleGalleryPick = useCallback(async () => {
|
|
294
297
|
console.log('[FullDocScanner] handleGalleryPick called');
|
|
@@ -353,11 +356,16 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
353
356
|
setCroppedImageData(null);
|
|
354
357
|
setProcessing(false);
|
|
355
358
|
setRectangleDetected(false);
|
|
359
|
+
setRectangleHint(false);
|
|
356
360
|
captureModeRef.current = null;
|
|
357
361
|
captureInProgressRef.current = false;
|
|
358
|
-
if (
|
|
359
|
-
clearTimeout(
|
|
360
|
-
|
|
362
|
+
if (rectangleCaptureTimeoutRef.current) {
|
|
363
|
+
clearTimeout(rectangleCaptureTimeoutRef.current);
|
|
364
|
+
rectangleCaptureTimeoutRef.current = null;
|
|
365
|
+
}
|
|
366
|
+
if (rectangleHintTimeoutRef.current) {
|
|
367
|
+
clearTimeout(rectangleHintTimeoutRef.current);
|
|
368
|
+
rectangleHintTimeoutRef.current = null;
|
|
361
369
|
}
|
|
362
370
|
// Reset DocScanner state
|
|
363
371
|
if (docScannerRef.current?.reset) {
|
|
@@ -369,52 +377,70 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
369
377
|
const stableCounter = event.stableCounter ?? 0;
|
|
370
378
|
const rectangleCoordinates = event.rectangleOnScreen ?? event.rectangleCoordinates;
|
|
371
379
|
const hasRectangle = Boolean(rectangleCoordinates);
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
|
|
380
|
+
const captureReady = hasRectangle && event.lastDetectionType === 0 && stableCounter >= 1;
|
|
381
|
+
|
|
382
|
+
const scheduleClear = (
|
|
383
|
+
ref: React.MutableRefObject<ReturnType<typeof setTimeout> | null>,
|
|
384
|
+
clearFn: () => void,
|
|
385
|
+
) => {
|
|
386
|
+
if (ref.current) {
|
|
387
|
+
clearTimeout(ref.current);
|
|
377
388
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
setRectangleDetected((prev) => {
|
|
382
|
-
if (!prev) {
|
|
383
|
-
return prev;
|
|
384
|
-
}
|
|
385
|
-
console.log('[FullDocScanner] Rectangle timeout - clearing detection');
|
|
386
|
-
return false;
|
|
387
|
-
});
|
|
389
|
+
ref.current = setTimeout(() => {
|
|
390
|
+
ref.current = null;
|
|
391
|
+
clearFn();
|
|
388
392
|
}, 350);
|
|
389
393
|
};
|
|
390
394
|
|
|
391
|
-
if (
|
|
392
|
-
|
|
393
|
-
|
|
395
|
+
if (hasRectangle) {
|
|
396
|
+
scheduleClear(rectangleHintTimeoutRef, () => setRectangleHint(false));
|
|
397
|
+
setRectangleHint(true);
|
|
398
|
+
} else {
|
|
399
|
+
if (rectangleHintTimeoutRef.current) {
|
|
400
|
+
clearTimeout(rectangleHintTimeoutRef.current);
|
|
401
|
+
rectangleHintTimeoutRef.current = null;
|
|
402
|
+
}
|
|
403
|
+
setRectangleHint(false);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (captureReady) {
|
|
407
|
+
scheduleClear(rectangleCaptureTimeoutRef, () => {
|
|
408
|
+
console.log('[FullDocScanner] Rectangle timeout - clearing detection');
|
|
409
|
+
setRectangleDetected(false);
|
|
410
|
+
});
|
|
411
|
+
setRectangleDetected(true);
|
|
394
412
|
} else if (!hasRectangle) {
|
|
395
|
-
if (
|
|
396
|
-
clearTimeout(
|
|
397
|
-
|
|
413
|
+
if (rectangleCaptureTimeoutRef.current) {
|
|
414
|
+
clearTimeout(rectangleCaptureTimeoutRef.current);
|
|
415
|
+
rectangleCaptureTimeoutRef.current = null;
|
|
398
416
|
}
|
|
399
417
|
setRectangleDetected(false);
|
|
400
|
-
} else {
|
|
401
|
-
|
|
402
|
-
|
|
418
|
+
} else if (rectangleDetected) {
|
|
419
|
+
scheduleClear(rectangleCaptureTimeoutRef, () => {
|
|
420
|
+
console.log('[FullDocScanner] Rectangle timeout - clearing detection');
|
|
421
|
+
setRectangleDetected(false);
|
|
422
|
+
});
|
|
403
423
|
}
|
|
404
424
|
|
|
405
425
|
console.log('[FullDocScanner] Rectangle detection update', {
|
|
406
426
|
lastDetectionType: event.lastDetectionType,
|
|
407
427
|
stableCounter,
|
|
408
428
|
hasRectangle,
|
|
409
|
-
|
|
429
|
+
captureReady,
|
|
410
430
|
});
|
|
411
|
-
}, []);
|
|
431
|
+
}, [rectangleDetected]);
|
|
412
432
|
|
|
413
|
-
useEffect(
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
433
|
+
useEffect(
|
|
434
|
+
() => () => {
|
|
435
|
+
if (rectangleCaptureTimeoutRef.current) {
|
|
436
|
+
clearTimeout(rectangleCaptureTimeoutRef.current);
|
|
437
|
+
}
|
|
438
|
+
if (rectangleHintTimeoutRef.current) {
|
|
439
|
+
clearTimeout(rectangleHintTimeoutRef.current);
|
|
440
|
+
}
|
|
441
|
+
},
|
|
442
|
+
[],
|
|
443
|
+
);
|
|
418
444
|
|
|
419
445
|
return (
|
|
420
446
|
<View style={styles.container}>
|
|
@@ -503,7 +529,7 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
503
529
|
>
|
|
504
530
|
<View style={[
|
|
505
531
|
styles.shutterInner,
|
|
506
|
-
|
|
532
|
+
rectangleHint && { backgroundColor: overlayColor }
|
|
507
533
|
]} />
|
|
508
534
|
</TouchableOpacity>
|
|
509
535
|
</View>
|
|
@@ -34,39 +34,18 @@ RCT_EXPORT_VIEW_PROPERTY(quality, float)
|
|
|
34
34
|
RCT_EXPORT_VIEW_PROPERTY(brightness, float)
|
|
35
35
|
RCT_EXPORT_VIEW_PROPERTY(contrast, float)
|
|
36
36
|
|
|
37
|
-
// Main capture method - returns a Promise
|
|
38
|
-
RCT_EXPORT_METHOD(capture:(
|
|
39
|
-
resolver:(RCTPromiseResolveBlock)resolve
|
|
37
|
+
// Main capture method - returns a Promise and uses the last mounted scanner view
|
|
38
|
+
RCT_EXPORT_METHOD(capture:(RCTPromiseResolveBlock)resolve
|
|
40
39
|
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
41
|
-
NSLog(@"[RNPdfScannerManager] capture
|
|
40
|
+
NSLog(@"[RNPdfScannerManager] capture requested (no reactTag)");
|
|
42
41
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
43
|
-
DocumentScannerView *targetView =
|
|
44
|
-
NSNumber *resolvedTag = ([reactTag isKindOfClass:[NSNumber class]]) ? (NSNumber *)reactTag : nil;
|
|
45
|
-
|
|
46
|
-
if (!resolvedTag && reactTag) {
|
|
47
|
-
NSLog(@"[RNPdfScannerManager] Unexpected reactTag type %@ - falling back to last known view", NSStringFromClass([reactTag class]));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (resolvedTag) {
|
|
51
|
-
UIView *view = [self.bridge.uiManager viewForReactTag:resolvedTag];
|
|
52
|
-
if ([view isKindOfClass:[DocumentScannerView class]]) {
|
|
53
|
-
targetView = (DocumentScannerView *)view;
|
|
54
|
-
self->_scannerView = targetView;
|
|
55
|
-
} else if (view) {
|
|
56
|
-
NSLog(@"[RNPdfScannerManager] View for tag %@ is not DocumentScannerView: %@", resolvedTag, NSStringFromClass(view.class));
|
|
57
|
-
} else {
|
|
58
|
-
NSLog(@"[RNPdfScannerManager] No view found for tag %@", resolvedTag);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (!targetView && self->_scannerView) {
|
|
63
|
-
NSLog(@"[RNPdfScannerManager] Falling back to last known scanner view");
|
|
64
|
-
targetView = self->_scannerView;
|
|
65
|
-
}
|
|
42
|
+
DocumentScannerView *targetView = self->_scannerView;
|
|
66
43
|
|
|
67
44
|
if (!targetView) {
|
|
68
|
-
NSLog(@"[RNPdfScannerManager] ERROR:
|
|
69
|
-
reject
|
|
45
|
+
NSLog(@"[RNPdfScannerManager] ERROR: Scanner view not yet ready for capture");
|
|
46
|
+
if (reject) {
|
|
47
|
+
reject(@"NO_VIEW", @"Document scanner view is not ready", nil);
|
|
48
|
+
}
|
|
70
49
|
return;
|
|
71
50
|
}
|
|
72
51
|
|
|
@@ -27,18 +27,15 @@ class PdfScanner extends React.Component {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
capture() {
|
|
30
|
-
console.log('[PdfScanner/ios.js] capture called
|
|
31
|
-
const handle = findNodeHandle(this.scannerRef.current);
|
|
32
|
-
console.log('[PdfScanner/ios.js] node handle (reactTag):', handle);
|
|
30
|
+
console.log('[PdfScanner/ios.js] capture called');
|
|
33
31
|
|
|
34
|
-
if (!
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
if (!this.scannerRef.current) {
|
|
33
|
+
const error = new Error('DocumentScanner native view is not ready');
|
|
34
|
+
console.error('[PdfScanner/ios.js] ERROR:', error.message);
|
|
35
|
+
return Promise.reject(error);
|
|
37
36
|
}
|
|
38
37
|
|
|
39
|
-
|
|
40
|
-
console.log('[PdfScanner/ios.js] Calling native capture with handle:', handle);
|
|
41
|
-
return NativeModules.RNPdfScannerManager.capture(handle);
|
|
38
|
+
return NativeModules.RNPdfScannerManager.capture();
|
|
42
39
|
}
|
|
43
40
|
|
|
44
41
|
render() {
|