diff --git a/CHANGELOG.md b/CHANGELOG.md index ef041139..b599a0d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Implemented import and export of annotations with relative image paths () - Using only single click to start editing or remove a point () - Added support for attributes in VOC XML format (https://github.com/opencv/cvat/pull/1792) +- Colorized object items in the side panel () ### Deprecated - @@ -54,6 +55,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - A couple of exceptions in AAM related with early object activation () - Propagation from the latest frame () - Number attribute value validation (didn't work well with floats) () +- Annotations aren't updated after reopening a task () +- Labels aren't updated after reopening a task () +- Canvas isn't fitted after collapsing side panel in attribute annotation mode () ### Security diff --git a/cvat-core/package.json b/cvat-core/package.json index 29bc84cd..dbff47de 100644 --- a/cvat-core/package.json +++ b/cvat-core/package.json @@ -1,6 +1,6 @@ { "name": "cvat-core", - "version": "3.0.1", + "version": "3.1.0", "description": "Part of Computer Vision Tool which presents an interface for client-side integration", "main": "babel.config.js", "scripts": { diff --git a/cvat-core/src/annotations.js b/cvat-core/src/annotations.js index e2f452a7..9d7eadbf 100644 --- a/cvat-core/src/annotations.js +++ b/cvat-core/src/annotations.js @@ -77,6 +77,15 @@ } } + async function closeSession(session) { + const sessionType = session instanceof Task ? 'task' : 'job'; + const cache = getCache(sessionType); + + if (cache.has(session)) { + cache.delete(session); + } + } + async function getAnnotations(session, frame, allTracks, filters) { const sessionType = session instanceof Task ? 'task' : 'job'; const cache = getCache(sessionType); @@ -365,5 +374,6 @@ redoActions, clearActions, getActions, + closeSession, }; })(); diff --git a/cvat-core/src/session.js b/cvat-core/src/session.js index 2af76d13..5d5bd7b2 100644 --- a/cvat-core/src/session.js +++ b/cvat-core/src/session.js @@ -1309,6 +1309,21 @@ }; } + /** + * Method removes all task related data from the client (annotations, history, etc.) + * @method close + * @returns {module:API.cvat.classes.Task} + * @memberof module:API.cvat.classes.Task + * @readonly + * @async + * @instance + * @throws {module:API.cvat.exceptions.PluginError} + */ + async close() { + const result = await PluginRegistry.apiWrapper.call(this, Task.prototype.close); + return result; + } + /** * Method updates data of a created task or creates new task from scratch * @method save @@ -1372,6 +1387,7 @@ redoActions, clearActions, getActions, + closeSession, } = require('./annotations'); buildDublicatedAPI(Job.prototype); @@ -1576,6 +1592,15 @@ return result; }; + Task.prototype.close.implementation = function closeTask() { + for (const job of this.jobs) { + closeSession(job); + } + + closeSession(this); + return this; + }; + Task.prototype.save.implementation = async function saveTaskImplementation(onUpdate) { // TODO: Add ability to change an owner and an assignee if (typeof (this.id) !== 'undefined') { diff --git a/cvat-ui/package-lock.json b/cvat-ui/package-lock.json index c5330cf4..8f0c1722 100644 --- a/cvat-ui/package-lock.json +++ b/cvat-ui/package-lock.json @@ -1,6 +1,6 @@ { "name": "cvat-ui", - "version": "1.4.1", + "version": "1.4.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/cvat-ui/package.json b/cvat-ui/package.json index 466901f0..e965092c 100644 --- a/cvat-ui/package.json +++ b/cvat-ui/package.json @@ -1,6 +1,6 @@ { "name": "cvat-ui", - "version": "1.4.1", + "version": "1.4.2", "description": "CVAT single-page application", "main": "src/index.tsx", "scripts": { diff --git a/cvat-ui/src/actions/annotation-actions.ts b/cvat-ui/src/actions/annotation-actions.ts index b669cabf..c373486a 100644 --- a/cvat-ui/src/actions/annotation-actions.ts +++ b/cvat-ui/src/actions/annotation-actions.ts @@ -906,6 +906,19 @@ export function confirmCanvasReady(): AnyAction { }; } +export function closeJob(): ThunkAction, {}, {}, AnyAction> { + return async (dispatch: ActionCreator): Promise => { + const { jobInstance } = receiveAnnotationsParameters(); + if (jobInstance) { + await jobInstance.task.close(); + } + + dispatch({ + type: AnnotationActionTypes.CLOSE_JOB, + }); + }; +} + export function getJobAsync( tid: number, jid: number, @@ -918,13 +931,6 @@ export function getJobAsync( const filters = initialFilters; const { showAllInterpolationTracks } = state.settings.workspace; - // Check if already loaded job is different from asking one - if (state.annotation.job.instance && state.annotation.job.instance.id !== jid) { - dispatch({ - type: AnnotationActionTypes.CLOSE_JOB, - }); - } - dispatch({ type: AnnotationActionTypes.GET_JOB, payload: { diff --git a/cvat-ui/src/base.scss b/cvat-ui/src/base.scss index eb7119ef..75b561ca 100644 --- a/cvat-ui/src/base.scss +++ b/cvat-ui/src/base.scss @@ -21,7 +21,8 @@ $danger-icon-color: #ff4136; $info-icon-color: #0074d9; $objects-bar-tabs-color: #bebebe; $objects-bar-icons-color: #242424; // #6e6e6e -$active-object-item-background-color: #d8ecff; +$active-label-background-color: #d8ecff; +$object-item-border-color: #000; $slider-color: #1890ff; $monospaced-fonts-stack: Consolas, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace; diff --git a/cvat-ui/src/components/annotation-page/attribute-annotation-workspace/attribute-annotation-sidebar/attribute-annotation-sidebar.tsx b/cvat-ui/src/components/annotation-page/attribute-annotation-workspace/attribute-annotation-sidebar/attribute-annotation-sidebar.tsx index 75ed16cb..51ff9b77 100644 --- a/cvat-ui/src/components/annotation-page/attribute-annotation-workspace/attribute-annotation-sidebar/attribute-annotation-sidebar.tsx +++ b/cvat-ui/src/components/annotation-page/attribute-annotation-workspace/attribute-annotation-sidebar/attribute-annotation-sidebar.tsx @@ -14,6 +14,7 @@ import { Row, Col } from 'antd/lib/grid'; import Text from 'antd/lib/typography/Text'; import Icon from 'antd/lib/icon'; +import { Canvas } from 'cvat-canvas-wrapper'; import { LogType } from 'cvat-logger'; import { activateObject as activateObjectAction, @@ -35,6 +36,7 @@ interface StateToProps { jobInstance: any; keyMap: Record; normalizedKeyMap: Record; + canvasInstance: Canvas; canvasIsReady: boolean; } @@ -60,6 +62,7 @@ function mapStateToProps(state: CombinedState): StateToProps { labels, }, canvas: { + instance: canvasInstance, ready: canvasIsReady, }, }, @@ -77,6 +80,7 @@ function mapStateToProps(state: CombinedState): StateToProps { states, keyMap, normalizedKeyMap, + canvasInstance, canvasIsReady, }; } @@ -103,6 +107,7 @@ function AttributeAnnotationSidebar(props: StateToProps & DispatchToProps): JSX. activateObject, keyMap, normalizedKeyMap, + canvasInstance, canvasIsReady, } = props; @@ -115,6 +120,19 @@ function AttributeAnnotationSidebar(props: StateToProps & DispatchToProps): JSX. const [sidebarCollapsed, setSidebarCollapsed] = useState(false); + const collapse = (): void => { + const [collapser] = window.document + .getElementsByClassName('attribute-annotation-sidebar'); + + if (collapser) { + collapser.addEventListener('transitionend', () => { + canvasInstance.fitCanvas(); + }, { once: true }); + } + + setSidebarCollapsed(!sidebarCollapsed); + }; + const [activeObjectState] = activatedStateID === null ? [null] : states.filter((objectState: any): boolean => ( objectState.clientID === activatedStateID @@ -235,7 +253,7 @@ function AttributeAnnotationSidebar(props: StateToProps & DispatchToProps): JSX. className={`cvat-objects-sidebar-sider ant-layout-sider-zero-width-trigger ant-layout-sider-zero-width-trigger-left`} - onClick={() => setSidebarCollapsed(!sidebarCollapsed)} + onClick={collapse} > {sidebarCollapsed ? : } diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx index ddcbf7a1..cd982fe0 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx @@ -858,7 +858,7 @@ function ObjectItemComponent(props: Props): JSX.Element { : 'cvat-objects-sidebar-state-item cvat-objects-sidebar-state-active-item'; return ( -
+
div:nth-child(1) { > div:nth-child(1) { @@ -218,7 +222,7 @@ } .cvat-objects-sidebar-label-active-item { - background: $active-object-item-background-color; + background: $active-label-background-color; } .cvat-objects-sidebar-label-item { diff --git a/cvat-ui/src/components/header/settings-modal/styles.scss b/cvat-ui/src/components/header/settings-modal/styles.scss index bcc778af..f0d23d20 100644 --- a/cvat-ui/src/components/header/settings-modal/styles.scss +++ b/cvat-ui/src/components/header/settings-modal/styles.scss @@ -111,6 +111,6 @@ } } -.cvat-settings-modal .ant-modal-body{ +.cvat-settings-modal .ant-modal-body { padding-top: 0; -} \ No newline at end of file +} diff --git a/cvat-ui/src/containers/annotation-page/top-bar/top-bar.tsx b/cvat-ui/src/containers/annotation-page/top-bar/top-bar.tsx index 8c75eeff..83c70521 100644 --- a/cvat-ui/src/containers/annotation-page/top-bar/top-bar.tsx +++ b/cvat-ui/src/containers/annotation-page/top-bar/top-bar.tsx @@ -22,6 +22,7 @@ import { searchAnnotationsAsync, changeWorkspace as changeWorkspaceAction, activateObject, + closeJob as closeJobAction, } from 'actions/annotation-actions'; import { Canvas } from 'cvat-canvas-wrapper'; @@ -58,6 +59,7 @@ interface DispatchToProps { redo(sessionInstance: any, frameNumber: any): void; searchAnnotations(sessionInstance: any, frameFrom: any, frameTo: any): void; changeWorkspace(workspace: Workspace): void; + closeJob(): void; } function mapStateToProps(state: CombinedState): StateToProps { @@ -153,6 +155,9 @@ function mapDispatchToProps(dispatch: any): DispatchToProps { dispatch(activateObject(null, null)); dispatch(changeWorkspaceAction(workspace)); }, + closeJob(): void { + dispatch(closeJobAction()); + }, }; } @@ -177,13 +182,16 @@ class AnnotationTopBarContainer extends React.PureComponent { this.autoSaveInterval = window.setInterval(this.autoSave.bind(this), autoSaveInterval); this.unblock = history.block((location: any) => { - if (jobInstance.annotations.hasUnsavedChanges() && location.pathname !== '/settings' - && location.pathname !== `/tasks/${jobInstance.task.id}/jobs/${jobInstance.id}`) { + const { task, id: jobID } = jobInstance; + const { id: taskID } = task; + + if (jobInstance.annotations.hasUnsavedChanges() + && location.pathname !== `/tasks/${taskID}/jobs/${jobID}`) { return 'You have unsaved changes, please confirm leaving this page.'; } return undefined; }); - this.beforeUnloadCallback = this.beforeUnloadCallback.bind(this); + window.addEventListener('beforeunload', this.beforeUnloadCallback); } @@ -238,9 +246,11 @@ class AnnotationTopBarContainer extends React.PureComponent { } public componentWillUnmount(): void { + const { closeJob } = this.props; window.clearInterval(this.autoSaveInterval); window.removeEventListener('beforeunload', this.beforeUnloadCallback); this.unblock(); + closeJob(); } private undo = (): void => { @@ -443,6 +453,17 @@ class AnnotationTopBarContainer extends React.PureComponent { copy(url); }; + private beforeUnloadCallback = (event: BeforeUnloadEvent): string | undefined => { + const { jobInstance } = this.props; + if (jobInstance.annotations.hasUnsavedChanges()) { + const confirmationMessage = 'You have unsaved changes, please confirm leaving this page.'; + // eslint-disable-next-line no-param-reassign + event.returnValue = confirmationMessage; + return confirmationMessage; + } + return undefined; + }; + private autoSave(): void { const { autoSave, saving } = this.props; @@ -458,16 +479,6 @@ class AnnotationTopBarContainer extends React.PureComponent { } } - private beforeUnloadCallback(event: BeforeUnloadEvent): any { - const { jobInstance } = this.props; - if (jobInstance.annotations.hasUnsavedChanges()) { - const confirmationMessage = 'You have unsaved changes, please confirm leaving this page.'; - // eslint-disable-next-line no-param-reassign - event.returnValue = confirmationMessage; - return confirmationMessage; - } - return undefined; - } public render(): JSX.Element { const {