This is an automated email from the ASF dual-hosted git repository. juzhiyuan pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/apisix-dashboard.git
The following commit(s) were added to refs/heads/master by this push: new f8e2ffa64 feat(stores/global): use jotai (#3134) f8e2ffa64 is described below commit f8e2ffa6421a50e153c4fc95ee7d93d730a3ecdb Author: YYYoung <isk...@outlook.com> AuthorDate: Sat Jul 26 14:13:21 2025 +0800 feat(stores/global): use jotai (#3134) --- package.json | 1 + pnpm-lock.yaml | 20 ++++++++++++++++ src/components/Header/SettingModalBtn.tsx | 7 ++++-- src/components/page/SettingsModal.tsx | 14 ++++++++---- src/config/req.ts | 8 ++++--- src/stores/global.ts | 38 +++++++++++-------------------- 6 files changed, 53 insertions(+), 35 deletions(-) diff --git a/package.json b/package.json index ec87fd28e..54695874e 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "fast-clean": "^1.4.0", "i18next": "^25.0.1", "immer": "^10.1.1", + "jotai": "^2.12.5", "mobx": "^6.13.7", "mobx-persist-store": "^1.1.8", "mobx-react-lite": "^4.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9516c2516..8811eb958 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -62,6 +62,9 @@ importers: immer: specifier: ^10.1.1 version: 10.1.1 + jotai: + specifier: ^2.12.5 + version: 2.12.5(@types/react@19.1.3)(react@19.1.0) mobx: specifier: ^6.13.7 version: 6.13.7 @@ -2467,6 +2470,18 @@ packages: resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} hasBin: true + jotai@2.12.5: + resolution: {integrity: sha512-G8m32HW3lSmcz/4mbqx0hgJIQ0ekndKWiYP7kWVKi0p6saLXdSoye+FZiOFyonnd7Q482LCzm8sMDl7Ar1NWDw==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=17.0.0' + react: '>=17.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + js-cookie@2.2.1: resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==} @@ -6467,6 +6482,11 @@ snapshots: jiti@2.4.2: {} + jotai@2.12.5(@types/react@19.1.3)(react@19.1.0): + optionalDependencies: + '@types/react': 19.1.3 + react: 19.1.0 + js-cookie@2.2.1: {} js-tokens@4.0.0: {} diff --git a/src/components/Header/SettingModalBtn.tsx b/src/components/Header/SettingModalBtn.tsx index a13ce1b15..1003d60fc 100644 --- a/src/components/Header/SettingModalBtn.tsx +++ b/src/components/Header/SettingModalBtn.tsx @@ -15,14 +15,17 @@ * limitations under the License. */ import { ActionIcon } from '@mantine/core'; +import { useSetAtom } from 'jotai'; -import { globalStore } from '@/stores/global'; +import { isSettingsOpenAtom } from '@/stores/global'; import IconSettings from '~icons/material-symbols/settings'; export const SettingModalBtn = () => { + const setIsSettingsOpen = useSetAtom(isSettingsOpenAtom); + return ( <ActionIcon - onClick={() => globalStore.settings.set('isOpen', true)} + onClick={() => setIsSettingsOpen(true)} variant="light" size="sm" > diff --git a/src/components/page/SettingsModal.tsx b/src/components/page/SettingsModal.tsx index 5cf8e8215..a9e582ee2 100644 --- a/src/components/page/SettingsModal.tsx +++ b/src/components/page/SettingsModal.tsx @@ -15,20 +15,23 @@ * limitations under the License. */ import { Divider, InputWrapper, Modal, Text, TextInput } from '@mantine/core'; +import { useAtom } from 'jotai'; import { useTranslation } from 'react-i18next'; import { queryClient } from '@/config/global'; -import { globalStore } from '@/stores/global'; +import { adminKeyAtom, isSettingsOpenAtom } from '@/stores/global'; import { sha } from '~build/git'; const AdminKey = () => { const { t } = useTranslation(); + const [adminKey, setAdminKey] = useAtom(adminKeyAtom); + return ( <TextInput label={t('settings.adminKey')} - value={globalStore.settings.adminKey} + value={adminKey} onChange={(e) => { - globalStore.settings.set('adminKey', e.currentTarget.value); + setAdminKey(e.currentTarget.value); setTimeout(() => { queryClient.invalidateQueries(); queryClient.refetchQueries(); @@ -52,11 +55,12 @@ const UICommitSha = () => { export const SettingsModal = () => { const { t } = useTranslation(); + const [isSettingsOpen, setIsSettingsOpen] = useAtom(isSettingsOpenAtom); return ( <Modal - opened={globalStore.settings.isOpen} - onClose={() => globalStore.settings.set('isOpen', false)} + opened={isSettingsOpen} + onClose={() => setIsSettingsOpen(false)} centered title={t('settings.title')} > diff --git a/src/config/req.ts b/src/config/req.ts index d0a6751ae..2f136c9c1 100644 --- a/src/config/req.ts +++ b/src/config/req.ts @@ -17,6 +17,7 @@ import { notifications } from '@mantine/notifications'; import axios, { AxiosError, type AxiosResponse, HttpStatusCode } from 'axios'; +import { getDefaultStore } from 'jotai'; import { stringify } from 'qs'; import { @@ -24,7 +25,7 @@ import { API_PREFIX, SKIP_INTERCEPTOR_HEADER, } from '@/config/constant'; -import { globalStore } from '@/stores/global'; +import { adminKeyAtom, isSettingsOpenAtom } from '@/stores/global'; export const req = axios.create(); @@ -40,7 +41,8 @@ req.interceptors.request.use((conf) => { }); }; conf.baseURL = API_PREFIX; - conf.headers.set(API_HEADER_KEY, globalStore.settings.adminKey); + const adminKey = getDefaultStore().get(adminKeyAtom); + conf.headers.set(API_HEADER_KEY, adminKey); return conf; }); @@ -84,7 +86,7 @@ req.interceptors.response.use( }); // Requires to enter admin key at 401 if (res.status === HttpStatusCode.Unauthorized) { - globalStore.settings.set('isOpen', true); + getDefaultStore().set(isSettingsOpenAtom, true); return Promise.resolve({ data: {} }); } } diff --git a/src/stores/global.ts b/src/stores/global.ts index ed49aa37d..0b0e5a450 100644 --- a/src/stores/global.ts +++ b/src/stores/global.ts @@ -14,30 +14,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { action, observable } from 'mobx'; -import { makePersistable } from 'mobx-persist-store'; +import { atom } from 'jotai'; +import { atomWithStorage } from 'jotai/utils'; -/** allow store use `set(key, value)` */ -const set = action(function <T, K extends keyof T>( - this: T, - key: K extends 'set' ? never : K, - value: T[K] -) { - this[key as K] = value; -}); +// Admin key with persistent storage +export const adminKeyAtom = atomWithStorage<string>( + 'settings:adminKey', + '', + undefined, + { + getOnInit: true, + } +); -const settingsStore = observable({ - set, - isOpen: false, - adminKey: '', -}); - -export const globalStore = observable({ - settings: settingsStore, -}); - -makePersistable(settingsStore, { - name: 'settings', - properties: ['adminKey'], - storage: window.localStorage, -}); +// Settings modal visibility state +export const isSettingsOpenAtom = atom<boolean>(false);