Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial stab at adding vanity domain modal logic #99

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 14 additions & 12 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
import {
faTasks,
faVial,
} from '@fortawesome/free-solid-svg-icons';
import { faTasks, faVial } from '@fortawesome/free-solid-svg-icons';
import { ThemeProvider } from '@material-ui/core/styles';

import AppBar from './Components/AppBar/AppBar';
import LocationModal from './Components/LocationModal/LocationModal';
// import Header from './Components/Header';
import LegalModal from './Components/LegalModal';
import theme from './theme';
import getViewportHeight from './utils/getViewportHeight';
import { trackGuideStatus } from './utils/tracking';
import Interstitial from './Components/Interstitial';
import GuideModal from './Components/GuideModal';
import Map from './Components/Map/Map';
import CheckSymptomsFlow from './Components/CheckSymptomsFlow';
@@ -20,6 +14,10 @@ import ActionType from './Components/Types/ActionType';
import LabelMapType from './Components/Types/LabelMapType';
import SearchFilterType from './Components/Types/SearchFilterType';

import getViewportHeight from './utils/getViewportHeight';
import theme from './theme';
import { trackGuideStatus } from './utils/tracking';

// Layout Component styles
const LayoutContainer = styled.div`
width: 100vw;
@@ -58,7 +56,7 @@ const defaultFilters: SearchFilterType = {
is_collecting_samples: false,
};

interface GeolocationCoordinates {
export interface GeolocationCoordinates {
coords: {
latitude: number;
longitude: number;
@@ -67,12 +65,15 @@ interface GeolocationCoordinates {
timestamp: number;
}

export const SearchContext = React.createContext<SearchFilterType>(defaultFilters);
export const SearchContext = React.createContext<SearchFilterType>(
defaultFilters
);
const geocoderContainerRef = React.createRef<any>();

let windowListener: any; // store event handler for resize events
let appointmentFlowUrl = '';
let actionType: ActionType;

const App = () => {
const [viewportHeight, setViewportHeight] = useState(0);
const [selectedPlace, setSelectedPlace] = useState(null);
@@ -127,7 +128,7 @@ const App = () => {
}}
/>

<LegalModal />
<Interstitial />

<MapContainer>
<Map
@@ -137,6 +138,7 @@ const App = () => {
setMap={setGlobalMap}
/>
</MapContainer>

{selectedPlace !== null && showLocationModal && (
<LocationModal
location={selectedPlace}
42 changes: 42 additions & 0 deletions src/Components/Interstitial.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import * as React from 'react';
import LegalModal from './LegalModal';
import RedirectModal from './RedirectModal';

type ModalType = 'none' | 'legal' | 'redirect';

const urls = ['communitiesagainstcovid.com', 'pruebadecovid.com'];

export default function Interstitial() {
const [modalType, setModalType] = React.useState<ModalType>('none');
const openLegalModal = React.useCallback(() => {
setModalType('legal');
}, []);

React.useEffect(() => {
// const { referrer } = document;
// TEMP for testing:
const referrer = 'https://www.communitiesagainstcovid.com';
const showRedirectModal: boolean = urls.reduce(
(prev: boolean, curr: string): boolean => prev || referrer.includes(curr),
false
);

if (showRedirectModal) {
setModalType('redirect');
} else {
setModalType('legal');
}
}, []);

switch (modalType) {
case 'legal': {
return <LegalModal />;
}
case 'redirect': {
return <RedirectModal onClose={openLegalModal} />;
}
default: {
return null;
}
}
}
114 changes: 114 additions & 0 deletions src/Components/RedirectModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import React from 'react';
import Autocomplete from '@material-ui/lab/Autocomplete';
import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogTitle from '@material-ui/core/DialogTitle';
import TextField from '@material-ui/core/TextField';
import useMediaQuery from '@material-ui/core/useMediaQuery';
import useTheme from '@material-ui/core/styles/useTheme';

import {
locateByGeoIp,
locateByBrowserGeo,
LocateCallback,
} from '../utils/locateUser';
import usePublicTestingSitesData from '../utils/usePublicTestingSitesData';

type Props = {
onClose?: Function;
};

type Coords = {
lat: number;
lon: number;
} | null;

export default function RedirectModal(props: Props) {
const { onClose } = props;
const [open, setOpen] = React.useState<boolean>(true);
const [testingSitesStatus, testingSitesData] = usePublicTestingSitesData();
// const [userLocation, setUserLocation] = React.useState<Coords>(null);
const inputRef = React.useRef<HTMLInputElement>();

const theme = useTheme();
const fullScreen = useMediaQuery(theme.breakpoints.down('xs'));

const handleClose = () => {
setOpen(false);
if (typeof onClose === 'function') {
onClose();
}
};

const handleOnLocateUser: LocateCallback = React.useCallback(
(geoLocation) => {
if (geoLocation && typeof inputRef.current !== 'undefined') {
// inputRef.current.value = `${geoLocation.latitude}, ${geoLocation.longitude}`;
}
},
[]
);

React.useEffect(() => {
locateByGeoIp((geoLocation) => {
handleOnLocateUser(geoLocation);
locateByBrowserGeo(handleOnLocateUser);
});
}, [handleOnLocateUser]);

return (
<Dialog
fullScreen={fullScreen}
open={open}
onClose={handleClose}
aria-labelledby="form-dialog-title"
>
<DialogTitle id="form-dialog-title">Get to the right place</DialogTitle>

<DialogContent style={{ overflow: 'hidden' }}>
<DialogContentText>
In order to show you the most relevant information, please confirm or
update the information below. This website is developed in partnership
with public health departments from across the country. If the box is
empty, you may need to click &ldquo;Allow&rdquo; when prompted by your
browser.
</DialogContentText>
<Autocomplete
id="places"
freeSolo={false}
options={testingSitesData}
loading={testingSitesStatus === 'loading'}
disabled={testingSitesStatus !== 'done'}
getOptionLabel={(option) => option.name}
renderInput={(params) => (
<TextField
{...params}
margin="normal"
id="place"
variant="outlined"
size="medium"
color="secondary"
inputRef={inputRef}
label="You can search by typing into the box."
helperText="ex: '94025', 'Menlo Park', 'San Mateo County', 'California'"
type="search"
/>
)}
/>
</DialogContent>

<DialogActions>
<Button onClick={handleClose} color="primary">
Close
</Button>

<Button onClick={handleClose} color="primary">
Go
</Button>
</DialogActions>
</Dialog>
);
}
15 changes: 10 additions & 5 deletions src/constants.tsx
Original file line number Diff line number Diff line change
@@ -3,12 +3,17 @@
export const ADD_LOCATION_FORM_URL: string =
'https://survey123.arcgis.com/share/ce7939ac077c4551b15bfbf3579a259c';

export const CAC_ORG_URL: string = 'https://codersagainstcovid.org'
export const CAC_ORG_URL: string = 'https://codersagainstcovid.org';
export const GITHUB_URL: string = 'https://github.com/codersagainstcovidorg';
export const API_URL: string =
'https://github.com/codersagainstcovidorg/covid19testing-backend';
export const URISA_GISCORPS_URL: string = 'https://www.giscorps.org/covid19-esri-282/'

export const URISA_GISCORPS_URL: string =
'https://www.giscorps.org/covid19-esri-282/';

export const TESTING_CRITERIA_URL_CDC: string = 'https://www.cdc.gov/coronavirus/2019-ncov/symptoms-testing/testing.html';
export const SYMPTOM_CHECKER_URL_CDC: string = 'https://www.cdc.gov/coronavirus/2019-ncov/symptoms-testing/index.html#cdc-chat-bot-open';
export const TESTING_CRITERIA_URL_CDC: string =
'https://www.cdc.gov/coronavirus/2019-ncov/symptoms-testing/testing.html';
export const SYMPTOM_CHECKER_URL_CDC: string =
'https://www.cdc.gov/coronavirus/2019-ncov/symptoms-testing/index.html#cdc-chat-bot-open';

export const GEOLOCATION_URL: string =
'https://pro.ip-api.com/json/?fields=status,lat,lon&key=WNyJJH2siHnfQU0';
38 changes: 38 additions & 0 deletions src/utils/locateUser.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { GEOLOCATION_URL } from '../constants';

export type LocateCallback = (location?: {
latitude: number;
longitude: number;
}) => any;

export async function locateByGeoIp(callback: LocateCallback) {
try {
const response = await fetch(GEOLOCATION_URL);
const data = await response.json();

if (data.status === 'success') {
const { lat, lon } = data;
callback({ latitude: lat, longitude: lon });
}
} catch (e) {
console.error('failed to get location from geoIp', e);
callback();
}
}

export function locateByBrowserGeo(callback: LocateCallback) {
navigator.geolocation.getCurrentPosition(
(res) => {
const { latitude, longitude } = res.coords;
callback({ latitude, longitude });
},
(e: any) => {
console.error('failed to get location from browser', e);
callback();
},
{
enableHighAccuracy: true,
timeout: 2000,
}
);
}
47 changes: 47 additions & 0 deletions src/utils/usePublicTestingSitesData.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';

const TESTING_SITES_URL =
'https://spreadsheets.google.com/feeds/list/1UluXXXujY87UVSPkcsY4slStZH6HojlBLZSAcR4iqQY/1/public/full?alt=json';

function formatEntries(entry: any) {
return {
name: entry?.gsx$areaname?.$t,
url: entry?.gsx$url?.$t,
};
}

type Status = 'init' | 'loading' | 'done' | 'error';

type Entry = {
name: string;
url?: string;
};

type Entries = Entry[] | [];

export default function usePublicTestingSitesData(): [Status, Entries] {
const [status, setStatus] = React.useState<Status>('init');
const [data, setData] = React.useState<Entries>([]);

async function fetchData() {
setStatus('loading');

try {
const res = await fetch(TESTING_SITES_URL);
const json = await res.json();

const mappedData = json.feed.entry.map(formatEntries);

setData(mappedData);
setStatus('done');
} catch (e) {
setStatus('error');
}
}

React.useEffect(() => {
fetchData();
}, []);

return [status, data];
}