merge master

This commit is contained in:
susott
2019-01-04 20:39:42 +01:00
50 changed files with 887 additions and 694 deletions
-6
View File
@@ -1,6 +0,0 @@
[android]
target = Google Inc.:Google APIs:23
[maven_repositories]
central = https://repo1.maven.org/maven2
-54
View File
@@ -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
+1 -2
View File
@@ -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`.
## Debugging
When running into an old version of the app try to run the following command first:
`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`
In order to see logging output from the app, run `npm run log` in a separate terminal.
## 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)
+7 -2
View File
@@ -1,5 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.drip">
<manifest
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.SYSTEM_ALERT_WINDOW"/>
@@ -36,9 +39,11 @@
android:grantUriPermissions="true"
android:exported="false">
<meta-data
tools:replace="android:resource"
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
<meta-data android:name="com.dieam.reactnativepushnotification.notification_channel_name"
android:value="drip-notification"/>
<meta-data android:name="com.dieam.reactnativepushnotification.notification_channel_description"
@@ -1,4 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<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>
+8
View File
@@ -35,3 +35,11 @@ ext {
targetSdkVersion = 26
supportLibVersion = "26.1.0"
}
subprojects {project ->
buildscript {
repositories {
maven { url = 'https://dl.bintray.com/android/android-tools/' }
}
}
}
+22 -25
View File
@@ -1,33 +1,30 @@
import React, { Component } from 'react'
import React from 'react'
import { Text } from 'react-native'
import styles from "../styles"
export default class AppText extends Component {
render() {
return (
<Text style={[styles.appText, this.props.style]}>
{this.props.children}
</Text>
)
}
export default function AppText(props) {
return (
<Text
style={[styles.appText, props.style]}
onPress={props.onPress}
>
{props.children}
</Text>
)
}
export class AppTextLight extends Component {
render() {
return (
<Text style={[styles.appTextLight, this.props.style]}>
{this.props.children}
</Text>
)
}
export function AppTextLight(props) {
return (
<Text style={[styles.appTextLight, props.style]}>
{props.children}
</Text>
)
}
export class SymptomSectionHeader extends Component {
render() {
return (
<AppText style={styles.symptomViewHeading}>
{this.props.children}
</AppText>
)
}
export function SymptomSectionHeader(props) {
return (
<AppText style={styles.symptomViewHeading}>
{props.children}
</AppText>
)
}
+7 -2
View File
@@ -7,7 +7,8 @@ import Calendar from './calendar'
import CycleDay from './cycle-day/cycle-day-overview'
import symptomViews from './cycle-day/symptoms'
import Chart from './chart/chart'
import Settings from './settings'
import SettingsMenu from './settings/settings-menu'
import settingsViews from './settings'
import Stats from './stats'
import {headerTitles, menuTitles} from '../i18n/en/labels'
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 isSettingsView = name => Object.keys(settingsViews).includes(name)
const isMenuItem = name => Object.keys(menuTitles).includes(name)
export default class App extends Component {
@@ -58,6 +60,8 @@ export default class App extends Component {
this.navigate(
this.originForSymptomView, { date: this.state.currentProps.date }
)
} else if (isSettingsView(this.state.currentPage)) {
this.navigate('SettingsMenu')
} else if(this.state.currentPage === 'CycleDay') {
this.navigate(this.menuOrigin)
} else {
@@ -68,7 +72,7 @@ export default class App extends Component {
render() {
const page = {
Home, Calendar, CycleDay, Chart, Settings, Stats, ...symptomViews
Home, Calendar, CycleDay, Chart, SettingsMenu, ...settingsViews, Stats, ...symptomViews
}[this.state.currentPage]
return (
<View style={{flex: 1}}>
@@ -81,6 +85,7 @@ export default class App extends Component {
title={headerTitlesLowerCase[this.state.currentPage]}
isSymptomView={true}
goBack={this.handleBackButtonPress}
date={this.state.currentProps.date}
/>}
+1 -1
View File
@@ -81,7 +81,7 @@ export default class CycleChart extends Component {
this.chartSymptoms.push('temperature')
}
const columnData = this.makeColumnInfo(nfpLines(), this.chartSymptoms)
const columnData = this.makeColumnInfo()
this.setState({
columns: columnData,
chartHeight: height
+1 -1
View File
@@ -52,7 +52,7 @@ export default class DayColumn extends Component {
this.fhmAndLtl = props.getFhmAndLtlInfo(
props.dateString,
props.temp,
this.data.temperature,
props.columnHeight
)
}
@@ -1,12 +1,13 @@
import React, { Component } from 'react'
import {
View, TouchableOpacity, Text, Alert
View, TouchableOpacity, Text, Alert, ToastAndroid
} from 'react-native'
import Icon from 'react-native-vector-icons/MaterialCommunityIcons'
import { saveSymptom } from '../../../db'
import styles, {iconStyles} from '../../../styles'
import {sharedDialogs as labels} from '../../../i18n/en/cycle-day'
export default class ActionButtonFooter extends Component {
render() {
const {
@@ -43,19 +44,26 @@ export default class ActionButtonFooter extends Component {
}, {
title: labels.save,
action: () => {
saveAction()
if (autoShowDayView) navigateToOverView()
if(saveDisabled) {
ToastAndroid.show(labels.disabledInfo, ToastAndroid.LONG);
console.log()
} else {
saveAction()
if (autoShowDayView) navigateToOverView()
}
},
disabledCondition: saveDisabled,
icon: 'content-save-outline'
}
]
return (
<View style={styles.menu}>
{buttons.map(({ title, action, disabledCondition, icon }, i) => {
const textStyle = [styles.menuText]
if (disabledCondition) textStyle.push(styles.menuTextInActive)
if (disabledCondition) {
textStyle.push(styles.menuTextInActive);
}
const iconStyle = disabledCondition ?
Object.assign(
{},
@@ -68,7 +76,6 @@ export default class ActionButtonFooter extends Component {
<TouchableOpacity
onPress={action}
style={styles.menuItem}
disabled={disabledCondition}
key={i.toString()}
>
<Icon name={icon} {...iconStyle} />
@@ -76,9 +83,10 @@ export default class ActionButtonFooter extends Component {
{title.toLowerCase()}
</Text>
</TouchableOpacity>
)
})}
</View>
)
}
}
}
+2 -2
View File
@@ -7,7 +7,7 @@ import {
import styles from '../../../styles'
import { saveSymptom } from '../../../db'
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 SelectTabGroup from '../select-tab-group'
import SymptomSection from './symptom-section'
@@ -80,7 +80,7 @@ export default class Mucus extends Component {
saveSymptom('mucus', this.props.date, {
feeling,
texture,
value: computeSensiplanValue(feeling, texture),
value: computeNfpValue(feeling, texture),
exclude: Boolean(this.state.exclude)
})
}}
+3 -11
View File
@@ -2,17 +2,9 @@ import React from 'react'
import {
View,
Text} from 'react-native'
import { LocalDate } from 'js-joda'
import moment from 'moment'
import styles from '../../styles'
import NavigationArrow from './navigation-arrow'
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()
}
import formatDate from '../helpers/format-date'
export default function CycleDayHeader({ date, ...props }) {
return (<View style={[styles.header, styles.headerCycleDay]}>
@@ -23,7 +15,7 @@ export default function CycleDayHeader({ date, ...props }) {
<NavigationArrow direction='left' {...props}/>
<View>
<Text style={styles.dateHeader}>
<FormattedDate date={date} />
{formatDate(date)}
</Text>
{props.cycleDayNumber &&
<Text style={styles.cycleDayNumber}>
@@ -33,4 +25,4 @@ export default function CycleDayHeader({ date, ...props }) {
<NavigationArrow direction='right' {...props}/>
</View>
)
}
}
+5 -1
View File
@@ -5,6 +5,7 @@ import {
import styles, { iconStyles } from '../../styles'
import FeatherIcon from 'react-native-vector-icons/Feather'
import NavigationArrow from './navigation-arrow'
import formatDate from '../helpers/format-date'
export default function SymptomViewHeader(props) {
return (
@@ -21,6 +22,9 @@ export default function SymptomViewHeader(props) {
<Text style={styles.dateHeader}>
{props.title}
</Text>
<Text style={styles.cycleDayNumber}>
{formatDate(props.date)}
</Text>
</View >
<FeatherIcon
name='info'
@@ -29,4 +33,4 @@ export default function SymptomViewHeader(props) {
/>
</View>
)
}
}
+9
View File
@@ -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()
}
-18
View File
@@ -9,7 +9,6 @@ import { getCycleDaysSortedByDate } from '../db'
import { getFertilityStatusForDay } from '../lib/sympto-adapter'
import styles from '../styles'
import AppText, { AppTextLight } from './app-text'
import nothingChanged from '../db/db-unchanged'
import DripHomeIcon from '../assets/drip-home-icons'
const HomeButton = ({ backgroundColor, children }) => {
@@ -42,23 +41,6 @@ export default class Home extends Component {
}
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) {
+13
View File
@@ -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
View File
@@ -38,7 +38,7 @@ export default class Menu extends Component {
{ title: t.Calendar, icon: 'calendar-range', onPress: () => this.goTo('Calendar') },
{ title: t.Chart, icon: 'chart-line', onPress: () => this.goTo('Chart') },
{ 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)}
</View >
)
+21
View File
@@ -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>
)
}
}
-31
View File
@@ -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)
}
}
@@ -1,10 +1,10 @@
import { Alert } from 'react-native'
import { DocumentPicker, DocumentPickerUtil } from 'react-native-document-picker'
import rnfs from 'react-native-fs'
import importCsv from '../../lib/import-export/import-from-csv'
import { shared as sharedLabels } from '../../i18n/en/labels'
import { settings as labels } from '../../i18n/en/settings'
import alertError from './alert-error'
import importCsv from '../../../lib/import-export/import-from-csv'
import { shared as sharedLabels } from '../../../i18n/en/labels'
import labels from '../../../i18n/en/settings'
import alertError from '../alert-error'
export default function openImportDialogAndImport() {
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>
)
}
}
+7 -65
View File
@@ -1,67 +1,9 @@
import React, { Component } from 'react'
import {
View,
TouchableOpacity,
ScrollView,
} 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'
import Reminders from './reminders'
import NfpSettings from './nfp-settings'
import ImportExport from './import-export'
import Password from './password'
import About from './about'
export default class Settings extends Component {
constructor(props) {
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>
)
}
export default {
Reminders, NfpSettings, ImportExport, Password, About
}
+44
View File
@@ -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>
)
}
}
@@ -1,15 +1,15 @@
import React, { Component } from 'react'
import { View } from 'react-native'
import Slider from '@ptomasroos/react-native-multi-slider'
import AppText from '../app-text'
import AppText from '../../app-text'
import {
scaleObservable,
saveTempScale,
} from '../../local-storage'
import { secondaryColor } from '../../styles/index'
import { settings as labels } from '../../i18n/en/settings'
import config from '../../config'
import alertError from './alert-error'
} from '../../../local-storage'
import { secondaryColor } from '../../../styles/index'
import labels from '../../../i18n/en/settings'
import config from '../../../config'
import alertError from '../alert-error'
export default class TempSlider extends Component {
constructor(props) {
@@ -4,13 +4,13 @@ import {
TouchableOpacity,
Switch
} from 'react-native'
import AppText from '../app-text'
import AppText from '../../app-text'
import {
useCervixObservable,
saveUseCervix
} from '../../local-storage'
import styles from '../../styles/index'
import { settings as labels } from '../../i18n/en/settings'
} from '../../../local-storage'
import styles from '../../../styles/index'
import labels from '../../../i18n/en/settings'
export default class UseCervixSetting extends Component {
constructor() {
+7 -100
View File
@@ -1,59 +1,15 @@
import React, { Component } from 'react'
import {
View,
TouchableOpacity,
} from 'react-native'
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 { View } from 'react-native'
import settings from '../../../i18n/en/settings'
import EnterNewPassword from './enter-new-password'
import SettingsButton from './settings-button'
import showBackUpReminder from './show-backup-reminder'
const SettingsButton = ({ children, ...props }) => {
return (
<TouchableOpacity
style={[
styles.settingsButton,
props.disabled ? styles.settingsButtonDisabled : null
]}
{ ...props }
>
<AppText style={styles.settingsButtonText}>
{children}
</AppText>
</TouchableOpacity>
)
}
export default class CreatePassword extends Component {
constructor() {
super()
this.state = {
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
})
isSettingPassword: false
}
}
@@ -66,35 +22,12 @@ export default class CreatePassword extends Component {
showBackUpReminder(this.toggleSettingPassword)
}
comparePasswords = () => {
return this.state.password === this.state.passwordConfirmation
}
handlePasswordInput = (password) => {
this.setState({ password })
}
handleConfirmationInput = (passwordConfirmation) => {
const { password } = this.state
this.setState({
passwordConfirmation,
isPasswordsMatch: passwordConfirmation === password
})
}
render () {
const {
isSettingPassword,
password,
passwordConfirmation,
shouldShowErrorMessage,
isSettingPassword
} = this.state
const labels = settings.passwordSettings
const isSaveButtonDisabled =
!password.length ||
!passwordConfirmation.length
if (!isSettingPassword) {
return (
<View>
@@ -104,33 +37,7 @@ export default class CreatePassword extends Component {
</View>
)
} else {
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>
)
return <EnterNewPassword />
}
}
+1 -1
View File
@@ -6,7 +6,7 @@ import {
import nodejs from 'nodejs-mobile-react-native'
import AppText from '../../app-text'
import styles from '../../../styles'
import { settings as labels } from '../../../i18n/en/settings'
import labels from '../../../i18n/en/settings'
import { requestHash, changeEncryptionAndRestartApp } from '../../../db'
import PasswordField from './password-field'
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>
)
}
}
+18 -16
View File
@@ -1,5 +1,5 @@
import React, { Component } from 'react'
import { View } from 'react-native'
import { View, ScrollView } from 'react-native'
import CreatePassword from './create'
import ChangePassword from './update'
import DeletePassword from './delete'
@@ -8,7 +8,7 @@ import {
hasEncryptionObservable
} from '../../../local-storage'
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 {
constructor(props) {
@@ -21,30 +21,32 @@ export default class PasswordSetting extends Component {
render() {
return (
<View style={styles.settingsSegment}>
<ScrollView>
<View style={styles.settingsSegment}>
<AppText style={styles.settingsSegmentTitle}>
{labels.passwordSettings.title}
</AppText>
<AppText style={styles.settingsSegmentTitle}>
{labels.passwordSettings.title}
</AppText>
{this.state.showUpdateAndDelete ?
<AppText>{labels.passwordSettings.explainerEnabled}</AppText>
:
<AppText>{labels.passwordSettings.explainerDisabled}</AppText>
}
{this.state.showUpdateAndDelete ?
<AppText>{labels.passwordSettings.explainerEnabled}</AppText>
:
<AppText>{labels.passwordSettings.explainerDisabled}</AppText>
}
{this.state.showUpdateAndDelete &&
{this.state.showUpdateAndDelete &&
<View>
<ChangePassword/>
<DeletePassword/>
</View>
}
}
{this.state.showCreate &&
{this.state.showCreate &&
<CreatePassword/>
}
}
</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 { 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) {
let title, message
+57 -77
View File
@@ -1,26 +1,23 @@
import React, { Component } from 'react'
import {
View,
TouchableOpacity} from 'react-native'
import { View } from 'react-native'
import nodejs from 'nodejs-mobile-react-native'
import AppText from '../../app-text'
import styles from '../../../styles'
import { shared } from '../../../i18n/en/labels'
import { settings as labels } from '../../../i18n/en/settings'
import { requestHash, changeEncryptionAndRestartApp } from '../../../db'
import { shared as sharedLabels } from '../../../i18n/en/labels'
import settings from '../../../i18n/en/settings'
import { requestHash } from '../../../db'
import EnterNewPassword from './enter-new-password'
import PasswordField from './password-field'
import SettingsButton from './settings-button'
import showBackUpReminder from './show-backup-reminder'
import checkCurrentPassword from './check-current-password'
export default class ChangePassword extends Component {
constructor() {
super()
this.state = {
enteringCurrentPassword: false,
currentPassword: null,
enteringNewPassword: false,
newPassword: null
enteringCurrentPassword: false,
enteringNewPassword: false
}
nodejs.channel.addListener(
@@ -28,17 +25,10 @@ export default class ChangePassword extends Component {
this.openNewPasswordField,
this
)
nodejs.channel.addListener(
'change-pw',
changeEncryptionAndRestartApp,
this
)
}
componentWillUnmount() {
nodejs.channel.removeListener('pre-change-pw-check', this.openNewPasswordField)
nodejs.channel.removeListener('change-pw', changeEncryptionAndRestartApp)
}
openNewPasswordField = async hash => {
@@ -53,77 +43,67 @@ export default class ChangePassword extends Component {
if (passwordCorrect) {
this.setState({
enteringCurrentPassword: false,
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() {
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>
<PasswordField
onChangeText={val => {
this.setState({
currentPassword: val,
wrongPassword: false
})
}}
value={this.state.currentPassword}
placeholder={labels.passwordSettings.enterCurrent}
/>
<TouchableOpacity
onPress={() => requestHash('pre-change-pw-check', this.state.currentPassword)}
disabled={!this.state.currentPassword}
style={styles.settingsButton}>
<AppText style={styles.settingsButtonText}>
{shared.unlock}
</AppText>
</TouchableOpacity>
</View>
}
const {
enteringCurrentPassword,
enteringNewPassword,
currentPassword
} = this.state
{this.state.enteringNewPassword &&
const labels = settings.passwordSettings
if (enteringCurrentPassword) {
return (
<View>
<PasswordField
style={styles.passwordField}
onChangeText={val => {
this.setState({
newPassword: val
})
}}
value={this.state.changedPassword}
placeholder={labels.passwordSettings.enterNew}
placeholder={labels.enterCurrent}
value={currentPassword}
onChangeText={this.handleCurrentPasswordInput}
/>
<TouchableOpacity
onPress={() => requestHash('change-pw', this.state.newPassword)}
disabled={ !this.state.newPassword }
style={styles.settingsButton}>
<AppText style={styles.settingsButtonText}>
{labels.passwordSettings.changePassword}
</AppText>
</TouchableOpacity>
<SettingsButton
onPress={this.checkCurrentPassword}
disabled={!currentPassword}
>
{sharedLabels.unlock}
</SettingsButton>
</View>
}
)
}
if (enteringNewPassword) {
return <EnterNewPassword />
}
return (
<View>
<SettingsButton
onPress={this.startChangingPassword}
disabled={currentPassword}
>
{labels.changePassword}
</SettingsButton>
</View>
)
}
+22
View File
@@ -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>
)
}
}
@@ -3,13 +3,13 @@ import {
View,
Switch
} from 'react-native'
import AppText from '../app-text'
import AppText from '../../app-text'
import {
periodReminderObservable,
savePeriodReminder
} from '../../local-storage'
import styles from '../../styles/index'
import { settings as labels } from '../../i18n/en/settings'
} from '../../../local-storage'
import styles from '../../../styles/index'
import labels from '../../../i18n/en/settings'
export default class PeriodReminderPicker extends Component {
constructor(props) {
@@ -5,14 +5,14 @@ import {
Switch
} from 'react-native'
import DateTimePicker from 'react-native-modal-datetime-picker-nevo'
import AppText from '../app-text'
import AppText from '../../app-text'
import {
tempReminderObservable,
saveTempReminder
} from '../../local-storage'
import styles from '../../styles/index'
import { settings as labels } from '../../i18n/en/settings'
import padWithZeros from '../helpers/pad-time-with-zeros'
} from '../../../local-storage'
import styles from '../../../styles/index'
import labels from '../../../i18n/en/settings'
import padWithZeros from '../../helpers/pad-time-with-zeros'
export default class TempReminderPicker extends Component {
constructor(props) {
+40
View File
@@ -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>
)
}
}
+2 -1
View File
@@ -92,5 +92,6 @@ export const sharedDialogs = {
areYouSureToUnset: 'Are you sure you want to unset all entered data?',
reallyUnsetData: 'Yes, I am sure',
save: 'Save',
unset: 'Unset'
unset: 'Unset',
disabledInfo: 'There is some data missing'
}
+9 -1
View File
@@ -1,3 +1,6 @@
import labels from './settings'
const settingsTitles = labels.menuTitles
export const shared = {
cancel: 'Cancel',
save: 'Save',
@@ -21,7 +24,12 @@ export const headerTitles = {
Calendar: 'Calendar',
Chart: 'Chart',
Stats: 'Statistics',
Settings: 'Settings',
SettingsMenu: 'Settings',
Reminders: settingsTitles.reminders,
NfpSettings: settingsTitles.nfpSettings,
ImportExport: settingsTitles.importExport,
Password: settingsTitles.password,
About: settingsTitles.about,
BleedingEditView: 'Bleeding',
TemperatureEditView: 'Temperature',
MucusEditView: 'Mucus',
+23 -1
View File
@@ -1,4 +1,12 @@
export const settings = {
export default {
menuTitles: {
reminders: 'Reminders',
importExport: 'Import and Export',
nfpSettings: 'NFP settings',
password: 'Password',
about: 'About'
},
export: {
errors: {
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.',
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.',
},
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
View File
@@ -39,7 +39,8 @@ export default function config(opts) {
const targetDate = LocalDate.parse(targetDateString)
const lastMensesLocalDate = LocalDate.parse(lastMensesStart.date)
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
return diffInDays + 1
}
+1 -13
View File
@@ -1,19 +1,8 @@
import objectPath from 'object-path'
import { Base64 } from 'js-base64'
import { getCycleDaysSortedByDate } from '../../db'
import getColumnNamesForCsv from './get-csv-column-names'
export default function makeDataURI() {
const cycleDaysSortedByDate = getCycleDaysSortedByDate()
if (!cycleDaysSortedByDate.length) return null
const csv = transformToCsv(cycleDaysSortedByDate)
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) {
export default function transformToCsv(cycleDays) {
const columnNames = getColumnNamesForCsv()
const rows = cycleDays
.map(day => {
@@ -23,7 +12,6 @@ function transformToCsv(cycleDays) {
})
})
.map(row => row.join(','))
rows.unshift(columnNames.join(','))
return rows.join('\n')
}
+23 -11
View File
@@ -1,16 +1,17 @@
import getFertilityStatus from './sympto'
import cycleModule from './cycle'
import { fertilityStatus } from '../i18n/en/labels'
import { useCervixObservable } from '../local-storage'
import { fertilityStatus as labels } from '../i18n/en/labels'
export function getFertilityStatusForDay(dateString) {
const status = getCycleStatusForDay(dateString)
if (!status) return {
status: fertilityStatus.fertile,
status: labels.fertile,
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 dayIsAfterPhaseStart = dateString >= phase.start.date
let dayIsBeforePhaseEnd
@@ -22,6 +23,12 @@ export function getFertilityStatusForDay(dateString) {
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)
}
@@ -58,27 +65,32 @@ function formatStatus(phaseNameForDay, dateString, status) {
const mapping = {
preOvulatory: () => {
return {
status: fertilityStatus.infertile,
status: labels.infertile,
phase: 1,
statusText: fertilityStatus.preOvuText
statusText: labels.preOvuText
}
},
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) {
return fertilityStatus.fertileUntilEvening
s = labels.fertileUntilEvening
} else {
s = labels.fertile
}
return {
status: fertilityStatus.fertile,
status: s,
phase: 2,
statusText: fertilityStatus.periOvuText
statusText: labels.periOvuText
}
},
postOvulatory: (dateString, status) => {
return {
status: fertilityStatus.infertile,
status: labels.infertile,
phase: 3,
statusText: fertilityStatus.postOvuText(status.temperatureShift.rule)
statusText: labels.postOvuText(status.temperatureShift.rule)
}
}
}
+18 -2
View File
@@ -51,7 +51,7 @@ function checkIfFirstHighMeasurement(temp, i, temperatureDays, ltl) {
function getResultForRegularRule(nextDaysAfterPotentialFhm, ltl) {
if (!nextDaysAfterPotentialFhm.every(day => day.temp > ltl)) return false
const thirdDay = nextDaysAfterPotentialFhm[1]
if (rounded(thirdDay.temp - ltl, 0.1) < 0.2) return false
if (isLessThan0Point2(thirdDay.temp - ltl)) return false
return {
detected: true,
rule: 0,
@@ -77,7 +77,7 @@ function getResultForSecondExceptionRule(nextDaysAfterPotentialFhm, ltl) {
if (nextDaysAfterPotentialFhm.length < 3) return false
if (secondOrThirdTempIsAtOrBelowLtl(nextDaysAfterPotentialFhm, ltl)) {
const fourthDay = nextDaysAfterPotentialFhm[2]
if (rounded(fourthDay.temp - ltl, 0.1) >= 0.2) {
if (isBiggerOrEqual0Point2(fourthDay.temp - ltl)) {
return {
detected: true,
rule: 2,
@@ -104,3 +104,19 @@ function rounded(val, step) {
// we round the difference because of JS decimal weirdness
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)
}
+234 -214
View File
@@ -771,12 +771,26 @@
}
},
"ajv": {
"version": "4.11.8",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz",
"integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=",
"version": "6.6.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.1.tgz",
"integrity": "sha512-ZoJjft5B+EJBjUyu9C9Hc0OZyPZSSlOF+plzouTrg6UlA8f+e/n8NIgBFG/9tppJtpPWfthHakK7juJdNDODww==",
"requires": {
"co": "^4.6.0",
"json-stable-stringify": "^1.0.1"
"fast-deep-equal": "^2.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": {
@@ -1248,9 +1262,9 @@
}
},
"assert-plus": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz",
"integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ="
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
},
"assertion-error": {
"version": "1.1.0",
@@ -1287,9 +1301,9 @@
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
},
"aws-sign2": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz",
"integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8="
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
},
"aws4": {
"version": "1.7.0",
@@ -2218,18 +2232,11 @@
"integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw=="
},
"basic-auth": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.0.tgz",
"integrity": "sha1-AV2z81PgLlY3d1X5YnQuiYHnu7o=",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
"requires": {
"safe-buffer": "5.1.1"
},
"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=="
}
"safe-buffer": "5.1.2"
}
},
"bcrypt-pbkdf": {
@@ -2255,27 +2262,11 @@
"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": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
"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": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.0.7.tgz",
@@ -2721,14 +2712,6 @@
"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": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/csvtojson/-/csvtojson-2.0.8.tgz",
@@ -3640,12 +3623,12 @@
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
},
"form-data": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz",
"integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=",
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.5",
"combined-stream": "^1.0.6",
"mime-types": "^2.1.12"
}
},
@@ -3677,6 +3660,14 @@
"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": {
"version": "1.0.0",
"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": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
@@ -4313,17 +4283,17 @@
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE="
},
"har-schema": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz",
"integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4="
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
},
"har-validator": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz",
"integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=",
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
"integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
"requires": {
"ajv": "^4.9.1",
"har-schema": "^1.0.5"
"ajv": "^6.5.5",
"har-schema": "^2.0.0"
}
},
"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": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
"integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
"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": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
@@ -4482,11 +4436,11 @@
"integrity": "sha1-p8TnWq6C87tJBOT0P2FWc7TVGMM="
},
"http-signature": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz",
"integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
"requires": {
"assert-plus": "^0.2.0",
"assert-plus": "^1.0.0",
"jsprim": "^1.2.2",
"sshpk": "^1.7.0"
}
@@ -4510,6 +4464,14 @@
"integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==",
"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": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.6.3.tgz",
@@ -5184,9 +5146,9 @@
}
},
"merge": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/merge/-/merge-1.2.0.tgz",
"integrity": "sha1-dTHjnUlJwoGma4xabgJl6LBYlNo="
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz",
"integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ=="
},
"merge-stream": {
"version": "1.0.1",
@@ -5629,13 +5591,13 @@
"integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y="
},
"morgan": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz",
"integrity": "sha1-0B+mxlhZt2/PMbPLU6OCGjEdgFE=",
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz",
"integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==",
"requires": {
"basic-auth": "~2.0.0",
"debug": "2.6.9",
"depd": "~1.1.1",
"depd": "~1.1.2",
"on-finished": "~2.3.0",
"on-headers": "~1.0.1"
}
@@ -5653,8 +5615,7 @@
"nan": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.11.0.tgz",
"integrity": "sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw==",
"optional": true
"integrity": "sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw=="
},
"nanomatch": {
"version": "1.2.13",
@@ -5719,6 +5680,23 @@
"resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz",
"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": {
"version": "0.6.1",
"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",
"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": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz",
@@ -5755,21 +5738,20 @@
}
},
"node-pre-gyp": {
"version": "0.6.39",
"resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz",
"integrity": "sha512-OsJV74qxnvz/AMGgcfZoDaeDXKD3oY3QVIbBmwszTFkRisTSXbMQyn4UWzUMOtA5SVhrBZOTp0wcoSBgfMfMmQ==",
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz",
"integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==",
"requires": {
"detect-libc": "^1.0.2",
"hawk": "3.1.3",
"mkdirp": "^0.5.1",
"needle": "^2.2.1",
"nopt": "^4.0.1",
"npm-packlist": "^1.1.6",
"npmlog": "^4.0.2",
"rc": "^1.1.7",
"request": "2.81.0",
"rc": "^1.2.7",
"rimraf": "^2.6.1",
"semver": "^5.3.0",
"tar": "^2.2.1",
"tar-pack": "^3.4.0"
"tar": "^4"
},
"dependencies": {
"gauge": {
@@ -5806,35 +5788,6 @@
"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": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
@@ -5937,6 +5890,20 @@
"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": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
@@ -6258,9 +6225,9 @@
"integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
},
"performance-now": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz",
"integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU="
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
},
"pify": {
"version": "3.0.0",
@@ -6387,20 +6354,25 @@
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
"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": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
},
"qs": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz",
"integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM="
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
},
"querystringify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.0.0.tgz",
"integrity": "sha512-eTPo5t/4bgaMNZxyjWx6N2a6AuE0mq51KWvpc7nU/MAqixcI6v6KrGUKES0HaomdnolQBBXU/++X6/QQ9KL4tw=="
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.0.tgz",
"integrity": "sha512-sluvZZ1YiTLD5jsqZcDmFyV2EwToyXZBfpoVOmktMmW+VEnhgakFHnasVph65fOjGPTWN0Nw3+XQaSeMayr0kg=="
},
"randomatic": {
"version": "3.1.0",
@@ -6746,26 +6718,32 @@
}
},
"realm": {
"version": "2.14.0",
"resolved": "https://registry.npmjs.org/realm/-/realm-2.14.0.tgz",
"integrity": "sha512-HiCj/ZE3iZNOyWexVkhDetNEfcTtSdgsg5lop7g7rcCsE+4NM3hDRxvBjrBSk/mgs6HXBAhHohzGmREbs6piRg==",
"version": "2.21.0",
"resolved": "https://registry.npmjs.org/realm/-/realm-2.21.0.tgz",
"integrity": "sha512-2XxkVogKOObhwBUcP7NPvyA9kU/HIeopVbAGgKanJWYw5z09J+I+q1CN2gCVR5EC4+H55Ht4loEhjDud5+LEYQ==",
"requires": {
"command-line-args": "^4.0.6",
"decompress": "^4.2.0",
"deepmerge": "2.1.0",
"fs-extra": "^4.0.2",
"ini": "^1.3.4",
"nan": "2.8.0",
"nan": "^2.10.0",
"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",
"prop-types": "^15.5.10",
"request": "^2.78.0",
"request": "^2.88.0",
"stream-counter": "^1.0.0",
"sync-request": "^3.0.1",
"url-parse": "^1.2.0"
},
"dependencies": {
"aws4": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
"integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
},
"fs-extra": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz",
@@ -6784,10 +6762,46 @@
"graceful-fs": "^4.1.6"
}
},
"nan": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz",
"integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo="
"oauth-sign": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
"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": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
@@ -7891,11 +7897,6 @@
"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": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
@@ -8000,41 +8001,50 @@
}
},
"tar": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz",
"integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=",
"version": "4.4.8",
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz",
"integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==",
"requires": {
"block-stream": "*",
"fstream": "^1.0.2",
"inherits": "2"
}
},
"tar-pack": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.1.tgz",
"integrity": "sha512-PPRybI9+jM5tjtCbN2cxmmRU7YmqT3Zv/UDy48tAh2XRkLa9bAORtSWLkVc13+GJF+cdTh1yEnHEk3cpTaL5Kg==",
"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"
"chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
"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"
}
},
"yallist": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A=="
}
}
},
"tar-stream": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.1.tgz",
"integrity": "sha512-IFLM5wp3QrJODQFPm6/to3LJZrONdBY/otxcvDIQzu217zKye6yVR3hhi9lAjrC2Z+m/j5oDxMPb1qcd8cIvpA==",
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz",
"integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==",
"requires": {
"bl": "^1.0.0",
"buffer-alloc": "^1.1.0",
"buffer-alloc": "^1.2.0",
"end-of-stream": "^1.0.0",
"fs-constants": "^1.0.0",
"readable-stream": "^2.3.0",
"to-buffer": "^1.1.0",
"to-buffer": "^1.1.1",
"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": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",
"integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po="
},
"unbzip2-stream": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.2.5.tgz",
"integrity": "sha512-izD3jxT8xkzwtXRUZjtmRwKnZoeECrfZ8ra/ketwOcusbZEp4mjULMnJOCfTDZBgGQAAY1AJ/IgxcwkavcX9Og==",
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.1.tgz",
"integrity": "sha512-fIZnvdjblYs7Cru/xC6tCPVhz7JkYcVQQkePwMLyQELzYTds2Xn8QefPVnvdVhhZqubxNA1cASXEH5wcK0Bucw==",
"requires": {
"buffer": "^3.0.1",
"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": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
"integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI="
},
"url-parse": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.3.tgz",
"integrity": "sha512-rh+KuAW36YKo0vClhQzLLveoj8FwPJNu65xLb7Mrt+eZht0IPT0IXgSv8gcMegZ6NvjJUALf6Mf25POlMwD1Fw==",
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.4.tgz",
"integrity": "sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg==",
"requires": {
"querystringify": "^2.0.0",
"requires-port": "^1.0.0"
+4 -2
View File
@@ -12,7 +12,8 @@
"log": "react-native log-android",
"test": "mocha --recursive --require @babel/register test && npm run lint",
"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": {
"@ptomasroos/react-native-multi-slider": "^1.0.0",
@@ -26,6 +27,7 @@
"nodejs-mobile-react-native": "^0.3.0",
"object-path": "^0.11.4",
"obv": "0.0.1",
"prop-types": "^15.6.2",
"react": "16.4.1",
"react-native": "~0.56.0",
"react-native-calendars": "^1.19.3",
@@ -36,7 +38,7 @@
"react-native-restart": "0.0.7",
"react-native-share": "^1.1.3",
"react-native-vector-icons": "^5.0.0",
"realm": "^2.7.1"
"realm": "^2.21.0"
},
"devDependencies": {
"@babel/register": "^7.0.0-beta.55",
+11
View File
@@ -43,6 +43,10 @@ export default StyleSheet.create({
fontWeight: 'bold',
fontFamily: textFont
},
link: {
color: cycleDayColor,
textDecorationLine: 'underline'
},
title: {
fontSize: 18,
color: 'black',
@@ -263,6 +267,9 @@ export default StyleSheet.create({
padding: 7,
fontFamily: 'textFont'
},
settingsSegmentLast: {
marginBottom: defaultTopMargin,
},
settingsSegmentTitle: {
fontWeight: 'bold',
fontFamily: 'textFont'
@@ -414,4 +421,8 @@ export const iconStyles = {
menuIconInactive: {
color: colorInActive,
},
infoInHeading: {
marginRight: 5,
color: 'black'
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ import dirtyChai from 'dirty-chai'
const expect = chai.expect
chai.use(dirtyChai)
import getSensiplanMucus from '../lib/sensiplan-mucus'
import getSensiplanMucus from '../lib/nfp-mucus'
describe('getSensiplanMucus', () => {