diff --git a/models/card/src/migration.ts b/models/card/src/migration.ts index 3d064bd113..7f17bdd0ba 100644 --- a/models/card/src/migration.ts +++ b/models/card/src/migration.ts @@ -14,7 +14,7 @@ // import { type Card, cardId, DOMAIN_CARD } from '@hcengineering/card' -import core, { TxOperations, type Client, type Data, type Doc } from '@hcengineering/core' +import core, { type Ref, TxOperations, type Client, type Data, type Doc } from '@hcengineering/core' import { tryMigrate, tryUpgrade, @@ -67,11 +67,69 @@ export const cardOperation: MigrateOperation = { { state: 'default-labels', func: defaultLabels + }, + { + state: 'fill-parent-info', + mode: 'upgrade', + func: fillParentInfo } ]) } } +async function fillParentInfo (client: Client): Promise { + const txOp = new TxOperations(client, core.account.System) + const cards = await client.findAll(card.class.Card, { parentInfo: { $exists: false }, parent: { $ne: null } }) + const cache = new Map, Card>() + for (const val of cards) { + if (val.parent == null) continue + const parent = await getCardParentWithParentInfo(txOp, val.parent, cache) + if (parent !== undefined) { + const parentInfo = [ + ...(parent.parentInfo ?? []), + { + _id: parent._id, + _class: parent._class, + title: parent.title + } + ] + await txOp.update(val, { parentInfo }) + val.parentInfo = parentInfo + cache.set(val._id, val) + } + } +} + +async function getCardParentWithParentInfo ( + txOp: TxOperations, + _id: Ref, + cache: Map, Card> +): Promise { + const doc = cache.get(_id) ?? (await txOp.findOne(card.class.Card, { _id })) + if (doc === undefined) return + if (doc.parentInfo === undefined) { + if (doc.parent == null) { + doc.parentInfo = [] + } else { + const parent = await getCardParentWithParentInfo(txOp, doc.parent, cache) + if (parent !== undefined) { + doc.parentInfo = [ + ...(parent.parentInfo ?? []), + { + _id: parent._id, + _class: parent._class, + title: parent.title + } + ] + } else { + doc.parentInfo = [] + } + } + } + cache.set(doc._id, doc) + return doc +} + async function removeVariantViewlets (client: Client): Promise { const txOp = new TxOperations(client, core.account.System) const desc = client diff --git a/packages/ui-next/src/utils.ts b/packages/ui-next/src/utils.ts index 576a86724f..24bdd9552a 100644 --- a/packages/ui-next/src/utils.ts +++ b/packages/ui-next/src/utils.ts @@ -12,6 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. // +import cardPlugin, { type Card } from '@hcengineering/card' +import chat from '@hcengineering/chat' +import { type LinkPreviewData, type Message, MessageType } from '@hcengineering/communication-types' +import { getEmployeeBySocialId } from '@hcengineering/contact' +import { employeeByPersonIdStore } from '@hcengineering/contact-resources' import { fillDefaults, generateId, @@ -21,28 +26,23 @@ import { type Ref, SortingOrder } from '@hcengineering/core' -import { jsonToMarkup, markupToJSON, markupToText } from '@hcengineering/text' -import { showPopup } from '@hcengineering/ui' -import { markdownToMarkup, markupToMarkdown } from '@hcengineering/text-markdown' -import { type Message, MessageType, type LinkPreviewData } from '@hcengineering/communication-types' +import emojiPlugin from '@hcengineering/emoji' import { canDisplayLinkPreview, fetchLinkPreviewDetails, getClient, getCommunicationClient } from '@hcengineering/presentation' -import { employeeByPersonIdStore } from '@hcengineering/contact-resources' -import cardPlugin, { type Card } from '@hcengineering/card' -import { openDoc } from '@hcengineering/view-resources' -import { getEmployeeBySocialId } from '@hcengineering/contact' -import { get } from 'svelte/store' -import chat from '@hcengineering/chat' import { makeRank } from '@hcengineering/rank' -import emojiPlugin from '@hcengineering/emoji' +import { jsonToMarkup, markupToJSON, markupToText } from '@hcengineering/text' +import { markdownToMarkup, markupToMarkdown } from '@hcengineering/text-markdown' +import { showPopup } from '@hcengineering/ui' +import { openDoc } from '@hcengineering/view-resources' +import { get } from 'svelte/store' import IconAt from './components/icons/IconAt.svelte' -import { type TextInputAction } from './types' import uiNext from './plugin' +import { type TextInputAction } from './types' export const defaultMessageInputActions: TextInputAction[] = [ { @@ -115,13 +115,22 @@ export async function replyToThread (message: Message, parentCard: Card): Promis get(employeeByPersonIdStore).get(message.creator) ?? (await getEmployeeBySocialId(client, message.creator)) const lastOne = await client.findOne(cardPlugin.class.Card, {}, { sort: { rank: SortingOrder.Descending } }) const title = createThreadTitle(message, parentCard) - const data = fillDefaults( + const data = fillDefaults( hierarchy, { title, rank: makeRank(lastOne?.rank, undefined), content: '' as MarkupBlobRef, - parent: parentCard._id + parent: parentCard._id, + blobs: {}, + parentInfo: [ + ...(parentCard.parentInfo ?? []), + { + _id: parentCard._id, + _class: parentCard._class, + title: parentCard.title + } + ] }, chat.masterTag.Thread ) diff --git a/server-plugins/card-resources/src/index.ts b/server-plugins/card-resources/src/index.ts index b9204856b7..ddc4d5326f 100644 --- a/server-plugins/card-resources/src/index.ts +++ b/server-plugins/card-resources/src/index.ts @@ -403,6 +403,21 @@ async function OnCardCreate (ctx: TxCreateDoc[], control: TriggerControl): } }) ) + if ((doc.parentInfo?.length ?? 0) === 0) { + const parentInfo = [ + ...(parent.parentInfo ?? []), + { + _id: parent._id, + _class: parent._class, + title: parent.title + } + ] + res.push( + control.txFactory.createTxUpdateDoc(doc._class, doc.space, doc._id, { + parentInfo + }) + ) + } } }