Skip to content

Commit

Permalink
feat: Replace share text with an image on iOS (#242)
Browse files Browse the repository at this point in the history
* Moved Cigarette in to a common component

* Made frequency terms global (not screen specific)

* Moved the cigarette count calculation to the cigarette block

* Moved frequency from Home state to global context

* Linting

* Added a share screen and share image

* Added a ShareScreen modal overlay

* i18n for the close button

* Fixed a circular import

* Replaced previous share with the image

* Keep the existing text notification on Android

* Linting

* Resolved Merge Conflict

* 👌🏻 Renamed currentFrequency as frequency

* 👌🏻 Added a blank line between relative and absolute imports

* 👌🏻 Removed the hard coded values (replaced with theme values)

* 👌🏻 Linting
  • Loading branch information
jamespearson authored and amaury1093 committed Oct 3, 2019
1 parent 8fdf14b commit 3250390
Show file tree
Hide file tree
Showing 19 changed files with 339 additions and 59 deletions.
6 changes: 5 additions & 1 deletion App/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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')
Expand All @@ -71,7 +73,9 @@ export function App () {
<ErrorContextProvider>
<LocationContextProvider>
<ApiContextProvider>
<Screens />
<FrequencyContextProvider>
<Screens />
</FrequencyContextProvider>
</ApiContextProvider>
</LocationContextProvider>
</ErrorContextProvider>
Expand Down
22 changes: 14 additions & 8 deletions App/Screens/Home/Footer/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// along with Sh**t! I Smoke. If not, see <http:https://www.gnu.org/licenses/>.

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';
Expand All @@ -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)!;
Expand Down Expand Up @@ -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 = () => {
Expand Down
39 changes: 11 additions & 28 deletions App/Screens/Home/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 <http:https://www.gnu.org/licenses/>.

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<Frequency>('daily');

const cigaretteCount = getCigaretteCount(frequency, api!);
const { frequency, setFrequency } = useContext(FrequencyContext);

trackScreen('HOME');

const cigarettesPerDay = api!.shootISmoke.cigarettes;

return (
<View style={styles.container}>
<SmokeVideo cigarettes={cigaretteCount} />
<SmokeVideo cigarettes={getCigaretteCount(frequency, cigarettesPerDay)} />
<Header
onChangeLocationClick={() => {
track('HOME_SCREEN_CHANGE_LOCATION_CLICK');
Expand All @@ -66,7 +49,7 @@ export function Home (props: HomeProps) {
/>
<ScrollView bounces={false}>
<CigaretteBlock
cigaretteCount={cigaretteCount}
cigarettesPerDay={cigarettesPerDay}
frequency={frequency}
style={styles.withMargin}
/>
Expand All @@ -81,7 +64,7 @@ export function Home (props: HomeProps) {
track('HOME_SCREEN_MONTHLY_CLICK');
}

setFrenquency(freq);
setFrequency(freq);
}}
style={styles.withMargin}
/>
Expand Down
6 changes: 3 additions & 3 deletions App/Screens/Home/SelectFrequency/SelectFrequency.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export function SelectFrequency (props: SelectFrequencyProps) {
}}
style={styles.boxButton}
>
{i18n.t('home_frequency_daily')}
{i18n.t('frequency_daily')}
</BoxButton>
<BoxButton
active={frequency === 'weekly'}
Expand All @@ -78,7 +78,7 @@ export function SelectFrequency (props: SelectFrequencyProps) {
}}
style={styles.boxButton}
>
{i18n.t('home_frequency_weekly')}
{i18n.t('frequency_weekly')}
</BoxButton>

<BoxButton
Expand All @@ -93,7 +93,7 @@ export function SelectFrequency (props: SelectFrequencyProps) {
}}
style={styles.boxButton}
>
{i18n.t('home_frequency_monthly')}
{i18n.t('frequency_monthly')}
</BoxButton>
</ScrollView>
);
Expand Down
21 changes: 20 additions & 1 deletion App/Screens/Screens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ import {
NavigationStackOptions,
NavigationStackProp
} from 'react-navigation-stack';
import { fadeIn } from 'react-navigation-transitions';

import { About } from './About';
import { Details } from './Details';
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';
Expand All @@ -60,7 +62,7 @@ function stackNavigatorOptions (initialRouteName: string) {
/**
* The main stack navigator, for the app.
*/
const RootStack = createAppContainer(
const MainStack = createAppContainer(
createStackNavigator(
{
About: {
Expand All @@ -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.
*/
Expand Down
64 changes: 64 additions & 0 deletions App/Screens/ShareScreen/ShareImage/ShareImage.tsx
Original file line number Diff line number Diff line change
@@ -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 <http:https://www.gnu.org/licenses/>.

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 (
<View style={styles.container}>
<CigaretteBlock cigarettesPerDay={cigarettesPerDay} frequency={frequency} displayFrequency style={{ paddingHorizontal: 0 }} />
<View>
<CurrentLocation api={api!} currentLocation={currentLocation!} numberOfLines={2} />
</View>

<Text style={styles.urlText}>https://shootismoke.github.io/</Text>

</View>
);
}

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
}
});
17 changes: 17 additions & 0 deletions App/Screens/ShareScreen/ShareImage/index.ts
Original file line number Diff line number Diff line change
@@ -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 <http:https://www.gnu.org/licenses/>.

export * from './ShareImage';
91 changes: 91 additions & 0 deletions App/Screens/ShareScreen/ShareScreen.tsx
Original file line number Diff line number Diff line change
@@ -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 <http:https://www.gnu.org/licenses/>.

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 (
<View style={styles.container}>
<View style={styles.imageContainer}>
<View ref={refViewShot} collapsable={false}>
<ShareImage />
</View>
<View style={styles.buttonContainer}>
<Button onPress={handleDismiss} style={styles.button}>{i18n.t('close_button').toUpperCase()}</Button>
</View>
</View>
</View>
);
}

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 }
]
}
});
Loading

0 comments on commit 3250390

Please sign in to comment.