Задать вопрос
vhuk1802
@vhuk1802

Почему при переподключению к вебсокету дублируються data в редактор коллаборатору?

Здравствуйте!
При переподключению к вебсокету дублируються данные в редактор.
Видео скрин
Код сервера:
import cors from '@fastify/cors'
import websocketPlugin from '@fastify/websocket'
import fastify from 'fastify'
import type { RawData } from 'ws'
import { checkAccountFromGraphQL, getProjectDocumentPageFromGraphQL, saveProjectDocumentPageToGraphQL } from './graphql.js'; 
import { Hocuspocus } from "@hocuspocus/server";
import { yTextToSlateElement, slateNodesToInsertDelta } from '@slate-yjs/core';
import * as Y from 'yjs';
import { Redis } from '@hocuspocus/extension-redis';
const HOST = process.env.HOST || '0.0.0.0';
const PORT = parseInt(process.env.SERVER_PORT || '3000', 10);

const app = fastify()

const hocuspocus = new Hocuspocus({

	extensions: [
		new Redis({
			host: "redis",
			port: 6379,

		})
	],
	async onAuthenticate({ token, requestHeaders, requestParameters, documentName }) {
		try {
			const endpoint = requestHeaders.origin + '/graphql';
			const projectId = requestParameters.get('projectId') as string;
			const documentId = requestParameters.get('documentId') as string;

			if (!projectId) {
				throw new Error("Project ID is required!");
			}

			const hasAccess = await checkAccountFromGraphQL(endpoint, token, projectId);

			if (!hasAccess) {
				throw new Error("Not authorized!");
			}

			return { token, projectId, documentId, endpoint }

		} catch (error) {
			console.error('Authentication error:', error);
			throw error;
		}
	},

	async onLoadDocument({ documentName, context, document }) {

		const { endpoint, token, projectId, documentId } = context;
	 	const sharedRoot = document.get('content', Y.XmlText) as Y.XmlText;

	 	if (sharedRoot.length > 0)
			return document

		const data = await getProjectDocumentPageFromGraphQL(endpoint, token, documentName, projectId, documentId);
	 	const insertDelta = slateNodesToInsertDelta(data);
	 	sharedRoot.applyDelta(insertDelta);
		return document;


	},
	onStoreDocument: async ({ document, documentName, context }) => {
		const sharedContent = document.get('content', Y.XmlText) as Y.XmlText;
		const data = yTextToSlateElement(sharedContent);
		const { token, projectId, documentId, endpoint } = context;
		saveProjectDocumentPageToGraphQL(endpoint, token, documentName, projectId, documentId, data.children)
	},
});

app.register(websocketPlugin)
app.register(cors, { origin: '*' })

app.register(async (app) => {
	app.get("/ws/collaboration", { websocket: true }, async (socket, req) => {
		     hocuspocus.handleConnection(socket, req.raw, {});
	});
	app.addContentTypeParser('*', (_, __, done) => done(null))
})

app.listen({ port: PORT, host: HOST }, (err, address) => {
	if (err) {
		console.error(err)
		process.exit(1)
	}
console.log(`Server started on ${HOST}:${PORT}`)
})

Код клиента:
export default ({ className, title, changeTitle, icon, pageId, changeIcon, readOnly, children, defaultValue, changeValue }: any) => {

    const { user } = useApp();;
    const { projectId, documentId } = useParams();
    const [isSyncing, setIsSyncing] = useState(false);
    const yjsPluginOptions: any = useMemo(() => ({
        render: {
            afterEditable: RemoteCursorOverlay,
        },
        options: {
            onSyncChange: ({ type, isSynced }) => setIsSyncing(isSynced),
            cursors: {
                data: {
                    name: user.fullName,
                    color: userAvatarColor(user.fullName),
                    previewUrl: user.previewUrl,
                    id: user.id
                },
            },
            providers: [
                {
                    type: 'hocuspocus',
                    options: {
                        name: pageId,
                        url: `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.hostname}:${_sharedData.websocketPort}/ws/collaboration?projectId=${projectId}&documentId=${documentId}`,
                        token: localStorage.getItem(LocalStorage.token),
                    },
                },
            ],
        }
    }), [user.fullName, pageId, projectId, documentId]);

    const plugins = useMemo(() => [YjsPlugin.configure(yjsPluginOptions)], [yjsPluginOptions]);
    const editor: any = useCreateEditor([], plugins);
    const theme: any = useTheme();
    const classes = useStyles();
    const titleRef = useRef<any>(null);
    const [mounted, setMounted] = useState(false);

    useEffect(() => {
        setMounted(true);
    }, []);

    useEffect(() => {

        if (readOnly && editor && defaultValue && !isEqual(editor?.children, defaultValue)) {
            editor.children = defaultValue;
            editor.onChange();
        }

    }, [defaultValue]);

    useEffect(() => {
        if (!mounted) return;

        const api = editor.getApi(YjsPlugin);
        if (!api) return;


        api.yjs.init({
            id: pageId,
            // value: defaultValue,
        }).then(() => {

            if (
                editor.children.length === 1 &&
                editor.children[0].type === 'p' &&
                editor.children[0].children?.[0]?.text === ''
            ) {
                editor.children = [];
                editor.onChange();
            }
        });

        return () => {
            editor.getApi(YjsPlugin).yjs.destroy();
        };
    }, [editor, mounted, pageId]);



    return (
        <Box className={cn(classes.root, className ?? '')}>
            <div data-registry="plate" className={`${theme.palette.mode} flex flex-col flex-1`} >
                    <Plate
                        readOnly={readOnly}
                        editor={editor}
                    >
                        <EditorWrapper
                            title={title}
                            changeTitle={changeTitle}
                            icon={icon}
                            changeIcon={changeIcon}
                            editor={editor}
                            titleRef={titleRef}
                            readOnly={readOnly}
                        >
                            {children}
                        </EditorWrapper>
                    </Plate>
            </div>
        </Box>
    );
}
  • Вопрос задан
  • 12 просмотров
Подписаться 1 Средний 1 комментарий
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы