Merge branch 'chore/make-function-components-5' into 'main'
Chore/make function components 5 See merge request bloodyhealth/drip!522
This commit is contained in:
+69
-185
@@ -1,32 +1,20 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import {
|
import { Dimensions, PixelRatio, StyleSheet, View } from 'react-native'
|
||||||
ActivityIndicator,
|
|
||||||
Dimensions,
|
|
||||||
FlatList,
|
|
||||||
PixelRatio,
|
|
||||||
StyleSheet,
|
|
||||||
View,
|
|
||||||
} from 'react-native'
|
|
||||||
|
|
||||||
import AppLoadingView from '../common/app-loading'
|
|
||||||
import AppPage from '../common/app-page'
|
import AppPage from '../common/app-page'
|
||||||
import AppText from '../common/app-text'
|
|
||||||
|
|
||||||
import DayColumn from './day-column'
|
import DayColumn from './day-column'
|
||||||
import HorizontalGrid from './horizontal-grid'
|
import HorizontalGrid from './horizontal-grid'
|
||||||
|
import MainGrid from './main-grid'
|
||||||
import NoData from './no-data'
|
import NoData from './no-data'
|
||||||
|
import NoTemperature from './no-temperature'
|
||||||
import Tutorial from './tutorial'
|
import Tutorial from './tutorial'
|
||||||
import YAxis from './y-axis'
|
import YAxis from './y-axis'
|
||||||
|
|
||||||
import { getCycleDaysSortedByDate } from '../../db'
|
import { getCycleDaysSortedByDate } from '../../db'
|
||||||
import nothingChanged from '../../db/db-unchanged'
|
import { getChartFlag, setChartFlag } from '../../local-storage'
|
||||||
import {
|
import { makeColumnInfo } from '../helpers/chart'
|
||||||
getChartFlag,
|
|
||||||
scaleObservable,
|
|
||||||
setChartFlag,
|
|
||||||
} from '../../local-storage'
|
|
||||||
import { makeColumnInfo, nfpLines } from '../helpers/chart'
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CHART_COLUMN_WIDTH,
|
CHART_COLUMN_WIDTH,
|
||||||
@@ -35,204 +23,108 @@ import {
|
|||||||
CHART_XAXIS_HEIGHT_RATIO,
|
CHART_XAXIS_HEIGHT_RATIO,
|
||||||
SYMPTOMS,
|
SYMPTOMS,
|
||||||
} from '../../config'
|
} from '../../config'
|
||||||
import { shared } from '../../i18n/en/labels'
|
import { Spacing } from '../../styles'
|
||||||
import { Colors, Spacing } from '../../styles'
|
|
||||||
|
|
||||||
class CycleChart extends Component {
|
const getSymptomsFromCycleDays = (cycleDays) =>
|
||||||
static propTypes = {
|
SYMPTOMS.filter((symptom) => cycleDays.some((cycleDay) => cycleDay[symptom]))
|
||||||
navigate: PropTypes.func,
|
|
||||||
end: PropTypes.bool,
|
|
||||||
setDate: PropTypes.func,
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(props) {
|
const CycleChart = ({ navigate, setDate }) => {
|
||||||
super(props)
|
const [shouldShowHint, setShouldShowHint] = useState(true)
|
||||||
|
|
||||||
this.state = {}
|
useEffect(async () => {
|
||||||
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 flag = await getChartFlag()
|
||||||
const shouldShowHint = flag === 'true' ? true : false
|
setShouldShowHint(flag === 'true')
|
||||||
this.setState({ shouldShowHint })
|
}, [])
|
||||||
|
|
||||||
|
const hideHint = () => {
|
||||||
|
setShouldShowHint(false)
|
||||||
|
setChartFlag()
|
||||||
}
|
}
|
||||||
|
|
||||||
setShouldShowHint = async () => {
|
const cycleDaysSortedByDate = getCycleDaysSortedByDate()
|
||||||
await setChartFlag()
|
|
||||||
this.setState({ shouldShowHint: false })
|
|
||||||
}
|
|
||||||
|
|
||||||
onLayout = () => {
|
const chartSymptoms = getSymptomsFromCycleDays(cycleDaysSortedByDate)
|
||||||
if (this.state.chartHeight) return false
|
const symptomRowSymptoms = chartSymptoms.filter(
|
||||||
|
(symptom) => symptom !== 'temperature'
|
||||||
|
)
|
||||||
|
|
||||||
this.reCalculateChartInfo()
|
const shouldShowTemperatureColumn = chartSymptoms.indexOf('temperature') > -1
|
||||||
this.updateListeners(this.reCalculateChartInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
prepareSymptomData = () => {
|
const { width, height } = Dimensions.get('window')
|
||||||
this.symptomRowSymptoms = SYMPTOMS.filter((symptomName) => {
|
const numberOfColumnsToRender = Math.round(width / CHART_COLUMN_WIDTH)
|
||||||
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 }) => {
|
const xAxisHeight = height * 0.7 * CHART_XAXIS_HEIGHT_RATIO
|
||||||
|
const remainingHeight = height * 0.7 - xAxisHeight
|
||||||
|
const symptomHeight = PixelRatio.roundToNearestPixel(
|
||||||
|
remainingHeight * CHART_SYMPTOM_HEIGHT_RATIO
|
||||||
|
)
|
||||||
|
const symptomRowHeight =
|
||||||
|
PixelRatio.roundToNearestPixel(symptomRowSymptoms.length * symptomHeight) +
|
||||||
|
CHART_GRID_LINE_HORIZONTAL_WIDTH
|
||||||
|
const columnHeight = remainingHeight - symptomRowHeight
|
||||||
|
|
||||||
|
const chartHeight = shouldShowTemperatureColumn
|
||||||
|
? height * 0.7
|
||||||
|
: symptomRowHeight + xAxisHeight
|
||||||
|
|
||||||
|
const columns = makeColumnInfo()
|
||||||
|
|
||||||
|
const renderColumn = ({ item }) => {
|
||||||
return (
|
return (
|
||||||
<DayColumn
|
<DayColumn
|
||||||
setDate={this.props.setDate}
|
setDate={setDate}
|
||||||
dateString={item}
|
dateString={item}
|
||||||
index={index}
|
navigate={navigate}
|
||||||
navigate={this.props.navigate}
|
symptomHeight={symptomHeight}
|
||||||
symptomHeight={this.symptomHeight}
|
columnHeight={columnHeight}
|
||||||
columnHeight={this.columnHeight}
|
symptomRowSymptoms={symptomRowSymptoms}
|
||||||
symptomRowSymptoms={this.symptomRowSymptoms}
|
chartSymptoms={chartSymptoms}
|
||||||
chartSymptoms={this.chartSymptoms}
|
shouldShowTemperatureColumn={shouldShowTemperatureColumn}
|
||||||
shouldShowTemperatureColumn={this.shouldShowTemperatureColumn}
|
xAxisHeight={xAxisHeight}
|
||||||
getFhmAndLtlInfo={this.getFhmAndLtlInfo}
|
|
||||||
xAxisHeight={this.xAxisHeight}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
reCalculateChartInfo = () => {
|
const hasDataToDisplay = chartSymptoms.length > 0
|
||||||
const { width, height } = Dimensions.get('window')
|
|
||||||
|
|
||||||
this.xAxisHeight = height * 0.7 * CHART_XAXIS_HEIGHT_RATIO
|
if (!hasDataToDisplay) {
|
||||||
const remainingHeight = height * 0.7 - this.xAxisHeight
|
return <NoData navigate={navigate} />
|
||||||
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 (
|
return (
|
||||||
<AppPage
|
<AppPage
|
||||||
contentContainerStyle={styles.pageContainer}
|
contentContainerStyle={styles.pageContainer}
|
||||||
onLayout={this.onLayout}
|
|
||||||
scrollViewStyle={styles.page}
|
scrollViewStyle={styles.page}
|
||||||
>
|
>
|
||||||
{!hasDataToDisplay && <NoData navigate={this.props.navigate} />}
|
|
||||||
{hasDataToDisplay && !chartHeight && !chartLoaded && <AppLoadingView />}
|
|
||||||
<View style={styles.chartContainer}>
|
<View style={styles.chartContainer}>
|
||||||
{shouldShowHint && chartLoaded && (
|
{shouldShowHint && <Tutorial onClose={hideHint} />}
|
||||||
<Tutorial onClose={this.setShouldShowHint} />
|
{!shouldShowTemperatureColumn && <NoTemperature />}
|
||||||
)}
|
|
||||||
{hasDataToDisplay &&
|
|
||||||
chartLoaded &&
|
|
||||||
!this.shouldShowTemperatureColumn && (
|
|
||||||
<View style={styles.centerItem}>
|
|
||||||
<AppText style={styles.warning}>
|
|
||||||
{shared.noTemperatureWarning}
|
|
||||||
</AppText>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
{hasDataToDisplay && (
|
|
||||||
<View style={styles.chartArea}>
|
<View style={styles.chartArea}>
|
||||||
{chartHeight && chartLoaded && (
|
|
||||||
<YAxis
|
<YAxis
|
||||||
height={this.columnHeight}
|
height={columnHeight}
|
||||||
symptomsToDisplay={this.symptomRowSymptoms}
|
symptomsToDisplay={symptomRowSymptoms}
|
||||||
symptomsSectionHeight={this.symptomRowHeight}
|
symptomsSectionHeight={symptomRowHeight}
|
||||||
shouldShowTemperatureColumn={this.shouldShowTemperatureColumn}
|
shouldShowTemperatureColumn={shouldShowTemperatureColumn}
|
||||||
xAxisHeight={this.xAxisHeight}
|
xAxisHeight={xAxisHeight}
|
||||||
/>
|
/>
|
||||||
)}
|
<MainGrid
|
||||||
|
data={columns}
|
||||||
{chartHeight && (
|
renderItem={renderColumn}
|
||||||
<FlatList
|
|
||||||
horizontal={true}
|
|
||||||
inverted={true}
|
|
||||||
showsHorizontalScrollIndicator={false}
|
|
||||||
data={this.state.columns}
|
|
||||||
renderItem={this.renderColumn}
|
|
||||||
keyExtractor={(item) => item}
|
|
||||||
initialNumToRender={numberOfColumnsToRender}
|
initialNumToRender={numberOfColumnsToRender}
|
||||||
windowSize={30}
|
|
||||||
onLayout={() => this.setState({ chartLoaded: true })}
|
|
||||||
onEndReached={() => this.setState({ end: true })}
|
|
||||||
ListFooterComponent={<LoadingMoreView end={this.state.end} />}
|
|
||||||
updateCellsBatchingPeriod={800}
|
|
||||||
contentContainerStyle={{ height: chartHeight }}
|
contentContainerStyle={{ height: chartHeight }}
|
||||||
/>
|
/>
|
||||||
)}
|
{shouldShowTemperatureColumn && (
|
||||||
{chartHeight && chartLoaded && (
|
<HorizontalGrid height={columnHeight} />
|
||||||
<React.Fragment>
|
|
||||||
{this.shouldShowTemperatureColumn && (
|
|
||||||
<HorizontalGrid height={this.columnHeight} />
|
|
||||||
)}
|
|
||||||
</React.Fragment>
|
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
</AppPage>
|
</AppPage>
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function LoadingMoreView({ end }) {
|
CycleChart.propTypes = {
|
||||||
return (
|
navigate: PropTypes.func,
|
||||||
<View style={styles.loadingContainer}>
|
setDate: PropTypes.func,
|
||||||
{!end && <ActivityIndicator size={'large'} color={Colors.orange} />}
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadingMoreView.propTypes = {
|
|
||||||
end: PropTypes.bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
@@ -242,20 +134,12 @@ const styles = StyleSheet.create({
|
|||||||
chartContainer: {
|
chartContainer: {
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
},
|
},
|
||||||
loadingContainer: {
|
|
||||||
height: '100%',
|
|
||||||
backgroundColor: Colors.turquoiseLight,
|
|
||||||
justifyContent: 'center',
|
|
||||||
},
|
|
||||||
page: {
|
page: {
|
||||||
marginVertical: Spacing.small,
|
marginVertical: Spacing.small,
|
||||||
},
|
},
|
||||||
pageContainer: {
|
pageContainer: {
|
||||||
paddingHorizontal: Spacing.base,
|
paddingHorizontal: Spacing.base,
|
||||||
},
|
},
|
||||||
warning: {
|
|
||||||
padding: Spacing.large,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export default CycleChart
|
export default CycleChart
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ import {
|
|||||||
symptomColorMethods,
|
symptomColorMethods,
|
||||||
getTemperatureProps,
|
getTemperatureProps,
|
||||||
isSymptomDataComplete,
|
isSymptomDataComplete,
|
||||||
|
nfpLines,
|
||||||
} from '../helpers/chart'
|
} from '../helpers/chart'
|
||||||
|
|
||||||
const DayColumn = ({
|
const DayColumn = ({
|
||||||
dateString,
|
dateString,
|
||||||
chartSymptoms,
|
chartSymptoms,
|
||||||
columnHeight,
|
columnHeight,
|
||||||
getFhmAndLtlInfo,
|
|
||||||
setDate,
|
setDate,
|
||||||
navigate,
|
navigate,
|
||||||
shouldShowTemperatureColumn,
|
shouldShowTemperatureColumn,
|
||||||
@@ -54,7 +54,7 @@ const DayColumn = ({
|
|||||||
}, data)
|
}, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
const fhmAndLtl = getFhmAndLtlInfo(
|
const fhmAndLtl = nfpLines()(
|
||||||
dateString,
|
dateString,
|
||||||
data.temperature ? data.temperature.value : null,
|
data.temperature ? data.temperature.value : null,
|
||||||
columnHeight
|
columnHeight
|
||||||
@@ -104,7 +104,6 @@ DayColumn.propTypes = {
|
|||||||
dateString: PropTypes.string.isRequired,
|
dateString: PropTypes.string.isRequired,
|
||||||
chartSymptoms: PropTypes.array,
|
chartSymptoms: PropTypes.array,
|
||||||
columnHeight: PropTypes.number.isRequired,
|
columnHeight: PropTypes.number.isRequired,
|
||||||
getFhmAndLtlInfo: PropTypes.func.isRequired,
|
|
||||||
navigate: PropTypes.func.isRequired,
|
navigate: PropTypes.func.isRequired,
|
||||||
setDate: PropTypes.func.isRequired,
|
setDate: PropTypes.func.isRequired,
|
||||||
shouldShowTemperatureColumn: PropTypes.bool,
|
shouldShowTemperatureColumn: PropTypes.bool,
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { ActivityIndicator, StyleSheet, View } from 'react-native'
|
||||||
|
|
||||||
|
import { Colors } from '../../styles'
|
||||||
|
|
||||||
|
function LoadingMoreView({ end }) {
|
||||||
|
return (
|
||||||
|
<View style={styles.loadingContainer}>
|
||||||
|
{!end && <ActivityIndicator size={'large'} color={Colors.orange} />}
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadingMoreView.propTypes = {
|
||||||
|
end: PropTypes.bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
loadingContainer: {
|
||||||
|
height: '100%',
|
||||||
|
backgroundColor: Colors.turquoiseLight,
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default LoadingMoreView
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import React, { useState } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { FlatList } from 'react-native'
|
||||||
|
|
||||||
|
import LoadingMoreView from './loading-more'
|
||||||
|
|
||||||
|
const MainGrid = (props) => {
|
||||||
|
const [endReached, setEndReached] = useState(false)
|
||||||
|
return (
|
||||||
|
<FlatList
|
||||||
|
horizontal={true}
|
||||||
|
inverted={true}
|
||||||
|
showsHorizontalScrollIndicator={false}
|
||||||
|
keyExtractor={(item) => item}
|
||||||
|
windowSize={30}
|
||||||
|
onEndReached={() => setEndReached(true)}
|
||||||
|
ListFooterComponent={<LoadingMoreView end={endReached} />}
|
||||||
|
updateCellsBatchingPeriod={800}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
MainGrid.propTypes = {
|
||||||
|
height: PropTypes.number,
|
||||||
|
data: PropTypes.array,
|
||||||
|
renderItem: PropTypes.func,
|
||||||
|
initialNumToRender: PropTypes.number,
|
||||||
|
contentContainerStyle: PropTypes.object,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MainGrid
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { StyleSheet } from 'react-native'
|
||||||
|
import AppText from '../common/app-text'
|
||||||
|
|
||||||
|
import { shared } from '../../i18n/en/labels'
|
||||||
|
|
||||||
|
import { Spacing } from '../../styles'
|
||||||
|
|
||||||
|
function NoTemperature() {
|
||||||
|
return <AppText style={styles.warning}>{shared.noTemperatureWarning}</AppText>
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
warning: {
|
||||||
|
padding: Spacing.large,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default NoTemperature
|
||||||
Reference in New Issue
Block a user