From f5277f9725c58891375313835714a72657937d03 Mon Sep 17 00:00:00 2001 From: Dmitry Kalinin Date: Mon, 29 Mar 2021 18:52:13 +0300 Subject: [PATCH] Saving settings to local storage (#3017) * Added saving settings to local storage * Added tooltip * Added CHANGELOG increased package version * Changed CHANGELOG --- CHANGELOG.md | 1 + cvat-ui/src/actions/settings-actions.ts | 12 +++- .../header/settings-modal/settings-modal.tsx | 62 +++++++++++++++++-- cvat-ui/src/reducers/settings-reducer.ts | 10 ++- cvat/apps/documentation/user_guide.md | 24 +++---- 5 files changed, 88 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ce06562210..e6a2c177946 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [Market-1501](https://www.aitribune.com/dataset/2018051063) format support () - Ability of upload manifest for dataset with images () - Annotations filters UI using react-awesome-query-builder (https://github.com/openvinotoolkit/cvat/issues/1418) +- Storing settings in local storage to keep them between browser sessions () - [ICDAR](https://rrc.cvc.uab.es/?ch=2) format support () - Added switcher to maintain poylgon crop behaviour () diff --git a/cvat-ui/src/actions/settings-actions.ts b/cvat-ui/src/actions/settings-actions.ts index 32aa3d41800..6fca99c3d33 100644 --- a/cvat-ui/src/actions/settings-actions.ts +++ b/cvat-ui/src/actions/settings-actions.ts @@ -3,7 +3,7 @@ // SPDX-License-Identifier: MIT import { AnyAction } from 'redux'; -import { GridColor, ColorBy } from 'reducers/interfaces'; +import { GridColor, ColorBy, SettingsState } from 'reducers/interfaces'; export enum SettingsActionTypes { SWITCH_ROTATE_ALL = 'SWITCH_ROTATE_ALL', @@ -32,6 +32,7 @@ export enum SettingsActionTypes { SWITCH_SHOWING_OBJECTS_TEXT_ALWAYS = 'SWITCH_SHOWING_OBJECTS_TEXT_ALWAYS', CHANGE_CANVAS_BACKGROUND_COLOR = 'CHANGE_CANVAS_BACKGROUND_COLOR', SWITCH_SETTINGS_DIALOG = 'SWITCH_SETTINGS_DIALOG', + SET_SETTINGS = 'SET_SETTINGS', } export function changeShapesOpacity(opacity: number): AnyAction { @@ -268,3 +269,12 @@ export function switchSettingsDialog(show?: boolean): AnyAction { }, }; } + +export function setSettings(settings: Partial): AnyAction { + return { + type: SettingsActionTypes.SET_SETTINGS, + payload: { + settings, + }, + }; +} diff --git a/cvat-ui/src/components/header/settings-modal/settings-modal.tsx b/cvat-ui/src/components/header/settings-modal/settings-modal.tsx index 9c076d77c2d..0965873b428 100644 --- a/cvat-ui/src/components/header/settings-modal/settings-modal.tsx +++ b/cvat-ui/src/components/header/settings-modal/settings-modal.tsx @@ -1,17 +1,22 @@ -// Copyright (C) 2020 Intel Corporation +// Copyright (C) 2020-2021 Intel Corporation // // SPDX-License-Identifier: MIT import './styles.scss'; -import React from 'react'; +import React, { useEffect } from 'react'; +import { useSelector, useDispatch } from 'react-redux'; import Tabs from 'antd/lib/tabs'; import Text from 'antd/lib/typography/Text'; import Modal from 'antd/lib/modal/Modal'; +import Button from 'antd/lib/button'; +import notification from 'antd/lib/notification'; +import Tooltip from 'antd/lib/tooltip'; import { PlayCircleOutlined, LaptopOutlined } from '@ant-design/icons'; +import { setSettings } from 'actions/settings-actions'; import WorkspaceSettingsContainer from 'containers/header/settings-modal/workspace-settings'; import PlayerSettingsContainer from 'containers/header/settings-modal/player-settings'; -import Button from 'antd/lib/button'; +import { CombinedState } from 'reducers/interfaces'; interface SettingsModalProps { visible: boolean; @@ -21,6 +26,44 @@ interface SettingsModalProps { const SettingsModal = (props: SettingsModalProps): JSX.Element => { const { visible, onClose } = props; + const settings = useSelector((state: CombinedState) => state.settings); + const dispatch = useDispatch(); + + const onSaveSettings = (): void => { + const settingsForSaving: any = {}; + for (const [key, value] of Object.entries(settings)) { + if (typeof value === 'object') { + settingsForSaving[key] = value; + } + } + localStorage.setItem('clientSettings', JSON.stringify(settingsForSaving)); + notification.success({ + message: 'Settings was successfully saved', + }); + }; + + useEffect(() => { + try { + const newSettings: any = {}; + const loadedSettings = JSON.parse(localStorage.getItem('clientSettings') as string); + for (const [sectionKey, section] of Object.entries(settings)) { + for (const [key, value] of Object.entries(section)) { + let settedValue = value; + if (sectionKey in loadedSettings && key in loadedSettings[sectionKey]) { + settedValue = loadedSettings[sectionKey][key]; + } + if (!(sectionKey in newSettings)) newSettings[sectionKey] = {}; + newSettings[sectionKey][key] = settedValue; + } + } + dispatch(setSettings(newSettings)); + } catch { + notification.error({ + message: 'Failed to load settings from local storage', + }); + } + }, []); + return ( { width={800} className='cvat-settings-modal' footer={( - + <> + + + + + )} >
diff --git a/cvat-ui/src/reducers/settings-reducer.ts b/cvat-ui/src/reducers/settings-reducer.ts index cdc811d4011..b4d4793b5ee 100644 --- a/cvat-ui/src/reducers/settings-reducer.ts +++ b/cvat-ui/src/reducers/settings-reducer.ts @@ -283,14 +283,20 @@ export default (state = defaultState, action: AnyAction): SettingsState => { showDialog: typeof action.payload.show === 'undefined' ? !state.showDialog : action.payload.show, }; } + case SettingsActionTypes.SET_SETTINGS: { + return { + ...state, + ...action.payload.settings, + }; + } case BoundariesActionTypes.RESET_AFTER_ERROR: case AnnotationActionTypes.GET_JOB_SUCCESS: { const { job } = action.payload; return { - ...defaultState, + ...state, player: { - ...defaultState.player, + ...state.player, resetZoom: job && job.task.mode === 'annotation', }, }; diff --git a/cvat/apps/documentation/user_guide.md b/cvat/apps/documentation/user_guide.md index 71a32dcb0ca..e6119d6be5f 100644 --- a/cvat/apps/documentation/user_guide.md +++ b/cvat/apps/documentation/user_guide.md @@ -1582,17 +1582,17 @@ The "Add rule" button adds a rule for objects display. A rule may use the follow **Supported properties:** -| Properties | Supported values | Description | -| ----------- | ------------------------------------------------------ | --------------------------------------------| -| `Label` | all the label names that are in the task | label name | -| `Type` | shape, track or tag | type of object | -| `Shape` | all shape types | type of shape | -| `Occluded` | true or false | occluded ([read more](#shape-mode-advanced))| -| `Width` | number of px or field | shape width | -| `Height` | number of px or field | shape height | -| `ServerID` | number or field | ID of the object on the server
(You can find out by forming a link to the object through the Action menu)| -| `ObjectID` | number or field | ID of the object in your client
(indicated on the objects sidebar)| -| `Attributes`| some other fields including attributes with a
similar type or a specific attribute value| any fields specified by a label | +| Properties | Supported values | Description | +| ------------ | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | +| `Label` | all the label names that are in the task | label name | +| `Type` | shape, track or tag | type of object | +| `Shape` | all shape types | type of shape | +| `Occluded` | true or false | occluded ([read more](#shape-mode-advanced)) | +| `Width` | number of px or field | shape width | +| `Height` | number of px or field | shape height | +| `ServerID` | number or field | ID of the object on the server
(You can find out by forming a link to the object through the Action menu) | +| `ObjectID` | number or field | ID of the object in your client
(indicated on the objects sidebar) | +| `Attributes` | some other fields including attributes with a
similar type or a specific attribute value | any fields specified by a label | **Supported operators for properties:** @@ -1624,7 +1624,7 @@ To add a group, click the "add group" button. Inside the group you can create ru If there is more than one rule in the group, they can be connected by `And` or `Or` operators. The rule group will work as well as a separate rule outside the group and will be joined by an - operator outside the group. +operator outside the group. You can create groups within other groups, to do so you need to click the add group button within the group. You can move rules and groups. To move the rule or group, drag it by the button.