Merge branch '671-feature-customisation-not-display-menstrual-bleeding-prediction' into 'main'

Feature: Customizable period prediction

Closes #671

See merge request bloodyhealth/drip!647
This commit is contained in:
bl00dymarie
2024-01-22 14:40:31 +00:00
9 changed files with 85 additions and 21 deletions
-1
View File
@@ -9,7 +9,6 @@ cache:
test_async: test_async:
script: script:
- npm install npm@7.0.1 -g - npm install npm@7.0.1 -g
- npm install
- npm test - npm test
variables: variables:
+7 -3
View File
@@ -14,6 +14,7 @@ import {
determinePredictionText, determinePredictionText,
formatWithOrdinalSuffix, formatWithOrdinalSuffix,
} from './helpers/home' } from './helpers/home'
import { periodPredictionObservable } from '../local-storage'
import { Colors, Fonts, Sizes, Spacing } from '../styles' import { Colors, Fonts, Sizes, Spacing } from '../styles'
import { LocalDate } from '@js-joda/core' import { LocalDate } from '@js-joda/core'
@@ -32,6 +33,7 @@ const Home = ({ navigate, setDate }) => {
const cycleDayNumber = getCycleDayNumber(todayDateString) const cycleDayNumber = getCycleDayNumber(todayDateString)
const { status, phase, statusText } = const { status, phase, statusText } =
getFertilityStatusForDay(todayDateString) getFertilityStatusForDay(todayDateString)
const isPeriodPredictionEnabled = periodPredictionObservable.value
const prediction = determinePredictionText(getPredictedMenses(), t) const prediction = determinePredictionText(getPredictedMenses(), t)
const cycleDayText = cycleDayNumber const cycleDayText = cycleDayNumber
@@ -65,9 +67,11 @@ const Home = ({ navigate, setDate }) => {
<Asterisk /> <Asterisk />
</View> </View>
)} )}
<View style={styles.line}> {isPeriodPredictionEnabled && (
<AppText style={styles.turquoiseText}>{prediction}</AppText> <View style={styles.line}>
</View> <AppText style={styles.turquoiseText}>{prediction}</AppText>
</View>
)}
<Button isCTA isSmall={false} onPress={navigateToCycleDayView}> <Button isCTA isSmall={false} onPress={navigateToCycleDayView}>
{t('labels.home.addDataForToday')} {t('labels.home.addDataForToday')}
</Button> </Button>
+3 -1
View File
@@ -6,7 +6,7 @@ import AppText from './app-text'
import { Containers } from '../../styles' import { Containers } from '../../styles'
const AppSwitch = ({ onToggle, text, value, trackColor }) => { const AppSwitch = ({ onToggle, text, value, trackColor, disabled }) => {
return ( return (
<View style={styles.container}> <View style={styles.container}>
<View style={styles.textContainer}> <View style={styles.textContainer}>
@@ -17,6 +17,7 @@ const AppSwitch = ({ onToggle, text, value, trackColor }) => {
style={styles.switch} style={styles.switch}
value={value} value={value}
trackColor={trackColor} trackColor={trackColor}
disabled={disabled}
/> />
</View> </View>
) )
@@ -27,6 +28,7 @@ AppSwitch.propTypes = {
text: PropTypes.string, text: PropTypes.string,
value: PropTypes.bool, value: PropTypes.bool,
trackColor: PropTypes.string, trackColor: PropTypes.string,
disabled: PropTypes.bool,
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
+2
View File
@@ -2,6 +2,7 @@ import { LocalDate } from '@js-joda/core'
import { verticalScale } from 'react-native-size-matters' import { verticalScale } from 'react-native-size-matters'
import { Colors, Fonts, Sizes } from '../../styles' import { Colors, Fonts, Sizes } from '../../styles'
import { periodPredictionObservable } from '../../local-storage'
const { shades } = Colors.iconColors.bleeding const { shades } = Colors.iconColors.bleeding
@@ -26,6 +27,7 @@ export const toCalFormat = (bleedingDaysSortedByDate) => {
} }
export const predictionToCalFormat = (predictedDays) => { export const predictionToCalFormat = (predictedDays) => {
if (!periodPredictionObservable.value) return {}
if (!predictedDays.length) return {} if (!predictedDays.length) return {}
const todayDateString = LocalDate.now().toString() const todayDateString = LocalDate.now().toString()
const middleIndex = (predictedDays[0].length - 1) / 2 const middleIndex = (predictedDays[0].length - 1) / 2
+24 -8
View File
@@ -6,7 +6,12 @@ import AppText from '../../common/app-text'
import TemperatureSlider from './temperature-slider' import TemperatureSlider from './temperature-slider'
import Segment from '../../common/segment' import Segment from '../../common/segment'
import { useCervixObservable, saveUseCervix } from '../../../local-storage' import {
periodPredictionObservable,
savePeriodPrediction,
useCervixObservable,
saveUseCervix,
} from '../../../local-storage'
import { Colors } from '../../../styles' import { Colors } from '../../../styles'
import labels from '../../../i18n/en/settings' import labels from '../../../i18n/en/settings'
@@ -15,9 +20,22 @@ const Settings = () => {
useCervixObservable.value useCervixObservable.value
) )
const [isEnabled, setIsEnabled] = useState(true) const [isPeriodPredictionEnabled, setPeriodPrediction] = useState(
periodPredictionObservable.value
)
const [isEnabled, setIsEnabled] = useState(false)
const toggleSwitch = () => setIsEnabled((previousState) => !previousState) const toggleSwitch = () => setIsEnabled((previousState) => !previousState)
const onPeriodPredictionToggle = (value) => {
setPeriodPrediction(value)
savePeriodPrediction(value)
}
const periodPredictionText = isPeriodPredictionEnabled
? labels.periodPrediction.on
: labels.periodPrediction.off
const onCervixToggle = (value) => { const onCervixToggle = (value) => {
setShouldUseCervix(value) setShouldUseCervix(value)
saveUseCervix(value) saveUseCervix(value)
@@ -103,13 +121,11 @@ const Settings = () => {
/> />
</Segment> </Segment>
<Segment title={'Period prediction'}> <Segment title={labels.periodPrediction.title} last>
<AppSwitch <AppSwitch
onToggle={toggleSwitch} onToggle={onPeriodPredictionToggle}
text={ text={periodPredictionText}
'If turned on drip will predict your next menstrual bleeding, after you have tracked 3 complete menstrual cycles.' value={isPeriodPredictionEnabled}
}
value={isEnabled}
trackColor={{ true: Colors.turquoiseDark }} trackColor={{ true: Colors.turquoiseDark }}
/> />
</Segment> </Segment>
+23 -7
View File
@@ -8,11 +8,15 @@ import TemperatureReminder from './temperature-reminder'
import { import {
periodReminderObservable, periodReminderObservable,
savePeriodReminder, savePeriodReminder,
periodPredictionObservable,
} from '../../../local-storage' } from '../../../local-storage'
import labels from '../../../i18n/en/settings' import labels from '../../../i18n/en/settings'
import { Alert, Pressable } from 'react-native'
const Reminders = () => { const Reminders = () => {
const isPeriodPredictionDisabled = !periodPredictionObservable.value
const [isPeriodReminderEnabled, setIsPeriodReminderEnabled] = useState( const [isPeriodReminderEnabled, setIsPeriodReminderEnabled] = useState(
periodReminderObservable.value.enabled periodReminderObservable.value.enabled
) )
@@ -21,15 +25,27 @@ const Reminders = () => {
savePeriodReminder({ enabled: isEnabled }) savePeriodReminder({ enabled: isEnabled })
} }
const reminderDisabledPrompt = () => {
if (!periodPredictionObservable.value) {
Alert.alert(
labels.periodReminder.alertNoPeriodReminder.title,
labels.periodReminder.alertNoPeriodReminder.message
)
}
}
return ( return (
<AppPage> <AppPage>
<Segment title={labels.periodReminder.title}> <Pressable onPress={reminderDisabledPrompt}>
<AppSwitch <Segment title={labels.periodReminder.title}>
onToggle={periodReminderToggle} <AppSwitch
text={labels.periodReminder.reminderText} onToggle={periodReminderToggle}
value={isPeriodReminderEnabled} text={labels.periodReminder.reminderText}
/> value={isPeriodReminderEnabled}
</Segment> disabled={isPeriodPredictionDisabled}
/>
</Segment>
</Pressable>
<Segment title={labels.tempReminder.title} last> <Segment title={labels.tempReminder.title} last>
<TemperatureReminder /> <TemperatureReminder />
</Segment> </Segment>
+10
View File
@@ -50,6 +50,11 @@ export default {
'Get a notification 3 days before your next period is likely to start.', 'Get a notification 3 days before your next period is likely to start.',
notification: (daysToEndOfPrediction) => notification: (daysToEndOfPrediction) =>
`Your next period is likely to start in 3 to ${daysToEndOfPrediction} days.`, `Your next period is likely to start in 3 to ${daysToEndOfPrediction} days.`,
alertNoPeriodReminder: {
title: 'Period predictions turned off',
message:
'To use the period reminder please first enable period predictions in the customization settings.',
},
}, },
useCervix: { useCervix: {
title: 'Secondary symptom', title: 'Secondary symptom',
@@ -58,6 +63,11 @@ export default {
cervixModeOff: cervixModeOff:
'By default, cervical mucus values are being used for symptothermal fertility detection. You can switch here to use cervix values for symptothermal fertility detection', 'By default, cervical mucus values are being used for symptothermal fertility detection. You can switch here to use cervix values for symptothermal fertility detection',
}, },
periodPrediction: {
title: 'Period predictions',
on: 'drip predicts your 3 next menstrual bleedings based on the statistics of your previously tracked cycles, min 3 complete cycles.',
off: 'There are no predictions for menstrual cycles displayed. If turned on the calendar and the home screen will display period predictions.',
},
passwordSettings: { passwordSettings: {
title: 'App password', title: 'App password',
explainerDisabled: explainerDisabled:
+1 -1
View File
@@ -27,7 +27,7 @@ After tracking at least 3 menstrual cycles, drip. will give you an overview of
· whether the length of your cycles varied significantly (in "stats" and in bleeding predictions) · whether the length of your cycles varied significantly (in "stats" and in bleeding predictions)
· and predict your next 3 cycles with a range of 3 or 5 days (on home screen and "calendar"). · and predict your next 3 cycles with a range of 3 or 5 days (on home screen and "calendar").
The app allows you to track different intensities of bleeding. On the chart and on the calendar, bleeding values are colored in different shades of red. The darker, the more intense your bleeding. Every bleeding value that is not excluded is taken into account for fertility calculation and prediction for the start of next cycles. The app allows you to track different intensities of bleeding. On the chart and on the calendar, bleeding values are colored in different shades of red. The darker, the more intense your bleeding. Every bleeding value that is not excluded is taken into account for fertility calculation and period predictions.
Excluding bleeding values is for tracking bleeding when it's not marking the start of a new cycle or the continuation of menstrual bleeding the day(s) before, e.g. bleeding caused by ovulation or a miscarriage. Excluding bleeding values is for tracking bleeding when it's not marking the start of a new cycle or the continuation of menstrual bleeding the day(s) before, e.g. bleeding caused by ovulation or a miscarriage.
+15
View File
@@ -44,6 +44,21 @@ export async function savePeriodReminder(reminder) {
periodReminderObservable.set(reminder) periodReminderObservable.set(reminder)
} }
export const periodPredictionObservable = Observable()
setObvWithInitValue('periodPrediction', periodPredictionObservable, true)
export async function savePeriodPrediction(bool) {
await AsyncStorage.setItem('periodPrediction', JSON.stringify(bool))
periodPredictionObservable.set(bool)
if (!periodPredictionObservable.value) {
const result = await AsyncStorage.getItem('periodReminder')
if (JSON.parse(result).enabled) {
periodReminderObservable.set(false)
}
}
}
export const useCervixObservable = Observable() export const useCervixObservable = Observable()
setObvWithInitValue('useCervix', useCervixObservable, false) setObvWithInitValue('useCervix', useCervixObservable, false)