From 1c1d0bc538e0e5d73ddf44119483ad3f13b6ecbe Mon Sep 17 00:00:00 2001 From: Kristina Date: Tue, 6 Aug 2024 13:44:23 +0400 Subject: [PATCH] Move notifications to person spaces (#6263) Signed-off-by: Kristina Fefelova --- .vscode/launch.json | 1 + desktop-package/package.json | 2 +- desktop/package.json | 2 +- models/chunter/src/migration.ts | 25 ++-- models/contact/src/index.ts | 14 +- models/contact/src/migration.ts | 81 ++++++++--- models/notification/package.json | 5 +- models/notification/src/index.ts | 16 ++- models/notification/src/migration.ts | 116 ++++++++++++++++ packages/ui/src/components/ListView.svelte | 61 ++++---- .../src/components/Channel.svelte | 2 +- .../src/components/chat/ChatAside.svelte | 2 +- .../chat/create/CreateChannel.svelte | 21 ++- .../chat/create/CreateDirectChat.svelte | 21 +-- .../chat/navigator/ChatNavGroup.svelte | 14 +- .../chat/navigator/ChatNavSection.svelte | 4 +- .../chat/navigator/ChatNavigator.svelte | 2 +- .../src/components/chat/utils.ts | 2 +- .../ChatMessageNotificationLabel.svelte | 4 +- plugins/chunter-resources/src/utils.ts | 36 +++-- plugins/contact/src/index.ts | 7 +- plugins/notification-assets/lang/en.json | 3 +- plugins/notification-assets/lang/es.json | 3 +- plugins/notification-assets/lang/fr.json | 3 +- plugins/notification-assets/lang/pt.json | 3 +- plugins/notification-assets/lang/ru.json | 3 +- plugins/notification-assets/lang/zh.json | 3 +- .../components/DocNotifyContextCard.svelte | 64 +++++++-- .../DocNotifyContextPresenter.svelte | 14 +- .../src/components/NotifyContextIcon.svelte | 6 +- .../src/components/inbox/Inbox.svelte | 14 +- .../inbox/InboxGroupedListView.svelte | 1 + .../src/inboxNotificationsClient.ts | 6 +- plugins/notification-resources/src/utils.ts | 45 +++--- plugins/notification/package.json | 1 + plugins/notification/src/index.ts | 16 ++- .../activity-resources/src/references.ts | 29 ++-- server-plugins/chunter-resources/src/index.ts | 32 +++-- server-plugins/contact-resources/src/index.ts | 44 +++++- server-plugins/gmail-resources/src/index.ts | 71 ++-------- .../notification-resources/src/index.ts | 131 +++++++----------- .../notification-resources/src/utils.ts | 65 +++++++-- server-plugins/notification/src/index.ts | 15 +- server-plugins/request-resources/src/index.ts | 10 +- .../telegram-resources/src/index.ts | 64 +-------- server-plugins/time-resources/src/index.ts | 55 ++++---- server/account/src/operations.ts | 2 +- server/mongo/src/storage.ts | 3 +- server/tool/src/index.ts | 2 +- .../github/pod-github/src/notifications.ts | 15 +- services/github/pod-github/src/platform.ts | 54 +++++--- services/github/pod-github/src/worker.ts | 14 +- 52 files changed, 729 insertions(+), 500 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 829d0722da..d28b8c8952 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -192,6 +192,7 @@ "MINIO_ACCESS_KEY": "minioadmin", "MINIO_SECRET_KEY": "minioadmin", "MINIO_ENDPOINT": "localhost", + "TRANSACTOR_URL": "ws://localhost:3333", "MONGO_URL": "mongodb://localhost:27017", "ACCOUNTS_URL": "http://localhost:3000", "TELEGRAM_DATABASE": "telegram-service", diff --git a/desktop-package/package.json b/desktop-package/package.json index 0b7e3cb98d..2291d5c053 100644 --- a/desktop-package/package.json +++ b/desktop-package/package.json @@ -1,6 +1,6 @@ { "name": "desktop", - "version": "0.6.266", + "version": "0.6.271", "main": "dist/main/electron.js", "author": "Hardcore Engineering ", "template": "@hcengineering/default-package", diff --git a/desktop/package.json b/desktop/package.json index 686f32fe53..b6aa591571 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@hcengineering/desktop", - "version": "0.6.266", + "version": "0.6.271", "main": "dist/main/electron.js", "template": "@hcengineering/webpack-package", "scripts": { diff --git a/models/chunter/src/migration.ts b/models/chunter/src/migration.ts index afdb2c97e3..ef1e2b4725 100644 --- a/models/chunter/src/migration.ts +++ b/models/chunter/src/migration.ts @@ -44,14 +44,14 @@ export const DOMAIN_COMMENT = 'comment' as Domain export async function createDocNotifyContexts ( client: MigrationUpgradeClient, tx: TxOperations, - attachedTo: Ref, - attachedToClass: Ref> + objectId: Ref, + objectClass: Ref>, + objectSpace: Ref ): Promise { const users = await client.findAll(core.class.Account, {}) const docNotifyContexts = await client.findAll(notification.class.DocNotifyContext, { user: { $in: users.map((it) => it._id) }, - attachedTo, - attachedToClass + objectId }) for (const user of users) { if (user._id === core.account.System) { @@ -62,8 +62,9 @@ export async function createDocNotifyContexts ( if (docNotifyContext === undefined) { await tx.createDoc(notification.class.DocNotifyContext, core.space.Space, { user: user._id, - attachedTo, - attachedToClass, + objectId, + objectClass, + objectSpace, isPinned: false }) } @@ -94,7 +95,7 @@ export async function createGeneral (client: MigrationUpgradeClient, tx: TxOpera topic: 'General Channel', private: false, archived: false, - members: await getAllEmployeeAccounts(tx), + members: await getAllPersonAccounts(tx), autoJoin: true }, chunter.space.General @@ -102,10 +103,10 @@ export async function createGeneral (client: MigrationUpgradeClient, tx: TxOpera } } - await createDocNotifyContexts(client, tx, chunter.space.General, chunter.class.Channel) + await createDocNotifyContexts(client, tx, chunter.space.General, chunter.class.Channel, core.space.Space) } -async function getAllEmployeeAccounts (tx: TxOperations): Promise[]> { +async function getAllPersonAccounts (tx: TxOperations): Promise[]> { const employees = await tx.findAll(contactPlugin.mixin.Employee, { active: true }) const accounts = await tx.findAll(contactPlugin.class.PersonAccount, { person: { $in: employees.map((it) => it._id) } @@ -114,7 +115,7 @@ async function getAllEmployeeAccounts (tx: TxOperations): Promise { - const accs = await getAllEmployeeAccounts(tx) + const accs = await getAllPersonAccounts(tx) const newMembers: Ref[] = [...current.members] for (const acc of accs) { if (!newMembers.includes(acc)) { @@ -150,7 +151,7 @@ export async function createRandom (client: MigrationUpgradeClient, tx: TxOperat topic: 'Random Talks', private: false, archived: false, - members: await getAllEmployeeAccounts(tx), + members: await getAllPersonAccounts(tx), autoJoin: true }, chunter.space.Random @@ -158,7 +159,7 @@ export async function createRandom (client: MigrationUpgradeClient, tx: TxOperat } } - await createDocNotifyContexts(client, tx, chunter.space.Random, chunter.class.Channel) + await createDocNotifyContexts(client, tx, chunter.space.Random, chunter.class.Channel, core.space.Space) } async function convertCommentsToChatMessages (client: MigrationClient): Promise { diff --git a/models/contact/src/index.ts b/models/contact/src/index.ts index 9bc7c59686..b4f6b18f28 100644 --- a/models/contact/src/index.ts +++ b/models/contact/src/index.ts @@ -29,7 +29,8 @@ import { type Organization, type Person, type PersonAccount, - type Status + type Status, + type PersonSpace } from '@hcengineering/contact' import { AccountRole, @@ -64,7 +65,7 @@ import { } from '@hcengineering/model' import attachment from '@hcengineering/model-attachment' import chunter from '@hcengineering/model-chunter' -import core, { TAccount, TAttachedDoc, TDoc } from '@hcengineering/model-core' +import core, { TAccount, TAttachedDoc, TDoc, TSpace } from '@hcengineering/model-core' import { createPublicLinkAction } from '@hcengineering/model-guest' import { generateClassNotificationTypes } from '@hcengineering/model-notification' import presentation from '@hcengineering/model-presentation' @@ -219,6 +220,12 @@ export class TContactsTab extends TDoc implements ContactsTab { index!: number } +@Model(contact.class.PersonSpace, core.class.Space) +export class TPersonSpace extends TSpace implements PersonSpace { + @Prop(TypeRef(contact.class.Person), contact.string.Person) + person!: Ref +} + export function createModel (builder: Builder): void { builder.createModel( TAvatarProvider, @@ -231,7 +238,8 @@ export function createModel (builder: Builder): void { TChannel, TStatus, TMember, - TContactsTab + TContactsTab, + TPersonSpace ) builder.mixin(contact.class.Contact, core.class.Class, activity.mixin.ActivityDoc, {}) diff --git a/models/contact/src/migration.ts b/models/contact/src/migration.ts index 506cc09d57..dc7989ea55 100644 --- a/models/contact/src/migration.ts +++ b/models/contact/src/migration.ts @@ -1,23 +1,32 @@ // -import { DOMAIN_TX, TxOperations, type Class, type Doc, type Domain, type Ref, type Space } from '@hcengineering/core' +import { + type Class, + type Doc, + type Domain, + DOMAIN_TX, + generateId, + type Ref, + type Space, + TxOperations +} from '@hcengineering/core' import { createDefaultSpace, - tryMigrate, - tryUpgrade, type MigrateOperation, type MigrateUpdate, type MigrationClient, type MigrationDocumentQuery, type MigrationUpgradeClient, - type ModelLogger + type ModelLogger, + tryMigrate, + tryUpgrade } from '@hcengineering/model' import activity, { DOMAIN_ACTIVITY } from '@hcengineering/model-activity' -import core from '@hcengineering/model-core' +import core, { DOMAIN_SPACE } from '@hcengineering/model-core' import { DOMAIN_VIEW } from '@hcengineering/model-view' +import { AvatarType, type Contact, type Person, type PersonSpace } from '@hcengineering/contact' -import { AvatarType, type Contact } from '@hcengineering/contact' -import contact, { DOMAIN_CONTACT, contactId } from './index' +import contact, { contactId, DOMAIN_CONTACT } from './index' async function createEmployeeEmail (client: TxOperations): Promise { const employees = await client.findAll(contact.mixin.Employee, {}) @@ -100,23 +109,55 @@ async function migrateAvatars (client: MigrationClient): Promise { ) } +async function createPersonSpaces (client: MigrationClient): Promise { + const spaces = await client.find(DOMAIN_SPACE, { _class: contact.class.PersonSpace }) + + if (spaces.length > 0) { + return + } + + const accounts = await client.model.findAll(contact.class.PersonAccount, {}) + const employees = await client.find(DOMAIN_CONTACT, { [contact.mixin.Employee]: { $exists: true } }) + + const newSpaces = new Map, PersonSpace>() + const now = Date.now() + + for (const account of accounts) { + const employee = employees.find(({ _id }) => _id === account.person) + if (employee === undefined) continue + + const space = newSpaces.get(account.person) + + if (space !== undefined) { + space.members.push(account._id) + } else { + newSpaces.set(account.person, { + _id: generateId(), + _class: contact.class.PersonSpace, + space: core.space.Space, + name: 'Personal space', + description: '', + private: true, + archived: false, + members: [account._id], + person: account.person, + modifiedBy: core.account.System, + createdBy: core.account.System, + modifiedOn: now, + createdOn: now + }) + } + } + + await client.create(DOMAIN_SPACE, Array.from(newSpaces.values())) +} + export const contactOperation: MigrateOperation = { async migrate (client: MigrationClient, logger: ModelLogger): Promise { await tryMigrate(client, contactId, [ { state: 'employees', func: async (client) => { - await client.update( - DOMAIN_TX, - { - objectClass: 'contact:class:EmployeeAccount' - }, - { - $rename: { 'attributes.employee': 'attributes.person' }, - $set: { objectClass: contact.class.PersonAccount } - } - ) - await client.update( DOMAIN_TX, { @@ -251,6 +292,10 @@ export const contactOperation: MigrateOperation = { { $rename: { avatarKind: 'avatarType' } } ) } + }, + { + state: 'create-person-spaces-v1', + func: createPersonSpaces } ]) }, diff --git a/models/notification/package.json b/models/notification/package.json index 11ecb5b4d5..12dbe6f086 100644 --- a/models/notification/package.json +++ b/models/notification/package.json @@ -30,6 +30,7 @@ "dependencies": { "@hcengineering/activity": "^0.6.0", "@hcengineering/chunter": "^0.6.20", + "@hcengineering/contact": "^0.6.24", "@hcengineering/core": "^0.6.32", "@hcengineering/model": "^0.6.11", "@hcengineering/model-attachment": "^0.6.0", @@ -39,10 +40,10 @@ "@hcengineering/model-workbench": "^0.6.1", "@hcengineering/notification": "^0.6.23", "@hcengineering/platform": "^0.6.11", + "@hcengineering/preference": "^0.6.13", "@hcengineering/setting": "^0.6.17", "@hcengineering/ui": "^0.6.15", "@hcengineering/view": "^0.6.13", - "@hcengineering/workbench": "^0.6.16", - "@hcengineering/preference": "^0.6.13" + "@hcengineering/workbench": "^0.6.16" } } diff --git a/models/notification/src/index.ts b/models/notification/src/index.ts index bef317510f..04a2b8233c 100644 --- a/models/notification/src/index.ts +++ b/models/notification/src/index.ts @@ -49,6 +49,7 @@ import { UX, type Builder } from '@hcengineering/model' +import { type PersonSpace } from '@hcengineering/contact' import core, { TClass, TDoc } from '@hcengineering/model-core' import preference, { TPreference } from '@hcengineering/model-preference' import view, { createAction, template } from '@hcengineering/model-view' @@ -195,13 +196,18 @@ export class TDocNotifyContext extends TDoc implements DocNotifyContext { @Index(IndexKind.Indexed) user!: Ref - @Prop(TypeRef(core.class.Doc), core.string.AttachedTo) + @Prop(TypeRef(core.class.Doc), core.string.Object) @Index(IndexKind.Indexed) - attachedTo!: Ref + objectId!: Ref - @Prop(TypeRef(core.class.Class), core.string.AttachedToClass) + @Prop(TypeRef(core.class.Class), core.string.Class) @Index(IndexKind.Indexed) - attachedToClass!: Ref> + objectClass!: Ref> + + @Prop(TypeRef(core.class.Space), core.string.Space) + objectSpace!: Ref + + declare space: Ref @Prop(TypeDate(), core.string.Date) lastViewedTimestamp?: Timestamp @@ -230,6 +236,8 @@ export class TInboxNotification extends TDoc implements InboxNotification { @Prop(TypeBoolean(), core.string.Boolean) archived!: boolean + declare space: Ref + title?: IntlString body?: IntlString intlParams?: Record diff --git a/models/notification/src/migration.ts b/models/notification/src/migration.ts index f65cf386ab..5bfca5fb32 100644 --- a/models/notification/src/migration.ts +++ b/models/notification/src/migration.ts @@ -29,8 +29,10 @@ import notification, { type InboxNotification } from '@hcengineering/notification' import { DOMAIN_PREFERENCE } from '@hcengineering/preference' +import contact, { type PersonSpace } from '@hcengineering/contact' import { DOMAIN_DOC_NOTIFY, DOMAIN_NOTIFICATION, DOMAIN_USER_NOTIFY } from './index' +import { DOMAIN_SPACE } from '@hcengineering/model-core' export async function removeNotifications ( client: MigrationClient, @@ -74,6 +76,115 @@ export async function removeNotifications ( } } +export async function migrateNotificationsSpace (client: MigrationClient): Promise { + const personSpaces = await client.find(DOMAIN_SPACE, { _class: contact.class.PersonSpace }, {}) + + await client.update( + DOMAIN_DOC_NOTIFY, + { + _class: notification.class.DocNotifyContext, + objectSpace: { $exists: false } + }, + { $rename: { space: 'objectSpace' } } + ) + + for (const space of personSpaces) { + await client.update( + DOMAIN_DOC_NOTIFY, + { + _class: notification.class.DocNotifyContext, + user: { $in: space.members } + }, + { space: space._id } + ) + await client.update( + DOMAIN_NOTIFICATION, + { + _class: notification.class.ActivityInboxNotification, + user: { $in: space.members } + }, + { space: space._id } + ) + await client.update( + DOMAIN_NOTIFICATION, + { + _class: notification.class.CommonInboxNotification, + user: { $in: space.members } + }, + { space: space._id } + ) + await client.update( + DOMAIN_NOTIFICATION, + { + _class: notification.class.MentionInboxNotification, + user: { $in: space.members } + }, + { space: space._id } + ) + } + + await client.deleteMany(DOMAIN_DOC_NOTIFY, { space: { $nin: personSpaces.map(({ _id }) => _id) } }) + await client.deleteMany(DOMAIN_NOTIFICATION, { + _class: notification.class.ActivityInboxNotification, + space: { $nin: personSpaces.map(({ _id }) => _id) } + }) + await client.deleteMany(DOMAIN_NOTIFICATION, { + _class: notification.class.CommonInboxNotification, + space: { $nin: personSpaces.map(({ _id }) => _id) } + }) + await client.deleteMany(DOMAIN_NOTIFICATION, { + _class: notification.class.MentionInboxNotification, + space: { $nin: personSpaces.map(({ _id }) => _id) } + }) + + while (true) { + const contexts = await client.find( + DOMAIN_DOC_NOTIFY, + { + _class: notification.class.DocNotifyContext, + attachedTo: { $exists: true } + }, + { limit: 500 } + ) + + if (contexts.length === 0) { + break + } + + const classesOfSpace = new Set>>() + + for (const context of contexts) { + const _class = (context as any).attachedToClass + if (client.hierarchy.isDerived(_class, core.class.Space)) { + classesOfSpace.add(_class) + } + } + if (classesOfSpace.size > 0) { + await client.update( + DOMAIN_DOC_NOTIFY, + { objectClass: { $in: Array.from(classesOfSpace) } }, + { objectSpace: core.space.Space } + ) + await client.update( + DOMAIN_DOC_NOTIFY, + { objectClass: { $in: Array.from(classesOfSpace) } }, + { $rename: { attachedTo: 'objectId', attachedToClass: 'objectClass' } } + ) + } + await client.update( + DOMAIN_DOC_NOTIFY, + { + _class: notification.class.DocNotifyContext, + _id: { $in: contexts.map(({ _id }) => _id) } + }, + { $rename: { attachedTo: 'objectId', attachedToClass: 'objectClass' } } + ) + } + + await client.deleteMany(DOMAIN_NOTIFICATION, { _class: notification.class.BrowserNotification }) + await client.deleteMany(DOMAIN_USER_NOTIFY, { _class: notification.class.BrowserNotification }) +} + export async function migrateSettings (client: MigrationClient): Promise { await client.update( DOMAIN_PREFERENCE, @@ -197,8 +308,13 @@ export const notificationOperation: MigrateOperation = { { isPinned: false } ) } + }, + { + state: 'migrate-notifications-space-v1', + func: migrateNotificationsSpace } ]) + await client.deleteMany(DOMAIN_USER_NOTIFY, { _class: notification.class.BrowserNotification, status: NotificationStatus.Notified diff --git a/packages/ui/src/components/ListView.svelte b/packages/ui/src/components/ListView.svelte index 9bec3b4876..0cd7be0cc2 100644 --- a/packages/ui/src/components/ListView.svelte +++ b/packages/ui/src/components/ListView.svelte @@ -26,6 +26,7 @@ export let colorsSchema: 'default' | 'lumia' = 'default' export let updateOnMouse = true export let lazy = false + export let minHeight: string | null = null export let highlightIndex: number | undefined = undefined const getKey: (index: number) => string = (index) => index.toString() @@ -76,35 +77,37 @@ > {#each Array(count) as _, row (getKey(row))} {#if lazy} - - dispatch('click', row)} - on:mouseover={mouseAttractor(() => { - if (updateOnMouse) { - onRow(row) - } - })} - on:mouseenter={mouseAttractor(() => { - if (updateOnMouse) { - onRow(row) - } - })} - > - - - - - - - - +
+ + dispatch('click', row)} + on:mouseover={mouseAttractor(() => { + if (updateOnMouse) { + onRow(row) + } + })} + on:mouseenter={mouseAttractor(() => { + if (updateOnMouse) { + onRow(row) + } + })} + > + + + + + + + + +
{:else} ).references ?? 0) > 0 diff --git a/plugins/chunter-resources/src/components/chat/ChatAside.svelte b/plugins/chunter-resources/src/components/chat/ChatAside.svelte index b27f58598f..9e3f94c7f5 100644 --- a/plugins/chunter-resources/src/components/chat/ChatAside.svelte +++ b/plugins/chunter-resources/src/components/chat/ChatAside.svelte @@ -36,7 +36,7 @@ $: threadId = context ? undefined : (_id as Ref) $: context && - objectQuery.query(context.attachedToClass, { _id: context.attachedTo }, (res) => { + objectQuery.query(context.objectClass, { _id: context.objectId }, (res) => { ;[object] = res }) diff --git a/plugins/chunter-resources/src/components/chat/create/CreateChannel.svelte b/plugins/chunter-resources/src/components/chat/create/CreateChannel.svelte index 9d48cb3d22..ca7cdddffe 100644 --- a/plugins/chunter-resources/src/components/chat/create/CreateChannel.svelte +++ b/plugins/chunter-resources/src/components/chat/create/CreateChannel.svelte @@ -18,6 +18,7 @@ import presentation, { getClient } from '@hcengineering/presentation' import core, { getCurrentAccount } from '@hcengineering/core' import notification from '@hcengineering/notification' + import contact, { PersonAccount } from '@hcengineering/contact' import Lock from '../../icons/Lock.svelte' import chunter from '../../../plugin' @@ -51,19 +52,27 @@ $: canSave = !!channelName async function save (): Promise { - const accountId = getCurrentAccount()._id + const account = getCurrentAccount() as PersonAccount + const space = await client.findOne( + contact.class.PersonSpace, + { person: account.person }, + { projection: { _id: 1 } } + ) + if (!space) return const channelId = await client.createDoc(chunter.class.Channel, core.space.Space, { name: channelName, description: '', private: selectedVisibilityId === 'private', archived: false, - members: [accountId], + members: [account._id], topic: description }) - await client.createDoc(notification.class.DocNotifyContext, channelId, { - user: accountId, - attachedTo: channelId, - attachedToClass: chunter.class.Channel, + + await client.createDoc(notification.class.DocNotifyContext, space._id, { + user: account._id, + objectId: channelId, + objectClass: chunter.class.Channel, + objectSpace: core.space.Space, isPinned: false }) diff --git a/plugins/chunter-resources/src/components/chat/create/CreateDirectChat.svelte b/plugins/chunter-resources/src/components/chat/create/CreateDirectChat.svelte index 8bcf323547..08bcb2400b 100644 --- a/plugins/chunter-resources/src/components/chat/create/CreateDirectChat.svelte +++ b/plugins/chunter-resources/src/components/chat/create/CreateDirectChat.svelte @@ -31,7 +31,7 @@ const dispatch = createEventDispatcher() const client = getClient() - const myAccId = getCurrentAccount()._id + const myAcc = getCurrentAccount() as PersonAccount const query = createQuery() let employeeIds: Ref[] = [] @@ -52,7 +52,7 @@ async function createDirectMessage (): Promise { const employeeAccounts = await client.findAll(contact.class.PersonAccount, { person: { $in: employeeIds } }) - const accIds = [myAccId, ...employeeAccounts.filter(({ _id }) => _id !== myAccId).map(({ _id }) => _id)].sort() + const accIds = [myAcc._id, ...employeeAccounts.filter(({ _id }) => _id !== myAcc._id).map(({ _id }) => _id)].sort() const existingDms = await client.findAll(chunter.class.DirectMessage, {}) @@ -75,9 +75,9 @@ })) const context = await client.findOne(notification.class.DocNotifyContext, { - user: myAccId, - attachedTo: dmId, - attachedToClass: chunter.class.DirectMessage + person: myAcc.person, + objectId: dmId, + objectClass: chunter.class.DirectMessage }) if (context !== undefined) { @@ -86,10 +86,13 @@ return } - await client.createDoc(notification.class.DocNotifyContext, dmId, { - user: myAccId, - attachedTo: dmId, - attachedToClass: chunter.class.DirectMessage, + const space = await client.findOne(contact.class.PersonSpace, { person: myAcc.person }, { projection: { _id: 1 } }) + if (!space) return + await client.createDoc(notification.class.DocNotifyContext, space._id, { + user: myAcc._id, + objectId: dmId, + objectClass: chunter.class.DirectMessage, + objectSpace: core.space.Space, isPinned: false }) diff --git a/plugins/chunter-resources/src/components/chat/navigator/ChatNavGroup.svelte b/plugins/chunter-resources/src/components/chat/navigator/ChatNavGroup.svelte index 33de29497f..a524e646bc 100644 --- a/plugins/chunter-resources/src/components/chat/navigator/ChatNavGroup.svelte +++ b/plugins/chunter-resources/src/components/chat/navigator/ChatNavGroup.svelte @@ -50,11 +50,11 @@ let sections: Section[] = [] - $: contexts = $contextsStore.filter(({ attachedToClass, isPinned }) => { + $: contexts = $contextsStore.filter(({ objectClass, isPinned }) => { if (model.isPinned !== isPinned) return false - if (model._class !== undefined && model._class !== attachedToClass) return false - if (model.skipClasses !== undefined && model.skipClasses.includes(attachedToClass)) return false - if (hierarchy.classHierarchyMixin(attachedToClass, activity.mixin.ActivityDoc) === undefined) return false + if (model._class !== undefined && model._class !== objectClass) return false + if (model.skipClasses !== undefined && model.skipClasses.includes(objectClass)) return false + if (hierarchy.classHierarchyMixin(objectClass, activity.mixin.ActivityDoc) === undefined) return false return true }) @@ -72,10 +72,10 @@ object !== undefined && getObjectGroup(object) === model.id && !$contextByDocStore.has(object._id) function loadObjects (contexts: DocNotifyContext[]): void { - const contextsByClass = groupByArray(contexts, ({ attachedToClass }) => attachedToClass) + const contextsByClass = groupByArray(contexts, ({ objectClass }) => objectClass) for (const [_class, ctx] of contextsByClass.entries()) { - const ids = ctx.map(({ attachedTo }) => attachedTo) + const ids = ctx.map(({ objectId }) => objectId) const { query, limit } = objectsQueryByClass.get(_class) ?? { query: createQuery(), limit: hierarchy.isDerived(_class, chunter.class.ChunterSpace) ? -1 : model.maxSectionItems ?? 5 @@ -187,7 +187,7 @@ if (_class === undefined) { return model.getActionsFn(contexts) } else { - return model.getActionsFn(contexts.filter(({ attachedToClass }) => attachedToClass === _class)) + return model.getActionsFn(contexts.filter(({ objectClass }) => objectClass === _class)) } } diff --git a/plugins/chunter-resources/src/components/chat/navigator/ChatNavSection.svelte b/plugins/chunter-resources/src/components/chat/navigator/ChatNavSection.svelte index 9dd3f67b76..5e35179cad 100644 --- a/plugins/chunter-resources/src/components/chat/navigator/ChatNavSection.svelte +++ b/plugins/chunter-resources/src/components/chat/navigator/ChatNavSection.svelte @@ -120,7 +120,7 @@ noDivider > {#each sortedItems as item (item.id)} - {@const context = contexts.find(({ attachedTo }) => attachedTo === item.id)} + {@const context = contexts.find(({ objectId }) => objectId === item.id)} {/each} {#if canShowMore} @@ -130,7 +130,7 @@ {/if} {#if visibleItem !== undefined && !isOpen} - {@const context = contexts.find(({ attachedTo }) => attachedTo === visibleItem?.id)} + {@const context = contexts.find(({ objectId }) => objectId === visibleItem?.id)} {/if} diff --git a/plugins/chunter-resources/src/components/chat/navigator/ChatNavigator.svelte b/plugins/chunter-resources/src/components/chat/navigator/ChatNavigator.svelte index f7d6ae1663..42049ec6ea 100644 --- a/plugins/chunter-resources/src/components/chat/navigator/ChatNavigator.svelte +++ b/plugins/chunter-resources/src/components/chat/navigator/ChatNavigator.svelte @@ -13,7 +13,7 @@ // limitations under the License. -->
{#if iconMixin && object} - {:else if !iconMixin} - + {:else} + {/if}
diff --git a/plugins/notification-resources/src/components/inbox/Inbox.svelte b/plugins/notification-resources/src/components/inbox/Inbox.svelte index 0d6004c343..ca8e3624d4 100644 --- a/plugins/notification-resources/src/components/inbox/Inbox.svelte +++ b/plugins/notification-resources/src/components/inbox/Inbox.svelte @@ -188,7 +188,7 @@ async function updateTabItems (inboxData: InboxData, notifyContexts: DocNotifyContext[]): Promise { const displayClasses = new Set( - notifyContexts.filter(({ _id }) => inboxData.has(_id)).map(({ attachedToClass }) => attachedToClass) + notifyContexts.filter(({ _id }) => inboxData.has(_id)).map(({ objectClass }) => objectClass) ) const classes = Array.from(displayClasses) @@ -249,8 +249,8 @@ return } - const isChunterChannel = hierarchy.isDerived(selectedContext.attachedToClass, chunter.class.ChunterSpace) - const panelComponent = hierarchy.classHierarchyMixin(selectedContext.attachedToClass, view.mixin.ObjectPanel) + const isChunterChannel = hierarchy.isDerived(selectedContext.objectClass, chunter.class.ChunterSpace) + const panelComponent = hierarchy.classHierarchyMixin(selectedContext.objectClass, view.mixin.ObjectPanel) selectedComponent = panelComponent?.component ?? view.component.EditDoc @@ -315,10 +315,10 @@ if ( selectedTabId === activity.class.ActivityMessage && - hierarchy.isDerived(context.attachedToClass, activity.class.ActivityMessage) + hierarchy.isDerived(context.objectClass, activity.class.ActivityMessage) ) { result.set(key, resNotifications) - } else if (context.attachedToClass === selectedTabId) { + } else if (context.objectClass === selectedTabId) { result.set(key, resNotifications) } } @@ -411,8 +411,8 @@ context === selectedContext)} noScroll + minHeight="5.625rem" kind="full-size" colorsSchema="lumia" lazy={true} diff --git a/plugins/notification-resources/src/inboxNotificationsClient.ts b/plugins/notification-resources/src/inboxNotificationsClient.ts index dce208794e..206f9b376b 100644 --- a/plugins/notification-resources/src/inboxNotificationsClient.ts +++ b/plugins/notification-resources/src/inboxNotificationsClient.ts @@ -90,6 +90,10 @@ export class InboxNotificationsClientImpl implements InboxNotificationsClient { private _contextByDoc = new Map, DocNotifyContext>() private constructor () { + void this.init() + } + + private async init (): Promise { this.contextsQuery.query( notification.class.DocNotifyContext, { @@ -97,7 +101,7 @@ export class InboxNotificationsClientImpl implements InboxNotificationsClient { }, (result: DocNotifyContext[]) => { this.contexts.set(result) - this._contextByDoc = new Map(result.map((updates) => [updates.attachedTo, updates])) + this._contextByDoc = new Map(result.map((updates) => [updates.objectId, updates])) this.contextByDoc.set(this._contextByDoc) } ) diff --git a/plugins/notification-resources/src/utils.ts b/plugins/notification-resources/src/utils.ts index 1e9b597515..4fd9a7ea6a 100644 --- a/plugins/notification-resources/src/utils.ts +++ b/plugins/notification-resources/src/utils.ts @@ -273,9 +273,9 @@ async function updateMeInCollaborators ( /** * @public */ -export async function unsubscribe (object: DocNotifyContext): Promise { +export async function unsubscribe (context: DocNotifyContext): Promise { const client = getClient() - await updateMeInCollaborators(client, object.attachedToClass, object.attachedTo, OpWithMe.Remove) + await updateMeInCollaborators(client, context.objectClass, context.objectId, OpWithMe.Remove) } /** @@ -586,50 +586,39 @@ export async function selectInboxContext ( ): Promise { const client = getClient() const hierarchy = client.getHierarchy() + const { objectId, objectClass } = context if (isMentionNotification(notification) && isActivityMessageClass(notification.mentionedInClass)) { const selectedMsg = notification.mentionedIn as Ref void navigateToInboxDoc( linkProviders, - context.attachedTo, - context.attachedToClass, - isActivityMessageClass(context.attachedToClass) ? (context.attachedTo as Ref) : undefined, + objectId, + objectClass, + isActivityMessageClass(objectClass) ? (objectId as Ref) : undefined, selectedMsg ) return } - if (hierarchy.isDerived(context.attachedToClass, activity.class.ActivityMessage)) { + if (hierarchy.isDerived(objectClass, activity.class.ActivityMessage)) { const message = (notification as WithLookup)?.$lookup?.attachedTo - if (context.attachedToClass === chunter.class.ThreadMessage) { + if (objectClass === chunter.class.ThreadMessage) { const thread = await client.findOne( chunter.class.ThreadMessage, { - _id: context.attachedTo as Ref + _id: objectId as Ref }, { projection: { _id: 1, attachedTo: 1 } } ) - void navigateToInboxDoc( - linkProviders, - context.attachedTo, - context.attachedToClass, - thread?.attachedTo, - thread?._id - ) + void navigateToInboxDoc(linkProviders, objectId, objectClass, thread?.attachedTo, thread?._id) return } if (isReactionMessage(message)) { - void navigateToInboxDoc( - linkProviders, - context.attachedTo, - context.attachedToClass, - undefined, - context.attachedTo as Ref - ) + void navigateToInboxDoc(linkProviders, objectId, objectClass, undefined, objectId as Ref) return } @@ -637,18 +626,18 @@ export async function selectInboxContext ( void navigateToInboxDoc( linkProviders, - context.attachedTo, - context.attachedToClass, - selectedMsg !== undefined ? (context.attachedTo as Ref) : undefined, - selectedMsg ?? (context.attachedTo as Ref) + objectId, + objectClass, + selectedMsg !== undefined ? (objectId as Ref) : undefined, + selectedMsg ?? (objectId as Ref) ) return } void navigateToInboxDoc( linkProviders, - context.attachedTo, - context.attachedToClass, + objectId, + objectClass, undefined, (notification as ActivityInboxNotification)?.attachedTo ) diff --git a/plugins/notification/package.json b/plugins/notification/package.json index 31d57b7d21..8c29582966 100644 --- a/plugins/notification/package.json +++ b/plugins/notification/package.json @@ -40,6 +40,7 @@ }, "dependencies": { "@hcengineering/activity": "^0.6.0", + "@hcengineering/contact": "^0.6.24", "@hcengineering/core": "^0.6.32", "@hcengineering/platform": "^0.6.11", "@hcengineering/preference": "^0.6.13", diff --git a/plugins/notification/src/index.ts b/plugins/notification/src/index.ts index 978fff22d7..393a9b8260 100644 --- a/plugins/notification/src/index.ts +++ b/plugins/notification/src/index.ts @@ -24,6 +24,7 @@ import { Markup, Mixin, Ref, + Space, Timestamp, Tx, TxOperations @@ -34,6 +35,8 @@ import { Preference } from '@hcengineering/preference' import { IntegrationType } from '@hcengineering/setting' import { AnyComponent, Location, ResolvedLocation } from '@hcengineering/ui' import { Action } from '@hcengineering/view' +import { PersonSpace } from '@hcengineering/contact' + import { Readable, Writable } from './types' export * from './types' @@ -226,7 +229,7 @@ export interface NotificationContextPresenter extends Class { /** * @public */ -export interface InboxNotification extends Doc { +export interface InboxNotification extends Doc { user: Ref isViewed: boolean @@ -273,12 +276,12 @@ export type DisplayInboxNotification = DisplayActivityInboxNotification | InboxN /** * @public */ -export interface DocNotifyContext extends Doc { +export interface DocNotifyContext extends Doc { user: Ref - // Context - attachedTo: Ref - attachedToClass: Ref> + objectId: Ref + objectClass: Ref> + objectSpace: Ref isPinned: boolean lastViewedTimestamp?: Timestamp @@ -423,7 +426,8 @@ const notification = plugin(notificationId, { CommonNotificationCollectionAdded: '' as IntlString, CommonNotificationCollectionRemoved: '' as IntlString, SoundNotificationsDescription: '' as IntlString, - Sound: '' as IntlString + Sound: '' as IntlString, + NoAccessToObject: '' as IntlString }, function: { Notify: '' as Resource, diff --git a/server-plugins/activity-resources/src/references.ts b/server-plugins/activity-resources/src/references.ts index f2021a96cc..407141e9c8 100644 --- a/server-plugins/activity-resources/src/references.ts +++ b/server-plugins/activity-resources/src/references.ts @@ -46,7 +46,7 @@ import { } from '@hcengineering/text' import { StorageAdapter, TriggerControl } from '@hcengineering/server-core' import activity, { ActivityMessage, ActivityReference, UserMentionInfo } from '@hcengineering/activity' -import contact, { Person, PersonAccount } from '@hcengineering/contact' +import contact, { Employee, Person, PersonAccount } from '@hcengineering/contact' import { getCommonNotificationTxes, getPushCollaboratorTx, @@ -54,7 +54,8 @@ import { isShouldNotifyTx, NotifyResult, applyNotificationProviders, - getNotificationContent + getNotificationContent, + toReceiverInfo } from '@hcengineering/server-notification-resources' async function getPersonAccount (person: Ref, control: TriggerControl): Promise { @@ -118,6 +119,14 @@ export async function getPersonNotificationTxes ( const doc = (await control.findAll(reference.srcDocClass, { _id: reference.srcDocId }))[0] + const receiverPerson = ( + await control.findAll(contact.mixin.Employee, { _id: receiver.person as Ref, active: true }, { limit: 1 }) + )[0] + if (receiverPerson === undefined) return res + + const receiverSpace = (await control.findAll(contact.class.PersonSpace, { person: receiver.person }, { limit: 1 }))[0] + if (receiverSpace === undefined) return res + const collaboratorsTx = await getCollaboratorsTxes(reference, control, receiver, doc) res.push(...collaboratorsTx) @@ -164,17 +173,19 @@ export async function getPersonNotificationTxes ( const sender = ( await control.modelDb.findAll(contact.class.PersonAccount, { _id: senderId as Ref }, { limit: 1 }) )[0] - const receiverPerson = (await control.findAll(contact.class.Person, { _id: receiver.person }, { limit: 1 }))[0] + const senderPerson = sender !== undefined ? (await control.findAll(contact.class.Person, { _id: sender.person }, { limit: 1 }))[0] : undefined - const receiverInfo = { + const receiverInfo = toReceiverInfo(control.hierarchy, { _id: receiver._id, account: receiver, - person: receiverPerson - } + person: receiverPerson, + space: receiverSpace._id + }) + if (receiverInfo === undefined) return res const senderInfo = { _id: senderId, @@ -200,7 +211,7 @@ export async function getPersonNotificationTxes ( senderInfo, reference.srcDocId, reference.srcDocClass, - space, + doc.space, originTx.modifiedOn, notifyResult, notification.class.MentionInboxNotification @@ -210,7 +221,7 @@ export async function getPersonNotificationTxes ( const context = ( await control.findAll( notification.class.DocNotifyContext, - { attachedTo: reference.srcDocId, user: receiver._id }, + { objectId: reference.srcDocId, user: receiver._id }, { projection: { _id: 1 } } ) )[0] @@ -222,7 +233,7 @@ export async function getPersonNotificationTxes ( docNotifyContext: context._id, _id: generateId(), _class: notification.class.CommonInboxNotification, - space, + space: receiverSpace._id, modifiedOn: originTx.modifiedOn, modifiedBy: sender._id } diff --git a/server-plugins/chunter-resources/src/index.ts b/server-plugins/chunter-resources/src/index.ts index 13ae584fc8..5ef6cfbbd0 100644 --- a/server-plugins/chunter-resources/src/index.ts +++ b/server-plugins/chunter-resources/src/index.ts @@ -394,7 +394,7 @@ async function OnChannelMembersChanged (tx: TxUpdateDoc, control: Trigg const removed = combineAttributes([tx.operations], 'members', '$pull', '$in') const res: Tx[] = [] - const allContexts = await control.findAll(notification.class.DocNotifyContext, { attachedTo: tx.objectId }) + const allContexts = await control.findAll(notification.class.DocNotifyContext, { objectId: tx.objectId }) if (removed.length > 0) { res.push( @@ -416,13 +416,25 @@ async function OnChannelMembersChanged (tx: TxUpdateDoc, control: Trigg ) } + const accounts = + added.length > 0 ? await control.modelDb.findAll(contact.class.PersonAccount, { _id: { $in: added } }) : [] + const spaces = + accounts.length > 0 + ? await control.findAll(contact.class.PersonSpace, { person: { $in: accounts.map((x) => x.person) } }) + : [] + for (const addedMember of added) { const context = allContexts.find(({ user }) => user === addedMember) if (context === undefined) { - const createTx = control.txFactory.createTxCreateDoc(notification.class.DocNotifyContext, tx.objectSpace, { - attachedTo: tx.objectId, - attachedToClass: tx.objectClass, + const account = accounts.find(({ _id }) => _id === addedMember) + if (account === undefined) continue + const space = spaces.find(({ person }) => person === account.person) + if (space === undefined) continue + const createTx = control.txFactory.createTxCreateDoc(notification.class.DocNotifyContext, space._id, { + objectId: tx.objectId, + objectClass: tx.objectClass, + objectSpace: tx.objectSpace, user: addedMember, lastViewedTimestamp: tx.modifiedOn, isPinned: false @@ -563,14 +575,14 @@ export async function updateChatInfo (control: TriggerControl, status: UserStatu const { hierarchy } = control const res: Tx[] = [] - const directContexts = contexts.filter(({ attachedToClass }) => - hierarchy.isDerived(attachedToClass, chunter.class.DirectMessage) + const directContexts = contexts.filter(({ objectClass }) => + hierarchy.isDerived(objectClass, chunter.class.DirectMessage) ) const activityContexts = contexts.filter( - ({ attachedToClass }) => - !hierarchy.isDerived(attachedToClass, chunter.class.DirectMessage) && - !hierarchy.isDerived(attachedToClass, chunter.class.Channel) && - !hierarchy.isDerived(attachedToClass, chunter.class.Channel) + ({ objectClass }) => + !hierarchy.isDerived(objectClass, chunter.class.DirectMessage) && + !hierarchy.isDerived(objectClass, chunter.class.Channel) && + !hierarchy.isDerived(objectClass, chunter.class.Channel) ) const directTxes = await hideOldDirects(directContexts, control, date) diff --git a/server-plugins/contact-resources/src/index.ts b/server-plugins/contact-resources/src/index.ts index d42140bf38..836648cc4f 100644 --- a/server-plugins/contact-resources/src/index.ts +++ b/server-plugins/contact-resources/src/index.ts @@ -26,7 +26,8 @@ import contact, { formatName, getFirstName, getLastName, - getName + getName, + PersonSpace } from '@hcengineering/contact' import core, { Account, @@ -41,7 +42,8 @@ import core, { TxProcessor, TxRemoveDoc, TxUpdateDoc, - concatLink + concatLink, + TxCUD } from '@hcengineering/core' import notification, { Collaborators } from '@hcengineering/notification' import { getMetadata } from '@hcengineering/platform' @@ -87,6 +89,10 @@ export async function OnEmployeeCreate (tx: Tx, control: TriggerControl): Promis if (acc === undefined) return [] const spaces = await control.findAll(core.class.Space, { autoJoin: true }) const result: Tx[] = [] + + const txes = await createPersonSpace(acc._id, mixinTx.objectId, control) + result.push(...txes) + for (const space of spaces) { if (space.members.includes(acc._id)) continue const pushTx = control.txFactory.createTxUpdateDoc(space._class, space.space, space._id, { @@ -99,6 +105,35 @@ export async function OnEmployeeCreate (tx: Tx, control: TriggerControl): Promis return result } +async function createPersonSpace ( + account: Ref, + person: Ref, + control: TriggerControl +): Promise[]> { + const personSpace = (await control.findAll(contact.class.PersonSpace, { person }, { limit: 1 })).shift() + if (personSpace !== undefined) { + if (personSpace.members.includes(account)) return [] + return [ + control.txFactory.createTxUpdateDoc(personSpace._class, personSpace.space, personSpace._id, { + $push: { + members: account + } + }) + ] + } + + return [ + control.txFactory.createTxCreateDoc(contact.class.PersonSpace, core.space.Space, { + name: 'Personal space', + description: '', + private: true, + archived: false, + person, + members: [account] + }) + ] +} + export async function OnPersonAccountCreate (tx: Tx, control: TriggerControl): Promise { const acc = TxProcessor.createDoc2Doc(tx as TxCreateDoc) const person = ( @@ -106,7 +141,12 @@ export async function OnPersonAccountCreate (tx: Tx, control: TriggerControl): P )[0] if (person === undefined) return [] const spaces = await control.findAll(core.class.Space, { autoJoin: true }) + const result: Tx[] = [] + const txes = await createPersonSpace(acc._id, person._id, control) + + result.push(...txes) + for (const space of spaces) { if (space.members.includes(acc._id)) continue const pushTx = control.txFactory.createTxUpdateDoc(space._class, space.space, space._id, { diff --git a/server-plugins/gmail-resources/src/index.ts b/server-plugins/gmail-resources/src/index.ts index e22563d6fe..8ad1e0d1d6 100644 --- a/server-plugins/gmail-resources/src/index.ts +++ b/server-plugins/gmail-resources/src/index.ts @@ -30,8 +30,12 @@ import { } from '@hcengineering/core' import gmail, { Message } from '@hcengineering/gmail' import { TriggerControl } from '@hcengineering/server-core' -import notification, { BaseNotificationType, InboxNotification, NotificationType } from '@hcengineering/notification' -import serverNotification, { NotificationProviderFunc, UserInfo } from '@hcengineering/server-notification' +import { BaseNotificationType, InboxNotification, NotificationType } from '@hcengineering/notification' +import serverNotification, { + NotificationProviderFunc, + ReceiverInfo, + SenderInfo +} from '@hcengineering/server-notification' import { getContentByTemplate } from '@hcengineering/server-notification-resources' import { getMetadata } from '@hcengineering/platform' @@ -74,47 +78,6 @@ export async function OnMessageCreate (tx: Tx, control: TriggerControl): Promise }) res.push(tx) } - if (message.incoming) { - const docs = await control.findAll(notification.class.DocNotifyContext, { - attachedTo: channel._id, - user: message.modifiedBy - }) - for (const doc of docs) { - // TODO: push inbox notification - // res.push( - // control.txFactory.createTxUpdateDoc(doc._class, doc.space, doc._id, { - // $push: { - // txes: { - // _id: tx._id as Ref>, - // modifiedOn: tx.modifiedOn, - // modifiedBy: tx.modifiedBy, - // isNew: true - // } - // } - // }) - // ) - res.push( - control.txFactory.createTxUpdateDoc(doc._class, doc.space, doc._id, { - lastUpdateTimestamp: tx.modifiedOn - }) - ) - } - if (docs.length === 0) { - res.push( - control.txFactory.createTxCreateDoc(notification.class.DocNotifyContext, channel.space, { - user: tx.modifiedBy, - attachedTo: channel._id, - attachedToClass: channel._class, - lastUpdateTimestamp: tx.modifiedOn, - isPinned: false - // TODO: push inbox notification - // txes: [ - // { _id: tx._id as Ref>, modifiedOn: tx.modifiedOn, modifiedBy: tx.modifiedBy, isNew: true } - // ] - }) - ) - } - } } return res @@ -167,8 +130,8 @@ async function notifyByEmail ( control: TriggerControl, type: Ref, doc: Doc | undefined, - sender: UserInfo, - receiver: UserInfo, + sender: SenderInfo, + receiver: ReceiverInfo, data: InboxNotification ): Promise { const account = receiver.account @@ -192,26 +155,14 @@ const SendEmailNotifications: NotificationProviderFunc = async ( types: BaseNotificationType[], object: Doc, data: InboxNotification, - receiver: UserInfo, - sender: UserInfo + receiver: ReceiverInfo, + sender: SenderInfo ): Promise => { if (types.length === 0) { return [] } - if (receiver.person === undefined) { - return [] - } - - const isEmployee = control.hierarchy.hasMixin(receiver.person, contact.mixin.Employee) - - if (!isEmployee) { - return [] - } - - const employee = control.hierarchy.as(receiver.person, contact.mixin.Employee) - - if (!employee.active) { + if (!receiver.person.active) { return [] } diff --git a/server-plugins/notification-resources/src/index.ts b/server-plugins/notification-resources/src/index.ts index a05d672037..e6a2b77002 100644 --- a/server-plugins/notification-resources/src/index.ts +++ b/server-plugins/notification-resources/src/index.ts @@ -76,7 +76,8 @@ import serverNotification, { getPersonAccountById, NOTIFICATION_BODY_SIZE, NOTIFICATION_TITLE_SIZE, - UserInfo + ReceiverInfo, + SenderInfo } from '@hcengineering/server-notification' import serverView from '@hcengineering/server-view' import { stripTags } from '@hcengineering/text' @@ -96,6 +97,7 @@ import { isUserEmployeeInFieldValue, isUserInFieldValue, replaceAll, + toReceiverInfo, updateNotifyContextsSpace } from './utils' @@ -121,8 +123,8 @@ export async function getCommonNotificationTxes ( control: TriggerControl, doc: Doc, data: Partial>, - receiver: UserInfo, - sender: UserInfo, + receiver: ReceiverInfo, + sender: SenderInfo, attachedTo: Ref, attachedToClass: Ref>, space: Ref, @@ -135,7 +137,7 @@ export async function getCommonNotificationTxes ( } const res: Tx[] = [] - const notifyContexts = await control.findAll(notification.class.DocNotifyContext, { attachedTo }) + const notifyContexts = await control.findAll(notification.class.DocNotifyContext, { objectId: attachedTo }) const notificationTx = await pushInboxNotifications( control, @@ -306,70 +308,38 @@ export async function getDocCollaborators ( return Array.from(collaborators.values()) } -function getDocNotifyContext ( - docNotifyContexts: DocNotifyContext[], - targetUser: Ref, - attachedTo: Ref, - res: Tx[] -): DocNotifyContext | undefined { - const context = docNotifyContexts.find((context) => context.user === targetUser && context.attachedTo === attachedTo) - - if (context !== undefined) { - return context - } - - const contextTx = (res as TxCUD[]).find((tx) => { - if (tx._class === core.class.TxCreateDoc && tx.objectClass === notification.class.DocNotifyContext) { - const createTx = tx as TxCreateDoc - - return createTx.attributes.attachedTo === attachedTo && createTx.attributes.user === targetUser - } - - return false - }) as TxCreateDoc | undefined - - if (contextTx !== undefined) { - return TxProcessor.createDoc2Doc(contextTx) - } - - return undefined -} - export async function pushInboxNotifications ( control: TriggerControl, res: Tx[], - target: UserInfo, - attachedTo: Ref, - attachedToClass: Ref>, - space: Ref, + receiver: ReceiverInfo, + objectId: Ref, + objectClass: Ref>, + objectSpace: Ref, contexts: DocNotifyContext[], data: Partial>, _class: Ref>, modifiedOn: Timestamp, shouldUpdateTimestamp = true ): Promise | undefined> { - const account = target.account - - if (account === undefined) { - return - } - const context = getDocNotifyContext(contexts, account._id, attachedTo, res) + const account = receiver.account + const context = contexts.find((context) => context.user === receiver._id && context.objectId === objectId) let docNotifyContextId: Ref if (context === undefined) { - const createContextTx = control.txFactory.createTxCreateDoc(notification.class.DocNotifyContext, space, { - user: account._id, - attachedTo, - attachedToClass, - lastUpdateTimestamp: shouldUpdateTimestamp ? modifiedOn : undefined, - isPinned: false + const createContextTx = control.txFactory.createTxCreateDoc(notification.class.DocNotifyContext, receiver.space, { + user: receiver._id, + objectId, + objectClass, + objectSpace, + isPinned: false, + lastUpdateTimestamp: shouldUpdateTimestamp ? modifiedOn : undefined }) await control.apply([createContextTx]) - if (target.account?.email !== undefined) { + if (receiver.account?.email !== undefined) { control.operationContext.derived.targets['docNotifyContext' + createContextTx._id] = (it) => { if (it._id === createContextTx._id) { - return [target.account?.email as string] + return [receiver.account?.email] } } } @@ -385,7 +355,7 @@ export async function pushInboxNotifications ( archived: false, ...data } - const notificationTx = control.txFactory.createTxCreateDoc(_class, space, notificationData) + const notificationTx = control.txFactory.createTxCreateDoc(_class, receiver.space, notificationData) res.push(notificationTx) return notificationTx @@ -490,12 +460,12 @@ export async function getTranslatedNotificationContent ( export async function createPushFromInbox ( control: TriggerControl, - target: UserInfo, + receiver: ReceiverInfo, attachedTo: Ref, attachedToClass: Ref>, data: Data, _class: Ref>, - sender: UserInfo, + sender: SenderInfo, _id: Ref, cache: Map, Doc> = new Map, Doc>() ): Promise { @@ -524,9 +494,9 @@ export async function createPushFromInbox ( } const path = [workbenchId, control.workspace.workspaceUrl, notificationId, encodeObjectURI(id, attachedToClass)] - await createPushNotification(control, target._id as Ref, title, body, _id, senderPerson, path) - return control.txFactory.createTxCreateDoc(notification.class.BrowserNotification, core.space.Workspace, { - user: target._id, + await createPushNotification(control, receiver._id as Ref, title, body, _id, senderPerson, path) + return control.txFactory.createTxCreateDoc(notification.class.BrowserNotification, receiver.space, { + user: receiver._id, status: NotificationStatus.New, title, body, @@ -606,18 +576,14 @@ export async function pushActivityInboxNotifications ( originTx: TxCUD, control: TriggerControl, res: Tx[], - target: UserInfo, - sender: UserInfo, + receiver: ReceiverInfo, + sender: SenderInfo, object: Doc, docNotifyContexts: DocNotifyContext[], activityMessage: ActivityMessage, shouldUpdateTimestamp: boolean ): Promise | undefined> { - if (target.account === undefined) { - return - } - - const content = await getNotificationContent(originTx, target.account, sender, object, control, activityMessage) + const content = await getNotificationContent(originTx, receiver.account, sender, object, control, activityMessage) const data: Partial> = { ...content, attachedTo: activityMessage._id, @@ -627,10 +593,10 @@ export async function pushActivityInboxNotifications ( return await pushInboxNotifications( control, res, - target, + receiver, activityMessage.attachedTo, activityMessage.attachedToClass, - activityMessage.space, + object.space, docNotifyContexts, data, notification.class.ActivityInboxNotification, @@ -647,8 +613,8 @@ export async function applyNotificationProviders ( control: TriggerControl, res: Tx[], object: Doc, - receiver: UserInfo, - sender: UserInfo + receiver: ReceiverInfo, + sender: SenderInfo ): Promise { const resources = await control.modelDb.findAll(serverNotification.class.NotificationProviderResources, {}) for (const [provider, types] of notifyResult.entries()) { @@ -690,8 +656,8 @@ export async function getNotificationTxes ( object: Doc, tx: TxCUD, originTx: TxCUD, - receiver: UserInfo, - sender: UserInfo, + receiver: ReceiverInfo, + sender: SenderInfo, params: NotifyParams, docNotifyContexts: DocNotifyContext[], activityMessages: ActivityMessage[] @@ -805,7 +771,7 @@ export async function createCollabDocInfo ( return res } - const notifyContexts = await control.findAllCtx(ctx, notification.class.DocNotifyContext, { attachedTo: object._id }) + const notifyContexts = await control.findAllCtx(ctx, notification.class.DocNotifyContext, { objectId: object._id }) await updateContextsTimestamp(notifyContexts, originTx.modifiedOn, control, originTx.modifiedBy) @@ -833,12 +799,15 @@ export async function createCollabDocInfo ( {}, async (ctx) => await getUsersInfo(ctx, [...Array.from(targets), originTx.modifiedBy as Ref], control) ) - const sender = usersInfo.find(({ _id }) => _id === originTx.modifiedBy) ?? { + const sender: SenderInfo = usersInfo.find(({ _id }) => _id === originTx.modifiedBy) ?? { _id: originTx.modifiedBy } for (const target of targets) { - const info = usersInfo.find(({ _id }) => _id === target) + const info: ReceiverInfo | undefined = toReceiverInfo( + control.hierarchy, + usersInfo.find(({ _id }) => _id === target) + ) if (info === undefined) continue @@ -858,7 +827,7 @@ export async function createCollabDocInfo ( const id = generateId() as string control.operationContext.derived.targets[id] = (it) => { if (ids.has(it._id)) { - return [info.account?.email as string] + return [info.account?.email] } } } @@ -1048,7 +1017,7 @@ async function updateCollaboratorsMixin ( if (newCollabs.length > 0) { const docNotifyContexts = await control.findAllCtx(ctx, notification.class.DocNotifyContext, { user: { $in: newCollabs }, - attachedTo: tx.objectId + objectId: tx.objectId }) const infos = await ctx.with( @@ -1056,11 +1025,13 @@ async function updateCollaboratorsMixin ( {}, async (ctx) => await getUsersInfo(ctx, [...newCollabs, originTx.modifiedBy] as Ref[], control) ) - const sender = infos.find(({ _id }) => _id === originTx.modifiedBy) ?? { _id: originTx.modifiedBy } + const sender: SenderInfo = infos.find(({ _id }) => _id === originTx.modifiedBy) ?? { _id: originTx.modifiedBy } for (const collab of newCollabs) { - const target = infos.find(({ _id }) => _id === collab) - + const target = toReceiverInfo( + hierarchy, + infos.find(({ _id }) => _id === collab) + ) if (target === undefined) continue for (const message of activityMessages) { @@ -1154,7 +1125,7 @@ async function removeCollaboratorDoc (tx: TxRemoveDoc, control: TriggerCont const res: Tx[] = [] const notifyContexts = await control.findAll( notification.class.DocNotifyContext, - { attachedTo: tx.objectId }, + { objectId: tx.objectId }, { projection: { _id: 1, @@ -1545,7 +1516,7 @@ async function OnActivityMessageRemove (message: ActivityMessage, control: Trigg return [] } - const contexts = await control.findAll(notification.class.DocNotifyContext, { attachedTo: message.attachedTo }) + const contexts = await control.findAll(notification.class.DocNotifyContext, { objectId: message.attachedTo }) if (contexts.length === 0) return [] const isLastUpdate = contexts.some((context) => { diff --git a/server-plugins/notification-resources/src/utils.ts b/server-plugins/notification-resources/src/utils.ts index acbd9da66a..d34f1ac3fa 100644 --- a/server-plugins/notification-resources/src/utils.ts +++ b/server-plugins/notification-resources/src/utils.ts @@ -46,8 +46,9 @@ import serverNotification, { getPersonAccountById, HTMLPresenter, NotificationPresenter, - TextPresenter, - UserInfo + ReceiverInfo, + SenderInfo, + TextPresenter } from '@hcengineering/server-notification' import { ActivityMessage, DocUpdateMessage } from '@hcengineering/activity' @@ -300,10 +301,12 @@ export async function updateNotifyContextsSpace ( return [] } - const notifyContexts = await control.findAll(notification.class.DocNotifyContext, { attachedTo: tx.objectId }) + const notifyContexts = await control.findAll(notification.class.DocNotifyContext, { objectId: tx.objectId }) return notifyContexts.map((value) => - control.txFactory.createTxUpdateDoc(value._class, value.space, value._id, { space: updateTx.operations.space }) + control.txFactory.createTxUpdateDoc(value._class, value.space, value._id, { + objectSpace: updateTx.operations.space + }) ) } @@ -319,7 +322,7 @@ export function getTextPresenter (_class: Ref>, hierarchy: Hierarchy) return hierarchy.classHierarchyMixin(_class, serverNotification.mixin.TextPresenter) } -async function getSenderName (control: TriggerControl, sender: UserInfo): Promise { +async function getSenderName (control: TriggerControl, sender: SenderInfo): Promise { if (sender._id === core.account.System) { return await translate(core.string.System, {}) } @@ -340,7 +343,7 @@ async function getFallbackNotificationFullfillment ( object: Doc, originTx: TxCUD, control: TriggerControl, - sender: UserInfo, + sender: SenderInfo, message?: ActivityMessage ): Promise { const title: IntlString = notification.string.CommonNotificationTitle @@ -419,7 +422,7 @@ function getNotificationPresenter (_class: Ref>, hierarchy: Hierarchy export async function getNotificationContent ( originTx: TxCUD, targetUser: PersonAccount, - sender: UserInfo, + sender: SenderInfo, object: Doc, control: TriggerControl, message?: ActivityMessage @@ -471,19 +474,51 @@ export async function getUsersInfo ( ctx: MeasureContext, ids: Ref[], control: TriggerControl -): Promise { +): Promise<(ReceiverInfo | SenderInfo)[]> { const accounts = await control.modelDb.findAll(contact.class.PersonAccount, { _id: { $in: ids } }) + const personIds = accounts.map((it) => it.person) + const accountById = toIdMap(accounts) const persons = toIdMap( await ctx.with( - 'query-find', + 'find-persons', {}, - async () => await control.findAll(contact.class.Person, { _id: { $in: accounts.map((it) => it.person) } }) + async () => await control.findAll(contact.class.Person, { _id: { $in: personIds } }) ) ) + const spaces = await ctx.with('find-person-spaces', {}, async () => { + const res = await control.findAll(contact.class.PersonSpace, { person: { $in: personIds } }) - return accounts.map((account) => ({ - _id: account._id, - account, - person: persons.get(account.person) - })) + return new Map(res.map((s) => [s.person, s])) + }) + + return ids.map((_id) => { + const account = accountById.get(_id) + return { + _id, + account, + person: account !== undefined ? persons.get(account.person) : undefined, + space: account !== undefined ? spaces.get(account.person)?._id : undefined + } + }) +} + +export function toReceiverInfo (hierarchy: Hierarchy, info?: SenderInfo | ReceiverInfo): ReceiverInfo | undefined { + if (info === undefined) return undefined + if (info.person === undefined) return undefined + if (info.account === undefined) return undefined + if (!('space' in info)) return undefined + if (info.space === undefined) return undefined + + const isEmployee = hierarchy.hasMixin(info.person, contact.mixin.Employee) + if (!isEmployee) return undefined + + const employee = hierarchy.as(info.person, contact.mixin.Employee) + if (!employee.active) return undefined + + return { + _id: info._id, + account: info.account, + person: employee, + space: info.space + } } diff --git a/server-plugins/notification/src/index.ts b/server-plugins/notification/src/index.ts index 86329693b1..9101638965 100644 --- a/server-plugins/notification/src/index.ts +++ b/server-plugins/notification/src/index.ts @@ -14,7 +14,7 @@ // limitations under the License. // -import contact, { Employee, Person, PersonAccount } from '@hcengineering/contact' +import contact, { Employee, Person, PersonAccount, PersonSpace } from '@hcengineering/contact' import { Account, Class, Doc, Mixin, Ref, Tx, TxCUD } from '@hcengineering/core' import { BaseNotificationType, @@ -135,7 +135,14 @@ export interface NotificationPresenter extends Class { presenter: Resource } -export interface UserInfo { +export interface ReceiverInfo { + _id: Ref + account: PersonAccount + person: Employee + space: Ref +} + +export interface SenderInfo { _id: Ref account?: PersonAccount person?: Person @@ -146,8 +153,8 @@ export type NotificationProviderFunc = ( types: BaseNotificationType[], object: Doc, data: InboxNotification, - receiver: UserInfo, - sender: UserInfo + receiver: ReceiverInfo, + sender: SenderInfo ) => Promise export interface NotificationProviderResources extends Doc { diff --git a/server-plugins/request-resources/src/index.ts b/server-plugins/request-resources/src/index.ts index 94e5c64bc0..03c5b39d7c 100644 --- a/server-plugins/request-resources/src/index.ts +++ b/server-plugins/request-resources/src/index.ts @@ -24,7 +24,8 @@ import { getNotificationTxes, getCollaborators, getTextPresenter, - getUsersInfo + getUsersInfo, + toReceiverInfo } from '@hcengineering/server-notification-resources' import { PersonAccount } from '@hcengineering/contact' @@ -144,7 +145,7 @@ async function getRequestNotificationTx (tx: TxCollectionCUD, cont if (collaborators.length === 0) return res const notifyContexts = await control.findAll(notification.class.DocNotifyContext, { - attachedTo: doc._id + objectId: doc._id }) const usersInfo = await getUsersInfo(control.ctx, [...collaborators, tx.modifiedBy] as Ref[], control) const senderInfo = usersInfo.find(({ _id }) => _id === tx.modifiedBy) ?? { @@ -152,7 +153,10 @@ async function getRequestNotificationTx (tx: TxCollectionCUD, cont } for (const target of collaborators) { - const targetInfo = usersInfo.find(({ _id }) => _id === target) + const targetInfo = toReceiverInfo( + control.hierarchy, + usersInfo.find(({ _id }) => _id === target) + ) if (targetInfo === undefined) continue const txes = await getNotificationTxes( diff --git a/server-plugins/telegram-resources/src/index.ts b/server-plugins/telegram-resources/src/index.ts index c5d5a81efb..c39226baac 100644 --- a/server-plugins/telegram-resources/src/index.ts +++ b/server-plugins/telegram-resources/src/index.ts @@ -31,9 +31,9 @@ import { } from '@hcengineering/core' import { TriggerControl } from '@hcengineering/server-core' import telegram, { TelegramMessage, TelegramNotificationRecord } from '@hcengineering/telegram' -import notification, { BaseNotificationType, InboxNotification, NotificationType } from '@hcengineering/notification' +import { BaseNotificationType, InboxNotification, NotificationType } from '@hcengineering/notification' import setting, { Integration } from '@hcengineering/setting' -import { NotificationProviderFunc, UserInfo } from '@hcengineering/server-notification' +import { NotificationProviderFunc, ReceiverInfo, SenderInfo } from '@hcengineering/server-notification' import { getMetadata, getResource } from '@hcengineering/platform' import serverTelegram from '@hcengineering/server-telegram' import { getTranslatedNotificationContent, getTextPresenter } from '@hcengineering/server-notification-resources' @@ -78,48 +78,6 @@ export async function OnMessageCreate (tx: Tx, control: TriggerControl): Promise }) res.push(tx) } - - if (message.incoming) { - const docs = await control.findAll(notification.class.DocNotifyContext, { - attachedTo: channel._id, - user: message.modifiedBy - }) - for (const doc of docs) { - // TODO: push inbox notifications - // res.push( - // control.txFactory.createTxUpdateDoc(doc._class, doc.space, doc._id, { - // $push: { - // txes: { - // _id: tx._id as Ref>, - // modifiedOn: tx.modifiedOn, - // modifiedBy: tx.modifiedBy, - // isNew: true - // } - // } - // }) - // ) - res.push( - control.txFactory.createTxUpdateDoc(doc._class, doc.space, doc._id, { - lastUpdateTimestamp: tx.modifiedOn - }) - ) - } - if (docs.length === 0) { - res.push( - control.txFactory.createTxCreateDoc(notification.class.DocNotifyContext, channel.space, { - user: tx.modifiedBy, - attachedTo: channel._id, - attachedToClass: channel._class, - lastUpdateTimestamp: tx.modifiedOn, - isPinned: false - // TODO: push inbox notifications - // txes: [ - // { _id: tx._id as Ref>, modifiedOn: tx.modifiedOn, modifiedBy: tx.modifiedBy, isNew: true } - // ] - }) - ) - } - } } return res @@ -228,17 +186,13 @@ const SendTelegramNotifications: NotificationProviderFunc = async ( types: BaseNotificationType[], doc: Doc, data: InboxNotification, - receiver: UserInfo, - sender: UserInfo + receiver: ReceiverInfo, + sender: SenderInfo ): Promise => { if (types.length === 0) { return [] } - if (receiver.person === undefined || receiver.account?.email === undefined) { - return [] - } - const botUrl = getMetadata(serverTelegram.metadata.BotUrl) if (botUrl === undefined || botUrl === '') { @@ -246,15 +200,7 @@ const SendTelegramNotifications: NotificationProviderFunc = async ( return [] } - const isEmployee = control.hierarchy.hasMixin(receiver.person, contact.mixin.Employee) - - if (!isEmployee) { - return [] - } - - const employee = control.hierarchy.as(receiver.person, contact.mixin.Employee) - - if (!employee.active) { + if (!receiver.person.active) { return [] } diff --git a/server-plugins/time-resources/src/index.ts b/server-plugins/time-resources/src/index.ts index 199a18c2e8..0301f5bb3b 100644 --- a/server-plugins/time-resources/src/index.ts +++ b/server-plugins/time-resources/src/index.ts @@ -44,7 +44,7 @@ import { jsonToMarkup, nodeDoc, nodeParagraph, nodeText } from '@hcengineering/t import tracker, { Issue, IssueStatus, Project, TimeSpendReport } from '@hcengineering/tracker' import serverTime, { OnToDo, ToDoFactory } from '@hcengineering/server-time' import time, { ProjectToDo, ToDo, ToDoPriority, TodoAutomationHelper, WorkSlot } from '@hcengineering/time' -import { UserInfo } from '@hcengineering/server-notification' +import { ReceiverInfo, SenderInfo } from '@hcengineering/server-notification' /** * @public @@ -186,9 +186,23 @@ export async function OnToDoCreate (tx: TxCUD, control: TriggerControl): Pr } const object = (await control.findAll(todo.attachedToClass, { _id: todo.attachedTo }))[0] - if (object === undefined) return [] + const person = ( + await control.findAll(contact.mixin.Employee, { _id: account.person as Ref, active: true }, { limit: 1 }) + )[0] + if (person === undefined) return [] + + const personSpace = (await control.findAll(contact.class.PersonSpace, { person: account.person }, { limit: 1 }))[0] + if (personSpace === undefined) return [] + + const receiverInfo: ReceiverInfo = { + _id: account._id, + account, + person, + space: personSpace._id + } + const senderAccount = await control.modelDb.findOne(contact.class.PersonAccount, { _id: tx.modifiedBy as Ref }) @@ -197,13 +211,12 @@ export async function OnToDoCreate (tx: TxCUD, control: TriggerControl): Pr ? (await control.findAll(contact.class.Person, { _id: senderAccount.person }))[0] : undefined - const senderInfo: UserInfo = { + const senderInfo: SenderInfo = { _id: tx.modifiedBy, account: senderAccount, person: senderPerson } - const res: Tx[] = [] const notifyResult = await isShouldNotifyTx(control, createTx, tx, todo, account, true, false) const content = await getNotificationContent(tx, account, senderInfo, todo, control) const data: Partial> = { @@ -215,30 +228,18 @@ export async function OnToDoCreate (tx: TxCUD, control: TriggerControl): Pr messageHtml: jsonToMarkup(nodeDoc(nodeParagraph(nodeText(todo.title)))) } - const person = (await control.modelDb.findAll(contact.class.Person, { _id: account.person }))[0] - - const receiverInfo: UserInfo = { - _id: account._id, - account, - person - } - - res.push( - ...(await getCommonNotificationTxes( - control, - object, - data, - receiverInfo, - senderInfo, - object._id, - object._class, - object.space, - createTx.modifiedOn, - notifyResult - )) + return await getCommonNotificationTxes( + control, + object, + data, + receiverInfo, + senderInfo, + object._id, + object._class, + object.space, + createTx.modifiedOn, + notifyResult ) - - return res } /** diff --git a/server/account/src/operations.ts b/server/account/src/operations.ts index 863208e1ac..a2c621bbef 100644 --- a/server/account/src/operations.ts +++ b/server/account/src/operations.ts @@ -1953,7 +1953,7 @@ async function createPersonAccount ( const ops = new TxOperations(connection, core.account.System) const name = combineName(account.first, account.last) - // Check if EmployeeAccount is not exists + // Check if PersonAccount is not exists if (shouldReplaceCurrent) { const currentAccount = await ops.findOne(contact.class.PersonAccount, {}) if (currentAccount !== undefined) { diff --git a/server/mongo/src/storage.ts b/server/mongo/src/storage.ts index a011c93566..89b96d1f0e 100644 --- a/server/mongo/src/storage.ts +++ b/server/mongo/src/storage.ts @@ -1520,8 +1520,7 @@ class MongoTxAdapter extends MongoAdapterBase implements TxAdapter { (tx._class === core.class.TxCreateDoc || tx._class === core.class.TxUpdateDoc || tx._class === core.class.TxRemoveDoc) && - ((tx as TxCUD).objectClass === 'contact:class:PersonAccount' || - (tx as TxCUD).objectClass === 'contact:class:EmployeeAccount') + (tx as TxCUD).objectClass === 'contact:class:PersonAccount' ) } model.forEach((tx) => (tx.modifiedBy === core.account.System && !isPersonAccount(tx) ? systemTx : userTx).push(tx)) diff --git a/server/tool/src/index.ts b/server/tool/src/index.ts index 58c7b4f186..e6e3c503eb 100644 --- a/server/tool/src/index.ts +++ b/server/tool/src/index.ts @@ -332,7 +332,7 @@ export async function upgradeModel ( (it) => it.modifiedBy !== core.account.System || (it as TxCUD).objectClass === contact.class.Person || - (it as TxCUD).objectClass === 'contact:class:EmployeeAccount' + (it as TxCUD).objectClass === 'contact:class:PersonAccount' ) ) ] diff --git a/services/github/pod-github/src/notifications.ts b/services/github/pod-github/src/notifications.ts index 65be33bb17..a7884d06ac 100644 --- a/services/github/pod-github/src/notifications.ts +++ b/services/github/pod-github/src/notifications.ts @@ -1,19 +1,21 @@ import { Account, Doc, Ref, TxOperations } from '@hcengineering/core' import notification, { DocNotifyContext } from '@hcengineering/notification' import { IntlString } from '@hcengineering/platform' +import { PersonSpace } from '@hcengineering/contact' import github from '@hcengineering/github' export async function createNotification ( client: TxOperations, forDoc: Doc, - data: { user: Ref, message: IntlString, props: Record } + data: { user: Ref, space: Ref, message: IntlString, props: Record } ): Promise { - let docNotifyContext = await client.findOne(notification.class.DocNotifyContext, { attachedTo: forDoc._id }) + let docNotifyContext = await client.findOne(notification.class.DocNotifyContext, { objectId: forDoc._id }) if (docNotifyContext?._id === undefined) { - const docNotifyContextId = await client.createDoc(notification.class.DocNotifyContext, forDoc.space, { - attachedTo: forDoc._id, - attachedToClass: forDoc._class, + const docNotifyContextId = await client.createDoc(notification.class.DocNotifyContext, data.space, { + objectId: forDoc._id, + objectClass: forDoc._class, + objectSpace: forDoc.space, user: data.user, isPinned: false }) @@ -21,7 +23,6 @@ export async function createNotification ( } // Check if we had already same notification send, and just unmark it viewed. - const existing = await client.findOne(notification.class.CommonInboxNotification, { user: data.user, message: data.message, @@ -32,7 +33,7 @@ export async function createNotification ( isViewed: false }) } else { - await client.createDoc(notification.class.CommonInboxNotification, forDoc.space, { + await client.createDoc(notification.class.CommonInboxNotification, data.space, { user: data.user, icon: github.icon.Github, message: data.message, diff --git a/services/github/pod-github/src/platform.ts b/services/github/pod-github/src/platform.ts index 4bc9f17347..292a7cdc09 100644 --- a/services/github/pod-github/src/platform.ts +++ b/services/github/pod-github/src/platform.ts @@ -409,13 +409,17 @@ export class PlatformWorker { const person = (await client.findOne(contact.class.Person, { _id: account.person })) as Person if (person !== undefined) { if (!revoke) { - await createNotification(client, person, { - user: account._id, - message: github.string.AuthenticatedWithGithub, - props: { - login: update.login - } - }) + const personSpace = await client.findOne(contact.class.PersonSpace, { person: person._id }) + if (personSpace !== undefined) { + await createNotification(client, person, { + user: account._id, + space: personSpace._id, + message: github.string.AuthenticatedWithGithub, + props: { + login: update.login + } + }) + } const githubAccount = (await client.findOne(core.class.Account, { email: 'github:' + update.login @@ -427,23 +431,31 @@ export class PlatformWorker { const dPerson = (await client.findOne(contact.class.Person, { _id: dummyPerson })) as Person if (person !== undefined && dPerson !== undefined) { - await createNotification(client, dPerson, { - user: account._id, - message: github.string.AuthenticatedWithGithubEmployee, - props: { - login: update.login - } - }) + const personSpace = await client.findOne(contact.class.PersonSpace, { person: person._id }) + if (personSpace !== undefined) { + await createNotification(client, dPerson, { + user: githubAccount._id, + space: personSpace._id, + message: github.string.AuthenticatedWithGithubEmployee, + props: { + login: update.login + } + }) + } } } } else { - await createNotification(client, person, { - user: account._id, - message: github.string.AuthenticationRevokedGithub, - props: { - login: update.login - } - }) + const personSpace = await client.findOne(contact.class.PersonSpace, { person: person._id }) + if (personSpace !== undefined) { + await createNotification(client, person, { + user: account._id, + space: personSpace._id, + message: github.string.AuthenticationRevokedGithub, + props: { + login: update.login + } + }) + } } } diff --git a/services/github/pod-github/src/worker.ts b/services/github/pod-github/src/worker.ts index dc6c4f1a74..cf4aff8570 100644 --- a/services/github/pod-github/src/worker.ts +++ b/services/github/pod-github/src/worker.ts @@ -543,11 +543,15 @@ export class GithubWorker implements IntegrationManager { if (accountRef !== undefined) { const person = await this.client.findOne(contact.class.Person, { _id: accountRef.person }) if (person !== undefined) { - await createNotification(this._client, person, { - user: account, - message: github.string.AuthenticatedWithGithubRequired, - props: {} - }) + const personSpace = await this.client.findOne(contact.class.PersonSpace, { person: person._id }) + if (personSpace !== undefined) { + await createNotification(this._client, person, { + user: account, + space: personSpace._id, + message: github.string.AuthenticatedWithGithubRequired, + props: {} + }) + } } } this.ctx.info('get octokit: return bot', { account })