Compare commits

..

1 Commits

Author SHA1 Message Date
lynn-janzen007 26ec47bad2 add activity indicator to period details modal and adjust styling to make it visible 2024-10-03 11:27:37 +02:00
21 changed files with 129 additions and 326 deletions
+1 -1
View File
@@ -30,7 +30,7 @@ ios/Index/DataStore
build/ build/
.idea .idea
.gradle .gradle
*.properties local.properties
*.iml *.iml
*.hprof *.hprof
+4 -38
View File
@@ -34,31 +34,13 @@ or clone it with HTTPS
git clone https://gitlab.com/bloodyhealth/drip.git git clone https://gitlab.com/bloodyhealth/drip.git
### 2. Node version ### 2. Node & yarn version
Make sure you are running Node 14 and classic yarn (v.1). It's easiest to switch Node versions using `nvm`, here's how to install NVM: https://github.com/nvm-sh/nvm#installing-and-updating. After installing nvm close the terminal and open it again to be able to use nvm. Make sure you are running Node 14 and classic yarn (v.1). It's easiest to switch Node versions using `nvm`, here's how to install NVM: https://github.com/nvm-sh/nvm#installing-and-updating. After installing nvm close the terminal and open it again to be able to use nvm.
Once you have nvm running you can install node 14: Once you have nvm running you can install node 14:
nvm install v14.19.3 nvm install v14.19.3
#### On Apple Silicon M1
NodeJS 14 does not compile on the M1 architecture, so it has to be installed through Rosetta: https://devzilla.io/using-nodejs-14-with-mac-silicon-m1 .
To activate Rosetta and switch to intel emulation run:
arch -x86_64 zsh
Run
arch
again to verify that it returns "i386".
Now install node 14:
nvm install v14.19.3
### 3. Yarn version
use npm to install yarn: use npm to install yarn:
npm install --global yarn npm install --global yarn
@@ -131,36 +113,20 @@ Minimum system requirements to run iOS app are as follows:
- MacOS 10.15.7 for Mac users - MacOS 10.15.7 for Mac users
- Xcode 13 (command line tools only might be enough) - Xcode 13 (command line tools only might be enough)
i. Install yarn dependencies i. Install XCode dependencies by running the following command from the root project directory:
yarn install ..
ii. Install XCode dependencies by running the following command from the root project directory:
cd ios && pod install && cd .. cd ios && pod install && cd ..
iii. To run app either open drip workspace ('drip.xcworkspace' file) with XCode and run "Build" or run the following command: ii. To run app either open drip workspace ('drip.xcworkspace' file) with XCode and run "Build" or run the following command:
yarn ios yarn ios
iiii. If you are building the app with XCode make sure you are running this as well: iii. If you are building the app with XCode make sure you are running this as well:
yarn start yarn start
### Troubleshooting ### Troubleshooting
#### [MacOS M1] Flipper problems
If a bug in the currently used Flipper version prevents building the project, comment out the respective line in the podfile, like so:
#use_flipper!()
Run
pod install
from the ios directory again to reload the dependencies.
#### [MacOS] Java problems #### [MacOS] Java problems
Make sure that you have Java 1.8 by running `java -version`. Make sure that you have Java 1.8 by running `java -version`.
+2 -18
View File
@@ -1,8 +1,6 @@
apply plugin: "com.android.application" apply plugin: "com.android.application"
import com.android.build.OutputFile import com.android.build.OutputFile
import java.util.Properties
import java.io.FileInputStream
/** /**
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
@@ -127,16 +125,6 @@ def enableHermes = project.ext.react.get("enableHermes", false);
*/ */
def nativeArchitectures = project.getProperties().get("reactNativeDebugArchitectures") def nativeArchitectures = project.getProperties().get("reactNativeDebugArchitectures")
// Create a variable called keystorePropertiesFile, and initialize it to your
// keystore.properties file, in the rootProject folder.
def keystorePropertiesFile = rootProject.file("keystore.properties")
// Initialize a new Properties() object called keystoreProperties.
def keystoreProperties = new Properties()
// Load your keystore.properties file into the keystoreProperties object.
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
android { android {
ndkVersion rootProject.ext.ndkVersion ndkVersion rootProject.ext.ndkVersion
compileSdkVersion rootProject.ext.compileSdkVersion compileSdkVersion rootProject.ext.compileSdkVersion
@@ -146,8 +134,8 @@ android {
applicationId "com.drip" applicationId "com.drip"
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 38 versionCode 33
versionName "1.2410.22" versionName "1.2403.19"
ndk { ndk {
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64" abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
} }
@@ -162,10 +150,6 @@ android {
keyPassword 'android' keyPassword 'android'
} }
release { release {
storeFile file('drip-release-key.keystore')
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storePassword keystoreProperties['storePassword']
if (project.hasProperty('DRIP_RELEASE_STORE_FILE')) { if (project.hasProperty('DRIP_RELEASE_STORE_FILE')) {
storeFile file(DRIP_RELEASE_STORE_FILE) storeFile file(DRIP_RELEASE_STORE_FILE)
storePassword DRIP_RELEASE_STORE_PASSWORD storePassword DRIP_RELEASE_STORE_PASSWORD
-1
View File
@@ -53,7 +53,6 @@ ext {
minSdkVersion = 21 minSdkVersion = 21
compileSdkVersion = 33 compileSdkVersion = 33
targetSdkVersion = 33 targetSdkVersion = 33
soLoaderVersion = "0.10.4+"
if (System.properties['os.arch'] == "aarch64") { if (System.properties['os.arch'] == "aarch64") {
// For M1 Users we need to use the NDK 24 which added support for aarch64 // For M1 Users we need to use the NDK 24 which added support for aarch64
+1 -1
View File
@@ -18,7 +18,7 @@ LoadingMoreView.propTypes = {
const styles = StyleSheet.create({ const styles = StyleSheet.create({
loadingContainer: { loadingContainer: {
height: '100%', height: 80,
backgroundColor: Colors.turquoiseLight, backgroundColor: Colors.turquoiseLight,
justifyContent: 'center', justifyContent: 'center',
}, },
-4
View File
@@ -18,10 +18,6 @@ const AboutSection = () => {
<AppPage title={t('title')}> <AppPage title={t('title')}>
<Segment> <Segment>
<AppText>{t('intro.text')}</AppText> <AppText>{t('intro.text')}</AppText>
<Button isCTA isSmall onPress={() => Linking.openURL(links.faq.url)}>
{t('intro.faq')}
</Button>
<AppText>{t('intro.contact')}</AppText>
<ButtonRow> <ButtonRow>
{[links.email, links.gitlab, links.website].map((link) => ( {[links.email, links.gitlab, links.website].map((link) => (
<Button <Button
@@ -1,56 +0,0 @@
import React from 'react'
import { View } from 'react-native'
import PropTypes from 'prop-types'
import Slider from '@ptomasroos/react-native-multi-slider'
import SliderLabel from './slider-label'
import { styles } from './slider-styles'
import {
ADVANCE_PERIOD_NOTICE_DAYS_MIN,
ADVANCE_PERIOD_NOTICE_DAYS_MAX,
} from '../../../config'
const AdvanceNoticeDaysSlider = ({
disabled,
advanceNoticeDays,
onAdvanceNoticeDaysChange,
}) => {
const sliderAccentBackground = disabled
? styles.disabledSliderAccentBackground
: styles.sliderAccentBackground
const sliderBackground = disabled
? styles.disabledSliderBackground
: styles.sliderBackground
return (
<View style={styles.container}>
<Slider
customLabel={SliderLabel}
enableLabel={true}
markerStyle={styles.marker}
markerOffsetY={styles.markerOffsetY}
max={ADVANCE_PERIOD_NOTICE_DAYS_MAX}
min={ADVANCE_PERIOD_NOTICE_DAYS_MIN}
onValuesChange={onAdvanceNoticeDaysChange}
step={1}
showSteps={true}
snapped={true}
trackStyle={styles.slider}
values={[advanceNoticeDays]}
enabledOne={!disabled}
enabledTwo={false}
selectedStyle={sliderAccentBackground}
unselectedStyle={sliderBackground}
/>
</View>
)
}
export default AdvanceNoticeDaysSlider
AdvanceNoticeDaysSlider.propTypes = {
disabled: PropTypes.bool,
advanceNoticeDays: PropTypes.number,
onAdvanceNoticeDaysChange: PropTypes.func,
}
@@ -1,34 +0,0 @@
import { StyleSheet } from 'react-native'
import { Colors, Sizes } from '../../../styles'
export const styles = StyleSheet.create({
container: {
alignItems: 'center',
paddingTop: Sizes.base,
},
marker: {
backgroundColor: Colors.turquoiseDark,
borderRadius: 50,
elevation: 4,
height: Sizes.subtitle,
width: Sizes.subtitle,
},
slider: {
borderRadius: 25,
height: Sizes.small,
paddingTop: Sizes.base,
},
sliderAccentBackground: {
backgroundColor: Colors.turquoiseDark,
},
disabledSliderAccentBackground: {
backgroundColor: Colors.grey,
},
sliderBackground: {
backgroundColor: Colors.turquoise,
},
disabledSliderBackground: {
backgroundColor: Colors.greyLight,
},
markerOffsetY: Sizes.tiny,
})
@@ -1,12 +1,13 @@
import React, { useState } from 'react' import React, { useState } from 'react'
import { View } from 'react-native' import { StyleSheet, View } from 'react-native'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import Slider from '@ptomasroos/react-native-multi-slider' import Slider from '@ptomasroos/react-native-multi-slider'
import SliderLabel from './slider-label'
import { styles } from './slider-styles'
import alertError from '../common/alert-error' import alertError from '../common/alert-error'
import SliderLabel from './slider-label'
import { scaleObservable, saveTempScale } from '../../../local-storage' import { scaleObservable, saveTempScale } from '../../../local-storage'
import { Colors, Sizes } from '../../../styles'
import labels from '../../../i18n/en/settings' import labels from '../../../i18n/en/settings'
import { TEMP_MIN, TEMP_MAX, TEMP_SLIDER_STEP } from '../../../config' import { TEMP_MIN, TEMP_MAX, TEMP_SLIDER_STEP } from '../../../config'
@@ -39,7 +40,7 @@ const TemperatureSlider = ({ disabled }) => {
customLabel={SliderLabel} customLabel={SliderLabel}
enableLabel={true} enableLabel={true}
markerStyle={styles.marker} markerStyle={styles.marker}
markerOffsetY={styles.markerOffsetY} markerOffsetY={Sizes.tiny}
max={TEMP_MAX} max={TEMP_MAX}
min={TEMP_MIN} min={TEMP_MIN}
onValuesChange={onTemperatureSliderChange} onValuesChange={onTemperatureSliderChange}
@@ -60,3 +61,34 @@ export default TemperatureSlider
TemperatureSlider.propTypes = { TemperatureSlider.propTypes = {
disabled: PropTypes.bool, disabled: PropTypes.bool,
} }
const styles = StyleSheet.create({
container: {
alignItems: 'center',
paddingTop: Sizes.base,
},
marker: {
backgroundColor: Colors.turquoiseDark,
borderRadius: 50,
elevation: 4,
height: Sizes.subtitle,
width: Sizes.subtitle,
},
slider: {
borderRadius: 25,
height: Sizes.small,
},
sliderAccentBackground: {
backgroundColor: Colors.turquoiseDark,
},
disabledSliderAccentBackground: {
backgroundColor: Colors.grey,
},
sliderBackground: {
backgroundColor: Colors.turquoise,
},
disabledSliderBackground: {
backgroundColor: Colors.greyLight,
},
})
@@ -1,59 +0,0 @@
import React, { useState } from 'react'
import AppSwitch from '../../common/app-switch'
import AdvanceNoticeDaysSlider from '../customization/advance-notice-days-slider'
import {
periodReminderObservable,
savePeriodReminder,
periodPredictionObservable,
saveAdvanceNoticeDays,
advanceNoticeDaysObservable,
} from '../../../local-storage'
import labels from '../../../i18n/en/settings'
const PeriodReminder = () => {
const isPeriodPredictionDisabled = !periodPredictionObservable.value
const [isPeriodReminderEnabled, setIsPeriodReminderEnabled] = useState(
periodReminderObservable.value.enabled
)
const [advanceNoticeDays, setAdvanceNoticeDays] = useState(
advanceNoticeDaysObservable.value
)
const periodReminderToggle = (isEnabled) => {
setIsPeriodReminderEnabled(isEnabled)
savePeriodReminder({ enabled: isEnabled })
}
const handleAdvanceNoticeDaysChange = (days) => {
setAdvanceNoticeDays(days)
saveAdvanceNoticeDays(days)
}
const reminderText =
advanceNoticeDays == 1
? labels.periodReminder.reminderTextSingular
: labels.periodReminder.reminderTextPlural(advanceNoticeDays)
return (
<>
<AppSwitch
onToggle={periodReminderToggle}
text={reminderText}
value={isPeriodReminderEnabled}
disabled={isPeriodPredictionDisabled}
/>
{isPeriodReminderEnabled && (
<AdvanceNoticeDaysSlider
disabled={isPeriodPredictionDisabled}
advanceNoticeDays={parseInt(advanceNoticeDays)}
onAdvanceNoticeDaysChange={handleAdvanceNoticeDaysChange}
/>
)}
</>
)
}
export default PeriodReminder
+22 -6
View File
@@ -1,11 +1,13 @@
import React from 'react' import React, { useState } from 'react'
import AppPage from '../../common/app-page' import AppPage from '../../common/app-page'
import AppSwitch from '../../common/app-switch'
import Segment from '../../common/segment' import Segment from '../../common/segment'
import TemperatureReminder from './temperature-reminder' import TemperatureReminder from './temperature-reminder'
import PeriodReminder from './period-reminder'
import { import {
periodReminderObservable,
savePeriodReminder,
periodPredictionObservable, periodPredictionObservable,
temperatureTrackingCategoryObservable, temperatureTrackingCategoryObservable,
} from '../../../local-storage' } from '../../../local-storage'
@@ -14,7 +16,17 @@ import labels from '../../../i18n/en/settings'
import { Alert, Pressable } from 'react-native' import { Alert, Pressable } from 'react-native'
const Reminders = () => { const Reminders = () => {
const periodReminderDisabledPrompt = () => { const isPeriodPredictionDisabled = !periodPredictionObservable.value
const [isPeriodReminderEnabled, setIsPeriodReminderEnabled] = useState(
periodReminderObservable.value.enabled
)
const periodReminderToggle = (isEnabled) => {
setIsPeriodReminderEnabled(isEnabled)
savePeriodReminder({ enabled: isEnabled })
}
const reminderDisabledPrompt = () => {
if (!periodPredictionObservable.value) { if (!periodPredictionObservable.value) {
Alert.alert( Alert.alert(
labels.periodReminder.alertNoPeriodReminder.title, labels.periodReminder.alertNoPeriodReminder.title,
@@ -31,12 +43,16 @@ const Reminders = () => {
) )
} }
} }
return ( return (
<AppPage> <AppPage>
<Pressable onPress={periodReminderDisabledPrompt}> <Pressable onPress={reminderDisabledPrompt}>
<Segment title={labels.periodReminder.title}> <Segment title={labels.periodReminder.title}>
<PeriodReminder /> <AppSwitch
onToggle={periodReminderToggle}
text={labels.periodReminder.reminderText}
value={isPeriodReminderEnabled}
disabled={isPeriodPredictionDisabled}
/>
</Segment> </Segment>
</Pressable> </Pressable>
<Pressable onPress={tempReminderDisabledPrompt}> <Pressable onPress={tempReminderDisabledPrompt}>
+17 -1
View File
@@ -1,4 +1,4 @@
import React from 'react' import React, { useState, useEffect } from 'react'
import { FlatList, StyleSheet, View } from 'react-native' import { FlatList, StyleSheet, View } from 'react-native'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@@ -9,6 +9,7 @@ import AppText from '../common/app-text'
import cycleModule from '../../lib/cycle' import cycleModule from '../../lib/cycle'
import { Spacing, Typography, Colors } from '../../styles' import { Spacing, Typography, Colors } from '../../styles'
import { humanizeDate } from '../helpers/format-date' import { humanizeDate } from '../helpers/format-date'
import LoadingMoreView from '../chart/loading-more'
const Item = ({ data }) => { const Item = ({ data }) => {
const { t } = useTranslation(null, { keyPrefix: 'plurals' }) const { t } = useTranslation(null, { keyPrefix: 'plurals' })
@@ -39,21 +40,36 @@ Item.propTypes = {
const PeriodDetailsModal = ({ onClose }) => { const PeriodDetailsModal = ({ onClose }) => {
const renderItem = ({ item }) => <Item data={item} /> const renderItem = ({ item }) => <Item data={item} />
const data = cycleModule().getStats() const data = cycleModule().getStats()
const [endReached, setEndReached] = useState(false)
if (!data || data.length === 0) return false if (!data || data.length === 0) return false
// const ITEM_HEIGHT = 50;
// const getItemLayout = (data, index) => ({
// length: ITEM_HEIGHT,
// offset: ITEM_HEIGHT * index,
// index
// });
return ( return (
<AppModal onClose={onClose}> <AppModal onClose={onClose}>
<View> <View>
<FlatList <FlatList
data={data} data={data}
renderItem={renderItem} renderItem={renderItem}
// getItemLayout={getItemLayout}
keyExtractor={(item) => item.date} keyExtractor={(item) => item.date}
ItemSeparatorComponent={ItemDivider} ItemSeparatorComponent={ItemDivider}
ListHeaderComponent={FlatListHeader} ListHeaderComponent={FlatListHeader}
ListHeaderComponentStyle={styles.headerDivider} ListHeaderComponentStyle={styles.headerDivider}
stickyHeaderIndices={[0]} stickyHeaderIndices={[0]}
windowSize={4}
contentContainerStyle={styles.container} contentContainerStyle={styles.container}
onEndReached={() => setEndReached(true)}
onEndReachedThreshold={0.1}
ListFooterComponent={<LoadingMoreView end={endReached} />}
updateCellsBatchingPeriod={100}
/> />
</View> </View>
</AppModal> </AppModal>
-4
View File
@@ -33,10 +33,6 @@ export const TEMP_MAX = 39
export const TEMP_MIN = 35 export const TEMP_MIN = 35
export const TEMP_SLIDER_STEP = 0.5 export const TEMP_SLIDER_STEP = 0.5
export const ADVANCE_PERIOD_NOTICE_DAYS_MIN = 1
export const ADVANCE_PERIOD_NOTICE_DAYS_MAX = 7
export const ADVANCE_PERIOD_NOTICE_DAYS_INIT_VALUE = 3
export const HIT_SLOP = { export const HIT_SLOP = {
top: verticalScale(20), top: verticalScale(20),
bottom: verticalScale(20), bottom: verticalScale(20),
+1 -3
View File
@@ -45,9 +45,7 @@
"text": "The drips are developing this app on a volunteer basis. We are always grateful for support. This could mean condriputing to the code, giving feedback, suggesting improvements or features, testing or donating. It helps and motivates us maintaining this app and developing new features. Thank you for your support!" "text": "The drips are developing this app on a volunteer basis. We are always grateful for support. This could mean condriputing to the code, giving feedback, suggesting improvements or features, testing or donating. It helps and motivates us maintaining this app and developing new features. Thank you for your support!"
}, },
"intro": { "intro": {
"text": "Please note that your data is stored locally on your phone and not on a server. This means your data cannot be read by anyone else unless they have access to your phone. We want to ensure that you stay in control of your own data. If you are planning to switch or reset your phone, please remember to export your data before doing so. You can reinstall the app afterwards and import your data.\n\nIf you encounter any issues, please take a look at our Frequently Asked Questions page.", "text": "Please note that your data is stored locally on your phone and not on a server. This means your data cannot be read by anyone else unless they have access to your phone. We want to ensure that you stay in control of your own data. If you are planning to switch or reset your phone, please remember to export your data before doing so. You can reinstall the app afterwards and import your data.\n\nIf you encounter any technical issues, don't hesitate to contact us via email. You can also contribute to the code base on Gitlab and visit our website."
"faq": "FAQ",
"contact": "\nIf your issue is not listed, don't hesitate to contact us via email. You can also contribute to the code base on Gitlab and visit our website."
}, },
"philosophy": { "philosophy": {
"title": "Remember to think for yourself", "title": "Remember to think for yourself",
-4
View File
@@ -39,8 +39,4 @@ export default {
url: 'https://www.flaticon.com', url: 'https://www.flaticon.com',
text: 'Flaticon', text: 'Flaticon',
}, },
faq: {
url: 'https://dripapp.org/faq',
text: 'FAQ',
},
} }
+4 -6
View File
@@ -60,12 +60,10 @@ export default {
}, },
periodReminder: { periodReminder: {
title: 'Next period reminder', title: 'Next period reminder',
reminderTextSingular: reminderText:
'Get a notification 1 day before your next period is likely to start.', 'Get a notification 3 days before your next period is likely to start.',
reminderTextPlural: (days) => notification: (daysToEndOfPrediction) =>
`Get a notification ${days} days before your next period is likely to start.`, `Your next period is likely to start in 3 to ${daysToEndOfPrediction} days.`,
notification: (advanceNoticeDays, daysToEndOfPrediction) =>
`Your next period is likely to start in ${advanceNoticeDays} to ${daysToEndOfPrediction} days.`,
alertNoPeriodReminder: { alertNoPeriodReminder: {
title: 'Period predictions turned off', title: 'Period predictions turned off',
message: message:
+1 -1
View File
@@ -19,7 +19,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.2410.22</string> <string>1.2403.19</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
+25 -55
View File
@@ -2,7 +2,6 @@ import { Platform } from 'react-native'
import { import {
tempReminderObservable, tempReminderObservable,
periodReminderObservable, periodReminderObservable,
advanceNoticeDaysObservable,
} from '../local-storage' } from '../local-storage'
import * as PN from 'react-native-push-notification' import * as PN from 'react-native-push-notification'
import { requestNotifications } from 'react-native-permissions' import { requestNotifications } from 'react-native-permissions'
@@ -14,19 +13,12 @@ import { getBleedingDaysSortedByDate } from '../db'
import cycleModule from './cycle' import cycleModule from './cycle'
import nothingChanged from '../db/db-unchanged' import nothingChanged from '../db/db-unchanged'
const DRIP_CHANNEL_ID = 'drip-channel-id'
const TEMPERATURE_REMINDER_ID = '1'
const PERIOD_REMINDER_ID = '2'
const PushNotification = Platform.OS === 'ios' ? PN : PN.default
export default function setupNotifications(navigate, setDate) { export default function setupNotifications(navigate, setDate) {
// for Android, this method call is necessary Platform.OS === 'android' ? requestNotifications() : null
if (Platform.OS === 'android') { const PushNotification = Platform.OS === 'ios' ? PN : PN.default
requestNotifications()
}
PushNotification.createChannel({ PushNotification.createChannel({
channelId: DRIP_CHANNEL_ID, // (required) channelId: 'drip-channel-id', // (required)
channelName: 'drip reminder', // (required) channelName: 'drip reminder', // (required)
playSound: false, // (optional) default: true playSound: false, // (optional) default: true
}) })
@@ -34,10 +26,7 @@ export default function setupNotifications(navigate, setDate) {
PushNotification.configure({ PushNotification.configure({
onNotification: (notification) => { onNotification: (notification) => {
// https://github.com/zo0r/react-native-push-notification/issues/966#issuecomment-479069106 // https://github.com/zo0r/react-native-push-notification/issues/966#issuecomment-479069106
if ( if (notification.data?.id === '1' || notification.id === '1') {
notification.data?.id === TEMPERATURE_REMINDER_ID ||
notification.id === TEMPERATURE_REMINDER_ID
) {
const todayDate = LocalDate.now().toString() const todayDate = LocalDate.now().toString()
setDate(todayDate) setDate(todayDate)
navigate('TemperatureEditView') navigate('TemperatureEditView')
@@ -48,7 +37,7 @@ export default function setupNotifications(navigate, setDate) {
}) })
tempReminderObservable((reminder) => { tempReminderObservable((reminder) => {
PushNotification.cancelLocalNotification({ id: TEMPERATURE_REMINDER_ID }) PushNotification.cancelLocalNotification({ id: '1' })
if (reminder.enabled) { if (reminder.enabled) {
const [hours, minutes] = reminder.time.split(':') const [hours, minutes] = reminder.time.split(':')
let target = new Moment() let target = new Moment()
@@ -61,75 +50,56 @@ export default function setupNotifications(navigate, setDate) {
} }
PushNotification.localNotificationSchedule({ PushNotification.localNotificationSchedule({
id: TEMPERATURE_REMINDER_ID, id: '1',
userInfo: { id: TEMPERATURE_REMINDER_ID }, userInfo: { id: '1' },
message: labels.tempReminder.notification, message: labels.tempReminder.notification,
date: target.toDate(), date: target.toDate(),
vibrate: false, vibrate: false,
repeatType: 'day', repeatType: 'day',
channelId: DRIP_CHANNEL_ID, channelId: 'drip-channel-id',
allowWhileIdle: true,
}) })
} }
}, false) }, false)
periodReminderObservable(() => updatePeriodNotification(), false) periodReminderObservable((reminder) => {
advanceNoticeDaysObservable(() => updatePeriodNotification(), false) PushNotification.cancelLocalNotification({ id: '2' })
if (reminder.enabled) setupPeriodReminder()
}, false)
getBleedingDaysSortedByDate().addListener((_, changes) => { getBleedingDaysSortedByDate().addListener((_, changes) => {
// the listener fires on setup, so we check if there were actually any changes // the listener fires on setup, so we check if there were actually any changes
if (nothingChanged(changes)) { if (nothingChanged(changes)) return
return PushNotification.cancelLocalNotification({ id: '2' })
} if (periodReminderObservable.value.enabled) setupPeriodReminder()
updatePeriodNotification()
}) })
} }
const updatePeriodNotification = () => { function setupPeriodReminder() {
// Cancel any existing period reminder const PushNotification = Platform.OS === 'ios' ? PN : PN.default
PushNotification.cancelLocalNotification({ id: PERIOD_REMINDER_ID })
// Set up a new period reminder if enabled
if (periodReminderObservable.value.enabled) {
schedulePeriodNotification()
}
}
function schedulePeriodNotification() {
const bleedingPrediction = cycleModule().getPredictedMenses() const bleedingPrediction = cycleModule().getPredictedMenses()
if (bleedingPrediction.length > 0) { if (bleedingPrediction.length > 0) {
const predictedBleedingStart = Moment( const predictedBleedingStart = Moment(
bleedingPrediction[0][0], bleedingPrediction[0][0],
'YYYY-MM-DD' 'YYYY-MM-DD'
) )
// 3 days before and at 6 am
const advanceNoticeDays = parseInt(advanceNoticeDaysObservable.value)
// ${advanceNoticeDays} days before and at 6 am
const reminderDate = predictedBleedingStart const reminderDate = predictedBleedingStart
.subtract(advanceNoticeDays, 'days') .subtract(3, 'days')
.hours(6) .hours(6)
.minutes(0) .minutes(0)
.seconds(0) .seconds(0)
if (reminderDate.isAfter()) { if (reminderDate.isAfter()) {
// period is likely to start in advanceNoticeDays to advanceNoticeDays + (length of prediction - 1) days // period is likely to start in 3 to 3 + (length of prediction - 1) days
const daysToEndOfPrediction = const daysToEndOfPrediction = bleedingPrediction[0].length + 2
advanceNoticeDays + bleedingPrediction[0].length - 1
PushNotification.localNotificationSchedule({ PushNotification.localNotificationSchedule({
id: PERIOD_REMINDER_ID, id: '2',
userInfo: { id: PERIOD_REMINDER_ID }, userInfo: { id: '2' },
message: labels.periodReminder.notification( message: labels.periodReminder.notification(daysToEndOfPrediction),
advanceNoticeDays,
daysToEndOfPrediction
),
date: reminderDate.toDate(), date: reminderDate.toDate(),
vibrate: false, vibrate: false,
channelId: DRIP_CHANNEL_ID, channelId: 'drip-channel-id',
allowWhileIdle: true,
}) })
} }
} }
+1 -15
View File
@@ -2,8 +2,6 @@ import AsyncStorage from '@react-native-async-storage/async-storage'
import Observable from 'obv' import Observable from 'obv'
import { TEMP_SCALE_MIN, TEMP_SCALE_MAX, TEMP_SCALE_UNITS } from './config' import { TEMP_SCALE_MIN, TEMP_SCALE_MAX, TEMP_SCALE_UNITS } from './config'
import { ADVANCE_PERIOD_NOTICE_DAYS_INIT_VALUE } from './config'
export const scaleObservable = Observable() export const scaleObservable = Observable()
setObvWithInitValue('tempScale', scaleObservable, { setObvWithInitValue('tempScale', scaleObservable, {
min: TEMP_SCALE_MIN, min: TEMP_SCALE_MIN,
@@ -61,18 +59,6 @@ export async function savePeriodPrediction(bool) {
} }
} }
export const advanceNoticeDaysObservable = Observable()
setObvWithInitValue(
'advanceNoticeDays',
advanceNoticeDaysObservable,
parseInt(ADVANCE_PERIOD_NOTICE_DAYS_INIT_VALUE)
)
export async function saveAdvanceNoticeDays(days) {
await AsyncStorage.setItem('advanceNoticeDays', JSON.stringify(days))
advanceNoticeDaysObservable.set(days)
}
export const useCervixAsSecondarySymptomObservable = Observable() export const useCervixAsSecondarySymptomObservable = Observable()
setObvWithInitValue( setObvWithInitValue(
'useCervixAsSecondarySymptom', 'useCervixAsSecondarySymptom',
@@ -123,7 +109,7 @@ export async function saveTemperatureTrackingCategory(bool) {
if (!temperatureTrackingCategoryObservable.value) { if (!temperatureTrackingCategoryObservable.value) {
// if temperature tracking is turned off, the temperature reminder gets disabled // if temperature tracking is turned off, the temperature reminder gets disabled
const tempReminderResult = await AsyncStorage.getItem('tempReminder') const tempReminderResult = await AsyncStorage.getItem('tempReminder')
if (tempReminderResult && JSON.parse(tempReminderResult).enabled) { if (tempReminderResult && JSON.parse(tempReminderResult).enabled) {
tempReminderObservable.set(false) tempReminderObservable.set(false)
} }
} }
+3 -4
View File
@@ -1,14 +1,13 @@
{ {
"name": "drip.", "name": "drip.",
"version": "1.2410.22", "version": "1.2403.19",
"contributors": [ "contributors": [
"Julia Friesel <julia.friesel@gmail.com>", "Julia Friesel <julia.friesel@gmail.com>",
"Marie Kochsiek", "Marie Kochsiek",
"Tina Baumann", "Tina Baumann",
"Sofiya Tepikin", "Sofiya Tepikin",
"Mariya Zadnepryanets", "Mariya Zadnepryanets",
"Lisa Hillebrand", "Lisa Hillebrand"
"Martha Dörfler"
], ],
"scripts": { "scripts": {
"start": "react-native start", "start": "react-native start",
@@ -48,7 +47,7 @@
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"react": "17.0.2", "react": "17.0.2",
"react-i18next": "^12.0.0", "react-i18next": "^12.0.0",
"react-native": "0.68.5", "react-native": "0.68.3",
"react-native-calendars": "^1.1287.0", "react-native-calendars": "^1.1287.0",
"react-native-document-picker": "^8.1.1", "react-native-document-picker": "^8.1.1",
"react-native-fs": "^2.20.0", "react-native-fs": "^2.20.0",
+11 -11
View File
@@ -6378,7 +6378,7 @@ promise@^7.1.1:
dependencies: dependencies:
asap "~2.0.3" asap "~2.0.3"
promise@^8.2.0: promise@^8.0.3:
version "8.3.0" version "8.3.0"
resolved "https://registry.yarnpkg.com/promise/-/promise-8.3.0.tgz#8cb333d1edeb61ef23869fbb8a4ea0279ab60e0a" resolved "https://registry.yarnpkg.com/promise/-/promise-8.3.0.tgz#8cb333d1edeb61ef23869fbb8a4ea0279ab60e0a"
integrity sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg== integrity sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==
@@ -6508,10 +6508,10 @@ react-native-calendars@^1.1287.0:
optionalDependencies: optionalDependencies:
moment "^2.29.4" moment "^2.29.4"
react-native-codegen@^0.0.18: react-native-codegen@^0.0.17:
version "0.0.18" version "0.0.17"
resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.0.18.tgz#99d6623d65292e8ce3fdb1d133a358caaa2145e7" resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.0.17.tgz#83fb814d94061cbd46667f510d2ddba35ffb50ac"
integrity sha512-XPI9aVsFy3dvgDZvyGWrFnknNiyb22kg5nHgxa0vjWTH9ENLBgVRZt9A64xHZ8BYihH+gl0p/1JNOCIEUzRPBg== integrity sha512-7GIEUmAemH9uWwB6iYXNNsPoPgH06pxzGRmdBzK98TgFBdYJZ7CBuZFPMe4jmHQTPOkQazKZ/w5O6/71JBixmw==
dependencies: dependencies:
"@babel/parser" "^7.14.0" "@babel/parser" "^7.14.0"
flow-parser "^0.121.0" flow-parser "^0.121.0"
@@ -6601,10 +6601,10 @@ react-native-version@^3.1.0:
resolve-from "^5.0.0" resolve-from "^5.0.0"
semver "^6.0.0" semver "^6.0.0"
react-native@0.68.5: react-native@0.68.3:
version "0.68.5" version "0.68.3"
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.68.5.tgz#8ba7389e00b757c59b6ea23bf38303d52367d155" resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.68.3.tgz#07ac7374acde9bc5e80f9e473e03d6b730528f1c"
integrity sha512-t3kiQ/gumFV+0r/NRSIGtYxanjY4da0utFqHgkMcRPJVwXFWC0Fr8YiOeRGYO1dp8EfrSsOjtfWic/inqVYlbQ== integrity sha512-LPgLQ4e96NWCrJPKlXzKfvlg1ddhfUplsEg00/cfBIMFZPJn2inzo9Rym8I/JYjmRORe4GjGY8kOem72hPm0Lw==
dependencies: dependencies:
"@jest/create-cache-key-function" "^27.0.1" "@jest/create-cache-key-function" "^27.0.1"
"@react-native-community/cli" "^7.0.3" "@react-native-community/cli" "^7.0.3"
@@ -6626,9 +6626,9 @@ react-native@0.68.5:
metro-source-map "0.67.0" metro-source-map "0.67.0"
nullthrows "^1.1.1" nullthrows "^1.1.1"
pretty-format "^26.5.2" pretty-format "^26.5.2"
promise "^8.2.0" promise "^8.0.3"
react-devtools-core "^4.23.0" react-devtools-core "^4.23.0"
react-native-codegen "^0.0.18" react-native-codegen "^0.0.17"
react-native-gradle-plugin "^0.0.6" react-native-gradle-plugin "^0.0.6"
react-refresh "^0.4.0" react-refresh "^0.4.0"
react-shallow-renderer "16.14.1" react-shallow-renderer "16.14.1"