blob: 329a94eb67b52d0ad1ff4bb4375c022cacf422fd [file] [log] [blame]
/********************************************************************************
* Copyright (c) 2020 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
********************************************************************************/
import {Injectable} from "@angular/core";
import {Actions, createEffect, ofType} from "@ngrx/effects";
import {Action} from "@ngrx/store";
import {concat, EMPTY, Observable, of, throwError} from "rxjs";
import {catchError, filter, ignoreElements, map, mergeMap, startWith, switchMap} from "rxjs/operators";
import {AttachmentsApiService} from "../../../../core/api/attachments";
import {MailApiService} from "../../../../core/api/mail";
import {arrayJoin, endWithObservable, ignoreError} from "../../../../util";
import {setErrorAction} from "../../../root/actions";
import {EErrorCode} from "../../../root/model";
import {
addAttachmentEntityAction,
deleteAttachmentsAction,
setAttachmentCacheAction,
submitAttachmentFormAction,
updateAttachmentTagsAction
} from "../../actions";
import {IAttachmentControlValue, IAttachmentError, IAttachmentFormValue} from "../../model";
import {FetchAttachmentsEffect} from "../fetch";
@Injectable({providedIn: "root"})
export class SubmitAttachmentsEffect {
public submit$ = createEffect(() => this.actions.pipe(
ofType(submitAttachmentFormAction),
switchMap((action) => {
return this.submit(action.statementId, action.taskId, action.value).pipe(
ignoreError()
);
})
));
public constructor(
public readonly actions: Actions,
public readonly attachmentsApiService: AttachmentsApiService,
public readonly fetchAttachmentsEffect: FetchAttachmentsEffect,
public readonly mailApiService: MailApiService
) {
}
public submit(
statementId: number,
taskId: string,
value: IAttachmentFormValue
): Observable<Action> {
const errors: IAttachmentError[] = [];
const edit = arrayJoin(value?.edit, value?.email).filter((_) => _?.id);
return concat(
this.editAttachments(statementId, taskId, edit, errors),
this.addAttachments(statementId, taskId, value?.add, errors),
this.transferEmailAttachments(statementId, taskId, value?.email, errors),
this.transferMailText(statementId, taskId, value?.transferMailText, value?.mailTextAttachmentId, errors),
this.fetchAttachmentsEffect.fetchAttachments(statementId)
).pipe(
endWithObservable(() => {
const lastError = errors.reverse()[0];
return lastError == null ? EMPTY : concat(
of(setErrorAction({statementId, error: lastError.message})),
throwError(lastError.error)
);
})
);
}
public transferMailText(
statementId: number,
taskId: string,
shouldBeAdded?: boolean,
mailTextAttachmentId?: number,
errors?: IAttachmentError[]
) {
const mailTextIsAlreadyAdded = mailTextAttachmentId == null;
if (shouldBeAdded && mailTextIsAlreadyAdded) {
return this.mailApiService.transferMailText(statementId, taskId).pipe(
map((entity) => addAttachmentEntityAction({statementId, entity})),
catchError((error) => {
errors.push({statementId, attachment: null, error, message: EErrorCode.FAILED_MAIL_TRANSFER});
return EMPTY;
})
);
}
if (!shouldBeAdded && !mailTextIsAlreadyAdded) {
return this.attachmentsApiService.deleteAttachment(statementId, taskId, mailTextAttachmentId).pipe(
map(() => deleteAttachmentsAction({statementId, entityIds: [mailTextAttachmentId]})),
catchError((error) => {
errors.push({statementId, attachment: null, error, message: EErrorCode.UNEXPECTED});
return EMPTY;
})
);
}
return EMPTY;
}
public transferEmailAttachments(statementId: number,
taskId: string,
emailAttachments: IAttachmentControlValue[],
errors: IAttachmentError[] = []): Observable<Action> {
const attachmentsToTransfer = arrayJoin(emailAttachments)
.filter((_) => _.isSelected && _.id == null)
.map((_) => ({name: _.name, tagIds: _.tagIds}));
return attachmentsToTransfer.length === 0 ? EMPTY :
this.mailApiService.transferMailAttachment(statementId, taskId, attachmentsToTransfer).pipe(
ignoreElements(),
catchError((error) => {
errors.push({statementId, attachment: null, error, message: EErrorCode.FAILED_MAIL_TRANSFER});
return EMPTY;
})
);
}
public editAttachments(
statementId: number,
taskId: string,
edit: IAttachmentControlValue[],
errors: IAttachmentError[] = []
): Observable<Action> {
return of(...arrayJoin(edit)).pipe(
filter((item) => item?.id != null),
mergeMap((item) => {
return this.editSingleAttachment(statementId, taskId, item.id, item.tagIds, !item.isSelected).pipe(
catchError((error) => {
errors.push({statementId, attachment: item, error, message: EErrorCode.UNEXPECTED});
return EMPTY;
})
);
}, 10),
startWith(updateAttachmentTagsAction({items: edit}))
);
}
public addAttachments(
statementId: number,
taskId: string,
add: IAttachmentControlValue[],
errors: IAttachmentError[] = []
): Observable<Action> {
const items: IAttachmentControlValue[] = [];
return of(...arrayJoin(add)).pipe(
filter((item) => item?.file instanceof File),
mergeMap((item) => {
return this.addSingleAttachment(statementId, taskId, item.file, item.tagIds).pipe(
catchError((error) => {
items.push(item);
errors.push({statementId, attachment: item, error, message: EErrorCode.FAILED_FILE_UPLOAD});
return EMPTY;
})
);
}, 2),
startWith(setAttachmentCacheAction({statementId, items: add})),
endWithObservable(() => of(setAttachmentCacheAction({statementId, items})))
);
}
private editSingleAttachment(
statementId: number,
taskId: string,
id: number,
tagIds: string[],
remove?: boolean
) {
return remove ?
this.attachmentsApiService.deleteAttachment(statementId, taskId, id).pipe(
map(() => deleteAttachmentsAction({statementId, entityIds: [id]}))
) :
this.attachmentsApiService.postAttachmentTags(statementId, taskId, id, ...arrayJoin(tagIds)).pipe(
ignoreElements(),
);
}
private addSingleAttachment(
statementId: number,
taskId: string,
file: File,
tagIds: string[]
): Observable<Action> {
return this.attachmentsApiService.postAttachment(statementId, taskId, file, ...arrayJoin(tagIds)).pipe(
map((entity) => addAttachmentEntityAction({statementId, entity}))
);
}
}