fluent-pad/client/src/components/CollaborativeEditor.tsx

84 lines
2.6 KiB
TypeScript
Raw Normal View History

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-03-04 15:40:14 +03:00
import { withErrorHandlingAsync } from './util';
import { addEntry, getHistory } from 'src/aqua/app';
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-14 13:17:48 +03:00
export const CollaborativeEditor = () => {
const client = useFluenceClient()!;
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 () => {
const res = await addEntry(client, changes, client.selfPeerId);
if (res.ret_code !== 0) {
throw new Error(
`Failed to add message to history service, code=${res.ret_code}, message=${res.err_msg}`,
);
}
2021-03-04 15:40:14 +03:00
});
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) => {
const [changes, isAuthorized] = args as [string, boolean];
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 getHistory(client);
for (let e of res.entries) {
2021-01-16 12:28:01 +03:00
syncClient.receiveChanges(e.body);
2021-01-16 01:38:09 +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
};
return (
<textarea
spellCheck={false}
className="code-editor"
disabled={text === null}
value={text ?? ''}
onChange={handleTextUpdate}
/>
);
2021-01-14 13:17:48 +03:00
};