react-native-expo-cropper 1.2.38 → 1.2.40

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.
@@ -8,7 +8,7 @@ exports["default"] = CustomCamera;
8
8
  var _react = _interopRequireWildcard(require("react"));
9
9
  var _reactNative = require("react-native");
10
10
  var _reactNativeSafeAreaContext = require("react-native-safe-area-context");
11
- var _expoCamera = require("expo-camera");
11
+ var _reactNativeVisionCamera = require("react-native-vision-camera");
12
12
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t2 in e) "default" !== _t2 && {}.hasOwnProperty.call(e, _t2) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t2)) && (i.get || i.set) ? o(f, _t2, i) : f[_t2] = e[_t2]); return f; })(e, t); }
13
13
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
14
14
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
@@ -38,44 +38,31 @@ function CustomCamera(_ref) {
38
38
  _useState4 = _slicedToArray(_useState3, 2),
39
39
  loadingBeforeCapture = _useState4[0],
40
40
  setLoadingBeforeCapture = _useState4[1];
41
- var _useState5 = (0, _react.useState)(null),
42
- _useState6 = _slicedToArray(_useState5, 2),
43
- hasPermission = _useState6[0],
44
- setHasPermission = _useState6[1];
45
41
  var cameraRef = (0, _react.useRef)(null);
46
42
  var cameraWrapperRef = (0, _react.useRef)(null);
47
43
  var insets = (0, _reactNativeSafeAreaContext.useSafeAreaInsets)();
48
- var _useState7 = (0, _react.useState)({
44
+ var _useState5 = (0, _react.useState)({
49
45
  width: 0,
50
46
  height: 0,
51
47
  x: 0,
52
48
  y: 0
53
49
  }),
50
+ _useState6 = _slicedToArray(_useState5, 2),
51
+ cameraWrapperLayout = _useState6[0],
52
+ setCameraWrapperLayout = _useState6[1];
53
+ var _useState7 = (0, _react.useState)(null),
54
54
  _useState8 = _slicedToArray(_useState7, 2),
55
- cameraWrapperLayout = _useState8[0],
56
- setCameraWrapperLayout = _useState8[1];
57
- var _useState9 = (0, _react.useState)(null),
58
- _useState0 = _slicedToArray(_useState9, 2),
59
- greenFrame = _useState0[0],
60
- setGreenFrame = _useState0[1];
55
+ greenFrame = _useState8[0],
56
+ setGreenFrame = _useState8[1];
57
+ var _useCameraPermission = (0, _reactNativeVisionCamera.useCameraPermission)(),
58
+ hasPermission = _useCameraPermission.hasPermission,
59
+ requestPermission = _useCameraPermission.requestPermission;
60
+ var device = (0, _reactNativeVisionCamera.useCameraDevice)('back');
61
61
  (0, _react.useEffect)(function () {
62
- _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
63
- var _yield$Camera$request, status;
64
- return _regenerator().w(function (_context) {
65
- while (1) switch (_context.n) {
66
- case 0:
67
- _context.n = 1;
68
- return _expoCamera.Camera.requestCameraPermissionsAsync();
69
- case 1:
70
- _yield$Camera$request = _context.v;
71
- status = _yield$Camera$request.status;
72
- setHasPermission(status === 'granted');
73
- case 2:
74
- return _context.a(2);
75
- }
76
- }, _callee);
77
- }))();
78
- }, []);
62
+ if (!hasPermission) {
63
+ requestPermission();
64
+ }
65
+ }, [hasPermission, requestPermission]);
79
66
 
80
67
  // Helper function to wait for multiple render cycles (works on iOS)
81
68
  var waitForRender = function waitForRender() {
@@ -94,23 +81,19 @@ function CustomCamera(_ref) {
94
81
  });
95
82
  };
96
83
 
97
- // CRITICAL FIX: Calculate green frame coordinates relative to camera preview
98
- // The green frame should be calculated on the wrapper (as it's visually drawn there)
99
- // But we store it with wrapper dimensions so ImageCropper can map it correctly
84
+ // Green frame coordinates relative to camera preview wrapper.
85
+ // Preview matches sensor output (VisionCamera) so framing matches capture.
100
86
  var calculateGreenFrameCoordinates = function calculateGreenFrameCoordinates() {
101
87
  var wrapperWidth = cameraWrapperLayout.width;
102
88
  var wrapperHeight = cameraWrapperLayout.height;
103
89
  if (wrapperWidth === 0 || wrapperHeight === 0) {
104
- console.warn("Camera wrapper layout not ready, cannot calculate green frame");
90
+ console.warn('Camera wrapper layout not ready, cannot calculate green frame');
105
91
  return null;
106
92
  }
107
-
108
- // Calculate green frame as percentage of WRAPPER
109
- var frameWidth = wrapperWidth * 0.85; // 85% of wrapper width
110
- var frameHeight = wrapperHeight * 0.70; // 70% of wrapper height
111
- var frameX = (wrapperWidth - frameWidth) / 2; // Centered horizontally
112
- var frameY = (wrapperHeight - frameHeight) / 2; // Centered vertically
113
-
93
+ var frameWidth = wrapperWidth * 0.85;
94
+ var frameHeight = wrapperHeight * 0.7;
95
+ var frameX = (wrapperWidth - frameWidth) / 2;
96
+ var frameY = (wrapperHeight - frameHeight) / 2;
114
97
  var frameCoords = {
115
98
  x: frameX,
116
99
  y: frameY,
@@ -118,19 +101,14 @@ function CustomCamera(_ref) {
118
101
  height: frameHeight,
119
102
  wrapperWidth: wrapperWidth,
120
103
  wrapperHeight: wrapperHeight,
121
- // ✅ Store percentages for easier mapping later
122
104
  percentX: frameX / wrapperWidth * 100,
123
105
  percentY: frameY / wrapperHeight * 100,
124
106
  percentWidth: 85,
125
- // 85% of wrapper width
126
- percentHeight: 70 // 70% of wrapper height
107
+ percentHeight: 70
127
108
  };
128
- console.log("✅ Green frame coordinates calculated:", frameCoords);
109
+ console.log('Green frame coordinates calculated:', frameCoords);
129
110
  return frameCoords;
130
111
  };
131
-
132
- // 🔁 Keep green frame state in sync with wrapper layout so we can both render it
133
- // and send the exact same coordinates along with the captured photo.
134
112
  (0, _react.useEffect)(function () {
135
113
  var coords = calculateGreenFrameCoordinates();
136
114
  if (coords) {
@@ -138,78 +116,101 @@ function CustomCamera(_ref) {
138
116
  }
139
117
  }, [cameraWrapperLayout]);
140
118
  var takePicture = /*#__PURE__*/function () {
141
- var _ref3 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
142
- var photo, greenFrameCoords, fixedUri, _t;
143
- return _regenerator().w(function (_context2) {
144
- while (1) switch (_context2.p = _context2.n) {
119
+ var _ref2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
120
+ var photo, uri, capturedAspectRatio, greenFrameCoords, _t;
121
+ return _regenerator().w(function (_context) {
122
+ while (1) switch (_context.p = _context.n) {
145
123
  case 0:
146
- if (!cameraRef.current) {
147
- _context2.n = 5;
124
+ if (!(!cameraRef.current || !device)) {
125
+ _context.n = 1;
148
126
  break;
149
127
  }
150
- _context2.p = 1;
151
- // Show loading after a delay (using setImmediate for iOS compatibility)
128
+ return _context.a(2);
129
+ case 1:
130
+ _context.p = 1;
152
131
  waitForRender(5).then(function () {
153
132
  setLoadingBeforeCapture(true);
154
133
  });
155
-
156
- // Wait a bit before taking picture (works on iOS)
157
- _context2.n = 2;
134
+ _context.n = 2;
158
135
  return waitForRender(2);
159
136
  case 2:
160
- _context2.n = 3;
161
- return cameraRef.current.takePictureAsync({
162
- quality: 1,
163
- // Qualité maximale (0-1, 1 = meilleure qualité)
164
- shutterSound: false,
165
- // skipProcessing: true = Désactiver le traitement automatique pour préserver la qualité pixel-perfect
166
- // IMPORTANT : Cela préserve la résolution originale et évite toute interpolation
167
- skipProcessing: true,
168
- // exif: true = Inclure les métadonnées EXIF (orientation, etc.)
169
- // L'orientation sera gérée dans ImageCropper si nécessaire via la fonction de rotation
170
- exif: true
137
+ _context.n = 3;
138
+ return cameraRef.current.takePhoto({
139
+ enableShutterSound: false,
140
+ flash: 'off'
171
141
  });
172
142
  case 3:
173
- photo = _context2.v;
174
- console.log("Photo captured with maximum quality:", {
175
- uri: photo.uri,
143
+ photo = _context.v;
144
+ if (!(!photo.path || !photo.width || !photo.height)) {
145
+ _context.n = 4;
146
+ break;
147
+ }
148
+ throw new Error('Invalid photo received from camera');
149
+ case 4:
150
+ uri = photo.path.startsWith('file://') ? photo.path : "file://".concat(photo.path);
151
+ capturedAspectRatio = photo.width / photo.height;
152
+ console.log('Photo captured (VisionCamera):', {
153
+ uri: uri,
176
154
  width: photo.width,
177
155
  height: photo.height,
178
- exif: photo.exif ? "present" : "missing"
156
+ aspectRatio: capturedAspectRatio.toFixed(3)
179
157
  });
180
-
181
- // CRITICAL FIX: Use the same green frame coordinates that are used for rendering
182
- // Fallback to recalculation if, for some reason, state is not yet set
183
- greenFrameCoords = greenFrame || calculateGreenFrameCoordinates(); // ✅ REFACTORISATION : Utiliser directement l'URI de la photo
184
- // L'orientation sera gérée dans ImageCropper si l'utilisateur utilise la fonction de rotation
185
- // skipProcessing: true préserve la qualité mais peut laisser l'orientation EXIF non appliquée
186
- // C'est acceptable car l'utilisateur peut corriger via la rotation dans ImageCropper
187
- fixedUri = photo.uri; // Envoyer l'URI et les coordonnées du green frame à ImageCropper
188
- onPhotoCaptured(fixedUri, {
158
+ greenFrameCoords = greenFrame || calculateGreenFrameCoordinates();
159
+ if (greenFrameCoords) {
160
+ _context.n = 5;
161
+ break;
162
+ }
163
+ throw new Error('Green frame coordinates not available');
164
+ case 5:
165
+ onPhotoCaptured(uri, {
189
166
  greenFrame: greenFrameCoords,
190
167
  capturedImageSize: {
191
168
  width: photo.width,
192
- height: photo.height
169
+ height: photo.height,
170
+ aspectRatio: capturedAspectRatio
193
171
  }
194
172
  });
195
173
  setLoadingBeforeCapture(false);
196
- _context2.n = 5;
174
+ _context.n = 7;
197
175
  break;
198
- case 4:
199
- _context2.p = 4;
200
- _t = _context2.v;
201
- console.error("Error capturing photo:", _t);
176
+ case 6:
177
+ _context.p = 6;
178
+ _t = _context.v;
179
+ console.error('Error capturing photo:', _t);
202
180
  setLoadingBeforeCapture(false);
203
- _reactNative.Alert.alert("Erreur", "Impossible de capturer la photo. Veuillez réessayer.");
204
- case 5:
205
- return _context2.a(2);
181
+ _reactNative.Alert.alert('Erreur', "Impossible de capturer la photo: ".concat(_t.message || 'Erreur inconnue', ". Veuillez r\xE9essayer."));
182
+ case 7:
183
+ return _context.a(2);
206
184
  }
207
- }, _callee2, null, [[1, 4]]);
185
+ }, _callee, null, [[1, 6]]);
208
186
  }));
209
187
  return function takePicture() {
210
- return _ref3.apply(this, arguments);
188
+ return _ref2.apply(this, arguments);
211
189
  };
212
190
  }();
191
+ if (!hasPermission) {
192
+ return /*#__PURE__*/_react["default"].createElement(_reactNative.SafeAreaView, {
193
+ style: styles.outerContainer
194
+ }, /*#__PURE__*/_react["default"].createElement(_reactNative.View, {
195
+ style: styles.permissionContainer
196
+ }, /*#__PURE__*/_react["default"].createElement(_reactNative.Text, {
197
+ style: styles.text
198
+ }, "Acc\xE8s \xE0 la cam\xE9ra requis pour prendre des photos."), /*#__PURE__*/_react["default"].createElement(_reactNative.TouchableOpacity, {
199
+ style: styles.button,
200
+ onPress: requestPermission
201
+ }, /*#__PURE__*/_react["default"].createElement(_reactNative.Text, {
202
+ style: styles.buttonText
203
+ }, "Autoriser la cam\xE9ra"))));
204
+ }
205
+ if (!device) {
206
+ return /*#__PURE__*/_react["default"].createElement(_reactNative.SafeAreaView, {
207
+ style: styles.outerContainer
208
+ }, /*#__PURE__*/_react["default"].createElement(_reactNative.View, {
209
+ style: styles.permissionContainer
210
+ }, /*#__PURE__*/_react["default"].createElement(_reactNative.Text, {
211
+ style: styles.text
212
+ }, "Aucune cam\xE9ra arri\xE8re disponible.")));
213
+ }
213
214
  return /*#__PURE__*/_react["default"].createElement(_reactNative.SafeAreaView, {
214
215
  style: styles.outerContainer
215
216
  }, /*#__PURE__*/_react["default"].createElement(_reactNative.View, {
@@ -223,14 +224,17 @@ function CustomCamera(_ref) {
223
224
  x: layout.x,
224
225
  y: layout.y
225
226
  });
226
- console.log("Camera wrapper layout updated:", layout);
227
+ console.log('Camera wrapper layout updated:', layout);
227
228
  }
228
- }, /*#__PURE__*/_react["default"].createElement(_expoCamera.CameraView, {
229
- style: styles.camera,
230
- facing: "back",
229
+ }, /*#__PURE__*/_react["default"].createElement(_reactNativeVisionCamera.Camera, {
231
230
  ref: cameraRef,
232
- onCameraReady: function onCameraReady() {
233
- return setIsReady(true);
231
+ style: _reactNative.StyleSheet.absoluteFill,
232
+ device: device,
233
+ isActive: true,
234
+ photo: true,
235
+ onInitialized: function onInitialized() {
236
+ setIsReady(true);
237
+ console.log('VisionCamera ready - preview matches capture');
234
238
  }
235
239
  }), loadingBeforeCapture && /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, /*#__PURE__*/_react["default"].createElement(_reactNative.View, {
236
240
  style: styles.loadingOverlay
@@ -278,7 +282,6 @@ var styles = _reactNative.StyleSheet.create({
278
282
  justifyContent: 'center',
279
283
  position: 'relative'
280
284
  },
281
- camera: _objectSpread({}, _reactNative.StyleSheet.absoluteFillObject),
282
285
  scanFrame: {
283
286
  position: 'absolute',
284
287
  borderWidth: 4,
@@ -300,14 +303,6 @@ var styles = _reactNative.StyleSheet.create({
300
303
  zIndex: 21,
301
304
  backgroundColor: 'transparent'
302
305
  }),
303
- cancelIcon: {
304
- position: 'absolute',
305
- top: 20,
306
- left: 20,
307
- backgroundColor: PRIMARY_GREEN,
308
- borderRadius: 5,
309
- padding: 8
310
- },
311
306
  buttonContainer: {
312
307
  position: 'absolute',
313
308
  bottom: 0,
@@ -329,16 +324,15 @@ var styles = _reactNative.StyleSheet.create({
329
324
  fontSize: 18,
330
325
  color: GLOW_WHITE
331
326
  },
332
- container: {
327
+ permissionContainer: {
333
328
  flex: 1,
334
- backgroundColor: DEEP_BLACK,
335
329
  justifyContent: 'center',
336
330
  alignItems: 'center',
337
331
  padding: 20
338
332
  },
339
- iconText: {
340
- fontSize: 18,
341
- color: GLOW_WHITE,
333
+ buttonText: {
334
+ fontSize: 16,
335
+ color: DEEP_BLACK,
342
336
  fontWeight: '600'
343
337
  }
344
338
  });