diff --git a/components/chart.js b/components/chart.js index 3539161..ce9c443 100644 --- a/components/chart.js +++ b/components/chart.js @@ -1,20 +1,21 @@ import React, { Component } from 'react' -import { ScrollView } from 'react-native' +import { FlatList } from 'react-native' import range from 'date-range' import Svg,{ G, - Polyline, Rect, Text, - Circle + Circle, + Line } from 'react-native-svg' import { LocalDate } from 'js-joda' -import { bleedingDaysSortedByDate, temperatureDaysSortedByDate, getOrCreateCycleDay } from '../db' +import { getCycleDay, getOrCreateCycleDay, cycleDaysSortedByDate } from '../db' const right = 600 const top = 10 const bottom = 350 const columnWidth = 30 +const middle = columnWidth / 2 const dateRow = { height: 30, width: right @@ -30,36 +31,21 @@ const curveColor = 'darkblue' export default class CycleChart extends Component { constructor(props) { super(props) - this.xAxisTicks = makeXAxisTicks(cycleDaysToShow) - this.state = { - curveCoordinates: this.makeCurveCoordinates(), - bleedIconCoordinates: this.makeBleedIconCoordinates() + columns: makeColumnInfo(cycleDaysToShow) } - this.setStateWithNewCurveCoordinates = (function (chartComponent) { - return function () { - chartComponent.setState({ - curveCoordinates: chartComponent.makeCurveCoordinates() - }) + this.recalculateChartInfo = (function(Chart) { + return function() { + Chart.setState({columns: makeColumnInfo(cycleDaysToShow)}) } })(this) - this.setStateWithNewBleedIconCoordinates = (function (chartComponent) { - return function () { - chartComponent.setState({ - bleedIconCoordinates: chartComponent.makeBleedIconCoordinates() - }) - } - })(this) - - temperatureDaysSortedByDate.addListener(this.setStateWithNewCurveCoordinates) - bleedingDaysSortedByDate.addListener(this.setStateWithNewBleedIconCoordinates) + cycleDaysSortedByDate.addListener(this.recalculateChartInfo) } componentWillUnmount() { - temperatureDaysSortedByDate.removeListener(this.setStateWithNewCurveCoordinates) - temperatureDaysSortedByDate.removeListener(this.setStateWithNewBleedIconCoordinates) + cycleDaysSortedByDate.removeListener(this.recalculateChartInfo) } passDateToDayView(dateString) { @@ -67,11 +53,11 @@ export default class CycleChart extends Component { this.props.navigation.navigate('cycleDay', { cycleDay }) } - makeDayColumn(columnInfo) { + makeDayColumn({ label, cycleDay, y }, index) { return ( - + this.passDateToDayView(label)}> {columnInfo.label.split('-')[2]} + >{label.split('-')[2]} + + {cycleDay && cycleDay.bleeding ? : null} + + {y ? this.drawDotAndLine(y, index) : null} ) } - makeColumnGrid(xAxisTicks) { - return xAxisTicks.map(this.makeDayColumn.bind(this)) - } + drawDotAndLine(currY, index) { + let lineToRight + let lineToLeft + const cols = this.state.columns - placeTouchHandlerRectangles() { - return this.xAxisTicks.map(columnInfo => { - return ( - this.passDateToDayView(columnInfo.label)} - /> - ) - }) - } + function makeLine(otherColY, x) { + const middleY = ((otherColY - currY) / 2) + currY + const rightTarget = [x, middleY] + return + } - placeBleedingSymbolsOnColumns() { - return this.state.bleedIconCoordinates.map(x => { - return () - }) - } + const thereIsADotToTheRight = index > 0 && cols[index - 1].y + const thereIsADotToTheLeft = index < cols.length - 1 && cols[index + 1].y - makeCurveCoordinates() { - return temperatureDaysSortedByDate - .filter(cycleDayIsNotInTheFuture()) - .reduce(separateIntoContinousChunks, [[]]) - .map(makeCurveCoordinatesForChunk.bind(this)) - } + if (thereIsADotToTheRight) { + lineToRight = makeLine(cols[index - 1].y, columnWidth) + } + if (thereIsADotToTheLeft) { + lineToLeft = makeLine(cols[index + 1].y, 0) + } - makeBleedIconCoordinates() { - return bleedingDaysSortedByDate - .filter(cycleDayIsNotInTheFuture()) - .map(day => { - const match = this.xAxisTicks.find(tick => { - return tick.label === day.date - }) - return match.rightOffset + columnWidth / 2 - }) - } - - makeTemperatureCurves() { - return this.state.curveCoordinates.map(makeCurveFromPoints) - } - - componentDidMount() { - this.scrollContainer.scrollToEnd() + return ( + + {lineToRight} + {lineToLeft} + ) } render() { return ( - { - if (scroll) this.scrollContainer = scroll + { + return ( + + {this.makeDayColumn(item, index)} + + ) }} - horizontal={true}> - - this.scrollContainer.scrollToEnd()} - > - - { this.makeColumnGrid(this.xAxisTicks) } - - { this.placeBleedingSymbolsOnColumns() } - - { this.makeTemperatureCurves() } - - {/* we place a trasnparent rectangle over every day column */} - {/* so that all elements including the line and circles are clickable */} - { this.placeTouchHandlerRectangles() } - - - + keyExtractor={item => item.label} + > + ) } } -function makeXAxisTicks(n) { +function makeColumnInfo(n) { const xAxisDates = getPreviousDays(n).map(jsDate => { return LocalDate.of( jsDate.getFullYear(), @@ -184,11 +148,13 @@ function makeXAxisTicks(n) { ).toString() }) - return xAxisDates.map((datestring, columnIndex) => { - const rightOffset = right - (columnWidth * (columnIndex + 1)) + return xAxisDates.map(datestring => { + const cycleDay = getCycleDay(datestring) + const temp = cycleDay && cycleDay.temperature && cycleDay.temperature.value return { label: datestring, - rightOffset + cycleDay, + y: temp ? normalizeToScale(temp) : null } }) } @@ -205,65 +171,4 @@ function normalizeToScale(temp) { const valueRelativeToScale = (temperatureScale.high - temp) / (temperatureScale.high - temperatureScale.low) const scaleHeight = bottom - top return scaleHeight * valueRelativeToScale -} - -function cycleDayIsNotInTheFuture() { - const today = LocalDate.now() - return function (cycleDay) { - const cycleDayLocalDate = LocalDate.parse(cycleDay.date) - return cycleDayLocalDate.isBefore(today) || cycleDayLocalDate.isEqual(today) - } -} - -function separateIntoContinousChunks(curveChunks, curr) { - const lastChunk = curveChunks[curveChunks.length - 1] - const lastSeenCycleDate = lastChunk.length && lastChunk[lastChunk.length - 1] - - if (!lastSeenCycleDate) { - lastChunk.push(curr) - return curveChunks - } - - const lastSeenLocalDate = LocalDate.parse(lastSeenCycleDate.date) - const currLocalDate = LocalDate.parse(curr.date) - if (lastSeenLocalDate.compareTo(currLocalDate) === 1) { - lastChunk.push(curr) - } else { - curveChunks.push([curr]) - } - - return curveChunks -} - -function makeCurveCoordinatesForChunk(chunk) { - return chunk - .map(cycleDay => { - const match = this.xAxisTicks.find(tick => tick.label === cycleDay.date) - const x = match.rightOffset + columnWidth / 2 - const y = normalizeToScale(cycleDay.temperature.value) - return [x, y] - }) -} - -function makeCurveFromPoints(curveChunkPoints, i) { - const pointsInPolyLineFormat = curveChunkPoints - .map(xYPair => xYPair.join()) - .join(' ') - - return ( - - - { makeDots(curveChunkPoints) } - - ) -} - -function makeDots(points) { - return points.map(([x, y], i) => ) } \ No newline at end of file diff --git a/db.js b/db.js index eced0a6..3fdd401 100644 --- a/db.js +++ b/db.js @@ -53,7 +53,7 @@ function saveTemperature(cycleDay, temperature) { }) } -const getCycleDaysSortedByDateView = () => db.objects('CycleDay').sorted('date', true) +const cycleDaysSortedByDate = db.objects('CycleDay').sorted('date', true) function saveBleeding(cycleDay, bleeding) { db.write(() => { @@ -73,6 +73,10 @@ function getOrCreateCycleDay(localDate) { return result } +function getCycleDay(localDate) { + return db.objectForPrimaryKey('CycleDay', localDate) +} + function deleteAll() { db.write(() => { db.deleteAll() @@ -95,7 +99,8 @@ export { getOrCreateCycleDay, bleedingDaysSortedByDate, temperatureDaysSortedByDate, - getCycleDaysSortedByDateView, + cycleDaysSortedByDate, deleteAll, - getPreviousTemperature + getPreviousTemperature, + getCycleDay }