diff --git a/components/chart/chart-legend.js b/components/chart/chart-legend.js index df6f8a60f77f36e6fb648ed2369cad9af5d0ebfa..4dc00b629f8051e40b55893c1cba9c9426b0ed4d 100644 --- a/components/chart/chart-legend.js +++ b/components/chart/chart-legend.js @@ -1,4 +1,5 @@ import React from 'react' +import PropTypes from 'prop-types' import { View } from 'react-native' import AppText from '../app-text' @@ -9,9 +10,9 @@ import { cycleDayColor } from '../../styles' import { shared as labels } from '../../i18n/en/labels' -const ChartLegend = () => { +const ChartLegend = ({ xAxisHeight }) => { return ( - <View style={[styles.yAxis, styles.chartLegend]}> + <View style={[styles.yAxis, styles.chartLegend, {height: xAxisHeight}]}> <DripHomeIcon name="circle" size={styles.yAxis.width - 7} @@ -24,4 +25,8 @@ const ChartLegend = () => { ) } +ChartLegend.propTypes = { + xAxisHeight: PropTypes.number.isRequired +} + export default ChartLegend diff --git a/components/chart/chart.js b/components/chart/chart.js index d11131ad9236f196c0d1012b359f76864cfcf7f9..ed0feba12442d87c263d4d3f031eee2665dccdcc 100644 --- a/components/chart/chart.js +++ b/components/chart/chart.js @@ -2,11 +2,13 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import { View, FlatList, ActivityIndicator } from 'react-native' +import NoData from './no-data' import AppLoadingView from '../app-loading' import YAxis from './y-axis' import nfpLines from './nfp-lines' import DayColumn from './day-column' import HorizontalGrid from './horizontal-grid' +import AppText from '../app-text' import { getCycleDaysSortedByDate } from '../../db' import nothingChanged from '../../db/db-unchanged' @@ -14,7 +16,7 @@ import { scaleObservable } from '../../local-storage' import { makeColumnInfo } from '../helpers/chart' import config from '../../config' - +import { shared } from '../../i18n/en/labels' import styles from './styles' export default class CycleChart extends Component { @@ -25,9 +27,26 @@ export default class CycleChart extends Component { constructor(props) { super(props) + this.state = {} this.cycleDaysSortedByDate = getCycleDaysSortedByDate() this.getFhmAndLtlInfo = nfpLines() + this.shouldShowTemperatureColumn = false + + this.prepareSymptomData() + } + + prepareSymptomData = () => { + this.symptomRowSymptoms = config.symptoms.filter((symptomName) => { + return this.cycleDaysSortedByDate.some(cycleDay => { + return cycleDay[symptomName] + }) + }) + this.chartSymptoms = [...this.symptomRowSymptoms] + if (this.cycleDaysSortedByDate.some(day => day.temperature)) { + this.chartSymptoms.push('temperature') + this.shouldShowTemperatureColumn = true + } } renderColumn = ({ item, index }) => { @@ -40,52 +59,40 @@ export default class CycleChart extends Component { columnHeight={this.columnHeight} symptomRowSymptoms={this.symptomRowSymptoms} chartSymptoms={this.chartSymptoms} + shouldShowTemperatureColumn={this.shouldShowTemperatureColumn} getFhmAndLtlInfo={this.getFhmAndLtlInfo} + xAxisHeight={this.xAxisHeight} /> ) } + reCalculateChartInfo = (nativeEvent) => { + const { height, width } = nativeEvent.layout + const xAxisCoefficient = this.shouldShowTemperatureColumn ? + config.xAxisHeightPercentage : config.xAxisHeightPercentageLarge + const symptomCoefficient = this.shouldShowTemperatureColumn ? + config.symptomHeightPercentage : config.symptomHeightPercentageLarge + + this.xAxisHeight = height * xAxisCoefficient + const remainingHeight = height - this.xAxisHeight + this.symptomHeight = remainingHeight * symptomCoefficient + this.symptomRowHeight = this.symptomRowSymptoms.length * + this.symptomHeight + this.columnHeight = remainingHeight - this.symptomRowHeight + const chartHeight = this.shouldShowTemperatureColumn ? + height : (this.symptomRowHeight + this.xAxisHeight) + + const numberOfColumnsToRender = Math.round(width / config.columnWidth) + const columns = makeColumnInfo() + + this.setState({ columns, chartHeight, numberOfColumnsToRender }) + } + onLayout = ({ nativeEvent }) => { if (this.state.chartHeight) return - const height = nativeEvent.layout.height - const reCalculateChartInfo = () => { - // how many symptoms need to be displayed on the chart's upper symptom row? - this.symptomRowSymptoms = [ - 'bleeding', - 'mucus', - 'cervix', - 'sex', - 'desire', - 'pain', - 'mood', - 'note' - ].filter((symptomName) => { - return this.cycleDaysSortedByDate.some(cycleDay => { - return cycleDay[symptomName] - }) - }) - this.xAxisHeight = height * config.xAxisHeightPercentage - const remainingHeight = height - this.xAxisHeight - this.symptomHeight = config.symptomHeightPercentage * remainingHeight - this.symptomRowHeight = this.symptomRowSymptoms.length * - this.symptomHeight - this.columnHeight = remainingHeight - this.symptomRowHeight - - this.chartSymptoms = [...this.symptomRowSymptoms] - if (this.cycleDaysSortedByDate.some(day => day.temperature)) { - this.chartSymptoms.push('temperature') - } - - const columnData = makeColumnInfo() - this.setState({ - columns: columnData, - chartHeight: height - }) - } - - reCalculateChartInfo() - this.updateListeners(reCalculateChartInfo) + this.reCalculateChartInfo(nativeEvent) + this.updateListeners(this.reCalculateChartInfo) } updateListeners(dataUpdateHandler) { @@ -110,44 +117,60 @@ export default class CycleChart extends Component { } render() { - const { chartHeight, chartLoaded } = this.state + const { chartHeight, chartLoaded, numberOfColumnsToRender } = this.state + const shouldShowChart = this.chartSymptoms.length > 0 ? true : false + return ( - <View - onLayout={this.onLayout} - style={styles.container} - > - {!chartLoaded && <AppLoadingView />} - - {chartHeight && chartLoaded && ( - <React.Fragment> - <YAxis - height={this.columnHeight} - symptomsToDisplay={this.symptomRowSymptoms} - symptomsSectionHeight={this.symptomRowHeight} - /> - <HorizontalGrid - height={this.columnHeight} - startPosition={this.symptomRowHeight} - /> - </React.Fragment> - )} - - {chartHeight && - <FlatList - horizontal={true} - inverted={true} - showsHorizontalScrollIndicator={false} - data={this.state.columns} - renderItem={this.renderColumn} - keyExtractor={item => item} - initialNumToRender={15} - windowSize={30} - onLayout={() => this.setState({chartLoaded: true})} - onEndReached={() => this.setState({end: true})} - ListFooterComponent={<LoadingMoreView end={this.state.end}/>} - updateCellsBatchingPeriod={800} - /> - } + <View onLayout={this.onLayout} style={styles.container}> + {!shouldShowChart && <NoData navigate={this.props.navigate}/>} + {shouldShowChart && !chartHeight && !chartLoaded && <AppLoadingView />} + <View style={styles.chartContainer}> + {shouldShowChart && ( + <View style={styles.chartArea}> + + {chartHeight && chartLoaded && ( + <React.Fragment> + <YAxis + height={this.columnHeight} + symptomsToDisplay={this.symptomRowSymptoms} + symptomsSectionHeight={this.symptomRowHeight} + shouldShowTemperatureColumn= + {this.shouldShowTemperatureColumn} + xAxisHeight={this.xAxisHeight} + /> + {this.shouldShowTemperatureColumn && (<HorizontalGrid + height={this.columnHeight} + startPosition={this.symptomRowHeight} + />)} + </React.Fragment> + )} + + {chartHeight && + <FlatList + horizontal={true} + inverted={true} + showsHorizontalScrollIndicator={false} + data={this.state.columns} + renderItem={this.renderColumn} + keyExtractor={item => item} + initialNumToRender={numberOfColumnsToRender} + windowSize={30} + onLayout={() => this.setState({chartLoaded: true})} + onEndReached={() => this.setState({end: true})} + ListFooterComponent={<LoadingMoreView end={this.state.end}/>} + updateCellsBatchingPeriod={800} + contentContainerStyle={{height: chartHeight}} + /> + } + </View> + )} + </View> + {shouldShowChart && chartLoaded && !this.shouldShowTemperatureColumn + && ( + <View style={styles.centerItem}> + <AppText style={{textAlign: 'center'}}>{shared.noTemperatureWarning}</AppText> + </View> + )} </View> ) } diff --git a/components/chart/cycle-day-label.js b/components/chart/cycle-day-label.js index 38a0d2e50b24620c34446ebd1205e9a1ee4309c6..bb794f4f9e3e36c7cef01b74b50a70594300c979 100644 --- a/components/chart/cycle-day-label.js +++ b/components/chart/cycle-day-label.js @@ -20,7 +20,7 @@ const CycleDayLabel = ({ height, date }) => { const boldDateLabel = isFirstDayOfMonth ? {fontWeight: 'bold'} : {} return ( - <View style={{ height }}> + <View style={[styles.chartLegend, { height }]}> <Text style={label.number}> {cycleDayNumber ? cycleDayNumber : ' '} </Text> diff --git a/components/chart/day-column.js b/components/chart/day-column.js index 687b993818e08ab37e9f90a5261282293b2f883a..efefe2ed4fc9f0e22f0316f0dfcfcdb223e1aea4 100644 --- a/components/chart/day-column.js +++ b/components/chart/day-column.js @@ -26,9 +26,10 @@ class DayColumn extends Component { getFhmAndLtlInfo: PropTypes.func.isRequired, navigate: PropTypes.func.isRequired, setDate: PropTypes.func.isRequired, + shouldShowTemperatureColumn: PropTypes.bool, symptomHeight: PropTypes.number.isRequired, symptomRowSymptoms: PropTypes.array, - xAxisHeight: PropTypes.number + xAxisHeight: PropTypes.number, } constructor(props) { @@ -77,10 +78,13 @@ class DayColumn extends Component { } render() { - const { dateString, + const { columnHeight, + dateString, + shouldShowTemperatureColumn, + symptomHeight, symptomRowSymptoms, - columnHeight, - xAxisHeight } = this.props + xAxisHeight + } = this.props return ( <TouchableOpacity @@ -98,17 +102,17 @@ class DayColumn extends Component { isSymptomDataComplete={ hasSymptomData && isSymptomDataComplete(symptom, dateString) } - height={this.props.symptomHeight} + height={symptomHeight} />) } )} - <TemperatureColumn + {shouldShowTemperatureColumn && <TemperatureColumn horizontalLinePosition={this.fhmAndLtl.drawLtlAt} isVerticalLine={this.fhmAndLtl.drawFhmLine} data={this.data && this.data.temperature} columnHeight={columnHeight} - /> + />} <CycleDayLabel height={xAxisHeight} diff --git a/components/chart/no-data.js b/components/chart/no-data.js new file mode 100644 index 0000000000000000000000000000000000000000..efb1ce3c1b87057482d8b8ad25688ee5d34d7d08 --- /dev/null +++ b/components/chart/no-data.js @@ -0,0 +1,31 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { View } from 'react-native' + +import AppText from '../app-text' +import SettingsButton from '../settings/shared/settings-button' + +import { shared } from '../../i18n/en/labels' +import styles from './styles' + +const NoData = ({ navigate }) => { + return ( + <View flex={1}> + <View style={styles.centerItem}> + <AppText>{shared.noDataWarning}</AppText> + <SettingsButton + onPress={() => {navigate('CycleDay')}} + style={{marginHorizontal: 40}} + > + {shared.noDataButtonText} + </SettingsButton> + </View> + </View> + ) +} + +NoData.propTypes = { + navigate: PropTypes.func, +} + +export default NoData \ No newline at end of file diff --git a/components/chart/styles.js b/components/chart/styles.js index 450b408192401fb47e282321f5a666550336aa73..fba4df1806d82c27328acabb4ec9ce4344506942 100644 --- a/components/chart/styles.js +++ b/components/chart/styles.js @@ -25,9 +25,14 @@ const orangeColor = '#bc6642' const mintColor = '#6ca299' const styles = { - container: { - flexDirection: 'row', - flex: 1, + container: { flex: 1 }, + chartContainer: { flexDirection: 'column' }, + chartArea: { flexDirection: 'row' }, + centerItem: { + flex:1, + alignItems: 'center', + justifyContent: 'center', + marginHorizontal: 25, }, curve: { stroke: colorTemperature, @@ -137,7 +142,7 @@ const styles = { }, chartLegend: { alignItems: 'center', - justifyContent: 'center', + justifyContent: 'flex-end', }, boldTick: { fontWeight: 'bold', diff --git a/components/chart/symptom-cell.js b/components/chart/symptom-cell.js index fa760422235383ed9a1ed753d549f5482f470a36..4b1124c181b0d9a523ec59d217302dbe7216cc41 100644 --- a/components/chart/symptom-cell.js +++ b/components/chart/symptom-cell.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types' import { View } from 'react-native' import styles from './styles' +import config from '../../config' const SymptomCell = ({ height, @@ -12,7 +13,7 @@ const SymptomCell = ({ }) => { const shouldDrawDot = symptomValue !== false - const styleParent = [styles.symptomRow, { height }] + const styleParent = [styles.symptomRow, { height, width: config.columnWidth }] let styleChild if (shouldDrawDot) { diff --git a/components/chart/y-axis.js b/components/chart/y-axis.js index acd3ccf75e8b3594cf096997ae54cc918bd69742..db27e1008966a14290b4f26f4b27926a62b3f708 100644 --- a/components/chart/y-axis.js +++ b/components/chart/y-axis.js @@ -8,8 +8,15 @@ import ChartLegend from './chart-legend' import styles from './styles' -const YAxis = ({ height, symptomsToDisplay, symptomsSectionHeight }) => { +const YAxis = ({ + height, + symptomsToDisplay, + symptomsSectionHeight, + shouldShowTemperatureColumn, + xAxisHeight +}) => { const symptomIconHeight = symptomsSectionHeight / symptomsToDisplay.length + return ( <View> <View style={[styles.yAxis, {height: symptomsSectionHeight}]}> @@ -22,8 +29,8 @@ const YAxis = ({ height, symptomsToDisplay, symptomsSectionHeight }) => { ) )} </View> - <TickList height={height} /> - <ChartLegend /> + {shouldShowTemperatureColumn && <TickList height={height} />} + <ChartLegend xAxisHeight={xAxisHeight} /> </View> ) } @@ -32,6 +39,8 @@ YAxis.propTypes = { height: PropTypes.number, symptomsToDisplay: PropTypes.array, symptomsSectionHeight: PropTypes.number, + shouldShowTemperatureColumn: PropTypes.bool, + xAxisHeight: PropTypes.number.isRequired } export default YAxis diff --git a/config.js b/config.js index d0c471ea618572d76c991926af5bc7b465267688..0fd19c583fd0bdcee6529e02428fc5cfcb0b906e 100644 --- a/config.js +++ b/config.js @@ -1,7 +1,9 @@ const config = { columnWidth: 25, xAxisHeightPercentage: 0.08, + xAxisHeightPercentageLarge: 0.12, symptomHeightPercentage: 0.05, + symptomHeightPercentageLarge: 0.1, temperatureScale: { defaultLow: 35, defaultHigh: 38, @@ -9,7 +11,17 @@ const config = { max: 40, units: 0.1, verticalPadding: 0.03 - } + }, + symptoms: [ + 'bleeding', + 'mucus', + 'cervix', + 'sex', + 'desire', + 'pain', + 'mood', + 'note' + ], } config.columnMiddle = config.columnWidth / 2 diff --git a/i18n/en/labels.js b/i18n/en/labels.js index ad20e172cb91a035204c7c9c368472e4542b8cc1..80478131d632c30096752354cb5ea5e0737245a7 100644 --- a/i18n/en/labels.js +++ b/i18n/en/labels.js @@ -15,7 +15,10 @@ export const shared = { date: 'Date', cycleDayWithLinebreak: 'Cycle\nday', loading: 'Loading ...', - enter: 'Enter' + noDataWarning: 'You haven\'t entered any data yet.', + noTemperatureWarning: 'You haven\'t entered any temperature data yet.', + noDataButtonText: 'Start entering data now', + enter: 'Enter', } export const headerTitles = {