Merge branch 'chore/set-date-on-navigation' into 'main'
Refactor cycle day overview screen See merge request bloodyhealth/drip!417
This commit is contained in:
@@ -1,15 +1,13 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { StyleSheet, View } from 'react-native'
|
import { StyleSheet, View } from 'react-native'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { LocalDate } from 'js-joda'
|
|
||||||
|
|
||||||
import AppPage from '../common/app-page'
|
import AppPage from '../common/app-page'
|
||||||
import SymptomBox from './symptom-box'
|
import SymptomBox from './symptom-box'
|
||||||
import SymptomPageTitle from './symptom-page-title'
|
import SymptomPageTitle from './symptom-page-title'
|
||||||
|
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import { getDate, setDate } from '../../slices/date'
|
import { getDate } from '../../slices/date'
|
||||||
import { navigate } from '../../slices/navigation'
|
|
||||||
|
|
||||||
import cycleModule from '../../lib/cycle'
|
import cycleModule from '../../lib/cycle'
|
||||||
import { dateToTitle } from '../helpers/format-date'
|
import { dateToTitle } from '../helpers/format-date'
|
||||||
@@ -20,52 +18,23 @@ import { general as labels} from '../../i18n/en/cycle-day'
|
|||||||
import { Spacing } from '../../styles'
|
import { Spacing } from '../../styles'
|
||||||
import { SYMPTOMS } from '../../config'
|
import { SYMPTOMS } from '../../config'
|
||||||
|
|
||||||
class CycleDayOverView extends Component {
|
const CycleDayOverView = ({ date, isTemperatureEditView }) => {
|
||||||
|
const cycleDay = getCycleDay(date)
|
||||||
static propTypes = {
|
|
||||||
navigate: PropTypes.func,
|
|
||||||
setDate: PropTypes.func,
|
|
||||||
cycleDay: PropTypes.object,
|
|
||||||
date: PropTypes.string,
|
|
||||||
isTemperatureEditView: PropTypes.bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props)
|
|
||||||
|
|
||||||
this.state = { cycleDay: getCycleDay(props.date), data: null }
|
|
||||||
if (props.isTemperatureEditView) {
|
|
||||||
const todayDateString = LocalDate.now().toString()
|
|
||||||
props.setDate(todayDateString)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateCycleDay = (date) => {
|
|
||||||
this.props.setDate(date)
|
|
||||||
this.setState({ cycleDay: getCycleDay(date) })
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { cycleDay } = this.state
|
|
||||||
const { date, isTemperatureEditView } = this.props
|
|
||||||
|
|
||||||
const { getCycleDayNumber } = cycleModule()
|
const { getCycleDayNumber } = cycleModule()
|
||||||
const cycleDayNumber = getCycleDayNumber(date)
|
const cycleDayNumber = getCycleDayNumber(date)
|
||||||
const subtitle = cycleDayNumber && `${labels.cycleDayNumber}${cycleDayNumber}`
|
const subtitle = cycleDayNumber && `${labels.cycleDayNumber}${cycleDayNumber}`
|
||||||
|
const [editedSymptom, setEditedSymptom] = useState(
|
||||||
|
isTemperatureEditView ? 'temperature' : ''
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppPage>
|
<AppPage>
|
||||||
<SymptomPageTitle
|
<SymptomPageTitle subtitle={subtitle} title={dateToTitle(date)} />
|
||||||
reloadSymptomData={this.updateCycleDay}
|
|
||||||
subtitle={subtitle}
|
|
||||||
title={dateToTitle(date)}
|
|
||||||
/>
|
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
{SYMPTOMS.map(symptom => {
|
{SYMPTOMS.map((symptom) => {
|
||||||
const symptomData = cycleDay && cycleDay[symptom]
|
const symptomData =
|
||||||
? cycleDay[symptom] : null
|
cycleDay && cycleDay[symptom] ? cycleDay[symptom] : null
|
||||||
|
|
||||||
const isSymptomEdited = isTemperatureEditView && symptom === 'temperature'
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SymptomBox
|
<SymptomBox
|
||||||
@@ -73,8 +42,8 @@ class CycleDayOverView extends Component {
|
|||||||
symptom={symptom}
|
symptom={symptom}
|
||||||
symptomData={symptomData}
|
symptomData={symptomData}
|
||||||
symptomDataToDisplay={getData(symptom, symptomData)}
|
symptomDataToDisplay={getData(symptom, symptomData)}
|
||||||
updateCycleDayData={this.updateCycleDay}
|
editedSymptom={editedSymptom}
|
||||||
isSymptomEdited={isSymptomEdited}
|
setEditedSymptom={setEditedSymptom}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
@@ -82,6 +51,11 @@ class CycleDayOverView extends Component {
|
|||||||
</AppPage>
|
</AppPage>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CycleDayOverView.propTypes = {
|
||||||
|
cycleDay: PropTypes.object,
|
||||||
|
date: PropTypes.string,
|
||||||
|
isTemperatureEditView: PropTypes.bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
@@ -89,24 +63,14 @@ const styles = StyleSheet.create({
|
|||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
flexWrap: 'wrap',
|
flexWrap: 'wrap',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
padding: Spacing.base
|
padding: Spacing.base,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = (state) => {
|
||||||
return({
|
return {
|
||||||
date: getDate(state),
|
date: getDate(state),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => {
|
export default connect(mapStateToProps, null)(CycleDayOverView)
|
||||||
return({
|
|
||||||
setDate: (date) => dispatch(setDate(date)),
|
|
||||||
navigate: (page) => dispatch(navigate(page)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps,
|
|
||||||
)(CycleDayOverView)
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { StyleSheet, View, TouchableOpacity } from 'react-native'
|
import { StyleSheet, View, TouchableOpacity } from 'react-native'
|
||||||
import { scale } from 'react-native-size-matters'
|
import { scale } from 'react-native-size-matters'
|
||||||
@@ -14,42 +14,15 @@ import { isDateInFuture } from '../helpers/cycle-day'
|
|||||||
import { Colors, Sizes, Spacing } from '../../styles'
|
import { Colors, Sizes, Spacing } from '../../styles'
|
||||||
import { headerTitles as symptomTitles } from '../../i18n/en/labels'
|
import { headerTitles as symptomTitles } from '../../i18n/en/labels'
|
||||||
|
|
||||||
class SymptomBox extends Component {
|
const SymptomBox = ({
|
||||||
static propTypes = {
|
date,
|
||||||
date: PropTypes.string.isRequired,
|
symptom,
|
||||||
isSymptomEdited: PropTypes.bool,
|
symptomData,
|
||||||
symptom: PropTypes.string.isRequired,
|
symptomDataToDisplay,
|
||||||
symptomData: PropTypes.object,
|
editedSymptom,
|
||||||
symptomDataToDisplay: PropTypes.string,
|
setEditedSymptom,
|
||||||
updateCycleDayData: PropTypes.func.isRequired,
|
}) => {
|
||||||
}
|
const isSymptomEdited = editedSymptom === symptom
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
isSymptomEdited: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props)
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isSymptomEdited: props.isSymptomEdited,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onFinishEditing = () => {
|
|
||||||
const { date, updateCycleDayData } = this.props
|
|
||||||
|
|
||||||
updateCycleDayData(date)
|
|
||||||
this.setState({ isSymptomEdited: false })
|
|
||||||
}
|
|
||||||
|
|
||||||
onEditSymptom = () => {
|
|
||||||
this.setState({ isSymptomEdited: true })
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { date, symptom, symptomData, symptomDataToDisplay } = this.props
|
|
||||||
const { isSymptomEdited } = this.state
|
|
||||||
const isSymptomDisabled = isDateInFuture(date) && symptom !== 'note'
|
const isSymptomDisabled = isDateInFuture(date) && symptom !== 'note'
|
||||||
const isExcluded = symptomData !== null ? symptomData.exclude : false
|
const isExcluded = symptomData !== null ? symptomData.exclude : false
|
||||||
|
|
||||||
@@ -67,18 +40,18 @@ class SymptomBox extends Component {
|
|||||||
]
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<>
|
||||||
{isSymptomEdited && (
|
{isSymptomEdited && (
|
||||||
<SymptomEditView
|
<SymptomEditView
|
||||||
symptom={symptom}
|
symptom={symptom}
|
||||||
symptomData={symptomData}
|
symptomData={symptomData}
|
||||||
onClose={this.onFinishEditing}
|
onClose={() => setEditedSymptom('')}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
disabled={isSymptomDisabled}
|
disabled={isSymptomDisabled}
|
||||||
onPress={this.onEditSymptom}
|
onPress={() => setEditedSymptom(symptom)}
|
||||||
style={styles.container}
|
style={styles.container}
|
||||||
testID={iconName}
|
testID={iconName}
|
||||||
>
|
>
|
||||||
@@ -99,9 +72,17 @@ class SymptomBox extends Component {
|
|||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</React.Fragment>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SymptomBox.propTypes = {
|
||||||
|
date: PropTypes.string.isRequired,
|
||||||
|
symptom: PropTypes.string.isRequired,
|
||||||
|
symptomData: PropTypes.object,
|
||||||
|
symptomDataToDisplay: PropTypes.string,
|
||||||
|
editedSymptom: PropTypes.string.isRequired,
|
||||||
|
setEditedSymptom: PropTypes.func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
|||||||
@@ -12,33 +12,25 @@ import { nextDate, prevDate } from '../helpers/cycle-day'
|
|||||||
import { Colors, Containers, Spacing, Typography } from '../../styles'
|
import { Colors, Containers, Spacing, Typography } from '../../styles'
|
||||||
import { HIT_SLOP } from '../../config'
|
import { HIT_SLOP } from '../../config'
|
||||||
|
|
||||||
const SymptomPageTitle = ({
|
const SymptomPageTitle = ({ date, setDate, subtitle, title }) => {
|
||||||
date,
|
|
||||||
reloadSymptomData,
|
|
||||||
setDate,
|
|
||||||
subtitle,
|
|
||||||
title,
|
|
||||||
}) => {
|
|
||||||
const navigate = (isForward) => {
|
const navigate = (isForward) => {
|
||||||
const nextDay = isForward ? nextDate(date) : prevDate(date)
|
const newDate = isForward ? nextDate(date) : prevDate(date)
|
||||||
reloadSymptomData(nextDay)
|
setDate(newDate)
|
||||||
setDate(nextDay)
|
|
||||||
}
|
}
|
||||||
const formattedTitle = title.length > 21
|
const formattedTitle =
|
||||||
? title.substring(0, 18) + '...'
|
title.length > 21 ? title.substring(0, 18) + '...' : title
|
||||||
: title
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<TouchableOpacity onPress={() => navigate(false)} hitSlop={HIT_SLOP}>
|
<TouchableOpacity onPress={() => navigate(false)} hitSlop={HIT_SLOP}>
|
||||||
<AppIcon name='chevron-left' color={Colors.orange}/>
|
<AppIcon name="chevron-left" color={Colors.orange} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<View style={styles.textContainer}>
|
<View style={styles.textContainer}>
|
||||||
<AppText style={styles.title}>{formattedTitle}</AppText>
|
<AppText style={styles.title}>{formattedTitle}</AppText>
|
||||||
{subtitle && <AppText style={styles.subtitle}>{subtitle}</AppText>}
|
{subtitle && <AppText style={styles.subtitle}>{subtitle}</AppText>}
|
||||||
</View>
|
</View>
|
||||||
<TouchableOpacity onPress={() => navigate(true)} hitSlop={HIT_SLOP}>
|
<TouchableOpacity onPress={() => navigate(true)} hitSlop={HIT_SLOP}>
|
||||||
<AppIcon name='chevron-right' color={Colors.orange}/>
|
<AppIcon name="chevron-right" color={Colors.orange} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
@@ -46,7 +38,6 @@ const SymptomPageTitle = ({
|
|||||||
|
|
||||||
SymptomPageTitle.propTypes = {
|
SymptomPageTitle.propTypes = {
|
||||||
date: PropTypes.string.isRequired,
|
date: PropTypes.string.isRequired,
|
||||||
reloadSymptomData: PropTypes.func.isRequired,
|
|
||||||
setDate: PropTypes.func.isRequired,
|
setDate: PropTypes.func.isRequired,
|
||||||
subtitle: PropTypes.string,
|
subtitle: PropTypes.string,
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
|
|||||||
+19
-8
@@ -1,24 +1,33 @@
|
|||||||
import {tempReminderObservable, periodReminderObservable} from '../local-storage'
|
import {
|
||||||
|
tempReminderObservable,
|
||||||
|
periodReminderObservable,
|
||||||
|
} from '../local-storage'
|
||||||
import Notification from 'react-native-push-notification'
|
import Notification from 'react-native-push-notification'
|
||||||
import Moment from 'moment'
|
import Moment from 'moment'
|
||||||
|
import { LocalDate } from 'js-joda'
|
||||||
|
|
||||||
import labels from '../i18n/en/settings'
|
import labels from '../i18n/en/settings'
|
||||||
import { getBleedingDaysSortedByDate } from '../db'
|
import { getBleedingDaysSortedByDate } from '../db'
|
||||||
import cycleModule from './cycle'
|
import cycleModule from './cycle'
|
||||||
import nothingChanged from '../db/db-unchanged'
|
import nothingChanged from '../db/db-unchanged'
|
||||||
|
import store from '../store'
|
||||||
|
import { setDate } from '../slices/date'
|
||||||
|
|
||||||
export default function setupNotifications(navigate) {
|
export default function setupNotifications(navigate) {
|
||||||
Notification.configure({
|
Notification.configure({
|
||||||
onNotification: (notification) => {
|
onNotification: (notification) => {
|
||||||
// https://github.com/zo0r/react-native-push-notification/issues/966#issuecomment-479069106
|
// https://github.com/zo0r/react-native-push-notification/issues/966#issuecomment-479069106
|
||||||
if (notification.data?.id === '1' || notification.id === '1') {
|
if (notification.data?.id === '1' || notification.id === '1') {
|
||||||
|
const todayDate = LocalDate.now().toString()
|
||||||
|
store.dispatch(setDate(todayDate))
|
||||||
navigate('TemperatureEditView')
|
navigate('TemperatureEditView')
|
||||||
} else {
|
} else {
|
||||||
navigate('Home')
|
navigate('Home')
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
tempReminderObservable(reminder => {
|
tempReminderObservable((reminder) => {
|
||||||
Notification.cancelLocalNotifications({ id: '1' })
|
Notification.cancelLocalNotifications({ id: '1' })
|
||||||
if (reminder.enabled) {
|
if (reminder.enabled) {
|
||||||
const [hours, minutes] = reminder.time.split(':')
|
const [hours, minutes] = reminder.time.split(':')
|
||||||
@@ -37,12 +46,12 @@ export default function setupNotifications(navigate) {
|
|||||||
message: labels.tempReminder.notification,
|
message: labels.tempReminder.notification,
|
||||||
date: target.toDate(),
|
date: target.toDate(),
|
||||||
vibrate: false,
|
vibrate: false,
|
||||||
repeatType: 'day'
|
repeatType: 'day',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, false)
|
}, false)
|
||||||
|
|
||||||
periodReminderObservable(reminder => {
|
periodReminderObservable((reminder) => {
|
||||||
Notification.cancelLocalNotifications({ id: '2' })
|
Notification.cancelLocalNotifications({ id: '2' })
|
||||||
if (reminder.enabled) setupPeriodReminder()
|
if (reminder.enabled) setupPeriodReminder()
|
||||||
}, false)
|
}, false)
|
||||||
@@ -53,13 +62,15 @@ export default function setupNotifications(navigate) {
|
|||||||
Notification.cancelLocalNotifications({ id: '2' })
|
Notification.cancelLocalNotifications({ id: '2' })
|
||||||
if (periodReminderObservable.value.enabled) setupPeriodReminder()
|
if (periodReminderObservable.value.enabled) setupPeriodReminder()
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupPeriodReminder() {
|
function setupPeriodReminder() {
|
||||||
const bleedingPrediction = cycleModule().getPredictedMenses()
|
const bleedingPrediction = cycleModule().getPredictedMenses()
|
||||||
if (bleedingPrediction.length > 0) {
|
if (bleedingPrediction.length > 0) {
|
||||||
const predictedBleedingStart = Moment(bleedingPrediction[0][0], "YYYY-MM-DD")
|
const predictedBleedingStart = Moment(
|
||||||
|
bleedingPrediction[0][0],
|
||||||
|
'YYYY-MM-DD'
|
||||||
|
)
|
||||||
// 3 days before and at 6 am
|
// 3 days before and at 6 am
|
||||||
const reminderDate = predictedBleedingStart
|
const reminderDate = predictedBleedingStart
|
||||||
.subtract(3, 'days')
|
.subtract(3, 'days')
|
||||||
@@ -76,7 +87,7 @@ function setupPeriodReminder() {
|
|||||||
userInfo: { id: '2' },
|
userInfo: { id: '2' },
|
||||||
message: labels.periodReminder.notification(daysToEndOfPrediction),
|
message: labels.periodReminder.notification(daysToEndOfPrediction),
|
||||||
date: reminderDate.toDate(),
|
date: reminderDate.toDate(),
|
||||||
vibrate: false
|
vibrate: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user