diff --git a/src/containers/Nodes/Nodes.tsx b/src/containers/Nodes/Nodes.tsx index cadeb8845..ef54a4e6f 100644 --- a/src/containers/Nodes/Nodes.tsx +++ b/src/containers/Nodes/Nodes.tsx @@ -17,7 +17,11 @@ import {selectAutoRefreshInterval} from '../../store/reducers/autoRefreshControl import {nodesApi} from '../../store/reducers/nodes/nodes'; import {filterNodes} from '../../store/reducers/nodes/selectors'; import type {NodesSortParams} from '../../store/reducers/nodes/types'; -import {ProblemFilterValues, changeFilter} from '../../store/reducers/settings/settings'; +import { + ProblemFilterValues, + changeFilter, + selectProblemFilter, +} from '../../store/reducers/settings/settings'; import type {ProblemFilterValue} from '../../store/reducers/settings/types'; import type {AdditionalNodesProps} from '../../types/additionalProps'; import {cn} from '../../utils/cn'; @@ -58,7 +62,7 @@ export const Nodes = ({path, additionalNodesProps = {}}: NodesProps) => { const isClusterNodes = !path; - const problemFilter = useTypedSelector((state) => state.settings.problemFilter); + const problemFilter = useTypedSelector(selectProblemFilter); const autorefresh = useTypedSelector(selectAutoRefreshInterval); const [useNodesEndpoint] = useSetting(USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY); diff --git a/src/containers/Nodes/VirtualNodes.tsx b/src/containers/Nodes/VirtualNodes.tsx index e723876a4..4197d8124 100644 --- a/src/containers/Nodes/VirtualNodes.tsx +++ b/src/containers/Nodes/VirtualNodes.tsx @@ -17,7 +17,11 @@ import type { } from '../../components/VirtualTable'; import {ResizeableVirtualTable} from '../../components/VirtualTable/ResizeableVirtualTable'; import type {NodesPreparedEntity} from '../../store/reducers/nodes/types'; -import {ProblemFilterValues, changeFilter} from '../../store/reducers/settings/settings'; +import { + ProblemFilterValues, + changeFilter, + selectProblemFilter, +} from '../../store/reducers/settings/settings'; import type {ProblemFilterValue} from '../../store/reducers/settings/types'; import type {AdditionalNodesProps} from '../../types/additionalProps'; import {cn} from '../../utils/cn'; @@ -56,7 +60,7 @@ export const VirtualNodes = ({path, parentContainer, additionalNodesProps}: Node const dispatch = useTypedDispatch(); - const problemFilter = useTypedSelector((state) => state.settings.problemFilter); + const problemFilter = useTypedSelector(selectProblemFilter); const filters = React.useMemo(() => { return [path, searchValue, problemFilter, uptimeFilter]; diff --git a/src/containers/Tenant/Diagnostics/HotKeys/HotKeys.tsx b/src/containers/Tenant/Diagnostics/HotKeys/HotKeys.tsx index 7e421dac1..cb3faca14 100644 --- a/src/containers/Tenant/Diagnostics/HotKeys/HotKeys.tsx +++ b/src/containers/Tenant/Diagnostics/HotKeys/HotKeys.tsx @@ -11,7 +11,7 @@ import {hotKeysApi} from '../../../../store/reducers/hotKeys/hotKeys'; import {schemaApi} from '../../../../store/reducers/schema/schema'; import type {HotKey} from '../../../../types/api/hotkeys'; import {cn} from '../../../../utils/cn'; -import {DEFAULT_TABLE_SETTINGS, IS_HOTKEYS_HELP_HIDDDEN_KEY} from '../../../../utils/constants'; +import {DEFAULT_TABLE_SETTINGS, IS_HOTKEYS_HELP_HIDDEN_KEY} from '../../../../utils/constants'; import {useSetting} from '../../../../utils/hooks'; import i18n from './i18n'; @@ -108,7 +108,7 @@ export function HotKeys({path}: HotKeysProps) { } function HelpCard() { - const [helpHidden, setHelpHidden] = useSetting(IS_HOTKEYS_HELP_HIDDDEN_KEY); + const [helpHidden, setHelpHidden] = useSetting(IS_HOTKEYS_HELP_HIDDEN_KEY); if (helpHidden) { return null; diff --git a/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx b/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx index ee04c734e..6de7f5327 100644 --- a/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx +++ b/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx @@ -32,7 +32,6 @@ import { import {useQueryModes, useSetting} from '../../../../utils/hooks'; import {LANGUAGE_YQL_ID} from '../../../../utils/monaco/yql/constants'; import {QUERY_ACTIONS} from '../../../../utils/query'; -import {parseJson} from '../../../../utils/utils'; import type {InitialPaneState} from '../../utils/paneVisibilityToggleHelpers'; import { PaneVisibilityActionTypes, @@ -146,20 +145,6 @@ function QueryEditor(props: QueryEditorProps) { }; }, []); - React.useEffect(() => { - const storageEventHandler = (event: StorageEvent) => { - if (event.key === SAVED_QUERIES_KEY) { - const v = parseJson(event.newValue); - setSavedQueries(v); - } - }; - - window.addEventListener('storage', storageEventHandler); - return () => { - window.removeEventListener('storage', storageEventHandler); - }; - }, [setSavedQueries]); - React.useEffect(() => { dispatchResultVisibilityState(PaneVisibilityActionTypes.triggerCollapse); }, []); diff --git a/src/lib.ts b/src/lib.ts index b25f9c5c4..b9ad12214 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -11,7 +11,7 @@ export {default as appRoutes} from './routes'; export {createApi, YdbEmbeddedAPI, YdbWebVersionAPI} from './services/api'; export {settingsManager} from './services/settings'; export {settings as userSettings} from './containers/UserSettings/settings'; -export {setUserSettings} from './store/reducers/settings/settings'; +export {setSettingValue, getSettingValue} from './store/reducers/settings/settings'; export {componentsRegistry} from './components/ComponentsProvider/componentsRegistry'; export {useSetting, useTypedSelector} from './utils/hooks'; diff --git a/src/services/settings.ts b/src/services/settings.ts index 14b9fea90..143c27c64 100644 --- a/src/services/settings.ts +++ b/src/services/settings.ts @@ -5,7 +5,7 @@ import { BINARY_DATA_IN_PLAIN_TEXT_DISPLAY, ENABLE_AUTOCOMPLETE, INVERTED_DISKS_KEY, - IS_HOTKEYS_HELP_HIDDDEN_KEY, + IS_HOTKEYS_HELP_HIDDEN_KEY, LANGUAGE_KEY, LAST_USED_QUERY_ACTION_KEY, PARTITIONS_HIDDEN_COLUMNS_KEY, @@ -41,7 +41,7 @@ export const DEFAULT_USER_SETTINGS = { [USE_CLUSTER_BALANCER_AS_BACKEND_KEY]: true, [ENABLE_AUTOCOMPLETE]: true, [AUTOCOMPLETE_ON_ENTER]: true, - [IS_HOTKEYS_HELP_HIDDDEN_KEY]: false, + [IS_HOTKEYS_HELP_HIDDEN_KEY]: false, } as const satisfies SettingsObject; class SettingsManager { diff --git a/src/store/configureStore.ts b/src/store/configureStore.ts index 86da25d23..bd6e64366 100644 --- a/src/store/configureStore.ts +++ b/src/store/configureStore.ts @@ -9,6 +9,7 @@ import {createApi} from '../services/api'; import {getUrlData} from './getUrlData'; import rootReducer from './reducers'; import {api as storeApi} from './reducers/api'; +import {syncUserSettingsFromLS} from './reducers/settings/settings'; import {UPDATE_REF} from './reducers/tooltip'; import getLocationMiddleware from './state-url-mapping'; @@ -37,6 +38,8 @@ function _configureStore< }).concat(locationMiddleware, ...middleware), }); + syncUserSettingsFromLS(store); + return store; } diff --git a/src/store/reducers/settings/settings.ts b/src/store/reducers/settings/settings.ts index e4c1e5064..260bbd5dd 100644 --- a/src/store/reducers/settings/settings.ts +++ b/src/store/reducers/settings/settings.ts @@ -1,20 +1,11 @@ -import type {Reducer, ThunkAction} from '@reduxjs/toolkit'; +import type {Store} from '@reduxjs/toolkit'; +import {createSlice} from '@reduxjs/toolkit'; -import type {RootState} from '../..'; -import type {SettingsObject} from '../../../services/settings'; import {DEFAULT_USER_SETTINGS, settingsManager} from '../../../services/settings'; +import {parseJson} from '../../../utils/utils'; +import type {AppDispatch} from '../../defaultStore'; -import type { - ProblemFilterValue, - SetSettingValueAction, - SettingsAction, - SettingsRootStateSlice, - SettingsState, -} from './types'; - -const CHANGE_PROBLEM_FILTER = 'settings/CHANGE_PROBLEM_FILTER'; -export const SET_SETTING_VALUE = 'settings/SET_VALUE'; -export const SET_USER_SETTINGS = 'settings/SET_USER_SETTINGS'; +import type {ProblemFilterValue, SettingsState} from './types'; export const ProblemFilterValues = { ALL: 'All', @@ -24,71 +15,60 @@ export const ProblemFilterValues = { const userSettings = settingsManager.extractSettingsFromLS(DEFAULT_USER_SETTINGS); const systemSettings = window.systemSettings || {}; -export const initialState = { +export const initialState: SettingsState = { problemFilter: ProblemFilterValues.ALL, userSettings, systemSettings, }; -const settings: Reducer = (state = initialState, action) => { - switch (action.type) { - case CHANGE_PROBLEM_FILTER: - return { - ...state, - problemFilter: action.data, - }; - - case SET_SETTING_VALUE: { - const newSettings = { - ...state.userSettings, - [action.data.name]: action.data.value, - }; - - return { - ...state, - userSettings: newSettings, - }; - } - case SET_USER_SETTINGS: { - return { - ...state, - userSettings: { - ...state.userSettings, - ...action.data, - }, - }; - } - default: - return state; - } -}; - -export const setSettingValue = ( - name: string, - value: unknown, -): ThunkAction => { - return (dispatch) => { - dispatch({type: SET_SETTING_VALUE, data: {name, value}}); +const settingsSlice = createSlice({ + name: 'settings', + initialState, + reducers: (create) => ({ + changeFilter: create.reducer((state, action) => { + state.problemFilter = action.payload; + }), + setSettingValue: create.reducer<{name: string; value: unknown}>((state, action) => { + state.userSettings[action.payload.name] = action.payload.value; + }), + }), + selectors: { + getSettingValue: (state, name: string) => state.userSettings[name], + selectProblemFilter: (state) => state.problemFilter, + }, +}); + +export const {changeFilter} = settingsSlice.actions; +export const {getSettingValue, selectProblemFilter} = settingsSlice.selectors; + +export const setSettingValue = (name: string, value: unknown) => { + return (dispatch: AppDispatch) => { + dispatch(settingsSlice.actions.setSettingValue({name, value})); settingsManager.setUserSettingsValue(name, value); }; }; -export const setUserSettings = (data: SettingsObject) => { - return {type: SET_USER_SETTINGS, data} as const; -}; - -export const getSettingValue = (state: SettingsRootStateSlice, name: string) => { - return state.settings.userSettings[name]; -}; - -export const changeFilter = (filter: ProblemFilterValue) => { - return { - type: CHANGE_PROBLEM_FILTER, - data: filter, - } as const; -}; +export function syncUserSettingsFromLS(store: Store) { + if (typeof window === 'undefined') { + return; + } -export const selectProblemFilter = (state: SettingsRootStateSlice) => state.settings.problemFilter; + window.addEventListener('storage', (event) => { + if (event.key && event.key in DEFAULT_USER_SETTINGS) { + const name = event.key as keyof typeof DEFAULT_USER_SETTINGS; + let value = DEFAULT_USER_SETTINGS[name]; + if (event.newValue !== null) { + value = parseJson(event.newValue); + } + store.dispatch( + settingsSlice.actions.setSettingValue({ + name, + value, + }), + ); + } + }); +} -export default settings; +export default settingsSlice.reducer; diff --git a/src/store/reducers/settings/types.ts b/src/store/reducers/settings/types.ts index 5ab367064..40dfcecd3 100644 --- a/src/store/reducers/settings/types.ts +++ b/src/store/reducers/settings/types.ts @@ -1,12 +1,7 @@ import type {SettingsObject} from '../../../services/settings'; import type {ValueOf} from '../../../types/common'; -import type { - ProblemFilterValues, - SET_SETTING_VALUE, - changeFilter, - setUserSettings, -} from './settings'; +import type {ProblemFilterValues} from './settings'; export type ProblemFilterValue = ValueOf; @@ -15,17 +10,3 @@ export interface SettingsState { userSettings: SettingsObject; systemSettings: SettingsObject; } - -export type SetSettingValueAction = { - type: typeof SET_SETTING_VALUE; - data: {name: string; value: unknown}; -}; - -export type SettingsAction = - | ReturnType - | ReturnType - | SetSettingValueAction; - -export interface SettingsRootStateSlice { - settings: SettingsState; -} diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 3da31838c..d925e6d79 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -138,4 +138,4 @@ export const ENABLE_AUTOCOMPLETE = 'enableAutocomplete'; export const AUTOCOMPLETE_ON_ENTER = 'autocompleteOnEnter'; -export const IS_HOTKEYS_HELP_HIDDDEN_KEY = 'isHotKeysHelpHidden'; +export const IS_HOTKEYS_HELP_HIDDEN_KEY = 'isHotKeysHelpHidden';