@teamnhz/rn-ui-toolkit 1.2.3 → 1.2.5
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,6 +1,6 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
2
|
import { ActivityIndicator } from "react-native";
|
|
3
|
-
import FastImage from "react-native-fast-image";
|
|
3
|
+
import FastImage from "@d11/react-native-fast-image";
|
|
4
4
|
const Image = (props) => {
|
|
5
5
|
const [isLoading, setIsLoading] = useState(props?.isLoading || false);
|
|
6
6
|
return (React.createElement(React.Fragment, null,
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
type Props = {
|
|
3
3
|
mediaType: "photo" | "video";
|
|
4
|
+
isMultiSelect?: boolean;
|
|
5
|
+
onSuccess: (data: any) => void;
|
|
4
6
|
visible: boolean;
|
|
5
|
-
onSuccess: (res: any) => void;
|
|
6
7
|
onClose: () => void;
|
|
7
8
|
enableCompression?: boolean;
|
|
9
|
+
imageCompressionOptions?: {
|
|
10
|
+
maxWidth?: number;
|
|
11
|
+
quality?: number;
|
|
12
|
+
};
|
|
13
|
+
videoCompressionOptions?: {
|
|
14
|
+
compressionMethod?: "auto" | "manual";
|
|
15
|
+
};
|
|
8
16
|
};
|
|
9
17
|
declare const ImagePicker: React.FC<Props>;
|
|
10
18
|
export default ImagePicker;
|
|
@@ -1,174 +1,498 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import {
|
|
1
|
+
// // import React, { useCallback } from "react";
|
|
2
|
+
// // import {
|
|
3
|
+
// // View,
|
|
4
|
+
// // Text,
|
|
5
|
+
// // TouchableOpacity,
|
|
6
|
+
// // StyleSheet,
|
|
7
|
+
// // Image,
|
|
8
|
+
// // Alert,
|
|
9
|
+
// // ViewStyle,
|
|
10
|
+
// // TextStyle,
|
|
11
|
+
// // ImageStyle,
|
|
12
|
+
// // } from "react-native";
|
|
13
|
+
// // import { BottomSheet, Dividers } from "../index";
|
|
14
|
+
// // import { launchCamera, launchImageLibrary } from "react-native-image-picker";
|
|
15
|
+
// // import ImageCropPicker from "react-native-image-crop-picker";
|
|
16
|
+
// // import { Colors, Images, Scale, Typography } from "../../styles";
|
|
17
|
+
// // type StyleProps = {
|
|
18
|
+
// // containerStyle?: ViewStyle;
|
|
19
|
+
// // rowStyle?: ViewStyle;
|
|
20
|
+
// // textStyle?: TextStyle;
|
|
21
|
+
// // iconStyle?: ImageStyle;
|
|
22
|
+
// // };
|
|
23
|
+
// // type Props = {
|
|
24
|
+
// // mediaType: "photo" | "video";
|
|
25
|
+
// // isMultiSelect?: boolean;
|
|
26
|
+
// // onSuccess: (data: any) => void;
|
|
27
|
+
// // visible: boolean;
|
|
28
|
+
// // onClose: () => void;
|
|
29
|
+
// // } & StyleProps;
|
|
30
|
+
// // const ImagePicker: React.FC<Props> = ({
|
|
31
|
+
// // mediaType,
|
|
32
|
+
// // isMultiSelect = false,
|
|
33
|
+
// // onSuccess,
|
|
34
|
+
// // visible,
|
|
35
|
+
// // onClose,
|
|
36
|
+
// // containerStyle,
|
|
37
|
+
// // rowStyle,
|
|
38
|
+
// // textStyle,
|
|
39
|
+
// // iconStyle,
|
|
40
|
+
// // }) => {
|
|
41
|
+
// // const onComplete = useCallback(
|
|
42
|
+
// // (data: any) => {
|
|
43
|
+
// // onSuccess(data);
|
|
44
|
+
// // onClose();
|
|
45
|
+
// // },
|
|
46
|
+
// // [onSuccess, onClose]
|
|
47
|
+
// // );
|
|
48
|
+
// // const onCamera = () => {
|
|
49
|
+
// // if (mediaType === "photo") {
|
|
50
|
+
// // if (isMultiSelect) {
|
|
51
|
+
// // ImageCropPicker.openCamera({ mediaType: "photo" })
|
|
52
|
+
// // .then((response) => onComplete([response]))
|
|
53
|
+
// // .catch(() => {});
|
|
54
|
+
// // } else {
|
|
55
|
+
// // ImageCropPicker.openCamera({ mediaType: "photo" })
|
|
56
|
+
// // .then((response) =>
|
|
57
|
+
// // ImageCropPicker.openCropper({
|
|
58
|
+
// // path: response?.path,
|
|
59
|
+
// // width: response?.width,
|
|
60
|
+
// // height: response?.height,
|
|
61
|
+
// // mediaType: "photo",
|
|
62
|
+
// // freeStyleCropEnabled: true,
|
|
63
|
+
// // }).then(onComplete)
|
|
64
|
+
// // )
|
|
65
|
+
// // .catch(() => {});
|
|
66
|
+
// // }
|
|
67
|
+
// // } else {
|
|
68
|
+
// // launchCamera({ mediaType: "video" }, (response) => {
|
|
69
|
+
// // if (response?.assets) {
|
|
70
|
+
// // onComplete({
|
|
71
|
+
// // path: response.assets[0]?.uri,
|
|
72
|
+
// // duration: response.assets[0]?.duration,
|
|
73
|
+
// // });
|
|
74
|
+
// // }
|
|
75
|
+
// // });
|
|
76
|
+
// // }
|
|
77
|
+
// // };
|
|
78
|
+
// // const onGallery = () => {
|
|
79
|
+
// // if (mediaType === "photo") {
|
|
80
|
+
// // if (isMultiSelect) {
|
|
81
|
+
// // ImageCropPicker.openPicker({
|
|
82
|
+
// // mediaType: "photo",
|
|
83
|
+
// // multiple: true,
|
|
84
|
+
// // maxFiles: 5,
|
|
85
|
+
// // })
|
|
86
|
+
// // .then((images) => {
|
|
87
|
+
// // if (images.length > 5) {
|
|
88
|
+
// // Alert.alert(
|
|
89
|
+
// // "Limit Exceeded",
|
|
90
|
+
// // "You can only select up to 5 images."
|
|
91
|
+
// // );
|
|
92
|
+
// // } else {
|
|
93
|
+
// // onComplete(images);
|
|
94
|
+
// // }
|
|
95
|
+
// // })
|
|
96
|
+
// // .catch(() => {});
|
|
97
|
+
// // } else {
|
|
98
|
+
// // ImageCropPicker.openPicker({ mediaType: "photo" })
|
|
99
|
+
// // .then((response) =>
|
|
100
|
+
// // ImageCropPicker.openCropper({
|
|
101
|
+
// // path: response?.path,
|
|
102
|
+
// // width: response?.width,
|
|
103
|
+
// // height: response?.height,
|
|
104
|
+
// // mediaType: "photo",
|
|
105
|
+
// // freeStyleCropEnabled: true,
|
|
106
|
+
// // }).then(onComplete)
|
|
107
|
+
// // )
|
|
108
|
+
// // .catch(() => {});
|
|
109
|
+
// // }
|
|
110
|
+
// // } else {
|
|
111
|
+
// // launchImageLibrary({ mediaType: "video" }, (result) => {
|
|
112
|
+
// // if (result?.assets) {
|
|
113
|
+
// // onComplete({
|
|
114
|
+
// // path: result.assets[0]?.uri,
|
|
115
|
+
// // duration: result.assets[0]?.duration,
|
|
116
|
+
// // });
|
|
117
|
+
// // }
|
|
118
|
+
// // });
|
|
119
|
+
// // }
|
|
120
|
+
// // };
|
|
121
|
+
// // return (
|
|
122
|
+
// // <BottomSheet visible={visible} onClose={onClose} height={230}>
|
|
123
|
+
// // <View style={[styles.container, containerStyle]}>
|
|
124
|
+
// // {/* Camera */}
|
|
125
|
+
// // <TouchableOpacity style={[styles.row, rowStyle]} onPress={onCamera}>
|
|
126
|
+
// // <Image source={Images.video_icon} style={[styles.icon, iconStyle]} />
|
|
127
|
+
// // <Text style={[styles.text, textStyle]}>Camera</Text>
|
|
128
|
+
// // </TouchableOpacity>
|
|
129
|
+
// // <Dividers small />
|
|
130
|
+
// // {/* Gallery */}
|
|
131
|
+
// // <TouchableOpacity style={[styles.row, rowStyle]} onPress={onGallery}>
|
|
132
|
+
// // <Image source={Images.image_icon} style={[styles.icon, iconStyle]} />
|
|
133
|
+
// // <Text style={[styles.text, textStyle]}>Gallery</Text>
|
|
134
|
+
// // </TouchableOpacity>
|
|
135
|
+
// // <Dividers small />
|
|
136
|
+
// // {/* Cancel */}
|
|
137
|
+
// // <TouchableOpacity style={[styles.row, rowStyle]} onPress={onClose}>
|
|
138
|
+
// // <Image source={Images.cancel} style={[styles.icon, iconStyle]} />
|
|
139
|
+
// // <Text style={[styles.text, textStyle]}>Cancel</Text>
|
|
140
|
+
// // </TouchableOpacity>
|
|
141
|
+
// // </View>
|
|
142
|
+
// // </BottomSheet>
|
|
143
|
+
// // );
|
|
144
|
+
// // };
|
|
145
|
+
// // export default ImagePicker;
|
|
146
|
+
// // const styles = StyleSheet.create({
|
|
147
|
+
// // container: { flex: 1, padding: 16 },
|
|
148
|
+
// // row: {
|
|
149
|
+
// // flexDirection: "row",
|
|
150
|
+
// // alignItems: "center",
|
|
151
|
+
// // },
|
|
152
|
+
// // text: { ...Typography.style.standardU(), color: Colors.white },
|
|
153
|
+
// // icon: {
|
|
154
|
+
// // width: Scale.moderateScale(20),
|
|
155
|
+
// // height: Scale.moderateScale(20),
|
|
156
|
+
// // marginRight: 10,
|
|
157
|
+
// // tintColor: Colors.white,
|
|
158
|
+
// // },
|
|
159
|
+
// // });
|
|
160
|
+
// import React, { useCallback } from "react";
|
|
161
|
+
// import {
|
|
162
|
+
// View,
|
|
163
|
+
// Text,
|
|
164
|
+
// TouchableOpacity,
|
|
165
|
+
// StyleSheet,
|
|
166
|
+
// Image,
|
|
167
|
+
// Alert,
|
|
168
|
+
// } from "react-native";
|
|
169
|
+
// import { BottomSheet, Dividers } from "../index";
|
|
170
|
+
// import { launchCamera, launchImageLibrary } from "react-native-image-picker";
|
|
171
|
+
// import ImageCropPicker from "react-native-image-crop-picker";
|
|
172
|
+
// import { Colors, Images, Scale, Typography } from "../../styles";
|
|
173
|
+
// // 👇 Import your permission utilities
|
|
174
|
+
// import {
|
|
175
|
+
// cameraPermissions,
|
|
176
|
+
// galleryPermissions,
|
|
177
|
+
// } from "../../utils/permissions"; // ✅ Make sure path is correct
|
|
178
|
+
// type Props = {
|
|
179
|
+
// mediaType: "photo" | "video";
|
|
180
|
+
// isMultiSelect?: boolean;
|
|
181
|
+
// onSuccess: (data: any) => void;
|
|
182
|
+
// visible: boolean;
|
|
183
|
+
// onClose: () => void;
|
|
184
|
+
// };
|
|
185
|
+
// const ImagePicker: React.FC<Props> = ({
|
|
186
|
+
// mediaType,
|
|
187
|
+
// isMultiSelect = false,
|
|
188
|
+
// onSuccess,
|
|
189
|
+
// visible,
|
|
190
|
+
// onClose,
|
|
191
|
+
// }) => {
|
|
192
|
+
// // ✅ Success handler
|
|
193
|
+
// const onComplete = useCallback(
|
|
194
|
+
// (data: any) => {
|
|
195
|
+
// onSuccess(data);
|
|
196
|
+
// onClose();
|
|
197
|
+
// },
|
|
198
|
+
// [onSuccess, onClose]
|
|
199
|
+
// );
|
|
200
|
+
// // ✅ CAMERA HANDLER with permission check
|
|
201
|
+
// const handleCamera = async () => {
|
|
202
|
+
// await cameraPermissions(async (granted: boolean) => {
|
|
203
|
+
// if (!granted) return; // ❌ Permission denied, handled by your alert
|
|
204
|
+
// if (mediaType === "photo") {
|
|
205
|
+
// if (isMultiSelect) {
|
|
206
|
+
// // single capture, multiple not supported directly from camera
|
|
207
|
+
// ImageCropPicker.openCamera({ mediaType: "photo" })
|
|
208
|
+
// .then((response) => onComplete([response]))
|
|
209
|
+
// .catch(() => {});
|
|
210
|
+
// } else {
|
|
211
|
+
// ImageCropPicker.openCamera({ mediaType: "photo" })
|
|
212
|
+
// .then((response) =>
|
|
213
|
+
// ImageCropPicker.openCropper({
|
|
214
|
+
// path: response?.path,
|
|
215
|
+
// width: response?.width,
|
|
216
|
+
// height: response?.height,
|
|
217
|
+
// mediaType: "photo",
|
|
218
|
+
// freeStyleCropEnabled: true,
|
|
219
|
+
// }).then(onComplete)
|
|
220
|
+
// )
|
|
221
|
+
// .catch(() => {});
|
|
222
|
+
// }
|
|
223
|
+
// } else {
|
|
224
|
+
// // 🎥 VIDEO CAPTURE
|
|
225
|
+
// launchCamera({ mediaType: "video" }, (response) => {
|
|
226
|
+
// if (response?.assets?.length) {
|
|
227
|
+
// onComplete({
|
|
228
|
+
// path: response.assets[0]?.uri,
|
|
229
|
+
// duration: response.assets[0]?.duration,
|
|
230
|
+
// });
|
|
231
|
+
// }
|
|
232
|
+
// });
|
|
233
|
+
// }
|
|
234
|
+
// });
|
|
235
|
+
// };
|
|
236
|
+
// // ✅ GALLERY HANDLER with permission check
|
|
237
|
+
// const handleGallery = async () => {
|
|
238
|
+
// await galleryPermissions(async (granted: boolean) => {
|
|
239
|
+
// if (!granted) return; // ❌ Permission denied, handled by your alert
|
|
240
|
+
// if (mediaType === "photo") {
|
|
241
|
+
// if (isMultiSelect) {
|
|
242
|
+
// ImageCropPicker.openPicker({
|
|
243
|
+
// mediaType: "photo",
|
|
244
|
+
// multiple: true,
|
|
245
|
+
// maxFiles: 5,
|
|
246
|
+
// })
|
|
247
|
+
// .then((images) => {
|
|
248
|
+
// if (images.length > 5) {
|
|
249
|
+
// Alert.alert(
|
|
250
|
+
// "Limit Exceeded",
|
|
251
|
+
// "You can only select up to 5 images."
|
|
252
|
+
// );
|
|
253
|
+
// } else {
|
|
254
|
+
// onComplete(images);
|
|
255
|
+
// }
|
|
256
|
+
// })
|
|
257
|
+
// .catch(() => {});
|
|
258
|
+
// } else {
|
|
259
|
+
// ImageCropPicker.openPicker({ mediaType: "photo" })
|
|
260
|
+
// .then((response) =>
|
|
261
|
+
// ImageCropPicker.openCropper({
|
|
262
|
+
// path: response?.path,
|
|
263
|
+
// width: response?.width,
|
|
264
|
+
// height: response?.height,
|
|
265
|
+
// mediaType: "photo",
|
|
266
|
+
// freeStyleCropEnabled: true,
|
|
267
|
+
// }).then(onComplete)
|
|
268
|
+
// )
|
|
269
|
+
// .catch(() => {});
|
|
270
|
+
// }
|
|
271
|
+
// } else {
|
|
272
|
+
// // 🎥 VIDEO FROM GALLERY
|
|
273
|
+
// launchImageLibrary({ mediaType: "video" }, (result) => {
|
|
274
|
+
// if (result?.assets?.length) {
|
|
275
|
+
// onComplete({
|
|
276
|
+
// path: result.assets[0]?.uri,
|
|
277
|
+
// duration: result.assets[0]?.duration,
|
|
278
|
+
// });
|
|
279
|
+
// }
|
|
280
|
+
// });
|
|
281
|
+
// }
|
|
282
|
+
// });
|
|
283
|
+
// };
|
|
284
|
+
// return (
|
|
285
|
+
// <BottomSheet visible={visible} onClose={onClose} height={230}>
|
|
286
|
+
// <View style={styles.container}>
|
|
287
|
+
// {/* 📸 Camera */}
|
|
288
|
+
// <TouchableOpacity style={styles.row} onPress={handleCamera}>
|
|
289
|
+
// <Image source={Images.video_icon} style={styles.icon} />
|
|
290
|
+
// <Text style={styles.text}>Camera</Text>
|
|
291
|
+
// </TouchableOpacity>
|
|
292
|
+
// <Dividers small />
|
|
293
|
+
// {/* 🖼️ Gallery */}
|
|
294
|
+
// <TouchableOpacity style={styles.row} onPress={handleGallery}>
|
|
295
|
+
// <Image source={Images.image_icon} style={styles.icon} />
|
|
296
|
+
// <Text style={styles.text}>Gallery</Text>
|
|
297
|
+
// </TouchableOpacity>
|
|
298
|
+
// <Dividers small />
|
|
299
|
+
// {/* ❌ Cancel */}
|
|
300
|
+
// <TouchableOpacity style={styles.row} onPress={onClose}>
|
|
301
|
+
// <Image source={Images.cancel} style={styles.icon} />
|
|
302
|
+
// <Text style={styles.text}>Cancel</Text>
|
|
303
|
+
// </TouchableOpacity>
|
|
304
|
+
// </View>
|
|
305
|
+
// </BottomSheet>
|
|
306
|
+
// );
|
|
307
|
+
// };
|
|
308
|
+
// export default ImagePicker;
|
|
309
|
+
// const styles = StyleSheet.create({
|
|
310
|
+
// container: { flex: 1, padding: 16 },
|
|
311
|
+
// row: {
|
|
312
|
+
// flexDirection: "row",
|
|
313
|
+
// alignItems: "center",
|
|
314
|
+
// },
|
|
315
|
+
// text: { ...Typography.style.standardU(), color: Colors.white },
|
|
316
|
+
// icon: {
|
|
317
|
+
// width: Scale.moderateScale(20),
|
|
318
|
+
// height: Scale.moderateScale(20),
|
|
319
|
+
// marginRight: 10,
|
|
320
|
+
// tintColor: Colors.white,
|
|
321
|
+
// },
|
|
322
|
+
// });
|
|
323
|
+
import React, { useCallback } from "react";
|
|
324
|
+
import { View, Text, TouchableOpacity, StyleSheet, Image, Alert, } from "react-native";
|
|
3
325
|
import { BottomSheet, Dividers } from "../index";
|
|
4
326
|
import { launchCamera, launchImageLibrary } from "react-native-image-picker";
|
|
5
327
|
import ImageCropPicker from "react-native-image-crop-picker";
|
|
6
328
|
import { Image as ImageCompressor, Video as VideoCompressor, } from "react-native-compressor";
|
|
7
|
-
import
|
|
329
|
+
import RNFS from "react-native-fs";
|
|
8
330
|
import { Colors, Images, Scale, Typography } from "../../styles";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
catch { }
|
|
23
|
-
// 🧹 If you stored something in ref or state in future
|
|
24
|
-
// you can reset here too.
|
|
25
|
-
};
|
|
26
|
-
//--------------------------------------
|
|
27
|
-
// 🟦 FINAL RESPONSE FORMAT
|
|
28
|
-
//--------------------------------------
|
|
29
|
-
const buildResponse = (raw, compressed) => ({
|
|
30
|
-
path: normalizePath(compressed || raw?.path || raw?.uri),
|
|
31
|
-
originalPath: normalizePath(raw?.path || raw?.uri),
|
|
32
|
-
fileName: raw?.fileName || "",
|
|
33
|
-
type: raw?.type || "",
|
|
34
|
-
duration: raw?.duration || undefined,
|
|
35
|
-
});
|
|
36
|
-
//--------------------------------------
|
|
37
|
-
// 🟦 MAIN COMPONENT
|
|
38
|
-
//--------------------------------------
|
|
39
|
-
const ImagePicker = ({ mediaType, visible, onSuccess, onClose, enableCompression = true, }) => {
|
|
40
|
-
//--------------------------------------
|
|
41
|
-
// SEND RAW (loader ON)
|
|
42
|
-
//--------------------------------------
|
|
43
|
-
const sendRaw = (raw) => {
|
|
44
|
-
onSuccess({
|
|
45
|
-
loading: true,
|
|
46
|
-
data: buildResponse(raw),
|
|
47
|
-
});
|
|
48
|
-
};
|
|
49
|
-
//--------------------------------------
|
|
50
|
-
// SEND FINAL (loader OFF)
|
|
51
|
-
//--------------------------------------
|
|
52
|
-
const sendFinal = (raw, compressed) => {
|
|
53
|
-
onSuccess({
|
|
54
|
-
loading: false,
|
|
55
|
-
data: buildResponse(raw, compressed),
|
|
56
|
-
});
|
|
57
|
-
resetInternalState();
|
|
58
|
-
};
|
|
59
|
-
//--------------------------------------
|
|
60
|
-
// COMPRESS IMAGE
|
|
61
|
-
//--------------------------------------
|
|
62
|
-
const compressImage = async (path) => {
|
|
331
|
+
// Import your permission utilities
|
|
332
|
+
import { cameraPermissions, galleryPermissions, checkMicroPhonePermission, } from "../../utils/permissions";
|
|
333
|
+
const ImagePicker = ({ mediaType, isMultiSelect = false, onSuccess, visible, onClose, enableCompression = true, imageCompressionOptions = { maxWidth: 1080, quality: 0.7 }, videoCompressionOptions = { compressionMethod: "auto" }, }) => {
|
|
334
|
+
// Success handler
|
|
335
|
+
const onComplete = useCallback((data) => {
|
|
336
|
+
onSuccess(data);
|
|
337
|
+
onClose();
|
|
338
|
+
}, [onSuccess, onClose]);
|
|
339
|
+
// Helper: Compress Image
|
|
340
|
+
const compressImage = async (imagePath) => {
|
|
341
|
+
if (!enableCompression)
|
|
342
|
+
return imagePath;
|
|
63
343
|
try {
|
|
64
|
-
|
|
65
|
-
maxWidth:
|
|
66
|
-
quality:
|
|
344
|
+
const compressedImage = await ImageCompressor.compress(imagePath, {
|
|
345
|
+
maxWidth: imageCompressionOptions.maxWidth,
|
|
346
|
+
quality: imageCompressionOptions.quality,
|
|
67
347
|
});
|
|
348
|
+
// Log compressed size (optional)
|
|
349
|
+
const stats = await RNFS.stat(compressedImage.replace("file://", ""));
|
|
350
|
+
const sizeInMB = stats.size / (1024 * 1024);
|
|
351
|
+
console.log("Compressed Image Size (MB):", sizeInMB);
|
|
352
|
+
return compressedImage;
|
|
68
353
|
}
|
|
69
|
-
catch {
|
|
70
|
-
|
|
354
|
+
catch (error) {
|
|
355
|
+
console.error("Image compression error:", error);
|
|
356
|
+
return imagePath; // Return original if compression fails
|
|
71
357
|
}
|
|
72
358
|
};
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
359
|
+
// Helper: Compress Video
|
|
360
|
+
const compressVideo = async (videoUri) => {
|
|
361
|
+
if (!enableCompression)
|
|
362
|
+
return videoUri;
|
|
77
363
|
try {
|
|
78
|
-
|
|
79
|
-
compressionMethod:
|
|
364
|
+
const compressedVideo = await VideoCompressor.compress(videoUri, {
|
|
365
|
+
compressionMethod: videoCompressionOptions.compressionMethod,
|
|
80
366
|
});
|
|
367
|
+
// Log compressed size (optional)
|
|
368
|
+
const stats = await RNFS.stat(compressedVideo.replace("file://", ""));
|
|
369
|
+
const sizeInMB = stats.size / (1024 * 1024);
|
|
370
|
+
console.log("Compressed Video Size (MB):", sizeInMB);
|
|
371
|
+
return compressedVideo;
|
|
81
372
|
}
|
|
82
|
-
catch {
|
|
83
|
-
|
|
373
|
+
catch (error) {
|
|
374
|
+
console.error("Video compression error:", error);
|
|
375
|
+
return videoUri; // Return original if compression fails
|
|
84
376
|
}
|
|
85
377
|
};
|
|
86
|
-
|
|
87
|
-
// CAMERA HANDLER
|
|
88
|
-
//--------------------------------------
|
|
378
|
+
// CAMERA HANDLER with permission check and compression
|
|
89
379
|
const handleCamera = async () => {
|
|
90
|
-
onClose(); // 🔥 CLOSE FIRST (best UX)
|
|
91
380
|
await cameraPermissions(async (granted) => {
|
|
92
381
|
if (!granted)
|
|
93
382
|
return;
|
|
94
|
-
// PHOTO
|
|
95
383
|
if (mediaType === "photo") {
|
|
96
384
|
try {
|
|
97
|
-
const
|
|
385
|
+
const response = await ImageCropPicker.openCamera({
|
|
98
386
|
mediaType: "photo",
|
|
99
|
-
cropping: true,
|
|
100
387
|
});
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
:
|
|
105
|
-
|
|
388
|
+
if (isMultiSelect) {
|
|
389
|
+
// Single capture for multi-select mode
|
|
390
|
+
const compressedPath = await compressImage(response.path);
|
|
391
|
+
onComplete([{ ...response, path: compressedPath }]);
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
// Crop and compress single image
|
|
395
|
+
const cropped = await ImageCropPicker.openCropper({
|
|
396
|
+
path: response?.path,
|
|
397
|
+
width: response?.width,
|
|
398
|
+
height: response?.height,
|
|
399
|
+
mediaType: "photo",
|
|
400
|
+
freeStyleCropEnabled: true,
|
|
401
|
+
});
|
|
402
|
+
const compressedPath = await compressImage(cropped.path);
|
|
403
|
+
onComplete({ ...cropped, path: compressedPath });
|
|
404
|
+
}
|
|
106
405
|
}
|
|
107
|
-
catch (
|
|
108
|
-
console.log("Camera
|
|
406
|
+
catch (error) {
|
|
407
|
+
console.log("Camera cancelled or error:", error);
|
|
109
408
|
}
|
|
110
409
|
}
|
|
111
|
-
// VIDEO
|
|
112
410
|
else {
|
|
113
|
-
|
|
114
|
-
|
|
411
|
+
// VIDEO CAPTURE - check microphone permission first
|
|
412
|
+
const micPermission = await checkMicroPhonePermission();
|
|
413
|
+
if (!micPermission) {
|
|
414
|
+
Alert.alert("Microphone Permission Required", "Please enable microphone access to record videos.");
|
|
115
415
|
return;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
416
|
+
}
|
|
417
|
+
launchCamera({
|
|
418
|
+
mediaType: "video",
|
|
419
|
+
durationLimit: 60,
|
|
420
|
+
videoQuality: "high",
|
|
421
|
+
}, async (response) => {
|
|
422
|
+
if (response?.assets?.length) {
|
|
423
|
+
const videoUri = response.assets[0]?.uri;
|
|
424
|
+
const compressedPath = await compressVideo(videoUri);
|
|
425
|
+
onComplete({
|
|
426
|
+
path: compressedPath,
|
|
427
|
+
duration: response.assets[0]?.duration,
|
|
428
|
+
});
|
|
429
|
+
}
|
|
125
430
|
});
|
|
126
431
|
}
|
|
127
432
|
});
|
|
128
433
|
};
|
|
129
|
-
|
|
130
|
-
// GALLERY HANDLER
|
|
131
|
-
//--------------------------------------
|
|
434
|
+
// GALLERY HANDLER with permission check and compression
|
|
132
435
|
const handleGallery = async () => {
|
|
133
|
-
onClose(); // 🔥 CLOSE IMMEDIATELY
|
|
134
436
|
await galleryPermissions(async (granted) => {
|
|
135
437
|
if (!granted)
|
|
136
438
|
return;
|
|
137
|
-
// PHOTO (Crop Picker)
|
|
138
439
|
if (mediaType === "photo") {
|
|
139
440
|
try {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
441
|
+
if (isMultiSelect) {
|
|
442
|
+
const images = await ImageCropPicker.openPicker({
|
|
443
|
+
mediaType: "photo",
|
|
444
|
+
multiple: true,
|
|
445
|
+
maxFiles: 5,
|
|
446
|
+
});
|
|
447
|
+
if (images.length > 5) {
|
|
448
|
+
Alert.alert("Limit Exceeded", "You can only select up to 5 images.");
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
// Compress multiple images
|
|
452
|
+
const compressedImages = await Promise.all(images.map(async (img) => ({
|
|
453
|
+
...img,
|
|
454
|
+
path: await compressImage(img.path),
|
|
455
|
+
})));
|
|
456
|
+
onComplete(compressedImages);
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
// Single image with crop
|
|
460
|
+
const response = await ImageCropPicker.openPicker({
|
|
461
|
+
mediaType: "photo",
|
|
462
|
+
});
|
|
463
|
+
const cropped = await ImageCropPicker.openCropper({
|
|
464
|
+
path: response?.path,
|
|
465
|
+
width: response?.width,
|
|
466
|
+
height: response?.height,
|
|
467
|
+
mediaType: "photo",
|
|
468
|
+
freeStyleCropEnabled: true,
|
|
469
|
+
});
|
|
470
|
+
const compressedPath = await compressImage(cropped.path);
|
|
471
|
+
onComplete({ ...cropped, path: compressedPath });
|
|
472
|
+
}
|
|
149
473
|
}
|
|
150
|
-
catch (
|
|
151
|
-
console.log("Gallery
|
|
474
|
+
catch (error) {
|
|
475
|
+
console.log("Gallery cancelled or error:", error);
|
|
152
476
|
}
|
|
153
477
|
}
|
|
154
|
-
// VIDEO
|
|
155
478
|
else {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
479
|
+
// VIDEO FROM GALLERY
|
|
480
|
+
launchImageLibrary({
|
|
481
|
+
mediaType: "video",
|
|
482
|
+
assetRepresentationMode: "current",
|
|
483
|
+
}, async (result) => {
|
|
484
|
+
if (result?.assets?.length) {
|
|
485
|
+
const videoUri = result.assets[0]?.uri;
|
|
486
|
+
const compressedPath = await compressVideo(videoUri);
|
|
487
|
+
onComplete({
|
|
488
|
+
path: compressedPath,
|
|
489
|
+
duration: result.assets[0]?.duration,
|
|
490
|
+
});
|
|
491
|
+
}
|
|
165
492
|
});
|
|
166
493
|
}
|
|
167
494
|
});
|
|
168
495
|
};
|
|
169
|
-
//--------------------------------------
|
|
170
|
-
// RETURN UI
|
|
171
|
-
//--------------------------------------
|
|
172
496
|
return (React.createElement(BottomSheet, { visible: visible, onClose: onClose, height: 230 },
|
|
173
497
|
React.createElement(View, { style: styles.container },
|
|
174
498
|
React.createElement(TouchableOpacity, { style: styles.row, onPress: handleCamera },
|
|
@@ -184,23 +508,13 @@ const ImagePicker = ({ mediaType, visible, onSuccess, onClose, enableCompression
|
|
|
184
508
|
React.createElement(Text, { style: styles.text }, "Cancel")))));
|
|
185
509
|
};
|
|
186
510
|
export default ImagePicker;
|
|
187
|
-
//--------------------------------------
|
|
188
|
-
// STYLES
|
|
189
|
-
//--------------------------------------
|
|
190
511
|
const styles = StyleSheet.create({
|
|
191
512
|
container: { flex: 1, padding: 16 },
|
|
192
513
|
row: {
|
|
193
514
|
flexDirection: "row",
|
|
194
515
|
alignItems: "center",
|
|
195
|
-
justifyContent: "center",
|
|
196
|
-
paddingVertical: 14,
|
|
197
|
-
},
|
|
198
|
-
text: {
|
|
199
|
-
...Typography.style.standardU(),
|
|
200
|
-
color: Colors.white,
|
|
201
|
-
marginLeft: 10,
|
|
202
|
-
textAlign: "center",
|
|
203
516
|
},
|
|
517
|
+
text: { ...Typography.style.standardU(), color: Colors.white },
|
|
204
518
|
icon: {
|
|
205
519
|
width: Scale.moderateScale(20),
|
|
206
520
|
height: Scale.moderateScale(20),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teamnhz/rn-ui-toolkit",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.5",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"react-native-date-picker": "^5.0.13",
|
|
21
21
|
"react-native-element-dropdown": "^2.12.4",
|
|
22
22
|
"react-native-elements": "^3.4.3",
|
|
23
|
-
"react-native-fast-image": "^8.
|
|
23
|
+
"@d11/react-native-fast-image": "^8.12.0",
|
|
24
24
|
"react-native-gesture-handler": "^2.26.0",
|
|
25
25
|
"react-native-image-crop-picker": "^0.42.0",
|
|
26
26
|
"react-native-image-picker": "^8.2.0",
|