Skip to content

Commit

Permalink
Merge pull request #17 from Gaya/starting-location
Browse files Browse the repository at this point in the history
Starting location by GPS and clicking / dragging on map
  • Loading branch information
Gaya authored Jun 29, 2020
2 parents 76d994f + 3d254e8 commit 7975a3f
Show file tree
Hide file tree
Showing 38 changed files with 4,865 additions and 3,329 deletions.
6,632 changes: 3,845 additions & 2,787 deletions package-lock.json

Large diffs are not rendered by default.

31 changes: 18 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,40 @@
"@material-ui/core": "^4.10.2",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "^4.0.0-alpha.56",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"@testing-library/jest-dom": "^5.11.0",
"@testing-library/react": "^10.4.3",
"@testing-library/user-event": "^12.0.11",
"@types/cors": "^2.8.6",
"@types/express": "^4.17.6",
"@types/history": "^4.7.6",
"@types/jest": "^24.0.0",
"@types/node": "^12.12.47",
"@types/jest": "^26.0.3",
"@types/lodash.debounce": "^4.0.6",
"@types/node": "^14.0.14",
"@types/node-fetch": "^2.5.7",
"@types/react": "^16.9.38",
"@types/react-dom": "^16.9.0",
"@types/react-leaflet": "^2.5.1",
"@types/recoil": "0.0.1",
"@types/react": "^16.9.41",
"@types/react-dom": "^16.9.8",
"@types/react-leaflet": "^2.5.2",
"@types/react-redux": "^7.1.9",
"cors": "^2.8.5",
"express": "^4.17.1",
"gpx-builder": "^3.3.0",
"history": "^4.10.1",
"history": "^5.0.0",
"leaflet": "^1.6.0",
"lodash.debounce": "^4.0.8",
"node-fetch": "^2.6.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-leaflet": "^2.7.0",
"react-leaflet-arrowheads": "^1.0.6",
"react-redux": "^7.2.0",
"react-scripts": "3.4.1",
"recoil": "0.0.8",
"redux": "^4.0.5",
"redux-devtools-extension": "^2.13.8",
"redux-thunk": "^2.3.0",
"reselect": "^4.0.0",
"serve-static": "^1.14.1",
"ts-node": "^8.10.2",
"typescript": "~3.7.2",
"use-debounce": "^3.4.2"
"typescript": "~3.9.5"
},
"scripts": {
"analyze": "source-map-explorer 'build/static/js/*.js'",
Expand Down
20 changes: 12 additions & 8 deletions src/components/App/App.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
import React, { useCallback, useEffect } from 'react';
import { useRecoilState } from 'recoil';
import { useDispatch, useSelector } from 'react-redux';

import RunMap from '../RunMap/RunMap';
import HeaderBar from '../HeaderBar/HeaderBar';
import MoreButton from '../MoreButton/MoreButton';
import SideBar from '../SideBar/SideBar';
import Error from '../Error/Error';

import { drawerOpenState } from '../../state/app';
import { isDrawerOpenedSelector } from '../../store/app/selectors';
import { closeDrawer, openDrawer } from '../../store/app/actions';

import useRouteNavigation from './useRouteNavigation';

import './App.css';

const App: React.FC = () => {
const [drawerOpen, setDrawerOpen] = useRecoilState(drawerOpenState);
const isDrawerOpened = useSelector(isDrawerOpenedSelector);
const dispatch = useDispatch();

const openDrawer = useCallback(() => setDrawerOpen(true), [setDrawerOpen]);
const closeDrawer = useCallback(() => setDrawerOpen(false), [setDrawerOpen]);
const onOpenDrawer = useCallback(() => dispatch(openDrawer()), [dispatch]);
const onCloseDrawer = useCallback(() => dispatch(closeDrawer()), [dispatch]);

useRouteNavigation(closeDrawer);
useRouteNavigation(onCloseDrawer);

// set window height for CSS
useEffect(() => {
Expand All @@ -28,10 +31,11 @@ const App: React.FC = () => {

return (
<div className="App">
<HeaderBar openDrawer={openDrawer} />
<SideBar isDrawerOpen={drawerOpen} onCloseDrawer={closeDrawer} />
<HeaderBar openDrawer={onOpenDrawer} />
<SideBar isDrawerOpen={isDrawerOpened} onCloseDrawer={onCloseDrawer} />
<RunMap />
<MoreButton />
<Error />
</div>
);
};
Expand Down
18 changes: 12 additions & 6 deletions src/components/App/AppWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import React from 'react';
import { RecoilRoot } from 'recoil';
import { Provider } from 'react-redux';

import { createMuiTheme, ThemeProvider } from '@material-ui/core/styles';
import teal from '@material-ui/core/colors/teal';
import deepOrange from '@material-ui/core/colors/deepOrange';

import { store } from '../../store/store';

import ErrorProvider from '../Error/ErrorProvider';

import './App.css';

const theme = createMuiTheme({
Expand All @@ -16,11 +20,13 @@ const theme = createMuiTheme({
});

const AppWrapper: React.FC = ({ children }) => (
<ThemeProvider theme={theme}>
<RecoilRoot>
{children}
</RecoilRoot>
</ThemeProvider>
<Provider store={store}>
<ThemeProvider theme={theme}>
<ErrorProvider>
{children}
</ErrorProvider>
</ThemeProvider>
</Provider>
);

export default AppWrapper;
82 changes: 40 additions & 42 deletions src/components/App/useRouteNavigation.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,23 @@
import { useSetRecoilState } from 'recoil';
import { useCallback, useEffect } from 'react';
import { useCallback, useEffect, useState } from 'react';
import { useDispatch } from 'react-redux';

import {
routeDistanceState,
routeFlippedState,
routeLocationState,
routeRandomSeedState,
routeTypeState,
setOnCompleteRoute,
} from '../../state/route';
import { history } from '../../utils/history';
import { randomSeed } from '../../state/utils';
import { randomSeed } from '../../store/route/utils';
import { updateRouteParameters } from '../../store/route/actions';

function useRouteNavigation(closeDrawer: () => void): void {
const setDistanceState = useSetRecoilState(routeDistanceState);
const setRouteTypeState = useSetRecoilState(routeTypeState);
const setRouteLocationState = useSetRecoilState(routeLocationState);
const setRouteRandomSeedState = useSetRecoilState(routeRandomSeedState);
const setRouteFlippedState = useSetRecoilState(routeFlippedState);
function hasRouteQueryParameters(search: string): boolean {
const params = new URLSearchParams(search);
const distance = params.get('distance');
const routeType = params.get('routeType');
const location = params.get('location');

return !!(distance && routeType && location);
}

function useLoadRouteFromQueryParameters(): (search: string) => void {
const dispatch = useDispatch();

// handle route loading on route change
const loadRouteFromQueryParameters = useCallback((search: string): boolean => {
return useCallback((search: string): void => {
const params = new URLSearchParams(search);
const distance = params.get('distance');
const routeType = params.get('routeType');
Expand All @@ -29,30 +26,36 @@ function useRouteNavigation(closeDrawer: () => void): void {
const flipped = params.get('flipped');

if (distance && routeType && location) {
setDistanceState(parseInt(distance, 10));
setRouteTypeState(parseInt(routeType, 10));
setRouteLocationState(location);
setRouteRandomSeedState(r ? parseInt(r, 10) : randomSeed());
setRouteFlippedState(!!(flipped && flipped !== 'false'));

return true;
dispatch(updateRouteParameters({
distance: parseInt(distance, 10),
routeType: parseInt(routeType, 10),
randomSeed: r ? parseInt(r, 10) : randomSeed(),
flipped: !!(flipped && flipped !== 'false'),
location,
}));
}
}, [dispatch]);
}

return false;
}, [
setDistanceState,
setRouteFlippedState,
setRouteLocationState,
setRouteRandomSeedState,
setRouteTypeState,
]);
function useRouteNavigation(closeDrawer: () => void): void {
const [initialLoaded, setInitialLoaded] = useState(false);
const loadRouteFromQueryParameters = useLoadRouteFromQueryParameters();

// listen to route changes
useEffect(() => history.listen((location) => {
loadRouteFromQueryParameters(location.search);
useEffect(() => history.listen((action) => {
loadRouteFromQueryParameters(action.location.search);
}), [loadRouteFromQueryParameters]);

const hasRoute = loadRouteFromQueryParameters(window.location.search);
// load initial route from query params
const hasRoute = hasRouteQueryParameters(window.location.search);

useEffect(() => {
if (hasRoute && !initialLoaded) {
loadRouteFromQueryParameters(window.location.search);
}

setInitialLoaded(true);
}, [hasRoute, initialLoaded, loadRouteFromQueryParameters]);

// initial load
useEffect(() => {
Expand All @@ -61,11 +64,6 @@ function useRouteNavigation(closeDrawer: () => void): void {
closeDrawer();
}
}, [closeDrawer, hasRoute]);

// register closing drawer after route is loaded
useEffect(() => {
setOnCompleteRoute(closeDrawer);
}, [closeDrawer]);
}

export default useRouteNavigation;
55 changes: 33 additions & 22 deletions src/components/ConfigureRun/ConfigureRun.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
import React, { FormEvent, useState } from 'react';
import { useRecoilValue, useRecoilValueLoadable } from 'recoil';

import React, {
FormEvent,
useCallback,
useState,
} from 'react';
import { useDispatch, useSelector } from 'react-redux';
import {
Button,
CircularProgress,
Grid,
Theme,
} from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';

import { routeDataQuery, routeDistanceState, routeTypeState } from '../../state/route';
import { makeStyles } from '@material-ui/core/styles';
import { randomSeed } from '../../store/route/utils';
import {
isLoading,
randomSeed,
safeStoredLocation,
} from '../../state/utils';
import { RouteTypeValue } from '../../types';
defaultDistanceSelector,
maximumDistanceSelector,
minimumDistanceSelector,
} from '../../store/app/selectors';

import { RouteTypeValue } from '../../types';
import Distance from './Distance';
import StartingPoint from './StartingPoint';
import RouteType from './RouteType';
import { setQueryParameters } from '../../utils/history';
import { defaultDistanceState, maximumDistanceState, minimumDistanceState } from '../../state/app';

import { isRouteLoadingSelector, routeParametersSelector } from '../../store/route/selectors';
import { updateRouteLocation } from '../../store/route/actions';

const routeTypes: RouteTypeValue[] = [
{
Expand Down Expand Up @@ -60,22 +65,28 @@ const useStyles = makeStyles((theme: Theme) => ({
}));

const ConfigureRun: React.FC = () => {
const route = useRecoilValueLoadable(routeDataQuery);
const dispatch = useDispatch();

const currentDistance = useRecoilValue(routeDistanceState);
const currentRouteType = useRecoilValue(routeTypeState);
const isGenerating = useSelector(isRouteLoadingSelector);
const params = useSelector(routeParametersSelector);

const setRouteLocation = useCallback(
(location: string): void => {
dispatch(updateRouteLocation(location));
},
[dispatch],
);
const location = params.location || null;

const defaultDistance = useRecoilValue(defaultDistanceState);
const minDistance = useRecoilValue(minimumDistanceState);
const maxDistance = useRecoilValue(maximumDistanceState);
const defaultDistance = useSelector(defaultDistanceSelector);
const maxDistance = useSelector(maximumDistanceSelector);
const minDistance = useSelector(minimumDistanceSelector);

const [distance, setDistance] = useState<number>(currentDistance || defaultDistance);
const [routeType, setRouteType] = useState<RouteTypeValue['id']>(currentRouteType || routeTypes[0].id);
const [location, setLocation] = useState<string | null>(safeStoredLocation()?.key || null);
const [distance, setDistance] = useState<number>(params.distance || defaultDistance);
const [routeType, setRouteType] = useState<RouteTypeValue['id']>(params.routeType || routeTypes[0].id);

const classes = useStyles();

const isGenerating = isLoading(route);
const canGenerate = distance
&& routeType
&& location;
Expand All @@ -100,7 +111,7 @@ const ConfigureRun: React.FC = () => {
<form onSubmit={handleSubmit}>
<Grid container spacing={3}>
<Grid item xs={12}>
<StartingPoint location={location} setLocation={setLocation} />
<StartingPoint location={location} setLocation={setRouteLocation} />
</Grid>

<Grid item xs={12}>
Expand Down
Loading

0 comments on commit 7975a3f

Please sign in to comment.