diff --git a/App/App.tsx b/App/App.tsx index fd1547d3..614260e8 100644 --- a/App/App.tsx +++ b/App/App.tsx @@ -25,6 +25,7 @@ import { Background as LoadingBackground } from './Screens/Loading/Background'; import { ApiContextProvider, ErrorContextProvider, + FrequencyContextProvider, LocationContextProvider } from './stores'; import { setupAmplitude, track } from './util/amplitude'; @@ -45,6 +46,7 @@ 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') @@ -71,7 +73,9 @@ export function App () { - + + + diff --git a/App/Screens/Home/Footer/Footer.tsx b/App/Screens/Home/Footer/Footer.tsx index 4a21b81e..e79d31d7 100644 --- a/App/Screens/Home/Footer/Footer.tsx +++ b/App/Screens/Home/Footer/Footer.tsx @@ -15,7 +15,7 @@ // along with Sh**t! I Smoke. If not, see . import React, { useContext } from 'react'; -import { Share, StyleSheet, View, ViewProps } from 'react-native'; +import { Platform, Share, StyleSheet, View, ViewProps } from 'react-native'; import { NavigationInjectedProps } from 'react-navigation'; import { aboutSections } from '../../About'; @@ -26,7 +26,7 @@ import { track } from '../../../util/amplitude'; import { isStationTooFar } from '../../../util/station'; import * as theme from '../../../util/theme'; -interface FooterProps extends NavigationInjectedProps, ViewProps {} +interface FooterProps extends NavigationInjectedProps, ViewProps { } export function Footer (props: FooterProps) { const { api } = useContext(ApiContext)!; @@ -55,12 +55,18 @@ export function Footer (props: FooterProps) { function handleShare () { track('HOME_SCREEN_SHARE_CLICK'); - Share.share({ - title: i18n.t('home_share_title'), - message: i18n.t('home_share_message', { - cigarettes: parseFloat(api!.shootISmoke.cigarettes).toFixed(2) - }) - }); + + // Share doesn't currently support images on Android, so the text version + if (Platform.OS === 'ios') { + props.navigation.navigate('ShareModal'); + } else { + Share.share({ + title: i18n.t('home_share_title'), + message: i18n.t('home_share_message', { + cigarettes: parseFloat(api!.shootISmoke.cigarettes).toFixed(2) + }) + }); + } } const renderBigButton = () => { diff --git a/App/Screens/Home/Home.tsx b/App/Screens/Home/Home.tsx index c38590a3..9ea2d3ef 100644 --- a/App/Screens/Home/Home.tsx +++ b/App/Screens/Home/Home.tsx @@ -14,50 +14,33 @@ // You should have received a copy of the GNU General Public License // along with Sh**t! I Smoke. If not, see . -import React, { useContext, useState } from 'react'; +import React, { useContext } from 'react'; import { ScrollView, StyleSheet, View } from 'react-native'; import { NavigationInjectedProps } from 'react-navigation'; import { AdditionalInfo } from './AdditionalInfo'; -import { CigaretteBlock } from './CigaretteBlock'; +import { CigaretteBlock, getCigaretteCount } from '../../components'; import { Footer } from './Footer'; import { Header } from './Header'; -import { Frequency, SelectFrequency } from './SelectFrequency'; +import { SelectFrequency } from './SelectFrequency'; import { SmokeVideo } from './SmokeVideo'; -import { ApiContext } from '../../stores'; -import { Api } from '../../stores/fetchApi'; +import { ApiContext, FrequencyContext } from '../../stores'; import { track, trackScreen } from '../../util/amplitude'; import * as theme from '../../util/theme'; -interface HomeProps extends NavigationInjectedProps {} - -/** - * Compute the number of cigarettes to show - */ -function getCigaretteCount (frequency: Frequency, api: Api) { - switch (frequency) { - case 'daily': { - return api.shootISmoke.cigarettes; - } - case 'weekly': - return api.shootISmoke.cigarettes * 7; - case 'monthly': { - return api.shootISmoke.cigarettes * 30; - } - } -} +interface HomeProps extends NavigationInjectedProps { } export function Home (props: HomeProps) { const { api } = useContext(ApiContext); - const [frequency, setFrenquency] = useState('daily'); - - const cigaretteCount = getCigaretteCount(frequency, api!); + const { frequency, setFrequency } = useContext(FrequencyContext); trackScreen('HOME'); + const cigarettesPerDay = api!.shootISmoke.cigarettes; + return ( - +
{ track('HOME_SCREEN_CHANGE_LOCATION_CLICK'); @@ -66,7 +49,7 @@ export function Home (props: HomeProps) { /> @@ -81,7 +64,7 @@ export function Home (props: HomeProps) { track('HOME_SCREEN_MONTHLY_CLICK'); } - setFrenquency(freq); + setFrequency(freq); }} style={styles.withMargin} /> diff --git a/App/Screens/Home/SelectFrequency/SelectFrequency.tsx b/App/Screens/Home/SelectFrequency/SelectFrequency.tsx index 31f696cf..a10731d5 100644 --- a/App/Screens/Home/SelectFrequency/SelectFrequency.tsx +++ b/App/Screens/Home/SelectFrequency/SelectFrequency.tsx @@ -62,7 +62,7 @@ export function SelectFrequency (props: SelectFrequencyProps) { }} style={styles.boxButton} > - {i18n.t('home_frequency_daily')} + {i18n.t('frequency_daily')} - {i18n.t('home_frequency_weekly')} + {i18n.t('frequency_weekly')} - {i18n.t('home_frequency_monthly')} + {i18n.t('frequency_monthly')} ); diff --git a/App/Screens/Screens.tsx b/App/Screens/Screens.tsx index 1e0c16b2..e284d93e 100644 --- a/App/Screens/Screens.tsx +++ b/App/Screens/Screens.tsx @@ -28,6 +28,7 @@ import { NavigationStackOptions, NavigationStackProp } from 'react-navigation-stack'; +import { fadeIn } from 'react-navigation-transitions'; import { About } from './About'; import { Details } from './Details'; @@ -35,6 +36,7 @@ import { ErrorScreen } from './ErrorScreen'; import { Home } from './Home'; import { Loading } from './Loading'; import { Search } from './Search'; +import { ShareScreen } from './ShareScreen'; import { ApiContext, ErrorContext } from '../stores'; import { Api } from '../stores/fetchApi'; import * as theme from '../util/theme'; @@ -60,7 +62,7 @@ function stackNavigatorOptions (initialRouteName: string) { /** * The main stack navigator, for the app. */ -const RootStack = createAppContainer( +const MainStack = createAppContainer( createStackNavigator( { About: { @@ -80,6 +82,23 @@ const RootStack = createAppContainer( ) ); +const RootStack = createAppContainer( + createStackNavigator( + { + Main: { + screen: MainStack + }, + ShareModal: { + screen: ShareScreen + } + }, + { + mode: 'modal', + headerMode: 'none', + transitionConfig: () => fadeIn() + } + ) +); /** * A stack navigator for the error case. */ diff --git a/App/Screens/ShareScreen/ShareImage/ShareImage.tsx b/App/Screens/ShareScreen/ShareImage/ShareImage.tsx new file mode 100644 index 00000000..1556f23f --- /dev/null +++ b/App/Screens/ShareScreen/ShareImage/ShareImage.tsx @@ -0,0 +1,64 @@ +// Sh**t! I Smoke +// Copyright (C) 2018-2019 Marcelo S. Coelho, Amaury Martiny + +// Sh**t! I Smoke is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Sh**t! I Smoke is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Sh**t! I Smoke. If not, see . + +import React, { useContext } from 'react'; +import { + StyleSheet, + Text, + View +} from 'react-native'; + +import { CigaretteBlock, CurrentLocation } from '../../../components'; +import { ApiContext, CurrentLocationContext, FrequencyContext } from '../../../stores'; +import * as theme from '../../../util/theme'; + +export function ShareImage () { + const { api } = useContext(ApiContext)!; + const { currentLocation } = useContext(CurrentLocationContext); + const { frequency } = useContext(FrequencyContext); + + const cigarettesPerDay = api!.shootISmoke.cigarettes; + + return ( + + + + + + + https://shootismoke.github.io/ + + + ); +} + +const styles = StyleSheet.create({ + container: { + ...theme.withPadding, + alignItems: 'flex-start', + flexDirection: 'column', + paddingTop: theme.spacing.normal, + paddingBottom: theme.spacing.normal, + width: 400, + backgroundColor: 'white' + }, + + urlText: { + ...theme.text, + alignSelf: 'center', + marginTop: theme.spacing.mini + } +}); diff --git a/App/Screens/ShareScreen/ShareImage/index.ts b/App/Screens/ShareScreen/ShareImage/index.ts new file mode 100644 index 00000000..58130df3 --- /dev/null +++ b/App/Screens/ShareScreen/ShareImage/index.ts @@ -0,0 +1,17 @@ +// Sh**t! I Smoke +// Copyright (C) 2018-2019 Marcelo S. Coelho, Amaury Martiny + +// Sh**t! I Smoke is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Sh**t! I Smoke is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Sh**t! I Smoke. If not, see . + +export * from './ShareImage'; diff --git a/App/Screens/ShareScreen/ShareScreen.tsx b/App/Screens/ShareScreen/ShareScreen.tsx new file mode 100644 index 00000000..d8d66ea0 --- /dev/null +++ b/App/Screens/ShareScreen/ShareScreen.tsx @@ -0,0 +1,91 @@ +// Sh**t! I Smoke +// Copyright (C) 2018-2019 Marcelo S. Coelho, Amaury Martiny + +// Sh**t! I Smoke is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Sh**t! I Smoke is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Sh**t! I Smoke. If not, see . + +import React, { useEffect, createRef } from 'react'; +import { Share, StyleSheet, View } from 'react-native'; +import { NavigationInjectedProps } from 'react-navigation'; +import { captureRef } from 'react-native-view-shot'; + +import { ShareImage } from './ShareImage'; +import { i18n } from '../../localization'; +import { Button } from '../../components'; +import * as theme from '../../util/theme'; + +interface ShareScreenProps extends NavigationInjectedProps { } + +export function ShareScreen (props: ShareScreenProps) { + const refViewShot = createRef(); + + useEffect(() => { + setTimeout(async () => { + try { + const uri = await captureRef(refViewShot, { + format: 'png', + quality: 1 + }); + + await Share.share({ + url: uri + }); + } catch (error) { + } + + handleDismiss(); + }, 750); + }); + + const handleDismiss = () => { + props.navigation.goBack(); + }; + return ( + + + + + + + + + + + ); +} + +const styles = StyleSheet.create({ + container: { + ...theme.fullScreen, + ...theme.withPadding, + flexGrow: 1, + flexDirection: 'column', + backgroundColor: 'rgba(0,0,0,0.8)' + }, + + buttonContainer: { + paddingVertical: theme.spacing.normal + }, + + button: { + paddingHorizontal: theme.spacing.mini + }, + + imageContainer: { + alignItems: 'center', + justifyContent: 'center', + transform: [ + { scale: 0.8 } + ] + } +}); diff --git a/App/Screens/ShareScreen/index.ts b/App/Screens/ShareScreen/index.ts new file mode 100644 index 00000000..f3a9496e --- /dev/null +++ b/App/Screens/ShareScreen/index.ts @@ -0,0 +1,17 @@ +// Sh**t! I Smoke +// Copyright (C) 2018-2019 Marcelo S. Coelho, Amaury Martiny + +// Sh**t! I Smoke is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Sh**t! I Smoke is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Sh**t! I Smoke. If not, see . + +export * from './ShareScreen'; diff --git a/App/Screens/Home/CigaretteBlock/CigaretteBlock.tsx b/App/components/CigaretteBlock/CigaretteBlock.tsx similarity index 71% rename from App/Screens/Home/CigaretteBlock/CigaretteBlock.tsx rename to App/components/CigaretteBlock/CigaretteBlock.tsx index fd5f57a7..b4b1b382 100644 --- a/App/Screens/Home/CigaretteBlock/CigaretteBlock.tsx +++ b/App/components/CigaretteBlock/CigaretteBlock.tsx @@ -17,16 +17,17 @@ import React, { useContext } from 'react'; import { StyleSheet, Text, View, ViewProps } from 'react-native'; -import { Cigarettes } from '../../../components'; -import { i18n } from '../../../localization'; -import { Frequency } from '../SelectFrequency'; -import { CurrentLocationContext } from '../../../stores'; +import { Cigarettes } from '../Cigarettes'; +import { i18n } from '../../localization'; +import { Frequency } from '../../Screens/Home/SelectFrequency'; +import { CurrentLocationContext } from '../../stores'; import swearWords from './swearWords'; -import * as theme from '../../../util/theme'; +import * as theme from '../../util/theme'; interface CigaretteBlockProps extends ViewProps { - cigaretteCount: number; + cigarettesPerDay: number; frequency: Frequency; + displayFrequency?: boolean } function getSwearWord (cigaretteCount: number) { @@ -36,9 +37,27 @@ function getSwearWord (cigaretteCount: number) { return swearWords[Math.floor(Math.random() * swearWords.length)]; } +/** + * Compute the number of cigarettes to show + */ +export function getCigaretteCount (frequency: Frequency, cigarettePerDay: number) { + switch (frequency) { + case 'daily': { + return cigarettePerDay; + } + case 'weekly': + return cigarettePerDay * 7; + case 'monthly': { + return cigarettePerDay * 30; + } + } +} + export function CigaretteBlock (props: CigaretteBlockProps) { const { isGps } = useContext(CurrentLocationContext)!; - const { cigaretteCount, frequency, style, ...rest } = props; + const { cigarettesPerDay, frequency, style, displayFrequency, ...rest } = props; + + const cigaretteCount = getCigaretteCount(frequency, cigarettesPerDay); const renderCigarettesText = () => { // Round to 1 decimal @@ -59,13 +78,15 @@ export function CigaretteBlock (props: CigaretteBlockProps) { const [firstPartText, secondPartText] = text.split('<'); + const frequencyText = displayFrequency ? ({i18n.t(`frequency_${frequency}`)}) : null; + return ( {firstPartText} {secondPartText.split('>')[0]} - {secondPartText.split('>')[1]} + {secondPartText.split('>')[1]} {frequencyText} ); }; diff --git a/App/Screens/Home/CigaretteBlock/index.ts b/App/components/CigaretteBlock/index.ts similarity index 100% rename from App/Screens/Home/CigaretteBlock/index.ts rename to App/components/CigaretteBlock/index.ts diff --git a/App/Screens/Home/CigaretteBlock/swearWords.ts b/App/components/CigaretteBlock/swearWords.ts similarity index 95% rename from App/Screens/Home/CigaretteBlock/swearWords.ts rename to App/components/CigaretteBlock/swearWords.ts index 27060dd5..7a0d2959 100644 --- a/App/Screens/Home/CigaretteBlock/swearWords.ts +++ b/App/components/CigaretteBlock/swearWords.ts @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Sh**t! I Smoke. If not, see . -import { i18n } from '../../../localization'; +import { i18n } from '../../localization'; export default [ i18n.t('home_swear_word_shoot'), diff --git a/App/components/index.ts b/App/components/index.ts index 6962280e..0447253a 100644 --- a/App/components/index.ts +++ b/App/components/index.ts @@ -19,6 +19,7 @@ export * from './Banner'; export * from './BoxButton'; export * from './Button'; export * from './Cigarettes'; +export * from './CigaretteBlock'; export * from './ChangeLocation'; export * from './CurrentLocation'; export * from './ListItem'; diff --git a/App/localization/languages/en-us.json b/App/localization/languages/en-us.json index e9009618..d569872a 100644 --- a/App/localization/languages/en-us.json +++ b/App/localization/languages/en-us.json @@ -1,9 +1,13 @@ { + "close_button": "Close", "error_screen_common_sorry": "Sorry!\n", "error_screen_error_cannot_load_cigarettes": "We cannot load your cigarettes.", "error_screen_choose_other_location": "Choose other location", "error_screen_error_description": "There's either a problem with our databases, or you don't have any Air Monitoring Stations near you. Try again later!", "error_screen_error_message": "Error: {{errorText}}", + "frequency_daily": "daily", + "frequency_weekly": "weekly", + "frequency_monthly": "monthly", "home_station_too_far_message": "We couldn’t find a closer station to you.\nResults may be inaccurate at this distance.", "home_beta_not_accurate": "Numbers may not be 100% accurate.", "home_share_title": "Did you know that you may be smoking up to 20 cigarettes per day, just for living in a big city?", @@ -17,9 +21,6 @@ "home_btn_more_details": "More details", "home_btn_faq_about": "Faq/About", "home_btn_share": "Share", - "home_frequency_daily": "daily", - "home_frequency_weekly": "weekly", - "home_frequency_monthly": "monthly", "home_common_you_smoke": "You smoke", "home_common_you_d_smoke": "You'd smoke", "home_common_you_ll_smoke": "You'll smoke", @@ -83,4 +84,4 @@ "about_credits_available_github": "available on Github", "about_language": "Language", "current_location_unknown_station": "Unknown AQI station" -} +} \ No newline at end of file diff --git a/App/localization/languages/en.json b/App/localization/languages/en.json index 0f518e90..5ffbe8f2 100644 --- a/App/localization/languages/en.json +++ b/App/localization/languages/en.json @@ -1,9 +1,13 @@ { + "close_button": "Close", "error_screen_common_sorry": "Sorry!\n", "error_screen_error_cannot_load_cigarettes": "We cannot load your cigarettes.", "error_screen_choose_other_location": "Choose other location", "error_screen_error_description": "There's either a problem with our databases, or you don't have any Air Monitoring Stations near you. Try again later!", "error_screen_error_message": "Error: {{errorText}}", + "frequency_daily": "daily", + "frequency_weekly": "weekly", + "frequency_monthly": "monthly", "home_station_too_far_message": "We couldn’t find a closer station to you.\nResults may be inaccurate at this distance.", "home_beta_not_accurate": "Numbers may not be 100% accurate.", "home_share_title": "Did you know that you may be smoking up to 20 cigarettes per day, just for living in a big city?", @@ -17,9 +21,6 @@ "home_btn_more_details": "More details", "home_btn_faq_about": "Faq/About", "home_btn_share": "Share", - "home_frequency_daily": "daily", - "home_frequency_weekly": "weekly", - "home_frequency_monthly": "monthly", "home_common_you_smoke": "You smoke", "home_common_you_d_smoke": "You'd smoke", "home_common_you_ll_smoke": "You'll smoke", @@ -83,4 +84,4 @@ "about_credits_available_github": "available on Github", "about_language": "Language", "current_location_unknown_station": "Unknown AQI station" -} +} \ No newline at end of file diff --git a/App/stores/frequency.tsx b/App/stores/frequency.tsx new file mode 100644 index 00000000..d431fa3e --- /dev/null +++ b/App/stores/frequency.tsx @@ -0,0 +1,46 @@ +// Sh**t! I Smoke +// Copyright (C) 2018-2019 Marcelo S. Coelho, Amaury Martiny + +// Sh**t! I Smoke is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Sh**t! I Smoke is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Sh**t! I Smoke. If not, see . + +import React, { createContext, useState } from 'react'; +import { Frequency } from '../Screens/Home/SelectFrequency'; + +import { noop } from '../util/noop'; + +interface Context { + frequency: Frequency + setFrequency: (newFrequency: Frequency) => void; +} + +export const FrequencyContext = createContext({ frequency: 'daily', setFrequency: noop }); + +export function FrequencyContextProvider ({ + children +}: { + children: JSX.Element; +}) { + const [frequency, setFrequency] = useState('daily'); + + return ( + + {children} + + ); +} diff --git a/App/stores/index.ts b/App/stores/index.ts index ea165d38..49c36226 100644 --- a/App/stores/index.ts +++ b/App/stores/index.ts @@ -16,4 +16,5 @@ export * from './api'; export * from './error'; +export * from './frequency'; export * from './location'; diff --git a/package.json b/package.json index 2566a843..a0b94d4d 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "expo-localization": "^7.0.0", "expo-location": "^7.0.0", "expo-permissions": "^7.0.0", + "expo-sharing": "~7.0.0", "fp-ts": "^2.0.5", "haversine": "^1.1.1", "i18n-js": "^3.3.0", @@ -40,8 +41,10 @@ "react-native-reanimated": "~1.2.0", "react-native-scroll-into-view": "^1.0.3", "react-native-size-matters": "^0.2.1", + "react-native-view-shot": "~2.6.0", "react-navigation": "^4.0.10", "react-navigation-stack": "^1.8.1", + "react-navigation-transitions": "^1.0.12", "retry-ts": "^0.1.0", "sentry-expo": "~2.0.1", "truncate": "^2.1.0" diff --git a/yarn.lock b/yarn.lock index 3a3790b0..8adc22c2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6496,7 +6496,7 @@ react-native-svg@*: resolved "https://registry.yarnpkg.com/react-native-svg/-/react-native-svg-9.5.3.tgz#2389f3ffd700c6441166496a1aeade31ead89c59" integrity sha512-VUOe4TLz7RFdmm/XT9EH87VSwlRykx49qbwJMA+dh9eFM7KPY1qH3kEyN7uRCqJD2eE8toxt9NpjR6ByvtPNlA== -react-native-view-shot@2.6.0: +react-native-view-shot@2.6.0, react-native-view-shot@~2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/react-native-view-shot/-/react-native-view-shot-2.6.0.tgz#3b23675826f67658366352c4b97b59a6aded2f43" integrity sha512-yO9vWi/11m2hEJl8FrW1SMeVzFfPtMKh20MUInGqlsL0H8Ya2JGGlFfrBzx1KiFR2hFb5OdsTLYNtcVZtJ6pLQ== @@ -6563,6 +6563,11 @@ react-navigation-stack@^1.8.1: dependencies: prop-types "^15.7.2" +react-navigation-transitions@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/react-navigation-transitions/-/react-navigation-transitions-1.0.12.tgz#7307668bc7d5fdbdc1954224e7891f7fe6cb5f74" + integrity sha512-Hp0wX9KoXwsFch6Fgiz9HpGjQZDhnyucLbCai0LcaOC3VpmgYmvkbeAg/mQ5Z5exuY6PPrh/+FXU00yMPDHkcw== + react-navigation@^4.0.10: version "4.0.10" resolved "https://registry.yarnpkg.com/react-navigation/-/react-navigation-4.0.10.tgz#ddf41134600689d6ba99e35dd22ba1f664f91e5c"