@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.
|
@@ -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
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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
|
-
|
|
46
|
+
return await ImageCompressor.compress(path, {
|
|
278
47
|
maxWidth: 1080,
|
|
279
48
|
quality: 0.7,
|
|
280
49
|
});
|
|
281
|
-
return compressed;
|
|
282
50
|
}
|
|
283
|
-
catch
|
|
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
|
-
|
|
60
|
+
return await VideoCompressor.compress(uri, {
|
|
290
61
|
compressionMethod: "auto",
|
|
291
62
|
});
|
|
292
|
-
return compressed;
|
|
293
63
|
}
|
|
294
|
-
catch
|
|
64
|
+
catch {
|
|
295
65
|
return uri;
|
|
296
66
|
}
|
|
297
67
|
};
|
|
298
|
-
|
|
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
|
|
78
|
+
const img = await ImageCropPicker.openCamera({
|
|
306
79
|
mediaType: "photo",
|
|
80
|
+
cropping: true,
|
|
307
81
|
});
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
82
|
+
sendRaw(img);
|
|
83
|
+
const compressed = enableCompression
|
|
84
|
+
? await compressImage(img.path)
|
|
85
|
+
: img.path;
|
|
86
|
+
sendFinal(img, compressed);
|
|
313
87
|
}
|
|
314
|
-
catch (
|
|
315
|
-
console.log(
|
|
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"
|
|
324
|
-
|
|
97
|
+
launchCamera({ mediaType: "video" }, async (res) => {
|
|
98
|
+
const raw = res?.assets?.[0];
|
|
99
|
+
if (!raw)
|
|
325
100
|
return;
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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
|
-
|
|
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
|
|
120
|
+
const img = await ImageCropPicker.openPicker({
|
|
347
121
|
mediaType: "photo",
|
|
122
|
+
cropping: true,
|
|
348
123
|
});
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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
|
-
|
|
136
|
+
launchImageLibrary({ mediaType: "video" }, async (res) => {
|
|
137
|
+
const raw = res?.assets?.[0];
|
|
138
|
+
if (!raw)
|
|
362
139
|
return;
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
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 },
|