From ca0dd235b6d36e5a84ffd333f4ed486f2a368ae1 Mon Sep 17 00:00:00 2001 From: Alexander Onnikov Date: Sat, 7 Oct 2023 12:17:06 +0700 Subject: [PATCH] UBER-853 Move snapshot creation to collaboration provider (#3807) Signed-off-by: Alexander Onnikov --- .../src/components/Collaboration.svelte | 30 ++++++++------- .../src/components/CollaboratorEditor.svelte | 13 ++++--- packages/text-editor/src/provider.ts | 38 +++++++++++++++++++ packages/text/src/node.ts | 28 ++++++++++++++ 4 files changed, 91 insertions(+), 18 deletions(-) create mode 100644 packages/text-editor/src/provider.ts diff --git a/packages/text-editor/src/components/Collaboration.svelte b/packages/text-editor/src/components/Collaboration.svelte index 3e0a42bdb3..4923fc888c 100644 --- a/packages/text-editor/src/components/Collaboration.svelte +++ b/packages/text-editor/src/components/Collaboration.svelte @@ -1,6 +1,6 @@ diff --git a/packages/text-editor/src/components/CollaboratorEditor.svelte b/packages/text-editor/src/components/CollaboratorEditor.svelte index 15dcaf72a1..4b75f4997e 100644 --- a/packages/text-editor/src/components/CollaboratorEditor.svelte +++ b/packages/text-editor/src/components/CollaboratorEditor.svelte @@ -19,7 +19,6 @@ import { DecorationSet } from 'prosemirror-view' import { getContext, createEventDispatcher, onDestroy, onMount } from 'svelte' import * as Y from 'yjs' - import { HocuspocusProvider } from '@hocuspocus/provider' import { AnyExtension, Editor, Extension, HTMLContent, getMarkRange, mergeAttributes } from '@tiptap/core' import Collaboration, { isChangeOrigin } from '@tiptap/extension-collaboration' import CollaborationCursor from '@tiptap/extension-collaboration-cursor' @@ -30,15 +29,16 @@ import { Completion } from '../Completion' import textEditorPlugin from '../plugin' + import { TiptapCollabProvider } from '../provider' import { CollaborationIds, TextFormatCategory, TextNodeAction } from '../types' import { calculateDecorations } from './diff/decorations' + import { defaultEditorAttributes } from './editor/editorProps' import { completionConfig, defaultExtensions } from './extensions' import { InlineStyleToolbar } from './extension/inlineStyleToolbar' import { NodeUuidExtension } from './extension/nodeUuid' import StyleButton from './StyleButton.svelte' import TextEditorStyleToolbar from './TextEditorStyleToolbar.svelte' - import { defaultEditorAttributes } from './editor/editorProps' export let documentId: string export let readonly = false @@ -66,11 +66,11 @@ const ydoc = (getContext(CollaborationIds.Doc) as Y.Doc | undefined) ?? new Y.Doc() - const contextProvider = getContext(CollaborationIds.Provider) as HocuspocusProvider | undefined + const contextProvider = getContext(CollaborationIds.Provider) as TiptapCollabProvider | undefined const provider = contextProvider ?? - new HocuspocusProvider({ + new TiptapCollabProvider({ url: collaboratorURL, name: documentId, document: ydoc, @@ -142,6 +142,10 @@ }) } + export function takeSnapshot (snapshotId: string) { + provider.copyContent(documentId, snapshotId) + } + let needFocus = false let focused = false @@ -274,7 +278,6 @@ editor.destroy() } catch (err: any) {} if (contextProvider === undefined) { - provider.configuration.websocketProvider.disconnect() provider.destroy() } } diff --git a/packages/text-editor/src/provider.ts b/packages/text-editor/src/provider.ts new file mode 100644 index 0000000000..b80a893c57 --- /dev/null +++ b/packages/text-editor/src/provider.ts @@ -0,0 +1,38 @@ +// +// 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 { HocuspocusProvider, HocuspocusProviderConfiguration } from '@hocuspocus/provider' + +export type TiptapCollabProviderConfiguration = HocuspocusProviderConfiguration & +Required> + +export class TiptapCollabProvider extends HocuspocusProvider { + constructor (configuration: TiptapCollabProviderConfiguration) { + super(configuration as HocuspocusProviderConfiguration) + } + + copyContent (sourceId: string, targetId: string): void { + const payload = { + action: 'document.copy', + params: { sourceId, targetId } + } + this.sendStateless(JSON.stringify(payload)) + } + + destroy (): void { + this.configuration.websocketProvider.disconnect() + super.destroy() + } +} diff --git a/packages/text/src/node.ts b/packages/text/src/node.ts index 9cf099b407..75fa1c47d5 100644 --- a/packages/text/src/node.ts +++ b/packages/text/src/node.ts @@ -38,3 +38,31 @@ export function yDocContentToNode (extensions: Extensions, content: ArrayBuffer, return schema.node(schema.topNodeType) } } + +/** + * Get ProseMirror nodes from Y.Doc content + * + * @public + */ +export function yDocContentToNodes (extensions: Extensions, content: ArrayBuffer): Node[] { + const schema = getSchema(extensions) + + const nodes: Node[] = [] + + try { + const ydoc = new Doc() + const uint8arr = new Uint8Array(content) + applyUpdate(ydoc, uint8arr) + + for (const field of ydoc.share.keys()) { + try { + const body = yDocToProsemirrorJSON(ydoc, field) + nodes.push(schema.nodeFromJSON(body)) + } catch {} + } + } catch (err: any) { + console.error(err) + } + + return nodes +}