mirror of
https://github.com/hcengineering/platform.git
synced 2025-05-10 01:15:03 +00:00
Fix nested tags (#8474)
Some checks are pending
CI / build (push) Waiting to run
CI / svelte-check (push) Blocked by required conditions
CI / formatting (push) Blocked by required conditions
CI / test (push) Blocked by required conditions
CI / uitest (push) Waiting to run
CI / uitest-pg (push) Waiting to run
CI / uitest-qms (push) Waiting to run
CI / uitest-workspaces (push) Waiting to run
CI / docker-build (push) Blocked by required conditions
CI / dist-build (push) Blocked by required conditions
Some checks are pending
CI / build (push) Waiting to run
CI / svelte-check (push) Blocked by required conditions
CI / formatting (push) Blocked by required conditions
CI / test (push) Blocked by required conditions
CI / uitest (push) Waiting to run
CI / uitest-pg (push) Waiting to run
CI / uitest-qms (push) Waiting to run
CI / uitest-workspaces (push) Waiting to run
CI / docker-build (push) Blocked by required conditions
CI / dist-build (push) Blocked by required conditions
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
parent
3c58f2b7f9
commit
099fb90c59
@ -94,6 +94,15 @@ export function createModel (builder: Builder): void {
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverCard.trigger.OnCardTag,
|
||||
isAsync: true,
|
||||
txMatch: {
|
||||
_class: core.class.TxMixin,
|
||||
objectClass: card.class.Card
|
||||
}
|
||||
})
|
||||
|
||||
builder.mixin(card.class.Card, core.class.Class, serverCore.mixin.SearchPresenter, {
|
||||
searchIcon: card.icon.Card,
|
||||
title: [['title']]
|
||||
|
@ -252,7 +252,7 @@ export class TxOperations implements Omit<Client, 'notify' | 'getConnection'> {
|
||||
if (hierarchy.isMixin(mixClass)) {
|
||||
const baseClass = hierarchy.getBaseClass(doc._class)
|
||||
|
||||
const byClass = this.splitMixinUpdate(update, mixClass, baseClass)
|
||||
const byClass = splitMixinUpdate(hierarchy, update, mixClass, baseClass)
|
||||
const ops = this.apply(doc._id)
|
||||
for (const it of byClass) {
|
||||
if (hierarchy.isMixin(it[0])) {
|
||||
@ -323,17 +323,7 @@ export class TxOperations implements Omit<Client, 'notify' | 'getConnection'> {
|
||||
date?: Timestamp,
|
||||
account?: PersonId
|
||||
): Promise<T> {
|
||||
// We need to update fields if they are different.
|
||||
const documentUpdate: DocumentUpdate<T> = {}
|
||||
for (const [k, v] of Object.entries(update)) {
|
||||
if (['_class', '_id', 'modifiedBy', 'modifiedOn', 'space', 'attachedTo', 'attachedToClass'].includes(k)) {
|
||||
continue
|
||||
}
|
||||
const dv = (doc as any)[k]
|
||||
if (!deepEqual(dv, v) && v !== undefined) {
|
||||
;(documentUpdate as any)[k] = v
|
||||
}
|
||||
}
|
||||
const documentUpdate = getDiffUpdate(doc, update)
|
||||
if (Object.keys(documentUpdate).length > 0) {
|
||||
await this.update(doc, documentUpdate, false, date ?? Date.now(), account)
|
||||
TxProcessor.applyUpdate(doc, documentUpdate)
|
||||
@ -372,56 +362,71 @@ export class TxOperations implements Omit<Client, 'notify' | 'getConnection'> {
|
||||
}
|
||||
return doc
|
||||
}
|
||||
}
|
||||
|
||||
private splitMixinUpdate<T extends Doc>(
|
||||
update: DocumentUpdate<T>,
|
||||
mixClass: Ref<Class<T>>,
|
||||
baseClass: Ref<Class<T>>
|
||||
): Map<Ref<Class<Doc>>, DocumentUpdate<T>> {
|
||||
const hierarchy = this.getHierarchy()
|
||||
const attributes = hierarchy.getAllAttributes(mixClass)
|
||||
|
||||
const updateAttrs = Object.fromEntries(
|
||||
Object.entries(update).filter((it) => !it[0].startsWith('$'))
|
||||
) as DocumentUpdate<T>
|
||||
const updateOps = Object.fromEntries(
|
||||
Object.entries(update).filter((it) => it[0].startsWith('$'))
|
||||
) as DocumentUpdate<T>
|
||||
|
||||
const result: Map<Ref<Class<Doc>>, DocumentUpdate<T>> = this.splitObjectAttributes(
|
||||
updateAttrs,
|
||||
baseClass,
|
||||
attributes
|
||||
)
|
||||
|
||||
for (const [key, value] of Object.entries(updateOps)) {
|
||||
const updates = this.splitObjectAttributes(value as object, baseClass, attributes)
|
||||
|
||||
for (const [opsClass, opsUpdate] of updates) {
|
||||
const upd: DocumentUpdate<T> = result.get(opsClass) ?? {}
|
||||
result.set(opsClass, { ...upd, [key]: opsUpdate })
|
||||
}
|
||||
export function getDiffUpdate<T extends Doc> (doc: T, update: T | Data<T> | DocumentUpdate<T>): DocumentUpdate<T> {
|
||||
// We need to update fields if they are different.
|
||||
const documentUpdate: DocumentUpdate<T> = {}
|
||||
for (const [k, v] of Object.entries(update)) {
|
||||
if (['_class', '_id', 'modifiedBy', 'modifiedOn', 'space', 'attachedTo', 'attachedToClass'].includes(k)) {
|
||||
continue
|
||||
}
|
||||
const dv = (doc as any)[k]
|
||||
if (!deepEqual(dv, v) && v !== undefined) {
|
||||
;(documentUpdate as any)[k] = v
|
||||
}
|
||||
}
|
||||
return documentUpdate
|
||||
}
|
||||
|
||||
return result
|
||||
export function splitMixinUpdate<T extends Doc> (
|
||||
hierarchy: Hierarchy,
|
||||
update: DocumentUpdate<T>,
|
||||
mixClass: Ref<Class<T>>,
|
||||
baseClass: Ref<Class<T>>
|
||||
): Map<Ref<Class<Doc>>, DocumentUpdate<T>> {
|
||||
const attributes = hierarchy.getAllAttributes(mixClass)
|
||||
|
||||
const updateAttrs = Object.fromEntries(
|
||||
Object.entries(update).filter((it) => !it[0].startsWith('$'))
|
||||
) as DocumentUpdate<T>
|
||||
const updateOps = Object.fromEntries(
|
||||
Object.entries(update).filter((it) => it[0].startsWith('$'))
|
||||
) as DocumentUpdate<T>
|
||||
|
||||
const result: Map<Ref<Class<Doc>>, DocumentUpdate<T>> = splitObjectAttributes(
|
||||
hierarchy,
|
||||
updateAttrs,
|
||||
baseClass,
|
||||
attributes
|
||||
)
|
||||
|
||||
for (const [key, value] of Object.entries(updateOps)) {
|
||||
const updates = splitObjectAttributes(hierarchy, value as object, baseClass, attributes)
|
||||
|
||||
for (const [opsClass, opsUpdate] of updates) {
|
||||
const upd: DocumentUpdate<T> = result.get(opsClass) ?? {}
|
||||
result.set(opsClass, { ...upd, [key]: opsUpdate })
|
||||
}
|
||||
}
|
||||
|
||||
private splitObjectAttributes<T extends object>(
|
||||
obj: T,
|
||||
objClass: Ref<Class<Doc>>,
|
||||
attributes: Map<string, AnyAttribute>
|
||||
): Map<Ref<Class<Doc>>, object> {
|
||||
const hierarchy = this.getHierarchy()
|
||||
return result
|
||||
}
|
||||
|
||||
const result = new Map<Ref<Class<Doc>>, any>()
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
const attributeOf = attributes.get(key)?.attributeOf
|
||||
const clazz = attributeOf !== undefined && hierarchy.isMixin(attributeOf) ? attributeOf : objClass
|
||||
result.set(clazz, { ...(result.get(clazz) ?? {}), [key]: value })
|
||||
}
|
||||
|
||||
return result
|
||||
function splitObjectAttributes<T extends object> (
|
||||
hierarchy: Hierarchy,
|
||||
obj: T,
|
||||
objClass: Ref<Class<Doc>>,
|
||||
attributes: Map<string, AnyAttribute>
|
||||
): Map<Ref<Class<Doc>>, object> {
|
||||
const result = new Map<Ref<Class<Doc>>, any>()
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
const attributeOf = attributes.get(key)?.attributeOf
|
||||
const clazz = attributeOf !== undefined && hierarchy.isMixin(attributeOf) ? attributeOf : objClass
|
||||
result.set(clazz, { ...(result.get(clazz) ?? {}), [key]: value })
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export interface CommitResult {
|
||||
|
@ -16,7 +16,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import card, { Card, Tag } from '@hcengineering/card'
|
||||
import { Class, Doc, fillDefaults, Ref } from '@hcengineering/core'
|
||||
import { Class, Doc, Mixin, Ref } from '@hcengineering/core'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import {
|
||||
ButtonIcon,
|
||||
@ -77,14 +77,17 @@
|
||||
async (res) => {
|
||||
if (res !== undefined) {
|
||||
await client.createMixin(doc._id, doc._class, doc.space, res, {})
|
||||
const updated = fillDefaults(hierarchy, hierarchy.clone(doc), res)
|
||||
await client.diffUpdate(doc, updated)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
let divScroll: HTMLElement
|
||||
|
||||
function isRemoveable (mixinId: Ref<Mixin<Doc>>, activeTags: Tag[]): boolean {
|
||||
const desc = hierarchy.getDescendants(mixinId)
|
||||
return !desc.some((p) => hierarchy.hasMixin(doc, p) && p !== mixinId)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="container py-4 gap-2">
|
||||
@ -94,9 +97,12 @@
|
||||
<ScrollerBar gap={'none'} bind:scroller={divScroll}>
|
||||
<div class="tags gap-2">
|
||||
{#each activeTags as mixin}
|
||||
<div class="tag no-word-wrap">
|
||||
{@const removable = isRemoveable(mixin._id, activeTags)}
|
||||
<div class="tag no-word-wrap" class:removable>
|
||||
<Label label={mixin.label} />
|
||||
<ButtonIcon icon={IconClose} size="extra-small" kind="tertiary" on:click={() => removeTag(mixin._id)} />
|
||||
{#if removable}
|
||||
<ButtonIcon icon={IconClose} size="extra-small" kind="tertiary" on:click={() => removeTag(mixin._id)} />
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
{#if dropdownItems.length > 0}
|
||||
@ -114,7 +120,7 @@
|
||||
align-items: center;
|
||||
|
||||
.tag {
|
||||
padding: 0.25rem 0.25rem 0.25rem 0.5rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
height: 1.5rem;
|
||||
border: 1px solid var(--theme-content-color);
|
||||
|
||||
@ -126,6 +132,10 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.25rem;
|
||||
|
||||
&.removable {
|
||||
padding-right: 0.25rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,9 +18,14 @@ import core, {
|
||||
AnyAttribute,
|
||||
Data,
|
||||
Doc,
|
||||
fillDefaults,
|
||||
getDiffUpdate,
|
||||
Mixin,
|
||||
Ref,
|
||||
splitMixinUpdate,
|
||||
Tx,
|
||||
TxCreateDoc,
|
||||
TxMixin,
|
||||
TxProcessor,
|
||||
TxRemoveDoc,
|
||||
TxUpdateDoc
|
||||
@ -391,6 +396,40 @@ async function OnCardCreate (ctx: TxCreateDoc<Card>[], control: TriggerControl):
|
||||
return res
|
||||
}
|
||||
|
||||
export async function OnCardTag (ctx: TxMixin<Card, Card>[], control: TriggerControl): Promise<Tx[]> {
|
||||
const res: Tx[] = []
|
||||
for (const tx of ctx) {
|
||||
if (tx.space === core.space.DerivedTx) continue
|
||||
if (tx._class !== core.class.TxMixin) continue
|
||||
const target = tx.mixin
|
||||
const to = control.hierarchy.getBaseClass(target)
|
||||
const ancestors = control.hierarchy.getAncestors(target).filter((p) => control.hierarchy.isDerived(p, to))
|
||||
const mixinAncestors: Ref<Mixin<Doc>>[] = []
|
||||
const doc = (await control.findAll(control.ctx, tx.objectClass, { _id: tx.objectId }))[0]
|
||||
if (doc === undefined) continue
|
||||
for (const anc of ancestors) {
|
||||
if (anc === target) continue
|
||||
if (control.hierarchy.hasMixin(doc, anc)) break
|
||||
if (anc === to) break
|
||||
mixinAncestors.unshift(anc)
|
||||
}
|
||||
for (const anc of mixinAncestors) {
|
||||
res.push(control.txFactory.createTxMixin(doc._id, doc._class, doc.space, anc, {}))
|
||||
}
|
||||
const updated = fillDefaults(control.hierarchy, control.hierarchy.as(control.hierarchy.clone(doc), target), target)
|
||||
const diff = getDiffUpdate(doc, updated)
|
||||
const splitted = splitMixinUpdate(control.hierarchy, diff, target, doc._class)
|
||||
for (const it of splitted) {
|
||||
if (control.hierarchy.isMixin(it[0])) {
|
||||
res.push(control.txFactory.createTxMixin(doc._id, doc._class, doc.space, it[0], it[1]))
|
||||
} else {
|
||||
res.push(control.txFactory.createTxUpdateDoc(it[0], doc.space, doc._id, it[1]))
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
export default async () => ({
|
||||
trigger: {
|
||||
@ -401,6 +440,7 @@ export default async () => ({
|
||||
OnTagRemove,
|
||||
OnCardRemove,
|
||||
OnCardCreate,
|
||||
OnCardUpdate
|
||||
OnCardUpdate,
|
||||
OnCardTag
|
||||
}
|
||||
})
|
||||
|
@ -34,6 +34,7 @@ export default plugin(serverCardId, {
|
||||
OnMasterTagRemove: '' as Resource<TriggerFunc>,
|
||||
OnCardCreate: '' as Resource<TriggerFunc>,
|
||||
OnCardUpdate: '' as Resource<TriggerFunc>,
|
||||
OnCardTag: '' as Resource<TriggerFunc>,
|
||||
OnCardRemove: '' as Resource<TriggerFunc>
|
||||
}
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user