From 6a779a79fc2685330ea193317f0b0e8f7b31eb01 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Mon, 7 Dec 2020 11:55:59 +0300 Subject: [PATCH 01/11] Login with next, alternative implementation --- cvat-ui/src/actions/auth-actions.ts | 8 +++++--- cvat-ui/src/components/cvat-app.tsx | 12 +++++++++--- cvat-ui/src/components/login-page/login-page.tsx | 11 ++++++++--- .../components/login-with-token/login-with-token.tsx | 8 +++++--- cvat-ui/src/index.tsx | 2 ++ cvat-ui/src/reducers/auth-reducer.ts | 8 +++++--- cvat-ui/src/reducers/interfaces.ts | 1 + 7 files changed, 35 insertions(+), 15 deletions(-) diff --git a/cvat-ui/src/actions/auth-actions.ts b/cvat-ui/src/actions/auth-actions.ts index 9db70b5a..0ea9a937 100644 --- a/cvat-ui/src/actions/auth-actions.ts +++ b/cvat-ui/src/actions/auth-actions.ts @@ -40,7 +40,7 @@ export const authActions = { authorizeSuccess: (user: any) => createAction(AuthActionTypes.AUTHORIZED_SUCCESS, { user }), authorizeFailed: (error: any) => createAction(AuthActionTypes.AUTHORIZED_FAILED, { error }), login: () => createAction(AuthActionTypes.LOGIN), - loginSuccess: (user: any) => createAction(AuthActionTypes.LOGIN_SUCCESS, { user }), + loginSuccess: (user: any, next: string | null) => createAction(AuthActionTypes.LOGIN_SUCCESS, { user, next }), loginFailed: (error: any) => createAction(AuthActionTypes.LOGIN_FAILED, { error }), register: () => createAction(AuthActionTypes.REGISTER), registerSuccess: (user: any) => createAction(AuthActionTypes.REGISTER_SUCCESS, { user }), @@ -98,14 +98,16 @@ export const registerAsync = ( } }; -export const loginAsync = (username: string, password: string): ThunkAction => async (dispatch) => { +export const loginAsync = (username: string, password: string, next: string | null): ThunkAction => async ( + dispatch, +) => { dispatch(authActions.login()); try { await cvat.server.login(username, password); const users = await cvat.users.get({ self: true }); - dispatch(authActions.loginSuccess(users[0])); + dispatch(authActions.loginSuccess(users[0], next)); } catch (error) { dispatch(authActions.loginFailed(error)); } diff --git a/cvat-ui/src/components/cvat-app.tsx b/cvat-ui/src/components/cvat-app.tsx index b3c742cd..cfefaa7d 100644 --- a/cvat-ui/src/components/cvat-app.tsx +++ b/cvat-ui/src/components/cvat-app.tsx @@ -64,6 +64,7 @@ interface CVATAppProps { authActionsInitialized: boolean; notifications: NotificationsState; user: any; + next: string | null; isModelPluginActive: boolean; } @@ -231,16 +232,19 @@ class CVATApplication extends React.PureComponent )} - + {/* eslint-disable-next-line */} @@ -337,7 +341,9 @@ class CVATApplication extends React.PureComponent - + 1 ? `/auth/login/?next=${location.pathname}` : '/auth/login'} + /> ); diff --git a/cvat-ui/src/components/login-page/login-page.tsx b/cvat-ui/src/components/login-page/login-page.tsx index f252606f..9e00e841 100644 --- a/cvat-ui/src/components/login-page/login-page.tsx +++ b/cvat-ui/src/components/login-page/login-page.tsx @@ -15,7 +15,8 @@ import CookieDrawer from './cookie-policy-drawer'; interface LoginPageComponentProps { fetching: boolean; renderResetPassword: boolean; - onLogin: (username: string, password: string) => void; + next: string; + onLogin: (username: string, password: string, next: string | null) => void; } function LoginPageComponent(props: LoginPageComponentProps & RouteComponentProps): JSX.Element { @@ -27,7 +28,11 @@ function LoginPageComponent(props: LoginPageComponentProps & RouteComponentProps xl: { span: 4 }, }; - const { fetching, onLogin, renderResetPassword } = props; + const { + fetching, onLogin, renderResetPassword, location, + } = props; + + const search = new URLSearchParams(location.search); return ( <> @@ -37,7 +42,7 @@ function LoginPageComponent(props: LoginPageComponentProps & RouteComponentProps { - onLogin(loginData.username, loginData.password); + onLogin(loginData.username, loginData.password, search.get('next')); }} /> diff --git a/cvat-ui/src/components/login-with-token/login-with-token.tsx b/cvat-ui/src/components/login-with-token/login-with-token.tsx index cadb0542..b40ca03a 100644 --- a/cvat-ui/src/components/login-with-token/login-with-token.tsx +++ b/cvat-ui/src/components/login-with-token/login-with-token.tsx @@ -3,15 +3,17 @@ // SPDX-License-Identifier: MIT import React, { useEffect } from 'react'; -import { Redirect, useParams } from 'react-router'; +import { Redirect, useParams, useLocation } from 'react-router'; import { useCookies } from 'react-cookie'; export default function LoginWithTokenComponent(): JSX.Element { - const { sessionId, token } = useParams(); + const location = useLocation(); + const { sessionId, token } = useParams<{ sessionId: string; token: string }>(); const [cookies, setCookie] = useCookies(['sessionid', 'csrftoken']); const expires1y = new Date(new Date().setFullYear(new Date().getFullYear() + 1)); const expires2w = new Date(new Date().setDate(new Date().getDate() + 13)); + const search = new URLSearchParams(location.search); setCookie('sessionid', sessionId, { path: '/', expires: expires2w }); setCookie('csrftoken', token, { path: '/', expires: expires1y }); @@ -24,7 +26,7 @@ export default function LoginWithTokenComponent(): JSX.Element { ); if (cookies.sessionid && cookies.csrftoken) { - return ; + return ; } return <>; } diff --git a/cvat-ui/src/index.tsx b/cvat-ui/src/index.tsx index 31188f77..1d527605 100644 --- a/cvat-ui/src/index.tsx +++ b/cvat-ui/src/index.tsx @@ -45,6 +45,7 @@ interface StateToProps { allowResetPassword: boolean; notifications: NotificationsState; user: any; + next: string | null; keyMap: Record; isModelPluginActive: boolean; } @@ -91,6 +92,7 @@ function mapStateToProps(state: CombinedState): StateToProps { allowResetPassword: auth.allowResetPassword, notifications: state.notifications, user: auth.user, + next: auth.next, keyMap: shortcuts.keyMap, isModelPluginActive: plugins.list.MODELS, }; diff --git a/cvat-ui/src/reducers/auth-reducer.ts b/cvat-ui/src/reducers/auth-reducer.ts index eac8449b..2e226c20 100644 --- a/cvat-ui/src/reducers/auth-reducer.ts +++ b/cvat-ui/src/reducers/auth-reducer.ts @@ -10,6 +10,7 @@ const defaultState: AuthState = { initialized: false, fetching: false, user: null, + next: null, authActionsFetching: false, authActionsInitialized: false, allowChangePassword: false, @@ -40,6 +41,7 @@ export default function (state = defaultState, action: AuthActions | BoundariesA ...state, fetching: false, user: action.payload.user, + next: action.payload.next, }; case AuthActionTypes.LOGIN_FAILED: return { @@ -94,9 +96,9 @@ export default function (state = defaultState, action: AuthActions | BoundariesA return { ...state, showChangePasswordDialog: - typeof action.payload.showChangePasswordDialog === 'undefined' - ? !state.showChangePasswordDialog - : action.payload.showChangePasswordDialog, + typeof action.payload.showChangePasswordDialog === 'undefined' ? + !state.showChangePasswordDialog : + action.payload.showChangePasswordDialog, }; case AuthActionTypes.REQUEST_PASSWORD_RESET: return { diff --git a/cvat-ui/src/reducers/interfaces.ts b/cvat-ui/src/reducers/interfaces.ts index 088be88d..8a518d68 100644 --- a/cvat-ui/src/reducers/interfaces.ts +++ b/cvat-ui/src/reducers/interfaces.ts @@ -14,6 +14,7 @@ export interface AuthState { initialized: boolean; fetching: boolean; user: any; + next: string | null; authActionsFetching: boolean; authActionsInitialized: boolean; showChangePasswordDialog: boolean; From d296f91a2cd6535056ddd11662465838b7acfa07 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Mon, 7 Dec 2020 12:32:22 +0300 Subject: [PATCH 02/11] Updated version & changelog --- CHANGELOG.md | 1 + cvat-ui/package-lock.json | 2 +- cvat-ui/package.json | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbfb1374..205b7657 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added documentation on how to mount cloud starage(AWS S3 bucket, Azure container, Google Drive) as FUSE () - Added ability to work with share files without copying inside () - Tooltips in label selectors () +- Page redirect after login using `next` query parameter () ### Changed diff --git a/cvat-ui/package-lock.json b/cvat-ui/package-lock.json index 45c2f0f4..866cc47f 100644 --- a/cvat-ui/package-lock.json +++ b/cvat-ui/package-lock.json @@ -1,6 +1,6 @@ { "name": "cvat-ui", - "version": "1.11.5", + "version": "1.12.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/cvat-ui/package.json b/cvat-ui/package.json index b3f718c5..b4e9637f 100644 --- a/cvat-ui/package.json +++ b/cvat-ui/package.json @@ -1,6 +1,6 @@ { "name": "cvat-ui", - "version": "1.11.5", + "version": "1.12.0", "description": "CVAT single-page application", "main": "src/index.tsx", "scripts": { From e22dd019e370daf25e6f37c2dadd30f355784241 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Mon, 7 Dec 2020 12:36:55 +0300 Subject: [PATCH 03/11] Fixed models initialization --- cvat-ui/src/components/cvat-app.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cvat-ui/src/components/cvat-app.tsx b/cvat-ui/src/components/cvat-app.tsx index cfefaa7d..093f9ff4 100644 --- a/cvat-ui/src/components/cvat-app.tsx +++ b/cvat-ui/src/components/cvat-app.tsx @@ -244,7 +244,11 @@ class CVATApplication extends React.PureComponent Date: Mon, 7 Dec 2020 16:34:46 +0300 Subject: [PATCH 04/11] Fixed tests --- tests/cypress/support/commands.js | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index aa48a82f..b3d06fc8 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -23,6 +23,7 @@ Cypress.Commands.add('logout', (username = Cypress.env('user')) => { }); cy.get('.anticon-logout').click(); cy.url().should('include', '/auth/login'); + cy.visit('/auth/login'); // clear query parameter "next" }); Cypress.Commands.add('userRegistration', (firstName, lastName, userName, emailAddr, password) => { From ccaca4526de569742b8a8c3fe8c5e1d2f3dd4e11 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Tue, 8 Dec 2020 20:01:22 +0300 Subject: [PATCH 05/11] Fixed one more usecase, simplified the code --- cvat-ui/src/actions/auth-actions.ts | 8 +++----- cvat-ui/src/components/cvat-app.tsx | 7 ++++--- cvat-ui/src/components/login-page/login-page.tsx | 9 ++------- .../src/components/login-with-token/login-with-token.tsx | 2 +- cvat-ui/src/index.tsx | 2 -- cvat-ui/src/reducers/auth-reducer.ts | 2 -- cvat-ui/src/reducers/interfaces.ts | 1 - 7 files changed, 10 insertions(+), 21 deletions(-) diff --git a/cvat-ui/src/actions/auth-actions.ts b/cvat-ui/src/actions/auth-actions.ts index 0ea9a937..9db70b5a 100644 --- a/cvat-ui/src/actions/auth-actions.ts +++ b/cvat-ui/src/actions/auth-actions.ts @@ -40,7 +40,7 @@ export const authActions = { authorizeSuccess: (user: any) => createAction(AuthActionTypes.AUTHORIZED_SUCCESS, { user }), authorizeFailed: (error: any) => createAction(AuthActionTypes.AUTHORIZED_FAILED, { error }), login: () => createAction(AuthActionTypes.LOGIN), - loginSuccess: (user: any, next: string | null) => createAction(AuthActionTypes.LOGIN_SUCCESS, { user, next }), + loginSuccess: (user: any) => createAction(AuthActionTypes.LOGIN_SUCCESS, { user }), loginFailed: (error: any) => createAction(AuthActionTypes.LOGIN_FAILED, { error }), register: () => createAction(AuthActionTypes.REGISTER), registerSuccess: (user: any) => createAction(AuthActionTypes.REGISTER_SUCCESS, { user }), @@ -98,16 +98,14 @@ export const registerAsync = ( } }; -export const loginAsync = (username: string, password: string, next: string | null): ThunkAction => async ( - dispatch, -) => { +export const loginAsync = (username: string, password: string): ThunkAction => async (dispatch) => { dispatch(authActions.login()); try { await cvat.server.login(username, password); const users = await cvat.users.get({ self: true }); - dispatch(authActions.loginSuccess(users[0], next)); + dispatch(authActions.loginSuccess(users[0])); } catch (error) { dispatch(authActions.loginFailed(error)); } diff --git a/cvat-ui/src/components/cvat-app.tsx b/cvat-ui/src/components/cvat-app.tsx index 8220a603..a7c5079e 100644 --- a/cvat-ui/src/components/cvat-app.tsx +++ b/cvat-ui/src/components/cvat-app.tsx @@ -64,7 +64,6 @@ interface CVATAppProps { authActionsInitialized: boolean; notifications: NotificationsState; user: any; - next: string | null; isModelPluginActive: boolean; } @@ -238,7 +237,6 @@ class CVATApplication extends React.PureComponent )} - + {/* eslint-disable-next-line */} diff --git a/cvat-ui/src/components/login-page/login-page.tsx b/cvat-ui/src/components/login-page/login-page.tsx index 9e00e841..59f92cce 100644 --- a/cvat-ui/src/components/login-page/login-page.tsx +++ b/cvat-ui/src/components/login-page/login-page.tsx @@ -15,7 +15,6 @@ import CookieDrawer from './cookie-policy-drawer'; interface LoginPageComponentProps { fetching: boolean; renderResetPassword: boolean; - next: string; onLogin: (username: string, password: string, next: string | null) => void; } @@ -28,11 +27,7 @@ function LoginPageComponent(props: LoginPageComponentProps & RouteComponentProps xl: { span: 4 }, }; - const { - fetching, onLogin, renderResetPassword, location, - } = props; - - const search = new URLSearchParams(location.search); + const { fetching, onLogin, renderResetPassword } = props; return ( <> @@ -42,7 +37,7 @@ function LoginPageComponent(props: LoginPageComponentProps & RouteComponentProps { - onLogin(loginData.username, loginData.password, search.get('next')); + onLogin(loginData.username, loginData.password); }} /> diff --git a/cvat-ui/src/components/login-with-token/login-with-token.tsx b/cvat-ui/src/components/login-with-token/login-with-token.tsx index b40ca03a..6d896c0d 100644 --- a/cvat-ui/src/components/login-with-token/login-with-token.tsx +++ b/cvat-ui/src/components/login-with-token/login-with-token.tsx @@ -26,7 +26,7 @@ export default function LoginWithTokenComponent(): JSX.Element { ); if (cookies.sessionid && cookies.csrftoken) { - return ; + return ; } return <>; } diff --git a/cvat-ui/src/index.tsx b/cvat-ui/src/index.tsx index 1d527605..31188f77 100644 --- a/cvat-ui/src/index.tsx +++ b/cvat-ui/src/index.tsx @@ -45,7 +45,6 @@ interface StateToProps { allowResetPassword: boolean; notifications: NotificationsState; user: any; - next: string | null; keyMap: Record; isModelPluginActive: boolean; } @@ -92,7 +91,6 @@ function mapStateToProps(state: CombinedState): StateToProps { allowResetPassword: auth.allowResetPassword, notifications: state.notifications, user: auth.user, - next: auth.next, keyMap: shortcuts.keyMap, isModelPluginActive: plugins.list.MODELS, }; diff --git a/cvat-ui/src/reducers/auth-reducer.ts b/cvat-ui/src/reducers/auth-reducer.ts index 2e226c20..54af7c75 100644 --- a/cvat-ui/src/reducers/auth-reducer.ts +++ b/cvat-ui/src/reducers/auth-reducer.ts @@ -10,7 +10,6 @@ const defaultState: AuthState = { initialized: false, fetching: false, user: null, - next: null, authActionsFetching: false, authActionsInitialized: false, allowChangePassword: false, @@ -41,7 +40,6 @@ export default function (state = defaultState, action: AuthActions | BoundariesA ...state, fetching: false, user: action.payload.user, - next: action.payload.next, }; case AuthActionTypes.LOGIN_FAILED: return { diff --git a/cvat-ui/src/reducers/interfaces.ts b/cvat-ui/src/reducers/interfaces.ts index 89217eef..e1b215f9 100644 --- a/cvat-ui/src/reducers/interfaces.ts +++ b/cvat-ui/src/reducers/interfaces.ts @@ -14,7 +14,6 @@ export interface AuthState { initialized: boolean; fetching: boolean; user: any; - next: string | null; authActionsFetching: boolean; authActionsInitialized: boolean; showChangePasswordDialog: boolean; From e9fc542dbc88bd6d11d6fae9bfe53fdba26c5c44 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Tue, 8 Dec 2020 20:03:05 +0300 Subject: [PATCH 06/11] Aborted extra change --- cvat-ui/src/components/login-page/login-page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cvat-ui/src/components/login-page/login-page.tsx b/cvat-ui/src/components/login-page/login-page.tsx index 59f92cce..f252606f 100644 --- a/cvat-ui/src/components/login-page/login-page.tsx +++ b/cvat-ui/src/components/login-page/login-page.tsx @@ -15,7 +15,7 @@ import CookieDrawer from './cookie-policy-drawer'; interface LoginPageComponentProps { fetching: boolean; renderResetPassword: boolean; - onLogin: (username: string, password: string, next: string | null) => void; + onLogin: (username: string, password: string) => void; } function LoginPageComponent(props: LoginPageComponentProps & RouteComponentProps): JSX.Element { From 1766eeec3202329583a1ea337f0cf1bd3bb9f420 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 9 Dec 2020 11:05:08 +0300 Subject: [PATCH 07/11] Fixed some not critical issues --- cvat-ui/src/actions/projects-actions.ts | 4 ++- cvat-ui/src/actions/review-actions.ts | 20 ++---------- cvat-ui/src/actions/tasks-actions.ts | 8 ++--- .../annotation-page/annotation-page.tsx | 6 ++-- .../src/components/task-page/task-page.tsx | 14 +++++---- cvat-ui/src/reducers/annotation-reducer.ts | 2 +- cvat-ui/src/reducers/review-reducer.ts | 9 ------ cvat-ui/src/reducers/tasks-reducer.ts | 31 +++++++++++++------ 8 files changed, 44 insertions(+), 50 deletions(-) diff --git a/cvat-ui/src/actions/projects-actions.ts b/cvat-ui/src/actions/projects-actions.ts index 2aa385db..d2794db3 100644 --- a/cvat-ui/src/actions/projects-actions.ts +++ b/cvat-ui/src/actions/projects-actions.ts @@ -141,9 +141,11 @@ export function updateProjectAsync(projectInstance: any): ThunkAction { dispatch(projectActions.updateProject()); await projectInstance.save(); const [project] = await cvat.projects.get({ id: projectInstance.id }); + // TODO: Check case when a project is not available anymore after update + // (assignee changes assignee and project is not public) dispatch(projectActions.updateProjectSuccess(project)); project.tasks.forEach((task: any) => { - dispatch(updateTaskSuccess(task)); + dispatch(updateTaskSuccess(task, task.id)); }); } catch (error) { let project = null; diff --git a/cvat-ui/src/actions/review-actions.ts b/cvat-ui/src/actions/review-actions.ts index 6cca02ec..b433d968 100644 --- a/cvat-ui/src/actions/review-actions.ts +++ b/cvat-ui/src/actions/review-actions.ts @@ -52,13 +52,7 @@ export const reviewActions = { reopenIssueSuccess: () => createAction(ReviewActionTypes.REOPEN_ISSUE_SUCCESS), reopenIssueFailed: (error: any) => createAction(ReviewActionTypes.REOPEN_ISSUE_FAILED, { error }), submitReview: (reviewId: number) => createAction(ReviewActionTypes.SUBMIT_REVIEW, { reviewId }), - submitReviewSuccess: (activeReview: any, reviews: any[], issues: any[], frame: number) => - createAction(ReviewActionTypes.SUBMIT_REVIEW_SUCCESS, { - activeReview, - reviews, - issues, - frame, - }), + submitReviewSuccess: () => createAction(ReviewActionTypes.SUBMIT_REVIEW_SUCCESS), submitReviewFailed: (error: any) => createAction(ReviewActionTypes.SUBMIT_REVIEW_FAILED, { error }), switchIssuesHiddenFlag: (hidden: boolean) => createAction(ReviewActionTypes.SWITCH_ISSUES_HIDDEN_FLAG, { hidden }), }; @@ -193,9 +187,6 @@ export const submitReviewAsync = (review: any): ThunkAction => async (dispatch, const { annotation: { job: { instance: jobInstance }, - player: { - frame: { number: frame }, - }, }, } = state; @@ -204,13 +195,8 @@ export const submitReviewAsync = (review: any): ThunkAction => async (dispatch, await review.submit(jobInstance.id); const [task] = await cvat.tasks.get({ id: jobInstance.task.id }); - dispatch(updateTaskSuccess(task)); - - const reviews = await jobInstance.reviews(); - const issues = await jobInstance.issues(); - const reviewInstance = new cvat.classes.Review({ job: jobInstance.id }); - - dispatch(reviewActions.submitReviewSuccess(reviewInstance, reviews, issues, frame)); + dispatch(updateTaskSuccess(task, jobInstance.task.id)); + dispatch(reviewActions.submitReviewSuccess()); } catch (error) { dispatch(reviewActions.submitReviewFailed(error)); } diff --git a/cvat-ui/src/actions/tasks-actions.ts b/cvat-ui/src/actions/tasks-actions.ts index 2be26d82..bd0fd450 100644 --- a/cvat-ui/src/actions/tasks-actions.ts +++ b/cvat-ui/src/actions/tasks-actions.ts @@ -437,10 +437,10 @@ function updateTask(): AnyAction { return action; } -export function updateTaskSuccess(task: any): AnyAction { +export function updateTaskSuccess(task: any, taskID: number): AnyAction { const action = { type: TasksActionTypes.UPDATE_TASK_SUCCESS, - payload: { task }, + payload: { task, taskID }, }; return action; @@ -465,7 +465,7 @@ export function updateTaskAsync(taskInstance: any): ThunkAction, C const userFetching = getState().auth.fetching; if (!userFetching && nextUser && currentUser.username === nextUser.username) { const [task] = await cvat.tasks.get({ id: taskInstance.id }); - dispatch(updateTaskSuccess(task)); + dispatch(updateTaskSuccess(task, taskInstance.id)); } } catch (error) { // try abort all changes @@ -490,7 +490,7 @@ export function updateJobAsync(jobInstance: any): ThunkAction, {}, dispatch(updateTask()); await jobInstance.save(); const [task] = await cvat.tasks.get({ id: jobInstance.task.id }); - dispatch(updateTaskSuccess(task)); + dispatch(updateTaskSuccess(task, jobInstance.task.id)); } catch (error) { // try abort all changes let task = null; diff --git a/cvat-ui/src/components/annotation-page/annotation-page.tsx b/cvat-ui/src/components/annotation-page/annotation-page.tsx index 2ef039fa..062f83b6 100644 --- a/cvat-ui/src/components/annotation-page/annotation-page.tsx +++ b/cvat-ui/src/components/annotation-page/annotation-page.tsx @@ -53,11 +53,13 @@ export default function AnnotationPageComponent(props: Props): JSX.Element { }; }, []); - if (job === null) { - if (!fetching) { + useEffect(() => { + if (job === null && !fetching) { getJob(); } + }, [job, fetching]); + if (job === null) { return ; } diff --git a/cvat-ui/src/components/task-page/task-page.tsx b/cvat-ui/src/components/task-page/task-page.tsx index 7249911c..ac440600 100644 --- a/cvat-ui/src/components/task-page/task-page.tsx +++ b/cvat-ui/src/components/task-page/task-page.tsx @@ -29,7 +29,13 @@ type Props = TaskPageComponentProps & RouteComponentProps<{ id: string }>; class TaskPageComponent extends React.PureComponent { public componentDidUpdate(): void { - const { deleteActivity, history } = this.props; + const { + deleteActivity, history, task, fetching, getTask, + } = this.props; + + if (task === null && !fetching) { + getTask(); + } if (deleteActivity) { history.replace('/tasks'); @@ -37,13 +43,9 @@ class TaskPageComponent extends React.PureComponent { } public render(): JSX.Element { - const { task, fetching, updating, getTask } = this.props; + const { task, updating } = this.props; if (task === null || updating) { - if (task === null && !fetching) { - getTask(); - } - return ; } diff --git a/cvat-ui/src/reducers/annotation-reducer.ts b/cvat-ui/src/reducers/annotation-reducer.ts index 7780eb9d..5b5b89cb 100644 --- a/cvat-ui/src/reducers/annotation-reducer.ts +++ b/cvat-ui/src/reducers/annotation-reducer.ts @@ -202,7 +202,7 @@ export default (state = defaultState, action: AnyAction): AnnotationState => { fetching: false, }, }, - } + }; } case AnnotationActionTypes.CHANGE_FRAME: { return { diff --git a/cvat-ui/src/reducers/review-reducer.ts b/cvat-ui/src/reducers/review-reducer.ts index a8c6d32e..919ea7ba 100644 --- a/cvat-ui/src/reducers/review-reducer.ts +++ b/cvat-ui/src/reducers/review-reducer.ts @@ -61,17 +61,8 @@ export default function (state: ReviewState = defaultState, action: any): Review }; } case ReviewActionTypes.SUBMIT_REVIEW_SUCCESS: { - const { - activeReview, reviews, issues, frame, - } = action.payload; - const frameIssues = computeFrameIssues(issues, activeReview, frame); - return { ...state, - activeReview, - reviews, - issues, - frameIssues, fetching: { ...state.fetching, reviewId: null, diff --git a/cvat-ui/src/reducers/tasks-reducer.ts b/cvat-ui/src/reducers/tasks-reducer.ts index 148d0a0e..c68a5f79 100644 --- a/cvat-ui/src/reducers/tasks-reducer.ts +++ b/cvat-ui/src/reducers/tasks-reducer.ts @@ -84,9 +84,9 @@ export default (state: TasksState = defaultState, action: AnyAction): TasksState const { dumps } = state.activities; dumps[task.id] = - task.id in dumps && !dumps[task.id].includes(dumper.name) - ? [...dumps[task.id], dumper.name] - : dumps[task.id] || [dumper.name]; + task.id in dumps && !dumps[task.id].includes(dumper.name) ? + [...dumps[task.id], dumper.name] : + dumps[task.id] || [dumper.name]; return { ...state, @@ -122,9 +122,9 @@ export default (state: TasksState = defaultState, action: AnyAction): TasksState const { exports: activeExports } = state.activities; activeExports[task.id] = - task.id in activeExports && !activeExports[task.id].includes(exporter.name) - ? [...activeExports[task.id], exporter.name] - : activeExports[task.id] || [exporter.name]; + task.id in activeExports && !activeExports[task.id].includes(exporter.name) ? + [...activeExports[task.id], exporter.name] : + activeExports[task.id] || [exporter.name]; return { ...state, @@ -299,15 +299,26 @@ export default (state: TasksState = defaultState, action: AnyAction): TasksState }; } case TasksActionTypes.UPDATE_TASK_SUCCESS: { + // a task will be undefined after updating when a user doesn't have access to the task anymore + const { task, taskID } = action.payload; + + if (typeof task === 'undefined') { + return { + ...state, + updating: false, + current: state.current.filter((_task: Task): boolean => _task.instance.id !== taskID), + }; + } + return { ...state, updating: false, current: state.current.map( - (task): Task => { - if (task.instance.id === action.payload.task.id) { + (_task): Task => { + if (_task.instance.id === task.id) { return { - ...task, - instance: action.payload.task, + ..._task, + instance: task, }; } From b89ea5841fe5c674862e1ca90231014692054c41 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 9 Dec 2020 11:10:10 +0300 Subject: [PATCH 08/11] Updated version & changelog --- CHANGELOG.md | 1 + cvat-ui/package-lock.json | 2 +- cvat-ui/package.json | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 205b7657..f47497a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - TypeError: Cannot read property 'toString' of undefined () - Extra shapes are drawn after Esc, or G pressed while drawing a region in grouping () - Reset state (reviews, issues) after logout or changing a job () +- TypeError: Cannot read property 'id' of undefined when updating a task () ### Security diff --git a/cvat-ui/package-lock.json b/cvat-ui/package-lock.json index 866cc47f..f139b848 100644 --- a/cvat-ui/package-lock.json +++ b/cvat-ui/package-lock.json @@ -1,6 +1,6 @@ { "name": "cvat-ui", - "version": "1.12.0", + "version": "1.12.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/cvat-ui/package.json b/cvat-ui/package.json index b4e9637f..e5bd2c6d 100644 --- a/cvat-ui/package.json +++ b/cvat-ui/package.json @@ -1,6 +1,6 @@ { "name": "cvat-ui", - "version": "1.12.0", + "version": "1.12.1", "description": "CVAT single-page application", "main": "src/index.tsx", "scripts": { From cf99cd6086c852b851ae23c4b0227099416d63cd Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 9 Dec 2020 11:21:28 +0300 Subject: [PATCH 09/11] using DidMount instead of DidUpdate --- cvat-ui/src/components/projects-page/project-list.tsx | 2 +- cvat-ui/src/components/task-page/task-page.tsx | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/cvat-ui/src/components/projects-page/project-list.tsx b/cvat-ui/src/components/projects-page/project-list.tsx index 134286be..431f5da7 100644 --- a/cvat-ui/src/components/projects-page/project-list.tsx +++ b/cvat-ui/src/components/projects-page/project-list.tsx @@ -42,7 +42,7 @@ export default function ProjectListComponent(): JSX.Element { {projectInstances.map( (row: any[]): JSX.Element => ( - + {row.map((instance: any) => ( diff --git a/cvat-ui/src/components/task-page/task-page.tsx b/cvat-ui/src/components/task-page/task-page.tsx index ac440600..7537ce5a 100644 --- a/cvat-ui/src/components/task-page/task-page.tsx +++ b/cvat-ui/src/components/task-page/task-page.tsx @@ -28,14 +28,16 @@ interface TaskPageComponentProps { type Props = TaskPageComponentProps & RouteComponentProps<{ id: string }>; class TaskPageComponent extends React.PureComponent { - public componentDidUpdate(): void { - const { - deleteActivity, history, task, fetching, getTask, - } = this.props; + public componentDidMount(): void { + const { task, fetching, getTask } = this.props; if (task === null && !fetching) { getTask(); } + } + + public componentDidUpdate(): void { + const { deleteActivity, history } = this.props; if (deleteActivity) { history.replace('/tasks'); From 355afcba7bab24d517dca6df6f18327594c9790b Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 9 Dec 2020 11:30:45 +0300 Subject: [PATCH 10/11] Fixed reducer --- cvat-ui/src/reducers/tasks-reducer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cvat-ui/src/reducers/tasks-reducer.ts b/cvat-ui/src/reducers/tasks-reducer.ts index c68a5f79..2cc8db82 100644 --- a/cvat-ui/src/reducers/tasks-reducer.ts +++ b/cvat-ui/src/reducers/tasks-reducer.ts @@ -322,7 +322,7 @@ export default (state: TasksState = defaultState, action: AnyAction): TasksState }; } - return task; + return _task; }, ), }; From c0c90745872b7f713deb0de2af84d64fb3b9493d Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 9 Dec 2020 11:40:44 +0300 Subject: [PATCH 11/11] Abort PR #2506 by customer request --- CHANGELOG.md | 1 - .../components/annotation-page/canvas/canvas-wrapper.tsx | 6 +++--- .../actions_tasks_objects/issue_2486_not_edit_object_aam.js | 6 +++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 205b7657..4a7f3b8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,7 +41,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Projects view layout fix () - Fixed the tasks view (infinite loading) when it is impossible to get a preview of the task () - Empty frames navigation () -- Disabled position editing in AAM () - TypeError: Cannot read property 'toString' of undefined () - Extra shapes are drawn after Esc, or G pressed while drawing a region in grouping () - Reset state (reviews, issues) after logout or changing a job () diff --git a/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper.tsx b/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper.tsx index d96ebf14..20007713 100644 --- a/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper.tsx +++ b/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper.tsx @@ -109,7 +109,7 @@ export default class CanvasWrapperComponent extends React.PureComponent { autoborders: automaticBordering, undefinedAttrValue: consts.UNDEFINED_ATTRIBUTE_VALUE, displayAllText: showObjectsTextAlways, - forceDisableEditing: [Workspace.ATTRIBUTE_ANNOTATION, Workspace.REVIEW_WORKSPACE].includes(workspace), + forceDisableEditing: workspace === Workspace.REVIEW_WORKSPACE, }); this.initialSetup(); @@ -260,11 +260,11 @@ export default class CanvasWrapperComponent extends React.PureComponent { } if (prevProps.workspace !== workspace) { - if ([Workspace.ATTRIBUTE_ANNOTATION, Workspace.REVIEW_WORKSPACE].includes(workspace)) { + if (workspace === Workspace.REVIEW_WORKSPACE) { canvasInstance.configure({ forceDisableEditing: true, }); - } else if ([Workspace.ATTRIBUTE_ANNOTATION, Workspace.REVIEW_WORKSPACE].includes(prevProps.workspace)) { + } else if (prevProps.workspace === Workspace.REVIEW_WORKSPACE) { canvasInstance.configure({ forceDisableEditing: false, }); diff --git a/tests/cypress/integration/actions_tasks_objects/issue_2486_not_edit_object_aam.js b/tests/cypress/integration/actions_tasks_objects/issue_2486_not_edit_object_aam.js index ace8fa9a..e2faee99 100644 --- a/tests/cypress/integration/actions_tasks_objects/issue_2486_not_edit_object_aam.js +++ b/tests/cypress/integration/actions_tasks_objects/issue_2486_not_edit_object_aam.js @@ -25,7 +25,7 @@ context("Object can't be draggable/resizable in AAM", () => { }); describe(`Testing issue "${issueId}"`, () => { - it('Create, acttivate a object', () => { + it.skip('Create, acttivate a object', () => { cy.createRectangle(createRectangleShape2Points); cy.get('#cvat_canvas_shape_1') .should('not.have.class', 'cvat_canvas_shape_activated') @@ -33,7 +33,7 @@ context("Object can't be draggable/resizable in AAM", () => { .should('have.class', 'cvat_canvas_shape_activated'); }); - it('Go to AAM', () => { + it.skip('Go to AAM', () => { cy.changeWorkspace('Attribute annotation', labelName); cy.get('#cvat_canvas_shape_1') .then((shape) => { @@ -50,7 +50,7 @@ context("Object can't be draggable/resizable in AAM", () => { }); }); - it('Try to move/resize the object', () => { + it.skip('Try to move/resize the object', () => { cy.get('.cvat-canvas-container') .trigger('mousedown', { button: 0 }) .trigger('mousemove', 550, 251)