mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 13:15:43 +00:00 
			
		
		
		
	chore: bump zustand (#9577)
* bump zustand to v5 * add missing shallow * fix missing shallow --------- Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
This commit is contained in:
		| @@ -101,7 +101,7 @@ | ||||
|         "react-window": "1.8.10", | ||||
|         "recharts": "^2.15.0", | ||||
|         "styled-components": "^6.1.14", | ||||
|         "zustand": "^4.5.5" | ||||
|         "zustand": "^5.0.3" | ||||
|     }, | ||||
|     "devDependencies": { | ||||
|         "@babel/core": "^7.26.10", | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| import { BackgroundImage } from '@mantine/core'; | ||||
| import { useEffect } from 'react'; | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { generateUrl } from '../functions/urls'; | ||||
| import { useServerApiState } from '../states/ApiState'; | ||||
|  | ||||
| @@ -11,10 +12,9 @@ export default function SplashScreen({ | ||||
| }: Readonly<{ | ||||
|   children: React.ReactNode; | ||||
| }>) { | ||||
|   const [server, fetchServerApiState] = useServerApiState((state) => [ | ||||
|     state.server, | ||||
|     state.fetchServerApiState | ||||
|   ]); | ||||
|   const [server, fetchServerApiState] = useServerApiState( | ||||
|     useShallow((state) => [state.server, state.fetchServerApiState]) | ||||
|   ); | ||||
|  | ||||
|   // Fetch server data on mount if no server data is present | ||||
|   useEffect(() => { | ||||
|   | ||||
| @@ -26,6 +26,7 @@ import { | ||||
|   IconFilter | ||||
| } from '@tabler/icons-react'; | ||||
| import { useCallback, useState } from 'react'; | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import type { CalendarState } from '../../hooks/UseCalendar'; | ||||
| import { useLocalState } from '../../states/LocalState'; | ||||
| import { FilterSelectDrawer } from '../../tables/FilterSelectDrawer'; | ||||
| @@ -51,12 +52,12 @@ export default function Calendar({ | ||||
|   filters, | ||||
|   state, | ||||
|   ...calendarProps | ||||
| }: InvenTreeCalendarProps) { | ||||
| }: Readonly<InvenTreeCalendarProps>) { | ||||
|   const [monthSelectOpened, setMonthSelectOpened] = useState<boolean>(false); | ||||
|  | ||||
|   const [filtersVisible, setFiltersVisible] = useState<boolean>(false); | ||||
|  | ||||
|   const [locale] = useLocalState((s) => [s.language]); | ||||
|   const [locale] = useLocalState(useShallow((s) => [s.language])); | ||||
|  | ||||
|   const selectMonth = useCallback( | ||||
|     (date: DateValue) => { | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import { IconInfoCircle } from '@tabler/icons-react'; | ||||
| import { useCallback, useEffect, useMemo, useState } from 'react'; | ||||
| import { type Layout, Responsive, WidthProvider } from 'react-grid-layout'; | ||||
|  | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { useDashboardItems } from '../../hooks/UseDashboardItems'; | ||||
| import { useLocalState } from '../../states/LocalState'; | ||||
| import DashboardMenu from './DashboardMenu'; | ||||
| @@ -21,12 +22,14 @@ export default function DashboardLayout() { | ||||
|  | ||||
|   // local/remote storage values for widget / layout | ||||
|   const [remoteWidgets, setRemoteWidgets, remoteLayouts, setRemoteLayouts] = | ||||
|     useLocalState((state) => [ | ||||
|     useLocalState( | ||||
|       useShallow((state) => [ | ||||
|         state.widgets, | ||||
|         state.setWidgets, | ||||
|         state.layouts, | ||||
|         state.setLayouts | ||||
|     ]); | ||||
|       ]) | ||||
|     ); | ||||
|  | ||||
|   const [editing, setEditing] = useDisclosure(false); | ||||
|   const [removing, setRemoving] = useDisclosure(false); | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| import { ActionIcon, Center, Group, Text, Tooltip } from '@mantine/core'; | ||||
| import { IconServer } from '@tabler/icons-react'; | ||||
|  | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { useServerApiState } from '../../states/ApiState'; | ||||
| import { ColorToggle } from '../items/ColorToggle'; | ||||
| import { LanguageToggle } from '../items/LanguageToggle'; | ||||
| @@ -12,7 +13,7 @@ export function AuthFormOptions({ | ||||
|   hostname: string; | ||||
|   toggleHostEdit: () => void; | ||||
| }>) { | ||||
|   const [server] = useServerApiState((state) => [state.server]); | ||||
|   const [server] = useServerApiState(useShallow((state) => [state.server])); | ||||
|  | ||||
|   return ( | ||||
|     <Center mx={'md'}> | ||||
|   | ||||
| @@ -18,6 +18,7 @@ import { useLocation, useNavigate } from 'react-router-dom'; | ||||
| import { ApiEndpoints } from '@lib/enums/ApiEndpoints'; | ||||
| import { apiUrl } from '@lib/functions/Api'; | ||||
| import { showNotification } from '@mantine/notifications'; | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { api } from '../../App'; | ||||
| import { | ||||
|   doBasicLogin, | ||||
| @@ -37,11 +38,13 @@ export function AuthenticationForm() { | ||||
|   const simpleForm = useForm({ initialValues: { email: '' } }); | ||||
|   const [classicLoginMode, setMode] = useDisclosure(true); | ||||
|   const [auth_config, sso_enabled, password_forgotten_enabled] = | ||||
|     useServerApiState((state) => [ | ||||
|     useServerApiState( | ||||
|       useShallow((state) => [ | ||||
|         state.auth_config, | ||||
|         state.sso_enabled, | ||||
|         state.password_forgotten_enabled | ||||
|     ]); | ||||
|       ]) | ||||
|     ); | ||||
|   const navigate = useNavigate(); | ||||
|   const location = useLocation(); | ||||
|   const { isLoggedIn } = useUserState(); | ||||
| @@ -207,11 +210,13 @@ export function RegistrationForm() { | ||||
|   }); | ||||
|   const navigate = useNavigate(); | ||||
|   const [auth_config, registration_enabled, sso_registration] = | ||||
|     useServerApiState((state) => [ | ||||
|     useServerApiState( | ||||
|       useShallow((state) => [ | ||||
|         state.auth_config, | ||||
|         state.registration_enabled, | ||||
|         state.sso_registration_enabled | ||||
|     ]); | ||||
|       ]) | ||||
|     ); | ||||
|   const [isRegistering, setIsRegistering] = useState<boolean>(false); | ||||
|  | ||||
|   async function handleRegistration() { | ||||
|   | ||||
| @@ -21,6 +21,7 @@ import { | ||||
| } from '@tabler/icons-react'; | ||||
|  | ||||
| import type { HostList } from '@lib/types/Server'; | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { Wrapper } from '../../pages/Auth/Layout'; | ||||
| import { useServerApiState } from '../../states/ApiState'; | ||||
| import { useLocalState } from '../../states/LocalState'; | ||||
| @@ -37,11 +38,9 @@ export function InstanceOptions({ | ||||
|   setHostEdit: () => void; | ||||
| }>) { | ||||
|   const [hostListEdit, setHostListEdit] = useToggle([false, true] as const); | ||||
|   const [setHost, setHostList, hostList] = useLocalState((state) => [ | ||||
|     state.setHost, | ||||
|     state.setHostList, | ||||
|     state.hostList | ||||
|   ]); | ||||
|   const [setHost, setHostList, hostList] = useLocalState( | ||||
|     useShallow((state) => [state.setHost, state.setHostList, state.hostList]) | ||||
|   ); | ||||
|   const hostListData = Object.keys(hostList).map((key) => ({ | ||||
|     value: key, | ||||
|     label: hostList[key]?.name | ||||
| @@ -111,7 +110,7 @@ function ServerInfo({ | ||||
|   hostList: HostList; | ||||
|   hostKey: string; | ||||
| }>) { | ||||
|   const [server] = useServerApiState((state) => [state.server]); | ||||
|   const [server] = useServerApiState(useShallow((state) => [state.server])); | ||||
|  | ||||
|   const items: any[] = [ | ||||
|     { | ||||
|   | ||||
| @@ -22,6 +22,7 @@ import type { FieldValues, UseControllerReturn } from 'react-hook-form'; | ||||
| import { FixedSizeGrid as Grid } from 'react-window'; | ||||
|  | ||||
| import type { ApiFormFieldType } from '@lib/types/Forms'; | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { useIconState } from '../../../states/IconState'; | ||||
| import { ApiIcon } from '../../items/ApiIcon'; | ||||
|  | ||||
| @@ -119,7 +120,7 @@ function ComboboxDropdown({ | ||||
|   onChange: (newVal: string | null) => void; | ||||
|   open: boolean; | ||||
| }>) { | ||||
|   const iconPacks = useIconState((s) => s.packages); | ||||
|   const iconPacks = useIconState(useShallow((s) => s.packages)); | ||||
|   const icons = useMemo<RenderIconType[]>(() => { | ||||
|     return iconPacks.flatMap((pack) => | ||||
|       Object.entries(pack.icons).flatMap(([name, icon]) => | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { useIconState } from '../../states/IconState'; | ||||
| import * as classes from './ApiIcon.css'; | ||||
|  | ||||
| @@ -9,7 +10,9 @@ type ApiIconProps = { | ||||
| export const ApiIcon = ({ name: _name, size = 22 }: ApiIconProps) => { | ||||
|   const [iconPackage, name, variant] = _name.split(':'); | ||||
|   const icon = useIconState( | ||||
|     useShallow( | ||||
|       (s) => s.packagesMap[iconPackage]?.icons[name]?.variants[variant] | ||||
|     ) | ||||
|   ); | ||||
|  | ||||
|   const unicode = icon ? String.fromCodePoint(Number.parseInt(icon, 16)) : ''; | ||||
|   | ||||
| @@ -1,15 +1,15 @@ | ||||
| import { Select } from '@mantine/core'; | ||||
| import { useEffect, useState } from 'react'; | ||||
|  | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { getSupportedLanguages } from '../../contexts/LanguageContext'; | ||||
| import { useLocalState } from '../../states/LocalState'; | ||||
|  | ||||
| export function LanguageSelect({ width = 80 }: Readonly<{ width?: number }>) { | ||||
|   const [value, setValue] = useState<string | null>(null); | ||||
|   const [locale, setLanguage] = useLocalState((state) => [ | ||||
|     state.language, | ||||
|     state.setLanguage | ||||
|   ]); | ||||
|   const [locale, setLanguage] = useLocalState( | ||||
|     useShallow((state) => [state.language, state.setLanguage]) | ||||
|   ); | ||||
|   const [langOptions, setLangOptions] = useState<any[]>([]); | ||||
|  | ||||
|   // change global language on change | ||||
|   | ||||
| @@ -1,9 +1,10 @@ | ||||
| import { Trans } from '@lingui/react/macro'; | ||||
|  | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { useUserState } from '../../states/UserState'; | ||||
|  | ||||
| export const OnlyStaff = ({ children }: { children: any }) => { | ||||
|   const [user] = useUserState((state) => [state.user]); | ||||
|   const [user] = useUserState(useShallow((state) => [state.user])); | ||||
|  | ||||
|   if (user?.is_staff) return children; | ||||
|   return <Trans>This information is only available for staff users</Trans>; | ||||
|   | ||||
| @@ -16,6 +16,7 @@ import { useQuery } from '@tanstack/react-query'; | ||||
|  | ||||
| import { ApiEndpoints } from '@lib/enums/ApiEndpoints'; | ||||
| import { apiUrl } from '@lib/functions/Api'; | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { api } from '../../App'; | ||||
| import { generateUrl } from '../../functions/urls'; | ||||
| import { useServerApiState } from '../../states/ApiState'; | ||||
| @@ -32,14 +33,14 @@ type AboutLookupRef = { | ||||
|  | ||||
| export function AboutInvenTreeModal({ | ||||
|   context, | ||||
|   id | ||||
|   id, | ||||
|   innerProps | ||||
| }: Readonly< | ||||
|   ContextModalProps<{ | ||||
|     modalBody: string; | ||||
|   }> | ||||
| >) { | ||||
|   const [user] = useUserState((state) => [state.user]); | ||||
|   const [server] = useServerApiState((state) => [state.server]); | ||||
|   const [user] = useUserState(useShallow((state) => [state.user])); | ||||
|  | ||||
|   if (!user?.is_staff) | ||||
|     return ( | ||||
| @@ -47,7 +48,18 @@ export function AboutInvenTreeModal({ | ||||
|         <Trans>This information is only available for staff users</Trans> | ||||
|       </Text> | ||||
|     ); | ||||
|   return <AboutContent context={context} id={id} innerProps={innerProps} />; | ||||
| } | ||||
|  | ||||
| const AboutContent = ({ | ||||
|   context, | ||||
|   id | ||||
| }: Readonly< | ||||
|   ContextModalProps<{ | ||||
|     modalBody: string; | ||||
|   }> | ||||
| >) => { | ||||
|   const [server] = useServerApiState(useShallow((state) => [state.server])); | ||||
|   const { isLoading, data } = useQuery({ | ||||
|     queryKey: ['version'], | ||||
|     queryFn: () => api.get(apiUrl(ApiEndpoints.version)).then((res) => res.data) | ||||
| @@ -185,7 +197,7 @@ export function AboutInvenTreeModal({ | ||||
|       </Group> | ||||
|     </Stack> | ||||
|   ); | ||||
| } | ||||
| }; | ||||
|  | ||||
| function renderVersionBadge(data: any) { | ||||
|   const badgeType = () => { | ||||
|   | ||||
| @@ -2,6 +2,7 @@ import { Trans } from '@lingui/react/macro'; | ||||
| import { Badge, Button, Divider, Group, Stack, Table } from '@mantine/core'; | ||||
| import type { ContextModalProps } from '@mantine/modals'; | ||||
|  | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { useServerApiState } from '../../states/ApiState'; | ||||
| import { OnlyStaff } from '../items/OnlyStaff'; | ||||
| import { StylishText } from '../items/StylishText'; | ||||
| @@ -10,7 +11,7 @@ export function ServerInfoModal({ | ||||
|   context, | ||||
|   id | ||||
| }: ContextModalProps<{ modalBody: string }>) { | ||||
|   const [server] = useServerApiState((state) => [state.server]); | ||||
|   const [server] = useServerApiState(useShallow((state) => [state.server])); | ||||
|  | ||||
|   return ( | ||||
|     <Stack> | ||||
|   | ||||
| @@ -3,6 +3,7 @@ import { IconExclamationCircle } from '@tabler/icons-react'; | ||||
| import { useMemo, useState } from 'react'; | ||||
|  | ||||
| import { t } from '@lingui/core/macro'; | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { docLinks } from '../../defaults/links'; | ||||
| import { useServerApiState } from '../../states/ApiState'; | ||||
| import { useGlobalSettingsState } from '../../states/SettingsState'; | ||||
| @@ -26,7 +27,7 @@ interface AlertInfo { | ||||
|  */ | ||||
| export function Alerts() { | ||||
|   const user = useUserState(); | ||||
|   const [server] = useServerApiState((state) => [state.server]); | ||||
|   const [server] = useServerApiState(useShallow((state) => [state.server])); | ||||
|   const globalSettings = useGlobalSettingsState(); | ||||
|  | ||||
|   const [dismissed, setDismissed] = useState<string[]>([]); | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import { Link, Route, Routes, useNavigate, useParams } from 'react-router-dom'; | ||||
| import type { To } from 'react-router-dom'; | ||||
|  | ||||
| import type { UiSizeType } from '@lib/types/Core'; | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { useLocalState } from '../../states/LocalState'; | ||||
| import { StylishText } from '../items/StylishText'; | ||||
| import * as classes from './DetailDrawer.css'; | ||||
| @@ -37,10 +38,9 @@ function DetailDrawerComponent({ | ||||
|   const content = renderContent(id); | ||||
|   const opened = useMemo(() => !!id && !!content, [id, content]); | ||||
|  | ||||
|   const [detailDrawerStack, addDetailDrawer] = useLocalState((state) => [ | ||||
|     state.detailDrawerStack, | ||||
|     state.addDetailDrawer | ||||
|   ]); | ||||
|   const [detailDrawerStack, addDetailDrawer] = useLocalState( | ||||
|     useShallow((state) => [state.detailDrawerStack, state.addDetailDrawer]) | ||||
|   ); | ||||
|  | ||||
|   return ( | ||||
|     <Drawer | ||||
| @@ -91,7 +91,9 @@ export function DetailDrawerLink({ | ||||
|   to, | ||||
|   text | ||||
| }: Readonly<{ to: To; text: string }>) { | ||||
|   const addDetailDrawer = useLocalState((state) => state.addDetailDrawer); | ||||
|   const addDetailDrawer = useLocalState( | ||||
|     useShallow((state) => state.addDetailDrawer) | ||||
|   ); | ||||
|  | ||||
|   const onNavigate = useCallback(() => { | ||||
|     addDetailDrawer(1); | ||||
|   | ||||
| @@ -1,7 +1,3 @@ | ||||
| import { ApiEndpoints } from '@lib/enums/ApiEndpoints'; | ||||
| import { apiUrl } from '@lib/functions/Api'; | ||||
| import { navigateToLink } from '@lib/functions/Navigation'; | ||||
| import { t } from '@lingui/core/macro'; | ||||
| import { | ||||
|   ActionIcon, | ||||
|   Container, | ||||
| @@ -16,6 +12,12 @@ import { IconBell, IconSearch } from '@tabler/icons-react'; | ||||
| import { useQuery } from '@tanstack/react-query'; | ||||
| import { type ReactNode, useEffect, useMemo, useState } from 'react'; | ||||
| import { useMatch, useNavigate } from 'react-router-dom'; | ||||
|  | ||||
| import { ApiEndpoints } from '@lib/enums/ApiEndpoints'; | ||||
| import { apiUrl } from '@lib/functions/Api'; | ||||
| import { navigateToLink } from '@lib/functions/Navigation'; | ||||
| import { t } from '@lingui/core/macro'; | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { api } from '../../App'; | ||||
| import type { NavigationUIFeature } from '../../components/plugins/PluginUIFeatureTypes'; | ||||
| import { getNavTabs } from '../../defaults/links'; | ||||
| @@ -38,11 +40,10 @@ import { NotificationDrawer } from './NotificationDrawer'; | ||||
| import { SearchDrawer } from './SearchDrawer'; | ||||
|  | ||||
| export function Header() { | ||||
|   const [setNavigationOpen, navigationOpen] = useLocalState((state) => [ | ||||
|     state.setNavigationOpen, | ||||
|     state.navigationOpen | ||||
|   ]); | ||||
|   const [server] = useServerApiState((state) => [state.server]); | ||||
|   const [setNavigationOpen, navigationOpen] = useLocalState( | ||||
|     useShallow((state) => [state.setNavigationOpen, state.navigationOpen]) | ||||
|   ); | ||||
|   const [server] = useServerApiState(useShallow((state) => [state.server])); | ||||
|   const [navDrawerOpened, { open: openNavDrawer, close: closeNavDrawer }] = | ||||
|     useDisclosure(navigationOpen); | ||||
|   const [ | ||||
|   | ||||
| @@ -18,6 +18,7 @@ import { | ||||
| } from '@tabler/icons-react'; | ||||
| import { Link, useNavigate } from 'react-router-dom'; | ||||
|  | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { doLogout } from '../../functions/auth'; | ||||
| import * as classes from '../../main.css'; | ||||
| import { useUserState } from '../../states/UserState'; | ||||
| @@ -25,10 +26,9 @@ import { vars } from '../../theme'; | ||||
|  | ||||
| export function MainMenu() { | ||||
|   const navigate = useNavigate(); | ||||
|   const [user, username] = useUserState((state) => [ | ||||
|     state.user, | ||||
|     state.username | ||||
|   ]); | ||||
|   const [user, username] = useUserState( | ||||
|     useShallow((state) => [state.user, state.username]) | ||||
|   ); | ||||
|   const { colorScheme, toggleColorScheme } = useMantineColorScheme(); | ||||
|  | ||||
|   return ( | ||||
|   | ||||
| @@ -31,6 +31,7 @@ import { | ||||
| import type { ModelType } from '@lib/enums/ModelType'; | ||||
| import { cancelEvent } from '@lib/functions/Events'; | ||||
| import { navigateToLink } from '@lib/functions/Navigation'; | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { identifierString } from '../../functions/conversion'; | ||||
| import { usePluginPanels } from '../../hooks/UsePluginPanels'; | ||||
| import { useLocalState } from '../../states/LocalState'; | ||||
| @@ -262,7 +263,8 @@ function IndexPanelComponent({ | ||||
|   selectedPanel, | ||||
|   panels | ||||
| }: Readonly<PanelProps>) { | ||||
|   const lastUsedPanel = useLocalState((state) => { | ||||
|   const lastUsedPanel = useLocalState( | ||||
|     useShallow((state) => { | ||||
|       const panelName = | ||||
|         selectedPanel || state.lastUsedPanels[pageKey] || panels[0]?.name; | ||||
|  | ||||
| @@ -274,7 +276,8 @@ function IndexPanelComponent({ | ||||
|       } | ||||
|  | ||||
|       return panelName; | ||||
|   }); | ||||
|     }) | ||||
|   ); | ||||
|  | ||||
|   return <Navigate to={lastUsedPanel} replace />; | ||||
| } | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import { useMantineColorScheme, useMantineTheme } from '@mantine/core'; | ||||
| import { useMemo } from 'react'; | ||||
| import { useNavigate } from 'react-router-dom'; | ||||
|  | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { api, queryClient } from '../../App'; | ||||
| import { useLocalState } from '../../states/LocalState'; | ||||
| import { | ||||
| @@ -24,7 +24,7 @@ import { | ||||
| } from '../../hooks/UseForm'; | ||||
|  | ||||
| export const useInvenTreeContext = () => { | ||||
|   const [locale, host] = useLocalState((s) => [s.language, s.host]); | ||||
|   const [locale, host] = useLocalState(useShallow((s) => [s.language, s.host])); | ||||
|   const navigate = useNavigate(); | ||||
|   const user = useUserState(); | ||||
|   const { colorScheme } = useMantineColorScheme(); | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import { I18nProvider } from '@lingui/react'; | ||||
| import { LoadingOverlay, Text } from '@mantine/core'; | ||||
| import { useEffect, useRef, useState } from 'react'; | ||||
|  | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { api } from '../App'; | ||||
| import { useServerApiState } from '../states/ApiState'; | ||||
| import { useLocalState } from '../states/LocalState'; | ||||
| @@ -61,8 +62,8 @@ export const getSupportedLanguages = (): Record<string, string> => { | ||||
| export function LanguageContext({ | ||||
|   children | ||||
| }: Readonly<{ children: JSX.Element }>) { | ||||
|   const [language] = useLocalState((state) => [state.language]); | ||||
|   const [server] = useServerApiState((state) => [state.server]); | ||||
|   const [language] = useLocalState(useShallow((state) => [state.language])); | ||||
|   const [server] = useServerApiState(useShallow((state) => [state.server])); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     activateLocale(defaultLocale); | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import { MantineProvider, createTheme } from '@mantine/core'; | ||||
| import { ModalsProvider } from '@mantine/modals'; | ||||
| import { Notifications } from '@mantine/notifications'; | ||||
| import { ContextMenuProvider } from 'mantine-contextmenu'; | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { AboutInvenTreeModal } from '../components/modals/AboutInvenTreeModal'; | ||||
| import { LicenseModal } from '../components/modals/LicenseModal'; | ||||
| import { QrModal } from '../components/modals/QrModal'; | ||||
| @@ -15,7 +16,7 @@ import { colorSchema } from './colorSchema'; | ||||
| export function ThemeContext({ | ||||
|   children | ||||
| }: Readonly<{ children: JSX.Element }>) { | ||||
|   const [userTheme] = useLocalState((state) => [state.userTheme]); | ||||
|   const [userTheme] = useLocalState(useShallow((state) => [state.userTheme])); | ||||
|  | ||||
|   // Theme | ||||
|   const myTheme = createTheme({ | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import { IconBarcode, IconLink, IconPointer } from '@tabler/icons-react'; | ||||
| import type { NavigateFunction } from 'react-router-dom'; | ||||
|  | ||||
| import { openContextModal } from '@mantine/modals'; | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { useLocalState } from '../states/LocalState'; | ||||
| import { useUserState } from '../states/UserState'; | ||||
| import { aboutInvenTree, docLinks, licenseInfo, serverInfo } from './links'; | ||||
| @@ -16,7 +17,9 @@ export function openQrModal(navigate: NavigateFunction) { | ||||
| } | ||||
|  | ||||
| export function getActions(navigate: NavigateFunction) { | ||||
|   const setNavigationOpen = useLocalState((state) => state.setNavigationOpen); | ||||
|   const setNavigationOpen = useLocalState( | ||||
|     useShallow((state) => state.setNavigationOpen) | ||||
|   ); | ||||
|   const { user } = useUserState(); | ||||
|  | ||||
|   const actions: SpotlightActionData[] = [ | ||||
|   | ||||
| @@ -500,15 +500,19 @@ function StockOperationsRow({ | ||||
|     props.removeFn(props.idx); | ||||
|   }; | ||||
|  | ||||
|   const callChangeFn = (idx: number, key: string, value: any) => { | ||||
|     setTimeout(() => props.changeFn(idx, key, value), 0); | ||||
|   }; | ||||
|  | ||||
|   const [packagingOpen, packagingHandlers] = useDisclosure(false, { | ||||
|     onOpen: () => { | ||||
|       if (transfer) { | ||||
|         props.changeFn(props.idx, 'packaging', record?.packaging || undefined); | ||||
|         callChangeFn(props.idx, 'packaging', record?.packaging || undefined); | ||||
|       } | ||||
|     }, | ||||
|     onClose: () => { | ||||
|       if (transfer) { | ||||
|         props.changeFn(props.idx, 'packaging', undefined); | ||||
|         callChangeFn(props.idx, 'packaging', undefined); | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
| @@ -520,7 +524,7 @@ function StockOperationsRow({ | ||||
|     }, | ||||
|     onClose: () => { | ||||
|       setStatus(undefined); | ||||
|       props.changeFn(props.idx, 'status', undefined); | ||||
|       callChangeFn(props.idx, 'status', undefined); | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   | ||||
| @@ -4,6 +4,8 @@ import { Anchor, Divider, Group, Loader, Text } from '@mantine/core'; | ||||
| import { useToggle } from '@mantine/hooks'; | ||||
| import { useEffect, useMemo, useState } from 'react'; | ||||
| import { useLocation, useNavigate, useSearchParams } from 'react-router-dom'; | ||||
|  | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { setApiDefaults } from '../../App'; | ||||
| import { AuthFormOptions } from '../../components/forms/AuthFormOptions'; | ||||
| import { AuthenticationForm } from '../../components/forms/AuthenticationForm'; | ||||
| @@ -19,15 +21,12 @@ import { useLocalState } from '../../states/LocalState'; | ||||
| import { Wrapper } from './Layout'; | ||||
|  | ||||
| export default function Login() { | ||||
|   const [hostKey, setHost, hostList] = useLocalState((state) => [ | ||||
|     state.hostKey, | ||||
|     state.setHost, | ||||
|     state.hostList | ||||
|   ]); | ||||
|   const [server, fetchServerApiState] = useServerApiState((state) => [ | ||||
|     state.server, | ||||
|     state.fetchServerApiState | ||||
|   ]); | ||||
|   const [hostKey, setHost, hostList] = useLocalState( | ||||
|     useShallow((state) => [state.hostKey, state.setHost, state.hostList]) | ||||
|   ); | ||||
|   const [server, fetchServerApiState] = useServerApiState( | ||||
|     useShallow((state) => [state.server, state.fetchServerApiState]) | ||||
|   ); | ||||
|   const [isLoggingIn, setIsLoggingIn] = useState<boolean>(false); | ||||
|   const hostname = | ||||
|     hostList[hostKey] === undefined ? t`No selection` : hostList[hostKey]?.name; | ||||
| @@ -36,7 +35,10 @@ export default function Login() { | ||||
|   const location = useLocation(); | ||||
|   const [searchParams] = useSearchParams(); | ||||
|   const [sso_registration, registration_enabled] = useServerApiState( | ||||
|     (state) => [state.sso_registration_enabled, state.registration_enabled] | ||||
|     useShallow((state) => [ | ||||
|       state.sso_registration_enabled, | ||||
|       state.registration_enabled | ||||
|     ]) | ||||
|   ); | ||||
|   const both_reg_enabled = | ||||
|     registration_enabled() || sso_registration() || false; | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import { useMemo } from 'react'; | ||||
| import { ApiEndpoints } from '@lib/enums/ApiEndpoints'; | ||||
| import type { ApiFormFieldSet } from '@lib/types/Forms'; | ||||
| import { useNavigate } from 'react-router-dom'; | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { ActionButton } from '../../../../components/buttons/ActionButton'; | ||||
| import { YesNoUndefinedButton } from '../../../../components/buttons/YesNoButton'; | ||||
| import { ActionDropdown } from '../../../../components/items/ActionDropdown'; | ||||
| @@ -17,10 +18,9 @@ import { useUserState } from '../../../../states/UserState'; | ||||
| export function AccountDetailPanel() { | ||||
|   const navigate = useNavigate(); | ||||
|  | ||||
|   const [user, fetchUserState] = useUserState((state) => [ | ||||
|     state.user, | ||||
|     state.fetchUserState | ||||
|   ]); | ||||
|   const [user, fetchUserState] = useUserState( | ||||
|     useShallow((state) => [state.user, state.fetchUserState]) | ||||
|   ); | ||||
|  | ||||
|   const userFields: ApiFormFieldSet = useMemo(() => { | ||||
|     return { | ||||
|   | ||||
| @@ -31,6 +31,7 @@ import { | ||||
| } from '@tabler/icons-react'; | ||||
| import { useQuery } from '@tanstack/react-query'; | ||||
| import { useMemo, useState } from 'react'; | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { api } from '../../../../App'; | ||||
| import { StylishText } from '../../../../components/items/StylishText'; | ||||
| import { ProviderLogin, authApi } from '../../../../functions/auth'; | ||||
| @@ -40,10 +41,9 @@ import { QrRegistrationForm } from './QrRegistrationForm'; | ||||
| import { useReauth } from './useConfirm'; | ||||
|  | ||||
| export function SecurityContent() { | ||||
|   const [auth_config, sso_enabled] = useServerApiState((state) => [ | ||||
|     state.auth_config, | ||||
|     state.sso_enabled | ||||
|   ]); | ||||
|   const [auth_config, sso_enabled] = useServerApiState( | ||||
|     useShallow((state) => [state.auth_config, state.sso_enabled]) | ||||
|   ); | ||||
|  | ||||
|   return ( | ||||
|     <Stack> | ||||
| @@ -510,7 +510,9 @@ function MfaAddSection({ | ||||
|   refetch: () => void; | ||||
|   showRecoveryCodes: (codes: Recoverycodes) => void; | ||||
| }>) { | ||||
|   const [auth_config] = useServerApiState((state) => [state.auth_config]); | ||||
|   const [auth_config] = useServerApiState( | ||||
|     useShallow((state) => [state.auth_config]) | ||||
|   ); | ||||
|   const [totpQrOpen, { open: openTotpQr, close: closeTotpQr }] = | ||||
|     useDisclosure(false); | ||||
|   const [totpQr, setTotpQr] = useState<{ totp_url: string; secret: string }>(); | ||||
|   | ||||
| @@ -17,6 +17,7 @@ import { | ||||
| import { IconRestore } from '@tabler/icons-react'; | ||||
| import { useState } from 'react'; | ||||
|  | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { ColorToggle } from '../../../../components/items/ColorToggle'; | ||||
| import { LanguageSelect } from '../../../../components/items/LanguageSelect'; | ||||
| import { StylishText } from '../../../../components/items/StylishText'; | ||||
| @@ -34,11 +35,9 @@ const LOOKUP = Object.assign( | ||||
|  | ||||
| export function UserTheme({ height }: Readonly<{ height: number }>) { | ||||
|   const theme = useMantineTheme(); | ||||
|   const [userTheme, setTheme, setLanguage] = useLocalState((state) => [ | ||||
|     state.userTheme, | ||||
|     state.setTheme, | ||||
|     state.setLanguage | ||||
|   ]); | ||||
|   const [userTheme, setTheme, setLanguage] = useLocalState( | ||||
|     useShallow((state) => [state.userTheme, state.setTheme, state.setLanguage]) | ||||
|   ); | ||||
|  | ||||
|   // radius | ||||
|   function getMark(value: number) { | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import { Accordion, Alert, Stack } from '@mantine/core'; | ||||
| import { IconInfoCircle } from '@tabler/icons-react'; | ||||
| import { lazy } from 'react'; | ||||
|  | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { StylishText } from '../../../../components/items/StylishText'; | ||||
| import { GlobalSettingList } from '../../../../components/settings/SettingList'; | ||||
| import { Loadable } from '../../../../functions/loading'; | ||||
| @@ -20,7 +21,7 @@ const PluginErrorTable = Loadable( | ||||
|  | ||||
| export default function PluginManagementPanel() { | ||||
|   const pluginsEnabled = useServerApiState( | ||||
|     (state) => state.server.plugins_enabled | ||||
|     useShallow((state) => state.server.plugins_enabled) | ||||
|   ); | ||||
|  | ||||
|   const user = useUserState(); | ||||
|   | ||||
| @@ -18,6 +18,7 @@ import { | ||||
| } from '@tabler/icons-react'; | ||||
| import { useMemo } from 'react'; | ||||
|  | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import PermissionDenied from '../../../components/errors/PermissionDenied'; | ||||
| import PageTitle from '../../../components/nav/PageTitle'; | ||||
| import { SettingsHeader } from '../../../components/nav/SettingsHeader'; | ||||
| @@ -309,7 +310,7 @@ export default function SystemSettings() { | ||||
|  | ||||
|   const user = useUserState(); | ||||
|  | ||||
|   const [server] = useServerApiState((state) => [state.server]); | ||||
|   const [server] = useServerApiState(useShallow((state) => [state.server])); | ||||
|  | ||||
|   if (!user.isLoggedIn()) { | ||||
|     return <Skeleton />; | ||||
|   | ||||
| @@ -10,6 +10,7 @@ import { | ||||
| } from '@tabler/icons-react'; | ||||
| import { useMemo } from 'react'; | ||||
|  | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import PageTitle from '../../../components/nav/PageTitle'; | ||||
| import { SettingsHeader } from '../../../components/nav/SettingsHeader'; | ||||
| import type { PanelType } from '../../../components/panels/Panel'; | ||||
| @@ -23,10 +24,9 @@ import { AccountContent } from './AccountSettings/UserPanel'; | ||||
|  * User settings page | ||||
|  */ | ||||
| export default function UserSettings() { | ||||
|   const [user, isLoggedIn] = useUserState((state) => [ | ||||
|     state.user, | ||||
|     state.isLoggedIn | ||||
|   ]); | ||||
|   const [user, isLoggedIn] = useUserState( | ||||
|     useShallow((state) => [state.user, state.isLoggedIn]) | ||||
|   ); | ||||
|  | ||||
|   const userSettingsPanels: PanelType[] = useMemo(() => { | ||||
|     return [ | ||||
|   | ||||
| @@ -11,6 +11,7 @@ import { apiUrl } from '@lib/functions/Api'; | ||||
| import type { TableFilter } from '@lib/types/Filters'; | ||||
| import { showNotification } from '@mantine/notifications'; | ||||
| import { useNavigate } from 'react-router-dom'; | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { api } from '../../App'; | ||||
| import { AddItemButton } from '../../components/buttons/AddItemButton'; | ||||
| import { EditApiForm } from '../../components/forms/ApiForm'; | ||||
| @@ -63,8 +64,7 @@ export function UserDrawer({ | ||||
|     throwError: true | ||||
|   }); | ||||
|  | ||||
|   const currentUserPk = useUserState((s) => s.user?.pk); | ||||
|  | ||||
|   const currentUserPk = useUserState(useShallow((s) => s.user?.pk)); | ||||
|   const isCurrentUser = useMemo( | ||||
|     () => currentUserPk === Number.parseInt(id, 10), | ||||
|     [currentUserPk, id] | ||||
|   | ||||
| @@ -2,6 +2,7 @@ import { useEffect } from 'react'; | ||||
| import { BrowserRouter } from 'react-router-dom'; | ||||
|  | ||||
| import { getBaseUrl } from '@lib/functions/Navigation'; | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { api, queryClient } from '../App'; | ||||
| import { ApiProvider } from '../contexts/ApiContext'; | ||||
| import { ThemeContext } from '../contexts/ThemeContext'; | ||||
| @@ -10,7 +11,7 @@ import { routes } from '../router'; | ||||
| import { useLocalState } from '../states/LocalState'; | ||||
|  | ||||
| export default function DesktopAppView() { | ||||
|   const [hostList] = useLocalState((state) => [state.hostList]); | ||||
|   const [hostList] = useLocalState(useShallow((state) => [state.hostList])); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     if (Object.keys(hostList).length === 0) { | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| import '@mantine/core/styles.css'; | ||||
| import { useViewportSize } from '@mantine/hooks'; | ||||
| import { lazy, useEffect } from 'react'; | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
|  | ||||
| import { setApiDefaults } from '../App'; | ||||
| import { Loadable } from '../functions/loading'; | ||||
| @@ -17,7 +18,9 @@ const DesktopAppView = Loadable(lazy(() => import('./DesktopAppView'))); | ||||
|  | ||||
| // Main App | ||||
| export default function MainView() { | ||||
|   const [allowMobile] = useLocalState((state) => [state.allowMobile]); | ||||
|   const [allowMobile] = useLocalState( | ||||
|     useShallow((state) => [state.allowMobile]) | ||||
|   ); | ||||
|   // Set initial login status | ||||
|   useEffect(() => { | ||||
|     try { | ||||
|   | ||||
| @@ -1,13 +1,16 @@ | ||||
| import { Trans } from '@lingui/react/macro'; | ||||
| import { Anchor, Center, Container, Stack, Text, Title } from '@mantine/core'; | ||||
|  | ||||
| import { useShallow } from 'zustand/react/shallow'; | ||||
| import { ThemeContext } from '../contexts/ThemeContext'; | ||||
| import { docLinks } from '../defaults/links'; | ||||
| import { IS_DEV } from '../main'; | ||||
| import { useLocalState } from '../states/LocalState'; | ||||
|  | ||||
| export default function MobileAppView() { | ||||
|   const [setAllowMobile] = useLocalState((state) => [state.setAllowMobile]); | ||||
|   const [setAllowMobile] = useLocalState( | ||||
|     useShallow((state) => [state.setAllowMobile]) | ||||
|   ); | ||||
|  | ||||
|   function ignore() { | ||||
|     setAllowMobile(true); | ||||
|   | ||||
| @@ -5465,11 +5465,6 @@ use-sidecar@^1.1.2: | ||||
|     detect-node-es "^1.1.0" | ||||
|     tslib "^2.0.0" | ||||
|  | ||||
| use-sync-external-store@1.2.2: | ||||
|   version "1.2.2" | ||||
|   resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9" | ||||
|   integrity sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw== | ||||
|  | ||||
| util-deprecate@^1.0.1: | ||||
|   version "1.0.2" | ||||
|   resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" | ||||
| @@ -5712,9 +5707,7 @@ zod@^3.22.4: | ||||
|   resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d" | ||||
|   integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g== | ||||
|  | ||||
| zustand@^4.5.5: | ||||
|   version "4.5.5" | ||||
|   resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.5.tgz#f8c713041543715ec81a2adda0610e1dc82d4ad1" | ||||
|   integrity sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q== | ||||
|   dependencies: | ||||
|     use-sync-external-store "1.2.2" | ||||
| zustand@^5.0.3: | ||||
|   version "5.0.3" | ||||
|   resolved "https://registry.yarnpkg.com/zustand/-/zustand-5.0.3.tgz#b323435b73d06b2512e93c77239634374b0e407f" | ||||
|   integrity sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg== | ||||
|   | ||||
		Reference in New Issue
	
	Block a user