import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { BehaviorSubject, combineLatest, from, shareReplay } from 'rxjs'; import { map, mergeMap, take, tap } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap'; import findIndex from 'lodash/findIndex'; import { Subscription } from '../models/subscription.model'; import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { SubscriptionsDataService } from '../subscriptions-data.service'; import { NotificationsService } from '../../notifications/notifications.service'; import { PaginatedList } from '../../../core/data/paginated-list.model'; import { RemoteData } from '../../../core/data/remote-data'; import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../../../core/shared/operators'; import { AuthService } from '../../../core/auth/auth.service'; import { isNotEmpty } from '../../empty.util'; @Component({ selector: 'ds-subscription-modal', templateUrl: './subscription-modal.component.html', styleUrls: ['./subscription-modal.component.scss'] }) /** * Modal that allows to manage the subscriptions for the selected item */ export class SubscriptionModalComponent implements OnInit { /** * DSpaceObject of which to get the subscriptions */ @Input() dso: DSpaceObject; /** * If given the subscription to edit by the form */ @Input() subscription: Subscription; /** * The eperson related to the subscription */ ePersonId: string; /** * A boolean representing if a request operation is pending * @type {BehaviorSubject} */ public processing$ = new BehaviorSubject(false); /** * If true, show a message explaining how to delete a subscription */ public showDeleteInfo$ = new BehaviorSubject(false); /** * Reactive form group that will be used to add/edit subscriptions */ subscriptionForm: UntypedFormGroup; /** * Used to show validation errors when user submits */ submitted = false; /** * Types of subscription to be shown on select */ subscriptionDefaultTypes = ['content']; /** * Frequencies to be shown as checkboxes */ frequencyDefaultValues = ['D', 'W', 'M']; /** * True if form status has changed and at least one frequency is checked */ isValid = false; /** * Event emitted when a given subscription has been updated */ @Output() updateSubscription: EventEmitter = new EventEmitter(); constructor( private formBuilder: UntypedFormBuilder, private modalService: NgbModal, private notificationsService: NotificationsService, private subscriptionService: SubscriptionsDataService, public activeModal: NgbActiveModal, private authService: AuthService, private translate: TranslateService, ) { } /** * When component starts initialize starting functionality */ ngOnInit(): void { this.authService.getAuthenticatedUserFromStore().pipe( take(1), map((ePerson) => ePerson.uuid), shareReplay(), ).subscribe((ePersonId: string) => { this.ePersonId = ePersonId; if (isNotEmpty(this.subscription)) { this.initFormByGivenSubscription(); } else { this.initFormByAllSubscriptions(); } }); this.subscriptionForm.valueChanges.subscribe((newValue) => { let anyFrequencySelected = false; for (let f of this.frequencyDefaultValues) { anyFrequencySelected = anyFrequencySelected || newValue.content.frequencies[f]; } this.isValid = anyFrequencySelected; }); } initFormByAllSubscriptions(): void { this.subscriptionForm = new UntypedFormGroup({}); for (let t of this.subscriptionDefaultTypes) { const formGroup = new UntypedFormGroup({}); formGroup.addControl('subscriptionId', this.formBuilder.control('')); formGroup.addControl('frequencies', this.formBuilder.group({})); for (let f of this.frequencyDefaultValues) { (formGroup.controls.frequencies as UntypedFormGroup).addControl(f, this.formBuilder.control(false)); } this.subscriptionForm.addControl(t, formGroup); } this.initFormDataBySubscriptions(); } /** * If the subscription is passed start the form with the information of subscription */ initFormByGivenSubscription(): void { const formGroup = new UntypedFormGroup({}); formGroup.addControl('subscriptionId', this.formBuilder.control(this.subscription.id)); formGroup.addControl('frequencies', this.formBuilder.group({})); (formGroup.get('frequencies') as UntypedFormGroup).addValidators(Validators.required); for (let f of this.frequencyDefaultValues) { const value = findIndex(this.subscription.subscriptionParameterList, ['value', f]) !== -1; (formGroup.controls.frequencies as UntypedFormGroup).addControl(f, this.formBuilder.control(value)); } this.subscriptionForm = this.formBuilder.group({ [this.subscription.subscriptionType]: formGroup }); } /** * Get subscriptions for the current ePerson & dso object relation. * If there are no subscriptions then start with an empty form. */ initFormDataBySubscriptions(): void { this.processing$.next(true); this.subscriptionService.getSubscriptionsByPersonDSO(this.ePersonId, this.dso?.uuid).pipe( getFirstSucceededRemoteDataPayload(), ).subscribe({ next: (res: PaginatedList) => { if (res.pageInfo.totalElements > 0) { this.showDeleteInfo$.next(true); for (let subscription of res.page) { const type = subscription.subscriptionType; const subscriptionGroup: UntypedFormGroup = this.subscriptionForm.get(type) as UntypedFormGroup; if (isNotEmpty(subscriptionGroup)) { subscriptionGroup.controls.subscriptionId.setValue(subscription.id); for (let parameter of subscription.subscriptionParameterList.filter((p) => p.name === 'frequency')) { (subscriptionGroup.controls.frequencies as UntypedFormGroup).controls[parameter.value]?.setValue(true); } } } } this.processing$.next(false); }, error: err => { this.processing$.next(false); } }); } /** * Create/update subscriptions if needed */ submit() { this.submitted = true; const subscriptionTypes: string[] = Object.keys(this.subscriptionForm.controls); const subscriptionsToBeCreated = []; const subscriptionsToBeUpdated = []; subscriptionTypes.forEach((subscriptionType: string) => { const subscriptionGroup: UntypedFormGroup = this.subscriptionForm.controls[subscriptionType] as UntypedFormGroup; if (subscriptionGroup.touched && subscriptionGroup.dirty) { const body = this.createBody( subscriptionGroup.controls.subscriptionId.value, subscriptionType, subscriptionGroup.controls.frequencies as UntypedFormGroup ); if (isNotEmpty(body.id)) { subscriptionsToBeUpdated.push(body); } else if (isNotEmpty(body.subscriptionParameterList)) { subscriptionsToBeCreated.push(body); } } }); const toBeProcessed = []; if (isNotEmpty(subscriptionsToBeCreated)) { toBeProcessed.push(from(subscriptionsToBeCreated).pipe( mergeMap((subscriptionBody) => { return this.subscriptionService.createSubscription(subscriptionBody, this.ePersonId, this.dso.uuid).pipe( getFirstCompletedRemoteData() ); }), tap((res: RemoteData) => { if (res.hasSucceeded) { const msg = this.translate.instant('subscriptions.modal.create.success', { type: res.payload.subscriptionType }); this.notificationsService.success(null, msg); } else { this.notificationsService.error(null, this.translate.instant('subscriptions.modal.create.error')); } }) )); } if (isNotEmpty(subscriptionsToBeUpdated)) { toBeProcessed.push(from(subscriptionsToBeUpdated).pipe( mergeMap((subscriptionBody) => { return this.subscriptionService.updateSubscription(subscriptionBody, this.ePersonId, this.dso.uuid).pipe( getFirstCompletedRemoteData() ); }), tap((res: RemoteData) => { if (res.hasSucceeded) { const msg = this.translate.instant('subscriptions.modal.update.success', { type: res.payload.subscriptionType }); this.notificationsService.success(null, msg); if (isNotEmpty(this.subscription)) { this.updateSubscription.emit(res.payload); } } else { this.notificationsService.error(null, this.translate.instant('subscriptions.modal.update.error')); } }) )); } combineLatest([...toBeProcessed]).subscribe((res) => { this.activeModal.close(); }); } private createBody(subscriptionId: string, subscriptionType: string, frequencies: UntypedFormGroup): Partial { const body = { id: (isNotEmpty(subscriptionId) ? subscriptionId : null), subscriptionType: subscriptionType, subscriptionParameterList: [] }; for (let frequency of this.frequencyDefaultValues) { if (frequencies.value[frequency]) { body.subscriptionParameterList.push( { name: 'frequency', value: frequency, } ); } } return body; } }