mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-27 11:01:56 +02:00
Merge pull request #28298 from overleaf/dp-editor-switch-split-test
Update editor switching behaviour for new user split test GitOrigin-RevId: 61ef678ba216323d283bda4cc77d8c465b8c87df
This commit is contained in:
@@ -1090,6 +1090,9 @@
|
||||
"neither_agree_nor_disagree": "",
|
||||
"new_compile_domain_notice": "",
|
||||
"new_create_tables_and_equations": "",
|
||||
"new_editor": "",
|
||||
"new_editor_experience": "",
|
||||
"new_editor_info": "",
|
||||
"new_file": "",
|
||||
"new_folder": "",
|
||||
"new_look_and_feel": "",
|
||||
|
||||
@@ -19,6 +19,7 @@ import SettingsMathPreview from './settings/settings-math-preview'
|
||||
import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
|
||||
import { ElementType } from 'react'
|
||||
import OLForm from '@/shared/components/ol/ol-form'
|
||||
import SettingsNewEditor from './settings/settings-new-editor'
|
||||
|
||||
const moduleSettings: Array<{
|
||||
import: { default: ElementType }
|
||||
@@ -56,6 +57,7 @@ export default function SettingsMenu() {
|
||||
<SettingsFontFamily />
|
||||
<SettingsLineHeight />
|
||||
<SettingsPdfViewer />
|
||||
<SettingsNewEditor />
|
||||
</OLForm>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import SettingsMenuSelect from './settings-menu-select'
|
||||
import { useSwitchEnableNewEditorState } from '@/features/ide-redesign/hooks/use-switch-enable-new-editor-state'
|
||||
import { useLayoutContext } from '@/shared/context/layout-context'
|
||||
import { useCallback } from 'react'
|
||||
import {
|
||||
canUseNewEditorViaNewUserFeatureFlag,
|
||||
useIsNewEditorEnabled,
|
||||
} from '@/features/ide-redesign/utils/new-editor-utils'
|
||||
|
||||
export default function SettingsNewEditor() {
|
||||
const { t } = useTranslation()
|
||||
const { setEditorRedesignStatus } = useSwitchEnableNewEditorState()
|
||||
const { setLeftMenuShown } = useLayoutContext()
|
||||
const enabled = useIsNewEditorEnabled()
|
||||
const show = canUseNewEditorViaNewUserFeatureFlag()
|
||||
|
||||
const onChange = useCallback(
|
||||
(newValue: boolean) => {
|
||||
setEditorRedesignStatus(newValue).then(() => setLeftMenuShown(false))
|
||||
},
|
||||
[setEditorRedesignStatus, setLeftMenuShown]
|
||||
)
|
||||
|
||||
if (!show) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingsMenuSelect
|
||||
onChange={onChange}
|
||||
value={enabled}
|
||||
options={[
|
||||
{
|
||||
value: true,
|
||||
label: t('on'),
|
||||
},
|
||||
{
|
||||
value: false,
|
||||
label: t('off'),
|
||||
},
|
||||
]}
|
||||
label={t('new_editor')}
|
||||
name="new-editor-setting"
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -15,10 +15,10 @@ import importOverleafModules from '../../../../macros/import-overleaf-module.mac
|
||||
import BackToEditorButton from './back-to-editor-button'
|
||||
import getMeta from '@/utils/meta'
|
||||
import { isSplitTestEnabled } from '@/utils/splitTestUtils'
|
||||
import { canUseNewEditor } from '@/features/ide-redesign/utils/new-editor-utils'
|
||||
import TryNewEditorButton from '../try-new-editor-button'
|
||||
import { OnlineUser } from '@/features/ide-react/context/online-users-context'
|
||||
import { Cobranding } from '../../../../../types/cobranding'
|
||||
import { canUseNewEditor } from '@/features/ide-redesign/utils/new-editor-utils'
|
||||
|
||||
const [publishModalModules] = importOverleafModules('publishModal') as {
|
||||
import: { default: ElementType }
|
||||
|
||||
@@ -2,14 +2,24 @@ import { useCallback } from 'react'
|
||||
import OLButton from '../../shared/components/ol/ol-button'
|
||||
import { useIdeRedesignSwitcherContext } from '../ide-react/context/ide-redesign-switcher-context'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { canUseNewEditorViaPrimaryFeatureFlag } from '../ide-redesign/utils/new-editor-utils'
|
||||
import { useSwitchEnableNewEditorState } from '../ide-redesign/hooks/use-switch-enable-new-editor-state'
|
||||
import { Spinner } from 'react-bootstrap'
|
||||
|
||||
const TryNewEditorButton = () => {
|
||||
const { t } = useTranslation()
|
||||
const { setShowSwitcherModal } = useIdeRedesignSwitcherContext()
|
||||
const showModal = canUseNewEditorViaPrimaryFeatureFlag()
|
||||
const { loading, setEditorRedesignStatus } = useSwitchEnableNewEditorState()
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
setShowSwitcherModal(true)
|
||||
}, [setShowSwitcherModal])
|
||||
if (showModal) {
|
||||
setShowSwitcherModal(true)
|
||||
} else {
|
||||
setEditorRedesignStatus(true)
|
||||
}
|
||||
}, [setShowSwitcherModal, showModal, setEditorRedesignStatus])
|
||||
|
||||
return (
|
||||
<div className="d-flex align-items-center">
|
||||
<OLButton
|
||||
@@ -18,7 +28,16 @@ const TryNewEditorButton = () => {
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
>
|
||||
{t('try_the_new_editor')}
|
||||
{loading ? (
|
||||
<Spinner
|
||||
animation="border"
|
||||
aria-hidden="true"
|
||||
size="sm"
|
||||
role="status"
|
||||
/>
|
||||
) : (
|
||||
t('try_the_new_editor')
|
||||
)}
|
||||
</OLButton>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import ToggleSetting from '../toggle-setting'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useSwitchEnableNewEditorState } from '@/features/ide-redesign/hooks/use-switch-enable-new-editor-state'
|
||||
import { useIsNewEditorEnabled } from '@/features/ide-redesign/utils/new-editor-utils'
|
||||
import { useCallback } from 'react'
|
||||
import { useLayoutContext } from '@/shared/context/layout-context'
|
||||
|
||||
export default function NewEditorSetting() {
|
||||
const { t } = useTranslation()
|
||||
const { setEditorRedesignStatus } = useSwitchEnableNewEditorState()
|
||||
const { setLeftMenuShown } = useLayoutContext()
|
||||
const enabled = useIsNewEditorEnabled()
|
||||
const handleToggle = useCallback(() => {
|
||||
setEditorRedesignStatus(!enabled).then(() => setLeftMenuShown(false))
|
||||
}, [enabled, setEditorRedesignStatus, setLeftMenuShown])
|
||||
|
||||
return (
|
||||
<ToggleSetting
|
||||
id="new-editor-setting"
|
||||
label={
|
||||
<div className="ide-setting-new-editor">
|
||||
{t('new_editor_experience')}
|
||||
<div className="ide-setting-beta-tag">{t('beta')}</div>
|
||||
</div>
|
||||
}
|
||||
description={t('new_editor_info')}
|
||||
checked={enabled}
|
||||
onChange={handleToggle}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
import React from 'react'
|
||||
|
||||
export default function Setting({
|
||||
label,
|
||||
controlId,
|
||||
children,
|
||||
description = undefined,
|
||||
}: {
|
||||
label: string
|
||||
label: React.ReactNode
|
||||
description: string | undefined
|
||||
controlId: string
|
||||
children: React.ReactNode
|
||||
@@ -19,7 +21,7 @@ export default function Setting({
|
||||
<div className="ide-setting-description">{description}</div>
|
||||
)}
|
||||
</div>
|
||||
{children}
|
||||
<div className="ide-setting-input">{children}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,9 +9,10 @@ export default function SettingsTabPane({ tab }: { tab: SettingsTab }) {
|
||||
<TabPane eventKey={key} key={key}>
|
||||
{sections.map(section => (
|
||||
<SettingsSection key={section.key} title={section.title}>
|
||||
{section.settings.map(({ key, component }) => (
|
||||
<Fragment key={key}>{component}</Fragment>
|
||||
))}
|
||||
{section.settings.map(
|
||||
({ key, component, hidden }) =>
|
||||
!hidden && <Fragment key={key}>{component}</Fragment>
|
||||
)}
|
||||
</SettingsSection>
|
||||
))}
|
||||
</TabPane>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import React from 'react'
|
||||
import Setting from './setting'
|
||||
import OLFormSwitch from '@/shared/components/ol/ol-form-switch'
|
||||
|
||||
@@ -10,7 +11,7 @@ export default function ToggleSetting({
|
||||
disabled,
|
||||
}: {
|
||||
id: string
|
||||
label: string
|
||||
label: React.ReactNode
|
||||
description: string
|
||||
checked: boolean | undefined
|
||||
onChange: (newValue: boolean) => void
|
||||
|
||||
@@ -4,6 +4,7 @@ import OLTooltip from '@/shared/components/ol/ol-tooltip'
|
||||
import { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { GiveFeedbackLink } from './give-feedback-link'
|
||||
import { useIsNewEditorEnabledViaPrimaryFeatureFlag } from '../../utils/new-editor-utils'
|
||||
|
||||
export const BetaActions = () => {
|
||||
const { t } = useTranslation()
|
||||
@@ -11,6 +12,11 @@ export const BetaActions = () => {
|
||||
const openEditorRedesignSwitcherModal = useCallback(() => {
|
||||
setShowSwitcherModal(true)
|
||||
}, [setShowSwitcherModal])
|
||||
const showBetaActions = useIsNewEditorEnabledViaPrimaryFeatureFlag()
|
||||
|
||||
if (!showBetaActions) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -30,6 +30,7 @@ import { useSurveyUrl } from '../../hooks/use-survey-url'
|
||||
import getMeta from '@/utils/meta'
|
||||
import EditorCloneProjectModalWrapper from '@/features/clone-project-modal/components/editor-clone-project-modal-wrapper'
|
||||
import useOpenProject from '@/shared/hooks/use-open-project'
|
||||
import { canUseNewEditorViaPrimaryFeatureFlag } from '../../utils/new-editor-utils'
|
||||
|
||||
export const ToolbarMenuBar = () => {
|
||||
const { t } = useTranslation()
|
||||
@@ -43,6 +44,7 @@ export const ToolbarMenuBar = () => {
|
||||
const [showWordCountModal, setShowWordCountModal] = useState(false)
|
||||
const [showCloneProjectModal, setShowCloneProjectModal] = useState(false)
|
||||
const openProject = useOpenProject()
|
||||
const showEditorSwitchMenuOption = canUseNewEditorViaPrimaryFeatureFlag()
|
||||
|
||||
const anonymous = getMeta('ol-anonymous')
|
||||
|
||||
@@ -283,20 +285,24 @@ export const ToolbarMenuBar = () => {
|
||||
title={t('contact_us')}
|
||||
onClick={openContactUsModal}
|
||||
/>
|
||||
<MenuBarOption
|
||||
eventKey="give_feedback"
|
||||
title={t('give_feedback')}
|
||||
href={surveyURL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
/>
|
||||
<DropdownDivider />
|
||||
<SwitchToOldEditorMenuBarOption />
|
||||
<MenuBarOption
|
||||
eventKey="whats_new"
|
||||
title="What's new?"
|
||||
onClick={openEditorRedesignSwitcherModal}
|
||||
/>
|
||||
{showEditorSwitchMenuOption && (
|
||||
<>
|
||||
<MenuBarOption
|
||||
eventKey="give_feedback"
|
||||
title={t('give_feedback')}
|
||||
href={surveyURL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
/>
|
||||
<DropdownDivider />
|
||||
<SwitchToOldEditorMenuBarOption />
|
||||
<MenuBarOption
|
||||
eventKey="whats_new"
|
||||
title="What's new?"
|
||||
onClick={openEditorRedesignSwitcherModal}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</MenuBarDropdown>
|
||||
</MenuBar>
|
||||
<WordCountModal
|
||||
|
||||
@@ -24,6 +24,8 @@ import LineHeightSetting from '../components/settings/appearance-settings/line-h
|
||||
import FontFamilySetting from '../components/settings/appearance-settings/font-family-setting'
|
||||
import { AvailableUnfilledIcon } from '@/shared/components/material-icon'
|
||||
import { EditorLeftMenuProvider } from '@/features/editor-left-menu/components/editor-left-menu-context'
|
||||
import NewEditorSetting from '../components/settings/editor-settings/new-editor-setting'
|
||||
import { canUseNewEditorViaNewUserFeatureFlag } from '../utils/new-editor-utils'
|
||||
|
||||
const [referenceSearchSettingModule] = importOverleafModules(
|
||||
'referenceSearchSetting'
|
||||
@@ -75,6 +77,7 @@ export const SettingsModalProvider: FC<React.PropsWithChildren> = ({
|
||||
children,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const showEditorSwitch = canUseNewEditorViaNewUserFeatureFlag()
|
||||
|
||||
// TODO ide-redesign-cleanup: Rename this field and move it directly into this context
|
||||
const { leftMenuShown, setLeftMenuShown } = useLayoutContext()
|
||||
@@ -209,6 +212,11 @@ export const SettingsModalProvider: FC<React.PropsWithChildren> = ({
|
||||
key: 'lineHeight',
|
||||
component: <LineHeightSetting />,
|
||||
},
|
||||
{
|
||||
key: 'newEditor',
|
||||
component: <NewEditorSetting />,
|
||||
hidden: !showEditorSwitch,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -226,7 +234,7 @@ export const SettingsModalProvider: FC<React.PropsWithChildren> = ({
|
||||
href: '/user/subscription',
|
||||
},
|
||||
],
|
||||
[t]
|
||||
[t, showEditorSwitch]
|
||||
)
|
||||
|
||||
const settingToTabMap = useMemo(() => {
|
||||
|
||||
@@ -14,32 +14,45 @@ const isNewUser = () => {
|
||||
return createdAt > NEW_USER_CUTOFF_DATE
|
||||
}
|
||||
|
||||
export const canUseNewEditor = () => {
|
||||
export const canUseNewEditorViaPrimaryFeatureFlag = () => {
|
||||
return isSplitTestEnabled('editor-redesign')
|
||||
}
|
||||
|
||||
export const canUseNewEditorViaNewUserFeatureFlag = () => {
|
||||
const newUserTestVariant = getSplitTestVariant('editor-redesign-new-users')
|
||||
|
||||
const canUseNewEditorViaPrimaryFeatureFlag =
|
||||
isSplitTestEnabled('editor-redesign')
|
||||
const canUseNewEditorViaNewUserFeatureFlag =
|
||||
return (
|
||||
!canUseNewEditorViaPrimaryFeatureFlag() &&
|
||||
isNewUser() &&
|
||||
(newUserTestVariant === 'new-editor' ||
|
||||
newUserTestVariant === 'new-editor-old-logs')
|
||||
)
|
||||
}
|
||||
|
||||
export const canUseNewEditor = () => {
|
||||
return (
|
||||
canUseNewEditorViaPrimaryFeatureFlag || canUseNewEditorViaNewUserFeatureFlag
|
||||
canUseNewEditorViaPrimaryFeatureFlag() ||
|
||||
canUseNewEditorViaNewUserFeatureFlag()
|
||||
)
|
||||
}
|
||||
|
||||
const canUseNewLogs = () => {
|
||||
const newUserTestVariant = getSplitTestVariant('editor-redesign-new-users')
|
||||
const canUseNewLogsViaPrimaryFeatureFlag =
|
||||
isSplitTestEnabled('editor-redesign')
|
||||
const canUseNewLogsViaNewUserFeatureFlag =
|
||||
isNewUser() && newUserTestVariant === 'new-editor'
|
||||
|
||||
return (
|
||||
canUseNewLogsViaPrimaryFeatureFlag || canUseNewLogsViaNewUserFeatureFlag
|
||||
canUseNewEditorViaPrimaryFeatureFlag() || canUseNewLogsViaNewUserFeatureFlag
|
||||
)
|
||||
}
|
||||
|
||||
export const useIsNewEditorEnabledViaPrimaryFeatureFlag = () => {
|
||||
const { userSettings } = useUserSettingsContext()
|
||||
const hasAccess = canUseNewEditorViaPrimaryFeatureFlag()
|
||||
const enabled = userSettings.enableNewEditor
|
||||
return hasAccess && enabled
|
||||
}
|
||||
|
||||
export const useIsNewEditorEnabled = () => {
|
||||
const { userSettings } = useUserSettingsContext()
|
||||
const hasAccess = canUseNewEditor()
|
||||
|
||||
@@ -91,3 +91,25 @@
|
||||
color: var(--content-secondary);
|
||||
font-size: var(--font-size-02);
|
||||
}
|
||||
|
||||
.ide-setting-input {
|
||||
min-width: 160px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-left: var(--spacing-06);
|
||||
}
|
||||
|
||||
.ide-setting-new-editor {
|
||||
display: flex;
|
||||
gap: var(--spacing-04);
|
||||
}
|
||||
|
||||
.ide-setting-beta-tag {
|
||||
font-size: var(--font-size-01);
|
||||
line-height: var(--line-height-01);
|
||||
color: var(--green-60);
|
||||
background: var(--bg-accent-03);
|
||||
border: 1px solid var(--green-50);
|
||||
border-radius: var(--border-radius-full);
|
||||
padding: var(--spacing-01) var(--spacing-03);
|
||||
}
|
||||
|
||||
@@ -1419,6 +1419,9 @@
|
||||
"neither_agree_nor_disagree": "Neither agree nor disagree",
|
||||
"new_compile_domain_notice": "We’ve recently migrated PDF downloads to a new domain. Something might be blocking your browser from accessing that new domain, <0>__compilesUserContentDomain__</0>. This could be caused by network blocking or a strict browser plugin rule. Please follow our <1>troubleshooting guide</1>.",
|
||||
"new_create_tables_and_equations": "NEW! <sparkle/> Create tables and equations in seconds",
|
||||
"new_editor": "New editor",
|
||||
"new_editor_experience": "New editor experience",
|
||||
"new_editor_info": "Our new editor is currently in beta. Disabling this option will change your experience to the old Overleaf editor.",
|
||||
"new_file": "New file",
|
||||
"new_folder": "New folder",
|
||||
"new_look_and_feel": "New look and feel",
|
||||
|
||||
Reference in New Issue
Block a user