Merge branch '363-alert-for-cervix-and-mucus-when-trying-to-navigate-away-and-one-value-is-missing' into 'master'

Basic auto save

Closes #363

See merge request bloodyhealth/drip!214
This commit is contained in:
Julia Friesel
2019-05-13 18:56:40 +00:00
22 changed files with 696 additions and 558 deletions
+5 -12
View File
@@ -125,19 +125,12 @@ export default class App extends Component {
goBack={this.handleBackButtonPress}
/>
}
{this.isSymptomView() &&
<Header
title={title}
isSymptomView={true}
goBack={this.handleBackButtonPress}
date={currentProps.date}
goToSymptomInfo={() => this.navigate(INFO_SYMPTOM_PAGE, {
symptomView: currentPage,
...currentProps
})}
/>}
<Page navigate={this.navigate} {...currentProps} />
<Page
navigate={this.navigate}
{...currentProps}
handleBackButtonPress={this.handleBackButtonPress}
/>
{!this.isSymptomView() &&
<Menu navigate={this.navigate} currentPage={currentPage} />
@@ -1,7 +1,6 @@
import React, { Component } from 'react'
import {
View, TouchableOpacity, Text, Alert, ToastAndroid
} from 'react-native'
View, TouchableOpacity, Text, Alert} from 'react-native'
import Icon from 'react-native-vector-icons/MaterialCommunityIcons'
import { saveSymptom } from '../../../db'
import styles, {iconStyles} from '../../../styles'
@@ -14,10 +13,8 @@ export default class ActionButtonFooter extends Component {
symptom,
currentSymptomValue,
date,
saveAction,
saveDisabled,
navigate,
autoShowDayView = true}
}
= this.props
const navigateToOverView = () => navigate('CycleDay', {date})
const buttons = [
@@ -44,43 +41,19 @@ export default class ActionButtonFooter extends Component {
(Object.values(currentSymptomValue).every(x => !x) && currentSymptomValue.constructor === Object)
),
icon: 'delete-outline'
}, {
title: labels.save,
action: () => {
if(saveDisabled) {
ToastAndroid.show(labels.disabledInfo, ToastAndroid.LONG)
} else {
saveAction()
if (autoShowDayView) navigateToOverView()
}
},
disabledCondition: saveDisabled,
icon: 'content-save-outline'
}
]
return (
<View style={styles.actionButtonFooter}>
{buttons.map(({ title, action, disabledCondition, icon }, i) => {
<View style={styles.menu}>
{buttons.map(({ title, action, icon }, i) => {
const textStyle = [styles.menuText]
if (disabledCondition) {
textStyle.push(styles.menuTextInActive)
}
const iconStyle = disabledCondition ?
Object.assign(
{},
iconStyles.menuIcon,
iconStyles.menuIconInactive
)
:
iconStyles.menuIcon
return (
<TouchableOpacity
onPress={action}
style={styles.actionButtonItem}
key={i.toString()}
>
<Icon name={icon} {...iconStyle} />
<Icon name={icon} {...iconStyles.menuIcon} />
<Text style={textStyle}>
{title.toLowerCase()}
</Text>
+17 -22
View File
@@ -1,29 +1,39 @@
import React, { Component } from 'react'
import React from 'react'
import {
View,
Switch,
ScrollView
} from 'react-native'
import styles from '../../../styles'
import { saveSymptom } from '../../../db'
import { bleeding } from '../../../i18n/en/cycle-day'
import ActionButtonFooter from './action-button-footer'
import SelectTabGroup from '../select-tab-group'
import SymptomSection from './symptom-section'
import SymptomView from './symptom-view'
export default class Bleeding extends Component {
export default class Bleeding extends SymptomView {
constructor(props) {
super(props)
const cycleDay = props.cycleDay
this.bleeding = cycleDay && cycleDay.bleeding
this.makeActionButtons = props.makeActionButtons
this.state = {
currentValue: this.bleeding && this.bleeding.value,
exclude: this.bleeding ? this.bleeding.exclude : false
}
}
render() {
symptomName = 'bleeding'
onBackButtonPress() {
if (typeof this.state.currentValue != 'number') {
this.deleteSymptomEntry()
return
}
this.saveSymptomEntry({
value: this.state.currentValue,
exclude: this.state.exclude
})
}
renderContent() {
const bleedingRadioProps = [
{ label: bleeding.labels[0], value: 0 },
{ label: bleeding.labels[1], value: 1 },
@@ -31,7 +41,6 @@ export default class Bleeding extends Component {
{ label: bleeding.labels[3], value: 3 },
]
return (
<View style={{ flex: 1 }}>
<ScrollView style={styles.page}>
<SymptomSection
header={bleeding.heaviness.header}
@@ -56,20 +65,6 @@ export default class Bleeding extends Component {
/>
</SymptomSection>
</ScrollView>
<ActionButtonFooter
symptom='bleeding'
date={this.props.date}
currentSymptomValue={this.bleeding}
saveAction={() => {
saveSymptom('bleeding', this.props.date, {
value: this.state.currentValue,
exclude: this.state.exclude
})
}}
saveDisabled={typeof this.state.currentValue != 'number'}
navigate={this.props.navigate}
/>
</View>
)
}
}
+23 -27
View File
@@ -1,27 +1,40 @@
import React, { Component } from 'react'
import React from 'react'
import {
View,
Switch,
ScrollView
} from 'react-native'
import styles from '../../../styles'
import { saveSymptom } from '../../../db'
import { cervix as labels } from '../../../i18n/en/cycle-day'
import ActionButtonFooter from './action-button-footer'
import SelectTabGroup from '../select-tab-group'
import SymptomSection from './symptom-section'
import { ActionHint } from '../../app-text'
import SymptomView from './symptom-view'
export default class Cervix extends Component {
export default class Cervix extends SymptomView {
constructor(props) {
super(props)
const cycleDay = props.cycleDay
this.cervix = cycleDay && cycleDay.cervix
this.makeActionButtons = props.makeActionButtons
this.state = this.cervix ? this.cervix : {}
}
render() {
symptomName = 'cervix'
onBackButtonPress() {
const nothingEntered = ['opening', 'firmness', 'position'].every(val => typeof this.state[val] != 'number')
if (nothingEntered) {
this.deleteSymptomEntry()
return
}
this.saveSymptomEntry({
opening: this.state.opening,
firmness: this.state.firmness,
position: this.state.position,
exclude: Boolean(this.state.exclude)
})
}
renderContent() {
const cervixOpeningRadioProps = [
{ label: labels.opening.categories[0], value: 0 },
{ label: labels.opening.categories[1], value: 1 },
@@ -36,9 +49,9 @@ export default class Cervix extends Component {
{ label: labels.position.categories[1], value: 1 },
{ label: labels.position.categories[2], value: 2 }
]
const mandatoryNotCompletedYet = typeof this.state.opening != 'number' || typeof this.state.firmness != 'number'
// TODO saving this info for notice when leaving incomplete data
// const mandatoryNotCompleted = typeof this.state.opening != 'number' || typeof this.state.firmness != 'number'
return (
<View style={{ flex: 1 }}>
<ScrollView style={styles.page}>
<SymptomSection
header="Opening"
@@ -83,23 +96,6 @@ export default class Cervix extends Component {
/>
</SymptomSection>
</ScrollView>
<ActionHint isVisible={mandatoryNotCompletedYet}>{labels.actionHint}</ActionHint>
<ActionButtonFooter
symptom='cervix'
date={this.props.date}
currentSymptomValue={this.cervix}
saveAction={() => {
saveSymptom('cervix', this.props.date, {
opening: this.state.opening,
firmness: this.state.firmness,
position: this.state.position,
exclude: Boolean(this.state.exclude)
})
}}
saveDisabled={mandatoryNotCompletedYet}
navigate={this.props.navigate}
/>
</View>
)
}
}
+14 -19
View File
@@ -1,33 +1,39 @@
import React, { Component } from 'react'
import React from 'react'
import {
View,
ScrollView
} from 'react-native'
import styles from '../../../styles'
import { saveSymptom } from '../../../db'
import { intensity, desire } from '../../../i18n/en/cycle-day'
import ActionButtonFooter from './action-button-footer'
import SelectTabGroup from '../select-tab-group'
import SymptomSection from './symptom-section'
import SymptomView from './symptom-view'
export default class Desire extends Component {
export default class Desire extends SymptomView {
constructor(props) {
super(props)
const cycleDay = props.cycleDay
this.desire = cycleDay && cycleDay.desire
this.makeActionButtons = props.makeActionButtons
const desireValue = this.desire && this.desire.value
this.state = { currentValue: desireValue }
}
render() {
symptomName = 'desire'
onBackButtonPress() {
if (typeof this.state.currentValue != 'number') {
this.deleteSymptomEntry()
return
}
this.saveSymptomEntry({ value: this.state.currentValue })
}
renderContent() {
const desireRadioProps = [
{ label: intensity[0], value: 0 },
{ label: intensity[1], value: 1 },
{ label: intensity[2], value: 2 }
]
return (
<View style={{ flex: 1 }}>
<ScrollView style={styles.page}>
<SymptomSection
header={desire.header}
@@ -40,17 +46,6 @@ export default class Desire extends Component {
/>
</SymptomSection>
</ScrollView>
<ActionButtonFooter
symptom='desire'
date={this.props.date}
currentSymptomValue={this.desire}
saveAction={() => {
saveSymptom('desire', this.props.date, { value: this.state.currentValue })
}}
saveDisabled={typeof this.state.currentValue != 'number'}
navigate={this.props.navigate}
/>
</View>
)
}
}
+20 -24
View File
@@ -1,17 +1,14 @@
import React, { Component } from 'react'
import React from 'react'
import {
ScrollView,
TextInput,
View
} from 'react-native'
import { saveSymptom } from '../../../db'
TextInput} from 'react-native'
import { mood as labels } from '../../../i18n/en/cycle-day'
import ActionButtonFooter from './action-button-footer'
import SelectBoxGroup from '../select-box-group'
import SymptomSection from './symptom-section'
import styles from '../../../styles'
import SymptomView from './symptom-view'
export default class Mood extends Component {
export default class Mood extends SymptomView {
constructor(props) {
super(props)
const cycleDay = props.cycleDay
@@ -25,6 +22,21 @@ export default class Mood extends Component {
}
}
symptomName = "mood"
onBackButtonPress() {
const nothingEntered = Object.values(this.state).every(val => !val)
if (nothingEntered) {
this.deleteSymptomEntry()
return
}
const copyOfState = Object.assign({}, this.state)
if (!copyOfState.other) {
copyOfState.note = null
}
this.saveSymptomEntry(copyOfState)
}
toggleState = (key) => {
const curr = this.state[key]
this.setState({[key]: !curr})
@@ -33,9 +45,8 @@ export default class Mood extends Component {
}
}
render() {
renderContent() {
return (
<View style={{ flex: 1 }}>
<ScrollView style={styles.page}>
<SymptomSection
explainer={labels.explainer}
@@ -58,21 +69,6 @@ export default class Mood extends Component {
}
</SymptomSection>
</ScrollView>
<ActionButtonFooter
symptom='mood'
date={this.props.date}
currentSymptomValue={this.state}
saveAction={() => {
const copyOfState = Object.assign({}, this.state)
if (!copyOfState.other) {
copyOfState.note = null
}
saveSymptom('mood', this.props.date, copyOfState)
}}
saveDisabled={Object.values(this.state).every(value => !value)}
navigate={this.props.navigate}
/>
</View>
)
}
}
+25 -29
View File
@@ -1,28 +1,43 @@
import React, { Component } from 'react'
import React from 'react'
import {
View,
Switch,
ScrollView
} from 'react-native'
import styles from '../../../styles'
import { saveSymptom } from '../../../db'
import { mucus as labels } from '../../../i18n/en/cycle-day'
import computeNfpValue from '../../../lib/nfp-mucus'
import ActionButtonFooter from './action-button-footer'
import SelectTabGroup from '../select-tab-group'
import SymptomSection from './symptom-section'
import { ActionHint } from '../../app-text'
import SymptomView from './symptom-view'
export default class Mucus extends Component {
export default class Mucus extends SymptomView {
constructor(props) {
super(props)
const cycleDay = props.cycleDay
this.mucus = cycleDay && cycleDay.mucus
this.makeActionButtons = props.makeActionButtons
this.state = this.mucus ? this.mucus : {}
}
render() {
symptomName = 'mucus'
onBackButtonPress() {
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({
feeling,
texture,
value: computeNfpValue(feeling, texture),
exclude: Boolean(this.state.exclude)
})
}
renderContent() {
const mucusFeeling = [
{ label: labels.feeling.categories[0], value: 0 },
{ label: labels.feeling.categories[1], value: 1 },
@@ -34,9 +49,9 @@ export default class Mucus extends Component {
{ label: labels.texture.categories[1], value: 1 },
{ label: labels.texture.categories[2], value: 2 }
]
const mandatoryNotCompletedYet = typeof this.state.feeling != 'number' || typeof this.state.texture != 'number'
// TODO leaving this info for notice when leaving incomplete data
// const mandatoryNotCompletedYet = typeof this.state.feeling != 'number' || typeof this.state.texture != 'number'
return (
<View style={{ flex: 1 }}>
<ScrollView style={styles.page}>
<SymptomSection
header='Feeling'
@@ -71,25 +86,6 @@ export default class Mucus extends Component {
/>
</SymptomSection>
</ScrollView>
<ActionHint isVisible={mandatoryNotCompletedYet}>{labels.actionHint}</ActionHint>
<ActionButtonFooter
symptom='mucus'
date={this.props.date}
currentSymptomValue={this.mucus}
saveAction={() => {
const feeling = this.state.feeling
const texture = this.state.texture
saveSymptom('mucus', this.props.date, {
feeling,
texture,
value: computeNfpValue(feeling, texture),
exclude: Boolean(this.state.exclude)
})
}}
saveDisabled={mandatoryNotCompletedYet}
navigate={this.props.navigate}
/>
</View>
)
}
}
+16 -21
View File
@@ -1,32 +1,40 @@
import React, { Component } from 'react'
import React from 'react'
import {
View,
ScrollView,
TextInput,
} from 'react-native'
import styles from '../../../styles'
import { saveSymptom } from '../../../db'
import ActionButtonFooter from './action-button-footer'
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'
export default class Note extends Component {
export default class Note extends SymptomView {
constructor(props) {
super(props)
const cycleDay = props.cycleDay
this.note = cycleDay && cycleDay.note
this.makeActionButtons = props.makeActionButtons
this.state = {
currentValue: this.note && this.note.value || ''
}
}
render() {
symptomName = 'note'
onBackButtonPress() {
if (!this.state.currentValue) {
this.deleteSymptomEntry()
return
}
this.saveSymptomEntry({
value: this.state.currentValue
})
}
renderContent() {
return (
<View style={{ flex: 1 }}>
<ScrollView style={styles.page}>
<SymptomSection
explainer={noteExplainer}
@@ -42,19 +50,6 @@ export default class Note extends Component {
/>
</SymptomSection>
</ScrollView>
<ActionButtonFooter
symptom='note'
date={this.props.date}
currentSymptomValue={this.note}
saveAction={() => {
saveSymptom('note', this.props.date, {
value: this.state.currentValue
})
}}
saveDisabled={!this.state.currentValue}
navigate={this.props.navigate}
/>
</View>
)
}
}
+21 -24
View File
@@ -1,18 +1,16 @@
import React, { Component } from 'react'
import React from 'react'
import {
ScrollView,
TextInput,
View
} from 'react-native'
import { saveSymptom } from '../../../db'
import { pain as labels } from '../../../i18n/en/cycle-day'
import { shared as sharedLabels } from '../../../i18n/en/labels'
import ActionButtonFooter from './action-button-footer'
import SelectBoxGroup from '../select-box-group'
import SymptomSection from './symptom-section'
import styles from '../../../styles'
import SymptomView from './symptom-view'
export default class Pain extends Component {
export default class Pain extends SymptomView {
constructor(props) {
super(props)
const cycleDay = props.cycleDay
@@ -26,6 +24,22 @@ export default class Pain extends Component {
}
}
symptomName = 'pain'
onBackButtonPress() {
const nothingEntered = Object.values(this.state).every(val => !val)
if (nothingEntered) {
this.deleteSymptomEntry()
return
}
const copyOfState = Object.assign({}, this.state)
if (!copyOfState.other) {
copyOfState.note = null
}
this.saveSymptomEntry(copyOfState)
}
toggleState = (key) => {
const curr = this.state[key]
this.setState({[key]: !curr})
@@ -34,9 +48,8 @@ export default class Pain extends Component {
}
}
render() {
renderContent() {
return (
<View style={{ flex: 1 }}>
<ScrollView style={styles.page}>
<SymptomSection
explainer={labels.explainer}
@@ -58,22 +71,6 @@ export default class Pain extends Component {
/>
}
</SymptomSection>
</ScrollView>
<ActionButtonFooter
symptom='pain'
date={this.props.date}
currentSymptomValue={this.state}
saveAction={() => {
const copyOfState = Object.assign({}, this.state)
if (!copyOfState.other) {
copyOfState.note = null
}
saveSymptom('pain', this.props.date, copyOfState)
}}
saveDisabled={Object.values(this.state).every(value => !value)}
navigate={this.props.navigate}
/>
</View>
)
</ScrollView>)
}
}
+20 -22
View File
@@ -1,18 +1,16 @@
import React, { Component } from 'react'
import React from 'react'
import {
TextInput,
View,
ScrollView
} from 'react-native'
import styles from '../../../styles'
import { saveSymptom } from '../../../db'
import { sex as sexLabels, contraceptives as contraceptivesLabels } from '../../../i18n/en/cycle-day'
import { shared as sharedLabels } from '../../../i18n/en/labels'
import ActionButtonFooter from './action-button-footer'
import SelectBoxGroup from '../select-box-group'
import SymptomSection from './symptom-section'
import SymptomView from './symptom-view'
export default class Sex extends Component {
export default class Sex extends SymptomView {
constructor(props) {
super(props)
const cycleDay = props.cycleDay
@@ -26,6 +24,22 @@ export default class Sex extends Component {
if (this.state.note) this.state.other = true
}
symptomName = "sex"
onBackButtonPress() {
const nothingEntered = Object.values(this.state).every(val => !val)
if (nothingEntered) {
this.deleteSymptomEntry()
return
}
const copyOfState = Object.assign({}, this.state)
if (!copyOfState.other) {
copyOfState.note = null
}
this.saveSymptomEntry(copyOfState)
}
toggleState = (key) => {
const curr = this.state[key]
this.setState({[key]: !curr})
@@ -34,9 +48,8 @@ export default class Sex extends Component {
}
}
render() {
renderContent() {
return (
<View style={{ flex: 1 }}>
<ScrollView style={styles.page}>
<SymptomSection
header={sexLabels.header}
@@ -71,21 +84,6 @@ export default class Sex extends Component {
/>
}
</ScrollView>
<ActionButtonFooter
symptom='sex'
date={this.props.date}
currentSymptomValue={this.state}
saveAction={() => {
const copyOfState = Object.assign({}, this.state)
if (!copyOfState.other) {
copyOfState.note = null
}
saveSymptom('sex', this.props.date, copyOfState)
}}
saveDisabled={Object.values(this.state).every(value => !value)}
navigate={this.props.navigate}
/>
</View>
)
}
}
@@ -0,0 +1,47 @@
import React, { Component } from 'react'
import { BackHandler, View } from 'react-native'
import { saveSymptom } from '../../../db'
import Header from '../../header/symptom-view'
import { headerTitles } from '../../../i18n/en/labels'
export default class SymptomView extends Component {
constructor(props) {
super()
// every specific symptom view provides their own onBackButtonPress method
this.backHandler = BackHandler.addEventListener('hardwareBackPress', this.onBackButtonPress.bind(this))
this.globalBackhandler = props.handleBackButtonPress
this.date = props.date
}
saveSymptomEntry(entry) {
saveSymptom(this.symptomName, this.date, entry)
}
deleteSymptomEntry() {
saveSymptom(this.symptomName, this.date)
}
componentWillUnmount() {
this.backHandler.remove()
}
render() {
return (
<View style={{flex: 1}}>
<Header
title={headerTitles[this.symptomName].toLowerCase()}
date={this.date}
goBack={() => {
this.onBackButtonPress()
this.globalBackhandler()
}}
deleteEntry={() => {
this.deleteSymptomEntry()
this.globalBackhandler()
}}
/>
{this.renderContent()}
</View>
)
}
}
+19 -32
View File
@@ -1,4 +1,4 @@
import React, { Component } from 'react'
import React from 'react'
import {
View,
Switch,
@@ -9,21 +9,21 @@ import {
import DateTimePicker from 'react-native-modal-datetime-picker-nevo'
import padWithZeros from '../../helpers/pad-time-with-zeros'
import { getPreviousTemperature, saveSymptom } from '../../../db'
import { getPreviousTemperature } from '../../../db'
import styles from '../../../styles'
import { LocalTime, ChronoUnit } from 'js-joda'
import { temperature as labels } from '../../../i18n/en/cycle-day'
import { scaleObservable } from '../../../local-storage'
import { shared as sharedLabels } from '../../../i18n/en/labels'
import ActionButtonFooter from './action-button-footer'
import config from '../../../config'
import AppTextInput from '../../app-text-input'
import AppText from '../../app-text'
import SymptomSection from './symptom-section'
import SymptomView from './symptom-view'
const minutes = ChronoUnit.MINUTES
export default class Temp extends Component {
export default class Temp extends SymptomView {
constructor(props) {
super(props)
const cycleDay = props.cycleDay
@@ -45,7 +45,7 @@ export default class Temp extends Component {
this.state.temperature = `${this.state.temperature}.0`
}
} else {
const prevTemp = getPreviousTemperature(this.props.date)
const prevTemp = getPreviousTemperature(props.date)
if (prevTemp) {
this.state.temperature = prevTemp.toString()
this.state.isSuggestion = true
@@ -53,6 +53,17 @@ export default class Temp extends Component {
}
}
symptomName = 'temperature'
onBackButtonPress() {
if (typeof this.state.temperature != 'string' || this.state.temperature === '') {
this.deleteSymptomEntry()
return
}
this.checkRangeAndSave()
}
saveTemperature = () => {
const dataToSave = {
value: Number(this.state.temperature),
@@ -60,8 +71,8 @@ export default class Temp extends Component {
time: this.state.time,
note: this.state.note
}
saveSymptom('temperature', this.props.date, dataToSave)
this.props.navigate('CycleDay', {date: this.props.date})
this.saveSymptomEntry(dataToSave)
}
checkRangeAndSave = () => {
@@ -105,13 +116,12 @@ export default class Temp extends Component {
this.setState({ isTimePickerVisible: true })
}
render() {
renderContent() {
const inputStyle = [styles.temperatureTextInput]
if (this.state.isSuggestion) {
inputStyle.push(styles.temperatureTextInputSuggestion)
}
return (
<View style={{ flex: 1 }}>
<ScrollView style={styles.page}>
<SymptomSection
header={labels.temperature.header}
@@ -178,29 +188,6 @@ export default class Temp extends Component {
/>
</SymptomSection>
</ScrollView>
<ActionButtonFooter
symptom='temperature'
date={this.props.date}
currentSymptomValue={this.temperature}
saveAction={() => this.checkRangeAndSave()}
saveDisabled={
this.state.temperature === '' ||
isNaN(Number(this.state.temperature)) ||
isInvalidTime(this.state.time)
}
navigate={this.props.navigate}
autoShowDayView={false}
/>
</View>
)
}
}
function isInvalidTime(timeString) {
try {
LocalTime.parse(timeString)
} catch (err) {
return true
}
return false
}
+1 -6
View File
@@ -3,7 +3,6 @@ import { Dimensions } from 'react-native'
import CycleDayHeader from './cycle-day'
import DefaultHeader from './default'
import BackButtonHeader from './back-button'
import SymptomViewHeader from './symptom-view'
export default function Header(p) {
const middle = Dimensions.get('window').width / 2
@@ -11,11 +10,7 @@ export default function Header(p) {
if (props.isCycleDayOverView) {
return (<CycleDayHeader {...props} />)
}
else if (props.isSymptomView) {
return (<SymptomViewHeader {...props} />)
}
else if (props.showBackButton) {
} else if (props.showBackButton) {
return (<BackButtonHeader {...props} />)
}
else {
+5 -3
View File
@@ -2,7 +2,8 @@ import React from 'react'
import {
View,
Text,
TouchableOpacity
TouchableOpacity,
Dimensions
} from 'react-native'
import styles, { iconStyles } from '../../styles'
import FeatherIcon from 'react-native-vector-icons/Feather'
@@ -10,11 +11,12 @@ import NavigationArrow from './navigation-arrow'
import formatDate from '../helpers/format-date'
export default function SymptomViewHeader(props) {
const middle = Dimensions.get('window').width / 2
return (
<View style={[styles.header, styles.headerCycleDay, styles.headerSymptom]}>
<View
style={styles.accentCircle}
left={props.middle - styles.accentCircle.width / 2}
left={middle - styles.accentCircle.width / 2}
/>
<NavigationArrow
direction='left'
@@ -29,7 +31,7 @@ export default function SymptomViewHeader(props) {
</Text>
</View >
<TouchableOpacity
onPress={() => props.goToSymptomInfo()}
onPress={props.deleteEntry}
style={styles.infoButton}
>
<FeatherIcon
+171
View File
@@ -0,0 +1,171 @@
const TemperatureSchema = {
name: 'Temperature',
properties: {
value: 'double',
exclude: 'bool',
time: {
type: 'string',
optional: true
},
note: {
type: 'string',
optional: true
}
}
}
const BleedingSchema = {
name: 'Bleeding',
properties: {
value: 'int',
exclude: 'bool'
}
}
const MucusSchema = {
name: 'Mucus',
properties: {
feeling: { type: 'int', optional: true },
texture: { type: 'int', optional: true },
value: { type: 'int', optional: true },
exclude: 'bool'
}
}
const CervixSchema = {
name: 'Cervix',
properties: {
opening: { type: 'int', optional: true },
firmness: { type: 'int', optional: true },
position: {type: 'int', optional: true },
exclude: 'bool'
}
}
const NoteSchema = {
name: 'Note',
properties: {
value: 'string'
}
}
const DesireSchema = {
name: 'Desire',
properties: {
value: 'int'
}
}
const SexSchema = {
name: 'Sex',
properties: {
solo: { type: 'bool', optional: true },
partner: { type: 'bool', optional: true },
condom: { type: 'bool', optional: true },
pill: { type: 'bool', optional: true },
iud: { type: 'bool', optional: true },
patch: { type: 'bool', optional: true },
ring: { type: 'bool', optional: true },
implant: { type: 'bool', optional: true },
diaphragm: { type: 'bool', optional: true },
none: { type: 'bool', optional: true },
other: { type: 'bool', optional: true },
note: { type: 'string', optional: true }
}
}
const PainSchema = {
name: 'Pain',
properties: {
cramps: { type: 'bool', optional: true },
ovulationPain: { type: 'bool', optional: true },
headache: { type: 'bool', optional: true },
backache: { type: 'bool', optional: true },
nausea: { type: 'bool', optional: true },
tenderBreasts: { type: 'bool', optional: true },
migraine: { type: 'bool', optional: true },
other: { type: 'bool', optional: true },
note: { type: 'string', optional: true }
}
}
const MoodSchema = {
name: 'Mood',
properties: {
happy: { type: 'bool', optional: true },
sad: { type: 'bool', optional: true },
stressed: { type: 'bool', optional: true },
balanced: { type: 'bool', optional: true },
fine: { type: 'bool', optional: true },
anxious: { type: 'bool', optional: true },
energetic: { type: 'bool', optional: true },
fatigue: { type: 'bool', optional: true },
angry: { type: 'bool', optional: true },
other: { type: 'bool', optional: true },
note: { type: 'string', optional: true }
}
}
const CycleDaySchema = {
name: 'CycleDay',
primaryKey: 'date',
properties: {
date: 'string',
temperature: {
type: 'Temperature',
optional: true
},
isCycleStart: 'bool',
bleeding: {
type: 'Bleeding',
optional: true
},
mucus: {
type: 'Mucus',
optional: true
},
cervix: {
type: 'Cervix',
optional: true
},
note: {
type: 'Note',
optional: true
},
desire: {
type: 'Desire',
optional: true
},
sex: {
type: 'Sex',
optional: true
},
pain: {
type: 'Pain',
optional: true
},
mood: {
type: 'Mood',
optional: true
}
}
}
export default {
schema: [
CycleDaySchema,
TemperatureSchema,
BleedingSchema,
MucusSchema,
CervixSchema,
NoteSchema,
DesireSchema,
SexSchema,
PainSchema,
MoodSchema
],
schemaVersion: 4,
migration: (oldRealm) => {
if (oldRealm.schemaVersion >= 4) return
}
}
+2 -1
View File
@@ -2,5 +2,6 @@ import schema0 from './0.js'
import schema1 from './1.js'
import schema2 from './2.js'
import schema3 from './3.js'
import schema4 from './4.js'
export default [schema0, schema1, schema2, schema3]
export default [schema0, schema1, schema2, schema3, schema4]
+9 -9
View File
@@ -32,15 +32,15 @@ export const headerTitles = {
Password: settingsTitles.password,
About: settingsTitles.about,
License: settingsTitles.license,
BleedingEditView: 'Bleeding',
TemperatureEditView: 'Temperature',
MucusEditView: 'Mucus',
CervixEditView: 'Cervix',
NoteEditView: 'Note',
DesireEditView: 'Desire',
SexEditView: 'Sex',
PainEditView: 'Pain',
MoodEditView: 'Mood',
bleeding: 'Bleeding',
temperature: 'Temperature',
mucus: 'Mucus',
cervix: 'Cervix',
note: 'Note',
desire: 'Desire',
sex: 'Sex',
pain: 'Pain',
mood: 'Mood',
InfoSymptom: 'Info'
}
+3
View File
@@ -1,4 +1,7 @@
export default function (feeling, texture) {
if (typeof feeling != 'number' || typeof texture != 'number') return null
const feelingMapping = {
0: 0,
1: 1,
+13 -1
View File
@@ -108,7 +108,15 @@ function formatCycleForSympto(cycle) {
if (day[symptomName] && day[symptomName].exclude) {
delete day[symptomName]
}
});
})
// remove days with incomplete cervix values
if (hasIncompleteCervixValue(day)) {
delete day.cervix
}
// remove days with incomplete mucus value (because nfp-mucus returns null when that's the case)
if (day.mucus && day.mucus.value === null) {
delete day.mucus
}
// change format
['bleeding', 'temperature', 'mucus'].forEach(symptomName => {
if (day[symptomName]) day[symptomName] = day[symptomName].value
@@ -120,3 +128,7 @@ function formatCycleForSympto(cycle) {
formatted.reverse()
return formatted
}
function hasIncompleteCervixValue(day) {
return day.cervix && (typeof day.cervix.opening != 'number' || typeof day.cervix.firmness != 'number')
}
+14 -33
View File
@@ -3229,8 +3229,7 @@
},
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
"optional": true
"bundled": true
},
"aproba": {
"version": "1.2.0",
@@ -3248,13 +3247,11 @@
},
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"optional": true
"bundled": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -3267,18 +3264,15 @@
},
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"optional": true
"bundled": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"optional": true
"bundled": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"optional": true
"bundled": true
},
"core-util-is": {
"version": "1.0.2",
@@ -3381,8 +3375,7 @@
},
"inherits": {
"version": "2.0.3",
"bundled": true,
"optional": true
"bundled": true
},
"ini": {
"version": "1.3.5",
@@ -3392,7 +3385,6 @@
"is-fullwidth-code-point": {
"version": "1.0.0",
"bundled": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -3405,20 +3397,17 @@
"minimatch": {
"version": "3.0.4",
"bundled": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
"bundled": true,
"optional": true
"bundled": true
},
"minipass": {
"version": "2.3.5",
"bundled": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@@ -3435,7 +3424,6 @@
"mkdirp": {
"version": "0.5.1",
"bundled": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@@ -3508,8 +3496,7 @@
},
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"optional": true
"bundled": true
},
"object-assign": {
"version": "4.1.1",
@@ -3519,7 +3506,6 @@
"once": {
"version": "1.4.0",
"bundled": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@@ -3595,8 +3581,7 @@
},
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
"optional": true
"bundled": true
},
"safer-buffer": {
"version": "2.1.2",
@@ -3626,7 +3611,6 @@
"string-width": {
"version": "1.0.2",
"bundled": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -3644,7 +3628,6 @@
"strip-ansi": {
"version": "3.0.1",
"bundled": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -3683,13 +3666,11 @@
},
"wrappy": {
"version": "1.0.2",
"bundled": true,
"optional": true
"bundled": true
},
"yallist": {
"version": "3.0.3",
"bundled": true,
"optional": true
"bundled": true
}
}
},
@@ -8097,9 +8078,9 @@
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
},
"sympto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/sympto/-/sympto-1.0.1.tgz",
"integrity": "sha512-wLDpugvScuXhSBhgJHZTGU9gTd5uDnuZDJuNz7aUSj1N28VOe2RhKwMF4RLwjy3s6i+BG1Lfa3uckNPCFPkUvA==",
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/sympto/-/sympto-1.0.4.tgz",
"integrity": "sha512-FSdSwPeE3BKvnJlPkHzVusGMTz4r6dW2eEEJbgPrgdkmPWmAFWTD7Hf7OhqQSbTLjiZY7jBeWDWuizb4UZMk1g==",
"requires": {
"js-joda": "^1.9.2"
}
+1 -1
View File
@@ -46,7 +46,7 @@
"react-native-share": "^1.1.3",
"react-native-vector-icons": "^5.0.0",
"realm": "^2.22.0",
"sympto": "^1.0.0"
"sympto": "^1.0.4"
},
"devDependencies": {
"@babel/core": "^7.2.2",
+5
View File
@@ -7,6 +7,11 @@ chai.use(dirtyChai)
import getSensiplanMucus from '../lib/nfp-mucus'
describe('getSensiplanMucus', () => {
it('returns null if there is no value for feeling or texture', () => {
expect(getSensiplanMucus()).to.be.null()
expect(getSensiplanMucus(undefined, 3)).to.be.null()
expect(getSensiplanMucus(2, undefined)).to.be.null()
})
describe('results in t for:', () => {
it('dry feeling and no texture', function () {