mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 11:01:54 +03:00
UBERF-7844 Collaborative markup activity diff (#6320)
Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
This commit is contained in:
parent
319ce9cafc
commit
27717058cc
@ -54,7 +54,9 @@ function getActivityControl (client: MigrationClient): ActivityControl {
|
||||
modelDb: client.model,
|
||||
hierarchy: client.hierarchy,
|
||||
findAll: async (_class, query, options) =>
|
||||
toFindResult(await client.find(client.hierarchy.getDomain(_class), query, options))
|
||||
toFindResult(await client.find(client.hierarchy.getDomain(_class), query, options)),
|
||||
storageAdapter: client.storageAdapter,
|
||||
workspace: client.workspaceId
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -513,7 +513,7 @@ export function createModel (builder: Builder): void {
|
||||
})
|
||||
|
||||
builder.mixin(core.class.TypeCollaborativeDoc, core.class.Class, view.mixin.ActivityAttributePresenter, {
|
||||
presenter: view.component.CollaborativeDocActivityPresenter
|
||||
presenter: view.component.MarkupDiffPresenter
|
||||
})
|
||||
|
||||
builder.mixin(core.class.TypeCollaborativeDocVersion, core.class.Class, view.mixin.InlineAttributEditor, {
|
||||
|
@ -74,7 +74,6 @@ export default mergeIds(viewId, view, {
|
||||
HTMLEditor: '' as AnyComponent,
|
||||
CollaborativeHTMLEditor: '' as AnyComponent,
|
||||
CollaborativeDocEditor: '' as AnyComponent,
|
||||
CollaborativeDocActivityPresenter: '' as AnyComponent,
|
||||
MarkupEditor: '' as AnyComponent,
|
||||
MarkupEditorPopup: '' as AnyComponent,
|
||||
ListView: '' as AnyComponent,
|
||||
|
@ -13,12 +13,30 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Markup } from '@hcengineering/core'
|
||||
import { Extensions, getSchema } from '@tiptap/core'
|
||||
import { Node, Schema } from 'prosemirror-model'
|
||||
import { prosemirrorJSONToYDoc, yDocToProsemirrorJSON } from 'y-prosemirror'
|
||||
import { Doc, applyUpdate, encodeStateAsUpdate } from 'yjs'
|
||||
import { prosemirrorJSONToYDoc, prosemirrorToYDoc, yDocToProsemirrorJSON } from 'y-prosemirror'
|
||||
import { Doc as YDoc, applyUpdate, encodeStateAsUpdate } from 'yjs'
|
||||
import { defaultExtensions } from './extensions'
|
||||
import { MarkupNode } from './markup/model'
|
||||
import { jsonToMarkup, markupToPmNode } from './markup/utils'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function markupToYDoc (markup: Markup, field: string): YDoc {
|
||||
const node = markupToPmNode(markup)
|
||||
return prosemirrorToYDoc(node, field)
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function yDocToMarkup (ydoc: YDoc, field: string): Markup {
|
||||
const json = yDocToProsemirrorJSON(ydoc, field)
|
||||
return jsonToMarkup(json as MarkupNode)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ProseMirror node from Y.Doc content
|
||||
@ -31,7 +49,7 @@ export function yDocContentToNode (
|
||||
schema?: Schema,
|
||||
extensions?: Extensions
|
||||
): Node {
|
||||
const ydoc = new Doc()
|
||||
const ydoc = new YDoc()
|
||||
const uint8arr = new Uint8Array(content)
|
||||
applyUpdate(ydoc, uint8arr)
|
||||
|
||||
@ -43,7 +61,7 @@ export function yDocContentToNode (
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function yDocToNode (ydoc: Doc, field?: string, schema?: Schema, extensions?: Extensions): Node {
|
||||
export function yDocToNode (ydoc: YDoc, field?: string, schema?: Schema, extensions?: Extensions): Node {
|
||||
schema ??= getSchema(extensions ?? defaultExtensions)
|
||||
|
||||
try {
|
||||
@ -66,7 +84,7 @@ export function yDocContentToNodes (content: ArrayBuffer, schema?: Schema, exten
|
||||
const nodes: Node[] = []
|
||||
|
||||
try {
|
||||
const ydoc = new Doc()
|
||||
const ydoc = new YDoc()
|
||||
const uint8arr = new Uint8Array(content)
|
||||
applyUpdate(ydoc, uint8arr)
|
||||
|
||||
@ -93,12 +111,12 @@ export function updateYDocContent (
|
||||
updateFn: (body: Record<string, any>) => Record<string, any>,
|
||||
schema?: Schema,
|
||||
extensions?: Extensions
|
||||
): Doc | undefined {
|
||||
): YDoc | undefined {
|
||||
schema ??= getSchema(extensions ?? defaultExtensions)
|
||||
|
||||
try {
|
||||
const ydoc = new Doc()
|
||||
const res = new Doc({ gc: false })
|
||||
const ydoc = new YDoc()
|
||||
const res = new YDoc({ gc: false })
|
||||
const uint8arr = new Uint8Array(content)
|
||||
applyUpdate(ydoc, uint8arr)
|
||||
|
||||
@ -121,10 +139,10 @@ export function updateYDocContent (
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function YDocFromContent (content: MarkupNode, field: string, schema?: Schema, extensions?: Extensions): Doc {
|
||||
export function YDocFromContent (content: MarkupNode, field: string, schema?: Schema, extensions?: Extensions): YDoc {
|
||||
schema ??= getSchema(extensions ?? defaultExtensions)
|
||||
|
||||
const res = new Doc({ gc: false })
|
||||
const res = new YDoc({ gc: false })
|
||||
|
||||
const yDoc = prosemirrorJSONToYDoc(schema, content, field)
|
||||
const update = encodeStateAsUpdate(yDoc)
|
||||
|
@ -1,21 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2024 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { CollaborativeDoc } from '@hcengineering/core'
|
||||
|
||||
export let value: CollaborativeDoc
|
||||
</script>
|
||||
|
||||
<span>{value}</span>
|
@ -28,7 +28,6 @@ import ClassPresenter from './components/ClassPresenter.svelte'
|
||||
import ClassRefPresenter from './components/ClassRefPresenter.svelte'
|
||||
import CollaborativeDocEditor from './components/CollaborativeDocEditor.svelte'
|
||||
import CollaborativeHTMLEditor from './components/CollaborativeHTMLEditor.svelte'
|
||||
import CollaborativeDocActivityPresenter from './components/CollaborativeDocActivityPresenter.svelte'
|
||||
import ColorsPopup from './components/ColorsPopup.svelte'
|
||||
import DateEditor from './components/DateEditor.svelte'
|
||||
import DatePresenter from './components/DatePresenter.svelte'
|
||||
@ -277,7 +276,6 @@ export default async (): Promise<Resources> => ({
|
||||
HTMLEditor,
|
||||
CollaborativeDocEditor,
|
||||
CollaborativeHTMLEditor,
|
||||
CollaborativeDocActivityPresenter,
|
||||
ListView,
|
||||
GrowPresenter,
|
||||
DividerPresenter,
|
||||
|
@ -197,7 +197,7 @@ function getDocUpdateMessageTx (
|
||||
}
|
||||
|
||||
export async function pushDocUpdateMessages (
|
||||
ctx: MeasureContext | undefined,
|
||||
ctx: MeasureContext,
|
||||
control: ActivityControl,
|
||||
res: TxCollectionCUD<Doc, DocUpdateMessage>[],
|
||||
object: Doc | undefined,
|
||||
@ -231,7 +231,7 @@ export async function pushDocUpdateMessages (
|
||||
: undefined
|
||||
}
|
||||
|
||||
const attributesUpdates = await getTxAttributesUpdates(control, originTx, tx, object, objectCache, controlRules)
|
||||
const attributesUpdates = await getTxAttributesUpdates(ctx, control, originTx, tx, object, objectCache, controlRules)
|
||||
|
||||
for (const attributeUpdates of attributesUpdates) {
|
||||
res.push(
|
||||
|
@ -3,9 +3,13 @@ import {
|
||||
AttachedDoc,
|
||||
type Attribute,
|
||||
Class,
|
||||
CollaborativeDoc,
|
||||
collaborativeDocFromLastVersion,
|
||||
Collection,
|
||||
Doc,
|
||||
Hierarchy,
|
||||
Markup,
|
||||
MeasureContext,
|
||||
Mixin,
|
||||
Ref,
|
||||
RefTo,
|
||||
@ -14,15 +18,18 @@ import {
|
||||
TxCUD,
|
||||
TxMixin,
|
||||
TxProcessor,
|
||||
TxUpdateDoc
|
||||
TxUpdateDoc,
|
||||
WorkspaceId
|
||||
} from '@hcengineering/core'
|
||||
import core from '@hcengineering/core/src/component'
|
||||
import { ActivityMessageControl, DocAttributeUpdates, DocUpdateAction } from '@hcengineering/activity'
|
||||
import { ActivityControl, DocObjectCache, getAllObjectTransactions } from '@hcengineering/server-activity'
|
||||
import { getDocCollaborators } from '@hcengineering/server-notification-resources'
|
||||
import notification from '@hcengineering/notification'
|
||||
import { TriggerControl } from '@hcengineering/server-core'
|
||||
import { StorageAdapter, TriggerControl } from '@hcengineering/server-core'
|
||||
import { translate } from '@hcengineering/platform'
|
||||
import { loadCollaborativeDoc } from '@hcengineering/collaboration'
|
||||
import { EmptyMarkup, yDocToMarkup } from '@hcengineering/text'
|
||||
|
||||
function getAvailableAttributesKeys (tx: TxCUD<Doc>, hierarchy: Hierarchy): string[] {
|
||||
if (hierarchy.isDerived(tx._class, core.class.TxUpdateDoc)) {
|
||||
@ -226,6 +233,7 @@ export async function getAttributeDiff (
|
||||
}
|
||||
|
||||
export async function getTxAttributesUpdates (
|
||||
ctx: MeasureContext,
|
||||
control: ActivityControl,
|
||||
originTx: TxCUD<Doc>,
|
||||
tx: TxCUD<Doc>,
|
||||
@ -296,6 +304,7 @@ export async function getTxAttributesUpdates (
|
||||
if (
|
||||
hierarchy.isDerived(attrClass, core.class.TypeMarkup) ||
|
||||
hierarchy.isDerived(attrClass, core.class.TypeCollaborativeMarkup) ||
|
||||
hierarchy.isDerived(attrClass, core.class.TypeCollaborativeDoc) ||
|
||||
mixin === notification.mixin.Collaborators
|
||||
) {
|
||||
if (docDiff === undefined) {
|
||||
@ -323,6 +332,16 @@ export async function getTxAttributesUpdates (
|
||||
}
|
||||
}
|
||||
|
||||
// we don't want to show collaborative documents in activity
|
||||
// instead we show their content as Markup
|
||||
// TODO this should be generalized via activity extension
|
||||
const attrType = mixin !== undefined ? hierarchy.findAttribute(mixin, key) : clazz
|
||||
if (attrType?.type?._class === core.class.TypeCollaborativeDoc) {
|
||||
attrClass = isMixin ? attrClass : core.class.TypeMarkup
|
||||
attrValue = await getMarkup(ctx, control.storageAdapter, control.workspace, attrValue, key)
|
||||
prevValue = await getMarkup(ctx, control.storageAdapter, control.workspace, prevValue, key)
|
||||
}
|
||||
|
||||
let setAttr = []
|
||||
|
||||
if (Array.isArray(attrValue)) {
|
||||
@ -406,3 +425,16 @@ export function getCollectionAttribute (
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
async function getMarkup (
|
||||
ctx: MeasureContext,
|
||||
storage: StorageAdapter,
|
||||
workspace: WorkspaceId,
|
||||
value: CollaborativeDoc,
|
||||
field: string
|
||||
): Promise<Markup> {
|
||||
if (value === undefined) return EmptyMarkup
|
||||
value = collaborativeDocFromLastVersion(value)
|
||||
const ydoc = await loadCollaborativeDoc(storage, workspace, value, ctx)
|
||||
return ydoc !== undefined ? yDocToMarkup(ydoc, field) : EmptyMarkup
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Doc, Hierarchy, ModelDb, Ref, Storage, TxCUD, TxFactory } from '@hcengineering/core'
|
||||
import { Doc, Hierarchy, ModelDb, Ref, Storage, TxCUD, TxFactory, WorkspaceId } from '@hcengineering/core'
|
||||
import { StorageAdapter } from '@hcengineering/server-core'
|
||||
|
||||
export interface DocObjectCache {
|
||||
docs: Map<Ref<Doc>, Doc | null>
|
||||
@ -10,4 +11,6 @@ export interface ActivityControl {
|
||||
hierarchy: Hierarchy
|
||||
txFactory: TxFactory
|
||||
modelDb: ModelDb
|
||||
storageAdapter: StorageAdapter
|
||||
workspace: WorkspaceId
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ async function getRequestNotificationTx (tx: TxCollectionCUD<Doc, Request>, cont
|
||||
if (doc === undefined) return []
|
||||
|
||||
const res: Tx[] = []
|
||||
const messagesTxes = await pushDocUpdateMessages(undefined, control, [], doc, tx)
|
||||
const messagesTxes = await pushDocUpdateMessages(control.ctx, control, [], doc, tx)
|
||||
|
||||
if (messagesTxes.length === 0) return []
|
||||
|
||||
|
@ -84,7 +84,6 @@ export async function loadCollaborativeDoc (
|
||||
for (const source of sources) {
|
||||
const { documentId, versionId } = collaborativeDocParse(source)
|
||||
|
||||
ctx.info('loading collaborative document', { source })
|
||||
const ydoc = await loadCollaborativeDocVersion(ctx, storageAdapter, workspace, documentId, versionId)
|
||||
|
||||
if (ydoc !== undefined) {
|
||||
|
Loading…
Reference in New Issue
Block a user