1import { css } from '@emotion/react'; 2import { spacing, theme, typography } from '@expo/styleguide'; 3import * as Sentry from '@sentry/browser'; 4import React, { useEffect, useState } from 'react'; 5 6import { getRedirectPath } from '~/common/error-utilities'; 7import Head from '~/components/Head'; 8import { Button } from '~/ui/components/Button'; 9import { NotFoundImage, RedirectImage, ServerErrorImage } from '~/ui/components/ErrorPage'; 10import { Layout } from '~/ui/components/Layout'; 11import { H1, P } from '~/ui/components/Text'; 12 13const REDIRECT_SUFFIX = '?redirected'; 14 15const renderRedirect = () => ( 16 // note(simek): "redirect-link" ID is needed for test-links script 17 <> 18 <Head title="Redirecting" /> 19 <RedirectImage /> 20 <H1 css={styles.header}>Redirecting</H1> 21 <P css={styles.description} id="redirect-link"> 22 Just a moment… 23 </P> 24 </> 25); 26 27const renderNotFoundAfterRedirect = () => ( 28 <> 29 <Head title="Not Found" /> 30 <ServerErrorImage /> 31 <H1 css={styles.header}>404: Not Found</H1> 32 <P css={styles.description} id="__redirect_failed"> 33 We took an educated guess and tried to direct you to the right page, but it seems that did not 34 work out! Maybe it doesn't exist anymore! 35 </P> 36 <Button theme="tertiary" href="/"> 37 Return Home 38 </Button> 39 </> 40); 41 42const renderNotFound = () => ( 43 <> 44 <Head title="Not Found" /> 45 <NotFoundImage /> 46 <H1 css={styles.header}>404: Not Found</H1> 47 <P css={styles.description} id="__not_found"> 48 We couldn't find the page you were looking for. Check the URL to make sure it's correct and 49 try again. 50 </P> 51 <Button theme="tertiary" href="/"> 52 Return Home 53 </Button> 54 </> 55); 56 57const Error = () => { 58 const [notFound, setNotFound] = useState<boolean>(false); 59 const [redirectFailed, setRedirectFailed] = useState<boolean>(false); 60 const [redirectPath, setRedirectPath] = useState<string | undefined>(undefined); 61 62 useEffect(() => { 63 if (typeof window === 'undefined') { 64 return; 65 } 66 67 const { pathname, search } = window.location; 68 69 if (search === REDIRECT_SUFFIX) { 70 Sentry.captureMessage(`Redirect failed`); 71 setRedirectFailed(true); 72 return; 73 } 74 75 const newRedirectPath = getRedirectPath(pathname); 76 77 if (newRedirectPath !== pathname) { 78 setRedirectPath(newRedirectPath); 79 return; 80 } 81 82 // We are confident now that we can render a not found error 83 setNotFound(true); 84 Sentry.captureMessage(`Page not found (404)`); 85 }, []); 86 87 useEffect(() => { 88 if (redirectPath && typeof window !== 'undefined') { 89 setTimeout(() => (window.location.href = `${redirectPath}${REDIRECT_SUFFIX}`), 1200); 90 } 91 }, [redirectPath]); 92 93 const getContent = () => { 94 if (redirectPath) { 95 return renderRedirect(); 96 } else if (redirectFailed) { 97 return renderNotFoundAfterRedirect(); 98 } else if (notFound) { 99 return renderNotFound(); 100 } 101 return undefined; 102 }; 103 104 return ( 105 <Layout cssLayout={styles.layout} cssContent={styles.container}> 106 {getContent()} 107 </Layout> 108 ); 109}; 110 111export default Error; 112 113const styles = { 114 layout: css({ 115 backgroundColor: theme.background.secondary, 116 }), 117 container: css({ 118 display: 'flex', 119 alignItems: 'center', 120 justifyContent: 'center', 121 flexDirection: 'column', 122 }), 123 header: css({ 124 ...typography.fontSizes[31], 125 marginTop: spacing[8], 126 }), 127 description: css({ 128 textAlign: 'center', 129 maxWidth: 450, 130 marginTop: spacing[6], 131 marginBottom: spacing[8], 132 color: theme.text.secondary, 133 }), 134}; 135