diff --git a/changelog.d/20240731_121418_klakhov_fix_issue_tool.md b/changelog.d/20240731_121418_klakhov_fix_issue_tool.md new file mode 100644 index 00000000000..7b67364325a --- /dev/null +++ b/changelog.d/20240731_121418_klakhov_fix_issue_tool.md @@ -0,0 +1,4 @@ +### Fixed + +- Issue tool was not reset after creating new issue + () diff --git a/cvat-canvas/src/typescript/canvasView.ts b/cvat-canvas/src/typescript/canvasView.ts index 8dc884f7525..8cca2967cd4 100644 --- a/cvat-canvas/src/typescript/canvasView.ts +++ b/cvat-canvas/src/typescript/canvasView.ts @@ -1875,6 +1875,7 @@ export class CanvasViewImpl implements CanvasView, Listener { this.canvas.style.cursor = 'pointer'; } else { this.regionSelector.select(false); + this.canvas.style.cursor = ''; } } else if (reason === UpdateReasons.DRAG_CANVAS) { if (this.mode === Mode.DRAG_CANVAS) { diff --git a/cvat-ui/src/actions/review-actions.ts b/cvat-ui/src/actions/review-actions.ts index 4db2dc323d3..2d8ee2a26ea 100644 --- a/cvat-ui/src/actions/review-actions.ts +++ b/cvat-ui/src/actions/review-actions.ts @@ -1,9 +1,11 @@ // Copyright (C) 2020-2022 Intel Corporation +// Copyright (C) 2024 CVAT.ai Corporation // // SPDX-License-Identifier: MIT import { ActionUnion, createAction, ThunkAction } from 'utils/redux'; import { getCore } from 'cvat-core-wrapper'; +import { NewIssueSource } from 'reducers'; const cvat = getCore(); @@ -33,8 +35,8 @@ export enum ReviewActionTypes { export const reviewActions = { createIssue: () => createAction(ReviewActionTypes.CREATE_ISSUE, {}), - startIssue: (position: number[]) => ( - createAction(ReviewActionTypes.START_ISSUE, { position: cvat.classes.Issue.hull(position) }) + startIssue: (position: number[], source = NewIssueSource.ISSUE_TOOL) => ( + createAction(ReviewActionTypes.START_ISSUE, { position: cvat.classes.Issue.hull(position), source }) ), finishIssueSuccess: (frame: number, issue: any) => ( createAction(ReviewActionTypes.FINISH_ISSUE_SUCCESS, { frame, issue }) @@ -78,7 +80,11 @@ export const finishIssueAsync = (message: string): ThunkAction => async (dispatc instance: jobInstance, }, }, - review: { newIssuePosition }, + review: { + newIssue: { + position: newIssuePosition, + }, + }, } = state; try { diff --git a/cvat-ui/src/components/annotation-page/canvas/views/canvas2d/canvas-wrapper.tsx b/cvat-ui/src/components/annotation-page/canvas/views/canvas2d/canvas-wrapper.tsx index e60aabf64ab..eeea359be55 100644 --- a/cvat-ui/src/components/annotation-page/canvas/views/canvas2d/canvas-wrapper.tsx +++ b/cvat-ui/src/components/annotation-page/canvas/views/canvas2d/canvas-wrapper.tsx @@ -726,10 +726,9 @@ class CanvasWrapperComponent extends React.PureComponent { }; private onCanvasPositionSelected = (event: any): void => { - const { onResetCanvas, onStartIssue } = this.props; + const { onStartIssue } = this.props; const { points } = event.detail; onStartIssue(points); - onResetCanvas(); }; private onCanvasMouseDown = (e: MouseEvent): void => { diff --git a/cvat-ui/src/components/annotation-page/review/create-issue-dialog.tsx b/cvat-ui/src/components/annotation-page/review/create-issue-dialog.tsx index 0ffc7f7cee4..c8dfda0872a 100644 --- a/cvat-ui/src/components/annotation-page/review/create-issue-dialog.tsx +++ b/cvat-ui/src/components/annotation-page/review/create-issue-dialog.tsx @@ -77,6 +77,7 @@ interface Props { left: number; angle: number; scale: number; + onCreateIssue: () => void; } export default function CreateIssueDialog(props: Props): ReactPortal { @@ -84,7 +85,7 @@ export default function CreateIssueDialog(props: Props): ReactPortal { const isMounted = useIsMounted(); const dispatch = useDispatch(); const { - top, left, angle, scale, + top, left, angle, scale, onCreateIssue, } = props; return ReactDOM.createPortal( @@ -100,6 +101,7 @@ export default function CreateIssueDialog(props: Props): ReactPortal { if (isMounted()) { setFetching(false); } + onCreateIssue(); }); }} cancel={() => { diff --git a/cvat-ui/src/components/annotation-page/review/issues-aggregator.tsx b/cvat-ui/src/components/annotation-page/review/issues-aggregator.tsx index 6cc005387e3..c66dcb35edb 100644 --- a/cvat-ui/src/components/annotation-page/review/issues-aggregator.tsx +++ b/cvat-ui/src/components/annotation-page/review/issues-aggregator.tsx @@ -7,15 +7,15 @@ import './styles.scss'; import React, { useState, useEffect, useCallback } from 'react'; import { useSelector, useDispatch, shallowEqual } from 'react-redux'; -import { ActiveControl, CombinedState } from 'reducers'; -import { Canvas } from 'cvat-canvas/src/typescript/canvas'; +import { ActiveControl, CombinedState, NewIssueSource } from 'reducers'; import { commentIssueAsync, resolveIssueAsync, reopenIssueAsync } from 'actions/review-actions'; import { AnnotationConflict, ConflictSeverity, ObjectState, QualityConflict, } from 'cvat-core-wrapper'; +import { Canvas, CanvasMode } from 'cvat-canvas-wrapper'; -import { highlightConflict } from 'actions/annotation-actions'; +import { highlightConflict, updateActiveControl } from 'actions/annotation-actions'; import CreateIssueDialog from './create-issue-dialog'; import HiddenIssueLabel from './hidden-issue-label'; import IssueDialog from './issue-dialog'; @@ -41,6 +41,7 @@ export default function IssueAggregatorComponent(): JSX.Element | null { canvasIsReady, annotationsZLayer, newIssuePosition, + newIssueSource, issueFetching, qualityConflicts, objectStates, @@ -54,7 +55,8 @@ export default function IssueAggregatorComponent(): JSX.Element | null { canvasInstance: state.annotation.canvas.instance, canvasIsReady: state.annotation.canvas.ready, annotationsZLayer: state.annotation.annotations.zLayer.cur, - newIssuePosition: state.review.newIssuePosition, + newIssuePosition: state.review.newIssue.position, + newIssueSource: state.review.newIssue.source, issueFetching: state.review.fetching.issueId, qualityConflicts: state.review.frameConflicts, objectStates: state.annotation.annotations.states, @@ -88,6 +90,13 @@ export default function IssueAggregatorComponent(): JSX.Element | null { const issueDialogs: JSX.Element[] = []; const conflictLabels: JSX.Element[] = []; + const onCreateIssue = useCallback(() => { + if (canvasReady && canvasInstance.mode() === CanvasMode.SELECT_REGION) { + canvasInstance.selectRegion(false); + dispatch(updateActiveControl(ActiveControl.CURSOR)); + } + }, [canvasReady, canvasInstance]); + useEffect(() => { if (canvasReady) { const { geometry: updatedGeometry } = canvasInstance; @@ -284,12 +293,13 @@ export default function IssueAggregatorComponent(): JSX.Element | null { return ( <> - {createLeft !== null && createTop !== null ? ( + {newIssueSource === NewIssueSource.ISSUE_TOOL && createLeft !== null && createTop !== null ? ( ) : null} {issueDialogs} diff --git a/cvat-ui/src/containers/annotation-page/canvas/canvas-context-menu.tsx b/cvat-ui/src/containers/annotation-page/canvas/canvas-context-menu.tsx index 0e400a1adb8..a5e32815028 100644 --- a/cvat-ui/src/containers/annotation-page/canvas/canvas-context-menu.tsx +++ b/cvat-ui/src/containers/annotation-page/canvas/canvas-context-menu.tsx @@ -1,5 +1,5 @@ // Copyright (C) 2020-2022 Intel Corporation -// Copyright (C) 2022-2023 CVAT.ai Corporation +// Copyright (C) 2022-2024 CVAT.ai Corporation // // SPDX-License-Identifier: MIT @@ -8,7 +8,7 @@ import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { - CombinedState, ContextMenuType, ShapeType, Workspace, + CombinedState, ContextMenuType, NewIssueSource, ShapeType, Workspace, } from 'reducers'; import CanvasContextMenuComponent from 'components/annotation-page/canvas/views/canvas2d/canvas-context-menu'; @@ -106,7 +106,7 @@ function mapDispatchToProps(dispatch: ThunkDispatch): DispatchToProps { dispatch(updateCanvasContextMenu(false, 0, 0)); }, openIssue(position: number[], message: string): void { - dispatch(reviewActions.startIssue(position)); + dispatch(reviewActions.startIssue(position, NewIssueSource.QUICK_ISSUE)); dispatch(finishIssueAsync(message)); dispatch(updateCanvasContextMenu(false, 0, 0)); }, diff --git a/cvat-ui/src/reducers/index.ts b/cvat-ui/src/reducers/index.ts index 853e2a735e2..ac92a6229f6 100644 --- a/cvat-ui/src/reducers/index.ts +++ b/cvat-ui/src/reducers/index.ts @@ -891,11 +891,19 @@ export enum ReviewStatus { REVIEW_FURTHER = 'review_further', } +export enum NewIssueSource { + ISSUE_TOOL = 'tool', + QUICK_ISSUE = 'quick_issue', +} + export interface ReviewState { issues: any[]; frameIssues: any[]; latestComments: string[]; - newIssuePosition: number[] | null; + newIssue: { + position: number[] | null; + source: NewIssueSource | null; + } issuesHidden: boolean; issuesResolvedHidden: boolean; conflicts: QualityConflict[]; diff --git a/cvat-ui/src/reducers/review-reducer.ts b/cvat-ui/src/reducers/review-reducer.ts index ac0d3ecc09a..2eef47f8bc6 100644 --- a/cvat-ui/src/reducers/review-reducer.ts +++ b/cvat-ui/src/reducers/review-reducer.ts @@ -1,4 +1,5 @@ // Copyright (C) 2020-2022 Intel Corporation +// Copyright (C) 2024 CVAT.ai Corporation // // SPDX-License-Identifier: MIT @@ -12,10 +13,13 @@ import { ReviewState } from '.'; const defaultState: ReviewState = { issues: [], latestComments: [], - frameIssues: [], // saved on the server and not saved on the server + frameIssues: [], conflicts: [], frameConflicts: [], - newIssuePosition: null, + newIssue: { + position: null, + source: null, + }, issuesHidden: false, issuesResolvedHidden: false, fetching: { @@ -43,16 +47,15 @@ export default function (state: ReviewState = defaultState, action: any): Review frameConflicts, }; } - case AnnotationActionTypes.CHANGE_FRAME: { - return { - ...state, - newIssuePosition: null, - }; - } + case AnnotationActionTypes.CHANGE_FRAME: + case ReviewActionTypes.CANCEL_ISSUE: case AnnotationActionTypes.DELETE_FRAME_SUCCESS: { return { ...state, - newIssuePosition: null, + newIssue: { + position: null, + source: null, + }, }; } case ReviewActionTypes.SUBMIT_REVIEW: { @@ -65,15 +68,7 @@ export default function (state: ReviewState = defaultState, action: any): Review }, }; } - case ReviewActionTypes.SUBMIT_REVIEW_SUCCESS: { - return { - ...state, - fetching: { - ...state.fetching, - jobId: null, - }, - }; - } + case ReviewActionTypes.SUBMIT_REVIEW_SUCCESS: case ReviewActionTypes.SUBMIT_REVIEW_FAILED: { return { ...state, @@ -97,10 +92,13 @@ export default function (state: ReviewState = defaultState, action: any): Review }; } case ReviewActionTypes.START_ISSUE: { - const { position } = action.payload; + const { position, source } = action.payload; return { ...state, - newIssuePosition: position, + newIssue: { + position, + source, + }, }; } case ReviewActionTypes.FINISH_ISSUE_SUCCESS: { @@ -124,13 +122,10 @@ export default function (state: ReviewState = defaultState, action: any): Review ).slice(-config.LATEST_COMMENTS_SHOWN_QUICK_ISSUE), frameIssues, issues, - newIssuePosition: null, - }; - } - case ReviewActionTypes.CANCEL_ISSUE: { - return { - ...state, - newIssuePosition: null, + newIssue: { + position: null, + source: null, + }, }; } case ReviewActionTypes.COMMENT_ISSUE: @@ -202,6 +197,4 @@ export default function (state: ReviewState = defaultState, action: any): Review default: return state; } - - return state; }