Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2021-11-29 22:19:21 +07:00 committed by GitHub
parent 0800f97f3c
commit c26c92023e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 67 additions and 47 deletions

View File

@ -140,16 +140,14 @@ class ActivityImpl implements Activity {
const parents = new Map<Ref<Doc>, DisplayTx>() const parents = new Map<Ref<Doc>, DisplayTx>()
const results: DisplayTx[] = [] let results: DisplayTx[] = []
for (const tx of txCUD) { for (const tx of txCUD) {
const { collectionCUD, updateCUD, result, tx: ntx } = this.createDisplayTx(tx, parents) const { collectionCUD, updateCUD, result, tx: ntx } = this.createDisplayTx(tx, parents)
// We do not need collection object updates, in main list of displayed transactions. // We do not need collection object updates, in main list of displayed transactions.
if (this.isDisplayTxRequired(collectionCUD, updateCUD, ntx, object)) { if (this.isDisplayTxRequired(collectionCUD, updateCUD, ntx, object)) {
// Combine previous update transaction for same field and if same operation and time treshold is ok // Combine previous update transaction for same field and if same operation and time treshold is ok
this.checkIntegratePreviousTx(results, result) results = this.integrateTxWithResults(results, result)
results.push(result)
this.updateRemovedState(result, results) this.updateRemovedState(result, results)
} }
} }
@ -188,7 +186,7 @@ class ActivityImpl implements Activity {
let updateCUD = false let updateCUD = false
const hierarchy = this.client.getHierarchy() const hierarchy = this.client.getHierarchy()
if (hierarchy.isDerived(tx._class, core.class.TxCollectionCUD)) { if (hierarchy.isDerived(tx._class, core.class.TxCollectionCUD)) {
tx = getCollectionTx((tx as TxCollectionCUD<Doc, AttachedDoc>)) tx = getCollectionTx(tx as TxCollectionCUD<Doc, AttachedDoc>)
collectionCUD = true collectionCUD = true
} }
let firstTx = parents.get(tx.objectId) let firstTx = parents.get(tx.objectId)
@ -237,24 +235,27 @@ class ActivityImpl implements Activity {
} }
} }
checkIntegratePreviousTx (results: DisplayTx[], result: DisplayTx): void { integrateTxWithResults (results: DisplayTx[], result: DisplayTx): DisplayTx[] {
if (results.length > 0) { const curUpdate = result.tx as unknown as TxUpdateDoc<Doc>
const prevTx = results[results.length - 1]
const newResult = results.filter((prevTx) => {
if (this.isSameKindTx(prevTx, result)) { if (this.isSameKindTx(prevTx, result)) {
const prevUpdate = prevTx.tx as unknown as TxUpdateDoc<Doc> const prevUpdate = prevTx.tx as unknown as TxUpdateDoc<Doc>
const curUpdate = result.tx as unknown as TxUpdateDoc<Doc>
if ( if (
isEqualOps(prevUpdate.operations, curUpdate.operations) && result.tx.modifiedOn - prevUpdate.modifiedOn < combineThreshold &&
result.tx.modifiedOn - prevUpdate.modifiedOn < combineThreshold isEqualOps(prevUpdate.operations, curUpdate.operations)
) { ) {
// we have same keys, l // we have same keys,
// Remember previous transactions // Remember previous transactions
result.txes.push(...prevTx.txes, prevTx.tx) result.txes.push(...prevTx.txes, prevTx.tx)
// Remove last item return false
results.splice(results.length - 1, 1)
} }
} }
}
return true
})
newResult.push(result)
return newResult
} }
isSameKindTx (prevTx: DisplayTx, result: DisplayTx): boolean { isSameKindTx (prevTx: DisplayTx, result: DisplayTx): boolean {

View File

@ -20,7 +20,18 @@
import core, { Class, Doc, Ref, TxCUD, TxUpdateDoc } from '@anticrm/core' import core, { Class, Doc, Ref, TxCUD, TxUpdateDoc } from '@anticrm/core'
import { getResource, IntlString } from '@anticrm/platform' import { getResource, IntlString } from '@anticrm/platform'
import { getClient } from '@anticrm/presentation' import { getClient } from '@anticrm/presentation'
import { AnyComponent, AnySvelteComponent, Component, Icon, IconEdit, IconMoreH, Label, Menu, showPopup, TimeSince } from '@anticrm/ui' import {
AnyComponent,
AnySvelteComponent,
Component,
Icon,
IconEdit,
IconMoreH,
Label,
Menu,
showPopup,
TimeSince
} from '@anticrm/ui'
import type { Action, AttributeModel } from '@anticrm/view' import type { Action, AttributeModel } from '@anticrm/view'
import { buildModel, getActions, getObjectPresenter } from '@anticrm/view-resources' import { buildModel, getActions, getObjectPresenter } from '@anticrm/view-resources'
import { activityKey, ActivityKey, DisplayTx } from '../activity' import { activityKey, ActivityKey, DisplayTx } from '../activity'
@ -29,7 +40,9 @@
export let viewlets: Map<ActivityKey, TxViewlet> export let viewlets: Map<ActivityKey, TxViewlet>
type TxDisplayViewlet = type TxDisplayViewlet =
| (Pick<TxViewlet, 'icon' | 'label' | 'display'|'editable' | 'hideOnRemove'> & { component?: AnyComponent | AnySvelteComponent }) | (Pick<TxViewlet, 'icon' | 'label' | 'display' | 'editable' | 'hideOnRemove'> & {
component?: AnyComponent | AnySvelteComponent
})
| undefined | undefined
let ptx: DisplayTx | undefined let ptx: DisplayTx | undefined
@ -102,7 +115,7 @@
const ops = { const ops = {
client, client,
_class: tx.updateTx.objectClass, _class: tx.updateTx.objectClass,
keys: Object.keys(tx.updateTx.operations).filter(id => !id.startsWith('$')), keys: Object.keys(tx.updateTx.operations).filter((id) => !id.startsWith('$')),
ignoreMissing: true ignoreMissing: true
} }
buildModel(ops).then((m) => { buildModel(ops).then((m) => {
@ -118,24 +131,30 @@
return (utx.operations as any)[key] return (utx.operations as any)[key]
} }
const showMenu = async (ev: MouseEvent): Promise<void> => { const showMenu = async (ev: MouseEvent): Promise<void> => {
showPopup(Menu, { showPopup(
actions: [{ Menu,
label: activity.string.Edit, {
icon: IconEdit, actions: [
action: () => { {
edit = true label: activity.string.Edit,
props = { ...props, edit } icon: IconEdit,
} action: () => {
}, ...actions.map(a => ({ edit = true
label: a.label, props = { ...props, edit }
icon: a.icon, }
action: async () => { },
const impl = await getResource(a.action) ...actions.map((a) => ({
await impl(tx.doc as Doc) label: a.label,
} icon: a.icon,
})) action: async () => {
] const impl = await getResource(a.action)
}, ev.target as HTMLElement) await impl(tx.doc as Doc)
}
}))
]
},
ev.target as HTMLElement
)
} }
const onCancelEdit = () => { const onCancelEdit = () => {
edit = false edit = false
@ -164,23 +183,21 @@
{/if} {/if}
</div> </div>
{#if viewlet && viewlet?.editable} {#if viewlet && viewlet?.editable}
<div class='edited'> <div class="edited">
{#if viewlet.label} {#if viewlet.label}
<Label label={viewlet.label} /> <Label label={viewlet.label} />
{/if} {/if}
{#if tx.updated} {#if tx.updated}
<Label label={activity.string.Edited}/> <Label label={activity.string.Edited} />
{/if} {/if}
<div class="menuOptions" on:click={(ev) => showMenu(ev)}> <div class="menuOptions" on:click={(ev) => showMenu(ev)}>
<IconMoreH size={'small'} /> <IconMoreH size={'small'} />
</div> </div>
</div> </div>
{:else} {:else if viewlet && viewlet.label}
{#if viewlet && viewlet.label} <div>
<div> <Label label={viewlet.label} />
<Label label={viewlet.label} /> </div>
</div>
{/if}
{/if} {/if}
{#if viewlet === undefined && model.length > 0 && tx.updateTx} {#if viewlet === undefined && model.length > 0 && tx.updateTx}
{#each model as m} {#each model as m}
@ -237,10 +254,12 @@
// :global(.msgactivity-container > *:last-child::after) { content: none; } // :global(.msgactivity-container > *:last-child::after) { content: none; }
.menuOptions { .menuOptions {
margin-left: .5rem; margin-left: 0.5rem;
opacity: .6; opacity: 0.6;
cursor: pointer; cursor: pointer;
&:hover { opacity: 1; } &:hover {
opacity: 1;
}
} }
.icon { .icon {
flex-shrink: 0; flex-shrink: 0;