react-native-expo-cropper 1.2.46 → 1.2.47

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.
@@ -45,22 +45,28 @@ function CustomCamera(_ref) {
45
45
  _useState6 = _slicedToArray(_useState5, 2),
46
46
  hasPermission = _useState6[0],
47
47
  setHasPermission = _useState6[1];
48
+ // Force a fresh native CameraView instance per "open" to avoid stale/cached preview.
49
+ var _useState7 = (0, _react.useState)(function () {
50
+ return "".concat(Date.now(), "-").concat(Math.random().toString(16).slice(2));
51
+ }),
52
+ _useState8 = _slicedToArray(_useState7, 1),
53
+ cameraViewMountKey = _useState8[0];
48
54
  var cameraRef = (0, _react.useRef)(null);
49
55
  var cameraWrapperRef = (0, _react.useRef)(null);
50
56
  var insets = (0, _reactNativeSafeAreaContext.useSafeAreaInsets)();
51
- var _useState7 = (0, _react.useState)({
57
+ var _useState9 = (0, _react.useState)({
52
58
  width: 0,
53
59
  height: 0,
54
60
  x: 0,
55
61
  y: 0
56
62
  }),
57
- _useState8 = _slicedToArray(_useState7, 2),
58
- cameraWrapperLayout = _useState8[0],
59
- setCameraWrapperLayout = _useState8[1];
60
- var _useState9 = (0, _react.useState)(null),
61
63
  _useState0 = _slicedToArray(_useState9, 2),
62
- greenFrame = _useState0[0],
63
- setGreenFrame = _useState0[1];
64
+ cameraWrapperLayout = _useState0[0],
65
+ setCameraWrapperLayout = _useState0[1];
66
+ var _useState1 = (0, _react.useState)(null),
67
+ _useState10 = _slicedToArray(_useState1, 2),
68
+ greenFrame = _useState10[0],
69
+ setGreenFrame = _useState10[1];
64
70
  (0, _react.useEffect)(function () {
65
71
  _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
66
72
  var _yield$Camera$request, status;
@@ -267,6 +273,7 @@ function CustomCamera(_ref) {
267
273
  console.log("Camera wrapper layout updated:", layout);
268
274
  }
269
275
  }, /*#__PURE__*/_react["default"].createElement(_expoCamera.CameraView, {
276
+ key: cameraViewMountKey,
270
277
  style: styles.camera,
271
278
  facing: "back",
272
279
  ref: cameraRef,
@@ -253,6 +253,7 @@ var ImageCropper = function ImageCropper(_ref) {
253
253
  var rotationInProgressRef = (0, _react.useRef)(false); // block duplicate taps immediately
254
254
  var lastValidPosition = (0, _react.useRef)(null);
255
255
  var insets = (0, _reactNativeSafeAreaContext.useSafeAreaInsets)();
256
+ var openCameraTimeoutRef = (0, _react.useRef)(null);
256
257
 
257
258
  // ✅ NEW ARCH: mobile does NOT export the final crop.
258
259
 
@@ -261,8 +262,17 @@ var ImageCropper = function ImageCropper(_ref) {
261
262
  var enableRotation = true; // rotation resets crop box so it is re-initialized on rotated image.
262
263
 
263
264
  (0, _react.useEffect)(function () {
265
+ // If a previous open was scheduled, cancel it.
266
+ if (openCameraTimeoutRef.current) {
267
+ clearTimeout(openCameraTimeoutRef.current);
268
+ openCameraTimeoutRef.current = null;
269
+ }
264
270
  if (openCameraFirst) {
265
- setShowCustomCamera(true);
271
+ // Delay opening the camera so the prior session can fully release.
272
+ openCameraTimeoutRef.current = setTimeout(function () {
273
+ setShowCustomCamera(true);
274
+ openCameraTimeoutRef.current = null;
275
+ }, 800);
266
276
  } else if (initialImage) {
267
277
  setImage(initialImage);
268
278
  sourceImageUri.current = initialImage;
@@ -276,6 +286,12 @@ var ImageCropper = function ImageCropper(_ref) {
276
286
  hasInitializedCropBox.current = false;
277
287
  imageSource.current = null;
278
288
  }
289
+ return function () {
290
+ if (openCameraTimeoutRef.current) {
291
+ clearTimeout(openCameraTimeoutRef.current);
292
+ openCameraTimeoutRef.current = null;
293
+ }
294
+ };
279
295
  }, [openCameraFirst, initialImage]);
280
296
 
281
297
  // ✅ REFACTORISATION : Stocker uniquement les dimensions originales (pas de calcul théorique)
@@ -1305,31 +1321,6 @@ var ImageCropper = function ImageCropper(_ref) {
1305
1321
  style: _ImageCropperStyles["default"].container
1306
1322
  }, showCustomCamera ? /*#__PURE__*/_react["default"].createElement(_CustomCamera["default"], {
1307
1323
  onPhotoCaptured: function onPhotoCaptured(uri, frameData) {
1308
- // ✅ Reset refs for new image so second (and later) photos don't use first image's layout (fixes white screen on some devices)
1309
- originalImageDimensions.current = {
1310
- width: 0,
1311
- height: 0
1312
- };
1313
- imageDisplayRect.current = {
1314
- x: 0,
1315
- y: 0,
1316
- width: 0,
1317
- height: 0
1318
- };
1319
- displayedImageLayout.current = {
1320
- x: 0,
1321
- y: 0,
1322
- width: 0,
1323
- height: 0
1324
- };
1325
- imageMeasure.current = {
1326
- x: 0,
1327
- y: 0,
1328
- width: 0,
1329
- height: 0
1330
- };
1331
- sourceImageUri.current = uri;
1332
-
1333
1324
  // ✅ CRITICAL FIX: Store green frame coordinates for coordinate conversion
1334
1325
  if (frameData && frameData.greenFrame) {
1335
1326
  cameraFrameData.current = {
@@ -1425,7 +1416,6 @@ var ImageCropper = function ImageCropper(_ref) {
1425
1416
  return hasMovement;
1426
1417
  }
1427
1418
  }, /*#__PURE__*/_react["default"].createElement(_reactNative.Image, {
1428
- key: image,
1429
1419
  source: {
1430
1420
  uri: image
1431
1421
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-expo-cropper",
3
- "version": "1.2.46",
3
+ "version": "1.2.47",
4
4
  "description": "Recadrage polygonal d'images.",
5
5
  "main": "index.js",
6
6
  "author": "PCS AGRI",
@@ -22,6 +22,8 @@ export default function CustomCamera({ onPhotoCaptured, onFrameCalculated }) {
22
22
  const [isReady, setIsReady] = useState(false);
23
23
  const [loadingBeforeCapture, setLoadingBeforeCapture] = useState(false);
24
24
  const [hasPermission, setHasPermission] = useState(null);
25
+ // Force a fresh native CameraView instance per "open" to avoid stale/cached preview.
26
+ const [cameraViewMountKey] = useState(() => `${Date.now()}-${Math.random().toString(16).slice(2)}`);
25
27
  const cameraRef = useRef(null);
26
28
  const cameraWrapperRef = useRef(null);
27
29
  const insets = useSafeAreaInsets();
@@ -213,6 +215,7 @@ export default function CustomCamera({ onPhotoCaptured, onFrameCalculated }) {
213
215
  }}
214
216
  >
215
217
  <CameraView
218
+ key={cameraViewMountKey}
216
219
  style={styles.camera}
217
220
  facing="back"
218
221
  ref={cameraRef}
@@ -141,6 +141,7 @@ const ImageCropper = ({ onConfirm, openCameraFirst, initialImage, addheight, rot
141
141
  const rotationInProgressRef = useRef(false); // block duplicate taps immediately
142
142
  const lastValidPosition = useRef(null);
143
143
  const insets = useSafeAreaInsets();
144
+ const openCameraTimeoutRef = useRef(null);
144
145
 
145
146
  // ✅ NEW ARCH: mobile does NOT export the final crop.
146
147
 
@@ -153,22 +154,39 @@ const ImageCropper = ({ onConfirm, openCameraFirst, initialImage, addheight, rot
153
154
 
154
155
 
155
156
  useEffect(() => {
156
- if (openCameraFirst) {
157
- setShowCustomCamera(true);
158
- } else if (initialImage) {
159
- setImage(initialImage);
160
- sourceImageUri.current = initialImage;
161
- // ✅ CRITICAL: Reset points when loading a new image from gallery
162
- // This ensures the crop box will be automatically initialized
163
- setPoints([]);
164
- rotationAngle.current = 0;
165
- // Clear camera frame data for gallery images
166
- cameraFrameData.current = null;
167
- // ✅ CRITICAL: Reset initialization guard for new image
168
- hasInitializedCropBox.current = false;
169
- imageSource.current = null;
170
- }
171
- }, [openCameraFirst, initialImage]);
157
+ // If a previous open was scheduled, cancel it.
158
+ if (openCameraTimeoutRef.current) {
159
+ clearTimeout(openCameraTimeoutRef.current);
160
+ openCameraTimeoutRef.current = null;
161
+ }
162
+
163
+ if (openCameraFirst) {
164
+ // Delay opening the camera so the prior session can fully release.
165
+ openCameraTimeoutRef.current = setTimeout(() => {
166
+ setShowCustomCamera(true);
167
+ openCameraTimeoutRef.current = null;
168
+ }, 800);
169
+ } else if (initialImage) {
170
+ setImage(initialImage);
171
+ sourceImageUri.current = initialImage;
172
+ // CRITICAL: Reset points when loading a new image from gallery
173
+ // This ensures the crop box will be automatically initialized
174
+ setPoints([]);
175
+ rotationAngle.current = 0;
176
+ // Clear camera frame data for gallery images
177
+ cameraFrameData.current = null;
178
+ // ✅ CRITICAL: Reset initialization guard for new image
179
+ hasInitializedCropBox.current = false;
180
+ imageSource.current = null;
181
+ }
182
+
183
+ return () => {
184
+ if (openCameraTimeoutRef.current) {
185
+ clearTimeout(openCameraTimeoutRef.current);
186
+ openCameraTimeoutRef.current = null;
187
+ }
188
+ };
189
+ }, [openCameraFirst, initialImage]);
172
190
 
173
191
 
174
192
  // ✅ REFACTORISATION : Stocker uniquement les dimensions originales (pas de calcul théorique)
@@ -1100,13 +1118,6 @@ const rotatePreviewImage = async (degrees) => {
1100
1118
  {showCustomCamera ? (
1101
1119
  <CustomCamera
1102
1120
  onPhotoCaptured={(uri, frameData) => {
1103
- // ✅ Reset refs for new image so second (and later) photos don't use first image's layout (fixes white screen on some devices)
1104
- originalImageDimensions.current = { width: 0, height: 0 };
1105
- imageDisplayRect.current = { x: 0, y: 0, width: 0, height: 0 };
1106
- displayedImageLayout.current = { x: 0, y: 0, width: 0, height: 0 };
1107
- imageMeasure.current = { x: 0, y: 0, width: 0, height: 0 };
1108
- sourceImageUri.current = uri;
1109
-
1110
1121
  // ✅ CRITICAL FIX: Store green frame coordinates for coordinate conversion
1111
1122
  if (frameData && frameData.greenFrame) {
1112
1123
  cameraFrameData.current = {
@@ -1204,7 +1215,6 @@ const rotatePreviewImage = async (degrees) => {
1204
1215
  }}
1205
1216
  >
1206
1217
  <Image
1207
- key={image}
1208
1218
  source={{ uri: image }}
1209
1219
  style={styles.image}
1210
1220
  resizeMode={cameraFrameData.current?.greenFrame ? 'cover' : 'contain'}