1import * as Sentry from '@sentry/browser'; 2import React from 'react'; 3 4import { getRedirectPath } from '~/common/error-utilities'; 5 6const REDIRECT_SUFFIX = '?redirected'; 7 8type State = { 9 notFound: boolean; 10 redirectPath?: string; 11 redirectFailed: boolean; 12}; 13export default class Error extends React.Component<object, State> { 14 state: State = { 15 notFound: false, 16 redirectPath: undefined, 17 redirectFailed: false, 18 }; 19 20 componentDidMount() { 21 this._maybeRedirect(); 22 } 23 24 _maybeRedirect = () => { 25 if (typeof window === 'undefined') { 26 return; 27 } 28 29 const { pathname } = window.location; 30 31 if (window.location.search === REDIRECT_SUFFIX) { 32 Sentry.captureMessage(`Redirect failed`); 33 this.setState({ redirectFailed: true }); 34 return; 35 } 36 37 const redirectPath = getRedirectPath(pathname); 38 39 if (redirectPath !== pathname) { 40 this.setState({ redirectPath }); 41 return; 42 } 43 44 // We are confident now that we can render a not found error 45 this.setState({ notFound: true }); 46 Sentry.captureMessage(`Page not found (404)`); 47 }; 48 49 componentDidUpdate(prevProps: object, prevState: State) { 50 if (prevState.redirectPath !== this.state.redirectPath && typeof window !== 'undefined') { 51 // Let people actually read the carefully crafted message and absorb the 52 // cool emoji selection, they can just click through if they want speed 53 setTimeout(() => { 54 window.location.href = `${this.state.redirectPath}?redirected`; 55 }, 1200); 56 } 57 } 58 59 render() { 60 return ( 61 <div 62 style={{ 63 display: 'flex', 64 flex: 1, 65 alignItems: 'center', 66 justifyContent: 'center', 67 height: '100vh', 68 flexDirection: 'column', 69 }}> 70 {this._renderContents()} 71 </div> 72 ); 73 } 74 75 _renderContents = () => { 76 const styles = { 77 description: { 78 textAlign: 'center' as const, 79 maxWidth: 450, 80 marginHorizontal: 30, 81 lineHeight: '1.7em', 82 }, 83 link: { 84 textAlign: 'center' as const, 85 marginTop: 20, 86 }, 87 }; 88 89 if (this.state.redirectPath) { 90 return ( 91 <> 92 <h1>️♀️️</h1> 93 <p style={styles.description}> 94 Hold tight, we are redirecting you to where we think this URL was intended to take you! 95 </p> 96 <p style={styles.link}> 97 <a id="redirect-link" href={this.state.redirectPath}> 98 Click here to possibly go there more quickly! 99 </a> 100 </p> 101 </> 102 ); 103 } else if (this.state.redirectFailed) { 104 return ( 105 <> 106 <h1>️</h1> 107 <p style={styles.description} id="__redirect_failed"> 108 We took an educated guess and tried to direct you to the right page, but it seems that 109 did not work out! Maybe it doesn't exist anymore! 110 </p> 111 <p style={styles.link}> 112 <a href="/">Go to the Expo documentation, you can try searching there</a> 113 </p> 114 </> 115 ); 116 } else if (this.state.notFound) { 117 return ( 118 <> 119 <h1></h1> 120 <p style={styles.description} id="__not_found"> 121 <strong style={{ fontWeight: 'bold' }}>Uh oh, we couldn't find this page!</strong> We've 122 made note of this and will investigate, but it's possible that the page you're looking 123 for no longer exists! 124 </p> 125 <p style={styles.link}> 126 <a href="/">Go to the Expo documentation, you can try searching there</a> 127 </p> 128 </> 129 ); 130 } else { 131 // Render nothing statically 132 } 133 }; 134} 135