From 109985333ff587f83be954f966fe05cc10098428 Mon Sep 17 00:00:00 2001 From: Kristina Date: Wed, 24 Apr 2024 20:18:55 +0400 Subject: [PATCH] Fix multiple mention notifications on edit doc (#5460) Signed-off-by: Kristina Fefelova --- models/activity/src/index.ts | 9 +- plugins/activity/src/index.ts | 8 +- .../activity-resources/src/references.ts | 105 +++++++++++++++--- 3 files changed, 105 insertions(+), 17 deletions(-) diff --git a/models/activity/src/index.ts b/models/activity/src/index.ts index 7d57f38ce8..1862eb3c2f 100644 --- a/models/activity/src/index.ts +++ b/models/activity/src/index.ts @@ -259,6 +259,12 @@ export class TReplyProvider extends TDoc implements ReplyProvider { function!: Resource<(message: ActivityMessage) => Promise> } +@Model(activity.class.UserMentionInfo, core.class.AttachedDoc, DOMAIN_ACTIVITY) +export class TUserMentionInfo extends TAttachedDoc { + user!: Ref + content!: string +} + export function createModel (builder: Builder): void { builder.createModel( TTxViewlet, @@ -276,7 +282,8 @@ export function createModel (builder: Builder): void { TIgnoreActivity, TActivityReference, TActivityMessagePreview, - TReplyProvider + TReplyProvider, + TUserMentionInfo ) builder.mixin(activity.class.DocUpdateMessage, core.class.Class, view.mixin.ObjectPresenter, { diff --git a/plugins/activity/src/index.ts b/plugins/activity/src/index.ts index 37d54ae882..a6ffdeaa89 100644 --- a/plugins/activity/src/index.ts +++ b/plugins/activity/src/index.ts @@ -302,6 +302,11 @@ export interface ReplyProvider extends Doc { function: Resource<(message: ActivityMessage) => Promise> } +export interface UserMentionInfo extends AttachedDoc { + user: Ref + content: string +} + /** * @public */ @@ -328,7 +333,8 @@ export default plugin(activityId, { Reaction: '' as Ref>, SavedMessage: '' as Ref>, ActivityReference: '' as Ref>, - ReplyProvider: '' as Ref> + ReplyProvider: '' as Ref>, + UserMentionInfo: '' as Ref> }, icon: { Activity: '' as Asset, diff --git a/server-plugins/activity-resources/src/references.ts b/server-plugins/activity-resources/src/references.ts index de6e530893..1268325772 100644 --- a/server-plugins/activity-resources/src/references.ts +++ b/server-plugins/activity-resources/src/references.ts @@ -36,17 +36,35 @@ import core, { Type } from '@hcengineering/core' import notification, { MentionInboxNotification } from '@hcengineering/notification' -import { extractReferences, markupToPmNode, pmNodeToMarkup, yDocContentToNodes } from '@hcengineering/text' +import { + extractReferences, + markupToPmNode, + pmNodeToMarkup, + yDocContentToNodes, + areEqualJson +} from '@hcengineering/text' import { StorageAdapter, TriggerControl } from '@hcengineering/server-core' -import activity, { ActivityMessage, ActivityReference } from '@hcengineering/activity' +import activity, { ActivityMessage, ActivityReference, UserMentionInfo } from '@hcengineering/activity' import contact, { Person, PersonAccount } from '@hcengineering/contact' import { - getPushCollaboratorTx, getCommonNotificationTxes, + getPushCollaboratorTx, isMessageAlreadyNotified, shouldNotifyCommon } from '@hcengineering/server-notification-resources' +async function getPersonAccount (person: Ref, control: TriggerControl): Promise { + return ( + await control.modelDb.findAll( + contact.class.PersonAccount, + { + person + }, + { limit: 1 } + ) + )[0] +} + export function isDocMentioned (doc: Ref, content: string | Buffer): boolean { const references = [] @@ -76,15 +94,8 @@ export async function getPersonNotificationTxes ( space: Ref, originTx: TxCUD ): Promise { - const receiver = ( - await control.modelDb.findAll( - contact.class.PersonAccount, - { - person: reference.attachedTo as Ref - }, - { limit: 1 } - ) - )[0] + const receiverPerson = reference.attachedTo as Ref + const receiver = await getPersonAccount(receiverPerson, control) if (receiver === undefined) { return [] @@ -111,6 +122,31 @@ export async function getPersonNotificationTxes ( return res } + const info = ( + await control.findAll(activity.class.UserMentionInfo, { + user: receiverPerson, + attachedTo: reference.attachedDocId + }) + )[0] + + if (info === undefined) { + res.push( + control.txFactory.createTxCreateDoc(activity.class.UserMentionInfo, space, { + attachedTo: reference.attachedDocId ?? reference.srcDocId, + attachedToClass: reference.attachedDocClass ?? reference.srcDocClass, + user: receiverPerson, + content: reference.message, + collection: 'mentions' + }) + ) + } else { + res.push( + control.txFactory.createTxUpdateDoc(info._class, info.space, info._id, { + content: reference.message + }) + ) + } + const data: Partial> = { header: activity.string.MentionedYouIn, messageHtml: reference.message, @@ -274,7 +310,7 @@ async function getCreateReferencesTxes ( ? (srcDocId as Ref) : srcDocSpace - return await getReferencesTxes(control, txFactory, refs, refSpace, [], originTx) + return await getReferencesTxes(control, txFactory, refs, refSpace, [], [], originTx) } async function getUpdateReferencesTxes ( @@ -330,12 +366,15 @@ async function getUpdateReferencesTxes ( attachedDocId, collection: 'references' }) + const userMentions = await control.findAll(activity.class.UserMentionInfo, { + attachedTo: attachedDocId + }) const refSpace: Ref = control.hierarchy.isDerived(srcDocClass, core.class.Space) ? (srcDocId as Ref) : srcDocSpace - return await getReferencesTxes(control, txFactory, references, refSpace, current, originTx) + return await getReferencesTxes(control, txFactory, references, refSpace, current, userMentions, originTx) } return [] @@ -402,6 +441,7 @@ async function getReferencesTxes ( references: Data[], space: Ref, current: ActivityReference[], + mentions: UserMentionInfo[], originTx: TxCUD ): Promise { const txes: Tx[] = [] @@ -428,6 +468,24 @@ async function getReferencesTxes ( } } + for (const mention of mentions) { + const refIndex = references.findIndex( + (r) => mention.user === r.attachedTo && mention.attachedTo === r.attachedDocId + ) + + const ref = references[refIndex] + + if (refIndex !== -1) { + const alreadyProcessed = areEqualJson(JSON.parse(mention.content), JSON.parse(ref.message)) + + if (alreadyProcessed) { + references.splice(refIndex, 1) + } + } else { + txes.push(txFactory.createTxRemoveDoc(mention._class, mention.space, mention._id)) + } + } + // Add missing references for (const ref of references) { txes.push(...(await createReferenceTxes(control, txFactory, ref, space, originTx))) @@ -447,11 +505,28 @@ async function getRemoveActivityReferenceTxes ( collection: 'references' }) + const mentions = await control.findAll(activity.class.UserMentionInfo, { + attachedTo: removedDocId + }) + for (const ref of refs) { const removeTx = txFactory.createTxRemoveDoc(ref._class, ref.space, ref._id) txes.push(txFactory.createTxCollectionCUD(ref.attachedToClass, ref.attachedTo, ref.space, ref.collection, removeTx)) } + for (const mention of mentions) { + const removeTx = txFactory.createTxRemoveDoc(mention._class, mention.space, mention._id) + txes.push( + txFactory.createTxCollectionCUD( + mention.attachedToClass, + mention.attachedTo, + mention.space, + mention.collection, + removeTx + ) + ) + } + return txes } @@ -559,7 +634,7 @@ async function ActivityReferenceRemove (tx: Tx, control: TriggerControl): Promis let hasMarkdown = false for (const attr of attributes.values()) { - if (isMarkupType(attr.type._class)) { + if (isMarkupType(attr.type._class) || isCollaborativeType(attr.type._class)) { hasMarkdown = true break }