diff --git a/src/components/ToiletDetailsPanel.tsx b/src/components/ToiletDetailsPanel.tsx
index 7bc4e3ad8..2da8a8a48 100644
--- a/src/components/ToiletDetailsPanel.tsx
+++ b/src/components/ToiletDetailsPanel.tsx
@@ -26,7 +26,6 @@ import parseISO from 'date-fns/parseISO';
import add from 'date-fns/add';
import Link from 'next/link';
import useComponentSize from '@rehooks/component-size';
-import L from 'leaflet';
import Box from './Box';
import Button from './Button';
@@ -105,10 +104,11 @@ function round(value: number, precision = 0) {
}
const DistanceTo = ({ from, to }) => {
- const fromLatLng = L.latLng(from.lat, from.lng);
+ // const fromLatLng = L.latLng(from.lat, from.lng);
- const toLatLng = L.latLng(to.lat, to.lng);
- const metersToLoo = fromLatLng.distanceTo(toLatLng);
+ // const toLatLng = L.latLng(to.lat, to.lng);
+ // const metersToLoo = fromLatLng.distanceTo(toLatLng);
+ const metersToLoo = 10000;
const distance =
metersToLoo < 1000
@@ -117,7 +117,7 @@ const DistanceTo = ({ from, to }) => {
return (
- {distance}
+ FIXME {distance}
);
};
diff --git a/src/components/useFilters.tsx b/src/components/useFilters.tsx
new file mode 100644
index 000000000..0a4a5d9d5
--- /dev/null
+++ b/src/components/useFilters.tsx
@@ -0,0 +1,44 @@
+import { useEffect, useState, useMemo } from 'react';
+import config, { FILTERS_KEY } from '../config';
+
+const useFilters = (toilets) => {
+ let initialState = config.getSettings(FILTERS_KEY);
+ // default any unsaved filters as 'false'
+ config.filters.forEach((filter) => {
+ initialState[filter.id] = initialState[filter.id] || false;
+ });
+ const [filters, setFilters] = useState(initialState);
+
+ // keep local storage and state in sync
+ useEffect(() => {
+ window.localStorage.setItem(FILTERS_KEY, JSON.stringify(filters));
+ }, [filters]);
+
+ // get the filter objects from config for the filters applied by the user
+ const applied = config.filters.filter((filter) => filters[filter.id]);
+
+ const filtered = useMemo(() => {
+ if (toilets) {
+ return toilets.filter((toilet: { [x: string]: any }) =>
+ applied.every((filter) => {
+ const value = toilet[filter.id];
+
+ if (value === null) {
+ return false;
+ }
+
+ return !!value;
+ })
+ );
+ }
+ return [];
+ }, [toilets, applied]);
+
+ return {
+ filtered,
+ filters,
+ setFilters,
+ };
+};
+
+export default useFilters;
diff --git a/src/pages/MapPage.tsx b/src/pages/MapPage.tsx
deleted file mode 100644
index 1edd7858f..000000000
--- a/src/pages/MapPage.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import {
- GetServerSideProps,
- GetServerSidePropsContext,
- GetStaticProps,
- GetStaticPropsContext,
- InferGetServerSidePropsType,
-} from 'next';
-import { useRouter } from 'next/router';
-import React from 'react';
-
-import HomePage from '.';
-
-const MapPage = ({
- lat,
- lng,
-}): InferGetServerSidePropsType => {
- return (
-
- );
-};
-
-export const getServerSideProps: GetServerSideProps = async (
- context: GetServerSidePropsContext
-) => {
- return {
- props: {
- lat: context?.query['lat'],
- lng: context?.query['lng'],
- },
- };
-};
-
-export default MapPage;
diff --git a/src/pages/explorer/voyager.tsx b/src/pages/explorer/voyager.tsx
index 467ea50dc..9aaae6e97 100644
--- a/src/pages/explorer/voyager.tsx
+++ b/src/pages/explorer/voyager.tsx
@@ -6,31 +6,31 @@ import config from '../../config';
import 'graphql-voyager/dist/voyager.css';
const VoyagerComponent = dynamic(
- () => import('graphql-voyager').then((mod) => mod.Voyager),
- { ssr: false }
+ () => import('graphql-voyager').then((mod) => mod.Voyager),
+ { ssr: false }
);
function introspectionProvider(query) {
- return fetch('/api', {
- method: 'post',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ query: query }),
- }).then((response) => response.json());
- }
+ return fetch('/api', {
+ method: 'post',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ query: query }),
+ }).then((response) => response.json());
+}
- const Voyager = () => (
-
-
- {config.getTitle('Schema Visualisation')}
-
+const Voyager = () => (
+
+
+ {config.getTitle('Schema Visualisation')}
+
-
-
-
-
- );
+
+
+
+
+);
- export default Voyager;
+export default Voyager;
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index 1316d3f52..06dd663ee 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -1,5 +1,4 @@
-import React, { useState } from 'react';
-import PropTypes from 'prop-types';
+import React from 'react';
import Head from 'next/head';
import dynamic from 'next/dynamic';
import PageLayout from '../components/PageLayout';
@@ -8,160 +7,36 @@ import Sidebar from '../components/Sidebar';
import Notification from '../components/Notification';
import VisuallyHidden from '../components/VisuallyHidden';
import { useMapState } from '../components/MapState';
-import config, { FILTERS_KEY } from '../config';
+import config from '../config';
import { useRouter } from 'next/router';
import { withApollo } from '../components/withApollo';
import { NextPage } from 'next';
-import {
- getServerPageFindLooById,
- getServerPageFindLoosNearby,
- useFindLooById,
- useFindLoosNearby,
-} from '../api-client/page';
-
-/**
- * SSR Migration plan
- * ---
- *
- * Look at getStaticProps to fetch loos for lat/lng at build time.
- * Look at using getStaticProps to pre-fetch /loos/[id].
- *
- * Use ISR (https://nextjs.org/docs/basic-features/data-fetching#incremental-static-regeneration)
- * ISR lets us regenerate loo pages and lat/lng loo list incrementally upon new requests
- * Set revalidate to throttle this by n seconds.
- */
+import { useFindLoosNearbyQuery } from '../api-client/graphql';
+import useFilters from '../components/useFilters';
const SIDEBAR_BOTTOM_MARGIN = 32;
const MapLoader = () => Loading map...
;
+const LooMap = dynamic(() => import('../components/LooMap'), {
+ loading: MapLoader,
+ ssr: false,
+});
-const HomePage = ({ initialPosition, ...props }) => {
+const HomePage = () => {
+ const router = useRouter();
+ const { message } = router.query;
const [mapState, setMapState] = useMapState();
- const LooMap = React.useMemo(
- () =>
- dynamic(() => import('../components/LooMap'), {
- loading: MapLoader,
- ssr: false,
- }),
- []
- );
- const ToiletDetailsPanel = React.useMemo(
- () =>
- dynamic(() => import('../components/ToiletDetailsPanel'), {
- loading: MapLoader,
- ssr: false,
- }),
- []
- );
- let initialState = config.getSettings(FILTERS_KEY);
- // default any unsaved filters as 'false'
- config.filters.forEach((filter) => {
- initialState[filter.id] = initialState[filter.id] || false;
+ const { data } = useFindLoosNearbyQuery({
+ variables: {
+ lat: mapState.center.lat,
+ lng: mapState.center.lng,
+ radius: Math.ceil(mapState.radius),
+ },
});
- const [filters, setFilters] = useState(initialState);
-
- // keep local storage and state in sync
- React.useEffect(() => {
- window.localStorage.setItem(FILTERS_KEY, JSON.stringify(filters));
- }, [filters]);
-
- const [toiletData, setToiletData] = React.useState([]);
-
- const router = useRouter();
-
- /**
- * Fetch nearby loo data when the map state changes.
- * TODO: Try initial fetch using SSR.
- */
- React.useEffect(() => {
- async function fetchNearbyLooData() {
- const variables = {
- lat: mapState.center.lat,
- lng: mapState.center.lng,
- radius: Math.ceil(mapState.radius),
- };
- const { props: toiletProps } = await getServerPageFindLoosNearby({
- variables,
- });
- if (toiletProps.data !== undefined) {
- setToiletData(toiletProps.data.loosByProximity);
- }
- }
- fetchNearbyLooData();
- }, [mapState]);
-
- const { id: selectedLooId } = router.query;
-
- /**
- * TODO: Fetch loo information as one big blob using SSR.
- */
- const [selectedLoo, setSelectedLoo] = React.useState(null);
- const [loadingSelectedLoo, setLoadingSelectedLoo] = React.useState(false);
- React.useEffect(() => {
- async function fetchNearbyLooData() {
- const { props: toiletProps } = await getServerPageFindLooById({
- variables: { id: selectedLooId as string },
- });
- if (toiletProps.data !== undefined) {
- setSelectedLoo(toiletProps.data.loo);
- setLoadingSelectedLoo(false);
- }
- }
- setLoadingSelectedLoo(true);
- fetchNearbyLooData();
- }, [selectedLooId]);
-
- // const { message } = queryString.parse(props.location.search);
- const message = 'TODO';
-
- // get the filter objects from config for the filters applied by the user
- const applied = config.filters.filter((filter) => filters[filter.id]);
+ const { filters, filtered, setFilters } = useFilters(data?.loosByProximity);
- // restrict the results to only those toilets which pass all of our filter requirements
- const toilets = toiletData.filter((toilet: { [x: string]: any }) =>
- applied.every((filter) => {
- const value = toilet[filter.id];
-
- if (value === null) {
- return false;
- }
-
- return !!value;
- })
- );
-
- const isLooPage = router.pathname === '/loos/[id]';
- const [shouldCenter, setShouldCenter] = React.useState(isLooPage);
-
- // set initial map center to toilet if on /loos/:id
- React.useEffect(() => {
- if (shouldCenter && selectedLoo) {
- setMapState({
- center: selectedLoo.location,
- });
-
- // don't recenter the map each time the id changes
- setShouldCenter(false);
- }
- }, [selectedLoo, shouldCenter, setMapState]);
-
- // set the map position if initialPosition prop exists
- React.useEffect(() => {
- if (initialPosition) {
- setMapState({
- center: initialPosition,
- });
- }
- }, [initialPosition, setMapState]);
-
- const [toiletPanelDimensions, setToiletPanelDimensions] = React.useState({});
-
- const pageTitle = config.getTitle(
- isLooPage && selectedLoo
- ? selectedLoo.name || 'Unnamed Toilet'
- : 'Find Toilet'
- );
+ const pageTitle = config.getTitle('Home');
return (
@@ -182,9 +57,7 @@ const HomePage = ({ initialPosition, ...props }) => {
right={0}
m={3}
maxWidth={326}
- maxHeight={`calc(100% - ${
- toiletPanelDimensions.height || 0
- }px - ${SIDEBAR_BOTTOM_MARGIN}px)`}
+ maxHeight={`calc(100% - 0px - ${SIDEBAR_BOTTOM_MARGIN}px)`}
overflowY="auto"
// center on small viewports
mx={['auto', 0]}
@@ -199,66 +72,15 @@ const HomePage = ({ initialPosition, ...props }) => {
{
- if (toilet.id === selectedLooId) {
- return {
- ...toilet,
- isHighlighted: true,
- };
- }
- return toilet;
- })}
+ loos={filtered}
center={mapState.center}
zoom={mapState.zoom}
onViewportChanged={setMapState}
- controlsOffset={toiletPanelDimensions.height}
+ controlsOffset={0}
/>
-
- {Boolean(selectedLooId) && selectedLoo && (
-
-
- {config.messages[message] && (
-
-
- {config.messages[message]}
-
-
- )}
-
-
- )}
);
};
-HomePage.propTypes = {
- initialPosition: PropTypes.shape({
- lat: PropTypes.number,
- lng: PropTypes.number,
- }),
-};
-
export default withApollo(HomePage as NextPage);
diff --git a/src/pages/loos/[id].tsx b/src/pages/loos/[id].tsx
deleted file mode 100644
index ff8bacd89..000000000
--- a/src/pages/loos/[id].tsx
+++ /dev/null
@@ -1,4 +0,0 @@
-import { NextPage } from 'next';
-import { withApollo } from '../../components/withApollo';
-import HomePage from '../index';
-export default HomePage as NextPage;
diff --git a/src/pages/loos/[id]/index.tsx b/src/pages/loos/[id]/index.tsx
new file mode 100644
index 000000000..1556eae83
--- /dev/null
+++ b/src/pages/loos/[id]/index.tsx
@@ -0,0 +1,152 @@
+import React from 'react';
+import Head from 'next/head';
+import dynamic from 'next/dynamic';
+import PageLayout from '../../../components/PageLayout';
+import Box from '../../../components/Box';
+import Sidebar from '../../../components/Sidebar';
+import Notification from '../../../components/Notification';
+import VisuallyHidden from '../../../components/VisuallyHidden';
+import ToiletDetailsPanel from '../../../components/ToiletDetailsPanel';
+import { useMapState } from '../../../components/MapState';
+import config from '../../../config';
+import { useRouter } from 'next/router';
+import { withApollo } from '../../../components/withApollo';
+import { NextPage } from 'next';
+import useFilters from '../../../components/useFilters';
+import {
+ useFindLoosNearbyQuery,
+ useFindLooByIdQuery,
+} from '../../../api-client/graphql';
+
+const SIDEBAR_BOTTOM_MARGIN = 32;
+const MapLoader = () => Loading map...
;
+const LooMap = dynamic(() => import('../../../components/LooMap'), {
+ loading: MapLoader,
+ ssr: false,
+});
+
+const LooPage = () => {
+ const [mapState, setMapState] = useMapState();
+ const router = useRouter();
+ const { id, message } = router.query;
+
+ const { data: looData, loading } = useFindLooByIdQuery({
+ variables: {
+ id: id as string,
+ },
+ });
+
+ const { data: nearbyLoos } = useFindLoosNearbyQuery({
+ variables: {
+ lat: looData?.loo.location.lat,
+ lng: looData?.loo.location.lng,
+ radius: Math.ceil(mapState.radius),
+ },
+ skip: !looData?.loo,
+ });
+
+ const { filters, filtered, setFilters } = useFilters(
+ nearbyLoos?.loosByProximity
+ );
+
+ // set initial map center to toilet if on /loos/:id
+ // React.useEffect(() => {
+ // if (shouldCenter && selectedLoo) {
+ // setMapState({
+ // center: selectedLoo.location,
+ // });
+
+ // // don't recenter the map each time the id changes
+ // setShouldCenter(false);
+ // }
+ // }, [selectedLoo, shouldCenter, setMapState]);
+
+ const [toiletPanelDimensions, setToiletPanelDimensions] = React.useState({});
+
+ const pageTitle = config.getTitle(looData?.loo.name || 'Unnamed Toilet');
+
+ return (
+
+
+ {pageTitle}
+
+
+
+ {pageTitle}
+
+
+
+
+ setMapState({ center })}
+ onUpdateMapPosition={setMapState}
+ mapCenter={mapState.center}
+ />
+
+
+ {
+ // if (toilet.id === selectedLooId) {
+ // return {
+ // ...toilet,
+ // isHighlighted: true,
+ // };
+ // }
+ // return toilet;
+ // })}
+ loos={filtered}
+ center={mapState.center}
+ zoom={mapState.zoom}
+ onViewportChanged={setMapState}
+ controlsOffset={toiletPanelDimensions.height}
+ />
+
+
+
+ {config.messages[message] && (
+
+
+ {config.messages[message]}
+
+
+ )}
+
+
+
+
+ );
+};
+
+export default withApollo(LooPage as NextPage);
diff --git a/src/pages/map/[lng]/[lat].tsx b/src/pages/map/[lng]/[lat].tsx
new file mode 100644
index 000000000..905c98cfa
--- /dev/null
+++ b/src/pages/map/[lng]/[lat].tsx
@@ -0,0 +1,114 @@
+import React, { useState } from 'react';
+import Head from 'next/head';
+import dynamic from 'next/dynamic';
+import PageLayout from '../../../components/PageLayout';
+import Box from '../../../components/Box';
+import Sidebar from '../../../components/Sidebar';
+import VisuallyHidden from '../../../components/VisuallyHidden';
+import { useMapState } from '../../../components/MapState';
+import config, { FILTERS_KEY } from '../../../config';
+import { useRouter } from 'next/router';
+import { withApollo } from '../../../components/withApollo';
+import { NextPage } from 'next';
+import { useFindLoosNearbyQuery } from '../../../api-client/graphql';
+
+const SIDEBAR_BOTTOM_MARGIN = 32;
+const MapLoader = () => Loading map...
;
+const LooMap = dynamic(() => import('../../../components/LooMap'), {
+ loading: MapLoader,
+ ssr: false,
+});
+
+const MapPage = () => {
+ const router = useRouter();
+ const [mapState, setMapState] = useMapState();
+ let initialState = config.getSettings(FILTERS_KEY);
+
+ // default any unsaved filters as 'false'
+ config.filters.forEach((filter) => {
+ initialState[filter.id] = initialState[filter.id] || false;
+ });
+
+ const [filters, setFilters] = useState(initialState);
+
+ // keep local storage and state in sync
+ React.useEffect(() => {
+ window.localStorage.setItem(FILTERS_KEY, JSON.stringify(filters));
+ }, [filters]);
+
+ const { data } = useFindLoosNearbyQuery({
+ variables: {
+ lat: mapState.center.lat,
+ lng: mapState.center.lng,
+ radius: Math.ceil(mapState.radius),
+ },
+ });
+
+ // get the filter objects from config for the filters applied by the user
+ const applied = config.filters.filter((filter) => filters[filter.id]);
+
+ const toilets = React.useMemo(() => {
+ if (data?.loosByProximity) {
+ return data.loosByProximity.filter((toilet: { [x: string]: any }) =>
+ applied.every((filter) => {
+ const value = toilet[filter.id];
+
+ if (value === null) {
+ return false;
+ }
+
+ return !!value;
+ })
+ );
+ }
+ return [];
+ }, [data?.loosByProximity, applied]);
+
+ const pageTitle = config.getTitle('Area Map');
+
+ return (
+
+
+ {pageTitle}
+
+
+
+ {pageTitle}
+
+
+
+
+ setMapState({ center })}
+ onUpdateMapPosition={setMapState}
+ mapCenter={mapState.center}
+ />
+
+
+
+
+
+ );
+};
+
+export default withApollo(HomePage as NextPage);