Splits the temperature view to simplify it
This commit is contained in:
@@ -0,0 +1,103 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
|
import { View } from 'react-native'
|
||||||
|
import AppText from '../../app-text'
|
||||||
|
import AppTextInput from '../../app-text-input'
|
||||||
|
|
||||||
|
import { temperature as labels } from '../../../i18n/en/cycle-day'
|
||||||
|
|
||||||
|
import styles from '../../../styles'
|
||||||
|
|
||||||
|
import { getPreviousTemperature } from '../../../db'
|
||||||
|
import { scaleObservable } from '../../../local-storage'
|
||||||
|
import config from '../../../config'
|
||||||
|
|
||||||
|
export default class TemperatureInput extends Component {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
temperature: PropTypes.string,
|
||||||
|
handleTemperatureChange: PropTypes.func,
|
||||||
|
date: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
let shouldShowSuggestion = false
|
||||||
|
let suggestedTemperature = null
|
||||||
|
|
||||||
|
if (!props.temperature) {
|
||||||
|
const prevTemp = getPreviousTemperature(props.date)
|
||||||
|
if (prevTemp) {
|
||||||
|
shouldShowSuggestion = true
|
||||||
|
suggestedTemperature = prevTemp.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
temperature: props.temperature,
|
||||||
|
shouldShowSuggestion,
|
||||||
|
suggestedTemperature
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTemperature = (temperature) => {
|
||||||
|
this.setState({
|
||||||
|
shouldShowSuggestion: false,
|
||||||
|
temperature
|
||||||
|
})
|
||||||
|
this.props.handleTemperatureChange(Number(temperature))
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const {
|
||||||
|
shouldShowSuggestion,
|
||||||
|
suggestedTemperature,
|
||||||
|
temperature
|
||||||
|
} = this.state
|
||||||
|
const inputStyle = [
|
||||||
|
styles.temperatureTextInput,
|
||||||
|
shouldShowSuggestion ? styles.temperatureTextInputSuggestion : null
|
||||||
|
]
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<View style={styles.framedSegmentInlineChildren}>
|
||||||
|
<AppTextInput
|
||||||
|
style={inputStyle}
|
||||||
|
autoFocus={true}
|
||||||
|
value={shouldShowSuggestion ? suggestedTemperature : temperature}
|
||||||
|
onChangeText={this.setTemperature}
|
||||||
|
keyboardType='numeric'
|
||||||
|
maxLength={5}
|
||||||
|
/>
|
||||||
|
<AppText style={{ marginLeft: 5 }}>°C</AppText>
|
||||||
|
</View>
|
||||||
|
<OutOfRangeWarning temperature={this.props.temperature} />
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const OutOfRangeWarning = ({ temperature }) => {
|
||||||
|
if (temperature === '') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = Number(temperature)
|
||||||
|
const { min, max } = config.temperatureScale
|
||||||
|
const range = { min, max }
|
||||||
|
const scale = scaleObservable.value
|
||||||
|
|
||||||
|
let warningMsg
|
||||||
|
|
||||||
|
if (value < range.min || value > range.max) {
|
||||||
|
warningMsg = labels.outOfAbsoluteRangeWarning
|
||||||
|
} else if (value < scale.min || value > scale.max) {
|
||||||
|
warningMsg = labels.outOfRangeWarning
|
||||||
|
} else {
|
||||||
|
warningMsg = null
|
||||||
|
}
|
||||||
|
|
||||||
|
return <AppText style={styles.hint}>{warningMsg}</AppText>
|
||||||
|
}
|
||||||
@@ -1,32 +1,31 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {
|
import { Switch, ScrollView } from 'react-native'
|
||||||
View,
|
import PropTypes from 'prop-types'
|
||||||
Switch,
|
|
||||||
Keyboard,
|
|
||||||
ScrollView
|
|
||||||
} from 'react-native'
|
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
|
|
||||||
import { getDate } from '../../../slices/date'
|
import { getDate } from '../../../slices/date'
|
||||||
|
|
||||||
import DateTimePicker from 'react-native-modal-datetime-picker-nevo'
|
|
||||||
import padWithZeros from '../../helpers/pad-time-with-zeros'
|
|
||||||
|
|
||||||
import { getPreviousTemperature } from '../../../db'
|
|
||||||
import styles from '../../../styles'
|
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 { scaleObservable } from '../../../local-storage'
|
|
||||||
import { shared as sharedLabels } from '../../../i18n/en/labels'
|
import { shared as sharedLabels } from '../../../i18n/en/labels'
|
||||||
import config from '../../../config'
|
|
||||||
import AppTextInput from '../../app-text-input'
|
import AppTextInput from '../../app-text-input'
|
||||||
import AppText from '../../app-text'
|
|
||||||
import SymptomSection from './symptom-section'
|
import SymptomSection from './symptom-section'
|
||||||
import SymptomView from './symptom-view'
|
import SymptomView from './symptom-view'
|
||||||
|
import TimeInput from './time-input'
|
||||||
|
import TemperatureInput from './temperature-input'
|
||||||
|
|
||||||
const minutes = ChronoUnit.MINUTES
|
const minutes = ChronoUnit.MINUTES
|
||||||
|
|
||||||
class Temp extends SymptomView {
|
class Temperature extends SymptomView {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
cycleDay: PropTypes.object,
|
||||||
|
handleBackButtonPress: PropTypes.func,
|
||||||
|
date: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
const cycleDay = props.cycleDay
|
const cycleDay = props.cycleDay
|
||||||
@@ -37,23 +36,9 @@ class Temp extends SymptomView {
|
|||||||
this.state = {
|
this.state = {
|
||||||
exclude: temp ? temp.exclude : false,
|
exclude: temp ? temp.exclude : false,
|
||||||
time: temp ? temp.time : LocalTime.now().truncatedTo(minutes).toString(),
|
time: temp ? temp.time : LocalTime.now().truncatedTo(minutes).toString(),
|
||||||
isTimePickerVisible: false,
|
temperature: temp ? temp.value : null,
|
||||||
note: temp ? temp.note : null
|
note: temp ? temp.note : null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (temp) {
|
|
||||||
this.state.temperature = temp.value.toString()
|
|
||||||
if (temp.value === Math.floor(temp.value)) {
|
|
||||||
this.state.temperature = `${this.state.temperature}.0`
|
|
||||||
}
|
|
||||||
this.state.outOfRangeWarning = makeOutOfRangeWarningMessage(this.state.temperature)
|
|
||||||
} else {
|
|
||||||
const prevTemp = getPreviousTemperature(props.date)
|
|
||||||
if (prevTemp) {
|
|
||||||
this.state.suggestedTemperature = prevTemp.toString()
|
|
||||||
this.state.isSuggestion = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
symptomName = 'temperature'
|
symptomName = 'temperature'
|
||||||
@@ -67,13 +52,13 @@ class Temp extends SymptomView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
autoSave = () => {
|
autoSave = () => {
|
||||||
if (typeof this.state.temperature != 'string' || this.state.temperature === '') {
|
if (!this.state.temperature) {
|
||||||
this.deleteSymptomEntry()
|
this.deleteSymptomEntry()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataToSave = {
|
const dataToSave = {
|
||||||
value: Number(this.state.temperature),
|
value: this.state.temperature,
|
||||||
exclude: this.state.exclude,
|
exclude: this.state.exclude,
|
||||||
time: this.state.time,
|
time: this.state.time,
|
||||||
note: this.state.note
|
note: this.state.note
|
||||||
@@ -82,73 +67,38 @@ class Temp extends SymptomView {
|
|||||||
this.saveSymptomEntry(dataToSave)
|
this.saveSymptomEntry(dataToSave)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTime = (time) => {
|
||||||
|
this.setState({ time })
|
||||||
|
}
|
||||||
|
|
||||||
setTemperature = (temperature) => {
|
setTemperature = (temperature) => {
|
||||||
if (isNaN(Number(temperature))) return
|
this.setState({ temperature })
|
||||||
this.setState({
|
|
||||||
temperature, isSuggestion: false,
|
|
||||||
outOfRangeWarning: makeOutOfRangeWarningMessage(temperature)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setNote = (note) => {
|
setNote = (note) => {
|
||||||
this.setState({ note })
|
this.setState({ note })
|
||||||
}
|
}
|
||||||
|
|
||||||
showTimePicker = () => {
|
|
||||||
Keyboard.dismiss()
|
|
||||||
this.setState({ isTimePickerVisible: true })
|
|
||||||
}
|
|
||||||
|
|
||||||
renderContent() {
|
renderContent() {
|
||||||
const inputStyle = [styles.temperatureTextInput]
|
const { temperature } = this.state
|
||||||
if (this.state.isSuggestion) {
|
|
||||||
inputStyle.push(styles.temperatureTextInputSuggestion)
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<ScrollView style={styles.page}>
|
<ScrollView style={styles.page}>
|
||||||
<SymptomSection
|
<SymptomSection
|
||||||
header={labels.temperature.header}
|
header={labels.temperature.header}
|
||||||
explainer={labels.temperature.explainer}
|
explainer={labels.temperature.explainer}
|
||||||
>
|
>
|
||||||
<View style={styles.framedSegmentInlineChildren}>
|
<TemperatureInput
|
||||||
<AppTextInput
|
temperature={temperature ? temperature.toFixed(2) : ''}
|
||||||
style={[inputStyle]}
|
date={this.props.date}
|
||||||
autoFocus={true}
|
handleTemperatureChange={this.setTemperature}
|
||||||
value={this.state.temperature || this.state.suggestedTemperature}
|
/>
|
||||||
onChangeText={this.setTemperature}
|
|
||||||
keyboardType='numeric'
|
|
||||||
maxLength={5}
|
|
||||||
/>
|
|
||||||
<AppText style={{ marginLeft: 5 }}>°C</AppText>
|
|
||||||
</View>
|
|
||||||
{this.state.outOfRangeWarning &&
|
|
||||||
<AppText style={styles.hint}>
|
|
||||||
{this.state.outOfRangeWarning}
|
|
||||||
</AppText>
|
|
||||||
}
|
|
||||||
</SymptomSection>
|
</SymptomSection>
|
||||||
<SymptomSection
|
<SymptomSection header={labels.time}>
|
||||||
header={labels.time}
|
<TimeInput
|
||||||
>
|
time={this.state.time}
|
||||||
<View style={styles.framedSegmentInlineChildren}>
|
handleTimeChange={this.setTime}
|
||||||
<AppTextInput
|
/>
|
||||||
style={[styles.temperatureTextInput]}
|
|
||||||
onFocus={this.showTimePicker}
|
|
||||||
value={this.state.time}
|
|
||||||
/>
|
|
||||||
<DateTimePicker
|
|
||||||
mode="time"
|
|
||||||
isVisible={this.state.isTimePickerVisible}
|
|
||||||
onConfirm={jsDate => {
|
|
||||||
this.setState({
|
|
||||||
time: padWithZeros(jsDate),
|
|
||||||
isTimePickerVisible: false
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
onCancel={() => this.setState({ isTimePickerVisible: false })}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</SymptomSection>
|
</SymptomSection>
|
||||||
<SymptomSection
|
<SymptomSection
|
||||||
header={labels.note.header}
|
header={labels.note.header}
|
||||||
@@ -188,23 +138,4 @@ const mapStateToProps = (state) => {
|
|||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
null
|
null
|
||||||
)(Temp)
|
)(Temperature)
|
||||||
|
|
||||||
function makeOutOfRangeWarningMessage(temperature) {
|
|
||||||
if (temperature === '') return
|
|
||||||
const value = Number(temperature)
|
|
||||||
const { min, max } = config.temperatureScale
|
|
||||||
const range = { min, max }
|
|
||||||
const scale = scaleObservable.value
|
|
||||||
let warningMsg
|
|
||||||
|
|
||||||
if (value < range.min || value > range.max) {
|
|
||||||
warningMsg = labels.outOfAbsoluteRangeWarning
|
|
||||||
} else if (value < scale.min || value > scale.max) {
|
|
||||||
warningMsg = labels.outOfRangeWarning
|
|
||||||
} else {
|
|
||||||
warningMsg = null
|
|
||||||
}
|
|
||||||
|
|
||||||
return warningMsg
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { Keyboard } from 'react-native'
|
||||||
|
|
||||||
|
import AppTextInput from '../../app-text-input'
|
||||||
|
import styles from '../../../styles'
|
||||||
|
|
||||||
|
import DateTimePicker from 'react-native-modal-datetime-picker-nevo'
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
|
export default class TimeInput extends Component {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
time: PropTypes.string,
|
||||||
|
handleTimeChange: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
isTimePickerVisible: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
showTimePicker = () => {
|
||||||
|
Keyboard.dismiss()
|
||||||
|
this.setState({ isTimePickerVisible: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
hideTimePicker = () => {
|
||||||
|
this.setState({ isTimePickerVisible: false })
|
||||||
|
}
|
||||||
|
|
||||||
|
handleConfirm = (jsDate) => {
|
||||||
|
// DateTimePicker also when in mode="time" returns full date (with time)
|
||||||
|
const time = moment(jsDate).format('HH:mm')
|
||||||
|
this.props.handleTimeChange(time)
|
||||||
|
this.setState({
|
||||||
|
isTimePickerVisible: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<AppTextInput
|
||||||
|
style={styles.temperatureTextInput}
|
||||||
|
onFocus={this.showTimePicker}
|
||||||
|
value={this.props.time}
|
||||||
|
/>
|
||||||
|
<DateTimePicker
|
||||||
|
mode="time"
|
||||||
|
isVisible={this.state.isTimePickerVisible}
|
||||||
|
onConfirm={this.handleConfirm}
|
||||||
|
onCancel={this.hideTimePicker}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user