diff --git a/packages/text/src/index.ts b/packages/text/src/index.ts index dfcdc7e589..87456d522c 100644 --- a/packages/text/src/index.ts +++ b/packages/text/src/index.ts @@ -16,6 +16,7 @@ export * from './extensions' export * from './markup/dsl' export * from './markup/model' +export * from './markup/reference' export * from './markup/traverse' export * from './markup/utils' export * from './nodes' diff --git a/packages/text/src/markup/__tests__/traverse.test.ts b/packages/text/src/markup/__tests__/traverse.test.ts index d21e1ce1b7..cafd4506dd 100644 --- a/packages/text/src/markup/__tests__/traverse.test.ts +++ b/packages/text/src/markup/__tests__/traverse.test.ts @@ -17,8 +17,8 @@ describe('traverseNode', () => { traverseNode(node as MarkupNode, callback) expect(callback).toHaveBeenCalledTimes(2) - expect(callback).toHaveBeenCalledWith(node) - expect(callback).toHaveBeenCalledWith(node.content[0]) + expect(callback).toHaveBeenCalledWith(node, undefined) + expect(callback).toHaveBeenCalledWith(node.content[0], node) }) it('should stop traversing if the callback returns false', () => { @@ -40,7 +40,7 @@ describe('traverseNode', () => { traverseNode(node, callback) expect(callback).toHaveBeenCalledTimes(1) - expect(callback).toHaveBeenCalledWith(node) + expect(callback).toHaveBeenCalledWith(node, undefined) }) }) diff --git a/packages/text/src/markup/model.ts b/packages/text/src/markup/model.ts index 453f729ea4..ae0b052aa6 100644 --- a/packages/text/src/markup/model.ts +++ b/packages/text/src/markup/model.ts @@ -84,6 +84,7 @@ export interface LinkMark extends MarkupMark { } /** @public */ -export interface ReferenceMark extends MarkupMark { +export interface ReferenceMarkupNode extends MarkupNode { + type: MarkupNodeType.reference attrs: { id: string, label: string, objectclass: string } } diff --git a/packages/text/src/markup/reference.ts b/packages/text/src/markup/reference.ts new file mode 100644 index 0000000000..a9e4c64261 --- /dev/null +++ b/packages/text/src/markup/reference.ts @@ -0,0 +1,49 @@ +// +// Copyright © 2023 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { Class, Doc, Ref } from '@hcengineering/core' +import { MarkupNode, MarkupNodeType, ReferenceMarkupNode } from '../markup/model' +import { traverseNode } from '../markup/traverse' + +/** + * @public + */ +export interface Reference { + objectId: Ref + objectClass: Ref> + parentNode: MarkupNode | null +} + +/** + * @public + */ +export function extractReferences (content: MarkupNode): Array { + const result: Array = [] + + traverseNode(content, (node, parent) => { + if (node.type === MarkupNodeType.reference) { + const reference = node as ReferenceMarkupNode + const objectId = reference.attrs.id as Ref + const objectClass = reference.attrs.objectclass as Ref> + const e = result.find((e) => e.objectId === objectId && e.objectClass === objectClass) + if (e === undefined) { + result.push({ objectId, objectClass, parentNode: parent ?? node }) + } + } + return true + }) + + return result +} diff --git a/packages/text/src/markup/traverse.ts b/packages/text/src/markup/traverse.ts index 4aacf27ae4..08f6dc1e5e 100644 --- a/packages/text/src/markup/traverse.ts +++ b/packages/text/src/markup/traverse.ts @@ -15,11 +15,22 @@ import { MarkupMark, MarkupNode } from './model' -export function traverseNode (node: MarkupNode, f: (el: MarkupNode) => boolean | undefined): void { - const result = f(node) +export function traverseNode ( + node: MarkupNode, + fn: (el: MarkupNode, parent: MarkupNode | undefined) => boolean | undefined +): void { + _traverseNode(node, undefined, fn) +} + +function _traverseNode ( + node: MarkupNode, + parent: MarkupNode | undefined, + fn: (el: MarkupNode, parent: MarkupNode | undefined) => boolean | undefined +): void { + const result = fn(node, parent) if (result !== false) { node.content?.forEach((p) => { - traverseNode(p, f) + _traverseNode(p, node, fn) }) } } diff --git a/packages/text/src/nodes/comment.ts b/packages/text/src/nodes/comment.ts index 1b09c0358b..9bec970fa6 100644 --- a/packages/text/src/nodes/comment.ts +++ b/packages/text/src/nodes/comment.ts @@ -14,14 +14,6 @@ // import { Node } from '@tiptap/core' -import { Node as ProseMirrorNode } from '@tiptap/pm/model' - -/** - * @public - */ -export interface Comment { - parentNode: ProseMirrorNode | null -} /** * @public diff --git a/packages/text/src/nodes/reference.ts b/packages/text/src/nodes/reference.ts index 71756022c5..17e69ae8d6 100644 --- a/packages/text/src/nodes/reference.ts +++ b/packages/text/src/nodes/reference.ts @@ -13,42 +13,9 @@ // limitations under the License. // -import { Class, Doc, Ref } from '@hcengineering/core' import { Node, mergeAttributes } from '@tiptap/core' -import { Node as ProseMirrorNode } from '@tiptap/pm/model' import { getDataAttribute } from './utils' -/** - * @public - */ -export interface Reference { - objectId: Ref - objectClass: Ref> - parentNode: ProseMirrorNode | null -} - -/** - * @public - */ -export function extractReferences (content: ProseMirrorNode): Array { - const result: Array = [] - - content.descendants((node, _pos, parent): boolean => { - if (node.type.name === ReferenceNode.name) { - const objectId = node.attrs.id as Ref - const objectClass = node.attrs.objectclass as Ref> - const e = result.find((e) => e.objectId === objectId && e.objectClass === objectClass) - if (e === undefined) { - result.push({ objectId, objectClass, parentNode: parent }) - } - } - - return true - }) - - return result -} - export interface ReferenceOptions { renderLabel: (props: { options: ReferenceOptions, node: any }) => string suggestion: { char: string } diff --git a/server-plugins/activity-resources/src/__tests__/references.test.ts b/server-plugins/activity-resources/src/__tests__/references.test.ts index 23a3fda4fa..0ce0f2895d 100644 --- a/server-plugins/activity-resources/src/__tests__/references.test.ts +++ b/server-plugins/activity-resources/src/__tests__/references.test.ts @@ -73,7 +73,7 @@ describe('extractBacklinks', () => { srcDocId: 'srcDocId', srcDocClass: 'srcDocClass', message: - '{"type":"paragraph","content":[{"type":"reference","attrs":{"id":"id","objectclass":"contact:class:Person","label":"Appleseed John","class":null}},{"type":"text","text":" hello"}]}', + '{"type":"paragraph","content":[{"type":"reference","attrs":{"id":"id","objectclass":"contact:class:Person","label":"Appleseed John"}},{"type":"text","text":" hello"}]}', attachedDocId: 'attachedDocId', attachedDocClass: 'attachedDocClass' } diff --git a/server-plugins/activity-resources/src/references.ts b/server-plugins/activity-resources/src/references.ts index e67ed3cf1a..2b455591e9 100644 --- a/server-plugins/activity-resources/src/references.ts +++ b/server-plugins/activity-resources/src/references.ts @@ -23,6 +23,7 @@ import core, { Doc, generateId, Hierarchy, + Markup, Ref, Space, Tx, @@ -50,12 +51,12 @@ import { toReceiverInfo, type NotificationProviderControl } from '@hcengineering/server-notification-resources' -import { areEqualJson, extractReferences, markupToPmNode, pmNodeToMarkup } from '@hcengineering/text' +import { areEqualJson, extractReferences, jsonToMarkup, markupToJSON } from '@hcengineering/text' export function isDocMentioned (doc: Ref, content: string): boolean { const references = [] - const node = markupToPmNode(content) + const node = markupToJSON(content) references.push(...extractReferences(node)) for (const ref of references) { @@ -491,13 +492,13 @@ export function getReferencesData ( srcDocClass: Ref>, attachedDocId: Ref | undefined, attachedDocClass: Ref> | undefined, - content: string + content: Markup ): Array> { const result: Array> = [] const references = [] - const doc = markupToPmNode(content) - references.push(...extractReferences(doc)) + const node = markupToJSON(content) + references.push(...extractReferences(node)) for (const ref of references) { if (ref.objectId !== attachedDocId && ref.objectId !== srcDocId) { @@ -507,7 +508,7 @@ export function getReferencesData ( collection: 'references', srcDocId, srcDocClass, - message: ref.parentNode !== null ? pmNodeToMarkup(ref.parentNode) : '', + message: ref.parentNode !== null ? jsonToMarkup(ref.parentNode) : '', attachedDocId, attachedDocClass })