Merge branch 'chart-refactoring-part-2' into 'master'
Chart refactoring part 2 See merge request bloodyhealth/drip!255
This commit is contained in:
@@ -0,0 +1,28 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
|
import { Shape } from 'react-native/Libraries/ART/ReactNativeART'
|
||||||
|
|
||||||
|
import styles from './styles'
|
||||||
|
|
||||||
|
const ChartLine = ({ path, isNfpLine = false }) => {
|
||||||
|
const strokeStyle =
|
||||||
|
isNfpLine ? styles.nfpLine.stroke : styles.column.stroke.color
|
||||||
|
const strokeWidth =
|
||||||
|
isNfpLine ? styles.nfpLine.strokeWidth : styles.column.stroke.width
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Shape
|
||||||
|
stroke={strokeStyle}
|
||||||
|
strokeWidth={strokeWidth}
|
||||||
|
d={path}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ChartLine.propTypes = {
|
||||||
|
path: PropTypes.object,
|
||||||
|
isNfpLine: PropTypes.bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChartLine
|
||||||
+12
-44
@@ -1,20 +1,20 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import { View, FlatList, ActivityIndicator } from 'react-native'
|
import { View, FlatList, ActivityIndicator } from 'react-native'
|
||||||
import { LocalDate } from 'js-joda'
|
|
||||||
|
|
||||||
|
import AppLoadingView from '../app-loading'
|
||||||
import YAxis from './y-axis'
|
import YAxis from './y-axis'
|
||||||
import nfpLines from './nfp-lines'
|
import nfpLines from './nfp-lines'
|
||||||
import DayColumn from './day-column'
|
import DayColumn from './day-column'
|
||||||
import HorizontalGrid from './horizontal-grid'
|
import HorizontalGrid from './horizontal-grid'
|
||||||
|
|
||||||
import { getCycleDaysSortedByDate, getAmountOfCycleDays } from '../../db'
|
import { getCycleDaysSortedByDate } from '../../db'
|
||||||
import styles from './styles'
|
import nothingChanged from '../../db/db-unchanged'
|
||||||
import { scaleObservable } from '../../local-storage'
|
import { scaleObservable } from '../../local-storage'
|
||||||
|
import { makeColumnInfo } from '../helpers/chart'
|
||||||
|
|
||||||
import config from '../../config'
|
import config from '../../config'
|
||||||
|
|
||||||
import AppLoadingView from '../app-loading'
|
import styles from './styles'
|
||||||
|
|
||||||
import nothingChanged from '../../db/db-unchanged'
|
|
||||||
|
|
||||||
export default class CycleChart extends Component {
|
export default class CycleChart extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -32,7 +32,6 @@ export default class CycleChart extends Component {
|
|||||||
navigate={this.props.navigate}
|
navigate={this.props.navigate}
|
||||||
symptomHeight={this.symptomHeight}
|
symptomHeight={this.symptomHeight}
|
||||||
columnHeight={this.columnHeight}
|
columnHeight={this.columnHeight}
|
||||||
chartHeight={this.state.chartHeight}
|
|
||||||
symptomRowSymptoms={this.symptomRowSymptoms}
|
symptomRowSymptoms={this.symptomRowSymptoms}
|
||||||
chartSymptoms={this.chartSymptoms}
|
chartSymptoms={this.chartSymptoms}
|
||||||
getFhmAndLtlInfo={this.getFhmAndLtlInfo}
|
getFhmAndLtlInfo={this.getFhmAndLtlInfo}
|
||||||
@@ -72,7 +71,7 @@ export default class CycleChart extends Component {
|
|||||||
this.chartSymptoms.push('temperature')
|
this.chartSymptoms.push('temperature')
|
||||||
}
|
}
|
||||||
|
|
||||||
const columnData = this.makeColumnInfo()
|
const columnData = makeColumnInfo()
|
||||||
this.setState({
|
this.setState({
|
||||||
columns: columnData,
|
columns: columnData,
|
||||||
chartHeight: height
|
chartHeight: height
|
||||||
@@ -104,42 +103,28 @@ export default class CycleChart extends Component {
|
|||||||
this.removeObvListener()
|
this.removeObvListener()
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
|
||||||
amountOfCycleDays = 30
|
|
||||||
} else {
|
|
||||||
// we don't want the chart to end abruptly before the first data day
|
|
||||||
amountOfCycleDays += 5
|
|
||||||
}
|
|
||||||
const localDates = getTodayAndPreviousDays(amountOfCycleDays)
|
|
||||||
return localDates.map(localDate => localDate.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { chartHeight, chartLoaded } = this.state
|
const { chartHeight, chartLoaded } = this.state
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
onLayout={this.onLayout}
|
onLayout={this.onLayout}
|
||||||
style={{ flexDirection: 'row', flex: 1 }}
|
style={styles.container}
|
||||||
>
|
>
|
||||||
{!chartLoaded && <AppLoadingView />}
|
{!chartLoaded && <AppLoadingView />}
|
||||||
|
|
||||||
{chartHeight && chartLoaded && (
|
{chartHeight && chartLoaded && (
|
||||||
|
<React.Fragment>
|
||||||
<YAxis
|
<YAxis
|
||||||
height={this.columnHeight}
|
height={this.columnHeight}
|
||||||
symptomsToDisplay={this.symptomRowSymptoms}
|
symptomsToDisplay={this.symptomRowSymptoms}
|
||||||
symptomsSectionHeight={this.symptomRowHeight}
|
symptomsSectionHeight={this.symptomRowHeight}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
|
|
||||||
{chartHeight && chartLoaded && (
|
|
||||||
<HorizontalGrid
|
<HorizontalGrid
|
||||||
height={this.columnHeight}
|
height={this.columnHeight}
|
||||||
startPosition={this.symptomRowHeight}
|
startPosition={this.symptomRowHeight}
|
||||||
/>)
|
/>
|
||||||
}
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
|
||||||
{chartHeight &&
|
{chartHeight &&
|
||||||
<FlatList
|
<FlatList
|
||||||
@@ -171,20 +156,3 @@ function LoadingMoreView(props) {
|
|||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTodayAndPreviousDays(n) {
|
|
||||||
const today = LocalDate.now()
|
|
||||||
const targetDate = today.minusDays(n)
|
|
||||||
|
|
||||||
function getDaysInRange(currDate, range) {
|
|
||||||
if (currDate.isBefore(targetDate)) {
|
|
||||||
return range
|
|
||||||
} else {
|
|
||||||
range.push(currDate)
|
|
||||||
const next = currDate.minusDays(1)
|
|
||||||
return getDaysInRange(next, range)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return getDaysInRange(today, [])
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
|
import { Text, View } from 'react-native'
|
||||||
|
|
||||||
|
import moment from 'moment'
|
||||||
|
import { LocalDate } from 'js-joda'
|
||||||
|
|
||||||
|
import styles from './styles'
|
||||||
|
import cycleModule from '../../lib/cycle'
|
||||||
|
|
||||||
|
const CycleDayLabel = ({ height, date }) => {
|
||||||
|
const { label } = styles.column
|
||||||
|
const dayDate = LocalDate.parse(date)
|
||||||
|
const cycleDayNumber = cycleModule().getCycleDayNumber(date)
|
||||||
|
|
||||||
|
const isFirstDayOfMonth = dayDate.dayOfMonth() === 1
|
||||||
|
const dateFormatting = isFirstDayOfMonth ? 'MMM' : 'Do'
|
||||||
|
const shortDate = moment(date, "YYYY-MM-DD").format(dateFormatting)
|
||||||
|
const boldDateLabel = isFirstDayOfMonth ? {fontWeight: 'bold'} : {}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={{ height }}>
|
||||||
|
<Text style={label.number}>
|
||||||
|
{cycleDayNumber ? cycleDayNumber : ' '}
|
||||||
|
</Text>
|
||||||
|
<Text style={[label.date, boldDateLabel]}>
|
||||||
|
{shortDate}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
CycleDayLabel.propTypes = {
|
||||||
|
height: PropTypes.number,
|
||||||
|
date: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CycleDayLabel
|
||||||
+22
-201
@@ -1,30 +1,20 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import {
|
import { TouchableOpacity } from 'react-native'
|
||||||
Text, View, TouchableOpacity
|
|
||||||
} from 'react-native'
|
|
||||||
import {
|
|
||||||
Surface,
|
|
||||||
Group as G,
|
|
||||||
Path,
|
|
||||||
Shape
|
|
||||||
} from 'react-native/Libraries/ART/ReactNativeART'
|
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
|
|
||||||
import { setDate } from '../../slices/date'
|
import { setDate } from '../../slices/date'
|
||||||
|
|
||||||
import { LocalDate } from 'js-joda'
|
|
||||||
import moment from 'moment'
|
|
||||||
import styles from './styles'
|
|
||||||
import config from '../../config'
|
|
||||||
import cycleModule from '../../lib/cycle'
|
|
||||||
import { getCycleDay } from '../../db'
|
import { getCycleDay } from '../../db'
|
||||||
|
|
||||||
import DotAndLine from './dot-and-line'
|
|
||||||
import SymptomCell from './symptom-cell'
|
import SymptomCell from './symptom-cell'
|
||||||
|
import TemperatureColumn from './temperature-column'
|
||||||
|
import CycleDayLabel from './cycle-day-label'
|
||||||
|
|
||||||
import { normalizeToScale } from '../helpers/chart'
|
import {
|
||||||
|
symptomColorMethods,
|
||||||
const label = styles.column.label
|
getTemperatureProps,
|
||||||
|
isSymptomDataComplete
|
||||||
|
} from '../helpers/chart'
|
||||||
|
|
||||||
class DayColumn extends Component {
|
class DayColumn extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -40,14 +30,13 @@ class DayColumn extends Component {
|
|||||||
|
|
||||||
if (symptomData && symptom === 'temperature') {
|
if (symptomData && symptom === 'temperature') {
|
||||||
symptomDataToDisplay[symptom] =
|
symptomDataToDisplay[symptom] =
|
||||||
this.getTemperatureProps(symptomData, columnHeight, dateString)
|
getTemperatureProps(symptomData, columnHeight, dateString)
|
||||||
} else {
|
} else {
|
||||||
if (symptomData && ! symptomData.exclude) {
|
if (symptomData && ! symptomData.exclude) {
|
||||||
// if symptomColorMethods entry doesn't exist for given symptom,
|
// if symptomColorMethods entry doesn't exist for given symptom,
|
||||||
// use 'default'
|
// use 'default'
|
||||||
const getSymptomColorIndex =
|
const getSymptomColorIndex =
|
||||||
this.symptomColorMethods[symptom] ||
|
symptomColorMethods[symptom] || symptomColorMethods['default']
|
||||||
this.symptomColorMethods['default']
|
|
||||||
|
|
||||||
symptomDataToDisplay[symptom] = getSymptomColorIndex(symptomData)
|
symptomDataToDisplay[symptom] = getSymptomColorIndex(symptomData)
|
||||||
}
|
}
|
||||||
@@ -64,75 +53,6 @@ class DayColumn extends Component {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
getTemperatureProps = (symptomData, columnHeight, dateString) => {
|
|
||||||
const extractedData = {}
|
|
||||||
const { value, exclude } = symptomData
|
|
||||||
const neighborTemperatureGraphPoints =
|
|
||||||
getInfoForNeighborColumns(dateString, columnHeight)
|
|
||||||
|
|
||||||
for (const key in neighborTemperatureGraphPoints) {
|
|
||||||
extractedData[key] = neighborTemperatureGraphPoints[key]
|
|
||||||
}
|
|
||||||
return Object.assign({
|
|
||||||
value,
|
|
||||||
y: normalizeToScale(value, columnHeight),
|
|
||||||
temperatureExclude: exclude,
|
|
||||||
}, extractedData)
|
|
||||||
}
|
|
||||||
|
|
||||||
symptomColorMethods = {
|
|
||||||
'mucus': (symptomData) => {
|
|
||||||
const { feeling, texture } = symptomData
|
|
||||||
const colorIndex = feeling + texture
|
|
||||||
return colorIndex
|
|
||||||
},
|
|
||||||
'cervix': (symptomData) => {
|
|
||||||
const { opening, firmness } = symptomData
|
|
||||||
const isDataComplete = opening !== null && firmness !== null
|
|
||||||
const isClosedAndHard =
|
|
||||||
isDataComplete &&
|
|
||||||
(opening === 0 && firmness === 0)
|
|
||||||
const colorIndex = isClosedAndHard ? 0 : 2
|
|
||||||
return colorIndex
|
|
||||||
},
|
|
||||||
'sex': (symptomData) => {
|
|
||||||
const { solo, partner } = symptomData
|
|
||||||
const colorIndex = (solo !== null && partner !== null) ?
|
|
||||||
(solo + 2 * partner - 1) : 0
|
|
||||||
return colorIndex
|
|
||||||
},
|
|
||||||
'bleeding': (symptomData) => {
|
|
||||||
const { value } = symptomData
|
|
||||||
const colorIndex = value
|
|
||||||
return colorIndex
|
|
||||||
},
|
|
||||||
'default': () => { // desire, pain, mood, note
|
|
||||||
const colorIndex = 0
|
|
||||||
return colorIndex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isSymptomDataComplete = (symptom) => {
|
|
||||||
const { dateString } = this.props
|
|
||||||
const cycleDayData = getCycleDay(dateString)
|
|
||||||
const symptomData = cycleDayData[symptom]
|
|
||||||
|
|
||||||
const dataCompletenessCheck = {
|
|
||||||
'cervix': () => {
|
|
||||||
const { opening, firmness } = symptomData
|
|
||||||
return (opening !== null) && (firmness !== null)
|
|
||||||
},
|
|
||||||
'mucus': () => {
|
|
||||||
const { feeling, texture } = symptomData
|
|
||||||
return (feeling !== null) && (texture !== null)
|
|
||||||
},
|
|
||||||
'default': () => {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (dataCompletenessCheck[symptom] || dataCompletenessCheck['default'])()
|
|
||||||
}
|
|
||||||
|
|
||||||
onDaySelect = (date) => {
|
onDaySelect = (date) => {
|
||||||
this.props.setDate(date)
|
this.props.setDate(date)
|
||||||
this.props.navigate('CycleDay')
|
this.props.navigate('CycleDay')
|
||||||
@@ -143,89 +63,11 @@ class DayColumn extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const columnElements = []
|
|
||||||
const { dateString,
|
const { dateString,
|
||||||
symptomRowSymptoms,
|
symptomRowSymptoms,
|
||||||
chartHeight,
|
|
||||||
columnHeight,
|
columnHeight,
|
||||||
xAxisHeight } = this.props
|
xAxisHeight } = this.props
|
||||||
|
|
||||||
if(this.fhmAndLtl.drawLtlAt) {
|
|
||||||
const ltlLine = (<Shape
|
|
||||||
stroke={styles.nfpLine.stroke}
|
|
||||||
strokeWidth={styles.nfpLine.strokeWidth}
|
|
||||||
d={new Path()
|
|
||||||
.moveTo(0, this.fhmAndLtl.drawLtlAt)
|
|
||||||
.lineTo(config.columnWidth, this.fhmAndLtl.drawLtlAt)
|
|
||||||
}
|
|
||||||
key='ltl'
|
|
||||||
/>)
|
|
||||||
columnElements.push(ltlLine)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.fhmAndLtl.drawFhmLine) {
|
|
||||||
const x = styles.nfpLine.strokeWidth / 2
|
|
||||||
const fhmLine = (<Shape
|
|
||||||
fill="red"
|
|
||||||
stroke={styles.nfpLine.stroke}
|
|
||||||
strokeWidth={styles.nfpLine.strokeWidth}
|
|
||||||
d={new Path().moveTo(x, x).lineTo(x, columnHeight)}
|
|
||||||
key='fhm'
|
|
||||||
/>)
|
|
||||||
columnElements.push(fhmLine)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.data && this.data.temperature && this.data.temperature.y) {
|
|
||||||
const { temperatureExclude,
|
|
||||||
y,
|
|
||||||
rightY,
|
|
||||||
leftY,
|
|
||||||
rightTemperatureExclude,
|
|
||||||
leftTemperatureExclude
|
|
||||||
} = this.data.temperature
|
|
||||||
|
|
||||||
columnElements.push(
|
|
||||||
<DotAndLine
|
|
||||||
y={y}
|
|
||||||
exclude={temperatureExclude}
|
|
||||||
rightY={rightY}
|
|
||||||
rightTemperatureExclude={rightTemperatureExclude}
|
|
||||||
leftY={leftY}
|
|
||||||
leftTemperatureExclude={leftTemperatureExclude}
|
|
||||||
key='dotandline'
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const cycleDayNumber = cycleModule().getCycleDayNumber(dateString)
|
|
||||||
const dayDate = LocalDate.parse(dateString)
|
|
||||||
const shortDate = dayDate.dayOfMonth() === 1 ?
|
|
||||||
moment(dateString, "YYYY-MM-DD").format('MMM')
|
|
||||||
:
|
|
||||||
moment(dateString, "YYYY-MM-DD").format('Do')
|
|
||||||
const boldDateLabel = dayDate.dayOfMonth() === 1 ? {fontWeight: 'bold'} : {}
|
|
||||||
|
|
||||||
const cycleDayLabel = (
|
|
||||||
<Text style = {label.number}>
|
|
||||||
{cycleDayNumber ? cycleDayNumber : ' '}
|
|
||||||
</Text>)
|
|
||||||
const dateLabel = (
|
|
||||||
<Text style = {[label.date, boldDateLabel]}>
|
|
||||||
{shortDate}
|
|
||||||
</Text>
|
|
||||||
)
|
|
||||||
|
|
||||||
const column = (
|
|
||||||
<G>
|
|
||||||
<Shape
|
|
||||||
stroke={styles.column.stroke.color}
|
|
||||||
strokeWidth={styles.column.stroke.width}
|
|
||||||
d={new Path().lineTo(0, chartHeight)}
|
|
||||||
/>
|
|
||||||
{ columnElements }
|
|
||||||
</G>
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={() => this.onDaySelect(dateString)}
|
onPress={() => this.onDaySelect(dateString)}
|
||||||
@@ -240,21 +82,25 @@ class DayColumn extends Component {
|
|||||||
symptom={symptom}
|
symptom={symptom}
|
||||||
symptomValue={hasSymptomData && this.data[symptom]}
|
symptomValue={hasSymptomData && this.data[symptom]}
|
||||||
isSymptomDataComplete={
|
isSymptomDataComplete={
|
||||||
hasSymptomData && this.isSymptomDataComplete(symptom)
|
hasSymptomData && isSymptomDataComplete(symptom, dateString)
|
||||||
}
|
}
|
||||||
height={this.props.symptomHeight}
|
height={this.props.symptomHeight}
|
||||||
/>)
|
/>)
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Surface width={config.columnWidth} height={columnHeight}>
|
<TemperatureColumn
|
||||||
{column}
|
horizontalLinePosition={this.fhmAndLtl.drawLtlAt}
|
||||||
</Surface>
|
isVerticalLine={this.fhmAndLtl.drawFhmLine}
|
||||||
|
data={this.data && this.data.temperature}
|
||||||
|
columnHeight={columnHeight}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<CycleDayLabel
|
||||||
|
height={xAxisHeight}
|
||||||
|
date={dateString}
|
||||||
|
/>
|
||||||
|
|
||||||
<View style={{height: xAxisHeight}}>
|
|
||||||
{cycleDayLabel}
|
|
||||||
{dateLabel}
|
|
||||||
</View>
|
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -270,28 +116,3 @@ export default connect(
|
|||||||
null,
|
null,
|
||||||
mapDispatchToProps,
|
mapDispatchToProps,
|
||||||
)(DayColumn)
|
)(DayColumn)
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ const orangeColor = '#bc6642'
|
|||||||
const mintColor = '#6ca299'
|
const mintColor = '#6ca299'
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
|
container: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
curve: {
|
curve: {
|
||||||
stroke: colorTemperature,
|
stroke: colorTemperature,
|
||||||
strokeWidth: lineWidth,
|
strokeWidth: lineWidth,
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
|
import { Surface , Path } from 'react-native/Libraries/ART/ReactNativeART'
|
||||||
|
|
||||||
|
import ChartLine from './chart-line'
|
||||||
|
import DotAndLine from './dot-and-line'
|
||||||
|
|
||||||
|
import styles from './styles'
|
||||||
|
import config from '../../config'
|
||||||
|
|
||||||
|
const TemperatureColumn = ({
|
||||||
|
horizontalLinePosition,
|
||||||
|
isVerticalLine,
|
||||||
|
data,
|
||||||
|
columnHeight
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
const x = styles.nfpLine.strokeWidth / 2
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Surface width={config.columnWidth} height={columnHeight}>
|
||||||
|
|
||||||
|
<ChartLine
|
||||||
|
path={new Path().lineTo(0, columnHeight)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{horizontalLinePosition && <ChartLine
|
||||||
|
path={new Path()
|
||||||
|
.moveTo(0, horizontalLinePosition)
|
||||||
|
.lineTo(config.columnWidth, horizontalLinePosition)
|
||||||
|
}
|
||||||
|
isNfpLine={true}
|
||||||
|
key='ltl'
|
||||||
|
/>}
|
||||||
|
|
||||||
|
{isVerticalLine && <ChartLine
|
||||||
|
path={new Path().moveTo(x, x).lineTo(x, columnHeight)}
|
||||||
|
isNfpLine={true}
|
||||||
|
key='fhm'
|
||||||
|
/>}
|
||||||
|
|
||||||
|
{data && data.y && <DotAndLine
|
||||||
|
y={data.y}
|
||||||
|
exclude={data.temperatureExclude}
|
||||||
|
rightY={data.rightY}
|
||||||
|
rightTemperatureExclude={data.rightTemperatureExclude}
|
||||||
|
leftY={data.leftY}
|
||||||
|
leftTemperatureExclude={data.leftTemperatureExclude}
|
||||||
|
key='dotandline'
|
||||||
|
/>}
|
||||||
|
|
||||||
|
</Surface>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
TemperatureColumn.propTypes = {
|
||||||
|
horizontalLinePosition: PropTypes.number,
|
||||||
|
isVerticalLine: PropTypes.bool,
|
||||||
|
data: PropTypes.object,
|
||||||
|
columnHeight: PropTypes.number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TemperatureColumn
|
||||||
@@ -1,6 +1,12 @@
|
|||||||
|
import { LocalDate } from 'js-joda'
|
||||||
|
|
||||||
import { scaleObservable, unitObservable } from '../../local-storage'
|
import { scaleObservable, unitObservable } from '../../local-storage'
|
||||||
|
import { getCycleDay, getAmountOfCycleDays } from '../../db'
|
||||||
|
|
||||||
import config from '../../config'
|
import config from '../../config'
|
||||||
|
|
||||||
|
//YAxis helpers
|
||||||
|
|
||||||
export function normalizeToScale(temp, columnHeight) {
|
export function normalizeToScale(temp, columnHeight) {
|
||||||
const scale = scaleObservable.value
|
const scale = scaleObservable.value
|
||||||
const valueRelativeToScale = (scale.max - temp) / (scale.max - scale.min)
|
const valueRelativeToScale = (scale.max - temp) / (scale.max - scale.min)
|
||||||
@@ -65,3 +71,130 @@ export function getTickList(columnHeight) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//DayColumn helpers
|
||||||
|
|
||||||
|
export function isSymptomDataComplete(symptom, dateString) {
|
||||||
|
const cycleDayData = getCycleDay(dateString)
|
||||||
|
const symptomData = cycleDayData[symptom]
|
||||||
|
|
||||||
|
const dataCompletenessCheck = {
|
||||||
|
'cervix': () => {
|
||||||
|
const { opening, firmness } = symptomData
|
||||||
|
return (opening !== null) && (firmness !== null)
|
||||||
|
},
|
||||||
|
'mucus': () => {
|
||||||
|
const { feeling, texture } = symptomData
|
||||||
|
return (feeling !== null) && (texture !== null)
|
||||||
|
},
|
||||||
|
'default': () => {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (dataCompletenessCheck[symptom] || dataCompletenessCheck['default'])()
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTemperatureProps(symptomData, columnHeight, dateString) {
|
||||||
|
const extractedData = {}
|
||||||
|
const { value, exclude } = symptomData
|
||||||
|
const neighborTemperatureGraphPoints =
|
||||||
|
getInfoForNeighborColumns(dateString, columnHeight)
|
||||||
|
|
||||||
|
for (const key in neighborTemperatureGraphPoints) {
|
||||||
|
extractedData[key] = neighborTemperatureGraphPoints[key]
|
||||||
|
}
|
||||||
|
return Object.assign({
|
||||||
|
value,
|
||||||
|
y: normalizeToScale(value, columnHeight),
|
||||||
|
temperatureExclude: exclude,
|
||||||
|
}, extractedData)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const symptomColorMethods = {
|
||||||
|
'mucus': (symptomData) => {
|
||||||
|
const { feeling, texture } = symptomData
|
||||||
|
const colorIndex = feeling + texture
|
||||||
|
return colorIndex
|
||||||
|
},
|
||||||
|
'cervix': (symptomData) => {
|
||||||
|
const { opening, firmness } = symptomData
|
||||||
|
const isDataComplete = opening !== null && firmness !== null
|
||||||
|
const isClosedAndHard =
|
||||||
|
isDataComplete &&
|
||||||
|
(opening === 0 && firmness === 0)
|
||||||
|
const colorIndex = isClosedAndHard ? 0 : 2
|
||||||
|
return colorIndex
|
||||||
|
},
|
||||||
|
'sex': (symptomData) => {
|
||||||
|
const { solo, partner } = symptomData
|
||||||
|
const colorIndex = (solo !== null && partner !== null) ?
|
||||||
|
(solo + 2 * partner - 1) : 0
|
||||||
|
return colorIndex
|
||||||
|
},
|
||||||
|
'bleeding': (symptomData) => {
|
||||||
|
const { value } = symptomData
|
||||||
|
const colorIndex = value
|
||||||
|
return colorIndex
|
||||||
|
},
|
||||||
|
'default': () => { // desire, pain, mood, note
|
||||||
|
const colorIndex = 0
|
||||||
|
return colorIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chart helpers
|
||||||
|
|
||||||
|
export function 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) {
|
||||||
|
amountOfCycleDays = 30
|
||||||
|
} else {
|
||||||
|
// we don't want the chart to end abruptly before the first data day
|
||||||
|
amountOfCycleDays += 5
|
||||||
|
}
|
||||||
|
const localDates = getTodayAndPreviousDays(amountOfCycleDays)
|
||||||
|
return localDates.map(localDate => localDate.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTodayAndPreviousDays(n) {
|
||||||
|
const today = LocalDate.now()
|
||||||
|
const targetDate = today.minusDays(n)
|
||||||
|
|
||||||
|
function getDaysInRange(currDate, range) {
|
||||||
|
if (currDate.isBefore(targetDate)) {
|
||||||
|
return range
|
||||||
|
} else {
|
||||||
|
range.push(currDate)
|
||||||
|
const next = currDate.minusDays(1)
|
||||||
|
return getDaysInRange(next, range)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return getDaysInRange(today, [])
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user