diff --git a/package.json b/package.json
index 79c5dde..25124db 100644
--- a/package.json
+++ b/package.json
@@ -20,7 +20,8 @@
"simple-peer": "^9.6.2",
"simplebar-react": "^2.1.0",
"socket.io-client": "^2.3.0",
- "theme-ui": "^0.3.1"
+ "theme-ui": "^0.3.1",
+ "webrtc-adapter": "^7.5.1"
},
"scripts": {
"predeploy": "yarn build",
diff --git a/src/components/Party.js b/src/components/Party.js
index 20b10b0..ecba181 100644
--- a/src/components/Party.js
+++ b/src/components/Party.js
@@ -14,6 +14,7 @@ function Party({
stream,
partyStreams,
onStreamStart,
+ onStreamEnd,
}) {
return (
-
+
diff --git a/src/components/StartStreamButton.js b/src/components/StartStreamButton.js
index 613dfd0..de7ad5d 100644
--- a/src/components/StartStreamButton.js
+++ b/src/components/StartStreamButton.js
@@ -1,19 +1,114 @@
-import React from "react";
-import { IconButton } from "theme-ui";
+import React, { useState } from "react";
+import { IconButton, Box, Text, Button, Label, Flex } from "theme-ui";
+import adapter from "webrtc-adapter";
+
+import Modal from "./Modal";
+
+function StartStreamButton({ onStreamStart, onStreamEnd, stream }) {
+ const [isStreamModalOpoen, setIsStreamModalOpen] = useState(false);
+ function openModal() {
+ setIsStreamModalOpen(true);
+ }
+ function closeModal() {
+ setIsStreamModalOpen(false);
+ setNoAudioTrack(false);
+ }
+
+ const unavailableMessage = (
+
+
+ Browser not supported.
+
+
+ See FAQ for more information.
+
+
+ );
+
+ const noAudioMessage = (
+
+
+ No audio found in screen share.
+
+ Ensure "Share audio" is selected when sharing.
+
+
+ See FAQ for more information.
+
+
+ );
+
+ const isSupported = adapter.browserDetails.browser === "chrome";
+ const [noAudioTrack, setNoAudioTrack] = useState(false);
+
+ function handleStreamStart() {
+ navigator.mediaDevices
+ .getDisplayMedia({
+ video: true,
+ audio: {
+ noiseSuppression: false,
+ autoGainControl: false,
+ echoCancellation: false,
+ },
+ })
+ .then((localStream) => {
+ const tracks = localStream.getTracks();
+
+ const hasAudio = tracks.some((track) => track.kind === "audio");
+ setNoAudioTrack(!hasAudio);
+
+ // Ensure an audio track is present
+ if (hasAudio) {
+ onStreamStart && onStreamStart(localStream);
+ closeModal();
+ } else {
+ // Stop the stream
+ for (let track of tracks) {
+ track.stop();
+ }
+ }
+ })
+ .catch(() => {});
+ }
-function StartStreamButton({ onStream }) {
return (
-
-
-
+ <>
+
+
+
+
+
+
+
+ Share your computers audio with the party
+
+ {!isSupported && unavailableMessage}
+ {isSupported && !stream && noAudioTrack && noAudioMessage}
+
+ {isSupported && !stream && (
+
+ )}
+ {isSupported && stream && (
+
+ )}
+
+
+
+ >
);
}
diff --git a/src/routes/Game.js b/src/routes/Game.js
index 41232f7..6ba6320 100644
--- a/src/routes/Game.js
+++ b/src/routes/Game.js
@@ -1,4 +1,4 @@
-import React, { useState, useRef, useEffect } from "react";
+import React, { useState, useRef, useEffect, useCallback } from "react";
import { Flex } from "theme-ui";
import { useParams } from "react-router-dom";
@@ -136,70 +136,51 @@ function Game() {
}
}
- useEffect(() => {
- if (stream) {
- const tracks = stream.getTracks();
+ 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 peer of Object.values(peers)) {
+ peer.addTrack(track, localStream);
+ }
+ }
+ }
+ }
- function handleStreamEnd() {
- setStream(null);
- for (let track of tracks) {
- // Only sending audio so only remove the audio track
- if (track.kind === "audio") {
- for (let peer of Object.values(peers)) {
- track.stop();
- peer.removeTrack(track, stream);
- }
+ 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 peer of Object.values(peers)) {
+ peer.removeTrack(track, localStream);
}
}
}
+ },
+ [peers]
+ );
+ 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(track);
+ handleStreamEnd(stream);
};
}
}
}
- }, [stream, peers]);
-
- function handleStreamStart() {
- navigator.mediaDevices
- .getDisplayMedia({
- video: true,
- audio: {
- noiseSuppression: false,
- autoGainControl: false,
- echoCancellation: false,
- },
- })
- .then((localStream) => {
- setStream(localStream);
- const tracks = localStream.getTracks();
-
- let noAudio = true;
- for (let track of tracks) {
- // Only add the audio track of the stream to the remote peer
- if (track.kind === "audio") {
- noAudio = false;
- for (let peer of Object.values(peers)) {
- peer.addTrack(track, localStream);
- }
- }
- }
-
- if (noAudio) {
- // TODO: move this to a ui element
- console.error("No audio tracks found in screen share");
- for (let track of tracks) {
- track.stop();
- }
- }
- });
- }
+ }, [stream, peers, handleStreamEnd]);
return (
@@ -214,6 +195,7 @@ function Game() {
stream={stream}
partyStreams={partyStreams}
onStreamStart={handleStreamStart}
+ onStreamEnd={handleStreamEnd}
/>