|
| 1 | +import { createDispatchMap } from '@ngxs/store'; |
| 2 | + |
| 3 | +import { TranslatePipe, TranslateService } from '@ngx-translate/core'; |
| 4 | + |
| 5 | +import { Button } from 'primeng/button'; |
| 6 | +import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; |
| 7 | +import { Message } from 'primeng/message'; |
| 8 | +import { Textarea } from 'primeng/textarea'; |
| 9 | + |
| 10 | +import { TitleCasePipe } from '@angular/common'; |
| 11 | +import { ChangeDetectionStrategy, Component, inject, OnInit, signal } from '@angular/core'; |
| 12 | +import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms'; |
| 13 | + |
| 14 | +import { formInputLimits } from '@osf/features/preprints/constants'; |
| 15 | +import { ProviderReviewsWorkflow, ReviewsState } from '@osf/features/preprints/enums'; |
| 16 | +import { getPreprintDocumentType } from '@osf/features/preprints/helpers'; |
| 17 | +import { Preprint, PreprintProviderDetails } from '@osf/features/preprints/models'; |
| 18 | +import { WithdrawPreprint } from '@osf/features/preprints/store/preprint'; |
| 19 | +import { INPUT_VALIDATION_MESSAGES } from '@shared/constants'; |
| 20 | +import { CustomValidators } from '@shared/utils'; |
| 21 | + |
| 22 | +@Component({ |
| 23 | + selector: 'osf-withdraw-dialog', |
| 24 | + imports: [Textarea, ReactiveFormsModule, Message, TranslatePipe, Button, TitleCasePipe], |
| 25 | + templateUrl: './withdraw-dialog.component.html', |
| 26 | + styleUrl: './withdraw-dialog.component.scss', |
| 27 | + changeDetection: ChangeDetectionStrategy.OnPush, |
| 28 | +}) |
| 29 | +export class WithdrawDialogComponent implements OnInit { |
| 30 | + private readonly config = inject(DynamicDialogConfig); |
| 31 | + private readonly translateService = inject(TranslateService); |
| 32 | + readonly dialogRef = inject(DynamicDialogRef); |
| 33 | + |
| 34 | + private provider!: PreprintProviderDetails; |
| 35 | + private preprint!: Preprint; |
| 36 | + |
| 37 | + private actions = createDispatchMap({ |
| 38 | + withdrawPreprint: WithdrawPreprint, |
| 39 | + }); |
| 40 | + |
| 41 | + protected inputLimits = formInputLimits; |
| 42 | + protected readonly INPUT_VALIDATION_MESSAGES = INPUT_VALIDATION_MESSAGES; |
| 43 | + |
| 44 | + withdrawalJustificationFormControl = new FormControl('', { |
| 45 | + nonNullable: true, |
| 46 | + validators: [ |
| 47 | + CustomValidators.requiredTrimmed(), |
| 48 | + Validators.minLength(this.inputLimits.withdrawalJustification.minLength), |
| 49 | + ], |
| 50 | + }); |
| 51 | + modalExplanation = signal<string>(''); |
| 52 | + withdrawRequestInProgress = signal<boolean>(false); |
| 53 | + |
| 54 | + public ngOnInit() { |
| 55 | + this.provider = this.config.data.provider; |
| 56 | + this.preprint = this.config.data.preprint; |
| 57 | + |
| 58 | + this.modalExplanation.set(this.calculateModalExplanation()); |
| 59 | + } |
| 60 | + |
| 61 | + withdraw() { |
| 62 | + if (this.withdrawalJustificationFormControl.invalid) { |
| 63 | + return; |
| 64 | + } |
| 65 | + |
| 66 | + const withdrawalJustification = this.withdrawalJustificationFormControl.value; |
| 67 | + this.withdrawRequestInProgress.set(true); |
| 68 | + this.actions.withdrawPreprint(this.preprint.id, withdrawalJustification).subscribe({ |
| 69 | + complete: () => { |
| 70 | + this.withdrawRequestInProgress.set(false); |
| 71 | + this.dialogRef.close(true); |
| 72 | + }, |
| 73 | + error: () => { |
| 74 | + this.withdrawRequestInProgress.set(false); |
| 75 | + }, |
| 76 | + }); |
| 77 | + } |
| 78 | + |
| 79 | + private calculateModalExplanation() { |
| 80 | + const providerReviewWorkflow = this.provider.reviewsWorkflow; |
| 81 | + const documentType = getPreprintDocumentType(this.provider, this.translateService); |
| 82 | + //[RNi] TODO: maybe extract to env, also see static pages |
| 83 | + const supportEmail = 'support@osf.io'; |
| 84 | + |
| 85 | + switch (providerReviewWorkflow) { |
| 86 | + case ProviderReviewsWorkflow.PreModeration: { |
| 87 | + if (this.preprint.reviewsState === ReviewsState.Pending) { |
| 88 | + return this.translateService.instant( |
| 89 | + 'Since this version is still pending approval and private, it can be withdrawn immediately. ' + |
| 90 | + 'The reason of withdrawal will be visible to service moderators. Once withdrawn, the {{singularPreprintWord}} ' + |
| 91 | + 'will remain private and never be made public.', |
| 92 | + { |
| 93 | + singularPreprintWord: documentType.singular, |
| 94 | + } |
| 95 | + ); |
| 96 | + } else |
| 97 | + return this.translateService.instant( |
| 98 | + '<strong>{{pluralCapitalizedPreprintWord}} are a permanent part of the scholarly record.' + |
| 99 | + ' Withdrawal requests are subject to this service’s policy on {{singularPreprintWord}} version' + |
| 100 | + ' removal and at the discretion of the moderators.</strong><br>This service uses pre-moderation. ' + |
| 101 | + 'This request will be submitted to service moderators for review. If the request is approved, this ' + |
| 102 | + '{singularPreprintWord} version will be replaced by a tombstone page with metadata and the reason ' + |
| 103 | + 'for withdrawal. This {singularPreprintWord} version will still be searchable by other users after removal.', |
| 104 | + { |
| 105 | + singularPreprintWord: documentType.singular, |
| 106 | + pluralCapitalizedPreprintWord: documentType.pluralCapitalized, |
| 107 | + } |
| 108 | + ); |
| 109 | + } |
| 110 | + case ProviderReviewsWorkflow.PostModeration: { |
| 111 | + return this.translateService.instant( |
| 112 | + '<strong>{pluralCapitalizedPreprintWord} are a permanent part of the scholarly record. ' + |
| 113 | + 'Withdrawal requests are subject to this service’s policy on {singularPreprintWord} version ' + |
| 114 | + 'removal and at the discretion of the moderators.</strong><br>This service uses post-moderation.' + |
| 115 | + ' This request will be submitted to service moderators for review. If the request is approved, this ' + |
| 116 | + '{singularPreprintWord} version will be replaced by a tombstone page with metadata and the reason for' + |
| 117 | + ' withdrawal. This {singularPreprintWord} version will still be searchable by other users after removal.', |
| 118 | + { |
| 119 | + singularPreprintWord: documentType.singular, |
| 120 | + pluralCapitalizedPreprintWord: documentType.pluralCapitalized, |
| 121 | + } |
| 122 | + ); |
| 123 | + } |
| 124 | + default: { |
| 125 | + return this.translateService.instant( |
| 126 | + '<strong>{pluralCapitalizedPreprintWord} are a permanent part of the scholarly record. ' + |
| 127 | + 'Withdrawal requests are subject to this service’s policy on {singularPreprintWord} version removal' + |
| 128 | + ' and at the discretion of the moderators.</strong><br>This request will be submitted to' + |
| 129 | + ' <a href="mailto:{supportEmail}" target="_blank">{supportEmail}</a> for review and removal.' + |
| 130 | + ' If the request is approved, this {singularPreprintWord} version will be replaced by a tombstone' + |
| 131 | + ' page with metadata and the reason for withdrawal. This {singularPreprintWord} version will still be ' + |
| 132 | + 'searchable by other users after removal.', |
| 133 | + { |
| 134 | + singularPreprintWord: documentType.singular, |
| 135 | + pluralCapitalizedPreprintWord: documentType.pluralCapitalized, |
| 136 | + supportEmail, |
| 137 | + } |
| 138 | + ); |
| 139 | + } |
| 140 | + } |
| 141 | + } |
| 142 | +} |
0 commit comments