import React, { Component } from 'react' import PropTypes from 'prop-types' import { ActivityIndicator, Dimensions, FlatList, PixelRatio, StyleSheet, View, } from 'react-native' import AppLoadingView from '../common/app-loading' import AppPage from '../common/app-page' import AppText from '../common/app-text' import DayColumn from './day-column' import HorizontalGrid from './horizontal-grid' import NoData from './no-data' import Tutorial from './tutorial' import YAxis from './y-axis' import { connect } from 'react-redux' import { navigate } from '../../slices/navigation' import { getCycleDaysSortedByDate } from '../../db' import nothingChanged from '../../db/db-unchanged' import { getChartFlag, scaleObservable, setChartFlag, } from '../../local-storage' import { makeColumnInfo, nfpLines } from '../helpers/chart' import { CHART_COLUMN_WIDTH, CHART_GRID_LINE_HORIZONTAL_WIDTH, CHART_SYMPTOM_HEIGHT_RATIO, CHART_XAXIS_HEIGHT_RATIO, SYMPTOMS, } from '../../config' import { shared } from '../../i18n/en/labels' import { Colors, Spacing } from '../../styles' class CycleChart extends Component { static propTypes = { navigate: PropTypes.func, end: PropTypes.bool, } constructor(props) { super(props) this.state = {} this.cycleDaysSortedByDate = getCycleDaysSortedByDate() this.getFhmAndLtlInfo = nfpLines() this.shouldShowTemperatureColumn = false this.checkShouldShowHint() this.prepareSymptomData() } componentWillUnmount() { this.cycleDaysSortedByDate.removeListener(this.handleDbChange) this.removeObvListener() } checkShouldShowHint = async () => { const flag = await getChartFlag() const shouldShowHint = flag === 'true' ? true : false this.setState({ shouldShowHint }) } setShouldShowHint = async () => { await setChartFlag() this.setState({ shouldShowHint: false }) } onLayout = () => { if (this.state.chartHeight) return false this.reCalculateChartInfo() this.updateListeners(this.reCalculateChartInfo) } prepareSymptomData = () => { this.symptomRowSymptoms = SYMPTOMS.filter((symptomName) => { return this.cycleDaysSortedByDate.some((cycleDay) => { return symptomName !== 'temperature' && cycleDay[symptomName] }) }) this.chartSymptoms = [...this.symptomRowSymptoms] if (this.cycleDaysSortedByDate.some((day) => day.temperature)) { this.chartSymptoms.push('temperature') this.shouldShowTemperatureColumn = true } } renderColumn = ({ item, index }) => { return ( ) } reCalculateChartInfo = () => { const { width, height } = Dimensions.get('window') this.xAxisHeight = height * 0.7 * CHART_XAXIS_HEIGHT_RATIO const remainingHeight = height * 0.7 - this.xAxisHeight this.symptomHeight = PixelRatio.roundToNearestPixel( remainingHeight * CHART_SYMPTOM_HEIGHT_RATIO ) this.symptomRowHeight = PixelRatio.roundToNearestPixel( this.symptomRowSymptoms.length * this.symptomHeight ) + CHART_GRID_LINE_HORIZONTAL_WIDTH this.columnHeight = remainingHeight - this.symptomRowHeight const chartHeight = this.shouldShowTemperatureColumn ? height * 0.7 : this.symptomRowHeight + this.xAxisHeight const numberOfColumnsToRender = Math.round(width / CHART_COLUMN_WIDTH) const columns = makeColumnInfo() this.setState({ columns, chartHeight, numberOfColumnsToRender }) } 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) } render() { const { chartHeight, chartLoaded, shouldShowHint, numberOfColumnsToRender, } = this.state const hasDataToDisplay = this.chartSymptoms.length > 0 return ( {!hasDataToDisplay && } {hasDataToDisplay && !chartHeight && !chartLoaded && } {shouldShowHint && chartLoaded && ( )} {hasDataToDisplay && chartLoaded && !this.shouldShowTemperatureColumn && ( {shared.noTemperatureWarning} )} {hasDataToDisplay && ( {chartHeight && chartLoaded && ( )} {chartHeight && ( item} initialNumToRender={numberOfColumnsToRender} windowSize={30} onLayout={() => this.setState({ chartLoaded: true })} onEndReached={() => this.setState({ end: true })} ListFooterComponent={} updateCellsBatchingPeriod={800} contentContainerStyle={{ height: chartHeight }} /> )} {chartHeight && chartLoaded && ( {this.shouldShowTemperatureColumn && ( )} )} )} ) } } function LoadingMoreView({ end }) { return ( {!end && } ) } LoadingMoreView.propTypes = { end: PropTypes.bool, } const styles = StyleSheet.create({ chartArea: { flexDirection: 'row', }, chartContainer: { flexDirection: 'column', }, loadingContainer: { height: '100%', backgroundColor: Colors.turquoiseLight, justifyContent: 'center', }, page: { marginVertical: Spacing.small, }, pageContainer: { paddingHorizontal: Spacing.base, }, warning: { padding: Spacing.large, }, }) const mapDispatchToProps = (dispatch) => { return { navigate: (page) => dispatch(navigate(page)), } } export default connect(null, mapDispatchToProps)(CycleChart)