@teamnhz/rn-ui-toolkit 1.1.6 → 1.1.8

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,17 +1,2 @@
1
- type AppHeaderProps = {
2
- navigation: any;
3
- headerTitle?: string;
4
- titleStyle?: object;
5
- leftImage?: any;
6
- onPressLeft?: () => void;
7
- leftIconStyle?: object;
8
- rightImageOne?: any;
9
- onPressRightOne?: () => void;
10
- rightImageOneStyle?: object;
11
- rightImageTwo?: any;
12
- onPressRightTwo?: () => void;
13
- rightImageTwoStyle?: object;
14
- headerStyle?: object;
15
- };
16
- declare const AppHeader: (props: AppHeaderProps) => void;
1
+ declare const AppHeader: (props: any) => any;
17
2
  export default AppHeader;
@@ -1,45 +1,66 @@
1
- import { View, TouchableOpacity, StyleSheet } from "react-native";
2
1
  import React from "react";
2
+ import { View, TouchableOpacity, StyleSheet, Platform, StatusBar, } from "react-native";
3
3
  import { Colors } from "../../styles";
4
4
  import { Image, T } from "../index";
5
+ const getSafeAreaTop = () => {
6
+ if (Platform.OS === "android") {
7
+ return StatusBar.currentHeight || 0;
8
+ }
9
+ return 20; // iOS safe area fallback
10
+ };
5
11
  const AppHeader = (props) => {
6
12
  const { navigation, headerTitle, titleStyle, leftImage, onPressLeft, leftIconStyle, rightImageOne, onPressRightOne, rightImageOneStyle, rightImageTwo, onPressRightTwo, rightImageTwoStyle, headerStyle, } = props;
13
+ const safeTop = getSafeAreaTop();
7
14
  navigation.setOptions({
15
+ headerLeftContainerStyle: {
16
+ paddingLeft: 12,
17
+ paddingBottom: Platform.OS === "ios" ? 4 : 0,
18
+ },
19
+ headerRightContainerStyle: {
20
+ paddingRight: 12,
21
+ paddingBottom: Platform.OS === "ios" ? 4 : 0,
22
+ },
23
+ headerTitleContainerStyle: {
24
+ paddingBottom: Platform.OS === "ios" ? 4 : 0,
25
+ },
26
+ headerStyle: [
27
+ styles.headerBase,
28
+ { paddingTop: safeTop },
29
+ headerStyle,
30
+ ],
8
31
  headerLeft: () => leftImage ? (React.createElement(TouchableOpacity, { onPress: onPressLeft, activeOpacity: 0.8 },
9
- React.createElement(Image, { source: leftImage, resizeMode: "contain", style: [styles.leftImage, leftIconStyle] }))) : null,
32
+ React.createElement(Image, { source: leftImage, resizeMode: "contain", style: [styles.icon, leftIconStyle] }))) : null,
10
33
  headerTitle: () => headerTitle ? (React.createElement(T, { title: headerTitle, style: [styles.headerTitleText, titleStyle] })) : null,
11
34
  headerRight: () => (React.createElement(View, { style: styles.headerRightWrapper },
12
35
  rightImageOne ? (React.createElement(TouchableOpacity, { onPress: onPressRightOne, activeOpacity: 0.8 },
13
- React.createElement(Image, { source: rightImageOne, resizeMode: "contain", style: [
14
- styles.rightImage,
15
- rightImageOneStyle,
16
- { marginRight: 10 },
17
- ] }))) : null,
36
+ React.createElement(Image, { source: rightImageOne, resizeMode: "contain", style: [styles.icon, rightImageOneStyle] }))) : null,
18
37
  rightImageTwo ? (React.createElement(TouchableOpacity, { onPress: onPressRightTwo, activeOpacity: 0.8 },
19
- React.createElement(Image, { source: rightImageTwo, resizeMode: "contain", style: [styles.rightImage, rightImageTwoStyle] }))) : null)),
20
- headerStyle: [styles.headerBase, headerStyle],
38
+ React.createElement(Image, { source: rightImageTwo, resizeMode: "contain", style: [styles.icon, rightImageTwoStyle] }))) : null)),
21
39
  });
40
+ return null;
22
41
  };
23
42
  export default AppHeader;
24
43
  const styles = StyleSheet.create({
25
44
  headerBase: {
26
- height: 100,
27
- width: "100%",
28
- },
29
- leftImage: {
30
- height: 44,
31
- width: 44,
45
+ backgroundColor: Colors.white,
46
+ borderBottomWidth: 1,
47
+ borderBottomColor: Colors.lightGrey,
48
+ height: Platform.OS === "ios" ? 90 : 70,
49
+ justifyContent: "center",
32
50
  },
33
- rightImage: {
34
- height: 44,
35
- width: 44,
51
+ icon: {
52
+ height: 28,
53
+ width: 28,
36
54
  },
37
55
  headerRightWrapper: {
38
56
  flexDirection: "row",
39
57
  alignItems: "center",
58
+ gap: 12,
40
59
  },
41
60
  headerTitleText: {
42
61
  color: Colors.black,
43
62
  textAlign: "center",
63
+ fontSize: 18,
64
+ fontWeight: "600",
44
65
  },
45
66
  });
@@ -2,9 +2,10 @@ import React from "react";
2
2
  type Props = {
3
3
  mediaType: "photo" | "video";
4
4
  isMultiSelect?: boolean;
5
- onSuccess: (data: any) => void;
5
+ onSuccess: (response: any) => void;
6
6
  visible: boolean;
7
7
  onClose: () => void;
8
+ enableCompression?: boolean;
8
9
  };
9
10
  declare const ImagePicker: React.FC<Props>;
10
11
  export default ImagePicker;
@@ -6,38 +6,49 @@
6
6
  // StyleSheet,
7
7
  // Image,
8
8
  // Alert,
9
- // ViewStyle,
10
- // TextStyle,
11
- // ImageStyle,
12
9
  // } from "react-native";
13
10
  // import { BottomSheet, Dividers } from "../index";
14
11
  // import { launchCamera, launchImageLibrary } from "react-native-image-picker";
15
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";
16
18
  // import { Colors, Images, Scale, Typography } from "../../styles";
17
- // type StyleProps = {
18
- // containerStyle?: ViewStyle;
19
- // rowStyle?: ViewStyle;
20
- // textStyle?: TextStyle;
21
- // iconStyle?: ImageStyle;
22
- // };
19
+ // // Import your permission utilities
20
+ // import {
21
+ // cameraPermissions,
22
+ // galleryPermissions,
23
+ // checkMicroPhonePermission,
24
+ // } from "../../utils/permissions";
23
25
  // type Props = {
24
26
  // mediaType: "photo" | "video";
25
27
  // isMultiSelect?: boolean;
26
28
  // onSuccess: (data: any) => void;
27
29
  // visible: boolean;
28
30
  // onClose: () => void;
29
- // } & StyleProps;
31
+ // // Compression options
32
+ // enableCompression?: boolean;
33
+ // imageCompressionOptions?: {
34
+ // maxWidth?: number;
35
+ // quality?: number;
36
+ // };
37
+ // videoCompressionOptions?: {
38
+ // compressionMethod?: "auto" | "manual";
39
+ // };
40
+ // };
30
41
  // const ImagePicker: React.FC<Props> = ({
31
42
  // mediaType,
32
43
  // isMultiSelect = false,
33
44
  // onSuccess,
34
45
  // visible,
35
46
  // onClose,
36
- // containerStyle,
37
- // rowStyle,
38
- // textStyle,
39
- // iconStyle,
47
+ // enableCompression = true,
48
+ // imageCompressionOptions = { maxWidth: 1080, quality: 0.7 },
49
+ // videoCompressionOptions = { compressionMethod: "auto" },
40
50
  // }) => {
51
+ // // Success handler
41
52
  // const onComplete = useCallback(
42
53
  // (data: any) => {
43
54
  // onSuccess(data);
@@ -45,98 +56,184 @@
45
56
  // },
46
57
  // [onSuccess, onClose]
47
58
  // );
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({
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({
58
110
  // path: response?.path,
59
111
  // width: response?.width,
60
112
  // height: response?.height,
61
113
  // mediaType: "photo",
62
114
  // 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
- // });
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);
74
121
  // }
75
- // });
76
- // }
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
+ // });
77
151
  // };
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) => {
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
+ // });
87
164
  // if (images.length > 5) {
88
165
  // Alert.alert(
89
166
  // "Limit Exceeded",
90
167
  // "You can only select up to 5 images."
91
168
  // );
92
- // } else {
93
- // onComplete(images);
169
+ // return;
94
170
  // }
95
- // })
96
- // .catch(() => {});
97
- // } else {
98
- // ImageCropPicker.openPicker({ mediaType: "photo" })
99
- // .then((response) =>
100
- // ImageCropPicker.openCropper({
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({
101
185
  // path: response?.path,
102
186
  // width: response?.width,
103
187
  // height: response?.height,
104
188
  // mediaType: "photo",
105
189
  // 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
- // });
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);
117
196
  // }
118
- // });
119
- // }
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
+ // });
120
217
  // };
121
218
  // return (
122
219
  // <BottomSheet visible={visible} onClose={onClose} height={230}>
123
- // <View style={[styles.container, containerStyle]}>
220
+ // <View style={styles.container}>
124
221
  // {/* 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>
222
+ // <TouchableOpacity style={styles.row} onPress={handleCamera}>
223
+ // <Image source={Images.video_icon} style={styles.icon} />
224
+ // <Text style={styles.text}>Camera</Text>
128
225
  // </TouchableOpacity>
129
226
  // <Dividers small />
130
227
  // {/* 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>
228
+ // <TouchableOpacity style={styles.row} onPress={handleGallery}>
229
+ // <Image source={Images.image_icon} style={styles.icon} />
230
+ // <Text style={styles.text}>Gallery</Text>
134
231
  // </TouchableOpacity>
135
232
  // <Dividers small />
136
233
  // {/* 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>
234
+ // <TouchableOpacity style={styles.row} onPress={onClose}>
235
+ // <Image source={Images.cancel} style={styles.icon} />
236
+ // <Text style={styles.text}>Cancel</Text>
140
237
  // </TouchableOpacity>
141
238
  // </View>
142
239
  // </BottomSheet>
@@ -157,100 +254,121 @@
157
254
  // tintColor: Colors.white,
158
255
  // },
159
256
  // });
160
- import React, { useCallback } from "react";
161
- import { View, Text, TouchableOpacity, StyleSheet, Image, Alert, } from "react-native";
257
+ import React from "react";
258
+ import { View, Text, TouchableOpacity, StyleSheet, Image, } from "react-native";
162
259
  import { BottomSheet, Dividers } from "../index";
163
260
  import { launchCamera, launchImageLibrary } from "react-native-image-picker";
164
261
  import ImageCropPicker from "react-native-image-crop-picker";
262
+ import { Image as ImageCompressor, Video as VideoCompressor, } from "react-native-compressor";
165
263
  import { Colors, Images, Scale, Typography } from "../../styles";
166
- // 👇 Import your permission utilities
167
- import { cameraPermissions, galleryPermissions, } from "../../utils/permissions"; // Make sure path is correct
168
- const ImagePicker = ({ mediaType, isMultiSelect = false, onSuccess, visible, onClose, }) => {
169
- // Success handler
170
- const onComplete = useCallback((data) => {
171
- onSuccess(data);
264
+ 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) => {
172
268
  onClose();
173
- }, [onSuccess, onClose]);
174
- // ✅ CAMERA HANDLER with permission check
269
+ onSuccess({ loading: true, data: rawData });
270
+ };
271
+ // After compression finished
272
+ const sendFinalCompressed = (compressed) => {
273
+ onSuccess({ loading: false, data: compressed });
274
+ };
275
+ const compressImage = async (path) => {
276
+ try {
277
+ const compressed = await ImageCompressor.compress(path, {
278
+ maxWidth: 1080,
279
+ quality: 0.7,
280
+ });
281
+ return compressed;
282
+ }
283
+ catch (e) {
284
+ return path;
285
+ }
286
+ };
287
+ const compressVideo = async (uri) => {
288
+ try {
289
+ const compressed = await VideoCompressor.compress(uri, {
290
+ compressionMethod: "auto",
291
+ });
292
+ return compressed;
293
+ }
294
+ catch (e) {
295
+ return uri;
296
+ }
297
+ };
298
+ // ---------- CAMERA ----------
175
299
  const handleCamera = async () => {
176
300
  await cameraPermissions(async (granted) => {
177
301
  if (!granted)
178
- return; // ❌ Permission denied, handled by your alert
302
+ return;
179
303
  if (mediaType === "photo") {
180
- if (isMultiSelect) {
181
- // single capture, multiple not supported directly from camera
182
- ImageCropPicker.openCamera({ mediaType: "photo" })
183
- .then((response) => onComplete([response]))
184
- .catch(() => { });
185
- }
186
- else {
187
- ImageCropPicker.openCamera({ mediaType: "photo" })
188
- .then((response) => ImageCropPicker.openCropper({
189
- path: response?.path,
190
- width: response?.width,
191
- height: response?.height,
304
+ try {
305
+ const picked = await ImageCropPicker.openCamera({
192
306
  mediaType: "photo",
193
- freeStyleCropEnabled: true,
194
- }).then(onComplete))
195
- .catch(() => { });
307
+ });
308
+ sendRawAndClose(picked); // CLOSE MODAL + START LOADER
309
+ setTimeout(async () => {
310
+ const compressedPath = await compressImage(picked.path);
311
+ sendFinalCompressed({ ...picked, path: compressedPath });
312
+ }, 10);
313
+ }
314
+ catch (err) {
315
+ console.log(err);
196
316
  }
197
317
  }
198
318
  else {
199
- // 🎥 VIDEO CAPTURE
200
- launchCamera({ mediaType: "video" }, (response) => {
201
- if (response?.assets?.length) {
202
- onComplete({
203
- path: response.assets[0]?.uri,
204
- duration: response.assets[0]?.duration,
319
+ // VIDEO
320
+ const mic = await checkMicroPhonePermission();
321
+ if (!mic)
322
+ return;
323
+ launchCamera({ mediaType: "video", durationLimit: 60, videoQuality: "high" }, (res) => {
324
+ if (!res?.assets?.length)
325
+ 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,
205
333
  });
206
- }
334
+ }, 10);
207
335
  });
208
336
  }
209
337
  });
210
338
  };
211
- // GALLERY HANDLER with permission check
339
+ // ---------- GALLERY ----------
212
340
  const handleGallery = async () => {
213
341
  await galleryPermissions(async (granted) => {
214
342
  if (!granted)
215
- return; // ❌ Permission denied, handled by your alert
343
+ return;
216
344
  if (mediaType === "photo") {
217
- if (isMultiSelect) {
218
- ImageCropPicker.openPicker({
345
+ try {
346
+ const picked = await ImageCropPicker.openPicker({
219
347
  mediaType: "photo",
220
- multiple: true,
221
- maxFiles: 5,
222
- })
223
- .then((images) => {
224
- if (images.length > 5) {
225
- Alert.alert("Limit Exceeded", "You can only select up to 5 images.");
226
- }
227
- else {
228
- onComplete(images);
229
- }
230
- })
231
- .catch(() => { });
348
+ });
349
+ sendRawAndClose(picked);
350
+ setTimeout(async () => {
351
+ const compressed = await compressImage(picked.path);
352
+ sendFinalCompressed({ ...picked, path: compressed });
353
+ }, 10);
232
354
  }
233
- else {
234
- ImageCropPicker.openPicker({ mediaType: "photo" })
235
- .then((response) => ImageCropPicker.openCropper({
236
- path: response?.path,
237
- width: response?.width,
238
- height: response?.height,
239
- mediaType: "photo",
240
- freeStyleCropEnabled: true,
241
- }).then(onComplete))
242
- .catch(() => { });
355
+ catch (e) {
356
+ console.log(e);
243
357
  }
244
358
  }
245
359
  else {
246
- // 🎥 VIDEO FROM GALLERY
247
- launchImageLibrary({ mediaType: "video" }, (result) => {
248
- if (result?.assets?.length) {
249
- onComplete({
250
- path: result.assets[0]?.uri,
251
- duration: result.assets[0]?.duration,
360
+ launchImageLibrary({ mediaType: "video" }, (res) => {
361
+ if (!res?.assets?.length)
362
+ 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,
252
370
  });
253
- }
371
+ }, 10);
254
372
  });
255
373
  }
256
374
  });
@@ -272,10 +390,7 @@ const ImagePicker = ({ mediaType, isMultiSelect = false, onSuccess, visible, onC
272
390
  export default ImagePicker;
273
391
  const styles = StyleSheet.create({
274
392
  container: { flex: 1, padding: 16 },
275
- row: {
276
- flexDirection: "row",
277
- alignItems: "center",
278
- },
393
+ row: { flexDirection: "row", alignItems: "center" },
279
394
  text: { ...Typography.style.standardU(), color: Colors.white },
280
395
  icon: {
281
396
  width: Scale.moderateScale(20),
@@ -35,6 +35,7 @@ interface InputProps extends TextInputProps {
35
35
  onPressCountryCode?: () => void;
36
36
  countryCodeWrapperStyle?: object;
37
37
  countryCodeTextStyle?: object;
38
+ isError?: boolean;
38
39
  }
39
40
  declare const Input: React.FC<InputProps>;
40
41
  export default Input;
@@ -4,7 +4,7 @@ import { useTranslation } from "react-i18next";
4
4
  import { Colors, Typography, Scale, Images } from "../../styles";
5
5
  import { defaultRegex } from "../../utils/regex";
6
6
  import { messages } from "../../constants/messages";
7
- const Input = ({ intlType, textKey, placeholder, inputPlaceholderTextColor, leftIcon, rightIcon, onLeftIconPress, onRightIconPress, style, containerStyle, inputStyle, errorTextStyle, leftIconStyle, rightIconStyle, leftIconWrapperStyle, rightIconWrapperStyle, type = "text", editable = true, error, value = "", customRegex, customErrorMessage, onChangeText, onValidation, onBlur: restOnBlur, onFocus: restOnFocus, multiline = false, maxLength, returnKeyType, countryCode, onPressCountryCode, countryCodeTextStyle, countryCodeWrapperStyle, ...rest }) => {
7
+ const Input = ({ intlType, textKey, placeholder, inputPlaceholderTextColor, leftIcon, rightIcon, onLeftIconPress, onRightIconPress, style, containerStyle, inputStyle, errorTextStyle, leftIconStyle, rightIconStyle, leftIconWrapperStyle, rightIconWrapperStyle, type = "text", editable = true, error, value = "", customRegex, customErrorMessage, onChangeText, onValidation, onBlur: restOnBlur, onFocus: restOnFocus, multiline = false, maxLength, returnKeyType, countryCode, onPressCountryCode, countryCodeTextStyle, countryCodeWrapperStyle, isError = true, ...rest }) => {
8
8
  const { t } = useTranslation();
9
9
  const [secureText, setSecureText] = useState(type === "password");
10
10
  const [internalError, setInternalError] = useState(null);
@@ -97,7 +97,7 @@ const Input = ({ intlType, textKey, placeholder, inputPlaceholderTextColor, left
97
97
  type === "password" ? (React.createElement(TouchableOpacity, { onPress: () => setSecureText(!secureText), style: [styles.iconWrapper, rightIconWrapperStyle] },
98
98
  React.createElement(Image, { source: secureText ? Images.Eyeoff : Images.Eyeon, style: [styles.icon, rightIconStyle], resizeMode: "contain" }))) : (rightIcon && (React.createElement(TouchableOpacity, { onPress: onRightIconPress, disabled: !onRightIconPress, style: [styles.iconWrapper, rightIconWrapperStyle] },
99
99
  React.createElement(Image, { source: rightIcon, style: [styles.icon, rightIconStyle], resizeMode: "contain" }))))),
100
- showError && (React.createElement(Text, { style: [styles.errorText, errorTextStyle] }, showError))));
100
+ showError && isError && (React.createElement(Text, { style: [styles.errorText, errorTextStyle] }, showError))));
101
101
  };
102
102
  export default Input;
103
103
  const styles = StyleSheet.create({
@@ -167,239 +167,3 @@ const styles = StyleSheet.create({
167
167
  color: Colors.textColor,
168
168
  },
169
169
  });
170
- // import React, { useState, useCallback, useEffect } from "react";
171
- // import {
172
- // View,
173
- // TextInput,
174
- // TextInputProps,
175
- // StyleSheet,
176
- // Image,
177
- // ImageSourcePropType,
178
- // TouchableOpacity,
179
- // Text,
180
- // NativeSyntheticEvent,
181
- // TextInputFocusEventData,
182
- // } from "react-native";
183
- // import { useTranslation } from "react-i18next";
184
- // import { Colors, Typography, Scale, Images } from "../../styles";
185
- // import { defaultRegex, InputType } from "../../utils/regex";
186
- // import { messages } from "../../constants/messages";
187
- // interface InputProps extends TextInputProps {
188
- // intlType?: string;
189
- // textKey?: string;
190
- // leftIcon?: ImageSourcePropType;
191
- // rightIcon?: ImageSourcePropType;
192
- // onLeftIconPress?: () => void;
193
- // onRightIconPress?: () => void;
194
- // type?: InputType;
195
- // editable?: boolean;
196
- // error?: string | boolean;
197
- // value?: string;
198
- // customRegex?: RegExp;
199
- // customErrorMessage?: string;
200
- // onChangeText?: (text: string) => void;
201
- // onValidation?: (errorObj: { type: InputType; hasError: boolean; message?: string }) => void;
202
- // containerStyle?: object;
203
- // inputStyle?: object;
204
- // errorTextStyle?: object;
205
- // leftIconStyle?: object;
206
- // rightIconStyle?: object;
207
- // }
208
- // const Input: React.FC<InputProps> = ({
209
- // intlType,
210
- // textKey,
211
- // placeholder,
212
- // leftIcon,
213
- // rightIcon,
214
- // onLeftIconPress,
215
- // onRightIconPress,
216
- // style,
217
- // containerStyle,
218
- // inputStyle,
219
- // errorTextStyle,
220
- // leftIconStyle,
221
- // rightIconStyle,
222
- // type = "text",
223
- // editable = true,
224
- // error,
225
- // value = "",
226
- // customRegex,
227
- // customErrorMessage,
228
- // onChangeText,
229
- // onValidation,
230
- // onBlur: restOnBlur,
231
- // onFocus: restOnFocus,
232
- // multiline = false,
233
- // ...rest
234
- // }) => {
235
- // const { t } = useTranslation();
236
- // const [secureText, setSecureText] = useState(type === "password");
237
- // const [internalError, setInternalError] = useState<string | null>(null);
238
- // const [wasBlurred, setWasBlurred] = useState(false);
239
- // const [isFocused, setIsFocused] = useState(false);
240
- // const translatedPlaceholder =
241
- // intlType && textKey ? String(t(textKey, { ns: intlType, value })) : placeholder;
242
- // const keyboardType: TextInputProps["keyboardType"] =
243
- // type === "email" ? "email-address" : type === "number" ? "numeric" : "default";
244
- // const validateInternal = useCallback(
245
- // (text: string) => {
246
- // if (!wasBlurred || !editable) return;
247
- // const regexToUse = customRegex ?? defaultRegex[type];
248
- // const fallbackMsg = messages.invalid[type] || "Invalid input";
249
- // const msg = customErrorMessage || fallbackMsg;
250
- // if (text === "" || !regexToUse.test(text)) {
251
- // setInternalError(msg);
252
- // onValidation?.({ type, hasError: true, message: msg });
253
- // } else {
254
- // setInternalError(null);
255
- // onValidation?.({ type, hasError: false });
256
- // }
257
- // },
258
- // [wasBlurred, editable, type, customRegex, customErrorMessage, onValidation]
259
- // );
260
- // useEffect(() => {
261
- // if (!customRegex) return;
262
- // if (internalError && value && customRegex.test(value)) {
263
- // setInternalError(null);
264
- // onValidation?.({ type, hasError: false });
265
- // }
266
- // }, [value]);
267
- // const handleChangeText = (text: string) => {
268
- // onChangeText?.(text);
269
- // if (wasBlurred) validateInternal(text);
270
- // };
271
- // const handleFocus = (e?: NativeSyntheticEvent<TextInputFocusEventData>) => {
272
- // setIsFocused(true);
273
- // restOnFocus?.(e as any);
274
- // };
275
- // const handleBlur = (e?: NativeSyntheticEvent<TextInputFocusEventData>) => {
276
- // setIsFocused(false);
277
- // if (!wasBlurred) setWasBlurred(true);
278
- // if (editable) validateInternal(value || "");
279
- // restOnBlur?.(e as any);
280
- // };
281
- // const rawError = (typeof error === "string" ? error : undefined) ?? internalError ?? null;
282
- // const showError = (() => {
283
- // if (!rawError || !editable) return null;
284
- // return wasBlurred && !isFocused ? rawError : null;
285
- // })();
286
- // return (
287
- // <View style={{ marginBottom: 10 }}>
288
- // <View
289
- // style={[
290
- // styles.container,
291
- // !editable && styles.disabled,
292
- // !!showError && styles.errorBorder,
293
- // isFocused && styles.focusedBorder,
294
- // style,
295
- // containerStyle,
296
- // multiline && styles.multilineContainer,
297
- // ]}
298
- // >
299
- // {leftIcon && (
300
- // <TouchableOpacity
301
- // onPress={onLeftIconPress}
302
- // disabled={!onLeftIconPress}
303
- // style={styles.iconWrapper}
304
- // >
305
- // <Image source={leftIcon} style={[styles.icon, leftIconStyle]} resizeMode="contain" />
306
- // </TouchableOpacity>
307
- // )}
308
- // <TextInput
309
- // style={[
310
- // styles.input,
311
- // !editable && styles.disabledText,
312
- // multiline && styles.multilineInput,
313
- // inputStyle,
314
- // ]}
315
- // placeholder={translatedPlaceholder}
316
- // placeholderTextColor={Colors.textGrey}
317
- // editable={editable}
318
- // secureTextEntry={secureText}
319
- // keyboardType={keyboardType}
320
- // value={value}
321
- // onChangeText={handleChangeText}
322
- // onFocus={handleFocus}
323
- // onBlur={handleBlur}
324
- // multiline={multiline}
325
- // {...rest}
326
- // />
327
- // {type === "password" ? (
328
- // <TouchableOpacity onPress={() => setSecureText(!secureText)} style={styles.iconWrapper}>
329
- // <Image
330
- // source={secureText ? Images.Eyeoff : Images.Eyeon}
331
- // style={[styles.icon, rightIconStyle]}
332
- // resizeMode="contain"
333
- // />
334
- // </TouchableOpacity>
335
- // ) : (
336
- // rightIcon && (
337
- // <TouchableOpacity
338
- // onPress={onRightIconPress}
339
- // disabled={!onRightIconPress}
340
- // style={styles.iconWrapper}
341
- // >
342
- // <Image source={rightIcon} style={[styles.icon, rightIconStyle]} resizeMode="contain" />
343
- // </TouchableOpacity>
344
- // )
345
- // )}
346
- // </View>
347
- // {showError && <Text style={[styles.errorText, errorTextStyle]}>{showError}</Text>}
348
- // </View>
349
- // );
350
- // };
351
- // export default Input;
352
- // const styles = StyleSheet.create({
353
- // container: {
354
- // flexDirection: "row",
355
- // alignItems: "center",
356
- // borderRadius: 5,
357
- // borderWidth: 1,
358
- // borderColor: Colors.primaryColor,
359
- // backgroundColor: Colors.white,
360
- // height: Scale.moderateScale(50),
361
- // paddingHorizontal: Scale.moderateScale(4),
362
- // },
363
- // multilineContainer: {
364
- // height: "auto",
365
- // minHeight: Scale.moderateScale(80),
366
- // alignItems: "flex-start",
367
- // paddingVertical: Scale.moderateScale(8),
368
- // },
369
- // focusedBorder: {
370
- // borderColor: Colors.primaryColor,
371
- // },
372
- // input: {
373
- // flex: 1,
374
- // ...Typography.style.standardU(),
375
- // textTransform: "none",
376
- // color: Colors.textColor,
377
- // },
378
- // multilineInput: {
379
- // textAlignVertical: "top",
380
- // paddingTop: Scale.moderateScale(5),
381
- // },
382
- // iconWrapper: {
383
- // padding: Scale.moderateScale(5),
384
- // },
385
- // icon: {
386
- // width: Scale.moderateScale(20),
387
- // height: Scale.moderateScale(20),
388
- // tintColor: Colors.primaryColor,
389
- // },
390
- // disabled: {
391
- // backgroundColor: Colors.lightGrey,
392
- // borderColor: Colors.borderGrey,
393
- // },
394
- // disabledText: {
395
- // color: Colors.disabledGrey,
396
- // },
397
- // errorBorder: {
398
- // borderColor: Colors.dangerRed,
399
- // },
400
- // errorText: {
401
- // color: Colors.dangerRed,
402
- // fontSize: Scale.moderateScale(12),
403
- // marginTop: 3,
404
- // },
405
- // });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teamnhz/rn-ui-toolkit",
3
- "version": "1.1.6",
3
+ "version": "1.1.8",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [
@@ -31,7 +31,9 @@
31
31
  "react-native-safe-area-context": "^5.6.1",
32
32
  "react-native-svg": "^15.12.1",
33
33
  "react-native-vector-icons": "^10.3.0",
34
- "react-native-video-trim": "^3.0.9"
34
+ "react-native-video-trim": "^3.0.9",
35
+ "react-native-compressor": "^1.13.0",
36
+ "react-native-fs": "^2.20.0"
35
37
  },
36
38
  "devDependencies": {
37
39
  "@react-native-documents/picker": "^10.1.5",