Implement @everyone/@here (#8890)

Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
Kristina
2025-05-12 14:42:58 +07:00
committed by GitHub
parent 137260fe5b
commit 3295adee57
47 changed files with 675 additions and 351 deletions
@@ -37,19 +37,18 @@ import core, {
type MeasureContext,
AccountUuid
} from '@hcengineering/core'
import notification, { MentionInboxNotification } from '@hcengineering/notification'
import notification, { MentionInboxNotification, NotificationType } from '@hcengineering/notification'
import { getPerson } from '@hcengineering/server-contact'
import { StorageAdapter, TriggerControl } from '@hcengineering/server-core'
import {
getCommonNotificationTxes,
getNotificationProviderControl,
getPushCollaboratorTx,
NotifyResult,
shouldNotifyCommon,
type NotificationProviderControl,
isShouldNotifyTx
getAllowedProviders,
type NotificationProviderControl
} from '@hcengineering/server-notification-resources'
import { areEqualJson, extractReferences, jsonToMarkup, markupToJSON } from '@hcengineering/text-core'
import { getReceiversInfo } from '@hcengineering/server-notification-resources'
export function isDocMentioned (doc: Ref<Doc>, content: string): boolean {
const references = []
@@ -75,51 +74,9 @@ export async function getPersonNotificationTxes (
originTx: TxCUD<Doc>,
notificationControl: NotificationProviderControl
): Promise<Tx[]> {
const receiverPersonRef = reference.attachedTo as Ref<Person>
const receiverSocialIdentity = await control.findAll(ctx, contact.class.SocialIdentity, {
attachedTo: receiverPersonRef
})
const receiverSocialIds = receiverSocialIdentity.map((si) => si._id) as PersonId[]
if (receiverSocialIds.includes(senderId)) {
return []
}
const receiverEmployee = (
await control.findAll(
ctx,
contact.mixin.Employee,
{ _id: receiverPersonRef as Ref<Employee>, active: true },
{ limit: 1 }
)
)[0]
const receiverAccount = receiverEmployee?.personUuid
if (receiverAccount == null) {
return []
}
const { hierarchy } = control
const res: Tx[] = []
const isAvailable = await checkSpace(receiverAccount, space, control, res)
if (!isAvailable) {
return []
}
const doc = (await control.findAll(ctx, reference.srcDocClass, { _id: reference.srcDocId }))[0]
const receiverSpace = (
await control.findAll(ctx, contact.class.PersonSpace, { person: receiverPersonRef }, { limit: 1 })
)[0]
if (receiverSpace === undefined) return res
const collaboratorsTx = await getCollaboratorsTxes(reference, control, receiverAccount, doc)
res.push(...collaboratorsTx)
if (doc === undefined) {
return res
}
const receiverPersonRef = reference.attachedTo as Ref<Person>
const info = (
await control.findAll<UserMentionInfo>(ctx, activity.class.UserMentionInfo, {
user: receiverPersonRef,
@@ -127,101 +84,138 @@ export async function getPersonNotificationTxes (
})
)[0]
if (info === undefined) {
res.push(
control.txFactory.createTxCreateDoc(activity.class.UserMentionInfo, space, {
attachedTo: reference.attachedDocId ?? reference.srcDocId,
attachedToClass: reference.attachedDocClass ?? reference.srcDocClass,
user: receiverPersonRef,
content: reference.message,
collection: 'mentions'
})
)
res.push(getUpdateMentionInfoTx(control, reference, space, info))
if (
originTx._class === core.class.TxCreateDoc &&
hierarchy.isDerived(originTx.objectClass, activity.class.ActivityMessage)
) {
return res
}
if (info !== undefined && hierarchy.isDerived(originTx.objectClass, activity.class.ActivityMessage)) {
return res
}
const doc = (await control.findAll(ctx, reference.srcDocClass, { _id: reference.srcDocId }))[0]
if (doc === undefined) return res
const docSpace = (await control.findAll<Space>(control.ctx, core.class.Space, { _id: space }, { limit: 1 }))[0]
if (docSpace === undefined) return res
const senderAccount = control.ctx.contextData.socialStringsToUsers.get(senderId)
let collaborators: AccountUuid[] = []
if ([contact.mention.Everyone, contact.mention.Here].includes(reference.attachedTo as Ref<Employee>)) {
collaborators = await getMultipleMentionCollaborators(reference, control, doc)
} else {
res.push(
control.txFactory.createTxUpdateDoc(info._class, info.space, info._id, {
content: reference.message
})
)
const employee = (
await control.findAll(ctx, contact.mixin.Employee, { _id: reference.attachedTo as Ref<Employee> })
)[0]
if (employee?.personUuid != null && employee.personUuid !== senderAccount) {
collaborators = [employee.personUuid]
const collaboratorsTx = getCollaboratorsTxes(control, employee.personUuid, doc)
res.push(...collaboratorsTx)
}
}
const data: Omit<Data<MentionInboxNotification>, 'docNotifyContext'> = {
header: activity.string.MentionedYouIn,
messageHtml: reference.message,
mentionedIn: reference.attachedDocId ?? reference.srcDocId,
mentionedInClass: reference.attachedDocClass ?? reference.srcDocClass,
objectId: reference.srcDocId,
objectClass: reference.srcDocClass,
user: receiverAccount,
isViewed: false,
archived: false
}
const filteredCollaborators = collaborators.filter(
(c) => c !== senderAccount && checkSpace(c, docSpace, control, res)
)
if (filteredCollaborators.length === 0) return res
const receivers = await getReceiversInfo(ctx, filteredCollaborators, control)
if (receivers.length === 0) return []
const senderPerson = await getPerson(control, senderId)
const receiver = {
account: receiverAccount,
socialIds: receiverSocialIds,
space: receiverSpace._id,
employee: receiverEmployee._id
}
const sender = {
socialId: senderId,
person: senderPerson
}
const type: NotificationType = control.modelDb.findAllSync(notification.class.NotificationType, {
_id: notification.ids.MentionNotificationType
})[0]
for (const receiver of receivers) {
const data: Omit<Data<MentionInboxNotification>, 'docNotifyContext'> = {
header: activity.string.MentionedYouIn,
messageHtml: reference.message,
mentionedIn: reference.attachedDocId ?? reference.srcDocId,
mentionedInClass: reference.attachedDocClass ?? reference.srcDocClass,
objectId: reference.srcDocId,
objectClass: reference.srcDocClass,
user: receiver.account,
isViewed: false,
archived: false
}
const notifyResult = await shouldNotifyCommon(
control,
receiverSocialIds,
notification.ids.MentionCommonNotificationType,
notificationControl
)
const messageNotifyResult = await getMessageNotifyResult(
reference,
receiverAccount,
receiverEmployee,
receiverSocialIds,
control,
originTx,
doc,
notificationControl
)
const allowedProviders = getAllowedProviders(control, receiver.socialIds, type, notificationControl)
const notifyResult = new Map(allowedProviders.map((it) => [it, [type]]))
for (const [provider] of messageNotifyResult.entries()) {
if (notifyResult.has(provider)) {
notifyResult.delete(provider)
if (notifyResult.has(notification.providers.InboxNotificationProvider)) {
const txes = await getCommonNotificationTxes(
control.ctx,
control,
doc,
data,
receiver,
sender,
reference.srcDocId,
reference.srcDocClass,
doc.space,
originTx.modifiedOn,
notifyResult,
notification.class.MentionInboxNotification,
originTx
)
res.push(...txes)
}
}
if (notifyResult.has(notification.providers.InboxNotificationProvider)) {
const txes = await getCommonNotificationTxes(
ctx,
control,
doc,
data,
receiver,
sender,
reference.srcDocId,
reference.srcDocClass,
doc.space,
originTx.modifiedOn,
notifyResult,
notification.class.MentionInboxNotification,
originTx
)
res.push(...txes)
}
return res
}
async function checkSpace (
account: AccountUuid,
spaceId: Ref<Space>,
function getUpdateMentionInfoTx (
control: TriggerControl,
res: Tx[]
): Promise<boolean> {
const space = (await control.findAll<Space>(control.ctx, core.class.Space, { _id: spaceId }, { limit: 1 }))[0]
reference: Data<ActivityReference>,
space: Ref<Space>,
info?: UserMentionInfo
): Tx {
if (info === undefined) {
return control.txFactory.createTxCreateDoc(activity.class.UserMentionInfo, space, {
attachedTo: reference.attachedDocId ?? reference.srcDocId,
attachedToClass: reference.attachedDocClass ?? reference.srcDocClass,
user: reference.attachedTo as Ref<Person>,
content: reference.message,
collection: 'mentions'
})
}
return control.txFactory.createTxUpdateDoc(info._class, info.space, info._id, {
content: reference.message
})
}
async function getMultipleMentionCollaborators (
reference: Data<ActivityReference>,
control: TriggerControl,
doc: Doc
): Promise<AccountUuid[]> {
const { hierarchy } = control
const personRef = reference.attachedTo as Ref<Person>
const mixin = hierarchy.classHierarchyMixin(doc._class, notification.mixin.ClassCollaborators)
if (mixin === undefined) return []
const collaborators = hierarchy.as(doc, notification.mixin.Collaborators).collaborators
if (collaborators.length === 0) return []
const statuses = Array.from(control.userStatusMap.values())
return personRef === contact.mention.Here
? collaborators.filter((it) => statuses.some((s) => s.online && s.user === it))
: collaborators
}
function checkSpace (account: AccountUuid, space: Space, control: TriggerControl, res: Tx[]): boolean {
const isMember = space.members.includes(account)
if (space.private) {
@@ -235,13 +229,7 @@ async function checkSpace (
return true
}
async function getCollaboratorsTxes (
reference: Data<ActivityReference>,
control: TriggerControl,
receiver: AccountUuid,
object?: Doc
): Promise<TxMixin<Doc, Doc>[]> {
const { hierarchy } = control
function getCollaboratorsTxes (control: TriggerControl, receiver: AccountUuid, object?: Doc): TxMixin<Doc, Doc>[] {
const res: TxMixin<Doc, Doc>[] = []
if (object !== undefined) {
@@ -253,72 +241,9 @@ async function getCollaboratorsTxes (
}
}
if (reference.attachedDocClass === undefined || reference.attachedDocId === undefined) {
return res
}
if (!hierarchy.isDerived(reference.attachedDocClass, activity.class.ActivityMessage)) {
return res
}
const message = (
await control.findAll<ActivityMessage>(
control.ctx,
reference.attachedDocClass,
{
_id: reference.attachedDocId as Ref<ActivityMessage>
},
{ limit: 1 }
)
)[0]
if (message === undefined) {
return res
}
// Add user to collaborators of message where user is mentioned
const messageTx = getPushCollaboratorTx(control, receiver, message)
if (messageTx !== undefined) {
res.push(messageTx)
}
return res
}
async function getMessageNotifyResult (
reference: Data<ActivityReference>,
account: AccountUuid,
person: Person,
personIds: PersonId[],
control: TriggerControl,
tx: TxCUD<Doc>,
doc: Doc,
notificationControl: NotificationProviderControl
): Promise<NotifyResult> {
const { hierarchy } = control
if (
reference.attachedDocClass === undefined ||
reference.attachedDocId === undefined ||
tx._class !== core.class.TxCreateDoc
) {
return new Map()
}
const mixin = control.hierarchy.as(doc, notification.mixin.Collaborators)
if (mixin === undefined || !mixin.collaborators.includes(account)) {
return new Map()
}
if (!hierarchy.isDerived(reference.attachedDocClass, activity.class.ActivityMessage)) {
return new Map()
}
return await isShouldNotifyTx(control, tx, doc, person._id, personIds, false, false, notificationControl, undefined)
}
function isMarkupType (type: Ref<Class<Type<any>>>): boolean {
return type === core.class.TypeMarkup
}
@@ -485,6 +410,41 @@ async function createReferenceTxes (
return [tx]
}
async function getRemoveMentionTxes (
control: TriggerControl,
mention: UserMentionInfo,
originTx: TxCUD<Doc>
): Promise<Tx[]> {
const res: Tx[] = []
res.push(control.txFactory.createTxRemoveDoc(mention._class, mention.space, mention._id))
if (control.hierarchy.isDerived(originTx.objectClass, activity.class.ActivityMessage)) {
const _id = originTx.objectId as Ref<ActivityMessage>
const person = (
await control.findAll(control.ctx, contact.mixin.Employee, { _id: mention.user as Ref<Employee> })
)[0]
if (person?.personUuid !== undefined) {
const activityNotification = await control.findAll(control.ctx, notification.class.ActivityInboxNotification, {
attachedTo: _id,
user: person.personUuid
})
const mentionNotifications = await control.findAll(control.ctx, notification.class.MentionInboxNotification, {
mentionedIn: _id,
user: person.personUuid
})
res.push(
...activityNotification
.filter((it) => it.types?.length === 1 && it.types[0] === notification.ids.MentionNotificationType)
.map((it) => control.txFactory.createTxRemoveDoc(it._class, it.space, it._id))
)
res.push(...mentionNotifications.map((it) => control.txFactory.createTxRemoveDoc(it._class, it.space, it._id)))
}
}
return res
}
async function getReferencesTxes (
ctx: MeasureContext,
control: TriggerControl,
@@ -535,7 +495,8 @@ async function getReferencesTxes (
references.splice(refIndex, 1)
}
} else {
txes.push(txFactory.createTxRemoveDoc(mention._class, mention.space, mention._id))
const removeTxes = await getRemoveMentionTxes(control, mention, originTx)
txes.push(...removeTxes)
}
}
@@ -562,6 +523,14 @@ async function getRemoveActivityReferenceTxes (
attachedTo: removedDocId
})
const notifications = await control.findAll(control.ctx, notification.class.MentionInboxNotification, {
mentionedIn: removedDocId
})
for (const notification of notifications) {
const removeTx = txFactory.createTxRemoveDoc(notification._class, notification.space, notification._id)
txes.push(removeTx)
}
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))
@@ -727,12 +696,9 @@ export async function ReferenceTrigger (txes: TxCUD<Doc>[], control: TriggerCont
const result: Tx[] = []
for (const tx of txes) {
if (control.hierarchy.isDerived(tx.objectClass, activity.class.ActivityReference)) {
continue
}
if (control.hierarchy.isDerived(tx.objectClass, notification.class.InboxNotification)) {
continue
}
if (control.hierarchy.isDerived(tx.objectClass, activity.class.ActivityReference)) continue
if (control.hierarchy.isDerived(tx.objectClass, notification.class.InboxNotification)) continue
if (control.hierarchy.isDerived(tx.objectClass, activity.class.UserMentionInfo)) continue
if (tx._class === core.class.TxCreateDoc) {
result.push(...(await ActivityReferenceCreate(tx, control)))
+27 -16
View File
@@ -15,7 +15,7 @@
import activity, { ActivityMessage, ActivityReference } from '@hcengineering/activity'
import chunter, { Channel, ChatMessage, chunterId, ChunterSpace, ThreadMessage } from '@hcengineering/chunter'
import contact, { Person } from '@hcengineering/contact'
import contact, { Employee, Person } from '@hcengineering/contact'
import { getAccountBySocialId, getPerson } from '@hcengineering/server-contact'
import core, {
PersonId,
@@ -36,7 +36,8 @@ import core, {
UserStatus,
type MeasureContext,
combineAttributes,
AccountUuid
AccountUuid,
notEmpty
} from '@hcengineering/core'
import notification, { DocNotifyContext, NotificationContent } from '@hcengineering/notification'
import { getMetadata, IntlString, translate } from '@hcengineering/platform'
@@ -46,7 +47,7 @@ import {
getDocCollaborators,
getMixinTx
} from '@hcengineering/server-notification-resources'
import { markupToText, stripTags } from '@hcengineering/text-core'
import { extractReferences, markupToText, stripTags } from '@hcengineering/text-core'
import { jsonToHTML, markupToJSON } from '@hcengineering/text'
import { workbenchId } from '@hcengineering/workbench'
@@ -175,14 +176,21 @@ async function OnChatMessageCreated (ctx: MeasureContext, tx: TxCUD<Doc>, contro
const isChannel = hierarchy.isDerived(targetDoc._class, chunter.class.Channel)
const res: Tx[] = []
const account = await getAccountBySocialId(control, message.modifiedBy)
if (account == null) {
return []
}
const node = markupToJSON(message.message)
const references = extractReferences(node)
const mentionedPersons = references
.filter(({ objectClass }) => control.hierarchy.isDerived(objectClass, contact.class.Person))
.map(({ objectId }) => objectId as Ref<Person>)
const employees =
mentionedPersons.length > 0
? await control.findAll(ctx, contact.mixin.Employee, { _id: { $in: mentionedPersons as Ref<Employee>[] } })
: []
const collaboratorsFromMessage = [...employees.map((it) => it.personUuid), account].filter(notEmpty)
if (hierarchy.hasMixin(targetDoc, notification.mixin.Collaborators)) {
const collaboratorsMixin = hierarchy.as(targetDoc, notification.mixin.Collaborators)
if (!collaboratorsMixin.collaborators.includes(account)) {
const newCollabs = collaboratorsFromMessage.filter((it) => !collaboratorsMixin.collaborators.includes(it))
if (newCollabs.length > 0) {
res.push(
control.txFactory.createTxMixin(
targetDoc._id,
@@ -190,22 +198,25 @@ async function OnChatMessageCreated (ctx: MeasureContext, tx: TxCUD<Doc>, contro
targetDoc.space,
notification.mixin.Collaborators,
{
$push: {
collaborators: account
}
$push: { collaborators: { $each: newCollabs, $position: 0 } }
}
)
)
}
} else {
const collaborators = await getDocCollaborators(ctx, targetDoc, mixin, control)
if (!collaborators.includes(account)) {
collaborators.push(account)
}
res.push(getMixinTx(tx, control, collaborators))
res.push(getMixinTx(tx, control, Array.from(new Set(collaborators.concat(collaboratorsFromMessage)))))
}
if (isChannel && !(targetDoc as Channel).members.includes(account)) {
if (collaboratorsFromMessage.length > 0) {
control.txFactory.createTxMixin(message._id, message._class, message.space, notification.mixin.Collaborators, {
$push: {
$push: { collaborators: { $each: collaboratorsFromMessage, $position: 0 } }
}
})
}
if (account != null && isChannel && !(targetDoc as Channel).members.includes(account)) {
res.push(...joinChannel(control, targetDoc as Channel, account))
}
+3 -3
View File
@@ -31,7 +31,7 @@ import {
} from '@hcengineering/core'
import gmail, { Message } from '@hcengineering/gmail'
import { TriggerControl } from '@hcengineering/server-core'
import { BaseNotificationType, InboxNotification, NotificationType } from '@hcengineering/notification'
import { NotificationType, InboxNotification } from '@hcengineering/notification'
import serverNotification, { ReceiverInfo, SenderInfo } from '@hcengineering/server-notification'
import { getContentByTemplate } from '@hcengineering/server-notification-resources'
import { getMetadata } from '@hcengineering/platform'
@@ -138,7 +138,7 @@ export async function sendEmailNotification (
async function notifyByEmail (
control: TriggerControl,
type: Ref<BaseNotificationType>,
type: Ref<NotificationType>,
doc: Doc | undefined,
sender: SenderInfo,
receiver: ReceiverInfo,
@@ -160,7 +160,7 @@ async function notifyByEmail (
const SendEmailNotifications = async (
control: TriggerControl,
types: BaseNotificationType[],
types: NotificationType[],
object: Doc,
data: InboxNotification,
receiver: ReceiverInfo,
@@ -47,7 +47,6 @@ import core, {
} from '@hcengineering/core'
import notification, {
ActivityInboxNotification,
BaseNotificationType,
ClassCollaborators,
Collaborators,
CommonInboxNotification,
@@ -87,6 +86,7 @@ import {
isMixinTx,
isShouldNotifyTx,
isUserEmployeeInFieldValueTypeMatch,
mentionTypeMatch,
messageToMarkup,
type NotificationProviderControl,
replaceAll,
@@ -147,6 +147,7 @@ export async function getCommonNotificationTxes (
data,
_class,
modifiedOn,
[],
true,
tx
)
@@ -191,7 +192,7 @@ function fillTemplate (
export async function getContentByTemplate (
doc: Doc | undefined,
sender: string,
type: Ref<BaseNotificationType>,
type: Ref<NotificationType>,
control: TriggerControl,
data: string,
notificationData?: InboxNotification,
@@ -352,6 +353,7 @@ export async function pushInboxNotifications (
data: Partial<Data<InboxNotification>>,
_class: Ref<Class<InboxNotification>>,
modifiedOn: Timestamp,
types: Ref<NotificationType>[],
shouldUpdateTimestamp = true,
tx?: TxCUD<Doc>
): Promise<TxCreateDoc<InboxNotification> | undefined> {
@@ -381,6 +383,7 @@ export async function pushInboxNotifications (
archived: false,
objectId,
objectClass,
types,
...data
}
const notificationTx = control.txFactory.createTxCreateDoc(_class, receiver.space, notificationData)
@@ -500,6 +503,7 @@ export async function pushActivityInboxNotifications (
object: Doc,
docNotifyContexts: DocNotifyContext[],
activityMessage: ActivityMessage,
types: Ref<NotificationType>[],
shouldUpdateTimestamp: boolean
): Promise<TxCreateDoc<InboxNotification> | undefined> {
const content = await getNotificationContent(originTx, receiver.employee, sender, object, control)
@@ -522,6 +526,7 @@ export async function pushActivityInboxNotifications (
data,
notification.class.ActivityInboxNotification,
activityMessage.modifiedOn,
types,
shouldUpdateTimestamp,
originTx
)
@@ -590,8 +595,7 @@ export async function getNotificationTxes (
control,
tx,
object,
receiver.employee,
receiver.socialIds,
receiver,
params.isOwn,
params.isSpace,
settings,
@@ -599,6 +603,7 @@ export async function getNotificationTxes (
)
if (notifyResult.has(notification.providers.InboxNotificationProvider)) {
const types = (notifyResult.get(notification.providers.InboxNotificationProvider) ?? []).map((it) => it._id)
const notificationTx = await pushActivityInboxNotifications(
ctx,
tx,
@@ -609,6 +614,7 @@ export async function getNotificationTxes (
object,
docNotifyContexts,
message,
types,
params.shouldUpdateTimestamp
)
@@ -753,7 +759,7 @@ export async function createCollabDocInfo (
cache.set(space._id, space)
const filteredCollaborators = control.hierarchy.isDerived(object._class, core.class.SystemSpace)
const filteredCollaborators = !space.private
? collaborators
: collaborators.filter(
(it) =>
@@ -1028,7 +1034,7 @@ async function updateCollaboratorsMixin (
prevCollabs = mixin !== undefined ? new Set(await getDocCollaborators(ctx, prevDoc, mixin, control)) : new Set()
}
const type = await control.modelDb.findOne(notification.class.BaseNotificationType, {
const type = await control.modelDb.findOne(notification.class.NotificationType, {
_id: notification.ids.CollaboratoAddNotification
})
@@ -1091,6 +1097,7 @@ async function updateCollaboratorsMixin (
prevDoc,
docNotifyContexts,
message,
[],
true
)
}
@@ -1712,6 +1719,7 @@ export default async () => ({
PushNotificationsHandler
},
function: {
IsUserEmployeeInFieldValueTypeMatch: isUserEmployeeInFieldValueTypeMatch
IsUserEmployeeInFieldValueTypeMatch: isUserEmployeeInFieldValueTypeMatch,
MentionTypeMatch: mentionTypeMatch
}
})
@@ -13,7 +13,7 @@
// limitations under the License.
//
import {
BaseNotificationType,
NotificationType,
DocNotifyContext,
InboxNotification,
NotificationProvider
@@ -32,7 +32,7 @@ export interface Content {
/**
* @public
*/
export type NotifyResult = Map<Ref<NotificationProvider>, BaseNotificationType[]>
export type NotifyResult = Map<Ref<NotificationProvider>, NotificationType[]>
export interface NotifyParams {
isOwn: boolean
@@ -40,14 +40,14 @@ import core, {
Ref,
Space,
Tx,
TxCreateDoc,
TxCUD,
TxMixin,
TxProcessor,
TxUpdateDoc
} from '@hcengineering/core'
import notification, {
BaseNotificationType,
Collaborators,
CommonNotificationType,
NotificationContent,
notificationId,
NotificationProvider,
@@ -67,9 +67,10 @@ import serverNotification, {
import serverView from '@hcengineering/server-view'
import { encodeObjectURI } from '@hcengineering/view'
import { workbenchId } from '@hcengineering/workbench'
import { extractReferences, markupToJSON, Reference } from '@hcengineering/text-core'
import { getPersonSpaces } from '@hcengineering/server-contact'
import { NotifyResult } from './types'
import { getPersonSpaces } from '@hcengineering/server-contact'
/**
* @public
@@ -94,6 +95,36 @@ export function isUserEmployeeInFieldValueTypeMatch (
}
}
export const mentionTypeMatch = (
tx: TxCreateDoc<ChatMessage>,
doc: Doc,
person: Ref<Person>,
socialIds: PersonId[],
type: NotificationType,
control: TriggerControl,
account: AccountUuid
): boolean => {
const hierarchy = control.hierarchy
if (tx._class !== core.class.TxCreateDoc) return false
if (!hierarchy.isDerived(tx.objectClass, chunter.class.ChatMessage)) return false
const message = TxProcessor.createDoc2Doc(tx)
const content: string = message.message
const references: Reference[] =
control.contextCache.get(`${message._id}_references`) ?? extractReferences(markupToJSON(content))
control.contextCache.set(`${message._id}_references`, references)
if (references.length === 0) return false
if (references.some(({ objectId }) => objectId === contact.mention.Everyone)) return true
if (references.some(({ objectId }) => objectId === contact.mention.Here)) {
const isOnline = Array.from(control.userStatusMap.values()).some(({ user, online }) => user === account && online)
if (isOnline) return true
}
return references.some(({ objectId }) => objectId === person)
}
/**
* @public
*/
@@ -119,27 +150,20 @@ function escapeRegExp (str: string): string {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}
export async function shouldNotifyCommon (
export function getAllowedProviders (
control: TriggerControl,
socialIds: PersonId[],
typeId: Ref<CommonNotificationType>,
type: NotificationType,
notificationControl: NotificationProviderControl
): Promise<NotifyResult> {
const type = (await control.modelDb.findAll(notification.class.CommonNotificationType, { _id: typeId }))[0]
if (type === undefined) {
return new Map()
}
const result = new Map<Ref<NotificationProvider>, BaseNotificationType[]>()
const providers = await control.modelDb.findAll(notification.class.NotificationProvider, {})
): Ref<NotificationProvider>[] {
const result: Ref<NotificationProvider>[] = []
const providers = control.modelDb.findAllSync(notification.class.NotificationProvider, {})
for (const provider of providers) {
const allowed = isAllowed(control, socialIds, type, provider, notificationControl)
if (allowed) {
const cur = result.get(provider._id) ?? []
result.set(provider._id, [...cur, type])
result.push(provider._id)
}
}
@@ -149,7 +173,7 @@ export async function shouldNotifyCommon (
export function isAllowed (
control: TriggerControl,
receiverIds: PersonId[],
type: BaseNotificationType,
type: NotificationType,
provider: NotificationProvider,
notificationControl: NotificationProviderControl
): boolean {
@@ -192,8 +216,7 @@ export async function isShouldNotifyTx (
control: TriggerControl,
tx: TxCUD<Doc>,
object: Doc,
person: Ref<Person>,
personIds: PersonId[],
receiver: ReceiverInfo,
isOwn: boolean,
isSpace: boolean,
notificationControl: NotificationProviderControl,
@@ -201,7 +224,7 @@ export async function isShouldNotifyTx (
): Promise<NotifyResult> {
const types = getMatchedTypes(control, tx, isOwn, isSpace, docUpdateMessage?.attributeUpdates?.attrKey)
const modifiedByPersonId = tx.modifiedBy
const result = new Map<Ref<NotificationProvider>, BaseNotificationType[]>()
const result = new Map<Ref<NotificationProvider>, NotificationType[]>()
let providers: NotificationProvider[] = control.modelDb.findAllSync(notification.class.NotificationProvider, {})
if (getMetadata(serverNotification.metadata.InboxOnlyNotifications) === true) {
@@ -209,7 +232,7 @@ export async function isShouldNotifyTx (
}
for (const type of types) {
if (type.allowedForAuthor !== true && personIds.includes(modifiedByPersonId)) {
if (type.allowedForAuthor !== true && receiver.socialIds.includes(modifiedByPersonId)) {
continue
}
@@ -217,7 +240,7 @@ export async function isShouldNotifyTx (
const mixin = control.hierarchy.as(type, serverNotification.mixin.TypeMatch)
if (mixin.func !== undefined) {
const f = await getResource(mixin.func)
let res = f(tx, object, person, personIds, type, control)
let res = f(tx, object, receiver.employee, receiver.socialIds, type, control, receiver.account)
if (res instanceof Promise) {
res = await res
}
@@ -225,7 +248,7 @@ export async function isShouldNotifyTx (
}
}
for (const provider of providers) {
const allowed = isAllowed(control, personIds, type, provider, notificationControl)
const allowed = isAllowed(control, receiver.socialIds, type, provider, notificationControl)
if (allowed) {
const cur = result.get(provider._id) ?? []
+4 -2
View File
@@ -55,7 +55,8 @@ export type TypeMatchFunc = Resource<
person: Ref<Person>,
socialIds: PersonId[],
type: NotificationType,
control: TriggerControl
control: TriggerControl,
account: AccountUuid
) => boolean | Promise<boolean>
>
@@ -123,6 +124,7 @@ export default plugin(serverNotificationId, {
PushNotificationsHandler: '' as Resource<TriggerFunc>
},
function: {
IsUserEmployeeInFieldValueTypeMatch: '' as TypeMatchFunc
IsUserEmployeeInFieldValueTypeMatch: '' as TypeMatchFunc,
MentionTypeMatch: '' as TypeMatchFunc
}
})
+1 -10
View File
@@ -307,16 +307,7 @@ export async function OnToDoCreate (txes: TxCUD<Doc>[], control: TriggerControl)
const senderInfo: SenderInfo = await getSenderInfo(control.ctx, tx.modifiedBy, control)
const notificationControl = await getNotificationProviderControl(control.ctx, control)
const notifyResult = await isShouldNotifyTx(
control,
createTx,
todo,
employee._id,
socialIds,
true,
false,
notificationControl
)
const notifyResult = await isShouldNotifyTx(control, createTx, todo, receiverInfo, true, false, notificationControl)
const content = await getNotificationContent(tx, employee._id, senderInfo, todo, control)
const data: Partial<Data<CommonInboxNotification>> = {
...content,