Update application code

This commit is contained in:
Pavel Murygin 2021-09-10 02:04:29 +03:00
parent fd7fadb2a9
commit 3c36e3a95b
8 changed files with 517 additions and 484 deletions

View File

@ -1,12 +1,12 @@
module App module App
import PeerId, Op, Peer from "@fluencelabs/aqua-lib/builtin.aqua" import PeerId, Op, Peer from "@fluencelabs/aqua-lib/builtin.aqua"
import AppConfig, FluentPad from "fluent-pad.aqua" import AppConfig, UserStatus, TextState from "fluent-pad.aqua"
import User, UserList, AuthResult from "user-list.aqua" import User, UserList, AuthResult from "user-list.aqua"
import EmptyServiceResult from "common.aqua" import EmptyServiceResult from "common.aqua"
import History, GetEntriesServiceResult, AddServiceResult from "history.aqua" import History, GetEntriesServiceResult, AddServiceResult from "history.aqua"
export join, getUserList, initAfterJoin, updateOnlineStatuses, leave, auth, getHistory, addEntry, FluentPad, AppConfig export join, getUserList, initAfterJoin, updateOnlineStatuses, leave, auth, getHistory, addEntry, UserStatus, TextState, AppConfig
func join(user: User) -> EmptyServiceResult: func join(user: User) -> EmptyServiceResult:
app <- AppConfig.getApp() app <- AppConfig.getApp()
@ -29,10 +29,10 @@ func initAfterJoin(me: User) -> []User:
isOnline <- Peer.is_connected(user.peer_id) isOnline <- Peer.is_connected(user.peer_id)
if isOnline: if isOnline:
on user.peer_id via user.relay_id: on user.peer_id via user.relay_id:
FluentPad.notifyUserAdded(me, true) UserStatus.notifyUserAdded(me, true)
-- else: -- else:
--` Op.identity("dontcare") --` Op.identity("dontcare")
par FluentPad.notifyUserAdded(user, isOnline) par UserStatus.notifyUserAdded(user, isOnline)
<- allUsers <- allUsers
@ -41,17 +41,17 @@ func updateOnlineStatuses():
for user <- allUsers par: for user <- allUsers par:
on user.peer_id via user.relay_id: on user.peer_id via user.relay_id:
isOnline <- Peer.is_connected(user.peer_id) isOnline <- Peer.is_connected(user.peer_id)
FluentPad.notifyOnline(user.peer_id, isOnline) UserStatus.notifyOnline(user.peer_id, isOnline)
func leave(): func leave():
app <- AppConfig.getApp() app <- AppConfig.getApp()
on app.user_list.peer_id: on app.user_list.peer_id:
UserList app.user_list.service_id UserList app.user_list.service_id
res <- UserList.leave(%init_peer_id%) res <- UserList.leave(INIT_PEER_ID)
allUsers <- getUserList() allUsers <- getUserList()
for user <- allUsers par: for user <- allUsers par:
on user.peer_id via user.relay_id: on user.peer_id via user.relay_id:
FluentPad.notifyUserRemoved(%init_peer_id%) UserStatus.notifyUserRemoved(INIT_PEER_ID)
func auth() -> AuthResult: func auth() -> AuthResult:
app <- AppConfig.getApp() app <- AppConfig.getApp()
@ -76,7 +76,7 @@ func addEntry(entry: string) -> AddServiceResult:
res <- History.add(entry, authRes.is_authenticated) res <- History.add(entry, authRes.is_authenticated)
allUsers <- getUserList() allUsers <- getUserList()
for user <- allUsers par: for user <- allUsers par:
if user.peer_id != %init_peer_id%: if user.peer_id != INIT_PEER_ID:
on user.peer_id via user.relay_id: on user.peer_id via user.relay_id:
FluentPad.notifyTextUpdate(entry, authRes.is_authenticated) TextState.notifyTextUpdate(entry, authRes.is_authenticated)
<- res <- res

View File

@ -1,4 +1,4 @@
module FluentPad declares AppConfig, FluentPad module FluentPad declares AppConfig, UserStatus, TextState
import PeerId from "@fluencelabs/aqua-lib/builtin.aqua" import PeerId from "@fluencelabs/aqua-lib/builtin.aqua"
import User from "user-list.aqua" import User from "user-list.aqua"
@ -11,10 +11,12 @@ data App:
history: ServiceInstance history: ServiceInstance
user_list: ServiceInstance user_list: ServiceInstance
service FluentPad("fluence/fluent-pad"): service UserStatus("fluence/fluent-pad/status"):
notifyOnline(userPeerId: string, isOnline: bool) notifyOnline(userPeerId: string, isOnline: bool)
notifyUserAdded(currentUser: User, isOnline: bool) notifyUserAdded(currentUser: User, isOnline: bool)
notifyUserRemoved(userPeerId: PeerId) notifyUserRemoved(userPeerId: PeerId)
service TextState("fluence/fluent-pad/text-state"):
notifyTextUpdate(changes: string, isAuthorized: bool) notifyTextUpdate(changes: string, isAuthorized: bool)
service AppConfig("fluence/get-config"): service AppConfig("fluence/get-config"):

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,6 @@
import config from 'src/app.json'; import config from 'src/app.json';
import { krasnodar } from '@fluencelabs/fluence-network-environment'; import { krasnodar } from '@fluencelabs/fluence-network-environment';
export const fluentPadServiceId = 'fluence/fluent-pad';
export const notifyOnlineFnName = 'notifyOnline';
export const notifyUserAddedFnName = 'notifyUserAdded';
export const notifyUserRemovedFnName = 'notifyUserRemoved';
export const notifyTextUpdateFnName = 'notifyTextUpdate';
export const userList = { export const userList = {
peer_id: config.services.user_list.node, peer_id: config.services.user_list.node,
service_id: config.services.user_list.id, service_id: config.services.user_list.id,
@ -23,7 +16,7 @@ export const fluentPadApp = {
history: history, history: history,
}; };
// export const relayNode = testNet[0]; // export const relayNode = krasnodar[0];
export const relayNode = { export const relayNode = {
multiaddr: '/ip4/127.0.0.1/tcp/4310/ws/p2p/12D3KooWKEprYXUXqoV5xSBeyqrWLpQLLH4PXfvVkDJtmcqmh5V3', multiaddr: '/ip4/127.0.0.1/tcp/4310/ws/p2p/12D3KooWKEprYXUXqoV5xSBeyqrWLpQLLH4PXfvVkDJtmcqmh5V3',
peerId: '12D3KooWKEprYXUXqoV5xSBeyqrWLpQLLH4PXfvVkDJtmcqmh5V3', peerId: '12D3KooWKEprYXUXqoV5xSBeyqrWLpQLLH4PXfvVkDJtmcqmh5V3',

View File

@ -7,42 +7,44 @@ import { UserList } from './UserList';
import { CollaborativeEditor } from './CollaborativeEditor'; import { CollaborativeEditor } from './CollaborativeEditor';
import { fluentPadApp, relayNode } from 'src/app/constants'; import { fluentPadApp, relayNode } from 'src/app/constants';
import { CheckResponse, withErrorHandlingAsync } from './util'; import { CheckResponse, withErrorHandlingAsync } from './util';
import { join, leave } from 'src/_aqua/app'; import { join, leave, registerAppConfig } from 'src/_aqua/app';
const createClientEx = async (relay) => {
const client = await createClient(relay);
client.aquaCallHandler.on('fluence/get-config', 'getApp', () => {
return fluentPadApp;
});
client.aquaCallHandler.on('fluence/get-config', 'get_init_peer_id', () => {
return client.selfPeerId;
});
client.aquaCallHandler.on('fluence/get-config', 'get_init_relay', () => {
return client.relayPeerId!;
});
return client;
};
const App = () => { const App = () => {
const [client, setClient] = useState<FluenceClient | null>(null); const [isConnected, setIsConnected] = useState<boolean>(false);
const [isInRoom, setIsInRoom] = useState<boolean>(false); const [isInRoom, setIsInRoom] = useState<boolean>(false);
const [nickName, setNickName] = useState(''); const [nickName, setNickName] = useState('');
const connect = async () => {
try {
await FluencePeer.default.init({ connectTo: relayNode });
setIsConnected(true);
registerAppConfig({
getApp: () => {
return fluentPadApp
},
})
}
catch (err) {
console.log('Peer initialization failed', err)
}
}
useEffect(() => { useEffect(() => {
createClientEx(relayNode) connect()
.then((client) => setClient(client))
.catch((err) => console.log('Client initialization failed', err));
}, []); }, []);
const joinRoom = async () => { const joinRoom = async () => {
if (!client) { if (!isConnected) {
return; return;
} }
await withErrorHandlingAsync(async () => { await withErrorHandlingAsync(async () => {
const res = await join(client, { const res = await join( {
peer_id: client.selfPeerId, peer_id: FluencePeer.default.connectionInfo.selfPeerId,
relay_id: client.relayPeerId!, relay_id: FluencePeer.default.connectionInfo.connectedRelay!,
name: nickName, name: nickName,
}); });
if (CheckResponse(res)) { if (CheckResponse(res)) {
@ -52,18 +54,18 @@ const App = () => {
}; };
const leaveRoom = async () => { const leaveRoom = async () => {
if (!client) { if (!isConnected) {
return; return;
} }
await withErrorHandlingAsync(async () => { await withErrorHandlingAsync(async () => {
await leave(client); await leave();
setIsInRoom(false); setIsInRoom(false);
}); });
}; };
return ( return (
<FluenceClientContext.Provider value={client}> <>
<div className="header-wrapper"> <div className="header-wrapper">
<div className="header"> <div className="header">
<div className="header-item"> <div className="header-item">
@ -75,7 +77,7 @@ const App = () => {
</div> </div>
<div className="header-item"> <div className="header-item">
Connection status: {client ? <span className="accent">connected</span> : 'disconnected'} Connection status: {isConnected ? <span className="accent">connected</span> : 'disconnected'}
</div> </div>
</div> </div>
</div> </div>
@ -105,7 +107,7 @@ const App = () => {
<input <input
type="submit" type="submit"
className="join-button" className="join-button"
disabled={isInRoom || !client || !nickName} disabled={isInRoom || !isConnected || !nickName}
value="Join" value="Join"
/> />
</form> </form>
@ -120,7 +122,7 @@ const App = () => {
)} )}
</div> </div>
</div> </div>
</FluenceClientContext.Provider> </>
); );
}; };

View File

@ -1,12 +1,9 @@
import _ from 'lodash'; import _ from 'lodash';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { PeerIdB58, subscribeToEvent } from '@fluencelabs/fluence';
import { fluentPadServiceId, notifyTextUpdateFnName } from 'src/app/constants';
import { useFluenceClient } from '../app/FluenceClientContext';
import { getUpdatedDocFromText, initDoc, SyncClient } from '../app/sync'; import { getUpdatedDocFromText, initDoc, SyncClient } from '../app/sync';
import { withErrorHandlingAsync } from './util'; import { withErrorHandlingAsync } from './util';
import { addEntry, getHistory } from 'src/aqua/app'; import { addEntry, getHistory, registerTextState } from 'src/_aqua/app';
const broadcastUpdates = _.debounce((text: string, syncClient: SyncClient) => { const broadcastUpdates = _.debounce((text: string, syncClient: SyncClient) => {
let doc = syncClient.getDoc(); let doc = syncClient.getDoc();
@ -17,7 +14,6 @@ const broadcastUpdates = _.debounce((text: string, syncClient: SyncClient) => {
}, 100); }, 100);
export const CollaborativeEditor = () => { export const CollaborativeEditor = () => {
const client = useFluenceClient()!;
const [text, setText] = useState<string | null>(null); const [text, setText] = useState<string | null>(null);
const [syncClient, setSyncClient] = useState(new SyncClient()); const [syncClient, setSyncClient] = useState(new SyncClient());
@ -28,7 +24,7 @@ export const CollaborativeEditor = () => {
syncClient.handleSendChanges = (changes: string) => { syncClient.handleSendChanges = (changes: string) => {
withErrorHandlingAsync(async () => { withErrorHandlingAsync(async () => {
const res = await addEntry(client, changes); const res = await addEntry(changes);
if (res.ret_code !== 0) { if (res.ret_code !== 0) {
throw new Error( throw new Error(
`Failed to add message to history service, code=${res.ret_code}, message=${res.err_msg}`, `Failed to add message to history service, code=${res.ret_code}, message=${res.err_msg}`,
@ -37,19 +33,19 @@ export const CollaborativeEditor = () => {
}); });
}; };
const unsub = subscribeToEvent(client, fluentPadServiceId, notifyTextUpdateFnName, (args, tetraplets) => { registerTextState({
const [changes, isAuthorized] = args as [string, boolean]; notifyTextUpdate: (changes, isAuthorized) => {
if (changes) {
if (changes) { syncClient.receiveChanges(changes);
syncClient.receiveChanges(changes); }
} }
}); })
syncClient.start(); syncClient.start();
// don't block // don't block
withErrorHandlingAsync(async () => { withErrorHandlingAsync(async () => {
const res = await getHistory(client); const res = await getHistory();
for (let e of res.entries) { for (let e of res.entries) {
syncClient.receiveChanges(e.body); syncClient.receiveChanges(e.body);
} }
@ -60,7 +56,6 @@ export const CollaborativeEditor = () => {
}); });
return () => { return () => {
unsub();
syncClient.stop(); syncClient.stop();
}; };
}, []); }, []);

View File

@ -1,14 +1,10 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import {
fluentPadServiceId,
notifyOnlineFnName,
notifyUserAddedFnName,
notifyUserRemovedFnName,
} from 'src/app/constants';
import { useFluenceClient } from '../app/FluenceClientContext';
import { PeerIdB58, subscribeToEvent } from '@fluencelabs/fluence';
import { withErrorHandlingAsync } from './util'; import { withErrorHandlingAsync } from './util';
import { initAfterJoin, updateOnlineStatuses } from 'src/aqua/app'; import { initAfterJoin, updateOnlineStatuses } from 'src/_aqua/app';
import { registerUserStatus } from 'src/_aqua/app';
import { FluencePeer, PeerIdB58 } from '@fluencelabs/fluence';
interface User { interface User {
id: PeerIdB58; id: PeerIdB58;
@ -16,16 +12,9 @@ interface User {
isOnline: boolean; isOnline: boolean;
} }
interface ApiUser {
name: string;
peer_id: string;
relay_id: string;
}
const refreshOnlineStatusTimeoutMs = 10000; const refreshOnlineStatusTimeoutMs = 10000;
export const UserList = (props: { selfName: string }) => { export const UserList = (props: { selfName: string }) => {
const client = useFluenceClient()!;
const [users, setUsers] = useState<Map<PeerIdB58, User>>(new Map()); const [users, setUsers] = useState<Map<PeerIdB58, User>>(new Map());
const updateOnlineStatus = (user, onlineStatus) => { const updateOnlineStatus = (user, onlineStatus) => {
@ -42,12 +31,15 @@ export const UserList = (props: { selfName: string }) => {
useEffect(() => { useEffect(() => {
const listRefreshTimer = setInterval(() => { const listRefreshTimer = setInterval(() => {
withErrorHandlingAsync(async () => { withErrorHandlingAsync(async () => {
// await updateOnlineStatuses(client); await updateOnlineStatuses();
}); });
}, refreshOnlineStatusTimeoutMs); }, refreshOnlineStatusTimeoutMs);
const unsub1 = subscribeToEvent(client, fluentPadServiceId, notifyUserAddedFnName, (args, _) => { registerUserStatus({
const [user, isOnline] = args as [ApiUser, boolean]; notifyOnline: (user, onlineStatus) => {
updateOnlineStatus(user, onlineStatus);
},
notifyUserAdded: (user, isOnline) => {
setUsers((prev) => { setUsers((prev) => {
const u = user; const u = user;
const result = new Map(prev); const result = new Map(prev);
@ -63,36 +55,29 @@ export const UserList = (props: { selfName: string }) => {
return result; return result;
}); });
}); },
const unsub2 = subscribeToEvent(client, fluentPadServiceId, notifyUserRemovedFnName, (args, _) => { notifyUserRemoved: (userLeft) => {
const [userLeft] = args as [PeerIdB58];
setUsers((prev) => { setUsers((prev) => {
const result = new Map(prev); const result = new Map(prev);
result.delete(userLeft); result.delete(userLeft);
return result; return result;
}); });
}); }
})
const unsub3 = subscribeToEvent(client, fluentPadServiceId, notifyOnlineFnName, (args, _) => {
const [user, onlineStatus] = args as [PeerIdB58, boolean];
updateOnlineStatus(user, onlineStatus);
});
// don't block // don't block
withErrorHandlingAsync(async () => { withErrorHandlingAsync(async () => {
await initAfterJoin(client, { await initAfterJoin({
name: props.selfName, name: props.selfName,
peer_id: client.selfPeerId, peer_id: FluencePeer.default.connectionInfo.selfPeerId,
relay_id: client.relayPeerId!, relay_id: FluencePeer.default.connectionInfo.connectedRelay!,
}); });
}); });
return () => { return () => {
clearTimeout(listRefreshTimer); clearTimeout(listRefreshTimer);
unsub1();
unsub2();
unsub3();
}; };
}, []); }, []);
@ -105,7 +90,7 @@ export const UserList = (props: { selfName: string }) => {
<ul> <ul>
{usersArray.map((x) => ( {usersArray.map((x) => (
<li key={x.id}> <li key={x.id}>
<span className={x.id === client.selfPeerId ? 'bold' : ''}>{x.name}</span> <span className={x.id === FluencePeer.default.connectionInfo.selfPeerId ? 'bold' : ''}>{x.name}</span>
<span className={x.isOnline ? 'green' : 'red'}> ({x.isOnline ? 'online' : 'offline'})</span> <span className={x.isOnline ? 'green' : 'red'}> ({x.isOnline ? 'online' : 'offline'})</span>
</li> </li>
))} ))}

0
deploy_docker.sh Normal file → Executable file
View File