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
+42 -78
View File
@@ -1,87 +1,61 @@
import React, { Component } from 'react'
import React, { useState } from 'react'
import { StyleSheet, View } from 'react-native'
import PropTypes from 'prop-types'
import { LocalDate } from 'js-joda'
import AppPage from '../common/app-page'
import SymptomBox from './symptom-box'
import SymptomPageTitle from './symptom-page-title'
import { connect } from 'react-redux'
import { getDate, setDate } from '../../slices/date'
import { navigate } from '../../slices/navigation'
import { getDate } from '../../slices/date'
import cycleModule from '../../lib/cycle'
import { dateToTitle } from '../helpers/format-date'
import { getCycleDay } from '../../db'
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 { 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,
}
const { getCycleDayNumber } = cycleModule()
const cycleDayNumber = getCycleDayNumber(date)
const subtitle = cycleDayNumber && `${labels.cycleDayNumber}${cycleDayNumber}`
const [editedSymptom, setEditedSymptom] = useState(
isTemperatureEditView ? 'temperature' : ''
)
constructor(props) {
super(props)
return (
<AppPage>
<SymptomPageTitle subtitle={subtitle} title={dateToTitle(date)} />
<View style={styles.container}>
{SYMPTOMS.map((symptom) => {
const symptomData =
cycleDay && cycleDay[symptom] ? cycleDay[symptom] : null
this.state = { cycleDay: getCycleDay(props.date), data: null }
if (props.isTemperatureEditView) {
const todayDateString = LocalDate.now().toString()
props.setDate(todayDateString)
}
}
return (
<SymptomBox
key={symptom}
symptom={symptom}
symptomData={symptomData}
symptomDataToDisplay={getData(symptom, symptomData)}
editedSymptom={editedSymptom}
setEditedSymptom={setEditedSymptom}
/>
)
})}
</View>
</AppPage>
)
}
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 cycleDayNumber = getCycleDayNumber(date)
const subtitle = cycleDayNumber && `${labels.cycleDayNumber}${cycleDayNumber}`
return (
<AppPage>
<SymptomPageTitle
reloadSymptomData={this.updateCycleDay}
subtitle={subtitle}
title={dateToTitle(date)}
/>
<View style={styles.container}>
{SYMPTOMS.map(symptom => {
const symptomData = cycleDay && cycleDay[symptom]
? cycleDay[symptom] : null
const isSymptomEdited = isTemperatureEditView && symptom === 'temperature'
return(
<SymptomBox
key={symptom}
symptom={symptom}
symptomData={symptomData}
symptomDataToDisplay={getData(symptom, symptomData)}
updateCycleDayData={this.updateCycleDay}
isSymptomEdited={isSymptomEdited}
/>
)
})}
</View>
</AppPage>
)
}
CycleDayOverView.propTypes = {
cycleDay: PropTypes.object,
date: PropTypes.string,
isTemperatureEditView: PropTypes.bool,
}
const styles = StyleSheet.create({
@@ -89,24 +63,14 @@ const styles = StyleSheet.create({
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'space-between',
padding: Spacing.base
}
padding: Spacing.base,
},
})
const mapStateToProps = (state) => {
return({
return {
date: getDate(state),
})
}
}
const mapDispatchToProps = (dispatch) => {
return({
setDate: (date) => dispatch(setDate(date)),
navigate: (page) => dispatch(navigate(page)),
})
}
export default connect(
mapStateToProps,
mapDispatchToProps,
)(CycleDayOverView)
export default connect(mapStateToProps, null)(CycleDayOverView)
+66 -85
View File
@@ -1,4 +1,4 @@
import React, { Component } from 'react'
import React from 'react'
import PropTypes from 'prop-types'
import { StyleSheet, View, TouchableOpacity } from 'react-native'
import { scale } from 'react-native-size-matters'
@@ -14,94 +14,75 @@ import { isDateInFuture } from '../helpers/cycle-day'
import { Colors, Sizes, Spacing } from '../../styles'
import { headerTitles as symptomTitles } from '../../i18n/en/labels'
class SymptomBox extends Component {
static propTypes = {
date: PropTypes.string.isRequired,
isSymptomEdited: PropTypes.bool,
symptom: PropTypes.string.isRequired,
symptomData: PropTypes.object,
symptomDataToDisplay: PropTypes.string,
updateCycleDayData: PropTypes.func.isRequired,
}
const SymptomBox = ({
date,
symptom,
symptomData,
symptomDataToDisplay,
editedSymptom,
setEditedSymptom,
}) => {
const isSymptomEdited = editedSymptom === symptom
const isSymptomDisabled = isDateInFuture(date) && symptom !== 'note'
const isExcluded = symptomData !== null ? symptomData.exclude : false
static defaultProps = {
isSymptomEdited: false,
}
const iconColor = isSymptomDisabled ? Colors.greyLight : Colors.grey
const iconName = `drip-icon-${symptom}`
const symptomNameStyle = [
styles.symptomName,
isSymptomDisabled && styles.symptomNameDisabled,
isExcluded && styles.symptomNameExcluded,
]
const textStyle = [
styles.text,
isSymptomDisabled && styles.textDisabled,
isExcluded && styles.textExcluded,
]
constructor(props) {
super(props)
return (
<>
{isSymptomEdited && (
<SymptomEditView
symptom={symptom}
symptomData={symptomData}
onClose={() => setEditedSymptom('')}
/>
)}
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 isExcluded = symptomData !== null ? symptomData.exclude : false
const iconColor = isSymptomDisabled ? Colors.greyLight : Colors.grey
const iconName = `drip-icon-${symptom}`
const symptomNameStyle = [
styles.symptomName,
isSymptomDisabled && styles.symptomNameDisabled,
isExcluded && styles.symptomNameExcluded,
]
const textStyle = [
styles.text,
isSymptomDisabled && styles.textDisabled,
isExcluded && styles.textExcluded,
]
return (
<React.Fragment>
{isSymptomEdited && (
<SymptomEditView
symptom={symptom}
symptomData={symptomData}
onClose={this.onFinishEditing}
/>
)}
<TouchableOpacity
disabled={isSymptomDisabled}
onPress={this.onEditSymptom}
style={styles.container}
testID={iconName}
>
<DripIcon
color={iconColor}
isActive={!isSymptomDisabled}
name={iconName}
size={Sizes.icon}
/>
<View style={styles.textContainer}>
<AppText style={symptomNameStyle}>
{symptomTitles[symptom].toLowerCase()}
<TouchableOpacity
disabled={isSymptomDisabled}
onPress={() => setEditedSymptom(symptom)}
style={styles.container}
testID={iconName}
>
<DripIcon
color={iconColor}
isActive={!isSymptomDisabled}
name={iconName}
size={Sizes.icon}
/>
<View style={styles.textContainer}>
<AppText style={symptomNameStyle}>
{symptomTitles[symptom].toLowerCase()}
</AppText>
{symptomDataToDisplay && (
<AppText style={textStyle} numberOfLines={4}>
{symptomDataToDisplay}
</AppText>
{symptomDataToDisplay && (
<AppText style={textStyle} numberOfLines={4}>
{symptomDataToDisplay}
</AppText>
)}
</View>
</TouchableOpacity>
</React.Fragment>
)
}
)}
</View>
</TouchableOpacity>
</>
)
}
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({
+7 -16
View File
@@ -12,33 +12,25 @@ import { nextDate, prevDate } from '../helpers/cycle-day'
import { Colors, Containers, Spacing, Typography } from '../../styles'
import { HIT_SLOP } from '../../config'
const SymptomPageTitle = ({
date,
reloadSymptomData,
setDate,
subtitle,
title,
}) => {
const SymptomPageTitle = ({ date, setDate, subtitle, title }) => {
const navigate = (isForward) => {
const nextDay = isForward ? nextDate(date) : prevDate(date)
reloadSymptomData(nextDay)
setDate(nextDay)
const newDate = isForward ? nextDate(date) : prevDate(date)
setDate(newDate)
}
const formattedTitle = title.length > 21
? title.substring(0, 18) + '...'
: title
const formattedTitle =
title.length > 21 ? title.substring(0, 18) + '...' : title
return (
<View style={styles.container}>
<TouchableOpacity onPress={() => navigate(false)} hitSlop={HIT_SLOP}>
<AppIcon name='chevron-left' color={Colors.orange}/>
<AppIcon name="chevron-left" color={Colors.orange} />
</TouchableOpacity>
<View style={styles.textContainer}>
<AppText style={styles.title}>{formattedTitle}</AppText>
{subtitle && <AppText style={styles.subtitle}>{subtitle}</AppText>}
</View>
<TouchableOpacity onPress={() => navigate(true)} hitSlop={HIT_SLOP}>
<AppIcon name='chevron-right' color={Colors.orange}/>
<AppIcon name="chevron-right" color={Colors.orange} />
</TouchableOpacity>
</View>
)
@@ -46,7 +38,6 @@ const SymptomPageTitle = ({
SymptomPageTitle.propTypes = {
date: PropTypes.string.isRequired,
reloadSymptomData: PropTypes.func.isRequired,
setDate: PropTypes.func.isRequired,
subtitle: PropTypes.string,
title: PropTypes.string,
+24 -13
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 Moment from 'moment'
import { LocalDate } from 'js-joda'
import labels from '../i18n/en/settings'
import { getBleedingDaysSortedByDate } from '../db'
import cycleModule from './cycle'
import nothingChanged from '../db/db-unchanged'
import store from '../store'
import { setDate } from '../slices/date'
export default function setupNotifications(navigate) {
Notification.configure({
onNotification: (notification) => {
// https://github.com/zo0r/react-native-push-notification/issues/966#issuecomment-479069106
if (notification.data?.id === '1' || notification.id === '1') {
const todayDate = LocalDate.now().toString()
store.dispatch(setDate(todayDate))
navigate('TemperatureEditView')
} else {
navigate('Home')
}
}
},
})
tempReminderObservable(reminder => {
Notification.cancelLocalNotifications({id: '1'})
tempReminderObservable((reminder) => {
Notification.cancelLocalNotifications({ id: '1' })
if (reminder.enabled) {
const [hours, minutes] = reminder.time.split(':')
let target = new Moment()
@@ -27,7 +36,7 @@ export default function setupNotifications(navigate) {
.minutes(parseInt(minutes))
.seconds(0)
if(target.isBefore(new Moment())) {
if (target.isBefore(new Moment())) {
target = target.add(1, 'd')
}
@@ -37,29 +46,31 @@ export default function setupNotifications(navigate) {
message: labels.tempReminder.notification,
date: target.toDate(),
vibrate: false,
repeatType: 'day'
repeatType: 'day',
})
}
}, false)
periodReminderObservable(reminder => {
Notification.cancelLocalNotifications({id: '2'})
periodReminderObservable((reminder) => {
Notification.cancelLocalNotifications({ id: '2' })
if (reminder.enabled) setupPeriodReminder()
}, false)
getBleedingDaysSortedByDate().addListener((_, changes) => {
// the listener fires on setup, so we check if there were actually any changes
if (nothingChanged(changes)) return
Notification.cancelLocalNotifications({id: '2'})
Notification.cancelLocalNotifications({ id: '2' })
if (periodReminderObservable.value.enabled) setupPeriodReminder()
})
}
function setupPeriodReminder() {
const bleedingPrediction = cycleModule().getPredictedMenses()
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
const reminderDate = predictedBleedingStart
.subtract(3, 'days')
@@ -68,7 +79,7 @@ function setupPeriodReminder() {
.seconds(0)
if (reminderDate.isAfter()) {
// period is likely to start in 3 to 3 + (length of prediction - 1) days
// period is likely to start in 3 to 3 + (length of prediction - 1) days
const daysToEndOfPrediction = bleedingPrediction[0].length + 2
Notification.localNotificationSchedule({
@@ -76,7 +87,7 @@ function setupPeriodReminder() {
userInfo: { id: '2' },
message: labels.periodReminder.notification(daysToEndOfPrediction),
date: reminderDate.toDate(),
vibrate: false
vibrate: false,
})
}
}