BindYjs.hooks.ts 1.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. import { useEffect, useMemo, useRef } from "react";
  2. import { createEditor } from "slate";
  3. import { withReact } from "slate-react";
  4. import * as Y from 'yjs';
  5. import { withYjs, YjsEditor, slateNodesToInsertDelta } from '@slate-yjs/core';
  6. import { Delta } from '@slate-yjs/core/dist/model/types';
  7. import { TextDelta } from '@/appflowy_app/interfaces/document';
  8. const initialValue = [{
  9. type: 'paragraph',
  10. children: [{ text: '' }],
  11. }];
  12. export function useBindYjs(delta: TextDelta[], update: (_delta: Delta) => void) {
  13. const yTextRef = useRef<Y.XmlText>();
  14. // Create a yjs document and get the shared type
  15. const sharedType = useMemo(() => {
  16. const ydoc = new Y.Doc()
  17. const _sharedType = ydoc.get('content', Y.XmlText) as Y.XmlText;
  18. const insertDelta = slateNodesToInsertDelta(initialValue);
  19. // Load the initial value into the yjs document
  20. _sharedType.applyDelta(insertDelta);
  21. const yText = insertDelta[0].insert as Y.XmlText;
  22. yTextRef.current = yText;
  23. return _sharedType;
  24. }, []);
  25. const editor = useMemo(() => withYjs(withReact(createEditor()), sharedType), []);
  26. useEffect(() => {
  27. YjsEditor.connect(editor);
  28. return () => {
  29. yTextRef.current = undefined;
  30. YjsEditor.disconnect(editor);
  31. }
  32. }, [editor]);
  33. useEffect(() => {
  34. const yText = yTextRef.current;
  35. if (!yText) return;
  36. const textEventHandler = (event: Y.YTextEvent) => {
  37. update(event.changes.delta as Delta);
  38. }
  39. yText.applyDelta(delta);
  40. yText.observe(textEventHandler);
  41. return () => {
  42. yText.unobserve(textEventHandler);
  43. }
  44. }, [delta])
  45. return { editor }
  46. }