2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-07-02 03:30:54 +00:00

feat(frontend): reduce flickering on reload (#9897)

* rename states to better fit naming conv

* fix name

* ensure splashscreen only loads after login check
this reduces a short splash of the splashscreen even if logged in

* move loader to the middle of the screen and remove fallback on a view screens
This commit is contained in:
Matthias Mair
2025-06-29 02:45:51 +02:00
committed by GitHub
parent ce8ece4f10
commit 4c11f8c911
69 changed files with 127 additions and 79 deletions

View File

@ -59,4 +59,7 @@ export interface UserStateProps {
isLoggedIn: () => boolean; isLoggedIn: () => boolean;
isStaff: () => boolean; isStaff: () => boolean;
isSuperuser: () => boolean; isSuperuser: () => boolean;
// login state
login_checked: boolean;
setLoginChecked: (value: boolean) => void;
} }

View File

@ -2,7 +2,8 @@ import { BackgroundImage } from '@mantine/core';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useShallow } from 'zustand/react/shallow'; import { useShallow } from 'zustand/react/shallow';
import { generateUrl } from '../functions/urls'; import { generateUrl } from '../functions/urls';
import { useServerApiState } from '../states/ApiState'; import { useServerApiState } from '../states/ServerApiState';
import { useUserState } from '../states/UserState';
/** /**
* Render content within a "splash screen" container. * Render content within a "splash screen" container.
@ -15,6 +16,9 @@ export default function SplashScreen({
const [server, fetchServerApiState] = useServerApiState( const [server, fetchServerApiState] = useServerApiState(
useShallow((state) => [state.server, state.fetchServerApiState]) useShallow((state) => [state.server, state.fetchServerApiState])
); );
const [checked_login] = useUserState(
useShallow((state) => [state.login_checked])
);
// Fetch server data on mount if no server data is present // Fetch server data on mount if no server data is present
useEffect(() => { useEffect(() => {
@ -23,7 +27,7 @@ export default function SplashScreen({
} }
}, [server]); }, [server]);
if (server.customize?.splash) { if (server.customize?.splash && checked_login) {
return ( return (
<BackgroundImage src={generateUrl(server.customize.splash)}> <BackgroundImage src={generateUrl(server.customize.splash)}>
{children} {children}

View File

@ -14,7 +14,7 @@ import { IconCamera, IconScan } from '@tabler/icons-react';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
import { useLocalStorage } from '@mantine/hooks'; import { useLocalStorage } from '@mantine/hooks';
import { useGlobalSettingsState } from '../../states/SettingsState'; import { useGlobalSettingsState } from '../../states/SettingsStates';
import { Boundary } from '../Boundary'; import { Boundary } from '../Boundary';
import BarcodeCameraInput from './BarcodeCameraInput'; import BarcodeCameraInput from './BarcodeCameraInput';
import BarcodeKeyboardInput from './BarcodeKeyboardInput'; import BarcodeKeyboardInput from './BarcodeKeyboardInput';

View File

@ -21,7 +21,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
import { ApiEndpoints } from '@lib/enums/ApiEndpoints'; import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
import { apiUrl } from '@lib/functions/Api'; import { apiUrl } from '@lib/functions/Api';
import { api } from '../../App'; import { api } from '../../App';
import { useGlobalSettingsState } from '../../states/SettingsState'; import { useGlobalSettingsState } from '../../states/SettingsStates';
import { CopyButton } from '../buttons/CopyButton'; import { CopyButton } from '../buttons/CopyButton';
import type { QrCodeType } from '../items/ActionDropdown'; import type { QrCodeType } from '../items/ActionDropdown';

View File

@ -6,7 +6,7 @@ import { ModelInformationDict } from '@lib/enums/ModelInformation';
import type { ModelType } from '@lib/enums/ModelType'; import type { ModelType } from '@lib/enums/ModelType';
import { eventModified } from '@lib/functions/Navigation'; import { eventModified } from '@lib/functions/Navigation';
import { generateUrl } from '../../functions/urls'; import { generateUrl } from '../../functions/urls';
import { useServerApiState } from '../../states/ApiState'; import { useServerApiState } from '../../states/ServerApiState';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import { ActionButton } from './ActionButton'; import { ActionButton } from './ActionButton';

View File

@ -13,7 +13,7 @@ import { useCreateApiFormModal } from '../../hooks/UseForm';
import { import {
useGlobalSettingsState, useGlobalSettingsState,
useUserSettingsState useUserSettingsState
} from '../../states/SettingsState'; } from '../../states/SettingsStates';
import { ActionDropdown } from '../items/ActionDropdown'; import { ActionDropdown } from '../items/ActionDropdown';
export function PrintingActions({ export function PrintingActions({

View File

@ -1,7 +1,7 @@
import { t } from '@lingui/core/macro'; import { t } from '@lingui/core/macro';
import { ModelType } from '@lib/enums/ModelType'; import { ModelType } from '@lib/enums/ModelType';
import { useGlobalSettingsState } from '../../states/SettingsState'; import { useGlobalSettingsState } from '../../states/SettingsStates';
import type { DashboardWidgetProps } from './DashboardWidget'; import type { DashboardWidgetProps } from './DashboardWidget';
import ColorToggleDashboardWidget from './widgets/ColorToggleWidget'; import ColorToggleDashboardWidget from './widgets/ColorToggleWidget';
import GetStartedWidget from './widgets/GetStartedWidget'; import GetStartedWidget from './widgets/GetStartedWidget';

View File

@ -26,7 +26,7 @@ import type { InvenTreeIconType } from '@lib/types/Icons';
import { useApi } from '../../contexts/ApiContext'; import { useApi } from '../../contexts/ApiContext';
import { formatDate, formatDecimal } from '../../defaults/formatters'; import { formatDate, formatDecimal } from '../../defaults/formatters';
import { InvenTreeIcon } from '../../functions/icons'; import { InvenTreeIcon } from '../../functions/icons';
import { useGlobalSettingsState } from '../../states/SettingsState'; import { useGlobalSettingsState } from '../../states/SettingsStates';
import { CopyButton } from '../buttons/CopyButton'; import { CopyButton } from '../buttons/CopyButton';
import { YesNoButton } from '../buttons/YesNoButton'; import { YesNoButton } from '../buttons/YesNoButton';
import { ProgressBar } from '../items/ProgressBar'; import { ProgressBar } from '../items/ProgressBar';

View File

@ -28,7 +28,7 @@ import { api } from '../../App';
import { InvenTreeIcon } from '../../functions/icons'; import { InvenTreeIcon } from '../../functions/icons';
import { showApiErrorMessage } from '../../functions/notifications'; import { showApiErrorMessage } from '../../functions/notifications';
import { useEditApiFormModal } from '../../hooks/UseForm'; import { useEditApiFormModal } from '../../hooks/UseForm';
import { useGlobalSettingsState } from '../../states/SettingsState'; import { useGlobalSettingsState } from '../../states/SettingsStates';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import { PartThumbTable } from '../../tables/part/PartThumbTable'; import { PartThumbTable } from '../../tables/part/PartThumbTable';
import { vars } from '../../theme'; import { vars } from '../../theme';

View File

@ -2,7 +2,7 @@ import { ActionIcon, Center, Group, Text, Tooltip } from '@mantine/core';
import { IconServer } from '@tabler/icons-react'; import { IconServer } from '@tabler/icons-react';
import { useShallow } from 'zustand/react/shallow'; import { useShallow } from 'zustand/react/shallow';
import { useServerApiState } from '../../states/ApiState'; import { useServerApiState } from '../../states/ServerApiState';
import { ColorToggle } from '../items/ColorToggle'; import { ColorToggle } from '../items/ColorToggle';
import { LanguageToggle } from '../items/LanguageToggle'; import { LanguageToggle } from '../items/LanguageToggle';

View File

@ -27,7 +27,7 @@ import {
followRedirect followRedirect
} from '../../functions/auth'; } from '../../functions/auth';
import { showLoginNotification } from '../../functions/notifications'; import { showLoginNotification } from '../../functions/notifications';
import { useServerApiState } from '../../states/ApiState'; import { useServerApiState } from '../../states/ServerApiState';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import { SsoButton } from '../buttons/SSOButton'; import { SsoButton } from '../buttons/SSOButton';

View File

@ -23,8 +23,8 @@ import {
import type { HostList } from '@lib/types/Server'; import type { HostList } from '@lib/types/Server';
import { useShallow } from 'zustand/react/shallow'; import { useShallow } from 'zustand/react/shallow';
import { Wrapper } from '../../pages/Auth/Layout'; import { Wrapper } from '../../pages/Auth/Layout';
import { useServerApiState } from '../../states/ApiState';
import { useLocalState } from '../../states/LocalState'; import { useLocalState } from '../../states/LocalState';
import { useServerApiState } from '../../states/ServerApiState';
import { ActionButton } from '../buttons/ActionButton'; import { ActionButton } from '../buttons/ActionButton';
import { HostOptionsForm } from './HostOptionsForm'; import { HostOptionsForm } from './HostOptionsForm';

View File

@ -19,7 +19,7 @@ import { apiUrl } from '@lib/functions/Api';
import { useShallow } from 'zustand/react/shallow'; import { useShallow } from 'zustand/react/shallow';
import { api } from '../../App'; import { api } from '../../App';
import { generateUrl } from '../../functions/urls'; import { generateUrl } from '../../functions/urls';
import { useServerApiState } from '../../states/ApiState'; import { useServerApiState } from '../../states/ServerApiState';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import { CopyButton } from '../buttons/CopyButton'; import { CopyButton } from '../buttons/CopyButton';
import { StylishText } from '../items/StylishText'; import { StylishText } from '../items/StylishText';

View File

@ -3,7 +3,7 @@ import { Badge, Button, Divider, Group, Stack, Table } from '@mantine/core';
import type { ContextModalProps } from '@mantine/modals'; import type { ContextModalProps } from '@mantine/modals';
import { useShallow } from 'zustand/react/shallow'; import { useShallow } from 'zustand/react/shallow';
import { useServerApiState } from '../../states/ApiState'; import { useServerApiState } from '../../states/ServerApiState';
import { OnlyStaff } from '../items/OnlyStaff'; import { OnlyStaff } from '../items/OnlyStaff';
export function ServerInfoModal({ export function ServerInfoModal({

View File

@ -5,8 +5,8 @@ import { useMemo, useState } from 'react';
import { t } from '@lingui/core/macro'; import { t } from '@lingui/core/macro';
import { useShallow } from 'zustand/react/shallow'; import { useShallow } from 'zustand/react/shallow';
import { docLinks } from '../../defaults/links'; import { docLinks } from '../../defaults/links';
import { useServerApiState } from '../../states/ApiState'; import { useServerApiState } from '../../states/ServerApiState';
import { useGlobalSettingsState } from '../../states/SettingsState'; import { useGlobalSettingsState } from '../../states/SettingsStates';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
interface AlertInfo { interface AlertInfo {

View File

@ -26,12 +26,12 @@ import { getNavTabs } from '../../defaults/links';
import { generateUrl } from '../../functions/urls'; import { generateUrl } from '../../functions/urls';
import { usePluginUIFeature } from '../../hooks/UsePluginUIFeature'; import { usePluginUIFeature } from '../../hooks/UsePluginUIFeature';
import * as classes from '../../main.css'; import * as classes from '../../main.css';
import { useServerApiState } from '../../states/ApiState';
import { useLocalState } from '../../states/LocalState'; import { useLocalState } from '../../states/LocalState';
import { useServerApiState } from '../../states/ServerApiState';
import { import {
useGlobalSettingsState, useGlobalSettingsState,
useUserSettingsState useUserSettingsState
} from '../../states/SettingsState'; } from '../../states/SettingsStates';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import { ScanButton } from '../buttons/ScanButton'; import { ScanButton } from '../buttons/ScanButton';
import { SpotlightButton } from '../buttons/SpotlightButton'; import { SpotlightButton } from '../buttons/SpotlightButton';

View File

@ -15,7 +15,7 @@ import { UserRoles } from '@lib/enums/Roles';
import { AboutLinks, DocumentationLinks } from '../../defaults/links'; import { AboutLinks, DocumentationLinks } from '../../defaults/links';
import useInstanceName from '../../hooks/UseInstanceName'; import useInstanceName from '../../hooks/UseInstanceName';
import * as classes from '../../main.css'; import * as classes from '../../main.css';
import { useGlobalSettingsState } from '../../states/SettingsState'; import { useGlobalSettingsState } from '../../states/SettingsStates';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import { InvenTreeLogo } from '../items/InvenTreeLogo'; import { InvenTreeLogo } from '../items/InvenTreeLogo';
import { type MenuLinkItem, MenuLinks } from '../items/MenuLinks'; import { type MenuLinkItem, MenuLinks } from '../items/MenuLinks';

View File

@ -3,7 +3,7 @@ import { useHotkeys } from '@mantine/hooks';
import { Fragment, type ReactNode, useMemo } from 'react'; import { Fragment, type ReactNode, useMemo } from 'react';
import { shortenString } from '../../functions/tables'; import { shortenString } from '../../functions/tables';
import { useUserSettingsState } from '../../states/SettingsState'; import { useUserSettingsState } from '../../states/SettingsStates';
import { ApiImage } from '../images/ApiImage'; import { ApiImage } from '../images/ApiImage';
import { StylishText } from '../items/StylishText'; import { StylishText } from '../items/StylishText';
import { type Breadcrumb, BreadcrumbList } from './BreadcrumbList'; import { type Breadcrumb, BreadcrumbList } from './BreadcrumbList';

View File

@ -1,5 +1,5 @@
import { useEffect, useMemo } from 'react'; import { useEffect, useMemo } from 'react';
import { useGlobalSettingsState } from '../../states/SettingsState'; import { useGlobalSettingsState } from '../../states/SettingsStates';
/** /**
* Component to set the page title * Component to set the page title

View File

@ -41,7 +41,7 @@ import { cancelEvent } from '@lib/functions/Events';
import { eventModified, navigateToLink } from '@lib/functions/Navigation'; import { eventModified, navigateToLink } from '@lib/functions/Navigation';
import { showNotification } from '@mantine/notifications'; import { showNotification } from '@mantine/notifications';
import { api } from '../../App'; import { api } from '../../App';
import { useUserSettingsState } from '../../states/SettingsState'; import { useUserSettingsState } from '../../states/SettingsStates';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import { Boundary } from '../Boundary'; import { Boundary } from '../Boundary';
import { RenderInstance } from '../render/Instance'; import { RenderInstance } from '../render/Instance';

View File

@ -7,7 +7,7 @@ import { useLocalState } from '../../states/LocalState';
import { import {
useGlobalSettingsState, useGlobalSettingsState,
useUserSettingsState useUserSettingsState
} from '../../states/SettingsState'; } from '../../states/SettingsStates';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import { ModelInformationDict } from '@lib/enums/ModelInformation'; import { ModelInformationDict } from '@lib/enums/ModelInformation';

View File

@ -3,7 +3,7 @@ import { Badge, Center, type MantineSize } from '@mantine/core';
import type { ModelType } from '@lib/enums/ModelType'; import type { ModelType } from '@lib/enums/ModelType';
import { statusColorMap } from '../../defaults/backendMappings'; import { statusColorMap } from '../../defaults/backendMappings';
import { resolveItem } from '../../functions/conversion'; import { resolveItem } from '../../functions/conversion';
import { useGlobalStatusState } from '../../states/StatusState'; import { useGlobalStatusState } from '../../states/GlobalStatusState';
export interface StatusCodeInterface { export interface StatusCodeInterface {
key: number; key: number;

View File

@ -16,7 +16,7 @@ import {
createPluginSettingsState, createPluginSettingsState,
useGlobalSettingsState, useGlobalSettingsState,
useUserSettingsState useUserSettingsState
} from '../../states/SettingsState'; } from '../../states/SettingsStates';
import { SettingItem } from './SettingItem'; import { SettingItem } from './SettingItem';
/** /**

View File

@ -6,8 +6,8 @@ import { type JSX, useEffect, useRef, useState } from 'react';
import { useShallow } from 'zustand/react/shallow'; import { useShallow } from 'zustand/react/shallow';
import { api } from '../App'; import { api } from '../App';
import { useServerApiState } from '../states/ApiState';
import { useLocalState } from '../states/LocalState'; import { useLocalState } from '../states/LocalState';
import { useServerApiState } from '../states/ServerApiState';
import { fetchGlobalStates } from '../states/states'; import { fetchGlobalStates } from '../states/states';
export const defaultLocale = 'en'; export const defaultLocale = 'en';

View File

@ -3,7 +3,7 @@ import dayjs from 'dayjs';
import { import {
useGlobalSettingsState, useGlobalSettingsState,
useUserSettingsState useUserSettingsState
} from '../states/SettingsState'; } from '../states/SettingsStates';
interface FormatDecmimalOptionsInterface { interface FormatDecmimalOptionsInterface {
digits?: number; digits?: number;

View File

@ -29,7 +29,7 @@ import {
useBatchCodeGenerator, useBatchCodeGenerator,
useSerialNumberGenerator useSerialNumberGenerator
} from '../hooks/UseGenerator'; } from '../hooks/UseGenerator';
import { useGlobalSettingsState } from '../states/SettingsState'; import { useGlobalSettingsState } from '../states/SettingsStates';
import { PartColumn } from '../tables/ColumnRenderers'; import { PartColumn } from '../tables/ColumnRenderers';
/** /**

View File

@ -6,7 +6,7 @@ import type {
StatusCodeInterface, StatusCodeInterface,
StatusCodeListInterface StatusCodeListInterface
} from '../components/render/StatusRenderer'; } from '../components/render/StatusRenderer';
import { useGlobalStatusState } from '../states/StatusState'; import { useGlobalStatusState } from '../states/GlobalStatusState';
export function projectCodeFields(): ApiFormFieldSet { export function projectCodeFields(): ApiFormFieldSet {
return { return {

View File

@ -6,7 +6,7 @@ import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
import { apiUrl } from '@lib/functions/Api'; import { apiUrl } from '@lib/functions/Api';
import type { ApiFormFieldSet } from '@lib/types/Forms'; import type { ApiFormFieldSet } from '@lib/types/Forms';
import { useApi } from '../contexts/ApiContext'; import { useApi } from '../contexts/ApiContext';
import { useGlobalSettingsState } from '../states/SettingsState'; import { useGlobalSettingsState } from '../states/SettingsStates';
/** /**
* Construct a set of fields for creating / editing a Part instance * Construct a set of fields for creating / editing a Part instance

View File

@ -51,7 +51,7 @@ import {
useBatchCodeGenerator, useBatchCodeGenerator,
useSerialNumberGenerator useSerialNumberGenerator
} from '../hooks/UseGenerator'; } from '../hooks/UseGenerator';
import { useGlobalSettingsState } from '../states/SettingsState'; import { useGlobalSettingsState } from '../states/SettingsStates';
/* /*
* Construct a set of fields for creating / editing a PurchaseOrderLineItem instance * Construct a set of fields for creating / editing a PurchaseOrderLineItem instance
*/ */

View File

@ -21,7 +21,7 @@ import type {
import type { TableFieldRowProps } from '../components/forms/fields/TableField'; import type { TableFieldRowProps } from '../components/forms/fields/TableField';
import { Thumbnail } from '../components/images/Thumbnail'; import { Thumbnail } from '../components/images/Thumbnail';
import { useCreateApiFormModal } from '../hooks/UseForm'; import { useCreateApiFormModal } from '../hooks/UseForm';
import { useGlobalSettingsState } from '../states/SettingsState'; import { useGlobalSettingsState } from '../states/SettingsStates';
import { StatusFilterOptions } from '../tables/Filter'; import { StatusFilterOptions } from '../tables/Filter';
export function useReturnOrderFields({ export function useReturnOrderFields({

View File

@ -22,7 +22,7 @@ import type {
import type { TableFieldRowProps } from '../components/forms/fields/TableField'; import type { TableFieldRowProps } from '../components/forms/fields/TableField';
import { ProgressBar } from '../components/items/ProgressBar'; import { ProgressBar } from '../components/items/ProgressBar';
import { useCreateApiFormModal } from '../hooks/UseForm'; import { useCreateApiFormModal } from '../hooks/UseForm';
import { useGlobalSettingsState } from '../states/SettingsState'; import { useGlobalSettingsState } from '../states/SettingsStates';
import { PartColumn } from '../tables/ColumnRenderers'; import { PartColumn } from '../tables/ColumnRenderers';
export function useSalesOrderFields({ export function useSalesOrderFields({

View File

@ -56,7 +56,7 @@ import {
useBatchCodeGenerator, useBatchCodeGenerator,
useSerialNumberGenerator useSerialNumberGenerator
} from '../hooks/UseGenerator'; } from '../hooks/UseGenerator';
import { useGlobalSettingsState } from '../states/SettingsState'; import { useGlobalSettingsState } from '../states/SettingsStates';
import { StatusFilterOptions } from '../tables/Filter'; import { StatusFilterOptions } from '../tables/Filter';
/** /**

View File

@ -7,8 +7,8 @@ import axios from 'axios';
import type { AxiosRequestConfig } from 'axios'; import type { AxiosRequestConfig } from 'axios';
import type { Location, NavigateFunction } from 'react-router-dom'; import type { Location, NavigateFunction } from 'react-router-dom';
import { api, setApiDefaults } from '../App'; import { api, setApiDefaults } from '../App';
import { useServerApiState } from '../states/ApiState';
import { useLocalState } from '../states/LocalState'; import { useLocalState } from '../states/LocalState';
import { useServerApiState } from '../states/ServerApiState';
import { useUserState } from '../states/UserState'; import { useUserState } from '../states/UserState';
import { fetchGlobalStates } from '../states/states'; import { fetchGlobalStates } from '../states/states';
import { showLoginNotification } from './notifications'; import { showLoginNotification } from './notifications';
@ -354,6 +354,7 @@ export const checkLoginState = async (
redirect?: any, redirect?: any,
no_redirect?: boolean no_redirect?: boolean
) => { ) => {
const { setLoginChecked } = useUserState.getState();
setApiDefaults(); setApiDefaults();
if (redirect == '/') { if (redirect == '/') {
@ -364,6 +365,7 @@ export const checkLoginState = async (
// Callback function when login is successful // Callback function when login is successful
const loginSuccess = () => { const loginSuccess = () => {
setLoginChecked(true);
showLoginNotification({ showLoginNotification({
title: t`Logged In`, title: t`Logged In`,
message: t`Successfully logged in` message: t`Successfully logged in`
@ -388,8 +390,10 @@ export const checkLoginState = async (
if (isLoggedIn()) { if (isLoggedIn()) {
loginSuccess(); loginSuccess();
} else if (!no_redirect) { } else if (!no_redirect) {
setLoginChecked(true);
navigate('/login', { state: redirect }); navigate('/login', { state: redirect });
} }
setLoginChecked(true);
}; };
function handleSuccessFullAuth( function handleSuccessFullAuth(

View File

@ -4,11 +4,13 @@ import { type JSX, Suspense } from 'react';
import { colorSchema } from '../contexts/colorSchema'; import { colorSchema } from '../contexts/colorSchema';
import { theme } from '../theme'; import { theme } from '../theme';
function LoadingFallback() { function LoadingFallback({
fullHeight = false
}: { fullHeight: boolean }): JSX.Element {
return ( return (
<MantineProvider theme={theme} colorSchemeManager={colorSchema}> <MantineProvider theme={theme} colorSchemeManager={colorSchema}>
<Stack> <Stack h={fullHeight ? '100vh' : undefined}>
<Center> <Center h={fullHeight ? '100vh' : undefined}>
<Loader /> <Loader />
</Center> </Center>
</Stack> </Stack>
@ -16,12 +18,21 @@ function LoadingFallback() {
); );
} }
export const Loadable = export function Loadable(
(Component: any) => (props: JSX.IntrinsicAttributes) => ( Component: any,
<Suspense fallback={<LoadingFallback />}> noFallback = false,
fullHeight = false
): any {
return (props: JSX.IntrinsicAttributes) => (
<Suspense
fallback={
!noFallback ? <LoadingFallback fullHeight={fullHeight} /> : undefined
}
>
<Component {...props} /> <Component {...props} />
</Suspense> </Suspense>
); );
}
export function LoadingItem({ item }: Readonly<{ item: any }>): JSX.Element { export function LoadingItem({ item }: Readonly<{ item: any }>): JSX.Element {
const Itm = Loadable(item); const Itm = Loadable(item);

View File

@ -13,7 +13,7 @@ import {
} from '../components/plugins/PluginUIFeature'; } from '../components/plugins/PluginUIFeature';
import RemoteComponent from '../components/plugins/RemoteComponent'; import RemoteComponent from '../components/plugins/RemoteComponent';
import { identifierString } from '../functions/conversion'; import { identifierString } from '../functions/conversion';
import { useGlobalSettingsState } from '../states/SettingsState'; import { useGlobalSettingsState } from '../states/SettingsStates';
import { useUserState } from '../states/UserState'; import { useUserState } from '../states/UserState';
interface DashboardLibraryProps { interface DashboardLibraryProps {

View File

@ -1,6 +1,6 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useGlobalSettingsState } from '../states/SettingsState'; import { useGlobalSettingsState } from '../states/SettingsStates';
/** /**
* Simple hook for returning the "instance name" of the Server * Simple hook for returning the "instance name" of the Server

View File

@ -14,7 +14,7 @@ import {
type PluginUIFeature, type PluginUIFeature,
PluginUIFeatureType PluginUIFeatureType
} from '../components/plugins/PluginUIFeature'; } from '../components/plugins/PluginUIFeature';
import { useGlobalSettingsState } from '../states/SettingsState'; import { useGlobalSettingsState } from '../states/SettingsStates';
/** /**
* Type definition for a plugin panel which extends the standard PanelType * Type definition for a plugin panel which extends the standard PanelType

View File

@ -11,7 +11,7 @@ import type {
PluginUIFeatureAPIResponse, PluginUIFeatureAPIResponse,
PluginUIFuncWithoutInvenTreeContextType PluginUIFuncWithoutInvenTreeContextType
} from '../components/plugins/PluginUIFeatureTypes'; } from '../components/plugins/PluginUIFeatureTypes';
import { useGlobalSettingsState } from '../states/SettingsState'; import { useGlobalSettingsState } from '../states/SettingsStates';
export function usePluginUIFeature<UIFeatureT extends BaseUIFeature>({ export function usePluginUIFeature<UIFeatureT extends BaseUIFeature>({
enabled = true, enabled = true,

View File

@ -2,7 +2,7 @@ import { useMemo } from 'react';
import type { ModelType } from '@lib/enums/ModelType'; import type { ModelType } from '@lib/enums/ModelType';
import { getStatusCodes } from '../components/render/StatusRenderer'; import { getStatusCodes } from '../components/render/StatusRenderer';
import { useGlobalStatusState } from '../states/StatusState'; import { useGlobalStatusState } from '../states/GlobalStatusState';
/** /**
* Hook to access status codes, which are enumerated by the backend. * Hook to access status codes, which are enumerated by the backend.

View File

@ -14,7 +14,7 @@ import SplashScreen from '../../components/SplashScreen';
import { StylishText } from '../../components/items/StylishText'; import { StylishText } from '../../components/items/StylishText';
import { doLogout } from '../../functions/auth'; import { doLogout } from '../../functions/auth';
export default function Layout() { export default function LoginLayoutComponent() {
return ( return (
<SplashScreen> <SplashScreen>
<Center mih='100vh'> <Center mih='100vh'>

View File

@ -16,8 +16,8 @@ import {
doBasicLogin, doBasicLogin,
followRedirect followRedirect
} from '../../functions/auth'; } from '../../functions/auth';
import { useServerApiState } from '../../states/ApiState';
import { useLocalState } from '../../states/LocalState'; import { useLocalState } from '../../states/LocalState';
import { useServerApiState } from '../../states/ServerApiState';
import { Wrapper } from './Layout'; import { Wrapper } from './Layout';
export default function Login() { export default function Login() {

View File

@ -35,7 +35,7 @@ import { useShallow } from 'zustand/react/shallow';
import { api } from '../../../../App'; import { api } from '../../../../App';
import { StylishText } from '../../../../components/items/StylishText'; import { StylishText } from '../../../../components/items/StylishText';
import { ProviderLogin, authApi } from '../../../../functions/auth'; import { ProviderLogin, authApi } from '../../../../functions/auth';
import { useServerApiState } from '../../../../states/ApiState'; import { useServerApiState } from '../../../../states/ServerApiState';
import { ApiTokenTable } from '../../../../tables/settings/ApiTokenTable'; import { ApiTokenTable } from '../../../../tables/settings/ApiTokenTable';
import { QrRegistrationForm } from './QrRegistrationForm'; import { QrRegistrationForm } from './QrRegistrationForm';
import { useReauth } from './useConfirm'; import { useReauth } from './useConfirm';

View File

@ -8,7 +8,7 @@ import { useShallow } from 'zustand/react/shallow';
import { StylishText } from '../../../../components/items/StylishText'; import { StylishText } from '../../../../components/items/StylishText';
import { GlobalSettingList } from '../../../../components/settings/SettingList'; import { GlobalSettingList } from '../../../../components/settings/SettingList';
import { Loadable } from '../../../../functions/loading'; import { Loadable } from '../../../../functions/loading';
import { useServerApiState } from '../../../../states/ApiState'; import { useServerApiState } from '../../../../states/ServerApiState';
import { useUserState } from '../../../../states/UserState'; import { useUserState } from '../../../../states/UserState';
const PluginListTable = Loadable( const PluginListTable = Loadable(

View File

@ -25,7 +25,7 @@ import { SettingsHeader } from '../../../components/nav/SettingsHeader';
import type { PanelType } from '../../../components/panels/Panel'; import type { PanelType } from '../../../components/panels/Panel';
import { PanelGroup } from '../../../components/panels/PanelGroup'; import { PanelGroup } from '../../../components/panels/PanelGroup';
import { GlobalSettingList } from '../../../components/settings/SettingList'; import { GlobalSettingList } from '../../../components/settings/SettingList';
import { useServerApiState } from '../../../states/ApiState'; import { useServerApiState } from '../../../states/ServerApiState';
import { useUserState } from '../../../states/UserState'; import { useUserState } from '../../../states/UserState';
/** /**

View File

@ -51,7 +51,7 @@ import {
} from '../../hooks/UseForm'; } from '../../hooks/UseForm';
import { useInstance } from '../../hooks/UseInstance'; import { useInstance } from '../../hooks/UseInstance';
import useStatusCodes from '../../hooks/UseStatusCodes'; import useStatusCodes from '../../hooks/UseStatusCodes';
import { useGlobalSettingsState } from '../../states/SettingsState'; import { useGlobalSettingsState } from '../../states/SettingsStates';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import BuildAllocatedStockTable from '../../tables/build/BuildAllocatedStockTable'; import BuildAllocatedStockTable from '../../tables/build/BuildAllocatedStockTable';
import BuildLineTable from '../../tables/build/BuildLineTable'; import BuildLineTable from '../../tables/build/BuildLineTable';

View File

@ -13,7 +13,7 @@ import PermissionDenied from '../../components/errors/PermissionDenied';
import { PageDetail } from '../../components/nav/PageDetail'; import { PageDetail } from '../../components/nav/PageDetail';
import type { PanelType } from '../../components/panels/Panel'; import type { PanelType } from '../../components/panels/Panel';
import { PanelGroup } from '../../components/panels/PanelGroup'; import { PanelGroup } from '../../components/panels/PanelGroup';
import { useGlobalSettingsState } from '../../states/SettingsState'; import { useGlobalSettingsState } from '../../states/SettingsStates';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import { PartCategoryFilter } from '../../tables/Filter'; import { PartCategoryFilter } from '../../tables/Filter';
import { BuildOrderTable } from '../../tables/build/BuildOrderTable'; import { BuildOrderTable } from '../../tables/build/BuildOrderTable';

View File

@ -17,7 +17,7 @@ import type { PanelType } from '../../components/panels/Panel';
import { PanelGroup } from '../../components/panels/PanelGroup'; import { PanelGroup } from '../../components/panels/PanelGroup';
import {} from '../../hooks/UseForm'; import {} from '../../hooks/UseForm';
import { useInstance } from '../../hooks/UseInstance'; import { useInstance } from '../../hooks/UseInstance';
import { useGlobalSettingsState } from '../../states/SettingsState'; import { useGlobalSettingsState } from '../../states/SettingsStates';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
/** /**

View File

@ -86,7 +86,7 @@ import { useInstance } from '../../hooks/UseInstance';
import { import {
useGlobalSettingsState, useGlobalSettingsState,
useUserSettingsState useUserSettingsState
} from '../../states/SettingsState'; } from '../../states/SettingsStates';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import { BomTable } from '../../tables/bom/BomTable'; import { BomTable } from '../../tables/bom/BomTable';
import { UsedInTable } from '../../tables/bom/UsedInTable'; import { UsedInTable } from '../../tables/bom/UsedInTable';

View File

@ -5,7 +5,7 @@ import { useMemo, useState } from 'react';
import { ApiEndpoints } from '@lib/enums/ApiEndpoints'; import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
import { UserRoles } from '@lib/enums/Roles'; import { UserRoles } from '@lib/enums/Roles';
import { useInstance } from '../../hooks/UseInstance'; import { useInstance } from '../../hooks/UseInstance';
import { useGlobalSettingsState } from '../../states/SettingsState'; import { useGlobalSettingsState } from '../../states/SettingsStates';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import BomPricingPanel from './pricing/BomPricingPanel'; import BomPricingPanel from './pricing/BomPricingPanel';
import PriceBreakPanel from './pricing/PriceBreakPanel'; import PriceBreakPanel from './pricing/PriceBreakPanel';

View File

@ -36,7 +36,7 @@ import {
import { formatCurrency, formatDate } from '../../../defaults/formatters'; import { formatCurrency, formatDate } from '../../../defaults/formatters';
import { InvenTreeIcon } from '../../../functions/icons'; import { InvenTreeIcon } from '../../../functions/icons';
import { useEditApiFormModal } from '../../../hooks/UseForm'; import { useEditApiFormModal } from '../../../hooks/UseForm';
import { useGlobalSettingsState } from '../../../states/SettingsState'; import { useGlobalSettingsState } from '../../../states/SettingsStates';
import { panelOptions } from '../PartPricingPanel'; import { panelOptions } from '../PartPricingPanel';
interface PricingOverviewEntry { interface PricingOverviewEntry {

View File

@ -41,7 +41,7 @@ import {
} from '../../hooks/UseForm'; } from '../../hooks/UseForm';
import { useInstance } from '../../hooks/UseInstance'; import { useInstance } from '../../hooks/UseInstance';
import useStatusCodes from '../../hooks/UseStatusCodes'; import useStatusCodes from '../../hooks/UseStatusCodes';
import { useGlobalSettingsState } from '../../states/SettingsState'; import { useGlobalSettingsState } from '../../states/SettingsStates';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import ExtraLineItemTable from '../../tables/general/ExtraLineItemTable'; import ExtraLineItemTable from '../../tables/general/ExtraLineItemTable';
import { PurchaseOrderLineItemTable } from '../../tables/purchasing/PurchaseOrderLineItemTable'; import { PurchaseOrderLineItemTable } from '../../tables/purchasing/PurchaseOrderLineItemTable';

View File

@ -41,7 +41,7 @@ import {
} from '../../hooks/UseForm'; } from '../../hooks/UseForm';
import { useInstance } from '../../hooks/UseInstance'; import { useInstance } from '../../hooks/UseInstance';
import useStatusCodes from '../../hooks/UseStatusCodes'; import useStatusCodes from '../../hooks/UseStatusCodes';
import { useGlobalSettingsState } from '../../states/SettingsState'; import { useGlobalSettingsState } from '../../states/SettingsStates';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import ExtraLineItemTable from '../../tables/general/ExtraLineItemTable'; import ExtraLineItemTable from '../../tables/general/ExtraLineItemTable';
import ReturnOrderLineItemTable from '../../tables/sales/ReturnOrderLineItemTable'; import ReturnOrderLineItemTable from '../../tables/sales/ReturnOrderLineItemTable';

View File

@ -47,7 +47,7 @@ import {
} from '../../hooks/UseForm'; } from '../../hooks/UseForm';
import { useInstance } from '../../hooks/UseInstance'; import { useInstance } from '../../hooks/UseInstance';
import useStatusCodes from '../../hooks/UseStatusCodes'; import useStatusCodes from '../../hooks/UseStatusCodes';
import { useGlobalSettingsState } from '../../states/SettingsState'; import { useGlobalSettingsState } from '../../states/SettingsStates';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import { BuildOrderTable } from '../../tables/build/BuildOrderTable'; import { BuildOrderTable } from '../../tables/build/BuildOrderTable';
import ExtraLineItemTable from '../../tables/general/ExtraLineItemTable'; import ExtraLineItemTable from '../../tables/general/ExtraLineItemTable';

View File

@ -84,7 +84,7 @@ import {
useEditApiFormModal useEditApiFormModal
} from '../../hooks/UseForm'; } from '../../hooks/UseForm';
import { useInstance } from '../../hooks/UseInstance'; import { useInstance } from '../../hooks/UseInstance';
import { useGlobalSettingsState } from '../../states/SettingsState'; import { useGlobalSettingsState } from '../../states/SettingsStates';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import BuildAllocatedStockTable from '../../tables/build/BuildAllocatedStockTable'; import BuildAllocatedStockTable from '../../tables/build/BuildAllocatedStockTable';
import SalesOrderAllocationTable from '../../tables/sales/SalesOrderAllocationTable'; import SalesOrderAllocationTable from '../../tables/sales/SalesOrderAllocationTable';

View File

@ -5,10 +5,14 @@ import { Loadable } from './functions/loading';
// Lazy loaded pages // Lazy loaded pages
export const LayoutComponent = Loadable( export const LayoutComponent = Loadable(
lazy(() => import('./components/nav/Layout')) lazy(() => import('./components/nav/Layout')),
true,
true
); );
export const LoginLayoutComponent = Loadable( export const LoginLayoutComponent = Loadable(
lazy(() => import('./pages/Auth/Layout')) lazy(() => import('./pages/Auth/Layout')),
true,
true
); );
export const Home = Loadable(lazy(() => import('./pages/Index/Home'))); export const Home = Loadable(lazy(() => import('./pages/Index/Home')));
@ -119,7 +123,11 @@ export const NotFound = Loadable(
// Auth // Auth
export const Login = Loadable(lazy(() => import('./pages/Auth/Login'))); export const Login = Loadable(lazy(() => import('./pages/Auth/Login')));
export const LoggedIn = Loadable(lazy(() => import('./pages/Auth/LoggedIn'))); export const LoggedIn = Loadable(
lazy(() => import('./pages/Auth/LoggedIn')),
true,
true
);
export const Logout = Loadable(lazy(() => import('./pages/Auth/Logout'))); export const Logout = Loadable(lazy(() => import('./pages/Auth/Logout')));
export const Register = Loadable(lazy(() => import('./pages/Auth/Register'))); export const Register = Loadable(lazy(() => import('./pages/Auth/Register')));
export const Mfa = Loadable(lazy(() => import('./pages/Auth/MFA'))); export const Mfa = Loadable(lazy(() => import('./pages/Auth/MFA')));
@ -132,7 +140,9 @@ export const ResetPassword = Loadable(
lazy(() => import('./pages/Auth/ResetPassword')) lazy(() => import('./pages/Auth/ResetPassword'))
); );
export const VerifyEmail = Loadable( export const VerifyEmail = Loadable(
lazy(() => import('./pages/Auth/VerifyEmail')) lazy(() => import('./pages/Auth/VerifyEmail')),
true,
true
); );
// Routes // Routes

View File

@ -198,5 +198,10 @@ export const useUserState = create<UserStateProps>((set, get) => ({
}, },
hasViewPermission: (model: ModelType) => { hasViewPermission: (model: ModelType) => {
return get().checkUserPermission(model, UserPermissions.view); return get().checkUserPermission(model, UserPermissions.view);
},
// login state
login_checked: false,
setLoginChecked: (value) => {
set({ login_checked: value });
} }
})); }));

View File

@ -1,10 +1,10 @@
import type { PluginProps } from '@lib/types/Plugins'; import type { PluginProps } from '@lib/types/Plugins';
import type { NavigateFunction } from 'react-router-dom'; import type { NavigateFunction } from 'react-router-dom';
import { setApiDefaults } from '../App'; import { setApiDefaults } from '../App';
import { useServerApiState } from './ApiState'; import { useGlobalStatusState } from './GlobalStatusState';
import { useIconState } from './IconState'; import { useIconState } from './IconState';
import { useGlobalSettingsState, useUserSettingsState } from './SettingsState'; import { useServerApiState } from './ServerApiState';
import { useGlobalStatusState } from './StatusState'; import { useGlobalSettingsState, useUserSettingsState } from './SettingsStates';
import { useUserState } from './UserState'; import { useUserState } from './UserState';
// Type interface fully defining the current server // Type interface fully defining the current server

View File

@ -14,7 +14,7 @@ import { TableStatusRenderer } from '../components/render/StatusRenderer';
import { RenderOwner, RenderUser } from '../components/render/User'; import { RenderOwner, RenderUser } from '../components/render/User';
import { formatCurrency, formatDate } from '../defaults/formatters'; import { formatCurrency, formatDate } from '../defaults/formatters';
import { resolveItem } from '../functions/conversion'; import { resolveItem } from '../functions/conversion';
import { useGlobalSettingsState } from '../states/SettingsState'; import { useGlobalSettingsState } from '../states/SettingsStates';
import type { TableColumn, TableColumnProps } from './Column'; import type { TableColumn, TableColumnProps } from './Column';
import { ProjectCodeHoverCard } from './TableHoverCard'; import { ProjectCodeHoverCard } from './TableHoverCard';

View File

@ -8,8 +8,11 @@ import type {
StatusCodeInterface, StatusCodeInterface,
StatusCodeListInterface StatusCodeListInterface
} from '../components/render/StatusRenderer'; } from '../components/render/StatusRenderer';
import { useGlobalSettingsState } from '../states/SettingsState'; import {
import { type StatusLookup, useGlobalStatusState } from '../states/StatusState'; type StatusLookup,
useGlobalStatusState
} from '../states/GlobalStatusState';
import { useGlobalSettingsState } from '../states/SettingsStates';
/** /**
* Return list of available filter options for a given filter * Return list of available filter options for a given filter

View File

@ -12,7 +12,7 @@ import { RenderUser } from '../../components/render/User';
import { useBuildOrderFields } from '../../forms/BuildForms'; import { useBuildOrderFields } from '../../forms/BuildForms';
import { useCreateApiFormModal } from '../../hooks/UseForm'; import { useCreateApiFormModal } from '../../hooks/UseForm';
import { useTable } from '../../hooks/UseTable'; import { useTable } from '../../hooks/UseTable';
import { useGlobalSettingsState } from '../../states/SettingsState'; import { useGlobalSettingsState } from '../../states/SettingsStates';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import { import {
BooleanColumn, BooleanColumn,

View File

@ -26,7 +26,7 @@ import {
useEditApiFormModal useEditApiFormModal
} from '../../hooks/UseForm'; } from '../../hooks/UseForm';
import { useTable } from '../../hooks/UseTable'; import { useTable } from '../../hooks/UseTable';
import { useServerApiState } from '../../states/ApiState'; import { useServerApiState } from '../../states/ServerApiState';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import type { TableColumn } from '../Column'; import type { TableColumn } from '../Column';
import { BooleanColumn } from '../ColumnRenderers'; import { BooleanColumn } from '../ColumnRenderers';

View File

@ -25,7 +25,7 @@ import { RenderUser } from '../../components/render/User';
import { shortenString } from '../../functions/tables'; import { shortenString } from '../../functions/tables';
import { useDeleteApiFormModal } from '../../hooks/UseForm'; import { useDeleteApiFormModal } from '../../hooks/UseForm';
import { useTable } from '../../hooks/UseTable'; import { useTable } from '../../hooks/UseTable';
import { useGlobalSettingsState } from '../../states/SettingsState'; import { useGlobalSettingsState } from '../../states/SettingsStates';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import type { TableColumn } from '../Column'; import type { TableColumn } from '../Column';
import { UserFilter } from '../Filter'; import { UserFilter } from '../Filter';

View File

@ -19,7 +19,7 @@ import {
useEditApiFormModal useEditApiFormModal
} from '../../hooks/UseForm'; } from '../../hooks/UseForm';
import { useTable } from '../../hooks/UseTable'; import { useTable } from '../../hooks/UseTable';
import { useGlobalStatusState } from '../../states/StatusState'; import { useGlobalStatusState } from '../../states/GlobalStatusState';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import type { TableColumn } from '../Column'; import type { TableColumn } from '../Column';
import { InvenTreeTable } from '../InvenTreeTable'; import { InvenTreeTable } from '../InvenTreeTable';

View File

@ -26,7 +26,7 @@ import {
import { InvenTreeIcon } from '../../functions/icons'; import { InvenTreeIcon } from '../../functions/icons';
import { useCreateApiFormModal } from '../../hooks/UseForm'; import { useCreateApiFormModal } from '../../hooks/UseForm';
import { useTable } from '../../hooks/UseTable'; import { useTable } from '../../hooks/UseTable';
import { useGlobalSettingsState } from '../../states/SettingsState'; import { useGlobalSettingsState } from '../../states/SettingsStates';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import type { TableColumn } from '../Column'; import type { TableColumn } from '../Column';
import { import {

View File

@ -28,7 +28,7 @@ import {
useEditApiFormModal useEditApiFormModal
} from '../../hooks/UseForm'; } from '../../hooks/UseForm';
import { useTable } from '../../hooks/UseTable'; import { useTable } from '../../hooks/UseTable';
import { useGlobalSettingsState } from '../../states/SettingsState'; import { useGlobalSettingsState } from '../../states/SettingsStates';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import type { TableColumn } from '../Column'; import type { TableColumn } from '../Column';
import { DateColumn, DescriptionColumn, NoteColumn } from '../ColumnRenderers'; import { DateColumn, DescriptionColumn, NoteColumn } from '../ColumnRenderers';

View File

@ -13,8 +13,16 @@ function checkMobile() {
return false; return false;
} }
const MobileAppView = Loadable(lazy(() => import('./MobileAppView'))); const MobileAppView = Loadable(
const DesktopAppView = Loadable(lazy(() => import('./DesktopAppView'))); lazy(() => import('./MobileAppView')),
true,
true
);
const DesktopAppView = Loadable(
lazy(() => import('./DesktopAppView')),
true,
true
);
// Main App // Main App
export default function MainView() { export default function MainView() {