diff --git a/components/helpers/home.js b/components/helpers/home.js new file mode 100644 index 0000000..98e9f20 --- /dev/null +++ b/components/helpers/home.js @@ -0,0 +1,64 @@ +import { ChronoUnit, LocalDate } from 'js-joda' + +import { formatDateForShortText } from './format-date' + +import { + home as labels, + bleedingPrediction as predictLabels, +} from '../../i18n/en/labels' + +function getTimes(prediction) { + const todayDate = LocalDate.now() + const predictedBleedingStart = LocalDate.parse(prediction[0][0]) + /* the range of predicted bleeding days can be either 3 or 5 */ + const predictedBleedingEnd = + LocalDate.parse(prediction[0][ prediction[0].length - 1 ]) + const daysToEnd = todayDate.until(predictedBleedingEnd, ChronoUnit.DAYS) + return { todayDate, predictedBleedingStart, predictedBleedingEnd, daysToEnd } +} + +export function determinePredictionText(bleedingPrediction) { + if (!bleedingPrediction.length) return predictLabels.noPrediction + const { + todayDate, + predictedBleedingStart, + predictedBleedingEnd, + daysToEnd + } = getTimes(bleedingPrediction) + if (todayDate.isBefore(predictedBleedingStart)) { + return predictLabels.predictionInFuture( + todayDate.until(predictedBleedingStart, ChronoUnit.DAYS), + todayDate.until(predictedBleedingEnd, ChronoUnit.DAYS) + ) + } + if (todayDate.isAfter(predictedBleedingEnd)) { + return predictLabels.predictionInPast( + formatDateForShortText(predictedBleedingStart), + formatDateForShortText(predictedBleedingEnd) + ) + } + if (daysToEnd === 0) { + return predictLabels.predictionStartedNoDaysLeft + } else if (daysToEnd === 1) { + return predictLabels.predictionStarted1DayLeft + } else { + return predictLabels.predictionStartedXDaysLeft(daysToEnd) + } +} + +export function getBleedingPredictionRange(prediction) { + if (!prediction.length) return labels.unknown + const { + todayDate, + predictedBleedingStart, + predictedBleedingEnd, + daysToEnd + } = getTimes(prediction) + if (todayDate.isBefore(predictedBleedingStart)) { + return `${todayDate.until(predictedBleedingStart, ChronoUnit.DAYS)}-${todayDate.until(predictedBleedingEnd, ChronoUnit.DAYS)}` + } + if (todayDate.isAfter(predictedBleedingEnd)) { + return labels.unknown + } + return (daysToEnd === 0 ? '0' : `0 - ${daysToEnd}`) +} diff --git a/components/home-element.js b/components/home-element.js new file mode 100644 index 0000000..5c2423b --- /dev/null +++ b/components/home-element.js @@ -0,0 +1,40 @@ +import React from 'react' +import { View } from 'react-native' +import PropTypes from 'prop-types' + +import Button from './button' + +import styles from '../styles' + +const HomeElement = ({ children, onPress, buttonColor, buttonLabel }) => { + return ( + + + {children[0]} + {children[1]} + + + + {children.slice(2)} + + + + ) +} + +HomeElement.propTypes = { + buttonColor: PropTypes.string, + buttonLabel: PropTypes.string, + children: PropTypes.node, + onPress: PropTypes.func, +} + +export default HomeElement diff --git a/components/home.js b/components/home.js index 69604b0..0b300cb 100644 --- a/components/home.js +++ b/components/home.js @@ -1,74 +1,55 @@ -import { ChronoUnit, LocalDate } from 'js-joda' +import { LocalDate } from 'js-joda' import React, { Component } from 'react' import { ScrollView, View } from 'react-native' import { connect } from 'react-redux' +import PropTypes from 'prop-types' import { navigate } from '../slices/navigation' -import { setDate } from '../slices/date' +import { getDate, setDate } from '../slices/date' import DripHomeIcon from '../assets/drip-home-icons' -import { - bleedingPrediction as predictLabels, - home as labels -} from '../i18n/en/labels' + +import AppText from './app-text' +import IconText from './icon-text' +import HomeElement from './home-element' + +import { home as labels } from '../i18n/en/labels' import links from '../i18n/en/links' + import cycleModule from '../lib/cycle' import { getFertilityStatusForDay } from '../lib/sympto-adapter' +import { + determinePredictionText, + getBleedingPredictionRange +} from './helpers/home' + import styles, { cycleDayColor, periodColor, secondaryColor } from '../styles' -import AppText from './app-text' -import Button from './button' -import { formatDateForShortText } from './helpers/format-date' - -const IconText = ({ children, wrapperStyles }) => { - return ( - - - { children } - - - ) -} - -const HomeElement = ({ children, onPress, buttonColor, buttonLabel }) => { - return ( - - - {children[0]} - {children[1]} - - - - {children.slice(2)} - - - - ) -} class Home extends Component { + + static propTypes = { + navigate: PropTypes.func, + setDate: PropTypes.func, + // The following three is not being used, + // we could see if it's possible to not pass them from the + cycleDay: PropTypes.object, + date: PropTypes.string, + handleBackButtonPress: PropTypes.func, + } + constructor(props) { super(props) - const { getCycleDayNumber, getPredictedMenses } = cycleModule() - this.getCycleDayNumber = getCycleDayNumber - this.getBleedingPrediction = getPredictedMenses - this.todayDateString = LocalDate.now().toString() - const prediction = this.getBleedingPrediction() - const fertilityStatus = getFertilityStatusForDay(this.todayDateString) - this.state = { - cycleDayNumber: this.getCycleDayNumber(this.todayDateString), - predictionText: determinePredictionText(prediction), - bleedingPredictionRange: getBleedingPredictionRange(prediction), - ...fertilityStatus - } + const { getCycleDayNumber, getPredictedMenses } = cycleModule() + + this.todayDateString = LocalDate.now().toString() + this.cycleDayNumber = getCycleDayNumber(this.todayDateString) + + const prediction = getPredictedMenses() + this.predictionText = determinePredictionText(prediction) + this.bleedingPredictionRange = getBleedingPredictionRange(prediction) + + this.fertilityStatus = getFertilityStatusForDay(this.todayDateString) } navigateToCycleDayView = () => { @@ -86,18 +67,22 @@ class Home extends Component { } render() { - const { cycleDayNumber, phase, status } = this.state + const { + cycleDayNumber, + predictionText, + bleedingPredictionRange, + } = this + + const { phase, status, statusText } = this.fertilityStatus + const cycleDayMoreText = cycleDayNumber ? labels.cycleDayKnown(cycleDayNumber) : labels.cycleDayNotEnoughInfo - const { statusText } = this.state - return ( - - - {cycleDayNumber || labels.unknown} - + {cycleDayNumber || labels.unknown} {cycleDayMoreText} @@ -122,12 +105,12 @@ class Home extends Component { > - - {this.state.bleedingPredictionRange} + + {bleedingPredictionRange} - {this.state.predictionText} + {predictionText} @@ -138,9 +121,7 @@ class Home extends Component { > - - { phase ? phase.toString() : labels.unknown } - + { phase ? phase.toString() : labels.unknown } { phase && @@ -158,6 +139,12 @@ class Home extends Component { } } +const mapStateToProps = (state) => { + return({ + date: getDate(state), + }) +} + const mapDispatchToProps = (dispatch) => { return({ navigate: (page) => dispatch(navigate(page)), @@ -166,63 +153,6 @@ const mapDispatchToProps = (dispatch) => { } export default connect( - null, + mapStateToProps, mapDispatchToProps, )(Home) - - -function getTimes(prediction) { - const todayDate = LocalDate.now() - const predictedBleedingStart = LocalDate.parse(prediction[0][0]) - /* the range of predicted bleeding days can be either 3 or 5 */ - const predictedBleedingEnd = - LocalDate.parse(prediction[0][ prediction[0].length - 1 ]) - const daysToEnd = todayDate.until(predictedBleedingEnd, ChronoUnit.DAYS) - return { todayDate, predictedBleedingStart, predictedBleedingEnd, daysToEnd } -} - -function determinePredictionText(bleedingPrediction) { - if (!bleedingPrediction.length) return predictLabels.noPrediction - const { - todayDate, - predictedBleedingStart, - predictedBleedingEnd, - daysToEnd - } = getTimes(bleedingPrediction) - if (todayDate.isBefore(predictedBleedingStart)) { - return predictLabels.predictionInFuture( - todayDate.until(predictedBleedingStart, ChronoUnit.DAYS), - todayDate.until(predictedBleedingEnd, ChronoUnit.DAYS) - ) - } - if (todayDate.isAfter(predictedBleedingEnd)) { - return predictLabels.predictionInPast( - formatDateForShortText(predictedBleedingStart), - formatDateForShortText(predictedBleedingEnd) - ) - } - if (daysToEnd === 0) { - return predictLabels.predictionStartedNoDaysLeft - } else if (daysToEnd === 1) { - return predictLabels.predictionStarted1DayLeft - } else { - return predictLabels.predictionStartedXDaysLeft(daysToEnd) - } -} - -function getBleedingPredictionRange(prediction) { - if (!prediction.length) return labels.unknown - const { - todayDate, - predictedBleedingStart, - predictedBleedingEnd, - daysToEnd - } = getTimes(prediction) - if (todayDate.isBefore(predictedBleedingStart)) { - return `${todayDate.until(predictedBleedingStart, ChronoUnit.DAYS)}-${todayDate.until(predictedBleedingEnd, ChronoUnit.DAYS)}` - } - if (todayDate.isAfter(predictedBleedingEnd)) { - return labels.unknown - } - return (daysToEnd === 0 ? '0' : `0 - ${daysToEnd}`) -} diff --git a/components/icon-text.js b/components/icon-text.js new file mode 100644 index 0000000..be30d48 --- /dev/null +++ b/components/icon-text.js @@ -0,0 +1,24 @@ +import React from 'react' +import { View } from 'react-native' +import PropTypes from 'prop-types' + +import AppText from './app-text' + +import styles from '../styles' + +const IconText = ({ children, wrapperStyles }) => { + return ( + + + { children } + + + ) +} + +IconText.propTypes = { + children: PropTypes.node, + wrapperStyles: PropTypes.object, +} + +export default IconText diff --git a/styles/index.js b/styles/index.js index 7825d11..1b9bb4f 100644 --- a/styles/index.js +++ b/styles/index.js @@ -131,14 +131,12 @@ export default StyleSheet.create({ homeIconTextWrapper: { alignItems: 'center', justifyContent: 'center', + width: 80, + position: 'absolute', }, homeIconAndText: { justifyContent: 'center' }, - wrapperIcon: { - width: 80, - position: 'absolute' - }, homeCircle: { borderRadius: 100, borderWidth: 2.3,