@widergy/mobile-ui 1.51.0 → 2.0.1
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.
- package/CHANGELOG.md +18 -0
- package/lib/components/Carousel/components/CarouselContainer/index.js +5 -3
- package/lib/components/FilePicker/constants.js +1 -4
- package/lib/components/FilePicker/index.js +33 -17
- package/lib/components/ImagePicker/ModalSelectionOption/index.js +14 -2
- package/lib/components/ImagePicker/index.js +5 -1
- package/lib/components/ImagePicker/layout.js +65 -5
- package/lib/components/MultipleFilePicker/index.js +275 -35
- package/lib/components/MultipleFilePicker/propTypes.js +16 -1
- package/lib/components/MultipleFilePicker/utils.js +44 -13
- package/lib/components/Overlay/index.js +8 -3
- package/lib/components/PhotoAlbum/index.js +28 -4
- package/lib/components/RateChart/components/RateStagesGraph/components/Bars/index.js +5 -4
- package/lib/components/UTActionCard/components/Header/index.js +1 -0
- package/lib/components/UTActionCard/components/Header/utils.js +4 -1
- package/lib/components/UTCheckBox/index.js +3 -15
- package/lib/components/UTCheckBox/theme.js +4 -18
- package/lib/components/UTCheckList/index.js +11 -7
- package/lib/components/UTDetailDrawer/index.js +4 -4
- package/lib/components/UTIcon/index.js +0 -1
- package/lib/components/UTIcon/utils.js +3 -3
- package/lib/components/UTModal/index.js +2 -0
- package/lib/components/UTRating/index.js +4 -3
- package/lib/components/UTRating/styles.js +3 -1
- package/lib/components/UTTabs/index.js +14 -6
- package/lib/components/UTTabs/styles.js +51 -19
- package/lib/components/UTTopbar/index.js +3 -5
- package/lib/components/UTTracker/components/Step/styles.js +8 -4
- package/lib/components/UTWorkflowContainer/versions/V1/index.js +19 -4
- package/lib/reactotronConfig.js +36 -3
- package/lib/utils/fileUtils.js/index.js +15 -10
- package/package.json +12 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
## [2.0.1](https://github.com/widergy/mobile-ui/compare/v2.0.0...v2.0.1) (2025-10-06)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* [CX-1176] changes action card widy ([162e236](https://github.com/widergy/mobile-ui/commit/162e2363a7fd65912967d15513fd11456ff73cba))
|
|
7
|
+
* add suggestion ([db892b5](https://github.com/widergy/mobile-ui/commit/db892b54bd689d80af05ba869c25022c55fa539d))
|
|
8
|
+
|
|
9
|
+
# [2.0.0](https://github.com/widergy/mobile-ui/compare/v1.51.0...v2.0.0) (2025-09-22)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
* feat!: [OUG-8855] update sdk (#453) ([ac9d0d3](https://github.com/widergy/mobile-ui/commit/ac9d0d3c5033f1bce8641b30f7afbdd15ae9857b)), closes [#453](https://github.com/widergy/mobile-ui/issues/453)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### BREAKING CHANGES
|
|
16
|
+
|
|
17
|
+
* updates core dependencies
|
|
18
|
+
|
|
1
19
|
# [1.51.0](https://github.com/widergy/mobile-ui/compare/v1.50.1...v1.51.0) (2025-09-18)
|
|
2
20
|
|
|
3
21
|
|
|
@@ -20,9 +20,11 @@ const CarouselContainer = ({ style, ...props }, ref) => {
|
|
|
20
20
|
]}
|
|
21
21
|
>
|
|
22
22
|
{single ? (
|
|
23
|
-
|
|
24
|
-
{
|
|
25
|
-
|
|
23
|
+
items.map((item, i) => (
|
|
24
|
+
<CarouselItem key={`item-${i + 1}`} {...itemProps} single>
|
|
25
|
+
{renderItem(item)}
|
|
26
|
+
</CarouselItem>
|
|
27
|
+
))
|
|
26
28
|
) : (
|
|
27
29
|
<CarouselComponent ref={ref} {...props} />
|
|
28
30
|
)}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
// eslint-disable-next-line import/no-unresolved
|
|
2
|
-
import DocumentPicker from 'react-native-document-picker';
|
|
3
|
-
|
|
4
1
|
import { MEGABYTE } from '../../utils/fileUtils.js';
|
|
5
2
|
|
|
6
3
|
export const UPLOAD_ICON = 'file-upload';
|
|
7
4
|
|
|
8
|
-
export const DEFAULT_ALLOWED_TYPES = [
|
|
5
|
+
export const DEFAULT_ALLOWED_TYPES = ['*/*'];
|
|
9
6
|
|
|
10
7
|
export const DEFAULT_MAX_SIZE = 10 * MEGABYTE;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import React, { Component } from 'react';
|
|
2
2
|
import { View } from 'react-native';
|
|
3
|
-
// eslint-disable-next-line import/no-unresolved
|
|
4
|
-
import DocumentPicker from 'react-native-document-picker';
|
|
5
3
|
import { isEmpty } from '@widergy/web-utils/lib/array';
|
|
4
|
+
import * as ExpoDocumentPicker from 'expo-document-picker';
|
|
6
5
|
|
|
7
6
|
import Picker from '../Picker';
|
|
8
7
|
import { retrieveFile, isImageByUri, blobToFile } from '../../utils/fileUtils.js';
|
|
8
|
+
import { getMimeType } from '../MultipleFilePicker/utils';
|
|
9
9
|
|
|
10
10
|
import { UPLOAD_ICON, DEFAULT_ALLOWED_TYPES, DEFAULT_MAX_SIZE } from './constants';
|
|
11
11
|
import filePickerPropTypes from './propTypes';
|
|
@@ -21,23 +21,46 @@ class FilePicker extends Component {
|
|
|
21
21
|
|
|
22
22
|
handleShowPicker = async () => {
|
|
23
23
|
const {
|
|
24
|
-
allowedTypes,
|
|
24
|
+
allowedTypes = DEFAULT_ALLOWED_TYPES,
|
|
25
25
|
onMaxSizeError,
|
|
26
26
|
onError,
|
|
27
27
|
onChange,
|
|
28
|
-
maxFileByteSize,
|
|
28
|
+
maxFileByteSize = DEFAULT_MAX_SIZE,
|
|
29
29
|
avoidRetrieveFile,
|
|
30
30
|
fileTypeError,
|
|
31
31
|
allowedPDFUploadSizes,
|
|
32
32
|
pdfFormatError
|
|
33
33
|
} = this.props;
|
|
34
34
|
try {
|
|
35
|
-
const
|
|
36
|
-
|
|
35
|
+
const normalizeTypes = types => {
|
|
36
|
+
if (!types || types.length === 0) return DEFAULT_ALLOWED_TYPES;
|
|
37
|
+
const hasMime = types.some(t => typeof t === 'string' && t.includes('/'));
|
|
38
|
+
if (hasMime) return types;
|
|
39
|
+
const mapped = types.map(getMimeType).filter(Boolean);
|
|
40
|
+
return mapped.length ? mapped : DEFAULT_ALLOWED_TYPES;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const pickTypes = !onlyPDFAllowed(allowedTypes) ? normalizeTypes(allowedTypes) : DEFAULT_ALLOWED_TYPES;
|
|
44
|
+
|
|
45
|
+
const result = await ExpoDocumentPicker.getDocumentAsync({
|
|
46
|
+
multiple: false,
|
|
47
|
+
type: pickTypes
|
|
37
48
|
});
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
49
|
+
|
|
50
|
+
if (result.canceled) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const asset = (result.assets && result.assets[0]) || result;
|
|
55
|
+
const document = {
|
|
56
|
+
uri: asset.uri,
|
|
57
|
+
type: asset.mimeType || asset.type || 'application/octet-stream',
|
|
58
|
+
size: asset.size,
|
|
59
|
+
name: asset.name || 'document'
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const isPDF = (document.type || '').includes('pdf');
|
|
63
|
+
const isImage = (document.type || '').includes('image');
|
|
41
64
|
if (onlyPDFAllowed(allowedTypes) && !isPDF) {
|
|
42
65
|
throw new Error(fileTypeError || 'El tipo de archivo debe ser PDF.');
|
|
43
66
|
}
|
|
@@ -66,9 +89,7 @@ class FilePicker extends Component {
|
|
|
66
89
|
}
|
|
67
90
|
this.setState({ fileName: document.name });
|
|
68
91
|
} catch (err) {
|
|
69
|
-
|
|
70
|
-
onError(err.message);
|
|
71
|
-
}
|
|
92
|
+
onError(err.message);
|
|
72
93
|
}
|
|
73
94
|
};
|
|
74
95
|
|
|
@@ -102,11 +123,6 @@ class FilePicker extends Component {
|
|
|
102
123
|
}
|
|
103
124
|
}
|
|
104
125
|
|
|
105
|
-
FilePicker.defaultProps = {
|
|
106
|
-
allowedTypes: DEFAULT_ALLOWED_TYPES,
|
|
107
|
-
maxFileByteSize: DEFAULT_MAX_SIZE
|
|
108
|
-
};
|
|
109
|
-
|
|
110
126
|
FilePicker.propTypes = filePickerPropTypes;
|
|
111
127
|
|
|
112
128
|
export default FilePicker;
|
|
@@ -29,10 +29,22 @@ const ModalSelectionOption = ({
|
|
|
29
29
|
{title}
|
|
30
30
|
</UTLabel>
|
|
31
31
|
<View style={styles.container}>
|
|
32
|
-
<UTButton
|
|
32
|
+
<UTButton
|
|
33
|
+
onPress={() => {
|
|
34
|
+
openOptionSelected(true);
|
|
35
|
+
}}
|
|
36
|
+
style={{ root: styles.button }}
|
|
37
|
+
variant="text"
|
|
38
|
+
>
|
|
33
39
|
{takePhotoButtonTitle}
|
|
34
40
|
</UTButton>
|
|
35
|
-
<UTButton
|
|
41
|
+
<UTButton
|
|
42
|
+
onPress={() => {
|
|
43
|
+
openOptionSelected(false);
|
|
44
|
+
}}
|
|
45
|
+
style={{ root: styles.button }}
|
|
46
|
+
variant="text"
|
|
47
|
+
>
|
|
36
48
|
{chooseFromLibraryButtonTitle}
|
|
37
49
|
</UTButton>
|
|
38
50
|
</View>
|
|
@@ -52,7 +52,11 @@ const ImagePickerComponent = ({
|
|
|
52
52
|
return;
|
|
53
53
|
}
|
|
54
54
|
const { assets } = response;
|
|
55
|
-
const item = assets[0];
|
|
55
|
+
const item = assets && assets.length > 0 ? assets[0] : null;
|
|
56
|
+
if (!item) {
|
|
57
|
+
onError('No se pudo obtener el archivo');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
56
60
|
const file = !avoidRetrieveFile && (await retrieveFile(item.uri, item.type));
|
|
57
61
|
if (!avoidRetrieveFile && !file) {
|
|
58
62
|
onError(response.errorCode);
|
|
@@ -3,6 +3,7 @@ import React, { useState } from 'react';
|
|
|
3
3
|
import { View, Image, TouchableWithoutFeedback, PermissionsAndroid } from 'react-native';
|
|
4
4
|
// eslint-disable-next-line import/no-unresolved
|
|
5
5
|
import { launchCamera, launchImageLibrary } from 'react-native-image-picker';
|
|
6
|
+
import * as ExpoImagePicker from 'expo-image-picker';
|
|
6
7
|
|
|
7
8
|
import { IS_IOS } from '../../utils/platformUtils/constants';
|
|
8
9
|
import Picker from '../Picker';
|
|
@@ -39,6 +40,7 @@ const ImagePicker = ({
|
|
|
39
40
|
const [openModalSelection, setOpenModalSelection] = useState(false);
|
|
40
41
|
const visibleModalSelection = () => setOpenModalSelection(true);
|
|
41
42
|
const closeModalSelection = () => setOpenModalSelection(false);
|
|
43
|
+
|
|
42
44
|
const requestPermissionCamera = async () => {
|
|
43
45
|
try {
|
|
44
46
|
const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.CAMERA);
|
|
@@ -52,11 +54,69 @@ const ImagePicker = ({
|
|
|
52
54
|
};
|
|
53
55
|
const openOptionSelected = async selected => {
|
|
54
56
|
closeModalSelection();
|
|
55
|
-
return
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
)
|
|
57
|
+
if (editable === false) return undefined;
|
|
58
|
+
if (typeof onShowImagePicker !== 'function') return undefined;
|
|
59
|
+
if (
|
|
60
|
+
!ExpoImagePicker ||
|
|
61
|
+
(!ExpoImagePicker.launchImageLibraryAsync && !ExpoImagePicker.launchCameraAsync)
|
|
62
|
+
) {
|
|
63
|
+
// eslint-disable-next-line no-console
|
|
64
|
+
console.error('No se encontró un ExpoImagePicker válido. instala "expo-image-picker".');
|
|
65
|
+
return () => {};
|
|
66
|
+
}
|
|
67
|
+
if (ExpoImagePicker?.launchImageLibraryAsync || ExpoImagePicker?.launchCameraAsync) {
|
|
68
|
+
if (selected) {
|
|
69
|
+
const expoCameraAdapter = async (options, cb) => {
|
|
70
|
+
try {
|
|
71
|
+
const perm = await ExpoImagePicker.requestCameraPermissionsAsync?.();
|
|
72
|
+
if (!perm || perm.status !== 'granted') {
|
|
73
|
+
cb?.({ didCancel: true, errorCode: 'Son necesarios permisos para utilizar la cámara' });
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const mediaTypes = ExpoImagePicker.MediaType ? [ExpoImagePicker.MediaType.image] : undefined;
|
|
77
|
+
const result = await ExpoImagePicker.launchCameraAsync({
|
|
78
|
+
mediaTypes,
|
|
79
|
+
allowsMultipleSelection: false,
|
|
80
|
+
quality: 1
|
|
81
|
+
});
|
|
82
|
+
cb?.({ assets: result?.assets || [], didCancel: result?.canceled });
|
|
83
|
+
} catch (e) {
|
|
84
|
+
cb?.({ didCancel: true, errorCode: String(e) });
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
return onShowImagePicker(expoCameraAdapter);
|
|
88
|
+
}
|
|
89
|
+
const expoGalleryAdapter = async (options, cb) => {
|
|
90
|
+
try {
|
|
91
|
+
if (
|
|
92
|
+
!ExpoImagePicker ||
|
|
93
|
+
(!ExpoImagePicker.launchImageLibraryAsync && !ExpoImagePicker.launchCameraAsync)
|
|
94
|
+
) {
|
|
95
|
+
// eslint-disable-next-line no-console
|
|
96
|
+
console.error('No se encontró un ExpoImagePicker válido. instala "expo-image-picker".');
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const perm = await ExpoImagePicker.requestMediaLibraryPermissionsAsync?.();
|
|
100
|
+
if (!perm || perm.status !== 'granted') {
|
|
101
|
+
cb?.({ didCancel: true, errorCode: 'Son necesarios permisos para acceder a tus fotos' });
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const mediaTypes = ExpoImagePicker.MediaType ? [ExpoImagePicker.MediaType.image] : undefined;
|
|
105
|
+
const result = await ExpoImagePicker.launchImageLibraryAsync({
|
|
106
|
+
mediaTypes,
|
|
107
|
+
allowsMultipleSelection: false,
|
|
108
|
+
quality: 1
|
|
109
|
+
});
|
|
110
|
+
cb?.({ assets: result?.assets || [], didCancel: result?.canceled });
|
|
111
|
+
} catch (e) {
|
|
112
|
+
cb?.({ didCancel: true, errorCode: String(e) });
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
return onShowImagePicker(expoGalleryAdapter);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return onShowImagePicker(
|
|
119
|
+
selected ? (IS_IOS ? launchCamera : await requestPermissionCamera()) : launchImageLibrary
|
|
60
120
|
);
|
|
61
121
|
};
|
|
62
122
|
|
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable no-undef */
|
|
2
|
+
import isEmpty from 'lodash/isEmpty';
|
|
3
|
+
import isArray from 'lodash/isArray';
|
|
2
4
|
// eslint-disable-next-line import/no-unresolved
|
|
3
5
|
import { launchCamera, launchImageLibrary } from 'react-native-image-picker';
|
|
4
6
|
// eslint-disable-next-line react-native/split-platform-components
|
|
5
|
-
import { View, PermissionsAndroid } from 'react-native';
|
|
6
|
-
|
|
7
|
-
import
|
|
8
|
-
import
|
|
7
|
+
import React, { View, PermissionsAndroid, Alert } from 'react-native';
|
|
8
|
+
import { useEffect, useState } from 'react';
|
|
9
|
+
import * as ExpoImagePicker from 'expo-image-picker';
|
|
10
|
+
import * as ExpoDocumentPicker from 'expo-document-picker';
|
|
9
11
|
|
|
10
12
|
import { IS_IOS } from '../../utils/platformUtils/constants';
|
|
11
13
|
import { retrieveFile, blobToFile } from '../../utils/fileUtils.js';
|
|
12
14
|
import UTBottomSheet from '../UTBottomSheet';
|
|
13
15
|
import UTButton from '../UTButton';
|
|
16
|
+
import { DEFAULT_ALLOWED_TYPES } from '../FilePicker/constants';
|
|
14
17
|
|
|
15
18
|
import { DEFAULT_MAX_SIZE } from './constants';
|
|
16
19
|
import {
|
|
17
|
-
|
|
20
|
+
getMimeType,
|
|
18
21
|
getInitialValuesFrom,
|
|
19
22
|
isFileFormatInvalid,
|
|
20
23
|
isFileSizeInvaid,
|
|
@@ -38,16 +41,16 @@ const MultipleFilePicker = ({
|
|
|
38
41
|
onChange,
|
|
39
42
|
onError: onError_,
|
|
40
43
|
onMaxSizeError: onMaxSizeError_,
|
|
44
|
+
permissionPrompt,
|
|
41
45
|
pickerText,
|
|
42
46
|
style,
|
|
43
47
|
title,
|
|
44
48
|
value
|
|
45
49
|
}) => {
|
|
46
50
|
const { files: values } = value || {};
|
|
47
|
-
|
|
48
51
|
const [newFile, setNewFile] = useState(null);
|
|
49
52
|
const [uploadedFiles, setUploadedFiles] = useState(isArray(values) ? getInitialValuesFrom(values) : []);
|
|
50
|
-
const [rawFiles, setRawFiles] = useState(isArray(values) ? values : []);
|
|
53
|
+
const [rawFiles, setRawFiles] = useState(Array.isArray(values) ? values : []);
|
|
51
54
|
|
|
52
55
|
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
|
53
56
|
const closeDrawer = () => setIsDrawerOpen(false);
|
|
@@ -64,44 +67,129 @@ const MultipleFilePicker = ({
|
|
|
64
67
|
|
|
65
68
|
useEffect(() => {
|
|
66
69
|
onChange?.(isEmpty(rawFiles) ? null : { files: rawFiles });
|
|
67
|
-
|
|
70
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
71
|
+
}, [rawFiles, onChange]);
|
|
68
72
|
|
|
69
73
|
useEffect(() => {
|
|
70
74
|
if (newFile) {
|
|
71
|
-
setUploadedFiles([...
|
|
72
|
-
setRawFiles([...
|
|
75
|
+
setUploadedFiles(prev => [...prev, newFile.uploadFile]);
|
|
76
|
+
setRawFiles(prev => [...prev, newFile.rawFile]);
|
|
73
77
|
}
|
|
74
78
|
}, [newFile]);
|
|
75
79
|
|
|
80
|
+
const remainingSlots = () => Math.max((maxFiles || 0) - uploadedFiles.length, 0);
|
|
81
|
+
|
|
82
|
+
const handleAssets = async response => {
|
|
83
|
+
if (!response || response.didCancel) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (response.errorCode) {
|
|
87
|
+
onError(response.errorCode);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
closeDrawer();
|
|
91
|
+
response.assets.forEach(async asset => {
|
|
92
|
+
const file = await retrieveFile(asset.uri, asset.type);
|
|
93
|
+
if (!file) {
|
|
94
|
+
onError(response.errorCode || 'No se pudo obtener el archivo');
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (isFileTypeInvalid({ type: asset.type }, allowedTypes, fileTypeError, onError)) return;
|
|
98
|
+
if (isFileSizeInvaid({ size: file.size }, maxFileByteSize, onMaxSizeError)) return;
|
|
99
|
+
setNewFile({
|
|
100
|
+
uploadFile: { name: file.data.name, size: file.data.size },
|
|
101
|
+
rawFile: blobToFile(file, asset.type)
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
|
|
76
106
|
const onPickFiles = async () => {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
107
|
+
if (!ExpoDocumentPicker || typeof ExpoDocumentPicker.getDocumentAsync !== 'function') {
|
|
108
|
+
// eslint-disable-next-line no-console
|
|
109
|
+
console.error('No se encontró un ExpoDocumentPicker válido. instala "expo-document-picker".');
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const normalizeTypes = types => {
|
|
114
|
+
if (!types || types.length === 0) return DEFAULT_ALLOWED_TYPES;
|
|
115
|
+
const hasMime = types.some(t => typeof t === 'string' && t.includes('/'));
|
|
116
|
+
if (hasMime) return types;
|
|
117
|
+
const mapped = types.map(getMimeType).filter(Boolean);
|
|
118
|
+
return mapped.length ? mapped : DEFAULT_ALLOWED_TYPES;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const types = normalizeTypes(allowedTypes);
|
|
122
|
+
const result = await ExpoDocumentPicker.getDocumentAsync({
|
|
123
|
+
multiple: true,
|
|
124
|
+
type: types
|
|
80
125
|
});
|
|
81
126
|
|
|
82
|
-
if (
|
|
127
|
+
if (result.canceled) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const documents = result.assets || [];
|
|
132
|
+
|
|
133
|
+
const slots = remainingSlots();
|
|
134
|
+
if (slots <= 0) {
|
|
83
135
|
onError('La cantidad de archivos supera al máximo permitido');
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
closeDrawer();
|
|
140
|
+
|
|
141
|
+
let added = 0;
|
|
142
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
143
|
+
for (const asset of documents) {
|
|
144
|
+
if (added >= slots) break;
|
|
145
|
+
// eslint-disable-next-line no-await-in-loop
|
|
146
|
+
const document = {
|
|
147
|
+
uri: asset.uri,
|
|
148
|
+
type: asset.mimeType || asset.type || 'application/octet-stream',
|
|
149
|
+
size: asset.size,
|
|
150
|
+
name: asset.name || 'document'
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
if (uploadedFiles.some(({ name }) => name === document.name)) {
|
|
154
|
+
// eslint-disable-next-line no-continue
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (isFileTypeInvalid(document, allowedTypes, fileTypeError, onError)) {
|
|
158
|
+
// eslint-disable-next-line no-continue
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (isFileSizeInvaid(document, maxFileByteSize, onMaxSizeError)) {
|
|
162
|
+
// eslint-disable-next-line no-continue
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// eslint-disable-next-line no-await-in-loop
|
|
167
|
+
const file = await retrieveFile(document.uri, document.type, document.name);
|
|
168
|
+
// eslint-disable-next-line no-await-in-loop
|
|
169
|
+
if (await isFileFormatInvalid(file, allowedPDFUploadSizes, onError)) {
|
|
170
|
+
// eslint-disable-next-line no-continue
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
setNewFile({
|
|
175
|
+
uploadFile: { name: document.name, size: document.size },
|
|
176
|
+
rawFile: blobToFile(file, document.type)
|
|
97
177
|
});
|
|
178
|
+
added += 1;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (documents.length > slots) {
|
|
182
|
+
onError('Algunos archivos no se agregaron por superar el máximo permitido');
|
|
98
183
|
}
|
|
99
184
|
};
|
|
100
185
|
|
|
101
186
|
// eslint-disable-next-line consistent-return
|
|
102
187
|
const requestPermissionCamera = async () => {
|
|
103
188
|
try {
|
|
104
|
-
const granted = await PermissionsAndroid.request(
|
|
189
|
+
const granted = await PermissionsAndroid.request(
|
|
190
|
+
PermissionsAndroid.PERMISSIONS.CAMERA,
|
|
191
|
+
permissionPrompt?.androidRationale
|
|
192
|
+
);
|
|
105
193
|
return granted === PermissionsAndroid.RESULTS.GRANTED
|
|
106
194
|
? launchCamera
|
|
107
195
|
: { errorCode: 'Son necesarios permisos para utilizar la cámara' };
|
|
@@ -111,14 +199,50 @@ const MultipleFilePicker = ({
|
|
|
111
199
|
};
|
|
112
200
|
|
|
113
201
|
const handleShowImagePicker = sourceImage => {
|
|
202
|
+
if (
|
|
203
|
+
!ExpoImagePicker ||
|
|
204
|
+
(!ExpoImagePicker.launchImageLibraryAsync && !ExpoImagePicker.launchCameraAsync)
|
|
205
|
+
) {
|
|
206
|
+
// eslint-disable-next-line no-console
|
|
207
|
+
console.error('No se encontró un ExpoImagePicker válido. instala "expo-image-picker".');
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
if (remainingSlots() <= 0) {
|
|
211
|
+
onError('La cantidad de archivos supera al máximo permitido');
|
|
212
|
+
return undefined;
|
|
213
|
+
}
|
|
214
|
+
if (!sourceImage || typeof sourceImage !== 'function') {
|
|
215
|
+
if (ExpoImagePicker?.launchImageLibraryAsync) {
|
|
216
|
+
const expoGalleryAdapter = async (options, cb) => {
|
|
217
|
+
try {
|
|
218
|
+
const selectionLimit = options?.selectionLimit ?? 1;
|
|
219
|
+
const mediaTypes = ExpoImagePicker.MediaType ? [ExpoImagePicker.MediaType.image] : undefined;
|
|
220
|
+
const result = await ExpoImagePicker.launchImageLibraryAsync({
|
|
221
|
+
mediaTypes,
|
|
222
|
+
allowsMultipleSelection: selectionLimit && selectionLimit > 1,
|
|
223
|
+
quality: 1
|
|
224
|
+
});
|
|
225
|
+
cb?.({ assets: result?.assets || [], didCancel: result?.canceled });
|
|
226
|
+
} catch (e) {
|
|
227
|
+
cb?.({ didCancel: true, errorCode: String(e) });
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
handleShowImagePicker(expoGalleryAdapter);
|
|
231
|
+
return undefined;
|
|
232
|
+
}
|
|
233
|
+
onError('No hay selector de imágenes disponible en este entorno.');
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
|
|
114
237
|
if (sourceImage?.errorCode) {
|
|
115
238
|
onError(sourceImage.errorCode);
|
|
116
|
-
return;
|
|
239
|
+
return undefined;
|
|
117
240
|
}
|
|
241
|
+
closeDrawer();
|
|
118
242
|
sourceImage(
|
|
119
243
|
{
|
|
120
244
|
mediaType: 'photo',
|
|
121
|
-
selectionLimit: Math.max(
|
|
245
|
+
selectionLimit: Math.max(remainingSlots(), 1)
|
|
122
246
|
},
|
|
123
247
|
async response => {
|
|
124
248
|
if (response.didCancel) return;
|
|
@@ -134,8 +258,8 @@ const MultipleFilePicker = ({
|
|
|
134
258
|
onError(response.errorCode);
|
|
135
259
|
return;
|
|
136
260
|
}
|
|
137
|
-
if (isFileTypeInvalid(
|
|
138
|
-
if (isFileSizeInvaid(file, maxFileByteSize, onMaxSizeError)) return;
|
|
261
|
+
if (isFileTypeInvalid({ type: asset.type }, allowedTypes, fileTypeError, onError)) return;
|
|
262
|
+
if (isFileSizeInvaid({ size: file.size }, maxFileByteSize, onMaxSizeError)) return;
|
|
139
263
|
|
|
140
264
|
setNewFile({
|
|
141
265
|
uploadFile: { name: file.data.name, size: file.data.size },
|
|
@@ -144,12 +268,128 @@ const MultipleFilePicker = ({
|
|
|
144
268
|
});
|
|
145
269
|
}
|
|
146
270
|
);
|
|
271
|
+
return undefined;
|
|
147
272
|
};
|
|
148
273
|
|
|
149
|
-
const onPickCamera = async () =>
|
|
274
|
+
const onPickCamera = async () => {
|
|
275
|
+
if (
|
|
276
|
+
!ExpoImagePicker ||
|
|
277
|
+
(!ExpoImagePicker.launchImageLibraryAsync && !ExpoImagePicker.launchCameraAsync)
|
|
278
|
+
) {
|
|
279
|
+
// eslint-disable-next-line no-console
|
|
280
|
+
console.error('No se encontró un ExpoImagePicker válido. instala "expo-image-picker".');
|
|
281
|
+
return undefined;
|
|
282
|
+
}
|
|
283
|
+
if (remainingSlots() <= 0) {
|
|
284
|
+
onError('La cantidad de archivos supera al máximo permitido');
|
|
285
|
+
return undefined;
|
|
286
|
+
}
|
|
287
|
+
if (ExpoImagePicker?.launchCameraAsync) {
|
|
288
|
+
const openCamera = async () => {
|
|
289
|
+
try {
|
|
290
|
+
const mediaTypes = ExpoImagePicker.MediaType ? [ExpoImagePicker.MediaType.image] : undefined;
|
|
291
|
+
closeDrawer();
|
|
292
|
+
const result = await ExpoImagePicker.launchCameraAsync({
|
|
293
|
+
mediaTypes,
|
|
294
|
+
allowsMultipleSelection: false,
|
|
295
|
+
quality: 1
|
|
296
|
+
});
|
|
297
|
+
await handleAssets({ assets: result?.assets || [], didCancel: result?.canceled }, 'expo:camera');
|
|
298
|
+
} catch (e) {
|
|
299
|
+
await handleAssets({ didCancel: true, errorCode: String(e) }, 'expo:camera');
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
const askAndOpenCamera = async () => {
|
|
303
|
+
try {
|
|
304
|
+
const perm = await ExpoImagePicker.requestCameraPermissionsAsync?.();
|
|
305
|
+
if (!perm || perm.status !== 'granted') {
|
|
306
|
+
onError('Son necesarios permisos para utilizar la cámara');
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
openCamera();
|
|
310
|
+
} catch (e) {
|
|
311
|
+
onError('Son necesarios permisos para utilizar la cámara');
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
const pre = permissionPrompt?.expoPrePrompt;
|
|
316
|
+
if (pre?.title || pre?.message) {
|
|
317
|
+
Alert.alert(
|
|
318
|
+
pre.title || 'Permiso de cámara',
|
|
319
|
+
pre.message || 'Necesitamos acceso a la cámara para tomar fotos.',
|
|
320
|
+
[
|
|
321
|
+
{ text: pre.cancelText || 'Cancelar', style: 'cancel' },
|
|
322
|
+
{ text: pre.confirmText || 'Continuar', onPress: askAndOpenCamera }
|
|
323
|
+
]
|
|
324
|
+
);
|
|
325
|
+
} else {
|
|
326
|
+
askAndOpenCamera();
|
|
327
|
+
}
|
|
328
|
+
return undefined;
|
|
329
|
+
}
|
|
150
330
|
handleShowImagePicker(IS_IOS ? launchCamera : await requestPermissionCamera());
|
|
331
|
+
return undefined;
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
const onPickGallery = () => {
|
|
335
|
+
if (
|
|
336
|
+
!ExpoImagePicker ||
|
|
337
|
+
(!ExpoImagePicker.launchImageLibraryAsync && !ExpoImagePicker.launchCameraAsync)
|
|
338
|
+
) {
|
|
339
|
+
// eslint-disable-next-line no-console
|
|
340
|
+
console.error('No se encontró un ExpoImagePicker válido. instala "expo-image-picker".');
|
|
341
|
+
return undefined;
|
|
342
|
+
}
|
|
343
|
+
if (remainingSlots() <= 0) {
|
|
344
|
+
onError('La cantidad de archivos supera al máximo permitido');
|
|
345
|
+
return undefined;
|
|
346
|
+
}
|
|
347
|
+
if (ExpoImagePicker?.launchImageLibraryAsync) {
|
|
348
|
+
const openGallery = async () => {
|
|
349
|
+
try {
|
|
350
|
+
const mediaTypes = ExpoImagePicker.MediaType ? [ExpoImagePicker.MediaType.image] : undefined;
|
|
351
|
+
closeDrawer();
|
|
352
|
+
const result = await ExpoImagePicker.launchImageLibraryAsync({
|
|
353
|
+
mediaTypes,
|
|
354
|
+
allowsMultipleSelection: false,
|
|
355
|
+
quality: 1
|
|
356
|
+
});
|
|
357
|
+
await handleAssets({ assets: result?.assets || [], didCancel: result?.canceled }, 'expo:gallery');
|
|
358
|
+
} catch (e) {
|
|
359
|
+
await handleAssets({ didCancel: true, errorCode: String(e) }, 'expo:gallery');
|
|
360
|
+
}
|
|
361
|
+
};
|
|
151
362
|
|
|
152
|
-
|
|
363
|
+
const askPermissionAndOpen = async () => {
|
|
364
|
+
try {
|
|
365
|
+
const perm = await ExpoImagePicker.requestMediaLibraryPermissionsAsync?.();
|
|
366
|
+
if (!perm || perm.status !== 'granted') {
|
|
367
|
+
onError('Son necesarios permisos para acceder a tus fotos');
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
openGallery();
|
|
371
|
+
} catch (e) {
|
|
372
|
+
onError('Son necesarios permisos para acceder a tus fotos');
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
const pre = permissionPrompt?.expoGalleryPrePrompt;
|
|
377
|
+
if (pre?.title || pre?.message) {
|
|
378
|
+
Alert.alert(
|
|
379
|
+
pre.title || 'Acceso a fotos',
|
|
380
|
+
pre.message || 'Necesitamos acceder a tu galería para seleccionar imágenes.',
|
|
381
|
+
[
|
|
382
|
+
{ text: pre.cancelText || 'Cancelar', style: 'cancel' },
|
|
383
|
+
{ text: pre.confirmText || 'Continuar', onPress: askPermissionAndOpen }
|
|
384
|
+
]
|
|
385
|
+
);
|
|
386
|
+
} else {
|
|
387
|
+
askPermissionAndOpen();
|
|
388
|
+
}
|
|
389
|
+
return undefined;
|
|
390
|
+
}
|
|
391
|
+
return handleShowImagePicker(launchImageLibrary);
|
|
392
|
+
};
|
|
153
393
|
|
|
154
394
|
const handleDeleteFile = index => {
|
|
155
395
|
onChange?.(null);
|