Merge pull request #19398 from overleaf/rh-rm-ieee-notification

[web] Remove IEEE Collabratec notification

GitOrigin-RevId: 14ec3e50ed4913b815620f5215df59b17fc03054
This commit is contained in:
roo hutton
2024-07-18 08:04:41 +00:00
committed by Copybot
parent 6c675d947e
commit 523e340a0d
9 changed files with 0 additions and 468 deletions
@@ -281,29 +281,6 @@ function personalAndGroupSubscriptions(userId) {
}
}
/**
* @param {string} [userId]
*/
function ieeeCollabratecRetirement(userId) {
return {
key: 'notification-ieee-collabratec-retirement',
create(callback) {
NotificationsHandler.createNotification(
userId,
this.key,
'notification_ieee_collabratec_retirement',
{},
new Date('2024-08-01'),
false,
callback
)
},
deleteAllUnread(callback) {
NotificationsHandler.markAsReadByKeyOnlyBulk(this.key, callback)
},
}
}
const NotificationsBuilder = {
// Note: notification keys should be url-safe
dropboxUnlinkedDueToLapsedReconfirmation,
@@ -315,7 +292,6 @@ const NotificationsBuilder = {
tpdsFileLimit,
groupInvitation,
personalAndGroupSubscriptions,
ieeeCollabratecRetirement,
}
NotificationsBuilder.promises = {
@@ -343,9 +319,6 @@ NotificationsBuilder.promises = {
personalAndGroupSubscriptions(userId) {
return promisifyAll(personalAndGroupSubscriptions(userId))
},
ieeeCollabratecRetirement(userId) {
return promisifyAll(ieeeCollabratecRetirement(userId))
},
}
module.exports = NotificationsBuilder
@@ -879,7 +879,6 @@
"normally_x_price_per_year": "",
"not_managed": "",
"not_now": "",
"notification_ieee_collabratec_retirement_message": "",
"notification_personal_and_group_subscriptions": "",
"notification_project_invite_accepted_message": "",
"notification_project_invite_message": "",
@@ -10,7 +10,6 @@ import {
Notification as NotificationType,
} from '../../../../../../../types/project/dashboard/notification'
import GroupInvitationNotification from './group-invitation/group-invitation'
import IEEERetirementBanner from '../ieee-retirement-banner'
import { debugConsole } from '@/utils/debugging'
import OLButton from '@/features/ui/components/ol/ol-button'
@@ -279,8 +278,6 @@ function CommonNotification({ notification }: CommonNotificationProps) {
/>
) : templateKey === 'notification_group_invitation' ? (
<GroupInvitationNotification notification={notification} />
) : templateKey === 'notification_ieee_collabratec_retirement' ? (
<IEEERetirementBanner id={id} />
) : templateKey === 'notification_personal_and_group_subscriptions' ? (
<Notification
type="warning"
@@ -1,67 +0,0 @@
import { useCallback, useEffect } from 'react'
import Notification from './notification'
import { Trans } from 'react-i18next'
import * as eventTracking from '../../../../infrastructure/event-tracking'
import useAsyncDismiss from './hooks/useAsyncDismiss'
let viewEventSent = false
type IEEERetirementBannerProps = {
id: number | undefined
}
export default function IEEERetirementBanner({
id,
}: IEEERetirementBannerProps) {
const { handleDismiss } = useAsyncDismiss()
const handleClose = useCallback(() => {
eventTracking.sendMB('promo-dismiss', {
name: 'ieee - retirement',
})
if (id) {
handleDismiss(id)
}
}, [id, handleDismiss])
const handleClickPlans = useCallback(() => {
eventTracking.sendMB('promo-click', {
name: 'ieee - retirement',
content: 'plans',
})
}, [])
const handleClickEmail = useCallback(() => {
eventTracking.sendMB('promo-click', {
name: 'ieee - retirement',
content: 'email',
})
}, [])
useEffect(() => {
if (!viewEventSent) {
eventTracking.sendMB('promo-prompt', {
name: 'ieee - retirement',
})
viewEventSent = true
}
}, [])
return (
<Notification
type="warning"
onDismiss={handleClose}
content={
<Trans
i18nKey="notification_ieee_collabratec_retirement_message"
components={[
// eslint-disable-next-line jsx-a11y/anchor-has-content,react/jsx-key
<a href="/user/subscription" onClick={handleClickPlans} />,
// eslint-disable-next-line jsx-a11y/anchor-has-content,react/jsx-key
<a href="mailto:authors@ieee.org" onClick={handleClickEmail} />,
]}
/>
}
/>
)
}
-1
View File
@@ -1291,7 +1291,6 @@
"not_registered": "Not registered",
"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>",
"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_ieee_collabratec_retirement_message": "As of April 2, 2024, free access to Overleaf Professional subscriptions via Collabratec or IEEE accounts has ended. Need to upgrade? <0>View our plans</0>. Contact <1>authors@ieee.org</1> with any questions.",
"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>",
-1
View File
@@ -1262,7 +1262,6 @@
"not_registered": "未注册",
"note_features_under_development": "<0>请注意</0>此计划中的功能仍在测试和快速开发中。 这意味着它们可能<0>改变</0>、<0>被删除</0>或<0>成为高级计划的一部分</0>",
"notification_features_upgraded_by_affiliation": "好消息!您的组织__institutionName__已有 Overleaf 订阅,并且您现在可以访问 Overleaf 的所有专业功能。",
"notification_ieee_collabratec_retirement_message": "从 2024年 1 月 31日起, IEEE 不再提供 Overleaf premium 功能给协作者。有问题请联系 <0>authors@ieee.org</0>。需要升级吗? <1>查看我们的订阅计划</1>",
"notification_personal_and_group_subscriptions": "我们发现您有<0>多个活跃的 __appName__ 订阅</0>。 为避免支付超出您需要的费用,请<1>检查您的订阅</1>。",
"notification_personal_subscription_not_required_due_to_affiliation": " 好消息!您的组织 __institutionName__ 与 Overleaf 有合作关系。您可以取消您的个人订阅,而不会失去访问您的任何利益。",
"notification_project_invite": "<b>__userName__</b> 想让您加入 <b>__projectName__</b> <a class=\"btn btn-sm btn-info pull-right\" href=\"’/project/__projectId__/invite/token/__token__’\">加入项目</a>",
@@ -1,165 +0,0 @@
const path = require('path')
const fs = require('fs')
const NotificationsBuilder = require('../app/src/Features/Notifications/NotificationsBuilder')
const { waitForDb } = require('../app/src/infrastructure/mongodb')
const { Subscription } = require('../app/src/models/Subscription')
const minimist = require('minimist')
const { db } = require('../app/src/infrastructure/mongodb')
const { promiseMapWithLimit } = require('@overleaf/promise-utils')
/**
* This script is used to notify some users in the IEEEPublications group that
* they will lose access to Overleaf.
*
* Parameters:
* --filename: the filename of the JSON file containing emails of users that
* should **not** be notified.
* --commit: if present, the script will commit the changes to the database.
*
* Usage:
* - dry run:
* node add_notification_ieee_collabratec_users.js --filename=emails.json
* - commit:
* node add_notification_ieee_collabratec_users.js --filename=emails.json --commit
*/
let COMMIT = false
let EMAILS_FILENAME
/**
* The IEEE have provided us with a list of active users that should not be removed
* (and therefore not notified). This method retrives those users.
*/
function getActiveUserEmails(filename) {
const data = fs.readFileSync(path.join(__dirname, filename), 'utf8')
const emailsArray = JSON.parse(data)
const emailsSet = new Set(emailsArray)
console.log(
`Read ${emailsSet.size} (${emailsArray.length} in array) emails from ${filename}`
)
return emailsSet
}
async function getIEEEUsers() {
return await db.subscriptions
.aggregate([
{ $match: { teamName: 'IEEEPublications' } },
{ $unwind: '$member_ids' },
{
$lookup: {
from: 'users',
localField: 'member_ids',
foreignField: '_id',
as: 'member_details',
},
},
{
$project: {
_id: 1,
teamName: 1,
'member_details._id': 1,
'member_details.email': 1,
'member_details.emails.email': 1,
},
},
])
.toArray()
}
async function main() {
const start = performance.now()
if (!EMAILS_FILENAME) {
throw new Error('No email filename provided')
}
await waitForDb()
const subscription = await Subscription.findOne({
teamName: 'IEEEPublications',
})
if (!subscription) {
console.error(`No IEEEPublications group subscription found so quitting`)
return
}
// First we remove all existing Collabratec retirement notifications
if (COMMIT) {
await db.notifications.deleteMany({
key: 'notification-ieee-collabratec-retirement',
})
}
let totalUsers = 0
let totalUsersNotified = 0
const usersArray = await getIEEEUsers()
const activeUsers = getActiveUserEmails(EMAILS_FILENAME)
const activeUsersFound = new Set()
// Then go through each collabratec user to see if we need to notify them
await promiseMapWithLimit(10, usersArray, async member => {
if (totalUsers % 5000 === 0)
console.log(
`notified: ${totalUsersNotified} - progress: ${totalUsers} / ${usersArray.length}`
)
totalUsers = totalUsers + 1
const userDetails = member.member_details[0]
for (const email of userDetails.emails) {
if (activeUsers.has(email.email)) {
activeUsersFound.add(email.email)
return
}
}
if (COMMIT) {
await NotificationsBuilder.promises
.ieeeCollabratecRetirement(userDetails._id.toString())
.create()
}
totalUsersNotified += 1
})
console.log(`Found ${totalUsers} users in IEEEPublications group`)
console.log(
`Found ${totalUsersNotified} users in IEEEPublications group to notify`
)
console.log(`Found ${activeUsersFound.size} active users`)
const activeUsersNotFound = Array.from(activeUsers).filter(
user => !activeUsersFound.has(user)
)
console.log(`${activeUsersNotFound.length} IEEE active users not found:`)
console.log(activeUsersNotFound)
const end = performance.now()
console.log(`Took ${end - start} ms`)
}
const setup = () => {
const argv = minimist(process.argv.slice(2))
COMMIT = argv.commit !== undefined
EMAILS_FILENAME = argv.filename
if (!COMMIT) {
console.warn('Doing dry run. Add --commit to commit changes')
}
}
setup()
main()
.then(() => {
process.exit(0)
})
.catch(err => {
console.error(err)
process.exit(1)
})
@@ -1,196 +0,0 @@
const path = require('path')
const fs = require('fs')
const AnalyticsManager = require('../app/src/Features/Analytics/AnalyticsManager')
const { waitForDb } = require('../app/src/infrastructure/mongodb')
const { Subscription } = require('../app/src/models/Subscription')
const minimist = require('minimist')
const { db } = require('../app/src/infrastructure/mongodb')
const { promiseMapWithLimit } = require('@overleaf/promise-utils')
const _ = require('lodash')
/**
* This script is used to remove some users from the IEEEPublications group.
*
* Parameters:
* --filename: the filename of the JSON file containing emails of users that
* should **not** be removed
* --commit: if present, the script will commit the changes to the database.
*
* Usage:
* - dry run:
* node scripts/remove_unwanted_ieee_collabratec_users.js --filename=emails-to-keep.json
* - commit:
* node scripts/remove_unwanted_ieee_collabratec_users.js --filename=emails-to-keep.json --commit
*/
let COMMIT = false
let EMAILS_FILENAME
/**
* The IEEE have provided us with a list of active users that should not be removed
* This method retrieves those users.
*/
function getActiveUserEmails(filename) {
const data = fs.readFileSync(path.join(__dirname, filename), 'utf8')
const emailsArray = JSON.parse(data)
const emailsSet = new Set(emailsArray)
console.log(
`Read ${emailsSet.size} (${emailsArray.length} in array) emails from ${filename}`
)
return emailsSet
}
async function getIEEEUsers() {
const results = await db.subscriptions
.aggregate([
{ $match: { teamName: 'IEEEPublications' } },
{ $unwind: '$member_ids' },
{
$lookup: {
from: 'users',
localField: 'member_ids',
foreignField: '_id',
as: 'member_details',
},
},
{
$project: {
_id: 1,
teamName: 1,
'member_details._id': 1,
'member_details.email': 1,
'member_details.emails.email': 1,
},
},
])
.toArray()
return results
.map(subscription => subscription.member_details[0])
.filter(Boolean)
}
async function main() {
const start = performance.now()
if (!EMAILS_FILENAME) {
throw new Error('No email filename provided')
}
await waitForDb()
const subscription = await Subscription.findOne({
teamName: 'IEEEPublications',
})
if (!subscription) {
console.error(`No IEEEPublications group subscription found so quitting`)
return
}
/**
* @type {string[]}
*/
const oldMemberIds = subscription.member_ids.map(id => id.toString())
console.log(
`Found ${oldMemberIds.length} members_ids in IEEEPublications group`
)
const usersArray = await getIEEEUsers()
console.log(
`Found ${usersArray.length} users in IEEEPublications group. (${oldMemberIds.length - usersArray.length} missing)`
)
const activeUsers = getActiveUserEmails(EMAILS_FILENAME)
const activeUsersFound = new Set()
/**
* @type {string[]}
*/
const memberIdsToRemove = []
let index = 0
// Then go through each collabratec user to see if we need to remove them
await promiseMapWithLimit(10, usersArray, async userDetails => {
if (index % 1000 === 0)
console.log(
`progress: ${index} / ${usersArray.length} (${memberIdsToRemove.length} to remove)`
)
index = index + 1
if (COMMIT) {
await AnalyticsManager.setUserPropertyForUser(
userDetails._id.toString(),
'ieee-retirement',
true
)
}
for (const email of userDetails.emails) {
if (activeUsers.has(email.email)) {
activeUsersFound.add(email.email)
return
}
}
memberIdsToRemove.push(userDetails._id.toString())
})
console.log(`Found ${memberIdsToRemove.length} users to remove`)
/**
* @type {string[]}
*/
const memberIdsToKeep = _.difference(oldMemberIds, memberIdsToRemove)
console.log(`Keeping ${memberIdsToKeep.length} users`)
if (COMMIT) {
await Subscription.updateOne(
{ teamName: 'IEEEPublications' },
{ member_ids: memberIdsToKeep }
)
}
console.log(`Found ${activeUsersFound.size} active users`)
const activeUsersNotFound = Array.from(activeUsers).filter(
user => !activeUsersFound.has(user)
)
console.log(`${activeUsersNotFound.length} IEEE active users not found:`)
console.log(activeUsersNotFound)
const subscriptionAfter = await Subscription.findOne({
teamName: 'IEEEPublications',
})
console.log(
`There are now ${subscriptionAfter?.member_ids?.length} members_ids in IEEEPublications group`
)
const end = performance.now()
console.log(`Took ${end - start} ms`)
}
const setup = () => {
const argv = minimist(process.argv.slice(2))
COMMIT = argv.commit !== undefined
EMAILS_FILENAME = argv.filename
if (!COMMIT) {
console.warn('Doing dry run. Add --commit to commit changes')
}
}
setup()
main()
.then(() => {
process.exit(0)
})
.catch(err => {
console.error(err)
process.exit(1)
})
@@ -7,7 +7,6 @@ type TemplateKey =
| 'notification_dropbox_unlinked_due_to_lapsed_reconfirmation'
| 'notification_group_invitation'
| 'notification_personal_and_group_subscriptions'
| 'notification_ieee_collabratec_retirement'
type NotificationBase = {
_id?: number
@@ -74,11 +73,6 @@ export interface NotificationGroupInvitation extends NotificationBase {
}
}
export interface NotificationIeeeCollabratecRetirement
extends NotificationBase {
templateKey: Extract<TemplateKey, 'notification_ieee_collabratec_retirement'>
}
export type Notification =
| NotificationProjectInvite
| NotificationWFH2020UpgradeOffer
@@ -87,7 +81,6 @@ export type Notification =
| NotificationDropboxDuplicateProjectNames
| NotificationDropboxUnlinkedDueToLapsedReconfirmation
| NotificationGroupInvitation
| NotificationIeeeCollabratecRetirement
export type Institution = {
_id?: number