@fto-consult/expo-ui 8.6.0 → 8.7.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.
@@ -11,6 +11,7 @@ module.exports = {
11
11
  "@react-navigation/stack": "^6.3.20",
12
12
  "@shopify/flash-list": "1.6.3",
13
13
  "expo": "^50.0.2",
14
+ "expo-barcode-scanner": "~12.9.2",
14
15
  "expo-camera": "~14.0.1",
15
16
  "expo-clipboard": "~5.0.1",
16
17
  "expo-font": "~11.10.2",
package/bin/create-app.js CHANGED
@@ -116,10 +116,13 @@ const createAPPJSONFile = (projectRoot,{name,version})=>{
116
116
  "cameraPermission" : `Autoriser $(PRODUCT_NAME) à accéder à votre camera`
117
117
  }, cameraPluginsOptions = {
118
118
  "cameraPermission" : `Autoriser $(PRODUCT_NAME) à accéder à votre camera`
119
+ }, barCodePermission = {
120
+ "cameraPermission": "Autoriser $(PRODUCT_NAME) à accéder à la caméra."
119
121
  };
120
122
  const plugins = [
121
123
  ["expo-image-picker",imagePluginOptions],
122
- ["expo-camera",cameraPluginsOptions]
124
+ ["expo-camera",cameraPluginsOptions],
125
+ ["expo-barcode-scanner",barCodePermission]
123
126
  ];
124
127
  appSheme = name? sanitizeFileName(name).replace(/ /g, '') : null;
125
128
  const appJSONPath = path.join(projectRoot,"app.json");
@@ -176,14 +179,16 @@ const createAPPJSONFile = (projectRoot,{name,version})=>{
176
179
  if(!Array.isArray(appPlugins)){
177
180
  appPlugins = plugins;
178
181
  } else {
179
- let hasFoundCamera = false, hasFoundImagePicker = false;
182
+ let hasFoundCamera = false, hasFoundImagePicker = false,hasFoundBarCode = false;
180
183
  appPlugins.map(pl=>{
181
184
  if(Array.isArray(pl)){
182
185
  if(typeof pl[0] ==="expo-image-picker"){
183
186
  hasFoundImagePicker = true;
184
187
  } else if(pl[0] === "expo-camera"){
185
188
  hasFoundCamera = true;
186
- }
189
+ } else if(pl[0] =="expo-barcode-scanner"){
190
+ hasFoundBarCode = true;
191
+ }
187
192
  }
188
193
  });
189
194
  if(!hasFoundImagePicker){
@@ -192,6 +197,9 @@ const createAPPJSONFile = (projectRoot,{name,version})=>{
192
197
  if(!hasFoundCamera){
193
198
  appPlugins.push(plugins[1]);
194
199
  }
200
+ if(!hasFoundBarCode){
201
+ appPlugins.push(plugins[2]);
202
+ }
195
203
  }
196
204
  appJSONManager.set({
197
205
  expo : {plugins:appPlugins}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fto-consult/expo-ui",
3
- "version": "8.6.0",
3
+ "version": "8.7.0",
4
4
  "description": "Bibliothèque de composants UI Expo,react-native",
5
5
  "scripts": {
6
6
  "clear-npx-cache": "npx clear-npx-cache",
@@ -0,0 +1,148 @@
1
+ import React, { useState, useEffect,useMemo } from '$react';
2
+ import { View, StyleSheet} from 'react-native';
3
+ import { BarCodeScanner } from 'expo-barcode-scanner';
4
+ import theme from "$theme";
5
+ import ActivityIndicator from "$ecomponents/ActivityIndicator";
6
+ import Label from "$ecomponents/Label";
7
+ import PropTypes from "prop-types";
8
+ import { isNonNullString,defaultStr } from '$cutils';
9
+ import Button from "$ecomponents/Button";
10
+ import Dialog from "$ecomponents/Dialog";
11
+
12
+ export const scannerTypes = {back:"back",front:"front"};
13
+
14
+ /***@see : https://docs.expo.dev/versions/latest/sdk/bar-code-scanner/ */
15
+ export default function App({onScan,onGrantAccess,testID,onDenyAccess,scannerProps,onCancel,dialogProps}) {
16
+ testID = defaultStr(testID,"RN_BarCodeScanner");
17
+ const [hasPermission, setHasPermission] = useState(null);
18
+ const [visible,setVisible] = useState(true);
19
+ dialogProps = Object.assign({},dialogProps);
20
+ scannerProps = Object.assign({},scannerProps);
21
+ const prevVisible = React.usePrevious(visible);
22
+ const cancelRef = React.useRef(false);
23
+ const cancel = ()=>{
24
+ cancelRef.current = true;
25
+ setVisible(false);
26
+ }
27
+ const getCameraType = ()=>{
28
+ let {type} = scannerProps;
29
+ if(isNonNullString(type)){
30
+ type = type.toLowerCase().trim();
31
+ } else type = scannerTypes.back;
32
+ if(!scannerTypes[type]){
33
+ type = scannerTypes.back;
34
+ }
35
+ return type;
36
+ }
37
+ const sType = useMemo(()=>{
38
+ return getCameraType();
39
+ },[scannerProps.type]);
40
+ const [scannerType,setScannerType] = useState(sType);
41
+ useEffect(()=>{
42
+ const type = getCameraType();
43
+ if(type !== scannerType){
44
+ setScannerType(type);
45
+ }
46
+ },scannerProps.type)
47
+ const isBack = scannerType === scannerTypes.back;
48
+ useEffect(() => {
49
+ const getBarCodeScannerPermissions = async () => {
50
+ const { status } = await BarCodeScanner.requestPermissionsAsync();
51
+ setHasPermission(status === 'granted');
52
+ };
53
+
54
+ getBarCodeScannerPermissions();
55
+ }, []);
56
+
57
+ const handleBarCodeScanned = ({ type, data,...rest }) => {
58
+ //setScanned(true);
59
+ if(typeof onScan =="function"){
60
+ onScan({type,data,code:data,barCode:data,...rest});
61
+ }
62
+ setVisible(false);
63
+ };
64
+ useEffect(()=>{
65
+ if(hasPermission ===false){
66
+ if(typeof onDenyAccess =="function"){
67
+ return onDenyAccess();
68
+ }
69
+ } else if(hasPermission !== null){
70
+ if(typeof onGrantAccess =="function"){
71
+ onGrantAccess();
72
+ }
73
+ }
74
+ },[hasPermission]);
75
+ useEffect(()=>{
76
+ if(prevVisible === visible) return;
77
+ if(prevVisible && !visible && cancelRef.current && typeof onCancel =="function"){
78
+ onCancel();
79
+ }
80
+ cancelRef.current = false;
81
+ },[visible]);
82
+ const switchCameraBtn = {
83
+ text : "Pivoter la camera",
84
+ icon : "camera-flip",
85
+ tooltip : `Cliquez pour basculer à la camera ${isBack ? "frontable":"arrière"}`,
86
+ onPress : ()=>{
87
+ setScannerType(isBack ? scannerTypes.front : scannerTypes.back);
88
+ }
89
+ };
90
+ return <Dialog
91
+ fullPage
92
+ actions={[switchCameraBtn]}
93
+ title = {`Scanner un code barre`}
94
+ {...dialogProps}
95
+ onBackActionPress={cancel}
96
+ visible = {visible}
97
+ >
98
+ {hasPermission === null || hasPermission === false ? <View style={[styles.center]}>
99
+ {hasPermission === false ? <Label fontSize={18} error textBold>Accès à la camera refusée. Vous devez autoriser l'accès à la camera.</Label> :
100
+ <View style={[styles.row]}>
101
+ <Label fontSize={18} warning textBold>Demande d'autorisation pour l'accès à la camera...</Label>
102
+ <ActivityIndicator size={'large'}/>
103
+ </View>}
104
+ </View> : <View style={[theme.styles.flex1]} testID={testID}>
105
+ <BarCodeScanner
106
+ ratio='16:9'
107
+ testID={testID+"_ScannerContent"}
108
+ {...scannerProps}
109
+ type={scannerType}
110
+ style={[theme.styles.flex1,{width:"100%",height:"100%"},scannerProps.style]}
111
+ onBarCodeScanned={handleBarCodeScanned}
112
+ />
113
+ <View style={[styles.row,theme.styles.w100]}>
114
+ <Button
115
+ primary
116
+ {...switchCameraBtn}
117
+ style={[theme.styles.p1]}
118
+ />
119
+ <Button
120
+ error
121
+ children = {"Annuler"}
122
+ icon = "camera-off"
123
+ title = {"Cliquez pour annuler l'opération"}
124
+ onPress = {cancel}
125
+ />
126
+ </View>
127
+ </View>}
128
+ </Dialog>;
129
+ }
130
+ const styles = StyleSheet.create({
131
+ center : {
132
+ justifyContent : "center",
133
+ alignItems : "center",
134
+ flexDirection : "column",
135
+ flex : 1,
136
+ },
137
+ row : {
138
+ flexDirection : "row",
139
+ justifyContent : "center",
140
+ alignItems : "center",
141
+ flexWrap :"wrap",
142
+ }
143
+ });
144
+ BarCodeScanner.propTypes = {
145
+ onScan : PropTypes.func,
146
+ onGrantAccess : PropTypes.func, //lorsque la permission est allouée
147
+ onDenyAccess : PropTypes.func, //lorsque la permission est refusée
148
+ }
@@ -0,0 +1,3 @@
1
+ export default function BarCodeScanner(){
2
+ return null;
3
+ }
@@ -174,8 +174,8 @@ export default function ImageComponent(props){
174
174
  setEditorProps({...editorProps,visible:true,...props})
175
175
  })
176
176
  },
177
- pickImage : (opts)=>{
178
- opts = getCropProps(opts);
177
+ pickImage : ()=>{
178
+ const opts = getCropProps(defaultObj(pickImageProps));
179
179
  opts.base64 = true;
180
180
  return pickImage(opts).then((image)=>handlePickedImage(image,opts));
181
181
  },
@@ -229,7 +229,9 @@ export default function ImageComponent(props){
229
229
  label : 'Enregistrer une photo',
230
230
  icon : 'camera',
231
231
  onPress : (a)=>{
232
- takePhoto().then(handlePickedImage);
232
+ const opts = getCropProps(defaultObj(pickImageProps));
233
+ opts.base64 = true;
234
+ takePhoto(opts).then(handlePickedImage);
233
235
  }
234
236
  })
235
237
  }
@@ -113,6 +113,7 @@ const UserProfileAvatarComponent = React.forwardRef(({drawerRef,chevronIconProps
113
113
  if(!withLabel){
114
114
  return <View testID={"RNProfilAvatar_AvatarContainer"} style={[theme.styles.row,theme.styles.alignItemsCenter]}>
115
115
  <Image
116
+ pickImageProps = {{quality:0.4}}
116
117
  {...props}
117
118
  {...aProps}
118
119
  size={size}
@@ -65,7 +65,7 @@ export const pickImageOrVideo = (options)=>{
65
65
  return checkPermission().then(()=>{
66
66
  return new Promise((resolve,reject)=>{
67
67
  ImagePicker.launchImageLibraryAsync(getFilePickerOptions(options)).then((result)=>{
68
- if(!result.cancelled) {
68
+ if(!result.cancelled && !result.canceled) {
69
69
  resolve(prepareImageResult(result));
70
70
  } else {
71
71
  notify.warning("Opération annulée par l'utilisateur");
@@ -116,7 +116,7 @@ export const takePhoto = (options)=>{
116
116
  return checkPermission(ImagePicker.requestCameraPermissionsAsync).then((perm)=>{
117
117
  options = {base64:true,...Object.assign({},options)}
118
118
  return ImagePicker.launchCameraAsync({...getFilePickerOptions(options)}).then((result)=>{
119
- if(!result.cancelled) {
119
+ if(!result.cancelled && !result.canceled) {
120
120
  resolve(prepareImageResult(result));
121
121
  } else {
122
122
  notify.warning("Opération annulée par l'utilisateur");
@@ -9,7 +9,7 @@ export const getFilePickerOptions = (options)=>{
9
9
  base64 : false, //Whether to also include the image data in Base64 format.
10
10
  exif : false, //Whether to also include the EXIF data for the image. On iOS the EXIF data does not include GPS tags in the camera case.
11
11
  mediaTypes : ImagePicker.MediaTypeOptions.All, //@see : https://docs.expo.dev/versions/latest/sdk/imagepicker/#mediatypeoptions
12
- quality : 1, //Specify the quality of compression, from 0 to 1. 0 means compress for small size, 1 means compress for maximum quality.
12
+ quality : 0.5, //Specify the quality of compression, from 0 to 1. 0 means compress for small size, 1 means compress for maximum quality.
13
13
  },options);
14
14
  }
15
15
  /*** retourne les ooptions pour la capture d'une photo