mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-13 21:27:46 +02:00
Implement @everyone/@here (#8890)
Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
@@ -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)))
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
@@ -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) ?? []
|
||||
|
||||
@@ -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
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user