From 111bfc1171a410ba73b5c9a3eccfe8e14f91bcbe Mon Sep 17 00:00:00 2001 From: John Tordoff Date: Thu, 30 Oct 2025 08:52:57 -0400 Subject: [PATCH 1/3] feat(enums): update enum for notification refactor node settings page --- .../shared/enums/subscriptions/subscription-frequency.enum.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/enums/subscriptions/subscription-frequency.enum.ts b/src/app/shared/enums/subscriptions/subscription-frequency.enum.ts index 2c9f96421..660924152 100644 --- a/src/app/shared/enums/subscriptions/subscription-frequency.enum.ts +++ b/src/app/shared/enums/subscriptions/subscription-frequency.enum.ts @@ -1,5 +1,5 @@ export enum SubscriptionFrequency { Never = 'none', Daily = 'daily', - Instant = 'instant', + Instant = 'instantly', } From 9aa505d811b43c8205a091a4e1bef6b3762ccd32 Mon Sep 17 00:00:00 2001 From: John Tordoff Date: Fri, 14 Nov 2025 08:36:35 -0500 Subject: [PATCH 2/3] [ENG-9758] fix(notifications): change settings page to default to correct notif (#761) - Ticket: https://openscience.atlassian.net/browse/ENG-9758 - Feature flag: n/a ## Purpose Make the notification preferences default and change to correct values. Corrosponses with this PR: https://github.com/CenterForOpenScience/osf.io/pull/11441 ## Summary of Changes Simple logic changes to make dropdowns functional --- .../constants/notifications-constants.ts | 11 +++++ .../notifications.component.spec.ts | 28 ++++++++---- .../notifications/notifications.component.ts | 43 ++++++++++++++++--- .../subscriptions/subscription-event.enum.ts | 2 +- 4 files changed, 69 insertions(+), 15 deletions(-) diff --git a/src/app/features/settings/notifications/constants/notifications-constants.ts b/src/app/features/settings/notifications/constants/notifications-constants.ts index 1988f384c..399e99dd0 100644 --- a/src/app/features/settings/notifications/constants/notifications-constants.ts +++ b/src/app/features/settings/notifications/constants/notifications-constants.ts @@ -12,3 +12,14 @@ export const SUBSCRIPTION_EVENTS: SubscriptionEventModel[] = [ labelKey: 'settings.notifications.notificationPreferences.items.preprints', }, ]; + +export const FORM_EVENT_TO_API_EVENT: Record = { + new_pending_submissions: 'new_pending_submissions', + files_updated: 'files_updated', + global_file_updated: 'global_file_updated', +}; + +export const API_EVENT_TO_FORM_EVENT: Record = { + new_pending_submissions: 'new_pending_submissions', + files_updated: 'global_file_updated', +}; diff --git a/src/app/features/settings/notifications/notifications.component.spec.ts b/src/app/features/settings/notifications/notifications.component.spec.ts index 20bbb53af..03950cb0c 100644 --- a/src/app/features/settings/notifications/notifications.component.spec.ts +++ b/src/app/features/settings/notifications/notifications.component.spec.ts @@ -41,13 +41,19 @@ describe('NotificationsComponent', () => { subscribeOsfHelpEmail: false, }; + // new_pending_submissions → global_reviews + // files_updated → global_file_updated const mockNotificationSubscriptions = [ - { id: 'id1', event: SubscriptionEvent.GlobalFileUpdated, frequency: SubscriptionFrequency.Daily }, { - id: 'id2', - event: SubscriptionEvent.GlobalFileUpdated, + id: 'osf_new_pending_submissions', + event: 'new_pending_submissions', frequency: SubscriptionFrequency.Instant, }, + { + id: 'cuzg4_global_file_updated', + event: 'files_updated', + frequency: SubscriptionFrequency.Daily, + }, ]; beforeEach(async () => { @@ -77,7 +83,7 @@ describe('NotificationsComponent', () => { return signal(null); }); - MOCK_STORE.dispatch.mockImplementation(() => of()); + MOCK_STORE.dispatch.mockReturnValue(of({})); await TestBed.configureTestingModule({ imports: [ @@ -116,9 +122,9 @@ describe('NotificationsComponent', () => { return signal(null); }); - component.emailPreferencesFormSubmit(); - expect(loaderService.hide).not.toHaveBeenCalled(); + component.emailPreferencesFormSubmit(); + expect(loaderService.hide).toHaveBeenCalledTimes(1); }); it('should handle subscription completion correctly', () => { @@ -136,11 +142,15 @@ describe('NotificationsComponent', () => { it('should call dispatch only once per subscription change', () => { const mockDispatch = jest.fn().mockReturnValue(of({})); MOCK_STORE.dispatch.mockImplementation(mockDispatch); - const event = SubscriptionEvent.GlobalFileUpdated; - const frequency = SubscriptionFrequency.Daily; - component.onSubscriptionChange(event, frequency); + component.onSubscriptionChange(SubscriptionEvent.GlobalFileUpdated, SubscriptionFrequency.Daily); expect(mockDispatch).toHaveBeenCalledTimes(1); }); + + it('should default to API value', () => { + const subs = component.notificationSubscriptionsForm.value; + expect(subs.new_pending_submissions).toBe(SubscriptionFrequency.Instant); + expect(subs.global_file_updated).toBe(SubscriptionFrequency.Daily); + }); }); diff --git a/src/app/features/settings/notifications/notifications.component.ts b/src/app/features/settings/notifications/notifications.component.ts index 327ae68ec..304aabce4 100644 --- a/src/app/features/settings/notifications/notifications.component.ts +++ b/src/app/features/settings/notifications/notifications.component.ts @@ -21,7 +21,7 @@ import { ToastService } from '@osf/shared/services/toast.service'; import { AccountSettings } from '../account-settings/models'; import { AccountSettingsSelectors, GetAccountSettings, UpdateAccountSettings } from '../account-settings/store'; -import { SUBSCRIPTION_EVENTS } from './constants'; +import { API_EVENT_TO_FORM_EVENT, FORM_EVENT_TO_API_EVENT, SUBSCRIPTION_EVENTS } from './constants'; import { EmailPreferencesForm, EmailPreferencesFormControls } from './models'; import { GetAllGlobalNotificationSubscriptions, @@ -80,7 +80,9 @@ export class NotificationsComponent implements OnInit { notificationSubscriptionsForm = this.fb.group( SUBSCRIPTION_EVENTS.reduce( (control, { event }) => { - control[event] = this.fb.control(SubscriptionFrequency.Never, { nonNullable: true }); + control[event as string] = this.fb.control(SubscriptionFrequency.Never, { + nonNullable: true, + }); return control; }, {} as Record> @@ -128,7 +130,24 @@ export class NotificationsComponent implements OnInit { onSubscriptionChange(event: SubscriptionEvent, frequency: SubscriptionFrequency) { const user = this.currentUser(); if (!user) return; - const id = `${user.id}_${event}`; + + const eventKey = event as string; + + const apiEventName = FORM_EVENT_TO_API_EVENT[eventKey] ?? eventKey; + + let id: string | undefined; + + if (event === SubscriptionEvent.GlobalReviews) { + const subs = this.notificationSubscriptions(); + const match = subs.find((s) => (s.event as string) === 'new_pending_submissions'); + if (match) { + id = match.id; + } else { + return; + } + } else { + id = `${user.id}_${apiEventName}`; + } this.loaderService.show(); this.actions.updateNotificationSubscription({ id, frequency }).subscribe(() => { @@ -145,10 +164,24 @@ export class NotificationsComponent implements OnInit { } private updateNotificationSubscriptionsForm() { + const subs = this.notificationSubscriptions(); + if (!subs?.length) { + return; + } + const patch: Record = {}; - for (const sub of this.notificationSubscriptions()) { - patch[sub.event] = sub.frequency; + for (const sub of subs) { + const apiEvent = sub.event as string | null; + if (!apiEvent) { + continue; + } + + const formEventKey = API_EVENT_TO_FORM_EVENT[apiEvent]; + + if (formEventKey) { + patch[formEventKey] = sub.frequency; + } } this.notificationSubscriptionsForm.patchValue(patch); diff --git a/src/app/shared/enums/subscriptions/subscription-event.enum.ts b/src/app/shared/enums/subscriptions/subscription-event.enum.ts index 3ce040bfe..c98ba610a 100644 --- a/src/app/shared/enums/subscriptions/subscription-event.enum.ts +++ b/src/app/shared/enums/subscriptions/subscription-event.enum.ts @@ -1,5 +1,5 @@ export enum SubscriptionEvent { GlobalFileUpdated = 'global_file_updated', - GlobalReviews = 'global_reviews', + GlobalReviews = 'new_pending_submissions', FileUpdated = 'file_updated', } From c57489fba3baa8761f574454d3f5e05fb57b23b4 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Thu, 20 Nov 2025 08:45:59 -0500 Subject: [PATCH 3/3] =?UTF-8?q?Revert=20"[ENG-9758]=20fix(notifications):?= =?UTF-8?q?=20change=20settings=20page=20to=20default=20to=20cor=E2=80=A6"?= =?UTF-8?q?=20(#780)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reverts CenterForOpenScience/angular-osf#761 Reason: this breaks notification settings for `Preprint submissions updated` This reverts commit 9aa505d811b43c8205a091a4e1bef6b3762ccd32. --- .../constants/notifications-constants.ts | 11 ----- .../notifications.component.spec.ts | 28 ++++-------- .../notifications/notifications.component.ts | 43 +++---------------- .../subscriptions/subscription-event.enum.ts | 2 +- 4 files changed, 15 insertions(+), 69 deletions(-) diff --git a/src/app/features/settings/notifications/constants/notifications-constants.ts b/src/app/features/settings/notifications/constants/notifications-constants.ts index 399e99dd0..1988f384c 100644 --- a/src/app/features/settings/notifications/constants/notifications-constants.ts +++ b/src/app/features/settings/notifications/constants/notifications-constants.ts @@ -12,14 +12,3 @@ export const SUBSCRIPTION_EVENTS: SubscriptionEventModel[] = [ labelKey: 'settings.notifications.notificationPreferences.items.preprints', }, ]; - -export const FORM_EVENT_TO_API_EVENT: Record = { - new_pending_submissions: 'new_pending_submissions', - files_updated: 'files_updated', - global_file_updated: 'global_file_updated', -}; - -export const API_EVENT_TO_FORM_EVENT: Record = { - new_pending_submissions: 'new_pending_submissions', - files_updated: 'global_file_updated', -}; diff --git a/src/app/features/settings/notifications/notifications.component.spec.ts b/src/app/features/settings/notifications/notifications.component.spec.ts index 03950cb0c..20bbb53af 100644 --- a/src/app/features/settings/notifications/notifications.component.spec.ts +++ b/src/app/features/settings/notifications/notifications.component.spec.ts @@ -41,19 +41,13 @@ describe('NotificationsComponent', () => { subscribeOsfHelpEmail: false, }; - // new_pending_submissions → global_reviews - // files_updated → global_file_updated const mockNotificationSubscriptions = [ + { id: 'id1', event: SubscriptionEvent.GlobalFileUpdated, frequency: SubscriptionFrequency.Daily }, { - id: 'osf_new_pending_submissions', - event: 'new_pending_submissions', + id: 'id2', + event: SubscriptionEvent.GlobalFileUpdated, frequency: SubscriptionFrequency.Instant, }, - { - id: 'cuzg4_global_file_updated', - event: 'files_updated', - frequency: SubscriptionFrequency.Daily, - }, ]; beforeEach(async () => { @@ -83,7 +77,7 @@ describe('NotificationsComponent', () => { return signal(null); }); - MOCK_STORE.dispatch.mockReturnValue(of({})); + MOCK_STORE.dispatch.mockImplementation(() => of()); await TestBed.configureTestingModule({ imports: [ @@ -122,9 +116,9 @@ describe('NotificationsComponent', () => { return signal(null); }); - component.emailPreferencesFormSubmit(); - expect(loaderService.hide).toHaveBeenCalledTimes(1); + + expect(loaderService.hide).not.toHaveBeenCalled(); }); it('should handle subscription completion correctly', () => { @@ -142,15 +136,11 @@ describe('NotificationsComponent', () => { it('should call dispatch only once per subscription change', () => { const mockDispatch = jest.fn().mockReturnValue(of({})); MOCK_STORE.dispatch.mockImplementation(mockDispatch); + const event = SubscriptionEvent.GlobalFileUpdated; + const frequency = SubscriptionFrequency.Daily; - component.onSubscriptionChange(SubscriptionEvent.GlobalFileUpdated, SubscriptionFrequency.Daily); + component.onSubscriptionChange(event, frequency); expect(mockDispatch).toHaveBeenCalledTimes(1); }); - - it('should default to API value', () => { - const subs = component.notificationSubscriptionsForm.value; - expect(subs.new_pending_submissions).toBe(SubscriptionFrequency.Instant); - expect(subs.global_file_updated).toBe(SubscriptionFrequency.Daily); - }); }); diff --git a/src/app/features/settings/notifications/notifications.component.ts b/src/app/features/settings/notifications/notifications.component.ts index 304aabce4..327ae68ec 100644 --- a/src/app/features/settings/notifications/notifications.component.ts +++ b/src/app/features/settings/notifications/notifications.component.ts @@ -21,7 +21,7 @@ import { ToastService } from '@osf/shared/services/toast.service'; import { AccountSettings } from '../account-settings/models'; import { AccountSettingsSelectors, GetAccountSettings, UpdateAccountSettings } from '../account-settings/store'; -import { API_EVENT_TO_FORM_EVENT, FORM_EVENT_TO_API_EVENT, SUBSCRIPTION_EVENTS } from './constants'; +import { SUBSCRIPTION_EVENTS } from './constants'; import { EmailPreferencesForm, EmailPreferencesFormControls } from './models'; import { GetAllGlobalNotificationSubscriptions, @@ -80,9 +80,7 @@ export class NotificationsComponent implements OnInit { notificationSubscriptionsForm = this.fb.group( SUBSCRIPTION_EVENTS.reduce( (control, { event }) => { - control[event as string] = this.fb.control(SubscriptionFrequency.Never, { - nonNullable: true, - }); + control[event] = this.fb.control(SubscriptionFrequency.Never, { nonNullable: true }); return control; }, {} as Record> @@ -130,24 +128,7 @@ export class NotificationsComponent implements OnInit { onSubscriptionChange(event: SubscriptionEvent, frequency: SubscriptionFrequency) { const user = this.currentUser(); if (!user) return; - - const eventKey = event as string; - - const apiEventName = FORM_EVENT_TO_API_EVENT[eventKey] ?? eventKey; - - let id: string | undefined; - - if (event === SubscriptionEvent.GlobalReviews) { - const subs = this.notificationSubscriptions(); - const match = subs.find((s) => (s.event as string) === 'new_pending_submissions'); - if (match) { - id = match.id; - } else { - return; - } - } else { - id = `${user.id}_${apiEventName}`; - } + const id = `${user.id}_${event}`; this.loaderService.show(); this.actions.updateNotificationSubscription({ id, frequency }).subscribe(() => { @@ -164,24 +145,10 @@ export class NotificationsComponent implements OnInit { } private updateNotificationSubscriptionsForm() { - const subs = this.notificationSubscriptions(); - if (!subs?.length) { - return; - } - const patch: Record = {}; - for (const sub of subs) { - const apiEvent = sub.event as string | null; - if (!apiEvent) { - continue; - } - - const formEventKey = API_EVENT_TO_FORM_EVENT[apiEvent]; - - if (formEventKey) { - patch[formEventKey] = sub.frequency; - } + for (const sub of this.notificationSubscriptions()) { + patch[sub.event] = sub.frequency; } this.notificationSubscriptionsForm.patchValue(patch); diff --git a/src/app/shared/enums/subscriptions/subscription-event.enum.ts b/src/app/shared/enums/subscriptions/subscription-event.enum.ts index c98ba610a..3ce040bfe 100644 --- a/src/app/shared/enums/subscriptions/subscription-event.enum.ts +++ b/src/app/shared/enums/subscriptions/subscription-event.enum.ts @@ -1,5 +1,5 @@ export enum SubscriptionEvent { GlobalFileUpdated = 'global_file_updated', - GlobalReviews = 'new_pending_submissions', + GlobalReviews = 'global_reviews', FileUpdated = 'file_updated', }