diff --git a/packages/renderer/assets/join-call.wav b/packages/renderer/assets/join-call.wav
new file mode 100644
index 0000000..f306de2
Binary files /dev/null and b/packages/renderer/assets/join-call.wav differ
diff --git a/packages/renderer/assets/leave-call.wav b/packages/renderer/assets/leave-call.wav
new file mode 100644
index 0000000..98e3b81
Binary files /dev/null and b/packages/renderer/assets/leave-call.wav differ
diff --git a/packages/renderer/index.html b/packages/renderer/index.html
index d1dbb1a..c40973c 100644
--- a/packages/renderer/index.html
+++ b/packages/renderer/index.html
@@ -6,7 +6,7 @@
diff --git a/packages/renderer/src/components/ServerConnection.tsx b/packages/renderer/src/components/ServerConnection.tsx
index fec52cf..14a18da 100644
--- a/packages/renderer/src/components/ServerConnection.tsx
+++ b/packages/renderer/src/components/ServerConnection.tsx
@@ -1,5 +1,6 @@
import { createContext, PropsWithChildren, ReactNode, useEffect, useMemo } from "react";
import ClientsListState from "../contexts/EphemeralState/ClientsListState";
+import PeerState from "../contexts/EphemeralState/PeerState";
import { connectApi } from "../lib/api";
interface ServerConnectionProps {
@@ -33,7 +34,9 @@ export default function ServerConnection(props: ServerConnectionProps) {
return
- {props.children}
+
+ {props.children}
+
}
\ No newline at end of file
diff --git a/packages/renderer/src/components/Voice.tsx b/packages/renderer/src/components/Voice.tsx
index b23c30e..89100d5 100644
--- a/packages/renderer/src/components/Voice.tsx
+++ b/packages/renderer/src/components/Voice.tsx
@@ -7,17 +7,21 @@ import { useApi } from "../lib/useApi";
export default function Voice(props: any) {
const { uid } = props;
- const { connected, peerId } = useContext(PeerContext);
+ const { connected, peerId, join } = useContext(PeerContext);
const { channel } = useChannel();
- const { send } = useApi({
-
- });
+ const { send } = useApi({});
const joinCall = useCallback(() => {
- if(peerId === null || connected === false) return;
+ if(peerId === null || connected === false || channel === null) return;
+ join(channel);
send('voice:join', { peerId, channelId: channel })
- }, [connected, peerId, channel])
+ }, [connected, peerId, channel]);
+
+ const leaveCall = useCallback(() => {
+ if(peerId === null || connected === false) return;
+ send('voice:leave', { peerId, channelId: channel })
+ }, [connected, peerId, channel]);
return
+
}
\ No newline at end of file
diff --git a/packages/renderer/src/contexts/EphemeralState/ClientsListState.tsx b/packages/renderer/src/contexts/EphemeralState/ClientsListState.tsx
index d48475d..29b5095 100644
--- a/packages/renderer/src/contexts/EphemeralState/ClientsListState.tsx
+++ b/packages/renderer/src/contexts/EphemeralState/ClientsListState.tsx
@@ -29,10 +29,6 @@ export default function ClientsListState(props: any) {
}
});
- useEffect(() => {
- console.log(clients);
- }, [clients]);
-
useEffect(() => {
send('clients:list')
}, []);
diff --git a/packages/renderer/src/contexts/EphemeralState/EphemeralState.tsx b/packages/renderer/src/contexts/EphemeralState/EphemeralState.tsx
index 1984bfb..300f5d6 100644
--- a/packages/renderer/src/contexts/EphemeralState/EphemeralState.tsx
+++ b/packages/renderer/src/contexts/EphemeralState/EphemeralState.tsx
@@ -82,9 +82,7 @@ export default function EphemeralState(props: {
isSettingsOpen: settings,
}}>
-
- {props.children}
-
+ {props.children}
diff --git a/packages/renderer/src/contexts/EphemeralState/PeerState.tsx b/packages/renderer/src/contexts/EphemeralState/PeerState.tsx
index 3d52f47..d029e48 100644
--- a/packages/renderer/src/contexts/EphemeralState/PeerState.tsx
+++ b/packages/renderer/src/contexts/EphemeralState/PeerState.tsx
@@ -1,13 +1,20 @@
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
import { Peer, MediaConnection } from "peerjs";
import { UserMediaContext } from "./UserMediaState";
+import { useApi } from "/@/lib/useApi";
export const PeerContext = createContext<{
connected: boolean;
- peerId: string | null
+ inCall: boolean;
+ peerId: string | null;
+ join: (channelId: string) => void;
+ leave: () => void;
}>({
connected: false,
- peerId: null
+ peerId: null,
+ inCall: false,
+ join: () => {},
+ leave: () => {}
});
function useCurrent(thing: T) {
@@ -20,6 +27,13 @@ function useCurrent(thing: T) {
return thingRef.current;
}
+interface Connection {
+ peerId: string;
+ clientId: string;
+ channelID: string;
+ call: any;
+}
+
export default function PeerState(props: any) {
const { mediaStream } = useContext(UserMediaContext);
@@ -29,17 +43,30 @@ export default function PeerState(props: any) {
const [peer, setPeer] = useState(null);
const [peerId, setPeerId] = useState(null);
const [incomingCalls, setIncomingCalls] = useState([]);
+ const [connections, setConnections] = useState([]);
+ const [channel, setChannel] = useState(null);
- const addCall = useCurrent(useCallback((call: MediaConnection) => {
- // HACK lookout for possible timing issues here.
- // if we get two incomming calls before a re-render
- // then our state could be out of date?!
- // a possible solution is to cache the
- // append to the list, and if the cache and
- // state disagree, add to the cache, and set state
- // to the cached value.
- setIncomingCalls([...incomingCalls, call]);
- }, [incomingCalls]))
+ const addIncomingCall = useCurrent(useCallback((call: MediaConnection) => {
+ setIncomingCalls(incomingCalls => ([...incomingCalls, call]));
+ }, []));
+
+ const { send } = useApi({
+ 'voice:join'(data: any) {
+ if(data.channelId !== channel) return
+ console.log('PEER STATE CONNECTIONS', data);
+ },
+ 'voice:leave'(data: any) {
+ if(data.channelId !== channel) return
+ console.log('PEER STATE CONNECTIONS', data)
+ },
+ 'voice:list'(data: any) {
+ if(data.uid !== channel) return
+ setConnections(connections => {
+
+ })
+ console.log('PEER STATE CONNECTIONS', data);
+ }
+ }, [channel]);
useEffect(() => {
if(connected) return;
@@ -59,7 +86,7 @@ export default function PeerState(props: any) {
});
peer.on('call', (call: MediaConnection) => {
- addCall(call);
+ addIncomingCall(call);
});
}, [connected]);
@@ -69,10 +96,23 @@ export default function PeerState(props: any) {
peer.call(id, mediaStream);
}, [peer, mediaStream])
+ const joinChannel = (channelId: string) => {
+ setChannel(channelId);
+ send('voice:list', { channelId })
+ }
+
+ useEffect(() => {
+ if(channel === null) return;
+ console.log('WE JOINED A CHANNEL')
+ }, [channel])
+
const value = useMemo(() => ({
connected,
peerId,
- }), [connected, peerId]);
+ inCall: connections.length === 0,
+ join: joinChannel,
+ leave: () => {}
+ }), [connected, peerId, connections]);
return
{props.children}
diff --git a/packages/renderer/src/lib/sound.ts b/packages/renderer/src/lib/sound.ts
new file mode 100644
index 0000000..be1ccaa
--- /dev/null
+++ b/packages/renderer/src/lib/sound.ts
@@ -0,0 +1,14 @@
+import joinCallSound from '../../assets/join-call.wav';
+import leaveCallSound from '../../assets/leave-call.wav';
+const beep = "data:audio/wav;base64,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qJrx4KOQjahZxWbcZgztj2c49nKmkId44S71j0c8eV9yDK6uPRzx5X18eDvjvQ6yKo9ZSS6l//8elePK/Lf//IInrOF/FvDoADYAGBMGb7FtErm5MXMlmPAJQVgWta7Zx2go+8xJ0UiCb8LHHdftWyLJE0QIAIsI+UbXu67dZMjmgDGCGl1H+vpF4NSDckSIkk7Vd+sxEhBQMRU8j/12UIRhzSaUdQ+rQU5kGeFxm+hb1oh6pWWmv3uvmReDl0UnvtapVaIzo1jZbf/pD6ElLqSX+rUmOQNpJFa/r+sa4e/pBlAABoAAAAA3CUgShLdGIxsY7AUABPRrgCABdDuQ5GC7DqPQCgbbJUAoRSUj+NIEig0YfyWUho1VBBBA//uQZB4ABZx5zfMakeAAAAmwAAAAF5F3P0w9GtAAACfAAAAAwLhMDmAYWMgVEG1U0FIGCBgXBXAtfMH10000EEEEEECUBYln03TTTdNBDZopopYvrTTdNa325mImNg3TTPV9q3pmY0xoO6bv3r00y+IDGid/9aaaZTGMuj9mpu9Mpio1dXrr5HERTZSmqU36A3CumzN/9Robv/Xx4v9ijkSRSNLQhAWumap82WRSBUqXStV/YcS+XVLnSS+WLDroqArFkMEsAS+eWmrUzrO0oEmE40RlMZ5+ODIkAyKAGUwZ3mVKmcamcJnMW26MRPgUw6j+LkhyHGVGYjSUUKNpuJUQoOIAyDvEyG8S5yfK6dhZc0Tx1KI/gviKL6qvvFs1+bWtaz58uUNnryq6kt5RzOCkPWlVqVX2a/EEBUdU1KrXLf40GoiiFXK///qpoiDXrOgqDR38JB0bw7SoL+ZB9o1RCkQjQ2CBYZKd/+VJxZRRZlqSkKiws0WFxUyCwsKiMy7hUVFhIaCrNQsKkTIsLivwKKigsj8XYlwt/WKi2N4d//uQRCSAAjURNIHpMZBGYiaQPSYyAAABLAAAAAAAACWAAAAApUF/Mg+0aohSIRobBAsMlO//Kk4soosy1JSFRYWaLC4qZBYWFRGZdwqKiwkNBVmoWFSJkWFxX4FFRQWR+LsS4W/rFRb/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VEFHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU291bmRib3kuZGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjAwNGh0dHA6Ly93d3cuc291bmRib3kuZGUAAAAAAAAAACU=";
+
+function play(sound: string) {
+ var snd = new Audio(sound);
+ snd.play();
+}
+
+export const sfx = {
+ message: () => play(beep),
+ joinCall: () => play(joinCallSound),
+ leaveCall: () => play(leaveCallSound)
+}
\ No newline at end of file
diff --git a/packages/renderer/src/pages/Channel.tsx b/packages/renderer/src/pages/Channel.tsx
index 10df928..fd578d8 100644
--- a/packages/renderer/src/pages/Channel.tsx
+++ b/packages/renderer/src/pages/Channel.tsx
@@ -8,6 +8,7 @@ import { useApi } from "../lib/useApi";
import { useContext, useEffect, useState } from "react";
import { VoiceChannelContext } from "../contexts/EphemeralState/VoiceChannelState";
import { ClientsListContext } from "../contexts/EphemeralState/ClientsListState";
+import { sfx } from "../lib/sound";
interface ChannelProps {
unread: number;
@@ -16,6 +17,12 @@ interface ChannelProps {
type: ChannelType;
}
+interface Participant {
+ peerId: string;
+ channelId: string;
+ clientId: string;
+}
+
export default function Channel(props: ChannelProps) {
const { clientName } = useContext(ClientsListContext);
const { channel, setChannel } = useChannel();
@@ -24,7 +31,7 @@ export default function Channel(props: ChannelProps) {
const selected = channel === uid;
const { voiceChannelId } = useContext(VoiceChannelContext);
- const [participants, setParticipants] = useState([]);
+ const [participants, setParticipants] = useState([]);
const { send } = useApi({
'voice:join'(data: any) {
@@ -34,14 +41,20 @@ export default function Channel(props: ChannelProps) {
peerId: data.peerId,
channelId: data.channelId
}])
- console.log('JOIN', data);
+ sfx.joinCall();
},
'voice:list'(data: any) {
if(type !== 'voice') return;
- console.log('CURRENTS', data);
+ if(uid !== data.uid) return;
+ setParticipants(data.participants);
},
'voice:leave'(data: any) {
- console.log(data);
+ sfx.leaveCall();
+ setParticipants(participants => participants.filter(p => (
+ p.channelId !== data.channelId ||
+ p.clientId !== data.clientId ||
+ p.peerId !== data.peerId
+ )));
},
}, [uid, type, participants])
@@ -53,84 +66,91 @@ export default function Channel(props: ChannelProps) {
}, [uid]);
return (
- {
- setChannel(uid, type);
- }}
- ref={ref}
- >
- {(type === 'text') ? (
-
- ) : ((type === 'voice') ? (
-
+ {
+ setChannel(uid, type);
+ }}
+ ref={ref}
+ >
+ {(type === 'text') ? (
+
+ ) : ((type === 'voice') ? (
+
+ ) : (
+
+ ))}
+
- ) : (
-
- ))}
-
- {name.toLowerCase().replaceAll(' ', '-').trim()}
-
- {/* {(unread > 0) && (
-
({unread})
- )}
-
0 ? 'bold' : '300',
- }}>
- {name}
-
-
{}}>Delete */}
-
- {participants.map(participant => (
-
- {clientName[participant.clientId]}
+ }}>
+ {name.toLowerCase().replaceAll(' ', '-').trim()}
- ))}
-
+ {/* {(unread > 0) && (
+
({unread})
+ )}
+
0 ? 'bold' : '300',
+ }}>
+ {name}
+
+
{}}>Delete */}
+
+
+
+ {participants.map(participant => (
+
+ {clientName[participant.clientId]}
+
+ ))}
+
+ >
)
}
\ No newline at end of file
diff --git a/packages/renderer/src/pages/Channels.tsx b/packages/renderer/src/pages/Channels.tsx
index efc5dea..b187eec 100644
--- a/packages/renderer/src/pages/Channels.tsx
+++ b/packages/renderer/src/pages/Channels.tsx
@@ -11,6 +11,7 @@ import useClientId from '../hooks/useClientId';
import useHomeServer from '../contexts/PersistentState/useHomeServerNative';
import Channel from './Channel';
import { ChannelType } from '../contexts/EphemeralState/EphemeralState';
+import { sfx } from '../lib/sound';
interface IChannel {
uid: string;
@@ -38,7 +39,9 @@ export default function Channels() {
setChannels([...channels, channel]);
},
'message:message'(message: IMessage) {
+ sfx.message();
if(channel === message.channel) return;
+
setUnreads({
...unreads,
[message.channel]: (unreads[message.channel] ?? 0) + 1,
diff --git a/packages/server/src/lib/WebSocketServer.ts b/packages/server/src/lib/WebSocketServer.ts
index b858abd..8e369c9 100644
--- a/packages/server/src/lib/WebSocketServer.ts
+++ b/packages/server/src/lib/WebSocketServer.ts
@@ -1,4 +1,4 @@
-import { WebSocketServer } from 'ws';
+import { WebSocketServer, WebSocket } from 'ws';
import { inspect } from 'util';
import { validateSessionToken } from '../routers/session';
@@ -30,7 +30,7 @@ export function expose(router: Function, port: number) {
throw new Error('action ' + action + ' payload not an object');
}
console.log('[IN]', action, data);
- const _return = await (router(action, data) as unknown as Promise);
+ const _return = await (router(action, data, ws, wss) as unknown as Promise);
if(_return) {
try {
switch(_return.type) {
@@ -71,8 +71,17 @@ enum ResponseType {
REPLY
}
-function send(client: any, action: string, data?: any) {
- client.send(JSON.stringify({action, data}));
+// var snd = new Audio("data:audio/wav;base64,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qJrx4KOQjahZxWbcZgztj2c49nKmkId44S71j0c8eV9yDK6uPRzx5X18eDvjvQ6yKo9ZSS6l//8elePK/Lf//IInrOF/FvDoADYAGBMGb7FtErm5MXMlmPAJQVgWta7Zx2go+8xJ0UiCb8LHHdftWyLJE0QIAIsI+UbXu67dZMjmgDGCGl1H+vpF4NSDckSIkk7Vd+sxEhBQMRU8j/12UIRhzSaUdQ+rQU5kGeFxm+hb1oh6pWWmv3uvmReDl0UnvtapVaIzo1jZbf/pD6ElLqSX+rUmOQNpJFa/r+sa4e/pBlAABoAAAAA3CUgShLdGIxsY7AUABPRrgCABdDuQ5GC7DqPQCgbbJUAoRSUj+NIEig0YfyWUho1VBBBA//uQZB4ABZx5zfMakeAAAAmwAAAAF5F3P0w9GtAAACfAAAAAwLhMDmAYWMgVEG1U0FIGCBgXBXAtfMH10000EEEEEECUBYln03TTTdNBDZopopYvrTTdNa325mImNg3TTPV9q3pmY0xoO6bv3r00y+IDGid/9aaaZTGMuj9mpu9Mpio1dXrr5HERTZSmqU36A3CumzN/9Robv/Xx4v9ijkSRSNLQhAWumap82WRSBUqXStV/YcS+XVLnSS+WLDroqArFkMEsAS+eWmrUzrO0oEmE40RlMZ5+ODIkAyKAGUwZ3mVKmcamcJnMW26MRPgUw6j+LkhyHGVGYjSUUKNpuJUQoOIAyDvEyG8S5yfK6dhZc0Tx1KI/gviKL6qvvFs1+bWtaz58uUNnryq6kt5RzOCkPWlVqVX2a/EEBUdU1KrXLf40GoiiFXK///qpoiDXrOgqDR38JB0bw7SoL+ZB9o1RCkQjQ2CBYZKd/+VJxZRRZlqSkKiws0WFxUyCwsKiMy7hUVFhIaCrNQsKkTIsLivwKKigsj8XYlwt/WKi2N4d//uQRCSAAjURNIHpMZBGYiaQPSYyAAABLAAAAAAAACWAAAAApUF/Mg+0aohSIRobBAsMlO//Kk4soosy1JSFRYWaLC4qZBYWFRGZdwqKiwkNBVmoWFSJkWFxX4FFRQWR+LsS4W/rFRb/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VEFHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU291bmRib3kuZGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjAwNGh0dHA6Ly93d3cuc291bmRib3kuZGUAAAAAAAAAACU=");
+// snd.play();
+
+export function send(client: WebSocket | Set, action: string, data?: any) {
+ if(client instanceof Set) {
+ for(const c of client) {
+ c.send(JSON.stringify({action, data}));
+ }
+ } else {
+ client.send(JSON.stringify({action, data}));
+ }
}
export function broadcast(data: any) {
diff --git a/packages/server/src/lib/router.ts b/packages/server/src/lib/router.ts
index 416e602..9678e4f 100644
--- a/packages/server/src/lib/router.ts
+++ b/packages/server/src/lib/router.ts
@@ -1,14 +1,12 @@
-
-
export default function router(routes: any) {
for(const routeName in routes) {
const route = routes[routeName];
if('routes' in route) {
for(const suffix of route.routes) {
const combinedRouteName = routeName + ':' + suffix;
- routes[combinedRouteName] = function(data: any) {
+ routes[combinedRouteName] = function(...args: any[]) {
// console.log(suffix, route, data)
- return route(suffix, data);
+ return route(suffix, ...args);
// console.log('INCOMMING', args)
};
}
@@ -16,9 +14,9 @@ export default function router(routes: any) {
}
}
- const sendFn = function(route: any, data: any) {
+ const sendFn = function(route: any, ...args: any[]) {
if(route in routes) {
- return routes[route](data);
+ return routes[route](...args);
} else {
console.warn(`route <${route}> not found`);
console.trace();
diff --git a/packages/server/src/routers/channel.ts b/packages/server/src/routers/channel.ts
index 728f107..907bf6b 100644
--- a/packages/server/src/routers/channel.ts
+++ b/packages/server/src/routers/channel.ts
@@ -10,6 +10,11 @@ export const mockVoiceChannels = [
uid: v4(),
name: 'Voice Test',
type: 'voice'
+ },
+ {
+ uid: v4(),
+ name: 'Voice Test 2',
+ type: 'voice'
}
]
diff --git a/packages/server/src/routers/voice.ts b/packages/server/src/routers/voice.ts
index c89cc3a..8e3a0fc 100644
--- a/packages/server/src/routers/voice.ts
+++ b/packages/server/src/routers/voice.ts
@@ -1,5 +1,6 @@
+import WebSocket from "ws";
import router from "../lib/router";
-import { broadcast, reply } from "../lib/WebSocketServer";
+import { broadcast, reply, send } from "../lib/WebSocketServer";
function filterInPlace(a: T[], condition: (v: T, i: number, a: T[]) => boolean) {
let i = 0, j = 0;
@@ -27,7 +28,7 @@ interface ClientChannelRelationship {
const participants: ClientChannelRelationship[] = [];
export default router({
- async join(data: any) {
+ async join(data: any, ws: WebSocket.WebSocket, wss: WebSocket.Server) {
const { $clientId, channelId, peerId } = data;
// TODO validate channel exists
if(participants
@@ -44,12 +45,17 @@ export default router({
participants.push(user_channel);
+ ws.on('close', () => {
+ filterInPlace(participants, (p => p !== user_channel));
+ send(wss.clients, 'voice:leave', user_channel)
+ })
+
return broadcast(user_channel)
},
async list(data: any) {
- const { uid } = data;
+ const uid = data.uid ?? data.channelId
return reply({
- uid,
+ uid: uid,
participants: participants.filter(v => uid === v.channelId)
});
},