diff --git a/cvat-ui/src/actions/tasks-actions.ts b/cvat-ui/src/actions/tasks-actions.ts index 7482d48ec678..818aadedf6b7 100644 --- a/cvat-ui/src/actions/tasks-actions.ts +++ b/cvat-ui/src/actions/tasks-actions.ts @@ -28,39 +28,39 @@ function getTasks(): AnyAction { } function getTasksSuccess(array: any[], previews: string[], - count: number, query: TasksQuery): AnyAction { + count: number, gettingQuery: TasksQuery): AnyAction { const action = { type: TasksActionTypes.GET_TASKS_SUCCESS, payload: { previews, array, count, - query, + gettingQuery, }, }; return action; } -function getTasksFailed(error: any, query: TasksQuery): AnyAction { +function getTasksFailed(gettingTasksError: any, gettingQuery: TasksQuery): AnyAction { const action = { type: TasksActionTypes.GET_TASKS_FAILED, payload: { - error, - query, + gettingTasksError, + gettingQuery, }, }; return action; } -export function getTasksAsync(query: TasksQuery): +export function getTasksAsync(gettingQuery: TasksQuery): ThunkAction, {}, {}, AnyAction> { return async (dispatch: ActionCreator): Promise => { dispatch(getTasks()); // We need remove all keys with null values from query - const filteredQuery = { ...query }; + const filteredQuery = { ...gettingQuery }; for (const key in filteredQuery) { if (filteredQuery[key] === null) { delete filteredQuery[key]; @@ -70,8 +70,8 @@ ThunkAction, {}, {}, AnyAction> { let result = null; try { result = await cvat.tasks.get(filteredQuery); - } catch (error) { - dispatch(getTasksFailed(error, query)); + } catch (gettingTasksError) { + dispatch(getTasksFailed(gettingTasksError, gettingQuery)); return; } @@ -93,7 +93,7 @@ ThunkAction, {}, {}, AnyAction> { } } - dispatch(getTasksSuccess(array, previews, result.count, query)); + dispatch(getTasksSuccess(array, previews, result.count, gettingQuery)); }; } @@ -121,13 +121,13 @@ function dumpAnnotationSuccess(task: any, dumper: any): AnyAction { return action; } -function dumpAnnotationFailed(task: any, dumper: any, error: any): AnyAction { +function dumpAnnotationFailed(task: any, dumper: any, dumpingError: any): AnyAction { const action = { type: TasksActionTypes.DUMP_ANNOTATIONS_FAILED, payload: { task, dumper, - error, + dumpingError, }, }; @@ -137,13 +137,12 @@ function dumpAnnotationFailed(task: any, dumper: any, error: any): AnyAction { export function dumpAnnotationsAsync(task: any, dumper: any): ThunkAction, {}, {}, AnyAction> { return async (dispatch: ActionCreator): Promise => { - dispatch(dumpAnnotation(task, dumper)); - try { + dispatch(dumpAnnotation(task, dumper)); const url = await task.annotations.dump(task.name, dumper); window.location.assign(url); - } catch (error) { - dispatch(dumpAnnotationFailed(task, dumper, error)); + } catch (dumpingError) { + dispatch(dumpAnnotationFailed(task, dumper, dumpingError)); return; } @@ -174,12 +173,12 @@ function loadAnnotationsSuccess(task: any): AnyAction { return action; } -function loadAnnotationsFailed(task: any, error: any): AnyAction { +function loadAnnotationsFailed(task: any, loadingError: any): AnyAction { const action = { type: TasksActionTypes.LOAD_ANNOTATIONS_FAILED, payload: { task, - error, + loadingError, }, }; @@ -189,12 +188,11 @@ function loadAnnotationsFailed(task: any, error: any): AnyAction { export function loadAnnotationsAsync(task: any, loader: any, file: File): ThunkAction, {}, {}, AnyAction> { return async (dispatch: ActionCreator): Promise => { - dispatch(loadAnnotations(task, loader)); - try { + dispatch(loadAnnotations(task, loader)); await task.annotations.upload(file, loader); - } catch (error) { - dispatch(loadAnnotationsFailed(task, error)); + } catch (loadingError) { + dispatch(loadAnnotationsFailed(task, loadingError)); return; } diff --git a/cvat-ui/src/reducers/interfaces.ts b/cvat-ui/src/reducers/interfaces.ts index 2004f2b9f2fb..056b8a306b94 100644 --- a/cvat-ui/src/reducers/interfaces.ts +++ b/cvat-ui/src/reducers/interfaces.ts @@ -19,36 +19,34 @@ export interface TasksQuery { [key: string]: string | number | null; } -export interface DumpState { - dumperName: string; -} - -export interface LoadState { - loaderName: string; -} - export interface Task { - instance: any; + instance: any; // cvat-core instance preview: string; } -export interface ActiveTask { - dump: DumpState[]; - load: LoadState | null; -} - export interface TasksState { initialized: boolean; + gettingTasksError: any; + gettingQuery: TasksQuery; count: number; current: Task[]; - active: { - [index: number]: ActiveTask; + activities: { + dumps: { + dumpingError: any; + byTask: { + // dumps in different formats at the same time + [tid: number]: string[]; // dumper names + }; + }; + loads: { + loadingError: any; + loadingDoneMessage: string; + byTask: { + // only one loading simultaneously + [tid: number]: string; // loader name + }; + }; }; - error: any; - dumpError: any; - loadError: any; - loadDone: string; - query: TasksQuery; } export interface FormatsState { diff --git a/cvat-ui/src/reducers/tasks-reducer.ts b/cvat-ui/src/reducers/tasks-reducer.ts index 203800b35eb6..f12f30fd6afd 100644 --- a/cvat-ui/src/reducers/tasks-reducer.ts +++ b/cvat-ui/src/reducers/tasks-reducer.ts @@ -1,18 +1,14 @@ import { AnyAction } from 'redux'; import { TasksActionTypes } from '../actions/tasks-actions'; -import { TasksState, Task, DumpState } from './interfaces'; +import { TasksState, Task } from './interfaces'; const defaultState: TasksState = { initialized: false, + gettingTasksError: null, count: 0, current: [], - active: {}, - dumpError: null, - loadError: null, - loadDone: '', - error: null, - query: { + gettingQuery: { page: 1, id: null, search: null, @@ -22,9 +18,41 @@ const defaultState: TasksState = { status: null, mode: null, }, + activities: { + dumps: { + dumpingError: null, + byTask: {}, + }, + loads: { + loadingError: null, + loadingDoneMessage: '', + byTask: {}, + }, + }, }; -export default (state = defaultState, action: AnyAction): TasksState => { +export default (inputState: TasksState = defaultState, action: AnyAction): TasksState => { + function cleanupTemporaryInfo(stateToResetErrors: TasksState): TasksState { + return { + ...stateToResetErrors, + gettingTasksError: null, + activities: { + ...stateToResetErrors.activities, + dumps: { + ...stateToResetErrors.activities.dumps, + dumpingError: null, + }, + loads: { + ...stateToResetErrors.activities.loads, + loadingError: null, + loadingDoneMessage: '', + }, + }, + }; + } + + const state = cleanupTemporaryInfo(inputState); + switch (action.type) { case TasksActionTypes.GET_TASKS: return { @@ -43,143 +71,147 @@ export default (state = defaultState, action: AnyAction): TasksState => { initialized: true, count: action.payload.count, current: combinedWithPreviews, - error: null, - dumpError: null, - loadError: null, - loadDone: '', - query: { ...action.payload.query }, + gettingQuery: { ...action.payload.gettingQuery }, }; } case TasksActionTypes.GET_TASKS_FAILED: return { ...state, initialized: true, - current: [], count: 0, - dumpError: null, - loadError: null, - loadDone: '', - error: action.payload.error, - query: { ...action.payload.query }, + current: [], + gettingQuery: { ...action.payload.gettingQuery }, + gettingTasksError: action.payload.gettingTasksError, }; - case TasksActionTypes.DUMP_ANNOTATIONS: { const { task } = action.payload; const { dumper } = action.payload; - const activeTask = { - ...state.active[task.id] || { - dump: [], - load: null, - }, + const tasksDumpingActivities = { + ...state.activities.dumps, }; - if (!activeTask.dump.map( - (dumpState): string => dumpState.dumperName, - ).includes(dumper.name)) { - activeTask.dump = [...activeTask.dump, { - dumperName: dumper.name, - }]; + const theTaskDumpingActivities = [...tasksDumpingActivities.byTask[task.id] || []]; + if (!theTaskDumpingActivities.includes(dumper.name)) { + theTaskDumpingActivities.push(dumper.name); + } else { + throw Error('Dump with the same dumper for this same task has been already started'); } - - const activeTasks = { ...state.active }; - activeTasks[task.id] = activeTask; + tasksDumpingActivities.byTask[task.id] = theTaskDumpingActivities; return { ...state, - active: activeTasks, + activities: { + ...state.activities, + dumps: tasksDumpingActivities, + }, }; } case TasksActionTypes.DUMP_ANNOTATIONS_SUCCESS: { const { task } = action.payload; const { dumper } = action.payload; - const activeTask = { ...state.active[task.id] }; - const activeDumps = activeTask.dump.filter( - (dumpState: DumpState): boolean => dumpState.dumperName !== dumper.name, - ); + const tasksDumpingActivities = { + ...state.activities.dumps, + }; - activeTask.dump = activeDumps; + const theTaskDumpingActivities = tasksDumpingActivities.byTask[task.id] + .filter((dumperName: string): boolean => dumperName !== dumper.name); - const activeTasks = { ...state.active }; - activeTasks[task.id] = activeTask; + tasksDumpingActivities.byTask[task.id] = theTaskDumpingActivities; return { ...state, - active: activeTasks, + activities: { + ...state.activities, + dumps: tasksDumpingActivities, + }, }; } case TasksActionTypes.DUMP_ANNOTATIONS_FAILED: { const { task } = action.payload; const { dumper } = action.payload; - const { error } = action.payload; + const { dumpingError } = action.payload; - const activeTask = { ...state.active[task.id] }; - const activeDumps = activeTask.dump.filter( - (dumpState: DumpState): boolean => dumpState.dumperName !== dumper.name, - ); + const tasksDumpingActivities = { + ...state.activities.dumps, + dumpingError, + }; - activeTask.dump = activeDumps; + const theTaskDumpingActivities = tasksDumpingActivities.byTask[task.id] + .filter((dumperName: string): boolean => dumperName !== dumper.name); - const activeTasks = { ...state.active }; - activeTasks[task.id] = activeTask; + tasksDumpingActivities.byTask[task.id] = theTaskDumpingActivities; return { ...state, - active: activeTasks, - dumpError: error, + activities: { + ...state.activities, + dumps: tasksDumpingActivities, + }, }; } case TasksActionTypes.LOAD_ANNOTATIONS: { const { task } = action.payload; const { loader } = action.payload; - const activeTask = { - ...state.active[task.id] || { - dump: [], - load: null, - }, + const tasksLoadingActivity = { + ...state.activities.loads, }; - activeTask.load = { - loaderName: loader.name, - }; + if (task.id in tasksLoadingActivity.byTask) { + throw Error('Load for this task has been already started'); + } - const activeTasks = { ...state.active }; - activeTasks[task.id] = activeTask; + tasksLoadingActivity.byTask[task.id] = loader.name; return { ...state, - active: activeTasks, - loadError: null, - loadDone: '', + activities: { + ...state.activities, + loads: tasksLoadingActivity, + }, }; } case TasksActionTypes.LOAD_ANNOTATIONS_SUCCESS: { const { task } = action.payload; - const activeTasks = state.active; - delete activeTasks[task.id]; + const tasksLoadingActivity = { + ...state.activities.loads, + }; + + delete tasksLoadingActivity.byTask[task.id]; return { ...state, - active: activeTasks, - loadError: null, - loadDone: `Annotations were uploaded for the task #${task.id}`, + activities: { + ...state.activities, + loads: { + ...tasksLoadingActivity, + loadingDoneMessage: `Annotations have been loaded to the task ${task.id}`, + }, + }, }; } case TasksActionTypes.LOAD_ANNOTATIONS_FAILED: { const { task } = action.payload; - const { error } = action.payload; + const { loadingError } = action.payload; - const activeTasks = state.active; - delete activeTasks[task.id]; + const tasksLoadingActivity = { + ...state.activities.loads, + }; + + delete tasksLoadingActivity.byTask[task.id]; return { ...state, - active: activeTasks, - loadError: error, - loadDone: '', + activities: { + ...state.activities, + loads: { + ...tasksLoadingActivity, + loadingError, + }, + }, }; } default: