Refactor cycle day overview screen

This commit is contained in:
Sofiya Tepikin
2022-07-23 10:22:42 +00:00
parent 77e1a16778
commit f6225c8523
4 changed files with 139 additions and 192 deletions
+26 -62
View File
@@ -1,87 +1,61 @@
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'
import { getCycleDay } from '../../db' import { getCycleDay } from '../../db'
import { getData } from '../helpers/cycle-day' import { getData } from '../helpers/cycle-day'
import { general as labels} from '../../i18n/en/cycle-day' 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
key={symptom} key={symptom}
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}
/> />
) )
})} })}
</View> </View>
</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)
+23 -42
View File
@@ -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({
+7 -16
View File
@@ -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,
+23 -12
View File
@@ -1,25 +1,34 @@
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(':')
let target = new Moment() let target = new Moment()
@@ -27,7 +36,7 @@ export default function setupNotifications(navigate) {
.minutes(parseInt(minutes)) .minutes(parseInt(minutes))
.seconds(0) .seconds(0)
if(target.isBefore(new Moment())) { if (target.isBefore(new Moment())) {
target = target.add(1, 'd') target = target.add(1, 'd')
} }
@@ -37,29 +46,31 @@ 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)
getBleedingDaysSortedByDate().addListener((_, changes) => { getBleedingDaysSortedByDate().addListener((_, changes) => {
// the listener fires on setup, so we check if there were actually any changes // the listener fires on setup, so we check if there were actually any changes
if (nothingChanged(changes)) return if (nothingChanged(changes)) return
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,
}) })
} }
} }