xref: /expo/docs/pages/_error.tsx (revision 88331e54)
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