From ec18bd7862915bd7c52d8e90f422fdc8aaa12e13 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Sun, 15 Jul 2018 22:30:04 +0200 Subject: [PATCH 01/13] Introduce generic saveSymptom --- db.js | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/db.js b/db.js index 3fdd401..00e1d94 100644 --- a/db.js +++ b/db.js @@ -47,20 +47,14 @@ const db = new Realm({ const bleedingDaysSortedByDate = db.objects('CycleDay').filtered('bleeding != null').sorted('date', true) const temperatureDaysSortedByDate = db.objects('CycleDay').filtered('temperature != null').sorted('date', true) -function saveTemperature(cycleDay, temperature) { +function saveSymptom(symptom, cycleDay, val) { db.write(() => { - cycleDay.temperature = temperature + cycleDay[symptom] = val }) } const cycleDaysSortedByDate = db.objects('CycleDay').sorted('date', true) -function saveBleeding(cycleDay, bleeding) { - db.write(() => { - cycleDay.bleeding = bleeding - }) -} - function getOrCreateCycleDay(localDate) { let result = db.objectForPrimaryKey('CycleDay', localDate) if (!result) { @@ -94,8 +88,7 @@ function getPreviousTemperature(cycleDay) { } export { - saveTemperature, - saveBleeding, + saveSymptom, getOrCreateCycleDay, bleedingDaysSortedByDate, temperatureDaysSortedByDate, From cdbc42ea3a8727905215e6dc7b33c00a241c751d Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Sun, 15 Jul 2018 22:31:44 +0200 Subject: [PATCH 02/13] Move format and labels to cycleday component dir --- {labels => components/cycle-day/labels}/format.js | 0 {labels => components/cycle-day/labels}/labels.js | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {labels => components/cycle-day/labels}/format.js (100%) rename {labels => components/cycle-day/labels}/labels.js (100%) diff --git a/labels/format.js b/components/cycle-day/labels/format.js similarity index 100% rename from labels/format.js rename to components/cycle-day/labels/format.js diff --git a/labels/labels.js b/components/cycle-day/labels/labels.js similarity index 100% rename from labels/labels.js rename to components/cycle-day/labels/labels.js From 0c92c85baf375872e46f353bb5ff60f640267817 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Sun, 15 Jul 2018 22:32:47 +0200 Subject: [PATCH 03/13] Simplify overview page markup --- components/cycle-day/cycle-day-overview.js | 81 ++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 components/cycle-day/cycle-day-overview.js diff --git a/components/cycle-day/cycle-day-overview.js b/components/cycle-day/cycle-day-overview.js new file mode 100644 index 0000000..6741598 --- /dev/null +++ b/components/cycle-day/cycle-day-overview.js @@ -0,0 +1,81 @@ +import React, { Component } from 'react' +import { + View, + Button, + Text +} from 'react-native' +import styles from '../../styles' +import { bleeding as labels} from './labels/labels' +import cycleDayModule from '../../lib/get-cycle-day-number' +import { bleedingDaysSortedByDate } from '../../db' + +const getCycleDayNumber = cycleDayModule() + +export default class DayView extends Component { + constructor(props) { + super(props) + this.cycleDay = props.cycleDay + this.showView = props.showView + this.state = { + cycleDayNumber: getCycleDayNumber(this.cycleDay.date), + } + + this.setStateWithCurrentCycleDayNumber = (function (DayViewComponent) { + return function () { + DayViewComponent.setState({ + cycleDayNumber: getCycleDayNumber(DayViewComponent.cycleDay.date) + }) + } + })(this) + + bleedingDaysSortedByDate.addListener(this.setStateWithCurrentCycleDayNumber) + } + + componentWillUnmount() { + bleedingDaysSortedByDate.removeListener(this.setStateWithCurrentCycleDayNumber) + } + + render() { + const bleedingValue = this.cycleDay.bleeding && this.cycleDay.bleeding.value + let bleedingLabel + if (typeof bleedingValue === 'number') { + bleedingLabel = `${labels[bleedingValue]}` + if (this.cycleDay.bleeding.exclude) bleedingLabel = "( " + bleedingLabel + " )" + } else { + bleedingLabel = 'edit' + } + const temperatureValue = this.cycleDay.temperature && this.cycleDay.temperature.value + let temperatureLabel + if (typeof temperatureValue === 'number') { + temperatureLabel = `${temperatureValue} °C - ${this.cycleDay.temperature.time}` + if (this.cycleDay.temperature.exclude) { + temperatureLabel = "( " + temperatureLabel + " )" + } + } else { + temperatureLabel = 'edit' + } + + return ( + + + Bleeding + + + + + + Temperature + + + + + + ) + } +} \ No newline at end of file From 9eb1ad6af1227efe7dd43e54572c06be684e0f07 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Sun, 15 Jul 2018 22:35:33 +0200 Subject: [PATCH 04/13] Simplify home screen markup --- components/home.js | 56 +++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/components/home.js b/components/home.js index 60327e3..1204f8e 100644 --- a/components/home.js +++ b/components/home.js @@ -5,7 +5,7 @@ import { Text } from 'react-native' import { LocalDate } from 'js-joda' -import styles from '../styles/index' +import styles from '../styles' import cycleDayModule from '../lib/get-cycle-day-number' import { getOrCreateCycleDay, bleedingDaysSortedByDate, deleteAll } from '../db' @@ -46,37 +46,31 @@ export default class Home extends Component { render() { const navigate = this.props.navigation.navigate return ( - - - - {this.state.welcomeText} - + + {this.state.welcomeText} + + - - - - - - - - - - - - - + + + + + + + + ) From eba5837350794694e04c49878402b55a1117e2c1 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Sun, 15 Jul 2018 22:36:06 +0200 Subject: [PATCH 05/13] Simplify markup and move symptom screens to symptom dir --- components/bleeding.js | 102 ------------------ components/cycle-day-overview.js | 83 -------------- components/cycle-day/action-buttons.js | 46 ++++++++ .../{cycle-day.js => cycle-day/index.js} | 21 ++-- components/cycle-day/symptoms/bleeding.js | 76 +++++++++++++ components/cycle-day/symptoms/temperature.js | 80 ++++++++++++++ components/home.js | 46 ++++---- components/temperature.js | 102 ------------------ styles/index.js | 83 ++++++-------- 9 files changed, 268 insertions(+), 371 deletions(-) delete mode 100644 components/bleeding.js delete mode 100644 components/cycle-day-overview.js create mode 100644 components/cycle-day/action-buttons.js rename components/{cycle-day.js => cycle-day/index.js} (70%) create mode 100644 components/cycle-day/symptoms/bleeding.js create mode 100644 components/cycle-day/symptoms/temperature.js delete mode 100644 components/temperature.js diff --git a/components/bleeding.js b/components/bleeding.js deleted file mode 100644 index 0472c3c..0000000 --- a/components/bleeding.js +++ /dev/null @@ -1,102 +0,0 @@ -import React, { Component } from 'react' -import { - View, - Button, - Text, - Switch -} from 'react-native' -import RadioForm from 'react-native-simple-radio-button' -import styles from '../styles/index' -import { saveBleeding } from '../db' -import { bleeding as labels } from '../labels/labels' - -export default class Bleeding extends Component { - constructor(props) { - super(props) - this.cycleDay = props.cycleDay - this.showView = props.showView - let bleedingValue = this.cycleDay.bleeding && this.cycleDay.bleeding.value - if (! (typeof bleedingValue === 'number') ){ - bleedingValue = -1 - } - this.state = { - currentValue: bleedingValue, - exclude: this.cycleDay.bleeding ? this.cycleDay.bleeding.exclude : false - } - } - - render() { - const bleedingRadioProps = [ - {label: labels[0], value: 0 }, - {label: labels[1], value: 1 }, - {label: labels[2], value: 2 }, - {label: labels[3], value: 3 }, - ] - return ( - - - - - Bleeding - - - { - this.setState({currentValue: itemValue}) - }} - /> - - - - - Exclude - - - { - this.setState({exclude: val}) - }} - value={this.state.exclude} - /> - - - - - - - - - - - - - - - - ) - } -} \ No newline at end of file diff --git a/components/cycle-day-overview.js b/components/cycle-day-overview.js deleted file mode 100644 index cc780da..0000000 --- a/components/cycle-day-overview.js +++ /dev/null @@ -1,83 +0,0 @@ -import React, { Component } from 'react' -import { - View, - Button, - Text -} from 'react-native' -import styles from '../styles/index' -import { bleeding as labels} from '../labels/labels' -import cycleDayModule from '../lib/get-cycle-day-number' -import { bleedingDaysSortedByDate } from '../db' - -const getCycleDayNumber = cycleDayModule() - -export default class DayView extends Component { - constructor(props) { - super(props) - this.cycleDay = props.cycleDay - this.showView = props.showView - this.state = { - cycleDayNumber: getCycleDayNumber(this.cycleDay.date), - } - - this.setStateWithCurrentCycleDayNumber = (function (DayViewComponent) { - return function () { - DayViewComponent.setState({ - cycleDayNumber: getCycleDayNumber(DayViewComponent.cycleDay.date) - }) - } - })(this) - - bleedingDaysSortedByDate.addListener(this.setStateWithCurrentCycleDayNumber) - } - - componentWillUnmount() { - bleedingDaysSortedByDate.removeListener(this.setStateWithCurrentCycleDayNumber) - } - - render() { - const bleedingValue = this.cycleDay.bleeding && this.cycleDay.bleeding.value - let bleedingLabel - if (typeof bleedingValue === 'number') { - bleedingLabel = `${labels[bleedingValue]}` - if (this.cycleDay.bleeding.exclude) bleedingLabel = "( " + bleedingLabel + " )" - } else { - bleedingLabel = 'edit' - } - const temperatureValue = this.cycleDay.temperature && this.cycleDay.temperature.value - let temperatureLabel - if (typeof temperatureValue === 'number') { - temperatureLabel = `${temperatureValue} °C` - if (this.cycleDay.temperature.exclude) temperatureLabel = "( " + temperatureLabel + " )" - } else { - temperatureLabel = 'edit' - } - - return ( - - - - Bleeding - - - - - - - - Temperature - - - - - - - ) - } -} \ No newline at end of file diff --git a/components/cycle-day/action-buttons.js b/components/cycle-day/action-buttons.js new file mode 100644 index 0000000..0008552 --- /dev/null +++ b/components/cycle-day/action-buttons.js @@ -0,0 +1,46 @@ +import React from 'react' +import { + View, + Button, +} from 'react-native' +import { saveSymptom } from '../../db' + +export default function (showView) { + return function ({ symptom, cycleDay, saveAction, saveDisabled}) { + const buttons = [ + { + title: 'Cancel', + action: () => showView('dayView') + }, + { + title: 'Delete', + action: () => { + saveSymptom(symptom, cycleDay) + showView('dayView') + } + }, { + title: 'Save', + action: () => { + saveAction() + showView('dayView') + }, + disabled: saveDisabled + } + ] + + return buttons.map(({ title, action, disabledCondition }, i) => { + const style = { flex: 1, marginHorizontal: 10 } + if (i === 0) style.marginLeft = 0 + if (i === buttons.length - 1) style.marginRight = 0 + return ( + + + + ) + }) + } +} \ No newline at end of file diff --git a/components/cycle-day.js b/components/cycle-day/index.js similarity index 70% rename from components/cycle-day.js rename to components/cycle-day/index.js index 7d46dea..bcd8465 100644 --- a/components/cycle-day.js +++ b/components/cycle-day/index.js @@ -3,12 +3,13 @@ import { View, Text } from 'react-native' -import cycleDayModule from '../lib/get-cycle-day-number' +import cycleDayModule from '../../lib/get-cycle-day-number' import DayView from './cycle-day-overview' -import BleedingEditView from './bleeding' -import TemperatureEditView from './temperature' -import { formatDateForViewHeader } from '../labels/format' -import styles from '../styles/index' +import BleedingEditView from './symptoms/bleeding' +import TemperatureEditView from './symptoms/temperature' +import { formatDateForViewHeader } from './labels/format' +import styles from '../../styles' +import actionButtonModule from './action-buttons' const getCycleDayNumber = cycleDayModule() @@ -24,12 +25,14 @@ export default class Day extends Component { this.showView = view => { this.setState({visibleComponent: view}) } + + this.makeActionButtons = actionButtonModule(this.showView) } render() { const cycleDayNumber = getCycleDayNumber(this.cycleDay.date) return ( - + {formatDateForViewHeader(this.cycleDay.date)} @@ -38,11 +41,11 @@ export default class Day extends Component { { cycleDayNumber && Cycle day {cycleDayNumber} } - + { { dayView: , - bleedingEditView: , - temperatureEditView: + bleedingEditView: , + temperatureEditView: }[this.state.visibleComponent] } diff --git a/components/cycle-day/symptoms/bleeding.js b/components/cycle-day/symptoms/bleeding.js new file mode 100644 index 0000000..1e637f0 --- /dev/null +++ b/components/cycle-day/symptoms/bleeding.js @@ -0,0 +1,76 @@ +import React, { Component } from 'react' +import { + View, + Text, + Switch +} from 'react-native' +import RadioForm from 'react-native-simple-radio-button' +import styles from '../../../styles' +import { saveSymptom } from '../../../db' +import { bleeding as labels } from '../labels/labels' + +export default class Bleeding extends Component { + constructor(props) { + super(props) + this.cycleDay = props.cycleDay + this.makeActionButtons = props.makeActionButtons + let bleedingValue = this.cycleDay.bleeding && this.cycleDay.bleeding.value + if (!(typeof bleedingValue === 'number')) { + bleedingValue = -1 + } + this.state = { + currentValue: bleedingValue, + exclude: this.cycleDay.bleeding ? this.cycleDay.bleeding.exclude : false + } + } + + render() { + const bleedingRadioProps = [ + { label: labels[0], value: 0 }, + { label: labels[1], value: 1 }, + { label: labels[2], value: 2 }, + { label: labels[3], value: 3 }, + ] + return ( + + Bleeding + + { + this.setState({ currentValue: itemValue }) + }} + /> + + + Exclude + { + this.setState({ exclude: val }) + }} + value={this.state.exclude} + /> + + + {this.makeActionButtons( + { + symptom: 'bleeding', + cycleDay: this.cycleDay, + saveAction: () => { + saveSymptom('bleeding', this.cycleDay, { + value: this.state.currentValue, + exclude: this.state.exclude + }) + }, + saveDisabled: this.state.currentValue === -1 + } + )} + + + ) + } +} \ No newline at end of file diff --git a/components/cycle-day/symptoms/temperature.js b/components/cycle-day/symptoms/temperature.js new file mode 100644 index 0000000..b9a3d5b --- /dev/null +++ b/components/cycle-day/symptoms/temperature.js @@ -0,0 +1,80 @@ +import React, { Component } from 'react' +import { + View, + Text, + TextInput, + Switch +} from 'react-native' + +import { getPreviousTemperature, saveSymptom } from '../../../db' +import styles from '../../../styles' +import { LocalTime, ChronoUnit } from 'js-joda' + +export default class Temp extends Component { + constructor(props) { + super(props) + this.cycleDay = props.cycleDay + this.makeActionButtons = props.makeActionButtons + let initialValue + + if (this.cycleDay.temperature) { + initialValue = this.cycleDay.temperature.value.toString() + this.time = this.cycleDay.temperature.time + } else { + const prevTemp = getPreviousTemperature(this.cycleDay) + initialValue = prevTemp ? prevTemp.toString() : '' + } + + this.state = { + currentValue: initialValue, + exclude: this.cycleDay.temperature ? this.cycleDay.temperature.exclude : false + } + } + + render() { + const cycleDay = this.cycleDay + return ( + + + Temperature (°C) + { + this.setState({ currentValue: val }) + }} + keyboardType='numeric' + value={this.state.currentValue} + /> + + + Exclude + { + this.setState({ exclude: val }) + }} + value={this.state.exclude} + /> + + + {this.makeActionButtons({ + symptom: 'temperature', + cycleDay: this.cycleDay, + saveAction: () => { + const dataToSave = { + value: Number(this.state.currentValue), + exclude: this.state.exclude + } + if (!cycleDay.temperature || cycleDay.temperature && !cycleDay.temperature.time) { + const now = LocalTime.now().truncatedTo(ChronoUnit.MINUTES).toString() + dataToSave.time = now + } + saveSymptom('temperature', cycleDay, dataToSave) + }, + saveDisabled: this.state.currentValue === '' + })} + + + ) + } +} diff --git a/components/home.js b/components/home.js index 1204f8e..0b00592 100644 --- a/components/home.js +++ b/components/home.js @@ -49,28 +49,30 @@ export default class Home extends Component { {this.state.welcomeText} - - - - - - - - - - + + + + + + + + + + + + ) diff --git a/components/temperature.js b/components/temperature.js deleted file mode 100644 index c6fccfc..0000000 --- a/components/temperature.js +++ /dev/null @@ -1,102 +0,0 @@ -import React, { Component } from 'react' -import { - View, - Text, - Button, - TextInput, - Switch -} from 'react-native' - -import { saveTemperature, getPreviousTemperature } from '../db' -import styles from '../styles/index' - -export default class Temp extends Component { - constructor(props) { - super(props) - this.cycleDay = props.cycleDay - this.showView = props.showView - let initialValue - - if(this.cycleDay.temperature) { - initialValue = this.cycleDay.temperature.value.toString() - } else { - const prevTemp = getPreviousTemperature(this.cycleDay) - initialValue = prevTemp ? prevTemp.toString() : '' - } - - this.state = { - currentValue: initialValue, - exclude: this.cycleDay.temperature ? this.cycleDay.temperature.exclude : false - } - } - - render() { - const cycleDay = this.cycleDay - return ( - - - - - Temperature (°C) - - - { - this.setState({currentValue: val}) - }} - keyboardType='numeric' - value = {this.state.currentValue} - /> - - - - - Exclude - - - { - this.setState({ exclude: val }) - }} - value = { this.state.exclude } - /> - - - - - - - - - - - - - - - - ) - } -} diff --git a/styles/index.js b/styles/index.js index 5eb9cc1..c01241f 100644 --- a/styles/index.js +++ b/styles/index.js @@ -14,21 +14,19 @@ export default StyleSheet.create({ dateHeader: { fontSize: 20, fontWeight: 'bold', - margin: 30, + margin: 15, color: 'white', textAlign: 'center', textAlignVertical: 'center' }, cycleDayNumber: { fontSize: 18, - margin: 20, + margin: 15, textAlign: 'center', textAlignVertical: 'center' }, symptomDayView: { fontSize: 20, - margin: 30, - textAlign: 'left', textAlignVertical: 'center' }, radioButton: { @@ -37,68 +35,47 @@ export default StyleSheet.create({ textAlign: 'center', textAlignVertical: 'center' }, - singleButtonView: { - flex: 1, - margin: 5 - }, - itemsInRowView: { - flex: 1, - flexDirection: 'row', - justifyContent: 'flex-start', - alignItems: 'center' - }, - itemsInRowSeparatedView: { - flex: 1, - flexDirection: 'row', - justifyContent: 'space-evenly', - alignItems: 'center' - }, symptomEditView: { - flex: 1, - flexDirection: 'column', justifyContent: 'space-between', - alignItems: 'flex-start' + marginHorizontal: 15 }, - symptomEditSplitSymptomsAndLastRowButtons: { - flex: 4, - flexDirection: 'column', - justifyContent: 'flex-start', - alignItems: 'flex-start' + symptomEditRow: { + justifyContent: 'space-between', + marginBottom: 10, }, - symptomEditListedSymptomView: { - flex: 1, - flexDirection: 'column', - justifyContent: 'flex-start', - alignItems: 'flex-start' - }, - cycleDayOuterView: { - flex: 1, - flexDirection: 'column', - justifyContent: 'space-around' + symptomViewRowInline: { + flexDirection: 'row', + justifyContent: 'space-between', + marginBottom: 10, + alignItems: 'center', + height: 50 }, cycleDayDateView: { - flex: 2, justifyContent: 'center', backgroundColor: 'steelblue' }, cycleDayNumberView: { - flex: 1, justifyContent: 'center', - backgroundColor: 'skyblue' + backgroundColor: 'skyblue', + marginBottom: 15 }, - cycleDaySymptomsView: { - flex: 8, - justifyContent: 'center' + homeButtons: { + marginHorizontal: 15 }, - homeContainerView: { - flex: 0.5, - flexDirection: 'column', - justifyContent: 'space-around' + homeButton: { + marginBottom: 15 }, - homeButtonsView: { - flex: 3, - flexDirection: 'column', - justifyContent: 'space-around', - margin: 5 + temperatureTextInput: { + width: 80, + textAlign: 'center', + fontSize: 20 + }, + actionButtonRow: { + flexDirection: 'row', + justifyContent: 'space-evenly', + marginTop: 50 + }, + symptomEditButton: { + width: 130 } }) \ No newline at end of file From 86392080188f5d7870ec32e61764e772cc4f880b Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Sun, 15 Jul 2018 23:01:10 +0200 Subject: [PATCH 06/13] Style radio button row --- components/cycle-day/symptoms/bleeding.js | 2 +- styles/index.js | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/components/cycle-day/symptoms/bleeding.js b/components/cycle-day/symptoms/bleeding.js index 1e637f0..7ec0574 100644 --- a/components/cycle-day/symptoms/bleeding.js +++ b/components/cycle-day/symptoms/bleeding.js @@ -34,7 +34,7 @@ export default class Bleeding extends Component { return ( Bleeding - + Date: Wed, 4 Jul 2018 13:03:23 +0200 Subject: [PATCH 07/13] [WIP] first draft implementing mucus --- components/cycle-day/cycle-day-overview.js | 29 +++++- components/cycle-day/index.js | 8 +- components/cycle-day/labels/labels.js | 8 +- components/cycle-day/symptoms/mucus.js | 107 +++++++++++++++++++++ db.js | 16 ++- 5 files changed, 159 insertions(+), 9 deletions(-) create mode 100644 components/cycle-day/symptoms/mucus.js diff --git a/components/cycle-day/cycle-day-overview.js b/components/cycle-day/cycle-day-overview.js index 6741598..d4ea2cc 100644 --- a/components/cycle-day/cycle-day-overview.js +++ b/components/cycle-day/cycle-day-overview.js @@ -5,7 +5,10 @@ import { Text } from 'react-native' import styles from '../../styles' -import { bleeding as labels} from './labels/labels' +import { + bleedingLabels as bleedingLabels, + mucusFeeling as feelingLabels +} from './labels/labels' import cycleDayModule from '../../lib/get-cycle-day-number' import { bleedingDaysSortedByDate } from '../../db' @@ -39,7 +42,7 @@ export default class DayView extends Component { const bleedingValue = this.cycleDay.bleeding && this.cycleDay.bleeding.value let bleedingLabel if (typeof bleedingValue === 'number') { - bleedingLabel = `${labels[bleedingValue]}` + bleedingLabel = `${bleedingLabels[bleedingValue]}` if (this.cycleDay.bleeding.exclude) bleedingLabel = "( " + bleedingLabel + " )" } else { bleedingLabel = 'edit' @@ -55,6 +58,15 @@ export default class DayView extends Component { temperatureLabel = 'edit' } + const mucusFeelingValue = this.cycleDay.mucus && this.cycleDay.mucus.value + let mucusFeelingLabel + if (typeof mucusFeelingValue === 'number') { + mucusFeelingLabel = `${feelingLabels[mucusFeelingValue]}` + if (this.cycleDay.mucus.exclude) mucusFeelingLabel = "( " + mucusFeelingLabel + " )" + } else { + mucusFeelingLabel = 'edit' + } + return ( @@ -75,7 +87,18 @@ export default class DayView extends Component { + + + Mucus + + + + + ) } -} \ No newline at end of file +} diff --git a/components/cycle-day/index.js b/components/cycle-day/index.js index bcd8465..3d63466 100644 --- a/components/cycle-day/index.js +++ b/components/cycle-day/index.js @@ -7,6 +7,7 @@ import cycleDayModule from '../../lib/get-cycle-day-number' import DayView from './cycle-day-overview' import BleedingEditView from './symptoms/bleeding' import TemperatureEditView from './symptoms/temperature' +import MucusEditView from './symptoms/mucus' import { formatDateForViewHeader } from './labels/format' import styles from '../../styles' import actionButtonModule from './action-buttons' @@ -44,12 +45,13 @@ export default class Day extends Component { { { dayView: , - bleedingEditView: , - temperatureEditView: + bleedingEditView: , + temperatureEditView: , + mucusEditView: }[this.state.visibleComponent] } ) } -} \ No newline at end of file +} diff --git a/components/cycle-day/labels/labels.js b/components/cycle-day/labels/labels.js index a5ed61a..83c1afe 100644 --- a/components/cycle-day/labels/labels.js +++ b/components/cycle-day/labels/labels.js @@ -1,5 +1,9 @@ const bleeding = ['spotting', 'light', 'medium', 'heavy'] +const mucusFeeling = ['dry', 'nothing', 'wet', 'slippery'] +const mucusTexture = ['nothing', 'creamy', 'egg white'] export { - bleeding -} \ No newline at end of file + bleeding, + mucusFeeling, + mucusTexture +} diff --git a/components/cycle-day/symptoms/mucus.js b/components/cycle-day/symptoms/mucus.js new file mode 100644 index 0000000..38489cf --- /dev/null +++ b/components/cycle-day/symptoms/mucus.js @@ -0,0 +1,107 @@ +import React, { Component } from 'react' +import { + View, + Button, + Text, + Switch +} from 'react-native' +import RadioForm from 'react-native-simple-radio-button' +import styles from '../../../styles' +import { saveSymptom } from '../../../db' +import { + mucusFeeling as feelingLabels, + mucusTexture as textureLabels +} from '../labels/labels' + +export default class Mucus extends Component { + constructor(props) { + super(props) + this.cycleDay = props.cycleDay + this.showView = props.showView + + this.currentFeelingValue = this.cycleDay.mucus && this.cycleDay.mucus.feeling + if (typeof this.currentFeelingValue !== 'number') { + this.currentFeelingValue = -1 + } + + this.currentTextureValue = this.cycleDay.mucus && this.cycleDay.mucus.texture + if (typeof this.currentTextureValue !== 'number') { + this.currentTextureValue = -1 + } + this.state = { + exclude: this.cycleDay.mucus ? this.cycleDay.mucus.exclude : false + } + } + + render() { + const mucusFeelingRadioProps = [ + {label: feelingLabels[0], value: 0 }, + {label: feelingLabels[1], value: 1 }, + {label: feelingLabels[2], value: 2 }, + {label: feelingLabels[3], value: 3 } + ] + const mucusTextureRadioProps = [ + {label: textureLabels[0], value: 0 }, + {label: textureLabels[1], value: 1 }, + {label: textureLabels[2], value: 2 } + ] + return( + + Mucus + Feeling + + { + this.currentFeelingValue = itemValue + }} + /> + + Texture + + { + this.currentTextureValue = itemValue + }} + /> + + + Exclude + { + this.setState({ exclude: val }) + }} + value={this.state.exclude} + /> + + + + {this.makeActionButtons( + { + symptom: 'mucus', + cycleDay: this.cycleDay, + saveAction: () => { + saveSymptom('mucus', this.cycleDay, { + feeling: this.currentFeelingValue, + texture: this.currentTextureValue, + exclude: this.state.exclude + }) + }, + saveDisabled: this.state.currentFeelingValue === -1 || this.state.currentTextureValue === -1 + } + )} + + + + ) + } +} diff --git a/db.js b/db.js index 00e1d94..455acd8 100644 --- a/db.js +++ b/db.js @@ -18,6 +18,15 @@ const BleedingSchema = { } } +const MucusSchema = { + name: 'Mucus', + properties: { + feeling: 'int', + texture: 'int', + exclude: 'bool' + } +} + const CycleDaySchema = { name: 'CycleDay', primaryKey: 'date', @@ -30,6 +39,10 @@ const CycleDaySchema = { bleeding: { type: 'Bleeding', optional: true + }, + mucus: { + type: 'Mucus', + optional: true } } } @@ -38,7 +51,8 @@ const db = new Realm({ schema: [ CycleDaySchema, TemperatureSchema, - BleedingSchema + BleedingSchema, + MucusSchema ], // we only want this in dev mode deleteRealmIfMigrationNeeded: true From bb27cb09832009ad56b69392f5901a97e987bfe8 Mon Sep 17 00:00:00 2001 From: emelko Date: Wed, 4 Jul 2018 13:58:16 +0200 Subject: [PATCH 08/13] Add disabled to check for feeling AND texture to be selected; needs state --- components/mucus.js | 155 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 components/mucus.js diff --git a/components/mucus.js b/components/mucus.js new file mode 100644 index 0000000..f2d643d --- /dev/null +++ b/components/mucus.js @@ -0,0 +1,155 @@ +import React, { Component } from 'react' +import { + View, + Button, + Text, + Switch +} from 'react-native' +import RadioForm from 'react-native-simple-radio-button' +import { saveMucus } from '../db' +import styles from '../styles/index' +import { + mucusFeeling as feelingLabels, + mucusTexture as textureLabels +} from '../labels/labels' + +export default class Mucus extends Component { + constructor(props) { + super(props) + this.cycleDay = props.cycleDay + this.showView = props.showView + + let currentFeelingValue = this.cycleDay.mucus && this.cycleDay.mucus.feeling + if (typeof currentFeelingValue !== 'number') { + currentFeelingValue = -1 + } + let currentTextureValue = this.cycleDay.mucus && this.cycleDay.mucus.texture + if (typeof currentTextureValue !== 'number') { + currentTextureValue = -1 + } + + this.state = { + currentFeelingValue, + currentTextureValue, + exclude: this.cycleDay.mucus ? this.cycleDay.mucus.exclude : false + } + + } + + render() { + const mucusFeelingRadioProps = [ + {label: feelingLabels[0], value: 0 }, + {label: feelingLabels[1], value: 1 }, + {label: feelingLabels[2], value: 2 }, + {label: feelingLabels[3], value: 3 } + ] + const mucusTextureRadioProps = [ + {label: textureLabels[0], value: 0 }, + {label: textureLabels[1], value: 1 }, + {label: textureLabels[2], value: 2 } + ] + return( + + + + + + Mucus + + + + Feeling + + + + { + this.setState({currentFeelingValue: itemValue}) + }} + /> + + + + Color/Texture + + + + { + this.setState({currentTextureValue: itemValue}) + }} + /> + + + + + + + + Exclude + + + + { + this.setState({exclude: val}) + }} + value={this.state.exclude} + /> + + + + + + + + + + + + + + + + + + + + + + + + ) + } +} From 9ab4bd0dfa625b3ef4be041bd66e35af82c1f4da Mon Sep 17 00:00:00 2001 From: emelko Date: Wed, 4 Jul 2018 14:14:05 +0200 Subject: [PATCH 09/13] If existing display mucus feeling and texture value on button --- components/cycle-day/cycle-day-overview.js | 28 ++++++++++++---------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/components/cycle-day/cycle-day-overview.js b/components/cycle-day/cycle-day-overview.js index d4ea2cc..98a7880 100644 --- a/components/cycle-day/cycle-day-overview.js +++ b/components/cycle-day/cycle-day-overview.js @@ -6,11 +6,12 @@ import { } from 'react-native' import styles from '../../styles' import { - bleedingLabels as bleedingLabels, - mucusFeeling as feelingLabels -} from './labels/labels' -import cycleDayModule from '../../lib/get-cycle-day-number' -import { bleedingDaysSortedByDate } from '../../db' + bleeding as labels, + mucusFeeling as feelingLabels, + mucusTexture as textureLabels, +} from '../labels/labels' +import cycleDayModule from '../lib/get-cycle-day-number' +import { bleedingDaysSortedByDate } from '../db' const getCycleDayNumber = cycleDayModule() @@ -42,7 +43,7 @@ export default class DayView extends Component { const bleedingValue = this.cycleDay.bleeding && this.cycleDay.bleeding.value let bleedingLabel if (typeof bleedingValue === 'number') { - bleedingLabel = `${bleedingLabels[bleedingValue]}` + bleedingLabel = `${labels[bleedingValue]}` if (this.cycleDay.bleeding.exclude) bleedingLabel = "( " + bleedingLabel + " )" } else { bleedingLabel = 'edit' @@ -58,13 +59,14 @@ export default class DayView extends Component { temperatureLabel = 'edit' } - const mucusFeelingValue = this.cycleDay.mucus && this.cycleDay.mucus.value - let mucusFeelingLabel - if (typeof mucusFeelingValue === 'number') { - mucusFeelingLabel = `${feelingLabels[mucusFeelingValue]}` - if (this.cycleDay.mucus.exclude) mucusFeelingLabel = "( " + mucusFeelingLabel + " )" + const mucusFeelingValue = this.cycleDay.mucus && this.cycleDay.mucus.feeling + const mucusTextureValue = this.cycleDay.mucus && this.cycleDay.mucus.texture + let mucusLabel + if (typeof mucusFeelingValue === 'number' && typeof mucusTextureValue === 'number') { + mucusLabel = `${feelingLabels[mucusFeelingValue]} + ${textureLabels[mucusTextureValue]}` + if (this.cycleDay.mucus.exclude) mucusLabel = "( " + mucusLabel + " )" } else { - mucusFeelingLabel = 'edit' + mucusLabel = 'edit' } return ( @@ -94,7 +96,7 @@ export default class DayView extends Component { From 7e0456c10fa3b7bfe6e07ed984d5dbec06aa4e6a Mon Sep 17 00:00:00 2001 From: emelko Date: Wed, 4 Jul 2018 22:54:06 +0200 Subject: [PATCH 10/13] Adding unit tests to all possible mucus feeling/texture combinations --- components/mucus.js | 3 +- lib/sensiplan-mucus.js | 16 ++++++++ test/sensiplan-mucus.spec.js | 80 ++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 lib/sensiplan-mucus.js create mode 100644 test/sensiplan-mucus.spec.js diff --git a/components/mucus.js b/components/mucus.js index f2d643d..4c75148 100644 --- a/components/mucus.js +++ b/components/mucus.js @@ -12,6 +12,7 @@ import { mucusFeeling as feelingLabels, mucusTexture as textureLabels } from '../labels/labels' +import computeSensiplanValue from '../lib/sensiplan-mucus' export default class Mucus extends Component { constructor(props) { @@ -137,11 +138,11 @@ export default class Mucus extends Component { saveMucus(this.cycleDay, { feeling: this.state.currentFeelingValue, texture: this.state.currentTextureValue, + computedValue: computeSensiplanValue(this.state.currentFeelingValue, this.state.currentTextureValue), exclude: this.state.exclude }) this.showView('dayView') }} - // FIXME: find out how disabled works when 2 values need to be checked disabled={ this.state.currentFeelingValue === -1 || this.state.currentTextureValue === -1 } title="Save"> diff --git a/lib/sensiplan-mucus.js b/lib/sensiplan-mucus.js new file mode 100644 index 0000000..27e1590 --- /dev/null +++ b/lib/sensiplan-mucus.js @@ -0,0 +1,16 @@ +export default function (feeling, texture) { + const feelingMapping = { + 0: 0, + 1: 1, + 2: 2, + 3: 4 + } + const textureMapping = { + 0: 0, + 1: 3, + 2: 4 + } + const nfpFeelingValue = feelingMapping[feeling] + const nfpTextureValue = textureMapping[texture] + return Math.max(nfpFeelingValue, nfpTextureValue) +} diff --git a/test/sensiplan-mucus.spec.js b/test/sensiplan-mucus.spec.js new file mode 100644 index 0000000..feb4e93 --- /dev/null +++ b/test/sensiplan-mucus.spec.js @@ -0,0 +1,80 @@ +import chai from 'chai' +import dirtyChai from 'dirty-chai' + +const expect = chai.expect +chai.use(dirtyChai) + +import getSensiplanMucus from '../lib/sensiplan-mucus' + +describe.only('getSensiplanMucus', () => { + + describe('results in t for:', () => { + it('dry feeling and no texture', function () { + const sensiplanValue = getSensiplanMucus(0, 0) + expect(sensiplanValue).to.eql(0) + }) + }) + + describe('results in Ø for:', () => { + it('no feeling and no texture', function () { + const sensiplanValue = getSensiplanMucus(1, 0) + expect(sensiplanValue).to.eql(1) + }) + }) + + describe('results in f for:', () => { + it('wet feeling and no texture', function () { + const sensiplanValue = getSensiplanMucus(2, 0) + expect(sensiplanValue).to.eql(2) + }) + }) + + describe('results in S for:', () => { + it('dry feeling and creamy texture', function () { + const sensiplanValue = getSensiplanMucus(0, 1) + expect(sensiplanValue).to.eql(3) + }) + + it('no feeling and creamy texture', function () { + const sensiplanValue = getSensiplanMucus(1, 1) + expect(sensiplanValue).to.eql(3) + }) + + it('wet feeling and creamy texture', function () { + const sensiplanValue = getSensiplanMucus(2, 1) + expect(sensiplanValue).to.eql(3) + }) + }) + + describe('results in +S for:', () => { + it('dry feeling and egg white texture', function () { + const sensiplanValue = getSensiplanMucus(0, 2) + expect(sensiplanValue).to.eql(4) + }) + + it('no feeling and egg white texture', function () { + const sensiplanValue = getSensiplanMucus(1, 2) + expect(sensiplanValue).to.eql(4) + }) + + it('wet feeling and egg white texture', function () { + const sensiplanValue = getSensiplanMucus(2, 2) + expect(sensiplanValue).to.eql(4) + }) + + it('slippery feeling and egg white texture', function () { + const sensiplanValue = getSensiplanMucus(3, 2) + expect(sensiplanValue).to.eql(4) + }) + + it('slippery feeling and creamy texture', function () { + const sensiplanValue = getSensiplanMucus(3, 1) + expect(sensiplanValue).to.eql(4) + }) + + it('slippery feeling and no texture', function () { + const sensiplanValue = getSensiplanMucus(3, 0) + expect(sensiplanValue).to.eql(4) + }) + }) +}) From d89e1038438ae61bfa95bfeaf1d1d5eb513fa5dc Mon Sep 17 00:00:00 2001 From: emelko Date: Fri, 13 Jul 2018 23:00:26 +0200 Subject: [PATCH 11/13] Adding computed NFP mucus value to database and do cycle day overview --- components/cycle-day/cycle-day-overview.js | 4 +++- components/cycle-day/labels/labels.js | 4 +++- components/mucus.js | 3 ++- db.js | 1 + 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/components/cycle-day/cycle-day-overview.js b/components/cycle-day/cycle-day-overview.js index 98a7880..2203653 100644 --- a/components/cycle-day/cycle-day-overview.js +++ b/components/cycle-day/cycle-day-overview.js @@ -9,6 +9,7 @@ import { bleeding as labels, mucusFeeling as feelingLabels, mucusTexture as textureLabels, + mucusNFP as computeSensiplanMucusLabels, } from '../labels/labels' import cycleDayModule from '../lib/get-cycle-day-number' import { bleedingDaysSortedByDate } from '../db' @@ -61,9 +62,10 @@ export default class DayView extends Component { const mucusFeelingValue = this.cycleDay.mucus && this.cycleDay.mucus.feeling const mucusTextureValue = this.cycleDay.mucus && this.cycleDay.mucus.texture + const mucusComputedValue = this.cycleDay.mucus && this.cycleDay.mucus.computedNfp let mucusLabel if (typeof mucusFeelingValue === 'number' && typeof mucusTextureValue === 'number') { - mucusLabel = `${feelingLabels[mucusFeelingValue]} + ${textureLabels[mucusTextureValue]}` + mucusLabel = `${feelingLabels[mucusFeelingValue]} + ${textureLabels[mucusTextureValue]} ( ${computeSensiplanMucusLabels[mucusComputedValue]} )` if (this.cycleDay.mucus.exclude) mucusLabel = "( " + mucusLabel + " )" } else { mucusLabel = 'edit' diff --git a/components/cycle-day/labels/labels.js b/components/cycle-day/labels/labels.js index 83c1afe..459370b 100644 --- a/components/cycle-day/labels/labels.js +++ b/components/cycle-day/labels/labels.js @@ -1,9 +1,11 @@ const bleeding = ['spotting', 'light', 'medium', 'heavy'] const mucusFeeling = ['dry', 'nothing', 'wet', 'slippery'] const mucusTexture = ['nothing', 'creamy', 'egg white'] +const mucusNFP = ['t', 'Ø', 'f', 'S', '+S'] export { bleeding, mucusFeeling, - mucusTexture + mucusTexture, + mucusNFP } diff --git a/components/mucus.js b/components/mucus.js index 4c75148..c1d47df 100644 --- a/components/mucus.js +++ b/components/mucus.js @@ -32,6 +32,7 @@ export default class Mucus extends Component { this.state = { currentFeelingValue, currentTextureValue, + computeSensiplanValue, exclude: this.cycleDay.mucus ? this.cycleDay.mucus.exclude : false } @@ -138,7 +139,7 @@ export default class Mucus extends Component { saveMucus(this.cycleDay, { feeling: this.state.currentFeelingValue, texture: this.state.currentTextureValue, - computedValue: computeSensiplanValue(this.state.currentFeelingValue, this.state.currentTextureValue), + computedNfp: computeSensiplanValue(this.state.currentFeelingValue, this.state.currentTextureValue), exclude: this.state.exclude }) this.showView('dayView') diff --git a/db.js b/db.js index 455acd8..c0533bf 100644 --- a/db.js +++ b/db.js @@ -23,6 +23,7 @@ const MucusSchema = { properties: { feeling: 'int', texture: 'int', + computedNfp: 'int', exclude: 'bool' } } From 0da3810aeb08d0d5dbc0c42a88168b86c1ea62b8 Mon Sep 17 00:00:00 2001 From: emelko Date: Fri, 13 Jul 2018 23:01:07 +0200 Subject: [PATCH 12/13] Better naming for bleedin labels --- components/bleeding.js | 102 +++++++++++++++++++++ components/cycle-day/cycle-day-overview.js | 4 +- 2 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 components/bleeding.js diff --git a/components/bleeding.js b/components/bleeding.js new file mode 100644 index 0000000..5fc6250 --- /dev/null +++ b/components/bleeding.js @@ -0,0 +1,102 @@ +import React, { Component } from 'react' +import { + View, + Button, + Text, + Switch +} from 'react-native' +import RadioForm from 'react-native-simple-radio-button' +import styles from '../styles/index' +import { saveBleeding } from '../db' +import { bleeding as bleedingLabels } from '../labels/labels' + +export default class Bleeding extends Component { + constructor(props) { + super(props) + this.cycleDay = props.cycleDay + this.showView = props.showView + let bleedingValue = this.cycleDay.bleeding && this.cycleDay.bleeding.value + if (! (typeof bleedingValue === 'number') ){ + bleedingValue = -1 + } + this.state = { + currentValue: bleedingValue, + exclude: this.cycleDay.bleeding ? this.cycleDay.bleeding.exclude : false + } + } + + render() { + const bleedingRadioProps = [ + {label: bleedingLabels[0], value: 0 }, + {label: bleedingLabels[1], value: 1 }, + {label: bleedingLabels[2], value: 2 }, + {label: bleedingLabels[3], value: 3 }, + ] + return ( + + + + + Bleeding + + + { + this.setState({currentValue: itemValue}) + }} + /> + + + + + Exclude + + + { + this.setState({exclude: val}) + }} + value={this.state.exclude} + /> + + + + + + + + + + + + + + + + ) + } +} diff --git a/components/cycle-day/cycle-day-overview.js b/components/cycle-day/cycle-day-overview.js index 2203653..1212e13 100644 --- a/components/cycle-day/cycle-day-overview.js +++ b/components/cycle-day/cycle-day-overview.js @@ -6,7 +6,7 @@ import { } from 'react-native' import styles from '../../styles' import { - bleeding as labels, + bleeding as bleedingLabels, mucusFeeling as feelingLabels, mucusTexture as textureLabels, mucusNFP as computeSensiplanMucusLabels, @@ -44,7 +44,7 @@ export default class DayView extends Component { const bleedingValue = this.cycleDay.bleeding && this.cycleDay.bleeding.value let bleedingLabel if (typeof bleedingValue === 'number') { - bleedingLabel = `${labels[bleedingValue]}` + bleedingLabel = `${bleedingLabels[bleedingValue]}` if (this.cycleDay.bleeding.exclude) bleedingLabel = "( " + bleedingLabel + " )" } else { bleedingLabel = 'edit' From 6f755c55dc0ff99bdb6e30619f5b81c1fe6c142f Mon Sep 17 00:00:00 2001 From: emelko Date: Mon, 16 Jul 2018 12:39:11 +0200 Subject: [PATCH 13/13] Post refactor clean-up --- components/bleeding.js | 102 ------------- components/cycle-day/action-buttons.js | 2 +- components/cycle-day/cycle-day-overview.js | 14 +- components/cycle-day/index.js | 6 +- components/cycle-day/symptoms/mucus.js | 38 ++--- components/mucus.js | 157 --------------------- 6 files changed, 30 insertions(+), 289 deletions(-) delete mode 100644 components/bleeding.js delete mode 100644 components/mucus.js diff --git a/components/bleeding.js b/components/bleeding.js deleted file mode 100644 index 5fc6250..0000000 --- a/components/bleeding.js +++ /dev/null @@ -1,102 +0,0 @@ -import React, { Component } from 'react' -import { - View, - Button, - Text, - Switch -} from 'react-native' -import RadioForm from 'react-native-simple-radio-button' -import styles from '../styles/index' -import { saveBleeding } from '../db' -import { bleeding as bleedingLabels } from '../labels/labels' - -export default class Bleeding extends Component { - constructor(props) { - super(props) - this.cycleDay = props.cycleDay - this.showView = props.showView - let bleedingValue = this.cycleDay.bleeding && this.cycleDay.bleeding.value - if (! (typeof bleedingValue === 'number') ){ - bleedingValue = -1 - } - this.state = { - currentValue: bleedingValue, - exclude: this.cycleDay.bleeding ? this.cycleDay.bleeding.exclude : false - } - } - - render() { - const bleedingRadioProps = [ - {label: bleedingLabels[0], value: 0 }, - {label: bleedingLabels[1], value: 1 }, - {label: bleedingLabels[2], value: 2 }, - {label: bleedingLabels[3], value: 3 }, - ] - return ( - - - - - Bleeding - - - { - this.setState({currentValue: itemValue}) - }} - /> - - - - - Exclude - - - { - this.setState({exclude: val}) - }} - value={this.state.exclude} - /> - - - - - - - - - - - - - - - - ) - } -} diff --git a/components/cycle-day/action-buttons.js b/components/cycle-day/action-buttons.js index 0008552..eb33201 100644 --- a/components/cycle-day/action-buttons.js +++ b/components/cycle-day/action-buttons.js @@ -24,7 +24,7 @@ export default function (showView) { saveAction() showView('dayView') }, - disabled: saveDisabled + disabledCondition: saveDisabled } ] diff --git a/components/cycle-day/cycle-day-overview.js b/components/cycle-day/cycle-day-overview.js index 1212e13..13d43a4 100644 --- a/components/cycle-day/cycle-day-overview.js +++ b/components/cycle-day/cycle-day-overview.js @@ -10,9 +10,9 @@ import { mucusFeeling as feelingLabels, mucusTexture as textureLabels, mucusNFP as computeSensiplanMucusLabels, -} from '../labels/labels' -import cycleDayModule from '../lib/get-cycle-day-number' -import { bleedingDaysSortedByDate } from '../db' +} from './labels/labels' +import cycleDayModule from '../../lib/get-cycle-day-number' +import { bleedingDaysSortedByDate } from '../../db' const getCycleDayNumber = cycleDayModule() @@ -91,11 +91,9 @@ export default class DayView extends Component { - - - Mucus - - + + Mucus + - - - - - - - - - - - - - - ) - } -}