Compare commits

...

103 Commits

Author SHA1 Message Date
tina f4fa2a7c57 Reverts changes to app-modal and changes input window for notes on iOS 2024-10-01 21:24:30 +02:00
tina 4de32e3801 Merge branch 'main' into '650-long-text-in-note-hides-behind-keyboard-1.2402.16'
# Conflicts:
#   android/app/build.gradle
#   ios/drip/Info.plist
#   package.json
2024-10-01 15:20:11 +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
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 3cedcf601e Merge branch 'Release-1.2403.19' into 'main'
Release 1.2403.19 update for Android and iOS

See merge request bloodyhealth/drip!678
2024-03-21 11:56:17 +00:00
bl00dymarie e4d97b362f Release 1.2403.19 update for Android and iOS 2024-03-21 12:53:15 +01: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
bl00dymarie d8cb679d77 Merge branch 'Add-ios-release-notes' into 'main'
Chore: Add release notes for iOS

See merge request bloodyhealth/drip!676
2024-03-19 13:24:13 +00:00
bl00dymarie 839acfa22d Add release notes for iOS 2024-03-19 14:22:08 +01:00
wunderfisch a0482291cf Merge branch '712-chore-testing-feedback-for-v1-2403-11-part-2' into 'main'
Resolve "Chore: Testing Feedback for v1.2403.11 part 2"

Closes #712

See merge request bloodyhealth/drip!675
2024-03-19 11:54:58 +00:00
bl00dymarie 37f718d1b0 Remove info from menu temporarily 2024-03-18 19:03:40 +01:00
bl00dymarie 9b0abd5367 Add one small info text to the bottom of customization index 2024-03-18 19:03:21 +01:00
bl00dymarie c8521cf5ed Merge branch '711-chore-testing-feedback-for-v1-2403-11' into 'main'
Resolve "Chore: Testing Feedback for v1.2403.11"

Closes #711

See merge request bloodyhealth/drip!673
2024-03-18 17:47:49 +00:00
wunderfisch f7c6f4bfd0 replace hard coded label 2024-03-18 18:46:00 +01:00
wunderfisch 15a0b3d270 adapt text according to customization 2024-03-18 18:42:45 +01:00
bl00dymarie 4e37f1b7de Unify spelling starting with capital letter 2024-03-17 17:00:53 +01:00
bl00dymarie 56e90b69e6 Rearrange segments in customization + add subheader 2024-03-16 14:39:38 +01:00
bl00dymarie b481bd8352 Don't show the exclude switch when fertility is off 2024-03-16 14:37:58 +01:00
bl00dymarie e33c13e5e0 Remove idle labels 2024-03-16 14:37:49 +01:00
wunderfisch 693c766da8 fixing cervical mucus button also showing disabled alert 2024-03-14 18:02:15 +01:00
bl00dymarie fc4bc625ce Add some info to home text elements 2024-03-14 14:28:38 +01:00
bl00dymarie 0e7d84874c Add more padding to button if home is almost empty 2024-03-14 14:28:09 +01:00
bl00dymarie a93a338e6e Improve wording for disabled alerts 2024-03-14 14:27:18 +01:00
wunderfisch 003f825ef4 Merge branch '676-feature-customisation-not-display-mucus-cervix' into 'main'
Disable and switch secondary symptom depending on cervical mucus and cervix turned on or off.

Closes #676

See merge request bloodyhealth/drip!656
2024-03-14 11:08:08 +00:00
wunderfisch fc520c4a20 Merge branch 'chore/refactor-customization' into '676-feature-customisation-not-display-mucus-cervix'
Chore/refactor customization

See merge request bloodyhealth/drip!672
2024-03-14 11:04:51 +00:00
bl00dymarie eaf01e98d5 renaming 2024-03-14 10:58:56 +00:00
bl00dymarie f5894c028e renaming 2024-03-14 10:54:18 +00:00
bl00dymarie a29c0e2eec Merge branch '676-feature-customisation-not-display-mucus-cervix' into 'chore/refactor-customization'
# Conflicts:
#   i18n/en/settings.js
2024-03-13 15:19:17 +00:00
bl00dymarie 18466ebcee Merge branch 'feature/Add-symptom-icons-to-customization' into '676-feature-customisation-not-display-mucus-cervix'
Feature: Add symptom icons to customization toggles

See merge request bloodyhealth/drip!666
2024-03-11 11:42:36 +00:00
wunderfisch b65b5f3561 refactoring disable function of temperature slider 2024-03-07 18:58:37 +01:00
wunderfisch 3e8f15e04e disable temperature slider when fertility or temperature turned off 2024-02-29 17:46:56 +01:00
bl00dymarie b17c86ffd6 Merge branch 'Chore/Unify-spelling-of-sympto-thermal' into '676-feature-customisation-not-display-mucus-cervix'
Chore: Unify spelling of sympto-thermal

See merge request bloodyhealth/drip!667
2024-02-29 10:37:05 +00:00
bl00dymarie 24df5cea31 Spell "science-based" 2024-02-29 10:35:22 +00:00
bl00dymarie 610383a103 Unify the spelling of "sympto-thermal" 2024-02-28 13:06:32 +01:00
wunderfisch ad47b4bee0 enabling alert for disabled button in tab group 2024-02-28 13:06:25 +01:00
bl00dymarie ffe8fab822 Add colored tracking icons to customization toggles 2024-02-28 12:27:59 +01:00
wunderfisch 661abc8aee revision of customization texts and refactoring behavior when disabled 2024-02-27 18:10:11 +01:00
bl00dymarie 46a02560e8 Add title labels to customization 2024-02-26 18:23:04 +01:00
bl00dymarie 9ec52b78cf Add comment for future TODO linking gitlab issue 2024-02-26 16:51:29 +00:00
bl00dymarie 8a65c081a8 Use translation for customization tracking categories 2024-02-26 17:24:44 +01:00
bl00dymarie 72823ef95c Generalize title of symptom labels 2024-02-26 17:24:44 +01:00
bl00dymarie 27776f93cc Merge branch '675-feature-customisation-not-display-fertility-phases' into '676-feature-customisation-not-display-mucus-cervix'
Resolve "feature: customisation (not) display fertility phases"

See merge request bloodyhealth/drip!659
2024-02-26 15:48:27 +00:00
wunderfisch ef4095d61c fixing issue import and empty statement 2024-02-26 16:32:08 +01:00
wunderfisch 4fc11d2f7e changes in customization texts and small changes to styling 2024-02-26 16:31:53 +01:00
wunderfisch 37152b3fec small changes in styling. more space beween switch lines, smaller switches in iOS (testing please), less bottom margin for segments 2024-02-26 16:31:29 +01:00
bl00dymarie 405e27bdbe Remove isSecondarySymptomDisabled;
because maybe we don't need it anymore with fertilityTracking taking its job
2024-02-23 16:50:30 +01:00
bl00dymarie 1c73509473 Move cervix & mucus off turn fertility off logic out of local storage 2024-02-23 16:38:44 +01:00
bl00dymarie 5855ea0a34 Move temp off turns fertility off logic out of local storage 2024-02-23 16:38:11 +01:00
bl00dymarie 27bb25e6da Add check for if tempReminder is null 2024-02-23 15:27:44 +01:00
bl00dymarie 8150b791ed Only check if fertility tracking enabled for display of secondary Symptom 2024-02-23 15:27:44 +01:00
bl00dymarie 3c0ea9b208 Turn fertility tracking off when mucus and cervix tracking are off 2024-02-23 15:27:44 +01:00
bl00dymarie d0d691c6af Turn fertility tracking off when temperature tracking is off 2024-02-23 15:27:44 +01:00
bl00dymarie 29c3b9b108 Disable fertility tracking switch depending on temp, mucus or cervix 2024-02-23 15:27:44 +01:00
bl00dymarie c037c630d3 Check for FertilityTrackingEnabled in sympto-adapter 2024-02-23 15:27:44 +01:00
bl00dymarie 31888c3331 Check for FertilityTrackingEnabled on chart 2024-02-23 15:27:44 +01:00
bl00dymarie faad51f4f1 Check for FertilityTrackingEnabled on home 2024-02-23 15:27:44 +01:00
bl00dymarie e395730d98 First step for fertilityTracking Toggle 2024-02-23 15:27:44 +01:00
wunderfisch 092b557396 make second symptom buttons show alert if switched off 2024-02-23 12:39:31 +01:00
wunderfisch 4ebec41a0a fix saving of secondary symptom 2024-02-23 12:16:32 +01:00
bl00dymarie e69ca93382 Remove idle code 2024-02-22 13:29:16 +00:00
bl00dymarie aa440742ca Merge branch 'main' into '676-feature-customisation-not-display-mucus-cervix'
# Conflicts:
#   components/settings/customization/index.js
2024-02-22 13:22:38 +00:00
bl00dymarie 7fff6cbdf4 Merge branch 'Chore/Bring-back-prettier' into 'main'
Bring back prettierrc as before

See merge request bloodyhealth/drip!664
2024-02-22 12:56:31 +00:00
bl00dymarie 75dbe8bfde Merge branch 'Chore/Add-color-to-switch-element' into 'main'
Add trackColor to Switch element directly

See merge request bloodyhealth/drip!663
2024-02-22 12:43:57 +00:00
bl00dymarie 4921aca3c3 Bring back prettierrc as before 2024-02-22 13:32:58 +01:00
bl00dymarie cbaf6977f6 Add trackColor to Switch element directly 2024-02-22 13:22:41 +01:00
bl00dymarie b9ec4c7108 Merge branch 'main' into 676-feature-customisation-not-display-mucus-cervix 2024-02-20 12:29:28 +01:00
bl00dymarie d66e5f36e5 Rename to secondarySymptomDisabledPrompt 2024-02-19 18:10:57 +01:00
bl00dymarie 69c546f684 Rename shouldUseCervix to useCervixAsSecondarySymptom 2024-02-19 18:05:20 +01:00
bl00dymarie dc304afe75 Rename labels to more neutral 'secondarySymptom' 2024-02-19 18:01:57 +01:00
bl00dymarie 63bc8a1daf Rename to useCervixAsSecondarySymptom 2024-02-19 17:51:25 +01:00
bl00dymarie 7fa130f9e1 Rename to useCervixAsSecondarySymptomObservable 2024-02-19 17:13:40 +01:00
bl00dymarie f741562496 Merge branch 'main' into '650-long-text-in-note-hides-behind-keyboard-1.2402.16'
# Conflicts:
#   android/app/build.gradle
#   package.json
2024-02-19 16:02:40 +00:00
bl00dymarie d338df1460 Merge branch 'Chore/ReactNative-Upgrade-0.68.3-Android' into 'main'
Chore: React native upgrade 0.68.3 android

See merge request bloodyhealth/drip!660
2024-02-19 16:01:09 +00:00
wunderfisch 5f55500831 small changes on secondary symptom to make it work nicly with the button logic instead of the former switch and to display the correct texts 2024-02-19 12:13:58 +01:00
bl00dymarie a23278b9b2 Update version to 1.2402.16 2024-02-16 17:45:34 +01:00
bl00dymarie f170bf608b Differentiate keyboardavoiding behavior based on ios/android 2024-02-16 17:43:38 +01:00
bl00dymarie 1979005ca0 Allow more lines for "other" under mood, pain, sex 2024-02-16 17:43:32 +01:00
bl00dymarie 8badf0cabb Limit lines to 3 for symptom day boxes 2024-02-16 17:43:26 +01:00
bl00dymarie 216a6b73ee Add multiline to notes in edit view 2024-02-16 17:43:20 +01:00
bl00dymarie 4eedb171a3 Full upgrade to react native v0.68.3 for Android 2024-02-15 14:31:46 +01:00
bl00dymarie 3193fa4f33 Partial update RN to 0.68.3 for android build 2024-02-15 13:14:28 +01:00
bl00dymarie d23b5fe420 Add oneTimeTransformIntoNumber for secondarySymptom Switch 2024-02-14 16:10:05 +01:00
bl00dymarie 311cee0e17 Adapt color for secondarySymptom Boxes 2024-02-14 16:10:00 +01:00
bl00dymarie 76137fc000 Replace hard coded text 2024-02-14 16:09:47 +01:00
bl00dymarie 09614319e3 Add SelectTabGroup for secondary Symptom Switch 2024-02-14 16:08:07 +01:00
wunderfisch ebbfd8aba5 merging work on secondary symptom logic and buttons 2024-02-14 10:26:13 +01:00
bl00dymarie 1662bb29f0 Change prop Type to value instead of bool for saveUseCervix 2024-02-13 17:24:29 +01:00
bl00dymarie a59dfa8336 Add SelectTabGroup for secondary Symptom Switch 2024-02-13 17:24:26 +01:00
wunderfisch d7f1eb81fb centralizing code to set the Secondary Symptom from different actions in one function 2024-02-13 15:46:04 +01:00
wunderfisch 82803bbb83 fix: disabling the secondary symptom toggle when not both categories are enabled, didn't work before after new save 2024-02-13 11:52:33 +01:00
wunderfisch 8a8b3217fa small changes on Secondary Symptom disable function 2024-02-12 16:05:49 +01:00
wunderfisch e11acbab78 Disable and switch secondary symptom depending on cervical mucus and cervix turned on or off. 2024-02-12 14:49:43 +01:00
44 changed files with 1134 additions and 458 deletions
+2 -1
View File
@@ -59,8 +59,9 @@ buck-out/
# Bundle artifact
*.jsbundle
# CocoaPods
# Ruby / CocoaPods
/ios/Pods/
/vendor/bundle/
# RN android release
android/app/bin/
+41
View File
@@ -2,6 +2,47 @@
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
### Changes
+2 -2
View File
@@ -33,9 +33,10 @@ We are an open source project and we highly appreciate contributions. At the sam
- 🔮 open source
- 🩸 feminist and gender inclusive
- 🔒 secure: data entered stays with that person/on their device
- 🔬 science based: we implemented the symptothermal method
- 🔬 science-based: we implemented the sympto-thermal method
This means that we will never implement anything that contradicts these core values. Some examples: We will never build a cloud integration, we will never make an ovulation prediction.
- If you would like to make a sustainable contribution to the project, we would be happy to join the game.
### Reporting Bugs or Making Suggestions
@@ -48,7 +49,6 @@ If you found a bug or have suggestions, please :one: first review the [list of e
- If you want to open a merge request, yeah :tada: exciting! We are using a template for merge requests to make sure we explain what we have done and why.
- Keep in mind that people who will review your merge request are more motivated to do so when the merge request is well explained and ideally not too big.
### Thank you
![](https://media.giphy.com/media/kPA88elN9kYco/giphy.gif)
+31 -1
View File
@@ -34,13 +34,31 @@ or clone it with HTTPS
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.
Once you have nvm running you can install node 14:
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:
npm install --global yarn
@@ -127,6 +145,18 @@ iii. If you are building the app with XCode make sure you are running this as we
### 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
Make sure that you have Java 1.8 by running `java -version`.
+121 -21
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)
2. [android building](#Building-in-Android)
- [APK](#APK)
- [AAB](#AAB)
3. [release sharing](#Share-the-release)
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!
## 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.
@@ -25,7 +53,16 @@ yarn release
The versionName and versionCode [are defined here](https://gitlab.com/bloodyhealth/drip/-/blob/5401789c46f4a02915ab900ef284581be420451c/android/app/build.gradle#L137-138) and in [package.json](https://gitlab.com/bloodyhealth/drip/-/blob/5401789c46f4a02915ab900ef284581be420451c/package.json#L3).
## Building in Android
**Note for iOS**
Update the version number for iOS in `ios/drip/Info.plist` under:
```
<key>CFBundleShortVersionString</key>
<string>1.2403.19</string>
```
### Android builds
APK versus AAB
@@ -33,7 +70,7 @@ APK versus AAB
(https://developer.android.com/build/building-cmdline)
### APK
#### APK
To build a release apk file, run the following command:
@@ -55,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/`
### AAB
#### AAB
To build a release aab file, run:
@@ -75,29 +112,88 @@ 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`
## Share the release
### iOS builds
### Gitlab repository
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".
Once the archiving process has completed you can chose to do the following:
"Distribute the app"
- 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
### User testing
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).
### 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
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.
#### 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
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.
Last time I checked it was [here](f3da9776b1943ffa32458e74ef86eeca98c1891c/index.html#L114).
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).
#### Phone screenshots
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.
### Communication
You probably want to share the app update by posting on one or more of these platforms:
@@ -106,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)
- [Linkedin](https://www.linkedin.com/company/34899684/)
- 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!
+2 -2
View File
@@ -134,8 +134,8 @@ android {
applicationId "com.drip"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 25
versionName "1.2311.14"
versionCode 33
versionName "1.2403.19"
ndk {
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}
+1 -1
View File
@@ -8,6 +8,6 @@
android:usesCleartextTraffic="true"
tools:targetApi="28"
tools:ignore="GoogleAppIndexingWarning">
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false" />
</application>
</manifest>
@@ -1,10 +1,10 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
* directory of this source tree.
*/
package com.rndiffapp;
package com.drip;
import android.content.Context;
import com.facebook.flipper.android.AndroidFlipperClient;
import com.facebook.flipper.android.utils.FlipperUtils;
@@ -18,6 +18,7 @@ import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
import com.facebook.react.ReactInstanceEventListener;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.network.NetworkingModule;
@@ -46,7 +47,7 @@ public class ReactNativeFlipper {
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
if (reactContext == null) {
reactInstanceManager.addReactInstanceEventListener(
new ReactInstanceManager.ReactInstanceEventListener() {
new ReactInstanceEventListener() {
@Override
public void onReactContextInitialized(ReactContext reactContext) {
reactInstanceManager.removeReactInstanceEventListener(this);
+3 -3
View File
@@ -57,12 +57,12 @@
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustPan"
android:screenOrientation="sensorPortrait">
android:screenOrientation="sensorPortrait"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -1,6 +1,8 @@
package com.drip;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
public class MainActivity extends ReactActivity {
@@ -12,4 +14,27 @@ public class MainActivity extends ReactActivity {
protected String getMainComponentName() {
return "drip";
}
/**
* Returns the instance of the {@link ReactActivityDelegate}. There the RootView is created and
* you can specify the rendered you wish to use (Fabric or the older renderer).
*/
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new MainActivityDelegate(this, getMainComponentName());
}
public static class MainActivityDelegate extends ReactActivityDelegate {
public MainActivityDelegate(ReactActivity activity, String mainComponentName) {
super(activity, mainComponentName);
}
@Override
protected ReactRootView createRootView() {
ReactRootView reactRootView = new ReactRootView(getContext());
// If you opted-in for the New Architecture, we enable the Fabric Renderer.
return reactRootView;
}
}
}
@@ -8,6 +8,7 @@ import com.facebook.react.ReactInstanceManager;
import cl.json.ShareApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.config.ReactFeatureFlags;
import com.facebook.soloader.SoLoader;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
@@ -62,7 +63,7 @@ public class MainApplication extends Application implements ReactApplication, Sh
We use reflection here to pick up the class that initializes Flipper,
since Flipper library is not available in release mode
*/
Class<?> aClass = Class.forName("com.rndiffapp.ReactNativeFlipper");
Class<?> aClass = Class.forName("com.drip.ReactNativeFlipper");
aClass
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
.invoke(null, context, reactInstanceManager);
+12 -2
View File
@@ -1,3 +1,5 @@
import org.apache.tools.ant.taskdefs.condition.Os
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
@@ -7,8 +9,9 @@ buildscript {
}
ext.kotlinVersion = '1.3.40'
dependencies {
classpath('com.android.tools.build:gradle:7.0.3')
classpath('com.android.tools.build:gradle:7.0.4')
classpath("com.facebook.react:react-native-gradle-plugin")
classpath("de.undercouch:gradle-download-task:4.1.2")
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
@@ -50,5 +53,12 @@ ext {
minSdkVersion = 21
compileSdkVersion = 33
targetSdkVersion = 33
if (System.properties['os.arch'] == "aarch64") {
// For M1 Users we need to use the NDK 24 which added support for aarch64
ndkVersion = "24.0.8215888"
} else {
// Otherwise we default to the side-by-side NDK version from AGP.
ndkVersion = "21.4.7075529"
}
}
+14 -5
View File
@@ -9,8 +9,8 @@
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx1024m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
@@ -25,7 +25,16 @@ android.useAndroidX=true
android.enableJetifier=true
# Version of flipper SDK to use with React Native
FLIPPER_VERSION=0.99.0
FLIPPER_VERSION=0.125.0
# https://github.com/facebook/react-native/issues/30729
org.gradle.jvmargs=-Xmx4g
# Use this property to specify which architecture you want to build.
# You can also override it from the CLI using
# ./gradlew <task> -PreactNativeArchitectures=x86_64
reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
# Use this property to enable support to the new architecture.
# This will allow you to use TurboModules and the Fabric render in
# your application. You should enable this flag either if you want
# to write custom TurboModules/Fabric components OR use libraries that
# are providing them.
newArchEnabled=false
Binary file not shown.
+1 -2
View File
@@ -1,6 +1,5 @@
#Wed Oct 11 14:45:21 CEST 2023
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
+166 -118
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
#!/bin/sh
#
# Copyright 2015 the original author or authors.
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,70 +17,103 @@
#
##############################################################################
##
## Gradle start up script for UN*X
##
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
MAX_FD=maximum
warn () {
echo "$*"
}
} >&2
die () {
echo
echo "$*"
echo
exit 1
}
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -88,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -99,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@@ -107,80 +140,95 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
+7
View File
@@ -3,3 +3,10 @@ rootProject.name = 'drip'
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
include ':app'
includeBuild('../node_modules/react-native-gradle-plugin')
if (settings.hasProperty("newArchEnabled") && settings.newArchEnabled == "true") {
include(":ReactAndroid")
project(":ReactAndroid").projectDir = file('../node_modules/react-native/ReactAndroid')
}
+16 -3
View File
@@ -14,7 +14,10 @@ import {
determinePredictionText,
formatWithOrdinalSuffix,
} from './helpers/home'
import { periodPredictionObservable } from '../local-storage'
import {
fertilityTrackingObservable,
periodPredictionObservable,
} from '../local-storage'
import { Colors, Fonts, Sizes, Spacing } from '../styles'
import { LocalDate } from '@js-joda/core'
@@ -28,11 +31,12 @@ const Home = ({ navigate, setDate }) => {
navigate('CycleDay')
}
const isFertilityTrackingEnabled = fertilityTrackingObservable.value
const todayDateString = LocalDate.now().toString()
const { getCycleDayNumber, getPredictedMenses } = cycleModule()
const cycleDayNumber = getCycleDayNumber(todayDateString)
const { status, phase, statusText } =
getFertilityStatusForDay(todayDateString)
isFertilityTrackingEnabled && getFertilityStatusForDay(todayDateString)
const isPeriodPredictionEnabled = periodPredictionObservable.value
const prediction = determinePredictionText(getPredictedMenses(), t)
@@ -47,6 +51,7 @@ const Home = ({ navigate, setDate }) => {
>
<AppText style={styles.title}>{moment().format('MMM Do YYYY')}</AppText>
{/* display if at least 1 bleeding day has been entered */}
{cycleDayNumber && (
<View style={styles.line}>
<AppText style={styles.whiteSubtitle}>{cycleDayText}</AppText>
@@ -55,7 +60,9 @@ const Home = ({ navigate, setDate }) => {
</AppText>
</View>
)}
{phase && (
{/* display if fertility tracking enabled and if phase 1, 2 or 3 has been identified */}
{isFertilityTrackingEnabled && phase && (
<View style={styles.line}>
<AppText style={styles.whiteSubtitle}>
{formatWithOrdinalSuffix(phase)}
@@ -67,11 +74,14 @@ const Home = ({ navigate, setDate }) => {
<Asterisk />
</View>
)}
{isPeriodPredictionEnabled && (
<View style={styles.line}>
<AppText style={styles.turquoiseText}>{prediction}</AppText>
</View>
)}
{!isFertilityTrackingEnabled && <View style={styles.largePadding}></View>}
<Button isCTA isSmall={false} onPress={navigateToCycleDayView}>
{t('labels.home.addDataForToday')}
</Button>
@@ -110,6 +120,9 @@ const styles = StyleSheet.create({
color: 'white',
fontSize: Sizes.subtitle,
},
largePadding: {
padding: Spacing.large,
},
})
Home.propTypes = {
+6
View File
@@ -22,6 +22,8 @@ import {
painTrackingCategoryObservable,
sexTrackingCategoryObservable,
temperatureTrackingCategoryObservable,
mucusTrackingCategoryObservable,
cervixTrackingCategoryObservable,
} from '../../local-storage'
import { makeColumnInfo } from '../helpers/chart'
@@ -72,6 +74,10 @@ const CycleChart = ({ navigate, setDate }) => {
const symptomRowEnabledSymptoms = symptomRowSymptoms.filter((symptom) => {
if (symptom === 'sex') {
return sexTrackingCategoryObservable.value ? symptom : null
} else if (symptom === 'mucus') {
return mucusTrackingCategoryObservable.value ? symptom : null
} else if (symptom === 'cervix') {
return cervixTrackingCategoryObservable.value ? symptom : null
} else if (symptom === 'desire') {
return desireTrackingCategoryObservable.value ? symptom : null
} else if (symptom === 'pain') {
-8
View File
@@ -1,7 +1,6 @@
import React from 'react'
import {
Dimensions,
KeyboardAvoidingView,
Modal,
StyleSheet,
TouchableOpacity,
@@ -20,7 +19,6 @@ const AppModal = ({ children, onClose }) => (
transparent={true}
visible={true}
>
<KeyboardAvoidingView behavior={'padding'} style={styles.safeAreaView}>
<TouchableOpacity onPress={onClose} style={styles.blackBackground} />
<View style={styles.modalWindow}>
<View style={styles.headerContainer}>
@@ -28,7 +26,6 @@ const AppModal = ({ children, onClose }) => (
</View>
{children}
</View>
</KeyboardAvoidingView>
</Modal>
)
@@ -65,11 +62,6 @@ const styles = StyleSheet.create({
elevation: 2, // works on android
minWidth: '80%',
},
safeAreaView: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
})
export default AppModal
+9 -4
View File
@@ -1,12 +1,13 @@
import React from 'react'
import { StyleSheet, Switch, View } from 'react-native'
import { Platform, StyleSheet, Switch, View } from 'react-native'
import PropTypes from 'prop-types'
import AppText from './app-text'
import { Containers } from '../../styles'
import { Colors, Containers, Spacing } from '../../styles'
const AppSwitch = ({ onToggle, text, value, trackColor, disabled }) => {
const AppSwitch = ({ onToggle, text, value, disabled }) => {
const trackColor = { true: Colors.turquoiseDark }
return (
<View style={styles.container}>
<View style={styles.textContainer}>
@@ -27,16 +28,20 @@ AppSwitch.propTypes = {
onToggle: PropTypes.func.isRequired,
text: PropTypes.string,
value: PropTypes.bool,
trackColor: PropTypes.string,
disabled: PropTypes.bool,
}
const styles = StyleSheet.create({
container: {
...Containers.rowContainer,
marginTop: Spacing.tiny,
},
switch: {
flex: 1,
transform:
Platform.OS === 'ios'
? [{ scaleX: 0.8 }, { scaleY: 0.8 }]
: [{ scaleX: 1 }, { scaleY: 1 }],
},
textContainer: {
flex: 4,
+8 -1
View File
@@ -6,13 +6,14 @@ import AppText from './app-text'
import { Colors, Containers, Spacing, Typography } from '../../styles'
const Segment = ({ children, last, title }) => {
const Segment = ({ children, last, title, subheader }) => {
const containerStyle = last ? styles.containerLast : styles.container
const commonStyle = Object.assign({}, containerStyle)
return (
<View style={commonStyle}>
{title && <AppText style={styles.title}>{title}</AppText>}
{subheader && <AppText style={styles.subheader}>{subheader}</AppText>}
{children}
</View>
)
@@ -23,6 +24,7 @@ Segment.propTypes = {
last: PropTypes.bool,
style: PropTypes.object,
title: PropTypes.string,
subheader: PropTypes.string,
}
const styles = StyleSheet.create({
@@ -39,6 +41,11 @@ const styles = StyleSheet.create({
title: {
...Typography.subtitle,
},
subheader: {
...Typography.subtitle,
fontWeight: 'bold',
marginBottom: Spacing.zero,
},
})
export default Segment
@@ -0,0 +1,63 @@
import React from 'react'
import { Platform, StyleSheet, Switch, View } from 'react-native'
import PropTypes from 'prop-types'
import AppText from './app-text'
import DripIcon from '../../assets/drip-icons'
import { Colors, Containers, Sizes, Spacing } from '../../styles'
const TrackingCategorySwitch = ({ onToggle, symptom, text, value }) => {
const trackColor = { true: Colors.turquoiseDark }
const iconColor = value ? Colors.iconColors[symptom].color : Colors.grey
return (
<View style={styles.container}>
<View style={styles.iconContainer}>
<DripIcon
color={iconColor}
name={`drip-icon-${symptom}`}
size={Sizes.title}
/>
</View>
<View style={styles.textContainer}>
<AppText>{text}</AppText>
</View>
<Switch
onValueChange={onToggle}
style={styles.appSwitch}
value={value}
trackColor={trackColor}
/>
</View>
)
}
TrackingCategorySwitch.propTypes = {
onToggle: PropTypes.func.isRequired,
symptom: PropTypes.string,
text: PropTypes.string,
value: PropTypes.bool,
}
const styles = StyleSheet.create({
container: {
...Containers.rowContainer,
marginVertical: Spacing.tiny,
},
iconContainer: {
marginRight: Spacing.tiny,
flex: 1,
},
textContainer: {
flex: 5,
},
appSwitch: {
flex: 2,
transform:
Platform.OS === 'ios'
? [{ scaleX: 0.8 }, { scaleY: 0.8 }]
: [{ scaleX: 1 }, { scaleY: 1 }],
},
})
export default TrackingCategorySwitch
@@ -16,6 +16,8 @@ import {
painTrackingCategoryObservable,
sexTrackingCategoryObservable,
temperatureTrackingCategoryObservable,
mucusTrackingCategoryObservable,
cervixTrackingCategoryObservable,
} from '../../local-storage'
import { Spacing } from '../../styles'
import { SYMPTOMS } from '../../config'
@@ -40,6 +42,10 @@ const CycleDayOverView = ({ date, setDate, isTemperatureEditView }) => {
return temperatureTrackingCategoryObservable.value ? symptom : null
} else if (symptom === 'sex') {
return sexTrackingCategoryObservable.value ? symptom : null
} else if (symptom === 'mucus') {
return mucusTrackingCategoryObservable.value ? symptom : null
} else if (symptom === 'cervix') {
return cervixTrackingCategoryObservable.value ? symptom : null
} else if (symptom === 'desire') {
return desireTrackingCategoryObservable.value ? symptom : null
} else if (symptom === 'pain') {
+62 -6
View File
@@ -1,22 +1,61 @@
import React from 'react'
import PropTypes from 'prop-types'
import { StyleSheet, TouchableOpacity, View } from 'react-native'
import { Alert, StyleSheet, TouchableOpacity, View } from 'react-native'
import AppText from '../common/app-text'
import { Colors, Containers } from '../../styles'
import labels from '../../i18n/en/settings'
export default function SelectTabGroup({
activeButton,
buttons,
onSelect,
disabled,
}) {
// TODO https://gitlab.com/bloodyhealth/drip/-/issues/707
const oneTimeTransformIntoNumber =
typeof activeButton === 'boolean' && Number(activeButton)
const isSecondarySymptomSwitch =
buttons[0]['label'] === labels.secondarySymptom.mucus
// Disable is only used for secondarySymptom in customization, if more come up maybe consider more tidy solution
const showDisabledAlert = (label) => {
if (
label === labels.secondarySymptom.cervix ||
label === labels.secondarySymptom.mucus
) {
Alert.alert(
labels.secondarySymptom.disabled.title,
labels.secondarySymptom.disabled.message
)
}
}
export default function SelectTabGroup({ activeButton, buttons, onSelect }) {
return (
<View style={styles.container}>
{buttons.map(({ label, value }, i) => {
const isActive = value === activeButton
const boxStyle = [styles.box, isActive && styles.boxActive]
const textStyle = [styles.text, isActive && styles.textActive]
const isActive =
value === activeButton || value === oneTimeTransformIntoNumber
const boxStyle = [
styles.box,
isActive && styles.boxActive,
isSecondarySymptomSwitch && styles.purpleBox,
isSecondarySymptomSwitch && isActive && styles.activePurpleBox,
disabled && styles.disabledBox,
]
const textStyle = [
styles.text,
isSecondarySymptomSwitch && styles.purpleText,
isActive && styles.textActive,
disabled && styles.greyText,
]
return (
<TouchableOpacity
onPress={() => onSelect(value)}
onPress={() =>
!disabled ? onSelect(value) : showDisabledAlert(label)
}
key={i}
style={boxStyle}
>
@@ -32,6 +71,7 @@ SelectTabGroup.propTypes = {
activeButton: PropTypes.number,
buttons: PropTypes.array.isRequired,
onSelect: PropTypes.func.isRequired,
disabled: PropTypes.bool,
}
const styles = StyleSheet.create({
@@ -50,4 +90,20 @@ const styles = StyleSheet.create({
textActive: {
color: 'white',
},
purpleBox: {
borderColor: Colors.purple,
},
activePurpleBox: {
backgroundColor: Colors.purple,
},
purpleText: {
color: Colors.purple,
},
greyText: {
color: Colors.grey,
},
disabledBox: {
borderColor: Colors.grey,
backgroundColor: Colors.turquoiseLight,
},
})
+2 -2
View File
@@ -20,7 +20,7 @@ const SymptomBox = ({
editedSymptom,
setEditedSymptom,
}) => {
const { t } = useTranslation(null, { keyPrefix: 'cycleDay.symptomBox' })
const { t } = useTranslation(null, { keyPrefix: 'symptoms' })
const isSymptomEdited = editedSymptom === symptom
const isSymptomDisabled = isDateInFuture(date) && symptom !== 'note'
const isExcluded = symptomData !== null ? symptomData.exclude : false
@@ -64,7 +64,7 @@ const SymptomBox = ({
<View style={styles.textContainer}>
<AppText style={symptomNameStyle}>{t(symptom)}</AppText>
{symptomDataToDisplay && (
<AppText style={textStyle} numberOfLines={4}>
<AppText style={textStyle} numberOfLines={3}>
{symptomDataToDisplay}
</AppText>
)}
+18 -4
View File
@@ -1,6 +1,12 @@
import React, { useState } from 'react'
import PropTypes from 'prop-types'
import { ScrollView, StyleSheet, View } from 'react-native'
import {
Dimensions,
Platform,
ScrollView,
StyleSheet,
View,
} from 'react-native'
import AppModal from '../common/app-modal'
import AppSwitch from '../common/app-switch'
@@ -15,6 +21,7 @@ import Temperature from './temperature'
import { blank, save, shouldShow, symtomPage } from '../helpers/cycle-day'
import { showToast } from '../helpers/general'
import { fertilityTrackingObservable } from '../../local-storage'
import { shared as sharedLabels } from '../../i18n/en/labels'
import info from '../../i18n/en/symptom-info'
import { Colors, Containers, Sizes, Spacing } from '../../styles'
@@ -25,6 +32,7 @@ const SymptomEditView = ({ date, onClose, symptom, symptomData }) => {
const [shouldShowInfo, setShouldShowInfo] = useState(false)
const getParsedData = () => JSON.parse(JSON.stringify(data))
const onPressLearnMore = () => setShouldShowInfo(!shouldShowInfo)
const isFertilityTrackingEnabled = fertilityTrackingObservable.value
const onEditNote = (note) => {
const parsedData = getParsedData()
@@ -109,9 +117,12 @@ const SymptomEditView = ({ date, onClose, symptom, symptomData }) => {
const noteText = symptom === 'note' ? data.value : data.note
const inputProps = {
multiline: true,
numberOfLines: 3,
numberOfLines: Platform.OS === 'ios' ? null : 4, // only Android
minHeight: Platform.OS === 'ios' ? styles.input.height : null,
maxHeight:
Platform.OS === 'ios' ? Dimensions.get('window').height * 0.4 : null,
style: symptom === 'note' ? null : styles.input, // overwrites previous 2 lines to fix note space in symptoms
scrollEnabled: true,
style: styles.input,
textAlignVertical: 'top',
}
@@ -167,7 +178,10 @@ const SymptomEditView = ({ date, onClose, symptom, symptomData }) => {
</Segment>
)
})}
{shouldShow(symptomConfig.excludeText) && (
{/* show exclude AppSwitch for bleeding, mucus, cervix, temperature */}
{/* but if fertility is off only for bleeding */}
{shouldShow(symptomConfig.excludeText) &&
(symptom === 'bleeding' || isFertilityTrackingEnabled) && (
<Segment style={styles.segmentBorder}>
<AppSwitch
onToggle={onExcludeToggle}
+7 -2
View File
@@ -1,6 +1,10 @@
import { LocalDate } from '@js-joda/core'
import { scaleObservable, unitObservable } from '../../local-storage'
import {
fertilityTrackingObservable,
scaleObservable,
unitObservable,
} from '../../local-storage'
import { getCycleStatusForDay } from '../../lib/sympto-adapter'
import { getCycleDay, getAmountOfCycleDays } from '../../db'
@@ -270,7 +274,8 @@ export function nfpLines() {
if (dateString < cycle.startDate) updateCurrentCycle(dateString)
if (cycle.noMoreCycles) return ret
const tempShift = cycle.status.temperatureShift
const tempShift =
fertilityTrackingObservable.value && cycle.status.temperatureShift
if (tempShift) {
if (tempShift.firstHighMeasurementDay.date === dateString) {
+219 -77
View File
@@ -1,38 +1,49 @@
import React, { useState } from 'react'
import { Alert, Pressable } from 'react-native'
import React, { useEffect, useState } from 'react'
import { Alert, Pressable, StyleSheet, View } from 'react-native'
import { useTranslation } from 'react-i18next'
import AppIcon from '../../common/app-icon'
import AppPage from '../../common/app-page'
import AppSwitch from '../../common/app-switch'
import AppText from '../../common/app-text'
import { Colors, Spacing, Typography } from '../../../styles'
import TemperatureSlider from './temperature-slider'
import Segment from '../../common/segment'
import TrackingCategorySwitch from '../../common/tracking-category-switch'
import SelectTabGroup from '../../cycle-day/select-tab-group'
import {
desireTrackingCategoryObservable,
fertilityTrackingObservable,
moodTrackingCategoryObservable,
noteTrackingCategoryObservable,
painTrackingCategoryObservable,
sexTrackingCategoryObservable,
temperatureTrackingCategoryObservable,
mucusTrackingCategoryObservable,
cervixTrackingCategoryObservable,
periodPredictionObservable,
useCervixAsSecondarySymptomObservable,
saveDesireTrackingCategory,
saveFertilityTrackingEnabled,
saveMoodTrackingCategory,
saveNoteTrackingCategory,
savePainTrackingCategory,
saveMucusTrackingCategory,
saveCervixTrackingCategory,
savePeriodPrediction,
saveSexTrackingCategory,
saveTemperatureTrackingCategory,
saveUseCervix,
periodPredictionObservable,
useCervixObservable,
saveUseCervixAsSecondarySymptom,
} from '../../../local-storage'
import { Colors } from '../../../styles'
import labels from '../../../i18n/en/settings'
import { SYMPTOMS } from '../../../config'
const Settings = () => {
const [shouldUseCervix, setShouldUseCervix] = useState(
useCervixObservable.value
)
const { t } = useTranslation(null, { keyPrefix: 'symptoms' })
const [useCervixAsSecondarySymptom, setUseCervixAsSecondarySymptom] =
useState(useCervixAsSecondarySymptomObservable.value)
const [isPeriodPredictionEnabled, setPeriodPrediction] = useState(
periodPredictionObservable.value
@@ -41,6 +52,14 @@ const Settings = () => {
const [isTemperatureTrackingCategoryEnabled, setTemperatureTrackingCategory] =
useState(temperatureTrackingCategoryObservable.value)
const [isMucusTrackingCategoryEnabled, setMucusTrackingCategory] = useState(
mucusTrackingCategoryObservable.value
)
const [isCervixTrackingCategoryEnabled, setCervixTrackingCategory] = useState(
cervixTrackingCategoryObservable.value
)
const [isSexTrackingCategoryEnabled, setSexTrackingCategory] = useState(
sexTrackingCategoryObservable.value
)
@@ -61,19 +80,33 @@ const Settings = () => {
noteTrackingCategoryObservable.value
)
const [isEnabled, setIsEnabled] = useState(false)
const toggleSwitch = () => setIsEnabled((previousState) => !previousState)
const [isFertilityTrackingEnabled, setFertilityTrackingEnabled] = useState(
fertilityTrackingObservable.value
)
const fertilityTrackingToggle = (value) => {
setFertilityTrackingEnabled(value)
saveFertilityTrackingEnabled(value)
}
const temperatureTrackingCategoryToggle = (value) => {
setTemperatureTrackingCategory(value)
saveTemperatureTrackingCategory(value)
if (!value) {
setFertilityTrackingEnabled(false)
saveFertilityTrackingEnabled(false)
}
}
const mucusTrackingCategoryToggle = (value) => {
manageSecondarySymptom(cervixTrackingCategoryObservable.value, value)
}
const cervixTrackingCategoryToggle = (value) => {
manageSecondarySymptom(value, mucusTrackingCategoryObservable.value)
}
const sexTrackingCategoryToggle = (value) => {
setSexTrackingCategory(value)
saveSexTrackingCategory(value)
}
const desireTrackingCategoryToggle = (value) => {
setDesireTrackingCategory(value)
saveDesireTrackingCategory(value)
@@ -90,120 +123,229 @@ const Settings = () => {
setNoteTrackingCategory(value)
saveNoteTrackingCategory(value)
}
const onPeriodPredictionToggle = (value) => {
setPeriodPrediction(value)
savePeriodPrediction(value)
}
const fertilityTrackingText = isFertilityTrackingEnabled
? labels.fertilityTracking.on
: labels.fertilityTracking.off
const periodPredictionText = isPeriodPredictionEnabled
? labels.periodPrediction.on
: labels.periodPrediction.off
const onCervixToggle = (value) => {
setShouldUseCervix(value)
saveUseCervix(value)
const secondarySymptomButtons = [
{
label: labels.secondarySymptom.mucus,
value: 0,
},
{
label: labels.secondarySymptom.cervix,
value: 1,
},
]
const onSelectTab = (value) => {
if (isMucusTrackingCategoryEnabled && isCervixTrackingCategoryEnabled) {
setUseCervixAsSecondarySymptom(value)
saveUseCervixAsSecondarySymptom(value)
} else {
secondarySymptomDisabledPrompt()
}
}
const cervixText = shouldUseCervix
? labels.useCervix.cervixModeOn
: labels.useCervix.cervixModeOff
// is needed so secondary symptom is set correct on load
useEffect(() => {
manageSecondarySymptom(
cervixTrackingCategoryObservable.value,
mucusTrackingCategoryObservable.value
)
}, [])
const manageSecondarySymptom = (cervix, mucus) => {
if (!cervix && mucus) {
setUseCervixAsSecondarySymptom(0)
saveUseCervixAsSecondarySymptom(0)
} else if (cervix && !mucus) {
setUseCervixAsSecondarySymptom(1)
saveUseCervixAsSecondarySymptom(1)
} else if (!cervix && !mucus) {
setFertilityTrackingEnabled(false)
saveFertilityTrackingEnabled(false)
}
setMucusTrackingCategory(mucus)
saveMucusTrackingCategory(mucus)
setCervixTrackingCategory(cervix)
saveCervixTrackingCategory(cervix)
}
const secondarySymptomDisabledPrompt = () => {
if (!isFertilityTrackingEnabled) {
Alert.alert(
labels.secondarySymptom.disabled.title,
labels.secondarySymptom.disabled.message
)
} else if (
!isMucusTrackingCategoryEnabled == isCervixTrackingCategoryEnabled
) {
Alert.alert(
labels.secondarySymptom.disabled.title,
labels.secondarySymptom.disabled.noSecondaryEnabled
)
}
}
const manageFertilityFeature =
isTemperatureTrackingCategoryEnabled &&
(isMucusTrackingCategoryEnabled || isCervixTrackingCategoryEnabled)
const cervixText = useCervixAsSecondarySymptom
? labels.secondarySymptom.cervixModeOn
: labels.secondarySymptom.cervixModeOff
const sliderDisabledPrompt = () => {
if (!isTemperatureTrackingCategoryEnabled) {
Alert.alert(labels.disabled.title, labels.disabled.message)
Alert.alert(labels.tempScale.disabled, labels.tempScale.disabledMessage)
}
}
const fertilityDisabledPrompt = () => {
if (!manageFertilityFeature) {
Alert.alert(
labels.fertilityTracking.disabledTitle,
labels.fertilityTracking.disabled
)
}
}
return (
<AppPage title={'Customization'}>
<Segment title={'Tracking categories'}>
<AppSwitch
<AppPage title={labels.customization.title}>
<Segment title={labels.customization.trackingCategories}>
<TrackingCategorySwitch
onToggle={temperatureTrackingCategoryToggle}
text={SYMPTOMS[1]}
text={t(SYMPTOMS[1])}
value={isTemperatureTrackingCategoryEnabled}
trackColor={{ true: Colors.turquoiseDark }}
symptom={SYMPTOMS[1]}
/>
<AppSwitch
<TrackingCategorySwitch
onToggle={(enabled) => {
mucusTrackingCategoryToggle(enabled)
}}
text={t(SYMPTOMS[2])}
value={isMucusTrackingCategoryEnabled}
symptom={SYMPTOMS[2]}
/>
<TrackingCategorySwitch
onToggle={(enabled) => {
cervixTrackingCategoryToggle(enabled)
}}
text={t(SYMPTOMS[3])}
value={isCervixTrackingCategoryEnabled}
symptom={SYMPTOMS[3]}
/>
<TrackingCategorySwitch
onToggle={sexTrackingCategoryToggle}
text={SYMPTOMS[4]}
text={t(SYMPTOMS[4])}
value={isSexTrackingCategoryEnabled}
trackColor={{ true: Colors.turquoiseDark }}
symptom={SYMPTOMS[4]}
/>
<AppSwitch
<TrackingCategorySwitch
onToggle={desireTrackingCategoryToggle}
text={SYMPTOMS[5]}
text={t(SYMPTOMS[5])}
value={isDesireTrackingCategoryEnabled}
trackColor={{ true: Colors.turquoiseDark }}
symptom={SYMPTOMS[5]}
/>
<AppSwitch
<TrackingCategorySwitch
onToggle={painTrackingCategoryToggle}
text={SYMPTOMS[6]}
text={t(SYMPTOMS[6])}
value={isPainTrackingCategoryEnabled}
trackColor={{ true: Colors.turquoiseDark }}
symptom={SYMPTOMS[6]}
/>
<AppSwitch
<TrackingCategorySwitch
onToggle={moodTrackingCategoryToggle}
text={SYMPTOMS[7]}
text={t(SYMPTOMS[7])}
value={isMoodTrackingCategoryEnabled}
trackColor={{ true: Colors.turquoiseDark }}
symptom={SYMPTOMS[7]}
/>
<AppSwitch
<TrackingCategorySwitch
onToggle={noteTrackingCategoryToggle}
text={SYMPTOMS[8]}
text={t(SYMPTOMS[8])}
value={isNoteTrackingCategoryEnabled}
trackColor={{ true: Colors.turquoiseDark }}
symptom={SYMPTOMS[8]}
/>
</Segment>
<Segment title={'Fertility feature'}>
<Pressable onPress={fertilityDisabledPrompt}>
<Segment title={labels.fertilityTracking.title}>
<AppText>{labels.fertilityTracking.message}</AppText>
<AppSwitch
onToggle={toggleSwitch}
text={'If turned on ...'}
value={isEnabled}
trackColor={{ true: Colors.turquoiseDark }}
onToggle={fertilityTrackingToggle}
text={fertilityTrackingText}
value={isFertilityTrackingEnabled}
disabled={!manageFertilityFeature}
/>
</Segment>
<Pressable onPress={sliderDisabledPrompt}>
<Segment title={labels.tempScale.segmentTitle}>
{isTemperatureTrackingCategoryEnabled && (
<>
<AppText>{labels.tempScale.segmentExplainer}</AppText>
<TemperatureSlider />
</>
)}
{!isTemperatureTrackingCategoryEnabled && (
<AppText>{labels.disabled.message}</AppText>
)}
</Segment>
</Pressable>
<Pressable onPress={sliderDisabledPrompt}>
<Segment title={labels.useCervix.title}>
{isTemperatureTrackingCategoryEnabled && (
<AppSwitch
onToggle={onCervixToggle}
text={cervixText}
value={shouldUseCervix}
trackColor={{ true: Colors.turquoiseDark }}
/>
)}
{!isTemperatureTrackingCategoryEnabled && (
<AppText>{labels.disabled.message}</AppText>
)}
</Segment>
</Pressable>
<Segment title={labels.periodPrediction.title} last>
<Segment title={labels.periodPrediction.title}>
<AppSwitch
onToggle={onPeriodPredictionToggle}
text={periodPredictionText}
value={isPeriodPredictionEnabled}
trackColor={{ true: Colors.turquoiseDark }}
/>
</Segment>
<Segment
subheader={labels.customization.subheaderSymptoThermalMethod}
last
></Segment>
<Pressable onPress={sliderDisabledPrompt}>
<Segment title={labels.tempScale.segmentTitle}>
<AppText>{labels.tempScale.segmentExplainer}</AppText>
<TemperatureSlider disabled={!isTemperatureTrackingCategoryEnabled} />
</Segment>
</Pressable>
<Pressable onPress={secondarySymptomDisabledPrompt}>
<Segment title={labels.secondarySymptom.title}>
<AppText>{cervixText}</AppText>
<SelectTabGroup
activeButton={useCervixAsSecondarySymptom}
buttons={secondarySymptomButtons}
onSelect={(value) => onSelectTab(value)}
disabled={!isFertilityTrackingEnabled}
/>
</Segment>
</Pressable>
<Segment last>
<View style={styles.line}>
<AppIcon
color={Colors.purple}
name="info-with-circle"
style={styles.icon}
/>
<AppText style={styles.title}>{labels.preOvu.title}</AppText>
</View>
<AppText>{labels.preOvu.note}</AppText>
</Segment>
</AppPage>
)
}
export default Settings
const styles = StyleSheet.create({
icon: {
marginRight: Spacing.base,
},
line: {
flexDirection: 'row',
alignItems: 'center',
},
title: {
...Typography.subtitle,
},
})
@@ -1,5 +1,6 @@
import React, { useState } from 'react'
import { StyleSheet, View } from 'react-native'
import PropTypes from 'prop-types'
import Slider from '@ptomasroos/react-native-multi-slider'
import alertError from '../common/alert-error'
@@ -10,7 +11,7 @@ import { Colors, Sizes } from '../../../styles'
import labels from '../../../i18n/en/settings'
import { TEMP_MIN, TEMP_MAX, TEMP_SLIDER_STEP } from '../../../config'
const TemperatureSlider = () => {
const TemperatureSlider = ({ disabled }) => {
const savedValue = scaleObservable.value
const [minTemperature, setMinTemperature] = useState(savedValue.min)
const [maxTemperature, setMaxTemperature] = useState(savedValue.max)
@@ -25,6 +26,14 @@ const TemperatureSlider = () => {
}
}
const sliderAccentBackground = disabled
? styles.disabledSliderAccentBackground
: styles.sliderAccentBackground
const sliderBackground = disabled
? styles.disabledSliderBackground
: styles.sliderBackground
return (
<View style={styles.container}>
<Slider
@@ -35,11 +44,13 @@ const TemperatureSlider = () => {
max={TEMP_MAX}
min={TEMP_MIN}
onValuesChange={onTemperatureSliderChange}
selectedStyle={styles.sliderAccentBackground}
step={TEMP_SLIDER_STEP}
trackStyle={styles.slider}
unselectedStyle={styles.sliderBackground}
values={[minTemperature, maxTemperature]}
enabledOne={!disabled}
enabledTwo={!disabled}
selectedStyle={sliderAccentBackground}
unselectedStyle={sliderBackground}
/>
</View>
)
@@ -47,6 +58,10 @@ const TemperatureSlider = () => {
export default TemperatureSlider
TemperatureSlider.propTypes = {
disabled: PropTypes.bool,
}
const styles = StyleSheet.create({
container: {
alignItems: 'center',
@@ -54,6 +69,7 @@ const styles = StyleSheet.create({
},
marker: {
backgroundColor: Colors.turquoiseDark,
borderRadius: 50,
elevation: 4,
height: Sizes.subtitle,
@@ -66,7 +82,13 @@ const styles = StyleSheet.create({
sliderAccentBackground: {
backgroundColor: Colors.turquoiseDark,
},
disabledSliderAccentBackground: {
backgroundColor: Colors.grey,
},
sliderBackground: {
backgroundColor: Colors.turquoise,
},
disabledSliderBackground: {
backgroundColor: Colors.greyLight,
},
})
-1
View File
@@ -11,7 +11,6 @@ const menuItems = [
{ label: 'reminders', componentName: 'Reminders' },
{ label: 'dataManagement', componentName: 'DataManagement' },
{ label: 'password', componentName: 'Password' },
{ label: 'info', componentName: 'Info' },
]
const SettingsMenu = ({ navigate }) => {
+5 -7
View File
@@ -7,8 +7,7 @@
"chart": {
"tutorial": "You can swipe the chart to view more dates."
},
"cycleDay": {
"symptomBox": {
"symptoms": {
"bleeding": "bleeding",
"temperature": "temperature",
"mucus": "cervical mucus",
@@ -18,7 +17,6 @@
"sex": "sex",
"pain": "pain",
"mood": "mood"
}
},
"labels": {
"bleedingPrediction": {
@@ -114,19 +112,19 @@
"menuItem": {
"dataManagement": {
"name": "Data",
"text": "import, export or delete your data"
"text": "Import, export or delete your data"
},
"customization": {
"name": "Customization",
"text": "define how you want to use drip"
"text": "Define how you want to use drip"
},
"password": {
"name": "Password",
"text": "set or edit your password"
"text": "Set, edit or delete your password"
},
"reminders": {
"name": "Reminders",
"text": "turn on/off reminders"
"text": "Turn on/off reminders"
},
"info": {
"name": "Info",
+44 -38
View File
@@ -4,57 +4,58 @@ export const intensity = ['low', 'medium', 'high']
export const bleeding = {
labels: ['spotting', 'light', 'medium', 'heavy'],
heaviness: {
header: "Heaviness",
explainer: "How heavy is the bleeding?",
header: 'Heaviness',
explainer: 'How heavy is the bleeding?',
},
exclude: {
header: "Exclude",
explainer: "You can exclude this value if it's not menstrual bleeding"
}
header: 'Exclude',
explainer: "You can exclude this value if it's not menstrual bleeding",
},
}
export const cervix = {
subcategories: {
opening: 'opening',
firmness: 'firmness',
position: 'position'
position: 'position',
},
opening: {
categories: ['closed', 'medium', 'open'],
explainer: 'Is your cervix open or closed?'
explainer: 'Is your cervix open or closed?',
},
firmness: {
categories: ['hard', 'soft'],
explainer: "When it's hard, it might feel like the tip of your nose"
explainer: "When it's hard, it might feel like the tip of your nose",
},
position: {
categories: ['low', 'medium', 'high'],
explainer: 'How high up in the vagina is the cervix?'
explainer: 'How high up in the vagina is the cervix?',
},
excludeExplainer: "You can exclude this value if you don't want to use it for fertility detection.",
actionHint: 'Choose values for at least "Opening" and "Firmness" to save.'
excludeExplainer:
"You can exclude this value if you don't want to use it for fertility detection.",
}
export const mucus = {
subcategories: {
feeling: 'feeling',
texture: 'texture'
texture: 'texture',
},
feeling: {
categories: ['dry', 'nothing', 'wet', 'slippery'],
explainer: 'What does your vaginal entrance feel like?'
explainer: 'What does your vaginal entrance feel like?',
},
texture: {
categories: ['nothing', 'creamy', 'egg white'],
explainer: "Looking at and touching your cervical mucus, which describes it best?"
explainer:
'Looking at and touching your cervical mucus, which describes it best?',
},
excludeExplainer: "You can exclude this value if you don't want to use it for fertility detection",
actionHint: 'Choose values for both "Feeling" and "Texture" to save.'
excludeExplainer:
"You can exclude this value if you don't want to use it for fertility detection",
}
export const desire = {
header: 'Intensity',
explainer: 'How would you rate your sexual desire?'
explainer: 'How would you rate your sexual desire?',
}
export const sex = {
@@ -62,7 +63,7 @@ export const sex = {
solo: 'solo',
partner: 'partner',
},
header: "Activity",
header: 'Activity',
explainer: 'Were you sexually active today?',
}
@@ -78,8 +79,8 @@ export const contraceptives = {
none: 'none',
other: 'other',
},
header: "Contraceptives",
explainer: 'Did you use contraceptives?'
header: 'Contraceptives',
explainer: 'Did you use contraceptives?',
}
export const pain = {
@@ -91,9 +92,9 @@ export const pain = {
nausea: 'nausea',
tenderBreasts: 'tender breasts',
migraine: 'migraine',
other: 'other'
other: 'other',
},
explainer: 'How did your body feel today?'
explainer: 'How did your body feel today?',
}
export const mood = {
@@ -107,34 +108,39 @@ export const mood = {
energetic: 'energetic',
fatigue: 'fatigue',
angry: 'angry',
other: 'other'
other: 'other',
},
explainer: 'How did you feel today?'
explainer: 'How did you feel today?',
}
export const temperature = {
outOfRangeWarning: 'This temperature value is out of the current range for the temperature chart. You can change the range in the settings.',
outOfAbsoluteRangeWarning: 'This temperature value is too high or low to be shown on the temperature chart.',
outOfRangeWarning:
'This temperature value is out of the current range for the temperature chart. You can change the range in the settings.',
outOfAbsoluteRangeWarning:
'This temperature value is too high or low to be shown on the temperature chart.',
temperature: {
header: "Temperature",
explainer: 'Take your temperature right after waking up, before getting out of bed'
header: 'Temperature',
explainer:
'Take your temperature right after waking up, before getting out of bed',
},
time: "Time",
time: 'Time',
note: {
header: "Note",
explainer: 'Is there anything that could have influenced this value, such as bad sleep or alcohol consumption?'
header: 'Note',
explainer:
'Is there anything that could have influenced this value, such as bad sleep or alcohol consumption?',
},
exclude: {
header: "Exclude",
explainer: "You can exclude this value if you don't want to use it for fertility detection"
}
header: 'Exclude',
explainer:
"You can exclude this value if you don't want to use it for fertility detection",
},
}
export const noteExplainer = "Anything you want to add for the day?"
export const noteExplainer = 'Anything you want to add for the day?'
export const general = {
cycleDayNumber: "Cycle day ",
today: "Today"
cycleDayNumber: 'Cycle day ',
today: 'Today',
}
export const sharedDialogs = {
@@ -144,5 +150,5 @@ export const sharedDialogs = {
reallyDeleteData: 'Yes, I am sure',
save: 'Save',
delete: 'Delete',
disabledInfo: 'There is some data missing'
disabledInfo: 'There is some data missing',
}
+35 -12
View File
@@ -1,6 +1,11 @@
import links from './links'
export default {
customization: {
title: 'Customization',
trackingCategories: 'Tracking categories',
subheaderSymptoThermalMethod: 'Sympto-thermal method settings',
},
export: {
errors: {
noData: 'There is no data to export',
@@ -32,17 +37,16 @@ export default {
tempScale: {
segmentTitle: 'Temperature scale',
segmentExplainer:
'Change the minimum and maximum value for the temperature chart',
'Change the minimum and maximum value for the temperature chart.',
min: 'Min',
max: 'Max',
loadError: 'Could not load saved temperature scale settings',
saveError: 'Could not save temperature scale settings',
disabled: 'Disabled',
disabledMessage:
'To use the temperature scale please first enable temperature tracking above.',
},
disabled: {
title: 'This feature is turned off',
message:
'Please first enable the temperature tracking category in the customization settings.',
},
tempReminder: {
title: 'Temperature reminder',
noTimeSet: 'Set a time for a daily reminder to take your temperature',
@@ -66,17 +70,36 @@ export default {
'To use the period reminder please first enable period predictions in the customization settings.',
},
},
useCervix: {
fertilityTracking: {
title: 'Fertility phases calculation',
disabledTitle: 'Disabled',
disabled:
'To use fertility phases calculation please enable both temperature tracking and either cervical mucus or cervix tracking above.',
message:
'If you enter menstrual bleeding, temperature and cervical mucus or cervix data according to the sympto-thermal method, drip will calculate cycle phases with the provided data.',
on: 'If you switch this off, drip will not show fertility related information.',
off: 'If you switch this on, drip will show fertility related information.',
},
secondarySymptom: {
title: 'Secondary symptom',
cervixModeOn:
'Cervix values are being used for symptothermal fertility detection. You can switch here to use cervical mucus values for symptothermal fertility detection',
'Cervix values are being used for fertility detection according to the sympto-thermal method.',
cervixModeOff:
'By default, cervical mucus values are being used for symptothermal fertility detection. You can switch here to use cervix values for symptothermal fertility detection',
'Cervical mucus values are being used for fertility detection according to the sympto-thermal method.',
disabled: {
title: 'Disabled',
message:
'To set a secondary symptom please first enable the cervical mucus or cervix tracking category as well as temperature and fertility phases calculation above.',
noSecondaryEnabled:
'To switch the secondary symptom both cervical mucus and cervix need to be enabled above.',
},
mucus: 'cervical mucus',
cervix: 'cervix',
},
periodPrediction: {
title: 'Period predictions',
on: 'drip predicts your 3 next menstrual bleedings based on the statistics of your previously tracked cycles, min 3 complete cycles.',
off: 'There are no predictions for menstrual cycles displayed. If turned on the calendar and the home screen will display period predictions.',
on: 'drip predicts your 3 next menstrual bleedings based on statistics if you previously tracked at least 3 complete cycles.',
off: 'There are no predictions for menstrual cycles displayed. If turned on, the calendar and the home screen will display period predictions.',
},
passwordSettings: {
title: 'App password',
@@ -119,6 +142,6 @@ Making any changes to your password setting will keep your data as it was before
},
preOvu: {
title: 'Infertile days at cycle start',
note: `drip. applies the sympto-thermal method for calculating infertile days at the start of the cycle (see ${links.wiki.url} for more info). However, drip. does not currently apply the so called 20-day-rule, which determines infertile days at the cycle start from past cycle lengths in case no past symptothermal info is available.`,
note: `drip. applies the sympto-thermal method for calculating infertile days at the start of the cycle (see ${links.wiki.url} for more info). However, drip. does not currently apply the so called 20-day-rule, which determines infertile days at the cycle start from past cycle lengths in case no past sympto-thermal info is available.`,
},
}
+2 -2
View File
@@ -25,7 +25,7 @@ export default {
After tracking at least 3 menstrual cycles, drip. will give you an overview of
· how long your cycles last on average (in "stats"),
· whether the length of your cycles varied significantly (in "stats" and in bleeding predictions)
· and predict your next 3 cycles with a range of 3 or 5 days (on home screen and "calendar").
· and predict your next 3 cycles with a range of 3 or 5 days (on home screen and "calendar") if this functionality is enabled in the customization settings.
The app allows you to track different intensities of bleeding. On the chart and on the calendar, bleeding values are colored in different shades of red. The darker, the more intense your bleeding. Every bleeding value that is not excluded is taken into account for fertility calculation and period predictions.
@@ -74,7 +74,7 @@ ${generalInfo.curiousNfp}`,
title: 'Tracking cervical mucus',
text: `Cervical mucus can help determine in which phase of the menstrual cycle you are.
By default the secondary symptom the app uses for the sympto-thermal method is cervical mucus.
By default the secondary symptom the app uses for the sympto-thermal method is cervical mucus. You can change this in the customization settings.
· How to identify fertile cervical mucus?
Tracking the feeling and the texture of your cervical mucus on a daily basis helps you identify changes of the quality of the cervical mucus. The values you enter for both feeling and texture of your cervical mucus are combined by drip. into one of five values following the sympto-thermal method.
+1 -1
View File
@@ -19,7 +19,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.2401.17</string>
<string>1.2403.19</string>
<key>CFBundleSignature</key>
<string>????</string>
<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
const feelingMapping = {
+12 -2
View File
@@ -1,9 +1,15 @@
import getFertilityStatus from 'sympto'
import cycleModule from './cycle'
import { useCervixObservable } from '../local-storage'
import { fertilityTrackingObservable, useCervixAsSecondarySymptomObservable } from '../local-storage'
import { fertilityStatus as labels } from '../i18n/en/labels'
const isFertilityTrackingEnabled = fertilityTrackingObservable.value
export function getFertilityStatusForDay(dateString) {
if (!isFertilityTrackingEnabled) {
return
}
const status = getCycleStatusForDay(dateString)
if (!status) return {
status: labels.fertile,
@@ -34,6 +40,10 @@ export function getFertilityStatusForDay(dateString) {
}
export function getCycleStatusForDay(dateString, opts = {}) {
if (!isFertilityTrackingEnabled) {
return
}
const {
getCycleForDay,
getCyclesBefore,
@@ -57,7 +67,7 @@ export function getCycleStatusForDay(dateString, opts = {}) {
}
}
cycleInfo.secondarySymptom = useCervixObservable.value ? 'cervix' : 'mucus'
cycleInfo.secondarySymptom = useCervixAsSecondarySymptomObservable.value ? 'cervix' : 'mucus'
return getFertilityStatus(cycleInfo)
}
+39 -7
View File
@@ -59,12 +59,19 @@ export async function savePeriodPrediction(bool) {
}
}
export const useCervixObservable = Observable()
setObvWithInitValue('useCervix', useCervixObservable, false)
export const useCervixAsSecondarySymptomObservable = Observable()
setObvWithInitValue(
'useCervixAsSecondarySymptom',
useCervixAsSecondarySymptomObservable,
0
)
export async function saveUseCervix(bool) {
await AsyncStorage.setItem('useCervix', JSON.stringify(bool))
useCervixObservable.set(bool)
export async function saveUseCervixAsSecondarySymptom(value) {
await AsyncStorage.setItem(
'useCervixAsSecondarySymptom',
JSON.stringify(value)
)
useCervixAsSecondarySymptomObservable.set(value)
}
export const hasEncryptionObservable = Observable()
@@ -100,13 +107,30 @@ export async function saveTemperatureTrackingCategory(bool) {
temperatureTrackingCategoryObservable.set(bool)
if (!temperatureTrackingCategoryObservable.value) {
const result = await AsyncStorage.getItem('tempReminder')
if (JSON.parse(result).enabled) {
// if temperature tracking is turned off, the temperature reminder gets disabled
const tempReminderResult = await AsyncStorage.getItem('tempReminder')
if (tempReminderResult && JSON.parse(tempReminderResult).enabled) {
tempReminderObservable.set(false)
}
}
}
export const mucusTrackingCategoryObservable = Observable()
setObvWithInitValue('mucus', mucusTrackingCategoryObservable, true)
export async function saveMucusTrackingCategory(bool) {
await AsyncStorage.setItem('mucus', JSON.stringify(bool))
mucusTrackingCategoryObservable.set(bool)
}
export const cervixTrackingCategoryObservable = Observable()
setObvWithInitValue('cervix', cervixTrackingCategoryObservable, true)
export async function saveCervixTrackingCategory(bool) {
await AsyncStorage.setItem('cervix', JSON.stringify(bool))
cervixTrackingCategoryObservable.set(bool)
}
export const sexTrackingCategoryObservable = Observable()
setObvWithInitValue('sex', sexTrackingCategoryObservable, true)
@@ -147,6 +171,14 @@ export async function saveNoteTrackingCategory(bool) {
noteTrackingCategoryObservable.set(bool)
}
export const fertilityTrackingObservable = Observable()
setObvWithInitValue('fertilityTracking', fertilityTrackingObservable, true)
export async function saveFertilityTrackingEnabled(bool) {
await AsyncStorage.setItem('fertilityTracking', JSON.stringify(bool))
fertilityTrackingObservable.set(bool)
}
async function setObvWithInitValue(key, obv, defaultValue) {
const result = await AsyncStorage.getItem(key)
let value
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "drip.",
"version": "1.2401.17",
"version": "1.2403.19",
"contributors": [
"Julia Friesel <julia.friesel@gmail.com>",
"Marie Kochsiek",
@@ -12,8 +12,8 @@
"scripts": {
"start": "react-native start",
"android": "react-native run-android",
"ios": "react-native run-ios --simulator=\"iPhone 15\"",
"iosSE": "react-native run-ios --simulator=\"iPhone SE (2nd generation)\"",
"ios": "react-native run-ios --simulator=\"iPhone 8 Plus\"",
"ios15": "react-native run-ios --simulator=\"iPhone 15 Plus\"",
"log": "react-native log-android",
"test": "jest test && yarn lint",
"test-watch": "jest --watch test",
+5 -1
View File
@@ -18,6 +18,7 @@ const shadesOfPink = ['#c485a6', '#b15c89', pinkColor] // light to dark
const lightGreenColor = '#bccd67'
const orangeColor = '#bc6642'
const mintColor = '#6ca299'
const turquoiseDark = '#69CBC1'
export default {
greyDark: '#555',
@@ -27,7 +28,7 @@ export default {
orange: '#F38337',
purple: '#3A2671',
purpleLight: '#938EB2',
turquoiseDark: '#69CBC1',
turquoiseDark: turquoiseDark,
turquoise: '#CFECEA',
turquoiseLight: '#E9F2ED',
iconColors: {
@@ -35,6 +36,9 @@ export default {
color: redColor,
shades: shadesOfRed,
},
temperature: {
color: turquoiseDark,
},
mucus: {
color: violetColor,
shades: shadesOfViolet,
+2 -1
View File
@@ -1,10 +1,11 @@
import { scale } from 'react-native-size-matters'
export default {
zero: '0%',
tiny: scale(4),
small: scale(10),
base: scale(16),
large: scale(20),
symptomTileWidth: '48%',
textWidth: '70%'
textWidth: '70%',
}
+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)
})
})
})