diff --git a/components/chart/chart.js b/components/chart/chart.js index 8ea7f31..b9e995e 100644 --- a/components/chart/chart.js +++ b/components/chart/chart.js @@ -3,10 +3,10 @@ import { View, FlatList } from 'react-native' import range from 'date-range' import { LocalDate } from 'js-joda' import Svg, { G } from 'react-native-svg' -import { makeYAxisLabels, normalizeToScale, makeHorizontalGrid } from './y-axis' +import { makeYAxisLabels, makeHorizontalGrid } from './y-axis' import nfpLines from './nfp-lines' import DayColumn from './day-column' -import { getCycleDay, getCycleDaysSortedByDate, getAmountOfCycleDays } from '../../db' +import { getCycleDaysSortedByDate, getAmountOfCycleDays } from '../../db' import styles from './styles' import { scaleObservable } from '../../local-storage' import config from '../../config' @@ -24,20 +24,24 @@ export default class CycleChart extends Component { constructor(props) { super(props) this.state = {} - this.renderColumn = ({item, index}) => { - return ( - - ) - } this.cycleDaysSortedByDate = getCycleDaysSortedByDate() + this.getFhmAndLtlInfo = nfpLines() + } + + renderColumn = ({ item, index }) => { + return ( + + ) } onLayout = ({ nativeEvent }) => { @@ -67,12 +71,12 @@ export default class CycleChart extends Component { this.symptomHeight this.columnHeight = remainingHeight - this.symptomRowHeight - const chartSymptoms = [...this.symptomRowSymptoms] + this.chartSymptoms = [...this.symptomRowSymptoms] if (this.cycleDaysSortedByDate.some(day => day.temperature)) { - chartSymptoms.push('temperature') + this.chartSymptoms.push('temperature') } - const columnData = this.makeColumnInfo(nfpLines(), chartSymptoms) + const columnData = this.makeColumnInfo() this.setState({ columns: columnData }) } @@ -85,7 +89,7 @@ export default class CycleChart extends Component { this.removeObvListener() } - makeColumnInfo(getFhmAndLtlInfo, chartSymptoms) { + makeColumnInfo() { let amountOfCycleDays = getAmountOfCycleDays() // if there's not much data yet, we want to show at least 30 days on the chart if (amountOfCycleDays < 30) { @@ -95,59 +99,13 @@ export default class CycleChart extends Component { amountOfCycleDays += 5 } const jsDates = getTodayAndPreviousDays(amountOfCycleDays) - const xAxisDates = jsDates.map(jsDate => { + return jsDates.map(jsDate => { return LocalDate.of( jsDate.getFullYear(), jsDate.getMonth() + 1, jsDate.getDate() ).toString() }) - - const columns = xAxisDates.map(dateString => { - const column = { dateString } - const cycleDay = getCycleDay(dateString) - let symptoms = {} - - if (cycleDay) { - symptoms = chartSymptoms.reduce((acc, symptom) => { - if (symptom === 'bleeding' || - symptom === 'temperature' || - symptom === 'mucus' || - symptom === 'desire' || - symptom === 'note' - ) { - acc[symptom] = cycleDay[symptom] && cycleDay[symptom].value - } else if (symptom === 'cervix') { - acc.cervix = cycleDay.cervix && - (cycleDay.cervix.opening + cycleDay.cervix.firmness) - } else if (symptom === 'sex') { - // solo = 1 + partner = 2 - acc.sex = cycleDay.sex && - (cycleDay.sex.solo + 2 * cycleDay.sex.partner) - } else if (symptom === 'pain') { - // is any pain documented? - acc.pain = cycleDay.pain && - Object.values(cycleDay.pain).some(x => x === true) - } - acc[`${symptom}Exclude`] = cycleDay[symptom] && cycleDay[symptom].exclude - return acc - }, {}) - - } - - const temp = symptoms.temperature - if (temp) { - column.y = normalizeToScale(temp, this.columnHeight) - } - - const fhmAndLtl = getFhmAndLtlInfo(dateString, temp, this.columnHeight) - return Object.assign(column, symptoms, fhmAndLtl) - }) - - return columns.map((col, i) => { - const info = getInfoForNeighborColumns(i, columns) - return Object.assign(col, info) - }) } render() { @@ -217,9 +175,10 @@ export default class CycleChart extends Component { showsHorizontalScrollIndicator={false} data={this.state.columns} renderItem={this.renderColumn} - keyExtractor={item => item.dateString} + keyExtractor={item => item} initialNumToRender={15} maxToRenderPerBatch={5} + windowSize={30} onLayout={() => this.setState({chartLoaded: true})} /> } @@ -228,7 +187,6 @@ export default class CycleChart extends Component { } } - function getTodayAndPreviousDays(n) { const today = new Date() today.setHours(0) @@ -240,26 +198,6 @@ function getTodayAndPreviousDays(n) { return range(earlierDate, today).reverse() } -function getInfoForNeighborColumns(index, cols) { - const ret = { - rightY: null, - rightTemperatureExclude: null, - leftY: null, - leftTemperatureExclude: null - } - const right = index > 0 ? cols[index - 1] : undefined - const left = index < cols.length - 1 ? cols[index + 1] : undefined - if (right && right.y) { - ret.rightY = right.y - ret.rightTemperatureExclude = right.temperatureExclude - } - if (left && left.y) { - ret.leftY = left.y - ret.leftTemperatureExclude = left.temperatureExclude - } - return ret -} - const symptomIcons = { 'bleeding': { 'viewBox': '10 10 320 400', diff --git a/components/chart/day-column.js b/components/chart/day-column.js index ec3dd95..e5abc0a 100644 --- a/components/chart/day-column.js +++ b/components/chart/day-column.js @@ -9,15 +9,55 @@ import styles from './styles' import config from '../../config' import { getOrCreateCycleDay } from '../../db' import cycleModule from '../../lib/cycle' +import { getCycleDay } from '../../db' import DotAndLine from './dot-and-line' +import { normalizeToScale } from './y-axis' const label = styles.column.label export default class DayColumn extends Component { - constructor() { + constructor(props) { super() + const dateString = props.dateString + const columnHeight = props.columnHeight this.getCycleDayNumber = cycleModule().getCycleDayNumber + const cycleDay = getCycleDay(dateString) + this.data = {} + if (cycleDay) { + this.data = props.chartSymptoms.reduce((acc, symptom) => { + if (['bleeding', 'temperature', 'mucus', 'desire', 'note'].includes(symptom)) { + acc[symptom] = cycleDay[symptom] && cycleDay[symptom].value + if (symptom === 'temperature' && acc.temperature) { + acc.y = normalizeToScale(acc.temperature, columnHeight) + const neighbor = getInfoForNeighborColumns(dateString, columnHeight) + for (const key in neighbor) { + acc[key] = neighbor[key] + } + } + } else if (symptom === 'cervix') { + acc.cervix = cycleDay.cervix && + (cycleDay.cervix.opening + cycleDay.cervix.firmness) + } else if (symptom === 'sex') { + // solo = 1 + partner = 2 + acc.sex = cycleDay.sex && + (cycleDay.sex.solo + 2 * cycleDay.sex.partner) + } else if (symptom === 'pain') { + // is any pain documented? + acc.pain = cycleDay.pain && + Object.values(cycleDay.pain).some(x => x === true) + } + acc[`${symptom}Exclude`] = cycleDay[symptom] && cycleDay[symptom].exclude + return acc + }, this.data) + } + + this.fhmAndLtl = props.getFhmAndLtlInfo( + props.dateString, + props.temp, + props.columnHeight + ) } + passDateToDayView(dateString) { const cycleDay = getOrCreateCycleDay(dateString) this.props.navigate('CycleDay', { cycleDay }) @@ -28,45 +68,29 @@ export default class DayColumn extends Component { } render() { - const { - dateString, - y, - temperatureExclude, - drawFhmLine, - drawLtlAt, - rightY, - rightTemperatureExclude, - leftY, - leftTemperatureExclude, - columnHeight, - symptomHeight, - chartHeight, - symptomRowSymptoms, - xAxisHeight - } = this.props - - const columnElements = [] + const dateString = this.props.dateString + const symptomHeight = this.props.symptomHeight - if(drawLtlAt) { + if(this.fhmAndLtl.drawLtlAt) { const ltlLine = () columnElements.push(ltlLine) } - if (drawFhmLine) { + if (this.fhmAndLtl.drawFhmLine) { const x = styles.nfpLine.strokeWidth / 2 const fhmLine = () @@ -74,15 +98,15 @@ export default class DayColumn extends Component { } - if (y) { + if (this.data.y) { columnElements.push( ) @@ -108,7 +132,7 @@ export default class DayColumn extends Component { const column = ( { columnElements } @@ -118,68 +142,71 @@ export default class DayColumn extends Component { const symptomIconViews = { bleeding: ( ), mucus: ( ), cervix: ( 0 ? styles.iconShades.cervix[2] : styles.iconShades.cervix[0]} + backgroundColor={this.data.cervix > 0 ? + styles.iconShades.cervix[2] : + styles.iconShades.cervix[0] + } /> ), sex: ( ), desire: ( ), pain: ( @@ -191,7 +218,7 @@ export default class DayColumn extends Component { ), note: ( @@ -209,14 +236,16 @@ export default class DayColumn extends Component { activeOpacity={1} > - {symptomRowSymptoms.map(symptomName => symptomIconViews[symptomName])} + {this.props.symptomRowSymptoms.map(symptomName => { + return symptomIconViews[symptomName] + })} - + {column} - + {cycleDayLabel} {dateLabel} @@ -237,3 +266,26 @@ function SymptomIconView(props) { ) } +function getInfoForNeighborColumns(dateString, columnHeight) { + const ret = { + rightY: null, + rightTemperatureExclude: null, + leftY: null, + leftTemperatureExclude: null + } + const target = LocalDate.parse(dateString) + const dayBefore = target.minusDays(1).toString() + const dayAfter = target.plusDays(1).toString() + const cycleDayBefore = getCycleDay(dayBefore) + const cycleDayAfter = getCycleDay(dayAfter) + if (cycleDayAfter && cycleDayAfter.temperature) { + ret.rightY = normalizeToScale(cycleDayAfter.temperature.value, columnHeight) + ret.rightTemperatureExclude = cycleDayAfter.temperature.exclude + } + if (cycleDayBefore && cycleDayBefore.temperature) { + ret.leftY = normalizeToScale(cycleDayBefore.temperature.value, columnHeight) + ret.leftTemperatureExclude = cycleDayBefore.temperature.exclude + } + + return ret +} diff --git a/components/chart/nfp-lines.js b/components/chart/nfp-lines.js index 51842d5..cf73c52 100644 --- a/components/chart/nfp-lines.js +++ b/components/chart/nfp-lines.js @@ -7,7 +7,12 @@ export default function () { } function updateCurrentCycle(dateString) { - cycle.status = getCycleStatusForDay(dateString) + // for the NFP lines, we don't care about potentially extending the + // preOvu phase, so we don't include all earlier cycles, as that is + // an expensive db operation at the moment + cycle.status = getCycleStatusForDay( + dateString, { excludeEarlierCycles: true } + ) if(!cycle.status) { cycle.noMoreCycles = true return diff --git a/lib/sympto-adapter.js b/lib/sympto-adapter.js index 363538e..08ec41f 100644 --- a/lib/sympto-adapter.js +++ b/lib/sympto-adapter.js @@ -21,7 +21,7 @@ export function getFertilityStatusStringForDay(dateString) { return mapToString(phaseNameForDay, dateString, status) } -export function getCycleStatusForDay(dateString) { +export function getCycleStatusForDay(dateString, opts = {}) { const { getCycleForDay, getCyclesBefore, @@ -37,6 +37,8 @@ export function getCycleStatusForDay(dateString) { if (previousCycle) { cycleInfo.previousCycle = formatCycleForSympto(previousCycle) + } + if (previousCycle && !opts.excludeEarlierCycles) { const earlierCycles = getCyclesBefore(previousCycle[0]) if (earlierCycles) { cycleInfo.earlierCycles = earlierCycles.map(formatCycleForSympto)