@fto-consult/expo-ui 6.67.16 → 6.68.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/package.json +1 -1
- package/src/AppEntry/index.js +321 -161
- package/src/components/Datagrid/SWRDatagrid.js +3 -4
- package/src/components/SplashScreen/index.js +5 -6
- package/src/context/Provider.js +126 -4
- package/src/context/hooks.js +0 -1
- package/src/context/index.js +1 -0
- package/src/context/utils.js +2 -0
- package/src/screens/Help/openLibraries.js +1 -1
- package/src/AppEntry/App.js +0 -315
package/package.json
CHANGED
package/src/AppEntry/index.js
CHANGED
@@ -1,174 +1,334 @@
|
|
1
|
-
import
|
2
|
-
import
|
3
|
-
import
|
4
|
-
import
|
5
|
-
import
|
6
|
-
import
|
7
|
-
import {
|
8
|
-
import {
|
9
|
-
import
|
10
|
-
import
|
11
|
-
import {
|
12
|
-
import
|
13
|
-
import {
|
1
|
+
import React from "$react"
|
2
|
+
import { AppState} from "react-native";
|
3
|
+
import BackHandler from "$ecomponents/BackHandler";
|
4
|
+
import * as Linking from 'expo-linking';
|
5
|
+
import APP from "$capp";
|
6
|
+
import {AppStateService,trackIDLE,stop as stopIDLE} from "$capp/idle";
|
7
|
+
import { NavigationContainer} from '@react-navigation/native';
|
8
|
+
import {navigationRef} from "$cnavigation"
|
9
|
+
import NetInfo from '$cutils/NetInfo';
|
10
|
+
import Auth from "$cauth";
|
11
|
+
import {isNativeMobile,isElectron} from "$cplatform";
|
12
|
+
import Navigation from "../navigation";
|
13
|
+
import {set as setSession,get as getSession} from "$session";
|
14
|
+
import { showConfirm } from "$ecomponents/Dialog";
|
15
|
+
import {close as closePreloader, isVisible as isPreloaderVisible} from "$epreloader";
|
16
|
+
import SplashScreen from "$ecomponents/SplashScreen";
|
17
|
+
import {decycle} from "$cutils/json";
|
18
|
+
import init from "$capp/init";
|
19
|
+
import { setIsInitialized} from "$capp/utils";
|
20
|
+
import {isObj,isNonNullString,isPromise,defaultObj,defaultStr} from "$cutils";
|
21
|
+
import {loadFonts} from "$ecomponents/Icon/Font";
|
22
|
+
import appConfig from "$capp/config";
|
23
|
+
import Preloader from "$preloader";
|
24
|
+
import {PreloaderProvider} from "$epreloader";
|
25
|
+
import BottomSheetProvider from "$ecomponents/BottomSheet/Provider";
|
26
|
+
import DialogProvider from "$ecomponents/Dialog/Provider";
|
27
|
+
import SimpleSelect from '$ecomponents/SimpleSelect';
|
28
|
+
import {Provider as AlertProvider} from '$ecomponents/Dialog/confirm/Alert';
|
29
|
+
import { DialogProvider as FormDataDialogProvider } from '$eform/FormData';
|
30
|
+
import ErrorBoundaryProvider from "$ecomponents/ErrorBoundary/Provider";
|
31
|
+
import notify, {notificationRef} from "$notify";
|
32
|
+
import DropdownAlert from '$ecomponents/Dialog/DropdownAlert';
|
33
|
+
import { PreferencesContext } from '../Preferences';
|
34
|
+
import ErrorBoundary from "$ecomponents/ErrorBoundary";
|
35
|
+
import {updateTheme,defaultTheme} from "$theme";
|
36
|
+
import StatusBar from "$ecomponents/StatusBar";
|
37
|
+
import {Provider as PaperProvider,Portal } from 'react-native-paper';
|
38
|
+
import FontIcon from "$ecomponents/Icon/Font";
|
39
|
+
import useContext from "$econtext/hooks";
|
40
|
+
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
41
|
+
import { StyleSheet } from "react-native";
|
42
|
+
import Logo from "$ecomponents/Logo";
|
43
|
+
import AppEntryRootView from "./RootView";
|
44
|
+
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
14
45
|
import { Dimensions,Keyboard } from 'react-native';
|
15
46
|
import {isTouchDevice} from "$platform";
|
16
|
-
import * as Utils from "$cutils";
|
17
|
-
import {useContext} from "$econtext/hooks";
|
18
|
-
import appConfig from "$capp/config";
|
19
|
-
import { useKeepAwake } from 'expo-keep-awake';
|
20
47
|
|
21
|
-
Object.map(Utils,(v,i)=>{
|
22
|
-
if(typeof v =='function' && typeof window !='undefined' && window && !window[i]){
|
23
|
-
window[i] = v;
|
24
|
-
}
|
25
|
-
});
|
26
48
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
49
|
+
let MAX_BACK_COUNT = 1;
|
50
|
+
let countBack = 0;
|
51
|
+
let isBackConfirmShowing = false;
|
52
|
+
|
53
|
+
const resetExitCounter = ()=>{
|
54
|
+
countBack = 0
|
55
|
+
isBackConfirmShowing = false;
|
56
|
+
};
|
57
|
+
|
58
|
+
const NAVIGATION_PERSISTENCE_KEY = 'NAVIGATION_STATE';
|
59
|
+
|
60
|
+
/****
|
61
|
+
* init {function}: ()=>Promise<{}> est la fonction d'initialisation de l'application
|
62
|
+
* initialRouteName : la route initiale par défaut
|
63
|
+
* getStartedRouteName : la route par défaut de getStarted lorsque l'application est en mode getStarted, c'est à dire lorsque la fonction init renvoie une erreur (reject)
|
64
|
+
*/
|
65
|
+
function App({init:initApp,initialRouteName:appInitialRouteName,render}) {
|
66
|
+
AppStateService.init();
|
67
|
+
const {FontsIconsFilter,beforeExit,preferences:appPreferences,navigation,getStartedRouteName} = useContext();
|
68
|
+
const {containerProps} = navigation;
|
69
|
+
const [initialState, setInitialState] = React.useState(undefined);
|
70
|
+
const appReadyRef = React.useRef(true);
|
71
|
+
const [state,setState] = React.useState({
|
72
|
+
isLoading : true,
|
73
|
+
isInitialized:false,
|
74
|
+
hasCallInitApp : false,
|
75
|
+
});
|
76
|
+
React.useEffect(() => {
|
77
|
+
///la fonction de rappel lorsque le composant est monté
|
78
|
+
const triggerKeyboardToggle = (status)=>{
|
79
|
+
APP.trigger(APP.EVENTS.KEYBOARD_DID_TOGGLE,{shown:status,status,visible:status,hide : !status});
|
80
|
+
}
|
81
|
+
const keyBoardDidShow = ()=>{
|
82
|
+
APP.trigger(APP.EVENTS.KEYBOARD_DID_SHOW);
|
83
|
+
triggerKeyboardToggle(true);
|
84
|
+
},keyBoardDidHide = ()=>{
|
85
|
+
APP.trigger(APP.EVENTS.KEYBOARD_DID_HIDE);
|
86
|
+
triggerKeyboardToggle(false);
|
87
|
+
}
|
88
|
+
const keyBoardDidShowListener = Keyboard.addListener("keyboardDidShow",keyBoardDidShow);
|
89
|
+
const keyBoardDidHideListener = Keyboard.addListener("keyboardDidHide",keyBoardDidHide);
|
90
|
+
const loadResources = ()=>{
|
91
|
+
return new Promise((resolve)=>{
|
92
|
+
loadFonts(FontsIconsFilter).catch((e)=>{
|
93
|
+
console.warn(e," ierror loading app resources fonts");
|
94
|
+
}).finally(()=>{
|
95
|
+
resolve(true);
|
96
|
+
});
|
97
|
+
})
|
98
|
+
}
|
99
|
+
const restoreState = () => {
|
100
|
+
return new Promise((resolve,reject)=>{
|
101
|
+
(async ()=>{
|
102
|
+
try {
|
103
|
+
const initialUrl = await Linking.getInitialURL();
|
104
|
+
if (isNativeMobile() || initialUrl === null) {
|
105
|
+
const savedState = getSession(NAVIGATION_PERSISTENCE_KEY);
|
106
|
+
if (isObj(savedState)) {
|
107
|
+
setInitialState(savedState);
|
108
|
+
}
|
109
|
+
}
|
110
|
+
} catch(e){ console.log(e," is state error")}
|
111
|
+
finally {
|
112
|
+
appReadyRef.current = true;
|
113
|
+
resolve({});
|
72
114
|
}
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
115
|
+
})();
|
116
|
+
});
|
117
|
+
};
|
118
|
+
const subscription = AppState.addEventListener('change', AppStateService.getInstance().handleAppStateChange);
|
119
|
+
const beforeExitApp = (cb)=>{
|
120
|
+
return new Promise((resolve,reject)=>{
|
121
|
+
Preloader.closeAll();
|
122
|
+
showConfirm({
|
123
|
+
title : "Quitter l'application",
|
124
|
+
message : 'Voulez vous vraiment quitter l\'application?',
|
125
|
+
yes : 'Oui',
|
126
|
+
no : 'Non',
|
127
|
+
onSuccess : ()=>{
|
128
|
+
const foreceExit = ()=>{
|
129
|
+
BackHandler.exitApp();
|
130
|
+
if(isElectron() && window.ELECTRON && typeof ELECTRON.exitApp =='function'){
|
131
|
+
ELECTRON.exitApp({APP});
|
132
|
+
}
|
133
|
+
}
|
134
|
+
const exit = ()=>{
|
135
|
+
if(typeof beforeExit =='function'){
|
136
|
+
const r2 = beforeExit()
|
137
|
+
if(!isPromise(r2)){
|
138
|
+
throw {message:'La fonction before exit du contexte doit retourner une promesse',returnedResult:r2}
|
139
|
+
}
|
140
|
+
return r2.then(foreceExit).catch(reject);
|
141
|
+
}
|
142
|
+
foreceExit();
|
143
|
+
}
|
144
|
+
const r = {APP,exit};
|
145
|
+
APP.trigger(APP.EVENTS.BEFORE_EXIT,exit,(result)=>{
|
146
|
+
if(isObj(result) || Array.isArray(result)){
|
147
|
+
for(let ik in result){
|
148
|
+
if(result[ik] === false) return reject({message:'EXIT APP DENIED BY BEFORE EXIT EVENT HANDLER AT POSITON {0}'.sprintf(ik)});
|
149
|
+
}
|
150
|
+
}
|
151
|
+
resolve(r);
|
152
|
+
if(typeof cb =='function'){
|
153
|
+
cb(r);
|
154
|
+
}
|
155
|
+
});
|
156
|
+
},
|
157
|
+
onCancel : reject
|
158
|
+
})
|
159
|
+
})
|
160
|
+
}
|
161
|
+
/**** onBeforeExit prend en paramètre la fonction de rappel CB, qui lorsque la demande de sortie d'application est acceptée, alors elle est exécutée */
|
162
|
+
if(typeof APP.beforeExit !=='function'){
|
163
|
+
Object.defineProperties(APP,{
|
164
|
+
beforeExit : {
|
165
|
+
value : beforeExitApp,
|
166
|
+
}
|
167
|
+
})
|
168
|
+
}
|
169
|
+
const backAction = (args) => {
|
170
|
+
if(navigationRef && navigationRef.canGoBack()? true : false){
|
171
|
+
resetExitCounter();
|
172
|
+
navigationRef.goBack(null);
|
173
|
+
return false;
|
174
|
+
}
|
175
|
+
if(isBackConfirmShowing) {
|
176
|
+
return;
|
177
|
+
}
|
178
|
+
if(countBack < MAX_BACK_COUNT){
|
179
|
+
countBack++;
|
180
|
+
isBackConfirmShowing = false;
|
181
|
+
if(countBack === MAX_BACK_COUNT){
|
182
|
+
notify.toast({text:'Cliquez à nouveau pour quiiter l\'application'});
|
84
183
|
}
|
85
|
-
if(
|
86
|
-
|
184
|
+
if(countBack === 2 && isPreloaderVisible()) {
|
185
|
+
closePreloader();
|
87
186
|
}
|
187
|
+
return false;
|
188
|
+
}
|
189
|
+
isBackConfirmShowing = true;
|
190
|
+
return beforeExitApp().finally(x=>{
|
191
|
+
isBackConfirmShowing = false;
|
192
|
+
}).then(({exit})=>{
|
193
|
+
exit();
|
194
|
+
})
|
195
|
+
};
|
196
|
+
const unsubscribeNetInfo = NetInfo.addEventListener(state => {
|
197
|
+
APP.setOnlineState(state);
|
198
|
+
});
|
199
|
+
NetInfo.fetch().catch((e)=>{
|
200
|
+
console.log(e," is net info heinn")
|
201
|
+
});
|
202
|
+
loadResources().finally(()=>{
|
203
|
+
(typeof initApp =='function'?initApp : init)({appConfig,contex:{setState}}).then((args)=>{
|
204
|
+
if(Auth.isLoggedIn()){
|
205
|
+
Auth.loginUser(false);
|
88
206
|
}
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
...
|
95
|
-
|
96
|
-
|
97
|
-
/* Customize the network state detector */
|
98
|
-
if(canFetchOffline) return true;
|
99
|
-
return APP.isOnline();
|
100
|
-
},
|
101
|
-
isVisible() {
|
102
|
-
const screen = activeScreenRef.current;
|
103
|
-
if(!screen) return false;
|
104
|
-
if(!screensRef.current[screen]){
|
105
|
-
screensRef.current[screen] = new Date();
|
106
|
-
return false;
|
107
|
-
}
|
108
|
-
const date = screensRef.current[screen];
|
109
|
-
const diff = new Date().getTime() - date.getTime();
|
110
|
-
const timeout = defaultNumber(swrConfig.refreshTimeout,SWR_REFRESH_TIMEOUT)
|
111
|
-
screensRef.current[screen] = new Date();
|
112
|
-
return diff >= timeout ? true : false;
|
113
|
-
},
|
114
|
-
initFocus(callback) {
|
115
|
-
let appState = AppState.currentState
|
116
|
-
const onAppStateChange = (nextAppState) => {
|
117
|
-
/* If it's resuming from background or inactive mode to active one */
|
118
|
-
const active = appState.match(/inactive|background/) && nextAppState === 'active';
|
119
|
-
if (active) {
|
120
|
-
callback()
|
121
|
-
}
|
122
|
-
appState = nextAppState;
|
123
|
-
appStateRef.current = !!active;
|
124
|
-
}
|
125
|
-
// Subscribe to the app state change events
|
126
|
-
const subscription = AppState.addEventListener('change', onAppStateChange);
|
127
|
-
return () => {
|
128
|
-
subscription?.remove()
|
129
|
-
}
|
130
|
-
},
|
131
|
-
initReconnect(cb) {
|
132
|
-
const callback = ()=>{
|
133
|
-
cb();
|
134
|
-
}
|
135
|
-
/* Register the listener with your state provider */
|
136
|
-
APP.on(APP.EVENTS.GO_ONLINE,callback);
|
137
|
-
return ()=>{
|
138
|
-
APP.off(APP.EVENTS.GO_ONLINE,callback);
|
139
|
-
}
|
140
|
-
}
|
141
|
-
}}
|
142
|
-
>
|
143
|
-
<App init={init} render={render}/>
|
144
|
-
</SWRConfig>
|
145
|
-
);
|
146
|
-
};
|
207
|
+
setState({
|
208
|
+
...state,hasGetStarted:true,...defaultObj(args && args?.state),hasCallInitApp:true,isInitialized:true,isLoading : false,
|
209
|
+
});
|
210
|
+
}).catch((e)=>{
|
211
|
+
console.error(e," loading resources for app initialization");
|
212
|
+
setState({...state,isInitialized:true,hasCallInitApp,isLoading : false,hasGetStarted:false});
|
213
|
+
})
|
214
|
+
});
|
147
215
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
defaultValue : appConfig.getDeviceId(),
|
154
|
-
yes : 'Définir',
|
155
|
-
placeholder : isMobileNative()? "":'Entrer une valeur unique sans espace SVP',
|
156
|
-
no : 'Annuler',
|
157
|
-
onSuccess : ({value})=>{
|
158
|
-
let message = null;
|
159
|
-
if(!value || value.contains(" ")){
|
160
|
-
message = "Merci d'entrer une valeur non nulle ne contenant pas d'espace";
|
216
|
+
const Events = {}
|
217
|
+
let events = [];
|
218
|
+
if(navigationRef && navigationRef.addListener){
|
219
|
+
for(let i in Events){
|
220
|
+
events.push(navigationRef.addListener(i,Events[i]));
|
161
221
|
}
|
162
|
-
|
163
|
-
|
222
|
+
}
|
223
|
+
APP.onElectron("BEFORE_EXIT",()=>{
|
224
|
+
return beforeExitApp().then(({exit})=>{
|
225
|
+
exit();
|
226
|
+
})
|
227
|
+
});
|
228
|
+
APP.on(APP.EVENTS.BACK_BUTTON,backAction);
|
229
|
+
return () => {
|
230
|
+
keyBoardDidShowListener && keyBoardDidShowListener.remove && keyBoardDidShowListener.remove();
|
231
|
+
keyBoardDidHideListener && keyBoardDidHideListener.remove && keyBoardDidHideListener.remove();
|
232
|
+
if(listener){
|
233
|
+
window.visualViewport.removeEventListener('resize', listener);
|
164
234
|
}
|
165
|
-
|
166
|
-
|
167
|
-
|
235
|
+
APP.off(APP.EVENTS.BACK_BUTTON,backAction);
|
236
|
+
if(subscription && subscription.remove){
|
237
|
+
subscription.remove();
|
168
238
|
}
|
169
|
-
|
170
|
-
|
239
|
+
events.map((ev)=>{
|
240
|
+
if(typeof ev =="function") ev();
|
241
|
+
})
|
242
|
+
unsubscribeNetInfo();
|
243
|
+
stopIDLE(false,true);
|
244
|
+
|
245
|
+
}
|
246
|
+
}, []);
|
247
|
+
const {isInitialized} = state;
|
248
|
+
const isLoading = state.isLoading || !isInitialized || !appReadyRef.current? true : false;
|
249
|
+
React.useEffect(()=>{
|
250
|
+
if(isInitialized){
|
251
|
+
setIsInitialized(true);
|
252
|
+
trackIDLE(true);
|
171
253
|
}
|
172
|
-
|
173
|
-
|
174
|
-
|
254
|
+
},[isInitialized]);
|
255
|
+
const hasGetStarted = state.hasGetStarted !== false? true : false;
|
256
|
+
const themeRef = React.useRef(null);
|
257
|
+
const [theme,setTheme] = React.useState(themeRef.current || updateTheme(defaultTheme));
|
258
|
+
themeRef.current = theme;
|
259
|
+
const updatePreferenceTheme = (customTheme,persist)=>{
|
260
|
+
setTheme(updateTheme(customTheme));
|
261
|
+
};
|
262
|
+
const forceRender = React.useForceRender();
|
263
|
+
const pref = typeof appPreferences =='function'? appPreferences({setTheme,forceRender,updateTheme:updatePreferenceTheme}) : appPreferences;
|
264
|
+
const preferences = React.useMemo(()=>({
|
265
|
+
updateTheme:updatePreferenceTheme,
|
266
|
+
theme,
|
267
|
+
...defaultObj(pref),
|
268
|
+
}),[theme,pref]);
|
269
|
+
const isLoaded = !isLoading && state.hasCallInitApp;
|
270
|
+
const child = isLoaded ? <NavigationContainer
|
271
|
+
ref={navigationRef}
|
272
|
+
initialState={initialState}
|
273
|
+
{...containerProps}
|
274
|
+
onStateChange={(state,...rest) =>{
|
275
|
+
setSession(NAVIGATION_PERSISTENCE_KEY,decycle(state),false);
|
276
|
+
if(typeof containerProps.onStateChange =='function'){
|
277
|
+
containerProps.onStateChange(state,...rest);
|
278
|
+
}
|
279
|
+
}}
|
280
|
+
fallback = {React.isValidElement(containerProps.fallback) ? containerProps.fallback : <Logo.Progress/>}
|
281
|
+
>
|
282
|
+
<Navigation
|
283
|
+
initialRouteName = {defaultStr(hasGetStarted ? appInitialRouteName : getStartedRouteName,"Home")}
|
284
|
+
state = {state}
|
285
|
+
hasGetStarted = {hasGetStarted}
|
286
|
+
isInitialized = {isInitialized}
|
287
|
+
onGetStart = {(e)=>{
|
288
|
+
setState({...state,hasGetStarted:true})
|
289
|
+
}}
|
290
|
+
/>
|
291
|
+
</NavigationContainer> : null;
|
292
|
+
const content = isLoaded ? typeof render == 'function'? render({children:child,appConfig,config:appConfig}) : child : null;
|
293
|
+
return <SafeAreaProvider>
|
294
|
+
<GestureHandlerRootView testID={"RN_MainAppGestureHanleRootView"} style={styles.gesture}>
|
295
|
+
<AppEntryRootView>
|
296
|
+
<PaperProvider
|
297
|
+
theme={theme}
|
298
|
+
settings={{
|
299
|
+
icon: (props) => {
|
300
|
+
return <FontIcon {...props}/>
|
301
|
+
},
|
302
|
+
}}
|
303
|
+
>
|
304
|
+
<Portal.Host testID="RN_NativePaperPortalHost">
|
305
|
+
<ErrorBoundaryProvider/>
|
306
|
+
<PreloaderProvider/>
|
307
|
+
<DialogProvider responsive testID={"RN_MainAppDialogProvider"}/>
|
308
|
+
<AlertProvider SimpleSelect={SimpleSelect}/>
|
309
|
+
<FormDataDialogProvider/>
|
310
|
+
<BottomSheetProvider/>
|
311
|
+
<DropdownAlert ref={notificationRef}/>
|
312
|
+
<ErrorBoundary>
|
313
|
+
<StatusBar/>
|
314
|
+
<SplashScreen isLoaded={isLoaded}>
|
315
|
+
<PreferencesContext.Provider value={preferences}>
|
316
|
+
{React.isValidElement(content) && content || child}
|
317
|
+
</PreferencesContext.Provider>
|
318
|
+
</SplashScreen>
|
319
|
+
</ErrorBoundary>
|
320
|
+
</Portal.Host>
|
321
|
+
</PaperProvider>
|
322
|
+
</AppEntryRootView>
|
323
|
+
</GestureHandlerRootView>
|
324
|
+
</SafeAreaProvider>
|
325
|
+
}
|
326
|
+
|
327
|
+
export default App;
|
328
|
+
|
329
|
+
const styles = StyleSheet.create({
|
330
|
+
gesture : {
|
331
|
+
flex : 1,
|
332
|
+
flexGrow : 1,
|
333
|
+
}
|
334
|
+
});
|
@@ -25,6 +25,7 @@ import {getRowsPerPagesLimits} from "./Common/utils";
|
|
25
25
|
import PropTypes from "prop-types";
|
26
26
|
import {Menu} from "$ecomponents/BottomSheet";
|
27
27
|
import session from "$session";
|
28
|
+
import { SWR_REFRESH_TIMEOUT } from "$econtext/utils";
|
28
29
|
import useContext from "$econtext/hooks";
|
29
30
|
import notify from "$cnotify";
|
30
31
|
|
@@ -46,15 +47,13 @@ export const setSessionData = (key,value)=>{
|
|
46
47
|
}
|
47
48
|
|
48
49
|
|
49
|
-
|
50
|
-
export const timeout = 5000*60;//5 minutes
|
51
50
|
/***@see : https://swr.vercel.app/docs/api */
|
52
51
|
|
53
52
|
export const getSWROptions = (defTimeout)=>{
|
54
|
-
const delay = defaultNumber(defTimeout,
|
53
|
+
const delay = defaultNumber(defTimeout,SWR_REFRESH_TIMEOUT);
|
55
54
|
return {
|
56
55
|
dedupingInterval : delay,
|
57
|
-
errorRetryInterval : Math.max(delay*2,
|
56
|
+
errorRetryInterval : Math.max(delay*2,SWR_REFRESH_TIMEOUT),
|
58
57
|
errorRetryCount : 5,
|
59
58
|
revalidateOnMount : false,//enable or disable automatic revalidation when component is mounted
|
60
59
|
revalidateOnFocus : true, //automatically revalidate when window gets focused (details)
|
@@ -27,13 +27,12 @@ const SplashScreenComponent = ({isLoaded,children , duration, delay,logoWidth,lo
|
|
27
27
|
loadingProgress: new Animated.Value(0),
|
28
28
|
});
|
29
29
|
const { loadingProgress, animationDone} = state;
|
30
|
+
const prevIsLoaded = React.usePrevious(isLoaded);
|
30
31
|
React.useEffect(()=>{
|
31
|
-
if(!
|
32
|
-
setState({...state,loadingProgress : new Animated.Value(0),animationDone:false});
|
33
|
-
} else {
|
32
|
+
if(isLoaded && !prevIsLoaded){
|
34
33
|
Animated.timing(loadingProgress, {
|
35
34
|
toValue: 100,
|
36
|
-
duration: duration ||
|
35
|
+
duration: duration || 100,
|
37
36
|
delay: delay || 0,
|
38
37
|
useNativeDriver: isNativeMobile(),
|
39
38
|
}).start(() => {
|
@@ -43,7 +42,7 @@ const SplashScreenComponent = ({isLoaded,children , duration, delay,logoWidth,lo
|
|
43
42
|
})
|
44
43
|
})
|
45
44
|
}
|
46
|
-
}
|
45
|
+
});
|
47
46
|
testID = defaultStr(testID,"RN_SplashscreenComponent")
|
48
47
|
logoWidth = defaultDecimal(logoWidth,150);
|
49
48
|
logoHeight = defaultDecimal(logoHeight,250);
|
@@ -67,7 +66,7 @@ const SplashScreenComponent = ({isLoaded,children , duration, delay,logoWidth,lo
|
|
67
66
|
extrapolate: "clamp",
|
68
67
|
}),
|
69
68
|
}
|
70
|
-
if(
|
69
|
+
if(isLoaded && animationDone){
|
71
70
|
return React.isValidElement(children)?children:null;
|
72
71
|
}
|
73
72
|
return <>
|
package/src/context/Provider.js
CHANGED
@@ -4,7 +4,7 @@ import {MD3LightTheme,MD3DarkTheme} from "react-native-paper";
|
|
4
4
|
import { useMaterial3Theme,isDynamicThemeSupported} from '@pchmn/expo-material3-theme';
|
5
5
|
import { useColorScheme } from 'react-native';
|
6
6
|
import {colorsAlias,Colors} from "$theme";
|
7
|
-
import {isObj,isNonNullString,defaultStr,extendObj} from "$cutils";
|
7
|
+
import {isObj,isNonNullString,defaultStr,extendObj,defaultNumber} from "$cutils";
|
8
8
|
import {getMainScreens} from "$escreens/mainScreens";
|
9
9
|
import {ExpoUIContext} from "./hooks";
|
10
10
|
import {enableAuth,disableAuth} from "$cauth/perms";
|
@@ -15,6 +15,22 @@ import { prepareScreens } from "./TableData";
|
|
15
15
|
import {extendFormFields} from "$ecomponents/Form/Fields";
|
16
16
|
import {AuthProvider} from '$cauth';
|
17
17
|
import { signInRef } from "$cauth/authSignIn2SignOut";
|
18
|
+
import APP from "$capp/instance";
|
19
|
+
import { AppState } from 'react-native'
|
20
|
+
import {canFetchOffline} from "$capi/utils";
|
21
|
+
import { SWR_REFRESH_TIMEOUT } from "./utils";
|
22
|
+
import * as Utils from "$cutils";
|
23
|
+
import {setDeviceIdRef} from "$capp";
|
24
|
+
import {isMobileNative} from "$cplatform";
|
25
|
+
import notify from "$cnotify";
|
26
|
+
import {showPrompt} from "$ecomponents/Dialog/confirm";
|
27
|
+
import {SWRConfig} from "$swr";
|
28
|
+
|
29
|
+
Object.map(Utils,(v,i)=>{
|
30
|
+
if(typeof v =='function' && typeof window !='undefined' && window && !window[i]){
|
31
|
+
window[i] = v;
|
32
|
+
}
|
33
|
+
});
|
18
34
|
|
19
35
|
/*****
|
20
36
|
les utilitaires disponibles à passer au provider :
|
@@ -64,6 +80,7 @@ import { signInRef } from "$cauth/authSignIn2SignOut";
|
|
64
80
|
realm : {}, //les options de configurations de la base de données realmdb
|
65
81
|
*/
|
66
82
|
const Provider = ({children,getTableData,handleHelpScreen,navigation,swrConfig,auth:cAuth,components:cComponents,convertFiltersToSQL,getStructData,tablesData,structsData,...props})=>{
|
83
|
+
require('$session');///initializing session
|
67
84
|
const {extendAppTheme} = appConfig;
|
68
85
|
const { theme : pTheme } = useMaterial3Theme();
|
69
86
|
navigation = defaultObj(navigation);
|
@@ -82,7 +99,62 @@ const Provider = ({children,getTableData,handleHelpScreen,navigation,swrConfig,a
|
|
82
99
|
appConfig.structsData = appConfig.structsData = isObj(structsData)? structsData : null;
|
83
100
|
getTableData = appConfig.getTable = appConfig.getTableData = getTableOrStructDataCall(tablesData,getTableData);
|
84
101
|
getStructData = appConfig.getStructData = getTableOrStructDataCall(structsData,getStructData);
|
85
|
-
|
102
|
+
|
103
|
+
///swr config settings
|
104
|
+
///garde pour chaque écran sa date de dernière activité
|
105
|
+
const screensRef = React.useRef({});//la liste des écrans actifs
|
106
|
+
const isScreenFocusedRef = React.useRef(true);
|
107
|
+
const activeScreenRef = React.useRef('');
|
108
|
+
const prevActiveScreenRef = React.useRef('');
|
109
|
+
const appStateRef = React.useRef({});
|
110
|
+
const swrRefreshTimeout = defaultNumber(swrConfig?.refreshTimeout,SWR_REFRESH_TIMEOUT)
|
111
|
+
swrConfig = extendObj({
|
112
|
+
provider: () => new Map(),
|
113
|
+
isOnline() {
|
114
|
+
/* Customize the network state detector */
|
115
|
+
if(canFetchOffline) return true;
|
116
|
+
return APP.isOnline();
|
117
|
+
},
|
118
|
+
isVisible() {
|
119
|
+
const screen = activeScreenRef.current;
|
120
|
+
if(!screen) return false;
|
121
|
+
if(!screensRef.current[screen]){
|
122
|
+
screensRef.current[screen] = new Date();
|
123
|
+
return false;
|
124
|
+
}
|
125
|
+
const date = screensRef.current[screen];
|
126
|
+
const diff = new Date().getTime() - date.getTime();
|
127
|
+
screensRef.current[screen] = new Date();
|
128
|
+
return diff >= swrRefreshTimeout ? true : false;
|
129
|
+
},
|
130
|
+
initFocus(callback) {
|
131
|
+
let appState = AppState.currentState
|
132
|
+
const onAppStateChange = (nextAppState) => {
|
133
|
+
/* If it's resuming from background or inactive mode to active one */
|
134
|
+
const active = appState.match(/inactive|background/) && nextAppState === 'active';
|
135
|
+
if (active) {
|
136
|
+
callback()
|
137
|
+
}
|
138
|
+
appState = nextAppState;
|
139
|
+
appStateRef.current = !!active;
|
140
|
+
}
|
141
|
+
// Subscribe to the app state change events
|
142
|
+
const subscription = AppState.addEventListener('change', onAppStateChange);
|
143
|
+
return () => {
|
144
|
+
subscription?.remove()
|
145
|
+
}
|
146
|
+
},
|
147
|
+
initReconnect(cb) {
|
148
|
+
const callback = ()=>{
|
149
|
+
cb();
|
150
|
+
}
|
151
|
+
/* Register the listener with your state provider */
|
152
|
+
APP.on(APP.EVENTS.GO_ONLINE,callback);
|
153
|
+
return ()=>{
|
154
|
+
APP.off(APP.EVENTS.GO_ONLINE,callback);
|
155
|
+
}
|
156
|
+
}
|
157
|
+
},swrConfig);
|
86
158
|
if(convertFiltersToSQL !== undefined){
|
87
159
|
appConfig.set("convertFiltersToSQL",convertFiltersToSQL);
|
88
160
|
}
|
@@ -158,6 +230,36 @@ const Provider = ({children,getTableData,handleHelpScreen,navigation,swrConfig,a
|
|
158
230
|
}
|
159
231
|
}
|
160
232
|
}
|
233
|
+
/**** setDeviceRef */
|
234
|
+
setDeviceIdRef.current = ()=>{
|
235
|
+
return new Promise((resolve,reject)=>{
|
236
|
+
showPrompt({
|
237
|
+
title : 'ID unique pour l\'appareil',
|
238
|
+
maxLength : 30,
|
239
|
+
defaultValue : appConfig.getDeviceId(),
|
240
|
+
yes : 'Définir',
|
241
|
+
placeholder : isMobileNative()? "":'Entrer une valeur unique sans espace SVP',
|
242
|
+
no : 'Annuler',
|
243
|
+
onSuccess : ({value})=>{
|
244
|
+
let message = null;
|
245
|
+
if(!value || value.contains(" ")){
|
246
|
+
message = "Merci d'entrer une valeur non nulle ne contenant pas d'espace";
|
247
|
+
}
|
248
|
+
if(value.length > 30){
|
249
|
+
message = "la valeur entrée doit avoir au plus 30 caractères";
|
250
|
+
}
|
251
|
+
if(message){
|
252
|
+
notify.error(message);
|
253
|
+
return reject({message})
|
254
|
+
}
|
255
|
+
resolve(value);
|
256
|
+
notify.success("la valeur ["+value+"] a été définie comme identifiant unique pour l'application instalée sur cet appareil");
|
257
|
+
}
|
258
|
+
})
|
259
|
+
})
|
260
|
+
}
|
261
|
+
|
262
|
+
|
161
263
|
const {screens} = navigation;
|
162
264
|
navigation.screens = React.useMemo(()=>{
|
163
265
|
const r = prepareScreens({
|
@@ -170,6 +272,25 @@ const Provider = ({children,getTableData,handleHelpScreen,navigation,swrConfig,a
|
|
170
272
|
},[]);
|
171
273
|
navigation.containerProps = defaultObj(navigation.containerProps);
|
172
274
|
const {linking} = navigation;
|
275
|
+
React.useEffect(()=>{
|
276
|
+
const onScreenFocus = ({sanitizedName})=>{
|
277
|
+
prevActiveScreenRef.current = activeScreenRef.current;
|
278
|
+
if(activeScreenRef.current){
|
279
|
+
screensRef.current[activeScreenRef.current] = null;
|
280
|
+
}
|
281
|
+
screensRef.current[sanitizedName] = new Date();
|
282
|
+
activeScreenRef.current = sanitizedName;
|
283
|
+
isScreenFocusedRef.current = true;
|
284
|
+
}, onScreenBlur = ()=>{
|
285
|
+
isScreenFocusedRef.current = false;
|
286
|
+
}
|
287
|
+
APP.on(APP.EVENTS.SCREEN_FOCUS,onScreenFocus);
|
288
|
+
APP.on(APP.EVENTS.SCREEN_BLUR,onScreenBlur);
|
289
|
+
return ()=>{
|
290
|
+
APP.off(APP.EVENTS.SCREEN_FOCUS,onScreenFocus);
|
291
|
+
APP.off(APP.EVENTS.SCREEN_BLUR,onScreenBlur);
|
292
|
+
}
|
293
|
+
},[]);
|
173
294
|
return <ExpoUIContext.Provider
|
174
295
|
value={{
|
175
296
|
...props,
|
@@ -189,10 +310,11 @@ const Provider = ({children,getTableData,handleHelpScreen,navigation,swrConfig,a
|
|
189
310
|
getStructData,
|
190
311
|
tablesData,
|
191
312
|
structsData,
|
192
|
-
appConfig,
|
193
313
|
swrConfig,
|
194
314
|
}}
|
195
|
-
children={<
|
315
|
+
children={<SWRConfig value={swrConfig}>
|
316
|
+
<AuthProvider {...auth} LoginComponent={Login}>{children}</AuthProvider>
|
317
|
+
</SWRConfig>}
|
196
318
|
/>;
|
197
319
|
}
|
198
320
|
const getTableOrStructDataCall = (tablesOrStructDatas,getTableOrStructDataFunc)=>{
|
package/src/context/hooks.js
CHANGED
@@ -6,7 +6,6 @@ import { useWindowDimensions } from "$cdimensions";
|
|
6
6
|
import {isObj,isNonNullString} from "$cutils";
|
7
7
|
import { StyleSheet } from "react-native";
|
8
8
|
import { createContext,useContext as useReactContext } from "react";
|
9
|
-
import appConfig from "$capp/config";
|
10
9
|
|
11
10
|
export const ExpoUIContext = createContext(null);
|
12
11
|
|
package/src/context/index.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
module.exports = {"@fto-consult/expo-ui":{"name":"@fto-consult/expo-ui","version":"6.
|
1
|
+
module.exports = {"@fto-consult/expo-ui":{"name":"@fto-consult/expo-ui","version":"6.68.0","repository":{"type":"git","url":"git+https://github.com/borispipo/expo-ui.git"},"homepage":"https://github.com/borispipo/expo-ui#readme"},"@babel/plugin-proposal-export-namespace-from":{"version":"7.18.9","url":"https://babel.dev/docs/en/next/babel-plugin-proposal-export-namespace-from","license":"MIT"},"@emotion/native":{"version":"11.11.0","url":"https://emotion.sh","license":"MIT"},"@emotion/react":{"version":"11.11.1","url":"https://github.com/emotion-js/emotion/tree/main/packages/react","license":"MIT"},"@expo/html-elements":{"version":"0.5.1","url":"https://github.com/expo/expo/tree/main/packages/html-elements","license":"MIT"},"@expo/metro-config":{"version":"0.10.7","url":"https://github.com/expo/expo.git","license":"MIT"},"@expo/vector-icons":{"version":"13.0.0","url":"https://expo.github.io/vector-icons","license":"MIT"},"@expo/webpack-config":{"version":"19.0.0","url":"https://github.com/expo/expo-cli.git","license":"MIT"},"@faker-js/faker":{"version":"8.0.2","url":"https://github.com/faker-js/faker.git","license":"MIT"},"@fto-consult/common":{"version":"3.58.1","url":"https://github.com/borispipo/common#readme","license":"ISC"},"@pchmn/expo-material3-theme":{"version":"1.3.1","url":"https://github.com/pchmn/expo-material3-theme#readme","license":"MIT"},"@react-native-async-storage/async-storage":{"version":"1.18.2","url":"https://github.com/react-native-async-storage/async-storage#readme","license":"MIT"},"@react-native-community/datetimepicker":{"version":"7.2.0","url":"https://github.com/react-native-community/datetimepicker#readme","license":"MIT"},"@react-native-community/netinfo":{"version":"9.3.10","url":"https://github.com/react-native-netinfo/react-native-netinfo#readme","license":"MIT"},"@react-native/assets-registry":{"version":"0.72.0","url":"git@github.com:facebook/react-native.git","license":"MIT"},"@react-navigation/native":{"version":"6.1.9","url":"https://reactnavigation.org","license":"MIT"},"@react-navigation/native-stack":{"version":"6.9.16","url":"https://github.com/software-mansion/react-native-screens#readme","license":"MIT"},"@react-navigation/stack":{"version":"6.3.20","url":"https://reactnavigation.org/docs/stack-navigator/","license":"MIT"},"@shopify/flash-list":{"version":"1.4.3","url":"https://shopify.github.io/flash-list/","license":"MIT"},"apexcharts":{"version":"3.44.0","url":"https://apexcharts.com","license":"MIT"},"babel-plugin-inline-dotenv":{"version":"1.7.0","url":"https://github.com/brysgo/babel-plugin-inline-dotenv#readme","license":"ISC"},"babel-plugin-module-resolver":{"version":"5.0.0","url":"https://github.com/tleunen/babel-plugin-module-resolver.git","license":"MIT"},"expo":{"version":"49.0.16","url":"https://github.com/expo/expo/tree/main/packages/expo","license":"MIT"},"expo-camera":{"version":"13.4.4","url":"https://docs.expo.dev/versions/latest/sdk/camera/","license":"MIT"},"expo-clipboard":{"version":"4.3.1","url":"https://docs.expo.dev/versions/latest/sdk/clipboard","license":"MIT"},"expo-font":{"version":"11.4.0","url":"https://docs.expo.dev/versions/latest/sdk/font/","license":"MIT"},"expo-image-picker":{"version":"14.3.2","url":"https://docs.expo.dev/versions/latest/sdk/imagepicker/","license":"MIT"},"expo-linking":{"version":"5.0.2","url":"https://docs.expo.dev/versions/latest/sdk/linking","license":"MIT"},"expo-sharing":{"version":"11.5.0","url":"https://docs.expo.dev/versions/latest/sdk/sharing/","license":"MIT"},"expo-sqlite":{"version":"11.3.3","url":"https://docs.expo.dev/versions/latest/sdk/sqlite/","license":"MIT"},"expo-status-bar":{"version":"1.6.0","url":"https://docs.expo.dev/versions/latest/sdk/status-bar/","license":"MIT"},"expo-system-ui":{"version":"2.4.0","url":"https://docs.expo.dev/versions/latest/sdk/system-ui","license":"MIT"},"expo-web-browser":{"version":"12.3.2","url":"https://docs.expo.dev/versions/latest/sdk/webbrowser/","license":"MIT"},"file-saver":{"version":"2.0.5","url":"https://github.com/eligrey/FileSaver.js#readme","license":"MIT"},"fs-extra":{"version":"11.1.1","url":"https://github.com/jprichardson/node-fs-extra","license":"MIT"},"google-libphonenumber":{"version":"3.2.33","url":"https://ruimarinho.github.io/google-libphonenumber/","license":"(MIT AND Apache-2.0)"},"htmlparser2-without-node-native":{"version":"3.9.2","url":"git://github.com/fb55/htmlparser2.git","license":"MIT"},"is-plain-obj":{"version":"4.1.0","license":"MIT"},"js-base64":{"version":"3.7.5","license":"BSD-3-Clause"},"pdfmake":{"version":"0.2.7","url":"http://pdfmake.org","license":"MIT"},"process":{"version":"0.11.10","url":"git://github.com/shtylman/node-process.git","license":"MIT"},"prop-types":{"version":"15.8.1","url":"https://facebook.github.io/react/","license":"MIT"},"react":{"version":"18.2.0","url":"https://reactjs.org/","license":"MIT"},"react-content-loader":{"version":"6.2.1","url":"https://github.com/danilowoz/react-content-loader","license":"MIT"},"react-dom":{"version":"18.2.0","url":"https://reactjs.org/","license":"MIT"},"react-native":{"version":"0.72.6","license":"MIT"},"react-native-big-list":{"version":"1.6.1","url":"https://marcocesarato.github.io/react-native-big-list-docs/","license":"GPL-3.0-or-later"},"react-native-blob-util":{"version":"0.18.6","url":"https://github.com/RonRadtke/react-native-blob-util","license":"MIT"},"react-native-gesture-handler":{"version":"2.12.1","url":"https://github.com/software-mansion/react-native-gesture-handler#readme","license":"MIT"},"react-native-get-random-values":{"version":"1.9.0","license":"MIT"},"react-native-iphone-x-helper":{"version":"1.3.1","url":"https://github.com/ptelad/react-native-iphone-x-helper#readme","license":"MIT"},"react-native-mime-types":{"version":"2.4.0","license":"MIT"},"react-native-paper":{"version":"5.11.1","url":"https://callstack.github.io/react-native-paper","license":"MIT"},"react-native-paper-dates":{"version":"0.20.3","url":"https://github.com/web-ridge/react-native-paper-dates#readme","license":"MIT"},"react-native-reanimated":{"version":"3.3.0","url":"https://github.com/software-mansion/react-native-reanimated#readme","license":"MIT"},"react-native-safe-area-context":{"version":"4.6.3","url":"https://github.com/th3rdwave/react-native-safe-area-context#readme","license":"MIT"},"react-native-screens":{"version":"3.22.1","url":"https://github.com/software-mansion/react-native-screens#readme","license":"MIT"},"react-native-svg":{"version":"13.9.0","url":"https://github.com/react-native-community/react-native-svg","license":"MIT"},"react-native-web":{"version":"0.19.9","url":"git://github.com/necolas/react-native-web.git","license":"MIT"},"react-native-webview":{"version":"13.2.2","url":"https://github.com/react-native-webview/react-native-webview#readme","license":"MIT"},"react-virtuoso":{"version":"4.6.2","url":"https://virtuoso.dev/","license":"MIT"},"sharp-cli":{"version":"2.1.0","url":"https://github.com/vseventer/sharp-cli","license":"MIT"},"tippy.js":{"version":"6.3.7","url":"https://atomiks.github.io/tippyjs/","license":"MIT"},"websql":{"version":"2.0.3","url":"git://github.com/nolanlawson/node-websql.git","license":"Apache-2.0"},"xlsx":{"version":"0.18.5","url":"https://sheetjs.com/","license":"Apache-2.0"}};
|
package/src/AppEntry/App.js
DELETED
@@ -1,315 +0,0 @@
|
|
1
|
-
import React from "$react"
|
2
|
-
import { AppState} from "react-native";
|
3
|
-
import BackHandler from "$ecomponents/BackHandler";
|
4
|
-
import * as Linking from 'expo-linking';
|
5
|
-
import APP from "$capp";
|
6
|
-
import {AppStateService,trackIDLE,stop as stopIDLE} from "$capp/idle";
|
7
|
-
import { NavigationContainer} from '@react-navigation/native';
|
8
|
-
import {navigationRef} from "$cnavigation"
|
9
|
-
import NetInfo from '$cutils/NetInfo';
|
10
|
-
import Auth from "$cauth";
|
11
|
-
import {isNativeMobile,isElectron} from "$cplatform";
|
12
|
-
import Navigation from "../navigation";
|
13
|
-
import {set as setSession,get as getSession} from "$session";
|
14
|
-
import { showConfirm } from "$ecomponents/Dialog";
|
15
|
-
import {close as closePreloader, isVisible as isPreloaderVisible} from "$epreloader";
|
16
|
-
import SplashScreen from "$ecomponents/SplashScreen";
|
17
|
-
import {decycle} from "$cutils/json";
|
18
|
-
import init from "$capp/init";
|
19
|
-
import { setIsInitialized} from "$capp/utils";
|
20
|
-
import {isObj,isNonNullString,isPromise,defaultObj,defaultStr} from "$cutils";
|
21
|
-
import {loadFonts} from "$ecomponents/Icon/Font";
|
22
|
-
import appConfig from "$capp/config";
|
23
|
-
import Preloader from "$preloader";
|
24
|
-
import {PreloaderProvider} from "$epreloader";
|
25
|
-
import BottomSheetProvider from "$ecomponents/BottomSheet/Provider";
|
26
|
-
import DialogProvider from "$ecomponents/Dialog/Provider";
|
27
|
-
import SimpleSelect from '$ecomponents/SimpleSelect';
|
28
|
-
import {Provider as AlertProvider} from '$ecomponents/Dialog/confirm/Alert';
|
29
|
-
import { DialogProvider as FormDataDialogProvider } from '$eform/FormData';
|
30
|
-
import ErrorBoundaryProvider from "$ecomponents/ErrorBoundary/Provider";
|
31
|
-
import notify, {notificationRef} from "$notify";
|
32
|
-
import DropdownAlert from '$ecomponents/Dialog/DropdownAlert';
|
33
|
-
import { PreferencesContext } from '../Preferences';
|
34
|
-
import ErrorBoundary from "$ecomponents/ErrorBoundary";
|
35
|
-
import {updateTheme,defaultTheme} from "$theme";
|
36
|
-
import StatusBar from "$ecomponents/StatusBar";
|
37
|
-
import {Provider as PaperProvider,Portal } from 'react-native-paper';
|
38
|
-
import FontIcon from "$ecomponents/Icon/Font";
|
39
|
-
import useContext from "$econtext/hooks";
|
40
|
-
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
41
|
-
import { StyleSheet } from "react-native";
|
42
|
-
import Logo from "$ecomponents/Logo";
|
43
|
-
import AppEntryRootView from "./RootView";
|
44
|
-
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
45
|
-
|
46
|
-
|
47
|
-
let MAX_BACK_COUNT = 1;
|
48
|
-
let countBack = 0;
|
49
|
-
let isBackConfirmShowing = false;
|
50
|
-
|
51
|
-
const resetExitCounter = ()=>{
|
52
|
-
countBack = 0
|
53
|
-
isBackConfirmShowing = false;
|
54
|
-
};
|
55
|
-
|
56
|
-
const NAVIGATION_PERSISTENCE_KEY = 'NAVIGATION_STATE';
|
57
|
-
|
58
|
-
/****
|
59
|
-
* init {function}: ()=>Promise<{}> est la fonction d'initialisation de l'application
|
60
|
-
* initialRouteName : la route initiale par défaut
|
61
|
-
* getStartedRouteName : la route par défaut de getStarted lorsque l'application est en mode getStarted, c'est à dire lorsque la fonction init renvoie une erreur (reject)
|
62
|
-
*/
|
63
|
-
function App({init:initApp,initialRouteName:appInitialRouteName,render}) {
|
64
|
-
AppStateService.init();
|
65
|
-
const {FontsIconsFilter,beforeExit,preferences:appPreferences,navigation,getStartedRouteName} = useContext();
|
66
|
-
const {containerProps} = navigation;
|
67
|
-
const [initialState, setInitialState] = React.useState(undefined);
|
68
|
-
const appReadyRef = React.useRef(true);
|
69
|
-
const [state,setState] = React.useState({
|
70
|
-
isLoading : true,
|
71
|
-
isInitialized:false,
|
72
|
-
hasCallInitApp : false,
|
73
|
-
});
|
74
|
-
React.useEffect(() => {
|
75
|
-
const loadResources = ()=>{
|
76
|
-
return new Promise((resolve)=>{
|
77
|
-
loadFonts(FontsIconsFilter).catch((e)=>{
|
78
|
-
console.warn(e," ierror loading app resources fonts");
|
79
|
-
}).finally(()=>{
|
80
|
-
resolve(true);
|
81
|
-
});
|
82
|
-
})
|
83
|
-
}
|
84
|
-
const restoreState = () => {
|
85
|
-
return new Promise((resolve,reject)=>{
|
86
|
-
(async ()=>{
|
87
|
-
try {
|
88
|
-
const initialUrl = await Linking.getInitialURL();
|
89
|
-
if (isNativeMobile() || initialUrl === null) {
|
90
|
-
const savedState = getSession(NAVIGATION_PERSISTENCE_KEY);
|
91
|
-
if (isObj(savedState)) {
|
92
|
-
setInitialState(savedState);
|
93
|
-
}
|
94
|
-
}
|
95
|
-
} catch(e){ console.log(e," is state error")}
|
96
|
-
finally {
|
97
|
-
appReadyRef.current = true;
|
98
|
-
resolve({});
|
99
|
-
}
|
100
|
-
})();
|
101
|
-
});
|
102
|
-
};
|
103
|
-
const subscription = AppState.addEventListener('change', AppStateService.getInstance().handleAppStateChange);
|
104
|
-
const beforeExitApp = (cb)=>{
|
105
|
-
return new Promise((resolve,reject)=>{
|
106
|
-
Preloader.closeAll();
|
107
|
-
showConfirm({
|
108
|
-
title : "Quitter l'application",
|
109
|
-
message : 'Voulez vous vraiment quitter l\'application?',
|
110
|
-
yes : 'Oui',
|
111
|
-
no : 'Non',
|
112
|
-
onSuccess : ()=>{
|
113
|
-
const foreceExit = ()=>{
|
114
|
-
BackHandler.exitApp();
|
115
|
-
if(isElectron() && window.ELECTRON && typeof ELECTRON.exitApp =='function'){
|
116
|
-
ELECTRON.exitApp({APP});
|
117
|
-
}
|
118
|
-
}
|
119
|
-
const exit = ()=>{
|
120
|
-
if(typeof beforeExit =='function'){
|
121
|
-
const r2 = beforeExit()
|
122
|
-
if(!isPromise(r2)){
|
123
|
-
throw {message:'La fonction before exit du contexte doit retourner une promesse',returnedResult:r2}
|
124
|
-
}
|
125
|
-
return r2.then(foreceExit).catch(reject);
|
126
|
-
}
|
127
|
-
foreceExit();
|
128
|
-
}
|
129
|
-
const r = {APP,exit};
|
130
|
-
APP.trigger(APP.EVENTS.BEFORE_EXIT,exit,(result)=>{
|
131
|
-
if(isObj(result) || Array.isArray(result)){
|
132
|
-
for(let ik in result){
|
133
|
-
if(result[ik] === false) return reject({message:'EXIT APP DENIED BY BEFORE EXIT EVENT HANDLER AT POSITON {0}'.sprintf(ik)});
|
134
|
-
}
|
135
|
-
}
|
136
|
-
resolve(r);
|
137
|
-
if(typeof cb =='function'){
|
138
|
-
cb(r);
|
139
|
-
}
|
140
|
-
});
|
141
|
-
},
|
142
|
-
onCancel : reject
|
143
|
-
})
|
144
|
-
})
|
145
|
-
}
|
146
|
-
/**** onBeforeExit prend en paramètre la fonction de rappel CB, qui lorsque la demande de sortie d'application est acceptée, alors elle est exécutée */
|
147
|
-
if(typeof APP.beforeExit !=='function'){
|
148
|
-
Object.defineProperties(APP,{
|
149
|
-
beforeExit : {
|
150
|
-
value : beforeExitApp,
|
151
|
-
}
|
152
|
-
})
|
153
|
-
}
|
154
|
-
const backAction = (args) => {
|
155
|
-
if(navigationRef && navigationRef.canGoBack()? true : false){
|
156
|
-
resetExitCounter();
|
157
|
-
navigationRef.goBack(null);
|
158
|
-
return false;
|
159
|
-
}
|
160
|
-
if(isBackConfirmShowing) {
|
161
|
-
return;
|
162
|
-
}
|
163
|
-
if(countBack < MAX_BACK_COUNT){
|
164
|
-
countBack++;
|
165
|
-
isBackConfirmShowing = false;
|
166
|
-
if(countBack === MAX_BACK_COUNT){
|
167
|
-
notify.toast({text:'Cliquez à nouveau pour quiiter l\'application'});
|
168
|
-
}
|
169
|
-
if(countBack === 2 && isPreloaderVisible()) {
|
170
|
-
closePreloader();
|
171
|
-
}
|
172
|
-
return false;
|
173
|
-
}
|
174
|
-
isBackConfirmShowing = true;
|
175
|
-
return beforeExitApp().finally(x=>{
|
176
|
-
isBackConfirmShowing = false;
|
177
|
-
}).then(({exit})=>{
|
178
|
-
exit();
|
179
|
-
})
|
180
|
-
};
|
181
|
-
const unsubscribeNetInfo = NetInfo.addEventListener(state => {
|
182
|
-
APP.setOnlineState(state);
|
183
|
-
});
|
184
|
-
NetInfo.fetch().catch((e)=>{
|
185
|
-
console.log(e," is net info heinn")
|
186
|
-
});
|
187
|
-
loadResources().finally(()=>{
|
188
|
-
(typeof initApp =='function'?initApp : init)({appConfig,contex:{setState}}).then((args)=>{
|
189
|
-
if(Auth.isLoggedIn()){
|
190
|
-
Auth.loginUser(false);
|
191
|
-
}
|
192
|
-
setState({
|
193
|
-
...state,hasGetStarted:true,...defaultObj(args && args?.state),hasCallInitApp:true,isInitialized:true,isLoading : false,
|
194
|
-
});
|
195
|
-
}).catch((e)=>{
|
196
|
-
console.error(e," loading resources for app initialization");
|
197
|
-
setState({...state,isInitialized:true,hasCallInitApp,isLoading : false,hasGetStarted:false});
|
198
|
-
})
|
199
|
-
});
|
200
|
-
|
201
|
-
const Events = {}
|
202
|
-
let events = [];
|
203
|
-
if(navigationRef && navigationRef.addListener){
|
204
|
-
for(let i in Events){
|
205
|
-
events.push(navigationRef.addListener(i,Events[i]));
|
206
|
-
}
|
207
|
-
}
|
208
|
-
APP.onElectron("BEFORE_EXIT",()=>{
|
209
|
-
return beforeExitApp().then(({exit})=>{
|
210
|
-
exit();
|
211
|
-
})
|
212
|
-
});
|
213
|
-
APP.on(APP.EVENTS.BACK_BUTTON,backAction);
|
214
|
-
return () => {
|
215
|
-
APP.off(APP.EVENTS.BACK_BUTTON,backAction);
|
216
|
-
|
217
|
-
if(subscription && subscription.remove){
|
218
|
-
subscription.remove();
|
219
|
-
}
|
220
|
-
events.map((ev)=>{
|
221
|
-
if(typeof ev =="function") ev();
|
222
|
-
})
|
223
|
-
unsubscribeNetInfo();
|
224
|
-
stopIDLE(false,true);
|
225
|
-
|
226
|
-
}
|
227
|
-
}, []);
|
228
|
-
const {isInitialized} = state;
|
229
|
-
const isLoading = state.isLoading || !isInitialized || !appReadyRef.current? true : false;
|
230
|
-
React.useEffect(()=>{
|
231
|
-
if(isInitialized){
|
232
|
-
setIsInitialized(true);
|
233
|
-
trackIDLE(true);
|
234
|
-
}
|
235
|
-
},[isInitialized]);
|
236
|
-
const hasGetStarted = state.hasGetStarted !== false? true : false;
|
237
|
-
const themeRef = React.useRef(null);
|
238
|
-
const [theme,setTheme] = React.useState(themeRef.current || updateTheme(defaultTheme));
|
239
|
-
themeRef.current = theme;
|
240
|
-
const updatePreferenceTheme = (customTheme,persist)=>{
|
241
|
-
setTheme(updateTheme(customTheme));
|
242
|
-
};
|
243
|
-
const forceRender = React.useForceRender();
|
244
|
-
const pref = typeof appPreferences =='function'? appPreferences({setTheme,forceRender,updateTheme:updatePreferenceTheme}) : appPreferences;
|
245
|
-
const preferences = React.useMemo(()=>({
|
246
|
-
updateTheme:updatePreferenceTheme,
|
247
|
-
theme,
|
248
|
-
...defaultObj(pref),
|
249
|
-
}),[theme,pref]);
|
250
|
-
const isLoaded = !isLoading && state.hasCallInitApp;
|
251
|
-
const child = isLoaded ? <NavigationContainer
|
252
|
-
ref={navigationRef}
|
253
|
-
initialState={initialState}
|
254
|
-
{...containerProps}
|
255
|
-
onStateChange={(state,...rest) =>{
|
256
|
-
setSession(NAVIGATION_PERSISTENCE_KEY,decycle(state),false);
|
257
|
-
if(typeof containerProps.onStateChange =='function'){
|
258
|
-
containerProps.onStateChange(state,...rest);
|
259
|
-
}
|
260
|
-
}}
|
261
|
-
fallback = {React.isValidElement(containerProps.fallback) ? containerProps.fallback : <Logo.Progress/>}
|
262
|
-
>
|
263
|
-
<Navigation
|
264
|
-
initialRouteName = {defaultStr(hasGetStarted ? appInitialRouteName : getStartedRouteName,"Home")}
|
265
|
-
state = {state}
|
266
|
-
hasGetStarted = {hasGetStarted}
|
267
|
-
isInitialized = {isInitialized}
|
268
|
-
onGetStart = {(e)=>{
|
269
|
-
setState({...state,hasGetStarted:true})
|
270
|
-
}}
|
271
|
-
/>
|
272
|
-
</NavigationContainer> : null;
|
273
|
-
const content = isLoaded ? typeof render == 'function'? render({children:child,appConfig,config:appConfig}) : child : null;
|
274
|
-
return <SafeAreaProvider>
|
275
|
-
<GestureHandlerRootView testID={"RN_MainAppGestureHanleRootView"} style={styles.gesture}>
|
276
|
-
<AppEntryRootView>
|
277
|
-
<PaperProvider
|
278
|
-
theme={theme}
|
279
|
-
settings={{
|
280
|
-
icon: (props) => {
|
281
|
-
return <FontIcon {...props}/>
|
282
|
-
},
|
283
|
-
}}
|
284
|
-
>
|
285
|
-
<Portal.Host testID="RN_NativePaperPortalHost">
|
286
|
-
<ErrorBoundaryProvider/>
|
287
|
-
<PreloaderProvider/>
|
288
|
-
<DialogProvider responsive testID={"RN_MainAppDialogProvider"}/>
|
289
|
-
<AlertProvider SimpleSelect={SimpleSelect}/>
|
290
|
-
<FormDataDialogProvider/>
|
291
|
-
<BottomSheetProvider/>
|
292
|
-
<DropdownAlert ref={notificationRef}/>
|
293
|
-
<ErrorBoundary>
|
294
|
-
<StatusBar/>
|
295
|
-
<SplashScreen isLoaded={isLoaded}>
|
296
|
-
<PreferencesContext.Provider value={preferences}>
|
297
|
-
{React.isValidElement(content) && content || child}
|
298
|
-
</PreferencesContext.Provider>
|
299
|
-
</SplashScreen>
|
300
|
-
</ErrorBoundary>
|
301
|
-
</Portal.Host>
|
302
|
-
</PaperProvider>
|
303
|
-
</AppEntryRootView>
|
304
|
-
</GestureHandlerRootView>
|
305
|
-
</SafeAreaProvider>
|
306
|
-
}
|
307
|
-
|
308
|
-
export default App;
|
309
|
-
|
310
|
-
const styles = StyleSheet.create({
|
311
|
-
gesture : {
|
312
|
-
flex : 1,
|
313
|
-
flexGrow : 1,
|
314
|
-
}
|
315
|
-
})
|