mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 02:51:54 +03:00
Move notifications to person spaces (#6263)
Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
parent
b997b0c753
commit
1c1d0bc538
1
.vscode/launch.json
vendored
1
.vscode/launch.json
vendored
@ -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",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "desktop",
|
||||
"version": "0.6.266",
|
||||
"version": "0.6.271",
|
||||
"main": "dist/main/electron.js",
|
||||
"author": "Hardcore Engineering <hey@huly.io>",
|
||||
"template": "@hcengineering/default-package",
|
||||
|
@ -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": {
|
||||
|
@ -44,14 +44,14 @@ export const DOMAIN_COMMENT = 'comment' as Domain
|
||||
export async function createDocNotifyContexts (
|
||||
client: MigrationUpgradeClient,
|
||||
tx: TxOperations,
|
||||
attachedTo: Ref<Doc>,
|
||||
attachedToClass: Ref<Class<Doc>>
|
||||
objectId: Ref<Doc>,
|
||||
objectClass: Ref<Class<Doc>>,
|
||||
objectSpace: Ref<Space>
|
||||
): Promise<void> {
|
||||
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<Ref<PersonAccount>[]> {
|
||||
async function getAllPersonAccounts (tx: TxOperations): Promise<Ref<PersonAccount>[]> {
|
||||
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<Ref<PersonAcco
|
||||
}
|
||||
|
||||
async function joinEmployees (current: Space, tx: TxOperations): Promise<void> {
|
||||
const accs = await getAllEmployeeAccounts(tx)
|
||||
const accs = await getAllPersonAccounts(tx)
|
||||
const newMembers: Ref<Account>[] = [...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<void> {
|
||||
|
@ -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<Person>
|
||||
}
|
||||
|
||||
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, {})
|
||||
|
@ -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<void> {
|
||||
const employees = await client.findAll(contact.mixin.Employee, {})
|
||||
@ -100,23 +109,55 @@ async function migrateAvatars (client: MigrationClient): Promise<void> {
|
||||
)
|
||||
}
|
||||
|
||||
async function createPersonSpaces (client: MigrationClient): Promise<void> {
|
||||
const spaces = await client.find<PersonSpace>(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<Ref<Person>, 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<void> {
|
||||
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
|
||||
}
|
||||
])
|
||||
},
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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<Account>
|
||||
|
||||
@Prop(TypeRef(core.class.Doc), core.string.AttachedTo)
|
||||
@Prop(TypeRef(core.class.Doc), core.string.Object)
|
||||
@Index(IndexKind.Indexed)
|
||||
attachedTo!: Ref<Doc>
|
||||
objectId!: Ref<Doc>
|
||||
|
||||
@Prop(TypeRef(core.class.Class), core.string.AttachedToClass)
|
||||
@Prop(TypeRef(core.class.Class), core.string.Class)
|
||||
@Index(IndexKind.Indexed)
|
||||
attachedToClass!: Ref<Class<Doc>>
|
||||
objectClass!: Ref<Class<Doc>>
|
||||
|
||||
@Prop(TypeRef(core.class.Space), core.string.Space)
|
||||
objectSpace!: Ref<Space>
|
||||
|
||||
declare space: Ref<PersonSpace>
|
||||
|
||||
@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<PersonSpace>
|
||||
|
||||
title?: IntlString
|
||||
body?: IntlString
|
||||
intlParams?: Record<string, string | number>
|
||||
|
@ -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<void> {
|
||||
const personSpaces = await client.find<PersonSpace>(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<DocNotifyContext>(
|
||||
DOMAIN_DOC_NOTIFY,
|
||||
{
|
||||
_class: notification.class.DocNotifyContext,
|
||||
attachedTo: { $exists: true }
|
||||
},
|
||||
{ limit: 500 }
|
||||
)
|
||||
|
||||
if (contexts.length === 0) {
|
||||
break
|
||||
}
|
||||
|
||||
const classesOfSpace = new Set<Ref<Class<Doc>>>()
|
||||
|
||||
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<DocNotifyContext>(
|
||||
DOMAIN_DOC_NOTIFY,
|
||||
{ objectClass: { $in: Array.from(classesOfSpace) } },
|
||||
{ objectSpace: core.space.Space }
|
||||
)
|
||||
await client.update<DocNotifyContext>(
|
||||
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<void> {
|
||||
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<BrowserNotification>(DOMAIN_USER_NOTIFY, {
|
||||
_class: notification.class.BrowserNotification,
|
||||
status: NotificationStatus.Notified
|
||||
|
@ -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}
|
||||
<Lazy>
|
||||
<ListViewItem
|
||||
bind:element={refs[row]}
|
||||
{colorsSchema}
|
||||
{addClass}
|
||||
{row}
|
||||
{kind}
|
||||
isHighlighted={row === highlightIndex}
|
||||
selected={row === selection}
|
||||
on:click={() => dispatch('click', row)}
|
||||
on:mouseover={mouseAttractor(() => {
|
||||
if (updateOnMouse) {
|
||||
onRow(row)
|
||||
}
|
||||
})}
|
||||
on:mouseenter={mouseAttractor(() => {
|
||||
if (updateOnMouse) {
|
||||
onRow(row)
|
||||
}
|
||||
})}
|
||||
>
|
||||
<svelte:fragment slot="category" let:item={itemIndex}>
|
||||
<slot name="category" item={itemIndex} />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="item" let:item={itemIndex}>
|
||||
<slot name="item" item={itemIndex} />
|
||||
</svelte:fragment>
|
||||
</ListViewItem>
|
||||
</Lazy>
|
||||
<div style="min-height: {minHeight}">
|
||||
<Lazy>
|
||||
<ListViewItem
|
||||
bind:element={refs[row]}
|
||||
{colorsSchema}
|
||||
{addClass}
|
||||
{row}
|
||||
{kind}
|
||||
isHighlighted={row === highlightIndex}
|
||||
selected={row === selection}
|
||||
on:click={() => dispatch('click', row)}
|
||||
on:mouseover={mouseAttractor(() => {
|
||||
if (updateOnMouse) {
|
||||
onRow(row)
|
||||
}
|
||||
})}
|
||||
on:mouseenter={mouseAttractor(() => {
|
||||
if (updateOnMouse) {
|
||||
onRow(row)
|
||||
}
|
||||
})}
|
||||
>
|
||||
<svelte:fragment slot="category" let:item={itemIndex}>
|
||||
<slot name="category" item={itemIndex} />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="item" let:item={itemIndex}>
|
||||
<slot name="item" item={itemIndex} />
|
||||
</svelte:fragment>
|
||||
</ListViewItem>
|
||||
</Lazy>
|
||||
</div>
|
||||
{:else}
|
||||
<ListViewItem
|
||||
bind:element={refs[row]}
|
||||
|
@ -77,7 +77,7 @@
|
||||
const ctx =
|
||||
context ??
|
||||
(await client.findOne(notification.class.DocNotifyContext, {
|
||||
attachedTo: object._id,
|
||||
objectId: object._id,
|
||||
user: getCurrentAccount()._id
|
||||
}))
|
||||
const hasRefs = ((object as WithReferences<Doc>).references ?? 0) > 0
|
||||
|
@ -36,7 +36,7 @@
|
||||
$: threadId = context ? undefined : (_id as Ref<ActivityMessage>)
|
||||
|
||||
$: context &&
|
||||
objectQuery.query(context.attachedToClass, { _id: context.attachedTo }, (res) => {
|
||||
objectQuery.query(context.objectClass, { _id: context.objectId }, (res) => {
|
||||
;[object] = res
|
||||
})
|
||||
</script>
|
||||
|
@ -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<void> {
|
||||
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
|
||||
})
|
||||
|
||||
|
@ -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<Employee>[] = []
|
||||
@ -52,7 +52,7 @@
|
||||
|
||||
async function createDirectMessage (): Promise<void> {
|
||||
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
|
||||
})
|
||||
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -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)}
|
||||
<ChatNavItem {context} isSelected={objectId === item.id} {item} type={'type-object'} on:select />
|
||||
{/each}
|
||||
{#if canShowMore}
|
||||
@ -130,7 +130,7 @@
|
||||
{/if}
|
||||
<svelte:fragment slot="visible" let:isOpen>
|
||||
{#if visibleItem !== undefined && !isOpen}
|
||||
{@const context = contexts.find(({ attachedTo }) => attachedTo === visibleItem?.id)}
|
||||
{@const context = contexts.find(({ objectId }) => objectId === visibleItem?.id)}
|
||||
<ChatNavItem {context} isSelected item={visibleItem} type={'type-object'} on:select />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { AccountRole, Doc, Ref, getCurrentAccount, hasAccountRole } from '@hcengineering/core'
|
||||
import { AccountRole, Doc, getCurrentAccount, hasAccountRole } from '@hcengineering/core'
|
||||
import { Scroller, SearchEdit, Label, ButtonIcon, IconAdd, showPopup, Menu } from '@hcengineering/ui'
|
||||
import { DocNotifyContext } from '@hcengineering/notification'
|
||||
import { SpecialNavModel } from '@hcengineering/workbench'
|
||||
|
@ -251,7 +251,7 @@ function sortDirects (items: ChatNavItemModel[], option: SortFnOptions): ChatNav
|
||||
|
||||
function sortActivityChannels (items: ChatNavItemModel[], option: SortFnOptions): ChatNavItemModel[] {
|
||||
const { contexts } = option
|
||||
const contextByDoc = new Map(contexts.map((context) => [context.attachedTo, context]))
|
||||
const contextByDoc = new Map(contexts.map((context) => [context.objectId, context]))
|
||||
|
||||
return items.sort((i1, i2) => {
|
||||
const context1 = contextByDoc.get(i1.id)
|
||||
|
@ -35,7 +35,7 @@
|
||||
let title: string | undefined = undefined
|
||||
let channel: Doc | undefined = undefined
|
||||
|
||||
$: isThread = hierarchy.isDerived(context.attachedToClass, chunter.class.ThreadMessage)
|
||||
$: isThread = hierarchy.isDerived(context.objectClass, chunter.class.ThreadMessage)
|
||||
|
||||
$: loadChannel(object, isThread)
|
||||
$: channel &&
|
||||
@ -46,7 +46,7 @@
|
||||
function loadChannel (object: ChatMessage, isThread: boolean): void {
|
||||
const _class = isThread ? (object as ThreadMessage).objectClass : object.attachedToClass
|
||||
const _id = isThread ? (object as ThreadMessage).objectId : object.attachedTo
|
||||
console.log({ _class, _id, isThread, object })
|
||||
|
||||
void client.findOne(_class, { _id, ...(isThread ? { space: object.space } : {}) }).then((res) => {
|
||||
channel = res
|
||||
})
|
||||
|
@ -491,7 +491,7 @@ export async function leaveChannelAction (
|
||||
}
|
||||
const client = getClient()
|
||||
const channel =
|
||||
props?.object ?? (await client.findOne(chunter.class.Channel, { _id: context.attachedTo as Ref<Channel> }))
|
||||
props?.object ?? (await client.findOne(chunter.class.Channel, { _id: context.objectId as Ref<Channel> }))
|
||||
|
||||
if (channel === undefined) {
|
||||
return
|
||||
@ -501,37 +501,33 @@ export async function leaveChannelAction (
|
||||
await resetChunterLocIfEqual(channel._id, channel._class, channel)
|
||||
}
|
||||
|
||||
export async function removeChannelAction (
|
||||
context?: DocNotifyContext,
|
||||
_?: Event,
|
||||
props?: { object?: Doc }
|
||||
): Promise<void> {
|
||||
export async function removeChannelAction (context?: DocNotifyContext, _?: Event): Promise<void> {
|
||||
if (context === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const inboxClient = InboxNotificationsClientImpl.getClient()
|
||||
const { objectId, objectClass, objectSpace } = context
|
||||
|
||||
if (hierarchy.isDerived(context.attachedToClass, chunter.class.Channel)) {
|
||||
const channel = await client.findOne(chunter.class.Channel, { _id: context.attachedTo as Ref<Channel> })
|
||||
if (hierarchy.isDerived(objectClass, chunter.class.Channel)) {
|
||||
const channel = await client.findOne(chunter.class.Channel, { _id: objectId as Ref<Channel>, space: objectSpace })
|
||||
await leaveChannel(channel, getCurrentAccount()._id)
|
||||
} else {
|
||||
const object = await client.findOne(context.attachedToClass, { _id: context.attachedTo })
|
||||
const account = getCurrentAccount() as PersonAccount
|
||||
const object = await client.findOne(objectClass, { _id: objectId, space: objectSpace })
|
||||
// const account = getCurrentAccount() as PersonAccount
|
||||
|
||||
await client.createMixin(context._id, context._class, context.space, chunter.mixin.ChannelInfo, { hidden: true })
|
||||
|
||||
const chatInfo = await client.findOne(chunter.class.ChatInfo, { user: account.person })
|
||||
|
||||
if (chatInfo !== undefined) {
|
||||
await client.update(chatInfo, { hidden: chatInfo.hidden.concat([context._id]) })
|
||||
}
|
||||
await resetChunterLocIfEqual(context.attachedTo, context.attachedToClass, object)
|
||||
// await client.createMixin(context._id, context._class, context.space, chunter.mixin.ChannelInfo, { hidden: true })
|
||||
//
|
||||
// const chatInfo = await client.findOne(chunter.class.ChatInfo, { user: account.person })
|
||||
//
|
||||
// if (chatInfo !== undefined) {
|
||||
// await client.update(chatInfo, { hidden: chatInfo.hidden.concat([context._id]) })
|
||||
// }
|
||||
await resetChunterLocIfEqual(objectId, objectClass, object)
|
||||
}
|
||||
|
||||
void inboxClient.readDoc(client, context.attachedTo)
|
||||
await client.remove(context)
|
||||
}
|
||||
|
||||
export function isThreadMessage (message: ActivityMessage): message is ThreadMessage {
|
||||
|
@ -179,6 +179,10 @@ export interface ContactsTab extends Doc {
|
||||
*/
|
||||
export const contactId = 'contact' as Plugin
|
||||
|
||||
export interface PersonSpace extends Space {
|
||||
person: Ref<Person>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -193,7 +197,8 @@ export const contactPlugin = plugin(contactId, {
|
||||
Organization: '' as Ref<Class<Organization>>,
|
||||
PersonAccount: '' as Ref<Class<PersonAccount>>,
|
||||
Status: '' as Ref<Class<Status>>,
|
||||
ContactsTab: '' as Ref<Class<ContactsTab>>
|
||||
ContactsTab: '' as Ref<Class<ContactsTab>>,
|
||||
PersonSpace: '' as Ref<Class<PersonSpace>>
|
||||
},
|
||||
mixin: {
|
||||
Employee: '' as Ref<Class<Employee>>
|
||||
|
@ -55,6 +55,7 @@
|
||||
"CommonNotificationCollectionAdded": "{senderName} added {collection}",
|
||||
"CommonNotificationCollectionRemoved": "{senderName} removed {collection}",
|
||||
"Sound": "Sound",
|
||||
"SoundNotificationsDescription": "Receive sound notifications for events."
|
||||
"SoundNotificationsDescription": "Receive sound notifications for events.",
|
||||
"NoAccessToObject": "You no longer have access to this object"
|
||||
}
|
||||
}
|
||||
|
@ -54,6 +54,7 @@
|
||||
"Sound": "Sonido",
|
||||
"SoundNotificationsDescription": "Reciba notificaciones de sonido para eventos.",
|
||||
"CommonNotificationCollectionAdded": "{senderName} añadió {collection}",
|
||||
"CommonNotificationCollectionRemoved": "{senderName} eliminó {collection}"
|
||||
"CommonNotificationCollectionRemoved": "{senderName} eliminó {collection}",
|
||||
"NoAccessToObject": "Ya no tienes acceso a este objeto"
|
||||
}
|
||||
}
|
@ -55,6 +55,7 @@
|
||||
"Sound": "Son",
|
||||
"SoundNotificationsDescription": "Recevez des notifications sonores pour les événements.",
|
||||
"CommonNotificationCollectionAdded": "{senderName} a ajouté {collection}",
|
||||
"CommonNotificationCollectionRemoved": "{senderName} a supprimé {collection}"
|
||||
"CommonNotificationCollectionRemoved": "{senderName} a supprimé {collection}",
|
||||
"NoAccessToObject": "Vous n'avez plus accès à cet objet"
|
||||
}
|
||||
}
|
@ -54,6 +54,7 @@
|
||||
"Sound": "Som",
|
||||
"SoundNotificationsDescription": "Receba notificações sonoras para eventos.",
|
||||
"CommonNotificationCollectionAdded": "{senderName} adicionou {collection}",
|
||||
"CommonNotificationCollectionRemoved": "{senderName} removeu {collection}"
|
||||
"CommonNotificationCollectionRemoved": "{senderName} removeu {collection}",
|
||||
"NoAccessToObject": "Você não tem mais acesso a este objeto"
|
||||
}
|
||||
}
|
@ -55,6 +55,7 @@
|
||||
"Sound": "Звук",
|
||||
"SoundNotificationsDescription": "Получайте звуковые уведомления о событиях.",
|
||||
"CommonNotificationCollectionAdded": "{senderName} добавил {collection}",
|
||||
"CommonNotificationCollectionRemoved": "{senderName} удалил {collection}"
|
||||
"CommonNotificationCollectionRemoved": "{senderName} удалил {collection}",
|
||||
"NoAccessToObject": "У вас больше нет доступа к этому объекту"
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,7 @@
|
||||
"Sound": "声音",
|
||||
"SoundNotificationsDescription": "接收事件的声音通知。",
|
||||
"CommonNotificationCollectionAdded": "{senderName} 添加了 {collection}",
|
||||
"CommonNotificationCollectionRemoved": "{senderName} 移除了 {collection}"
|
||||
"CommonNotificationCollectionRemoved": "{senderName} 移除了 {collection}",
|
||||
"NoAccessToObject": "您不再可以访问此对象"
|
||||
}
|
||||
}
|
||||
|
@ -13,17 +13,17 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { ButtonIcon, CheckBox, Component, IconMoreV, Label, showPopup, Spinner } from '@hcengineering/ui'
|
||||
import { ButtonIcon, CheckBox, Component, IconMoreV, Label, Loading, showPopup, Spinner } from '@hcengineering/ui'
|
||||
import notification, {
|
||||
ActivityNotificationViewlet,
|
||||
DisplayInboxNotification,
|
||||
DocNotifyContext,
|
||||
InboxNotification
|
||||
} from '@hcengineering/notification'
|
||||
import { createQuery, getClient, isSpace, isSpaceClass } from '@hcengineering/presentation'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { getDocTitle, getDocIdentifier, Menu } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import core, { Class, Doc, IdMap, Ref, WithLookup } from '@hcengineering/core'
|
||||
import { Class, Doc, IdMap, Ref, WithLookup } from '@hcengineering/core'
|
||||
import chunter from '@hcengineering/chunter'
|
||||
import { personAccountByIdStore } from '@hcengineering/contact-resources'
|
||||
import { Person, PersonAccount } from '@hcengineering/contact'
|
||||
@ -50,17 +50,19 @@
|
||||
const query = createQuery()
|
||||
|
||||
let object: Doc | undefined = undefined
|
||||
let isLoading = true
|
||||
|
||||
$: query.query(
|
||||
value.attachedToClass,
|
||||
{ _id: value.attachedTo, space: isSpaceClass(value.attachedToClass) ? core.space.Space : value.space },
|
||||
value.objectClass,
|
||||
{ _id: value.objectId, space: value.objectSpace },
|
||||
(res) => {
|
||||
object = res[0]
|
||||
isLoading = false
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
|
||||
$: if (object?._id !== value.attachedTo) {
|
||||
$: if (object !== undefined && object?._id !== value.objectId) {
|
||||
object = undefined
|
||||
}
|
||||
|
||||
@ -82,10 +84,7 @@
|
||||
title = res
|
||||
})
|
||||
|
||||
$: presenterMixin = hierarchy.classHierarchyMixin(
|
||||
value.attachedToClass,
|
||||
notification.mixin.NotificationContextPresenter
|
||||
)
|
||||
$: presenterMixin = hierarchy.classHierarchyMixin(value.objectClass, notification.mixin.NotificationContextPresenter)
|
||||
|
||||
let groupedNotifications: Array<InboxNotification[]> = []
|
||||
|
||||
@ -202,7 +201,11 @@
|
||||
dispatch('click', { context: value })
|
||||
}}
|
||||
>
|
||||
{#if object}
|
||||
{#if isLoading}
|
||||
<div class="loading">
|
||||
<Loading />
|
||||
</div>
|
||||
{:else if object}
|
||||
<div class="header">
|
||||
<NotifyContextIcon {value} notifyCount={unreadCount} {object} />
|
||||
|
||||
@ -213,13 +216,13 @@
|
||||
{#if idTitle}
|
||||
{idTitle}
|
||||
{:else}
|
||||
<Label label={hierarchy.getClass(value.attachedToClass).label} />
|
||||
<Label label={hierarchy.getClass(value.objectClass).label} />
|
||||
{/if}
|
||||
<span class="title overflow-label clear-mins" {title}>
|
||||
{#if title}
|
||||
{title}
|
||||
{:else}
|
||||
<Label label={hierarchy.getClass(value.attachedToClass).label} />
|
||||
<Label label={hierarchy.getClass(value.objectClass).label} />
|
||||
{/if}
|
||||
</span>
|
||||
{/if}
|
||||
@ -270,6 +273,35 @@
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="header">
|
||||
<NotifyContextIcon {value} notifyCount={unreadCount} {object} />
|
||||
|
||||
<div class="labels">
|
||||
<Label label={hierarchy.getClass(value.objectClass).label} />
|
||||
</div>
|
||||
|
||||
<div class="actions clear-mins">
|
||||
<div class="flex-center">
|
||||
{#if archivingPromise !== undefined}
|
||||
<Spinner size="small" />
|
||||
{:else}
|
||||
<CheckBox checked={archived} kind="todo" size="medium" on:value={checkContext} />
|
||||
{/if}
|
||||
</div>
|
||||
<ButtonIcon
|
||||
icon={IconMoreV}
|
||||
size="small"
|
||||
kind="tertiary"
|
||||
inheritColor
|
||||
pressed={isActionMenuOpened}
|
||||
on:click={showMenu}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content mt-2">
|
||||
<Label label={notification.string.NoAccessToObject} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@ -369,4 +401,10 @@
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.loading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
||||
|
@ -14,9 +14,9 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { DocNotifyContext } from '@hcengineering/notification'
|
||||
import core, { Doc } from '@hcengineering/core'
|
||||
import { Doc } from '@hcengineering/core'
|
||||
import { getDocLinkTitle, getDocTitle } from '@hcengineering/view-resources'
|
||||
import { createQuery, getClient, isSpaceClass } from '@hcengineering/presentation'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import chunter from '@hcengineering/chunter'
|
||||
import NotifyContextIcon from './NotifyContextIcon.svelte'
|
||||
|
||||
@ -27,13 +27,9 @@
|
||||
|
||||
let object: Doc | undefined
|
||||
|
||||
$: objectQuery.query(
|
||||
value.attachedToClass,
|
||||
{ _id: value.attachedTo, space: isSpaceClass(value.attachedToClass) ? core.space.Space : value.space },
|
||||
(res) => {
|
||||
object = res[0]
|
||||
}
|
||||
)
|
||||
$: objectQuery.query(value.objectClass, { _id: value.objectId, space: value.objectSpace }, (res) => {
|
||||
object = res[0]
|
||||
})
|
||||
|
||||
async function getTitle (object: Doc) {
|
||||
if (object._class === chunter.class.DirectMessage) {
|
||||
|
@ -30,14 +30,14 @@
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
$: iconMixin = hierarchy.classHierarchyMixin(value.attachedToClass, view.mixin.ObjectIcon)
|
||||
$: iconMixin = hierarchy.classHierarchyMixin(value.objectClass, view.mixin.ObjectIcon)
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
{#if iconMixin && object}
|
||||
<Component is={iconMixin.component} props={{ value: object, size }} />
|
||||
{:else if !iconMixin}
|
||||
<Icon icon={classIcon(client, value.attachedToClass) ?? notification.icon.Notifications} {size} />
|
||||
{:else}
|
||||
<Icon icon={classIcon(client, value.objectClass) ?? notification.icon.Notifications} {size} />
|
||||
{/if}
|
||||
|
||||
<div class="notifyMarker">
|
||||
|
@ -188,7 +188,7 @@
|
||||
|
||||
async function updateTabItems (inboxData: InboxData, notifyContexts: DocNotifyContext[]): Promise<void> {
|
||||
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 @@
|
||||
<Component
|
||||
is={selectedComponent}
|
||||
props={{
|
||||
_id: selectedContext.attachedTo,
|
||||
_class: selectedContext.attachedToClass,
|
||||
_id: selectedContext.objectId,
|
||||
_class: selectedContext.objectClass,
|
||||
context: selectedContext,
|
||||
activityMessage: selectedMessage,
|
||||
props: { context: selectedContext }
|
||||
|
@ -104,6 +104,7 @@
|
||||
count={displayData.length}
|
||||
highlightIndex={displayData.findIndex(([context]) => context === selectedContext)}
|
||||
noScroll
|
||||
minHeight="5.625rem"
|
||||
kind="full-size"
|
||||
colorsSchema="lumia"
|
||||
lazy={true}
|
||||
|
@ -90,6 +90,10 @@ export class InboxNotificationsClientImpl implements InboxNotificationsClient {
|
||||
private _contextByDoc = new Map<Ref<Doc>, DocNotifyContext>()
|
||||
|
||||
private constructor () {
|
||||
void this.init()
|
||||
}
|
||||
|
||||
private async init (): Promise<void> {
|
||||
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)
|
||||
}
|
||||
)
|
||||
|
@ -273,9 +273,9 @@ async function updateMeInCollaborators (
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function unsubscribe (object: DocNotifyContext): Promise<void> {
|
||||
export async function unsubscribe (context: DocNotifyContext): Promise<void> {
|
||||
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<void> {
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const { objectId, objectClass } = context
|
||||
|
||||
if (isMentionNotification(notification) && isActivityMessageClass(notification.mentionedInClass)) {
|
||||
const selectedMsg = notification.mentionedIn as Ref<ActivityMessage>
|
||||
|
||||
void navigateToInboxDoc(
|
||||
linkProviders,
|
||||
context.attachedTo,
|
||||
context.attachedToClass,
|
||||
isActivityMessageClass(context.attachedToClass) ? (context.attachedTo as Ref<ActivityMessage>) : undefined,
|
||||
objectId,
|
||||
objectClass,
|
||||
isActivityMessageClass(objectClass) ? (objectId as Ref<ActivityMessage>) : undefined,
|
||||
selectedMsg
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
if (hierarchy.isDerived(context.attachedToClass, activity.class.ActivityMessage)) {
|
||||
if (hierarchy.isDerived(objectClass, activity.class.ActivityMessage)) {
|
||||
const message = (notification as WithLookup<ActivityInboxNotification>)?.$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<ThreadMessage>
|
||||
_id: objectId as Ref<ThreadMessage>
|
||||
},
|
||||
{ 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<ActivityMessage>
|
||||
)
|
||||
void navigateToInboxDoc(linkProviders, objectId, objectClass, undefined, objectId as Ref<ActivityMessage>)
|
||||
return
|
||||
}
|
||||
|
||||
@ -637,18 +626,18 @@ export async function selectInboxContext (
|
||||
|
||||
void navigateToInboxDoc(
|
||||
linkProviders,
|
||||
context.attachedTo,
|
||||
context.attachedToClass,
|
||||
selectedMsg !== undefined ? (context.attachedTo as Ref<ActivityMessage>) : undefined,
|
||||
selectedMsg ?? (context.attachedTo as Ref<ActivityMessage>)
|
||||
objectId,
|
||||
objectClass,
|
||||
selectedMsg !== undefined ? (objectId as Ref<ActivityMessage>) : undefined,
|
||||
selectedMsg ?? (objectId as Ref<ActivityMessage>)
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
void navigateToInboxDoc(
|
||||
linkProviders,
|
||||
context.attachedTo,
|
||||
context.attachedToClass,
|
||||
objectId,
|
||||
objectClass,
|
||||
undefined,
|
||||
(notification as ActivityInboxNotification)?.attachedTo
|
||||
)
|
||||
|
@ -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",
|
||||
|
@ -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<Doc> {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface InboxNotification extends Doc {
|
||||
export interface InboxNotification extends Doc<PersonSpace> {
|
||||
user: Ref<Account>
|
||||
isViewed: boolean
|
||||
|
||||
@ -273,12 +276,12 @@ export type DisplayInboxNotification = DisplayActivityInboxNotification | InboxN
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface DocNotifyContext extends Doc {
|
||||
export interface DocNotifyContext extends Doc<PersonSpace> {
|
||||
user: Ref<Account>
|
||||
|
||||
// Context
|
||||
attachedTo: Ref<Doc>
|
||||
attachedToClass: Ref<Class<Doc>>
|
||||
objectId: Ref<Doc>
|
||||
objectClass: Ref<Class<Doc>>
|
||||
objectSpace: Ref<Space>
|
||||
|
||||
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<NotifyFunc>,
|
||||
|
@ -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<Person>, control: TriggerControl): Promise<PersonAccount | undefined> {
|
||||
@ -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<Employee>, 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<PersonAccount> }, { 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
|
||||
}
|
||||
|
@ -394,7 +394,7 @@ async function OnChannelMembersChanged (tx: TxUpdateDoc<Channel>, 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<Channel>, 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)
|
||||
|
@ -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<Account>,
|
||||
person: Ref<Person>,
|
||||
control: TriggerControl
|
||||
): Promise<TxCUD<PersonSpace>[]> {
|
||||
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<Tx[]> {
|
||||
const acc = TxProcessor.createDoc2Doc(tx as TxCreateDoc<PersonAccount>)
|
||||
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, {
|
||||
|
@ -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<TxCUD<Doc>>,
|
||||
// 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<TxCUD<Doc>>, modifiedOn: tx.modifiedOn, modifiedBy: tx.modifiedBy, isNew: true }
|
||||
// ]
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
@ -167,8 +130,8 @@ async function notifyByEmail (
|
||||
control: TriggerControl,
|
||||
type: Ref<BaseNotificationType>,
|
||||
doc: Doc | undefined,
|
||||
sender: UserInfo,
|
||||
receiver: UserInfo,
|
||||
sender: SenderInfo,
|
||||
receiver: ReceiverInfo,
|
||||
data: InboxNotification
|
||||
): Promise<void> {
|
||||
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<Tx[]> => {
|
||||
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 []
|
||||
}
|
||||
|
||||
|
@ -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<Data<CommonInboxNotification>>,
|
||||
receiver: UserInfo,
|
||||
sender: UserInfo,
|
||||
receiver: ReceiverInfo,
|
||||
sender: SenderInfo,
|
||||
attachedTo: Ref<Doc>,
|
||||
attachedToClass: Ref<Class<Doc>>,
|
||||
space: Ref<Space>,
|
||||
@ -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<Account>,
|
||||
attachedTo: Ref<Doc>,
|
||||
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<Doc>[]).find((tx) => {
|
||||
if (tx._class === core.class.TxCreateDoc && tx.objectClass === notification.class.DocNotifyContext) {
|
||||
const createTx = tx as TxCreateDoc<DocNotifyContext>
|
||||
|
||||
return createTx.attributes.attachedTo === attachedTo && createTx.attributes.user === targetUser
|
||||
}
|
||||
|
||||
return false
|
||||
}) as TxCreateDoc<DocNotifyContext> | undefined
|
||||
|
||||
if (contextTx !== undefined) {
|
||||
return TxProcessor.createDoc2Doc(contextTx)
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
export async function pushInboxNotifications (
|
||||
control: TriggerControl,
|
||||
res: Tx[],
|
||||
target: UserInfo,
|
||||
attachedTo: Ref<Doc>,
|
||||
attachedToClass: Ref<Class<Doc>>,
|
||||
space: Ref<Space>,
|
||||
receiver: ReceiverInfo,
|
||||
objectId: Ref<Doc>,
|
||||
objectClass: Ref<Class<Doc>>,
|
||||
objectSpace: Ref<Space>,
|
||||
contexts: DocNotifyContext[],
|
||||
data: Partial<Data<InboxNotification>>,
|
||||
_class: Ref<Class<InboxNotification>>,
|
||||
modifiedOn: Timestamp,
|
||||
shouldUpdateTimestamp = true
|
||||
): Promise<TxCreateDoc<InboxNotification> | 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<DocNotifyContext>
|
||||
|
||||
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<Doc>,
|
||||
attachedToClass: Ref<Class<Doc>>,
|
||||
data: Data<InboxNotification>,
|
||||
_class: Ref<Class<InboxNotification>>,
|
||||
sender: UserInfo,
|
||||
sender: SenderInfo,
|
||||
_id: Ref<Doc>,
|
||||
cache: Map<Ref<Doc>, Doc> = new Map<Ref<Doc>, Doc>()
|
||||
): Promise<Tx | undefined> {
|
||||
@ -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<PersonAccount>, 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<PersonAccount>, 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<Doc>,
|
||||
control: TriggerControl,
|
||||
res: Tx[],
|
||||
target: UserInfo,
|
||||
sender: UserInfo,
|
||||
receiver: ReceiverInfo,
|
||||
sender: SenderInfo,
|
||||
object: Doc,
|
||||
docNotifyContexts: DocNotifyContext[],
|
||||
activityMessage: ActivityMessage,
|
||||
shouldUpdateTimestamp: boolean
|
||||
): Promise<TxCreateDoc<InboxNotification> | 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<Data<ActivityInboxNotification>> = {
|
||||
...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<void> {
|
||||
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<Doc>,
|
||||
originTx: TxCUD<Doc>,
|
||||
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<PersonAccount>], 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<PersonAccount>[], 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<Doc>, 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) => {
|
||||
|
@ -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<Class<Doc>>, hierarchy: Hierarchy)
|
||||
return hierarchy.classHierarchyMixin(_class, serverNotification.mixin.TextPresenter)
|
||||
}
|
||||
|
||||
async function getSenderName (control: TriggerControl, sender: UserInfo): Promise<string> {
|
||||
async function getSenderName (control: TriggerControl, sender: SenderInfo): Promise<string> {
|
||||
if (sender._id === core.account.System) {
|
||||
return await translate(core.string.System, {})
|
||||
}
|
||||
@ -340,7 +343,7 @@ async function getFallbackNotificationFullfillment (
|
||||
object: Doc,
|
||||
originTx: TxCUD<Doc>,
|
||||
control: TriggerControl,
|
||||
sender: UserInfo,
|
||||
sender: SenderInfo,
|
||||
message?: ActivityMessage
|
||||
): Promise<NotificationContent> {
|
||||
const title: IntlString = notification.string.CommonNotificationTitle
|
||||
@ -419,7 +422,7 @@ function getNotificationPresenter (_class: Ref<Class<Doc>>, hierarchy: Hierarchy
|
||||
export async function getNotificationContent (
|
||||
originTx: TxCUD<Doc>,
|
||||
targetUser: PersonAccount,
|
||||
sender: UserInfo,
|
||||
sender: SenderInfo,
|
||||
object: Doc,
|
||||
control: TriggerControl,
|
||||
message?: ActivityMessage
|
||||
@ -471,19 +474,51 @@ export async function getUsersInfo (
|
||||
ctx: MeasureContext,
|
||||
ids: Ref<PersonAccount>[],
|
||||
control: TriggerControl
|
||||
): Promise<UserInfo[]> {
|
||||
): 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
|
||||
}
|
||||
}
|
||||
|
@ -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<Doc> {
|
||||
presenter: Resource<NotificationContentProvider>
|
||||
}
|
||||
|
||||
export interface UserInfo {
|
||||
export interface ReceiverInfo {
|
||||
_id: Ref<Account>
|
||||
account: PersonAccount
|
||||
person: Employee
|
||||
space: Ref<PersonSpace>
|
||||
}
|
||||
|
||||
export interface SenderInfo {
|
||||
_id: Ref<Account>
|
||||
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<Tx[]>
|
||||
|
||||
export interface NotificationProviderResources extends Doc {
|
||||
|
@ -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<Doc, Request>, 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<PersonAccount>[], control)
|
||||
const senderInfo = usersInfo.find(({ _id }) => _id === tx.modifiedBy) ?? {
|
||||
@ -152,7 +153,10 @@ async function getRequestNotificationTx (tx: TxCollectionCUD<Doc, Request>, 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(
|
||||
|
@ -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<TxCUD<Doc>>,
|
||||
// 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<TxCUD<Doc>>, 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<Tx[]> => {
|
||||
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 []
|
||||
}
|
||||
|
||||
|
@ -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<Doc>, 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<Employee>, 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<PersonAccount>
|
||||
})
|
||||
@ -197,13 +211,12 @@ export async function OnToDoCreate (tx: TxCUD<Doc>, 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<Data<CommonInboxNotification>> = {
|
||||
@ -215,30 +228,18 @@ export async function OnToDoCreate (tx: TxCUD<Doc>, 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
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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) {
|
||||
|
@ -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<Doc>).objectClass === 'contact:class:PersonAccount' ||
|
||||
(tx as TxCUD<Doc>).objectClass === 'contact:class:EmployeeAccount')
|
||||
(tx as TxCUD<Doc>).objectClass === 'contact:class:PersonAccount'
|
||||
)
|
||||
}
|
||||
model.forEach((tx) => (tx.modifiedBy === core.account.System && !isPersonAccount(tx) ? systemTx : userTx).push(tx))
|
||||
|
@ -332,7 +332,7 @@ export async function upgradeModel (
|
||||
(it) =>
|
||||
it.modifiedBy !== core.account.System ||
|
||||
(it as TxCUD<Doc>).objectClass === contact.class.Person ||
|
||||
(it as TxCUD<Doc>).objectClass === 'contact:class:EmployeeAccount'
|
||||
(it as TxCUD<Doc>).objectClass === 'contact:class:PersonAccount'
|
||||
)
|
||||
)
|
||||
]
|
||||
|
@ -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<Account>, message: IntlString, props: Record<string, any> }
|
||||
data: { user: Ref<Account>, space: Ref<PersonSpace>, message: IntlString, props: Record<string, any> }
|
||||
): Promise<void> {
|
||||
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,
|
||||
|
@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 })
|
||||
|
Loading…
Reference in New Issue
Block a user