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

Merge pull request #27329 from overleaf/dp-editor-redesign-beta

Prepare editor redesign for beta release

GitOrigin-RevId: 584c0303f652640669234bd227780ed4f20b76c0
Mathias Jakobsen 1 год назад
Родитель
Сommit
8f2001e477

+ 2 - 0
services/web/app/src/Features/Tutorial/TutorialController.mjs

@@ -19,6 +19,8 @@ const VALID_KEYS = [
   'try-redesign-again-nudge-promo',
   'write-and-cite-nudge-in-linked-file',
   'ide-redesign-new-survey-promo',
+  'ide-redesign-beta-intro',
+  'ide-redesign-labs-user-beta-promo',
 ]
 
 async function completeTutorial(req, res, next) {

+ 10 - 0
services/web/frontend/extracted-translations.json

@@ -180,9 +180,11 @@
   "back_to_your_projects": "",
   "basic_compile_time": "",
   "before_you_use_error_assistant": "",
+  "beta": "",
   "beta_program_already_participating": "",
   "beta_program_benefits": "",
   "beta_program_not_participating": "",
+  "beta_program_the_new_overleaf_editor": "",
   "billed_annually_at": "",
   "billed_monthly_at": "",
   "billed_yearly": "",
@@ -892,6 +894,7 @@
   "it_looks_like_your_account_is_billed_manually_purchasing_additional_license_or_upgrading_subscription": "",
   "it_looks_like_your_payment_details_are_missing_please_update_your_billing_information": "",
   "italics": "",
+  "join_beta": "",
   "join_beta_program": "",
   "join_now": "",
   "join_overleaf_labs": "",
@@ -1779,6 +1782,7 @@
   "thanks_for_subscribing_to_plan_with_add_on": "",
   "thanks_for_subscribing_to_the_add_on": "",
   "thanks_for_subscribing_you_help_sl": "",
+  "thanks_for_trying_the_new_editor": "",
   "thanks_settings_updated": "",
   "the_add_on_will_remain_active_until": "",
   "the_code_editor_color_scheme": "",
@@ -1787,8 +1791,10 @@
   "the_following_files_and_folders_already_exist_in_this_project": "",
   "the_following_folder_already_exists_in_this_project": "",
   "the_following_folder_already_exists_in_this_project_plural": "",
+  "the_labs_experiment_has_now_finished": "",
   "the_latex_engine_used_for_compiling": "",
   "the_new_overleaf_editor": "",
+  "the_new_overleaf_editor_try_now_in_beta": "",
   "the_next_payment_will_be_collected_on": "",
   "the_original_text_has_changed": "",
   "the_overleaf_color_scheme": "",
@@ -1817,6 +1823,7 @@
   "this_experiment_isnt_accepting_new_participants": "",
   "this_field_is_required": "",
   "this_grants_access_to_features_2": "",
+  "this_is_a_beta_release_for_the_new_overleaf_editor": "",
   "this_is_a_labs_experiment_for_the_new_overleaf_editor_some_features_are_still_in_progress": "",
   "this_is_a_new_feature": "",
   "this_is_the_file_that_references_pulled_from_your_reference_manager_will_be_added_to": "",
@@ -2102,11 +2109,13 @@
   "were_performing_maintenance": "",
   "were_redesigning_our_editor_to_make_it_easier_to_use": "",
   "were_reducing_compile_timeout": "",
+  "weve_redesigned_our_editor_to_make_it_easier_to_use_and_future_ready": "",
   "what_did_you_find_most_helpful": "",
   "what_do_you_need_help_with": "",
   "what_does_this_mean_for_you": "",
   "what_happens_when_sso_is_enabled": "",
   "what_should_we_call_you": "",
+  "whats_different": "",
   "whats_different_in_the_new_editor": "",
   "when_you_tick_the_include_caption_box": "",
   "why_might_this_happen": "",
@@ -2162,6 +2171,7 @@
   "you_can_select_or_invite_collaborator": "",
   "you_can_select_or_invite_collaborator_plural": "",
   "you_can_still_use_your_premium_features": "",
+  "you_can_switch_back_to_the_old_editor_at_any_time": "",
   "you_cant_add_or_change_password_due_to_sso": "",
   "you_cant_join_this_group_subscription": "",
   "you_dont_have_any_add_ons_on_your_account": "",

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

@@ -3,10 +3,12 @@ import OLButton from '../ui/components/ol/ol-button'
 import { useIdeRedesignSwitcherContext } from '../ide-react/context/ide-redesign-switcher-context'
 import MaterialIcon from '@/shared/components/material-icon'
 import { useTranslation } from 'react-i18next'
+import { isNewEditorInBeta } from '../ide-redesign/utils/new-editor-utils'
 
 const TryNewEditorButton = () => {
   const { t } = useTranslation()
   const { setShowSwitcherModal } = useIdeRedesignSwitcherContext()
+  const newEditorBeta = isNewEditorInBeta()
 
   const onClick = useCallback(() => {
     setShowSwitcherModal(true)
@@ -17,7 +19,9 @@ const TryNewEditorButton = () => {
         className="toolbar-experiment-button"
         onClick={onClick}
         size="sm"
-        leadingIcon={<MaterialIcon type="experiment" unfilled />}
+        leadingIcon={
+          !newEditorBeta && <MaterialIcon type="experiment" unfilled />
+        }
         variant="secondary"
       >
         {t('try_the_new_editor')}

+ 2 - 0
services/web/frontend/js/features/ide-react/components/layout/main-layout.tsx

@@ -16,6 +16,7 @@ import HistoryContainer from '@/features/ide-react/components/history-container'
 import getMeta from '@/utils/meta'
 import { useEditorContext } from '@/shared/context/editor-context'
 import importOverleafModules from '../../../../../macros/import-overleaf-module.macro'
+import LabsUserBetaPromo from '@/features/ide-redesign/components/labs-user-beta-promo'
 
 const mainEditorLayoutModalsModules: Array<{
   import: { default: ElementType }
@@ -99,6 +100,7 @@ export const MainLayout: FC = () => {
             <PanelGroup autoSaveId="ide-inner-layout" direction="horizontal">
               <Panel className="ide-react-panel" id="panel-main" order={1}>
                 <HistoryContainer />
+                <LabsUserBetaPromo />
                 <EditorAndPdf />
               </Panel>
 

+ 16 - 2
services/web/frontend/js/features/ide-react/components/modals/modals.tsx

@@ -2,15 +2,29 @@ import { memo } from 'react'
 import ForceDisconnected from '@/features/ide-react/components/modals/force-disconnected'
 import { UnsavedDocs } from '@/features/ide-react/components/unsaved-docs/unsaved-docs'
 import SystemMessages from '@/shared/components/system-messages'
-import { IdeRedesignSwitcherModal } from '@/features/ide-redesign/components/switcher-modal/modal'
+import { IdeRedesignSwitcherModal as IdeRedesignSwitcherModalLabs } from '@/features/ide-redesign/components/switcher-modal/modal'
+import {
+  IdeRedesignSwitcherModal as IdeRedesignSwitcherModalBeta,
+  IdeRedesignIntroModal,
+} from '@/features/ide-redesign/components/switcher-modal/beta-modal'
+import { isNewEditorInBeta } from '@/features/ide-redesign/utils/new-editor-utils'
 
 export const Modals = memo(() => {
+  const newEditorBeta = isNewEditorInBeta()
+
   return (
     <>
       <ForceDisconnected />
       <UnsavedDocs />
       <SystemMessages />
-      <IdeRedesignSwitcherModal />
+      {newEditorBeta ? (
+        <>
+          <IdeRedesignIntroModal />
+          <IdeRedesignSwitcherModalBeta />
+        </>
+      ) : (
+        <IdeRedesignSwitcherModalLabs />
+      )}
     </>
   )
 })

+ 76 - 0
services/web/frontend/js/features/ide-redesign/components/labs-user-beta-promo.tsx

@@ -0,0 +1,76 @@
+import OLButton from '@/features/ui/components/ol/ol-button'
+import MaterialIcon from '@/shared/components/material-icon'
+import Notification from '@/shared/components/notification'
+import { useEditorContext } from '@/shared/context/editor-context'
+import useTutorial from '@/shared/hooks/promotions/use-tutorial'
+import { useEffect } from 'react'
+import { isNewEditorInBeta } from '../utils/new-editor-utils'
+import { isInExperiment } from '@/utils/labs-utils'
+import { useUserContext } from '@/shared/context/user-context'
+import { useTranslation } from 'react-i18next'
+
+const TUTORIAL_KEY = 'ide-redesign-labs-user-beta-promo'
+
+export default function LabsUserBetaPromoWrapper() {
+  const user = useUserContext()
+
+  // Show beta promo to labs users who are not in beta
+  // when the redesign switches to beta
+  if (
+    isNewEditorInBeta() &&
+    isInExperiment('editor-redesign') &&
+    !user.betaProgram
+  ) {
+    return <LabsUserBetaPromo />
+  }
+}
+
+const LabsUserBetaPromo = () => {
+  const { inactiveTutorials } = useEditorContext()
+  const { showPopup, tryShowingPopup, dismissTutorial } = useTutorial(
+    TUTORIAL_KEY,
+    { name: TUTORIAL_KEY }
+  )
+
+  useEffect(() => {
+    if (!inactiveTutorials.includes(TUTORIAL_KEY)) {
+      tryShowingPopup()
+    }
+  }, [tryShowingPopup, inactiveTutorials])
+
+  if (!showPopup) {
+    return null
+  }
+
+  return (
+    <Notification
+      className="ide-redesign-labs-user-beta-promo"
+      customIcon={<MaterialIcon type="experiment" unfilled />}
+      type="success"
+      isDismissible
+      content={<NotificationContent />}
+      onDismiss={dismissTutorial}
+    />
+  )
+}
+
+const NotificationContent = () => {
+  const { t } = useTranslation()
+  return (
+    <>
+      <div className="ide-redesign-labs-user-beta-promo-title">
+        {t('thanks_for_trying_the_new_editor')}
+      </div>
+      <div>{t('the_labs_experiment_has_now_finished')}</div>
+      <OLButton
+        variant="secondary"
+        className="ide-redesign-labs-user-beta-promo-button"
+        href="/beta/participate"
+        target="_blank"
+        rel="noreferrer noopener"
+      >
+        {t('join_beta')}
+      </OLButton>
+    </>
+  )
+}

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

@@ -0,0 +1,243 @@
+import { useIdeRedesignSwitcherContext } from '@/features/ide-react/context/ide-redesign-switcher-context'
+import OLButton from '@/features/ui/components/ol/ol-button'
+import OLModal, {
+  OLModalBody,
+  OLModalFooter,
+  OLModalHeader,
+  OLModalTitle,
+} from '@/features/ui/components/ol/ol-modal'
+import { FC, useCallback, useEffect } from 'react'
+import {
+  canUseNewEditor,
+  useIsNewEditorEnabled,
+} from '../../utils/new-editor-utils'
+import Notification from '@/shared/components/notification'
+import { useSwitchEnableNewEditorState } from '../../hooks/use-switch-enable-new-editor-state'
+import { useTranslation } from 'react-i18next'
+import { useEditorAnalytics } from '@/shared/hooks/use-editor-analytics'
+import { useSurveyUrl } from '../../hooks/use-survey-url'
+import useTutorial from '@/shared/hooks/promotions/use-tutorial'
+import { useEditorContext } from '@/shared/context/editor-context'
+
+const TUTORIAL_KEY = 'ide-redesign-beta-intro'
+
+export const IdeRedesignIntroModal: FC = () => {
+  const { t } = useTranslation()
+  const { inactiveTutorials } = useEditorContext()
+  const { showPopup, tryShowingPopup, dismissTutorial } = useTutorial(
+    TUTORIAL_KEY,
+    {
+      name: TUTORIAL_KEY,
+    }
+  )
+
+  useEffect(() => {
+    if (!inactiveTutorials.includes(TUTORIAL_KEY)) {
+      tryShowingPopup()
+    }
+  }, [tryShowingPopup, inactiveTutorials])
+
+  const hasAccess = canUseNewEditor()
+  if (!hasAccess) {
+    return null
+  }
+
+  return (
+    <OLModal
+      show={showPopup}
+      onHide={dismissTutorial}
+      className="ide-redesign-switcher-modal"
+    >
+      <OLModalHeader closeButton>
+        <OLModalTitle>
+          {t('the_new_overleaf_editor_try_now_in_beta')}
+        </OLModalTitle>
+      </OLModalHeader>
+      <OLModalBody>
+        <p>
+          {t(
+            'weve_redesigned_our_editor_to_make_it_easier_to_use_and_future_ready'
+          )}{' '}
+          {t('you_can_switch_back_to_the_old_editor_at_any_time')}
+        </p>
+        <SwitcherWhatsDifferent />
+      </OLModalBody>
+      <OLModalFooter>
+        <OLButton onClick={dismissTutorial} variant="secondary">
+          {t('close')}
+        </OLButton>
+      </OLModalFooter>
+    </OLModal>
+  )
+}
+
+export const IdeRedesignSwitcherModal = () => {
+  const { t } = useTranslation()
+  const { showSwitcherModal, setShowSwitcherModal } =
+    useIdeRedesignSwitcherContext()
+  const onHide = useCallback(
+    () => setShowSwitcherModal(false),
+    [setShowSwitcherModal]
+  )
+  const { loading, error, setEditorRedesignStatus } =
+    useSwitchEnableNewEditorState()
+  const enabled = useIsNewEditorEnabled()
+  const hasAccess = canUseNewEditor()
+  if (!hasAccess) {
+    return null
+  }
+
+  const Content = enabled
+    ? SwitcherModalContentEnabled
+    : SwitcherModalContentDisabled
+
+  return (
+    <OLModal
+      show={showSwitcherModal}
+      onHide={onHide}
+      className="ide-redesign-switcher-modal"
+    >
+      <OLModalHeader closeButton>
+        <OLModalTitle>
+          {enabled
+            ? t('beta_program_the_new_overleaf_editor')
+            : t('the_new_overleaf_editor_try_now_in_beta')}
+        </OLModalTitle>
+      </OLModalHeader>
+      {error && <Notification type="error" content={error} isDismissible />}
+      <Content
+        setEditorRedesignStatus={setEditorRedesignStatus}
+        hide={onHide}
+        loading={loading}
+      />
+    </OLModal>
+  )
+}
+
+type ModalContentProps = {
+  setEditorRedesignStatus: (enabled: boolean) => Promise<void>
+  hide: () => void
+  loading: boolean
+}
+
+const SwitcherModalContentEnabled: FC<ModalContentProps> = ({
+  setEditorRedesignStatus,
+  hide,
+  loading,
+}) => {
+  const { t } = useTranslation()
+  const { sendEvent } = useEditorAnalytics()
+  const disable = useCallback(() => {
+    sendEvent('editor-redesign-toggle', {
+      action: 'disable',
+      location: 'modal',
+    })
+    setEditorRedesignStatus(false)
+      .then(hide)
+      .catch(() => {
+        // do nothing, we're already showing the error
+      })
+  }, [setEditorRedesignStatus, hide, sendEvent])
+
+  const surveyURL = useSurveyUrl()
+
+  return (
+    <>
+      <OLModalBody>
+        <p>
+          {t(
+            'weve_redesigned_our_editor_to_make_it_easier_to_use_and_future_ready'
+          )}
+        </p>
+        <SwitcherWhatsDifferent />
+      </OLModalBody>
+      <OLModalFooter>
+        <OLButton
+          onClick={disable}
+          variant="secondary"
+          className="me-auto"
+          disabled={loading}
+        >
+          {t('switch_to_old_editor')}
+        </OLButton>
+        <OLButton onClick={hide} variant="secondary">
+          {t('cancel')}
+        </OLButton>
+        <OLButton
+          href={surveyURL}
+          target="_blank"
+          rel="noopener noreferrer"
+          variant="primary"
+        >
+          {t('give_feedback')}
+        </OLButton>
+      </OLModalFooter>
+    </>
+  )
+}
+
+const SwitcherModalContentDisabled: FC<ModalContentProps> = ({
+  setEditorRedesignStatus,
+  hide,
+  loading,
+}) => {
+  const { t } = useTranslation()
+  const { sendEvent } = useEditorAnalytics()
+  const enable = useCallback(() => {
+    sendEvent('editor-redesign-toggle', {
+      action: 'enable',
+      location: 'modal',
+    })
+    setEditorRedesignStatus(true)
+      .then(hide)
+      .catch(() => {
+        // do nothing, we're already showing the error
+      })
+  }, [setEditorRedesignStatus, hide, sendEvent])
+  return (
+    <>
+      <OLModalBody>
+        <p>
+          {t(
+            'weve_redesigned_our_editor_to_make_it_easier_to_use_and_future_ready'
+          )}
+        </p>
+        <SwitcherWhatsDifferent />
+        <LeavingNote />
+      </OLModalBody>
+      <OLModalFooter>
+        <OLButton onClick={hide} variant="secondary">
+          {t('cancel')}
+        </OLButton>
+        <OLButton onClick={enable} variant="primary" disabled={loading}>
+          {t('switch_to_new_editor')}
+        </OLButton>
+      </OLModalFooter>
+    </>
+  )
+}
+
+const SwitcherWhatsDifferent = () => {
+  const { t } = useTranslation()
+
+  return (
+    <div className="ide-redesign-switcher-modal-whats-new">
+      <h4>{t('whats_different')}</h4>
+      <ul>
+        <li>{t('new_look_and_feel')}</li>
+        <li>
+          {t('new_navigation_introducing_left_hand_side_rail_and_top_menus')}
+        </li>
+        <li>{t('new_look_and_placement_of_the_settings')}</li>
+        <li>{t('improved_dark_mode')}</li>
+        <li>{t('review_panel_and_error_logs_moved_to_the_left')}</li>
+      </ul>
+    </div>
+  )
+}
+
+const LeavingNote = () => {
+  const { t } = useTranslation()
+
+  return <p>{t('you_can_switch_back_to_the_old_editor_at_any_time')}</p>
+}

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

@@ -0,0 +1,36 @@
+import { useIdeRedesignSwitcherContext } from '@/features/ide-react/context/ide-redesign-switcher-context'
+import OLButton from '@/features/ui/components/ol/ol-button'
+import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
+import { useCallback } from 'react'
+import { useTranslation } from 'react-i18next'
+import { GiveFeedbackLink } from './give-feedback-link'
+
+export const BetaActions = () => {
+  const { t } = useTranslation()
+  const { setShowSwitcherModal } = useIdeRedesignSwitcherContext()
+  const openEditorRedesignSwitcherModal = useCallback(() => {
+    setShowSwitcherModal(true)
+  }, [setShowSwitcherModal])
+
+  return (
+    <>
+      <div className="ide-redesign-toolbar-button-container">
+        <OLTooltip
+          id="tooltip-beta-button"
+          description={t('this_is_a_beta_release_for_the_new_overleaf_editor')}
+          overlayProps={{ delay: 0, placement: 'bottom' }}
+        >
+          <OLButton
+            size="sm"
+            variant="secondary"
+            className="ide-redesign-beta-button"
+            onClick={openEditorRedesignSwitcherModal}
+          >
+            {t('beta')}
+          </OLButton>
+        </OLTooltip>
+      </div>
+      <GiveFeedbackLink />
+    </>
+  )
+}

+ 20 - 0
services/web/frontend/js/features/ide-redesign/components/toolbar/give-feedback-link.tsx

@@ -0,0 +1,20 @@
+import { useTranslation } from 'react-i18next'
+import { useSurveyUrl } from '../../hooks/use-survey-url'
+
+export const GiveFeedbackLink = () => {
+  const { t } = useTranslation()
+  const surveyURL = useSurveyUrl()
+
+  return (
+    <div className="ide-redesign-toolbar-button-container">
+      <a
+        href={surveyURL}
+        rel="noopener noreferrer"
+        target="_blank"
+        className="ide-redesign-toolbar-labs-feedback-link"
+      >
+        {t('give_feedback')}
+      </a>
+    </div>
+  )
+}

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

@@ -4,7 +4,7 @@ import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
 import MaterialIcon from '@/shared/components/material-icon'
 import { useCallback } from 'react'
 import { useTranslation } from 'react-i18next'
-import { useSurveyUrl } from '../../hooks/use-survey-url'
+import { GiveFeedbackLink } from './give-feedback-link'
 
 export const LabsActions = () => {
   const { t } = useTranslation()
@@ -12,7 +12,6 @@ export const LabsActions = () => {
   const openEditorRedesignSwitcherModal = useCallback(() => {
     setShowSwitcherModal(true)
   }, [setShowSwitcherModal])
-  const surveyURL = useSurveyUrl()
 
   return (
     <>
@@ -35,16 +34,7 @@ export const LabsActions = () => {
           </OLButton>
         </OLTooltip>
       </div>
-      <div className="ide-redesign-toolbar-button-container">
-        <a
-          href={surveyURL}
-          rel="noopener noreferrer"
-          target="_blank"
-          className="ide-redesign-toolbar-labs-feedback-link"
-        >
-          {t('give_feedback')}
-        </a>
-      </div>
+      <GiveFeedbackLink />
     </>
   )
 }

+ 4 - 1
services/web/frontend/js/features/ide-redesign/components/toolbar/toolbar.tsx

@@ -16,6 +16,8 @@ import importOverleafModules from '../../../../../macros/import-overleaf-module.
 import UpgradeButton from './upgrade-button'
 import getMeta from '@/utils/meta'
 import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context'
+import { isNewEditorInBeta } from '../../utils/new-editor-utils'
+import { BetaActions } from './beta-actions'
 
 const [publishModalModules] = importOverleafModules('publishModal')
 const SubmitProjectButton = publishModalModules?.import.NewPublishToolbarButton
@@ -28,6 +30,7 @@ export const Toolbar = () => {
   const shouldDisplaySubmitButton =
     (permissionsLevel === 'owner' || permissionsLevel === 'readAndWrite') &&
     SubmitProjectButton
+  const newEditorBeta = isNewEditorInBeta()
 
   const handleBackToEditorClick = useCallback(() => {
     eventTracking.sendMB('navigation-clicked-history', { action: 'close' })
@@ -54,7 +57,7 @@ export const Toolbar = () => {
       </div>
       <ToolbarProjectTitle />
       <div className="ide-redesign-toolbar-actions">
-        <LabsActions />
+        {newEditorBeta ? <BetaActions /> : <LabsActions />}
         <OnlineUsers />
         <ShowHistoryButton />
         <ChangeLayoutButton />

+ 15 - 1
services/web/frontend/js/features/ide-redesign/hooks/use-survey-url.tsx

@@ -1,7 +1,21 @@
-import { useFeatureFlag } from '@/shared/context/split-test-context'
+import {
+  useFeatureFlag,
+  useSplitTest,
+} from '@/shared/context/split-test-context'
+import { isNewEditorInBeta } from '../utils/new-editor-utils'
 
 export const useSurveyUrl = () => {
   const newErrorlogs = useFeatureFlag('new-editor-error-logs-redesign')
+  const splitTestConfig = useSplitTest('editor-redesign')
+  const newEditorBeta = isNewEditorInBeta()
+
+  if (newEditorBeta) {
+    return (
+      splitTestConfig.info?.badgeInfo?.url ||
+      'https://forms.gle/NGkALNUiMbanjp3Q7'
+    )
+  }
+
   if (newErrorlogs) {
     return 'https://forms.gle/83QJ9ALJkiugxTZf8'
   }

+ 13 - 2
services/web/frontend/js/features/ide-redesign/utils/new-editor-utils.ts

@@ -1,9 +1,20 @@
 import { useUserSettingsContext } from '@/shared/context/user-settings-context'
 import { isInExperiment } from '@/utils/labs-utils'
+import getMeta from '@/utils/meta'
 import { isSplitTestEnabled } from '@/utils/splitTestUtils'
 
-export const canUseNewEditor = () =>
-  isInExperiment('editor-redesign') || isSplitTestEnabled('editor-redesign')
+export const isNewEditorInBeta = () => {
+  const splitTestInfo = getMeta('ol-splitTestInfo') || {}
+  return splitTestInfo['editor-redesign']?.phase === 'beta'
+}
+
+export const canUseNewEditor = () => {
+  const inBetaPhase = isNewEditorInBeta()
+  return (
+    (!inBetaPhase && isInExperiment('editor-redesign')) ||
+    isSplitTestEnabled('editor-redesign')
+  )
+}
 
 export const useIsNewEditorEnabled = () => {
   const { userSettings } = useUserSettingsContext()

+ 25 - 0
services/web/frontend/stylesheets/bootstrap-5/pages/editor/ide-redesign.scss

@@ -53,3 +53,28 @@
 .ide-redesign-editor-content {
   height: 100%;
 }
+
+.ide-redesign-labs-user-beta-promo {
+  position: absolute;
+  top: 60px;
+  right: 28px;
+  z-index: 100000;
+  max-width: 560px;
+  font-size: var(--font-size-03);
+  line-height: var(--line-height-03);
+
+  .notification-content {
+    display: flex;
+    flex-direction: column;
+    gap: var(--spacing-02);
+    align-items: flex-start;
+  }
+}
+
+.ide-redesign-labs-user-beta-promo-title {
+  font-weight: bold;
+}
+
+.ide-redesign-labs-user-beta-promo-button {
+  margin-top: var(--spacing-05);
+}

+ 4 - 0
services/web/frontend/stylesheets/bootstrap-5/pages/editor/toolbar-redesign.scss

@@ -237,3 +237,7 @@
 .ide-redesign-labs-button.btn.btn-secondary {
   @include labs-button;
 }
+
+.ide-redesign-beta-button.btn.btn-secondary {
+  @include labs-button;
+}

+ 9 - 0
services/web/locales/en.json

@@ -237,6 +237,7 @@
   "beta_program_not_participating": "You are not enrolled in the Beta Program",
   "beta_program_opt_in_action": "Opt-In to Beta Program",
   "beta_program_opt_out_action": "Opt-Out of Beta Program",
+  "beta_program_the_new_overleaf_editor": "Beta program: the new Overleaf editor",
   "bibliographies": "Bibliographies",
   "billed_annually_at": "Billed annually at <0>__price__</0> <1>(includes plan and any add-ons)</1>",
   "billed_monthly_at": "Billed monthly at <0>__price__</0> <1>(includes plan and any add-ons)</1>",
@@ -1149,6 +1150,7 @@
   "italics": "Italics",
   "ja": "Japanese",
   "january": "January",
+  "join_beta": "Join beta",
   "join_beta_program": "Join the beta program",
   "join_labs": "Join Labs",
   "join_now": "Join now",
@@ -2293,6 +2295,7 @@
   "thanks_for_subscribing_to_plan_with_add_on": "Thank you for subscribing to the __planName__ plan with the __addOnName__ add-on. It’s support from people like yourself that allows __appName__ to continue to grow and improve.",
   "thanks_for_subscribing_to_the_add_on": "Thank you for subscribing to the __addOnName__ add-on. It’s support from people like yourself that allows __appName__ to continue to grow and improve.",
   "thanks_for_subscribing_you_help_sl": "Thank you for subscribing to the __planName__ plan. It’s support from people like yourself that allows __appName__ to continue to grow and improve.",
+  "thanks_for_trying_the_new_editor": "Thanks for trying the new editor",
   "thanks_settings_updated": "Thanks, your settings have been updated.",
   "the_add_on_will_remain_active_until": "The add-on will remain active until the end of the current billing period.",
   "the_code_editor_color_scheme": "The code editor color scheme",
@@ -2302,8 +2305,10 @@
   "the_following_files_and_folders_already_exist_in_this_project": "The following files and folders already exist in this project:",
   "the_following_folder_already_exists_in_this_project": "The following folder already exists in this project:",
   "the_following_folder_already_exists_in_this_project_plural": "The following folders already exist in this project:",
+  "the_labs_experiment_has_now_finished": "The Labs experiment has now finished, but you can still use the new editor by joining our beta program.",
   "the_latex_engine_used_for_compiling": "The LaTeX engine used for compiling",
   "the_new_overleaf_editor": "The new Overleaf editor",
+  "the_new_overleaf_editor_try_now_in_beta": "The new Overleaf editor — try now in beta",
   "the_next_payment_will_be_collected_on": "The next payment will be collected on <strong>__date__</strong>.",
   "the_original_text_has_changed": "The original text has changed, so this suggestion can’t be applied",
   "the_overleaf_color_scheme": "The __appName__ color scheme",
@@ -2341,6 +2346,7 @@
   "this_experiment_isnt_accepting_new_participants": "This experiment isn’t accepting new participants.",
   "this_field_is_required": "This field is required",
   "this_grants_access_to_features_2": "This grants you access to <0>__appName__</0> <0>__featureType__</0> features.",
+  "this_is_a_beta_release_for_the_new_overleaf_editor": "This is a beta release for the new Overleaf editor. You can switch back to the old editor at any time.",
   "this_is_a_labs_experiment_for_the_new_overleaf_editor_some_features_are_still_in_progress": "This is a Labs experiment for the new Overleaf editor. Some features are still in progress, so if you need something that’s missing, you can switch back to the old editor at any time.",
   "this_is_a_new_feature": "This is a new feature",
   "this_is_the_file_that_references_pulled_from_your_reference_manager_will_be_added_to": "This is the file that references pulled from your reference manager will be added to.",
@@ -2650,12 +2656,14 @@
   "were_performing_maintenance": "We’re performing maintenance on Overleaf and you need to wait a moment. Sorry for any inconvenience. The editor will refresh automatically in __seconds__ seconds.",
   "were_redesigning_our_editor_to_make_it_easier_to_use": "We’re redesigning our editor to make it easier to use and ensure it’s future ready. Try it out and give us your feedback to help us get this right. (Some features are still in the works, so you can switch back at any time.)",
   "were_reducing_compile_timeout": "We’re in the process of <0>reducing the compile timeout limit</0> on our free plan, which may affect your project in future.",
+  "weve_redesigned_our_editor_to_make_it_easier_to_use_and_future_ready": "We’ve redesigned our editor to make it easier to use and future ready. It’s now in beta, so try it out and give us your feedback.",
   "what_did_you_find_most_helpful": "What did you find most helpful?",
   "what_do_you_need": "What do you need?",
   "what_do_you_need_help_with": "What do you need help with?",
   "what_does_this_mean_for_you": "This means:",
   "what_happens_when_sso_is_enabled": "What happens when SSO is enabled?",
   "what_should_we_call_you": "What should we call you?",
+  "whats_different": "What’s different?",
   "whats_different_in_the_new_editor": "What’s different in the new editor?",
   "when_you_join_labs": "When you join Labs, you can choose which experiments you want to be part of. Once you’ve done that, you can use Overleaf as normal, but you’ll see any labs features marked with this badge:",
   "when_you_tick_the_include_caption_box": "When you tick the box “Include caption” the image will be inserted into your document with a placeholder caption. To edit it, you simply select the placeholder text and type to replace it with your own.",
@@ -2719,6 +2727,7 @@
   "you_can_select_or_invite_collaborator": "You can select or invite __count__ collaborator on your current plan. Upgrade to add more editors or reviewers.",
   "you_can_select_or_invite_collaborator_plural": "You can select or invite __count__ collaborators on your current plan. Upgrade to add more editors or reviewers.",
   "you_can_still_use_your_premium_features": "You can still use your premium features until the pause becomes active.",
+  "you_can_switch_back_to_the_old_editor_at_any_time": "You can switch back to the old editor at any time.",
   "you_cant_add_or_change_password_due_to_sso": "You can’t add or change your password because your group or organization uses <0>single sign-on (SSO)</0>.",
   "you_cant_join_this_group_subscription": "You can’t join this group subscription",
   "you_cant_reset_password_due_to_sso": "You can’t reset your password because your group or organization uses SSO. <0>Log in with SSO</0>.",