Skip to content

Commit

Permalink
Merge branch 'main' of github.com:metleeha/codepair into update/depen…
Browse files Browse the repository at this point in the history
…dencies
  • Loading branch information
metleeha committed Aug 8, 2022
2 parents 026331c + 43d973a commit 8d19005
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 115 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ jobs:

- name: Install and Build 🔧
run: |
npm ci
npm ci --no-optional
npm run build
13 changes: 7 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"redux-thunk": "^2.3.0",
"sass": "^1.54.3",
"typescript": "^3.7.5",
"yorkie-js-sdk": "^0.2.12"
"yorkie-js-sdk": "^0.2.14"
},
"scripts": {
"start": "REACT_APP_GIT_HASH=`git rev-parse --short HEAD` react-scripts start",
Expand Down Expand Up @@ -100,7 +100,7 @@
"last 1 safari version"
]
},
"devDependencies": {
"optionalDependencies": {
"fsevents": "^2.3.2"
}
}
214 changes: 108 additions & 106 deletions src/components/Editor/CodeEditor/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useMemo, useRef, useCallback } from 'react';
import React, { useEffect, useMemo, useRef, useCallback, useState } from 'react';
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles';
import { useSelector } from 'react-redux';
import { ActorID, DocEvent, TextChange } from 'yorkie-js-sdk';
Expand Down Expand Up @@ -82,6 +82,7 @@ export default function CodeEditor({ forwardedRef }: CodeEditorProps) {
const client = useSelector((state: AppState) => state.docState.client);
const peers = useSelector((state: AppState) => state.peerState.peers);
const cursorMapRef = useRef<Map<ActorID, Cursor>>(new Map());
const [editor, setEditor] = useState<CodeMirror.Editor | null>(null);

const connectCursor = useCallback((clientID: ActorID, metadata: Metadata) => {
cursorMapRef.current.set(clientID, new Cursor(clientID, metadata));
Expand All @@ -94,128 +95,129 @@ export default function CodeEditor({ forwardedRef }: CodeEditorProps) {
}
}, []);

const getCmInstanceCallback = useCallback(
(editor: CodeMirror.Editor) => {
if (!client || !doc) {
return;
}

// eslint-disable-next-line no-param-reassign
forwardedRef.current = editor;

const updateCursor = (clientID: ActorID, pos: CodeMirror.Position) => {
const cursor = cursorMapRef.current.get(clientID);
cursor?.updateCursor(editor, pos);
};

const updateLine = (clientID: ActorID, fromPos: CodeMirror.Position, toPos: CodeMirror.Position) => {
const cursor = cursorMapRef.current.get(clientID);
cursor?.updateLine(editor, fromPos, toPos);
};
const getCmInstanceCallback = useCallback((cm: CodeMirror.Editor) => {
setEditor(cm);
}, []);

let syncText = () => {};
useEffect(() => {
for (const [id, peer] of Object.entries(peers)) {
if (cursorMapRef.current.has(id) && peer.status === ConnectionStatus.Disconnected) {
disconnectCursor(id);
} else if (!cursorMapRef.current.has(id) && peer.status === ConnectionStatus.Connected) {
connectCursor(id, peer.metadata);
}
}
}, [peers]);

doc.subscribe((event: DocEvent) => {
if (event.type === 'remote-change') {
// display remote cursors
for (const { change } of event.value) {
const actor = change.getID().getActorID()!;
if (actor !== client.getID()) {
if (!cursorMapRef.current.has(actor)) {
return;
}
useEffect(() => {
if (!client || !doc || !editor) {
return;
}

const cursor = cursorMapRef.current.get(actor);
if (cursor!.isActive()) {
return;
}
// eslint-disable-next-line no-param-reassign
forwardedRef.current = editor;

const updateCursor = (clientID: ActorID, pos: CodeMirror.Position) => {
const cursor = cursorMapRef.current.get(clientID);
cursor?.updateCursor(editor, pos);
};

const updateLine = (clientID: ActorID, fromPos: CodeMirror.Position, toPos: CodeMirror.Position) => {
const cursor = cursorMapRef.current.get(clientID);
cursor?.updateLine(editor, fromPos, toPos);
};

let syncText = () => {};

doc.subscribe((event: DocEvent) => {
if (event.type === 'remote-change') {
// display remote cursors
for (const { change } of event.value) {
const actor = change.getID().getActorID()!;
if (actor !== client.getID()) {
if (!cursorMapRef.current.has(actor)) {
return;
}

updateCursor(actor, editor.posFromIndex(0));
const cursor = cursorMapRef.current.get(actor);
if (cursor!.isActive()) {
return;
}

updateCursor(actor, editor.posFromIndex(0));
}
} else if (event.type === 'snapshot') {
// re-sync for the new text from the snapshot
syncText();
}
});
} else if (event.type === 'snapshot') {
// re-sync for the new text from the snapshot
syncText();
}
});

// local to remote
editor.on('beforeChange', (instance: CodeMirror.Editor, change: CodeMirror.EditorChange) => {
if (change.origin === 'yorkie' || change.origin === 'setValue') {
return;
}
// local to remote
editor.on('beforeChange', (instance: CodeMirror.Editor, change: CodeMirror.EditorChange) => {
if (change.origin === 'yorkie' || change.origin === 'setValue') {
return;
}

const from = editor.indexFromPos(change.from);
const to = editor.indexFromPos(change.to);
const content = change.text.join('\n');
const from = editor.indexFromPos(change.from);
const to = editor.indexFromPos(change.to);
const content = change.text.join('\n');

doc.update((root) => {
root.content.edit(from, to, content);
});
doc.update((root) => {
root.content.edit(from, to, content);
});
});

editor.on('beforeSelectionChange', (instance: CodeMirror.Editor, data: CodeMirror.EditorSelectionChange) => {
if (!data.origin) {
return;
}
editor.on('beforeSelectionChange', (instance: CodeMirror.Editor, data: CodeMirror.EditorSelectionChange) => {
if (!data.origin) {
return;
}

const from = editor.indexFromPos(data.ranges[0].anchor);
const to = editor.indexFromPos(data.ranges[0].head);
const from = editor.indexFromPos(data.ranges[0].anchor);
const to = editor.indexFromPos(data.ranges[0].head);

doc.update((root) => {
root.content.select(from, to);
});
doc.update((root) => {
root.content.select(from, to);
});

// remote to local
const changeEventHandler = (changes: TextChange[]) => {
changes.forEach((change) => {
const { actor, from, to } = change;
if (change.type === 'content') {
const content = change.content || '';
if (actor !== client.getID()) {
const fromPos = editor.posFromIndex(from);
const toPos = editor.posFromIndex(to);
editor.replaceRange(content, fromPos, toPos, 'yorkie');
}
} else if (change.type === 'selection') {
if (actor !== client.getID()) {
let fromPos = editor.posFromIndex(from);
let toPos = editor.posFromIndex(to);
updateCursor(actor, toPos);
if (from > to) {
[toPos, fromPos] = [fromPos, toPos];
}
updateLine(actor, fromPos, toPos);
});

// remote to local
const changeEventHandler = (changes: TextChange[]) => {
changes.forEach((change) => {
const { actor, from, to } = change;
if (change.type === 'content') {
const content = change.content || '';
if (actor !== client.getID()) {
const fromPos = editor.posFromIndex(from);
const toPos = editor.posFromIndex(to);
editor.replaceRange(content, fromPos, toPos, 'yorkie');
}
} else if (change.type === 'selection') {
if (actor !== client.getID()) {
let fromPos = editor.posFromIndex(from);
let toPos = editor.posFromIndex(to);
updateCursor(actor, toPos);
if (from > to) {
[toPos, fromPos] = [fromPos, toPos];
}
updateLine(actor, fromPos, toPos);
}
});
};

// sync text of document and editor
syncText = () => {
const text = doc.getRoot().content;
text.onChanges(changeEventHandler);
editor.setValue(text.toString());
};
syncText();
editor.addKeyMap(menu.codeKeyMap);
editor.setOption('keyMap', menu.codeKeyMap);
editor.getDoc().clearHistory();
editor.focus();
},
[client, doc, menu],
);

useEffect(() => {
for (const [id, peer] of Object.entries(peers)) {
if (cursorMapRef.current.has(id) && peer.status === ConnectionStatus.Disconnected) {
disconnectCursor(id);
} else if (!cursorMapRef.current.has(id) && peer.status === ConnectionStatus.Connected) {
connectCursor(id, peer.metadata);
}
}
}, [peers]);
}
});
};

// sync text of document and editor
syncText = () => {
const text = doc.getRoot().content;
text.onChanges(changeEventHandler);
editor.setValue(text.toString());
};
syncText();
editor.addKeyMap(menu.codeKeyMap);
editor.setOption('keyMap', menu.codeKeyMap);
editor.getDoc().clearHistory();
editor.focus();
}, [editor]);

const options = useMemo(() => {
const opts = {
Expand Down

0 comments on commit 8d19005

Please sign in to comment.