Yjs协同编辑内容重复问题

问题描述 投票:0回答:1

描述:

我正在使用 Yjs 作为我的 React 应用程序中的脚本编辑器实现协作编辑功能。协作编辑效果很好,但当多个用户同时键入时我遇到了问题。对于每次按键,Yjs 文档中的整个键入内容都会被复制。

这是我的设置的简化概述:

套接字处理(socket.mjs):

// socket.mjs
import { createServer } from 'http';
import { parse } from 'url';
import next from 'next';
import { Server as SocketIOServer } from 'socket.io';
import * as Y from 'yjs';
import dotenv from 'dotenv';

const envFile = process.env.NODE_ENV === 'production' ? '.env.production' : '.env.development';
dotenv.config({ path: envFile });

const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();

const ydoc = new Y.Doc();
const yxmlFragment = ydoc.getXmlFragment('shared');

const ydocMap = new Map();

const activeUsers = {};

app.prepare().then(() => {
  const server = createServer((req, res) => {
    const parsedUrl = parse(req.url, true);
    handle(req, res, parsedUrl);
  });

  const io = new SocketIOServer(server, {
    cors: {
      origin: process.env.BASE_URL || "http://localhost:3000", // Use environment variable or default
      methods: ["GET", "POST"],
      allowedHeaders: ["*"],
      credentials: true
    }
  });

  io.on('connection', (socket) => {
    // When a user connects, send the current document state as a binary update
    console.log('Initiating')
    socket.emit('init', Y.encodeStateAsUpdate(ydoc));
    socket.on('updateScene', ({ sceneId, userId, update }) => {
        console.log(sceneId);
        const ydocForScene = ydocMap.get(sceneId);
        Y.applyUpdate(ydocForScene, update);

        socket.to(sceneId).emit(`update:${sceneId}`, { update: update, userId: userId });
    });
  });

  const PORT = process.env.SOCKET_PORT || 4000;
  server.listen(PORT, () => {
    console.log(`> Ready on http://localhost:${PORT}`);
  });
});

脚本编辑器组件(ScriptEditor.tsx):

// ScriptEditor.tsx
import * as yup from 'yup'
import io from 'socket.io-client';
import * as Y from 'yjs';

const ScriptEditor = (props: any) => {
  // Initialize Yjs document and WebsocketProvider
  const ydoc = new Y.Doc();
  const wsProvider = new WebsocketProvider('ws://localhost:3001', 'my-room', ydoc);
  initializeSocket(ydoc);

useEffect(() => {
    // Initialize once and store in refs
    if (!ydocRef.current) {
      ydocRef.current = new Y.Doc();
    }
    if (!yxmlFragmentRef.current) {
      yxmlFragmentRef.current = ydocRef.current.getXmlFragment('shared');
    }
    if (!socketRef.current) {
      socketRef.current = io(process.env.WEB_SOCKET_URL);
      socketRef.current.on('init', (update) => {
        if (update) {
          const updatedArray = new Uint8Array(update);
          Y.applyUpdate(ydocRef.current, updatedArray);
        }
      });
    }
     
    const ydoc = ydocRef.current;
    const yxmlFragment = yxmlFragmentRef.current;
    const socket = socketRef.current;

  socket.on(`update:${sceneId}`, (data) => {
    if (data.userId != userData.user.id) {
      try {
        // Convert the received ArrayBuffer to a Uint8Array
        const update = new Uint8Array(data.update);
        // Apply the update to the new document
        Y.applyUpdate(ydoc, update);
       } catch (e) {
          console.error('Error applying update:', e);
       }
    }
  });
}
  return (
    <div>
      {/* Your script editor UI and contenteditable area */}
      <div
            id="script-container"
            ref={editorRef}
            style={{ outline: 'none' }}
            contentEditable={!(project?.isDemoProject || !allowEdit)}
            dangerouslySetInnerHTML={{ __html: scriptText }}
            onInput={valueChange}
            onMouseUp={handleSelection}
            onKeyDown={handleKeyDown}
            onPaste={handlePaste}
          >
    </div>
  );
};

export default ScriptEditor;

问题:

每当多个用户同时在协作脚本编辑器中输入内容时,每次按键都会导致整个内容在 Yjs 文档中重复。这会导致意外行为,并使协作编辑功能变得不切实际。

问题:

  1. 当多个用户在 Yjs 协作编辑设置中同时输入时,什么可能导致此重复问题?
  2. 如何确保只有用户所做的更改才会反映在Yjs文档中,而没有不必要的重复?

此外,我想提一下,我们正在使用自定义的 contenteditable

div
进行脚本编辑,并且我们没有使用任何内置插件(例如 Quill)。任何见解、建议或代码改进将不胜感激。谢谢!

reactjs next.js socket.io collaborative-editing yjs
1个回答
0
投票

我需要查看您的输入处理代码(例如 valueChange)。但我猜你正在通过完全覆盖其状态来更新

yXmlFragment
。 Yjs 将其解释为删除所有现有文本并插入所有新文本;如果两个用户同时这样做,他们都会插入完整的新文本,并复制它。

相反,您需要找出精确的更改(例如在...插入字符“x”)并调用相应的 Yjs 方法。例如。请参阅 y-prosemirror 的

updateYText
方法,该方法采用 diff 并将精确更改发送到 XML 树中的
Y.Text
节点:https://github.com/yjs/y-prosemirror/blob/master/src/插件/sync-plugin.js

© www.soinside.com 2019 - 2024. All rights reserved.