diff --git a/src/components/Party.js b/src/components/Party.js
new file mode 100644
index 0000000..0ec7e87
--- /dev/null
+++ b/src/components/Party.js
@@ -0,0 +1,17 @@
+import React from "react";
+
+import { Flex } from "theme-ui";
+
+import PartyVideo from "./PartyVideo";
+
+function Party({ streams }) {
+ return (
+
+ {Object.entries(streams).map(([id, stream]) => (
+
+ ))}
+
+ );
+}
+
+export default Party;
diff --git a/src/components/PartyVideo.js b/src/components/PartyVideo.js
new file mode 100644
index 0000000..5c8ca1a
--- /dev/null
+++ b/src/components/PartyVideo.js
@@ -0,0 +1,15 @@
+import React, { useRef, useEffect } from "react";
+
+function PartyVideo({ stream }) {
+ const videoRef = useRef();
+
+ useEffect(() => {
+ if (videoRef.current) {
+ videoRef.current.srcObject = stream;
+ }
+ }, [stream]);
+
+ return ;
+}
+
+export default PartyVideo;
diff --git a/src/components/Token.js b/src/components/Token.js
index 1a6815d..294cd2d 100644
--- a/src/components/Token.js
+++ b/src/components/Token.js
@@ -1,6 +1,5 @@
import React from "react";
import Draggable from "react-draggable";
-import { Styled } from "theme-ui";
function Token({ onDrag, position }) {
return (
diff --git a/src/contexts/GameContext.js b/src/contexts/GameContext.js
index 0dc8548..2579bb0 100644
--- a/src/contexts/GameContext.js
+++ b/src/contexts/GameContext.js
@@ -4,7 +4,7 @@ const GameContext = React.createContext();
export function GameProvider({ children }) {
const [gameId, setGameId] = useState(null);
- const value = [gameId, setGameId];
+ const value = { gameId, setGameId };
return {children};
}
diff --git a/src/helpers/useSession.js b/src/helpers/useSession.js
index 113b846..170a74b 100644
--- a/src/helpers/useSession.js
+++ b/src/helpers/useSession.js
@@ -1,10 +1,17 @@
import { useEffect, useState } from "react";
import Peer from "peerjs";
+const getUserMedia =
+ navigator.getUserMedia ||
+ navigator.webkitGetUserMedia ||
+ navigator.mozGetUserMedia;
+
function useSession(onConnectionOpen, onConnectionSync) {
const [peerId, setPeerId] = useState(null);
const [peer, setPeer] = useState(null);
const [connections, setConnections] = useState({});
+ const [streams, setStreams] = useState({});
+
function addConnection(connection) {
setConnections(prevConnnections => ({
...prevConnnections,
@@ -12,13 +19,35 @@ function useSession(onConnectionOpen, onConnectionSync) {
}));
}
+ function addStream(stream, id) {
+ setStreams(prevStreams => ({
+ ...prevStreams,
+ [id]: stream
+ }));
+ }
+
useEffect(() => {
setPeer(new Peer());
}, []);
+ // Clean up stream on dismount
+ useEffect(() => {
+ return () => {
+ for (let stream of Object.values(streams)) {
+ for (let track of stream.getTracks()) {
+ track.stop();
+ }
+ }
+ };
+ }, [streams]);
+
useEffect(() => {
function handleOpen(id) {
setPeerId(id);
+
+ getUserMedia({ video: true, audio: true }, stream => {
+ addStream(stream, id);
+ });
}
function handleConnection(connection) {
@@ -34,7 +63,7 @@ function useSession(onConnectionOpen, onConnectionSync) {
}
}
- addConnection(connection);
+ addConnection(connection, false);
if (onConnectionOpen) {
onConnectionOpen(connection);
@@ -47,22 +76,30 @@ function useSession(onConnectionOpen, onConnectionSync) {
return rest;
});
}
-
connection.on("close", removeConnection);
connection.on("error", removeConnection);
}
+ function handleCall(call) {
+ call.answer(streams[peerId]);
+ call.on("stream", remoteStream => {
+ addStream(remoteStream, call.peer);
+ });
+ }
+
if (!peer) {
return;
}
peer.on("open", handleOpen);
peer.on("connection", handleConnection);
+ peer.on("call", handleCall);
return () => {
peer.removeListener("open", handleOpen);
peer.removeListener("connection", handleConnection);
+ peer.removeListener("call", handleCall);
};
- }, [peer, peerId, connections, onConnectionOpen]);
+ }, [peer, peerId, connections, onConnectionOpen, onConnectionSync, streams]);
function sync(connectionIds) {
for (let connectionId of connectionIds) {
@@ -72,7 +109,11 @@ function useSession(onConnectionOpen, onConnectionSync) {
const connection = peer.connect(connectionId, {
metadata: { sync: false }
});
- addConnection(connection);
+ addConnection(connection, false);
+ const call = peer.call(connectionId, streams[peerId]);
+ call.on("stream", stream => {
+ addStream(stream, connectionId);
+ });
}
}
@@ -93,10 +134,18 @@ function useSession(onConnectionOpen, onConnectionSync) {
onConnectionOpen(connection);
}
});
- addConnection(connection);
+
+ console.log(streams);
+
+ const call = peer.call(connectionId, streams[peerId]);
+ call.on("stream", remoteStream => {
+ addStream(remoteStream, connectionId);
+ });
+
+ addConnection(connection, false);
}
- return [peer, peerId, connections, connectTo];
+ return { peer, peerId, streams, connections, connectTo };
}
export default useSession;
diff --git a/src/routes/Game.js b/src/routes/Game.js
index 0a36eda..07a4c8f 100644
--- a/src/routes/Game.js
+++ b/src/routes/Game.js
@@ -11,21 +11,22 @@ import GameContext from "../contexts/GameContext";
import useSession from "../helpers/useSession";
import Token from "../components/Token";
+import Party from "../components/Party";
function Game() {
- const [gameId, setGameId] = useContext(GameContext);
+ const { gameId } = useContext(GameContext);
const handleConnectionOpenCallback = useCallback(handleConnectionOpen);
const handleConnectionSyncCallback = useCallback(handleConnectionSync);
- const [peer, peerId, connections, connectTo] = useSession(
+ const { peerId, connections, connectTo, streams } = useSession(
handleConnectionOpenCallback,
handleConnectionSyncCallback
);
useEffect(() => {
- if (gameId !== null && peerId !== null) {
+ if (gameId !== null && peerId !== null && streams[peerId]) {
connectTo(gameId);
}
- }, [gameId, peerId, connectTo]);
+ }, [gameId, peerId, connectTo, streams]);
const [imageSource, setImageSource] = useState(null);
const imageDataRef = useRef(null);
@@ -34,7 +35,7 @@ function Game() {
imageDataRef.current = event.target.files[0];
setImageSource(URL.createObjectURL(imageDataRef.current));
for (let connection of Object.values(connections)) {
- connection.send({ id: "image", data: imageDataRef.current });
+ connection.data.send({ id: "image", data: imageDataRef.current });
}
}
@@ -82,6 +83,7 @@ function Game() {
/>
+
diff --git a/src/routes/Home.js b/src/routes/Home.js
index f285058..808a8fd 100644
--- a/src/routes/Home.js
+++ b/src/routes/Home.js
@@ -5,7 +5,7 @@ import { Container, Flex, Button } from "theme-ui";
import GameContext from "../contexts/GameContext";
function Home() {
- const [gameId, setGameId] = useContext(GameContext);
+ const { setGameId } = useContext(GameContext);
function handleStartGame() {
setGameId(null);
diff --git a/src/routes/Join.js b/src/routes/Join.js
index b0b1b82..c9f288f 100644
--- a/src/routes/Join.js
+++ b/src/routes/Join.js
@@ -5,7 +5,7 @@ import { Container, Box, Label, Input, Button } from "theme-ui";
import GameContext from "../contexts/GameContext";
function Join() {
- const [gameId, setGameId] = useContext(GameContext);
+ const { gameId, setGameId } = useContext(GameContext);
function handleChange(event) {
setGameId(event.target.value);