i deadass just dont care

main
Valerie 2022-08-11 14:29:24 -04:00
parent 1aead4cc6b
commit b73ab2c691
15 changed files with 219 additions and 122 deletions

Binary file not shown.

Binary file not shown.

View File

@ -6,7 +6,7 @@
<meta http-equiv="Content-Security-Policy" content="
default-src 'self' data: https://ssl.gstatic.com https://fonts.gstatic.com;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
media-src *;
media-src * data:;
img-src 'self' data: content: http://tinygraphs.com;
connect-src *;">
<meta content="width=device-width, initial-scale=1.0" name="viewport">

View File

@ -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 <ServerConnectionContext.Provider value={serverConnection}>
<ClientsListState>
{props.children}
<PeerState>
{props.children}
</PeerState>
</ClientsListState>
</ServerConnectionContext.Provider>
}

View File

@ -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 <div style={{
width: '100%',
@ -32,6 +36,7 @@ export default function Voice(props: any) {
<fieldset>
<legend>Actions</legend>
<button onClick={joinCall}>Join Call</button>
<button onClick={leaveCall}>Leave Call</button>
</fieldset>
</div>
}

View File

@ -29,10 +29,6 @@ export default function ClientsListState(props: any) {
}
});
useEffect(() => {
console.log(clients);
}, [clients]);
useEffect(() => {
send('clients:list')
}, []);

View File

@ -82,9 +82,7 @@ export default function EphemeralState(props: {
isSettingsOpen: settings,
}}>
<UserMediaState>
<PeerState>
{props.children}
</PeerState>
{props.children}
</UserMediaState>
</SettingsContext.Provider>
</TransparencyContext.Provider>

View File

@ -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<T>(thing: T) {
@ -20,6 +27,13 @@ function useCurrent<T>(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<Peer | null>(null);
const [peerId, setPeerId] = useState<string | null>(null);
const [incomingCalls, setIncomingCalls] = useState<MediaConnection[]>([]);
const [connections, setConnections] = useState<Connection[]>([]);
const [channel, setChannel] = useState<string | null>(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 <PeerContext.Provider value={value}>
{props.children}

View File

@ -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)
}

View File

@ -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<any[]>([]);
const [participants, setParticipants] = useState<Participant[]>([]);
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 (
<div
style={{
margin: '2px 2px',
display: 'grid',
gridTemplateColumns: 'min-content 1fr',
cursor: 'pointer',
background: selected ? 'var(--neutral-5)' :
hover ? 'var(--neutral-4)' :
'inherit',
borderRadius: '8px',
transform:'skew(-20deg, 0deg)',
transition: 'background 300ms, color 300ms',
}}
onClick={() => {
setChannel(uid, type);
}}
ref={ref}
>
{(type === 'text') ? (
<CgHashtag color={
selected ? 'var(--neutral-9)' :
hover ? 'var(--neutral-7)' :
'var(--neutral-7)'
} size={24} style={{
margin: '4px',
transition: 'background 300ms, color 300ms',
transform:'skew(-5deg, 0deg)',
}}></CgHashtag>
) : ((type === 'voice') ? (
<MdVolumeUp color={
selected ? 'var(--neutral-9)' :
hover ? 'var(--neutral-7)' :
'var(--neutral-7)'
} size={24} style={{
margin: '4px',
<>
<div
style={{
margin: '2px 2px',
display: 'grid',
gridTemplateColumns: 'min-content 1fr',
cursor: 'pointer',
background: selected ? 'var(--neutral-5)' :
hover ? 'var(--neutral-4)' :
'inherit',
borderRadius: '8px',
transform:'skew(-20deg, 0deg)',
transition: 'background 300ms, color 300ms',
}}
onClick={() => {
setChannel(uid, type);
}}
ref={ref}
>
{(type === 'text') ? (
<CgHashtag color={
selected ? 'var(--neutral-9)' :
hover ? 'var(--neutral-7)' :
'var(--neutral-7)'
} size={24} style={{
margin: '4px',
transition: 'background 300ms, color 300ms',
transform:'skew(-5deg, 0deg)',
}}></CgHashtag>
) : ((type === 'voice') ? (
<MdVolumeUp color={
selected ? 'var(--neutral-9)' :
hover ? 'var(--neutral-7)' :
'var(--neutral-7)'
} size={24} style={{
margin: '4px',
transition: 'background 300ms, color 300ms',
transform:'skew(20deg, 0deg)',
}}></MdVolumeUp>
) : (
<BsQuestionLg color={
selected ? 'var(--neutral-9)' :
hover ? 'var(--neutral-7)' :
'var(--neutral-7)'
} size={24} style={{
margin: '4px',
transition: 'background 300ms, color 300ms',
transform:'skew(20deg, 0deg)',
}}></BsQuestionLg>
))}
<div style={{
lineHeight: '32px',
color: selected ? 'var(--neutral-9)' :
hover ? 'var(--neutral-9)' :
'var(--neutral-7)',
transform:'skew(20deg, 0deg)',
}}></MdVolumeUp>
) : (
<BsQuestionLg color={
selected ? 'var(--neutral-9)' :
hover ? 'var(--neutral-7)' :
'var(--neutral-7)'
} size={24} style={{
margin: '4px',
transition: 'background 300ms, color 300ms',
transform:'skew(20deg, 0deg)',
}}></BsQuestionLg>
))}
<div style={{
lineHeight: '32px',
color: selected ? 'var(--neutral-9)' :
hover ? 'var(--neutral-9)' :
'var(--neutral-7)',
transform:'skew(20deg, 0deg)',
transition: 'background 300ms, color 300ms',
}}>
{name.toLowerCase().replaceAll(' ', '-').trim()}
</div>
{/* {(unread > 0) && (
<span style={{ paddingRight: '8px' }}>({unread})</span>
)}
<span style={{
fontWeight: (unread ?? 0) > 0 ? 'bold' : '300',
}}>
{name}
</span>
<a style={{
color: 'rgba(0, 100, 200, 1)',
marginLeft: '8px',
fontSize: '10px',
}} href="#" onClick={() => {}}>Delete</a> */}
<br></br>
{participants.map(participant => (
<div key={participant.clientId}>
{clientName[participant.clientId]}
}}>
{name.toLowerCase().replaceAll(' ', '-').trim()}
</div>
))}
</div>
{/* {(unread > 0) && (
<span style={{ paddingRight: '8px' }}>({unread})</span>
)}
<span style={{
fontWeight: (unread ?? 0) > 0 ? 'bold' : '300',
}}>
{name}
</span>
<a style={{
color: 'rgba(0, 100, 200, 1)',
marginLeft: '8px',
fontSize: '10px',
}} href="#" onClick={() => {}}>Delete</a> */}
</div>
<div style={{
// outline: '1px solid white',
paddingLeft: '32px',
}}>
{participants.map(participant => (
<div key={participant.clientId}>
{clientName[participant.clientId]}
</div>
))}
</div>
</>
)
}

View File

@ -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,

View File

@ -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<any>);
const _return = await (router(action, data, ws, wss) as unknown as Promise<any>);
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<WebSocket>, 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) {

View File

@ -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();

View File

@ -10,6 +10,11 @@ export const mockVoiceChannels = [
uid: v4(),
name: 'Voice Test',
type: 'voice'
},
{
uid: v4(),
name: 'Voice Test 2',
type: 'voice'
}
]

View File

@ -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<T>(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)
});
},