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)