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
This commit is contained in:
M Fahru 2023-10-17 10:13:41 -07:00 committed by Copybot
parent a4d9c900fc
commit e53f3bb158
6 changed files with 119 additions and 0 deletions

View file

@ -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 = { const NotificationsBuilder = {
// Note: notification keys should be url-safe // Note: notification keys should be url-safe
dropboxUnlinkedDueToLapsedReconfirmation, dropboxUnlinkedDueToLapsedReconfirmation,
@ -265,6 +291,7 @@ const NotificationsBuilder = {
ipMatcherAffiliation, ipMatcherAffiliation,
tpdsFileLimit, tpdsFileLimit,
groupInvitation, groupInvitation,
personalAndGroupSubscriptions,
} }
NotificationsBuilder.promises = { NotificationsBuilder.promises = {
@ -286,6 +313,9 @@ NotificationsBuilder.promises = {
projectInvite(invite, project, sendingUser, user) { projectInvite(invite, project, sendingUser, user) {
return promisifyAll(projectInvite(invite, project, sendingUser, user)) return promisifyAll(projectInvite(invite, project, sendingUser, user))
}, },
personalAndGroupSubscriptions(userId) {
return promisifyAll(personalAndGroupSubscriptions(userId))
},
} }
module.exports = NotificationsBuilder module.exports = NotificationsBuilder

View file

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

View file

@ -257,6 +257,19 @@ function CommonNotification({ notification }: CommonNotificationProps) {
</Notification> </Notification>
) : templateKey === 'notification_group_invitation' ? ( ) : templateKey === 'notification_group_invitation' ? (
<GroupInvitationNotification notification={notification} /> <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 bsStyle="info" onDismiss={() => id && handleDismiss(id)}>
<Notification.Body>{html}</Notification.Body> <Notification.Body>{html}</Notification.Body>

View file

@ -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>", "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": "Theres nothing complicated or difficult for you to install, and you can <0>__start_now__</0>, even if youve never seen it before. __appName__ comes with a complete, ready to go LaTeX environment which runs on our servers.", "nothing_to_install_ready_to_go": "Theres nothing complicated or difficult for you to install, and you can <0>__start_now__</0>, even if youve 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 Overleafs Professional features.", "notification_features_upgraded_by_affiliation": "Good news! Your affiliated organization __institutionName__ has an Overleaf subscription, and you now have access to all of Overleafs Professional features.",
"notification_personal_and_group_subscriptions": "Weve spotted that youve 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 Overleafs Professional features through your affiliation. You can cancel your individual subscription without losing access to any features.", "notification_personal_subscription_not_required_due_to_affiliation": " Good news! Your affiliated organization __institutionName__ has an Overleaf subscription, and you now have access to Overleafs 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": "<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": "Youve joined <b>__projectName__</b>", "notification_project_invite_accepted_message": "Youve joined <b>__projectName__</b>",

View file

@ -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)
})

View file

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