From b7470151b453df5c9c13422525e69b6017ff9e82 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Fri, 7 Sep 2018 20:01:53 +0200 Subject: [PATCH 01/37] Copy database and delete old one --- android/app/build.gradle | 1 + .../main/java/com/drip/MainApplication.java | 2 + android/settings.gradle | 2 + components/home.js | 11 +- db/index.js | 21 ++- ios/drip.xcodeproj/project.pbxproj | 19 ++ package-lock.json | 172 +++++------------- package.json | 1 + 8 files changed, 102 insertions(+), 127 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index f33fb0e..b85bcdb 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -149,6 +149,7 @@ android { } dependencies { + compile project(':react-native-restart') compile project(':react-native-push-notification') compile project(':react-native-vector-icons') compile project(':react-native-fs') diff --git a/android/app/src/main/java/com/drip/MainApplication.java b/android/app/src/main/java/com/drip/MainApplication.java index da1f79f..efe98fc 100644 --- a/android/app/src/main/java/com/drip/MainApplication.java +++ b/android/app/src/main/java/com/drip/MainApplication.java @@ -3,6 +3,7 @@ package com.drip; import android.app.Application; import com.facebook.react.ReactApplication; +import com.avishayil.rnrestart.ReactNativeRestartPackage; import com.dieam.reactnativepushnotification.ReactNativePushNotificationPackage; import com.oblador.vectoricons.VectorIconsPackage; import com.rnfs.RNFSPackage; @@ -30,6 +31,7 @@ public class MainApplication extends Application implements ReactApplication, Sh protected List getPackages() { return Arrays.asList( new MainReactPackage(), + new ReactNativeRestartPackage(), new ReactNativePushNotificationPackage(), new VectorIconsPackage(), new RNFSPackage(), diff --git a/android/settings.gradle b/android/settings.gradle index fe009fa..8dc197c 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,4 +1,6 @@ rootProject.name = 'drip' +include ':react-native-restart' +project(':react-native-restart').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-restart/android') include ':react-native-push-notification' project(':react-native-push-notification').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-push-notification/android') include ':react-native-vector-icons' diff --git a/components/home.js b/components/home.js index 1fa051d..9bd82a1 100644 --- a/components/home.js +++ b/components/home.js @@ -8,7 +8,7 @@ import { import { LocalDate, ChronoUnit } from 'js-joda' import styles from '../styles/index' import cycleModule from '../lib/cycle' -import { getOrCreateCycleDay, bleedingDaysSortedByDate, fillWithMucusDummyData, fillWithCervixDummyData, deleteAll } from '../db' +import { encrypt, getOrCreateCycleDay, bleedingDaysSortedByDate, fillWithMucusDummyData, fillWithCervixDummyData, deleteAll } from '../db' import {bleedingPrediction as labels} from './labels' const getCycleDayNumber = cycleModule().getCycleDayNumber @@ -78,6 +78,15 @@ export default class Home extends Component { title="delete everything"> + + + ) diff --git a/db/index.js b/db/index.js index 1b79d7b..ecbb2ce 100644 --- a/db/index.js +++ b/db/index.js @@ -1,5 +1,7 @@ import Realm from 'realm' import { LocalDate, ChronoUnit } from 'js-joda' +import fs from 'react-native-fs' +import restart from 'react-native-restart' import { cycleWithFhmMucus, longAndComplicatedCycleWithMucus, @@ -154,7 +156,7 @@ const realmConfig = { deleteRealmIfMigrationNeeded: true } -const db = new Realm(realmConfig) +let db = new Realm(realmConfig) const bleedingDaysSortedByDate = db.objects('CycleDay').filtered('bleeding != null').sorted('date', true) const temperatureDaysSortedByDate = db.objects('CycleDay').filtered('temperature != null').sorted('date', true) @@ -289,6 +291,20 @@ function tryToImportWithoutDelete(cycleDays) { }) } +async function encrypt() { + const oldPath = db.path + const dir = db.path.split('/') + dir.pop() + dir.push('copied.realm') + const copyPath = dir.join('/') + db.writeCopyTo(copyPath) + db.close() + await fs.unlink(oldPath) + await fs.moveFile(copyPath, oldPath) + db = new Realm(realmConfig) + restart.Restart() +} + export { saveSymptom, getOrCreateCycleDay, @@ -303,5 +319,6 @@ export { getAmountOfCycleDays, schema, tryToImportWithDelete, - tryToImportWithoutDelete + tryToImportWithoutDelete, + encrypt } diff --git a/ios/drip.xcodeproj/project.pbxproj b/ios/drip.xcodeproj/project.pbxproj index 25f1f76..2db2b35 100644 --- a/ios/drip.xcodeproj/project.pbxproj +++ b/ios/drip.xcodeproj/project.pbxproj @@ -58,6 +58,7 @@ A1410AC4C98A49B2820D9E45 /* Zocial.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B6F5078F7DEC470782757471 /* Zocial.ttf */; }; 29DF0CCC1AEA4C92BCA0BCCD /* libRNDocumentPicker.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D211D71BE5A8436A978770A9 /* libRNDocumentPicker.a */; }; 17AD822C42A44BADA96BD860 /* libRNFS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 84CCEBD3B2C44758853BC941 /* libRNFS.a */; }; + 72DA6B4241504DB096AFAD40 /* libRCTRestart.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AB636AA0286D45CE9B23B2C3 /* libRCTRestart.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -389,6 +390,8 @@ D211D71BE5A8436A978770A9 /* libRNDocumentPicker.a */ = {isa = PBXFileReference; name = "libRNDocumentPicker.a"; path = "libRNDocumentPicker.a"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; }; 49089E09BFCF4F3DB209B6E9 /* RNFS.xcodeproj */ = {isa = PBXFileReference; name = "RNFS.xcodeproj"; path = "../node_modules/react-native-fs/RNFS.xcodeproj"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; }; 84CCEBD3B2C44758853BC941 /* libRNFS.a */ = {isa = PBXFileReference; name = "libRNFS.a"; path = "libRNFS.a"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; }; + 50DBC4BCDDF74A10AEDC99D5 /* RCTRestart.xcodeproj */ = {isa = PBXFileReference; name = "RCTRestart.xcodeproj"; path = "../node_modules/react-native-restart/ios/RCTRestart.xcodeproj"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; }; + AB636AA0286D45CE9B23B2C3 /* libRCTRestart.a */ = {isa = PBXFileReference; name = "libRCTRestart.a"; path = "libRCTRestart.a"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -424,6 +427,7 @@ AED64B7892744F21B3A156BB /* libRNVectorIcons.a in Frameworks */, 29DF0CCC1AEA4C92BCA0BCCD /* libRNDocumentPicker.a in Frameworks */, 17AD822C42A44BADA96BD860 /* libRNFS.a in Frameworks */, + 72DA6B4241504DB096AFAD40 /* libRCTRestart.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -618,6 +622,7 @@ D1E5ACC4B66345868F556374 /* RNVectorIcons.xcodeproj */, 1F05FE29622E4F21AF70C2B7 /* RNDocumentPicker.xcodeproj */, 49089E09BFCF4F3DB209B6E9 /* RNFS.xcodeproj */, + 50DBC4BCDDF74A10AEDC99D5 /* RCTRestart.xcodeproj */, ); name = Libraries; sourceTree = ""; @@ -1284,6 +1289,7 @@ "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -1292,6 +1298,7 @@ "$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager", "$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker", "$(SRCROOT)/../node_modules/react-native-fs/**", + "$(SRCROOT)/../node_modules/react-native-restart/ios/RCTRestart/**", ); }; name = Debug; @@ -1315,6 +1322,7 @@ "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -1323,6 +1331,7 @@ "$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager", "$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker", "$(SRCROOT)/../node_modules/react-native-fs/**", + "$(SRCROOT)/../node_modules/react-native-restart/ios/RCTRestart/**", ); }; name = Release; @@ -1349,6 +1358,7 @@ "$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager", "$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker", "$(SRCROOT)/../node_modules/react-native-fs/**", + "$(SRCROOT)/../node_modules/react-native-restart/ios/RCTRestart/**", ); }; name = Debug; @@ -1374,6 +1384,7 @@ "$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager", "$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker", "$(SRCROOT)/../node_modules/react-native-fs/**", + "$(SRCROOT)/../node_modules/react-native-restart/ios/RCTRestart/**", ); }; name = Release; @@ -1406,6 +1417,7 @@ "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -1414,6 +1426,7 @@ "$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager", "$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker", "$(SRCROOT)/../node_modules/react-native-fs/**", + "$(SRCROOT)/../node_modules/react-native-restart/ios/RCTRestart/**", ); }; name = Debug; @@ -1446,6 +1459,7 @@ "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -1454,6 +1468,7 @@ "$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager", "$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker", "$(SRCROOT)/../node_modules/react-native-fs/**", + "$(SRCROOT)/../node_modules/react-native-restart/ios/RCTRestart/**", ); }; name = Release; @@ -1485,6 +1500,7 @@ "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -1493,6 +1509,7 @@ "$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager", "$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker", "$(SRCROOT)/../node_modules/react-native-fs/**", + "$(SRCROOT)/../node_modules/react-native-restart/ios/RCTRestart/**", ); }; name = Debug; @@ -1524,6 +1541,7 @@ "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -1532,6 +1550,7 @@ "$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager", "$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker", "$(SRCROOT)/../node_modules/react-native-fs/**", + "$(SRCROOT)/../node_modules/react-native-restart/ios/RCTRestart/**", ); }; name = Release; diff --git a/package-lock.json b/package-lock.json index bb711bf..0e8cf6f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1088,11 +1088,6 @@ } } }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", @@ -1287,9 +1282,9 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "atob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.1.tgz", - "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" }, "aws-sign2": { "version": "0.6.0", @@ -1992,9 +1987,9 @@ } }, "babel-preset-fbjs": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-2.2.0.tgz", - "integrity": "sha512-jj0KFJDioYZMtPtZf77dQuU+Ad/1BtN0UnAYlHDa8J8f4tGXr3YrPoJImD5MdueaOPeN/jUdrCgu330EfXr0XQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-2.3.0.tgz", + "integrity": "sha512-ZOpAI1/bN0Y3J1ZAK9gRsFkHy9gGgJoDRUjtUCla/129LC7uViq9nIK22YdHfey8szohYoZY3f9L2lGOv0Edqw==", "requires": { "babel-plugin-check-es2015-constants": "^6.8.0", "babel-plugin-syntax-class-properties": "^6.8.0", @@ -2205,11 +2200,6 @@ "kind-of": "^6.0.2" } }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", @@ -2252,9 +2242,9 @@ } }, "big-integer": { - "version": "1.6.34", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.34.tgz", - "integrity": "sha512-+w6B0Uo0ZvTSzDkXjoBCTNK0oe+aVL+yPi7kwGZm8hd8+Nj1AFPoxoq1Bl/mEu/G/ivOkUc1LRqVR0XeWFUzuA==" + "version": "1.6.36", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.36.tgz", + "integrity": "sha512-t70bfa7HYEA1D9idDbmuv7YbsbVkQ+Hp+8KFSul4aE5e/i1bjCNIRYJZlA8Q8p0r9T8cF/RVvwUgRA//FydEyg==" }, "bl": { "version": "1.2.2", @@ -2405,13 +2395,6 @@ "to-object-path": "^0.3.0", "union-value": "^1.0.0", "unset-value": "^1.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - } } }, "caller-path": { @@ -2513,11 +2496,6 @@ "requires": { "is-descriptor": "^0.1.0" } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" } } }, @@ -2620,9 +2598,9 @@ } }, "commander": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.16.0.tgz", - "integrity": "sha512-sVXqklSaotK9at437sFlFpyOcJonxe0yST/AG9DkQKUdIE6IqGIMv4SfAQSKaJbSdVEJYItASCrBiVQHq1HQew==" + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.18.0.tgz", + "integrity": "sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ==" }, "commondir": { "version": "1.0.1", @@ -2696,9 +2674,12 @@ "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, "convert-source-map": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", - "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=" + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "requires": { + "safe-buffer": "~5.1.1" + } }, "copy-descriptor": { "version": "0.1.1", @@ -2964,11 +2945,6 @@ "kind-of": "^6.0.2" } }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", @@ -3736,13 +3712,11 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true, - "optional": true + "bundled": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3755,18 +3729,15 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true, - "optional": true + "bundled": true }, "concat-map": { "version": "0.0.1", - "bundled": true, - "optional": true + "bundled": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true, - "optional": true + "bundled": true }, "core-util-is": { "version": "1.0.2", @@ -3869,8 +3840,7 @@ }, "inherits": { "version": "2.0.3", - "bundled": true, - "optional": true + "bundled": true }, "ini": { "version": "1.3.5", @@ -3880,7 +3850,6 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3893,20 +3862,17 @@ "minimatch": { "version": "3.0.4", "bundled": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "bundled": true, - "optional": true + "bundled": true }, "minipass": { "version": "2.2.4", "bundled": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -3923,7 +3889,6 @@ "mkdirp": { "version": "0.5.1", "bundled": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -3996,8 +3961,7 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, - "optional": true + "bundled": true }, "object-assign": { "version": "4.1.1", @@ -4007,7 +3971,6 @@ "once": { "version": "1.4.0", "bundled": true, - "optional": true, "requires": { "wrappy": "1" } @@ -4113,7 +4076,6 @@ "string-width": { "version": "1.0.2", "bundled": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -4394,13 +4356,6 @@ "get-value": "^2.0.6", "has-values": "^1.0.0", "isobject": "^3.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - } } }, "has-values": { @@ -4834,13 +4789,6 @@ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "requires": { "isobject": "^3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - } } }, "is-posix-bracket": { @@ -5674,9 +5622,9 @@ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" }, "nan": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", - "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.0.tgz", + "integrity": "sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw==", "optional": true }, "nanomatch": { @@ -5971,13 +5919,6 @@ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "requires": { "isobject": "^3.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - } } }, "object.omit": { @@ -5995,13 +5936,6 @@ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "requires": { "isobject": "^3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - } } }, "obv": { @@ -6192,9 +6126,9 @@ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" }, "path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" }, "path-type": { "version": "2.0.0", @@ -6373,9 +6307,9 @@ "integrity": "sha512-eTPo5t/4bgaMNZxyjWx6N2a6AuE0mq51KWvpc7nU/MAqixcI6v6KrGUKES0HaomdnolQBBXU/++X6/QQ9KL4tw==" }, "randomatic": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.0.0.tgz", - "integrity": "sha512-VdxFOIEY3mNO5PtSRkkle/hPJDHvQhK21oa73K4yAc9qmp6N429gAyF1gZMOTMeS0/AYzaV/2Trcef+NaIonSA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.0.tgz", + "integrity": "sha512-KnGPVE0lo2WoXxIZ7cPR8YBpiol4gsSuOwDSg410oHh80ZMp5EiypNqL2K4Z77vJn6lB5rap7IkAmcUlalcnBQ==", "requires": { "is-number": "^4.0.0", "kind-of": "^6.0.0", @@ -6427,14 +6361,14 @@ "integrity": "sha1-K7qMaUBMXkqUQ5hgC8xMlB+GBoI=" }, "react-deep-force-update": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/react-deep-force-update/-/react-deep-force-update-1.1.1.tgz", - "integrity": "sha1-vNMUeAJ7ZLMznxCJIatSC0MT3Cw=" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/react-deep-force-update/-/react-deep-force-update-1.1.2.tgz", + "integrity": "sha512-WUSQJ4P/wWcusaH+zZmbECOk7H5N2pOIl0vzheeornkIMhu+qrNdGFm0bDZLCb0hSF0jf/kH1SgkNGfBdTc4wA==" }, "react-devtools-core": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-3.2.3.tgz", - "integrity": "sha1-o34ZnZSGXiy7YWuXvo9YIGdOar0=", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-3.3.3.tgz", + "integrity": "sha512-RftDnDpmIjNz39JQA04EmUD6wxpDW1J+zKSh25g/JOdtoe9BeoreIXbeQJcdnHBPZ4x3Dpy64nrXYzHnpO684A==", "requires": { "shell-quote": "^1.6.1", "ws": "^3.3.1" @@ -6610,6 +6544,11 @@ "resolved": "https://registry.npmjs.org/react-native-push-notification/-/react-native-push-notification-3.1.1.tgz", "integrity": "sha512-4+4yQXNPqh5IVvpSBmR4Cy/UeMjTcfE8KIJgEuT7pME97WK+aGPn6W3ybhOoXC1n+ZWKfrAlsHydLE4xfBZDJg==" }, + "react-native-restart": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/react-native-restart/-/react-native-restart-0.0.7.tgz", + "integrity": "sha512-/7TdL3pHM7H0Cut6U6SK1jd6fVYvLlQq0O4inJ7j4YD0w9BtAAFqLkLnBDOA0jL4oLw/89yR8HhLOzNFGvVsfw==" + }, "react-native-share": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/react-native-share/-/react-native-share-1.1.0.tgz", @@ -6863,9 +6802,9 @@ "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" }, "repeat-element": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" }, "repeat-string": { "version": "1.6.1", @@ -7337,11 +7276,6 @@ } } }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", @@ -7643,11 +7577,6 @@ "kind-of": "^6.0.2" } }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", @@ -8397,11 +8326,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" } } }, diff --git a/package.json b/package.json index a58e129..a988aa2 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "react-native-fs": "^2.10.14", "react-native-modal-datetime-picker-nevo": "^4.11.0", "react-native-push-notification": "^3.1.1", + "react-native-restart": "0.0.7", "react-native-share": "^1.1.0", "react-native-vector-icons": "^5.0.0", "realm": "^2.7.1" From 4839f95312c4f09f3ddce5f9dfdad6c12ddbc75a Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Sun, 9 Sep 2018 17:15:31 +0200 Subject: [PATCH 02/37] Use nodejs-mobile and bcrypt to generate encryption key, and encrypt db --- android/app/build.gradle | 1 + .../main/java/com/drip/MainApplication.java | 2 + android/settings.gradle | 2 + components/app.js | 11 + components/home.js | 5 +- db/index.js | 18 +- ios/drip.xcodeproj/project.pbxproj | 252 ++++++++++++++++++ nodejs-assets/nodejs-project/main.js | 17 ++ .../nodejs-project/package-lock.json | 11 + nodejs-assets/nodejs-project/package.json | 6 + package-lock.json | 96 +++++++ package.json | 1 + 12 files changed, 417 insertions(+), 5 deletions(-) create mode 100644 nodejs-assets/nodejs-project/main.js create mode 100644 nodejs-assets/nodejs-project/package-lock.json create mode 100644 nodejs-assets/nodejs-project/package.json diff --git a/android/app/build.gradle b/android/app/build.gradle index b85bcdb..9c525a6 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -149,6 +149,7 @@ android { } dependencies { + compile project(':nodejs-mobile-react-native') compile project(':react-native-restart') compile project(':react-native-push-notification') compile project(':react-native-vector-icons') diff --git a/android/app/src/main/java/com/drip/MainApplication.java b/android/app/src/main/java/com/drip/MainApplication.java index efe98fc..dc83fe7 100644 --- a/android/app/src/main/java/com/drip/MainApplication.java +++ b/android/app/src/main/java/com/drip/MainApplication.java @@ -3,6 +3,7 @@ package com.drip; import android.app.Application; import com.facebook.react.ReactApplication; +import com.janeasystems.rn_nodejs_mobile.RNNodeJsMobilePackage; import com.avishayil.rnrestart.ReactNativeRestartPackage; import com.dieam.reactnativepushnotification.ReactNativePushNotificationPackage; import com.oblador.vectoricons.VectorIconsPackage; @@ -31,6 +32,7 @@ public class MainApplication extends Application implements ReactApplication, Sh protected List getPackages() { return Arrays.asList( new MainReactPackage(), + new RNNodeJsMobilePackage(), new ReactNativeRestartPackage(), new ReactNativePushNotificationPackage(), new VectorIconsPackage(), diff --git a/android/settings.gradle b/android/settings.gradle index 8dc197c..327fbe1 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,4 +1,6 @@ rootProject.name = 'drip' +include ':nodejs-mobile-react-native' +project(':nodejs-mobile-react-native').projectDir = new File(rootProject.projectDir, '../node_modules/nodejs-mobile-react-native/android') include ':react-native-restart' project(':react-native-restart').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-restart/android') include ':react-native-push-notification' diff --git a/components/app.js b/components/app.js index ba58437..ba7e2c7 100644 --- a/components/app.js +++ b/components/app.js @@ -1,5 +1,6 @@ import React, { Component } from 'react' import { View, BackHandler } from 'react-native' +import nodejs from 'nodejs-mobile-react-native' import Header from './header' import Menu from './menu' import Home from './home' @@ -11,6 +12,7 @@ import Settings from './settings' import Stats from './stats' import {headerTitles as titles} from './labels' import setupNotifications from '../lib/notifications' +import { encrypt } from '../db' const isSymptomView = name => Object.keys(symptomViews).indexOf(name) > -1 @@ -22,6 +24,15 @@ export default class App extends Component { } this.backHandler = BackHandler.addEventListener('hardwareBackPress', this.handleBackButtonPress) setupNotifications(this.navigate) + nodejs.start('main.js') + nodejs.channel.addListener( + 'message', + msg => { + msg = JSON.parse(msg) + encrypt(msg.message) + }, + this + ) } componentWillUnmount() { diff --git a/components/home.js b/components/home.js index 9bd82a1..b483274 100644 --- a/components/home.js +++ b/components/home.js @@ -8,7 +8,7 @@ import { import { LocalDate, ChronoUnit } from 'js-joda' import styles from '../styles/index' import cycleModule from '../lib/cycle' -import { encrypt, getOrCreateCycleDay, bleedingDaysSortedByDate, fillWithMucusDummyData, fillWithCervixDummyData, deleteAll } from '../db' +import { requestHash, getOrCreateCycleDay, bleedingDaysSortedByDate, fillWithMucusDummyData, fillWithCervixDummyData, deleteAll } from '../db' import {bleedingPrediction as labels} from './labels' const getCycleDayNumber = cycleModule().getCycleDayNumber @@ -81,8 +81,7 @@ export default class Home extends Component { diff --git a/db/index.js b/db/index.js index ecbb2ce..fc68de6 100644 --- a/db/index.js +++ b/db/index.js @@ -1,5 +1,6 @@ import Realm from 'realm' import { LocalDate, ChronoUnit } from 'js-joda' +import nodejs from 'nodejs-mobile-react-native' import fs from 'react-native-fs' import restart from 'react-native-restart' import { @@ -291,7 +292,14 @@ function tryToImportWithoutDelete(cycleDays) { }) } -async function encrypt() { +function requestHash() { + nodejs.channel.send(JSON.stringify({ + type: 'request-hash', + message: 'mypassword' + })) +} + +async function encrypt(hash) { const oldPath = db.path const dir = db.path.split('/') dir.pop() @@ -300,7 +308,12 @@ async function encrypt() { db.writeCopyTo(copyPath) db.close() await fs.unlink(oldPath) - await fs.moveFile(copyPath, oldPath) + const key = new Array(64).map((_, i) => { + let code = hash.charCodeAt(i) + if (isNaN(code)) code = 0 + return code + }) + realmConfig.enryptionKey = key db = new Realm(realmConfig) restart.Restart() } @@ -320,5 +333,6 @@ export { schema, tryToImportWithDelete, tryToImportWithoutDelete, + requestHash, encrypt } diff --git a/ios/drip.xcodeproj/project.pbxproj b/ios/drip.xcodeproj/project.pbxproj index 2db2b35..34660c7 100644 --- a/ios/drip.xcodeproj/project.pbxproj +++ b/ios/drip.xcodeproj/project.pbxproj @@ -59,6 +59,11 @@ 29DF0CCC1AEA4C92BCA0BCCD /* libRNDocumentPicker.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D211D71BE5A8436A978770A9 /* libRNDocumentPicker.a */; }; 17AD822C42A44BADA96BD860 /* libRNFS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 84CCEBD3B2C44758853BC941 /* libRNFS.a */; }; 72DA6B4241504DB096AFAD40 /* libRCTRestart.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AB636AA0286D45CE9B23B2C3 /* libRCTRestart.a */; }; + E09F3B05A4F84E9883101CC7 /* libRNNodeJsMobile.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F992F2D99E614DD79FAD6565 /* libRNNodeJsMobile.a */; }; + E43EF009AC8C4698AB322190 /* NodeMobile.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C225FC4966694B9FBD32E946 /* NodeMobile.framework */; }; + 8EA186B6112C41D1B206762D /* NodeMobile.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C225FC4966694B9FBD32E946 /* NodeMobile.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + E4584E55EEC24302A3E84A23 /* nodejs-project in Resources */ = {isa = PBXBuildFile; fileRef = 6466AE2461BE4FA88B8372F0 /* nodejs-project */; }; + A16B351C3F3644CF95F104D2 /* builtin_modules in Resources */ = {isa = PBXBuildFile; fileRef = 36F1B55D0DEE47AA9AF4BBDD /* builtin_modules */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -392,6 +397,11 @@ 84CCEBD3B2C44758853BC941 /* libRNFS.a */ = {isa = PBXFileReference; name = "libRNFS.a"; path = "libRNFS.a"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; }; 50DBC4BCDDF74A10AEDC99D5 /* RCTRestart.xcodeproj */ = {isa = PBXFileReference; name = "RCTRestart.xcodeproj"; path = "../node_modules/react-native-restart/ios/RCTRestart.xcodeproj"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; }; AB636AA0286D45CE9B23B2C3 /* libRCTRestart.a */ = {isa = PBXFileReference; name = "libRCTRestart.a"; path = "libRCTRestart.a"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; }; + 65F706FAFA1444AE9937D472 /* RNNodeJsMobile.xcodeproj */ = {isa = PBXFileReference; name = "RNNodeJsMobile.xcodeproj"; path = "../node_modules/nodejs-mobile-react-native/ios/RNNodeJsMobile.xcodeproj"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; }; + F992F2D99E614DD79FAD6565 /* libRNNodeJsMobile.a */ = {isa = PBXFileReference; name = "libRNNodeJsMobile.a"; path = "libRNNodeJsMobile.a"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; }; + C225FC4966694B9FBD32E946 /* NodeMobile.framework */ = {isa = PBXFileReference; name = "NodeMobile.framework"; path = "../node_modules/nodejs-mobile-react-native/ios/NodeMobile.framework"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = wrapper.framework; explicitFileType = undefined; includeInIndex = 0; }; + 6466AE2461BE4FA88B8372F0 /* nodejs-project */ = {isa = PBXFileReference; name = "nodejs-project"; path = "../nodejs-assets/nodejs-project"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; + 36F1B55D0DEE47AA9AF4BBDD /* builtin_modules */ = {isa = PBXFileReference; name = "builtin_modules"; path = "../node_modules/nodejs-mobile-react-native/install/resources/nodejs-modules/builtin_modules"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -428,6 +438,8 @@ 29DF0CCC1AEA4C92BCA0BCCD /* libRNDocumentPicker.a in Frameworks */, 17AD822C42A44BADA96BD860 /* libRNFS.a in Frameworks */, 72DA6B4241504DB096AFAD40 /* libRCTRestart.a in Frameworks */, + E09F3B05A4F84E9883101CC7 /* libRNNodeJsMobile.a in Frameworks */, + E43EF009AC8C4698AB322190 /* NodeMobile.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -580,6 +592,7 @@ 2D16E6891FA4F8E400B85C8A /* libReact.a */, 9AEBF0735214455AAEDF56D5 /* libc++.tbd */, CD8C8B91E0A747B3883A0D56 /* libz.tbd */, + C225FC4966694B9FBD32E946 /* NodeMobile.framework */, ); name = Frameworks; sourceTree = ""; @@ -623,6 +636,7 @@ 1F05FE29622E4F21AF70C2B7 /* RNDocumentPicker.xcodeproj */, 49089E09BFCF4F3DB209B6E9 /* RNFS.xcodeproj */, 50DBC4BCDDF74A10AEDC99D5 /* RCTRestart.xcodeproj */, + 65F706FAFA1444AE9937D472 /* RNNodeJsMobile.xcodeproj */, ); name = Libraries; sourceTree = ""; @@ -645,6 +659,8 @@ 83CBBA001A601CBA00E9B192 /* Products */, 2D16E6871FA4F8E400B85C8A /* Frameworks */, 006C39A0B9774387BC5ACA43 /* Resources */, + 6466AE2461BE4FA88B8372F0 /* nodejs-project */, + 36F1B55D0DEE47AA9AF4BBDD /* builtin_modules */, ); indentWidth = 2; sourceTree = ""; @@ -722,6 +738,10 @@ 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, + 2B572382D4504B8FB4B9D251 /* Embed Frameworks */, + 7DDFD19623084447885928A6 /* Build NodeJS Mobile Native Modules */, + 554E2494DF2646B083F4BD1D /* Sign NodeJS Mobile Native Modules */, + 8F5D6E75B7D344BD80BC6EC0 /* Remove NodeJS Mobile Framework Simulator Strips */, ); buildRules = ( ); @@ -1153,6 +1173,8 @@ DB91E6CCC3EB4A549D947797 /* Octicons.ttf in Resources */, 3DF2498A20844F298CD84CC3 /* SimpleLineIcons.ttf in Resources */, A1410AC4C98A49B2820D9E45 /* Zocial.ttf in Resources */, + E4584E55EEC24302A3E84A23 /* nodejs-project in Resources */, + A16B351C3F3644CF95F104D2 /* builtin_modules in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1202,6 +1224,160 @@ shellPath = /bin/sh; shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; }; + 7DDFD19623084447885928A6 /* Build NodeJS Mobile Native Modules */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + name = "Build NodeJS Mobile Native Modules"; + inputPaths = ( + ); + outputPaths = ( + ); + shellPath = /bin/sh; + shellScript = " +set -e +if [ -z \"$NODEJS_MOBILE_BUILD_NATIVE_MODULES\" ]; then +# If build native modules preference is not set, look for it in the project's +#nodejs-assets/BUILD_NATIVE_MODULES.txt file. +NODEJS_ASSETS_DIR=\"$( cd \"$PROJECT_DIR\" && cd ../nodejs-assets/ && pwd )\" +PREFERENCE_FILE_PATH=\"$NODEJS_ASSETS_DIR/BUILD_NATIVE_MODULES.txt\" + if [ -f \"$PREFERENCE_FILE_PATH\" ]; then + NODEJS_MOBILE_BUILD_NATIVE_MODULES=\"$(cat $PREFERENCE_FILE_PATH | xargs)\" + fi +fi +if [ -z \"$NODEJS_MOBILE_BUILD_NATIVE_MODULES\" ]; then +# If build native modules preference is not set, try to find .gyp files +#to turn it on. + gypfiles=($(find \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -type f -name \"*.gyp\")) + if [ ${#gypfiles[@]} -gt 0 ]; then + NODEJS_MOBILE_BUILD_NATIVE_MODULES=1 + else + NODEJS_MOBILE_BUILD_NATIVE_MODULES=0 + fi +fi +if [ \"1\" != \"$NODEJS_MOBILE_BUILD_NATIVE_MODULES\" ]; then exit 0; fi +# Delete object files that may already come from within the npm package. +find \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.o\" -type f -delete +find \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.a\" -type f -delete +find \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.node\" -type f -delete +# Delete bundle contents that may be there from previous builds. +find \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -path \"*/*.node/*\" -delete +find \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.node\" -type d -delete +find \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -path \"*/*.framework/*\" -delete +find \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.framework\" -type d -delete +# Apply patches to the modules package.json +if [ -d \"$CODESIGNING_FOLDER_PATH\"/nodejs-project/node_modules/ ]; then + PATCH_SCRIPT_DIR=\"$( cd \"$PROJECT_DIR\" && cd ../node_modules/nodejs-mobile-react-native/scripts/ && pwd )\" + NODEJS_PROJECT_MODULES_DIR=\"$( cd \"$CODESIGNING_FOLDER_PATH\" && cd nodejs-project/node_modules/ && pwd )\" + node \"$PATCH_SCRIPT_DIR\"/patch-package.js $NODEJS_PROJECT_MODULES_DIR +fi +# Get the nodejs-mobile-gyp location +if [ -d \"$PROJECT_DIR/../node_modules/nodejs-mobile-gyp/\" ]; then + NODEJS_MOBILE_GYP_DIR=\"$( cd \"$PROJECT_DIR\" && cd ../node_modules/nodejs-mobile-gyp/ && pwd )\" +else + NODEJS_MOBILE_GYP_DIR=\"$( cd \"$PROJECT_DIR\" && cd ../node_modules/nodejs-mobile-react-native/node_modules/nodejs-mobile-gyp/ && pwd )\" +fi +NODEJS_MOBILE_GYP_BIN_FILE=\"$NODEJS_MOBILE_GYP_DIR\"/bin/node-gyp.js +# Rebuild modules with right environment +NODEJS_HEADERS_DIR=\"$( cd \"$PROJECT_DIR\" && cd ../node_modules/nodejs-mobile-react-native/ios/libnode/ && pwd )\" +pushd $CODESIGNING_FOLDER_PATH/nodejs-project/ +if [ \"$PLATFORM_NAME\" == \"iphoneos\" ] +then + GYP_DEFINES=\"OS=ios\" npm_config_nodedir=\"$NODEJS_HEADERS_DIR\" npm_config_node_gyp=\"$NODEJS_MOBILE_GYP_BIN_FILE\" npm_config_platform=\"ios\" npm_config_format=\"make-ios\" npm_config_node_engine=\"chakracore\" npm_config_arch=\"arm64\" npm --verbose rebuild --build-from-source +else + GYP_DEFINES=\"OS=ios\" npm_config_nodedir=\"$NODEJS_HEADERS_DIR\" npm_config_node_gyp=\"$NODEJS_MOBILE_GYP_BIN_FILE\" npm_config_platform=\"ios\" npm_config_format=\"make-ios\" npm_config_node_engine=\"chakracore\" npm_config_arch=\"x64\" npm --verbose rebuild --build-from-source +fi +popd +"; + }; + 554E2494DF2646B083F4BD1D /* Sign NodeJS Mobile Native Modules */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + name = "Sign NodeJS Mobile Native Modules"; + inputPaths = ( + ); + outputPaths = ( + ); + shellPath = /bin/sh; + shellScript = " +set -e +if [ -z \"$NODEJS_MOBILE_BUILD_NATIVE_MODULES\" ]; then +# If build native modules preference is not set, look for it in the project's +#nodejs-assets/BUILD_NATIVE_MODULES.txt file. +NODEJS_ASSETS_DIR=\"$( cd \"$PROJECT_DIR\" && cd ../nodejs-assets/ && pwd )\" +PREFERENCE_FILE_PATH=\"$NODEJS_ASSETS_DIR/BUILD_NATIVE_MODULES.txt\" + if [ -f \"$PREFERENCE_FILE_PATH\" ]; then + NODEJS_MOBILE_BUILD_NATIVE_MODULES=\"$(cat $PREFERENCE_FILE_PATH | xargs)\" + fi +fi +if [ -z \"$NODEJS_MOBILE_BUILD_NATIVE_MODULES\" ]; then +# If build native modules preference is not set, try to find .gyp files +#to turn it on. + gypfiles=($(find \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -type f -name \"*.gyp\")) + if [ ${#gypfiles[@]} -gt 0 ]; then + NODEJS_MOBILE_BUILD_NATIVE_MODULES=1 + else + NODEJS_MOBILE_BUILD_NATIVE_MODULES=0 + fi +fi +if [ \"1\" != \"$NODEJS_MOBILE_BUILD_NATIVE_MODULES\" ]; then exit 0; fi +# Delete object files +find \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.o\" -type f -delete +find \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.a\" -type f -delete +# Create Info.plist for each framework built and loader override. +PATCH_SCRIPT_DIR=\"$( cd \"$PROJECT_DIR\" && cd ../node_modules/nodejs-mobile-react-native/scripts/ && pwd )\" +NODEJS_PROJECT_DIR=\"$( cd \"$CODESIGNING_FOLDER_PATH\" && cd nodejs-project/ && pwd )\" +node \"$PATCH_SCRIPT_DIR\"/ios-create-plists-and-dlopen-override.js $NODEJS_PROJECT_DIR +# Embed every resulting .framework in the application and delete them afterwards. +embed_framework() +{ + FRAMEWORK_NAME=\"$(basename \"$1\")\" + cp -r \"$1\" \"$TARGET_BUILD_DIR/$FRAMEWORKS_FOLDER_PATH/\" + + /usr/bin/codesign --force --sign $EXPANDED_CODE_SIGN_IDENTITY --preserve-metadata=identifier,entitlements,flags --timestamp=none \"$TARGET_BUILD_DIR/$FRAMEWORKS_FOLDER_PATH/$FRAMEWORK_NAME\" +} +find \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.framework\" -type d | while read frmwrk_path; do embed_framework \"$frmwrk_path\"; done + +#Delete gyp temporary .deps dependency folders from the project structure. +find \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -path \"*/.deps/*\" -delete +find \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \".deps\" -type d -delete + +#Delete frameworks from their build paths +find \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -path \"*/*.framework/*\" -delete +find \"$CODESIGNING_FOLDER_PATH/nodejs-project/\" -name \"*.framework\" -type d -delete +"; + }; + 8F5D6E75B7D344BD80BC6EC0 /* Remove NodeJS Mobile Framework Simulator Strips */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + name = "Remove NodeJS Mobile Framework Simulator Strips"; + inputPaths = ( + ); + outputPaths = ( + ); + shellPath = /bin/sh; + shellScript = " +set -e +FRAMEWORK_BINARY_PATH=\"$TARGET_BUILD_DIR/$FRAMEWORKS_FOLDER_PATH/NodeMobile.framework/NodeMobile\" +FRAMEWORK_STRIPPED_PATH=\"$FRAMEWORK_BINARY_PATH-strip\" +if [ \"$PLATFORM_NAME\" != \"iphonesimulator\" ]; then + if $(lipo \"$FRAMEWORK_BINARY_PATH\" -verify_arch \"x86_64\") ; then + lipo -output \"$FRAMEWORK_STRIPPED_PATH\" -remove \"x86_64\" \"$FRAMEWORK_BINARY_PATH\" + rm \"$FRAMEWORK_BINARY_PATH\" + mv \"$FRAMEWORK_STRIPPED_PATH\" \"$FRAMEWORK_BINARY_PATH\" + echo \"Removed simulator strip from NodeMobile.framework\" + fi +fi +"; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -1290,6 +1466,7 @@ "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -1299,7 +1476,14 @@ "$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker", "$(SRCROOT)/../node_modules/react-native-fs/**", "$(SRCROOT)/../node_modules/react-native-restart/ios/RCTRestart/**", + "$(SRCROOT)/../node_modules/nodejs-mobile-react-native/ios/**", ); + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"../node_modules/nodejs-mobile-react-native/ios\"", + "\"../node_modules/nodejs-mobile-react-native/ios\"", + ); + ENABLE_BITCODE = NO; }; name = Debug; }; @@ -1323,6 +1507,7 @@ "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -1332,7 +1517,14 @@ "$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker", "$(SRCROOT)/../node_modules/react-native-fs/**", "$(SRCROOT)/../node_modules/react-native-restart/ios/RCTRestart/**", + "$(SRCROOT)/../node_modules/nodejs-mobile-react-native/ios/**", ); + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"../node_modules/nodejs-mobile-react-native/ios\"", + "\"../node_modules/nodejs-mobile-react-native/ios\"", + ); + ENABLE_BITCODE = NO; }; name = Release; }; @@ -1359,7 +1551,13 @@ "$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker", "$(SRCROOT)/../node_modules/react-native-fs/**", "$(SRCROOT)/../node_modules/react-native-restart/ios/RCTRestart/**", + "$(SRCROOT)/../node_modules/nodejs-mobile-react-native/ios/**", ); + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"../node_modules/nodejs-mobile-react-native/ios\"", + ); + ENABLE_BITCODE = NO; }; name = Debug; }; @@ -1385,7 +1583,13 @@ "$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker", "$(SRCROOT)/../node_modules/react-native-fs/**", "$(SRCROOT)/../node_modules/react-native-restart/ios/RCTRestart/**", + "$(SRCROOT)/../node_modules/nodejs-mobile-react-native/ios/**", ); + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"../node_modules/nodejs-mobile-react-native/ios\"", + ); + ENABLE_BITCODE = NO; }; name = Release; }; @@ -1418,6 +1622,7 @@ "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -1427,7 +1632,14 @@ "$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker", "$(SRCROOT)/../node_modules/react-native-fs/**", "$(SRCROOT)/../node_modules/react-native-restart/ios/RCTRestart/**", + "$(SRCROOT)/../node_modules/nodejs-mobile-react-native/ios/**", ); + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"../node_modules/nodejs-mobile-react-native/ios\"", + "\"../node_modules/nodejs-mobile-react-native/ios\"", + ); + ENABLE_BITCODE = NO; }; name = Debug; }; @@ -1460,6 +1672,7 @@ "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -1469,7 +1682,14 @@ "$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker", "$(SRCROOT)/../node_modules/react-native-fs/**", "$(SRCROOT)/../node_modules/react-native-restart/ios/RCTRestart/**", + "$(SRCROOT)/../node_modules/nodejs-mobile-react-native/ios/**", ); + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"../node_modules/nodejs-mobile-react-native/ios\"", + "\"../node_modules/nodejs-mobile-react-native/ios\"", + ); + ENABLE_BITCODE = NO; }; name = Release; }; @@ -1501,6 +1721,7 @@ "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -1510,7 +1731,14 @@ "$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker", "$(SRCROOT)/../node_modules/react-native-fs/**", "$(SRCROOT)/../node_modules/react-native-restart/ios/RCTRestart/**", + "$(SRCROOT)/../node_modules/nodejs-mobile-react-native/ios/**", ); + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"../node_modules/nodejs-mobile-react-native/ios\"", + "\"../node_modules/nodejs-mobile-react-native/ios\"", + ); + ENABLE_BITCODE = NO; }; name = Debug; }; @@ -1542,6 +1770,7 @@ "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -1551,7 +1780,14 @@ "$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker", "$(SRCROOT)/../node_modules/react-native-fs/**", "$(SRCROOT)/../node_modules/react-native-restart/ios/RCTRestart/**", + "$(SRCROOT)/../node_modules/nodejs-mobile-react-native/ios/**", ); + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"../node_modules/nodejs-mobile-react-native/ios\"", + "\"../node_modules/nodejs-mobile-react-native/ios\"", + ); + ENABLE_BITCODE = NO; }; name = Release; }; @@ -1593,6 +1829,7 @@ MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; + ENABLE_BITCODE = NO; }; name = Debug; }; @@ -1628,6 +1865,7 @@ MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; + ENABLE_BITCODE = NO; }; name = Release; }; @@ -1680,6 +1918,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 2B572382D4504B8FB4B9D251 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8EA186B6112C41D1B206762D /* NodeMobile.framework in Embed Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + name = "Embed Frameworks"; + dstPath = ""; + dstSubfolderSpec = 10; + }; +/* End PBXCopyFilesBuildPhase section */ }; rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; } diff --git a/nodejs-assets/nodejs-project/main.js b/nodejs-assets/nodejs-project/main.js new file mode 100644 index 0000000..77ab09d --- /dev/null +++ b/nodejs-assets/nodejs-project/main.js @@ -0,0 +1,17 @@ +const rnBridge = require('rn-bridge') +try { + const bcryptjs = require('bcryptjs') + + rnBridge.channel.on('message', (msg) => { + msg = JSON.parse(msg) + if (msg.type === 'request-hash') { + const hash = bcryptjs.hashSync(msg.message, 10) + rnBridge.channel.send(JSON.stringify({ + type: 'hash', + message: hash + })) + } + }) +} catch (err) { + rnBridge.channel.send(JSON.stringify(err.message)) +} \ No newline at end of file diff --git a/nodejs-assets/nodejs-project/package-lock.json b/nodejs-assets/nodejs-project/package-lock.json new file mode 100644 index 0000000..4a3b908 --- /dev/null +++ b/nodejs-assets/nodejs-project/package-lock.json @@ -0,0 +1,11 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" + } + } +} diff --git a/nodejs-assets/nodejs-project/package.json b/nodejs-assets/nodejs-project/package.json new file mode 100644 index 0000000..a51d26f --- /dev/null +++ b/nodejs-assets/nodejs-project/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "bcryptjs": "^2.4.3" + }, + "main": "main.js" +} diff --git a/package-lock.json b/package-lock.json index 0e8cf6f..26216ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2467,6 +2467,11 @@ "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", "dev": true }, + "chownr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", + "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=" + }, "circular-json": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", @@ -5515,6 +5520,30 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, + "minipass": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.4.tgz", + "integrity": "sha512-mlouk1OHlaUE8Odt1drMtG1bAJA4ZA6B/ehysgV0LUIrDHdKgo1KorZq3pK0b/7Z7LJIQ12MNM6aC+Tn6lUZ5w==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + }, + "dependencies": { + "yallist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", + "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=" + } + } + }, + "minizlib": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.0.tgz", + "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==", + "requires": { + "minipass": "^2.2.1" + } + }, "mixin-deep": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", @@ -5685,6 +5714,11 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=" + }, "negotiator": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", @@ -5813,6 +5847,68 @@ } } }, + "nodejs-mobile-gyp": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/nodejs-mobile-gyp/-/nodejs-mobile-gyp-0.2.0.tgz", + "integrity": "sha512-zdeI4UhZUaDbwfOSS7ZgtKzagNb7nUiqV/Es67LKixiSIMSE1+9fwoVu9TVpCPFgizsoxRSxaxtSmZlL0yRmYA==", + "requires": { + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "minimatch": "^3.0.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "2", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^3.1.3", + "which": "1" + }, + "dependencies": { + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "requires": { + "abbrev": "1" + } + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" + }, + "tar": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-3.2.1.tgz", + "integrity": "sha512-ZSzds1E0IqutvMU8HxjMaU8eB7urw2fGwTq88ukDOVuUIh0656l7/P7LiVPxhO5kS4flcRJQk8USG+cghQbTUQ==", + "requires": { + "chownr": "^1.0.1", + "minipass": "^2.0.2", + "minizlib": "^1.0.3", + "mkdirp": "^0.5.0", + "yallist": "^3.0.2" + } + }, + "yallist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", + "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=" + } + } + }, + "nodejs-mobile-react-native": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/nodejs-mobile-react-native/-/nodejs-mobile-react-native-0.3.0.tgz", + "integrity": "sha512-cY0tF27Mf3VbIGA8xz/c6j7WlP1b8sapYfb+SZUbjYxztYH1YFGdjymAQ/s4vtvK0rXf2h1WXMRjRw6nu4jjKA==", + "requires": { + "mkdirp": "^0.5.1", + "ncp": "^2.0.0", + "nodejs-mobile-gyp": "^0.2.0", + "xcode": "^0.9.3" + } + }, "nopt": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", diff --git a/package.json b/package.json index a988aa2..035ca51 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "js-base64": "^2.4.8", "js-joda": "^1.8.2", "moment": "^2.22.2", + "nodejs-mobile-react-native": "^0.3.0", "object-path": "^0.11.4", "obv": "0.0.1", "react": "16.4.1", From 2716decfde9382a867ed71fb78b06d5b22d1c58a Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Tue, 11 Sep 2018 08:41:54 +0200 Subject: [PATCH 03/37] Add comment about node process stdout --- nodejs-assets/nodejs-project/main.js | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/nodejs-assets/nodejs-project/main.js b/nodejs-assets/nodejs-project/main.js index 77ab09d..1dc6cf8 100644 --- a/nodejs-assets/nodejs-project/main.js +++ b/nodejs-assets/nodejs-project/main.js @@ -1,17 +1,15 @@ +// too see stdout / stderr from this process, run +// adb logcat | grep "NODEJS-MOBILE" const rnBridge = require('rn-bridge') -try { - const bcryptjs = require('bcryptjs') +const bcryptjs = require('bcryptjs') - rnBridge.channel.on('message', (msg) => { - msg = JSON.parse(msg) - if (msg.type === 'request-hash') { - const hash = bcryptjs.hashSync(msg.message, 10) - rnBridge.channel.send(JSON.stringify({ - type: 'hash', - message: hash - })) - } - }) -} catch (err) { - rnBridge.channel.send(JSON.stringify(err.message)) -} \ No newline at end of file +rnBridge.channel.on('message', (msg) => { + msg = JSON.parse(msg) + if (msg.type === 'request-hash') { + const hash = bcryptjs.hashSync(msg.message, 10) + rnBridge.channel.send(JSON.stringify({ + type: 'hash', + message: hash + })) + } +}) \ No newline at end of file From 04e43b823d8bbb01c84e0be9e54dedf367dc91cf Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Tue, 11 Sep 2018 18:03:34 +0200 Subject: [PATCH 04/37] Extract schema and open db when called --- db/index.js | 191 +++++++++------------------------------------------ db/schema.js | 140 +++++++++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+), 158 deletions(-) create mode 100644 db/schema.js diff --git a/db/index.js b/db/index.js index fc68de6..5560251 100644 --- a/db/index.js +++ b/db/index.js @@ -11,157 +11,28 @@ import { longAndComplicatedCycleWithCervix, cycleWithTempAndNoCervixShift } from './fixtures' +import { hasEncryptionObservable } from '../local-storage' +import dbSchema from './schema' -const TemperatureSchema = { - name: 'Temperature', - properties: { - value: 'double', - exclude: 'bool', - time: { - type: 'string', - optional: true - }, - note: { - type: 'string', - optional: true - } - } -} - -const BleedingSchema = { - name: 'Bleeding', - properties: { - value: 'int', - exclude: 'bool' - } -} - -const MucusSchema = { - name: 'Mucus', - properties: { - feeling: 'int', - texture: 'int', - value: 'int', - exclude: 'bool' - } -} - -const CervixSchema = { - name: 'Cervix', - properties: { - opening: 'int', - firmness: 'int', - position: {type: 'int', optional: true }, - exclude: 'bool' - } -} - -const NoteSchema = { - name: 'Note', - properties: { - value: 'string' - } -} - -const DesireSchema = { - name: 'Desire', - properties: { - value: 'int' - } -} - -const SexSchema = { - name: 'Sex', - properties: { - solo: { type: 'bool', optional: true }, - partner: { type: 'bool', optional: true }, - condom: { type: 'bool', optional: true }, - pill: { type: 'bool', optional: true }, - iud: { type: 'bool', optional: true }, - patch: { type: 'bool', optional: true }, - ring: { type: 'bool', optional: true }, - implant: { type: 'bool', optional: true }, - other: { type: 'bool', optional: true }, - note: { type: 'string', optional: true } - } -} - -const PainSchema = { - name: 'Pain', - properties: { - cramps: { type: 'bool', optional: true }, - ovulationPain: { type: 'bool', optional: true }, - headache: { type: 'bool', optional: true }, - backache: { type: 'bool', optional: true }, - nausea: { type: 'bool', optional: true }, - tenderBreasts: { type: 'bool', optional: true }, - migraine: { type: 'bool', optional: true }, - other: { type: 'bool', optional: true }, - note: { type: 'string', optional: true } - } -} - -const CycleDaySchema = { - name: 'CycleDay', - primaryKey: 'date', - properties: { - date: 'string', - temperature: { - type: 'Temperature', - optional: true - }, - bleeding: { - type: 'Bleeding', - optional: true - }, - mucus: { - type: 'Mucus', - optional: true - }, - cervix: { - type: 'Cervix', - optional: true - }, - note: { - type: 'Note', - optional: true - }, - desire: { - type: 'Desire', - optional: true - }, - sex: { - type: 'Sex', - optional: true - }, - pain: { - type: 'Pain', - optional: true - } - } -} - +let db const realmConfig = { - schema: [ - CycleDaySchema, - TemperatureSchema, - BleedingSchema, - MucusSchema, - CervixSchema, - NoteSchema, - DesireSchema, - SexSchema, - PainSchema - ], - // we only want this in dev mode - deleteRealmIfMigrationNeeded: true + dbSchema } -let db = new Realm(realmConfig) +export function openDbConnection(key) { + realmConfig.encyptionKey = key + db = new Realm(realmConfig) +} -const bleedingDaysSortedByDate = db.objects('CycleDay').filtered('bleeding != null').sorted('date', true) -const temperatureDaysSortedByDate = db.objects('CycleDay').filtered('temperature != null').sorted('date', true) -const cycleDaysSortedByDate = db.objects('CycleDay').sorted('date', true) +function getBleedingDaysSortedByDate() { + return db.objects('CycleDay').filtered('bleeding != null').sorted('date', true) +} +function getTemperatureDaysSortedByDate() { + return db.objects('CycleDay').filtered('temperature != null').sorted('date', true) +} +function getCycleDaysSortedByDate() { + return db.objects('CycleDay').sorted('date', true) +} function saveSymptom(symptom, cycleDay, val) { db.write(() => { @@ -244,7 +115,7 @@ function deleteAll() { function getPreviousTemperature(cycleDay) { cycleDay.wrappedDate = LocalDate.parse(cycleDay.date) - const winner = temperatureDaysSortedByDate.find(day => { + const winner = getTemperatureDaysSortedByDate().find(day => { const wrappedDate = LocalDate.parse(day.date) return wrappedDate.isBefore(cycleDay.wrappedDate) }) @@ -252,11 +123,6 @@ function getPreviousTemperature(cycleDay) { return winner.temperature.value } -const schema = db.schema.reduce((acc, curr) => { - acc[curr.name] = curr.properties - return acc -}, {}) - function tryToCreateCycleDay(day, i) { try { db.create('CycleDay', day) @@ -267,6 +133,7 @@ function tryToCreateCycleDay(day, i) { } function getAmountOfCycleDays() { + const cycleDaysSortedByDate = getCycleDaysSortedByDate() const amountOfCycleDays = cycleDaysSortedByDate.length if (!amountOfCycleDays) return 0 const earliest = cycleDaysSortedByDate[amountOfCycleDays - 1] @@ -275,6 +142,13 @@ function getAmountOfCycleDays() { return earliestAsLocalDate.until(today, ChronoUnit.DAYS) } +function getSchema() { + return db.schema.reduce((acc, curr) => { + acc[curr.name] = curr.properties + return acc + }, {}) +} + function tryToImportWithDelete(cycleDays) { db.write(() => { db.delete(db.objects('CycleDay')) @@ -292,10 +166,10 @@ function tryToImportWithoutDelete(cycleDays) { }) } -function requestHash() { +function requestHash(pw) { nodejs.channel.send(JSON.stringify({ type: 'request-hash', - message: 'mypassword' + message: pw || 'mypassword' })) } @@ -315,22 +189,23 @@ async function encrypt(hash) { }) realmConfig.enryptionKey = key db = new Realm(realmConfig) + await hasEncryptionObservable.set(true) restart.Restart() } export { saveSymptom, getOrCreateCycleDay, - bleedingDaysSortedByDate, - temperatureDaysSortedByDate, - cycleDaysSortedByDate, fillWithMucusDummyData, fillWithCervixDummyData, + getBleedingDaysSortedByDate, + getTemperatureDaysSortedByDate, + getCycleDaysSortedByDate, deleteAll, getPreviousTemperature, getCycleDay, getAmountOfCycleDays, - schema, + getSchema, tryToImportWithDelete, tryToImportWithoutDelete, requestHash, diff --git a/db/schema.js b/db/schema.js new file mode 100644 index 0000000..8e8792a --- /dev/null +++ b/db/schema.js @@ -0,0 +1,140 @@ +const TemperatureSchema = { + name: 'Temperature', + properties: { + value: 'double', + exclude: 'bool', + time: { + type: 'string', + optional: true + }, + note: { + type: 'string', + optional: true + } + } +} + +const BleedingSchema = { + name: 'Bleeding', + properties: { + value: 'int', + exclude: 'bool' + } +} + +const MucusSchema = { + name: 'Mucus', + properties: { + feeling: 'int', + texture: 'int', + value: 'int', + exclude: 'bool' + } +} + +const CervixSchema = { + name: 'Cervix', + properties: { + opening: 'int', + firmness: 'int', + position: {type: 'int', optional: true }, + exclude: 'bool' + } +} + +const NoteSchema = { + name: 'Note', + properties: { + value: 'string' + } +} + +const DesireSchema = { + name: 'Desire', + properties: { + value: 'int' + } +} + +const SexSchema = { + name: 'Sex', + properties: { + solo: { type: 'bool', optional: true }, + partner: { type: 'bool', optional: true }, + condom: { type: 'bool', optional: true }, + pill: { type: 'bool', optional: true }, + iud: { type: 'bool', optional: true }, + patch: { type: 'bool', optional: true }, + ring: { type: 'bool', optional: true }, + implant: { type: 'bool', optional: true }, + other: { type: 'bool', optional: true }, + note: { type: 'string', optional: true } + } +} + +const PainSchema = { + name: 'Pain', + properties: { + cramps: { type: 'bool', optional: true }, + ovulationPain: { type: 'bool', optional: true }, + headache: { type: 'bool', optional: true }, + backache: { type: 'bool', optional: true }, + nausea: { type: 'bool', optional: true }, + tenderBreasts: { type: 'bool', optional: true }, + migraine: { type: 'bool', optional: true }, + other: { type: 'bool', optional: true }, + note: { type: 'string', optional: true } + } +} + +const CycleDaySchema = { + name: 'CycleDay', + primaryKey: 'date', + properties: { + date: 'string', + temperature: { + type: 'Temperature', + optional: true + }, + bleeding: { + type: 'Bleeding', + optional: true + }, + mucus: { + type: 'Mucus', + optional: true + }, + cervix: { + type: 'Cervix', + optional: true + }, + note: { + type: 'Note', + optional: true + }, + desire: { + type: 'Desire', + optional: true + }, + sex: { + type: 'Sex', + optional: true + }, + pain: { + type: 'Pain', + optional: true + } + } +} + +export default [ + CycleDaySchema, + TemperatureSchema, + BleedingSchema, + MucusSchema, + CervixSchema, + NoteSchema, + DesireSchema, + SexSchema, + PainSchema +] \ No newline at end of file From 3e3cef87695e47c63d14c435322cbaf74e687f5a Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Tue, 11 Sep 2018 18:06:19 +0200 Subject: [PATCH 05/37] Only use db functions when components are actually mounted --- components/calendar.js | 6 ++++-- components/chart/chart.js | 7 ++++--- components/chart/day-column.js | 7 +++++-- components/home.js | 9 ++++----- lib/cycle.js | 4 ++-- lib/sympto-adapter.js | 12 ++++++------ 6 files changed, 25 insertions(+), 20 deletions(-) diff --git a/components/calendar.js b/components/calendar.js index 95e8bf8..74b754b 100644 --- a/components/calendar.js +++ b/components/calendar.js @@ -1,13 +1,15 @@ import React, { Component } from 'react' import { CalendarList } from 'react-native-calendars' import {LocalDate} from 'js-joda' -import { getOrCreateCycleDay, bleedingDaysSortedByDate } from '../db' +import { getOrCreateCycleDay, getBleedingDaysSortedByDate } from '../db' import cycleModule from '../lib/cycle' import {shadesOfRed} from '../styles/index' import styles from '../styles/index' + export default class CalendarView extends Component { constructor(props) { + const bleedingDaysSortedByDate = getBleedingDaysSortedByDate() super(props) const predictedMenses = cycleModule().getPredictedMenses() this.state = { @@ -31,7 +33,7 @@ export default class CalendarView extends Component { } componentWillUnmount() { - bleedingDaysSortedByDate.removeListener(this.setStateWithCalFormattedDays) + getBleedingDaysSortedByDate().removeListener(this.setStateWithCalFormattedDays) } passDateToDayView = (result) => { diff --git a/components/chart/chart.js b/components/chart/chart.js index a4cc7d1..f6da539 100644 --- a/components/chart/chart.js +++ b/components/chart/chart.js @@ -5,7 +5,7 @@ import { LocalDate } from 'js-joda' import { makeYAxisLabels, normalizeToScale, makeHorizontalGrid } from './y-axis' import nfpLines from './nfp-lines' import DayColumn from './day-column' -import { getCycleDay, cycleDaysSortedByDate, getAmountOfCycleDays } from '../../db' +import { getCycleDay, getCycleDaysSortedByDate, getAmountOfCycleDays } from '../../db' import styles from './styles' import { scaleObservable } from '../../local-storage' import config from '../../config' @@ -25,6 +25,7 @@ export default class CycleChart extends Component { /> ) } + this.cycleDaysSortedByDate = getCycleDaysSortedByDate() } onLayout = ({ nativeEvent }) => { @@ -35,12 +36,12 @@ export default class CycleChart extends Component { this.setState({ columns: this.makeColumnInfo(nfpLines(height)) }) } - cycleDaysSortedByDate.addListener(this.reCalculateChartInfo) + this.cycleDaysSortedByDate.addListener(this.reCalculateChartInfo) this.removeObvListener = scaleObservable(this.reCalculateChartInfo, false) } componentWillUnmount() { - cycleDaysSortedByDate.removeListener(this.reCalculateChartInfo) + this.cycleDaysSortedByDate.removeListener(this.reCalculateChartInfo) this.removeObvListener() } diff --git a/components/chart/day-column.js b/components/chart/day-column.js index 9b010fd..e541ab0 100644 --- a/components/chart/day-column.js +++ b/components/chart/day-column.js @@ -9,10 +9,13 @@ import { getOrCreateCycleDay } from '../../db' import cycleModule from '../../lib/cycle' import DotAndLine from './dot-and-line' -const getCycleDayNumber = cycleModule().getCycleDayNumber const label = styles.column.label export default class DayColumn extends Component { + constructor() { + super() + this.getCycleDayNumber = cycleModule().getCycleDayNumber + } passDateToDayView(dateString) { const cycleDay = getOrCreateCycleDay(dateString) this.props.navigate('CycleDay', { cycleDay }) @@ -68,7 +71,7 @@ export default class DayColumn extends Component { ) } - const cycleDayNumber = getCycleDayNumber(dateString) + const cycleDayNumber = this.getCycleDayNumber(dateString) const shortDate = dateString.split('-').slice(1).join('-') const cycleDayLabel = ( diff --git a/components/home.js b/components/home.js index b483274..b7156d9 100644 --- a/components/home.js +++ b/components/home.js @@ -8,14 +8,13 @@ import { import { LocalDate, ChronoUnit } from 'js-joda' import styles from '../styles/index' import cycleModule from '../lib/cycle' -import { requestHash, getOrCreateCycleDay, bleedingDaysSortedByDate, fillWithMucusDummyData, fillWithCervixDummyData, deleteAll } from '../db' +import { requestHash, getOrCreateCycleDay, getBleedingDaysSortedByDate, fillWithMucusDummyData, fillWithCervixDummyData, deleteAll } from '../db' import {bleedingPrediction as labels} from './labels' -const getCycleDayNumber = cycleModule().getCycleDayNumber - export default class Home extends Component { constructor(props) { super(props) + const getCycleDayNumber = cycleModule().getCycleDayNumber this.todayDateString = LocalDate.now().toString() const cycleDayNumber = getCycleDayNumber(this.todayDateString) @@ -34,11 +33,11 @@ export default class Home extends Component { } })(this) - bleedingDaysSortedByDate.addListener(this.setStateWithCurrentText) + getBleedingDaysSortedByDate().addListener(this.setStateWithCurrentText) } componentWillUnmount() { - bleedingDaysSortedByDate.removeListener(this.setStateWithCurrentText) + getBleedingDaysSortedByDate().removeListener(this.setStateWithCurrentText) } passTodayToDayView() { diff --git a/lib/cycle.js b/lib/cycle.js index da91e2d..7deb02e 100644 --- a/lib/cycle.js +++ b/lib/cycle.js @@ -13,8 +13,8 @@ export default function config(opts) { if (!opts) { // we only want to require (and run) the db module // when not running the tests - bleedingDaysSortedByDate = require('../db').bleedingDaysSortedByDate - cycleDaysSortedByDate = require('../db').cycleDaysSortedByDate + bleedingDaysSortedByDate = require('../db').getBleedingDaysSortedByDate() + cycleDaysSortedByDate = require('../db').getCycleDaysSortedByDate() maxBreakInBleeding = 1 maxCycleLength = 99 minCyclesForPrediction = 3 diff --git a/lib/sympto-adapter.js b/lib/sympto-adapter.js index b6ab217..363538e 100644 --- a/lib/sympto-adapter.js +++ b/lib/sympto-adapter.js @@ -2,12 +2,6 @@ import getFertilityStatus from './sympto' import cycleModule from './cycle' import { fertilityStatus } from '../components/cycle-day/labels/labels' -const { - getCycleForDay, - getCyclesBefore, - getPreviousCycle -} = cycleModule() - export function getFertilityStatusStringForDay(dateString) { const status = getCycleStatusForDay(dateString) if (!status) return fertilityStatus.unknown @@ -28,6 +22,12 @@ export function getFertilityStatusStringForDay(dateString) { } export function getCycleStatusForDay(dateString) { + const { + getCycleForDay, + getCyclesBefore, + getPreviousCycle + } = cycleModule() + const cycle = getCycleForDay(dateString) if (!cycle) return null From 3d61459f30f9d32789f35f610edf9f7120d9dee8 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Tue, 11 Sep 2018 18:06:54 +0200 Subject: [PATCH 06/37] Introduce password prompt page and save encryption flag --- components/password-prompt.js | 68 ++++++++++++++++++++++++++++ index.js | 4 +- local-storage/index.js | 22 +++++---- nodejs-assets/nodejs-project/main.js | 9 +++- 4 files changed, 90 insertions(+), 13 deletions(-) create mode 100644 components/password-prompt.js diff --git a/components/password-prompt.js b/components/password-prompt.js new file mode 100644 index 0000000..c7d0644 --- /dev/null +++ b/components/password-prompt.js @@ -0,0 +1,68 @@ +import React, { Component } from 'react' +import { View, TextInput, TouchableOpacity } from 'react-native' +import nodejs from 'nodejs-mobile-react-native' +import AppText from './app-text' +import { hasEncryptionObservable } from '../local-storage' +import styles from '../styles' +import labels from './labels' +import { openDbConnection } from '../db' +import App from './app' + +export default class PasswordPrompt extends Component { + constructor() { + super() + this.state = {} + hasEncryptionObservable.once((hasEncryption) => { + if (hasEncryption) { + this.setState({showPasswordPrompt: true}) + } else { + openDbConnection() + this.setState({showApp: true}) + } + }) + nodejs.channel.addListener( + 'message', + msg => { + msg = JSON.parse(msg) + if (msg.type === 'password-check-result') { + if (msg.message) { + this.setState({showApp: true}) + } else { + this.setState({wrongPassword: true}) + } + } + }, + this + ) + } + + render() { + return ( + + {this.state.showApp ? + + : + + {this.state.showPasswordPrompt && + + this.setState({password: val})} + /> + { + + }} + style={styles.settingsButton}> + + {labels.export.button} + + + {this.state.wrongPassword && Wrong PAssword!} + + } + + } + + ) + } +} \ No newline at end of file diff --git a/index.js b/index.js index 25e2616..b17ba73 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,4 @@ import { AppRegistry } from 'react-native' -import App from './components/app' +import PasswordPrompt from './components/password-prompt' -AppRegistry.registerComponent('home', () => App) \ No newline at end of file +AppRegistry.registerComponent('home', () => PasswordPrompt) \ No newline at end of file diff --git a/local-storage/index.js b/local-storage/index.js index 83f50e5..5fffb42 100644 --- a/local-storage/index.js +++ b/local-storage/index.js @@ -19,12 +19,24 @@ scaleObservable((scale) => { } }) +export async function saveTempScale(scale) { + await AsyncStorage.setItem('tempScale', JSON.stringify(scale)) + scaleObservable.set(scale) +} export const tempReminderObservable = Observable() setObvWithInitValue('tempReminder', tempReminderObservable, { enabled: false }) +export async function saveTempReminder(reminder) { + await AsyncStorage.setItem('tempReminder', JSON.stringify(reminder)) + tempReminderObservable.set(reminder) +} + +export const hasEncryptionObservable = Observable() +setObvWithInitValue('hasEncryption', hasEncryptionObservable, false) + async function setObvWithInitValue(key, obv, defaultValue) { const result = await AsyncStorage.getItem(key) let value @@ -34,14 +46,4 @@ async function setObvWithInitValue(key, obv, defaultValue) { value = defaultValue } obv.set(value) -} - -export async function saveTempScale(scale) { - await AsyncStorage.setItem('tempScale', JSON.stringify(scale)) - scaleObservable.set(scale) -} - -export async function saveTempReminder(reminder) { - await AsyncStorage.setItem('tempReminder', JSON.stringify(reminder)) - tempReminderObservable.set(reminder) } \ No newline at end of file diff --git a/nodejs-assets/nodejs-project/main.js b/nodejs-assets/nodejs-project/main.js index 1dc6cf8..465e56c 100644 --- a/nodejs-assets/nodejs-project/main.js +++ b/nodejs-assets/nodejs-project/main.js @@ -5,11 +5,18 @@ const bcryptjs = require('bcryptjs') rnBridge.channel.on('message', (msg) => { msg = JSON.parse(msg) - if (msg.type === 'request-hash') { + if (msg.type === 'request-password-hash') { const hash = bcryptjs.hashSync(msg.message, 10) rnBridge.channel.send(JSON.stringify({ type: 'hash', message: hash })) + } else if (msg.type === 'request-SHA512') { + // do the thing + } else if (msg.type === 'check-password') { + rnBridge.channel.send(JSON.stringify({ + type: 'password-check-result', + message: bcryptjs.compareSync(msg.message.password, msg.message.hash) + })) } }) \ No newline at end of file From 34648a3d89b23e62439e45585ef8efc30e766266 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Wed, 12 Sep 2018 08:51:36 +0200 Subject: [PATCH 07/37] Take user password and try to open db --- components/app.js | 11 ------ components/labels.js | 4 +++ components/password-prompt.js | 36 ++++++++++++------- db/index.js | 19 +++++----- local-storage/index.js | 5 +++ nodejs-assets/nodejs-project/main.js | 20 +++++------ .../nodejs-project/package-lock.json | 11 ------ nodejs-assets/nodejs-project/package.json | 4 +-- 8 files changed, 53 insertions(+), 57 deletions(-) delete mode 100644 nodejs-assets/nodejs-project/package-lock.json diff --git a/components/app.js b/components/app.js index ba7e2c7..ba58437 100644 --- a/components/app.js +++ b/components/app.js @@ -1,6 +1,5 @@ import React, { Component } from 'react' import { View, BackHandler } from 'react-native' -import nodejs from 'nodejs-mobile-react-native' import Header from './header' import Menu from './menu' import Home from './home' @@ -12,7 +11,6 @@ import Settings from './settings' import Stats from './stats' import {headerTitles as titles} from './labels' import setupNotifications from '../lib/notifications' -import { encrypt } from '../db' const isSymptomView = name => Object.keys(symptomViews).indexOf(name) > -1 @@ -24,15 +22,6 @@ export default class App extends Component { } this.backHandler = BackHandler.addEventListener('hardwareBackPress', this.handleBackButtonPress) setupNotifications(this.navigate) - nodejs.start('main.js') - nodejs.channel.addListener( - 'message', - msg => { - msg = JSON.parse(msg) - encrypt(msg.message) - }, - this - ) } componentWillUnmount() { diff --git a/components/labels.js b/components/labels.js index c61a939..5f1e749 100644 --- a/components/labels.js +++ b/components/labels.js @@ -90,4 +90,8 @@ export const bleedingPrediction = { predictionStarted1DayLeft: 'Your period is likely to start today or tomorrow.', predictionStartedNoDaysLeft: 'Your period is likely to start today.', predictionInPast: (startDate, endDate) => `Based on your documented data, your period was likely to start between ${startDate} and ${endDate}.` +} + +export const passwordPrompt = { + title: 'Log in' } \ No newline at end of file diff --git a/components/password-prompt.js b/components/password-prompt.js index c7d0644..4453af6 100644 --- a/components/password-prompt.js +++ b/components/password-prompt.js @@ -1,11 +1,11 @@ import React, { Component } from 'react' import { View, TextInput, TouchableOpacity } from 'react-native' import nodejs from 'nodejs-mobile-react-native' -import AppText from './app-text' +import { AppText } from './app-text' import { hasEncryptionObservable } from '../local-storage' import styles from '../styles' -import labels from './labels' -import { openDbConnection } from '../db' +import { passwordPrompt } from './labels' +import { openDbConnection, requestHash } from '../db' import App from './app' export default class PasswordPrompt extends Component { @@ -16,18 +16,24 @@ export default class PasswordPrompt extends Component { if (hasEncryption) { this.setState({showPasswordPrompt: true}) } else { - openDbConnection() + openDbConnection('something-wrong') this.setState({showApp: true}) } }) + nodejs.start('main.js') nodejs.channel.addListener( 'message', - msg => { + async msg => { msg = JSON.parse(msg) - if (msg.type === 'password-check-result') { - if (msg.message) { - this.setState({showApp: true}) - } else { + if (msg.type === 'sha512') { + const key = new Int8Array(64) + for (let i = 0; i < msg.message.length; i++) { + key[i] = msg.message.charCodeAt(i) + } + try { + await openDbConnection(key) + } catch(err) { + console.log(err) this.setState({wrongPassword: true}) } } @@ -47,14 +53,20 @@ export default class PasswordPrompt extends Component { this.setState({password: val})} + style={{ + borderWidth: 1, + borderColor: 'grey', + margin: 5 + }} /> { - + requestHash(this.state.password) }} - style={styles.settingsButton}> + > - {labels.export.button} + { passwordPrompt.title } {this.state.wrongPassword && Wrong PAssword!} diff --git a/db/index.js b/db/index.js index 5560251..d68d46b 100644 --- a/db/index.js +++ b/db/index.js @@ -11,17 +11,17 @@ import { longAndComplicatedCycleWithCervix, cycleWithTempAndNoCervixShift } from './fixtures' -import { hasEncryptionObservable } from '../local-storage' +import { saveEncryptionFlag } from '../local-storage' import dbSchema from './schema' let db const realmConfig = { - dbSchema + schema: dbSchema } -export function openDbConnection(key) { - realmConfig.encyptionKey = key - db = new Realm(realmConfig) +export async function openDbConnection(key) { + realmConfig.encryptionKey = key + db = await Realm.open(realmConfig) } function getBleedingDaysSortedByDate() { @@ -167,8 +167,9 @@ function tryToImportWithoutDelete(cycleDays) { } function requestHash(pw) { + console.log('requesting hash') nodejs.channel.send(JSON.stringify({ - type: 'request-hash', + type: 'request-SHA512', message: pw || 'mypassword' })) } @@ -179,6 +180,8 @@ async function encrypt(hash) { dir.pop() dir.push('copied.realm') const copyPath = dir.join('/') + const exists = await fs.exists(copyPath) + if (exists) await fs.unlink(copyPath) db.writeCopyTo(copyPath) db.close() await fs.unlink(oldPath) @@ -187,9 +190,9 @@ async function encrypt(hash) { if (isNaN(code)) code = 0 return code }) - realmConfig.enryptionKey = key + realmConfig.encryptionKey = key db = new Realm(realmConfig) - await hasEncryptionObservable.set(true) + await saveEncryptionFlag(true) restart.Restart() } diff --git a/local-storage/index.js b/local-storage/index.js index 5fffb42..d41d4d7 100644 --- a/local-storage/index.js +++ b/local-storage/index.js @@ -37,6 +37,11 @@ export async function saveTempReminder(reminder) { export const hasEncryptionObservable = Observable() setObvWithInitValue('hasEncryption', hasEncryptionObservable, false) +export async function saveEncryptionFlag(bool) { + await AsyncStorage.setItem('hasEncryption', JSON.stringify(bool)) + hasEncryptionObservable.set(bool) +} + async function setObvWithInitValue(key, obv, defaultValue) { const result = await AsyncStorage.getItem(key) let value diff --git a/nodejs-assets/nodejs-project/main.js b/nodejs-assets/nodejs-project/main.js index 465e56c..f986f42 100644 --- a/nodejs-assets/nodejs-project/main.js +++ b/nodejs-assets/nodejs-project/main.js @@ -1,22 +1,18 @@ // too see stdout / stderr from this process, run // adb logcat | grep "NODEJS-MOBILE" const rnBridge = require('rn-bridge') -const bcryptjs = require('bcryptjs') +const crypto = require('crypto') rnBridge.channel.on('message', (msg) => { msg = JSON.parse(msg) - if (msg.type === 'request-password-hash') { - const hash = bcryptjs.hashSync(msg.message, 10) + if (msg.type === 'request-SHA512') { + const hash = crypto.createHash('sha256') + hash.update(msg.message) + const result = hash.digest('hex') + console.log(result) rnBridge.channel.send(JSON.stringify({ - type: 'hash', - message: hash - })) - } else if (msg.type === 'request-SHA512') { - // do the thing - } else if (msg.type === 'check-password') { - rnBridge.channel.send(JSON.stringify({ - type: 'password-check-result', - message: bcryptjs.compareSync(msg.message.password, msg.message.hash) + type: 'sha512', + message: result })) } }) \ No newline at end of file diff --git a/nodejs-assets/nodejs-project/package-lock.json b/nodejs-assets/nodejs-project/package-lock.json deleted file mode 100644 index 4a3b908..0000000 --- a/nodejs-assets/nodejs-project/package-lock.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "requires": true, - "lockfileVersion": 1, - "dependencies": { - "bcryptjs": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", - "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" - } - } -} diff --git a/nodejs-assets/nodejs-project/package.json b/nodejs-assets/nodejs-project/package.json index a51d26f..3310508 100644 --- a/nodejs-assets/nodejs-project/package.json +++ b/nodejs-assets/nodejs-project/package.json @@ -1,6 +1,4 @@ { - "dependencies": { - "bcryptjs": "^2.4.3" - }, + "dependencies": {}, "main": "main.js" } From f2f0278a1cb5fb61043e3ad7ef3e7d020fabbcf4 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Thu, 13 Sep 2018 16:52:54 +0200 Subject: [PATCH 08/37] Actually use SHA512, not 256 --- components/password-prompt.js | 8 +++++--- nodejs-assets/nodejs-project/main.js | 3 +-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/components/password-prompt.js b/components/password-prompt.js index 4453af6..5ecc5cb 100644 --- a/components/password-prompt.js +++ b/components/password-prompt.js @@ -26,9 +26,11 @@ export default class PasswordPrompt extends Component { async msg => { msg = JSON.parse(msg) if (msg.type === 'sha512') { - const key = new Int8Array(64) - for (let i = 0; i < msg.message.length; i++) { - key[i] = msg.message.charCodeAt(i) + const hash = msg.message + const key = new Uint8Array(64) + for (let i = 0; i < key.length; i++) { + const twoDigitHex = hash.slice(i * 2, i * 2 + 2) + key[i] = parseInt(twoDigitHex, 16) } try { await openDbConnection(key) diff --git a/nodejs-assets/nodejs-project/main.js b/nodejs-assets/nodejs-project/main.js index f986f42..20227c7 100644 --- a/nodejs-assets/nodejs-project/main.js +++ b/nodejs-assets/nodejs-project/main.js @@ -6,10 +6,9 @@ const crypto = require('crypto') rnBridge.channel.on('message', (msg) => { msg = JSON.parse(msg) if (msg.type === 'request-SHA512') { - const hash = crypto.createHash('sha256') + const hash = crypto.createHash('sha512') hash.update(msg.message) const result = hash.digest('hex') - console.log(result) rnBridge.channel.send(JSON.stringify({ type: 'sha512', message: result From b82b2d625ac2cb5b71d8a5a2faf31adfc51041c9 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Thu, 13 Sep 2018 17:07:10 +0200 Subject: [PATCH 09/37] Use SHA512 for encryption too --- components/home.js | 24 +++++++++++++++++++- components/password-prompt.js | 41 ++++++++++++++++++++--------------- db/index.js | 9 ++------ 3 files changed, 49 insertions(+), 25 deletions(-) diff --git a/components/home.js b/components/home.js index b7156d9..799f845 100644 --- a/components/home.js +++ b/components/home.js @@ -6,9 +6,10 @@ import { ScrollView } from 'react-native' import { LocalDate, ChronoUnit } from 'js-joda' +import nodejs from 'nodejs-mobile-react-native' import styles from '../styles/index' import cycleModule from '../lib/cycle' -import { requestHash, getOrCreateCycleDay, getBleedingDaysSortedByDate, fillWithMucusDummyData, fillWithCervixDummyData, deleteAll } from '../db' +import { requestHash, getOrCreateCycleDay, getBleedingDaysSortedByDate, fillWithMucusDummyData, fillWithCervixDummyData, deleteAll, encryptAndRestartApp } from '../db' import {bleedingPrediction as labels} from './labels' export default class Home extends Component { @@ -34,10 +35,31 @@ export default class Home extends Component { })(this) getBleedingDaysSortedByDate().addListener(this.setStateWithCurrentText) + + this.startEncryption = async (msg) => { + msg = JSON.parse(msg) + if (msg.type === 'sha512') { + const hash = msg.message + const key = new Uint8Array(64) + for (let i = 0; i < key.length; i++) { + const twoDigitHex = hash.slice(i * 2, i * 2 + 2) + key[i] = parseInt(twoDigitHex, 16) + } + encryptAndRestartApp(key) + } + } + + nodejs.start('main.js') + nodejs.channel.addListener( + 'message', + this.startEncryption, + this + ) } componentWillUnmount() { getBleedingDaysSortedByDate().removeListener(this.setStateWithCurrentText) + nodejs.channel.removeListener('message', this.startEncryption) } passTodayToDayView() { diff --git a/components/password-prompt.js b/components/password-prompt.js index 5ecc5cb..f3dcc97 100644 --- a/components/password-prompt.js +++ b/components/password-prompt.js @@ -20,30 +20,37 @@ export default class PasswordPrompt extends Component { this.setState({showApp: true}) } }) + + this.tryToOpenDb = async (msg) => { + msg = JSON.parse(msg) + if (msg.type === 'sha512') { + const hash = msg.message + const key = new Uint8Array(64) + for (let i = 0; i < key.length; i++) { + const twoDigitHex = hash.slice(i * 2, i * 2 + 2) + key[i] = parseInt(twoDigitHex, 16) + } + try { + await openDbConnection(key) + } catch (err) { + console.log(err) + this.setState({ wrongPassword: true }) + } + } + } + nodejs.start('main.js') nodejs.channel.addListener( 'message', - async msg => { - msg = JSON.parse(msg) - if (msg.type === 'sha512') { - const hash = msg.message - const key = new Uint8Array(64) - for (let i = 0; i < key.length; i++) { - const twoDigitHex = hash.slice(i * 2, i * 2 + 2) - key[i] = parseInt(twoDigitHex, 16) - } - try { - await openDbConnection(key) - } catch(err) { - console.log(err) - this.setState({wrongPassword: true}) - } - } - }, + this.tryToOpenDb, this ) } + componentWillUnmount() { + nodejs.channel.removeListener('message', this.tryToOpenDb) + } + render() { return ( diff --git a/db/index.js b/db/index.js index d68d46b..74a7852 100644 --- a/db/index.js +++ b/db/index.js @@ -174,7 +174,7 @@ function requestHash(pw) { })) } -async function encrypt(hash) { +async function encryptAndRestartApp(key) { const oldPath = db.path const dir = db.path.split('/') dir.pop() @@ -185,11 +185,6 @@ async function encrypt(hash) { db.writeCopyTo(copyPath) db.close() await fs.unlink(oldPath) - const key = new Array(64).map((_, i) => { - let code = hash.charCodeAt(i) - if (isNaN(code)) code = 0 - return code - }) realmConfig.encryptionKey = key db = new Realm(realmConfig) await saveEncryptionFlag(true) @@ -212,5 +207,5 @@ export { tryToImportWithDelete, tryToImportWithoutDelete, requestHash, - encrypt + encryptAndRestartApp } From 3c1653bc8c710d4f4712ef73ca2daf6e7dacbd44 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Thu, 13 Sep 2018 17:28:28 +0200 Subject: [PATCH 10/37] Add db deletion button for when things go wrong --- components/password-prompt.js | 14 +++++++++++++- db/index.js | 12 +++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/components/password-prompt.js b/components/password-prompt.js index f3dcc97..796e072 100644 --- a/components/password-prompt.js +++ b/components/password-prompt.js @@ -5,7 +5,7 @@ import { AppText } from './app-text' import { hasEncryptionObservable } from '../local-storage' import styles from '../styles' import { passwordPrompt } from './labels' -import { openDbConnection, requestHash } from '../db' +import { openDbConnection, requestHash, deleteDbAndOpenNew } from '../db' import App from './app' export default class PasswordPrompt extends Component { @@ -32,6 +32,7 @@ export default class PasswordPrompt extends Component { } try { await openDbConnection(key) + this.setState({showApp: true}) } catch (err) { console.log(err) this.setState({ wrongPassword: true }) @@ -79,6 +80,17 @@ export default class PasswordPrompt extends Component { {this.state.wrongPassword && Wrong PAssword!} + { + await deleteDbAndOpenNew() + this.setState({showApp: true}) + }} + > + + {'Delete old db and make unencrypted new'} + + } diff --git a/db/index.js b/db/index.js index 74a7852..fc096a9 100644 --- a/db/index.js +++ b/db/index.js @@ -20,7 +20,7 @@ const realmConfig = { } export async function openDbConnection(key) { - realmConfig.encryptionKey = key + if(key) realmConfig.encryptionKey = key db = await Realm.open(realmConfig) } @@ -167,7 +167,6 @@ function tryToImportWithoutDelete(cycleDays) { } function requestHash(pw) { - console.log('requesting hash') nodejs.channel.send(JSON.stringify({ type: 'request-SHA512', message: pw || 'mypassword' @@ -191,6 +190,12 @@ async function encryptAndRestartApp(key) { restart.Restart() } +async function deleteDbAndOpenNew() { + const exists = await fs.exists(Realm.defaultPath) + if (exists) await fs.unlink(Realm.defaultPath) + await openDbConnection() +} + export { saveSymptom, getOrCreateCycleDay, @@ -207,5 +212,6 @@ export { tryToImportWithDelete, tryToImportWithoutDelete, requestHash, - encryptAndRestartApp + encryptAndRestartApp, + deleteDbAndOpenNew } From 88fc3cad091becfe583f1eb912710db609140112 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Thu, 13 Sep 2018 19:05:06 +0200 Subject: [PATCH 11/37] Pull apart settings module --- components/settings.js | 283 -------------------- components/settings/alert-error.js | 6 + components/settings/export-dialog.js | 31 +++ components/settings/import-dialog.js | 58 ++++ components/settings/index.js | 61 +++++ components/settings/temp-reminder-picker.js | 83 ++++++ components/settings/temp-slider.js | 71 +++++ lib/import-export/export-to-csv.js | 3 +- lib/import-export/get-csv-column-names.js | 3 +- lib/import-export/import-from-csv.js | 4 +- 10 files changed, 317 insertions(+), 286 deletions(-) delete mode 100644 components/settings.js create mode 100644 components/settings/alert-error.js create mode 100644 components/settings/export-dialog.js create mode 100644 components/settings/import-dialog.js create mode 100644 components/settings/index.js create mode 100644 components/settings/temp-reminder-picker.js create mode 100644 components/settings/temp-slider.js 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)) From b153f8d0fffdfbd06681ce837be7f888366a4c57 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Thu, 13 Sep 2018 20:42:24 +0200 Subject: [PATCH 12/37] Add password setting and check password before deleting it --- components/labels.js | 6 ++ components/password-prompt.js | 38 +++++------ components/settings/index.js | 4 ++ components/settings/password-setting.js | 85 +++++++++++++++++++++++++ db/index.js | 40 ++++++++++-- 5 files changed, 144 insertions(+), 29 deletions(-) create mode 100644 components/settings/password-setting.js diff --git a/components/labels.js b/components/labels.js index 5f1e749..7ddbae1 100644 --- a/components/labels.js +++ b/components/labels.js @@ -48,6 +48,12 @@ export const settings = { noTimeSet: 'Set a time for a daily reminder to take your temperature', timeSet: time => `Daily reminder set for ${time}`, notification: 'Record your morning temperature' + }, + passwordSettings: { + title: 'App password', + explainerDisabled: "Encrypt the app's database with a password. You have to enter the password every time the app is started.", + explainerEnabled: "Your app's data is encrypted with your password.", + deletePassword: "Delete password" } } diff --git a/components/password-prompt.js b/components/password-prompt.js index 796e072..e21dba6 100644 --- a/components/password-prompt.js +++ b/components/password-prompt.js @@ -5,7 +5,7 @@ import { AppText } from './app-text' import { hasEncryptionObservable } from '../local-storage' import styles from '../styles' import { passwordPrompt } from './labels' -import { openDbConnection, requestHash, deleteDbAndOpenNew } from '../db' +import { openDbConnection, requestHash, deleteDbAndOpenNew, openDb } from '../db' import App from './app' export default class PasswordPrompt extends Component { @@ -21,35 +21,27 @@ export default class PasswordPrompt extends Component { } }) - this.tryToOpenDb = async (msg) => { - msg = JSON.parse(msg) - if (msg.type === 'sha512') { - const hash = msg.message - const key = new Uint8Array(64) - for (let i = 0; i < key.length; i++) { - const twoDigitHex = hash.slice(i * 2, i * 2 + 2) - key[i] = parseInt(twoDigitHex, 16) - } - try { - await openDbConnection(key) - this.setState({showApp: true}) - } catch (err) { - console.log(err) - this.setState({ wrongPassword: true }) - } - } - } - nodejs.start('main.js') nodejs.channel.addListener( 'message', - this.tryToOpenDb, + this.passHashToDb, this ) } + passHashToDb = async msg => { + msg = JSON.parse(msg) + if (msg.type != 'sha512') return + try { + await openDb({hash: msg.message, persistConnection: true }) + this.setState({ showApp: true }) + } catch (err) { + this.setState({ wrongPassword: true }) + } + } + componentWillUnmount() { - nodejs.channel.removeListener('message', this.tryToOpenDb) + nodejs.channel.removeListener('message', this.passHashToDb) } render() { @@ -71,7 +63,7 @@ export default class PasswordPrompt extends Component { /> { + onPress={() => { requestHash(this.state.password) }} > diff --git a/components/settings/index.js b/components/settings/index.js index 5e729be..c52f3ae 100644 --- a/components/settings/index.js +++ b/components/settings/index.js @@ -11,6 +11,7 @@ import TempReminderPicker from './temp-reminder-picker' import TempSlider from './temp-slider' import openImportDialogAndImport from './import-dialog' import openShareDialogAndExport from './export-dialog' +import PasswordSetting from './password-setting' export default class Settings extends Component { constructor(props) { @@ -29,6 +30,9 @@ export default class Settings extends Component { {labels.tempScale.segmentExplainer} + + + {labels.export.button} diff --git a/components/settings/password-setting.js b/components/settings/password-setting.js new file mode 100644 index 0000000..9fe820b --- /dev/null +++ b/components/settings/password-setting.js @@ -0,0 +1,85 @@ +import React, { Component } from 'react' +import { + View, + TouchableOpacity, + TextInput +} from 'react-native' +import nodejs from 'nodejs-mobile-react-native' +import { AppText } from '../app-text' +import { + hasEncryptionObservable +} from '../../local-storage' +import styles from '../../styles/index' +import { settings as labels } from '../labels' +import { requestHash, openDb } from '../../db' + +export default class PasswordSetting extends Component { + constructor(props) { + super(props) + this.state = { + enabled: hasEncryptionObservable.value + } + + nodejs.start('main.js') + nodejs.channel.addListener( + 'message', + this.passHashToDb, + this + ) + } + + componentWillUnmount() { + nodejs.channel.removeListener('message', this.passHashToDb) + } + + passHashToDb = async (msg) => { + msg = JSON.parse(msg) + if (msg.type != 'sha512') return + try { + await openDb({ hash: msg.message, persistConnection: false }) + this.setState({ + wrongPassword: false, + enterOldPassword: false + }) + } catch (err) { + this.setState({wrongPassword: true}) + } + } + + render() { + return ( + + + {labels.passwordSettings.title} + + {this.state.enabled ? + {labels.passwordSettings.explainerEnabled} + : + {labels.passwordSettings.explainerDisabled} + } + {this.state.enterOldPassword && + this.setState({ oldPassword: val })} + /> + } + {this.state.wrongPassword && Wrong PAssword!} + { + if (!this.state.enterOldPassword) { + this.setState({ enterOldPassword: true }) + } else { + requestHash(this.state.oldPassword) + } + }} + style={styles.settingsButton}> + + {labels.passwordSettings.deletePassword} + + + + ) + } +} \ No newline at end of file diff --git a/db/index.js b/db/index.js index fc096a9..ceac742 100644 --- a/db/index.js +++ b/db/index.js @@ -19,11 +19,6 @@ const realmConfig = { schema: dbSchema } -export async function openDbConnection(key) { - if(key) realmConfig.encryptionKey = key - db = await Realm.open(realmConfig) -} - function getBleedingDaysSortedByDate() { return db.objects('CycleDay').filtered('bleeding != null').sorted('date', true) } @@ -173,6 +168,22 @@ function requestHash(pw) { })) } +export async function openDb ({ hash, persistConnection }) { + if (hash) { + const key = new Uint8Array(64) + for (let i = 0; i < key.length; i++) { + const twoDigitHex = hash.slice(i * 2, i * 2 + 2) + key[i] = parseInt(twoDigitHex, 16) + } + + realmConfig.encryptionKey = key + } + + const connection = await Realm.open(realmConfig) + + if (persistConnection) db = connection +} + async function encryptAndRestartApp(key) { const oldPath = db.path const dir = db.path.split('/') @@ -190,10 +201,27 @@ async function encryptAndRestartApp(key) { restart.Restart() } +export async function removeDbEncryptionAndRestartApp(key) { + const oldPath = db.path + const dir = db.path.split('/') + dir.pop() + dir.push('copied.realm') + const copyPath = dir.join('/') + const exists = await fs.exists(copyPath) + if (exists) await fs.unlink(copyPath) + db.writeCopyTo(copyPath) + db.close() + await fs.unlink(oldPath) + realmConfig.encryptionKey = key + db = new Realm(realmConfig) + await saveEncryptionFlag(true) + restart.Restart() +} + async function deleteDbAndOpenNew() { const exists = await fs.exists(Realm.defaultPath) if (exists) await fs.unlink(Realm.defaultPath) - await openDbConnection() + await openDb({ persistConnection: true }) } export { From 8da85235581f1de5f2b93d1c1c7712dd4e65c4f3 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Fri, 14 Sep 2018 07:34:28 +0200 Subject: [PATCH 13/37] Implement wrong-password-try-again? flow --- components/labels.js | 10 +++-- components/password-prompt.js | 26 ++++++----- components/settings/password-setting.js | 57 +++++++++++++++++-------- styles/index.js | 13 +++++- 4 files changed, 74 insertions(+), 32 deletions(-) diff --git a/components/labels.js b/components/labels.js index 7ddbae1..814b6d3 100644 --- a/components/labels.js +++ b/components/labels.js @@ -3,7 +3,10 @@ export const shared = { save: 'Save', errorTitle: 'Error', successTitle: 'Success', - warning: 'Warning' + warning: 'Warning', + incorrectPassword: 'Password incorrect', + incorrectPasswordMessage: 'That password is incorrect.', + tryAgain: 'Try again' } export const settings = { @@ -52,8 +55,9 @@ export const settings = { passwordSettings: { title: 'App password', explainerDisabled: "Encrypt the app's database with a password. You have to enter the password every time the app is started.", - explainerEnabled: "Your app's data is encrypted with your password.", - deletePassword: "Delete password" + explainerEnabled: "Password protection and database encryption is currently enabled", + deletePassword: "Delete password", + enterCurrent: "Please enter your current password", } } diff --git a/components/password-prompt.js b/components/password-prompt.js index e21dba6..847859e 100644 --- a/components/password-prompt.js +++ b/components/password-prompt.js @@ -1,17 +1,19 @@ import React, { Component } from 'react' -import { View, TextInput, TouchableOpacity } from 'react-native' +import { View, TextInput, TouchableOpacity, Alert } from 'react-native' import nodejs from 'nodejs-mobile-react-native' import { AppText } from './app-text' import { hasEncryptionObservable } from '../local-storage' import styles from '../styles' -import { passwordPrompt } from './labels' +import { passwordPrompt, shared } from './labels' import { openDbConnection, requestHash, deleteDbAndOpenNew, openDb } from '../db' import App from './app' export default class PasswordPrompt extends Component { constructor() { super() - this.state = {} + this.state = { + password: null + } hasEncryptionObservable.once((hasEncryption) => { if (hasEncryption) { this.setState({showPasswordPrompt: true}) @@ -36,7 +38,14 @@ export default class PasswordPrompt extends Component { await openDb({hash: msg.message, persistConnection: true }) this.setState({ showApp: true }) } catch (err) { - this.setState({ wrongPassword: true }) + Alert.alert( + shared.incorrectPassword, + shared.incorrectPasswordMessage, + [{ + text: shared.tryAgain, + onPress: () => this.setState({password: null}) + }] + ) } } @@ -50,16 +59,12 @@ export default class PasswordPrompt extends Component { {this.state.showApp ? : - + {this.state.showPasswordPrompt && this.setState({password: val})} - style={{ - borderWidth: 1, - borderColor: 'grey', - margin: 5 - }} + style={styles.passwordField} /> - {this.state.wrongPassword && Wrong PAssword!} { diff --git a/components/settings/password-setting.js b/components/settings/password-setting.js index 9fe820b..385fa5c 100644 --- a/components/settings/password-setting.js +++ b/components/settings/password-setting.js @@ -2,7 +2,8 @@ import React, { Component } from 'react' import { View, TouchableOpacity, - TextInput + TextInput, + Alert } from 'react-native' import nodejs from 'nodejs-mobile-react-native' import { AppText } from '../app-text' @@ -10,14 +11,16 @@ import { hasEncryptionObservable } from '../../local-storage' import styles from '../../styles/index' -import { settings as labels } from '../labels' +import { settings as labels, shared } from '../labels' import { requestHash, openDb } from '../../db' export default class PasswordSetting extends Component { constructor(props) { super(props) this.state = { - enabled: hasEncryptionObservable.value + enabled: hasEncryptionObservable.value, + currentPassword: null, + enteringCurrentPassword: false } nodejs.start('main.js') @@ -38,11 +41,25 @@ export default class PasswordSetting extends Component { try { await openDb({ hash: msg.message, persistConnection: false }) this.setState({ - wrongPassword: false, - enterOldPassword: false + enteringCurrentPassword: false }) } catch (err) { - this.setState({wrongPassword: true}) + Alert.alert( + shared.incorrectPassword, + shared.incorrectPasswordMessage, + [{ + text: shared.cancel, + onPress: () => { + this.setState({ + enteringCurrentPassword: false, + currentPassword: null + }) + } + }, { + text: shared.tryAgain, + onPress: () => this.setState({currentPassword: null}) + }] + ) } } @@ -57,21 +74,27 @@ export default class PasswordSetting extends Component { : {labels.passwordSettings.explainerDisabled} } - {this.state.enterOldPassword && - this.setState({ oldPassword: val })} - /> + {this.state.enteringCurrentPassword && + + { + this.setState({ + currentPassword: val, + wrongPassword: false + }) + }} + value={this.state.currentPassword} + placeholder={labels.passwordSettings.enterCurrent} + /> + } - {this.state.wrongPassword && Wrong PAssword!} { - if (!this.state.enterOldPassword) { - this.setState({ enterOldPassword: true }) + if (!this.state.enteringCurrentPassword) { + this.setState({ enteringCurrentPassword: true }) } else { - requestHash(this.state.oldPassword) + requestHash(this.state.currentPassword) } }} style={styles.settingsButton}> diff --git a/styles/index.js b/styles/index.js index 629533a..00b958d 100644 --- a/styles/index.js +++ b/styles/index.js @@ -249,6 +249,17 @@ export default StyleSheet.create({ fontSize: 20, color: secondaryColor, marginTop: 1 + }, + passwordField: { + backgroundColor: 'white', + padding: 10, + marginTop: 10, + marginHorizontal: 10 + }, + passwordPrompt: { + backgroundColor: 'lightgrey', + flex: 1, + padding: 30 } }) @@ -268,6 +279,6 @@ export const iconStyles = { color: fontOnPrimaryColor }, menuIconInactive: { - color: 'lightgrey' + color: 'lightgrey', }, } \ No newline at end of file From 467dc8d4245f2cee76b3fc94aa06d1b4427130d3 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Fri, 14 Sep 2018 07:53:38 +0200 Subject: [PATCH 14/37] Add bacup warning --- components/labels.js | 5 ++++- components/settings/password-setting.js | 12 +++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/components/labels.js b/components/labels.js index 814b6d3..5f405fc 100644 --- a/components/labels.js +++ b/components/labels.js @@ -6,7 +6,8 @@ export const shared = { warning: 'Warning', incorrectPassword: 'Password incorrect', incorrectPasswordMessage: 'That password is incorrect.', - tryAgain: 'Try again' + tryAgain: 'Try again', + ok: 'OK' } export const settings = { @@ -58,6 +59,8 @@ export const settings = { explainerEnabled: "Password protection and database encryption is currently enabled", deletePassword: "Delete password", enterCurrent: "Please enter your current password", + backupReminderTitle: 'Have you made a backup of your data?', + backupReminder: 'When you make changes to your password, we delete your old data and store it in a new version. To be safe, please backup your data using the export function before making changes to your password. Making any changes to your password setting will also restart the app immediately.', } } diff --git a/components/settings/password-setting.js b/components/settings/password-setting.js index 385fa5c..210fadb 100644 --- a/components/settings/password-setting.js +++ b/components/settings/password-setting.js @@ -92,7 +92,17 @@ export default class PasswordSetting extends Component { { if (!this.state.enteringCurrentPassword) { - this.setState({ enteringCurrentPassword: true }) + Alert.alert( + labels.passwordSettings.backupReminderTitle, + labels.passwordSettings.backupReminder, + [{ + text: shared.cancel, + style: 'cancel' + }, { + text: shared.ok, + onPress: () => this.setState({enteringCurrentPassword: true}) + }] + ) } else { requestHash(this.state.currentPassword) } From 6c49d8a36c562a46eb4dac6f21923afdecc1a1e4 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Fri, 14 Sep 2018 09:19:31 +0200 Subject: [PATCH 15/37] Add wrapper for password screen and logged-in app --- components/app-wrapper.js | 26 +++++++++++ components/password-prompt.js | 82 +++++++++++++++++------------------ index.js | 4 +- 3 files changed, 67 insertions(+), 45 deletions(-) create mode 100644 components/app-wrapper.js diff --git a/components/app-wrapper.js b/components/app-wrapper.js new file mode 100644 index 0000000..889161c --- /dev/null +++ b/components/app-wrapper.js @@ -0,0 +1,26 @@ +import React, { Component } from 'react' +import { View } from 'react-native' +import nodejs from 'nodejs-mobile-react-native' +import App from './app' +import PasswordPrompt from './password-prompt' + +export default class AppWrapper extends Component { + constructor() { + super() + this.state = {} + nodejs.start('main.js') + } + render() { + return ( + + {this.state.showApp ? + + : + this.setState({showApp: true})} + /> + } + + ) + } +} \ No newline at end of file diff --git a/components/password-prompt.js b/components/password-prompt.js index 847859e..d2e7b36 100644 --- a/components/password-prompt.js +++ b/components/password-prompt.js @@ -5,25 +5,25 @@ import { AppText } from './app-text' import { hasEncryptionObservable } from '../local-storage' import styles from '../styles' import { passwordPrompt, shared } from './labels' -import { openDbConnection, requestHash, deleteDbAndOpenNew, openDb } from '../db' -import App from './app' +import { requestHash, deleteDbAndOpenNew, openDb } from '../db' export default class PasswordPrompt extends Component { - constructor() { - super() + constructor(props) { + super(props) this.state = { password: null } - hasEncryptionObservable.once((hasEncryption) => { + hasEncryptionObservable.once(async hasEncryption => { + hasEncryption = JSON.parse(hasEncryption) if (hasEncryption) { this.setState({showPasswordPrompt: true}) } else { - openDbConnection('something-wrong') - this.setState({showApp: true}) + await openDb({persistConnection: true}) + console.log(this.props) + this.props.onCorrectPassword() } }) - nodejs.start('main.js') nodejs.channel.addListener( 'message', this.passHashToDb, @@ -35,8 +35,8 @@ export default class PasswordPrompt extends Component { msg = JSON.parse(msg) if (msg.type != 'sha512') return try { + console.log('password prompt opening db') await openDb({hash: msg.message, persistConnection: true }) - this.setState({ showApp: true }) } catch (err) { Alert.alert( shared.incorrectPassword, @@ -46,7 +46,9 @@ export default class PasswordPrompt extends Component { onPress: () => this.setState({password: null}) }] ) + return } + this.setState({ showApp: true }) } componentWillUnmount() { @@ -55,40 +57,34 @@ export default class PasswordPrompt extends Component { render() { return ( - - {this.state.showApp ? - - : - - {this.state.showPasswordPrompt && - - this.setState({password: val})} - style={styles.passwordField} - /> - { - requestHash(this.state.password) - }} - > - - { passwordPrompt.title } - - - { - await deleteDbAndOpenNew() - this.setState({showApp: true}) - }} - > - - {'Delete old db and make unencrypted new'} - - - - } + + {this.state.showPasswordPrompt && + + this.setState({ password: val })} + style={styles.passwordField} + /> + { + requestHash(this.state.password) + }} + > + + {passwordPrompt.title} + + + { + await deleteDbAndOpenNew() + this.setState({ showApp: true }) + }} + > + + {'Delete old db and make unencrypted new'} + + } diff --git a/index.js b/index.js index b17ba73..9ff353c 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,4 @@ import { AppRegistry } from 'react-native' -import PasswordPrompt from './components/password-prompt' +import AppWrapper from './components/app-wrapper' -AppRegistry.registerComponent('home', () => PasswordPrompt) \ No newline at end of file +AppRegistry.registerComponent('home', () => AppWrapper) \ No newline at end of file From 3dbc26f50afe45f954b8b3527fa3c9f2ac2c6662 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Fri, 14 Sep 2018 09:20:21 +0200 Subject: [PATCH 16/37] Unify encrypt function --- components/app-wrapper.js | 4 +- components/home.js | 13 +----- components/password-prompt.js | 6 +-- components/settings/password-setting.js | 7 ++-- db/index.js | 56 +++++++++++-------------- 5 files changed, 34 insertions(+), 52 deletions(-) diff --git a/components/app-wrapper.js b/components/app-wrapper.js index 889161c..cd50bb7 100644 --- a/components/app-wrapper.js +++ b/components/app-wrapper.js @@ -17,7 +17,9 @@ export default class AppWrapper extends Component { : this.setState({showApp: true})} + showApp={() => { + this.setState({showApp: true}) + }} /> } diff --git a/components/home.js b/components/home.js index 799f845..658f809 100644 --- a/components/home.js +++ b/components/home.js @@ -9,7 +9,7 @@ import { LocalDate, ChronoUnit } from 'js-joda' import nodejs from 'nodejs-mobile-react-native' import styles from '../styles/index' import cycleModule from '../lib/cycle' -import { requestHash, getOrCreateCycleDay, getBleedingDaysSortedByDate, fillWithMucusDummyData, fillWithCervixDummyData, deleteAll, encryptAndRestartApp } from '../db' +import { requestHash, getOrCreateCycleDay, getBleedingDaysSortedByDate, fillWithMucusDummyData, fillWithCervixDummyData, deleteAll, changeEncryptionAndRestartApp } from '../db' import {bleedingPrediction as labels} from './labels' export default class Home extends Component { @@ -38,18 +38,9 @@ export default class Home extends Component { this.startEncryption = async (msg) => { msg = JSON.parse(msg) - if (msg.type === 'sha512') { - const hash = msg.message - const key = new Uint8Array(64) - for (let i = 0; i < key.length; i++) { - const twoDigitHex = hash.slice(i * 2, i * 2 + 2) - key[i] = parseInt(twoDigitHex, 16) - } - encryptAndRestartApp(key) - } + changeEncryptionAndRestartApp(msg.message) } - nodejs.start('main.js') nodejs.channel.addListener( 'message', this.startEncryption, diff --git a/components/password-prompt.js b/components/password-prompt.js index d2e7b36..dd43489 100644 --- a/components/password-prompt.js +++ b/components/password-prompt.js @@ -19,8 +19,7 @@ export default class PasswordPrompt extends Component { this.setState({showPasswordPrompt: true}) } else { await openDb({persistConnection: true}) - console.log(this.props) - this.props.onCorrectPassword() + this.props.showApp() } }) @@ -35,7 +34,6 @@ export default class PasswordPrompt extends Component { msg = JSON.parse(msg) if (msg.type != 'sha512') return try { - console.log('password prompt opening db') await openDb({hash: msg.message, persistConnection: true }) } catch (err) { Alert.alert( @@ -48,7 +46,7 @@ export default class PasswordPrompt extends Component { ) return } - this.setState({ showApp: true }) + this.props.showApp() } componentWillUnmount() { diff --git a/components/settings/password-setting.js b/components/settings/password-setting.js index 210fadb..56859ac 100644 --- a/components/settings/password-setting.js +++ b/components/settings/password-setting.js @@ -12,7 +12,7 @@ import { } from '../../local-storage' import styles from '../../styles/index' import { settings as labels, shared } from '../labels' -import { requestHash, openDb } from '../../db' +import { requestHash, openDb, changeEncryptionAndRestartApp } from '../../db' export default class PasswordSetting extends Component { constructor(props) { @@ -40,9 +40,6 @@ export default class PasswordSetting extends Component { if (msg.type != 'sha512') return try { await openDb({ hash: msg.message, persistConnection: false }) - this.setState({ - enteringCurrentPassword: false - }) } catch (err) { Alert.alert( shared.incorrectPassword, @@ -60,7 +57,9 @@ export default class PasswordSetting extends Component { onPress: () => this.setState({currentPassword: null}) }] ) + return } + await changeEncryptionAndRestartApp() } render() { diff --git a/db/index.js b/db/index.js index ceac742..f12b53f 100644 --- a/db/index.js +++ b/db/index.js @@ -170,13 +170,7 @@ function requestHash(pw) { export async function openDb ({ hash, persistConnection }) { if (hash) { - const key = new Uint8Array(64) - for (let i = 0; i < key.length; i++) { - const twoDigitHex = hash.slice(i * 2, i * 2 + 2) - key[i] = parseInt(twoDigitHex, 16) - } - - realmConfig.encryptionKey = key + realmConfig.encryptionKey = hashToInt8Array(hash) } const connection = await Realm.open(realmConfig) @@ -184,37 +178,26 @@ export async function openDb ({ hash, persistConnection }) { if (persistConnection) db = connection } -async function encryptAndRestartApp(key) { - const oldPath = db.path +export async function changeEncryptionAndRestartApp(hash) { + let key + if (hash) key = hashToInt8Array(hash) + const defaultPath = db.path const dir = db.path.split('/') dir.pop() dir.push('copied.realm') const copyPath = dir.join('/') const exists = await fs.exists(copyPath) if (exists) await fs.unlink(copyPath) - db.writeCopyTo(copyPath) + // for some reason, realm complains if we give it a key with value undefined + if (key) { + db.writeCopyTo(copyPath, key) + } else { + db.writeCopyTo(copyPath) + } db.close() - await fs.unlink(oldPath) - realmConfig.encryptionKey = key - db = new Realm(realmConfig) - await saveEncryptionFlag(true) - restart.Restart() -} - -export async function removeDbEncryptionAndRestartApp(key) { - const oldPath = db.path - const dir = db.path.split('/') - dir.pop() - dir.push('copied.realm') - const copyPath = dir.join('/') - const exists = await fs.exists(copyPath) - if (exists) await fs.unlink(copyPath) - db.writeCopyTo(copyPath) - db.close() - await fs.unlink(oldPath) - realmConfig.encryptionKey = key - db = new Realm(realmConfig) - await saveEncryptionFlag(true) + await fs.unlink(defaultPath) + await fs.moveFile(copyPath, defaultPath) + await saveEncryptionFlag(key ? true : false) restart.Restart() } @@ -222,6 +205,16 @@ async function deleteDbAndOpenNew() { const exists = await fs.exists(Realm.defaultPath) if (exists) await fs.unlink(Realm.defaultPath) await openDb({ persistConnection: true }) + await saveEncryptionFlag(false) +} + +function hashToInt8Array(hash) { + const key = new Uint8Array(64) + for (let i = 0; i < key.length; i++) { + const twoDigitHex = hash.slice(i * 2, i * 2 + 2) + key[i] = parseInt(twoDigitHex, 16) + } + return key } export { @@ -240,6 +233,5 @@ export { tryToImportWithDelete, tryToImportWithoutDelete, requestHash, - encryptAndRestartApp, deleteDbAndOpenNew } From 01a1fa4385f028fbfa787a336defe1dbd83d7af7 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Fri, 14 Sep 2018 12:19:22 +0200 Subject: [PATCH 17/37] Mask password input --- components/labels.js | 3 ++- components/password-prompt.js | 2 ++ components/settings/password-setting.js | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/components/labels.js b/components/labels.js index 5f405fc..8c5d427 100644 --- a/components/labels.js +++ b/components/labels.js @@ -106,5 +106,6 @@ export const bleedingPrediction = { } export const passwordPrompt = { - title: 'Log in' + title: 'Log in', + enterPassword: 'Enter password here' } \ No newline at end of file diff --git a/components/password-prompt.js b/components/password-prompt.js index dd43489..85bed6b 100644 --- a/components/password-prompt.js +++ b/components/password-prompt.js @@ -61,6 +61,8 @@ export default class PasswordPrompt extends Component { this.setState({ password: val })} style={styles.passwordField} + secureTextEntry={true} + placeholder={passwordPrompt.enterPassword} /> } From a74eb4080578efc18d285b26bd3e38b904b497b2 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Fri, 14 Sep 2018 13:08:11 +0200 Subject: [PATCH 18/37] Stylin' --- assets/drip_small.png | Bin 0 -> 5232 bytes components/labels.js | 2 +- components/password-prompt.js | 20 ++++++++++-------- styles/index.js | 37 ++++++++++++++++++++++++++-------- 4 files changed, 42 insertions(+), 17 deletions(-) create mode 100644 assets/drip_small.png diff --git a/assets/drip_small.png b/assets/drip_small.png new file mode 100644 index 0000000000000000000000000000000000000000..abdcde1a17e2008629a6df73d3f44417d5a0f120 GIT binary patch literal 5232 zcmcgwcQl;e*4BHOAOz7{5Jn4P)X@nNC4M?Vlwc%!jLzsS!30qv5u%3)(MF3F(WAFP z^fnlbGTg~`|Ng%9{d4bLYrp5Lb=Ldt^RDxpXYc*QKQ`2+rDCHZARwUC)zL71wE%2EN~0c;S6}%7GkxGcy&Ur$bQ_=*U_l z;Ne))(bLgG_*OpU@U4A`uApZQA`j6opSOi^Y(wPfmSpQHj2oKB#XH9KMDxqmJAzm2 zZ9D8~C?x0)v$(mF_|vNP9Rm*bw_-&FdS*rW_gAfTZw@{x4P} zsgY5Js`+5v`-so7i7ifV;JCQJd){jp?l1fk=q@AJ%QxQk_50zu3!96gQu$~19Kpid zbzI^vE-uBB)q=ZGwGsoc=jis-N+*3o7hYoPscc1E;y1AetxDX`bNbsUpb%wpO8 zC(!Al2dH>e{>~XjEtXh1Hlb0mJ@d^hd55mR#>&-#G|VK#=jCcc;1^xT{<6k#bHR;u19|2!TctK0xO|;j90lQD&EW6|T&zd?aXn7}oG73W0a;@!Um*pXM z(*=ZN>@vGLKSA)dv1;54I{;Dkxqc?X?Y<+n2SBI_bBw570~r=~p_3(hVx+qi{ykmJ zgQ)FnHLbs!oJm3FFNwI1PIoU3NopBct4+*?#Y!L8VjnOT`qlIK{!yZsdh+ieQ8J)p zG*YZAS91NZ_N~q>SgKC?t*{wk!oJ5$p?+dc{B&+P;TeD`VzJEdNgq;g&$yi|jZ836 zxI<@k*`Ps0U1#`bwLozV1=7kAYTmceX~upg)KV**aB3?OudY3Bmj|7>ij1LVd+iac zp-A$|X2zcGI*DPF1~`g&Uq=#5Na%k8hV$w zTcP#cgdH_yNQ6sepS6E=;8==9BP&Om#WN9uI4AkY-(ACW@@jF9L?Bpxf;NAK-8p!B zo{tNfQj4*RuE1vpJWb7c*ITi6HJP2RfGeKo+fgH7uVyXzs^ux|MYHWmAbvf1{5ypd zQ-2sk>u1zFLjEiZgj#$>UbE}=u0%yO@aE|z%TUt0^7je!_{~jC@ZsWnQH%332znQZ zrxZhb`Y0K3*hDQ$dLH|S6I(GotHkcqr#!fJ5%~sj5*ZsmAHB}hAL)$;<24%#APl3M zauZ#^5cGT9LG;UOemw3XLk~T=V{3}Kg#0+NFFDnoFRPye)x0vQy4);jTu$>EEpUS% z?!r?}Fp)RJ3+%q;lQt?+>?wSkvb}N7>l0jwk~N;C!J8*7PnkUa!BV)N6Fu*otZLnO zcjd=85m~6MAIF;loK#EhXLvoYXcDHj1U2NY;W2LMy5;1yGoVh?ci%St*BkBdCrv$u zIn2hQlYfw-)UA=MNh7FV%`7upK7imIfjfJZ#pUG$w^@<+^TsG&!_`$TFF7?J^oYgs zkUU``sre~lwiKJ2C}yG=i)01jp)YJ`06Q5IUw>yH!rzM8@s0NEV3u0xb-Ihnm8nOnD?VYd zWd%1vm~wa`+6yLF=h}S|B_x2T(w9(H{0n;C%;?1+M-KkAb?p_Mifbz@VQa-y8bich%oluxEg zEzepb7Y80UO0V`jRo z`$&zGgdy?~3Z8xugul`VgdxMt8)v0FB^DId*}J6j~TH>mJXlbRX>l5Ql~$Es~Ng0th)f#(+F$ed>S(! z7fZLR>a_EMFLY?-|3PoNSZwg!b;(3h~`3F zCw}BI*C9{-@z_O7MjW!0?n3jMqZ%HMjVNJH%`LZuyf{xNYbS7T4H2|YQe@Jt8LzIUPcQ!g90d*Np>%hPiIS?=U|oB6#5Q(71~6^_xf0QIqeBGIV| z#+{L72_r5#=%7~<3v$KaV+L$EJJ=k3Cpja|D~zL4bkJEBfZKL-%5!)fa-s@uGR#uUvv`dxL#Hl-bl@gYznT-!rU6+{KhcX(* zDiXbq>yIYCL}pMNESjDUxVJVFPkvR3sGc!M%p6)bV^&_G;sbr-L4Wf;_7^x)&dJl- zbVZY!i9CNnjc_uu*fH(EcDZfBf%m?Eezz#07l*6Mhb!4lH7u|#!gY}w8xLiFnZ*8F z%nyA!fJEhD7$k?YJ_|LB{Ua8f=_k0w!Scb6Z20}ll3`(=7uWcgY8gvNIpwassd$Z8 zUOwD}&%R;|8ZawZv7M|c0zg%VkS#a*L zM~C`i!@7n&y}~=bPneanUDhJ+dG9}$WKsz8E!BI_^>NJfmkXOW5IC4t+dZ%iV>>eN z5?h_NwRPlYayGeD;FeEGlk?ewu&xOmL`Ynqx}3Ykq%MGPkdYKc9k~6mP}&FzFUr>Z z*;z8vp_FjGPdd<_S_bKIHKq(RQnBQ~(hBV&YU6jeJK+?IRm1GnRcDl&b6ln`*|pf) zo)djqX>~ga`E0qOmu+>F-jrzuUF&LJwZ&B&$^KcTVO)$BcAt~@+4q>_8k?I@&jSj2 zih8Luj0%2ukGkOH7S#U0#Q*3KcDXc?`D8-nB3owMxrFGCP#FJ1G1DHayt8^KF429QFfyNL(e|XhMSjTu@*gM%v>e+p`B;;>)EfI zfPnxhdy@~w$7Sxzzq0L4U-uG0Iy2@clbr<#r>Rv~r)Qp7%a{9V%NOe>R5%7lx#XUy z{Qi{0AO7pjFjJV->z}uXwl2BEf_wG-e-3)HuahF5b1d3S)9K#4zme%_;xe2EOLWxeV$cgM&;>#)I zAV3S8da)ut@~wpy<-q+sK;Qjyn%!m8d zjMr^oVD*f_^}MbfgZ!8uKEr%gDs`?Cx(llAGjDaoZmdzvR|NL;yRjB4d-r0?cst1n zmKWM$UY@$56T$6nmeKjZE8_RLxl|X-8L)ValIY0sdE4-11sR^!kYW-;!jYouWXLpbOgVf+U2!RB~AV#xo8Y_&PEzQ(Q_Q%=3bhdgxV z&p&^?Sd6>=uE5WABa(D1bSpGWI%zVCN5x>Wp%c$SuvQcgc7l+&ipv3?F#1yD(k-ul z)hvv9^+smb5C=t-l z8Rx+aXw-ZYdJ^a)WT5WfWBpwkLi}i_$x~uB1)rCSN82JiXLK~`kyLIll#JLT#p=d{9WAL_u6M||ny1&oREqrc-q^)VcY3EP_B3BlA zzBJMd(my1F{G%B|Pk``cIJCQB!Pj@k9WELagcdf`_}*mt7`~oz9_qzY0ch-Q>S7(q zWLW>A9*4US`q^Blojad(Rjumu`gln8oUbc-PU66bc-44a*+lq~8A%5@ET8nFJfsDh zTBF0Mnl>))4I1)Y&))oz{mL<9DQYkv7Cn)?OgemaYw_g8kI)m|D05zX%BydS*!=vh z-Mc=Kh4(CoZN0{*bUmKRxUf=UgfxL5ev3|Zr-M&>MU#8K7xJ$A6!V94# zt>?=^4nS)%i%%UE>3ZBPXB^Qv4eEd9#v=8=_{Iv_McubJAB_nqfXTVNehJ>3rQn#4 zN9Q*rUeC7~mu{j@7^|ZC_6;aFrU_jZTfNy1VCHf`Sgv=N4|a-)Bk4*tmZxf^uSIUH z8CJNYw9ET2&1}^|cQg+ogqXM*l`|N0IU@zRV`8$K<_z(fo zS2^+CF25oI#{V`(=yLa0bGNo%?<0*0&pW z;o(YHT%crHer|*1OiFa27B9Jj$rmpKb7>1&3!O2oAcbz}7goob42NunC>4BM>u=?g zf~t|Eg@M3q=_Dfoei913)}pUUBV6y>|JNG$|G!WmX(|`4rM%oEVZ_N6IBzKGI@s5N ze$s*P1+GA@ANxsb-L>^+5bi|Z`j5ErV=PqI`70Y(=8aA}5Rnww3CD|1pdC<*Y^3X_ zExkF8n5-v{v5I!H2X>pT;)q)se1lcT@BqAS({Tm`a>}1P`yZ7@sXq6rdvvnkwxwam zvQ_XVCh@>#Q~t5*Lk+$NVDZ()9(mhBdhHywSUC#ah=f#Iw5?@Jn=@=s;bo;a=f*$b z^!XPv0gFmk)!p}ufLc5Y^})^y>2*7hFO^N6``9uo86FMEp8{n}e*=|>4c;83w#}Wj z0E^9&q!KyboXbs!zdovGH5$zQXPikE^PM0{hwm-@mS6E+ip>V_;9_mG{seB4ome)f e@qbv?@r^3|rzlYX literal 0 HcmV?d00001 diff --git a/components/labels.js b/components/labels.js index 8c5d427..1c90f88 100644 --- a/components/labels.js +++ b/components/labels.js @@ -106,6 +106,6 @@ export const bleedingPrediction = { } export const passwordPrompt = { - title: 'Log in', + title: 'Unlock app', enterPassword: 'Enter password here' } \ No newline at end of file diff --git a/components/password-prompt.js b/components/password-prompt.js index 85bed6b..7351689 100644 --- a/components/password-prompt.js +++ b/components/password-prompt.js @@ -1,5 +1,5 @@ import React, { Component } from 'react' -import { View, TextInput, TouchableOpacity, Alert } from 'react-native' +import { View, TextInput, TouchableOpacity, Alert, Image } from 'react-native' import nodejs from 'nodejs-mobile-react-native' import { AppText } from './app-text' import { hasEncryptionObservable } from '../local-storage' @@ -55,33 +55,37 @@ export default class PasswordPrompt extends Component { render() { return ( - + {this.state.showPasswordPrompt && - + + this.setState({ password: val })} - style={styles.passwordField} + style={styles.passwordPromptField} secureTextEntry={true} placeholder={passwordPrompt.enterPassword} /> { requestHash(this.state.password) }} > - + {passwordPrompt.title} { await deleteDbAndOpenNew() this.setState({ showApp: true }) }} > - + {'Delete old db and make unencrypted new'} diff --git a/styles/index.js b/styles/index.js index 00b958d..82c6e71 100644 --- a/styles/index.js +++ b/styles/index.js @@ -170,7 +170,7 @@ export default StyleSheet.create({ backgroundColor: secondaryColor, padding: 10, alignItems: 'center', - margin: 10 + margin: 10, }, settingsButtonText: { color: fontOnPrimaryColor @@ -251,16 +251,37 @@ export default StyleSheet.create({ marginTop: 1 }, passwordField: { - backgroundColor: 'white', padding: 10, marginTop: 10, - marginHorizontal: 10 + marginHorizontal: 10, + backgroundColor: 'white' + }, + passwordPromptPage: { + padding: 30, + alignItems: 'center' + }, + passwordPromptField: { + padding: 10, + marginTop: 10, + marginHorizontal: 10, + borderBottomWidth: 3, + borderBottomColor: primaryColor, + width: '100%', + fontSize: 20, + marginVertical: 20 + }, + passwordPromptButton: { + backgroundColor: secondaryColor, + padding: 10, + alignItems: 'center', + margin: 10, + width: '100%', + borderRadius: 10 + }, + passwordPromptButtonText: { + color: fontOnPrimaryColor, + fontSize: 20 }, - passwordPrompt: { - backgroundColor: 'lightgrey', - flex: 1, - padding: 30 - } }) export const iconStyles = { From d41574235fe3459d7df73dd92c47c309db81f85f Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Fri, 14 Sep 2018 13:43:45 +0200 Subject: [PATCH 19/37] Add forgot password flow --- components/labels.js | 9 +++++++- components/password-prompt.js | 39 ++++++++++++++++++++++++++++------- styles/index.js | 4 ++++ 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/components/labels.js b/components/labels.js index 1c90f88..d4b5876 100644 --- a/components/labels.js +++ b/components/labels.js @@ -107,5 +107,12 @@ export const bleedingPrediction = { export const passwordPrompt = { title: 'Unlock app', - enterPassword: 'Enter password here' + enterPassword: 'Enter password here', + deleteDatabaseExplainer: "If you've forgotten your password, unfortunately, there is nothing we can do to recover your data, because it is encrypted with the password only you know. You can, however, delete all your encrypted data and start fresh. Once all data has been erased, you can set a new password in the settings, if you like.", + forgotPassword: 'Forgot your password?', + deleteDatabaseTitle: 'Forgot your password?', + deleteData: 'Yes, delete all my data', + areYouSureTitle: 'Are you sure?', + areYouSure: 'Are you absolutely sure you want to permanently delete all your data?', + reallyDeleteData: 'Yes, I am sure' } \ No newline at end of file diff --git a/components/password-prompt.js b/components/password-prompt.js index 7351689..3c972eb 100644 --- a/components/password-prompt.js +++ b/components/password-prompt.js @@ -4,7 +4,7 @@ import nodejs from 'nodejs-mobile-react-native' import { AppText } from './app-text' import { hasEncryptionObservable } from '../local-storage' import styles from '../styles' -import { passwordPrompt, shared } from './labels' +import { passwordPrompt as labels, shared } from './labels' import { requestHash, deleteDbAndOpenNew, openDb } from '../db' export default class PasswordPrompt extends Component { @@ -66,7 +66,7 @@ export default class PasswordPrompt extends Component { onChangeText={val => this.setState({ password: val })} style={styles.passwordPromptField} secureTextEntry={true} - placeholder={passwordPrompt.enterPassword} + placeholder={labels.enterPassword} /> - {passwordPrompt.title} + {labels.title} { - await deleteDbAndOpenNew() - this.setState({ showApp: true }) + Alert.alert( + labels.deleteDatabaseTitle, + labels.deleteDatabaseExplainer, + [{ + text: shared.cancel, + style: 'cancel' + }, { + text: labels.deleteData, + onPress: () => { + Alert.alert( + labels.areYouSureTitle, + labels.areYouSure, + [{ + text: shared.cancel, + style: 'cancel' + }, { + text: labels.reallyDeleteData, + onPress: async () => { + await deleteDbAndOpenNew() + this.props.showApp() + } + }] + ) + } + }] + ) }} > - - {'Delete old db and make unencrypted new'} + + {labels.forgotPassword} diff --git a/styles/index.js b/styles/index.js index 82c6e71..ee63b83 100644 --- a/styles/index.js +++ b/styles/index.js @@ -282,6 +282,10 @@ export default StyleSheet.create({ color: fontOnPrimaryColor, fontSize: 20 }, + passwordPromptForgotPasswordText: { + marginTop: 20, + color: 'grey' + } }) export const iconStyles = { From c476a7a278c12f0fe17945432964a22ce4298fda Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Fri, 14 Sep 2018 14:32:28 +0200 Subject: [PATCH 20/37] Clean up --- components/chart/chart.js | 2 +- components/home.js | 8 +--- components/password-prompt.js | 59 +++++++++++++------------ components/settings/password-setting.js | 1 + db/index.js | 58 +++++++----------------- 5 files changed, 50 insertions(+), 78 deletions(-) diff --git a/components/chart/chart.js b/components/chart/chart.js index f6da539..ddd8d00 100644 --- a/components/chart/chart.js +++ b/components/chart/chart.js @@ -72,7 +72,7 @@ export default class CycleChart extends Component { 'pain', 'note' ].filter((symptomName) => { - return cycleDaysSortedByDate.some(cycleDay => cycleDay[symptomName]) + return this.cycleDaysSortedByDate.some(cycleDay => cycleDay[symptomName]) }) const columns = xAxisDates.map(dateString => { diff --git a/components/home.js b/components/home.js index 658f809..59d5340 100644 --- a/components/home.js +++ b/components/home.js @@ -9,7 +9,7 @@ import { LocalDate, ChronoUnit } from 'js-joda' import nodejs from 'nodejs-mobile-react-native' import styles from '../styles/index' import cycleModule from '../lib/cycle' -import { requestHash, getOrCreateCycleDay, getBleedingDaysSortedByDate, fillWithMucusDummyData, fillWithCervixDummyData, deleteAll, changeEncryptionAndRestartApp } from '../db' +import { requestHash, getOrCreateCycleDay, getBleedingDaysSortedByDate, fillWithMucusDummyData, fillWithCervixDummyData, changeEncryptionAndRestartApp } from '../db' import {bleedingPrediction as labels} from './labels' export default class Home extends Component { @@ -84,12 +84,6 @@ export default class Home extends Component { title="fill with example data for cervix&temp"> - - - - - - ) diff --git a/components/labels.js b/components/labels.js index d4b5876..15e922a 100644 --- a/components/labels.js +++ b/components/labels.js @@ -55,10 +55,12 @@ export const settings = { }, passwordSettings: { title: 'App password', - explainerDisabled: "Encrypt the app's database with a password. You have to enter the password every time the app is started.", + explainerDisabled: "Encrypt the app's database with a password. You need to enter the password every time the app is started.", explainerEnabled: "Password protection and database encryption is currently enabled", + setPassword: 'Set password', deletePassword: "Delete password", enterCurrent: "Please enter your current password", + enterNew: "Please enter a new password", backupReminderTitle: 'Have you made a backup of your data?', backupReminder: 'When you make changes to your password, we delete your old data and store it in a new version. To be safe, please backup your data using the export function before making changes to your password. Making any changes to your password setting will also restart the app immediately.', } diff --git a/components/settings/password-setting.js b/components/settings/password-setting.js index 7fc212f..be1b974 100644 --- a/components/settings/password-setting.js +++ b/components/settings/password-setting.js @@ -18,7 +18,7 @@ export default class PasswordSetting extends Component { constructor(props) { super(props) this.state = { - enabled: hasEncryptionObservable.value, + encryptionEnabled: hasEncryptionObservable.value, currentPassword: null, enteringCurrentPassword: false } @@ -38,8 +38,20 @@ export default class PasswordSetting extends Component { passHashToDb = async (msg) => { msg = JSON.parse(msg) if (msg.type != 'sha512') return + if (this.state.encryptionEnabled) { + await this.removeEncryption(msg.message) + } else if (!this.state.encryptionEnabled) { + await changeEncryptionAndRestartApp(msg.message) + } + } + + addEncryption = async hash => { + changeEncryptionAndRestartApp(hash) + } + + removeEncryption = async hash => { try { - await openDb({ hash: msg.message, persistConnection: false }) + await openDb({ hash, persistConnection: false }) } catch (err) { console.log(err) Alert.alert( @@ -69,7 +81,7 @@ export default class PasswordSetting extends Component { {labels.passwordSettings.title} - {this.state.enabled ? + {this.state.encryptionEnabled ? {labels.passwordSettings.explainerEnabled} : {labels.passwordSettings.explainerDisabled} @@ -90,30 +102,71 @@ export default class PasswordSetting extends Component { /> } - { - if (!this.state.enteringCurrentPassword) { - Alert.alert( - labels.passwordSettings.backupReminderTitle, - labels.passwordSettings.backupReminder, - [{ - text: shared.cancel, - style: 'cancel' - }, { - text: shared.ok, - onPress: () => this.setState({enteringCurrentPassword: true}) - }] - ) - } else { - requestHash(this.state.currentPassword) - } - }} - style={styles.settingsButton}> - - {labels.passwordSettings.deletePassword} - - + {this.state.encryptionEnabled && + { + if (!this.state.enteringCurrentPassword) { + showBackUpReminder(() => { + this.setState({ enteringCurrentPassword: true }) + }) + } else { + requestHash(this.state.currentPassword) + } + }} + style={styles.settingsButton}> + + {labels.passwordSettings.deletePassword} + + + } + + {this.state.enteringNewPassword && + + { + this.setState({ + newPassword: val + }) + }} + value={this.state.newPassword} + placeholder={labels.passwordSettings.enterNew} + secureTextEntry={true} + /> + + } + {!this.state.encryptionEnabled && + { + if (!this.state.enteringNewPassword) { + showBackUpReminder(() => { + this.setState({ enteringNewPassword: true }) + }) + } else { + requestHash(this.state.newPassword) + } + }} + style={styles.settingsButton}> + + {labels.passwordSettings.setPassword} + + + } ) } +} + +function showBackUpReminder(okHandler) { + Alert.alert( + labels.passwordSettings.backupReminderTitle, + labels.passwordSettings.backupReminder, + [{ + text: shared.cancel, + style: 'cancel' + }, { + text: shared.ok, + onPress: okHandler + }] + ) } \ No newline at end of file diff --git a/db/index.js b/db/index.js index 4ef5a4e..4173c1d 100644 --- a/db/index.js +++ b/db/index.js @@ -157,7 +157,7 @@ export function tryToImportWithoutDelete(cycleDays) { export function requestHash(pw) { nodejs.channel.send(JSON.stringify({ type: 'request-SHA512', - message: pw || 'mypassword' + message: pw })) } From 19288510c5c7d843495ffbd27c7c49ac776cf8ab Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Fri, 14 Sep 2018 17:04:13 +0200 Subject: [PATCH 22/37] Remove current password check and add change password flow --- components/labels.js | 4 +- components/settings/password-setting.js | 119 ++++++++++-------------- 2 files changed, 50 insertions(+), 73 deletions(-) diff --git a/components/labels.js b/components/labels.js index 15e922a..d7f8199 100644 --- a/components/labels.js +++ b/components/labels.js @@ -58,8 +58,8 @@ export const settings = { explainerDisabled: "Encrypt the app's database with a password. You need to enter the password every time the app is started.", explainerEnabled: "Password protection and database encryption is currently enabled", setPassword: 'Set password', - deletePassword: "Delete password", - enterCurrent: "Please enter your current password", + deletePassword: 'Remove password protection', + changePassword: 'Change password', enterNew: "Please enter a new password", backupReminderTitle: 'Have you made a backup of your data?', backupReminder: 'When you make changes to your password, we delete your old data and store it in a new version. To be safe, please backup your data using the export function before making changes to your password. Making any changes to your password setting will also restart the app immediately.', diff --git a/components/settings/password-setting.js b/components/settings/password-setting.js index be1b974..0b3a3fb 100644 --- a/components/settings/password-setting.js +++ b/components/settings/password-setting.js @@ -12,18 +12,18 @@ import { } from '../../local-storage' import styles from '../../styles/index' import { settings as labels, shared } from '../labels' -import { requestHash, openDb, changeEncryptionAndRestartApp } from '../../db' +import { requestHash, changeEncryptionAndRestartApp } from '../../db' export default class PasswordSetting extends Component { constructor(props) { super(props) this.state = { - encryptionEnabled: hasEncryptionObservable.value, - currentPassword: null, - enteringCurrentPassword: false + showUpdateAndDelete: hasEncryptionObservable.value, + showSetPassword: !hasEncryptionObservable.value, + settingNewPassword: false, + changingPassword: false } - nodejs.start('main.js') nodejs.channel.addListener( 'message', this.passHashToDb, @@ -38,41 +38,7 @@ export default class PasswordSetting extends Component { passHashToDb = async (msg) => { msg = JSON.parse(msg) if (msg.type != 'sha512') return - if (this.state.encryptionEnabled) { - await this.removeEncryption(msg.message) - } else if (!this.state.encryptionEnabled) { - await changeEncryptionAndRestartApp(msg.message) - } - } - - addEncryption = async hash => { - changeEncryptionAndRestartApp(hash) - } - - removeEncryption = async hash => { - try { - await openDb({ hash, persistConnection: false }) - } catch (err) { - console.log(err) - Alert.alert( - shared.incorrectPassword, - shared.incorrectPasswordMessage, - [{ - text: shared.cancel, - onPress: () => { - this.setState({ - enteringCurrentPassword: false, - currentPassword: null - }) - } - }, { - text: shared.tryAgain, - onPress: () => this.setState({currentPassword: null}) - }] - ) - return - } - await changeEncryptionAndRestartApp() + await changeEncryptionAndRestartApp(msg.message) } render() { @@ -81,44 +47,55 @@ export default class PasswordSetting extends Component { {labels.passwordSettings.title} - {this.state.encryptionEnabled ? + {this.state.showUpdateAndDelete ? {labels.passwordSettings.explainerEnabled} : {labels.passwordSettings.explainerDisabled} } - {this.state.enteringCurrentPassword && + + {this.state.showUpdateAndDelete && - { - this.setState({ - currentPassword: val, - wrongPassword: false - }) + {this.state.changingPassword && + + { + this.setState({ + changedPassword: val + }) + }} + value={this.state.changedPassword} + placeholder={labels.passwordSettings.enterNew} + secureTextEntry={true} + /> + + } + { + if (!this.state.changingPassword) { + showBackUpReminder(() => { + this.setState({ changingPassword: true }) + }) + } else { + requestHash(this.state.changedPassword) + } }} - value={this.state.currentPassword} - placeholder={labels.passwordSettings.enterCurrent} - secureTextEntry={true} - /> + style={styles.settingsButton}> + + {labels.passwordSettings.changePassword} + + + { + showBackUpReminder(() => changeEncryptionAndRestartApp()) + }} + style={styles.settingsButton}> + + {labels.passwordSettings.deletePassword} + + } - {this.state.encryptionEnabled && - { - if (!this.state.enteringCurrentPassword) { - showBackUpReminder(() => { - this.setState({ enteringCurrentPassword: true }) - }) - } else { - requestHash(this.state.currentPassword) - } - }} - style={styles.settingsButton}> - - {labels.passwordSettings.deletePassword} - - - } {this.state.enteringNewPassword && @@ -135,7 +112,7 @@ export default class PasswordSetting extends Component { /> } - {!this.state.encryptionEnabled && + {this.state.showSetPassword && { if (!this.state.enteringNewPassword) { From 9efd366812c0d537e56db166e710c44826a02fd3 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Fri, 14 Sep 2018 17:17:00 +0200 Subject: [PATCH 23/37] Improve backup warning text --- components/labels.js | 2 +- components/password-prompt.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/components/labels.js b/components/labels.js index d7f8199..e8fad47 100644 --- a/components/labels.js +++ b/components/labels.js @@ -62,7 +62,7 @@ export const settings = { changePassword: 'Change password', enterNew: "Please enter a new password", backupReminderTitle: 'Have you made a backup of your data?', - backupReminder: 'When you make changes to your password, we delete your old data and store it in a new version. To be safe, please backup your data using the export function before making changes to your password. Making any changes to your password setting will also restart the app immediately.', + backupReminder: 'Just to be safe, please backup your data using the export function before making changes to your password.\n\nPlease also make sure you do not lose your password. There is no way to recover your data if you do.\n\nMaking any changes to your password setting will restart the app.', } } diff --git a/components/password-prompt.js b/components/password-prompt.js index 1c933e7..d9125bf 100644 --- a/components/password-prompt.js +++ b/components/password-prompt.js @@ -36,7 +36,6 @@ export default class PasswordPrompt extends Component { try { await openDb({hash: msg.message, persistConnection: true }) } catch (err) { - console.log(err) Alert.alert( shared.incorrectPassword, shared.incorrectPasswordMessage, From 1ffd5c8ba33dbcbd7218f6e6952c96ac415b3ce5 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Fri, 14 Sep 2018 18:06:25 +0200 Subject: [PATCH 24/37] Disallow empty passwords --- components/password-prompt.js | 1 + components/settings/password-setting.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/components/password-prompt.js b/components/password-prompt.js index d9125bf..78d1b93 100644 --- a/components/password-prompt.js +++ b/components/password-prompt.js @@ -102,6 +102,7 @@ export default class PasswordPrompt extends Component { onPress={() => { requestHash(this.state.password) }} + disabled={!this.state.password} > {labels.title} diff --git a/components/settings/password-setting.js b/components/settings/password-setting.js index 0b3a3fb..f8336ec 100644 --- a/components/settings/password-setting.js +++ b/components/settings/password-setting.js @@ -80,6 +80,7 @@ export default class PasswordSetting extends Component { requestHash(this.state.changedPassword) } }} + disabled={this.state.changingPassword && !this.state.changedPassword} style={styles.settingsButton}> {labels.passwordSettings.changePassword} @@ -123,6 +124,7 @@ export default class PasswordSetting extends Component { requestHash(this.state.newPassword) } }} + disabled={this.state.enteringNewPassword && !this.state.newPassword} style={styles.settingsButton}> {labels.passwordSettings.setPassword} From 905fb56dec2da0da3422e9da161f0f19e6ad8303 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Fri, 14 Sep 2018 18:16:21 +0200 Subject: [PATCH 25/37] Add long password reminder --- components/labels.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/labels.js b/components/labels.js index e8fad47..a3af834 100644 --- a/components/labels.js +++ b/components/labels.js @@ -61,8 +61,8 @@ export const settings = { deletePassword: 'Remove password protection', changePassword: 'Change password', enterNew: "Please enter a new password", - backupReminderTitle: 'Have you made a backup of your data?', - backupReminder: 'Just to be safe, please backup your data using the export function before making changes to your password.\n\nPlease also make sure you do not lose your password. There is no way to recover your data if you do.\n\nMaking any changes to your password setting will restart the app.', + backupReminderTitle: 'Read this before making changes to your password', + backupReminder: 'Just to be safe, please backup your data using the export function before making changes to your password.\n\nLonger passwords are better! Consider using a passphrase.\n\nPlease also make sure you do not lose your password. There is no way to recover your data if you do.\n\nMaking any changes to your password setting will restart the app.', } } From 6e3a3a30597bbbfcd64e2cc6630dda44dd0ca91b Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Fri, 14 Sep 2018 18:35:49 +0200 Subject: [PATCH 26/37] Remove duplicate view --- components/settings/index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/components/settings/index.js b/components/settings/index.js index c52f3ae..4a20916 100644 --- a/components/settings/index.js +++ b/components/settings/index.js @@ -30,9 +30,7 @@ export default class Settings extends Component { {labels.tempScale.segmentExplainer} - - - + {labels.export.button} From 1c5c6a804b78c06be3c6eda1145a24e4a97e261a Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Fri, 14 Sep 2018 18:57:55 +0200 Subject: [PATCH 27/37] Remove unneeded argument to openDb --- components/password-prompt.js | 4 ++-- db/index.js | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/components/password-prompt.js b/components/password-prompt.js index 78d1b93..d86ea6e 100644 --- a/components/password-prompt.js +++ b/components/password-prompt.js @@ -18,7 +18,7 @@ export default class PasswordPrompt extends Component { if (hasEncryption) { this.setState({showPasswordPrompt: true}) } else { - await openDb({persistConnection: true}) + await openDb() this.props.showApp() } }) @@ -34,7 +34,7 @@ export default class PasswordPrompt extends Component { msg = JSON.parse(msg) if (msg.type != 'sha512') return try { - await openDb({hash: msg.message, persistConnection: true }) + await openDb(msg.message) } catch (err) { Alert.alert( shared.incorrectPassword, diff --git a/db/index.js b/db/index.js index 4173c1d..9745327 100644 --- a/db/index.js +++ b/db/index.js @@ -161,14 +161,12 @@ export function requestHash(pw) { })) } -export async function openDb ({ hash, persistConnection }) { +export async function openDb (hash) { if (hash) { realmConfig.encryptionKey = hashToInt8Array(hash) } - const connection = await Realm.open(realmConfig) - - if (persistConnection) db = connection + db = await Realm.open(realmConfig) } export async function changeEncryptionAndRestartApp(hash) { @@ -197,7 +195,7 @@ export async function changeEncryptionAndRestartApp(hash) { export async function deleteDbAndOpenNew() { const exists = await fs.exists(Realm.defaultPath) if (exists) await fs.unlink(Realm.defaultPath) - await openDb({ persistConnection: true }) + await openDb() await saveEncryptionFlag(false) } From 8e8f6fcf6cd61753d080a22b02e928eaca9456ed Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Fri, 14 Sep 2018 19:03:18 +0200 Subject: [PATCH 28/37] Autofocus new password field --- components/settings/password-setting.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/settings/password-setting.js b/components/settings/password-setting.js index f8336ec..b9170e7 100644 --- a/components/settings/password-setting.js +++ b/components/settings/password-setting.js @@ -59,6 +59,7 @@ export default class PasswordSetting extends Component { { this.setState({ changedPassword: val @@ -102,6 +103,7 @@ export default class PasswordSetting extends Component { { this.setState({ newPassword: val From 9f11170f8bd028e5cef600dcdeee7bf11be173b1 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Fri, 14 Sep 2018 20:24:16 +0200 Subject: [PATCH 29/37] Bring back persist argument to openDb This reverts commit fc1028dba411711c46e3976693552632af4b9dc6. --- components/password-prompt.js | 4 ++-- db/index.js | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/components/password-prompt.js b/components/password-prompt.js index d86ea6e..78d1b93 100644 --- a/components/password-prompt.js +++ b/components/password-prompt.js @@ -18,7 +18,7 @@ export default class PasswordPrompt extends Component { if (hasEncryption) { this.setState({showPasswordPrompt: true}) } else { - await openDb() + await openDb({persistConnection: true}) this.props.showApp() } }) @@ -34,7 +34,7 @@ export default class PasswordPrompt extends Component { msg = JSON.parse(msg) if (msg.type != 'sha512') return try { - await openDb(msg.message) + await openDb({hash: msg.message, persistConnection: true }) } catch (err) { Alert.alert( shared.incorrectPassword, diff --git a/db/index.js b/db/index.js index 9745327..4173c1d 100644 --- a/db/index.js +++ b/db/index.js @@ -161,12 +161,14 @@ export function requestHash(pw) { })) } -export async function openDb (hash) { +export async function openDb ({ hash, persistConnection }) { if (hash) { realmConfig.encryptionKey = hashToInt8Array(hash) } - db = await Realm.open(realmConfig) + const connection = await Realm.open(realmConfig) + + if (persistConnection) db = connection } export async function changeEncryptionAndRestartApp(hash) { @@ -195,7 +197,7 @@ export async function changeEncryptionAndRestartApp(hash) { export async function deleteDbAndOpenNew() { const exists = await fs.exists(Realm.defaultPath) if (exists) await fs.unlink(Realm.defaultPath) - await openDb() + await openDb({ persistConnection: true }) await saveEncryptionFlag(false) } From 49b1110dd3763af0703b74cdcfdacaea39715be3 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Sat, 15 Sep 2018 10:34:02 +0200 Subject: [PATCH 30/37] Bring back check password flow This reverts commit a029ba8766315f9197f67f5141606077520dc584. --- components/labels.js | 4 +- components/settings/password-setting.js | 153 ------------------------ 2 files changed, 2 insertions(+), 155 deletions(-) delete mode 100644 components/settings/password-setting.js diff --git a/components/labels.js b/components/labels.js index a3af834..7f1657b 100644 --- a/components/labels.js +++ b/components/labels.js @@ -58,8 +58,8 @@ export const settings = { explainerDisabled: "Encrypt the app's database with a password. You need to enter the password every time the app is started.", explainerEnabled: "Password protection and database encryption is currently enabled", setPassword: 'Set password', - deletePassword: 'Remove password protection', - changePassword: 'Change password', + deletePassword: "Delete password", + enterCurrent: "Please enter your current password", enterNew: "Please enter a new password", backupReminderTitle: 'Read this before making changes to your password', backupReminder: 'Just to be safe, please backup your data using the export function before making changes to your password.\n\nLonger passwords are better! Consider using a passphrase.\n\nPlease also make sure you do not lose your password. There is no way to recover your data if you do.\n\nMaking any changes to your password setting will restart the app.', diff --git a/components/settings/password-setting.js b/components/settings/password-setting.js deleted file mode 100644 index b9170e7..0000000 --- a/components/settings/password-setting.js +++ /dev/null @@ -1,153 +0,0 @@ -import React, { Component } from 'react' -import { - View, - TouchableOpacity, - TextInput, - Alert -} from 'react-native' -import nodejs from 'nodejs-mobile-react-native' -import { AppText } from '../app-text' -import { - hasEncryptionObservable -} from '../../local-storage' -import styles from '../../styles/index' -import { settings as labels, shared } from '../labels' -import { requestHash, changeEncryptionAndRestartApp } from '../../db' - -export default class PasswordSetting extends Component { - constructor(props) { - super(props) - this.state = { - showUpdateAndDelete: hasEncryptionObservable.value, - showSetPassword: !hasEncryptionObservable.value, - settingNewPassword: false, - changingPassword: false - } - - nodejs.channel.addListener( - 'message', - this.passHashToDb, - this - ) - } - - componentWillUnmount() { - nodejs.channel.removeListener('message', this.passHashToDb) - } - - passHashToDb = async (msg) => { - msg = JSON.parse(msg) - if (msg.type != 'sha512') return - await changeEncryptionAndRestartApp(msg.message) - } - - render() { - return ( - - - {labels.passwordSettings.title} - - {this.state.showUpdateAndDelete ? - {labels.passwordSettings.explainerEnabled} - : - {labels.passwordSettings.explainerDisabled} - } - - {this.state.showUpdateAndDelete && - - {this.state.changingPassword && - - { - this.setState({ - changedPassword: val - }) - }} - value={this.state.changedPassword} - placeholder={labels.passwordSettings.enterNew} - secureTextEntry={true} - /> - - } - { - if (!this.state.changingPassword) { - showBackUpReminder(() => { - this.setState({ changingPassword: true }) - }) - } else { - requestHash(this.state.changedPassword) - } - }} - disabled={this.state.changingPassword && !this.state.changedPassword} - style={styles.settingsButton}> - - {labels.passwordSettings.changePassword} - - - { - showBackUpReminder(() => changeEncryptionAndRestartApp()) - }} - style={styles.settingsButton}> - - {labels.passwordSettings.deletePassword} - - - - } - - {this.state.enteringNewPassword && - - { - this.setState({ - newPassword: val - }) - }} - value={this.state.newPassword} - placeholder={labels.passwordSettings.enterNew} - secureTextEntry={true} - /> - - } - {this.state.showSetPassword && - { - if (!this.state.enteringNewPassword) { - showBackUpReminder(() => { - this.setState({ enteringNewPassword: true }) - }) - } else { - requestHash(this.state.newPassword) - } - }} - disabled={this.state.enteringNewPassword && !this.state.newPassword} - style={styles.settingsButton}> - - {labels.passwordSettings.setPassword} - - - } - - ) - } -} - -function showBackUpReminder(okHandler) { - Alert.alert( - labels.passwordSettings.backupReminderTitle, - labels.passwordSettings.backupReminder, - [{ - text: shared.cancel, - style: 'cancel' - }, { - text: shared.ok, - onPress: okHandler - }] - ) -} \ No newline at end of file From 80f08c0642bce62b5d818855b7a3df7171e46d47 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Sat, 15 Sep 2018 10:34:46 +0200 Subject: [PATCH 31/37] Don't rely on local storage knowing about encryption state --- components/password-prompt.js | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/components/password-prompt.js b/components/password-prompt.js index 78d1b93..6ff5904 100644 --- a/components/password-prompt.js +++ b/components/password-prompt.js @@ -1,8 +1,8 @@ import React, { Component } from 'react' import { View, TextInput, TouchableOpacity, Alert, Image } from 'react-native' import nodejs from 'nodejs-mobile-react-native' +import { saveEncryptionFlag } from '../local-storage' import { AppText } from './app-text' -import { hasEncryptionObservable } from '../local-storage' import styles from '../styles' import { passwordPrompt as labels, shared } from './labels' import { requestHash, deleteDbAndOpenNew, openDb } from '../db' @@ -13,21 +13,25 @@ export default class PasswordPrompt extends Component { this.state = { password: null } - hasEncryptionObservable.once(async hasEncryption => { - hasEncryption = JSON.parse(hasEncryption) - if (hasEncryption) { - this.setState({showPasswordPrompt: true}) - } else { - await openDb({persistConnection: true}) - this.props.showApp() - } - }) nodejs.channel.addListener( 'message', this.passHashToDb, this ) + + this.tryToOpenDb() + } + + async tryToOpenDb() { + try { + await openDb({ persistConnection: true }) + await saveEncryptionFlag(false) + } catch (err) { + this.setState({ showPasswordPrompt: true }) + await saveEncryptionFlag(true) + return + } } passHashToDb = async msg => { From 8ed26f3e5947e8b8c955748071a23ecbdaf3e221 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Sat, 15 Sep 2018 11:22:00 +0200 Subject: [PATCH 32/37] Pull password settings apart into individual components --- components/labels.js | 6 +- components/password-prompt.js | 16 +-- components/settings/index.js | 2 +- .../password/check-current-password.js | 23 ++++ components/settings/password/create.js | 61 +++++++++ components/settings/password/delete.js | 76 +++++++++++ components/settings/password/index.js | 50 +++++++ .../settings/password/password-field.js | 16 +++ .../settings/password/show-backup-reminder.js | 16 +++ components/settings/password/update.js | 129 ++++++++++++++++++ db/index.js | 9 +- nodejs-assets/nodejs-project/main.js | 15 +- 12 files changed, 392 insertions(+), 27 deletions(-) create mode 100644 components/settings/password/check-current-password.js create mode 100644 components/settings/password/create.js create mode 100644 components/settings/password/delete.js create mode 100644 components/settings/password/index.js create mode 100644 components/settings/password/password-field.js create mode 100644 components/settings/password/show-backup-reminder.js create mode 100644 components/settings/password/update.js diff --git a/components/labels.js b/components/labels.js index 7f1657b..7c38808 100644 --- a/components/labels.js +++ b/components/labels.js @@ -7,7 +7,8 @@ export const shared = { incorrectPassword: 'Password incorrect', incorrectPasswordMessage: 'That password is incorrect.', tryAgain: 'Try again', - ok: 'OK' + ok: 'OK', + unlock: 'Unlock' } export const settings = { @@ -58,7 +59,8 @@ export const settings = { explainerDisabled: "Encrypt the app's database with a password. You need to enter the password every time the app is started.", explainerEnabled: "Password protection and database encryption is currently enabled", setPassword: 'Set password', - deletePassword: "Delete password", + changePassword: 'Change password', + deletePassword: 'Delete password', enterCurrent: "Please enter your current password", enterNew: "Please enter a new password", backupReminderTitle: 'Read this before making changes to your password', diff --git a/components/password-prompt.js b/components/password-prompt.js index 6ff5904..8e4a719 100644 --- a/components/password-prompt.js +++ b/components/password-prompt.js @@ -15,7 +15,7 @@ export default class PasswordPrompt extends Component { } nodejs.channel.addListener( - 'message', + 'check-pw', this.passHashToDb, this ) @@ -27,6 +27,7 @@ export default class PasswordPrompt extends Component { try { await openDb({ persistConnection: true }) await saveEncryptionFlag(false) + this.props.showApp() } catch (err) { this.setState({ showPasswordPrompt: true }) await saveEncryptionFlag(true) @@ -34,18 +35,16 @@ export default class PasswordPrompt extends Component { } } - passHashToDb = async msg => { - msg = JSON.parse(msg) - if (msg.type != 'sha512') return + passHashToDb = async hash => { try { - await openDb({hash: msg.message, persistConnection: true }) + await openDb({ hash, persistConnection: true }) } catch (err) { Alert.alert( shared.incorrectPassword, shared.incorrectPasswordMessage, [{ text: shared.tryAgain, - onPress: () => this.setState({password: null}) + onPress: () => this.setState({ password: null }) }] ) return @@ -73,6 +72,7 @@ export default class PasswordPrompt extends Component { text: labels.reallyDeleteData, onPress: async () => { await deleteDbAndOpenNew() + await saveEncryptionFlag(false) this.props.showApp() } }] @@ -83,7 +83,7 @@ export default class PasswordPrompt extends Component { } componentWillUnmount() { - nodejs.channel.removeListener('message', this.passHashToDb) + nodejs.channel.removeListener('check-pw', this.passHashToDb) } render() { @@ -104,7 +104,7 @@ export default class PasswordPrompt extends Component { { - requestHash(this.state.password) + requestHash('check-pw', this.state.password) }} disabled={!this.state.password} > diff --git a/components/settings/index.js b/components/settings/index.js index 4a20916..e0828d8 100644 --- a/components/settings/index.js +++ b/components/settings/index.js @@ -11,7 +11,7 @@ import TempReminderPicker from './temp-reminder-picker' import TempSlider from './temp-slider' import openImportDialogAndImport from './import-dialog' import openShareDialogAndExport from './export-dialog' -import PasswordSetting from './password-setting' +import PasswordSetting from './password' export default class Settings extends Component { constructor(props) { diff --git a/components/settings/password/check-current-password.js b/components/settings/password/check-current-password.js new file mode 100644 index 0000000..b177de4 --- /dev/null +++ b/components/settings/password/check-current-password.js @@ -0,0 +1,23 @@ +import { Alert } from 'react-native' +import { openDb } from '../../../db' +import { shared } from '../../labels' + +export default async function checkPassword({hash, onCancel, onTryAgain }) { + try { + await openDb({ hash, persistConnection: false }) + 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 new file mode 100644 index 0000000..97559ee --- /dev/null +++ b/components/settings/password/create.js @@ -0,0 +1,61 @@ +import React, { Component } from 'react' +import { + View, + TouchableOpacity, +} from 'react-native' +import nodejs from 'nodejs-mobile-react-native' +import { AppText } from '../../app-text' +import styles from '../../../styles' +import { settings as labels } from '../../labels' +import { requestHash, changeEncryptionAndRestartApp } from '../../../db' +import PasswordField from './password-field' +import showBackUpReminder from './show-backup-reminder' + +export default class CreatePassword extends Component { + constructor() { + super() + this.state = { + enteringNewPassword: false, + newPassword: null + } + nodejs.channel.addListener( + 'create-pw-hash', + changeEncryptionAndRestartApp, + this + ) + } + + componentWillUnmount() { + nodejs.channel.removeListener('create-pw-hash', changeEncryptionAndRestartApp) + } + + render () { + return ( + + {this.state.enteringNewPassword && + this.setState({newPassword: val})} + /> + } + { + if (!this.state.enteringNewPassword) { + showBackUpReminder(() => { + this.setState({ enteringNewPassword: true }) + }) + } else { + requestHash('create-pw-hash', this.state.newPassword) + } + }} + disabled={this.state.enteringNewPassword && !this.state.newPassword} + style={styles.settingsButton}> + + {labels.passwordSettings.setPassword} + + + + ) + } +} \ No newline at end of file diff --git a/components/settings/password/delete.js b/components/settings/password/delete.js new file mode 100644 index 0000000..0673961 --- /dev/null +++ b/components/settings/password/delete.js @@ -0,0 +1,76 @@ +import React, { Component } from 'react' +import { + View, + TouchableOpacity +} from 'react-native' +import nodejs from 'nodejs-mobile-react-native' +import { AppText } from '../../app-text' +import styles from '../../../styles' +import { settings as labels } from '../../labels' +import { requestHash, changeEncryptionAndRestartApp } from '../../../db' +import PasswordField from './password-field' +import showBackUpReminder from './show-backup-reminder' +import checkCurrentPassword from './check-current-password' + +export default class DeletePassword extends Component { + constructor() { + super() + this.state = { + enteringCurrentPassword: false, + currentPassword: null + } + + nodejs.channel.addListener( + 'pre-delete-pw-check', + this.removeEncryption, + this + ) + } + + componentWillUnmount() { + nodejs.channel.removeListener('pre-delete-pw-check', this.removeEncryption) + } + + removeEncryption = async hash => { + const passwordIsCorrect = await checkCurrentPassword({ + hash, + onTryAgain: () => this.setState({currentPassword: null}), + onCancel: () => this.setState({ + enteringCurrentPassword: false, + currentPassword: null + }) + }) + + if (passwordIsCorrect) await changeEncryptionAndRestartApp() + } + + render() { + return ( + + {this.state.enteringCurrentPassword && + this.setState({ currentPassword: val })} + value={this.state.currentPassword} + placeholder={labels.passwordSettings.enterCurrent} + /> + } + { + if (!this.state.enteringCurrentPassword) { + showBackUpReminder(() => { + this.setState({ enteringCurrentPassword: true }) + }) + } else { + requestHash('pre-delete-pw-check', this.state.currentPassword) + } + }} + style={styles.settingsButton} + > + + {labels.passwordSettings.deletePassword} + + + + ) + } +} \ No newline at end of file diff --git a/components/settings/password/index.js b/components/settings/password/index.js new file mode 100644 index 0000000..87ece3d --- /dev/null +++ b/components/settings/password/index.js @@ -0,0 +1,50 @@ +import React, { Component } from 'react' +import { View } from 'react-native' +import CreatePassword from './create' +import ChangePassword from './update' +import DeletePassword from './delete' +import { AppText } from '../../app-text' +import { + hasEncryptionObservable +} from '../../../local-storage' +import styles from '../../../styles/index' +import { settings as labels } from '../../labels' + +export default class PasswordSetting extends Component { + constructor(props) { + super(props) + this.state = { + showUpdateAndDelete: hasEncryptionObservable.value, + showCreate: !hasEncryptionObservable.value + } + } + + render() { + return ( + + + + {labels.passwordSettings.title} + + + {this.state.showUpdateAndDelete ? + {labels.passwordSettings.explainerEnabled} + : + {labels.passwordSettings.explainerDisabled} + } + + {this.state.showUpdateAndDelete && + + + + + } + + {this.state.showCreate && + + } + + + ) + } +} \ No newline at end of file diff --git a/components/settings/password/password-field.js b/components/settings/password/password-field.js new file mode 100644 index 0000000..7193566 --- /dev/null +++ b/components/settings/password/password-field.js @@ -0,0 +1,16 @@ +import React from 'react' +import { TextInput } from 'react-native' +import styles from '../../../styles' + +export default function PasswordField(props) { + return ( + + ) +} diff --git a/components/settings/password/show-backup-reminder.js b/components/settings/password/show-backup-reminder.js new file mode 100644 index 0000000..ea50dcc --- /dev/null +++ b/components/settings/password/show-backup-reminder.js @@ -0,0 +1,16 @@ +import { Alert } from 'react-native' +import { settings as labels, shared } from '../../labels' + +export default function showBackUpReminder(okHandler) { + Alert.alert( + labels.passwordSettings.backupReminderTitle, + labels.passwordSettings.backupReminder, + [{ + text: shared.cancel, + style: 'cancel' + }, { + text: shared.ok, + onPress: okHandler + }] + ) +} \ No newline at end of file diff --git a/components/settings/password/update.js b/components/settings/password/update.js new file mode 100644 index 0000000..12af518 --- /dev/null +++ b/components/settings/password/update.js @@ -0,0 +1,129 @@ + +import React, { Component } from 'react' +import { + View, + TouchableOpacity} from 'react-native' +import nodejs from 'nodejs-mobile-react-native' +import { AppText } from '../../app-text' +import styles from '../../../styles' +import { settings as labels, shared } from '../../labels' +import { requestHash, changeEncryptionAndRestartApp } from '../../../db' +import PasswordField from './password-field' +import showBackUpReminder from './show-backup-reminder' +import checkCurrentPassword from './check-current-password' + +export default class ChangePassword extends Component { + constructor() { + super() + this.state = { + enteringCurrentPassword: false, + currentPassword: null, + enteringNewPassword: false, + newPassword: null + } + + nodejs.channel.addListener( + 'pre-change-pw-check', + this.openNewPasswordField, + this + ) + + nodejs.channel.addListener( + 'change-pw', + changeEncryptionAndRestartApp, + this + ) + } + + componentWillUnmount() { + nodejs.channel.removeListener('pre-change-pw-check', this.openNewPasswordField) + nodejs.channel.removeListener('change-pw', changeEncryptionAndRestartApp) + } + + openNewPasswordField = async hash => { + const passwordCorrect = await checkCurrentPassword({ + hash, + onTryAgain: () => this.setState({ currentPassword: null }), + onCancel: () => this.setState({ + enteringCurrentPassword: false, + currentPassword: null + }) + }) + + if (passwordCorrect) { + this.setState({ + enteringCurrentPassword: false, + currentPassword: null, + enteringNewPassword: true + }) + } + } + + render() { + return ( + + {!this.state.enteringCurrentPassword && + !this.state.enteringNewPassword && + showBackUpReminder(() => { + this.setState({ enteringCurrentPassword: true }) + })} + disabled={this.state.currentPassword} + style={styles.settingsButton}> + + {labels.passwordSettings.changePassword} + + + } + + {this.state.enteringCurrentPassword && + + { + this.setState({ + currentPassword: val, + wrongPassword: false + }) + }} + value={this.state.currentPassword} + placeholder={labels.passwordSettings.enterCurrent} + /> + requestHash('pre-change-pw-check', this.state.currentPassword)} + disabled={!this.state.currentPassword} + style={styles.settingsButton}> + + {shared.unlock} + + + + } + + {this.state.enteringNewPassword && + + { + this.setState({ + newPassword: val + }) + }} + value={this.state.changedPassword} + placeholder={labels.passwordSettings.enterNew} + /> + + requestHash('change-pw', this.state.newPassword)} + disabled={ !this.state.newPassword } + style={styles.settingsButton}> + + {labels.passwordSettings.changePassword} + + + + } + + + ) + } +} \ No newline at end of file diff --git a/db/index.js b/db/index.js index 4173c1d..6dc4feb 100644 --- a/db/index.js +++ b/db/index.js @@ -11,7 +11,6 @@ import { longAndComplicatedCycleWithCervix, cycleWithTempAndNoCervixShift } from './fixtures' -import { saveEncryptionFlag } from '../local-storage' import dbSchema from './schema' let db @@ -154,9 +153,9 @@ export function tryToImportWithoutDelete(cycleDays) { }) } -export function requestHash(pw) { - nodejs.channel.send(JSON.stringify({ - type: 'request-SHA512', +export function requestHash(type, pw) { + nodejs.channel.post('request-SHA512', JSON.stringify({ + type: type, message: pw })) } @@ -190,7 +189,6 @@ export async function changeEncryptionAndRestartApp(hash) { db.close() await fs.unlink(defaultPath) await fs.moveFile(copyPath, defaultPath) - await saveEncryptionFlag(key ? true : false) restart.Restart() } @@ -198,7 +196,6 @@ export async function deleteDbAndOpenNew() { const exists = await fs.exists(Realm.defaultPath) if (exists) await fs.unlink(Realm.defaultPath) await openDb({ persistConnection: true }) - await saveEncryptionFlag(false) } function hashToInt8Array(hash) { diff --git a/nodejs-assets/nodejs-project/main.js b/nodejs-assets/nodejs-project/main.js index 20227c7..616196d 100644 --- a/nodejs-assets/nodejs-project/main.js +++ b/nodejs-assets/nodejs-project/main.js @@ -3,15 +3,10 @@ const rnBridge = require('rn-bridge') const crypto = require('crypto') -rnBridge.channel.on('message', (msg) => { +rnBridge.channel.on('request-SHA512', (msg) => { msg = JSON.parse(msg) - if (msg.type === 'request-SHA512') { - const hash = crypto.createHash('sha512') - hash.update(msg.message) - const result = hash.digest('hex') - rnBridge.channel.send(JSON.stringify({ - type: 'sha512', - message: result - })) - } + const hash = crypto.createHash('sha512') + hash.update(msg.message) + const result = hash.digest('hex') + rnBridge.channel.post(msg.type, result) }) \ No newline at end of file From a15f1177be549ab19117d88e0d31b7ba8d772304 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Mon, 17 Sep 2018 15:26:38 +0200 Subject: [PATCH 33/37] Disbale delete pw when current one not given --- components/settings/password/delete.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/settings/password/delete.js b/components/settings/password/delete.js index 0673961..c4edf40 100644 --- a/components/settings/password/delete.js +++ b/components/settings/password/delete.js @@ -64,6 +64,10 @@ export default class DeletePassword extends Component { requestHash('pre-delete-pw-check', this.state.currentPassword) } }} + disabled={ + this.state.enteringCurrentPassword && + !this.state.currentPassword + } style={styles.settingsButton} > From d87a8567546a5d487203e098b9b1b1dbab2001b3 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Mon, 17 Sep 2018 15:37:56 +0200 Subject: [PATCH 34/37] Attach and remove listeners from same db collection --- components/calendar.js | 10 +++++----- components/home.js | 25 ++++++++++++------------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/components/calendar.js b/components/calendar.js index 74b754b..d9a0fd9 100644 --- a/components/calendar.js +++ b/components/calendar.js @@ -9,11 +9,11 @@ import styles from '../styles/index' export default class CalendarView extends Component { constructor(props) { - const bleedingDaysSortedByDate = getBleedingDaysSortedByDate() super(props) + this.bleedingDays = getBleedingDaysSortedByDate() const predictedMenses = cycleModule().getPredictedMenses() this.state = { - bleedingDaysInCalFormat: toCalFormat(bleedingDaysSortedByDate), + bleedingDaysInCalFormat: toCalFormat(this.bleedingDays), predictedBleedingDaysInCalFormat: predictionToCalFormat(predictedMenses), todayInCalFormat: todayToCalFormat() } @@ -22,18 +22,18 @@ export default class CalendarView extends Component { return function() { const predictedMenses = cycleModule().getPredictedMenses() CalendarComponent.setState({ - bleedingDaysInCalFormat: toCalFormat(bleedingDaysSortedByDate), + bleedingDaysInCalFormat: toCalFormat(this.bleedingDays), predictedBleedingDaysInCalFormat: predictionToCalFormat(predictedMenses), todayInCalFormat: todayToCalFormat() }) } })(this) - bleedingDaysSortedByDate.addListener(this.setStateWithCalFormattedDays) + this.bleedingDays.addListener(this.setStateWithCalFormattedDays) } componentWillUnmount() { - getBleedingDaysSortedByDate().removeListener(this.setStateWithCalFormattedDays) + this.bleedingDays.removeListener(this.setStateWithCalFormattedDays) } passDateToDayView = (result) => { diff --git a/components/home.js b/components/home.js index 025a77b..fffe693 100644 --- a/components/home.js +++ b/components/home.js @@ -14,30 +14,29 @@ import {bleedingPrediction as labels} from './labels' export default class Home extends Component { constructor(props) { super(props) - const getCycleDayNumber = cycleModule().getCycleDayNumber + this.getCycleDayNumber = cycleModule().getCycleDayNumber this.todayDateString = LocalDate.now().toString() - const cycleDayNumber = getCycleDayNumber(this.todayDateString) + const cycleDayNumber = this.getCycleDayNumber(this.todayDateString) this.state = { welcomeText: determineWelcomeText(cycleDayNumber), predictionText: determinePredictionText() } - this.setStateWithCurrentText = (function (HomeComponent) { - return function () { - const cycleDayNumber = getCycleDayNumber(HomeComponent.todayDateString) - HomeComponent.setState({ - welcomeText: determineWelcomeText(cycleDayNumber), - predictionText: determinePredictionText() - }) - } - })(this) + this.bleedingDays = getBleedingDaysSortedByDate() + this.bleedingDays.addListener(this.setStateWithCurrentText) + } - getBleedingDaysSortedByDate().addListener(this.setStateWithCurrentText) + setStateWithCurrentText = () => { + const cycleDayNumber = this.getCycleDayNumber(this.todayDateString) + this.setState({ + welcomeText: determineWelcomeText(cycleDayNumber), + predictionText: determinePredictionText() + }) } componentWillUnmount() { - getBleedingDaysSortedByDate().removeListener(this.setStateWithCurrentText) + this.bleedingDays.removeListener(this.setStateWithCurrentText) } passTodayToDayView() { From d8c8f5034e1dc8708a3a6c68883f7cfaaefe6c5f Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Mon, 17 Sep 2018 16:01:21 +0200 Subject: [PATCH 35/37] Simplify calendar recalculate method --- components/calendar.js | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/components/calendar.js b/components/calendar.js index d9a0fd9..4ffce88 100644 --- a/components/calendar.js +++ b/components/calendar.js @@ -18,20 +18,18 @@ export default class CalendarView extends Component { todayInCalFormat: todayToCalFormat() } - this.setStateWithCalFormattedDays = (function (CalendarComponent) { - return function() { - const predictedMenses = cycleModule().getPredictedMenses() - CalendarComponent.setState({ - bleedingDaysInCalFormat: toCalFormat(this.bleedingDays), - predictedBleedingDaysInCalFormat: predictionToCalFormat(predictedMenses), - todayInCalFormat: todayToCalFormat() - }) - } - })(this) - this.bleedingDays.addListener(this.setStateWithCalFormattedDays) } + setStateWithCalFormattedDays = () => { + const predictedMenses = cycleModule().getPredictedMenses() + this.setState({ + bleedingDaysInCalFormat: toCalFormat(this.bleedingDays), + predictedBleedingDaysInCalFormat: predictionToCalFormat(predictedMenses), + todayInCalFormat: todayToCalFormat() + }) + } + componentWillUnmount() { this.bleedingDays.removeListener(this.setStateWithCalFormattedDays) } From 9cc9cb6d3e5cd2017152a57b596a078d04884290 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Mon, 17 Sep 2018 16:37:54 +0200 Subject: [PATCH 36/37] Move irrelevant stuff out of try block --- components/password-prompt.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/password-prompt.js b/components/password-prompt.js index 8e4a719..ce91221 100644 --- a/components/password-prompt.js +++ b/components/password-prompt.js @@ -26,13 +26,14 @@ export default class PasswordPrompt extends Component { async tryToOpenDb() { try { await openDb({ persistConnection: true }) - await saveEncryptionFlag(false) - this.props.showApp() } catch (err) { this.setState({ showPasswordPrompt: true }) await saveEncryptionFlag(true) return } + + await saveEncryptionFlag(false) + this.props.showApp() } passHashToDb = async hash => { From dc5d3c3928ca5daa229985373f9dbc3cd4681cfa Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Tue, 18 Sep 2018 08:08:48 +0200 Subject: [PATCH 37/37] Change backup warning for delete --- components/labels.js | 4 +++- components/settings/password/delete.js | 2 +- .../settings/password/show-backup-reminder.js | 15 ++++++++++++--- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/components/labels.js b/components/labels.js index 7c38808..7fde2b8 100644 --- a/components/labels.js +++ b/components/labels.js @@ -64,7 +64,9 @@ export const settings = { enterCurrent: "Please enter your current password", enterNew: "Please enter a new password", backupReminderTitle: 'Read this before making changes to your password', - backupReminder: 'Just to be safe, please backup your data using the export function before making changes to your password.\n\nLonger passwords are better! Consider using a passphrase.\n\nPlease also make sure you do not lose your password. There is no way to recover your data if you do.\n\nMaking any changes to your password setting will restart the app.', + backupReminder: 'Just to be safe, please backup your data using the export function before making changes to your password.\n\nLonger passwords are better! Consider using a passphrase.\n\nPlease also make sure you do not lose your password. There is no way to recover your data if you do.\n\nMaking any changes to your password setting will keep your data as it was before and restart the app.', + deleteBackupReminderTitle: 'Read this before deleting your password', + deleteBackupReminder: 'Deleting your password means your data will no longer be encrypted.\n\nJust to be safe, please backup your data using the export function before deleting your password.\n\nMaking any changes to your password setting will keep your data as it was before and restart the app.', } } diff --git a/components/settings/password/delete.js b/components/settings/password/delete.js index c4edf40..58666f5 100644 --- a/components/settings/password/delete.js +++ b/components/settings/password/delete.js @@ -59,7 +59,7 @@ export default class DeletePassword extends Component { if (!this.state.enteringCurrentPassword) { showBackUpReminder(() => { this.setState({ enteringCurrentPassword: true }) - }) + }, true) } else { requestHash('pre-delete-pw-check', this.state.currentPassword) } diff --git a/components/settings/password/show-backup-reminder.js b/components/settings/password/show-backup-reminder.js index ea50dcc..11a7d83 100644 --- a/components/settings/password/show-backup-reminder.js +++ b/components/settings/password/show-backup-reminder.js @@ -1,10 +1,19 @@ import { Alert } from 'react-native' import { settings as labels, shared } from '../../labels' -export default function showBackUpReminder(okHandler) { +export default function showBackUpReminder(okHandler, isDelete) { + let title, message + if (isDelete) { + title = labels.passwordSettings.deleteBackupReminderTitle + message = labels.passwordSettings.deleteBackupReminder + } else { + title = labels.passwordSettings.backupReminderTitle + message = labels.passwordSettings.backupReminder + } + Alert.alert( - labels.passwordSettings.backupReminderTitle, - labels.passwordSettings.backupReminder, + title, + message, [{ text: shared.cancel, style: 'cancel'