From 2471b92eaf2ea05f90afd68239739d65a39ca7e5 Mon Sep 17 00:00:00 2001
From: Julia Friesel <julia.friesel@gmail.com>
Date: Fri, 24 Aug 2018 09:53:57 +0200
Subject: [PATCH] Determine chart height dynamically and fill the window

---
 .eslintrc                                    |   9 +-
 components/chart/chart.js                    | 129 +++++++++++--------
 components/chart/day-column.js               |  27 ++--
 components/chart/nfp-lines.js                |   4 +-
 components/chart/styles.js                   |   5 +-
 components/chart/y-axis.js                   |  30 ++---
 components/cycle-day/symptoms/temperature.js |   2 -
 config.js                                    |   8 +-
 styles/index.js                              |  10 +-
 9 files changed, 126 insertions(+), 98 deletions(-)

diff --git a/.eslintrc b/.eslintrc
index fb7b9841..028ba73c 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -49,6 +49,13 @@
     "prefer-const": "error",
     "no-trailing-spaces": "error",
     "react/prop-types": 0,
-    "max-len": [1, {"ignoreStrings": true}]
+    "max-len": [
+      1,
+      {
+        "ignoreStrings": true,
+        "ignoreComments": true,
+        "ignoreTemplateLiterals": true
+      }
+    ]
   }
 }
\ No newline at end of file
diff --git a/components/chart/chart.js b/components/chart/chart.js
index 09bcf449..d8a97134 100644
--- a/components/chart/chart.js
+++ b/components/chart/chart.js
@@ -1,9 +1,9 @@
 import React, { Component } from 'react'
-import { View, FlatList, ScrollView } from 'react-native'
+import { View, FlatList, Text } from 'react-native'
 import range from 'date-range'
 import { LocalDate } from 'js-joda'
 import { makeYAxisLabels, normalizeToScale, makeHorizontalGrid } from './y-axis'
-import setUpFertilityStatusFunc from './nfp-lines'
+import nfpLines from './nfp-lines'
 import DayColumn from './day-column'
 import { getCycleDay, cycleDaysSortedByDate, getAmountOfCycleDays } from '../../db'
 import styles from './styles'
@@ -12,24 +12,27 @@ import { scaleObservable } from '../../local-storage'
 export default class CycleChart extends Component {
   constructor(props) {
     super(props)
-    this.state = {
-      columns: makeColumnInfo(setUpFertilityStatusFunc()),
-    }
+    this.state = {}
     this.renderColumn = ({item, index}) => {
       return (
         <DayColumn
           {...item}
           index={index}
           navigate={this.props.navigate}
+          chartHeight={this.state.chartHeight}
         />
       )
     }
+  }
 
-    this.reCalculateChartInfo = (function(Chart) {
-      return function() {
-        Chart.setState({columns: makeColumnInfo(setUpFertilityStatusFunc())})
-      }
-    })(this)
+  onLayout = ({ nativeEvent }) => {
+    if (this.state.chartHeight) return
+
+    const height = nativeEvent.layout.height
+    this.setState({ chartHeight: height })
+    this.reCalculateChartInfo = () => {
+      this.setState({ columns: this.makeColumnInfo(nfpLines(height)) })
+    }
 
     cycleDaysSortedByDate.addListener(this.reCalculateChartInfo)
     this.removeObvListener = scaleObservable(this.reCalculateChartInfo, false)
@@ -40,13 +43,65 @@ export default class CycleChart extends Component {
     this.removeObvListener()
   }
 
+  makeColumnInfo(getFhmAndLtlInfo) {
+    let amountOfCycleDays = getAmountOfCycleDays()
+    // if there's not much data yet, we want to show at least 30 days on the chart
+    if (amountOfCycleDays < 30) {
+      amountOfCycleDays = 30
+    } else {
+      // we don't want the chart to end abruptly before the first data day
+      amountOfCycleDays += 5
+    }
+    const jsDates = getTodayAndPreviousDays(amountOfCycleDays)
+    const xAxisDates = jsDates.map(jsDate => {
+      return LocalDate.of(
+        jsDate.getFullYear(),
+        jsDate.getMonth() + 1,
+        jsDate.getDate()
+      ).toString()
+    })
+
+    const columns = xAxisDates.map(dateString => {
+      const cycleDay = getCycleDay(dateString)
+      const symptoms = ['temperature', 'mucus', 'bleeding'].reduce((acc, symptom) => {
+        acc[symptom] = cycleDay && cycleDay[symptom] && cycleDay[symptom].value
+        acc[`${symptom}Exclude`] = cycleDay && cycleDay[symptom] && cycleDay[symptom].exclude
+        return acc
+      }, {})
+
+      const temp = symptoms.temperature
+      return {
+        dateString,
+        y: temp ? normalizeToScale(temp, this.state.chartHeight) : null,
+        ...symptoms,
+        ...getFhmAndLtlInfo(dateString, temp)
+      }
+    })
+
+    return columns.map((col, i) => {
+      const info = getInfoForNeighborColumns(i, columns)
+      return Object.assign(col, info)
+    })
+  }
+
   render() {
     return (
-      <ScrollView>
-        <View style={{ flexDirection: 'row', marginTop: 50 }}>
-          <View {...styles.yAxis}>{makeYAxisLabels()}</View>
-          {makeHorizontalGrid()}
-          {<FlatList
+      <View
+        onLayout={this.onLayout}
+        style={{ flexDirection: 'row', flex: 1 }}
+      >
+        {!this.state.chartHeight && <Text>Loading...</Text>}
+        {this.state.chartHeight &&
+          <View
+            style={[styles.yAxis, {height: this.state.chartHeight}]}
+          >
+            {makeYAxisLabels(this.state.chartHeight)}
+          </View>}
+
+        {this.state.chartHeight && makeHorizontalGrid(this.state.chartHeight)}
+
+        {this.state.chartHeight &&
+          <FlatList
             horizontal={true}
             inverted={true}
             showsHorizontalScrollIndicator={false}
@@ -56,51 +111,13 @@ export default class CycleChart extends Component {
             initialNumToRender={15}
             maxToRenderPerBatch={5}
           >
-          </FlatList>}
-        </View>
-      </ScrollView>
+          </FlatList>
+        }
+      </View>
     )
   }
 }
 
-function makeColumnInfo(getFhmAndLtlInfo) {
-  let amountOfCycleDays = getAmountOfCycleDays()
-  // if there's not much data yet, we want to show at least 30 days on the chart
-  if (amountOfCycleDays < 30) {
-    amountOfCycleDays = 30
-  } else {
-    // we don't want the chart to end abruptly before the first data day
-    amountOfCycleDays += 5
-  }
-  const xAxisDates = getTodayAndPreviousDays(amountOfCycleDays).map(jsDate => {
-    return LocalDate.of(
-      jsDate.getFullYear(),
-      jsDate.getMonth() + 1,
-      jsDate.getDate()
-    ).toString()
-  })
-
-  const columns = xAxisDates.map(dateString => {
-    const cycleDay = getCycleDay(dateString)
-    const symptoms = ['temperature', 'mucus', 'bleeding'].reduce((acc, symptom) => {
-      acc[symptom] = cycleDay && cycleDay[symptom] && cycleDay[symptom].value
-      acc[`${symptom}Exclude`] = cycleDay && cycleDay[symptom] && cycleDay[symptom].exclude
-      return acc
-    }, {})
-
-    return {
-      dateString,
-      y: symptoms.temperature ? normalizeToScale(symptoms.temperature) : null,
-      ...symptoms,
-      ...getFhmAndLtlInfo(dateString, symptoms.temperature)
-    }
-  })
-
-  return columns.map((col, i) => {
-    const info = getInfoForNeighborColumns(i, columns)
-    return Object.assign(col, info)
-  })
-}
 
 function getTodayAndPreviousDays(n) {
   const today = new Date()
diff --git a/components/chart/day-column.js b/components/chart/day-column.js
index 1b61a7ae..b6bf2d53 100644
--- a/components/chart/day-column.js
+++ b/components/chart/day-column.js
@@ -105,25 +105,22 @@ export default class DayColumn extends Component {
     const cycleDayNumber = getCycleDayNumber(dateString)
     const shortDate = dateString.split('-').slice(1).join('-')
     const cycleDayLabel = (
-      <Text style={label.number} y={config.cycleDayNumberRowY}>
+      <Text style={label.number}>
         {cycleDayNumber}
       </Text>)
     const dateLabel = (
-      <Text style = {label.date} y={config.dateRowY}>
+      <Text style = {label.date}>
         {shortDate}
       </Text>
     )
-    columnElements.push(
-      <View position='absolute' bottom={0} key='date'>
-        {cycleDayLabel}
-        {dateLabel}
-      </View>
-    )
 
-    return React.createElement(
+    const columnHeight = this.props.chartHeight * config.columnHeightPercentage
+    const xAxisHeight = this.props.chartHeight * config.xAxisHeightPercentage
+
+    const column = React.createElement(
       TouchableOpacity,
       {
-        style: styles.column.rect,
+        style: [styles.column.rect, {height: columnHeight}],
         key: this.props.index.toString(),
         onPress: () => {
           this.passDateToDayView(dateString)
@@ -132,5 +129,15 @@ export default class DayColumn extends Component {
       },
       columnElements
     )
+
+    return (
+      <View>
+        {column}
+        <View style={{height: xAxisHeight}}>
+          {cycleDayLabel}
+          {dateLabel}
+        </View>
+      </View>
+    )
   }
 }
\ No newline at end of file
diff --git a/components/chart/nfp-lines.js b/components/chart/nfp-lines.js
index d9f4507f..ded95e81 100644
--- a/components/chart/nfp-lines.js
+++ b/components/chart/nfp-lines.js
@@ -1,7 +1,7 @@
 import { getCycleStatusForDay } from '../../lib/sympto-adapter'
 import { normalizeToScale } from './y-axis'
 
-export default function () {
+export default function (chartHeight) {
   const cycle = {
     status: null
   }
@@ -71,7 +71,7 @@ export default function () {
         dateIsInPeriOrPostPhase(dateString) &&
         isInTempMeasuringPhase(temperature, dateString)
       ) {
-        ret.drawLtlAt = normalizeToScale(tempShift.ltl)
+        ret.drawLtlAt = normalizeToScale(tempShift.ltl, chartHeight)
       }
     }
 
diff --git a/components/chart/styles.js b/components/chart/styles.js
index 8ee5a30e..d60e1a0c 100644
--- a/components/chart/styles.js
+++ b/components/chart/styles.js
@@ -38,10 +38,10 @@ const styles = {
     },
     rect: {
       width: config.columnWidth,
-      height: config.chartHeight,
       borderStyle: 'solid',
       borderColor: 'grey',
-      borderWidth: 0.5
+      borderLeftWidth: 0.5,
+      borderRightWidth: 0.5,
     }
   },
   bleedingIcon: {
@@ -63,7 +63,6 @@ const styles = {
     '#993299'
   ],
   yAxis: {
-    height: config.chartHeight,
     width: config.columnWidth,
     borderRightWidth: 0.5,
     borderColor: 'lightgrey',
diff --git a/components/chart/y-axis.js b/components/chart/y-axis.js
index 5b78b368..e65449b9 100644
--- a/components/chart/y-axis.js
+++ b/components/chart/y-axis.js
@@ -4,19 +4,18 @@ import config from '../../config'
 import styles from './styles'
 import { scaleObservable } from '../../local-storage'
 
-export function makeYAxisLabels() {
+export function makeYAxisLabels(chartHeight) {
   const units = config.temperatureScale.units
   const scaleMax = scaleObservable.value.max
+  const style = styles.yAxisLabel
 
-  return getTickPositions().map((y, i) => {
-    const style = styles.yAxisLabel
+  return getTickPositions(chartHeight).map((y, i) => {
     // this eyeballing is sadly necessary because RN does not
     // support percentage values for transforms, which we'd need
     // to reliably place the label vertically centered to the grid
-    style.top = y - 8
     return (
       <Text
-        style={{ ...style }}
+        style={[style, {top: y - 8}]}
         key={i}>
         {scaleMax - i * units}
       </Text>
@@ -24,8 +23,8 @@ export function makeYAxisLabels() {
   })
 }
 
-export function makeHorizontalGrid() {
-  return getTickPositions().map(tick => {
+export function makeHorizontalGrid(chartHeight) {
+  return getTickPositions(chartHeight).map(tick => {
     return (
       <View
         top={tick}
@@ -36,24 +35,25 @@ export function makeHorizontalGrid() {
   })
 }
 
-function getTickPositions() {
+function getTickPositions(chartHeight) {
   const units = config.temperatureScale.units
   const scaleMin = scaleObservable.value.min
   const scaleMax = scaleObservable.value.max
-  const numberOfTicks = (scaleMax - scaleMin) * (1 / units)
-  const tickDistance = config.chartHeight / numberOfTicks
+  const numberOfTicks = (scaleMax - scaleMin) * (1 / units) + 1
+  const columnHeight = chartHeight * config.columnHeightPercentage
+  const tickDistance = columnHeight / numberOfTicks
 
   const tickPositions = []
-  // for style reasons, we don't want the first and last tick
-  for (let i = 1; i < numberOfTicks - 1; i++) {
-    tickPositions.push(tickDistance * i)
+  const margin = tickDistance / 2
+  for (let i = 0; i < numberOfTicks; i++) {
+    tickPositions.push(tickDistance * i + margin)
   }
   return tickPositions
 }
 
-export function normalizeToScale(temp) {
+export function normalizeToScale(temp, chartHeight) {
   const scale = scaleObservable.value
   const valueRelativeToScale = (scale.max - temp) / (scale.max - scale.min)
-  const scaleHeight = config.chartHeight
+  const scaleHeight = chartHeight * config.columnHeightPercentage
   return scaleHeight * valueRelativeToScale
 }
\ No newline at end of file
diff --git a/components/cycle-day/symptoms/temperature.js b/components/cycle-day/symptoms/temperature.js
index fd3f905e..fda52006 100644
--- a/components/cycle-day/symptoms/temperature.js
+++ b/components/cycle-day/symptoms/temperature.js
@@ -42,7 +42,6 @@ export default class Temp extends Component {
       }
     } else {
       const prevTemp = getPreviousTemperature(this.cycleDay)
-      console.log(prevTemp)
       if (prevTemp) {
         this.state.temperature = prevTemp.toString()
         this.state.isSuggestion = true
@@ -122,7 +121,6 @@ export default class Temp extends Component {
 class TempInput extends Component {
   checkRange = () => {
     const value = Number(this.props.value)
-    console.log(value)
     if (isNaN(value)) return
     const scale = scaleObservable.value
     if (value < scale.min || value > scale.max) {
diff --git a/config.js b/config.js
index d44ba54c..ffb8f604 100644
--- a/config.js
+++ b/config.js
@@ -1,6 +1,7 @@
 const config = {
-  chartHeight: 350,
   columnWidth: 25,
+  columnHeightPercentage: 0.92,
+  xAxisHeightPercentage: 0.08,
   temperatureScale: {
     defaultLow: 35,
     defaultHigh: 38,
@@ -10,9 +11,6 @@ const config = {
   }
 }
 
-const margin = 3
-config.columnMiddle = config.columnWidth / 2,
-config.dateRowY = config.chartHeight - 15 - margin
-config.cycleDayNumberRowY = config.chartHeight - margin
+config.columnMiddle = config.columnWidth / 2
 
 export default config
\ No newline at end of file
diff --git a/styles/index.js b/styles/index.js
index 43a55996..406de7ab 100644
--- a/styles/index.js
+++ b/styles/index.js
@@ -92,16 +92,17 @@ export default StyleSheet.create({
   },
   header: {
     backgroundColor: primaryColor,
-    paddingVertical: 18,
     paddingHorizontal: 15,
     alignItems: 'center',
-    justifyContent: 'center'
+    justifyContent: 'center',
+    height: '10%'
   },
   menu: {
     backgroundColor: primaryColor,
     alignItems: 'center',
     justifyContent: 'space-between',
-    flexDirection: 'row'
+    flexDirection: 'row',
+    height: '12%'
   },
   menuItem: {
     alignItems: 'center',
@@ -116,7 +117,8 @@ export default StyleSheet.create({
   },
   headerCycleDay: {
     flexDirection: 'row',
-    justifyContent: 'space-between'
+    justifyContent: 'space-between',
+    height: '15%'
   },
   navigationArrow: {
     fontSize: 60,
-- 
GitLab