try to fix merge conflicts
This commit is contained in:
+6
-6
@@ -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
|
||||
|
||||
|
||||
@@ -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`.
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,4 @@
|
||||
import { createIconSetFromFontello } from 'react-native-vector-icons'
|
||||
import fontelloConfig from './fonts/config-drip-home-icons.json'
|
||||
|
||||
export default createIconSetFromFontello(fontelloConfig)
|
||||
@@ -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)
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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}
|
||||
<DripHomeIcon
|
||||
name="circle"
|
||||
size={styles.yAxis.width - 7}
|
||||
color={cycleDayColor}
|
||||
/>
|
||||
</Surface>
|
||||
<AppText style={[
|
||||
styles.column.label.date,
|
||||
styles.yAxisLabels.dateLabel
|
||||
]}>
|
||||
{labels.date}
|
||||
{labels.date.toLowerCase()}
|
||||
</AppText>
|
||||
</View>
|
||||
</View>}
|
||||
|
||||
@@ -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]}>
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
@@ -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}/>
|
||||
|
||||
@@ -24,6 +24,7 @@ export default function SymptomViewHeader(props) {
|
||||
</View >
|
||||
<FeatherIcon
|
||||
name='info'
|
||||
style={styles.symptomInfoIcon}
|
||||
{...iconStyles.symptomHeaderIcons}
|
||||
/>
|
||||
</View>
|
||||
|
||||
+31
-35
@@ -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}>
|
||||
|
||||
<HomeButton backgroundColor={cycleDayColor}>
|
||||
{labels.editToday}
|
||||
</AppText>
|
||||
</View>
|
||||
</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}>
|
||||
|
||||
<HomeButton backgroundColor={periodColor}>
|
||||
{labels.trackPeriod}
|
||||
</AppText>
|
||||
</View>
|
||||
</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}>
|
||||
|
||||
<HomeButton backgroundColor={secondaryColor}>
|
||||
{labels.checkFertility}
|
||||
</AppText>
|
||||
</View>
|
||||
</HomeButton>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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 () {
|
||||
const {
|
||||
isSettingPassword,
|
||||
password,
|
||||
passwordConfirmation,
|
||||
shouldShowErrorMessage,
|
||||
} = this.state
|
||||
const labels = settings.passwordSettings
|
||||
|
||||
const isSaveButtonDisabled =
|
||||
!password.length ||
|
||||
!passwordConfirmation.length
|
||||
|
||||
if (!isSettingPassword) {
|
||||
return (
|
||||
<View>
|
||||
{this.state.enteringNewPassword &&
|
||||
<PasswordField
|
||||
placeholder={labels.passwordSettings.enterNew}
|
||||
value={this.state.newPassword}
|
||||
onChangeText={val => this.setState({newPassword: val})}
|
||||
/>
|
||||
}
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
if (!this.state.enteringNewPassword) {
|
||||
showBackUpReminder(() => {
|
||||
this.setState({ enteringNewPassword: true })
|
||||
})
|
||||
<SettingsButton onPress={this.startSettingPassword}>
|
||||
{labels.setPassword}
|
||||
</SettingsButton>
|
||||
</View>
|
||||
)
|
||||
} 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}
|
||||
return (
|
||||
<View>
|
||||
<PasswordField
|
||||
placeholder={labels.enterNew}
|
||||
value={password}
|
||||
onChangeText={this.handlePasswordInput}
|
||||
/>
|
||||
<PasswordField
|
||||
autoFocus={false}
|
||||
placeholder={labels.confirmPassword}
|
||||
value={passwordConfirmation}
|
||||
onChangeText={this.handleConfirmationInput}
|
||||
/>
|
||||
{
|
||||
shouldShowErrorMessage &&
|
||||
<AppText style={styles.errorMessage}>
|
||||
{labels.passwordsDontMatch}
|
||||
</AppText>
|
||||
</TouchableOpacity>
|
||||
}
|
||||
<SettingsButton
|
||||
onPress={this.savePassword}
|
||||
disabled={isSaveButtonDisabled}
|
||||
>
|
||||
{labels.savePassword}
|
||||
</SettingsButton>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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',
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user