react-native-rectangle-doc-scanner 3.60.0 → 3.61.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.
@@ -83,12 +83,15 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
83
83
  try {
84
84
  console.log('[FullDocScanner] openCropper called with path:', imagePath);
85
85
  setProcessing(true);
86
- // Normalize path - ensure file:// prefix for iOS
87
- const normalizedPath = imagePath.startsWith('file://') ? imagePath : `file://${imagePath}`;
88
- console.log('[FullDocScanner] Normalized path:', normalizedPath);
89
- // Add timeout to prevent hanging
90
- const cropperPromise = react_native_image_crop_picker_1.default.openCropper({
91
- path: normalizedPath,
86
+ // Clean path - remove file:// prefix for react-native-image-crop-picker
87
+ // The library handles the prefix internally and double prefixing causes issues
88
+ let cleanPath = imagePath;
89
+ if (cleanPath.startsWith('file://')) {
90
+ cleanPath = cleanPath.replace('file://', '');
91
+ }
92
+ console.log('[FullDocScanner] Clean path for cropper:', cleanPath);
93
+ const croppedImage = await react_native_image_crop_picker_1.default.openCropper({
94
+ path: cleanPath,
92
95
  mediaType: 'photo',
93
96
  width: cropWidth,
94
97
  height: cropHeight,
@@ -98,10 +101,6 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
98
101
  includeBase64: true,
99
102
  compressImageQuality: 0.9,
100
103
  });
101
- const timeoutPromise = new Promise((_, reject) => {
102
- setTimeout(() => reject(new Error('Cropper timeout after 30 seconds')), 30000);
103
- });
104
- const croppedImage = await Promise.race([cropperPromise, timeoutPromise]);
105
104
  console.log('[FullDocScanner] Cropper returned:', {
106
105
  path: croppedImage.path,
107
106
  hasBase64: !!croppedImage.data,
@@ -134,14 +133,16 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
134
133
  croppedPath: document.croppedPath,
135
134
  initialPath: document.initialPath,
136
135
  captureMode: captureModeRef.current,
136
+ captureInProgress: captureInProgressRef.current,
137
137
  });
138
- captureInProgressRef.current = false;
139
138
  const captureMode = captureModeRef.current;
139
+ // Reset capture state
140
+ captureInProgressRef.current = false;
141
+ captureModeRef.current = null;
140
142
  if (!captureMode) {
141
143
  console.warn('[FullDocScanner] Missing capture mode for capture result, ignoring');
142
144
  return;
143
145
  }
144
- captureModeRef.current = null;
145
146
  const normalizedDoc = normalizeCapturedDocument(document);
146
147
  if (captureMode === 'no-grid') {
147
148
  console.log('[FullDocScanner] No grid at capture button press: opening cropper for manual selection');
@@ -184,12 +185,27 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
184
185
  const captureMode = rectangleDetected ? 'grid' : 'no-grid';
185
186
  captureModeRef.current = captureMode;
186
187
  captureInProgressRef.current = true;
188
+ // Add timeout to reset state if capture hangs
189
+ const captureTimeout = setTimeout(() => {
190
+ if (captureInProgressRef.current) {
191
+ console.error('[FullDocScanner] Capture timeout - resetting state');
192
+ captureModeRef.current = null;
193
+ captureInProgressRef.current = false;
194
+ emitError(new Error('Capture timeout'), 'Capture timed out. Please try again.');
195
+ }
196
+ }, 10000);
187
197
  scannerInstance.capture()
188
198
  .then((result) => {
189
- console.log('[FullDocScanner] Manual capture success:', result);
190
- captureInProgressRef.current = false;
199
+ clearTimeout(captureTimeout);
200
+ console.log('[FullDocScanner] Manual capture promise resolved:', {
201
+ hasResult: !!result,
202
+ croppedImage: result?.croppedImage,
203
+ initialImage: result?.initialImage,
204
+ });
205
+ // Note: captureInProgressRef is reset in handleCapture
191
206
  })
192
207
  .catch((error) => {
208
+ clearTimeout(captureTimeout);
193
209
  const errorMessage = error instanceof Error ? error.message : String(error);
194
210
  console.error('[FullDocScanner] Manual capture failed:', errorMessage, error);
195
211
  captureModeRef.current = null;
@@ -265,29 +281,33 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
265
281
  const stableCounter = event.stableCounter ?? 0;
266
282
  const hasRectangle = Boolean(event.rectangleOnScreen ?? event.rectangleCoordinates);
267
283
  const isGoodRectangle = hasRectangle && event.lastDetectionType === 0;
268
- if (hasRectangle) {
284
+ // Clear timeout immediately when rectangle is lost
285
+ if (!hasRectangle || !isGoodRectangle) {
286
+ if (rectangleTimeoutRef.current) {
287
+ clearTimeout(rectangleTimeoutRef.current);
288
+ rectangleTimeoutRef.current = null;
289
+ }
290
+ setRectangleDetected(false);
291
+ }
292
+ else {
293
+ // Rectangle detected - clear any existing timeout
269
294
  if (rectangleTimeoutRef.current) {
270
295
  clearTimeout(rectangleTimeoutRef.current);
271
296
  }
297
+ setRectangleDetected(true);
298
+ // Set timeout to clear rectangle after brief period of no updates
272
299
  rectangleTimeoutRef.current = setTimeout(() => {
273
300
  rectangleTimeoutRef.current = null;
274
301
  setRectangleDetected(false);
302
+ console.log('[FullDocScanner] Rectangle timeout - clearing detection');
275
303
  }, 300);
276
304
  }
277
- else if (rectangleTimeoutRef.current) {
278
- clearTimeout(rectangleTimeoutRef.current);
279
- rectangleTimeoutRef.current = null;
280
- }
281
- setRectangleDetected((prev) => {
282
- if (prev !== isGoodRectangle) {
283
- console.log('[FullDocScanner] Rectangle detection update', {
284
- lastDetectionType: event.lastDetectionType,
285
- stableCounter,
286
- hasRectangle,
287
- isGoodRectangle,
288
- });
289
- }
290
- return isGoodRectangle;
305
+ console.log('[FullDocScanner] Rectangle detection update', {
306
+ lastDetectionType: event.lastDetectionType,
307
+ stableCounter,
308
+ hasRectangle,
309
+ isGoodRectangle,
310
+ rectangleDetected: isGoodRectangle,
291
311
  });
292
312
  }, []);
293
313
  (0, react_1.useEffect)(() => () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-rectangle-doc-scanner",
3
- "version": "3.60.0",
3
+ "version": "3.61.0",
4
4
  "description": "Native-backed document scanner for React Native with customizable overlays.",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -122,13 +122,16 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
122
122
  console.log('[FullDocScanner] openCropper called with path:', imagePath);
123
123
  setProcessing(true);
124
124
 
125
- // Normalize path - ensure file:// prefix for iOS
126
- const normalizedPath = imagePath.startsWith('file://') ? imagePath : `file://${imagePath}`;
127
- console.log('[FullDocScanner] Normalized path:', normalizedPath);
125
+ // Clean path - remove file:// prefix for react-native-image-crop-picker
126
+ // The library handles the prefix internally and double prefixing causes issues
127
+ let cleanPath = imagePath;
128
+ if (cleanPath.startsWith('file://')) {
129
+ cleanPath = cleanPath.replace('file://', '');
130
+ }
131
+ console.log('[FullDocScanner] Clean path for cropper:', cleanPath);
128
132
 
129
- // Add timeout to prevent hanging
130
- const cropperPromise = ImageCropPicker.openCropper({
131
- path: normalizedPath,
133
+ const croppedImage = await ImageCropPicker.openCropper({
134
+ path: cleanPath,
132
135
  mediaType: 'photo',
133
136
  width: cropWidth,
134
137
  height: cropHeight,
@@ -139,12 +142,6 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
139
142
  compressImageQuality: 0.9,
140
143
  });
141
144
 
142
- const timeoutPromise = new Promise((_, reject) => {
143
- setTimeout(() => reject(new Error('Cropper timeout after 30 seconds')), 30000);
144
- });
145
-
146
- const croppedImage = await Promise.race([cropperPromise, timeoutPromise]) as any;
147
-
148
145
  console.log('[FullDocScanner] Cropper returned:', {
149
146
  path: croppedImage.path,
150
147
  hasBase64: !!croppedImage.data,
@@ -186,19 +183,20 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
186
183
  croppedPath: document.croppedPath,
187
184
  initialPath: document.initialPath,
188
185
  captureMode: captureModeRef.current,
186
+ captureInProgress: captureInProgressRef.current,
189
187
  });
190
188
 
191
- captureInProgressRef.current = false;
192
-
193
189
  const captureMode = captureModeRef.current;
194
190
 
191
+ // Reset capture state
192
+ captureInProgressRef.current = false;
193
+ captureModeRef.current = null;
194
+
195
195
  if (!captureMode) {
196
196
  console.warn('[FullDocScanner] Missing capture mode for capture result, ignoring');
197
197
  return;
198
198
  }
199
199
 
200
- captureModeRef.current = null;
201
-
202
200
  const normalizedDoc = normalizeCapturedDocument(document);
203
201
 
204
202
  if (captureMode === 'no-grid') {
@@ -253,12 +251,31 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
253
251
  captureModeRef.current = captureMode;
254
252
  captureInProgressRef.current = true;
255
253
 
254
+ // Add timeout to reset state if capture hangs
255
+ const captureTimeout = setTimeout(() => {
256
+ if (captureInProgressRef.current) {
257
+ console.error('[FullDocScanner] Capture timeout - resetting state');
258
+ captureModeRef.current = null;
259
+ captureInProgressRef.current = false;
260
+ emitError(
261
+ new Error('Capture timeout'),
262
+ 'Capture timed out. Please try again.',
263
+ );
264
+ }
265
+ }, 10000);
266
+
256
267
  scannerInstance.capture()
257
268
  .then((result) => {
258
- console.log('[FullDocScanner] Manual capture success:', result);
259
- captureInProgressRef.current = false;
269
+ clearTimeout(captureTimeout);
270
+ console.log('[FullDocScanner] Manual capture promise resolved:', {
271
+ hasResult: !!result,
272
+ croppedImage: result?.croppedImage,
273
+ initialImage: result?.initialImage,
274
+ });
275
+ // Note: captureInProgressRef is reset in handleCapture
260
276
  })
261
277
  .catch((error: unknown) => {
278
+ clearTimeout(captureTimeout);
262
279
  const errorMessage = error instanceof Error ? error.message : String(error);
263
280
  console.error('[FullDocScanner] Manual capture failed:', errorMessage, error);
264
281
  captureModeRef.current = null;
@@ -353,29 +370,34 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
353
370
  const hasRectangle = Boolean(event.rectangleOnScreen ?? event.rectangleCoordinates);
354
371
  const isGoodRectangle = hasRectangle && event.lastDetectionType === 0;
355
372
 
356
- if (hasRectangle) {
373
+ // Clear timeout immediately when rectangle is lost
374
+ if (!hasRectangle || !isGoodRectangle) {
357
375
  if (rectangleTimeoutRef.current) {
358
376
  clearTimeout(rectangleTimeoutRef.current);
377
+ rectangleTimeoutRef.current = null;
359
378
  }
379
+ setRectangleDetected(false);
380
+ } else {
381
+ // Rectangle detected - clear any existing timeout
382
+ if (rectangleTimeoutRef.current) {
383
+ clearTimeout(rectangleTimeoutRef.current);
384
+ }
385
+ setRectangleDetected(true);
386
+
387
+ // Set timeout to clear rectangle after brief period of no updates
360
388
  rectangleTimeoutRef.current = setTimeout(() => {
361
389
  rectangleTimeoutRef.current = null;
362
390
  setRectangleDetected(false);
391
+ console.log('[FullDocScanner] Rectangle timeout - clearing detection');
363
392
  }, 300);
364
- } else if (rectangleTimeoutRef.current) {
365
- clearTimeout(rectangleTimeoutRef.current);
366
- rectangleTimeoutRef.current = null;
367
393
  }
368
394
 
369
- setRectangleDetected((prev) => {
370
- if (prev !== isGoodRectangle) {
371
- console.log('[FullDocScanner] Rectangle detection update', {
372
- lastDetectionType: event.lastDetectionType,
373
- stableCounter,
374
- hasRectangle,
375
- isGoodRectangle,
376
- });
377
- }
378
- return isGoodRectangle;
395
+ console.log('[FullDocScanner] Rectangle detection update', {
396
+ lastDetectionType: event.lastDetectionType,
397
+ stableCounter,
398
+ hasRectangle,
399
+ isGoodRectangle,
400
+ rectangleDetected: isGoodRectangle,
379
401
  });
380
402
  }, []);
381
403