diff --git a/package.json b/package.json
index 9bc045f..827d458 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,8 @@
"@fortawesome/fontawesome-svg-core": "^1.2.15",
"@fortawesome/free-solid-svg-icons": "^5.7.2",
"@fortawesome/react-fontawesome": "^0.1.4",
+ "@material-ui/core": "^3.9.2",
+ "@material-ui/icons": "^3.0.2",
"approximate-number": "^2.0.0",
"array-buffer-to-hex": "^1.0.0",
"blessed": "^0.1.81",
@@ -43,6 +45,7 @@
"qs": "^6.6.0",
"react": "^16.4.2",
"react-app-rewired": "^2.1.0",
+ "react-compound-slider": "^1.2.1",
"react-dom": "^16.4.2",
"react-force-graph-3d": "^1.6.1",
"react-router-dom": "^4.3.1",
@@ -55,6 +58,7 @@
"tinyqueue": "^2.0.0",
"urlsafe-base64": "^1.0.0",
"use-events": "^1.2.0",
+ "use-fullscreen": "^0.0.5",
"uuid": "^3.3.2",
"worker-loader": "^2.0.0",
"wrtc": "^0.3.5",
diff --git a/src/App.js b/src/App.js
deleted file mode 100644
index 4554388..0000000
--- a/src/App.js
+++ /dev/null
@@ -1,235 +0,0 @@
-import React, {useEffect, useState, useMemo, useRef} from 'react';
-import URI from './ppspp/uri';
-import DiagnosticMenu from './DiagnosticMenu';
-import SwarmPlayer from './SwarmPlayer';
-import {ChunkedReadStream} from './chunkedStream';
-import {Client} from './client';
-import {ConnManager} from './wrtc';
-import {PubSubConsumer} from './pubsub';
-import PlayButton from './PlayButton';
-import qs from 'qs';
-import {useTimeout, useAsync} from 'react-use';
-import hexToUint8Array from './hexToUint8Array';
-import moment from 'moment';
-
-import './App.scss';
-
-const getDefaultBootstrapAddress = () => {
- const proto = window.location.protocol === 'https:' ? 'wss' : 'ws';
- const host = process.env.NODE_ENV === 'development'
- ? window.location.hostname + ':8080'
- : window.location.host;
- return `${proto}://${host}`;
-};
-
-const useSwarm = ({ppsppClient} = {}) => {
- const [swarm, setSwarm] = useState(null);
- const join = uri => setSwarm(ppsppClient.joinSwarm(URI.parse(uri)));
- return [swarm, join];
-};
-
-const usePubSubSwarm = (client, name) => {
- const [swarm, setSwarm] = useState(null);
- const [consumer, setConsumer] = useState(null);
-
- useEffect(() => {
- if (client) {
- setImmediate(() => {
- const {uri} = client.bootstrap.swarms.find(desc => desc.name === name);
- const swarm = client.ppsppClient.joinSwarm(URI.parse(uri));
- const consumer = new PubSubConsumer(swarm);
-
- setSwarm(swarm);
- setConsumer(consumer);
-
- return () => client.ppsppClient.leaveSwarm(URI.parse(uri));
- });
- }
- }, [client]);
-
- return [consumer, swarm];
-};
-
-const useIndexSwarm = client => usePubSubSwarm(client, 'index');
-
-const useQuery = queryString => useMemo(() => {
- return qs.parse(queryString, {ignoreQueryPrefix: true}) || {};
-}, [queryString]);
-
-const NoiseLogger = ({swarm}) => {
- useEffect(() => {
- if (swarm) {
- const stream = new ChunkedReadStream(swarm);
- stream.on('data', ({length}) => console.log(`received ${length} bytes`));
- }
- }, [swarm]);
-
- return ;
-};
-
-const PubSubLogger = ({indexSwarm, swarm}) => {
- useEffect(() => {
- if (swarm) {
- const consumer = new PubSubConsumer(swarm);
- consumer.on('message', message => console.log(message));
- }
- }, [swarm]);
-
- return (
- <>
-
-
- >
- );
-};
-
-const useChatSwarm = client => {
- const [consumer] = usePubSubSwarm(client, 'chat');
- const [messages, setMessages] = useState([]);
-
- useEffect(() => {
- if (consumer == null) {
- return;
- }
-
- const handleMessage = message => setMessages(prev => ([
- ...prev.slice(prev.length > 100 ? 1 : 0),
- message,
- ]));
-
- consumer.on('message', e => console.log(e));
- consumer.on('message', handleMessage);
- return () => consumer.removeListener('message', handleMessage);
- }, [consumer]);
-
- const sendMessage = message => {
- if (client.dhtClient) {
- client.dhtClient.send(
- hexToUint8Array(client.bootstrap.bootstrapId),
- 'chat.message',
- {message},
- );
- }
- };
-
- return [messages, sendMessage];
-};
-
-const ChatMessages = ({messages}) => {
- const items = messages.map(({time, message, id}) => (
-
- {moment(time).format('HH:mm:ss')}
- {message}
-
- )).reverse();
-
- return (
-
- );
-};
-
-const Chat = ({client}) => {
- const [messages, sendMessage] = useChatSwarm(client);
- const [message, setMessage] = useState('');
- const input = useRef();
-
- const handleSubmit = e => {
- e.preventDefault();
-
- sendMessage(message);
- setMessage('');
- };
-
- const handleChange = e => {
- setMessage(e.target.value);
- };
-
- return (
-
-
-
-
- );
-};
-
-const App = ({
- location,
- match: {params},
- clientTimeoutMs = 5000,
-}) => {
- const query = useQuery(location.search);
- const autoPlay = 'autoplay' in query;
- const bootstrapAddress = query.bootstrap || getDefaultBootstrapAddress();
- const swarmName = params.name;
-
- const clientTimeout = useTimeout(clientTimeoutMs);
- const {
- loading: clientLoading,
- error: clientError,
- value: client,
- } = useAsync(() => Client.create(new ConnManager(bootstrapAddress)), []);
-
- const index = null;
- const indexSwarm = null;
- // const [index, indexSwarm] = useIndexSwarm(client);
- const [swarm, joinSwarm] = useSwarm(client);
-
- const swarmDesc = client?.bootstrap.swarms.find(desc => desc.name === swarmName);
- const error = clientError || (autoPlay && clientTimeout) || !(clientLoading || swarmDesc);
-
- useEffect(() => {
- if (autoPlay && swarmDesc) {
- setImmediate(() => joinSwarm(swarmDesc.uri));
- }
- }, [autoPlay, swarmDesc]);
-
- if (swarm) {
- const Component = {
- 'application/octet-stream': NoiseLogger,
- 'application/json': PubSubLogger,
- 'video/mpeg-ts': SwarmPlayer,
- }[swarmDesc.contentType];
-
- return (
-
- );
- }
-
- const indexSwarmDiagnosticMenu = indexSwarm && ;
- const chat = 'chat' in query && ;
-
- return (
- <>
- {chat}
- {indexSwarmDiagnosticMenu}
-
- joinSwarm(swarmDesc.uri)}
- pulse={!clientLoading && !autoPlay}
- flicker={clientLoading || autoPlay}
- error={error}
- blur
- />
- >
- );
-};
-
-export default App;
diff --git a/src/Router.js b/src/Router.js
deleted file mode 100644
index 4efd537..0000000
--- a/src/Router.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import React from 'react';
-import {HashRouter, Redirect, Route, Switch} from 'react-router-dom';
-import App from './App';
-import Test from './Test';
-import DhtGraph from './DhtGraph';
-
-const Router = () => (
-
-
-
-
-
-
-
-
-);
-
-export default Router;
diff --git a/src/SwarmPlayer.scss b/src/SwarmPlayer.scss
deleted file mode 100644
index 12ae92c..0000000
--- a/src/SwarmPlayer.scss
+++ /dev/null
@@ -1,26 +0,0 @@
-.swarm_player__video {
- height: 100vh;
- width: 100vw;
-}
-
-.swarm_player__waiting_spinner {
- position: absolute;
- top: 50%;
- left: 50%;
- margin: -8px 0 0 -8px;
- animation: spin 2s linear infinite;
- opacity: 0.8;
-
- svg {
- transform: scale(6);
- }
-}
-
-@keyframes spin {
- 0% {
- transform: rotate(0deg);
- }
- 100% {
- transform: rotate(360deg);
- }
-}
diff --git a/src/components/App.js b/src/components/App.js
new file mode 100644
index 0000000..a60694c
--- /dev/null
+++ b/src/components/App.js
@@ -0,0 +1,86 @@
+import React, {useEffect, useState} from 'react';
+import URI from '../ppspp/uri';
+import {Client} from '../client';
+import {ConnManager} from '../wrtc';
+import PlayButton from './PlayButton';
+import {useTimeout, useAsync} from 'react-use';
+import useQuery from '../hooks/useQuery';
+import VideoPlayer from './VideoPlayer';
+
+import './App.scss';
+
+const NoiseLogger = React.lazy(() => import('./NoiseLogger'));
+const PubSubLogger = React.lazy(() => import('./PubSubLogger'));
+
+const getDefaultBootstrapAddress = () => {
+ const proto = window.location.protocol === 'https:' ? 'wss' : 'ws';
+ const host = process.env.NODE_ENV === 'development'
+ ? window.location.hostname + ':8080'
+ : window.location.host;
+ return `${proto}://${host}`;
+};
+
+const useSwarm = ({ppsppClient} = {}) => {
+ const [swarm, setSwarm] = useState(null);
+ const join = uri => setSwarm(ppsppClient.joinSwarm(URI.parse(uri)));
+ return [swarm, join];
+};
+
+const App = ({
+ location,
+ match: {params},
+ clientTimeoutMs = 5000,
+}) => {
+ const query = useQuery(location.search);
+ const autoPlay = 'autoplay' in query;
+ const bootstrapAddress = query.bootstrap || getDefaultBootstrapAddress();
+ const swarmName = params.name;
+
+ const clientTimeout = useTimeout(clientTimeoutMs);
+ const {
+ loading: clientLoading,
+ error: clientError,
+ value: client,
+ } = useAsync(() => Client.create(new ConnManager(bootstrapAddress)), []);
+
+ const [swarm, joinSwarm] = useSwarm(client);
+
+ const swarmDesc = client?.bootstrap.swarms.find(desc => desc.name === swarmName);
+ const error = clientError || (autoPlay && clientTimeout) || !(clientLoading || swarmDesc);
+
+ useEffect(() => {
+ if (autoPlay && swarmDesc) {
+ setImmediate(() => joinSwarm(swarmDesc.uri));
+ }
+ }, [autoPlay, swarmDesc]);
+
+ if (swarm) {
+ const Component = {
+ 'application/octet-stream': NoiseLogger,
+ 'application/json': PubSubLogger,
+ 'video/mpeg-ts': VideoPlayer,
+ }[swarmDesc.contentType];
+
+ return (
+
+ );
+ }
+
+ return (
+ <>
+
+ joinSwarm(swarmDesc.uri)}
+ pulse={!clientLoading && !autoPlay}
+ flicker={clientLoading || autoPlay}
+ error={error}
+ blur
+ />
+ >
+ );
+};
+
+export default App;
diff --git a/src/App.scss b/src/components/App.scss
similarity index 98%
rename from src/App.scss
rename to src/components/App.scss
index 35dbfdd..55d1df2 100644
--- a/src/App.scss
+++ b/src/components/App.scss
@@ -82,7 +82,7 @@ body {
margin-left: -350px;
height: calc(100vh + 700px);
width: calc(100vw + 700px);
- background-image: url("/noise.png");
+ background-image: url("./noise.png");
animation: idle-jitter 500ms infinite
}
diff --git a/src/components/Chat.js b/src/components/Chat.js
new file mode 100644
index 0000000..57b0f9b
--- /dev/null
+++ b/src/components/Chat.js
@@ -0,0 +1,38 @@
+import React, {useState, useRef} from 'react';
+import ChatMessages from './ChatMessages';
+import useChatSwarm from '../hooks/useChatSwarm';
+
+const Chat = ({client}) => {
+ const [messages, sendMessage] = useChatSwarm(client);
+ const [message, setMessage] = useState('');
+ const input = useRef();
+
+ const handleSubmit = e => {
+ e.preventDefault();
+
+ sendMessage(message);
+ setMessage('');
+ };
+
+ const handleChange = e => {
+ setMessage(e.target.value);
+ };
+
+ return (
+
+
+
+
+ );
+};
+
+export default Chat;
diff --git a/src/components/ChatMessages.js b/src/components/ChatMessages.js
new file mode 100644
index 0000000..a17fdca
--- /dev/null
+++ b/src/components/ChatMessages.js
@@ -0,0 +1,19 @@
+import React from 'react';
+import moment from 'moment';
+
+const Messages = ({messages}) => {
+ const items = messages.map(({time, message, id}) => (
+
+ {moment(time).format('HH:mm:ss')}
+ {message}
+
+ )).reverse();
+
+ return (
+
+ );
+};
+
+export default Messages;
diff --git a/src/DhtGraph.js b/src/components/DhtGraph.js
similarity index 97%
rename from src/DhtGraph.js
rename to src/components/DhtGraph.js
index 81e9c7e..ad871cd 100644
--- a/src/DhtGraph.js
+++ b/src/components/DhtGraph.js
@@ -1,6 +1,6 @@
import React, {useEffect, useReducer, useState} from 'react';
-import {Server, ConnManager} from './loopback';
-import {Client} from './client';
+import {Server, ConnManager} from '../loopback';
+import {Client} from '../client';
import arrayBufferToHex from 'array-buffer-to-hex';
import ForceGraph3D from 'react-force-graph-3d';
import {schemeCategory10} from 'd3-scale-chromatic';
@@ -74,7 +74,7 @@ const reduceGraph = (graph, {type, ...data}) => {
}),
};
case 'REMOVE_LINK':
- console.log(data);
+ // console.log(data);
return {
nodes: graph.nodes,
links: graph.links.filter(({source, target}) => {
@@ -123,7 +123,7 @@ const useGraph = () => {
// }
const {id, allChannels} = dhtClient;
- console.log(allChannels);
+ // console.log(allChannels);
const source = arrayBufferToHex(id);
dispatchGraphAction({
@@ -241,7 +241,7 @@ const App = () => {
const [graph, {addNodes, deleteNodes}] = useGraph();
const handleNodeClick = useNodePinger();
- console.log(graph);
+ // console.log(graph);
// useEffect(() => {
// let n = 1;
@@ -259,7 +259,6 @@ const App = () => {
-
diff --git a/src/DiagnosticMenu/index.css b/src/components/DiagnosticMenu/index.css
similarity index 100%
rename from src/DiagnosticMenu/index.css
rename to src/components/DiagnosticMenu/index.css
diff --git a/src/DiagnosticMenu/index.js b/src/components/DiagnosticMenu/index.js
similarity index 100%
rename from src/DiagnosticMenu/index.js
rename to src/components/DiagnosticMenu/index.js
diff --git a/src/components/NoiseLogger.js b/src/components/NoiseLogger.js
new file mode 100644
index 0000000..ceef569
--- /dev/null
+++ b/src/components/NoiseLogger.js
@@ -0,0 +1,16 @@
+import React, {useEffect} from 'react';
+import {ChunkedReadStream} from '../chunkedStream';
+import DiagnosticMenu from './DiagnosticMenu';
+
+const NoiseLogger = ({swarm}) => {
+ useEffect(() => {
+ if (swarm) {
+ const stream = new ChunkedReadStream(swarm);
+ stream.on('data', ({length}) => console.log(`received ${length} bytes`));
+ }
+ }, [swarm]);
+
+ return ;
+};
+
+export default NoiseLogger;
diff --git a/src/PlayButton.js b/src/components/PlayButton.js
similarity index 100%
rename from src/PlayButton.js
rename to src/components/PlayButton.js
diff --git a/src/PlayButton.scss b/src/components/PlayButton.scss
similarity index 100%
rename from src/PlayButton.scss
rename to src/components/PlayButton.scss
diff --git a/src/components/PubSubLogger.js b/src/components/PubSubLogger.js
new file mode 100644
index 0000000..d307483
--- /dev/null
+++ b/src/components/PubSubLogger.js
@@ -0,0 +1,21 @@
+import React, {useEffect} from 'react';
+import {PubSubConsumer} from '../pubsub';
+import DiagnosticMenu from './DiagnosticMenu';
+
+const PubSubLogger = ({indexSwarm, swarm}) => {
+ useEffect(() => {
+ if (swarm) {
+ const consumer = new PubSubConsumer(swarm);
+ consumer.on('message', message => console.log(message));
+ }
+ }, [swarm]);
+
+ return (
+ <>
+
+
+ >
+ );
+};
+
+export default PubSubLogger;
diff --git a/src/components/Router.js b/src/components/Router.js
new file mode 100644
index 0000000..0af29bb
--- /dev/null
+++ b/src/components/Router.js
@@ -0,0 +1,24 @@
+import React, {Suspense, lazy} from 'react';
+import {HashRouter, Redirect, Route, Switch} from 'react-router-dom';
+import App from './App';
+import PlayButton from './PlayButton';
+
+const Test = lazy(() => import('./Test'));
+const DhtGraph = lazy(() => import('./DhtGraph'));
+
+console.log({App, Test, DhtGraph});
+
+const Router = () => (
+
+ }>
+
+ } />
+ } />
+
+
+
+
+
+);
+
+export default Router;
diff --git a/src/Test.js b/src/components/Test.js
similarity index 89%
rename from src/Test.js
rename to src/components/Test.js
index d06b27a..2b8fdef 100644
--- a/src/Test.js
+++ b/src/components/Test.js
@@ -1,7 +1,7 @@
import React, {useEffect, useState} from 'react';
-import {Server, ConnManager} from './loopback';
-import {Client} from './client';
-import {ChunkedReadStream, ChunkedWriteStreamInjector} from './chunkedStream';
+import {Server, ConnManager} from '../loopback';
+import {Client} from '../client';
+import {ChunkedReadStream, ChunkedWriteStreamInjector} from '../chunkedStream';
import DiagnosticMenu from './DiagnosticMenu';
import './App.scss';
diff --git a/src/components/VideoControls.js b/src/components/VideoControls.js
new file mode 100644
index 0000000..5f96ba1
--- /dev/null
+++ b/src/components/VideoControls.js
@@ -0,0 +1,75 @@
+import React from 'react';
+import VideoVolume from './VideoVolume';
+import classNames from 'classnames';
+import {
+ Pause,
+ PlayArrow,
+ VolumeOff,
+ VolumeMute,
+ VolumeDown,
+ VolumeUp,
+ Fullscreen,
+ FullscreenExit,
+} from '@material-ui/icons';
+
+import './VideoPlayer.scss';
+
+const VideoControls = ({
+ playing,
+ pause,
+ play,
+ volume,
+ unmute,
+ mute,
+ fullscreen,
+ toggleFullscreen,
+ visible,
+ setVolume,
+}) => {
+ const playButton = playing
+ ?
+ : ;
+
+ const volumeIcons = [
+ VolumeOff,
+ VolumeMute,
+ VolumeDown,
+ VolumeUp,
+ ];
+ const VolumeIcon = volumeIcons[Math.ceil(volume * (volumeIcons.length - 1))];
+ const handleVolumeClick = () => volume === 0 ? unmute() : mute();
+
+ let fullscreenButton;
+ if (document.fullscreenEnabled) {
+ const Icon = fullscreen ? FullscreenExit : Fullscreen;
+ fullscreenButton = (
+
+
+
+ );
+ }
+
+ const controlsClassName = classNames({
+ swarm_player__controls: true,
+ visible: visible,
+ });
+
+ return (
+
+
+
+ {playButton}
+
+
+
+
+
+
+
+ {fullscreenButton}
+
+
+ );
+};
+
+export default VideoControls;
diff --git a/src/components/VideoPlayer.js b/src/components/VideoPlayer.js
new file mode 100644
index 0000000..7081410
--- /dev/null
+++ b/src/components/VideoPlayer.js
@@ -0,0 +1,79 @@
+import React, {useEffect, useRef, useState} from 'react';
+import DiagnosticMenu from './DiagnosticMenu';
+import PlayButton from './PlayButton';
+import useSwarmMediaSource from '../hooks/useSwarmMediaSource';
+import useVideo from '../hooks/useVideo';
+import {useDebounce} from 'react-use';
+import useFullscreen from 'use-fullscreen';
+import {Loop} from '@material-ui/icons';
+import VideoControls from './VideoControls';
+
+import './VideoPlayer.scss';
+
+const SwarmPlayer = ({swarm, indexSwarm}) => {
+ const [videoState, videoProps, videoControls] = useVideo();
+ const mediaSource = useSwarmMediaSource(swarm);
+
+ useEffect(() => {
+ if (videoProps.ref.current != null && mediaSource != null) {
+ videoProps.ref.current.src = URL.createObjectURL(mediaSource);
+ videoControls.play();
+ }
+ }, [videoProps.ref, mediaSource]);
+
+ const [controlsVisible, setControlsVisible] = useState(false);
+ const [lastActive, setLastActive] = useState(false);
+
+ useDebounce(() => setControlsVisible(false), 5000, [lastActive]);
+
+ const handleMouseMove = () => {
+ setControlsVisible(true);
+ setLastActive(Date.now());
+ };
+
+ const handleMouseOut = () => setControlsVisible(false);
+
+ const ref = useRef();
+ const [isFullscreen, toggleFullscreen] = useFullscreen();
+
+ const playButton = (videoState.waiting && videoState.loaded) ? (
+
+
+
+ ) : (
+
+ );
+
+ return (
+
+ {/* */}
+
+
+ );
+};
+
+export default SwarmPlayer;
diff --git a/src/components/VideoPlayer.scss b/src/components/VideoPlayer.scss
new file mode 100644
index 0000000..f4955d1
--- /dev/null
+++ b/src/components/VideoPlayer.scss
@@ -0,0 +1,121 @@
+.swarm_player__video {
+ height: 100vh;
+ width: 100vw;
+}
+
+.swarm_player__waiting_spinner {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ margin: -16px 0 0 -16px;
+ animation: spin 2s linear infinite;
+ opacity: 0.8;
+ height: 24px;
+ width: 24px;
+
+ svg {
+ transform: scale(6);
+ height: 24px;
+ width: 24px;
+ }
+}
+
+@keyframes spin {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(-360deg);
+ }
+}
+
+.swarm_player__controls {
+ position: absolute;
+ bottom: 0;
+ height: 40px;
+ width: 100%;
+ display: flex;
+ justify-content: space-between;
+ opacity: 0;
+ background: rgba(0, 0, 0, 0.8);
+ transition: 400ms opacity ease-in-out;
+
+ &.visible {
+ opacity: 1;
+ }
+
+ .swarm_player__controls__group {
+ display: flex;
+ }
+
+ .play,
+ .settings,
+ .fullscreen {
+ width: 30px;
+ flex-grow: 0;
+ flex-shrink: 0;
+ }
+
+ .volume {
+ flex-grow: 0;
+ width: 200px;
+ display: flex;
+ justify-content: space-between;
+ }
+
+ .play,
+ .settings,
+ .fullscreen,
+ .volume {
+ padding: 10px;
+
+ svg {
+ cursor: pointer;
+ fill: #fff;
+ }
+ }
+
+ .video_volume__slider {
+ width: 150px;
+ position: relative;
+ margin: 9px 10px;
+ opacity: 0;
+ transition: 400ms opacity ease-in-out;
+ }
+
+ .volume:hover .video_volume__slider,
+ .video_volume__slider.dragging {
+ opacity: 1;
+ }
+
+ .video_volume__rail {
+ position: absolute;
+ width: 100%;
+ height: 8px;
+ border-radius: 8px;
+ cursor: pointer;
+ background-color: #bbb;
+ }
+
+ .video_volume__track {
+ position: absolute;
+ height: 8px;
+ z-index: 1px;
+ background-color: #eee;
+ border-radius: 8px;
+ cursor: pointer;
+ }
+
+ .video_volume__handle {
+ position: absolute;
+ margin-left: -11px;
+ margin-top: -6px;
+ z-index: 2;
+ width: 18px;
+ height: 18px;
+ cursor: pointer;
+ border-radius: 50%;
+ box-shadow: 1px 1px 1px 1px rgba(100, 100, 100, 0.2);
+ background-color: #fff;
+ }
+}
diff --git a/src/components/VideoVolume.js b/src/components/VideoVolume.js
new file mode 100644
index 0000000..55b3d16
--- /dev/null
+++ b/src/components/VideoVolume.js
@@ -0,0 +1,89 @@
+import React, {useState} from 'react';
+import {Slider, Rail, Handles, Tracks} from 'react-compound-slider';
+import classNames from 'classnames';
+
+export const Handle = ({
+ domain: [min, max],
+ handle: {id, value, percent},
+ getHandleProps,
+}) => (
+
+);
+
+export const Track = ({source, target, getTrackProps}) => (
+
+);
+
+const VideoVolume = ({
+ value,
+ onUpdate,
+}) => {
+ const [dragging, setDragging] = useState(false);
+
+ const sliderClassNames = classNames({
+ video_volume__slider: true,
+ dragging,
+ });
+
+ return (
+ setDragging(true)}
+ onSlideEnd={() => setDragging(false)}
+ values={[value]}
+ >
+
+ {({getRailProps}) => }
+
+
+ {({handles, getHandleProps}) => (
+
+ {handles.map(handle => (
+
+ ))}
+
+ )}
+
+
+ {({tracks, getTrackProps}) => (
+
+ {tracks.map(({id, source, target}) => (
+
+ ))}
+
+ )}
+
+
+ );
+};
+
+export default VideoVolume;
diff --git a/public/noise.png b/src/components/noise.png
similarity index 100%
rename from public/noise.png
rename to src/components/noise.png
diff --git a/src/hooks/useChatSwarm.js b/src/hooks/useChatSwarm.js
new file mode 100644
index 0000000..e62e195
--- /dev/null
+++ b/src/hooks/useChatSwarm.js
@@ -0,0 +1,37 @@
+import {useEffect, useState} from 'react';
+import usePubSubSwarm from './usePubSubSwarm';
+import hexToUint8Array from '../hexToUint8Array';
+
+const useChatSwarm = client => {
+ const [consumer] = usePubSubSwarm(client, 'chat');
+ const [messages, setMessages] = useState([]);
+
+ useEffect(() => {
+ if (consumer == null) {
+ return;
+ }
+
+ const handleMessage = message => setMessages(prev => ([
+ ...prev.slice(prev.length > 100 ? 1 : 0),
+ message,
+ ]));
+
+ consumer.on('message', e => console.log(e));
+ consumer.on('message', handleMessage);
+ return () => consumer.removeListener('message', handleMessage);
+ }, [consumer]);
+
+ const sendMessage = message => {
+ if (client.dhtClient) {
+ client.dhtClient.send(
+ hexToUint8Array(client.bootstrap.bootstrapId),
+ 'chat.message',
+ {message},
+ );
+ }
+ };
+
+ return [messages, sendMessage];
+};
+
+export default useChatSwarm;
diff --git a/src/hooks/useIndexSwarm.js b/src/hooks/useIndexSwarm.js
new file mode 100644
index 0000000..77644c0
--- /dev/null
+++ b/src/hooks/useIndexSwarm.js
@@ -0,0 +1,3 @@
+import usePubSubSwarm from './usePubSubSwarm';
+
+const useIndexSwarm = client => usePubSubSwarm(client, 'index');
diff --git a/src/hooks/usePubSubSwarm b/src/hooks/usePubSubSwarm
new file mode 100644
index 0000000..aa3aefc
--- /dev/null
+++ b/src/hooks/usePubSubSwarm
@@ -0,0 +1,37 @@
+import React, {useEffect, useState, useMemo, useRef} from 'react';
+import URI from '../ppspp/uri';
+import DiagnosticMenu from './DiagnosticMenu';
+import SwarmPlayer from './SwarmPlayer';
+import {ChunkedReadStream} from '../chunkedStream';
+import {Client} from '../client';
+import {ConnManager} from '../wrtc';
+import {PubSubConsumer} from '../pubsub';
+import PlayButton from './PlayButton';
+import qs from 'qs';
+import {useTimeout, useAsync} from 'react-use';
+import hexToUint8Array from '../hexToUint8Array';
+import moment from 'moment';
+
+const usePubSubSwarm = (client, name) => {
+ const [swarm, setSwarm] = useState(null);
+ const [consumer, setConsumer] = useState(null);
+
+ useEffect(() => {
+ if (client) {
+ setImmediate(() => {
+ const {uri} = client.bootstrap.swarms.find(desc => desc.name === name);
+ const swarm = client.ppsppClient.joinSwarm(URI.parse(uri));
+ const consumer = new PubSubConsumer(swarm);
+
+ setSwarm(swarm);
+ setConsumer(consumer);
+
+ return () => client.ppsppClient.leaveSwarm(URI.parse(uri));
+ });
+ }
+ }, [client]);
+
+ return [consumer, swarm];
+};
+
+const useIndexSwarm = client => usePubSubSwarm(client, 'index');
diff --git a/src/hooks/usePubSubSwarm.js b/src/hooks/usePubSubSwarm.js
new file mode 100644
index 0000000..95a73c6
--- /dev/null
+++ b/src/hooks/usePubSubSwarm.js
@@ -0,0 +1,28 @@
+
+import {useEffect, useState} from 'react';
+import URI from '../ppspp/uri';
+import {PubSubConsumer} from '../pubsub';
+
+const usePubSubSwarm = (client, name) => {
+ const [swarm, setSwarm] = useState(null);
+ const [consumer, setConsumer] = useState(null);
+
+ useEffect(() => {
+ if (client) {
+ setImmediate(() => {
+ const {uri} = client.bootstrap.swarms.find(desc => desc.name === name);
+ const swarm = client.ppsppClient.joinSwarm(URI.parse(uri));
+ const consumer = new PubSubConsumer(swarm);
+
+ setSwarm(swarm);
+ setConsumer(consumer);
+
+ return () => client.ppsppClient.leaveSwarm(URI.parse(uri));
+ });
+ }
+ }, [client]);
+
+ return [consumer, swarm];
+};
+
+export default usePubSubSwarm;
diff --git a/src/hooks/useQuery.js b/src/hooks/useQuery.js
new file mode 100644
index 0000000..208f645
--- /dev/null
+++ b/src/hooks/useQuery.js
@@ -0,0 +1,10 @@
+
+
+import {useMemo} from 'react';
+import qs from 'qs';
+
+const useQuery = queryString => useMemo(() => {
+ return qs.parse(queryString, {ignoreQueryPrefix: true}) || {};
+}, [queryString]);
+
+export default useQuery;
diff --git a/src/hooks/useSwarmMediaSource.js b/src/hooks/useSwarmMediaSource.js
new file mode 100644
index 0000000..cfa65ac
--- /dev/null
+++ b/src/hooks/useSwarmMediaSource.js
@@ -0,0 +1,72 @@
+import {useState} from 'react';
+import muxjs from 'mux.js';
+import {ChunkedFragmentedReadStream} from '../chunkedStream';
+import {Buffer} from 'buffer';
+
+const useSwarmMediaSource = swarm => {
+ const [mediaSource] = useState(() => {
+ const mediaSource = new MediaSource();
+ mediaSource.addEventListener('sourceopen', handleSourceOpen);
+ return mediaSource;
+ }, []);
+
+ function handleSourceOpen() {
+ const sourceBuffer = mediaSource.addSourceBuffer('video/mp4; codecs="mp4a.40.5,avc1.64001F"');
+ // sourceBuffer.addEventListener('updatestart', e => console.log(e));
+ // sourceBuffer.addEventListener('updateend', e => console.log(e));
+ sourceBuffer.addEventListener('error', e => console.log(e));
+
+ const videoSegments = [];
+ const appendBuffer = newSegment => {
+ if (newSegment !== undefined && (videoSegments.length !== 0 || sourceBuffer.updating)) {
+ videoSegments.push(newSegment);
+ return;
+ }
+
+ if (sourceBuffer.updating) {
+ return;
+ }
+
+ const segment = newSegment || videoSegments.shift();
+ if (segment === undefined) {
+ return;
+ }
+
+ try {
+ sourceBuffer.appendBuffer(segment);
+ } catch (e) {
+ videoSegments.unshift(segment);
+ setImmediate(appendBuffer);
+ }
+ };
+
+ sourceBuffer.addEventListener('updateend', () => appendBuffer());
+
+ const transmuxer = new muxjs.mp4.Transmuxer();
+ let initSet = false;
+ transmuxer.on('data', event => {
+ if (event.type === 'combined') {
+ const buf = initSet
+ ? event.data
+ : Buffer.concat([Buffer.from(event.initSegment), Buffer.from(event.data)]);
+ initSet = true;
+
+ appendBuffer(buf);
+ } else {
+ console.log('unhandled event', event.type);
+ }
+ });
+
+ const stream = new ChunkedFragmentedReadStream(swarm);
+ stream.on('start', data => transmuxer.push(data));
+ stream.on('data', data => transmuxer.push(data));
+ stream.on('end', data => {
+ transmuxer.push(data);
+ transmuxer.flush();
+ });
+ }
+
+ return mediaSource;
+};
+
+export default useSwarmMediaSource;
diff --git a/src/SwarmPlayer.js b/src/hooks/useVideo.js
similarity index 60%
rename from src/SwarmPlayer.js
rename to src/hooks/useVideo.js
index 9d4ada7..40173eb 100644
--- a/src/SwarmPlayer.js
+++ b/src/hooks/useVideo.js
@@ -1,79 +1,4 @@
-import React, {useRef, useEffect, useState} from 'react';
-import muxjs from 'mux.js';
-import {ChunkedFragmentedReadStream} from './chunkedStream';
-import DiagnosticMenu from './DiagnosticMenu';
-import {Buffer} from 'buffer';
-import PlayButton from './PlayButton';
-import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
-import {faSyncAlt} from '@fortawesome/free-solid-svg-icons';
-
-import './SwarmPlayer.scss';
-
-const useSwarmMediaSource = swarm => {
- const [mediaSource] = useState(() => {
- const mediaSource = new MediaSource();
- mediaSource.addEventListener('sourceopen', handleSourceOpen);
- return mediaSource;
- }, []);
-
- function handleSourceOpen() {
- const sourceBuffer = mediaSource.addSourceBuffer('video/mp4; codecs="mp4a.40.5,avc1.64001F"');
- // sourceBuffer.addEventListener('updatestart', e => console.log(e));
- // sourceBuffer.addEventListener('updateend', e => console.log(e));
- sourceBuffer.addEventListener('error', e => console.log(e));
-
- const videoSegments = [];
- const appendBuffer = newSegment => {
- if (newSegment !== undefined && (videoSegments.length !== 0 || sourceBuffer.updating)) {
- videoSegments.push(newSegment);
- return;
- }
-
- if (sourceBuffer.updating) {
- return;
- }
-
- const segment = newSegment || videoSegments.shift();
- if (segment === undefined) {
- return;
- }
-
- try {
- sourceBuffer.appendBuffer(segment);
- } catch (e) {
- videoSegments.unshift(segment);
- setImmediate(appendBuffer);
- }
- };
-
- sourceBuffer.addEventListener('updateend', () => appendBuffer());
-
- const transmuxer = new muxjs.mp4.Transmuxer();
- let initSet = false;
- transmuxer.on('data', event => {
- if (event.type === 'combined') {
- const buf = initSet
- ? event.data
- : Buffer.concat([Buffer.from(event.initSegment), Buffer.from(event.data)]);
- initSet = true;
-
- appendBuffer(buf);
- } else {
- console.log('unhandled event', event.type);
- }
- });
-
- const stream = new ChunkedFragmentedReadStream(swarm);
- stream.on('start', data => transmuxer.push(data));
- stream.on('data', data => transmuxer.push(data));
- stream.on('end', data => {
- transmuxer.push(data);
- transmuxer.flush();
- });
- }
-
- return mediaSource;
-};
+import {useEffect, useRef, useState} from 'react';
export const VideoReadyState = {
// No information is available about the media resource.
@@ -101,6 +26,7 @@ const useVideo = () => {
const [waiting, setWaiting] = useState(true);
const [muted, setMuted] = useState(null);
const [volume, setVolume] = useState(null);
+ const [savedVolume, setSavedVolume] = useState(null);
const [readyState, setReadyState] = useState(0);
useEffect(() => {
@@ -113,6 +39,8 @@ const useVideo = () => {
setPaused(ref.current.paused);
setReadyState(ref.current.readyState);
+ console.log(ref);
+
ref.current.addEventListener('audioprocess', e => console.log(new Date().toUTCString(), 'audioprocess', e));
ref.current.addEventListener('canplay', e => console.log(new Date().toUTCString(), 'canplay', e));
ref.current.addEventListener('canplaythrough', e => console.log(new Date().toUTCString(), 'canplaythrough', e));
@@ -207,6 +135,15 @@ const useVideo = () => {
}
};
+ const mute = () => {
+ setSavedVolume(ref.current.volume);
+ ref.current.volume = 0;
+ };
+
+ const unmute = () => {
+ ref.current.volume = savedVolume || 0.5;
+ };
+
return [
{
readyState,
@@ -234,50 +171,12 @@ const useVideo = () => {
},
{
play,
+ pause: () => ref.current && ref.current.pause(),
+ setVolume: volume => ref.current && (ref.current.volume = volume),
+ mute,
+ unmute,
},
];
};
-const SwarmPlayer = ({swarm, indexSwarm}) =>{
- const [videoState, videoProps, videoControls] = useVideo();
- const mediaSource = useSwarmMediaSource(swarm);
-
- useEffect(() => {
- if (videoProps.ref.current != null && mediaSource != null) {
- videoProps.ref.current.src = URL.createObjectURL(mediaSource);
- videoControls.play();
- }
- }, [videoProps.ref, mediaSource]);
-
- console.log(videoState);
-
- const playButton = (videoState.waiting && videoState.loaded) ? (
-
-
-
- ) : (
-
- );
-
- return (
-
- {/* */}
-
-
- );
-};
-
-export default SwarmPlayer;
+export default useVideo;
diff --git a/src/index.js b/src/index.js
index c91281a..776701f 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,6 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
-import Router from './Router';
+import Router from './components/Router';
import './index.css';
diff --git a/src/ppspp/scheduler.js b/src/ppspp/scheduler.js
index f1595ac..59570f9 100644
--- a/src/ppspp/scheduler.js
+++ b/src/ppspp/scheduler.js
@@ -709,7 +709,7 @@ export class Scheduler {
// TODO: how to pick this... maybe remote discard window size?
// const startBin = this.loadedChunks.max();
// const startBin = this.loadedChunks.max() - 32;
- const startBin = this.lastCompletedBin;
+ const startBin = this.lastCompletedBin - 32;
// bail if no chunks have been loaded yet
if (!isFinite(startBin)) {
diff --git a/yarn.lock b/yarn.lock
index 71b7f63..579d012 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -890,7 +890,7 @@
dependencies:
regenerator-runtime "^0.12.0"
-"@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0":
+"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.4":
version "7.3.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.3.4.tgz#73d12ba819e365fcf7fd152aed56d6df97d21c83"
integrity sha512-IvfvnMdSaLBateu0jfsYIpZTxAc2cKEXEMiezGGN75QcBcecDUKd3PgLAncT0oOgxKy8dd8hrJKj9MfzgfZd6g==
@@ -962,6 +962,66 @@
humps "^2.0.1"
prop-types "^15.5.10"
+"@material-ui/core@^3.9.2":
+ version "3.9.2"
+ resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-3.9.2.tgz#41ed1a470e981d199829eb5d9317a671c66a6f7d"
+ integrity sha512-aukR3mSH3g115St2OnqoeMRtmxzxxx+Mch7pFKRV3Tz3URExBlZwOolimjxKZpG4LGec8HlhREawafLsDzjVWQ==
+ dependencies:
+ "@babel/runtime" "^7.2.0"
+ "@material-ui/system" "^3.0.0-alpha.0"
+ "@material-ui/utils" "^3.0.0-alpha.2"
+ "@types/jss" "^9.5.6"
+ "@types/react-transition-group" "^2.0.8"
+ brcast "^3.0.1"
+ classnames "^2.2.5"
+ csstype "^2.5.2"
+ debounce "^1.1.0"
+ deepmerge "^3.0.0"
+ dom-helpers "^3.2.1"
+ hoist-non-react-statics "^3.2.1"
+ is-plain-object "^2.0.4"
+ jss "^9.8.7"
+ jss-camel-case "^6.0.0"
+ jss-default-unit "^8.0.2"
+ jss-global "^3.0.0"
+ jss-nested "^6.0.1"
+ jss-props-sort "^6.0.0"
+ jss-vendor-prefixer "^7.0.0"
+ normalize-scroll-left "^0.1.2"
+ popper.js "^1.14.1"
+ prop-types "^15.6.0"
+ react-event-listener "^0.6.2"
+ react-transition-group "^2.2.1"
+ recompose "0.28.0 - 0.30.0"
+ warning "^4.0.1"
+
+"@material-ui/icons@^3.0.2":
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/@material-ui/icons/-/icons-3.0.2.tgz#d67a6dd1ec8312d3a88ec97944a63daeef24fe10"
+ integrity sha512-QY/3gJnObZQ3O/e6WjH+0ah2M3MOgLOzCy8HTUoUx9B6dDrS18vP7Ycw3qrDEKlB6q1KNxy6CZHm5FCauWGy2g==
+ dependencies:
+ "@babel/runtime" "^7.2.0"
+ recompose "0.28.0 - 0.30.0"
+
+"@material-ui/system@^3.0.0-alpha.0":
+ version "3.0.0-alpha.2"
+ resolved "https://registry.yarnpkg.com/@material-ui/system/-/system-3.0.0-alpha.2.tgz#096e80c8bb0f70aea435b9e38ea7749ee77b4e46"
+ integrity sha512-odmxQ0peKpP7RQBQ8koly06YhsPzcoVib1vByVPBH4QhwqBXuYoqlCjt02846fYspAqkrWzjxnWUD311EBbxOA==
+ dependencies:
+ "@babel/runtime" "^7.2.0"
+ deepmerge "^3.0.0"
+ prop-types "^15.6.0"
+ warning "^4.0.1"
+
+"@material-ui/utils@^3.0.0-alpha.2":
+ version "3.0.0-alpha.3"
+ resolved "https://registry.yarnpkg.com/@material-ui/utils/-/utils-3.0.0-alpha.3.tgz#836c62ea46f5ffc6f0b5ea05ab814704a86908b1"
+ integrity sha512-rwMdMZptX0DivkqBuC+Jdq7BYTXwqKai5G5ejPpuEDKpWzi1Oxp+LygGw329FrKpuKeiqpcymlqJTjmy+quWng==
+ dependencies:
+ "@babel/runtime" "^7.2.0"
+ prop-types "^15.6.0"
+ react-is "^16.6.3"
+
"@mrmlnc/readdir-enhanced@^2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
@@ -1085,6 +1145,14 @@
resolved "https://registry.yarnpkg.com/@tweenjs/tween.js/-/tween.js-17.3.0.tgz#1cbea14b4b5baa292274c96766fd1b2a56b7ca9f"
integrity sha512-SPkhNj9/wGfbdX2C3B3KhttLQ4iesd+Ny8Dv1RnqF1MFUIqsZz/OJVLzJEHSEl7zheNx70dvqrwfbCFDQ0sWBw==
+"@types/jss@^9.5.6":
+ version "9.5.8"
+ resolved "https://registry.yarnpkg.com/@types/jss/-/jss-9.5.8.tgz#258391f42211c042fc965508d505cbdc579baa5b"
+ integrity sha512-bBbHvjhm42UKki+wZpR89j73ykSXg99/bhuKuYYePtpma3ZAnmeGnl0WxXiZhPGsIfzKwCUkpPC0jlrVMBfRxA==
+ dependencies:
+ csstype "^2.0.0"
+ indefinite-observable "^1.0.1"
+
"@types/node@*":
version "11.10.4"
resolved "https://registry.yarnpkg.com/@types/node/-/node-11.10.4.tgz#3f5fc4f0f322805f009e00ab35a2ff3d6b778e42"
@@ -1100,6 +1168,21 @@
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.1.tgz#48fd98c1561fe718b61733daed46ff115b496e18"
integrity sha512-eqz8c/0kwNi/OEHQfvIuJVLTst3in0e7uTKeuY+WL/zfKn0xVujOTp42bS/vUUokhK5P2BppLd9JXMOMHcgbjA==
+"@types/react-transition-group@^2.0.8":
+ version "2.0.16"
+ resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-2.0.16.tgz#2dcb9e396ab385ee19c4af1c9caa469a14cd042f"
+ integrity sha512-FUJEx2BGJPU1qVQoWd9v7wpOwnCPTWhcE4iTaU5prry9SvwiI11lCXOci8Nz9cM/Fuf650l7Skg6nlVeCYjPFA==
+ dependencies:
+ "@types/react" "*"
+
+"@types/react@*":
+ version "16.8.8"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.8.tgz#4b60a469fd2469f7aa6eaa0f8cfbc51f6d76e662"
+ integrity sha512-xwEvyet96u7WnB96kqY0yY7qxx/pEpU51QeACkKFtrgjjXITQn0oO1iwPEraXVgh10ZFPix7gs1R4OJXF7P5sg==
+ dependencies:
+ "@types/prop-types" "*"
+ csstype "^2.2.0"
+
"@types/react@^16.8.2":
version "16.8.7"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.7.tgz#7b1c0223dd5494f9b4501ad2a69aa6acb350a29b"
@@ -1592,7 +1675,7 @@ arrify@^1.0.1:
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
-asap@~2.0.6:
+asap@~2.0.3, asap@~2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
@@ -2173,6 +2256,11 @@ braces@^2.3.1, braces@^2.3.2:
split-string "^3.0.2"
to-regex "^3.0.1"
+brcast@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/brcast/-/brcast-3.0.1.tgz#6256a8349b20de9eed44257a9b24d71493cd48dd"
+ integrity sha512-eI3yqf9YEqyGl9PCNTR46MGvDylGtaHjalcz6Q3fAPnP/PhpKkkve52vFdfGpwp4VUvK6LUr4TQN+2stCrEwTg==
+
bresenham@0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/bresenham/-/bresenham-0.0.3.tgz#abdab9e5b194e27c757cd314d8444314f299877a"
@@ -2516,6 +2604,11 @@ chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3:
strip-ansi "^3.0.0"
supports-color "^2.0.0"
+change-emitter@^0.1.2:
+ version "0.1.6"
+ resolved "https://registry.yarnpkg.com/change-emitter/-/change-emitter-0.1.6.tgz#e8b2fe3d7f1ab7d69a32199aff91ea6931409515"
+ integrity sha1-6LL+PX8at9aaMhma/5HqaTFAlRU=
+
chardet@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
@@ -2590,7 +2683,7 @@ class-utils@^0.3.5:
isobject "^3.0.0"
static-extend "^0.1.1"
-classnames@^2.2.6:
+classnames@^2.2.5, classnames@^2.2.6:
version "2.2.6"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==
@@ -2897,6 +2990,11 @@ core-js@2.6.4:
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.4.tgz#b8897c062c4d769dd30a0ac5c73976c47f92ea0d"
integrity sha512-05qQ5hXShcqGkPZpXEFLIpxayZscVD2kuMBZewxiIPPEagukO4mqgPA9CWhUvFBJfy3ODdK2p9xyHh7FTU9/7A==
+core-js@^1.0.0:
+ version "1.2.7"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
+ integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=
+
core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.7:
version "2.6.5"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.5.tgz#44bc8d249e7fb2ff5d00e0341a7ffb94fbf67895"
@@ -3137,6 +3235,13 @@ css-url-regex@^1.1.0:
resolved "https://registry.yarnpkg.com/css-url-regex/-/css-url-regex-1.1.0.tgz#83834230cc9f74c457de59eebd1543feeb83b7ec"
integrity sha1-g4NCMMyfdMRX3lnuvRVD/uuDt+w=
+css-vendor@^0.3.8:
+ version "0.3.8"
+ resolved "https://registry.yarnpkg.com/css-vendor/-/css-vendor-0.3.8.tgz#6421cfd3034ce664fe7673972fd0119fc28941fa"
+ integrity sha1-ZCHP0wNM5mT+dnOXL9ARn8KJQfo=
+ dependencies:
+ is-in-browser "^1.0.2"
+
css-what@2.1, css-what@^2.1.2:
version "2.1.3"
resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
@@ -3244,7 +3349,7 @@ cssstyle@^1.0.0:
dependencies:
cssom "0.3.x"
-csstype@^2.2.0, csstype@^2.5.5:
+csstype@^2.0.0, csstype@^2.2.0, csstype@^2.5.2, csstype@^2.5.5:
version "2.6.3"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.3.tgz#b701e5968245bf9b08d54ac83d00b624e622a9fa"
integrity sha512-rINUZXOkcBmoHWEyu7JdHu5JMzkGRoMX4ov9830WNgxf5UYxcBUO0QTKAqeJ5EZfSdlrcJYkC8WwfVW7JYi4yg==
@@ -3268,7 +3373,7 @@ cyclist@~0.2.2:
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640"
integrity sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=
-d3-array@^1.2.0:
+d3-array@^1.2.0, d3-array@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f"
integrity sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==
@@ -3447,6 +3552,11 @@ deep-is@~0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
+deepmerge@^3.0.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-3.2.0.tgz#58ef463a57c08d376547f8869fdc5bcee957f44e"
+ integrity sha512-6+LuZGU7QCNUnAJyX8cIrlzoEgggTM6B7mm+znKOX4t5ltluT9KLjN6g61ECMS0LTsLW7yDpNoxhix5FZcrIow==
+
default-gateway@^2.6.0:
version "2.7.2"
resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-2.7.2.tgz#b7ef339e5e024b045467af403d50348db4642d0f"
@@ -3625,6 +3735,13 @@ dom-converter@^0.2:
dependencies:
utila "~0.4"
+dom-helpers@^3.2.1, dom-helpers@^3.3.1:
+ version "3.4.0"
+ resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8"
+ integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==
+ dependencies:
+ "@babel/runtime" "^7.1.2"
+
dom-serializer@0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0"
@@ -3787,6 +3904,13 @@ encodeurl@~1.0.2:
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
+encoding@^0.1.11:
+ version "0.1.12"
+ resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
+ integrity sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=
+ dependencies:
+ iconv-lite "~0.4.13"
+
end-of-stream@^1.0.0, end-of-stream@^1.1.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
@@ -4361,6 +4485,19 @@ fb-watchman@^2.0.0:
dependencies:
bser "^2.0.0"
+fbjs@^0.8.1:
+ version "0.8.17"
+ resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
+ integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=
+ dependencies:
+ core-js "^1.0.0"
+ isomorphic-fetch "^2.1.1"
+ loose-envify "^1.0.0"
+ object-assign "^4.1.0"
+ promise "^7.1.1"
+ setimmediate "^1.0.5"
+ ua-parser-js "^0.7.18"
+
fenwick-tree@^0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/fenwick-tree/-/fenwick-tree-0.0.0.tgz#4693b8a46c55d3cb5bcfc763e37a42718424e0a1"
@@ -5116,11 +5253,18 @@ hoek@4.x.x:
resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb"
integrity sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==
-hoist-non-react-statics@^2.5.0:
+hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.0:
version "2.5.5"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47"
integrity sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==
+hoist-non-react-statics@^3.2.1:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz#b09178f0122184fb95acf525daaecb4d8f45958b"
+ integrity sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==
+ dependencies:
+ react-is "^16.7.0"
+
home-or-tmp@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
@@ -5304,7 +5448,7 @@ iconv-lite@0.4.23:
dependencies:
safer-buffer ">= 2.1.2 < 3"
-iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4:
+iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
@@ -5433,6 +5577,13 @@ in-publish@^2.0.0:
resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.0.tgz#e20ff5e3a2afc2690320b6dc552682a9c7fadf51"
integrity sha1-4g/146KvwmkDILbcVSaCqcf631E=
+indefinite-observable@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/indefinite-observable/-/indefinite-observable-1.0.2.tgz#0a328793ab2385d4b9dca23eaab4afe6936a73f8"
+ integrity sha512-Mps0898zEduHyPhb7UCgNmfzlqNZknVmaFz5qzr0mm04YQ5FGLhAyK/dJ+NaRxGyR6juQXIxh5Ev0xx+qq0nYA==
+ dependencies:
+ symbol-observable "1.2.0"
+
indent-string@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80"
@@ -5762,6 +5913,11 @@ is-glob@^4.0.0:
dependencies:
is-extglob "^2.1.1"
+is-in-browser@^1.0.2, is-in-browser@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835"
+ integrity sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU=
+
is-installed-globally@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80"
@@ -5877,7 +6033,7 @@ is-root@2.0.0:
resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.0.0.tgz#838d1e82318144e5a6f77819d90207645acc7019"
integrity sha512-F/pJIk8QD6OX5DNhRB7hWamLsUilmkDGho48KbgZ6xg/lmAZXHxzXQ91jzB3yRSw5kdQGGGc4yz8HYhTYIMWPg==
-is-stream@^1.0.0, is-stream@^1.1.0:
+is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
@@ -5950,6 +6106,14 @@ isobject@^3.0.0, isobject@^3.0.1:
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
+isomorphic-fetch@^2.1.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
+ integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=
+ dependencies:
+ node-fetch "^1.0.1"
+ whatwg-fetch ">=0.10.0"
+
isstream@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
@@ -6534,6 +6698,51 @@ jsprim@^1.2.2:
json-schema "0.2.3"
verror "1.10.0"
+jss-camel-case@^6.0.0:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/jss-camel-case/-/jss-camel-case-6.1.0.tgz#ccb1ff8d6c701c02a1fed6fb6fb6b7896e11ce44"
+ integrity sha512-HPF2Q7wmNW1t79mCqSeU2vdd/vFFGpkazwvfHMOhPlMgXrJDzdj9viA2SaHk9ZbD5pfL63a8ylp4++irYbbzMQ==
+ dependencies:
+ hyphenate-style-name "^1.0.2"
+
+jss-default-unit@^8.0.2:
+ version "8.0.2"
+ resolved "https://registry.yarnpkg.com/jss-default-unit/-/jss-default-unit-8.0.2.tgz#cc1e889bae4c0b9419327b314ab1c8e2826890e6"
+ integrity sha512-WxNHrF/18CdoAGw2H0FqOEvJdREXVXLazn7PQYU7V6/BWkCV0GkmWsppNiExdw8dP4TU1ma1dT9zBNJ95feLmg==
+
+jss-global@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/jss-global/-/jss-global-3.0.0.tgz#e19e5c91ab2b96353c227e30aa2cbd938cdaafa2"
+ integrity sha512-wxYn7vL+TImyQYGAfdplg7yaxnPQ9RaXY/cIA8hawaVnmmWxDHzBK32u1y+RAvWboa3lW83ya3nVZ/C+jyjZ5Q==
+
+jss-nested@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/jss-nested/-/jss-nested-6.0.1.tgz#ef992b79d6e8f63d939c4397b9d99b5cbbe824ca"
+ integrity sha512-rn964TralHOZxoyEgeq3hXY8hyuCElnvQoVrQwKHVmu55VRDd6IqExAx9be5HgK0yN/+hQdgAXQl/GUrBbbSTA==
+ dependencies:
+ warning "^3.0.0"
+
+jss-props-sort@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/jss-props-sort/-/jss-props-sort-6.0.0.tgz#9105101a3b5071fab61e2d85ea74cc22e9b16323"
+ integrity sha512-E89UDcrphmI0LzmvYk25Hp4aE5ZBsXqMWlkFXS0EtPkunJkRr+WXdCNYbXbksIPnKlBenGB9OxzQY+mVc70S+g==
+
+jss-vendor-prefixer@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/jss-vendor-prefixer/-/jss-vendor-prefixer-7.0.0.tgz#0166729650015ef19d9f02437c73667231605c71"
+ integrity sha512-Agd+FKmvsI0HLcYXkvy8GYOw3AAASBUpsmIRvVQheps+JWaN892uFOInTr0DRydwaD91vSSUCU4NssschvF7MA==
+ dependencies:
+ css-vendor "^0.3.8"
+
+jss@^9.8.7:
+ version "9.8.7"
+ resolved "https://registry.yarnpkg.com/jss/-/jss-9.8.7.tgz#ed9763fc0f2f0260fc8260dac657af61e622ce05"
+ integrity sha512-awj3XRZYxbrmmrx9LUSj5pXSUfm12m8xzi/VKeqI1ZwWBtQ0kVPTs3vYs32t4rFw83CgFDukA8wKzOE9sMQnoQ==
+ dependencies:
+ is-in-browser "^1.1.3"
+ symbol-observable "^1.1.0"
+ warning "^3.0.0"
+
jsx-ast-utils@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz#e801b1b39985e20fffc87b40e3748080e2dcac7f"
@@ -7448,6 +7657,14 @@ node-emoji@^1.4.1:
dependencies:
lodash.toarray "^4.4.0"
+node-fetch@^1.0.1:
+ version "1.7.3"
+ resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
+ integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==
+ dependencies:
+ encoding "^0.1.11"
+ is-stream "^1.0.1"
+
node-forge@0.7.5:
version "0.7.5"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.5.tgz#6c152c345ce11c52f465c2abd957e8639cd674df"
@@ -7672,6 +7889,11 @@ normalize-range@^0.1.2:
resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=
+normalize-scroll-left@^0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/normalize-scroll-left/-/normalize-scroll-left-0.1.2.tgz#6b79691ba79eb5fb107fa5edfbdc06b55caee2aa"
+ integrity sha512-F9YMRls0zCF6BFIE2YnXDRpHPpfd91nOIaNdDgrx5YMoPLo8Wqj+6jNXHQsYBavJeXP4ww8HCt0xQAKc5qk2Fg==
+
normalize-url@^1.0.0:
version "1.9.1"
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c"
@@ -8321,6 +8543,11 @@ polished@^2.3.3:
dependencies:
"@babel/runtime" "^7.2.0"
+popper.js@^1.14.1:
+ version "1.14.7"
+ resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.14.7.tgz#e31ec06cfac6a97a53280c3e55e4e0c860e7738e"
+ integrity sha512-4q1hNvoUre/8srWsH7hnoSJ5xVmIL4qgz+s4qf2TnJIMyZFUFMGH+9vE7mXynAlHSZ/NdTmmow86muD0myUkVQ==
+
portfinder@^1.0.9:
version "1.0.20"
resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.20.tgz#bea68632e54b2e13ab7b0c4775e9b41bf270e44a"
@@ -9031,6 +9258,13 @@ promise@8.0.2:
dependencies:
asap "~2.0.6"
+promise@^7.1.1:
+ version "7.3.1"
+ resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
+ integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==
+ dependencies:
+ asap "~2.0.3"
+
prompts@^0.1.9:
version "0.1.14"
resolved "https://registry.yarnpkg.com/prompts/-/prompts-0.1.14.tgz#a8e15c612c5c9ec8f8111847df3337c9cbd443b2"
@@ -9039,7 +9273,7 @@ prompts@^0.1.9:
kleur "^2.0.1"
sisteransi "^0.1.1"
-prop-types@^15.5.10, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.0:
+prop-types@^15.5.10, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.0, prop-types@^15.7.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@@ -9249,6 +9483,16 @@ react-app-rewired@^2.1.0:
dotenv "^6.2.0"
semver "^5.6.0"
+react-compound-slider@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/react-compound-slider/-/react-compound-slider-1.2.1.tgz#0f127a9944cf860f2041b70035e96a2f1d39d2c6"
+ integrity sha512-N1cwnSkjuiXvVWo8kNDcQ/jiQK2KrybEhnf72kQYL0OcLc36WrlAu9xVo8Yz6c3xWCJI+WKFcFjmitI3FQKn9g==
+ dependencies:
+ "@babel/runtime" "^7.3.4"
+ d3-array "^1.2.4"
+ prop-types "^15.7.2"
+ warning "^3.0.0"
+
react-dev-utils@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-7.0.3.tgz#f1316cfffd792fd41b0c28ad5db86c1d74484d6f"
@@ -9294,6 +9538,15 @@ react-error-overlay@^5.1.3:
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-5.1.3.tgz#16fcbde75ed4dc6161dc6dc959b48e92c6ffa9ad"
integrity sha512-GoqeM3Xadie7XUApXOjkY3Qhs8RkwB/Za4WMedBGrOKH1eTuKGyoAECff7jiVonJchOx6KZ9i8ILO5XIoHB+Tg==
+react-event-listener@^0.6.2:
+ version "0.6.6"
+ resolved "https://registry.yarnpkg.com/react-event-listener/-/react-event-listener-0.6.6.tgz#758f7b991cad9086dd39fd29fad72127e1d8962a"
+ integrity sha512-+hCNqfy7o9wvO6UgjqFmBzARJS7qrNoda0VqzvOuioEpoEXKutiKuv92dSz6kP7rYLmyHPyYNLesi5t/aH1gfw==
+ dependencies:
+ "@babel/runtime" "^7.2.0"
+ prop-types "^15.6.0"
+ warning "^4.0.1"
+
react-force-graph-3d@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/react-force-graph-3d/-/react-force-graph-3d-1.6.1.tgz#d197027f10ff5389093ab5ffc4670e64d79d092f"
@@ -9303,6 +9556,11 @@ react-force-graph-3d@^1.6.1:
prop-types "^15.7.0"
react-kapsule "^1.4.1"
+react-is@^16.6.3, react-is@^16.7.0:
+ version "16.8.4"
+ resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.4.tgz#90f336a68c3a29a096a3d648ab80e87ec61482a2"
+ integrity sha512-PVadd+WaUDOAciICm/J1waJaSvgq+4rHE/K70j0PFqKhkTBsPv/82UGQJNXAngz1fOQLLxI6z1sEDmJDQhCTAA==
+
react-is@^16.8.1:
version "16.8.3"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.3.tgz#4ad8b029c2a718fc0cfc746c8d4e1b7221e5387d"
@@ -9313,6 +9571,11 @@ react-kapsule@^1.4.1:
resolved "https://registry.yarnpkg.com/react-kapsule/-/react-kapsule-1.4.1.tgz#4dfef562d0757f43933e9959f4b57e418881bd8a"
integrity sha512-YZRaxMWaotkWlHtgNgOxucDG+ulkJPAgBmw+RZLPNNm6WOofeW5fdXiR3ubKzhQZ8JdT0pNB6+fR3NK0T4Kong==
+react-lifecycles-compat@^3.0.2, react-lifecycles-compat@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
+ integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
+
react-router-dom@^4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.3.1.tgz#4c2619fc24c4fa87c9fd18f4fb4a43fe63fbd5c6"
@@ -9393,6 +9656,16 @@ react-scripts@2.1.5:
optionalDependencies:
fsevents "1.2.4"
+react-transition-group@^2.2.1:
+ version "2.6.1"
+ resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.6.1.tgz#abf4a95e2f13fb9ba83a970a896fedbc5c4856a2"
+ integrity sha512-9DHwCy0aOYEe35frlEN68N9ut/THDQBLnVoQuKTvzF4/s3tk7lqkefCqxK2Nv96fOh6JXk6tQtliygk6tl3bQA==
+ dependencies:
+ dom-helpers "^3.3.1"
+ loose-envify "^1.4.0"
+ prop-types "^15.6.2"
+ react-lifecycles-compat "^3.0.4"
+
react-use@^5.5.6:
version "5.5.6"
resolved "https://registry.yarnpkg.com/react-use/-/react-use-5.5.6.tgz#cf7f07bc323ab23a66aef0cb083d164ff08093cc"
@@ -9519,6 +9792,18 @@ rebound@^0.1.0:
resolved "https://registry.yarnpkg.com/rebound/-/rebound-0.1.0.tgz#0638c61a93666bb515a58a03e1cfb34021e88b72"
integrity sha1-BjjGGpNma7UVpYoD4c+zQCHoi3I=
+"recompose@0.28.0 - 0.30.0":
+ version "0.30.0"
+ resolved "https://registry.yarnpkg.com/recompose/-/recompose-0.30.0.tgz#82773641b3927e8c7d24a0d87d65aeeba18aabd0"
+ integrity sha512-ZTrzzUDa9AqUIhRk4KmVFihH0rapdCSMFXjhHbNrjAWxBuUD/guYlyysMnuHjlZC/KRiOKRtB4jf96yYSkKE8w==
+ dependencies:
+ "@babel/runtime" "^7.0.0"
+ change-emitter "^0.1.2"
+ fbjs "^0.8.1"
+ hoist-non-react-statics "^2.3.1"
+ react-lifecycles-compat "^3.0.2"
+ symbol-observable "^1.0.4"
+
reconnecting-websocket@^3.2.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/reconnecting-websocket/-/reconnecting-websocket-3.2.2.tgz#8097514e926e9855e03c39e76efa2e3d1f371bee"
@@ -10088,7 +10373,7 @@ set-value@^2.0.0:
is-plain-object "^2.0.3"
split-string "^3.0.1"
-setimmediate@^1.0.4:
+setimmediate@^1.0.4, setimmediate@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
@@ -10712,6 +10997,11 @@ svgo@^1.0.0, svgo@^1.1.1:
unquote "~1.1.1"
util.promisify "~1.0.0"
+symbol-observable@1.2.0, symbol-observable@^1.0.4, symbol-observable@^1.1.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
+ integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
+
symbol-tree@^3.2.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6"
@@ -11116,6 +11406,11 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
+ua-parser-js@^0.7.18:
+ version "0.7.19"
+ resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.19.tgz#94151be4c0a7fb1d001af7022fdaca4642659e4b"
+ integrity sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ==
+
uglify-js@3.4.x, uglify-js@^3.1.4:
version "3.4.9"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3"
@@ -11331,6 +11626,11 @@ use-events@^1.2.0:
dependencies:
resize-observer-polyfill "1.5.1"
+use-fullscreen@^0.0.5:
+ version "0.0.5"
+ resolved "https://registry.yarnpkg.com/use-fullscreen/-/use-fullscreen-0.0.5.tgz#ad18151cf5f91550ffabeb6740a6a469be9eeee2"
+ integrity sha512-t1UfX7jJOR5Jso3NiLn6j1J33e//Jmcp03It0IxrCI5QJ8E48TNIu6q1GaUciCrmiZmyb3EhnDlSPi5kVbgcwA==
+
use-onclickoutside@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/use-onclickoutside/-/use-onclickoutside-0.3.0.tgz#a7c6bb68c8643c9a3437e34840d0cb2b96ae32b1"
@@ -11638,7 +11938,7 @@ whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3:
dependencies:
iconv-lite "0.4.24"
-whatwg-fetch@3.0.0:
+whatwg-fetch@3.0.0, whatwg-fetch@>=0.10.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb"
integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==