react-native-rectangle-doc-scanner 3.60.0 → 3.62.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/DocScanner.js +34 -0
- package/dist/FullDocScanner.js +49 -29
- package/package.json +1 -1
- package/src/DocScanner.tsx +38 -0
- package/src/FullDocScanner.tsx +54 -32
package/dist/DocScanner.js
CHANGED
|
@@ -88,6 +88,13 @@ exports.DocScanner = (0, react_1.forwardRef)(({ onCapture, overlayColor = DEFAUL
|
|
|
88
88
|
return Math.min(100, Math.max(0, quality));
|
|
89
89
|
}, [quality]);
|
|
90
90
|
const handlePictureTaken = (0, react_1.useCallback)((event) => {
|
|
91
|
+
console.log('[DocScanner] handlePictureTaken called with event:', {
|
|
92
|
+
hasInitialImage: !!event.initialImage,
|
|
93
|
+
hasCroppedImage: !!event.croppedImage,
|
|
94
|
+
hasRectangleCoordinates: !!event.rectangleCoordinates,
|
|
95
|
+
width: event.width,
|
|
96
|
+
height: event.height,
|
|
97
|
+
});
|
|
91
98
|
setIsAutoCapturing(false);
|
|
92
99
|
const normalizedRectangle = normalizeRectangle(event.rectangleCoordinates ?? null) ?? lastRectangleRef.current;
|
|
93
100
|
const quad = normalizedRectangle ? (0, coordinate_1.rectangleToQuad)(normalizedRectangle) : null;
|
|
@@ -96,7 +103,15 @@ exports.DocScanner = (0, react_1.forwardRef)(({ onCapture, overlayColor = DEFAUL
|
|
|
96
103
|
const initialPath = event.initialImage ?? null;
|
|
97
104
|
const croppedPath = event.croppedImage ?? null;
|
|
98
105
|
const editablePath = initialPath ?? croppedPath;
|
|
106
|
+
console.log('[DocScanner] Processing captured image:', {
|
|
107
|
+
origin,
|
|
108
|
+
initialPath,
|
|
109
|
+
croppedPath,
|
|
110
|
+
editablePath,
|
|
111
|
+
hasQuad: !!quad,
|
|
112
|
+
});
|
|
99
113
|
if (editablePath) {
|
|
114
|
+
console.log('[DocScanner] Calling onCapture callback');
|
|
100
115
|
onCapture?.({
|
|
101
116
|
path: editablePath,
|
|
102
117
|
initialPath,
|
|
@@ -108,8 +123,12 @@ exports.DocScanner = (0, react_1.forwardRef)(({ onCapture, overlayColor = DEFAUL
|
|
|
108
123
|
origin,
|
|
109
124
|
});
|
|
110
125
|
}
|
|
126
|
+
else {
|
|
127
|
+
console.warn('[DocScanner] No editable path available, skipping onCapture');
|
|
128
|
+
}
|
|
111
129
|
setDetectedRectangle(null);
|
|
112
130
|
if (captureResolvers.current) {
|
|
131
|
+
console.log('[DocScanner] Resolving capture promise');
|
|
113
132
|
captureResolvers.current.resolve(event);
|
|
114
133
|
captureResolvers.current = null;
|
|
115
134
|
}
|
|
@@ -121,42 +140,57 @@ exports.DocScanner = (0, react_1.forwardRef)(({ onCapture, overlayColor = DEFAUL
|
|
|
121
140
|
}
|
|
122
141
|
}, []);
|
|
123
142
|
const capture = (0, react_1.useCallback)(() => {
|
|
143
|
+
console.log('[DocScanner] capture() called');
|
|
124
144
|
captureOriginRef.current = 'manual';
|
|
125
145
|
const instance = scannerRef.current;
|
|
126
146
|
if (!instance || typeof instance.capture !== 'function') {
|
|
147
|
+
console.error('[DocScanner] Native instance not ready:', {
|
|
148
|
+
hasInstance: !!instance,
|
|
149
|
+
hasCaptureMethod: instance ? typeof instance.capture : 'no instance',
|
|
150
|
+
});
|
|
127
151
|
captureOriginRef.current = 'auto';
|
|
128
152
|
return Promise.reject(new Error('DocumentScanner native instance is not ready'));
|
|
129
153
|
}
|
|
130
154
|
if (captureResolvers.current) {
|
|
155
|
+
console.warn('[DocScanner] Capture already in progress');
|
|
131
156
|
captureOriginRef.current = 'auto';
|
|
132
157
|
return Promise.reject(new Error('capture_in_progress'));
|
|
133
158
|
}
|
|
159
|
+
console.log('[DocScanner] Calling native capture method...');
|
|
134
160
|
let result;
|
|
135
161
|
try {
|
|
136
162
|
result = instance.capture();
|
|
163
|
+
console.log('[DocScanner] Native capture method called, result type:', typeof result, 'isPromise:', !!(result && typeof result.then === 'function'));
|
|
137
164
|
}
|
|
138
165
|
catch (error) {
|
|
166
|
+
console.error('[DocScanner] Native capture threw error:', error);
|
|
139
167
|
captureOriginRef.current = 'auto';
|
|
140
168
|
return Promise.reject(error);
|
|
141
169
|
}
|
|
142
170
|
if (result && typeof result.then === 'function') {
|
|
171
|
+
console.log('[DocScanner] Native returned a promise, waiting for resolution...');
|
|
143
172
|
return result
|
|
144
173
|
.then((payload) => {
|
|
174
|
+
console.log('[DocScanner] Native promise resolved with payload:', payload);
|
|
145
175
|
handlePictureTaken(payload);
|
|
146
176
|
return payload;
|
|
147
177
|
})
|
|
148
178
|
.catch((error) => {
|
|
179
|
+
console.error('[DocScanner] Native promise rejected:', error);
|
|
149
180
|
captureOriginRef.current = 'auto';
|
|
150
181
|
throw error;
|
|
151
182
|
});
|
|
152
183
|
}
|
|
184
|
+
console.log('[DocScanner] Native did not return a promise, using callback-based approach');
|
|
153
185
|
return new Promise((resolve, reject) => {
|
|
154
186
|
captureResolvers.current = {
|
|
155
187
|
resolve: (value) => {
|
|
188
|
+
console.log('[DocScanner] Callback resolver called with:', value);
|
|
156
189
|
captureOriginRef.current = 'auto';
|
|
157
190
|
resolve(value);
|
|
158
191
|
},
|
|
159
192
|
reject: (reason) => {
|
|
193
|
+
console.error('[DocScanner] Callback rejector called with:', reason);
|
|
160
194
|
captureOriginRef.current = 'auto';
|
|
161
195
|
reject(reason);
|
|
162
196
|
},
|
package/dist/FullDocScanner.js
CHANGED
|
@@ -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
|
-
//
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
190
|
-
|
|
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
|
-
|
|
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
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
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
package/src/DocScanner.tsx
CHANGED
|
@@ -150,6 +150,14 @@ export const DocScanner = forwardRef<DocScannerHandle, Props>(
|
|
|
150
150
|
|
|
151
151
|
const handlePictureTaken = useCallback(
|
|
152
152
|
(event: PictureEvent) => {
|
|
153
|
+
console.log('[DocScanner] handlePictureTaken called with event:', {
|
|
154
|
+
hasInitialImage: !!event.initialImage,
|
|
155
|
+
hasCroppedImage: !!event.croppedImage,
|
|
156
|
+
hasRectangleCoordinates: !!event.rectangleCoordinates,
|
|
157
|
+
width: event.width,
|
|
158
|
+
height: event.height,
|
|
159
|
+
});
|
|
160
|
+
|
|
153
161
|
setIsAutoCapturing(false);
|
|
154
162
|
|
|
155
163
|
const normalizedRectangle =
|
|
@@ -162,7 +170,16 @@ export const DocScanner = forwardRef<DocScannerHandle, Props>(
|
|
|
162
170
|
const croppedPath = event.croppedImage ?? null;
|
|
163
171
|
const editablePath = initialPath ?? croppedPath;
|
|
164
172
|
|
|
173
|
+
console.log('[DocScanner] Processing captured image:', {
|
|
174
|
+
origin,
|
|
175
|
+
initialPath,
|
|
176
|
+
croppedPath,
|
|
177
|
+
editablePath,
|
|
178
|
+
hasQuad: !!quad,
|
|
179
|
+
});
|
|
180
|
+
|
|
165
181
|
if (editablePath) {
|
|
182
|
+
console.log('[DocScanner] Calling onCapture callback');
|
|
166
183
|
onCapture?.({
|
|
167
184
|
path: editablePath,
|
|
168
185
|
initialPath,
|
|
@@ -173,11 +190,14 @@ export const DocScanner = forwardRef<DocScannerHandle, Props>(
|
|
|
173
190
|
height: event.height ?? 0,
|
|
174
191
|
origin,
|
|
175
192
|
});
|
|
193
|
+
} else {
|
|
194
|
+
console.warn('[DocScanner] No editable path available, skipping onCapture');
|
|
176
195
|
}
|
|
177
196
|
|
|
178
197
|
setDetectedRectangle(null);
|
|
179
198
|
|
|
180
199
|
if (captureResolvers.current) {
|
|
200
|
+
console.log('[DocScanner] Resolving capture promise');
|
|
181
201
|
captureResolvers.current.resolve(event);
|
|
182
202
|
captureResolvers.current = null;
|
|
183
203
|
}
|
|
@@ -193,43 +213,61 @@ export const DocScanner = forwardRef<DocScannerHandle, Props>(
|
|
|
193
213
|
}, []);
|
|
194
214
|
|
|
195
215
|
const capture = useCallback((): Promise<PictureEvent> => {
|
|
216
|
+
console.log('[DocScanner] capture() called');
|
|
196
217
|
captureOriginRef.current = 'manual';
|
|
197
218
|
const instance = scannerRef.current;
|
|
219
|
+
|
|
198
220
|
if (!instance || typeof instance.capture !== 'function') {
|
|
221
|
+
console.error('[DocScanner] Native instance not ready:', {
|
|
222
|
+
hasInstance: !!instance,
|
|
223
|
+
hasCaptureMethod: instance ? typeof instance.capture : 'no instance',
|
|
224
|
+
});
|
|
199
225
|
captureOriginRef.current = 'auto';
|
|
200
226
|
return Promise.reject(new Error('DocumentScanner native instance is not ready'));
|
|
201
227
|
}
|
|
228
|
+
|
|
202
229
|
if (captureResolvers.current) {
|
|
230
|
+
console.warn('[DocScanner] Capture already in progress');
|
|
203
231
|
captureOriginRef.current = 'auto';
|
|
204
232
|
return Promise.reject(new Error('capture_in_progress'));
|
|
205
233
|
}
|
|
206
234
|
|
|
235
|
+
console.log('[DocScanner] Calling native capture method...');
|
|
207
236
|
let result: any;
|
|
208
237
|
try {
|
|
209
238
|
result = instance.capture();
|
|
239
|
+
console.log('[DocScanner] Native capture method called, result type:', typeof result, 'isPromise:', !!(result && typeof result.then === 'function'));
|
|
210
240
|
} catch (error) {
|
|
241
|
+
console.error('[DocScanner] Native capture threw error:', error);
|
|
211
242
|
captureOriginRef.current = 'auto';
|
|
212
243
|
return Promise.reject(error);
|
|
213
244
|
}
|
|
245
|
+
|
|
214
246
|
if (result && typeof result.then === 'function') {
|
|
247
|
+
console.log('[DocScanner] Native returned a promise, waiting for resolution...');
|
|
215
248
|
return result
|
|
216
249
|
.then((payload: PictureEvent) => {
|
|
250
|
+
console.log('[DocScanner] Native promise resolved with payload:', payload);
|
|
217
251
|
handlePictureTaken(payload);
|
|
218
252
|
return payload;
|
|
219
253
|
})
|
|
220
254
|
.catch((error: unknown) => {
|
|
255
|
+
console.error('[DocScanner] Native promise rejected:', error);
|
|
221
256
|
captureOriginRef.current = 'auto';
|
|
222
257
|
throw error;
|
|
223
258
|
});
|
|
224
259
|
}
|
|
225
260
|
|
|
261
|
+
console.log('[DocScanner] Native did not return a promise, using callback-based approach');
|
|
226
262
|
return new Promise<PictureEvent>((resolve, reject) => {
|
|
227
263
|
captureResolvers.current = {
|
|
228
264
|
resolve: (value) => {
|
|
265
|
+
console.log('[DocScanner] Callback resolver called with:', value);
|
|
229
266
|
captureOriginRef.current = 'auto';
|
|
230
267
|
resolve(value);
|
|
231
268
|
},
|
|
232
269
|
reject: (reason) => {
|
|
270
|
+
console.error('[DocScanner] Callback rejector called with:', reason);
|
|
233
271
|
captureOriginRef.current = 'auto';
|
|
234
272
|
reject(reason);
|
|
235
273
|
},
|
package/src/FullDocScanner.tsx
CHANGED
|
@@ -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
|
-
//
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
130
|
-
|
|
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
|
-
|
|
259
|
-
|
|
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
|
-
|
|
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
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
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
|
|