import React, { Component } from 'react' import { View, FlatList, ActivityIndicator } from 'react-native' import range from 'date-range' import { LocalDate } from 'js-joda' import { Surface, Group as G } from 'react-native/Libraries/ART/ReactNativeART' import { makeYAxisLabels, makeHorizontalGrid } from './y-axis' import nfpLines from './nfp-lines' import DayColumn from './day-column' import { getCycleDaysSortedByDate, getAmountOfCycleDays } from '../../db' import styles from './styles' import { scaleObservable } from '../../local-storage' import config from '../../config' import AppText from '../app-text' import { shared as labels } from '../labels' 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' export default class CycleChart extends Component { constructor(props) { super(props) this.state = {} this.cycleDaysSortedByDate = getCycleDaysSortedByDate() this.getFhmAndLtlInfo = nfpLines() } renderColumn = ({ item, index }) => { return ( ) } 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) } componentWillUnmount() { this.cycleDaysSortedByDate.removeListener(this.handleDbChange) 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 jsDates = getTodayAndPreviousDays(amountOfCycleDays) return jsDates.map(jsDate => { return LocalDate.of( jsDate.getFullYear(), jsDate.getMonth() + 1, jsDate.getDate() ).toString() }) } render() { return ( {!this.state.chartLoaded && {labels.loading} } {this.state.chartHeight && this.state.chartLoaded && {this.symptomRowSymptoms.map(symptomName => { return {symptomIcons[symptomName].icon} })} {makeYAxisLabels(this.columnHeight)} {labels.date} } {this.state.chartHeight && this.state.chartLoaded && makeHorizontalGrid(this.columnHeight, this.symptomRowHeight) } {this.state.chartHeight && item} initialNumToRender={15} windowSize={30} onLayout={() => this.setState({chartLoaded: true})} onEndReached={() => this.setState({end: true})} ListFooterComponent={} updateCellsBatchingPeriod={800} /> } ) } } function LoadingMoreView(props) { return ( {!props.end && } ) } function getTodayAndPreviousDays(n) { const today = new Date() today.setHours(0) today.setMinutes(0) today.setSeconds(0) today.setMilliseconds(0) const earlierDate = new Date(today - (range.DAY * n)) return range(earlierDate, today).reverse() } const symptomIcons = { 'bleeding': { 'viewBox': '10 10 320 400', 'color': styles.iconShades.bleeding[3], 'icon': }, 'mucus': { 'viewBox': '10 10 320 400', 'color': styles.iconShades.mucus[4], 'icon': }, 'cervix': { 'viewBox': '10 10 320 440', 'color': styles.iconShades.cervix[3], 'icon': }, 'desire': { 'viewBox': '10 10 320 380', 'color': styles.iconShades.desire[2], 'icon': }, 'sex': { 'viewBox': '10 10 320 400', 'color': styles.iconShades.sex[2], 'icon': }, 'pain': { 'viewBox': '10 10 300 400', 'color': styles.iconShades.pain[0], 'icon': }, 'note': { 'viewBox': '10 10 270 400', 'color': styles.iconShades.note[0], 'icon': }, }