Fix card parent loop

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2025-06-03 13:14:23 +05:00
parent 1705a46bb4
commit c3c4c5c5a0
No known key found for this signature in database
GPG Key ID: 211936D31001B31C
3 changed files with 97 additions and 15 deletions

View File

@ -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<void> {
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<Ref<Card>, 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<Card>,
cache: Map<Ref<Card>, Card>
): Promise<Card | undefined> {
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<void> {
const txOp = new TxOperations(client, core.account.System)
const desc = client

View File

@ -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<Card>(
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
)

View File

@ -403,6 +403,21 @@ async function OnCardCreate (ctx: TxCreateDoc<Card>[], 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
})
)
}
}
}