From 3f6e96f58edbedeacb7bc1e7b366adb820e963b6 Mon Sep 17 00:00:00 2001 From: Domagoj Kriskovic Date: Thu, 4 Dec 2025 11:33:25 +0100 Subject: [PATCH] Add email notifications settings for redesigned editor GitOrigin-RevId: d31f13bbc668d790564618e6b4a8a83fdf2d8780 --- .../Features/Project/ProjectController.mjs | 1 + .../web/frontend/extracted-translations.json | 8 +++ ...alSymbolsRoundedUnfilledPartialSlice.woff2 | Bin 8592 -> 8684 bytes .../material-symbols/unfilled-symbols.mjs | 1 + .../project-notifications-setting.tsx | 51 ++++++++++++++++++ .../settings/radio-button-setting.tsx | 51 ++++++++++++++++++ .../settings/settings-modal-body.tsx | 17 ++++++ .../contexts/settings-modal-context.tsx | 32 ++++++++++- .../stylesheets/pages/editor/settings.scss | 34 +++++++++++- services/web/locales/en.json | 8 +++ 10 files changed, 200 insertions(+), 3 deletions(-) create mode 100644 services/web/frontend/js/features/ide-redesign/components/settings/editor-settings/project-notifications-setting.tsx create mode 100644 services/web/frontend/js/features/ide-redesign/components/settings/radio-button-setting.tsx diff --git a/services/web/app/src/Features/Project/ProjectController.mjs b/services/web/app/src/Features/Project/ProjectController.mjs index 9297fe08a9..1d47042304 100644 --- a/services/web/app/src/Features/Project/ProjectController.mjs +++ b/services/web/app/src/Features/Project/ProjectController.mjs @@ -460,6 +460,7 @@ const _ProjectController = { 'writefull-asymetric-queue-size-per-model', 'pdf-dark-mode', 'editor-redesign-opt-out', + 'email-notifications', ].filter(Boolean) const getUserValues = async userId => diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index 5f7b913308..4b6fa33f01 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -131,6 +131,8 @@ "all_logs": "", "all_premium_features": "", "all_premium_features_including": "", + "all_project_activity": "", + "all_project_activity_description": "", "all_projects": "", "all_projects_will_be_transferred_immediately": "", "all_these_experiments_are_available_exclusively": "", @@ -541,6 +543,7 @@ "email_link_expired": "", "email_managed_by_group_is": "", "email_must_be_linked_to_institution": "", + "email_notifications_are_currently_in_beta": "", "email_or_password_wrong_try_again": "", "email_remove_by_date": "", "emails_and_affiliations_explanation": "", @@ -1158,6 +1161,7 @@ "no_pdf_error_reason_unrecoverable_error": "", "no_pdf_error_title": "", "no_preview_available": "", + "no_project_notifications_description": "", "no_projects": "", "no_resolved_comments": "", "no_search_results": "", @@ -1361,6 +1365,7 @@ "project_linked_to": "", "project_name": "", "project_not_linked_to_github": "", + "project_notifications": "", "project_ownership_transfer_confirmation_1": "", "project_ownership_transfer_confirmation_2": "", "project_renamed_or_deleted": "", @@ -1480,6 +1485,8 @@ "replace_from_computer": "", "replace_from_project_files": "", "replace_from_url": "", + "replies_to_your_activity_only": "", + "replies_to_your_activity_only_description": "", "reply": "", "repository_name": "", "repository_visibility": "", @@ -1868,6 +1875,7 @@ "then_x_price_per_year": "", "there_are_lots_of_options_to_edit_and_customize_your_figures": "", "there_was_a_problem_restoring_the_project_please_try_again_in_a_few_moments_or_contact_us": "", + "these_settings_might_change_in_the_future": "", "they_lose_access_to_account": "", "they_will_be_removed_from_the_group": "", "they_will_continue_to_have_access_to_any_projects_shared_with_them": "", diff --git a/services/web/frontend/fonts/material-symbols/MaterialSymbolsRoundedUnfilledPartialSlice.woff2 b/services/web/frontend/fonts/material-symbols/MaterialSymbolsRoundedUnfilledPartialSlice.woff2 index da383987f3c233e7f0cba97df2f389c116f5b5af..0687bc0fcba3263d3f7dd819bc43c85f6150d331 100644 GIT binary patch literal 8684 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 Krj2A50001H4t5{_ literal 8592 zcmV;BA#dJyPew8T0RR9103nb74gdfE08C&203kL20RR9100000000000000000000 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 diff --git a/services/web/frontend/fonts/material-symbols/unfilled-symbols.mjs b/services/web/frontend/fonts/material-symbols/unfilled-symbols.mjs index 078c2c9bae..209b03d649 100644 --- a/services/web/frontend/fonts/material-symbols/unfilled-symbols.mjs +++ b/services/web/frontend/fonts/material-symbols/unfilled-symbols.mjs @@ -42,6 +42,7 @@ export default /** @type {const} */ ([ 'more_vert', 'neurology', 'note_add', + 'notifications', 'open_in_new', 'password', 'picture_as_pdf', diff --git a/services/web/frontend/js/features/ide-redesign/components/settings/editor-settings/project-notifications-setting.tsx b/services/web/frontend/js/features/ide-redesign/components/settings/editor-settings/project-notifications-setting.tsx new file mode 100644 index 0000000000..4ba6b2176e --- /dev/null +++ b/services/web/frontend/js/features/ide-redesign/components/settings/editor-settings/project-notifications-setting.tsx @@ -0,0 +1,51 @@ +import { useTranslation } from 'react-i18next' +import RadioButtonSetting, { RadioOption } from '../radio-button-setting' +import { useState } from 'react' + +type NotificationLevel = 'all' | 'replies' | 'off' + +export default function ProjectNotificationsSetting() { + const { t } = useTranslation() + // TODO: Connect to project settings context when backend support is added + const [notificationLevel, setNotificationLevel] = + useState('all') + + const options: Array> = [ + { + value: 'all', + label: t('all_project_activity'), + description: t('all_project_activity_description'), + }, + { + value: 'replies', + label: t('replies_to_your_activity_only'), + description: t('replies_to_your_activity_only_description'), + }, + { + value: 'off', + label: t('off'), + description: t('no_project_notifications_description'), + }, + ] + + return ( + <> + +
+ + {t('email_notifications_are_currently_in_beta')}{' '} + {t('these_settings_might_change_in_the_future')}{' '} + + {/* TODO: update forms link */} + + {t('give_feedback')} + +
+ + ) +} diff --git a/services/web/frontend/js/features/ide-redesign/components/settings/radio-button-setting.tsx b/services/web/frontend/js/features/ide-redesign/components/settings/radio-button-setting.tsx new file mode 100644 index 0000000000..bfbea7cc85 --- /dev/null +++ b/services/web/frontend/js/features/ide-redesign/components/settings/radio-button-setting.tsx @@ -0,0 +1,51 @@ +import React from 'react' + +export type RadioOption = { + value: T + label: string + description?: string +} + +type RadioButtonSettingProps = { + id: string + options: Array> + value: T | undefined + onChange: (value: T) => void +} + +export default function RadioButtonSetting({ + id, + options, + value, + onChange, +}: RadioButtonSettingProps) { + const handleChange = (event: React.ChangeEvent) => { + onChange(event.target.value as T) + } + + return ( +
+ {options.map(option => ( + + ))} +
+ ) +} diff --git a/services/web/frontend/js/features/ide-redesign/components/settings/settings-modal-body.tsx b/services/web/frontend/js/features/ide-redesign/components/settings/settings-modal-body.tsx index 6c4082887a..3530d0dbc9 100644 --- a/services/web/frontend/js/features/ide-redesign/components/settings/settings-modal-body.tsx +++ b/services/web/frontend/js/features/ide-redesign/components/settings/settings-modal-body.tsx @@ -3,6 +3,9 @@ import MaterialIcon from '@/shared/components/material-icon' import { Nav, NavLink, TabContainer, TabContent } from 'react-bootstrap' import { SettingsEntry } from '../../contexts/settings-modal-context' import SettingsTabPane from './settings-tab-pane' +import BetaBadgeIcon from '@/shared/components/beta-badge-icon' +import OLTooltip from '@/shared/components/ol/ol-tooltip' +import { useTranslation } from 'react-i18next' export const SettingsModalBody = ({ activeTab, @@ -42,6 +45,8 @@ export const SettingsModalBody = ({ } const SettingsNavLink = ({ entry }: { entry: SettingsEntry }) => { + const { t } = useTranslation() + if ('href' in entry) { return ( { unfilled /> {entry.title} +
+ {entry.key === 'project_notifications' && ( + + + + + + )} ) diff --git a/services/web/frontend/js/features/ide-redesign/contexts/settings-modal-context.tsx b/services/web/frontend/js/features/ide-redesign/contexts/settings-modal-context.tsx index 884bc2ee15..bdfba12e0f 100644 --- a/services/web/frontend/js/features/ide-redesign/contexts/settings-modal-context.tsx +++ b/services/web/frontend/js/features/ide-redesign/contexts/settings-modal-context.tsx @@ -28,6 +28,7 @@ import NewEditorSetting from '../components/settings/editor-settings/new-editor- import DarkModePdfSetting from '../components/settings/appearance-settings/dark-mode-pdf-setting' import { useProjectSettingsContext } from '@/features/editor-left-menu/context/project-settings-context' import { useFeatureFlag } from '@/shared/context/split-test-context' +import ProjectNotificationsSetting from '../components/settings/editor-settings/project-notifications-setting' const [referenceSearchSettingModule] = importOverleafModules( 'referenceSearchSetting' @@ -51,6 +52,7 @@ export type SettingsTab = { icon: AvailableUnfilledIcon sections: SettingsSection[] title: string + hidden?: boolean } type SettingsLink = { @@ -58,6 +60,7 @@ type SettingsLink = { icon: AvailableUnfilledIcon href: string title: string + hidden?: boolean } export type SettingsEntry = SettingsLink | SettingsTab @@ -85,7 +88,8 @@ export const SettingsModalProvider: FC = ({ const { leftMenuShown, setLeftMenuShown } = useLayoutContext() const hasDarkModePdf = useFeatureFlag('pdf-dark-mode') - const settingsTabs: SettingsEntry[] = useMemo( + const hasEmailNotifications = useFeatureFlag('email-notifications') + const allSettingsTabs: SettingsEntry[] = useMemo( () => [ { key: 'editor', @@ -229,6 +233,25 @@ export const SettingsModalProvider: FC = ({ }, ], }, + + { + key: 'project_notifications', + title: t('project_notifications'), + icon: 'notifications' as const, + sections: [ + { + key: 'general', + settings: [ + { + key: 'projectNotifications', + component: , + }, + ], + }, + ], + hidden: !hasEmailNotifications, + }, + { key: 'account_settings', title: t('account_settings'), @@ -242,7 +265,12 @@ export const SettingsModalProvider: FC = ({ href: '/user/subscription', }, ], - [t, overallTheme, hasDarkModePdf] + [t, overallTheme, hasDarkModePdf, hasEmailNotifications] + ) + + const settingsTabs = useMemo( + () => allSettingsTabs.filter(tab => !tab.hidden), + [allSettingsTabs] ) const settingToTabMap = useMemo(() => { diff --git a/services/web/frontend/stylesheets/pages/editor/settings.scss b/services/web/frontend/stylesheets/pages/editor/settings.scss index 61af7d9035..0ba97d6baa 100644 --- a/services/web/frontend/stylesheets/pages/editor/settings.scss +++ b/services/web/frontend/stylesheets/pages/editor/settings.scss @@ -1,5 +1,5 @@ .ide-settings-tab-nav.nav { - width: 240px; + width: 300px; border-right: var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color); padding: var(--spacing-02); @@ -98,3 +98,35 @@ justify-content: flex-end; margin-left: var(--spacing-06); } + +.ide-radio-setting-options { + display: flex; + flex-direction: column; + gap: var(--spacing-06); +} + +.ide-radio-option { + display: flex; + align-items: flex-start; + gap: var(--spacing-04); + cursor: pointer; +} + +.ide-radio-input { + margin-top: var(--spacing-02); +} + +.ide-radio-text { + display: flex; + flex-direction: column; +} + +.project-notifications-beta-note { + margin-top: var(--spacing-06); + padding: var(--spacing-04); + font-size: var(--font-size-02); +} + +.beta-note-text { + color: var(--content-secondary); +} diff --git a/services/web/locales/en.json b/services/web/locales/en.json index 6ab4119ae6..f9865a90a1 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -157,6 +157,8 @@ "all_premium_features": "All premium features", "all_premium_features_including": "All premium features, including:", "all_prices_displayed_are_in_currency": "All prices displayed are in __recommendedCurrency__.", + "all_project_activity": "All project activity", + "all_project_activity_description": "You’ll be notified about all comments and track changes in this project.", "all_projects": "All projects", "all_projects_will_be_transferred_immediately": "All projects will be transferred to the new owner immediately.", "all_templates": "All templates", @@ -686,6 +688,7 @@ "email_link_expired": "Email link expired, please request a new one.", "email_managed_by_group_is": "The email address that will be managed by your group is <0>__email__.", "email_must_be_linked_to_institution": "As a member of __institutionName__, this email address can only be added via single sign-on on your <0>account settings page. Please add a different recovery email address.", + "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_remove_by_date": "If this is not done by __date__, it will be removed from the account.", @@ -1498,6 +1501,7 @@ "no_pdf_error_title": "No PDF", "no_planned_maintenance": "There is currently no planned maintenance", "no_preview_available": "Sorry, no preview is available.", + "no_project_notifications_description": "You won’t be notified about this project.", "no_projects": "No projects", "no_resolved_comments": "No resolved comments", "no_search_results": "No Search Results", @@ -1776,6 +1780,7 @@ "project_linked_to": "This project is linked to", "project_name": "Project name", "project_not_linked_to_github": "This project is not linked to a GitHub repository. You can create a repository for it in GitHub:", + "project_notifications": "Project notifications", "project_ownership_transfer_confirmation_1": "Are you sure you want to make <0>__user__ the owner of <1>__project__?", "project_ownership_transfer_confirmation_2": "This action cannot be undone. The new owner will be notified and will be able to change project access settings (including removing your own access).", "project_renamed_or_deleted": "Project Renamed or Deleted", @@ -1914,6 +1919,8 @@ "replace_from_computer": "Replace from computer", "replace_from_project_files": "Replace from project files", "replace_from_url": "Replace from URL", + "replies_to_your_activity_only": "Replies to your activity only", + "replies_to_your_activity_only_description": "You’ll be notified about direct replies and activity on your track changes only.", "reply": "Reply", "repository_name": "Repository Name", "repository_visibility": "Repository visibility", @@ -2388,6 +2395,7 @@ "there_are_lots_of_options_to_edit_and_customize_your_figures": "There are lots of options to edit and customize your figures, such as wrapping text around the figure, rotating the image, or including multiple images in a single figure. You’ll need to edit the LaTeX code to do this. <0>Find out how", "there_was_a_problem_restoring_the_project_please_try_again_in_a_few_moments_or_contact_us": "There was a problem restoring the project. Please try again in a few moments. Contact us if the problem persists.", "there_was_an_error_opening_your_content": "There was an error creating your project", + "these_settings_might_change_in_the_future": "These settings might change in the future.", "thesis": "Thesis", "they_lose_access_to_account": "They lose all access to this Overleaf account immediately", "they_will_be_removed_from_the_group": "They will be removed from the group.",