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

Merge pull request #25505 from overleaf/jdt-redirect-to-wf-based-on-prem-src

Manage on Writefull should provide the right instructions based on premiumSource

GitOrigin-RevId: bc6dcc5962d18220c445315acbb3b4040ff23d5d
Jimmy Domagala-Tang 1 год назад
Родитель
Сommit
d6cd041704

+ 0 - 10
services/web/app/src/Features/Subscription/FeaturesUpdater.js

@@ -197,14 +197,6 @@ async function doSyncFromV1(v1UserId) {
   return refreshFeatures(user._id, 'sync-v1')
 }
 
-async function hasFeaturesViaWritefull(userId) {
-  const user = await UserGetter.promises.getUser(userId, {
-    _id: 1,
-    writefull: 1,
-  })
-  return Boolean(user?.writefull?.isPremium)
-}
-
 module.exports = {
   featuresEpochIsCurrent,
   computeFeatures: callbackify(computeFeatures),
@@ -217,12 +209,10 @@ module.exports = {
     'featuresChanged',
   ]),
   scheduleRefreshFeatures: callbackify(scheduleRefreshFeatures),
-  hasFeaturesViaWritefull: callbackify(hasFeaturesViaWritefull),
   promises: {
     computeFeatures,
     refreshFeatures,
     scheduleRefreshFeatures,
     doSyncFromV1,
-    hasFeaturesViaWritefull,
   },
 }

+ 8 - 5
services/web/app/src/Features/Subscription/SubscriptionController.js

@@ -26,6 +26,7 @@ const { AI_ADD_ON_CODE } = require('./PaymentProviderEntities')
 const PlansLocator = require('./PlansLocator')
 const PaymentProviderEntities = require('./PaymentProviderEntities')
 const { User } = require('../../models/User')
+const UserGetter = require('../User/UserGetter')
 
 /**
  * @import { SubscriptionChangeDescription } from '../../../../types/subscription/subscription-change-preview'
@@ -154,9 +155,10 @@ async function userSubscriptionPage(req, res) {
       'Failed to list groups with group settings enabled for advertising'
     )
   }
-
-  const hasAiAssistViaWritefull =
-    await FeaturesUpdater.promises.hasFeaturesViaWritefull(user._id)
+  const {
+    isPremium: hasAiAssistViaWritefull,
+    premiumSource: aiAssistViaWritefullSource,
+  } = await UserGetter.promises.getWritefullData(user._id)
 
   const data = {
     title: 'your_subscription',
@@ -181,6 +183,7 @@ async function userSubscriptionPage(req, res) {
     isManagedAccount: !!req.managedBy,
     userRestrictions: Array.from(req.userRestrictions || []),
     hasAiAssistViaWritefull,
+    aiAssistViaWritefullSource,
   }
   res.render('subscriptions/dashboard-react', data)
 }
@@ -346,8 +349,8 @@ async function previewAddonPurchase(req, res) {
     subscriptionChange =
       await SubscriptionHandler.promises.previewAddonPurchase(userId, addOnCode)
 
-    const hasAiAssistViaWritefull =
-      await FeaturesUpdater.promises.hasFeaturesViaWritefull(userId)
+    const { isPremium: hasAiAssistViaWritefull } =
+      await UserGetter.promises.getWritefullData(userId)
     const isAiUpgrade =
       PaymentProviderEntities.subscriptionChangeIsAiAssistUpgrade(
         subscriptionChange

+ 21 - 1
services/web/app/src/Features/User/UserGetter.js

@@ -137,6 +137,19 @@ async function getSsoUsersAtInstitution(institutionId, projection) {
   ).exec()
 }
 
+async function getWritefullData(userId) {
+  const user = await UserGetter.promises.getUser(userId, {
+    writefull: 1,
+  })
+  if (!user) {
+    throw new Error('user not found')
+  }
+  return {
+    isPremium: Boolean(user?.writefull?.isPremium),
+    premiumSource: user.writefull.premiumSource || null,
+  }
+}
+
 const UserGetter = {
   getSsoUsersAtInstitution: callbackify(getSsoUsersAtInstitution),
 
@@ -271,6 +284,7 @@ const UserGetter = {
       callback(error)
     })
   },
+  getWritefullData: callbackify(getWritefullData),
 }
 
 const decorateFullEmails = (
@@ -346,10 +360,16 @@ const decorateFullEmails = (
 }
 
 UserGetter.promises = promisifyAll(UserGetter, {
-  without: ['getSsoUsersAtInstitution', 'getUserFullEmails', 'getUserFeatures'],
+  without: [
+    'getSsoUsersAtInstitution',
+    'getUserFullEmails',
+    'getUserFeatures',
+    'getWritefullData',
+  ],
 })
 UserGetter.promises.getUserFullEmails = getUserFullEmails
 UserGetter.promises.getSsoUsersAtInstitution = getSsoUsersAtInstitution
 UserGetter.promises.getUserFeatures = getUserFeatures
+UserGetter.promises.getWritefullData = getWritefullData
 
 module.exports = UserGetter

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

@@ -23,6 +23,7 @@ block append meta
   meta(name="ol-showGroupDiscount" data-type="boolean", content=showGroupDiscount)
   meta(name="ol-groupSettingsEnabledFor" data-type="json" content=groupSettingsEnabledFor)
   meta(name="ol-hasAiAssistViaWritefull" data-type="boolean", content=hasAiAssistViaWritefull)
+  meta(name="ol-aiAssistViaWritefullSource" data-type="string", content=aiAssistViaWritefullSource)
   meta(name="ol-user" data-type="json" content=user)
   if (personalSubscription && personalSubscription.payment)
     meta(name="ol-recurlyApiKey" content=settings.apis.recurly.publicKey)

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

@@ -104,7 +104,8 @@
   "aggregate_to": "",
   "agree": "",
   "agree_with_the_terms": "",
-  "ai_assist_in_overleaf_is_included_via_writefull": "",
+  "ai_assist_in_overleaf_is_included_via_writefull_groups": "",
+  "ai_assist_in_overleaf_is_included_via_writefull_individual": "",
   "ai_assistance_to_help_you": "",
   "ai_based_language_tools": "",
   "ai_can_make_mistakes": "",

+ 20 - 10
services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/modals/writefull-bundle-management-modal.tsx

@@ -13,14 +13,18 @@ import { Dropdown, DropdownMenu, DropdownToggle } from 'react-bootstrap'
 import OLDropdownMenuItem from '@/features/ui/components/ol/ol-dropdown-menu-item'
 import MaterialIcon from '@/shared/components/material-icon'
 import { ADD_ON_NAME } from '@/features/subscription/data/add-on-codes'
+import getMeta from '@/utils/meta'
 
 function WritefullBundleManagementModal() {
   const modalId: SubscriptionDashModalIds = 'manage-on-writefull'
   const { t } = useTranslation()
   const { handleCloseModal, modalIdShown } = useSubscriptionDashboardContext()
+  const aiAssistViaWritefullSource = getMeta('ol-aiAssistViaWritefullSource')
 
   if (modalIdShown !== modalId) return null
 
+  const individualWFSubscription = aiAssistViaWritefullSource === 'individual'
+
   return (
     <OLModal
       id={modalId}
@@ -34,22 +38,28 @@ function WritefullBundleManagementModal() {
       </OLModalHeader>
 
       <OLModalBody>
-        <p>{t('ai_assist_in_overleaf_is_included_via_writefull')}</p>
+        <p>
+          {individualWFSubscription
+            ? t('ai_assist_in_overleaf_is_included_via_writefull_individual')
+            : t('ai_assist_in_overleaf_is_included_via_writefull_groups')}
+        </p>
       </OLModalBody>
 
       <OLModalFooter>
         <OLButton variant="secondary" onClick={handleCloseModal}>
           {t('back')}
         </OLButton>
-        <OLButton
-          variant="primary"
-          onClick={handleCloseModal}
-          href="https://my.writefull.com/account"
-          target="_blank"
-          rel="noreferrer"
-        >
-          {t('go_to_writefull')}
-        </OLButton>
+        {individualWFSubscription && (
+          <OLButton
+            variant="primary"
+            onClick={handleCloseModal}
+            href="https://my.writefull.com/account"
+            target="_blank"
+            rel="noreferrer"
+          >
+            {t('go_to_writefull')}
+          </OLButton>
+        )}
       </OLModalFooter>
     </OLModal>
   )

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

@@ -59,6 +59,7 @@ export interface Meta {
     string,
     { annual: string; monthly: string; annualDividedByTwelve: string }
   >
+  'ol-aiAssistViaWritefullSource': string
   'ol-allInReconfirmNotificationPeriods': UserEmailData[]
   'ol-allowedExperiments': string[]
   'ol-allowedImageNames': AllowedImageName[]
@@ -81,6 +82,7 @@ export interface Meta {
   'ol-cannot-reactivate-subscription': boolean
   'ol-cannot-use-ai': boolean
   'ol-chatEnabled': boolean
+
   'ol-compilesUserContentDomain': string
   'ol-countryCode': PricingFormState['country']
   'ol-couponCode': PricingFormState['coupon']

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

@@ -125,7 +125,8 @@
   "aggregate_to": "to",
   "agree": "Agree",
   "agree_with_the_terms": "I agree with the Overleaf terms",
-  "ai_assist_in_overleaf_is_included_via_writefull": "AI Assist in Overleaf is included as part of your Writefull subscription. You can cancel or manage your access to AI Assist in your Writefull subscription settings.",
+  "ai_assist_in_overleaf_is_included_via_writefull_groups": "AI Assist in Overleaf is included as part of your group or organization’s Writefull subscription. To make changes you’ll need to speak to your subscription admin",
+  "ai_assist_in_overleaf_is_included_via_writefull_individual": "AI Assist in Overleaf is included as part of your Writefull subscription. You can cancel or manage your access to AI Assist in your Writefull subscription settings.",
   "ai_assistance_to_help_you": "AI assistance to help you fix LaTeX errors",
   "ai_based_language_tools": "AI-based language tools tailored to research writing",
   "ai_can_make_mistakes": "AI can make mistakes. Review fixes before you apply them.",

+ 3 - 1
services/web/test/unit/src/Subscription/SubscriptionControllerTests.js

@@ -127,6 +127,9 @@ describe('SubscriptionController', function () {
       getUser: sinon.stub().callsArgWith(2, null, this.user),
       promises: {
         getUser: sinon.stub().resolves(this.user),
+        getWritefullData: sinon
+          .stub()
+          .resolves({ isPremium: false, premiumSource: null }),
       },
     }
     this.SplitTestV2Hander = {
@@ -157,7 +160,6 @@ describe('SubscriptionController', function () {
         },
         './FeaturesUpdater': (this.FeaturesUpdater = {
           promises: {
-            hasFeaturesViaWritefull: sinon.stub().resolves(false),
             refreshFeatures: sinon.stub().resolves({ features: {} }),
           },
         }),