Users list with online statuses syncrhonization works

This commit is contained in:
Pavel Murygin 2021-01-14 12:17:36 +03:00
parent 4879878e9f
commit 735c5cbe0c
3 changed files with 126 additions and 67 deletions

View File

@ -3,35 +3,38 @@ import React, { useEffect, useState } from 'react';
import { connect } from 'src/fluence';
import './App.scss';
import { FluenceClientContext } from './FluenceClientContext';
import { FluenceClientContext, useFluenceClient } from './FluenceClientContext';
import { UserList } from './UserList';
import * as calls from 'src/fluence/calls';
const App = () => {
const [client, setClient] = useState<FluenceClient | null>(null);
const [isConnected, setIsConnected] = useState<boolean>(false);
const [isInRoom, setIsInRoom] = useState(false);
const [isInRoom, setIsInRoom] = useState<boolean>(false);
const [nickName, setNickName] = useState('myNickName');
useEffect(() => {
const fn = async () => {
const c = await connect();
setIsConnected(true);
setClient(c);
};
// fn();
fn();
}, []);
const joinRoom = async () => {
const c = await connect();
setIsConnected(true);
setClient(c);
await calls.joinRoom(nickName);
if (!client) {
return;
}
await calls.joinRoom(client, nickName);
setIsInRoom(true);
};
const leaveRoom = async () => {
await calls.leaveRoom();
if (!client) {
return;
}
await calls.leaveRoom(client);
setIsInRoom(false);
};
@ -39,7 +42,7 @@ const App = () => {
<FluenceClientContext.Provider value={client}>
<div className="App">
<div>
<div>Connection status: {isConnected ? 'connected' : 'disconnected'}</div>
<div>Connection status: {client ? 'connected' : 'disconnected'}</div>
<div>
<label>Nickname: </label>
<input
@ -53,7 +56,7 @@ const App = () => {
/>
</div>
<div>
<button disabled={isInRoom} onClick={joinRoom}>
<button disabled={isInRoom || !client} onClick={joinRoom}>
Join Room
</button>
</div>
@ -64,7 +67,7 @@ const App = () => {
</div>
</div>
<div>{isConnected && client && isInRoom && <UserList />}</div>
<div>{isInRoom && client && <UserList selfName={nickName} />}</div>
</div>
</FluenceClientContext.Provider>
);

View File

@ -28,7 +28,7 @@ type PeerId = string;
const refreshTimeoutMs = 7000;
export const UserList = () => {
export const UserList = (props: { selfName: string }) => {
const client = useFluenceClient()!;
const [users, setUsers] = useState<Map<PeerId, User>>(new Map());
@ -45,7 +45,7 @@ export const UserList = () => {
calls.updateOnlineStatuses(client);
}, refreshTimeoutMs);
subscribeToEvent(client, fluentPadServiceId, notifyUserAddedFnName, (args, _) => {
const unsub1 = subscribeToEvent(client, fluentPadServiceId, notifyUserAddedFnName, (args, _) => {
const users = args.flatMap((x) => x).flatMap((x) => x) as calls.User[];
setUsers((prev) => {
const result = new Map(prev);
@ -54,29 +54,31 @@ export const UserList = () => {
continue;
}
const isCurrentUser = u.peer_id === client.selfPeerId.toB58String();
result.set(u.peer_id, {
name: u.name,
id: u.peer_id,
isOnline: false,
shouldBecomeOnline: false,
isOnline: isCurrentUser,
shouldBecomeOnline: isCurrentUser,
});
}
return result;
});
});
subscribeToEvent(client, fluentPadServiceId, notifyUserRemovedFnName, (args, _) => {
const users = args.flatMap((x) => x) as calls.User[];
const unsub2 = subscribeToEvent(client, fluentPadServiceId, notifyUserRemovedFnName, (args, _) => {
const users = args.flatMap((x) => x) as PeerId[];
setUsers((prev) => {
const result = new Map(prev);
for (let u of users) {
result.delete(u.peer_id);
result.delete(u);
}
return result;
});
});
const unsub = subscribeToEvent(client, fluentPadServiceId, notifyOnlineFnName, (args, _) => {
const unsub3 = subscribeToEvent(client, fluentPadServiceId, notifyOnlineFnName, (args, _) => {
const [[peerId], immediately] = args;
setUsers((prev) => {
const result = new Map(prev);
@ -94,11 +96,14 @@ export const UserList = () => {
});
// don't block
calls.getInitialUserList(client);
calls.getUserList(client);
calls.notifySelfAdded(client, props.selfName);
return () => {
clearTimeout(listRefreshTimer);
unsub();
unsub1();
unsub2();
unsub3();
};
}, []);

View File

@ -5,6 +5,7 @@ import {
historyServiceId,
notifyOnlineFnName,
notifyUserAddedFnName,
notifyUserRemovedFnName,
servicesNodePid,
userListServiceId,
} from './constants';
@ -40,27 +41,6 @@ const throwIfError = (result: ServiceResult) => {
}
};
const notifyOnlineStatusesAir = `
(fold allUsers.$.users! u
(par
(seq
(call u.$.relay_id ("op" "identity") [])
(seq
(call u.$.peer_id ("op" "identity") [])
(seq
(call u.$.relay_id ("op" "identity") [])
(seq
(call myRelay ("op" "identity") [])
(call myPeerId (fluentPadServiceId notifyOnline) [u.$.peer_id immediately])
)
)
)
)
(next u)
)
)
`;
export const updateOnlineStatuses = async (client: FluenceClient) => {
const particle = new Particle(
`
@ -68,7 +48,24 @@ export const updateOnlineStatuses = async (client: FluenceClient) => {
(call myRelay ("op" "identity") [])
(seq
(call node (userlist "get_users") [] allUsers)
${notifyOnlineStatusesAir}
(fold allUsers.$.users! u
(par
(seq
(call u.$.relay_id ("op" "identity") [])
(seq
(call u.$.peer_id ("op" "identity") [])
(seq
(call u.$.relay_id ("op" "identity") [])
(seq
(call myRelay ("op" "identity") [])
(call myPeerId (fluentPadServiceId notifyOnline) [u.$.peer_id immediately])
)
)
)
)
(next u)
)
)
)
)
`,
@ -86,19 +83,53 @@ export const updateOnlineStatuses = async (client: FluenceClient) => {
sendParticle(client, particle);
};
export const getInitialUserList = async (client: FluenceClient) => {
export const notifySelfAdded = (client: FluenceClient, name: string) => {
const particle = new Particle(
`
(seq
(call myRelay ("op" "identity") [])
(seq
(call node (userlist "get_users") [] allUsers)
(par
(seq
(call myRelay ("op" "identity") [])
(call myPeerId (fluentPadServiceId notifyUserAdded) [allUsers.$.users!])
(fold allUsers.$.users! u
(par
(seq
(call u.$.relay_id ("op" "identity") [])
(call u.$.peer_id (fluentPadServiceId notifyUserAdded) [myUser])
)
(next u)
)
${notifyOnlineStatusesAir}
)
)
)
`,
{
node: servicesNodePid,
userlist: userListServiceId,
myRelay: client.relayPeerID.toB58String(),
myPeerId: client.selfPeerId.toB58String(),
fluentPadServiceId: fluentPadServiceId,
notifyUserAdded: notifyUserAddedFnName,
myUser: {
name: name,
peer_id: client.selfPeerId.toB58String(),
relay_id: client.relayPeerID.toB58String(),
},
},
);
sendParticle(client, particle);
};
export const getUserList = async (client: FluenceClient) => {
const particle = new Particle(
`
(seq
(call myRelay ("op" "identity") [])
(seq
(call node (userlist "get_users") [] allUsers)
(seq
(call myRelay ("op" "identity") [])
(call myPeerId (fluentPadServiceId notifyUserAdded) [allUsers.$.users!])
)
)
)
@ -117,7 +148,7 @@ export const getInitialUserList = async (client: FluenceClient) => {
await sendParticle(client, particle);
};
export const joinRoom = async (nickName: string) => {
export const joinRoom = async (client: FluenceClient, nickName: string) => {
let joinRoomAir = `
(call node (userlist "join") [user] result)
`;
@ -125,30 +156,50 @@ export const joinRoom = async (nickName: string) => {
const data = new Map();
data.set('user', {
name: nickName,
peer_id: fluenceClient.selfPeerId.toB58String(),
relay_id: fluenceClient.relayPeerID.toB58String(),
peer_id: client.selfPeerId.toB58String(),
relay_id: client.relayPeerID.toB58String(),
});
data.set('userlist', userListServiceId);
data.set('node', servicesNodePid);
const [result] = await fluenceClient.fetch<[GetUsersResult]>(joinRoomAir, ['result'], data);
const [result] = await client.fetch<[GetUsersResult]>(joinRoomAir, ['result'], data);
throwIfError(result);
return result.users;
};
export const leaveRoom = async () => {
let leaveRoomAir = `
(call node (userlist "leave") [ownPeerId] callResult)
`;
export const leaveRoom = async (client: FluenceClient) => {
const particle = new Particle(
`
(seq
(call myRelay ("op" "identity") [])
(seq
(call node (userlist "leave") [myPeerId])
(seq
(call node (userlist "get_users") [] allUsers)
(fold allUsers.$.users! u
(par
(seq
(call u.$.relay_id ("op" "identity") [])
(call u.$.peer_id (fluentPadServiceId notifyUserRemoved) [myPeerId])
)
(next u)
)
)
)
)
)
`,
{
node: servicesNodePid,
userlist: userListServiceId,
myRelay: client.relayPeerID.toB58String(),
myPeerId: client.selfPeerId.toB58String(),
fluentPadServiceId: fluentPadServiceId,
notifyUserRemoved: notifyUserRemovedFnName,
},
);
const data = new Map();
data.set('ownPeerId', fluenceClient.selfPeerId.toB58String());
data.set('userlist', userListServiceId);
data.set('node', servicesNodePid);
const [result] = await fluenceClient.fetch<[ServiceResult]>(leaveRoomAir, ['callResult'], data);
throwIfError(result);
return result;
await sendParticle(client, particle);
};
export const removeUser = async (userPeerId: string) => {