Refactor error banners and add new slimlined reconnect and offline banners

This commit is contained in:
Mitchell McCaffrey 2021-03-18 14:02:13 +11:00
parent 83d80fbbbd
commit 815cc3aead
12 changed files with 155 additions and 63 deletions

View File

@ -2,7 +2,13 @@ import React from "react";
import Modal from "react-modal"; import Modal from "react-modal";
import { useThemeUI, Close } from "theme-ui"; import { useThemeUI, Close } from "theme-ui";
function Banner({ isOpen, onRequestClose, children, allowClose }) { function Banner({
isOpen,
onRequestClose,
children,
allowClose,
backgroundColor,
}) {
const { theme } = useThemeUI(); const { theme } = useThemeUI();
return ( return (
@ -12,7 +18,7 @@ function Banner({ isOpen, onRequestClose, children, allowClose }) {
style={{ style={{
overlay: { bottom: "0", top: "initial", zIndex: 2000 }, overlay: { bottom: "0", top: "initial", zIndex: 2000 },
content: { content: {
backgroundColor: theme.colors.highlight, backgroundColor: backgroundColor || theme.colors.highlight,
color: "hsl(210, 50%, 96%)", color: "hsl(210, 50%, 96%)",
top: "initial", top: "initial",
left: "50%", left: "50%",
@ -22,7 +28,7 @@ function Banner({ isOpen, onRequestClose, children, allowClose }) {
border: "none", border: "none",
padding: "8px", padding: "8px",
margin: "8px", margin: "8px",
paddingRight: "24px", paddingRight: allowClose ? "24px" : "8px",
maxWidth: "500px", maxWidth: "500px",
transform: "translateX(-50%)", transform: "translateX(-50%)",
}, },

View File

@ -0,0 +1,18 @@
import React from "react";
import { Box, Text } from "theme-ui";
import Banner from "./Banner";
function ErrorBanner({ error, onRequestClose }) {
return (
<Banner isOpen={!!error} onRequestClose={onRequestClose}>
<Box p={1}>
<Text as="p" variant="body2">
Error: {error && error.message}
</Text>
</Box>
</Banner>
);
}
export default ErrorBanner;

View File

@ -0,0 +1,33 @@
import React from "react";
import { Flex } from "theme-ui";
import Banner from "./Banner";
import OfflineIcon from "../../icons/OfflineIcon";
function OfflineBanner({ isOpen }) {
return (
<Banner
isOpen={isOpen}
onRequestClose={() => {}}
allowClose={false}
backgroundColor="transparent"
>
<Flex
sx={{
width: "28px",
height: "28px",
borderRadius: "28px",
alignItems: "center",
justifyContent: "center",
}}
bg="overlay"
title="Unable to connect to game, refresh to reconnect."
aria-label="Unable to connect to game, refresh to reconnect."
>
<OfflineIcon />
</Flex>
</Banner>
);
}
export default OfflineBanner;

View File

@ -0,0 +1,33 @@
import React from "react";
import { Flex } from "theme-ui";
import Banner from "./Banner";
import ReconnectingIcon from "../../icons/ReconnectingIcon";
function ReconnectBanner({ isOpen }) {
return (
<Banner
isOpen={isOpen}
onRequestClose={() => {}}
allowClose={false}
backgroundColor="transparent"
>
<Flex
sx={{
width: "28px",
height: "28px",
borderRadius: "28px",
alignItems: "center",
justifyContent: "center",
}}
bg="overlay"
title="Disconnected. Attempting to reconnect..."
aria-label="Disconnected. Attempting to reconnect..."
>
<ReconnectingIcon />
</Flex>
</Banner>
);
}
export default ReconnectBanner;

View File

@ -1,5 +1,4 @@
import React, { useRef, useEffect, useState } from "react"; import React, { useRef, useEffect, useState } from "react";
import { Box, Text } from "theme-ui";
import { Engine } from "@babylonjs/core/Engines/engine"; import { Engine } from "@babylonjs/core/Engines/engine";
import { Scene } from "@babylonjs/core/scene"; import { Scene } from "@babylonjs/core/scene";
import { Vector3, Color4, Matrix } from "@babylonjs/core/Maths/math"; import { Vector3, Color4, Matrix } from "@babylonjs/core/Maths/math";
@ -19,7 +18,7 @@ import ReactResizeDetector from "react-resize-detector";
import usePreventTouch from "../../hooks/usePreventTouch"; import usePreventTouch from "../../hooks/usePreventTouch";
import Banner from "../Banner"; import ErrorBanner from "../banner/ErrorBanner";
const diceThrowSpeed = 2; const diceThrowSpeed = 2;
@ -166,13 +165,7 @@ function DiceInteraction({ onSceneMount, onPointerDown, onPointerUp }) {
style={{ outline: "none" }} style={{ outline: "none" }}
/> />
</ReactResizeDetector> </ReactResizeDetector>
<Banner isOpen={!!error} onRequestClose={() => setError()}> <ErrorBanner error={error} onRequestClose={() => setError()} />
<Box p={1}>
<Text as="p" variant="body2">
Error: {error && error.message}
</Text>
</Box>
</Banner>
</div> </div>
); );
} }

View File

@ -3,7 +3,7 @@ import { Text, IconButton, Box, Flex } from "theme-ui";
import StreamMuteIcon from "../../icons/StreamMuteIcon"; import StreamMuteIcon from "../../icons/StreamMuteIcon";
import Banner from "../Banner"; import Banner from "../banner/Banner";
import Slider from "../Slider"; import Slider from "../Slider";
function Stream({ stream, nickname }) { function Stream({ stream, nickname }) {

View File

@ -1,7 +1,6 @@
import React, { useState, useEffect, useContext } from "react"; import React, { useState, useEffect, useContext } from "react";
import { Box, Text } from "theme-ui";
import Banner from "../components/Banner"; import ErrorBanner from "../components/banner/ErrorBanner";
import { getDatabase } from "../database"; import { getDatabase } from "../database";
@ -64,16 +63,10 @@ export function DatabaseProvider({ children }) {
<DatabaseContext.Provider value={value}> <DatabaseContext.Provider value={value}>
<> <>
{children} {children}
<Banner <ErrorBanner
isOpen={!!databaseError} error={databaseError}
onRequestClose={() => setDatabaseError()} onRequestClose={() => setDatabaseError()}
> />
<Box p={1}>
<Text as="p" variant="body2">
{databaseError && databaseError.message}
</Text>
</Box>
</Banner>
</> </>
</DatabaseContext.Provider> </DatabaseContext.Provider>
); );

18
src/icons/OfflineIcon.js Normal file
View File

@ -0,0 +1,18 @@
import React from "react";
function PointerToolIcon() {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
height="24"
viewBox="0 0 24 24"
width="24"
fill="currentcolor"
>
<path d="M0 0h24v24H0V0zm0 0h24v24H0V0z" fill="none" />
<path d="M10 5.74v-.19c0-.68-.71-1.11-1.32-.82-.19.09-.36.2-.54.3L9.6 6.49c.24-.18.4-.45.4-.75zM20 12c0-2.21-.91-4.2-2.36-5.64l1.51-1.51c.31-.31.09-.85-.36-.85H14v4.79c0 .45.54.67.85.35l1.39-1.39C17.32 8.85 18 10.34 18 12c0 .85-.18 1.66-.5 2.39l1.48 1.48C19.62 14.72 20 13.41 20 12zM3.57 4.7c-.39.39-.39 1.02 0 1.41l1.65 1.65C4.45 9 4 10.44 4 12c0 2.21.91 4.2 2.36 5.64l-1.51 1.51c-.31.31-.09.85.36.85H9.5c.28 0 .5-.22.5-.5v-4.29c0-.45-.54-.67-.85-.35l-1.39 1.39C6.68 15.15 6 13.66 6 12c0-1 .26-1.93.69-2.76l8.07 8.07c-.01.02-.01.02-.01.04-.43.12-.75.48-.75.91v.18c0 .68.71 1.11 1.32.82.31-.14.61-.31.9-.49l1.87 1.87c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L4.98 4.7c-.39-.39-1.02-.39-1.41 0z" />
</svg>
);
}
export default PointerToolIcon;

View File

@ -0,0 +1,28 @@
import React from "react";
function ReconnectingIcon() {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
height="24"
viewBox="0 0 24 24"
width="24"
fill="currentcolor"
>
<path d="M.01 0h24v24h-24V0z" fill="none" />
<path d="M12 4V2.21c0-.45-.54-.67-.85-.35l-2.8 2.79c-.2.2-.2.51 0 .71l2.79 2.79c.32.31.86.09.86-.36V6c3.31 0 6 2.69 6 6 0 .79-.15 1.56-.44 2.25-.15.36-.04.77.23 1.04.51.51 1.37.33 1.64-.34.37-.91.57-1.91.57-2.95 0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-.79.15-1.56.44-2.25.15-.36.04-.77-.23-1.04-.51-.51-1.37-.33-1.64.34C4.2 9.96 4 10.96 4 12c0 4.42 3.58 8 8 8v1.79c0 .45.54.67.85.35l2.79-2.79c.2-.2.2-.51 0-.71l-2.79-2.79c-.31-.31-.85-.09-.85.36V18z">
<animateTransform
attributeName="transform"
attributeType="XML"
type="rotate"
from="360 12 12"
to="0 12 12"
dur="2s"
repeatCount="indefinite"
/>
</path>
</svg>
);
}
export default ReconnectingIcon;

View File

@ -8,7 +8,7 @@ import { useToasts } from "react-toast-notifications";
import Modal from "../components/Modal"; import Modal from "../components/Modal";
import LoadingOverlay from "../components/LoadingOverlay"; import LoadingOverlay from "../components/LoadingOverlay";
import LoadingBar from "../components/LoadingBar"; import LoadingBar from "../components/LoadingBar";
import Banner from "../components/Banner"; import ErrorBanner from "../components/banner/ErrorBanner";
import { useAuth } from "../contexts/AuthContext"; import { useAuth } from "../contexts/AuthContext";
@ -271,13 +271,7 @@ function ImportExportModal({ isOpen, onRequestClose }) {
/> />
</Box> </Box>
)} )}
<Banner isOpen={!!error} onRequestClose={() => setError()}> <ErrorBanner error={error} onRequestClose={() => setError()} />
<Box p={1}>
<Text as="p" variant="body2">
Error: {error && error.message}
</Text>
</Box>
</Banner>
<SelectDataModal <SelectDataModal
isOpen={showImportSelector} isOpen={showImportSelector}
onRequestClose={handleImportSelectorClose} onRequestClose={handleImportSelectorClose}

View File

@ -12,7 +12,7 @@ import {
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
import Footer from "../components/Footer"; import Footer from "../components/Footer";
import Banner from "../components/Banner"; import ErrorBanner from "../components/banner/ErrorBanner";
import LoadingOverlay from "../components/LoadingOverlay"; import LoadingOverlay from "../components/LoadingOverlay";
import { logError } from "../helpers/logging"; import { logError } from "../helpers/logging";
@ -159,13 +159,7 @@ function Donate() {
</Flex> </Flex>
<Footer /> <Footer />
{loading && <LoadingOverlay />} {loading && <LoadingOverlay />}
<Banner isOpen={!!error} onRequestClose={() => setError(null)}> <ErrorBanner error={error} onRequestClose={() => setError()} />
<Box p={1}>
<Text as="p" variant="body2">
{error}
</Text>
</Box>
</Banner>
</Flex> </Flex>
); );
} }

View File

@ -2,7 +2,9 @@ import React, { useState, useEffect, useRef } from "react";
import { Flex, Box, Text } from "theme-ui"; import { Flex, Box, Text } from "theme-ui";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import Banner from "../components/Banner"; import Banner from "../components/banner/Banner";
import ReconnectBanner from "../components/banner/ReconnectBanner";
import OfflineBanner from "../components/banner/OfflineBanner";
import LoadingOverlay from "../components/LoadingOverlay"; import LoadingOverlay from "../components/LoadingOverlay";
import Link from "../components/Link"; import Link from "../components/Link";
import MapLoadingOverlay from "../components/map/MapLoadingOverlay"; import MapLoadingOverlay from "../components/map/MapLoadingOverlay";
@ -125,28 +127,8 @@ function Game() {
</Text> </Text>
</Box> </Box>
</Banner> </Banner>
<Banner <OfflineBanner isOpen={sessionStatus === "offline"} />
isOpen={sessionStatus === "offline"} <ReconnectBanner isOpen={sessionStatus === "reconnecting"} />
onRequestClose={() => {}}
allowClose={false}
>
<Box p={1}>
<Text as="p" variant="body2">
Unable to connect to game, refresh to reconnect.
</Text>
</Box>
</Banner>
<Banner
isOpen={sessionStatus === "reconnecting"}
onRequestClose={() => {}}
allowClose={false}
>
<Box p={1}>
<Text as="p" variant="body2">
Disconnected. Attempting to reconnect...
</Text>
</Box>
</Banner>
<AuthModal <AuthModal
isOpen={sessionStatus === "auth"} isOpen={sessionStatus === "auth"}
onSubmit={handleAuthSubmit} onSubmit={handleAuthSubmit}