mirror of
https://github.com/fluencelabs/fluent-pad
synced 2025-04-25 00:42:14 +00:00
Improve text synchronization performance
This commit is contained in:
parent
77b70b7c49
commit
633f43ec4d
6
client/package-lock.json
generated
6
client/package-lock.json
generated
@ -2190,6 +2190,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
|
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
|
||||||
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4="
|
"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": {
|
"@types/minimatch": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
"@types/react-dom": "^16.9.10",
|
"@types/react-dom": "^16.9.10",
|
||||||
"automerge": "^0.14.2",
|
"automerge": "^0.14.2",
|
||||||
"diff-match-patch": "^1.0.5",
|
"diff-match-patch": "^1.0.5",
|
||||||
|
"lodash": "^4.17.20",
|
||||||
"node-sass": "^4.14.1",
|
"node-sass": "^4.14.1",
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^17.0.1",
|
"react-dom": "^17.0.1",
|
||||||
@ -49,5 +50,7 @@
|
|||||||
"last 1 safari version"
|
"last 1 safari version"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {}
|
"devDependencies": {
|
||||||
|
"@types/lodash": "^4.14.167"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 { dev } from '@fluencelabs/fluence-network-environment';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
@ -16,6 +16,7 @@ const App = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fn = async () => {
|
const fn = async () => {
|
||||||
const c = await createClient(dev[0]);
|
const c = await createClient(dev[0]);
|
||||||
|
|
||||||
setClient(c);
|
setClient(c);
|
||||||
};
|
};
|
||||||
fn();
|
fn();
|
||||||
@ -66,11 +67,14 @@ const App = () => {
|
|||||||
Leave Room
|
Leave Room
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<button onClick={() => calls.clean(client!)}>Clean</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="wrapper">
|
<div className="wrapper">
|
||||||
<div>{isInRoom && client && <CollaborativeEditor />}</div>
|
<div>{isInRoom && client && <CollaborativeEditor />}</div>
|
||||||
{/* <div>{isInRoom && client && <UserList selfName={nickName} />}</div> */}
|
<div>{isInRoom && client && <UserList selfName={nickName} />}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</FluenceClientContext.Provider>
|
</FluenceClientContext.Provider>
|
||||||
|
@ -4,7 +4,8 @@ import { useEffect, useState } from 'react';
|
|||||||
import { fluentPadServiceId, notifyTextUpdateFnName } from 'src/fluence/constants';
|
import { fluentPadServiceId, notifyTextUpdateFnName } from 'src/fluence/constants';
|
||||||
import { useFluenceClient } from './FluenceClientContext';
|
import { useFluenceClient } from './FluenceClientContext';
|
||||||
import * as calls from 'src/fluence/calls';
|
import * as calls from 'src/fluence/calls';
|
||||||
import { subscribeToEvent } from '@fluencelabs/fluence';
|
import { FluenceClient, subscribeToEvent } from '@fluencelabs/fluence';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
interface TextDoc {
|
interface TextDoc {
|
||||||
value: Automerge.Text;
|
value: Automerge.Text;
|
||||||
@ -78,13 +79,26 @@ const applyStates = (startingDoc: TextDoc | null, entries: calls.Entry[]) => {
|
|||||||
return res;
|
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 = () => {
|
export const CollaborativeEditor = () => {
|
||||||
const client = useFluenceClient()!;
|
const client = useFluenceClient()!;
|
||||||
const [text, setText] = useState<TextDoc | null>(null);
|
const [text, setText] = useState<TextDoc | null>(Automerge.from({ value: new Automerge.Text() }));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsub1 = subscribeToEvent(client, fluentPadServiceId, notifyTextUpdateFnName, (args, tetraplets) => {
|
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);
|
const state = parseState(stateStr);
|
||||||
if (state && text) {
|
if (state && text) {
|
||||||
const newDoc = Automerge.merge(text, state);
|
const newDoc = Automerge.merge(text, state);
|
||||||
@ -116,25 +130,20 @@ export const CollaborativeEditor = () => {
|
|||||||
setText(newDoc);
|
setText(newDoc);
|
||||||
|
|
||||||
// don't block
|
// don't block
|
||||||
setImmediate(async () => {
|
setImmediate(broadcastUpdates, client, newDoc);
|
||||||
const entry = {
|
|
||||||
fluentPadState: Automerge.save(newDoc),
|
|
||||||
};
|
|
||||||
const entryStr = JSON.stringify(entry);
|
|
||||||
|
|
||||||
await calls.addEntry(client, entryStr);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<textarea value={textValue} disabled={!text} onChange={handleTextUpdate} />
|
<textarea value={textValue} disabled={!text} onChange={handleTextUpdate} />
|
||||||
Automerge changes:
|
<div>
|
||||||
<ul>
|
Automerge changes:
|
||||||
{amHistory.map((value, index) => (
|
<ul>
|
||||||
<li key={index}>{value}</li>
|
{amHistory.map((value, index) => (
|
||||||
))}
|
<li key={index}>{value}</li>
|
||||||
</ul>
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -262,7 +262,7 @@ export const addEntry = async (client: FluenceClient, entry: string) => {
|
|||||||
(par
|
(par
|
||||||
(seq
|
(seq
|
||||||
(call u.$.relay_id ("op" "identity") [])
|
(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)
|
(next u)
|
||||||
)
|
)
|
||||||
@ -284,7 +284,29 @@ export const addEntry = async (client: FluenceClient, entry: string) => {
|
|||||||
fluentPadServiceId: fluentPadServiceId,
|
fluentPadServiceId: fluentPadServiceId,
|
||||||
notifyTextUpdate: notifyTextUpdateFnName,
|
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);
|
await sendParticle(client, particle);
|
||||||
|
@ -7,8 +7,8 @@ export const notifyUserAddedFnName = 'notifyUserAdded';
|
|||||||
export const notifyUserRemovedFnName = 'notifyUserRemoved';
|
export const notifyUserRemovedFnName = 'notifyUserRemoved';
|
||||||
export const notifyTextUpdateFnName = 'notifyTextUpdate';
|
export const notifyTextUpdateFnName = 'notifyTextUpdate';
|
||||||
|
|
||||||
export const userListServiceId = '65b7232f-45ff-464f-a804-c4c8ce563726';
|
export const userListServiceId = '03edcc81-7777-4234-b048-36305d8d65e2';
|
||||||
export const historyServiceId = '29a5724d-c865-42fe-bfc1-e2c6b84a89b4';
|
export const historyServiceId = '7fb57031-1d3d-4391-9a9f-da40a6c4963d';
|
||||||
|
|
||||||
export const userListNodePid = dev[2].peerId;
|
export const userListNodePid = dev[2].peerId;
|
||||||
export const historyNodePid = dev[2].peerId;
|
export const historyNodePid = dev[2].peerId;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user