Skip to content

Commit

Permalink
feat: create loader context [Not Completed]
Browse files Browse the repository at this point in the history
  • Loading branch information
hel-axelor committed Jan 29, 2024
1 parent 88981d4 commit 6492620
Show file tree
Hide file tree
Showing 6 changed files with 270 additions and 61 deletions.
28 changes: 21 additions & 7 deletions packages/apps/stock/src/screens/loader/LoaderScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,33 +34,47 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import React from 'react';
import React, {useState} from 'react';
import {View} from 'react-native';
import {Screen} from '@axelor/aos-mobile-ui';
import {Button, Screen} from '@axelor/aos-mobile-ui';
import {LoaderPopup} from '@axelor/aos-mobile-core';

// Screen for test Loader functionnalities
const LoaderScreen = () => {
const [runProccess, setRunProccess] = useState(false);

const process = () =>
new Promise(resolve => {
setTimeout(() => {
resolve('Process finished');
}, 10000);
});

const handleCustomAction = () => {
console.log('Custom action executed!');
const handleSuccessAction = () => {
setRunProccess(false);
console.log('Success action executed!');
};

const handleErrorAction = () => {
setRunProccess(false);
console.log('Error action executed!');
};

return (
<Screen>
<View>
<Button
title="Run process"
onPress={() => setRunProccess(true)}
disabled={runProccess}
/>
<LoaderPopup
process={process}
runProccess={runProccess}
timeout={5000}
onSuccess={handleCustomAction}
onError={() => console.warn('An error has occurred!')}
disabled={true}
onSuccess={handleSuccessAction}
onError={handleErrorAction}
disabled={false}
/>
</View>
</Screen>
Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/app/ContextsProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {requestBuilder} from '../apiProviders/Standard/requests.helper';
import {core_modelAPI, core_searchFields, core_sortFields} from '../models';
import {HeaderBandProvider} from '../header';
import {addModuleForms, formConfigsProvider} from '../forms';
import {LoaderProvider} from '../components';

const ApplicationContext = createContext(null);

Expand Down Expand Up @@ -166,7 +167,9 @@ const ContextsProvider = ({
defaultTheme={defaultWritingTheme}
writingStylesConfig={writingStylesConfig}>
<ConfigProvider showModulesSubtitle={showModulesSubtitle}>
<HeaderBandProvider>{children}</HeaderBandProvider>
<HeaderBandProvider>
<LoaderProvider>{children}</LoaderProvider>
</HeaderBandProvider>
</ConfigProvider>
</WritingThemeProvider>
</ThemeProvider>
Expand Down
131 changes: 131 additions & 0 deletions packages/core/src/components/templates/Loader/LoaderContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2024 Axelor (<http://axelor.com>).
*
* 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 <http://www.gnu.org/licenses/>.
*/

import React, {
createContext,
useCallback,
useContext,
useMemo,
useReducer,
} from 'react';

interface LoaderContextState {
notifyMe: boolean;
showPopup: boolean;
getCurrentNotifyMe: () => boolean;
setNotifyMe: (option: boolean) => void;
setShowPopup: (option: boolean) => void;
}

interface LoaderAction {
type: string;
payload: boolean;
}

const defaultLoaderContext = {
notifyMe: false,
showPopup: false,
getCurrentNotifyMe: () => {
throw new Error(
'LoaderProvider should be mounted to get current notify me',
);
},
setNotifyMe: () => {
throw new Error('LoaderProvider should be mounted to set notify me');
},
setShowPopup: () => {
throw new Error('LoaderProvider should be mounted to show popup');
},
};

const LoaderContext = createContext<LoaderContextState>(defaultLoaderContext);

const actionTypes = {
getCurrentNotifyMe: 'getCurrentNotifyMe',
setNotifyMe: 'setNotifyMe',
setShowPopup: 'setShowPopup',
};

const LoaderReducer = (
state: LoaderContextState,
action: LoaderAction,
): LoaderContextState => {
switch (action.type) {
case actionTypes.setNotifyMe: {
return {
...state,
notifyMe: action.payload as boolean,
};
}
case actionTypes.setShowPopup: {
return {
...state,
showPopup: action.payload as boolean,
};
}
}
};

const actions = {
setNotifyMe: option => ({
type: actionTypes.setNotifyMe,
payload: option,
}),
setShowPopup: option => ({
type: actionTypes.setShowPopup,
payload: option,
}),
};

export const LoaderProvider = ({children}) => {
const [state, dispatch] = useReducer(LoaderReducer, defaultLoaderContext);

const getCurrentNotifyMe = useCallback(() => {
console.log('getCurrentNotifyMe', state.notifyMe);
return state.notifyMe;
}, [state]);

const setNotifyMe = useCallback(option => {
console.log('setNotifyMe', option);
dispatch(actions.setNotifyMe(option));
}, []);

const setShowPopup = useCallback(
option => dispatch(actions.setShowPopup(option)),
[],
);

const LoaderContextState = useMemo<LoaderContextState>(
() => ({
...state,
getCurrentNotifyMe,
setNotifyMe,
setShowPopup,
}),
[state, getCurrentNotifyMe, setNotifyMe, setShowPopup],
);

return (
<LoaderContext.Provider value={LoaderContextState}>
{children}
</LoaderContext.Provider>
);
};

export const useLoader = (): LoaderContextState =>
useContext<LoaderContextState>(LoaderContext);
57 changes: 30 additions & 27 deletions packages/core/src/components/templates/Loader/LoaderPopup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import React, {useCallback, useEffect, useRef, useState} from 'react';
import React, {useEffect, useRef} from 'react';
import {ActivityIndicator, Dimensions, StyleSheet, View} from 'react-native';
import {
BlockInteractionScreen,
Expand All @@ -30,11 +30,13 @@ import {
import {useTranslator} from '../../../i18n';
import {useNavigation} from '../../../hooks/use-navigation';
import useLoaderListner from './use-loader-listener';
import {useLoader} from './LoaderContext';

interface LoaderPopupProps {
process: () => Promise<any>;
onSuccess: () => void;
onError: () => void;
runProccess: boolean;
timeout: number;
disabled: boolean;
}
Expand All @@ -43,53 +45,54 @@ const LoaderPopup = ({
process,
onSuccess,
onError,
runProccess = false,
timeout = 100,
disabled = false,
}: LoaderPopupProps) => {
const navigation = useNavigation();
const I18n = useTranslator();
const Colors = useThemeColor();
const {setActivityIndicator} = useConfig();

const timeoutRef = useRef(null);

const {notifyMe, showPopup, setNotifyMe, setShowPopup} = useLoader();

const {loading, listener} = useLoaderListner({
process,
onSuccess: () => {
setShowPopup(false);
!disabled && onSuccess();
},
onError: () => {
setShowPopup(false);
!disabled && onError();
},
onFinish: () => setShowPopup(false),
onSuccess,
onError,
disabled,
});

const [showPopup, setShowPopup] = useState<boolean>(false);

const handleGoBack = useCallback(() => {
navigation.goBack();
}, [navigation]);
useEffect(() => {
if (notifyMe) {
navigation.goBack();
}
}, [navigation, notifyMe]);

useEffect(() => {
listener();
if (runProccess) {
listener();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
}, [runProccess]);

useEffect(() => {
if (loading && !showPopup) {
setActivityIndicator(true);
}
if (loading) {
if (!showPopup) {
setActivityIndicator(true);
}

if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = setTimeout(() => {
setActivityIndicator(false);
setShowPopup(true);
}, timeout);
}

timeoutRef.current = setTimeout(() => {
setActivityIndicator(false);
setShowPopup(true);
}, timeout);

return () => {
setActivityIndicator(false);
clearTimeout(timeoutRef.current);
};
}, [timeout, loading, showPopup, setActivityIndicator, setShowPopup]);
Expand Down Expand Up @@ -118,7 +121,7 @@ const LoaderPopup = ({
<Button
iconName="check-lg"
title={I18n.t('Base_Loader_NotifyMe')}
onPress={handleGoBack}
onPress={() => setNotifyMe(true)}
/>
</Card>
</BlockInteractionScreen>
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/components/templates/Loader/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@

export {default as LoaderPopup} from './LoaderPopup';
export {default as useLoaderListner} from './use-loader-listener';
export * from './LoaderContext';
Loading

0 comments on commit 6492620

Please sign in to comment.