Replaces the inheritance with composition pattern in the Symptom view

This commit is contained in:
Sofiya Tepikin
2019-08-16 20:32:02 +02:00
parent c0235d148b
commit 8ce840306a
17 changed files with 2577 additions and 2495 deletions
+1
View File
@@ -125,6 +125,7 @@ class App extends Component {
<Page
navigate={this.navigate}
cycleDay={cycleDay}
date={this.props.date}
handleBackButtonPress={this.handleBackButtonPress}
/>
+49 -44
View File
@@ -1,56 +1,70 @@
import React from 'react'
import { Switch, ScrollView } from 'react-native'
import { connect } from 'react-redux'
import React, { Component } from 'react'
import { Switch } from 'react-native'
import PropTypes from 'prop-types'
import { getDate } from '../../../slices/date'
import styles from '../../../styles'
import { bleeding } from '../../../i18n/en/cycle-day'
import SelectTabGroup from '../select-tab-group'
import SymptomSection from './symptom-section'
import SymptomView from './symptom-view'
class Bleeding extends SymptomView {
import { getLabelsList } from '../../helpers/labels'
import { saveSymptom } from '../../../db'
class Bleeding extends Component {
static propTypes = {
cycleDay: PropTypes.object,
handleBackButtonPress: PropTypes.func,
date: PropTypes.string.isRequired,
}
constructor(props) {
super(props)
const cycleDay = props.cycleDay
this.bleeding = cycleDay && cycleDay.bleeding
this.state = {
currentValue: this.bleeding && this.bleeding.value,
exclude: this.bleeding ? this.bleeding.exclude : false
}
const symptom = 'bleeding'
const { cycleDay } = props
const defaultSymptomData = {
value: null,
exclude: false
}
symptomName = 'bleeding'
const symptomData =
cycleDay && cycleDay[symptom] ? cycleDay[symptom] : defaultSymptomData
this.state = { ...symptomData }
this.bleedingRadioProps = getLabelsList(bleeding.labels)
this.symptom = symptom
}
autoSave = () => {
if (typeof this.state.currentValue != 'number') {
this.deleteSymptomEntry()
return
}
this.saveSymptomEntry({
value: this.state.currentValue,
exclude: this.state.exclude
})
const { date } = this.props
const valuesToSave = { ...this.state }
const hasValueToSave = typeof this.state.value === 'number'
saveSymptom(this.symptom, date, hasValueToSave ? valuesToSave : null)
}
renderContent() {
const bleedingRadioProps = [
{ label: bleeding.labels[0], value: 0 },
{ label: bleeding.labels[1], value: 1 },
{ label: bleeding.labels[2], value: 2 },
{ label: bleeding.labels[3], value: 3 },
]
componentDidUpdate() {
this.autoSave()
}
render() {
return (
<ScrollView style={styles.page}>
<SymptomView
symptom={this.symptom}
values={this.state}
handleBackButtonPress={this.props.handleBackButtonPress}
date={this.props.date}
>
<SymptomSection
header={bleeding.heaviness.header}
explainer={bleeding.heaviness.explainer}
>
<SelectTabGroup
buttons={bleedingRadioProps}
active={this.state.currentValue}
onSelect={val => this.setState({ currentValue: val })}
buttons={this.bleedingRadioProps}
active={this.state.value}
onSelect={val => this.setState({ value: val })}
/>
</SymptomSection>
<SymptomSection
@@ -65,18 +79,9 @@ class Bleeding extends SymptomView {
value={this.state.exclude}
/>
</SymptomSection>
</ScrollView>
</SymptomView>
)
}
}
const mapStateToProps = (state) => {
return({
date: getDate(state)
})
}
export default connect(
mapStateToProps,
null
)(Bleeding)
export default Bleeding
+54 -55
View File
@@ -1,68 +1,76 @@
import React from 'react'
import {
Switch,
ScrollView
} from 'react-native'
import { connect } from 'react-redux'
import React, { Component } from 'react'
import { Switch } from 'react-native'
import PropTypes from 'prop-types'
import { getDate } from '../../../slices/date'
import styles from '../../../styles'
import { cervix as labels } from '../../../i18n/en/cycle-day'
import SelectTabGroup from '../select-tab-group'
import SymptomSection from './symptom-section'
import SymptomView from './symptom-view'
class Cervix extends SymptomView {
import { getLabelsList } from '../../helpers/labels'
import { saveSymptom } from '../../../db'
class Cervix extends Component {
static propTypes = {
cycleDay: PropTypes.object,
handleBackButtonPress: PropTypes.func,
date: PropTypes.string.isRequired,
}
constructor(props) {
super(props)
const cycleDay = props.cycleDay
this.cervix = cycleDay && cycleDay.cervix
this.state = this.cervix ? this.cervix : {}
}
const symptom = 'cervix'
const { cycleDay } = props
symptomName = 'cervix'
const defaultSymptomData = {}
const symptomData =
cycleDay && cycleDay[symptom] ? cycleDay[symptom] : defaultSymptomData
this.state = { ...symptomData }
this.cervixOpeningRadioProps = getLabelsList(labels.opening.categories)
this.cervixFirmnessRadioProps = getLabelsList(labels.firmness.categories)
this.cervixPositionRadioProps = getLabelsList(labels.position.categories)
this.symptom = symptom
}
autoSave = () => {
const nothingEntered = ['opening', 'firmness', 'position'].every(val => typeof this.state[val] != 'number')
if (nothingEntered) {
this.deleteSymptomEntry()
return
const { date } = this.props
const { opening, firmness, position, exclude } = this.state
const valuesToSave = {
opening,
firmness,
position,
exclude: Boolean(exclude)
}
const nothingEntered = ['opening', 'firmness', 'position'].every(
val => typeof this.state[val] !== 'number')
saveSymptom(this.symptom, date, nothingEntered ? null : valuesToSave)
}
this.saveSymptomEntry({
opening: this.state.opening,
firmness: this.state.firmness,
position: this.state.position,
exclude: Boolean(this.state.exclude)
})
componentDidUpdate() {
this.autoSave()
}
renderContent() {
const cervixOpeningRadioProps = [
{ label: labels.opening.categories[0], value: 0 },
{ label: labels.opening.categories[1], value: 1 },
{ label: labels.opening.categories[2], value: 2 }
]
const cervixFirmnessRadioProps = [
{ label: labels.firmness.categories[0], value: 0 },
{ label: labels.firmness.categories[1], value: 1 }
]
const cervixPositionRadioProps = [
{ label: labels.position.categories[0], value: 0 },
{ label: labels.position.categories[1], value: 1 },
{ label: labels.position.categories[2], value: 2 }
]
render() {
// TODO saving this info for notice when leaving incomplete data
// const mandatoryNotCompleted = typeof this.state.opening != 'number' || typeof this.state.firmness != 'number'
return (
<ScrollView style={styles.page}>
<SymptomView
symptom={this.symptom}
values={this.state}
handleBackButtonPress={this.props.handleBackButtonPress}
date={this.props.date}
>
<SymptomSection
header="Opening"
explainer={labels.opening.explainer}
>
<SelectTabGroup
buttons={cervixOpeningRadioProps}
buttons={this.cervixOpeningRadioProps}
active={this.state.opening}
onSelect={val => this.setState({ opening: val })}
/>
@@ -72,7 +80,7 @@ class Cervix extends SymptomView {
explainer={labels.firmness.explainer}
>
<SelectTabGroup
buttons={cervixFirmnessRadioProps}
buttons={this.cervixFirmnessRadioProps}
active={this.state.firmness}
onSelect={val => this.setState({ firmness: val })}
/>
@@ -82,7 +90,7 @@ class Cervix extends SymptomView {
explainer={labels.position.explainer}
>
<SelectTabGroup
buttons={cervixPositionRadioProps}
buttons={this.cervixPositionRadioProps}
active={this.state.position}
onSelect={val => this.setState({ position: val })}
/>
@@ -99,18 +107,9 @@ class Cervix extends SymptomView {
value={this.state.exclude}
/>
</SymptomSection>
</ScrollView>
</SymptomView>
)
}
}
const mapStateToProps = (state) => {
return({
date: getDate(state)
})
}
export default connect(
mapStateToProps,
null
)(Cervix)
export default Cervix
+46 -41
View File
@@ -1,66 +1,71 @@
import React from 'react'
import {
ScrollView
} from 'react-native'
import { connect } from 'react-redux'
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { getDate } from '../../../slices/date'
import styles from '../../../styles'
import { intensity, desire } from '../../../i18n/en/cycle-day'
import SelectTabGroup from '../select-tab-group'
import SymptomSection from './symptom-section'
import SymptomView from './symptom-view'
class Desire extends SymptomView {
import { getLabelsList } from '../../helpers/labels'
import { saveSymptom } from '../../../db'
class Desire extends Component {
static propTypes = {
cycleDay: PropTypes.object,
handleBackButtonPress: PropTypes.func,
date: PropTypes.string.isRequired,
}
constructor(props) {
super(props)
const cycleDay = props.cycleDay
this.desire = cycleDay && cycleDay.desire
const desireValue = this.desire && this.desire.value
this.state = { currentValue: desireValue }
}
const symptom = 'desire'
const { cycleDay } = props
symptomName = 'desire'
const defaultSymptomData = { value: '' }
const symptomData =
cycleDay && cycleDay[symptom] ? cycleDay[symptom] : defaultSymptomData
this.state = { ...symptomData }
this.symptom = symptom
this.desireRadioProps = getLabelsList(intensity)
}
autoSave = () => {
if (typeof this.state.currentValue != 'number') {
this.deleteSymptomEntry()
return
}
this.saveSymptomEntry({ value: this.state.currentValue })
const { date } = this.props
const valuesToSave = { ...this.state }
const hasValueToSave = typeof this.state.value === 'number'
saveSymptom(this.symptom, date, hasValueToSave ? valuesToSave : null)
}
renderContent() {
const desireRadioProps = [
{ label: intensity[0], value: 0 },
{ label: intensity[1], value: 1 },
{ label: intensity[2], value: 2 }
]
componentDidUpdate() {
this.autoSave()
}
render() {
return (
<ScrollView style={styles.page}>
<SymptomView
symptom={this.symptom}
values={this.state}
handleBackButtonPress={this.props.handleBackButtonPress}
date={this.props.date}
>
<SymptomSection
header={desire.header}
explainer={desire.explainer}
>
<SelectTabGroup
buttons={desireRadioProps}
active={this.state.currentValue}
onSelect={val => this.setState({ currentValue: val })}
buttons={this.desireRadioProps}
active={this.state.value}
onSelect={val => this.setState({ value: val })}
/>
</SymptomSection>
</ScrollView>
</SymptomView>
)
}
}
const mapStateToProps = (state) => {
return({
date: getDate(state)
})
}
export default connect(
mapStateToProps,
null
)(Desire)
export default Desire
+47 -41
View File
@@ -1,44 +1,54 @@
import React from 'react'
import {
ScrollView,
TextInput} from 'react-native'
import { connect } from 'react-redux'
import { getDate } from '../../../slices/date'
import React, { Component } from 'react'
import { TextInput } from 'react-native'
import PropTypes from 'prop-types'
import { mood as labels } from '../../../i18n/en/cycle-day'
import SelectBoxGroup from '../select-box-group'
import SymptomSection from './symptom-section'
import styles from '../../../styles'
import SymptomView from './symptom-view'
class Mood extends SymptomView {
import { saveSymptom } from '../../../db'
class Mood extends Component {
static propTypes = {
cycleDay: PropTypes.object,
handleBackButtonPress: PropTypes.func,
date: PropTypes.string.isRequired,
}
constructor(props) {
super(props)
const cycleDay = props.cycleDay
if (cycleDay && cycleDay.mood) {
this.state = Object.assign({}, cycleDay.mood)
} else {
this.state = {}
}
if (this.state.note) {
this.state.other = true
}
}
const symptom = 'mood'
const { cycleDay } = props
symptomName = "mood"
const defaultSymptomData = {}
const symptomData =
cycleDay && cycleDay[symptom] ? cycleDay[symptom] : defaultSymptomData
this.state = { ...symptomData }
// We make sure other is always true when there is a note,
// e.g. when import is messed up.
if (this.state.note) this.state.other = true
this.symptom = symptom
}
autoSave = () => {
const { date } = this.props
const valuesToSave = Object.assign({}, this.state)
if (!valuesToSave.other) {
valuesToSave.note = null
}
const nothingEntered = Object.values(this.state).every(val => !val)
if (nothingEntered) {
this.deleteSymptomEntry()
return
saveSymptom(this.symptom, date, nothingEntered ? null : valuesToSave)
}
const copyOfState = Object.assign({}, this.state)
if (!copyOfState.other) {
copyOfState.note = null
}
this.saveSymptomEntry(copyOfState)
componentDidUpdate() {
this.autoSave()
}
toggleState = (key) => {
@@ -49,9 +59,14 @@ class Mood extends SymptomView {
}
}
renderContent() {
render() {
return (
<ScrollView style={styles.page}>
<SymptomView
symptom={this.symptom}
values={this.state}
handleBackButtonPress={this.props.handleBackButtonPress}
date={this.props.date}
>
<SymptomSection
explainer={labels.explainer}
>
@@ -72,18 +87,9 @@ class Mood extends SymptomView {
/>
}
</SymptomSection>
</ScrollView>
</SymptomView>
)
}
}
const mapStateToProps = (state) => {
return({
date: getDate(state)
})
}
export default connect(
mapStateToProps,
null
)(Mood)
export default Mood
+53 -53
View File
@@ -1,68 +1,77 @@
import React from 'react'
import {
Switch,
ScrollView
} from 'react-native'
import { connect } from 'react-redux'
import React, { Component } from 'react'
import { Switch } from 'react-native'
import PropTypes from 'prop-types'
import { getDate } from '../../../slices/date'
import styles from '../../../styles'
import { mucus as labels } from '../../../i18n/en/cycle-day'
import computeNfpValue from '../../../lib/nfp-mucus'
import SelectTabGroup from '../select-tab-group'
import SymptomSection from './symptom-section'
import SymptomView from './symptom-view'
class Mucus extends SymptomView {
import { getLabelsList } from '../../helpers/labels'
import { saveSymptom } from '../../../db'
class Mucus extends Component {
static propTypes = {
cycleDay: PropTypes.object,
handleBackButtonPress: PropTypes.func,
date: PropTypes.string.isRequired,
}
constructor(props) {
super(props)
const cycleDay = props.cycleDay
this.mucus = cycleDay && cycleDay.mucus
this.state = this.mucus ? this.mucus : {}
const symptom = 'mucus'
const { cycleDay } = props
const defaultSymptomData = {}
const symptomData =
cycleDay && cycleDay[symptom] ? cycleDay[symptom] : defaultSymptomData
this.state = { ...symptomData }
this.mucusFeeling = getLabelsList(labels.feeling.categories)
this.mucusTexture = getLabelsList(labels.texture.categories)
this.symptom = symptom
}
symptomName = 'mucus'
autoSave = () => {
const nothingEntered = ['feeling', 'texture'].every(val => typeof this.state[val] != 'number')
if (nothingEntered) {
this.deleteSymptomEntry()
return
}
const feeling = this.state.feeling
const texture = this.state.texture
this.saveSymptomEntry({
shouldAutoSave = () => {
const { date } = this.props
const nothingEntered = ['feeling', 'texture'].every(
val => typeof this.state[val] !== 'number'
)
const { feeling, texture, exclude} = this.state
const valuesToSave = {
feeling,
texture,
value: computeNfpValue(feeling, texture),
exclude: Boolean(this.state.exclude)
})
exclude: Boolean(exclude)
}
saveSymptom(this.symptom, date, nothingEntered ? null : valuesToSave)
}
renderContent() {
const mucusFeeling = [
{ label: labels.feeling.categories[0], value: 0 },
{ label: labels.feeling.categories[1], value: 1 },
{ label: labels.feeling.categories[2], value: 2 },
{ label: labels.feeling.categories[3], value: 3 }
]
const mucusTexture = [
{ label: labels.texture.categories[0], value: 0 },
{ label: labels.texture.categories[1], value: 1 },
{ label: labels.texture.categories[2], value: 2 }
]
componentDidUpdate() {
this.shouldAutoSave()
}
render() {
// TODO leaving this info for notice when leaving incomplete data
// const mandatoryNotCompletedYet = typeof this.state.feeling != 'number' || typeof this.state.texture != 'number'
return (
<ScrollView style={styles.page}>
<SymptomView
symptom={this.symptom}
values={this.state}
handleBackButtonPress={this.props.handleBackButtonPress}
date={this.props.date}
>
<SymptomSection
header='Feeling'
explainer={labels.feeling.explainer}
>
<SelectTabGroup
buttons={mucusFeeling}
buttons={this.mucusFeeling}
onSelect={val => this.setState({ feeling: val })}
active={this.state.feeling}
/>
@@ -72,7 +81,7 @@ class Mucus extends SymptomView {
explainer={labels.texture.explainer}
>
<SelectTabGroup
buttons={mucusTexture}
buttons={this.mucusTexture}
onSelect={val => this.setState({ texture: val })}
active={this.state.texture}
/>
@@ -89,18 +98,9 @@ class Mucus extends SymptomView {
value={this.state.exclude}
/>
</SymptomSection>
</ScrollView>
</SymptomView>
)
}
}
const mapStateToProps = (state) => {
return({
date: getDate(state)
})
}
export default connect(
mapStateToProps,
null
)(Mucus)
export default Mucus
+34 -40
View File
@@ -1,70 +1,64 @@
import React from 'react'
import {
ScrollView,
TextInput,
} from 'react-native'
import { connect } from 'react-redux'
import React, { Component } from 'react'
import { TextInput } from 'react-native'
import { getDate } from '../../../slices/date'
import styles from '../../../styles'
import SymptomSection from './symptom-section'
import { noteExplainer } from '../../../i18n/en/cycle-day'
import { shared as sharedLabels } from '../../../i18n/en/labels'
import SymptomView from './symptom-view'
class Note extends SymptomView {
import { saveSymptom } from '../../../db'
class Note extends Component {
constructor(props) {
super(props)
const cycleDay = props.cycleDay
this.note = cycleDay && cycleDay.note
const symptom = 'note'
const { cycleDay } = props
this.state = {
currentValue: this.note && this.note.value || ''
}
}
const defaultSymptomData = { value: '' }
symptomName = 'note'
const symptomData =
cycleDay && cycleDay[symptom] ? cycleDay[symptom] : defaultSymptomData
this.state = { ...symptomData }
this.symptom = symptom
}
autoSave = () => {
if (!this.state.currentValue) {
this.deleteSymptomEntry()
return
}
this.saveSymptomEntry({
value: this.state.currentValue
})
const { date } = this.props
const valuesToSave = { ...this.state }
saveSymptom(this.symptom, date, this.state.value ? valuesToSave : null)
}
renderContent() {
componentDidUpdate() {
this.autoSave()
}
render() {
return (
<ScrollView style={styles.page}>
<SymptomView
symptom={this.symptom}
values={this.state}
handleBackButtonPress={this.props.handleBackButtonPress}
date={this.props.date}
>
<SymptomSection
explainer={noteExplainer}
>
<TextInput
autoFocus={!this.state.currentValue}
autoFocus={true}
multiline={true}
placeholder={sharedLabels.enter}
onChangeText={(val) => {
this.setState({ currentValue: val })
this.setState({ value: val })
}}
value={this.state.currentValue}
value={this.state.value}
testID='noteInput'
/>
</SymptomSection>
</ScrollView>
</SymptomView>
)
}
}
const mapStateToProps = (state) => {
return({
date: getDate(state)
})
}
export default connect(
mapStateToProps,
null
)(Note)
export default Note
+47 -42
View File
@@ -1,47 +1,55 @@
import React from 'react'
import {
ScrollView,
TextInput,
} from 'react-native'
import { connect } from 'react-redux'
import { getDate } from '../../../slices/date'
import React, { Component } from 'react'
import { TextInput } from 'react-native'
import PropTypes from 'prop-types'
import { pain as labels } from '../../../i18n/en/cycle-day'
import { shared as sharedLabels } from '../../../i18n/en/labels'
import SelectBoxGroup from '../select-box-group'
import SymptomSection from './symptom-section'
import styles from '../../../styles'
import SymptomView from './symptom-view'
class Pain extends SymptomView {
import { saveSymptom } from '../../../db'
class Pain extends Component {
static propTypes = {
cycleDay: PropTypes.object,
handleBackButtonPress: PropTypes.func,
date: PropTypes.string.isRequired,
}
constructor(props) {
super(props)
const cycleDay = props.cycleDay
if (cycleDay && cycleDay.pain) {
this.state = Object.assign({}, cycleDay.pain)
} else {
this.state = {}
}
if (this.state.note) {
this.state.other = true
}
}
const symptom = 'pain'
const { cycleDay } = props
symptomName = 'pain'
const defaultSymptomData = {}
const symptomData =
cycleDay && cycleDay[symptom] ? cycleDay[symptom] : defaultSymptomData
this.state = { ...symptomData }
// We make sure other is always true when there is a note,
// e.g. when import is messed up.
if (this.state.note) this.state.other = true
this.symptom = symptom
}
autoSave = () => {
const { date } = this.props
const valuesToSave = Object.assign({}, this.state)
if (!valuesToSave.other) {
valuesToSave.note = null
}
const nothingEntered = Object.values(this.state).every(val => !val)
if (nothingEntered) {
this.deleteSymptomEntry()
return
saveSymptom(this.symptom, date, nothingEntered ? null : valuesToSave)
}
const copyOfState = Object.assign({}, this.state)
if (!copyOfState.other) {
copyOfState.note = null
}
this.saveSymptomEntry(copyOfState)
componentDidUpdate() {
this.autoSave()
}
toggleState = (key) => {
@@ -52,9 +60,14 @@ class Pain extends SymptomView {
}
}
renderContent() {
render() {
return (
<ScrollView style={styles.page}>
<SymptomView
symptom={this.symptom}
values={this.state}
handleBackButtonPress={this.props.handleBackButtonPress}
date={this.props.date}
>
<SymptomSection
explainer={labels.explainer}
>
@@ -75,17 +88,9 @@ class Pain extends SymptomView {
/>
}
</SymptomSection>
</ScrollView>)
</SymptomView>
)
}
}
const mapStateToProps = (state) => {
return({
date: getDate(state)
})
}
export default connect(
mapStateToProps,
null
)(Pain)
export default Pain
+43 -39
View File
@@ -1,47 +1,55 @@
import React from 'react'
import {
TextInput,
ScrollView
} from 'react-native'
import { connect } from 'react-redux'
import React, { Component } from 'react'
import { TextInput } from 'react-native'
import PropTypes from 'prop-types'
import { getDate } from '../../../slices/date'
import styles from '../../../styles'
import { sex as sexLabels, contraceptives as contraceptivesLabels } from '../../../i18n/en/cycle-day'
import { shared as sharedLabels } from '../../../i18n/en/labels'
import SelectBoxGroup from '../select-box-group'
import SymptomSection from './symptom-section'
import SymptomView from './symptom-view'
class Sex extends SymptomView {
import { saveSymptom } from '../../../db'
class Sex extends Component {
static propTypes = {
cycleDay: PropTypes.object,
handleBackButtonPress: PropTypes.func,
date: PropTypes.string.isRequired,
}
constructor(props) {
super(props)
const cycleDay = props.cycleDay
if (cycleDay && cycleDay.sex) {
this.state = Object.assign({}, cycleDay.sex)
} else {
this.state = {}
}
const symptom = 'sex'
const { cycleDay } = props
const defaultSymptomData = {}
const symptomData =
cycleDay && cycleDay[symptom] ? cycleDay[symptom] : defaultSymptomData
this.state = { ...symptomData }
// We make sure other is always true when there is a note,
// e.g. when import is messed up.
if (this.state.note) this.state.other = true
}
symptomName = "sex"
this.symptom = symptom
}
autoSave = () => {
const { date } = this.props
const valuesToSave = Object.assign({}, this.state)
if (!valuesToSave.other) {
valuesToSave.note = null
}
const nothingEntered = Object.values(this.state).every(val => !val)
if (nothingEntered) {
this.deleteSymptomEntry()
return
saveSymptom(this.symptom, date, nothingEntered ? null : valuesToSave)
}
const copyOfState = Object.assign({}, this.state)
if (!copyOfState.other) {
copyOfState.note = null
}
this.saveSymptomEntry(copyOfState)
componentDidUpdate() {
this.autoSave()
}
toggleState = (key) => {
@@ -52,9 +60,14 @@ class Sex extends SymptomView {
}
}
renderContent() {
render() {
return (
<ScrollView style={styles.page}>
<SymptomView
symptom={this.symptom}
values={this.state}
handleBackButtonPress={this.props.handleBackButtonPress}
date={this.props.date}
>
<SymptomSection
header={sexLabels.header}
explainer={sexLabels.explainer}
@@ -87,18 +100,9 @@ class Sex extends SymptomView {
}}
/>
}
</ScrollView>
</SymptomView>
)
}
}
const mapStateToProps = (state) => {
return({
date: getDate(state)
})
}
export default connect(
mapStateToProps,
null
)(Sex)
export default Sex
@@ -0,0 +1,41 @@
import React, { Component } from 'react'
import { TouchableOpacity } from 'react-native'
import PropTypes from 'prop-types'
import Icon from 'react-native-vector-icons/Entypo'
import InfoPopUp from './info-symptom'
import styles, { iconStyles } from '../../../styles'
export default class SymptomInfo extends Component {
static propTypes = {
symptom: PropTypes.string
}
constructor() {
super()
this.state = { showInfo: false }
}
showInfo = () => this.setState({ showInfo: true })
hideInfo = () => this.setState({ showInfo: false })
render() {
return (
<React.Fragment>
<TouchableOpacity
onPress={this.showInfo}
style={styles.infoButtonSymptomView}
testID="symptomInfoButton"
>
<Icon name="info-with-circle" style={iconStyles.info} />
</TouchableOpacity>
{ this.state.showInfo &&
<InfoPopUp symptom={this.props.symptom} close={this.hideInfo} />
}
</React.Fragment>
)
}
}
+76 -64
View File
@@ -1,102 +1,114 @@
import React, { Component } from 'react'
import {
View, Alert, TouchableOpacity
} from 'react-native'
import { ScrollView, View, Alert } from 'react-native'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { getDate } from '../../../slices/date'
import { saveSymptom } from '../../../db'
import InfoPopUp from './info-symptom'
import Header from '../../header/symptom-view'
import SymptomInfo from './symptom-info'
import { headerTitles } from '../../../i18n/en/labels'
import { sharedDialogs } from '../../../i18n/en/cycle-day'
import Icon from 'react-native-vector-icons/Entypo'
import styles, { iconStyles } from '../../../styles'
import styles from '../../../styles'
class SymptomView extends Component {
constructor(props, symptomName) {
static propTypes = {
symptom: PropTypes.string.isRequired,
values: PropTypes.object,
date: PropTypes.string,
}
constructor(props) {
super()
this.symptomName = symptomName
this.values = props.values
this.state = {
shouldShowDelete: this.checkIfHasValuesToDelete()
}
this.date = props.date
this.navigate = props.navigate
this.state = {
showInfo: false
}
}
componentDidUpdate() {
this.autoSave()
const shouldShowDelete = this.checkIfHasValuesToDelete()
if (shouldShowDelete !== this.state.shouldShowDelete) {
this.setState({ shouldShowDelete: this.checkIfHasValuesToDelete() })
}
saveSymptomEntry(entry) {
saveSymptom(this.symptomName, this.date, entry)
}
deleteSymptomEntry() {
saveSymptom(this.symptomName, this.date)
const { symptom, date } = this.props
saveSymptom(symptom, date, null)
}
isDeleteIconActive() {
const symptomValueHasBeenFilledOut = key => {
// the state tracks whether the symptom info should be shown,
// we ignore that property
if (key === 'showInfo') return
checkIfHasValuesToDelete() {
const valueHasBeenFilledOut = key => {
// is there any meaningful value in the current state?
return this.state[key] || this.state[key] === 0
return this.values[key] || this.values[key] === 0
}
const symptomValues = Object.keys(this.state)
const valuesKeys = Object.keys(this.values)
return symptomValues.some(symptomValueHasBeenFilledOut)
return valuesKeys.some(valueHasBeenFilledOut)
}
render() {
return (
<View style={{flex: 1}}>
<Header
title={headerTitles[this.symptomName].toLowerCase()}
date={this.date}
goBack={this.props.handleBackButtonPress}
deleteIconActive={this.isDeleteIconActive()}
deleteEntry={() => {
Alert.alert(
sharedDialogs.areYouSureTitle,
sharedDialogs.areYouSureToDelete,
[{
text: sharedDialogs.cancel,
style: 'cancel'
}, {
text: sharedDialogs.reallyDeleteData,
onPress: () => {
onDeleteConfirmation = () => {
this.deleteSymptomEntry()
this.props.handleBackButtonPress()
}
}]
showConfirmationAlert = () => {
const cancelButton = {
text: sharedDialogs.cancel,
style: 'cancel'
}
const confirmationButton = {
text: sharedDialogs.reallyDeleteData,
onPress: this.onDeleteConfirmation
}
return Alert.alert(
sharedDialogs.areYouSureTitle,
sharedDialogs.areYouSureToDelete,
[cancelButton, confirmationButton]
)
}}
}
render() {
const { symptom } = this.props
return (
<View style={{flex: 1}}>
<Header
title={headerTitles[symptom].toLowerCase()}
date={this.date}
goBack={this.props.handleBackButtonPress}
shouldShowDelete={this.state.shouldShowDelete}
onDelete={this.showConfirmationAlert}
/>
<View flex={1}>
{ this.renderContent() }
<TouchableOpacity
onPress={() => {
this.setState({showInfo: true})
}}
style={styles.infoButtonSymptomView}
testID="symptomInfoButton"
>
<Icon
name="info-with-circle"
style={iconStyles.info}
/>
</TouchableOpacity>
{ this.state.showInfo &&
<InfoPopUp
symptom={this.symptomName}
close={() => this.setState({showInfo: false})}
/>
}
<ScrollView style={styles.page}>
{this.props.children}
</ScrollView>
<SymptomInfo symptom={symptom} />
</View>
</View>
)
}
}
export default SymptomView
const mapStateToProps = (state) => {
return({
date: getDate(state)
})
}
export default connect(
mapStateToProps,
null
)(SymptomView)
+45 -44
View File
@@ -1,11 +1,7 @@
import React from 'react'
import { Switch, ScrollView } from 'react-native'
import React, { Component } from 'react'
import { Switch } from 'react-native'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { getDate } from '../../../slices/date'
import styles from '../../../styles'
import { LocalTime, ChronoUnit } from 'js-joda'
import { temperature as labels } from '../../../i18n/en/cycle-day'
import { shared as sharedLabels } from '../../../i18n/en/labels'
@@ -16,32 +12,38 @@ import SymptomView from './symptom-view'
import TimeInput from './time-input'
import TemperatureInput from './temperature-input'
import { saveSymptom } from '../../../db'
const minutes = ChronoUnit.MINUTES
class Temperature extends SymptomView {
class Temperature extends Component {
static propTypes = {
cycleDay: PropTypes.object,
handleBackButtonPress: PropTypes.func,
date: PropTypes.string,
date: PropTypes.string.isRequired,
}
constructor(props) {
super(props)
const cycleDay = props.cycleDay
this.temperature = cycleDay && cycleDay.temperature
const symptom = 'temperature'
const { cycleDay } = props
const temp = this.temperature
this.state = {
exclude: temp ? temp.exclude : false,
time: temp ? temp.time : LocalTime.now().truncatedTo(minutes).toString(),
temperature: temp ? temp.value : null,
note: temp ? temp.note : null
}
const defaultSymptomData = {
time: LocalTime.now().truncatedTo(minutes).toString(),
temperature: null,
note: '',
exclude: false
}
symptomName = 'temperature'
const symptomData =
cycleDay && cycleDay[symptom] ? cycleDay[symptom] : defaultSymptomData
const { value, ...restSymptomData } = symptomData
this.state = { temperature: value, ...restSymptomData }
this.symptom = symptom
}
isDeleteIconActive() {
return ['temperature', 'note', 'exclude'].some(key => {
@@ -52,19 +54,19 @@ class Temperature extends SymptomView {
}
autoSave = () => {
if (!this.state.temperature) {
this.deleteSymptomEntry()
return
const { date } = this.props
const { temperature, exclude, time, note } = this.state
console.log('/// autoSave state: ', this.state)
const valuesToSave = {
value: temperature,
exclude,
time,
note
}
const dataToSave = {
value: this.state.temperature,
exclude: this.state.exclude,
time: this.state.time,
note: this.state.note
}
this.saveSymptomEntry(dataToSave)
saveSymptom(this.symptom, date, temperature ? valuesToSave : null)
}
setTime = (time) => {
@@ -79,11 +81,20 @@ class Temperature extends SymptomView {
this.setState({ note })
}
renderContent() {
componentDidUpdate() {
this.autoSave()
}
render() {
const { temperature } = this.state
return (
<ScrollView style={styles.page}>
<SymptomView
symptom={'temperature'}
values={this.state}
handleBackButtonPress={this.props.handleBackButtonPress}
date={this.props.date}
>
<SymptomSection
header={labels.temperature.header}
explainer={labels.temperature.explainer}
@@ -106,7 +117,6 @@ class Temperature extends SymptomView {
>
<AppTextInput
multiline={true}
autoFocus={this.state.focusTextArea}
placeholder={sharedLabels.enter}
value={this.state.note}
onChangeText={this.setNote}
@@ -125,18 +135,9 @@ class Temperature extends SymptomView {
value={this.state.exclude}
/>
</SymptomSection>
</ScrollView>
</SymptomView>
)
}
}
const mapStateToProps = (state) => {
return({
date: getDate(state)
})
}
export default connect(
mapStateToProps,
null
)(Temperature)
export default Temperature
@@ -21,7 +21,6 @@ export default class TimeInput extends Component {
this.state = {
isTimePickerVisible: false,
}
}
showTimePicker = () => {
+24 -21
View File
@@ -5,12 +5,23 @@ import {
TouchableOpacity,
Dimensions
} from 'react-native'
import PropTypes from 'prop-types'
import styles, { iconStyles } from '../../styles'
import Icon from 'react-native-vector-icons/AntDesign'
import NavigationArrow from './navigation-arrow'
import formatDate from '../helpers/format-date'
SymptomViewHeader.propTypes = {
title: PropTypes.string,
date: PropTypes.string,
goBack: PropTypes.func,
deleteIconActive: PropTypes.bool,
onDelete: PropTypes.func,
}
export default function SymptomViewHeader(props) {
const { goBack, title, date, shouldShowDelete, onDelete } = props
const middle = Dimensions.get('window').width / 2
return (
<View style={[styles.header, styles.headerCycleDay, styles.headerSymptom]}>
@@ -18,34 +29,26 @@ export default function SymptomViewHeader(props) {
style={styles.accentCircle}
left={middle - styles.accentCircle.width / 2}
/>
<NavigationArrow
direction='left'
{...props}
/>
<NavigationArrow direction='left' goBack={goBack} />
<View>
<Text style={styles.dateHeader} testID='symptomViewTitleName'>
{props.title}
{title}
</Text>
{ props.date &&
{ date &&
<Text style={styles.cycleDayNumber} testID='symptomViewTitleDate'>
{formatDate(props.date)}
{formatDate(date)}
</Text>
}
</View >
{ props.deleteIconActive &&
<TouchableOpacity
onPress={props.deleteEntry}
style={[
styles.headerDeleteButton,
]}
>
<Icon
name="delete"
{...iconStyles.symptomHeaderIcons}
/>
</TouchableOpacity>
}
{ shouldShowDelete && <DeleteButton onDelete={onDelete} />}
</View>
)
}
const DeleteButton = ({ onDelete }) => {
return (
<TouchableOpacity onPress={onDelete} style={styles.headerDeleteButton}>
<Icon name="delete" {...iconStyles.symptomHeaderIcons} />
</TouchableOpacity>
)
}
+2
View File
@@ -0,0 +1,2 @@
export const getLabelsList =
(categories) => categories.map((label, i) => ({ label, value: i }))
+3
View File
@@ -105,6 +105,9 @@ describe('Symptom Data Input', () => {
case 'temperature':
await enterTemperature()
expectedSymptomSummary = formExpectedSymptomSummary('temperature')
console.log(
'This test a bit flaky. console.log apparently helps to fix it.'
)
break
case 'note':
await enterNote()
+2006 -2004
View File
File diff suppressed because it is too large Load Diff