From 75734993e7e53f81878df0a2f284b01b357e2aac Mon Sep 17 00:00:00 2001 From: Jimmy Domagala-Tang Date: Wed, 7 Jan 2026 11:41:33 -0500 Subject: [PATCH] Add Notification Preferences Page To User Settings (#30116) * changing to pug conditional format for admin page conditional render * feat: update notifications settings page and open BE route feat: adding new page for notification prefs on user settings, along with updating saving prefs for global preferences adding error handling and disabling when request is inflight formatting and adding split test for viewing notification ettings fix: updating to levels of preferences, and removing global type for preferences as we will now share the same backing settings between global and project preferences feat: add global mute to user settings for notifications making params in preferences schema optional feat: update global settings to only set mute, and remove optional settings fix: store userId as objectId, and filter based on global mute setting GitOrigin-RevId: 947a95dc02d12b4a2d8e3cc29bd04c23af2aef25 --- .../src/Features/User/UserPagesController.mjs | 3 +++ .../web/frontend/extracted-translations.json | 3 +++ .../components/notifications-section.tsx | 16 +++++++++++++++ .../js/features/settings/components/root.tsx | 19 ++++++++++-------- services/web/frontend/js/utils/meta.ts | 2 ++ .../web/frontend/stylesheets/pages/all.scss | 1 + .../pages/notification-settings.scss | 20 +++++++++++++++++++ services/web/locales/en.json | 2 ++ services/web/types/notifications.ts | 7 +++++++ 9 files changed, 65 insertions(+), 8 deletions(-) create mode 100644 services/web/frontend/js/features/settings/components/notifications-section.tsx create mode 100644 services/web/frontend/stylesheets/pages/notification-settings.scss create mode 100644 services/web/types/notifications.ts diff --git a/services/web/app/src/Features/User/UserPagesController.mjs b/services/web/app/src/Features/User/UserPagesController.mjs index b4c72fd82f..e25c1ca8f8 100644 --- a/services/web/app/src/Features/User/UserPagesController.mjs +++ b/services/web/app/src/Features/User/UserPagesController.mjs @@ -11,6 +11,7 @@ import _ from 'lodash' import { expressify } from '@overleaf/promise-utils' import Features from '../../infrastructure/Features.mjs' import Modules from '../../infrastructure/Modules.mjs' +import SplitTestHandler from '../SplitTests/SplitTestHandler.mjs' async function settingsPage(req, res) { const userId = SessionManager.getLoggedInUserId(req.session) @@ -116,6 +117,8 @@ async function settingsPage(req, res) { ) } + await SplitTestHandler.promises.getAssignment(req, res, 'email-notifications') + res.render('user/settings', { title: 'account_settings', user: { diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index ff8f673ba6..cbd1becc43 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -550,6 +550,7 @@ "email_must_be_linked_to_institution": "", "email_notifications_are_currently_in_beta": "", "email_or_password_wrong_try_again": "", + "email_preferences": "", "email_remove_by_date": "", "emails_and_affiliations_explanation": "", "emails_and_affiliations_title": "", @@ -1047,6 +1048,7 @@ "make_primary": "", "make_private": "", "manage_beta_program_membership": "", + "manage_email_preferences": "", "manage_files_from_your_dropbox_folder": "", "manage_group_members_subtext": "", "manage_group_settings": "", @@ -1142,6 +1144,7 @@ "new_tag": "", "new_tag_name": "", "newsletter": "", + "newsletter_info_note": "", "newsletter_onboarding_accept": "", "next": "", "next_page": "", diff --git a/services/web/frontend/js/features/settings/components/notifications-section.tsx b/services/web/frontend/js/features/settings/components/notifications-section.tsx new file mode 100644 index 0000000000..067e99ee70 --- /dev/null +++ b/services/web/frontend/js/features/settings/components/notifications-section.tsx @@ -0,0 +1,16 @@ +import { useTranslation } from 'react-i18next' + +function NotificationsSection() { + const { t } = useTranslation() + + return ( + <> +

{t('email_preferences')}

+ + {t('manage_email_preferences')} + + + ) +} + +export default NotificationsSection diff --git a/services/web/frontend/js/features/settings/components/root.tsx b/services/web/frontend/js/features/settings/components/root.tsx index 31e1b16541..c2a71b229f 100644 --- a/services/web/frontend/js/features/settings/components/root.tsx +++ b/services/web/frontend/js/features/settings/components/root.tsx @@ -15,13 +15,14 @@ import LeaveSection from './leave-section' import * as eventTracking from '../../../infrastructure/event-tracking' import { UserProvider } from '../../../shared/context/user-context' import { SSOProvider } from '../context/sso-context' -import { SplitTestProvider } from '@/shared/context/split-test-context' import useWaitForI18n from '../../../shared/hooks/use-wait-for-i18n' import useScrollToIdOnLoad from '../../../shared/hooks/use-scroll-to-id-on-load' import { SSOAlert } from './emails/sso-alert' import OLRow from '@/shared/components/ol/ol-row' import OLCol from '@/shared/components/ol/ol-col' import OLPageContentCard from '@/shared/components/ol/ol-page-content-card' +import { isSplitTestEnabled } from '@/utils/splitTestUtils' +import NotificationsSection from './notifications-section' function SettingsPageRoot() { const { isReady } = useWaitForI18n() @@ -45,7 +46,7 @@ function SettingsPageRoot() { function SettingsPageContent() { const { t } = useTranslation() const { isOverleaf, labsEnabled } = getMeta('ol-ExposedSettings') - + const inNotificationsSplitTest = isSplitTestEnabled('email-notifications') return ( @@ -66,11 +67,9 @@ function SettingsPageContent() {
- - - - - + + + {isOverleaf ? ( <> @@ -86,7 +85,11 @@ function SettingsPageContent() { {isOverleaf ? ( <>
- + {inNotificationsSplitTest ? ( + + ) : ( + + )}
diff --git a/services/web/frontend/js/utils/meta.ts b/services/web/frontend/js/utils/meta.ts index 404d614bdc..75dc79e40e 100644 --- a/services/web/frontend/js/utils/meta.ts +++ b/services/web/frontend/js/utils/meta.ts @@ -66,6 +66,7 @@ import { Subscription as AdminSubscription } from '../../../types/admin/subscrip import { AdminCapability } from '../../../types/admin-capabilities' import { AlgoliaConfig } from '../../../modules/algolia-search/frontend/js/types' import { WritefullPublicEnv } from '@wf/domain/writefull-public-env' +import { UserNotificationPreferences } from '../../../types/notifications' export interface Meta { 'ol-ExposedSettings': ExposedSettings @@ -317,6 +318,7 @@ export interface Meta { 'ol-userCanExtendTrial': boolean 'ol-userCanNotStartRequestedTrial': boolean 'ol-userEmails': UserEmailData[] + 'ol-userNotificationPreferences': UserNotificationPreferences 'ol-userSettings': UserSettings 'ol-user_id': string | undefined 'ol-users': ManagedUser[] diff --git a/services/web/frontend/stylesheets/pages/all.scss b/services/web/frontend/stylesheets/pages/all.scss index de358919ea..e9e0e1778a 100644 --- a/services/web/frontend/stylesheets/pages/all.scss +++ b/services/web/frontend/stylesheets/pages/all.scss @@ -60,3 +60,4 @@ @import 'portals'; @import 'wiki'; @import 'templates'; +@import 'notification-settings'; diff --git a/services/web/frontend/stylesheets/pages/notification-settings.scss b/services/web/frontend/stylesheets/pages/notification-settings.scss new file mode 100644 index 0000000000..0058c4af99 --- /dev/null +++ b/services/web/frontend/stylesheets/pages/notification-settings.scss @@ -0,0 +1,20 @@ +.notification-settings-container { + input { + margin-top: var(--spacing-01); + margin-bottom: auto; + accent-color: var(--green-50); + } + + .setting-container { + display: flex; + gap: 10px; + } + + h2 { + margin-bottom: 0; + } + + h4 { + margin-bottom: 0; + } +} diff --git a/services/web/locales/en.json b/services/web/locales/en.json index 4be04f15aa..fb4a0bbc22 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -698,6 +698,7 @@ "email_notifications_are_currently_in_beta": "Email notifications are currently in beta.", "email_or_password_wrong_try_again": "Your email or password is incorrect. Please try again.", "email_or_password_wrong_try_again_or_reset": "Your email or password is incorrect. Please try again, or <0>set or reset your password.", + "email_preferences": "Email preferences", "email_remove_by_date": "If this is not done by __date__, it will be removed from the account.", "email_required": "Email required", "email_sent": "Email Sent", @@ -1363,6 +1364,7 @@ "make_primary": "Make primary", "make_private": "Make private", "manage_beta_program_membership": "Manage beta program membership", + "manage_email_preferences": "Manage email preferences", "manage_files_from_your_dropbox_folder": "Manage files from your Dropbox folder", "manage_group_members_subtext": "Add or remove members from your group subscription", "manage_group_settings": "Manage group settings", diff --git a/services/web/types/notifications.ts b/services/web/types/notifications.ts new file mode 100644 index 0000000000..0360d01490 --- /dev/null +++ b/services/web/types/notifications.ts @@ -0,0 +1,7 @@ +type GlobalNotificationPreferences = { + muteAllNotifications: boolean +} + +export type UserNotificationPreferences = { + newsletter: boolean +} & GlobalNotificationPreferences