|
@@ -1,25 +1,12 @@
|
|
|
-import { useEffect, useState } from 'react';
|
|
|
|
|
|
|
+import { useEffect } from 'react';
|
|
|
|
|
|
|
|
import type { Extension } from '@codemirror/state';
|
|
import type { Extension } from '@codemirror/state';
|
|
|
import { keymap, scrollPastEnd } from '@codemirror/view';
|
|
import { keymap, scrollPastEnd } from '@codemirror/view';
|
|
|
-import type { Nullable } from '@growi/core';
|
|
|
|
|
-// TODO: import socket.io-client types wihtout lint error
|
|
|
|
|
-// import type { Socket, DefaultEventsMap } from 'socket.io-client';
|
|
|
|
|
-// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
|
-// @ts-ignore
|
|
|
|
|
-import { yCollab } from 'y-codemirror.next';
|
|
|
|
|
-import { SocketIOProvider } from 'y-socket.io';
|
|
|
|
|
-import * as Y from 'yjs';
|
|
|
|
|
-
|
|
|
|
|
-import { GlobalCodeMirrorEditorKey, AcceptedUploadFileType, userColor } from '../consts';
|
|
|
|
|
-import { useCodeMirrorEditorIsolated } from '../stores';
|
|
|
|
|
|
|
|
|
|
-import { CodeMirrorEditor } from '.';
|
|
|
|
|
|
|
+import { GlobalCodeMirrorEditorKey, AcceptedUploadFileType } from '../consts';
|
|
|
|
|
+import { useCodeMirrorEditorIsolated, useCollaborativeEditorMode } from '../stores';
|
|
|
|
|
|
|
|
-// TODO: use SocketEventName
|
|
|
|
|
-// import { SocketEventName } from '~/interfaces/websocket';
|
|
|
|
|
-// TODO: import { GLOBAL_SOCKET_NS } from '~/stores/websocket';
|
|
|
|
|
-const GLOBAL_SOCKET_NS = '/';
|
|
|
|
|
|
|
+import { CodeMirrorEditor } from '.';
|
|
|
|
|
|
|
|
const additionalExtensions: Extension[] = [
|
|
const additionalExtensions: Extension[] = [
|
|
|
scrollPastEnd(),
|
|
scrollPastEnd(),
|
|
@@ -31,121 +18,22 @@ type Props = {
|
|
|
onUpload?: (files: File[]) => void,
|
|
onUpload?: (files: File[]) => void,
|
|
|
acceptedFileType?: AcceptedUploadFileType,
|
|
acceptedFileType?: AcceptedUploadFileType,
|
|
|
indentSize?: number,
|
|
indentSize?: number,
|
|
|
- pageId: Nullable<string>,
|
|
|
|
|
userName?: string,
|
|
userName?: string,
|
|
|
- socket?: any, // Socket<DefaultEventsMap, DefaultEventsMap>,
|
|
|
|
|
- initialValue: string,
|
|
|
|
|
- setMarkdownToPreview: React.Dispatch<React.SetStateAction<string>>,
|
|
|
|
|
|
|
+ pageId?: string,
|
|
|
|
|
+ initialValue?: string,
|
|
|
|
|
+ onOpenEditor?: (markdown: string) => void,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
export const CodeMirrorEditorMain = (props: Props): JSX.Element => {
|
|
export const CodeMirrorEditorMain = (props: Props): JSX.Element => {
|
|
|
const {
|
|
const {
|
|
|
- onSave, onChange, onUpload, acceptedFileType, indentSize, pageId, userName, initialValue, socket, setMarkdownToPreview,
|
|
|
|
|
|
|
+ onSave, onChange, onUpload, acceptedFileType, indentSize, userName, pageId, initialValue, onOpenEditor,
|
|
|
} = props;
|
|
} = props;
|
|
|
|
|
|
|
|
const { data: codeMirrorEditor } = useCodeMirrorEditorIsolated(GlobalCodeMirrorEditorKey.MAIN);
|
|
const { data: codeMirrorEditor } = useCodeMirrorEditorIsolated(GlobalCodeMirrorEditorKey.MAIN);
|
|
|
- const [ydoc, setYdoc] = useState<Y.Doc | null>(null);
|
|
|
|
|
- const [provider, setProvider] = useState<SocketIOProvider | null>(null);
|
|
|
|
|
- const [cPageId, setCPageId] = useState(pageId);
|
|
|
|
|
-
|
|
|
|
|
- const acceptedFileTypeNoOpt = acceptedFileType ?? AcceptedUploadFileType.NONE;
|
|
|
|
|
-
|
|
|
|
|
- // cleanup ydoc and socketIOProvider
|
|
|
|
|
- useEffect(() => {
|
|
|
|
|
- if (cPageId === pageId) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (!provider || !ydoc || socket == null) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- ydoc.destroy();
|
|
|
|
|
- setYdoc(null);
|
|
|
|
|
-
|
|
|
|
|
- provider.destroy();
|
|
|
|
|
- provider.disconnect();
|
|
|
|
|
- setProvider(null);
|
|
|
|
|
-
|
|
|
|
|
- // TODO: catch ydoc:sync:error
|
|
|
|
|
- // TODO: use SocketEventName
|
|
|
|
|
- socket.off('ydoc:sync');
|
|
|
|
|
-
|
|
|
|
|
- setCPageId(pageId);
|
|
|
|
|
- }, [cPageId, pageId, provider, socket, ydoc]);
|
|
|
|
|
-
|
|
|
|
|
- // setup ydoc
|
|
|
|
|
- useEffect(() => {
|
|
|
|
|
- if (ydoc != null) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- const _ydoc = new Y.Doc();
|
|
|
|
|
- setYdoc(_ydoc);
|
|
|
|
|
- }, [initialValue, ydoc]);
|
|
|
|
|
|
|
+ useCollaborativeEditorMode(userName, pageId, initialValue, onOpenEditor, codeMirrorEditor);
|
|
|
|
|
|
|
|
- // setup socketIOProvider
|
|
|
|
|
- useEffect(() => {
|
|
|
|
|
- if (ydoc == null || provider != null || socket == null) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const socketIOProvider = new SocketIOProvider(
|
|
|
|
|
- GLOBAL_SOCKET_NS,
|
|
|
|
|
- `yjs/${pageId}`,
|
|
|
|
|
- ydoc,
|
|
|
|
|
- { autoConnect: true },
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- socketIOProvider.awareness.setLocalStateField('user', {
|
|
|
|
|
- name: userName ? `${userName}` : `Guest User ${Math.floor(Math.random() * 100)}`,
|
|
|
|
|
- color: userColor.color,
|
|
|
|
|
- colorLight: userColor.light,
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- socketIOProvider.on('sync', (isSync: boolean) => {
|
|
|
|
|
- if (isSync) {
|
|
|
|
|
- // TODO: use SocketEventName
|
|
|
|
|
- socket.emit('ydoc:sync', { pageId, initialValue });
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- // TODO: delete this code
|
|
|
|
|
- socketIOProvider.on('status', ({ status: _status }: { status: string }) => {
|
|
|
|
|
- if (_status) console.log(_status);
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- setProvider(socketIOProvider);
|
|
|
|
|
- }, [initialValue, pageId, provider, socket, userName, ydoc]);
|
|
|
|
|
-
|
|
|
|
|
- // attach YDoc to CodeMirror
|
|
|
|
|
- useEffect(() => {
|
|
|
|
|
- if (ydoc == null || provider == null) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const ytext = ydoc.getText('codemirror');
|
|
|
|
|
- const undoManager = new Y.UndoManager(ytext);
|
|
|
|
|
-
|
|
|
|
|
- const cleanup = codeMirrorEditor?.appendExtensions?.([
|
|
|
|
|
- yCollab(ytext, provider.awareness, { undoManager }),
|
|
|
|
|
- ]);
|
|
|
|
|
-
|
|
|
|
|
- return cleanup;
|
|
|
|
|
- }, [codeMirrorEditor, provider, setMarkdownToPreview, ydoc]);
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- // initialize markdown and preview
|
|
|
|
|
- useEffect(() => {
|
|
|
|
|
- if (ydoc == null) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const ytext = ydoc.getText('codemirror');
|
|
|
|
|
- codeMirrorEditor?.initDoc(ytext.toString());
|
|
|
|
|
- setMarkdownToPreview(ytext.toString());
|
|
|
|
|
- // TODO: Check the reproduction conditions that made this code necessary and confirm reproduction
|
|
|
|
|
- // mutateIsEnabledUnsavedWarning(false);
|
|
|
|
|
- }, [codeMirrorEditor, initialValue, pageId, setMarkdownToPreview, socket, ydoc]);
|
|
|
|
|
|
|
+ const acceptedFileTypeNoOpt = acceptedFileType ?? AcceptedUploadFileType.NONE;
|
|
|
|
|
|
|
|
// setup additional extensions
|
|
// setup additional extensions
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|