Przeglądaj źródła

[web] Remove stale "You already have a subscription" notification (#33187)

* Remove stale "You already have a subscription" notification after cancel/plan change

The notification was derived from a server-rendered meta tag set at page load,
so it persisted through cancel and plan-change flows. Now derived directly from
the URL param on the client; the param is stripped on cancel button click
(replaceState) and before plan-change reloads (location.replace via
reloadWithoutHasSubscription helper).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix format

* Update services/web/test/frontend/features/subscription/components/dashboard/subscription-dashboard.test.tsx

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Fix change-plan tests after location.reload → location.replace migration

reloadWithoutHasSubscription calls location.replace() not location.reload(),
so update assertions accordingly. Also stub toString() to return the jsdom
origin so FlashMessage's replaceState call doesn't throw a SecurityError.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Guard reloadWithoutHasSubscription against empty URL

When called after component unmount, useLocation's toString() returns '',
causing new URL('') to throw. No-op early to avoid the exception.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Guard against empty URL in history state replacement for subscription cancellation

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
GitOrigin-RevId: 8408ee971adf038e2d819eae5df060ace62a7e14
Antoine Clausse 3 miesięcy temu
rodzic
commit
dd44f4e2e8

+ 0 - 2
services/web/app/src/Features/Subscription/SubscriptionController.mjs

@@ -217,7 +217,6 @@ async function userSubscriptionPage(req, res) {
   const userCanExtendTrial = (
     await Modules.promises.hooks.fire('userCanExtendTrial', user)
   )?.[0]
-  const fromPlansPage = req.query.hasSubscription
   const redirectedPaymentErrorCode = req.query.errorCode
   const isInTrial = SubscriptionHelper.isInTrial(
     personalSubscription?.payment?.trialEndsAt
@@ -321,7 +320,6 @@ async function userSubscriptionPage(req, res) {
     planCodesChangingAtTermEnd: plansData?.planCodesChangingAtTermEnd,
     user,
     hasSubscription,
-    fromPlansPage,
     redirectedPaymentErrorCode,
     personalSubscription,
     userCanExtendTrial,

+ 0 - 1
services/web/app/views/subscriptions/dashboard-react.pug

@@ -47,7 +47,6 @@ block append meta
 		content=currentInstitutionsWithLicence
 	)
 	meta(name='ol-hasSubscription' data-type='boolean' content=hasSubscription)
-	meta(name='ol-fromPlansPage' data-type='boolean' content=fromPlansPage)
 	meta(
 		name='ol-subscriptionPaymentErrorCode'
 		data-type='string'

+ 7 - 0
services/web/frontend/js/features/subscription/components/dashboard/states/active/cancel-subscription-button.tsx

@@ -4,9 +4,12 @@ import { useSubscriptionDashboardContext } from '../../../../context/subscriptio
 import OLButton from '@/shared/components/ol/ol-button'
 import { PaidSubscription } from '../../../../../../../../types/subscription/dashboard/subscription'
 import { useFeatureFlag } from '@/shared/context/split-test-context'
+import { useLocation } from '@/shared/hooks/use-location'
+import { stripHasSubscription } from '../../../../data/subscription-url'
 
 export function CancelSubscriptionButton() {
   const { t } = useTranslation()
+  const location = useLocation()
   const {
     recurlyLoadError,
     personalSubscription,
@@ -35,6 +38,10 @@ export function CancelSubscriptionButton() {
       plan_code: subscription?.planCode,
       is_trial: isInTrial,
     })
+    const url = location.toString()
+    if (url) {
+      window.history.replaceState(null, '', stripHasSubscription(url))
+    }
     if (enablePause) {
       setModalIdShown('pause-subscription')
     } else {

+ 6 - 3
services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/modals/change-to-group-modal.tsx

@@ -9,7 +9,10 @@ import {
 import getMeta from '../../../../../../../../utils/meta'
 import { useSubscriptionDashboardContext } from '../../../../../../context/subscription-dashboard-context'
 import GenericErrorAlert from '../../../../generic-error-alert'
-import { subscriptionUpdateUrl } from '../../../../../../data/subscription-url'
+import {
+  subscriptionUpdateUrl,
+  reloadWithoutHasSubscription,
+} from '../../../../../../data/subscription-url'
 import { getRecurlyGroupPlanCode } from '../../../../../../util/recurly-group-plan-code'
 import { useLocation } from '../../../../../../../../shared/hooks/use-location'
 import {
@@ -139,11 +142,11 @@ export function ChangeToGroupModal() {
           ),
         },
       })
-      location.reload()
+      reloadWithoutHasSubscription(location)
     } catch (e) {
       const { handled } = await handleStripePaymentAction(e as FetchError)
       if (handled) {
-        location.reload()
+        reloadWithoutHasSubscription(location)
         return
       }
       setError(true)

+ 6 - 3
services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/modals/confirm-change-plan-modal.tsx

@@ -7,7 +7,10 @@ import {
 } from '../../../../../../../../infrastructure/fetch-json'
 import getMeta from '../../../../../../../../utils/meta'
 import { useSubscriptionDashboardContext } from '../../../../../../context/subscription-dashboard-context'
-import { subscriptionUpdateUrl } from '../../../../../../data/subscription-url'
+import {
+  subscriptionUpdateUrl,
+  reloadWithoutHasSubscription,
+} from '../../../../../../data/subscription-url'
 import { useLocation } from '../../../../../../../../shared/hooks/use-location'
 import {
   OLModal,
@@ -40,12 +43,12 @@ export function ConfirmChangePlanModal() {
           plan_code: planCodeToChangeTo,
         },
       })
-      location.reload()
+      reloadWithoutHasSubscription(location)
     } catch (e) {
       const fetchError = e as FetchError
       const { handled } = await handleStripePaymentAction(fetchError)
       if (handled) {
-        location.reload()
+        reloadWithoutHasSubscription(location)
       } else {
         setError(fetchError)
         setInflight(false)

+ 5 - 2
services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/modals/keep-current-plan-modal.tsx

@@ -3,7 +3,10 @@ import { useTranslation, Trans } from 'react-i18next'
 import { SubscriptionDashModalIds } from '../../../../../../../../../../types/subscription/dashboard/modal-ids'
 import { postJSON } from '../../../../../../../../infrastructure/fetch-json'
 import { useSubscriptionDashboardContext } from '../../../../../../context/subscription-dashboard-context'
-import { cancelPendingSubscriptionChangeUrl } from '../../../../../../data/subscription-url'
+import {
+  cancelPendingSubscriptionChangeUrl,
+  reloadWithoutHasSubscription,
+} from '../../../../../../data/subscription-url'
 import { useLocation } from '../../../../../../../../shared/hooks/use-location'
 import {
   OLModal,
@@ -30,7 +33,7 @@ export function KeepCurrentPlanModal() {
 
     try {
       await postJSON(cancelPendingSubscriptionChangeUrl)
-      location.reload()
+      reloadWithoutHasSubscription(location)
     } catch (e) {
       setError(true)
       setInflight(false)

+ 4 - 1
services/web/frontend/js/features/subscription/components/dashboard/subscription-dashboard.tsx

@@ -27,10 +27,13 @@ function SubscriptionDashboard() {
     personalSubscription,
   } = useSubscriptionDashboardContext()
 
+  const fromPlansPage = new URLSearchParams(window.location.search).has(
+    'hasSubscription'
+  )
+
   const subscription = personalSubscription as PaidSubscription
 
   const hasAiAssistViaWritefull = getMeta('ol-hasAiAssistViaWritefull')
-  const fromPlansPage = getMeta('ol-fromPlansPage')
   const hasRedirectedPaymentError = Boolean(
     getMeta('ol-subscriptionPaymentErrorCode')
   )

+ 15 - 0
services/web/frontend/js/features/subscription/data/subscription-url.ts

@@ -6,3 +6,18 @@ export const redirectAfterCancelSubscriptionUrl = '/user/subscription/canceled'
 export const extendTrialUrl = '/user/subscription/extend'
 export const reactivateSubscriptionUrl = '/user/subscription/reactivate'
 export const billingPortalUrl = '/user/subscription/payment/account-management'
+
+export function stripHasSubscription(url: string): string {
+  const parsed = new URL(url)
+  parsed.searchParams.delete('hasSubscription')
+  return parsed.toString()
+}
+
+export function reloadWithoutHasSubscription(location: {
+  toString(): string
+  replace(url: string): void
+}) {
+  const url = location.toString()
+  if (!url) return
+  location.replace(stripHasSubscription(url))
+}

+ 0 - 1
services/web/frontend/js/utils/meta.ts

@@ -131,7 +131,6 @@ export interface Meta {
   'ol-featureUsage': FeatureUsage
   'ol-features': Features
   'ol-footer': FooterMetadata
-  'ol-fromPlansPage': boolean
   'ol-galleryTagName': string
   'ol-gitBridgeEnabled': boolean
   'ol-gitBridgePublicBaseUrl': string

+ 7 - 6
services/web/test/frontend/features/subscription/components/dashboard/states/active/change-plan/change-plan.test.tsx

@@ -27,6 +27,7 @@ describe('<ChangePlanModal />', function () {
   beforeEach(function () {
     this.locationWrapperSandbox = sinon.createSandbox()
     this.locationWrapperStub = this.locationWrapperSandbox.stub(location)
+    this.locationWrapperStub.toString.returns('https://www.test-overleaf.com/')
   })
 
   afterEach(function () {
@@ -214,9 +215,9 @@ describe('<ChangePlanModal />', function () {
       screen.getByRole('button', { name: 'Processing…' })
 
       // page is reloaded on success
-      const reloadStub = this.locationWrapperStub.reload
+      const replaceStub = this.locationWrapperStub.replace
       await waitFor(() => {
-        expect(reloadStub).to.have.been.called
+        expect(replaceStub).to.have.been.called
       })
     })
 
@@ -304,9 +305,9 @@ describe('<ChangePlanModal />', function () {
       screen.getByRole('button', { name: 'Processing…' })
 
       // page is reloaded on success
-      const reloadStub = this.locationWrapperStub.reload
+      const replaceStub = this.locationWrapperStub.replace
       await waitFor(() => {
-        expect(reloadStub).to.have.been.called
+        expect(replaceStub).to.have.been.called
       })
     })
 
@@ -536,9 +537,9 @@ describe('<ChangePlanModal />', function () {
       screen.getByRole('button', { name: 'Processing…' })
 
       // page is reloaded on success
-      const reloadStub = this.locationWrapperStub.reload
+      const replaceStub = this.locationWrapperStub.replace
       await waitFor(() => {
-        expect(reloadStub).to.have.been.called
+        expect(replaceStub).to.have.been.called
       })
     })
 

+ 8 - 9
services/web/test/frontend/features/subscription/components/dashboard/subscription-dashboard.test.tsx

@@ -84,15 +84,14 @@ describe('<SubscriptionDashboard />', function () {
   })
 
   it('Show a warning when coming from plans page', function () {
-    renderWithSubscriptionDashContext(<SubscriptionDashboard />, {
-      metaTags: [
-        {
-          name: 'ol-fromPlansPage',
-          value: true,
-        },
-      ],
-    })
+    window.history.pushState({}, '', '?hasSubscription=true')
+
+    try {
+      renderWithSubscriptionDashContext(<SubscriptionDashboard />)
 
-    screen.getByText('You already have a subscription')
+      screen.getByText('You already have a subscription')
+    } finally {
+      window.history.replaceState({}, '', window.location.pathname)
+    }
   })
 })