react-native-rectangle-doc-scanner 3.58.0 → 3.60.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.
@@ -167,7 +167,8 @@ exports.DocScanner = (0, react_1.forwardRef)(({ onCapture, overlayColor = DEFAUL
167
167
  captureOriginRef.current = 'manual';
168
168
  capture().catch((error) => {
169
169
  captureOriginRef.current = 'auto';
170
- console.warn('[DocScanner] manual capture failed', error);
170
+ console.error('[DocScanner] manual capture failed', error);
171
+ throw error;
171
172
  });
172
173
  }, [capture]);
173
174
  const handleRectangleDetect = (0, react_1.useCallback)((event) => {
@@ -83,8 +83,12 @@ 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
- const croppedImage = await react_native_image_crop_picker_1.default.openCropper({
87
- path: imagePath,
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,
88
92
  mediaType: 'photo',
89
93
  width: cropWidth,
90
94
  height: cropHeight,
@@ -94,12 +98,16 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
94
98
  includeBase64: true,
95
99
  compressImageQuality: 0.9,
96
100
  });
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]);
97
105
  console.log('[FullDocScanner] Cropper returned:', {
98
106
  path: croppedImage.path,
99
107
  hasBase64: !!croppedImage.data,
100
108
  });
101
109
  setProcessing(false);
102
- // Show check_DP confirmation screen
110
+ // Show confirmation screen
103
111
  setCroppedImageData({
104
112
  path: croppedImage.path,
105
113
  base64: croppedImage.data ?? undefined,
@@ -108,11 +116,14 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
108
116
  catch (error) {
109
117
  console.error('[FullDocScanner] openCropper error:', error);
110
118
  setProcessing(false);
111
- if (error?.message !== 'User cancelled image selection') {
112
- emitError(error instanceof Error ? error : new Error(String(error)), 'Failed to crop image.');
119
+ const errorMessage = error?.message || String(error);
120
+ if (errorMessage === 'User cancelled image selection' ||
121
+ errorMessage.includes('cancelled') ||
122
+ errorMessage.includes('cancel')) {
123
+ console.log('[FullDocScanner] User cancelled cropper');
113
124
  }
114
125
  else {
115
- console.log('[FullDocScanner] User cancelled cropper');
126
+ emitError(error instanceof Error ? error : new Error(errorMessage), 'Failed to crop image. Please try again.');
116
127
  }
117
128
  }
118
129
  }, [cropWidth, cropHeight, emitError]);
@@ -125,14 +136,9 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
125
136
  captureMode: captureModeRef.current,
126
137
  });
127
138
  captureInProgressRef.current = false;
128
- if (document.origin === 'auto') {
129
- console.log('[FullDocScanner] Ignoring auto capture result');
130
- return;
131
- }
132
139
  const captureMode = captureModeRef.current;
133
140
  if (!captureMode) {
134
- console.warn('[FullDocScanner] Missing capture mode for manual capture result');
135
- captureModeRef.current = null;
141
+ console.warn('[FullDocScanner] Missing capture mode for capture result, ignoring');
136
142
  return;
137
143
  }
138
144
  captureModeRef.current = null;
@@ -184,29 +190,37 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
184
190
  captureInProgressRef.current = false;
185
191
  })
186
192
  .catch((error) => {
193
+ const errorMessage = error instanceof Error ? error.message : String(error);
194
+ console.error('[FullDocScanner] Manual capture failed:', errorMessage, error);
187
195
  captureModeRef.current = null;
188
196
  captureInProgressRef.current = false;
189
- console.error('[FullDocScanner] Manual capture failed:', error);
190
197
  if (error instanceof Error && error.message !== 'capture_in_progress') {
191
- emitError(error, 'Failed to capture image.');
198
+ emitError(error, 'Failed to capture image. Please try again.');
192
199
  }
193
200
  });
194
201
  }, [processing, rectangleDetected, emitError]);
195
202
  const handleGalleryPick = (0, react_1.useCallback)(async () => {
196
203
  console.log('[FullDocScanner] handleGalleryPick called');
197
204
  if (processing || isGalleryOpen) {
205
+ console.log('[FullDocScanner] Skipping gallery pick - already processing:', { processing, isGalleryOpen });
198
206
  return;
199
207
  }
200
208
  try {
201
209
  setIsGalleryOpen(true);
210
+ console.log('[FullDocScanner] Launching image library...');
202
211
  const result = await (0, react_native_image_picker_1.launchImageLibrary)({
203
212
  mediaType: 'photo',
204
213
  quality: 1,
205
214
  selectionLimit: 1,
206
215
  });
216
+ console.log('[FullDocScanner] Image library result:', {
217
+ didCancel: result.didCancel,
218
+ hasAssets: !!result.assets,
219
+ assetsLength: result.assets?.length,
220
+ });
207
221
  setIsGalleryOpen(false);
208
222
  if (result.didCancel || !result.assets?.[0]?.uri) {
209
- console.log('[FullDocScanner] User cancelled gallery picker');
223
+ console.log('[FullDocScanner] User cancelled gallery picker or no image selected');
210
224
  return;
211
225
  }
212
226
  const imageUri = result.assets[0].uri;
@@ -215,6 +229,7 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
215
229
  await openCropper(imageUri);
216
230
  }
217
231
  catch (error) {
232
+ console.error('[FullDocScanner] Gallery pick error:', error);
218
233
  setIsGalleryOpen(false);
219
234
  emitError(error instanceof Error ? error : new Error(String(error)), 'Failed to pick image from gallery.');
220
235
  }
@@ -290,7 +305,7 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
290
305
  react_1.default.createElement(react_native_1.Text, { style: styles.confirmButtonText }, mergedStrings.retake)),
291
306
  react_1.default.createElement(react_native_1.TouchableOpacity, { style: [styles.confirmButton, styles.confirmButtonPrimary], onPress: handleConfirm, accessibilityLabel: mergedStrings.confirm, accessibilityRole: "button" },
292
307
  react_1.default.createElement(react_native_1.Text, { style: styles.confirmButtonText }, mergedStrings.confirm))))) : (react_1.default.createElement(react_native_1.View, { style: styles.flex },
293
- react_1.default.createElement(DocScanner_1.DocScanner, { ref: docScannerRef, autoCapture: true, overlayColor: overlayColor, showGrid: showGrid, gridColor: resolvedGridColor, gridLineWidth: gridLineWidth, minStableFrames: minStableFrames ?? 6, detectionConfig: detectionConfig, onCapture: handleCapture, onRectangleDetect: handleRectangleDetect, showManualCaptureButton: false },
308
+ 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 },
294
309
  react_1.default.createElement(react_native_1.View, { style: styles.overlayTop, pointerEvents: "box-none" },
295
310
  react_1.default.createElement(react_native_1.TouchableOpacity, { style: styles.closeButton, onPress: handleClose, accessibilityLabel: mergedStrings.cancel, accessibilityRole: "button" },
296
311
  react_1.default.createElement(react_native_1.Text, { style: styles.closeButtonLabel }, "\u00D7"))),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-rectangle-doc-scanner",
3
- "version": "3.58.0",
3
+ "version": "3.60.0",
4
4
  "description": "Native-backed document scanner for React Native with customizable overlays.",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -241,7 +241,8 @@ export const DocScanner = forwardRef<DocScannerHandle, Props>(
241
241
  captureOriginRef.current = 'manual';
242
242
  capture().catch((error) => {
243
243
  captureOriginRef.current = 'auto';
244
- console.warn('[DocScanner] manual capture failed', error);
244
+ console.error('[DocScanner] manual capture failed', error);
245
+ throw error;
245
246
  });
246
247
  }, [capture]);
247
248
 
@@ -121,8 +121,14 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
121
121
  try {
122
122
  console.log('[FullDocScanner] openCropper called with path:', imagePath);
123
123
  setProcessing(true);
124
- const croppedImage = await ImageCropPicker.openCropper({
125
- path: imagePath,
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);
128
+
129
+ // Add timeout to prevent hanging
130
+ const cropperPromise = ImageCropPicker.openCropper({
131
+ path: normalizedPath,
126
132
  mediaType: 'photo',
127
133
  width: cropWidth,
128
134
  height: cropHeight,
@@ -133,6 +139,12 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
133
139
  compressImageQuality: 0.9,
134
140
  });
135
141
 
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
+
136
148
  console.log('[FullDocScanner] Cropper returned:', {
137
149
  path: croppedImage.path,
138
150
  hasBase64: !!croppedImage.data,
@@ -140,7 +152,7 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
140
152
 
141
153
  setProcessing(false);
142
154
 
143
- // Show check_DP confirmation screen
155
+ // Show confirmation screen
144
156
  setCroppedImageData({
145
157
  path: croppedImage.path,
146
158
  base64: croppedImage.data ?? undefined,
@@ -148,13 +160,18 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
148
160
  } catch (error) {
149
161
  console.error('[FullDocScanner] openCropper error:', error);
150
162
  setProcessing(false);
151
- if ((error as any)?.message !== 'User cancelled image selection') {
163
+
164
+ const errorMessage = (error as any)?.message || String(error);
165
+
166
+ if (errorMessage === 'User cancelled image selection' ||
167
+ errorMessage.includes('cancelled') ||
168
+ errorMessage.includes('cancel')) {
169
+ console.log('[FullDocScanner] User cancelled cropper');
170
+ } else {
152
171
  emitError(
153
- error instanceof Error ? error : new Error(String(error)),
154
- 'Failed to crop image.',
172
+ error instanceof Error ? error : new Error(errorMessage),
173
+ 'Failed to crop image. Please try again.',
155
174
  );
156
- } else {
157
- console.log('[FullDocScanner] User cancelled cropper');
158
175
  }
159
176
  }
160
177
  },
@@ -173,16 +190,10 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
173
190
 
174
191
  captureInProgressRef.current = false;
175
192
 
176
- if (document.origin === 'auto') {
177
- console.log('[FullDocScanner] Ignoring auto capture result');
178
- return;
179
- }
180
-
181
193
  const captureMode = captureModeRef.current;
182
194
 
183
195
  if (!captureMode) {
184
- console.warn('[FullDocScanner] Missing capture mode for manual capture result');
185
- captureModeRef.current = null;
196
+ console.warn('[FullDocScanner] Missing capture mode for capture result, ignoring');
186
197
  return;
187
198
  }
188
199
 
@@ -248,13 +259,15 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
248
259
  captureInProgressRef.current = false;
249
260
  })
250
261
  .catch((error: unknown) => {
262
+ const errorMessage = error instanceof Error ? error.message : String(error);
263
+ console.error('[FullDocScanner] Manual capture failed:', errorMessage, error);
251
264
  captureModeRef.current = null;
252
265
  captureInProgressRef.current = false;
253
- console.error('[FullDocScanner] Manual capture failed:', error);
266
+
254
267
  if (error instanceof Error && error.message !== 'capture_in_progress') {
255
268
  emitError(
256
269
  error,
257
- 'Failed to capture image.',
270
+ 'Failed to capture image. Please try again.',
258
271
  );
259
272
  }
260
273
  });
@@ -263,21 +276,30 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
263
276
  const handleGalleryPick = useCallback(async () => {
264
277
  console.log('[FullDocScanner] handleGalleryPick called');
265
278
  if (processing || isGalleryOpen) {
279
+ console.log('[FullDocScanner] Skipping gallery pick - already processing:', { processing, isGalleryOpen });
266
280
  return;
267
281
  }
268
282
 
269
283
  try {
270
284
  setIsGalleryOpen(true);
285
+ console.log('[FullDocScanner] Launching image library...');
286
+
271
287
  const result = await launchImageLibrary({
272
288
  mediaType: 'photo',
273
289
  quality: 1,
274
290
  selectionLimit: 1,
275
291
  });
276
292
 
293
+ console.log('[FullDocScanner] Image library result:', {
294
+ didCancel: result.didCancel,
295
+ hasAssets: !!result.assets,
296
+ assetsLength: result.assets?.length,
297
+ });
298
+
277
299
  setIsGalleryOpen(false);
278
300
 
279
301
  if (result.didCancel || !result.assets?.[0]?.uri) {
280
- console.log('[FullDocScanner] User cancelled gallery picker');
302
+ console.log('[FullDocScanner] User cancelled gallery picker or no image selected');
281
303
  return;
282
304
  }
283
305
 
@@ -287,6 +309,7 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
287
309
  // Open cropper with the selected image
288
310
  await openCropper(imageUri);
289
311
  } catch (error) {
312
+ console.error('[FullDocScanner] Gallery pick error:', error);
290
313
  setIsGalleryOpen(false);
291
314
  emitError(
292
315
  error instanceof Error ? error : new Error(String(error)),
@@ -395,7 +418,7 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
395
418
  <View style={styles.flex}>
396
419
  <DocScanner
397
420
  ref={docScannerRef}
398
- autoCapture={true}
421
+ autoCapture={false}
399
422
  overlayColor={overlayColor}
400
423
  showGrid={showGrid}
401
424
  gridColor={resolvedGridColor}
@@ -12,6 +12,8 @@
12
12
  [self setEnableBorderDetection:YES];
13
13
  [self setDelegate: self];
14
14
  _hasSetupCamera = NO;
15
+ self.manualOnly = YES;
16
+ self.detectionCountBeforeCapture = NSIntegerMax;
15
17
  }
16
18
 
17
19
  return self;