Merge branch 'master' into link-nfp-rules
This commit is contained in:
+9
-9
@@ -1,4 +1,4 @@
|
|||||||
# Contributing to drip
|
# Contributing to drip aka CONDRIBUTING
|
||||||
So good to see you here, hello :wave\_tone1: :wave\_tone2: :wave\_tone3: :wave\_tone4: :wave\_tone5:
|
So good to see you here, hello :wave\_tone1: :wave\_tone2: :wave\_tone3: :wave\_tone4: :wave\_tone5:
|
||||||
|
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ So good to see you here, hello :wave\_tone1: :wave\_tone2: :wave\_tone3: :wave\_
|
|||||||
|
|
||||||
[What should I know before I get started?](#what-should-i-know-before-i-get-started)
|
[What should I know before I get started?](#what-should-i-know-before-i-get-started)
|
||||||
|
|
||||||
[How can I contribute?](#how-can-i-contribute)
|
[How can I condribute?](#how-can-i-condribute)
|
||||||
|
|
||||||
[Thank you](#thank-you)
|
[Thank you](#thank-you)
|
||||||
|
|
||||||
@@ -23,9 +23,9 @@ We have prepared something for **you**: check out our [README](https://gitlab.co
|
|||||||
|
|
||||||
Let us know if you want to suggest improvements for the README and open a merge request (which is just like Github's pull request)
|
Let us know if you want to suggest improvements for the README and open a merge request (which is just like Github's pull request)
|
||||||
|
|
||||||
## How can I contribute?
|
## How can I condribute?
|
||||||
|
|
||||||
### Your First Code Contribution
|
### Your First Code Condribution
|
||||||
|
|
||||||
We are fans of labels, at least for our issues. You can find a list of `newbie` issues [here](https://gitlab.com/bloodyhealth/drip/issues?label_name%5B%5D=Newbie).
|
We are fans of labels, at least for our issues. You can find a list of `newbie` issues [here](https://gitlab.com/bloodyhealth/drip/issues?label_name%5B%5D=Newbie).
|
||||||
If you decide to work on an issue, please click on `Create branch` based on that issue. You can find this as a dropdown option right under `Create merge request`.
|
If you decide to work on an issue, please click on `Create branch` based on that issue. You can find this as a dropdown option right under `Create merge request`.
|
||||||
@@ -40,19 +40,19 @@ You found a bug :bug:? Please feel free to investigate and save logs for more sp
|
|||||||
|
|
||||||
Before creating a new issue, please review the [list of existing issues](https://gitlab.com/bloodyhealth/drip/issues) to make sure the bug is unknown. You are invited to describe the bug in all its details e.g. in what context/settings has it occurred?
|
Before creating a new issue, please review the [list of existing issues](https://gitlab.com/bloodyhealth/drip/issues) to make sure the bug is unknown. You are invited to describe the bug in all its details e.g. in what context/settings has it occurred?
|
||||||
|
|
||||||
To send us a new issue you can also use our gitlab email: incoming+bloodyhealth/drip@incoming.gitlab.com. It will automagically add a new issue to the list with a description text taken from the body of your email.
|
To send us a new issue you can also use our [gitlab email](mailto:incoming+bloodyhealth/drip@incoming.gitlab.com). It will automagically add a new issue to the list with a description text taken from the body of your email.
|
||||||
|
|
||||||
### Suggesting Enhancements
|
### Suggesting Enhancements
|
||||||
|
|
||||||
Do you have suggestions for enhancing the app or for cleaning up some code? Fantastic!
|
Do you have suggestions for enhancing the app or for cleaning up some code? Fantastic!
|
||||||
|
|
||||||
Before creating a new issue, please review the [list of existing issues](https://gitlab.com/bloodyhealth/drip/issues) to make sure nobody else had the same idea before you! You are then invited to open a new issue with a somewhat extensive description, you can use emojis or GIFs if it helps :)!
|
Before creating a new issue, please review the [list of existing issues](https://gitlab.com/bloodyhealth/drip/issues) to make sure nobody else had the same idea before you! You are then invited to open a new issue with a somewhat extensive description, you can use emojis or GIFs if it helps :)!
|
||||||
|
|
||||||
To send us a new issue you can also use our [gitlab email](mailto:incoming+bloodyhealth/drip@incoming.gitlab.com). It will automagically add a new issue to the list with the title taken from the subject line and the description text for the issue taken from the body of your email.
|
To send us a new issue you can also use our [gitlab email](mailto:incoming+bloodyhealth/drip@incoming.gitlab.com). It will automagically add a new issue to the list with the title taken from the subject line and the description text for the issue taken from the body of your email.
|
||||||
|
|
||||||
### Thank you
|
### Thank you
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Thank you for contributing to open source, thank you for contributing to drip!
|
Thank you for condributing to open source, thank you for condributing to drip!
|
||||||
Much love from Bloody Health :heart\_exclamation:
|
Much love from Bloody Health :heart\_exclamation:
|
||||||
|
|||||||
@@ -1,74 +1,106 @@
|
|||||||
# Bloody Health Cycle Tracker
|
# drip, the open-source cycle tracking app
|
||||||
|
|
||||||
A menstrual cycle tracking app that's open-source and leaves your data on your phone. Use it to track your menstrual cycle and/or for fertility awareness!
|
A menstrual cycle tracking app that's open-source and leaves your data on your phone. Use it to track your menstrual cycle and/or for fertility awareness!
|
||||||
|
Find more information on [our website](https://bloodyhealth.gitlab.io/).
|
||||||
|
|
||||||
|
The app is build in React Native and currently developed for Android.
|
||||||
|
|
||||||
|
Here --> you will find our [contributing guide](https://gitlab.com/bloodyhealth/drip/blob/master/CONTRIBUTING.md).
|
||||||
|
|
||||||
## Development setup
|
## Development setup
|
||||||
1. Install [Android Studio](https://developer.android.com/studio/) - you'll need it to install some dependencies.
|
|
||||||
|
|
||||||
1. Make sure you are running Node 8 (newer versions won’t work). It's easiest to switch Node versions using `nvm`, here’s how to do it:
|
1. Install [Android Studio](https://developer.android.com/studio/) - you'll need it to install some dependencies.
|
||||||
|
|
||||||
|
1. Make sure you are running Node 8 (newer versions won’t work). It's easiest to switch Node versions using `nvm`, here’s how to do it:
|
||||||
|
|
||||||
```
|
```
|
||||||
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.0/install.sh | bash
|
$ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.0/install.sh | bash
|
||||||
nvm install v8
|
$ nvm install v8
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Clone this repository:
|
1. Clone this repository:
|
||||||
|
|
||||||
```
|
```
|
||||||
git clone git@gitlab.com:bloodyhealth/drip.git
|
$ git clone git@gitlab.com:bloodyhealth/drip.git
|
||||||
cd drip
|
$ cd drip
|
||||||
```
|
```
|
||||||
and run
|
and run
|
||||||
```
|
```
|
||||||
npm install
|
$ npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Open Android Studio and click on "Open an existing Android Studio project". Navigate to the drip repository you cloned and double click the android folder. It detects, downloads and cofigures requirements that might be missing, like the NDK and CMake to build the native code part of the project. Also see the [nodejs-mobile repository](https://github.com/janeasystems/nodejs-mobile) for the necessary prerequisites for your system.
|
1. Open Android Studio and click on "Open an existing Android Studio project". Navigate to the drip repository you cloned and double click the android folder. It detects, downloads and cofigures requirements that might be missing, like the NDK and CMake to build the native code part of the project. Also see the [nodejs-mobile repository](https://github.com/janeasystems/nodejs-mobile) for the necessary prerequisites for your system.
|
||||||
|
|
||||||
1. Either start a [virtual device in Android Studio](https://developer.android.com/studio/run/emulator) or [set your physical device like your Android phone up](https://developer.android.com/training/basics/firstapp/running-app) to run the app.
|
1. Either start a [virtual device in Android Studio](https://developer.android.com/studio/run/emulator) or [set your physical device like your Android phone up](https://developer.android.com/training/basics/firstapp/running-app) to run the app.
|
||||||
|
|
||||||
1. Open a terminal and run `npm run android`
|
1. Open a terminal and run
|
||||||
|
```
|
||||||
|
$ npm run android
|
||||||
|
```
|
||||||
|
|
||||||
1. In another tab, run `npm run log` to see logging output
|
1. To see logging output, run the following command in another tab:
|
||||||
|
```
|
||||||
|
$ npm run log
|
||||||
|
```
|
||||||
|
|
||||||
1. Run `adb shell input keyevent 82` and select enable hot reloading (see https://facebook.github.io/react-native/docs/debugging.html)
|
1. Run the following command and select enable hot reloading (see https://facebook.github.io/react-native/docs/debugging.html):
|
||||||
|
```
|
||||||
|
$ adb shell input keyevent 82
|
||||||
|
```
|
||||||
|
|
||||||
1. We recommend installing an [ESLint plugin in your editor](https://eslint.org/docs/user-guide/integrations#editors). There's an `.eslintrc` file in this project which will be used by the plugin to check your code for style errors and potential bugs.
|
1. We recommend installing an [ESLint plugin in your editor](https://eslint.org/docs/user-guide/integrations#editors). There's an `.eslintrc` file in this project which will be used by the plugin to check your code for style errors and potential bugs.
|
||||||
|
|
||||||
## Troubleshooting
|
### Troubleshooting
|
||||||
### [MacOS] Java problems
|
#### [MacOS] Java problems
|
||||||
|
|
||||||
Make sure that you have Java 1.8 by running `java -version`.
|
Make sure that you have Java 1.8 by running `java -version`.
|
||||||
|
|
||||||
If you don't have Java installed, or your Java version is different, the app may not work. You can try just using Android Studio's Java by prepending it to your `$PATH` in your shell profile:
|
If you don't have Java installed, or your Java version is different, the app may not work. You can try just using Android Studio's Java by prepending it to your `$PATH` in your shell profile:
|
||||||
|
|
||||||
```
|
```
|
||||||
export PATH="/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin:${PATH}"
|
$ export PATH="/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin:${PATH}"
|
||||||
```
|
```
|
||||||
|
|
||||||
Now, `which java` should output `/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java`, and the correct Java version should be used.
|
Now, `which java` should output `/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java`, and the correct Java version should be used.
|
||||||
|
|
||||||
### [MacOS] Ninja
|
#### [MacOS] Ninja
|
||||||
If `npm` says `CMake was unable to find a build program corresponding to "Ninja".`:
|
If `npm` says `CMake was unable to find a build program corresponding to "Ninja".`:
|
||||||
```
|
```
|
||||||
brew install ninja
|
$ brew install ninja
|
||||||
```
|
```
|
||||||
|
|
||||||
### [MacOS] adb not on the path
|
### [MacOS] adb not on the path
|
||||||
If you get error messages about `adb` not being found on your path:
|
If you get error messages about `adb` not being found on your path:
|
||||||
```
|
```
|
||||||
ln -s ~/Library/Android/sdk/platform-tools/adb /usr/local/bin/adb
|
$ ln -s ~/Library/Android/sdk/platform-tools/adb /usr/local/bin/adb
|
||||||
```
|
```
|
||||||
|
|
||||||
## Windows problems
|
### [Windows 10] react native problems
|
||||||
|
|
||||||
Unfortunately, the react native version we use doesn't work on Windows 10 it seems, find [more info here](https://github.com/facebook/react-native/issues/20015).
|
Unfortunately, the react native version we use doesn't work on Windows 10 it seems, find [more info here](https://github.com/facebook/react-native/issues/20015).
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
You can run the tests with `npm test`.
|
You can run the tests with:
|
||||||
|
```
|
||||||
|
$ npm test
|
||||||
|
```
|
||||||
|
|
||||||
## Debugging
|
## Debugging
|
||||||
In order to see logging output from the app, run `npm run log` in a separate terminal.
|
In order to see logging output from the app, run `npm run log` in a separate terminal. You can output specific code you want to see, with:
|
||||||
|
`console.log(theVariableIWantToSeeHere)`
|
||||||
|
or just a random string to check if this piece of code is actually running:
|
||||||
|
`console.log("HELLO")`.
|
||||||
|
|
||||||
## NFP rules
|
## NFP rules
|
||||||
More information about how the app calculates fertility status and bleeding predictions in the [wiki on Gitlab](https://gitlab.com/bloodyhealth/drip/wikis/home)
|
More information about how the app calculates fertility status and bleeding predictions in the [wiki on Gitlab](https://gitlab.com/bloodyhealth/drip/wikis/home)
|
||||||
|
|
||||||
|
## Adding a new tracking icon
|
||||||
|
|
||||||
|
1. We use [fontello](http://fontello.com/) to create icon fonts for us. You need to upload the complete set of tracking icons (bleeding, mucus, ...) including the new icon you wish to add, all in SVG.
|
||||||
|
2. Download webfont from fontello
|
||||||
|
3. Copy both the content of `config.json` and `font.tff` into `assets/fonts`, replacing it with the current content of `config-drip-icon-font.json` and `drip-icon-font.tff`.
|
||||||
|
4. Now run the following command in your console:
|
||||||
|
```
|
||||||
|
$ react-native link
|
||||||
|
```
|
||||||
|
5. You should be able to use the icon now within drip, e.g. in Cycle Day Overview and on the chart.
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -7,21 +7,7 @@
|
|||||||
"ascent": 850,
|
"ascent": 850,
|
||||||
"glyphs": [
|
"glyphs": [
|
||||||
{
|
{
|
||||||
"uid": "1997bdc6b7d535061226f74d34c92481",
|
"uid": "4f484b20e814e8731840cd67d0f8ba3d",
|
||||||
"css": "drip-icon-sex",
|
|
||||||
"code": 59398,
|
|
||||||
"src": "custom_icons",
|
|
||||||
"selected": true,
|
|
||||||
"svg": {
|
|
||||||
"path": "M559.7 683.7C536.5 702.7 513.1 721.5 489.6 740.1 497.1 739.1 504.7 738.1 512.3 737.1 444.8 690.1 380.4 638.6 320.2 582.6 245.1 512.6 171.9 432.9 130.6 337.6 96.7 259.5 97.9 160.9 173.4 107.5 210.3 81.4 255.6 72.8 300 77.5 354.6 83.1 404.3 113.5 442.5 151.6 459.6 168.7 486.1 142.2 469 125.1 391.2 47.5 272.2 10.5 170.8 65 128 88.1 96.3 127.5 80.8 173.3 65.6 218 69.2 267.6 82 312.3 110.3 411.7 181.6 496.8 252.6 569.1 317.7 635.5 390.1 694.9 465.3 749.5 474.6 756.3 483.9 762.9 493.3 769.5 500.8 774.7 509.8 771.5 516.1 766.6 539.6 748 563 729.3 586.2 710.2 594.1 703.7 592.9 690.4 586.2 683.7 578.4 676 567.6 677.3 559.7 683.7L559.7 683.7ZM475.8 246.5C522 168.5 601.4 102.7 689.7 79.2 727.7 69.1 769.1 68.9 804.7 86.9 846.9 108.3 874.8 153 886.7 197.7 901 251.6 883.3 308.5 859 356.7 828.6 416.7 785.1 469.6 739.4 518.5 723 536.1 749.4 562.7 766 545 815.7 491.8 862.6 434 895 368.4 922.6 312.5 937.9 249.5 922.8 187.7 910.7 138.1 879.1 90.5 836.5 62.1 800.5 38.1 755.2 31.3 712.9 36.8 602 51.2 499.4 133.1 443.4 227.5 431 248.4 463.4 267.2 475.8 246.5L475.8 246.5ZM617.7 965.1C647.4 964 672.2 945.2 679.8 916.2 682.6 905.3 681.5 892.8 681.5 881.7 681.5 844.8 681.5 807.8 681.5 770.9 681.5 721.3 681.5 671.8 681.5 622.2 681.5 598 644 598 644 622.2 644 667.7 644 713.2 644 758.8 644 793.2 644 827.6 644 862 644 884.7 651.4 926.4 617.7 927.6 593.6 928.5 593.5 966 617.7 965.1L617.7 965.1ZM553.9 699.7C553.9 741.1 553.9 782.5 553.9 823.9 553.9 848.1 553.9 872.3 553.9 896.5 553.9 933.9 579.3 963.7 617.7 965.1 641.9 966 641.8 928.5 617.7 927.6 593.2 926.7 591.4 905.3 591.4 886.5 591.4 860.7 591.4 834.9 591.4 809.1 591.4 772.6 591.4 736.1 591.4 699.7 591.4 675.5 553.9 675.5 553.9 699.7L553.9 699.7ZM644 627.4C644 657.7 644 688 644 718.3 644 742.5 654 762.3 673.9 776.2 699 793.9 735 786.6 755.1 765.1 769.2 750 771.6 731 771.6 711.4 771.6 692.4 771.6 673.3 771.6 654.3 771.6 628.5 771.6 602.8 771.6 577 771.6 562.7 771.6 548.4 771.6 534 771.6 509.9 734.1 509.9 734.1 534 734.1 592 734.1 650 734.1 707.9 734.1 722.8 734.9 736.6 720.2 745.5 702.1 756.4 681.5 739.9 681.5 720.9 681.5 690 681.5 659.1 681.5 628.2 681.5 627.9 681.5 627.7 681.5 627.4 681.5 603.2 644 603.2 644 627.4L644 627.4Z",
|
|
||||||
"width": 1000
|
|
||||||
},
|
|
||||||
"search": [
|
|
||||||
"drip-icon-sex"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uid": "5a7d6d7d4e51bd96e25119b5103ef11b",
|
|
||||||
"css": "drip-icon-bleeding",
|
"css": "drip-icon-bleeding",
|
||||||
"code": 59392,
|
"code": 59392,
|
||||||
"src": "custom_icons",
|
"src": "custom_icons",
|
||||||
@@ -35,7 +21,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"uid": "e3b976b3410fb55084ca71693bd0bb49",
|
"uid": "75d9ba1baffd8f79881fe026da7fb340",
|
||||||
"css": "drip-icon-cervix",
|
"css": "drip-icon-cervix",
|
||||||
"code": 59393,
|
"code": 59393,
|
||||||
"src": "custom_icons",
|
"src": "custom_icons",
|
||||||
@@ -49,11 +35,25 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"uid": "37fdd360714e62dd2e7788676c5259f2",
|
"uid": "2c24d7b60d46a65942c1b0f1e14b39a2",
|
||||||
"css": "drip-icon-desire",
|
"css": "drip-icon-mood",
|
||||||
"code": 59394,
|
"code": 59394,
|
||||||
"src": "custom_icons",
|
"src": "custom_icons",
|
||||||
"selected": true,
|
"selected": true,
|
||||||
|
"svg": {
|
||||||
|
"path": "M913 500C913 611 868 717 789 796 709 874 600 915 490 913 379 911 273 862 198 781 121 700 83 590 87 479 92 369 144 265 226 191 309 116 421 80 531 88 635 95 732 144 802 218 874 294 913 396 913 500 913 524 951 524 951 500 950 386 907 276 829 192 752 109 644 59 531 50 417 42 303 80 214 152 127 222 70 326 54 436 38 550 66 667 133 761 198 853 296 917 406 941 519 965 639 944 737 883 832 825 903 731 934 624 945 584 950 542 951 500 951 476 913 476 913 500M302 421C302 438 288 452 271 452 254 452 240 438 240 421 240 404 254 390 271 390 288 390 302 404 302 421M760 421C760 438 746 452 729 452 712 452 698 438 698 421 698 404 712 390 729 390 746 390 760 404 760 421",
|
||||||
|
"width": 1000
|
||||||
|
},
|
||||||
|
"search": [
|
||||||
|
"drip-icon-mood"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "1cb199b75d815374a7c95351251c3268",
|
||||||
|
"css": "drip-icon-desire",
|
||||||
|
"code": 59395,
|
||||||
|
"src": "custom_icons",
|
||||||
|
"selected": true,
|
||||||
"svg": {
|
"svg": {
|
||||||
"path": "M508 446.6C531 408.2 569.1 376.1 612.1 363.1 629.6 357.9 649.7 356.1 666.7 363.4 686.7 372.2 700.1 390.9 707.4 411 722.9 453.9 697.1 499.2 673 533.3 641.7 577.5 600.5 615.1 559.5 650.2 540.6 666.4 521.1 682 501.5 697.5 509.1 696.5 516.7 695.5 524.2 694.6 492.1 672.1 461.4 647.7 432.3 621.4 393.8 586.7 357.2 547.9 332.7 501.8 312.2 463.4 305.1 415.4 335.6 379.9 350.4 362.6 371.7 352.4 394.4 351.6 422.7 350.6 447.8 365.6 467.3 384.9 484.5 401.8 511.1 375.3 493.8 358.3 451.7 316.9 391 299.8 337.5 330.1 290.4 356.9 271.3 411.9 279.6 463.4 287.7 513.6 321.2 558.5 353.9 595.8 387.4 633.9 426.2 667.4 466.3 698.3 479.1 708.1 492.1 717.7 505.3 726.9 512.7 732.1 521.7 729 528 724 567.3 692.9 606 661.1 641.6 625.9 679.8 588.3 716.8 545.6 737.2 495.4 756.7 447.7 751.6 399.1 719.7 358.1 689.8 319.5 637.9 314.6 594.5 329.5 545.4 346.2 502.1 383.4 475.7 427.7 463.2 448.5 495.7 467.3 508 446.6L508 446.6ZM53.7 537.2C54.8 535.6 56 534 57.2 532.4 58.6 530.5 59.9 528.7 61.3 526.9 62.2 525.7 63.1 524.6 63.9 523.4 66.1 520.7 61.2 527 65.1 521.9 77.8 505.7 91.2 489.9 104.9 474.5 147.7 426.8 195.9 382 246.9 345.2 309.5 300.1 378.8 264.8 454.7 249.7 538 233.2 624.2 248.5 699.2 288.9 803.3 344.9 882.4 439.9 946.3 537.2 947.3 529.6 948.3 522.1 949.2 514.5 872 592 788.2 666.6 688.9 714.6 606.2 754.7 514.4 767.2 424.5 746.8 279.9 713.9 156.3 613.7 50.8 514.5 33.2 497.9 6.6 524.4 24.2 541 120.7 631.7 228 717.3 353.2 764.4 443.9 798.5 542.3 804 635.3 775.7 768.7 735.2 879.3 637.8 975.8 541 981.5 535.3 983.3 525.3 978.7 518.3 909.5 412.9 823.7 311.4 710.2 252.3 630.2 210.6 540.6 196.8 451.8 212.3 307.8 237.4 182.5 334.6 85.9 439.8 63 464.7 40.7 490.6 21.3 518.3 7.4 538.1 39.9 556.8 53.7 537.2Z",
|
"path": "M508 446.6C531 408.2 569.1 376.1 612.1 363.1 629.6 357.9 649.7 356.1 666.7 363.4 686.7 372.2 700.1 390.9 707.4 411 722.9 453.9 697.1 499.2 673 533.3 641.7 577.5 600.5 615.1 559.5 650.2 540.6 666.4 521.1 682 501.5 697.5 509.1 696.5 516.7 695.5 524.2 694.6 492.1 672.1 461.4 647.7 432.3 621.4 393.8 586.7 357.2 547.9 332.7 501.8 312.2 463.4 305.1 415.4 335.6 379.9 350.4 362.6 371.7 352.4 394.4 351.6 422.7 350.6 447.8 365.6 467.3 384.9 484.5 401.8 511.1 375.3 493.8 358.3 451.7 316.9 391 299.8 337.5 330.1 290.4 356.9 271.3 411.9 279.6 463.4 287.7 513.6 321.2 558.5 353.9 595.8 387.4 633.9 426.2 667.4 466.3 698.3 479.1 708.1 492.1 717.7 505.3 726.9 512.7 732.1 521.7 729 528 724 567.3 692.9 606 661.1 641.6 625.9 679.8 588.3 716.8 545.6 737.2 495.4 756.7 447.7 751.6 399.1 719.7 358.1 689.8 319.5 637.9 314.6 594.5 329.5 545.4 346.2 502.1 383.4 475.7 427.7 463.2 448.5 495.7 467.3 508 446.6L508 446.6ZM53.7 537.2C54.8 535.6 56 534 57.2 532.4 58.6 530.5 59.9 528.7 61.3 526.9 62.2 525.7 63.1 524.6 63.9 523.4 66.1 520.7 61.2 527 65.1 521.9 77.8 505.7 91.2 489.9 104.9 474.5 147.7 426.8 195.9 382 246.9 345.2 309.5 300.1 378.8 264.8 454.7 249.7 538 233.2 624.2 248.5 699.2 288.9 803.3 344.9 882.4 439.9 946.3 537.2 947.3 529.6 948.3 522.1 949.2 514.5 872 592 788.2 666.6 688.9 714.6 606.2 754.7 514.4 767.2 424.5 746.8 279.9 713.9 156.3 613.7 50.8 514.5 33.2 497.9 6.6 524.4 24.2 541 120.7 631.7 228 717.3 353.2 764.4 443.9 798.5 542.3 804 635.3 775.7 768.7 735.2 879.3 637.8 975.8 541 981.5 535.3 983.3 525.3 978.7 518.3 909.5 412.9 823.7 311.4 710.2 252.3 630.2 210.6 540.6 196.8 451.8 212.3 307.8 237.4 182.5 334.6 85.9 439.8 63 464.7 40.7 490.6 21.3 518.3 7.4 538.1 39.9 556.8 53.7 537.2Z",
|
||||||
"width": 1000
|
"width": 1000
|
||||||
@@ -63,9 +63,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"uid": "5dcc5c051418d85ae94bfaac3c8175c7",
|
"uid": "813a69db3b2730571df3cf0eb0bb3528",
|
||||||
"css": "drip-icon-mucus",
|
"css": "drip-icon-mucus",
|
||||||
"code": 59395,
|
"code": 59396,
|
||||||
"src": "custom_icons",
|
"src": "custom_icons",
|
||||||
"selected": true,
|
"selected": true,
|
||||||
"svg": {
|
"svg": {
|
||||||
@@ -77,9 +77,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"uid": "d8fad6833f4dee2490000651bccc7d4a",
|
"uid": "fdfbc3e281bcd53763b75cb37c73bbd7",
|
||||||
"css": "drip-icon-note",
|
"css": "drip-icon-note",
|
||||||
"code": 59396,
|
"code": 59397,
|
||||||
"src": "custom_icons",
|
"src": "custom_icons",
|
||||||
"selected": true,
|
"selected": true,
|
||||||
"svg": {
|
"svg": {
|
||||||
@@ -91,9 +91,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"uid": "9263adab051a978c30a160dc73875a3c",
|
"uid": "6e43bf3b2a6702e1ff7654a1839261d6",
|
||||||
"css": "drip-icon-pain",
|
"css": "drip-icon-pain",
|
||||||
"code": 59397,
|
"code": 59398,
|
||||||
"src": "custom_icons",
|
"src": "custom_icons",
|
||||||
"selected": true,
|
"selected": true,
|
||||||
"svg": {
|
"svg": {
|
||||||
@@ -105,11 +105,25 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"uid": "1313779ea10951cb173798dab27f8fdb",
|
"uid": "d215f0e211e5036effaaae07df935e8c",
|
||||||
"css": "drip-icon-temperature",
|
"css": "drip-icon-sex",
|
||||||
"code": 59399,
|
"code": 59399,
|
||||||
"src": "custom_icons",
|
"src": "custom_icons",
|
||||||
"selected": true,
|
"selected": true,
|
||||||
|
"svg": {
|
||||||
|
"path": "M559.7 683.7C536.5 702.7 513.1 721.5 489.6 740.1 497.1 739.1 504.7 738.1 512.3 737.1 444.8 690.1 380.4 638.6 320.2 582.6 245.1 512.6 171.9 432.9 130.6 337.6 96.7 259.5 97.9 160.9 173.4 107.5 210.3 81.4 255.6 72.8 300 77.5 354.6 83.1 404.3 113.5 442.5 151.6 459.6 168.7 486.1 142.2 469 125.1 391.2 47.5 272.2 10.5 170.8 65 128 88.1 96.3 127.5 80.8 173.3 65.6 218 69.2 267.6 82 312.3 110.3 411.7 181.6 496.8 252.6 569.1 317.7 635.5 390.1 694.9 465.3 749.5 474.6 756.3 483.9 762.9 493.3 769.5 500.8 774.7 509.8 771.5 516.1 766.6 539.6 748 563 729.3 586.2 710.2 594.1 703.7 592.9 690.4 586.2 683.7 578.4 676 567.6 677.3 559.7 683.7L559.7 683.7ZM475.8 246.5C522 168.5 601.4 102.7 689.7 79.2 727.7 69.1 769.1 68.9 804.7 86.9 846.9 108.3 874.8 153 886.7 197.7 901 251.6 883.3 308.5 859 356.7 828.6 416.7 785.1 469.6 739.4 518.5 723 536.1 749.4 562.7 766 545 815.7 491.8 862.6 434 895 368.4 922.6 312.5 937.9 249.5 922.8 187.7 910.7 138.1 879.1 90.5 836.5 62.1 800.5 38.1 755.2 31.3 712.9 36.8 602 51.2 499.4 133.1 443.4 227.5 431 248.4 463.4 267.2 475.8 246.5L475.8 246.5ZM617.7 965.1C647.4 964 672.2 945.2 679.8 916.2 682.6 905.3 681.5 892.8 681.5 881.7 681.5 844.8 681.5 807.8 681.5 770.9 681.5 721.3 681.5 671.8 681.5 622.2 681.5 598 644 598 644 622.2 644 667.7 644 713.2 644 758.8 644 793.2 644 827.6 644 862 644 884.7 651.4 926.4 617.7 927.6 593.6 928.5 593.5 966 617.7 965.1L617.7 965.1ZM553.9 699.7C553.9 741.1 553.9 782.5 553.9 823.9 553.9 848.1 553.9 872.3 553.9 896.5 553.9 933.9 579.3 963.7 617.7 965.1 641.9 966 641.8 928.5 617.7 927.6 593.2 926.7 591.4 905.3 591.4 886.5 591.4 860.7 591.4 834.9 591.4 809.1 591.4 772.6 591.4 736.1 591.4 699.7 591.4 675.5 553.9 675.5 553.9 699.7L553.9 699.7ZM644 627.4C644 657.7 644 688 644 718.3 644 742.5 654 762.3 673.9 776.2 699 793.9 735 786.6 755.1 765.1 769.2 750 771.6 731 771.6 711.4 771.6 692.4 771.6 673.3 771.6 654.3 771.6 628.5 771.6 602.8 771.6 577 771.6 562.7 771.6 548.4 771.6 534 771.6 509.9 734.1 509.9 734.1 534 734.1 592 734.1 650 734.1 707.9 734.1 722.8 734.9 736.6 720.2 745.5 702.1 756.4 681.5 739.9 681.5 720.9 681.5 690 681.5 659.1 681.5 628.2 681.5 627.9 681.5 627.7 681.5 627.4 681.5 603.2 644 603.2 644 627.4L644 627.4Z",
|
||||||
|
"width": 1000
|
||||||
|
},
|
||||||
|
"search": [
|
||||||
|
"drip-icon-sex"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "7c566174d70d495b28a413d7a60f6df4",
|
||||||
|
"css": "drip-icon-temperature",
|
||||||
|
"code": 59400,
|
||||||
|
"src": "custom_icons",
|
||||||
|
"selected": true,
|
||||||
"svg": {
|
"svg": {
|
||||||
"path": "M477.7 400.3C503.8 400.3 529.8 400.3 555.8 400.3 580 400.3 580 362.8 555.8 362.8 529.8 362.8 503.8 362.8 477.7 362.8 453.6 362.8 453.6 400.3 477.7 400.3L477.7 400.3ZM330.5 530.7C356.5 530.7 382.6 530.7 408.6 530.7 432.8 530.7 432.8 493.2 408.6 493.2 382.6 493.2 356.5 493.2 330.5 493.2 306.3 493.2 306.3 530.7 330.5 530.7L330.5 530.7ZM330.5 400.3C356.5 400.3 382.6 400.3 408.6 400.3 432.8 400.3 432.8 362.8 408.6 362.8 382.6 362.8 356.5 362.8 330.5 362.8 306.3 362.8 306.3 400.3 330.5 400.3L330.5 400.3ZM330.5 269.9C356.5 269.9 382.6 269.9 408.6 269.9 432.8 269.9 432.8 232.4 408.6 232.4 382.6 232.4 356.5 232.4 330.5 232.4 306.3 232.4 306.3 269.9 330.5 269.9L330.5 269.9ZM574.8 76.7C573.4 44.2 549.4 20.1 516.9 18.8 484.4 17.4 460.2 46.3 458.9 76.7 458.5 87.2 458.8 97.7 458.8 108.2 458.9 173.1 458.9 238.1 458.9 303.1 459 421.8 459.1 540.5 459.1 659.2 459.1 659.3 459.1 659.3 459.1 659.3 463.7 653.2 468.3 647.2 472.9 641.2 414.5 656.6 368.9 700.7 350.4 758 332.1 814.9 347.7 877.4 385.7 922.4 423.3 966.9 484.1 987.5 541.2 979.2 597.3 971.1 646.7 934.3 671.3 883.2 717.7 786.7 662.7 670.5 560.6 641.8 565.2 647.8 569.8 653.8 574.4 659.9 574.5 537.5 574.6 415.1 574.7 292.7 574.8 243.8 574.8 194.9 574.8 146 574.9 121.8 537.4 121.8 537.3 146 537.2 268.4 537.1 390.8 537 513.2 537 562.1 536.9 611 536.9 659.9 536.9 668.2 542.6 675.7 550.6 678 603.9 692.9 644.3 738.8 650.1 794.3 655.8 848.9 627.5 900.7 580 927.4 533.3 953.7 472.2 947.5 430.7 914 387.4 878.8 370.8 821.1 386.6 768 399.9 723 438.1 689.2 482.9 677.3 490.9 675.2 496.6 667.5 496.6 659.3 496.6 541.3 496.5 423.4 496.4 305.5 496.4 245.4 496.4 185.4 496.3 125.3 496.3 110.9 496.3 96.6 496.3 82.2 496.3 77.5 496.5 73.3 497.9 68.7 504.7 46.7 536.5 56.5 537.3 76.7 538.3 100.8 575.9 100.9 574.8 76.7L574.8 76.7Z",
|
"path": "M477.7 400.3C503.8 400.3 529.8 400.3 555.8 400.3 580 400.3 580 362.8 555.8 362.8 529.8 362.8 503.8 362.8 477.7 362.8 453.6 362.8 453.6 400.3 477.7 400.3L477.7 400.3ZM330.5 530.7C356.5 530.7 382.6 530.7 408.6 530.7 432.8 530.7 432.8 493.2 408.6 493.2 382.6 493.2 356.5 493.2 330.5 493.2 306.3 493.2 306.3 530.7 330.5 530.7L330.5 530.7ZM330.5 400.3C356.5 400.3 382.6 400.3 408.6 400.3 432.8 400.3 432.8 362.8 408.6 362.8 382.6 362.8 356.5 362.8 330.5 362.8 306.3 362.8 306.3 400.3 330.5 400.3L330.5 400.3ZM330.5 269.9C356.5 269.9 382.6 269.9 408.6 269.9 432.8 269.9 432.8 232.4 408.6 232.4 382.6 232.4 356.5 232.4 330.5 232.4 306.3 232.4 306.3 269.9 330.5 269.9L330.5 269.9ZM574.8 76.7C573.4 44.2 549.4 20.1 516.9 18.8 484.4 17.4 460.2 46.3 458.9 76.7 458.5 87.2 458.8 97.7 458.8 108.2 458.9 173.1 458.9 238.1 458.9 303.1 459 421.8 459.1 540.5 459.1 659.2 459.1 659.3 459.1 659.3 459.1 659.3 463.7 653.2 468.3 647.2 472.9 641.2 414.5 656.6 368.9 700.7 350.4 758 332.1 814.9 347.7 877.4 385.7 922.4 423.3 966.9 484.1 987.5 541.2 979.2 597.3 971.1 646.7 934.3 671.3 883.2 717.7 786.7 662.7 670.5 560.6 641.8 565.2 647.8 569.8 653.8 574.4 659.9 574.5 537.5 574.6 415.1 574.7 292.7 574.8 243.8 574.8 194.9 574.8 146 574.9 121.8 537.4 121.8 537.3 146 537.2 268.4 537.1 390.8 537 513.2 537 562.1 536.9 611 536.9 659.9 536.9 668.2 542.6 675.7 550.6 678 603.9 692.9 644.3 738.8 650.1 794.3 655.8 848.9 627.5 900.7 580 927.4 533.3 953.7 472.2 947.5 430.7 914 387.4 878.8 370.8 821.1 386.6 768 399.9 723 438.1 689.2 482.9 677.3 490.9 675.2 496.6 667.5 496.6 659.3 496.6 541.3 496.5 423.4 496.4 305.5 496.4 245.4 496.4 185.4 496.3 125.3 496.3 110.9 496.3 96.6 496.3 82.2 496.3 77.5 496.5 73.3 497.9 68.7 504.7 46.7 536.5 56.5 537.3 76.7 538.3 100.8 575.9 100.9 574.8 76.7L574.8 76.7Z",
|
||||||
"width": 1000
|
"width": 1000
|
||||||
@@ -119,4 +133,4 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -13,14 +13,6 @@ export default function AppText(props) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AppTextLight(props) {
|
|
||||||
return (
|
|
||||||
<Text style={[styles.appTextLight, props.style]}>
|
|
||||||
{props.children}
|
|
||||||
</Text>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ActionHint(props) {
|
export function ActionHint(props) {
|
||||||
if(props.isVisible) {
|
if(props.isVisible) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
+79
-37
@@ -11,6 +11,7 @@ import SettingsMenu from './settings/settings-menu'
|
|||||||
import settingsViews from './settings'
|
import settingsViews from './settings'
|
||||||
import Stats from './stats'
|
import Stats from './stats'
|
||||||
import {headerTitles, menuTitles} from '../i18n/en/labels'
|
import {headerTitles, menuTitles} from '../i18n/en/labels'
|
||||||
|
import InfoSymptom from './cycle-day/symptoms/info-symptom'
|
||||||
import setupNotifications from '../lib/notifications'
|
import setupNotifications from '../lib/notifications'
|
||||||
|
|
||||||
// design wants everyhting lowercased, but we don't
|
// design wants everyhting lowercased, but we don't
|
||||||
@@ -19,20 +20,17 @@ const headerTitlesLowerCase = Object.keys(headerTitles).reduce((acc, curr) => {
|
|||||||
acc[curr] = headerTitles[curr].toLowerCase()
|
acc[curr] = headerTitles[curr].toLowerCase()
|
||||||
return acc
|
return acc
|
||||||
}, {})
|
}, {})
|
||||||
const menuTitlesLowerCase = Object.keys(menuTitles).reduce((acc, curr) => {
|
|
||||||
acc[curr] = menuTitles[curr].toLowerCase()
|
|
||||||
return acc
|
|
||||||
}, {})
|
|
||||||
|
|
||||||
const isSymptomView = name => Object.keys(symptomViews).includes(name)
|
const HOME_PAGE = 'Home'
|
||||||
const isSettingsView = name => Object.keys(settingsViews).includes(name)
|
const INFO_SYMPTOM_PAGE = 'InfoSymptom'
|
||||||
const isMenuItem = name => Object.keys(menuTitles).includes(name)
|
const CYCLE_DAY_PAGE = 'CycleDay'
|
||||||
|
const SETTINGS_MENU_PAGE = 'SettingsMenu'
|
||||||
|
|
||||||
export default class App extends Component {
|
export default class App extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
this.state = {
|
this.state = {
|
||||||
currentPage: 'Home'
|
currentPage: HOME_PAGE
|
||||||
}
|
}
|
||||||
this.backHandler = BackHandler.addEventListener('hardwareBackPress', this.handleBackButtonPress)
|
this.backHandler = BackHandler.addEventListener('hardwareBackPress', this.handleBackButtonPress)
|
||||||
setupNotifications(this.navigate)
|
setupNotifications(this.navigate)
|
||||||
@@ -43,63 +41,107 @@ export default class App extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
navigate = (pageName, props) => {
|
navigate = (pageName, props) => {
|
||||||
|
const { currentPage } = this.state
|
||||||
// for the back button to work properly, we want to
|
// for the back button to work properly, we want to
|
||||||
// remember two origins: which menu item we came from
|
// remember two origins: which menu item we came from
|
||||||
// and from where we navigated to the symptom view (day
|
// and from where we navigated to the symptom view (day
|
||||||
// view or home page)
|
// view or home page)
|
||||||
if (isMenuItem(this.state.currentPage)) {
|
if (this.isMenuItem()) {
|
||||||
this.menuOrigin = this.state.currentPage
|
this.menuOrigin = currentPage
|
||||||
}
|
}
|
||||||
this.originForSymptomView = this.state.currentPage
|
if (!this.isSymptomView() && !this.isInfoSymptomView()) {
|
||||||
this.setState({currentPage: pageName, currentProps: props})
|
this.originForSymptomView = currentPage
|
||||||
|
}
|
||||||
|
this.setState({ currentPage: pageName, currentProps: props })
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBackButtonPress = () => {
|
handleBackButtonPress = () => {
|
||||||
if (this.state.currentPage === 'Home') return false
|
const { currentPage, currentProps } = this.state
|
||||||
if (isSymptomView(this.state.currentPage)) {
|
if (currentPage === HOME_PAGE) return false
|
||||||
|
if (this.isSymptomView()) {
|
||||||
this.navigate(
|
this.navigate(
|
||||||
this.originForSymptomView, { date: this.state.currentProps.date }
|
this.originForSymptomView, { date: currentProps.date }
|
||||||
)
|
)
|
||||||
} else if (isSettingsView(this.state.currentPage)) {
|
} else if (this.isSettingsView()) {
|
||||||
this.navigate('SettingsMenu')
|
this.navigate(SETTINGS_MENU_PAGE)
|
||||||
} else if(this.state.currentPage === 'CycleDay') {
|
} else if (currentPage === CYCLE_DAY_PAGE) {
|
||||||
this.navigate(this.menuOrigin)
|
this.navigate(this.menuOrigin)
|
||||||
|
} else if (this.isInfoSymptomView()) {
|
||||||
|
const { date, cycleDay, symptomView } = currentProps
|
||||||
|
this.navigate(
|
||||||
|
symptomView, { date, cycleDay })
|
||||||
} else {
|
} else {
|
||||||
this.navigate('Home')
|
this.navigate(HOME_PAGE)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isMenuItem() {
|
||||||
|
return Object.keys(menuTitles).includes(this.state.currentPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
isSymptomView() {
|
||||||
|
return Object.keys(symptomViews).includes(this.state.currentPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
isInfoSymptomView() {
|
||||||
|
return this.state.currentPage === INFO_SYMPTOM_PAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
isSettingsView() {
|
||||||
|
return Object.keys(settingsViews).includes(this.state.currentPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
isDefaultView() {
|
||||||
|
const { currentPage } = this.state
|
||||||
|
return currentPage !== CYCLE_DAY_PAGE &&
|
||||||
|
!this.isSymptomView() &&
|
||||||
|
!this.isInfoSymptomView()
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const page = {
|
const { currentPage, currentProps } = this.state
|
||||||
Home, Calendar, CycleDay, Chart, SettingsMenu, ...settingsViews, Stats, ...symptomViews
|
const pages = {
|
||||||
}[this.state.currentPage]
|
Home,
|
||||||
|
Calendar,
|
||||||
|
CycleDay,
|
||||||
|
Chart,
|
||||||
|
InfoSymptom,
|
||||||
|
SettingsMenu,
|
||||||
|
...settingsViews,
|
||||||
|
Stats,
|
||||||
|
...symptomViews
|
||||||
|
}
|
||||||
|
const page = pages[currentPage]
|
||||||
|
const title = headerTitlesLowerCase[currentPage]
|
||||||
|
const isSymptomView = this.isSymptomView()
|
||||||
return (
|
return (
|
||||||
<View style={{flex: 1}}>
|
<View style={{flex: 1}}>
|
||||||
{this.state.currentPage != 'CycleDay' && !isSymptomView(this.state.currentPage) &&
|
{this.isDefaultView() &&
|
||||||
|
<Header title={title} />
|
||||||
|
}
|
||||||
|
{this.isInfoSymptomView() &&
|
||||||
|
<Header title={title} goBack={this.handleBackButtonPress} />
|
||||||
|
}
|
||||||
|
{isSymptomView &&
|
||||||
<Header
|
<Header
|
||||||
title={headerTitlesLowerCase[this.state.currentPage]}
|
title={title}
|
||||||
/>}
|
|
||||||
{isSymptomView(this.state.currentPage) &&
|
|
||||||
<Header
|
|
||||||
title={headerTitlesLowerCase[this.state.currentPage]}
|
|
||||||
isSymptomView={true}
|
isSymptomView={true}
|
||||||
goBack={this.handleBackButtonPress}
|
goBack={this.handleBackButtonPress}
|
||||||
date={this.state.currentProps.date}
|
date={currentProps.date}
|
||||||
|
goToSymptomInfo={() => this.navigate(INFO_SYMPTOM_PAGE, {
|
||||||
|
symptomView: currentPage,
|
||||||
|
...currentProps
|
||||||
|
})}
|
||||||
/>}
|
/>}
|
||||||
|
|
||||||
|
|
||||||
{React.createElement(page, {
|
{React.createElement(page, {
|
||||||
navigate: this.navigate,
|
navigate: this.navigate,
|
||||||
...this.state.currentProps
|
...currentProps
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{!isSymptomView(this.state.currentPage) &&
|
{!isSymptomView &&
|
||||||
<Menu
|
<Menu navigate={this.navigate} currentPage={currentPage} />
|
||||||
navigate={this.navigate}
|
|
||||||
titles={menuTitlesLowerCase}
|
|
||||||
currentPage={this.state.currentPage}
|
|
||||||
/>
|
|
||||||
}
|
}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ const symptomIcons = {
|
|||||||
desire: <DripIcon size={16} name='drip-icon-desire' color={styles.iconShades.desire[2]}/>,
|
desire: <DripIcon size={16} name='drip-icon-desire' color={styles.iconShades.desire[2]}/>,
|
||||||
sex: <DripIcon size={16} name='drip-icon-sex' color={styles.iconShades.sex[2]}/>,
|
sex: <DripIcon size={16} name='drip-icon-sex' color={styles.iconShades.sex[2]}/>,
|
||||||
pain: <DripIcon size={16} name='drip-icon-pain' color={styles.iconShades.pain[0]}/>,
|
pain: <DripIcon size={16} name='drip-icon-pain' color={styles.iconShades.pain[0]}/>,
|
||||||
|
mood: <DripIcon size={16} name='drip-icon-mood' color={styles.iconShades.mood[0]}/>,
|
||||||
note: <DripIcon size={16} name='drip-icon-note' color={styles.iconShades.note[0]}/>
|
note: <DripIcon size={16} name='drip-icon-note' color={styles.iconShades.note[0]}/>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,6 +63,7 @@ export default class CycleChart extends Component {
|
|||||||
'sex',
|
'sex',
|
||||||
'desire',
|
'desire',
|
||||||
'pain',
|
'pain',
|
||||||
|
'mood',
|
||||||
'note'
|
'note'
|
||||||
].filter((symptomName) => {
|
].filter((symptomName) => {
|
||||||
return this.cycleDaysSortedByDate.some(cycleDay => {
|
return this.cycleDaysSortedByDate.some(cycleDay => {
|
||||||
|
|||||||
@@ -44,6 +44,10 @@ export default class DayColumn extends Component {
|
|||||||
// is any pain documented?
|
// is any pain documented?
|
||||||
acc.pain = cycleDay.pain &&
|
acc.pain = cycleDay.pain &&
|
||||||
Object.values(cycleDay.pain).some(x => x === true)
|
Object.values(cycleDay.pain).some(x => x === true)
|
||||||
|
} else if (symptom === 'mood') {
|
||||||
|
// is mood documented?
|
||||||
|
acc.mood = cycleDay.mood &&
|
||||||
|
Object.values(cycleDay.mood).some(x => x === true)
|
||||||
}
|
}
|
||||||
acc[`${symptom}Exclude`] = cycleDay[symptom] && cycleDay[symptom].exclude
|
acc[`${symptom}Exclude`] = cycleDay[symptom] && cycleDay[symptom].exclude
|
||||||
return acc
|
return acc
|
||||||
@@ -214,6 +218,18 @@ export default class DayColumn extends Component {
|
|||||||
/>
|
/>
|
||||||
</SymptomIconView>
|
</SymptomIconView>
|
||||||
),
|
),
|
||||||
|
mood: (
|
||||||
|
<SymptomIconView
|
||||||
|
value={this.data.mood}
|
||||||
|
symptomHeight={symptomHeight}
|
||||||
|
key='mood'
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
{...styles.symptomIcon}
|
||||||
|
backgroundColor={styles.iconShades.mood}
|
||||||
|
/>
|
||||||
|
</SymptomIconView>
|
||||||
|
),
|
||||||
note: (
|
note: (
|
||||||
<SymptomIconView
|
<SymptomIconView
|
||||||
value={this.data.note}
|
value={this.data.note}
|
||||||
|
|||||||
@@ -77,7 +77,8 @@ const styles = {
|
|||||||
'#9e346c',
|
'#9e346c',
|
||||||
],
|
],
|
||||||
'pain': ['#bccd67'],
|
'pain': ['#bccd67'],
|
||||||
'note': ['#6CA299']
|
'mood': ['#bc6642'],
|
||||||
|
'note': ['#6ca299']
|
||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
width: 27,
|
width: 27,
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import * as labels from '../../i18n/en/cycle-day'
|
|||||||
import AppText from '../app-text'
|
import AppText from '../app-text'
|
||||||
import DripIcon from '../../assets/drip-icons'
|
import DripIcon from '../../assets/drip-icons'
|
||||||
|
|
||||||
const bleedingLabels = labels.bleeding
|
const bleedingLabels = labels.bleeding.labels
|
||||||
const feelingLabels = labels.mucus.feeling.categories
|
const feelingLabels = labels.mucus.feeling.categories
|
||||||
const textureLabels = labels.mucus.texture.categories
|
const textureLabels = labels.mucus.texture.categories
|
||||||
const openingLabels = labels.cervix.opening.categories
|
const openingLabels = labels.cervix.opening.categories
|
||||||
@@ -24,6 +24,7 @@ const intensityLabels = labels.intensity
|
|||||||
const sexLabels = labels.sex.categories
|
const sexLabels = labels.sex.categories
|
||||||
const contraceptiveLabels = labels.contraceptives.categories
|
const contraceptiveLabels = labels.contraceptives.categories
|
||||||
const painLabels = labels.pain.categories
|
const painLabels = labels.pain.categories
|
||||||
|
const moodLabels = labels.mood.categories
|
||||||
|
|
||||||
export default class CycleDayOverView extends Component {
|
export default class CycleDayOverView extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -56,7 +57,7 @@ export default class CycleDayOverView extends Component {
|
|||||||
const l = {
|
const l = {
|
||||||
bleeding: bleeding => {
|
bleeding: bleeding => {
|
||||||
if (isNumber(bleeding.value)) {
|
if (isNumber(bleeding.value)) {
|
||||||
let bleedingLabel = `${bleedingLabels[bleeding.value]}`
|
let bleedingLabel = bleedingLabels[bleeding.value]
|
||||||
if (bleeding.exclude) bleedingLabel = "( " + bleedingLabel + " )"
|
if (bleeding.exclude) bleedingLabel = "( " + bleedingLabel + " )"
|
||||||
return bleedingLabel
|
return bleedingLabel
|
||||||
}
|
}
|
||||||
@@ -143,6 +144,25 @@ export default class CycleDayOverView extends Component {
|
|||||||
painLabel = painLabel.join(', ')
|
painLabel = painLabel.join(', ')
|
||||||
return painLabel
|
return painLabel
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
mood: mood => {
|
||||||
|
let moodLabel = []
|
||||||
|
if (mood && Object.values(mood).some(val => val)){
|
||||||
|
Object.keys(mood).forEach(key => {
|
||||||
|
if(mood[key] && key !== 'other' && key !== 'note') {
|
||||||
|
moodLabel.push(moodLabels[key])
|
||||||
|
}
|
||||||
|
if(key === 'other' && mood.other) {
|
||||||
|
let label = moodLabels[key]
|
||||||
|
if(mood.note) {
|
||||||
|
label = `${label} (${mood.note})`
|
||||||
|
}
|
||||||
|
moodLabel.push(label)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
moodLabel = moodLabel.join(', ')
|
||||||
|
return moodLabel
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,6 +246,14 @@ export default class CycleDayOverView extends Component {
|
|||||||
iconName='drip-icon-pain'
|
iconName='drip-icon-pain'
|
||||||
>
|
>
|
||||||
</SymptomBox>
|
</SymptomBox>
|
||||||
|
<SymptomBox
|
||||||
|
title='Mood'
|
||||||
|
onPress={() => this.navigate('MoodEditView')}
|
||||||
|
data={this.getLabel('mood')}
|
||||||
|
disabled={dateInFuture}
|
||||||
|
iconName='drip-icon-mood'
|
||||||
|
>
|
||||||
|
</SymptomBox>
|
||||||
<SymptomBox
|
<SymptomBox
|
||||||
title='Note'
|
title='Note'
|
||||||
onPress={() => this.navigate('NoteEditView')}
|
onPress={() => this.navigate('NoteEditView')}
|
||||||
@@ -236,9 +264,9 @@ export default class CycleDayOverView extends Component {
|
|||||||
{/* this is just to make the last row adhere to the grid
|
{/* this is just to make the last row adhere to the grid
|
||||||
(and) because there are no pseudo properties in RN */}
|
(and) because there are no pseudo properties in RN */}
|
||||||
<FillerBoxes />
|
<FillerBoxes />
|
||||||
</View >
|
</View>
|
||||||
</ScrollView >
|
</ScrollView>
|
||||||
</View >
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -248,9 +276,9 @@ export default class CycleDayOverView extends Component {
|
|||||||
|
|
||||||
class SymptomBox extends Component {
|
class SymptomBox extends Component {
|
||||||
render() {
|
render() {
|
||||||
const d = this.props.data
|
const hasData = this.props.data
|
||||||
const boxActive = d ? styles.symptomBoxActive : {}
|
const boxActive = hasData ? styles.symptomBoxActive : {}
|
||||||
const textActive = d ? styles.symptomTextActive : {}
|
const textActive = hasData ? styles.symptomTextActive : {}
|
||||||
const disabledStyle = this.props.disabled ? styles.symptomInFuture : {}
|
const disabledStyle = this.props.disabled ? styles.symptomInFuture : {}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -259,7 +287,7 @@ class SymptomBox extends Component {
|
|||||||
disabled={this.props.disabled}
|
disabled={this.props.disabled}
|
||||||
>
|
>
|
||||||
<View style={[styles.symptomBox, boxActive, disabledStyle]}>
|
<View style={[styles.symptomBox, boxActive, disabledStyle]}>
|
||||||
<DripIcon name={this.props.iconName} size={50} color={d ? 'white' : 'black'}/>
|
<DripIcon name={this.props.iconName} size={50} color={hasData ? 'white' : 'black'}/>
|
||||||
<AppText style={[textActive, disabledStyle]}>
|
<AppText style={[textActive, disabledStyle]}>
|
||||||
{this.props.title.toLowerCase()}
|
{this.props.title.toLowerCase()}
|
||||||
</AppText>
|
</AppText>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import NoteEditView from './note'
|
|||||||
import DesireEditView from './desire'
|
import DesireEditView from './desire'
|
||||||
import SexEditView from './sex'
|
import SexEditView from './sex'
|
||||||
import PainEditView from './pain'
|
import PainEditView from './pain'
|
||||||
|
import MoodEditView from './mood'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
BleedingEditView,
|
BleedingEditView,
|
||||||
@@ -15,5 +16,6 @@ export default {
|
|||||||
NoteEditView,
|
NoteEditView,
|
||||||
DesireEditView,
|
DesireEditView,
|
||||||
SexEditView,
|
SexEditView,
|
||||||
PainEditView
|
PainEditView,
|
||||||
|
MoodEditView
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import {
|
||||||
|
View,
|
||||||
|
ScrollView
|
||||||
|
} from 'react-native'
|
||||||
|
import styles from '../../../styles'
|
||||||
|
import AppText from '../../app-text'
|
||||||
|
import * as labels from '../../../i18n/en/symptom-info.js'
|
||||||
|
|
||||||
|
export default class InfoSymptom extends Component {
|
||||||
|
render() {
|
||||||
|
const symptomView = this.props.symptomView
|
||||||
|
const symptomMapping = {
|
||||||
|
BleedingEditView: 'bleeding',
|
||||||
|
CervixEditView: 'cervix',
|
||||||
|
DesireEditView: 'desire',
|
||||||
|
MucusEditView: 'mucus',
|
||||||
|
NoteEditView: 'note',
|
||||||
|
PainEditView: 'pain',
|
||||||
|
SexEditView: 'sex',
|
||||||
|
TemperatureEditView: 'temperature'
|
||||||
|
}
|
||||||
|
const currentSymptom = symptomMapping[symptomView]
|
||||||
|
const currentSymptomText = labels.symptomInfo[currentSymptom]
|
||||||
|
const currentSymptomTitle = labels.symptomTitle[currentSymptom]
|
||||||
|
return (
|
||||||
|
<ScrollView>
|
||||||
|
<View style={[styles.textWrappingView]}>
|
||||||
|
<AppText style={styles.title}>
|
||||||
|
{currentSymptomTitle}
|
||||||
|
</AppText>
|
||||||
|
<AppText style={styles.paragraph}>
|
||||||
|
{currentSymptomText}
|
||||||
|
{labels.symptomTitle.currentSymptomTitle}
|
||||||
|
</AppText>
|
||||||
|
<AppText style={styles.paragraph}>
|
||||||
|
{labels.symptomInfo.currentSymptomText}
|
||||||
|
</AppText>
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import {
|
||||||
|
ScrollView,
|
||||||
|
TextInput,
|
||||||
|
View
|
||||||
|
} from 'react-native'
|
||||||
|
import { saveSymptom } from '../../../db'
|
||||||
|
import { mood as labels } from '../../../i18n/en/cycle-day'
|
||||||
|
import ActionButtonFooter from './action-button-footer'
|
||||||
|
import SelectBoxGroup from '../select-box-group'
|
||||||
|
import SymptomSection from './symptom-section'
|
||||||
|
import styles from '../../../styles'
|
||||||
|
|
||||||
|
export default class Mood extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
const cycleDay = props.cycleDay
|
||||||
|
if (cycleDay && cycleDay.mood) {
|
||||||
|
this.state = Object.assign({}, cycleDay.mood)
|
||||||
|
} else {
|
||||||
|
this.state = {}
|
||||||
|
}
|
||||||
|
if (this.state.note) {
|
||||||
|
this.state.other = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleState = (key) => {
|
||||||
|
const curr = this.state[key]
|
||||||
|
this.setState({[key]: !curr})
|
||||||
|
if (key === 'other' && !curr) {
|
||||||
|
this.setState({focusTextArea: true})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<View style={{ flex: 1 }}>
|
||||||
|
<ScrollView style={styles.page}>
|
||||||
|
<SymptomSection
|
||||||
|
explainer={labels.explainer}
|
||||||
|
>
|
||||||
|
<SelectBoxGroup
|
||||||
|
labels={labels.categories}
|
||||||
|
onSelect={this.toggleState}
|
||||||
|
optionsState={this.state}
|
||||||
|
/>
|
||||||
|
{ this.state.other &&
|
||||||
|
<TextInput
|
||||||
|
autoFocus={this.state.focusTextArea}
|
||||||
|
multiline={true}
|
||||||
|
placeholder="Enter"
|
||||||
|
value={this.state.note}
|
||||||
|
onChangeText={(val) => {
|
||||||
|
this.setState({note: val})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</SymptomSection>
|
||||||
|
</ScrollView>
|
||||||
|
<ActionButtonFooter
|
||||||
|
symptom='mood'
|
||||||
|
date={this.props.date}
|
||||||
|
currentSymptomValue={this.state}
|
||||||
|
saveAction={() => {
|
||||||
|
const copyOfState = Object.assign({}, this.state)
|
||||||
|
if (!copyOfState.other) {
|
||||||
|
copyOfState.note = null
|
||||||
|
}
|
||||||
|
saveSymptom('mood', this.props.date, copyOfState)
|
||||||
|
}}
|
||||||
|
saveDisabled={Object.values(this.state).every(value => !value)}
|
||||||
|
navigate={this.props.navigate}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import {
|
||||||
|
View,
|
||||||
|
Text} from 'react-native'
|
||||||
|
import styles from '../../styles'
|
||||||
|
|
||||||
|
export default function DefaultHeader(props) {
|
||||||
|
return (
|
||||||
|
<View style={styles.header}>
|
||||||
|
<View style={styles.accentCircle} />
|
||||||
|
<Text style={styles.headerText}>
|
||||||
|
{props.title}
|
||||||
|
</Text>
|
||||||
|
</View >
|
||||||
|
)
|
||||||
|
}
|
||||||
+17
-20
@@ -1,27 +1,24 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {
|
import { Dimensions } from 'react-native'
|
||||||
View,
|
|
||||||
Text,
|
|
||||||
Dimensions
|
|
||||||
} from 'react-native'
|
|
||||||
import styles from '../../styles'
|
|
||||||
import CycleDayHeader from './cycle-day'
|
import CycleDayHeader from './cycle-day'
|
||||||
|
import DefaultHeader from './default'
|
||||||
|
import InfoSymptomHeader from './info-symptom'
|
||||||
import SymptomViewHeader from './symptom-view'
|
import SymptomViewHeader from './symptom-view'
|
||||||
|
|
||||||
export default function Header(p) {
|
export default function Header(p) {
|
||||||
const middle = Dimensions.get('window').width / 2
|
const middle = Dimensions.get('window').width / 2
|
||||||
const props = Object.assign({}, p, {middle})
|
const props = Object.assign({}, p, {middle})
|
||||||
return (
|
|
||||||
props.isCycleDayOverView ?
|
if (props.isCycleDayOverView) {
|
||||||
<CycleDayHeader {...props} />
|
return (<CycleDayHeader {...props} />)
|
||||||
: props.isSymptomView ?
|
}
|
||||||
<SymptomViewHeader {...props}/>
|
else if (props.isSymptomView) {
|
||||||
:
|
return (<SymptomViewHeader {...props} />)
|
||||||
<View style={styles.header}>
|
}
|
||||||
<View style={styles.accentCircle} />
|
else if (props.title === 'info') {
|
||||||
<Text style={styles.headerText}>
|
return (<InfoSymptomHeader {...props} />)
|
||||||
{props.title}
|
}
|
||||||
</Text>
|
else {
|
||||||
</View >
|
return (<DefaultHeader {...props} />)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import {
|
||||||
|
Text,
|
||||||
|
TouchableOpacity,
|
||||||
|
View
|
||||||
|
} from 'react-native'
|
||||||
|
import styles, { iconStyles } from '../../styles'
|
||||||
|
import NavigationArrow from './navigation-arrow'
|
||||||
|
import Icon from 'react-native-vector-icons/Entypo'
|
||||||
|
|
||||||
|
export default function InfoSymptomHeader(props) {
|
||||||
|
return (
|
||||||
|
<View style={[styles.header, styles.headerCycleDay, styles.headerSymptom]}>
|
||||||
|
<View
|
||||||
|
style={styles.accentCircle}
|
||||||
|
left={props.middle - styles.accentCircle.width / 2}
|
||||||
|
/>
|
||||||
|
<NavigationArrow direction='left' {...props}/>
|
||||||
|
<View>
|
||||||
|
<Text style={styles.headerText}>
|
||||||
|
{props.title}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<TouchableOpacity style={styles.hiddenIcon}>
|
||||||
|
<Icon
|
||||||
|
name={'chevron-thin-right'}
|
||||||
|
{...iconStyles.hiddenIcon}
|
||||||
|
/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {
|
import {
|
||||||
View,
|
View,
|
||||||
Text} from 'react-native'
|
Text,
|
||||||
|
TouchableOpacity
|
||||||
|
} from 'react-native'
|
||||||
import styles, { iconStyles } from '../../styles'
|
import styles, { iconStyles } from '../../styles'
|
||||||
import FeatherIcon from 'react-native-vector-icons/Feather'
|
import FeatherIcon from 'react-native-vector-icons/Feather'
|
||||||
import NavigationArrow from './navigation-arrow'
|
import NavigationArrow from './navigation-arrow'
|
||||||
@@ -26,11 +28,17 @@ export default function SymptomViewHeader(props) {
|
|||||||
{formatDate(props.date)}
|
{formatDate(props.date)}
|
||||||
</Text>
|
</Text>
|
||||||
</View >
|
</View >
|
||||||
<FeatherIcon
|
<TouchableOpacity
|
||||||
name='info'
|
onPress={() => props.goToSymptomInfo()}
|
||||||
style={styles.symptomInfoIcon}
|
style={styles.infoButton}
|
||||||
{...iconStyles.symptomHeaderIcons}
|
>
|
||||||
/>
|
<FeatherIcon
|
||||||
|
name="info"
|
||||||
|
style={styles.symptomInfoIcon}
|
||||||
|
{...iconStyles.symptomHeaderIcons}
|
||||||
|
/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,8 @@ import {links} from '../../i18n/en/settings'
|
|||||||
|
|
||||||
export default function(url) {
|
export default function(url) {
|
||||||
const link = Object.values(links).find(link => link.url === url)
|
const link = Object.values(links).find(link => link.url === url)
|
||||||
|
if (url === 'mailto:bloodyhealth@mailbox.org') {
|
||||||
|
console.log(links.email.url === url)
|
||||||
|
}
|
||||||
return link ? link.text : url
|
return link ? link.text : url
|
||||||
}
|
}
|
||||||
+17
-15
@@ -1,16 +1,20 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import { ScrollView, View, TouchableOpacity, TouchableHighlight, Dimensions } from 'react-native'
|
import { ScrollView, View, TouchableHighlight, TouchableOpacity, Dimensions } from 'react-native'
|
||||||
import { LocalDate, ChronoUnit } from 'js-joda'
|
import { LocalDate, ChronoUnit } from 'js-joda'
|
||||||
import Icon from 'react-native-vector-icons/Entypo'
|
import Icon from 'react-native-vector-icons/Entypo'
|
||||||
import Hyperlink from 'react-native-hyperlink'
|
import Hyperlink from 'react-native-hyperlink'
|
||||||
import { secondaryColor, cycleDayColor, periodColor } from '../styles'
|
import { secondaryColor, cycleDayColor, periodColor } from '../styles'
|
||||||
import { home as labels, bleedingPrediction as predictLabels, shared, links } from '../i18n/en/labels'
|
import {
|
||||||
|
home as labels,
|
||||||
|
bleedingPrediction as predictLabels,
|
||||||
|
shared,
|
||||||
|
} from '../i18n/en/labels'
|
||||||
|
import links from '../i18n/en/links'
|
||||||
import cycleModule from '../lib/cycle'
|
import cycleModule from '../lib/cycle'
|
||||||
import { getCycleDaysSortedByDate } from '../db'
|
|
||||||
import { getFertilityStatusForDay } from '../lib/sympto-adapter'
|
import { getFertilityStatusForDay } from '../lib/sympto-adapter'
|
||||||
import replace from './helpers/replace-url-with-text'
|
import replace from './helpers/replace-url-with-text'
|
||||||
import styles from '../styles'
|
import styles from '../styles'
|
||||||
import AppText, { AppTextLight } from './app-text'
|
import AppText from './app-text'
|
||||||
import DripHomeIcon from '../assets/drip-home-icons'
|
import DripHomeIcon from '../assets/drip-home-icons'
|
||||||
import Button from './button'
|
import Button from './button'
|
||||||
|
|
||||||
@@ -36,9 +40,9 @@ const ShowMoreToggler = ({ isShowingMore, onToggle }) => {
|
|||||||
const IconText = ({ children, wrapperStyles }) => {
|
const IconText = ({ children, wrapperStyles }) => {
|
||||||
return (
|
return (
|
||||||
<View style={[styles.homeIconTextWrapper, wrapperStyles]}>
|
<View style={[styles.homeIconTextWrapper, wrapperStyles]}>
|
||||||
<AppTextLight style={styles.iconText}>
|
<AppText style={styles.iconText}>
|
||||||
{ children }
|
{ children }
|
||||||
</AppTextLight>
|
</AppText>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -76,8 +80,6 @@ export default class Home extends Component {
|
|||||||
bleedingPredictionRange: getBleedingPredictionRange(prediction),
|
bleedingPredictionRange: getBleedingPredictionRange(prediction),
|
||||||
...fertilityStatus
|
...fertilityStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
this.cycleDays = getCycleDaysSortedByDate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
passTodayTo(componentName) {
|
passTodayTo(componentName) {
|
||||||
@@ -96,7 +98,7 @@ export default class Home extends Component {
|
|||||||
labels.cycleDayKnown(cycleDayNumber) :
|
labels.cycleDayKnown(cycleDayNumber) :
|
||||||
labels.cycleDayNotEnoughInfo
|
labels.cycleDayNotEnoughInfo
|
||||||
|
|
||||||
const { statusText } = this.state;
|
const { statusText } = this.state
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View flex={1}>
|
<View flex={1}>
|
||||||
@@ -156,12 +158,12 @@ export default class Home extends Component {
|
|||||||
}
|
}
|
||||||
{ isShowingMore &&
|
{ isShowingMore &&
|
||||||
<View>
|
<View>
|
||||||
<AppText styles={styles.paragraph}>
|
<Hyperlink linkStyle={styles.link} linkText={replace}>
|
||||||
{ statusText && <AppText>{ `${status}.` }</AppText> }
|
<AppText styles={styles.paragraph}>
|
||||||
<Hyperlink linkStyle={styles.link} linkText={replace}>
|
{ statusText }
|
||||||
<AppText>${links.moreToNfp.url}`}</AppText>
|
</AppText>
|
||||||
</Hyperlink>
|
<AppText>{links.moreAboutNfp.url}</AppText>
|
||||||
</AppText>
|
</Hyperlink>
|
||||||
</View>
|
</View>
|
||||||
}
|
}
|
||||||
</HomeElement>
|
</HomeElement>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const labels = settingsLabels.license
|
|||||||
export default function License({setLicense}) {
|
export default function License({setLicense}) {
|
||||||
return (
|
return (
|
||||||
<ScrollView style={styles.licensePage}>
|
<ScrollView style={styles.licensePage}>
|
||||||
<Hyperlink linkStyle={styles.link} linkText={replace}>
|
<Hyperlink linkStyle={styles.link} linkText={replace} linkDefault>
|
||||||
<AppText style={styles.settingsSegmentTitle}>{labels.title}</AppText>
|
<AppText style={styles.settingsSegmentTitle}>{labels.title}</AppText>
|
||||||
<AppText>{labels.text}</AppText>
|
<AppText>{labels.text}</AppText>
|
||||||
</Hyperlink>
|
</Hyperlink>
|
||||||
|
|||||||
+72
-34
@@ -1,46 +1,84 @@
|
|||||||
import React, { Component } from 'react'
|
import React from 'react'
|
||||||
import {
|
import {
|
||||||
View,
|
View,
|
||||||
Text,
|
Text,
|
||||||
TouchableOpacity
|
TouchableOpacity
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
|
|
||||||
|
import settingsViews from './settings'
|
||||||
|
|
||||||
|
import { menuTitles } from '../i18n/en/labels'
|
||||||
|
|
||||||
import styles, { iconStyles, secondaryColor } from '../styles'
|
import styles, { iconStyles, secondaryColor } from '../styles'
|
||||||
import Icon from 'react-native-vector-icons/MaterialCommunityIcons'
|
import Icon from 'react-native-vector-icons/MaterialCommunityIcons'
|
||||||
|
|
||||||
export default class Menu extends Component {
|
const menuTitlesLowerCase = Object.keys(menuTitles).reduce((acc, curr) => {
|
||||||
makeMenuItem = ({ title, icon, onPress}, i) => {
|
acc[curr] = menuTitles[curr].toLowerCase()
|
||||||
const styleActive = (this.props.currentPage.toLowerCase() === title) ?
|
return acc
|
||||||
{color: secondaryColor} : {}
|
}, {})
|
||||||
return (
|
|
||||||
<TouchableOpacity
|
|
||||||
onPress={onPress}
|
|
||||||
style={styles.menuItem}
|
|
||||||
key={i.toString()}
|
|
||||||
>
|
|
||||||
<Icon name={icon} {...iconStyles.menuIcon} {...styleActive} />
|
|
||||||
<Text style={[styles.menuText, styleActive]}>
|
|
||||||
{title}
|
|
||||||
</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
goTo(componentName) {
|
const menuItems = [
|
||||||
this.props.navigate(componentName)
|
{
|
||||||
|
labelKey: 'Home',
|
||||||
|
icon: 'home',
|
||||||
|
component: 'Home',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
labelKey: 'Calendar',
|
||||||
|
icon: 'calendar-range',
|
||||||
|
component: 'Calendar',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
labelKey: 'Chart',
|
||||||
|
icon: 'chart-line',
|
||||||
|
component: 'Chart',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
labelKey: 'Stats',
|
||||||
|
icon: 'chart-pie',
|
||||||
|
component: 'Stats',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
labelKey: 'Settings',
|
||||||
|
icon: 'settings',
|
||||||
|
component: 'SettingsMenu',
|
||||||
|
children: Object.keys(settingsViews),
|
||||||
}
|
}
|
||||||
|
]
|
||||||
|
|
||||||
render() {
|
const MenuItem = ({ icon, labelKey, active, onPress }) => {
|
||||||
const t = this.props.titles
|
const styleActive = active ? { color: secondaryColor } : null
|
||||||
return (
|
return (
|
||||||
<View style={styles.menu}>
|
<TouchableOpacity
|
||||||
{[
|
style={styles.menuItem}
|
||||||
{ title: t.Home, icon: 'home', onPress: () => this.goTo('Home') },
|
onPress={onPress}
|
||||||
{ title: t.Calendar, icon: 'calendar-range', onPress: () => this.goTo('Calendar') },
|
>
|
||||||
{ title: t.Chart, icon: 'chart-line', onPress: () => this.goTo('Chart') },
|
<Icon name={icon} {...iconStyles.menuIcon} {...styleActive} />
|
||||||
{ title: t.Stats, icon: 'chart-pie', onPress: () => this.goTo('Stats') },
|
<Text style={[styles.menuText, styleActive]}>
|
||||||
{ title: t.Settings, icon: 'settings', onPress: () => this.goTo('SettingsMenu') },
|
{menuTitlesLowerCase[labelKey]}
|
||||||
].map(this.makeMenuItem)}
|
</Text>
|
||||||
</View >
|
</TouchableOpacity>
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Menu = ({ currentPage, navigate }) => {
|
||||||
|
return (
|
||||||
|
<View style={styles.menu}>
|
||||||
|
{ menuItems.map(({ icon, labelKey, component, children }) => {
|
||||||
|
const isActive = (component === currentPage) ||
|
||||||
|
(children && children.indexOf(currentPage) !== -1)
|
||||||
|
return (
|
||||||
|
<MenuItem
|
||||||
|
key={labelKey}
|
||||||
|
labelKey={labelKey}
|
||||||
|
icon={icon}
|
||||||
|
active={isActive}
|
||||||
|
onPress={() => navigate(component)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
)}
|
||||||
|
</View >
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Menu
|
||||||
@@ -12,7 +12,7 @@ export default class AboutSection extends Component {
|
|||||||
return (
|
return (
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
<SettingsSegment title={labels.aboutSection.title}>
|
<SettingsSegment title={labels.aboutSection.title}>
|
||||||
<Hyperlink linkStyle={styles.link} linkText={replace}>
|
<Hyperlink linkStyle={styles.link} linkText={replace} linkDefault>
|
||||||
<AppText>{labels.aboutSection.text}</AppText>
|
<AppText>{labels.aboutSection.text}</AppText>
|
||||||
</Hyperlink>
|
</Hyperlink>
|
||||||
</SettingsSegment>
|
</SettingsSegment>
|
||||||
@@ -23,7 +23,7 @@ export default class AboutSection extends Component {
|
|||||||
<AppText>{labels.credits.note}</AppText>
|
<AppText>{labels.credits.note}</AppText>
|
||||||
</SettingsSegment>
|
</SettingsSegment>
|
||||||
<SettingsSegment title={labels.website.title}>
|
<SettingsSegment title={labels.website.title}>
|
||||||
<Hyperlink linkStyle={styles.link}>
|
<Hyperlink linkStyle={styles.link} linkDefault>
|
||||||
<AppText>{links.website.url}</AppText>
|
<AppText>{links.website.url}</AppText>
|
||||||
</Hyperlink>
|
</Hyperlink>
|
||||||
</SettingsSegment>
|
</SettingsSegment>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default class License extends Component {
|
|||||||
return (
|
return (
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
<View style={styles.settingsSegment}>
|
<View style={styles.settingsSegment}>
|
||||||
<Hyperlink linkStyle={styles.link} linkText={replace}>
|
<Hyperlink linkStyle={styles.link} linkText={replace} linkDefault>
|
||||||
<AppText style={styles.settingsSegmentTitle}>{`${labels.license.title} `}</AppText>
|
<AppText style={styles.settingsSegmentTitle}>{`${labels.license.title} `}</AppText>
|
||||||
<AppText>{`${labels.license.text} `}</AppText>
|
<AppText>{`${labels.license.text} `}</AppText>
|
||||||
</Hyperlink>
|
</Hyperlink>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export default class Settings extends Component {
|
|||||||
<Icon name="info-with-circle" style={iconStyles.infoInHeading}/>
|
<Icon name="info-with-circle" style={iconStyles.infoInHeading}/>
|
||||||
<AppText style={styles.settingsSegmentTitle}>{`${labels.preOvu.title} `}</AppText>
|
<AppText style={styles.settingsSegmentTitle}>{`${labels.preOvu.title} `}</AppText>
|
||||||
</View>
|
</View>
|
||||||
<Hyperlink linkStyle={styles.link} linkText={replaceUrlWithText}>
|
<Hyperlink linkStyle={styles.link} linkText={replaceUrlWithText} linkDefault>
|
||||||
<AppText>{labels.preOvu.note}</AppText>
|
<AppText>{labels.preOvu.note}</AppText>
|
||||||
</Hyperlink>
|
</Hyperlink>
|
||||||
</SettingsSegment>
|
</SettingsSegment>
|
||||||
|
|||||||
@@ -64,7 +64,6 @@ export default class ConfirmWithPassword extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
||||||
const { password } = this.state
|
const { password } = this.state
|
||||||
const labels = settings.passwordSettings
|
const labels = settings.passwordSettings
|
||||||
|
|
||||||
@@ -75,12 +74,27 @@ export default class ConfirmWithPassword extends Component {
|
|||||||
value={password}
|
value={password}
|
||||||
onChangeText={this.handlePasswordInput}
|
onChangeText={this.handlePasswordInput}
|
||||||
/>
|
/>
|
||||||
<SettingsButton
|
<View style={{
|
||||||
onPress={this.initPasswordCheck}
|
flex: 1,
|
||||||
disabled={!password}
|
flexDirection: 'row',
|
||||||
>
|
justifyContent: 'space-between'
|
||||||
{shared.confirmToProceed}
|
}}>
|
||||||
</SettingsButton>
|
<SettingsButton
|
||||||
|
onPress={this.props.onCancel}
|
||||||
|
secondary
|
||||||
|
>
|
||||||
|
{shared.cancel}
|
||||||
|
</SettingsButton>
|
||||||
|
<SettingsButton
|
||||||
|
onPress={this.initPasswordCheck}
|
||||||
|
disabled={!password}
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{shared.confirmToProceed}
|
||||||
|
</SettingsButton>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -5,16 +5,22 @@ import { TouchableOpacity } from 'react-native'
|
|||||||
import AppText from '../../app-text'
|
import AppText from '../../app-text'
|
||||||
import styles from '../../../styles'
|
import styles from '../../../styles'
|
||||||
|
|
||||||
const SettingsButton = ({ children, ...props }) => {
|
const SettingsButton = ({ children, style, secondary, ...props }) => {
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[
|
style={[
|
||||||
styles.settingsButton,
|
styles.settingsButton,
|
||||||
props.disabled ? styles.settingsButtonDisabled : null
|
secondary ? null : styles.settingsButtonAccent,
|
||||||
|
props.disabled ? styles.settingsButtonDisabled : null,
|
||||||
|
style
|
||||||
]}
|
]}
|
||||||
{ ...props }
|
{ ...props }
|
||||||
>
|
>
|
||||||
<AppText style={styles.settingsButtonText}>
|
<AppText style={
|
||||||
|
secondary ?
|
||||||
|
styles.settingsButtonSecondaryText :
|
||||||
|
styles.settingsButtonText
|
||||||
|
}>
|
||||||
{children}
|
{children}
|
||||||
</AppText>
|
</AppText>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|||||||
+188
@@ -0,0 +1,188 @@
|
|||||||
|
import cycleModule from '../../lib/cycle'
|
||||||
|
|
||||||
|
const TemperatureSchema = {
|
||||||
|
name: 'Temperature',
|
||||||
|
properties: {
|
||||||
|
value: 'double',
|
||||||
|
exclude: 'bool',
|
||||||
|
time: {
|
||||||
|
type: 'string',
|
||||||
|
optional: true
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
type: 'string',
|
||||||
|
optional: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const BleedingSchema = {
|
||||||
|
name: 'Bleeding',
|
||||||
|
properties: {
|
||||||
|
value: 'int',
|
||||||
|
exclude: 'bool'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const MucusSchema = {
|
||||||
|
name: 'Mucus',
|
||||||
|
properties: {
|
||||||
|
feeling: 'int',
|
||||||
|
texture: 'int',
|
||||||
|
value: 'int',
|
||||||
|
exclude: 'bool'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const CervixSchema = {
|
||||||
|
name: 'Cervix',
|
||||||
|
properties: {
|
||||||
|
opening: 'int',
|
||||||
|
firmness: 'int',
|
||||||
|
position: {type: 'int', optional: true },
|
||||||
|
exclude: 'bool'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const NoteSchema = {
|
||||||
|
name: 'Note',
|
||||||
|
properties: {
|
||||||
|
value: 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const DesireSchema = {
|
||||||
|
name: 'Desire',
|
||||||
|
properties: {
|
||||||
|
value: 'int'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const SexSchema = {
|
||||||
|
name: 'Sex',
|
||||||
|
properties: {
|
||||||
|
solo: { type: 'bool', optional: true },
|
||||||
|
partner: { type: 'bool', optional: true },
|
||||||
|
condom: { type: 'bool', optional: true },
|
||||||
|
pill: { type: 'bool', optional: true },
|
||||||
|
iud: { type: 'bool', optional: true },
|
||||||
|
patch: { type: 'bool', optional: true },
|
||||||
|
ring: { type: 'bool', optional: true },
|
||||||
|
implant: { type: 'bool', optional: true },
|
||||||
|
diaphragm: { type: 'bool', optional: true },
|
||||||
|
none: { type: 'bool', optional: true },
|
||||||
|
other: { type: 'bool', optional: true },
|
||||||
|
note: { type: 'string', optional: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const PainSchema = {
|
||||||
|
name: 'Pain',
|
||||||
|
properties: {
|
||||||
|
cramps: { type: 'bool', optional: true },
|
||||||
|
ovulationPain: { type: 'bool', optional: true },
|
||||||
|
headache: { type: 'bool', optional: true },
|
||||||
|
backache: { type: 'bool', optional: true },
|
||||||
|
nausea: { type: 'bool', optional: true },
|
||||||
|
tenderBreasts: { type: 'bool', optional: true },
|
||||||
|
migraine: { type: 'bool', optional: true },
|
||||||
|
other: { type: 'bool', optional: true },
|
||||||
|
note: { type: 'string', optional: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const MoodSchema = {
|
||||||
|
name: 'Mood',
|
||||||
|
properties: {
|
||||||
|
happy: { type: 'bool', optional: true },
|
||||||
|
sad: { type: 'bool', optional: true },
|
||||||
|
stressed: { type: 'bool', optional: true },
|
||||||
|
balanced: { type: 'bool', optional: true },
|
||||||
|
fine: { type: 'bool', optional: true },
|
||||||
|
anxious: { type: 'bool', optional: true },
|
||||||
|
energetic: { type: 'bool', optional: true },
|
||||||
|
fatigue: { type: 'bool', optional: true },
|
||||||
|
angry: { type: 'bool', optional: true },
|
||||||
|
other: { type: 'bool', optional: true },
|
||||||
|
note: { type: 'string', optional: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const CycleDaySchema = {
|
||||||
|
name: 'CycleDay',
|
||||||
|
primaryKey: 'date',
|
||||||
|
properties: {
|
||||||
|
date: 'string',
|
||||||
|
temperature: {
|
||||||
|
type: 'Temperature',
|
||||||
|
optional: true
|
||||||
|
},
|
||||||
|
isCycleStart: 'bool',
|
||||||
|
bleeding: {
|
||||||
|
type: 'Bleeding',
|
||||||
|
optional: true
|
||||||
|
},
|
||||||
|
mucus: {
|
||||||
|
type: 'Mucus',
|
||||||
|
optional: true
|
||||||
|
},
|
||||||
|
cervix: {
|
||||||
|
type: 'Cervix',
|
||||||
|
optional: true
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
type: 'Note',
|
||||||
|
optional: true
|
||||||
|
},
|
||||||
|
desire: {
|
||||||
|
type: 'Desire',
|
||||||
|
optional: true
|
||||||
|
},
|
||||||
|
sex: {
|
||||||
|
type: 'Sex',
|
||||||
|
optional: true
|
||||||
|
},
|
||||||
|
pain: {
|
||||||
|
type: 'Pain',
|
||||||
|
optional: true
|
||||||
|
},
|
||||||
|
mood: {
|
||||||
|
type: 'Mood',
|
||||||
|
optional: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
schema: [
|
||||||
|
CycleDaySchema,
|
||||||
|
TemperatureSchema,
|
||||||
|
BleedingSchema,
|
||||||
|
MucusSchema,
|
||||||
|
CervixSchema,
|
||||||
|
NoteSchema,
|
||||||
|
DesireSchema,
|
||||||
|
SexSchema,
|
||||||
|
PainSchema,
|
||||||
|
MoodSchema
|
||||||
|
],
|
||||||
|
schemaVersion: 3,
|
||||||
|
migration: (oldRealm, newRealm) => {
|
||||||
|
if (oldRealm.schemaVersion >= 3) return
|
||||||
|
const oldBleedingDays = oldRealm.objects('CycleDay')
|
||||||
|
.filtered('bleeding != null')
|
||||||
|
.sorted('date', true)
|
||||||
|
|
||||||
|
const { isMensesStart } = cycleModule({
|
||||||
|
bleedingDaysSortedByDate: oldBleedingDays
|
||||||
|
})
|
||||||
|
|
||||||
|
const newBleedingDays = newRealm.objects('CycleDay')
|
||||||
|
.filtered('bleeding != null')
|
||||||
|
.sorted('date', true)
|
||||||
|
|
||||||
|
oldBleedingDays.forEach((day, i) => {
|
||||||
|
newBleedingDays[i].isCycleStart = isMensesStart(day)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
import schema0 from './0.js'
|
import schema0 from './0.js'
|
||||||
import schema1 from './1.js'
|
import schema1 from './1.js'
|
||||||
import schema2 from './2.js'
|
import schema2 from './2.js'
|
||||||
|
import schema3 from './3.js'
|
||||||
|
|
||||||
export default [schema0, schema1, schema2]
|
export default [schema0, schema1, schema2, schema3]
|
||||||
+25
-9
@@ -2,15 +2,15 @@ export const mucusNFP = ['t', 'Ø', 'f', 'S', 'S+']
|
|||||||
export const intensity = ['low', 'medium', 'high']
|
export const intensity = ['low', 'medium', 'high']
|
||||||
|
|
||||||
export const bleeding = {
|
export const bleeding = {
|
||||||
labels: ['spotting', 'light', 'medium', 'heavy'],
|
labels: ['spotting', 'light', 'medium', 'heavy'],
|
||||||
heaviness: {
|
heaviness: {
|
||||||
header: "Heaviness",
|
header: "Heaviness",
|
||||||
explainer: "How heavy is the bleeding?",
|
explainer: "How heavy is the bleeding?",
|
||||||
},
|
},
|
||||||
exclude: {
|
exclude: {
|
||||||
header: "Exclude",
|
header: "Exclude",
|
||||||
explainer: "You can exclude this value if it's not menstrual bleeding"
|
explainer: "You can exclude this value if it's not menstrual bleeding"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const cervix = {
|
export const cervix = {
|
||||||
@@ -86,6 +86,22 @@ export const pain = {
|
|||||||
explainer: 'How did your body feel today?'
|
explainer: 'How did your body feel today?'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const mood = {
|
||||||
|
categories: {
|
||||||
|
happy: 'Happy',
|
||||||
|
sad: 'Sad',
|
||||||
|
stressed: 'Stressed',
|
||||||
|
balanced: 'Balanced',
|
||||||
|
fine: 'Fine',
|
||||||
|
anxious: 'Anxious',
|
||||||
|
energetic: 'Energetic',
|
||||||
|
fatigue: 'Fatigue',
|
||||||
|
angry: 'Angry',
|
||||||
|
other: 'Other'
|
||||||
|
},
|
||||||
|
explainer: 'How did you feel today?'
|
||||||
|
}
|
||||||
|
|
||||||
export const temperature = {
|
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.',
|
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.',
|
outOfAbsoluteRangeWarning: 'This temperature value is too high or low to be shown on the temperature chart.',
|
||||||
|
|||||||
+3
-1
@@ -39,7 +39,9 @@ export const headerTitles = {
|
|||||||
NoteEditView: 'Note',
|
NoteEditView: 'Note',
|
||||||
DesireEditView: 'Desire',
|
DesireEditView: 'Desire',
|
||||||
SexEditView: 'Sex',
|
SexEditView: 'Sex',
|
||||||
PainEditView: 'Pain'
|
PainEditView: 'Pain',
|
||||||
|
MoodEditView: 'Mood',
|
||||||
|
InfoSymptom: 'Info'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const menuTitles = {
|
export const menuTitles = {
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
export default {
|
||||||
|
gitlab: {
|
||||||
|
url: 'https://gitlab.com/bloodyhealth/drip',
|
||||||
|
text: 'GitLab'
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
url: 'mailto:bloodyhealth@mailbox.org',
|
||||||
|
text: 'email'
|
||||||
|
},
|
||||||
|
wiki: {
|
||||||
|
url: 'https://gitlab.com/bloodyhealth/drip/wikis/home',
|
||||||
|
text: 'wiki'
|
||||||
|
},
|
||||||
|
website: {
|
||||||
|
url: 'https://bloodyhealth.gitlab.io/'
|
||||||
|
},
|
||||||
|
moreAboutNfp: {
|
||||||
|
url: 'https://gitlab.com/bloodyhealth/drip/wikis/nfp/intro',
|
||||||
|
text: 'More'
|
||||||
|
},
|
||||||
|
}
|
||||||
+4
-24
@@ -1,25 +1,4 @@
|
|||||||
|
import links from './links'
|
||||||
export const links = {
|
|
||||||
gitlab: {
|
|
||||||
url: 'https://gitlab.com/bloodyhealth/drip',
|
|
||||||
text: 'GitLab'
|
|
||||||
},
|
|
||||||
email: {
|
|
||||||
url: 'mailto:bl00dyhealth@mailbox.org',
|
|
||||||
text: 'email'
|
|
||||||
},
|
|
||||||
wiki: {
|
|
||||||
url: 'https://gitlab.com/bloodyhealth/drip/wikis/home',
|
|
||||||
text: 'wiki'
|
|
||||||
},
|
|
||||||
website: {
|
|
||||||
url: 'https://bloodyhealth.gitlab.io/'
|
|
||||||
},
|
|
||||||
moreToNfp: {
|
|
||||||
url: 'https://gitlab.com/bloodyhealth/drip/wikis/nfp/intro',
|
|
||||||
text: 'More'
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
menuTitles: {
|
menuTitles: {
|
||||||
@@ -51,7 +30,8 @@ export default {
|
|||||||
deleteOption: 'Import and delete existing',
|
deleteOption: 'Import and delete existing',
|
||||||
errors: {
|
errors: {
|
||||||
couldNotOpenFile: 'Could not open file',
|
couldNotOpenFile: 'Could not open file',
|
||||||
postFix: 'No data was imported or changed'
|
postFix: 'No data was imported or changed',
|
||||||
|
futureEdit: 'Future dates may only contain a note, no other symptoms'
|
||||||
},
|
},
|
||||||
success: {
|
success: {
|
||||||
message: 'Data successfully imported'
|
message: 'Data successfully imported'
|
||||||
@@ -128,7 +108,7 @@ export default {
|
|||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details: https://www.gnu.org/licenses/gpl-3.0.html
|
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details: https://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
You can contact us by email at bloodyhealth@mailbox.com.`
|
You can contact us by bloodyhealth@mailbox.org.`
|
||||||
},
|
},
|
||||||
version: {
|
version: {
|
||||||
title: 'Version'
|
title: 'Version'
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
export const symptomInfo = {
|
||||||
|
bleeding: `Tracking menstrual bleeding allows you to know the beginning and the end of a menstrual cycle.
|
||||||
|
|
||||||
|
After a while of tracking it you will get an overview of how long your cycles last on average, whether the length of your last cycles vary a lot and how many days of menstrual bleeding you usually experience.
|
||||||
|
|
||||||
|
The app allows you to track different intensity of bleeding: spotting, light, medium and heavy. Every bleeding value that is not excluded is taken into account for fertility calculation and prediction for the start of next cycles.
|
||||||
|
|
||||||
|
Excluding bleeding values is for tracking bleeding when it's not marking the start of a new cycle or the continuation of menstrual bleeding the day(s) before,
|
||||||
|
e.g. bleeding caused by miscarriage or ovulation.`,
|
||||||
|
cervix: `The cervix is located inside the body at the end of the vaginal canal, between the vagina and the uterus.
|
||||||
|
|
||||||
|
Tracking how open and how firm the cervix feels like can help determine in which phase of a menstrual cycle you are.
|
||||||
|
|
||||||
|
When you track your cervix as a secondary symptom in addition to tracking your basal body temperature, the app helps you identify in which phase of your cycle you are.`,
|
||||||
|
desire: `Sexual desire can be influenced by the menstrual cycle and its hormonal changes. The app allows you to track the intensity of your sexual desire for your mere information, it is not taken into account for any calculation.`,
|
||||||
|
mucus: `Cervical mucus can help determine in which phase of the menstrual cycle you are.
|
||||||
|
|
||||||
|
When you track your cervical mucus as a secondary symptom in addition to tracking your basal body temperature, the app helps you identify in which phase of your cycle you are.
|
||||||
|
|
||||||
|
The values you enter for both feeling and texture of your cervical mucus are combined by drip into one of five NFP-conforming values, from least to most fertile: t, ∅, f, S, and S+. Please note that drip does not yet support "parenthesis values": According to NFP rules, you can qualify a mucus value by putting parentheses around it, to indicate that it doesn't fully meet the descriptors of one of the five categories, and instead is in between. This functionality will be supported in the future.
|
||||||
|
`,
|
||||||
|
note: `Note allows you to track any extra information you want to save here.`,
|
||||||
|
pain: `Keep track of different kinds of pain you experience. They may be influenced by or have an impact on your menstrual cycles.`,
|
||||||
|
sex: `Did you know that having an orgasm can help release cramps?`,
|
||||||
|
temperature: `One of the body signs you need to track for knowing your fertility status is your body basal temperature. The body temperature changes over the course of a menstrual cycle, it rises after ovulation.
|
||||||
|
|
||||||
|
What is body basal temperature?
|
||||||
|
It's your temperature after laying still for at least 6 hours. For many this is when they are waking up in the morning after sleeping at least 6 hours and before getting up.
|
||||||
|
|
||||||
|
Which thermometer to use?
|
||||||
|
The thermometer must indicate 2 decimal places.
|
||||||
|
|
||||||
|
How to measure?
|
||||||
|
You can either measure rectally, vaginally or orally. If you chose rectal or vaginal measurement you need to measure for at least 3 minutes long. If you chose oral measurement you want to measure for at least 5 minutes long. Pick one way and stick to it.`
|
||||||
|
}
|
||||||
|
export const symptomTitle = {
|
||||||
|
bleeding: "Tracking menstrual bleeding",
|
||||||
|
cervix: "Tracking your cervix",
|
||||||
|
desire: "Tracking sexual desire",
|
||||||
|
mucus: "Tracking cervixal mucus",
|
||||||
|
note: "Notes",
|
||||||
|
pain: "Tracking pain",
|
||||||
|
sex: "Tracking sex and contraceptives",
|
||||||
|
temperature: "Tracking body basal temperature"
|
||||||
|
}
|
||||||
@@ -69,8 +69,20 @@
|
|||||||
F165D5E4692041AD900573C8 /* Prompt-Thin.ttf in Resources */ = {isa = PBXBuildFile; fileRef = CB2405AD4098483C85C1A261 /* Prompt-Thin.ttf */; };
|
F165D5E4692041AD900573C8 /* Prompt-Thin.ttf in Resources */ = {isa = PBXBuildFile; fileRef = CB2405AD4098483C85C1A261 /* Prompt-Thin.ttf */; };
|
||||||
BF3587E45FCA48DEA13183A1 /* fontello.ttf in Resources */ = {isa = PBXBuildFile; fileRef = BB0CB875BB2749F4A46AA5F1 /* fontello.ttf */; };
|
BF3587E45FCA48DEA13183A1 /* fontello.ttf in Resources */ = {isa = PBXBuildFile; fileRef = BB0CB875BB2749F4A46AA5F1 /* fontello.ttf */; };
|
||||||
BBD61F152BE74DD7AED99DFB /* drip-icon-font.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 05154E9AE0EA4BE19F3D9E0B /* drip-icon-font.ttf */; };
|
BBD61F152BE74DD7AED99DFB /* drip-icon-font.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 05154E9AE0EA4BE19F3D9E0B /* drip-icon-font.ttf */; };
|
||||||
|
603395317D2E430CB186563E /* Dosis-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = E877E85FF5D24E5BA0E2FAF6 /* Dosis-Bold.ttf */; };
|
||||||
|
FB7D6EB165EA43ECBA23EC7A /* Dosis-Book.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B2B00C6F14084717B1C21702 /* Dosis-Book.ttf */; };
|
||||||
|
E4809ED51AE54B3EBFDEFDFA /* Dosis-ExtraBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 753D3CC26048498884261EC6 /* Dosis-ExtraBold.ttf */; };
|
||||||
|
DC49A82960AF42C3B9168DB3 /* Dosis-ExtraLight.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D5A481D3A46E4831A3863C68 /* Dosis-ExtraLight.ttf */; };
|
||||||
|
64083B0F5264453FAC04251B /* Dosis-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 97B6A2FB33264796A4D917E0 /* Dosis-Light.ttf */; };
|
||||||
|
9CA5B31E59524C9490DAFD48 /* Dosis-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 2A1E40B398D54045B358F0DB /* Dosis-Medium.ttf */; };
|
||||||
|
082F2BD2BEA046FE8EE58763 /* Dosis-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 2A26A6F601D64F3A8D4A02B0 /* Dosis-SemiBold.ttf */; };
|
||||||
DAA390B1EE7442D88A768596 /* drip-home-icon-font.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 94973D7B7BBA4B0FBE713A0E /* drip-home-icon-font.ttf */; };
|
DAA390B1EE7442D88A768596 /* drip-home-icon-font.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 94973D7B7BBA4B0FBE713A0E /* drip-home-icon-font.ttf */; };
|
||||||
BA7CE1E95B7843D7B0CF85FF /* drip-home-icons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 887F1D52A4684A5280CB79AA /* drip-home-icons.ttf */; };
|
BA7CE1E95B7843D7B0CF85FF /* drip-home-icons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 887F1D52A4684A5280CB79AA /* drip-home-icons.ttf */; };
|
||||||
|
26DAA39DDC6B436E8342239B /* Dosis-medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 096E5227936940FEBA7321FE /* Dosis-medium.ttf */; };
|
||||||
|
B9A5B9946C4C456C823B7641 /* Prompt-ExtraLight.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5E7B0A75F8004C6699B70F86 /* Prompt-ExtraLight.ttf */; };
|
||||||
|
5D921C348AC14944835A4D82 /* OpenSans-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 673C016DDDD74C2F89050279 /* OpenSans-Light.ttf */; };
|
||||||
|
2B66457E5A344222AB41C4FF /* OpenSans-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D05637D8F19344B098982AE5 /* OpenSans-Regular.ttf */; };
|
||||||
|
71D0BCE4666A4AB8A0874B5A /* OpenSans-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 644690BCCEBF41789960B9A2 /* OpenSans-SemiBold.ttf */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@@ -414,8 +426,20 @@
|
|||||||
CB2405AD4098483C85C1A261 /* Prompt-Thin.ttf */ = {isa = PBXFileReference; name = "Prompt-Thin.ttf"; path = "../assets/fonts/Prompt-Thin.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
CB2405AD4098483C85C1A261 /* Prompt-Thin.ttf */ = {isa = PBXFileReference; name = "Prompt-Thin.ttf"; path = "../assets/fonts/Prompt-Thin.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
BB0CB875BB2749F4A46AA5F1 /* fontello.ttf */ = {isa = PBXFileReference; name = "fontello.ttf"; path = "../assets/fonts/fontello.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
BB0CB875BB2749F4A46AA5F1 /* fontello.ttf */ = {isa = PBXFileReference; name = "fontello.ttf"; path = "../assets/fonts/fontello.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
05154E9AE0EA4BE19F3D9E0B /* drip-icon-font.ttf */ = {isa = PBXFileReference; name = "drip-icon-font.ttf"; path = "../assets/fonts/drip-icon-font.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
05154E9AE0EA4BE19F3D9E0B /* drip-icon-font.ttf */ = {isa = PBXFileReference; name = "drip-icon-font.ttf"; path = "../assets/fonts/drip-icon-font.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
E877E85FF5D24E5BA0E2FAF6 /* Dosis-Bold.ttf */ = {isa = PBXFileReference; name = "Dosis-Bold.ttf"; path = "../assets/fonts/Dosis-Bold.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
B2B00C6F14084717B1C21702 /* Dosis-Book.ttf */ = {isa = PBXFileReference; name = "Dosis-Book.ttf"; path = "../assets/fonts/Dosis-Book.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
753D3CC26048498884261EC6 /* Dosis-ExtraBold.ttf */ = {isa = PBXFileReference; name = "Dosis-ExtraBold.ttf"; path = "../assets/fonts/Dosis-ExtraBold.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
D5A481D3A46E4831A3863C68 /* Dosis-ExtraLight.ttf */ = {isa = PBXFileReference; name = "Dosis-ExtraLight.ttf"; path = "../assets/fonts/Dosis-ExtraLight.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
97B6A2FB33264796A4D917E0 /* Dosis-Light.ttf */ = {isa = PBXFileReference; name = "Dosis-Light.ttf"; path = "../assets/fonts/Dosis-Light.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
2A1E40B398D54045B358F0DB /* Dosis-Medium.ttf */ = {isa = PBXFileReference; name = "Dosis-Medium.ttf"; path = "../assets/fonts/Dosis-Medium.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
2A26A6F601D64F3A8D4A02B0 /* Dosis-SemiBold.ttf */ = {isa = PBXFileReference; name = "Dosis-SemiBold.ttf"; path = "../assets/fonts/Dosis-SemiBold.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
94973D7B7BBA4B0FBE713A0E /* drip-home-icon-font.ttf */ = {isa = PBXFileReference; name = "drip-home-icon-font.ttf"; path = "../assets/fonts/drip-home-icon-font.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
94973D7B7BBA4B0FBE713A0E /* drip-home-icon-font.ttf */ = {isa = PBXFileReference; name = "drip-home-icon-font.ttf"; path = "../assets/fonts/drip-home-icon-font.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
887F1D52A4684A5280CB79AA /* drip-home-icons.ttf */ = {isa = PBXFileReference; name = "drip-home-icons.ttf"; path = "../assets/fonts/drip-home-icons.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
887F1D52A4684A5280CB79AA /* drip-home-icons.ttf */ = {isa = PBXFileReference; name = "drip-home-icons.ttf"; path = "../assets/fonts/drip-home-icons.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
096E5227936940FEBA7321FE /* Dosis-medium.ttf */ = {isa = PBXFileReference; name = "Dosis-medium.ttf"; path = "../assets/fonts/Dosis-medium.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
5E7B0A75F8004C6699B70F86 /* Prompt-ExtraLight.ttf */ = {isa = PBXFileReference; name = "Prompt-ExtraLight.ttf"; path = "../assets/fonts/Prompt-ExtraLight.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
673C016DDDD74C2F89050279 /* OpenSans-Light.ttf */ = {isa = PBXFileReference; name = "OpenSans-Light.ttf"; path = "../assets/fonts/OpenSans-Light.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
D05637D8F19344B098982AE5 /* OpenSans-Regular.ttf */ = {isa = PBXFileReference; name = "OpenSans-Regular.ttf"; path = "../assets/fonts/OpenSans-Regular.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
|
644690BCCEBF41789960B9A2 /* OpenSans-SemiBold.ttf */ = {isa = PBXFileReference; name = "OpenSans-SemiBold.ttf"; path = "../assets/fonts/OpenSans-SemiBold.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -724,8 +748,20 @@
|
|||||||
CB2405AD4098483C85C1A261 /* Prompt-Thin.ttf */,
|
CB2405AD4098483C85C1A261 /* Prompt-Thin.ttf */,
|
||||||
BB0CB875BB2749F4A46AA5F1 /* fontello.ttf */,
|
BB0CB875BB2749F4A46AA5F1 /* fontello.ttf */,
|
||||||
05154E9AE0EA4BE19F3D9E0B /* drip-icon-font.ttf */,
|
05154E9AE0EA4BE19F3D9E0B /* drip-icon-font.ttf */,
|
||||||
|
E877E85FF5D24E5BA0E2FAF6 /* Dosis-Bold.ttf */,
|
||||||
|
B2B00C6F14084717B1C21702 /* Dosis-Book.ttf */,
|
||||||
|
753D3CC26048498884261EC6 /* Dosis-ExtraBold.ttf */,
|
||||||
|
D5A481D3A46E4831A3863C68 /* Dosis-ExtraLight.ttf */,
|
||||||
|
97B6A2FB33264796A4D917E0 /* Dosis-Light.ttf */,
|
||||||
|
2A1E40B398D54045B358F0DB /* Dosis-Medium.ttf */,
|
||||||
|
2A26A6F601D64F3A8D4A02B0 /* Dosis-SemiBold.ttf */,
|
||||||
94973D7B7BBA4B0FBE713A0E /* drip-home-icon-font.ttf */,
|
94973D7B7BBA4B0FBE713A0E /* drip-home-icon-font.ttf */,
|
||||||
887F1D52A4684A5280CB79AA /* drip-home-icons.ttf */,
|
887F1D52A4684A5280CB79AA /* drip-home-icons.ttf */,
|
||||||
|
096E5227936940FEBA7321FE /* Dosis-medium.ttf */,
|
||||||
|
5E7B0A75F8004C6699B70F86 /* Prompt-ExtraLight.ttf */,
|
||||||
|
673C016DDDD74C2F89050279 /* OpenSans-Light.ttf */,
|
||||||
|
D05637D8F19344B098982AE5 /* OpenSans-Regular.ttf */,
|
||||||
|
644690BCCEBF41789960B9A2 /* OpenSans-SemiBold.ttf */,
|
||||||
);
|
);
|
||||||
name = Resources;
|
name = Resources;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -761,9 +797,9 @@
|
|||||||
13B07F8E1A680F5B00A75B9A /* Resources */,
|
13B07F8E1A680F5B00A75B9A /* Resources */,
|
||||||
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
|
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
|
||||||
2B572382D4504B8FB4B9D251 /* Embed Frameworks */,
|
2B572382D4504B8FB4B9D251 /* Embed Frameworks */,
|
||||||
0909337B049F4ECB933412B2 /* Build NodeJS Mobile Native Modules */,
|
FF1D4199225D4DA692E5AEB6 /* Build NodeJS Mobile Native Modules */,
|
||||||
B6AAD324BDBA4E92AB5627B3 /* Sign NodeJS Mobile Native Modules */,
|
BF4F9DB28A984C43A497C8E6 /* Sign NodeJS Mobile Native Modules */,
|
||||||
3DE3CFA1BEC54E9D9B5B9D47 /* Remove NodeJS Mobile Framework Simulator Strips */,
|
6292723A374D49FDB7CF4114 /* Remove NodeJS Mobile Framework Simulator Strips */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@@ -1202,8 +1238,20 @@
|
|||||||
F165D5E4692041AD900573C8 /* Prompt-Thin.ttf in Resources */,
|
F165D5E4692041AD900573C8 /* Prompt-Thin.ttf in Resources */,
|
||||||
BF3587E45FCA48DEA13183A1 /* fontello.ttf in Resources */,
|
BF3587E45FCA48DEA13183A1 /* fontello.ttf in Resources */,
|
||||||
BBD61F152BE74DD7AED99DFB /* drip-icon-font.ttf in Resources */,
|
BBD61F152BE74DD7AED99DFB /* drip-icon-font.ttf in Resources */,
|
||||||
|
603395317D2E430CB186563E /* Dosis-Bold.ttf in Resources */,
|
||||||
|
FB7D6EB165EA43ECBA23EC7A /* Dosis-Book.ttf in Resources */,
|
||||||
|
E4809ED51AE54B3EBFDEFDFA /* Dosis-ExtraBold.ttf in Resources */,
|
||||||
|
DC49A82960AF42C3B9168DB3 /* Dosis-ExtraLight.ttf in Resources */,
|
||||||
|
64083B0F5264453FAC04251B /* Dosis-Light.ttf in Resources */,
|
||||||
|
9CA5B31E59524C9490DAFD48 /* Dosis-Medium.ttf in Resources */,
|
||||||
|
082F2BD2BEA046FE8EE58763 /* Dosis-SemiBold.ttf in Resources */,
|
||||||
DAA390B1EE7442D88A768596 /* drip-home-icon-font.ttf in Resources */,
|
DAA390B1EE7442D88A768596 /* drip-home-icon-font.ttf in Resources */,
|
||||||
BA7CE1E95B7843D7B0CF85FF /* drip-home-icons.ttf in Resources */,
|
BA7CE1E95B7843D7B0CF85FF /* drip-home-icons.ttf in Resources */,
|
||||||
|
26DAA39DDC6B436E8342239B /* Dosis-medium.ttf in Resources */,
|
||||||
|
B9A5B9946C4C456C823B7641 /* Prompt-ExtraLight.ttf in Resources */,
|
||||||
|
5D921C348AC14944835A4D82 /* OpenSans-Light.ttf in Resources */,
|
||||||
|
2B66457E5A344222AB41C4FF /* OpenSans-Regular.ttf in Resources */,
|
||||||
|
71D0BCE4666A4AB8A0874B5A /* OpenSans-SemiBold.ttf in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -1253,7 +1301,7 @@
|
|||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh";
|
shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh";
|
||||||
};
|
};
|
||||||
0909337B049F4ECB933412B2 /* Build NodeJS Mobile Native Modules */ = {
|
FF1D4199225D4DA692E5AEB6 /* Build NodeJS Mobile Native Modules */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
@@ -1321,7 +1369,7 @@ fi
|
|||||||
popd
|
popd
|
||||||
";
|
";
|
||||||
};
|
};
|
||||||
B6AAD324BDBA4E92AB5627B3 /* Sign NodeJS Mobile Native Modules */ = {
|
BF4F9DB28A984C43A497C8E6 /* Sign NodeJS Mobile Native Modules */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
@@ -1381,7 +1429,7 @@ find \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -path \"*/*.framework/*\" -del
|
|||||||
find \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.framework\" -type d -delete
|
find \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.framework\" -type d -delete
|
||||||
";
|
";
|
||||||
};
|
};
|
||||||
3DE3CFA1BEC54E9D9B5B9D47 /* Remove NodeJS Mobile Framework Simulator Strips */ = {
|
6292723A374D49FDB7CF4114 /* Remove NodeJS Mobile Framework Simulator Strips */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
|||||||
@@ -72,8 +72,20 @@
|
|||||||
<string>Prompt-Thin.ttf</string>
|
<string>Prompt-Thin.ttf</string>
|
||||||
<string>fontello.ttf</string>
|
<string>fontello.ttf</string>
|
||||||
<string>drip-icon-font.ttf</string>
|
<string>drip-icon-font.ttf</string>
|
||||||
|
<string>Dosis-Bold.ttf</string>
|
||||||
|
<string>Dosis-Book.ttf</string>
|
||||||
|
<string>Dosis-ExtraBold.ttf</string>
|
||||||
|
<string>Dosis-ExtraLight.ttf</string>
|
||||||
|
<string>Dosis-Light.ttf</string>
|
||||||
|
<string>Dosis-Medium.ttf</string>
|
||||||
|
<string>Dosis-SemiBold.ttf</string>
|
||||||
<string>drip-home-icon-font.ttf</string>
|
<string>drip-home-icon-font.ttf</string>
|
||||||
<string>drip-home-icons.ttf</string>
|
<string>drip-home-icons.ttf</string>
|
||||||
|
<string>Dosis-medium.ttf</string>
|
||||||
|
<string>Prompt-ExtraLight.ttf</string>
|
||||||
|
<string>OpenSans-Light.ttf</string>
|
||||||
|
<string>OpenSans-Regular.ttf</string>
|
||||||
|
<string>OpenSans-SemiBold.ttf</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import {
|
|||||||
updateCycleStartsForAllCycleDays
|
updateCycleStartsForAllCycleDays
|
||||||
} from '../../db'
|
} from '../../db'
|
||||||
import getColumnNamesForCsv from './get-csv-column-names'
|
import getColumnNamesForCsv from './get-csv-column-names'
|
||||||
|
import { LocalDate } from 'js-joda'
|
||||||
|
import labels from '../../i18n/en/settings'
|
||||||
|
|
||||||
export default async function importCsv(csv, deleteFirst) {
|
export default async function importCsv(csv, deleteFirst) {
|
||||||
const parseFuncs = {
|
const parseFuncs = {
|
||||||
@@ -48,6 +50,7 @@ export default async function importCsv(csv, deleteFirst) {
|
|||||||
|
|
||||||
//remove symptoms where all fields are null
|
//remove symptoms where all fields are null
|
||||||
putNullForEmptySymptoms(cycleDays)
|
putNullForEmptySymptoms(cycleDays)
|
||||||
|
throwIfFutureData(cycleDays)
|
||||||
|
|
||||||
if (deleteFirst) {
|
if (deleteFirst) {
|
||||||
tryToImportWithDelete(cycleDays)
|
tryToImportWithDelete(cycleDays)
|
||||||
@@ -87,4 +90,15 @@ function getDbType(modelProperties, path) {
|
|||||||
if (path.length === 1) return modelProperties[path[0]].type
|
if (path.length === 1) return modelProperties[path[0]].type
|
||||||
const modelName = modelProperties[path[0]].objectType
|
const modelName = modelProperties[path[0]].objectType
|
||||||
return getDbType(schema[modelName], path.slice(1))
|
return getDbType(schema[modelName], path.slice(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
function throwIfFutureData(cycleDays) {
|
||||||
|
const today = LocalDate.now().toString()
|
||||||
|
for (const i in cycleDays) {
|
||||||
|
const day = cycleDays[i]
|
||||||
|
// notes are allowed for future dates but everything else isn't
|
||||||
|
if (day.date > today && Object.keys(day).some(symptom => symptom != 'date' && symptom != 'note')) {
|
||||||
|
throw new Error(labels.import.errors.futureEdit)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+39
-20
@@ -13,8 +13,10 @@ export const shadesOfRed = [
|
|||||||
export const cycleDayColor = '#29287f'
|
export const cycleDayColor = '#29287f'
|
||||||
export const periodColor = '#802249'
|
export const periodColor = '#802249'
|
||||||
|
|
||||||
const fontRegular = 'Prompt-Light'
|
const headerFont = 'Prompt-ExtraLight'
|
||||||
const fontLight = 'Prompt-Thin'
|
|
||||||
|
const textFont = 'OpenSans-Light'
|
||||||
|
const textFontBold = 'OpenSans-SemiBold'
|
||||||
|
|
||||||
const regularSize = 16
|
const regularSize = 16
|
||||||
const hintSize = 14
|
const hintSize = 14
|
||||||
@@ -27,18 +29,15 @@ const colorInActive = '#666666'
|
|||||||
export default StyleSheet.create({
|
export default StyleSheet.create({
|
||||||
appText: {
|
appText: {
|
||||||
color: 'black',
|
color: 'black',
|
||||||
fontFamily: fontRegular,
|
fontFamily: textFont,
|
||||||
fontSize: regularSize
|
fontSize: regularSize,
|
||||||
},
|
letterSpacing: 0.5
|
||||||
appTextLight: {
|
|
||||||
color: 'black',
|
|
||||||
fontFamily: fontLight,
|
|
||||||
fontSize: regularSize
|
|
||||||
},
|
},
|
||||||
actionHint: {
|
actionHint: {
|
||||||
color: secondaryColor,
|
color: secondaryColor,
|
||||||
fontFamily: fontRegular,
|
fontFamily: textFont,
|
||||||
fontSize: hintSize,
|
fontSize: hintSize,
|
||||||
|
fontWeight: 'bold',
|
||||||
margin: defaultIndentation
|
margin: defaultIndentation
|
||||||
},
|
},
|
||||||
paragraph: {
|
paragraph: {
|
||||||
@@ -46,6 +45,7 @@ export default StyleSheet.create({
|
|||||||
},
|
},
|
||||||
emphasis: {
|
emphasis: {
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
|
fontFamily: textFontBold
|
||||||
},
|
},
|
||||||
link: {
|
link: {
|
||||||
color: cycleDayColor,
|
color: cycleDayColor,
|
||||||
@@ -69,20 +69,20 @@ export default StyleSheet.create({
|
|||||||
},
|
},
|
||||||
dateHeader: {
|
dateHeader: {
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
fontFamily: fontLight,
|
fontFamily: headerFont,
|
||||||
color: fontOnPrimaryColor,
|
color: fontOnPrimaryColor,
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
},
|
},
|
||||||
headerText: {
|
headerText: {
|
||||||
fontSize: 30,
|
fontSize: 30,
|
||||||
fontFamily: fontLight,
|
fontFamily: headerFont,
|
||||||
color: fontOnPrimaryColor,
|
color: fontOnPrimaryColor,
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
paddingBottom: 4
|
paddingBottom: 4
|
||||||
},
|
},
|
||||||
accentCircle: {
|
accentCircle: {
|
||||||
borderColor: secondaryColor,
|
borderColor: secondaryColor,
|
||||||
borderWidth: 0.5,
|
borderWidth: 1,
|
||||||
width: 40,
|
width: 40,
|
||||||
height: 40,
|
height: 40,
|
||||||
borderRadius: 100,
|
borderRadius: 100,
|
||||||
@@ -133,7 +133,7 @@ export default StyleSheet.create({
|
|||||||
},
|
},
|
||||||
homeCircle: {
|
homeCircle: {
|
||||||
borderRadius: 100,
|
borderRadius: 100,
|
||||||
borderWidth: 0.7,
|
borderWidth: 2.3,
|
||||||
width: 80,
|
width: 80,
|
||||||
height: 80,
|
height: 80,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@@ -155,7 +155,7 @@ export default StyleSheet.create({
|
|||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
color: fontOnPrimaryColor,
|
color: fontOnPrimaryColor,
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
fontFamily: fontLight
|
fontFamily: headerFont
|
||||||
},
|
},
|
||||||
symptomViewHeading: {
|
symptomViewHeading: {
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
@@ -229,6 +229,9 @@ export default StyleSheet.create({
|
|||||||
navigationArrow: {
|
navigationArrow: {
|
||||||
padding: 20
|
padding: 20
|
||||||
},
|
},
|
||||||
|
hiddenIcon: {
|
||||||
|
padding: 20
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
backgroundColor: primaryColor,
|
backgroundColor: primaryColor,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@@ -243,10 +246,11 @@ export default StyleSheet.create({
|
|||||||
},
|
},
|
||||||
menuText: {
|
menuText: {
|
||||||
color: fontOnPrimaryColor,
|
color: fontOnPrimaryColor,
|
||||||
fontFamily: fontLight
|
fontFamily: headerFont
|
||||||
},
|
},
|
||||||
menuTextInActive: {
|
menuTextInActive: {
|
||||||
color: colorInActive
|
color: colorInActive,
|
||||||
|
fontFamily: headerFont
|
||||||
},
|
},
|
||||||
temperatureTextInput: {
|
temperatureTextInput: {
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
@@ -272,18 +276,22 @@ export default StyleSheet.create({
|
|||||||
marginTop: defaultTopMargin,
|
marginTop: defaultTopMargin,
|
||||||
marginHorizontal: defaultIndentation,
|
marginHorizontal: defaultIndentation,
|
||||||
padding: 7,
|
padding: 7,
|
||||||
|
fontFamily: textFont
|
||||||
},
|
},
|
||||||
settingsSegmentLast: {
|
settingsSegmentLast: {
|
||||||
marginBottom: defaultTopMargin,
|
marginBottom: defaultTopMargin,
|
||||||
},
|
},
|
||||||
settingsSegmentTitle: {
|
settingsSegmentTitle: {
|
||||||
fontWeight: 'bold'
|
fontWeight: 'bold',
|
||||||
|
fontFamily: textFontBold
|
||||||
},
|
},
|
||||||
settingsButton: {
|
settingsButton: {
|
||||||
backgroundColor: secondaryColor,
|
|
||||||
padding: 10,
|
padding: 10,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
margin: 10,
|
margin: 10
|
||||||
|
},
|
||||||
|
settingsButtonAccent: {
|
||||||
|
backgroundColor: secondaryColor
|
||||||
},
|
},
|
||||||
settingsButtonDisabled: {
|
settingsButtonDisabled: {
|
||||||
backgroundColor: colorInActive
|
backgroundColor: colorInActive
|
||||||
@@ -291,6 +299,10 @@ export default StyleSheet.create({
|
|||||||
settingsButtonText: {
|
settingsButtonText: {
|
||||||
color: fontOnPrimaryColor
|
color: fontOnPrimaryColor
|
||||||
},
|
},
|
||||||
|
settingsButtonSecondaryText: {
|
||||||
|
color: secondaryColor
|
||||||
|
|
||||||
|
},
|
||||||
statsRow: {
|
statsRow: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
width: '100%'
|
width: '100%'
|
||||||
@@ -402,6 +414,9 @@ export default StyleSheet.create({
|
|||||||
marginTop: 20,
|
marginTop: 20,
|
||||||
color: 'grey'
|
color: 'grey'
|
||||||
},
|
},
|
||||||
|
infoButton: {
|
||||||
|
paddingVertical: 20
|
||||||
|
},
|
||||||
licensePage: {
|
licensePage: {
|
||||||
paddingVertical: 20,
|
paddingVertical: 20,
|
||||||
paddingHorizontal: 10
|
paddingHorizontal: 10
|
||||||
@@ -442,5 +457,9 @@ export const iconStyles = {
|
|||||||
infoInHeading: {
|
infoInHeading: {
|
||||||
marginRight: 5,
|
marginRight: 5,
|
||||||
color: 'black'
|
color: 'black'
|
||||||
|
},
|
||||||
|
hiddenIcon: {
|
||||||
|
size: 20,
|
||||||
|
display: 'none'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user