2021-01-16 12:28:01 +03:00
|
|
|
import _ from 'lodash';
|
|
|
|
import { useEffect, useState } from 'react';
|
2021-01-17 20:22:10 +03:00
|
|
|
import { PeerIdB58, subscribeToEvent } from '@fluencelabs/fluence';
|
2021-01-16 12:28:01 +03:00
|
|
|
|
2021-01-16 13:24:26 +03:00
|
|
|
import { fluentPadServiceId, notifyTextUpdateFnName } from 'src/app/constants';
|
|
|
|
import { useFluenceClient } from '../app/FluenceClientContext';
|
|
|
|
import { getUpdatedDocFromText, initDoc, SyncClient } from '../app/sync';
|
2021-01-16 16:28:00 +03:00
|
|
|
import * as api from 'src/app/api';
|
2021-03-04 15:40:14 +03:00
|
|
|
import { withErrorHandlingAsync } from './util';
|
2021-01-14 13:17:48 +03:00
|
|
|
|
2021-01-16 12:28:01 +03:00
|
|
|
const broadcastUpdates = _.debounce((text: string, syncClient: SyncClient) => {
|
|
|
|
let doc = syncClient.getDoc();
|
|
|
|
if (doc) {
|
|
|
|
let newDoc = getUpdatedDocFromText(doc, text);
|
|
|
|
syncClient.syncDoc(newDoc);
|
2021-01-14 23:57:24 +03:00
|
|
|
}
|
2021-01-16 12:28:01 +03:00
|
|
|
}, 100);
|
2021-01-16 00:48:57 +03:00
|
|
|
|
2021-01-14 13:17:48 +03:00
|
|
|
export const CollaborativeEditor = () => {
|
|
|
|
const client = useFluenceClient()!;
|
2021-01-16 20:23:42 +03:00
|
|
|
const [text, setText] = useState<string | null>(null);
|
2021-01-16 12:28:01 +03:00
|
|
|
const [syncClient, setSyncClient] = useState(new SyncClient());
|
2021-01-14 13:17:48 +03:00
|
|
|
|
|
|
|
useEffect(() => {
|
2021-01-16 12:28:01 +03:00
|
|
|
syncClient.handleDocUpdate = (doc) => {
|
|
|
|
setText(doc.text.toString());
|
|
|
|
};
|
|
|
|
|
|
|
|
syncClient.handleSendChanges = (changes: string) => {
|
2021-03-04 15:40:14 +03:00
|
|
|
withErrorHandlingAsync(async () => {
|
|
|
|
await api.addEntry(client, changes);
|
|
|
|
});
|
2021-01-16 12:28:01 +03:00
|
|
|
};
|
2021-01-16 01:38:09 +03:00
|
|
|
|
2021-01-16 12:28:01 +03:00
|
|
|
const unsub = subscribeToEvent(client, fluentPadServiceId, notifyTextUpdateFnName, (args, tetraplets) => {
|
2021-01-17 20:22:10 +03:00
|
|
|
const [authorPeerId, changes, isAuthorized] = args as [PeerIdB58, string, boolean];
|
|
|
|
if (authorPeerId === client.selfPeerId) {
|
2021-01-16 00:48:57 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-01-16 12:28:01 +03:00
|
|
|
if (changes) {
|
|
|
|
syncClient.receiveChanges(changes);
|
2021-01-14 23:57:24 +03:00
|
|
|
}
|
2021-01-14 13:17:48 +03:00
|
|
|
});
|
|
|
|
|
2021-01-16 12:28:01 +03:00
|
|
|
syncClient.start();
|
|
|
|
|
2021-01-14 13:17:48 +03:00
|
|
|
// don't block
|
2021-03-04 15:40:14 +03:00
|
|
|
withErrorHandlingAsync(async () => {
|
|
|
|
const res = await api.getHistory(client);
|
2021-01-16 01:38:09 +03:00
|
|
|
for (let e of res) {
|
2021-01-16 12:28:01 +03:00
|
|
|
syncClient.receiveChanges(e.body);
|
2021-01-16 01:38:09 +03:00
|
|
|
}
|
2021-01-16 20:23:42 +03:00
|
|
|
|
|
|
|
if (syncClient.getDoc() === undefined) {
|
|
|
|
syncClient.syncDoc(initDoc());
|
|
|
|
}
|
2021-01-14 13:17:48 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
return () => {
|
2021-01-16 12:28:01 +03:00
|
|
|
unsub();
|
|
|
|
syncClient.stop();
|
2021-01-14 13:17:48 +03:00
|
|
|
};
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
const handleTextUpdate = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
2021-01-16 12:28:01 +03:00
|
|
|
const newText = e.target.value;
|
|
|
|
setText(newText);
|
|
|
|
broadcastUpdates(newText, syncClient);
|
2021-01-14 13:17:48 +03:00
|
|
|
};
|
|
|
|
|
2021-01-16 20:23:42 +03:00
|
|
|
return (
|
|
|
|
<textarea
|
|
|
|
spellCheck={false}
|
|
|
|
className="code-editor"
|
|
|
|
disabled={text === null}
|
|
|
|
value={text ?? ''}
|
|
|
|
onChange={handleTextUpdate}
|
|
|
|
/>
|
|
|
|
);
|
2021-01-14 13:17:48 +03:00
|
|
|
};
|