diff --git a/src/components/AppIntlLoader.js b/src/components/AppIntlLoader.js
index 68623edaf..801e3cd45 100644
--- a/src/components/AppIntlLoader.js
+++ b/src/components/AppIntlLoader.js
@@ -7,9 +7,10 @@ import rtlDetect from 'rtl-detect';
import * as R from 'ramda';
import { AppIntlProvider } from './AppIntlProvider';
+import { useSplashLoading } from '../hooks/state';
+import { useWatch } from '../hooks';
import withDashboardActions from '../containers/Dashboard/withDashboardActions';
-import withDashboard from '../containers/Dashboard/withDashboard';
const SUPPORTED_LOCALES = [
{ name: 'English', value: 'en' },
@@ -63,20 +64,13 @@ function transformMomentLocale(currentLocale) {
}
/**
- * Application Intl loader.
+ * Loads application locales of the given current locale.
+ * @param {string} currentLocale
+ * @returns {{ isLoading: boolean }}
*/
-function AppIntlLoader({ appIntlIsLoading, setAppIntlIsLoading, children }) {
- const [isLocalsLoading, setIsLocalsLoading] = React.useState(true);
- const [isYupLoading, setIsYupLoading] = React.useState(true);
-
- // Retrieve the current locale.
- const currentLocale = getCurrentLocal();
-
- // Detarmines the document direction based on the given locale.
- const isRTL = rtlDetect.isRtlLang(currentLocale);
-
- // Modifies the html document direction
- useDocumentDirectionModifier(currentLocale, isRTL);
+function useAppLoadLocales(currentLocale) {
+ const [startLoading, stopLoading] = useSplashLoading();
+ const [isLoading, setIsLoading] = React.useState(true);
React.useEffect(() => {
// Lodas the locales data file.
@@ -91,33 +85,72 @@ function AppIntlLoader({ appIntlIsLoading, setAppIntlIsLoading, children }) {
})
.then(() => {
moment.locale(transformMomentLocale(currentLocale));
- setIsLocalsLoading(false);
+ setIsLoading(false);
});
- }, [currentLocale, setIsLocalsLoading]);
+ }, [currentLocale, stopLoading]);
+
+ // Watches the value to start/stop splash screen.
+ useWatch(isLoading, (value) => (value ? startLoading() : stopLoading()), {
+ immediate: true,
+ });
+
+ return { isLoading };
+}
+
+/**
+ * Loads application yup locales based on the given current locale.
+ * @param {string} currentLocale
+ * @returns {{ isLoading: boolean }}
+ */
+function useAppYupLoadLocales(currentLocale) {
+ const [startLoading, stopLoading] = useSplashLoading();
+ const [isLoading, setIsLoading] = React.useState(true);
React.useEffect(() => {
loadYupLocales(currentLocale)
.then(({ locale }) => {
setLocale(locale);
- setIsYupLoading(false);
+ setIsLoading(false);
})
.then(() => {});
- }, [currentLocale]);
+ }, [currentLocale, stopLoading]);
- React.useEffect(() => {
- if (!isLocalsLoading && !isYupLoading) {
- setAppIntlIsLoading(false);
- }
+ // Watches the valiue to start/stop splash screen.
+ useWatch(isLoading, (value) => (value ? startLoading() : stopLoading()), {
+ immediate: true,
});
+ return { isLoading };
+}
+
+/**
+ * Application Intl loader.
+ */
+function AppIntlLoader({ children }) {
+ // Retrieve the current locale.
+ const currentLocale = getCurrentLocal();
+
+ // Detarmines the document direction based on the given locale.
+ const isRTL = rtlDetect.isRtlLang(currentLocale);
+
+ // Modifies the html document direction
+ useDocumentDirectionModifier(currentLocale, isRTL);
+
+ // Loads yup localization of the given locale.
+ const { isLoading: isAppYupLocalesLoading } =
+ useAppYupLoadLocales(currentLocale);
+
+ // Loads application locales of the given locale.
+ const { isLoading: isAppLocalesLoading } = useAppLoadLocales(currentLocale);
+
+ // Detarmines whether the app locales loading.
+ const isLoading = isAppYupLocalesLoading && isAppLocalesLoading;
+
return (
- {appIntlIsLoading ? null : children}
+ {isLoading ? null : children}
);
}
-export default R.compose(
- withDashboardActions,
- withDashboard(({ appIntlIsLoading }) => ({ appIntlIsLoading })),
-)(AppIntlLoader);
+export default R.compose(withDashboardActions)(AppIntlLoader);
diff --git a/src/components/Authentication.js b/src/components/Authentication.js
index a9b8575c2..a34060825 100644
--- a/src/components/Authentication.js
+++ b/src/components/Authentication.js
@@ -6,7 +6,6 @@ import authenticationRoutes from 'routes/authentication';
import { FormattedMessage as T } from 'components';
import Icon from 'components/Icon';
import { useIsAuthenticated } from 'hooks/state';
-import {AuthenticationBoot} from '../containers/Authentication/AuthenticationBoot';
import 'style/pages/Authentication/Auth.scss';
function PageFade(props) {
@@ -26,7 +25,6 @@ export default function AuthenticationWrapper({ ...rest }) {
) : (
-
{
- // Once the all requests complete change the app loading state.
- if (
- isAuthUserSuccess &&
+ const [startLoading, stopLoading] = useSplashLoading();
+
+ // Splash loading when organization request loading and
+ // applicaiton still not booted.
+ useWatch(isOrgLoading, (value) => {
+ value && !isBooted.current && startLoading();
+ });
+
+ // Splash loading when request authenticated user loading and
+ // application still not booted yet.
+ useWatch(isAuthUserLoading, (value) => {
+ value && !isBooted.current && startLoading();
+ });
+
+ // Stop splash loading once organization request success.
+ useWatch(isCurrentOrganizationSuccess, (value) => {
+ value && stopLoading();
+ });
+
+ // Stop splash loading once authenticated user request success.
+ useWatch(isAuthUserSuccess, (value) => {
+ value && stopLoading();
+ });
+
+ // Once the all requests complete change the app loading state.
+ useWhen(
+ isAuthUserSuccess &&
isCurrentOrganizationSuccess &&
- localeCookie === organization?.metadata?.language
- ) {
- setAppIsLoading(false);
+ localeCookie === organization?.metadata?.language,
+ () => {
isBooted.current = true;
- }
- }, [
- isAuthUserSuccess,
- isCurrentOrganizationSuccess,
- organization,
- setAppIsLoading,
- localeCookie,
- ]);
+ },
+ );
return null;
}
@@ -72,5 +95,4 @@ export const DashboardBoot = R.compose(
withAuthentication(({ authenticatedUserId }) => ({
authenticatedUserId,
})),
- withDashboardActions,
)(DashboardBootJSX);
diff --git a/src/components/Dashboard/PrivatePages.js b/src/components/Dashboard/PrivatePages.js
index 8c2a2d421..643752389 100644
--- a/src/components/Dashboard/PrivatePages.js
+++ b/src/components/Dashboard/PrivatePages.js
@@ -4,10 +4,9 @@ import { Switch, Route } from 'react-router';
import Dashboard from 'components/Dashboard/Dashboard';
import SetupWizardPage from 'containers/Setup/WizardSetupPage';
-import EnsureOrganizationIsReady from 'components/Guards/EnsureOrganizationIsReady';
-import EnsureOrganizationIsNotReady from 'components/Guards/EnsureOrganizationIsNotReady';
+import EnsureOrganizationIsReady from '../../components/Guards/EnsureOrganizationIsReady';
+import EnsureOrganizationIsNotReady from '../../components/Guards/EnsureOrganizationIsNotReady';
import { PrivatePagesProvider } from './PrivatePagesProvider';
-import { DashboardBoot } from '../../components';
import 'style/pages/Dashboard/Dashboard.scss';
@@ -17,8 +16,6 @@ import 'style/pages/Dashboard/Dashboard.scss';
export default function DashboardPrivatePages() {
return (
-
-
diff --git a/src/components/Dashboard/PrivatePagesProvider.js b/src/components/Dashboard/PrivatePagesProvider.js
index 956c6063a..1f7098817 100644
--- a/src/components/Dashboard/PrivatePagesProvider.js
+++ b/src/components/Dashboard/PrivatePagesProvider.js
@@ -1,9 +1,31 @@
import React from 'react';
+import * as R from 'ramda';
+
import { AuthenticatedUser } from './AuthenticatedUser';
+import { DashboardBoot } from '../../components';
+
+import withDashboard from '../../containers/Dashboard/withDashboard';
/**
* Private pages provider.
*/
-export function PrivatePagesProvider({ children }) {
- return {children};
+function PrivatePagesProviderComponent({
+ splashScreenCompleted,
+
+ // #ownProps
+ children,
+}) {
+ return (
+
+
+
+ {splashScreenCompleted ? children : null}
+
+ );
}
+
+export const PrivatePagesProvider = R.compose(
+ withDashboard(({ splashScreenCompleted }) => ({
+ splashScreenCompleted,
+ })),
+)(PrivatePagesProviderComponent);
diff --git a/src/components/Dashboard/SplashScreen.js b/src/components/Dashboard/SplashScreen.js
index fc32548e4..238cc94f0 100644
--- a/src/components/Dashboard/SplashScreen.js
+++ b/src/components/Dashboard/SplashScreen.js
@@ -3,13 +3,12 @@ import * as R from 'ramda';
import BigcapitalLoading from './BigcapitalLoading';
import withDashboard from '../../containers/Dashboard/withDashboard';
-function SplashScreenComponent({ appIsLoading, appIntlIsLoading }) {
- return appIsLoading || appIntlIsLoading ? : null;
+function SplashScreenComponent({ splashScreenLoading }) {
+ return splashScreenLoading ? : null;
}
export const SplashScreen = R.compose(
- withDashboard(({ appIsLoading, appIntlIsLoading }) => ({
- appIsLoading,
- appIntlIsLoading,
+ withDashboard(({ splashScreenLoading }) => ({
+ splashScreenLoading,
})),
)(SplashScreenComponent);
diff --git a/src/components/Guards/EnsureOrganizationIsReady.js b/src/components/Guards/EnsureOrganizationIsReady.js
index ba9e8024f..097efcdd4 100644
--- a/src/components/Guards/EnsureOrganizationIsReady.js
+++ b/src/components/Guards/EnsureOrganizationIsReady.js
@@ -6,7 +6,6 @@ import { compose } from 'utils';
import withAuthentication from 'containers/Authentication/withAuthentication';
import withOrganization from 'containers/Organization/withOrganization';
-
function EnsureOrganizationIsReady({
// #ownProps
children,
@@ -15,10 +14,10 @@ function EnsureOrganizationIsReady({
// #withOrganizationByOrgId
isOrganizationReady,
}) {
- return (isOrganizationReady) ? children : (
-
+ return isOrganizationReady ? (
+ children
+ ) : (
+
);
}
@@ -28,4 +27,4 @@ export default compose(
organizationId: props.currentOrganizationId,
})),
withOrganization(({ isOrganizationReady }) => ({ isOrganizationReady })),
-)(EnsureOrganizationIsReady);
\ No newline at end of file
+)(EnsureOrganizationIsReady);
diff --git a/src/containers/Authentication/AuthenticationBoot.js b/src/containers/Authentication/AuthenticationBoot.js
deleted file mode 100644
index 4ac6c9899..000000000
--- a/src/containers/Authentication/AuthenticationBoot.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import React from 'react';
-import * as R from 'ramda';
-
-import withDashboardActions from '../../containers/Dashboard/withDashboardActions';
-
-function AuthenticationBootJSX({ setAppIsLoading }) {
- React.useEffect(() => {
- setAppIsLoading(false);
- }, [setAppIsLoading]);
-
- return null;
-}
-export const AuthenticationBoot = R.compose(withDashboardActions)(
- AuthenticationBootJSX,
-);
diff --git a/src/containers/Dashboard/withDashboard.js b/src/containers/Dashboard/withDashboard.js
index d4303c6dc..d3e885adc 100644
--- a/src/containers/Dashboard/withDashboard.js
+++ b/src/containers/Dashboard/withDashboard.js
@@ -11,8 +11,8 @@ export default (mapState) => {
sidebarExpended: state.dashboard.sidebarExpended,
preferencesPageTitle: state.dashboard.preferencesPageTitle,
dashboardBackLink: state.dashboard.backLink,
- appIsLoading: state.dashboard.appIsLoading,
- appIntlIsLoading: state.dashboard.appIntlIsLoading
+ splashScreenLoading: state.dashboard.splashScreenLoading > 0,
+ splashScreenCompleted: state.dashboard.splashScreenLoading === 0,
};
return mapState ? mapState(mapped, state, props) : mapped;
};
diff --git a/src/containers/Dashboard/withDashboardActions.js b/src/containers/Dashboard/withDashboardActions.js
index 8c188539e..69b00a3d4 100644
--- a/src/containers/Dashboard/withDashboardActions.js
+++ b/src/containers/Dashboard/withDashboardActions.js
@@ -2,9 +2,8 @@ import { connect } from 'react-redux';
import t from 'store/types';
import {
toggleExpendSidebar,
- appIsLoading,
- appIntlIsLoading
} from 'store/dashboard/dashboard.actions';
+import { splashStartLoading, splashStopLoading } from '../../store/dashboard/dashboard.actions';
const mapActionsToProps = (dispatch) => ({
changePageTitle: (pageTitle) =>
@@ -50,15 +49,17 @@ const mapActionsToProps = (dispatch) => ({
dispatch({
type: 'CHANGE_PREFERENCES_PAGE_TITLE',
pageTitle,
- }),
+ }),
setDashboardBackLink: (backLink) =>
dispatch({
type: t.SET_DASHBOARD_BACK_LINK,
payload: { backLink },
}),
- setAppIsLoading: (isLoading) => dispatch(appIsLoading(isLoading)),
- setAppIntlIsLoading: (isLoading) => dispatch(appIntlIsLoading(isLoading)),
+
+ // Splash screen start/stop loading.
+ splashStartLoading: () => splashStartLoading(),
+ splashStopLoading: () => splashStopLoading(),
});
export default connect(null, mapActionsToProps);
diff --git a/src/hooks/index.js b/src/hooks/index.js
index cc615a214..dbb2944e0 100644
--- a/src/hooks/index.js
+++ b/src/hooks/index.js
@@ -1,3 +1,4 @@
+import { isEqual } from 'lodash';
import React, { useRef, useEffect, useMemo } from 'react';
import useAsync from './async';
import useAutofocus from './useAutofocus';
@@ -97,7 +98,6 @@ export function useLocalStorage(key, initialValue) {
return [storedValue, setValue];
}
-
export function useMemorizedColumnsWidths(tableName) {
const [get, save] = useLocalStorage(`${tableName}.columns_widths`, {});
@@ -105,4 +105,43 @@ export function useMemorizedColumnsWidths(tableName) {
save(columnsResizing.columnWidths);
};
return [get, save, handleColumnResizing];
-}
\ No newline at end of file
+}
+
+// Hook
+function usePrevious(value) {
+ // The ref object is a generic container whose current property is mutable ...
+ // ... and can hold any value, similar to an instance property on a class
+ const ref = useRef();
+ // Store current value in ref
+ useEffect(() => {
+ ref.current = value;
+ }, [value]); // Only re-run if value changes
+ // Return previous value (happens before update in useEffect above)
+ return ref.current;
+}
+
+export function useWhen(condition, callback) {
+ React.useEffect(() => {
+ if (condition) {
+ callback();
+ }
+ }, [condition, callback]);
+}
+
+export function useWhenNot(condition, callback) {
+ return useWhen(!condition, callback);
+}
+
+export function useWatch(state, callback, props) {
+ const config = { immediate: false, ...props };
+
+ const previosuState = usePrevious(state);
+ const flag = React.useRef(false);
+
+ React.useEffect(() => {
+ if (!isEqual(previosuState, state) || (config.immediate && !flag.current)) {
+ flag.current = true;
+ callback(state);
+ }
+ }, [previosuState, state, config.immediate, callback]);
+}
diff --git a/src/hooks/state/authentication.js b/src/hooks/state/authentication.js
index 6116988b7..8a0afd385 100644
--- a/src/hooks/state/authentication.js
+++ b/src/hooks/state/authentication.js
@@ -3,11 +3,13 @@ import { useCallback } from 'react';
import { isAuthenticated } from 'store/authentication/authentication.reducer';
import {
setLogin,
- setStoreReset,
} from 'store/authentication/authentication.actions';
import { useQueryClient } from 'react-query';
import { removeCookie } from '../../utils';
+/**
+ * Removes the authentication cookies.
+ */
function removeAuthenticationCookies() {
removeCookie('token');
removeCookie('organization_id');
diff --git a/src/hooks/state/dashboard.js b/src/hooks/state/dashboard.js
index 542fe458b..04c22e37f 100644
--- a/src/hooks/state/dashboard.js
+++ b/src/hooks/state/dashboard.js
@@ -1,6 +1,10 @@
import { useCallback } from 'react';
import { useDispatch } from 'react-redux';
-import { dashboardPageTitle } from 'store/dashboard/dashboard.actions';
+import {
+ splashStopLoading,
+ splashStartLoading,
+ dashboardPageTitle,
+} from '../../store/dashboard/dashboard.actions';
export const useDispatchAction = (action) => {
const dispatch = useDispatch();
@@ -17,11 +21,12 @@ export const useDashboardPageTitle = () => {
return useDispatchAction(dashboardPageTitle);
};
-export const useSetAccountsTableQuery = () => {
-
+/**
+ * Splash loading screen actions.
+ */
+export const useSplashLoading = () => {
+ return [
+ useDispatchAction(splashStartLoading),
+ useDispatchAction(splashStopLoading),
+ ];
};
-
-export const useAccountsTableQuery = () => {
-
-}
-