Newer
Older
import { View, FlatList, ActivityIndicator } from 'react-native'
import range from 'date-range'
import { LocalDate } from 'js-joda'
import { makeYAxisLabels, makeHorizontalGrid } from './y-axis'
import nfpLines from './nfp-lines'
import { getCycleDaysSortedByDate, getAmountOfCycleDays } from '../../db'
import { scaleObservable } from '../../local-storage'
import BleedingIcon from '../../assets/bleeding'
import CervixIcon from '../../assets/cervix'
import DesireIcon from '../../assets/desire'
import MucusIcon from '../../assets/mucus'
import NoteIcon from '../../assets/note'
import PainIcon from '../../assets/pain'
import SexIcon from '../../assets/sex'
import CycleDayIcon from '../../assets/home-circle'
import nothingChanged from '../../db/db-unchanged'
constructor(props) {
super(props)
this.cycleDaysSortedByDate = getCycleDaysSortedByDate()
this.getFhmAndLtlInfo = nfpLines()
}
renderColumn = ({ item, index }) => {
return (
<DayColumn
dateString={item}
index={index}
navigate={this.props.navigate}
symptomHeight={this.symptomHeight}
columnHeight={this.columnHeight}
chartHeight={this.state.chartHeight}
symptomRowSymptoms={this.symptomRowSymptoms}
chartSymptoms={this.chartSymptoms}
getFhmAndLtlInfo={this.getFhmAndLtlInfo}
/>
)
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',
'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 = this.makeColumnInfo(nfpLines(), this.chartSymptoms)
this.setState({
columns: columnData,
chartHeight: height
})
}
reCalculateChartInfo()
this.updateListeners(reCalculateChartInfo)
}
updateListeners(dataUpdateHandler) {
// remove existing listeners
if(this.handleDbChange) {
this.cycleDaysSortedByDate.removeListener(this.handleDbChange)
}
if (this.removeObvListener) this.removeObvListener()
this.handleDbChange = (_, changes) => {
if (nothingChanged(changes)) return
dataUpdateHandler()
this.cycleDaysSortedByDate.addListener(this.handleDbChange)
this.removeObvListener = scaleObservable(dataUpdateHandler, false)
this.cycleDaysSortedByDate.removeListener(this.handleDbChange)
}
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 jsDates = getTodayAndPreviousDays(amountOfCycleDays)
return jsDates.map(jsDate => {
return LocalDate.of(
jsDate.getFullYear(),
jsDate.getMonth() + 1,
jsDate.getDate()
).toString()
})
}
<View
onLayout={this.onLayout}
style={{ flexDirection: 'row', flex: 1 }}
>
{!this.state.chartLoaded &&
<View style={{width: '100%', justifyContent: 'center', alignItems: 'center'}}>
</View>
}
{this.state.chartHeight && this.state.chartLoaded &&
<View style={[styles.yAxis, {height: this.symptomRowHeight}]}>
{this.symptomRowSymptoms.map(symptomName => {
return <View
style={{ alignItems: 'center', justifyContent: 'center' }}
key={symptomName}
width={styles.yAxis.width}
height={this.symptomRowHeight /
this.symptomRowSymptoms.length}
width={styles.yAxis.width * 0.8}
height={this.symptomRowHeight /
this.symptomRowSymptoms.length * 0.8}
viewBox={symptomIcons[symptomName].viewBox}
>
<G fill={symptomIcons[symptomName].color}>
{symptomIcons[symptomName].icon}
</G>
</Svg>
<View style={[styles.yAxis, {height: this.columnHeight}]}>
{makeYAxisLabels(this.columnHeight)}
</View>
<View style={[styles.yAxis, { alignItems: 'center', justifyContent: 'center' }]}>
<Svg
width={styles.yAxis.width * 0.7}
height={styles.yAxis.width * 0.7}
>
<G fill="none" stroke="#1E0B7A" strokeWidth="10">
<CycleDayIcon/>
</G>
</Svg>
<AppText style={[
styles.column.label.date,
styles.yAxisLabels.dateLabel
]}>
{this.state.chartHeight && this.state.chartLoaded &&
makeHorizontalGrid(this.columnHeight, this.symptomRowHeight)
}
{this.state.chartHeight &&
<FlatList
horizontal={true}
inverted={true}
showsHorizontalScrollIndicator={false}
data={this.state.columns}
renderItem={this.renderColumn}
keyExtractor={item => item}
windowSize={30}
onLayout={() => this.setState({chartLoaded: true})}
onEndReached={() => this.setState({end: true})}
ListFooterComponent={<LoadingMoreView end={this.state.end}/>}
updateCellsBatchingPeriod={800}
function LoadingMoreView(props) {
return (
<View style={styles.loadingMore}>
{!props.end &&
<ActivityIndicator size={'large'} color={'white'}/>
}
</View>
)
}
function getTodayAndPreviousDays(n) {
today.setHours(0)
today.setMinutes(0)
today.setSeconds(0)
today.setMilliseconds(0)
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
const symptomIcons = {
'bleeding': {
'viewBox': '10 10 320 400',
'color': styles.iconShades.bleeding[3],
'icon': <BleedingIcon strokeWidth={'5'}/>
},
'mucus': {
'viewBox': '10 10 320 400',
'color': styles.iconShades.mucus[4],
'icon': <MucusIcon/>
},
'cervix': {
'viewBox': '10 10 320 440',
'color': styles.iconShades.cervix[3],
'icon': <CervixIcon/>
},
'desire': {
'viewBox': '10 10 320 380',
'color': styles.iconShades.desire[2],
'icon': <DesireIcon/>
},
'sex': {
'viewBox': '10 10 320 400',
'color': styles.iconShades.sex[2],
'icon': <SexIcon/>
},
'pain': {
'viewBox': '10 10 300 400',
'color': styles.iconShades.pain[0],
'icon': <PainIcon/>
},
'note': {
'viewBox': '10 10 270 400',
'color': styles.iconShades.note[0],
'icon': <NoteIcon/>
},