diff --git a/.buckconfig b/.buckconfig deleted file mode 100644 index 934256c..0000000 --- a/.buckconfig +++ /dev/null @@ -1,6 +0,0 @@ - -[android] - target = Google Inc.:Google APIs:23 - -[maven_repositories] - central = https://repo1.maven.org/maven2 diff --git a/.flowconfig b/.flowconfig deleted file mode 100644 index 7d5e2d3..0000000 --- a/.flowconfig +++ /dev/null @@ -1,54 +0,0 @@ -[ignore] -; We fork some components by platform -.*/*[.]android.js - -; Ignore "BUCK" generated dirs -/\.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\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) -suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ -suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy -suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError - -[version] -^0.67.0 diff --git a/README.md b/README.md index 33c2411..a7b4754 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 207d90a..5c5243a 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,8 @@ - + @@ -36,9 +39,11 @@ android:grantUriPermissions="true" android:exported="false"> + + + \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index 3d21e10..12fbc47 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -35,3 +35,11 @@ ext { targetSdkVersion = 26 supportLibVersion = "26.1.0" } + +subprojects {project -> + buildscript { + repositories { + maven { url = 'https://dl.bintray.com/android/android-tools/' } + } + } +} diff --git a/components/app-text.js b/components/app-text.js index eabab32..6e996da 100644 --- a/components/app-text.js +++ b/components/app-text.js @@ -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 ( - - {this.props.children} - - ) - } +export default function AppText(props) { + return ( + + {props.children} + + ) } -export class AppTextLight extends Component { - render() { - return ( - - {this.props.children} - - ) - } +export function AppTextLight(props) { + return ( + + {props.children} + + ) } -export class SymptomSectionHeader extends Component { - render() { - return ( - - {this.props.children} - - ) - } +export function SymptomSectionHeader(props) { + return ( + + {props.children} + + ) } \ No newline at end of file diff --git a/components/app.js b/components/app.js index b3218a5..39318f3 100644 --- a/components/app.js +++ b/components/app.js @@ -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 ( @@ -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} />} diff --git a/components/chart/chart.js b/components/chart/chart.js index c11db6b..b8d2142 100644 --- a/components/chart/chart.js +++ b/components/chart/chart.js @@ -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 diff --git a/components/chart/day-column.js b/components/chart/day-column.js index fe4b82c..e9c6f76 100644 --- a/components/chart/day-column.js +++ b/components/chart/day-column.js @@ -52,7 +52,7 @@ export default class DayColumn extends Component { this.fhmAndLtl = props.getFhmAndLtlInfo( props.dateString, - props.temp, + this.data.temperature, props.columnHeight ) } diff --git a/components/cycle-day/symptoms/action-button-footer.js b/components/cycle-day/symptoms/action-button-footer.js index 076b0bb..9da25e2 100644 --- a/components/cycle-day/symptoms/action-button-footer.js +++ b/components/cycle-day/symptoms/action-button-footer.js @@ -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 ( {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 { @@ -76,9 +83,10 @@ export default class ActionButtonFooter extends Component { {title.toLowerCase()} + ) })} ) } -} \ No newline at end of file +} diff --git a/components/cycle-day/symptoms/mucus.js b/components/cycle-day/symptoms/mucus.js index f2c268d..7de9896 100644 --- a/components/cycle-day/symptoms/mucus.js +++ b/components/cycle-day/symptoms/mucus.js @@ -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) }) }} diff --git a/components/header/cycle-day.js b/components/header/cycle-day.js index b5dbbdf..0cb3252 100644 --- a/components/header/cycle-day.js +++ b/components/header/cycle-day.js @@ -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 ( @@ -23,7 +15,7 @@ export default function CycleDayHeader({ date, ...props }) { - + {formatDate(date)} {props.cycleDayNumber && @@ -33,4 +25,4 @@ export default function CycleDayHeader({ date, ...props }) { ) -} \ No newline at end of file +} diff --git a/components/header/symptom-view.js b/components/header/symptom-view.js index 3f6a21f..2081776 100644 --- a/components/header/symptom-view.js +++ b/components/header/symptom-view.js @@ -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) { {props.title} + + {formatDate(props.date)} + ) -} \ No newline at end of file +} diff --git a/components/helpers/format-date.js b/components/helpers/format-date.js new file mode 100644 index 0000000..079ea4d --- /dev/null +++ b/components/helpers/format-date.js @@ -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() +} diff --git a/components/home.js b/components/home.js index ad4dce1..e57633d 100644 --- a/components/home.js +++ b/components/home.js @@ -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) { diff --git a/components/link.js b/components/link.js new file mode 100644 index 0000000..f5d2c10 --- /dev/null +++ b/components/link.js @@ -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 ( + Linking.openURL(props.href)} + >{props.text} + ) +} \ No newline at end of file diff --git a/components/menu.js b/components/menu.js index 4e80cbb..7a692e0 100644 --- a/components/menu.js +++ b/components/menu.js @@ -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)} ) diff --git a/components/settings/about.js b/components/settings/about.js new file mode 100644 index 0000000..5d7cba1 --- /dev/null +++ b/components/settings/about.js @@ -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 ( + + + {`${labels.aboutSection.title} `} + {`${labels.aboutSection.segmentExplainer} `} + + + {`${labels.credits.title} `} + {`${labels.credits.note}`} + + + ) + } +} diff --git a/components/settings/export-dialog.js b/components/settings/export-dialog.js deleted file mode 100644 index e87b9d2..0000000 --- a/components/settings/export-dialog.js +++ /dev/null @@ -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) - } -} - diff --git a/components/settings/import-export/export-dialog.js b/components/settings/import-export/export-dialog.js new file mode 100644 index 0000000..58301cf --- /dev/null +++ b/components/settings/import-export/export-dialog.js @@ -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) + } +} + diff --git a/components/settings/import-dialog.js b/components/settings/import-export/import-dialog.js similarity index 86% rename from components/settings/import-dialog.js rename to components/settings/import-export/import-dialog.js index 5701b08..2ff977e 100644 --- a/components/settings/import-dialog.js +++ b/components/settings/import-export/import-dialog.js @@ -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( diff --git a/components/settings/import-export/index.js b/components/settings/import-export/index.js new file mode 100644 index 0000000..430f29b --- /dev/null +++ b/components/settings/import-export/index.js @@ -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 ( + + + + {labels.export.button} + + {labels.export.segmentExplainer} + + + {labels.export.button} + + + + + + {labels.import.button} + + {labels.import.segmentExplainer} + + + {labels.import.button} + + + + + ) + } +} diff --git a/components/settings/index.js b/components/settings/index.js index b91acbc..a1eaa03 100644 --- a/components/settings/index.js +++ b/components/settings/index.js @@ -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 ( - - - - - - {labels.tempScale.segmentTitle} - - {labels.tempScale.segmentExplainer} - - - - - - - {labels.export.button} - - {labels.export.segmentExplainer} - - - {labels.export.button} - - - - - - {labels.import.button} - - {labels.import.segmentExplainer} - - - {labels.import.button} - - - - - ) - } +export default { + Reminders, NfpSettings, ImportExport, Password, About } diff --git a/components/settings/nfp-settings/index.js b/components/settings/nfp-settings/index.js new file mode 100644 index 0000000..e975dc7 --- /dev/null +++ b/components/settings/nfp-settings/index.js @@ -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 ( + + + + + {labels.tempScale.segmentTitle} + + {labels.tempScale.segmentExplainer} + + + + + + {`${labels.preOvu.title} `} + + + {labels.preOvu.note1} + + {labels.preOvu.note2} + + + + ) + } +} diff --git a/components/settings/temp-slider.js b/components/settings/nfp-settings/temp-slider.js similarity index 86% rename from components/settings/temp-slider.js rename to components/settings/nfp-settings/temp-slider.js index 7052299..9dcdfb9 100644 --- a/components/settings/temp-slider.js +++ b/components/settings/nfp-settings/temp-slider.js @@ -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) { diff --git a/components/settings/use-cervix.js b/components/settings/nfp-settings/use-cervix.js similarity index 87% rename from components/settings/use-cervix.js rename to components/settings/nfp-settings/use-cervix.js index 668a0e8..ecfe5d3 100644 --- a/components/settings/use-cervix.js +++ b/components/settings/nfp-settings/use-cervix.js @@ -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() { diff --git a/components/settings/password/create.js b/components/settings/password/create.js index 68b7b2d..bda688f 100644 --- a/components/settings/password/create.js +++ b/components/settings/password/create.js @@ -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 ( - - - {children} - - - ) -} - 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 ( @@ -104,33 +37,7 @@ export default class CreatePassword extends Component { ) } else { - return ( - - - - { - shouldShowErrorMessage && - - {labels.passwordsDontMatch} - - } - - {labels.savePassword} - - - ) + return } } diff --git a/components/settings/password/delete.js b/components/settings/password/delete.js index d75f2de..6cef844 100644 --- a/components/settings/password/delete.js +++ b/components/settings/password/delete.js @@ -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' diff --git a/components/settings/password/enter-new-password.js b/components/settings/password/enter-new-password.js new file mode 100644 index 0000000..ed09ead --- /dev/null +++ b/components/settings/password/enter-new-password.js @@ -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 ( + + + + { + shouldShowErrorMessage && + + {labels.passwordsDontMatch} + + } + + {labels.savePassword} + + + ) + } +} \ No newline at end of file diff --git a/components/settings/password/index.js b/components/settings/password/index.js index 47e4848..f5741e3 100644 --- a/components/settings/password/index.js +++ b/components/settings/password/index.js @@ -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 ( - + + - - {labels.passwordSettings.title} - + + {labels.passwordSettings.title} + - {this.state.showUpdateAndDelete ? - {labels.passwordSettings.explainerEnabled} - : - {labels.passwordSettings.explainerDisabled} - } + {this.state.showUpdateAndDelete ? + {labels.passwordSettings.explainerEnabled} + : + {labels.passwordSettings.explainerDisabled} + } - {this.state.showUpdateAndDelete && + {this.state.showUpdateAndDelete && - } + } - {this.state.showCreate && + {this.state.showCreate && - } + } - + + ) } } \ No newline at end of file diff --git a/components/settings/password/settings-button.js b/components/settings/password/settings-button.js new file mode 100644 index 0000000..97272bb --- /dev/null +++ b/components/settings/password/settings-button.js @@ -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 ( + + + {children} + + + ) +} + +SettingsButton.propTypes = { + onPress: PropTypes.func.isRequired, + disabled: PropTypes.bool +} + +export default SettingsButton \ No newline at end of file diff --git a/components/settings/password/show-backup-reminder.js b/components/settings/password/show-backup-reminder.js index 708b81c..f61e5a4 100644 --- a/components/settings/password/show-backup-reminder.js +++ b/components/settings/password/show-backup-reminder.js @@ -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 diff --git a/components/settings/password/update.js b/components/settings/password/update.js index fc10cca..a246c0e 100644 --- a/components/settings/password/update.js +++ b/components/settings/password/update.js @@ -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 ( - - {!this.state.enteringCurrentPassword && - !this.state.enteringNewPassword && - showBackUpReminder(() => { - this.setState({ enteringCurrentPassword: true }) - })} - disabled={this.state.currentPassword} - style={styles.settingsButton}> - - {labels.passwordSettings.changePassword} - - - } - {this.state.enteringCurrentPassword && - - { - this.setState({ - currentPassword: val, - wrongPassword: false - }) - }} - value={this.state.currentPassword} - placeholder={labels.passwordSettings.enterCurrent} - /> - requestHash('pre-change-pw-check', this.state.currentPassword)} - disabled={!this.state.currentPassword} - style={styles.settingsButton}> - - {shared.unlock} - - - - } + const { + enteringCurrentPassword, + enteringNewPassword, + currentPassword + } = this.state - {this.state.enteringNewPassword && + const labels = settings.passwordSettings + + if (enteringCurrentPassword) { + return ( { - this.setState({ - newPassword: val - }) - }} - value={this.state.changedPassword} - placeholder={labels.passwordSettings.enterNew} + placeholder={labels.enterCurrent} + value={currentPassword} + onChangeText={this.handleCurrentPasswordInput} /> - - requestHash('change-pw', this.state.newPassword)} - disabled={ !this.state.newPassword } - style={styles.settingsButton}> - - {labels.passwordSettings.changePassword} - - + + {sharedLabels.unlock} + - } + ) + } + if (enteringNewPassword) { + return + } + + return ( + + + {labels.changePassword} + ) } diff --git a/components/settings/reminders/index.js b/components/settings/reminders/index.js new file mode 100644 index 0000000..ca234d1 --- /dev/null +++ b/components/settings/reminders/index.js @@ -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 ( + + + + + ) + } +} diff --git a/components/settings/period-reminder.js b/components/settings/reminders/period-reminder.js similarity index 85% rename from components/settings/period-reminder.js rename to components/settings/reminders/period-reminder.js index 1bc41cb..3c07c10 100644 --- a/components/settings/period-reminder.js +++ b/components/settings/reminders/period-reminder.js @@ -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) { diff --git a/components/settings/temp-reminder-picker.js b/components/settings/reminders/temp-reminder-picker.js similarity index 90% rename from components/settings/temp-reminder-picker.js rename to components/settings/reminders/temp-reminder-picker.js index 48a7611..09dda42 100644 --- a/components/settings/temp-reminder-picker.js +++ b/components/settings/reminders/temp-reminder-picker.js @@ -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) { diff --git a/components/settings/settings-menu.js b/components/settings/settings-menu.js new file mode 100644 index 0000000..4152c11 --- /dev/null +++ b/components/settings/settings-menu.js @@ -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 ( + + { menu.map(menuItem)} + + ) + + function menuItem(item) { + return ( + props.navigate(item.component)} + > + {item.title} + + ) + } +} \ No newline at end of file diff --git a/i18n/en/cycle-day.js b/i18n/en/cycle-day.js index ff7ed56..78cd4e2 100644 --- a/i18n/en/cycle-day.js +++ b/i18n/en/cycle-day.js @@ -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' } diff --git a/i18n/en/labels.js b/i18n/en/labels.js index 059e2c0..743f4c1 100644 --- a/i18n/en/labels.js +++ b/i18n/en/labels.js @@ -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', diff --git a/i18n/en/settings.js b/i18n/en/settings.js index eef6615..9d46eb4 100644 --- a/i18n/en/settings.js +++ b/i18n/en/settings.js @@ -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' } } \ No newline at end of file diff --git a/lib/cycle.js b/lib/cycle.js index 0f47690..b675f52 100644 --- a/lib/cycle.js +++ b/lib/cycle.js @@ -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 } diff --git a/lib/import-export/export-to-csv.js b/lib/import-export/export-to-csv.js index 619c5e2..cb73126 100644 --- a/lib/import-export/export-to-csv.js +++ b/lib/import-export/export-to-csv.js @@ -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') } diff --git a/lib/sensiplan-mucus.js b/lib/nfp-mucus.js similarity index 100% rename from lib/sensiplan-mucus.js rename to lib/nfp-mucus.js diff --git a/lib/sympto-adapter.js b/lib/sympto-adapter.js index 628ea2b..980cbfe 100644 --- a/lib/sympto-adapter.js +++ b/lib/sympto-adapter.js @@ -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) } } } diff --git a/lib/sympto/temperature.js b/lib/sympto/temperature.js index d738ba3..1e8c87e 100644 --- a/lib/sympto/temperature.js +++ b/lib/sympto/temperature.js @@ -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) +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 0964c8a..9ae02fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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" diff --git a/package.json b/package.json index e01b525..f50ced9 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/styles/index.js b/styles/index.js index 36393df..118e1ce 100644 --- a/styles/index.js +++ b/styles/index.js @@ -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' + } } diff --git a/test/sensiplan-mucus.spec.js b/test/sensiplan-mucus.spec.js index df1c461..accbfe3 100644 --- a/test/sensiplan-mucus.spec.js +++ b/test/sensiplan-mucus.spec.js @@ -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', () => {