Переглянути джерело

Merge pull request #15044 from overleaf/mf-send-warning-to-users-with-personal-and-group-subscriptions

Show notification warning to user with both personal and group subscriptions

GitOrigin-RevId: 7f46d7af10389f552175ce26fae9469e0167f95b
M Fahru 2 роки тому
батько
коміт
e53f3bb158

+ 30 - 0
services/web/app/src/Features/Notifications/NotificationsBuilder.js

@@ -255,6 +255,32 @@ function groupInvitation(userId, subscriptionId, managedUsersEnabled) {
   }
 }
 
+function personalAndGroupSubscriptions(userId) {
+  return {
+    key: 'personal-and-group-subscriptions',
+    create(callback) {
+      if (callback == null) {
+        callback = function () {}
+      }
+      NotificationsHandler.createNotification(
+        userId,
+        this.key,
+        'notification_personal_and_group_subscriptions',
+        {},
+        null,
+        false,
+        callback
+      )
+    },
+    read(callback) {
+      if (callback == null) {
+        callback = function () {}
+      }
+      NotificationsHandler.markAsReadByKeyOnly(this.key, callback)
+    },
+  }
+}
+
 const NotificationsBuilder = {
   // Note: notification keys should be url-safe
   dropboxUnlinkedDueToLapsedReconfirmation,
@@ -265,6 +291,7 @@ const NotificationsBuilder = {
   ipMatcherAffiliation,
   tpdsFileLimit,
   groupInvitation,
+  personalAndGroupSubscriptions,
 }
 
 NotificationsBuilder.promises = {
@@ -286,6 +313,9 @@ NotificationsBuilder.promises = {
   projectInvite(invite, project, sendingUser, user) {
     return promisifyAll(projectInvite(invite, project, sendingUser, user))
   },
+  personalAndGroupSubscriptions(userId) {
+    return promisifyAll(personalAndGroupSubscriptions(userId))
+  },
 }
 
 module.exports = NotificationsBuilder

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

@@ -738,6 +738,7 @@
   "normally_x_price_per_year": "",
   "not_managed": "",
   "not_now": "",
+  "notification_personal_and_group_subscriptions": "",
   "notification_project_invite_accepted_message": "",
   "notification_project_invite_message": "",
   "number_of_users": "",

+ 13 - 0
services/web/frontend/js/features/project-list/components/notifications/groups/common.tsx

@@ -257,6 +257,19 @@ function CommonNotification({ notification }: CommonNotificationProps) {
         </Notification>
       ) : templateKey === 'notification_group_invitation' ? (
         <GroupInvitationNotification notification={notification} />
+      ) : templateKey === 'notification_personal_and_group_subscriptions' ? (
+        <Notification
+          bsStyle="warning"
+          onDismiss={() => id && handleDismiss(id)}
+        >
+          <Notification.Body>
+            <Trans
+              i18nKey="notification_personal_and_group_subscriptions"
+              /* eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key */
+              components={[<strong />, <a href="/user/subscription" />]}
+            />
+          </Notification.Body>
+        </Notification>
       ) : (
         <Notification bsStyle="info" onDismiss={() => id && handleDismiss(id)}>
           <Notification.Body>{html}</Notification.Body>

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

@@ -1161,6 +1161,7 @@
   "note_features_under_development": "<0>Please note</0> that features in this program are still being tested and actively developed. This means that they might <0>change</0>, be <0>removed</0> or <0>become part of a premium plan</0>",
   "nothing_to_install_ready_to_go": "There’s nothing complicated or difficult for you to install, and you can <0>__start_now__</0>, even if you’ve never seen it before. __appName__ comes with a complete, ready to go LaTeX environment which runs on our servers.",
   "notification_features_upgraded_by_affiliation": "Good news! Your affiliated organization __institutionName__ has an Overleaf subscription, and you now have access to all of Overleaf’s Professional features.",
+  "notification_personal_and_group_subscriptions": "We’ve spotted that you’ve got <0>more than one active __appName__ subscription</0>. To avoid paying more than you need to, <1>review your subscriptions</1>.",
   "notification_personal_subscription_not_required_due_to_affiliation": " Good news! Your affiliated organization __institutionName__ has an Overleaf subscription, and you now have access to Overleaf’s Professional features through your affiliation. You can cancel your individual subscription without losing access to any features.",
   "notification_project_invite": "<b>__userName__</b> would like you to join <b>__projectName__</b> <a class=\"btn btn-sm btn-info pull-right\" href=\"/project/__projectId__/invite/token/__token__\">Join Project</a>",
   "notification_project_invite_accepted_message": "You’ve joined <b>__projectName__</b>",

+ 73 - 0
services/web/scripts/back_fill_warning_user_personal_and_group_subscription.js

@@ -0,0 +1,73 @@
+const NotificationsBuilder = require('../app/src/Features/Notifications/NotificationsBuilder')
+const { db, waitForDb } = require('../app/src/infrastructure/mongodb')
+const { batchedUpdate } = require('./helpers/batchedUpdate')
+
+const DRY_RUN = !process.argv.includes('--dry-run=false')
+
+if (DRY_RUN) {
+  console.log('Doing dry run')
+}
+
+async function processBatch(groupSubscriptionsBatch) {
+  console.log('\n')
+  console.log('----- Batch computation started -----')
+  const flattenedMemberIds = groupSubscriptionsBatch
+    .map(sub => sub.member_ids)
+    .flatMap(memberId => memberId)
+  const uniqueFlattenedMemberIds = [...new Set(flattenedMemberIds)]
+
+  const userWithIndividualAndGroupSubscriptions = await db.subscriptions
+    .find({
+      groupPlan: false,
+      'recurlyStatus.state': 'active',
+      admin_id: { $in: uniqueFlattenedMemberIds },
+    })
+    .toArray()
+
+  console.log(
+    `Found ${userWithIndividualAndGroupSubscriptions.length} affected users in this batch`
+  )
+
+  if (DRY_RUN) {
+    console.error('---')
+    console.error('Dry-run enabled, use --dry-run=false to commit changes')
+    console.error('---')
+  } else {
+    if (userWithIndividualAndGroupSubscriptions.length > 0) {
+      console.log(
+        `Notifying ${userWithIndividualAndGroupSubscriptions.length} users`
+      )
+
+      for (const notif of userWithIndividualAndGroupSubscriptions) {
+        await NotificationsBuilder.promises
+          .personalAndGroupSubscriptions(notif.admin_id.toString())
+          .create()
+      }
+
+      console.log(
+        `${userWithIndividualAndGroupSubscriptions.length} users successfully notified in this batch`
+      )
+    } else {
+      console.log(
+        'No users currently subscribe to both individual and group subscription in this batch'
+      )
+    }
+  }
+}
+
+async function main() {
+  await waitForDb()
+
+  await batchedUpdate('subscriptions', { groupPlan: true }, processBatch, {
+    member_ids: 1,
+  })
+}
+
+main()
+  .then(() => {
+    process.exit(0)
+  })
+  .catch(err => {
+    console.error(err)
+    process.exit(1)
+  })

+ 1 - 0
services/web/types/project/dashboard/notification.ts

@@ -6,6 +6,7 @@ type TemplateKey =
   | 'notification_dropbox_duplicate_project_names'
   | 'notification_dropbox_unlinked_due_to_lapsed_reconfirmation'
   | 'notification_group_invitation'
+  | 'notification_personal_and_group_subscriptions'
 
 type NotificationBase = {
   _id?: number