Improve text synchronization performance

This commit is contained in:
Pavel Murygin 2021-01-16 00:48:57 +03:00
parent 77b70b7c49
commit 633f43ec4d
6 changed files with 68 additions and 24 deletions

View File

@ -2190,6 +2190,12 @@
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4="
},
"@types/lodash": {
"version": "4.14.167",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.167.tgz",
"integrity": "sha512-w7tQPjARrvdeBkX/Rwg95S592JwxqOjmms3zWQ0XZgSyxSLdzWaYH3vErBhdVS/lRBX7F8aBYcYJYTr5TMGOzw==",
"dev": true
},
"@types/minimatch": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",

View File

@ -14,6 +14,7 @@
"@types/react-dom": "^16.9.10",
"automerge": "^0.14.2",
"diff-match-patch": "^1.0.5",
"lodash": "^4.17.20",
"node-sass": "^4.14.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
@ -49,5 +50,7 @@
"last 1 safari version"
]
},
"devDependencies": {}
"devDependencies": {
"@types/lodash": "^4.14.167"
}
}

View File

@ -1,4 +1,4 @@
import { createClient, FluenceClient, subscribeToEvent } from '@fluencelabs/fluence';
import { createClient, FluenceClient } from '@fluencelabs/fluence';
import { dev } from '@fluencelabs/fluence-network-environment';
import React, { useEffect, useState } from 'react';
@ -16,6 +16,7 @@ const App = () => {
useEffect(() => {
const fn = async () => {
const c = await createClient(dev[0]);
setClient(c);
};
fn();
@ -66,11 +67,14 @@ const App = () => {
Leave Room
</button>
</div>
<div>
<button onClick={() => calls.clean(client!)}>Clean</button>
</div>
</div>
<div className="wrapper">
<div>{isInRoom && client && <CollaborativeEditor />}</div>
{/* <div>{isInRoom && client && <UserList selfName={nickName} />}</div> */}
<div>{isInRoom && client && <UserList selfName={nickName} />}</div>
</div>
</div>
</FluenceClientContext.Provider>

View File

@ -4,7 +4,8 @@ import { useEffect, useState } from 'react';
import { fluentPadServiceId, notifyTextUpdateFnName } from 'src/fluence/constants';
import { useFluenceClient } from './FluenceClientContext';
import * as calls from 'src/fluence/calls';
import { subscribeToEvent } from '@fluencelabs/fluence';
import { FluenceClient, subscribeToEvent } from '@fluencelabs/fluence';
import _ from 'lodash';
interface TextDoc {
value: Automerge.Text;
@ -78,13 +79,26 @@ const applyStates = (startingDoc: TextDoc | null, entries: calls.Entry[]) => {
return res;
};
const broadcastUpdates = _.debounce(async (client: FluenceClient, doc: TextDoc) => {
const entry = {
fluentPadState: Automerge.save(doc),
};
const entryStr = JSON.stringify(entry);
await calls.addEntry(client, entryStr);
}, 200);
export const CollaborativeEditor = () => {
const client = useFluenceClient()!;
const [text, setText] = useState<TextDoc | null>(null);
const [text, setText] = useState<TextDoc | null>(Automerge.from({ value: new Automerge.Text() }));
useEffect(() => {
const unsub1 = subscribeToEvent(client, fluentPadServiceId, notifyTextUpdateFnName, (args, tetraplets) => {
const [stateStr, isAuthorized] = args;
const [authorPeerId, stateStr, isAuthorized] = args;
if (authorPeerId === client.selfPeerId.toB58String()) {
return;
}
const state = parseState(stateStr);
if (state && text) {
const newDoc = Automerge.merge(text, state);
@ -116,25 +130,20 @@ export const CollaborativeEditor = () => {
setText(newDoc);
// don't block
setImmediate(async () => {
const entry = {
fluentPadState: Automerge.save(newDoc),
};
const entryStr = JSON.stringify(entry);
await calls.addEntry(client, entryStr);
});
setImmediate(broadcastUpdates, client, newDoc);
};
return (
<div>
<textarea value={textValue} disabled={!text} onChange={handleTextUpdate} />
Automerge changes:
<ul>
{amHistory.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
<div>
Automerge changes:
<ul>
{amHistory.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
</div>
</div>
);
};

View File

@ -262,7 +262,7 @@ export const addEntry = async (client: FluenceClient, entry: string) => {
(par
(seq
(call u.$.relay_id ("op" "identity") [])
(call u.$.peer_id (fluentPadServiceId notifyTextUpdate) [entry token.$.["is_authenticated"]])
(call u.$.peer_id (fluentPadServiceId notifyTextUpdate) [myPeerId entry token.$.["is_authenticated"]])
)
(next u)
)
@ -284,7 +284,29 @@ export const addEntry = async (client: FluenceClient, entry: string) => {
fluentPadServiceId: fluentPadServiceId,
notifyTextUpdate: notifyTextUpdateFnName,
},
99999999,
);
await sendParticle(client, particle);
};
export const clean = async (client: FluenceClient) => {
const particle = new Particle(
`
(seq
(call myRelay ("op" "identity") [])
(seq
(call userlistNode (userlist "clear") [])
(call historyNode (history "clear") [])
)
)
`,
{
myRelay: client.relayPeerID.toB58String(),
userlist: userListServiceId,
history: historyServiceId,
userlistNode: userListNodePid,
historyNode: historyNodePid,
},
);
await sendParticle(client, particle);

View File

@ -7,8 +7,8 @@ export const notifyUserAddedFnName = 'notifyUserAdded';
export const notifyUserRemovedFnName = 'notifyUserRemoved';
export const notifyTextUpdateFnName = 'notifyTextUpdate';
export const userListServiceId = '65b7232f-45ff-464f-a804-c4c8ce563726';
export const historyServiceId = '29a5724d-c865-42fe-bfc1-e2c6b84a89b4';
export const userListServiceId = '03edcc81-7777-4234-b048-36305d8d65e2';
export const historyServiceId = '7fb57031-1d3d-4391-9a9f-da40a6c4963d';
export const userListNodePid = dev[2].peerId;
export const historyNodePid = dev[2].peerId;