xref: /expo/docs/pages/_error.tsx (revision f4b1168b)
1import { css } from '@emotion/react';
2import { Button, theme, typography } from '@expo/styleguide';
3import { spacing } from '@expo/styleguide-base';
4import * as Sentry from '@sentry/browser';
5import { useEffect, useState } from 'react';
6
7import { getRedirectPath } from '~/common/error-utilities';
8import Head from '~/components/Head';
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="secondary" 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="secondary" 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      extra: {
86        '404': pathname,
87      },
88    });
89  }, []);
90
91  useEffect(() => {
92    if (redirectPath && typeof window !== 'undefined') {
93      setTimeout(() => (window.location.href = `${redirectPath}${REDIRECT_SUFFIX}`), 1200);
94    }
95  }, [redirectPath]);
96
97  const getContent = () => {
98    if (redirectPath) {
99      return renderRedirect();
100    } else if (redirectFailed) {
101      return renderNotFoundAfterRedirect();
102    } else if (notFound) {
103      return renderNotFound();
104    }
105    return undefined;
106  };
107
108  return (
109    <Layout cssLayout={styles.layout} cssContent={styles.container}>
110      {getContent()}
111    </Layout>
112  );
113};
114
115export default Error;
116
117const styles = {
118  layout: css({
119    backgroundColor: theme.background.subtle,
120  }),
121  container: css({
122    display: 'flex',
123    alignItems: 'center',
124    justifyContent: 'center',
125    flexDirection: 'column',
126  }),
127  header: css({
128    ...typography.fontSizes[31],
129    marginTop: spacing[8],
130  }),
131  description: css({
132    textAlign: 'center',
133    maxWidth: 450,
134    marginTop: spacing[6],
135    marginBottom: spacing[8],
136    color: theme.text.secondary,
137  }),
138};
139