diff --git a/packages/apps/stock/src/screens/loader/LoaderScreen.js b/packages/apps/stock/src/screens/loader/LoaderScreen.js index a73517f0fe..1422b0b34b 100644 --- a/packages/apps/stock/src/screens/loader/LoaderScreen.js +++ b/packages/apps/stock/src/screens/loader/LoaderScreen.js @@ -48,7 +48,7 @@ const LoaderScreen = () => { setTimeout(() => { resolve('Process completed'); // reject('Process failed'); - }, 10000); + }, 15000); }); const handleSuccessAction = () => { @@ -76,7 +76,7 @@ const LoaderScreen = () => { onSuccess={handleSuccessAction} onError={handleErrorAction} disabled={false} - autoLeave={true} + autoLeave={false} /> diff --git a/packages/core/src/app/RootNavigator.js b/packages/core/src/app/RootNavigator.js index 8a06040bd1..a35061e9d8 100644 --- a/packages/core/src/app/RootNavigator.js +++ b/packages/core/src/app/RootNavigator.js @@ -30,6 +30,7 @@ import {useTranslator} from '../i18n'; import {showToastMessage} from '../utils'; import {logout} from '../features/authSlice'; import {useSessionExpired} from '../apiProviders/config'; +import {useLoaderListener} from '../components'; const {Navigator, Screen} = createNativeStackNavigator(); @@ -49,6 +50,7 @@ const RootNavigator = ({ const {sessionExpired} = useSessionExpired(); const {registerHeaderBand} = useHeaderBand(); + const {numberProcesses} = useLoaderListener(); const {logged} = useSelector(state => state.auth); @@ -121,6 +123,18 @@ const RootNavigator = ({ } }, [handleSessionExpired, sessionExpired]); + useEffect(() => { + registerHeaderBand({ + key: 'loader', + text: I18n.t('Base_Loader_ProccessesInBackground', { + numberProcesses, + }), + color: Colors.cautionColor, + order: 15, + showIf: numberProcesses > 0, + }); + }, [I18n, Colors, registerHeaderBand, numberProcesses]); + return ( {!logged ? ( diff --git a/packages/core/src/components/templates/Loader/LoaderPopup.tsx b/packages/core/src/components/templates/Loader/LoaderPopup.tsx index 10ddc5ad93..b97f38e7f4 100644 --- a/packages/core/src/components/templates/Loader/LoaderPopup.tsx +++ b/packages/core/src/components/templates/Loader/LoaderPopup.tsx @@ -29,7 +29,7 @@ import { } from '@axelor/aos-mobile-ui'; import {useTranslator} from '../../../i18n'; import {useNavigation} from '../../../hooks/use-navigation'; -import useLoaderListner from './use-loader-listener'; +import useProcessRegister from './use-process-register'; import {processProvider} from './ProcessProvider'; interface LoaderPopupProps { @@ -60,7 +60,7 @@ const LoaderPopup = ({ const timeoutRef = useRef(null); - const {processItem, loading} = useLoaderListner( + const {processItem, loading} = useProcessRegister( { disabled, autoLeave, diff --git a/packages/core/src/components/templates/Loader/ProcessProvider.ts b/packages/core/src/components/templates/Loader/ProcessProvider.ts index 5c7b571ddc..0643e6923b 100644 --- a/packages/core/src/components/templates/Loader/ProcessProvider.ts +++ b/packages/core/src/components/templates/Loader/ProcessProvider.ts @@ -30,10 +30,16 @@ import { class ProcessProvider { private _events: Map; private _processMap: Map; + private _numberOfRunningProcess: number; constructor() { this._events = new Map(); this._processMap = new Map(); + this._numberOfRunningProcess = 0; + } + + get numberOfRunningProcess() { + return this._numberOfRunningProcess; } on(key: string, e: EventType, c: callBack) { @@ -106,6 +112,8 @@ class ProcessProvider { completed: true, }); + this._decrementNumberOfRunningProcess(); + this.emit( p.key, status === ProcessStatus.COMPLETED @@ -155,6 +163,8 @@ class ProcessProvider { status: ProcessStatus.RUNNING, }); + this._incrementNumberOfRunningProcess(); + this.on(p.key, EventType.COMPLETED, this.onCompleted); this.on(p.key, EventType.FAILED, this.onFailed); this.emit(p.key, EventType.STARTED); @@ -168,6 +178,17 @@ class ProcessProvider { this.onFinish(p, ProcessStatus.FAILED, error, I18n); } } + + private _incrementNumberOfRunningProcess() { + this._numberOfRunningProcess++; + } + + private _decrementNumberOfRunningProcess() { + this._numberOfRunningProcess = Math.max( + 0, + this._numberOfRunningProcess - 1, + ); + } } export const processProvider = new ProcessProvider(); diff --git a/packages/core/src/components/templates/Loader/index.ts b/packages/core/src/components/templates/Loader/index.ts index 6a54e97a96..b0900b7b7e 100644 --- a/packages/core/src/components/templates/Loader/index.ts +++ b/packages/core/src/components/templates/Loader/index.ts @@ -19,4 +19,5 @@ export {default as LoaderPopup} from './LoaderPopup'; export * from './ProcessProvider'; export * from './types'; -export {default as useLoaderListner} from './use-loader-listener'; +export {default as useLoaderListener} from './use-loader-listener'; +export {default as useProcessRegister} from './use-process-register'; diff --git a/packages/core/src/components/templates/Loader/use-loader-listener.ts b/packages/core/src/components/templates/Loader/use-loader-listener.ts index 6e03f8d974..7e91a97add 100644 --- a/packages/core/src/components/templates/Loader/use-loader-listener.ts +++ b/packages/core/src/components/templates/Loader/use-loader-listener.ts @@ -17,40 +17,17 @@ */ import {useEffect, useMemo, useState} from 'react'; -import {EventType, ProcessItem, ProcessOption} from './types'; import {processProvider} from './ProcessProvider'; -import {generateUniqueID} from './loader-helper'; -const useLoaderListner = ( - processOptions: ProcessOption, - onFinish = () => {}, -) => { - const [key, setKey] = useState(); - const [loading, setLoading] = useState(false); - const [processItem, setProcessItem] = useState(); - - const onFinishCallback = () => { - setLoading(false); - onFinish(); - }; +const useLoaderListener = () => { + const [numberProcesses, setNumberProcesses] = useState(0); useEffect(() => { - const unid = generateUniqueID(); - const p = processProvider.registerProcess(unid, processOptions); - - processProvider.on(unid, EventType.STARTED, () => setLoading(true)); - processProvider.on(unid, EventType.COMPLETED, onFinishCallback); - processProvider.on(unid, EventType.FAILED, onFinishCallback); - - setKey(unid); - setProcessItem(p); + setNumberProcesses(processProvider.numberOfRunningProcess); // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [processProvider.numberOfRunningProcess]); - return useMemo( - () => ({key, processItem, loading}), - [key, processItem, loading], - ); + return useMemo(() => ({numberProcesses}), [numberProcesses]); }; -export default useLoaderListner; +export default useLoaderListener; diff --git a/packages/core/src/components/templates/Loader/use-process-register.ts b/packages/core/src/components/templates/Loader/use-process-register.ts new file mode 100644 index 0000000000..89a4bd9a46 --- /dev/null +++ b/packages/core/src/components/templates/Loader/use-process-register.ts @@ -0,0 +1,56 @@ +/* + * Axelor Business Solutions + * + * Copyright (C) 2024 Axelor (). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import {useEffect, useMemo, useState} from 'react'; +import {EventType, ProcessItem, ProcessOption} from './types'; +import {processProvider} from './ProcessProvider'; +import {generateUniqueID} from './loader-helper'; + +const useProcessRegister = ( + processOptions: ProcessOption, + onFinish = () => {}, +) => { + const [key, setKey] = useState(); + const [loading, setLoading] = useState(false); + const [processItem, setProcessItem] = useState(); + + const onFinishCallback = () => { + setLoading(false); + onFinish(); + }; + + useEffect(() => { + const unid = generateUniqueID(); + const p = processProvider.registerProcess(unid, processOptions); + + processProvider.on(unid, EventType.STARTED, () => setLoading(true)); + processProvider.on(unid, EventType.COMPLETED, onFinishCallback); + processProvider.on(unid, EventType.FAILED, onFinishCallback); + + setKey(unid); + setProcessItem(p); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return useMemo( + () => ({key, processItem, loading}), + [key, processItem, loading], + ); +}; + +export default useProcessRegister; diff --git a/packages/core/src/i18n/translations/en.json b/packages/core/src/i18n/translations/en.json index e92cd022b7..35f1c4e9b6 100644 --- a/packages/core/src/i18n/translations/en.json +++ b/packages/core/src/i18n/translations/en.json @@ -164,6 +164,7 @@ "Base_Loader_DoNotCloseTheApp": "Do not close the application until the process is done.", "Base_Loader_NotifyMe": "Notify me when it's ready", "Base_Loader_LoadingInProgress": "Loading in progress", + "Base_Loader_ProccessesInBackground": "{{numberProcesses}} processes are running in background. Do not close the application.", "Base_SliceAction_FetchAttachedFiles": "fetch attached files", "Base_SliceAction_FetchFilesDetails": "fetch file details", "Base_SliceAction_FetchMetaModule": "fetch meta modules", diff --git a/packages/core/src/i18n/translations/fr.json b/packages/core/src/i18n/translations/fr.json index 64999dec9c..36bfbfbd9a 100644 --- a/packages/core/src/i18n/translations/fr.json +++ b/packages/core/src/i18n/translations/fr.json @@ -164,6 +164,7 @@ "Base_Loader_DoNotCloseTheApp": "Veuillez ne pas fermer l'application avant que le processus soit terminé.", "Base_Loader_NotifyMe": "M'avertir lorsqu'il est prêt", "Base_Loader_LoadingInProgress": "Chargement en cours", + "Base_Loader_ProccessesInBackground": "{{numberProcesses}} processus sont en cours d'exécution en arrière-plan. Veuillez ne pas fermer l'application.", "Base_SliceAction_FetchAttachedFiles": "récupération des fichiers joints", "Base_SliceAction_FetchFilesDetails": "récupération des détails du fichier", "Base_SliceAction_FetchMetaModule": "récupération des modules",