From c0eb6d8e201231898577558f4a0c98d440a9ef4f Mon Sep 17 00:00:00 2001 From: Manan Jadhav Date: Tue, 14 Sep 2021 01:32:59 +0530 Subject: [PATCH 01/38] feat(periodic-background-updates): Initial setup for background fetch --- android/app/proguard-rules.pro | 3 ++ android/build.gradle | 4 ++ ios/NewExpensify.xcodeproj/project.pbxproj | 26 +++++++----- ios/NewExpensify/AppDelegate.m | 4 ++ ios/NewExpensify/Info.plist | 6 +++ package-lock.json | 5 +++ package.json | 1 + src/Expensify.js | 5 ++- .../initializeBackgroundFetch.js | 42 +++++++++++++++++++ 9 files changed, 85 insertions(+), 11 deletions(-) create mode 100644 src/libs/backgroundTasks/initializeBackgroundFetch.js diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro index 7dab035002a2..aece992e5197 100644 --- a/android/app/proguard-rules.pro +++ b/android/app/proguard-rules.pro @@ -10,3 +10,6 @@ # Add any project specific keep options here: -keep class com.facebook.hermes.unicode.** { *; } -keep class com.facebook.jni.** { *; } + +# [react-native-background-fetch] +-keep class com.transistorsoft.rnbackgroundfetch.HeadlessTask { *; } diff --git a/android/build.gradle b/android/build.gradle index 0f9aece0cfa0..a10632a2d8ce 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -41,5 +41,9 @@ allprojects { google() jcenter() maven { url 'https://www.jitpack.io' } + maven { + // react-native-background-fetch + url("${project(':react-native-background-fetch').projectDir}/libs") + } } } diff --git a/ios/NewExpensify.xcodeproj/project.pbxproj b/ios/NewExpensify.xcodeproj/project.pbxproj index 0102b9f060be..1ccdb0ef33db 100644 --- a/ios/NewExpensify.xcodeproj/project.pbxproj +++ b/ios/NewExpensify.xcodeproj/project.pbxproj @@ -31,6 +31,8 @@ 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; 8821A238A081483FA947BC4E /* GTAmericaExp-RgIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 918D7FEFF96242E6B5F5E14D /* GTAmericaExp-RgIt.otf */; }; DB77016704074197AB6633BB /* GTAmericaExpMono-RgIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 5150E5D0D7F74DBA8D7C1914 /* GTAmericaExpMono-RgIt.otf */; }; + E8B0732B26EA56E8009FC59C /* RNBackgroundFetch+AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E8B0732A26EA56E8009FC59C /* RNBackgroundFetch+AppDelegate.m */; }; + E8B0732C26EA56E8009FC59C /* RNBackgroundFetch+AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E8B0732A26EA56E8009FC59C /* RNBackgroundFetch+AppDelegate.m */; }; E9DF872D2525201700607FDC /* AirshipConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9DF872C2525201700607FDC /* AirshipConfig.plist */; }; ED814D34526B415CAFA0451E /* GTAmericaExpMono-BdIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 3981452A2C7340EBBA2B9BD1 /* GTAmericaExpMono-BdIt.otf */; }; F2C9290F276DB05EEF04D160 /* libPods-NewExpensify-NewExpensifyTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 820334EEB8DDED5217A15684 /* libPods-NewExpensify-NewExpensifyTests.a */; }; @@ -82,6 +84,7 @@ D314A6D536D2ADA797DCDAC9 /* Pods-NewExpensify-NewExpensifyTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify-NewExpensifyTests.debug.xcconfig"; path = "Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests.debug.xcconfig"; sourceTree = ""; }; D3D8FA6E27ED5DD98F515230 /* Pods-NewExpensify.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify.debug.xcconfig"; path = "Target Support Files/Pods-NewExpensify/Pods-NewExpensify.debug.xcconfig"; sourceTree = ""; }; DB5A1365442D4419AF6F08E5 /* GTAmericaExp-MdIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-MdIt.otf"; path = "../assets/fonts/GTAmericaExp-MdIt.otf"; sourceTree = ""; }; + E8B0732A26EA56E8009FC59C /* RNBackgroundFetch+AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "RNBackgroundFetch+AppDelegate.m"; path = "../node_modules/react-native-background-fetch/ios/RNBackgroundFetch/RNBackgroundFetch+AppDelegate.m"; sourceTree = ""; }; E9DF872C2525201700607FDC /* AirshipConfig.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = AirshipConfig.plist; sourceTree = ""; }; EB3C4841B1DB1490EA09A324 /* Pods-NewExpensify.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify.release.xcconfig"; path = "Target Support Files/Pods-NewExpensify/Pods-NewExpensify.release.xcconfig"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; @@ -128,6 +131,7 @@ 13B07FAE1A68108700A75B9A /* NewExpensify */ = { isa = PBXGroup; children = ( + E8B0732A26EA56E8009FC59C /* RNBackgroundFetch+AppDelegate.m */, 0F5BE0CD252686320097D869 /* GoogleService-Info.plist */, E9DF872C2525201700607FDC /* AirshipConfig.plist */, 008F07F21AC5B25A0029DE68 /* main.jsbundle */, @@ -282,9 +286,9 @@ TestTargetID = 13B07F861A680F5B00A75B9A; }; 13B07F861A680F5B00A75B9A = { - DevelopmentTeam = 368M544MTT; + DevelopmentTeam = 72TRNPHV7D; LastSwiftMigration = 1230; - ProvisioningStyle = Manual; + ProvisioningStyle = Automatic; }; }; }; @@ -760,6 +764,7 @@ 00E356F31AD99517003FC87E /* ExpensifyCashTests.m in Sources */, 0F5E5351263B73FD004CA14F /* EnvironmentChecker.m in Sources */, 7041848626A8E47D00E09F4D /* RCTStartupTimer.m in Sources */, + E8B0732C26EA56E8009FC59C /* RNBackgroundFetch+AppDelegate.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -770,6 +775,7 @@ 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, 18D050E0262400AF000D658B /* BridgingFile.swift in Sources */, 0F5E5350263B73FD004CA14F /* EnvironmentChecker.m in Sources */, + E8B0732B26EA56E8009FC59C /* RNBackgroundFetch+AppDelegate.m in Sources */, 7041848526A8E47D00E09F4D /* RCTStartupTimer.m in Sources */, 13B07FC11A68108700A75B9A /* main.m in Sources */, ); @@ -841,10 +847,10 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = NewExpensify/Chat.entitlements; - CODE_SIGN_IDENTITY = "iPhone Distribution"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 3; - DEVELOPMENT_TEAM = 368M544MTT; + DEVELOPMENT_TEAM = 72TRNPHV7D; ENABLE_BITCODE = NO; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; INFOPLIST_FILE = "$(SRCROOT)/NewExpensify/Info.plist"; @@ -858,7 +864,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.chat.expensify.chat; PRODUCT_NAME = "New Expensify"; - PROVISIONING_PROFILE_SPECIFIER = chat_expensify_appstore; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "ExpensifyCash-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -874,10 +880,10 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = NewExpensify/Chat.entitlements; - CODE_SIGN_IDENTITY = "iPhone Distribution"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 3; - DEVELOPMENT_TEAM = 368M544MTT; + DEVELOPMENT_TEAM = 72TRNPHV7D; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; INFOPLIST_FILE = NewExpensify/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 11.0; @@ -890,7 +896,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.chat.expensify.chat; PRODUCT_NAME = "New Expensify"; - PROVISIONING_PROFILE_SPECIFIER = chat_expensify_appstore; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "ExpensifyCash-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/ios/NewExpensify/AppDelegate.m b/ios/NewExpensify/AppDelegate.m index a9e4f15e78dd..0d3044eb1dee 100644 --- a/ios/NewExpensify/AppDelegate.m +++ b/ios/NewExpensify/AppDelegate.m @@ -10,6 +10,7 @@ #import "RNBootSplash.h" #import +#import #import #import @@ -79,6 +80,9 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( // module in the JS so we can measure total time starting in the native layer and ending in // the JS layer. [RCTStartupTimer start]; + + // [REQUIRED] Register BackgroundFetch + [[TSBackgroundFetch sharedInstance] didFinishLaunching]; return YES; } diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 72fd87107d91..5e6f1bae01b7 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -91,6 +91,8 @@ UIBackgroundModes + fetch + processing remote-notification UIFileSharingEnabled @@ -110,5 +112,9 @@ UIViewControllerBasedStatusBarAppearance + BGTaskSchedulerPermittedIdentifiers + + com.transistorsoft.fetch + diff --git a/package-lock.json b/package-lock.json index fd638ec057cc..bbe908b71567 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41058,6 +41058,11 @@ "prop-types": "^15.7.2" } }, + "react-native-background-fetch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/react-native-background-fetch/-/react-native-background-fetch-4.0.3.tgz", + "integrity": "sha512-Rh/xmlLJwt7dtbXBm2jv0WS+KNBG9di/Vd1qut7Mi88Ap2C+8Yv3UOcEacs3VUSauw3moWtnMtDBaoK9ZgYOIQ==" + }, "react-native-bootsplash": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/react-native-bootsplash/-/react-native-bootsplash-3.2.0.tgz", diff --git a/package.json b/package.json index 44897c131af8..1cbcf3c257b1 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "react-collapse": "^5.1.0", "react-dom": "^17.0.2", "react-native": "0.64.1", + "react-native-background-fetch": "^4.0.3", "react-native-bootsplash": "^3.2.0", "react-native-collapsible": "^1.6.0", "react-native-config": "^1.4.0", diff --git a/src/Expensify.js b/src/Expensify.js index 8bc11177fa57..dec346f694fe 100644 --- a/src/Expensify.js +++ b/src/Expensify.js @@ -3,7 +3,6 @@ import PropTypes from 'prop-types'; import React, {PureComponent} from 'react'; import {View, AppState} from 'react-native'; import {withOnyx} from 'react-native-onyx'; - import BootSplash from './libs/BootSplash'; import * as ActiveClientManager from './libs/ActiveClientManager'; import ONYXKEYS from './ONYXKEYS'; @@ -16,6 +15,8 @@ import Visibility from './libs/Visibility'; import GrowlNotification from './components/GrowlNotification'; import {growlRef} from './libs/Growl'; import StartupTimer from './libs/StartupTimer'; +import initializeBackgroundFetch from './libs/backgroundTasks/initializeBackgroundFetch'; + const propTypes = { /* Onyx Props */ @@ -84,6 +85,8 @@ class Expensify extends PureComponent { }); AppState.addEventListener('change', this.initializeClient); + + initializeBackgroundFetch(); } componentDidUpdate(prevProps) { diff --git a/src/libs/backgroundTasks/initializeBackgroundFetch.js b/src/libs/backgroundTasks/initializeBackgroundFetch.js new file mode 100644 index 000000000000..1970b536177c --- /dev/null +++ b/src/libs/backgroundTasks/initializeBackgroundFetch.js @@ -0,0 +1,42 @@ +import BackgroundFetch from 'react-native-background-fetch'; + +/** + * Initialize task to run background fetch + */ +export default async function initializeBackgroundFetch() { // eslint-disable-line + // Setup task config + const config = { + minimumFetchInterval: 15, // <-- minutes (15 is minimum allowed) + // Android options + stopOnTerminate: false, + startOnBoot: true, + requiredNetworkType: BackgroundFetch.NETWORK_TYPE_ANY, // Default + requiresCharging: false, // Default + requiresDeviceIdle: false, // Default + requiresBatteryNotLow: false, // Default + requiresStorageNotLow: false, // Default + enableHeadless: true, + }; + + // Setup BackgroundFetch task + const onEvent = async (taskId) => { // eslint-disable-line + console.log('[BackgroundFetch] task: ', taskId); + + // TODO: retrieve network data, persist to Onyx + const x = 1 + 1; + console.log('[BackgroundFetch] x', x); + + // Signal to OS that task is complete. + BackgroundFetch.finish(taskId); + }; + + // Invoked when task exceeds its allowed running-time + const onTimeout = async (taskId) => { // eslint-disable-line + console.warn('[BackgroundFetch] TIMEOUT task: ', taskId); + BackgroundFetch.finish(taskId); + }; + + // Initialize BackgroundFetch only once when component mounts. + const status = await BackgroundFetch.configure(config, onEvent, onTimeout); + console.log('[BackgroundFetch] configure status: ', status); +} From b17d2ca8b3e7f619d98844c5046d5c78b489a587 Mon Sep 17 00:00:00 2001 From: Manan Jadhav Date: Wed, 20 Oct 2021 03:32:05 +0530 Subject: [PATCH 02/38] fix(background-fetch): WIP Adding headless task --- index.js | 33 +++++++++++++++++++ .../initializeBackgroundFetch.js | 9 +++-- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 625576f20ef7..bcee12851281 100644 --- a/index.js +++ b/index.js @@ -4,9 +4,42 @@ import 'react-native-gesture-handler'; import {AppRegistry} from 'react-native'; +import BackgroundFetch from 'react-native-background-fetch'; + import App from './src/App'; import Config from './src/CONFIG'; import additionalAppSetup from './src/setup'; + +const headlessTask = async (event) => { // eslint-disable-line + + // Get task id from event {}: + const taskId = event.taskId; + const isTimeout = event.timeout; // <-- true when your background-time has expired. + if (isTimeout) { + // This task has exceeded its allowed running-time. + // You must stop what you're doing immediately finish(taskId) + console.log('[BackgroundFetchHeadlessTask] Headless TIMEOUT:', taskId); + BackgroundFetch.finish(taskId); + return; + } + + console.log('[BackgroundFetch HeadlessTask] start: ', taskId); + + // Perform an example HTTP request. + // Important: await asychronous tasks when using HeadlessJS. + const response = await fetch('http://5361-103-93-193-198.ngrok.io/'); + const responseJson = await response.json(); + console.log('[BackgroundFetchHeadlessTask] response: ', responseJson); + + // Required: Signal to native code that your task is complete. + // If you don't do this, your app could be terminated and/or assigned + // battery-blame for consuming too much time in background. + BackgroundFetch.finish(taskId); +}; + +// Register your BackgroundFetch HeadlessTask +BackgroundFetch.registerHeadlessTask(headlessTask); + AppRegistry.registerComponent(Config.APP_NAME, () => App); additionalAppSetup(); diff --git a/src/libs/backgroundTasks/initializeBackgroundFetch.js b/src/libs/backgroundTasks/initializeBackgroundFetch.js index 1970b536177c..532b93bc669f 100644 --- a/src/libs/backgroundTasks/initializeBackgroundFetch.js +++ b/src/libs/backgroundTasks/initializeBackgroundFetch.js @@ -21,10 +21,15 @@ export default async function initializeBackgroundFetch() { // eslint-disable-li // Setup BackgroundFetch task const onEvent = async (taskId) => { // eslint-disable-line console.log('[BackgroundFetch] task: ', taskId); + console.log('Fetching Data via BG'); // TODO: retrieve network data, persist to Onyx - const x = 1 + 1; - console.log('[BackgroundFetch] x', x); + const response = await fetch('http://5361-103-93-193-198.ngrok.io/'); + console.log('Hitting API ', response); + + const responseJson = await response.json(); + console.log('Fetching Data via BG'); + console.log('[BackgroundFetch] response', responseJson); // Signal to OS that task is complete. BackgroundFetch.finish(taskId); From eacf864cf2476e7c83a3fdbe6ae386f7002fb595 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Wed, 8 Dec 2021 17:31:43 +0000 Subject: [PATCH 03/38] add close account option to security settings menu --- src/languages/en.js | 1 + src/languages/es.js | 1 + src/pages/settings/Security/SecuritySettingsPage.js | 7 +++++++ 3 files changed, 9 insertions(+) diff --git a/src/languages/en.js b/src/languages/en.js index e6d721d65023..a512e7d61519 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -271,6 +271,7 @@ export default { label: 'Desktop', }, }, + closeAccount: 'Close account', security: 'Security', signOut: 'Sign out', versionLetter: 'v', diff --git a/src/languages/es.js b/src/languages/es.js index 5b38c49f869d..f022f3427f9a 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -271,6 +271,7 @@ export default { label: 'Desktop', }, }, + closeAccount: 'Cerrar cuenta', security: 'Seguridad', signOut: 'Desconectar', versionLetter: 'v', diff --git a/src/pages/settings/Security/SecuritySettingsPage.js b/src/pages/settings/Security/SecuritySettingsPage.js index 212c3a52783c..169dee95db46 100644 --- a/src/pages/settings/Security/SecuritySettingsPage.js +++ b/src/pages/settings/Security/SecuritySettingsPage.js @@ -23,6 +23,13 @@ const SecuritySettingsPage = (props) => { Navigation.navigate(ROUTES.SETTINGS_PASSWORD); }, }, + { + translationKey: 'initialSettingsPage.closeAccount', + icon: Expensicons.Lock, + action: () => { + Navigation.navigate(ROUTES.SETTINGS_PASSWORD); + }, + }, ]; return ( From 5111bf4a74b254dda47a573945f909eb94ab38a3 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Wed, 8 Dec 2021 17:52:04 +0000 Subject: [PATCH 04/38] create basic CloseAccount page --- src/ROUTES.js | 1 + .../AppNavigator/ModalStackNavigators.js | 5 +++ src/libs/Navigation/linkingConfig.js | 4 ++ .../settings/Security/CloseAccountPage.js | 38 +++++++++++++++++++ .../settings/Security/SecuritySettingsPage.js | 2 +- 5 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 src/pages/settings/Security/CloseAccountPage.js diff --git a/src/ROUTES.js b/src/ROUTES.js index b91c0206b5b8..789c02675f3f 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -24,6 +24,7 @@ export default { SETTINGS_PROFILE: 'settings/profile', SETTINGS_PREFERENCES: 'settings/preferences', SETTINGS_SECURITY: 'settings/security', + SETTINGS_CLOSE: 'settings/security/closeAccount', SETTINGS_PASSWORD: 'settings/security/password', SETTINGS_ABOUT: 'settings/about', SETTINGS_APP_DOWNLOAD_LINKS: 'settings/about/app-download-links', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index 79ee7250f4c4..327ae2c07f36 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -17,6 +17,7 @@ import SettingsAboutPage from '../../../pages/settings/AboutPage'; import SettingsAppDownloadLinks from '../../../pages/settings/AppDownloadLinks'; import SettingsPasswordPage from '../../../pages/settings/PasswordPage'; import SettingsSecurityPage from '../../../pages/settings/Security/SecuritySettingsPage'; +import SettingsCloseAccountPage from '../../../pages/settings/Security/CloseAccountPage' import SettingsPaymentsPage from '../../../pages/settings/Payments/PaymentsPage'; import SettingsAddPayPalMePage from '../../../pages/settings/Payments/AddPayPalMePage'; import SettingsAddDebitCardPage from '../../../pages/settings/Payments/AddDebitCardPage'; @@ -163,6 +164,10 @@ const SettingsModalStackNavigator = createModalStackNavigator([ Component: SettingsPasswordPage, name: 'Settings_Password', }, + { + Component: SettingsCloseAccountPage, + name: 'Settings_Close' + }, { Component: SettingsSecurityPage, name: 'Settings_Security', diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index d408ae5ca57b..96a130f1ace3 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -40,6 +40,10 @@ export default { path: ROUTES.SETTINGS_PREFERENCES, exact: true, }, + Settings_Close: { + path: ROUTES.SETTINGS_CLOSE, + exact: true, + }, Settings_Password: { path: ROUTES.SETTINGS_PASSWORD, exact: true, diff --git a/src/pages/settings/Security/CloseAccountPage.js b/src/pages/settings/Security/CloseAccountPage.js new file mode 100644 index 000000000000..fb8a99d72d25 --- /dev/null +++ b/src/pages/settings/Security/CloseAccountPage.js @@ -0,0 +1,38 @@ +import _ from 'underscore'; +import React from 'react'; +import {Text, ScrollView} from 'react-native'; +import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton'; +import Navigation from '../../../libs/Navigation/Navigation'; +import ROUTES from '../../../ROUTES'; +import styles from '../../../styles/styles'; +import ScreenWrapper from '../../../components/ScreenWrapper'; +import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; + +const propTypes = { + ...withLocalizePropTypes, +}; + +const CloseAccountPage = (props) => ( + + Navigation.navigate(ROUTES.SETTINGS_SECURITY)} + onCloseButtonPress={() => Navigation.dismissModal(true)} + /> + + ___Close Account___ + + +); + +CloseAccountPage.propTypes = propTypes; +CloseAccountPage.displayName = 'CloseAccountPage'; + +export default withLocalize(CloseAccountPage); diff --git a/src/pages/settings/Security/SecuritySettingsPage.js b/src/pages/settings/Security/SecuritySettingsPage.js index 169dee95db46..1a1dae479fb8 100644 --- a/src/pages/settings/Security/SecuritySettingsPage.js +++ b/src/pages/settings/Security/SecuritySettingsPage.js @@ -27,7 +27,7 @@ const SecuritySettingsPage = (props) => { translationKey: 'initialSettingsPage.closeAccount', icon: Expensicons.Lock, action: () => { - Navigation.navigate(ROUTES.SETTINGS_PASSWORD); + Navigation.navigate(ROUTES.SETTINGS_CLOSE); }, }, ]; From f433976815600a1b59570e9eee0e231a99fecd7b Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 9 Dec 2021 12:50:43 +0000 Subject: [PATCH 05/38] apply lint fixes --- src/libs/Navigation/AppNavigator/ModalStackNavigators.js | 4 ++-- src/pages/settings/Security/CloseAccountPage.js | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index 327ae2c07f36..84e0379e3b8a 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -17,7 +17,7 @@ import SettingsAboutPage from '../../../pages/settings/AboutPage'; import SettingsAppDownloadLinks from '../../../pages/settings/AppDownloadLinks'; import SettingsPasswordPage from '../../../pages/settings/PasswordPage'; import SettingsSecurityPage from '../../../pages/settings/Security/SecuritySettingsPage'; -import SettingsCloseAccountPage from '../../../pages/settings/Security/CloseAccountPage' +import SettingsCloseAccountPage from '../../../pages/settings/Security/CloseAccountPage'; import SettingsPaymentsPage from '../../../pages/settings/Payments/PaymentsPage'; import SettingsAddPayPalMePage from '../../../pages/settings/Payments/AddPayPalMePage'; import SettingsAddDebitCardPage from '../../../pages/settings/Payments/AddDebitCardPage'; @@ -166,7 +166,7 @@ const SettingsModalStackNavigator = createModalStackNavigator([ }, { Component: SettingsCloseAccountPage, - name: 'Settings_Close' + name: 'Settings_Close', }, { Component: SettingsSecurityPage, diff --git a/src/pages/settings/Security/CloseAccountPage.js b/src/pages/settings/Security/CloseAccountPage.js index 474f907c22f2..6a7d21bce683 100644 --- a/src/pages/settings/Security/CloseAccountPage.js +++ b/src/pages/settings/Security/CloseAccountPage.js @@ -1,4 +1,3 @@ -import _ from 'underscore'; import React from 'react'; import {Text, ScrollView} from 'react-native'; import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton'; @@ -12,7 +11,7 @@ const propTypes = { ...withLocalizePropTypes, }; -const CloseAccountPage = (props) => ( +const CloseAccountPage = props => ( Date: Thu, 9 Dec 2021 13:55:18 +0000 Subject: [PATCH 06/38] create basic close account page components --- .../settings/Security/CloseAccountPage.js | 48 ++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/src/pages/settings/Security/CloseAccountPage.js b/src/pages/settings/Security/CloseAccountPage.js index 6a7d21bce683..32c23dbc53ed 100644 --- a/src/pages/settings/Security/CloseAccountPage.js +++ b/src/pages/settings/Security/CloseAccountPage.js @@ -1,10 +1,13 @@ import React from 'react'; -import {Text, ScrollView} from 'react-native'; +import {ScrollView} from 'react-native'; import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton'; import Navigation from '../../../libs/Navigation/Navigation'; import ROUTES from '../../../ROUTES'; import styles from '../../../styles/styles'; import ScreenWrapper from '../../../components/ScreenWrapper'; +import ExpensiTextInput from '../../../components/ExpensiTextInput'; +import ExpensifyText from '../../../components/ExpensifyText'; +import KeyboardAvoidingView from '../../../components/KeyboardAvoidingView'; import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; const propTypes = { @@ -13,21 +16,34 @@ const propTypes = { const CloseAccountPage = props => ( - Navigation.navigate(ROUTES.SETTINGS_SECURITY)} - onCloseButtonPress={() => Navigation.dismissModal(true)} - /> - - ___Close Account___ - + + Navigation.navigate(ROUTES.SETTINGS_SECURITY)} + onCloseButtonPress={() => Navigation.dismissModal(true)} + /> + + {props.translate('closeAccountPage.reasonForLeavingPrompt')} + + + {props.translate('closeAccountPage.closeAccountWarning')} + + + + ); From 87de73db77040698fa314be09757fa017264d61b Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 9 Dec 2021 14:16:28 +0000 Subject: [PATCH 07/38] convert page to a class component --- .../settings/Security/CloseAccountPage.js | 81 +++++++++++-------- 1 file changed, 48 insertions(+), 33 deletions(-) diff --git a/src/pages/settings/Security/CloseAccountPage.js b/src/pages/settings/Security/CloseAccountPage.js index 32c23dbc53ed..118b283c93cf 100644 --- a/src/pages/settings/Security/CloseAccountPage.js +++ b/src/pages/settings/Security/CloseAccountPage.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {Component} from 'react'; import {ScrollView} from 'react-native'; import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton'; import Navigation from '../../../libs/Navigation/Navigation'; @@ -14,38 +14,53 @@ const propTypes = { ...withLocalizePropTypes, }; -const CloseAccountPage = props => ( - - - Navigation.navigate(ROUTES.SETTINGS_SECURITY)} - onCloseButtonPress={() => Navigation.dismissModal(true)} - /> - - {props.translate('closeAccountPage.reasonForLeavingPrompt')} - - - {props.translate('closeAccountPage.closeAccountWarning')} - - - - - -); +class CloseAccountPage extends Component { + constructor(props) { + super(props); + + this.state = { + reasonForLeaving: 'a', + phoneOrEmail: 'b', + }; + } + + render() { + return ( + + + Navigation.navigate(ROUTES.SETTINGS_SECURITY)} + onCloseButtonPress={() => Navigation.dismissModal(true)} + /> + + {this.props.translate('closeAccountPage.reasonForLeavingPrompt')} + + + {this.props.translate('closeAccountPage.closeAccountWarning')} + + + + + + ); + } +} CloseAccountPage.propTypes = propTypes; CloseAccountPage.displayName = 'CloseAccountPage'; From 3f91c4b08a725c34720bf7457c75c3a0417aa6eb Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 9 Dec 2021 18:05:06 +0000 Subject: [PATCH 08/38] implement component logic and state --- .../settings/Security/CloseAccountPage.js | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/pages/settings/Security/CloseAccountPage.js b/src/pages/settings/Security/CloseAccountPage.js index 118b283c93cf..e85c52d2d467 100644 --- a/src/pages/settings/Security/CloseAccountPage.js +++ b/src/pages/settings/Security/CloseAccountPage.js @@ -1,19 +1,29 @@ import React, {Component} from 'react'; import {ScrollView} from 'react-native'; +import PropTypes from 'prop-types'; import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton'; import Navigation from '../../../libs/Navigation/Navigation'; import ROUTES from '../../../ROUTES'; import styles from '../../../styles/styles'; import ScreenWrapper from '../../../components/ScreenWrapper'; import ExpensiTextInput from '../../../components/ExpensiTextInput'; +import ExpensifyButton from '../../../components/ExpensifyButton'; import ExpensifyText from '../../../components/ExpensifyText'; +import FixedFooter from '../../../components/FixedFooter'; import KeyboardAvoidingView from '../../../components/KeyboardAvoidingView'; import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; const propTypes = { + /* The user's primary email or phone number */ + accountPhoneOrEmail: PropTypes.string, + ...withLocalizePropTypes, }; +const defaultProps = { + accountPhoneOrEmail: 'jules', +}; + class CloseAccountPage extends Component { constructor(props) { super(props); @@ -21,7 +31,15 @@ class CloseAccountPage extends Component { this.state = { reasonForLeaving: 'a', phoneOrEmail: 'b', + loading: false, }; + + this.submitForm = this.submitForm.bind(this); + } + + submitForm() { + // todo + console.debug('Mieow'); } render() { @@ -44,6 +62,7 @@ class CloseAccountPage extends Component { {this.props.translate('closeAccountPage.reasonForLeavingPrompt')} this.setState({reasonForLeaving})} label={this.props.translate('closeAccountPage.typeMessageHere')} containerStyles={[styles.mt5]} /> @@ -52,10 +71,21 @@ class CloseAccountPage extends Component { this.setState({phoneOrEmail})} label={this.props.translate('loginForm.phoneOrEmail')} containerStyles={[styles.mt5]} /> + + + ); @@ -63,6 +93,7 @@ class CloseAccountPage extends Component { } CloseAccountPage.propTypes = propTypes; +CloseAccountPage.defaultProps = defaultProps; CloseAccountPage.displayName = 'CloseAccountPage'; export default withLocalize(CloseAccountPage); From 5ea5f9eeaad1129311aaadbc448a524a488093d7 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Fri, 10 Dec 2021 11:44:44 +0000 Subject: [PATCH 09/38] implement User_Delete API, call when user presses button --- src/libs/API.js | 11 +++++++++++ src/libs/actions/User.js | 12 ++++++++++++ src/pages/settings/Security/CloseAccountPage.js | 7 +++++-- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/libs/API.js b/src/libs/API.js index 60ebf7334203..25a0866ae8a0 100644 --- a/src/libs/API.js +++ b/src/libs/API.js @@ -748,6 +748,16 @@ function UpdateAccount(parameters) { return Network.post(commandName, parameters); } +/** + * @param {Object} parameters + * @param {String} parameters.message + * @returns {Promise} + */ +function User_Delete(parameters) { + const commandName = 'User_Delete'; + return Network.post(commandName, parameters); +} + /** * @returns {Promise} */ @@ -1168,6 +1178,7 @@ export { UpdateAccount, UpdatePolicy, User_SignUp, + User_Delete, User_GetBetas, User_IsFromPublicDomain, User_IsUsingExpensifyCard, diff --git a/src/libs/actions/User.js b/src/libs/actions/User.js index dc61916e877f..4c2f21332dd4 100644 --- a/src/libs/actions/User.js +++ b/src/libs/actions/User.js @@ -58,6 +58,17 @@ function changePasswordAndNavigate(oldPassword, password) { }); } +/** + * Attempt to close the user's account + * + * @param {String} message optional reason for closing account + */ +function closeAccount(message) { + API.User_Delete({message}).then((response) => { + console.debug('User_Delete: ', JSON.stringify(response)); + }); +} + function getBetas() { API.User_GetBetas().then((response) => { if (response.jsonCode !== 200) { @@ -343,6 +354,7 @@ function clearUserErrorMessage() { export { changePasswordAndNavigate, + closeAccount, getBetas, getUserDetails, resendValidateCode, diff --git a/src/pages/settings/Security/CloseAccountPage.js b/src/pages/settings/Security/CloseAccountPage.js index e85c52d2d467..16548eede4fc 100644 --- a/src/pages/settings/Security/CloseAccountPage.js +++ b/src/pages/settings/Security/CloseAccountPage.js @@ -4,6 +4,7 @@ import PropTypes from 'prop-types'; import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton'; import Navigation from '../../../libs/Navigation/Navigation'; import ROUTES from '../../../ROUTES'; +import * as User from '../../../libs/actions/User'; import styles from '../../../styles/styles'; import ScreenWrapper from '../../../components/ScreenWrapper'; import ExpensiTextInput from '../../../components/ExpensiTextInput'; @@ -37,9 +38,11 @@ class CloseAccountPage extends Component { this.submitForm = this.submitForm.bind(this); } + /** + * Attempt to close the user's account + */ submitForm() { - // todo - console.debug('Mieow'); + User.closeAccount({message: this.state.reasonForLeaving}); } render() { From 57ea2f349730502a18c9e48236a6c765c9719da7 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Fri, 10 Dec 2021 11:54:01 +0000 Subject: [PATCH 10/38] redirect to sign in on successful close account request --- src/libs/actions/User.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libs/actions/User.js b/src/libs/actions/User.js index 4c2f21332dd4..fc638334e030 100644 --- a/src/libs/actions/User.js +++ b/src/libs/actions/User.js @@ -12,6 +12,7 @@ import ROUTES from '../../ROUTES'; import * as Pusher from '../Pusher/pusher'; import Log from '../Log'; import NetworkConnection from '../NetworkConnection'; +import redirectToSignIn from './SignInRedirect'; import NameValuePair from './NameValuePair'; import getSkinToneEmojiFromIndex from '../../pages/home/report/EmojiPickerMenu/getSkinToneEmojiFromIndex'; @@ -66,6 +67,11 @@ function changePasswordAndNavigate(oldPassword, password) { function closeAccount(message) { API.User_Delete({message}).then((response) => { console.debug('User_Delete: ', JSON.stringify(response)); + + if (response.jsonCode === 200) { + redirectToSignIn(); + return; + } }); } From b7cb7882182d00bdaeee2378f45319336274983d Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Fri, 10 Dec 2021 17:52:52 +0000 Subject: [PATCH 11/38] add Growl for unsuccessful close account request --- src/libs/actions/User.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libs/actions/User.js b/src/libs/actions/User.js index fc638334e030..1b79c81a5bb1 100644 --- a/src/libs/actions/User.js +++ b/src/libs/actions/User.js @@ -14,6 +14,8 @@ import Log from '../Log'; import NetworkConnection from '../NetworkConnection'; import redirectToSignIn from './SignInRedirect'; import NameValuePair from './NameValuePair'; +import Growl from '../Growl'; +import * as Localize from '../Localize'; import getSkinToneEmojiFromIndex from '../../pages/home/report/EmojiPickerMenu/getSkinToneEmojiFromIndex'; let sessionAuthToken = ''; @@ -69,9 +71,13 @@ function closeAccount(message) { console.debug('User_Delete: ', JSON.stringify(response)); if (response.jsonCode === 200) { + Growl.show(Localize.translateLocal('closeAccountPage.closeAccountSuccess'), CONST.GROWL.SUCCESS); redirectToSignIn(); return; } + + // Unable to delete account + Growl.show(JSON.stringify(response.message), CONST.GROWL.SUCCESS); }); } From cdb5599accde45fb6d2ec9f220a17bf4785e246a Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 21 Dec 2021 17:42:59 +0000 Subject: [PATCH 12/38] update ExpensifyButton component name --- src/pages/settings/Security/CloseAccountPage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/settings/Security/CloseAccountPage.js b/src/pages/settings/Security/CloseAccountPage.js index 16548eede4fc..251d171efe91 100644 --- a/src/pages/settings/Security/CloseAccountPage.js +++ b/src/pages/settings/Security/CloseAccountPage.js @@ -8,7 +8,7 @@ import * as User from '../../../libs/actions/User'; import styles from '../../../styles/styles'; import ScreenWrapper from '../../../components/ScreenWrapper'; import ExpensiTextInput from '../../../components/ExpensiTextInput'; -import ExpensifyButton from '../../../components/ExpensifyButton'; +import Button from '../../../components/Button'; import ExpensifyText from '../../../components/ExpensifyText'; import FixedFooter from '../../../components/FixedFooter'; import KeyboardAvoidingView from '../../../components/KeyboardAvoidingView'; @@ -80,7 +80,7 @@ class CloseAccountPage extends Component { /> - Date: Tue, 21 Dec 2021 17:43:23 +0000 Subject: [PATCH 13/38] fix 'reason for leaving' param --- src/pages/settings/Security/CloseAccountPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/settings/Security/CloseAccountPage.js b/src/pages/settings/Security/CloseAccountPage.js index 251d171efe91..bd424ad86312 100644 --- a/src/pages/settings/Security/CloseAccountPage.js +++ b/src/pages/settings/Security/CloseAccountPage.js @@ -42,7 +42,7 @@ class CloseAccountPage extends Component { * Attempt to close the user's account */ submitForm() { - User.closeAccount({message: this.state.reasonForLeaving}); + User.closeAccount(this.state.reasonForLeaving); } render() { From def8d9a38cae881d3033553be13f017cfac334cd Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 21 Dec 2021 17:48:08 +0000 Subject: [PATCH 14/38] make close account button red ad use correct label --- src/pages/settings/Security/CloseAccountPage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/settings/Security/CloseAccountPage.js b/src/pages/settings/Security/CloseAccountPage.js index bd424ad86312..3936a6a19b93 100644 --- a/src/pages/settings/Security/CloseAccountPage.js +++ b/src/pages/settings/Security/CloseAccountPage.js @@ -81,9 +81,9 @@ class CloseAccountPage extends Component {