merge master
This commit is contained in:
@@ -1,6 +0,0 @@
|
|||||||
|
|
||||||
[android]
|
|
||||||
target = Google Inc.:Google APIs:23
|
|
||||||
|
|
||||||
[maven_repositories]
|
|
||||||
central = https://repo1.maven.org/maven2
|
|
||||||
-54
@@ -1,54 +0,0 @@
|
|||||||
[ignore]
|
|
||||||
; We fork some components by platform
|
|
||||||
.*/*[.]android.js
|
|
||||||
|
|
||||||
; Ignore "BUCK" generated dirs
|
|
||||||
<PROJECT_ROOT>/\.buckd/
|
|
||||||
|
|
||||||
; Ignore unexpected extra "@providesModule"
|
|
||||||
.*/node_modules/.*/node_modules/fbjs/.*
|
|
||||||
|
|
||||||
; Ignore duplicate module providers
|
|
||||||
; For RN Apps installed via npm, "Libraries" folder is inside
|
|
||||||
; "node_modules/react-native" but in the source repo it is in the root
|
|
||||||
.*/Libraries/react-native/React.js
|
|
||||||
|
|
||||||
; Ignore polyfills
|
|
||||||
.*/Libraries/polyfills/.*
|
|
||||||
|
|
||||||
; Ignore metro
|
|
||||||
.*/node_modules/metro/.*
|
|
||||||
|
|
||||||
[include]
|
|
||||||
|
|
||||||
[libs]
|
|
||||||
node_modules/react-native/Libraries/react-native/react-native-interface.js
|
|
||||||
node_modules/react-native/flow/
|
|
||||||
node_modules/react-native/flow-github/
|
|
||||||
|
|
||||||
[options]
|
|
||||||
emoji=true
|
|
||||||
|
|
||||||
module.system=haste
|
|
||||||
|
|
||||||
munge_underscores=true
|
|
||||||
|
|
||||||
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
|
|
||||||
|
|
||||||
module.file_ext=.js
|
|
||||||
module.file_ext=.jsx
|
|
||||||
module.file_ext=.json
|
|
||||||
module.file_ext=.native.js
|
|
||||||
|
|
||||||
suppress_type=$FlowIssue
|
|
||||||
suppress_type=$FlowFixMe
|
|
||||||
suppress_type=$FlowFixMeProps
|
|
||||||
suppress_type=$FlowFixMeState
|
|
||||||
|
|
||||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
|
|
||||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
|
|
||||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
|
|
||||||
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
|
|
||||||
|
|
||||||
[version]
|
|
||||||
^0.67.0
|
|
||||||
@@ -53,8 +53,7 @@ Unfortunately, the react native version we use doesn't work on Windows 10 it see
|
|||||||
You can run the tests with `npm test`.
|
You can run the tests with `npm test`.
|
||||||
|
|
||||||
## Debugging
|
## Debugging
|
||||||
When running into an old version of the app try to run the following command first:
|
In order to see logging output from the app, run `npm run log` in a separate terminal.
|
||||||
`react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res`
|
|
||||||
|
|
||||||
## 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)
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest
|
||||||
package="com.drip">
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
package="com.drip"
|
||||||
|
>
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||||
@@ -36,9 +39,11 @@
|
|||||||
android:grantUriPermissions="true"
|
android:grantUriPermissions="true"
|
||||||
android:exported="false">
|
android:exported="false">
|
||||||
<meta-data
|
<meta-data
|
||||||
|
tools:replace="android:resource"
|
||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
android:resource="@xml/filepaths" />
|
android:resource="@xml/filepaths" />
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
<meta-data android:name="com.dieam.reactnativepushnotification.notification_channel_name"
|
<meta-data android:name="com.dieam.reactnativepushnotification.notification_channel_name"
|
||||||
android:value="drip-notification"/>
|
android:value="drip-notification"/>
|
||||||
<meta-data android:name="com.dieam.reactnativepushnotification.notification_channel_description"
|
<meta-data android:name="com.dieam.reactnativepushnotification.notification_channel_description"
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<external-path name="Downloads" path="Download/" />
|
<external-path name="Downloads" path="Download/" />
|
||||||
|
<root-path name="root" path="" />
|
||||||
|
<files-path
|
||||||
|
name="files-path"
|
||||||
|
path="." /> <!-- Used to access into application data files -->
|
||||||
</paths>
|
</paths>
|
||||||
@@ -35,3 +35,11 @@ ext {
|
|||||||
targetSdkVersion = 26
|
targetSdkVersion = 26
|
||||||
supportLibVersion = "26.1.0"
|
supportLibVersion = "26.1.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subprojects {project ->
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
maven { url = 'https://dl.bintray.com/android/android-tools/' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+12
-15
@@ -1,33 +1,30 @@
|
|||||||
import React, { Component } from 'react'
|
import React from 'react'
|
||||||
import { Text } from 'react-native'
|
import { Text } from 'react-native'
|
||||||
import styles from "../styles"
|
import styles from "../styles"
|
||||||
|
|
||||||
export default class AppText extends Component {
|
export default function AppText(props) {
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Text style={[styles.appText, this.props.style]}>
|
<Text
|
||||||
{this.props.children}
|
style={[styles.appText, props.style]}
|
||||||
|
onPress={props.onPress}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AppTextLight extends Component {
|
export function AppTextLight(props) {
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Text style={[styles.appTextLight, this.props.style]}>
|
<Text style={[styles.appTextLight, props.style]}>
|
||||||
{this.props.children}
|
{props.children}
|
||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SymptomSectionHeader extends Component {
|
export function SymptomSectionHeader(props) {
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<AppText style={styles.symptomViewHeading}>
|
<AppText style={styles.symptomViewHeading}>
|
||||||
{this.props.children}
|
{props.children}
|
||||||
</AppText>
|
</AppText>
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+7
-2
@@ -7,7 +7,8 @@ import Calendar from './calendar'
|
|||||||
import CycleDay from './cycle-day/cycle-day-overview'
|
import CycleDay from './cycle-day/cycle-day-overview'
|
||||||
import symptomViews from './cycle-day/symptoms'
|
import symptomViews from './cycle-day/symptoms'
|
||||||
import Chart from './chart/chart'
|
import Chart from './chart/chart'
|
||||||
import Settings from './settings'
|
import SettingsMenu from './settings/settings-menu'
|
||||||
|
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 setupNotifications from '../lib/notifications'
|
import setupNotifications from '../lib/notifications'
|
||||||
@@ -24,6 +25,7 @@ const menuTitlesLowerCase = Object.keys(menuTitles).reduce((acc, curr) => {
|
|||||||
}, {})
|
}, {})
|
||||||
|
|
||||||
const isSymptomView = name => Object.keys(symptomViews).includes(name)
|
const isSymptomView = name => Object.keys(symptomViews).includes(name)
|
||||||
|
const isSettingsView = name => Object.keys(settingsViews).includes(name)
|
||||||
const isMenuItem = name => Object.keys(menuTitles).includes(name)
|
const isMenuItem = name => Object.keys(menuTitles).includes(name)
|
||||||
|
|
||||||
export default class App extends Component {
|
export default class App extends Component {
|
||||||
@@ -58,6 +60,8 @@ export default class App extends Component {
|
|||||||
this.navigate(
|
this.navigate(
|
||||||
this.originForSymptomView, { date: this.state.currentProps.date }
|
this.originForSymptomView, { date: this.state.currentProps.date }
|
||||||
)
|
)
|
||||||
|
} else if (isSettingsView(this.state.currentPage)) {
|
||||||
|
this.navigate('SettingsMenu')
|
||||||
} else if(this.state.currentPage === 'CycleDay') {
|
} else if(this.state.currentPage === 'CycleDay') {
|
||||||
this.navigate(this.menuOrigin)
|
this.navigate(this.menuOrigin)
|
||||||
} else {
|
} else {
|
||||||
@@ -68,7 +72,7 @@ export default class App extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const page = {
|
const page = {
|
||||||
Home, Calendar, CycleDay, Chart, Settings, Stats, ...symptomViews
|
Home, Calendar, CycleDay, Chart, SettingsMenu, ...settingsViews, Stats, ...symptomViews
|
||||||
}[this.state.currentPage]
|
}[this.state.currentPage]
|
||||||
return (
|
return (
|
||||||
<View style={{flex: 1}}>
|
<View style={{flex: 1}}>
|
||||||
@@ -81,6 +85,7 @@ export default class App extends Component {
|
|||||||
title={headerTitlesLowerCase[this.state.currentPage]}
|
title={headerTitlesLowerCase[this.state.currentPage]}
|
||||||
isSymptomView={true}
|
isSymptomView={true}
|
||||||
goBack={this.handleBackButtonPress}
|
goBack={this.handleBackButtonPress}
|
||||||
|
date={this.state.currentProps.date}
|
||||||
/>}
|
/>}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ export default class CycleChart extends Component {
|
|||||||
this.chartSymptoms.push('temperature')
|
this.chartSymptoms.push('temperature')
|
||||||
}
|
}
|
||||||
|
|
||||||
const columnData = this.makeColumnInfo(nfpLines(), this.chartSymptoms)
|
const columnData = this.makeColumnInfo()
|
||||||
this.setState({
|
this.setState({
|
||||||
columns: columnData,
|
columns: columnData,
|
||||||
chartHeight: height
|
chartHeight: height
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export default class DayColumn extends Component {
|
|||||||
|
|
||||||
this.fhmAndLtl = props.getFhmAndLtlInfo(
|
this.fhmAndLtl = props.getFhmAndLtlInfo(
|
||||||
props.dateString,
|
props.dateString,
|
||||||
props.temp,
|
this.data.temperature,
|
||||||
props.columnHeight
|
props.columnHeight
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import {
|
import {
|
||||||
View, TouchableOpacity, Text, Alert
|
View, TouchableOpacity, Text, Alert, ToastAndroid
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import Icon from 'react-native-vector-icons/MaterialCommunityIcons'
|
import Icon from 'react-native-vector-icons/MaterialCommunityIcons'
|
||||||
import { saveSymptom } from '../../../db'
|
import { saveSymptom } from '../../../db'
|
||||||
import styles, {iconStyles} from '../../../styles'
|
import styles, {iconStyles} from '../../../styles'
|
||||||
import {sharedDialogs as labels} from '../../../i18n/en/cycle-day'
|
import {sharedDialogs as labels} from '../../../i18n/en/cycle-day'
|
||||||
|
|
||||||
|
|
||||||
export default class ActionButtonFooter extends Component {
|
export default class ActionButtonFooter extends Component {
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
@@ -43,19 +44,26 @@ export default class ActionButtonFooter extends Component {
|
|||||||
}, {
|
}, {
|
||||||
title: labels.save,
|
title: labels.save,
|
||||||
action: () => {
|
action: () => {
|
||||||
|
if(saveDisabled) {
|
||||||
|
ToastAndroid.show(labels.disabledInfo, ToastAndroid.LONG);
|
||||||
|
console.log()
|
||||||
|
} else {
|
||||||
saveAction()
|
saveAction()
|
||||||
if (autoShowDayView) navigateToOverView()
|
if (autoShowDayView) navigateToOverView()
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
disabledCondition: saveDisabled,
|
disabledCondition: saveDisabled,
|
||||||
icon: 'content-save-outline'
|
icon: 'content-save-outline'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.menu}>
|
<View style={styles.menu}>
|
||||||
{buttons.map(({ title, action, disabledCondition, icon }, i) => {
|
{buttons.map(({ title, action, disabledCondition, icon }, i) => {
|
||||||
const textStyle = [styles.menuText]
|
const textStyle = [styles.menuText]
|
||||||
if (disabledCondition) textStyle.push(styles.menuTextInActive)
|
if (disabledCondition) {
|
||||||
|
textStyle.push(styles.menuTextInActive);
|
||||||
|
}
|
||||||
const iconStyle = disabledCondition ?
|
const iconStyle = disabledCondition ?
|
||||||
Object.assign(
|
Object.assign(
|
||||||
{},
|
{},
|
||||||
@@ -68,7 +76,6 @@ export default class ActionButtonFooter extends Component {
|
|||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={action}
|
onPress={action}
|
||||||
style={styles.menuItem}
|
style={styles.menuItem}
|
||||||
disabled={disabledCondition}
|
|
||||||
key={i.toString()}
|
key={i.toString()}
|
||||||
>
|
>
|
||||||
<Icon name={icon} {...iconStyle} />
|
<Icon name={icon} {...iconStyle} />
|
||||||
@@ -76,6 +83,7 @@ export default class ActionButtonFooter extends Component {
|
|||||||
{title.toLowerCase()}
|
{title.toLowerCase()}
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
import styles from '../../../styles'
|
import styles from '../../../styles'
|
||||||
import { saveSymptom } from '../../../db'
|
import { saveSymptom } from '../../../db'
|
||||||
import { mucus as labels } from '../../../i18n/en/cycle-day'
|
import { mucus as labels } from '../../../i18n/en/cycle-day'
|
||||||
import computeSensiplanValue from '../../../lib/sensiplan-mucus'
|
import computeNfpValue from '../../../lib/nfp-mucus'
|
||||||
import ActionButtonFooter from './action-button-footer'
|
import ActionButtonFooter from './action-button-footer'
|
||||||
import SelectTabGroup from '../select-tab-group'
|
import SelectTabGroup from '../select-tab-group'
|
||||||
import SymptomSection from './symptom-section'
|
import SymptomSection from './symptom-section'
|
||||||
@@ -80,7 +80,7 @@ export default class Mucus extends Component {
|
|||||||
saveSymptom('mucus', this.props.date, {
|
saveSymptom('mucus', this.props.date, {
|
||||||
feeling,
|
feeling,
|
||||||
texture,
|
texture,
|
||||||
value: computeSensiplanValue(feeling, texture),
|
value: computeNfpValue(feeling, texture),
|
||||||
exclude: Boolean(this.state.exclude)
|
exclude: Boolean(this.state.exclude)
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -2,17 +2,9 @@ import React from 'react'
|
|||||||
import {
|
import {
|
||||||
View,
|
View,
|
||||||
Text} from 'react-native'
|
Text} from 'react-native'
|
||||||
import { LocalDate } from 'js-joda'
|
|
||||||
import moment from 'moment'
|
|
||||||
import styles from '../../styles'
|
import styles from '../../styles'
|
||||||
import NavigationArrow from './navigation-arrow'
|
import NavigationArrow from './navigation-arrow'
|
||||||
|
import formatDate from '../helpers/format-date'
|
||||||
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 }) {
|
export default function CycleDayHeader({ date, ...props }) {
|
||||||
return (<View style={[styles.header, styles.headerCycleDay]}>
|
return (<View style={[styles.header, styles.headerCycleDay]}>
|
||||||
@@ -23,7 +15,7 @@ export default function CycleDayHeader({ date, ...props }) {
|
|||||||
<NavigationArrow direction='left' {...props}/>
|
<NavigationArrow direction='left' {...props}/>
|
||||||
<View>
|
<View>
|
||||||
<Text style={styles.dateHeader}>
|
<Text style={styles.dateHeader}>
|
||||||
<FormattedDate date={date} />
|
{formatDate(date)}
|
||||||
</Text>
|
</Text>
|
||||||
{props.cycleDayNumber &&
|
{props.cycleDayNumber &&
|
||||||
<Text style={styles.cycleDayNumber}>
|
<Text style={styles.cycleDayNumber}>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
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'
|
||||||
|
import formatDate from '../helpers/format-date'
|
||||||
|
|
||||||
export default function SymptomViewHeader(props) {
|
export default function SymptomViewHeader(props) {
|
||||||
return (
|
return (
|
||||||
@@ -21,6 +22,9 @@ export default function SymptomViewHeader(props) {
|
|||||||
<Text style={styles.dateHeader}>
|
<Text style={styles.dateHeader}>
|
||||||
{props.title}
|
{props.title}
|
||||||
</Text>
|
</Text>
|
||||||
|
<Text style={styles.cycleDayNumber}>
|
||||||
|
{formatDate(props.date)}
|
||||||
|
</Text>
|
||||||
</View >
|
</View >
|
||||||
<FeatherIcon
|
<FeatherIcon
|
||||||
name='info'
|
name='info'
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { LocalDate } from 'js-joda'
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
|
export default function (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()
|
||||||
|
}
|
||||||
@@ -9,7 +9,6 @@ import { getCycleDaysSortedByDate } from '../db'
|
|||||||
import { getFertilityStatusForDay } from '../lib/sympto-adapter'
|
import { getFertilityStatusForDay } from '../lib/sympto-adapter'
|
||||||
import styles from '../styles'
|
import styles from '../styles'
|
||||||
import AppText, { AppTextLight } from './app-text'
|
import AppText, { AppTextLight } from './app-text'
|
||||||
import nothingChanged from '../db/db-unchanged'
|
|
||||||
import DripHomeIcon from '../assets/drip-home-icons'
|
import DripHomeIcon from '../assets/drip-home-icons'
|
||||||
|
|
||||||
const HomeButton = ({ backgroundColor, children }) => {
|
const HomeButton = ({ backgroundColor, children }) => {
|
||||||
@@ -42,23 +41,6 @@ export default class Home extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.cycleDays = getCycleDaysSortedByDate()
|
this.cycleDays = getCycleDaysSortedByDate()
|
||||||
this.cycleDays.addListener(this.updateState)
|
|
||||||
}
|
|
||||||
|
|
||||||
updateState = (_, changes) => {
|
|
||||||
if (nothingChanged(changes)) return
|
|
||||||
const prediction = this.getBleedingPrediction()
|
|
||||||
const fertilityStatus = getFertilityStatusForDay(this.todayDateString)
|
|
||||||
this.setState({
|
|
||||||
cycleDayNumber: this.getCycleDayNumber(this.todayDateString),
|
|
||||||
predictionText: determinePredictionText(prediction),
|
|
||||||
bleedingPredictionRange: getBleedingPredictionRange(prediction),
|
|
||||||
...fertilityStatus
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
this.cycleDays.removeListener(this.updateState)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
passTodayTo(componentName) {
|
passTodayTo(componentName) {
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { Linking } from 'react-native'
|
||||||
|
import AppText from "./app-text"
|
||||||
|
import styles from '../styles';
|
||||||
|
|
||||||
|
export default function Link(props) {
|
||||||
|
return (
|
||||||
|
<AppText
|
||||||
|
style={styles.link}
|
||||||
|
onPress={() => Linking.openURL(props.href)}
|
||||||
|
>{props.text}</AppText>
|
||||||
|
)
|
||||||
|
}
|
||||||
+1
-1
@@ -38,7 +38,7 @@ export default class Menu extends Component {
|
|||||||
{ title: t.Calendar, icon: 'calendar-range', onPress: () => this.goTo('Calendar') },
|
{ title: t.Calendar, icon: 'calendar-range', onPress: () => this.goTo('Calendar') },
|
||||||
{ title: t.Chart, icon: 'chart-line', onPress: () => this.goTo('Chart') },
|
{ title: t.Chart, icon: 'chart-line', onPress: () => this.goTo('Chart') },
|
||||||
{ title: t.Stats, icon: 'chart-pie', onPress: () => this.goTo('Stats') },
|
{ title: t.Stats, icon: 'chart-pie', onPress: () => this.goTo('Stats') },
|
||||||
{ title: t.Settings, icon: 'settings', onPress: () => this.goTo('Settings') },
|
{ title: t.Settings, icon: 'settings', onPress: () => this.goTo('SettingsMenu') },
|
||||||
].map(this.makeMenuItem)}
|
].map(this.makeMenuItem)}
|
||||||
</View >
|
</View >
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import { View, ScrollView } from 'react-native'
|
||||||
|
import AppText from '../app-text'
|
||||||
|
import styles from '../../styles/index'
|
||||||
|
import labels from '../../i18n/en/settings'
|
||||||
|
export default class AboutSection extends Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<ScrollView>
|
||||||
|
<View style={styles.settingsSegment}>
|
||||||
|
<AppText style={styles.settingsSegmentTitle}>{`${labels.aboutSection.title} `}</AppText>
|
||||||
|
<AppText>{`${labels.aboutSection.segmentExplainer} `}</AppText>
|
||||||
|
</View>
|
||||||
|
<View style={[styles.settingsSegment, styles.settingsSegmentLast]}>
|
||||||
|
<AppText style={styles.settingsSegmentTitle}>{`${labels.credits.title} `}</AppText>
|
||||||
|
<AppText>{`${labels.credits.note}`}</AppText>
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import Share from 'react-native-share'
|
|
||||||
import getDataAsCsvDataUri from '../../lib/import-export/export-to-csv'
|
|
||||||
import alertError from './alert-error'
|
|
||||||
import { settings as labels } from '../../i18n/en/settings'
|
|
||||||
|
|
||||||
export default async function openShareDialogAndExport() {
|
|
||||||
let data
|
|
||||||
try {
|
|
||||||
data = getDataAsCsvDataUri()
|
|
||||||
if (!data) {
|
|
||||||
return alertError(labels.errors.noData)
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
return alertError(labels.errors.couldNotConvert)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await Share.open({
|
|
||||||
title: labels.export.title,
|
|
||||||
url: data,
|
|
||||||
subject: labels.export.subject,
|
|
||||||
type: 'text/csv',
|
|
||||||
showAppsToView: true
|
|
||||||
})
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
return alertError(labels.export.errors.problemSharing)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import Share from 'react-native-share'
|
||||||
|
|
||||||
|
import { getCycleDaysSortedByDate } from '../../../db'
|
||||||
|
import getDataAsCsvDataUri from '../../../lib/import-export/export-to-csv'
|
||||||
|
import alertError from '../alert-error'
|
||||||
|
import settings from '../../../i18n/en/settings'
|
||||||
|
import RNFS from 'react-native-fs'
|
||||||
|
|
||||||
|
export default async function exportData() {
|
||||||
|
let data
|
||||||
|
const labels = settings.export
|
||||||
|
const cycleDaysByDate = getCycleDaysSortedByDate()
|
||||||
|
|
||||||
|
if (!cycleDaysByDate.length) return alertError(labels.errors.noData)
|
||||||
|
|
||||||
|
try {
|
||||||
|
data = getDataAsCsvDataUri(cycleDaysByDate)
|
||||||
|
if (!data) {
|
||||||
|
return alertError(labels.errors.noData)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
return alertError(labels.errors.couldNotConvert)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const path = RNFS.DocumentDirectoryPath + '/data.csv'
|
||||||
|
await RNFS.writeFile(path, data)
|
||||||
|
|
||||||
|
await Share.open({
|
||||||
|
title: labels.title,
|
||||||
|
url: `file://${path}`,
|
||||||
|
subject: labels.subject,
|
||||||
|
type: 'text/csv',
|
||||||
|
showAppsToView: true
|
||||||
|
})
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
return alertError(labels.errors.problemSharing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+4
-4
@@ -1,10 +1,10 @@
|
|||||||
import { Alert } from 'react-native'
|
import { Alert } from 'react-native'
|
||||||
import { DocumentPicker, DocumentPickerUtil } from 'react-native-document-picker'
|
import { DocumentPicker, DocumentPickerUtil } from 'react-native-document-picker'
|
||||||
import rnfs from 'react-native-fs'
|
import rnfs from 'react-native-fs'
|
||||||
import importCsv from '../../lib/import-export/import-from-csv'
|
import importCsv from '../../../lib/import-export/import-from-csv'
|
||||||
import { shared as sharedLabels } from '../../i18n/en/labels'
|
import { shared as sharedLabels } from '../../../i18n/en/labels'
|
||||||
import { settings as labels } from '../../i18n/en/settings'
|
import labels from '../../../i18n/en/settings'
|
||||||
import alertError from './alert-error'
|
import alertError from '../alert-error'
|
||||||
|
|
||||||
export default function openImportDialogAndImport() {
|
export default function openImportDialogAndImport() {
|
||||||
Alert.alert(
|
Alert.alert(
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import {
|
||||||
|
View, ScrollView,
|
||||||
|
TouchableOpacity,
|
||||||
|
} from 'react-native'
|
||||||
|
import styles from '../../../styles/index'
|
||||||
|
import labels from '../../../i18n/en/settings'
|
||||||
|
import AppText from '../../app-text'
|
||||||
|
import openImportDialogAndImport from './import-dialog'
|
||||||
|
import openShareDialogAndExport from './export-dialog'
|
||||||
|
|
||||||
|
export default class Settings extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this.state = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<ScrollView>
|
||||||
|
<View style={styles.settingsSegment}>
|
||||||
|
<AppText style={styles.settingsSegmentTitle}>
|
||||||
|
{labels.export.button}
|
||||||
|
</AppText>
|
||||||
|
<AppText>{labels.export.segmentExplainer}</AppText>
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={openShareDialogAndExport}
|
||||||
|
style={styles.settingsButton}>
|
||||||
|
<AppText style={styles.settingsButtonText}>
|
||||||
|
{labels.export.button}
|
||||||
|
</AppText>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
<View style={styles.settingsSegment}>
|
||||||
|
<AppText style={styles.settingsSegmentTitle}>
|
||||||
|
{labels.import.button}
|
||||||
|
</AppText>
|
||||||
|
<AppText>{labels.import.segmentExplainer}</AppText>
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={openImportDialogAndImport}
|
||||||
|
style={styles.settingsButton}>
|
||||||
|
<AppText style={styles.settingsButtonText}>
|
||||||
|
{labels.import.button}
|
||||||
|
</AppText>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,67 +1,9 @@
|
|||||||
import React, { Component } from 'react'
|
import Reminders from './reminders'
|
||||||
import {
|
import NfpSettings from './nfp-settings'
|
||||||
View,
|
import ImportExport from './import-export'
|
||||||
TouchableOpacity,
|
import Password from './password'
|
||||||
ScrollView,
|
import About from './about'
|
||||||
} from 'react-native'
|
|
||||||
import styles from '../../styles/index'
|
|
||||||
import { settings as labels } from '../../i18n/en/settings'
|
|
||||||
import AppText from '../app-text'
|
|
||||||
import TempReminderPicker from './temp-reminder-picker'
|
|
||||||
import PeriodReminderPicker from './period-reminder'
|
|
||||||
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 {
|
export default {
|
||||||
constructor(props) {
|
Reminders, NfpSettings, ImportExport, Password, About
|
||||||
super(props)
|
|
||||||
this.state = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<ScrollView>
|
|
||||||
<TempReminderPicker/>
|
|
||||||
<UseCervixSetting/>
|
|
||||||
<View style={styles.settingsSegment}>
|
|
||||||
<AppText style={styles.settingsSegmentTitle}>
|
|
||||||
{labels.tempScale.segmentTitle}
|
|
||||||
</AppText>
|
|
||||||
<AppText>{labels.tempScale.segmentExplainer}</AppText>
|
|
||||||
<TempSlider/>
|
|
||||||
</View>
|
|
||||||
<PeriodReminderPicker/>
|
|
||||||
<PasswordSetting />
|
|
||||||
<View style={styles.settingsSegment}>
|
|
||||||
<AppText style={styles.settingsSegmentTitle}>
|
|
||||||
{labels.export.button}
|
|
||||||
</AppText>
|
|
||||||
<AppText>{labels.export.segmentExplainer}</AppText>
|
|
||||||
<TouchableOpacity
|
|
||||||
onPress={openShareDialogAndExport}
|
|
||||||
style={styles.settingsButton}>
|
|
||||||
<AppText style={styles.settingsButtonText}>
|
|
||||||
{labels.export.button}
|
|
||||||
</AppText>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
<View style={styles.settingsSegment}>
|
|
||||||
<AppText style={styles.settingsSegmentTitle}>
|
|
||||||
{labels.import.button}
|
|
||||||
</AppText>
|
|
||||||
<AppText>{labels.import.segmentExplainer}</AppText>
|
|
||||||
<TouchableOpacity
|
|
||||||
onPress={openImportDialogAndImport}
|
|
||||||
style={styles.settingsButton}>
|
|
||||||
<AppText style={styles.settingsButtonText}>
|
|
||||||
{labels.import.button}
|
|
||||||
</AppText>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
</ScrollView>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import {
|
||||||
|
ScrollView, View
|
||||||
|
} from 'react-native'
|
||||||
|
import styles, { iconStyles } from '../../../styles'
|
||||||
|
import labels from '../../../i18n/en/settings'
|
||||||
|
import AppText from '../../app-text'
|
||||||
|
import TempSlider from './temp-slider'
|
||||||
|
import UseCervixSetting from './use-cervix'
|
||||||
|
import Icon from 'react-native-vector-icons/Entypo'
|
||||||
|
import Link from '../../link'
|
||||||
|
|
||||||
|
export default class Settings extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this.state = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<ScrollView>
|
||||||
|
<UseCervixSetting/>
|
||||||
|
<View style={styles.settingsSegment}>
|
||||||
|
<AppText style={styles.settingsSegmentTitle}>
|
||||||
|
{labels.tempScale.segmentTitle}
|
||||||
|
</AppText>
|
||||||
|
<AppText>{labels.tempScale.segmentExplainer}</AppText>
|
||||||
|
<TempSlider/>
|
||||||
|
</View>
|
||||||
|
<View style={[styles.settingsSegment, styles.settingsSegmentLast]}>
|
||||||
|
<View style={{flexDirection: 'row', alignItems: 'center'}}>
|
||||||
|
<Icon name="info-with-circle" style={iconStyles.infoInHeading}/>
|
||||||
|
<AppText style={styles.settingsSegmentTitle}>{`${labels.preOvu.title} `}</AppText>
|
||||||
|
</View>
|
||||||
|
<AppText>
|
||||||
|
{labels.preOvu.note1}
|
||||||
|
<Link text={labels.preOvu.link} href="https://gitlab.com/bloodyhealth/drip/wikis/home" />
|
||||||
|
{labels.preOvu.note2}
|
||||||
|
</AppText>
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
-6
@@ -1,15 +1,15 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import { View } from 'react-native'
|
import { View } from 'react-native'
|
||||||
import Slider from '@ptomasroos/react-native-multi-slider'
|
import Slider from '@ptomasroos/react-native-multi-slider'
|
||||||
import AppText from '../app-text'
|
import AppText from '../../app-text'
|
||||||
import {
|
import {
|
||||||
scaleObservable,
|
scaleObservable,
|
||||||
saveTempScale,
|
saveTempScale,
|
||||||
} from '../../local-storage'
|
} from '../../../local-storage'
|
||||||
import { secondaryColor } from '../../styles/index'
|
import { secondaryColor } from '../../../styles/index'
|
||||||
import { settings as labels } from '../../i18n/en/settings'
|
import labels from '../../../i18n/en/settings'
|
||||||
import config from '../../config'
|
import config from '../../../config'
|
||||||
import alertError from './alert-error'
|
import alertError from '../alert-error'
|
||||||
|
|
||||||
export default class TempSlider extends Component {
|
export default class TempSlider extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -4,13 +4,13 @@ import {
|
|||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
Switch
|
Switch
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import AppText from '../app-text'
|
import AppText from '../../app-text'
|
||||||
import {
|
import {
|
||||||
useCervixObservable,
|
useCervixObservable,
|
||||||
saveUseCervix
|
saveUseCervix
|
||||||
} from '../../local-storage'
|
} from '../../../local-storage'
|
||||||
import styles from '../../styles/index'
|
import styles from '../../../styles/index'
|
||||||
import { settings as labels } from '../../i18n/en/settings'
|
import labels from '../../../i18n/en/settings'
|
||||||
|
|
||||||
export default class UseCervixSetting extends Component {
|
export default class UseCervixSetting extends Component {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -1,59 +1,15 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import {
|
import { View } from 'react-native'
|
||||||
View,
|
import settings from '../../../i18n/en/settings'
|
||||||
TouchableOpacity,
|
import EnterNewPassword from './enter-new-password'
|
||||||
} from 'react-native'
|
import SettingsButton from './settings-button'
|
||||||
import nodejs from 'nodejs-mobile-react-native'
|
|
||||||
import AppText from '../../app-text'
|
|
||||||
import styles from '../../../styles'
|
|
||||||
import { settings } from '../../../i18n/en/settings'
|
|
||||||
import { requestHash, changeEncryptionAndRestartApp } from '../../../db'
|
|
||||||
import PasswordField from './password-field'
|
|
||||||
import showBackUpReminder from './show-backup-reminder'
|
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 {
|
export default class CreatePassword extends Component {
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
this.state = {
|
this.state = {
|
||||||
isSettingPassword: false,
|
isSettingPassword: false
|
||||||
password: '',
|
|
||||||
passwordConfirmation: '',
|
|
||||||
shouldShowErrorMessage: false,
|
|
||||||
}
|
|
||||||
nodejs.channel.addListener(
|
|
||||||
'create-pw-hash',
|
|
||||||
changeEncryptionAndRestartApp,
|
|
||||||
this
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
nodejs.channel.removeListener('create-pw-hash', changeEncryptionAndRestartApp)
|
|
||||||
}
|
|
||||||
|
|
||||||
savePassword = () => {
|
|
||||||
if (this.comparePasswords()) {
|
|
||||||
requestHash('create-pw-hash', this.state.password)
|
|
||||||
} else {
|
|
||||||
this.setState({
|
|
||||||
shouldShowErrorMessage: true
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,35 +22,12 @@ export default class CreatePassword extends Component {
|
|||||||
showBackUpReminder(this.toggleSettingPassword)
|
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 () {
|
render () {
|
||||||
const {
|
const {
|
||||||
isSettingPassword,
|
isSettingPassword
|
||||||
password,
|
|
||||||
passwordConfirmation,
|
|
||||||
shouldShowErrorMessage,
|
|
||||||
} = this.state
|
} = this.state
|
||||||
const labels = settings.passwordSettings
|
const labels = settings.passwordSettings
|
||||||
|
|
||||||
const isSaveButtonDisabled =
|
|
||||||
!password.length ||
|
|
||||||
!passwordConfirmation.length
|
|
||||||
|
|
||||||
if (!isSettingPassword) {
|
if (!isSettingPassword) {
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
@@ -104,33 +37,7 @@ export default class CreatePassword extends Component {
|
|||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return (
|
return <EnterNewPassword />
|
||||||
<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>
|
|
||||||
}
|
|
||||||
<SettingsButton
|
|
||||||
onPress={this.savePassword}
|
|
||||||
disabled={isSaveButtonDisabled}
|
|
||||||
>
|
|
||||||
{labels.savePassword}
|
|
||||||
</SettingsButton>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
import nodejs from 'nodejs-mobile-react-native'
|
import nodejs from 'nodejs-mobile-react-native'
|
||||||
import AppText from '../../app-text'
|
import AppText from '../../app-text'
|
||||||
import styles from '../../../styles'
|
import styles from '../../../styles'
|
||||||
import { settings as labels } from '../../../i18n/en/settings'
|
import labels from '../../../i18n/en/settings'
|
||||||
import { requestHash, changeEncryptionAndRestartApp } from '../../../db'
|
import { requestHash, changeEncryptionAndRestartApp } from '../../../db'
|
||||||
import PasswordField from './password-field'
|
import PasswordField from './password-field'
|
||||||
import showBackUpReminder from './show-backup-reminder'
|
import showBackUpReminder from './show-backup-reminder'
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import { View } from 'react-native'
|
||||||
|
import nodejs from 'nodejs-mobile-react-native'
|
||||||
|
|
||||||
|
import { requestHash, changeEncryptionAndRestartApp } from '../../../db'
|
||||||
|
import AppText from '../../app-text'
|
||||||
|
import PasswordField from './password-field'
|
||||||
|
import SettingsButton from './settings-button'
|
||||||
|
|
||||||
|
import styles from '../../../styles'
|
||||||
|
import settings from '../../../i18n/en/settings'
|
||||||
|
|
||||||
|
const LISTENER_TYPE = 'create-or-change-pw'
|
||||||
|
|
||||||
|
export default class EnterNewPassword extends Component {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
this.state = {
|
||||||
|
password: '',
|
||||||
|
passwordConfirmation: '',
|
||||||
|
shouldShowErrorMessage: false,
|
||||||
|
}
|
||||||
|
nodejs.channel.addListener(
|
||||||
|
LISTENER_TYPE,
|
||||||
|
changeEncryptionAndRestartApp,
|
||||||
|
this
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
nodejs.channel.removeListener(LISTENER_TYPE, changeEncryptionAndRestartApp)
|
||||||
|
}
|
||||||
|
|
||||||
|
savePassword = () => {
|
||||||
|
if (this.comparePasswords()) {
|
||||||
|
requestHash(LISTENER_TYPE, this.state.password)
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
shouldShowErrorMessage: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
comparePasswords = () => {
|
||||||
|
return this.state.password === this.state.passwordConfirmation
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePasswordInput = (password) => {
|
||||||
|
this.setState({ password })
|
||||||
|
}
|
||||||
|
|
||||||
|
handleConfirmationInput = (passwordConfirmation) => {
|
||||||
|
this.setState({ passwordConfirmation })
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const {
|
||||||
|
password,
|
||||||
|
passwordConfirmation,
|
||||||
|
shouldShowErrorMessage,
|
||||||
|
} = this.state
|
||||||
|
const labels = settings.passwordSettings
|
||||||
|
|
||||||
|
const isSaveButtonDisabled =
|
||||||
|
!password.length ||
|
||||||
|
!passwordConfirmation.length
|
||||||
|
|
||||||
|
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>
|
||||||
|
}
|
||||||
|
<SettingsButton
|
||||||
|
onPress={this.savePassword}
|
||||||
|
disabled={isSaveButtonDisabled}
|
||||||
|
>
|
||||||
|
{labels.savePassword}
|
||||||
|
</SettingsButton>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import { View } from 'react-native'
|
import { View, ScrollView } from 'react-native'
|
||||||
import CreatePassword from './create'
|
import CreatePassword from './create'
|
||||||
import ChangePassword from './update'
|
import ChangePassword from './update'
|
||||||
import DeletePassword from './delete'
|
import DeletePassword from './delete'
|
||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
hasEncryptionObservable
|
hasEncryptionObservable
|
||||||
} from '../../../local-storage'
|
} from '../../../local-storage'
|
||||||
import styles from '../../../styles/index'
|
import styles from '../../../styles/index'
|
||||||
import { settings as labels } from '../../../i18n/en/settings'
|
import labels from '../../../i18n/en/settings'
|
||||||
|
|
||||||
export default class PasswordSetting extends Component {
|
export default class PasswordSetting extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -21,6 +21,7 @@ export default class PasswordSetting extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
|
<ScrollView>
|
||||||
<View style={styles.settingsSegment}>
|
<View style={styles.settingsSegment}>
|
||||||
|
|
||||||
<AppText style={styles.settingsSegmentTitle}>
|
<AppText style={styles.settingsSegmentTitle}>
|
||||||
@@ -45,6 +46,7 @@ export default class PasswordSetting extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
</View>
|
</View>
|
||||||
|
</ScrollView>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
|
import { TouchableOpacity } from 'react-native'
|
||||||
|
import AppText from '../../app-text'
|
||||||
|
import styles from '../../../styles'
|
||||||
|
|
||||||
|
const SettingsButton = ({ children, ...props }) => {
|
||||||
|
return (
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[
|
||||||
|
styles.settingsButton,
|
||||||
|
props.disabled ? styles.settingsButtonDisabled : null
|
||||||
|
]}
|
||||||
|
{ ...props }
|
||||||
|
>
|
||||||
|
<AppText style={styles.settingsButtonText}>
|
||||||
|
{children}
|
||||||
|
</AppText>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsButton.propTypes = {
|
||||||
|
onPress: PropTypes.func.isRequired,
|
||||||
|
disabled: PropTypes.bool
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SettingsButton
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Alert } from 'react-native'
|
import { Alert } from 'react-native'
|
||||||
import { shared } from '../../../i18n/en/labels'
|
import { shared } from '../../../i18n/en/labels'
|
||||||
import { settings as labels } from '../../../i18n/en/settings'
|
import labels from '../../../i18n/en/settings'
|
||||||
|
|
||||||
export default function showBackUpReminder(okHandler, isDelete) {
|
export default function showBackUpReminder(okHandler, isDelete) {
|
||||||
let title, message
|
let title, message
|
||||||
|
|||||||
@@ -1,26 +1,23 @@
|
|||||||
|
|
||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import {
|
import { View } from 'react-native'
|
||||||
View,
|
|
||||||
TouchableOpacity} from 'react-native'
|
|
||||||
import nodejs from 'nodejs-mobile-react-native'
|
import nodejs from 'nodejs-mobile-react-native'
|
||||||
import AppText from '../../app-text'
|
import { shared as sharedLabels } from '../../../i18n/en/labels'
|
||||||
import styles from '../../../styles'
|
import settings from '../../../i18n/en/settings'
|
||||||
import { shared } from '../../../i18n/en/labels'
|
import { requestHash } from '../../../db'
|
||||||
import { settings as labels } from '../../../i18n/en/settings'
|
import EnterNewPassword from './enter-new-password'
|
||||||
import { requestHash, changeEncryptionAndRestartApp } from '../../../db'
|
|
||||||
import PasswordField from './password-field'
|
import PasswordField from './password-field'
|
||||||
|
import SettingsButton from './settings-button'
|
||||||
import showBackUpReminder from './show-backup-reminder'
|
import showBackUpReminder from './show-backup-reminder'
|
||||||
import checkCurrentPassword from './check-current-password'
|
import checkCurrentPassword from './check-current-password'
|
||||||
|
|
||||||
|
|
||||||
export default class ChangePassword extends Component {
|
export default class ChangePassword extends Component {
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
this.state = {
|
this.state = {
|
||||||
enteringCurrentPassword: false,
|
|
||||||
currentPassword: null,
|
currentPassword: null,
|
||||||
enteringNewPassword: false,
|
enteringCurrentPassword: false,
|
||||||
newPassword: null
|
enteringNewPassword: false
|
||||||
}
|
}
|
||||||
|
|
||||||
nodejs.channel.addListener(
|
nodejs.channel.addListener(
|
||||||
@@ -28,17 +25,10 @@ export default class ChangePassword extends Component {
|
|||||||
this.openNewPasswordField,
|
this.openNewPasswordField,
|
||||||
this
|
this
|
||||||
)
|
)
|
||||||
|
|
||||||
nodejs.channel.addListener(
|
|
||||||
'change-pw',
|
|
||||||
changeEncryptionAndRestartApp,
|
|
||||||
this
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
nodejs.channel.removeListener('pre-change-pw-check', this.openNewPasswordField)
|
nodejs.channel.removeListener('pre-change-pw-check', this.openNewPasswordField)
|
||||||
nodejs.channel.removeListener('change-pw', changeEncryptionAndRestartApp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
openNewPasswordField = async hash => {
|
openNewPasswordField = async hash => {
|
||||||
@@ -53,77 +43,67 @@ export default class ChangePassword extends Component {
|
|||||||
|
|
||||||
if (passwordCorrect) {
|
if (passwordCorrect) {
|
||||||
this.setState({
|
this.setState({
|
||||||
enteringCurrentPassword: false,
|
|
||||||
currentPassword: null,
|
currentPassword: null,
|
||||||
enteringNewPassword: true
|
enteringNewPassword: true,
|
||||||
|
enteringCurrentPassword: false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
startChangingPassword = () => {
|
||||||
|
showBackUpReminder(() => {
|
||||||
|
this.setState({ enteringCurrentPassword: true })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCurrentPasswordInput = (currentPassword) => {
|
||||||
|
this.setState({ currentPassword })
|
||||||
|
}
|
||||||
|
|
||||||
|
checkCurrentPassword = () => {
|
||||||
|
requestHash('pre-change-pw-check', this.state.currentPassword)
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
||||||
|
const {
|
||||||
|
enteringCurrentPassword,
|
||||||
|
enteringNewPassword,
|
||||||
|
currentPassword
|
||||||
|
} = this.state
|
||||||
|
|
||||||
|
const labels = settings.passwordSettings
|
||||||
|
|
||||||
|
if (enteringCurrentPassword) {
|
||||||
return (
|
return (
|
||||||
<View>
|
|
||||||
{!this.state.enteringCurrentPassword &&
|
|
||||||
!this.state.enteringNewPassword &&
|
|
||||||
<TouchableOpacity
|
|
||||||
onPress={() => showBackUpReminder(() => {
|
|
||||||
this.setState({ enteringCurrentPassword: true })
|
|
||||||
})}
|
|
||||||
disabled={this.state.currentPassword}
|
|
||||||
style={styles.settingsButton}>
|
|
||||||
<AppText style={styles.settingsButtonText}>
|
|
||||||
{labels.passwordSettings.changePassword}
|
|
||||||
</AppText>
|
|
||||||
</TouchableOpacity>
|
|
||||||
}
|
|
||||||
|
|
||||||
{this.state.enteringCurrentPassword &&
|
|
||||||
<View>
|
<View>
|
||||||
<PasswordField
|
<PasswordField
|
||||||
onChangeText={val => {
|
placeholder={labels.enterCurrent}
|
||||||
this.setState({
|
value={currentPassword}
|
||||||
currentPassword: val,
|
onChangeText={this.handleCurrentPasswordInput}
|
||||||
wrongPassword: false
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
value={this.state.currentPassword}
|
|
||||||
placeholder={labels.passwordSettings.enterCurrent}
|
|
||||||
/>
|
/>
|
||||||
<TouchableOpacity
|
<SettingsButton
|
||||||
onPress={() => requestHash('pre-change-pw-check', this.state.currentPassword)}
|
onPress={this.checkCurrentPassword}
|
||||||
disabled={!this.state.currentPassword}
|
disabled={!currentPassword}
|
||||||
style={styles.settingsButton}>
|
>
|
||||||
<AppText style={styles.settingsButtonText}>
|
{sharedLabels.unlock}
|
||||||
{shared.unlock}
|
</SettingsButton>
|
||||||
</AppText>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
</View>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
{this.state.enteringNewPassword &&
|
if (enteringNewPassword) {
|
||||||
|
return <EnterNewPassword />
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
<View>
|
<View>
|
||||||
<PasswordField
|
<SettingsButton
|
||||||
style={styles.passwordField}
|
onPress={this.startChangingPassword}
|
||||||
onChangeText={val => {
|
disabled={currentPassword}
|
||||||
this.setState({
|
>
|
||||||
newPassword: val
|
{labels.changePassword}
|
||||||
})
|
</SettingsButton>
|
||||||
}}
|
|
||||||
value={this.state.changedPassword}
|
|
||||||
placeholder={labels.passwordSettings.enterNew}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TouchableOpacity
|
|
||||||
onPress={() => requestHash('change-pw', this.state.newPassword)}
|
|
||||||
disabled={ !this.state.newPassword }
|
|
||||||
style={styles.settingsButton}>
|
|
||||||
<AppText style={styles.settingsButtonText}>
|
|
||||||
{labels.passwordSettings.changePassword}
|
|
||||||
</AppText>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
}
|
|
||||||
|
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import {
|
||||||
|
ScrollView,
|
||||||
|
} from 'react-native'
|
||||||
|
import TempReminderPicker from './temp-reminder-picker'
|
||||||
|
import PeriodReminderPicker from './period-reminder'
|
||||||
|
|
||||||
|
export default class Settings extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this.state = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<ScrollView>
|
||||||
|
<TempReminderPicker/>
|
||||||
|
<PeriodReminderPicker/>
|
||||||
|
</ScrollView>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-4
@@ -3,13 +3,13 @@ import {
|
|||||||
View,
|
View,
|
||||||
Switch
|
Switch
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import AppText from '../app-text'
|
import AppText from '../../app-text'
|
||||||
import {
|
import {
|
||||||
periodReminderObservable,
|
periodReminderObservable,
|
||||||
savePeriodReminder
|
savePeriodReminder
|
||||||
} from '../../local-storage'
|
} from '../../../local-storage'
|
||||||
import styles from '../../styles/index'
|
import styles from '../../../styles/index'
|
||||||
import { settings as labels } from '../../i18n/en/settings'
|
import labels from '../../../i18n/en/settings'
|
||||||
|
|
||||||
export default class PeriodReminderPicker extends Component {
|
export default class PeriodReminderPicker extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
+5
-5
@@ -5,14 +5,14 @@ import {
|
|||||||
Switch
|
Switch
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import DateTimePicker from 'react-native-modal-datetime-picker-nevo'
|
import DateTimePicker from 'react-native-modal-datetime-picker-nevo'
|
||||||
import AppText from '../app-text'
|
import AppText from '../../app-text'
|
||||||
import {
|
import {
|
||||||
tempReminderObservable,
|
tempReminderObservable,
|
||||||
saveTempReminder
|
saveTempReminder
|
||||||
} from '../../local-storage'
|
} from '../../../local-storage'
|
||||||
import styles from '../../styles/index'
|
import styles from '../../../styles/index'
|
||||||
import { settings as labels } from '../../i18n/en/settings'
|
import labels from '../../../i18n/en/settings'
|
||||||
import padWithZeros from '../helpers/pad-time-with-zeros'
|
import padWithZeros from '../../helpers/pad-time-with-zeros'
|
||||||
|
|
||||||
export default class TempReminderPicker extends Component {
|
export default class TempReminderPicker extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import {
|
||||||
|
TouchableOpacity,
|
||||||
|
ScrollView,
|
||||||
|
} from 'react-native'
|
||||||
|
import styles from '../../styles/index'
|
||||||
|
import settingsLabels from '../../i18n/en/settings'
|
||||||
|
import AppText from '../app-text'
|
||||||
|
|
||||||
|
console.log(settingsLabels.menuTitles)
|
||||||
|
const labels = settingsLabels.menuTitles
|
||||||
|
console.log(settingsLabels.menuTitles)
|
||||||
|
|
||||||
|
const menu = [
|
||||||
|
{title: labels.reminders, component: 'Reminders'},
|
||||||
|
{title: labels.nfpSettings, component: 'NfpSettings'},
|
||||||
|
{title: labels.importExport, component: 'ImportExport'},
|
||||||
|
{title: labels.password, component: 'Password'},
|
||||||
|
{title: labels.about, component: 'About'}
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function SettingsMenu(props) {
|
||||||
|
return (
|
||||||
|
<ScrollView>
|
||||||
|
{ menu.map(menuItem)}
|
||||||
|
</ScrollView>
|
||||||
|
)
|
||||||
|
|
||||||
|
function menuItem(item) {
|
||||||
|
return (
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.settingsSegment}
|
||||||
|
key={item.title}
|
||||||
|
onPress={() => props.navigate(item.component)}
|
||||||
|
>
|
||||||
|
<AppText>{item.title}</AppText>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -92,5 +92,6 @@ export const sharedDialogs = {
|
|||||||
areYouSureToUnset: 'Are you sure you want to unset all entered data?',
|
areYouSureToUnset: 'Are you sure you want to unset all entered data?',
|
||||||
reallyUnsetData: 'Yes, I am sure',
|
reallyUnsetData: 'Yes, I am sure',
|
||||||
save: 'Save',
|
save: 'Save',
|
||||||
unset: 'Unset'
|
unset: 'Unset',
|
||||||
|
disabledInfo: 'There is some data missing'
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-1
@@ -1,3 +1,6 @@
|
|||||||
|
import labels from './settings'
|
||||||
|
const settingsTitles = labels.menuTitles
|
||||||
|
|
||||||
export const shared = {
|
export const shared = {
|
||||||
cancel: 'Cancel',
|
cancel: 'Cancel',
|
||||||
save: 'Save',
|
save: 'Save',
|
||||||
@@ -21,7 +24,12 @@ export const headerTitles = {
|
|||||||
Calendar: 'Calendar',
|
Calendar: 'Calendar',
|
||||||
Chart: 'Chart',
|
Chart: 'Chart',
|
||||||
Stats: 'Statistics',
|
Stats: 'Statistics',
|
||||||
Settings: 'Settings',
|
SettingsMenu: 'Settings',
|
||||||
|
Reminders: settingsTitles.reminders,
|
||||||
|
NfpSettings: settingsTitles.nfpSettings,
|
||||||
|
ImportExport: settingsTitles.importExport,
|
||||||
|
Password: settingsTitles.password,
|
||||||
|
About: settingsTitles.about,
|
||||||
BleedingEditView: 'Bleeding',
|
BleedingEditView: 'Bleeding',
|
||||||
TemperatureEditView: 'Temperature',
|
TemperatureEditView: 'Temperature',
|
||||||
MucusEditView: 'Mucus',
|
MucusEditView: 'Mucus',
|
||||||
|
|||||||
+23
-1
@@ -1,4 +1,12 @@
|
|||||||
export const settings = {
|
|
||||||
|
export default {
|
||||||
|
menuTitles: {
|
||||||
|
reminders: 'Reminders',
|
||||||
|
importExport: 'Import and Export',
|
||||||
|
nfpSettings: 'NFP settings',
|
||||||
|
password: 'Password',
|
||||||
|
about: 'About'
|
||||||
|
},
|
||||||
export: {
|
export: {
|
||||||
errors: {
|
errors: {
|
||||||
noData: 'There is no data to export',
|
noData: 'There is no data to export',
|
||||||
@@ -67,5 +75,19 @@ export const settings = {
|
|||||||
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.',
|
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',
|
deleteBackupReminderTitle: 'Read this before deleting your password',
|
||||||
deleteBackupReminder: 'Deleting your password means your data will no longer be encrypted.\n\nJust to be safe, please backup your data using the export function before deleting your password.\n\nMaking any changes to your password setting will keep your data as it was before and restart the app.',
|
deleteBackupReminder: 'Deleting your password means your data will no longer be encrypted.\n\nJust to be safe, please backup your data using the export function before deleting your password.\n\nMaking any changes to your password setting will keep your data as it was before and restart the app.',
|
||||||
|
},
|
||||||
|
aboutSection: {
|
||||||
|
title: 'About',
|
||||||
|
segmentExplainer: 'Please note that your data is stored locally on your phone and not on a server. We want to ensure that you stay in control of those sensitive information. If you are planning to switch or reset your phone, please remember to export your data before doing so. You can reinstall the app afterwards and import your data.\n\nIf you encounter any technical issues, don\'t hesitate to contact us via email (bl00dyhealth@mailbox.org). You can also contribute to the code base on GitLab (https://gitlab.com/bloodyhealth/drip/).',
|
||||||
|
},
|
||||||
|
preOvu: {
|
||||||
|
title: 'Infertile days at cycle start',
|
||||||
|
note1: "drip applies NFP's rules for calculating infertile days at the start of the cycle (see the ",
|
||||||
|
link: 'wiki',
|
||||||
|
note2: " for more info). However, drip does not currently apply the so called 20-day-rule, which determines infertile days at the cycle start from past cycle lengths in case no past symptothermal info is available."
|
||||||
|
},
|
||||||
|
credits: {
|
||||||
|
title: 'Credits',
|
||||||
|
note: 'Thanks and lots of <3 to all of our contributors, as well as, and especially, Susanne Umscheid for the wonderful visual and logo design, and Paula Härtel for the symptom icons'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
-1
@@ -39,7 +39,8 @@ export default function config(opts) {
|
|||||||
const targetDate = LocalDate.parse(targetDateString)
|
const targetDate = LocalDate.parse(targetDateString)
|
||||||
const lastMensesLocalDate = LocalDate.parse(lastMensesStart.date)
|
const lastMensesLocalDate = LocalDate.parse(lastMensesStart.date)
|
||||||
const diffInDays = lastMensesLocalDate.until(targetDate, DAYS)
|
const diffInDays = lastMensesLocalDate.until(targetDate, DAYS)
|
||||||
|
// take maxCycleLength into account (we don't display cycle day numbers higher than 99 at the moment)
|
||||||
|
if (diffInDays >= 99) return null
|
||||||
// cycle starts at day 1
|
// cycle starts at day 1
|
||||||
return diffInDays + 1
|
return diffInDays + 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,8 @@
|
|||||||
import objectPath from 'object-path'
|
import objectPath from 'object-path'
|
||||||
import { Base64 } from 'js-base64'
|
|
||||||
import { getCycleDaysSortedByDate } from '../../db'
|
|
||||||
import getColumnNamesForCsv from './get-csv-column-names'
|
import getColumnNamesForCsv from './get-csv-column-names'
|
||||||
|
|
||||||
export default function makeDataURI() {
|
|
||||||
const cycleDaysSortedByDate = getCycleDaysSortedByDate()
|
|
||||||
if (!cycleDaysSortedByDate.length) return null
|
|
||||||
|
|
||||||
const csv = transformToCsv(cycleDaysSortedByDate)
|
export default function transformToCsv(cycleDays) {
|
||||||
const encoded = Base64.encodeURI(csv)
|
|
||||||
// this is the MIME type android/libcore/MimeUtils expects, so we oblige
|
|
||||||
return `data:text/comma-separated-values;base64,${encoded}`
|
|
||||||
}
|
|
||||||
|
|
||||||
function transformToCsv(cycleDays) {
|
|
||||||
const columnNames = getColumnNamesForCsv()
|
const columnNames = getColumnNamesForCsv()
|
||||||
const rows = cycleDays
|
const rows = cycleDays
|
||||||
.map(day => {
|
.map(day => {
|
||||||
@@ -23,7 +12,6 @@ function transformToCsv(cycleDays) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map(row => row.join(','))
|
.map(row => row.join(','))
|
||||||
|
|
||||||
rows.unshift(columnNames.join(','))
|
rows.unshift(columnNames.join(','))
|
||||||
return rows.join('\n')
|
return rows.join('\n')
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-11
@@ -1,16 +1,17 @@
|
|||||||
import getFertilityStatus from './sympto'
|
import getFertilityStatus from './sympto'
|
||||||
import cycleModule from './cycle'
|
import cycleModule from './cycle'
|
||||||
import { fertilityStatus } from '../i18n/en/labels'
|
|
||||||
import { useCervixObservable } from '../local-storage'
|
import { useCervixObservable } from '../local-storage'
|
||||||
|
import { fertilityStatus as labels } from '../i18n/en/labels'
|
||||||
|
|
||||||
export function getFertilityStatusForDay(dateString) {
|
export function getFertilityStatusForDay(dateString) {
|
||||||
const status = getCycleStatusForDay(dateString)
|
const status = getCycleStatusForDay(dateString)
|
||||||
if (!status) return {
|
if (!status) return {
|
||||||
status: fertilityStatus.fertile,
|
status: labels.fertile,
|
||||||
phase: null
|
phase: null
|
||||||
}
|
}
|
||||||
|
|
||||||
const phaseNameForDay = Object.keys(status.phases).find(phaseName => {
|
const phases = Object.keys(status.phases)
|
||||||
|
const phaseNameForDay = phases.find(phaseName => {
|
||||||
const phase = status.phases[phaseName]
|
const phase = status.phases[phaseName]
|
||||||
const dayIsAfterPhaseStart = dateString >= phase.start.date
|
const dayIsAfterPhaseStart = dateString >= phase.start.date
|
||||||
let dayIsBeforePhaseEnd
|
let dayIsBeforePhaseEnd
|
||||||
@@ -22,6 +23,12 @@ export function getFertilityStatusForDay(dateString) {
|
|||||||
return dayIsAfterPhaseStart && dayIsBeforePhaseEnd
|
return dayIsAfterPhaseStart && dayIsBeforePhaseEnd
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// if there's only cycle data for the pre phase and the target day is after its end,
|
||||||
|
// the day is in the peri phase
|
||||||
|
if (phases.length === 1 && phases[0] === 'preOvulatory' && !phaseNameForDay) {
|
||||||
|
return formatStatus('periOvulatory', dateString, {phases: {periOvulatory: {}}})
|
||||||
|
}
|
||||||
|
|
||||||
return formatStatus(phaseNameForDay, dateString, status)
|
return formatStatus(phaseNameForDay, dateString, status)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,27 +65,32 @@ function formatStatus(phaseNameForDay, dateString, status) {
|
|||||||
const mapping = {
|
const mapping = {
|
||||||
preOvulatory: () => {
|
preOvulatory: () => {
|
||||||
return {
|
return {
|
||||||
status: fertilityStatus.infertile,
|
status: labels.infertile,
|
||||||
phase: 1,
|
phase: 1,
|
||||||
statusText: fertilityStatus.preOvuText
|
statusText: labels.preOvuText
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
periOvulatory: (dateString, status) => {
|
periOvulatory: (dateString, status) => {
|
||||||
const phaseEnd = status.phases.periOvulatory.end
|
//there might not actually be any data for the phase
|
||||||
|
const peri = status.phases.periOvulatory
|
||||||
|
const phaseEnd = peri && peri.end
|
||||||
|
let s
|
||||||
if (phaseEnd && phaseEnd.date === dateString) {
|
if (phaseEnd && phaseEnd.date === dateString) {
|
||||||
return fertilityStatus.fertileUntilEvening
|
s = labels.fertileUntilEvening
|
||||||
|
} else {
|
||||||
|
s = labels.fertile
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
status: fertilityStatus.fertile,
|
status: s,
|
||||||
phase: 2,
|
phase: 2,
|
||||||
statusText: fertilityStatus.periOvuText
|
statusText: labels.periOvuText
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
postOvulatory: (dateString, status) => {
|
postOvulatory: (dateString, status) => {
|
||||||
return {
|
return {
|
||||||
status: fertilityStatus.infertile,
|
status: labels.infertile,
|
||||||
phase: 3,
|
phase: 3,
|
||||||
statusText: fertilityStatus.postOvuText(status.temperatureShift.rule)
|
statusText: labels.postOvuText(status.temperatureShift.rule)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ function checkIfFirstHighMeasurement(temp, i, temperatureDays, ltl) {
|
|||||||
function getResultForRegularRule(nextDaysAfterPotentialFhm, ltl) {
|
function getResultForRegularRule(nextDaysAfterPotentialFhm, ltl) {
|
||||||
if (!nextDaysAfterPotentialFhm.every(day => day.temp > ltl)) return false
|
if (!nextDaysAfterPotentialFhm.every(day => day.temp > ltl)) return false
|
||||||
const thirdDay = nextDaysAfterPotentialFhm[1]
|
const thirdDay = nextDaysAfterPotentialFhm[1]
|
||||||
if (rounded(thirdDay.temp - ltl, 0.1) < 0.2) return false
|
if (isLessThan0Point2(thirdDay.temp - ltl)) return false
|
||||||
return {
|
return {
|
||||||
detected: true,
|
detected: true,
|
||||||
rule: 0,
|
rule: 0,
|
||||||
@@ -77,7 +77,7 @@ function getResultForSecondExceptionRule(nextDaysAfterPotentialFhm, ltl) {
|
|||||||
if (nextDaysAfterPotentialFhm.length < 3) return false
|
if (nextDaysAfterPotentialFhm.length < 3) return false
|
||||||
if (secondOrThirdTempIsAtOrBelowLtl(nextDaysAfterPotentialFhm, ltl)) {
|
if (secondOrThirdTempIsAtOrBelowLtl(nextDaysAfterPotentialFhm, ltl)) {
|
||||||
const fourthDay = nextDaysAfterPotentialFhm[2]
|
const fourthDay = nextDaysAfterPotentialFhm[2]
|
||||||
if (rounded(fourthDay.temp - ltl, 0.1) >= 0.2) {
|
if (isBiggerOrEqual0Point2(fourthDay.temp - ltl)) {
|
||||||
return {
|
return {
|
||||||
detected: true,
|
detected: true,
|
||||||
rule: 2,
|
rule: 2,
|
||||||
@@ -104,3 +104,19 @@ function rounded(val, step) {
|
|||||||
// we round the difference because of JS decimal weirdness
|
// we round the difference because of JS decimal weirdness
|
||||||
return Math.round(val * inverted) / inverted
|
return Math.round(val * inverted) / inverted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// since we're dealing with floats, there is some imprecision in comparisons,
|
||||||
|
// so we add an error margin that is definitely much smaller than any possible
|
||||||
|
// actual difference between values
|
||||||
|
// see https://floating-point-gui.de/errors/comparison/ for background
|
||||||
|
|
||||||
|
const errorMargin = 0.0001
|
||||||
|
|
||||||
|
function isLessThan0Point2(val) {
|
||||||
|
return val < (0.2 - errorMargin)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isBiggerOrEqual0Point2(val) {
|
||||||
|
return val >= (0.2 - errorMargin)
|
||||||
|
}
|
||||||
Generated
+232
-212
@@ -771,12 +771,26 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ajv": {
|
"ajv": {
|
||||||
"version": "4.11.8",
|
"version": "6.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.1.tgz",
|
||||||
"integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=",
|
"integrity": "sha512-ZoJjft5B+EJBjUyu9C9Hc0OZyPZSSlOF+plzouTrg6UlA8f+e/n8NIgBFG/9tppJtpPWfthHakK7juJdNDODww==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"co": "^4.6.0",
|
"fast-deep-equal": "^2.0.1",
|
||||||
"json-stable-stringify": "^1.0.1"
|
"fast-json-stable-stringify": "^2.0.0",
|
||||||
|
"json-schema-traverse": "^0.4.1",
|
||||||
|
"uri-js": "^4.2.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"fast-deep-equal": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
|
||||||
|
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
|
||||||
|
},
|
||||||
|
"json-schema-traverse": {
|
||||||
|
"version": "0.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||||
|
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ajv-keywords": {
|
"ajv-keywords": {
|
||||||
@@ -1248,9 +1262,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"assert-plus": {
|
"assert-plus": {
|
||||||
"version": "0.2.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
|
||||||
"integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ="
|
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
|
||||||
},
|
},
|
||||||
"assertion-error": {
|
"assertion-error": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
@@ -1287,9 +1301,9 @@
|
|||||||
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
|
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
|
||||||
},
|
},
|
||||||
"aws-sign2": {
|
"aws-sign2": {
|
||||||
"version": "0.6.0",
|
"version": "0.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
|
||||||
"integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8="
|
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
|
||||||
},
|
},
|
||||||
"aws4": {
|
"aws4": {
|
||||||
"version": "1.7.0",
|
"version": "1.7.0",
|
||||||
@@ -2218,18 +2232,11 @@
|
|||||||
"integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw=="
|
"integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw=="
|
||||||
},
|
},
|
||||||
"basic-auth": {
|
"basic-auth": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
||||||
"integrity": "sha1-AV2z81PgLlY3d1X5YnQuiYHnu7o=",
|
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"safe-buffer": "5.1.1"
|
"safe-buffer": "5.1.2"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"safe-buffer": {
|
|
||||||
"version": "5.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
|
|
||||||
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bcrypt-pbkdf": {
|
"bcrypt-pbkdf": {
|
||||||
@@ -2255,27 +2262,11 @@
|
|||||||
"safe-buffer": "^5.1.1"
|
"safe-buffer": "^5.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"block-stream": {
|
|
||||||
"version": "0.0.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
|
|
||||||
"integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
|
|
||||||
"requires": {
|
|
||||||
"inherits": "~2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"bluebird": {
|
"bluebird": {
|
||||||
"version": "3.5.1",
|
"version": "3.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
|
||||||
"integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA=="
|
"integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA=="
|
||||||
},
|
},
|
||||||
"boom": {
|
|
||||||
"version": "2.10.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz",
|
|
||||||
"integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=",
|
|
||||||
"requires": {
|
|
||||||
"hoek": "2.x.x"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"bplist-creator": {
|
"bplist-creator": {
|
||||||
"version": "0.0.7",
|
"version": "0.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.0.7.tgz",
|
||||||
@@ -2721,14 +2712,6 @@
|
|||||||
"which": "^1.2.9"
|
"which": "^1.2.9"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cryptiles": {
|
|
||||||
"version": "2.0.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz",
|
|
||||||
"integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=",
|
|
||||||
"requires": {
|
|
||||||
"boom": "2.x.x"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"csvtojson": {
|
"csvtojson": {
|
||||||
"version": "2.0.8",
|
"version": "2.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/csvtojson/-/csvtojson-2.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/csvtojson/-/csvtojson-2.0.8.tgz",
|
||||||
@@ -3640,12 +3623,12 @@
|
|||||||
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
|
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
|
||||||
},
|
},
|
||||||
"form-data": {
|
"form-data": {
|
||||||
"version": "2.1.4",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
|
||||||
"integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=",
|
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"asynckit": "^0.4.0",
|
"asynckit": "^0.4.0",
|
||||||
"combined-stream": "^1.0.5",
|
"combined-stream": "^1.0.6",
|
||||||
"mime-types": "^2.1.12"
|
"mime-types": "^2.1.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -3677,6 +3660,14 @@
|
|||||||
"klaw": "^1.0.0"
|
"klaw": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"fs-minipass": {
|
||||||
|
"version": "1.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz",
|
||||||
|
"integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
|
||||||
|
"requires": {
|
||||||
|
"minipass": "^2.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"fs.realpath": {
|
"fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
@@ -4144,27 +4135,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fstream": {
|
|
||||||
"version": "1.0.11",
|
|
||||||
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
|
|
||||||
"integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=",
|
|
||||||
"requires": {
|
|
||||||
"graceful-fs": "^4.1.2",
|
|
||||||
"inherits": "~2.0.0",
|
|
||||||
"mkdirp": ">=0.5 0",
|
|
||||||
"rimraf": "2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fstream-ignore": {
|
|
||||||
"version": "1.0.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz",
|
|
||||||
"integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=",
|
|
||||||
"requires": {
|
|
||||||
"fstream": "^1.0.0",
|
|
||||||
"inherits": "2",
|
|
||||||
"minimatch": "^3.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"function-bind": {
|
"function-bind": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||||
@@ -4313,17 +4283,17 @@
|
|||||||
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE="
|
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE="
|
||||||
},
|
},
|
||||||
"har-schema": {
|
"har-schema": {
|
||||||
"version": "1.0.5",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
|
||||||
"integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4="
|
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
|
||||||
},
|
},
|
||||||
"har-validator": {
|
"har-validator": {
|
||||||
"version": "4.2.1",
|
"version": "5.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
|
||||||
"integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=",
|
"integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"ajv": "^4.9.1",
|
"ajv": "^6.5.5",
|
||||||
"har-schema": "^1.0.5"
|
"har-schema": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"has": {
|
"has": {
|
||||||
@@ -4400,28 +4370,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hawk": {
|
|
||||||
"version": "3.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz",
|
|
||||||
"integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=",
|
|
||||||
"requires": {
|
|
||||||
"boom": "2.x.x",
|
|
||||||
"cryptiles": "2.x.x",
|
|
||||||
"hoek": "2.x.x",
|
|
||||||
"sntp": "1.x.x"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"he": {
|
"he": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
|
||||||
"integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
|
"integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"hoek": {
|
|
||||||
"version": "2.16.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
|
|
||||||
"integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0="
|
|
||||||
},
|
|
||||||
"home-or-tmp": {
|
"home-or-tmp": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
|
||||||
@@ -4482,11 +4436,11 @@
|
|||||||
"integrity": "sha1-p8TnWq6C87tJBOT0P2FWc7TVGMM="
|
"integrity": "sha1-p8TnWq6C87tJBOT0P2FWc7TVGMM="
|
||||||
},
|
},
|
||||||
"http-signature": {
|
"http-signature": {
|
||||||
"version": "1.1.1",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
||||||
"integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=",
|
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"assert-plus": "^0.2.0",
|
"assert-plus": "^1.0.0",
|
||||||
"jsprim": "^1.2.2",
|
"jsprim": "^1.2.2",
|
||||||
"sshpk": "^1.7.0"
|
"sshpk": "^1.7.0"
|
||||||
}
|
}
|
||||||
@@ -4510,6 +4464,14 @@
|
|||||||
"integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==",
|
"integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"ignore-walk": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
|
||||||
|
"requires": {
|
||||||
|
"minimatch": "^3.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"image-size": {
|
"image-size": {
|
||||||
"version": "0.6.3",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.6.3.tgz",
|
||||||
@@ -5184,9 +5146,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"merge": {
|
"merge": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/merge/-/merge-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz",
|
||||||
"integrity": "sha1-dTHjnUlJwoGma4xabgJl6LBYlNo="
|
"integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ=="
|
||||||
},
|
},
|
||||||
"merge-stream": {
|
"merge-stream": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
@@ -5629,13 +5591,13 @@
|
|||||||
"integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y="
|
"integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y="
|
||||||
},
|
},
|
||||||
"morgan": {
|
"morgan": {
|
||||||
"version": "1.9.0",
|
"version": "1.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz",
|
||||||
"integrity": "sha1-0B+mxlhZt2/PMbPLU6OCGjEdgFE=",
|
"integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"basic-auth": "~2.0.0",
|
"basic-auth": "~2.0.0",
|
||||||
"debug": "2.6.9",
|
"debug": "2.6.9",
|
||||||
"depd": "~1.1.1",
|
"depd": "~1.1.2",
|
||||||
"on-finished": "~2.3.0",
|
"on-finished": "~2.3.0",
|
||||||
"on-headers": "~1.0.1"
|
"on-headers": "~1.0.1"
|
||||||
}
|
}
|
||||||
@@ -5653,8 +5615,7 @@
|
|||||||
"nan": {
|
"nan": {
|
||||||
"version": "2.11.0",
|
"version": "2.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/nan/-/nan-2.11.0.tgz",
|
||||||
"integrity": "sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw==",
|
"integrity": "sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw=="
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"nanomatch": {
|
"nanomatch": {
|
||||||
"version": "1.2.13",
|
"version": "1.2.13",
|
||||||
@@ -5719,6 +5680,23 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz",
|
||||||
"integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M="
|
"integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M="
|
||||||
},
|
},
|
||||||
|
"needle": {
|
||||||
|
"version": "2.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/needle/-/needle-2.2.4.tgz",
|
||||||
|
"integrity": "sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA==",
|
||||||
|
"requires": {
|
||||||
|
"debug": "^2.1.2",
|
||||||
|
"iconv-lite": "^0.4.4",
|
||||||
|
"sax": "^1.2.4"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"sax": {
|
||||||
|
"version": "1.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
||||||
|
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"negotiator": {
|
"negotiator": {
|
||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
|
||||||
@@ -5738,6 +5716,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
|
||||||
"integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs="
|
"integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs="
|
||||||
},
|
},
|
||||||
|
"node-machine-id": {
|
||||||
|
"version": "1.1.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-machine-id/-/node-machine-id-1.1.10.tgz",
|
||||||
|
"integrity": "sha512-6SVxo3Ic2Qc09z1rCJh3No7ubizPLszImsMQnZZWfzeOC6SYU4orN214++c3ikB8uaP/A6dwSlO88A3ohI5oNA=="
|
||||||
|
},
|
||||||
"node-modules-regexp": {
|
"node-modules-regexp": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz",
|
||||||
@@ -5755,21 +5738,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node-pre-gyp": {
|
"node-pre-gyp": {
|
||||||
"version": "0.6.39",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz",
|
"resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz",
|
||||||
"integrity": "sha512-OsJV74qxnvz/AMGgcfZoDaeDXKD3oY3QVIbBmwszTFkRisTSXbMQyn4UWzUMOtA5SVhrBZOTp0wcoSBgfMfMmQ==",
|
"integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"detect-libc": "^1.0.2",
|
"detect-libc": "^1.0.2",
|
||||||
"hawk": "3.1.3",
|
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
|
"needle": "^2.2.1",
|
||||||
"nopt": "^4.0.1",
|
"nopt": "^4.0.1",
|
||||||
|
"npm-packlist": "^1.1.6",
|
||||||
"npmlog": "^4.0.2",
|
"npmlog": "^4.0.2",
|
||||||
"rc": "^1.1.7",
|
"rc": "^1.2.7",
|
||||||
"request": "2.81.0",
|
|
||||||
"rimraf": "^2.6.1",
|
"rimraf": "^2.6.1",
|
||||||
"semver": "^5.3.0",
|
"semver": "^5.3.0",
|
||||||
"tar": "^2.2.1",
|
"tar": "^4"
|
||||||
"tar-pack": "^3.4.0"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"gauge": {
|
"gauge": {
|
||||||
@@ -5806,35 +5788,6 @@
|
|||||||
"set-blocking": "~2.0.0"
|
"set-blocking": "~2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"request": {
|
|
||||||
"version": "2.81.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz",
|
|
||||||
"integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=",
|
|
||||||
"requires": {
|
|
||||||
"aws-sign2": "~0.6.0",
|
|
||||||
"aws4": "^1.2.1",
|
|
||||||
"caseless": "~0.12.0",
|
|
||||||
"combined-stream": "~1.0.5",
|
|
||||||
"extend": "~3.0.0",
|
|
||||||
"forever-agent": "~0.6.1",
|
|
||||||
"form-data": "~2.1.1",
|
|
||||||
"har-validator": "~4.2.1",
|
|
||||||
"hawk": "~3.1.3",
|
|
||||||
"http-signature": "~1.1.0",
|
|
||||||
"is-typedarray": "~1.0.0",
|
|
||||||
"isstream": "~0.1.2",
|
|
||||||
"json-stringify-safe": "~5.0.1",
|
|
||||||
"mime-types": "~2.1.7",
|
|
||||||
"oauth-sign": "~0.8.1",
|
|
||||||
"performance-now": "^0.2.0",
|
|
||||||
"qs": "~6.4.0",
|
|
||||||
"safe-buffer": "^5.0.1",
|
|
||||||
"stringstream": "~0.0.4",
|
|
||||||
"tough-cookie": "~2.3.0",
|
|
||||||
"tunnel-agent": "^0.6.0",
|
|
||||||
"uuid": "^3.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"string-width": {
|
"string-width": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||||
@@ -5937,6 +5890,20 @@
|
|||||||
"remove-trailing-separator": "^1.0.1"
|
"remove-trailing-separator": "^1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"npm-bundled": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g=="
|
||||||
|
},
|
||||||
|
"npm-packlist": {
|
||||||
|
"version": "1.1.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.12.tgz",
|
||||||
|
"integrity": "sha512-WJKFOVMeAlsU/pjXuqVdzU0WfgtIBCupkEVwn+1Y0ERAbUfWw8R4GjgVbaKnUjRoD2FoQbHOCbOyT5Mbs9Lw4g==",
|
||||||
|
"requires": {
|
||||||
|
"ignore-walk": "^3.0.1",
|
||||||
|
"npm-bundled": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"npm-run-path": {
|
"npm-run-path": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
|
||||||
@@ -6258,9 +6225,9 @@
|
|||||||
"integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
|
"integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
|
||||||
},
|
},
|
||||||
"performance-now": {
|
"performance-now": {
|
||||||
"version": "0.2.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||||
"integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU="
|
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
|
||||||
},
|
},
|
||||||
"pify": {
|
"pify": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
@@ -6387,20 +6354,25 @@
|
|||||||
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
|
||||||
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
|
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
|
||||||
},
|
},
|
||||||
|
"psl": {
|
||||||
|
"version": "1.1.31",
|
||||||
|
"resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz",
|
||||||
|
"integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw=="
|
||||||
|
},
|
||||||
"punycode": {
|
"punycode": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
|
||||||
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
|
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
|
||||||
},
|
},
|
||||||
"qs": {
|
"qs": {
|
||||||
"version": "6.4.0",
|
"version": "6.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
|
||||||
"integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM="
|
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
|
||||||
},
|
},
|
||||||
"querystringify": {
|
"querystringify": {
|
||||||
"version": "2.0.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.0.tgz",
|
||||||
"integrity": "sha512-eTPo5t/4bgaMNZxyjWx6N2a6AuE0mq51KWvpc7nU/MAqixcI6v6KrGUKES0HaomdnolQBBXU/++X6/QQ9KL4tw=="
|
"integrity": "sha512-sluvZZ1YiTLD5jsqZcDmFyV2EwToyXZBfpoVOmktMmW+VEnhgakFHnasVph65fOjGPTWN0Nw3+XQaSeMayr0kg=="
|
||||||
},
|
},
|
||||||
"randomatic": {
|
"randomatic": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
@@ -6746,26 +6718,32 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"realm": {
|
"realm": {
|
||||||
"version": "2.14.0",
|
"version": "2.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/realm/-/realm-2.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/realm/-/realm-2.21.0.tgz",
|
||||||
"integrity": "sha512-HiCj/ZE3iZNOyWexVkhDetNEfcTtSdgsg5lop7g7rcCsE+4NM3hDRxvBjrBSk/mgs6HXBAhHohzGmREbs6piRg==",
|
"integrity": "sha512-2XxkVogKOObhwBUcP7NPvyA9kU/HIeopVbAGgKanJWYw5z09J+I+q1CN2gCVR5EC4+H55Ht4loEhjDud5+LEYQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"command-line-args": "^4.0.6",
|
"command-line-args": "^4.0.6",
|
||||||
"decompress": "^4.2.0",
|
"decompress": "^4.2.0",
|
||||||
"deepmerge": "2.1.0",
|
"deepmerge": "2.1.0",
|
||||||
"fs-extra": "^4.0.2",
|
"fs-extra": "^4.0.2",
|
||||||
"ini": "^1.3.4",
|
"ini": "^1.3.4",
|
||||||
"nan": "2.8.0",
|
"nan": "^2.10.0",
|
||||||
"node-fetch": "^1.6.3",
|
"node-fetch": "^1.6.3",
|
||||||
"node-pre-gyp": "^0.6.36",
|
"node-machine-id": "^1.1.10",
|
||||||
|
"node-pre-gyp": "^0.11.0",
|
||||||
"progress": "^2.0.0",
|
"progress": "^2.0.0",
|
||||||
"prop-types": "^15.5.10",
|
"prop-types": "^15.5.10",
|
||||||
"request": "^2.78.0",
|
"request": "^2.88.0",
|
||||||
"stream-counter": "^1.0.0",
|
"stream-counter": "^1.0.0",
|
||||||
"sync-request": "^3.0.1",
|
"sync-request": "^3.0.1",
|
||||||
"url-parse": "^1.2.0"
|
"url-parse": "^1.2.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"aws4": {
|
||||||
|
"version": "1.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
|
||||||
|
"integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
|
||||||
|
},
|
||||||
"fs-extra": {
|
"fs-extra": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz",
|
||||||
@@ -6784,10 +6762,46 @@
|
|||||||
"graceful-fs": "^4.1.6"
|
"graceful-fs": "^4.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nan": {
|
"oauth-sign": {
|
||||||
"version": "2.8.0",
|
"version": "0.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
|
||||||
"integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo="
|
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
|
||||||
|
},
|
||||||
|
"request": {
|
||||||
|
"version": "2.88.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
|
||||||
|
"integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
|
||||||
|
"requires": {
|
||||||
|
"aws-sign2": "~0.7.0",
|
||||||
|
"aws4": "^1.8.0",
|
||||||
|
"caseless": "~0.12.0",
|
||||||
|
"combined-stream": "~1.0.6",
|
||||||
|
"extend": "~3.0.2",
|
||||||
|
"forever-agent": "~0.6.1",
|
||||||
|
"form-data": "~2.3.2",
|
||||||
|
"har-validator": "~5.1.0",
|
||||||
|
"http-signature": "~1.2.0",
|
||||||
|
"is-typedarray": "~1.0.0",
|
||||||
|
"isstream": "~0.1.2",
|
||||||
|
"json-stringify-safe": "~5.0.1",
|
||||||
|
"mime-types": "~2.1.19",
|
||||||
|
"oauth-sign": "~0.9.0",
|
||||||
|
"performance-now": "^2.1.0",
|
||||||
|
"qs": "~6.5.2",
|
||||||
|
"safe-buffer": "^5.1.2",
|
||||||
|
"tough-cookie": "~2.4.3",
|
||||||
|
"tunnel-agent": "^0.6.0",
|
||||||
|
"uuid": "^3.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tough-cookie": {
|
||||||
|
"version": "2.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
|
||||||
|
"integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
|
||||||
|
"requires": {
|
||||||
|
"psl": "^1.1.24",
|
||||||
|
"punycode": "^1.4.1"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -7698,14 +7712,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sntp": {
|
|
||||||
"version": "1.0.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz",
|
|
||||||
"integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=",
|
|
||||||
"requires": {
|
|
||||||
"hoek": "2.x.x"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"source-map": {
|
"source-map": {
|
||||||
"version": "0.5.7",
|
"version": "0.5.7",
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||||
@@ -7891,11 +7897,6 @@
|
|||||||
"safe-buffer": "~5.1.0"
|
"safe-buffer": "~5.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"stringstream": {
|
|
||||||
"version": "0.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz",
|
|
||||||
"integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA=="
|
|
||||||
},
|
|
||||||
"strip-ansi": {
|
"strip-ansi": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||||
@@ -8000,41 +8001,50 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tar": {
|
"tar": {
|
||||||
"version": "2.2.1",
|
"version": "4.4.8",
|
||||||
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz",
|
||||||
"integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=",
|
"integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"block-stream": "*",
|
"chownr": "^1.1.1",
|
||||||
"fstream": "^1.0.2",
|
"fs-minipass": "^1.2.5",
|
||||||
"inherits": "2"
|
"minipass": "^2.3.4",
|
||||||
|
"minizlib": "^1.1.1",
|
||||||
|
"mkdirp": "^0.5.0",
|
||||||
|
"safe-buffer": "^5.1.2",
|
||||||
|
"yallist": "^3.0.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"chownr": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g=="
|
||||||
|
},
|
||||||
|
"minizlib": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==",
|
||||||
|
"requires": {
|
||||||
|
"minipass": "^2.2.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tar-pack": {
|
"yallist": {
|
||||||
"version": "3.4.1",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
|
||||||
"integrity": "sha512-PPRybI9+jM5tjtCbN2cxmmRU7YmqT3Zv/UDy48tAh2XRkLa9bAORtSWLkVc13+GJF+cdTh1yEnHEk3cpTaL5Kg==",
|
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A=="
|
||||||
"requires": {
|
}
|
||||||
"debug": "^2.2.0",
|
|
||||||
"fstream": "^1.0.10",
|
|
||||||
"fstream-ignore": "^1.0.5",
|
|
||||||
"once": "^1.3.3",
|
|
||||||
"readable-stream": "^2.1.4",
|
|
||||||
"rimraf": "^2.5.1",
|
|
||||||
"tar": "^2.2.1",
|
|
||||||
"uid-number": "^0.0.6"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tar-stream": {
|
"tar-stream": {
|
||||||
"version": "1.6.1",
|
"version": "1.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz",
|
||||||
"integrity": "sha512-IFLM5wp3QrJODQFPm6/to3LJZrONdBY/otxcvDIQzu217zKye6yVR3hhi9lAjrC2Z+m/j5oDxMPb1qcd8cIvpA==",
|
"integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"bl": "^1.0.0",
|
"bl": "^1.0.0",
|
||||||
"buffer-alloc": "^1.1.0",
|
"buffer-alloc": "^1.2.0",
|
||||||
"end-of-stream": "^1.0.0",
|
"end-of-stream": "^1.0.0",
|
||||||
"fs-constants": "^1.0.0",
|
"fs-constants": "^1.0.0",
|
||||||
"readable-stream": "^2.3.0",
|
"readable-stream": "^2.3.0",
|
||||||
"to-buffer": "^1.1.0",
|
"to-buffer": "^1.1.1",
|
||||||
"xtend": "^4.0.0"
|
"xtend": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -8299,20 +8309,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"uid-number": {
|
|
||||||
"version": "0.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz",
|
|
||||||
"integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE="
|
|
||||||
},
|
|
||||||
"ultron": {
|
"ultron": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",
|
||||||
"integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po="
|
"integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po="
|
||||||
},
|
},
|
||||||
"unbzip2-stream": {
|
"unbzip2-stream": {
|
||||||
"version": "1.2.5",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.1.tgz",
|
||||||
"integrity": "sha512-izD3jxT8xkzwtXRUZjtmRwKnZoeECrfZ8ra/ketwOcusbZEp4mjULMnJOCfTDZBgGQAAY1AJ/IgxcwkavcX9Og==",
|
"integrity": "sha512-fIZnvdjblYs7Cru/xC6tCPVhz7JkYcVQQkePwMLyQELzYTds2Xn8QefPVnvdVhhZqubxNA1cASXEH5wcK0Bucw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"buffer": "^3.0.1",
|
"buffer": "^3.0.1",
|
||||||
"through": "^2.3.6"
|
"through": "^2.3.6"
|
||||||
@@ -8425,15 +8430,30 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"uri-js": {
|
||||||
|
"version": "4.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
|
||||||
|
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
|
||||||
|
"requires": {
|
||||||
|
"punycode": "^2.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"punycode": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"urix": {
|
"urix": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
|
||||||
"integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI="
|
"integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI="
|
||||||
},
|
},
|
||||||
"url-parse": {
|
"url-parse": {
|
||||||
"version": "1.4.3",
|
"version": "1.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.4.tgz",
|
||||||
"integrity": "sha512-rh+KuAW36YKo0vClhQzLLveoj8FwPJNu65xLb7Mrt+eZht0IPT0IXgSv8gcMegZ6NvjJUALf6Mf25POlMwD1Fw==",
|
"integrity": "sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"querystringify": "^2.0.0",
|
"querystringify": "^2.0.0",
|
||||||
"requires-port": "^1.0.0"
|
"requires-port": "^1.0.0"
|
||||||
|
|||||||
+4
-2
@@ -12,7 +12,8 @@
|
|||||||
"log": "react-native log-android",
|
"log": "react-native log-android",
|
||||||
"test": "mocha --recursive --require @babel/register test && npm run lint",
|
"test": "mocha --recursive --require @babel/register test && npm run lint",
|
||||||
"test-watch": "mocha --recursive --require @babel/register --watch test",
|
"test-watch": "mocha --recursive --require @babel/register --watch test",
|
||||||
"lint": "eslint components lib test"
|
"lint": "eslint components lib test",
|
||||||
|
"devtool": "adb shell input keyevent 82"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ptomasroos/react-native-multi-slider": "^1.0.0",
|
"@ptomasroos/react-native-multi-slider": "^1.0.0",
|
||||||
@@ -26,6 +27,7 @@
|
|||||||
"nodejs-mobile-react-native": "^0.3.0",
|
"nodejs-mobile-react-native": "^0.3.0",
|
||||||
"object-path": "^0.11.4",
|
"object-path": "^0.11.4",
|
||||||
"obv": "0.0.1",
|
"obv": "0.0.1",
|
||||||
|
"prop-types": "^15.6.2",
|
||||||
"react": "16.4.1",
|
"react": "16.4.1",
|
||||||
"react-native": "~0.56.0",
|
"react-native": "~0.56.0",
|
||||||
"react-native-calendars": "^1.19.3",
|
"react-native-calendars": "^1.19.3",
|
||||||
@@ -36,7 +38,7 @@
|
|||||||
"react-native-restart": "0.0.7",
|
"react-native-restart": "0.0.7",
|
||||||
"react-native-share": "^1.1.3",
|
"react-native-share": "^1.1.3",
|
||||||
"react-native-vector-icons": "^5.0.0",
|
"react-native-vector-icons": "^5.0.0",
|
||||||
"realm": "^2.7.1"
|
"realm": "^2.21.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/register": "^7.0.0-beta.55",
|
"@babel/register": "^7.0.0-beta.55",
|
||||||
|
|||||||
@@ -43,6 +43,10 @@ export default StyleSheet.create({
|
|||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
fontFamily: textFont
|
fontFamily: textFont
|
||||||
},
|
},
|
||||||
|
link: {
|
||||||
|
color: cycleDayColor,
|
||||||
|
textDecorationLine: 'underline'
|
||||||
|
},
|
||||||
title: {
|
title: {
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
color: 'black',
|
color: 'black',
|
||||||
@@ -263,6 +267,9 @@ export default StyleSheet.create({
|
|||||||
padding: 7,
|
padding: 7,
|
||||||
fontFamily: 'textFont'
|
fontFamily: 'textFont'
|
||||||
},
|
},
|
||||||
|
settingsSegmentLast: {
|
||||||
|
marginBottom: defaultTopMargin,
|
||||||
|
},
|
||||||
settingsSegmentTitle: {
|
settingsSegmentTitle: {
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
fontFamily: 'textFont'
|
fontFamily: 'textFont'
|
||||||
@@ -414,4 +421,8 @@ export const iconStyles = {
|
|||||||
menuIconInactive: {
|
menuIconInactive: {
|
||||||
color: colorInActive,
|
color: colorInActive,
|
||||||
},
|
},
|
||||||
|
infoInHeading: {
|
||||||
|
marginRight: 5,
|
||||||
|
color: 'black'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import dirtyChai from 'dirty-chai'
|
|||||||
const expect = chai.expect
|
const expect = chai.expect
|
||||||
chai.use(dirtyChai)
|
chai.use(dirtyChai)
|
||||||
|
|
||||||
import getSensiplanMucus from '../lib/sensiplan-mucus'
|
import getSensiplanMucus from '../lib/nfp-mucus'
|
||||||
|
|
||||||
describe('getSensiplanMucus', () => {
|
describe('getSensiplanMucus', () => {
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user