diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e5651b705514f32cce32a3f0ede80dc21af94b09..695c101f9790ff7536b21a740ff0c0e0751d5428 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,25 +14,25 @@ So good to see you here, hello :wave\_tone1: :wave\_tone2: :wave\_tone3: :wave\_ ## TL;DR -You just want to say hello? Send us a [nice email](mailto:bl00dyhealth@mailbox.org?Subject=Nice%20incoming%20mail) :postbox: or tweet :bird: at us @bl00dyhealth. +You just want to say hello? Send us a [nice email](mailto:bl00dyhealth@mailbox.org?Subject=Nice%20incoming%20mail) :postbox: or tweet :bird: at us [@bl00dyhealth](https://twitter.com/bl00dyhealth). ## What should I know before I get started? We have prepared something for **you**: check out our [README](https://gitlab.com/bloodyhealth/drip/blob/master/README.md) for more information on how to set up and install everything you'll need. [Ping us](mailto:bl00dyhealth@mailbox.org) if you could need some help :helmet\_with\_cross: ! -Let us know if you want to suggest improvements for the README. +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? ### Your First Code Contribution 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 create a branch based on that issue. -This allows us to keep track of the issues that are related to an existing branch, which tells everyone "somebody's on it". +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`. +This allows us to keep track of the issues that are related to an existing branch and tells everyone "somebody is working on it". If you want to open a merge request, yeah :tada: exciting! We are using a template for merge requests to make sure we explain what we have done and why. -Keep in mind that people who will review your merge request are more motivated to do so when the merge request is well explained. +Keep in mind that people who will review your merge request are more motivated to do so when the merge request is well explained and ideally not too big. ### Reporting Bugs @@ -48,7 +48,7 @@ Do you have suggestions for enhancing the app or for cleaning up some code? Fant 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: 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 the title taken from the subject line and the description text for the issue taken from the body of your email. ### Thank you diff --git a/README.md b/README.md index 1f66c451740d472433f74a7f36a7262a4008c8f0..33c2411de527d38a07dd89bf42f0e40a111f1618 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Bloody Health Cycle Tracker -A menstrual cycle tracking app that's open-source and leaves your data on your phone. Use it to track your menstrual cycle 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! ## Development setup 1. Install [Android Studio](https://developer.android.com/studio/) - you'll need it to install some dependencies. @@ -19,11 +19,11 @@ A menstrual cycle tracking app that's open-source and leaves your data on your p cd drip ``` -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 check out nodejs-mobile repository 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 (or make sure it's already running, you should see a phone on your screen) or [set your physical device like your 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 in Android Studio and run `npm install` +1. Open a terminal and run `npm install` 1. Run `npm run android` @@ -33,6 +33,22 @@ A menstrual cycle tracking app that's open-source and leaves your data on your p 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. +## Java problems on macOS + +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: + + ``` + 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. + +## Windows 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). + ## Tests You can run the tests with `npm test`. diff --git a/android/app/src/main/assets/fonts/drip-home-icon-font.ttf b/android/app/src/main/assets/fonts/drip-home-icon-font.ttf new file mode 100644 index 0000000000000000000000000000000000000000..3c7621a677e16fbb3f65109579c2a109d47dd43f Binary files /dev/null and b/android/app/src/main/assets/fonts/drip-home-icon-font.ttf differ diff --git a/android/app/src/main/assets/fonts/drip-home-icons.ttf b/android/app/src/main/assets/fonts/drip-home-icons.ttf new file mode 100644 index 0000000000000000000000000000000000000000..586a697bdc42f7a6d81cb2250b2eca0b5db86c25 Binary files /dev/null and b/android/app/src/main/assets/fonts/drip-home-icons.ttf differ diff --git a/assets/drip-home-icons.js b/assets/drip-home-icons.js new file mode 100644 index 0000000000000000000000000000000000000000..dbee9619a1f19bbf160818e764ebd61fe4f639a8 --- /dev/null +++ b/assets/drip-home-icons.js @@ -0,0 +1,4 @@ +import { createIconSetFromFontello } from 'react-native-vector-icons' +import fontelloConfig from './fonts/config-drip-home-icons.json' + +export default createIconSetFromFontello(fontelloConfig) \ No newline at end of file diff --git a/assets/drip-icons.js b/assets/drip-icons.js index d7e7ea916e3e1fb1d744225c4850bfa3759feca7..e8b539851ddf09f4b7b57061f2ecb34d8f312cfc 100644 --- a/assets/drip-icons.js +++ b/assets/drip-icons.js @@ -1,4 +1,4 @@ import { createIconSetFromFontello } from 'react-native-vector-icons' -import fontelloConfig from './fonts/config.json' +import fontelloConfig from './fonts/config-drip-icon-font.json' export default createIconSetFromFontello(fontelloConfig) \ No newline at end of file diff --git a/assets/fonts/config-drip-home-icons.json b/assets/fonts/config-drip-home-icons.json new file mode 100644 index 0000000000000000000000000000000000000000..37e722ffb57baaa0a45d61e7d8e4f7c91f3b1d83 --- /dev/null +++ b/assets/fonts/config-drip-home-icons.json @@ -0,0 +1,38 @@ +{ + "name": "drip-home-icons", + "css_prefix_text": "icon-", + "css_use_suffix": false, + "hinting": true, + "units_per_em": 1000, + "ascent": 850, + "glyphs": [ + { + "uid": "3b01a0a3395a3845dd3194e1a7ad96ff", + "css": "drop", + "code": 59393, + "src": "custom_icons", + "selected": true, + "svg": { + "path": "M438.7 21.6L432.4 26.2C419.8 35.5 406.3 43.6 394.5 54.4 366.3 80.3 338.7 106.7 311.8 133.8 245.3 200.8 184.2 272.4 129.6 349.5 98.6 393.2 69.3 438.1 47.8 487.9 33.4 521.1 22.7 555.5 20.7 592.3 18.1 640.4 25.6 687 40.6 732.5 66.6 811.5 113.1 875.4 184.7 919.4H184.7C257.5 964 337.2 982.1 422.1 977.4 472.7 974.6 521.1 962.2 566.4 939.3H566.4C666.3 888.9 731.2 808.4 763.4 701.8V701.7C776.9 657 780.7 611.3 772.8 565V565C765.3 521.1 748.6 480.9 726.4 443H726.4C697.3 393.4 660.4 350 623.3 307.2 587.2 265.5 551 224.1 516.3 181.5 489.9 149.1 468 113.9 454.2 74.5V74.5C449.3 60.4 445.7 45.6 440.9 29.1ZM435.6 29.9C440.4 46.3 444.1 61.2 449.1 75.6 463.2 115.7 485.4 151.5 512.1 184.2 546.9 226.9 583.1 268.3 619.2 310 656.3 352.8 692.9 395.9 721.7 445 743.7 482.5 760.1 522 767.4 565.2 775.2 610.7 771.5 655.4 758.2 699.5 726.4 804.9 662.7 884 564 933.8 519.3 956.3 471.7 968.5 421.8 971.3 337.9 975.9 259.4 958.1 187.5 914 117 870.8 71.4 808.1 45.7 730.1 31 685.2 23.6 639.2 26.2 591.9 28 555.9 38.5 522.2 52.7 489.3 74 440.1 103.1 395.5 134 351.9 188.5 275.1 249.3 203.7 315.6 137 342.5 109.9 370.1 83.5 398.2 57.7 409.4 47.3 422.7 39.3 435.6 29.9Z", + "width": 798 + }, + "search": [ + "drop2px" + ] + }, + { + "uid": "ce239ac4aab54b2846603081216e0e84", + "css": "circle", + "code": 59392, + "src": "custom_icons", + "selected": true, + "svg": { + "path": "M271.2 13.4C284.9 14 294.1 18 300.4 24.3 306.6 30.6 310.5 40 310.8 53.8 311.3 70.7 311 87.8 310.9 105.1H310.6V115.3C310.6 136.2 310.7 157 310.5 177.9V177.9 177.9C310.5 181.3 309.6 182.3 309.1 182.7 308.6 183.1 307.8 183.7 304.9 183H304.8L304.8 182.9C303.1 182.5 302.7 182.1 302.6 182 302.6 181.9 302.4 182 302.4 180.7V180.6 180.6C302.6 148.3 303.1 116.1 303.5 83.7V83.7 83.7C303.5 75.9 304 67.5 303.7 58.7L303.7 58.7V58.7C303.3 48.2 300.6 38.7 294 31.8 287.4 24.8 277.9 21.7 267.3 21H267.3C254 20.1 241.1 20.8 228.8 20.9 201.1 21.2 173.5 21.6 145.9 21.7H145.9C142.8 21.7 141.8 21 141.4 20.7 141.1 20.4 140.6 20 140.6 17.6 140.5 15.2 141.1 14.6 141.4 14.3 141.6 14 142.1 13.5 144.5 13.5H144.5 144.6C174.1 13.4 203.5 13.4 233.1 13.4 246.2 13.4 258.9 12.9 271.2 13.4ZM742.5 56.6C743.2 56.5 745 56.4 747.9 58.1L747.9 58.1H747.9C834.2 107.1 900.3 175.2 946.7 262.8 976.6 319.3 995 379.2 1001.3 442.6 1012.7 556.6 987.8 661.9 925.8 758.2 851.7 873.1 748.8 947.6 614.9 974.9H614.9C444.5 1009.7 294.6 969.3 167.2 849.5 87.6 774.6 39.5 682.3 21.1 574.3V574.3C16.6 547.8 15.1 520.6 13.2 502.8 16.2 355.8 64.4 240.3 159 142.8 183.4 117.7 210.5 96 239.6 76.1 243 73.8 244.9 73.8 245.7 73.9 246.5 74 246.8 74.3 247.3 75 248.6 77 248.6 77.8 248.3 78.8 248 79.8 247 81.6 243.9 83.7 148.8 147.5 82.9 234.5 46.5 342.9V342.9C27 401.4 19 461.9 23.2 523.4V523.4C31.2 640.8 76.8 742.9 157.6 828 231.5 905.8 322.1 955.6 428.4 971.8 584.4 995.4 724.4 954.8 841.1 847.3H841.1C922.7 772.1 972.8 677.6 989.5 568V568C1013.4 410.6 967.3 272.1 859.7 155.8 826 119.4 786.8 89.8 744 65.6L744 65.6 744 65.5C740.6 63.7 739.6 62.1 739.5 61.6 739.3 61.1 739 60.9 740.2 58.7L740.2 58.7 740.2 58.7C741.2 56.9 741.7 56.8 742.5 56.6Z", + "width": 1017 + }, + "search": [ + "circle2px" + ] + } + ] +} \ No newline at end of file diff --git a/assets/fonts/config.json b/assets/fonts/config-drip-icon-font.json similarity index 100% rename from assets/fonts/config.json rename to assets/fonts/config-drip-icon-font.json diff --git a/assets/fonts/drip-home-icons.ttf b/assets/fonts/drip-home-icons.ttf new file mode 100644 index 0000000000000000000000000000000000000000..586a697bdc42f7a6d81cb2250b2eca0b5db86c25 Binary files /dev/null and b/assets/fonts/drip-home-icons.ttf differ diff --git a/assets/home-circle.js b/assets/home-circle.js deleted file mode 100644 index 50034fef92e70487925cb64b9ac54e2ce14d1907..0000000000000000000000000000000000000000 --- a/assets/home-circle.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react' -import { Group as G, Shape, Transform } from 'react-native/Libraries/ART/ReactNativeART' - -const circle = "m 43,12 c -27.59196,17.168 -43.07131,51.34003 -37.74253,83.40217 4.65932,33.15379 31.20731,61.73087 63.90256,68.88417 30.61528,7.42782 64.74574,-4.34916 84.21519,-29.12633 21.61526,-26.12878 24.59233,-65.67005 7.10091,-94.73675 -7.6702,-13.15691 -18.99314,-24.14869 -32.37613,-31.41826" -const pointyPart = "m 43,32 c -0.0509,-6.38363 0.10148,-12.77739 -0.0757,-19.15472 -1.02117,-5.71918 -7.64221,-3.72111 -11.7681,-4.08628 -3.79908,0 -7.59816,0 -11.39724,0" -const color = "#1E0B7A" - -export default function CycleDayIcon(props) { - return ( - <G transform={new Transform().scale(props.scale)}> - <Shape stroke={color} strokeWidth={props.strokeWidth} d={circle}/> - <Shape stroke={color} strokeWidth={props.strokeWidth} d={pointyPart}/> - </G> - ) -} \ No newline at end of file diff --git a/assets/home-drop.js b/assets/home-drop.js deleted file mode 100644 index 6bde404d562bf0768ef576cf0feab1e127031998..0000000000000000000000000000000000000000 --- a/assets/home-drop.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react' -import { Surface, Group as G, Shape, Transform } from 'react-native/Libraries/ART/ReactNativeART' - -export default function HomeDropIcon(props) { - return ( - <Surface width={83} height={103.56}> - <G transform={new Transform().scale(props.scale).translate(-345, -330)}> - <Shape stroke="#89113E" strokeWidth="2" d="M492.723,455.44 - c-5.531,39.136-41.74,66.377-80.876,60.847C372.712,510.757,351,483.64,351,444.115c0-37.555,79.739-114.673,80.391-105.969 - C434.248,376.247,499.843,405.058,492.723,455.44z"/> - </G> - </Surface> - ) -} diff --git a/components/chart/chart.js b/components/chart/chart.js index dc3be06cfcf511d85267a81d66960981838f6f92..c11db6bfee008d2722a9f97314109ab41358a94f 100644 --- a/components/chart/chart.js +++ b/components/chart/chart.js @@ -2,18 +2,18 @@ import React, { Component } from 'react' import { View, FlatList, ActivityIndicator } from 'react-native' import range from 'date-range' import { LocalDate } from 'js-joda' -import { Surface } from 'react-native/Libraries/ART/ReactNativeART' import { makeYAxisLabels, makeHorizontalGrid } from './y-axis' import nfpLines from './nfp-lines' import DayColumn from './day-column' import { getCycleDaysSortedByDate, getAmountOfCycleDays } from '../../db' import styles from './styles' +import { cycleDayColor } from '../../styles' import { scaleObservable } from '../../local-storage' import config from '../../config' import AppText from '../app-text' import { shared as labels } from '../../i18n/en/labels' import DripIcon from '../../assets/drip-icons' -import CycleDayIcon from '../../assets/home-circle' +import DripHomeIcon from '../../assets/drip-home-icons' import nothingChanged from '../../db/db-unchanged' const symptomIcons = { @@ -163,20 +163,16 @@ export default class CycleChart extends Component { {makeYAxisLabels(this.columnHeight)} </View> <View style={[styles.yAxis, { alignItems: 'center', justifyContent: 'center' }]}> - <Surface - width={styles.yAxis.width * 0.8} - height={styles.yAxis.width * 0.8} - > - <CycleDayIcon - strokeWidth={10} - scale={0.12} - /> - </Surface> + <DripHomeIcon + name="circle" + size={styles.yAxis.width - 7} + color={cycleDayColor} + /> <AppText style={[ styles.column.label.date, styles.yAxisLabels.dateLabel ]}> - {labels.date} + {labels.date.toLowerCase()} </AppText> </View> </View>} diff --git a/components/cycle-day/cycle-day-overview.js b/components/cycle-day/cycle-day-overview.js index 92b8ac0267a7f540732323af28abd0211f0db49a..88de356047e2f3c7ba53ee93f2ccfe4805a68015 100644 --- a/components/cycle-day/cycle-day-overview.js +++ b/components/cycle-day/cycle-day-overview.js @@ -261,7 +261,7 @@ class SymptomBox extends Component { <View style={[styles.symptomBox, boxActive, disabledStyle]}> <DripIcon name={this.props.iconName} size={50} color={d ? 'white' : 'black'}/> <AppText style={[textActive, disabledStyle]}> - {this.props.title} + {this.props.title.toLowerCase()} </AppText> </View> <View style={[styles.symptomDataBox, disabledStyle]}> diff --git a/components/cycle-day/symptoms/action-button-footer.js b/components/cycle-day/symptoms/action-button-footer.js index 34585509f421377e22411f4253592c378adb73c4..076b0bb827c5d9eca7d07c5ef0804dfa5f92699b 100644 --- a/components/cycle-day/symptoms/action-button-footer.js +++ b/components/cycle-day/symptoms/action-button-footer.js @@ -64,7 +64,6 @@ export default class ActionButtonFooter extends Component { ) : iconStyles.menuIcon - return ( <TouchableOpacity onPress={action} @@ -74,7 +73,7 @@ export default class ActionButtonFooter extends Component { > <Icon name={icon} {...iconStyle} /> <Text style={textStyle}> - {title} + {title.toLowerCase()} </Text> </TouchableOpacity> ) diff --git a/components/header/cycle-day.js b/components/header/cycle-day.js index a78cf1b11939bba1f8616e3b0bae46597b565ac9..b5dbbdfb2b15d615aad69c5c3144eb09950c59a4 100644 --- a/components/header/cycle-day.js +++ b/components/header/cycle-day.js @@ -2,11 +2,19 @@ import React from 'react' import { View, Text} from 'react-native' +import { LocalDate } from 'js-joda' import moment from 'moment' import styles from '../../styles' import NavigationArrow from './navigation-arrow' -export default function CycleDayHeader(props) { +const FormattedDate = ({ date }) => { + const today = LocalDate.now() + const dateToDisplay = LocalDate.parse(date) + const formattedDate = today.equals(dateToDisplay) ? 'today' : moment(date).format('MMMM Do YYYY') + return formattedDate.toLowerCase() +} + +export default function CycleDayHeader({ date, ...props }) { return (<View style={[styles.header, styles.headerCycleDay]}> <View style={styles.accentCircle} @@ -15,11 +23,11 @@ export default function CycleDayHeader(props) { <NavigationArrow direction='left' {...props}/> <View> <Text style={styles.dateHeader}> - {moment(props.date).format('MMMM Do YYYY')} + <FormattedDate date={date} /> </Text> {props.cycleDayNumber && <Text style={styles.cycleDayNumber}> - Cycle day {props.cycleDayNumber} + {`Cycle day ${props.cycleDayNumber}`.toLowerCase()} </Text>} </View> <NavigationArrow direction='right' {...props}/> diff --git a/components/header/symptom-view.js b/components/header/symptom-view.js index df8b4bebc872343f4dc2778c860e808e52c1ff05..3f6a21f16e7bd78c5e14f2f0a009cfd664c6783b 100644 --- a/components/header/symptom-view.js +++ b/components/header/symptom-view.js @@ -24,6 +24,7 @@ export default function SymptomViewHeader(props) { </View > <FeatherIcon name='info' + style={styles.symptomInfoIcon} {...iconStyles.symptomHeaderIcons} /> </View> diff --git a/components/home.js b/components/home.js index 8f794f4bfc12c9077eb1e3ee8da912fdabd4d36c..ad4dce1985653f0d58412a62860bb5d5a408897f 100644 --- a/components/home.js +++ b/components/home.js @@ -2,17 +2,28 @@ import React, { Component } from 'react' import { ScrollView, View, TouchableOpacity, TouchableHighlight, Dimensions } from 'react-native' import { LocalDate, ChronoUnit } from 'js-joda' import Icon from 'react-native-vector-icons/Entypo' -import { Surface } from 'react-native/Libraries/ART/ReactNativeART' import { secondaryColor, cycleDayColor, periodColor } from '../styles' import { home as labels, bleedingPrediction as predictLabels, shared } from '../i18n/en/labels' -import CycleDayIcon from '../assets/home-circle' -import Drop from '../assets/home-drop' import cycleModule from '../lib/cycle' import { getCycleDaysSortedByDate } from '../db' import { getFertilityStatusForDay } from '../lib/sympto-adapter' import styles from '../styles' import AppText, { AppTextLight } from './app-text' import nothingChanged from '../db/db-unchanged' +import DripHomeIcon from '../assets/drip-home-icons' + +const HomeButton = ({ backgroundColor, children }) => { + return ( + <View style={[ + styles.homeButton, + {backgroundColor} + ]}> + <AppText style={styles.homeButtonText}> + {children} + </AppText> + </View> + ) +} export default class Home extends Component { constructor(props) { @@ -73,15 +84,7 @@ export default class Home extends Component { style={styles.homeIconElement} > <View position='absolute'> - <Surface - width={80} - height={80} - > - <CycleDayIcon - strokeWidth={2} - scale={0.46} - /> - </Surface> + <DripHomeIcon name="circle" size={80} color={cycleDayColor}/> </View> <View style={[styles.homeIconTextWrapper, styles.wrapperCycle]}> <AppTextLight style={styles.iconText}> @@ -92,14 +95,11 @@ export default class Home extends Component { { this.state.showMore && <AppText style={styles.paragraph}>{cycleDayMoreText}</AppText> } - <View style={[ - styles.homeButton, - { backgroundColor: cycleDayColor } - ]}> - <AppText style={styles.homeButtonText}> - {labels.editToday} - </AppText> - </View> + + <HomeButton backgroundColor={cycleDayColor}> + {labels.editToday} + </HomeButton> + </TouchableOpacity> <TouchableOpacity @@ -107,7 +107,7 @@ export default class Home extends Component { style={styles.homeIconElement} > <View position='absolute'> - <Drop scale={0.55}/> + <DripHomeIcon name="drop" size={105} color={periodColor} /> </View> <View style={[styles.homeIconTextWrapper, styles.wrapperDrop]}> <AppTextLight style={styles.iconText}> @@ -120,21 +120,21 @@ export default class Home extends Component { {this.state.predictionText} </AppText> } - <View style={[ - styles.homeButton, - { backgroundColor: periodColor } - ]}> - <AppText style={styles.homeButtonText}> - {labels.trackPeriod} - </AppText> - </View> + + <HomeButton backgroundColor={periodColor}> + {labels.trackPeriod} + </HomeButton> + </TouchableOpacity> <TouchableOpacity onPress={() => this.props.navigate('Chart')} style={styles.homeIconElement} > - <View style={styles.homeCircle}> + + <View style={styles.homeCircle} position='absolute' /> + + <View style={[styles.homeIconTextWrapper, styles.wrapperCircle]}> <AppTextLight style={styles.iconText}> {this.state.phase ? this.state.phase.toString() @@ -153,14 +153,10 @@ export default class Home extends Component { {this.state.statusText} </AppText> } - <View style={[ - styles.homeButton, - { backgroundColor: secondaryColor } - ]}> - <AppText style={styles.homeButtonText}> - {labels.checkFertility} - </AppText> - </View> + + <HomeButton backgroundColor={secondaryColor}> + {labels.checkFertility} + </HomeButton> </TouchableOpacity> </View> diff --git a/components/settings/index.js b/components/settings/index.js index bfddbc7026af06dcc27415f631e5dd8c668bb429..b91acbc9aef59de4430bc8f67018556552640a13 100644 --- a/components/settings/index.js +++ b/components/settings/index.js @@ -13,6 +13,7 @@ import TempSlider from './temp-slider' import openImportDialogAndImport from './import-dialog' import openShareDialogAndExport from './export-dialog' import PasswordSetting from './password' +import UseCervixSetting from './use-cervix' export default class Settings extends Component { constructor(props) { @@ -24,6 +25,7 @@ export default class Settings extends Component { return ( <ScrollView> <TempReminderPicker/> + <UseCervixSetting/> <View style={styles.settingsSegment}> <AppText style={styles.settingsSegmentTitle}> {labels.tempScale.segmentTitle} diff --git a/components/settings/password/create.js b/components/settings/password/create.js index 87bd2bf86beb7f053756c12a31107d58c36ebed4..68b7b2d2f014bbca23ec6047a23391934bd68608 100644 --- a/components/settings/password/create.js +++ b/components/settings/password/create.js @@ -6,17 +6,35 @@ import { import nodejs from 'nodejs-mobile-react-native' import AppText from '../../app-text' import styles from '../../../styles' -import { settings as labels } from '../../../i18n/en/settings' +import { settings } from '../../../i18n/en/settings' import { requestHash, changeEncryptionAndRestartApp } from '../../../db' import PasswordField from './password-field' import showBackUpReminder from './show-backup-reminder' +const SettingsButton = ({ children, ...props }) => { + return ( + <TouchableOpacity + style={[ + styles.settingsButton, + props.disabled ? styles.settingsButtonDisabled : null + ]} + { ...props } + > + <AppText style={styles.settingsButtonText}> + {children} + </AppText> + </TouchableOpacity> + ) +} + export default class CreatePassword extends Component { constructor() { super() this.state = { - enteringNewPassword: false, - newPassword: null + isSettingPassword: false, + password: '', + passwordConfirmation: '', + shouldShowErrorMessage: false, } nodejs.channel.addListener( 'create-pw-hash', @@ -29,33 +47,91 @@ export default class CreatePassword extends Component { nodejs.channel.removeListener('create-pw-hash', changeEncryptionAndRestartApp) } + savePassword = () => { + if (this.comparePasswords()) { + requestHash('create-pw-hash', this.state.password) + } else { + this.setState({ + shouldShowErrorMessage: true + }) + } + } + + toggleSettingPassword = () => { + const { isSettingPassword } = this.state + this.setState({ isSettingPassword: !isSettingPassword }) + } + + startSettingPassword = () => { + showBackUpReminder(this.toggleSettingPassword) + } + + comparePasswords = () => { + return this.state.password === this.state.passwordConfirmation + } + + handlePasswordInput = (password) => { + this.setState({ password }) + } + + handleConfirmationInput = (passwordConfirmation) => { + const { password } = this.state + this.setState({ + passwordConfirmation, + isPasswordsMatch: passwordConfirmation === password + }) + } + render () { - return ( - <View> - {this.state.enteringNewPassword && + const { + isSettingPassword, + password, + passwordConfirmation, + shouldShowErrorMessage, + } = this.state + const labels = settings.passwordSettings + + const isSaveButtonDisabled = + !password.length || + !passwordConfirmation.length + + if (!isSettingPassword) { + return ( + <View> + <SettingsButton onPress={this.startSettingPassword}> + {labels.setPassword} + </SettingsButton> + </View> + ) + } else { + return ( + <View> <PasswordField - placeholder={labels.passwordSettings.enterNew} - value={this.state.newPassword} - onChangeText={val => this.setState({newPassword: val})} + placeholder={labels.enterNew} + value={password} + onChangeText={this.handlePasswordInput} /> - } - <TouchableOpacity - onPress={() => { - if (!this.state.enteringNewPassword) { - showBackUpReminder(() => { - this.setState({ enteringNewPassword: true }) - }) - } else { - requestHash('create-pw-hash', this.state.newPassword) - } - }} - disabled={this.state.enteringNewPassword && !this.state.newPassword} - style={styles.settingsButton}> - <AppText style={styles.settingsButtonText}> - {labels.passwordSettings.setPassword} - </AppText> - </TouchableOpacity> - </View> - ) + <PasswordField + autoFocus={false} + placeholder={labels.confirmPassword} + value={passwordConfirmation} + onChangeText={this.handleConfirmationInput} + /> + { + shouldShowErrorMessage && + <AppText style={styles.errorMessage}> + {labels.passwordsDontMatch} + </AppText> + } + <SettingsButton + onPress={this.savePassword} + disabled={isSaveButtonDisabled} + > + {labels.savePassword} + </SettingsButton> + </View> + ) + } + } } \ No newline at end of file diff --git a/components/settings/password/password-field.js b/components/settings/password/password-field.js index 1757331e788c7fbf815d55e3b80f6a6684d83c9e..a62b4354946287d845332967998761aebc0da9a0 100644 --- a/components/settings/password/password-field.js +++ b/components/settings/password/password-field.js @@ -6,7 +6,7 @@ export default function PasswordField(props) { return ( <TextInput style={styles.passwordField} - autoFocus={true} + autoFocus={props.autoFocus === false ? false : true} secureTextEntry={true} onChangeText={props.onChangeText} value={props.value} diff --git a/components/settings/use-cervix.js b/components/settings/use-cervix.js new file mode 100644 index 0000000000000000000000000000000000000000..668a0e8268065f8bd5cfd187db8b6d9d2c500081 --- /dev/null +++ b/components/settings/use-cervix.js @@ -0,0 +1,48 @@ +import React, { Component } from 'react' +import { + View, + TouchableOpacity, + Switch +} from 'react-native' +import AppText from '../app-text' +import { + useCervixObservable, + saveUseCervix +} from '../../local-storage' +import styles from '../../styles/index' +import { settings as labels } from '../../i18n/en/settings' + +export default class UseCervixSetting extends Component { + constructor() { + super() + this.state = {useCervix: useCervixObservable.value} + } + + render() { + return ( + <TouchableOpacity + style={styles.settingsSegment} + > + <AppText style={styles.settingsSegmentTitle}> + {labels.useCervix.title} + </AppText> + <View style={{ flexDirection: 'row', alignItems: 'center' }}> + <View style={{ flex: 1 }}> + {this.state.useCervix ? + <AppText>{labels.useCervix.cervixModeOn}</AppText> + : + <AppText>{labels.useCervix.cervixModeOff}</AppText> + } + </View> + <Switch + value={this.state.useCervix} + onValueChange={bool => { + this.setState({ useCervix: bool }) + saveUseCervix(bool) + }} + /> + </View> + </TouchableOpacity> + ) + } +} diff --git a/i18n/en/settings.js b/i18n/en/settings.js index e985f7d84730529d382af6b3d5bf995b30b96af4..eef6615c83f7e59a6502eda61208ab1d828aa811 100644 --- a/i18n/en/settings.js +++ b/i18n/en/settings.js @@ -46,15 +46,23 @@ export const settings = { reminderText: 'Get a notification 3 days before your next period is likely to start.', notification: daysToEndOfPrediction => `Your next period is likely to start in 3 to ${daysToEndOfPrediction} days.` }, + useCervix: { + title: 'Secondary symptom', + cervixModeOn: 'Cervix values are being used for symptothermal fertility detection. You can switch here to use mucus values for symptothermal fertility detection', + cervixModeOff: 'By default, mucus values are being used for symptothermal fertility detection. You can switch here to use cervix values for symptothermal fertility detection' + }, passwordSettings: { title: 'App password', explainerDisabled: "Encrypt the app's database with a password. You need to enter the password every time the app is started.", explainerEnabled: "Password protection and database encryption is currently enabled", setPassword: 'Set password', + savePassword: 'Save password', changePassword: 'Change password', deletePassword: 'Delete password', enterCurrent: "Please enter your current password", enterNew: "Please enter a new password", + confirmPassword: "Please confirm your password", + passwordsDontMatch: "Password and confirmation don't match", backupReminderTitle: 'Read this before making changes to your password', backupReminder: 'Just to be safe, please backup your data using the export function before making changes to your password.\n\nLonger passwords are better! Consider using a passphrase.\n\nPlease also make sure you do not lose your password. There is no way to recover your data if you do.\n\nMaking any changes to your password setting will keep your data as it was before and restart the app.', deleteBackupReminderTitle: 'Read this before deleting your password', diff --git a/ios/drip.xcodeproj/project.pbxproj b/ios/drip.xcodeproj/project.pbxproj index 2827186a1c73632d62b1c017a15427ec4adf4e5a..9728bbf7c99e3a3726d30ae132a65f364560751e 100644 --- a/ios/drip.xcodeproj/project.pbxproj +++ b/ios/drip.xcodeproj/project.pbxproj @@ -76,6 +76,8 @@ 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 */; }; + BA7CE1E95B7843D7B0CF85FF /* drip-home-icons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 887F1D52A4684A5280CB79AA /* drip-home-icons.ttf */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -426,6 +428,8 @@ 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; }; + 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; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -741,6 +745,8 @@ 97B6A2FB33264796A4D917E0 /* Dosis-Light.ttf */, 2A1E40B398D54045B358F0DB /* Dosis-Medium.ttf */, 2A26A6F601D64F3A8D4A02B0 /* Dosis-SemiBold.ttf */, + 94973D7B7BBA4B0FBE713A0E /* drip-home-icon-font.ttf */, + 887F1D52A4684A5280CB79AA /* drip-home-icons.ttf */, ); name = Resources; sourceTree = "<group>"; @@ -1224,6 +1230,8 @@ 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 */, + BA7CE1E95B7843D7B0CF85FF /* drip-home-icons.ttf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1387,7 +1395,7 @@ embed_framework() { FRAMEWORK_NAME=\"$(basename \"$1\")\" cp -r \"$1\" \"$TARGET_BUILD_DIR/$FRAMEWORKS_FOLDER_PATH/\" - + /usr/bin/codesign --force --sign $EXPANDED_CODE_SIGN_IDENTITY --preserve-metadata=identifier,entitlements,flags --timestamp=none \"$TARGET_BUILD_DIR/$FRAMEWORKS_FOLDER_PATH/$FRAMEWORK_NAME\" } find \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.framework\" -type d | while read frmwrk_path; do embed_framework \"$frmwrk_path\"; done diff --git a/ios/drip/Info.plist b/ios/drip/Info.plist index a8a6c54d7e3a30a65a8cc0cb95d7909bc325048c..713f70adcb0c71203529a797bfe416a2e85e2672 100644 --- a/ios/drip/Info.plist +++ b/ios/drip/Info.plist @@ -72,6 +72,7 @@ <string>Prompt-Thin.ttf</string> <string>fontello.ttf</string> <string>drip-icon-font.ttf</string> +<<<<<<< HEAD <string>Dosis-Bold.ttf</string> <string>Dosis-Book.ttf</string> <string>Dosis-ExtraBold.ttf</string> @@ -79,6 +80,10 @@ <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-icons.ttf</string> +>>>>>>> master </array> </dict> </plist> diff --git a/lib/sympto-adapter.js b/lib/sympto-adapter.js index 821e7c5220b801008401618cd9b9f6adc3cd2c8a..628ea2b97137cd1c07aa87477af406637eb260ab 100644 --- a/lib/sympto-adapter.js +++ b/lib/sympto-adapter.js @@ -1,6 +1,7 @@ import getFertilityStatus from './sympto' import cycleModule from './cycle' import { fertilityStatus } from '../i18n/en/labels' +import { useCervixObservable } from '../local-storage' export function getFertilityStatusForDay(dateString) { const status = getCycleStatusForDay(dateString) @@ -48,6 +49,8 @@ export function getCycleStatusForDay(dateString, opts = {}) { } } + cycleInfo.secondarySymptom = useCervixObservable.value ? 'cervix' : 'mucus' + return getFertilityStatus(cycleInfo) } diff --git a/local-storage/index.js b/local-storage/index.js index 5f4af70e252bc350db736a5729fbb8279b611879..89b37d953a6d6433213faee5e8a4fffad31a31fa 100644 --- a/local-storage/index.js +++ b/local-storage/index.js @@ -44,6 +44,14 @@ export async function savePeriodReminder(reminder) { periodReminderObservable.set(reminder) } +export const useCervixObservable = Observable() +setObvWithInitValue('useCervix', useCervixObservable, false) + +export async function saveUseCervix(bool) { + await AsyncStorage.setItem('useCervix', JSON.stringify(bool)) + useCervixObservable.set(bool) +} + export const hasEncryptionObservable = Observable() setObvWithInitValue('hasEncryption', hasEncryptionObservable, false)