Merge branch 'main' into '650-long-text-in-note-hides-behind-keyboard-1.2402.16'
# Conflicts: # android/app/build.gradle # ios/drip/Info.plist # package.json
This commit is contained in:
+16
-3
@@ -14,7 +14,10 @@ import {
|
||||
determinePredictionText,
|
||||
formatWithOrdinalSuffix,
|
||||
} from './helpers/home'
|
||||
import { periodPredictionObservable } from '../local-storage'
|
||||
import {
|
||||
fertilityTrackingObservable,
|
||||
periodPredictionObservable,
|
||||
} from '../local-storage'
|
||||
|
||||
import { Colors, Fonts, Sizes, Spacing } from '../styles'
|
||||
import { LocalDate } from '@js-joda/core'
|
||||
@@ -28,11 +31,12 @@ const Home = ({ navigate, setDate }) => {
|
||||
navigate('CycleDay')
|
||||
}
|
||||
|
||||
const isFertilityTrackingEnabled = fertilityTrackingObservable.value
|
||||
const todayDateString = LocalDate.now().toString()
|
||||
const { getCycleDayNumber, getPredictedMenses } = cycleModule()
|
||||
const cycleDayNumber = getCycleDayNumber(todayDateString)
|
||||
const { status, phase, statusText } =
|
||||
getFertilityStatusForDay(todayDateString)
|
||||
isFertilityTrackingEnabled && getFertilityStatusForDay(todayDateString)
|
||||
const isPeriodPredictionEnabled = periodPredictionObservable.value
|
||||
const prediction = determinePredictionText(getPredictedMenses(), t)
|
||||
|
||||
@@ -47,6 +51,7 @@ const Home = ({ navigate, setDate }) => {
|
||||
>
|
||||
<AppText style={styles.title}>{moment().format('MMM Do YYYY')}</AppText>
|
||||
|
||||
{/* display if at least 1 bleeding day has been entered */}
|
||||
{cycleDayNumber && (
|
||||
<View style={styles.line}>
|
||||
<AppText style={styles.whiteSubtitle}>{cycleDayText}</AppText>
|
||||
@@ -55,7 +60,9 @@ const Home = ({ navigate, setDate }) => {
|
||||
</AppText>
|
||||
</View>
|
||||
)}
|
||||
{phase && (
|
||||
|
||||
{/* display if fertility tracking enabled and if phase 1, 2 or 3 has been identified */}
|
||||
{isFertilityTrackingEnabled && phase && (
|
||||
<View style={styles.line}>
|
||||
<AppText style={styles.whiteSubtitle}>
|
||||
{formatWithOrdinalSuffix(phase)}
|
||||
@@ -67,11 +74,14 @@ const Home = ({ navigate, setDate }) => {
|
||||
<Asterisk />
|
||||
</View>
|
||||
)}
|
||||
|
||||
{isPeriodPredictionEnabled && (
|
||||
<View style={styles.line}>
|
||||
<AppText style={styles.turquoiseText}>{prediction}</AppText>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{!isFertilityTrackingEnabled && <View style={styles.largePadding}></View>}
|
||||
<Button isCTA isSmall={false} onPress={navigateToCycleDayView}>
|
||||
{t('labels.home.addDataForToday')}
|
||||
</Button>
|
||||
@@ -110,6 +120,9 @@ const styles = StyleSheet.create({
|
||||
color: 'white',
|
||||
fontSize: Sizes.subtitle,
|
||||
},
|
||||
largePadding: {
|
||||
padding: Spacing.large,
|
||||
},
|
||||
})
|
||||
|
||||
Home.propTypes = {
|
||||
|
||||
@@ -22,6 +22,8 @@ import {
|
||||
painTrackingCategoryObservable,
|
||||
sexTrackingCategoryObservable,
|
||||
temperatureTrackingCategoryObservable,
|
||||
mucusTrackingCategoryObservable,
|
||||
cervixTrackingCategoryObservable,
|
||||
} from '../../local-storage'
|
||||
import { makeColumnInfo } from '../helpers/chart'
|
||||
|
||||
@@ -72,6 +74,10 @@ const CycleChart = ({ navigate, setDate }) => {
|
||||
const symptomRowEnabledSymptoms = symptomRowSymptoms.filter((symptom) => {
|
||||
if (symptom === 'sex') {
|
||||
return sexTrackingCategoryObservable.value ? symptom : null
|
||||
} else if (symptom === 'mucus') {
|
||||
return mucusTrackingCategoryObservable.value ? symptom : null
|
||||
} else if (symptom === 'cervix') {
|
||||
return cervixTrackingCategoryObservable.value ? symptom : null
|
||||
} else if (symptom === 'desire') {
|
||||
return desireTrackingCategoryObservable.value ? symptom : null
|
||||
} else if (symptom === 'pain') {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import React from 'react'
|
||||
import { StyleSheet, Switch, View } from 'react-native'
|
||||
import { Platform, StyleSheet, Switch, View } from 'react-native'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import AppText from './app-text'
|
||||
|
||||
import { Containers } from '../../styles'
|
||||
import { Colors, Containers, Spacing } from '../../styles'
|
||||
|
||||
const AppSwitch = ({ onToggle, text, value, trackColor, disabled }) => {
|
||||
const AppSwitch = ({ onToggle, text, value, disabled }) => {
|
||||
const trackColor = { true: Colors.turquoiseDark }
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={styles.textContainer}>
|
||||
@@ -27,16 +28,20 @@ AppSwitch.propTypes = {
|
||||
onToggle: PropTypes.func.isRequired,
|
||||
text: PropTypes.string,
|
||||
value: PropTypes.bool,
|
||||
trackColor: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
...Containers.rowContainer,
|
||||
marginTop: Spacing.tiny,
|
||||
},
|
||||
switch: {
|
||||
flex: 1,
|
||||
transform:
|
||||
Platform.OS === 'ios'
|
||||
? [{ scaleX: 0.8 }, { scaleY: 0.8 }]
|
||||
: [{ scaleX: 1 }, { scaleY: 1 }],
|
||||
},
|
||||
textContainer: {
|
||||
flex: 4,
|
||||
|
||||
@@ -6,13 +6,14 @@ import AppText from './app-text'
|
||||
|
||||
import { Colors, Containers, Spacing, Typography } from '../../styles'
|
||||
|
||||
const Segment = ({ children, last, title }) => {
|
||||
const Segment = ({ children, last, title, subheader }) => {
|
||||
const containerStyle = last ? styles.containerLast : styles.container
|
||||
const commonStyle = Object.assign({}, containerStyle)
|
||||
|
||||
return (
|
||||
<View style={commonStyle}>
|
||||
{title && <AppText style={styles.title}>{title}</AppText>}
|
||||
{subheader && <AppText style={styles.subheader}>{subheader}</AppText>}
|
||||
{children}
|
||||
</View>
|
||||
)
|
||||
@@ -23,6 +24,7 @@ Segment.propTypes = {
|
||||
last: PropTypes.bool,
|
||||
style: PropTypes.object,
|
||||
title: PropTypes.string,
|
||||
subheader: PropTypes.string,
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
@@ -39,6 +41,11 @@ const styles = StyleSheet.create({
|
||||
title: {
|
||||
...Typography.subtitle,
|
||||
},
|
||||
subheader: {
|
||||
...Typography.subtitle,
|
||||
fontWeight: 'bold',
|
||||
marginBottom: Spacing.zero,
|
||||
},
|
||||
})
|
||||
|
||||
export default Segment
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import React from 'react'
|
||||
import { Platform, StyleSheet, Switch, View } from 'react-native'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import AppText from './app-text'
|
||||
|
||||
import DripIcon from '../../assets/drip-icons'
|
||||
import { Colors, Containers, Sizes, Spacing } from '../../styles'
|
||||
|
||||
const TrackingCategorySwitch = ({ onToggle, symptom, text, value }) => {
|
||||
const trackColor = { true: Colors.turquoiseDark }
|
||||
const iconColor = value ? Colors.iconColors[symptom].color : Colors.grey
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={styles.iconContainer}>
|
||||
<DripIcon
|
||||
color={iconColor}
|
||||
name={`drip-icon-${symptom}`}
|
||||
size={Sizes.title}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.textContainer}>
|
||||
<AppText>{text}</AppText>
|
||||
</View>
|
||||
<Switch
|
||||
onValueChange={onToggle}
|
||||
style={styles.appSwitch}
|
||||
value={value}
|
||||
trackColor={trackColor}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
TrackingCategorySwitch.propTypes = {
|
||||
onToggle: PropTypes.func.isRequired,
|
||||
symptom: PropTypes.string,
|
||||
text: PropTypes.string,
|
||||
value: PropTypes.bool,
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
...Containers.rowContainer,
|
||||
marginVertical: Spacing.tiny,
|
||||
},
|
||||
iconContainer: {
|
||||
marginRight: Spacing.tiny,
|
||||
flex: 1,
|
||||
},
|
||||
textContainer: {
|
||||
flex: 5,
|
||||
},
|
||||
appSwitch: {
|
||||
flex: 2,
|
||||
transform:
|
||||
Platform.OS === 'ios'
|
||||
? [{ scaleX: 0.8 }, { scaleY: 0.8 }]
|
||||
: [{ scaleX: 1 }, { scaleY: 1 }],
|
||||
},
|
||||
})
|
||||
export default TrackingCategorySwitch
|
||||
@@ -16,6 +16,8 @@ import {
|
||||
painTrackingCategoryObservable,
|
||||
sexTrackingCategoryObservable,
|
||||
temperatureTrackingCategoryObservable,
|
||||
mucusTrackingCategoryObservable,
|
||||
cervixTrackingCategoryObservable,
|
||||
} from '../../local-storage'
|
||||
import { Spacing } from '../../styles'
|
||||
import { SYMPTOMS } from '../../config'
|
||||
@@ -40,6 +42,10 @@ const CycleDayOverView = ({ date, setDate, isTemperatureEditView }) => {
|
||||
return temperatureTrackingCategoryObservable.value ? symptom : null
|
||||
} else if (symptom === 'sex') {
|
||||
return sexTrackingCategoryObservable.value ? symptom : null
|
||||
} else if (symptom === 'mucus') {
|
||||
return mucusTrackingCategoryObservable.value ? symptom : null
|
||||
} else if (symptom === 'cervix') {
|
||||
return cervixTrackingCategoryObservable.value ? symptom : null
|
||||
} else if (symptom === 'desire') {
|
||||
return desireTrackingCategoryObservable.value ? symptom : null
|
||||
} else if (symptom === 'pain') {
|
||||
|
||||
@@ -1,22 +1,61 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { StyleSheet, TouchableOpacity, View } from 'react-native'
|
||||
import { Alert, StyleSheet, TouchableOpacity, View } from 'react-native'
|
||||
|
||||
import AppText from '../common/app-text'
|
||||
|
||||
import { Colors, Containers } from '../../styles'
|
||||
import labels from '../../i18n/en/settings'
|
||||
|
||||
export default function SelectTabGroup({
|
||||
activeButton,
|
||||
buttons,
|
||||
onSelect,
|
||||
disabled,
|
||||
}) {
|
||||
// TODO https://gitlab.com/bloodyhealth/drip/-/issues/707
|
||||
const oneTimeTransformIntoNumber =
|
||||
typeof activeButton === 'boolean' && Number(activeButton)
|
||||
const isSecondarySymptomSwitch =
|
||||
buttons[0]['label'] === labels.secondarySymptom.mucus
|
||||
|
||||
// Disable is only used for secondarySymptom in customization, if more come up maybe consider more tidy solution
|
||||
const showDisabledAlert = (label) => {
|
||||
if (
|
||||
label === labels.secondarySymptom.cervix ||
|
||||
label === labels.secondarySymptom.mucus
|
||||
) {
|
||||
Alert.alert(
|
||||
labels.secondarySymptom.disabled.title,
|
||||
labels.secondarySymptom.disabled.message
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default function SelectTabGroup({ activeButton, buttons, onSelect }) {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{buttons.map(({ label, value }, i) => {
|
||||
const isActive = value === activeButton
|
||||
const boxStyle = [styles.box, isActive && styles.boxActive]
|
||||
const textStyle = [styles.text, isActive && styles.textActive]
|
||||
const isActive =
|
||||
value === activeButton || value === oneTimeTransformIntoNumber
|
||||
const boxStyle = [
|
||||
styles.box,
|
||||
isActive && styles.boxActive,
|
||||
isSecondarySymptomSwitch && styles.purpleBox,
|
||||
isSecondarySymptomSwitch && isActive && styles.activePurpleBox,
|
||||
disabled && styles.disabledBox,
|
||||
]
|
||||
const textStyle = [
|
||||
styles.text,
|
||||
isSecondarySymptomSwitch && styles.purpleText,
|
||||
isActive && styles.textActive,
|
||||
disabled && styles.greyText,
|
||||
]
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={() => onSelect(value)}
|
||||
onPress={() =>
|
||||
!disabled ? onSelect(value) : showDisabledAlert(label)
|
||||
}
|
||||
key={i}
|
||||
style={boxStyle}
|
||||
>
|
||||
@@ -32,6 +71,7 @@ SelectTabGroup.propTypes = {
|
||||
activeButton: PropTypes.number,
|
||||
buttons: PropTypes.array.isRequired,
|
||||
onSelect: PropTypes.func.isRequired,
|
||||
disabled: PropTypes.bool,
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
@@ -50,4 +90,20 @@ const styles = StyleSheet.create({
|
||||
textActive: {
|
||||
color: 'white',
|
||||
},
|
||||
purpleBox: {
|
||||
borderColor: Colors.purple,
|
||||
},
|
||||
activePurpleBox: {
|
||||
backgroundColor: Colors.purple,
|
||||
},
|
||||
purpleText: {
|
||||
color: Colors.purple,
|
||||
},
|
||||
greyText: {
|
||||
color: Colors.grey,
|
||||
},
|
||||
disabledBox: {
|
||||
borderColor: Colors.grey,
|
||||
backgroundColor: Colors.turquoiseLight,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -20,7 +20,7 @@ const SymptomBox = ({
|
||||
editedSymptom,
|
||||
setEditedSymptom,
|
||||
}) => {
|
||||
const { t } = useTranslation(null, { keyPrefix: 'cycleDay.symptomBox' })
|
||||
const { t } = useTranslation(null, { keyPrefix: 'symptoms' })
|
||||
const isSymptomEdited = editedSymptom === symptom
|
||||
const isSymptomDisabled = isDateInFuture(date) && symptom !== 'note'
|
||||
const isExcluded = symptomData !== null ? symptomData.exclude : false
|
||||
|
||||
@@ -15,6 +15,7 @@ import Temperature from './temperature'
|
||||
import { blank, save, shouldShow, symtomPage } from '../helpers/cycle-day'
|
||||
import { showToast } from '../helpers/general'
|
||||
|
||||
import { fertilityTrackingObservable } from '../../local-storage'
|
||||
import { shared as sharedLabels } from '../../i18n/en/labels'
|
||||
import info from '../../i18n/en/symptom-info'
|
||||
import { Colors, Containers, Sizes, Spacing } from '../../styles'
|
||||
@@ -25,6 +26,7 @@ const SymptomEditView = ({ date, onClose, symptom, symptomData }) => {
|
||||
const [shouldShowInfo, setShouldShowInfo] = useState(false)
|
||||
const getParsedData = () => JSON.parse(JSON.stringify(data))
|
||||
const onPressLearnMore = () => setShouldShowInfo(!shouldShowInfo)
|
||||
const isFertilityTrackingEnabled = fertilityTrackingObservable.value
|
||||
|
||||
const onEditNote = (note) => {
|
||||
const parsedData = getParsedData()
|
||||
@@ -167,15 +169,18 @@ const SymptomEditView = ({ date, onClose, symptom, symptomData }) => {
|
||||
</Segment>
|
||||
)
|
||||
})}
|
||||
{shouldShow(symptomConfig.excludeText) && (
|
||||
<Segment style={styles.segmentBorder}>
|
||||
<AppSwitch
|
||||
onToggle={onExcludeToggle}
|
||||
text={symtomPage[symptom].excludeText}
|
||||
value={data.exclude}
|
||||
/>
|
||||
</Segment>
|
||||
)}
|
||||
{/* show exclude AppSwitch for bleeding, mucus, cervix, temperature */}
|
||||
{/* but if fertility is off only for bleeding */}
|
||||
{shouldShow(symptomConfig.excludeText) &&
|
||||
(symptom === 'bleeding' || isFertilityTrackingEnabled) && (
|
||||
<Segment style={styles.segmentBorder}>
|
||||
<AppSwitch
|
||||
onToggle={onExcludeToggle}
|
||||
text={symtomPage[symptom].excludeText}
|
||||
value={data.exclude}
|
||||
/>
|
||||
</Segment>
|
||||
)}
|
||||
{shouldShow(symptomConfig.note) && (
|
||||
<Segment style={styles.segmentBorder}>
|
||||
<AppText>{symtomPage[symptom].note}</AppText>
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { LocalDate } from '@js-joda/core'
|
||||
|
||||
import { scaleObservable, unitObservable } from '../../local-storage'
|
||||
import {
|
||||
fertilityTrackingObservable,
|
||||
scaleObservable,
|
||||
unitObservable,
|
||||
} from '../../local-storage'
|
||||
import { getCycleStatusForDay } from '../../lib/sympto-adapter'
|
||||
import { getCycleDay, getAmountOfCycleDays } from '../../db'
|
||||
|
||||
@@ -270,7 +274,8 @@ export function nfpLines() {
|
||||
if (dateString < cycle.startDate) updateCurrentCycle(dateString)
|
||||
if (cycle.noMoreCycles) return ret
|
||||
|
||||
const tempShift = cycle.status.temperatureShift
|
||||
const tempShift =
|
||||
fertilityTrackingObservable.value && cycle.status.temperatureShift
|
||||
|
||||
if (tempShift) {
|
||||
if (tempShift.firstHighMeasurementDay.date === dateString) {
|
||||
|
||||
@@ -1,38 +1,49 @@
|
||||
import React, { useState } from 'react'
|
||||
import { Alert, Pressable } from 'react-native'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { Alert, Pressable, StyleSheet, View } from 'react-native'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import AppIcon from '../../common/app-icon'
|
||||
import AppPage from '../../common/app-page'
|
||||
import AppSwitch from '../../common/app-switch'
|
||||
import AppText from '../../common/app-text'
|
||||
import { Colors, Spacing, Typography } from '../../../styles'
|
||||
import TemperatureSlider from './temperature-slider'
|
||||
import Segment from '../../common/segment'
|
||||
import TrackingCategorySwitch from '../../common/tracking-category-switch'
|
||||
import SelectTabGroup from '../../cycle-day/select-tab-group'
|
||||
|
||||
import {
|
||||
desireTrackingCategoryObservable,
|
||||
fertilityTrackingObservable,
|
||||
moodTrackingCategoryObservable,
|
||||
noteTrackingCategoryObservable,
|
||||
painTrackingCategoryObservable,
|
||||
sexTrackingCategoryObservable,
|
||||
temperatureTrackingCategoryObservable,
|
||||
mucusTrackingCategoryObservable,
|
||||
cervixTrackingCategoryObservable,
|
||||
periodPredictionObservable,
|
||||
useCervixAsSecondarySymptomObservable,
|
||||
saveDesireTrackingCategory,
|
||||
saveFertilityTrackingEnabled,
|
||||
saveMoodTrackingCategory,
|
||||
saveNoteTrackingCategory,
|
||||
savePainTrackingCategory,
|
||||
saveMucusTrackingCategory,
|
||||
saveCervixTrackingCategory,
|
||||
savePeriodPrediction,
|
||||
saveSexTrackingCategory,
|
||||
saveTemperatureTrackingCategory,
|
||||
saveUseCervix,
|
||||
periodPredictionObservable,
|
||||
useCervixObservable,
|
||||
saveUseCervixAsSecondarySymptom,
|
||||
} from '../../../local-storage'
|
||||
import { Colors } from '../../../styles'
|
||||
import labels from '../../../i18n/en/settings'
|
||||
import { SYMPTOMS } from '../../../config'
|
||||
|
||||
const Settings = () => {
|
||||
const [shouldUseCervix, setShouldUseCervix] = useState(
|
||||
useCervixObservable.value
|
||||
)
|
||||
const { t } = useTranslation(null, { keyPrefix: 'symptoms' })
|
||||
|
||||
const [useCervixAsSecondarySymptom, setUseCervixAsSecondarySymptom] =
|
||||
useState(useCervixAsSecondarySymptomObservable.value)
|
||||
|
||||
const [isPeriodPredictionEnabled, setPeriodPrediction] = useState(
|
||||
periodPredictionObservable.value
|
||||
@@ -41,6 +52,14 @@ const Settings = () => {
|
||||
const [isTemperatureTrackingCategoryEnabled, setTemperatureTrackingCategory] =
|
||||
useState(temperatureTrackingCategoryObservable.value)
|
||||
|
||||
const [isMucusTrackingCategoryEnabled, setMucusTrackingCategory] = useState(
|
||||
mucusTrackingCategoryObservable.value
|
||||
)
|
||||
|
||||
const [isCervixTrackingCategoryEnabled, setCervixTrackingCategory] = useState(
|
||||
cervixTrackingCategoryObservable.value
|
||||
)
|
||||
|
||||
const [isSexTrackingCategoryEnabled, setSexTrackingCategory] = useState(
|
||||
sexTrackingCategoryObservable.value
|
||||
)
|
||||
@@ -61,19 +80,33 @@ const Settings = () => {
|
||||
noteTrackingCategoryObservable.value
|
||||
)
|
||||
|
||||
const [isEnabled, setIsEnabled] = useState(false)
|
||||
const toggleSwitch = () => setIsEnabled((previousState) => !previousState)
|
||||
const [isFertilityTrackingEnabled, setFertilityTrackingEnabled] = useState(
|
||||
fertilityTrackingObservable.value
|
||||
)
|
||||
|
||||
const fertilityTrackingToggle = (value) => {
|
||||
setFertilityTrackingEnabled(value)
|
||||
saveFertilityTrackingEnabled(value)
|
||||
}
|
||||
|
||||
const temperatureTrackingCategoryToggle = (value) => {
|
||||
setTemperatureTrackingCategory(value)
|
||||
saveTemperatureTrackingCategory(value)
|
||||
if (!value) {
|
||||
setFertilityTrackingEnabled(false)
|
||||
saveFertilityTrackingEnabled(false)
|
||||
}
|
||||
}
|
||||
const mucusTrackingCategoryToggle = (value) => {
|
||||
manageSecondarySymptom(cervixTrackingCategoryObservable.value, value)
|
||||
}
|
||||
const cervixTrackingCategoryToggle = (value) => {
|
||||
manageSecondarySymptom(value, mucusTrackingCategoryObservable.value)
|
||||
}
|
||||
|
||||
const sexTrackingCategoryToggle = (value) => {
|
||||
setSexTrackingCategory(value)
|
||||
saveSexTrackingCategory(value)
|
||||
}
|
||||
|
||||
const desireTrackingCategoryToggle = (value) => {
|
||||
setDesireTrackingCategory(value)
|
||||
saveDesireTrackingCategory(value)
|
||||
@@ -90,120 +123,229 @@ const Settings = () => {
|
||||
setNoteTrackingCategory(value)
|
||||
saveNoteTrackingCategory(value)
|
||||
}
|
||||
|
||||
const onPeriodPredictionToggle = (value) => {
|
||||
setPeriodPrediction(value)
|
||||
savePeriodPrediction(value)
|
||||
}
|
||||
|
||||
const fertilityTrackingText = isFertilityTrackingEnabled
|
||||
? labels.fertilityTracking.on
|
||||
: labels.fertilityTracking.off
|
||||
|
||||
const periodPredictionText = isPeriodPredictionEnabled
|
||||
? labels.periodPrediction.on
|
||||
: labels.periodPrediction.off
|
||||
|
||||
const onCervixToggle = (value) => {
|
||||
setShouldUseCervix(value)
|
||||
saveUseCervix(value)
|
||||
const secondarySymptomButtons = [
|
||||
{
|
||||
label: labels.secondarySymptom.mucus,
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
label: labels.secondarySymptom.cervix,
|
||||
value: 1,
|
||||
},
|
||||
]
|
||||
|
||||
const onSelectTab = (value) => {
|
||||
if (isMucusTrackingCategoryEnabled && isCervixTrackingCategoryEnabled) {
|
||||
setUseCervixAsSecondarySymptom(value)
|
||||
saveUseCervixAsSecondarySymptom(value)
|
||||
} else {
|
||||
secondarySymptomDisabledPrompt()
|
||||
}
|
||||
}
|
||||
|
||||
const cervixText = shouldUseCervix
|
||||
? labels.useCervix.cervixModeOn
|
||||
: labels.useCervix.cervixModeOff
|
||||
// is needed so secondary symptom is set correct on load
|
||||
useEffect(() => {
|
||||
manageSecondarySymptom(
|
||||
cervixTrackingCategoryObservable.value,
|
||||
mucusTrackingCategoryObservable.value
|
||||
)
|
||||
}, [])
|
||||
|
||||
const manageSecondarySymptom = (cervix, mucus) => {
|
||||
if (!cervix && mucus) {
|
||||
setUseCervixAsSecondarySymptom(0)
|
||||
saveUseCervixAsSecondarySymptom(0)
|
||||
} else if (cervix && !mucus) {
|
||||
setUseCervixAsSecondarySymptom(1)
|
||||
saveUseCervixAsSecondarySymptom(1)
|
||||
} else if (!cervix && !mucus) {
|
||||
setFertilityTrackingEnabled(false)
|
||||
saveFertilityTrackingEnabled(false)
|
||||
}
|
||||
setMucusTrackingCategory(mucus)
|
||||
saveMucusTrackingCategory(mucus)
|
||||
setCervixTrackingCategory(cervix)
|
||||
saveCervixTrackingCategory(cervix)
|
||||
}
|
||||
|
||||
const secondarySymptomDisabledPrompt = () => {
|
||||
if (!isFertilityTrackingEnabled) {
|
||||
Alert.alert(
|
||||
labels.secondarySymptom.disabled.title,
|
||||
labels.secondarySymptom.disabled.message
|
||||
)
|
||||
} else if (
|
||||
!isMucusTrackingCategoryEnabled == isCervixTrackingCategoryEnabled
|
||||
) {
|
||||
Alert.alert(
|
||||
labels.secondarySymptom.disabled.title,
|
||||
labels.secondarySymptom.disabled.noSecondaryEnabled
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const manageFertilityFeature =
|
||||
isTemperatureTrackingCategoryEnabled &&
|
||||
(isMucusTrackingCategoryEnabled || isCervixTrackingCategoryEnabled)
|
||||
|
||||
const cervixText = useCervixAsSecondarySymptom
|
||||
? labels.secondarySymptom.cervixModeOn
|
||||
: labels.secondarySymptom.cervixModeOff
|
||||
|
||||
const sliderDisabledPrompt = () => {
|
||||
if (!isTemperatureTrackingCategoryEnabled) {
|
||||
Alert.alert(labels.disabled.title, labels.disabled.message)
|
||||
Alert.alert(labels.tempScale.disabled, labels.tempScale.disabledMessage)
|
||||
}
|
||||
}
|
||||
|
||||
const fertilityDisabledPrompt = () => {
|
||||
if (!manageFertilityFeature) {
|
||||
Alert.alert(
|
||||
labels.fertilityTracking.disabledTitle,
|
||||
labels.fertilityTracking.disabled
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<AppPage title={'Customization'}>
|
||||
<Segment title={'Tracking categories'}>
|
||||
<AppSwitch
|
||||
<AppPage title={labels.customization.title}>
|
||||
<Segment title={labels.customization.trackingCategories}>
|
||||
<TrackingCategorySwitch
|
||||
onToggle={temperatureTrackingCategoryToggle}
|
||||
text={SYMPTOMS[1]}
|
||||
text={t(SYMPTOMS[1])}
|
||||
value={isTemperatureTrackingCategoryEnabled}
|
||||
trackColor={{ true: Colors.turquoiseDark }}
|
||||
symptom={SYMPTOMS[1]}
|
||||
/>
|
||||
<AppSwitch
|
||||
<TrackingCategorySwitch
|
||||
onToggle={(enabled) => {
|
||||
mucusTrackingCategoryToggle(enabled)
|
||||
}}
|
||||
text={t(SYMPTOMS[2])}
|
||||
value={isMucusTrackingCategoryEnabled}
|
||||
symptom={SYMPTOMS[2]}
|
||||
/>
|
||||
<TrackingCategorySwitch
|
||||
onToggle={(enabled) => {
|
||||
cervixTrackingCategoryToggle(enabled)
|
||||
}}
|
||||
text={t(SYMPTOMS[3])}
|
||||
value={isCervixTrackingCategoryEnabled}
|
||||
symptom={SYMPTOMS[3]}
|
||||
/>
|
||||
<TrackingCategorySwitch
|
||||
onToggle={sexTrackingCategoryToggle}
|
||||
text={SYMPTOMS[4]}
|
||||
text={t(SYMPTOMS[4])}
|
||||
value={isSexTrackingCategoryEnabled}
|
||||
trackColor={{ true: Colors.turquoiseDark }}
|
||||
symptom={SYMPTOMS[4]}
|
||||
/>
|
||||
<AppSwitch
|
||||
<TrackingCategorySwitch
|
||||
onToggle={desireTrackingCategoryToggle}
|
||||
text={SYMPTOMS[5]}
|
||||
text={t(SYMPTOMS[5])}
|
||||
value={isDesireTrackingCategoryEnabled}
|
||||
trackColor={{ true: Colors.turquoiseDark }}
|
||||
symptom={SYMPTOMS[5]}
|
||||
/>
|
||||
<AppSwitch
|
||||
<TrackingCategorySwitch
|
||||
onToggle={painTrackingCategoryToggle}
|
||||
text={SYMPTOMS[6]}
|
||||
text={t(SYMPTOMS[6])}
|
||||
value={isPainTrackingCategoryEnabled}
|
||||
trackColor={{ true: Colors.turquoiseDark }}
|
||||
symptom={SYMPTOMS[6]}
|
||||
/>
|
||||
<AppSwitch
|
||||
<TrackingCategorySwitch
|
||||
onToggle={moodTrackingCategoryToggle}
|
||||
text={SYMPTOMS[7]}
|
||||
text={t(SYMPTOMS[7])}
|
||||
value={isMoodTrackingCategoryEnabled}
|
||||
trackColor={{ true: Colors.turquoiseDark }}
|
||||
symptom={SYMPTOMS[7]}
|
||||
/>
|
||||
<AppSwitch
|
||||
<TrackingCategorySwitch
|
||||
onToggle={noteTrackingCategoryToggle}
|
||||
text={SYMPTOMS[8]}
|
||||
text={t(SYMPTOMS[8])}
|
||||
value={isNoteTrackingCategoryEnabled}
|
||||
trackColor={{ true: Colors.turquoiseDark }}
|
||||
symptom={SYMPTOMS[8]}
|
||||
/>
|
||||
</Segment>
|
||||
|
||||
<Segment title={'Fertility feature'}>
|
||||
<AppSwitch
|
||||
onToggle={toggleSwitch}
|
||||
text={'If turned on ...'}
|
||||
value={isEnabled}
|
||||
trackColor={{ true: Colors.turquoiseDark }}
|
||||
/>
|
||||
</Segment>
|
||||
|
||||
<Pressable onPress={sliderDisabledPrompt}>
|
||||
<Segment title={labels.tempScale.segmentTitle}>
|
||||
{isTemperatureTrackingCategoryEnabled && (
|
||||
<>
|
||||
<AppText>{labels.tempScale.segmentExplainer}</AppText>
|
||||
<TemperatureSlider />
|
||||
</>
|
||||
)}
|
||||
{!isTemperatureTrackingCategoryEnabled && (
|
||||
<AppText>{labels.disabled.message}</AppText>
|
||||
)}
|
||||
<Pressable onPress={fertilityDisabledPrompt}>
|
||||
<Segment title={labels.fertilityTracking.title}>
|
||||
<AppText>{labels.fertilityTracking.message}</AppText>
|
||||
<AppSwitch
|
||||
onToggle={fertilityTrackingToggle}
|
||||
text={fertilityTrackingText}
|
||||
value={isFertilityTrackingEnabled}
|
||||
disabled={!manageFertilityFeature}
|
||||
/>
|
||||
</Segment>
|
||||
</Pressable>
|
||||
|
||||
<Pressable onPress={sliderDisabledPrompt}>
|
||||
<Segment title={labels.useCervix.title}>
|
||||
{isTemperatureTrackingCategoryEnabled && (
|
||||
<AppSwitch
|
||||
onToggle={onCervixToggle}
|
||||
text={cervixText}
|
||||
value={shouldUseCervix}
|
||||
trackColor={{ true: Colors.turquoiseDark }}
|
||||
/>
|
||||
)}
|
||||
{!isTemperatureTrackingCategoryEnabled && (
|
||||
<AppText>{labels.disabled.message}</AppText>
|
||||
)}
|
||||
</Segment>
|
||||
</Pressable>
|
||||
|
||||
<Segment title={labels.periodPrediction.title} last>
|
||||
<Segment title={labels.periodPrediction.title}>
|
||||
<AppSwitch
|
||||
onToggle={onPeriodPredictionToggle}
|
||||
text={periodPredictionText}
|
||||
value={isPeriodPredictionEnabled}
|
||||
trackColor={{ true: Colors.turquoiseDark }}
|
||||
/>
|
||||
</Segment>
|
||||
|
||||
<Segment
|
||||
subheader={labels.customization.subheaderSymptoThermalMethod}
|
||||
last
|
||||
></Segment>
|
||||
|
||||
<Pressable onPress={sliderDisabledPrompt}>
|
||||
<Segment title={labels.tempScale.segmentTitle}>
|
||||
<AppText>{labels.tempScale.segmentExplainer}</AppText>
|
||||
<TemperatureSlider disabled={!isTemperatureTrackingCategoryEnabled} />
|
||||
</Segment>
|
||||
</Pressable>
|
||||
|
||||
<Pressable onPress={secondarySymptomDisabledPrompt}>
|
||||
<Segment title={labels.secondarySymptom.title}>
|
||||
<AppText>{cervixText}</AppText>
|
||||
<SelectTabGroup
|
||||
activeButton={useCervixAsSecondarySymptom}
|
||||
buttons={secondarySymptomButtons}
|
||||
onSelect={(value) => onSelectTab(value)}
|
||||
disabled={!isFertilityTrackingEnabled}
|
||||
/>
|
||||
</Segment>
|
||||
</Pressable>
|
||||
<Segment last>
|
||||
<View style={styles.line}>
|
||||
<AppIcon
|
||||
color={Colors.purple}
|
||||
name="info-with-circle"
|
||||
style={styles.icon}
|
||||
/>
|
||||
<AppText style={styles.title}>{labels.preOvu.title}</AppText>
|
||||
</View>
|
||||
<AppText>{labels.preOvu.note}</AppText>
|
||||
</Segment>
|
||||
</AppPage>
|
||||
)
|
||||
}
|
||||
|
||||
export default Settings
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
icon: {
|
||||
marginRight: Spacing.base,
|
||||
},
|
||||
line: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
title: {
|
||||
...Typography.subtitle,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useState } from 'react'
|
||||
import { StyleSheet, View } from 'react-native'
|
||||
import PropTypes from 'prop-types'
|
||||
import Slider from '@ptomasroos/react-native-multi-slider'
|
||||
|
||||
import alertError from '../common/alert-error'
|
||||
@@ -10,7 +11,7 @@ import { Colors, Sizes } from '../../../styles'
|
||||
import labels from '../../../i18n/en/settings'
|
||||
import { TEMP_MIN, TEMP_MAX, TEMP_SLIDER_STEP } from '../../../config'
|
||||
|
||||
const TemperatureSlider = () => {
|
||||
const TemperatureSlider = ({ disabled }) => {
|
||||
const savedValue = scaleObservable.value
|
||||
const [minTemperature, setMinTemperature] = useState(savedValue.min)
|
||||
const [maxTemperature, setMaxTemperature] = useState(savedValue.max)
|
||||
@@ -25,6 +26,14 @@ const TemperatureSlider = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const sliderAccentBackground = disabled
|
||||
? styles.disabledSliderAccentBackground
|
||||
: styles.sliderAccentBackground
|
||||
|
||||
const sliderBackground = disabled
|
||||
? styles.disabledSliderBackground
|
||||
: styles.sliderBackground
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Slider
|
||||
@@ -35,11 +44,13 @@ const TemperatureSlider = () => {
|
||||
max={TEMP_MAX}
|
||||
min={TEMP_MIN}
|
||||
onValuesChange={onTemperatureSliderChange}
|
||||
selectedStyle={styles.sliderAccentBackground}
|
||||
step={TEMP_SLIDER_STEP}
|
||||
trackStyle={styles.slider}
|
||||
unselectedStyle={styles.sliderBackground}
|
||||
values={[minTemperature, maxTemperature]}
|
||||
enabledOne={!disabled}
|
||||
enabledTwo={!disabled}
|
||||
selectedStyle={sliderAccentBackground}
|
||||
unselectedStyle={sliderBackground}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
@@ -47,6 +58,10 @@ const TemperatureSlider = () => {
|
||||
|
||||
export default TemperatureSlider
|
||||
|
||||
TemperatureSlider.propTypes = {
|
||||
disabled: PropTypes.bool,
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
alignItems: 'center',
|
||||
@@ -54,6 +69,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
marker: {
|
||||
backgroundColor: Colors.turquoiseDark,
|
||||
|
||||
borderRadius: 50,
|
||||
elevation: 4,
|
||||
height: Sizes.subtitle,
|
||||
@@ -66,7 +82,13 @@ const styles = StyleSheet.create({
|
||||
sliderAccentBackground: {
|
||||
backgroundColor: Colors.turquoiseDark,
|
||||
},
|
||||
disabledSliderAccentBackground: {
|
||||
backgroundColor: Colors.grey,
|
||||
},
|
||||
sliderBackground: {
|
||||
backgroundColor: Colors.turquoise,
|
||||
},
|
||||
disabledSliderBackground: {
|
||||
backgroundColor: Colors.greyLight,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -11,7 +11,6 @@ const menuItems = [
|
||||
{ label: 'reminders', componentName: 'Reminders' },
|
||||
{ label: 'dataManagement', componentName: 'DataManagement' },
|
||||
{ label: 'password', componentName: 'Password' },
|
||||
{ label: 'info', componentName: 'Info' },
|
||||
]
|
||||
|
||||
const SettingsMenu = ({ navigate }) => {
|
||||
|
||||
Reference in New Issue
Block a user