Chore: add translations to stats
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
import React from 'react'
|
||||
import { StyleSheet, View } from 'react-native'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import AppText from '../common/app-text'
|
||||
|
||||
import { Sizes, Spacing, Typography } from '../../styles'
|
||||
|
||||
const StatsOverview = ({ data }) => {
|
||||
return data.map((rowContent, i) => <Row key={i} rowContent={rowContent} />)
|
||||
}
|
||||
|
||||
StatsOverview.propTypes = {
|
||||
data: PropTypes.array.isRequired,
|
||||
}
|
||||
|
||||
const Row = ({ rowContent }) => {
|
||||
return (
|
||||
<View style={styles.row}>
|
||||
<Cell content={rowContent[0]} isLeft />
|
||||
<Cell content={rowContent[1]} />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
Row.propTypes = {
|
||||
rowContent: PropTypes.array.isRequired,
|
||||
}
|
||||
|
||||
const Cell = ({ content, isLeft }) => {
|
||||
const styleContainer = isLeft ? styles.cellLeft : styles.cellRight
|
||||
const styleText = isLeft ? styles.accentPurpleBig : styles.accentOrange
|
||||
const numberOfLines = isLeft ? 1 : 2
|
||||
const ellipsizeMode = isLeft ? 'clip' : 'tail'
|
||||
|
||||
return (
|
||||
<View style={styleContainer}>
|
||||
<AppText
|
||||
numberOfLines={numberOfLines}
|
||||
ellipsizeMode={ellipsizeMode}
|
||||
style={styleText}
|
||||
>
|
||||
{content}
|
||||
</AppText>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
Cell.propTypes = {
|
||||
content: PropTypes.node.isRequired,
|
||||
isLeft: PropTypes.bool,
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
accentOrange: {
|
||||
...Typography.accentOrange,
|
||||
fontSize: Sizes.small,
|
||||
margin: Sizes.tiny,
|
||||
},
|
||||
accentPurpleBig: {
|
||||
...Typography.accentPurpleBig,
|
||||
marginRight: Spacing.tiny,
|
||||
},
|
||||
cellLeft: {
|
||||
alignItems: 'flex-end',
|
||||
flex: 3,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
cellRight: { flex: 5 },
|
||||
row: { flexDirection: 'row' },
|
||||
})
|
||||
|
||||
export default StatsOverview
|
||||
@@ -0,0 +1,107 @@
|
||||
import React from 'react'
|
||||
import { FlatList, StyleSheet, View } from 'react-native'
|
||||
import PropTypes from 'prop-types'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import AppText from '../common/app-text'
|
||||
|
||||
import cycleModule from '../../lib/cycle'
|
||||
import { Spacing, Typography, Colors } from '../../styles'
|
||||
import { humanizeDate } from '../helpers/format-date'
|
||||
|
||||
const Item = ({ data }) => {
|
||||
const { t } = useTranslation(null, { keyPrefix: 'plurals' })
|
||||
|
||||
if (!data) return false
|
||||
|
||||
const { date, cycleLength, bleedingLength } = data
|
||||
|
||||
return (
|
||||
<View style={styles.row}>
|
||||
<View style={styles.accentCell}>
|
||||
<AppText>{humanizeDate(date)}</AppText>
|
||||
</View>
|
||||
<View style={styles.cell}>
|
||||
<AppText>{t('day', { count: cycleLength })}</AppText>
|
||||
</View>
|
||||
<View style={styles.cell}>
|
||||
<AppText>{t('day', { count: bleedingLength })}</AppText>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
Item.propTypes = {
|
||||
data: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
const StatsTable = () => {
|
||||
const renderItem = ({ item }) => <Item data={item} />
|
||||
const data = cycleModule().getStats()
|
||||
|
||||
if (!data || data.length === 0) return false
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
data={data}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={(item) => item.date}
|
||||
ItemSeparatorComponent={ItemDivider}
|
||||
ListHeaderComponent={FlatListHeader}
|
||||
ListHeaderComponentStyle={styles.headerDivider}
|
||||
stickyHeaderIndices={[0]}
|
||||
contentContainerStyle={styles.container}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const ItemDivider = () => <View style={styles.divider} />
|
||||
|
||||
const FlatListHeader = () => (
|
||||
<View style={styles.row}>
|
||||
<View style={styles.accentCell}>
|
||||
<AppText style={styles.header}>{'Cycle Start'}</AppText>
|
||||
</View>
|
||||
<View style={styles.cell}>
|
||||
<AppText style={styles.header}>{'Cycle Length'}</AppText>
|
||||
</View>
|
||||
<View style={styles.cell}>
|
||||
<AppText style={styles.header}>{'Bleeding'}</AppText>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
divider: {
|
||||
height: 1,
|
||||
width: '100%',
|
||||
backgroundColor: Colors.grey,
|
||||
},
|
||||
header: {
|
||||
...Typography.accentOrange,
|
||||
paddingVertical: Spacing.small,
|
||||
},
|
||||
headerDivider: {
|
||||
borderBottomColor: Colors.purple,
|
||||
borderBottomWidth: 2,
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
paddingVertical: Spacing.tiny,
|
||||
backgroundColor: Colors.turquoiseLight,
|
||||
},
|
||||
cell: {
|
||||
flex: 2,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
accentCell: {
|
||||
flex: 3,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
container: {
|
||||
paddingHorizontal: Spacing.base,
|
||||
},
|
||||
})
|
||||
|
||||
export default StatsTable
|
||||
@@ -0,0 +1,123 @@
|
||||
import React from 'react'
|
||||
import { ImageBackground, View } from 'react-native'
|
||||
import { ScaledSheet } from 'react-native-size-matters'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import AppText from '../common/app-text'
|
||||
import StatsOverview from './StatsOverview'
|
||||
import StatsTable from './StatsTable'
|
||||
|
||||
import cycleModule from '../../lib/cycle'
|
||||
import { getCycleLengthStats as getCycleInfo } from '../../lib/cycle-length'
|
||||
|
||||
import { Containers, Sizes, Spacing, Typography } from '../../styles'
|
||||
|
||||
const image = require('../../assets/cycle-icon.png')
|
||||
|
||||
const Stats = () => {
|
||||
const { t } = useTranslation(null, { keyPrefix: 'stats' })
|
||||
|
||||
const cycleLengths = cycleModule().getAllCycleLengths()
|
||||
const numberOfCycles = cycleLengths.length
|
||||
const cycleData =
|
||||
numberOfCycles > 0
|
||||
? getCycleInfo(cycleLengths)
|
||||
: { minimum: '—', maximum: '—', stdDeviation: '—' }
|
||||
const standardDeviation = cycleData.stdDeviation
|
||||
? cycleData.stdDeviation
|
||||
: '—'
|
||||
const statsData = [
|
||||
[cycleData.minimum, t('overview.min')],
|
||||
[cycleData.maximum, t('overview.max')],
|
||||
[standardDeviation, t('overview.standardDeviation')],
|
||||
[numberOfCycles, t('overview.completedCycles')],
|
||||
]
|
||||
|
||||
return (
|
||||
<View style={styles.pageContainer}>
|
||||
<View style={styles.overviewContainer}>
|
||||
<AppText>{t('intro')}</AppText>
|
||||
{numberOfCycles === 0 ? (
|
||||
<AppText>{t('noData')}</AppText>
|
||||
) : (
|
||||
<View style={styles.container}>
|
||||
<View style={styles.columnLeft}>
|
||||
<ImageBackground
|
||||
source={image}
|
||||
imageStyle={styles.image}
|
||||
style={styles.imageContainter}
|
||||
>
|
||||
<AppText
|
||||
numberOfLines={1}
|
||||
ellipsizeMode="clip"
|
||||
style={styles.accentPurpleGiant}
|
||||
>
|
||||
{cycleData.mean}
|
||||
</AppText>
|
||||
<AppText style={styles.accentPurpleHuge}>
|
||||
{t('overview.days')}
|
||||
</AppText>
|
||||
</ImageBackground>
|
||||
<AppText style={styles.accentOrange}>
|
||||
{t('overview.average')}
|
||||
</AppText>
|
||||
</View>
|
||||
<View style={styles.columnRight}>
|
||||
<StatsOverview data={statsData} />
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
<StatsTable />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const column = {
|
||||
flexDirection: 'column',
|
||||
}
|
||||
|
||||
const styles = ScaledSheet.create({
|
||||
accentOrange: {
|
||||
...Typography.accentOrange,
|
||||
fontSize: Sizes.small,
|
||||
},
|
||||
accentPurpleGiant: {
|
||||
...Typography.accentPurpleGiant,
|
||||
marginTop: Spacing.base * -2,
|
||||
},
|
||||
accentPurpleHuge: {
|
||||
...Typography.accentPurpleHuge,
|
||||
marginTop: Spacing.base * -1,
|
||||
},
|
||||
container: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
columnLeft: {
|
||||
...column,
|
||||
flex: 3,
|
||||
},
|
||||
columnRight: {
|
||||
...column,
|
||||
flex: 5,
|
||||
paddingTop: Spacing.small,
|
||||
},
|
||||
image: {
|
||||
resizeMode: 'contain',
|
||||
},
|
||||
imageContainter: {
|
||||
paddingTop: Spacing.large * 2.5,
|
||||
marginBottom: Spacing.large,
|
||||
},
|
||||
overviewContainer: {
|
||||
paddingHorizontal: Spacing.base,
|
||||
paddingTop: Spacing.base,
|
||||
},
|
||||
pageContainer: {
|
||||
...Containers.pageContainer,
|
||||
},
|
||||
})
|
||||
|
||||
export default Stats
|
||||
Reference in New Issue
Block a user