From cb95c980310c220fa1648cd0e24ce7259f61089a Mon Sep 17 00:00:00 2001 From: mashazyu Date: Thu, 3 Jan 2019 23:37:15 +0100 Subject: [PATCH] adds delete app data functionality --- .../data-management/delete-data-dialog.js | 19 +++ .../settings/data-management/delete-data.js | 126 ++++++++++++++++++ components/settings/data-management/index.js | 68 ++++++---- components/settings/password/index.js | 1 - db/index.js | 10 ++ i18n/en/settings.js | 9 +- 6 files changed, 205 insertions(+), 28 deletions(-) create mode 100644 components/settings/data-management/delete-data-dialog.js create mode 100644 components/settings/data-management/delete-data.js diff --git a/components/settings/data-management/delete-data-dialog.js b/components/settings/data-management/delete-data-dialog.js new file mode 100644 index 0000000..0122576 --- /dev/null +++ b/components/settings/data-management/delete-data-dialog.js @@ -0,0 +1,19 @@ +import { Alert } from 'react-native' +import { shared as sharedLabels } from '../../../i18n/en/labels' +import labels from '../../../i18n/en/settings' + +export default function showDeleteDialog(okHandler) { + + const { question, message, confirmation } = labels.deleteSegment + + Alert.alert( + question, + message, + [{ + text: confirmation, + onPress: okHandler + }, { + text: sharedLabels.cancel, style: 'cancel', onPress: () => { } + }] + ) +} \ No newline at end of file diff --git a/components/settings/data-management/delete-data.js b/components/settings/data-management/delete-data.js new file mode 100644 index 0000000..3bd154d --- /dev/null +++ b/components/settings/data-management/delete-data.js @@ -0,0 +1,126 @@ +import React, { Component } from 'react' +import { View } from 'react-native' +import nodejs from 'nodejs-mobile-react-native' +import RNFS from 'react-native-fs' + +import settings from '../../../i18n/en/settings' +import { requestHash, clearDb } from '../../../db' +import { hasEncryptionObservable } from '../../../local-storage' +import PasswordField from '../password/password-field' +import SettingsButton from '../settings-button' +import showDeleteDialog from './delete-data-dialog' +import checkCurrentPassword from '../password/check-current-password' +import alertError from '../alert-error' + + +export default class DeleteData extends Component { + constructor() { + super() + this.state = { + isPasswordSet: hasEncryptionObservable.value, + currentPassword: null, + enteringCurrentPassword: false + } + + nodejs.channel.addListener( + 'pre-change-pw-check', + this.openNewPasswordField, + this + ) + } + + componentWillUnmount() { + nodejs.channel.removeListener('pre-change-pw-check', this.openNewPasswordField) + } + + openNewPasswordField = async hash => { + const passwordCorrect = await checkCurrentPassword({ + hash, + onTryAgain: () => this.setState({ currentPassword: null }), + onCancel: () => this.setState({ + enteringCurrentPassword: false, + currentPassword: null + }) + }) + + if (passwordCorrect) { + await this.deleteAppData() + } + } + + startDataDeletion = () => { + showDeleteDialog(() => { + if (this.state.isPasswordSet) { + this.setState({ enteringCurrentPassword: true }) + } else { + this.deleteAppData() + } + }) + } + + deleteExportedFile = async () => { + const path = RNFS.DocumentDirectoryPath + '/data.csv' + const isFileExist = await RNFS.exists(path) + if (isFileExist) { + await RNFS.unlink(path) + } + } + + deleteAppData = async () => { + const { errors } = settings.deleteSegment + try { + await clearDb() + await this.deleteExportedFile() + this.props.onDeleteData() + } catch (err) { + return alertError(errors.couldNotDeleteFile) + } + } + + handleCurrentPasswordInput = (currentPassword) => { + this.setState({ currentPassword }) + } + + checkCurrentPassword = () => { + requestHash('pre-change-pw-check', this.state.currentPassword) + } + + render() { + + const { + enteringCurrentPassword, + currentPassword + } = this.state + + const labels = settings.passwordSettings + + if (enteringCurrentPassword) { + return ( + + + + {settings.deleteSegment.title} + + + ) + } + + return ( + + + {settings.deleteSegment.title} + + + ) + } +} \ No newline at end of file diff --git a/components/settings/data-management/index.js b/components/settings/data-management/index.js index 52e4717..806052d 100644 --- a/components/settings/data-management/index.js +++ b/components/settings/data-management/index.js @@ -1,35 +1,51 @@ -import React from 'react' +import React, { Component } from 'react' import { ScrollView } from 'react-native' -import labels from '../../../i18n/en/settings' import AppText from '../../app-text' import SettingsSegment from '../settings-segment' import SettingsButton from '../settings-button' import openImportDialogAndImport from './import-dialog' import openShareDialogAndExport from './export-dialog' +import { isDbEmpty } from '../../../db' +import DeleteData from './delete-data' +import labels from '../../../i18n/en/settings' -const Settings = () => { - return ( - - - {labels.export.segmentExplainer} - - {labels.export.button} - - - - {labels.import.segmentExplainer} - - {labels.import.button} - - - - {labels.deleteSegment.explainer} - - {labels.deleteSegment.title} - - - - ) +class DataManagement extends Component { + constructor() { + super() + this.state = { + isDataEmpty: isDbEmpty() + } + } + onDeleteData = () => { + this.setState({ + isDataEmpty: true + }) + } + render() { + return ( + + + {labels.export.segmentExplainer} + + {labels.export.button} + + + + {labels.import.segmentExplainer} + + {labels.import.button} + + + {!isDbEmpty() && ( + + {labels.deleteSegment.explainer} + + + )} + + ) + } } -export default Settings + +export default DataManagement diff --git a/components/settings/password/index.js b/components/settings/password/index.js index e263cae..fca0966 100644 --- a/components/settings/password/index.js +++ b/components/settings/password/index.js @@ -8,7 +8,6 @@ import AppText from '../../app-text' import { hasEncryptionObservable } from '../../../local-storage' -import styles from '../../../styles/index' import labels from '../../../i18n/en/settings' export default class PasswordSetting extends Component { diff --git a/db/index.js b/db/index.js index ca9752e..47942d3 100644 --- a/db/index.js +++ b/db/index.js @@ -223,12 +223,22 @@ export async function changeEncryptionAndRestartApp(hash) { restart.Restart() } +export function isDbEmpty () { + return db.empty +} + export async function deleteDbAndOpenNew() { const exists = await fs.exists(Realm.defaultPath) if (exists) await fs.unlink(Realm.defaultPath) await openDb() } +export async function clearDb() { + await db.write(async () => { + await db.deleteAll() + }) +} + function hashToInt8Array(hash) { const key = new Uint8Array(64) for (let i = 0; i < key.length; i++) { diff --git a/i18n/en/settings.js b/i18n/en/settings.js index 4628f45..a4149a5 100644 --- a/i18n/en/settings.js +++ b/i18n/en/settings.js @@ -37,7 +37,14 @@ export default { }, deleteSegment: { title: 'Delete app data', - explainer: 'Delete all app data from this phone' + explainer: 'Delete all app data from this phone', + question: 'Do you want to delete data from this phone?', + message: 'Please note that deletion of the data is permanent and irreversible. We recomend to export existing data before deletion.', + confirmation: 'Yes, I want to delete data permanently', + errors: { + couldNotDeleteFile: 'Could not delete file', + postFix: 'No data was deleted or changed' + } }, tempScale: { segmentTitle: 'Temperature scale',