Skip to content

Commit

Permalink
feat: Implement miles/kilometer selector (#252)
Browse files Browse the repository at this point in the history
* feat: extend i18n data with distance

* feat: implement initial switch logic and styling

* chore: add prettier config file

* feat: extend translations with distance

* feat: create DistanceUnit Store

* refactor: use new DistanceUnit Store

* fix: add missing whitespaces

* chore: remove prettierrc

* Update App/stores/distanceUnit.tsx

Co-Authored-By: Amaury Martiny <[email protected]>

* fix: resolve review's comments

* fix: resolve lint errors
  • Loading branch information
matepapp authored and amaury1093 committed Oct 6, 2019
1 parent 33b6ca9 commit ca91f5b
Show file tree
Hide file tree
Showing 12 changed files with 146 additions and 105 deletions.
6 changes: 4 additions & 2 deletions App/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { Screens } from './Screens';
import { Background as LoadingBackground } from './Screens/Loading/Background';
import {
ApiContextProvider,
DistanceUnitProvider,
ErrorContextProvider,
FrequencyContextProvider,
LocationContextProvider
Expand All @@ -46,7 +47,6 @@ export function App () {
const [ready, setReady] = useState(false);
useEffect(() => {
Promise.all([

Font.loadAsync({
'gotham-black': require('../assets/fonts/Gotham-Black.ttf'),
'gotham-book': require('../assets/fonts/Gotham-Book.ttf')
Expand Down Expand Up @@ -74,7 +74,9 @@ export function App () {
<LocationContextProvider>
<ApiContextProvider>
<FrequencyContextProvider>
<Screens />
<DistanceUnitProvider>
<Screens />
</DistanceUnitProvider>
</FrequencyContextProvider>
</ApiContextProvider>
</LocationContextProvider>
Expand Down
88 changes: 45 additions & 43 deletions App/Screens/About/About.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,7 @@

import Constants from 'expo-constants';
import React from 'react';
import {
Linking,
Platform,
ScrollView,
StyleSheet,
Text,
View
} from 'react-native';
import { Linking, Platform, ScrollView, StyleSheet, Switch, Text, View } from 'react-native';
import { ScrollIntoView, wrapScrollView } from 'react-native-scroll-into-view';
import { scale } from 'react-native-size-matters';
import { NavigationInjectedProps } from 'react-navigation';
Expand All @@ -33,6 +26,7 @@ import { BackButton } from '../../components';
import { i18n } from '../../localization';
import { trackScreen } from '../../util/amplitude';
import * as theme from '../../util/theme';
import { useDistanceUnit } from '../../stores/distanceUnit';

const CustomScrollView = wrapScrollView(ScrollView);
const scrollViewOptions = {
Expand All @@ -57,9 +51,7 @@ const handleOpenAqi = () => {
};

const handleOpenArticle = () => {
Linking.openURL(
'http:https://berkeleyearth.org/air-pollution-and-cigarette-equivalence/'
);
Linking.openURL('http:https://berkeleyearth.org/air-pollution-and-cigarette-equivalence/');
};

const handleOpenGithub = () => {
Expand All @@ -77,35 +69,26 @@ interface AboutProps

export function About (props: AboutProps) {
const { navigation } = props;
const { distanceUnit, localizedDistanceUnit, setDistanceUnit } = useDistanceUnit();

trackScreen('ABOUT');

const toggleDistanceSwitch = (value: boolean) => setDistanceUnit(value ? 'km' : 'mile');

return (
<CustomScrollView
scrollIntoViewOptions={scrollViewOptions}
style={theme.withPadding}
>
<BackButton
onPress={() => navigation.goBack()}
style={styles.backButton}
/>
<CustomScrollView scrollIntoViewOptions={scrollViewOptions} style={theme.withPadding}>
<BackButton onPress={() => navigation.goBack()} style={styles.backButton} />

<View style={styles.section}>
<Text style={styles.h2}>
{i18n.t('about_how_do_you_calculate_the_number_of_cigarettes_title')}
</Text>
<Text style={theme.text}>
{i18n.t(
'about_how_do_you_calculate_the_number_of_cigarettes_message_1'
)}{' '}
{i18n.t('about_how_do_you_calculate_the_number_of_cigarettes_message_1')}{' '}
<Text onPress={handleOpenArticle} style={theme.link}>
{i18n.t(
'about_how_do_you_calculate_the_number_of_cigarettes_link_1'
)}
{i18n.t('about_how_do_you_calculate_the_number_of_cigarettes_link_1')}
</Text>
{i18n.t(
'about_how_do_you_calculate_the_number_of_cigarettes_message_2'
)}
{i18n.t('about_how_do_you_calculate_the_number_of_cigarettes_message_2')}
<Text style={styles.micro}>&micro;</Text>
g/m&sup3;
{' \u207D'}
Expand All @@ -126,15 +109,11 @@ export function About (props: AboutProps) {
style={styles.section}
>
<Text style={styles.h2}>{i18n.t('about_beta_inaccurate_title')}</Text>
<Text style={theme.text}>
{i18n.t('about_beta_inaccurate_message')}
</Text>
<Text style={theme.text}>{i18n.t('about_beta_inaccurate_message')}</Text>
</ScrollIntoView>

<View style={styles.section}>
<Text style={styles.h2}>
{i18n.t('about_where_does_data_come_from_title')}
</Text>
<Text style={styles.h2}>{i18n.t('about_where_does_data_come_from_title')}</Text>
<Text style={theme.text}>
{i18n.t('about_where_does_data_come_from_message_1')}{' '}
<Text onPress={handleOpenAqi} style={theme.link}>
Expand All @@ -145,17 +124,11 @@ export function About (props: AboutProps) {
</View>

<ScrollIntoView
onMount={
navigation.getParam('scrollInto') === 'aboutWhyIsTheStationSoFarTitle'
}
onMount={navigation.getParam('scrollInto') === 'aboutWhyIsTheStationSoFarTitle'}
style={styles.section}
>
<Text style={styles.h2}>
{i18n.t('about_why_is_the_station_so_far_title')}
</Text>
<Text style={theme.text}>
{i18n.t('about_why_is_the_station_so_far_message')}
</Text>
<Text style={styles.h2}>{i18n.t('about_why_is_the_station_so_far_title')}</Text>
<Text style={theme.text}>{i18n.t('about_why_is_the_station_so_far_message')}</Text>
</ScrollIntoView>

<View style={styles.section}>
Expand All @@ -169,6 +142,18 @@ export function About (props: AboutProps) {
</Text>
</View>

<View style={styles.distance}>
<Text style={styles.h2}>{i18n.t('about_distance_unit_title')}</Text>
<View style={styles.distanceSwitchWrapper}>
<Switch
onValueChange={toggleDistanceSwitch}
trackColor={{ true: theme.primaryColor, false: theme.iconBackgroundColor }}
value={distanceUnit === 'km'}
/>
<Text style={styles.distanceText}>{localizedDistanceUnit('long')}</Text>
</View>
</View>

<View style={styles.credits}>
<Text style={styles.h2}>{i18n.t('about_credits_title')}</Text>
<Text style={theme.text}>
Expand Down Expand Up @@ -217,6 +202,23 @@ const styles = StyleSheet.create({
marginBottom: theme.spacing.normal,
paddingTop: theme.spacing.big
},
distance: {
borderTopColor: theme.iconBackgroundColor,
borderTopWidth: 1,
marginBottom: theme.spacing.big,
paddingTop: theme.spacing.big
},
distanceSwitchWrapper: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center'
},
distanceText: {
...theme.text,
fontSize: scale(14),
paddingLeft: theme.spacing.small,
textTransform: 'capitalize'
},
h2: {
...theme.title,
fontSize: scale(20),
Expand Down
27 changes: 7 additions & 20 deletions App/Screens/Details/Details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,8 @@ import { Header } from './Header';
import { i18n } from '../../localization';
import { ApiContext, CurrentLocationContext } from '../../stores';
import { trackScreen } from '../../util/amplitude';
import {
distanceToStation,
getCorrectLatLng,
DistanceUnit
} from '../../util/station';
import { useDistanceUnit } from '../../stores/distanceUnit';
import { distanceToStation, getCorrectLatLng } from '../../util/station';

interface DetailsProps extends NavigationInjectedProps {}

Expand All @@ -43,9 +40,8 @@ export function Details (props: DetailsProps) {

const [showMap, setShowMap] = useState(false);
const { api } = useContext(ApiContext);
const { currentLocation: _currentLocation } = useContext(
CurrentLocationContext
);
const { currentLocation: _currentLocation } = useContext(CurrentLocationContext);
const { distanceUnit } = useDistanceUnit();

trackScreen('DETAILS');

Expand All @@ -68,14 +64,7 @@ export function Details (props: DetailsProps) {
// object` error. It's related to the MapView below.
const currentLocation = { ..._currentLocation! };

const haversineDistanceUnit = i18n.t(
'haversine_distance_unit'
) as DistanceUnit;
const distance = distanceToStation(
currentLocation!,
api!,
haversineDistanceUnit
);
const distance = distanceToStation(currentLocation!, api!, distanceUnit);

const station = {
description: api!.shootISmoke.station || '',
Expand All @@ -94,11 +83,9 @@ export function Details (props: DetailsProps) {
<MapView
initialRegion={{
latitude: (currentLocation.latitude + station.latitude) / 2,
latitudeDelta:
Math.abs(currentLocation.latitude - station.latitude) * 2,
latitudeDelta: Math.abs(currentLocation.latitude - station.latitude) * 2,
longitude: (currentLocation.longitude + station.longitude) / 2,
longitudeDelta:
Math.abs(currentLocation.longitude - station.longitude) * 2
longitudeDelta: Math.abs(currentLocation.longitude - station.longitude) * 2
}}
onMapReady={handleMapReady}
style={styles.map}
Expand Down
4 changes: 3 additions & 1 deletion App/Screens/Details/Distance/Distance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ import { StyleSheet, Text } from 'react-native';
import { Banner } from '../../../components';
import { i18n } from '../../../localization';
import * as theme from '../../../util/theme';
import { useDistanceUnit } from '../../../stores/distanceUnit';

interface DistanceProps {
distance: number;
}

export function Distance (props: DistanceProps) {
const distanceUnit = i18n.t('distance_unit');
const { localizedDistanceUnit } = useDistanceUnit();
const distanceUnit = localizedDistanceUnit('short');

return (
<Banner elevated shadowPosition="top" style={styles.banner}>
Expand Down
37 changes: 9 additions & 28 deletions App/Screens/Home/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,60 +15,41 @@
// along with Sh**t! I Smoke. If not, see <http:https://www.gnu.org/licenses/>.

import React, { useContext } from 'react';
import {
GestureResponderEvent,
Image,
StyleSheet,
Text,
View
} from 'react-native';
import { GestureResponderEvent, Image, StyleSheet, Text, View } from 'react-native';
import { scale } from 'react-native-size-matters';

import alert from '../../../../assets/images/alert.png';
import { ChangeLocation, CurrentLocation } from '../../../components';
import { i18n } from '../../../localization';
import { ApiContext, CurrentLocationContext } from '../../../stores';
import {
distanceToStation,
isStationTooFar,
DistanceUnit
} from '../../../util/station';
import { distanceToStation, isStationTooFar } from '../../../util/station';
import { useDistanceUnit } from '../../../stores/distanceUnit';
import * as theme from '../../../util/theme';

interface HeaderProps {
onChangeLocationClick: (event: GestureResponderEvent) => void;
onChangeLocationClick: (event: GestureResponderEvent) => void
}

export function Header (props: HeaderProps) {
const { api } = useContext(ApiContext)!;
const { currentLocation, isGps } = useContext(CurrentLocationContext);
const { distanceUnit, localizedDistanceUnit } = useDistanceUnit();
const { onChangeLocationClick } = props;

const distanceUnit = i18n.t('distance_unit');
const haversineDistanceUnit = i18n.t(
'haversine_distance_unit'
) as DistanceUnit;
const distance = distanceToStation(
currentLocation!,
api!,
haversineDistanceUnit
);
const shortDistanceUnit = localizedDistanceUnit('short');
const distance = distanceToStation(currentLocation!, api!, distanceUnit);
const isTooFar = isStationTooFar(currentLocation!, api!);

return (
<View style={styles.container}>
<View style={styles.currentLocation}>
<CurrentLocation
api={api!}
currentLocation={currentLocation!}
numberOfLines={2}
/>
<CurrentLocation api={api!} currentLocation={currentLocation!} numberOfLines={2} />
<View style={styles.distance}>
{isTooFar && <Image source={alert} style={styles.warning} />}
<Text style={styles.distanceText}>
{i18n.t('home_header_air_quality_station_distance', {
distanceToStation: distance,
distanceUnit
distanceUnit: shortDistanceUnit
})}{' '}
{!isGps && i18n.t('home_header_from_search')}
</Text>
Expand Down
9 changes: 6 additions & 3 deletions App/localization/languages/en-us.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,10 @@
"details_header_latest_update_ago": "ago",
"details_header_primary_pollutant_label": "Primary Pollutant:",
"details_distance_label": "AQI Station: {{distanceToStation}}{{distanceUnit}} away",
"distance_unit": "mi",
"haversine_distance_unit": "mile",
"distance_unit_short_km": "km",
"distance_unit_short_mi": "mi",
"distance_unit_long_km": "kilometer",
"distance_unit_long_mi": "mile",
"past_stations_loading": "Loading",
"past_stations_date_from_to": "{{startDate}} to {{endDate}}",
"past_stations_monitored_weekly": "Stations monitored from you during the week",
Expand All @@ -76,6 +78,7 @@
"about_weird_results_message_1": "We have also encountered a few surprising results: large cities with better air than small villages; sudden huge increases in the number of cigarettes; stations of the same town showing significantly different numbers... The fact is air quality depends on several factors such as temperature, pressure, humidity and even wind direction and intensity. If the result seems weird for you, check",
"about_weird_results_link_1": "WAQI",
"about_weird_results_message_2": "for more information and history on your station.",
"about_distance_unit_title": "Distance Unit",
"about_credits_title": "Credits",
"about_credits_concept_and_development": "Concept & Development by",
"about_credits_design_and_copywriting": "Design & Copywriting by",
Expand All @@ -84,4 +87,4 @@
"about_credits_available_github": "available on Github",
"about_language": "Language",
"current_location_unknown_station": "Unknown AQI station"
}
}
9 changes: 6 additions & 3 deletions App/localization/languages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,10 @@
"details_header_latest_update_ago": "ago",
"details_header_primary_pollutant_label": "Primary Pollutant:",
"details_distance_label": "AQI Station: {{distanceToStation}}{{distanceUnit}} away",
"distance_unit": "km",
"haversine_distance_unit": "km",
"distance_unit_short_km": "km",
"distance_unit_short_mi": "mi",
"distance_unit_long_km": "kilometer",
"distance_unit_long_mi": "mile",
"past_stations_loading": "Loading",
"past_stations_date_from_to": "{{startDate}} to {{endDate}}",
"past_stations_monitored_weekly": "Stations monitored from you during the week",
Expand All @@ -76,6 +78,7 @@
"about_weird_results_message_1": "We have also encountered a few surprising results: large cities with better air than small villages; sudden huge increases in the number of cigarettes; stations of the same town showing significantly different numbers... The fact is air quality depends on several factors such as temperature, pressure, humidity and even wind direction and intensity. If the result seems weird for you, check",
"about_weird_results_link_1": "WAQI",
"about_weird_results_message_2": "for more information and history on your station.",
"about_distance_unit_title": "Distance Unit",
"about_credits_title": "Credits",
"about_credits_concept_and_development": "Concept & Development by",
"about_credits_design_and_copywriting": "Design & Copywriting by",
Expand All @@ -84,4 +87,4 @@
"about_credits_available_github": "available on Github",
"about_language": "Language",
"current_location_unknown_station": "Unknown AQI station"
}
}
Loading

0 comments on commit ca91f5b

Please sign in to comment.