Skip to content

Commit

Permalink
Migration to react-router v6 (#375)
Browse files Browse the repository at this point in the history
  • Loading branch information
antoinejaussoin authored Mar 25, 2022
1 parent 910ec84 commit 6b29ab5
Show file tree
Hide file tree
Showing 25 changed files with 243 additions and 268 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ It features the following technologies:

- [React 17](https://github.com/facebook/react)
- [React Hooks](https://reactjs.org/docs/hooks-intro.html)
- [React Router 4](https://github.com/ReactTraining/react-router)
- [TypeScript 4](https://www.typescriptlang.org/)
- [React Router 6](https://reactrouter.com/)
- [TypeScript 4.6](https://www.typescriptlang.org/)
- [Recoil.js](https://recoiljs.org), as the global state management library
- [Socket IO](http://socket.io)
- [Webpack 4](https://github.com/webpack/webpack) (See older versions for Webpack 1, 2 and 3)
- [Webpack 5](https://github.com/webpack/webpack) (See older versions for Webpack 1, 2 and 3)
- [MUI 5](https://mui.com) for our components (previously known as Material-UI)
- [Material UI design](https://www.google.com/design/spec/material-design/introduction.html)
- [Emotion](https://emotion.sh/docs/introduction)
Expand Down Expand Up @@ -89,6 +89,7 @@ This will run a demo version, which you can turn into a fully licenced version b
- Adding the option of paying for Retrospected Pro annually, getting one month free in the process
- Update prices, especially for USD
- Make the integration tests less brittle by using specific attributes
- Upgrade (finally!) to React-Router v6.

### Version 4.12.1 (hotfix)

Expand Down
4 changes: 2 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"@types/react-copy-to-clipboard": "5.0.2",
"@types/react-dom": "17.0.13",
"@types/react-helmet": "6.1.5",
"@types/react-router-dom": "5.3.3",
"@types/react-router-dom": "^5.3.3",
"@types/shortid": "0.0.29",
"@types/uuid": "8.3.4",
"bowser": "2.11.0",
Expand Down Expand Up @@ -57,7 +57,7 @@
"react-helmet": "6.1.0",
"react-markdown": "6.0.3",
"react-password-strength-bar": "0.4.0",
"react-router-dom": "5.3.0",
"react-router-dom": "6.2.2",
"react-scripts": "5.0.0",
"react-scroll-to-bottom": "4.2.0",
"react-social-login-buttons": "3.6.0",
Expand Down
69 changes: 8 additions & 61 deletions frontend/src/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,26 @@
import { Component } from 'react';
import styled from '@emotion/styled';
import * as Sentry from '@sentry/browser';
import Typography from '@mui/material/Typography';
import Button from '@mui/material/Button';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import ErrorBoundaryContent from 'ErrorBoundaryContent';

interface ErrorBoundaryState {
errored: boolean;
}

class ErrorBoundary extends Component<RouteComponentProps, ErrorBoundaryState> {
class ErrorBoundary extends Component<{}, ErrorBoundaryState> {
unregisterHistoryListener?: () => void = undefined;

constructor(props: RouteComponentProps) {
constructor(props: {}) {
super(props);
this.state = { errored: false };
this.refresh = this.refresh.bind(this);
this.goBackHome = this.goBackHome.bind(this);
this.handleRouteChange = this.handleRouteChange.bind(this);
}

static getDerivedStateFromError() {
return { errored: true };
}

componentDidMount() {
this.unregisterHistoryListener = this.props.history.listen(() => {
this.setState({ errored: false });
});
}

componentWillUnmount() {
if (this.unregisterHistoryListener) {
this.unregisterHistoryListener();
}
handleRouteChange() {
this.setState({ errored: false });
}

componentDidCatch(error: Error, errorInfo: any) {
Expand All @@ -43,54 +31,13 @@ class ErrorBoundary extends Component<RouteComponentProps, ErrorBoundaryState> {
});
}

refresh() {
window.location.reload();
}

goBackHome() {
this.props.history.push('/');
}

render() {
if (this.state.errored) {
return (
<Container>
<Content>
<Typography variant="h1">Ooopsie...</Typography>
<Typography variant="h2">
Something went badly wrong, we logged the error to try and fix it
ASAP.
</Typography>
<Buttons>
<Button onClick={this.goBackHome} color="primary">
Home Page
</Button>
<Button onClick={this.refresh} color="secondary">
Refresh
</Button>
</Buttons>
</Content>
</Container>
);
return <ErrorBoundaryContent onHistoryChange={this.handleRouteChange} />;
}

return this.props.children;
}
}

export default withRouter(ErrorBoundary);

const Container = styled.div`
width: 100%;
position: relative;
text-align: center;
margin: 0 auto;
`;

const Content = styled.div`
margin-top: 200px;
`;

const Buttons = styled.div`
margin-top: 40px;
`;
export default ErrorBoundary;
58 changes: 58 additions & 0 deletions frontend/src/ErrorBoundaryContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import styled from '@emotion/styled';
import Typography from '@mui/material/Typography';
import Button from '@mui/material/Button';
import { useLocation, useNavigate } from 'react-router-dom';
import { useEffect, useState } from 'react';

type ErrorBoundaryContentProps = {
onHistoryChange: () => void;
};

export default function ErrorBoundaryContent({
onHistoryChange,
}: ErrorBoundaryContentProps) {
const navigate = useNavigate();
const location = useLocation();
const [initialLocation] = useState(location.pathname);

useEffect(() => {
if (location.pathname !== initialLocation) {
onHistoryChange();
}
}, [location.pathname, initialLocation, onHistoryChange]);

return (
<Container>
<Content>
<Typography variant="h1">Ooopsie...</Typography>
<Typography variant="h2">
Something went badly wrong, we logged the error to try and fix it
ASAP.
</Typography>
<Buttons>
<Button onClick={() => navigate('/')} color="primary">
Home Page
</Button>
<Button onClick={() => window.location.reload()} color="secondary">
Refresh
</Button>
</Buttons>
</Content>
</Container>
);
}

const Container = styled.div`
width: 100%;
position: relative;
text-align: center;
margin: 0 auto;
`;

const Content = styled.div`
margin-top: 200px;
`;

const Buttons = styled.div`
margin-top: 40px;
`;
67 changes: 35 additions & 32 deletions frontend/src/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { useEffect, useCallback, lazy, Suspense } from 'react';
import { useHistory, Redirect, Switch, Route } from 'react-router-dom';
import {
useNavigate,
Routes,
Route,
useLocation,
useMatch,
} from 'react-router-dom';
import { trackPageView } from './track';
import styled from '@emotion/styled';
import AppBar from '@mui/material/AppBar';
Expand Down Expand Up @@ -86,24 +92,23 @@ const Title = styled(Typography)`
`;

function App() {
const history = useHistory();
const navigate = useNavigate();
const backend = useBackendCapabilities();
const isCompatible = useIsCompatibleBrowser();
const { toggle: togglePanel } = useSidePanel();
const isInitialised = useIsInitialised();
const user = useUser();
const isPro = useIsPro();
const displayGoPro = !isPro && user && user.accountType !== 'anonymous';
const goToHome = useCallback(() => history.push('/'), [history]);
const goToHome = useCallback(() => navigate('/'), [navigate]);
const location = useLocation();
const isOnGamePage = !!useMatch('game/:gameId/*');

// Tracks page views on every location change
useEffect(() => {
trackPageView(window.location.pathname);
const unregister = history.listen((location) => {
trackPageView(location.pathname);
});
return () => {
unregister();
};
}, [history]);
trackPageView(location.pathname);
}, [location.pathname]);

return (
<div>
{!backend.licenced ? (
Expand Down Expand Up @@ -161,7 +166,7 @@ function App() {
</Hidden>
) : null}
<Spacer />
<Route path="/game/:gameId" component={Invite} />
{isOnGamePage ? <Invite /> : null}
{isInitialised ? (
<AccountMenu />
) : (
Expand All @@ -170,27 +175,25 @@ function App() {
</Toolbar>
</AppBar>
<Suspense fallback={<CodeSplitLoader />}>
<Switch>
<Route path="/" exact>
{user ? <Home /> : null}
<Routes>
<Route path="/" element={user ? <Home /> : null} />
<Route path="game/:gameId/*" element={<Game />} />
<Route path="validate" element={<ValidatePage />} />
<Route path="reset" element={<ResetPasswordPage />} />
<Route path="account" element={<AccountPage />} />
<Route path="login" element={<LoginPage />} />
<Route path="subscribe" element={<SubscribePageOuter />}>
<Route path="success" element={<SuccessPage />} />
<Route path="cancel" element={<CancelPage />} />
</Route>
<Redirect from="/session/:gameId" to="/game/:gameId" />
<Route path="/game/:gameId" component={Game} />
<Route path="/validate" component={ValidatePage} />
<Route path="/reset" component={ResetPasswordPage} />
<Route path="/account" component={AccountPage} />
<Route path="/login" component={LoginPage} />
<Route path="/subscribe" component={SubscribePageOuter} exact />
<Route path="/subscribe/success" component={SuccessPage} exact />
<Route path="/subscribe/cancel" component={CancelPage} exact />
<Route path="/admin" component={AdminPage} />
<Route path="/privacy" component={PrivacyPolicyPage} />
<Route path="/terms" component={TermsAndConditionsPage} />
<Route path="/cookies" component={CookiesPolicyPage} />
<Route path="/acceptable-use" component={AcceptableUsePolicyPage} />
<Route path="/disclaimer" component={DisclaimerPage} />
<Route path="/how-does-encryption-work" component={EncryptionDoc} />
</Switch>
<Route path="admin" element={<AdminPage />} />
<Route path="privacy" element={<PrivacyPolicyPage />} />
<Route path="terms" element={<TermsAndConditionsPage />} />
<Route path="cookies" element={<CookiesPolicyPage />} />
<Route path="acceptable-use" element={<AcceptableUsePolicyPage />} />
<Route path="disclaimer" element={<DisclaimerPage />} />
<Route path="how-does-encryption-work" element={<EncryptionDoc />} />
</Routes>
</Suspense>
<Panel />
<OutdatedBrowser show={!isCompatible} />
Expand Down
14 changes: 7 additions & 7 deletions frontend/src/auth/AccountMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import useTranslation from '../translations/useTranslations';
import { logout } from '../api';
import UserContext from './Context';
import Avatar from '../components/Avatar';
import { useHistory } from 'react-router-dom';
import { useNavigate } from 'react-router-dom';
import { Logout, Star } from '@mui/icons-material';
import { colors, Divider, ListItemIcon, ListItemText } from '@mui/material';
import AccountCircle from '@mui/icons-material/AccountCircle';
Expand All @@ -21,7 +21,7 @@ const AccountMenu = () => {
const [modalOpened, setModalOpen] = useState(false);
const [menuOpen, setMenuOpen] = useState(false);
const menuAnchor = useRef<HTMLDivElement>(null);
const history = useHistory();
const navigate = useNavigate();
const closeMenu = useCallback(() => setMenuOpen(false), []);
const openMenu = useCallback(() => setMenuOpen(true), []);

Expand All @@ -44,14 +44,14 @@ const AccountMenu = () => {
}, [setUser]);

const handleAccount = useCallback(() => {
history.push('/account');
navigate('/account');
setMenuOpen(false);
}, [history]);
}, [navigate]);

const handleSubscribe = useCallback(() => {
history.push('/subscribe');
navigate('/subscribe');
setMenuOpen(false);
}, [history]);
}, [navigate]);

const user = useUser();
if (user) {
Expand Down Expand Up @@ -122,7 +122,7 @@ const AvatarContainer = styled.div`
align-items: center;
cursor: pointer;
> *:first-child {
> :first-of-type {
margin-right: 10px;
}
`;
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/auth/modal/account/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,13 @@ const Login = ({

const Links = styled.div`
display: flex;
> :first-child {
> :first-of-type {
margin-right: 20px;
}
@media (max-width: 400px) {
flex-direction: column;
> :first-child {
> :first-of-type {
margin-right: 0px;
margin-bottom: 5px;
}
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/components/HowDoesItWorkButton.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { colors } from '@mui/material';
import { Info } from '@mui/icons-material';
import { useHistory } from 'react-router';
import { useNavigate } from 'react-router';
import styled from '@emotion/styled';

interface HowDoesItWorkButtonProps {
Expand All @@ -12,14 +12,14 @@ export default function HowDoesItWorkButton({
children,
url,
}: HowDoesItWorkButtonProps) {
const history = useHistory();
const navigate = useNavigate();
return (
<Container>
{children}

<LinkContainer>
<Info />
<GoLink onClick={() => history.push(url)}>How does that work?</GoLink>
<GoLink onClick={() => navigate(url)}>How does that work?</GoLink>
</LinkContainer>
</Container>
);
Expand Down
Loading

0 comments on commit 6b29ab5

Please sign in to comment.