diff --git a/components/settings/about.js b/components/settings/about.js
index 5d7cba1..49f5843 100644
--- a/components/settings/about.js
+++ b/components/settings/about.js
@@ -1,20 +1,20 @@
import React, { Component } from 'react'
-import { View, ScrollView } from 'react-native'
+import { ScrollView } from 'react-native'
import AppText from '../app-text'
+import SettingsSegment from './settings-segment'
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/data-management/confirm-with-password.js b/components/settings/data-management/confirm-with-password.js
new file mode 100644
index 0000000..fee296a
--- /dev/null
+++ b/components/settings/data-management/confirm-with-password.js
@@ -0,0 +1,88 @@
+import React, { Component } from 'react'
+import { View, Alert } from 'react-native'
+
+import nodejs from 'nodejs-mobile-react-native'
+import { requestHash, openDb } from '../../../db'
+
+import PasswordField from '../password/password-field'
+import SettingsButton from '../settings-button'
+
+import settings from '../../../i18n/en/settings'
+import { shared } from '../../../i18n/en/labels'
+
+export default class ConfirmWithPassword extends Component {
+ constructor() {
+ super()
+ this.state = {
+ password: null
+ }
+ nodejs.channel.addListener(
+ 'password-check',
+ this.checkPassword,
+ this
+ )
+ }
+
+ componentWillUnmount() {
+ nodejs.channel.removeListener('password-check', this.checkPassword)
+ }
+
+ resetPasswordInput = () => {
+ this.setState({ password: null })
+ }
+
+
+ onIncorrectPassword = () => {
+ Alert.alert(
+ shared.incorrectPassword,
+ shared.incorrectPasswordMessage,
+ [{
+ text: shared.cancel,
+ onPress: this.props.onCancel
+ }, {
+ text: shared.tryAgain,
+ onPress: this.resetPasswordInput
+ }]
+ )
+ }
+
+ checkPassword = async hash => {
+ try {
+ await openDb(hash)
+ this.props.onSuccess()
+ } catch (err) {
+ this.onIncorrectPassword()
+ }
+ }
+
+ handlePasswordInput = (password) => {
+ this.setState({ password })
+ }
+
+ initPasswordCheck = () => {
+ requestHash('password-check', this.state.password)
+ }
+
+ render() {
+
+ const { password } = this.state
+ const labels = settings.passwordSettings
+
+ return (
+
+
+
+ {settings.deleteSegment.title}
+
+
+ )
+
+ }
+}
\ No newline at end of file
diff --git a/components/settings/data-management/constants.js b/components/settings/data-management/constants.js
new file mode 100644
index 0000000..610cd03
--- /dev/null
+++ b/components/settings/data-management/constants.js
@@ -0,0 +1 @@
+export const EXPORT_FILE_NAME = 'data.csv'
\ 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..dd09c4a
--- /dev/null
+++ b/components/settings/data-management/delete-data.js
@@ -0,0 +1,97 @@
+import React, { Component } from 'react'
+import RNFS from 'react-native-fs'
+import { Alert, ToastAndroid } from 'react-native'
+
+import { clearDb, isDbEmpty } from '../../../db'
+import { hasEncryptionObservable } from '../../../local-storage'
+import SettingsButton from '../settings-button'
+import ConfirmWithPassword from './confirm-with-password'
+import alertError from '../alert-error'
+
+import settings from '../../../i18n/en/settings'
+import { shared as sharedLabels } from '../../../i18n/en/labels'
+import { EXPORT_FILE_NAME } from './constants'
+
+const exportedFilePath = `${RNFS.DocumentDirectoryPath}/${EXPORT_FILE_NAME}`
+
+export default class DeleteData extends Component {
+ constructor() {
+ super()
+ this.state = {
+ isPasswordSet: hasEncryptionObservable.value,
+ isConfirmingWithPassword: false
+ }
+ }
+
+ onAlertConfirmation = () => {
+ if (this.state.isPasswordSet) {
+ this.setState({ isConfirmingWithPassword: true })
+ } else {
+ this.deleteAppData()
+ }
+ }
+
+ alertBeforeDeletion = async () => {
+ const { question, message, confirmation, errors } = settings.deleteSegment
+ if (isDbEmpty() && !await RNFS.exists(exportedFilePath)) {
+ alertError(errors.noData)
+ } else {
+ Alert.alert(
+ question,
+ message,
+ [{
+ text: confirmation,
+ onPress: this.onAlertConfirmation
+ }, {
+ text: sharedLabels.cancel,
+ style: 'cancel',
+ onPress: this.cancelConfirmationWithPassword
+ }]
+ )
+ }
+ }
+
+ deleteExportedFile = async () => {
+ if (await RNFS.exists(exportedFilePath)) {
+ await RNFS.unlink(exportedFilePath)
+ }
+ }
+
+ deleteAppData = async () => {
+ const { errors, success } = settings.deleteSegment
+
+ try {
+ if (!isDbEmpty()) {
+ clearDb()
+ }
+ await this.deleteExportedFile()
+ ToastAndroid.show(success.message, ToastAndroid.LONG)
+ } catch (err) {
+ alertError(errors.couldNotDeleteFile)
+ }
+ this.cancelConfirmationWithPassword()
+ }
+
+ cancelConfirmationWithPassword = () => {
+ this.setState({ isConfirmingWithPassword: false })
+ }
+
+ render() {
+ const { isConfirmingWithPassword } = this.state
+
+ if (isConfirmingWithPassword) {
+ return (
+
+ )
+ }
+
+ return (
+
+ {settings.deleteSegment.title}
+
+ )
+ }
+}
\ No newline at end of file
diff --git a/components/settings/import-export/export-dialog.js b/components/settings/data-management/export-dialog.js
similarity index 90%
rename from components/settings/import-export/export-dialog.js
rename to components/settings/data-management/export-dialog.js
index 58301cf..cfc5f5d 100644
--- a/components/settings/import-export/export-dialog.js
+++ b/components/settings/data-management/export-dialog.js
@@ -4,6 +4,7 @@ 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 { EXPORT_FILE_NAME } from './constants'
import RNFS from 'react-native-fs'
export default async function exportData() {
@@ -24,7 +25,7 @@ export default async function exportData() {
}
try {
- const path = RNFS.DocumentDirectoryPath + '/data.csv'
+ const path = `${RNFS.DocumentDirectoryPath}/${EXPORT_FILE_NAME}`
await RNFS.writeFile(path, data)
await Share.open({
diff --git a/components/settings/import-export/import-dialog.js b/components/settings/data-management/import-dialog.js
similarity index 100%
rename from components/settings/import-export/import-dialog.js
rename to components/settings/data-management/import-dialog.js
diff --git a/components/settings/data-management/index.js b/components/settings/data-management/index.js
new file mode 100644
index 0000000..4199760
--- /dev/null
+++ b/components/settings/data-management/index.js
@@ -0,0 +1,38 @@
+import React from 'react'
+import { ScrollView } from 'react-native'
+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 DeleteData from './delete-data'
+import labels from '../../../i18n/en/settings'
+import styles from '../../../styles/index'
+
+const DataManagement = () => {
+ return (
+
+
+ {labels.export.segmentExplainer}
+
+ {labels.export.button}
+
+
+
+ {labels.import.segmentExplainer}
+
+ {labels.import.button}
+
+
+
+ {labels.deleteSegment.explainer}
+
+
+
+ )
+}
+
+export default DataManagement
\ No newline at end of file
diff --git a/components/settings/import-export/index.js b/components/settings/import-export/index.js
deleted file mode 100644
index 430f29b..0000000
--- a/components/settings/import-export/index.js
+++ /dev/null
@@ -1,50 +0,0 @@
-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 a1eaa03..7bbbdc5 100644
--- a/components/settings/index.js
+++ b/components/settings/index.js
@@ -1,9 +1,9 @@
import Reminders from './reminders'
import NfpSettings from './nfp-settings'
-import ImportExport from './import-export'
+import DataManagement from './data-management'
import Password from './password'
import About from './about'
export default {
- Reminders, NfpSettings, ImportExport, Password, About
+ Reminders, NfpSettings, DataManagement, Password, About
}
diff --git a/components/settings/password/check-current-password.js b/components/settings/password/check-current-password.js
index 76ddc94..92d78bf 100644
--- a/components/settings/password/check-current-password.js
+++ b/components/settings/password/check-current-password.js
@@ -3,18 +3,21 @@ import { openDb } from '../../../db'
import { shared } from '../../../i18n/en/labels'
export default async function checkPassword({hash, onCancel, onTryAgain }) {
- const connected = await openDb(hash)
- if (connected) return true
- Alert.alert(
- shared.incorrectPassword,
- shared.incorrectPasswordMessage,
- [{
- text: shared.cancel,
- onPress: onCancel
- }, {
- text: shared.tryAgain,
- onPress: onTryAgain
- }]
- )
- return false
+ try {
+ await openDb(hash)
+ return true
+ } catch (err) {
+ Alert.alert(
+ shared.incorrectPassword,
+ shared.incorrectPasswordMessage,
+ [{
+ text: shared.cancel,
+ onPress: onCancel
+ }, {
+ text: shared.tryAgain,
+ onPress: onTryAgain
+ }]
+ )
+ return false
+ }
}
\ No newline at end of file
diff --git a/components/settings/password/create.js b/components/settings/password/create.js
index bda688f..cdc9b69 100644
--- a/components/settings/password/create.js
+++ b/components/settings/password/create.js
@@ -2,7 +2,7 @@ import React, { Component } from 'react'
import { View } from 'react-native'
import settings from '../../../i18n/en/settings'
import EnterNewPassword from './enter-new-password'
-import SettingsButton from './settings-button'
+import SettingsButton from '../settings-button'
import showBackUpReminder from './show-backup-reminder'
export default class CreatePassword extends Component {
diff --git a/components/settings/password/enter-new-password.js b/components/settings/password/enter-new-password.js
index ed09ead..084a6bc 100644
--- a/components/settings/password/enter-new-password.js
+++ b/components/settings/password/enter-new-password.js
@@ -5,7 +5,7 @@ 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 SettingsButton from '../settings-button'
import styles from '../../../styles'
import settings from '../../../i18n/en/settings'
diff --git a/components/settings/password/index.js b/components/settings/password/index.js
index f5741e3..fca0966 100644
--- a/components/settings/password/index.js
+++ b/components/settings/password/index.js
@@ -3,11 +3,11 @@ import { View, ScrollView } from 'react-native'
import CreatePassword from './create'
import ChangePassword from './update'
import DeletePassword from './delete'
+import SettingsSegment from '../settings-segment'
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 {
@@ -22,11 +22,7 @@ export default class PasswordSetting extends Component {
render() {
return (
-
-
-
- {labels.passwordSettings.title}
-
+
{this.state.showUpdateAndDelete ?
{labels.passwordSettings.explainerEnabled}
@@ -45,7 +41,7 @@ export default class PasswordSetting extends Component {
}
-
+
)
}
diff --git a/components/settings/password/update.js b/components/settings/password/update.js
index a246c0e..bac6d2b 100644
--- a/components/settings/password/update.js
+++ b/components/settings/password/update.js
@@ -6,7 +6,7 @@ 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 SettingsButton from '../settings-button'
import showBackUpReminder from './show-backup-reminder'
import checkCurrentPassword from './check-current-password'
diff --git a/components/settings/password/settings-button.js b/components/settings/settings-button.js
similarity index 84%
rename from components/settings/password/settings-button.js
rename to components/settings/settings-button.js
index 97272bb..bfa8983 100644
--- a/components/settings/password/settings-button.js
+++ b/components/settings/settings-button.js
@@ -2,8 +2,8 @@ import React from 'react'
import PropTypes from 'prop-types'
import { TouchableOpacity } from 'react-native'
-import AppText from '../../app-text'
-import styles from '../../../styles'
+import AppText from '../app-text'
+import styles from '../../styles'
const SettingsButton = ({ children, ...props }) => {
return (
diff --git a/components/settings/settings-menu.js b/components/settings/settings-menu.js
index 4152c11..3d927e0 100644
--- a/components/settings/settings-menu.js
+++ b/components/settings/settings-menu.js
@@ -7,14 +7,12 @@ 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.dataManagement, component: 'DataManagement'},
{title: labels.password, component: 'Password'},
{title: labels.about, component: 'About'}
]
diff --git a/components/settings/settings-segment.js b/components/settings/settings-segment.js
new file mode 100644
index 0000000..0a87719
--- /dev/null
+++ b/components/settings/settings-segment.js
@@ -0,0 +1,21 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+
+import { View } from 'react-native'
+import AppText from '../app-text'
+import styles from '../../styles'
+
+const SettingsSegment = ({ children, ...props }) => {
+ return (
+
+ {props.title}
+ {children}
+
+ )
+}
+
+SettingsSegment.propTypes = {
+ title: PropTypes.string.isRequired
+}
+
+export default SettingsSegment
\ No newline at end of file
diff --git a/db/index.js b/db/index.js
index ca9752e..5fcbab0 100644
--- a/db/index.js
+++ b/db/index.js
@@ -223,12 +223,20 @@ 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 function clearDb() {
+ db.write(db.deleteAll)
+}
+
function hashToInt8Array(hash) {
const key = new Uint8Array(64)
for (let i = 0; i < key.length; i++) {
diff --git a/i18n/en/labels.js b/i18n/en/labels.js
index 743f4c1..17e3526 100644
--- a/i18n/en/labels.js
+++ b/i18n/en/labels.js
@@ -27,7 +27,7 @@ export const headerTitles = {
SettingsMenu: 'Settings',
Reminders: settingsTitles.reminders,
NfpSettings: settingsTitles.nfpSettings,
- ImportExport: settingsTitles.importExport,
+ DataManagement: settingsTitles.dataManagement,
Password: settingsTitles.password,
About: settingsTitles.about,
BleedingEditView: 'Bleeding',
diff --git a/i18n/en/settings.js b/i18n/en/settings.js
index 9d46eb4..d3565d3 100644
--- a/i18n/en/settings.js
+++ b/i18n/en/settings.js
@@ -2,7 +2,7 @@
export default {
menuTitles: {
reminders: 'Reminders',
- importExport: 'Import and Export',
+ dataManagement: 'Manage your data',
nfpSettings: 'NFP settings',
password: 'Password',
about: 'About'
@@ -35,6 +35,21 @@ export default {
},
segmentExplainer: 'Import data in CSV format'
},
+ deleteSegment: {
+ title: 'Delete app data',
+ explainer: 'Delete app data from this phone',
+ question: 'Do you want to delete app data from this phone?',
+ message: 'Please note that deletion of the app data is permanent and irreversible. We recommend exporting existing data before deletion.',
+ confirmation: 'Delete app data permanently',
+ errors: {
+ couldNotDeleteFile: 'Could not delete data',
+ postFix: 'No data was deleted or changed',
+ noData: 'There is no data to delete'
+ },
+ success: {
+ message: 'App data successfully deleted'
+ }
+ },
tempScale: {
segmentTitle: 'Temperature scale',
segmentExplainer: 'Change the minimum and maximum value for the temperature chart',