diff --git a/src/app/features/moderation/collection-moderation.routes.ts b/src/app/features/moderation/collection-moderation.routes.ts index 76d153d3b..eea233a4b 100644 --- a/src/app/features/moderation/collection-moderation.routes.ts +++ b/src/app/features/moderation/collection-moderation.routes.ts @@ -3,11 +3,12 @@ import { provideStates } from '@ngxs/store'; import { Routes } from '@angular/router'; import { CollectionsModerationState } from '@osf/features/moderation/store/collections-moderation'; -import { ResourceType } from '@osf/shared/enums/resource-type.enum'; +import { CurrentResourceType, ResourceType } from '@osf/shared/enums/resource-type.enum'; import { ActivityLogsState } from '@shared/stores/activity-logs'; import { CollectionsState } from '@shared/stores/collections'; import { ModeratorsState } from './store/moderators'; +import { ProviderSubscriptionsState } from './store/provider-subscriptions'; import { CollectionModerationTab } from './enums'; export const collectionModerationRoutes: Routes = [ @@ -46,7 +47,8 @@ export const collectionModerationRoutes: Routes = [ import('./components/notification-settings/notification-settings.component').then( (m) => m.NotificationSettingsComponent ), - data: { tab: CollectionModerationTab.Settings }, + data: { tab: CollectionModerationTab.Settings, resourceType: CurrentResourceType.Collections }, + providers: [provideStates([ProviderSubscriptionsState])], }, ], }, diff --git a/src/app/features/moderation/components/notification-settings/notification-settings.component.html b/src/app/features/moderation/components/notification-settings/notification-settings.component.html index 07e77467a..3ac4f1a3a 100644 --- a/src/app/features/moderation/components/notification-settings/notification-settings.component.html +++ b/src/app/features/moderation/components/notification-settings/notification-settings.component.html @@ -1,6 +1,42 @@ -

- {{ 'moderation.settingsMessage' | translate }} - +

{{ 'moderation.notificationPreferences.title' | translate }}

+

+ {{ 'moderation.notificationPreferences.note' | translate }} + {{ 'moderation.userSettings' | translate }}

+ +@if (!isLoading()) { +
+ @for (sub of subscriptions(); track sub.id) { + + + + + {{ selectedOption.label | translate }} + + + + {{ item.label | translate }} + + + } +
+} @else { +
+ @for (_ of [1, 2]; track $index) { + + + } +
+} diff --git a/src/app/features/moderation/components/notification-settings/notification-settings.component.scss b/src/app/features/moderation/components/notification-settings/notification-settings.component.scss index e69de29bb..2ce2707f4 100644 --- a/src/app/features/moderation/components/notification-settings/notification-settings.component.scss +++ b/src/app/features/moderation/components/notification-settings/notification-settings.component.scss @@ -0,0 +1,21 @@ +@use "styles/variables" as var; + +.notification-configuration { + display: grid; + gap: 12px; + align-items: center; + grid-template-columns: 0.5fr 2fr; + + .dropdown { + width: 50%; + } + + @media (max-width: var.$breakpoint-sm) { + grid-template-columns: 1fr; + row-gap: 0; + + .dropdown { + width: 100%; + } + } +} diff --git a/src/app/features/moderation/components/notification-settings/notification-settings.component.spec.ts b/src/app/features/moderation/components/notification-settings/notification-settings.component.spec.ts index a5411f8d7..9faeb1fd6 100644 --- a/src/app/features/moderation/components/notification-settings/notification-settings.component.spec.ts +++ b/src/app/features/moderation/components/notification-settings/notification-settings.component.spec.ts @@ -1,24 +1,137 @@ +import { Store } from '@ngxs/store'; + +import { MockProvider } from 'ng-mocks'; + import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ActivatedRoute } from '@angular/router'; + +import { SUBSCRIPTION_FREQUENCY_OPTIONS } from '@osf/shared/constants/subscription-options.const'; +import { CurrentResourceType } from '@osf/shared/enums/resource-type.enum'; +import { SubscriptionEvent } from '@osf/shared/enums/subscriptions/subscription-event.enum'; +import { SubscriptionFrequency } from '@osf/shared/enums/subscriptions/subscription-frequency.enum'; +import { NotificationSubscription } from '@osf/shared/models/notifications/notification-subscription.model'; +import { ToastService } from '@osf/shared/services/toast.service'; + +import { + GetProviderSubscriptions, + ProviderSubscriptionsSelectors, + UpdateProviderSubscription, +} from '../../store/provider-subscriptions'; import { NotificationSettingsComponent } from './notification-settings.component'; +import { ToastServiceMock } from '@testing/mocks/toast.service.mock'; import { OSFTestingModule } from '@testing/osf.testing.module'; +import { ActivatedRouteMockBuilder } from '@testing/providers/route-provider.mock'; +import { provideMockStore } from '@testing/providers/store-provider.mock'; + +const MOCK_PROVIDER_SUBSCRIPTIONS: NotificationSubscription[] = [ + { + id: 'sub-1', + event: SubscriptionEvent.ProviderNewPendingSubmissions, + frequency: SubscriptionFrequency.Instant, + }, + { + id: 'sub-2', + event: SubscriptionEvent.ProviderNewPendingWithdrawRequests, + frequency: SubscriptionFrequency.Never, + }, +]; + +async function createComponent(resourceType: CurrentResourceType, providerId = 'test-provider-123') { + const mockActivatedRoute = ActivatedRouteMockBuilder.create() + .withParams({ providerId }) + .withData({ resourceType }) + .build(); + + await TestBed.configureTestingModule({ + imports: [NotificationSettingsComponent, OSFTestingModule], + providers: [ + MockProvider(ActivatedRoute, mockActivatedRoute), + ToastServiceMock, + provideMockStore({ + signals: [ + { selector: ProviderSubscriptionsSelectors.getSubscriptions, value: MOCK_PROVIDER_SUBSCRIPTIONS }, + { selector: ProviderSubscriptionsSelectors.isLoading, value: false }, + ], + }), + ], + }).compileComponents(); + + const fixture = TestBed.createComponent(NotificationSettingsComponent); + const component = fixture.componentInstance; + const toastService = TestBed.inject(ToastService) as jest.Mocked; + const store = TestBed.inject(Store); + + return { fixture, component, toastService, store }; +} describe('NotificationSettingsComponent', () => { let component: NotificationSettingsComponent; let fixture: ComponentFixture; + let toastService: jest.Mocked; + let store: Store; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [NotificationSettingsComponent, OSFTestingModule], - }).compileComponents(); + const mockProviderId = 'test-provider-123'; - fixture = TestBed.createComponent(NotificationSettingsComponent); - component = fixture.componentInstance; - fixture.detectChanges(); + beforeEach(async () => { + ({ fixture, component, toastService, store } = await createComponent( + CurrentResourceType.Preprints, + mockProviderId + )); }); it('should create', () => { + fixture.detectChanges(); expect(component).toBeTruthy(); }); + + it('should read providerId and resourceType from route', () => { + fixture.detectChanges(); + expect(component.providerId()).toBe(mockProviderId); + expect(component.resourceType()).toBe(CurrentResourceType.Preprints); + }); + + it('should dispatch GetProviderSubscriptions on init', () => { + fixture.detectChanges(); + expect(store.dispatch as jest.Mock).toHaveBeenCalledWith( + new GetProviderSubscriptions(CurrentResourceType.Preprints, mockProviderId) + ); + }); + + it('should dispatch UpdateProviderSubscription and show toast on frequency change', () => { + fixture.detectChanges(); + + component.onFrequencyChange(MOCK_PROVIDER_SUBSCRIPTIONS[0], SubscriptionFrequency.Daily); + + expect(store.dispatch as jest.Mock).toHaveBeenCalledWith( + new UpdateProviderSubscription({ + providerType: CurrentResourceType.Preprints, + providerId: mockProviderId, + subscriptionId: 'sub-1', + frequency: SubscriptionFrequency.Daily, + }) + ); + expect(toastService.showSuccess).toHaveBeenCalledWith('moderation.notificationPreferences.successUpdate'); + }); + + it('should not dispatch UpdateProviderSubscription if frequency is unchanged', () => { + fixture.detectChanges(); + + component.onFrequencyChange(MOCK_PROVIDER_SUBSCRIPTIONS[0], SubscriptionFrequency.Instant); + + expect(store.dispatch as jest.Mock).not.toHaveBeenCalledWith(expect.any(UpdateProviderSubscription)); + }); + + it('should expose frequencyOptions from SUBSCRIPTION_FREQUENCY_OPTIONS', () => { + expect(component.frequencyOptions).toEqual(SUBSCRIPTION_FREQUENCY_OPTIONS); + }); + + it('should populate form controls when subscriptions load', () => { + fixture.detectChanges(); + expect(component.form.contains('sub-1')).toBe(true); + expect(component.form.contains('sub-2')).toBe(true); + expect(component.form.get('sub-1')?.value).toBe(SubscriptionFrequency.Instant); + expect(component.form.get('sub-2')?.value).toBe(SubscriptionFrequency.Never); + }); }); diff --git a/src/app/features/moderation/components/notification-settings/notification-settings.component.ts b/src/app/features/moderation/components/notification-settings/notification-settings.component.ts index ce5b3ccd3..0dbde36ec 100644 --- a/src/app/features/moderation/components/notification-settings/notification-settings.component.ts +++ b/src/app/features/moderation/components/notification-settings/notification-settings.component.ts @@ -1,13 +1,102 @@ +import { createDispatchMap, select } from '@ngxs/store'; + import { TranslatePipe } from '@ngx-translate/core'; -import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { RouterLink } from '@angular/router'; +import { Select } from 'primeng/select'; +import { Skeleton } from 'primeng/skeleton'; + +import { of } from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { ChangeDetectionStrategy, Component, DestroyRef, effect, inject, OnInit, Signal } from '@angular/core'; +import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop'; +import { FormBuilder, FormControl, FormRecord, ReactiveFormsModule } from '@angular/forms'; +import { ActivatedRoute, RouterLink } from '@angular/router'; + +import { SUBSCRIPTION_FREQUENCY_OPTIONS } from '@osf/shared/constants/subscription-options.const'; +import { CurrentResourceType } from '@osf/shared/enums/resource-type.enum'; +import { SubscriptionFrequency } from '@osf/shared/enums/subscriptions/subscription-frequency.enum'; +import { NotificationSubscription } from '@osf/shared/models/notifications/notification-subscription.model'; +import { ToastService } from '@osf/shared/services/toast.service'; + +import { + GetProviderSubscriptions, + ProviderSubscriptionsSelectors, + UpdateProviderSubscription, +} from '../../store/provider-subscriptions'; @Component({ selector: 'osf-notification-settings', - imports: [TranslatePipe, RouterLink], + imports: [TranslatePipe, RouterLink, ReactiveFormsModule, Select, Skeleton], templateUrl: './notification-settings.component.html', styleUrl: './notification-settings.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class NotificationSettingsComponent {} +export class NotificationSettingsComponent implements OnInit { + private readonly route = inject(ActivatedRoute); + private readonly fb = inject(FormBuilder); + private readonly toastService = inject(ToastService); + private readonly destroyRef = inject(DestroyRef); + + readonly providerId = toSignal( + this.route.parent?.params.pipe(map((params) => params['providerId'])) ?? of(undefined) + ); + readonly resourceType: Signal = toSignal( + this.route.data.pipe(map((params) => params['resourceType'])) + ); + + subscriptions = select(ProviderSubscriptionsSelectors.getSubscriptions); + isLoading = select(ProviderSubscriptionsSelectors.isLoading); + + readonly form = new FormRecord>({}); + + readonly frequencyOptions = SUBSCRIPTION_FREQUENCY_OPTIONS; + + private readonly actions = createDispatchMap({ + getProviderSubscriptions: GetProviderSubscriptions, + updateProviderSubscription: UpdateProviderSubscription, + }); + + constructor() { + effect(() => { + const subs = this.subscriptions(); + subs.forEach((sub) => { + const control = this.form.controls[sub.id]; + if (!control) { + this.form.addControl(sub.id, this.fb.control(sub.frequency, { nonNullable: true }), { emitEvent: false }); + return; + } + + if (control.value !== sub.frequency) { + control.setValue(sub.frequency, { emitEvent: false }); + } + }); + }); + } + + ngOnInit(): void { + const providerType = this.resourceType(); + const providerId = this.providerId(); + if (providerType && providerId) { + this.actions.getProviderSubscriptions(providerType, providerId); + } + } + + onFrequencyChange(sub: NotificationSubscription, frequency: SubscriptionFrequency): void { + if (sub.frequency === frequency) return; + + const providerType = this.resourceType(); + const providerId = this.providerId(); + if (!providerType || !providerId) return; + + this.actions + .updateProviderSubscription({ + providerType, + providerId, + subscriptionId: sub.id, + frequency, + }) + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe(() => this.toastService.showSuccess('moderation.notificationPreferences.successUpdate')); + } +} diff --git a/src/app/features/moderation/preprint-moderation.routes.ts b/src/app/features/moderation/preprint-moderation.routes.ts index be607da8e..943723162 100644 --- a/src/app/features/moderation/preprint-moderation.routes.ts +++ b/src/app/features/moderation/preprint-moderation.routes.ts @@ -2,10 +2,11 @@ import { provideStates } from '@ngxs/store'; import { Routes } from '@angular/router'; -import { ResourceType } from '@osf/shared/enums/resource-type.enum'; +import { CurrentResourceType, ResourceType } from '@osf/shared/enums/resource-type.enum'; import { ModeratorsState } from './store/moderators'; import { PreprintModerationState } from './store/preprint-moderation'; +import { ProviderSubscriptionsState } from './store/provider-subscriptions'; import { PreprintModerationTab } from './enums'; export const preprintModerationRoutes: Routes = [ @@ -51,7 +52,8 @@ export const preprintModerationRoutes: Routes = [ import('./components/notification-settings/notification-settings.component').then( (m) => m.NotificationSettingsComponent ), - data: { tab: PreprintModerationTab.Notifications }, + data: { tab: PreprintModerationTab.Notifications, resourceType: CurrentResourceType.Preprints }, + providers: [provideStates([ProviderSubscriptionsState])], }, { path: 'settings', diff --git a/src/app/features/moderation/registry-moderation.routes.ts b/src/app/features/moderation/registry-moderation.routes.ts index 7afada056..2cebabc30 100644 --- a/src/app/features/moderation/registry-moderation.routes.ts +++ b/src/app/features/moderation/registry-moderation.routes.ts @@ -2,9 +2,10 @@ import { provideStates } from '@ngxs/store'; import { Routes } from '@angular/router'; -import { ResourceType } from '@osf/shared/enums/resource-type.enum'; +import { CurrentResourceType, ResourceType } from '@osf/shared/enums/resource-type.enum'; import { ModeratorsState } from './store/moderators'; +import { ProviderSubscriptionsState } from './store/provider-subscriptions'; import { RegistryModerationState } from './store/registry-moderation'; import { RegistryModerationTab } from './enums'; @@ -48,8 +49,11 @@ export const registryModerationRoutes: Routes = [ { path: 'settings', loadComponent: () => - import('./components/registry-settings/registry-settings.component').then((m) => m.RegistrySettingsComponent), - data: { tab: RegistryModerationTab.Settings }, + import('./components/notification-settings/notification-settings.component').then( + (m) => m.NotificationSettingsComponent + ), + data: { tab: RegistryModerationTab.Settings, resourceType: CurrentResourceType.Registrations }, + providers: [provideStates([ProviderSubscriptionsState])], }, ], }, diff --git a/src/app/features/moderation/services/index.ts b/src/app/features/moderation/services/index.ts index 9ef953442..6a70accb5 100644 --- a/src/app/features/moderation/services/index.ts +++ b/src/app/features/moderation/services/index.ts @@ -1,3 +1,4 @@ export { ModeratorsService } from './moderators.service'; export { PreprintModerationService } from './preprint-moderation.service'; +export { ProviderSubscriptionService } from './provider-subscription.service'; export { RegistryModerationService } from './registry-moderation.service'; diff --git a/src/app/features/moderation/services/provider-subscription.service.ts b/src/app/features/moderation/services/provider-subscription.service.ts new file mode 100644 index 000000000..21277c50b --- /dev/null +++ b/src/app/features/moderation/services/provider-subscription.service.ts @@ -0,0 +1,56 @@ +import { map, Observable } from 'rxjs'; + +import { inject, Injectable } from '@angular/core'; + +import { ENVIRONMENT } from '@core/provider/environment.provider'; +import { SubscriptionFrequency } from '@osf/shared/enums/subscriptions/subscription-frequency.enum'; +import { SubscriptionType } from '@osf/shared/enums/subscriptions/subscription-type.enum'; +import { NotificationSubscriptionMapper } from '@osf/shared/mappers/notification-subscription.mapper'; +import { JsonApiResponse } from '@osf/shared/models/common/json-api.model'; +import { NotificationSubscription } from '@osf/shared/models/notifications/notification-subscription.model'; +import { NotificationSubscriptionGetResponseJsonApi } from '@osf/shared/models/notifications/notification-subscription-json-api.model'; +import { JsonApiService } from '@osf/shared/services/json-api.service'; + +@Injectable({ + providedIn: 'root', +}) +export class ProviderSubscriptionService { + private readonly jsonApiService = inject(JsonApiService); + private readonly environment = inject(ENVIRONMENT); + + private providerUrl(providerType: string, providerId: string): string { + return `${this.environment.apiDomainUrl}/v2/providers/${providerType}/${providerId}/subscriptions/`; + } + + getProviderSubscriptions(providerType: string, providerId: string): Observable { + return this.jsonApiService + .get< + JsonApiResponse + >(this.providerUrl(providerType, providerId)) + .pipe( + map((responses) => responses.data.map((response) => NotificationSubscriptionMapper.fromGetResponse(response))) + ); + } + + updateProviderSubscription( + providerType: string, + providerId: string, + subscriptionId: string, + frequency: SubscriptionFrequency + ): Observable { + const request = { + data: { + id: subscriptionId, + type: SubscriptionType.Node, + attributes: { frequency }, + }, + }; + + return this.jsonApiService + .patch( + `${this.providerUrl(providerType, providerId)}${subscriptionId}/`, + request + ) + .pipe(map((response) => NotificationSubscriptionMapper.fromGetResponse(response))); + } +} diff --git a/src/app/features/moderation/store/provider-subscriptions/index.ts b/src/app/features/moderation/store/provider-subscriptions/index.ts new file mode 100644 index 000000000..067ead0ef --- /dev/null +++ b/src/app/features/moderation/store/provider-subscriptions/index.ts @@ -0,0 +1,4 @@ +export * from './provider-subscriptions.actions'; +export * from './provider-subscriptions.model'; +export * from './provider-subscriptions.selectors'; +export * from './provider-subscriptions.state'; diff --git a/src/app/features/moderation/store/provider-subscriptions/provider-subscriptions.actions.ts b/src/app/features/moderation/store/provider-subscriptions/provider-subscriptions.actions.ts new file mode 100644 index 000000000..f286ba90f --- /dev/null +++ b/src/app/features/moderation/store/provider-subscriptions/provider-subscriptions.actions.ts @@ -0,0 +1,23 @@ +import { SubscriptionFrequency } from '@osf/shared/enums/subscriptions/subscription-frequency.enum'; + +export class GetProviderSubscriptions { + static readonly type = '[Provider Subscriptions] Get'; + + constructor( + public providerType: string, + public providerId: string + ) {} +} + +export class UpdateProviderSubscription { + static readonly type = '[Provider Subscriptions] Update'; + + constructor( + public payload: { + providerType: string; + providerId: string; + subscriptionId: string; + frequency: SubscriptionFrequency; + } + ) {} +} diff --git a/src/app/features/moderation/store/provider-subscriptions/provider-subscriptions.model.ts b/src/app/features/moderation/store/provider-subscriptions/provider-subscriptions.model.ts new file mode 100644 index 000000000..3e369a169 --- /dev/null +++ b/src/app/features/moderation/store/provider-subscriptions/provider-subscriptions.model.ts @@ -0,0 +1,14 @@ +import { NotificationSubscription } from '@osf/shared/models/notifications/notification-subscription.model'; +import { AsyncStateModel } from '@osf/shared/models/store/async-state.model'; + +export interface ProviderSubscriptionsStateModel { + subscriptions: AsyncStateModel; +} + +export const PROVIDER_SUBSCRIPTIONS_STATE_DEFAULTS: ProviderSubscriptionsStateModel = { + subscriptions: { + data: [], + isLoading: false, + error: '', + }, +}; diff --git a/src/app/features/moderation/store/provider-subscriptions/provider-subscriptions.selectors.ts b/src/app/features/moderation/store/provider-subscriptions/provider-subscriptions.selectors.ts new file mode 100644 index 000000000..897122177 --- /dev/null +++ b/src/app/features/moderation/store/provider-subscriptions/provider-subscriptions.selectors.ts @@ -0,0 +1,18 @@ +import { Selector } from '@ngxs/store'; + +import { NotificationSubscription } from '@osf/shared/models/notifications/notification-subscription.model'; + +import { ProviderSubscriptionsStateModel } from './provider-subscriptions.model'; +import { ProviderSubscriptionsState } from './provider-subscriptions.state'; + +export class ProviderSubscriptionsSelectors { + @Selector([ProviderSubscriptionsState]) + static getSubscriptions(state: ProviderSubscriptionsStateModel): NotificationSubscription[] { + return state.subscriptions.data; + } + + @Selector([ProviderSubscriptionsState]) + static isLoading(state: ProviderSubscriptionsStateModel): boolean { + return state.subscriptions.isLoading; + } +} diff --git a/src/app/features/moderation/store/provider-subscriptions/provider-subscriptions.state.ts b/src/app/features/moderation/store/provider-subscriptions/provider-subscriptions.state.ts new file mode 100644 index 000000000..1bacb2f53 --- /dev/null +++ b/src/app/features/moderation/store/provider-subscriptions/provider-subscriptions.state.ts @@ -0,0 +1,70 @@ +import { Action, State, StateContext } from '@ngxs/store'; +import { patch, updateItem } from '@ngxs/store/operators'; + +import { catchError, tap } from 'rxjs'; + +import { inject, Injectable } from '@angular/core'; + +import { handleSectionError } from '@osf/shared/helpers/state-error.handler'; +import { NotificationSubscription } from '@osf/shared/models/notifications/notification-subscription.model'; + +import { ProviderSubscriptionService } from '../../services'; + +import { GetProviderSubscriptions, UpdateProviderSubscription } from './provider-subscriptions.actions'; +import { PROVIDER_SUBSCRIPTIONS_STATE_DEFAULTS, ProviderSubscriptionsStateModel } from './provider-subscriptions.model'; + +@State({ + name: 'providerSubscriptions', + defaults: PROVIDER_SUBSCRIPTIONS_STATE_DEFAULTS, +}) +@Injectable() +export class ProviderSubscriptionsState { + private readonly providerSubscriptionService = inject(ProviderSubscriptionService); + + @Action(GetProviderSubscriptions) + getProviderSubscriptions(ctx: StateContext, action: GetProviderSubscriptions) { + ctx.setState(patch({ subscriptions: patch({ isLoading: true }) })); + + return this.providerSubscriptionService.getProviderSubscriptions(action.providerType, action.providerId).pipe( + tap((subscriptions) => { + ctx.setState( + patch({ + subscriptions: patch({ + data: subscriptions, + isLoading: false, + }), + }) + ); + }), + catchError((error) => handleSectionError(ctx, 'subscriptions', error)) + ); + } + + @Action(UpdateProviderSubscription) + updateProviderSubscription(ctx: StateContext, action: UpdateProviderSubscription) { + return this.providerSubscriptionService + .updateProviderSubscription( + action.payload.providerType, + action.payload.providerId, + action.payload.subscriptionId, + action.payload.frequency + ) + .pipe( + tap((updatedSubscription) => { + ctx.setState( + patch({ + subscriptions: patch({ + data: updateItem( + (sub) => sub.id === action.payload.subscriptionId, + updatedSubscription + ), + error: null, + isLoading: false, + }), + }) + ); + }), + catchError((error) => handleSectionError(ctx, 'subscriptions', error)) + ); + } +} diff --git a/src/app/shared/constants/subscription-options.const.ts b/src/app/shared/constants/subscription-options.const.ts new file mode 100644 index 000000000..b36694531 --- /dev/null +++ b/src/app/shared/constants/subscription-options.const.ts @@ -0,0 +1,7 @@ +import { SubscriptionFrequency } from '../enums/subscriptions/subscription-frequency.enum'; + +export const SUBSCRIPTION_FREQUENCY_OPTIONS = [ + { label: 'settings.notifications.frequency.never', value: SubscriptionFrequency.Never }, + { label: 'settings.notifications.frequency.daily', value: SubscriptionFrequency.Daily }, + { label: 'settings.notifications.frequency.instant', value: SubscriptionFrequency.Instant }, +]; diff --git a/src/app/shared/enums/subscriptions/subscription-event.enum.ts b/src/app/shared/enums/subscriptions/subscription-event.enum.ts index 3ce040bfe..e5732ac55 100644 --- a/src/app/shared/enums/subscriptions/subscription-event.enum.ts +++ b/src/app/shared/enums/subscriptions/subscription-event.enum.ts @@ -2,4 +2,6 @@ export enum SubscriptionEvent { GlobalFileUpdated = 'global_file_updated', GlobalReviews = 'global_reviews', FileUpdated = 'file_updated', + ProviderNewPendingSubmissions = 'provider_new_pending_submissions', + ProviderNewPendingWithdrawRequests = 'provider_new_pending_withdraw_requests', } diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index e31380f1c..67594ed43 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1567,6 +1567,15 @@ "titleZA": "Title: Z-A", "oldest": "Date: oldest to newest", "newest": "Date: newest to oldest" + }, + "notificationPreferences": { + "title": "Configure reviews notification preferences", + "note": "To configure other notification preferences visit your", + "items": { + "provider_new_pending_submissions": "New pending submissions", + "provider_new_pending_withdraw_requests": "New pending withdraw requests" + }, + "successUpdate": "Notification preference updated successfully" } }, "settings": { @@ -1702,6 +1711,11 @@ "preprints": "Preprint submissions updated" }, "successUpdate": "Notification preferences successfully updated." + }, + "frequency": { + "daily": "Daily", + "instant": "Instant", + "never": "Never" } }, "addons": {