ydb-embedded-ui 4.26.0 → 4.27.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.27.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.27.0...v4.27.1) (2024-01-10)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * enable extract and set user settings manually ([#629](https://github.com/ydb-platform/ydb-embedded-ui/issues/629)) ([5eecd58](https://github.com/ydb-platform/ydb-embedded-ui/commit/5eecd58249688b4a8b3ecad766564f7b404e839c))
9
+
10
+ ## [4.27.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.26.0...v4.27.0) (2023-12-28)
11
+
12
+
13
+ ### Features
14
+
15
+ * migrate from external settings api ([#621](https://github.com/ydb-platform/ydb-embedded-ui/issues/621)) ([ae2fbbe](https://github.com/ydb-platform/ydb-embedded-ui/commit/ae2fbbef66d9aba150012027baf8b89bf79cd741))
16
+
3
17
  ## [4.26.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.25.0...v4.26.0) (2023-12-14)
4
18
 
5
19
 
@@ -16,8 +16,8 @@ import ReduxTooltip from '../ReduxTooltip/ReduxTooltip';
16
16
  import Header from '../Header/Header';
17
17
  import AppIcons from '../AppIcons/AppIcons';
18
18
 
19
- import {getParsedSettingValue} from '../../store/reducers/settings/settings';
20
19
  import {THEME_KEY} from '../../utils/constants';
20
+ import {useSetting} from '../../utils/hooks';
21
21
 
22
22
  import './App.scss';
23
23
  import PropTypes from 'prop-types';
@@ -72,7 +72,10 @@ Content.propTypes = {
72
72
  };
73
73
 
74
74
  function ContentWrapper(props) {
75
- const {theme, singleClusterMode, isAuthenticated} = props;
75
+ const {singleClusterMode, isAuthenticated} = props;
76
+
77
+ const [theme] = useSetting(THEME_KEY);
78
+
76
79
  return (
77
80
  <HistoryContext.Consumer>
78
81
  {(history) => (
@@ -96,7 +99,6 @@ function ContentWrapper(props) {
96
99
  }
97
100
 
98
101
  ContentWrapper.propTypes = {
99
- theme: PropTypes.string,
100
102
  singleClusterMode: PropTypes.bool,
101
103
  isAuthenticated: PropTypes.bool,
102
104
  children: PropTypes.node,
@@ -104,7 +106,6 @@ ContentWrapper.propTypes = {
104
106
 
105
107
  function mapStateToProps(state) {
106
108
  return {
107
- theme: getParsedSettingValue(state, THEME_KEY),
108
109
  isAuthenticated: state.authentication.isAuthenticated,
109
110
  singleClusterMode: state.singleClusterMode,
110
111
  };
@@ -1,5 +1,5 @@
1
1
  import React, {useState} from 'react';
2
- import {connect} from 'react-redux';
2
+ import {useDispatch} from 'react-redux';
3
3
  import {useLocation} from 'react-router';
4
4
  import {useHistory} from 'react-router-dom';
5
5
  import cn from 'bem-cn-lite';
@@ -19,7 +19,6 @@ import settingsIcon from '../../assets/icons/settings.svg';
19
19
  import supportIcon from '../../assets/icons/support.svg';
20
20
 
21
21
  import {logout} from '../../store/reducers/authentication/authentication';
22
- import {getParsedSettingValue, setSettingValue} from '../../store/reducers/settings/settings';
23
22
  import {TENANT_PAGE, TENANT_PAGES_IDS} from '../../store/reducers/tenant/constants';
24
23
  import routes, {TENANT, createHref, parseQuery} from '../../routes';
25
24
  import {useSetting, useTypedSelector} from '../../utils/hooks';
@@ -117,10 +116,6 @@ function YdbUserDropdown({isCompact, popupAnchor, ydbUser}: YdbUserDropdownProps
117
116
 
118
117
  interface AsideNavigationProps {
119
118
  children: React.ReactNode;
120
- ydbUser: string;
121
- compact: boolean;
122
- logout: VoidFunction;
123
- setSettingValue: (name: string, value: string) => void;
124
119
  }
125
120
 
126
121
  enum Panel {
@@ -189,15 +184,19 @@ export const useGetLeftNavigationItems = () => {
189
184
 
190
185
  function AsideNavigation(props: AsideNavigationProps) {
191
186
  const history = useHistory();
187
+ const dispatch = useDispatch();
192
188
 
193
189
  const [visiblePanel, setVisiblePanel] = useState<Panel>();
194
190
 
195
- const setIsCompact = (compact: boolean) => {
196
- props.setSettingValue(ASIDE_HEADER_COMPACT_KEY, JSON.stringify(compact));
197
- };
191
+ const {user: ydbUser} = useTypedSelector((state) => state.authentication);
192
+ const [compact, setIsCompact] = useSetting<boolean>(ASIDE_HEADER_COMPACT_KEY);
198
193
 
199
194
  const menuItems = useGetLeftNavigationItems();
200
195
 
196
+ const onLogout = () => {
197
+ dispatch(logout());
198
+ };
199
+
201
200
  return (
202
201
  <React.Fragment>
203
202
  <AsideHeader
@@ -207,7 +206,7 @@ function AsideNavigation(props: AsideNavigationProps) {
207
206
  onClick: () => history.push('/'),
208
207
  }}
209
208
  menuItems={menuItems}
210
- compact={props.compact}
209
+ compact={compact}
211
210
  onChangeCompact={setIsCompact}
212
211
  className={b()}
213
212
  renderContent={() => props.children}
@@ -248,8 +247,8 @@ function AsideNavigation(props: AsideNavigationProps) {
248
247
  isCompact={compact}
249
248
  popupAnchor={asideRef}
250
249
  ydbUser={{
251
- login: props.ydbUser,
252
- logout: props.logout,
250
+ login: ydbUser,
251
+ logout: onLogout,
253
252
  }}
254
253
  />
255
254
  </React.Fragment>
@@ -269,18 +268,4 @@ function AsideNavigation(props: AsideNavigationProps) {
269
268
  );
270
269
  }
271
270
 
272
- const mapStateToProps = (state: any) => {
273
- const {user: ydbUser} = state.authentication;
274
-
275
- return {
276
- ydbUser,
277
- compact: getParsedSettingValue(state, ASIDE_HEADER_COMPACT_KEY),
278
- };
279
- };
280
-
281
- const mapDispatchToProps = {
282
- logout,
283
- setSettingValue,
284
- };
285
-
286
- export default connect(mapStateToProps, mapDispatchToProps)(AsideNavigation);
271
+ export default AsideNavigation;
@@ -70,10 +70,7 @@ export const Partitions = ({path}: PartitionsProps) => {
70
70
  error: nodesError,
71
71
  } = useTypedSelector((state) => state.nodesList);
72
72
 
73
- const [hiddenColumns, setHiddenColumns] = useSetting<string[]>(
74
- PARTITIONS_HIDDEN_COLUMNS_KEY,
75
- [],
76
- );
73
+ const [hiddenColumns, setHiddenColumns] = useSetting<string[]>(PARTITIONS_HIDDEN_COLUMNS_KEY);
77
74
 
78
75
  const [columns, columnsIdsForSelector] = useGetPartitionsColumns(selectedConsumer);
79
76
 
@@ -17,7 +17,6 @@ import {
17
17
  setTenantPath,
18
18
  } from '../../../../store/reducers/executeQuery';
19
19
  import {getExplainQuery, getExplainQueryAst} from '../../../../store/reducers/explainQuery';
20
- import {getParsedSettingValue, setSettingValue} from '../../../../store/reducers/settings/settings';
21
20
  import {setShowPreview} from '../../../../store/reducers/schema/schema';
22
21
  import {
23
22
  DEFAULT_IS_QUERY_RESULT_COLLAPSED,
@@ -86,6 +85,7 @@ function QueryEditor(props) {
86
85
  const [queryMode, setQueryMode] = useQueryModes();
87
86
  const [useMultiSchema] = useSetting(QUERY_USE_MULTI_SCHEMA_KEY);
88
87
  const [lastUsedQueryAction, setLastUsedQueryAction] = useSetting(LAST_USED_QUERY_ACTION_KEY);
88
+ const [savedQueries, setSavedQueries] = useSetting(SAVED_QUERIES_KEY);
89
89
 
90
90
  useEffect(() => {
91
91
  if (savedPath !== path) {
@@ -417,7 +417,7 @@ function QueryEditor(props) {
417
417
 
418
418
  const storageEventHandler = (event) => {
419
419
  if (event.key === SAVED_QUERIES_KEY) {
420
- props.setSettingValue(SAVED_QUERIES_KEY, event.newValue);
420
+ setSavedQueries(event.newValue);
421
421
  }
422
422
  };
423
423
 
@@ -430,8 +430,6 @@ function QueryEditor(props) {
430
430
  const onSaveQueryHandler = (queryName) => {
431
431
  const {
432
432
  executeQuery: {input},
433
- savedQueries = [],
434
- setSettingValue,
435
433
  } = props;
436
434
 
437
435
  const queryIndex = savedQueries.findIndex(
@@ -445,11 +443,11 @@ function QueryEditor(props) {
445
443
  newSavedQueries.push(newQuery);
446
444
  }
447
445
 
448
- setSettingValue(SAVED_QUERIES_KEY, JSON.stringify(newSavedQueries));
446
+ setSavedQueries(newSavedQueries);
449
447
  };
450
448
 
451
449
  const renderControls = () => {
452
- const {executeQuery, explainQuery, savedQueries} = props;
450
+ const {executeQuery, explainQuery} = props;
453
451
 
454
452
  return (
455
453
  <QueryEditorControls
@@ -511,7 +509,6 @@ const mapStateToProps = (state) => {
511
509
  return {
512
510
  executeQuery: state.executeQuery,
513
511
  explainQuery: state.explainQuery,
514
- savedQueries: getParsedSettingValue(state, SAVED_QUERIES_KEY),
515
512
  showPreview: state.schema.showPreview,
516
513
  currentSchema: state.schema.currentSchema,
517
514
  monacoHotKey: state.executeQuery?.monacoHotKey,
@@ -525,7 +522,6 @@ const mapDispatchToProps = {
525
522
  goToNextQuery,
526
523
  getExplainQuery,
527
524
  getExplainQueryAst,
528
- setSettingValue,
529
525
  setShowPreview,
530
526
  setMonacoHotKey,
531
527
  setTenantPath,
@@ -34,7 +34,6 @@ import type {StorageApiRequestParams} from '../store/reducers/storage/types';
34
34
 
35
35
  import {backend as BACKEND} from '../store';
36
36
  import {prepareSortValue} from '../utils/filters';
37
- import {settingsApi} from '../utils/settings';
38
37
 
39
38
  const config = {withCredentials: !window.custom_backend};
40
39
 
@@ -378,10 +377,12 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
378
377
  path_id: tenantId?.PathId,
379
378
  });
380
379
  }
381
- postSetting(name: string, value: string) {
380
+
381
+ /** @deprecated use localStorage instead */
382
+ postSetting(settingsApi: string, name: string, value: string) {
382
383
  return this.request({
383
384
  method: 'PATCH',
384
- url: settingsApi || '',
385
+ url: settingsApi,
385
386
  data: {[name]: value},
386
387
  });
387
388
  }
@@ -0,0 +1,109 @@
1
+ import {TENANT_PAGES_IDS} from '../store/reducers/tenant/constants';
2
+
3
+ import {
4
+ ASIDE_HEADER_COMPACT_KEY,
5
+ CLUSTER_INFO_HIDDEN_KEY,
6
+ INVERTED_DISKS_KEY,
7
+ LANGUAGE_KEY,
8
+ LAST_USED_QUERY_ACTION_KEY,
9
+ PARTITIONS_HIDDEN_COLUMNS_KEY,
10
+ QUERY_INITIAL_MODE_KEY,
11
+ QUERY_USE_MULTI_SCHEMA_KEY,
12
+ SAVED_QUERIES_KEY,
13
+ TENANT_INITIAL_PAGE_KEY,
14
+ THEME_KEY,
15
+ USE_BACKEND_PARAMS_FOR_TABLES_KEY,
16
+ USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY,
17
+ } from '../utils/constants';
18
+ import {QUERY_ACTIONS, QUERY_MODES} from '../utils/query';
19
+ import {parseJson} from '../utils/utils';
20
+
21
+ export type SettingsObject = Record<string, unknown>;
22
+
23
+ const USE_LOCAL_STORAGE_FOR_SETTINGS_KEY = 'useLocalStorageForSettings';
24
+
25
+ /** User settings keys and their default values */
26
+ export const DEFAULT_USER_SETTINGS: SettingsObject = {
27
+ [THEME_KEY]: 'system',
28
+ [LANGUAGE_KEY]: undefined,
29
+ [INVERTED_DISKS_KEY]: false,
30
+ [USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY]: false,
31
+ [QUERY_USE_MULTI_SCHEMA_KEY]: false,
32
+ [SAVED_QUERIES_KEY]: [],
33
+ [TENANT_INITIAL_PAGE_KEY]: TENANT_PAGES_IDS.query,
34
+ [QUERY_INITIAL_MODE_KEY]: QUERY_MODES.script,
35
+ [LAST_USED_QUERY_ACTION_KEY]: QUERY_ACTIONS.execute,
36
+ [ASIDE_HEADER_COMPACT_KEY]: true,
37
+ [PARTITIONS_HIDDEN_COLUMNS_KEY]: [],
38
+ [CLUSTER_INFO_HIDDEN_KEY]: true,
39
+ [USE_BACKEND_PARAMS_FOR_TABLES_KEY]: false,
40
+ };
41
+
42
+ class SettingsManager {
43
+ constructor() {
44
+ // Migrate settings to LS if external API was used before
45
+ const settingsApi = window.web_version ? window.systemSettings?.settingsApi : undefined;
46
+
47
+ const useLocalStorage = this.readUserSettingsValue(USE_LOCAL_STORAGE_FOR_SETTINGS_KEY);
48
+
49
+ if (settingsApi && !useLocalStorage) {
50
+ const externalUserSettings = window.userSettings;
51
+
52
+ if (externalUserSettings) {
53
+ Object.entries(externalUserSettings).forEach(([key, value]) =>
54
+ this.setUserSettingsValue(key, value),
55
+ );
56
+ }
57
+
58
+ this.setUserSettingsValue(USE_LOCAL_STORAGE_FOR_SETTINGS_KEY, true);
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Returns parsed settings value.
64
+ * If value cannot be parsed, returns initially stored string.
65
+ * If there is no value, return default value
66
+ */
67
+ readUserSettingsValue(key: string, defaultValue?: unknown) {
68
+ return this.readValueFromLS(key) ?? defaultValue;
69
+ }
70
+
71
+ /**
72
+ * Stringify value and set it to LS
73
+ */
74
+ setUserSettingsValue(key: string, value: unknown) {
75
+ return this.setValueToLS(key, value);
76
+ }
77
+
78
+ /**
79
+ * Extract values by provided settings object
80
+ */
81
+ extractSettingsFromLS = (settings: SettingsObject) => {
82
+ return Object.entries(settings).reduce<SettingsObject>((acc, [key, value]) => {
83
+ acc[key] = this.readUserSettingsValue(key, value);
84
+ return acc;
85
+ }, {});
86
+ };
87
+
88
+ private readValueFromLS = (key: string): unknown => {
89
+ try {
90
+ const value = localStorage.getItem(key);
91
+
92
+ return parseJson(value);
93
+ } catch {
94
+ return undefined;
95
+ }
96
+ };
97
+
98
+ private setValueToLS = (key: string, value: unknown): void => {
99
+ try {
100
+ if (typeof value === 'string') {
101
+ localStorage.setItem(key, value);
102
+ } else {
103
+ localStorage.setItem(key, JSON.stringify(value));
104
+ }
105
+ } catch {}
106
+ };
107
+ }
108
+
109
+ export const settingsManager = new SettingsManager();
@@ -9,10 +9,10 @@ import type {
9
9
  QueryInHistory,
10
10
  } from '../../types/store/executeQuery';
11
11
  import type {QueryRequestParams, QueryMode, QuerySyntax} from '../../types/store/query';
12
- import {getValueFromLS, parseJson} from '../../utils/utils';
13
12
  import {QUERIES_HISTORY_KEY} from '../../utils/constants';
14
13
  import {QUERY_MODES, QUERY_SYNTAX, parseQueryAPIExecuteResponse} from '../../utils/query';
15
14
  import {parseQueryError} from '../../utils/error';
15
+ import {settingsManager} from '../../services/settings';
16
16
  import '../../services/api';
17
17
 
18
18
  import {createRequestActionTypes, createApiRequest} from '../utils';
@@ -28,7 +28,10 @@ const GO_TO_NEXT_QUERY = 'query/GO_TO_NEXT_QUERY';
28
28
  const SET_MONACO_HOT_KEY = 'query/SET_MONACO_HOT_KEY';
29
29
  const SET_TENANT_PATH = 'query/SET_TENANT_PATH';
30
30
 
31
- const queriesHistoryInitial: string[] = parseJson(getValueFromLS(QUERIES_HISTORY_KEY, '[]'));
31
+ const queriesHistoryInitial = settingsManager.readUserSettingsValue(
32
+ QUERIES_HISTORY_KEY,
33
+ [],
34
+ ) as string[];
32
35
 
33
36
  const sliceLimit = queriesHistoryInitial.length - MAXIMUM_QUERIES_IN_HISTORY;
34
37
 
@@ -97,7 +100,7 @@ const executeQuery: Reducer<ExecuteQueryState, ExecuteQueryAction> = (
97
100
  const newQueries = [...state.history.queries, {queryText, syntax}].slice(
98
101
  state.history.queries.length >= MAXIMUM_QUERIES_IN_HISTORY ? 1 : 0,
99
102
  );
100
- window.localStorage.setItem(QUERIES_HISTORY_KEY, JSON.stringify(newQueries));
103
+ settingsManager.setUserSettingsValue(QUERIES_HISTORY_KEY, newQueries);
101
104
  const currentIndex = newQueries.length - 1;
102
105
 
103
106
  return {
@@ -1,32 +1,8 @@
1
1
  import type {Reducer} from 'redux';
2
2
  import type {ThunkAction} from 'redux-thunk';
3
3
 
4
- import {
5
- SAVED_QUERIES_KEY,
6
- THEME_KEY,
7
- TENANT_INITIAL_PAGE_KEY,
8
- INVERTED_DISKS_KEY,
9
- ASIDE_HEADER_COMPACT_KEY,
10
- USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY,
11
- PARTITIONS_HIDDEN_COLUMNS_KEY,
12
- QUERY_INITIAL_MODE_KEY,
13
- CLUSTER_INFO_HIDDEN_KEY,
14
- LAST_USED_QUERY_ACTION_KEY,
15
- USE_BACKEND_PARAMS_FOR_TABLES_KEY,
16
- LANGUAGE_KEY,
17
- QUERY_USE_MULTI_SCHEMA_KEY,
18
- } from '../../../utils/constants';
19
4
  import '../../../services/api';
20
- import {parseJson} from '../../../utils/utils';
21
- import {QUERY_ACTIONS, QUERY_MODES} from '../../../utils/query';
22
- import {
23
- readSavedSettingsValue,
24
- settingsApi,
25
- systemSettings,
26
- userSettings,
27
- } from '../../../utils/settings';
28
-
29
- import {TENANT_PAGES_IDS} from '../tenant/constants';
5
+ import {DEFAULT_USER_SETTINGS, SettingsObject, settingsManager} from '../../../services/settings';
30
6
 
31
7
  import type {RootState} from '..';
32
8
  import type {
@@ -39,45 +15,19 @@ import type {
39
15
 
40
16
  const CHANGE_PROBLEM_FILTER = 'settings/CHANGE_PROBLEM_FILTER';
41
17
  export const SET_SETTING_VALUE = 'settings/SET_VALUE';
18
+ export const SET_USER_SETTINGS = 'settings/SET_USER_SETTINGS';
42
19
 
43
20
  export const ProblemFilterValues = {
44
21
  ALL: 'All',
45
22
  PROBLEMS: 'With problems',
46
23
  } as const;
47
24
 
25
+ const userSettings = settingsManager.extractSettingsFromLS(DEFAULT_USER_SETTINGS);
26
+ const systemSettings = window.systemSettings || {};
27
+
48
28
  export const initialState = {
49
29
  problemFilter: ProblemFilterValues.ALL,
50
- userSettings: {
51
- ...userSettings,
52
- [THEME_KEY]: readSavedSettingsValue(THEME_KEY, 'system'),
53
- [LANGUAGE_KEY]: readSavedSettingsValue(LANGUAGE_KEY),
54
- [INVERTED_DISKS_KEY]: readSavedSettingsValue(INVERTED_DISKS_KEY, 'false'),
55
- [USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY]: readSavedSettingsValue(
56
- USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY,
57
- 'false',
58
- ),
59
- [QUERY_USE_MULTI_SCHEMA_KEY]: readSavedSettingsValue(QUERY_USE_MULTI_SCHEMA_KEY, 'false'),
60
- [SAVED_QUERIES_KEY]: readSavedSettingsValue(SAVED_QUERIES_KEY, '[]'),
61
- [TENANT_INITIAL_PAGE_KEY]: readSavedSettingsValue(
62
- TENANT_INITIAL_PAGE_KEY,
63
- TENANT_PAGES_IDS.query,
64
- ),
65
- [QUERY_INITIAL_MODE_KEY]: readSavedSettingsValue(
66
- QUERY_INITIAL_MODE_KEY,
67
- QUERY_MODES.script,
68
- ),
69
- [LAST_USED_QUERY_ACTION_KEY]: readSavedSettingsValue(
70
- LAST_USED_QUERY_ACTION_KEY,
71
- QUERY_ACTIONS.execute,
72
- ),
73
- [ASIDE_HEADER_COMPACT_KEY]: readSavedSettingsValue(ASIDE_HEADER_COMPACT_KEY, 'true'),
74
- [PARTITIONS_HIDDEN_COLUMNS_KEY]: readSavedSettingsValue(PARTITIONS_HIDDEN_COLUMNS_KEY),
75
- [CLUSTER_INFO_HIDDEN_KEY]: readSavedSettingsValue(CLUSTER_INFO_HIDDEN_KEY, 'true'),
76
- [USE_BACKEND_PARAMS_FOR_TABLES_KEY]: readSavedSettingsValue(
77
- USE_BACKEND_PARAMS_FOR_TABLES_KEY,
78
- 'false',
79
- ),
80
- },
30
+ userSettings,
81
31
  systemSettings,
82
32
  };
83
33
 
@@ -100,7 +50,15 @@ const settings: Reducer<SettingsState, SettingsAction> = (state = initialState,
100
50
  userSettings: newSettings,
101
51
  };
102
52
  }
103
-
53
+ case SET_USER_SETTINGS: {
54
+ return {
55
+ ...state,
56
+ userSettings: {
57
+ ...state.userSettings,
58
+ ...action.data,
59
+ },
60
+ };
61
+ }
104
62
  default:
105
63
  return state;
106
64
  }
@@ -108,33 +66,21 @@ const settings: Reducer<SettingsState, SettingsAction> = (state = initialState,
108
66
 
109
67
  export const setSettingValue = (
110
68
  name: string,
111
- value: string,
69
+ value: unknown,
112
70
  ): ThunkAction<void, RootState, unknown, SetSettingValueAction> => {
113
71
  return (dispatch) => {
114
72
  dispatch({type: SET_SETTING_VALUE, data: {name, value}});
115
73
 
116
- // If there is no settingsApi, use localStorage
117
- if (settingsApi) {
118
- window.api.postSetting(name, value);
119
- } else {
120
- try {
121
- localStorage.setItem(name, value);
122
- } catch {}
123
- }
74
+ settingsManager.setUserSettingsValue(name, value);
124
75
  };
125
76
  };
126
77
 
127
- export const getSettingValue = (state: SettingsRootStateSlice, name: string) => {
128
- return state.settings.userSettings[name];
78
+ export const setUserSettings = (data: SettingsObject) => {
79
+ return {type: SET_USER_SETTINGS, data} as const;
129
80
  };
130
81
 
131
- /**
132
- * Returns parsed settings value.
133
- * If value cannot be parsed, returns initially stored string
134
- */
135
- export const getParsedSettingValue = (state: SettingsRootStateSlice, name: string) => {
136
- const value = state.settings.userSettings[name];
137
- return parseJson(value);
82
+ export const getSettingValue = (state: SettingsRootStateSlice, name: string) => {
83
+ return state.settings.userSettings[name];
138
84
  };
139
85
 
140
86
  export const changeFilter = (filter: ProblemFilterValue) => {
@@ -1,20 +1,24 @@
1
1
  import type {ValueOf} from '../../../types/common';
2
- import {changeFilter, ProblemFilterValues, SET_SETTING_VALUE} from './settings';
2
+ import type {SettingsObject} from '../../../services/settings';
3
+ import {changeFilter, setUserSettings, ProblemFilterValues, SET_SETTING_VALUE} from './settings';
3
4
 
4
5
  export type ProblemFilterValue = ValueOf<typeof ProblemFilterValues>;
5
6
 
6
7
  export interface SettingsState {
7
8
  problemFilter: ProblemFilterValue;
8
- userSettings: Record<string, string | undefined>;
9
- systemSettings: Record<string, string | undefined>;
9
+ userSettings: SettingsObject;
10
+ systemSettings: SettingsObject;
10
11
  }
11
12
 
12
13
  export type SetSettingValueAction = {
13
14
  type: typeof SET_SETTING_VALUE;
14
- data: {name: string; value: string};
15
+ data: {name: string; value: unknown};
15
16
  };
16
17
 
17
- export type SettingsAction = ReturnType<typeof changeFilter> | SetSettingValueAction;
18
+ export type SettingsAction =
19
+ | ReturnType<typeof changeFilter>
20
+ | ReturnType<typeof setUserSettings>
21
+ | SetSettingValueAction;
18
22
 
19
23
  export interface SettingsRootStateSlice {
20
24
  settings: SettingsState;
@@ -39,8 +39,8 @@ interface Window {
39
39
  __REDUX_DEVTOOLS_EXTENSION_COMPOSE__?: typeof import('redux').compose;
40
40
  store?: import('redux').Store;
41
41
 
42
- userSettings?: Record<string, string | undefined>;
43
- systemSettings?: Record<string, string | undefined>;
42
+ userSettings?: import('../services/settings').SettingsObject;
43
+ systemSettings?: import('../services/settings').SettingsObject;
44
44
 
45
45
  api: import('../services/api').YdbEmbeddedAPI;
46
46
  }
@@ -1,22 +1,21 @@
1
1
  import {useCallback} from 'react';
2
2
  import {useDispatch} from 'react-redux';
3
3
 
4
- import {getParsedSettingValue, setSettingValue} from '../../store/reducers/settings/settings';
4
+ import {getSettingValue, setSettingValue} from '../../store/reducers/settings/settings';
5
5
 
6
6
  import {useTypedSelector} from './useTypedSelector';
7
7
 
8
8
  export const useSetting = <T>(key: string, defaultValue?: T): [T, (value: T) => void] => {
9
9
  const dispatch = useDispatch();
10
10
 
11
- const settingValue: T = useTypedSelector(
12
- (state) => getParsedSettingValue(state, key) ?? defaultValue,
13
- );
11
+ const settingValue = useTypedSelector((state) => {
12
+ // Since we type setter value as T, we assume that received value is also T
13
+ return (getSettingValue(state, key) ?? defaultValue) as T;
14
+ });
14
15
 
15
16
  const setValue = useCallback(
16
17
  (value: T) => {
17
- const preparedValue = typeof value === 'string' ? value : JSON.stringify(value);
18
-
19
- dispatch(setSettingValue(key, preparedValue));
18
+ dispatch(setSettingValue(key, value));
20
19
  },
21
20
  [dispatch, key],
22
21
  );
@@ -3,7 +3,7 @@ import {configure as configureUiKit} from '@gravity-ui/uikit';
3
3
  import {configure as configureYdbUiComponents} from 'ydb-ui-components';
4
4
 
5
5
  import {LANGUAGE_KEY} from '../constants';
6
- import {readSavedSettingsValue} from '../settings';
6
+ import {settingsManager} from '../../services/settings';
7
7
 
8
8
  enum Lang {
9
9
  En = 'en',
@@ -11,7 +11,7 @@ enum Lang {
11
11
  }
12
12
 
13
13
  const defaultLang = Lang.En;
14
- const currentLang = readSavedSettingsValue(LANGUAGE_KEY, defaultLang);
14
+ const currentLang = settingsManager.readUserSettingsValue(LANGUAGE_KEY, defaultLang) as Lang;
15
15
 
16
16
  const i18n = new I18N();
17
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ydb-embedded-ui",
3
- "version": "4.26.0",
3
+ "version": "4.27.1",
4
4
  "files": [
5
5
  "dist"
6
6
  ],
@@ -1,13 +0,0 @@
1
- import {getValueFromLS} from './utils';
2
-
3
- export const userSettings = window.userSettings || {};
4
- export const systemSettings = window.systemSettings || {};
5
-
6
- export const settingsApi = window.web_version ? systemSettings.settingsApi : undefined;
7
-
8
- export function readSavedSettingsValue(key: string, defaultValue?: string) {
9
- // If there is no settingsApi, use localStorage
10
- const savedValue = settingsApi ? userSettings[key] : getValueFromLS(key);
11
-
12
- return savedValue ?? defaultValue;
13
- }