Use doc update instead lastView (#3027)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2023-04-20 13:27:02 +06:00 committed by GitHub
parent e5e9b69365
commit 8407c89709
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 54 additions and 127 deletions

View File

@ -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
}
}

View File

@ -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}

View File

@ -91,6 +91,7 @@ export interface DisplayTx {
isOwnTx: boolean
collectionAttribute?: Attribute<Collection<AttachedDoc>>
originTx: TxCUD<Doc>
}
/**

View File

@ -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>

View File

@ -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}

View File

@ -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 {

View File

@ -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>

View File

@ -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
}