Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9b9db80949 | |||
| 6cfbcd0408 | |||
| 93608ca8ef |
@@ -8,13 +8,15 @@ import { Sizes } from '../../styles'
|
|||||||
import { CHART_TICK_WIDTH } from '../../config'
|
import { CHART_TICK_WIDTH } from '../../config'
|
||||||
|
|
||||||
const Tick = ({ yPosition, height, isBold, shouldShowLabel, label }) => {
|
const Tick = ({ yPosition, height, isBold, shouldShowLabel, label }) => {
|
||||||
const top = yPosition - height / 2
|
const top = yPosition - height / 2 - 4
|
||||||
const containerStyle = [styles.container, { flexBasis: height, height, top }]
|
const containerStyle = [styles.container, { flexBasis: height, height, top }]
|
||||||
const textStyle = isBold ? styles.textBold : styles.textNormal
|
const textStyle = isBold ? styles.textBold : styles.textNormal
|
||||||
|
|
||||||
|
if (!shouldShowLabel) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={containerStyle}>
|
<View style={containerStyle}>
|
||||||
<AppText style={textStyle}>{shouldShowLabel && label}</AppText>
|
<AppText style={textStyle}>{label}</AppText>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -36,6 +38,7 @@ const styles = StyleSheet.create({
|
|||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
right: 0,
|
right: 0,
|
||||||
width: CHART_TICK_WIDTH,
|
width: CHART_TICK_WIDTH,
|
||||||
|
minHeight: Sizes.base + 2,
|
||||||
},
|
},
|
||||||
textBold: {
|
textBold: {
|
||||||
fontSize: Sizes.base,
|
fontSize: Sizes.base,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { LocalDate } from '@js-joda/core'
|
|||||||
import { scaleObservable, unitObservable } from '../../local-storage'
|
import { scaleObservable, unitObservable } from '../../local-storage'
|
||||||
import { getCycleStatusForDay } from '../../lib/sympto-adapter'
|
import { getCycleStatusForDay } from '../../lib/sympto-adapter'
|
||||||
import { getCycleDay, getAmountOfCycleDays } from '../../db'
|
import { getCycleDay, getAmountOfCycleDays } from '../../db'
|
||||||
|
import { Sizes } from '../../styles'
|
||||||
|
|
||||||
//YAxis helpers
|
//YAxis helpers
|
||||||
|
|
||||||
@@ -50,14 +51,18 @@ export function getTickList(columnHeight) {
|
|||||||
const label = tick.toFixed(1)
|
const label = tick.toFixed(1)
|
||||||
let shouldShowLabel
|
let shouldShowLabel
|
||||||
|
|
||||||
// when temp range <= 2, units === 0.1 we show temp values with step 0.2
|
// when units === 0.1 and tick height is big enough, we show temp values with step 0.2
|
||||||
// when temp range > 2, units === 0.5 we show temp values with step 0.5
|
// when units === 0.5 and tick height is not big enough, we show temp values with step 1
|
||||||
|
// otherwise we show temp values with step 0.5
|
||||||
|
|
||||||
if (unit === 0.1) {
|
switch (true) {
|
||||||
// show label with step 0.2
|
case unit === 0.1 && tickHeight > Sizes.base + 2:
|
||||||
shouldShowLabel = !((label * 10) % 2)
|
shouldShowLabel = !((label * 10) % 2)
|
||||||
} else {
|
break
|
||||||
// show label with step 0.5
|
case unit === 0.5 && tickHeight <= Sizes.base + 2:
|
||||||
|
shouldShowLabel = !((label * 10) % 10)
|
||||||
|
break
|
||||||
|
default:
|
||||||
shouldShowLabel = !((label * 10) % 5)
|
shouldShowLabel = !((label * 10) % 5)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
import React, { useState } from 'react'
|
|
||||||
|
|
||||||
import AppLoadingView from '../../common/app-loading'
|
|
||||||
import AppPage from '../../common/app-page'
|
|
||||||
import AppText from '../../common/app-text'
|
|
||||||
import Segment from '../../common/segment'
|
|
||||||
|
|
||||||
import DeleteData from './delete-data'
|
|
||||||
|
|
||||||
import labels from '../../../i18n/en/settings'
|
|
||||||
import ImportData from './ImportData'
|
|
||||||
import ExportData from './ExportData'
|
|
||||||
|
|
||||||
const DataManagement = () => {
|
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
|
||||||
const [isDeletingData, setIsDeletingData] = useState(false)
|
|
||||||
|
|
||||||
if (isLoading) return <AppLoadingView />
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AppPage>
|
|
||||||
<ExportData
|
|
||||||
resetIsDeletingData={() => setIsDeletingData(false)}
|
|
||||||
setIsLoading={setIsLoading}
|
|
||||||
/>
|
|
||||||
<ImportData
|
|
||||||
resetIsDeletingData={() => setIsDeletingData(false)}
|
|
||||||
setIsLoading={setIsLoading}
|
|
||||||
/>
|
|
||||||
<Segment title={labels.deleteSegment.title} last>
|
|
||||||
<AppText>{labels.deleteSegment.explainer}</AppText>
|
|
||||||
<DeleteData
|
|
||||||
isDeletingData={isDeletingData}
|
|
||||||
onStartDeletion={() => setIsDeletingData(true)}
|
|
||||||
/>
|
|
||||||
</Segment>
|
|
||||||
</AppPage>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DataManagement
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import { getCycleDaysSortedByDate, mapRealmObjToJsObj } from '../../../db'
|
|
||||||
import getDataAsCsvDataUri from '../../../lib/import-export/export-to-csv'
|
|
||||||
import alertError from '../common/alert-error'
|
|
||||||
import { EXPORT_FILE_NAME } from './constants'
|
|
||||||
import RNFS from 'react-native-fs'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
|
|
||||||
import AppText from '../../common/app-text'
|
|
||||||
import Button from '../../common/button'
|
|
||||||
import Segment from '../../common/segment'
|
|
||||||
import Share from 'react-native-share'
|
|
||||||
|
|
||||||
export default function ExportData({ setIsLoading, resetIsDeletingData }) {
|
|
||||||
const { t } = useTranslation(null, {
|
|
||||||
keyPrefix: 'hamburgerMenu.settings.data.export',
|
|
||||||
})
|
|
||||||
|
|
||||||
async function startExport() {
|
|
||||||
resetIsDeletingData()
|
|
||||||
setIsLoading(true)
|
|
||||||
await exportData()
|
|
||||||
setIsLoading(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getData() {
|
|
||||||
const cycleDaysByDate = mapRealmObjToJsObj(getCycleDaysSortedByDate())
|
|
||||||
|
|
||||||
try {
|
|
||||||
return cycleDaysByDate.length
|
|
||||||
? getDataAsCsvDataUri(cycleDaysByDate)
|
|
||||||
: null
|
|
||||||
} catch (err) {
|
|
||||||
alertError(t('error.convert'))
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function exportData() {
|
|
||||||
const data = await getData()
|
|
||||||
if (!data) {
|
|
||||||
alertError(t('error.data'))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const path = `${RNFS.DocumentDirectoryPath}/${EXPORT_FILE_NAME}`
|
|
||||||
await RNFS.writeFile(path, data)
|
|
||||||
|
|
||||||
await Share.open({
|
|
||||||
title: t('title'),
|
|
||||||
url: `file://${path}`,
|
|
||||||
subject: t('title'),
|
|
||||||
type: 'text/csv',
|
|
||||||
showAppsToView: true,
|
|
||||||
failOnCancel: false,
|
|
||||||
})
|
|
||||||
} catch (err) {
|
|
||||||
return alertError(t('error.share'))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Segment title={t('button')}>
|
|
||||||
<AppText>{t('text')}</AppText>
|
|
||||||
<Button isCTA onPress={startExport}>
|
|
||||||
{t('button')}
|
|
||||||
</Button>
|
|
||||||
</Segment>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
ExportData.propTypes = {
|
|
||||||
resetIsDeletingData: PropTypes.func.isRequired,
|
|
||||||
setIsLoading: PropTypes.func.isRequired,
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import { Alert } from 'react-native'
|
|
||||||
import DocumentPicker from 'react-native-document-picker'
|
|
||||||
import rnfs from 'react-native-fs'
|
|
||||||
import importCsv from '../../../lib/import-export/import-from-csv'
|
|
||||||
import alertError from '../common/alert-error'
|
|
||||||
import Segment from '../../common/segment'
|
|
||||||
import AppText from '../../common/app-text'
|
|
||||||
import Button from '../../common/button'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
|
|
||||||
export default function ImportData({ resetIsDeletingData, setIsLoading }) {
|
|
||||||
const { t } = useTranslation(null, {
|
|
||||||
keyPrefix: 'hamburgerMenu.settings.data.import',
|
|
||||||
})
|
|
||||||
|
|
||||||
async function startImport(shouldDeleteExistingData) {
|
|
||||||
setIsLoading(true)
|
|
||||||
await importData(shouldDeleteExistingData)
|
|
||||||
setIsLoading(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getFileInfo() {
|
|
||||||
try {
|
|
||||||
const fileInfo = await DocumentPicker.pickSingle({
|
|
||||||
type: [DocumentPicker.types.csv, 'text/comma-separated-values'],
|
|
||||||
})
|
|
||||||
return fileInfo
|
|
||||||
} catch (error) {
|
|
||||||
if (DocumentPicker.isCancel(error)) return // User cancelled the picker, exit any dialogs or menus and move on
|
|
||||||
showImportErrorAlert(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getFileContent() {
|
|
||||||
const fileInfo = await getFileInfo()
|
|
||||||
if (!fileInfo) return null
|
|
||||||
|
|
||||||
try {
|
|
||||||
const fileContent = await rnfs.readFile(fileInfo.uri, 'utf8')
|
|
||||||
return fileContent
|
|
||||||
} catch (err) {
|
|
||||||
return showImportErrorAlert(t('errors.couldNotOpenFile'))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function importData(shouldDeleteExistingData) {
|
|
||||||
const fileContent = await getFileContent()
|
|
||||||
if (!fileContent) return
|
|
||||||
|
|
||||||
try {
|
|
||||||
await importCsv(fileContent, shouldDeleteExistingData)
|
|
||||||
Alert.alert(t('success.title'), t('success.message'))
|
|
||||||
} catch (err) {
|
|
||||||
showImportErrorAlert(err.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function openImportDialog() {
|
|
||||||
resetIsDeletingData()
|
|
||||||
Alert.alert(t('dialog.title'), t('dialog.message'), [
|
|
||||||
{
|
|
||||||
text: t('dialog.cancel'),
|
|
||||||
style: 'cancel',
|
|
||||||
onPress: () => {},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: t('dialog.replace'),
|
|
||||||
onPress: () => startImport(false),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: t('dialog.delete'),
|
|
||||||
onPress: () => startImport(true),
|
|
||||||
},
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
function showImportErrorAlert(message) {
|
|
||||||
const errorMessage = t('errors.noDataImported', { message })
|
|
||||||
alertError(errorMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Segment title={t('button')}>
|
|
||||||
<AppText>{t('segmentExplainer')}</AppText>
|
|
||||||
<Button isCTA onPress={openImportDialog}>
|
|
||||||
{t('button')}
|
|
||||||
</Button>
|
|
||||||
</Segment>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
ImportData.propTypes = {
|
|
||||||
resetIsDeletingData: PropTypes.func.isRequired,
|
|
||||||
setIsLoading: PropTypes.func.isRequired,
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import Share from 'react-native-share'
|
||||||
|
|
||||||
|
import { getCycleDaysSortedByDate, mapRealmObjToJsObj } from '../../../db'
|
||||||
|
import getDataAsCsvDataUri from '../../../lib/import-export/export-to-csv'
|
||||||
|
import alertError from '../common/alert-error'
|
||||||
|
import settings from '../../../i18n/en/settings'
|
||||||
|
import { EXPORT_FILE_NAME } from './constants'
|
||||||
|
import RNFS from 'react-native-fs'
|
||||||
|
|
||||||
|
export default async function exportData() {
|
||||||
|
let data
|
||||||
|
const labels = settings.export
|
||||||
|
const cycleDaysByDate = mapRealmObjToJsObj(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}/${EXPORT_FILE_NAME}`
|
||||||
|
await RNFS.writeFile(path, data)
|
||||||
|
|
||||||
|
await Share.open({
|
||||||
|
title: labels.title,
|
||||||
|
url: `file://${path}`,
|
||||||
|
subject: labels.subject,
|
||||||
|
type: 'text/csv',
|
||||||
|
showAppsToView: true,
|
||||||
|
failOnCancel: false,
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
return alertError(labels.errors.problemSharing)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { Alert } from 'react-native'
|
||||||
|
import DocumentPicker 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 labels from '../../../i18n/en/settings'
|
||||||
|
import alertError from '../common/alert-error'
|
||||||
|
|
||||||
|
export function openImportDialog(onImportData) {
|
||||||
|
Alert.alert(labels.import.title, labels.import.message, [
|
||||||
|
{
|
||||||
|
text: sharedLabels.cancel,
|
||||||
|
style: 'cancel',
|
||||||
|
onPress: () => {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: labels.import.replaceOption,
|
||||||
|
onPress: () => onImportData(false),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: labels.import.deleteOption,
|
||||||
|
onPress: () => onImportData(true),
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getFileContent() {
|
||||||
|
let fileInfo
|
||||||
|
try {
|
||||||
|
fileInfo = await DocumentPicker.pickSingle({
|
||||||
|
type: [DocumentPicker.types.csv, 'text/comma-separated-values'],
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
if (DocumentPicker.isCancel(error)) {
|
||||||
|
// User cancelled the picker, exit any dialogs or menus and move on
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
importError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let fileContent
|
||||||
|
try {
|
||||||
|
fileContent = await rnfs.readFile(fileInfo.uri, 'utf8')
|
||||||
|
} catch (err) {
|
||||||
|
return importError(labels.import.errors.couldNotOpenFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileContent
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function importData(shouldDeleteExistingData, fileContent) {
|
||||||
|
try {
|
||||||
|
await importCsv(fileContent, shouldDeleteExistingData)
|
||||||
|
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)
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
import AppLoadingView from '../../common/app-loading'
|
||||||
|
import AppPage from '../../common/app-page'
|
||||||
|
import AppText from '../../common/app-text'
|
||||||
|
import Button from '../../common/button'
|
||||||
|
import Segment from '../../common/segment'
|
||||||
|
|
||||||
|
import { openImportDialog, getFileContent, importData } from './import-dialog'
|
||||||
|
import openShareDialogAndExport from './export-dialog'
|
||||||
|
import DeleteData from './delete-data'
|
||||||
|
|
||||||
|
import labels from '../../../i18n/en/settings'
|
||||||
|
import { ACTION_DELETE, ACTION_EXPORT, ACTION_IMPORT } from '../../../config'
|
||||||
|
|
||||||
|
const DataManagement = () => {
|
||||||
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
|
const [currentAction, setCurrentAction] = useState(null)
|
||||||
|
|
||||||
|
const startImportFlow = async (shouldDeleteExistingData) => {
|
||||||
|
setIsLoading(true)
|
||||||
|
const fileContent = await getFileContent()
|
||||||
|
if (fileContent) {
|
||||||
|
await importData(shouldDeleteExistingData, fileContent)
|
||||||
|
}
|
||||||
|
setIsLoading(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const startExport = () => {
|
||||||
|
setCurrentAction(ACTION_EXPORT)
|
||||||
|
openShareDialogAndExport()
|
||||||
|
}
|
||||||
|
|
||||||
|
const startImport = () => {
|
||||||
|
setCurrentAction(ACTION_IMPORT)
|
||||||
|
openImportDialog(startImportFlow)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLoading) return <AppLoadingView />
|
||||||
|
|
||||||
|
const isDeletingData = currentAction === ACTION_DELETE
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AppPage>
|
||||||
|
<Segment title={labels.export.button}>
|
||||||
|
<AppText>{labels.export.segmentExplainer}</AppText>
|
||||||
|
<Button isCTA onPress={startExport}>
|
||||||
|
{labels.export.button}
|
||||||
|
</Button>
|
||||||
|
</Segment>
|
||||||
|
<Segment title={labels.import.button}>
|
||||||
|
<AppText>{labels.import.segmentExplainer}</AppText>
|
||||||
|
<Button isCTA onPress={startImport}>
|
||||||
|
{labels.import.button}
|
||||||
|
</Button>
|
||||||
|
</Segment>
|
||||||
|
<Segment title={labels.deleteSegment.title} last>
|
||||||
|
<AppText>{labels.deleteSegment.explainer}</AppText>
|
||||||
|
<DeleteData
|
||||||
|
isDeletingData={isDeletingData}
|
||||||
|
onStartDeletion={() => setCurrentAction(ACTION_DELETE)}
|
||||||
|
/>
|
||||||
|
</Segment>
|
||||||
|
</AppPage>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DataManagement
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import Reminders from './reminders/reminders'
|
import Reminders from './reminders/reminders'
|
||||||
import NfpSettings from './nfp-settings'
|
import NfpSettings from './nfp-settings'
|
||||||
import DataManagement from './data-management/DataManagement'
|
import DataManagement from './data-management'
|
||||||
import Password from './password'
|
import Password from './password'
|
||||||
import About from './About'
|
import About from './About'
|
||||||
import License from './License'
|
import License from './License'
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import { PixelRatio, StatusBar } from 'react-native'
|
import { PixelRatio, StatusBar } from 'react-native'
|
||||||
import { scale, verticalScale } from 'react-native-size-matters'
|
import { scale, verticalScale } from 'react-native-size-matters'
|
||||||
|
|
||||||
|
export const ACTION_DELETE = 'delete'
|
||||||
|
export const ACTION_EXPORT = 'export'
|
||||||
|
export const ACTION_IMPORT = 'import'
|
||||||
|
|
||||||
export const SYMPTOMS = [
|
export const SYMPTOMS = [
|
||||||
'bleeding',
|
'bleeding',
|
||||||
'temperature',
|
'temperature',
|
||||||
@@ -36,7 +40,7 @@ export const HIT_SLOP = {
|
|||||||
top: verticalScale(20),
|
top: verticalScale(20),
|
||||||
bottom: verticalScale(20),
|
bottom: verticalScale(20),
|
||||||
left: scale(20),
|
left: scale(20),
|
||||||
right: scale(20),
|
right: scale(20)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const STATUSBAR_HEIGHT = StatusBar.currentHeight
|
export const STATUSBAR_HEIGHT = StatusBar.currentHeight
|
||||||
|
|||||||
@@ -80,38 +80,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"data": {
|
|
||||||
"export": {
|
|
||||||
"button": "Export data",
|
|
||||||
"error": {
|
|
||||||
"convert": "Could not convert data to CSV",
|
|
||||||
"data": "There is no data to export",
|
|
||||||
"share": "There was a problem sharing the data export file"
|
|
||||||
},
|
|
||||||
"text": "Export data in CSV format for backup or so you can use it elsewhere",
|
|
||||||
"title": "My drip. data export"
|
|
||||||
},
|
|
||||||
"import": {
|
|
||||||
"button": "Import data",
|
|
||||||
"dialog": {
|
|
||||||
"cancel": "Cancel",
|
|
||||||
"delete": "Import and delete existing",
|
|
||||||
"message": "There are two options for the import:\n\n1. Keep existing cycle days and replace only the ones in the import file.\n\n2. Delete all existing cycle days and import cycle days from file",
|
|
||||||
"replace": "Import and replace",
|
|
||||||
"title": "Keep existing data?"
|
|
||||||
},
|
|
||||||
"errors": {
|
|
||||||
"couldNotOpenFile": "Could not open file",
|
|
||||||
"futureEdit": "Future dates may only contain a note, no other symptoms",
|
|
||||||
"noDataImported": "{{message}}\n\nNo data was imported or changed"
|
|
||||||
},
|
|
||||||
"segmentExplainer": "Import data in CSV format",
|
|
||||||
"success": {
|
|
||||||
"message": "Data successfully imported",
|
|
||||||
"title": "Success"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"menuItem": {
|
"menuItem": {
|
||||||
"dataManagement": {
|
"dataManagement": {
|
||||||
"name": "Data",
|
"name": "Data",
|
||||||
|
|||||||
@@ -19,6 +19,36 @@ export default {
|
|||||||
text: '',
|
text: '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
export: {
|
||||||
|
errors: {
|
||||||
|
noData: 'There is no data to export',
|
||||||
|
couldNotConvert: 'Could not convert data to CSV',
|
||||||
|
problemSharing: 'There was a problem sharing the data export file',
|
||||||
|
},
|
||||||
|
title: 'My drip. data export',
|
||||||
|
subject: 'My drip. data export',
|
||||||
|
button: 'Export data',
|
||||||
|
segmentExplainer:
|
||||||
|
'Export data in CSV format for backup or so you can use it elsewhere',
|
||||||
|
},
|
||||||
|
import: {
|
||||||
|
button: 'Import data',
|
||||||
|
title: 'Keep existing data?',
|
||||||
|
message: `There are two options for the import:
|
||||||
|
1. Keep existing cycle days and replace only the ones in the import file.
|
||||||
|
2. Delete all existing cycle days and import cycle days from file.`,
|
||||||
|
replaceOption: 'Import and replace',
|
||||||
|
deleteOption: 'Import and delete existing',
|
||||||
|
errors: {
|
||||||
|
couldNotOpenFile: 'Could not open file',
|
||||||
|
postFix: 'No data was imported or changed',
|
||||||
|
futureEdit: 'Future dates may only contain a note, no other symptoms',
|
||||||
|
},
|
||||||
|
success: {
|
||||||
|
message: 'Data successfully imported',
|
||||||
|
},
|
||||||
|
segmentExplainer: 'Import data in CSV format',
|
||||||
|
},
|
||||||
deleteSegment: {
|
deleteSegment: {
|
||||||
title: 'Delete app data',
|
title: 'Delete app data',
|
||||||
explainer: 'Delete app data from this phone',
|
explainer: 'Delete app data from this phone',
|
||||||
|
|||||||
+8
-2
@@ -12,9 +12,15 @@ export const unitObservable = Observable()
|
|||||||
unitObservable.set(TEMP_SCALE_UNITS)
|
unitObservable.set(TEMP_SCALE_UNITS)
|
||||||
scaleObservable((scale) => {
|
scaleObservable((scale) => {
|
||||||
const scaleRange = scale.max - scale.min
|
const scaleRange = scale.max - scale.min
|
||||||
if (scaleRange <= 1.5) {
|
|
||||||
|
switch (true) {
|
||||||
|
case scaleRange <= 1:
|
||||||
unitObservable.set(0.1)
|
unitObservable.set(0.1)
|
||||||
} else {
|
break
|
||||||
|
case scaleRange > 1 && scaleRange <= 2:
|
||||||
|
unitObservable.set(0.2)
|
||||||
|
break
|
||||||
|
default:
|
||||||
unitObservable.set(0.5)
|
unitObservable.set(0.5)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user