Просмотр исходного кода

Merge pull request #29511 from overleaf/dp-new-users-to-new-editor

Move all new users to use the new editor

GitOrigin-RevId: e3611e5853da4b96db9f4cc37114ededb8632aed
David 9 месяцев назад
Родитель
Сommit
c4b3cd2a77

+ 2 - 2
services/web/frontend/js/features/editor-left-menu/components/settings/settings-new-editor.tsx

@@ -4,7 +4,7 @@ import { useSwitchEnableNewEditorState } from '@/features/ide-redesign/hooks/use
 import { useLayoutContext } from '@/shared/context/layout-context'
 import { useCallback } from 'react'
 import {
-  canUseNewEditorViaNewUserFeatureFlag,
+  canUseNewEditorAsNewUser,
   useIsNewEditorEnabled,
 } from '@/features/ide-redesign/utils/new-editor-utils'
 
@@ -13,7 +13,7 @@ export default function SettingsNewEditor() {
   const { setEditorRedesignStatus } = useSwitchEnableNewEditorState()
   const { setLeftMenuShown } = useLayoutContext()
   const enabled = useIsNewEditorEnabled()
-  const show = canUseNewEditorViaNewUserFeatureFlag()
+  const show = canUseNewEditorAsNewUser()
 
   const onChange = useCallback(
     (newValue: boolean) => {

+ 2 - 2
services/web/frontend/js/features/editor-navigation-toolbar/try-new-editor-button.tsx

@@ -2,13 +2,13 @@ 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 { canUseNewEditorAsExistingUser } from '../ide-redesign/utils/new-editor-utils'
 import { useSwitchEnableNewEditorState } from '../ide-redesign/hooks/use-switch-enable-new-editor-state'
 
 const TryNewEditorButton = () => {
   const { t } = useTranslation()
   const { setShowSwitcherModal } = useIdeRedesignSwitcherContext()
-  const showModal = canUseNewEditorViaPrimaryFeatureFlag()
+  const showModal = canUseNewEditorAsExistingUser()
   const { loading, setEditorRedesignStatus } = useSwitchEnableNewEditorState()
 
   const onClick = useCallback(() => {

+ 2 - 2
services/web/frontend/js/features/ide-redesign/components/switcher-modal/beta-modal.tsx

@@ -11,7 +11,7 @@ import { FC, useCallback, useEffect } from 'react'
 import {
   canUseNewEditor,
   useIsNewEditorEnabled,
-  useIsNewEditorEnabledViaPrimaryFeatureFlag,
+  useIsNewEditorEnabledAsExistingUser,
 } from '../../utils/new-editor-utils'
 import Notification from '@/shared/components/notification'
 import { useSwitchEnableNewEditorState } from '../../hooks/use-switch-enable-new-editor-state'
@@ -32,7 +32,7 @@ export const IdeRedesignIntroModal: FC = () => {
       name: TUTORIAL_KEY,
     }
   )
-  const hasAccess = useIsNewEditorEnabledViaPrimaryFeatureFlag()
+  const hasAccess = useIsNewEditorEnabledAsExistingUser()
 
   useEffect(() => {
     if (!hasAccess) return

+ 2 - 2
services/web/frontend/js/features/ide-redesign/components/toolbar/beta-actions.tsx

@@ -4,7 +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'
+import { useIsNewEditorEnabledAsExistingUser } from '../../utils/new-editor-utils'
 
 export const BetaActions = () => {
   const { t } = useTranslation()
@@ -12,7 +12,7 @@ export const BetaActions = () => {
   const openEditorRedesignSwitcherModal = useCallback(() => {
     setShowSwitcherModal(true)
   }, [setShowSwitcherModal])
-  const showBetaActions = useIsNewEditorEnabledViaPrimaryFeatureFlag()
+  const showBetaActions = useIsNewEditorEnabledAsExistingUser()
 
   if (!showBetaActions) {
     return null

+ 2 - 2
services/web/frontend/js/features/ide-redesign/components/toolbar/menu-bar.tsx

@@ -30,7 +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'
+import { canUseNewEditorAsExistingUser } from '../../utils/new-editor-utils'
 
 export const ToolbarMenuBar = () => {
   const { t } = useTranslation()
@@ -44,7 +44,7 @@ export const ToolbarMenuBar = () => {
   const [showWordCountModal, setShowWordCountModal] = useState(false)
   const [showCloneProjectModal, setShowCloneProjectModal] = useState(false)
   const openProject = useOpenProject()
-  const showEditorSwitchMenuOption = canUseNewEditorViaPrimaryFeatureFlag()
+  const showEditorSwitchMenuOption = canUseNewEditorAsExistingUser()
 
   const anonymous = getMeta('ol-anonymous')
 

+ 2 - 2
services/web/frontend/js/features/ide-redesign/contexts/settings-modal-context.tsx

@@ -25,7 +25,7 @@ import FontFamilySetting from '../components/settings/appearance-settings/font-f
 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'
+import { canUseNewEditorAsNewUser } from '../utils/new-editor-utils'
 
 const [referenceSearchSettingModule] = importOverleafModules(
   'referenceSearchSetting'
@@ -77,7 +77,7 @@ export const SettingsModalProvider: FC<React.PropsWithChildren> = ({
   children,
 }) => {
   const { t } = useTranslation()
-  const showEditorSwitch = canUseNewEditorViaNewUserFeatureFlag()
+  const showEditorSwitch = canUseNewEditorAsNewUser()
 
   // TODO ide-redesign-cleanup: Rename this field and move it directly into this context
   const { leftMenuShown, setLeftMenuShown } = useLayoutContext()

+ 33 - 23
services/web/frontend/js/features/ide-redesign/utils/new-editor-utils.ts

@@ -2,13 +2,24 @@ import { useUserSettingsContext } from '@/shared/context/user-settings-context'
 import getMeta from '@/utils/meta'
 import { isSplitTestEnabled, getSplitTestVariant } from '@/utils/splitTestUtils'
 
-export const ignoringUserCutoffDate =
+const ignoringUserCutoffDate =
   new URLSearchParams(window.location.search).get('skip-new-user-check') ===
   'true'
 
-const NEW_USER_CUTOFF_DATE = new Date(Date.UTC(2025, 8, 23, 13, 0, 0)) // 2pm British Summer Time on September 23, 2025
+// For E2E tests, allow forcing a user to be treated as an existing user
+const existingUserOverride =
+  new URLSearchParams(window.location.search).get('existing-user-override') ===
+  'true'
+
+// We don't want to enable the new editor on server-pro/CE until we have fully rolled it out on SaaS
+const { isOverleaf } = getMeta('ol-ExposedSettings')
+
+const SPLIT_TEST_USER_CUTOFF_DATE = new Date(Date.UTC(2025, 8, 23, 13, 0, 0)) // 2pm British Summer Time on September 23, 2025
+const NEW_USER_CUTOFF_DATE = new Date(Date.UTC(2025, 10, 12, 12, 0, 0)) // 12pm GMT on November 12, 2025
 
 export const isNewUser = () => {
+  if (existingUserOverride) return false
+
   if (ignoringUserCutoffDate) return true
   const user = getMeta('ol-user')
 
@@ -18,32 +29,37 @@ export const isNewUser = () => {
   return createdAt > NEW_USER_CUTOFF_DATE
 }
 
-export const canUseNewEditorViaPrimaryFeatureFlag = () => {
-  return isSplitTestEnabled('editor-redesign')
-}
+export const isSplitTestUser = () => {
+  if (existingUserOverride) return false
 
-export const canUseNewEditorViaNewUserFeatureFlag = () => {
-  const newUserTestVariant = getSplitTestVariant('editor-redesign-new-users')
+  const user = getMeta('ol-user')
+  if (!user.signUpDate) return false
 
+  const createdAt = new Date(user.signUpDate)
   return (
-    !canUseNewEditorViaPrimaryFeatureFlag() &&
-    isNewUser() &&
-    (newUserTestVariant === 'new-editor' ||
-      newUserTestVariant === 'new-editor-old-logs' ||
-      newUserTestVariant === 'new-editor-new-logs-old-position')
+    createdAt > SPLIT_TEST_USER_CUTOFF_DATE && createdAt <= NEW_USER_CUTOFF_DATE
   )
 }
 
-export const canUseNewEditor = () => {
+export const canUseNewEditorAsExistingUser = () => {
+  return !canUseNewEditorAsNewUser() && isSplitTestEnabled('editor-redesign')
+}
+
+export const canUseNewEditorAsNewUser = () => {
+  const newUserTestVariant = getSplitTestVariant('editor-redesign-new-users')
   return (
-    canUseNewEditorViaPrimaryFeatureFlag() ||
-    canUseNewEditorViaNewUserFeatureFlag()
+    isOverleaf &&
+    (isNewUser() || (isSplitTestUser() && newUserTestVariant !== 'default'))
   )
 }
 
-export const useIsNewEditorEnabledViaPrimaryFeatureFlag = () => {
+export const canUseNewEditor = () => {
+  return canUseNewEditorAsExistingUser() || canUseNewEditorAsNewUser()
+}
+
+export const useIsNewEditorEnabledAsExistingUser = () => {
   const { userSettings } = useUserSettingsContext()
-  const hasAccess = canUseNewEditorViaPrimaryFeatureFlag()
+  const hasAccess = canUseNewEditorAsExistingUser()
   const enabled = userSettings.enableNewEditor
   return hasAccess && enabled
 }
@@ -54,9 +70,3 @@ export const useIsNewEditorEnabled = () => {
   const enabled = userSettings.enableNewEditor
   return hasAccess && enabled
 }
-
-export function useNewEditorVariant() {
-  const newEditor = useIsNewEditorEnabled()
-  if (!newEditor) return 'default'
-  return 'new-editor-new-logs-old-position'
-}

+ 5 - 17
services/web/frontend/js/features/pdf-preview/components/timeout-upgrade-prompt-new.tsx

@@ -8,12 +8,9 @@ import OLButton from '@/shared/components/ol/ol-button'
 import * as eventTracking from '../../../infrastructure/event-tracking'
 import getMeta from '@/utils/meta'
 import { populateEditorRedesignSegmentation } from '@/shared/hooks/use-editor-analytics'
-import {
-  isNewUser,
-  useIsNewEditorEnabled,
-} from '@/features/ide-redesign/utils/new-editor-utils'
-import { getSplitTestVariant, isSplitTestEnabled } from '@/utils/splitTestUtils'
 import CompileTimeoutPaywallModal from '@/features/pdf-preview/components/compile-timeout-paywall-modal'
+import { useIsNewEditorEnabled } from '@/features/ide-redesign/utils/new-editor-utils'
+import { isSplitTestEnabled } from '@/utils/splitTestUtils'
 
 function TimeoutUpgradePromptNew() {
   const {
@@ -98,21 +95,12 @@ const CompileTimeout = memo(function CompileTimeout({
   isCompileTimeoutTargetPlansEnabled,
 }: CompileTimeoutProps) {
   const { t } = useTranslation()
+  const newEditor = useIsNewEditorEnabled()
   const extraSearchParams = useMemo(() => {
-    if (!isNewUser()) {
-      return undefined
-    }
-
-    const variant = getSplitTestVariant('editor-redesign-new-users')
-
-    if (!variant) {
-      return undefined
-    }
-
     return {
-      itm_content: variant,
+      itm_content: newEditor ? 'new-editor' : 'old-editor',
     }
-  }, [])
+  }, [newEditor])
 
   const handleFreeTrialClick = useCallback(
     (event: React.MouseEvent<HTMLButtonElement>) => {

+ 0 - 86
services/web/test/frontend/components/pdf-preview/new-editor-utils.test.tsx

@@ -1,86 +0,0 @@
-import { expect } from 'chai'
-import { mockScope } from './scope'
-import { EditorProviders } from '../../helpers/editor-providers'
-import { renderHook } from '@testing-library/react'
-import { useNewEditorVariant } from '@/features/ide-redesign/utils/new-editor-utils'
-
-describe('new-editor-utils', function () {
-  describe('useNewEditorVariant', function () {
-    const newEditorVariants = [
-      { splitTestVariant: 'default', uiVariant: 'default' },
-      {
-        splitTestVariant: 'new-editor',
-        uiVariant: 'new-editor-new-logs-old-position',
-      },
-      {
-        splitTestVariant: 'new-editor-old-logs',
-        uiVariant: 'new-editor-new-logs-old-position',
-      },
-      {
-        splitTestVariant: 'new-editor-new-logs-old-position',
-        uiVariant: 'new-editor-new-logs-old-position',
-      },
-    ]
-    for (const variant of newEditorVariants) {
-      it(`forwards ?editor-redesign-new-users=${variant}`, function () {
-        window.metaAttributesCache.set('ol-splitTestVariants', {
-          'editor-redesign-new-users': variant.splitTestVariant,
-        })
-
-        const scope = mockScope()
-
-        const { result } = renderHook(() => useNewEditorVariant(), {
-          wrapper: ({ children }) => (
-            <EditorProviders
-              scope={scope}
-              userSettings={{ enableNewEditor: true }}
-            >
-              {children}
-            </EditorProviders>
-          ),
-        })
-        expect(result.current).to.equal(variant.uiVariant)
-      })
-    }
-    for (const variant of newEditorVariants) {
-      it(`ignores ?editor-redesign-new-users=${variant} when disabled by user`, function () {
-        window.metaAttributesCache.set('ol-splitTestVariants', {
-          'editor-redesign-new-users': variant.splitTestVariant,
-        })
-
-        const scope = mockScope()
-
-        const { result } = renderHook(() => useNewEditorVariant(), {
-          wrapper: ({ children }) => (
-            <EditorProviders
-              scope={scope}
-              userSettings={{ enableNewEditor: false }}
-            >
-              {children}
-            </EditorProviders>
-          ),
-        })
-        expect(result.current).to.equal('default')
-      })
-    }
-    it(`handles ?editor-redesign=enabled`, function () {
-      window.metaAttributesCache.set('ol-splitTestVariants', {
-        'editor-redesign': 'enabled',
-      })
-
-      const scope = mockScope()
-
-      const { result } = renderHook(() => useNewEditorVariant(), {
-        wrapper: ({ children }) => (
-          <EditorProviders
-            scope={scope}
-            userSettings={{ enableNewEditor: true }}
-          >
-            {children}
-          </EditorProviders>
-        ),
-      })
-      expect(result.current).to.equal('new-editor-new-logs-old-position')
-    })
-  })
-})