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