import React, { useState, useEffect, useCallback, useRef } from "react"; import { useToasts } from "react-toast-notifications"; // Load session for auto complete // eslint-disable-next-line no-unused-vars import Session from "./Session"; import { isStreamStopped, omit } from "../helpers/shared"; import { useParty } from "../contexts/PartyContext"; import Party from "../components/party/Party"; /** * @typedef {object} NetworkedPartyProps * @property {string} gameId * @property {Session} session */ /** * @param {NetworkedPartyProps} props */ function NetworkedParty({ gameId, session }) { const partyState = useParty(); const [stream, setStream] = useState(null); const [partyStreams, setPartyStreams] = useState({}); const { addToast } = useToasts(); function handleStreamStart(localStream) { setStream(localStream); const tracks = localStream.getTracks(); for (let track of tracks) { // Only add the audio track of the stream to the remote peer if (track.kind === "audio") { for (let player of Object.values(partyState)) { session.startStreamTo(player.sessionId, track, localStream); } } } } const handleStreamEnd = useCallback( (localStream) => { setStream(null); const tracks = localStream.getTracks(); for (let track of tracks) { track.stop(); // Only sending audio so only remove the audio track if (track.kind === "audio") { for (let player of Object.values(partyState)) { session.endStreamTo(player.sessionId, track, localStream); } } } }, [session, partyState] ); // Keep a reference to players who have just joined to show the joined notification const joinedPlayersRef = useRef([]); useEffect(() => { if (joinedPlayersRef.current.length > 0) { for (let id of joinedPlayersRef.current) { if (partyState[id]) { addToast(`${partyState[id].nickname} joined the party`); } } joinedPlayersRef.current = []; } }, [partyState, addToast]); useEffect(() => { function handlePlayerJoined(sessionId) { if (stream) { const tracks = stream.getTracks(); for (let track of tracks) { if (track.kind === "audio") { session.startStreamTo(sessionId, track, stream); } } } // Add player to join notification list // Can't just show the notification here as the partyState data isn't populated at this point joinedPlayersRef.current.push(sessionId); } function handlePlayerLeft(sessionId) { if (partyState[sessionId]) { addToast(`${partyState[sessionId].nickname} left the party`); } } function handlePeerTrackAdded({ peer, stream: remoteStream }) { setPartyStreams((prevStreams) => ({ ...prevStreams, [peer.id]: remoteStream, })); } function handlePeerTrackRemoved({ peer, stream: remoteStream }) { if (isStreamStopped(remoteStream)) { setPartyStreams((prevStreams) => omit(prevStreams, [peer.id])); } else { setPartyStreams((prevStreams) => ({ ...prevStreams, [peer.id]: remoteStream, })); } } session.on("playerJoined", handlePlayerJoined); session.on("playerLeft", handlePlayerLeft); session.on("peerTrackAdded", handlePeerTrackAdded); session.on("peerTrackRemoved", handlePeerTrackRemoved); return () => { session.off("playerJoined", handlePlayerJoined); session.off("playerLeft", handlePlayerLeft); session.off("peerTrackAdded", handlePeerTrackAdded); session.off("peerTrackRemoved", handlePeerTrackRemoved); }; }); useEffect(() => { if (stream) { const tracks = stream.getTracks(); // Detect when someone has ended the screen sharing // by looking at the streams video track onended // the audio track doesn't seem to trigger this event for (let track of tracks) { if (track.kind === "video") { track.onended = function () { handleStreamEnd(stream); }; } } } }, [stream, handleStreamEnd]); return ( <> ); } export default NetworkedParty;