Add general error message for wardrobe crashes

Boom, pulled some stuff out into util! Now we also have error boundaries in both panels of the wardrobe page.

You can test this one by visiting `/outfits/new?send-test-error-for-sentry`, just like on the home page! Util component for fake errors owo
This commit is contained in:
Emi Matchu 2021-05-05 00:22:28 -07:00
parent 6dc6ad2578
commit 65af7ef64c
6 changed files with 139 additions and 106 deletions

View file

@ -29,6 +29,7 @@ import {
ErrorMessage, ErrorMessage,
Heading1, Heading1,
Heading2, Heading2,
TestErrorSender,
useCommonStyles, useCommonStyles,
useLocalStorage, useLocalStorage,
usePageTitle, usePageTitle,
@ -51,12 +52,6 @@ function HomePage() {
const [previewState, setPreviewState] = React.useState(null); const [previewState, setPreviewState] = React.useState(null);
React.useEffect(() => {
if (window.location.href.includes("send-test-error-for-sentry")) {
throw new Error("Test error for Sentry");
}
});
return ( return (
<Flex direction="column" align="center" textAlign="center" marginTop="8"> <Flex direction="column" align="center" textAlign="center" marginTop="8">
<Box <Box
@ -103,6 +98,7 @@ function HomePage() {
<WIPCallout details="We started building this last year, but, well… what a year 😅 Anyway, this will eventually become the main site, at impress.openneo.net!"> <WIPCallout details="We started building this last year, but, well… what a year 😅 Anyway, this will eventually become the main site, at impress.openneo.net!">
Maybe we'll rename it to Impress 2021 or maybe not! 🤔 Maybe we'll rename it to Impress 2021 or maybe not! 🤔
</WIPCallout> </WIPCallout>
<TestErrorSender />
</Flex> </Flex>
); );
} }

View file

@ -1,11 +1,9 @@
import React from "react"; import React from "react";
import { Box, Flex, Grid, Link } from "@chakra-ui/react"; import { Box } from "@chakra-ui/react";
import { loadable } from "./util"; import { loadable } from "./util";
import * as Sentry from "@sentry/react"; import * as Sentry from "@sentry/react";
import { WarningIcon } from "@chakra-ui/icons";
import ErrorGrundoImg from "./images/error-grundo.png"; import { MajorErrorMessage } from "./util";
import ErrorGrundoImg2x from "./images/error-grundo@2x.png";
const GlobalHeader = loadable(() => import("./GlobalHeader")); const GlobalHeader = loadable(() => import("./GlobalHeader"));
const GlobalFooter = loadable(() => import("./GlobalFooter")); const GlobalFooter = loadable(() => import("./GlobalFooter"));
@ -41,49 +39,4 @@ function PageLayout({ children }) {
); );
} }
function MajorErrorMessage({ error }) {
return (
<Flex justify="center" marginTop="8">
<Grid
templateAreas='"icon title" "icon description" "icon details"'
templateColumns="auto 1fr"
maxWidth="500px"
columnGap="4"
>
<Box gridArea="icon" marginTop="2">
<Box
as="img"
src={ErrorGrundoImg}
srcSet={`${ErrorGrundoImg} 1x, ${ErrorGrundoImg2x} 2x`}
borderRadius="full"
boxShadow="md"
width="100px"
height="100px"
alt=""
/>
</Box>
<Box gridArea="title" fontSize="lg" marginBottom="1">
Ah dang, I broke it 😖
</Box>
<Box gridArea="description" marginBottom="2">
There was an error displaying this page. I'll get info about it
automatically, but you can tell me more at{" "}
<Link href="mailto:matchu@openneo.net" color="green.400">
matchu@openneo.net
</Link>
!
</Box>
<Box gridArea="details" fontSize="xs" opacity="0.8">
<WarningIcon
marginRight="1.5"
marginTop="-2px"
aria-label="Error message"
/>
"{error.message}"
</Box>
</Grid>
</Flex>
);
}
export default PageLayout; export default PageLayout;

View file

@ -1,5 +1,6 @@
import React from "react"; import React from "react";
import { Box, Flex } from "@chakra-ui/react"; import { Box, Flex } from "@chakra-ui/react";
import * as Sentry from "@sentry/react";
import ItemsPanel from "./ItemsPanel"; import ItemsPanel from "./ItemsPanel";
import SearchToolbar, { import SearchToolbar, {
@ -7,6 +8,7 @@ import SearchToolbar, {
searchQueryIsEmpty, searchQueryIsEmpty,
} from "./SearchToolbar"; } from "./SearchToolbar";
import SearchPanel from "./SearchPanel"; import SearchPanel from "./SearchPanel";
import { MajorErrorMessage, TestErrorSender } from "../util";
/** /**
* ItemsAndSearchPanels manages the shared layout and state for: * ItemsAndSearchPanels manages the shared layout and state for:
@ -21,13 +23,20 @@ import SearchPanel from "./SearchPanel";
* performing some wiring to help them interact with each other via simple * performing some wiring to help them interact with each other via simple
* state and refs. * state and refs.
*/ */
function ItemsAndSearchPanels({ loading, outfitState, outfitSaving, dispatchToOutfit }) { function ItemsAndSearchPanels({
loading,
outfitState,
outfitSaving,
dispatchToOutfit,
}) {
const [searchQuery, setSearchQuery] = React.useState(emptySearchQuery); const [searchQuery, setSearchQuery] = React.useState(emptySearchQuery);
const scrollContainerRef = React.useRef(); const scrollContainerRef = React.useRef();
const searchQueryRef = React.useRef(); const searchQueryRef = React.useRef();
const firstSearchResultRef = React.useRef(); const firstSearchResultRef = React.useRef();
return ( return (
<Sentry.ErrorBoundary fallback={MajorErrorMessage}>
<TestErrorSender />
<Flex direction="column" height="100%"> <Flex direction="column" height="100%">
<Box px="5" py="3" boxShadow="sm"> <Box px="5" py="3" boxShadow="sm">
<SearchToolbar <SearchToolbar
@ -75,6 +84,7 @@ function ItemsAndSearchPanels({ loading, outfitState, outfitSaving, dispatchToOu
</Box> </Box>
)} )}
</Flex> </Flex>
</Sentry.ErrorBoundary>
); );
} }

View file

@ -32,7 +32,12 @@ function WardrobePageLayout({ previewAndControls, itemsAndSearch }) {
height="100%" height="100%"
width="100%" width="100%"
> >
<Box gridArea="previewAndControls" bg="gray.900" position="relative"> <Box
gridArea="previewAndControls"
bg="gray.900"
color="gray.50"
position="relative"
>
{previewAndControls} {previewAndControls}
</Box> </Box>
<Box gridArea="itemsAndSearch" bg={itemsAndSearchBackground}> <Box gridArea="itemsAndSearch" bg={itemsAndSearchBackground}>

View file

@ -2,13 +2,14 @@ import React from "react";
import { Box, DarkMode } from "@chakra-ui/react"; import { Box, DarkMode } from "@chakra-ui/react";
import gql from "graphql-tag"; import gql from "graphql-tag";
import { useQuery } from "@apollo/client"; import { useQuery } from "@apollo/client";
import * as Sentry from "@sentry/react";
import OutfitThumbnail, { import OutfitThumbnail, {
outfitThumbnailFragment, outfitThumbnailFragment,
getOutfitThumbnailRenderSize, getOutfitThumbnailRenderSize,
} from "../components/OutfitThumbnail"; } from "../components/OutfitThumbnail";
import { useOutfitPreview } from "../components/OutfitPreview"; import { useOutfitPreview } from "../components/OutfitPreview";
import { loadable } from "../util"; import { loadable, MajorErrorMessage, TestErrorSender } from "../util";
const OutfitControls = loadable(() => import("./OutfitControls")); const OutfitControls = loadable(() => import("./OutfitControls"));
@ -34,7 +35,8 @@ function WardrobePreviewAndControls({
}); });
return ( return (
<> <Sentry.ErrorBoundary fallback={MajorErrorMessage}>
<TestErrorSender />
<Box position="absolute" top="0" bottom="0" left="0" right="0"> <Box position="absolute" top="0" bottom="0" left="0" right="0">
<DarkMode>{preview}</DarkMode> <DarkMode>{preview}</DarkMode>
</Box> </Box>
@ -46,7 +48,7 @@ function WardrobePreviewAndControls({
appearance={appearance} appearance={appearance}
/> />
</Box> </Box>
</> </Sentry.ErrorBoundary>
); );
} }

View file

@ -1,8 +1,19 @@
import React from "react"; import React from "react";
import { Box, Heading, useColorModeValue } from "@chakra-ui/react"; import {
Box,
Flex,
Grid,
Heading,
Link,
useColorModeValue,
} from "@chakra-ui/react";
import loadableLibrary from "@loadable/component"; import loadableLibrary from "@loadable/component";
import * as Sentry from "@sentry/react"; import * as Sentry from "@sentry/react";
import ErrorGrundoImg from "./images/error-grundo.png";
import ErrorGrundoImg2x from "./images/error-grundo@2x.png";
import { WarningIcon } from "@chakra-ui/icons";
/** /**
* Delay hides its content at first, then shows it after the given delay. * Delay hides its content at first, then shows it after the given delay.
* *
@ -375,3 +386,59 @@ export function logAndCapture(e) {
console.error(e); console.error(e);
Sentry.captureException(e); Sentry.captureException(e);
} }
export function MajorErrorMessage({ error }) {
return (
<Flex justify="center" marginTop="8">
<Grid
templateAreas='"icon title" "icon description" "icon details"'
templateColumns="auto 1fr"
maxWidth="500px"
marginX="8"
columnGap="4"
>
<Box gridArea="icon" marginTop="2">
<Box
as="img"
src={ErrorGrundoImg}
srcSet={`${ErrorGrundoImg} 1x, ${ErrorGrundoImg2x} 2x`}
borderRadius="full"
boxShadow="md"
width="100px"
height="100px"
alt=""
/>
</Box>
<Box gridArea="title" fontSize="lg" marginBottom="1">
Ah dang, I broke it 😖
</Box>
<Box gridArea="description" marginBottom="2">
There was an error displaying this page. I'll get info about it
automatically, but you can tell me more at{" "}
<Link href="mailto:matchu@openneo.net" color="green.400">
matchu@openneo.net
</Link>
!
</Box>
<Box gridArea="details" fontSize="xs" opacity="0.8">
<WarningIcon
marginRight="1.5"
marginTop="-2px"
aria-label="Error message"
/>
"{error.message}"
</Box>
</Grid>
</Flex>
);
}
export function TestErrorSender() {
React.useEffect(() => {
if (window.location.href.includes("send-test-error-for-sentry")) {
throw new Error("Test error for Sentry");
}
});
return null;
}