mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-23 03:22:19 +03:00
Use doc update instead lastView (#3027)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
parent
e5e9b69365
commit
8407c89709
@ -278,6 +278,7 @@ class ActivityImpl implements Activity {
|
||||
|
||||
createDisplayTx (tx: TxCUD<Doc>, parents: Map<Ref<Doc>, DisplayTx>, isOwnTx: boolean): [DisplayTx, boolean, boolean] {
|
||||
let collectionAttribute: Attribute<Collection<AttachedDoc>> | undefined
|
||||
const originTx = tx
|
||||
if (this.hierarchy.isDerived(tx._class, core.class.TxCollectionCUD)) {
|
||||
const cltx = tx as TxCollectionCUD<Doc, AttachedDoc>
|
||||
tx = TxProcessor.extractTx(cltx) as TxCUD<Doc>
|
||||
@ -295,7 +296,7 @@ class ActivityImpl implements Activity {
|
||||
}
|
||||
}
|
||||
let firstTx = parents.get(tx.objectId)
|
||||
const result: DisplayTx = newDisplayTx(tx, this.hierarchy, isOwnTx)
|
||||
const result: DisplayTx = newDisplayTx(tx, this.hierarchy, isOwnTx, originTx)
|
||||
|
||||
result.collectionAttribute = collectionAttribute
|
||||
|
||||
@ -418,7 +419,12 @@ function getCombineOpFromTx (result: DisplayTx): any {
|
||||
return curUpdate
|
||||
}
|
||||
|
||||
export function newDisplayTx (tx: TxCUD<Doc>, hierarchy: Hierarchy, isOwnTx: boolean): DisplayTx {
|
||||
export function newDisplayTx (
|
||||
tx: TxCUD<Doc>,
|
||||
hierarchy: Hierarchy,
|
||||
isOwnTx: boolean,
|
||||
originTx: TxCUD<Doc> = tx
|
||||
): DisplayTx {
|
||||
const createTx = hierarchy.isDerived(tx._class, core.class.TxCreateDoc) ? (tx as TxCreateDoc<Doc>) : undefined
|
||||
return {
|
||||
tx,
|
||||
@ -430,7 +436,8 @@ export function newDisplayTx (tx: TxCUD<Doc>, hierarchy: Hierarchy, isOwnTx: boo
|
||||
removed: false,
|
||||
mixin: false,
|
||||
mixinTx: hierarchy.isDerived(tx._class, core.class.TxMixin) ? (tx as TxMixin<Doc, Doc>) : undefined,
|
||||
doc: createTx !== undefined ? TxProcessor.createDoc2Doc(createTx) : undefined
|
||||
doc: createTx !== undefined ? TxProcessor.createDoc2Doc(createTx) : undefined,
|
||||
originTx
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,31 +15,34 @@
|
||||
<script lang="ts">
|
||||
import activity, { DisplayTx, TxViewlet } from '@hcengineering/activity'
|
||||
import chunter from '@hcengineering/chunter'
|
||||
import core, { Class, Doc, Ref, SortingOrder } from '@hcengineering/core'
|
||||
import notification, { LastView } from '@hcengineering/notification'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import core, { Class, Doc, Ref, SortingOrder, TxCUD } from '@hcengineering/core'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Component, Grid, Label, Spinner } from '@hcengineering/ui'
|
||||
import { Writable } from 'svelte/store'
|
||||
import { ActivityKey, activityKey, newActivity } from '../activity'
|
||||
import { filterCollectionTxes } from '../utils'
|
||||
import ActivityFilter from './ActivityFilter.svelte'
|
||||
import TxView from './TxView.svelte'
|
||||
import notification, { DocUpdates, Writable } from '@hcengineering/notification'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
|
||||
export let object: Doc
|
||||
export let showCommenInput: boolean = true
|
||||
export let transparent: boolean = false
|
||||
|
||||
getResource(notification.function.GetNotificationClient).then((res) => {
|
||||
updatesStore = res().docUpdatesStore
|
||||
})
|
||||
let updatesStore: Writable<Map<Ref<Doc>, DocUpdates>> | undefined
|
||||
|
||||
$: updates = $updatesStore?.get(object._id)
|
||||
$: newTxes = updates?.txes ?? []
|
||||
|
||||
let txes: DisplayTx[] = []
|
||||
|
||||
const client = getClient()
|
||||
const attrs = client.getHierarchy().getAllAttributes(object._class)
|
||||
|
||||
const activityQuery = newActivity(client, attrs)
|
||||
getResource(notification.function.GetNotificationClient).then((res) => {
|
||||
lastViews = res().getLastViews()
|
||||
})
|
||||
let lastViews: Writable<LastView> | undefined
|
||||
|
||||
let viewlets: Map<ActivityKey, TxViewlet>
|
||||
|
||||
@ -80,16 +83,10 @@
|
||||
|
||||
let filtered: DisplayTx[] = []
|
||||
|
||||
$: newTxPos = newTx(filtered, $lastViews)
|
||||
|
||||
function newTx (txes: DisplayTx[], lastViews: LastView | undefined): number {
|
||||
const lastView = (lastViews as any)?.[object._id]
|
||||
if (lastView === undefined || lastView === -1) return -1
|
||||
for (let index = 0; index < txes.length; index++) {
|
||||
const tx = txes[index]
|
||||
if (tx.tx.modifiedOn > lastView) return index - 1
|
||||
}
|
||||
return -1
|
||||
function isNew (tx: DisplayTx | undefined, newTxes: [Ref<TxCUD<Doc>>, number][]): boolean {
|
||||
if (tx === undefined) return false
|
||||
const index = newTxes.findIndex((p) => p[0] === tx.originTx._id)
|
||||
return index !== -1
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -108,7 +105,7 @@
|
||||
{#if filtered}
|
||||
<Grid column={1} rowGap={0.75}>
|
||||
{#each filtered as tx, i}
|
||||
<TxView {tx} {viewlets} isNew={newTxPos < i && newTxPos !== -1} isNextNew={newTxPos <= i && newTxPos !== -1} />
|
||||
<TxView {tx} {viewlets} isNew={isNew(tx, newTxes)} isNextNew={isNew(filtered[i + 1], newTxes)} />
|
||||
{/each}
|
||||
</Grid>
|
||||
{/if}
|
||||
|
@ -91,6 +91,7 @@ export interface DisplayTx {
|
||||
isOwnTx: boolean
|
||||
|
||||
collectionAttribute?: Attribute<Collection<AttachedDoc>>
|
||||
originTx: TxCUD<Doc>
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,6 +67,6 @@
|
||||
$: update(filtered, newTxes)
|
||||
|
||||
function createDisplayTxes (txes: TxCollectionCUD<Doc, AttachedDoc>[]): DisplayTx[] {
|
||||
return txes.map((p) => newDisplayTx(TxProcessor.extractTx(p) as TxCUD<Doc>, hierarchy, false))
|
||||
return txes.map((p) => newDisplayTx(TxProcessor.extractTx(p) as TxCUD<Doc>, hierarchy, false, p))
|
||||
}
|
||||
</script>
|
||||
|
@ -21,10 +21,10 @@
|
||||
export let kind: 'table' | 'block' = 'block'
|
||||
|
||||
const notificationClient = NotificationClientImpl.getClient()
|
||||
const lastViews = notificationClient.getLastViews()
|
||||
const store = notificationClient.docUpdatesStore
|
||||
$: docUpdate = $store.get(value._id)
|
||||
|
||||
$: lastView = (($lastViews as any) ?? {})[value._id]
|
||||
$: hasNotification = lastView !== undefined && lastView !== -1 && lastView < value.modifiedOn
|
||||
$: hasNotification = (docUpdate?.txes?.length ?? 0) > 0
|
||||
</script>
|
||||
|
||||
{#if hasNotification}
|
||||
|
@ -25,6 +25,9 @@ import { get, writable, Writable } from 'svelte/store'
|
||||
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())
|
||||
|
||||
private readonly docUpdatesQuery = createQuery(true)
|
||||
|
||||
private readonly lastViewQuery = createQuery()
|
||||
private readonly user: Ref<Account>
|
||||
@ -42,6 +45,15 @@ export class NotificationClientImpl implements NotificationClient {
|
||||
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])))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
static createClient (): void {
|
||||
|
@ -151,6 +151,7 @@ export const notificationId = 'notification' as Plugin
|
||||
* @public
|
||||
*/
|
||||
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>
|
||||
|
@ -29,7 +29,6 @@ import core, {
|
||||
Ref,
|
||||
RefTo,
|
||||
Space,
|
||||
Timestamp,
|
||||
Tx,
|
||||
TxCUD,
|
||||
TxCollectionCUD,
|
||||
@ -56,8 +55,7 @@ import serverNotification, {
|
||||
TextPresenter,
|
||||
createLastViewTx,
|
||||
getEmployeeAccount,
|
||||
getEmployeeAccountById,
|
||||
getUpdateLastViewTx
|
||||
getEmployeeAccountById
|
||||
} from '@hcengineering/server-notification'
|
||||
import { Content } from './types'
|
||||
import { replaceAll } from './utils'
|
||||
@ -274,52 +272,12 @@ async function getEmailNotificationTx (
|
||||
}
|
||||
}
|
||||
|
||||
async function getUpdateLastViewTxes (
|
||||
doc: Doc,
|
||||
_id: Ref<Doc>,
|
||||
_class: Ref<Class<Doc>>,
|
||||
modifiedOn: Timestamp,
|
||||
user: Ref<Account>,
|
||||
control: TriggerControl
|
||||
): Promise<Tx[]> {
|
||||
const updatedUsers: Set<Ref<Account>> = new Set<Ref<Account>>()
|
||||
const result: Tx[] = []
|
||||
const tx = await getUpdateLastViewTx(control.findAll, _id, modifiedOn, user)
|
||||
if (tx !== undefined) {
|
||||
updatedUsers.add(user)
|
||||
result.push(tx)
|
||||
}
|
||||
const docClass = control.hierarchy.getClass(doc._class)
|
||||
const anotherUserNotifications = control.hierarchy.as(docClass, notification.mixin.AnotherUserNotifications)
|
||||
for (const field of anotherUserNotifications?.fields ?? []) {
|
||||
const value = (doc as any)[field]
|
||||
if (value != null) {
|
||||
for (const employeeId of Array.isArray(value) ? value : [value]) {
|
||||
const account = await getEmployeeAccount(employeeId, control)
|
||||
if (account !== undefined) {
|
||||
if (updatedUsers.has(account._id)) continue
|
||||
const assigneeTx = await createLastViewTx(control.findAll, _id, account._id)
|
||||
if (assigneeTx !== undefined) {
|
||||
updatedUsers.add(account._id)
|
||||
result.push(assigneeTx)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function UpdateLastView (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
const actualTx = TxProcessor.extractTx(tx)
|
||||
if (
|
||||
![core.class.TxUpdateDoc, core.class.TxCreateDoc, core.class.TxMixin, core.class.TxRemoveDoc].includes(
|
||||
actualTx._class
|
||||
)
|
||||
) {
|
||||
if (actualTx._class !== core.class.TxRemoveDoc) {
|
||||
return []
|
||||
}
|
||||
|
||||
@ -329,65 +287,16 @@ export async function UpdateLastView (tx: Tx, control: TriggerControl): Promise<
|
||||
|
||||
const result: Tx[] = []
|
||||
|
||||
switch (actualTx._class) {
|
||||
case core.class.TxCreateDoc: {
|
||||
const createTx = actualTx as TxCreateDoc<Doc>
|
||||
if (control.hierarchy.isDerived(createTx.objectClass, core.class.AttachedDoc)) {
|
||||
const doc = TxProcessor.createDoc2Doc(createTx as TxCreateDoc<AttachedDoc>)
|
||||
if (control.hierarchy.classHierarchyMixin(doc.attachedToClass, notification.mixin.TrackedDoc) !== undefined) {
|
||||
const attachedTxes = await getUpdateLastViewTxes(
|
||||
doc,
|
||||
doc.attachedTo,
|
||||
doc.attachedToClass,
|
||||
createTx.modifiedOn,
|
||||
createTx.modifiedBy,
|
||||
control
|
||||
)
|
||||
result.push(...attachedTxes)
|
||||
}
|
||||
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]: ''
|
||||
}
|
||||
if (control.hierarchy.classHierarchyMixin(createTx.objectClass, notification.mixin.TrackedDoc) !== undefined) {
|
||||
const doc = TxProcessor.createDoc2Doc(createTx)
|
||||
const parentTxes = await getUpdateLastViewTxes(
|
||||
doc,
|
||||
doc._id,
|
||||
doc._class,
|
||||
createTx.modifiedOn,
|
||||
createTx.modifiedBy,
|
||||
control
|
||||
)
|
||||
result.push(...parentTxes)
|
||||
}
|
||||
return result
|
||||
}
|
||||
case core.class.TxUpdateDoc:
|
||||
case core.class.TxMixin: {
|
||||
const tx = actualTx as TxCUD<Doc>
|
||||
if (control.hierarchy.classHierarchyMixin(tx.objectClass, notification.mixin.TrackedDoc) !== undefined) {
|
||||
const doc = (await control.findAll(tx.objectClass, { _id: tx.objectId }, { limit: 1 }))[0]
|
||||
if (doc !== undefined) {
|
||||
return await getUpdateLastViewTxes(doc, doc._id, doc._class, tx.modifiedOn, tx.modifiedBy, control)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
case core.class.TxRemoveDoc: {
|
||||
const tx = actualTx as TxCUD<Doc>
|
||||
const lastViews = await control.findAll(notification.class.LastView, { [tx.objectId]: { $exists: true } })
|
||||
for (const lastView of lastViews) {
|
||||
const clearTx = control.txFactory.createTxUpdateDoc(lastView._class, lastView.space, lastView._id, {
|
||||
$unset: {
|
||||
[tx.objectId]: ''
|
||||
}
|
||||
})
|
||||
result.push(clearTx)
|
||||
}
|
||||
return result
|
||||
}
|
||||
default:
|
||||
break
|
||||
})
|
||||
result.push(clearTx)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user