mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-29 11:43:49 +00:00
Activity change attached fix (#2428)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
parent
688434a0e6
commit
4ca2b97345
@ -193,6 +193,9 @@ export function TypeReportedTime (): Type<number> {
|
|||||||
@Model(tracker.class.Issue, core.class.AttachedDoc, DOMAIN_TRACKER)
|
@Model(tracker.class.Issue, core.class.AttachedDoc, DOMAIN_TRACKER)
|
||||||
@UX(tracker.string.Issue, tracker.icon.Issue, tracker.string.Issue)
|
@UX(tracker.string.Issue, tracker.icon.Issue, tracker.string.Issue)
|
||||||
export class TIssue extends TAttachedDoc implements Issue {
|
export class TIssue extends TAttachedDoc implements Issue {
|
||||||
|
@Prop(TypeRef(tracker.class.Issue), tracker.string.Parent)
|
||||||
|
declare attachedTo: Ref<Issue>
|
||||||
|
|
||||||
@Prop(TypeString(), tracker.string.Title)
|
@Prop(TypeString(), tracker.string.Title)
|
||||||
@Index(IndexKind.FullText)
|
@Index(IndexKind.FullText)
|
||||||
title!: string
|
title!: string
|
||||||
|
@ -34,7 +34,8 @@ export default mergeIds(trackerId, tracker, {
|
|||||||
GotoProjects: '' as IntlString,
|
GotoProjects: '' as IntlString,
|
||||||
GotoTrackerApplication: '' as IntlString,
|
GotoTrackerApplication: '' as IntlString,
|
||||||
SearchIssue: '' as IntlString,
|
SearchIssue: '' as IntlString,
|
||||||
NewRelatedIssue: '' as IntlString
|
NewRelatedIssue: '' as IntlString,
|
||||||
|
Parent: '' as IntlString
|
||||||
},
|
},
|
||||||
component: {
|
component: {
|
||||||
// Required to pass build without errorsF
|
// Required to pass build without errorsF
|
||||||
|
@ -96,24 +96,29 @@ export interface Activity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ActivityImpl implements Activity {
|
class ActivityImpl implements Activity {
|
||||||
private readonly txQuery1: LiveQuery
|
private readonly ownTxQuery: LiveQuery
|
||||||
private readonly txQuery2: LiveQuery
|
private readonly attachedTxQuery: LiveQuery
|
||||||
|
private readonly attachedChangeTxQuery: LiveQuery
|
||||||
private readonly hiddenAttributes: Set<string>
|
private readonly hiddenAttributes: Set<string>
|
||||||
private editable: Map<Ref<Class<Doc>>, boolean> | undefined
|
private editable: Map<Ref<Class<Doc>>, boolean> | undefined
|
||||||
|
|
||||||
private txes1: Array<TxCUD<Doc>> = []
|
private ownTxes: Array<TxCUD<Doc>> = []
|
||||||
private txes2: Array<TxCUD<Doc>> = []
|
private attachedTxes: Array<TxCollectionCUD<Doc, AttachedDoc>> = []
|
||||||
|
private attacheChangedTxes: Array<TxCollectionCUD<Doc, AttachedDoc>> = []
|
||||||
|
private readonly hierarchy: Hierarchy
|
||||||
constructor (readonly client: Client, attributes: Map<string, AnyAttribute>) {
|
constructor (readonly client: Client, attributes: Map<string, AnyAttribute>) {
|
||||||
|
this.hierarchy = client.getHierarchy()
|
||||||
this.hiddenAttributes = new Set(
|
this.hiddenAttributes = new Set(
|
||||||
[...attributes.entries()].filter(([, value]) => value.hidden === true).map(([key]) => key)
|
[...attributes.entries()].filter(([, value]) => value.hidden === true).map(([key]) => key)
|
||||||
)
|
)
|
||||||
this.txQuery1 = createQuery()
|
this.ownTxQuery = createQuery()
|
||||||
this.txQuery2 = createQuery()
|
this.attachedTxQuery = createQuery()
|
||||||
|
this.attachedChangeTxQuery = createQuery()
|
||||||
}
|
}
|
||||||
|
|
||||||
private notify (object: Doc, listener: DisplayTxListener, sort: SortingOrder): void {
|
private notify (object: Doc, listener: DisplayTxListener, sort: SortingOrder): void {
|
||||||
if (this.editable != null) {
|
if (this.editable != null) {
|
||||||
this.combineTransactions(object, this.txes1, this.txes2, this.editable).then(
|
this.combineTransactions(object, this.ownTxes, this.attachedTxes, this.attacheChangedTxes, this.editable).then(
|
||||||
(result) => {
|
(result) => {
|
||||||
const sorted = result.sort((a, b) => (a.tx.modifiedOn - b.tx.modifiedOn) * sort)
|
const sorted = result.sort((a, b) => (a.tx.modifiedOn - b.tx.modifiedOn) * sort)
|
||||||
listener(sorted)
|
listener(sorted)
|
||||||
@ -128,11 +133,11 @@ class ActivityImpl implements Activity {
|
|||||||
update (object: Doc, listener: DisplayTxListener, sort: SortingOrder, editable: Map<Ref<Class<Doc>>, boolean>): void {
|
update (object: Doc, listener: DisplayTxListener, sort: SortingOrder, editable: Map<Ref<Class<Doc>>, boolean>): void {
|
||||||
let isAttached = false
|
let isAttached = false
|
||||||
|
|
||||||
isAttached = this.client.getHierarchy().isDerived(object._class, core.class.AttachedDoc)
|
isAttached = this.hierarchy.isDerived(object._class, core.class.AttachedDoc)
|
||||||
|
|
||||||
this.editable = editable
|
this.editable = editable
|
||||||
|
|
||||||
this.txQuery1.query<TxCollectionCUD<Doc, AttachedDoc>>(
|
this.ownTxQuery.query<TxCUD<Doc>>(
|
||||||
isAttached ? core.class.TxCollectionCUD : core.class.TxCUD,
|
isAttached ? core.class.TxCollectionCUD : core.class.TxCUD,
|
||||||
isAttached
|
isAttached
|
||||||
? { 'tx.objectId': object._id as Ref<AttachedDoc> }
|
? { 'tx.objectId': object._id as Ref<AttachedDoc> }
|
||||||
@ -143,54 +148,106 @@ class ActivityImpl implements Activity {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
(result) => {
|
(result) => {
|
||||||
this.txes1 = result
|
this.ownTxes = result
|
||||||
this.notify(object, listener, sort)
|
this.notify(object, listener, sort)
|
||||||
},
|
},
|
||||||
{ sort: { modifiedOn: SortingOrder.Descending } }
|
{ sort: { modifiedOn: SortingOrder.Ascending } }
|
||||||
)
|
)
|
||||||
|
|
||||||
this.txQuery2.query<TxCUD<Doc>>(
|
this.attachedTxQuery.query<TxCollectionCUD<Doc, AttachedDoc>>(
|
||||||
core.class.TxCollectionCUD,
|
core.class.TxCollectionCUD,
|
||||||
{
|
{
|
||||||
objectId: object._id,
|
objectId: object._id,
|
||||||
'tx._class': { $in: [core.class.TxCreateDoc, core.class.TxUpdateDoc, core.class.TxRemoveDoc] }
|
'tx._class': { $in: [core.class.TxCreateDoc, core.class.TxUpdateDoc, core.class.TxRemoveDoc] }
|
||||||
},
|
},
|
||||||
(result) => {
|
(result) => {
|
||||||
this.txes2 = result
|
this.attachedTxes = result
|
||||||
this.notify(object, listener, sort)
|
this.notify(object, listener, sort)
|
||||||
},
|
},
|
||||||
{ sort: { modifiedOn: SortingOrder.Descending } }
|
{ sort: { modifiedOn: SortingOrder.Ascending } }
|
||||||
|
)
|
||||||
|
|
||||||
|
this.attachedChangeTxQuery.query<TxCollectionCUD<Doc, AttachedDoc>>(
|
||||||
|
core.class.TxCollectionCUD,
|
||||||
|
{
|
||||||
|
'tx.operations.attachedTo': object._id,
|
||||||
|
'tx._class': core.class.TxUpdateDoc
|
||||||
|
},
|
||||||
|
(result) => {
|
||||||
|
this.attacheChangedTxes = result
|
||||||
|
this.notify(object, listener, sort)
|
||||||
|
},
|
||||||
|
{ sort: { modifiedOn: SortingOrder.Ascending } }
|
||||||
)
|
)
|
||||||
// In case editable is changed
|
// In case editable is changed
|
||||||
this.notify(object, listener, sort)
|
this.notify(object, listener, sort)
|
||||||
}
|
}
|
||||||
|
|
||||||
async combineTransactions (
|
async combineTransactions (
|
||||||
object: Doc,
|
doc: Doc,
|
||||||
txes1: Array<TxCUD<Doc>>,
|
ownTxes: Array<TxCUD<Doc>>,
|
||||||
txes2: Array<TxCUD<Doc>>,
|
attachedTxes: Array<TxCollectionCUD<Doc, AttachedDoc>>,
|
||||||
|
attachedChangeTxes: Array<TxCollectionCUD<Doc, AttachedDoc>>,
|
||||||
editable: Map<Ref<Class<Doc>>, boolean>
|
editable: Map<Ref<Class<Doc>>, boolean>
|
||||||
): Promise<DisplayTx[]> {
|
): Promise<DisplayTx[]> {
|
||||||
const hierarchy = this.client.getHierarchy()
|
|
||||||
|
|
||||||
// We need to sort with with natural order, to build a proper doc values.
|
|
||||||
const allTx = Array.from(txes1).concat(txes2).sort(this.sortByLastModified)
|
|
||||||
const txCUD: Array<TxCUD<Doc>> = this.filterTxCUD(allTx, hierarchy)
|
|
||||||
|
|
||||||
const parents = new Map<Ref<Doc>, DisplayTx>()
|
const parents = new Map<Ref<Doc>, DisplayTx>()
|
||||||
|
|
||||||
let results: DisplayTx[] = []
|
let ownResults: DisplayTx[] = []
|
||||||
|
let attachedResults: DisplayTx[] = []
|
||||||
|
|
||||||
for (const tx of txCUD) {
|
for (const tx of ownTxes) {
|
||||||
const { collectionCUD, updateCUD, mixinCUD, result, tx: ntx } = this.createDisplayTx(tx, parents)
|
if (!this.filterUpdateTx(tx)) continue
|
||||||
// We do not need collection object updates, in main list of displayed transactions.
|
const result = this.createDisplayTx(tx, parents)
|
||||||
if (this.isDisplayTxRequired(collectionCUD, updateCUD || mixinCUD, 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
|
||||||
results = this.integrateTxWithResults(results, result, editable)
|
ownResults = this.integrateTxWithResults(ownResults, result, editable)
|
||||||
this.updateRemovedState(result, results)
|
this.updateRemovedState(result, ownResults)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let tx of Array.from(attachedTxes)
|
||||||
|
.concat(attachedChangeTxes)
|
||||||
|
.sort((a, b) => a.modifiedOn - b.modifiedOn)) {
|
||||||
|
if (!this.filterUpdateTx(tx)) continue
|
||||||
|
const changeAttached = this.isChangeAttachedTx(tx)
|
||||||
|
if (changeAttached || this.isDisplayTxRequired(tx)) {
|
||||||
|
if (changeAttached) {
|
||||||
|
tx = await this.createFakeTx(doc, tx)
|
||||||
|
}
|
||||||
|
const result = this.createDisplayTx(tx, parents)
|
||||||
|
// Combine previous update transaction for same field and if same operation and time treshold is ok
|
||||||
|
attachedResults = this.integrateTxWithResults(attachedResults, result, editable)
|
||||||
|
this.updateRemovedState(result, attachedResults)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Array.from(results)
|
return Array.from(ownResults).concat(attachedResults)
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createFakeTx (
|
||||||
|
doc: Doc,
|
||||||
|
cltx: TxCollectionCUD<Doc, AttachedDoc>
|
||||||
|
): Promise<TxCollectionCUD<Doc, AttachedDoc>> {
|
||||||
|
if (doc._id === cltx.objectId) {
|
||||||
|
cltx.tx._class = core.class.TxRemoveDoc
|
||||||
|
} else {
|
||||||
|
const createTx = await this.client.findOne(core.class.TxCollectionCUD, {
|
||||||
|
'tx.objectId': cltx.tx.objectId,
|
||||||
|
'tx._class': core.class.TxCreateDoc
|
||||||
|
})
|
||||||
|
if (createTx !== undefined) {
|
||||||
|
cltx.tx = createTx.tx
|
||||||
|
cltx.tx.modifiedBy = cltx.modifiedBy
|
||||||
|
cltx.tx.modifiedOn = cltx.modifiedOn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cltx
|
||||||
|
}
|
||||||
|
|
||||||
|
private isChangeAttachedTx (cltx: TxCollectionCUD<Doc, AttachedDoc>): boolean {
|
||||||
|
const tx = TxProcessor.extractTx(cltx)
|
||||||
|
if (this.hierarchy.isDerived(tx._class, core.class.TxUpdateDoc)) {
|
||||||
|
const utx = tx as TxUpdateDoc<AttachedDoc>
|
||||||
|
return utx.operations.attachedTo !== undefined
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateRemovedState (result: DisplayTx, results: DisplayTx[]): void {
|
private updateRemovedState (result: DisplayTx, results: DisplayTx[]): void {
|
||||||
@ -204,42 +261,32 @@ class ActivityImpl implements Activity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sortByLastModified (a: TxCUD<Doc>, b: TxCUD<Doc>): number {
|
private isDisplayTxRequired (cltx: TxCollectionCUD<Doc, AttachedDoc>): boolean {
|
||||||
return a.modifiedOn - b.modifiedOn
|
// Check if collection attribute is hidden
|
||||||
|
if (this.hiddenAttributes.has(cltx.collection)) {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
const tx = TxProcessor.extractTx(cltx)
|
||||||
isDisplayTxRequired (collectionCUD: boolean, cudOp: boolean, ntx: TxCUD<Doc>, object: Doc): boolean {
|
if ([core.class.TxCreateDoc, core.class.TxRemoveDoc].includes(tx._class)) return true
|
||||||
return !(collectionCUD && cudOp) || ntx.objectId === object._id
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly getUpdateTx = (tx: TxCUD<Doc>): TxUpdateDoc<Doc> | undefined => {
|
private readonly getUpdateTx = (tx: TxCUD<Doc>): TxUpdateDoc<Doc> | undefined => {
|
||||||
if (tx._class !== core.class.TxCollectionCUD) {
|
if (this.hierarchy.isDerived(tx._class, core.class.TxCollectionCUD)) {
|
||||||
|
const cltx = tx as TxCollectionCUD<Doc, AttachedDoc>
|
||||||
|
tx = TxProcessor.extractTx(cltx) as TxCUD<Doc>
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tx._class !== core.class.TxUpdateDoc) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const colTx = tx as TxCollectionCUD<Doc, any>
|
return tx as TxUpdateDoc<Doc>
|
||||||
|
|
||||||
if (colTx.tx._class !== core.class.TxUpdateDoc) {
|
|
||||||
return undefined
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return colTx.tx as TxUpdateDoc<Doc>
|
filterUpdateTx (tx: TxCUD<Doc>): boolean {
|
||||||
}
|
|
||||||
|
|
||||||
filterTxCUD (allTx: Array<TxCUD<Doc>>, hierarchy: Hierarchy): Array<TxCUD<Doc>> {
|
|
||||||
return allTx
|
|
||||||
.filter((tx) => hierarchy.isDerived(tx._class, core.class.TxCUD))
|
|
||||||
.filter((tx) => {
|
|
||||||
const utx = this.getUpdateTx(tx)
|
const utx = this.getUpdateTx(tx)
|
||||||
|
|
||||||
if (hierarchy.isDerived(tx._class, core.class.TxCollectionCUD)) {
|
|
||||||
// Check if collection attribute is hidden
|
|
||||||
const txColl = tx as TxCollectionCUD<Doc, AttachedDoc>
|
|
||||||
if (this.hiddenAttributes.has(txColl.collection)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (utx === undefined) {
|
if (utx === undefined) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -251,26 +298,18 @@ class ActivityImpl implements Activity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return !this.hiddenAttributes.has(ops[0])
|
return !this.hiddenAttributes.has(ops[0])
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createDisplayTx (
|
createDisplayTx (tx: TxCUD<Doc>, parents: Map<Ref<Doc>, DisplayTx>): DisplayTx {
|
||||||
tx: TxCUD<Doc>,
|
|
||||||
parents: Map<Ref<Doc>, DisplayTx>
|
|
||||||
): { collectionCUD: boolean, updateCUD: boolean, mixinCUD: boolean, result: DisplayTx, tx: TxCUD<Doc> } {
|
|
||||||
let collectionCUD = false
|
|
||||||
let updateCUD = false
|
|
||||||
let mixinCUD = false
|
|
||||||
const hierarchy = this.client.getHierarchy()
|
|
||||||
let collectionAttribute: Attribute<Collection<AttachedDoc>> | undefined
|
let collectionAttribute: Attribute<Collection<AttachedDoc>> | undefined
|
||||||
if (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 = getCollectionTx(cltx)
|
tx = TxProcessor.extractTx(cltx) as TxCUD<Doc>
|
||||||
|
|
||||||
// Check mixin classes for desired attribute
|
// Check mixin classes for desired attribute
|
||||||
for (const cl of hierarchy.getDescendants(cltx.objectClass)) {
|
for (const cl of this.hierarchy.getDescendants(cltx.objectClass)) {
|
||||||
try {
|
try {
|
||||||
collectionAttribute = hierarchy.getAttribute(cl, cltx.collection) as Attribute<Collection<AttachedDoc>>
|
collectionAttribute = this.hierarchy.getAttribute(cl, cltx.collection) as Attribute<Collection<AttachedDoc>>
|
||||||
if (collectionAttribute !== undefined) {
|
if (collectionAttribute !== undefined) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -278,10 +317,9 @@ class ActivityImpl implements Activity {
|
|||||||
// Ignore
|
// Ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
collectionCUD = cltx.tx._class === core.class.TxUpdateDoc || cltx.tx._class === core.class.TxMixin
|
|
||||||
}
|
}
|
||||||
let firstTx = parents.get(tx.objectId)
|
let firstTx = parents.get(tx.objectId)
|
||||||
const result: DisplayTx = newDisplayTx(tx, hierarchy)
|
const result: DisplayTx = newDisplayTx(tx, this.hierarchy)
|
||||||
result.collectionAttribute = collectionAttribute
|
result.collectionAttribute = collectionAttribute
|
||||||
|
|
||||||
result.doc = firstTx?.doc ?? result.doc
|
result.doc = firstTx?.doc ?? result.doc
|
||||||
@ -290,22 +328,22 @@ class ActivityImpl implements Activity {
|
|||||||
parents.set(tx.objectId, firstTx)
|
parents.set(tx.objectId, firstTx)
|
||||||
|
|
||||||
// If we have updates also apply them all.
|
// If we have updates also apply them all.
|
||||||
updateCUD = this.checkUpdateState(result, firstTx)
|
this.checkUpdateState(result, firstTx)
|
||||||
mixinCUD = this.checkMixinState(result, firstTx)
|
this.checkMixinState(result, firstTx)
|
||||||
|
|
||||||
this.checkRemoveState(hierarchy, tx, firstTx, result)
|
this.checkRemoveState(tx, firstTx, result)
|
||||||
return { collectionCUD, updateCUD, mixinCUD, result, tx }
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkRemoveState (hierarchy: Hierarchy, tx: TxCUD<Doc>, firstTx: DisplayTx, result: DisplayTx): void {
|
private checkRemoveState (tx: TxCUD<Doc>, firstTx: DisplayTx, result: DisplayTx): void {
|
||||||
if (hierarchy.isDerived(tx._class, core.class.TxRemoveDoc)) {
|
if (this.hierarchy.isDerived(tx._class, core.class.TxRemoveDoc)) {
|
||||||
firstTx.removed = true
|
firstTx.removed = true
|
||||||
result.removed = true
|
result.removed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkUpdateState (result: DisplayTx, firstTx: DisplayTx): boolean {
|
checkUpdateState (result: DisplayTx, firstTx: DisplayTx): boolean {
|
||||||
if (this.client.getHierarchy().isDerived(result.tx._class, core.class.TxUpdateDoc) && result.doc !== undefined) {
|
if (this.hierarchy.isDerived(result.tx._class, core.class.TxUpdateDoc) && result.doc !== undefined) {
|
||||||
firstTx.doc = TxProcessor.updateDoc2Doc(result.doc, result.tx as TxUpdateDoc<Doc>)
|
firstTx.doc = TxProcessor.updateDoc2Doc(result.doc, result.tx as TxUpdateDoc<Doc>)
|
||||||
firstTx.updated = true
|
firstTx.updated = true
|
||||||
result.updated = true
|
result.updated = true
|
||||||
@ -315,7 +353,7 @@ class ActivityImpl implements Activity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
checkMixinState (result: DisplayTx, firstTx: DisplayTx): boolean {
|
checkMixinState (result: DisplayTx, firstTx: DisplayTx): boolean {
|
||||||
if (this.client.getHierarchy().isDerived(result.tx._class, core.class.TxMixin) && result.doc !== undefined) {
|
if (this.hierarchy.isDerived(result.tx._class, core.class.TxMixin) && result.doc !== undefined) {
|
||||||
const mix = result.tx as TxMixin<Doc, Doc>
|
const mix = result.tx as TxMixin<Doc, Doc>
|
||||||
firstTx.doc = TxProcessor.updateMixin4Doc(result.doc, mix)
|
firstTx.doc = TxProcessor.updateMixin4Doc(result.doc, mix)
|
||||||
firstTx.mixin = true
|
firstTx.mixin = true
|
||||||
@ -339,7 +377,7 @@ class ActivityImpl implements Activity {
|
|||||||
const newResult = results.filter((prevTx) => {
|
const newResult = results.filter((prevTx) => {
|
||||||
const prevUpdate: any = getCombineOpFromTx(prevTx)
|
const prevUpdate: any = getCombineOpFromTx(prevTx)
|
||||||
// If same tx or same collection
|
// If same tx or same collection
|
||||||
if (this.isSameKindTx(prevTx, result, result.tx._class) || prevUpdate === curUpdate) {
|
if (this.isSameKindTx(prevTx, result) || prevUpdate === curUpdate) {
|
||||||
if (result.tx.modifiedOn - prevTx.tx.modifiedOn < combineThreshold && isEqualOps(prevUpdate, curUpdate)) {
|
if (result.tx.modifiedOn - prevTx.tx.modifiedOn < combineThreshold && isEqualOps(prevUpdate, curUpdate)) {
|
||||||
// we have same keys,
|
// we have same keys,
|
||||||
// Remember previous transactions
|
// Remember previous transactions
|
||||||
@ -363,11 +401,10 @@ class ActivityImpl implements Activity {
|
|||||||
return newResult
|
return newResult
|
||||||
}
|
}
|
||||||
|
|
||||||
isSameKindTx (prevTx: DisplayTx, result: DisplayTx, _class: Ref<Class<Doc>>): boolean {
|
isSameKindTx (prevTx: DisplayTx, result: DisplayTx): boolean {
|
||||||
return (
|
return (
|
||||||
prevTx.tx.objectId === result.tx.objectId && // Same document id
|
prevTx.tx.objectId === result.tx.objectId && // Same document id
|
||||||
prevTx.tx._class === result.tx._class && // Same transaction class
|
prevTx.tx._class === result.tx._class && // Same transaction class
|
||||||
result.tx._class === _class &&
|
|
||||||
prevTx.tx.modifiedBy === result.tx.modifiedBy // Same user
|
prevTx.tx.modifiedBy === result.tx.modifiedBy // Same user
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -402,18 +439,6 @@ export function newDisplayTx (tx: TxCUD<Doc>, hierarchy: Hierarchy): DisplayTx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCollectionTx (cltx: TxCollectionCUD<Doc, AttachedDoc>): TxCUD<Doc> {
|
|
||||||
if (cltx.tx._class === core.class.TxCreateDoc) {
|
|
||||||
// We need to update tx to contain attachedDoc, attachedClass & collection
|
|
||||||
const create = cltx.tx as TxCreateDoc<AttachedDoc>
|
|
||||||
create.attributes.attachedTo = cltx.objectId
|
|
||||||
create.attributes.attachedToClass = cltx.objectClass
|
|
||||||
create.attributes.collection = cltx.collection
|
|
||||||
return create
|
|
||||||
}
|
|
||||||
return cltx.tx
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct an new activity, to listend for displayed transactions in UI.
|
* Construct an new activity, to listend for displayed transactions in UI.
|
||||||
* @param client
|
* @param client
|
||||||
|
@ -200,7 +200,7 @@
|
|||||||
{#if viewlet === undefined && model.length > 0 && tx.updateTx}
|
{#if viewlet === undefined && model.length > 0 && tx.updateTx}
|
||||||
{#each model as m, i}
|
{#each model as m, i}
|
||||||
{#await getValue(client, m, tx) then value}
|
{#await getValue(client, m, tx) then value}
|
||||||
{#if value.set === null}
|
{#if value.set === null || value.set === undefined}
|
||||||
<span class="lower"><Label label={activity.string.Unset} /> <Label label={m.label} /></span>
|
<span class="lower"><Label label={activity.string.Unset} /> <Label label={m.label} /></span>
|
||||||
{:else if value.added.length}
|
{:else if value.added.length}
|
||||||
<span class="lower" class:flex-grow={hasMessageType}>
|
<span class="lower" class:flex-grow={hasMessageType}>
|
||||||
|
@ -192,19 +192,28 @@ function getModifiedAttributes (tx: DisplayTx): any[] {
|
|||||||
return [{}]
|
return [{}]
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildRemovedDoc (client: TxOperations, objectId: Ref<Doc>): Promise<Doc | undefined> {
|
async function buildRemovedDoc (
|
||||||
const txes = await client.findAll(core.class.TxCUD, { objectId }, { sort: { modifiedOn: 1 } })
|
client: TxOperations,
|
||||||
let doc: Doc
|
objectId: Ref<Doc>,
|
||||||
let createTx = txes.find((tx) => tx._class === core.class.TxCreateDoc)
|
_class: Ref<Class<Doc>>
|
||||||
if (createTx === undefined) {
|
): Promise<Doc | undefined> {
|
||||||
const collectionTxes = txes.filter((tx) => tx._class === core.class.TxCollectionCUD) as Array<
|
const isAttached = client.getHierarchy().isDerived(_class, core.class.AttachedDoc)
|
||||||
TxCollectionCUD<Doc, AttachedDoc>
|
const txes = await client.findAll<TxCUD<Doc>>(
|
||||||
>
|
isAttached ? core.class.TxCollectionCUD : core.class.TxCUD,
|
||||||
createTx = collectionTxes.find((p) => p.tx._class === core.class.TxCreateDoc)
|
isAttached
|
||||||
}
|
? { 'tx.objectId': objectId as Ref<AttachedDoc> }
|
||||||
|
: {
|
||||||
|
objectId
|
||||||
|
},
|
||||||
|
{ sort: { modifiedOn: 1 } }
|
||||||
|
)
|
||||||
|
const createTx = isAttached
|
||||||
|
? txes.find((tx) => (tx as TxCollectionCUD<Doc, AttachedDoc>).tx._class === core.class.TxCreateDoc)
|
||||||
|
: txes.find((tx) => tx._class === core.class.TxCreateDoc)
|
||||||
if (createTx === undefined) return
|
if (createTx === undefined) return
|
||||||
doc = TxProcessor.createDoc2Doc(createTx as TxCreateDoc<Doc>)
|
let doc = TxProcessor.createDoc2Doc(createTx as TxCreateDoc<Doc>)
|
||||||
for (const tx of txes) {
|
for (let tx of txes) {
|
||||||
|
tx = TxProcessor.extractTx(tx) as TxCUD<Doc>
|
||||||
if (tx._class === core.class.TxUpdateDoc) {
|
if (tx._class === core.class.TxUpdateDoc) {
|
||||||
doc = TxProcessor.updateDoc2Doc(doc, tx as TxUpdateDoc<Doc>)
|
doc = TxProcessor.updateDoc2Doc(doc, tx as TxUpdateDoc<Doc>)
|
||||||
} else if (tx._class === core.class.TxMixin) {
|
} else if (tx._class === core.class.TxMixin) {
|
||||||
@ -216,7 +225,8 @@ async function buildRemovedDoc (client: TxOperations, objectId: Ref<Doc>): Promi
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getAllRealValues (client: TxOperations, values: any[], _class: Ref<Class<Doc>>): Promise<any[]> {
|
async function getAllRealValues (client: TxOperations, values: any[], _class: Ref<Class<Doc>>): Promise<any[]> {
|
||||||
if (!client.getHierarchy().isDerived(_class, core.class.Doc) || values.some((value) => typeof value !== 'string')) {
|
if (values.length === 0) return []
|
||||||
|
if (values.some((value) => typeof value !== 'string')) {
|
||||||
return values
|
return values
|
||||||
}
|
}
|
||||||
const realValues = await client.findAll(_class, { _id: { $in: values } })
|
const realValues = await client.findAll(_class, { _id: { $in: values } })
|
||||||
@ -226,13 +236,12 @@ async function getAllRealValues (client: TxOperations, values: any[], _class: Re
|
|||||||
...(await Promise.all(
|
...(await Promise.all(
|
||||||
values
|
values
|
||||||
.filter((value) => !realValuesIds.includes(value))
|
.filter((value) => !realValuesIds.includes(value))
|
||||||
.map(async (value) => await buildRemovedDoc(client, value))
|
.map(async (value) => await buildRemovedDoc(client, value, _class))
|
||||||
))
|
))
|
||||||
].filter((v) => v != null)
|
].filter((v) => v != null)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getValue (client: TxOperations, m: AttributeModel, tx: DisplayTx): Promise<any> {
|
function combineAttributes (attributes: any[], key: string, operator: string, arrayKey: string): any[] {
|
||||||
function combineAttributes (attributes: any[], key: string, operator: string, arrayKey: string): any[] {
|
|
||||||
return Array.from(
|
return Array.from(
|
||||||
new Set(
|
new Set(
|
||||||
attributes.flatMap((attr) =>
|
attributes.flatMap((attr) =>
|
||||||
@ -240,7 +249,9 @@ export async function getValue (client: TxOperations, m: AttributeModel, tx: Dis
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
).filter((v) => v != null)
|
).filter((v) => v != null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getValue (client: TxOperations, m: AttributeModel, tx: DisplayTx): Promise<any> {
|
||||||
const utxs = getModifiedAttributes(tx)
|
const utxs = getModifiedAttributes(tx)
|
||||||
const value = {
|
const value = {
|
||||||
set: utxs[0][m.key],
|
set: utxs[0][m.key],
|
||||||
|
@ -15,8 +15,8 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { TxViewlet } from '@hcengineering/activity'
|
import { TxViewlet } from '@hcengineering/activity'
|
||||||
import { ActivityKey, DisplayTx, getCollectionTx, newDisplayTx, TxView } from '@hcengineering/activity-resources'
|
import { ActivityKey, DisplayTx, newDisplayTx, TxView } from '@hcengineering/activity-resources'
|
||||||
import core, { AttachedDoc, Doc, TxCollectionCUD, WithLookup } from '@hcengineering/core'
|
import core, { Doc, TxCUD, TxProcessor, WithLookup } from '@hcengineering/core'
|
||||||
import { Notification, NotificationStatus } from '@hcengineering/notification'
|
import { Notification, NotificationStatus } from '@hcengineering/notification'
|
||||||
import { getClient } from '@hcengineering/presentation'
|
import { getClient } from '@hcengineering/presentation'
|
||||||
import { ActionIcon, Component, getPlatformColor, IconBack, IconCheck, IconDelete } from '@hcengineering/ui'
|
import { ActionIcon, Component, getPlatformColor, IconBack, IconCheck, IconDelete } from '@hcengineering/ui'
|
||||||
@ -32,7 +32,7 @@
|
|||||||
let tx = notification.$lookup?.tx
|
let tx = notification.$lookup?.tx
|
||||||
if (tx) {
|
if (tx) {
|
||||||
if (hierarchy.isDerived(tx._class, core.class.TxCollectionCUD)) {
|
if (hierarchy.isDerived(tx._class, core.class.TxCollectionCUD)) {
|
||||||
tx = getCollectionTx(tx as TxCollectionCUD<Doc, AttachedDoc>)
|
tx = TxProcessor.extractTx(tx) as TxCUD<Doc>
|
||||||
}
|
}
|
||||||
return newDisplayTx(tx, hierarchy)
|
return newDisplayTx(tx, hierarchy)
|
||||||
}
|
}
|
||||||
|
@ -271,19 +271,24 @@ class TServerStorage implements ServerStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async buildRemovedDoc (ctx: MeasureContext, tx: TxRemoveDoc<Doc>): Promise<Doc | undefined> {
|
private async buildRemovedDoc (ctx: MeasureContext, tx: TxRemoveDoc<Doc>): Promise<Doc | undefined> {
|
||||||
const txes = await this.findAll(ctx, core.class.TxCUD, { objectId: tx.objectId }, { sort: { modifiedOn: 1 } })
|
const isAttached = this.hierarchy.isDerived(tx.objectClass, core.class.AttachedDoc)
|
||||||
let doc: Doc
|
const txes = await this.findAll<TxCUD<Doc>>(
|
||||||
let createTx = txes.find((tx) => tx._class === core.class.TxCreateDoc)
|
ctx,
|
||||||
if (createTx === undefined) {
|
isAttached ? core.class.TxCollectionCUD : core.class.TxCUD,
|
||||||
const collectionTxes = txes.filter((tx) => tx._class === core.class.TxCollectionCUD) as TxCollectionCUD<
|
isAttached
|
||||||
Doc,
|
? { 'tx.objectId': tx.objectId as Ref<AttachedDoc> }
|
||||||
AttachedDoc
|
: {
|
||||||
>[]
|
objectId: tx.objectId
|
||||||
createTx = collectionTxes.find((p) => p.tx._class === core.class.TxCreateDoc)
|
},
|
||||||
}
|
{ sort: { modifiedOn: 1 } }
|
||||||
|
)
|
||||||
|
const createTx = isAttached
|
||||||
|
? txes.find((tx) => (tx as TxCollectionCUD<Doc, AttachedDoc>).tx._class === core.class.TxCreateDoc)
|
||||||
|
: txes.find((tx) => tx._class === core.class.TxCreateDoc)
|
||||||
if (createTx === undefined) return
|
if (createTx === undefined) return
|
||||||
doc = TxProcessor.createDoc2Doc(createTx as TxCreateDoc<Doc>)
|
let doc = TxProcessor.createDoc2Doc(createTx as TxCreateDoc<Doc>)
|
||||||
for (const tx of txes) {
|
for (let tx of txes) {
|
||||||
|
tx = TxProcessor.extractTx(tx) as TxCUD<Doc>
|
||||||
if (tx._class === core.class.TxUpdateDoc) {
|
if (tx._class === core.class.TxUpdateDoc) {
|
||||||
doc = TxProcessor.updateDoc2Doc(doc, tx as TxUpdateDoc<Doc>)
|
doc = TxProcessor.updateDoc2Doc(doc, tx as TxUpdateDoc<Doc>)
|
||||||
} else if (tx._class === core.class.TxMixin) {
|
} else if (tx._class === core.class.TxMixin) {
|
||||||
|
Loading…
Reference in New Issue
Block a user