diff --git a/components/settings.js b/components/settings.js
deleted file mode 100644
index 125cd27..0000000
--- a/components/settings.js
+++ /dev/null
@@ -1,283 +0,0 @@
-import React, { Component } from 'react'
-import {
- View,
- TouchableOpacity,
- ScrollView,
- Alert,
- Switch
-} from 'react-native'
-import DateTimePicker from 'react-native-modal-datetime-picker-nevo'
-import Slider from '@ptomasroos/react-native-multi-slider'
-import Share from 'react-native-share'
-import { DocumentPicker, DocumentPickerUtil } from 'react-native-document-picker'
-import rnfs from 'react-native-fs'
-import styles, { secondaryColor } from '../styles/index'
-import config from '../config'
-import { settings as labels, shared as sharedLabels } from './labels'
-import getDataAsCsvDataUri from '../lib/import-export/export-to-csv'
-import importCsv from '../lib/import-export/import-from-csv'
-import {
- scaleObservable,
- saveTempScale,
- tempReminderObservable,
- saveTempReminder
-} from '../local-storage'
-import { AppText } from './app-text'
-
-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}
-
-
-
-
- )
- }
-}
-
-class TempReminderPicker extends Component {
- constructor(props) {
- super(props)
- this.state = Object.assign({}, tempReminderObservable.value)
- }
-
- render() {
- return (
- this.setState({ isTimePickerVisible: true })}
- >
-
- {labels.tempReminder.title}
-
-
-
- {this.state.time && this.state.enabled ?
- {labels.tempReminder.timeSet(this.state.time)}
- :
- {labels.tempReminder.noTimeSet}
- }
-
- {
- this.setState({ enabled: switchOn })
- if (switchOn && !this.state.time) {
- this.setState({ isTimePickerVisible: true })
- }
- if (!switchOn) saveTempReminder({ enabled: false })
- }}
- />
- {
- const time = padWithZeros(`${jsDate.getHours()}:${jsDate.getMinutes()}`)
- this.setState({
- time,
- isTimePickerVisible: false,
- enabled: true
- })
- saveTempReminder({
- time,
- enabled: true
- })
- }}
- onCancel={() => {
- this.setState({ isTimePickerVisible: false })
- if (!this.state.time) this.setState({enabled: false})
- }}
- />
-
-
- )
- }
-}
-
-class TempSlider extends Component {
- constructor(props) {
- super(props)
- this.state = Object.assign({}, scaleObservable.value)
- }
-
- onValuesChange = (values) => {
- this.setState({
- min: values[0],
- max: values[1]
- })
- }
-
- onValuesChangeFinish = (values) => {
- this.setState({
- min: values[0],
- max: values[1]
- })
- try {
- saveTempScale(this.state)
- } catch(err) {
- alertError(labels.tempScale.saveError)
- }
- }
-
- render() {
- return (
-
- {`${labels.tempScale.min} ${this.state.min}`}
- {`${labels.tempScale.max} ${this.state.max}`}
-
-
- )
- }
-}
-
-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)
- }
-}
-
-function openImportDialogAndImport() {
- Alert.alert(
- labels.import.title,
- labels.import.message,
- [{
- text: labels.import.replaceOption,
- onPress: () => getFileContentAndImport({ deleteExisting: false })
- }, {
- text: labels.import.deleteOption,
- onPress: () => getFileContentAndImport({ deleteExisting: true })
- }, {
- text: sharedLabels.cancel, style: 'cancel', onPress: () => { }
- }]
- )
-}
-
-async function getFileContentAndImport({ deleteExisting }) {
- let fileInfo
- try {
- fileInfo = await new Promise((resolve, reject) => {
- DocumentPicker.show({
- filetype: [DocumentPickerUtil.allFiles()],
- }, (err, res) => {
- if (err) return reject(err)
- resolve(res)
- })
- })
- } catch (err) {
- // because cancelling also triggers an error, we do nothing here
- return
- }
-
- let fileContent
- try {
- fileContent = await rnfs.readFile(fileInfo.uri, 'utf8')
- } catch (err) {
- return importError(labels.import.errors.couldNotOpenFile)
- }
-
- try {
- await importCsv(fileContent, deleteExisting)
- Alert.alert(sharedLabels.successTitle, labels.import.success.message)
- } catch(err) {
- importError(err.message)
- }
-}
-
-function alertError(msg) {
- Alert.alert(sharedLabels.errorTitle, msg)
-}
-
-function importError(msg) {
- const postFixed = `${msg}\n\n${labels.import.errors.postFix}`
- alertError(postFixed)
-}
-
-function padWithZeros(time) {
- const vals = time.split(':')
- return vals.map(val => {
- if (parseInt(val) < 10) {
- val = `0${val}`
- }
- return val
- }).join(':')
-}
\ No newline at end of file
diff --git a/components/settings/alert-error.js b/components/settings/alert-error.js
new file mode 100644
index 0000000..ae37540
--- /dev/null
+++ b/components/settings/alert-error.js
@@ -0,0 +1,6 @@
+import { Alert } from 'react-native'
+import { shared as sharedLabels } from '../labels'
+
+export default function alertError(msg) {
+ Alert.alert(sharedLabels.errorTitle, msg)
+}
\ No newline at end of file
diff --git a/components/settings/export-dialog.js b/components/settings/export-dialog.js
new file mode 100644
index 0000000..4e3c21f
--- /dev/null
+++ b/components/settings/export-dialog.js
@@ -0,0 +1,31 @@
+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 '../labels'
+
+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-dialog.js b/components/settings/import-dialog.js
new file mode 100644
index 0000000..a076717
--- /dev/null
+++ b/components/settings/import-dialog.js
@@ -0,0 +1,58 @@
+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 { settings as labels, shared as sharedLabels } from '../labels'
+import alertError from './alert-error'
+
+export default function openImportDialogAndImport() {
+ Alert.alert(
+ labels.import.title,
+ labels.import.message,
+ [{
+ text: labels.import.replaceOption,
+ onPress: () => getFileContentAndImport({ deleteExisting: false })
+ }, {
+ text: labels.import.deleteOption,
+ onPress: () => getFileContentAndImport({ deleteExisting: true })
+ }, {
+ text: sharedLabels.cancel, style: 'cancel', onPress: () => { }
+ }]
+ )
+}
+
+async function getFileContentAndImport({ deleteExisting }) {
+ let fileInfo
+ try {
+ fileInfo = await new Promise((resolve, reject) => {
+ DocumentPicker.show({
+ filetype: [DocumentPickerUtil.allFiles()],
+ }, (err, res) => {
+ if (err) return reject(err)
+ resolve(res)
+ })
+ })
+ } catch (err) {
+ // because cancelling also triggers an error, we do nothing here
+ return
+ }
+
+ let fileContent
+ try {
+ fileContent = await rnfs.readFile(fileInfo.uri, 'utf8')
+ } catch (err) {
+ return importError(labels.import.errors.couldNotOpenFile)
+ }
+
+ try {
+ await importCsv(fileContent, deleteExisting)
+ Alert.alert(sharedLabels.successTitle, labels.import.success.message)
+ } catch(err) {
+ importError(err.message)
+ }
+}
+
+function importError(msg) {
+ const postFixed = `${msg}\n\n${labels.import.errors.postFix}`
+ alertError(postFixed)
+}
\ No newline at end of file
diff --git a/components/settings/index.js b/components/settings/index.js
new file mode 100644
index 0000000..5e729be
--- /dev/null
+++ b/components/settings/index.js
@@ -0,0 +1,61 @@
+import React, { Component } from 'react'
+import {
+ View,
+ TouchableOpacity,
+ ScrollView,
+} from 'react-native'
+import styles from '../../styles/index'
+import { settings as labels } from '../labels'
+import { AppText } from '../app-text'
+import TempReminderPicker from './temp-reminder-picker'
+import TempSlider from './temp-slider'
+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.tempScale.segmentTitle}
+
+ {labels.tempScale.segmentExplainer}
+
+
+
+
+ {labels.export.button}
+
+ {labels.export.segmentExplainer}
+
+
+ {labels.export.button}
+
+
+
+
+
+ {labels.import.button}
+
+ {labels.import.segmentExplainer}
+
+
+ {labels.import.button}
+
+
+
+
+ )
+ }
+}
diff --git a/components/settings/temp-reminder-picker.js b/components/settings/temp-reminder-picker.js
new file mode 100644
index 0000000..8cfbf86
--- /dev/null
+++ b/components/settings/temp-reminder-picker.js
@@ -0,0 +1,83 @@
+import React, { Component } from 'react'
+import {
+ View,
+ TouchableOpacity,
+ Switch
+} from 'react-native'
+import DateTimePicker from 'react-native-modal-datetime-picker-nevo'
+import { AppText } from '../app-text'
+import {
+ tempReminderObservable,
+ saveTempReminder
+} from '../../local-storage'
+import styles from '../../styles/index'
+import { settings as labels } from '../labels'
+
+export default class TempReminderPicker extends Component {
+ constructor(props) {
+ super(props)
+ this.state = Object.assign({}, tempReminderObservable.value)
+ }
+
+ render() {
+ return (
+ this.setState({ isTimePickerVisible: true })}
+ >
+
+ {labels.tempReminder.title}
+
+
+
+ {this.state.time && this.state.enabled ?
+ {labels.tempReminder.timeSet(this.state.time)}
+ :
+ {labels.tempReminder.noTimeSet}
+ }
+
+ {
+ this.setState({ enabled: switchOn })
+ if (switchOn && !this.state.time) {
+ this.setState({ isTimePickerVisible: true })
+ }
+ if (!switchOn) saveTempReminder({ enabled: false })
+ }}
+ />
+ {
+ const time = padWithZeros(`${jsDate.getHours()}:${jsDate.getMinutes()}`)
+ this.setState({
+ time,
+ isTimePickerVisible: false,
+ enabled: true
+ })
+ saveTempReminder({
+ time,
+ enabled: true
+ })
+ }}
+ onCancel={() => {
+ this.setState({ isTimePickerVisible: false })
+ if (!this.state.time) this.setState({enabled: false})
+ }}
+ />
+
+
+ )
+ }
+}
+
+function padWithZeros(time) {
+ const vals = time.split(':')
+ return vals.map(val => {
+ if (parseInt(val) < 10) {
+ val = `0${val}`
+ }
+ return val
+ }).join(':')
+}
\ No newline at end of file
diff --git a/components/settings/temp-slider.js b/components/settings/temp-slider.js
new file mode 100644
index 0000000..f21fd74
--- /dev/null
+++ b/components/settings/temp-slider.js
@@ -0,0 +1,71 @@
+import React, { Component } from 'react'
+import { View } from 'react-native'
+import Slider from '@ptomasroos/react-native-multi-slider'
+import { AppText } from '../app-text'
+import {
+ scaleObservable,
+ saveTempScale,
+} from '../../local-storage'
+import { secondaryColor } from '../../styles/index'
+import { settings as labels } from '../labels'
+import config from '../../config'
+import alertError from './alert-error'
+
+export default class TempSlider extends Component {
+ constructor(props) {
+ super(props)
+ this.state = Object.assign({}, scaleObservable.value)
+ }
+
+ onValuesChange = (values) => {
+ this.setState({
+ min: values[0],
+ max: values[1]
+ })
+ }
+
+ onValuesChangeFinish = (values) => {
+ this.setState({
+ min: values[0],
+ max: values[1]
+ })
+ try {
+ saveTempScale(this.state)
+ } catch(err) {
+ alertError(labels.tempScale.saveError)
+ }
+ }
+
+ render() {
+ return (
+
+ {`${labels.tempScale.min} ${this.state.min}`}
+ {`${labels.tempScale.max} ${this.state.max}`}
+
+
+ )
+ }
+}
\ No newline at end of file
diff --git a/lib/import-export/export-to-csv.js b/lib/import-export/export-to-csv.js
index c5bca2b..619c5e2 100644
--- a/lib/import-export/export-to-csv.js
+++ b/lib/import-export/export-to-csv.js
@@ -1,9 +1,10 @@
import objectPath from 'object-path'
import { Base64 } from 'js-base64'
-import { cycleDaysSortedByDate } from '../../db'
+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)
diff --git a/lib/import-export/get-csv-column-names.js b/lib/import-export/get-csv-column-names.js
index 1b63943..3e8b425 100644
--- a/lib/import-export/get-csv-column-names.js
+++ b/lib/import-export/get-csv-column-names.js
@@ -1,9 +1,10 @@
-import { schema } from '../../db'
+import { getSchema } from '../../db'
export default function getColumnNamesForCsv() {
return getPrefixedKeys('CycleDay')
function getPrefixedKeys(schemaName, prefix) {
+ const schema = getSchema()
const model = schema[schemaName]
return Object.keys(model).reduce((acc, key) => {
const prefixedKey = prefix ? [prefix, key].join('.') : key
diff --git a/lib/import-export/import-from-csv.js b/lib/import-export/import-from-csv.js
index 77c2444..c34ebc9 100644
--- a/lib/import-export/import-from-csv.js
+++ b/lib/import-export/import-from-csv.js
@@ -1,6 +1,6 @@
import csvParser from 'csvtojson'
import isObject from 'isobject'
-import { schema, tryToImportWithDelete, tryToImportWithoutDelete } from '../../db'
+import { getSchema, tryToImportWithDelete, tryToImportWithoutDelete } from '../../db'
import getColumnNamesForCsv from './get-csv-column-names'
export default async function importCsv(csv, deleteFirst) {
@@ -23,6 +23,7 @@ export default async function importCsv(csv, deleteFirst) {
return Number(val)
}
+ const schema = getSchema()
const config = {
ignoreEmpty: true,
colParser: getColumnNamesForCsv().reduce((acc, colName) => {
@@ -76,6 +77,7 @@ function putNullForEmptySymptoms(data) {
}
function getDbType(modelProperties, path) {
+ const schema = getSchema()
if (path.length === 1) return modelProperties[path[0]].type
const modelName = modelProperties[path[0]].objectType
return getDbType(schema[modelName], path.slice(1))