UBER-845: Add NotificationPresenter to send rich text notifications (#3729)

Signed-off-by: Maxim Karmatskikh <mkarmatskih@gmail.com>
This commit is contained in:
Maksim Karmatskikh
2023-09-28 12:08:00 +07:00
committed by GitHub
parent a3fd97e3b4
commit 1ca9de297a
21 changed files with 368 additions and 47 deletions
@@ -45,16 +45,18 @@ import notification, {
ClassCollaborators,
Collaborators,
DocUpdates,
DocUpdateTx,
EmailNotification,
NotificationProvider,
NotificationType
} from '@hcengineering/notification'
import { getResource } from '@hcengineering/platform'
import { IntlString, getResource } from '@hcengineering/platform'
import type { TriggerControl } from '@hcengineering/server-core'
import serverNotification, {
HTMLPresenter,
TextPresenter,
getEmployee,
NotificationPresenter,
getPersonAccount,
getPersonAccountById
} from '@hcengineering/server-notification'
@@ -182,6 +184,10 @@ export function getTextPresenter (_class: Ref<Class<Doc>>, hierarchy: Hierarchy)
return hierarchy.classHierarchyMixin(_class, serverNotification.mixin.TextPresenter)
}
function getNotificationPresenter (_class: Ref<Class<Doc>>, hierarchy: Hierarchy): NotificationPresenter | undefined {
return hierarchy.classHierarchyMixin(_class, serverNotification.mixin.NotificationPresenter)
}
function fillTemplate (template: string, sender: string, doc: string, data: string): string {
let res = replaceAll(template, '{sender}', sender)
res = replaceAll(res, '{doc}', doc)
@@ -462,10 +468,43 @@ async function isShouldNotify (
}
}
async function findPersonForAccount (control: TriggerControl, personId: Ref<Person>): Promise<Person | undefined> {
const persons = await control.findAll(contact.class.Person, { _id: personId })
if (persons !== undefined && persons.length > 0) {
return persons[0]
}
return undefined
}
async function getFallbackNotificationFullfillment (
object: Doc,
originTx: TxCUD<Doc>,
control: TriggerControl
): Promise<Record<string, string | number>> {
const intlParams: Record<string, string | number> = {}
const textPresenter = getTextPresenter(object._class, control.hierarchy)
if (textPresenter !== undefined) {
const textPresenterFunc = await getResource(textPresenter.presenter)
intlParams.title = await textPresenterFunc(object, control)
}
const account = control.modelDb.getObject(originTx.modifiedBy) as PersonAccount
if (account !== undefined) {
const senderPerson = await findPersonForAccount(control, account.person)
if (senderPerson !== undefined) {
const senderName = formatName(senderPerson.name)
intlParams.senderName = senderName
}
}
return intlParams
}
/**
* @public
*/
export function pushNotification (
export async function pushNotification (
control: TriggerControl,
res: Tx[],
target: Ref<Account>,
@@ -473,7 +512,46 @@ export function pushNotification (
originTx: TxCUD<Doc>,
docUpdates: DocUpdates[],
modifiedBy?: Ref<Account>
): void {
): Promise<void> {
let title: IntlString = notification.string.CommonNotificationTitle
let body: IntlString = notification.string.CommonNotificationBody
let intlParams: Record<string, string | number> = await getFallbackNotificationFullfillment(object, originTx, control)
let intlParamsNotLocalized: Record<string, IntlString> | undefined
const notificationPresenter = getNotificationPresenter(object._class, control.hierarchy)
if (notificationPresenter !== undefined) {
const getFuillfillmentParams = await getResource(notificationPresenter.presenter)
const updateIntlParams = await getFuillfillmentParams(object, originTx, target, control)
title = updateIntlParams.title
body = updateIntlParams.body
intlParams = {
...intlParams,
...updateIntlParams.intlParams
}
if (updateIntlParams.intlParamsNotLocalized != null) {
intlParamsNotLocalized = updateIntlParams.intlParamsNotLocalized
}
}
const tx: DocUpdateTx = {
_id: originTx._id,
modifiedOn: originTx.modifiedOn,
modifiedBy: modifiedBy ?? originTx.modifiedBy,
isNew: true
}
if (title !== undefined) {
tx.title = title
}
if (body !== undefined) {
tx.body = body
}
if (intlParams !== undefined) {
tx.intlParams = intlParams
}
if (intlParamsNotLocalized !== undefined) {
tx.intlParamsNotLocalized = intlParamsNotLocalized
}
const current = docUpdates.find((p) => p.user === target)
if (current === undefined) {
res.push(
@@ -483,27 +561,13 @@ export function pushNotification (
attachedToClass: object._class,
hidden: false,
lastTxTime: originTx.modifiedOn,
txes: [
{
_id: originTx._id,
modifiedOn: originTx.modifiedOn,
modifiedBy: modifiedBy ?? originTx.modifiedBy,
isNew: true
}
]
txes: [tx]
})
)
} else {
res.push(
control.txFactory.createTxUpdateDoc(current._class, current.space, current._id, {
$push: {
txes: {
_id: originTx._id,
modifiedOn: originTx.modifiedOn,
modifiedBy: modifiedBy ?? originTx.modifiedBy,
isNew: true
}
}
$push: { txes: tx }
})
)
res.push(
@@ -528,7 +592,7 @@ async function getNotificationTxes (
const res: Tx[] = []
const allowed = await isShouldNotify(control, tx, originTx, object, target, isOwn, isSpace)
if (allowed.allowed) {
pushNotification(control, res, target, object, originTx, docUpdates)
await pushNotification(control, res, target, object, originTx, docUpdates)
}
if (allowed.emails.length === 0) return res
const acc = await getPersonAccountById(target, control)
@@ -711,7 +775,7 @@ async function updateCollaboratorsMixin (
attachedTo: tx.objectId
})
for (const collab of newCollabs) {
pushNotification(control, res, collab, prevDoc, originTx, docUpdates)
await pushNotification(control, res, collab, prevDoc, originTx, docUpdates)
}
}
}