Store Actions And Effects Get Files

This commit is contained in:
Lorenz Hilpert
2020-05-25 14:35:59 +02:00
parent baa8b21a15
commit 711cb2cce7
18 changed files with 435 additions and 48 deletions

View File

@@ -6,17 +6,15 @@ import { environment } from '../environments/environment';
import { RootState } from './store/root.state';
import { rootReducer } from './store/root.reducer';
import { EffectsModule } from '@ngrx/effects';
import { TaskCalendarEffects } from './store/branch';
import { TaskCalendarEffects, TaskFilesEffects } from './store/branch';
export const metaReducers: MetaReducer<RootState>[] = !environment.production
? [storeFreeze]
: [];
export const metaReducers: MetaReducer<RootState>[] = !environment.production ? [storeFreeze] : [];
@NgModule({
imports: [
StoreModule.forRoot(rootReducer, { metaReducers }),
EffectsModule.forRoot([TaskCalendarEffects]),
EffectsModule.forRoot([TaskCalendarEffects, TaskFilesEffects]),
StoreDevtoolsModule.instrument({ name: 'Ngrx Store' }),
],
})
export class AppStoreModule { }
export class AppStoreModule {}

View File

@@ -1,9 +1,11 @@
import { combineReducers, Action } from '@ngrx/store';
import { BranchState } from './branch.state';
import { taskCalendarReducer } from './task-calendar';
import { taskFilesReducer } from './task-files';
const _branchReducer = combineReducers<BranchState>({
taskCalendar: taskCalendarReducer,
taskFiles: taskFilesReducer,
});
export function branchReducer(state: BranchState, action: Action) {

View File

@@ -1,5 +1,7 @@
import { TaskCalendarState } from './task-calendar';
import { TaskFilesState } from './task-files';
export interface BranchState {
taskCalendar: TaskCalendarState;
taskFiles: TaskFilesState;
}

View File

@@ -2,3 +2,6 @@ export * from './branch.reducer';
export * from './branch.state';
export * from './branch.selectors';
export * from './task-calendar';
export * from './task-files';
export * from './task-calendar/mappings';
export * from './task-files/defs';

View File

@@ -5,7 +5,10 @@ import {
ResponseArgsOfIEnumerableOfDisplayInfoDTO,
DisplayInfoRequest,
ResponseArgsOfEntityDTOContainerOfBranchDTO,
} from '@swagger/eis/lib';
FileDTO,
ResponseArgsOfConfirmationDTO,
ResponseArgs,
} from '@swagger/eis';
const prefix = '[Branch] [Task Calendar]';
@@ -27,12 +30,18 @@ export const getTasksResponse = createAction(
}>()
);
export const getTaskRequest = createAction(`${prefix} Get Tasks Request`, props<{ taskId: number }>());
export const getTaskResponse = createAction(`${prefix} Get Tasks Response`, props<{ response: StrictHttpResponse<DisplayInfoDTO> }>());
export const patchTaskRequest = createAction(`${prefix} Patch Task Request`);
export const patchTaskResponse = createAction(`${prefix} Patch Task Response`);
export const setTasks = createAction(`${prefix} Set Tasks`, props<{ tasks: DisplayInfoDTO[] }>());
export const setTask = createAction(`${prefix} Set Task`, props<{ task: DisplayInfoDTO }>());
export const setFilter = createAction(`${prefix} Set Filter`, props<{ filter: DisplayInfoRequest }>());
export const patchFilter = createAction(`${prefix} Patch Filter`, props<{ filter: Partial<DisplayInfoRequest> }>());
@@ -41,11 +50,28 @@ export const openTaskDialog = createAction(`${prefix} Open Task Dialog`, props<{
export const patchTask = createAction(`${prefix} Patch Task`, props<{ taskId: number; changes: Partial<DisplayInfoDTO> }>());
export const patchTaskEditStatus = createAction(
`${prefix} Patch Task Status`,
props<{ taskId: number; state: 'edit' | 'reset' | 'complete' }>()
export const patchTaskStatus = createAction(
`${prefix} Patch Task Status Request`,
props<{ taskId: number; state: 'edit' | 'reset' | 'complete'; file?: FileDTO }>()
);
export const addTaskImage = createAction(`${prefix} Add Task Image`, props<{ taksId: number; image: File }>());
export const patchTaskEditRequest = createAction(`${prefix} Patch Task Edit Request`, props<{ taskId: number }>());
export const removeTaskImage = createAction(`${prefix} Remove Task Image`, props<{ taksId: number; imageId: number }>());
export const patchTaskEditResponse = createAction(
`${prefix} Patch Task Edit Response`,
props<{ response: StrictHttpResponse<ResponseArgsOfConfirmationDTO> }>()
);
export const patchTaskResetRequest = createAction(`${prefix} Patch Task Reset Request`, props<{ taskId: number }>());
export const patchTaskResetResponse = createAction(
`${prefix} Patch Task Reset Response`,
props<{ response: StrictHttpResponse<ResponseArgsOfConfirmationDTO> }>()
);
export const patchTaskCompleteRequest = createAction(`${prefix} Patch Task Complete Request`, props<{ taskId: number; file?: FileDTO }>());
export const patchTaskCompleteResponse = createAction(
`${prefix} Patch Task Complete Response`,
props<{ taskId: number; response: StrictHttpResponse<ResponseArgs> }>()
);

View File

@@ -5,17 +5,19 @@ import {
ListResponseArgsOfDisplayInfoDTO,
StrictHttpResponse,
ResponseArgsOfEntityDTOContainerOfBranchDTO,
ResponseArgsOfConfirmationDTO,
ResponseArgs,
} from '@swagger/eis';
import * as actions from './task-calendar.actions';
import { TaskCalendarFacade } from './task-calendar.facade';
import { withLatestFrom, switchMap, catchError, map, flatMap } from 'rxjs/operators';
import { of } from 'rxjs';
import { of, NEVER, Observable } from 'rxjs';
@Injectable()
export class TaskCalendarEffects implements OnInitEffects {
constructor(private actions$: Actions, private taskCalendarFacade: TaskCalendarFacade, private eisPublicService: EISPublicService) {}
getTasks$ = createEffect(() =>
getTasksRequest$ = createEffect(() =>
this.actions$.pipe(
ofType(actions.getTasksRequest),
withLatestFrom(this.taskCalendarFacade.filter$),
@@ -47,7 +49,7 @@ export class TaskCalendarEffects implements OnInitEffects {
)
);
getCurrentBranch$ = createEffect(() =>
getCurrentBranchRequest$ = createEffect(() =>
this.actions$.pipe(
ofType(actions.getCurrentBranchRequest),
switchMap(() =>
@@ -72,6 +74,94 @@ export class TaskCalendarEffects implements OnInitEffects {
)
);
patchTaskStatus$ = createEffect(() =>
this.actions$.pipe(
ofType(actions.patchTaskStatus),
flatMap((action) => {
if (action.state === 'complete') {
return [actions.patchTaskCompleteRequest({ taskId: action.taskId, file: action.file })];
} else if (action.state === 'edit') {
return [actions.patchTaskEditRequest({ taskId: action.taskId })];
} else if (action.state === 'reset') {
return [actions.patchTaskResetRequest({ taskId: action.taskId })];
}
return NEVER;
})
)
);
patchTaskEditRequest$ = createEffect(() =>
this.actions$.pipe(
ofType(actions.patchTaskEditRequest),
flatMap((action) =>
this.eisPublicService
.EISPublicSetInfoToEditResponse({ infoId: action.taskId })
.pipe(catchError((err) => of<StrictHttpResponse<ResponseArgsOfConfirmationDTO>>(err)))
),
map((response) => actions.patchTaskEditResponse({ response }))
)
);
patchTaskEditResponse$ = createEffect(() =>
this.actions$.pipe(
ofType(actions.patchTaskEditResponse),
flatMap((action) => {
if (action.response.ok) {
return [actions.getTaskRequest({ taskId: action.response.body.result.id })];
}
return NEVER;
})
)
);
patchTaskResetRequest$ = createEffect(() =>
this.actions$.pipe(
ofType(actions.patchTaskResetRequest),
flatMap((action) =>
this.eisPublicService
.EISPublicResetConfirmationResponse({ infoId: action.taskId })
.pipe(catchError((err) => of<StrictHttpResponse<ResponseArgsOfConfirmationDTO>>(err)))
),
map((response) => actions.patchTaskResetResponse({ response }))
)
);
patchTaskResetResponse$ = createEffect(() =>
this.actions$.pipe(
ofType(actions.patchTaskResetResponse),
flatMap((action) => {
if (action.response.ok) {
return [actions.getTaskRequest({ taskId: action.response.body.result.id })];
}
return NEVER;
})
)
);
patchTaskCompleteRequest$ = createEffect(() =>
this.actions$.pipe(
ofType(actions.patchTaskCompleteRequest),
flatMap((action) =>
this.eisPublicService.EISPublicCompleteConfirmationResponse({ infoId: action.taskId, file: action.file }).pipe(
catchError((err) => of<StrictHttpResponse<ResponseArgs>>(err)),
map((response) => actions.patchTaskCompleteResponse({ taskId: action.taskId, response }))
)
)
)
);
patchTaskCompleteResponse$ = createEffect(() =>
this.actions$.pipe(
ofType(actions.patchTaskCompleteResponse),
flatMap((action) => {
if (action.response.ok) {
return [actions.getTaskRequest({ taskId: action.taskId })];
}
return NEVER;
})
)
);
ngrxOnInitEffects() {
return actions.getCurrentBranchRequest();
}

View File

@@ -1,5 +1,5 @@
import { Action, createReducer, on } from '@ngrx/store';
import { TaskCalendarState, INITIAL_TASK_CALENDAR_STATE, adapter } from './task-calendar.state';
import { TaskCalendarState, INITIAL_TASK_CALENDAR_STATE, taskCalendarAdapter } from './task-calendar.state';
import * as actions from './task-calendar.actions';
import { DisplayInfoRequest } from '@swagger/eis';
@@ -7,10 +7,10 @@ const _taskCalendarReducer = createReducer<TaskCalendarState>(
INITIAL_TASK_CALENDAR_STATE,
on(actions.getTasksRequest, (s): TaskCalendarState => ({ ...s, fetching: true })),
on(actions.getTasksResponse, (s): TaskCalendarState => ({ ...s, fetching: false })),
on(actions.setTasks, (s, a): TaskCalendarState => adapter.addAll(a.tasks, s)),
on(actions.setTasks, (s, a): TaskCalendarState => taskCalendarAdapter.addAll(a.tasks, s)),
on(actions.setFilter, (s, a): TaskCalendarState => ({ ...s, filter: a.filter })),
on(actions.patchFilter, (s, a): TaskCalendarState => ({ ...s, filter: { ...(s.filter || ({} as DisplayInfoRequest)), ...a.filter } })),
on(actions.patchTask, (s, a): TaskCalendarState => adapter.updateOne({ id: a.taskId, changes: a.changes }, s))
on(actions.patchTask, (s, a): TaskCalendarState => taskCalendarAdapter.updateOne({ id: a.taskId, changes: a.changes }, s))
);
export function taskCalendarReducer(state: TaskCalendarState, action: Action) {

View File

@@ -4,39 +4,23 @@ import { DisplayInfoDTO } from '@swagger/eis';
import { fromDisplayInfoToTask } from './mappings';
import { parseStringDate } from '../../../modules/task-calendar/utils';
import { fromDisplayInfoToCalendarItem } from './mappings/calendar-item-mapper';
import { taskCalendarAdapter } from './task-calendar.state';
export const selectTaskCalendarState = createSelector(
selectBranchState,
(s) => s.taskCalendar
);
export const selectTaskCalendarState = createSelector(selectBranchState, (s) => s.taskCalendar);
export const selectFetching = createSelector(
selectTaskCalendarState,
(s) => s.fetching
);
const { selectAll } = taskCalendarAdapter.getSelectors(selectTaskCalendarState);
export const selectTasks = createSelector(
selectTaskCalendarState,
(s) => s.tasks
);
export const selectFetching = createSelector(selectTaskCalendarState, (s) => s.fetching);
export const selectFilters = createSelector(
selectTaskCalendarState,
(s) => s.filter
);
export const selectTasks = createSelector(selectAll, (s) => s);
export const selectTaskById = createSelector(
selectTasks,
(s: DisplayInfoDTO[], id: number) => s.find((task) => task.id === id)
);
export const selectFilters = createSelector(selectTaskCalendarState, (s) => s.filter);
export const selectMappedTasks = createSelector(selectTasks, (s) =>
s.map((tasks) => fromDisplayInfoToTask(tasks))
);
export const selectTaskById = createSelector(selectTasks, (s: DisplayInfoDTO[], id: number) => s.find((task) => task.id === id));
export const selectCalendarItems = createSelector(selectTasks, (s) =>
s.map((task) => fromDisplayInfoToCalendarItem(task))
);
export const selectMappedTasks = createSelector(selectTasks, (s) => s.map((tasks) => fromDisplayInfoToTask(tasks)));
export const selectCalendarItems = createSelector(selectTasks, (s) => s.map((task) => fromDisplayInfoToCalendarItem(task)));
export const selectMinMaxDates = createSelector(selectFilters, (s) => ({
minDate: parseStringDate(s.start),

View File

@@ -9,10 +9,10 @@ export interface TaskCalendarState extends EntityState<DisplayInfoDTO> {
const { minDate, maxDate } = getMinAndMaxDate(3);
export const adapter = createEntityAdapter<DisplayInfoDTO>();
export const taskCalendarAdapter = createEntityAdapter<DisplayInfoDTO>();
export const INITIAL_TASK_CALENDAR_STATE: TaskCalendarState = {
...adapter.getInitialState(),
...taskCalendarAdapter.getInitialState(),
fetching: false,
filter: {
branchId: undefined,

View File

@@ -0,0 +1,6 @@
import { FileDTO as EisFileDTO } from '@swagger/eis';
export interface FileDTO extends EisFileDTO {
taskId: number;
section: 'file' | 'image' | 'teaser' | 'confirmation';
}

View File

@@ -0,0 +1 @@
export * from './file-dto';

View File

@@ -0,0 +1,7 @@
export * from './task-files.reducer';
export * from './task-files.selectors';
export * from './task-files.effects';
export * from './task-files.state';
export * from './task-files.facade';
export * from './task-files.actions';
export * from './defs';

View File

@@ -0,0 +1,41 @@
import { createAction, props } from '@ngrx/store';
import { StrictHttpResponse, ResponseArgsOfIEnumerableOfFileDTO } from '@swagger/eis';
import { FileDTO } from './defs';
const prefix = '[Branch] [Task Files]';
export const getFiles = createAction(`${prefix} Get Files`, props<{ taskId: number }>());
export const getFilesRequest = createAction(`${prefix} Get Files Request`, props<{ taskId: number }>());
export const getFilesResponse = createAction(
`${prefix} Get Files Response`,
props<{ taskId: number; response: StrictHttpResponse<ResponseArgsOfIEnumerableOfFileDTO> }>()
);
export const getImageTeaserFilesRequest = createAction(`${prefix} Get Image Teaser Files Request`, props<{ taskId: number }>());
export const getImageTeaserFilesResponse = createAction(
`${prefix} Get Image Teaser Files Response`,
props<{ taskId: number; response: StrictHttpResponse<ResponseArgsOfIEnumerableOfFileDTO> }>()
);
export const getImageFilesRequest = createAction(`${prefix} Get Image Files Request`, props<{ taskId: number }>());
export const getImageFilesResponse = createAction(
`${prefix} Get Image Files Response`,
props<{ taskId: number; response: StrictHttpResponse<ResponseArgsOfIEnumerableOfFileDTO> }>()
);
export const getConfiramtionImageFileRequest = createAction(`${prefix} Get Confirmation Image File Request`, props<{ taskId: number }>());
export const getConfiramtionImageFileResponse = createAction(
`${prefix} Get Confirmation Image File Response`,
props<{ taskId: number; response: StrictHttpResponse<ResponseArgsOfIEnumerableOfFileDTO> }>()
);
export const addFiles = createAction(`${prefix} Set Files`, props<{ files: FileDTO[] }>());
export const clear = createAction(`${prefix} Clear`, props<{ taskId: number; section: 'file' | 'image' | 'teaser' | 'confirmation' }>());
export const clearAll = createAction(`${prefix} Clear All`);

View File

@@ -0,0 +1,172 @@
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import * as actions from './task-files.actions';
import { flatMap, catchError, map } from 'rxjs/operators';
import { EISPublicService, StrictHttpResponse, ResponseArgsOfIEnumerableOfFileDTO } from '@swagger/eis';
import { of, NEVER } from 'rxjs';
import { FileDTO } from './defs';
@Injectable()
export class TaskFilesEffects {
constructor(private actions$: Actions, private eisPublicService: EISPublicService) {}
getFiles$ = createEffect(() =>
this.actions$.pipe(
ofType(actions.getFiles),
flatMap((action) => [
actions.getFilesRequest({ taskId: action.taskId }),
actions.getImageFilesRequest({ taskId: action.taskId }),
actions.getImageTeaserFilesRequest({ taskId: action.taskId }),
actions.getConfiramtionImageFileRequest({ taskId: action.taskId }),
])
)
);
getFilesRequest$ = createEffect(() =>
this.actions$.pipe(
ofType(actions.getFilesRequest),
flatMap((action) =>
this.eisPublicService
.EISPublicGetFilesResponse({
infoId: action.taskId,
})
.pipe(
catchError((err) => of<StrictHttpResponse<ResponseArgsOfIEnumerableOfFileDTO>>(err)),
map((response) => actions.getFilesResponse({ taskId: action.taskId, response }))
)
)
)
);
getFilesResponse$ = createEffect(() =>
this.actions$.pipe(
ofType(actions.getFilesResponse),
flatMap((action) => {
if (action.response.ok) {
return [
actions.clear({ taskId: action.taskId, section: 'file' }),
actions.addFiles({
files: action.response.body.result.map<FileDTO>((file) => ({
...file,
section: 'file',
taskId: action.taskId,
})),
}),
];
}
return NEVER;
})
)
);
getImageFilesRequest$ = createEffect(() =>
this.actions$.pipe(
ofType(actions.getImageFilesRequest),
flatMap((action) =>
this.eisPublicService
.EISPublicGetImagesResponse({
infoId: action.taskId,
})
.pipe(
catchError((err) => of<StrictHttpResponse<ResponseArgsOfIEnumerableOfFileDTO>>(err)),
map((response) => actions.getImageFilesResponse({ taskId: action.taskId, response }))
)
)
)
);
getImageFilesResponse$ = createEffect(() =>
this.actions$.pipe(
ofType(actions.getImageFilesResponse),
flatMap((action) => {
if (action.response.ok) {
return [
actions.clear({ taskId: action.taskId, section: 'image' }),
actions.addFiles({
files: action.response.body.result.map<FileDTO>((file) => ({
...file,
section: 'image',
taskId: action.taskId,
})),
}),
];
}
return NEVER;
})
)
);
getImageTeaserFilesRequest$ = createEffect(() =>
this.actions$.pipe(
ofType(actions.getImageTeaserFilesRequest),
flatMap((action) =>
this.eisPublicService
.EISPublicGetImagesResponse({
infoId: action.taskId,
})
.pipe(
catchError((err) => of<StrictHttpResponse<ResponseArgsOfIEnumerableOfFileDTO>>(err)),
map((response) => actions.getImageTeaserFilesResponse({ taskId: action.taskId, response }))
)
)
)
);
getImageTeaserFilesResponse$ = createEffect(() =>
this.actions$.pipe(
ofType(actions.getImageTeaserFilesResponse),
flatMap((action) => {
if (action.response.ok) {
return [
actions.clear({ taskId: action.taskId, section: 'teaser' }),
actions.addFiles({
files: action.response.body.result.map<FileDTO>((file) => ({
...file,
section: 'teaser',
taskId: action.taskId,
})),
}),
];
}
return NEVER;
})
)
);
getConfiramtionImageFileRequest$ = createEffect(() =>
this.actions$.pipe(
ofType(actions.getConfiramtionImageFileRequest),
flatMap((action) =>
this.eisPublicService
.EISPublicGetImagesResponse({
infoId: action.taskId,
})
.pipe(
catchError((err) => of<StrictHttpResponse<ResponseArgsOfIEnumerableOfFileDTO>>(err)),
map((response) => actions.getConfiramtionImageFileResponse({ taskId: action.taskId, response }))
)
)
)
);
getConfiramtionImageFileResponse$ = createEffect(() =>
this.actions$.pipe(
ofType(actions.getConfiramtionImageFileResponse),
flatMap((action) => {
if (action.response.ok) {
return [
actions.clear({ taskId: action.taskId, section: 'confirmation' }),
actions.addFiles({
files: action.response.body.result.map<FileDTO>((file) => ({
...file,
section: 'confirmation',
taskId: action.taskId,
})),
}),
];
}
return NEVER;
})
)
);
}

View File

@@ -0,0 +1,18 @@
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import * as selectors from './task-files.selectors';
import * as actions from './task-files.actions';
import { tap } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class TaskFilesFacade {
constructor(private store: Store<any>) {}
getFilesByTaskId$(taskId: number) {
return this.store.select(selectors.selectFilesByTaskId, taskId).pipe(tap(() => this.getFiles(taskId)));
}
getFiles(taskId: number) {
this.store.dispatch(actions.getFiles({ taskId }));
}
}

View File

@@ -0,0 +1,21 @@
import { createReducer, Action, on } from '@ngrx/store';
import { INITIAL_TASK_FILES_STATE, TaskFilesState, taskFilesAdapter } from './task-files.state';
import * as actions from './task-files.actions';
const _taskFilesReducer = createReducer<TaskFilesState>(
INITIAL_TASK_FILES_STATE,
on(actions.clear, (s, a) =>
taskFilesAdapter.removeMany(
Object.values(s.entities)
.filter((file) => file.taskId === a.taskId && file.section === a.section)
.map((file) => file.id),
s
)
),
on(actions.clearAll, (s) => taskFilesAdapter.removeAll(s)),
on(actions.addFiles, (s, a) => taskFilesAdapter.addAll(a.files, s))
);
export function taskFilesReducer(state: TaskFilesState, action: Action) {
return _taskFilesReducer(state, action);
}

View File

@@ -0,0 +1,10 @@
import { selectBranchState } from '../branch.selectors';
import { createSelector } from '@ngrx/store';
import { taskFilesAdapter } from './task-files.state';
import { FileDTO } from './defs';
export const selectTaskFilesState = createSelector(selectBranchState, (s) => s.taskFiles);
const { selectAll } = taskFilesAdapter.getSelectors(selectTaskFilesState);
export const selectFilesByTaskId = createSelector(selectAll, (s: FileDTO[], taskId: number) => s.filter((file) => file.taskId === taskId));

View File

@@ -1,4 +1,10 @@
import { EntityState } from '@ngrx/entity';
import { FileDTO } from '@swagger/eis';
import { EntityState, createEntityAdapter } from '@ngrx/entity';
import { FileDTO } from './defs';
export interface TaskFilesState extends EntityState<FileDTO> {}
export const taskFilesAdapter = createEntityAdapter<FileDTO>();
export const INITIAL_TASK_FILES_STATE: TaskFilesState = {
...taskFilesAdapter.getInitialState(),
};