Added video calling
This commit is contained in:
parent
25ee35de44
commit
fb43cc3fc2
17
src/components/Party.js
Normal file
17
src/components/Party.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { Flex } from "theme-ui";
|
||||||
|
|
||||||
|
import PartyVideo from "./PartyVideo";
|
||||||
|
|
||||||
|
function Party({ streams }) {
|
||||||
|
return (
|
||||||
|
<Flex p={4} bg="highlight" sx={{ flexDirection: "column", width: "200px" }}>
|
||||||
|
{Object.entries(streams).map(([id, stream]) => (
|
||||||
|
<PartyVideo key={id} stream={stream} />
|
||||||
|
))}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Party;
|
15
src/components/PartyVideo.js
Normal file
15
src/components/PartyVideo.js
Normal file
@ -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 <video ref={videoRef} autoPlay muted />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PartyVideo;
|
@ -1,6 +1,5 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Draggable from "react-draggable";
|
import Draggable from "react-draggable";
|
||||||
import { Styled } from "theme-ui";
|
|
||||||
|
|
||||||
function Token({ onDrag, position }) {
|
function Token({ onDrag, position }) {
|
||||||
return (
|
return (
|
||||||
|
@ -4,7 +4,7 @@ const GameContext = React.createContext();
|
|||||||
|
|
||||||
export function GameProvider({ children }) {
|
export function GameProvider({ children }) {
|
||||||
const [gameId, setGameId] = useState(null);
|
const [gameId, setGameId] = useState(null);
|
||||||
const value = [gameId, setGameId];
|
const value = { gameId, setGameId };
|
||||||
return <GameContext.Provider value={value}>{children}</GameContext.Provider>;
|
return <GameContext.Provider value={value}>{children}</GameContext.Provider>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,17 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import Peer from "peerjs";
|
import Peer from "peerjs";
|
||||||
|
|
||||||
|
const getUserMedia =
|
||||||
|
navigator.getUserMedia ||
|
||||||
|
navigator.webkitGetUserMedia ||
|
||||||
|
navigator.mozGetUserMedia;
|
||||||
|
|
||||||
function useSession(onConnectionOpen, onConnectionSync) {
|
function useSession(onConnectionOpen, onConnectionSync) {
|
||||||
const [peerId, setPeerId] = useState(null);
|
const [peerId, setPeerId] = useState(null);
|
||||||
const [peer, setPeer] = useState(null);
|
const [peer, setPeer] = useState(null);
|
||||||
const [connections, setConnections] = useState({});
|
const [connections, setConnections] = useState({});
|
||||||
|
const [streams, setStreams] = useState({});
|
||||||
|
|
||||||
function addConnection(connection) {
|
function addConnection(connection) {
|
||||||
setConnections(prevConnnections => ({
|
setConnections(prevConnnections => ({
|
||||||
...prevConnnections,
|
...prevConnnections,
|
||||||
@ -12,13 +19,35 @@ function useSession(onConnectionOpen, onConnectionSync) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addStream(stream, id) {
|
||||||
|
setStreams(prevStreams => ({
|
||||||
|
...prevStreams,
|
||||||
|
[id]: stream
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setPeer(new Peer());
|
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(() => {
|
useEffect(() => {
|
||||||
function handleOpen(id) {
|
function handleOpen(id) {
|
||||||
setPeerId(id);
|
setPeerId(id);
|
||||||
|
|
||||||
|
getUserMedia({ video: true, audio: true }, stream => {
|
||||||
|
addStream(stream, id);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleConnection(connection) {
|
function handleConnection(connection) {
|
||||||
@ -34,7 +63,7 @@ function useSession(onConnectionOpen, onConnectionSync) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addConnection(connection);
|
addConnection(connection, false);
|
||||||
|
|
||||||
if (onConnectionOpen) {
|
if (onConnectionOpen) {
|
||||||
onConnectionOpen(connection);
|
onConnectionOpen(connection);
|
||||||
@ -47,22 +76,30 @@ function useSession(onConnectionOpen, onConnectionSync) {
|
|||||||
return rest;
|
return rest;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
connection.on("close", removeConnection);
|
connection.on("close", removeConnection);
|
||||||
connection.on("error", removeConnection);
|
connection.on("error", removeConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleCall(call) {
|
||||||
|
call.answer(streams[peerId]);
|
||||||
|
call.on("stream", remoteStream => {
|
||||||
|
addStream(remoteStream, call.peer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!peer) {
|
if (!peer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
peer.on("open", handleOpen);
|
peer.on("open", handleOpen);
|
||||||
peer.on("connection", handleConnection);
|
peer.on("connection", handleConnection);
|
||||||
|
peer.on("call", handleCall);
|
||||||
return () => {
|
return () => {
|
||||||
peer.removeListener("open", handleOpen);
|
peer.removeListener("open", handleOpen);
|
||||||
peer.removeListener("connection", handleConnection);
|
peer.removeListener("connection", handleConnection);
|
||||||
|
peer.removeListener("call", handleCall);
|
||||||
};
|
};
|
||||||
}, [peer, peerId, connections, onConnectionOpen]);
|
}, [peer, peerId, connections, onConnectionOpen, onConnectionSync, streams]);
|
||||||
|
|
||||||
function sync(connectionIds) {
|
function sync(connectionIds) {
|
||||||
for (let connectionId of connectionIds) {
|
for (let connectionId of connectionIds) {
|
||||||
@ -72,7 +109,11 @@ function useSession(onConnectionOpen, onConnectionSync) {
|
|||||||
const connection = peer.connect(connectionId, {
|
const connection = peer.connect(connectionId, {
|
||||||
metadata: { sync: false }
|
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);
|
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;
|
export default useSession;
|
||||||
|
@ -11,21 +11,22 @@ import GameContext from "../contexts/GameContext";
|
|||||||
import useSession from "../helpers/useSession";
|
import useSession from "../helpers/useSession";
|
||||||
|
|
||||||
import Token from "../components/Token";
|
import Token from "../components/Token";
|
||||||
|
import Party from "../components/Party";
|
||||||
|
|
||||||
function Game() {
|
function Game() {
|
||||||
const [gameId, setGameId] = useContext(GameContext);
|
const { gameId } = useContext(GameContext);
|
||||||
const handleConnectionOpenCallback = useCallback(handleConnectionOpen);
|
const handleConnectionOpenCallback = useCallback(handleConnectionOpen);
|
||||||
const handleConnectionSyncCallback = useCallback(handleConnectionSync);
|
const handleConnectionSyncCallback = useCallback(handleConnectionSync);
|
||||||
const [peer, peerId, connections, connectTo] = useSession(
|
const { peerId, connections, connectTo, streams } = useSession(
|
||||||
handleConnectionOpenCallback,
|
handleConnectionOpenCallback,
|
||||||
handleConnectionSyncCallback
|
handleConnectionSyncCallback
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (gameId !== null && peerId !== null) {
|
if (gameId !== null && peerId !== null && streams[peerId]) {
|
||||||
connectTo(gameId);
|
connectTo(gameId);
|
||||||
}
|
}
|
||||||
}, [gameId, peerId, connectTo]);
|
}, [gameId, peerId, connectTo, streams]);
|
||||||
|
|
||||||
const [imageSource, setImageSource] = useState(null);
|
const [imageSource, setImageSource] = useState(null);
|
||||||
const imageDataRef = useRef(null);
|
const imageDataRef = useRef(null);
|
||||||
@ -34,7 +35,7 @@ function Game() {
|
|||||||
imageDataRef.current = event.target.files[0];
|
imageDataRef.current = event.target.files[0];
|
||||||
setImageSource(URL.createObjectURL(imageDataRef.current));
|
setImageSource(URL.createObjectURL(imageDataRef.current));
|
||||||
for (let connection of Object.values(connections)) {
|
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() {
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
<Party streams={streams} />
|
||||||
<Flex sx={{ justifyContent: "center" }}>
|
<Flex sx={{ justifyContent: "center" }}>
|
||||||
<Image src={imageSource} />
|
<Image src={imageSource} />
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -5,7 +5,7 @@ import { Container, Flex, Button } from "theme-ui";
|
|||||||
import GameContext from "../contexts/GameContext";
|
import GameContext from "../contexts/GameContext";
|
||||||
|
|
||||||
function Home() {
|
function Home() {
|
||||||
const [gameId, setGameId] = useContext(GameContext);
|
const { setGameId } = useContext(GameContext);
|
||||||
|
|
||||||
function handleStartGame() {
|
function handleStartGame() {
|
||||||
setGameId(null);
|
setGameId(null);
|
||||||
|
@ -5,7 +5,7 @@ import { Container, Box, Label, Input, Button } from "theme-ui";
|
|||||||
import GameContext from "../contexts/GameContext";
|
import GameContext from "../contexts/GameContext";
|
||||||
|
|
||||||
function Join() {
|
function Join() {
|
||||||
const [gameId, setGameId] = useContext(GameContext);
|
const { gameId, setGameId } = useContext(GameContext);
|
||||||
|
|
||||||
function handleChange(event) {
|
function handleChange(event) {
|
||||||
setGameId(event.target.value);
|
setGameId(event.target.value);
|
||||||
|
Loading…
Reference in New Issue
Block a user