mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-31 21:01:33 +02:00
Add email notifications settings for redesigned editor
GitOrigin-RevId: d31f13bbc668d790564618e6b4a8a83fdf2d8780
This commit is contained in:
committed by
Copybot
parent
b9224ea11d
commit
3f6e96f58e
@@ -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 =>
|
||||
|
||||
@@ -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": "",
|
||||
|
||||
Binary file not shown.
@@ -42,6 +42,7 @@ export default /** @type {const} */ ([
|
||||
'more_vert',
|
||||
'neurology',
|
||||
'note_add',
|
||||
'notifications',
|
||||
'open_in_new',
|
||||
'password',
|
||||
'picture_as_pdf',
|
||||
|
||||
@@ -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<NotificationLevel>('all')
|
||||
|
||||
const options: Array<RadioOption<NotificationLevel>> = [
|
||||
{
|
||||
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 (
|
||||
<>
|
||||
<RadioButtonSetting
|
||||
id="projectNotifications"
|
||||
options={options}
|
||||
value={notificationLevel}
|
||||
onChange={setNotificationLevel}
|
||||
/>
|
||||
<div className="project-notifications-beta-note">
|
||||
<span className="beta-note-text">
|
||||
{t('email_notifications_are_currently_in_beta')}{' '}
|
||||
{t('these_settings_might_change_in_the_future')}{' '}
|
||||
</span>
|
||||
{/* TODO: update forms link */}
|
||||
<a target="_blank" rel="noopener noreferrer" href="https://forms.gle/">
|
||||
{t('give_feedback')}
|
||||
</a>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import React from 'react'
|
||||
|
||||
export type RadioOption<T extends string = string> = {
|
||||
value: T
|
||||
label: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
type RadioButtonSettingProps<T extends string = string> = {
|
||||
id: string
|
||||
options: Array<RadioOption<T>>
|
||||
value: T | undefined
|
||||
onChange: (value: T) => void
|
||||
}
|
||||
|
||||
export default function RadioButtonSetting<T extends string = string>({
|
||||
id,
|
||||
options,
|
||||
value,
|
||||
onChange,
|
||||
}: RadioButtonSettingProps<T>) {
|
||||
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
onChange(event.target.value as T)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="ide-radio-setting-options">
|
||||
{options.map(option => (
|
||||
<label key={`${id}-${option.value}`} className="ide-radio-option">
|
||||
<input
|
||||
type="radio"
|
||||
id={`${id}-${option.value}`}
|
||||
name={id}
|
||||
value={option.value}
|
||||
checked={value === option.value}
|
||||
onChange={handleChange}
|
||||
className="ide-radio-input"
|
||||
/>
|
||||
<div className="ide-radio-text">
|
||||
<span className="ide-setting-title">{option.label}</span>
|
||||
{option.description && (
|
||||
<span className="ide-setting-description">
|
||||
{option.description}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -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 (
|
||||
<a
|
||||
@@ -77,6 +82,18 @@ const SettingsNavLink = ({ entry }: { entry: SettingsEntry }) => {
|
||||
unfilled
|
||||
/>
|
||||
<span>{entry.title}</span>
|
||||
<div className="flex-grow-1" />
|
||||
{entry.key === 'project_notifications' && (
|
||||
<OLTooltip
|
||||
id="project-notifications-beta-badge"
|
||||
description={t('email_notifications_are_currently_in_beta')}
|
||||
overlayProps={{ placement: 'right', delay: 100 }}
|
||||
>
|
||||
<span>
|
||||
<BetaBadgeIcon />
|
||||
</span>
|
||||
</OLTooltip>
|
||||
)}
|
||||
</NavLink>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -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<React.PropsWithChildren> = ({
|
||||
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<React.PropsWithChildren> = ({
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
key: 'project_notifications',
|
||||
title: t('project_notifications'),
|
||||
icon: 'notifications' as const,
|
||||
sections: [
|
||||
{
|
||||
key: 'general',
|
||||
settings: [
|
||||
{
|
||||
key: 'projectNotifications',
|
||||
component: <ProjectNotificationsSetting />,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
hidden: !hasEmailNotifications,
|
||||
},
|
||||
|
||||
{
|
||||
key: 'account_settings',
|
||||
title: t('account_settings'),
|
||||
@@ -242,7 +265,12 @@ export const SettingsModalProvider: FC<React.PropsWithChildren> = ({
|
||||
href: '/user/subscription',
|
||||
},
|
||||
],
|
||||
[t, overallTheme, hasDarkModePdf]
|
||||
[t, overallTheme, hasDarkModePdf, hasEmailNotifications]
|
||||
)
|
||||
|
||||
const settingsTabs = useMemo(
|
||||
() => allSettingsTabs.filter(tab => !tab.hidden),
|
||||
[allSettingsTabs]
|
||||
)
|
||||
|
||||
const settingToTabMap = useMemo(() => {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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__</0>.",
|
||||
"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</0> 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</0>.",
|
||||
"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__</0> the owner of <1>__project__</1>?",
|
||||
"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</0>",
|
||||
"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.",
|
||||
|
||||
Reference in New Issue
Block a user