mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-15 04:49:00 +00: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] {
|
createDisplayTx (tx: TxCUD<Doc>, parents: Map<Ref<Doc>, DisplayTx>, isOwnTx: boolean): [DisplayTx, boolean, boolean] {
|
||||||
let collectionAttribute: Attribute<Collection<AttachedDoc>> | undefined
|
let collectionAttribute: Attribute<Collection<AttachedDoc>> | undefined
|
||||||
|
const originTx = tx
|
||||||
if (this.hierarchy.isDerived(tx._class, core.class.TxCollectionCUD)) {
|
if (this.hierarchy.isDerived(tx._class, core.class.TxCollectionCUD)) {
|
||||||
const cltx = tx as TxCollectionCUD<Doc, AttachedDoc>
|
const cltx = tx as TxCollectionCUD<Doc, AttachedDoc>
|
||||||
tx = TxProcessor.extractTx(cltx) as TxCUD<Doc>
|
tx = TxProcessor.extractTx(cltx) as TxCUD<Doc>
|
||||||
@ -295,7 +296,7 @@ class ActivityImpl implements Activity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let firstTx = parents.get(tx.objectId)
|
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
|
result.collectionAttribute = collectionAttribute
|
||||||
|
|
||||||
@ -418,7 +419,12 @@ function getCombineOpFromTx (result: DisplayTx): any {
|
|||||||
return curUpdate
|
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
|
const createTx = hierarchy.isDerived(tx._class, core.class.TxCreateDoc) ? (tx as TxCreateDoc<Doc>) : undefined
|
||||||
return {
|
return {
|
||||||
tx,
|
tx,
|
||||||
@ -430,7 +436,8 @@ export function newDisplayTx (tx: TxCUD<Doc>, hierarchy: Hierarchy, isOwnTx: boo
|
|||||||
removed: false,
|
removed: false,
|
||||||
mixin: false,
|
mixin: false,
|
||||||
mixinTx: hierarchy.isDerived(tx._class, core.class.TxMixin) ? (tx as TxMixin<Doc, Doc>) : undefined,
|
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">
|
<script lang="ts">
|
||||||
import activity, { DisplayTx, TxViewlet } from '@hcengineering/activity'
|
import activity, { DisplayTx, TxViewlet } from '@hcengineering/activity'
|
||||||
import chunter from '@hcengineering/chunter'
|
import chunter from '@hcengineering/chunter'
|
||||||
import core, { Class, Doc, Ref, SortingOrder } from '@hcengineering/core'
|
import core, { Class, Doc, Ref, SortingOrder, TxCUD } from '@hcengineering/core'
|
||||||
import notification, { LastView } from '@hcengineering/notification'
|
|
||||||
import { getResource } from '@hcengineering/platform'
|
|
||||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||||
import { Component, Grid, Label, Spinner } from '@hcengineering/ui'
|
import { Component, Grid, Label, Spinner } from '@hcengineering/ui'
|
||||||
import { Writable } from 'svelte/store'
|
|
||||||
import { ActivityKey, activityKey, newActivity } from '../activity'
|
import { ActivityKey, activityKey, newActivity } from '../activity'
|
||||||
import { filterCollectionTxes } from '../utils'
|
import { filterCollectionTxes } from '../utils'
|
||||||
import ActivityFilter from './ActivityFilter.svelte'
|
import ActivityFilter from './ActivityFilter.svelte'
|
||||||
import TxView from './TxView.svelte'
|
import TxView from './TxView.svelte'
|
||||||
|
import notification, { DocUpdates, Writable } from '@hcengineering/notification'
|
||||||
|
import { getResource } from '@hcengineering/platform'
|
||||||
|
|
||||||
export let object: Doc
|
export let object: Doc
|
||||||
export let showCommenInput: boolean = true
|
export let showCommenInput: boolean = true
|
||||||
export let transparent: boolean = false
|
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[] = []
|
let txes: DisplayTx[] = []
|
||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const attrs = client.getHierarchy().getAllAttributes(object._class)
|
const attrs = client.getHierarchy().getAllAttributes(object._class)
|
||||||
|
|
||||||
const activityQuery = newActivity(client, attrs)
|
const activityQuery = newActivity(client, attrs)
|
||||||
getResource(notification.function.GetNotificationClient).then((res) => {
|
|
||||||
lastViews = res().getLastViews()
|
|
||||||
})
|
|
||||||
let lastViews: Writable<LastView> | undefined
|
|
||||||
|
|
||||||
let viewlets: Map<ActivityKey, TxViewlet>
|
let viewlets: Map<ActivityKey, TxViewlet>
|
||||||
|
|
||||||
@ -80,16 +83,10 @@
|
|||||||
|
|
||||||
let filtered: DisplayTx[] = []
|
let filtered: DisplayTx[] = []
|
||||||
|
|
||||||
$: newTxPos = newTx(filtered, $lastViews)
|
function isNew (tx: DisplayTx | undefined, newTxes: [Ref<TxCUD<Doc>>, number][]): boolean {
|
||||||
|
if (tx === undefined) return false
|
||||||
function newTx (txes: DisplayTx[], lastViews: LastView | undefined): number {
|
const index = newTxes.findIndex((p) => p[0] === tx.originTx._id)
|
||||||
const lastView = (lastViews as any)?.[object._id]
|
return index !== -1
|
||||||
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
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -108,7 +105,7 @@
|
|||||||
{#if filtered}
|
{#if filtered}
|
||||||
<Grid column={1} rowGap={0.75}>
|
<Grid column={1} rowGap={0.75}>
|
||||||
{#each filtered as tx, i}
|
{#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}
|
{/each}
|
||||||
</Grid>
|
</Grid>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -91,6 +91,7 @@ export interface DisplayTx {
|
|||||||
isOwnTx: boolean
|
isOwnTx: boolean
|
||||||
|
|
||||||
collectionAttribute?: Attribute<Collection<AttachedDoc>>
|
collectionAttribute?: Attribute<Collection<AttachedDoc>>
|
||||||
|
originTx: TxCUD<Doc>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -67,6 +67,6 @@
|
|||||||
$: update(filtered, newTxes)
|
$: update(filtered, newTxes)
|
||||||
|
|
||||||
function createDisplayTxes (txes: TxCollectionCUD<Doc, AttachedDoc>[]): DisplayTx[] {
|
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>
|
</script>
|
||||||
|
@ -21,10 +21,10 @@
|
|||||||
export let kind: 'table' | 'block' = 'block'
|
export let kind: 'table' | 'block' = 'block'
|
||||||
|
|
||||||
const notificationClient = NotificationClientImpl.getClient()
|
const notificationClient = NotificationClientImpl.getClient()
|
||||||
const lastViews = notificationClient.getLastViews()
|
const store = notificationClient.docUpdatesStore
|
||||||
|
$: docUpdate = $store.get(value._id)
|
||||||
|
|
||||||
$: lastView = (($lastViews as any) ?? {})[value._id]
|
$: hasNotification = (docUpdate?.txes?.length ?? 0) > 0
|
||||||
$: hasNotification = lastView !== undefined && lastView !== -1 && lastView < value.modifiedOn
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if hasNotification}
|
{#if hasNotification}
|
||||||
|
@ -25,6 +25,9 @@ import { get, writable, Writable } from 'svelte/store'
|
|||||||
export class NotificationClientImpl implements NotificationClient {
|
export class NotificationClientImpl implements NotificationClient {
|
||||||
protected static _instance: NotificationClientImpl | undefined = undefined
|
protected static _instance: NotificationClientImpl | undefined = undefined
|
||||||
private readonly lastViewsStore = writable<LastView>()
|
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 lastViewQuery = createQuery()
|
||||||
private readonly user: Ref<Account>
|
private readonly user: Ref<Account>
|
||||||
@ -42,6 +45,15 @@ export class NotificationClientImpl implements NotificationClient {
|
|||||||
void client.tx(u)
|
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 {
|
static createClient (): void {
|
||||||
|
@ -151,6 +151,7 @@ export const notificationId = 'notification' as Plugin
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export interface NotificationClient {
|
export interface NotificationClient {
|
||||||
|
docUpdatesStore: Writable<Map<Ref<Doc>, DocUpdates>>
|
||||||
getLastViews: () => Writable<LastView>
|
getLastViews: () => Writable<LastView>
|
||||||
updateLastView: (_id: Ref<Doc>, _class: Ref<Class<Doc>>, time?: Timestamp, force?: boolean) => Promise<void>
|
updateLastView: (_id: Ref<Doc>, _class: Ref<Class<Doc>>, time?: Timestamp, force?: boolean) => Promise<void>
|
||||||
unsubscribe: (_id: Ref<Doc>) => Promise<void>
|
unsubscribe: (_id: Ref<Doc>) => Promise<void>
|
||||||
|
@ -29,7 +29,6 @@ import core, {
|
|||||||
Ref,
|
Ref,
|
||||||
RefTo,
|
RefTo,
|
||||||
Space,
|
Space,
|
||||||
Timestamp,
|
|
||||||
Tx,
|
Tx,
|
||||||
TxCUD,
|
TxCUD,
|
||||||
TxCollectionCUD,
|
TxCollectionCUD,
|
||||||
@ -56,8 +55,7 @@ import serverNotification, {
|
|||||||
TextPresenter,
|
TextPresenter,
|
||||||
createLastViewTx,
|
createLastViewTx,
|
||||||
getEmployeeAccount,
|
getEmployeeAccount,
|
||||||
getEmployeeAccountById,
|
getEmployeeAccountById
|
||||||
getUpdateLastViewTx
|
|
||||||
} from '@hcengineering/server-notification'
|
} from '@hcengineering/server-notification'
|
||||||
import { Content } from './types'
|
import { Content } from './types'
|
||||||
import { replaceAll } from './utils'
|
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
|
* @public
|
||||||
*/
|
*/
|
||||||
export async function UpdateLastView (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
export async function UpdateLastView (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||||
const actualTx = TxProcessor.extractTx(tx)
|
const actualTx = TxProcessor.extractTx(tx)
|
||||||
if (
|
if (actualTx._class !== core.class.TxRemoveDoc) {
|
||||||
![core.class.TxUpdateDoc, core.class.TxCreateDoc, core.class.TxMixin, core.class.TxRemoveDoc].includes(
|
|
||||||
actualTx._class
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,65 +287,16 @@ export async function UpdateLastView (tx: Tx, control: TriggerControl): Promise<
|
|||||||
|
|
||||||
const result: Tx[] = []
|
const result: Tx[] = []
|
||||||
|
|
||||||
switch (actualTx._class) {
|
const removeTx = actualTx as TxRemoveDoc<Doc>
|
||||||
case core.class.TxCreateDoc: {
|
const lastViews = await control.findAll(notification.class.LastView, { [removeTx.objectId]: { $exists: true } })
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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) {
|
for (const lastView of lastViews) {
|
||||||
const clearTx = control.txFactory.createTxUpdateDoc(lastView._class, lastView.space, lastView._id, {
|
const clearTx = control.txFactory.createTxUpdateDoc(lastView._class, lastView.space, lastView._id, {
|
||||||
$unset: {
|
$unset: {
|
||||||
[tx.objectId]: ''
|
[removeTx.objectId]: ''
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
result.push(clearTx)
|
result.push(clearTx)
|
||||||
}
|
}
|
||||||
return result
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user