Compare commits

...

29 Commits

Author SHA1 Message Date
Hannelore Meier 79b268fe4c 506: Review changes. 2024-10-26 21:01:57 +02:00
Hannelore Meier dddb095463 506: Enable custom timing for period reminders
Users can now specify how many days prior to their next predicted period they want to be notified
2024-10-26 21:01:09 +02:00
bl00dymarie 805587302b Merge branch '730-bug-bad-version-of-the-soloader-sdk' into 'main'
Resolve "Bug: Bad version of the SoLoader SDK"

Closes #730

See merge request bloodyhealth/drip!689
2024-10-24 12:25:51 +00:00
bl00dymarie e3f44d7654 Release: 1.2410.22 2024-10-24 14:19:05 +02:00
bl00dymarie 590acd0bcb Fix: Bad version of the SoLoader SDK 2024-10-24 14:19:05 +02:00
bl00dymarie 7710e9c9bd Merge branch '728-reminders-appear-only-after-opening-the-app-on-some-phone-models' into 'main'
Fix: Allow push notification in idle state for Android

Closes #728

See merge request bloodyhealth/drip!687
2024-10-22 12:06:27 +00:00
Lynn a434242640 Added step "yarn install" to iOS set-up 2024-10-18 18:59:47 +00:00
bl00dymarie 05f28b072a Fix: Allow push notification in idle state for Android 2024-10-15 13:38:42 +02:00
bl00dymarie 223ac148bd Merge branch 'Chore/Fix-android-build' into 'main'
Chore/fix android build

See merge request bloodyhealth/drip!686
2024-10-15 11:36:01 +00:00
bl00dymarie e82dcb6cc1 Chore: Store android keystore properties in separate file 2024-10-07 18:02:05 +02:00
bl00dymarie bd9c586edb Chore: Upgrade react-native to 0.68.5 2024-10-07 18:01:56 +02:00
wunderfisch d492a27797 Merge branch '723-feature-link-faq-in-app' into 'main'
adds a button leading to faq on website to about section in the app

Closes #723

See merge request bloodyhealth/drip!684
2024-10-03 16:35:28 +00:00
wunderfisch 22a451d4e6 Merge branch 'chore/add-condriputers' into 'main'
addding condriputers

See merge request bloodyhealth/drip!682
2024-10-03 16:35:10 +00:00
Lynn ae5f31015b Update README.md for M1 installations, fixed typo 2024-09-18 10:02:11 +00:00
Lynn c7104f7758 Update README.md for M1 installations 2024-09-18 10:00:33 +00:00
wunderfisch 86bdb8a1f8 adds a button leading to faq on website to about section in the app 2024-09-09 21:00:21 +02:00
bl00dymarie 5bc6be7849 Merge branch 'Chore/Finetune-release-documentation' into 'main'
Chore: Clean up TOC and add fdroid documentation

See merge request bloodyhealth/drip!679
2024-04-10 16:01:52 +00:00
bl00dymarie e6c253f2cb Merge branch 'Chore/Update-run-script-for-which-iPhone-simulator' into 'main'
Chore: Update run script for which iPhone simulator

See merge request bloodyhealth/drip!680
2024-04-08 10:47:54 +00:00
bl00dymarie 06fab6d2ca Chore: Update run script for which iPhone simulator 2024-04-08 12:39:36 +02:00
bl00dymarie b44d4c57e1 Update file RELEASE.md 2024-04-02 11:57:53 +00:00
bl00dymarie 35224ba722 Merge branch 'Chore/Update-changelog' into 'main'
Update Changelog for v1.2403.19

See merge request bloodyhealth/drip!674
2024-03-25 14:33:12 +00:00
bl00dymarie 2ca033d6d1 Update internal Nfp wording 2024-03-25 15:03:40 +01:00
bl00dymarie 09edaec721 Merge branch 'Chore/Update-RELEASE' into 'main'
Add extensive list of release steps

See merge request bloodyhealth/drip!677
2024-03-25 13:31:17 +00:00
bl00dymarie 7bbb6eaeab Fix formatting 2024-03-20 19:06:34 +01:00
bl00dymarie 09cb31035c Fix anchor links 2024-03-20 19:02:24 +01:00
bl00dymarie 536017914c Add extensive list of release steps 2024-03-20 18:55:58 +01:00
bl00dymarie b2457f4751 Update changelog for v1.2403.19 after implenting user testing feedback 2024-03-19 14:28:09 +01:00
bl00dymarie fd10a78a40 Update Changelog for v1.2403.11 2024-03-19 14:24:19 +01:00
wunderfisch 4212906917 addding condriputers 2024-02-29 17:54:25 +01:00
24 changed files with 549 additions and 215 deletions
+1 -1
View File
@@ -30,7 +30,7 @@ ios/Index/DataStore
build/ build/
.idea .idea
.gradle .gradle
local.properties *.properties
*.iml *.iml
*.hprof *.hprof
+41
View File
@@ -2,6 +2,47 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## v1.2403.19 Android & iOS
### Changes
- Disables temperature slider if temperature tracking off
- Disables secondary symptom if fertility and or cervix/cervical mucus are off
- Disables temperature reminder if temperature tracking off
- Disabled period reminder if period predictions off
- Return from sympto adapter if fertility off
- Restructure settings menu
- Unify wording to "sympto-thermal method"
- Format decimal to x.0 instead of x.00 used for standard deviation and average cycle in stats
- Use SelectTabGroup for secondary symptom customization
- Android changes after updating React Native to 0.68.3
- Update Android Gradle plugin from 7.0.3 to 7.0.4
- Update NDK to "24.0.8215888" only for M1 users which added support for aarch64
- Update metadata phone screenshots for Fdroid store listing
- Updated dependencies:
- @react-native-community/datetimepicker from 6.3.1 to 7.2.0
- @react-native-async-storage/async-storage from ^1.17.9 to ^1.18.2
- metro-react-native-babel-preset from ^0.66.2 to ^0.67.0
### Adds
- Customization settings can turn on & off:
- Tracking categories
- Period predictions
- Fertility phases calculation
- Home displays text elements depending on customization settings
- Chart displays tracking category elements depending on customization settings
- CycleDay displays tracking category elements and exclude switch depending on customization settings
- Reminder can be disabled depending on customization settings
- Adds disabled and more styling to AppSwitch
- Adds TrackingCategorySwitch
- Adds disabled, more styling and alert to SelectTabGroup
- Adds more marginTop to License page
- Adds info text to Password menu item in Settings
## v1.2401.17 iOS ## v1.2401.17 iOS
### Changes ### Changes
+38 -4
View File
@@ -34,13 +34,31 @@ or clone it with HTTPS
git clone https://gitlab.com/bloodyhealth/drip.git git clone https://gitlab.com/bloodyhealth/drip.git
### 2. Node & yarn version ### 2. Node 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
@@ -113,20 +131,36 @@ 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
### 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`.
+102 -22
View File
@@ -1,16 +1,44 @@
# How to release a new app version for Android # How to release
_Note: You need the release-key for Android to bundle a signed release that can be uploaded and published via the Google Play Store. A similar process for Apple requires a certificate to upload and publish the app to the App Store. More documentation on 'How to release a new app version for iOS' coming soon._ drip is developed in React Native for iOS and Android and is released on 4 different platforms:
# Table of Contents 1. [Google Play Store](https://play.google.com/store/apps/details?id=com.drip)
2. [Apple App Store](https://apps.apple.com/us/app/drip/id1584564949)
3. [F-Droid](https://f-droid.org/packages/com.drip/)
4. [drip Website](https://dripapp.org)
1. [version updating](#Version-updating) In an ideal world the app version is the same across platforms. In the past this has never been the case. The release v1.2403.19 is the first to be up to date on all 4 platforms!
2. [android building](#Building-in-Android)
- [APK](#APK)
- [AAB](#AAB)
3. [release sharing](#Share-the-release)
## Version updating Releasing a new version is very exciting and brings happy changes like fixing a bug, improving a feature, updating dependencies or adding a new functionality to the app. It is more than just pressing the button "publish new version".
_Note_: You need the release-key for Android to bundle a signed release that can be uploaded and published via the Google Play Store. A similar process for Apple requires a certificate to upload and publish the app to the App Store.
## Release steps
### 1. [Code](#code)
- 1.1 [Version updating](#version-updating)
- 1.2 [Android builds](#android-builds)
- 1.3 [iOS builds](#ios-builds)
- 1.4 [User testing](#user-testing)
- 1.5 [Release tag](#release-tag)
### 2. [Documentation](#documentation)
- 2.1 [Changelog](#changelog)
- 2.2 [Release notes](#release-notes)
- 2.3 [Releases on Gitlab](#releases-on-gitlab)
- 2.4 [Phone screenshots](#phone-screenshots)
### 3. [Publishing](#publishing)
- 3.1 [Google Play Console](#google-play-console)
- 3.2 [Apple App Store Connect](#apple-app-store-connect)
- 3.3 [F-droid](#f-droid)
- 3.4 [drip website](#drip-website)
- 3.5 [Communication](#communication)
- 3.6 [Self care](#self-care)
## Code
### Version updating
When you are done with a chore, a feature or a bugfix, you may want to share it with testers and eventually publish a release. In order to identify a specific app version we can update the version name, which is created based on the following format: `1.yymm.d` e.g. `1.2311.7`. If you want to upload a new app version to Google Play you also need to update the version code. When you are done with a chore, a feature or a bugfix, you may want to share it with testers and eventually publish a release. In order to identify a specific app version we can update the version name, which is created based on the following format: `1.yymm.d` e.g. `1.2311.7`. If you want to upload a new app version to Google Play you also need to update the version code.
@@ -34,7 +62,7 @@ Update the version number for iOS in `ios/drip/Info.plist` under:
<string>1.2403.19</string> <string>1.2403.19</string>
``` ```
## Building in Android ### Android builds
APK versus AAB APK versus AAB
@@ -42,7 +70,7 @@ APK versus AAB
(https://developer.android.com/build/building-cmdline) (https://developer.android.com/build/building-cmdline)
### APK #### APK
To build a release apk file, run the following command: To build a release apk file, run the following command:
@@ -64,7 +92,7 @@ _which is a shortcut for:_ `zipalign -v -p 4 ./android/app/build/outputs/apk/rel
It adds a file name `app-release_signed.apk` in the same folder in `./android/app/build/outputs/apk/release/` It adds a file name `app-release_signed.apk` in the same folder in `./android/app/build/outputs/apk/release/`
### AAB #### AAB
To build a release aab file, run: To build a release aab file, run:
@@ -84,7 +112,7 @@ yarn sign-android-aab-release
_which is a shortcut for:_ `jarsigner -keystore ./android/app/drip-release-key.keystore ./android/app/build/outputs/bundle/release/app-release.aab drip-release-key` _which is a shortcut for:_ `jarsigner -keystore ./android/app/drip-release-key.keystore ./android/app/build/outputs/bundle/release/app-release.aab drip-release-key`
## Building in iOS ### iOS builds
To build an .ipa archive file for an upload to the AppStore you need to go to xCode and select Build -> "Any iOS Device" and under "Product" -> "Archive". To build an .ipa archive file for an upload to the AppStore you need to go to xCode and select Build -> "Any iOS Device" and under "Product" -> "Archive".
@@ -95,29 +123,77 @@ Once the archiving process has completed you can chose to do the following:
- TestFlight & App Store for when you want to upload it for external testing and/or production release - TestFlight & App Store for when you want to upload it for external testing and/or production release
- TestFlight Internal Only for when you want to upload it for internal testing - TestFlight Internal Only for when you want to upload it for internal testing
## Share the release ### User testing
### Gitlab repository To enable external testing you need to remember that Google Play and Apple App Store might take up to 1 day for their review process. "External testing" for iOS allows testing drip on Testflight anonymously via a public link. "Open testing" for Android allows testing drip on Google Play as beta tester below the normal production listing.
For a quick and easy way to share an apk to testers who are willing to sideload drip onto their Android phones, do this: Upload a signed apk to the Gitlab repository of the drip website under `/release` https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/-/tree/main/release and maybe adapt the name of the apk with a more specific name than "app-release.apk". Now you can simply share a direct link to download your newly bundled apk, e.g. [a download link for v1.2311.14](https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/-/blob/main/release/v1.2311.14.apk). For a quick and easy way to share an apk to testers who are willing to sideload drip onto their Android phones, do this: Upload a signed apk to the Gitlab repository of the drip website under `/release` https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/-/tree/main/release and maybe adapt the name of the apk with a more specific name than "app-release.apk". Now you can simply share a direct link to download your newly bundled apk, e.g. [a download link for v1.2311.14](https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/-/blob/main/release/v1.2311.14.apk).
### Release tag
[Tags](https://gitlab.com/bloodyhealth/drip/-/tags) can mark a specific point in the coding/commmit history and helps us identify the version status of a released app. They are named "iOS-v1.2401.17" or "Release-v1.yymm.d".
Any tag starting with "Release" or "Android" will be checked by https://gitlab.com/fdroidci
## Documentation
### Changelog
The [changelog](https://gitlab.com/bloodyhealth/drip/-/blob/main/CHANGELOG.md) should reflect the technical / code changes between a previous and the new version. Please update the changelog file with any relevant additions, fixes and changes in the following format:
>**v1.yymm.d**
>
>**Changes**
>
>Changing the color of funky button
>Updating a library from 1.2.3 to 2.3.4
>
>**Adds**
>
>New feature for calendar
>
>**Fixed**
>
>Small bug in chart
### Release notes
These notes are for the users and curious ones who may want to start using drip. They should be based on the changelog but written in a friendly and easy to understand way. The focus is on the user perspective and the impact of the changes for the user. Behind the scenes and in depth code changes are less relevant.
Google Play limits these notes to 500 characters, whereas Apple's App Store limits these notes to 4.000 characters. In Fdroid there are no release notes.
### Releases on Gitlab
Under [Releases](https://gitlab.com/bloodyhealth/drip/-/releases) we keep track of all drip releases.
### Phone screenshots
If there are visual changes in the app you may want to update the screenshots for the Google Play Store listing, which allows up to 8 and for Apple's App Store, which allows up to 10 screenshots. Keep in mind that both Google Play and Apple have specific resolution requirements. You'll find Google's in Grow -> Store presence -> Main Store Listing -> Phone screenshots and Apple's on the main App Store Connect site. Here is a link for [Apple's screenshot specifications](https://developer.apple.com/help/app-store-connect/reference/screenshot-specifications).
Please also update [phone screenshots for the website](https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/-/tree/main/assets) and set links on [/index](https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/-/blob/f3da9776b1943ffa32458e74ef86eeca98c1891c/index.html#L47) and [/media](https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/-/blob/c7f999bb7ad736345321537cbffa3f4c24eeee6d/media.html#L33) that can then also be attached to a social media post.
## Publishing
### Google Play Console ### Google Play Console
Upload a signed aab to the [Google Play Console for developers](https://play.google.com/console/) and add it to the "App bundle explorer". This requires a higher versionCode and a different version name compared to previously uploaded aab or apk files. Upload a signed aab to the [Google Play Console for developers](https://play.google.com/console/) and add it to the "App bundle explorer". This requires a higher versionCode and a different version name compared to previously uploaded aab or apk files.
You can decide if you want the new app version to get released for testing (internal, closed or open) or for production. Keep in mind that any track other than "internal testing" triggers an external review by Google and might take a few hours. You can decide if you want the new app version to get released for testing (internal, closed or open) or for production. Keep in mind that any track other than "internal testing" triggers an external review by Google and might take a few hours.
#### Phone screenshots ### Apple App Store Connect
If there are visual changes in the app you may want to update the screenshots for the Google Play Store listing. Keep in mind that Google Play has specific resolution requirements. You'll find them in Grow -> Store presence -> Main Store Listing -> Phone screenshots. Upload a new version and submit it for review, before it can be published.
### F-droid
This account runs automated checks for drip looking at new `Release` or `Android` [tags](https://gitlab.com/bloodyhealth/drip/-/tags/) and updates the app's metadata yaml file in Fdroid without further ado.
However this is not the full story. Please have a look at previous commits to see what necessary changes got pushed, [see here](https://gitlab.com/fdroid/fdroiddata/-/commits/master/metadata/com.drip.yml).
### drip website ### drip website
After a new version has been published on Google Play (or F-Droid) the apk version that is downloadable directly from the [drip website](https://dripapp.org) needs to get updated as well. Therefore you upload a signed apk to the [repository](https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/) and adapt the name and link on /index.html. After a new version has been published on Google Play (or F-Droid) the apk version that is downloadable directly from the [drip website](https://dripapp.org) needs to get updated as well. Therefore you upload a signed apk to the [repository](https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/) as [we did in this commit](https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/-/commit/f8c0f90c1ae9f23bf8e1bc311790b85443149a4d), and adapt the name and link on /index.html [as we did in this commit](https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/-/commit2f8850ff5fa78615a4f335b625ea4a67d4acf03a) and [this commit](https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/-/commit/f3da9776b1943ffa32458e74ef86eeca98c1891c). Last time I checked it was [here](f3da9776b1943ffa32458e74ef86eeca98c1891c/index.html#L114).
Last time I checked it was [here](f3da9776b1943ffa32458e74ef86eeca98c1891c/index.html#L114).
#### Phone screenshots ### Communication
Please also update [phone screenshots here](https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/-/tree/main/assets) and set links on [/index](https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/-/blob/f3da9776b1943ffa32458e74ef86eeca98c1891c/index.html#L47) and [/media](https://gitlab.com/bloodyhealth/bloodyhealth.gitlab.io/-/blob/c7f999bb7ad736345321537cbffa3f4c24eeee6d/media.html#L33) that can then also be attached to a social media post.
You probably want to share the app update by posting on one or more of these platforms: You probably want to share the app update by posting on one or more of these platforms:
@@ -126,3 +202,7 @@ You probably want to share the app update by posting on one or more of these pla
- [Ko-fi](https://ko-fi.com/dripapp) - [Ko-fi](https://ko-fi.com/dripapp)
- [Linkedin](https://www.linkedin.com/company/34899684/) - [Linkedin](https://www.linkedin.com/company/34899684/)
- Different tech, privacy, feminist oriented slacks - Different tech, privacy, feminist oriented slacks
### Self care
Congratulations. Take a break, eat some chocolate, go see a live show of your favorite band, masturbate <3!
+18 -2
View File
@@ -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
+1
View File
@@ -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
+4
View File
@@ -18,6 +18,10 @@ 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
@@ -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
+6 -22
View File
@@ -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}>
+4
View File
@@ -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),
+3 -1
View File
@@ -45,7 +45,9 @@
"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 technical issues, don't hesitate to contact us via email. You can also contribute to the code base on Gitlab and visit our website." "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.",
"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,4 +39,8 @@ export default {
url: 'https://www.flaticon.com', url: 'https://www.flaticon.com',
text: 'Flaticon', text: 'Flaticon',
}, },
faq: {
url: 'https://dripapp.org/faq',
text: 'FAQ',
},
} }
+6 -4
View File
@@ -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
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.2403.19</string> <string>1.2410.22</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
+1 -1
View File
@@ -1,4 +1,4 @@
export default function getSensiplanMucus(feeling, texture) { export default function getNfpMucus(feeling, texture) {
if (typeof feeling != 'number' || typeof texture != 'number') return null if (typeof feeling != 'number' || typeof texture != 'number') return null
const feelingMapping = { const feelingMapping = {
+55 -25
View File
@@ -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
View File
@@ -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)
} }
} }
+6 -5
View File
@@ -1,19 +1,20 @@
{ {
"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",
"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",
"android": "react-native run-android", "android": "react-native run-android",
"ios": "react-native run-ios --simulator=\"iPhone 15\"", "ios": "react-native run-ios --simulator=\"iPhone 8 Plus\"",
"iosSE": "react-native run-ios --simulator=\"iPhone SE (2nd generation)\"", "ios15": "react-native run-ios --simulator=\"iPhone 15 Plus\"",
"log": "react-native log-android", "log": "react-native log-android",
"test": "jest test && yarn lint", "test": "jest test && yarn lint",
"test-watch": "jest --watch test", "test-watch": "jest --watch test",
@@ -47,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",
+79
View File
@@ -0,0 +1,79 @@
import getNfpMucus from '../lib/nfp-mucus'
describe('getNfpMucus', () => {
test('returns null if there is no value for feeling or texture', () => {
expect(getNfpMucus()).toBeNull()
expect(getNfpMucus(undefined, 3)).toBeNull()
expect(getNfpMucus(2, undefined)).toBeNull()
})
describe('results in t for:', () => {
test('dry feeling and no texture', function () {
const nfpValue = getNfpMucus(0, 0)
expect(nfpValue).toEqual(0)
})
})
describe('results in Ø for:', () => {
test('no feeling and no texture', function () {
const nfpValue = getNfpMucus(1, 0)
expect(nfpValue).toEqual(1)
})
})
describe('results in f for:', () => {
test('wet feeling and no texture', function () {
const nfpValue = getNfpMucus(2, 0)
expect(nfpValue).toEqual(2)
})
})
describe('results in S for:', () => {
test('dry feeling and creamy texture', function () {
const nfpValue = getNfpMucus(0, 1)
expect(nfpValue).toEqual(3)
})
test('no feeling and creamy texture', function () {
const nfpValue = getNfpMucus(1, 1)
expect(nfpValue).toEqual(3)
})
test('wet feeling and creamy texture', function () {
const nfpValue = getNfpMucus(2, 1)
expect(nfpValue).toEqual(3)
})
})
describe('results in +S for:', () => {
test('dry feeling and egg white texture', function () {
const nfpValue = getNfpMucus(0, 2)
expect(nfpValue).toEqual(4)
})
test('no feeling and egg white texture', function () {
const nfpValue = getNfpMucus(1, 2)
expect(nfpValue).toEqual(4)
})
test('wet feeling and egg white texture', function () {
const nfpValue = getNfpMucus(2, 2)
expect(nfpValue).toEqual(4)
})
test('slippery feeling and egg white texture', function () {
const nfpValue = getNfpMucus(3, 2)
expect(nfpValue).toEqual(4)
})
test('slippery feeling and creamy texture', function () {
const nfpValue = getNfpMucus(3, 1)
expect(nfpValue).toEqual(4)
})
test('slippery feeling and no texture', function () {
const nfpValue = getNfpMucus(3, 0)
expect(nfpValue).toEqual(4)
})
})
})
-79
View File
@@ -1,79 +0,0 @@
import getSensiplanMucus from '../lib/nfp-mucus'
describe('getSensiplanMucus', () => {
test('returns null if there is no value for feeling or texture', () => {
expect(getSensiplanMucus()).toBeNull()
expect(getSensiplanMucus(undefined, 3)).toBeNull()
expect(getSensiplanMucus(2, undefined)).toBeNull()
})
describe('results in t for:', () => {
test('dry feeling and no texture', function () {
const sensiplanValue = getSensiplanMucus(0, 0)
expect(sensiplanValue).toEqual(0)
})
})
describe('results in Ø for:', () => {
test('no feeling and no texture', function () {
const sensiplanValue = getSensiplanMucus(1, 0)
expect(sensiplanValue).toEqual(1)
})
})
describe('results in f for:', () => {
test('wet feeling and no texture', function () {
const sensiplanValue = getSensiplanMucus(2, 0)
expect(sensiplanValue).toEqual(2)
})
})
describe('results in S for:', () => {
test('dry feeling and creamy texture', function () {
const sensiplanValue = getSensiplanMucus(0, 1)
expect(sensiplanValue).toEqual(3)
})
test('no feeling and creamy texture', function () {
const sensiplanValue = getSensiplanMucus(1, 1)
expect(sensiplanValue).toEqual(3)
})
test('wet feeling and creamy texture', function () {
const sensiplanValue = getSensiplanMucus(2, 1)
expect(sensiplanValue).toEqual(3)
})
})
describe('results in +S for:', () => {
test('dry feeling and egg white texture', function () {
const sensiplanValue = getSensiplanMucus(0, 2)
expect(sensiplanValue).toEqual(4)
})
test('no feeling and egg white texture', function () {
const sensiplanValue = getSensiplanMucus(1, 2)
expect(sensiplanValue).toEqual(4)
})
test('wet feeling and egg white texture', function () {
const sensiplanValue = getSensiplanMucus(2, 2)
expect(sensiplanValue).toEqual(4)
})
test('slippery feeling and egg white texture', function () {
const sensiplanValue = getSensiplanMucus(3, 2)
expect(sensiplanValue).toEqual(4)
})
test('slippery feeling and creamy texture', function () {
const sensiplanValue = getSensiplanMucus(3, 1)
expect(sensiplanValue).toEqual(4)
})
test('slippery feeling and no texture', function () {
const sensiplanValue = getSensiplanMucus(3, 0)
expect(sensiplanValue).toEqual(4)
})
})
})
+11 -11
View File
@@ -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"