Signaling seems to be working better now; loads and player state changes persist existing connections.
Signed-off-by: James Ketrenos <james_eikona@ketrenos.com>
This commit is contained in:
parent
80fad00cc9
commit
67e319e2c5
@ -13,7 +13,6 @@ import { GlobalContext } from "./GlobalContext.js";
|
|||||||
//import { PingPong } from "./PingPong.js";
|
//import { PingPong } from "./PingPong.js";
|
||||||
import { PlayerList } from "./PlayerList.js";
|
import { PlayerList } from "./PlayerList.js";
|
||||||
import { Chat } from "./Chat.js";
|
import { Chat } from "./Chat.js";
|
||||||
import { MediaAgent } from "./MediaControl.js";
|
|
||||||
import { Board } from "./Board.js";
|
import { Board } from "./Board.js";
|
||||||
import { Actions } from "./Actions.js";
|
import { Actions } from "./Actions.js";
|
||||||
import { base, gamesPath } from './Common.js';
|
import { base, gamesPath } from './Common.js';
|
||||||
@ -40,7 +39,6 @@ const Table = () => {
|
|||||||
const [ name, setName ] = useState("");
|
const [ name, setName ] = useState("");
|
||||||
const [ error, setError ] = useState(undefined);
|
const [ error, setError ] = useState(undefined);
|
||||||
const [ warning, setWarning ] = useState(undefined);
|
const [ warning, setWarning ] = useState(undefined);
|
||||||
const [ peers, setPeers ] = useState({});
|
|
||||||
const [loaded, setLoaded] = useState(false);
|
const [loaded, setLoaded] = useState(false);
|
||||||
|
|
||||||
const [state, setState] = useState(undefined);
|
const [state, setState] = useState(undefined);
|
||||||
@ -49,14 +47,10 @@ const Table = () => {
|
|||||||
const [buildActive, setBuildActive] = useState(false);
|
const [buildActive, setBuildActive] = useState(false);
|
||||||
const [cardActive, setCardActive] = useState(undefined);
|
const [cardActive, setCardActive] = useState(undefined);
|
||||||
const [winnerDismissed, setWinnerDismissed] = useState(undefined);
|
const [winnerDismissed, setWinnerDismissed] = useState(undefined);
|
||||||
const [global, setGlobal] = useState({ setPeers });
|
const [global, setGlobal] = useState({});
|
||||||
const [count, setCount] = useState(0);
|
const [count, setCount] = useState(0);
|
||||||
const fields = [ 'id', 'state', 'color', 'name', 'private' ];
|
const fields = [ 'id', 'state', 'color', 'name', 'private' ];
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
console.log(`app - media-agent - peers`, peers);
|
|
||||||
}, [peers]);
|
|
||||||
|
|
||||||
const onWsOpen = (event) => {
|
const onWsOpen = (event) => {
|
||||||
console.log(`ws: open`);
|
console.log(`ws: open`);
|
||||||
setError("");
|
setError("");
|
||||||
@ -163,14 +157,12 @@ const Table = () => {
|
|||||||
|
|
||||||
if (global.ws !== connection
|
if (global.ws !== connection
|
||||||
|| global.name !== name
|
|| global.name !== name
|
||||||
|| global.gameId !== gameId
|
|| global.gameId !== gameId) {
|
||||||
|| global.peers !== peers) {
|
|
||||||
console.log(`board - (app) - setting global`, global,
|
console.log(`board - (app) - setting global`, global,
|
||||||
{connection, name, gameId, peers});
|
{connection, name, gameId});
|
||||||
setGlobal({
|
setGlobal({
|
||||||
ws: connection,
|
ws: connection,
|
||||||
name, gameId, peers,
|
name, gameId
|
||||||
setPeers
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,7 +305,6 @@ const Table = () => {
|
|||||||
console.log(`board - (app) - Render with ws: ${ws ? '!' : ''}NULL, connection: ${connection ? '!' : ''}NULL`);
|
console.log(`board - (app) - Render with ws: ${ws ? '!' : ''}NULL, connection: ${connection ? '!' : ''}NULL`);
|
||||||
|
|
||||||
return <GlobalContext.Provider value={global}>
|
return <GlobalContext.Provider value={global}>
|
||||||
<MediaAgent/>
|
|
||||||
{ /* <PingPong/> */ }
|
{ /* <PingPong/> */ }
|
||||||
<div className="Table">
|
<div className="Table">
|
||||||
<Activities/>
|
<Activities/>
|
||||||
|
@ -20,13 +20,13 @@ const Video = ({ srcObject, local, ...props }) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const ref = refVideo.current;
|
const ref = refVideo.current;
|
||||||
if (debug) console.log('<video> bind');
|
if (debug) console.log('media-controls <video> bind');
|
||||||
ref.srcObject = srcObject;
|
ref.srcObject = srcObject;
|
||||||
if (local) {
|
if (local) {
|
||||||
ref.muted = true;
|
ref.muted = true;
|
||||||
}
|
}
|
||||||
return () => {
|
return () => {
|
||||||
if (debug) console.log('<video> unbind');
|
if (debug) console.log('media-controls <video> unbind');
|
||||||
if (ref) {
|
if (ref) {
|
||||||
ref.srcObject = undefined;
|
ref.srcObject = undefined;
|
||||||
}
|
}
|
||||||
@ -35,26 +35,23 @@ const Video = ({ srcObject, local, ...props }) => {
|
|||||||
return <video ref={refVideo} {...props} />;
|
return <video ref={refVideo} {...props} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MediaAgent = () => {
|
const MediaAgent = ({setPeers}) => {
|
||||||
const { name, ws, peers, setPeers } = useContext(GlobalContext);
|
const { name, ws } = useContext(GlobalContext);
|
||||||
|
const [ peers ] = useState({});
|
||||||
const [stream, setStream] = useState(undefined);
|
const [stream, setStream] = useState(undefined);
|
||||||
|
|
||||||
const onTrack = useCallback((event) => {
|
const onTrack = useCallback((event) => {
|
||||||
const connection = event.target;
|
const connection = event.target;
|
||||||
|
console.log("media-agent - ontrack", event);
|
||||||
if (debug) console.log("ontrack", event);
|
for (let peer in peers) {
|
||||||
for (let key in peers) {
|
if (peers[peer].connection === connection) {
|
||||||
if (peers[key].connection === connection) {
|
console.log(`media-agent - ontrack - remote ${peer} stream assigned.`);
|
||||||
Object.assign(peers[key].attributes, {
|
Object.assign(peers[peer].attributes, {
|
||||||
srcObject: event.streams[0]
|
srcObject: event.streams[0]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}, [peers]);
|
||||||
if (debug) console.log(`media-agent - ontrack - remote`, peers);
|
|
||||||
setPeers(Object.assign({}, peers));
|
|
||||||
}, [peers, setPeers]);
|
|
||||||
const refOnTrack = useRef(onTrack);
|
const refOnTrack = useRef(onTrack);
|
||||||
|
|
||||||
const sendMessage = useCallback((data) => {
|
const sendMessage = useCallback((data) => {
|
||||||
@ -66,12 +63,13 @@ const MediaAgent = () => {
|
|||||||
console.log('media-agent - Signaling server said to add peer:', config);
|
console.log('media-agent - Signaling server said to add peer:', config);
|
||||||
|
|
||||||
if (!stream) {
|
if (!stream) {
|
||||||
console.log(`No local media stream`);
|
console.log(`media-agent - No local media stream`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const peer_id = config.peer_id;
|
const peer_id = config.peer_id;
|
||||||
if (peer_id in peers) {
|
if (peer_id in peers) {
|
||||||
|
/* This is normal */
|
||||||
console.log(`media-agent - addPeer - ${peer_id} already in peers`);
|
console.log(`media-agent - addPeer - ${peer_id} already in peers`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -92,16 +90,14 @@ const MediaAgent = () => {
|
|||||||
credential: "1!viagenie"
|
credential: "1!viagenie"
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
peers[peer_id] = {
|
peers[peer_id] = {
|
||||||
|
name: peer_id,
|
||||||
connection,
|
connection,
|
||||||
muted: false,
|
initialized: false,
|
||||||
videoOn: true,
|
attributes: {}
|
||||||
attributes: {
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
console.log(`media-agent - addPeer - remote`, peers);
|
console.log(`media-agent - addPeer - remote`, peers);
|
||||||
setPeers(Object.assign({}, peers));
|
setPeers(Object.assign({}, peers));
|
||||||
@ -113,7 +109,7 @@ const MediaAgent = () => {
|
|||||||
connection.addEventListener('icecandidateerror', (event) => {
|
connection.addEventListener('icecandidateerror', (event) => {
|
||||||
if (event.errorCode === 701) {
|
if (event.errorCode === 701) {
|
||||||
if (connection.icegatheringstate === 'gathering') {
|
if (connection.icegatheringstate === 'gathering') {
|
||||||
console.log(`Unable to reach host: ${event.url}`);
|
console.log(`media-agent - Unable to reach host: ${event.url}`);
|
||||||
} else {
|
} else {
|
||||||
console.error(`media-agent - icecandidateerror - `, event.errorCode, event.hostcandidate, event.url, event.errorText);
|
console.error(`media-agent - icecandidateerror - `, event.errorCode, event.hostcandidate, event.url, event.errorText);
|
||||||
}
|
}
|
||||||
@ -122,18 +118,18 @@ const MediaAgent = () => {
|
|||||||
|
|
||||||
connection.onicecandidate = (event) => {
|
connection.onicecandidate = (event) => {
|
||||||
if (!event.candidate) {
|
if (!event.candidate) {
|
||||||
console.error(`media-agent - icecanditate - gathering is complete: ${connection.connectionState}`);
|
console.log(`media-agent - icecanditate - gathering is complete: ${connection.connectionState}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* If a srflx candidate was found, notify that the STUN server works! */
|
/* If a srflx candidate was found, notify that the STUN server works! */
|
||||||
if (event.candidate.type === "srflx"){
|
if (event.candidate.type === "srflx"){
|
||||||
console.log("The STUN server is reachable!");
|
console.log("media-agent - The STUN server is reachable!");
|
||||||
console.log(` Your Public IP Address is: ${event.candidate.address}`);
|
console.log(`media-agent - Your Public IP Address is: ${event.candidate.address}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If a relay candidate was found, notify that the TURN server works! */
|
/* If a relay candidate was found, notify that the TURN server works! */
|
||||||
if (event.candidate.type === "relay"){
|
if (event.candidate.type === "relay"){
|
||||||
console.log("The TURN server is reachable !");
|
console.log("media-agent - The TURN server is reachable !");
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`media-agent - onicecandidate - `, event.candidate);
|
console.log(`media-agent - onicecandidate - `, event.candidate);
|
||||||
@ -147,7 +143,7 @@ const MediaAgent = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
connection.ontrack = e => refOnTrack.current(e);;
|
connection.ontrack = e => refOnTrack.current(e);
|
||||||
|
|
||||||
/* Add our local stream */
|
/* Add our local stream */
|
||||||
connection.addStream(stream);
|
connection.addStream(stream);
|
||||||
@ -267,7 +263,7 @@ const MediaAgent = () => {
|
|||||||
const refWsMessage = useRef(onWsMessage);
|
const refWsMessage = useRef(onWsMessage);
|
||||||
|
|
||||||
const onWsClose = (event) => {
|
const onWsClose = (event) => {
|
||||||
console.log(`${name} Disconnected from signaling server`);
|
console.log(`media-agent - ${name} Disconnected from signaling server`);
|
||||||
/* Tear down all of our peer connections and remove all the
|
/* Tear down all of our peer connections and remove all the
|
||||||
* media divs when we disconnect */
|
* media divs when we disconnect */
|
||||||
for (let peer_id in peers) {
|
for (let peer_id in peers) {
|
||||||
@ -280,7 +276,7 @@ const MediaAgent = () => {
|
|||||||
for (let id in peers) {
|
for (let id in peers) {
|
||||||
delete peers[id];
|
delete peers[id];
|
||||||
}
|
}
|
||||||
if (debug) console.log(`media-agent - close - local or remote?`, peers);
|
if (debug) console.log(`media-agent - close`, peers);
|
||||||
setPeers(Object.assign({}, peers));
|
setPeers(Object.assign({}, peers));
|
||||||
}
|
}
|
||||||
const refWsClose = useRef(onWsClose);
|
const refWsClose = useRef(onWsClose);
|
||||||
@ -314,9 +310,9 @@ const MediaAgent = () => {
|
|||||||
if (!(name in peers)) {
|
if (!(name in peers)) {
|
||||||
update = true;
|
update = true;
|
||||||
peers[name] = {
|
peers[name] = {
|
||||||
|
name: name,
|
||||||
local: true,
|
local: true,
|
||||||
muted: true,
|
initialized: false,
|
||||||
videoOn: false,
|
|
||||||
attributes: {
|
attributes: {
|
||||||
local: true,
|
local: true,
|
||||||
srcObject: stream
|
srcObject: stream
|
||||||
@ -331,8 +327,9 @@ const MediaAgent = () => {
|
|||||||
update = true;
|
update = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (update) {
|
if (update) {
|
||||||
if (debug) console.log(`media-agent - Adding local`, peers);
|
if (debug) console.log(`media-agent - Setting global peers`, peers);
|
||||||
setPeers(Object.assign({}, peers));
|
setPeers(Object.assign({}, peers));
|
||||||
}
|
}
|
||||||
}, [peers, name, setPeers, stream]);
|
}, [peers, name, setPeers, stream]);
|
||||||
@ -345,7 +342,7 @@ const MediaAgent = () => {
|
|||||||
const setup_local_media = () => {
|
const setup_local_media = () => {
|
||||||
/* Ask user for permission to use the computers microphone and/or camera,
|
/* Ask user for permission to use the computers microphone and/or camera,
|
||||||
* attach it to an <audio> or <video> tag if they give us access. */
|
* attach it to an <audio> or <video> tag if they give us access. */
|
||||||
console.log("Requesting access to local audio / video inputs");
|
console.log("media-agent - Requesting access to local audio / video inputs");
|
||||||
|
|
||||||
navigator.getUserMedia = (navigator.getUserMedia ||
|
navigator.getUserMedia = (navigator.getUserMedia ||
|
||||||
navigator.webkitGetUserMedia ||
|
navigator.webkitGetUserMedia ||
|
||||||
@ -354,7 +351,7 @@ const MediaAgent = () => {
|
|||||||
|
|
||||||
return navigator.mediaDevices.getUserMedia({audio: true, video: true})
|
return navigator.mediaDevices.getUserMedia({audio: true, video: true})
|
||||||
.then((media) => { /* user accepted access to a/v */
|
.then((media) => { /* user accepted access to a/v */
|
||||||
console.log("Access granted to audio/video");
|
console.log("media-agent - Access granted to audio/video");
|
||||||
media.getVideoTracks().forEach((track) => {
|
media.getVideoTracks().forEach((track) => {
|
||||||
track.applyConstraints({
|
track.applyConstraints({
|
||||||
"video": {
|
"video": {
|
||||||
@ -378,80 +375,88 @@ const MediaAgent = () => {
|
|||||||
sendMessage({ type: 'join' });
|
sendMessage({ type: 'join' });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debug) console.log(`media-agent - WebSocket open request. Attempting to create local media.`)
|
let abort = false;
|
||||||
if (!stream) {
|
if (!stream) {
|
||||||
|
if (debug) console.log(`media-agent - WebSocket open request. Attempting to create local media.`);
|
||||||
setup_local_media().then((media) => {
|
setup_local_media().then((media) => {
|
||||||
/* once the user has given us access to their
|
/* once the user has given us access to their
|
||||||
* microphone/camcorder, join the channel and start peering up */
|
* microphone/camcorder, join the channel and start peering up */
|
||||||
|
if (abort) {
|
||||||
|
console.log(`media-agent - aborting setting local media`);
|
||||||
|
} else {
|
||||||
setStream(media);
|
setStream(media);
|
||||||
join();
|
join();
|
||||||
|
}
|
||||||
}).catch((error) => { /* user denied access to a/v */
|
}).catch((error) => { /* user denied access to a/v */
|
||||||
console.error(error);
|
console.error(error);
|
||||||
console.log("Access denied for audio/video");
|
console.log("media-agent - Access denied for audio/video");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [ws, setStream, stream, peers, name, sendMessage]);
|
|
||||||
|
return () => {
|
||||||
|
abort = true;
|
||||||
|
console.log(`media-agent - abort media setup!`);
|
||||||
|
};
|
||||||
|
}, [ws, setStream, stream, name, sendMessage]);
|
||||||
|
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MediaControl = ({isSelf, peer}) => {
|
const MediaControl = ({isSelf, peer}) => {
|
||||||
const { peers } = useContext(GlobalContext);
|
|
||||||
const [control, setControl] = useState(undefined);
|
|
||||||
const [media, setMedia] = useState(undefined);
|
const [media, setMedia] = useState(undefined);
|
||||||
const [muted, setMuted] = useState(undefined);
|
const [muted, setMuted] = useState(undefined);
|
||||||
const [videoOn, setVideoOn] = useState(undefined);
|
const [videoOn, setVideoOn] = useState(undefined);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setControl(peers[peer]);
|
if (!peer) {
|
||||||
}, [peer, peers, setControl]);
|
setMedia(undefined);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (peer.local) {
|
||||||
|
setMuted(true);
|
||||||
|
setVideoOn(false);
|
||||||
|
} else {
|
||||||
|
setMuted(false);
|
||||||
|
setVideoOn(true);
|
||||||
|
}
|
||||||
|
setMedia(peer);
|
||||||
|
}, [peer, setMedia, setMuted, setVideoOn]);
|
||||||
|
|
||||||
const toggleMute = (event) => {
|
const toggleMute = (event) => {
|
||||||
if (debug) console.log(`MediaControl - toggleMute`, !muted);
|
if (debug) console.log(`MediaControl - toggleMute - ${peer.name}`, !muted);
|
||||||
setMuted(!muted);
|
setMuted(!muted);
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const toggleVideo = (event) => {
|
const toggleVideo = (event) => {
|
||||||
if (debug) console.log(`MediaControl - toggleVideo`, !videoOn);
|
if (debug) console.log(`MediaControl - toggleVideo - ${peer.name}`, !videoOn);
|
||||||
setVideoOn(!videoOn);
|
setVideoOn(!videoOn);
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!control) {
|
if (!media) {
|
||||||
setMedia(undefined);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setMuted(control.muted);
|
if (media.attributes.srcObject) {
|
||||||
setVideoOn(control.videoOn);
|
media.attributes.srcObject.getAudioTracks().forEach((track) => {
|
||||||
setMedia(control);
|
|
||||||
}, [control, setMedia]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!control) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (control.attributes.srcObject) {
|
|
||||||
control.attributes.srcObject.getAudioTracks().forEach((track) => {
|
|
||||||
track.enabled = !muted;
|
track.enabled = !muted;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [control, muted]);
|
}, [media, muted]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!control) {
|
if (!media) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (control.attributes.srcObject) {
|
if (media.attributes.srcObject) {
|
||||||
control.attributes.srcObject.getVideoTracks().forEach((track) => {
|
media.attributes.srcObject.getVideoTracks().forEach((track) => {
|
||||||
track.enabled = videoOn;
|
track.enabled = videoOn;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [control, videoOn]);
|
}, [media, videoOn]);
|
||||||
|
|
||||||
if (!control) {
|
if (!media) {
|
||||||
return <div className="MediaControl">
|
return <div className="MediaControl">
|
||||||
<div>
|
<div>
|
||||||
{ isSelf && <MicOff color={'disabled'}/> }
|
{ isSelf && <MicOff color={'disabled'}/> }
|
||||||
|
@ -4,7 +4,8 @@ import List from '@material-ui/core/List';
|
|||||||
|
|
||||||
import "./PlayerList.css";
|
import "./PlayerList.css";
|
||||||
import { PlayerColor } from './PlayerColor.js';
|
import { PlayerColor } from './PlayerColor.js';
|
||||||
import { MediaControl } from "./MediaControl.js";
|
import { MediaAgent, MediaControl } from "./MediaControl.js";
|
||||||
|
|
||||||
import { GlobalContext } from "./GlobalContext.js";
|
import { GlobalContext } from "./GlobalContext.js";
|
||||||
|
|
||||||
const PlayerList = () => {
|
const PlayerList = () => {
|
||||||
@ -13,6 +14,7 @@ const PlayerList = () => {
|
|||||||
const [unselected, setUneslected] = useState([]);
|
const [unselected, setUneslected] = useState([]);
|
||||||
const [state, setState] = useState('lobby');
|
const [state, setState] = useState('lobby');
|
||||||
const [color, setColor] = useState(undefined);
|
const [color, setColor] = useState(undefined);
|
||||||
|
const [peers, setPeers] = useState({});
|
||||||
|
|
||||||
const onWsMessage = (event) => {
|
const onWsMessage = (event) => {
|
||||||
const data = JSON.parse(event.data);
|
const data = JSON.parse(event.data);
|
||||||
@ -113,7 +115,7 @@ const PlayerList = () => {
|
|||||||
<div className="Name">{name ? name : 'Available' }</div>
|
<div className="Name">{name ? name : 'Available' }</div>
|
||||||
{ name && !item.live && <div className="NoNetwork"></div> }
|
{ name && !item.live && <div className="NoNetwork"></div> }
|
||||||
</div>
|
</div>
|
||||||
{ name && item.live && <MediaControl peer={name} isSelf={item.color === color}/> }
|
{ name && item.live && <MediaControl peer={peers[name]} isSelf={item.color === color}/> }
|
||||||
{ !name && <div></div> }
|
{ !name && <div></div> }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -122,12 +124,13 @@ const PlayerList = () => {
|
|||||||
const waiting = unselected.map((player) => {
|
const waiting = unselected.map((player) => {
|
||||||
return <div className={player === name ? 'Self' : ''} key={player}>
|
return <div className={player === name ? 'Self' : ''} key={player}>
|
||||||
<div>{ player }</div>
|
<div>{ player }</div>
|
||||||
<MediaControl peer={player} isSelf={name === player}/>
|
<MediaControl peer={peers[name]} isSelf={name === player}/>
|
||||||
</div>
|
</div>
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper className="PlayerList">
|
<Paper className="PlayerList">
|
||||||
|
<MediaAgent setPeers={setPeers}/>
|
||||||
<List className="PlayerSelector">
|
<List className="PlayerSelector">
|
||||||
{ playerElements }
|
{ playerElements }
|
||||||
</List>
|
</List>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user