@teamnhz/rn-ui-toolkit 1.1.8 → 1.2.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.
@@ -2,7 +2,7 @@ import React from "react";
2
2
  type Props = {
3
3
  mediaType: "photo" | "video";
4
4
  isMultiSelect?: boolean;
5
- onSuccess: (response: any) => void;
5
+ onSuccess: (res: any) => void;
6
6
  visible: boolean;
7
7
  onClose: () => void;
8
8
  enableCompression?: boolean;
@@ -1,378 +1,152 @@
1
- // import React, { useCallback } from "react";
2
- // import {
3
- // View,
4
- // Text,
5
- // TouchableOpacity,
6
- // StyleSheet,
7
- // Image,
8
- // Alert,
9
- // } from "react-native";
10
- // import { BottomSheet, Dividers } from "../index";
11
- // import { launchCamera, launchImageLibrary } from "react-native-image-picker";
12
- // import ImageCropPicker from "react-native-image-crop-picker";
13
- // import {
14
- // Image as ImageCompressor,
15
- // Video as VideoCompressor,
16
- // } from "react-native-compressor";
17
- // import RNFS from "react-native-fs";
18
- // import { Colors, Images, Scale, Typography } from "../../styles";
19
- // // Import your permission utilities
20
- // import {
21
- // cameraPermissions,
22
- // galleryPermissions,
23
- // checkMicroPhonePermission,
24
- // } from "../../utils/permissions";
25
- // type Props = {
26
- // mediaType: "photo" | "video";
27
- // isMultiSelect?: boolean;
28
- // onSuccess: (data: any) => void;
29
- // visible: boolean;
30
- // onClose: () => void;
31
- // // Compression options
32
- // enableCompression?: boolean;
33
- // imageCompressionOptions?: {
34
- // maxWidth?: number;
35
- // quality?: number;
36
- // };
37
- // videoCompressionOptions?: {
38
- // compressionMethod?: "auto" | "manual";
39
- // };
40
- // };
41
- // const ImagePicker: React.FC<Props> = ({
42
- // mediaType,
43
- // isMultiSelect = false,
44
- // onSuccess,
45
- // visible,
46
- // onClose,
47
- // enableCompression = true,
48
- // imageCompressionOptions = { maxWidth: 1080, quality: 0.7 },
49
- // videoCompressionOptions = { compressionMethod: "auto" },
50
- // }) => {
51
- // // Success handler
52
- // const onComplete = useCallback(
53
- // (data: any) => {
54
- // onSuccess(data);
55
- // onClose();
56
- // },
57
- // [onSuccess, onClose]
58
- // );
59
- // // Helper: Compress Image
60
- // const compressImage = async (imagePath: string) => {
61
- // if (!enableCompression) return imagePath;
62
- // try {
63
- // const compressedImage = await ImageCompressor.compress(imagePath, {
64
- // maxWidth: imageCompressionOptions.maxWidth,
65
- // quality: imageCompressionOptions.quality,
66
- // });
67
- // // Log compressed size (optional)
68
- // const stats = await RNFS.stat(compressedImage.replace("file://", ""));
69
- // const sizeInMB = stats.size / (1024 * 1024);
70
- // console.log("Compressed Image Size (MB):", sizeInMB);
71
- // return compressedImage;
72
- // } catch (error) {
73
- // console.error("Image compression error:", error);
74
- // return imagePath; // Return original if compression fails
75
- // }
76
- // };
77
- // // Helper: Compress Video
78
- // const compressVideo = async (videoUri: string) => {
79
- // if (!enableCompression) return videoUri;
80
- // try {
81
- // const compressedVideo = await VideoCompressor.compress(videoUri, {
82
- // compressionMethod: videoCompressionOptions.compressionMethod,
83
- // });
84
- // // Log compressed size (optional)
85
- // const stats = await RNFS.stat(compressedVideo.replace("file://", ""));
86
- // const sizeInMB = stats.size / (1024 * 1024);
87
- // console.log("Compressed Video Size (MB):", sizeInMB);
88
- // return compressedVideo;
89
- // } catch (error) {
90
- // console.error("Video compression error:", error);
91
- // return videoUri; // Return original if compression fails
92
- // }
93
- // };
94
- // // CAMERA HANDLER with permission check and compression
95
- // const handleCamera = async () => {
96
- // await cameraPermissions(async (granted: boolean) => {
97
- // if (!granted) return;
98
- // if (mediaType === "photo") {
99
- // try {
100
- // const response = await ImageCropPicker.openCamera({
101
- // mediaType: "photo",
102
- // });
103
- // if (isMultiSelect) {
104
- // // Single capture for multi-select mode
105
- // const compressedPath = await compressImage(response.path);
106
- // onComplete([{ ...response, path: compressedPath }]);
107
- // } else {
108
- // // Crop and compress single image
109
- // const cropped = await ImageCropPicker.openCropper({
110
- // path: response?.path,
111
- // width: response?.width,
112
- // height: response?.height,
113
- // mediaType: "photo",
114
- // freeStyleCropEnabled: true,
115
- // });
116
- // const compressedPath = await compressImage(cropped.path);
117
- // onComplete({ ...cropped, path: compressedPath });
118
- // }
119
- // } catch (error) {
120
- // console.log("Camera cancelled or error:", error);
121
- // }
122
- // } else {
123
- // // VIDEO CAPTURE - check microphone permission first
124
- // const micPermission = await checkMicroPhonePermission();
125
- // if (!micPermission) {
126
- // Alert.alert(
127
- // "Microphone Permission Required",
128
- // "Please enable microphone access to record videos."
129
- // );
130
- // return;
131
- // }
132
- // launchCamera(
133
- // {
134
- // mediaType: "video",
135
- // durationLimit: 60,
136
- // videoQuality: "high",
137
- // },
138
- // async (response) => {
139
- // if (response?.assets?.length) {
140
- // const videoUri = response.assets[0]?.uri;
141
- // const compressedPath = await compressVideo(videoUri);
142
- // onComplete({
143
- // path: compressedPath,
144
- // duration: response.assets[0]?.duration,
145
- // });
146
- // }
147
- // }
148
- // );
149
- // }
150
- // });
151
- // };
152
- // // GALLERY HANDLER with permission check and compression
153
- // const handleGallery = async () => {
154
- // await galleryPermissions(async (granted: boolean) => {
155
- // if (!granted) return;
156
- // if (mediaType === "photo") {
157
- // try {
158
- // if (isMultiSelect) {
159
- // const images = await ImageCropPicker.openPicker({
160
- // mediaType: "photo",
161
- // multiple: true,
162
- // maxFiles: 5,
163
- // });
164
- // if (images.length > 5) {
165
- // Alert.alert(
166
- // "Limit Exceeded",
167
- // "You can only select up to 5 images."
168
- // );
169
- // return;
170
- // }
171
- // // Compress multiple images
172
- // const compressedImages = await Promise.all(
173
- // images.map(async (img) => ({
174
- // ...img,
175
- // path: await compressImage(img.path),
176
- // }))
177
- // );
178
- // onComplete(compressedImages);
179
- // } else {
180
- // // Single image with crop
181
- // const response = await ImageCropPicker.openPicker({
182
- // mediaType: "photo",
183
- // });
184
- // const cropped = await ImageCropPicker.openCropper({
185
- // path: response?.path,
186
- // width: response?.width,
187
- // height: response?.height,
188
- // mediaType: "photo",
189
- // freeStyleCropEnabled: true,
190
- // });
191
- // const compressedPath = await compressImage(cropped.path);
192
- // onComplete({ ...cropped, path: compressedPath });
193
- // }
194
- // } catch (error) {
195
- // console.log("Gallery cancelled or error:", error);
196
- // }
197
- // } else {
198
- // // VIDEO FROM GALLERY
199
- // launchImageLibrary(
200
- // {
201
- // mediaType: "video",
202
- // assetRepresentationMode: "current",
203
- // },
204
- // async (result) => {
205
- // if (result?.assets?.length) {
206
- // const videoUri = result.assets[0]?.uri;
207
- // const compressedPath = await compressVideo(videoUri);
208
- // onComplete({
209
- // path: compressedPath,
210
- // duration: result.assets[0]?.duration,
211
- // });
212
- // }
213
- // }
214
- // );
215
- // }
216
- // });
217
- // };
218
- // return (
219
- // <BottomSheet visible={visible} onClose={onClose} height={230}>
220
- // <View style={styles.container}>
221
- // {/* Camera */}
222
- // <TouchableOpacity style={styles.row} onPress={handleCamera}>
223
- // <Image source={Images.video_icon} style={styles.icon} />
224
- // <Text style={styles.text}>Camera</Text>
225
- // </TouchableOpacity>
226
- // <Dividers small />
227
- // {/* Gallery */}
228
- // <TouchableOpacity style={styles.row} onPress={handleGallery}>
229
- // <Image source={Images.image_icon} style={styles.icon} />
230
- // <Text style={styles.text}>Gallery</Text>
231
- // </TouchableOpacity>
232
- // <Dividers small />
233
- // {/* Cancel */}
234
- // <TouchableOpacity style={styles.row} onPress={onClose}>
235
- // <Image source={Images.cancel} style={styles.icon} />
236
- // <Text style={styles.text}>Cancel</Text>
237
- // </TouchableOpacity>
238
- // </View>
239
- // </BottomSheet>
240
- // );
241
- // };
242
- // export default ImagePicker;
243
- // const styles = StyleSheet.create({
244
- // container: { flex: 1, padding: 16 },
245
- // row: {
246
- // flexDirection: "row",
247
- // alignItems: "center",
248
- // },
249
- // text: { ...Typography.style.standardU(), color: Colors.white },
250
- // icon: {
251
- // width: Scale.moderateScale(20),
252
- // height: Scale.moderateScale(20),
253
- // marginRight: 10,
254
- // tintColor: Colors.white,
255
- // },
256
- // });
257
1
  import React from "react";
258
2
  import { View, Text, TouchableOpacity, StyleSheet, Image, } from "react-native";
259
3
  import { BottomSheet, Dividers } from "../index";
260
4
  import { launchCamera, launchImageLibrary } from "react-native-image-picker";
261
5
  import ImageCropPicker from "react-native-image-crop-picker";
262
6
  import { Image as ImageCompressor, Video as VideoCompressor, } from "react-native-compressor";
263
- import { Colors, Images, Scale, Typography } from "../../styles";
264
7
  import { cameraPermissions, galleryPermissions, checkMicroPhonePermission, } from "../../utils/permissions";
265
- const ImagePicker = ({ mediaType, isMultiSelect = false, onSuccess, visible, onClose, enableCompression = true, }) => {
266
- // Close modal + start loader in parent
267
- const sendRawAndClose = (rawData) => {
268
- onClose();
269
- onSuccess({ loading: true, data: rawData });
8
+ import { Colors, Images, Scale, Typography } from "../../styles";
9
+ //--------------------------------------
10
+ // 🟦 FORMAT RESPONSE (single standard)
11
+ //--------------------------------------
12
+ const normalizePath = (p) => p ? p.replace("file://", "") : "";
13
+ const buildResponse = (raw, compressed) => {
14
+ return {
15
+ path: normalizePath(compressed || raw?.path || raw?.uri),
16
+ originalPath: normalizePath(raw?.path || raw?.uri),
17
+ fileName: raw?.fileName || "",
18
+ type: raw?.type || "",
19
+ duration: raw?.duration || undefined,
270
20
  };
271
- // After compression finished
272
- const sendFinalCompressed = (compressed) => {
273
- onSuccess({ loading: false, data: compressed });
21
+ };
22
+ const ImagePicker = ({ mediaType, onSuccess, visible, onClose, enableCompression = true, }) => {
23
+ //--------------------------------------
24
+ // 🟦 SEND RAW (loading true)
25
+ //--------------------------------------
26
+ const sendRaw = (raw) => {
27
+ onSuccess({
28
+ loading: true,
29
+ data: buildResponse(raw),
30
+ });
274
31
  };
32
+ //--------------------------------------
33
+ // 🟦 SEND FINAL COMPRESSED
34
+ //--------------------------------------
35
+ const sendFinal = (raw, compressedPath) => {
36
+ onSuccess({
37
+ loading: false,
38
+ data: buildResponse(raw, compressedPath),
39
+ });
40
+ };
41
+ //--------------------------------------
42
+ // 🟦 COMPRESS IMAGE
43
+ //--------------------------------------
275
44
  const compressImage = async (path) => {
276
45
  try {
277
- const compressed = await ImageCompressor.compress(path, {
46
+ return await ImageCompressor.compress(path, {
278
47
  maxWidth: 1080,
279
48
  quality: 0.7,
280
49
  });
281
- return compressed;
282
50
  }
283
- catch (e) {
51
+ catch {
284
52
  return path;
285
53
  }
286
54
  };
55
+ //--------------------------------------
56
+ // 🟦 COMPRESS VIDEO
57
+ //--------------------------------------
287
58
  const compressVideo = async (uri) => {
288
59
  try {
289
- const compressed = await VideoCompressor.compress(uri, {
60
+ return await VideoCompressor.compress(uri, {
290
61
  compressionMethod: "auto",
291
62
  });
292
- return compressed;
293
63
  }
294
- catch (e) {
64
+ catch {
295
65
  return uri;
296
66
  }
297
67
  };
298
- // ---------- CAMERA ----------
68
+ //--------------------------------------
69
+ // 🟦 TAKE PHOTO / VIDEO FROM CAMERA
70
+ //--------------------------------------
299
71
  const handleCamera = async () => {
300
72
  await cameraPermissions(async (granted) => {
301
73
  if (!granted)
302
74
  return;
75
+ // PHOTO FROM CAMERA
303
76
  if (mediaType === "photo") {
304
77
  try {
305
- const picked = await ImageCropPicker.openCamera({
78
+ const img = await ImageCropPicker.openCamera({
306
79
  mediaType: "photo",
80
+ cropping: true,
307
81
  });
308
- sendRawAndClose(picked); // CLOSE MODAL + START LOADER
309
- setTimeout(async () => {
310
- const compressedPath = await compressImage(picked.path);
311
- sendFinalCompressed({ ...picked, path: compressedPath });
312
- }, 10);
82
+ sendRaw(img);
83
+ const compressed = enableCompression
84
+ ? await compressImage(img.path)
85
+ : img.path;
86
+ sendFinal(img, compressed);
313
87
  }
314
- catch (err) {
315
- console.log(err);
88
+ catch (e) {
89
+ console.log("Camera Photo Error", e);
316
90
  }
317
91
  }
92
+ // VIDEO FROM CAMERA
318
93
  else {
319
- // VIDEO
320
94
  const mic = await checkMicroPhonePermission();
321
95
  if (!mic)
322
96
  return;
323
- launchCamera({ mediaType: "video", durationLimit: 60, videoQuality: "high" }, (res) => {
324
- if (!res?.assets?.length)
97
+ launchCamera({ mediaType: "video" }, async (res) => {
98
+ const raw = res?.assets?.[0];
99
+ if (!raw)
325
100
  return;
326
- const raw = res.assets[0];
327
- sendRawAndClose(raw);
328
- setTimeout(async () => {
329
- const compressed = await compressVideo(raw.uri);
330
- sendFinalCompressed({
331
- path: compressed,
332
- duration: raw.duration,
333
- });
334
- }, 10);
101
+ sendRaw(raw);
102
+ const compressed = enableCompression
103
+ ? await compressVideo(raw.uri)
104
+ : raw.uri;
105
+ sendFinal(raw, compressed);
335
106
  });
336
107
  }
337
108
  });
338
109
  };
339
- // ---------- GALLERY ----------
110
+ //--------------------------------------
111
+ // 🟦 PICK FROM GALLERY
112
+ //--------------------------------------
340
113
  const handleGallery = async () => {
341
114
  await galleryPermissions(async (granted) => {
342
115
  if (!granted)
343
116
  return;
117
+ // PHOTO FROM GALLERY (CROP PICKER)
344
118
  if (mediaType === "photo") {
345
119
  try {
346
- const picked = await ImageCropPicker.openPicker({
120
+ const img = await ImageCropPicker.openPicker({
347
121
  mediaType: "photo",
122
+ cropping: true,
348
123
  });
349
- sendRawAndClose(picked);
350
- setTimeout(async () => {
351
- const compressed = await compressImage(picked.path);
352
- sendFinalCompressed({ ...picked, path: compressed });
353
- }, 10);
124
+ sendRaw(img);
125
+ const compressed = enableCompression
126
+ ? await compressImage(img.path)
127
+ : img.path;
128
+ sendFinal(img, compressed);
354
129
  }
355
130
  catch (e) {
356
- console.log(e);
131
+ console.log("Gallery Photo Error", e);
357
132
  }
358
133
  }
134
+ // VIDEO FROM GALLERY
359
135
  else {
360
- launchImageLibrary({ mediaType: "video" }, (res) => {
361
- if (!res?.assets?.length)
136
+ launchImageLibrary({ mediaType: "video" }, async (res) => {
137
+ const raw = res?.assets?.[0];
138
+ if (!raw)
362
139
  return;
363
- const raw = res.assets[0];
364
- sendRawAndClose(raw);
365
- setTimeout(async () => {
366
- const compressed = await compressVideo(raw.uri);
367
- sendFinalCompressed({
368
- path: compressed,
369
- duration: raw.duration,
370
- });
371
- }, 10);
140
+ sendRaw(raw);
141
+ const compressed = enableCompression
142
+ ? await compressVideo(raw.uri)
143
+ : raw.uri;
144
+ sendFinal(raw, compressed);
372
145
  });
373
146
  }
374
147
  });
375
148
  };
149
+ //--------------------------------------
376
150
  return (React.createElement(BottomSheet, { visible: visible, onClose: onClose, height: 230 },
377
151
  React.createElement(View, { style: styles.container },
378
152
  React.createElement(TouchableOpacity, { style: styles.row, onPress: handleCamera },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teamnhz/rn-ui-toolkit",
3
- "version": "1.1.8",
3
+ "version": "1.2.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [