Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 79b268fe4c | |||
| dddb095463 | |||
| 805587302b | |||
| e3f44d7654 | |||
| 590acd0bcb | |||
| 7710e9c9bd | |||
| a434242640 | |||
| 05f28b072a | |||
| 223ac148bd | |||
| e82dcb6cc1 | |||
| bd9c586edb |
+1
-1
@@ -30,7 +30,7 @@ ios/Index/DataStore
|
|||||||
build/
|
build/
|
||||||
.idea
|
.idea
|
||||||
.gradle
|
.gradle
|
||||||
local.properties
|
*.properties
|
||||||
*.iml
|
*.iml
|
||||||
*.hprof
|
*.hprof
|
||||||
|
|
||||||
|
|||||||
@@ -131,15 +131,19 @@ 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 XCode dependencies by running the following command from the root project directory:
|
i. Install yarn dependencies
|
||||||
|
|
||||||
|
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 ..
|
||||||
|
|
||||||
ii. To run app either open drip workspace ('drip.xcworkspace' file) with XCode and run "Build" or run the following command:
|
iii. To run app either open drip workspace ('drip.xcworkspace' file) with XCode and run "Build" or run the following command:
|
||||||
|
|
||||||
yarn ios
|
yarn ios
|
||||||
|
|
||||||
iii. If you are building the app with XCode make sure you are running this as well:
|
iiii. If you are building the app with XCode make sure you are running this as well:
|
||||||
|
|
||||||
yarn start
|
yarn start
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
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
|
||||||
@@ -125,6 +127,16 @@ 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
|
||||||
@@ -134,8 +146,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 33
|
versionCode 38
|
||||||
versionName "1.2403.19"
|
versionName "1.2410.22"
|
||||||
ndk {
|
ndk {
|
||||||
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
||||||
}
|
}
|
||||||
@@ -150,6 +162,10 @@ 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
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ 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
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
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,
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
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,13 +1,12 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { StyleSheet, View } from 'react-native'
|
import { 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 alertError from '../common/alert-error'
|
|
||||||
import SliderLabel from './slider-label'
|
import SliderLabel from './slider-label'
|
||||||
|
import { styles } from './slider-styles'
|
||||||
|
import alertError from '../common/alert-error'
|
||||||
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'
|
||||||
|
|
||||||
@@ -40,7 +39,7 @@ const TemperatureSlider = ({ disabled }) => {
|
|||||||
customLabel={SliderLabel}
|
customLabel={SliderLabel}
|
||||||
enableLabel={true}
|
enableLabel={true}
|
||||||
markerStyle={styles.marker}
|
markerStyle={styles.marker}
|
||||||
markerOffsetY={Sizes.tiny}
|
markerOffsetY={styles.markerOffsetY}
|
||||||
max={TEMP_MAX}
|
max={TEMP_MAX}
|
||||||
min={TEMP_MIN}
|
min={TEMP_MIN}
|
||||||
onValuesChange={onTemperatureSliderChange}
|
onValuesChange={onTemperatureSliderChange}
|
||||||
@@ -61,34 +60,3 @@ 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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
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
|
||||||
@@ -1,13 +1,11 @@
|
|||||||
import React, { useState } from 'react'
|
import React 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'
|
||||||
@@ -16,17 +14,7 @@ import labels from '../../../i18n/en/settings'
|
|||||||
import { Alert, Pressable } from 'react-native'
|
import { Alert, Pressable } from 'react-native'
|
||||||
|
|
||||||
const Reminders = () => {
|
const Reminders = () => {
|
||||||
const isPeriodPredictionDisabled = !periodPredictionObservable.value
|
const periodReminderDisabledPrompt = () => {
|
||||||
|
|
||||||
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,
|
||||||
@@ -43,16 +31,12 @@ const Reminders = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppPage>
|
<AppPage>
|
||||||
<Pressable onPress={reminderDisabledPrompt}>
|
<Pressable onPress={periodReminderDisabledPrompt}>
|
||||||
<Segment title={labels.periodReminder.title}>
|
<Segment title={labels.periodReminder.title}>
|
||||||
<AppSwitch
|
<PeriodReminder />
|
||||||
onToggle={periodReminderToggle}
|
|
||||||
text={labels.periodReminder.reminderText}
|
|
||||||
value={isPeriodReminderEnabled}
|
|
||||||
disabled={isPeriodPredictionDisabled}
|
|
||||||
/>
|
|
||||||
</Segment>
|
</Segment>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
<Pressable onPress={tempReminderDisabledPrompt}>
|
<Pressable onPress={tempReminderDisabledPrompt}>
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ 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),
|
||||||
|
|||||||
+6
-4
@@ -60,10 +60,12 @@ export default {
|
|||||||
},
|
},
|
||||||
periodReminder: {
|
periodReminder: {
|
||||||
title: 'Next period reminder',
|
title: 'Next period reminder',
|
||||||
reminderText:
|
reminderTextSingular:
|
||||||
'Get a notification 3 days before your next period is likely to start.',
|
'Get a notification 1 day before your next period is likely to start.',
|
||||||
notification: (daysToEndOfPrediction) =>
|
reminderTextPlural: (days) =>
|
||||||
`Your next period is likely to start in 3 to ${daysToEndOfPrediction} days.`,
|
`Get a notification ${days} days before your next period is likely to start.`,
|
||||||
|
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
@@ -19,7 +19,7 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.2403.19</string>
|
<string>1.2410.22</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
|
|||||||
+55
-25
@@ -2,6 +2,7 @@ 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'
|
||||||
@@ -13,12 +14,19 @@ 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) {
|
||||||
Platform.OS === 'android' ? requestNotifications() : null
|
// for Android, this method call is necessary
|
||||||
const PushNotification = Platform.OS === 'ios' ? PN : PN.default
|
if (Platform.OS === 'android') {
|
||||||
|
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
|
||||||
})
|
})
|
||||||
@@ -26,7 +34,10 @@ 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 (notification.data?.id === '1' || notification.id === '1') {
|
if (
|
||||||
|
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')
|
||||||
@@ -37,7 +48,7 @@ export default function setupNotifications(navigate, setDate) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
tempReminderObservable((reminder) => {
|
tempReminderObservable((reminder) => {
|
||||||
PushNotification.cancelLocalNotification({ id: '1' })
|
PushNotification.cancelLocalNotification({ id: TEMPERATURE_REMINDER_ID })
|
||||||
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()
|
||||||
@@ -50,56 +61,75 @@ export default function setupNotifications(navigate, setDate) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PushNotification.localNotificationSchedule({
|
PushNotification.localNotificationSchedule({
|
||||||
id: '1',
|
id: TEMPERATURE_REMINDER_ID,
|
||||||
userInfo: { id: '1' },
|
userInfo: { id: TEMPERATURE_REMINDER_ID },
|
||||||
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((reminder) => {
|
periodReminderObservable(() => updatePeriodNotification(), false)
|
||||||
PushNotification.cancelLocalNotification({ id: '2' })
|
advanceNoticeDaysObservable(() => updatePeriodNotification(), false)
|
||||||
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)) return
|
if (nothingChanged(changes)) {
|
||||||
PushNotification.cancelLocalNotification({ id: '2' })
|
return
|
||||||
if (periodReminderObservable.value.enabled) setupPeriodReminder()
|
}
|
||||||
|
|
||||||
|
updatePeriodNotification()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupPeriodReminder() {
|
const updatePeriodNotification = () => {
|
||||||
const PushNotification = Platform.OS === 'ios' ? PN : PN.default
|
// Cancel any existing period reminder
|
||||||
|
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(3, 'days')
|
.subtract(advanceNoticeDays, '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 3 to 3 + (length of prediction - 1) days
|
// period is likely to start in advanceNoticeDays to advanceNoticeDays + (length of prediction - 1) days
|
||||||
const daysToEndOfPrediction = bleedingPrediction[0].length + 2
|
const daysToEndOfPrediction =
|
||||||
|
advanceNoticeDays + bleedingPrediction[0].length - 1
|
||||||
|
|
||||||
PushNotification.localNotificationSchedule({
|
PushNotification.localNotificationSchedule({
|
||||||
id: '2',
|
id: PERIOD_REMINDER_ID,
|
||||||
userInfo: { id: '2' },
|
userInfo: { id: PERIOD_REMINDER_ID },
|
||||||
message: labels.periodReminder.notification(daysToEndOfPrediction),
|
message: labels.periodReminder.notification(
|
||||||
|
advanceNoticeDays,
|
||||||
|
daysToEndOfPrediction
|
||||||
|
),
|
||||||
date: reminderDate.toDate(),
|
date: reminderDate.toDate(),
|
||||||
vibrate: false,
|
vibrate: false,
|
||||||
channelId: 'drip-channel-id',
|
channelId: DRIP_CHANNEL_ID,
|
||||||
|
allowWhileIdle: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-1
@@ -2,6 +2,8 @@ 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,
|
||||||
@@ -59,6 +61,18 @@ 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',
|
||||||
@@ -109,7 +123,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "drip.",
|
"name": "drip.",
|
||||||
"version": "1.2403.19",
|
"version": "1.2410.22",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
"Julia Friesel <julia.friesel@gmail.com>",
|
"Julia Friesel <julia.friesel@gmail.com>",
|
||||||
"Marie Kochsiek",
|
"Marie Kochsiek",
|
||||||
@@ -48,7 +48,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.3",
|
"react-native": "0.68.5",
|
||||||
"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",
|
||||||
|
|||||||
@@ -6378,7 +6378,7 @@ promise@^7.1.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
asap "~2.0.3"
|
asap "~2.0.3"
|
||||||
|
|
||||||
promise@^8.0.3:
|
promise@^8.2.0:
|
||||||
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.17:
|
react-native-codegen@^0.0.18:
|
||||||
version "0.0.17"
|
version "0.0.18"
|
||||||
resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.0.17.tgz#83fb814d94061cbd46667f510d2ddba35ffb50ac"
|
resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.0.18.tgz#99d6623d65292e8ce3fdb1d133a358caaa2145e7"
|
||||||
integrity sha512-7GIEUmAemH9uWwB6iYXNNsPoPgH06pxzGRmdBzK98TgFBdYJZ7CBuZFPMe4jmHQTPOkQazKZ/w5O6/71JBixmw==
|
integrity sha512-XPI9aVsFy3dvgDZvyGWrFnknNiyb22kg5nHgxa0vjWTH9ENLBgVRZt9A64xHZ8BYihH+gl0p/1JNOCIEUzRPBg==
|
||||||
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.3:
|
react-native@0.68.5:
|
||||||
version "0.68.3"
|
version "0.68.5"
|
||||||
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.68.3.tgz#07ac7374acde9bc5e80f9e473e03d6b730528f1c"
|
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.68.5.tgz#8ba7389e00b757c59b6ea23bf38303d52367d155"
|
||||||
integrity sha512-LPgLQ4e96NWCrJPKlXzKfvlg1ddhfUplsEg00/cfBIMFZPJn2inzo9Rym8I/JYjmRORe4GjGY8kOem72hPm0Lw==
|
integrity sha512-t3kiQ/gumFV+0r/NRSIGtYxanjY4da0utFqHgkMcRPJVwXFWC0Fr8YiOeRGYO1dp8EfrSsOjtfWic/inqVYlbQ==
|
||||||
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.3:
|
|||||||
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.0.3"
|
promise "^8.2.0"
|
||||||
react-devtools-core "^4.23.0"
|
react-devtools-core "^4.23.0"
|
||||||
react-native-codegen "^0.0.17"
|
react-native-codegen "^0.0.18"
|
||||||
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"
|
||||||
|
|||||||
Reference in New Issue
Block a user