diff --git a/src/components/LoadingOverlay.js b/src/components/LoadingOverlay.js index eca0d0b..f6a30be 100644 --- a/src/components/LoadingOverlay.js +++ b/src/components/LoadingOverlay.js @@ -1,9 +1,9 @@ import React from "react"; -import { Box } from "theme-ui"; +import { Box, Progress } from "theme-ui"; import Spinner from "./Spinner"; -function LoadingOverlay() { +function LoadingOverlay({ progress }) { return ( + {progress && ( + + )} ); } diff --git a/src/components/map/Map.js b/src/components/map/Map.js index b7a1119..198f826 100644 --- a/src/components/map/Map.js +++ b/src/components/map/Map.js @@ -36,7 +36,7 @@ function Map({ disabledTokens, }) { const { tokensById } = useContext(TokenDataContext); - const { isLoading } = useContext(MapLoadingContext); + const { isLoading, loadingProgress } = useContext(MapLoadingContext); const gridX = map && map.gridX; const gridY = map && map.gridY; @@ -299,7 +299,7 @@ function Map({ {tokenMenu} {tokenDragOverlay} - {isLoading && } + {isLoading && } } selectedToolId={selectedToolId} diff --git a/src/contexts/MapLoadingContext.js b/src/contexts/MapLoadingContext.js index 13ec1d7..12f0f4a 100644 --- a/src/contexts/MapLoadingContext.js +++ b/src/contexts/MapLoadingContext.js @@ -1,4 +1,5 @@ import React, { useState } from "react"; +import { omit, isEmpty } from "../helpers/shared"; const MapLoadingContext = React.createContext(); @@ -13,12 +14,36 @@ export function MapLoadingProvider({ children }) { setLoadingAssetCount((prevLoadingAssets) => prevLoadingAssets - 1); } + const [assetProgress, setAssetProgress] = useState({}); + function assetProgressUpdate({ id, count, total }) { + if (count === total) { + setAssetProgress(omit(assetProgress, [id])); + } else { + setAssetProgress((prevAssetProgress) => ({ + ...prevAssetProgress, + [id]: { count, total }, + })); + } + } + const isLoading = loadingAssetCount > 0; + let loadingProgress = null; + if (!isEmpty(assetProgress)) { + let total = 0; + let count = 0; + for (let progress of Object.values(assetProgress)) { + total += progress.total; + count += progress.count; + } + loadingProgress = count / total; + } const value = { assetLoadStart, assetLoadFinish, isLoading, + assetProgressUpdate, + loadingProgress, }; return ( diff --git a/src/helpers/Peer.js b/src/helpers/Peer.js index 5a5a385..32984eb 100644 --- a/src/helpers/Peer.js +++ b/src/helpers/Peer.js @@ -29,6 +29,12 @@ class Peer extends SimplePeer { chunk.count++; this.currentChunks[unpacked.id] = chunk; + this.emit("dataProgress", { + id: unpacked.id, + count: chunk.count, + total: chunk.total, + }); + // All chunks have been loaded if (chunk.count === chunk.total) { // Merge chunks with a blob diff --git a/src/helpers/useSession.js b/src/helpers/useSession.js index 22076f6..282ceba 100644 --- a/src/helpers/useSession.js +++ b/src/helpers/useSession.js @@ -13,6 +13,7 @@ function useSession( onPeerConnected, onPeerDisconnected, onPeerData, + onPeerDataProgress, onPeerTrackAdded, onPeerTrackRemoved, onPeerError @@ -76,6 +77,10 @@ function useSession( onPeerData && onPeerData({ peer, data }); } + function handleDataProgress({ id, count, total }) { + onPeerDataProgress && onPeerDataProgress({ id, count, total }); + } + function handleTrack(track, stream) { onPeerTrackAdded && onPeerTrackAdded({ peer, track, stream }); track.addEventListener("mute", () => { @@ -96,6 +101,7 @@ function useSession( peer.connection.on("signal", handleSignal); peer.connection.on("connect", handleConnect); peer.connection.on("dataComplete", handleDataComplete); + peer.connection.on("dataProgress", handleDataProgress); peer.connection.on("track", handleTrack); peer.connection.on("close", handleClose); peer.connection.on("error", handleError); @@ -105,6 +111,7 @@ function useSession( handleSignal, handleConnect, handleDataComplete, + handleDataProgress, handleTrack, handleClose, handleError, @@ -118,6 +125,7 @@ function useSession( handleSignal, handleConnect, handleDataComplete, + handleDataProgress, handleTrack, handleClose, handleError, @@ -125,6 +133,7 @@ function useSession( peer.connection.off("signal", handleSignal); peer.connection.off("connect", handleConnect); peer.connection.off("dataComplete", handleDataComplete); + peer.connection.off("dataProgress", handleDataProgress); peer.connection.off("track", handleTrack); peer.connection.off("close", handleClose); peer.connection.off("error", handleError); @@ -135,6 +144,7 @@ function useSession( onPeerConnected, onPeerDisconnected, onPeerData, + onPeerDataProgress, onPeerTrackAdded, onPeerTrackRemoved, onPeerError, diff --git a/src/routes/Game.js b/src/routes/Game.js index f479a7a..9acfdd0 100644 --- a/src/routes/Game.js +++ b/src/routes/Game.js @@ -33,13 +33,16 @@ function Game() { const { authenticationStatus, userId, nickname, setNickname } = useContext( AuthContext ); - const { assetLoadStart, assetLoadFinish } = useContext(MapLoadingContext); + const { assetLoadStart, assetLoadFinish, assetProgressUpdate } = useContext( + MapLoadingContext + ); const { peers, socket, connected } = useSession( gameId, handlePeerConnected, handlePeerDisconnected, handlePeerData, + handlePeerDataProgress, handlePeerTrackAdded, handlePeerTrackRemoved, handlePeerError @@ -322,7 +325,6 @@ function Game() { if (cachedMap && cachedMap.lastModified === newMap.lastModified) { setCurrentMap(cachedMap); } else { - assetLoadStart(); peer.connection.send({ id: "mapRequest", data: newMap.id }); } } else { @@ -336,7 +338,6 @@ function Game() { } // A new map response with a file attached if (data.id === "mapResponse") { - assetLoadFinish(); if (data.data && data.data.type === "file") { const newMap = { ...data.data, file: data.data.file }; putMap(newMap).then(() => { @@ -357,7 +358,6 @@ function Game() { !cachedToken || cachedToken.lastModified !== newToken.lastModified ) { - assetLoadStart(); peer.connection.send({ id: "tokenRequest", data: newToken.id, @@ -370,7 +370,6 @@ function Game() { peer.connection.send({ id: "tokenResponse", data: token }); } if (data.id === "tokenResponse") { - assetLoadFinish(); const newToken = data.data; if (newToken && newToken.type === "file") { putToken(newToken); @@ -414,6 +413,16 @@ function Game() { } } + function handlePeerDataProgress({ id, total, count }) { + if (count === 1) { + assetLoadStart(); + } + if (total === count) { + assetLoadFinish(); + } + assetProgressUpdate({ id, total, count }); + } + function handlePeerDisconnected(peer) { setPartyNicknames((prevNicknames) => omit(prevNicknames, [peer.id])); }