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

chore: Analytics notice and settings component #2568

Merged
merged 13 commits into from
Aug 2, 2023
1 change: 1 addition & 0 deletions apps/wallet-mobile/.storybook/storybook.requires.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const getStories = () => {
"./src/Catalyst/VotingBanner.stories.tsx": require("../src/Catalyst/VotingBanner.stories.tsx"),
"./src/Catalyst/VotingRegistration.stories.tsx": require("../src/Catalyst/VotingRegistration.stories.tsx"),
"./src/components/AmountItem/AmountItem.stories.tsx": require("../src/components/AmountItem/AmountItem.stories.tsx"),
"./src/components/Analytics/Analytics.stories.tsx": require("../src/components/Analytics/Analytics.stories.tsx"),
"./src/components/Boundary/Boundary.stories.tsx": require("../src/components/Boundary/Boundary.stories.tsx"),
"./src/components/Button/Button.stories.tsx": require("../src/components/Button/Button.stories.tsx"),
"./src/components/Checkbox/Checkbox.stories.tsx": require("../src/components/Checkbox/Checkbox.stories.tsx"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {defineMessages, useIntl} from 'react-intl'
import {FlatList, InteractionManager, Linking, RefreshControl, StyleSheet, Text, TouchableOpacity} from 'react-native'
import {SafeAreaView} from 'react-native-safe-area-context'

import {AnalyticsNotice} from '../../components/Analytics/AnalyticsNotice'
import {Button} from '../../components/Button'
import {Icon} from '../../components/Icon'
import {PleaseWaitModal} from '../../components/PleaseWaitModal'
Expand Down Expand Up @@ -92,6 +93,8 @@ export const WalletSelectionScreen = () => {
<OnlyDevButton />

<PleaseWaitModal title={strings.loadingWallet} spinnerText={strings.pleaseWait} visible={isLoading} />

<AnalyticsNotice invisible={isLoading} />
</SafeAreaView>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {storiesOf} from '@storybook/react-native'
import React from 'react'

import {Analytics} from './Analytics'

storiesOf('Analytics', module)
.add('Notice', () => <Analytics type="notice" />)
.add('Settings', () => <Analytics type="settings" />)
238 changes: 238 additions & 0 deletions apps/wallet-mobile/src/components/Analytics/Analytics.tsx
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we have the both stories? notice and settings, then I think we are g2g

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The component has the stories for both. Not their containers though.

Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
import React from 'react'
import {defineMessages, useIntl} from 'react-intl'
import {Linking, ScrollView, StyleSheet, Switch, TouchableOpacity, View} from 'react-native'

import {Button, Spacer, Text} from '../../components'
import {useMetrics} from '../../metrics/metricsManager'
import {COLORS} from '../../theme'
import {AnalyticsImage} from './AnalyticsImage'

type Props = {
type: 'notice' | 'settings'
onClose?: () => void
}

export const Analytics = ({type, onClose}: Props) => {
const strings = useStrings()
const metrics = useMetrics()

return (
<ScrollView style={styles.scrollView}>
<View style={styles.centered}>
{type === 'notice' && (
<>
<Text style={styles.title}>{strings.header}</Text>

<Spacer height={12} />
</>
)}

<AnalyticsImage />
</View>

<Spacer height={12} />

<View style={styles.centered}>
{type === 'settings' && (
<>
<Text style={styles.paragraph} bold>
{strings.header}
</Text>

<Spacer height={12} />
</>
)}

<Text style={styles.paragraph} bold={type === 'notice'}>
{strings.description}
</Text>

<Spacer height={12} />
</View>

<Spacer height={12} />

<View>
{list.map(({style, icon, key}) => (
<View key={key} style={styles.item}>
<Text style={style}>{icon}</Text>

<Text style={styles.text}>{strings[key]}</Text>
</View>
))}
</View>

<Spacer height={12} />

<TouchableOpacity onPress={() => Linking.openURL('')}>
<Text style={styles.link}>{strings.more}</Text>
</TouchableOpacity>

<Spacer height={12} />

{type === 'notice' && (
<>
<Spacer height={12} />

<View style={styles.buttons}>
<Button
block
outlineShelley
onPress={() => {
metrics.disable()
onClose?.()
}}
title={strings.skip}
style={styles.skip}
/>

<Button
block
shelleyTheme
onPress={() => {
metrics.enable()
onClose?.()
}}
title={strings.accept}
/>
</View>
</>
)}

{type === 'settings' && (
<View style={styles.toggle}>
<Text bold>{strings.toggle}</Text>

<Switch
value={metrics.isEnabled}
onValueChange={() => (metrics.isEnabled ? metrics.disable() : metrics.enable())}
/>
</View>
)}
</ScrollView>
)
}

const styles = StyleSheet.create({
scrollView: {
paddingHorizontal: 10,
},
text: {
fontSize: 14,
lineHeight: 22,
},
item: {
display: 'flex',
flexDirection: 'row',
alignItems: 'baseline',
},
paragraph: {
fontSize: 14,
lineHeight: 22,
textAlign: 'center',
},
link: {
color: COLORS.BLUE_LIGHTER,
textAlign: 'center',
},
centered: {
alignItems: 'center',
justifyContent: 'center',
},
title: {
fontSize: 20,
lineHeight: 22,
fontWeight: 'bold',
textAlign: 'center',
},
buttons: {
flexDirection: 'column',
gap: 8,
},
skip: {
borderWidth: 0,
},
tick: {
color: COLORS.DARK_BLUE,
paddingRight: 8,
},
cross: {
color: COLORS.RED,
paddingRight: 8,
},
toggle: {display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'},
})

const list = [
{style: styles.tick, icon: '✓', key: 'anonymous'},
{style: styles.tick, icon: '✓', key: 'optout'},
{style: styles.cross, icon: '✕', key: 'private'},
{style: styles.cross, icon: '✕', key: 'noip'},
{style: styles.cross, icon: '✕', key: 'nosell'},
] as const

const bold = {b: (text) => <Text bold>{text}</Text>}

const useStrings = () => {
const intl = useIntl()
return {
header: intl.formatMessage(messages.header),
description: intl.formatMessage(messages.description),
anonymous: intl.formatMessage(messages.anonymous),
optout: intl.formatMessage(messages.optout),
private: intl.formatMessage(messages.private, bold),
noip: intl.formatMessage(messages.noip, bold),
nosell: intl.formatMessage(messages.nosell, bold),
more: intl.formatMessage(messages.more),
skip: intl.formatMessage(messages.skip),
accept: intl.formatMessage(messages.accept),
toggle: intl.formatMessage(messages.toggle),
}
}

const messages = defineMessages({
header: {
id: 'analytics.header',
defaultMessage: '!!!Join the journey to improve Yoroi',
},
description: {
id: 'analytics.description',
defaultMessage: '!!!Share user insights to help us fine tune Yoroi to better serve your needs.',
},
anonymous: {
id: 'analytics.anonymous',
defaultMessage: '!!!Anonymous analytics data',
},
optout: {
id: 'analytics.optout',
defaultMessage: '!!!You can always opt-out via Settings',
},
private: {
id: 'analytics.private',
defaultMessage: '!!!We <b>cannot</b> access private keys',
},
noip: {
id: 'analytics.noip',
defaultMessage: '!!!We <b>are not</b> recording IP addresses',
},
nosell: {
id: 'analytics.nosell',
defaultMessage: '!!!We <b>do not</b> sell data',
},
more: {
id: 'analytics.more',
defaultMessage: '!!!Learn more about user insights',
},
skip: {
id: 'analytics.skip',
defaultMessage: '!!!Skip',
},
accept: {
id: 'analytics.accept',
defaultMessage: '!!!Accept',
},
toggle: {
id: 'analytics.toggle',
defaultMessage: '!!!Allow Yoroi analytics',
},
})
Loading