From 09293b88d6168be8c521529dd7f97a1f6ee85887 Mon Sep 17 00:00:00 2001 From: greenyossi Date: Sun, 19 Apr 2020 17:30:44 +0300 Subject: [PATCH] Feature/improved location tracking and time intersection algorithms (#195) - move localization to server based data (fallback to local data). - missing some typing fixes. - moved also notifications and external links to texts. - algorithm done. - moved logic to service. - moved params to config. - initiate config in background services. - fixed change language title issue. - added scroll to change language. - fixed missing short language text for added language. - fixed google-services mismatch. - removed unnecessary code. - fixed failed tests. - fixed eslint configuration. - added close option for 'not forced' version update. - replaced to animated hamagen logo in main screen. - fixed 'no data' screen not showing although GPS is off. - fixed logout from google if getting timeline data fails. - added auto retry to avoid failed timeline fetch on first try in iOS. - fix force update to not show if app version > server version. - add scheme to run ios script. - mock native modules - removed firebase analytics. - fixed exposure to show the latest between sick startTime and user startTime. - exposures flow screen UI refactor. - fixed foreground timer interval. - add WifiMacAddressDatabase, UserLocationsDatabase, IntersectionSickDatabase mockImplementation to setup file - tracker test pass again - fix for HV points overriding the last point in DB. - fix for unignored sample locations. - move firebase mock from setup to __mocks__ - fixed transistor SDK ignore params. - fixed wrong velocity calculations. - MC-95. - MC-94. - MC-7. - MC-33. - MC-36. - MC-93. - updateDBAccordingToSampleVelocity UT - extened Database mock - add check for onError functon to not call - accessibility fixes. - fixed #159. - fixed #155. - fixed #177. - fixed #150, #160. - updated texts file. - check with v2 force updates params from server. - crash fix. - disabled hermes. - added headless task to background geolocation. - move error check to afterAll function and add with multiple points from db test - Add test to SampleService (#186) - add scheme to run ios script - mock native modules - linting, removing unnecessary code. - move redux-mock-store to dev dependencies - fixed google timeline logout. - clear cookies after flow completed (whatever the result is). - add locale action test - fix for failed tests. - fix for BG geolocation headless callback. - improved BG geolocation config. - bumped versions. - another fix to BG geolocation config. - add axios simple mock - updated texts. - add axios mock - ignore locations with timestamp earlier then the last location saved. - add moment mock - add LocationHistoryService test start - insertToSampleDB tests - add sha256 mock - change store dispatch to return value only in tracker test - LocationHistoryService test and new mocks - fix updateDBAccordingToSampleVelocity failing tests - updated config. Co-authored-by: YossiGreen <> Co-authored-by: Sagiv Stekolshik Co-authored-by: sagivStekolshik Co-authored-by: SagivOnoApps <63947020+SagivOnoApps@users.noreply.github.com> --- .eslintrc | 5 +- __mocks__/axios.js | 12 + __mocks__/latlon-geohash.js | 7 + __mocks__/moment.js | 21 + .../react-native-background-geolocation.ts | 10 + __mocks__/react-native-firebase.ts | 34 ++ __mocks__/react-native-permissions.ts | 20 + __mocks__/react-native-sqlite-storage.ts | 10 + __mocks__/setupFile.js | 138 ++++-- android/app/build.gradle | 20 +- android/app/src/debug/google-services.json | 95 ++++ android/app/src/debug/res/values/strings.xml | 2 +- .../java/com/hamagen/MainApplication.java | 7 - android/app/src/qa/google-services.json | 58 +++ android/app/src/qa/res/values/strings.xml | 2 +- index.js | 29 +- ios/Podfile | 2 +- ios/Podfile.lock | 24 +- .../project.pbxproj | 16 +- ios/codeAgainstCorona/Info-qa.plist | 2 + package.json | 11 +- src/actions/GeneralActions.ts | 10 +- src/actions/LocaleActions.ts | 57 ++- .../ExposuresActions.test.ts} | 37 +- src/actions/__tests__/LocaleActions.test.ts | 120 +++++ src/assets/lottie/magen logo.json | 2 +- src/assets/onboarding/driving.png | Bin 0 -> 11169 bytes src/assets/pdf/usage-ar.pdf | Bin 61063 -> 0 bytes src/assets/pdf/usage-eng.pdf | Bin 55104 -> 0 bytes src/assets/pdf/usage-ru.pdf | Bin 49622 -> 0 bytes src/assets/pdf/usage.pdf | Bin 45190 -> 0 bytes src/components/Loading.tsx | 39 +- src/components/Main/ExposureInstructions.tsx | 84 ++-- src/components/Main/ExposuresDetected.tsx | 55 ++- .../ExposuresHistory/ExposuresHistory.tsx | 18 +- .../Main/FilterDriving/FilterDriving.tsx | 82 ++++ .../Main/LocationHistory/LocationHistory.tsx | 5 +- src/components/Main/NoData.tsx | 3 +- src/components/Main/NoExposures.tsx | 18 +- src/components/Main/ScanHome.tsx | 35 +- src/components/Main/ScanHomeHeader.tsx | 4 +- src/components/Onboarding/AllSet.tsx | 14 +- .../Onboarding/FilterDrivingOnBoarding.tsx | 81 +++ src/components/Onboarding/Location.tsx | 40 +- .../Onboarding/LocationHistoryOnBoarding.tsx | 3 +- src/components/Onboarding/LocationIOS.tsx | 5 +- src/components/Onboarding/Notifications.tsx | 3 +- src/components/Onboarding/Welcome.tsx | 5 +- src/components/common/ChangeLanguage.tsx | 66 +-- .../common/ChangeLanguageButton.tsx | 20 +- src/components/common/CloseButton.tsx | 45 ++ src/components/common/ForceTerms.tsx | 3 +- src/components/common/ForceUpdate.tsx | 22 +- src/components/common/GeneralWebview.tsx | 12 +- src/components/common/GoogleTimeLine.tsx | 50 +- src/components/common/TermsOfUse.tsx | 17 +- src/components/common/Text.tsx | 2 +- src/components/common/WebviewHeader.tsx | 12 +- src/components/common/index.ts | 1 + src/config/config.ts | 4 +- src/config/default_config.json | 43 +- src/constants/ActionTypes.ts | 1 + src/constants/Constants.ts | 2 + src/database/Database.js | 23 + src/locale/LocaleData.ts | 181 ++++++- src/locale/texts.json | 271 ++++++++-- src/model/LocaleData.ts | 96 ---- src/reducers/GeneralReducer.ts | 10 +- src/reducers/LocaleReducer.ts | 25 +- src/services/AnalyticsService.ts | 5 - src/services/BackgroundService.ts | 12 +- src/services/LocationHistoryService.ts | 8 +- src/services/LocationService.ts | 89 +++- src/services/NavigationService.ts | 3 - src/services/SampleService.ts | 126 ++++- src/services/ToGeoJson.js | 461 ------------------ src/services/Tracker.ts | 88 ++-- .../__tests__/LocationHistoryService.test.ts | 184 +++++++ src/services/__tests__/SampleService.test.ts | 147 ++++++ src/services/{ => __tests__}/Tracker.test.ts | 186 +++++-- src/types/index.ts | 93 ++-- yarn.lock | 39 +- 82 files changed, 2445 insertions(+), 1147 deletions(-) create mode 100644 __mocks__/axios.js create mode 100644 __mocks__/latlon-geohash.js create mode 100644 __mocks__/moment.js create mode 100644 __mocks__/react-native-background-geolocation.ts create mode 100644 __mocks__/react-native-firebase.ts create mode 100644 __mocks__/react-native-permissions.ts create mode 100644 __mocks__/react-native-sqlite-storage.ts rename src/actions/{ExposuresActions.test.js => __tests__/ExposuresActions.test.ts} (70%) create mode 100644 src/actions/__tests__/LocaleActions.test.ts create mode 100644 src/assets/onboarding/driving.png delete mode 100644 src/assets/pdf/usage-ar.pdf delete mode 100644 src/assets/pdf/usage-eng.pdf delete mode 100644 src/assets/pdf/usage-ru.pdf delete mode 100644 src/assets/pdf/usage.pdf create mode 100644 src/components/Main/FilterDriving/FilterDriving.tsx create mode 100644 src/components/Onboarding/FilterDrivingOnBoarding.tsx create mode 100644 src/components/common/CloseButton.tsx delete mode 100644 src/model/LocaleData.ts delete mode 100644 src/services/AnalyticsService.ts delete mode 100755 src/services/NavigationService.ts delete mode 100644 src/services/ToGeoJson.js create mode 100644 src/services/__tests__/LocationHistoryService.test.ts create mode 100644 src/services/__tests__/SampleService.test.ts rename src/services/{ => __tests__}/Tracker.test.ts (58%) diff --git a/.eslintrc b/.eslintrc index 36a4b73a..4fe2738b 100755 --- a/.eslintrc +++ b/.eslintrc @@ -51,5 +51,8 @@ "plugins": [ "react-hooks" ], - "globals": { "fetch": false } + "globals": { "fetch": false }, + "env": { + "jest": true + } } diff --git a/__mocks__/axios.js b/__mocks__/axios.js new file mode 100644 index 00000000..02d96772 --- /dev/null +++ b/__mocks__/axios.js @@ -0,0 +1,12 @@ +// import mockAxios from 'jest-mock-axios'; +const axios = { + get: jest.fn(), + post: jest.fn() +} + +axios.mockClear = function() { + for(const key in axios) { + axios[key]?.mockClear?.() + } +} +export default axios; \ No newline at end of file diff --git a/__mocks__/latlon-geohash.js b/__mocks__/latlon-geohash.js new file mode 100644 index 00000000..3b02c5f3 --- /dev/null +++ b/__mocks__/latlon-geohash.js @@ -0,0 +1,7 @@ +export const encode = jest.fn() + +const geohash = { + encode +} + +export default geohash \ No newline at end of file diff --git a/__mocks__/moment.js b/__mocks__/moment.js new file mode 100644 index 00000000..f6a99980 --- /dev/null +++ b/__mocks__/moment.js @@ -0,0 +1,21 @@ +export const subtract = jest.fn() +export const date = jest.fn() +export const month = jest.fn() +export const year = jest.fn() +export const valueOf = jest.fn() + +const moment = jest.fn().mockImplementation(() => ({ + subtract, + date, + month, + year, + valueOf, +})) + +moment.mockClear = function(){ + for(const element in moment){ + moment[element]?.mockClear?.() + } +} + +export default moment \ No newline at end of file diff --git a/__mocks__/react-native-background-geolocation.ts b/__mocks__/react-native-background-geolocation.ts new file mode 100644 index 00000000..d62fd575 --- /dev/null +++ b/__mocks__/react-native-background-geolocation.ts @@ -0,0 +1,10 @@ +const onLocation = jest.fn().mockImplementation(() => ({})); + +const ready = jest.fn().mockImplementation(() => { + return jest.fn().mockResolvedValue(true); +}); + +export default { + onLocation, + ready +}; diff --git a/__mocks__/react-native-firebase.ts b/__mocks__/react-native-firebase.ts new file mode 100644 index 00000000..d9e5ded1 --- /dev/null +++ b/__mocks__/react-native-firebase.ts @@ -0,0 +1,34 @@ +export const logEvent = jest.fn(); + +const firebase = { + messaging: jest.fn(() => { + return { + hasPermission: jest.fn(() => Promise.resolve(true)), + subscribeToTopic: jest.fn(), + unsubscribeFromTopic: jest.fn(), + requestPermission: jest.fn(() => Promise.resolve(true)), + getToken: jest.fn(() => Promise.resolve('myMockToken')) + }; + }), + notifications: jest.fn(() => { + return { + onNotification: jest.fn(), + onNotificationDisplayed: jest.fn() + }; + }) +}; + +firebase.notifications.Android = { + Channel: jest.fn(() => ({ + setDescription: jest.fn(), + setSound: jest.fn(), + enableVibration: jest.fn(), + setVibrationPattern: jest.fn() + })), + Importance: { + Max: {} + } +}; + + +export default firebase; diff --git a/__mocks__/react-native-permissions.ts b/__mocks__/react-native-permissions.ts new file mode 100644 index 00000000..27ea52e2 --- /dev/null +++ b/__mocks__/react-native-permissions.ts @@ -0,0 +1,20 @@ +import { Permission } from 'react-native-permissions'; + +export const PERMISSIONS = { + IOS: { + LOCATION_ALWAYS: 'LOCATION_ALWAYS' + } +}; + +export const RESULTS = { + GRANTED: 'GRANTED' +}; + +// mock out any functions you want in this style... +export const check = async (permission: Permission) => { + return jest.fn().mockResolvedValue(true); +}; + +export const request = async (permission: Permission) => { + return jest.fn().mockResolvedValue(true); +}; diff --git a/__mocks__/react-native-sqlite-storage.ts b/__mocks__/react-native-sqlite-storage.ts new file mode 100644 index 00000000..2aa6070a --- /dev/null +++ b/__mocks__/react-native-sqlite-storage.ts @@ -0,0 +1,10 @@ +const echoTest = jest.fn().mockResolvedValue() +const openDatabase = jest.fn() + +const sqlite = { + enablePromise: jest.fn(), + echoTest, + openDatabase +} + +export default sqlite \ No newline at end of file diff --git a/__mocks__/setupFile.js b/__mocks__/setupFile.js index cc9774b7..721628f3 100644 --- a/__mocks__/setupFile.js +++ b/__mocks__/setupFile.js @@ -1,5 +1,5 @@ -import mockAsyncStorage from '@react-native-community/async-storage/jest/async-storage-mock'; import { NativeModules } from 'react-native'; +import mockAsyncStorage from '@react-native-community/async-storage/jest/async-storage-mock'; global.fetch = require('jest-fetch-mock'); @@ -12,7 +12,8 @@ NativeModules.RNCNetInfo = { NativeModules.SettingsManager = { settings: { - AppleLocale: 'he' + AppleLocale: 'he', + AppleLanguages: ['he'] } }; @@ -20,21 +21,80 @@ NativeModules.I18nManager = { localeIdentifier: 'he' }; -jest.mock('@react-native-community/async-storage', () => mockAsyncStorage); +jest.mock('@react-native-community/async-storage', () => ({ + ...mockAsyncStorage, + mockClear() { + Object.keys(mockAsyncStorage).forEach(key => mockAsyncStorage[key]?.mockClear?.()); + mockAsyncStorage.clear(); + } })); -jest.mock('../src/database/Database.js', () => { - const listSamples = jest.fn(); +jest.mock('@tmcw/togeojson', () => ({ + kml: jest.fn() +})) - const UserLocationsDatabase = function () { - return { listSamples }; - }; +jest.mock('../src/services/ErrorService', () => ({ + onError: jest.fn() + // onError: jest.fn(e => console.log(e)) +})); + +jest.mock('../src/services/sha256', () => ({ + sha256: jest.fn().mockImplementation(char => 'a') +})) + +jest.mock('../src/database/Database.js', () => { const containsObjectID = jest.fn(); const addSickRecord = jest.fn(); - const IntersectionSickDatabase = function () { - return { containsObjectID, addSickRecord }; + const IntersectionSickDatabase = jest.fn().mockImplementation(() => ({ + containsObjectID, + addSickRecord + })); + + const containsWifiHash = jest.fn(); + const addWifiMacAddresses = jest.fn(); + const WifiMacAddressDatabase = jest.fn().mockImplementation(() => ({ + containsWifiHash, + addWifiMacAddresses + })); + + const updateLastSampleEndTime = jest.fn(); + const addSample = jest.fn(); + const listSamples = jest.fn(); + const purgeSamplesTable = jest.fn(); + const getLastPointEntered = jest.fn(); + const insertBulkSamples = jest.fn(); + + const UserLocationsDatabase = jest.fn().mockImplementation(() => ({ + updateLastSampleEndTime, + addSample, + listSamples, + purgeSamplesTable, + getLastPointEntered, + insertBulkSamples + })); + + const db = { + UserLocationsDatabase, + WifiMacAddressDatabase, + IntersectionSickDatabase, + containsObjectID, + addSickRecord, + containsWifiHash, + addWifiMacAddresses, + updateLastSampleEndTime, + addSample, + listSamples, + purgeSamplesTable, + getLastPointEntered, + insertBulkSamples + }; + + return { + ...db, + mockClear() { + Object.keys(db).forEach(key => db[key]?.mockClear?.()); + } }; - return { UserLocationsDatabase, IntersectionSickDatabase }; }); jest.mock('react-native-device-info', () => { @@ -45,43 +105,6 @@ jest.mock('react-native-device-info', () => { }; }); -jest.mock('react-native-firebase', () => { - - - const firebase = { - messaging: jest.fn(() => { - return { - hasPermission: jest.fn(() => Promise.resolve(true)), - subscribeToTopic: jest.fn(), - unsubscribeFromTopic: jest.fn(), - requestPermission: jest.fn(() => Promise.resolve(true)), - getToken: jest.fn(() => Promise.resolve('myMockToken')) - }; - }), - notifications: jest.fn(() => { - return { - onNotification: jest.fn(), - onNotificationDisplayed: jest.fn() - }; - }) - } - - firebase.notifications.Android = { - Channel: jest.fn(() => ({ - setDescription: jest.fn(), - setSound: jest.fn(), - enableVibration: jest.fn(), - setVibrationPattern: jest.fn() - })), - Importance: { - Max: {} - } - }; - - return firebase; - -}); - jest.mock('react-native-background-timer', () => { return { @@ -95,6 +118,23 @@ jest.mock('../src/config/config.ts', () => { return { __esModule: true, namedExport: jest.fn(), - default: jest.fn(() => originalModule['com.hamagen.dev']), + locationServiceIgnoreConfidenceThreshold: 80, + locationServiceIgnoreSampleVelocityThreshold: 2.8, + locationHistoryIgnoreList: ['should ignore from test'], + default: jest.fn(() => originalModule['com.hamagen']), + }; +}); + +jest.mock('../src/store.ts', () => { + const dispatch = jest.fn() + + const store = jest.fn().mockImplementation(() => ({ dispatch })); + + return { + __esModule: true, + namedExport: jest.fn(), + default: store, + dispatch }; }); + diff --git a/android/app/build.gradle b/android/app/build.gradle index 584f1b71..f6785f31 100755 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -137,8 +137,8 @@ android { applicationId "com.hamagen" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 27 - versionName "1.1.3" + versionCode 32 + versionName "1.2.4" } dexOptions { @@ -177,14 +177,14 @@ android { buildTypes { debug { - versionNameSuffix ".dev" - applicationIdSuffix ".dev" + versionNameSuffix ".qa" + applicationIdSuffix ".qa" signingConfig signingConfigs.debug } qa { initWith release - versionNameSuffix ".dev" - applicationIdSuffix ".dev" + versionNameSuffix ".qa" + applicationIdSuffix ".qa" signingConfig signingConfigs.release matchingFallbacks = ["release"] } @@ -223,16 +223,16 @@ android { } dependencies { - implementation 'io.radar:sdk:2.1.+' - implementation "com.google.android.gms:play-services-base:17.1.0" - implementation 'com.google.firebase:firebase-core:17.2.3' - implementation "com.google.firebase:firebase-messaging:20.1.2" + implementation "com.google.android.gms:play-services-base:17.2.1" + implementation 'com.google.firebase:firebase-core:17.3.0' + implementation "com.google.firebase:firebase-messaging:20.1.5" implementation fileTree(dir: "libs", include: ["*.jar"]) implementation "com.facebook.react:react-native:+" // From node_modules if (enableHermes) { def hermesPath = "../../node_modules/hermes-engine/android/"; debugImplementation files(hermesPath + "hermes-debug.aar") + qaImplementation files(hermesPath + "hermes-debug.aar") releaseImplementation files(hermesPath + "hermes-release.aar") } else { implementation jscFlavor diff --git a/android/app/src/debug/google-services.json b/android/app/src/debug/google-services.json index c64c6ebb..c822809b 100644 --- a/android/app/src/debug/google-services.json +++ b/android/app/src/debug/google-services.json @@ -6,6 +6,50 @@ "storage_bucket": "hamagen-aa88d.appspot.com" }, "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:776051238571:android:6a28fec1527d21684af9ee", + "android_client_info": { + "package_name": "com.hamagen" + } + }, + "oauth_client": [ + { + "client_id": "776051238571-l562rviorn5u7gqejo6l8scnsr07knpj.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.hamagen", + "certificate_hash": "5e8f16062ea3cd2c4a0d547876baa6f38cabf625" + } + }, + { + "client_id": "776051238571-h98i1874433nq1tbm5p7acfjoptj74eo.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBTUXVwo9ouIiPFNXBdz9o4D1KJq7Es1qg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "776051238571-h98i1874433nq1tbm5p7acfjoptj74eo.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "776051238571-mbp8deiq0b0blfsb0pn0sc25tfj99rk6.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "com.hamagen" + } + } + ] + } + } + }, { "client_info": { "mobilesdk_app_id": "1:776051238571:android:27428629073a56ce4af9ee", @@ -38,6 +82,57 @@ { "client_id": "776051238571-h98i1874433nq1tbm5p7acfjoptj74eo.apps.googleusercontent.com", "client_type": 3 + }, + { + "client_id": "776051238571-mbp8deiq0b0blfsb0pn0sc25tfj99rk6.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "com.hamagen" + } + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:776051238571:android:0ce459535c98709f4af9ee", + "android_client_info": { + "package_name": "com.hamagen.qa" + } + }, + "oauth_client": [ + { + "client_id": "776051238571-igc55i4ajk7b7kchmqjg3dfg7ogqeguo.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.hamagen.qa", + "certificate_hash": "5e8f16062ea3cd2c4a0d547876baa6f38cabf625" + } + }, + { + "client_id": "776051238571-h98i1874433nq1tbm5p7acfjoptj74eo.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBTUXVwo9ouIiPFNXBdz9o4D1KJq7Es1qg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "776051238571-h98i1874433nq1tbm5p7acfjoptj74eo.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "776051238571-mbp8deiq0b0blfsb0pn0sc25tfj99rk6.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "com.hamagen" + } } ] } diff --git a/android/app/src/debug/res/values/strings.xml b/android/app/src/debug/res/values/strings.xml index 24519649..dbaf6911 100755 --- a/android/app/src/debug/res/values/strings.xml +++ b/android/app/src/debug/res/values/strings.xml @@ -1,5 +1,5 @@ המגן Dev LocalPush - c10047e800a8d08ee0e7236fc2b35d595bf89f020d99ea125eaf578c5a6180bd + 5ae5c10d23f016a9fb6946e0d0c51b44aa582e9c1a8fcd8b41235b4b0d7769f8 diff --git a/android/app/src/main/java/com/hamagen/MainApplication.java b/android/app/src/main/java/com/hamagen/MainApplication.java index a2a9cc35..65da3b25 100755 --- a/android/app/src/main/java/com/hamagen/MainApplication.java +++ b/android/app/src/main/java/com/hamagen/MainApplication.java @@ -12,11 +12,8 @@ import android.view.WindowManager; import android.content.res.Configuration; -import android.content.Context; import android.util.DisplayMetrics; -import io.radar.sdk.Radar; - import android.content.IntentFilter; import io.rumors.reactnativesettings.RNSettingsPackage; import io.rumors.reactnativesettings.receivers.GpsLocationReceiver; @@ -25,7 +22,6 @@ import com.facebook.react.modules.i18nmanager.I18nUtil; import io.invertase.firebase.messaging.RNFirebaseMessagingPackage; import io.invertase.firebase.notifications.RNFirebaseNotificationsPackage; -import io.invertase.firebase.analytics.RNFirebaseAnalyticsPackage; public class MainApplication extends Application implements ReactApplication { @@ -44,7 +40,6 @@ protected List getPackages() { // packages.add(new MyReactNativePackage()); packages.add(new RNFirebaseMessagingPackage()); packages.add(new RNFirebaseNotificationsPackage()); - packages.add(new RNFirebaseAnalyticsPackage()); return packages; } @@ -63,8 +58,6 @@ public ReactNativeHost getReactNativeHost() { public void onCreate() { super.onCreate(); - Radar.initialize("prj_test_pk_0c1593aef4c81653ef790cd63e33f6ac61195590"); - // disable font scaling adjustFontScale(getApplicationContext(),getResources().getConfiguration()); diff --git a/android/app/src/qa/google-services.json b/android/app/src/qa/google-services.json index 8bc7a62b..c822809b 100644 --- a/android/app/src/qa/google-services.json +++ b/android/app/src/qa/google-services.json @@ -6,6 +6,50 @@ "storage_bucket": "hamagen-aa88d.appspot.com" }, "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:776051238571:android:6a28fec1527d21684af9ee", + "android_client_info": { + "package_name": "com.hamagen" + } + }, + "oauth_client": [ + { + "client_id": "776051238571-l562rviorn5u7gqejo6l8scnsr07knpj.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.hamagen", + "certificate_hash": "5e8f16062ea3cd2c4a0d547876baa6f38cabf625" + } + }, + { + "client_id": "776051238571-h98i1874433nq1tbm5p7acfjoptj74eo.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBTUXVwo9ouIiPFNXBdz9o4D1KJq7Es1qg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "776051238571-h98i1874433nq1tbm5p7acfjoptj74eo.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "776051238571-mbp8deiq0b0blfsb0pn0sc25tfj99rk6.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "com.hamagen" + } + } + ] + } + } + }, { "client_info": { "mobilesdk_app_id": "1:776051238571:android:27428629073a56ce4af9ee", @@ -38,6 +82,13 @@ { "client_id": "776051238571-h98i1874433nq1tbm5p7acfjoptj74eo.apps.googleusercontent.com", "client_type": 3 + }, + { + "client_id": "776051238571-mbp8deiq0b0blfsb0pn0sc25tfj99rk6.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "com.hamagen" + } } ] } @@ -75,6 +126,13 @@ { "client_id": "776051238571-h98i1874433nq1tbm5p7acfjoptj74eo.apps.googleusercontent.com", "client_type": 3 + }, + { + "client_id": "776051238571-mbp8deiq0b0blfsb0pn0sc25tfj99rk6.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "com.hamagen" + } } ] } diff --git a/android/app/src/qa/res/values/strings.xml b/android/app/src/qa/res/values/strings.xml index d129e743..f3209f3e 100755 --- a/android/app/src/qa/res/values/strings.xml +++ b/android/app/src/qa/res/values/strings.xml @@ -1,5 +1,5 @@ המגן QA LocalPush - c10047e800a8d08ee0e7236fc2b35d595bf89f020d99ea125eaf578c5a6180bd + 5ae5c10d23f016a9fb6946e0d0c51b44aa582e9c1a8fcd8b41235b4b0d7769f8 diff --git a/index.js b/index.js index 7f2ec713..a0a375e3 100755 --- a/index.js +++ b/index.js @@ -5,16 +5,29 @@ import BackgroundFetch from 'react-native-background-fetch'; import BackgroundGeolocation from 'react-native-background-geolocation'; import App from './src/App'; import { name as appName } from './app.json'; +import { updateDBAccordingToSampleVelocity } from './src/services/SampleService'; import { checkSickPeople } from './src/services/Tracker'; -import { insertDB } from './src/services/SampleService'; import { onError } from './src/services/ErrorService'; import { initConfig } from './src/config/config'; -BackgroundGeolocation.onLocation( - async (location) => { +const onLocationReceived = async (location) => { + // ignore non-distinct locations from the SDK + if (location.sample) { + return; + } + + try { location.timestamp = moment(location.timestamp).valueOf(); await initConfig(); - await insertDB(location); + await updateDBAccordingToSampleVelocity(location); + } catch (error) { + onError({ error }); + } +}; + +BackgroundGeolocation.onLocation( + async (location) => { + await onLocationReceived(location); }, (error) => { onError({ error }); } @@ -34,8 +47,12 @@ const BackgroundFetchHeadlessTask = async (event) => { } }; -const BackgroundGeolocationHeadlessTask = async (event, params) => { - console.log('[BackgroundGeolocation HeadlessTask] -', event.name, params); +const BackgroundGeolocationHeadlessTask = async (event) => { + console.log('[BackgroundGeolocation HeadlessTask] -', event.name); + + if (event.name === 'location') { + await onLocationReceived(event.params); + } }; AppRegistry.registerComponent(appName, () => App); diff --git a/ios/Podfile b/ios/Podfile index 09e3f4da..080e202b 100755 --- a/ios/Podfile +++ b/ios/Podfile @@ -35,10 +35,10 @@ def project_pods pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec' pod 'Permission-LocationAlways', :path => "../node_modules/react-native-permissions/ios/LocationAlways.podspec" + pod 'Permission-Motion', :path => "../node_modules/react-native-permissions/ios/Motion.podspec" pod 'Firebase/Core', '~> 5.20.2' pod 'Firebase/Messaging', '~> 5.20.2' - pod 'GoogleIDFASupport', '~> 3.14.0' use_native_modules! end diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 539e94ad..aa9c810c 100755 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -64,7 +64,6 @@ PODS: - GoogleUtilities/Network (~> 5.2) - "GoogleUtilities/NSData+zlib (~> 5.2)" - nanopb (~> 0.3) - - GoogleIDFASupport (3.14.0) - GoogleUtilities/AppDelegateSwizzler (5.8.0): - GoogleUtilities/Environment - GoogleUtilities/Logger @@ -94,6 +93,8 @@ PODS: - nanopb/encode (0.3.9011) - Permission-LocationAlways (2.0.10): - RNPermissions + - Permission-Motion (2.0.10): + - RNPermissions - Protobuf (3.11.4) - RCTRequired (0.61.5) - RCTTypeSafety (0.61.5): @@ -260,6 +261,8 @@ PODS: - React-jsinspector (0.61.5) - react-native-background-timer (2.2.0): - React + - react-native-cookies (2.0.8): + - React - react-native-netinfo (5.6.2): - React - react-native-network-info (5.2.1): @@ -320,7 +323,7 @@ PODS: - React - RNCAsyncStorage (1.7.1): - React - - RNCMaskedView (0.1.1): + - RNCMaskedView (0.1.8): - React - RNDeviceInfo (5.4.0): - React @@ -355,10 +358,10 @@ DEPENDENCIES: - Firebase/Messaging (~> 5.20.2) - Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`) - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) - - GoogleIDFASupport (~> 3.14.0) - lottie-ios (from `../node_modules/lottie-ios`) - lottie-react-native (from `../node_modules/lottie-react-native`) - Permission-LocationAlways (from `../node_modules/react-native-permissions/ios/LocationAlways.podspec`) + - Permission-Motion (from `../node_modules/react-native-permissions/ios/Motion.podspec`) - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`) - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`) - React (from `../node_modules/react-native/`) @@ -371,6 +374,7 @@ DEPENDENCIES: - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`) - react-native-background-timer (from `../node_modules/react-native-background-timer`) + - "react-native-cookies (from `../node_modules/@react-native-community/cookies`)" - "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)" - react-native-network-info (from `../node_modules/react-native-network-info`) - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) @@ -418,7 +422,6 @@ SPEC REPOS: - FirebaseInstanceID - FirebaseMessaging - GoogleAppMeasurement - - GoogleIDFASupport - GoogleUtilities - nanopb - Protobuf @@ -440,6 +443,8 @@ EXTERNAL SOURCES: :path: "../node_modules/lottie-react-native" Permission-LocationAlways: :path: "../node_modules/react-native-permissions/ios/LocationAlways.podspec" + Permission-Motion: + :path: "../node_modules/react-native-permissions/ios/Motion.podspec" RCTRequired: :path: "../node_modules/react-native/Libraries/RCTRequired" RCTTypeSafety: @@ -460,6 +465,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/jsinspector" react-native-background-timer: :path: "../node_modules/react-native-background-timer" + react-native-cookies: + :path: "../node_modules/@react-native-community/cookies" react-native-netinfo: :path: "../node_modules/@react-native-community/netinfo" react-native-network-info: @@ -542,12 +549,12 @@ SPEC CHECKSUMS: Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51 glog: 1f3da668190260b06b429bb211bfbee5cd790c28 GoogleAppMeasurement: ffe513e90551844a739e7bcbb1d2aca1c28a4338 - GoogleIDFASupport: aaf8c10bd429abb1c15349d5252244f5eda8ead1 GoogleUtilities: 04fce34bcd5620c1ee76fb79172105c74a4df335 lottie-ios: 85ce835dd8c53e02509f20729fc7d6a4e6645a0a lottie-react-native: 2a1a82bb326ae51331a5520de0cf706733c6db69 nanopb: 18003b5e52dab79db540fe93fe9579f399bd1ccd Permission-LocationAlways: 738e3b24333d52c87361804bb0a1641de014c405 + Permission-Motion: 6d251146daf1102ebe143a2618adc18946353ad2 Protobuf: 176220c526ad8bd09ab1fb40a978eac3fef665f7 RCTRequired: b153add4da6e7dbc44aebf93f3cf4fcae392ddf1 RCTTypeSafety: 9aa1b91d7f9310fc6eadc3cf95126ffe818af320 @@ -559,6 +566,7 @@ SPEC CHECKSUMS: React-jsiexecutor: d5525f9ed5f782fdbacb64b9b01a43a9323d2386 React-jsinspector: fa0ecc501688c3c4c34f28834a76302233e29dc0 react-native-background-timer: 1f7d560647b40e6a60b01c452ba29c54bf581fc4 + react-native-cookies: 4eb6243b344684bda432f661c28dab2c491ebb0c react-native-netinfo: 73303369946c2487c600418961bfdc87748b832f react-native-network-info: d1290ffc0bd0709e11436f5b8d7f605dcc5c4530 react-native-safe-area-context: e200d4433aba6b7e60b52da5f37af11f7a0b0392 @@ -581,7 +589,7 @@ SPEC CHECKSUMS: RNBackgroundFetch: 8d8d66b47eafcb9e772b2eb95057939038b3ef95 RNBackgroundGeolocation: 8f0a20788100fbf08987d80f2053ae6eba79c566 RNCAsyncStorage: 44395cb9c7c1523104c2b499eb426ef7aff82bca - RNCMaskedView: b79e193409a90bf6b5170d421684f437ff4e2278 + RNCMaskedView: 4383c3140f1235391fd6029968c166f09ddd0973 RNDeviceInfo: c7b1dc457611b9231bb780a882102e2c93edc7af RNFirebase: 37daa9a346d070f9f6ee1f3b4aaf4c8e3b1d5d1c RNGestureHandler: 946a7691e41df61e2c4b1884deab41a4cdc3afff @@ -592,6 +600,6 @@ SPEC CHECKSUMS: RNWifi: 1747f039612dd494192ba6c8bc498606ad9f3ec4 Yoga: f2a7cd4280bfe2cca5a7aed98ba0eb3d1310f18b -PODFILE CHECKSUM: 217685a55686ea466821625ffcc92870d0bd76f7 +PODFILE CHECKSUM: d1a9e37d36f29edcc78431c125e44debfdac8991 -COCOAPODS: 1.8.4 +COCOAPODS: 1.9.1 diff --git a/ios/codeAgainstCorona.xcodeproj/project.pbxproj b/ios/codeAgainstCorona.xcodeproj/project.pbxproj index 345e33a1..6b8088da 100644 --- a/ios/codeAgainstCorona.xcodeproj/project.pbxproj +++ b/ios/codeAgainstCorona.xcodeproj/project.pbxproj @@ -534,12 +534,12 @@ CODE_SIGN_ENTITLEMENTS = "codeAgainstCorona/Code against corona.entitlements"; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 27; + CURRENT_PROJECT_VERSION = 32; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = N3P977USJJ; INFOPLIST_FILE = codeAgainstCorona/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 1.1.3; + MARKETING_VERSION = 1.2.4; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -565,11 +565,11 @@ CODE_SIGN_ENTITLEMENTS = "codeAgainstCorona/Code against corona.entitlements"; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 27; + CURRENT_PROJECT_VERSION = 32; DEVELOPMENT_TEAM = N3P977USJJ; INFOPLIST_FILE = codeAgainstCorona/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 1.1.3; + MARKETING_VERSION = 1.2.4; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -693,7 +693,7 @@ CODE_SIGN_ENTITLEMENTS = "המגן QA.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 26; + CURRENT_PROJECT_VERSION = 32; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = N3P977USJJ; FRAMEWORK_SEARCH_PATHS = ( @@ -704,7 +704,7 @@ ); INFOPLIST_FILE = "codeAgainstCorona/Info-qa.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 1.1.2; + MARKETING_VERSION = 1.2.4; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -730,7 +730,7 @@ CODE_SIGN_ENTITLEMENTS = "המגן QA.entitlements"; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 26; + CURRENT_PROJECT_VERSION = 32; DEVELOPMENT_TEAM = N3P977USJJ; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -740,7 +740,7 @@ ); INFOPLIST_FILE = "codeAgainstCorona/Info-qa.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 1.1.2; + MARKETING_VERSION = 1.2.4; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", diff --git a/ios/codeAgainstCorona/Info-qa.plist b/ios/codeAgainstCorona/Info-qa.plist index 059c227d..a49e5d0e 100755 --- a/ios/codeAgainstCorona/Info-qa.plist +++ b/ios/codeAgainstCorona/Info-qa.plist @@ -47,6 +47,8 @@ יש לאפשר גישה למיקום על מנת להשוות את נתיבך אל מול נתיבי חולי קורונה מאומתים NSLocationWhenInUseUsageDescription יש לאפשר גישה למיקום על מנת להשוות את נתיבך אל מול נתיבי חולי קורונה מאומתים + NSMotionUsageDescription + יש לאפשר גישה לחיישני תנועה על מנת להשוות את נתיבך אל מול נתיבי חולי קורונה מאומתים UIAppFonts SimplerPro_V3-Black.otf diff --git a/package.json b/package.json index 0694380c..953e9439 100755 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "android": "react-native run-android", - "ios": "react-native run-ios", + "ios": "react-native run-ios --scheme hamagen", "start": "react-native start", "test": "jest", "lint": "eslint . --ext .js,.jsx,.ts,.tsx" @@ -12,13 +12,15 @@ "dependencies": { "@react-native-community/async-storage": "1.7.1", "@react-native-community/cli": "2.9.0", - "@react-native-community/masked-view": "https://github.com/greenyossi/react-native-masked-view.git", + "@react-native-community/cookies": "^2.0.8", + "@react-native-community/masked-view": "^0.1.8", "@react-native-community/netinfo": "5.6.2", "@react-navigation/bottom-tabs": "5.0.3", "@react-navigation/drawer": "5.0.3", "@react-navigation/native": "5.0.3", "@react-navigation/stack": "5.0.3", "@redux-offline/redux-offline": "2.5.2-native.3", + "@tmcw/togeojson": "https://github.com/tmcw/togeojson.git", "async-lock": "1.2.2", "axios": "0.19.0", "events": "3.0.0", @@ -55,6 +57,7 @@ "redux": "4.0.5", "redux-thunk": "2.3.0", "rn-fetch-blob": "0.12.0", + "seedrandom": "^3.0.5", "sha256": "00.2.0", "xmldom": "0.3.0" }, @@ -73,6 +76,8 @@ "@types/react-native-uuid-generator": "4.0.0", "@types/react-redux": "7.1.5", "@types/react-test-renderer": "16.9.1", + "@types/redux-mock-store": "^1.0.2", + "@types/seedrandom": "^2.4.28", "@types/xmldom": "0.1.29", "@typescript-eslint/eslint-plugin": "2.12.0", "@typescript-eslint/parser": "2.12.0", @@ -91,7 +96,7 @@ "metro-react-native-babel-preset": "0.56.0", "mock-async-storage": "2.2.0", "react-test-renderer": "16.9.0", - "redux-mock-store": "1.5.4", + "redux-mock-store": "^1.5.4", "tslint": "5.17.0", "tslint-config-prettier": "1.18.0", "tslint-eslint-rules": "5.4.0", diff --git a/src/actions/GeneralActions.ts b/src/actions/GeneralActions.ts index 868f7164..37cbd62c 100644 --- a/src/actions/GeneralActions.ts +++ b/src/actions/GeneralActions.ts @@ -19,7 +19,7 @@ export const toggleWebview = (isShow: boolean, usageType:string) => (dispatch: a export const checkForceUpdate = () => async (dispatch: any) => { try { - const { data: { ios, android, shouldForceIOS, shouldForceAndroid, terms } } = await axios.get(`${config().versionsUrl}?r=${Math.random()}`, { headers: { 'Content-Type': 'application/json;charset=utf-8' } }); + const { data: { v2: { ios, android, shouldForceIOS, shouldForceAndroid, terms } } } = await axios.get(`${config().versionsUrl}?r=${Math.random()}`, { headers: { 'Content-Type': 'application/json;charset=utf-8' } }); const termsVersion = JSON.parse(await AsyncStorage.getItem(CURRENT_TERMS_VERSION) || '0'); @@ -33,8 +33,8 @@ export const checkForceUpdate = () => async (dispatch: any) => { const appVersion = DeviceInfo.getVersion().split('.').map((level: string) => parseFloat(level)); const shouldForce = IS_IOS ? shouldForceIOS : shouldForceAndroid; - if (shouldForce && isOlderVersion(serverVersion, appVersion)) { - dispatch({ type: SHOW_FORCE_UPDATE }); + if (isOlderVersion(serverVersion, appVersion)) { + dispatch({ type: SHOW_FORCE_UPDATE, payload: { shouldForce } }); } } catch (error) { onError({ error }); @@ -43,6 +43,10 @@ export const checkForceUpdate = () => async (dispatch: any) => { const isOlderVersion = (serverVersion: number[], appVersion: number[]) => { for (let i = 0; i < serverVersion.length; i++) { + if (appVersion[i] > serverVersion[i]) { + return false; + } + if (serverVersion[i] > appVersion[i]) { return true; } diff --git a/src/actions/LocaleActions.ts b/src/actions/LocaleActions.ts index d7deab1b..f3bf07cc 100644 --- a/src/actions/LocaleActions.ts +++ b/src/actions/LocaleActions.ts @@ -2,7 +2,7 @@ import axios from 'axios'; import AsyncStorage from '@react-native-community/async-storage'; import { NativeModules } from 'react-native'; import { onError } from '../services/ErrorService'; -import LocaleData from '../locale/LocaleData'; +import localeData, { LocaleData } from '../locale/LocaleData'; import config from '../config/config'; import { TOGGLE_CHANGE_LANGUAGE, LOCALE_CHANGED, INIT_LOCALE } from '../constants/ActionTypes'; import { CURRENT_LOCALE, IS_IOS } from '../constants/Constants'; @@ -11,34 +11,52 @@ export const toggleChangeLanguage = (isShow: boolean) => (dispatch: any) => disp export const initLocale = () => async (dispatch: any) => { try { - const locale = IS_IOS ? NativeModules.SettingsManager.settings.AppleLocale : NativeModules.I18nManager.localeIdentifier; - - let activeLocale: 'he'|'iw'|'en'|'ar'|'am'|'ru'|'fr' = (await AsyncStorage.getItem(CURRENT_LOCALE) || locale).substr(0, 2); - - if (activeLocale === 'iw') { - activeLocale = 'he'; - } + const activeLocale = await getActiveLocale(); await AsyncStorage.setItem(CURRENT_LOCALE, activeLocale); - const { data } = await axios.get(`${config().stringsUrl}?r=${Math.random()}`, { headers: { 'Content-Type': 'application/json;charset=utf-8' } }); + const { data }: { data: LocaleData } = await axios.get(`${config().stringsUrl}?r=${Math.random()}`, { headers: { 'Content-Type': 'application/json;charset=utf-8' } }); + + const { languages, notificationData, externalUrls } = data; dispatch({ type: INIT_LOCALE, payload: { + languages, + notificationData, + externalUrls, strings: data[activeLocale] || data.he, locale: activeLocale, isRTL: ['he', 'ar'].includes(activeLocale), localeData: data } }); + + return Promise.resolve({ locale: activeLocale, notificationData }); } catch (error) { - dispatch({ type: LOCALE_CHANGED, payload: { strings: LocaleData.he, locale: 'he', isRTL: true } }); + const activeLocale = await getActiveLocale(); + const { languages, externalUrls, notificationData } = localeData; + + dispatch({ + type: INIT_LOCALE, + payload: { + languages, + externalUrls, + notificationData, + strings: localeData[activeLocale], + locale: activeLocale, + isRTL: true, + localeData + } + }); + onError({ error }); + + return Promise.resolve({ locale: activeLocale, notificationData }); } }; -export const changeLocale = (locale: 'he'|'en'|'ar'|'am'|'ru'|'fr') => async (dispatch: any) => { +export const changeLocale = (locale: string) => async (dispatch: any) => { try { await AsyncStorage.setItem(CURRENT_LOCALE, locale); dispatch({ type: LOCALE_CHANGED, payload: { locale } }); @@ -46,3 +64,20 @@ export const changeLocale = (locale: 'he'|'en'|'ar'|'am'|'ru'|'fr') => async (di onError({ error }); } }; + +const getActiveLocale = () => new Promise(async (resolve) => { + let locale = IS_IOS ? NativeModules.SettingsManager.settings.AppleLocale : NativeModules.I18nManager.localeIdentifier; + + if (locale === undefined) { + // eslint-disable-next-line prefer-destructuring + locale = NativeModules.SettingsManager.settings.AppleLanguages[0]; + } + + let activeLocale: string = (await AsyncStorage.getItem(CURRENT_LOCALE) || locale).substr(0, 2); + + if (activeLocale === 'iw') { + activeLocale = 'he'; + } + + resolve(activeLocale); +}); diff --git a/src/actions/ExposuresActions.test.js b/src/actions/__tests__/ExposuresActions.test.ts similarity index 70% rename from src/actions/ExposuresActions.test.js rename to src/actions/__tests__/ExposuresActions.test.ts index 8c7c18b5..3c16e720 100644 --- a/src/actions/ExposuresActions.test.js +++ b/src/actions/__tests__/ExposuresActions.test.ts @@ -1,18 +1,23 @@ import AsyncStorage from '@react-native-community/async-storage'; import configureMockStore from 'redux-mock-store'; import thunk from 'redux-thunk'; -import * as actionTypes from '../constants/ActionTypes'; -import * as constants from '../constants/Constants'; -import * as ExposuresActions from './ExposuresActions'; -import { Exposure } from '../types'; +import * as actionTypes from '../../constants/ActionTypes'; +import * as constants from '../../constants/Constants'; +import { setExposures, setValidExposure, dismissExposure, removeValidExposure } from '../ExposuresActions'; +import { Exposure } from '../../types'; const mockStore = configureMockStore([thunk]); - describe('ExposuresActions', () => { test('setExposures()', async () => { const sickRecord: Exposure = { properties: { + OBJECTID: 2, + Key_Field: 2, + Name: 'name', + Place: 'place', + fromTime_utc: 10 * 60 * 60 * 1000, + toTime_utc: 20 * 60 * 60 * 1000, fromTime: 10 * 60 * 60 * 1000, toTime: 20 * 60 * 60 * 1000, }, @@ -24,6 +29,12 @@ describe('ExposuresActions', () => { const exposures: Exposure[] = [ { properties: { + OBJECTID: 2, + Key_Field: 2, + Name: 'name', + Place: 'place', + fromTime_utc: 10 * 60 * 60 * 1000, + toTime_utc: 20 * 60 * 60 * 1000, fromTime: 10 * 60 * 60 * 1000, toTime: 20 * 60 * 60 * 1000, }, @@ -39,7 +50,7 @@ describe('ExposuresActions', () => { ]; await AsyncStorage.setItem(constants.DISMISSED_EXPOSURES, '[1]'); const store = mockStore({}); - return store.dispatch(ExposuresActions.setExposures([sickRecord])).then(() => { + return store.dispatch(setExposures([sickRecord])).then(() => { const actions = store.getActions(); expect(actions).toEqual(expectedActions); }); @@ -48,6 +59,12 @@ describe('ExposuresActions', () => { test('setValidExposure()', async () => { const sickExposure: Exposure = { properties: { + OBJECTID: 1, + Key_Field: 1, + Name: 'name', + Place: 'place', + fromTime_utc: 10 * 60 * 60 * 1000, + toTime_utc: 20 * 60 * 60 * 1000, fromTime: 10 * 60 * 60 * 1000, toTime: 20 * 60 * 60 * 1000, }, @@ -60,7 +77,7 @@ describe('ExposuresActions', () => { { type: actionTypes.SET_VALID_EXPOSURE, payload: { validExposure: sickExposure } }, ]; const store = mockStore({}); - store.dispatch(ExposuresActions.setValidExposure(sickExposure)).then(() => { + store.dispatch(setValidExposure(sickExposure)).then(() => { const actions = store.getActions(); expect(actions).toEqual(expectedActions); }); @@ -71,7 +88,7 @@ describe('ExposuresActions', () => { { type: actionTypes.REMOVE_VALID_EXPOSURE }, ]; const store = mockStore({}); - return store.dispatch(ExposuresActions.removeValidExposure()).then(() => { + return store.dispatch(removeValidExposure()).then(() => { const actions = store.getActions(); expect(actions).toEqual(expectedActions); }); @@ -83,12 +100,12 @@ describe('ExposuresActions', () => { ]; await AsyncStorage.setItem(constants.DISMISSED_EXPOSURES, '[1]'); const store = mockStore({}); - store.dispatch(ExposuresActions.dismissExposure(1)).then(() => { + store.dispatch(dismissExposure(1)).then(() => { const actions = store.getActions(); expect(actions).toEqual(expectedActions); }); await AsyncStorage.removeItem(constants.DISMISSED_EXPOSURES); - store.dispatch(ExposuresActions.dismissExposure(1)).then(() => { + store.dispatch(dismissExposure(1)).then(() => { const actions = store.getActions(); expect(actions).toEqual(expectedActions); }); diff --git a/src/actions/__tests__/LocaleActions.test.ts b/src/actions/__tests__/LocaleActions.test.ts new file mode 100644 index 00000000..405b8d70 --- /dev/null +++ b/src/actions/__tests__/LocaleActions.test.ts @@ -0,0 +1,120 @@ +import { NativeModules } from 'react-native'; +import axios from 'axios' +import AsyncStorage from '@react-native-community/async-storage'; +import configureMockStore from 'redux-mock-store'; +import thunk from 'redux-thunk'; +import {LOCALE_CHANGED} from '../../constants/ActionTypes'; + +import {initLocale, changeLocale} from '../LocaleActions' +import { onError } from '../../services/ErrorService'; + +const mockStore = configureMockStore([thunk]); + +const MAthRandomSpy = jest.spyOn(Math, 'random').mockReturnValue(0.5) + +beforeEach(() => { + onError.mockClear() + axios.mockClear() + AsyncStorage.mockClear() +}) + +describe('Locale action', () => { + describe('initLocale', () => { + test('init call', async () => { + //setup + // AsyncStorage.setItem.mockResolvedValueOnce('he') + const store = mockStore() + const mockResponse = { + data: { + languages: 'he', + notificationData: undefined, + externalUrls: undefined, + he: {} + }, + status: 200, + statusText: 'OK', + headers: {}, + config: {}, + } + + axios.get.mockResolvedValueOnce(mockResponse) + + + await store.dispatch(initLocale()) + + + // expect(store.getActions()).toBe({}) + expect(onError).toBeCalledTimes(0) + }) + + test('default to AppleLanguages', async () => { + NativeModules.SettingsManager.settings.AppleLocale = undefined + const mockResponse = { + data: { + languages: 'he', + notificationData: undefined, + externalUrls: undefined, + he: {} + }, + status: 200, + statusText: 'OK', + headers: {}, + config: {}, + } + + axios.get.mockResolvedValueOnce(mockResponse) + + const store = mockStore() + await store.dispatch(initLocale()) + expect(onError).toBeCalledTimes(0) + }) + + + + test('async storage rejects', async () => { + AsyncStorage.setItem.mockRejectedValueOnce(new TypeError('storage error')) + const mockResponse = { + data: { + languages: 'he', + notificationData: undefined, + externalUrls: undefined, + he: {} + }, + status: 200, + statusText: 'OK', + headers: {}, + config: {}, + } + + axios.get.mockResolvedValueOnce(mockResponse) + + const store = mockStore() + await store.dispatch(initLocale()) + + expect(onError).toBeCalledTimes(1) + }) + + test('locale data fetch fail', async () => { + + }) + }) + + describe('changeLocale', () => { + test('change from he to en', async () => { + const store = mockStore() + // locale is he + await store.dispatch(changeLocale('en')) + expect(store.getActions()).toEqual([{ type: LOCALE_CHANGED, payload: { locale: 'en' } }]) + + }) + + + test('async storage rejects', async () => { + AsyncStorage.setItem.mockRejectedValueOnce(new TypeError('storage error')) + const store = mockStore() + // locale is he + await store.dispatch(changeLocale('en')) + expect(onError).toBeCalledTimes(1) + }) + }) +}) \ No newline at end of file diff --git a/src/assets/lottie/magen logo.json b/src/assets/lottie/magen logo.json index ae35d3e0..38e6e6bd 100644 --- a/src/assets/lottie/magen logo.json +++ b/src/assets/lottie/magen logo.json @@ -1 +1 @@ -{"v":"5.5.9","fr":60,"ip":60,"op":189,"w":1000,"h":1000,"nm":"loader final","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"v","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[500,500,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[61,-77.5],[-16,41.5],[-61,-12]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":72,"s":[0]},{"t":118,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":15,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false}],"ip":21,"op":615,"st":21,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[500,500,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"t":60,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[163.951,147.971],[163.994,149.56],[-5.566,18.897],[-219.646,20.829],[-219.352,19.81],[-4.883,15.085]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":74,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[181.513,96.287],[172.611,123.828],[-31.688,97.889],[-211.029,-4.903],[-201.79,-31.875],[5.91,-16.365]],"c":true}]},{"t":124,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[229.944,-48.491],[229.73,-46.725],[4.971,-12.376],[-153.91,-175.456],[-153.359,-176.653],[5.91,-16.365]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.322441789216,0.322441789216,0.322441789216,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":60,"s":[0,-264.9],"to":[0,87.65],"ti":[0,-87.65]},{"t":124,"s":[0,261]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":-18.5,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":594,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"main Outlines 2","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[499.852,499.852,0],"ix":2},"a":{"a":0,"k":[313.852,313.852,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-19.837,35.265],[0,40.775],[0,0]],"o":[[0,0],[0,0],[34.715,-20.939],[19.836,-35.265],[0,0],[0,0]],"v":[[-93.398,-243],[-93.398,243],[-20.664,198.918],[63.092,112.408],[93.398,-4.408],[93.398,-179.082]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.498039245605,0.823529471603,0.89019613827,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[407.249,313.852],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-19.837,-35.817],[-34.714,-20.939],[0,0],[0,0]],"o":[[0,0],[0,40.776],[19.837,35.816],[0,0],[0,0],[0,0]],"v":[[-93.398,-179.082],[-93.398,-4.409],[-63.092,112.408],[20.663,198.918],[93.398,243],[93.398,-243]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.570960789101,0.895596612669,0.961320465686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[220.453,313.852],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":594,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"main Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[499.852,499.852,0],"ix":2},"a":{"a":0,"k":[313.852,313.852,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-19.837,35.265],[0,40.775],[0,0]],"o":[[0,0],[0,0],[34.715,-20.939],[19.836,-35.265],[0,0],[0,0]],"v":[[-93.398,-243],[-93.398,243],[-20.664,198.918],[63.092,112.408],[93.398,-4.408],[93.398,-179.082]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.172999991623,0.74900004069,0.862999949736,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[407.249,313.852],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-19.837,-35.817],[-34.714,-20.939],[0,0],[0,0]],"o":[[0,0],[0,40.776],[19.837,35.816],[0,0],[0,0],[0,0]],"v":[[-93.398,-179.082],[-93.398,-4.409],[-63.092,112.408],[20.663,198.918],[93.398,243],[93.398,-243]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.497999991623,0.823999980852,0.889999988032,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[220.453,313.852],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":594,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"back Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[499.852,499.852,0],"ix":2},"a":{"a":0,"k":[313.852,313.852,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":60,"s":[100,100,100]},{"i":{"x":[0.187,0.187,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":107.824,"s":[95,95,100]},{"t":188,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-21.315,37.892],[0,43.744],[0,0]],"o":[[0,0],[0,0],[36.964,-22.295],[21.086,-37.489],[0,0],[0,0]],"v":[[-100.898,-259.697],[-100.898,259.697],[-20.417,210.92],[68.666,118.919],[100.898,-5.251],[100.898,-190.646]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.922000002394,0.922000002394,0.922000002394,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[414.749,314.695],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-21.055,-38.017],[-37.43,-22.576],[0,0],[0,0]],"o":[[0,0],[0,43.16],[20.853,37.65],[0,0],[0,0],[0,0]],"v":[[-100.898,-190.646],[-100.898,-5.251],[-68.714,118.833],[20.389,210.903],[100.898,259.697],[100.898,-259.697]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.957000014361,0.957000014361,0.957000014361,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[212.953,314.695],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":594,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[500,500,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[900,900],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.980392156863,0.980392156863,0.980392156863,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":594,"st":0,"bm":0}],"markers":[]} \ No newline at end of file +{"v":"5.6.3","fr":29,"ip":0,"op":72,"w":335,"h":435,"nm":"Pre-comp 3","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":2,"ty":4,"nm":"▨ Mask 2","parent":4,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[15.05,-17.868],[-3.73,6.912],[-14.69,-4.078],[-20.5,1.742],[-3.44,17.872],[20.5,-13.718],[15.05,-17.868]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.592000007629,0.592000007629,0.592000007629,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Mask","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":1740,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Rectangle","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[97.177,41.09,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[53.615,53.615],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.924224019051,0.924224019051,0.924224019051,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1740,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Mask","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50.5,46.128,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[15.05,-17.868],[-3.73,6.912],[-14.69,-4.078],[-20.5,1.742],[-3.44,17.872],[20.5,-13.718],[15.05,-17.868]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.592000007629,0.592000007629,0.592000007629,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Mask","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1740,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Mask Copy","sr":1,"ks":{"o":{"a":0,"k":13,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50.5,46.128,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-3.73,6.912],[-14.69,-4.078],[-20.5,1.742],[-3.44,17.872],[20.5,-13.718],[15.05,-17.868]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.254365801811,0.638020813465,0.890196025372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Mask Copy","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1740,"st":0,"bm":1}]},{"id":"comp_1","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Fill 7","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[172.095,150.15,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-5.77,10.22],[-0.01,11.82],[0,0]],"o":[[0,0],[0,0],[10.09,-6.07],[5.76,-10.23],[-0.01,0],[0,0]],"v":[[-27.143,-70.455],[-27.143,70.455],[-6.003,57.675],[18.337,32.595],[27.148,-1.275],[27.148,-51.925]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.172999992967,0.749000012875,0.862999975681,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Fill 7","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1740,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Fill 5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[63.525,150.15,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-5.77,-10.39],[-10.09,-6.07],[0,0],[0,0]],"o":[[0,0],[0,11.82],[5.76,10.38],[0,0],[0,0],[0,0]],"v":[[-27.143,-51.925],[-27.143,-1.275],[-18.333,32.595],[6.008,57.675],[27.148,70.455],[27.148,-70.455]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.497999995947,0.824000000954,0.889999985695,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Fill 5","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1740,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":2,"ty":0,"nm":"▽ Rectangle","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[174.125,198.5,0],"ix":2},"a":{"a":0,"k":[50.5,45.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6,"x":"var $bm_rt;\n$bm_rt = thisComp.layer('\\u25bd Group 9').transform.scale;"}},"ao":0,"w":101,"h":91,"ip":0,"op":1740,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":1,"nm":"Medium Cyan Solid 2","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[167.185,215,0],"ix":2},"a":{"a":0,"k":[483.57,807.91,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-31.563,-39.907],[0,0]],"o":[[0,0],[0,0],[0,0],[31.563,39.907],[0,0]],"v":[[483.57,667],[373.57,703.91],[374.186,808.999],[402.822,887.003],[483.57,948.82]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"sw":750,"sh":1334,"sc":"#7fd2e3","ip":0,"op":72,"st":-108,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 3","tt":1,"sr":1,"ks":{"o":{"a":0,"k":28,"ix":11},"r":{"a":0,"k":-143,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":45,"s":[153.486,-70.383,0],"to":[11.551,10.946,0],"ti":[43.882,-87.33,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":58.576,"s":[79.705,135.2,0],"to":[-39.952,79.508,0],"ti":[-6.507,-6.166,0]},{"t":71.505859375,"s":[10.486,299.617,0]}],"ix":2},"a":{"a":0,"k":[-8.014,-330.383,0],"ix":1},"s":{"a":0,"k":[205.707,162.043,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[101.215,-117.668],[-101.215,-117.668],[-101.215,117.668],[101.215,117.668]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.75,0.996,1,0.446,0.624,0.91,0.945,0.68,0.498,0.824,0.89,0,1,0.282,0.5,0.681,0],"ix":9}},"s":{"a":0,"k":[-99.658,2.936],"ix":5},"e":{"a":0,"k":[102.286,-3.236],"ix":6},"t":2,"h":{"a":0,"k":0,"ix":7},"a":{"a":0,"k":0,"ix":8},"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-8.014,-330.383],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,26.666],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":72,"st":-108,"bm":0},{"ddd":0,"ind":6,"ty":1,"nm":"Medium Cyan Solid 2","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[167.185,215,0],"ix":2},"a":{"a":0,"k":[483.57,807.91,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-11.53,20.45],[0,23.643],[0,0]],"o":[[0,0],[0,0],[20.178,-12.143],[11.528,-20.448],[0,0],[0,0]],"v":[[483.57,667],[483.57,948.82],[525.843,923.257],[574.526,873.092],[592.139,805.354],[592.139,704.064]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"sw":750,"sh":1334,"sc":"#7fd2e3","ip":0,"op":72,"st":-108,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Shape Layer 2","tt":1,"sr":1,"ks":{"o":{"a":0,"k":23,"ix":11},"r":{"a":0,"k":-138,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":36,"s":[301.486,-6.383,0],"to":[0.667,1.333,0],"ti":[64.558,-73.378,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":50.869,"s":[182.705,158.2,0],"to":[-69.952,79.508,0],"ti":[-6.507,-6.166,0]},{"t":63.798828125,"s":[40.486,349.617,0]}],"ix":2},"a":{"a":0,"k":[-8.014,-330.383,0],"ix":1},"s":{"a":0,"k":[212.557,219.437,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[101.215,-117.668],[-101.215,-117.668],[-101.215,117.668],[101.215,117.668]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.75,0.996,1,0.446,0.624,0.91,0.945,0.68,0.498,0.824,0.89,0,1,0.282,0.5,0.681,0],"ix":9}},"s":{"a":0,"k":[-96.439,-16.582],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":2,"h":{"a":0,"k":0,"ix":7},"a":{"a":0,"k":0,"ix":8},"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-8.014,-330.383],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,20.03],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":72,"st":-108,"bm":0},{"ddd":0,"ind":8,"ty":0,"nm":"▽ Group 9","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[167.185,215,0],"ix":2},"a":{"a":0,"k":[118,151,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.67,0.67,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-50,"s":[100,100,100]},{"i":{"x":[0.67,0.67,0.667],"y":[1,1,1]},"o":{"x":[0,0,0.333],"y":[0,0,0]},"t":8,"s":[100,100,100]},{"i":{"x":[0.67,0.67,0.667],"y":[1,1,1]},"o":{"x":[0,0,0.333],"y":[0,0,0]},"t":19,"s":[98,98,100]},{"t":35,"s":[100,100,100]}],"ix":6}},"ao":0,"w":236,"h":302,"ip":-50,"op":1690,"st":-50,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Combined Shape Outlines","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":28,"s":[84]},{"t":70,"s":[3]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[167.554,215,0],"ix":2},"a":{"a":0,"k":[109,141,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-50,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-49,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-48,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-47,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-46,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-45,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-44,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-43,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-42,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-41,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-40,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-39,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-38,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-37,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-36,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-35,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-34,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-33,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-32,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-31,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-30,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-29,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-28,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-27,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-26,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-25,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-24,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-23,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-22,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-21,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-20,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-19,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-18,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-17,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-16,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-15,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-14,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-13,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-12,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-11,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-10,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-9,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-8,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-7,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-6,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-5,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-4,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-3,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-2,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":-1,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":1,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":2,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":3,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":4,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":5,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":6,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1.002,1.002,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":7,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.619,0.619,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.083,0.083,0]},"t":8,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.741,0.741,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.107,0.107,0]},"t":9,"s":[99.972,99.972,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.759,0.759,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.123,0.123,0]},"t":10,"s":[99.87,99.87,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.764,0.764,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.127,0.127,0]},"t":11,"s":[99.657,99.657,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.801,0.801,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.129,0.129,0]},"t":12,"s":[99.255,99.255,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.857,0.857,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.143,0.143,0]},"t":13,"s":[98.515,98.515,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.871,0.871,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.199,0.199,0]},"t":14,"s":[97.485,97.485,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.873,0.873,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.236,0.236,0]},"t":15,"s":[96.745,96.745,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.877,0.877,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.241,0.241,0]},"t":16,"s":[96.343,96.343,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.893,0.893,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.259,0.259,0]},"t":17,"s":[96.13,96.13,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.955,0.955,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.381,0.381,0]},"t":18,"s":[96.028,96.028,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.637,0.637,1]},"o":{"x":[0.167,0.167,0.167],"y":[-0.1,-0.1,0]},"t":19,"s":[96,96,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.757,0.757,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.108,0.108,0]},"t":20,"s":[96.013,96.013,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.779,0.779,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.127,0.127,0]},"t":21,"s":[96.056,96.056,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.787,0.787,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.134,0.134,0]},"t":22,"s":[96.139,96.139,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.137,0.137,0]},"t":23,"s":[96.276,96.276,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.03,0.03,0]},"t":24,"s":[96.489,96.489,100]},{"t":70,"s":[151.489,151.489,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-11.529,-20.77],[-20.176,-12.143],[0,0],[0,0],[-11.53,20.45],[0,23.643],[0,0]],"o":[[0,0],[0,0],[0,23.646],[11.531,20.768],[0,0],[0,0],[20.178,-12.143],[11.528,-20.448],[0,0],[0,0]],"v":[[0.001,-140.91],[-108.57,-103.845],[-108.57,-2.556],[-90.956,65.183],[-42.275,115.349],[0.001,140.91],[42.273,115.347],[90.957,65.182],[108.57,-2.556],[108.57,-103.846]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.660125613213,0.885631144047,0.94950979948,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":23,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":28,"s":[8]},{"t":70,"s":[0]}],"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.498039215686,0.823529411765,0.890196078431,1],"ix":4},"o":{"a":0,"k":42,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[108.57,140.91],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":72,"st":-50,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/src/assets/onboarding/driving.png b/src/assets/onboarding/driving.png new file mode 100644 index 0000000000000000000000000000000000000000..1684d2705fab6bffb7bbeed83cbb608e3ae52726 GIT binary patch literal 11169 zcmeHtWmgRQ#iYVDGzqE(e;(2$9dVPIg;6PPj2A{GDedW1P!}^Ia}*=XM>SnF zT#g8>14LwwX+@de3eNC1o~hx}w82quC{Y=*q8&QolnQAYnv@}=rvs_ z=a<)iONMAOi@VN$z17b#RGwD(zCXSPZdV8Pytqgs7_`bMvOjl{+$m(agV1JM4?My%JWRit6<$C)fq=P~+@@_~Tz1St}j}14fxXi1y$<}xSiMNdsmEN&n zsa3IHyQ}{J9hE^ef3T7G(=76c~1_kF_R2!r&8Q%8}K;fDNqj*rL+$Z_!F;;nms?T^{^>ns|%(FY9s7EAj;5D%h}2 zILWk;{_ z))G7{cA0)Hu#;S;mweu1KJu|Aa}}t`MLCNrK|Gtf|H+qsaBLS>=d{RHOQX>$mdOV< z1F=0Us2E(|qVV%3?`kcCq`XLyfox0d^RBopsss$WGa2bF^XF!*BH*oV6|C^AJ0>g! z_K(dQqdrTJ`Fmb)6XwmDwfQq4@%9k;0ls&t8g;saE4dWH;$+p$t{=W)(xU1VUAFjsySc|B^2>>K zaOtmS3ee|+On3l71aw^)$)jq&(Q$UZ6uUgI7=ilQ-K8(!BU=hjqWc5jCP_`~{EjrH zP1B2Ku1ek(Pcf)}B*3f#RygC|7*nG*RaB#bmH>(Zy@%o_8|zIb<0zg2ReWRo4yYs7 z@Nlp2xlI&2^hGR3u6d6kqb0@fh?)lw3qK>f&X=j2Xco0Wp~XPmaq4QM0sy4b^tG$f z;^1MxQ>@yY_DOQb0nSpWw-s%3EY`mltvW3S(n|Ooi>N6LqkJt_XIGh)nRLvYELs>P z`UH^o^bbEZ_j45)Zd4#xa89`u(}+?-#ElsGlf4R=$KzIV#8q}zAPW9Yy1fUUE=LR9 z3}JQ*ZOo9v#Nv;FkqgQOv0T`73I6QbA{OYi?lab+;0 z!DMWHL<(^5;JlrAB5jg5p@hT7BwV<`vog5AV__%8V=0~&16 ziaPSh`!4;GlK37aY6(ab<2&1(XHw!iGpSdE3#W#Q#OCk6G0LB%|CrH$FQHXN_RMHR z1viV?|4}h}P*KQ6jC1`e?jvsXHsXazozd(dq3IsFt8ak9*Xs?MyOQZ_&II3W;>Lb7 zz=Kfl%w?uivC_4KqiT#hB^&}yqL0SWLTLSx6SXQ*u(+0&n#`liZ}vRtqarq2I$Wl> zPk{NgB!aAmf$*Dg3Ww-K>YGo&1K9ayuP6e-+hq+-s4NF+2U^>Nk`esUK?J-ItTz&C zB=51sAnMPdL58=+$68!A`^B6E3&oL`^FONBqz>-Ucf|<|moq{6)@wD)S`e8nIKY=KZ`l6^l-rpqF}Z^dOc=0A6CyclcHrj zJDmT`hLi{o5$*s2mo$AAkDKG9!Inyk4eN)PxZf1nbp3U}xup9#jye=h0=n;}-?LEZ z;3NxVp>z@;HyQ>D5f#?eKUmI|GX)Vdm>Nn;VY}1ANm8-{#&6*1D<@Q;CX0d6cyjZZ zP>y~~sGTxO=p~@9GGbGX>ClRSpd9}v{C{r?BHnue7Zzh=5IZ5;v5)1PYi}OPLkXu3 zEo0(;yUMxNW6llg###SIe*BK4gecr%Du2m)ZLN~knF=ESY%5a?`n#9&mdfSOv2*cQ zDS58~D=k)LAQ?oOFJ=1nIn7S7ptJg7CkI4zp^U%iBsWtLD4l7FEeVLA5(PUEGP>K? z!y#`#@_um#y!v;0l`cm3a4$tD97Rsht{ih}3lJArA!+R+$V>jWKT z-+Jhb0+-b8A8RK`pITTuO}AKkSLCJ)x+KZYF9m{$ z_N-?|Sosh2bcN^#0M$bsACHL#Bv<9gE~B#_hqjDsjGGkLmqYnC2F!(zgzG^$ef(x$ z-Qw1VXfD9|y!thS6TEPYj13p+qn}YlMFoxH` zIQ|$Aqs1PErZ%2jy?EYry!GMnm@?@tyEb{{5gG6t(=D!(96*0*Eul#BG1aIib8bI8 zH;CCzZug|H0jYqSA07Qqvgxy&xlUOv&t{AG3WP0tuYq9{{qmAw{KiM<&A;-^mFj-R zhF@^@9nH>!Os01>!UEr(8puR&bh|gW{MTsCS?PW5$op;cq+7iml7Z%#LI{7^p+?Q< zJ53H(dztCR*9J0?vhNe?+H`psmTy3%gF)~PF*^v1wf(~qyX-YX<~zq0s1ZHK#&qf; zq`%R0jT2!#(iixrohd;5os9j6&p8OiJE~OQYS{Qb)&(p8tQm_?=N8;LMdS`1A@SlS z{Ln!`DPkD+KwlNy&dx{aVAVwWj^i6vk8!%5s%s`wmv(tN)DXAaTKdSYl1y``gq3*d z9lOq@>JwGAr2aU22TnJ`L7O#?qFMWv;~WoC=Q*2Q;ocwrxCh(R5KgfK)WEtVm& zI?m+;=dFvVkA*YEfe}VkGjQC#y*`bQZRR^0W+LM{Pv5})?xxEXmeDTYg1@1J)#DlD zZ>UBaxGGl9L%?y?O&u}w8S+L{!rvCp5^NfH{oxz`Z2=lwWVVjop0+1XWz4JPRJ=3$ z*#!N*I$W;3+%$A8+Ob8YcWe}O*`IR_KW;3pZExnxO4~6r&S? zXF65Kirwhi?h{H6B)54>_^g%mQ(Y8>i~@9JvDZL$m0HC z9YxvUzoH-&8TUYvAWApq>WT8>V4bw@s8IoWr6~t&$sN`Qfrs|jRXx&Fn;TM^CL7jV zn>akA)lO&ap_CYLUrN=Dh^u|6S#JPcurEf10&^8@zt6{5Ie%6jmS=+0uUeDTW98qi zh#J|wyI5$&8B(!^8)d5P?D#4T%avvaMb!fEAfUmfMD_fl8s6Z!`0;G3*DPDAvMGKfl1!mS+0w+$!WbxZNE7s+j>2FESUrbfo@cf=iYesj~gekW~|-K;Vm zSO0%gpi;fzwJD|OafedjFV7m^F4v?gwgSBG%TF8=kthSF0}6%j@*T~fC*>z5vnKMa z(ZGtY9!B>&dGU%p{Z@aO1lh1LFF0AbUzqqRl)-aom8MPT&fZRnc|>n5Yu1ORZi+gc z12v?`6r^wv*{$~G;*A?KQY~dAQy8u!Mw9I!t7x@ zZ+R`l*s(kEDw&A%FC8rzCmxd8nnu(1LDD8yM*lz1FY&3%$vZ1KJb>hsT#NoD2Jy}0 zk9uWC`||i6t6Dx)5)t9@t^$6LAM=b$fS)MFsL?$eg{*sK71JM#&&G{I3R$jQ=aFO9 z_W?WiV2AUQ-gzdj@f!iDt!Qt7_olVnM*Z*#UR0M-oHLCaAdM-Z&J;(1vaS?(4(SYi zq>{t2vzbAV^H}U{0zFf7;W-LsJ*xcCX*_Q~OsTQ=H(|1%B$F^*?PYRZNE|Gho`+n@Hak*yn&#mq&r>k81R9u{z zk*Vg`@B|9yVV+Cf7R?4N{%ljp3Io?Wqf)vg*XsVwVCU*jId)AN6DRwgXUAvt!MT)h znCYjdHXrwoivhdXn*JYFAeNtQ#y6b<9MvZ2>_7fmeWZf0@%Nn1Cpo9F54XtnW!9-< zPa;|w1|`>m6V@TW(@<&_K*uSn_vBnffgMIai=N%nh=^IeNVNZUn>-#S6-hGN5FD|o$SM7h&fPBt)b6TB$qmm%>GE22@yNJ>5qDxPu(9&?2KcruK|nNGiTH3JZDv+_nPLd(C)17HtXZ^aip?l$ z$aVYZ)p6lGNq3cJatY&L6-Ctt<+MdB1(qjZzH)z)U${Cf|t&YEijZiNx zMUe&yG3d70m8*d+0blp5OlYGO;u2%Wu6UmFTqU|FZb1RBVO2wHd4^5D(7%9nJk-!O z!j@;Q73D@9iOu$2t6`|#g`*r_!VMNPV+ldqVl8%AWI^5J2tdZRY!_en7lenr^OU7z zzYyzAe)E7FsKa+NiqNip-ihlT$an5sk3}c^c6}AhHX!ZZuN%mJ4^P33n)$pH&C}pj zX2tWHKV(TLD9B$L$TBqk1L1cG#Sr$+7mLm;lz~=liI@mn($=~e8!%rDi*a$2KHT@( zIvY2|KvKMl@DUXIDG)RgvWBfKe~-)4nrMySj6-FyWZby~Va>JDo-1A+(6_}5uRs_h zDK`^iWXN3@ulgl7NE1w1R6To^KGNj(s5t6cMv{a1fqMFOV3|H}eVkfl5~sb`$JAG8 z$*4kn&L~D8*i&G9(z{A$P1rGw32CZkuG}JLEr%FPe&>wsJMB(@0b$JMYd@wD zksAm#mQ!_KqG0cYra3A~>SQeB4yLIT2oQClP)x5(VN`w&BW-E0keFjSqeW+%&>mx` zW?LQmd$g-n`C65tS3#`xdC-fBZw4okhw)$EP=`&8_(IL&`3$N#+hY48vpoGUsnWsawyugt4F>Z zczyqm)Y>z1IqmAH-PX;)!yo>zA*vz=uV6AUgmqk#ZVty4StGW2N*}u+Wipf9C$g*} z_$lk>&-KeZQwmDD?D7?l!nB9>C3JSp#ai}DIq#r}Jv_Y3FN(0OLpme$2n1dcNnbZ+f`s<5pe$15*lkA)|DTTBi!jzKekMjcgOJ7(zN2xuI4QRbs z8&p1JaO}=BkQcqoVy?CgEG5ql18KEq`*;u1PesVw{MFs`Za02q`&g>^t>`G6UZ&2C zgDeS&mYe??V+HXFD^hh92w?g3Bc~b&`6-JcJ?pYqB+%j9sPlNS%A#+}B)k}*R|oYZ z4!>;hx>MB4V~ly}B|8NC3F0dPJyb3K%~WekALeRAvla9u|KL%ebWKvRrQf#Z`uSMX zCc!4_3Zj5lF|8TgK)iI#tNSktIfNOF+Ex5$=~xrdW2U#0Iqs$yRWZXTIQQ^K-_oE^ z!(7IS3n-da7s#9Q&wBd>XOyBhu-(sn#t2%JWge@Y`Q{hvo9zOO39`$+D!|E9cY?+5 zMGvf3{BnPd_1dOZ^_^TqN0Sk`bFN+ABlC8cJ<(L}JS%=qt8wnN04t+snppPo#HN$x z>xJNlvrQ-egc#zPw1sM=H$CuI!D=T zlqILg(WU5sei~IjSB>{?PUhslwWl1<(-{BCy3$YKL-p^NI87al%X1Neb;+2X<20Fi zH3GM0-6_na6g!IM9kN<_&czj$q&ZAZOC~_{n-5DPjLZ9?gD4Qk{XgBcO&^!87Z)zL zx>aHr43yNUUK=j226WxIQ~CL`DPXbfoC7b<^=_APC7?ky8o_+wEz5E-$tn3t-c{mt}*=Q$e@ z5Z8~uTi&z+wrJwbzk|KM07Lp^VNW~t;jHbA%EZ4K_#G)g_CBdn#aduOu@}tr$wxDO z66Add^*+st>0gYqO)_vI*$O*CLxADW`^9tD4_2(dJVMEsHAp4d`b~P z%V`yNm-Dq~e#={aihy&Y)L-?E*r^loJJ5PXyEzA)0jyr{!{+SHC`Cwxx z1V1$?qHWV%OX8&h`rx;rmb1c=Dz+OXiiuc2nGV`@iT-(yL^Af<&3yYKAI9$-{C!`a zqhi7fe*o7icVUC{1&r&?6g|YkJ<)%X2GgtPn?v|gr!u-!9&hPTe_pJb5s;ls5vinK zvOgbSu^Y7iE!aaRUT=>D0oGIWg*|6)-F1-vFKhQZiS^u<#Y%|Q$azc;rY4aD)DK4h zf3^r7e2AKcK@H@zTL9RO$NoCQf4Vr-jEq>SvOyvPneAFDRe2R;I!$H?17Qm*o2zs2 zHoEUu7O2=jvUg=^C70D+`0$uGM&ug`mTeMOe;R7n_xMd8^Va0d@wYHpi7BLU;e7bf z!n1V`l>H zjN|Ns_U5n-xdUB-O^(Sdx2HPZ%J==z5qfEcVOw$iUtJ5%{zzrJK1H=F%#s0VvCRaA z`y#dG4UeA{Q?BkKIORrY`hIl;a4sXR^4xcl1x`Ii$oqRHwtfarEJu?Yb8^19mW9`E zJsy%@6{fqW{p)IQka)bI2KkfS$@R)=-R`RTToVJWzcF$c&40Is#OqyV<0Kt#taq_t zPU{9&)h~jVIoz`q>3TU3JDzw627zM?UJWaM*$Nn5bJTLSVB7Vrr1KPJV!jc%C8?YrcMYDbz+R`Mw}bYmZ~b8pcisjb4y zQA!8ROX*1c*g)$%jaiz}sL1=Qu&4LjO8&GwTn~L!JPSS$+c*qf%_%auJk#zZ(rsDy zFV1A4BSYHNOk{XztX-no3e&?pULUuUv+&*sP>J6;kAfewlcdZp>4La7VwHXXi)}?R zLR*~VfA+=^59}kDv|Y7TP>+GgjQV7ad+P;@hV#-SFVNj%a+Ej77zb>6F!ivP?(edo z{XACi^gXiF;fV~mo>X7V4?nY46-~{)Ehn24okQTQ!Jxq!yF3GibXbMiVUJM;&%U{( zEa!Lzr~sa06{Cnu|AnE4)-X7%02($H#n{zrOrJ-7qFwp|%k5T!6MgdQh%Ww<7(rI8 zfeF-;gp@*F!%BZ8XmURD^Xhe~w$gkgY9w)x$vW^b%1_^W@Xu&I8|M`gfWjEbtz&zQ z#m@^lahuU%H6nLV2)8;G++>h#wJc(nUbxS507ru1>d!m)ps(qvC1NXeFMm(kyT=0z zSmq<$b9J)X0O$1x$G1vFM5PD}=@LOPtXS-oBdg9?D!zAnJ&=PFud&`dhutygdg>oPXMX zwkLW*`s;P?@}S-H(_rUY^q}dxtqf8PPwpGzUdI;F*GaN%|5*h7m3sHV4D~(9xn1y? z%X5Of&+_G5KgVUOx=iL%L;0;&ZQQ=;+P7c2H}U8)NR*o*8);Yn(o*G{>#he|EA|3a zt1DVL^L8z!9;G|>ld+V*E88L1zdCoRJzk+*EzUswO#|?Gp*ZY7mu3`^(nrpt%tk@Z zbuQ-X=-kX=tw|caRIL80@$I4IQjC_mIp)m~6-P0Y@<)VewO0Z2qAJeI>F~kF*H5@X zH3Dh&Cr_fle1p)Lx-mT*i6`~l)BLZ|tM*+-6lwKlK2#U$8f`xkVbZ>Tce9&X`te#$ zP}-&a^+VQND#dqkT`8FB8I#PN>WB&4TWM6vmd<>IhG)bO1jn5%kVkddA&@61|DfvM$c9 z!90s{%vgl9Fk_E)3RRs_{jP9=D}>sc9-e>E{2O?8S?+B_1cWDaoo@`2TK8arf*K8`7O=EI$^(CfiFa%M6Se&4mVjwAVP>@ z3-49UQ*?CpWz15Yw?3Z*klOX(^NwyaTIR8o|4;JKEnPdn5^p>T$;RD>^;tuuZ@rxf zhtFk=U(UFn)?TW_>K(1Lo`UmiYs|M3=gf@h36?SklqElZfiGPyj&VIz1k})DE09Z1idD{!{7d0beFm;U-{g=G zz0!&0?IoERFZdxzi3m9Gqt4B1P9@F9Q2(Mym$qj|pAIMvg~GOGh~tkEy$mA;2ap)r zFaC~#-Gw+bdxy?cf>AJ7glR$TO8q{~*%= zB!${jS$EfN)F<1GSan*mKNkSlua!{nQ&({Hzf!E8Y_5tApdkB8!i=I=7!~x6Sm01! zTqfO1fIxl#K|k0%HL3jA`+>#OBy3n30iR5gauup_5ZV6c=SkY$J5y{Z6aC)Jb~x-_ z$mMm1>gP~lQxe32vq*kLD$R4j|Ih`#0w^OXr>9S)Ro5 z0nLFw#+JVSWXlb5_qASm37dW|4lnBHEMB-7=%G8^ViH(*XU7lZA4NDyM3OX>gmd-{ zBoZk+yI032EyLc9B7AcxX3}edM~N`r#jqPkre;Tmo$b|%SxUwpzlk4QU~5Yp=k6Y1 zicR|Y#t#<@d{0^5hp_-#R@51X>fPzvUfyA4^%&LdSFRy%@38re=b@0t&A z0C`O-GH#1(s{JoN^W8-4J4yH?()Qd?7*^F8Fi~jnUph>O1BKx^nSB%g6A6XqkJD3V zg`j3-gyNsQ-_;gEp@}vm9EyeZmC&w<0ir}FpdfT=TnsraoMb;6lzyV4n?Q%Um^{5G zxZXnZo)RiF0172Tfi^i$Oz6BCCv+@;uDA#RiuwORfzknTa*h9a)pw-CeHJ1-VOc;_ zkO_3&;7&pQ`+sSDiXwEpV4~tg3@Y?h911I&sJr3{h3$07bX-;$tit)GSK}hLa~jKI z6vuT-fZ1S(vLpX08FOeL4smt*hF1~^qtWwbBRPR|Kh>nyum}K!G)4>Cy|rpVIniZz z-?{1~_&?y))^W@o_ef~J;rF7Q?b`>pw|oM?$MOnnGvZy{fR9ywyTMmsxN5?=wR<)DjX? z`*XYu)bBHdGW4D+!4aoDh0G5QUN%ze(DGwG=^W;?w=*jhpwJ zS3D;_67`}Eb|$1W3*G7Dp4%{=#YC2(BsqIJ<;j~}B;5oJ3cQr1G>fJGmptU09}giR zlN6C~AzN%M|E!518S_shcg_h?WXLoIv%tYy{7Ze(Jvx@ZryA2i;z@|Kk(0donp2wS zbE}*cvNdR;Bsbi$6FIu1ymr+GxmG^q1>CN`~ z$|%}Xi~7+I@!i~v^MS@-zjQnWOE+cK5Z!DWB)+QeJvi2g5Z>da{~Zk@n**9A6ZrxW z{UkthLeC#>-@}c&R5r}f1EbH^Ei1hbu>AIlb!({S*B9PglKASRwglFz_|a3Qu2I7O zPGwpB>L5|iUiJp~m29A<<#L(9ZbDxd zE{&7|Szqs(15HD`s8)qiWx~7R7+R!pUaOAu7I>--lJ_;F78zXYwi}x`+V}#?*l){+ zO8B#q%J;s&|JrqCqMwuo@y}jb`)q~vZAlvJy}RN+8!0rRgA>q2aI6)sy~|BI<~Rp{ z)cis@C>&@#n8`b&3b$Mpqv%0%_$ic!;&^0+OBCqwOV>v1rJmWgj_(*bW!ka0m=rYs PjmOGKDNELgn}+;93$`GQ literal 0 HcmV?d00001 diff --git a/src/assets/pdf/usage-ar.pdf b/src/assets/pdf/usage-ar.pdf deleted file mode 100644 index 0a4421b28501e0438fc5d8034df3e802a990bc37..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61063 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�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 diff --git a/src/assets/pdf/usage-eng.pdf b/src/assets/pdf/usage-eng.pdf deleted file mode 100644 index 5f3b8fbdc5d2b983677c3b72ec292f07614aac2e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55104 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 Wmd9qfZDfgAqjflW_N=^EO#T38iuXVO diff --git a/src/assets/pdf/usage-ru.pdf b/src/assets/pdf/usage-ru.pdf deleted file mode 100644 index a51cb16a1d404697fc3ae9c7d5c73f0f58c5effb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49622 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đ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�zC``0N5a;N{QLD*wEj?4Y{-kq6-91})zIP40Dl{d-v;J2z-B|CR^f z0(H|r@>qa?S3J|-*(x3b)?j_2=XL03pDpe5pO`T!t{{AXK0a<9hr zH#s)Ysr^$QC=d8geQexpAP4l%vRthHEXxiWtACyw00`vx4|yP`^>=*$5SZ(qbpSZo zLDzu)kOM9K|6LBq`OmQgaJ^ct&ycmA4tlgqW2b96-Je zr2o~oN!yz{fZX|?2{AGqGComJ7EW;%ZgDOypqRLruqcPPxG0ANn>dh@or8;&ol5}W z|1I*mR){&6h+3L~0NieNWXvLvVQ; XHF9?SZR!9nE)FgPN=k7B355Rx!XC|t diff --git a/src/assets/pdf/usage.pdf b/src/assets/pdf/usage.pdf deleted file mode 100644 index 6878a9fb2e4b4d7ce393c34ce5124ab0f54897cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45190 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 diff --git a/src/components/Loading.tsx b/src/components/Loading.tsx index 42a5d641..dc46a1e8 100755 --- a/src/components/Loading.tsx +++ b/src/components/Loading.tsx @@ -11,12 +11,14 @@ import BackgroundGeolocation, { State } from 'react-native-background-geolocatio import Welcome from './Onboarding/Welcome'; import Location from './Onboarding/Location'; import LocationIOS from './Onboarding/LocationIOS'; +import FilterDrivingOnBoarding from './Onboarding/FilterDrivingOnBoarding'; import LocationHistoryOnBoarding from './Onboarding/LocationHistoryOnBoarding'; import Notifications from './Onboarding/Notifications'; import AllSet from './Onboarding/AllSet'; import ScanHome from './Main/ScanHome'; import ExposuresHistory from './Main/ExposuresHistory/ExposuresHistory'; import LocationHistory from './Main/LocationHistory/LocationHistory'; +import FilterDriving from './Main/FilterDriving/FilterDriving'; import { Loader, ChangeLanguage, GeneralWebview, ForceUpdate, ForceTerms } from './common'; import { initLocale } from '../actions/LocaleActions'; import { checkForceUpdate, toggleWebview } from '../actions/GeneralActions'; @@ -29,6 +31,7 @@ import { startForegroundTimer } from '../services/Tracker'; import { IntersectionSickDatabase } from '../database/Database'; import { initConfig } from '../config/config'; import store from '../store'; +import { ExternalUrls, NotificationData, Strings } from '../locale/LocaleData'; import { ValidExposure } from '../types'; import { SET_VALID_EXPOSURE, @@ -48,11 +51,14 @@ import { interface Props { isRTL: boolean, - strings: any, - locale: 'he'|'en'|'ar'|'am'|'ru'|'fr', + strings: Strings, + locale: string, + externalUrls: ExternalUrls, + notificationData: NotificationData, showLoader: boolean, showWebview: boolean, showForceUpdate: boolean, + shouldForce: boolean, showChangeLanguage: boolean, usageType: string, showForceTerms: boolean, @@ -69,11 +75,14 @@ const Loading = ( showChangeLanguage, strings, locale, + externalUrls, + notificationData, initLocale, showWebview, usageType, toggleWebview, showForceUpdate, + shouldForce, showForceTerms, checkForceUpdate, termsVersion @@ -117,7 +126,15 @@ const Loading = ( await purgeSamplesDB(); const state: State = await BackgroundGeolocation.getState(); - !state.enabled && await startSampling(locale); + + if (!state.enabled) { + await startSampling(locale, notificationData); + } else if (!state.enableHeadless) { + await BackgroundGeolocation.setConfig({ + enableHeadless: true, + foregroundService: true + }); + } await startForegroundTimer(); @@ -144,8 +161,8 @@ const Loading = ( setInitialRoute('ScanHome'); } catch (error) { - // TODO handle in error handling phase - setInitialRoute('ScanHome'); + const notFirstTime = await AsyncStorage.getItem(IS_FIRST_TIME); + setInitialRoute(notFirstTime === null ? 'Welcome' : 'ScanHome'); onError({ error }); } }; @@ -171,18 +188,20 @@ const Loading = ( + + - toggleWebview(false, '')} usageType={usageType} /> - + toggleWebview(false, '')} usageType={usageType} /> + ) @@ -197,11 +216,11 @@ const styles = StyleSheet.create({ const mapStateToProps = (state: any) => { const { - general: { showLoader, showWebview, showForceUpdate, usageType, showForceTerms, termsVersion }, - locale: { showChangeLanguage, strings, locale, isRTL } + general: { showLoader, showWebview, showForceUpdate, shouldForce, usageType, showForceTerms, termsVersion }, + locale: { showChangeLanguage, strings, locale, isRTL, externalUrls, notificationData } } = state; - return { strings, showLoader, showChangeLanguage, showWebview, locale, showForceUpdate, usageType, showForceTerms, isRTL, termsVersion }; + return { strings, showLoader, showChangeLanguage, showWebview, locale, showForceUpdate, shouldForce, usageType, showForceTerms, isRTL, termsVersion, externalUrls, notificationData }; }; const mapDispatchToProps = (dispatch: any) => { diff --git a/src/components/Main/ExposureInstructions.tsx b/src/components/Main/ExposureInstructions.tsx index 82bf1256..3c10bb75 100644 --- a/src/components/Main/ExposureInstructions.tsx +++ b/src/components/Main/ExposureInstructions.tsx @@ -3,13 +3,21 @@ import { View, StyleSheet, Linking, ScrollView } from 'react-native'; import moment from 'moment'; import { Exposure } from '../../types'; import { FadeInView, Icon, Text, TouchableOpacity } from '../common'; -import config from '../../config/config'; -import { BASIC_SHADOW_STYLES, IS_SMALL_SCREEN, MAIN_COLOR, SCREEN_WIDTH } from '../../constants/Constants'; +import { ExternalUrls, Languages, Strings } from '../../locale/LocaleData'; +import { + BASIC_SHADOW_STYLES, + IS_SMALL_SCREEN, + MAIN_COLOR, + PADDING_BOTTOM, + SCREEN_WIDTH +} from '../../constants/Constants'; interface Props { isRTL: boolean, - strings: any, - locale: 'he'|'en'|'ar'|'am'|'ru'|'fr', + strings: Strings, + locale: string, + languages: Languages, + externalUrls: ExternalUrls, exposure: Exposure, removeValidExposure(): void } @@ -18,6 +26,8 @@ const ExposureInstructions = ( { isRTL, locale, + languages, + externalUrls, strings: { scanHome: { inDate, fromHour }, exposureInstructions: { title, weUnderstand, wrong, keepSafe, goIntoIsolation, reportIsolation, allInstructions, reportSite } @@ -26,17 +36,16 @@ const ExposureInstructions = ( removeValidExposure }: Props ) => { - const relevantLocale: 'he'|'en'|'ar'|'am'|'ru'|'fr' = ['he', 'en', 'ar', 'am', 'ru', 'fr'].includes(locale) ? locale : 'he'; + const relevantLocale: string = Object.keys(languages.short).includes(locale) ? locale : 'he'; - const furtherInstructions = config().furtherInstructions[relevantLocale]; - const reportForm = config().reportForm[relevantLocale]; + const furtherInstructions = externalUrls.furtherInstructions[relevantLocale]; + const reportForm = externalUrls.reportForm[relevantLocale]; const renderActionButton = (icon: number, text: string, buttonText: string, action: () => void) => ( - - - - {text} - + + + + {text} {buttonText} @@ -44,33 +53,30 @@ const ExposureInstructions = ( ); - const ContentContainer = IS_SMALL_SCREEN ? ScrollView : View; - return ( - - + + {title} {`${weUnderstand}${Place} ${inDate} ${moment(fromTime).format('DD.MM.YY')} ${fromHour} ${moment(fromTime).format('HH:mm')}?`} - + {wrong} - - {keepSafe} + {keepSafe} - - {renderActionButton(require('../../assets/main/isolation.png'), goIntoIsolation, allInstructions, () => Linking.openURL(furtherInstructions))} - {renderActionButton(require('../../assets/main/report.png'), reportIsolation, reportSite, () => Linking.openURL(reportForm))} - - - + {renderActionButton(require('../../assets/main/isolation.png'), goIntoIsolation, allInstructions, () => Linking.openURL(furtherInstructions))} + {renderActionButton(require('../../assets/main/report.png'), reportIsolation, reportSite, () => Linking.openURL(reportForm))} + ); }; @@ -81,6 +87,12 @@ const styles = StyleSheet.create({ justifyContent: 'space-around', alignItems: 'center' }, + subContainer: { + paddingHorizontal: 30, + alignItems: 'center', + paddingTop: IS_SMALL_SCREEN ? 25 : 40, + paddingBottom: PADDING_BOTTOM(10) + }, title: { fontSize: 22, marginBottom: 20 @@ -97,22 +109,30 @@ const styles = StyleSheet.create({ }, actionButtonContainer: { ...BASIC_SHADOW_STYLES, - width: (SCREEN_WIDTH - 60) / 2, - paddingVertical: 20, - borderRadius: 20, + width: SCREEN_WIDTH - 40, + paddingVertical: 15, + paddingHorizontal: 18, + borderRadius: 16, + marginBottom: 12, alignItems: 'center', justifyContent: 'space-between' }, button: { - width: ((SCREEN_WIDTH - 60) / 2) - (IS_SMALL_SCREEN ? 10 : 50), - height: 35, + width: 82, + height: 32, alignItems: 'center', justifyContent: 'center', - borderRadius: 7, + borderRadius: 6, backgroundColor: MAIN_COLOR }, + actionText: { + flex: 1, + lineHeight: 16, + fontSize: IS_SMALL_SCREEN ? 14 : 16, + paddingHorizontal: 10 + }, buttonText: { - fontSize: 14, + fontSize: IS_SMALL_SCREEN ? 12 : 14, color: '#fff' } }); diff --git a/src/components/Main/ExposuresDetected.tsx b/src/components/Main/ExposuresDetected.tsx index 536bdeb5..1658ae90 100644 --- a/src/components/Main/ExposuresDetected.tsx +++ b/src/components/Main/ExposuresDetected.tsx @@ -1,7 +1,8 @@ -import React, { useRef, useState } from 'react'; -import { View, StyleSheet, Animated } from 'react-native'; +import React, { useState } from 'react'; +import { View, StyleSheet, Animated, ScrollView } from 'react-native'; import moment from 'moment'; import { FadeInView, Icon, Text, TouchableOpacity } from '../common'; +import { Strings } from '../../locale/LocaleData'; import { Exposure } from '../../types'; import { BASIC_SHADOW_STYLES, @@ -13,7 +14,7 @@ import { interface Props { isRTL: boolean, - strings: any, + strings: Strings, exposures: Exposure[], onValidExposure(exposure: Exposure): void, dismissExposure(exposureId: number): void @@ -23,16 +24,14 @@ const ExposuresDetected = ( { isRTL, strings: { - scanHome: { found, exposureEvents, reportedAt, inDate, fromHour, wereYouThere, no, canContinue, yes, needDirections }, - exposureInstructions: { weUnderstand } + scanHome: { inDate, fromHour, wereYouThere, no, canContinue, yes, needDirections, suspectedExposure, events, possibleExposure, atPlace }, }, exposures, onValidExposure, dismissExposure }: Props ) => { - const currentExposure = useRef(1); - + const [containerHeight, setContainerHeight] = useState(0); const [anim] = useState(new Animated.Value(1)); const scale = { @@ -52,14 +51,15 @@ const ExposuresDetected = ( } }; - const renderExposure = ({ properties: { Name, Place, fromTime } }: Exposure) => ( + const renderExposure = ({ properties: { Place, fromTime } }: Exposure) => ( - {`${currentExposure.current}/${exposures.length}`} + {`1/${exposures.length}`} + {possibleExposure} - {`${weUnderstand}${Place} ${inDate} ${moment(fromTime).format('DD.MM.YY')} ${fromHour} ${moment(fromTime).format('HH:mm')}?`} + {`${atPlace}${Place} ${inDate} ${moment(fromTime).format('DD.MM.YY')} ${fromHour} ${moment(fromTime).format('HH:mm')}?`} @@ -76,15 +76,23 @@ const ExposuresDetected = ( return ( - - - {`${found} ${exposures.length} ${exposureEvents}`} - + setContainerHeight(height)} + contentContainerStyle={[styles.subContainer, { minHeight: containerHeight }]} + showsVerticalScrollIndicator={false} + > + + + {`${suspectedExposure} ${exposures.length} ${events}`} + - {renderExposure(exposures[0])} + {renderExposure(exposures[0])} - - {wereYouThere} + + + + + {wereYouThere} {renderActionButton(no, canContinue, onDismissExposure)} @@ -98,9 +106,13 @@ const ExposuresDetected = ( const styles = StyleSheet.create({ container: { flex: 1, + alignItems: 'center' + }, + subContainer: { + width: SCREEN_WIDTH, justifyContent: 'space-around', alignItems: 'center', - paddingBottom: PADDING_BOTTOM(0) + paddingBottom: 10 }, title: { fontSize: IS_SMALL_SCREEN ? 18 : 22 @@ -113,6 +125,13 @@ const styles = StyleSheet.create({ borderRadius: 8, padding: 25 }, + footer: { + width: SCREEN_WIDTH, + paddingTop: 10, + paddingBottom: PADDING_BOTTOM(10), + alignItems: 'center', + backgroundColor: '#fff' + }, actionButtonsWrapper: { width: SCREEN_WIDTH * 0.88, justifyContent: 'space-between', diff --git a/src/components/Main/ExposuresHistory/ExposuresHistory.tsx b/src/components/Main/ExposuresHistory/ExposuresHistory.tsx index 6642956e..d4052b93 100644 --- a/src/components/Main/ExposuresHistory/ExposuresHistory.tsx +++ b/src/components/Main/ExposuresHistory/ExposuresHistory.tsx @@ -2,14 +2,15 @@ import React from 'react'; import { View, StyleSheet, FlatList } from 'react-native'; import { connect } from 'react-redux'; import moment from 'moment'; -import { Icon, TouchableOpacity, Text } from '../../common'; +import { Icon, Text, CloseButton } from '../../common'; +import { Strings } from '../../../locale/LocaleData'; import { Exposure } from '../../../types'; import { PADDING_TOP, SCREEN_HEIGHT, SCREEN_WIDTH } from '../../../constants/Constants'; interface Props { navigation: any, isRTL: boolean, - strings: any, + strings: Strings, pastExposures: Exposure[] } @@ -41,9 +42,9 @@ const ExposuresHistory = ( - - {Place} - + + {Place} + {`${inDate} `} {`${moment(fromTime).format('DD.MM.YY')} `} {`${fromHour} `} @@ -62,9 +63,7 @@ const ExposuresHistory = ( return ( - - - + @@ -108,10 +107,9 @@ const styles = StyleSheet.create({ }, listItemSubContainer: { width: SCREEN_WIDTH * 0.875, - height: 80, justifyContent: 'center', alignItems: 'center', - paddingHorizontal: 15 + paddingVertical: 5 }, text: { fontSize: 14, diff --git a/src/components/Main/FilterDriving/FilterDriving.tsx b/src/components/Main/FilterDriving/FilterDriving.tsx new file mode 100644 index 00000000..f050bd12 --- /dev/null +++ b/src/components/Main/FilterDriving/FilterDriving.tsx @@ -0,0 +1,82 @@ +import React from 'react'; +import { View, StyleSheet } from 'react-native'; +import { connect } from 'react-redux'; +import { ActionButton, Text, Icon, TouchableOpacity } from '../../common'; +import { onMotionPermissionSkipped, requestMotionPermissions } from '../../../services/LocationService'; +import { onError } from '../../../services/ErrorService'; +import { Strings } from '../../../locale/LocaleData'; +import { IS_SMALL_SCREEN, MAIN_COLOR, PADDING_BOTTOM, PADDING_TOP } from '../../../constants/Constants'; + +interface Props { + navigation: any, + strings: Strings, +} + +const FilterDriving = ({ navigation, strings: { filterDriving: { title, desc1, desc2, desc3, button, skip } } }: Props) => { + const requestPermissions = async () => { + try { + await requestMotionPermissions(true); + navigation.pop(); + } catch (error) { + onError({ error }); + } + }; + + const onSkip = async () => { + await onMotionPermissionSkipped(); + navigation.pop(); + }; + + return ( + + + { + !IS_SMALL_SCREEN && ( + + ) + } + + {title} + + + {desc1} + {` ${desc2} `} + {desc3} + + + + + + + + {skip} + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'space-between', + paddingTop: PADDING_TOP(90), + paddingBottom: PADDING_BOTTOM(30), + backgroundColor: '#fff' + }, + title: { + fontSize: 22, + marginVertical: 25 + } +}); + +const mapStateToProps = (state: any) => { + const { + locale: { strings } + } = state; + + return { strings }; +}; + +export default connect(mapStateToProps, null)(FilterDriving); diff --git a/src/components/Main/LocationHistory/LocationHistory.tsx b/src/components/Main/LocationHistory/LocationHistory.tsx index b79b3aeb..b67f3aab 100644 --- a/src/components/Main/LocationHistory/LocationHistory.tsx +++ b/src/components/Main/LocationHistory/LocationHistory.tsx @@ -2,13 +2,14 @@ import React from 'react'; import { View, StyleSheet } from 'react-native'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; +import { GoogleTimeLine } from '../../common'; import { toggleWebview } from '../../../actions/GeneralActions'; +import { Strings } from '../../../locale/LocaleData'; import { PADDING_BOTTOM, PADDING_TOP, USAGE_PRIVACY } from '../../../constants/Constants'; -import { GoogleTimeLine } from '../../common'; interface Props { navigation: any, - strings: any, + strings: Strings, toggleWebview(isShow: boolean, usageType: string): void } diff --git a/src/components/Main/NoData.tsx b/src/components/Main/NoData.tsx index dff89fa9..fbe4a07b 100644 --- a/src/components/Main/NoData.tsx +++ b/src/components/Main/NoData.tsx @@ -2,10 +2,11 @@ import React from 'react'; import { StyleSheet } from 'react-native'; import { connect } from 'react-redux'; import { FadeInView, Icon, Text } from '../common'; +import { Strings } from '../../locale/LocaleData'; import { IS_SMALL_SCREEN, SCREEN_WIDTH } from '../../constants/Constants'; interface Props { - strings: any + strings: Strings } const NoData = ({ strings: { scanHome: { noData, noDataDesc } } }: Props) => { diff --git a/src/components/Main/NoExposures.tsx b/src/components/Main/NoExposures.tsx index 095cdc7e..8352f086 100644 --- a/src/components/Main/NoExposures.tsx +++ b/src/components/Main/NoExposures.tsx @@ -4,12 +4,13 @@ import moment from 'moment'; import LottieView from 'lottie-react-native'; import LocationHistoryInfo from './LocationHistoryInfo'; import { FadeInView, Text, TouchableOpacity } from '../common'; +import { Strings } from '../../locale/LocaleData'; import { IS_SMALL_SCREEN, MAIN_COLOR, PADDING_BOTTOM, SCREEN_WIDTH, USAGE_PRIVACY } from '../../constants/Constants'; interface Props { isRTL: boolean, firstPoint?: number, - strings: any, + strings: Strings, hideLocationHistory: boolean, goToLocationHistory(): void, toggleWebview(isShow: boolean, usageType: string): void @@ -64,19 +65,19 @@ const NoExposures = ( {!hideLocationHistory && } - + - {descriptions()} + {descriptions()} - {recommendation} + {recommendation} toggleWebview(true, USAGE_PRIVACY)}> {additionalInfo} @@ -95,13 +96,10 @@ const styles = StyleSheet.create({ paddingBottom: PADDING_BOTTOM(50) }, lottie: { - width: SCREEN_WIDTH * (IS_SMALL_SCREEN ? 0.3 : 0.5), - height: SCREEN_WIDTH * (IS_SMALL_SCREEN ? 0.3 : 0.5), + width: SCREEN_WIDTH * (IS_SMALL_SCREEN ? 0.25 : 0.45), + height: SCREEN_WIDTH * (IS_SMALL_SCREEN ? 0.25 : 0.45), marginBottom: IS_SMALL_SCREEN ? 10 : 25 }, - text: { - width: 220 - }, bottomBorder: { alignSelf: 'stretch', height: 2, diff --git a/src/components/Main/ScanHome.tsx b/src/components/Main/ScanHome.tsx index 37778370..d54a2efa 100644 --- a/src/components/Main/ScanHome.tsx +++ b/src/components/Main/ScanHome.tsx @@ -15,14 +15,17 @@ import NoExposures from './NoExposures'; import ExposureInstructions from './ExposureInstructions'; import { checkForceUpdate, checkIfHideLocationHistory, toggleWebview } from '../../actions/GeneralActions'; import { dismissExposure, removeValidExposure, setValidExposure } from '../../actions/ExposuresActions'; -import { checkPermissions } from '../../services/LocationService'; +import { checkLocationPermissions, goToFilterDrivingIfNeeded } from '../../services/LocationService'; +import { ExternalUrls, Languages, Strings } from '../../locale/LocaleData'; import { Exposure } from '../../types'; interface Props { navigation: any, isRTL: boolean, - strings: any, - locale: 'he'|'en'|'ar'|'am'|'ru'|'fr', + strings: Strings, + locale: string, + languages: Languages, + externalUrls: ExternalUrls, exposures: Exposure[], validExposure: Exposure, firstPoint?: number, @@ -41,6 +44,8 @@ const ScanHome = ( isRTL, strings, locale, + languages, + externalUrls, exposures, validExposure, setValidExposure, @@ -60,6 +65,7 @@ const ScanHome = ( setTimeout(() => { SplashScreen.hide(); checkForceUpdate(); + goToFilterDrivingIfNeeded(navigation); }, 3000); checkIfHideLocationHistory(); @@ -94,7 +100,7 @@ const ScanHome = ( ); const checkConnectionStatusOnLoad = async () => { - const locationPermission = await checkPermissions(); + const locationPermission = await checkLocationPermissions(); const networkStatus = await NetInfo.fetch(); const GPSStatus = await RNSettings.getSetting(RNSettings.LOCATION_SETTING); @@ -105,10 +111,11 @@ const ScanHome = ( if (state === 'active' && appStateStatus.current !== 'active') { checkIfHideLocationHistory(); - const locationPermission = await checkPermissions(); + const locationPermission = await checkLocationPermissions(); const GPSStatus = await RNSettings.getSetting(RNSettings.LOCATION_SETTING); + const networkStatus = await NetInfo.fetch(); - setIsConnected({ hasLocation: locationPermission === RESULTS.GRANTED, hasNetwork, hasGPS: GPSStatus === RNSettings.ENABLED }); + setIsConnected({ hasLocation: locationPermission === RESULTS.GRANTED, hasNetwork: networkStatus.isConnected, hasGPS: GPSStatus === RNSettings.ENABLED }); } appStateStatus.current = state; @@ -121,9 +128,17 @@ const ScanHome = ( const renderRelevantState = () => { if (validExposure) { return ( - + ); - } if (!hasLocation || !hasNetwork) { + } if (!hasLocation || !hasNetwork || !hasGPS) { return ( ); @@ -177,12 +192,12 @@ const styles = StyleSheet.create({ const mapStateToProps = (state: any) => { const { - locale: { isRTL, strings, locale }, + locale: { isRTL, strings, locale, languages, externalUrls }, general: { hideLocationHistory }, exposures: { exposures, validExposure, firstPoint } } = state; - return { isRTL, strings, locale, exposures, validExposure, firstPoint, hideLocationHistory }; + return { isRTL, strings, locale, languages, externalUrls, exposures, validExposure, firstPoint, hideLocationHistory }; }; diff --git a/src/components/Main/ScanHomeHeader.tsx b/src/components/Main/ScanHomeHeader.tsx index a184a01c..2f28b5dd 100644 --- a/src/components/Main/ScanHomeHeader.tsx +++ b/src/components/Main/ScanHomeHeader.tsx @@ -2,11 +2,12 @@ import React from 'react'; import { View, StyleSheet, ImageBackground } from 'react-native'; import LottieView from 'lottie-react-native'; import { TouchableOpacity, Text, Icon, ChangeLanguageButton } from '../common'; +import { Strings } from '../../locale/LocaleData'; import { BASIC_SHADOW_STYLES, IS_SMALL_SCREEN, PADDING_TOP, SCREEN_HEIGHT, SCREEN_WIDTH } from '../../constants/Constants'; interface Props { isRTL: boolean, - strings: any, + strings: Strings, isConnected: boolean, showChangeLanguage: boolean, goToExposureHistory(): void @@ -83,6 +84,7 @@ const styles = StyleSheet.create({ backgroundColor: '#fff' }, headerItemContainer: { + height: 45, alignItems: 'center' }, indicatorWrapper: { diff --git a/src/components/Onboarding/AllSet.tsx b/src/components/Onboarding/AllSet.tsx index c5b2b410..533fe77d 100644 --- a/src/components/Onboarding/AllSet.tsx +++ b/src/components/Onboarding/AllSet.tsx @@ -9,15 +9,17 @@ import { scheduleTask } from '../../services/BackgroundService'; import { startForegroundTimer } from '../../services/Tracker'; import { onError } from '../../services/ErrorService'; import { startSampling } from '../../services/SampleService'; +import { NotificationData, Strings } from '../../locale/LocaleData'; import { SCREEN_WIDTH, IS_FIRST_TIME } from '../../constants/Constants'; interface Props { navigation: any, - locale: 'he'|'en'|'ar'|'am'|'ru'|'fr', - strings: any + locale: string, + notificationData: NotificationData, + strings: Strings } -const AllSet = ({ navigation, strings: { allSet: { allGood } }, locale }: Props) => { +const AllSet = ({ navigation, strings: { allSet: { allGood } }, locale, notificationData }: Props) => { useEffect(() => { setTimeout(() => { onboardingDoneActions(); @@ -43,7 +45,7 @@ const AllSet = ({ navigation, strings: { allSet: { allGood } }, locale }: Props) await AsyncStorage.setItem(IS_FIRST_TIME, 'true'); startForegroundTimer(); - await startSampling(locale); + await startSampling(locale, notificationData); await scheduleTask(); navigation.replace('ScanHome'); @@ -85,10 +87,10 @@ const styles = StyleSheet.create({ const mapStateToProps = (state: any) => { const { - locale: { strings, locale } + locale: { strings, locale, notificationData } } = state; - return { strings, locale }; + return { strings, locale, notificationData }; }; export default connect(mapStateToProps, null)(AllSet); diff --git a/src/components/Onboarding/FilterDrivingOnBoarding.tsx b/src/components/Onboarding/FilterDrivingOnBoarding.tsx new file mode 100644 index 00000000..98656e78 --- /dev/null +++ b/src/components/Onboarding/FilterDrivingOnBoarding.tsx @@ -0,0 +1,81 @@ +import React from 'react'; +import { StyleSheet, View } from 'react-native'; +import { connect } from 'react-redux'; +import { Strings } from '../../locale/LocaleData'; +import { ActionButton, GeneralContainer, Icon, OnboardingHeader, Text, TouchableOpacity } from '../common'; +import { onMotionPermissionSkipped, requestMotionPermissions } from '../../services/LocationService'; +import { onError } from '../../services/ErrorService'; +import { IS_SMALL_SCREEN, MAIN_COLOR } from '../../constants/Constants'; + +interface Props { + navigation: any, + strings: Strings, +} + +const FilterDrivingOnBoarding = ({ navigation, strings: { filterDriving: { title, desc1, desc2, desc3, button, skip } } }: Props) => { + const requestPermissions = async () => { + try { + await requestMotionPermissions(false); + navigation.navigate('LocationHistoryOnBoarding'); + } catch (error) { + onError({ error }); + } + }; + + const onSkip = async () => { + await onMotionPermissionSkipped(); + navigation.navigate('LocationHistoryOnBoarding'); + }; + + return ( + + + + + { + !IS_SMALL_SCREEN && ( + + ) + } + + {title} + + + {desc1} + {` ${desc2} `} + {desc3} + + + + + + + + {skip} + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: 'space-between', + alignItems: 'center' + }, + title: { + fontSize: 22, + marginVertical: 25 + } +}); + +const mapStateToProps = (state: any) => { + const { + locale: { strings } + } = state; + + return { strings }; +}; + +export default connect(mapStateToProps, null)(FilterDrivingOnBoarding); diff --git a/src/components/Onboarding/Location.tsx b/src/components/Onboarding/Location.tsx index 67bd9a0f..35ab79d7 100644 --- a/src/components/Onboarding/Location.tsx +++ b/src/components/Onboarding/Location.tsx @@ -2,17 +2,19 @@ import React, { useRef, useState } from 'react'; import { View, StyleSheet } from 'react-native'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; +import DeviceInfo from 'react-native-device-info'; import LottieView from 'lottie-react-native'; import * as Animatable from 'react-native-animatable'; import { ActionButton, GeneralContainer, OnboardingHeader, Text, TermsOfUse } from '../common'; import { toggleWebview } from '../../actions/GeneralActions'; -import { requestPermissions } from '../../services/LocationService'; +import { requestLocationPermissions } from '../../services/LocationService'; +import { Strings } from '../../locale/LocaleData'; import { IS_IOS, IS_SMALL_SCREEN, MAIN_COLOR, USAGE_ON_BOARDING } from '../../constants/Constants'; interface Props { navigation: any, isRTL: boolean, - strings: any, + strings: Strings, toggleWebview(isShow: boolean, usageType: string): void } @@ -23,14 +25,20 @@ const Location = ({ navigation, isRTL, strings, toggleWebview }: Props) => { const [isTOUAccepted, setIsTOUAccepted] = useState(false); - const requestLocationPermissions = async () => { + const requestPermissions = async () => { try { if (!isTOUAccepted) { return animRef.current.shake(1000); } - await requestPermissions(); - navigation.navigate(IS_IOS ? 'LocationIOS' : 'LocationHistoryOnBoarding'); + await requestLocationPermissions(); + + if (IS_IOS) { + navigation.navigate('LocationIOS'); + } else { + const androidVersion = parseFloat(DeviceInfo.getSystemVersion().split(',')[0]); + navigation.navigate(androidVersion >= 10 ? 'FilterDrivingOnBoarding' : 'LocationHistoryOnBoarding'); + } } catch (e) { // handled in service } @@ -41,15 +49,17 @@ const Location = ({ navigation, isRTL, strings, toggleWebview }: Props) => { - {!IS_SMALL_SCREEN && ( - - )} + { + !IS_SMALL_SCREEN && ( + + ) + } {title} {subTitle1} @@ -67,7 +77,7 @@ const Location = ({ navigation, isRTL, strings, toggleWebview }: Props) => { /> - + ); diff --git a/src/components/Onboarding/LocationHistoryOnBoarding.tsx b/src/components/Onboarding/LocationHistoryOnBoarding.tsx index cfecd9d1..6371f73f 100644 --- a/src/components/Onboarding/LocationHistoryOnBoarding.tsx +++ b/src/components/Onboarding/LocationHistoryOnBoarding.tsx @@ -4,11 +4,12 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { GeneralContainer, GoogleTimeLine, OnboardingHeader } from '../common'; import { toggleWebview } from '../../actions/GeneralActions'; +import { Strings } from '../../locale/LocaleData'; import { IS_IOS, USAGE_PRIVACY } from '../../constants/Constants'; interface Props { navigation: any, - strings: any, + strings: Strings, toggleWebview(isShow: boolean, usageType: string): void } diff --git a/src/components/Onboarding/LocationIOS.tsx b/src/components/Onboarding/LocationIOS.tsx index 108364f8..a1e09118 100644 --- a/src/components/Onboarding/LocationIOS.tsx +++ b/src/components/Onboarding/LocationIOS.tsx @@ -4,11 +4,12 @@ import { connect } from 'react-redux'; import { check, PERMISSIONS, RESULTS } from 'react-native-permissions'; import { ActionButton, GeneralContainer, TouchableOpacity, Text, OnboardingHeader, Icon } from '../common'; import { onError } from '../../services/ErrorService'; +import { Strings } from '../../locale/LocaleData'; import { IS_SMALL_SCREEN, MAIN_COLOR, SCREEN_WIDTH } from '../../constants/Constants'; interface Props { navigation: any, - strings: any, + strings: Strings, isRTL: boolean } @@ -65,7 +66,7 @@ const LocationIOS = ({ navigation, strings: { locationIOS: { title, subTitle1, s navigation.navigate('LocationHistoryOnBoarding')} + onPress={() => navigation.navigate('FilterDrivingOnBoarding')} containerStyle={{ marginVertical: IS_SMALL_SCREEN ? 0 : 20 }} /> diff --git a/src/components/Onboarding/Notifications.tsx b/src/components/Onboarding/Notifications.tsx index 9abdaf0c..220fd41b 100644 --- a/src/components/Onboarding/Notifications.tsx +++ b/src/components/Onboarding/Notifications.tsx @@ -4,11 +4,12 @@ import { connect } from 'react-redux'; import LottieView from 'lottie-react-native'; import { ActionButton, GeneralContainer, OnboardingHeader, Text } from '../common'; import { initPushNotifications } from '../../services/PushService'; +import { Strings } from '../../locale/LocaleData'; import { IS_SMALL_SCREEN } from '../../constants/Constants'; interface Props { navigation: any, - strings: any + strings: Strings } const Notifications = ({ navigation, strings: { notifications: { title, subTitle1, subTitle2, approveNotifications } } }: Props) => { diff --git a/src/components/Onboarding/Welcome.tsx b/src/components/Onboarding/Welcome.tsx index ab98e9c7..ba2adbec 100644 --- a/src/components/Onboarding/Welcome.tsx +++ b/src/components/Onboarding/Welcome.tsx @@ -5,10 +5,11 @@ import SplashScreen from 'react-native-splash-screen'; import { bindActionCreators } from 'redux'; import { checkForceUpdate } from '../../actions/GeneralActions'; import { ActionButton, GeneralContainer, Text, Icon, OnboardingHeader } from '../common'; +import { Strings } from '../../locale/LocaleData'; interface Props { navigation: any, - strings: any, + strings: Strings, checkForceUpdate(): void } @@ -16,7 +17,7 @@ const Welcome = ({ navigation, strings: { general: { start }, welcome: { title, useEffect(() => { setTimeout(() => { SplashScreen.hide(); - checkForceUpdate(); + setTimeout(() => checkForceUpdate(), 1000); }, 3000); }, []); diff --git a/src/components/common/ChangeLanguage.tsx b/src/components/common/ChangeLanguage.tsx index 1938a95c..bceac508 100644 --- a/src/components/common/ChangeLanguage.tsx +++ b/src/components/common/ChangeLanguage.tsx @@ -1,21 +1,23 @@ import React, { ElementType } from 'react'; -import { View, StyleSheet, Modal } from 'react-native'; +import { View, StyleSheet, Modal, ScrollView } from 'react-native'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; -import { Icon, Text, TouchableOpacity } from '.'; +import { CloseButton, Text, TouchableOpacity } from '.'; import { changeLocale, toggleChangeLanguage } from '../../actions/LocaleActions'; -import { IS_SMALL_SCREEN, MAIN_COLOR, PADDING_TOP, SCREEN_HEIGHT, SCREEN_WIDTH } from '../../constants/Constants'; +import { Languages, Strings } from '../../locale/LocaleData'; +import { IS_SMALL_SCREEN, MAIN_COLOR, SCREEN_HEIGHT, SCREEN_WIDTH } from '../../constants/Constants'; interface Props { isVisible: boolean, - strings: any, - locale: 'he'|'en'|'ar'|'am'|'ru'|'fr', - changeLocale(locale: 'he'|'en'|'ar'|'am'|'ru'|'fr'): void, + strings: Strings, + locale: string, + languages: Languages, + changeLocale(locale: string): void, toggleChangeLanguage(isShow: boolean): void } -let ChangeLanguage: ElementType = ({ isVisible, locale, strings: { languages: { title, long } }, changeLocale, toggleChangeLanguage }: Props) => { - const onButtonPress = (selectedLocale: 'he'|'en'|'ar'|'am'|'ru'|'fr') => { +let ChangeLanguage: ElementType = ({ isVisible, locale, strings: { languages: { title } }, languages: { long }, changeLocale, toggleChangeLanguage }: Props) => { + const onButtonPress = (selectedLocale: string) => { selectedLocale !== locale && changeLocale(selectedLocale); toggleChangeLanguage(false); }; @@ -28,21 +30,26 @@ let ChangeLanguage: ElementType = ({ isVisible, locale, strings: { languages: { onRequestClose={() => toggleChangeLanguage(false)} > - toggleChangeLanguage(false)}> - - + toggleChangeLanguage(false)} /> - {title} + + {title} + - { - Object.keys(long).map((key: string, index: number) => ( - onButtonPress(key)}> - - {long[key]} - - - )) - } + + { + Object.keys(long).map((key: string, index: number) => ( + onButtonPress(key)}> + + {long[key]} + + + )) + } + ); @@ -52,15 +59,14 @@ const styles = StyleSheet.create({ container: { width: SCREEN_WIDTH, height: SCREEN_HEIGHT, - justifyContent: 'center', - alignItems: 'center', backgroundColor: '#fff' }, - close: { - position: 'absolute', - top: PADDING_TOP(20), - left: 20, - zIndex: 1000 + titleWrapper: { + width: SCREEN_WIDTH, + height: SCREEN_HEIGHT * 0.2, + paddingTop: SCREEN_HEIGHT * 0.1, + justifyContent: 'center', + alignItems: 'center' }, title: { fontSize: 22, @@ -83,10 +89,10 @@ const styles = StyleSheet.create({ const mapStateToProps = (state: any) => { const { - locale: { strings, locale } + locale: { strings, locale, languages } } = state; - return { strings, locale }; + return { strings, locale, languages }; }; const mapDispatchToProps = (dispatch: any) => { diff --git a/src/components/common/ChangeLanguageButton.tsx b/src/components/common/ChangeLanguageButton.tsx index a02d3c1e..1f74daf9 100644 --- a/src/components/common/ChangeLanguageButton.tsx +++ b/src/components/common/ChangeLanguageButton.tsx @@ -4,16 +4,24 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { Icon, TouchableOpacity, Text } from '.'; import { toggleChangeLanguage } from '../../actions/LocaleActions'; +import { Languages, Strings } from '../../locale/LocaleData'; interface Props { - strings: any, - locale: 'he'|'en'|'ar'|'am'|'ru'|'fr', + locale: string, + languages: Languages, + strings: Strings, toggleChangeLanguage(isShow: boolean): void } -let ChangeLanguageButton: ElementType = ({ locale, strings: { languages: { short } }, toggleChangeLanguage }: Props) => { +let ChangeLanguageButton: ElementType = ({ locale, strings: { languages: { title } }, languages: { short, long }, toggleChangeLanguage }: Props) => { return ( - toggleChangeLanguage(true)}> + toggleChangeLanguage(true)} + importantForAccessibility="no-hide-descendants" + accessibilityElementsHidden + accessibilityHint={long[locale]} + accessibilityLabel={title} + > {short[locale]} @@ -37,10 +45,10 @@ const styles = StyleSheet.create({ const mapStateToProps = (state: any) => { const { - locale: { strings, locale } + locale: { locale, languages, strings } } = state; - return { strings, locale }; + return { locale, languages, strings }; }; const mapDispatchToProps = (dispatch: any) => { diff --git a/src/components/common/CloseButton.tsx b/src/components/common/CloseButton.tsx new file mode 100644 index 00000000..f9cdf799 --- /dev/null +++ b/src/components/common/CloseButton.tsx @@ -0,0 +1,45 @@ +import React, { ElementType } from 'react'; +import { StyleSheet } from 'react-native'; +import { connect } from 'react-redux'; +import { TouchableOpacity, Icon } from '.'; +import { Strings } from '../../locale/LocaleData'; +import { PADDING_TOP } from '../../constants/Constants'; + +interface Props { + strings: Strings, + isSmall?: boolean, + onPress(): void +} + +let CloseButton: ElementType = ({ strings: { general: { close } }, isSmall, onPress }: Props) => { + return ( + + + + ); +}; + +const styles = StyleSheet.create({ + close: { + position: 'absolute', + zIndex: 1000 + } +}); + +const mapStateToProps = (state: any) => { + const { + locale: { strings } + } = state; + + return { strings }; +}; + +CloseButton = connect(mapStateToProps, null)(CloseButton); + +export { CloseButton }; diff --git a/src/components/common/ForceTerms.tsx b/src/components/common/ForceTerms.tsx index 117334f2..1df89370 100644 --- a/src/components/common/ForceTerms.tsx +++ b/src/components/common/ForceTerms.tsx @@ -2,11 +2,12 @@ import React, { useRef, useState } from 'react'; import { View, StyleSheet, Modal } from 'react-native'; import * as Animatable from 'react-native-animatable'; import { ActionButton, Icon, TermsOfUse, Text } from '.'; +import { Strings } from '../../locale/LocaleData'; import { IS_SMALL_SCREEN, PADDING_TOP, SCREEN_HEIGHT, SCREEN_WIDTH } from '../../constants/Constants'; interface Props { isRTL: boolean, - strings: any, + strings: Strings, isVisible: boolean, onSeeTerms(): void, onApprovedTerms(): void diff --git a/src/components/common/ForceUpdate.tsx b/src/components/common/ForceUpdate.tsx index feadd8a4..68f83551 100644 --- a/src/components/common/ForceUpdate.tsx +++ b/src/components/common/ForceUpdate.tsx @@ -1,29 +1,41 @@ import React from 'react'; import { View, StyleSheet, Modal, Linking } from 'react-native'; -import { ActionButton, Icon, Text } from '.'; +import { ActionButton, CloseButton, Icon, Text } from '.'; +import store from '../../store'; +import { Strings } from '../../locale/LocaleData'; +import { HIDE_FORCE_UPDATE } from '../../constants/ActionTypes'; import { IS_IOS, IS_SMALL_SCREEN, PADDING_TOP, SCREEN_HEIGHT, SCREEN_WIDTH } from '../../constants/Constants'; interface Props { isVisible: boolean, - strings: any + shouldForce: boolean, + strings: Strings } -const ForceUpdate = ({ isVisible, strings: { forceUpdate: { title, desc } } }: Props) => { +const ForceUpdate = ({ isVisible, shouldForce, strings: { forceUpdate: { title, desc, dontForceDesc } } }: Props) => { const appStoreUrl = 'https://itunes.apple.com/us/app/id1503224314?ls=1&mt=8'; const playStoreUrl = 'https://play.google.com/store/apps/details?id=com.hamagen'; + const closeModal = () => store().dispatch({ type: HIDE_FORCE_UPDATE }); + return ( {}} + onRequestClose={shouldForce ? () => {} : closeModal} > + { + !shouldForce && ( + + ) + } + {title} - {desc} + {shouldForce ? desc : dontForceDesc} Linking.openURL(IS_IOS ? appStoreUrl : playStoreUrl)} /> diff --git a/src/components/common/GeneralWebview.tsx b/src/components/common/GeneralWebview.tsx index d9f41568..82b94437 100644 --- a/src/components/common/GeneralWebview.tsx +++ b/src/components/common/GeneralWebview.tsx @@ -2,20 +2,18 @@ import React from 'react'; import { View, StyleSheet, Modal } from 'react-native'; import WebView from 'react-native-webview'; import { WebviewHeader } from '.'; -import config from '../../config/config'; +import { ExternalUrls } from '../../locale/LocaleData'; import { USAGE_PRIVACY } from '../../constants/Constants'; interface Props { isVisible: boolean, - locale: 'he'|'en'|'ar'|'am'|'ru'|'fr', + locale: string, + externalUrls: ExternalUrls, usageType: string, closeWebview(): void } -const GeneralWebview = ({ isVisible, locale, closeWebview, usageType }: Props) => { - const usageSourceOnBoarding = config().usageTerms; - const usageSourcePrivacy = config().privacyTerms; - +const GeneralWebview = ({ isVisible, locale, externalUrls, closeWebview, usageType }: Props) => { return ( (false); const webViewRef = useRef(null); const [{ openWebview, isLoggedIn, state }, setState] = useState({ openWebview: false, isLoggedIn: false, state: 'before' }); @@ -129,6 +133,13 @@ const GoogleTimeLine = ({ strings, toggleWebview, onCompletion }: GoogleTimeLine } }; + const retryKMLDownload = (didRetry: any, kmlUrls: string[]) => new Promise((resolve) => { + setTimeout(async () => { + didRetry.current = true; + resolve(await Promise.all(kmlUrls.map(url => fetch(url).then(r => r.text())))); + }, IS_IOS ? 5000 : 10); + }); + const onMessage = async ({ nativeEvent: { data } }: WebViewMessageEvent) => { if (!data) { return; @@ -142,24 +153,29 @@ const GoogleTimeLine = ({ strings, toggleWebview, onCompletion }: GoogleTimeLine const kmlUrls = getLastNrDaysKmlUrls(); - const texts = await Promise.all(kmlUrls.map(url => fetch(url).then(r => r.text()))); + let texts = await Promise.all(kmlUrls.map(url => fetch(url).then(r => r.text()))); if (texts[0].indexOf('DOCTYPE') > -1 && texts[0].indexOf('Error') > -1) { - return onFetchError('Fetch KML error'); + if (!didRetry.current) { + texts = await retryKMLDownload(didRetry, kmlUrls); + + if (texts[0].indexOf('DOCTYPE') > -1 && texts[0].indexOf('Error') > -1) { + return onFetchError('Fetch KML error'); + } + } else { + return onFetchError('Fetch KML error'); + } } const pointsData = texts.flatMap((kml: string) => kmlToGeoJson(kml)); if (pointsData.length === 0) { - // once 14 days flow completed for the first time - await AsyncStorage.setItem(SHOULD_HIDE_LOCATION_HISTORY, 'true'); - store().dispatch(checkIfHideLocationHistory()); - return setState(prevState => ({ ...prevState, openWebview: false, isLoggedIn: false, state: 'successNotFound' })); + return onFlowEnd('successNotFound'); } await insertToSampleDB(pointsData); - setState(prevState => ({ ...prevState, openWebview: false, isLoggedIn: false, state: 'successFound' })); + return onFlowEnd('successFound'); } catch (error) { await onFetchError(error); } @@ -167,10 +183,26 @@ const GoogleTimeLine = ({ strings, toggleWebview, onCompletion }: GoogleTimeLine }; const onFetchError = async (error: any) => { - setState(prevState => ({ ...prevState, openWebview: false, isLoggedIn: false, state: 'failed' })); + await onFlowEnd('failed'); onError({ error }); }; + const onFlowEnd = async (state: 'before'|'successFound'|'successNotFound'|'failed') => { + if (state !== 'failed') { + // once 14 days flow completed for the first time + await AsyncStorage.setItem(SHOULD_HIDE_LOCATION_HISTORY, 'true'); + store().dispatch(checkIfHideLocationHistory()); + } + + webViewRef.current?.injectJavaScript('setTimeout(() => { document.location = "https://accounts.google.com/logout"; true; }, 10)'); + + setTimeout(async () => { + didRetry.current = false; + setState(prevState => ({ ...prevState, openWebview: false, isLoggedIn: false, state })); + await CookieManager.clearAll(true); + }, 300); + }; + return ( diff --git a/src/components/common/TermsOfUse.tsx b/src/components/common/TermsOfUse.tsx index 704ab5d3..9ae24d93 100644 --- a/src/components/common/TermsOfUse.tsx +++ b/src/components/common/TermsOfUse.tsx @@ -1,30 +1,33 @@ import React from 'react'; import { StyleSheet, View } from 'react-native'; import { Icon, TouchableOpacity, Text } from '.'; +import { Strings } from '../../locale/LocaleData'; import { SCREEN_WIDTH, TEXT_COLOR, USAGE_ON_BOARDING } from '../../constants/Constants'; interface Props { isRTL: boolean, - strings: any, + strings: Strings, value: boolean, onValueSelected(value: boolean): void, toggleWebview(isShow: boolean, usageType: string): void } -const TermsOfUse = ({ isRTL, strings: { location: { consent1, consent2 } }, value, onValueSelected, toggleWebview }: Props) => { +const TermsOfUse = ({ isRTL, strings: { general: { readTOU, approveTOU }, location: { consent1, consent2 } }, value, onValueSelected, toggleWebview }: Props) => { return ( - onValueSelected(!value)}> - + + onValueSelected(!value)} accessibilityLabel={approveTOU} accessibilityRole="checkbox" checked={value}> {value && } + + toggleWebview(true, USAGE_ON_BOARDING)} accessibilityHint={readTOU}> {consent1} - toggleWebview(true, USAGE_ON_BOARDING)}>{consent2} + {consent2} - - + + ); }; diff --git a/src/components/common/Text.tsx b/src/components/common/Text.tsx index d10d08fe..f2d60a96 100644 --- a/src/components/common/Text.tsx +++ b/src/components/common/Text.tsx @@ -5,7 +5,7 @@ import { IS_SMALL_SCREEN, TEXT_COLOR } from '../../constants/Constants'; interface Props extends TextProps { style?: TextStyle, - locale: 'he'|'en'|'ar'|'am'|'ru'|'fr', + locale: string, reference?: MutableRefObject, children?: ReactNode, bold?: boolean, diff --git a/src/components/common/WebviewHeader.tsx b/src/components/common/WebviewHeader.tsx index 83dc6b4d..b6ad88a9 100644 --- a/src/components/common/WebviewHeader.tsx +++ b/src/components/common/WebviewHeader.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { View, StyleSheet, ImageBackground } from 'react-native'; +import { CloseButton } from '.'; import { IS_SMALL_SCREEN, PADDING_TOP, SCREEN_HEIGHT, SCREEN_WIDTH } from '../../constants/Constants'; -import { Icon, TouchableOpacity } from '.'; interface Props { hideClose?: boolean, @@ -18,9 +18,7 @@ const WebviewHeader = ({ hideClose, closeModal }: Props) => { > { !hideClose && ( - - - + ) } @@ -30,12 +28,6 @@ const WebviewHeader = ({ hideClose, closeModal }: Props) => { }; const styles = StyleSheet.create({ - close: { - position: 'absolute', - top: PADDING_TOP(IS_SMALL_SCREEN ? 10 : 20), - left: IS_SMALL_SCREEN ? 10 : 20, - zIndex: 1000 - }, headerContainer: { width: SCREEN_WIDTH, height: SCREEN_HEIGHT * 0.17, diff --git a/src/components/common/index.ts b/src/components/common/index.ts index 20ae3622..dae56152 100644 --- a/src/components/common/index.ts +++ b/src/components/common/index.ts @@ -14,3 +14,4 @@ export * from './TermsOfUse'; export * from './ForceTerms'; export * from './GoogleTimeLine'; export * from './WebviewHeader'; +export * from './CloseButton'; diff --git a/src/config/config.ts b/src/config/config.ts index 98fb099b..b7974f03 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -5,9 +5,9 @@ import DefaultConfig from './default_config.json'; import { Config } from '../types'; // @ts-ignore -const env: 'com.hamagen.dev'|'com.hamagen.qa'|'com.hamagen' = DeviceInfo.getBundleId(); +const env: 'com.hamagen.qa'|'com.hamagen' = DeviceInfo.getBundleId(); -let config: Config = DefaultConfig[env]; +let config: Config = DefaultConfig[env] || DefaultConfig['com.hamagen.qa']; export const initConfig = async () => new Promise(async (resolve) => { try { diff --git a/src/config/default_config.json b/src/config/default_config.json index f13a76e6..e98c9476 100644 --- a/src/config/default_config.json +++ b/src/config/default_config.json @@ -1,7 +1,7 @@ { "com.hamagen.dev": { - "dataUrl": "https://matrixdemos.blob.core.windows.net/mabar/PointsUtc.json", - "dataUrl_utc": "https://matrixdemos.blob.core.windows.net/mabar/PointsUtc.json", + "dataUrl": "https://matrixdemos.blob.core.windows.net/mabar/Points.json", + "dataUrl_utc": "https://matrixdemos.blob.core.windows.net/mabar/Points.json", "stringsUrl": "https://matrixdemos.blob.core.windows.net/mabar/texts.json", "versionsUrl": "https://matrixdemos.blob.core.windows.net/mabar/versions.json", "sampleDistance": 50, @@ -12,6 +12,13 @@ "bufferUnits": "meter", "sickGeometryLongIndex": 0, "sickGeometryLatIndex": 1, + "locationServiceIgnoreList": [ + "running", + "on_bicycle", + "in_vehicle" + ], + "locationServiceIgnoreConfidenceThreshold": 80, + "locationServiceIgnoreSampleVelocityThreshold": 2.8, "locationHistoryIgnoreList": [ "On a train", "In a taxi or rideshare", @@ -54,8 +61,8 @@ "body": "انقر هنا لمعرفة ما إذا كنت قد تعرضت" }, "fr": { - "title": "Une ou plusieurs expositions peuvent avoir été détectées", - "body": "Cliquez ici pour savoir si vous avez été exposé " + "title": "Une ou plusieurs expositions peuvent avoir été détectées", + "body": "Cliquez ici pour savoir si vous avez été exposé " }, "duration": 10000 }, @@ -93,8 +100,8 @@ } }, "com.hamagen.qa": { - "dataUrl": "https://matrixdemos.blob.core.windows.net/mabar/PointsUtc.json", - "dataUrl_utc": "https://matrixdemos.blob.core.windows.net/mabar/PointsUtc.json", + "dataUrl": "https://matrixdemos.blob.core.windows.net/mabar/Points.json", + "dataUrl_utc": "https://matrixdemos.blob.core.windows.net/mabar/Points.json", "stringsUrl": "https://matrixdemos.blob.core.windows.net/mabar/texts.json", "versionsUrl": "https://matrixdemos.blob.core.windows.net/mabar/versions.json", "sampleDistance": 50, @@ -105,6 +112,13 @@ "bufferUnits": "meter", "sickGeometryLongIndex": 0, "sickGeometryLatIndex": 1, + "locationServiceIgnoreList": [ + "running", + "on_bicycle", + "in_vehicle" + ], + "locationServiceIgnoreConfidenceThreshold": 80, + "locationServiceIgnoreSampleVelocityThreshold": 2.8, "locationHistoryIgnoreList": [ "On a train", "In a taxi or rideshare", @@ -147,8 +161,8 @@ "body": "انقر هنا لمعرفة ما إذا كنت قد تعرضت" }, "fr": { - "title": "Une ou plusieurs expositions peuvent avoir été détectées", - "body": "Cliquez ici pour savoir si vous avez été exposé " + "title": "Une ou plusieurs expositions peuvent avoir été détectées", + "body": "Cliquez ici pour savoir si vous avez été exposé " }, "duration": 10000 }, @@ -186,7 +200,7 @@ } }, "com.hamagen": { - "dataUrl": "https://gisweb.azureedge.net/Points.json", + "dataUrl": "https://gisweb.azureedge.net/PointsUtc.json", "dataUrl_utc": "https://gisweb.azureedge.net/PointsUtc.json", "stringsUrl": "https://gisweb.azureedge.net/texts.json", "versionsUrl": "https://gisweb.azureedge.net/versions.json", @@ -198,6 +212,13 @@ "bufferUnits": "meter", "sickGeometryLongIndex": 0, "sickGeometryLatIndex": 1, + "locationServiceIgnoreList": [ + "running", + "on_bicycle", + "in_vehicle" + ], + "locationServiceIgnoreConfidenceThreshold": 80, + "locationServiceIgnoreSampleVelocityThreshold": 2.8, "locationHistoryIgnoreList": [ "On a train", "In a taxi or rideshare", @@ -240,8 +261,8 @@ "body": "انقر هنا لمعرفة ما إذا كنت قد تعرضت" }, "fr": { - "title": "Une ou plusieurs expositions peuvent avoir été détectées", - "body": "Cliquez ici pour savoir si vous avez été exposé " + "title": "Une ou plusieurs expositions peuvent avoir été détectées", + "body": "Cliquez ici pour savoir si vous avez été exposé " }, "duration": 10000 }, diff --git a/src/constants/ActionTypes.ts b/src/constants/ActionTypes.ts index 114a95ef..b32320c1 100755 --- a/src/constants/ActionTypes.ts +++ b/src/constants/ActionTypes.ts @@ -9,6 +9,7 @@ export const REMOVE_VALID_EXPOSURE = 'REMOVE_VALID_EXPOSURE'; export const UPDATE_PAST_EXPOSURES = 'UPDATE_PAST_EXPOSURES'; export const DISMISS_EXPOSURE = 'DISMISS_EXPOSURE'; export const SHOW_FORCE_UPDATE = 'SHOW_FORCE_UPDATE'; +export const HIDE_FORCE_UPDATE = 'HIDE_FORCE_UPDATE'; export const INIT_LOCALE = 'INIT_LOCALE'; export const RESET_EXPOSURES = 'RESET_EXPOSURES'; export const UPDATE_FIRST_POINT = 'UPDATE_FIRST_POINT'; diff --git a/src/constants/Constants.ts b/src/constants/Constants.ts index 99fe7f18..694540b1 100755 --- a/src/constants/Constants.ts +++ b/src/constants/Constants.ts @@ -47,6 +47,8 @@ export const LAST_POINT_START_TIME = 'LAST_POINT_START_TIME'; export const DID_UPDATE_LOCATIONS_TIME_TO_UTC = 'DID_UPDATE_LOCATIONS_TIME_TO_UTC'; export const SHOULD_HIDE_LOCATION_HISTORY = 'SHOULD_HIDE_LOCATION_HISTORY'; export const IS_LAST_POINT_FROM_TIMELINE = 'IS_LAST_POINT_FROM_TIMELINE'; +export const HIGH_VELOCITY_POINTS = 'HIGH_VELOCITY_POINTS'; +export const MOTION_PERMISSION_CALL_TO_ACTION = 'MOTION_PERMISSION_CALL_TO_ACTION'; // GeneralWebview export const USAGE_ON_BOARDING = 'USAGE_ON_BOARDING'; diff --git a/src/database/Database.js b/src/database/Database.js index 24c9e062..5bcd4fed 100755 --- a/src/database/Database.js +++ b/src/database/Database.js @@ -185,6 +185,29 @@ export class UserLocationsDatabase { }); }); } + + getLastPointEntered() { + return new Promise((resolve) => { + this.initDB().then((db) => { + db.transaction((tx) => { + tx.executeSql('SELECT * from Samples WHERE rowid=(SELECT MAX(rowid) from Samples)').then(([tx, results]) => { + if (results.rows.length > 0) { + const row = results.rows.item(0); + resolve(row); + } else { + resolve(null); + } + }); + }).then((result) => { + this.closeDatabase(db); + }).catch((err) => { + console.log(err); + }); + }).catch((err) => { + console.log(err); + }); + }); + } } export class IntersectionSickDatabase { diff --git a/src/locale/LocaleData.ts b/src/locale/LocaleData.ts index 9c10bede..1a28bb81 100644 --- a/src/locale/LocaleData.ts +++ b/src/locale/LocaleData.ts @@ -1,6 +1,183 @@ import texts from './texts.json'; -import LocaleData from '../model/LocaleData'; -const localeData: LocaleData = { he: texts.he, en: texts.en, ru: texts.ru, ar: texts.ar, am: texts.am, fr: texts.fr }; +const localeData: LocaleData = texts; + +// TODO fix TS to allow both static and dynamic keys without error +export interface LocaleData { + languages: Languages, + externalUrls: ExternalUrls, + notificationData: NotificationData, + [languageKey: string]: Strings +} + +export interface Languages { + short: { + [languageKey: string]: string + }, + long: { + [languageKey: string]: string + } +} + +export interface ExternalUrls { + furtherInstructions: { + [languageKey: string]: string + }, + reportForm: { + [languageKey: string]: string + }, + usageTerms: { + [languageKey: string]: string + }, + privacyTerms: { + [languageKey: string]: string + } +} + +// TODO fix TS to allow both static and dynamic keys without error +export interface NotificationData { + androidNotification: { + [languageKey: string]: string + }, + sickMessage: { + [languageKey: string]: { + title: string, + body: string + }, + duration: number + } +} + +export interface Strings { + general: { + start: string, + additionalInfo: string, + close: string, + readTOU: string, + approveTOU: string + }, + languages: { + title: string, + short: { + [languageKey: string]: string + }, + long: { + [languageKey: string]: string + } + }, + forceUpdate: { + title: string, + desc: string, + dontForceDesc: string + }, + forceTerms: { + title: string, + desc: string, + approve: string + }, + welcome: { + title: string, + subTitle1: string, + subTitle2: string + }, + location: { + title: string, + subTitle1: string, + subTitle2IOS: string, + subTitle2Android: string, + dataAnonymous: string, + consent1: string, + consent2: string, + approveLocation: string + }, + locationIOS: { + title: string, + subTitle1: string, + subTitle2: string, + goToSettings: string, + set: string + }, + notifications: { + title: string, + subTitle1: string, + subTitle2: string, + approveNotifications: string + }, + allSet: { + allGood: string + }, + scanHome: { + hasData: string, + noData: string, + exposureHistory: string, + noDataDesc: string, + noExposure: string, + noExposure1: string, + noExposure2: string, + noExposure3: string, + noExposure4: string, + accordingToData: string, + from: string, + at: string, + until: string, + notFound: string, + recommendation: string, + found: string, + exposureEvents: string, + reportedAt: string, + inDate: string, + fromHour: string, + toHour: string, + wereYouThere: string, + no: string, + canContinue: string, + yes: string, + needDirections: string, + suspectedExposure: string, + events: string, + possibleExposure: string, + atPlace: string + }, + exposuresHistory: { + title: string, + noExposures: string + }, + exposureInstructions: { + title: string, + weUnderstand: string, + wrong: string, + keepSafe: string, + goIntoIsolation: string, + reportIsolation: string, + allInstructions: string, + reportSite: string + }, + locationHistory: { + beforeCheckTitle: string, + beforeCheckDesc: string, + beforeCheckDesc2: string, + beforeCheckButton: string, + skip: string, + successFoundTitle: string, + successFoundDesc: string, + successFoundButton: string, + successNotFoundTitle: string, + successNotFoundDesc: string, + successNotFoundButton: string, + failedTitle: string, + failedDesc: string, + failedButton: string, + info: string, + moreInfo: string + }, + filterDriving: { + title: string, + desc1: string, + desc2: string, + desc3: string, + button: string, + skip: string + } +} export default localeData; diff --git a/src/locale/texts.json b/src/locale/texts.json index aabbc1f4..18d94afa 100644 --- a/src/locale/texts.json +++ b/src/locale/texts.json @@ -1,8 +1,100 @@ { + "languages": { + "short": { + "he": "עב", + "en": "En", + "ru": "Ru", + "ar": "عر", + "am": "አማ", + "fr": "Fr" + }, + "long": { + "he": "עברית", + "en": "English", + "ru": "Русский", + "ar": "عربيه", + "am": "አማርኛ", + "fr": "Français" + } + }, + "externalUrls": { + "furtherInstructions": { + "he": "https://govextra.gov.il/ministry-of-health/corona/corona-virus/", + "en": "https://govextra.gov.il/ministry-of-health/corona/corona-virus-en/", + "am": "https://govextra.gov.il/ministry-of-health/corona/corona-virus/", + "ru": "https://govextra.gov.il/ministry-of-health/corona/corona-virus-ru", + "ar": "https://govextra.gov.il/ministry-of-health/corona/corona-virus-ar/", + "fr": "https://govextra.gov.il/ministry-of-health/corona/corona-virus/" + }, + "reportForm": { + "he": "https://govforms.gov.il/mw/forms/QuarantineForExposees%40health.gov.il", + "en": "https://govforms.gov.il/mw/forms/QuarantineForExposees%40health.gov.il", + "am": "https://govforms.gov.il/mw/forms/QuarantineForExposees%40health.gov.il", + "ru": "https://govforms.gov.il/mw/forms/QuarantineForExposees%40health.gov.il", + "ar": "https://govforms.gov.il/mw/forms/QuarantineForExposees%40health.gov.il", + "fr": "https://govforms.gov.il/mw/forms/QuarantineForExposees%40health.gov.il" + }, + "usageTerms": { + "he": "https://go.gov.il/magen-terms-he", + "en": "https://go.gov.il/magen-terms-en", + "ar": "https://go.gov.il/magen-terms-ar", + "ru": "https://go.gov.il/magen-terms-ru", + "am": "https://go.gov.il/magen-terms-am", + "fr": "https://go.gov.il/magen-terms-fr" + }, + "privacyTerms": { + "he": "https://go.gov.il/HAMAGEN", + "en": "https://go.gov.il/HAMAGEN-EN", + "ar": "https://go.gov.il/HAMAGEN-AR", + "ru": "https://go.gov.il/HAMAGEN-RU", + "am": "https://go.gov.il/HAMAGEN-AM", + "fr": "https://go.gov.il/HAMAGEN-FR" + } + }, + "notificationData": { + "androidNotification": { + "he": "אפליקציית המגן רצה ברקע ומגנה עליך", + "en": "Hamagen runs in the background and protects you", + "am": "የተከላካይ መተግበሪያ በጀርባ ውስጥ ይሠራል እና እርስዎን ይጠብቃል", + "ru": "Хамаген бежит в фоновом режиме и защищает тебя", + "ar": "يعمل تطبيق המגן في الخلفية ويحميك", + "fr": "Hamagen court en arrière-plan et vous protège" + }, + "sickMessage": { + "he": { + "title": "יתכן כי זוהתה חשיפה אחת או יותר", + "body": "יש ללחוץ כאן כדי לברר אם נחשפת" + }, + "en": { + "title": "One or more exposures may have been detected", + "body": "Click here to find out if you have been exposed" + }, + "am": { + "title": "አንድ ወይም ከዚያ በላይ ተጋላጭነቶች ተከስተው ሊሆን ይችላል", + "body": "የተጋለጡ መሆንዎን ለማወቅ እዚህ ጠቅ ያድርጉ" + }, + "ru": { + "title": "Возможно, обнаружено одно или несколько совпадений", + "body": "Нажмите здесь, чтобы узнать, если вы были выставлены" + }, + "ar": { + "title": "ربما تم الكشف عن تداخل واحد أو أكثر", + "body": "انقر هنا لمعرفة ما إذا كنت قد تعرضت" + }, + "fr": { + "title": "Une ou plusieurs expositions peuvent avoir été détectées", + "body": "Cliquez ici pour savoir si vous avez été exposé " + }, + "duration": 10000 + } + }, "he": { "general": { "start": "התחלה", - "additionalInfo": "מדיניות פרטיות ומידע נוסף" + "additionalInfo": "מדיניות פרטיות ומידע נוסף", + "close": "סגור", + "readTOU": "קרא את תנאי השימוש", + "approveTOU": "אשר את תנאי השימוש" }, "languages": { "title": "בחר שפה", @@ -25,7 +117,8 @@ }, "forceUpdate": { "title": "עדכון גרסה", - "desc": "קיימת גרסה חדשה לאפלקציה. על מנת שנוכל להמשיך לשמור על הבריאות של כולנו, אנו ממליצים לעדכן את הגרסה" + "desc": "קיימת גרסה חדשה לאפלקציה. על מנת שנוכל להמשיך לשמור על הבריאות של כולנו, אנו ממליצים לעדכן את הגרסה", + "dontForceDesc": "קיימת גרסה חדשה לאפלקציה. על מנת שנוכל להמשיך לשמור על הבריאות של כולנו, אנו ממליצים לעדכן את הגרסה" }, "forceTerms": { "title": "עדכון תנאי השימוש", @@ -85,19 +178,23 @@ "inDate": "בתאריך", "fromHour": "בסביבות השעה", "toHour": "עד שעה:", - "whereYouThere": "האם היית בסביבת המקום באותה שעה?", + "wereYouThere": "האם היית בסביבת המקום באותו זמן?", "no": "לא", "canContinue": "אפשר להמשיך", "yes": "כן", - "needDirections": "אשמח להנחיות" + "needDirections": "אשמח להנחיות", + "suspectedExposure": "חשד לחשיפה:", + "events": "אירועים", + "possibleExposure": "ייתכן שהייתה חפיפה מול חולה קורונה:", + "atPlace": "ב" }, "exposuresHistory": { "title": "היסטוריית חפיפות", "noExposures": "אין נקודות\nחפיפה" }, "exposureInstructions": { - "title": "הנחיות לאחר חשיפה", - "weUnderstand": "האם היית ב", + "title": "חשד לחשיפה: מה לעשות?", + "weUnderstand": "אנחנו מבינים שהיית בנקודת חפיפה ב", "wrong": "טעות, לא הייתי שם", "keepSafe": "על מנת לשמור על בריאות משפחתך והסובבים אותך אלו ההנחיות של משרד הבריאות", "goIntoIsolation": "להיכנס באופן מידי לבידוד למשך 14 יום מרגע החפיפה", @@ -120,14 +217,25 @@ "failedTitle": "לא הצלחנו לקבל נתונים מגוגל", "failedDesc": "מומלץ לבדוק את החיבור שלך לאינטרנט, את שם המשתמש והסיסמא לגוגל ולנסות שוב", "failedButton": "ניסיון נוסף", - "info": "על מנת לאתר מוקדי חפיפה מ-14 הימים האחרונים, נצטרך לקבל את נתוני המיקום שלך מהחשבון בגוגל", - "moreInfo": " להמשך" + "info": "על מנת לאתר מוקדי חפיפה מ-14 הימים האחרונים, נצטרך לקבל את נתוני המיקום שלך מהחשבון בגוגל ", + "moreInfo": "להמשך" + }, + "filterDriving": { + "title": "סינון נסיעות", + "desc1": "אנחנו יודעים שנסיעות עלולות להוסיף נתונים לא רלוונטיים להשוואה, ולכן חשוב לעדכן אותנו על ידי", + "desc2": "הפעלת גישה לפעולות תנועה,", + "desc3": "כדי שלא נכלול את מיקומי הנסיעה שלך בהשוואה למיקומים של חולי קורונה", + "button": "גישה לפעולות תנועה", + "skip": "אל תורידו נסיעות מהמיקומים שלי" } }, "ar": { "general": { "start": "إبدأ", - "additionalInfo": "معلومات إضافية " + "additionalInfo": "معلومات إضافية ", + "close": "أغلق", + "readTOU": "إقرأ شروط الإستعمال", + "approveTOU": "قبول شروط الإستخدام" }, "languages": { "title": "إختر لغة", @@ -150,7 +258,8 @@ }, "forceUpdate": { "title": "تحديث الإصدار", - "desc": "هنالك إصدار جديد من التطبيق، للحفاظ على صحتنا جميعا ، نوصي بتحديث الإصدار" + "desc": "هنالك إصدار جديد من التطبيق، للحفاظ على صحتنا جميعا ، نوصي بتحديث الإصدار", + "dontForceDesc": "هنالك إصدار جديد من التطبيق، للحفاظ على صحتنا جميعا ، نوصي بتحديث الإصدار" }, "forceTerms": { "title": "تحديث شروط الإستخدام", @@ -210,19 +319,23 @@ "inDate": "في تاريخ", "fromHour": "من الساعة :", "toHour": " حتى الساعة:", - "whereYouThere": "هل تواجدتم في هذا المكان في هذه الساعة?", + "wereYouThere": "هل تواجدتم في هذا المكان في هذه الساعة?", "no": "لا", "canContinue": "هل نستطيع التقدم؟", "yes": "نعم", - "needDirections": "أود إستلام توجيهات" + "needDirections": "أود إستلام توجيهات", + "suspectedExposure": "إشتباه بالتعرض لمريض كورونا:", + "events": " أحداث ", + "possibleExposure": "هنالك إحتمالية التعرض لمريض كورونا:", + "atPlace": " في" }, "exposuresHistory": { "title": "تاريخ التطابق", - "noExposures": "لا يوجد نقاط تطابق " + "noExposures": "لا يوجد نقاط تطابق" }, "exposureInstructions": { - "title": "توصيات ", - "weUnderstand": "نحن نتفهم تواجدكم في نقطة تطابق", + "title": "لديك إشتباه بالتعرض لمريض كورونا؟ ماذا يجب أن تفعل؟", + "weUnderstand": "نحن نعلم أنك كنت في نقطة تطابق في", "wrong": "هذا بلاغ خاطئ، لم أتواجد هناك", "keepSafe": "من أجل الحفاظ على صحتكم و صحة عائلاتكم و المحيطين بكم يجب عليكم تطبيق توصيات وزارة الصحة و الدخول في حجر صحي لمدة 14 يوم من تاريخ اللقاء مع المصاب", "goIntoIsolation": "يرجى الدخول فورا الى الحجر الصحي لمدة ١٤ يوم من وقت التعرض", @@ -247,12 +360,23 @@ "failedButton": "محاولة أخرى", "info": "من أجل تحديد أماكن التطابق خلال ال ١٤ يوم الأخيرة، يجب الحصول على بيانات الموقع الخاصة بكم من خلال حساب google ", "moreInfo": "للإستمرار" + }, + "filterDriving": { + "title": "تنقيح السفريات", + "desc1": "نحن نعلم أن السفريات قد تضيف بيانات ليست ذات صلة ولذلك يجب تحديثنا بالمعلومات من خلال", + "desc2": "تفعيل الوصول الى أماكن التحرك,", + "desc3": "حتى لا نشمل أماكن التنقل الخاصة بك بالمقارنة مع أماكن تواجد مرضى كورونا", + "button": "الوصول الى حركات التنقل", + "skip": "لا تقوموا بإزالة أماكن السفريات من أماكن الموقع الخاصة بي" } }, "en": { "general": { "start": "START", - "additionalInfo": "Additional Privacy information" + "additionalInfo": "Additional Privacy information", + "close": "Close", + "readTOU": "Read the terms of use", + "approveTOU": "Confirm the terms of use" }, "languages": { "title": "Select language", @@ -275,7 +399,8 @@ }, "forceUpdate": { "title": "Version Update", - "desc": "There is a new version of the app. In order for us to continue to maintain the health of us all, we recommend updating the app" + "desc": "There is a new version of the app. In order for us to continue to maintain the health of us all, we recommend updating the app", + "dontForceDesc": "There is a new version of the app. In order for us to continue to maintain the health of us all, we recommend updating the app" }, "forceTerms": { "title": "Terms of Use Updated", @@ -333,21 +458,25 @@ "exposureEvents": "Exposure events", "reportedAt": "Reported At:", "inDate": "In", - "fromHour": "From:", + "fromHour": "from", "toHour": "Until:", - "whereYouThere": "Where you around in this time?", + "wereYouThere": "Where you around in this time?", "no": "No", "canContinue": "You can proceed", "yes": "Yes", - "needDirections": "Receive instructions" + "needDirections": "Receive instructions", + "suspectedExposure": "Suspected Exposure:", + "events": "Events", + "possibleExposure": "There may have been an exposure to a Corona patient:", + "atPlace": "At " }, "exposuresHistory": { "title": "Overlap history", "noExposures": "No overlap points found" }, "exposureInstructions": { - "title": "Post-exposure guidelines", - "weUnderstand": "It appears your location has intersected with a person known to be infected with the coronavirus", + "title": "Suspicion of exposure: What to do?", + "weUnderstand": "It appears your location has intersected with a person known to be infected with the coronavirus at ", "wrong": "Must be a mistake, I wasn’t there", "keepSafe": "You must take the following steps", "goIntoIsolation": "Stay at home and avoid contact with other people for 14 days from the moment of contact", @@ -372,12 +501,23 @@ "failedButton": "Try Again", "info": "In order to accurately determine your locations for the last 14 days, your location data must be enabled from your Google Account. ", "moreInfo": "More info" + }, + "filterDriving": { + "title": "Driving filtering", + "desc1": "We know that driving can add irrelevant data for comparison, so it's important to update us by", + "desc2": "by enabling access to motion sensors,", + "desc3": "so we don't include your driving locations compared to Corona patients locations", + "button": "Access to motion sensors", + "skip": "Don't remove driving from my locations" } }, "ru": { "general": { "start": "Начать", - "additionalInfo": "политика конфиденциальности" + "additionalInfo": "политика конфиденциальности", + "close": "закрыть:", + "readTOU": "см. Руководство:", + "approveTOU": "согласен с условиями" }, "languages": { "title": "Выберите язык", @@ -400,7 +540,8 @@ }, "forceUpdate": { "title": "Обновить версию", - "desc": "Появилась новая версия приложения. Чтобы продолжать поддерживать здоровье всех нас, мы рекомендуем обновить версию" + "desc": "Появилась новая версия приложения. Чтобы продолжать поддерживать здоровье всех нас, мы рекомендуем обновить версию", + "dontForceDesc": "Появилась новая версия приложения. Чтобы продолжать поддерживать здоровье всех нас, мы рекомендуем обновить версию" }, "forceTerms": { "title": "Обновить Условия использования", @@ -458,22 +599,26 @@ "exposureEvents": "Потенциальное соприкосновение", "reportedAt": "Документально записан", "inDate": "Число", - "fromHour": "С часов,:", + "fromHour": "С часов", "toHour": "до часов:", - "whereYouThere": "Были ли вы в этом месте в то же время?", + "wereYouThere": "Были ли вы в этом месте в то же время?", "no": "Нет", "canContinue": "Можно продолжить", "yes": "Да", - "needDirections": "Инструкции" + "needDirections": "Инструкции", + "suspectedExposure": "Подозрение на контакт:", + "events": "случая", + "possibleExposure": "Возможно контакт с больным был :", + "atPlace": "в " }, "exposuresHistory": { "title": "История контактов", "noExposures": "Нет точек соприкосновения" }, "exposureInstructions": { - "title": "Рекомендации после выявления контакта", - "weUnderstand": "Находились ли вы в пункте", - "wrong":"Ошибка, меня там не было.", + "title": "Подозрение на контакт? ", + "weUnderstand": "Похоже, ваш контакт был в точке", + "wrong": "Ошибка, меня там не было.", "keepSafe": "Для сохранения здоровья Вашей семьи и окружающих, следуйте рекомендациям Минздрава: ", "goIntoIsolation": "Оставайтесь дома на карантине в течение ближайших 14 дней", "reportIsolation": "Сообщите о вашем карантине на сайте Минздрава", @@ -497,12 +642,23 @@ "failedButton": "Еще одна попытка", "info": "Для точного определения вашего местоположения за последние 14 дней данные о вашем местоположении должны быть включены из вашей учетной записи Google. ", "moreInfo": "Узнать больше" + }, + "filterDriving": { + "title": "Фильтр поездок", + "desc1": "Мы знаем, что поездки могут добавить маршруты для сравнения, поэтому нам важно получить ", + "desc2": " доступ к трафику,", + "desc3": "так ваши поездки не войдут в места соприкосновений с больными", + "button": "Доступ к трафику ", + "skip": "Оставить поездки для сравнения " } }, "am": { "general": { "start": "ጀምር", - "additionalInfo": "ስለግላዊነት የበለጠ ለመረዳት" + "additionalInfo": "ስለግላዊነት የበለጠ ለመረዳት", + "close": "ዝጋው", + "readTOU": "የአጠቃቁም ቅድመ ሁኔታ አንብብ", + "approveTOU": "በአጠቃቀሙን ቅድመ ሁኔታ መስማማትህን ግለፅ" }, "languages": { "title": "ቋንቋ ይምረጡ", @@ -525,7 +681,8 @@ }, "forceUpdate": { "title": "ሥሪት አዘምን", - "desc": "አዲስ የመተግበሪያው ስሪት አለ። የሁላችንን ጤና ለመጠበቅ ፣ ስሪቱን ማዘመን እንመክራለን" + "desc": "አዲስ የመተግበሪያው ስሪት አለ። የሁላችንን ጤና ለመጠበቅ ፣ ስሪቱን ማዘመን እንመክራለን", + "dontForceDesc": "አዲስ የመተግበሪያው ስሪት አለ። የሁላችንን ጤና ለመጠበቅ ፣ ስሪቱን ማዘመን እንመክራለን" }, "forceTerms": { "title": "የአጠቃቀም ውሎችን አዘምን", @@ -585,19 +742,23 @@ "inDate": "በ", "fromHour": "ከሰአቱ", "toHour": "እስከ", - "whereYouThere": "ከዚህ ቦታ በተመሰሳሳይ ስአት ነበሩ ወይ?", + "wereYouThere": "ከዚህ ቦታ በተመሰሳሳይ ስአት ነበሩ ወይ?", "no": "አልነበርሁም", "canContinue": "ቀጥል", "yes": "አወ ነበርሁ", - "needDirections": "ምን ማድረግ አለብኝ" + "needDirections": "ምን ማድረግ አለብኝ", + "suspectedExposure": "የንክኪ ጥርጣሬ", + "events": "ክስተቶች /ገጠመኞች", + "possibleExposure": "ከኮሮና ህመምተኛ ጋር ንኪኪ ሊኖር ይችላል", + "atPlace": "በ " }, "exposuresHistory": { "title": "ከተያዘ ሰው ጋር ተቀራርበዋል", "noExposures": "ድርርብ አልተገኘም።" }, "exposureInstructions": { - "title": "ለቫይረሱ ከተጋለጡ በኋላ የተሰጡ መመሪያዎች", - "weUnderstand": "ነበርክ?", + "title": "ተጋልጠዋል የሚል ጥርጣሬ አለ ፣ ምን ማድረግ አለብዎት?", + "weUnderstand": "እንደተረዳነው በንክኪ በተጠረጠረ ቦታ ነበርክ", "wrong": "አልነበርኩም።,", "keepSafe": "የቤተሰብህን ጤንነት ለመጠበቅ የሚከተሉትን የጤና ጥበቃ መ/ቤት መመሪያዎች ጠብቅ ፡,", "goIntoIsolation": "-ለቫይረሱ ከተጋለጡበት ጊዜ ጀምሮ በአስቸኳይ ለ14 ቀናተ እራስዎን ያግልሉ፡፡,", @@ -622,12 +783,23 @@ "failedButton": "ሌላ ሙከራ", "info": "ያለፉት 14 ቀናት አካባቢዎን በትክክል ለማወቅ የአከባቢዎ ውሂብ ከ Google መለያ መንቃት አለበት። ", "moreInfo": "የበለጠ ለመረዳት" + }, + "filterDriving": { + "title": "የጉዞ ምርጫ", + "desc1": "ጉዞዎች አስፈላጊ ያልሆኑ መረጃዎችን ጥምሩ ላይ እንደሚጨምር እናውቀዋለን ስለዚህ ይህን በተመለከተ እንድታሳውቁን ያስፈልጋል", + "desc2": "የእንቅስቃሴውን መረጃ ለመቀበል በመተግበሪያው ላይ መፍቀድ/መስማማት", + "desc3": "የጉዞ እንቅስቃሴዎችን ከኮሮና ህመምተኛ የጉዞ እንቅስቃሴ ጋር ለማነፃፀር እንዳይጨምር", + "button": "የእንቅስቃሴ መረጃ ላይ መስማማት/መፍቀድ", + "skip": "የጉዞ መረጃ ታሪኬን አትውሰዱ/ አታውርዱ" } }, "fr": { "general": { "start": "COMMENCER", - "additionalInfo": "Informations supplémentaires sur la confidentialité" + "additionalInfo": "Informations supplémentaires sur la confidentialité", + "close": "Fermer", + "readTOU": "Lire les conditions d'utilisation", + "approveTOU": "Accepter les conditions d'utilisation" }, "languages": { "title": "Choisir la langue", @@ -650,7 +822,8 @@ }, "forceUpdate": { "title": "Mise à jour de la version", - "desc": "Il existe une nouvelle version de l'application. Afin de continuer à maintenir la santé de nous tous, nous vous recommandons de mettre à jour l'application" + "desc": "Il existe une nouvelle version de l'application. Afin de continuer à maintenir la santé de nous tous, nous vous recommandons de mettre à jour l'application", + "dontForceDesc": "Il existe une nouvelle version de l'application. Afin de continuer à maintenir la santé de nous tous, nous vous recommandons de mettre à jour l'application" }, "forceTerms": { "title": "Conditions d'utilisation mises à jour", @@ -708,21 +881,25 @@ "exposureEvents": "Événements d'exposition", "reportedAt": "Signalé à:\n", "inDate": "à", - "fromHour": "De:", + "fromHour": "de", "toHour": "Jusqu'à:", - "whereYouThere": "Etiez-vous a cet endroit à ce moment la?", + "wereYouThere": "Etiez-vous a cet endroit à ce moment la?", "no": "Non", "canContinue": "Vous pouvez continuer", "yes": "Oui", - "needDirections": "Recevez les instructions" + "needDirections": "Recevez les instructions", + "suspectedExposure": "Suspicion d'exposition:", + "events": "les événements", + "possibleExposure": "Il peut y avoir un chevauchement avec le patient Corona:", + "atPlace": "À " }, "exposuresHistory": { "title": "Historique des intersections\n", "noExposures": "Pas d'intersection trouvée" }, "exposureInstructions": { - "title": "Guide post-exposition", - "weUnderstand": "Etiez-vous à", + "title": "Suspicion d'exposition: que faire?", + "weUnderstand": "Etiez-vous à ", "wrong": "Non, je n'y etais pas", "keepSafe": "Pour votre protection, suivez les étapes suivantes", "goIntoIsolation": "Isolez-vous pendant 14 jours depuis le moment du contact", @@ -747,6 +924,14 @@ "failedButton": "Un autre essai", "info": "Afin de trouver des emplacements qui se chevauchent au cours des 14 derniers jours, nous devons obtenir vos données de localisation de Google ", "moreInfo": "Continuer" + }, + "filterDriving": { + "title": "Filtre de conduite", + "desc1": "Nous savons que la conduite peut ajouter des données non pertinentes à des fins de comparaison, il est donc important de nous mettre à jour en", + "desc2": "en permettant l'accès aux détecteurs de mouvement,", + "desc3": "donc nous n'incluons pas vos emplacements de conduite par rapport aux emplacements des patients Corona", + "button": "Accès aux détecteurs de mouvement", + "skip": "Ne supprimez pas la conduite de mes emplacements" } } } diff --git a/src/model/LocaleData.ts b/src/model/LocaleData.ts deleted file mode 100644 index e09df424..00000000 --- a/src/model/LocaleData.ts +++ /dev/null @@ -1,96 +0,0 @@ -export default interface LocaleData { - [languageKey: string]: LocaleDataLanguageOption -} - -interface LocaleDataLanguageOption { - general: { - start: string - additionalInfo: string - }, - languages: { - title: string, - short: { - [key: string]: string - }, - long: { - [key: string]: string - } - }, - forceUpdate: { - title: string - desc: string - }, - forceTerms: { - title: string - desc: string - approve: string - }, - welcome: { - title: string - subTitle1: string - subTitle2: string - }, - location: { - title: string - subTitle1: string - subTitle2IOS: string - subTitle2Android: string - dataAnonymous: string - consent1: string - consent2: string - approveLocation: string - }, - locationIOS: { - title: string - subTitle1: string - subTitle2: string, - goToSettings: string - set: string - }, - notifications: { - title: string - subTitle1: string - subTitle2: string - approveNotifications: string - }, - allSet: { - allGood: string - }, - scanHome: { - hasData: string - noData: string - exposureHistory: string - noDataDesc: string - noExposure: string - noExposure1: string - noExposure2: string - noExposure3: string - noExposure4: string - recommendation: string - found: string - exposureEvents: string - reportedAt: string - inDate: string - fromHour: string - toHour: string - wereYouThere: string - no: string - canContinue: string - yes: string - needDirections: string - }, - exposuresHistory: { - title: string - noExposures: string - }, - exposureInstructions: { - title: string - weUnderstand: string - wrong: string - keepSafe: string - goIntoIsolation: string - reportIsolation: string - allInstructions: string - reportSite: string - } -} \ No newline at end of file diff --git a/src/reducers/GeneralReducer.ts b/src/reducers/GeneralReducer.ts index ef534a19..d2baea06 100644 --- a/src/reducers/GeneralReducer.ts +++ b/src/reducers/GeneralReducer.ts @@ -3,6 +3,7 @@ import { TOGGLE_LOADER, TOGGLE_WEBVIEW, SHOW_FORCE_UPDATE, + HIDE_FORCE_UPDATE, SHOW_FORCE_TERMS, HIDE_FORCE_TERMS, HIDE_LOCATION_HISTORY @@ -13,6 +14,7 @@ interface GeneralReducer { showLoader: boolean, showWebview: boolean, showForceUpdate: boolean, + shouldForce: boolean, showForceTerms: boolean, termsVersion: number, hideLocationHistory: boolean @@ -22,6 +24,7 @@ const INITIAL_STATE = { showLoader: false, showWebview: false, showForceUpdate: false, + shouldForce: false, showForceTerms: false, usageType: USAGE_PRIVACY, termsVersion: 0, @@ -41,7 +44,12 @@ export default (state: GeneralReducer = INITIAL_STATE, action: ReducerAction) => } case SHOW_FORCE_UPDATE: { - return { ...state, showForceUpdate: true }; + const { shouldForce } = action.payload; + return { ...state, showForceUpdate: true, shouldForce }; + } + + case HIDE_FORCE_UPDATE: { + return { ...state, showForceUpdate: false }; } case SHOW_FORCE_TERMS: { diff --git a/src/reducers/LocaleReducer.ts b/src/reducers/LocaleReducer.ts index 94fd2be0..7659f660 100644 --- a/src/reducers/LocaleReducer.ts +++ b/src/reducers/LocaleReducer.ts @@ -1,35 +1,42 @@ -import LocaleData from '../locale/LocaleData'; +import localeData, { ExternalUrls, Languages, LocaleData, NotificationData, Strings } from '../locale/LocaleData'; import { ReducerAction } from '../types'; import { TOGGLE_CHANGE_LANGUAGE, LOCALE_CHANGED, INIT_LOCALE } from '../constants/ActionTypes'; interface LocaleReducer { showChangeLanguage: boolean, - strings: any, + languages: Languages|{}, + externalUrls: ExternalUrls|{}, + notificationData: NotificationData|{}, + strings: Strings|{}, isRTL: boolean, - locale: 'he'|'en'|'ar'|'am'|'ru'|'fr'|undefined, - localeData: { he: any, en: any, ar: any, am: any, ru: any, fr:any } + locale: string, + localeData: LocaleData } const INITIAL_STATE = { showChangeLanguage: false, + languages: {}, + externalUrls: {}, + notificationData: {}, strings: {}, isRTL: false, - locale: undefined, - localeData: LocaleData + locale: 'he', + localeData }; export default (state: LocaleReducer = INITIAL_STATE, action: ReducerAction) => { switch (action.type) { case INIT_LOCALE: { - const { isRTL, locale, localeData, strings } = action.payload; - return { ...state, isRTL, locale, localeData, strings }; + const { isRTL, locale, localeData, strings, languages, externalUrls, notificationData } = action.payload; + return { ...state, isRTL, locale, localeData, strings, languages, externalUrls, notificationData }; } + case TOGGLE_CHANGE_LANGUAGE: { return { ...state, showChangeLanguage: action.payload }; } case LOCALE_CHANGED: { - const { locale }: { locale: 'he'|'en'|'ar'|'am'|'ru'|'fr' } = action.payload; + const { locale } = action.payload; return { ...state, strings: { ...state.localeData[locale] }, locale, isRTL: ['he', 'ar'].includes(locale) }; } diff --git a/src/services/AnalyticsService.ts b/src/services/AnalyticsService.ts deleted file mode 100644 index 5d102f6e..00000000 --- a/src/services/AnalyticsService.ts +++ /dev/null @@ -1,5 +0,0 @@ -import firebase from 'react-native-firebase'; - -export const logEvent = (name: string, params: any) => { - firebase.analytics().logEvent(name, params); -}; diff --git a/src/services/BackgroundService.ts b/src/services/BackgroundService.ts index 9c96a1e3..6c36951b 100644 --- a/src/services/BackgroundService.ts +++ b/src/services/BackgroundService.ts @@ -14,10 +14,14 @@ export const scheduleTask = async () => { enableHeadless: true }, async () => { - console.log('Background fetch event fired'); - await initConfig(); - await checkSickPeople(); - BackgroundFetch.finish(BackgroundFetch.FETCH_RESULT_NEW_DATA); + try { + console.log('Background fetch event fired'); + await initConfig(); + await checkSickPeople(); + BackgroundFetch.finish(BackgroundFetch.FETCH_RESULT_NEW_DATA); + } catch (error) { + onError({ error }); + } }, (error: any) => onError({ error }) ); diff --git a/src/services/LocationHistoryService.ts b/src/services/LocationHistoryService.ts index cf7dea84..b41191d1 100644 --- a/src/services/LocationHistoryService.ts +++ b/src/services/LocationHistoryService.ts @@ -12,7 +12,7 @@ import { UPDATE_FIRST_POINT } from '../constants/ActionTypes'; import { FIRST_POINT_TS, IS_LAST_POINT_FROM_TIMELINE, SHOULD_HIDE_LOCATION_HISTORY } from '../constants/Constants'; // tslint:disable-next-line:no-var-requires -const togeojson = require('./ToGeoJson.js'); +const togeojson = require('@tmcw/togeojson'); export const getLoadingHTML = () => { return '' @@ -38,8 +38,7 @@ export const getLastNrDaysKmlUrls = () => { }); }; -const createObject = (point: any, timespan: any) => { - return { +const createObject = (point: any, timespan: any) => ({ startTime: moment(timespan.begin).valueOf(), endTime: moment(timespan.end).valueOf(), long: point[0], @@ -47,8 +46,7 @@ const createObject = (point: any, timespan: any) => { geoHash: geoHash.encode(point[1], point[0]), accuracy: 0, wifiHash: '' - }; -}; +}) export const kmlToGeoJson = (text: any) => { const kml = new DOMParser().parseFromString(text); diff --git a/src/services/LocationService.ts b/src/services/LocationService.ts index efd23d7a..8160874c 100755 --- a/src/services/LocationService.ts +++ b/src/services/LocationService.ts @@ -1,18 +1,21 @@ import { check, request, PERMISSIONS, RESULTS } from 'react-native-permissions'; import BackgroundGeolocation from 'react-native-background-geolocation'; +import moment from 'moment'; import AsyncStorage from '@react-native-community/async-storage'; import { UserLocationsDatabase } from '../database/Database'; import { onError } from './ErrorService'; import config from '../config/config'; -import { DID_UPDATE_LOCATIONS_TIME_TO_UTC, IS_IOS } from '../constants/Constants'; +import { NotificationData } from '../locale/LocaleData'; +import { DID_UPDATE_LOCATIONS_TIME_TO_UTC, IS_IOS, MOTION_PERMISSION_CALL_TO_ACTION } from '../constants/Constants'; -export const permission = IS_IOS ? PERMISSIONS.IOS.LOCATION_ALWAYS : PERMISSIONS.ANDROID.ACCESS_BACKGROUND_LOCATION; +export const locationPermission = IS_IOS ? PERMISSIONS.IOS.LOCATION_ALWAYS : PERMISSIONS.ANDROID.ACCESS_BACKGROUND_LOCATION; +export const motionPermission = IS_IOS ? PERMISSIONS.IOS.MOTION : PERMISSIONS.ANDROID.ACTIVITY_RECOGNITION; -export const checkPermissions = () => new Promise(async (resolve) => { +export const checkLocationPermissions = () => new Promise(async (resolve) => { try { let status; - status = await check(permission); + status = await check(locationPermission); if (!IS_IOS && status === RESULTS.UNAVAILABLE) { status = await check(PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION); @@ -25,12 +28,12 @@ export const checkPermissions = () => new Promise(async (resolve) => { } }); -export const requestPermissions = () => new Promise(async (resolve) => { +export const requestLocationPermissions = () => new Promise(async (resolve) => { try { - const status = await check(permission); + const status = await check(locationPermission); if (status !== RESULTS.GRANTED) { - const res = await request(permission); + const res = await request(locationPermission); if (!IS_IOS && res === RESULTS.UNAVAILABLE) { await request(PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION); @@ -44,34 +47,94 @@ export const requestPermissions = () => new Promise(async (resolve) => { } }); -export const startLocationTracking = async (locale: 'he'|'en'|'ar'|'am'|'ru'|'fr') => { +export const checkMotionPermissions = () => new Promise(async (resolve) => { try { - const status = await check(permission); + const res = await check(motionPermission); + resolve(res); + } catch (error) { + resolve(false); + onError({ error }); + } +}); + +export const requestMotionPermissions = (updateService: boolean) => new Promise(async (resolve) => { + try { + const res = await request(motionPermission); + + if (updateService && (res === RESULTS.GRANTED || res === RESULTS.UNAVAILABLE)) { + await BackgroundGeolocation.setConfig({ disableMotionActivityUpdates: false }); + } + + resolve(res === RESULTS.GRANTED); + } catch (error) { + resolve(RESULTS.BLOCKED); + onError({ error }); + } +}); + +export const goToFilterDrivingIfNeeded = async (navigation: any) => { + try { + const res = await checkMotionPermissions(); + + if (res === RESULTS.BLOCKED || res === RESULTS.GRANTED || res === RESULTS.UNAVAILABLE) { + return; + } + + const motionPermissionCTA = JSON.parse(await AsyncStorage.getItem(MOTION_PERMISSION_CALL_TO_ACTION) || 'false'); + + if (!motionPermissionCTA || ((motionPermissionCTA.tries < 5) && (moment(motionPermissionCTA.lastTry).diff(moment(), 'days') > 5))) { + navigation.navigate('FilterDriving'); + } + } catch (error) { + onError({ error }); + } +}; + +export const onMotionPermissionSkipped = async () => { + try { + const motionPermissionCTA = JSON.parse(await AsyncStorage.getItem(MOTION_PERMISSION_CALL_TO_ACTION) || 'false'); + + const updatedTries = motionPermissionCTA ? motionPermissionCTA.tries + 1 : 1; + + await AsyncStorage.setItem(MOTION_PERMISSION_CALL_TO_ACTION, JSON.stringify({ tries: updatedTries, lastTry: moment().valueOf() })); + } catch (error) { + onError({ error }); + } +}; + +export const startLocationTracking = async (locale: string, notificationData: NotificationData) => { + try { + const status = await check(locationPermission); if (status !== RESULTS.GRANTED) { - const res = await request(permission); + const res = await request(locationPermission); if (!IS_IOS && res === RESULTS.UNAVAILABLE) { await request(PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION); } } + const motionPermissions = await checkMotionPermissions(); + const disableMotionActivityUpdates = (motionPermissions !== RESULTS.GRANTED && motionPermissions !== RESULTS.UNAVAILABLE); + await BackgroundGeolocation.ready({ // Geolocation Config desiredAccuracy: BackgroundGeolocation.DESIRED_ACCURACY_HIGH, distanceFilter: config().sampleDistance, locationUpdateInterval: config().sampleInterval, fastestLocationUpdateInterval: config().sampleInterval, - disableMotionActivityUpdates: true, // Activity Recognition + disableMotionActivityUpdates, stopTimeout: 1, // Application config logLevel: BackgroundGeolocation.LOG_LEVEL_VERBOSE, stopOnTerminate: false, startOnBoot: true, + foregroundService: true, notification: { - text: config().androidNotification[locale] - } + text: notificationData.androidNotification[locale] + }, + enableHeadless: true }, (state) => { console.log('BackgroundGeolocation is configured and ready: ', state.enabled); diff --git a/src/services/NavigationService.ts b/src/services/NavigationService.ts deleted file mode 100755 index cde8e2a8..00000000 --- a/src/services/NavigationService.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { EventEmitter } from 'events'; - -export const emitter: EventEmitter = new EventEmitter(); diff --git a/src/services/SampleService.ts b/src/services/SampleService.ts index 5a8cb501..5f8df423 100755 --- a/src/services/SampleService.ts +++ b/src/services/SampleService.ts @@ -4,20 +4,31 @@ import AsyncLock from 'async-lock'; import moment from 'moment'; import { startLocationTracking } from './LocationService'; import { UserLocationsDatabase, WifiMacAddressDatabase } from '../database/Database'; -import { sha256 } from './sha256.js'; +import { sha256 } from './sha256'; import { getWifiList } from './WifiService'; import { onError } from './ErrorService'; -import { FIRST_POINT_TS, IS_LAST_POINT_FROM_TIMELINE, LAST_POINT_START_TIME } from '../constants/Constants'; import store from '../store'; +import config from '../config/config'; +import { NotificationData } from '../locale/LocaleData'; +import { DBLocation, Sample, VelocityRecord } from '../types'; import { UPDATE_FIRST_POINT } from '../constants/ActionTypes'; +import { + FIRST_POINT_TS, + HIGH_VELOCITY_POINTS, + IS_LAST_POINT_FROM_TIMELINE, + LAST_POINT_START_TIME +} from '../constants/Constants'; + +// tslint:disable-next-line:no-var-requires +const haversine = require('haversine'); const lock = new AsyncLock(); -export const startSampling = async (locale: 'he'|'en'|'ar'|'am'|'ru'|'fr') => { - await startLocationTracking(locale); +export const startSampling = async (locale: string, notificationData: NotificationData) => { + await startLocationTracking(locale, notificationData); }; -export const insertDB = async (sample: any) => new Promise(async (resolve) => { +export const insertDB = async (sample: Sample) => new Promise(async (resolve) => { // prevent race condition of entering multiple points at the same time await lock.acquire('insertDB', async (done) => { try { @@ -30,7 +41,7 @@ export const insertDB = async (sample: any) => new Promise(async (resolve) => { } // check last point timestamp and ignore if same point entered again. - const lastPointStartTime = hasLastPointTimestamp(); + const lastPointStartTime = await hasLastPointTimestamp(); if (lastPointStartTime && (lastPointStartTime === sample.timestamp)) { resolve(); @@ -63,7 +74,7 @@ export const insertDB = async (sample: any) => new Promise(async (resolve) => { wifiHash }; - const finalSample = { ...sampleObj, hash: sha256(JSON.stringify(sampleObj)) }; + const finalSample: DBLocation = { ...sampleObj, hash: sha256(JSON.stringify(sampleObj)) }; await db.addSample(finalSample); @@ -73,7 +84,7 @@ export const insertDB = async (sample: any) => new Promise(async (resolve) => { await wifiMacAddressDatabase.addWifiMacAddresses({ wifiHash, wifiList }); } - resolve(); + resolve(true); done(); return true; } catch (error) { @@ -92,7 +103,7 @@ const hasFirstPointTimestamp = () => new Promise(async (resolve) => { } }); -const hasLastPointTimestamp = () => new Promise(async (resolve) => { +const hasLastPointTimestamp = () => new Promise(async (resolve) => { try { resolve(JSON.parse(await AsyncStorage.getItem(LAST_POINT_START_TIME) || 'false')); } catch (error) { @@ -129,3 +140,100 @@ export const purgeSamplesDB = () => new Promise(async (resolve, reject) => { onError({ error }); } }); + +export const updateDBAccordingToSampleVelocity = async (location: Sample) => { + try { + const { is_moving, activity: { confidence }, coords: { speed } } = location; + + const db = new UserLocationsDatabase(); + + const highVelocityPoints = JSON.parse(await AsyncStorage.getItem(HIGH_VELOCITY_POINTS) || '[]'); + + const lastPointFromDB = await db.getLastPointEntered(); + const lastPointFromHVP = highVelocityPoints[highVelocityPoints.length - 1]; + + // ignore locations with timestamp earlier then the last location saved + if ((lastPointFromHVP && (lastPointFromHVP.timestamp > location.timestamp)) || (lastPointFromDB && (lastPointFromDB.startTime > location.timestamp))) { + return; + } + + const isLastPointEndTimeUpdated = JSON.parse(await AsyncStorage.getItem(IS_LAST_POINT_FROM_TIMELINE) || 'false'); + + if (is_moving && (speed > config().locationServiceIgnoreSampleVelocityThreshold) && (confidence > config().locationServiceIgnoreConfidenceThreshold)) { + if (!isLastPointEndTimeUpdated) { + await db.updateLastSampleEndTime(location.timestamp); + await AsyncStorage.setItem(IS_LAST_POINT_FROM_TIMELINE, 'true'); // raise this flag to prevent next point to override the previous point endTime + } + return; + } + + let pointsToCheck; + + if (highVelocityPoints.length === 0) { + // in case this is the first point entered + if (!lastPointFromDB) { + return await insertDB(location); + } + + pointsToCheck = [{ + coords: { + latitude: lastPointFromDB.lat, + longitude: lastPointFromDB.long, + accuracy: lastPointFromDB.accuracy + }, + timestamp: lastPointFromDB.endTime + }]; + } else { + pointsToCheck = highVelocityPoints; + } + + const isHighVelocity = evalVelocity([...pointsToCheck, location]); + + if (isHighVelocity) { + if (highVelocityPoints.length === 0 && !isLastPointEndTimeUpdated) { + await db.updateLastSampleEndTime(location.timestamp); + await AsyncStorage.setItem(IS_LAST_POINT_FROM_TIMELINE, 'true'); // raise this flag to prevent next point to override the previous point endTime + } + await AsyncStorage.setItem(HIGH_VELOCITY_POINTS, JSON.stringify([...highVelocityPoints, location])); + } else { + await AsyncStorage.removeItem(HIGH_VELOCITY_POINTS); + await insertDB(location); + } + } catch (error) { + onError({ error }); + } +}; + +const evalVelocity = (myData: Sample[]) => { + const velRec: VelocityRecord[] = mapPairs(myData, (a, b) => evalVelocity2Loc(a, b)); + + return velRec[velRec.length - 1].velocity > config().locationServiceIgnoreSampleVelocityThreshold; +}; + +function mapPairs(array: T[], fn: (first: T, second: T, index: number) => U): U[] { + if (array.length === 0) { + throw new Error('No pairs in empty array'); + } + + const ret: U[] = []; + + for (let i = 0; i < array.length - 1; i++) { + ret.push(fn(array[i], array[i + 1], i)); + } + + return ret; +} + +const evalVelocity2Loc = (prevData: Sample, currData: Sample) => { + const distMeter = haversine( + { latitude: currData.coords.latitude, longitude: currData.coords.longitude }, + { latitude: prevData.coords.latitude, longitude: prevData.coords.longitude }, + { unit: config().bufferUnits } + ); + + const timeDiffInSeconds = Math.floor((currData.timestamp - prevData.timestamp) / 1000); + + const velocity = (timeDiffInSeconds > 0) ? distMeter / timeDiffInSeconds : 0; + + return { distMeter, timeDiff: timeDiffInSeconds, velocity }; +}; diff --git a/src/services/ToGeoJson.js b/src/services/ToGeoJson.js deleted file mode 100644 index 325d5df0..00000000 --- a/src/services/ToGeoJson.js +++ /dev/null @@ -1,461 +0,0 @@ -import { XMLSerializer } from 'xmldom'; - -const toGeoJSON = (function () { - const removeSpace = /\s*/g; - const trimSpace = /^\s*|\s*$/g; - const splitSpace = /\s+/; - // generate a short, numeric hash of a string - function okhash(x) { - if (!x || !x.length) return 0; - for (var i = 0, h = 0; i < x.length; i++) { - h = ((h << 5) - h) + x.charCodeAt(i) | 0; - } return h; - } - // all Y children of X - function get(x, y) { return x.getElementsByTagName(y); } - function attr(x, y) { return x.getAttribute(y); } - function attrf(x, y) { return parseFloat(attr(x, y)); } - // one Y child of X, if any, otherwise null - function get1(x, y) { const n = get(x, y); return n.length ? n[0] : null; } - // https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize - function norm(el) { if (el.normalize) { el.normalize(); } return el; } - // cast array x into numbers - function numarray(x) { - for (var j = 0, o = []; j < x.length; j++) { o[j] = parseFloat(x[j]); } - return o; - } - // get the content of a text node, if any - function nodeVal(x) { - if (x) { norm(x); } - return (x && x.textContent) || ''; - } - // get the contents of multiple text nodes, if present - function getMulti(x, ys) { - const o = {}; let n; let - k; - for (k = 0; k < ys.length; k++) { - n = get1(x, ys[k]); - if (n) o[ys[k]] = nodeVal(n); - } - return o; - } - // add properties of Y to X, overwriting if present in both - function extend(x, y) { for (const k in y) x[k] = y[k]; } - // get one coordinate from a coordinate array, if any - function coord1(v) { return numarray(v.replace(removeSpace, '').split(',')); } - // get all coordinates from a coordinate array as [[],[]] - function coord(v) { - const coords = v.replace(trimSpace, '').split(splitSpace); - const o = []; - for (let i = 0; i < coords.length; i++) { - o.push(coord1(coords[i])); - } - return o; - } - function coordPair(x) { - const ll = [attrf(x, 'lon'), attrf(x, 'lat')]; - const ele = get1(x, 'ele'); - // handle namespaced attribute in browser - const heartRate = get1(x, 'gpxtpx:hr') || get1(x, 'hr'); - const time = get1(x, 'time'); - let e; - if (ele) { - e = parseFloat(nodeVal(ele)); - if (!isNaN(e)) { - ll.push(e); - } - } - return { - coordinates: ll, - time: time ? nodeVal(time) : null, - heartRate: heartRate ? parseFloat(nodeVal(heartRate)) : null - }; - } - - // create a new feature collection parent object - function fc() { - return { - type: 'FeatureCollection', - features: [] - }; - } - - let serializer; - if (typeof XMLSerializer !== 'undefined') { - /* istanbul ignore next */ - serializer = new XMLSerializer(); - } else { - const isNodeEnv = (typeof process === 'object' && !process.browser); - const isTitaniumEnv = (typeof Titanium === 'object'); - if (typeof exports === 'object' && (isNodeEnv || isTitaniumEnv)) { - serializer = new (require('xmldom').XMLSerializer)(); - } else { - throw new Error('Unable to initialize serializer'); - } - } - function xml2str(str) { - // IE9 will create a new XMLSerializer but it'll crash immediately. - // This line is ignored because we don't run coverage tests in IE9 - /* istanbul ignore next */ - if (str.xml !== undefined) return str.xml; - return serializer.serializeToString(str); - } - - const t = { - kml(doc) { - const gj = fc(); - // styleindex keeps track of hashed styles in order to match features - const styleIndex = {}; const styleByHash = {}; - // stylemapindex keeps track of style maps to expose in properties - const styleMapIndex = {}; - // atomic geospatial types supported by KML - MultiGeometry is - // handled separately - const geotypes = ['Polygon', 'LineString', 'Point', 'Track', 'gx:Track']; - // all root placemarks in the file - const placemarks = get(doc, 'Placemark'); - const styles = get(doc, 'Style'); - const styleMaps = get(doc, 'StyleMap'); - - for (let k = 0; k < styles.length; k++) { - const hash = okhash(xml2str(styles[k])).toString(16); - styleIndex[`#${attr(styles[k], 'id')}`] = hash; - styleByHash[hash] = styles[k]; - } - for (let l = 0; l < styleMaps.length; l++) { - styleIndex[`#${attr(styleMaps[l], 'id')}`] = okhash(xml2str(styleMaps[l])).toString(16); - const pairs = get(styleMaps[l], 'Pair'); - const pairsMap = {}; - for (let m = 0; m < pairs.length; m++) { - pairsMap[nodeVal(get1(pairs[m], 'key'))] = nodeVal(get1(pairs[m], 'styleUrl')); - } - styleMapIndex[`#${attr(styleMaps[l], 'id')}`] = pairsMap; - } - for (let j = 0; j < placemarks.length; j++) { - gj.features = gj.features.concat(getPlacemark(placemarks[j])); - } - function kmlColor(v) { - let color; let - opacity; - v = v || ''; - if (v.substr(0, 1) === '#') { v = v.substr(1); } - if (v.length === 6 || v.length === 3) { color = v; } - if (v.length === 8) { - opacity = parseInt(v.substr(0, 2), 16) / 255; - color = `#${v.substr(6, 2) - }${v.substr(4, 2) - }${v.substr(2, 2)}`; - } - return [color, isNaN(opacity) ? undefined : opacity]; - } - function gxCoord(v) { return numarray(v.split(' ')); } - function gxCoords(root) { - let elems = get(root, 'coord', 'gx'); const coords = []; const - times = []; - if (elems.length === 0) elems = get(root, 'gx:coord'); - for (let i = 0; i < elems.length; i++) coords.push(gxCoord(nodeVal(elems[i]))); - const timeElems = get(root, 'when'); - for (let j = 0; j < timeElems.length; j++) times.push(nodeVal(timeElems[j])); - return { - coords, - times - }; - } - function getGeometry(root) { - let geomNode; let geomNodes; let i; let j; let k; const geoms = []; const - coordTimes = []; - if (get1(root, 'MultiGeometry')) { return getGeometry(get1(root, 'MultiGeometry')); } - if (get1(root, 'MultiTrack')) { return getGeometry(get1(root, 'MultiTrack')); } - if (get1(root, 'gx:MultiTrack')) { return getGeometry(get1(root, 'gx:MultiTrack')); } - for (i = 0; i < geotypes.length; i++) { - geomNodes = get(root, geotypes[i]); - if (geomNodes) { - for (j = 0; j < geomNodes.length; j++) { - geomNode = geomNodes[j]; - if (geotypes[i] === 'Point') { - geoms.push({ - type: 'Point', - coordinates: coord1(nodeVal(get1(geomNode, 'coordinates'))) - }); - } else if (geotypes[i] === 'LineString') { - geoms.push({ - type: 'LineString', - coordinates: coord(nodeVal(get1(geomNode, 'coordinates'))) - }); - } else if (geotypes[i] === 'Polygon') { - const rings = get(geomNode, 'LinearRing'); - const coords = []; - for (k = 0; k < rings.length; k++) { - coords.push(coord(nodeVal(get1(rings[k], 'coordinates')))); - } - geoms.push({ - type: 'Polygon', - coordinates: coords - }); - } else if (geotypes[i] === 'Track' - || geotypes[i] === 'gx:Track') { - const track = gxCoords(geomNode); - geoms.push({ - type: 'LineString', - coordinates: track.coords - }); - if (track.times.length) coordTimes.push(track.times); - } - } - } - } - return { - geoms, - coordTimes - }; - } - function getPlacemark(root) { - const geomsAndTimes = getGeometry(root); let i; const properties = {}; - const name = nodeVal(get1(root, 'name')); - const address = nodeVal(get1(root, 'address')); - let styleUrl = nodeVal(get1(root, 'styleUrl')); - const description = nodeVal(get1(root, 'description')); - const timeSpan = get1(root, 'TimeSpan'); - const timeStamp = get1(root, 'TimeStamp'); - const extendedData = get1(root, 'ExtendedData'); - let lineStyle = get1(root, 'LineStyle'); - let polyStyle = get1(root, 'PolyStyle'); - const visibility = get1(root, 'visibility'); - - if (!geomsAndTimes.geoms.length) return []; - if (name) properties.name = name; - if (address) properties.address = address; - if (styleUrl) { - if (styleUrl[0] !== '#') { - styleUrl = `#${styleUrl}`; - } - - properties.styleUrl = styleUrl; - if (styleIndex[styleUrl]) { - properties.styleHash = styleIndex[styleUrl]; - } - if (styleMapIndex[styleUrl]) { - properties.styleMapHash = styleMapIndex[styleUrl]; - properties.styleHash = styleIndex[styleMapIndex[styleUrl].normal]; - } - // Try to populate the lineStyle or polyStyle since we got the style hash - const style = styleByHash[properties.styleHash]; - if (style) { - if (!lineStyle) lineStyle = get1(style, 'LineStyle'); - if (!polyStyle) polyStyle = get1(style, 'PolyStyle'); - const iconStyle = get1(style, 'IconStyle'); - if (iconStyle) { - const icon = get1(iconStyle, 'Icon'); - if (icon) { - const href = nodeVal(get1(icon, 'href')); - if (href) properties.icon = href; - } - } - } - } - if (description) properties.description = description; - if (timeSpan) { - const begin = nodeVal(get1(timeSpan, 'begin')); - const end = nodeVal(get1(timeSpan, 'end')); - properties.timespan = { begin, end }; - } - if (timeStamp) { - properties.timestamp = nodeVal(get1(timeStamp, 'when')); - } - if (lineStyle) { - const linestyles = kmlColor(nodeVal(get1(lineStyle, 'color'))); - const color = linestyles[0]; - const opacity = linestyles[1]; - const width = parseFloat(nodeVal(get1(lineStyle, 'width'))); - if (color) properties.stroke = color; - if (!isNaN(opacity)) properties['stroke-opacity'] = opacity; - if (!isNaN(width)) properties['stroke-width'] = width; - } - if (polyStyle) { - const polystyles = kmlColor(nodeVal(get1(polyStyle, 'color'))); - const pcolor = polystyles[0]; - const popacity = polystyles[1]; - const fill = nodeVal(get1(polyStyle, 'fill')); - const outline = nodeVal(get1(polyStyle, 'outline')); - if (pcolor) properties.fill = pcolor; - if (!isNaN(popacity)) properties['fill-opacity'] = popacity; - if (fill) properties['fill-opacity'] = fill === '1' ? properties['fill-opacity'] || 1 : 0; - if (outline) properties['stroke-opacity'] = outline === '1' ? properties['stroke-opacity'] || 1 : 0; - } - if (extendedData) { - const datas = get(extendedData, 'Data'); - const simpleDatas = get(extendedData, 'SimpleData'); - - for (i = 0; i < datas.length; i++) { - properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value')); - } - for (i = 0; i < simpleDatas.length; i++) { - properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]); - } - } - if (visibility) { - properties.visibility = nodeVal(visibility); - } - if (geomsAndTimes.coordTimes.length) { - properties.coordTimes = (geomsAndTimes.coordTimes.length === 1) - ? geomsAndTimes.coordTimes[0] : geomsAndTimes.coordTimes; - } - const feature = { - type: 'Feature', - geometry: (geomsAndTimes.geoms.length === 1) ? geomsAndTimes.geoms[0] : { - type: 'GeometryCollection', - geometries: geomsAndTimes.geoms - }, - properties - }; - if (attr(root, 'id')) feature.id = attr(root, 'id'); - return [feature]; - } - return gj; - }, - gpx(doc) { - let i; - const tracks = get(doc, 'trk'); - const routes = get(doc, 'rte'); - const waypoints = get(doc, 'wpt'); - // a feature collection - const gj = fc(); - let feature; - for (i = 0; i < tracks.length; i++) { - feature = getTrack(tracks[i]); - if (feature) gj.features.push(feature); - } - for (i = 0; i < routes.length; i++) { - feature = getRoute(routes[i]); - if (feature) gj.features.push(feature); - } - for (i = 0; i < waypoints.length; i++) { - gj.features.push(getPoint(waypoints[i])); - } - function initializeArray(arr, size) { - for (let h = 0; h < size; h++) { - arr.push(null); - } - return arr; - } - function getPoints(node, pointname) { - const pts = get(node, pointname); - const line = []; - const times = []; - const heartRates = []; - const l = pts.length; - if (l < 2) return {}; // Invalid line in GeoJSON - for (let i = 0; i < l; i++) { - const c = coordPair(pts[i]); - line.push(c.coordinates); - if (c.time) times.push(c.time); - if (c.heartRate || heartRates.length) { - if (!heartRates.length) initializeArray(heartRates, i); - heartRates.push(c.heartRate || null); - } - } - return { - line, - times, - heartRates - }; - } - function getTrack(node) { - const segments = get(node, 'trkseg'); - const track = []; - const times = []; - const heartRates = []; - let line; - for (let i = 0; i < segments.length; i++) { - line = getPoints(segments[i], 'trkpt'); - if (line) { - if (line.line) track.push(line.line); - if (line.times && line.times.length) times.push(line.times); - if (heartRates.length || (line.heartRates && line.heartRates.length)) { - if (!heartRates.length) { - for (let s = 0; s < i; s++) { - heartRates.push(initializeArray([], track[s].length)); - } - } - if (line.heartRates && line.heartRates.length) { - heartRates.push(line.heartRates); - } else { - heartRates.push(initializeArray([], line.line.length || 0)); - } - } - } - } - if (track.length === 0) return; - const properties = getProperties(node); - extend(properties, getLineStyle(get1(node, 'extensions'))); - if (times.length) properties.coordTimes = track.length === 1 ? times[0] : times; - if (heartRates.length) properties.heartRates = track.length === 1 ? heartRates[0] : heartRates; - return { - type: 'Feature', - properties, - geometry: { - type: track.length === 1 ? 'LineString' : 'MultiLineString', - coordinates: track.length === 1 ? track[0] : track - } - }; - } - function getRoute(node) { - const line = getPoints(node, 'rtept'); - if (!line.line) return; - const prop = getProperties(node); - extend(prop, getLineStyle(get1(node, 'extensions'))); - const routeObj = { - type: 'Feature', - properties: prop, - geometry: { - type: 'LineString', - coordinates: line.line - } - }; - return routeObj; - } - function getPoint(node) { - const prop = getProperties(node); - extend(prop, getMulti(node, ['sym'])); - return { - type: 'Feature', - properties: prop, - geometry: { - type: 'Point', - coordinates: coordPair(node).coordinates - } - }; - } - function getLineStyle(extensions) { - const style = {}; - if (extensions) { - const lineStyle = get1(extensions, 'line'); - if (lineStyle) { - const color = nodeVal(get1(lineStyle, 'color')); - const opacity = parseFloat(nodeVal(get1(lineStyle, 'opacity'))); - const width = parseFloat(nodeVal(get1(lineStyle, 'width'))); - if (color) style.stroke = color; - if (!isNaN(opacity)) style['stroke-opacity'] = opacity; - // GPX width is in mm, convert to px with 96 px per inch - if (!isNaN(width)) style['stroke-width'] = width * 96 / 25.4; - } - } - return style; - } - function getProperties(node) { - const prop = getMulti(node, ['name', 'cmt', 'desc', 'type', 'time', 'keywords']); - const links = get(node, 'link'); - if (links.length) prop.links = []; - for (var i = 0, link; i < links.length; i++) { - link = { href: attr(links[i], 'href') }; - extend(link, getMulti(links[i], ['text', 'type'])); - prop.links.push(link); - } - return prop; - } - return gj; - } - }; - return t; -}()); - -if (typeof module !== 'undefined') module.exports = toGeoJSON; diff --git a/src/services/Tracker.ts b/src/services/Tracker.ts index 7b09011d..11eeee93 100644 --- a/src/services/Tracker.ts +++ b/src/services/Tracker.ts @@ -1,4 +1,3 @@ -import { NativeModules } from 'react-native'; import BackgroundTimer from 'react-native-background-timer'; import AsyncStorage from '@react-native-community/async-storage'; import moment from 'moment'; @@ -9,11 +8,13 @@ import { import { Exposure, Location, SickJSON } from '../types'; import { registerLocalNotification } from './PushService'; import { setExposures } from '../actions/ExposuresActions'; +import { onError } from './ErrorService'; +import { initLocale } from '../actions/LocaleActions'; import config from '../config/config'; import store from '../store'; -import { onError } from './ErrorService'; -import { IS_IOS, LAST_FETCH_TS } from '../constants/Constants'; +import { LAST_FETCH_TS } from '../constants/Constants'; +// tslint:disable-next-line:no-var-requires const haversine = require('haversine'); export const startForegroundTimer = async () => { @@ -21,7 +22,7 @@ export const startForegroundTimer = async () => { BackgroundTimer.runBackgroundTimer(async () => { await checkSickPeople(); - }, config().sampleInterval); + }, config().fetchMilliseconds); }; export const queryDB = async () => { @@ -82,7 +83,7 @@ export const getIntersectingSickRecords = ( && isSpaceOverlapping(userRecord, sickRecord) ) { // add sick people you intersects - sickRecord.properties.fromTime_utc = userRecord.startTime; + sickRecord.properties.fromTime_utc = Math.max(userRecord.startTime, sickRecord.properties.fromTime_utc); sickRecord.properties.toTime_utc = userRecord.endTime; sickPeopleIntersected.push(sickRecord); } @@ -98,28 +99,9 @@ const checkMillisecondsDiff = (to: number, from: number) => { }; export const isTimeOverlapping = (userRecord: Location, sickRecord: Exposure) => { - // End time in the range - return ( - (userRecord.endTime > sickRecord.properties.fromTime_utc - && userRecord.endTime < sickRecord.properties.toTime_utc - && checkMillisecondsDiff( - userRecord.endTime, - Math.max(sickRecord.properties.fromTime_utc, userRecord.startTime), - )) - // in the range - || (userRecord.startTime < sickRecord.properties.fromTime_utc - && userRecord.endTime > sickRecord.properties.toTime_utc - && checkMillisecondsDiff( - sickRecord.properties.toTime_utc, - sickRecord.properties.fromTime_utc, - )) - // Start time in the range - || (userRecord.startTime > sickRecord.properties.fromTime_utc - && userRecord.startTime < sickRecord.properties.toTime_utc - && checkMillisecondsDiff( - Math.min(sickRecord.properties.toTime_utc, userRecord.endTime), - userRecord.startTime, - )) + return checkMillisecondsDiff( + Math.min(userRecord.endTime, sickRecord.properties.toTime_utc), + Math.max(userRecord.startTime, sickRecord.properties.fromTime_utc) ); }; @@ -138,41 +120,37 @@ export const isSpaceOverlapping = ({ lat, long }: Location, { properties: { radi }; export const onSickPeopleNotify = async (sickPeopleIntersected: Exposure[]) => { - const dbSick = new IntersectionSickDatabase(); + try { + const dbSick = new IntersectionSickDatabase(); - const exposuresToUpdate = []; + const exposuresToUpdate = []; - for (const currSick of sickPeopleIntersected) { - const queryResult = await dbSick.containsObjectID( - currSick.properties.Key_Field, - ); + for (const currSick of sickPeopleIntersected) { + const queryResult = await dbSick.containsObjectID( + currSick.properties.Key_Field, + ); - if (!queryResult) { - currSick.properties.fromTime = currSick.properties.fromTime_utc; - currSick.properties.toTime = currSick.properties.toTime_utc; - currSick.properties.OBJECTID = currSick.properties.Key_Field; + if (!queryResult) { + currSick.properties.fromTime = currSick.properties.fromTime_utc; + currSick.properties.toTime = currSick.properties.toTime_utc; + currSick.properties.OBJECTID = currSick.properties.Key_Field; - exposuresToUpdate.push(currSick); - await dbSick.addSickRecord(currSick); + exposuresToUpdate.push(currSick); + await dbSick.addSickRecord(currSick); + } } - } - - store().dispatch(setExposures(exposuresToUpdate)); - let locale: 'he' | 'en' | 'ar' | 'am' | 'ru' | 'fr' = (IS_IOS - ? NativeModules.SettingsManager.settings.AppleLocale - : NativeModules.I18nManager.localeIdentifier - ).substr(0, 2); + store().dispatch(setExposures(exposuresToUpdate)); - if (!['he', 'en', 'ar', 'am', 'ru', 'fr'].includes(locale)) { - locale = 'he'; - } + const { locale, notificationData } = await store().dispatch(initLocale()); - exposuresToUpdate.length > 0 - && (await registerLocalNotification( - config().sickMessage[locale].title, - config().sickMessage[locale].body, - config().sickMessage.duration, + exposuresToUpdate.length > 0 && await registerLocalNotification( + notificationData.sickMessage[locale].title, + notificationData.sickMessage[locale].body, + notificationData.sickMessage.duration, 'ms', - )); + ); + } catch (error) { + onError({ error }); + } }; diff --git a/src/services/__tests__/LocationHistoryService.test.ts b/src/services/__tests__/LocationHistoryService.test.ts new file mode 100644 index 00000000..cee3cda2 --- /dev/null +++ b/src/services/__tests__/LocationHistoryService.test.ts @@ -0,0 +1,184 @@ +import { onError } from '../ErrorService'; +import AsyncStorage from '@react-native-community/async-storage' +import moment,{valueOf,subtract,date,month,year,} from 'moment'; +import {encode} from 'latlon-geohash'; +import * as db from '../../database/Database'; +import {sha256} from '../sha256' +import {dispatch} from '../../store' +import { FIRST_POINT_TS, SHOULD_HIDE_LOCATION_HISTORY } from '../../constants/Constants'; +import * as generalActions from '../../actions/GeneralActions' +import {insertToSampleDB,kmlToGeoJson,getLoadingHTML, getLastNrDaysKmlUrls} from '../LocationHistoryService' + +import {kml} from '@tmcw/togeojson' + + +const actionSpy = jest.spyOn(generalActions, 'checkIfHideLocationHistory') + +beforeEach(() => { + AsyncStorage.mockClear() + moment.mockClear() +}) + + +jest.mock('xmldom', () => ({ + DOMParser: jest.fn().mockImplementation(() => ({ + parseFromString: jest.fn() + })) +})) + +// const {subtract, date, month, year} = momentSubFn + +describe('LocationHistoryService', () => { + + describe('getLoadingHTML', () => { + test('Is valid HTML', () => { + const { DOMParser } = jest.requireActual('xmldom') + const loadedHTML = getLoadingHTML() + // check if parsing the dom works + expect(new DOMParser().parseFromString(loadedHTML)).toBeDefined() + + }) + }) + + describe('getLastNrDaysKmlUrls', () => { + + test('make an array for 2 weeks', ()=> { + subtract.mockImplementation(() => ({ + date, + month, + year, + })) + expect(getLastNrDaysKmlUrls()).toHaveLength(14) + // length of 2 weeks + expect(subtract).toBeCalledTimes(14) + }) + }) + + describe('kmlToGeoJson', () => { + + beforeEach(() => { + kml.mockClear() + }) + + test('no text', () => { + kml.mockReturnValue({features: []}) + expect(kmlToGeoJson()).toHaveLength(0) + expect(kmlToGeoJson('')).toHaveLength(0) + }) + + test('filters results', () => { + const features = [ + { + geometry: { + type: 'Point', + + }, + properties: { + timespan: { + begin: 1, + end: 2 + }, + coordinates: [1, 1], + Category: 'should ignore from test', + + } + }, + ] + kml.mockReturnValue({features}) + expect(kmlToGeoJson('')).toHaveLength(0) + }) + + test('make proper sample obj', () => { + const features = [ + { + geometry: { + type: 'Point', + coordinates: [34.8077312410001, 32.1154996280001], + }, + properties: { + OBJECTID: 1720, + Key_Field: 1720, + Name: 'חולה 15', + Place: 'קלאוזנר 14, רמת אביב (קלפי ייעודית למבודדי בית)', + Comments: + 'על מי ששעת הגעתו לקלפי זו היתה בין השעות 10:15-11:15 להאריך את הבידוד הביתי ל14 יום מיום הבחירות', + POINT_X: 34.80773124, + POINT_Y: 32.11549963, + timespan: { + begin:1583144100000, + end: 1583147700000 + }, + sourceOID: 1, + stayTimes: '10:15-11:15', + }, + } + ] + valueOf.mockReturnValueOnce(1).mockReturnValueOnce(2) + kml.mockReturnValue({features}) + encode.mockReturnValueOnce(1) + + expect(kmlToGeoJson('')).toEqual([{ + startTime: 1, + endTime: 2, + long: 34.8077312410001, + lat: 32.1154996280001, + geoHash: 1, + accuracy: 0, + wifiHash: '' + }]) + }) + + }) + + describe('insertToSampleDB', () => { + beforeEach(() => { + dispatch.mockClear() + actionSpy.mockClear() + }) + // we assume insertToSampleDB will be called with array > 0 so we will not check + const sampleData = [ + { + startTime: 1583346600000, + endTime: 1583351500000, + accuracy: 10, + long: 35.535289000000034, + lat: 32.78675100000004, + }, + { + startTime: 1584468100000, + endTime: 1584568739000, + accuracy: 5, + long: 34.901541, + lat: 32.132502, + }, + + ] + + test('two data point',async () => { + + expect(await insertToSampleDB(sampleData)).toBeUndefined() + // sha was called with all the sample data + expect(sha256).toHaveBeenCalledWith(JSON.stringify(sampleData[0])) + expect(sha256).toHaveBeenCalledWith(JSON.stringify(sampleData[1])) + + expect(db.insertBulkSamples).toBeCalledTimes(1) + expect(db.insertBulkSamples).toBeCalledWith(sampleData.reduce((curr, sample) => `${curr}(${sample.lat},${sample.long},${sample.accuracy},${sample.startTime},${sample.endTime},'${sample.geoHash}','','a'),`,'').slice(0, -1)) + + expect(actionSpy).toBeCalledTimes(1) + + + } ) + + test('with FIRST_POINT_TS', async () => { + await AsyncStorage.setItem(FIRST_POINT_TS, (sampleData[0].startTime - 2000).toString()) + + expect(await insertToSampleDB(sampleData)).toBeUndefined() + + expect(AsyncStorage.setItem).toBeCalledWith(SHOULD_HIDE_LOCATION_HISTORY,'true') + expect(actionSpy).toBeCalledTimes(1) + expect(dispatch).toBeCalledTimes(1) + + }) + + }) +}) \ No newline at end of file diff --git a/src/services/__tests__/SampleService.test.ts b/src/services/__tests__/SampleService.test.ts new file mode 100644 index 00000000..0ac15ba0 --- /dev/null +++ b/src/services/__tests__/SampleService.test.ts @@ -0,0 +1,147 @@ +import AsyncStorage from '@react-native-community/async-storage'; +import { startSampling, insertDB, purgeSamplesDB, updateDBAccordingToSampleVelocity } from '../SampleService'; +import { onError } from '../ErrorService'; +import * as db from '../../database/Database'; +import { IS_LAST_POINT_FROM_TIMELINE, HIGH_VELOCITY_POINTS } from '../../constants/Constants'; + +jest.mock('../WifiService', () => ({ + getWifiList: jest.fn().mockResolvedValue(['ssdid']), +})); + +beforeEach(() => { + onError.mockClear(); + db.mockClear(); + AsyncStorage.mockClear(); +}); + +afterEach(() => { + expect(onError).toBeCalledTimes(0); +}); + +describe('Sample Service', () => { + describe('startSampling', () => { + test('check resolves', async () => { + // onError.mockImplementationOnce(console.log); + expect(startSampling('en', { androidNotification: {} })).resolves.toBeTruthy(); + }); + + test('check resolves for unknown language', async () => { + expect(startSampling('ge', { androidNotification: {} })).resolves.toBeTruthy(); + }); + }); + + describe('insertDB', () => { + const DBSample = { + coords: { + latitude: 31.307915, + longitude: 34.612383, + accuracy: 'high', + }, + timestamp: new Date().getTime(), + + }; + test('check empty storage', async () => { + // mock async storage has no value for FIRST_POINT_TS + AsyncStorage.getItem.mockResolvedValueOnce(undefined); + + expect(await insertDB(DBSample)).toBeTruthy(); + expect(AsyncStorage.setItem).toBeCalledTimes(2); + }); + + test('check storage not empty', async () => { + // mock async storage has value for FIRST_POINT_TS + AsyncStorage.getItem.mockResolvedValueOnce(true).mockResolvedValueOnce(false); + + expect(await insertDB(DBSample)).toBeTruthy(); + }); + }); + + describe('purgeSamplesDB', () => { + test('check purge DB returning resolved true', async () => { + new db.UserLocationsDatabase().purgeSamplesTable.mockResolvedValueOnce(true); + expect(purgeSamplesDB()).resolves.toBeUndefined(); + }); + + test('check purge DB returning resolved true', async () => { + new db.UserLocationsDatabase().purgeSamplesTable.mockRejectedValueOnce(new Error('Data base error')); + + + expect(purgeSamplesDB()).rejects.toBeUndefined(); + }); + }); + + describe('updateDBAccordingToSampleVelocity', () => { + const sample = { + activity: { + type: 'type', + confidence: 85 + }, + coords: { + latitude: 123.345, + longitude: 543.213, + accuracy: 0.4, + speed: 3 + }, + is_moving: true, + timestamp: new Date().getTime() + }; + + test('with last point missing', async () => { + // mock IS_LAST_POINT_FROM_TIMELINE not there + AsyncStorage.getItem.mockResolvedValueOnce(false); + expect(await updateDBAccordingToSampleVelocity(sample)).toBeFalsy(); + // check the storage is set with correct args + expect(AsyncStorage.setItem).toHaveBeenCalledWith(IS_LAST_POINT_FROM_TIMELINE, 'true'); + expect(AsyncStorage.setItem).toBeCalledTimes(1); + // check the db is set with correct args + expect(db.updateLastSampleEndTime).toHaveBeenCalledWith(sample.timestamp); + expect(db.updateLastSampleEndTime).toHaveBeenCalledTimes(1); + }); + + test('without velocity points', async () => { + // lower speed + sample.coords.speed = 2; + // mock IS_LAST_POINT_FROM_TIMELINE + await AsyncStorage.setItem(IS_LAST_POINT_FROM_TIMELINE, 'true') + // return insertDB + expect(await updateDBAccordingToSampleVelocity(sample)).toBeTruthy(); + }); + + test('with one point from DB', async () => { + sample.coords.speed = 2; + // mock IS_LAST_POINT_FROM_TIMELINE + await AsyncStorage.setItem(IS_LAST_POINT_FROM_TIMELINE, 'true') + + db.getLastPointEntered.mockResolvedValueOnce({ lat: 123.123, long: 321.321, accuracy: 0.7, endTime: new Date().getTime() }); + await updateDBAccordingToSampleVelocity(sample) + expect(AsyncStorage.removeItem).toBeCalledWith(HIGH_VELOCITY_POINTS); + }); + + test('with multiple points from db', async () => { + sample.is_moving = false; + const highVelocityPoints = [{ + coords: { + latitude: 123.123, + longitude: 321.321, + accuracy: 0.95 + }, + timestamp: 1586708482332 + }, + { + coords: { + latitude: 123.123, + longitude: 321.321, + accuracy: 0.95 + }, + timestamp: 1586708482332 - 20000 + }]; + + await AsyncStorage.setItem(IS_LAST_POINT_FROM_TIMELINE, 'false'); + await AsyncStorage.setItem(HIGH_VELOCITY_POINTS, JSON.stringify(highVelocityPoints)); + AsyncStorage.setItem.mockClear(); + + await updateDBAccordingToSampleVelocity(sample); + expect(AsyncStorage.setItem).toBeCalledWith(HIGH_VELOCITY_POINTS, JSON.stringify([...highVelocityPoints, sample])); + }); + }); +}); diff --git a/src/services/Tracker.test.ts b/src/services/__tests__/Tracker.test.ts similarity index 58% rename from src/services/Tracker.test.ts rename to src/services/__tests__/Tracker.test.ts index 3dd47c86..26225aca 100644 --- a/src/services/Tracker.test.ts +++ b/src/services/__tests__/Tracker.test.ts @@ -1,18 +1,21 @@ -// import fetch from 'cross-fetch'; -import {NativeModules} from 'react-native'; -import config from '../config/config'; -import * as tracker from './Tracker'; -import * as db from '../database/Database'; -import * as constants from '../constants/Constants'; - -jest.mock('./PushService', () => { +import { NativeModules } from 'react-native'; +import fetch from 'jest-fetch-mock'; +import * as tracker from '../Tracker'; +import { onError } from '../ErrorService'; +import * as db from '../../database/Database'; +import config from '../../config/config'; +import * as constants from '../../constants/Constants'; +import {valueOf} from 'moment'; +import {dispatch} from '../../store' +jest.mock('../PushService', () => { const registerLocalNotification = jest.fn(); - return {registerLocalNotification}; + return { registerLocalNotification }; }); + const oneMinute = 60 * 1000; const oneHour = 60 * oneMinute; -const {intersectMilliseconds} = config(); +const { intersectMilliseconds } = config(); const largerThanIntersectMilliseconds = 1.1 * intersectMilliseconds; const smallerThanIntersectMilliseconds = 0.9 * intersectMilliseconds; const sickPeople = { @@ -22,6 +25,7 @@ const sickPeople = { }, properties: { OBJECTID: 1720, + Key_Field: 1720, Name: 'חולה 15', Place: 'קלאוזנר 14, רמת אביב (קלפי ייעודית למבודדי בית)', Comments: @@ -30,6 +34,8 @@ const sickPeople = { POINT_Y: 32.11549963, fromTime: 1583144100000, toTime: 1583147700000, + fromTime_utc: 1583144100000, + toTime_utc: 1583147700000, sourceOID: 1, stayTimes: '10:15-11:15', }, @@ -41,6 +47,12 @@ const sickRecord = { fromTime_utc: 10 * oneHour, toTime_utc: 20 * oneHour, radius: 500, + OBJECTID: 1, + Key_Field: 1, + Name: 'name', + Place: 'place', + fromTime: 10 * oneHour, + toTime: 20 * oneHour }, geometry: { type: 'Point', @@ -48,72 +60,118 @@ const sickRecord = { }, }; +const userRecordExtras = { + lat: 32, + long: 34, + accuracy: 0, + geoHash: '', + hash: '', + wifiHash: '' +}; + +const userRecordExtras2 = { + startTime: oneHour, + endTime: 2 * oneHour, + accuracy: 0, + geoHash: '', + hash: '', + wifiHash: '' +}; + +beforeAll(() => { + dispatch.mockImplementation(() => ({ + locale: 'he', + notificationData: { + androidNotification: {}, + sickMessage: { + he: { + title: 'כותרת', + body: 'הודעה' + } + } + } + })) +}) + +afterAll(() => { + dispatch.mockClear() +}) + +beforeEach(() => { + onError.mockClear(); +}); + +afterEach(() => { + expect(onError).toBeCalledTimes(0); +}); + describe('Tracker', () => { + // ==================================== // Check all TimeOverlapping Scenario // ==================================== - test('isTimeOverlapping()', async () => { - //Check user time not intersects before sick time range + test('isTimeOverlapping()', () => { + // Check user time not intersects before sick time range expect( tracker.isTimeOverlapping( - {startTime: oneHour, endTime: 2 * oneHour}, + { startTime: oneHour, endTime: 2 * oneHour, ...userRecordExtras }, sickRecord, ), ).toBe(false); - //Check user no intersects same end time + // Check user no intersects same end time expect( tracker.isTimeOverlapping( - {startTime: 8 * oneHour, endTime: 10 * oneHour}, + { startTime: 8 * oneHour, endTime: 10 * oneHour, ...userRecordExtras }, sickRecord, ), ).toBe(false); - //Check user end time intersects sick range + // Check user end time intersects sick range expect( tracker.isTimeOverlapping( - {startTime: 8 * oneHour, endTime: 12 * oneHour}, + { startTime: 8 * oneHour, endTime: 12 * oneHour, ...userRecordExtras }, sickRecord, ), ).toBe(true); - //Check sick user full time inside user time + // Check sick user full time inside user time expect( tracker.isTimeOverlapping( - {startTime: 9 * oneHour, endTime: 22 * oneHour}, + { startTime: 9 * oneHour, endTime: 22 * oneHour, ...userRecordExtras }, sickRecord, ), ).toBe(true); - //Check user full time in sick range + // Check user full time in sick range expect( tracker.isTimeOverlapping( - {startTime: 11 * oneHour, endTime: 12 * oneHour}, + { startTime: 11 * oneHour, endTime: 12 * oneHour, ...userRecordExtras }, sickRecord, ), ).toBe(true); - //Check user start time intersects with sick time + // Check user start time intersects with sick time expect( tracker.isTimeOverlapping( - {startTime: 15 * oneHour, endTime: 22 * oneHour}, + { startTime: 15 * oneHour, endTime: 22 * oneHour, ...userRecordExtras }, sickRecord, ), ).toBe(true); - //Check no intersects but same end time + // Check no intersects but same end time expect( tracker.isTimeOverlapping( - {startTime: 20 * oneHour, endTime: 22 * oneHour}, + { startTime: 20 * oneHour, endTime: 22 * oneHour, ...userRecordExtras }, sickRecord, ), ).toBe(false); - //Check user time not intersects after sick time + // Check user time not intersects after sick time expect( tracker.isTimeOverlapping( - {startTime: 21 * oneHour, endTime: 26 * oneHour}, + { startTime: 21 * oneHour, endTime: 26 * oneHour, ...userRecordExtras }, sickRecord, ), ).toBe(false); @@ -122,70 +180,74 @@ describe('Tracker', () => { // Check the milliseconds overlapping restriction // ================================================ - //Check smaller than intersect milliseconds + // Check smaller than intersect milliseconds expect( tracker.isTimeOverlapping( { startTime: 8 * oneHour, endTime: 10 * oneHour + smallerThanIntersectMilliseconds, + ...userRecordExtras }, sickRecord, ), ).toBe(false); - //Check larger than intersect milliseconds + // Check larger than intersect milliseconds expect( tracker.isTimeOverlapping( { startTime: 9 * oneHour, endTime: 10 * oneHour + largerThanIntersectMilliseconds, + ...userRecordExtras }, sickRecord, ), ).toBe(true); - //Check larger than intersect milliseconds + // Check larger than intersect milliseconds expect( tracker.isTimeOverlapping( { startTime: 18 * oneHour + smallerThanIntersectMilliseconds, endTime: 21 * oneHour, + ...userRecordExtras }, sickRecord, ), ).toBe(true); + }); - test('unitTestGeography()', async () => { + test('unitTestGeography()', () => { // South expect( - tracker.isSpaceOverlapping({long: 34.612383, lat: 31.307915}, sickRecord), + tracker.isSpaceOverlapping({ long: 34.612383, lat: 31.307915, ...userRecordExtras2 }, sickRecord), ).toBe(true); expect( - tracker.isSpaceOverlapping({long: 34.612645, lat: 31.305848}, sickRecord), + tracker.isSpaceOverlapping({ long: 34.612645, lat: 31.305848, ...userRecordExtras2 }, sickRecord), ).toBe(false); // West expect( - tracker.isSpaceOverlapping({long: 34.609032, lat: 31.311498}, sickRecord), + tracker.isSpaceOverlapping({ long: 34.609032, lat: 31.311498, ...userRecordExtras2 }, sickRecord), ).toBe(true); expect( - tracker.isSpaceOverlapping({long: 34.604462, lat: 31.311608}, sickRecord), + tracker.isSpaceOverlapping({ long: 34.604462, lat: 31.311608, ...userRecordExtras2 }, sickRecord), ).toBe(false); // North-East expect( - tracker.isSpaceOverlapping({long: 34.615315, lat: 31.312473}, sickRecord), + tracker.isSpaceOverlapping({ long: 34.615315, lat: 31.312473, ...userRecordExtras2 }, sickRecord), ).toBe(true); expect( - tracker.isSpaceOverlapping({long: 34.618952, lat: 31.314902}, sickRecord), + tracker.isSpaceOverlapping({ long: 34.618952, lat: 31.314902, ...userRecordExtras2 }, sickRecord), ).toBe(false); }); test('unitTestIntersectingRecords()', async () => { fetch(config().dataUrl_utc, { - headers: {'Content-Type': 'application/json;charset=utf-8'}, + headers: { 'Content-Type': 'application/json;charset=utf-8' }, }) .then(response => response.json()) - .then(async responseJson => { + .then(async (responseJson) => { const myData = [ { startTime: 1583144150000, @@ -193,6 +255,9 @@ describe('Tracker', () => { accuracy: 5, long: 34.807731241000056, lat: 32.115499628000066, + geoHash: '', + hash: '', + wifiHash: '' }, { startTime: 1583346600000, @@ -200,6 +265,9 @@ describe('Tracker', () => { accuracy: 10, long: 35.535289000000034, lat: 32.78675100000004, + geoHash: '', + hash: '', + wifiHash: '' }, { startTime: 1583346600000, @@ -207,6 +275,9 @@ describe('Tracker', () => { accuracy: 10, long: 37.535289000000034, lat: 32.78675100000004, + geoHash: '', + hash: '', + wifiHash: '' }, { startTime: 1584391600000, @@ -214,6 +285,9 @@ describe('Tracker', () => { accuracy: 10, long: 35.535289000000034, lat: 32.78675100000004, + geoHash: '', + hash: '', + wifiHash: '' }, ]; @@ -224,32 +298,33 @@ describe('Tracker', () => { expect(intersectingRecords.length).toEqual(2); }); + }); test('queryDB()', async () => { - const userLocationDB = new db.UserLocationsDatabase(); const rows = ['data1', 'data2', 'data3']; - userLocationDB.listSamples.mockReturnValueOnce(Promise.resolve(rows)); - await expect(tracker.queryDB()).resolves.toEqual(rows); + const userLocationDB = new db.UserLocationsDatabase(); + userLocationDB.listSamples.mockResolvedValueOnce(rows); + expect(tracker.queryDB()).resolves.toEqual(rows); }); test('onSickPeopleNotify()', async () => { const sickDB = new db.IntersectionSickDatabase(); const rows: any = []; - sickDB.addSickRecord.mockReturnValueOnce(Promise.resolve(rows)); - sickDB.containsObjectID.mockReturnValueOnce(Promise.resolve(rows)); + sickDB.addSickRecord.mockResolvedValueOnce(rows); + sickDB.containsObjectID.mockResolvedValueOnce(rows); // check he - await expect(tracker.onSickPeopleNotify(sickPeopleArray)).resolves.toEqual( + expect(tracker.onSickPeopleNotify(sickPeopleArray)).resolves.toEqual( undefined, ); // check unsupported language NativeModules.SettingsManager.settings.AppleLocale = 'gh'; - await expect(tracker.onSickPeopleNotify(sickPeopleArray)).resolves.toEqual( + expect(tracker.onSickPeopleNotify(sickPeopleArray)).resolves.toEqual( undefined, ); constants.IS_IOS = false; - await expect(tracker.onSickPeopleNotify(sickPeopleArray)).resolves.toEqual( + expect(tracker.onSickPeopleNotify(sickPeopleArray)).resolves.toEqual( undefined, ); }); @@ -298,8 +373,9 @@ describe('Tracker', () => { }, ], }; - fetch.mockResponseOnce(JSON.stringify(responseJSON)); + fetch.once(JSON.stringify(responseJSON)); userLocationDB.listSamples.mockReturnValueOnce(Promise.resolve(sampleData)); + valueOf.mockReturnValueOnce(new Date().getTime()) await tracker.checkSickPeople(); }); @@ -308,18 +384,30 @@ describe('Tracker', () => { const responseJSON = { features: 'asd', }; - fetch.mockResponseOnce(JSON.stringify(responseJSON)); + fetch.once(JSON.stringify(responseJSON)); userLocationDB.listSamples.mockReturnValueOnce(Promise.resolve([])); + await tracker.checkSickPeople(); }); test('checkSickPeople() - fetch error', async () => { + fetch.mockReject(new Error('fake error message')) const userLocationDB = new db.UserLocationsDatabase(); userLocationDB.listSamples.mockReturnValueOnce(Promise.resolve([])); + // valueOf.mockReturnValueOnce(new Date().getTime()) + await tracker.checkSickPeople(); + + expect(onError).toBeCalledTimes(1) + // make sure after all wont fail + onError.mockClear() }); test('startForegroundTimer()', async () => { + const responseJSON = { + features: 'asd', + }; + fetch.once(JSON.stringify(responseJSON)); await tracker.startForegroundTimer(); }); }); diff --git a/src/types/index.ts b/src/types/index.ts index e4e9864b..22de26e8 100755 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -11,49 +11,10 @@ export interface Config { bufferUnits: string, sickGeometryLongIndex: number, sickGeometryLatIndex: number, - locationHistoryIgnoreList: string[], - androidNotification: { he: string, en: string, am: string, ru: string, ar: string, fr: string }, - sickMessage: { - he: { title: string, body: string }, - en: { title: string, body: string }, - am: { title: string, body: string }, - ru: { title: string, body: string }, - ar: { title: string, body: string }, - fr: { title: string, body: string } - duration: number - }, - furtherInstructions: { - he: string, - en: string, - am: string, - ru: string, - ar: string, - fr: string - }, - reportForm: { - he: string, - en: string, - am: string, - ru: string, - ar: string, - fr: string - }, - usageTerms: { - he: string, - en: string, - am: string, - ru: string, - ar: string, - fr: string - }, - privacyTerms: { - he: string, - en: string, - am: string, - ru: string, - ar: string, - fr: string - } + locationServiceIgnoreList: string[], + locationServiceIgnoreConfidenceThreshold: number, + locationServiceIgnoreSampleVelocityThreshold: number, + locationHistoryIgnoreList: string[] } export interface ReducerAction { @@ -69,12 +30,6 @@ export interface ErrorService { showError?: boolean } -export interface Locale { - locale: 'he'|'en'|'ar'|'am'|'ru'|'fr', - isRTL: boolean, - strings: any -} - export interface Fonts { [key: string]: string } @@ -103,6 +58,32 @@ export interface Exposure { } } +export interface Sample { + activity: { + type: string, + confidence: number + }, + coords: { + latitude: number, + longitude: number, + accuracy: number, + speed: number + }, + is_moving: boolean, + timestamp: number +} + +export interface DBLocation { + lat: number, + long: number, + accuracy: number, + startTime: number, + endTime: number, + geoHash: string, + wifiHash: string, + hash: string +} + export interface Location { geoHash: string, hash: string, @@ -114,15 +95,13 @@ export interface Location { long: number } -export interface DBExposure { - OBJECTID: number, - Name: string, - Place: string, - fromTime: number, - toTime: number -} - export interface ValidExposure { exposure: Exposure, timestamp: number } + +export interface VelocityRecord { + distMeter: number, + timeDiff: number, + velocity: number, +} diff --git a/yarn.lock b/yarn.lock index 2c2e2c85..a0b5ab2b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1240,6 +1240,13 @@ wcwidth "^1.0.1" ws "^1.1.0" +"@react-native-community/cookies@^2.0.8": + version "2.0.8" + resolved "https://registry.yarnpkg.com/@react-native-community/cookies/-/cookies-2.0.8.tgz#baedf268fc32d463d3314f2d9f4b50c977c8f1d2" + integrity sha512-uIb+VBMoOz/zlh2FxZ1FEXc+/RpgD+YrF8XG7jvQcJu3GimsD8mf6hxfjHUDnFonilQgL0jDB2biTR5rF1XGbg== + dependencies: + invariant "^2.2.4" + "@react-native-community/eslint-config@0.0.5": version "0.0.5" resolved "https://registry.yarnpkg.com/@react-native-community/eslint-config/-/eslint-config-0.0.5.tgz#584f6493258202a57efc22e7be66966e43832795" @@ -1257,9 +1264,10 @@ eslint-plugin-react-native "3.6.0" prettier "1.16.4" -"@react-native-community/masked-view@https://github.com/greenyossi/react-native-masked-view.git": - version "0.1.1" - resolved "https://github.com/greenyossi/react-native-masked-view.git#eaadd2b66420dae9ce9c0520d2af7491181f7699" +"@react-native-community/masked-view@^0.1.8": + version "0.1.8" + resolved "https://registry.yarnpkg.com/@react-native-community/masked-view/-/masked-view-0.1.8.tgz#05c9c800f258274a34abd584596464b86fbd92dc" + integrity sha512-y+mnsPPC4Etzs/waHIC9Pj3DwhVQGqVru0n1KSqHZc0moKNAZ0NRBWYVat9awLIjjVrnepUva/+/bi31T4hR4A== "@react-native-community/netinfo@5.6.2": version "5.6.2" @@ -1331,6 +1339,10 @@ dependencies: type-detect "4.0.8" +"@tmcw/togeojson@https://github.com/tmcw/togeojson.git": + version "4.0.0" + resolved "https://github.com/tmcw/togeojson.git#47a726b69bd8b7f0eb4adcb27c021136e86afdae" + "@types/async-lock@1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/async-lock/-/async-lock-1.1.1.tgz#81f218213bebcc5f740efe9648272c774a2e4b4b" @@ -1496,6 +1508,18 @@ "@types/prop-types" "*" csstype "^2.2.0" +"@types/redux-mock-store@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/redux-mock-store/-/redux-mock-store-1.0.2.tgz#c27d5deadfb29d8514bdb0fc2cadae6feea1922d" + integrity sha512-6LBtAQBN34i7SI5X+Qs4zpTEZO1tTDZ6sZ9fzFjYwTl3nLQXaBtwYdoV44CzNnyKu438xJ1lSIYyw0YMvunESw== + dependencies: + redux "^4.0.5" + +"@types/seedrandom@^2.4.28": + version "2.4.28" + resolved "https://registry.yarnpkg.com/@types/seedrandom/-/seedrandom-2.4.28.tgz#9ce8fa048c1e8c85cb71d7fe4d704e000226036f" + integrity sha512-SMA+fUwULwK7sd/ZJicUztiPs8F1yCPwF3O23Z9uQ32ME5Ha0NmDK9+QTsYE4O2tHXChzXomSWWeIhCnoN1LqA== + "@types/stack-utils@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" @@ -7138,7 +7162,7 @@ realpath-native@^2.0.0: resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-2.0.0.tgz#7377ac429b6e1fd599dc38d08ed942d0d7beb866" integrity sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q== -redux-mock-store@1.5.4: +redux-mock-store@^1.5.4: version "1.5.4" resolved "https://registry.yarnpkg.com/redux-mock-store/-/redux-mock-store-1.5.4.tgz#90d02495fd918ddbaa96b83aef626287c9ab5872" integrity sha512-xmcA0O/tjCLXhh9Fuiq6pMrJCwFRaouA8436zcikdIpYWWCjU76CRk+i2bHx8EeiSiMGnB85/lZdU3wIJVXHTA== @@ -7159,7 +7183,7 @@ redux-thunk@2.3.0: resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622" integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw== -redux@4.0.5, redux@^4.0.0: +redux@4.0.5, redux@^4.0.0, redux@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f" integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w== @@ -7501,6 +7525,11 @@ scheduler@0.15.0, scheduler@^0.15.0: loose-envify "^1.1.0" object-assign "^4.1.1" +seedrandom@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-3.0.5.tgz#54edc85c95222525b0c7a6f6b3543d8e0b3aa0a7" + integrity sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg== + "semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"