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

[web] Paywall CTA split-test (#17555)

* Fix `{splitTest.requiredCohortSize && (...)}` can display `0`

* Get `paywall-cta` assignment in ProjectController.js

* CTA change: "Get Dropbox Sync"

https://miro.com/app/board/uXjVMFLu5J8=/?openComment=3458764578763286026

* CTA change: "Get GitHub Sync"

https://miro.com/app/board/uXjVMFLu5J8=/?openComment=3458764578763286736

* CTA change: "Get Git integration"

https://miro.com/app/board/uXjVMFLu5J8=/?openComment=3458764578763286283

* CTA change: "Add more collaborators"

https://miro.com/app/board/uXjVMFLu5J8=/?openComment=3458764578763884855

* CTA change: "Get track changes"

https://miro.com/app/board/uXjVMFLu5J8=/?openComment=3458764578764030761

* Update wording and position: "Already subscribed? Try refreshing the page."

https://miro.com/app/board/uXjVMFLu5J8=/?openComment=3458764578764366969

* Casing update: "Upgrade to track changes"

https://miro.com/app/board/uXjVMFLu5J8=/?openComment=3458764578764209961

* CTA changes: "Get Mendeley integration" + "Get Zotero integration"

https://miro.com/app/board/uXjVMFLu5J8=/?openComment=3458764578764547424

https://miro.com/app/board/uXjVMFLu5J8=/?openComment=3458764578764547269

* CTA change: "Get full project history"

https://miro.com/app/board/uXjVMFLu5J8=/?openComment=3458764578764762005

* Casing update: "full project history"

https://miro.com/app/board/uXjVMFLu5J8=/?openComment=3458764578764762280

* CTA change: "Get more compile time" in timeout-upgrade-prompt-new.tsx

https://miro.com/app/board/uXjVMFLu5J8=/?openComment=3458764578764762563

* CTA change: "Get more compile time" in compile-timeout-warning.tsx

https://miro.com/app/board/uXjVMFLu5J8=/?openComment=3458764578764762726

* CTA change: "Get advanced reference search"

https://miro.com/app/board/uXjVMFLu5J8=/?openComment=3458764578764969087

* Update casing and wording: "advanced reference search"

https://miro.com/app/board/uXjVMFLu5J8=/?openComment=3458764578764969412

* CTA change: "Get Symbol Palette"

https://miro.com/app/board/uXjVMFLu5J8=/?openComment=3458764578765128906

* Update title: "Subscribe to find the symbols you need faster"

https://miro.com/app/board/uXjVMFLu5J8=/?openComment=3458764578765289664

* Revert use of `satisfies`: it doesn't work with our version of prettier (https://github.com/prettier/prettier/issues/13516)

* CTA change: "Get more compile time" in compile-timeout-changing-soon.tsx

⚠️ not in miro

* Rename `paywallCtaAssignment`, remove useless export and comment

Addressing Fahru's review

* Add alternative texts in `/registration/try-premium` page

* CTA change: "Get more compile time" in timeout-upgrade-prompt.jsx

https://miro.com/app/board/uXjVMFLu5J8=/?openComment=3458764578764762563

* CTA change: "Get GitHub Sync" in import-project-from-github-modal-content.tsx

Not in Miro, but related to: https://miro.com/app/board/uXjVMFLu5J8=/?openComment=3458764578763286736

* CTA change: "Get more compile time" in compile-time-warning.tsx

Not in Miro, but related to others

* Fix compile-time-warning style (spacings, overflows out of window)

* `npm run format:fix`

GitOrigin-RevId: 0d8d1808b5901c2761d35494c49713d26721bcfd
Antoine Clausse 2 лет назад
Родитель
Сommit
92a58c8f3b

+ 3 - 0
services/web/app/src/Features/Project/ProjectController.js

@@ -616,6 +616,9 @@ const ProjectController = {
         compileLogEventsAssignment(cb) {
         compileLogEventsAssignment(cb) {
           SplitTestHandler.getAssignment(req, res, 'compile-log-events', cb)
           SplitTestHandler.getAssignment(req, res, 'compile-log-events', cb)
         },
         },
+        paywallCtaAssignment(cb) {
+          SplitTestHandler.getAssignment(req, res, 'paywall-cta', cb)
+        },
         projectTags(cb) {
         projectTags(cb) {
           if (!userId) {
           if (!userId) {
             return cb(null, [])
             return cb(null, [])

+ 9 - 0
services/web/app/src/Features/Project/ProjectListController.js

@@ -400,6 +400,15 @@ async function projectListPage(req, res, next) {
     )
     )
   }
   }
 
 
+  try {
+    await SplitTestHandler.promises.getAssignment(req, res, 'paywall-cta')
+  } catch (error) {
+    logger.error(
+      { err: error },
+      'failed to get "paywall-cta" split test assignment'
+    )
+  }
+
   res.render('project/list-react', {
   res.render('project/list-react', {
     title: 'your_projects',
     title: 'your_projects',
     usersBestSubscription,
     usersBestSubscription,

+ 13 - 1
services/web/frontend/extracted-translations.json

@@ -41,6 +41,7 @@
   "add_company_details": "",
   "add_company_details": "",
   "add_email_to_claim_features": "",
   "add_email_to_claim_features": "",
   "add_files": "",
   "add_files": "",
+  "add_more_collaborators": "",
   "add_more_managers": "",
   "add_more_managers": "",
   "add_more_members": "",
   "add_more_members": "",
   "add_new_email": "",
   "add_new_email": "",
@@ -65,6 +66,7 @@
   "all_premium_features_including": "",
   "all_premium_features_including": "",
   "all_projects": "",
   "all_projects": "",
   "all_projects_will_be_transferred_immediately": "",
   "all_projects_will_be_transferred_immediately": "",
+  "already_subscribed_try_refreshing_the_page": "",
   "also": "",
   "also": "",
   "an_email_has_already_been_sent_to": "",
   "an_email_has_already_been_sent_to": "",
   "an_error_occurred_when_verifying_the_coupon_code": "",
   "an_error_occurred_when_verifying_the_coupon_code": "",
@@ -409,7 +411,6 @@
   "find_out_more_about_institution_login": "",
   "find_out_more_about_institution_login": "",
   "find_out_more_about_the_file_outline": "",
   "find_out_more_about_the_file_outline": "",
   "find_out_more_nt": "",
   "find_out_more_nt": "",
-  "find_the_symbols_you_need_with_premium": "",
   "first_name": "",
   "first_name": "",
   "first_x_days_free_after_that_y_per_month": "",
   "first_x_days_free_after_that_y_per_month": "",
   "first_x_days_free_after_that_y_per_year": "",
   "first_x_days_free_after_that_y_per_year": "",
@@ -439,9 +440,17 @@
   "generic_if_problem_continues_contact_us": "",
   "generic_if_problem_continues_contact_us": "",
   "generic_linked_file_compile_error": "",
   "generic_linked_file_compile_error": "",
   "generic_something_went_wrong": "",
   "generic_something_went_wrong": "",
+  "get_advanced_reference_search": "",
   "get_collaborative_benefits": "",
   "get_collaborative_benefits": "",
   "get_discounted_plan": "",
   "get_discounted_plan": "",
+  "get_dropbox_sync": "",
+  "get_full_project_history": "",
+  "get_git_integration": "",
+  "get_github_sync": "",
+  "get_more_compile_time": "",
   "get_most_subscription_by_checking_features": "",
   "get_most_subscription_by_checking_features": "",
+  "get_symbol_palette": "",
+  "get_track_changes": "",
   "git": "",
   "git": "",
   "git_authentication_token": "",
   "git_authentication_token": "",
   "git_authentication_token_create_modal_info_1": "",
   "git_authentication_token_create_modal_info_1": "",
@@ -741,6 +750,7 @@
   "maximum_files_uploaded_together": "",
   "maximum_files_uploaded_together": "",
   "maybe_later": "",
   "maybe_later": "",
   "members_management": "",
   "members_management": "",
+  "mendeley_cta": "",
   "mendeley_groups_loading_error": "",
   "mendeley_groups_loading_error": "",
   "mendeley_groups_relink": "",
   "mendeley_groups_relink": "",
   "mendeley_integration": "",
   "mendeley_integration": "",
@@ -1247,6 +1257,7 @@
   "subject_to_additional_vat": "",
   "subject_to_additional_vat": "",
   "submit_title": "",
   "submit_title": "",
   "subscribe": "",
   "subscribe": "",
+  "subscribe_to_find_the_symbols_you_need_faster": "",
   "subscription_admins_cannot_be_deleted": "",
   "subscription_admins_cannot_be_deleted": "",
   "subscription_canceled": "",
   "subscription_canceled": "",
   "subscription_canceled_and_terminate_on_x": "",
   "subscription_canceled_and_terminate_on_x": "",
@@ -1594,6 +1605,7 @@
   "youve_unlinked_all_users": "",
   "youve_unlinked_all_users": "",
   "zoom_in": "",
   "zoom_in": "",
   "zoom_out": "",
   "zoom_out": "",
+  "zotero_cta": "",
   "zotero_groups_loading_error": "",
   "zotero_groups_loading_error": "",
   "zotero_groups_relink": "",
   "zotero_groups_relink": "",
   "zotero_integration": "",
   "zotero_integration": "",

+ 10 - 2
services/web/frontend/js/features/history/components/change-list/owner-paywall-prompt.tsx

@@ -4,6 +4,7 @@ import { useCallback, useEffect, useState } from 'react'
 import * as eventTracking from '../../../../infrastructure/event-tracking'
 import * as eventTracking from '../../../../infrastructure/event-tracking'
 import StartFreeTrialButton from '../../../../shared/components/start-free-trial-button'
 import StartFreeTrialButton from '../../../../shared/components/start-free-trial-button'
 import { paywallPrompt } from '../../../../main/account-upgrade'
 import { paywallPrompt } from '../../../../main/account-upgrade'
+import { useSplitTestContext } from '@/shared/context/split-test-context'
 
 
 function FeatureItem({ text }: { text: string }) {
 function FeatureItem({ text }: { text: string }) {
   return (
   return (
@@ -17,6 +18,9 @@ export function OwnerPaywallPrompt() {
   const { t } = useTranslation()
   const { t } = useTranslation()
   const [clickedFreeTrialButton, setClickedFreeTrialButton] = useState(false)
   const [clickedFreeTrialButton, setClickedFreeTrialButton] = useState(false)
 
 
+  const { splitTestVariants } = useSplitTestContext()
+  const hasNewPaywallCta = splitTestVariants['paywall-cta'] === 'enabled'
+
   useEffect(() => {
   useEffect(() => {
     eventTracking.send('subscription-funnel', 'editor-click-feature', 'history')
     eventTracking.send('subscription-funnel', 'editor-click-feature', 'history')
     paywallPrompt('history')
     paywallPrompt('history')
@@ -32,7 +36,7 @@ export function OwnerPaywallPrompt() {
       <p>{t('currently_seeing_only_24_hrs_history')}</p>
       <p>{t('currently_seeing_only_24_hrs_history')}</p>
       <p>
       <p>
         <strong>
         <strong>
-          {t('upgrade_to_get_feature', { feature: 'full Project History' })}
+          {t('upgrade_to_get_feature', { feature: 'full project history' })}
         </strong>
         </strong>
       </p>
       </p>
       <ul className="history-feature-list">
       <ul className="history-feature-list">
@@ -50,7 +54,11 @@ export function OwnerPaywallPrompt() {
           source="history"
           source="history"
           buttonProps={{ bsStyle: 'default', className: 'btn-premium' }}
           buttonProps={{ bsStyle: 'default', className: 'btn-premium' }}
           handleClick={handleFreeTrialClick}
           handleClick={handleFreeTrialClick}
-        />
+        >
+          {hasNewPaywallCta
+            ? t('get_full_project_history')
+            : t('start_free_trial')}
+        </StartFreeTrialButton>
       </p>
       </p>
       {clickedFreeTrialButton ? (
       {clickedFreeTrialButton ? (
         <p className="small">{t('refresh_page_after_starting_free_trial')}</p>
         <p className="small">{t('refresh_page_after_starting_free_trial')}</p>

+ 5 - 1
services/web/frontend/js/features/pdf-preview/components/compile-time-warning.tsx

@@ -5,6 +5,7 @@ import * as eventTracking from '../../../infrastructure/event-tracking'
 import StartFreeTrialButton from '../../../shared/components/start-free-trial-button'
 import StartFreeTrialButton from '../../../shared/components/start-free-trial-button'
 import { useDetachCompileContext } from '../../../shared/context/detach-compile-context'
 import { useDetachCompileContext } from '../../../shared/context/detach-compile-context'
 import usePersistedState from '../../../shared/hooks/use-persisted-state'
 import usePersistedState from '../../../shared/hooks/use-persisted-state'
+import { useSplitTestContext } from '@/shared/context/split-test-context'
 
 
 const TWENTY_FOUR_DAYS = 24 * 60 * 60 * 24 * 1000
 const TWENTY_FOUR_DAYS = 24 * 60 * 60 * 24 * 1000
 
 
@@ -24,6 +25,9 @@ function CompileTimeWarning() {
     isProjectOwner,
     isProjectOwner,
   } = useDetachCompileContext()
   } = useDetachCompileContext()
 
 
+  const { splitTestVariants } = useSplitTestContext()
+  const hasNewPaywallCta = splitTestVariants['paywall-cta'] === 'enabled'
+
   useEffect(() => {
   useEffect(() => {
     if (deliveryLatencies && deliveryLatencies.compileTimeServerE2E) {
     if (deliveryLatencies && deliveryLatencies.compileTimeServerE2E) {
       // compile-timeout-20s test
       // compile-timeout-20s test
@@ -101,7 +105,7 @@ function CompileTimeWarning() {
             handleClick={handleUpgradeClick}
             handleClick={handleUpgradeClick}
             source="compile-time-warning"
             source="compile-time-warning"
           >
           >
-            {t('upgrade')}
+            {hasNewPaywallCta ? t('get_more_compile_time') : t('upgrade')}
           </StartFreeTrialButton>
           </StartFreeTrialButton>
         </div>
         </div>
       </div>
       </div>

+ 7 - 1
services/web/frontend/js/features/pdf-preview/components/compile-timeout-changing-soon.tsx

@@ -3,6 +3,7 @@ import StartFreeTrialButton from '@/shared/components/start-free-trial-button'
 import { Trans, useTranslation } from 'react-i18next'
 import { Trans, useTranslation } from 'react-i18next'
 import * as eventTracking from '@/infrastructure/event-tracking'
 import * as eventTracking from '@/infrastructure/event-tracking'
 import { FC } from 'react'
 import { FC } from 'react'
+import { useSplitTestContext } from '@/shared/context/split-test-context'
 
 
 const sendInfoClickEvent = () => {
 const sendInfoClickEvent = () => {
   eventTracking.sendMB('paywall-info-click', {
   eventTracking.sendMB('paywall-info-click', {
@@ -17,6 +18,9 @@ export const CompileTimeoutChangingSoon: FC<{
 }> = ({ isProjectOwner = false, handleDismissChangingSoon }) => {
 }> = ({ isProjectOwner = false, handleDismissChangingSoon }) => {
   const { t } = useTranslation()
   const { t } = useTranslation()
 
 
+  const { splitTestVariants } = useSplitTestContext()
+  const hasNewPaywallCta = splitTestVariants['paywall-cta'] === 'enabled'
+
   const compileTimeoutChangesBlogLink = (
   const compileTimeoutChangesBlogLink = (
     /* eslint-disable-next-line jsx-a11y/anchor-has-content */
     /* eslint-disable-next-line jsx-a11y/anchor-has-content */
     <a
     <a
@@ -51,7 +55,9 @@ export const CompileTimeoutChangingSoon: FC<{
               className: 'btn-secondary-compile-timeout-override',
               className: 'btn-secondary-compile-timeout-override',
             }}
             }}
           >
           >
-            {t('start_free_trial_without_exclamation')}
+            {hasNewPaywallCta
+              ? t('get_more_compile_time')
+              : t('start_free_trial_without_exclamation')}
           </StartFreeTrialButton>
           </StartFreeTrialButton>
         }
         }
         ariaLive="polite"
         ariaLive="polite"

+ 7 - 1
services/web/frontend/js/features/pdf-preview/components/compile-timeout-warning.tsx

@@ -2,6 +2,7 @@ import Notification from '@/shared/components/notification'
 import StartFreeTrialButton from '@/shared/components/start-free-trial-button'
 import StartFreeTrialButton from '@/shared/components/start-free-trial-button'
 import { useTranslation } from 'react-i18next'
 import { useTranslation } from 'react-i18next'
 import { FC } from 'react'
 import { FC } from 'react'
+import { useSplitTestContext } from '@/shared/context/split-test-context'
 
 
 export const CompileTimeoutWarning: FC<{
 export const CompileTimeoutWarning: FC<{
   handleDismissWarning: () => void
   handleDismissWarning: () => void
@@ -9,6 +10,9 @@ export const CompileTimeoutWarning: FC<{
 }> = ({ handleDismissWarning, showNewCompileTimeoutUI }) => {
 }> = ({ handleDismissWarning, showNewCompileTimeoutUI }) => {
   const { t } = useTranslation()
   const { t } = useTranslation()
 
 
+  const { splitTestVariants } = useSplitTestContext()
+  const hasNewPaywallCta = splitTestVariants['paywall-cta'] === 'enabled'
+
   return (
   return (
     <Notification
     <Notification
       action={
       action={
@@ -19,7 +23,9 @@ export const CompileTimeoutWarning: FC<{
             className: 'btn-secondary-compile-timeout-override',
             className: 'btn-secondary-compile-timeout-override',
           }}
           }}
         >
         >
-          {t('start_free_trial_without_exclamation')}
+          {hasNewPaywallCta
+            ? t('get_more_compile_time')
+            : t('start_free_trial_without_exclamation')}
         </StartFreeTrialButton>
         </StartFreeTrialButton>
       }
       }
       ariaLive="polite"
       ariaLive="polite"

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

@@ -6,6 +6,7 @@ import PdfLogEntry from './pdf-log-entry'
 import { useStopOnFirstError } from '../../../shared/hooks/use-stop-on-first-error'
 import { useStopOnFirstError } from '../../../shared/hooks/use-stop-on-first-error'
 import { Button } from 'react-bootstrap'
 import { Button } from 'react-bootstrap'
 import * as eventTracking from '../../../infrastructure/event-tracking'
 import * as eventTracking from '../../../infrastructure/event-tracking'
+import { useSplitTestContext } from '@/shared/context/split-test-context'
 
 
 function TimeoutUpgradePromptNew() {
 function TimeoutUpgradePromptNew() {
   const {
   const {
@@ -58,6 +59,10 @@ const CompileTimeout = memo(function CompileTimeout({
   isProjectOwner,
   isProjectOwner,
 }: CompileTimeoutProps) {
 }: CompileTimeoutProps) {
   const { t } = useTranslation()
   const { t } = useTranslation()
+
+  const { splitTestVariants } = useSplitTestContext()
+  const hasNewPaywallCta = splitTestVariants['paywall-cta'] === 'enabled'
+
   return (
   return (
     <PdfLogEntry
     <PdfLogEntry
       headerTitle={t('your_compile_timed_out')}
       headerTitle={t('your_compile_timed_out')}
@@ -107,7 +112,9 @@ const CompileTimeout = memo(function CompileTimeout({
                   block: true,
                   block: true,
                 }}
                 }}
               >
               >
-                {t('start_a_free_trial')}
+                {hasNewPaywallCta
+                  ? t('get_more_compile_time')
+                  : t('start_a_free_trial')}
               </StartFreeTrialButton>
               </StartFreeTrialButton>
             </p>
             </p>
           )}
           )}

+ 9 - 1
services/web/frontend/js/features/pdf-preview/components/timeout-upgrade-prompt.jsx

@@ -4,12 +4,16 @@ import StartFreeTrialButton from '../../../shared/components/start-free-trial-bu
 import { memo } from 'react'
 import { memo } from 'react'
 import PdfLogEntry from './pdf-log-entry'
 import PdfLogEntry from './pdf-log-entry'
 import UpgradeBenefits from '../../../shared/components/upgrade-benefits'
 import UpgradeBenefits from '../../../shared/components/upgrade-benefits'
+import { useSplitTestContext } from '@/shared/context/split-test-context'
 
 
 function TimeoutUpgradePrompt() {
 function TimeoutUpgradePrompt() {
   const { t } = useTranslation()
   const { t } = useTranslation()
 
 
   const { hasPremiumCompile, isProjectOwner } = useEditorContext()
   const { hasPremiumCompile, isProjectOwner } = useEditorContext()
 
 
+  const { splitTestVariants } = useSplitTestContext()
+  const hasNewPaywallCta = splitTestVariants['paywall-cta'] === 'enabled'
+
   if (!window.ExposedSettings.enableSubscriptions || hasPremiumCompile) {
   if (!window.ExposedSettings.enableSubscriptions || hasPremiumCompile) {
     return null
     return null
   }
   }
@@ -36,7 +40,11 @@ function TimeoutUpgradePrompt() {
                   bsStyle: 'success',
                   bsStyle: 'success',
                   className: 'row-spaced-small',
                   className: 'row-spaced-small',
                 }}
                 }}
-              />
+              >
+                {hasNewPaywallCta
+                  ? t('get_more_compile_time')
+                  : t('start_free_trial')}
+              </StartFreeTrialButton>
             </p>
             </p>
           )}
           )}
         </>
         </>

+ 4 - 1
services/web/frontend/js/features/project-list/components/project-list-root.tsx

@@ -25,6 +25,7 @@ import LoadMore from './load-more'
 import { useEffect } from 'react'
 import { useEffect } from 'react'
 import withErrorBoundary from '../../../infrastructure/error-boundary'
 import withErrorBoundary from '../../../infrastructure/error-boundary'
 import { GenericErrorBoundaryFallback } from '../../../shared/components/generic-error-boundary-fallback'
 import { GenericErrorBoundaryFallback } from '../../../shared/components/generic-error-boundary-fallback'
+import { SplitTestProvider } from '@/shared/context/split-test-context'
 
 
 function ProjectListRoot() {
 function ProjectListRoot() {
   const { isReady } = useWaitForI18n()
   const { isReady } = useWaitForI18n()
@@ -40,7 +41,9 @@ export function ProjectListRootInner() {
   return (
   return (
     <ProjectListProvider>
     <ProjectListProvider>
       <ColorPickerProvider>
       <ColorPickerProvider>
-        <ProjectListPageContent />
+        <SplitTestProvider>
+          <ProjectListPageContent />
+        </SplitTestProvider>
       </ColorPickerProvider>
       </ColorPickerProvider>
     </ProjectListProvider>
     </ProjectListProvider>
   )
   )

+ 6 - 1
services/web/frontend/js/features/share-project-modal/components/add-collaborators-upgrade.tsx

@@ -18,6 +18,7 @@ export default function AddCollaboratorsUpgrade() {
   const { splitTestVariants } = useSplitTestContext()
   const { splitTestVariants } = useSplitTestContext()
 
 
   const variant = splitTestVariants['project-share-modal-paywall']
   const variant = splitTestVariants['project-share-modal-paywall']
+  const hasNewPaywallCta = splitTestVariants['paywall-cta'] === 'enabled'
 
 
   return (
   return (
     <div className={variant === 'default' ? 'add-collaborators-upgrade' : ''}>
     <div className={variant === 'default' ? 'add-collaborators-upgrade' : ''}>
@@ -33,7 +34,11 @@ export default function AddCollaboratorsUpgrade() {
             buttonProps={{ bsStyle: 'success' }}
             buttonProps={{ bsStyle: 'success' }}
             handleClick={() => setStartedFreeTrial(true)}
             handleClick={() => setStartedFreeTrial(true)}
             source="project-sharing"
             source="project-sharing"
-          />
+          >
+            {hasNewPaywallCta
+              ? t('add_more_collaborators')
+              : t('start_free_trial')}
+          </StartFreeTrialButton>
         ) : (
         ) : (
           <Button
           <Button
             bsStyle="primary"
             bsStyle="primary"

+ 10 - 2
services/web/frontend/js/features/source-editor/components/review-panel/upgrade-track-changes-modal.tsx

@@ -6,6 +6,7 @@ import { useProjectContext } from '../../../../shared/context/project-context'
 import { useUserContext } from '../../../../shared/context/user-context'
 import { useUserContext } from '../../../../shared/context/user-context'
 import { startFreeTrial, upgradePlan } from '../../../../main/account-upgrade'
 import { startFreeTrial, upgradePlan } from '../../../../main/account-upgrade'
 import { memo } from 'react'
 import { memo } from 'react'
+import { useSplitTestContext } from '@/shared/context/split-test-context'
 
 
 type UpgradeTrackChangesModalProps = {
 type UpgradeTrackChangesModalProps = {
   show: boolean
   show: boolean
@@ -20,6 +21,9 @@ function UpgradeTrackChangesModal({
   const project = useProjectContext()
   const project = useProjectContext()
   const user = useUserContext()
   const user = useUserContext()
 
 
+  const { splitTestVariants } = useSplitTestContext()
+  const hasNewPaywallCta = splitTestVariants['paywall-cta'] === 'enabled'
+
   return (
   return (
     <AccessibleModal show={show} onHide={() => setShow(false)}>
     <AccessibleModal show={show} onHide={() => setShow(false)}>
       <Modal.Header closeButton>
       <Modal.Header closeButton>
@@ -42,7 +46,6 @@ function UpgradeTrackChangesModal({
         <h4 className="teaser-title">
         <h4 className="teaser-title">
           {t('see_changes_in_your_documents_live')}
           {t('see_changes_in_your_documents_live')}
         </h4>
         </h4>
-        <p className="small">{t('refresh_page_after_starting_free_trial')}</p>
         <Row>
         <Row>
           <Col md={10} mdOffset={1}>
           <Col md={10} mdOffset={1}>
             <ul className="list-unstyled">
             <ul className="list-unstyled">
@@ -58,6 +61,9 @@ function UpgradeTrackChangesModal({
             </ul>
             </ul>
           </Col>
           </Col>
         </Row>
         </Row>
+        <p className="small">
+          {t('already_subscribed_try_refreshing_the_page')}
+        </p>
         {project.owner && (
         {project.owner && (
           <Row className="text-center">
           <Row className="text-center">
             {project.owner._id === user.id ? (
             {project.owner._id === user.id ? (
@@ -67,7 +73,9 @@ function UpgradeTrackChangesModal({
                   className="btn-primary"
                   className="btn-primary"
                   onClick={() => startFreeTrial('track-changes')}
                   onClick={() => startFreeTrial('track-changes')}
                 >
                 >
-                  {t('try_it_for_free')}
+                  {hasNewPaywallCta
+                    ? t('get_track_changes')
+                    : t('try_it_for_free')}
                 </Button>
                 </Button>
               ) : (
               ) : (
                 <Button
                 <Button

+ 4 - 4
services/web/frontend/stylesheets/app/editor.less

@@ -654,24 +654,24 @@ CodeMirror
 .compile-time-warning {
 .compile-time-warning {
   padding: @padding-sm;
   padding: @padding-sm;
   background-color: @ol-green;
   background-color: @ol-green;
-  width: 420px;
+  max-width: 420px;
   box-shadow: 5px 5px 6px rgba(0, 0, 0, 0.3);
   box-shadow: 5px 5px 6px rgba(0, 0, 0, 0.3);
 
 
   .warning-content {
   .warning-content {
     display: flex;
     display: flex;
     align-items: center;
     align-items: center;
     justify-content: space-between;
     justify-content: space-between;
-    margin-right: 32px;
+    flex-wrap: wrap;
+    gap: @alert-padding;
   }
   }
 
 
   .warning-text {
   .warning-text {
-    max-width: 300px;
-    padding-right: @alert-padding;
     display: inline-block;
     display: inline-block;
     font-size: @font-size-small;
     font-size: @font-size-small;
   }
   }
   .upgrade-prompt {
   .upgrade-prompt {
     display: inline-flex;
     display: inline-flex;
+    margin-left: auto;
   }
   }
 
 
   button.btn {
   button.btn {

+ 18 - 2
services/web/locales/en.json

@@ -62,6 +62,7 @@
   "add_email": "Add Email",
   "add_email": "Add Email",
   "add_email_to_claim_features": "Add an institutional email address to claim your features.",
   "add_email_to_claim_features": "Add an institutional email address to claim your features.",
   "add_files": "Add Files",
   "add_files": "Add Files",
+  "add_more_collaborators": "Add more collaborators",
   "add_more_managers": "Add more managers",
   "add_more_managers": "Add more managers",
   "add_more_members": "Add more members",
   "add_more_members": "Add more members",
   "add_new_email": "Add new email",
   "add_new_email": "Add new email",
@@ -97,6 +98,7 @@
   "all_projects_will_be_transferred_immediately": "All projects will be transferred to the new owner immediately.",
   "all_projects_will_be_transferred_immediately": "All projects will be transferred to the new owner immediately.",
   "all_templates": "All Templates",
   "all_templates": "All Templates",
   "already_have_sl_account": "Already have an __appName__ account?",
   "already_have_sl_account": "Already have an __appName__ account?",
+  "already_subscribed_try_refreshing_the_page": "Already subscribed? Try refreshing the page.",
   "also": "Also",
   "also": "Also",
   "also_available_as_on_premises": "Also available as On-Premises",
   "also_available_as_on_premises": "Also available as On-Premises",
   "alternatively_create_new_institution_account": "Alternatively, you can create a <b>new account</b> with your institution email (<b>__email__</b>) by clicking <b>__clickText__</b>.",
   "alternatively_create_new_institution_account": "Alternatively, you can create a <b>new account</b> with your institution email (<b>__email__</b>) by clicking <b>__clickText__</b>.",
@@ -608,7 +610,6 @@
   "find_out_more_about_institution_login": "Find out more about institutional login",
   "find_out_more_about_institution_login": "Find out more about institutional login",
   "find_out_more_about_the_file_outline": "Find out more about the file outline",
   "find_out_more_about_the_file_outline": "Find out more about the file outline",
   "find_out_more_nt": "Find out more.",
   "find_out_more_nt": "Find out more.",
-  "find_the_symbols_you_need_with_premium": "Find the symbols you need faster with Overleaf Premium",
   "first_name": "First Name",
   "first_name": "First Name",
   "first_x_days_free_after_that_y_per_month": "First <0>__trialLen__ days free</0>, after that <0>__price__</0> per month",
   "first_x_days_free_after_that_y_per_month": "First <0>__trialLen__ days free</0>, after that <0>__price__</0> per month",
   "first_x_days_free_after_that_y_per_year": "First <0>__trialLen__ days free</0>, after that <0>__price__</0> per year",
   "first_x_days_free_after_that_y_per_year": "First <0>__trialLen__ days free</0>, after that <0>__price__</0> per year",
@@ -670,12 +671,21 @@
   "generic_if_problem_continues_contact_us": "If the problem continues please contact us",
   "generic_if_problem_continues_contact_us": "If the problem continues please contact us",
   "generic_linked_file_compile_error": "This project’s output files are not available because it failed to compile. Please open the project to see the compilation error details.",
   "generic_linked_file_compile_error": "This project’s output files are not available because it failed to compile. Please open the project to see the compilation error details.",
   "generic_something_went_wrong": "Sorry, something went wrong",
   "generic_something_went_wrong": "Sorry, something went wrong",
+  "get_advanced_reference_search": "Get advanced reference search",
   "get_collaborative_benefits": "Get the collaborative benefits from __appName__, even if you prefer to work offline",
   "get_collaborative_benefits": "Get the collaborative benefits from __appName__, even if you prefer to work offline",
   "get_discounted_plan": "Get discounted plan",
   "get_discounted_plan": "Get discounted plan",
+  "get_dropbox_sync": "Get Dropbox Sync",
+  "get_full_project_history": "Get full project history",
+  "get_git_integration": "Get Git integration",
+  "get_github_sync": "Get GitHub Sync",
   "get_in_touch_having_problems": "<a href=\"__link__\">Get in touch with support</a> if you’re having problems",
   "get_in_touch_having_problems": "<a href=\"__link__\">Get in touch with support</a> if you’re having problems",
   "get_involved": "Get involved",
   "get_involved": "Get involved",
+  "get_more_compile_time": "Get more compile time",
   "get_most_subscription_by_checking_features": "Get the most out of your __appName__ subscription by checking out <0>__appName__’s features</0>.",
   "get_most_subscription_by_checking_features": "Get the most out of your __appName__ subscription by checking out <0>__appName__’s features</0>.",
+  "get_symbol_palette": "Get Symbol Palette",
+  "get_the_best_writing_experience": "Get the best writing experience",
   "get_the_most_out_headline": "Get the most out of __appName__ with features such as:",
   "get_the_most_out_headline": "Get the most out of __appName__ with features such as:",
+  "get_track_changes": "Get track changes",
   "git": "Git",
   "git": "Git",
   "git_authentication_token": "Git authentication token",
   "git_authentication_token": "Git authentication token",
   "git_authentication_token_create_modal_info_1": "This is your Git authentication token. You should enter this when prompted for a password.",
   "git_authentication_token_create_modal_info_1": "This is your Git authentication token. You should enter this when prompted for a password.",
@@ -1107,6 +1117,7 @@
   "maybe_later": "Maybe later",
   "maybe_later": "Maybe later",
   "members_management": "Members management",
   "members_management": "Members management",
   "mendeley": "Mendeley",
   "mendeley": "Mendeley",
+  "mendeley_cta": "Get Mendeley integration",
   "mendeley_groups_loading_error": "There was an error loading groups from Mendeley",
   "mendeley_groups_loading_error": "There was an error loading groups from Mendeley",
   "mendeley_groups_relink": "There was an error accessing your Mendeley data. This was likely caused by lack of permissions. Please re-link your account and try again.",
   "mendeley_groups_relink": "There was an error accessing your Mendeley data. This was likely caused by lack of permissions. Please re-link your account and try again.",
   "mendeley_integration": "Mendeley Integration",
   "mendeley_integration": "Mendeley Integration",
@@ -1185,6 +1196,7 @@
   "no_existing_password": "Please use the password reset form to set your password",
   "no_existing_password": "Please use the password reset form to set your password",
   "no_featured_templates": "No featured templates",
   "no_featured_templates": "No featured templates",
   "no_folder": "No folder",
   "no_folder": "No folder",
+  "no_i_dont_need_these": "No, I don’t need these",
   "no_image_files_found": "No image files found",
   "no_image_files_found": "No image files found",
   "no_members": "No members",
   "no_members": "No members",
   "no_messages": "No messages",
   "no_messages": "No messages",
@@ -1806,6 +1818,7 @@
   "submit": "submit",
   "submit": "submit",
   "submit_title": "Submit",
   "submit_title": "Submit",
   "subscribe": "Subscribe",
   "subscribe": "Subscribe",
+  "subscribe_to_find_the_symbols_you_need_faster": "Subscribe to find the symbols you need faster",
   "subscription": "Subscription",
   "subscription": "Subscription",
   "subscription_admin_panel": "admin panel",
   "subscription_admin_panel": "admin panel",
   "subscription_admins_cannot_be_deleted": "You cannot delete your account while on a subscription. Please cancel your subscription and try again. If you keep seeing this message please contact us.",
   "subscription_admins_cannot_be_deleted": "You cannot delete your account while on a subscription. Please cancel your subscription and try again. If you keep seeing this message please contact us.",
@@ -2071,7 +2084,7 @@
   "upgrade_for_plenty_more_compile_time": "Upgrade to get plenty more compile time.",
   "upgrade_for_plenty_more_compile_time": "Upgrade to get plenty more compile time.",
   "upgrade_now": "Upgrade Now",
   "upgrade_now": "Upgrade Now",
   "upgrade_to_get_feature": "Upgrade to get __feature__, plus:",
   "upgrade_to_get_feature": "Upgrade to get __feature__, plus:",
-  "upgrade_to_track_changes": "Upgrade to Track Changes",
+  "upgrade_to_track_changes": "Upgrade to track changes",
   "upload": "Upload",
   "upload": "Upload",
   "upload_failed": "Upload failed",
   "upload_failed": "Upload failed",
   "upload_from_computer": "Upload from computer",
   "upload_from_computer": "Upload from computer",
@@ -2155,6 +2168,7 @@
   "work_or_university_sso": "Work/university single sign-on",
   "work_or_university_sso": "Work/university single sign-on",
   "work_with_non_overleaf_users": "Work with non Overleaf users",
   "work_with_non_overleaf_users": "Work with non Overleaf users",
   "would_you_like_to_see_a_university_subscription": "Would you like to see a university-wide __appName__ subscription at your university?",
   "would_you_like_to_see_a_university_subscription": "Would you like to see a university-wide __appName__ subscription at your university?",
+  "write_and_collaborate_faster_with_features_like": "Write and collaborate faster with features like:",
   "writefull": "Writefull",
   "writefull": "Writefull",
   "writefull_alternate_login_prompt": "If you have a Writefull account, <0>log in</0> to get started. If you don’t have a Writefull account, we’ll create one for you. By enabling Writefull, you accept Writefull’s <1>terms of service</1> and <2>privacy policy.</2>",
   "writefull_alternate_login_prompt": "If you have a Writefull account, <0>log in</0> to get started. If you don’t have a Writefull account, we’ll create one for you. By enabling Writefull, you accept Writefull’s <1>terms of service</1> and <2>privacy policy.</2>",
   "writefull_disable_prompt_body": "Writefull is enabled. You can switch it on or off in the account settings anytime.",
   "writefull_disable_prompt_body": "Writefull is enabled. You can switch it on or off in the account settings anytime.",
@@ -2175,6 +2189,7 @@
   "x_price_per_user": "__price__ per user",
   "x_price_per_user": "__price__ per user",
   "x_price_per_year": "__price__ per year",
   "x_price_per_year": "__price__ per year",
   "year": "year",
   "year": "year",
+  "yes_im_in": "Yes, I’m in",
   "yes_move_me_to_personal_plan": "Yes, move me to the Personal plan",
   "yes_move_me_to_personal_plan": "Yes, move me to the Personal plan",
   "yes_that_is_correct": "Yes, that’s correct",
   "yes_that_is_correct": "Yes, that’s correct",
   "you": "You",
   "you": "You",
@@ -2255,6 +2270,7 @@
   "zoom_out": "Zoom out",
   "zoom_out": "Zoom out",
   "zotero": "Zotero",
   "zotero": "Zotero",
   "zotero_and_mendeley_integrations": "<0>Zotero</0> and <0>Mendeley</0> integrations",
   "zotero_and_mendeley_integrations": "<0>Zotero</0> and <0>Mendeley</0> integrations",
+  "zotero_cta": "Get Zotero integration",
   "zotero_groups_loading_error": "There was an error loading groups from Zotero",
   "zotero_groups_loading_error": "There was an error loading groups from Zotero",
   "zotero_groups_relink": "There was an error accessing your Zotero data. This was likely caused by lack of permissions. Please re-link your account and try again.",
   "zotero_groups_relink": "There was an error accessing your Zotero data. This was likely caused by lack of permissions. Please re-link your account and try again.",
   "zotero_integration": "Zotero Integration",
   "zotero_integration": "Zotero Integration",