Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Saving settings to local storage #3017

Merged
merged 5 commits into from
Mar 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 (<https://github.com/openvinotoolkit/cvat/pull/2869>)
- Ability of upload manifest for dataset with images (<https://github.com/openvinotoolkit/cvat/pull/2763>)
- 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 (<https://github.com/openvinotoolkit/cvat/pull/3017>)
- [ICDAR](https://rrc.cvc.uab.es/?ch=2) format support (<https://github.com/openvinotoolkit/cvat/pull/2866>)

### Changed
Expand Down
2 changes: 1 addition & 1 deletion cvat-ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cvat-ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cvat-ui",
"version": "1.16.1",
"version": "1.17.0",
"description": "CVAT single-page application",
"main": "src/index.tsx",
"scripts": {
Expand Down
14 changes: 12 additions & 2 deletions cvat-ui/src/actions/settings-actions.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// 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',
Expand Down Expand Up @@ -31,6 +31,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 {
Expand Down Expand Up @@ -258,3 +259,12 @@ export function switchSettingsDialog(show?: boolean): AnyAction {
},
};
}

export function setSettings(settings: Partial<SettingsState>): AnyAction {
return {
type: SettingsActionTypes.SET_SETTINGS,
payload: {
settings,
},
};
}
62 changes: 56 additions & 6 deletions cvat-ui/src/components/header/settings-modal/settings-modal.tsx
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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',
});
}
Comment on lines +46 to +64
Copy link
Member

@bsekachev bsekachev Mar 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not to do it in defaultState for settings reducer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First of all, code for saving and restoring settings is placed in the same place. Secondly, we will try to load settings after first render, so it's a very small time gain for the first render, i guess. And it will be easier to modify this solution for server side config loading, for example.

}, []);

return (
<Modal
title='Settings'
Expand All @@ -29,9 +72,16 @@ const SettingsModal = (props: SettingsModalProps): JSX.Element => {
width={800}
className='cvat-settings-modal'
footer={(
<Button type='primary' onClick={onClose}>
Close
</Button>
<>
<Tooltip title='Will save settings from this page and appearance settings on standard workspace page in browser'>
<Button type='primary' onClick={onSaveSettings}>
Save
</Button>
</Tooltip>
<Button type='default' onClick={onClose}>
Close
</Button>
Comment on lines +76 to +83
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to update dock. Do we need button close? We have "X" and also click everywhere out of the modal closes the modal

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's default behaviour for the modal element.

</>
)}
>
<div className='cvat-settings-tabs'>
Expand Down
16 changes: 12 additions & 4 deletions cvat-ui/src/reducers/settings-reducer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

Expand All @@ -9,7 +9,9 @@ import { AuthActionTypes } from 'actions/auth-actions';
import { SettingsActionTypes } from 'actions/settings-actions';
import { AnnotationActionTypes } from 'actions/annotation-actions';

import { SettingsState, GridColor, FrameSpeed, ColorBy } from './interfaces';
import {
SettingsState, GridColor, FrameSpeed, ColorBy,
} from './interfaces';

const defaultState: SettingsState = {
shapes: {
Expand Down Expand Up @@ -271,14 +273,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',
},
};
Expand Down
24 changes: 12 additions & 12 deletions cvat/apps/documentation/user_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <br>(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 <br>(indicated on the objects sidebar)|
| `Attributes`| some other fields including attributes with a <br>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 <br>(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 <br>(indicated on the objects sidebar) |
| `Attributes` | some other fields including attributes with a <br>similar type or a specific attribute value | any fields specified by a label |

**Supported operators for properties:**

Expand Down Expand Up @@ -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.
Expand Down