Added modal to radio and added error feedback in it
This commit is contained in:
parent
8c5d58ae93
commit
84f201858a
@ -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",
|
||||
|
@ -14,6 +14,7 @@ function Party({
|
||||
stream,
|
||||
partyStreams,
|
||||
onStreamStart,
|
||||
onStreamEnd,
|
||||
}) {
|
||||
return (
|
||||
<Flex
|
||||
@ -53,7 +54,11 @@ function Party({
|
||||
</Box>
|
||||
<Flex sx={{ flexDirection: "column" }}>
|
||||
<ChangeNicknameButton nickname={nickname} onChange={onNicknameChange} />
|
||||
<StartStreamButton onStream={onStreamStart} />
|
||||
<StartStreamButton
|
||||
onStreamStart={onStreamStart}
|
||||
onStreamEnd={onStreamEnd}
|
||||
stream={stream}
|
||||
/>
|
||||
<AddPartyMemberButton gameId={gameId} />
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
@ -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 = (
|
||||
<Box p={2} bg="hsla(230, 20%, 0%, 20%)">
|
||||
<Text>
|
||||
Browser not supported.
|
||||
<br />
|
||||
<br />
|
||||
See FAQ for more information.
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
|
||||
const noAudioMessage = (
|
||||
<Box p={2} bg="hsla(230, 20%, 0%, 20%)">
|
||||
<Text>
|
||||
No audio found in screen share.
|
||||
<br />
|
||||
Ensure "Share audio" is selected when sharing.
|
||||
<br />
|
||||
<br />
|
||||
See FAQ for more information.
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
|
||||
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 (
|
||||
<IconButton m={1} aria-label="Start Radio Stream" onClick={onStream}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
fill="currentcolor"
|
||||
>
|
||||
<path d="M3.24 6.15C2.51 6.43 2 7.17 2 8v12c0 1.1.9 2 2 2h16c1.11 0 2-.9 2-2V8c0-1.1-.9-2-2-2H8.3l7.43-3c.46-.19.68-.71.49-1.17-.19-.46-.71-.68-1.17-.49L3.24 6.15zM7 20c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm13-8h-2v-1c0-.55-.45-1-1-1s-1 .45-1 1v1H4V9c0-.55.45-1 1-1h14c.55 0 1 .45 1 1v3z" />
|
||||
</svg>
|
||||
</IconButton>
|
||||
<>
|
||||
<IconButton m={1} aria-label="Start Radio Stream" onClick={openModal}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
fill="currentcolor"
|
||||
>
|
||||
<path d="M3.24 6.15C2.51 6.43 2 7.17 2 8v12c0 1.1.9 2 2 2h16c1.11 0 2-.9 2-2V8c0-1.1-.9-2-2-2H8.3l7.43-3c.46-.19.68-.71.49-1.17-.19-.46-.71-.68-1.17-.49L3.24 6.15zM7 20c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm13-8h-2v-1c0-.55-.45-1-1-1s-1 .45-1 1v1H4V9c0-.55.45-1 1-1h14c.55 0 1 .45 1 1v3z" />
|
||||
</svg>
|
||||
</IconButton>
|
||||
<Modal isOpen={isStreamModalOpoen} onRequestClose={closeModal}>
|
||||
<Box>
|
||||
<Label pt={2} pb={1}>
|
||||
Radio (experimental)
|
||||
</Label>
|
||||
<Text mb={2} variant="caption">
|
||||
Share your computers audio with the party
|
||||
</Text>
|
||||
{!isSupported && unavailableMessage}
|
||||
{isSupported && !stream && noAudioTrack && noAudioMessage}
|
||||
<Flex py={2}>
|
||||
{isSupported && !stream && (
|
||||
<Button sx={{ flexGrow: 1 }} onClick={handleStreamStart}>
|
||||
Start Radio
|
||||
</Button>
|
||||
)}
|
||||
{isSupported && stream && (
|
||||
<Button sx={{ flexGrow: 1 }} onClick={() => onStreamEnd(stream)}>
|
||||
Stop Radio
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
</Box>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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 (
|
||||
<Flex sx={{ flexDirection: "column", height: "100%" }}>
|
||||
@ -214,6 +195,7 @@ function Game() {
|
||||
stream={stream}
|
||||
partyStreams={partyStreams}
|
||||
onStreamStart={handleStreamStart}
|
||||
onStreamEnd={handleStreamEnd}
|
||||
/>
|
||||
<Map
|
||||
mapSource={mapSource}
|
||||
|
20
yarn.lock
20
yarn.lock
@ -9622,6 +9622,13 @@ rsvp@^4.8.4:
|
||||
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"
|
||||
integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==
|
||||
|
||||
rtcpeerconnection-shim@^1.2.15:
|
||||
version "1.2.15"
|
||||
resolved "https://registry.yarnpkg.com/rtcpeerconnection-shim/-/rtcpeerconnection-shim-1.2.15.tgz#e7cc189a81b435324c4949aa3dfb51888684b243"
|
||||
integrity sha512-C6DxhXt7bssQ1nHb154lqeL0SXz5Dx4RczXZu2Aa/L1NJFnEVDxFwCBo3fqtuljhHIGceg5JKBV4XJ0gW5JKyw==
|
||||
dependencies:
|
||||
sdp "^2.6.0"
|
||||
|
||||
run-async@^2.2.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
|
||||
@ -9733,6 +9740,11 @@ schema-utils@^2.5.0, schema-utils@^2.6.0, schema-utils@^2.6.1, schema-utils@^2.6
|
||||
ajv "^6.10.2"
|
||||
ajv-keywords "^3.4.1"
|
||||
|
||||
sdp@^2.12.0, sdp@^2.6.0:
|
||||
version "2.12.0"
|
||||
resolved "https://registry.yarnpkg.com/sdp/-/sdp-2.12.0.tgz#338a106af7560c86e4523f858349680350d53b22"
|
||||
integrity sha512-jhXqQAQVM+8Xj5EjJGVweuEzgtGWb3tmEEpl3CLP3cStInSbVHSg0QWOGQzNq8pSID4JkpeV2mPqlMDLrm0/Vw==
|
||||
|
||||
select-hose@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
|
||||
@ -11175,6 +11187,14 @@ webpack@4.41.5:
|
||||
watchpack "^1.6.0"
|
||||
webpack-sources "^1.4.1"
|
||||
|
||||
webrtc-adapter@^7.5.1:
|
||||
version "7.5.1"
|
||||
resolved "https://registry.yarnpkg.com/webrtc-adapter/-/webrtc-adapter-7.5.1.tgz#30895069da4fed9578597c066e6194fe83b97b37"
|
||||
integrity sha512-R5LkIR/APjODkstSXFOztOmINXQ0nqIGfUoKTtCzjyiDXHNgwhkqZ9vi8UzGyjfUBibuZ0ZzVyV10qtuLGW3CQ==
|
||||
dependencies:
|
||||
rtcpeerconnection-shim "^1.2.15"
|
||||
sdp "^2.12.0"
|
||||
|
||||
websocket-driver@>=0.5.1:
|
||||
version "0.7.3"
|
||||
resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.3.tgz#a2d4e0d4f4f116f1e6297eba58b05d430100e9f9"
|
||||
|
Loading…
Reference in New Issue
Block a user