mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-22 03:14:40 +03:00
TSK-1341 Remove last view (#3115)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
parent
9b04d366e3
commit
ae8fcf8d62
@ -184,10 +184,6 @@ export function createModel (builder: Builder, options = { addApplication: true
|
||||
}
|
||||
})
|
||||
|
||||
builder.mixin(spaceClass, core.class.Class, notification.mixin.SpaceLastEdit, {
|
||||
lastEditField: 'lastMessage'
|
||||
})
|
||||
|
||||
builder.mixin(spaceClass, core.class.Class, view.mixin.ObjectEditor, {
|
||||
editor: chunter.component.EditChannel
|
||||
})
|
||||
@ -197,12 +193,10 @@ export function createModel (builder: Builder, options = { addApplication: true
|
||||
getName: chunter.function.GetDmName
|
||||
})
|
||||
|
||||
builder.mixin(chunter.class.Message, core.class.Class, notification.mixin.TrackedDoc, {})
|
||||
builder.mixin(chunter.class.Message, core.class.Class, notification.mixin.ClassCollaborators, {
|
||||
fields: ['createdBy', 'replies']
|
||||
})
|
||||
|
||||
builder.mixin(chunter.class.ChunterSpace, core.class.Class, notification.mixin.TrackedDoc, {})
|
||||
builder.mixin(chunter.class.DirectMessage, core.class.Class, notification.mixin.ClassCollaborators, {
|
||||
fields: ['members']
|
||||
})
|
||||
|
@ -324,10 +324,6 @@ export function createModel (builder: Builder): void {
|
||||
inlineEditor: contact.component.ContactArrayEditor
|
||||
})
|
||||
|
||||
builder.mixin(contact.class.Contact, core.class.Class, notification.mixin.TrackedDoc, {})
|
||||
|
||||
builder.mixin(contact.class.Channel, core.class.Class, notification.mixin.TrackedDoc, {})
|
||||
|
||||
builder.mixin(contact.class.Contact, core.class.Class, notification.mixin.ClassCollaborators, {
|
||||
fields: []
|
||||
})
|
||||
|
@ -45,7 +45,6 @@ import core, { TAttachedDoc, TDoc } from '@hcengineering/model-core'
|
||||
import presentation from '@hcengineering/model-presentation'
|
||||
import view, { actionTemplates, createAction } from '@hcengineering/model-view'
|
||||
import workbench from '@hcengineering/model-workbench'
|
||||
import notification from '@hcengineering/notification'
|
||||
import tags from '@hcengineering/tags'
|
||||
import document from './plugin'
|
||||
|
||||
@ -187,8 +186,6 @@ export function createModel (builder: Builder): void {
|
||||
component: document.component.CreateDocument
|
||||
})
|
||||
|
||||
builder.mixin(document.class.Document, core.class.Class, notification.mixin.TrackedDoc, {})
|
||||
|
||||
builder.mixin(document.class.Document, core.class.Class, view.mixin.ObjectPanel, {
|
||||
component: document.component.EditDoc
|
||||
})
|
||||
|
@ -20,7 +20,6 @@ import attachment from '@hcengineering/model-attachment'
|
||||
import core, { TAttachedDoc } from '@hcengineering/model-core'
|
||||
import { createAction } from '@hcengineering/model-view'
|
||||
import workbench from '@hcengineering/model-workbench'
|
||||
import notification from '@hcengineering/notification'
|
||||
import setting from '@hcengineering/setting'
|
||||
import view, { Viewlet } from '@hcengineering/view'
|
||||
import inventory from './plugin'
|
||||
@ -160,8 +159,6 @@ export function createModel (builder: Builder): void {
|
||||
inventory.category.Inventory
|
||||
)
|
||||
|
||||
builder.mixin(inventory.class.Product, core.class.Class, notification.mixin.TrackedDoc, {})
|
||||
|
||||
createAction(builder, {
|
||||
label: inventory.string.CreateSubcategory,
|
||||
icon: inventory.icon.Categories,
|
||||
|
@ -37,7 +37,6 @@ import view, { createAction } from '@hcengineering/model-view'
|
||||
import {
|
||||
DocUpdates,
|
||||
EmailNotification,
|
||||
LastView,
|
||||
Notification,
|
||||
NotificationGroup,
|
||||
notificationId,
|
||||
@ -46,8 +45,7 @@ import {
|
||||
NotificationSetting,
|
||||
NotificationStatus,
|
||||
NotificationTemplate,
|
||||
NotificationType,
|
||||
SpaceLastEdit
|
||||
NotificationType
|
||||
} from '@hcengineering/notification'
|
||||
import type { Asset, IntlString } from '@hcengineering/platform'
|
||||
import setting from '@hcengineering/setting'
|
||||
@ -61,13 +59,6 @@ export { notification as default }
|
||||
|
||||
export const DOMAIN_NOTIFICATION = 'notification' as Domain
|
||||
|
||||
@Model(notification.class.LastView, core.class.Doc, DOMAIN_NOTIFICATION)
|
||||
export class TLastView extends TDoc implements LastView {
|
||||
@Prop(TypeRef(core.class.Account), core.string.ModifiedBy)
|
||||
@Index(IndexKind.Indexed)
|
||||
user!: Ref<Account>
|
||||
}
|
||||
|
||||
@Model(notification.class.Notification, core.class.AttachedDoc, DOMAIN_NOTIFICATION)
|
||||
export class TNotification extends TAttachedDoc implements Notification {
|
||||
@Prop(TypeRef(core.class.Tx), 'TX' as IntlString)
|
||||
@ -137,19 +128,11 @@ export class TNotificationSetting extends TPreference implements NotificationSet
|
||||
enabled!: boolean
|
||||
}
|
||||
|
||||
@Mixin(notification.mixin.SpaceLastEdit, core.class.Class)
|
||||
export class TSpaceLastEdit extends TClass implements SpaceLastEdit {
|
||||
lastEditField!: string
|
||||
}
|
||||
|
||||
@Mixin(notification.mixin.ClassCollaborators, core.class.Class)
|
||||
export class TClassCollaborators extends TClass {
|
||||
fields!: string[]
|
||||
}
|
||||
|
||||
@Mixin(notification.mixin.TrackedDoc, core.class.Class)
|
||||
export class TTrackedDoc extends TClass {}
|
||||
|
||||
@Mixin(notification.mixin.Collaborators, core.class.Doc)
|
||||
@UX(notification.string.Collaborators)
|
||||
export class TCollaborators extends TDoc {
|
||||
@ -175,23 +158,20 @@ export class TDocUpdates extends TDoc implements DocUpdates {
|
||||
hidden!: boolean
|
||||
|
||||
attachedToClass!: Ref<Class<Doc>>
|
||||
lastTx!: Ref<TxCUD<Doc>>
|
||||
lastTxTime!: Timestamp
|
||||
lastTx?: Ref<TxCUD<Doc>>
|
||||
lastTxTime?: Timestamp
|
||||
txes!: [Ref<TxCUD<Doc>>, Timestamp][]
|
||||
}
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(
|
||||
TLastView,
|
||||
TNotification,
|
||||
TEmaiNotification,
|
||||
TNotificationType,
|
||||
TNotificationProvider,
|
||||
TNotificationSetting,
|
||||
TNotificationGroup,
|
||||
TSpaceLastEdit,
|
||||
TClassCollaborators,
|
||||
TTrackedDoc,
|
||||
TCollaborators,
|
||||
TDocUpdates,
|
||||
TNotificationObjectPresenter
|
||||
|
@ -13,22 +13,9 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import core, {
|
||||
Account,
|
||||
AttachedDoc,
|
||||
Class,
|
||||
Collection,
|
||||
Data,
|
||||
Doc,
|
||||
DOMAIN_TX,
|
||||
generateId,
|
||||
Ref,
|
||||
TxCollectionCUD,
|
||||
TxOperations,
|
||||
TxRemoveDoc
|
||||
} from '@hcengineering/core'
|
||||
import core, { AttachedDoc, Class, Collection, Data, Doc, DOMAIN_TX, Ref, TxOperations } from '@hcengineering/core'
|
||||
import { MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@hcengineering/model'
|
||||
import notification, { LastView, NotificationType } from '@hcengineering/notification'
|
||||
import notification, { DocUpdates, NotificationType } from '@hcengineering/notification'
|
||||
import { DOMAIN_NOTIFICATION } from '.'
|
||||
|
||||
async function fillNotificationText (client: MigrationClient): Promise<void> {
|
||||
@ -80,79 +67,6 @@ async function createSpace (client: MigrationUpgradeClient): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
async function migrateLastView (client: MigrationClient): Promise<void> {
|
||||
// lets clear last view txes (it should be derived and shouldn't store in tx collection)
|
||||
const txes = await client.find(DOMAIN_TX, {
|
||||
objectClass: notification.class.LastView
|
||||
})
|
||||
for (const tx of txes) {
|
||||
await client.delete(DOMAIN_TX, tx._id)
|
||||
}
|
||||
|
||||
const h = client.hierarchy
|
||||
const docClasses = h.getDescendants(core.class.Doc)
|
||||
const trackedClasses = docClasses.filter((p) => h.hasMixin(h.getClass(p), notification.mixin.TrackedDoc))
|
||||
const allowedClasses = new Set<Ref<Class<Doc>>>()
|
||||
trackedClasses.forEach((p) => h.getDescendants(p).forEach((a) => allowedClasses.add(a)))
|
||||
|
||||
const removeTxes = await client.find<TxRemoveDoc<Doc>>(
|
||||
DOMAIN_TX,
|
||||
{
|
||||
_class: core.class.TxRemoveDoc
|
||||
},
|
||||
{ projection: { objectId: 1 } }
|
||||
)
|
||||
|
||||
const removedDocs: Set<Ref<Doc>> = new Set(removeTxes.map((p) => p.objectId))
|
||||
const removedCollectionTxes = await client.find<TxCollectionCUD<Doc, AttachedDoc>>(
|
||||
DOMAIN_TX,
|
||||
{
|
||||
_class: core.class.TxCollectionCUD,
|
||||
'tx._class': core.class.TxRemoveDoc
|
||||
},
|
||||
{ projection: { tx: 1 } }
|
||||
)
|
||||
removedCollectionTxes.forEach((p) => p.tx.objectId)
|
||||
|
||||
const newLastView: Map<Ref<Account>, LastView> = new Map()
|
||||
let total = 0
|
||||
while (true) {
|
||||
const lastViews = await client.find<LastView>(
|
||||
DOMAIN_NOTIFICATION,
|
||||
{
|
||||
_class: notification.class.LastView,
|
||||
attachedTo: { $exists: true }
|
||||
},
|
||||
{ limit: 10000 }
|
||||
)
|
||||
total += lastViews.length
|
||||
console.log(`migrate ${total} notifications`)
|
||||
if (lastViews.length === 0) break
|
||||
for (const lastView of lastViews) {
|
||||
if (
|
||||
lastView.user !== core.account.System &&
|
||||
allowedClasses.has(lastView.attachedToClass) &&
|
||||
!removedDocs.has(lastView.attachedTo)
|
||||
) {
|
||||
const obj: LastView = newLastView.get(lastView.user) ?? {
|
||||
user: lastView.user,
|
||||
modifiedBy: lastView.user,
|
||||
modifiedOn: Date.now(),
|
||||
_id: generateId(),
|
||||
space: notification.space.Notifications,
|
||||
_class: notification.class.LastView
|
||||
}
|
||||
obj[lastView.attachedTo] = lastView.lastView
|
||||
newLastView.set(lastView.user, obj)
|
||||
}
|
||||
}
|
||||
await Promise.all(lastViews.map((p) => client.delete(DOMAIN_NOTIFICATION, p._id)))
|
||||
}
|
||||
for (const [, lastView] of newLastView) {
|
||||
await client.create(DOMAIN_NOTIFICATION, lastView)
|
||||
}
|
||||
}
|
||||
|
||||
async function fillCollaborators (client: MigrationClient): Promise<void> {
|
||||
const targetClasses = await client.model.findAll(notification.mixin.ClassCollaborators, {})
|
||||
for (const targetClass of targetClasses) {
|
||||
@ -227,6 +141,28 @@ async function createCustomFieldTypes (client: MigrationUpgradeClient): Promise<
|
||||
}
|
||||
}
|
||||
|
||||
async function changeDocUpdatesSpaces (client: MigrationUpgradeClient): Promise<void> {
|
||||
const txop = new TxOperations(client, core.account.System)
|
||||
const docUpdates = await client.findAll(notification.class.DocUpdates, { space: notification.space.Notifications })
|
||||
const map = new Map<Ref<Class<Doc>>, Map<Ref<Doc>, DocUpdates[]>>()
|
||||
for (const docUpdate of docUpdates) {
|
||||
const _class = map.get(docUpdate.attachedToClass) ?? new Map()
|
||||
const arr = _class.get(docUpdate.attachedTo) ?? []
|
||||
arr.push(docUpdate)
|
||||
_class.set(docUpdate.attachedTo, arr)
|
||||
map.set(docUpdate.attachedToClass, _class)
|
||||
}
|
||||
for (const [_class, arr] of map) {
|
||||
const ids = Array.from(arr.keys())
|
||||
const docs = await client.findAll(_class, { _id: { $in: ids } })
|
||||
for (const doc of docs) {
|
||||
const updateDocs = arr.get(doc._id)
|
||||
if (updateDocs === undefined) continue
|
||||
await Promise.all(updateDocs.map(async (p) => await txop.update(p, { space: doc.space })))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function cleanOutdatedSettings (client: MigrationClient): Promise<void> {
|
||||
const res = await client.find(DOMAIN_NOTIFICATION, {
|
||||
_class: notification.class.NotificationSetting
|
||||
@ -240,7 +176,6 @@ export const notificationOperation: MigrateOperation = {
|
||||
async migrate (client: MigrationClient): Promise<void> {
|
||||
await removeSettings(client)
|
||||
await fillNotificationText(client)
|
||||
await migrateLastView(client)
|
||||
await fillCollaborators(client)
|
||||
await fillDocUpdatesHidder(client)
|
||||
await cleanOutdatedSettings(client)
|
||||
@ -248,5 +183,6 @@ export const notificationOperation: MigrateOperation = {
|
||||
async upgrade (client: MigrationUpgradeClient): Promise<void> {
|
||||
await createSpace(client)
|
||||
await createCustomFieldTypes(client)
|
||||
await changeDocUpdatesSpaces(client)
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,6 @@ import { Application } from '@hcengineering/workbench'
|
||||
|
||||
export default mergeIds(notificationId, notification, {
|
||||
string: {
|
||||
LastView: '' as IntlString,
|
||||
PlatformNotification: '' as IntlString,
|
||||
BrowserNotification: '' as IntlString,
|
||||
EmailNotification: '' as IntlString,
|
||||
|
@ -679,10 +679,6 @@ export function createModel (builder: Builder): void {
|
||||
recruit.viewlet.ApplicantDashboard
|
||||
)
|
||||
|
||||
builder.mixin(recruit.class.Applicant, core.class.Class, notification.mixin.TrackedDoc, {})
|
||||
|
||||
builder.mixin(recruit.class.Vacancy, core.class.Class, notification.mixin.TrackedDoc, {})
|
||||
|
||||
builder.mixin(recruit.class.Applicant, core.class.Class, task.mixin.KanbanCard, {
|
||||
card: recruit.component.KanbanCard
|
||||
})
|
||||
|
@ -55,22 +55,10 @@ export function createModel (builder: Builder): void {
|
||||
trigger: serverNotification.trigger.OnBacklinkCreate
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverNotification.trigger.UpdateLastView
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverNotification.trigger.CollaboratorDocHandler
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverNotification.trigger.OnUpdateLastView
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverNotification.trigger.OnAddCollborator
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverNotification.trigger.OnAttributeCreate
|
||||
})
|
||||
|
@ -34,7 +34,7 @@ import {
|
||||
} from '@hcengineering/model'
|
||||
import core, { TAttachedDoc, TClass, TDoc, TSpace, TStatus } from '@hcengineering/model-core'
|
||||
import view, { createAction, template, actionTemplates as viewTemplates } from '@hcengineering/model-view'
|
||||
import notification from '@hcengineering/notification'
|
||||
import {} from '@hcengineering/notification'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import tags from '@hcengineering/tags'
|
||||
import {
|
||||
@ -321,8 +321,6 @@ export function createModel (builder: Builder): void {
|
||||
editor: task.component.TaskHeader
|
||||
})
|
||||
|
||||
builder.mixin(task.class.Task, core.class.Class, notification.mixin.TrackedDoc, {})
|
||||
|
||||
builder.createDoc(
|
||||
view.class.ActionCategory,
|
||||
core.space.Model,
|
||||
|
@ -954,8 +954,6 @@ export function createModel (builder: Builder): void {
|
||||
inlineEditor: tracker.component.ComponentStatusEditor
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.Issue, core.class.Class, notification.mixin.TrackedDoc, {})
|
||||
|
||||
builder.mixin(tracker.class.TypeIssuePriority, core.class.Class, view.mixin.AllValuesFunc, {
|
||||
func: tracker.function.GetAllPriority
|
||||
})
|
||||
|
@ -22,9 +22,8 @@
|
||||
export let size: IconSize = 'small'
|
||||
|
||||
const notificationClient = NotificationClientImpl.getClient()
|
||||
const lastViews = notificationClient.getLastViews()
|
||||
$: lastView = $lastViews[object._id]
|
||||
$: subscribed = lastView !== undefined && lastView !== -1
|
||||
const store = notificationClient.docUpdatesStore
|
||||
$: subscribed = $store.get(object._id) !== undefined
|
||||
</script>
|
||||
|
||||
{#if subscribed}
|
||||
|
@ -15,8 +15,8 @@
|
||||
<script lang="ts">
|
||||
import attachment, { Attachment } from '@hcengineering/attachment'
|
||||
import type { ChunterMessage, Message } from '@hcengineering/chunter'
|
||||
import core, { Ref, Space, Timestamp, WithLookup } from '@hcengineering/core'
|
||||
import { LastView } from '@hcengineering/notification'
|
||||
import core, { Doc, Ref, Space, Timestamp, WithLookup } from '@hcengineering/core'
|
||||
import { DocUpdates } from '@hcengineering/notification'
|
||||
import { NotificationClientImpl } from '@hcengineering/notification-resources'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { location as locationStore } from '@hcengineering/ui'
|
||||
@ -69,11 +69,11 @@
|
||||
}
|
||||
})
|
||||
|
||||
let messages: WithLookup<Message>[] | undefined
|
||||
let messages: WithLookup<Message>[] = []
|
||||
const query = createQuery()
|
||||
|
||||
const notificationClient = NotificationClientImpl.getClient()
|
||||
const lastViews = notificationClient.getLastViews()
|
||||
const docUpdates = notificationClient.docUpdatesStore
|
||||
|
||||
$: updateQuery(space)
|
||||
|
||||
@ -90,8 +90,8 @@
|
||||
},
|
||||
(res) => {
|
||||
messages = res
|
||||
newMessagesPos = newMessagesStart(messages)
|
||||
notificationClient.updateLastView(space, chunter.class.ChunterSpace)
|
||||
newMessagesPos = newMessagesStart(messages, $docUpdates)
|
||||
notificationClient.read(space)
|
||||
},
|
||||
{
|
||||
lookup: {
|
||||
@ -102,22 +102,22 @@
|
||||
)
|
||||
}
|
||||
|
||||
function newMessagesStart (messages: Message[]): number {
|
||||
function newMessagesStart (messages: Message[], docUpdates: Map<Ref<Doc>, DocUpdates>): number {
|
||||
if (space === undefined) return -1
|
||||
const lastView = ($lastViews as any)[space]
|
||||
if (lastView === undefined || lastView === -1) return -1
|
||||
const docUpdate = docUpdates.get(space)
|
||||
const lastView = docUpdate?.txes?.[0]?.[1]
|
||||
if (docUpdate === undefined || lastView === undefined) return -1
|
||||
for (let index = 0; index < messages.length; index++) {
|
||||
const message = messages[index]
|
||||
if (message.createOn > lastView) return index
|
||||
if (message.createOn >= lastView) return index
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
$: markUnread($lastViews)
|
||||
function markUnread (lastViews: LastView) {
|
||||
if (messages === undefined) return
|
||||
const newPos = newMessagesStart(messages)
|
||||
if (newPos !== -1 || newMessagesPos === -1) {
|
||||
$: markUnread(messages, $docUpdates)
|
||||
function markUnread (messages: Message[], docUpdates: Map<Ref<Doc>, DocUpdates>) {
|
||||
const newPos = newMessagesStart(messages, docUpdates)
|
||||
if (newPos !== -1) {
|
||||
newMessagesPos = newPos
|
||||
}
|
||||
}
|
||||
@ -165,7 +165,7 @@
|
||||
}
|
||||
|
||||
let showFixed: boolean | undefined
|
||||
let selectedDate: Timestamp | undefined = messages ? getDay(messages[0].createOn) : undefined
|
||||
let selectedDate: Timestamp | undefined = undefined
|
||||
function handleScroll () {
|
||||
const upperVisible = getFirstVisible()
|
||||
if (upperVisible) {
|
||||
|
@ -18,15 +18,7 @@
|
||||
import type { ChunterSpace, Message, ThreadMessage } from '@hcengineering/chunter'
|
||||
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
|
||||
import { employeeByIdStore } from '@hcengineering/contact-resources'
|
||||
import core, {
|
||||
FindOptions,
|
||||
generateId,
|
||||
getCurrentAccount,
|
||||
IdMap,
|
||||
Ref,
|
||||
SortingOrder,
|
||||
TxFactory
|
||||
} from '@hcengineering/core'
|
||||
import core, { FindOptions, IdMap, Ref, SortingOrder, generateId, getCurrentAccount } from '@hcengineering/core'
|
||||
import { NotificationClientImpl } from '@hcengineering/notification-resources'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Label } from '@hcengineering/ui'
|
||||
@ -97,7 +89,7 @@
|
||||
} else {
|
||||
comments = res.reverse()
|
||||
}
|
||||
notificationClient.updateLastView(id, chunter.class.Message)
|
||||
notificationClient.read(id)
|
||||
},
|
||||
options
|
||||
)
|
||||
@ -130,8 +122,7 @@
|
||||
if (parent === undefined) return
|
||||
const { message, attachments } = event.detail
|
||||
const me = getCurrentAccount()._id
|
||||
const txFactory = new TxFactory(me)
|
||||
const tx = txFactory.createTxCreateDoc<ThreadMessage>(
|
||||
await client.createDoc(
|
||||
chunter.class.ThreadMessage,
|
||||
parent.space,
|
||||
{
|
||||
@ -145,9 +136,6 @@
|
||||
},
|
||||
commentId
|
||||
)
|
||||
tx.attributes.createOn = tx.modifiedOn
|
||||
await notificationClient.updateLastView(_id, chunter.class.Message, tx.modifiedOn, true)
|
||||
await client.tx(tx)
|
||||
|
||||
// Create an backlink to document
|
||||
await createBacklinks(client, parent._id, parent._class, commentId, message)
|
||||
|
@ -16,8 +16,8 @@
|
||||
import attachment, { Attachment } from '@hcengineering/attachment'
|
||||
import { AttachmentRefInput } from '@hcengineering/attachment-resources'
|
||||
import type { ChunterMessage, Message, ThreadMessage } from '@hcengineering/chunter'
|
||||
import core, { Ref, Space, generateId, getCurrentAccount } from '@hcengineering/core'
|
||||
import { LastView } from '@hcengineering/notification'
|
||||
import core, { Doc, Ref, Space, generateId, getCurrentAccount } from '@hcengineering/core'
|
||||
import { DocUpdates } from '@hcengineering/notification'
|
||||
import { NotificationClientImpl } from '@hcengineering/notification-resources'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { IconClose, Label, getCurrentResolvedLocation, navigate } from '@hcengineering/ui'
|
||||
@ -59,7 +59,7 @@
|
||||
})
|
||||
|
||||
const notificationClient = NotificationClientImpl.getClient()
|
||||
const lastViews = notificationClient.getLastViews()
|
||||
const docUpdates = notificationClient.docUpdatesStore
|
||||
|
||||
const lookup = {
|
||||
_id: { attachments: attachment.class.Attachment, reactions: chunter.class.Reaction },
|
||||
@ -101,8 +101,8 @@
|
||||
},
|
||||
(res) => {
|
||||
comments = res
|
||||
newMessagesPos = newMessagesStart(comments, $lastViews)
|
||||
notificationClient.updateLastView(id, chunter.class.Message)
|
||||
newMessagesPos = newMessagesStart(comments, $docUpdates)
|
||||
notificationClient.read(id)
|
||||
},
|
||||
{
|
||||
lookup
|
||||
@ -159,23 +159,25 @@
|
||||
}
|
||||
let comments: ThreadMessage[] = []
|
||||
|
||||
function newMessagesStart (comments: ThreadMessage[], lastViews: LastView): number {
|
||||
const lastView = (lastViews as any)[_id]
|
||||
if (lastView === undefined || lastView === -1) return -1
|
||||
function newMessagesStart (comments: ThreadMessage[], docUpdates: Map<Ref<Doc>, DocUpdates>): number {
|
||||
const docUpdate = docUpdates.get(_id)
|
||||
const lastView = docUpdate?.txes?.[0]?.[1]
|
||||
if (docUpdate === undefined || lastView === undefined) return -1
|
||||
for (let index = 0; index < comments.length; index++) {
|
||||
const comment = comments[index]
|
||||
if (comment.createOn > lastView) return index
|
||||
if (comment.createOn >= lastView) return index
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
$: markUnread($lastViews)
|
||||
function markUnread (lastViews: LastView) {
|
||||
const newPos = newMessagesStart(comments, lastViews)
|
||||
if (newPos !== -1 || newMessagesPos === -1) {
|
||||
$: markUnread(comments, $docUpdates)
|
||||
function markUnread (comments: ThreadMessage[], docUpdates: Map<Ref<Doc>, DocUpdates>) {
|
||||
const newPos = newMessagesStart(comments, docUpdates)
|
||||
if (newPos !== -1) {
|
||||
newMessagesPos = newPos
|
||||
}
|
||||
}
|
||||
|
||||
let newMessagesPos: number = -1
|
||||
let loading = false
|
||||
</script>
|
||||
|
@ -67,12 +67,12 @@ export { CommentPopup, CommentsPresenter }
|
||||
|
||||
async function MarkUnread (object: Message): Promise<void> {
|
||||
const client = NotificationClientImpl.getClient()
|
||||
await client.updateLastView(object.space, chunter.class.ChunterSpace, object.createOn - 1, true)
|
||||
await client.forceRead(object.space, chunter.class.ChunterSpace)
|
||||
}
|
||||
|
||||
async function MarkCommentUnread (object: ThreadMessage): Promise<void> {
|
||||
const client = NotificationClientImpl.getClient()
|
||||
await client.updateLastView(object.attachedTo, object.attachedToClass, object.createOn - 1, true)
|
||||
await client.forceRead(object.attachedTo, object.attachedToClass)
|
||||
}
|
||||
|
||||
async function SubscribeMessage (object: Message): Promise<void> {
|
||||
@ -117,14 +117,20 @@ async function UnsubscribeMessage (object: ChunterMessage): Promise<void> {
|
||||
}
|
||||
}
|
||||
)
|
||||
await notificationClient.unsubscribe(object.attachedTo)
|
||||
const docUpdate = notificationClient.docUpdatesMap.get(object.attachedTo)
|
||||
if (docUpdate !== undefined) {
|
||||
await client.remove(docUpdate)
|
||||
}
|
||||
} else {
|
||||
await client.updateMixin(object._id, object._class, object.space, notification.mixin.Collaborators, {
|
||||
$pull: {
|
||||
collaborators: acc._id
|
||||
}
|
||||
})
|
||||
await notificationClient.unsubscribe(object._id)
|
||||
const docUpdate = notificationClient.docUpdatesMap.get(object._id)
|
||||
if (docUpdate !== undefined) {
|
||||
await client.remove(docUpdate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
import type { Channel, ChannelProvider } from '@hcengineering/contact'
|
||||
import contact from '@hcengineering/contact'
|
||||
import type { AttachedData, Doc, Ref } from '@hcengineering/core'
|
||||
import notification, { LastView } from '@hcengineering/notification'
|
||||
import notification, { DocUpdates } from '@hcengineering/notification'
|
||||
import { Asset, IntlString, getResource } from '@hcengineering/platform'
|
||||
import presentation from '@hcengineering/presentation'
|
||||
import {
|
||||
@ -50,8 +50,8 @@
|
||||
export let focusIndex = -1
|
||||
export let restricted: Ref<ChannelProvider>[] = []
|
||||
|
||||
let lastViews: Writable<LastView | undefined> = writable()
|
||||
getResource(notification.function.GetNotificationClient).then((res) => (lastViews = res().getLastViews()))
|
||||
let docUpdates: Writable<Map<Ref<Doc>, DocUpdates>> = writable(new Map())
|
||||
getResource(notification.function.GetNotificationClient).then((res) => (docUpdates = res().docUpdatesStore))
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
interface Item {
|
||||
@ -70,11 +70,11 @@
|
||||
function getProvider (
|
||||
item: AttachedData<Channel>,
|
||||
map: Map<Ref<ChannelProvider>, ChannelProvider>,
|
||||
lastViews: LastView | undefined
|
||||
docUpdates: Map<Ref<Doc>, DocUpdates>
|
||||
): Item | undefined {
|
||||
const provider = map.get(item.provider)
|
||||
if (provider) {
|
||||
const notification = (item as Channel)._id !== undefined ? isNew(item as Channel, lastViews) : false
|
||||
const notification = (item as Channel)._id !== undefined ? isNew(item as Channel, docUpdates) : false
|
||||
return {
|
||||
label: provider.label,
|
||||
icon: provider.icon as Asset,
|
||||
@ -92,16 +92,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
function isNew (item: Channel, lastViews: LastView | undefined): boolean {
|
||||
if (item.lastMessage === undefined) return false
|
||||
const lastView =
|
||||
(item as Channel)._id !== undefined && lastViews !== undefined ? lastViews[(item as Channel)._id] : undefined
|
||||
return lastView ? lastView < item.lastMessage : (item.items ?? 0) > 0
|
||||
function isNew (item: Channel, docUpdates: Map<Ref<Doc>, DocUpdates>): boolean {
|
||||
const docUpdate = docUpdates.get(item._id)
|
||||
return docUpdate ? docUpdate.txes.length > 0 : (item.items ?? 0) > 0
|
||||
}
|
||||
|
||||
async function update (
|
||||
value: AttachedData<Channel>[] | Channel | null,
|
||||
lastViews: LastView | undefined,
|
||||
docUpdates: Map<Ref<Doc>, DocUpdates>,
|
||||
channelProviders: ChannelProvider[]
|
||||
) {
|
||||
if (value == null) {
|
||||
@ -112,13 +110,13 @@
|
||||
const map = getChannelProviders(channelProviders)
|
||||
if (Array.isArray(value)) {
|
||||
for (const item of value) {
|
||||
const provider = getProvider(item, map, lastViews)
|
||||
const provider = getProvider(item, map, docUpdates)
|
||||
if (provider !== undefined) {
|
||||
result.push(provider)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const provider = getProvider(value, map, lastViews)
|
||||
const provider = getProvider(value, map, docUpdates)
|
||||
if (provider !== undefined) {
|
||||
result.push(provider)
|
||||
}
|
||||
@ -127,7 +125,7 @@
|
||||
updateMenu(displayItems, channelProviders)
|
||||
}
|
||||
|
||||
$: if (value) update(value, $lastViews, $channelProviders)
|
||||
$: if (value) update(value, $docUpdates, $channelProviders)
|
||||
|
||||
let displayItems: Item[] = []
|
||||
let actions: Action[] = []
|
||||
@ -148,7 +146,7 @@
|
||||
icon: pr.icon ?? contact.icon.SocialEdit,
|
||||
label: pr.label,
|
||||
action: async () => {
|
||||
const provider = getProvider({ provider: pr._id, value: '' }, getChannelProviders(providers), $lastViews)
|
||||
const provider = getProvider({ provider: pr._id, value: '' }, getChannelProviders(providers), $docUpdates)
|
||||
if (provider !== undefined) {
|
||||
if (_displayItems.filter((it) => it.provider === pr._id).length === 0) {
|
||||
displayItems = [..._displayItems, provider]
|
||||
|
@ -16,8 +16,8 @@
|
||||
<script lang="ts">
|
||||
import type { Channel, ChannelProvider } from '@hcengineering/contact'
|
||||
import type { AttachedData, Doc, Ref } from '@hcengineering/core'
|
||||
import notification, { LastView } from '@hcengineering/notification'
|
||||
import { Asset, getResource, IntlString } from '@hcengineering/platform'
|
||||
import notification, { DocUpdates } from '@hcengineering/notification'
|
||||
import { Asset, IntlString, getResource } from '@hcengineering/platform'
|
||||
import type { AnyComponent } from '@hcengineering/ui'
|
||||
import { Button } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
@ -31,8 +31,8 @@
|
||||
export let reverse: boolean = false
|
||||
export let integrations: Set<Ref<Doc>> = new Set<Ref<Doc>>()
|
||||
|
||||
let lastViews: Writable<LastView | undefined> = writable()
|
||||
getResource(notification.function.GetNotificationClient).then((res) => (lastViews = res().getLastViews()))
|
||||
let docUpdates: Writable<Map<Ref<Doc>, DocUpdates>> = writable(new Map())
|
||||
getResource(notification.function.GetNotificationClient).then((res) => (docUpdates = res().docUpdatesStore))
|
||||
|
||||
interface Item {
|
||||
label: IntlString
|
||||
@ -48,11 +48,11 @@
|
||||
function getProvider (
|
||||
item: AttachedData<Channel>,
|
||||
map: Map<Ref<ChannelProvider>, ChannelProvider>,
|
||||
lastViews: LastView | undefined
|
||||
docUpdates: Map<Ref<Doc>, DocUpdates>
|
||||
): any | undefined {
|
||||
const provider = map.get(item.provider)
|
||||
if (provider) {
|
||||
const notification = (item as Channel)._id !== undefined ? isNew(item as Channel, lastViews) : false
|
||||
const notification = (item as Channel)._id !== undefined ? isNew(item as Channel, docUpdates) : false
|
||||
return {
|
||||
label: provider.label,
|
||||
icon: provider.icon as Asset,
|
||||
@ -66,16 +66,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
function isNew (item: Channel, lastViews: LastView | undefined): boolean {
|
||||
if (item.lastMessage === undefined) return false
|
||||
const lastView =
|
||||
(item as Channel)._id !== undefined && lastViews !== undefined ? lastViews[(item as Channel)._id] : undefined
|
||||
return lastView ? lastView < item.lastMessage : (item.items ?? 0) > 0
|
||||
function isNew (item: Channel, docUpdates: Map<Ref<Doc>, DocUpdates>): boolean {
|
||||
const docUpdate = docUpdates.get(item._id)
|
||||
return docUpdate ? docUpdate.txes.length > 0 : (item.items ?? 0) > 0
|
||||
}
|
||||
|
||||
async function update (
|
||||
value: AttachedData<Channel>[] | Channel | null,
|
||||
lastViews: LastView | undefined,
|
||||
docUpdates: Map<Ref<Doc>, DocUpdates>,
|
||||
channels: ChannelProvider[]
|
||||
) {
|
||||
if (value === null) {
|
||||
@ -86,13 +84,13 @@
|
||||
const map = getChannelProviders(channels)
|
||||
if (Array.isArray(value)) {
|
||||
for (const item of value) {
|
||||
const provider = getProvider(item, map, lastViews)
|
||||
const provider = getProvider(item, map, docUpdates)
|
||||
if (provider !== undefined) {
|
||||
result.push(provider)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const provider = getProvider(value, map, lastViews)
|
||||
const provider = getProvider(value, map, docUpdates)
|
||||
if (provider !== undefined) {
|
||||
result.push(provider)
|
||||
}
|
||||
@ -100,7 +98,7 @@
|
||||
displayItems = result
|
||||
}
|
||||
|
||||
$: if (value) update(value, $lastViews, $channelProviders)
|
||||
$: if (value) update(value, $docUpdates, $channelProviders)
|
||||
|
||||
let displayItems: Item[] = []
|
||||
let divHTML: HTMLElement
|
||||
|
@ -60,7 +60,6 @@
|
||||
export let embedded = false
|
||||
|
||||
let lastId: Ref<Doc> = _id
|
||||
let lastClass: Ref<Class<Doc>> = _class
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
@ -79,17 +78,15 @@
|
||||
function read (_id: Ref<Doc>) {
|
||||
if (lastId !== _id) {
|
||||
const prev = lastId
|
||||
const prevClass = lastClass
|
||||
lastId = _id
|
||||
lastClass = _class
|
||||
notificationClient.then((client) => client.updateLastView(prev, prevClass))
|
||||
notificationClient.then((client) => client.read(prev))
|
||||
}
|
||||
}
|
||||
|
||||
const currentUser = getCurrentAccount() as EmployeeAccount
|
||||
|
||||
onDestroy(async () => {
|
||||
notificationClient.then((client) => client.updateLastView(_id, _class))
|
||||
notificationClient.then((client) => client.read(_id))
|
||||
})
|
||||
|
||||
let requests: DocumentRequest[] = []
|
||||
|
@ -46,7 +46,7 @@
|
||||
{ attachedTo: channelId },
|
||||
(res) => {
|
||||
messages = res
|
||||
notificationClient.updateLastView(channelId, channel._class, undefined, true)
|
||||
notificationClient.read(channelId)
|
||||
},
|
||||
{ sort: { sendOn: SortingOrder.Descending } }
|
||||
)
|
||||
@ -68,7 +68,7 @@
|
||||
messages: convertMessages(object, channel, selectedMessages, $employeeAccountByIdStore, $employeeByIdStore)
|
||||
}
|
||||
)
|
||||
await notificationClient.updateLastView(channel._id, channel._class, undefined, true)
|
||||
await notificationClient.read(channel._id)
|
||||
clear()
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,7 @@
|
||||
async function selectHandler (e: CustomEvent): Promise<void> {
|
||||
currentMessage = e.detail
|
||||
if (channel !== undefined) {
|
||||
await notificationClient.updateLastView(channel._id, channel._class, undefined, true)
|
||||
await notificationClient.read(channel._id)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,7 @@
|
||||
},
|
||||
objectId
|
||||
)
|
||||
await notificationClient.updateLastView(channel._id, channel._class, undefined, true)
|
||||
await notificationClient.read(channel._id)
|
||||
objectId = generateId()
|
||||
dispatch('close')
|
||||
}
|
||||
|
@ -92,7 +92,7 @@
|
||||
.map((m) => m.trim())
|
||||
.filter((m) => m.length)
|
||||
})
|
||||
await notificationClient.updateLastView(channel._id, channel._class, undefined, true)
|
||||
await notificationClient.read(channel._id)
|
||||
for (const attachment of attachments) {
|
||||
await client.addCollection(
|
||||
attachmentP.class.Attachment,
|
||||
|
@ -1,6 +1,5 @@
|
||||
{
|
||||
"string": {
|
||||
"LastView": "Last View",
|
||||
"Notification": "Notification",
|
||||
"Notifications": "Notifications",
|
||||
"NoNotifications": "No notifications",
|
||||
|
@ -1,6 +1,5 @@
|
||||
{
|
||||
"string": {
|
||||
"LastView": "Последний просмотр",
|
||||
"Notification": "Увдомление",
|
||||
"Notifications": "Уведомления",
|
||||
"NoNotifications": "Нет уведомлений",
|
||||
|
@ -13,8 +13,8 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact, { EmployeeAccount } from '@hcengineering/contact'
|
||||
import { Doc, getCurrentAccount, Ref } from '@hcengineering/core'
|
||||
import { EmployeeAccount } from '@hcengineering/contact'
|
||||
import { getCurrentAccount, Ref } from '@hcengineering/core'
|
||||
import {
|
||||
NotificationProvider,
|
||||
NotificationSetting,
|
||||
@ -26,14 +26,10 @@
|
||||
import { getCurrentLocation, showPanel } from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
import notification from '../plugin'
|
||||
import { NotificationClientImpl } from '../utils'
|
||||
|
||||
const query = createQuery()
|
||||
const settingQuery = createQuery()
|
||||
const providersQuery = createQuery()
|
||||
const notificationClient = NotificationClientImpl.getClient()
|
||||
const lastViews = notificationClient.getLastViews()
|
||||
const lastViewId: Ref<Doc> = ((getCurrentAccount() as EmployeeAccount).employee + 'notification') as Ref<Doc>
|
||||
|
||||
let settingsReceived = false
|
||||
let settings: Map<Ref<NotificationType>, NotificationSetting> = new Map<Ref<NotificationType>, NotificationSetting>()
|
||||
@ -129,16 +125,6 @@
|
||||
alreadyShown.clear()
|
||||
}, 5000)
|
||||
|
||||
const lastView = ($lastViews as any)[lastViewId]
|
||||
if ((lastView ?? notifyInstance.modifiedOn) > 0) {
|
||||
await notificationClient.updateLastView(
|
||||
lastViewId,
|
||||
contact.class.Employee,
|
||||
notifyInstance.modifiedOn,
|
||||
lastView === undefined
|
||||
)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
const notification = new Notification(getCurrentLocation().path[1], {
|
||||
tag: notifyInstance._id,
|
||||
|
@ -24,7 +24,7 @@
|
||||
const store = notificationClient.docUpdatesStore
|
||||
$: docUpdate = $store.get(value._id)
|
||||
|
||||
$: hasNotification = (docUpdate?.txes?.length ?? 0) > 0
|
||||
$: hasNotification = (docUpdate?.txes?.length ?? 0) > 0 && docUpdate?.hidden !== true
|
||||
</script>
|
||||
|
||||
{#if hasNotification}
|
||||
|
@ -14,44 +14,35 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import core, { Account, Class, Doc, getCurrentAccount, Ref, Timestamp } from '@hcengineering/core'
|
||||
import notification, { DocUpdates, LastView, NotificationClient } from '@hcengineering/notification'
|
||||
import { Account, Class, Doc, getCurrentAccount, Ref } from '@hcengineering/core'
|
||||
import notification, { Collaborators, DocUpdates, NotificationClient } from '@hcengineering/notification'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { get, writable, Writable } from 'svelte/store'
|
||||
import { writable } from 'svelte/store'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export class NotificationClientImpl implements NotificationClient {
|
||||
protected static _instance: NotificationClientImpl | undefined = undefined
|
||||
private readonly lastViewsStore = writable<LastView>()
|
||||
readonly docUpdatesStore = writable<Map<Ref<Doc>, DocUpdates>>(new Map())
|
||||
docUpdatesMap: Map<Ref<Doc>, DocUpdates> = new Map()
|
||||
readonly docUpdates = writable<DocUpdates[]>([])
|
||||
|
||||
private readonly docUpdatesQuery = createQuery(true)
|
||||
|
||||
private readonly lastViewQuery = createQuery()
|
||||
private readonly user: Ref<Account>
|
||||
|
||||
private constructor () {
|
||||
this.user = getCurrentAccount()._id
|
||||
this.lastViewQuery.query(notification.class.LastView, { user: this.user }, (result) => {
|
||||
this.lastViewsStore.set(result[0])
|
||||
if (result[0] === undefined) {
|
||||
const client = getClient()
|
||||
const u = client.txFactory.createTxCreateDoc(notification.class.LastView, notification.space.Notifications, {
|
||||
user: this.user
|
||||
})
|
||||
u.space = core.space.DerivedTx
|
||||
void client.tx(u)
|
||||
}
|
||||
})
|
||||
this.docUpdatesQuery.query(
|
||||
notification.class.DocUpdates,
|
||||
{
|
||||
user: this.user
|
||||
},
|
||||
(result) => {
|
||||
this.docUpdatesStore.set(new Map(result.map((p) => [p.attachedTo, p])))
|
||||
this.docUpdates.set(result)
|
||||
this.docUpdatesMap = new Map(result.map((p) => [p.attachedTo, p]))
|
||||
this.docUpdatesStore.set(this.docUpdatesMap)
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -67,46 +58,49 @@ export class NotificationClientImpl implements NotificationClient {
|
||||
return NotificationClientImpl._instance
|
||||
}
|
||||
|
||||
getLastViews (): Writable<LastView> {
|
||||
return this.lastViewsStore
|
||||
}
|
||||
|
||||
async updateLastView (
|
||||
_id: Ref<Doc>,
|
||||
_class: Ref<Class<Doc>>,
|
||||
time?: Timestamp,
|
||||
force: boolean = false
|
||||
): Promise<void> {
|
||||
async read (_id: Ref<Doc>): Promise<void> {
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const mixin = hierarchy.classHierarchyMixin(_class, notification.mixin.TrackedDoc)
|
||||
if (mixin === undefined) return
|
||||
const lastView = time ?? new Date().getTime()
|
||||
const obj = get(this.lastViewsStore)
|
||||
if (obj !== undefined) {
|
||||
const current = obj[_id] as Timestamp | undefined
|
||||
if (current !== undefined || force) {
|
||||
if (current === -1 && !force) return
|
||||
if (force || (current ?? 0) < lastView) {
|
||||
const u = client.txFactory.createTxUpdateDoc(obj._class, obj.space, obj._id, {
|
||||
[_id]: lastView
|
||||
})
|
||||
u.space = core.space.DerivedTx
|
||||
await client.tx(u)
|
||||
}
|
||||
}
|
||||
const docUpdate = this.docUpdatesMap.get(_id)
|
||||
if (docUpdate !== undefined) {
|
||||
await client.update(docUpdate, { txes: [] })
|
||||
}
|
||||
}
|
||||
|
||||
async unsubscribe (_id: Ref<Doc>): Promise<void> {
|
||||
async forceRead (_id: Ref<Doc>, _class: Ref<Class<Doc>>): Promise<void> {
|
||||
const client = getClient()
|
||||
const obj = get(this.lastViewsStore)
|
||||
if (obj !== undefined) {
|
||||
const u = client.txFactory.createTxUpdateDoc(obj._class, obj.space, obj._id, {
|
||||
[_id]: -1
|
||||
})
|
||||
u.space = core.space.DerivedTx
|
||||
await client.tx(u)
|
||||
const docUpdate = this.docUpdatesMap.get(_id)
|
||||
if (docUpdate !== undefined) {
|
||||
await client.update(docUpdate, { txes: [] })
|
||||
} else {
|
||||
const doc = await client.findOne(_class, { _id })
|
||||
if (doc !== undefined) {
|
||||
const hiearachy = client.getHierarchy()
|
||||
const collab = hiearachy.as<Doc, Collaborators>(doc, notification.mixin.Collaborators)
|
||||
if (collab.collaborators === undefined) {
|
||||
await client.createMixin<Doc, Collaborators>(
|
||||
collab._id,
|
||||
collab._class,
|
||||
collab.space,
|
||||
notification.mixin.Collaborators,
|
||||
{
|
||||
collaborators: [this.user]
|
||||
}
|
||||
)
|
||||
} else if (!collab.collaborators.includes(this.user)) {
|
||||
await client.updateMixin(collab._id, collab._class, collab.space, notification.mixin.Collaborators, {
|
||||
$push: {
|
||||
collaborators: this.user
|
||||
}
|
||||
})
|
||||
}
|
||||
await client.createDoc(notification.class.DocUpdates, doc.space, {
|
||||
attachedTo: _id,
|
||||
attachedToClass: _class,
|
||||
user: this.user,
|
||||
hidden: true,
|
||||
txes: []
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,14 +34,6 @@ import { Writable } from './types'
|
||||
import { Preference } from '@hcengineering/preference'
|
||||
export * from './types'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface LastView extends Doc {
|
||||
user: Ref<Account>
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -135,20 +127,6 @@ export interface NotificationSetting extends Preference {
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface SpaceLastEdit extends Class<Doc> {
|
||||
lastEditField: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface AnotherUserNotifications extends Class<Doc> {
|
||||
fields: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -156,11 +134,6 @@ export interface ClassCollaborators extends Class<Doc> {
|
||||
fields: string[] // Ref<Account> | Ref<Employee> | Ref<Account>[] | Ref<Employee>[]
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface TrackedDoc extends Class<Doc> {}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -183,8 +156,8 @@ export interface DocUpdates extends Doc {
|
||||
attachedTo: Ref<Doc>
|
||||
attachedToClass: Ref<Class<Doc>>
|
||||
hidden: boolean
|
||||
lastTx: Ref<TxCUD<Doc>>
|
||||
lastTxTime: Timestamp
|
||||
lastTx?: Ref<TxCUD<Doc>>
|
||||
lastTxTime?: Timestamp
|
||||
txes: [Ref<TxCUD<Doc>>, Timestamp][]
|
||||
}
|
||||
|
||||
@ -198,9 +171,9 @@ export const notificationId = 'notification' as Plugin
|
||||
*/
|
||||
export interface NotificationClient {
|
||||
docUpdatesStore: Writable<Map<Ref<Doc>, DocUpdates>>
|
||||
getLastViews: () => Writable<LastView>
|
||||
updateLastView: (_id: Ref<Doc>, _class: Ref<Class<Doc>>, time?: Timestamp, force?: boolean) => Promise<void>
|
||||
unsubscribe: (_id: Ref<Doc>) => Promise<void>
|
||||
docUpdates: Writable<DocUpdates[]>
|
||||
read: (_id: Ref<Doc>) => Promise<void>
|
||||
forceRead: (_id: Ref<Doc>, _class: Ref<Class<Doc>>, space: Ref<Space>) => Promise<void>
|
||||
}
|
||||
|
||||
/**
|
||||
@ -213,15 +186,11 @@ export type NotificationClientFactoy = () => NotificationClient
|
||||
*/
|
||||
const notification = plugin(notificationId, {
|
||||
mixin: {
|
||||
SpaceLastEdit: '' as Ref<Mixin<SpaceLastEdit>>,
|
||||
AnotherUserNotifications: '' as Ref<Mixin<AnotherUserNotifications>>,
|
||||
ClassCollaborators: '' as Ref<Mixin<ClassCollaborators>>,
|
||||
Collaborators: '' as Ref<Mixin<Collaborators>>,
|
||||
TrackedDoc: '' as Ref<Mixin<TrackedDoc>>,
|
||||
NotificationObjectPresenter: '' as Ref<Mixin<NotificationObjectPresenter>>
|
||||
},
|
||||
class: {
|
||||
LastView: '' as Ref<Class<LastView>>,
|
||||
Notification: '' as Ref<Class<Notification>>,
|
||||
EmailNotification: '' as Ref<Class<EmailNotification>>,
|
||||
NotificationType: '' as Ref<Class<NotificationType>>,
|
||||
|
@ -44,7 +44,7 @@
|
||||
const notificationClient = getResource(notification.function.GetNotificationClient).then((res) => res())
|
||||
|
||||
onDestroy(async () => {
|
||||
notificationClient.then((client) => client.updateLastView(_id, recruit.class.Vacancy))
|
||||
notificationClient.then((client) => client.read(_id))
|
||||
})
|
||||
|
||||
const client = getClient()
|
||||
@ -57,7 +57,7 @@
|
||||
const prev = lastId
|
||||
lastId = _id
|
||||
if (prev) {
|
||||
notificationClient.then((client) => client.updateLastView(prev, recruit.class.Vacancy))
|
||||
notificationClient.then((client) => client.read(prev))
|
||||
}
|
||||
query.query(recruit.class.Vacancy, { _id }, (result) => {
|
||||
object = result[0] as Required<Vacancy>
|
||||
|
@ -110,7 +110,7 @@
|
||||
(res) => {
|
||||
messages = res.reverse()
|
||||
if (channel !== undefined) {
|
||||
notificationClient.updateLastView(channel._id, channel._class, undefined, true)
|
||||
notificationClient.read(channel._id)
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -171,7 +171,7 @@
|
||||
}
|
||||
)
|
||||
if (channel !== undefined) {
|
||||
await notificationClient.updateLastView(channel._id, channel._class, channel.modifiedOn, true)
|
||||
await notificationClient.read(channel._id)
|
||||
}
|
||||
clear()
|
||||
}
|
||||
|
@ -47,7 +47,6 @@
|
||||
export let embedded = false
|
||||
|
||||
let lastId: Ref<Doc> = _id
|
||||
let lastClass: Ref<Class<Doc>> = _class
|
||||
const queryClient = createQuery()
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
@ -66,15 +65,13 @@
|
||||
function read (_id: Ref<Doc>) {
|
||||
if (lastId !== _id) {
|
||||
const prev = lastId
|
||||
const prevClass = lastClass
|
||||
lastId = _id
|
||||
lastClass = _class
|
||||
notificationClient.then((client) => client.updateLastView(prev, prevClass))
|
||||
notificationClient.then((client) => client.read(prev))
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(async () => {
|
||||
notificationClient.then((client) => client.updateLastView(_id, _class))
|
||||
notificationClient.then((client) => client.read(_id))
|
||||
})
|
||||
|
||||
$: _id &&
|
||||
|
@ -46,7 +46,6 @@
|
||||
export let _class: Ref<Class<IssueTemplate>>
|
||||
|
||||
let lastId: Ref<Doc> = _id
|
||||
let lastClass: Ref<Class<Doc>> = _class
|
||||
const query = createQuery()
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
@ -65,15 +64,13 @@
|
||||
function read (_id: Ref<Doc>) {
|
||||
if (lastId !== _id) {
|
||||
const prev = lastId
|
||||
const prevClass = lastClass
|
||||
lastId = _id
|
||||
lastClass = _class
|
||||
notificationClient.then((client) => client.updateLastView(prev, prevClass))
|
||||
notificationClient.then((client) => client.read(prev))
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(async () => {
|
||||
notificationClient.then((client) => client.updateLastView(_id, _class))
|
||||
notificationClient.then((client) => client.read(_id))
|
||||
})
|
||||
|
||||
$: _id &&
|
||||
|
@ -44,7 +44,6 @@
|
||||
|
||||
let realObjectClass: Ref<Class<Doc>> = _class
|
||||
let lastId: Ref<Doc> = _id
|
||||
let lastClass: Ref<Class<Doc>> = _class
|
||||
let object: Doc
|
||||
|
||||
const client = getClient()
|
||||
@ -55,15 +54,13 @@
|
||||
function read (_id: Ref<Doc>) {
|
||||
if (lastId !== _id) {
|
||||
const prev = lastId
|
||||
const prevClass = lastClass
|
||||
lastId = _id
|
||||
lastClass = _class
|
||||
notificationClient.then((client) => client.updateLastView(prev, prevClass))
|
||||
notificationClient.then((client) => client.read(prev))
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(async () => {
|
||||
notificationClient.then((client) => client.updateLastView(_id, _class))
|
||||
notificationClient.then((client) => client.read(_id))
|
||||
})
|
||||
|
||||
const query = createQuery()
|
||||
|
@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import type { Class, Doc, Ref, Space } from '@hcengineering/core'
|
||||
import core from '@hcengineering/core'
|
||||
import notification, { LastView } from '@hcengineering/notification'
|
||||
import { DocUpdates } from '@hcengineering/notification'
|
||||
import { NotificationClientImpl } from '@hcengineering/notification-resources'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import preference from '@hcengineering/preference'
|
||||
@ -98,20 +98,13 @@
|
||||
}
|
||||
|
||||
const notificationClient = NotificationClientImpl.getClient()
|
||||
const lastViews = notificationClient.getLastViews()
|
||||
const docUpdates = notificationClient.docUpdatesStore
|
||||
const hierarchy = client.getHierarchy()
|
||||
$: clazz = hierarchy.getClass(model.spaceClass)
|
||||
$: lastEditMixin = hierarchy.as(clazz, notification.mixin.SpaceLastEdit)
|
||||
|
||||
function isChanged (space: Space, lastViews: LastView): boolean {
|
||||
const field = lastEditMixin?.lastEditField
|
||||
const lastView = lastViews[space._id]
|
||||
if (lastView === undefined || lastView === -1) return false
|
||||
if (field === undefined) return false
|
||||
const value = (space as any)[field]
|
||||
if (isNaN(value)) return false
|
||||
|
||||
return lastView < value
|
||||
function isChanged (space: Space, docUpdates: Map<Ref<Doc>, DocUpdates>): boolean {
|
||||
const update = docUpdates.get(space._id)
|
||||
if (update === undefined) return false
|
||||
return update.txes.length > 0 && update.hidden !== true
|
||||
}
|
||||
|
||||
function getParentActions (): Action[] {
|
||||
@ -147,7 +140,7 @@
|
||||
icon={classIcon(client, space._class)}
|
||||
selected={deselect ? false : currentSpace === space._id}
|
||||
actions={() => getActions(space)}
|
||||
bold={isChanged(space, $lastViews)}
|
||||
bold={isChanged(space, $docUpdates)}
|
||||
/>
|
||||
{/await}
|
||||
</NavLink>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import type { Doc, Ref, Space } from '@hcengineering/core'
|
||||
import core from '@hcengineering/core'
|
||||
import notification, { LastView } from '@hcengineering/notification'
|
||||
import { DocUpdates } from '@hcengineering/notification'
|
||||
import { NotificationClientImpl } from '@hcengineering/notification-resources'
|
||||
import { IntlString, getResource } from '@hcengineering/platform'
|
||||
import preference from '@hcengineering/preference'
|
||||
@ -74,20 +74,12 @@
|
||||
}
|
||||
|
||||
const notificationClient = NotificationClientImpl.getClient()
|
||||
const lastViews = notificationClient.getLastViews()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const docUpdates = notificationClient.docUpdatesStore
|
||||
|
||||
function isChanged (space: Space, lastViews: LastView): boolean {
|
||||
const clazz = hierarchy.getClass(space._class)
|
||||
const lastEditMixin = hierarchy.as(clazz, notification.mixin.SpaceLastEdit)
|
||||
const field = lastEditMixin?.lastEditField
|
||||
const lastView = lastViews[space._id]
|
||||
if (lastView === undefined || lastView === -1) return false
|
||||
if (field === undefined) return false
|
||||
const value = (space as any)[field]
|
||||
if (isNaN(value)) return false
|
||||
|
||||
return lastView < value
|
||||
function isChanged (space: Space, docUpdates: Map<Ref<Doc>, DocUpdates>): boolean {
|
||||
const update = docUpdates.get(space._id)
|
||||
if (update === undefined) return false
|
||||
return update.txes.length > 0 && update.hidden !== true
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -102,7 +94,7 @@
|
||||
icon={classIcon(client, space._class)}
|
||||
selected={currentSpace === space._id}
|
||||
actions={() => getActions(space)}
|
||||
bold={isChanged(space, $lastViews)}
|
||||
bold={isChanged(space, $docUpdates)}
|
||||
/>
|
||||
</NavLink>
|
||||
{/await}
|
||||
|
@ -26,7 +26,6 @@ import core, {
|
||||
Hierarchy,
|
||||
Ref,
|
||||
Tx,
|
||||
TxCollectionCUD,
|
||||
TxCreateDoc,
|
||||
TxCUD,
|
||||
TxProcessor,
|
||||
@ -192,84 +191,11 @@ async function ThreadMessageDelete (tx: Tx, control: TriggerControl): Promise<Tx
|
||||
return [updateTx]
|
||||
}
|
||||
|
||||
async function MessageCreate (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
const hierarchy = control.hierarchy
|
||||
const actualTx = TxProcessor.extractTx(tx)
|
||||
if (actualTx._class !== core.class.TxCreateDoc) return []
|
||||
const doc = TxProcessor.createDoc2Doc(actualTx as TxCreateDoc<Doc>)
|
||||
if (!hierarchy.isDerived(doc._class, chunter.class.Message)) {
|
||||
return []
|
||||
}
|
||||
|
||||
const message = doc as Message
|
||||
|
||||
const channel = (
|
||||
await control.findAll(
|
||||
chunter.class.ChunterSpace,
|
||||
{
|
||||
_id: message.space
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
)[0]
|
||||
|
||||
if (channel?.lastMessage === undefined || channel.lastMessage < message.createOn) {
|
||||
const res = control.txFactory.createTxUpdateDoc<ChunterSpace>(channel._class, channel.space, channel._id, {
|
||||
lastMessage: message.createOn
|
||||
})
|
||||
return [res]
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
async function MessageDelete (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
const hierarchy = control.hierarchy
|
||||
|
||||
const rmTx = TxProcessor.extractTx(tx) as TxCollectionCUD<ChunterSpace, Message>
|
||||
if (rmTx._class !== core.class.TxRemoveDoc) return []
|
||||
if (!hierarchy.isDerived(rmTx.objectClass, chunter.class.Message)) {
|
||||
return []
|
||||
}
|
||||
|
||||
const message = control.removedMap.get(rmTx.objectId) as Message
|
||||
|
||||
if (message === undefined) {
|
||||
return []
|
||||
}
|
||||
|
||||
const channel = (
|
||||
await control.findAll(
|
||||
chunter.class.ChunterSpace,
|
||||
{
|
||||
_id: message.space
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
)[0]
|
||||
|
||||
if (channel?.lastMessage === message.createOn) {
|
||||
const messages = await control.findAll(chunter.class.Message, {
|
||||
attachedTo: channel._id
|
||||
})
|
||||
const lastMessageDate = messages.reduce((maxDate, mess) => (mess.createOn > maxDate ? mess.createOn : maxDate), 0)
|
||||
|
||||
const updateTx = control.txFactory.createTxUpdateDoc<ChunterSpace>(channel._class, channel.space, channel._id, {
|
||||
lastMessage: lastMessageDate > 0 ? lastMessageDate : undefined
|
||||
})
|
||||
|
||||
return [updateTx]
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function ChunterTrigger (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
const promises = [
|
||||
MessageCreate(tx, control),
|
||||
MessageDelete(tx, control),
|
||||
ThreadMessageCreate(tx, control),
|
||||
ThreadMessageDelete(tx, control),
|
||||
CommentCreate(tx as TxCUD<Doc>, control)
|
||||
|
@ -100,7 +100,7 @@ export async function OnMessageCreate (tx: Tx, control: TriggerControl): Promise
|
||||
}
|
||||
if (docs.length === 0) {
|
||||
res.push(
|
||||
control.txFactory.createTxCreateDoc(notification.class.DocUpdates, notification.space.Notifications, {
|
||||
control.txFactory.createTxCreateDoc(notification.class.DocUpdates, channel.space, {
|
||||
user: tx.modifiedBy,
|
||||
attachedTo: channel._id,
|
||||
attachedToClass: channel._class,
|
||||
|
@ -45,7 +45,6 @@ import notification, {
|
||||
Collaborators,
|
||||
DocUpdates,
|
||||
EmailNotification,
|
||||
LastView,
|
||||
NotificationProvider,
|
||||
NotificationType
|
||||
} from '@hcengineering/notification'
|
||||
@ -54,7 +53,6 @@ import type { TriggerControl } from '@hcengineering/server-core'
|
||||
import serverNotification, {
|
||||
HTMLPresenter,
|
||||
TextPresenter,
|
||||
createLastViewTx,
|
||||
getEmployeeAccount,
|
||||
getEmployeeAccountById
|
||||
} from '@hcengineering/server-notification'
|
||||
@ -254,62 +252,6 @@ async function getEmailNotificationTx (
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function UpdateLastView (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
const actualTx = TxProcessor.extractTx(tx)
|
||||
if (actualTx._class !== core.class.TxRemoveDoc) {
|
||||
return []
|
||||
}
|
||||
|
||||
if ((actualTx as TxCUD<Doc>).objectClass === notification.class.LastView) {
|
||||
return []
|
||||
}
|
||||
|
||||
const result: Tx[] = []
|
||||
|
||||
const removeTx = actualTx as TxRemoveDoc<Doc>
|
||||
const lastViews = await control.findAll(notification.class.LastView, { [removeTx.objectId]: { $exists: true } })
|
||||
for (const lastView of lastViews) {
|
||||
const clearTx = control.txFactory.createTxUpdateDoc(lastView._class, lastView.space, lastView._id, {
|
||||
$unset: {
|
||||
[removeTx.objectId]: ''
|
||||
}
|
||||
})
|
||||
result.push(clearTx)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function OnUpdateLastView (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
const actualTx = TxProcessor.extractTx(tx) as TxUpdateDoc<LastView>
|
||||
if (actualTx._class !== core.class.TxUpdateDoc) return []
|
||||
if (actualTx.objectClass !== notification.class.LastView) return []
|
||||
const result: Tx[] = []
|
||||
const lastView = (await control.findAll(notification.class.LastView, { _id: actualTx.objectId }))[0]
|
||||
if (lastView === undefined) return result
|
||||
for (const key in actualTx.operations) {
|
||||
const docs = await control.findAll(notification.class.DocUpdates, {
|
||||
attachedTo: key as Ref<Doc>,
|
||||
user: lastView.user
|
||||
})
|
||||
for (const doc of docs) {
|
||||
const txes = doc.txes.filter((p) => p[1] > actualTx.operations[key])
|
||||
result.push(
|
||||
control.txFactory.createTxUpdateDoc(doc._class, doc.space, doc._id, {
|
||||
txes
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
function getBacklink (ptx: TxCollectionCUD<Doc, Backlink>): Backlink {
|
||||
return TxProcessor.createDoc2Doc(ptx.tx as TxCreateDoc<Backlink>)
|
||||
}
|
||||
@ -493,7 +435,7 @@ async function getNotificationTxes (
|
||||
const current = docUpdates.find((p) => p.user === target)
|
||||
if (current === undefined) {
|
||||
res.push(
|
||||
control.txFactory.createTxCreateDoc(notification.class.DocUpdates, notification.space.Notifications, {
|
||||
control.txFactory.createTxCreateDoc(notification.class.DocUpdates, object.space, {
|
||||
user: target,
|
||||
attachedTo: object._id,
|
||||
attachedToClass: object._class,
|
||||
@ -709,6 +651,17 @@ function isMixinTx (tx: TxUpdateDoc<Doc> | TxMixin<Doc, Doc>): tx is TxMixin<Doc
|
||||
return tx._class === core.class.TxMixin
|
||||
}
|
||||
|
||||
async function changeSpaceTxes (control: TriggerControl, tx: TxUpdateDoc<Doc> | TxMixin<Doc, Doc>): Promise<Tx[]> {
|
||||
if (tx._class !== core.class.TxUpdateDoc) return []
|
||||
const ctx = tx as TxUpdateDoc<Doc>
|
||||
if (ctx.operations.space === undefined) return []
|
||||
const docUpdates = await control.findAll(notification.class.DocUpdates, { attachedTo: tx.objectId })
|
||||
|
||||
return docUpdates.map((value) =>
|
||||
control.txFactory.createTxUpdateDoc(value._class, value.space, value._id, { space: ctx.operations.space })
|
||||
)
|
||||
}
|
||||
|
||||
async function updateCollaboratorDoc (
|
||||
tx: TxUpdateDoc<Doc> | TxMixin<Doc, Doc>,
|
||||
control: TriggerControl,
|
||||
@ -750,47 +703,11 @@ async function updateCollaboratorDoc (
|
||||
|
||||
res = res.concat(await getSpaceCollabTxes(control, doc, tx, originTx))
|
||||
|
||||
res = res.concat(await changeSpaceTxes(control, tx))
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function OnAddCollborator (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
const result: Tx[] = []
|
||||
const actualTx = TxProcessor.extractTx(tx) as TxMixin<Doc, Collaborators>
|
||||
|
||||
if (actualTx._class !== core.class.TxMixin) return []
|
||||
if (actualTx.mixin !== notification.mixin.Collaborators) return []
|
||||
if (actualTx.attributes.collaborators !== undefined) {
|
||||
for (const collab of actualTx.attributes.collaborators) {
|
||||
const resTx = await createLastViewTx(control.findAll, actualTx.objectId, collab)
|
||||
if (resTx !== undefined) {
|
||||
result.push(resTx)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (actualTx.attributes.$push?.collaborators !== undefined) {
|
||||
const collab = actualTx.attributes.$push?.collaborators
|
||||
if (typeof collab === 'object') {
|
||||
if ('$each' in collab) {
|
||||
for (const collaborator of collab.$each) {
|
||||
const resTx = await createLastViewTx(control.findAll, actualTx.objectId, collaborator)
|
||||
if (resTx !== undefined) {
|
||||
result.push(resTx)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const resTx = await createLastViewTx(control.findAll, actualTx.objectId, collab)
|
||||
if (resTx !== undefined) {
|
||||
result.push(resTx)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -897,9 +814,6 @@ export default async () => ({
|
||||
trigger: {
|
||||
OnBacklinkCreate,
|
||||
CollaboratorDocHandler: collaboratorDocHandler,
|
||||
OnUpdateLastView,
|
||||
UpdateLastView,
|
||||
OnAddCollborator,
|
||||
OnAttributeCreate,
|
||||
OnAttributeUpdate
|
||||
},
|
||||
|
@ -15,8 +15,8 @@
|
||||
//
|
||||
|
||||
import contact, { Employee, EmployeeAccount } from '@hcengineering/contact'
|
||||
import { Account, Class, Doc, Mixin, Ref, Tx, TxCreateDoc, TxFactory, TxUpdateDoc } from '@hcengineering/core'
|
||||
import notification, { LastView, NotificationType } from '@hcengineering/notification'
|
||||
import { Account, Class, Doc, Mixin, Ref, Tx } from '@hcengineering/core'
|
||||
import { NotificationType } from '@hcengineering/notification'
|
||||
import { Plugin, Resource, plugin } from '@hcengineering/platform'
|
||||
import type { TriggerControl, TriggerFunc } from '@hcengineering/server-core'
|
||||
|
||||
@ -25,42 +25,6 @@ import type { TriggerControl, TriggerFunc } from '@hcengineering/server-core'
|
||||
*/
|
||||
export const serverNotificationId = 'server-notification' as Plugin
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function getUpdateLastViewTx (
|
||||
findAll: TriggerControl['findAll'],
|
||||
attachedTo: Ref<Doc>,
|
||||
lastView: number,
|
||||
user: Ref<Account>
|
||||
): Promise<TxUpdateDoc<LastView> | TxCreateDoc<LastView> | undefined> {
|
||||
const current = (
|
||||
await findAll(
|
||||
notification.class.LastView,
|
||||
{
|
||||
user
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
)[0]
|
||||
const factory = new TxFactory(user, true)
|
||||
if (current !== undefined) {
|
||||
if (current[attachedTo] === -1 || current[attachedTo] >= lastView) {
|
||||
return
|
||||
}
|
||||
const u = factory.createTxUpdateDoc(current._class, current.space, current._id, {
|
||||
[attachedTo]: lastView
|
||||
})
|
||||
return u
|
||||
} else {
|
||||
const u = factory.createTxCreateDoc(notification.class.LastView, notification.space.Notifications, {
|
||||
user,
|
||||
[attachedTo]: lastView
|
||||
})
|
||||
return u
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -115,38 +79,6 @@ export async function getEmployee (employee: Ref<Employee>, control: TriggerCont
|
||||
return account
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function createLastViewTx (
|
||||
findAll: TriggerControl['findAll'],
|
||||
attachedTo: Ref<Doc>,
|
||||
user: Ref<Account>
|
||||
): Promise<TxCreateDoc<LastView> | TxUpdateDoc<LastView> | undefined> {
|
||||
const current = (
|
||||
await findAll(
|
||||
notification.class.LastView,
|
||||
{
|
||||
user
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
)[0]
|
||||
const factory = new TxFactory(user, true)
|
||||
if (current === undefined) {
|
||||
const u = factory.createTxCreateDoc(notification.class.LastView, notification.space.Notifications, {
|
||||
user,
|
||||
[attachedTo]: 1
|
||||
})
|
||||
return u
|
||||
} else if (current[attachedTo] === undefined) {
|
||||
const u = factory.createTxUpdateDoc(current._class, current.space, current._id, {
|
||||
[attachedTo]: 1
|
||||
})
|
||||
return u
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -191,10 +123,7 @@ export default plugin(serverNotificationId, {
|
||||
},
|
||||
trigger: {
|
||||
OnBacklinkCreate: '' as Resource<TriggerFunc>,
|
||||
UpdateLastView: '' as Resource<TriggerFunc>,
|
||||
OnUpdateLastView: '' as Resource<TriggerFunc>,
|
||||
CollaboratorDocHandler: '' as Resource<TriggerFunc>,
|
||||
OnAddCollborator: '' as Resource<TriggerFunc>,
|
||||
OnAttributeCreate: '' as Resource<TriggerFunc>,
|
||||
OnAttributeUpdate: '' as Resource<TriggerFunc>
|
||||
},
|
||||
|
@ -101,7 +101,7 @@ export async function OnMessageCreate (tx: Tx, control: TriggerControl): Promise
|
||||
}
|
||||
if (docs.length === 0) {
|
||||
res.push(
|
||||
control.txFactory.createTxCreateDoc(notification.class.DocUpdates, notification.space.Notifications, {
|
||||
control.txFactory.createTxCreateDoc(notification.class.DocUpdates, channel.space, {
|
||||
user: tx.modifiedBy,
|
||||
attachedTo: channel._id,
|
||||
attachedToClass: channel._class,
|
||||
|
Loading…
Reference in New Issue
Block a user