diff --git a/components/settings/data-management/DataManagement.js b/components/settings/data-management/DataManagement.js index d36ad70..1e4d2a3 100644 --- a/components/settings/data-management/DataManagement.js +++ b/components/settings/data-management/DataManagement.js @@ -6,40 +6,23 @@ 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' +import ImportData from './ImportData' 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 [isDeletingData, setIsDeletingData] = useState(false) const startExport = () => { - setCurrentAction(ACTION_EXPORT) + setIsDeletingData(false) openShareDialogAndExport() } - const startImport = () => { - setCurrentAction(ACTION_IMPORT) - openImportDialog(startImportFlow) - } - if (isLoading) return - const isDeletingData = currentAction === ACTION_DELETE - return ( @@ -48,17 +31,15 @@ const DataManagement = () => { {labels.export.button} - - {labels.import.segmentExplainer} - - + setIsDeletingData(false)} + setIsLoading={setIsLoading} + /> {labels.deleteSegment.explainer} setCurrentAction(ACTION_DELETE)} + onStartDeletion={() => setIsDeletingData(true)} /> diff --git a/components/settings/data-management/ImportData.js b/components/settings/data-management/ImportData.js new file mode 100644 index 0000000..d88a21e --- /dev/null +++ b/components/settings/data-management/ImportData.js @@ -0,0 +1,97 @@ +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 startImportFlow(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: () => startImportFlow(false), + }, + { + text: t('dialog.delete'), + onPress: () => startImportFlow(true), + }, + ]) + } + + function showImportErrorAlert(message) { + const errorMessage = t('errors.noDataImported', { message }) + alertError(errorMessage) + } + + return ( + + {t('segmentExplainer')} + + + ) +} + +ImportData.propTypes = { + resetIsDeletingData: PropTypes.func.isRequired, + setIsLoading: PropTypes.func.isRequired, +} diff --git a/i18n/en.json b/i18n/en.json index 7102896..39a1123 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -80,6 +80,28 @@ } }, "settings": { + "data": { + "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": { "dataManagement": { "name": "Data", diff --git a/i18n/en/settings.js b/i18n/en/settings.js index bb5117c..4cdffcd 100644 --- a/i18n/en/settings.js +++ b/i18n/en/settings.js @@ -31,24 +31,6 @@ export default { 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: { title: 'Delete app data', explainer: 'Delete app data from this phone',