diff --git a/src/adapters/notifications/MockNotificationAdapter.ts b/src/adapters/notifications/MockNotificationAdapter.ts index e47a3825c..4aaab77c4 100644 --- a/src/adapters/notifications/MockNotificationAdapter.ts +++ b/src/adapters/notifications/MockNotificationAdapter.ts @@ -2,6 +2,7 @@ import { BroadCastNotificationInputParams, NotificationAdapterInterface, OrttoPerson, + ProjectOwnershipChangedParams, ProjectsHaveNewRankingInputParam, } from './NotificationAdapterInterface'; import { Donation } from '../../entities/donation'; @@ -267,4 +268,13 @@ export class MockNotificationAdapter implements NotificationAdapterInterface { }); return Promise.resolve(undefined); } + + notifyProjectOwnershipChange( + params: ProjectOwnershipChangedParams, + ): Promise { + logger.debug('MockNotificationAdapter notifyProjectOwnershipChange', { + params, + }); + return Promise.resolve(undefined); + } } diff --git a/src/adapters/notifications/NotificationAdapterInterface.ts b/src/adapters/notifications/NotificationAdapterInterface.ts index 7e19aaacb..cd57cd081 100644 --- a/src/adapters/notifications/NotificationAdapterInterface.ts +++ b/src/adapters/notifications/NotificationAdapterInterface.ts @@ -19,6 +19,12 @@ export interface ProjectsHaveNewRankingInputParam { newBottomRank: number; } +export interface ProjectOwnershipChangedParams { + previousOwnerUser: User | null; + newOwnerUser: User | null; + projectName: string; +} + export interface OrttoPerson { fields: { 'str::first': string; @@ -114,4 +120,7 @@ export interface NotificationAdapterInterface { params: BroadCastNotificationInputParams, ): Promise; projectsHaveNewRank(params: ProjectsHaveNewRankingInputParam): Promise; + notifyProjectOwnershipChange( + params: ProjectOwnershipChangedParams, + ): Promise; } diff --git a/src/adapters/notifications/NotificationCenterAdapter.ts b/src/adapters/notifications/NotificationCenterAdapter.ts index 1cc873ba6..c8b48f52c 100644 --- a/src/adapters/notifications/NotificationCenterAdapter.ts +++ b/src/adapters/notifications/NotificationCenterAdapter.ts @@ -4,6 +4,7 @@ import { BroadCastNotificationInputParams, NotificationAdapterInterface, OrttoPerson, + ProjectOwnershipChangedParams, ProjectsHaveNewRankingInputParam, } from './NotificationAdapterInterface'; import { Donation } from '../../entities/donation'; @@ -989,6 +990,46 @@ export class NotificationCenterAdapter implements NotificationAdapterInterface { }); } } + + async notifyProjectOwnershipChange( + params: ProjectOwnershipChangedParams, + ): Promise { + try { + const newOwnerPayload = { + email: params.newOwnerUser?.email, + userId: params.newOwnerUser?.id || 0, + ownerName: params.newOwnerUser?.name || '', + projectName: params.projectName, + }; + + if (newOwnerPayload.email) { + await callSendNotification({ + eventName: NOTIFICATIONS_EVENT_NAMES.PROJECT_OWNERSHIP_CHANGED_TO, + segment: { + payload: newOwnerPayload, + }, + }); + } + + const oldOwnerPayload = { + email: params.previousOwnerUser?.email, + userId: params.previousOwnerUser?.id || 0, + ownerName: params.previousOwnerUser?.name || '', + projectName: params.projectName, + }; + + if (oldOwnerPayload.email) { + await callSendNotification({ + eventName: NOTIFICATIONS_EVENT_NAMES.PROJECT_OWNERSHIP_CHANGED_FROM, + segment: { + payload: oldOwnerPayload, + }, + }); + } + } catch (e) { + logger.error('notifyProjectOwnershipChange >> error', e); + } + } } const getEmailDataDonationAttributes = async (params: { diff --git a/src/analytics/analytics.ts b/src/analytics/analytics.ts index 44b40ffc4..9f4213aa9 100644 --- a/src/analytics/analytics.ts +++ b/src/analytics/analytics.ts @@ -49,4 +49,6 @@ export enum NOTIFICATIONS_EVENT_NAMES { SUBSCRIBE_ONBOARDING = 'Subscribe onboarding', CREATE_ORTTO_PROFILE = 'Create Ortto profile', SEND_EMAIL_CONFIRMATION = 'Send email confirmation', + PROJECT_OWNERSHIP_CHANGED_TO = 'Project ownership changed to', + PROJECT_OWNERSHIP_CHANGED_FROM = 'Project ownership changed from', } diff --git a/src/server/adminJs/tabs/projectsTab.ts b/src/server/adminJs/tabs/projectsTab.ts index 6a7596a73..1c0ec7f92 100644 --- a/src/server/adminJs/tabs/projectsTab.ts +++ b/src/server/adminJs/tabs/projectsTab.ts @@ -56,6 +56,7 @@ import { User } from '../../../entities/user'; import { refreshProjectEstimatedMatchingView } from '../../../services/projectViewsService'; import { extractAdminJsReferrerUrlParams } from '../adminJs'; import { relateManyProjectsToQfRound } from '../../../repositories/qfRoundRepository2'; +import { NotificationCenterAdapter } from '../../../adapters/notifications/NotificationCenterAdapter'; // add queries depending on which filters were selected export const buildProjectsQuery = ( @@ -658,6 +659,22 @@ export const exportProjectsWithFiltersToCsv = async ( }; } }; + +async function sendOwnershipChangeEmails( + project: Project, + previousOwnerUser: User | null, + newOwnerUser: User | null, +) { + const notificationCenter = new NotificationCenterAdapter(); + + const params = { + previousOwnerUser, + newOwnerUser, + projectName: project.title, + }; + await notificationCenter.notifyProjectOwnershipChange(params); +} + export const projectsTab = { resource: Project, options: { @@ -1082,9 +1099,10 @@ export const projectsTab = { }); if (project) { if (request?.record?.params?.adminChanged) { - const adminUser = await User.findOne({ + const newAdminUser = await User.findOne({ where: { id: request?.record?.params?.newAdminId }, }); + const previousAdminUser = project.adminUser; const previousAdminAddress = project.adminUser?.walletAddress; if (previousAdminAddress) { if (project.adminAddressHistory) { @@ -1093,8 +1111,13 @@ export const projectsTab = { project.adminAddressHistory = [previousAdminAddress]; } } - project.adminUser = adminUser!; + project.adminUser = newAdminUser!; await project.save(); + await sendOwnershipChangeEmails( + project, + previousAdminUser, + newAdminUser, + ); } // Not required for now // Project.notifySegment(project, SegmentEvents.PROJECT_EDITED);