From 3ce3814d1b60a73908acc73442d22638809a2b6b Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 2 Sep 2020 08:55:45 -0700
Subject: [PATCH 01/70] setup airship in RN, iOS, Android

---
 .../src/main/assets/airshipconfig.properties  |   5 +
 ios/AirshipConfig.plist                       |  16 ++
 ios/Podfile                                   |   2 +-
 ios/Podfile.lock                              |  29 ++-
 ios/ReactNativeChat.xcodeproj/project.pbxproj | 228 ++++++++++++++++++
 ios/ReactNativeChat/Chat.entitlements         |   8 +
 ios/ReactNativeChat/Info.plist                |  22 +-
 package-lock.json                             |   5 +
 package.json                                  |   3 +-
 9 files changed, 303 insertions(+), 15 deletions(-)
 create mode 100644 android/app/src/main/assets/airshipconfig.properties
 create mode 100644 ios/AirshipConfig.plist
 create mode 100644 ios/ReactNativeChat/Chat.entitlements

diff --git a/android/app/src/main/assets/airshipconfig.properties b/android/app/src/main/assets/airshipconfig.properties
new file mode 100644
index 000000000000..25e6fac733e5
--- /dev/null
+++ b/android/app/src/main/assets/airshipconfig.properties
@@ -0,0 +1,5 @@
+developmentAppKey = 5MOp6a0aQ6G3Xw1kKGfgrA
+developmentAppSecret = XFzdgGwRQSyellrQtYar3A
+
+productionAppKey = 4XPPo9Z4Q2KVAbIBkYP9ew
+productionAppSecret = 6aTetk0vQ2aQmGpRITwXOg
diff --git a/ios/AirshipConfig.plist b/ios/AirshipConfig.plist
new file mode 100644
index 000000000000..e7d565babcdf
--- /dev/null
+++ b/ios/AirshipConfig.plist
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>detectProvisioningMode</key>
+  <true/>
+  <key>developmentAppKey</key>
+  <string>5MOp6a0aQ6G3Xw1kKGfgrA</string>
+  <key>developmentAppSecret</key>
+  <string>XFzdgGwRQSyellrQtYar3A</string>
+  <key>productionAppKey</key>
+  <string>4XPPo9Z4Q2KVAbIBkYP9ew</string>
+  <key>productionAppSecret</key>
+  <string>6aTetk0vQ2aQmGpRITwXOg</string>
+</dict>
+</plist>
diff --git a/ios/Podfile b/ios/Podfile
index 0bc36d16e8c6..175f3f2cbc65 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -1,7 +1,7 @@
 require_relative '../node_modules/react-native/scripts/react_native_pods'
 require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
 
-platform :ios, '10.0'
+platform :ios, '11.0'
 
 target 'ReactNativeChat' do
   config = use_native_modules!
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 56c878ab2562..a3e265cc92b0 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -1,4 +1,16 @@
 PODS:
+  - Airship (13.5.4):
+    - Airship/Automation (= 13.5.4)
+    - Airship/Core (= 13.5.4)
+    - Airship/ExtendedActions (= 13.5.4)
+    - Airship/MessageCenter (= 13.5.4)
+  - Airship/Automation (13.5.4):
+    - Airship/Core
+  - Airship/Core (13.5.4)
+  - Airship/ExtendedActions (13.5.4):
+    - Airship/Core
+  - Airship/MessageCenter (13.5.4):
+    - Airship/Core
   - boost-for-react-native (1.63.0)
   - CocoaAsyncSocket (7.6.4)
   - CocoaLibEvent (1.0.0)
@@ -308,6 +320,9 @@ PODS:
     - React-jsi (= 0.63.2)
   - RNCAsyncStorage (1.11.0):
     - React
+  - urbanairship-react-native (8.1.0):
+    - Airship (= 13.5.4)
+    - React
   - Yoga (1.14.0)
   - YogaKit (1.18.1):
     - Yoga (~> 1.14)
@@ -364,10 +379,12 @@ DEPENDENCIES:
   - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
   - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
   - "RNCAsyncStorage (from `../node_modules/@react-native-community/async-storage`)"
+  - urbanairship-react-native (from `../node_modules/urbanairship-react-native`)
   - Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
 
 SPEC REPOS:
   trunk:
+    - Airship
     - boost-for-react-native
     - CocoaAsyncSocket
     - CocoaLibEvent
@@ -442,10 +459,13 @@ EXTERNAL SOURCES:
     :path: "../node_modules/react-native/ReactCommon"
   RNCAsyncStorage:
     :path: "../node_modules/@react-native-community/async-storage"
+  urbanairship-react-native:
+    :path: "../node_modules/urbanairship-react-native"
   Yoga:
     :path: "../node_modules/react-native/ReactCommon/yoga"
 
 SPEC CHECKSUMS:
+  Airship: cbbd372a4ffc8dcfeacec01fd623155ec41f33ed
   boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
   CocoaAsyncSocket: 694058e7c0ed05a9e217d1b3c7ded962f4180845
   CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f
@@ -474,8 +494,8 @@ SPEC CHECKSUMS:
   React-jsinspector: b14e62ebe7a66e9231e9581279909f2fc3db6606
   react-native-config: 9a061347e0136fdb32d43a34d60999297d672361
   react-native-netinfo: d2c312fa4b151214e1d5c8456ddb5f28ff24a576
-  react-native-safe-area-context: eb91fe1fb3f7b87d9c30a7f0808407d8569d539d
-  react-native-webview: df9ff59f76e991aefa7174831a0af48ddbac5f1b
+  react-native-safe-area-context: 0ed9288ed4409beabb0817b54efc047286fc84da
+  react-native-webview: 797f50d16bb271c4270bc742040a64c79ec7147c
   React-RCTActionSheet: 910163b6b09685a35c4ebbc52b66d1bfbbe39fc5
   React-RCTAnimation: 9a883bbe1e9d2e158d4fb53765ed64c8dc2200c6
   React-RCTBlob: 39cf0ece1927996c4466510e25d2105f67010e13
@@ -486,10 +506,11 @@ SPEC CHECKSUMS:
   React-RCTText: 1b6773e776e4b33f90468c20fe3b16ca3e224bb8
   React-RCTVibration: 4d2e726957f4087449739b595f107c0d4b6c2d2d
   ReactCommon: a0a1edbebcac5e91338371b72ffc66aa822792ce
-  RNCAsyncStorage: d059c3ee71738c39834a627476322a5a8cd5bf36
+  RNCAsyncStorage: db711e29e5e0500d9bd21aa0c2e397efa45302b1
+  urbanairship-react-native: fa123940041a6a13ab7dac192e32833c53754f00
   Yoga: 7740b94929bbacbddda59bf115b5317e9a161598
   YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
 
-PODFILE CHECKSUM: 7f6bb9c130ea13236f8249f800f75d671c1833fa
+PODFILE CHECKSUM: d03a22d8299d9564ca7fc55d0a779f6fbf0d2b37
 
 COCOAPODS: 1.9.3
diff --git a/ios/ReactNativeChat.xcodeproj/project.pbxproj b/ios/ReactNativeChat.xcodeproj/project.pbxproj
index 5c10ae04a2a8..dbca010d608f 100644
--- a/ios/ReactNativeChat.xcodeproj/project.pbxproj
+++ b/ios/ReactNativeChat.xcodeproj/project.pbxproj
@@ -18,6 +18,7 @@
 		7F7721B764D444D6861A9B39 /* GTAmericaExp-Thin.otf in Resources */ = {isa = PBXBuildFile; fileRef = A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */; };
 		81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
 		CC07911F22C2437DA6708BD2 /* GTAmericaExp-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = AE65058949E14DA5A2D5435D /* GTAmericaExp-Medium.otf */; };
+		E9D5EB0E24FF136E003E8917 /* AirshipConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9D5EB0D24FF136E003E8917 /* AirshipConfig.plist */; };
 		EB699F7A1A5F6AF391540E98 /* libPods-ReactNativeChat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7D428799B67913B9C601A158 /* libPods-ReactNativeChat.a */; };
 /* End PBXBuildFile section */
 
@@ -54,6 +55,8 @@
 		A5AAD008CBD84A6CAEB9AC97 /* GTAmericaExp-Bold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Bold.otf"; path = "../assets/fonts/GTAmericaExp-Bold.otf"; sourceTree = "<group>"; };
 		AE65058949E14DA5A2D5435D /* GTAmericaExp-Medium.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Medium.otf"; path = "../assets/fonts/GTAmericaExp-Medium.otf"; sourceTree = "<group>"; };
 		D232EFF53A6330C07E8BC0F8 /* Pods-ReactNativeChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.release.xcconfig"; sourceTree = "<group>"; };
+		E9D5EB0C24FF116D003E8917 /* Chat.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Chat.entitlements; path = ReactNativeChat/Chat.entitlements; sourceTree = "<group>"; };
+		E9D5EB0D24FF136E003E8917 /* AirshipConfig.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = AirshipConfig.plist; sourceTree = "<group>"; };
 		ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
 		ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; };
 /* End PBXFileReference section */
@@ -98,6 +101,7 @@
 		13B07FAE1A68108700A75B9A /* ReactNativeChat */ = {
 			isa = PBXGroup;
 			children = (
+				E9D5EB0C24FF116D003E8917 /* Chat.entitlements */,
 				008F07F21AC5B25A0029DE68 /* main.jsbundle */,
 				13B07FAF1A68108700A75B9A /* AppDelegate.h */,
 				13B07FB01A68108700A75B9A /* AppDelegate.m */,
@@ -105,6 +109,7 @@
 				13B07FB61A68108700A75B9A /* Info.plist */,
 				81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
 				13B07FB71A68108700A75B9A /* main.m */,
+				E9D5EB0D24FF136E003E8917 /* AirshipConfig.plist */,
 			);
 			name = ReactNativeChat;
 			sourceTree = "<group>";
@@ -273,6 +278,7 @@
 				81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
 				13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
 				11FA3546D54246CD8F819152 /* GTAmericaExp-Bold.otf in Resources */,
+				E9D5EB0E24FF136E003E8917 /* AirshipConfig.plist in Resources */,
 				49F157C845CE4D56AA72B80E /* GTAmericaExp-Light.otf in Resources */,
 				CC07911F22C2437DA6708BD2 /* GTAmericaExp-Medium.otf in Resources */,
 				4912B60FA66C4604A56AD575 /* GTAmericaExp-Regular.otf in Resources */,
@@ -348,10 +354,120 @@
 			);
 			inputPaths = (
 				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAAutomationActions.plist",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerContentView.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerView.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageButtonView.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageFullScreenViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageHTMLViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageModalViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageResizableViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAAutomation.xcdatamodeld",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/ar.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/cs.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/da.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/de.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/en.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/es-419.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/es.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/fi.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/fr.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/hi.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/hu.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/id.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/it.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/iw.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/ja.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/ko.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/ms.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/nl.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/no.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/pl.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/pt-PT.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/pt.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/ro.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/ru.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/sk.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/sv.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/th.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/tr.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/UAEvents.xcdatamodeld",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/UARemoteData.xcdatamodeld",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/vi.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/zh-Hans.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/zh-Hant.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/ios/UADefaultActions.plist",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/ios/UANativeBridge",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/ios/UANotificationCategories.plist",
+				"${PODS_ROOT}/Airship/Airship/AirshipExtendedActions/Resources/UAExtendedActions.plist",
+				"${PODS_ROOT}/Airship/Airship/AirshipExtendedActions/Resources/UARateAppPromptView.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UADefaultMessageCenterListViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UADefaultMessageCenterMessageViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UAMessageCenterActions.plist",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UAMessageCenterListCell.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UAMessageCenterListViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UAMessageCenterMessageViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UAMessageCenterPlaceholderIcon.png",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UAInbox.xcdatamodeld",
 				"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
 			);
 			name = "[CP] Copy Pods Resources";
 			outputPaths = (
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAAutomationActions.plist",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageBannerContentView.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageBannerView.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageButtonView.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageFullScreenViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageHTMLViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageModalViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageResizableViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAAutomation.momd",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ar.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/cs.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/da.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/de.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/en.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/es-419.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/es.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/fi.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/fr.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/hi.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/hu.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/id.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/it.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/iw.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ja.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ko.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ms.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/nl.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/no.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/pl.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/pt-PT.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/pt.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ro.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ru.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/sk.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/sv.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/th.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/tr.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAEvents.momd",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UARemoteData.momd",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/vi.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/zh-Hans.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/zh-Hant.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UADefaultActions.plist",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UANativeBridge",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UANotificationCategories.plist",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAExtendedActions.plist",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UARateAppPromptView.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UADefaultMessageCenterListViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UADefaultMessageCenterMessageViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAMessageCenterActions.plist",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAMessageCenterListCell.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAMessageCenterListViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAMessageCenterMessageViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAMessageCenterPlaceholderIcon.png",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInbox.momd",
 				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -366,10 +482,120 @@
 			);
 			inputPaths = (
 				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAAutomationActions.plist",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerContentView.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerView.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageButtonView.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageFullScreenViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageHTMLViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageModalViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageResizableViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAAutomation.xcdatamodeld",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/ar.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/cs.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/da.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/de.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/en.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/es-419.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/es.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/fi.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/fr.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/hi.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/hu.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/id.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/it.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/iw.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/ja.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/ko.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/ms.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/nl.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/no.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/pl.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/pt-PT.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/pt.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/ro.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/ru.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/sk.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/sv.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/th.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/tr.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/UAEvents.xcdatamodeld",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/UARemoteData.xcdatamodeld",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/vi.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/zh-Hans.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/zh-Hant.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/ios/UADefaultActions.plist",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/ios/UANativeBridge",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/ios/UANotificationCategories.plist",
+				"${PODS_ROOT}/Airship/Airship/AirshipExtendedActions/Resources/UAExtendedActions.plist",
+				"${PODS_ROOT}/Airship/Airship/AirshipExtendedActions/Resources/UARateAppPromptView.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UADefaultMessageCenterListViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UADefaultMessageCenterMessageViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UAMessageCenterActions.plist",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UAMessageCenterListCell.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UAMessageCenterListViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UAMessageCenterMessageViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UAMessageCenterPlaceholderIcon.png",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UAInbox.xcdatamodeld",
 				"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
 			);
 			name = "[CP] Copy Pods Resources";
 			outputPaths = (
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAAutomationActions.plist",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageBannerContentView.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageBannerView.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageButtonView.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageFullScreenViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageHTMLViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageModalViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageResizableViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAAutomation.momd",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ar.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/cs.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/da.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/de.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/en.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/es-419.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/es.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/fi.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/fr.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/hi.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/hu.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/id.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/it.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/iw.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ja.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ko.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ms.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/nl.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/no.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/pl.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/pt-PT.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/pt.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ro.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ru.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/sk.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/sv.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/th.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/tr.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAEvents.momd",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UARemoteData.momd",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/vi.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/zh-Hans.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/zh-Hant.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UADefaultActions.plist",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UANativeBridge",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UANotificationCategories.plist",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAExtendedActions.plist",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UARateAppPromptView.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UADefaultMessageCenterListViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UADefaultMessageCenterMessageViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAMessageCenterActions.plist",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAMessageCenterListCell.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAMessageCenterListViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAMessageCenterMessageViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAMessageCenterPlaceholderIcon.png",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInbox.momd",
 				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -478,6 +704,7 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
+				CODE_SIGN_ENTITLEMENTS = ReactNativeChat/Chat.entitlements;
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 3;
 				DEVELOPMENT_TEAM = Y823MCGK2T;
@@ -504,6 +731,7 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
+				CODE_SIGN_ENTITLEMENTS = ReactNativeChat/Chat.entitlements;
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 3;
 				DEVELOPMENT_TEAM = Y823MCGK2T;
diff --git a/ios/ReactNativeChat/Chat.entitlements b/ios/ReactNativeChat/Chat.entitlements
new file mode 100644
index 000000000000..903def2af530
--- /dev/null
+++ b/ios/ReactNativeChat/Chat.entitlements
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>aps-environment</key>
+	<string>development</string>
+</dict>
+</plist>
diff --git a/ios/ReactNativeChat/Info.plist b/ios/ReactNativeChat/Info.plist
index 8c3d3b39fc8c..d7ad268a88a6 100644
--- a/ios/ReactNativeChat/Info.plist
+++ b/ios/ReactNativeChat/Info.plist
@@ -23,33 +23,33 @@
 	<key>CFBundleVersion</key>
 	<string>21</string>
 	<key>ITSAppUsesNonExemptEncryption</key>
-	<false />
+	<false/>
 	<key>LSRequiresIPhoneOS</key>
-	<true />
+	<true/>
 	<key>NSAppTransportSecurity</key>
 	<dict>
 		<key>NSAllowsArbitraryLoads</key>
-		<true />
+		<true/>
 		<key>NSExceptionDomains</key>
 		<dict>
 			<key>localhost</key>
 			<dict>
 				<key>NSExceptionAllowsInsecureHTTPLoads</key>
-				<true />
+				<true/>
 				<key>NSIncludesSubdomains</key>
-				<true />
+				<true/>
 			</dict>
 			<key>www.expensify.com.dev</key>
 			<dict>
 				<key>NSExceptionAllowsInsecureHTTPLoads</key>
-				<true />
+				<true/>
 				<key>NSIncludesSubdomains</key>
-				<true />
+				<true/>
 			</dict>
 		</dict>
 	</dict>
 	<key>NSLocationWhenInUseUsageDescription</key>
-	<string />
+	<string></string>
 	<key>UIAppFonts</key>
 	<array>
 		<string>GTAmericaExp-Bold.otf</string>
@@ -58,6 +58,10 @@
 		<string>GTAmericaExp-Regular.otf</string>
 		<string>GTAmericaExp-Thin.otf</string>
 	</array>
+	<key>UIBackgroundModes</key>
+	<array>
+		<string>remote-notification</string>
+	</array>
 	<key>UILaunchStoryboardName</key>
 	<string>LaunchScreen</string>
 	<key>UIRequiredDeviceCapabilities</key>
@@ -71,6 +75,6 @@
 		<string>UIInterfaceOrientationLandscapeRight</string>
 	</array>
 	<key>UIViewControllerBasedStatusBarAppearance</key>
-	<false />
+	<false/>
 </dict>
 </plist>
diff --git a/package-lock.json b/package-lock.json
index fc765d1f6057..0920b4fa9bad 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17471,6 +17471,11 @@
         }
       }
     },
+    "urbanairship-react-native": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/urbanairship-react-native/-/urbanairship-react-native-8.1.0.tgz",
+      "integrity": "sha512-iyw32YwoXz7lrng2W5D+l+S4tgGx64Mi4h9qtQQLXyjXDZMtt73T8Sv6+W4KxyHq+YfbkAlWhYDCaTsN33dq/w=="
+    },
     "uri-js": {
       "version": "4.2.2",
       "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
diff --git a/package.json b/package.json
index 8376c9503c7b..d6b3ac9ffcab 100644
--- a/package.json
+++ b/package.json
@@ -51,7 +51,8 @@
     "react-router-native": "^5.2.0",
     "react-web-config": "^1.0.0",
     "save": "^2.4.0",
-    "underscore": "^1.10.2"
+    "underscore": "^1.10.2",
+    "urbanairship-react-native": "^8.1.0"
   },
   "devDependencies": {
     "@babel/core": "^7.11.1",

From deab1099107c5a264967d8b1b4faa15d1100478a Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Tue, 8 Sep 2020 12:50:26 -0700
Subject: [PATCH 02/70] port code from BrowserNotifications to
 Notification/index.js

---
 src/lib/Notification/BrowserNotifications.js | 126 -------------------
 src/lib/Notification/index.js                | 123 +++++++++++++++++-
 2 files changed, 121 insertions(+), 128 deletions(-)
 delete mode 100644 src/lib/Notification/BrowserNotifications.js

diff --git a/src/lib/Notification/BrowserNotifications.js b/src/lib/Notification/BrowserNotifications.js
deleted file mode 100644
index 22f3dd8f7c6e..000000000000
--- a/src/lib/Notification/BrowserNotifications.js
+++ /dev/null
@@ -1,126 +0,0 @@
-// Web and desktop implementation only. Do not import for direct use. Use Notification.
-
-import Str from '../Str';
-import CONST from '../../CONST';
-import * as ActiveClientManager from '../ActiveClientManager';
-
-const EXPENSIFY_ICON_URL = `${CONST.CLOUDFRONT_URL}/images/favicon-2019.png`;
-const DEFAULT_DELAY = 4000;
-
-/**
- * Checks if the user has granted permission to show browser notifications
- *
- * @return {Promise}
- */
-function canUseBrowserNotifications() {
-    return new Promise((resolve) => {
-        // They have no browser notifications so we can't use this feature
-        if (!window.Notification) {
-            return resolve(false);
-        }
-
-        // Check if they previously granted or denied us access to send a notification
-        const permissionGranted = Notification.permission === 'granted';
-
-        if (permissionGranted || Notification.permisson === 'denied') {
-            return resolve(permissionGranted);
-        }
-
-        // Check their global preferences for browser notifications and ask permission if they have none
-        Notification.requestPermission()
-            .then((status) => {
-                resolve(status === 'granted');
-            });
-    });
-}
-
-/**
- * Light abstraction around browser push notifications.
- * Checks for permission before determining whether to send.
- *
- * @param {Object} params
- * @param {String} params.title
- * @param {String} params.body
- * @param {String} [params.icon] Default to Expensify logo
- * @param {Number} [params.delay]
- * @param {Function} [params.onClick]
- * @param {String} [params.tag]
- *
- * @return {Promise} - resolves with Notification object or undefined
- */
-function push({
-    title,
-    body,
-    delay = DEFAULT_DELAY,
-    onClick = () => {},
-    tag = '',
-    icon = EXPENSIFY_ICON_URL,
-}) {
-    return new Promise((resolve) => {
-        if (!title || !body) {
-            throw new Error('BrowserNotification must include title and body parameter.');
-        }
-
-        canUseBrowserNotifications().then((canUseNotifications) => {
-            if (!canUseNotifications) {
-                resolve();
-                return;
-            }
-
-            const notification = new Notification(title, {
-                body,
-                icon,
-                tag,
-            });
-
-            // If we pass in a delay param greater than 0 the notification
-            // will auto-close after the specified time.
-            if (delay > 0) {
-                setTimeout(notification.close.bind(notification), delay);
-            }
-
-            notification.onclick = (event) => {
-                event.preventDefault();
-                onClick();
-                window.parent.focus();
-                window.focus();
-                notification.close();
-            };
-
-            resolve(notification);
-        });
-    });
-}
-
-/**
- * BrowserNotification
- * @namespace
- */
-export default {
-    /**
-     * Create a report comment notification
-     *
-     * @param {Object} params
-     * @param {Object} params.reportAction
-     * @param {Function} params.onClick
-     */
-    pushReportCommentNotification({reportAction, onClick}) {
-        if (!ActiveClientManager.isClientTheLeader()) {
-            return;
-        }
-
-        const {person, message} = reportAction;
-
-        const plainTextPerson = Str.htmlDecode(person.map(f => f.text).join());
-
-        // Specifically target the comment part of the message
-        const plainTextMessage = Str.htmlDecode((message.find(f => f.type === 'COMMENT') || {}).text);
-
-        push({
-            title: `New message from ${plainTextPerson}`,
-            body: plainTextMessage,
-            delay: 0,
-            onClick,
-        });
-    },
-};
diff --git a/src/lib/Notification/index.js b/src/lib/Notification/index.js
index 356d9749b684..ba6505c0a5ee 100644
--- a/src/lib/Notification/index.js
+++ b/src/lib/Notification/index.js
@@ -1,7 +1,126 @@
-import BrowserNotifications from './BrowserNotifications';
+// Web and desktop implementation only
+
+import Str from '../Str';
+import CONST from '../../CONST';
+import * as ActiveClientManager from '../ActiveClientManager';
+
+const EXPENSIFY_ICON_URL = `${CONST.CLOUDFRONT_URL}/images/favicon-2019.png`;
+const DEFAULT_DELAY = 4000;
+
+/**
+ * Checks if the user has granted permission to show browser notifications
+ *
+ * @return {Promise}
+ */
+function canUseBrowserNotifications() {
+    return new Promise((resolve) => {
+        // They have no browser notifications so we can't use this feature
+        if (!window.Notification) {
+            return resolve(false);
+        }
+
+        // Check if they previously granted or denied us access to send a notification
+        const permissionGranted = Notification.permission === 'granted';
+
+        if (permissionGranted || Notification.permisson === 'denied') {
+            return resolve(permissionGranted);
+        }
+
+        // Check their global preferences for browser notifications and ask permission if they have none
+        Notification.requestPermission()
+            .then((status) => {
+                resolve(status === 'granted');
+            });
+    });
+}
+
+/**
+ * Light abstraction around browser push notifications.
+ * Checks for permission before determining whether to send.
+ *
+ * @param {Object} params
+ * @param {String} params.title
+ * @param {String} params.body
+ * @param {String} [params.icon] Default to Expensify logo
+ * @param {Number} [params.delay]
+ * @param {Function} [params.onClick]
+ * @param {String} [params.tag]
+ *
+ * @return {Promise} - resolves with Notification object or undefined
+ */
+function push({
+    title,
+    body,
+    delay = DEFAULT_DELAY,
+    onClick = () => {},
+    tag = '',
+    icon = EXPENSIFY_ICON_URL,
+}) {
+    return new Promise((resolve) => {
+        if (!title || !body) {
+            throw new Error('BrowserNotification must include title and body parameter.');
+        }
+
+        canUseBrowserNotifications().then((canUseNotifications) => {
+            if (!canUseNotifications) {
+                resolve();
+                return;
+            }
+
+            const notification = new Notification(title, {
+                body,
+                icon,
+                tag,
+            });
+
+            // If we pass in a delay param greater than 0 the notification
+            // will auto-close after the specified time.
+            if (delay > 0) {
+                setTimeout(notification.close.bind(notification), delay);
+            }
+
+            notification.onclick = (event) => {
+                event.preventDefault();
+                onClick();
+                window.parent.focus();
+                window.focus();
+                notification.close();
+            };
+
+            resolve(notification);
+        });
+    });
+}
+
+/**
+ * Create a report comment notification
+ *
+ * @param {Object} params
+ * @param {Object} params.reportAction
+ * @param {Function} params.onClick
+ */
+function pushReportCommentNotification({reportAction, onClick}) {
+    if (!ActiveClientManager.isClientTheLeader()) {
+        return;
+    }
+
+    const {person, message} = reportAction;
+
+    const plainTextPerson = Str.htmlDecode(person.map(f => f.text).join());
+
+    // Specifically target the comment part of the message
+    const plainTextMessage = Str.htmlDecode((message.find(f => f.type === 'COMMENT') || {}).text);
+
+    push({
+        title: `New message from ${plainTextPerson}`,
+        body: plainTextMessage,
+        delay: 0,
+        onClick,
+    });
+}
 
 function showCommentNotification({reportAction, onClick}) {
-    BrowserNotifications.pushReportCommentNotification({reportAction, onClick});
+    pushReportCommentNotification({reportAction, onClick});
 }
 
 export default {

From 9ac89a5133cd1f1dc79d417598066f2a5157e11b Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Tue, 8 Sep 2020 13:48:49 -0700
Subject: [PATCH 03/70] export enableUserNotifications and call it in
 Expensify::componentDidMount

---
 src/Expensify.js                     |  4 +++-
 src/lib/Notification/index.js        | 15 +++++++++++++++
 src/lib/Notification/index.native.js | 21 +++++++++++++++++++--
 3 files changed, 37 insertions(+), 3 deletions(-)

diff --git a/src/Expensify.js b/src/Expensify.js
index 8fdbfc167ee5..a84c6258ef3f 100644
--- a/src/Expensify.js
+++ b/src/Expensify.js
@@ -10,6 +10,7 @@ import * as ActiveClientManager from './lib/ActiveClientManager';
 import IONKEYS from './IONKEYS';
 import withIon from './components/withIon';
 import styles from './style/StyleSheet';
+import Notification from './lib/Notification';
 
 import {
     Route,
@@ -52,7 +53,8 @@ class Expensify extends Component {
         Ion.get(IONKEYS.SESSION, 'authToken', '')
             .then((authToken) => {
                 this.setState({loading: false, authToken});
-            });
+            })
+            .then(() => Notification.enableUserNotifications());
     }
 
     /**
diff --git a/src/lib/Notification/index.js b/src/lib/Notification/index.js
index ba6505c0a5ee..4a3fc9ac6718 100644
--- a/src/lib/Notification/index.js
+++ b/src/lib/Notification/index.js
@@ -7,6 +7,8 @@ import * as ActiveClientManager from '../ActiveClientManager';
 const EXPENSIFY_ICON_URL = `${CONST.CLOUDFRONT_URL}/images/favicon-2019.png`;
 const DEFAULT_DELAY = 4000;
 
+/* ====== Private Functions ====== */
+
 /**
  * Checks if the user has granted permission to show browser notifications
  *
@@ -119,10 +121,23 @@ function pushReportCommentNotification({reportAction, onClick}) {
     });
 }
 
+/* ====== Public Functions (signatures must match index.native.js) ====== */
+
+/**
+ * See if user notification permissions are set, and ask for permission if not set.
+ *
+ * @returns {Promise}
+ */
+function enableUserNotifications() {
+    return canUseBrowserNotifications();
+}
+
+
 function showCommentNotification({reportAction, onClick}) {
     pushReportCommentNotification({reportAction, onClick});
 }
 
 export default {
+    enableUserNotifications,
     showCommentNotification,
 };
diff --git a/src/lib/Notification/index.native.js b/src/lib/Notification/index.native.js
index e1b798612c61..8c13e868bfe2 100644
--- a/src/lib/Notification/index.native.js
+++ b/src/lib/Notification/index.native.js
@@ -1,4 +1,21 @@
-// Browser Notifications are not supported on mobile so we'll just noop here.
+import {UrbanAirship} from 'urbanairship-react-native';
+
+/* ====== Public Functions (signatures must match index.js) ====== */
+
+/**
+ * See if user notification permissions are set, and ask for permission if not set.
+ *
+ * @returns {Promise}
+ */
+function enableUserNotifications() {
+    return UrbanAirship.enableUserPushNotifications();
+}
+
+function showCommentNotification({reportAction, onClick}) {
+    // TODO: show "snackbar"/GROWL notification
+}
+
 export default {
-    showCommentNotification: () => {},
+    enableUserNotifications,
+    showCommentNotification,
 };

From f4b3617436ae95be047dbb8e43cb9b909bdbcd08 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Tue, 8 Sep 2020 14:35:46 -0700
Subject: [PATCH 04/70] isClientTheLeader is a promise

---
 src/lib/Notification/index.js | 29 ++++++++++++++++-------------
 1 file changed, 16 insertions(+), 13 deletions(-)

diff --git a/src/lib/Notification/index.js b/src/lib/Notification/index.js
index 4a3fc9ac6718..bc42ced6c402 100644
--- a/src/lib/Notification/index.js
+++ b/src/lib/Notification/index.js
@@ -102,23 +102,26 @@ function push({
  * @param {Function} params.onClick
  */
 function pushReportCommentNotification({reportAction, onClick}) {
-    if (!ActiveClientManager.isClientTheLeader()) {
-        return;
-    }
+    ActiveClientManager.isClientTheLeader()
+        .then((isClientTheLeader) => {
+            if (!isClientTheLeader) {
+                return;
+            }
 
-    const {person, message} = reportAction;
+            const {person, message} = reportAction;
 
-    const plainTextPerson = Str.htmlDecode(person.map(f => f.text).join());
+            const plainTextPerson = Str.htmlDecode(person.map(f => f.text).join());
 
-    // Specifically target the comment part of the message
-    const plainTextMessage = Str.htmlDecode((message.find(f => f.type === 'COMMENT') || {}).text);
+            // Specifically target the comment part of the message
+            const plainTextMessage = Str.htmlDecode((message.find(f => f.type === 'COMMENT') || {}).text);
 
-    push({
-        title: `New message from ${plainTextPerson}`,
-        body: plainTextMessage,
-        delay: 0,
-        onClick,
-    });
+            push({
+                title: `New message from ${plainTextPerson}`,
+                body: plainTextMessage,
+                delay: 0,
+                onClick,
+            });
+        });
 }
 
 /* ====== Public Functions (signatures must match index.native.js) ====== */

From 7714a1c049192b936d71d9bc1788a7863a00aa2c Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 16 Sep 2020 11:38:57 -0700
Subject: [PATCH 05/70] revert react-native-notification libary add and remove
 blank file

---
 src/lib/Notification/BrowserNotifications.js | 0
 src/lib/Notification/index.js                | 2 --
 2 files changed, 2 deletions(-)
 delete mode 100644 src/lib/Notification/BrowserNotifications.js

diff --git a/src/lib/Notification/BrowserNotifications.js b/src/lib/Notification/BrowserNotifications.js
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/src/lib/Notification/index.js b/src/lib/Notification/index.js
index bc42ced6c402..fe5f056c2380 100644
--- a/src/lib/Notification/index.js
+++ b/src/lib/Notification/index.js
@@ -1,5 +1,3 @@
-// Web and desktop implementation only
-
 import Str from '../Str';
 import CONST from '../../CONST';
 import * as ActiveClientManager from '../ActiveClientManager';

From 9dbe08e6e0084526ee37327c03fb59606c49994f Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 16 Sep 2020 13:28:53 -0700
Subject: [PATCH 06/70] update browser notifications to match master

---
 src/lib/Notification/index.js | 37 ++++++++++++++++-------------------
 1 file changed, 17 insertions(+), 20 deletions(-)

diff --git a/src/lib/Notification/index.js b/src/lib/Notification/index.js
index fe5f056c2380..376c57f0b9b8 100644
--- a/src/lib/Notification/index.js
+++ b/src/lib/Notification/index.js
@@ -100,26 +100,23 @@ function push({
  * @param {Function} params.onClick
  */
 function pushReportCommentNotification({reportAction, onClick}) {
-    ActiveClientManager.isClientTheLeader()
-        .then((isClientTheLeader) => {
-            if (!isClientTheLeader) {
-                return;
-            }
-
-            const {person, message} = reportAction;
-
-            const plainTextPerson = Str.htmlDecode(person.map(f => f.text).join());
-
-            // Specifically target the comment part of the message
-            const plainTextMessage = Str.htmlDecode((message.find(f => f.type === 'COMMENT') || {}).text);
-
-            push({
-                title: `New message from ${plainTextPerson}`,
-                body: plainTextMessage,
-                delay: 0,
-                onClick,
-            });
-        });
+    if (!ActiveClientManager.isClientTheLeader()) {
+        console.debug('[BrowserNotifications] Skipping notification because this client is not the leader');
+        return;
+    }
+
+    const {person, message} = reportAction;
+    const plainTextPerson = Str.htmlDecode(person.map(f => f.text).join());
+
+    // Specifically target the comment part of the message
+    const plainTextMessage = Str.htmlDecode((message.find(f => f.type === 'COMMENT') || {}).text);
+
+    push({
+        title: `New message from ${plainTextPerson}`,
+        body: plainTextMessage,
+        delay: 0,
+        onClick,
+    });
 }
 
 /* ====== Public Functions (signatures must match index.native.js) ====== */

From 4a98da3cb1992f17ad2794b0d1ef614b7dc6d1ee Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Thu, 17 Sep 2020 11:39:50 -0700
Subject: [PATCH 07/70] got it running on a physical device

---
 ios/AirshipConfig.plist                       | 20 +++++++++----------
 ios/ReactNativeChat.xcodeproj/project.pbxproj | 20 ++++++++++---------
 2 files changed, 21 insertions(+), 19 deletions(-)

diff --git a/ios/AirshipConfig.plist b/ios/AirshipConfig.plist
index e7d565babcdf..3502dc33584f 100644
--- a/ios/AirshipConfig.plist
+++ b/ios/AirshipConfig.plist
@@ -2,15 +2,15 @@
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
-  <key>detectProvisioningMode</key>
-  <true/>
-  <key>developmentAppKey</key>
-  <string>5MOp6a0aQ6G3Xw1kKGfgrA</string>
-  <key>developmentAppSecret</key>
-  <string>XFzdgGwRQSyellrQtYar3A</string>
-  <key>productionAppKey</key>
-  <string>4XPPo9Z4Q2KVAbIBkYP9ew</string>
-  <key>productionAppSecret</key>
-  <string>6aTetk0vQ2aQmGpRITwXOg</string>
+	<key>detectProvisioningMode</key>
+	<true/>
+	<key>developmentAppKey</key>
+	<string>uulSSfTDQJ2r0PMpjRrhmQ</string>
+	<key>developmentAppSecret</key>
+	<string>D4Bhf0HrQEehrPua74Tyiw</string>
+	<key>productionAppKey</key>
+	<string>55vypj0ARc6cN09MX7ogtQ</string>
+	<key>productionAppSecret</key>
+	<string>EsSaqbdLSvmyC6kSBFJCtQ</string>
 </dict>
 </plist>
diff --git a/ios/ReactNativeChat.xcodeproj/project.pbxproj b/ios/ReactNativeChat.xcodeproj/project.pbxproj
index 4023de37fbd5..c57a8fc531d3 100644
--- a/ios/ReactNativeChat.xcodeproj/project.pbxproj
+++ b/ios/ReactNativeChat.xcodeproj/project.pbxproj
@@ -240,7 +240,7 @@
 					13B07F861A680F5B00A75B9A = {
 						DevelopmentTeam = 368M544MTT;
 						LastSwiftMigration = 1120;
-						ProvisioningStyle = Manual;
+						ProvisioningStyle = Automatic;
 					};
 				};
 			};
@@ -704,13 +704,14 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
-				CODE_SIGN_IDENTITY = "iPhone Distribution";
-				CODE_SIGN_STYLE = Manual;
 				CODE_SIGN_ENTITLEMENTS = ReactNativeChat/Chat.entitlements;
+				CODE_SIGN_IDENTITY = "Apple Development";
+				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 3;
-				DEVELOPMENT_TEAM = 368M544MTT;
+				DEVELOPMENT_TEAM = "";
 				ENABLE_BITCODE = NO;
 				INFOPLIST_FILE = ReactNativeChat/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
 				MARKETING_VERSION = 1.0.0;
 				OTHER_LDFLAGS = (
@@ -720,7 +721,7 @@
 				);
 				PRODUCT_BUNDLE_IDENTIFIER = com.chat.expensify.chat;
 				PRODUCT_NAME = Chat;
-				PROVISIONING_PROFILE_SPECIFIER = chat_expensify_appstore;
+				PROVISIONING_PROFILE_SPECIFIER = "";
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
@@ -735,11 +736,12 @@
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_ENTITLEMENTS = ReactNativeChat/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 = "";
 				INFOPLIST_FILE = ReactNativeChat/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
 				MARKETING_VERSION = 1.0.0;
 				OTHER_LDFLAGS = (
@@ -749,7 +751,7 @@
 				);
 				PRODUCT_BUNDLE_IDENTIFIER = com.chat.expensify.chat;
 				PRODUCT_NAME = Chat;
-				PROVISIONING_PROFILE_SPECIFIER = chat_expensify_appstore;
+				PROVISIONING_PROFILE_SPECIFIER = "";
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
 				VERSIONING_SYSTEM = "apple-generic";

From 1572e4d737e604412c3189caada7d6eb3394c27e Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Tue, 22 Sep 2020 11:52:43 -0700
Subject: [PATCH 08/70] present notifications in the foreground on iOS

---
 ios/Podfile                          | 2 +-
 src/lib/Notification/index.native.js | 7 ++++++-
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/ios/Podfile b/ios/Podfile
index 175f3f2cbc65..0bc36d16e8c6 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -1,7 +1,7 @@
 require_relative '../node_modules/react-native/scripts/react_native_pods'
 require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
 
-platform :ios, '11.0'
+platform :ios, '10.0'
 
 target 'ReactNativeChat' do
   config = use_native_modules!
diff --git a/src/lib/Notification/index.native.js b/src/lib/Notification/index.native.js
index 8c13e868bfe2..18b9ef1eaa49 100644
--- a/src/lib/Notification/index.native.js
+++ b/src/lib/Notification/index.native.js
@@ -1,4 +1,8 @@
-import {UrbanAirship} from 'urbanairship-react-native';
+import {UrbanAirship, NotificationOptionsIOS} from 'urbanairship-react-native';
+import _ from 'underscore';
+
+// By default, we set all foreground push notification options to true (only relevant to iOS)
+const foregroundPresentationOptions = _.mapObject(_.invert(NotificationOptionsIOS), () => true);
 
 /* ====== Public Functions (signatures must match index.js) ====== */
 
@@ -8,6 +12,7 @@ import {UrbanAirship} from 'urbanairship-react-native';
  * @returns {Promise}
  */
 function enableUserNotifications() {
+    UrbanAirship.setForegroundPresentationOptions(foregroundPresentationOptions);
     return UrbanAirship.enableUserPushNotifications();
 }
 

From 4bae6371c7242f85fad69f096a34f8818ae0da13 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Thu, 24 Sep 2020 10:48:32 -0700
Subject: [PATCH 09/70] bump deployment target back to iOS 11, it was necessary

---
 ios/Podfile                                   |   2 +-
 ios/Podfile.lock                              |  10 +-
 ios/ReactNativeChat.xcodeproj/project.pbxproj | 114 +++++++++---------
 3 files changed, 63 insertions(+), 63 deletions(-)

diff --git a/ios/Podfile b/ios/Podfile
index 0bc36d16e8c6..175f3f2cbc65 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -1,7 +1,7 @@
 require_relative '../node_modules/react-native/scripts/react_native_pods'
 require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
 
-platform :ios, '10.0'
+platform :ios, '11.0'
 
 target 'ReactNativeChat' do
   config = use_native_modules!
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index a3e265cc92b0..20aefa622226 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -493,9 +493,9 @@ SPEC CHECKSUMS:
   React-jsiexecutor: 8ca588cc921e70590820ce72b8789b02c67cce38
   React-jsinspector: b14e62ebe7a66e9231e9581279909f2fc3db6606
   react-native-config: 9a061347e0136fdb32d43a34d60999297d672361
-  react-native-netinfo: d2c312fa4b151214e1d5c8456ddb5f28ff24a576
-  react-native-safe-area-context: 0ed9288ed4409beabb0817b54efc047286fc84da
-  react-native-webview: 797f50d16bb271c4270bc742040a64c79ec7147c
+  react-native-netinfo: 38a1e0b3e89b48e4b5833ed475d8df5f049f6f90
+  react-native-safe-area-context: eb91fe1fb3f7b87d9c30a7f0808407d8569d539d
+  react-native-webview: df9ff59f76e991aefa7174831a0af48ddbac5f1b
   React-RCTActionSheet: 910163b6b09685a35c4ebbc52b66d1bfbbe39fc5
   React-RCTAnimation: 9a883bbe1e9d2e158d4fb53765ed64c8dc2200c6
   React-RCTBlob: 39cf0ece1927996c4466510e25d2105f67010e13
@@ -506,8 +506,8 @@ SPEC CHECKSUMS:
   React-RCTText: 1b6773e776e4b33f90468c20fe3b16ca3e224bb8
   React-RCTVibration: 4d2e726957f4087449739b595f107c0d4b6c2d2d
   ReactCommon: a0a1edbebcac5e91338371b72ffc66aa822792ce
-  RNCAsyncStorage: db711e29e5e0500d9bd21aa0c2e397efa45302b1
-  urbanairship-react-native: fa123940041a6a13ab7dac192e32833c53754f00
+  RNCAsyncStorage: d059c3ee71738c39834a627476322a5a8cd5bf36
+  urbanairship-react-native: 87a82abfbc182a854f66eb7fd25198ce62e95a54
   Yoga: 7740b94929bbacbddda59bf115b5317e9a161598
   YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
 
diff --git a/ios/ReactNativeChat.xcodeproj/project.pbxproj b/ios/ReactNativeChat.xcodeproj/project.pbxproj
index c57a8fc531d3..e50a6046a870 100644
--- a/ios/ReactNativeChat.xcodeproj/project.pbxproj
+++ b/ios/ReactNativeChat.xcodeproj/project.pbxproj
@@ -12,14 +12,14 @@
 		13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
 		13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
 		13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
-		46C9221F3EA3DD84BD134825 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 074C00650065A406EE912586 /* libPods-ReactNativeChat-ReactNativeChatTests.a */; };
+		3839692A1DFA39E5C949EF81 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5454FE20DD9F0241A833767D /* libPods-ReactNativeChat-ReactNativeChatTests.a */; };
 		4912B60FA66C4604A56AD575 /* GTAmericaExp-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 8C7003903C1E4957824899BB /* GTAmericaExp-Regular.otf */; };
 		49F157C845CE4D56AA72B80E /* GTAmericaExp-Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = 67D5C3A6A7FA417C8A853FC1 /* GTAmericaExp-Light.otf */; };
+		4DD473EE693CF831650829C4 /* libPods-ReactNativeChat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6C81185C23FEA09F54BF32FA /* libPods-ReactNativeChat.a */; };
 		7F7721B764D444D6861A9B39 /* GTAmericaExp-Thin.otf in Resources */ = {isa = PBXBuildFile; fileRef = A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */; };
 		81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
 		CC07911F22C2437DA6708BD2 /* GTAmericaExp-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = AE65058949E14DA5A2D5435D /* GTAmericaExp-Medium.otf */; };
 		E9D5EB0E24FF136E003E8917 /* AirshipConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9D5EB0D24FF136E003E8917 /* AirshipConfig.plist */; };
-		EB699F7A1A5F6AF391540E98 /* libPods-ReactNativeChat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7D428799B67913B9C601A158 /* libPods-ReactNativeChat.a */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -37,28 +37,28 @@
 		00E356EE1AD99517003FC87E /* ReactNativeChatTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactNativeChatTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		00E356F21AD99517003FC87E /* ReactNativeChatTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReactNativeChatTests.m; sourceTree = "<group>"; };
-		05C75E1764EA836C54E53E91 /* Pods-ReactNativeChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.debug.xcconfig"; sourceTree = "<group>"; };
-		074C00650065A406EE912586 /* libPods-ReactNativeChat-ReactNativeChatTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat-ReactNativeChatTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		13B07F961A680F5B00A75B9A /* Chat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Chat.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ReactNativeChat/AppDelegate.h; sourceTree = "<group>"; };
 		13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = ReactNativeChat/AppDelegate.m; sourceTree = "<group>"; };
 		13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = ReactNativeChat/Images.xcassets; sourceTree = "<group>"; };
 		13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ReactNativeChat/Info.plist; sourceTree = "<group>"; };
 		13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ReactNativeChat/main.m; sourceTree = "<group>"; };
-		45D4E4039B764281ECADF639 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; sourceTree = "<group>"; };
-		4DAAA8DFB4A4EF50B076BE3D /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; sourceTree = "<group>"; };
+		5454FE20DD9F0241A833767D /* libPods-ReactNativeChat-ReactNativeChatTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat-ReactNativeChatTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		67D5C3A6A7FA417C8A853FC1 /* GTAmericaExp-Light.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Light.otf"; path = "../assets/fonts/GTAmericaExp-Light.otf"; sourceTree = "<group>"; };
-		7D428799B67913B9C601A158 /* libPods-ReactNativeChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		6C81185C23FEA09F54BF32FA /* libPods-ReactNativeChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		7FC84F0F8F1E4ABFF9DBB4C1 /* Pods-ReactNativeChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.release.xcconfig"; sourceTree = "<group>"; };
 		81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = ReactNativeChat/LaunchScreen.storyboard; sourceTree = "<group>"; };
 		8C7003903C1E4957824899BB /* GTAmericaExp-Regular.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Regular.otf"; path = "../assets/fonts/GTAmericaExp-Regular.otf"; sourceTree = "<group>"; };
 		A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Thin.otf"; path = "../assets/fonts/GTAmericaExp-Thin.otf"; sourceTree = "<group>"; };
 		A5AAD008CBD84A6CAEB9AC97 /* GTAmericaExp-Bold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Bold.otf"; path = "../assets/fonts/GTAmericaExp-Bold.otf"; sourceTree = "<group>"; };
 		AE65058949E14DA5A2D5435D /* GTAmericaExp-Medium.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Medium.otf"; path = "../assets/fonts/GTAmericaExp-Medium.otf"; sourceTree = "<group>"; };
-		D232EFF53A6330C07E8BC0F8 /* Pods-ReactNativeChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.release.xcconfig"; sourceTree = "<group>"; };
+		C22C74DA9BEE32E2C61363D3 /* Pods-ReactNativeChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.debug.xcconfig"; sourceTree = "<group>"; };
+		D65289D5EBFF81AA41B765AF /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; sourceTree = "<group>"; };
 		E9D5EB0C24FF116D003E8917 /* Chat.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Chat.entitlements; path = ReactNativeChat/Chat.entitlements; sourceTree = "<group>"; };
 		E9D5EB0D24FF136E003E8917 /* AirshipConfig.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = AirshipConfig.plist; sourceTree = "<group>"; };
 		ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
 		ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; };
+		F4C3A250E339EC05E79EAD6F /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -66,7 +66,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				46C9221F3EA3DD84BD134825 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */,
+				3839692A1DFA39E5C949EF81 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -74,7 +74,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				EB699F7A1A5F6AF391540E98 /* libPods-ReactNativeChat.a in Frameworks */,
+				4DD473EE693CF831650829C4 /* libPods-ReactNativeChat.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -119,8 +119,8 @@
 			children = (
 				ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
 				ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
-				7D428799B67913B9C601A158 /* libPods-ReactNativeChat.a */,
-				074C00650065A406EE912586 /* libPods-ReactNativeChat-ReactNativeChatTests.a */,
+				6C81185C23FEA09F54BF32FA /* libPods-ReactNativeChat.a */,
+				5454FE20DD9F0241A833767D /* libPods-ReactNativeChat-ReactNativeChatTests.a */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
@@ -172,10 +172,10 @@
 		EC29677F0A49C2946A495A33 /* Pods */ = {
 			isa = PBXGroup;
 			children = (
-				05C75E1764EA836C54E53E91 /* Pods-ReactNativeChat.debug.xcconfig */,
-				D232EFF53A6330C07E8BC0F8 /* Pods-ReactNativeChat.release.xcconfig */,
-				4DAAA8DFB4A4EF50B076BE3D /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */,
-				45D4E4039B764281ECADF639 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */,
+				C22C74DA9BEE32E2C61363D3 /* Pods-ReactNativeChat.debug.xcconfig */,
+				7FC84F0F8F1E4ABFF9DBB4C1 /* Pods-ReactNativeChat.release.xcconfig */,
+				F4C3A250E339EC05E79EAD6F /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */,
+				D65289D5EBFF81AA41B765AF /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */,
 			);
 			path = Pods;
 			sourceTree = "<group>";
@@ -187,11 +187,11 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "ReactNativeChatTests" */;
 			buildPhases = (
-				19A04238C3D2F6F09678CFA0 /* [CP] Check Pods Manifest.lock */,
+				C3A02165469B388F253FA6CD /* [CP] Check Pods Manifest.lock */,
 				00E356EA1AD99517003FC87E /* Sources */,
 				00E356EB1AD99517003FC87E /* Frameworks */,
 				00E356EC1AD99517003FC87E /* Resources */,
-				ABE62262A44D31B78B23818B /* [CP] Copy Pods Resources */,
+				49E28E3419E3718BB2C96C89 /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -207,13 +207,13 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ReactNativeChat" */;
 			buildPhases = (
-				387B40C2520CE2773517F705 /* [CP] Check Pods Manifest.lock */,
+				0AEF09139365285D56E829A2 /* [CP] Check Pods Manifest.lock */,
 				FD10A7F022414F080027D42C /* Start Packager */,
 				13B07F871A680F5B00A75B9A /* Sources */,
 				13B07F8C1A680F5B00A75B9A /* Frameworks */,
 				13B07F8E1A680F5B00A75B9A /* Resources */,
 				00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
-				EC2DD7546E5E118A6D3073B2 /* [CP] Copy Pods Resources */,
+				1CA26990DE02013A9C3C5F31 /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -303,29 +303,7 @@
 			shellPath = /bin/sh;
 			shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh";
 		};
-		19A04238C3D2F6F09678CFA0 /* [CP] Check Pods Manifest.lock */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputFileListPaths = (
-			);
-			inputPaths = (
-				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
-				"${PODS_ROOT}/Manifest.lock",
-			);
-			name = "[CP] Check Pods Manifest.lock";
-			outputFileListPaths = (
-			);
-			outputPaths = (
-				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-ReactNativeChatTests-checkManifestLockResult.txt",
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
-			showEnvVarsInLog = 0;
-		};
-		387B40C2520CE2773517F705 /* [CP] Check Pods Manifest.lock */ = {
+		0AEF09139365285D56E829A2 /* [CP] Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -347,13 +325,13 @@
 			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
 			showEnvVarsInLog = 0;
 		};
-		ABE62262A44D31B78B23818B /* [CP] Copy Pods Resources */ = {
+		1CA26990DE02013A9C3C5F31 /* [CP] Copy Pods Resources */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 			);
 			inputPaths = (
-				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh",
+				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAAutomationActions.plist",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerContentView.xib",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerView.xib",
@@ -472,16 +450,16 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh\"\n";
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
-		EC2DD7546E5E118A6D3073B2 /* [CP] Copy Pods Resources */ = {
+		49E28E3419E3718BB2C96C89 /* [CP] Copy Pods Resources */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 			);
 			inputPaths = (
-				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh",
+				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAAutomationActions.plist",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerContentView.xib",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerView.xib",
@@ -600,7 +578,29 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh\"\n";
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		C3A02165469B388F253FA6CD /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-ReactNativeChatTests-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
 			showEnvVarsInLog = 0;
 		};
 		FD10A7F022414F080027D42C /* Start Packager */ = {
@@ -655,7 +655,7 @@
 /* Begin XCBuildConfiguration section */
 		00E356F61AD99517003FC87E /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 4DAAA8DFB4A4EF50B076BE3D /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */;
+			baseConfigurationReference = F4C3A250E339EC05E79EAD6F /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
@@ -664,7 +664,7 @@
 					"$(inherited)",
 				);
 				INFOPLIST_FILE = ReactNativeChatTests/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 10.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				OTHER_LDFLAGS = (
 					"-ObjC",
@@ -679,13 +679,13 @@
 		};
 		00E356F71AD99517003FC87E /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 45D4E4039B764281ECADF639 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */;
+			baseConfigurationReference = D65289D5EBFF81AA41B765AF /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
 				COPY_PHASE_STRIP = NO;
 				INFOPLIST_FILE = ReactNativeChatTests/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 10.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				OTHER_LDFLAGS = (
 					"-ObjC",
@@ -700,7 +700,7 @@
 		};
 		13B07F941A680F5B00A75B9A /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 05C75E1764EA836C54E53E91 /* Pods-ReactNativeChat.debug.xcconfig */;
+			baseConfigurationReference = C22C74DA9BEE32E2C61363D3 /* Pods-ReactNativeChat.debug.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
@@ -731,7 +731,7 @@
 		};
 		13B07F951A680F5B00A75B9A /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = D232EFF53A6330C07E8BC0F8 /* Pods-ReactNativeChat.release.xcconfig */;
+			baseConfigurationReference = 7FC84F0F8F1E4ABFF9DBB4C1 /* Pods-ReactNativeChat.release.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
@@ -805,7 +805,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 10.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
 				LIBRARY_SEARCH_PATHS = (
 					"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
@@ -858,7 +858,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 10.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
 				LIBRARY_SEARCH_PATHS = (
 					"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",

From 6acfdc82f53f3ae82cd34416a05d9d00d41166c5 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Thu, 24 Sep 2020 11:18:25 -0700
Subject: [PATCH 10/70] separate out browser notifications from push
 notifications

---
 src/Expensify.js                              |  2 --
 .../{ => BrowserNotification}/index.js        | 19 +++-----------
 .../BrowserNotification/index.native.js       |  4 +++
 src/lib/Notification/BrowserNotifications.js  |  0
 src/lib/Notification/index.native.js          | 26 -------------------
 src/lib/actions/Report.js                     |  4 +--
 6 files changed, 10 insertions(+), 45 deletions(-)
 rename src/lib/Notification/{ => BrowserNotification}/index.js (89%)
 create mode 100644 src/lib/Notification/BrowserNotification/index.native.js
 delete mode 100644 src/lib/Notification/BrowserNotifications.js
 delete mode 100644 src/lib/Notification/index.native.js

diff --git a/src/Expensify.js b/src/Expensify.js
index 834b3b88ea6c..e83e06622a75 100644
--- a/src/Expensify.js
+++ b/src/Expensify.js
@@ -11,7 +11,6 @@ import * as ActiveClientManager from './lib/ActiveClientManager';
 import IONKEYS from './IONKEYS';
 import withIon from './components/withIon';
 import styles from './style/StyleSheet';
-import Notification from './lib/Notification';
 
 import {
     Route,
@@ -55,7 +54,6 @@ class Expensify extends Component {
             key: IONKEYS.SESSION,
             callback: this.removeLoadingState,
         });
-        Notification.enableUserNotifications();
     }
 
     /**
diff --git a/src/lib/Notification/index.js b/src/lib/Notification/BrowserNotification/index.js
similarity index 89%
rename from src/lib/Notification/index.js
rename to src/lib/Notification/BrowserNotification/index.js
index f3d99e3f4160..c93ffa8e9af3 100644
--- a/src/lib/Notification/index.js
+++ b/src/lib/Notification/BrowserNotification/index.js
@@ -1,6 +1,6 @@
-import Str from '../Str';
-import CONST from '../../CONST';
-import * as ActiveClientManager from '../ActiveClientManager';
+import Str from '../../Str';
+import CONST from '../../../CONST';
+import * as ActiveClientManager from '../../ActiveClientManager';
 
 const EXPENSIFY_ICON_URL = `${CONST.CLOUDFRONT_URL}/images/favicon-2019.png`;
 const DEFAULT_DELAY = 4000;
@@ -119,23 +119,12 @@ function pushReportCommentNotification({reportAction, onClick}) {
     });
 }
 
-/* ====== Public Functions (signatures must match index.native.js) ====== */
-
-/**
- * See if user notification permissions are set, and ask for permission if not set.
- *
- * @returns {Promise}
- */
-function enableUserNotifications() {
-    return canUseBrowserNotifications();
-}
-
+/* ====== Public Functions ====== */
 
 function showCommentNotification({reportAction, onClick}) {
     pushReportCommentNotification({reportAction, onClick});
 }
 
 export default {
-    enableUserNotifications,
     showCommentNotification,
 };
diff --git a/src/lib/Notification/BrowserNotification/index.native.js b/src/lib/Notification/BrowserNotification/index.native.js
new file mode 100644
index 000000000000..e1b798612c61
--- /dev/null
+++ b/src/lib/Notification/BrowserNotification/index.native.js
@@ -0,0 +1,4 @@
+// Browser Notifications are not supported on mobile so we'll just noop here.
+export default {
+    showCommentNotification: () => {},
+};
diff --git a/src/lib/Notification/BrowserNotifications.js b/src/lib/Notification/BrowserNotifications.js
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/src/lib/Notification/index.native.js b/src/lib/Notification/index.native.js
deleted file mode 100644
index 18b9ef1eaa49..000000000000
--- a/src/lib/Notification/index.native.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import {UrbanAirship, NotificationOptionsIOS} from 'urbanairship-react-native';
-import _ from 'underscore';
-
-// By default, we set all foreground push notification options to true (only relevant to iOS)
-const foregroundPresentationOptions = _.mapObject(_.invert(NotificationOptionsIOS), () => true);
-
-/* ====== Public Functions (signatures must match index.js) ====== */
-
-/**
- * See if user notification permissions are set, and ask for permission if not set.
- *
- * @returns {Promise}
- */
-function enableUserNotifications() {
-    UrbanAirship.setForegroundPresentationOptions(foregroundPresentationOptions);
-    return UrbanAirship.enableUserPushNotifications();
-}
-
-function showCommentNotification({reportAction, onClick}) {
-    // TODO: show "snackbar"/GROWL notification
-}
-
-export default {
-    enableUserNotifications,
-    showCommentNotification,
-};
diff --git a/src/lib/actions/Report.js b/src/lib/actions/Report.js
index 6cf962906118..5533ab2a530f 100644
--- a/src/lib/actions/Report.js
+++ b/src/lib/actions/Report.js
@@ -8,7 +8,7 @@ import CONFIG from '../../CONFIG';
 import * as Pusher from '../Pusher/pusher';
 import promiseAllSettled from '../promiseAllSettled';
 import ExpensiMark from '../ExpensiMark';
-import Notification from '../Notification';
+import BrowserNotification from '../Notification/BrowserNotification';
 import * as PersonalDetails from './PersonalDetails';
 import {redirect} from './App';
 
@@ -206,7 +206,7 @@ function updateReportWithNewAction(reportID, reportAction) {
     }
 
     console.debug('[NOTIFICATION] Creating notification');
-    Notification.showCommentNotification({
+    BrowserNotification.showCommentNotification({
         reportAction,
         onClick: () => {
             // Navigate to this report onClick

From 8b8e20f7309a63785e56d8e90b225a165c43194b Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Thu, 24 Sep 2020 11:38:03 -0700
Subject: [PATCH 11/70] setup push notifications in separate module

---
 src/Expensify.js                              |  2 ++
 .../Notification/PushNotification/index.js    |  4 ++++
 .../PushNotification/index.native.js          | 21 +++++++++++++++++++
 3 files changed, 27 insertions(+)
 create mode 100644 src/lib/Notification/PushNotification/index.js
 create mode 100644 src/lib/Notification/PushNotification/index.native.js

diff --git a/src/Expensify.js b/src/Expensify.js
index e83e06622a75..7c496a95d369 100644
--- a/src/Expensify.js
+++ b/src/Expensify.js
@@ -11,6 +11,7 @@ import * as ActiveClientManager from './lib/ActiveClientManager';
 import IONKEYS from './IONKEYS';
 import withIon from './components/withIon';
 import styles from './style/StyleSheet';
+import PushNotfication from './lib/Notification/PushNotification';
 
 import {
     Route,
@@ -54,6 +55,7 @@ class Expensify extends Component {
             key: IONKEYS.SESSION,
             callback: this.removeLoadingState,
         });
+        PushNotfication.enableUserNotifications();
     }
 
     /**
diff --git a/src/lib/Notification/PushNotification/index.js b/src/lib/Notification/PushNotification/index.js
new file mode 100644
index 000000000000..ac647f61a4c4
--- /dev/null
+++ b/src/lib/Notification/PushNotification/index.js
@@ -0,0 +1,4 @@
+// Push notifications are only supported on mobile, so we'll just noop here
+export default {
+    enableUserNotifications: () => {},
+}
diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
new file mode 100644
index 000000000000..be0bd7f7d5b1
--- /dev/null
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -0,0 +1,21 @@
+import {UrbanAirship, NotificationOptionsIOS} from 'urbanairship-react-native';
+import _ from 'underscore';
+
+// By default, we set all foreground push notification options to true (only relevant to iOS)
+const foregroundPresentationOptions = _.mapObject(_.invert(NotificationOptionsIOS), () => true);
+
+/* ====== Public Functions (signatures must match index.js) ====== */
+
+/**
+ * See if user notification permissions are set, and ask for permission if not set.
+ *
+ * @returns {Promise}
+ */
+function enableUserNotifications() {
+    UrbanAirship.setForegroundPresentationOptions(foregroundPresentationOptions);
+    return UrbanAirship.enableUserPushNotifications();
+}
+
+export default {
+    enableUserNotifications,
+};

From 866d8dda3d2809c64e7e0798810427eae2f93015 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Thu, 24 Sep 2020 18:14:34 -0700
Subject: [PATCH 12/70] register/deregister for push notifications on
 signin/signout

---
 src/Expensify.js                              |  4 +--
 src/lib/Ion.js                                | 34 ++++++++++++++++---
 .../Notification/PushNotification/index.js    |  2 +-
 .../PushNotification/index.native.js          | 31 +++++++++--------
 4 files changed, 50 insertions(+), 21 deletions(-)

diff --git a/src/Expensify.js b/src/Expensify.js
index 7c496a95d369..ad0519fa00a0 100644
--- a/src/Expensify.js
+++ b/src/Expensify.js
@@ -11,7 +11,7 @@ import * as ActiveClientManager from './lib/ActiveClientManager';
 import IONKEYS from './IONKEYS';
 import withIon from './components/withIon';
 import styles from './style/StyleSheet';
-import PushNotfication from './lib/Notification/PushNotification';
+import PushNotification from './lib/Notification/PushNotification';
 
 import {
     Route,
@@ -55,7 +55,7 @@ class Expensify extends Component {
             key: IONKEYS.SESSION,
             callback: this.removeLoadingState,
         });
-        PushNotfication.enableUserNotifications();
+        PushNotification.enablePushNotifications();
     }
 
     /**
diff --git a/src/lib/Ion.js b/src/lib/Ion.js
index cf67dbd36a29..980af94ff9ac 100644
--- a/src/lib/Ion.js
+++ b/src/lib/Ion.js
@@ -28,7 +28,7 @@ const callbackToStateMapping = {};
  * Get some data from the store
  *
  * @param {string} key
- * @returns {*}
+ * @returns {Promise<*>}
  */
 function get(key) {
     return AsyncStorage.getItem(key)
@@ -224,11 +224,37 @@ function multiSet(data) {
 
 /**
  * Clear out all the data in the store
- *
- * @returns {Promise}
  */
 function clear() {
-    return AsyncStorage.clear();
+    // Perform a depth-first search of all the IONKEYS,
+    // and collect all the keys whose values are NOT null or undefined
+    const currentKeySet = (
+        function dfs(obj, parentKey = '', keySet = []) {
+            return _.map(Object.keys(obj), key => get(key)
+                .then((val) => {
+                    if (typeof val === 'object') {
+                        keySet.concat(dfs(val, `${parentKey}.${key}`, keySet));
+                    } else if (val !== null && val !== undefined) {
+                        keySet.push(key);
+                    }
+                    return keySet;
+                }));
+        }
+    )(IONKEYS);
+
+    // Await all the keys from Ion.get
+    Promise.all(currentKeySet)
+        .then((keys) => {
+            // Then clear the store
+            AsyncStorage.clear();
+            return keys;
+        })
+        .then((keys) => {
+            // Then notify all the relevant subscribers that their underlying Ion data has been deleted
+            _.each(keys, (key) => {
+                keyChanged(key, undefined);
+            });
+        });
 }
 
 /**
diff --git a/src/lib/Notification/PushNotification/index.js b/src/lib/Notification/PushNotification/index.js
index ac647f61a4c4..31d68db189a3 100644
--- a/src/lib/Notification/PushNotification/index.js
+++ b/src/lib/Notification/PushNotification/index.js
@@ -1,4 +1,4 @@
 // Push notifications are only supported on mobile, so we'll just noop here
 export default {
     enableUserNotifications: () => {},
-}
+};
diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index be0bd7f7d5b1..62fbc149b489 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -1,21 +1,24 @@
-import {UrbanAirship, NotificationOptionsIOS} from 'urbanairship-react-native';
-import _ from 'underscore';
-
-// By default, we set all foreground push notification options to true (only relevant to iOS)
-const foregroundPresentationOptions = _.mapObject(_.invert(NotificationOptionsIOS), () => true);
-
-/* ====== Public Functions (signatures must match index.js) ====== */
+import {UrbanAirship} from 'urbanairship-react-native';
+import Ion from '../../Ion';
+import IONKEYS from '../../../IONKEYS';
 
 /**
- * See if user notification permissions are set, and ask for permission if not set.
- *
- * @returns {Promise}
+ * Get permissions and register this device as a named user in AirshipAPI.
  */
-function enableUserNotifications() {
-    UrbanAirship.setForegroundPresentationOptions(foregroundPresentationOptions);
-    return UrbanAirship.enableUserPushNotifications();
+function enablePushNotifications() {
+    UrbanAirship.enableUserPushNotifications()
+        .finally(() => {
+            Ion.connect({
+                key: IONKEYS.SESSION,
+                callback: (sessionData) => {
+                    // This will register this device with the named user associated with this accountID,
+                    // or clear the the named user (deregister this device) if sessionData.accountID is undefined
+                    UrbanAirship.setNamedUser(sessionData.accountID.toString() || undefined);
+                }
+            });
+        });
 }
 
 export default {
-    enableUserNotifications,
+    enablePushNotifications,
 };

From 8103e061d7a700b4539f2d1dafda1291013c86ce Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Fri, 25 Sep 2020 10:35:59 -0700
Subject: [PATCH 13/70] Setup listeners and action binding for push
 notifications

---
 src/lib/Ion.js                                |  2 +-
 src/lib/Notification/NotificationType.js      |  5 ++
 .../PushNotification/index.native.js          | 49 ++++++++++++++++++-
 src/lib/actions/Report.js                     | 15 ++++--
 4 files changed, 65 insertions(+), 6 deletions(-)
 create mode 100644 src/lib/Notification/NotificationType.js

diff --git a/src/lib/Ion.js b/src/lib/Ion.js
index 980af94ff9ac..e43de8146888 100644
--- a/src/lib/Ion.js
+++ b/src/lib/Ion.js
@@ -243,7 +243,7 @@ function clear() {
     )(IONKEYS);
 
     // Await all the keys from Ion.get
-    Promise.all(currentKeySet)
+    Promise.allSettled(currentKeySet)
         .then((keys) => {
             // Then clear the store
             AsyncStorage.clear();
diff --git a/src/lib/Notification/NotificationType.js b/src/lib/Notification/NotificationType.js
new file mode 100644
index 000000000000..1a54a2770214
--- /dev/null
+++ b/src/lib/Notification/NotificationType.js
@@ -0,0 +1,5 @@
+export default {
+    REPORT: {
+        COMMENT: 'reportComment',
+    }
+};
diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index 62fbc149b489..33c23c098f37 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -1,7 +1,38 @@
-import {UrbanAirship} from 'urbanairship-react-native';
+import {UrbanAirship, EventType} from 'urbanairship-react-native';
 import Ion from '../../Ion';
 import IONKEYS from '../../../IONKEYS';
 
+/* ====== Private Functions ====== */
+
+const notificationTypeActionMap = {};
+
+/**
+ * Setup listener for push notifications, and trigger any bound actions.
+ */
+function setupPushNotificationCallbacks() {
+    UrbanAirship.addListener(EventType.PushReceived, (notification) => {
+        console.debug(`[PUSH_NOTIFICATION] Push received - {
+            title: ${notification.title},
+            message: ${notification.message},
+            payload: ${notification.payload}
+        }`);
+
+        if (!notification.payload.type) {
+            console.debug('[PUSH_NOTIFICATION] Notification of unknown type received...ignoring.');
+            return;
+        }
+        if (!notificationTypeActionMap[notification.payload.type]) {
+            console.debug(`[PUSH_NOTIFICATION] No callback setup for notification type: ${notification.payload.type}`);
+            return;
+        }
+
+        const action = notificationTypeActionMap[notification.payload.type].action;
+        action(...notification.payload);
+    });
+}
+
+/* ====== Public Functions ====== */
+
 /**
  * Get permissions and register this device as a named user in AirshipAPI.
  */
@@ -16,9 +47,25 @@ function enablePushNotifications() {
                     UrbanAirship.setNamedUser(sessionData.accountID.toString() || undefined);
                 }
             });
+        })
+        .then(() => {
+            setupPushNotificationCallbacks();
         });
 }
 
+/**
+ * Bind an action (from src/lib/actions) to a push notification of a given type.
+ * See https://github.com/Expensify/Web-Expensify/blob/master/lib/MobilePushNotifications.php for the various
+ * types of push notifications sent, along with the data that they provide.
+ *
+ * @param {string} notificationType
+ * @param {Function} action
+ */
+function bindActionToPushNotification(notificationType, action) {
+    notificationTypeActionMap[notificationType] = action;
+}
+
 export default {
     enablePushNotifications,
+    bindActionToPushNotification,
 };
diff --git a/src/lib/actions/Report.js b/src/lib/actions/Report.js
index 5533ab2a530f..742fe3c9c32b 100644
--- a/src/lib/actions/Report.js
+++ b/src/lib/actions/Report.js
@@ -9,6 +9,8 @@ import * as Pusher from '../Pusher/pusher';
 import promiseAllSettled from '../promiseAllSettled';
 import ExpensiMark from '../ExpensiMark';
 import BrowserNotification from '../Notification/BrowserNotification';
+import PushNotification from '../Notification/PushNotification';
+import NotificationType from '../Notification/NotificationType';
 import * as PersonalDetails from './PersonalDetails';
 import {redirect} from './App';
 
@@ -193,7 +195,7 @@ function updateReportWithNewAction(reportID, reportAction) {
 
     // If this comment is from the current user we don't want to parrot whatever they wrote back to them.
     if (reportAction.actorEmail === currentUserEmail) {
-        console.debug('[NOTIFICATION] No notification because comment is from the currently logged in user');
+        console.debug('[BROWSER_NOTIFICATION] No notification because comment is from the currently logged in user');
         return;
     }
 
@@ -201,11 +203,11 @@ function updateReportWithNewAction(reportID, reportAction) {
 
     // If we are currently viewing this report do not show a notification.
     if (reportID === currentReportID) {
-        console.debug('[NOTIFICATION] No notification because it was a comment for the current report');
+        console.debug('[BROWSER_NOTIFICATION] No notification because it was a comment for the current report');
         return;
     }
 
-    console.debug('[NOTIFICATION] Creating notification');
+    console.debug('[BROWSER_NOTIFICATION] Creating notification');
     BrowserNotification.showCommentNotification({
         reportAction,
         onClick: () => {
@@ -224,9 +226,14 @@ function subscribeToReportCommentEvents() {
         return;
     }
 
-    Pusher.subscribe(pusherChannelName, 'reportComment', (pushJSON) => {
+    Pusher.subscribe(pusherChannelName, NotificationType.REPORT.COMMENT, (pushJSON) => {
         updateReportWithNewAction(pushJSON.reportID, pushJSON.reportAction);
     });
+
+    PushNotification.bindActionToPushNotification(NotificationType.REPORT.COMMENT, (payload) => {
+        const {reportID, reportAction} = payload;
+        updateReportWithNewAction(reportID, reportAction);
+    });
 }
 
 /**

From 67820acaa7ba3677d623f59374b0ebc4fbc78d87 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Fri, 25 Sep 2020 10:40:40 -0700
Subject: [PATCH 14/70] rename browser notifications to localnotifications

---
 .../BrowserNotification/index.native.js          |  4 ----
 .../index.js                                     | 16 ++++++++--------
 .../LocalNotification/index.native.js            |  4 ++++
 src/lib/actions/Report.js                        | 10 +++++-----
 4 files changed, 17 insertions(+), 17 deletions(-)
 delete mode 100644 src/lib/Notification/BrowserNotification/index.native.js
 rename src/lib/Notification/{BrowserNotification => LocalNotification}/index.js (83%)
 create mode 100644 src/lib/Notification/LocalNotification/index.native.js

diff --git a/src/lib/Notification/BrowserNotification/index.native.js b/src/lib/Notification/BrowserNotification/index.native.js
deleted file mode 100644
index e1b798612c61..000000000000
--- a/src/lib/Notification/BrowserNotification/index.native.js
+++ /dev/null
@@ -1,4 +0,0 @@
-// Browser Notifications are not supported on mobile so we'll just noop here.
-export default {
-    showCommentNotification: () => {},
-};
diff --git a/src/lib/Notification/BrowserNotification/index.js b/src/lib/Notification/LocalNotification/index.js
similarity index 83%
rename from src/lib/Notification/BrowserNotification/index.js
rename to src/lib/Notification/LocalNotification/index.js
index c93ffa8e9af3..f9f85ea87c7e 100644
--- a/src/lib/Notification/BrowserNotification/index.js
+++ b/src/lib/Notification/LocalNotification/index.js
@@ -8,13 +8,13 @@ const DEFAULT_DELAY = 4000;
 /* ====== Private Functions ====== */
 
 /**
- * Checks if the user has granted permission to show browser notifications
+ * Checks if the user has granted permission to show local notifications
  *
  * @return {Promise}
  */
-function canUseBrowserNotifications() {
+function canUseLocalNotifications() {
     return new Promise((resolve) => {
-        // They have no browser notifications so we can't use this feature
+        // They have no local notifications so we can't use this feature
         if (!window.Notification) {
             return resolve(false);
         }
@@ -26,7 +26,7 @@ function canUseBrowserNotifications() {
             return resolve(permissionGranted);
         }
 
-        // Check their global preferences for browser notifications and ask permission if they have none
+        // Check their global preferences for local notifications and ask permission if they have none
         Notification.requestPermission()
             .then((status) => {
                 resolve(status === 'granted');
@@ -35,7 +35,7 @@ function canUseBrowserNotifications() {
 }
 
 /**
- * Light abstraction around browser push notifications.
+ * Light abstraction around local push notifications.
  * Checks for permission before determining whether to send.
  *
  * @param {Object} params
@@ -58,10 +58,10 @@ function push({
 }) {
     return new Promise((resolve) => {
         if (!title || !body) {
-            throw new Error('BrowserNotification must include title and body parameter.');
+            throw new Error('LocalNotification must include title and body parameter.');
         }
 
-        canUseBrowserNotifications().then((canUseNotifications) => {
+        canUseLocalNotifications().then((canUseNotifications) => {
             if (!canUseNotifications) {
                 resolve();
                 return;
@@ -101,7 +101,7 @@ function push({
  */
 function pushReportCommentNotification({reportAction, onClick}) {
     if (!ActiveClientManager.isClientTheLeader()) {
-        console.debug('[BrowserNotifications] Skipping notification because this client is not the leader');
+        console.debug('[LocalNotifications] Skipping notification because this client is not the leader');
         return;
     }
 
diff --git a/src/lib/Notification/LocalNotification/index.native.js b/src/lib/Notification/LocalNotification/index.native.js
new file mode 100644
index 000000000000..8dca0ebb7537
--- /dev/null
+++ b/src/lib/Notification/LocalNotification/index.native.js
@@ -0,0 +1,4 @@
+// Local Notifications are not currently supported on mobile so we'll just noop here.
+export default {
+    showCommentNotification: () => {},
+};
diff --git a/src/lib/actions/Report.js b/src/lib/actions/Report.js
index 742fe3c9c32b..709f6050c4f1 100644
--- a/src/lib/actions/Report.js
+++ b/src/lib/actions/Report.js
@@ -8,7 +8,7 @@ import CONFIG from '../../CONFIG';
 import * as Pusher from '../Pusher/pusher';
 import promiseAllSettled from '../promiseAllSettled';
 import ExpensiMark from '../ExpensiMark';
-import BrowserNotification from '../Notification/BrowserNotification';
+import LocalNotification from '../Notification/LocalNotification';
 import PushNotification from '../Notification/PushNotification';
 import NotificationType from '../Notification/NotificationType';
 import * as PersonalDetails from './PersonalDetails';
@@ -195,7 +195,7 @@ function updateReportWithNewAction(reportID, reportAction) {
 
     // If this comment is from the current user we don't want to parrot whatever they wrote back to them.
     if (reportAction.actorEmail === currentUserEmail) {
-        console.debug('[BROWSER_NOTIFICATION] No notification because comment is from the currently logged in user');
+        console.debug('[LOCAL_NOTIFICATION] No notification because comment is from the currently logged in user');
         return;
     }
 
@@ -203,12 +203,12 @@ function updateReportWithNewAction(reportID, reportAction) {
 
     // If we are currently viewing this report do not show a notification.
     if (reportID === currentReportID) {
-        console.debug('[BROWSER_NOTIFICATION] No notification because it was a comment for the current report');
+        console.debug('[LOCAL_NOTIFICATION] No notification because it was a comment for the current report');
         return;
     }
 
-    console.debug('[BROWSER_NOTIFICATION] Creating notification');
-    BrowserNotification.showCommentNotification({
+    console.debug('[LOCAL_NOTIFICATION] Creating notification');
+    LocalNotification.showCommentNotification({
         reportAction,
         onClick: () => {
             // Navigate to this report onClick

From 54f9cbc1013742f29df4fc332d03f320d7480edc Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Fri, 25 Sep 2020 14:46:29 -0700
Subject: [PATCH 15/70] ignore push notifications if app is in the foreground

---
 src/lib/Notification/PushNotification/index.native.js | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index 33c23c098f37..9877dccee074 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -1,3 +1,4 @@
+import {AppState} from 'react-native';
 import {UrbanAirship, EventType} from 'urbanairship-react-native';
 import Ion from '../../Ion';
 import IONKEYS from '../../../IONKEYS';
@@ -17,8 +18,13 @@ function setupPushNotificationCallbacks() {
             payload: ${notification.payload}
         }`);
 
+        // If app is in foreground,  we'll assume pusher is connected so we'll ignore this push notification
+        if (AppState.currentState === 'active') {
+            console.debug('[PUSH_NOTIFICATION] App is in foreground, ignoring push notification.');
+            return;
+        }
         if (!notification.payload.type) {
-            console.debug('[PUSH_NOTIFICATION] Notification of unknown type received...ignoring.');
+            console.debug('[PUSH_NOTIFICATION] Notification of unknown type received, ignoring.');
             return;
         }
         if (!notificationTypeActionMap[notification.payload.type]) {

From 5bf7eafee0638a11dc1a10ff0526ef863eae109b Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Fri, 25 Sep 2020 16:15:47 -0700
Subject: [PATCH 16/70] merge master into Rory-PushNotifications

---
 ios/Podfile.lock                              |   4 +-
 ios/ReactNativeChat.xcodeproj/project.pbxproj | 332 +++++++++++++++---
 2 files changed, 278 insertions(+), 58 deletions(-)

diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 2eb9b8784651..196bca0184aa 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -512,8 +512,8 @@ SPEC CHECKSUMS:
   React-RCTText: 1b6773e776e4b33f90468c20fe3b16ca3e224bb8
   React-RCTVibration: 4d2e726957f4087449739b595f107c0d4b6c2d2d
   ReactCommon: a0a1edbebcac5e91338371b72ffc66aa822792ce
-  RNCAsyncStorage: d059c3ee71738c39834a627476322a5a8cd5bf36
-  urbanairship-react-native: 87a82abfbc182a854f66eb7fd25198ce62e95a54
+  RNCAsyncStorage: db711e29e5e0500d9bd21aa0c2e397efa45302b1
+  urbanairship-react-native: fa123940041a6a13ab7dac192e32833c53754f00
   Yoga: 7740b94929bbacbddda59bf115b5317e9a161598
   YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
 
diff --git a/ios/ReactNativeChat.xcodeproj/project.pbxproj b/ios/ReactNativeChat.xcodeproj/project.pbxproj
index 911b93d1d30b..854c80613f86 100644
--- a/ios/ReactNativeChat.xcodeproj/project.pbxproj
+++ b/ios/ReactNativeChat.xcodeproj/project.pbxproj
@@ -8,14 +8,14 @@
 
 /* Begin PBXBuildFile section */
 		00E356F31AD99517003FC87E /* ReactNativeChatTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* ReactNativeChatTests.m */; };
-		097C76E5807C5DFC565DC655 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C4F81DB9C6DA6418525441E5 /* libPods-ReactNativeChat-ReactNativeChatTests.a */; };
 		11FA3546D54246CD8F819152 /* GTAmericaExp-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = A5AAD008CBD84A6CAEB9AC97 /* GTAmericaExp-Bold.otf */; };
 		13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
 		13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
 		13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
-		347C4A976B972BC7A903F97D /* libPods-ReactNativeChat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6ABF95746519766B23B4E2D0 /* libPods-ReactNativeChat.a */; };
 		4912B60FA66C4604A56AD575 /* GTAmericaExp-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 8C7003903C1E4957824899BB /* GTAmericaExp-Regular.otf */; };
 		49F157C845CE4D56AA72B80E /* GTAmericaExp-Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = 67D5C3A6A7FA417C8A853FC1 /* GTAmericaExp-Light.otf */; };
+		60953A1918F99DB6992E7324 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A701CE31FD631756636DC73 /* libPods-ReactNativeChat-ReactNativeChatTests.a */; };
+		71B0ACFFCD2E81B6C714ED34 /* libPods-ReactNativeChat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1BD00A86B037F3449D36DEB5 /* libPods-ReactNativeChat.a */; };
 		7F7721B764D444D6861A9B39 /* GTAmericaExp-Thin.otf in Resources */ = {isa = PBXBuildFile; fileRef = A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */; };
 		81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
 		CC07911F22C2437DA6708BD2 /* GTAmericaExp-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = AE65058949E14DA5A2D5435D /* GTAmericaExp-Medium.otf */; };
@@ -36,24 +36,24 @@
 		00E356EE1AD99517003FC87E /* ReactNativeChatTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactNativeChatTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		00E356F21AD99517003FC87E /* ReactNativeChatTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReactNativeChatTests.m; sourceTree = "<group>"; };
+		0BA2C82CAFF5F2E69E3B49E9 /* Pods-ReactNativeChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.release.xcconfig"; sourceTree = "<group>"; };
 		13B07F961A680F5B00A75B9A /* Chat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Chat.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ReactNativeChat/AppDelegate.h; sourceTree = "<group>"; };
 		13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = ReactNativeChat/AppDelegate.m; sourceTree = "<group>"; };
 		13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = ReactNativeChat/Images.xcassets; sourceTree = "<group>"; };
 		13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ReactNativeChat/Info.plist; sourceTree = "<group>"; };
 		13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ReactNativeChat/main.m; sourceTree = "<group>"; };
-		1CFBC073CF30A72CCF109A40 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; sourceTree = "<group>"; };
+		1BD00A86B037F3449D36DEB5 /* libPods-ReactNativeChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		2CE86F5913AAD81AF5C4F74A /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; sourceTree = "<group>"; };
+		3C5AC1DDF78903A0E0DCE06E /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; sourceTree = "<group>"; };
 		67D5C3A6A7FA417C8A853FC1 /* GTAmericaExp-Light.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Light.otf"; path = "../assets/fonts/GTAmericaExp-Light.otf"; sourceTree = "<group>"; };
-		6ABF95746519766B23B4E2D0 /* libPods-ReactNativeChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = ReactNativeChat/LaunchScreen.storyboard; sourceTree = "<group>"; };
+		8A701CE31FD631756636DC73 /* libPods-ReactNativeChat-ReactNativeChatTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat-ReactNativeChatTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		8C7003903C1E4957824899BB /* GTAmericaExp-Regular.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Regular.otf"; path = "../assets/fonts/GTAmericaExp-Regular.otf"; sourceTree = "<group>"; };
-		9B9FEA91E46D2B2609028E69 /* Pods-ReactNativeChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.debug.xcconfig"; sourceTree = "<group>"; };
 		A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Thin.otf"; path = "../assets/fonts/GTAmericaExp-Thin.otf"; sourceTree = "<group>"; };
 		A5AAD008CBD84A6CAEB9AC97 /* GTAmericaExp-Bold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Bold.otf"; path = "../assets/fonts/GTAmericaExp-Bold.otf"; sourceTree = "<group>"; };
 		AE65058949E14DA5A2D5435D /* GTAmericaExp-Medium.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Medium.otf"; path = "../assets/fonts/GTAmericaExp-Medium.otf"; sourceTree = "<group>"; };
-		BA6C613DE6755E40F7EDDC68 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; sourceTree = "<group>"; };
-		C4F81DB9C6DA6418525441E5 /* libPods-ReactNativeChat-ReactNativeChatTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat-ReactNativeChatTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
-		C6C1D4F5C262ACDAF1800CAD /* Pods-ReactNativeChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.release.xcconfig"; sourceTree = "<group>"; };
+		C95F4AF390CEA0E1D68B4B91 /* Pods-ReactNativeChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.debug.xcconfig"; sourceTree = "<group>"; };
 		ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
 		ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; };
 /* End PBXFileReference section */
@@ -63,7 +63,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				097C76E5807C5DFC565DC655 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */,
+				60953A1918F99DB6992E7324 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -71,7 +71,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				347C4A976B972BC7A903F97D /* libPods-ReactNativeChat.a in Frameworks */,
+				71B0ACFFCD2E81B6C714ED34 /* libPods-ReactNativeChat.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -114,8 +114,8 @@
 			children = (
 				ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
 				ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
-				6ABF95746519766B23B4E2D0 /* libPods-ReactNativeChat.a */,
-				C4F81DB9C6DA6418525441E5 /* libPods-ReactNativeChat-ReactNativeChatTests.a */,
+				1BD00A86B037F3449D36DEB5 /* libPods-ReactNativeChat.a */,
+				8A701CE31FD631756636DC73 /* libPods-ReactNativeChat-ReactNativeChatTests.a */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
@@ -167,10 +167,10 @@
 		EC29677F0A49C2946A495A33 /* Pods */ = {
 			isa = PBXGroup;
 			children = (
-				9B9FEA91E46D2B2609028E69 /* Pods-ReactNativeChat.debug.xcconfig */,
-				C6C1D4F5C262ACDAF1800CAD /* Pods-ReactNativeChat.release.xcconfig */,
-				BA6C613DE6755E40F7EDDC68 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */,
-				1CFBC073CF30A72CCF109A40 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */,
+				C95F4AF390CEA0E1D68B4B91 /* Pods-ReactNativeChat.debug.xcconfig */,
+				0BA2C82CAFF5F2E69E3B49E9 /* Pods-ReactNativeChat.release.xcconfig */,
+				2CE86F5913AAD81AF5C4F74A /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */,
+				3C5AC1DDF78903A0E0DCE06E /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */,
 			);
 			path = Pods;
 			sourceTree = "<group>";
@@ -182,11 +182,11 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "ReactNativeChatTests" */;
 			buildPhases = (
-				67625BD6E56D7B445CE0B75D /* [CP] Check Pods Manifest.lock */,
+				AAC7D3DC030D4C07E1FAB9DE /* [CP] Check Pods Manifest.lock */,
 				00E356EA1AD99517003FC87E /* Sources */,
 				00E356EB1AD99517003FC87E /* Frameworks */,
 				00E356EC1AD99517003FC87E /* Resources */,
-				740ED6E57E4D77AE97454014 /* [CP] Copy Pods Resources */,
+				7A9C37AA593615200704B167 /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -202,13 +202,13 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ReactNativeChat" */;
 			buildPhases = (
-				25778851D923FF95A2D6EE6D /* [CP] Check Pods Manifest.lock */,
+				8CDB7E879D6E05C91FBF0ECF /* [CP] Check Pods Manifest.lock */,
 				FD10A7F022414F080027D42C /* Start Packager */,
 				13B07F871A680F5B00A75B9A /* Sources */,
 				13B07F8C1A680F5B00A75B9A /* Frameworks */,
 				13B07F8E1A680F5B00A75B9A /* Resources */,
 				00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
-				930B6ED2A0414681598DB7E1 /* [CP] Copy Pods Resources */,
+				8018F2812F21A5A4857D8808 /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -297,84 +297,304 @@
 			shellPath = /bin/sh;
 			shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh";
 		};
-		25778851D923FF95A2D6EE6D /* [CP] Check Pods Manifest.lock */ = {
+		7A9C37AA593615200704B167 /* [CP] Copy Pods Resources */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 			);
-			inputFileListPaths = (
-			);
 			inputPaths = (
-				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
-				"${PODS_ROOT}/Manifest.lock",
-			);
-			name = "[CP] Check Pods Manifest.lock";
-			outputFileListPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAAutomationActions.plist",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerContentView.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerView.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageButtonView.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageFullScreenViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageHTMLViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageModalViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageResizableViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAAutomation.xcdatamodeld",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/ar.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/cs.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/da.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/de.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/en.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/es-419.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/es.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/fi.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/fr.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/hi.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/hu.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/id.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/it.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/iw.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/ja.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/ko.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/ms.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/nl.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/no.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/pl.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/pt-PT.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/pt.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/ro.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/ru.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/sk.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/sv.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/th.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/tr.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/UAEvents.xcdatamodeld",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/UARemoteData.xcdatamodeld",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/vi.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/zh-Hans.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/zh-Hant.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/ios/UADefaultActions.plist",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/ios/UANativeBridge",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/ios/UANotificationCategories.plist",
+				"${PODS_ROOT}/Airship/Airship/AirshipExtendedActions/Resources/UAExtendedActions.plist",
+				"${PODS_ROOT}/Airship/Airship/AirshipExtendedActions/Resources/UARateAppPromptView.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UADefaultMessageCenterListViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UADefaultMessageCenterMessageViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UAMessageCenterActions.plist",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UAMessageCenterListCell.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UAMessageCenterListViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UAMessageCenterMessageViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UAMessageCenterPlaceholderIcon.png",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UAInbox.xcdatamodeld",
+				"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
 			);
+			name = "[CP] Copy Pods Resources";
 			outputPaths = (
-				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-checkManifestLockResult.txt",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAAutomationActions.plist",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageBannerContentView.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageBannerView.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageButtonView.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageFullScreenViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageHTMLViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageModalViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageResizableViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAAutomation.momd",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ar.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/cs.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/da.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/de.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/en.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/es-419.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/es.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/fi.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/fr.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/hi.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/hu.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/id.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/it.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/iw.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ja.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ko.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ms.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/nl.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/no.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/pl.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/pt-PT.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/pt.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ro.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ru.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/sk.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/sv.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/th.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/tr.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAEvents.momd",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UARemoteData.momd",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/vi.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/zh-Hans.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/zh-Hant.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UADefaultActions.plist",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UANativeBridge",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UANotificationCategories.plist",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAExtendedActions.plist",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UARateAppPromptView.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UADefaultMessageCenterListViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UADefaultMessageCenterMessageViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAMessageCenterActions.plist",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAMessageCenterListCell.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAMessageCenterListViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAMessageCenterMessageViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAMessageCenterPlaceholderIcon.png",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInbox.momd",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
-		67625BD6E56D7B445CE0B75D /* [CP] Check Pods Manifest.lock */ = {
+		8018F2812F21A5A4857D8808 /* [CP] Copy Pods Resources */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 			);
-			inputFileListPaths = (
-			);
 			inputPaths = (
-				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
-				"${PODS_ROOT}/Manifest.lock",
-			);
-			name = "[CP] Check Pods Manifest.lock";
-			outputFileListPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAAutomationActions.plist",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerContentView.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerView.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageButtonView.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageFullScreenViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageHTMLViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageModalViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageResizableViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAAutomation.xcdatamodeld",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/ar.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/cs.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/da.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/de.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/en.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/es-419.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/es.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/fi.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/fr.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/hi.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/hu.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/id.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/it.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/iw.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/ja.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/ko.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/ms.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/nl.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/no.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/pl.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/pt-PT.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/pt.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/ro.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/ru.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/sk.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/sv.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/th.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/tr.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/UAEvents.xcdatamodeld",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/UARemoteData.xcdatamodeld",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/vi.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/zh-Hans.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/common/zh-Hant.lproj",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/ios/UADefaultActions.plist",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/ios/UANativeBridge",
+				"${PODS_ROOT}/Airship/Airship/AirshipCore/Resources/ios/UANotificationCategories.plist",
+				"${PODS_ROOT}/Airship/Airship/AirshipExtendedActions/Resources/UAExtendedActions.plist",
+				"${PODS_ROOT}/Airship/Airship/AirshipExtendedActions/Resources/UARateAppPromptView.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UADefaultMessageCenterListViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UADefaultMessageCenterMessageViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UAMessageCenterActions.plist",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UAMessageCenterListCell.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UAMessageCenterListViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UAMessageCenterMessageViewController.xib",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UAMessageCenterPlaceholderIcon.png",
+				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UAInbox.xcdatamodeld",
+				"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
 			);
+			name = "[CP] Copy Pods Resources";
 			outputPaths = (
-				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-ReactNativeChatTests-checkManifestLockResult.txt",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAAutomationActions.plist",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageBannerContentView.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageBannerView.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageButtonView.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageFullScreenViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageHTMLViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageModalViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageResizableViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAAutomation.momd",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ar.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/cs.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/da.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/de.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/en.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/es-419.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/es.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/fi.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/fr.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/hi.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/hu.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/id.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/it.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/iw.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ja.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ko.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ms.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/nl.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/no.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/pl.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/pt-PT.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/pt.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ro.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ru.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/sk.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/sv.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/th.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/tr.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAEvents.momd",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UARemoteData.momd",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/vi.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/zh-Hans.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/zh-Hant.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UADefaultActions.plist",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UANativeBridge",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UANotificationCategories.plist",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAExtendedActions.plist",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UARateAppPromptView.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UADefaultMessageCenterListViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UADefaultMessageCenterMessageViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAMessageCenterActions.plist",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAMessageCenterListCell.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAMessageCenterListViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAMessageCenterMessageViewController.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAMessageCenterPlaceholderIcon.png",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInbox.momd",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
-		740ED6E57E4D77AE97454014 /* [CP] Copy Pods Resources */ = {
+		8CDB7E879D6E05C91FBF0ECF /* [CP] Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 			);
+			inputFileListPaths = (
+			);
 			inputPaths = (
-				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh",
-				"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
 			);
-			name = "[CP] Copy Pods Resources";
 			outputPaths = (
-				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
+				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-checkManifestLockResult.txt",
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh\"\n";
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
 			showEnvVarsInLog = 0;
 		};
-		930B6ED2A0414681598DB7E1 /* [CP] Copy Pods Resources */ = {
+		AAC7D3DC030D4C07E1FAB9DE /* [CP] Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 			);
+			inputFileListPaths = (
+			);
 			inputPaths = (
-				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh",
-				"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
 			);
-			name = "[CP] Copy Pods Resources";
 			outputPaths = (
-				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
+				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-ReactNativeChatTests-checkManifestLockResult.txt",
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh\"\n";
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
 			showEnvVarsInLog = 0;
 		};
 		FD10A7F022414F080027D42C /* Start Packager */ = {
@@ -429,7 +649,7 @@
 /* Begin XCBuildConfiguration section */
 		00E356F61AD99517003FC87E /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = BA6C613DE6755E40F7EDDC68 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */;
+			baseConfigurationReference = 2CE86F5913AAD81AF5C4F74A /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
@@ -453,7 +673,7 @@
 		};
 		00E356F71AD99517003FC87E /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 1CFBC073CF30A72CCF109A40 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */;
+			baseConfigurationReference = 3C5AC1DDF78903A0E0DCE06E /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
@@ -474,7 +694,7 @@
 		};
 		13B07F941A680F5B00A75B9A /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 9B9FEA91E46D2B2609028E69 /* Pods-ReactNativeChat.debug.xcconfig */;
+			baseConfigurationReference = C95F4AF390CEA0E1D68B4B91 /* Pods-ReactNativeChat.debug.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
@@ -503,7 +723,7 @@
 		};
 		13B07F951A680F5B00A75B9A /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = C6C1D4F5C262ACDAF1800CAD /* Pods-ReactNativeChat.release.xcconfig */;
+			baseConfigurationReference = 0BA2C82CAFF5F2E69E3B49E9 /* Pods-ReactNativeChat.release.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;

From cfed894b27d33fcb6a6d66a4f52432f0d085fd5b Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Fri, 25 Sep 2020 16:20:44 -0700
Subject: [PATCH 17/70] fix merge error

---
 .../Notification/LocalNotification/index.js   | 20 ++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

diff --git a/src/lib/Notification/LocalNotification/index.js b/src/lib/Notification/LocalNotification/index.js
index 75003f6346dc..aca777de3338 100644
--- a/src/lib/Notification/LocalNotification/index.js
+++ b/src/lib/Notification/LocalNotification/index.js
@@ -105,15 +105,17 @@ export default {
      */
     pushReportCommentNotification({reportAction, onClick}) {
         const {person, message} = reportAction;
-        const plainTextPerson = Str.htmlDecode(person.map(f => f.text).join());
+        const plainTextPerson = Str.htmlDecode(person.map(f => f.text)
+            .join());
 
-    // Specifically target the comment part of the message
-    const plainTextMessage = Str.htmlDecode((message.find(f => f.type === 'COMMENT') || {}).text);
+        // Specifically target the comment part of the message
+        const plainTextMessage = Str.htmlDecode((message.find(f => f.type === 'COMMENT') || {}).text);
 
-    push({
-        title: `New message from ${plainTextPerson}`,
-        body: plainTextMessage,
-        delay: 0,
-        onClick,
-    });
+        push({
+            title: `New message from ${plainTextPerson}`,
+            body: plainTextMessage,
+            delay: 0,
+            onClick,
+        });
+    }
 };

From d7292dd91cc45b8f72b4fc144ac5670f0a4a2562 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Tue, 29 Sep 2020 15:04:48 -0700
Subject: [PATCH 18/70] fixed issues with Ion.clear()

---
 ios/ReactNativeChat.xcodeproj/project.pbxproj | 174 +++++++++---------
 src/lib/Ion.js                                |  16 +-
 .../Notification/PushNotification/index.js    |   3 +-
 3 files changed, 99 insertions(+), 94 deletions(-)

diff --git a/ios/ReactNativeChat.xcodeproj/project.pbxproj b/ios/ReactNativeChat.xcodeproj/project.pbxproj
index 854c80613f86..50ef63a05776 100644
--- a/ios/ReactNativeChat.xcodeproj/project.pbxproj
+++ b/ios/ReactNativeChat.xcodeproj/project.pbxproj
@@ -14,11 +14,11 @@
 		13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
 		4912B60FA66C4604A56AD575 /* GTAmericaExp-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 8C7003903C1E4957824899BB /* GTAmericaExp-Regular.otf */; };
 		49F157C845CE4D56AA72B80E /* GTAmericaExp-Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = 67D5C3A6A7FA417C8A853FC1 /* GTAmericaExp-Light.otf */; };
-		60953A1918F99DB6992E7324 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A701CE31FD631756636DC73 /* libPods-ReactNativeChat-ReactNativeChatTests.a */; };
-		71B0ACFFCD2E81B6C714ED34 /* libPods-ReactNativeChat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1BD00A86B037F3449D36DEB5 /* libPods-ReactNativeChat.a */; };
 		7F7721B764D444D6861A9B39 /* GTAmericaExp-Thin.otf in Resources */ = {isa = PBXBuildFile; fileRef = A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */; };
 		81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
+		A2CDC0C5FF0B4238EDF00E70 /* libPods-ReactNativeChat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 98FAF3CC3CFF9F89A372A57F /* libPods-ReactNativeChat.a */; };
 		CC07911F22C2437DA6708BD2 /* GTAmericaExp-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = AE65058949E14DA5A2D5435D /* GTAmericaExp-Medium.otf */; };
+		DD56F9D34944614CB5A2BC9C /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D7846B56920B8E09BA63C843 /* libPods-ReactNativeChat-ReactNativeChatTests.a */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -36,24 +36,24 @@
 		00E356EE1AD99517003FC87E /* ReactNativeChatTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactNativeChatTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		00E356F21AD99517003FC87E /* ReactNativeChatTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReactNativeChatTests.m; sourceTree = "<group>"; };
-		0BA2C82CAFF5F2E69E3B49E9 /* Pods-ReactNativeChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.release.xcconfig"; sourceTree = "<group>"; };
 		13B07F961A680F5B00A75B9A /* Chat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Chat.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ReactNativeChat/AppDelegate.h; sourceTree = "<group>"; };
 		13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = ReactNativeChat/AppDelegate.m; sourceTree = "<group>"; };
 		13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = ReactNativeChat/Images.xcassets; sourceTree = "<group>"; };
 		13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ReactNativeChat/Info.plist; sourceTree = "<group>"; };
 		13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ReactNativeChat/main.m; sourceTree = "<group>"; };
-		1BD00A86B037F3449D36DEB5 /* libPods-ReactNativeChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat.a"; sourceTree = BUILT_PRODUCTS_DIR; };
-		2CE86F5913AAD81AF5C4F74A /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; sourceTree = "<group>"; };
-		3C5AC1DDF78903A0E0DCE06E /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; sourceTree = "<group>"; };
+		52FD02A97003546BD29ED1F1 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; sourceTree = "<group>"; };
 		67D5C3A6A7FA417C8A853FC1 /* GTAmericaExp-Light.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Light.otf"; path = "../assets/fonts/GTAmericaExp-Light.otf"; sourceTree = "<group>"; };
+		79C70276BBEA634D78F5CE7F /* Pods-ReactNativeChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.release.xcconfig"; sourceTree = "<group>"; };
 		81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = ReactNativeChat/LaunchScreen.storyboard; sourceTree = "<group>"; };
-		8A701CE31FD631756636DC73 /* libPods-ReactNativeChat-ReactNativeChatTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat-ReactNativeChatTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		8C7003903C1E4957824899BB /* GTAmericaExp-Regular.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Regular.otf"; path = "../assets/fonts/GTAmericaExp-Regular.otf"; sourceTree = "<group>"; };
+		98FAF3CC3CFF9F89A372A57F /* libPods-ReactNativeChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Thin.otf"; path = "../assets/fonts/GTAmericaExp-Thin.otf"; sourceTree = "<group>"; };
 		A5AAD008CBD84A6CAEB9AC97 /* GTAmericaExp-Bold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Bold.otf"; path = "../assets/fonts/GTAmericaExp-Bold.otf"; sourceTree = "<group>"; };
 		AE65058949E14DA5A2D5435D /* GTAmericaExp-Medium.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Medium.otf"; path = "../assets/fonts/GTAmericaExp-Medium.otf"; sourceTree = "<group>"; };
-		C95F4AF390CEA0E1D68B4B91 /* Pods-ReactNativeChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.debug.xcconfig"; sourceTree = "<group>"; };
+		B37FE0EDA609BB155B22BDDE /* Pods-ReactNativeChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.debug.xcconfig"; sourceTree = "<group>"; };
+		B7FD7A35A63E32551C5F2579 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; sourceTree = "<group>"; };
+		D7846B56920B8E09BA63C843 /* libPods-ReactNativeChat-ReactNativeChatTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat-ReactNativeChatTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
 		ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; };
 /* End PBXFileReference section */
@@ -63,7 +63,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				60953A1918F99DB6992E7324 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */,
+				DD56F9D34944614CB5A2BC9C /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -71,7 +71,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				71B0ACFFCD2E81B6C714ED34 /* libPods-ReactNativeChat.a in Frameworks */,
+				A2CDC0C5FF0B4238EDF00E70 /* libPods-ReactNativeChat.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -114,8 +114,8 @@
 			children = (
 				ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
 				ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
-				1BD00A86B037F3449D36DEB5 /* libPods-ReactNativeChat.a */,
-				8A701CE31FD631756636DC73 /* libPods-ReactNativeChat-ReactNativeChatTests.a */,
+				98FAF3CC3CFF9F89A372A57F /* libPods-ReactNativeChat.a */,
+				D7846B56920B8E09BA63C843 /* libPods-ReactNativeChat-ReactNativeChatTests.a */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
@@ -167,10 +167,10 @@
 		EC29677F0A49C2946A495A33 /* Pods */ = {
 			isa = PBXGroup;
 			children = (
-				C95F4AF390CEA0E1D68B4B91 /* Pods-ReactNativeChat.debug.xcconfig */,
-				0BA2C82CAFF5F2E69E3B49E9 /* Pods-ReactNativeChat.release.xcconfig */,
-				2CE86F5913AAD81AF5C4F74A /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */,
-				3C5AC1DDF78903A0E0DCE06E /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */,
+				B37FE0EDA609BB155B22BDDE /* Pods-ReactNativeChat.debug.xcconfig */,
+				79C70276BBEA634D78F5CE7F /* Pods-ReactNativeChat.release.xcconfig */,
+				B7FD7A35A63E32551C5F2579 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */,
+				52FD02A97003546BD29ED1F1 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */,
 			);
 			path = Pods;
 			sourceTree = "<group>";
@@ -182,11 +182,11 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "ReactNativeChatTests" */;
 			buildPhases = (
-				AAC7D3DC030D4C07E1FAB9DE /* [CP] Check Pods Manifest.lock */,
+				42ECEAF38FD51DAF5C36C421 /* [CP] Check Pods Manifest.lock */,
 				00E356EA1AD99517003FC87E /* Sources */,
 				00E356EB1AD99517003FC87E /* Frameworks */,
 				00E356EC1AD99517003FC87E /* Resources */,
-				7A9C37AA593615200704B167 /* [CP] Copy Pods Resources */,
+				47BFF8CFFA227C730E0A25B9 /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -202,13 +202,13 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ReactNativeChat" */;
 			buildPhases = (
-				8CDB7E879D6E05C91FBF0ECF /* [CP] Check Pods Manifest.lock */,
+				2B36DE1F24E920095A8809AF /* [CP] Check Pods Manifest.lock */,
 				FD10A7F022414F080027D42C /* Start Packager */,
 				13B07F871A680F5B00A75B9A /* Sources */,
 				13B07F8C1A680F5B00A75B9A /* Frameworks */,
 				13B07F8E1A680F5B00A75B9A /* Resources */,
 				00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
-				8018F2812F21A5A4857D8808 /* [CP] Copy Pods Resources */,
+				14A7FEF4B64F88E63C4E8ED4 /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -235,7 +235,7 @@
 					13B07F861A680F5B00A75B9A = {
 						DevelopmentTeam = 368M544MTT;
 						LastSwiftMigration = 1120;
-						ProvisioningStyle = Manual;
+						ProvisioningStyle = Automatic;
 					};
 				};
 			};
@@ -297,13 +297,13 @@
 			shellPath = /bin/sh;
 			shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh";
 		};
-		7A9C37AA593615200704B167 /* [CP] Copy Pods Resources */ = {
+		14A7FEF4B64F88E63C4E8ED4 /* [CP] Copy Pods Resources */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 			);
 			inputPaths = (
-				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh",
+				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAAutomationActions.plist",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerContentView.xib",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerView.xib",
@@ -422,16 +422,60 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh\"\n";
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
-		8018F2812F21A5A4857D8808 /* [CP] Copy Pods Resources */ = {
+		2B36DE1F24E920095A8809AF /* [CP] Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 			);
+			inputFileListPaths = (
+			);
 			inputPaths = (
-				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh",
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
+		42ECEAF38FD51DAF5C36C421 /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-ReactNativeChatTests-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
+		47BFF8CFFA227C730E0A25B9 /* [CP] Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAAutomationActions.plist",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerContentView.xib",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerView.xib",
@@ -550,51 +594,7 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh\"\n";
-			showEnvVarsInLog = 0;
-		};
-		8CDB7E879D6E05C91FBF0ECF /* [CP] Check Pods Manifest.lock */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputFileListPaths = (
-			);
-			inputPaths = (
-				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
-				"${PODS_ROOT}/Manifest.lock",
-			);
-			name = "[CP] Check Pods Manifest.lock";
-			outputFileListPaths = (
-			);
-			outputPaths = (
-				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-checkManifestLockResult.txt",
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
-			showEnvVarsInLog = 0;
-		};
-		AAC7D3DC030D4C07E1FAB9DE /* [CP] Check Pods Manifest.lock */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputFileListPaths = (
-			);
-			inputPaths = (
-				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
-				"${PODS_ROOT}/Manifest.lock",
-			);
-			name = "[CP] Check Pods Manifest.lock";
-			outputFileListPaths = (
-			);
-			outputPaths = (
-				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-ReactNativeChatTests-checkManifestLockResult.txt",
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
 		FD10A7F022414F080027D42C /* Start Packager */ = {
@@ -649,7 +649,7 @@
 /* Begin XCBuildConfiguration section */
 		00E356F61AD99517003FC87E /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 2CE86F5913AAD81AF5C4F74A /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */;
+			baseConfigurationReference = B7FD7A35A63E32551C5F2579 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
@@ -658,7 +658,7 @@
 					"$(inherited)",
 				);
 				INFOPLIST_FILE = ReactNativeChatTests/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 10.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				OTHER_LDFLAGS = (
 					"-ObjC",
@@ -673,13 +673,13 @@
 		};
 		00E356F71AD99517003FC87E /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 3C5AC1DDF78903A0E0DCE06E /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */;
+			baseConfigurationReference = 52FD02A97003546BD29ED1F1 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
 				COPY_PHASE_STRIP = NO;
 				INFOPLIST_FILE = ReactNativeChatTests/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 10.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				OTHER_LDFLAGS = (
 					"-ObjC",
@@ -694,14 +694,14 @@
 		};
 		13B07F941A680F5B00A75B9A /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = C95F4AF390CEA0E1D68B4B91 /* Pods-ReactNativeChat.debug.xcconfig */;
+			baseConfigurationReference = B37FE0EDA609BB155B22BDDE /* Pods-ReactNativeChat.debug.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
-				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 = "";
 				ENABLE_BITCODE = NO;
 				INFOPLIST_FILE = ReactNativeChat/Info.plist;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -713,7 +713,7 @@
 				);
 				PRODUCT_BUNDLE_IDENTIFIER = com.chat.expensify.chat;
 				PRODUCT_NAME = Chat;
-				PROVISIONING_PROFILE_SPECIFIER = chat_expensify_appstore;
+				PROVISIONING_PROFILE_SPECIFIER = "";
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
@@ -723,14 +723,14 @@
 		};
 		13B07F951A680F5B00A75B9A /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 0BA2C82CAFF5F2E69E3B49E9 /* Pods-ReactNativeChat.release.xcconfig */;
+			baseConfigurationReference = 79C70276BBEA634D78F5CE7F /* Pods-ReactNativeChat.release.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
-				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 = "";
 				INFOPLIST_FILE = ReactNativeChat/Info.plist;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
 				MARKETING_VERSION = 1.0.0;
@@ -741,7 +741,7 @@
 				);
 				PRODUCT_BUNDLE_IDENTIFIER = com.chat.expensify.chat;
 				PRODUCT_NAME = Chat;
-				PROVISIONING_PROFILE_SPECIFIER = chat_expensify_appstore;
+				PROVISIONING_PROFILE_SPECIFIER = "";
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
 				VERSIONING_SYSTEM = "apple-generic";
@@ -795,7 +795,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 10.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
 				LIBRARY_SEARCH_PATHS = (
 					"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
@@ -848,7 +848,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 10.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
 				LIBRARY_SEARCH_PATHS = (
 					"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
diff --git a/src/lib/Ion.js b/src/lib/Ion.js
index 97c56d9ab7ab..c25088b3d56a 100644
--- a/src/lib/Ion.js
+++ b/src/lib/Ion.js
@@ -3,6 +3,7 @@ import AsyncStorage from '@react-native-community/async-storage';
 import addStorageEventHandler from './addStorageEventHandler';
 import Str from './Str';
 import IONKEYS from '../IONKEYS';
+import promiseAllSettled from './promiseAllSettled';
 
 // Keeps track of the last connectionID that was used so we can keep incrementing it
 let lastConnectionID = 0;
@@ -224,11 +225,11 @@ function clear() {
     // and collect all the keys whose values are NOT null or undefined
     const currentKeySet = (
         function dfs(obj, parentKey = '', keySet = []) {
-            return _.map(Object.keys(obj), key => get(key)
-                .then((val) => {
-                    if (typeof val === 'object') {
-                        keySet.concat(dfs(val, `${parentKey}.${key}`, keySet));
-                    } else if (val !== null && val !== undefined) {
+            return _.map(obj, (value, key) => get(value)
+                .then((ionValue) => {
+                    if (typeof value === 'object') {
+                        keySet.concat(dfs(value, `${parentKey}.${key}`, keySet));
+                    } else if (ionValue !== null && ionValue !== undefined) {
                         keySet.push(key);
                     }
                     return keySet;
@@ -237,7 +238,10 @@ function clear() {
     )(IONKEYS);
 
     // Await all the keys from Ion.get
-    Promise.allSettled(currentKeySet)
+    promiseAllSettled(currentKeySet)
+        .then(promiseSet => _.filter(promiseSet, promise => promise.status === 'fulfilled'))
+        .then(promiseResults => _.map(promiseResults, promiseResult => promiseResult.value))
+        .then(keySet => _.union(...keySet))
         .then((keys) => {
             // Then clear the store
             AsyncStorage.clear();
diff --git a/src/lib/Notification/PushNotification/index.js b/src/lib/Notification/PushNotification/index.js
index 31d68db189a3..79b681576b3d 100644
--- a/src/lib/Notification/PushNotification/index.js
+++ b/src/lib/Notification/PushNotification/index.js
@@ -1,4 +1,5 @@
 // Push notifications are only supported on mobile, so we'll just noop here
 export default {
-    enableUserNotifications: () => {},
+    enablePushNotifications: () => {},
+    bindActionToPushNotification: () => {},
 };

From 26b7b7c093da61a9b7174c4a736613c8614db5a1 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 30 Sep 2020 11:07:42 -0700
Subject: [PATCH 19/70] fix Ion.clear to never pass object key to AsyncStorage

---
 src/lib/Ion.js | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/src/lib/Ion.js b/src/lib/Ion.js
index c25088b3d56a..7daa934a2dde 100644
--- a/src/lib/Ion.js
+++ b/src/lib/Ion.js
@@ -225,15 +225,15 @@ function clear() {
     // and collect all the keys whose values are NOT null or undefined
     const currentKeySet = (
         function dfs(obj, parentKey = '', keySet = []) {
-            return _.map(obj, (value, key) => get(value)
-                .then((ionValue) => {
-                    if (typeof value === 'object') {
-                        keySet.concat(dfs(value, `${parentKey}.${key}`, keySet));
-                    } else if (ionValue !== null && ionValue !== undefined) {
-                        keySet.push(key);
-                    }
-                    return keySet;
-                }));
+            return _.map(obj, (value, key) => typeof value === 'object'
+                ? keySet.concat(dfs(value, `${parentKey}.${key}`, keySet))
+                : get(value)
+                    .then((ionValue) => {
+                        if (ionValue !== null && ionValue !== undefined) {
+                            keySet.push(key);
+                        }
+                        return keySet;
+                    }));
         }
     )(IONKEYS);
 

From 16a9ef3fc7f05a663886f36746dfdbdd87439b0a Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 30 Sep 2020 11:09:10 -0700
Subject: [PATCH 20/70] fix JS style

---
 src/lib/Ion.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/lib/Ion.js b/src/lib/Ion.js
index 7daa934a2dde..c849df7e982a 100644
--- a/src/lib/Ion.js
+++ b/src/lib/Ion.js
@@ -225,7 +225,7 @@ function clear() {
     // and collect all the keys whose values are NOT null or undefined
     const currentKeySet = (
         function dfs(obj, parentKey = '', keySet = []) {
-            return _.map(obj, (value, key) => typeof value === 'object'
+            return _.map(obj, (value, key) => (typeof value === 'object'
                 ? keySet.concat(dfs(value, `${parentKey}.${key}`, keySet))
                 : get(value)
                     .then((ionValue) => {
@@ -233,7 +233,7 @@ function clear() {
                             keySet.push(key);
                         }
                         return keySet;
-                    }));
+                    })));
         }
     )(IONKEYS);
 

From b58ff7d9e279b23b79beb3709c2d755d088afd59 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 30 Sep 2020 13:23:16 -0700
Subject: [PATCH 21/70] reconfigure iOS push notifications

---
 ios/ReactNativeChat.xcodeproj/project.pbxproj | 114 ++++++++++--------
 1 file changed, 61 insertions(+), 53 deletions(-)

diff --git a/ios/ReactNativeChat.xcodeproj/project.pbxproj b/ios/ReactNativeChat.xcodeproj/project.pbxproj
index 50ef63a05776..5172586422b0 100644
--- a/ios/ReactNativeChat.xcodeproj/project.pbxproj
+++ b/ios/ReactNativeChat.xcodeproj/project.pbxproj
@@ -12,13 +12,14 @@
 		13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
 		13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
 		13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
+		2A03B7FAB1989524E18D4F97 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C89947EEA39BBF00B7EF26A6 /* libPods-ReactNativeChat-ReactNativeChatTests.a */; };
 		4912B60FA66C4604A56AD575 /* GTAmericaExp-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 8C7003903C1E4957824899BB /* GTAmericaExp-Regular.otf */; };
 		49F157C845CE4D56AA72B80E /* GTAmericaExp-Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = 67D5C3A6A7FA417C8A853FC1 /* GTAmericaExp-Light.otf */; };
 		7F7721B764D444D6861A9B39 /* GTAmericaExp-Thin.otf in Resources */ = {isa = PBXBuildFile; fileRef = A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */; };
 		81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
-		A2CDC0C5FF0B4238EDF00E70 /* libPods-ReactNativeChat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 98FAF3CC3CFF9F89A372A57F /* libPods-ReactNativeChat.a */; };
+		B50BF560D54753753D2B7522 /* libPods-ReactNativeChat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 59D3EEA5B40E14473132477B /* libPods-ReactNativeChat.a */; };
 		CC07911F22C2437DA6708BD2 /* GTAmericaExp-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = AE65058949E14DA5A2D5435D /* GTAmericaExp-Medium.otf */; };
-		DD56F9D34944614CB5A2BC9C /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D7846B56920B8E09BA63C843 /* libPods-ReactNativeChat-ReactNativeChatTests.a */; };
+		E9DF872D2525201700607FDC /* AirshipConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9DF872C2525201700607FDC /* AirshipConfig.plist */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -42,18 +43,20 @@
 		13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = ReactNativeChat/Images.xcassets; sourceTree = "<group>"; };
 		13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ReactNativeChat/Info.plist; sourceTree = "<group>"; };
 		13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ReactNativeChat/main.m; sourceTree = "<group>"; };
-		52FD02A97003546BD29ED1F1 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; sourceTree = "<group>"; };
+		20AD5495BB19D0CE21D0F356 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; sourceTree = "<group>"; };
+		2791C2E4A4F1D9EC4BCA1311 /* Pods-ReactNativeChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.release.xcconfig"; sourceTree = "<group>"; };
+		47B12692BCB071BA3DA6B107 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; sourceTree = "<group>"; };
+		59D3EEA5B40E14473132477B /* libPods-ReactNativeChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		67D5C3A6A7FA417C8A853FC1 /* GTAmericaExp-Light.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Light.otf"; path = "../assets/fonts/GTAmericaExp-Light.otf"; sourceTree = "<group>"; };
-		79C70276BBEA634D78F5CE7F /* Pods-ReactNativeChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.release.xcconfig"; sourceTree = "<group>"; };
 		81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = ReactNativeChat/LaunchScreen.storyboard; sourceTree = "<group>"; };
 		8C7003903C1E4957824899BB /* GTAmericaExp-Regular.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Regular.otf"; path = "../assets/fonts/GTAmericaExp-Regular.otf"; sourceTree = "<group>"; };
-		98FAF3CC3CFF9F89A372A57F /* libPods-ReactNativeChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Thin.otf"; path = "../assets/fonts/GTAmericaExp-Thin.otf"; sourceTree = "<group>"; };
 		A5AAD008CBD84A6CAEB9AC97 /* GTAmericaExp-Bold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Bold.otf"; path = "../assets/fonts/GTAmericaExp-Bold.otf"; sourceTree = "<group>"; };
 		AE65058949E14DA5A2D5435D /* GTAmericaExp-Medium.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Medium.otf"; path = "../assets/fonts/GTAmericaExp-Medium.otf"; sourceTree = "<group>"; };
-		B37FE0EDA609BB155B22BDDE /* Pods-ReactNativeChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.debug.xcconfig"; sourceTree = "<group>"; };
-		B7FD7A35A63E32551C5F2579 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; sourceTree = "<group>"; };
-		D7846B56920B8E09BA63C843 /* libPods-ReactNativeChat-ReactNativeChatTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat-ReactNativeChatTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		C19B554C5F616C1AD56D7A56 /* Pods-ReactNativeChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.debug.xcconfig"; sourceTree = "<group>"; };
+		C89947EEA39BBF00B7EF26A6 /* libPods-ReactNativeChat-ReactNativeChatTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat-ReactNativeChatTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		E9DF86F625251E4A00607FDC /* Chat.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Chat.entitlements; path = ReactNativeChat/Chat.entitlements; sourceTree = "<group>"; };
+		E9DF872C2525201700607FDC /* AirshipConfig.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = AirshipConfig.plist; sourceTree = "<group>"; };
 		ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
 		ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; };
 /* End PBXFileReference section */
@@ -63,7 +66,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				DD56F9D34944614CB5A2BC9C /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */,
+				2A03B7FAB1989524E18D4F97 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -71,7 +74,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				A2CDC0C5FF0B4238EDF00E70 /* libPods-ReactNativeChat.a in Frameworks */,
+				B50BF560D54753753D2B7522 /* libPods-ReactNativeChat.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -98,6 +101,8 @@
 		13B07FAE1A68108700A75B9A /* ReactNativeChat */ = {
 			isa = PBXGroup;
 			children = (
+				E9DF872C2525201700607FDC /* AirshipConfig.plist */,
+				E9DF86F625251E4A00607FDC /* Chat.entitlements */,
 				008F07F21AC5B25A0029DE68 /* main.jsbundle */,
 				13B07FAF1A68108700A75B9A /* AppDelegate.h */,
 				13B07FB01A68108700A75B9A /* AppDelegate.m */,
@@ -114,8 +119,8 @@
 			children = (
 				ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
 				ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
-				98FAF3CC3CFF9F89A372A57F /* libPods-ReactNativeChat.a */,
-				D7846B56920B8E09BA63C843 /* libPods-ReactNativeChat-ReactNativeChatTests.a */,
+				59D3EEA5B40E14473132477B /* libPods-ReactNativeChat.a */,
+				C89947EEA39BBF00B7EF26A6 /* libPods-ReactNativeChat-ReactNativeChatTests.a */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
@@ -167,10 +172,10 @@
 		EC29677F0A49C2946A495A33 /* Pods */ = {
 			isa = PBXGroup;
 			children = (
-				B37FE0EDA609BB155B22BDDE /* Pods-ReactNativeChat.debug.xcconfig */,
-				79C70276BBEA634D78F5CE7F /* Pods-ReactNativeChat.release.xcconfig */,
-				B7FD7A35A63E32551C5F2579 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */,
-				52FD02A97003546BD29ED1F1 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */,
+				C19B554C5F616C1AD56D7A56 /* Pods-ReactNativeChat.debug.xcconfig */,
+				2791C2E4A4F1D9EC4BCA1311 /* Pods-ReactNativeChat.release.xcconfig */,
+				47B12692BCB071BA3DA6B107 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */,
+				20AD5495BB19D0CE21D0F356 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */,
 			);
 			path = Pods;
 			sourceTree = "<group>";
@@ -182,11 +187,11 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "ReactNativeChatTests" */;
 			buildPhases = (
-				42ECEAF38FD51DAF5C36C421 /* [CP] Check Pods Manifest.lock */,
+				AE43224DB5FDB1A6AD5D05C0 /* [CP] Check Pods Manifest.lock */,
 				00E356EA1AD99517003FC87E /* Sources */,
 				00E356EB1AD99517003FC87E /* Frameworks */,
 				00E356EC1AD99517003FC87E /* Resources */,
-				47BFF8CFFA227C730E0A25B9 /* [CP] Copy Pods Resources */,
+				9549BADC0A8863C4AE3E64ED /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -202,13 +207,13 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ReactNativeChat" */;
 			buildPhases = (
-				2B36DE1F24E920095A8809AF /* [CP] Check Pods Manifest.lock */,
+				65C9D025A7C6606100A5D923 /* [CP] Check Pods Manifest.lock */,
 				FD10A7F022414F080027D42C /* Start Packager */,
 				13B07F871A680F5B00A75B9A /* Sources */,
 				13B07F8C1A680F5B00A75B9A /* Frameworks */,
 				13B07F8E1A680F5B00A75B9A /* Resources */,
 				00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
-				14A7FEF4B64F88E63C4E8ED4 /* [CP] Copy Pods Resources */,
+				C70727B1CBBD0122615E6118 /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -273,6 +278,7 @@
 				81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
 				13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
 				11FA3546D54246CD8F819152 /* GTAmericaExp-Bold.otf in Resources */,
+				E9DF872D2525201700607FDC /* AirshipConfig.plist in Resources */,
 				49F157C845CE4D56AA72B80E /* GTAmericaExp-Light.otf in Resources */,
 				CC07911F22C2437DA6708BD2 /* GTAmericaExp-Medium.otf in Resources */,
 				4912B60FA66C4604A56AD575 /* GTAmericaExp-Regular.otf in Resources */,
@@ -297,13 +303,35 @@
 			shellPath = /bin/sh;
 			shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh";
 		};
-		14A7FEF4B64F88E63C4E8ED4 /* [CP] Copy Pods Resources */ = {
+		65C9D025A7C6606100A5D923 /* [CP] Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 			);
+			inputFileListPaths = (
+			);
 			inputPaths = (
-				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh",
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
+		9549BADC0A8863C4AE3E64ED /* [CP] Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAAutomationActions.plist",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerContentView.xib",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerView.xib",
@@ -422,32 +450,10 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh\"\n";
-			showEnvVarsInLog = 0;
-		};
-		2B36DE1F24E920095A8809AF /* [CP] Check Pods Manifest.lock */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputFileListPaths = (
-			);
-			inputPaths = (
-				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
-				"${PODS_ROOT}/Manifest.lock",
-			);
-			name = "[CP] Check Pods Manifest.lock";
-			outputFileListPaths = (
-			);
-			outputPaths = (
-				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-checkManifestLockResult.txt",
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
-		42ECEAF38FD51DAF5C36C421 /* [CP] Check Pods Manifest.lock */ = {
+		AE43224DB5FDB1A6AD5D05C0 /* [CP] Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -469,13 +475,13 @@
 			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
 			showEnvVarsInLog = 0;
 		};
-		47BFF8CFFA227C730E0A25B9 /* [CP] Copy Pods Resources */ = {
+		C70727B1CBBD0122615E6118 /* [CP] Copy Pods Resources */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 			);
 			inputPaths = (
-				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh",
+				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAAutomationActions.plist",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerContentView.xib",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerView.xib",
@@ -594,7 +600,7 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh\"\n";
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
 		FD10A7F022414F080027D42C /* Start Packager */ = {
@@ -649,7 +655,7 @@
 /* Begin XCBuildConfiguration section */
 		00E356F61AD99517003FC87E /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = B7FD7A35A63E32551C5F2579 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */;
+			baseConfigurationReference = 47B12692BCB071BA3DA6B107 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
@@ -673,7 +679,7 @@
 		};
 		00E356F71AD99517003FC87E /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 52FD02A97003546BD29ED1F1 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */;
+			baseConfigurationReference = 20AD5495BB19D0CE21D0F356 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
@@ -694,10 +700,11 @@
 		};
 		13B07F941A680F5B00A75B9A /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = B37FE0EDA609BB155B22BDDE /* Pods-ReactNativeChat.debug.xcconfig */;
+			baseConfigurationReference = C19B554C5F616C1AD56D7A56 /* Pods-ReactNativeChat.debug.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
+				CODE_SIGN_ENTITLEMENTS = ReactNativeChat/Chat.entitlements;
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 3;
@@ -723,10 +730,11 @@
 		};
 		13B07F951A680F5B00A75B9A /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 79C70276BBEA634D78F5CE7F /* Pods-ReactNativeChat.release.xcconfig */;
+			baseConfigurationReference = 2791C2E4A4F1D9EC4BCA1311 /* Pods-ReactNativeChat.release.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
+				CODE_SIGN_ENTITLEMENTS = ReactNativeChat/Chat.entitlements;
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 3;

From 1d181aef51abd7ba2b308cb6a228305092e21fe1 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 30 Sep 2020 22:52:28 -0700
Subject: [PATCH 22/70] fix Ion.clear, for real this time

---
 src/lib/Ion.js | 43 ++++++++++++++++++++++++++-----------------
 1 file changed, 26 insertions(+), 17 deletions(-)

diff --git a/src/lib/Ion.js b/src/lib/Ion.js
index c849df7e982a..0ff716529648 100644
--- a/src/lib/Ion.js
+++ b/src/lib/Ion.js
@@ -218,30 +218,39 @@ function multiSet(data) {
 }
 
 /**
- * Clear out all the data in the store
+ * Perform a depth-first search of all the IONKEYS, and collect all the keys whose values are NOT null or undefined
+ *
+ * @returns {Promise<Array<string>>}
  */
-function clear() {
-    // Perform a depth-first search of all the IONKEYS,
-    // and collect all the keys whose values are NOT null or undefined
+function getCurrentKeySet() {
     const currentKeySet = (
-        function dfs(obj, parentKey = '', keySet = []) {
-            return _.map(obj, (value, key) => (typeof value === 'object'
-                ? keySet.concat(dfs(value, `${parentKey}.${key}`, keySet))
-                : get(value)
-                    .then((ionValue) => {
-                        if (ionValue !== null && ionValue !== undefined) {
-                            keySet.push(key);
+        function dfs(keyObj) {
+            return _.reduce(keyObj, (keySet, key) => ((typeof key === 'object')
+                ? _.union(keySet, dfs(key))
+                : _.union(keySet, [get(key)
+                    .then((ionVal) => {
+                        if (ionVal === null || ionVal === undefined) {
+                            return Promise.reject();
                         }
-                        return keySet;
-                    })));
+                        return key;
+                    })
+                ])
+            ), []);
         }
     )(IONKEYS);
 
+    // Wait for all promises generated by the recursive function to finish, then filter and extract the results
+    return promiseAllSettled(currentKeySet)
+        .then(keySet => _.filter(keySet, key => key.status === 'fulfilled'))
+        .then(keySet => _.flatten(_.map(keySet, key => key.value)));
+}
+
+/**
+ * Clear out all the data in the store
+ */
+function clear() {
     // Await all the keys from Ion.get
-    promiseAllSettled(currentKeySet)
-        .then(promiseSet => _.filter(promiseSet, promise => promise.status === 'fulfilled'))
-        .then(promiseResults => _.map(promiseResults, promiseResult => promiseResult.value))
-        .then(keySet => _.union(...keySet))
+    getCurrentKeySet()
         .then((keys) => {
             // Then clear the store
             AsyncStorage.clear();

From 28809b17cc4c55719f459cc8dff728cd6a84253c Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 30 Sep 2020 23:04:48 -0700
Subject: [PATCH 23/70] add console debug for subscribing/unsubscribing from
 push notifications

---
 src/lib/Notification/PushNotification/index.native.js | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index 9877dccee074..790eea23db8d 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -48,9 +48,14 @@ function enablePushNotifications() {
             Ion.connect({
                 key: IONKEYS.SESSION,
                 callback: (sessionData) => {
+                    const accountID = sessionData?.accountID.toString() || undefined;
+                    console.debug(`[PUSH_NOTIFICATION] ${accountID
+                        ? `Subscribing to push notifications for accountID ${accountID}`
+                        : 'Unsubscribing from push notifications'}.`);
+
                     // This will register this device with the named user associated with this accountID,
                     // or clear the the named user (deregister this device) if sessionData.accountID is undefined
-                    UrbanAirship.setNamedUser(sessionData.accountID.toString() || undefined);
+                    UrbanAirship.setNamedUser(accountID);
                 }
             });
         })

From 29a4599f8bd6aaff410634298d4f2b8a836a5579 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Thu, 1 Oct 2020 15:25:36 -0700
Subject: [PATCH 24/70] success, receiving push notifications through our API
 and cli tools

---
 src/lib/Notification/PushNotification/index.native.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index 790eea23db8d..542ec29e3e43 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -48,7 +48,7 @@ function enablePushNotifications() {
             Ion.connect({
                 key: IONKEYS.SESSION,
                 callback: (sessionData) => {
-                    const accountID = sessionData?.accountID.toString() || undefined;
+                    const accountID = sessionData?.accountID.toString() || null;
                     console.debug(`[PUSH_NOTIFICATION] ${accountID
                         ? `Subscribing to push notifications for accountID ${accountID}`
                         : 'Unsubscribing from push notifications'}.`);

From e4016e737681be30e4aaf892eea61385d019905f Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Fri, 2 Oct 2020 00:01:14 -0700
Subject: [PATCH 25/70] setup callbacks for notificationResponse events as well
 as PushReceived

---
 .../PushNotification/index.native.js          | 79 +++++++++++++------
 src/lib/actions/Report.js                     |  8 +-
 2 files changed, 60 insertions(+), 27 deletions(-)

diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index 542ec29e3e43..ae1151fecd6b 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -1,3 +1,4 @@
+import _ from 'underscore';
 import {AppState} from 'react-native';
 import {UrbanAirship, EventType} from 'urbanairship-react-native';
 import Ion from '../../Ion';
@@ -5,35 +6,58 @@ import IONKEYS from '../../../IONKEYS';
 
 /* ====== Private Functions ====== */
 
-const notificationTypeActionMap = {};
+const notificationEventActionMap = {};
 
 /**
- * Setup listener for push notifications, and trigger any bound actions.
+ * Handle a push notification event, and trigger and bound actions.
+ *
+ * @param {string} eventType
+ * @param {object} notification
+ */
+function pushNotificationEventCallback(eventType, notification) {
+    const actionMap = notificationEventActionMap[eventType];
+    const payload = notification.extras?.payload;
+
+    console.debug(`[PUSH_NOTIFICATION] ${eventType} - {
+                title: ${notification.title},
+                message: ${notification.alert},
+                payload: ${payload}
+            }`);
+
+    // If a push notification is received while the app is in foreground,
+    // we'll assume pusher is connected so we'll ignore this push notification
+    if (AppState.currentState === 'active') {
+        console.debug('[PUSH_NOTIFICATION] App is in foreground, ignoring push notification.');
+        return;
+    }
+
+    if (!payload?.type) {
+        console.debug('[PUSH_NOTIFICATION] Notification of unknown type received.');
+        return;
+    }
+
+    if (!actionMap[payload.type]) {
+        console.debug(`[PUSH_NOTIFICATION] No callback set up: {
+                    event: ${eventType},
+                    notification type: ${payload.type},
+                }`);
+        return;
+    }
+
+    const action = actionMap[payload.type];
+    action(payload);
+}
+
+/**
+ * Setup listener for push notification events.
  */
 function setupPushNotificationCallbacks() {
     UrbanAirship.addListener(EventType.PushReceived, (notification) => {
-        console.debug(`[PUSH_NOTIFICATION] Push received - {
-            title: ${notification.title},
-            message: ${notification.message},
-            payload: ${notification.payload}
-        }`);
-
-        // If app is in foreground,  we'll assume pusher is connected so we'll ignore this push notification
-        if (AppState.currentState === 'active') {
-            console.debug('[PUSH_NOTIFICATION] App is in foreground, ignoring push notification.');
-            return;
-        }
-        if (!notification.payload.type) {
-            console.debug('[PUSH_NOTIFICATION] Notification of unknown type received, ignoring.');
-            return;
-        }
-        if (!notificationTypeActionMap[notification.payload.type]) {
-            console.debug(`[PUSH_NOTIFICATION] No callback setup for notification type: ${notification.payload.type}`);
-            return;
-        }
+        pushNotificationEventCallback(EventType.PushReceived, notification);
+    });
 
-        const action = notificationTypeActionMap[notification.payload.type].action;
-        action(...notification.payload);
+    UrbanAirship.addListener(EventType.NotificationResponse, (event) => {
+        pushNotificationEventCallback(EventType.NotificationResponse, event.notification);
     });
 }
 
@@ -71,9 +95,14 @@ function enablePushNotifications() {
  *
  * @param {string} notificationType
  * @param {Function} action
+ * @param {string?} triggerEvent - The event that should trigger this action. Should be one of UrbanAirship.EventType
  */
-function bindActionToPushNotification(notificationType, action) {
-    notificationTypeActionMap[notificationType] = action;
+function bindActionToPushNotification(notificationType, action, triggerEvent = EventType.PushReceived) {
+    const event = _.contains(_.values(EventType), triggerEvent) ? triggerEvent : EventType.PushReceived;
+    notificationEventActionMap[event] = {
+        ...notificationEventActionMap[event],
+        [notificationType]: action,
+    };
 }
 
 export default {
diff --git a/src/lib/actions/Report.js b/src/lib/actions/Report.js
index 941473353404..9d39b1eccf70 100644
--- a/src/lib/actions/Report.js
+++ b/src/lib/actions/Report.js
@@ -231,10 +231,14 @@ function subscribeToReportCommentEvents() {
         updateReportWithNewAction(pushJSON.reportID, pushJSON.reportAction);
     });
 
-    PushNotification.bindActionToPushNotification(NotificationType.REPORT.COMMENT, (payload) => {
-        const {reportID, reportAction} = payload;
+    PushNotification.bindActionToPushNotification(NotificationType.REPORT.COMMENT, ({reportID, reportAction}) => {
         updateReportWithNewAction(reportID, reportAction);
     });
+
+    // Open correct report when push notification is clicked
+    PushNotification.bindActionToPushNotification(NotificationType.REPORT.COMMENT, ({reportID}) => {
+        redirect(reportID);
+    }, 'notificationResponse');
 }
 
 /**

From 2243eaf679d3c61cc75c211a3551c7d2e70e7628 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Fri, 2 Oct 2020 16:05:58 -0700
Subject: [PATCH 26/70] only setNamedUser if accountID has changed

---
 src/Expensify.js                              |  2 +-
 src/lib/Ion.js                                |  2 +-
 .../Notification/LocalNotification/index.js   |  2 --
 .../PushNotification/index.native.js          | 22 +++++++++++--------
 src/lib/actions/Report.js                     |  4 ++--
 5 files changed, 17 insertions(+), 15 deletions(-)

diff --git a/src/Expensify.js b/src/Expensify.js
index a767ad3f4306..94d15de38478 100644
--- a/src/Expensify.js
+++ b/src/Expensify.js
@@ -53,7 +53,7 @@ class Expensify extends Component {
             key: IONKEYS.SESSION,
             callback: this.removeLoadingState,
         });
-        PushNotification.enablePushNotifications();
+        PushNotification.enable();
     }
 
     /**
diff --git a/src/lib/Ion.js b/src/lib/Ion.js
index 0ff716529648..72378cf86c31 100644
--- a/src/lib/Ion.js
+++ b/src/lib/Ion.js
@@ -259,7 +259,7 @@ function clear() {
         .then((keys) => {
             // Then notify all the relevant subscribers that their underlying Ion data has been deleted
             _.each(keys, (key) => {
-                keyChanged(key, undefined);
+                keyChanged(key, null);
             });
         });
 }
diff --git a/src/lib/Notification/LocalNotification/index.js b/src/lib/Notification/LocalNotification/index.js
index aca777de3338..d3a04a7bf7bd 100644
--- a/src/lib/Notification/LocalNotification/index.js
+++ b/src/lib/Notification/LocalNotification/index.js
@@ -4,8 +4,6 @@ import CONST from '../../../CONST';
 const EXPENSIFY_ICON_URL = `${CONST.CLOUDFRONT_URL}/images/favicon-2019.png`;
 const DEFAULT_DELAY = 4000;
 
-/* ====== Private Functions ====== */
-
 /**
  * Checks if the user has granted permission to show local notifications
  *
diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index ae1151fecd6b..b6b927dfaa9f 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -4,9 +4,8 @@ import {UrbanAirship, EventType} from 'urbanairship-react-native';
 import Ion from '../../Ion';
 import IONKEYS from '../../../IONKEYS';
 
-/* ====== Private Functions ====== */
-
 const notificationEventActionMap = {};
+let currentNamedUser;
 
 /**
  * Handle a push notification event, and trigger and bound actions.
@@ -61,18 +60,22 @@ function setupPushNotificationCallbacks() {
     });
 }
 
-/* ====== Public Functions ====== */
-
 /**
  * Get permissions and register this device as a named user in AirshipAPI.
  */
-function enablePushNotifications() {
+function enable() {
     UrbanAirship.enableUserPushNotifications()
         .finally(() => {
             Ion.connect({
                 key: IONKEYS.SESSION,
                 callback: (sessionData) => {
                     const accountID = sessionData?.accountID.toString() || null;
+
+                    // No need to re-subscribe if we're just re-authenticating the same account.
+                    if (accountID === currentNamedUser) {
+                        return;
+                    }
+
                     console.debug(`[PUSH_NOTIFICATION] ${accountID
                         ? `Subscribing to push notifications for accountID ${accountID}`
                         : 'Unsubscribing from push notifications'}.`);
@@ -80,10 +83,11 @@ function enablePushNotifications() {
                     // This will register this device with the named user associated with this accountID,
                     // or clear the the named user (deregister this device) if sessionData.accountID is undefined
                     UrbanAirship.setNamedUser(accountID);
+                    currentNamedUser = accountID;
                 }
             });
         })
-        .then(() => {
+        .finally(() => {
             setupPushNotificationCallbacks();
         });
 }
@@ -97,7 +101,7 @@ function enablePushNotifications() {
  * @param {Function} action
  * @param {string?} triggerEvent - The event that should trigger this action. Should be one of UrbanAirship.EventType
  */
-function bindActionToPushNotification(notificationType, action, triggerEvent = EventType.PushReceived) {
+function bind(notificationType, action, triggerEvent = EventType.PushReceived) {
     const event = _.contains(_.values(EventType), triggerEvent) ? triggerEvent : EventType.PushReceived;
     notificationEventActionMap[event] = {
         ...notificationEventActionMap[event],
@@ -106,6 +110,6 @@ function bindActionToPushNotification(notificationType, action, triggerEvent = E
 }
 
 export default {
-    enablePushNotifications,
-    bindActionToPushNotification,
+    enable,
+    bind,
 };
diff --git a/src/lib/actions/Report.js b/src/lib/actions/Report.js
index 9d39b1eccf70..be58fb3d1392 100644
--- a/src/lib/actions/Report.js
+++ b/src/lib/actions/Report.js
@@ -231,12 +231,12 @@ function subscribeToReportCommentEvents() {
         updateReportWithNewAction(pushJSON.reportID, pushJSON.reportAction);
     });
 
-    PushNotification.bindActionToPushNotification(NotificationType.REPORT.COMMENT, ({reportID, reportAction}) => {
+    PushNotification.bind(NotificationType.REPORT.COMMENT, ({reportID, reportAction}) => {
         updateReportWithNewAction(reportID, reportAction);
     });
 
     // Open correct report when push notification is clicked
-    PushNotification.bindActionToPushNotification(NotificationType.REPORT.COMMENT, ({reportID}) => {
+    PushNotification.bind(NotificationType.REPORT.COMMENT, ({reportID}) => {
         redirect(reportID);
     }, 'notificationResponse');
 }

From e5477c8b31097aa6dcc89c6d469c145ebbf52036 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Fri, 2 Oct 2020 17:51:38 -0700
Subject: [PATCH 27/70] update exports in index.js as well as index.native.js

---
 src/lib/Notification/PushNotification/index.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/lib/Notification/PushNotification/index.js b/src/lib/Notification/PushNotification/index.js
index 79b681576b3d..39361f532e5e 100644
--- a/src/lib/Notification/PushNotification/index.js
+++ b/src/lib/Notification/PushNotification/index.js
@@ -1,5 +1,5 @@
 // Push notifications are only supported on mobile, so we'll just noop here
 export default {
-    enablePushNotifications: () => {},
-    bindActionToPushNotification: () => {},
+    enable: () => {},
+    bind: () => {},
 };

From 1326414b28d69b9e005633f9681951970dc1841f Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Sat, 3 Oct 2020 10:45:02 -0700
Subject: [PATCH 28/70] use multiRemove instead of clear

---
 src/lib/Ion.js                                        | 2 +-
 src/lib/Notification/LocalNotification/index.js       | 5 ++---
 src/lib/Notification/PushNotification/index.native.js | 2 +-
 3 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/src/lib/Ion.js b/src/lib/Ion.js
index 72378cf86c31..daa012b3a807 100644
--- a/src/lib/Ion.js
+++ b/src/lib/Ion.js
@@ -253,7 +253,7 @@ function clear() {
     getCurrentKeySet()
         .then((keys) => {
             // Then clear the store
-            AsyncStorage.clear();
+            AsyncStorage.multiRemove(keys);
             return keys;
         })
         .then((keys) => {
diff --git a/src/lib/Notification/LocalNotification/index.js b/src/lib/Notification/LocalNotification/index.js
index d3a04a7bf7bd..47b55c19c885 100644
--- a/src/lib/Notification/LocalNotification/index.js
+++ b/src/lib/Notification/LocalNotification/index.js
@@ -103,8 +103,7 @@ export default {
      */
     pushReportCommentNotification({reportAction, onClick}) {
         const {person, message} = reportAction;
-        const plainTextPerson = Str.htmlDecode(person.map(f => f.text)
-            .join());
+        const plainTextPerson = Str.htmlDecode(person.map(f => f.text).join());
 
         // Specifically target the comment part of the message
         const plainTextMessage = Str.htmlDecode((message.find(f => f.type === 'COMMENT') || {}).text);
@@ -115,5 +114,5 @@ export default {
             delay: 0,
             onClick,
         });
-    }
+    },
 };
diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index b6b927dfaa9f..7578b80d28dd 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -81,7 +81,7 @@ function enable() {
                         : 'Unsubscribing from push notifications'}.`);
 
                     // This will register this device with the named user associated with this accountID,
-                    // or clear the the named user (deregister this device) if sessionData.accountID is undefined
+                    // or clear the the named user (deregister this device) if sessionData.accountID is null
                     UrbanAirship.setNamedUser(accountID);
                     currentNamedUser = accountID;
                 }

From ea04e7aba3297ffc413b03c930b28c4dffd281bc Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Sat, 3 Oct 2020 10:49:16 -0700
Subject: [PATCH 29/70] get rid of unnecessary second finally

---
 src/lib/Notification/PushNotification/index.native.js | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index 7578b80d28dd..0443be5cd91f 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -86,8 +86,6 @@ function enable() {
                     currentNamedUser = accountID;
                 }
             });
-        })
-        .finally(() => {
             setupPushNotificationCallbacks();
         });
 }

From 9f2cf119f57e1819f42e988916b94596453f64f9 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Mon, 5 Oct 2020 11:35:28 -0700
Subject: [PATCH 30/70] clean up constants

---
 src/lib/Notification/NotificationType.js                 | 5 -----
 src/lib/Notification/PushNotification/EventType.js       | 9 +++++++++
 .../Notification/PushNotification/NotificationType.js    | 9 +++++++++
 src/lib/Notification/PushNotification/index.js           | 5 +++++
 src/lib/Notification/PushNotification/index.native.js    | 3 +++
 src/lib/actions/Report.js                                | 9 ++++-----
 6 files changed, 30 insertions(+), 10 deletions(-)
 delete mode 100644 src/lib/Notification/NotificationType.js
 create mode 100644 src/lib/Notification/PushNotification/EventType.js
 create mode 100644 src/lib/Notification/PushNotification/NotificationType.js

diff --git a/src/lib/Notification/NotificationType.js b/src/lib/Notification/NotificationType.js
deleted file mode 100644
index 1a54a2770214..000000000000
--- a/src/lib/Notification/NotificationType.js
+++ /dev/null
@@ -1,5 +0,0 @@
-export default {
-    REPORT: {
-        COMMENT: 'reportComment',
-    }
-};
diff --git a/src/lib/Notification/PushNotification/EventType.js b/src/lib/Notification/PushNotification/EventType.js
new file mode 100644
index 000000000000..fdebe96a6522
--- /dev/null
+++ b/src/lib/Notification/PushNotification/EventType.js
@@ -0,0 +1,9 @@
+/**
+ * This is just a local copy of the EventType enum from 'urbanairship-react-native'.
+ * Importing it on any index.js file breaks web, so we'll export this from PushNotifcation/index.js instead.
+ * Update it as necessary.
+ */
+export default {
+    NotificationResponse: 'notificationResponse',
+    PushReceived: 'pushReceived',
+};
diff --git a/src/lib/Notification/PushNotification/NotificationType.js b/src/lib/Notification/PushNotification/NotificationType.js
new file mode 100644
index 000000000000..18d57c528731
--- /dev/null
+++ b/src/lib/Notification/PushNotification/NotificationType.js
@@ -0,0 +1,9 @@
+/**
+ * See https://github.com/Expensify/Web-Expensify/blob/master/lib/MobilePushNotifications.php for the various
+ * types of push notifications sent by our API.
+ */
+export default {
+    REPORT: {
+        COMMENT: 'reportComment',
+    }
+};
diff --git a/src/lib/Notification/PushNotification/index.js b/src/lib/Notification/PushNotification/index.js
index 39361f532e5e..50876587d73a 100644
--- a/src/lib/Notification/PushNotification/index.js
+++ b/src/lib/Notification/PushNotification/index.js
@@ -1,5 +1,10 @@
+import EventType from './EventType';
+import NotificationType from './NotificationType';
+
 // Push notifications are only supported on mobile, so we'll just noop here
 export default {
     enable: () => {},
     bind: () => {},
+    EventType,
+    NotificationType,
 };
diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index 0443be5cd91f..5a7052fe6582 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -3,6 +3,7 @@ import {AppState} from 'react-native';
 import {UrbanAirship, EventType} from 'urbanairship-react-native';
 import Ion from '../../Ion';
 import IONKEYS from '../../../IONKEYS';
+import NotificationType from './NotificationType';
 
 const notificationEventActionMap = {};
 let currentNamedUser;
@@ -110,4 +111,6 @@ function bind(notificationType, action, triggerEvent = EventType.PushReceived) {
 export default {
     enable,
     bind,
+    EventType,
+    NotificationType,
 };
diff --git a/src/lib/actions/Report.js b/src/lib/actions/Report.js
index be58fb3d1392..1635b063d6df 100644
--- a/src/lib/actions/Report.js
+++ b/src/lib/actions/Report.js
@@ -10,7 +10,6 @@ import promiseAllSettled from '../promiseAllSettled';
 import ExpensiMark from '../ExpensiMark';
 import LocalNotification from '../Notification/LocalNotification';
 import PushNotification from '../Notification/PushNotification';
-import NotificationType from '../Notification/NotificationType';
 import * as PersonalDetails from './PersonalDetails';
 import {redirect} from './App';
 import * as ActiveClientManager from '../ActiveClientManager';
@@ -227,18 +226,18 @@ function subscribeToReportCommentEvents() {
         return;
     }
 
-    Pusher.subscribe(pusherChannelName, NotificationType.REPORT.COMMENT, (pushJSON) => {
+    Pusher.subscribe(pusherChannelName, 'reportComment', (pushJSON) => {
         updateReportWithNewAction(pushJSON.reportID, pushJSON.reportAction);
     });
 
-    PushNotification.bind(NotificationType.REPORT.COMMENT, ({reportID, reportAction}) => {
+    PushNotification.bind(PushNotification.NotificationType.REPORT.COMMENT, ({reportID, reportAction}) => {
         updateReportWithNewAction(reportID, reportAction);
     });
 
     // Open correct report when push notification is clicked
-    PushNotification.bind(NotificationType.REPORT.COMMENT, ({reportID}) => {
+    PushNotification.bind(PushNotification.NotificationType.REPORT.COMMENT, ({reportID}) => {
         redirect(reportID);
-    }, 'notificationResponse');
+    }, PushNotification.EventType.NotificationResponse);
 }
 
 /**

From f1b79920300d1fe89dd4a304b031ab7a56955fe7 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Mon, 5 Oct 2020 12:53:45 -0700
Subject: [PATCH 31/70] lift Ion.connect to module top-level

---
 .../PushNotification/index.native.js          | 48 +++++++++----------
 1 file changed, 24 insertions(+), 24 deletions(-)

diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index 5a7052fe6582..e3ee16b7836d 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -8,6 +8,27 @@ import NotificationType from './NotificationType';
 const notificationEventActionMap = {};
 let currentNamedUser;
 
+Ion.connect({
+    key: IONKEYS.SESSION,
+    callback: (sessionData) => {
+        const accountID = sessionData?.accountID.toString() || null;
+
+        // No need to re-subscribe if we're just re-authenticating the same account.
+        if (accountID === currentNamedUser) {
+            return;
+        }
+
+        console.debug(`[PUSH_NOTIFICATION] ${accountID
+            ? `Subscribing to push notifications for accountID ${accountID}`
+            : 'Unsubscribing from push notifications'}.`);
+
+        // This will register this device with the named user associated with this accountID,
+        // or clear the the named user (deregister this device) if sessionData.accountID is null
+        UrbanAirship.setNamedUser(accountID);
+        currentNamedUser = accountID;
+    }
+});
+
 /**
  * Handle a push notification event, and trigger and bound actions.
  *
@@ -65,30 +86,7 @@ function setupPushNotificationCallbacks() {
  * Get permissions and register this device as a named user in AirshipAPI.
  */
 function enable() {
-    UrbanAirship.enableUserPushNotifications()
-        .finally(() => {
-            Ion.connect({
-                key: IONKEYS.SESSION,
-                callback: (sessionData) => {
-                    const accountID = sessionData?.accountID.toString() || null;
-
-                    // No need to re-subscribe if we're just re-authenticating the same account.
-                    if (accountID === currentNamedUser) {
-                        return;
-                    }
-
-                    console.debug(`[PUSH_NOTIFICATION] ${accountID
-                        ? `Subscribing to push notifications for accountID ${accountID}`
-                        : 'Unsubscribing from push notifications'}.`);
-
-                    // This will register this device with the named user associated with this accountID,
-                    // or clear the the named user (deregister this device) if sessionData.accountID is null
-                    UrbanAirship.setNamedUser(accountID);
-                    currentNamedUser = accountID;
-                }
-            });
-            setupPushNotificationCallbacks();
-        });
+    UrbanAirship.enableUserPushNotifications();
 }
 
 /**
@@ -108,6 +106,8 @@ function bind(notificationType, action, triggerEvent = EventType.PushReceived) {
     };
 }
 
+setupPushNotificationCallbacks();
+
 export default {
     enable,
     bind,

From a51a9d7cf8280a665e9da560e3e323b2cdc5a509 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Mon, 5 Oct 2020 13:26:09 -0700
Subject: [PATCH 32/70] simplify Ion.clear()

---
 ios/ReactNativeChat.xcodeproj/project.pbxproj | 98 +++++++++----------
 src/lib/Ion.js                                | 39 +-------
 2 files changed, 51 insertions(+), 86 deletions(-)

diff --git a/ios/ReactNativeChat.xcodeproj/project.pbxproj b/ios/ReactNativeChat.xcodeproj/project.pbxproj
index 5172586422b0..e83c99d375e5 100644
--- a/ios/ReactNativeChat.xcodeproj/project.pbxproj
+++ b/ios/ReactNativeChat.xcodeproj/project.pbxproj
@@ -12,12 +12,12 @@
 		13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
 		13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
 		13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
-		2A03B7FAB1989524E18D4F97 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C89947EEA39BBF00B7EF26A6 /* libPods-ReactNativeChat-ReactNativeChatTests.a */; };
+		313413538A9BE55696C2224D /* libPods-ReactNativeChat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 848CBF6C1DB3C0828826161C /* libPods-ReactNativeChat.a */; };
 		4912B60FA66C4604A56AD575 /* GTAmericaExp-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 8C7003903C1E4957824899BB /* GTAmericaExp-Regular.otf */; };
 		49F157C845CE4D56AA72B80E /* GTAmericaExp-Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = 67D5C3A6A7FA417C8A853FC1 /* GTAmericaExp-Light.otf */; };
+		6FE1B6251C9FF5E7AABEE91D /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DDFD250395A7B040B9E02C49 /* libPods-ReactNativeChat-ReactNativeChatTests.a */; };
 		7F7721B764D444D6861A9B39 /* GTAmericaExp-Thin.otf in Resources */ = {isa = PBXBuildFile; fileRef = A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */; };
 		81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
-		B50BF560D54753753D2B7522 /* libPods-ReactNativeChat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 59D3EEA5B40E14473132477B /* libPods-ReactNativeChat.a */; };
 		CC07911F22C2437DA6708BD2 /* GTAmericaExp-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = AE65058949E14DA5A2D5435D /* GTAmericaExp-Medium.otf */; };
 		E9DF872D2525201700607FDC /* AirshipConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9DF872C2525201700607FDC /* AirshipConfig.plist */; };
 /* End PBXBuildFile section */
@@ -43,18 +43,18 @@
 		13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = ReactNativeChat/Images.xcassets; sourceTree = "<group>"; };
 		13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ReactNativeChat/Info.plist; sourceTree = "<group>"; };
 		13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ReactNativeChat/main.m; sourceTree = "<group>"; };
-		20AD5495BB19D0CE21D0F356 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; sourceTree = "<group>"; };
-		2791C2E4A4F1D9EC4BCA1311 /* Pods-ReactNativeChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.release.xcconfig"; sourceTree = "<group>"; };
-		47B12692BCB071BA3DA6B107 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; sourceTree = "<group>"; };
-		59D3EEA5B40E14473132477B /* libPods-ReactNativeChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		19D17D03325B64CA77B4C7C2 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; sourceTree = "<group>"; };
 		67D5C3A6A7FA417C8A853FC1 /* GTAmericaExp-Light.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Light.otf"; path = "../assets/fonts/GTAmericaExp-Light.otf"; sourceTree = "<group>"; };
 		81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = ReactNativeChat/LaunchScreen.storyboard; sourceTree = "<group>"; };
+		848CBF6C1DB3C0828826161C /* libPods-ReactNativeChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		8562FCBA31783E835D40AE4E /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; sourceTree = "<group>"; };
 		8C7003903C1E4957824899BB /* GTAmericaExp-Regular.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Regular.otf"; path = "../assets/fonts/GTAmericaExp-Regular.otf"; sourceTree = "<group>"; };
 		A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Thin.otf"; path = "../assets/fonts/GTAmericaExp-Thin.otf"; sourceTree = "<group>"; };
 		A5AAD008CBD84A6CAEB9AC97 /* GTAmericaExp-Bold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Bold.otf"; path = "../assets/fonts/GTAmericaExp-Bold.otf"; sourceTree = "<group>"; };
 		AE65058949E14DA5A2D5435D /* GTAmericaExp-Medium.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Medium.otf"; path = "../assets/fonts/GTAmericaExp-Medium.otf"; sourceTree = "<group>"; };
-		C19B554C5F616C1AD56D7A56 /* Pods-ReactNativeChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.debug.xcconfig"; sourceTree = "<group>"; };
-		C89947EEA39BBF00B7EF26A6 /* libPods-ReactNativeChat-ReactNativeChatTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat-ReactNativeChatTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		AFE2072C5C5D03B97FE52E93 /* Pods-ReactNativeChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.release.xcconfig"; sourceTree = "<group>"; };
+		DDFD250395A7B040B9E02C49 /* libPods-ReactNativeChat-ReactNativeChatTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat-ReactNativeChatTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		DF306290FA18EA099078465D /* Pods-ReactNativeChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.debug.xcconfig"; sourceTree = "<group>"; };
 		E9DF86F625251E4A00607FDC /* Chat.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Chat.entitlements; path = ReactNativeChat/Chat.entitlements; sourceTree = "<group>"; };
 		E9DF872C2525201700607FDC /* AirshipConfig.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = AirshipConfig.plist; sourceTree = "<group>"; };
 		ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
@@ -66,7 +66,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				2A03B7FAB1989524E18D4F97 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */,
+				6FE1B6251C9FF5E7AABEE91D /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -74,7 +74,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				B50BF560D54753753D2B7522 /* libPods-ReactNativeChat.a in Frameworks */,
+				313413538A9BE55696C2224D /* libPods-ReactNativeChat.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -119,8 +119,8 @@
 			children = (
 				ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
 				ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
-				59D3EEA5B40E14473132477B /* libPods-ReactNativeChat.a */,
-				C89947EEA39BBF00B7EF26A6 /* libPods-ReactNativeChat-ReactNativeChatTests.a */,
+				848CBF6C1DB3C0828826161C /* libPods-ReactNativeChat.a */,
+				DDFD250395A7B040B9E02C49 /* libPods-ReactNativeChat-ReactNativeChatTests.a */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
@@ -172,10 +172,10 @@
 		EC29677F0A49C2946A495A33 /* Pods */ = {
 			isa = PBXGroup;
 			children = (
-				C19B554C5F616C1AD56D7A56 /* Pods-ReactNativeChat.debug.xcconfig */,
-				2791C2E4A4F1D9EC4BCA1311 /* Pods-ReactNativeChat.release.xcconfig */,
-				47B12692BCB071BA3DA6B107 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */,
-				20AD5495BB19D0CE21D0F356 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */,
+				DF306290FA18EA099078465D /* Pods-ReactNativeChat.debug.xcconfig */,
+				AFE2072C5C5D03B97FE52E93 /* Pods-ReactNativeChat.release.xcconfig */,
+				8562FCBA31783E835D40AE4E /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */,
+				19D17D03325B64CA77B4C7C2 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */,
 			);
 			path = Pods;
 			sourceTree = "<group>";
@@ -187,11 +187,11 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "ReactNativeChatTests" */;
 			buildPhases = (
-				AE43224DB5FDB1A6AD5D05C0 /* [CP] Check Pods Manifest.lock */,
+				6761FB93B39CF064D0A856B0 /* [CP] Check Pods Manifest.lock */,
 				00E356EA1AD99517003FC87E /* Sources */,
 				00E356EB1AD99517003FC87E /* Frameworks */,
 				00E356EC1AD99517003FC87E /* Resources */,
-				9549BADC0A8863C4AE3E64ED /* [CP] Copy Pods Resources */,
+				31C5ED843EABAE54529CDCBE /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -207,13 +207,13 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ReactNativeChat" */;
 			buildPhases = (
-				65C9D025A7C6606100A5D923 /* [CP] Check Pods Manifest.lock */,
+				A68510C24CDDDCC50451CD6A /* [CP] Check Pods Manifest.lock */,
 				FD10A7F022414F080027D42C /* Start Packager */,
 				13B07F871A680F5B00A75B9A /* Sources */,
 				13B07F8C1A680F5B00A75B9A /* Frameworks */,
 				13B07F8E1A680F5B00A75B9A /* Resources */,
 				00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
-				C70727B1CBBD0122615E6118 /* [CP] Copy Pods Resources */,
+				A5A9ED97657CCE72AB7645C9 /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -303,29 +303,7 @@
 			shellPath = /bin/sh;
 			shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh";
 		};
-		65C9D025A7C6606100A5D923 /* [CP] Check Pods Manifest.lock */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputFileListPaths = (
-			);
-			inputPaths = (
-				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
-				"${PODS_ROOT}/Manifest.lock",
-			);
-			name = "[CP] Check Pods Manifest.lock";
-			outputFileListPaths = (
-			);
-			outputPaths = (
-				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-checkManifestLockResult.txt",
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
-			showEnvVarsInLog = 0;
-		};
-		9549BADC0A8863C4AE3E64ED /* [CP] Copy Pods Resources */ = {
+		31C5ED843EABAE54529CDCBE /* [CP] Copy Pods Resources */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -453,7 +431,7 @@
 			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
-		AE43224DB5FDB1A6AD5D05C0 /* [CP] Check Pods Manifest.lock */ = {
+		6761FB93B39CF064D0A856B0 /* [CP] Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -475,7 +453,7 @@
 			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
 			showEnvVarsInLog = 0;
 		};
-		C70727B1CBBD0122615E6118 /* [CP] Copy Pods Resources */ = {
+		A5A9ED97657CCE72AB7645C9 /* [CP] Copy Pods Resources */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -603,6 +581,28 @@
 			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
+		A68510C24CDDDCC50451CD6A /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
 		FD10A7F022414F080027D42C /* Start Packager */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
@@ -655,7 +655,7 @@
 /* Begin XCBuildConfiguration section */
 		00E356F61AD99517003FC87E /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 47B12692BCB071BA3DA6B107 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */;
+			baseConfigurationReference = 8562FCBA31783E835D40AE4E /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
@@ -679,7 +679,7 @@
 		};
 		00E356F71AD99517003FC87E /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 20AD5495BB19D0CE21D0F356 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */;
+			baseConfigurationReference = 19D17D03325B64CA77B4C7C2 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
@@ -700,7 +700,7 @@
 		};
 		13B07F941A680F5B00A75B9A /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = C19B554C5F616C1AD56D7A56 /* Pods-ReactNativeChat.debug.xcconfig */;
+			baseConfigurationReference = DF306290FA18EA099078465D /* Pods-ReactNativeChat.debug.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
@@ -730,7 +730,7 @@
 		};
 		13B07F951A680F5B00A75B9A /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 2791C2E4A4F1D9EC4BCA1311 /* Pods-ReactNativeChat.release.xcconfig */;
+			baseConfigurationReference = AFE2072C5C5D03B97FE52E93 /* Pods-ReactNativeChat.release.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
diff --git a/src/lib/Ion.js b/src/lib/Ion.js
index daa012b3a807..51a33fc3285b 100644
--- a/src/lib/Ion.js
+++ b/src/lib/Ion.js
@@ -3,7 +3,6 @@ import AsyncStorage from '@react-native-community/async-storage';
 import addStorageEventHandler from './addStorageEventHandler';
 import Str from './Str';
 import IONKEYS from '../IONKEYS';
-import promiseAllSettled from './promiseAllSettled';
 
 // Keeps track of the last connectionID that was used so we can keep incrementing it
 let lastConnectionID = 0;
@@ -217,47 +216,13 @@ function multiSet(data) {
         });
 }
 
-/**
- * Perform a depth-first search of all the IONKEYS, and collect all the keys whose values are NOT null or undefined
- *
- * @returns {Promise<Array<string>>}
- */
-function getCurrentKeySet() {
-    const currentKeySet = (
-        function dfs(keyObj) {
-            return _.reduce(keyObj, (keySet, key) => ((typeof key === 'object')
-                ? _.union(keySet, dfs(key))
-                : _.union(keySet, [get(key)
-                    .then((ionVal) => {
-                        if (ionVal === null || ionVal === undefined) {
-                            return Promise.reject();
-                        }
-                        return key;
-                    })
-                ])
-            ), []);
-        }
-    )(IONKEYS);
-
-    // Wait for all promises generated by the recursive function to finish, then filter and extract the results
-    return promiseAllSettled(currentKeySet)
-        .then(keySet => _.filter(keySet, key => key.status === 'fulfilled'))
-        .then(keySet => _.flatten(_.map(keySet, key => key.value)));
-}
-
 /**
  * Clear out all the data in the store
  */
 function clear() {
-    // Await all the keys from Ion.get
-    getCurrentKeySet()
-        .then((keys) => {
-            // Then clear the store
-            AsyncStorage.multiRemove(keys);
-            return keys;
-        })
+    AsyncStorage.getAllKeys()
         .then((keys) => {
-            // Then notify all the relevant subscribers that their underlying Ion data has been deleted
+            AsyncStorage.clear();
             _.each(keys, (key) => {
                 keyChanged(key, null);
             });

From c24ee998e7ee267f02707b7483a452429c5b6a77 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Mon, 5 Oct 2020 14:40:44 -0700
Subject: [PATCH 33/70] use register and deregister instead of Ion.connect

---
 src/Expensify.js                              |  2 -
 src/lib/API.js                                |  2 +
 .../PushNotification/index.native.js          | 62 +++++++++----------
 src/lib/actions/Session.js                    |  2 +
 4 files changed, 34 insertions(+), 34 deletions(-)

diff --git a/src/Expensify.js b/src/Expensify.js
index 94d15de38478..834d94e8b57a 100644
--- a/src/Expensify.js
+++ b/src/Expensify.js
@@ -9,7 +9,6 @@ import * as ActiveClientManager from './lib/ActiveClientManager';
 import IONKEYS from './IONKEYS';
 import withIon from './components/withIon';
 import styles from './style/StyleSheet';
-import PushNotification from './lib/Notification/PushNotification';
 
 import {
     Route,
@@ -53,7 +52,6 @@ class Expensify extends Component {
             key: IONKEYS.SESSION,
             callback: this.removeLoadingState,
         });
-        PushNotification.enable();
     }
 
     /**
diff --git a/src/lib/API.js b/src/lib/API.js
index 5553fd4d5ef8..2445b684454c 100644
--- a/src/lib/API.js
+++ b/src/lib/API.js
@@ -9,6 +9,7 @@ import Str from './Str';
 import Guid from './Guid';
 import redirectToSignIn from './actions/SignInRedirect';
 import Activity from './Activity';
+import PushNotification from './Notification/PushNotification';
 
 // Holds all of the callbacks that need to be triggered when the network reconnects
 const reconnectionCallbacks = [];
@@ -379,6 +380,7 @@ function authenticate(parameters) {
         twoFactorAuthCode: parameters.twoFactorAuthCode,
         exitTo: parameters.exitTo,
     })
+        .then(({accountID}) => PushNotification.register(accountID))
         .catch((error) => {
             console.error(error);
             console.debug('[SIGNIN] Request error');
diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index e3ee16b7836d..6a326968be6d 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -1,33 +1,36 @@
 import _ from 'underscore';
 import {AppState} from 'react-native';
 import {UrbanAirship, EventType} from 'urbanairship-react-native';
-import Ion from '../../Ion';
-import IONKEYS from '../../../IONKEYS';
 import NotificationType from './NotificationType';
 
 const notificationEventActionMap = {};
-let currentNamedUser;
 
-Ion.connect({
-    key: IONKEYS.SESSION,
-    callback: (sessionData) => {
-        const accountID = sessionData?.accountID.toString() || null;
-
-        // No need to re-subscribe if we're just re-authenticating the same account.
-        if (accountID === currentNamedUser) {
-            return;
-        }
-
-        console.debug(`[PUSH_NOTIFICATION] ${accountID
-            ? `Subscribing to push notifications for accountID ${accountID}`
-            : 'Unsubscribing from push notifications'}.`);
+/**
+ * Register this device for push notifications for the given accountID.
+ *
+ * @param {string} accountID
+ */
+function register(accountID) {
+    // Get permissions to display push notifications
+    UrbanAirship.enableUserPushNotifications()
+        .then((isEnabled) => {
+            if (!isEnabled) {
+                console.debug('[PUSH_NOTIFICATIONS] User has disabled visible push notifications for this app.');
+            }
+        });
+
+    // Register this device as a named user in AirshipAPI
+    console.debug(`[PUSH_NOTIFICATIONS] Subscribing to notifications for account ID ${accountID}`);
+    UrbanAirship.setNamedUser(accountID);
+}
 
-        // This will register this device with the named user associated with this accountID,
-        // or clear the the named user (deregister this device) if sessionData.accountID is null
-        UrbanAirship.setNamedUser(accountID);
-        currentNamedUser = accountID;
-    }
-});
+/**
+ * Deregister this device from push notifications.
+ */
+function deregister() {
+    console.debug('[PUSH_NOTIFICATIONS] Unsubscribing from push notifications.');
+    UrbanAirship.setNamedUser(null);
+}
 
 /**
  * Handle a push notification event, and trigger and bound actions.
@@ -72,7 +75,7 @@ function pushNotificationEventCallback(eventType, notification) {
 /**
  * Setup listener for push notification events.
  */
-function setupPushNotificationCallbacks() {
+function setupEventListeners() {
     UrbanAirship.addListener(EventType.PushReceived, (notification) => {
         pushNotificationEventCallback(EventType.PushReceived, notification);
     });
@@ -82,13 +85,6 @@ function setupPushNotificationCallbacks() {
     });
 }
 
-/**
- * Get permissions and register this device as a named user in AirshipAPI.
- */
-function enable() {
-    UrbanAirship.enableUserPushNotifications();
-}
-
 /**
  * Bind an action (from src/lib/actions) to a push notification of a given type.
  * See https://github.com/Expensify/Web-Expensify/blob/master/lib/MobilePushNotifications.php for the various
@@ -106,10 +102,12 @@ function bind(notificationType, action, triggerEvent = EventType.PushReceived) {
     };
 }
 
-setupPushNotificationCallbacks();
+// Setup the listeners when this module first loads
+setupEventListeners();
 
 export default {
-    enable,
+    register,
+    deregister,
     bind,
     EventType,
     NotificationType,
diff --git a/src/lib/actions/Session.js b/src/lib/actions/Session.js
index 542b9fb83fbd..e22e00da09bb 100644
--- a/src/lib/actions/Session.js
+++ b/src/lib/actions/Session.js
@@ -2,6 +2,7 @@ import Ion from '../Ion';
 import * as API from '../API';
 import IONKEYS from '../../IONKEYS';
 import redirectToSignIn from './SignInRedirect';
+import PushNotification from '../Notification/PushNotification';
 
 let credentials;
 Ion.connect({
@@ -30,6 +31,7 @@ function signIn(partnerUserID, partnerUserSecret, twoFactorAuthCode = '', exitTo
  * Clears the Ion store and redirects user to the sign in page
  */
 function signOut() {
+    PushNotification.deregister();
     redirectToSignIn();
     if (!credentials || !credentials.login) {
         return;

From b0697519608165ff28e740dcc3c8d259bf656314 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Mon, 5 Oct 2020 15:17:00 -0700
Subject: [PATCH 34/70] test and clean up push notification registration

---
 src/lib/API.js                                        | 6 +++++-
 src/lib/Ion.js                                        | 4 +++-
 src/lib/Notification/PushNotification/index.native.js | 4 ++--
 3 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/src/lib/API.js b/src/lib/API.js
index 2445b684454c..a1f97aae56d0 100644
--- a/src/lib/API.js
+++ b/src/lib/API.js
@@ -177,8 +177,12 @@ function request(command, parameters, type = 'post') {
                 // create a login for the user
                 if (parameters.useExpensifyLogin) {
                     return createLogin(Str.generateDeviceLoginID(), Guid())
-                        .then(() => setSuccessfulSignInData(response, parameters.exitTo));
+                        .then(() => {
+                            setSuccessfulSignInData(response, parameters.exitTo);
+                            return response;
+                        });
                 }
+                return response;
             })
             .catch(error => Ion.merge(IONKEYS.SESSION, {error: error.message}));
     }
diff --git a/src/lib/Ion.js b/src/lib/Ion.js
index 28e338b24228..8b698ed9725f 100644
--- a/src/lib/Ion.js
+++ b/src/lib/Ion.js
@@ -211,9 +211,11 @@ function multiSet(data) {
 
 /**
  * Clear out all the data in the store
+ *
+ * @returns {Promise<void>}
  */
 function clear() {
-    AsyncStorage.getAllKeys()
+    return AsyncStorage.clear()
         .then((keys) => {
             AsyncStorage.clear();
             _.each(keys, (key) => {
diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index 6a326968be6d..3288e75c0e00 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -8,7 +8,7 @@ const notificationEventActionMap = {};
 /**
  * Register this device for push notifications for the given accountID.
  *
- * @param {string} accountID
+ * @param {string|int} accountID
  */
 function register(accountID) {
     // Get permissions to display push notifications
@@ -21,7 +21,7 @@ function register(accountID) {
 
     // Register this device as a named user in AirshipAPI
     console.debug(`[PUSH_NOTIFICATIONS] Subscribing to notifications for account ID ${accountID}`);
-    UrbanAirship.setNamedUser(accountID);
+    UrbanAirship.setNamedUser(accountID.toString());
 }
 
 /**

From 15bc4e5ce186db4f09af3f48c594de3f03b5892e Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Mon, 5 Oct 2020 15:56:03 -0700
Subject: [PATCH 35/70] rebuild project to fix bad merge

---
 ios/ReactNativeChat.xcodeproj/project.pbxproj | 106 +++++++++---------
 1 file changed, 53 insertions(+), 53 deletions(-)

diff --git a/ios/ReactNativeChat.xcodeproj/project.pbxproj b/ios/ReactNativeChat.xcodeproj/project.pbxproj
index 961cfd2b329a..b58d3c168fc3 100644
--- a/ios/ReactNativeChat.xcodeproj/project.pbxproj
+++ b/ios/ReactNativeChat.xcodeproj/project.pbxproj
@@ -8,7 +8,6 @@
 
 /* Begin PBXBuildFile section */
 		00E356F31AD99517003FC87E /* ReactNativeChatTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* ReactNativeChatTests.m */; };
-		097C76E5807C5DFC565DC655 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C4F81DB9C6DA6418525441E5 /* libPods-ReactNativeChat-ReactNativeChatTests.a */; };
 		13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
 		13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
 		13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
@@ -17,10 +16,11 @@
 		1E76D5232522316A005A268F /* GTAmericaExp-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = AE65058949E14DA5A2D5435D /* GTAmericaExp-Medium.otf */; };
 		1E76D5242522316A005A268F /* GTAmericaExp-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 8C7003903C1E4957824899BB /* GTAmericaExp-Regular.otf */; };
 		1E76D5252522316A005A268F /* GTAmericaExp-Thin.otf in Resources */ = {isa = PBXBuildFile; fileRef = A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */; };
-		347C4A976B972BC7A903F97D /* libPods-ReactNativeChat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6ABF95746519766B23B4E2D0 /* libPods-ReactNativeChat.a */; };
-		81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
 		425866037F4C482AAB46CB8B /* GTAmericaExp-BdIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = A8D6F2F722FD4E66A38EBBB6 /* GTAmericaExp-BdIt.otf */; };
 		6856B78873B64C44A92E51DB /* GTAmericaExp-MdIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = DB5A1365442D4419AF6F08E5 /* GTAmericaExp-MdIt.otf */; };
+		76B5E637A882833D630D1395 /* libPods-ReactNativeChat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E51D584B72117ADCE3326B2 /* libPods-ReactNativeChat.a */; };
+		7B29927C98D70D00A8F49E77 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 114D30B1289BB8C2E09B148C /* libPods-ReactNativeChat-ReactNativeChatTests.a */; };
+		81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
 		8821A238A081483FA947BC4E /* GTAmericaExp-RgIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 918D7FEFF96242E6B5F5E14D /* GTAmericaExp-RgIt.otf */; };
 		E9DF872D2525201700607FDC /* AirshipConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9DF872C2525201700607FDC /* AirshipConfig.plist */; };
 /* End PBXBuildFile section */
@@ -40,31 +40,31 @@
 		00E356EE1AD99517003FC87E /* ReactNativeChatTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactNativeChatTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		00E356F21AD99517003FC87E /* ReactNativeChatTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReactNativeChatTests.m; sourceTree = "<group>"; };
+		114D30B1289BB8C2E09B148C /* libPods-ReactNativeChat-ReactNativeChatTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat-ReactNativeChatTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		13B07F961A680F5B00A75B9A /* Chat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Chat.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ReactNativeChat/AppDelegate.h; sourceTree = "<group>"; };
 		13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = ReactNativeChat/AppDelegate.m; sourceTree = "<group>"; };
 		13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = ReactNativeChat/Images.xcassets; sourceTree = "<group>"; };
 		13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ReactNativeChat/Info.plist; sourceTree = "<group>"; };
 		13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ReactNativeChat/main.m; sourceTree = "<group>"; };
-		19D17D03325B64CA77B4C7C2 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; sourceTree = "<group>"; };
+		1573AB335CA25AB88FB106C0 /* Pods-ReactNativeChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.release.xcconfig"; sourceTree = "<group>"; };
 		67D5C3A6A7FA417C8A853FC1 /* GTAmericaExp-Light.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Light.otf"; path = "../assets/fonts/GTAmericaExp-Light.otf"; sourceTree = "<group>"; };
+		7E51D584B72117ADCE3326B2 /* libPods-ReactNativeChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = ReactNativeChat/LaunchScreen.storyboard; sourceTree = "<group>"; };
-		848CBF6C1DB3C0828826161C /* libPods-ReactNativeChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat.a"; sourceTree = BUILT_PRODUCTS_DIR; };
-		8562FCBA31783E835D40AE4E /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; sourceTree = "<group>"; };
 		8C7003903C1E4957824899BB /* GTAmericaExp-Regular.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Regular.otf"; path = "../assets/fonts/GTAmericaExp-Regular.otf"; sourceTree = "<group>"; };
+		918D7FEFF96242E6B5F5E14D /* GTAmericaExp-RgIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-RgIt.otf"; path = "../assets/fonts/GTAmericaExp-RgIt.otf"; sourceTree = "<group>"; };
 		A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Thin.otf"; path = "../assets/fonts/GTAmericaExp-Thin.otf"; sourceTree = "<group>"; };
 		A5AAD008CBD84A6CAEB9AC97 /* GTAmericaExp-Bold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Bold.otf"; path = "../assets/fonts/GTAmericaExp-Bold.otf"; sourceTree = "<group>"; };
+		A8D6F2F722FD4E66A38EBBB6 /* GTAmericaExp-BdIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-BdIt.otf"; path = "../assets/fonts/GTAmericaExp-BdIt.otf"; sourceTree = "<group>"; };
 		AE65058949E14DA5A2D5435D /* GTAmericaExp-Medium.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Medium.otf"; path = "../assets/fonts/GTAmericaExp-Medium.otf"; sourceTree = "<group>"; };
-		AFE2072C5C5D03B97FE52E93 /* Pods-ReactNativeChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.release.xcconfig"; sourceTree = "<group>"; };
-		DDFD250395A7B040B9E02C49 /* libPods-ReactNativeChat-ReactNativeChatTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat-ReactNativeChatTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
-		DF306290FA18EA099078465D /* Pods-ReactNativeChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.debug.xcconfig"; sourceTree = "<group>"; };
+		DB5A1365442D4419AF6F08E5 /* GTAmericaExp-MdIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-MdIt.otf"; path = "../assets/fonts/GTAmericaExp-MdIt.otf"; sourceTree = "<group>"; };
+		DFFB9269BD80E5509E6F5318 /* Pods-ReactNativeChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.debug.xcconfig"; sourceTree = "<group>"; };
+		E3AB9696F691D1C5D449D583 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; sourceTree = "<group>"; };
 		E9DF86F625251E4A00607FDC /* Chat.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Chat.entitlements; path = ReactNativeChat/Chat.entitlements; sourceTree = "<group>"; };
 		E9DF872C2525201700607FDC /* AirshipConfig.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = AirshipConfig.plist; sourceTree = "<group>"; };
 		ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
 		ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; };
-		A8D6F2F722FD4E66A38EBBB6 /* GTAmericaExp-BdIt.otf */ = {isa = PBXFileReference; name = "GTAmericaExp-BdIt.otf"; path = "../assets/fonts/GTAmericaExp-BdIt.otf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
-		DB5A1365442D4419AF6F08E5 /* GTAmericaExp-MdIt.otf */ = {isa = PBXFileReference; name = "GTAmericaExp-MdIt.otf"; path = "../assets/fonts/GTAmericaExp-MdIt.otf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
-		918D7FEFF96242E6B5F5E14D /* GTAmericaExp-RgIt.otf */ = {isa = PBXFileReference; name = "GTAmericaExp-RgIt.otf"; path = "../assets/fonts/GTAmericaExp-RgIt.otf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
+		FE4548417FE26480911F90C0 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -72,7 +72,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				6FE1B6251C9FF5E7AABEE91D /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */,
+				7B29927C98D70D00A8F49E77 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -80,7 +80,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				313413538A9BE55696C2224D /* libPods-ReactNativeChat.a in Frameworks */,
+				76B5E637A882833D630D1395 /* libPods-ReactNativeChat.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -125,8 +125,8 @@
 			children = (
 				ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
 				ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
-				848CBF6C1DB3C0828826161C /* libPods-ReactNativeChat.a */,
-				DDFD250395A7B040B9E02C49 /* libPods-ReactNativeChat-ReactNativeChatTests.a */,
+				7E51D584B72117ADCE3326B2 /* libPods-ReactNativeChat.a */,
+				114D30B1289BB8C2E09B148C /* libPods-ReactNativeChat-ReactNativeChatTests.a */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
@@ -181,10 +181,10 @@
 		EC29677F0A49C2946A495A33 /* Pods */ = {
 			isa = PBXGroup;
 			children = (
-				DF306290FA18EA099078465D /* Pods-ReactNativeChat.debug.xcconfig */,
-				AFE2072C5C5D03B97FE52E93 /* Pods-ReactNativeChat.release.xcconfig */,
-				8562FCBA31783E835D40AE4E /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */,
-				19D17D03325B64CA77B4C7C2 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */,
+				DFFB9269BD80E5509E6F5318 /* Pods-ReactNativeChat.debug.xcconfig */,
+				1573AB335CA25AB88FB106C0 /* Pods-ReactNativeChat.release.xcconfig */,
+				E3AB9696F691D1C5D449D583 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */,
+				FE4548417FE26480911F90C0 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */,
 			);
 			path = Pods;
 			sourceTree = "<group>";
@@ -196,11 +196,11 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "ReactNativeChatTests" */;
 			buildPhases = (
-				6761FB93B39CF064D0A856B0 /* [CP] Check Pods Manifest.lock */,
+				4B525D90C06C76F3070B31EC /* [CP] Check Pods Manifest.lock */,
 				00E356EA1AD99517003FC87E /* Sources */,
 				00E356EB1AD99517003FC87E /* Frameworks */,
 				00E356EC1AD99517003FC87E /* Resources */,
-				31C5ED843EABAE54529CDCBE /* [CP] Copy Pods Resources */,
+				4F03936CD68F10F4FD14F832 /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -216,13 +216,13 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ReactNativeChat" */;
 			buildPhases = (
-				A68510C24CDDDCC50451CD6A /* [CP] Check Pods Manifest.lock */,
+				940DE2C6A94F35A5F9B805C7 /* [CP] Check Pods Manifest.lock */,
 				FD10A7F022414F080027D42C /* Start Packager */,
 				13B07F871A680F5B00A75B9A /* Sources */,
 				13B07F8C1A680F5B00A75B9A /* Frameworks */,
 				13B07F8E1A680F5B00A75B9A /* Resources */,
 				00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
-				A5A9ED97657CCE72AB7645C9 /* [CP] Copy Pods Resources */,
+				5DE984ADFC7810B96C04A896 /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -315,7 +315,29 @@
 			shellPath = /bin/sh;
 			shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh";
 		};
-		31C5ED843EABAE54529CDCBE /* [CP] Copy Pods Resources */ = {
+		4B525D90C06C76F3070B31EC /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-ReactNativeChatTests-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
+		4F03936CD68F10F4FD14F832 /* [CP] Copy Pods Resources */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -443,29 +465,7 @@
 			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
-		6761FB93B39CF064D0A856B0 /* [CP] Check Pods Manifest.lock */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputFileListPaths = (
-			);
-			inputPaths = (
-				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
-				"${PODS_ROOT}/Manifest.lock",
-			);
-			name = "[CP] Check Pods Manifest.lock";
-			outputFileListPaths = (
-			);
-			outputPaths = (
-				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-ReactNativeChatTests-checkManifestLockResult.txt",
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
-			showEnvVarsInLog = 0;
-		};
-		A5A9ED97657CCE72AB7645C9 /* [CP] Copy Pods Resources */ = {
+		5DE984ADFC7810B96C04A896 /* [CP] Copy Pods Resources */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -593,7 +593,7 @@
 			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
-		A68510C24CDDDCC50451CD6A /* [CP] Check Pods Manifest.lock */ = {
+		940DE2C6A94F35A5F9B805C7 /* [CP] Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -667,7 +667,7 @@
 /* Begin XCBuildConfiguration section */
 		00E356F61AD99517003FC87E /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 8562FCBA31783E835D40AE4E /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */;
+			baseConfigurationReference = E3AB9696F691D1C5D449D583 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
@@ -691,7 +691,7 @@
 		};
 		00E356F71AD99517003FC87E /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 19D17D03325B64CA77B4C7C2 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */;
+			baseConfigurationReference = FE4548417FE26480911F90C0 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
@@ -712,7 +712,7 @@
 		};
 		13B07F941A680F5B00A75B9A /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = DF306290FA18EA099078465D /* Pods-ReactNativeChat.debug.xcconfig */;
+			baseConfigurationReference = DFFB9269BD80E5509E6F5318 /* Pods-ReactNativeChat.debug.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
@@ -742,7 +742,7 @@
 		};
 		13B07F951A680F5B00A75B9A /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = AFE2072C5C5D03B97FE52E93 /* Pods-ReactNativeChat.release.xcconfig */;
+			baseConfigurationReference = 1573AB335CA25AB88FB106C0 /* Pods-ReactNativeChat.release.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;

From 93fb232d1ff09458216286f9370707fcc9fa9088 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Tue, 6 Oct 2020 12:58:12 -0700
Subject: [PATCH 36/70] got basic android notificaitons showing up

---
 android/app/build.gradle                      |  5 ++
 android/app/google-services.json              | 47 +++++++++++++++++++
 .../src/main/assets/airshipconfig.properties  | 12 +++--
 android/build.gradle                          |  2 +
 4 files changed, 62 insertions(+), 4 deletions(-)
 create mode 100644 android/app/google-services.json

diff --git a/android/app/build.gradle b/android/app/build.gradle
index 146d12e96717..2c82633cd94d 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -236,3 +236,8 @@ task copyDownloadableDepsToLibs(type: Copy) {
 }
 
 apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
+
+def googleServicesFile = rootProject.file('app/google-services.json')
+if (googleServicesFile.exists()) {
+    apply plugin: 'com.google.gms.google-services'  // Google Play services Gradle plugin
+}
diff --git a/android/app/google-services.json b/android/app/google-services.json
new file mode 100644
index 000000000000..545f49d765bf
--- /dev/null
+++ b/android/app/google-services.json
@@ -0,0 +1,47 @@
+{
+    "project_info": {
+        "project_number": "921154746561",
+        "firebase_url": "https://expensify-chat.firebaseio.com",
+        "project_id": "expensify-chat",
+        "storage_bucket": "expensify-chat.appspot.com"
+    },
+    "client": [
+        {
+            "client_info": {
+                "mobilesdk_app_id": "1:921154746561:android:4f04268f25f84eaf027c40",
+                "android_client_info": {
+                    "package_name": "com.expensify.chat"
+                }
+            },
+            "oauth_client": [
+                {
+                    "client_id": "921154746561-gpsoaqgqfuqrfsjdf8l7vohfkfj7b9up.apps.googleusercontent.com",
+                    "client_type": 3
+                }
+            ],
+            "api_key": [
+                {
+                    "current_key": "AIzaSyCVwQb9lBI06bDIwHOw10AkdJyquXoMngk"
+                }
+            ],
+            "services": {
+                "appinvite_service": {
+                    "other_platform_oauth_client": [
+                        {
+                            "client_id": "921154746561-gpsoaqgqfuqrfsjdf8l7vohfkfj7b9up.apps.googleusercontent.com",
+                            "client_type": 3
+                        },
+                        {
+                            "client_id": "921154746561-s3uqn2oe4m85tufi6mqflbfbuajrm2i3.apps.googleusercontent.com",
+                            "client_type": 2,
+                            "ios_info": {
+                                "bundle_id": "com.chat.expensify.chat"
+                            }
+                        }
+                    ]
+                }
+            }
+        }
+    ],
+    "configuration_version": "1"
+}
diff --git a/android/app/src/main/assets/airshipconfig.properties b/android/app/src/main/assets/airshipconfig.properties
index 25e6fac733e5..52970d9fce76 100644
--- a/android/app/src/main/assets/airshipconfig.properties
+++ b/android/app/src/main/assets/airshipconfig.properties
@@ -1,5 +1,9 @@
-developmentAppKey = 5MOp6a0aQ6G3Xw1kKGfgrA
-developmentAppSecret = XFzdgGwRQSyellrQtYar3A
+developmentAppKey = uulSSfTDQJ2r0PMpjRrhmQ
+developmentAppSecret = D4Bhf0HrQEehrPua74Tyiw
 
-productionAppKey = 4XPPo9Z4Q2KVAbIBkYP9ew
-productionAppSecret = 6aTetk0vQ2aQmGpRITwXOg
+productionAppKey = 55vypj0ARc6cN09MX7ogtQ
+productionAppSecret = EsSaqbdLSvmyC6kSBFJCtQ
+
+# Notification Customization
+notificationIcon = @mipmap/ic_launcher_round
+notificationAccentColor = #37444C
diff --git a/android/build.gradle b/android/build.gradle
index a6786bab1366..b6e382f25c9c 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -14,6 +14,8 @@ buildscript {
     }
     dependencies {
         classpath("com.android.tools.build:gradle:3.5.3")
+        classpath("com.google.gms:google-services:4.2.0")
+
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files
     }

From 221ddb807e42d0e1640388022856eb3df50d716a Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Tue, 6 Oct 2020 15:52:57 -0700
Subject: [PATCH 37/70] simplify promise chain and remove second call to
 AsyncStorage.clear

---
 src/lib/API.js | 5 ++---
 src/lib/Ion.js | 1 -
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/lib/API.js b/src/lib/API.js
index a1f97aae56d0..f6b310a6a4d2 100644
--- a/src/lib/API.js
+++ b/src/lib/API.js
@@ -179,10 +179,10 @@ function request(command, parameters, type = 'post') {
                     return createLogin(Str.generateDeviceLoginID(), Guid())
                         .then(() => {
                             setSuccessfulSignInData(response, parameters.exitTo);
-                            return response;
+                            PushNotification.register(response.accountID);
                         });
                 }
-                return response;
+                PushNotification.register(response.accountID);
             })
             .catch(error => Ion.merge(IONKEYS.SESSION, {error: error.message}));
     }
@@ -384,7 +384,6 @@ function authenticate(parameters) {
         twoFactorAuthCode: parameters.twoFactorAuthCode,
         exitTo: parameters.exitTo,
     })
-        .then(({accountID}) => PushNotification.register(accountID))
         .catch((error) => {
             console.error(error);
             console.debug('[SIGNIN] Request error');
diff --git a/src/lib/Ion.js b/src/lib/Ion.js
index 8b698ed9725f..7f6b67303e38 100644
--- a/src/lib/Ion.js
+++ b/src/lib/Ion.js
@@ -217,7 +217,6 @@ function multiSet(data) {
 function clear() {
     return AsyncStorage.clear()
         .then((keys) => {
-            AsyncStorage.clear();
             _.each(keys, (key) => {
                 keyChanged(key, null);
             });

From e760d2034b8ea2f19eb5c0ea27093c355f64de61 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Tue, 6 Oct 2020 17:06:00 -0700
Subject: [PATCH 38/70] make android push notifications pretty

---
 .../app/src/main/assets/airshipconfig.properties |   4 ++--
 .../main/res/drawable-hdpi/ic_notification.png   | Bin 0 -> 916 bytes
 .../main/res/drawable-mdpi/ic_notification.png   | Bin 0 -> 543 bytes
 .../main/res/drawable-xhdpi/ic_notification.png  | Bin 0 -> 1368 bytes
 .../main/res/drawable-xxhdpi/ic_notification.png | Bin 0 -> 2377 bytes
 .../res/drawable-xxxhdpi/ic_notification.png     | Bin 0 -> 3645 bytes
 6 files changed, 2 insertions(+), 2 deletions(-)
 create mode 100644 android/app/src/main/res/drawable-hdpi/ic_notification.png
 create mode 100644 android/app/src/main/res/drawable-mdpi/ic_notification.png
 create mode 100644 android/app/src/main/res/drawable-xhdpi/ic_notification.png
 create mode 100644 android/app/src/main/res/drawable-xxhdpi/ic_notification.png
 create mode 100644 android/app/src/main/res/drawable-xxxhdpi/ic_notification.png

diff --git a/android/app/src/main/assets/airshipconfig.properties b/android/app/src/main/assets/airshipconfig.properties
index 52970d9fce76..6e37dfb7465e 100644
--- a/android/app/src/main/assets/airshipconfig.properties
+++ b/android/app/src/main/assets/airshipconfig.properties
@@ -5,5 +5,5 @@ productionAppKey = 55vypj0ARc6cN09MX7ogtQ
 productionAppSecret = EsSaqbdLSvmyC6kSBFJCtQ
 
 # Notification Customization
-notificationIcon = @mipmap/ic_launcher_round
-notificationAccentColor = #37444C
+notificationIcon = ic_notification
+notificationAccentColor = #2EAAE2
diff --git a/android/app/src/main/res/drawable-hdpi/ic_notification.png b/android/app/src/main/res/drawable-hdpi/ic_notification.png
new file mode 100644
index 0000000000000000000000000000000000000000..e084753fb90ed8aa3699f1ac59e9736c6fc1e14f
GIT binary patch
diff --git a/android/app/src/main/res/drawable-mdpi/ic_notification.png b/android/app/src/main/res/drawable-mdpi/ic_notification.png
new file mode 100644
index 0000000000000000000000000000000000000000..669579148c6192e3676508ea32abc7292e3b1a7a
GIT binary patch
diff --git a/android/app/src/main/res/drawable-xhdpi/ic_notification.png b/android/app/src/main/res/drawable-xhdpi/ic_notification.png
new file mode 100644
index 0000000000000000000000000000000000000000..18c06a8d16c3bff609e74e9c62dc23d8fffd436c
GIT binary patch
diff --git a/android/app/src/main/res/drawable-xxhdpi/ic_notification.png b/android/app/src/main/res/drawable-xxhdpi/ic_notification.png
new file mode 100644
index 0000000000000000000000000000000000000000..da94014c33f09ad6d2c4c5beae14d0242751f8ba
GIT binary patch
diff --git a/android/app/src/main/res/drawable-xxxhdpi/ic_notification.png b/android/app/src/main/res/drawable-xxxhdpi/ic_notification.png
new file mode 100644
index 0000000000000000000000000000000000000000..96f1cb18a2be87d6a016dc031404a292d8d51b9b
GIT binary patch

From ed2ef62c6d0a6c7eda20eef2d53e3b0f4a52d3d9 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 7 Oct 2020 08:54:31 -0700
Subject: [PATCH 39/70] fix ion clear

---
 src/lib/Ion.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/lib/Ion.js b/src/lib/Ion.js
index 7f6b67303e38..553277d0e31d 100644
--- a/src/lib/Ion.js
+++ b/src/lib/Ion.js
@@ -215,8 +215,9 @@ function multiSet(data) {
  * @returns {Promise<void>}
  */
 function clear() {
-    return AsyncStorage.clear()
+    return AsyncStorage.getAllKeys()
         .then((keys) => {
+            AsyncStorage.clear();
             _.each(keys, (key) => {
                 keyChanged(key, null);
             });

From a7f8bbba849f3445a8198fd65d4df4cfb72f6956 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 7 Oct 2020 10:10:33 -0700
Subject: [PATCH 40/70] revert/simplify changes to BrowserNotifications

---
 .../LocalNotification/BrowserNotifications.js | 119 ++++++++++++++++++
 .../Notification/LocalNotification/index.js   | 117 +----------------
 2 files changed, 123 insertions(+), 113 deletions(-)
 create mode 100644 src/lib/Notification/LocalNotification/BrowserNotifications.js

diff --git a/src/lib/Notification/LocalNotification/BrowserNotifications.js b/src/lib/Notification/LocalNotification/BrowserNotifications.js
new file mode 100644
index 000000000000..84b6a8990388
--- /dev/null
+++ b/src/lib/Notification/LocalNotification/BrowserNotifications.js
@@ -0,0 +1,119 @@
+// Web and desktop implementation only. Do not import for direct use. Use LocalNotification.
+import Str from '../../Str';
+import CONST from '../../../CONST';
+
+const EXPENSIFY_ICON_URL = `${CONST.CLOUDFRONT_URL}/images/favicon-2019.png`;
+const DEFAULT_DELAY = 4000;
+
+/**
+ * Checks if the user has granted permission to show browser notifications
+ *
+ * @return {Promise}
+ */
+function canUseBrowserNotifications() {
+    return new Promise((resolve) => {
+        // They have no browser notifications so we can't use this feature
+        if (!window.Notification) {
+            return resolve(false);
+        }
+
+        // Check if they previously granted or denied us access to send a notification
+        const permissionGranted = Notification.permission === 'granted';
+
+        if (permissionGranted || Notification.permission === 'denied') {
+            return resolve(permissionGranted);
+        }
+
+        // Check their global preferences for browser notifications and ask permission if they have none
+        Notification.requestPermission()
+            .then((status) => {
+                resolve(status === 'granted');
+            });
+    });
+}
+
+/**
+ * Light abstraction around browser push notifications.
+ * Checks for permission before determining whether to send.
+ *
+ * @param {Object} params
+ * @param {String} params.title
+ * @param {String} params.body
+ * @param {String} [params.icon] Default to Expensify logo
+ * @param {Number} [params.delay]
+ * @param {Function} [params.onClick]
+ * @param {String} [params.tag]
+ *
+ * @return {Promise} - resolves with Notification object or undefined
+ */
+function push({
+    title,
+    body,
+    delay = DEFAULT_DELAY,
+    onClick = () => {},
+    tag = '',
+    icon = EXPENSIFY_ICON_URL,
+}) {
+    return new Promise((resolve) => {
+        if (!title || !body) {
+            throw new Error('BrowserNotification must include title and body parameter.');
+        }
+
+        canUseBrowserNotifications().then((canUseNotifications) => {
+            if (!canUseNotifications) {
+                resolve();
+                return;
+            }
+
+            const notification = new Notification(title, {
+                body,
+                icon,
+                tag,
+            });
+
+            // If we pass in a delay param greater than 0 the notification
+            // will auto-close after the specified time.
+            if (delay > 0) {
+                setTimeout(notification.close.bind(notification), delay);
+            }
+
+            notification.onclick = (event) => {
+                event.preventDefault();
+                onClick();
+                window.parent.focus();
+                window.focus();
+                notification.close();
+            };
+
+            resolve(notification);
+        });
+    });
+}
+
+/**
+ * BrowserNotification
+ * @namespace
+ */
+export default {
+    /**
+     * Create a report comment notification
+     *
+     * @param {Object} params
+     * @param {Object} params.reportAction
+     * @param {Function} params.onClick
+     */
+    pushReportCommentNotification({reportAction, onClick}) {
+        const {person, message} = reportAction;
+        const plainTextPerson = Str.htmlDecode(person.map(f => f.text).join());
+
+        // Specifically target the comment part of the message
+        const plainTextMessage = Str.htmlDecode((message.find(f => f.type === 'COMMENT') || {}).text);
+
+        push({
+            title: `New message from ${plainTextPerson}`,
+            body: plainTextMessage,
+            delay: 0,
+            onClick,
+        });
+    },
+};
diff --git a/src/lib/Notification/LocalNotification/index.js b/src/lib/Notification/LocalNotification/index.js
index 47b55c19c885..356d9749b684 100644
--- a/src/lib/Notification/LocalNotification/index.js
+++ b/src/lib/Notification/LocalNotification/index.js
@@ -1,118 +1,9 @@
-import Str from '../../Str';
-import CONST from '../../../CONST';
+import BrowserNotifications from './BrowserNotifications';
 
-const EXPENSIFY_ICON_URL = `${CONST.CLOUDFRONT_URL}/images/favicon-2019.png`;
-const DEFAULT_DELAY = 4000;
-
-/**
- * Checks if the user has granted permission to show local notifications
- *
- * @return {Promise}
- */
-function canUseLocalNotifications() {
-    return new Promise((resolve) => {
-        // They have no local notifications so we can't use this feature
-        if (!window.Notification) {
-            return resolve(false);
-        }
-
-        // Check if they previously granted or denied us access to send a notification
-        const permissionGranted = Notification.permission === 'granted';
-
-        if (permissionGranted || Notification.permission === 'denied') {
-            return resolve(permissionGranted);
-        }
-
-        // Check their global preferences for local notifications and ask permission if they have none
-        Notification.requestPermission()
-            .then((status) => {
-                resolve(status === 'granted');
-            });
-    });
+function showCommentNotification({reportAction, onClick}) {
+    BrowserNotifications.pushReportCommentNotification({reportAction, onClick});
 }
 
-/**
- * Light abstraction around local push notifications.
- * Checks for permission before determining whether to send.
- *
- * @param {Object} params
- * @param {String} params.title
- * @param {String} params.body
- * @param {String} [params.icon] Default to Expensify logo
- * @param {Number} [params.delay]
- * @param {Function} [params.onClick]
- * @param {String} [params.tag]
- *
- * @return {Promise} - resolves with Notification object or undefined
- */
-function push({
-    title,
-    body,
-    delay = DEFAULT_DELAY,
-    onClick = () => {},
-    tag = '',
-    icon = EXPENSIFY_ICON_URL,
-}) {
-    return new Promise((resolve) => {
-        if (!title || !body) {
-            throw new Error('LocalNotification must include title and body parameter.');
-        }
-
-        canUseLocalNotifications().then((canUseNotifications) => {
-            if (!canUseNotifications) {
-                resolve();
-                return;
-            }
-
-            const notification = new Notification(title, {
-                body,
-                icon,
-                tag,
-            });
-
-            // If we pass in a delay param greater than 0 the notification
-            // will auto-close after the specified time.
-            if (delay > 0) {
-                setTimeout(notification.close.bind(notification), delay);
-            }
-
-            notification.onclick = (event) => {
-                event.preventDefault();
-                onClick();
-                window.parent.focus();
-                window.focus();
-                notification.close();
-            };
-
-            resolve(notification);
-        });
-    });
-}
-
-/**
- * LocalNotification
- * @namespace
- */
 export default {
-    /**
-     * Create a report comment notification
-     *
-     * @param {Object} params
-     * @param {Object} params.reportAction
-     * @param {Function} params.onClick
-     */
-    pushReportCommentNotification({reportAction, onClick}) {
-        const {person, message} = reportAction;
-        const plainTextPerson = Str.htmlDecode(person.map(f => f.text).join());
-
-        // Specifically target the comment part of the message
-        const plainTextMessage = Str.htmlDecode((message.find(f => f.type === 'COMMENT') || {}).text);
-
-        push({
-            title: `New message from ${plainTextPerson}`,
-            body: plainTextMessage,
-            delay: 0,
-            onClick,
-        });
-    },
+    showCommentNotification,
 };

From 3ecd752794a8fd948d000f58de23a69b9e0adac8 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 7 Oct 2020 10:16:22 -0700
Subject: [PATCH 41/70] clean up event binding

---
 .../PushNotification/EventType.js             |  9 ------
 .../Notification/PushNotification/index.js    |  8 ++---
 .../PushNotification/index.native.js          | 29 ++++++++++++-------
 src/lib/actions/Report.js                     |  6 ++--
 4 files changed, 25 insertions(+), 27 deletions(-)
 delete mode 100644 src/lib/Notification/PushNotification/EventType.js

diff --git a/src/lib/Notification/PushNotification/EventType.js b/src/lib/Notification/PushNotification/EventType.js
deleted file mode 100644
index fdebe96a6522..000000000000
--- a/src/lib/Notification/PushNotification/EventType.js
+++ /dev/null
@@ -1,9 +0,0 @@
-/**
- * This is just a local copy of the EventType enum from 'urbanairship-react-native'.
- * Importing it on any index.js file breaks web, so we'll export this from PushNotifcation/index.js instead.
- * Update it as necessary.
- */
-export default {
-    NotificationResponse: 'notificationResponse',
-    PushReceived: 'pushReceived',
-};
diff --git a/src/lib/Notification/PushNotification/index.js b/src/lib/Notification/PushNotification/index.js
index 50876587d73a..e3a88d16764d 100644
--- a/src/lib/Notification/PushNotification/index.js
+++ b/src/lib/Notification/PushNotification/index.js
@@ -1,10 +1,10 @@
-import EventType from './EventType';
 import NotificationType from './NotificationType';
 
 // Push notifications are only supported on mobile, so we'll just noop here
 export default {
-    enable: () => {},
-    bind: () => {},
-    EventType,
+    register: () => {},
+    deregister: () => {},
+    onReceived: () => {},
+    onSelected: () => {},
     NotificationType,
 };
diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index 3288e75c0e00..549f0b781744 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -1,4 +1,3 @@
-import _ from 'underscore';
 import {AppState} from 'react-native';
 import {UrbanAirship, EventType} from 'urbanairship-react-native';
 import NotificationType from './NotificationType';
@@ -86,29 +85,37 @@ function setupEventListeners() {
 }
 
 /**
- * Bind an action (from src/lib/actions) to a push notification of a given type.
+ * Bind a callback to a push notification of a given type.
  * See https://github.com/Expensify/Web-Expensify/blob/master/lib/MobilePushNotifications.php for the various
  * types of push notifications sent, along with the data that they provide.
  *
  * @param {string} notificationType
- * @param {Function} action
- * @param {string?} triggerEvent - The event that should trigger this action. Should be one of UrbanAirship.EventType
+ * @param {Function} callback
+ * @param {string?} triggerEvent - The event that should trigger this callback. Should be one of UrbanAirship.EventType
  */
-function bind(notificationType, action, triggerEvent = EventType.PushReceived) {
-    const event = _.contains(_.values(EventType), triggerEvent) ? triggerEvent : EventType.PushReceived;
-    notificationEventActionMap[event] = {
-        ...notificationEventActionMap[event],
-        [notificationType]: action,
+function bind(notificationType, callback, triggerEvent) {
+    notificationEventActionMap[triggerEvent] = {
+        ...notificationEventActionMap[triggerEvent],
+        [notificationType]: callback,
     };
 }
 
+
+function onReceived(notificationType, callback) {
+    bind(notificationType, callback, EventType.PushReceived);
+}
+
+function onSelected(notificationType, callback) {
+    bind(notificationType, callback, EventType.NotificationResponse);
+}
+
 // Setup the listeners when this module first loads
 setupEventListeners();
 
 export default {
     register,
     deregister,
-    bind,
-    EventType,
+    onReceived,
+    onSelected,
     NotificationType,
 };
diff --git a/src/lib/actions/Report.js b/src/lib/actions/Report.js
index c962bbce963e..674b255637bc 100644
--- a/src/lib/actions/Report.js
+++ b/src/lib/actions/Report.js
@@ -236,14 +236,14 @@ function subscribeToReportCommentEvents() {
         updateReportWithNewAction(pushJSON.reportID, pushJSON.reportAction);
     });
 
-    PushNotification.bind(PushNotification.NotificationType.REPORT.COMMENT, ({reportID, reportAction}) => {
+    PushNotification.onReceived(PushNotification.NotificationType.REPORT.COMMENT, ({reportID, reportAction}) => {
         updateReportWithNewAction(reportID, reportAction);
     });
 
     // Open correct report when push notification is clicked
-    PushNotification.bind(PushNotification.NotificationType.REPORT.COMMENT, ({reportID}) => {
+    PushNotification.onSelected(PushNotification.NotificationType.REPORT.COMMENT, ({reportID}) => {
         redirect(reportID);
-    }, PushNotification.EventType.NotificationResponse);
+    });
 }
 
 /**

From e64fe75d41a7497e6ed915a2293d98e1631ad7fe Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 7 Oct 2020 10:21:32 -0700
Subject: [PATCH 42/70] clean up constants

---
 src/lib/Notification/PushNotification/NotificationType.js | 4 +---
 src/lib/Notification/PushNotification/index.js            | 2 +-
 src/lib/Notification/PushNotification/index.native.js     | 2 +-
 src/lib/actions/Report.js                                 | 4 ++--
 4 files changed, 5 insertions(+), 7 deletions(-)

diff --git a/src/lib/Notification/PushNotification/NotificationType.js b/src/lib/Notification/PushNotification/NotificationType.js
index 18d57c528731..1afa89d00201 100644
--- a/src/lib/Notification/PushNotification/NotificationType.js
+++ b/src/lib/Notification/PushNotification/NotificationType.js
@@ -3,7 +3,5 @@
  * types of push notifications sent by our API.
  */
 export default {
-    REPORT: {
-        COMMENT: 'reportComment',
-    }
+    REPORT_COMMENT: 'reportComment',
 };
diff --git a/src/lib/Notification/PushNotification/index.js b/src/lib/Notification/PushNotification/index.js
index e3a88d16764d..ad8c27a5ddba 100644
--- a/src/lib/Notification/PushNotification/index.js
+++ b/src/lib/Notification/PushNotification/index.js
@@ -6,5 +6,5 @@ export default {
     deregister: () => {},
     onReceived: () => {},
     onSelected: () => {},
-    NotificationType,
+    TYPE: NotificationType,
 };
diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index 549f0b781744..b1e5932677f4 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -117,5 +117,5 @@ export default {
     deregister,
     onReceived,
     onSelected,
-    NotificationType,
+    TYPE: NotificationType,
 };
diff --git a/src/lib/actions/Report.js b/src/lib/actions/Report.js
index 674b255637bc..70610b9dec11 100644
--- a/src/lib/actions/Report.js
+++ b/src/lib/actions/Report.js
@@ -236,12 +236,12 @@ function subscribeToReportCommentEvents() {
         updateReportWithNewAction(pushJSON.reportID, pushJSON.reportAction);
     });
 
-    PushNotification.onReceived(PushNotification.NotificationType.REPORT.COMMENT, ({reportID, reportAction}) => {
+    PushNotification.onReceived(PushNotification.TYPE.REPORT_COMMENT, ({reportID, reportAction}) => {
         updateReportWithNewAction(reportID, reportAction);
     });
 
     // Open correct report when push notification is clicked
-    PushNotification.onSelected(PushNotification.NotificationType.REPORT.COMMENT, ({reportID}) => {
+    PushNotification.onSelected(PushNotification.TYPE.REPORT_COMMENT, ({reportID}) => {
         redirect(reportID);
     });
 }

From 5ae956fe658e08ed4d0428256d7c67373f07b67d Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 7 Oct 2020 10:28:06 -0700
Subject: [PATCH 43/70] remove unnecessary call to PushNotification.register

---
 src/lib/API.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/lib/API.js b/src/lib/API.js
index d0e2adb35c71..5b79aa81def4 100644
--- a/src/lib/API.js
+++ b/src/lib/API.js
@@ -152,7 +152,6 @@ function request(command, parameters, type = 'post') {
                             PushNotification.register(response.accountID);
                         });
                 }
-                PushNotification.register(response.accountID);
             })
             .catch(error => Ion.merge(IONKEYS.SESSION, {error: error.message}));
     }

From bea51566075584e6d082b09c6977c3b4658fd848 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 7 Oct 2020 10:32:10 -0700
Subject: [PATCH 44/70] clean up console debugs

---
 .../PushNotification/index.native.js           | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index b1e5932677f4..7b2ce5a1c77f 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -41,11 +41,11 @@ function pushNotificationEventCallback(eventType, notification) {
     const actionMap = notificationEventActionMap[eventType];
     const payload = notification.extras?.payload;
 
-    console.debug(`[PUSH_NOTIFICATION] ${eventType} - {
-                title: ${notification.title},
-                message: ${notification.alert},
-                payload: ${payload}
-            }`);
+    console.debug(`[PUSH_NOTIFICATION] ${eventType}`, {
+        title: notification.title,
+        message: notification.alert,
+        payload
+    });
 
     // If a push notification is received while the app is in foreground,
     // we'll assume pusher is connected so we'll ignore this push notification
@@ -60,10 +60,10 @@ function pushNotificationEventCallback(eventType, notification) {
     }
 
     if (!actionMap[payload.type]) {
-        console.debug(`[PUSH_NOTIFICATION] No callback set up: {
-                    event: ${eventType},
-                    notification type: ${payload.type},
-                }`);
+        console.debug('[PUSH_NOTIFICATION] No callback set up: ', {
+            event: eventType,
+            notificationType: payload.type,
+        });
         return;
     }
 

From ffa719275bf2ae60051a8b7c49dbb75f214247d8 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 7 Oct 2020 10:52:27 -0700
Subject: [PATCH 45/70] clean up callback handler

---
 .../PushNotification/index.native.js             | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index 7b2ce5a1c77f..5e7cee8e83e6 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -38,7 +38,7 @@ function deregister() {
  * @param {object} notification
  */
 function pushNotificationEventCallback(eventType, notification) {
-    const actionMap = notificationEventActionMap[eventType];
+    const actionMap = notificationEventActionMap[eventType] ?? {};
     const payload = notification.extras?.payload;
 
     console.debug(`[PUSH_NOTIFICATION] ${eventType}`, {
@@ -47,27 +47,31 @@ function pushNotificationEventCallback(eventType, notification) {
         payload
     });
 
+    if (!payload) {
+        console.debug('[PUSH_NOTIFICATION] Notification has null or undefined payload, not executing any callback.');
+        return;
+    }
+
     // If a push notification is received while the app is in foreground,
     // we'll assume pusher is connected so we'll ignore this push notification
     if (AppState.currentState === 'active') {
-        console.debug('[PUSH_NOTIFICATION] App is in foreground, ignoring push notification.');
+        console.debug('[PUSH_NOTIFICATION] App is in foreground, not executing any callback.');
         return;
     }
 
-    if (!payload?.type) {
+    if (!payload.type) {
         console.debug('[PUSH_NOTIFICATION] Notification of unknown type received.');
         return;
     }
 
-    if (!actionMap[payload.type]) {
+    const action = actionMap[payload.type];
+    if (!action) {
         console.debug('[PUSH_NOTIFICATION] No callback set up: ', {
             event: eventType,
             notificationType: payload.type,
         });
         return;
     }
-
-    const action = actionMap[payload.type];
     action(payload);
 }
 

From 22ca23cdff6e1beec7b5c9ea50ab617649a9d3be Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 7 Oct 2020 11:17:52 -0700
Subject: [PATCH 46/70] add comments and nix Ion.clear changes

---
 src/lib/Ion.js                                      | 10 +---------
 .../Notification/PushNotification/index.native.js   | 13 ++++++++++++-
 2 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/src/lib/Ion.js b/src/lib/Ion.js
index 553277d0e31d..bdfb674e177c 100644
--- a/src/lib/Ion.js
+++ b/src/lib/Ion.js
@@ -211,17 +211,9 @@ function multiSet(data) {
 
 /**
  * Clear out all the data in the store
- *
- * @returns {Promise<void>}
  */
 function clear() {
-    return AsyncStorage.getAllKeys()
-        .then((keys) => {
-            AsyncStorage.clear();
-            _.each(keys, (key) => {
-                keyChanged(key, null);
-            });
-        });
+    AsyncStorage.clear();
 }
 
 /**
diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index 5e7cee8e83e6..28cb2bcf90f6 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -104,11 +104,22 @@ function bind(notificationType, callback, triggerEvent) {
     };
 }
 
-
+/**
+ * Bind a callback to be executed when a push notification of a given type is received.
+ *
+ * @param {string} notificationType
+ * @param {Function} callback
+ */
 function onReceived(notificationType, callback) {
     bind(notificationType, callback, EventType.PushReceived);
 }
 
+/**
+ * Bind a callback to be executed when a push notification of a given type is tapped by the user.
+ *
+ * @param {string} notificationType
+ * @param {Function} callback
+ */
 function onSelected(notificationType, callback) {
     bind(notificationType, callback, EventType.NotificationResponse);
 }

From 441677252f7f0486a5d9356db8d10fc8edf01c25 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 7 Oct 2020 11:23:50 -0700
Subject: [PATCH 47/70] remove unnecessary object copy in bind

---
 src/lib/Notification/PushNotification/index.native.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index 28cb2bcf90f6..6d0c8468067a 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -98,10 +98,10 @@ function setupEventListeners() {
  * @param {string?} triggerEvent - The event that should trigger this callback. Should be one of UrbanAirship.EventType
  */
 function bind(notificationType, callback, triggerEvent) {
-    notificationEventActionMap[triggerEvent] = {
-        ...notificationEventActionMap[triggerEvent],
-        [notificationType]: callback,
-    };
+    if (!notificationEventActionMap[triggerEvent]) {
+        notificationEventActionMap[triggerEvent] = {};
+    }
+    notificationEventActionMap[triggerEvent][notificationType] = callback;
 }
 
 /**

From f783f3d9fe0ce4764f9cdec9d12d1afc1786ef02 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 7 Oct 2020 11:30:39 -0700
Subject: [PATCH 48/70] use lodashGet instead of optional chaining

---
 src/lib/Notification/PushNotification/index.native.js | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index 6d0c8468067a..240b21cbbee8 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -1,5 +1,6 @@
 import {AppState} from 'react-native';
 import {UrbanAirship, EventType} from 'urbanairship-react-native';
+import lodashGet from 'lodash.get';
 import NotificationType from './NotificationType';
 
 const notificationEventActionMap = {};
@@ -39,7 +40,7 @@ function deregister() {
  */
 function pushNotificationEventCallback(eventType, notification) {
     const actionMap = notificationEventActionMap[eventType] ?? {};
-    const payload = notification.extras?.payload;
+    const payload = lodashGet(notification, 'extras.payload');
 
     console.debug(`[PUSH_NOTIFICATION] ${eventType}`, {
         title: notification.title,
@@ -83,6 +84,8 @@ function setupEventListeners() {
         pushNotificationEventCallback(EventType.PushReceived, notification);
     });
 
+    // Note: the NotificationResponse event has a nested PushReceived event,
+    // so event.notification refers to the same thing as notification above ^
     UrbanAirship.addListener(EventType.NotificationResponse, (event) => {
         pushNotificationEventCallback(EventType.NotificationResponse, event.notification);
     });

From 1ba64965879c46bb80e6eb6afa42276c3b01eff4 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 7 Oct 2020 11:49:57 -0700
Subject: [PATCH 49/70] add note about callback overwriting

---
 src/lib/Notification/PushNotification/index.native.js | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index 240b21cbbee8..838d775b23ed 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -96,6 +96,10 @@ function setupEventListeners() {
  * See https://github.com/Expensify/Web-Expensify/blob/master/lib/MobilePushNotifications.php for the various
  * types of push notifications sent, along with the data that they provide.
  *
+ * Note: This implementation allows for only one callback to be bound to an Event/Type pair. For example,
+ *       if we attempt to bind two callbacks to the PushReceived event for reportComment notifications,
+ *       the second will overwrite the first.
+ *
  * @param {string} notificationType
  * @param {Function} callback
  * @param {string?} triggerEvent - The event that should trigger this callback. Should be one of UrbanAirship.EventType

From 95818a54ac78d47beb5d815bc2e6eed683fa6627 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 7 Oct 2020 12:09:26 -0700
Subject: [PATCH 50/70] return promise from Ion.clear

---
 src/lib/Ion.js | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/lib/Ion.js b/src/lib/Ion.js
index bdfb674e177c..4f165869b5fc 100644
--- a/src/lib/Ion.js
+++ b/src/lib/Ion.js
@@ -211,9 +211,11 @@ function multiSet(data) {
 
 /**
  * Clear out all the data in the store
+ *
+ * @returns {Promise<void>}
  */
 function clear() {
-    AsyncStorage.clear();
+    return AsyncStorage.clear();
 }
 
 /**

From 7645985bc97afdc9b88c39185800ff931f8a3c29 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 7 Oct 2020 12:13:54 -0700
Subject: [PATCH 51/70] use || instead of ??, and add comment

---
 src/lib/Notification/PushNotification/index.native.js | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index 838d775b23ed..186b09a2769e 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -19,7 +19,8 @@ function register(accountID) {
             }
         });
 
-    // Register this device as a named user in AirshipAPI
+    // Register this device as a named user in AirshipAPI.
+    // Regardless of opt-in status, we still want to receive silent push notifications.
     console.debug(`[PUSH_NOTIFICATIONS] Subscribing to notifications for account ID ${accountID}`);
     UrbanAirship.setNamedUser(accountID.toString());
 }
@@ -39,7 +40,7 @@ function deregister() {
  * @param {object} notification
  */
 function pushNotificationEventCallback(eventType, notification) {
-    const actionMap = notificationEventActionMap[eventType] ?? {};
+    const actionMap = notificationEventActionMap[eventType] || {};
     const payload = lodashGet(notification, 'extras.payload');
 
     console.debug(`[PUSH_NOTIFICATION] ${eventType}`, {

From 105fb11aff9aea7a08d753d3b497920ef898c768 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 7 Oct 2020 12:15:53 -0700
Subject: [PATCH 52/70] update comment on opt-in status

---
 src/lib/Notification/PushNotification/index.native.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index 186b09a2769e..5371c77e22d3 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -20,7 +20,7 @@ function register(accountID) {
         });
 
     // Register this device as a named user in AirshipAPI.
-    // Regardless of opt-in status, we still want to receive silent push notifications.
+    // Regardless of the user's opt-in status, we still want to receive silent push notifications.
     console.debug(`[PUSH_NOTIFICATIONS] Subscribing to notifications for account ID ${accountID}`);
     UrbanAirship.setNamedUser(accountID.toString());
 }

From a226e40a9408f8177ea5f60903e0aa63038639ba Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 7 Oct 2020 15:32:43 -0700
Subject: [PATCH 53/70] move PushNotification.register, update comment, fix
 redirection

---
 src/lib/API.js                                        | 4 ++--
 src/lib/Notification/PushNotification/index.native.js | 2 +-
 src/lib/actions/Report.js                             | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/lib/API.js b/src/lib/API.js
index 066d54a11941..bc8cb3f16911 100644
--- a/src/lib/API.js
+++ b/src/lib/API.js
@@ -122,8 +122,9 @@ function createLogin(login, password) {
  * @param {string} exitTo
  */
 function setSuccessfulSignInData(data, exitTo) {
-    const redirectTo = exitTo ? Str.normalizeUrl(exitTo) : ROUTES.ROOT;
+    PushNotification.register(data.accountID);
 
+    const redirectTo = exitTo ? Str.normalizeUrl(exitTo) : ROUTES.ROOT;
     Ion.multiSet({
         [IONKEYS.SESSION]: _.pick(data, 'authToken', 'accountID', 'email'),
         [IONKEYS.APP_REDIRECT_TO]: redirectTo
@@ -172,7 +173,6 @@ function request(command, parameters, type = 'post') {
                     return createLogin(Str.generateDeviceLoginID(), Guid())
                         .then(() => {
                             setSuccessfulSignInData(response, parameters.exitTo);
-                            PushNotification.register(response.accountID);
                         });
                 }
             })
diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index 5371c77e22d3..f4e96b333262 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -11,7 +11,7 @@ const notificationEventActionMap = {};
  * @param {string|int} accountID
  */
 function register(accountID) {
-    // Get permissions to display push notifications
+    // Get permissions to display push notifications (prompts user on iOS, but not Android)
     UrbanAirship.enableUserPushNotifications()
         .then((isEnabled) => {
             if (!isEnabled) {
diff --git a/src/lib/actions/Report.js b/src/lib/actions/Report.js
index 5dd950d7a60d..daa301f94353 100644
--- a/src/lib/actions/Report.js
+++ b/src/lib/actions/Report.js
@@ -248,7 +248,7 @@ function subscribeToReportCommentEvents() {
 
     // Open correct report when push notification is clicked
     PushNotification.onSelected(PushNotification.TYPE.REPORT_COMMENT, ({reportID}) => {
-        redirect(reportID);
+        redirect(ROUTES.getReportRoute(reportID));
     });
 }
 

From e721350632ea8ec58eba4d5c5cc5879e36e588f1 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Thu, 8 Oct 2020 15:27:53 -0700
Subject: [PATCH 54/70] Open to correct report

---
 src/IONKEYS.js             |  1 +
 src/lib/actions/Report.js  |  2 ++
 src/lib/actions/Sidebar.js | 34 ++++++++++++++++++++++++++++++++++
 src/page/home/HomePage.js  | 36 +++++++++++++++++++++++++++++++++++-
 4 files changed, 72 insertions(+), 1 deletion(-)
 create mode 100644 src/lib/actions/Sidebar.js

diff --git a/src/IONKEYS.js b/src/IONKEYS.js
index c804de758aee..596a0b106860 100644
--- a/src/IONKEYS.js
+++ b/src/IONKEYS.js
@@ -12,6 +12,7 @@ export default {
     NETWORK: 'network',
     PERSONAL_DETAILS: 'personalDetails',
     SESSION: 'session',
+    IS_SIDEBAR_SHOWN: 'isSidebarShown',
 
     // Collection Keys
     COLLECTION: {
diff --git a/src/lib/actions/Report.js b/src/lib/actions/Report.js
index daa301f94353..5e6ecb5b051d 100644
--- a/src/lib/actions/Report.js
+++ b/src/lib/actions/Report.js
@@ -16,6 +16,7 @@ import * as ActiveClientManager from '../ActiveClientManager';
 import Visibility from '../Visibility';
 import ROUTES from '../../ROUTES';
 import NetworkConnection from '../NetworkConnection';
+import {hide as hideSidebar} from './Sidebar';
 
 let currentUserEmail;
 let currentUserAccountID;
@@ -249,6 +250,7 @@ function subscribeToReportCommentEvents() {
     // Open correct report when push notification is clicked
     PushNotification.onSelected(PushNotification.TYPE.REPORT_COMMENT, ({reportID}) => {
         redirect(ROUTES.getReportRoute(reportID));
+        hideSidebar();
     });
 }
 
diff --git a/src/lib/actions/Sidebar.js b/src/lib/actions/Sidebar.js
new file mode 100644
index 000000000000..c2a58724e8b7
--- /dev/null
+++ b/src/lib/actions/Sidebar.js
@@ -0,0 +1,34 @@
+import Ion from '../Ion';
+import IONKEYS from '../../IONKEYS';
+
+let isSidebarShown;
+Ion.connect({
+    key: IONKEYS.IS_SIDEBAR_SHOWN,
+    callback: val => isSidebarShown = val,
+});
+
+function hide() {
+    if (isSidebarShown) {
+        Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, false);
+    } else {
+        console.debug("Attempting to hide sidebar, but it's already hidden.");
+    }
+}
+
+function show() {
+    if (!isSidebarShown) {
+        Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, true);
+    } else {
+        console.debug("Attempting to show sidebar, but it's already visible.");
+    }
+}
+
+function toggle() {
+    Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, !isSidebarShown);
+}
+
+export {
+    hide,
+    show,
+    toggle,
+};
diff --git a/src/page/home/HomePage.js b/src/page/home/HomePage.js
index ffca8a76933a..6f0ebaf1d12e 100644
--- a/src/page/home/HomePage.js
+++ b/src/page/home/HomePage.js
@@ -1,4 +1,5 @@
 import React from 'react';
+import PropTypes from 'prop-types';
 import {
     StatusBar,
     View,
@@ -17,11 +18,22 @@ import {fetch as fetchPersonalDetails} from '../../lib/actions/PersonalDetails';
 import * as Pusher from '../../lib/Pusher/pusher';
 import ROUTES from '../../ROUTES';
 import NetworkConnection from '../../lib/NetworkConnection';
+import IONKEYS from '../../IONKEYS';
+import withIon from '../../components/withIon';
 
 const windowSize = Dimensions.get('window');
 const widthBreakPoint = 1000;
 
-export default class App extends React.Component {
+// There are times where we need to be able to toggle the sidebar view from elsewhere in the application,
+// so this prop mirrors the internal state variable hamburgerShown, but can be modified via Ion
+const propTypes = {
+    sidebarShown: PropTypes.bool,
+};
+const defaultProps = {
+    sidebarShown: 'visible',
+};
+
+class App extends React.Component {
     constructor(props) {
         super(props);
 
@@ -54,6 +66,17 @@ export default class App extends React.Component {
         StatusBar.setTranslucent(true);
     }
 
+    componentDidUpdate(prevProps) {
+        if (this.props.sidebarShown === prevProps.sidebarShown
+            || this.props.sidebarShown === this.state.hamburgerShown) {
+            // Nothing has changed or needs to change
+            return;
+        }
+
+        // Visibility of the sidebar has been externally modified via Ion, toggle visibility
+        this.toggleHamburger();
+    }
+
     componentWillUnmount() {
         Dimensions.removeEventListener('change', this.toggleHamburgerBasedOnDimensions);
     }
@@ -189,3 +212,14 @@ export default class App extends React.Component {
         );
     }
 }
+
+App.propTypes = propTypes;
+App.defaultProps = defaultProps;
+
+export default withIon(
+    {
+        sidebarShown: {
+            key: IONKEYS.IS_SIDEBAR_SHOWN
+        },
+    },
+)(App);

From 3ddecd48cba9ec5206730a1df37d75491726e412 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Thu, 8 Oct 2020 16:13:00 -0700
Subject: [PATCH 55/70] fix bad merge

---
 ios/Podfile.lock                              |   2 +-
 ios/ReactNativeChat.xcodeproj/project.pbxproj | 155 +++++++++---------
 2 files changed, 78 insertions(+), 79 deletions(-)

diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 37b49e4dea84..d554e25729f7 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -513,8 +513,8 @@ SPEC CHECKSUMS:
   React-RCTVibration: 8e9fb25724a0805107fc1acc9075e26f814df454
   ReactCommon: 4167844018c9ed375cc01a843e9ee564399e53c3
   RNCAsyncStorage: db711e29e5e0500d9bd21aa0c2e397efa45302b1
-  Yoga: 7d13633d129fd179e01b8953d38d47be90db185a
   urbanairship-react-native: fa123940041a6a13ab7dac192e32833c53754f00
+  Yoga: 7d13633d129fd179e01b8953d38d47be90db185a
   YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
 
 PODFILE CHECKSUM: d03a22d8299d9564ca7fc55d0a779f6fbf0d2b37
diff --git a/ios/ReactNativeChat.xcodeproj/project.pbxproj b/ios/ReactNativeChat.xcodeproj/project.pbxproj
index 46d7ffcb51d0..6cbb0dd3fccf 100644
--- a/ios/ReactNativeChat.xcodeproj/project.pbxproj
+++ b/ios/ReactNativeChat.xcodeproj/project.pbxproj
@@ -8,7 +8,6 @@
 
 /* Begin PBXBuildFile section */
 		00E356F31AD99517003FC87E /* ReactNativeChatTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* ReactNativeChatTests.m */; };
-		097C76E5807C5DFC565DC655 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C4F81DB9C6DA6418525441E5 /* libPods-ReactNativeChat-ReactNativeChatTests.a */; };
 		12DD1878FCB9487C9F031C86 /* GTAmericaExpMono-Rg.otf in Resources */ = {isa = PBXBuildFile; fileRef = 8437A5A38F2047E0BCCD7C2F /* GTAmericaExpMono-Rg.otf */; };
 		13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
 		13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
@@ -20,10 +19,10 @@
 		1E76D5252522316A005A268F /* GTAmericaExp-Thin.otf in Resources */ = {isa = PBXBuildFile; fileRef = A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */; };
 		425866037F4C482AAB46CB8B /* GTAmericaExp-BdIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = A8D6F2F722FD4E66A38EBBB6 /* GTAmericaExp-BdIt.otf */; };
 		6856B78873B64C44A92E51DB /* GTAmericaExp-MdIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = DB5A1365442D4419AF6F08E5 /* GTAmericaExp-MdIt.otf */; };
-		76B5E637A882833D630D1395 /* libPods-ReactNativeChat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E51D584B72117ADCE3326B2 /* libPods-ReactNativeChat.a */; };
-		7B29927C98D70D00A8F49E77 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 114D30B1289BB8C2E09B148C /* libPods-ReactNativeChat-ReactNativeChatTests.a */; };
 		81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
 		8821A238A081483FA947BC4E /* GTAmericaExp-RgIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 918D7FEFF96242E6B5F5E14D /* GTAmericaExp-RgIt.otf */; };
+		B16D4BD326B0B9B4DA96C07B /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1ED810200DB87A6886D9047B /* libPods-ReactNativeChat-ReactNativeChatTests.a */; };
+		C7BD52ACC1778570CFDBD261 /* libPods-ReactNativeChat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AAF45C452A55F5015AFBA2DA /* libPods-ReactNativeChat.a */; };
 		E9DF872D2525201700607FDC /* AirshipConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9DF872C2525201700607FDC /* AirshipConfig.plist */; };
 /* End PBXBuildFile section */
 
@@ -42,32 +41,32 @@
 		00E356EE1AD99517003FC87E /* ReactNativeChatTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactNativeChatTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		00E356F21AD99517003FC87E /* ReactNativeChatTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReactNativeChatTests.m; sourceTree = "<group>"; };
-		114D30B1289BB8C2E09B148C /* libPods-ReactNativeChat-ReactNativeChatTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat-ReactNativeChatTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		11400CBD046A6B49C0F58522 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; sourceTree = "<group>"; };
 		13B07F961A680F5B00A75B9A /* Chat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Chat.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ReactNativeChat/AppDelegate.h; sourceTree = "<group>"; };
 		13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = ReactNativeChat/AppDelegate.m; sourceTree = "<group>"; };
 		13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = ReactNativeChat/Images.xcassets; sourceTree = "<group>"; };
 		13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ReactNativeChat/Info.plist; sourceTree = "<group>"; };
 		13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ReactNativeChat/main.m; sourceTree = "<group>"; };
-		1573AB335CA25AB88FB106C0 /* Pods-ReactNativeChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.release.xcconfig"; sourceTree = "<group>"; };
+		1ED810200DB87A6886D9047B /* libPods-ReactNativeChat-ReactNativeChatTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat-ReactNativeChatTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		45A1776C969AC6BC28A48FD5 /* Pods-ReactNativeChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.release.xcconfig"; sourceTree = "<group>"; };
 		67D5C3A6A7FA417C8A853FC1 /* GTAmericaExp-Light.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Light.otf"; path = "../assets/fonts/GTAmericaExp-Light.otf"; sourceTree = "<group>"; };
-		7E51D584B72117ADCE3326B2 /* libPods-ReactNativeChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		780FFFD9D89A54FBB0173A64 /* Pods-ReactNativeChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.debug.xcconfig"; sourceTree = "<group>"; };
 		81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = ReactNativeChat/LaunchScreen.storyboard; sourceTree = "<group>"; };
 		8437A5A38F2047E0BCCD7C2F /* GTAmericaExpMono-Rg.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExpMono-Rg.otf"; path = "../assets/fonts/GTAmericaExpMono-Rg.otf"; sourceTree = "<group>"; };
+		89558FC7B9FD9C2B1E64B3D6 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; sourceTree = "<group>"; };
 		8C7003903C1E4957824899BB /* GTAmericaExp-Regular.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Regular.otf"; path = "../assets/fonts/GTAmericaExp-Regular.otf"; sourceTree = "<group>"; };
-		918D7FEFF96242E6B5F5E14D /* GTAmericaExp-RgIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-RgIt.otf"; path = "../assets/fonts/GTAmericaExp-RgIt.otf"; sourceTree = "<group>"; };
+		918D7FEFF96242E6B5F5E14D /* GTAmericaExp-RgIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-RgIt.otf"; path = "../assets/fonts/GTAmericaExp-RgIt.otf"; sourceTree = "<group>"; };
 		A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Thin.otf"; path = "../assets/fonts/GTAmericaExp-Thin.otf"; sourceTree = "<group>"; };
 		A5AAD008CBD84A6CAEB9AC97 /* GTAmericaExp-Bold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Bold.otf"; path = "../assets/fonts/GTAmericaExp-Bold.otf"; sourceTree = "<group>"; };
-		A8D6F2F722FD4E66A38EBBB6 /* GTAmericaExp-BdIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-BdIt.otf"; path = "../assets/fonts/GTAmericaExp-BdIt.otf"; sourceTree = "<group>"; };
+		A8D6F2F722FD4E66A38EBBB6 /* GTAmericaExp-BdIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-BdIt.otf"; path = "../assets/fonts/GTAmericaExp-BdIt.otf"; sourceTree = "<group>"; };
+		AAF45C452A55F5015AFBA2DA /* libPods-ReactNativeChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		AE65058949E14DA5A2D5435D /* GTAmericaExp-Medium.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Medium.otf"; path = "../assets/fonts/GTAmericaExp-Medium.otf"; sourceTree = "<group>"; };
-		DB5A1365442D4419AF6F08E5 /* GTAmericaExp-MdIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-MdIt.otf"; path = "../assets/fonts/GTAmericaExp-MdIt.otf"; sourceTree = "<group>"; };
-		DFFB9269BD80E5509E6F5318 /* Pods-ReactNativeChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.debug.xcconfig"; sourceTree = "<group>"; };
-		E3AB9696F691D1C5D449D583 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; sourceTree = "<group>"; };
+		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 = "<group>"; };
 		E9DF86F625251E4A00607FDC /* Chat.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Chat.entitlements; path = ReactNativeChat/Chat.entitlements; sourceTree = "<group>"; };
 		E9DF872C2525201700607FDC /* AirshipConfig.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = AirshipConfig.plist; sourceTree = "<group>"; };
 		ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
 		ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; };
-		FE4548417FE26480911F90C0 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -75,7 +74,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				7B29927C98D70D00A8F49E77 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */,
+				B16D4BD326B0B9B4DA96C07B /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -83,7 +82,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				76B5E637A882833D630D1395 /* libPods-ReactNativeChat.a in Frameworks */,
+				C7BD52ACC1778570CFDBD261 /* libPods-ReactNativeChat.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -128,8 +127,8 @@
 			children = (
 				ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
 				ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
-				7E51D584B72117ADCE3326B2 /* libPods-ReactNativeChat.a */,
-				114D30B1289BB8C2E09B148C /* libPods-ReactNativeChat-ReactNativeChatTests.a */,
+				AAF45C452A55F5015AFBA2DA /* libPods-ReactNativeChat.a */,
+				1ED810200DB87A6886D9047B /* libPods-ReactNativeChat-ReactNativeChatTests.a */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
@@ -185,10 +184,10 @@
 		EC29677F0A49C2946A495A33 /* Pods */ = {
 			isa = PBXGroup;
 			children = (
-				DFFB9269BD80E5509E6F5318 /* Pods-ReactNativeChat.debug.xcconfig */,
-				1573AB335CA25AB88FB106C0 /* Pods-ReactNativeChat.release.xcconfig */,
-				E3AB9696F691D1C5D449D583 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */,
-				FE4548417FE26480911F90C0 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */,
+				780FFFD9D89A54FBB0173A64 /* Pods-ReactNativeChat.debug.xcconfig */,
+				45A1776C969AC6BC28A48FD5 /* Pods-ReactNativeChat.release.xcconfig */,
+				11400CBD046A6B49C0F58522 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */,
+				89558FC7B9FD9C2B1E64B3D6 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */,
 			);
 			path = Pods;
 			sourceTree = "<group>";
@@ -200,11 +199,11 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "ReactNativeChatTests" */;
 			buildPhases = (
-				4B525D90C06C76F3070B31EC /* [CP] Check Pods Manifest.lock */,
+				5A19AC3AD32F2EC262BB3D81 /* [CP] Check Pods Manifest.lock */,
 				00E356EA1AD99517003FC87E /* Sources */,
 				00E356EB1AD99517003FC87E /* Frameworks */,
 				00E356EC1AD99517003FC87E /* Resources */,
-				4F03936CD68F10F4FD14F832 /* [CP] Copy Pods Resources */,
+				C02DF4DB6DD02FB0086EF92D /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -220,13 +219,13 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ReactNativeChat" */;
 			buildPhases = (
-				940DE2C6A94F35A5F9B805C7 /* [CP] Check Pods Manifest.lock */,
+				A0552D5A4933C592087CE272 /* [CP] Check Pods Manifest.lock */,
 				FD10A7F022414F080027D42C /* Start Packager */,
 				13B07F871A680F5B00A75B9A /* Sources */,
 				13B07F8C1A680F5B00A75B9A /* Frameworks */,
 				13B07F8E1A680F5B00A75B9A /* Resources */,
 				00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
-				5DE984ADFC7810B96C04A896 /* [CP] Copy Pods Resources */,
+				028A7F85A8806BD60E472C86 /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -320,35 +319,13 @@
 			shellPath = /bin/sh;
 			shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh";
 		};
-		4B525D90C06C76F3070B31EC /* [CP] Check Pods Manifest.lock */ = {
+		028A7F85A8806BD60E472C86 /* [CP] Copy Pods Resources */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 			);
-			inputFileListPaths = (
-			);
 			inputPaths = (
-				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
-				"${PODS_ROOT}/Manifest.lock",
-			);
-			name = "[CP] Check Pods Manifest.lock";
-			outputFileListPaths = (
-			);
-			outputPaths = (
-				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-ReactNativeChatTests-checkManifestLockResult.txt",
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
-			showEnvVarsInLog = 0;
-		};
-		4F03936CD68F10F4FD14F832 /* [CP] Copy Pods Resources */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputPaths = (
-				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh",
+				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAAutomationActions.plist",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerContentView.xib",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerView.xib",
@@ -467,16 +444,60 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh\"\n";
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
-		5DE984ADFC7810B96C04A896 /* [CP] Copy Pods Resources */ = {
+		5A19AC3AD32F2EC262BB3D81 /* [CP] Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 			);
+			inputFileListPaths = (
+			);
 			inputPaths = (
-				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh",
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-ReactNativeChatTests-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
+		A0552D5A4933C592087CE272 /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
+		C02DF4DB6DD02FB0086EF92D /* [CP] Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAAutomationActions.plist",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerContentView.xib",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerView.xib",
@@ -595,29 +616,7 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh\"\n";
-			showEnvVarsInLog = 0;
-		};
-		940DE2C6A94F35A5F9B805C7 /* [CP] Check Pods Manifest.lock */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputFileListPaths = (
-			);
-			inputPaths = (
-				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
-				"${PODS_ROOT}/Manifest.lock",
-			);
-			name = "[CP] Check Pods Manifest.lock";
-			outputFileListPaths = (
-			);
-			outputPaths = (
-				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-checkManifestLockResult.txt",
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
 		FD10A7F022414F080027D42C /* Start Packager */ = {
@@ -672,7 +671,7 @@
 /* Begin XCBuildConfiguration section */
 		00E356F61AD99517003FC87E /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = E3AB9696F691D1C5D449D583 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */;
+			baseConfigurationReference = 11400CBD046A6B49C0F58522 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
@@ -696,7 +695,7 @@
 		};
 		00E356F71AD99517003FC87E /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = FE4548417FE26480911F90C0 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */;
+			baseConfigurationReference = 89558FC7B9FD9C2B1E64B3D6 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
@@ -717,7 +716,7 @@
 		};
 		13B07F941A680F5B00A75B9A /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = DFFB9269BD80E5509E6F5318 /* Pods-ReactNativeChat.debug.xcconfig */;
+			baseConfigurationReference = 780FFFD9D89A54FBB0173A64 /* Pods-ReactNativeChat.debug.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
@@ -747,7 +746,7 @@
 		};
 		13B07F951A680F5B00A75B9A /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 1573AB335CA25AB88FB106C0 /* Pods-ReactNativeChat.release.xcconfig */;
+			baseConfigurationReference = 45A1776C969AC6BC28A48FD5 /* Pods-ReactNativeChat.release.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;

From 8d2624653d95d01ca9570120a4d779d1d4f77704 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Sat, 10 Oct 2020 01:49:56 -0700
Subject: [PATCH 56/70] refactor to use props instead of state

---
 ios/ReactNativeChat.xcodeproj/project.pbxproj | 62 +++++++++----------
 src/lib/actions/Report.js                     |  2 +-
 src/lib/actions/Sidebar.js                    | 23 ++-----
 src/page/home/HomePage.js                     | 62 +++++++++++--------
 4 files changed, 70 insertions(+), 79 deletions(-)

diff --git a/ios/ReactNativeChat.xcodeproj/project.pbxproj b/ios/ReactNativeChat.xcodeproj/project.pbxproj
index 6cbb0dd3fccf..d0916cd554f0 100644
--- a/ios/ReactNativeChat.xcodeproj/project.pbxproj
+++ b/ios/ReactNativeChat.xcodeproj/project.pbxproj
@@ -19,10 +19,9 @@
 		1E76D5252522316A005A268F /* GTAmericaExp-Thin.otf in Resources */ = {isa = PBXBuildFile; fileRef = A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */; };
 		425866037F4C482AAB46CB8B /* GTAmericaExp-BdIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = A8D6F2F722FD4E66A38EBBB6 /* GTAmericaExp-BdIt.otf */; };
 		6856B78873B64C44A92E51DB /* GTAmericaExp-MdIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = DB5A1365442D4419AF6F08E5 /* GTAmericaExp-MdIt.otf */; };
+		7FCE4151DD6F240D79C76D54 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CA1BDD8031A1E142B091B04C /* libPods-ReactNativeChat-ReactNativeChatTests.a */; };
 		81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
 		8821A238A081483FA947BC4E /* GTAmericaExp-RgIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 918D7FEFF96242E6B5F5E14D /* GTAmericaExp-RgIt.otf */; };
-		B16D4BD326B0B9B4DA96C07B /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1ED810200DB87A6886D9047B /* libPods-ReactNativeChat-ReactNativeChatTests.a */; };
-		C7BD52ACC1778570CFDBD261 /* libPods-ReactNativeChat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AAF45C452A55F5015AFBA2DA /* libPods-ReactNativeChat.a */; };
 		E9DF872D2525201700607FDC /* AirshipConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9DF872C2525201700607FDC /* AirshipConfig.plist */; };
 /* End PBXBuildFile section */
 
@@ -41,28 +40,28 @@
 		00E356EE1AD99517003FC87E /* ReactNativeChatTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactNativeChatTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		00E356F21AD99517003FC87E /* ReactNativeChatTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReactNativeChatTests.m; sourceTree = "<group>"; };
-		11400CBD046A6B49C0F58522 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; sourceTree = "<group>"; };
+		04747A2A129775BC6D5FB11E /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; sourceTree = "<group>"; };
 		13B07F961A680F5B00A75B9A /* Chat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Chat.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ReactNativeChat/AppDelegate.h; sourceTree = "<group>"; };
 		13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = ReactNativeChat/AppDelegate.m; sourceTree = "<group>"; };
 		13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = ReactNativeChat/Images.xcassets; sourceTree = "<group>"; };
 		13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ReactNativeChat/Info.plist; sourceTree = "<group>"; };
 		13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ReactNativeChat/main.m; sourceTree = "<group>"; };
-		1ED810200DB87A6886D9047B /* libPods-ReactNativeChat-ReactNativeChatTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat-ReactNativeChatTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
-		45A1776C969AC6BC28A48FD5 /* Pods-ReactNativeChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.release.xcconfig"; sourceTree = "<group>"; };
+		1B0B34DED5369216D0A764FC /* libPods-ReactNativeChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		5CC0C0D201D8B0E84349C5D9 /* Pods-ReactNativeChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.release.xcconfig"; sourceTree = "<group>"; };
 		67D5C3A6A7FA417C8A853FC1 /* GTAmericaExp-Light.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Light.otf"; path = "../assets/fonts/GTAmericaExp-Light.otf"; sourceTree = "<group>"; };
-		780FFFD9D89A54FBB0173A64 /* Pods-ReactNativeChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.debug.xcconfig"; sourceTree = "<group>"; };
+		74AADA7F1FF55EDBED3577EF /* Pods-ReactNativeChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.debug.xcconfig"; sourceTree = "<group>"; };
 		81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = ReactNativeChat/LaunchScreen.storyboard; sourceTree = "<group>"; };
 		8437A5A38F2047E0BCCD7C2F /* GTAmericaExpMono-Rg.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExpMono-Rg.otf"; path = "../assets/fonts/GTAmericaExpMono-Rg.otf"; sourceTree = "<group>"; };
-		89558FC7B9FD9C2B1E64B3D6 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; sourceTree = "<group>"; };
 		8C7003903C1E4957824899BB /* GTAmericaExp-Regular.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Regular.otf"; path = "../assets/fonts/GTAmericaExp-Regular.otf"; sourceTree = "<group>"; };
 		918D7FEFF96242E6B5F5E14D /* GTAmericaExp-RgIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-RgIt.otf"; path = "../assets/fonts/GTAmericaExp-RgIt.otf"; sourceTree = "<group>"; };
 		A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Thin.otf"; path = "../assets/fonts/GTAmericaExp-Thin.otf"; sourceTree = "<group>"; };
 		A5AAD008CBD84A6CAEB9AC97 /* GTAmericaExp-Bold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Bold.otf"; path = "../assets/fonts/GTAmericaExp-Bold.otf"; sourceTree = "<group>"; };
 		A8D6F2F722FD4E66A38EBBB6 /* GTAmericaExp-BdIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-BdIt.otf"; path = "../assets/fonts/GTAmericaExp-BdIt.otf"; sourceTree = "<group>"; };
-		AAF45C452A55F5015AFBA2DA /* libPods-ReactNativeChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		AE65058949E14DA5A2D5435D /* GTAmericaExp-Medium.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Medium.otf"; path = "../assets/fonts/GTAmericaExp-Medium.otf"; sourceTree = "<group>"; };
+		CA1BDD8031A1E142B091B04C /* libPods-ReactNativeChat-ReactNativeChatTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat-ReactNativeChatTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		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 = "<group>"; };
+		E26E64F06A3AAEBFEBC8DA39 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; sourceTree = "<group>"; };
 		E9DF86F625251E4A00607FDC /* Chat.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Chat.entitlements; path = ReactNativeChat/Chat.entitlements; sourceTree = "<group>"; };
 		E9DF872C2525201700607FDC /* AirshipConfig.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = AirshipConfig.plist; sourceTree = "<group>"; };
 		ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
@@ -74,7 +73,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				B16D4BD326B0B9B4DA96C07B /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */,
+				7FCE4151DD6F240D79C76D54 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -82,7 +81,6 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				C7BD52ACC1778570CFDBD261 /* libPods-ReactNativeChat.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -127,8 +125,8 @@
 			children = (
 				ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
 				ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
-				AAF45C452A55F5015AFBA2DA /* libPods-ReactNativeChat.a */,
-				1ED810200DB87A6886D9047B /* libPods-ReactNativeChat-ReactNativeChatTests.a */,
+				1B0B34DED5369216D0A764FC /* libPods-ReactNativeChat.a */,
+				CA1BDD8031A1E142B091B04C /* libPods-ReactNativeChat-ReactNativeChatTests.a */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
@@ -184,10 +182,10 @@
 		EC29677F0A49C2946A495A33 /* Pods */ = {
 			isa = PBXGroup;
 			children = (
-				780FFFD9D89A54FBB0173A64 /* Pods-ReactNativeChat.debug.xcconfig */,
-				45A1776C969AC6BC28A48FD5 /* Pods-ReactNativeChat.release.xcconfig */,
-				11400CBD046A6B49C0F58522 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */,
-				89558FC7B9FD9C2B1E64B3D6 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */,
+				74AADA7F1FF55EDBED3577EF /* Pods-ReactNativeChat.debug.xcconfig */,
+				5CC0C0D201D8B0E84349C5D9 /* Pods-ReactNativeChat.release.xcconfig */,
+				04747A2A129775BC6D5FB11E /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */,
+				E26E64F06A3AAEBFEBC8DA39 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */,
 			);
 			path = Pods;
 			sourceTree = "<group>";
@@ -199,11 +197,11 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "ReactNativeChatTests" */;
 			buildPhases = (
-				5A19AC3AD32F2EC262BB3D81 /* [CP] Check Pods Manifest.lock */,
+				4AA825B12E7B3F55B87F5C30 /* [CP] Check Pods Manifest.lock */,
 				00E356EA1AD99517003FC87E /* Sources */,
 				00E356EB1AD99517003FC87E /* Frameworks */,
 				00E356EC1AD99517003FC87E /* Resources */,
-				C02DF4DB6DD02FB0086EF92D /* [CP] Copy Pods Resources */,
+				1D66AEB2460B724558BAA81A /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -219,13 +217,13 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ReactNativeChat" */;
 			buildPhases = (
-				A0552D5A4933C592087CE272 /* [CP] Check Pods Manifest.lock */,
+				5E809F301DA69AF242773767 /* [CP] Check Pods Manifest.lock */,
 				FD10A7F022414F080027D42C /* Start Packager */,
 				13B07F871A680F5B00A75B9A /* Sources */,
 				13B07F8C1A680F5B00A75B9A /* Frameworks */,
 				13B07F8E1A680F5B00A75B9A /* Resources */,
 				00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
-				028A7F85A8806BD60E472C86 /* [CP] Copy Pods Resources */,
+				E83AE97FD5F7FEB8B5672146 /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -319,13 +317,13 @@
 			shellPath = /bin/sh;
 			shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh";
 		};
-		028A7F85A8806BD60E472C86 /* [CP] Copy Pods Resources */ = {
+		1D66AEB2460B724558BAA81A /* [CP] Copy Pods Resources */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 			);
 			inputPaths = (
-				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh",
+				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAAutomationActions.plist",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerContentView.xib",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerView.xib",
@@ -444,10 +442,10 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh\"\n";
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
-		5A19AC3AD32F2EC262BB3D81 /* [CP] Check Pods Manifest.lock */ = {
+		4AA825B12E7B3F55B87F5C30 /* [CP] Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -469,7 +467,7 @@
 			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
 			showEnvVarsInLog = 0;
 		};
-		A0552D5A4933C592087CE272 /* [CP] Check Pods Manifest.lock */ = {
+		5E809F301DA69AF242773767 /* [CP] Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -491,13 +489,13 @@
 			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
 			showEnvVarsInLog = 0;
 		};
-		C02DF4DB6DD02FB0086EF92D /* [CP] Copy Pods Resources */ = {
+		E83AE97FD5F7FEB8B5672146 /* [CP] Copy Pods Resources */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 			);
 			inputPaths = (
-				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh",
+				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAAutomationActions.plist",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerContentView.xib",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerView.xib",
@@ -616,7 +614,7 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh\"\n";
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
 		FD10A7F022414F080027D42C /* Start Packager */ = {
@@ -671,7 +669,7 @@
 /* Begin XCBuildConfiguration section */
 		00E356F61AD99517003FC87E /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 11400CBD046A6B49C0F58522 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */;
+			baseConfigurationReference = 04747A2A129775BC6D5FB11E /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
@@ -695,7 +693,7 @@
 		};
 		00E356F71AD99517003FC87E /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 89558FC7B9FD9C2B1E64B3D6 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */;
+			baseConfigurationReference = E26E64F06A3AAEBFEBC8DA39 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
@@ -716,7 +714,7 @@
 		};
 		13B07F941A680F5B00A75B9A /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 780FFFD9D89A54FBB0173A64 /* Pods-ReactNativeChat.debug.xcconfig */;
+			baseConfigurationReference = 74AADA7F1FF55EDBED3577EF /* Pods-ReactNativeChat.debug.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
@@ -746,7 +744,7 @@
 		};
 		13B07F951A680F5B00A75B9A /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 45A1776C969AC6BC28A48FD5 /* Pods-ReactNativeChat.release.xcconfig */;
+			baseConfigurationReference = 5CC0C0D201D8B0E84349C5D9 /* Pods-ReactNativeChat.release.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
diff --git a/src/lib/actions/Report.js b/src/lib/actions/Report.js
index 68c88484dbba..0e77ed574ed8 100644
--- a/src/lib/actions/Report.js
+++ b/src/lib/actions/Report.js
@@ -16,7 +16,7 @@ import * as ActiveClientManager from '../ActiveClientManager';
 import Visibility from '../Visibility';
 import ROUTES from '../../ROUTES';
 import NetworkConnection from '../NetworkConnection';
-import {hide as hideSidebar} from './Sidebar';
+import hideSidebar from './Sidebar';
 
 let currentUserEmail;
 let currentUserAccountID;
diff --git a/src/lib/actions/Sidebar.js b/src/lib/actions/Sidebar.js
index c2a58724e8b7..867fff90016d 100644
--- a/src/lib/actions/Sidebar.js
+++ b/src/lib/actions/Sidebar.js
@@ -7,28 +7,13 @@ Ion.connect({
     callback: val => isSidebarShown = val,
 });
 
+/**
+ * Hide the sidebar, if it is shown.
+ */
 function hide() {
     if (isSidebarShown) {
         Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, false);
-    } else {
-        console.debug("Attempting to hide sidebar, but it's already hidden.");
     }
 }
 
-function show() {
-    if (!isSidebarShown) {
-        Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, true);
-    } else {
-        console.debug("Attempting to show sidebar, but it's already visible.");
-    }
-}
-
-function toggle() {
-    Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, !isSidebarShown);
-}
-
-export {
-    hide,
-    show,
-    toggle,
-};
+export default hide;
diff --git a/src/page/home/HomePage.js b/src/page/home/HomePage.js
index 6f0ebaf1d12e..c322a5907b4f 100644
--- a/src/page/home/HomePage.js
+++ b/src/page/home/HomePage.js
@@ -18,6 +18,7 @@ import {fetch as fetchPersonalDetails} from '../../lib/actions/PersonalDetails';
 import * as Pusher from '../../lib/Pusher/pusher';
 import ROUTES from '../../ROUTES';
 import NetworkConnection from '../../lib/NetworkConnection';
+import Ion from '../../lib/Ion';
 import IONKEYS from '../../IONKEYS';
 import withIon from '../../components/withIon';
 
@@ -27,10 +28,10 @@ const widthBreakPoint = 1000;
 // There are times where we need to be able to toggle the sidebar view from elsewhere in the application,
 // so this prop mirrors the internal state variable hamburgerShown, but can be modified via Ion
 const propTypes = {
-    sidebarShown: PropTypes.bool,
+    hamburgerShown: PropTypes.bool,
 };
 const defaultProps = {
-    sidebarShown: 'visible',
+    hamburgerShown: true,
 };
 
 class App extends React.Component {
@@ -38,15 +39,19 @@ class App extends React.Component {
         super(props);
 
         this.state = {
-            hamburgerShown: true,
             isHamburgerEnabled: windowSize.width <= widthBreakPoint,
         };
 
+        // This flag differentiates between a completed animation and external modification of IONKEYS.SIDEBAR_SHOWN
+        this.isCompletedAnimation = false;
+
         this.toggleHamburger = this.toggleHamburger.bind(this);
         this.dismissHamburger = this.dismissHamburger.bind(this);
         this.showHamburger = this.showHamburger.bind(this);
         this.toggleHamburgerBasedOnDimensions = this.toggleHamburgerBasedOnDimensions.bind(this);
-        this.animationTranslateX = new Animated.Value(!this.state.hamburgerShown ? -300 : 0);
+        this.animationTranslateX = new Animated.Value(!props.hamburgerShown ? -300 : 0);
+
+        Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, props.hamburgerShown);
     }
 
     componentDidMount() {
@@ -67,14 +72,18 @@ class App extends React.Component {
     }
 
     componentDidUpdate(prevProps) {
-        if (this.props.sidebarShown === prevProps.sidebarShown
-            || this.props.sidebarShown === this.state.hamburgerShown) {
-            // Nothing has changed or needs to change
+        if (this.props.hamburgerShown === prevProps.hamburgerShown) {
+            // Nothing has changed
             return;
         }
 
-        // Visibility of the sidebar has been externally modified via Ion, toggle visibility
-        this.toggleHamburger();
+        // If an animation just completed, don't trigger a new one
+        if (this.isCompletedAnimation) {
+            this.isCompletedAnimation = false;
+            return;
+        }
+
+        this.animateHamburger(prevProps.hamburgerShown);
     }
 
     componentWillUnmount() {
@@ -87,10 +96,10 @@ class App extends React.Component {
      */
     toggleHamburgerBasedOnDimensions({window: changedWindow}) {
         this.setState({isHamburgerEnabled: changedWindow.width <= widthBreakPoint});
-        if (!this.state.hamburgerShown && changedWindow.width > widthBreakPoint) {
-            this.setState({hamburgerShown: true});
-        } else if (this.state.hamburgerShown && changedWindow.width < widthBreakPoint) {
-            this.setState({hamburgerShown: false});
+        if (!this.props.hamburgerShown && changedWindow.width > widthBreakPoint) {
+            Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, true);
+        } else if (this.props.hamburgerShown && changedWindow.width < widthBreakPoint) {
+            Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, false);
         }
     }
 
@@ -100,7 +109,7 @@ class App extends React.Component {
      * Only changes hamburger state on small screens (e.g. Mobile and mWeb)
      */
     dismissHamburger() {
-        if (!this.state.hamburgerShown) {
+        if (!this.props.hamburgerShown) {
             return;
         }
 
@@ -113,7 +122,7 @@ class App extends React.Component {
      * Only changes hamburger state on smaller screens (e.g. Mobile and mWeb)
      */
     showHamburger() {
-        if (this.state.hamburgerShown) {
+        if (this.props.hamburgerShown) {
             return;
         }
 
@@ -134,10 +143,9 @@ class App extends React.Component {
             easing: Easing.ease,
             useNativeDriver: false
         }).start(({finished}) => {
-            // If the hamburger is currently shown, we want to hide it only after the animation is complete
-            // Otherwise, we can't see the animation
             if (finished && hamburgerIsShown) {
-                this.setState({hamburgerShown: false});
+                this.isCompletedAnimation = true;
+                Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, !hamburgerIsShown);
             }
         });
     }
@@ -151,20 +159,20 @@ class App extends React.Component {
             return;
         }
 
-        const hamburgerIsShown = this.state.hamburgerShown;
-
-        // If the hamburger currently is not shown, we want to immediately make it visible for the animation
-        if (!hamburgerIsShown) {
-            this.setState({hamburgerShown: true});
+        // If the hamburger currently is not shown, we want to make it visible before the animation
+        if (!this.state.hamburgerShown) {
+            Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, true);
+            return;
         }
 
-        this.animateHamburger(hamburgerIsShown);
+        // Otherwise, we want to hide it after the animation
+        this.animateHamburger(true);
     }
 
     render() {
-        const hamburgerStyle = this.state.isHamburgerEnabled && this.state.hamburgerShown
+        const hamburgerStyle = this.state.isHamburgerEnabled && this.props.hamburgerShown
             ? styles.hamburgerOpenAbsolute : styles.hamburgerOpen;
-        const visibility = this.state.hamburgerShown ? styles.dFlex : styles.dNone;
+        const visibility = this.props.hamburgerShown ? styles.dFlex : styles.dNone;
         const appContentWrapperStyle = !this.state.isHamburgerEnabled ? styles.appContentWrapperLarge : null;
         const appContentStyle = !this.state.isHamburgerEnabled ? styles.appContentRounded : null;
         return (
@@ -218,7 +226,7 @@ App.defaultProps = defaultProps;
 
 export default withIon(
     {
-        sidebarShown: {
+        hamburgerShown: {
             key: IONKEYS.IS_SIDEBAR_SHOWN
         },
     },

From 0c80e86d0d4c44c34ff4b5f7eccfbc16f1f2d56b Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Sat, 10 Oct 2020 02:15:34 -0700
Subject: [PATCH 57/70] fix bad merge

---
 ios/Podfile.lock                              |  23 ++-
 ios/ReactNativeChat.xcodeproj/project.pbxproj | 141 +++++++++---------
 src/page/home/HomePage.js                     |   2 +-
 3 files changed, 95 insertions(+), 71 deletions(-)

diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 8c1457e58c0e..1bcc9d1e74cd 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -1,4 +1,16 @@
 PODS:
+  - Airship (13.5.4):
+    - Airship/Automation (= 13.5.4)
+    - Airship/Core (= 13.5.4)
+    - Airship/ExtendedActions (= 13.5.4)
+    - Airship/MessageCenter (= 13.5.4)
+  - Airship/Automation (13.5.4):
+    - Airship/Core
+  - Airship/Core (13.5.4)
+  - Airship/ExtendedActions (13.5.4):
+    - Airship/Core
+  - Airship/MessageCenter (13.5.4):
+    - Airship/Core
   - boost-for-react-native (1.63.0)
   - CocoaAsyncSocket (7.6.4)
   - CocoaLibEvent (1.0.0)
@@ -312,6 +324,9 @@ PODS:
     - React
   - RNCPushNotificationIOS (1.5.0):
     - React
+  - urbanairship-react-native (8.1.0):
+    - Airship (= 13.5.4)
+    - React
   - Yoga (1.14.0)
   - YogaKit (1.18.1):
     - Yoga (~> 1.14)
@@ -370,10 +385,12 @@ DEPENDENCIES:
   - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
   - "RNCAsyncStorage (from `../node_modules/@react-native-community/async-storage`)"
   - "RNCPushNotificationIOS (from `../node_modules/@react-native-community/push-notification-ios`)"
+  - urbanairship-react-native (from `../node_modules/urbanairship-react-native`)
   - Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
 
 SPEC REPOS:
   trunk:
+    - Airship
     - boost-for-react-native
     - CocoaAsyncSocket
     - CocoaLibEvent
@@ -452,10 +469,13 @@ EXTERNAL SOURCES:
     :path: "../node_modules/@react-native-community/async-storage"
   RNCPushNotificationIOS:
     :path: "../node_modules/@react-native-community/push-notification-ios"
+  urbanairship-react-native:
+    :path: "../node_modules/urbanairship-react-native"
   Yoga:
     :path: "../node_modules/react-native/ReactCommon/yoga"
 
 SPEC CHECKSUMS:
+  Airship: cbbd372a4ffc8dcfeacec01fd623155ec41f33ed
   boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
   CocoaAsyncSocket: 694058e7c0ed05a9e217d1b3c7ded962f4180845
   CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f
@@ -499,9 +519,10 @@ SPEC CHECKSUMS:
   ReactCommon: 4167844018c9ed375cc01a843e9ee564399e53c3
   RNCAsyncStorage: db711e29e5e0500d9bd21aa0c2e397efa45302b1
   RNCPushNotificationIOS: 8025ff0b610d7b28d29ddc1b619cd55814362e4c
+  urbanairship-react-native: fa123940041a6a13ab7dac192e32833c53754f00
   Yoga: 7d13633d129fd179e01b8953d38d47be90db185a
   YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
 
-PODFILE CHECKSUM: 7f6bb9c130ea13236f8249f800f75d671c1833fa
+PODFILE CHECKSUM: d03a22d8299d9564ca7fc55d0a779f6fbf0d2b37
 
 COCOAPODS: 1.9.3
diff --git a/ios/ReactNativeChat.xcodeproj/project.pbxproj b/ios/ReactNativeChat.xcodeproj/project.pbxproj
index 8aa0c5b956dc..fba4798478f9 100644
--- a/ios/ReactNativeChat.xcodeproj/project.pbxproj
+++ b/ios/ReactNativeChat.xcodeproj/project.pbxproj
@@ -19,11 +19,11 @@
 		1E76D5252522316A005A268F /* GTAmericaExp-Thin.otf in Resources */ = {isa = PBXBuildFile; fileRef = A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */; };
 		425866037F4C482AAB46CB8B /* GTAmericaExp-BdIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = A8D6F2F722FD4E66A38EBBB6 /* GTAmericaExp-BdIt.otf */; };
 		6856B78873B64C44A92E51DB /* GTAmericaExp-MdIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = DB5A1365442D4419AF6F08E5 /* GTAmericaExp-MdIt.otf */; };
-		712B637C71EDACB60BFCCFDF /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5198B14DF64811534104106B /* libPods-ReactNativeChat-ReactNativeChatTests.a */; };
-		7EF88515CE8703E46E771C2D /* libPods-ReactNativeChat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B2E64A28C6D8EC058C5C3144 /* libPods-ReactNativeChat.a */; };
+		7EF88515CE8703E46E771C2D /* (null) in Frameworks */ = {isa = PBXBuildFile; };
 		81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
 		8821A238A081483FA947BC4E /* GTAmericaExp-RgIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 918D7FEFF96242E6B5F5E14D /* GTAmericaExp-RgIt.otf */; };
 		E9DF872D2525201700607FDC /* AirshipConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9DF872C2525201700607FDC /* AirshipConfig.plist */; };
+		F574082BD3E243B7DA37B0BA /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8012DB2DB4A4578220958B92 /* libPods-ReactNativeChat-ReactNativeChatTests.a */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -41,27 +41,28 @@
 		00E356EE1AD99517003FC87E /* ReactNativeChatTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactNativeChatTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		00E356F21AD99517003FC87E /* ReactNativeChatTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReactNativeChatTests.m; sourceTree = "<group>"; };
-		04747A2A129775BC6D5FB11E /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; sourceTree = "<group>"; };
 		13B07F961A680F5B00A75B9A /* Chat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Chat.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ReactNativeChat/AppDelegate.h; sourceTree = "<group>"; };
 		13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = ReactNativeChat/AppDelegate.m; sourceTree = "<group>"; };
 		13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = ReactNativeChat/Images.xcassets; sourceTree = "<group>"; };
 		13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ReactNativeChat/Info.plist; sourceTree = "<group>"; };
 		13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ReactNativeChat/main.m; sourceTree = "<group>"; };
-		5CC0C0D201D8B0E84349C5D9 /* Pods-ReactNativeChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.release.xcconfig"; sourceTree = "<group>"; };
+		547FB1575833AEDE29F25896 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; sourceTree = "<group>"; };
 		67D5C3A6A7FA417C8A853FC1 /* GTAmericaExp-Light.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Light.otf"; path = "../assets/fonts/GTAmericaExp-Light.otf"; sourceTree = "<group>"; };
-		74AADA7F1FF55EDBED3577EF /* Pods-ReactNativeChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.debug.xcconfig"; sourceTree = "<group>"; };
+		8012DB2DB4A4578220958B92 /* libPods-ReactNativeChat-ReactNativeChatTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat-ReactNativeChatTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = ReactNativeChat/LaunchScreen.storyboard; sourceTree = "<group>"; };
 		8437A5A38F2047E0BCCD7C2F /* GTAmericaExpMono-Rg.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExpMono-Rg.otf"; path = "../assets/fonts/GTAmericaExpMono-Rg.otf"; sourceTree = "<group>"; };
+		84BEA7302209B03FEC158B9A /* Pods-ReactNativeChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.debug.xcconfig"; sourceTree = "<group>"; };
 		8C7003903C1E4957824899BB /* GTAmericaExp-Regular.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Regular.otf"; path = "../assets/fonts/GTAmericaExp-Regular.otf"; sourceTree = "<group>"; };
-		918D7FEFF96242E6B5F5E14D /* GTAmericaExp-RgIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-RgIt.otf"; path = "../assets/fonts/GTAmericaExp-RgIt.otf"; sourceTree = "<group>"; };
+		918D7FEFF96242E6B5F5E14D /* GTAmericaExp-RgIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-RgIt.otf"; path = "../assets/fonts/GTAmericaExp-RgIt.otf"; sourceTree = "<group>"; };
 		A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Thin.otf"; path = "../assets/fonts/GTAmericaExp-Thin.otf"; sourceTree = "<group>"; };
 		A5AAD008CBD84A6CAEB9AC97 /* GTAmericaExp-Bold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Bold.otf"; path = "../assets/fonts/GTAmericaExp-Bold.otf"; sourceTree = "<group>"; };
 		A8D6F2F722FD4E66A38EBBB6 /* GTAmericaExp-BdIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-BdIt.otf"; path = "../assets/fonts/GTAmericaExp-BdIt.otf"; sourceTree = "<group>"; };
 		AE65058949E14DA5A2D5435D /* GTAmericaExp-Medium.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Medium.otf"; path = "../assets/fonts/GTAmericaExp-Medium.otf"; sourceTree = "<group>"; };
-		CA1BDD8031A1E142B091B04C /* libPods-ReactNativeChat-ReactNativeChatTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat-ReactNativeChatTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		BC4434CE76865567DAD7D7FB /* libPods-ReactNativeChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		BF057013BA158EE4172DAA52 /* Pods-ReactNativeChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.release.xcconfig"; sourceTree = "<group>"; };
+		D8D3A45F65070233676930CF /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; sourceTree = "<group>"; };
 		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 = "<group>"; };
-		E26E64F06A3AAEBFEBC8DA39 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; sourceTree = "<group>"; };
 		E9DF86F625251E4A00607FDC /* Chat.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Chat.entitlements; path = ReactNativeChat/Chat.entitlements; sourceTree = "<group>"; };
 		E9DF872C2525201700607FDC /* AirshipConfig.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = AirshipConfig.plist; sourceTree = "<group>"; };
 		ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
@@ -73,7 +74,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				7FCE4151DD6F240D79C76D54 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */,
+				F574082BD3E243B7DA37B0BA /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -81,7 +82,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				7EF88515CE8703E46E771C2D /* libPods-ReactNativeChat.a in Frameworks */,
+				7EF88515CE8703E46E771C2D /* (null) in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -126,8 +127,8 @@
 			children = (
 				ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
 				ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
-				B2E64A28C6D8EC058C5C3144 /* libPods-ReactNativeChat.a */,
-				5198B14DF64811534104106B /* libPods-ReactNativeChat-ReactNativeChatTests.a */,
+				BC4434CE76865567DAD7D7FB /* libPods-ReactNativeChat.a */,
+				8012DB2DB4A4578220958B92 /* libPods-ReactNativeChat-ReactNativeChatTests.a */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
@@ -183,10 +184,10 @@
 		EC29677F0A49C2946A495A33 /* Pods */ = {
 			isa = PBXGroup;
 			children = (
-				66FC9F8F0AF1190BE595FC04 /* Pods-ReactNativeChat.debug.xcconfig */,
-				2061509B833DB0123E4416F5 /* Pods-ReactNativeChat.release.xcconfig */,
-				BB3B578785B6B7D4E739A8C8 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */,
-				B81A8F09B6065AF7D0A5D2CD /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */,
+				84BEA7302209B03FEC158B9A /* Pods-ReactNativeChat.debug.xcconfig */,
+				BF057013BA158EE4172DAA52 /* Pods-ReactNativeChat.release.xcconfig */,
+				D8D3A45F65070233676930CF /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */,
+				547FB1575833AEDE29F25896 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */,
 			);
 			path = Pods;
 			sourceTree = "<group>";
@@ -198,11 +199,11 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "ReactNativeChatTests" */;
 			buildPhases = (
-				ECEBF6D927AD6913C7D49FAA /* [CP] Check Pods Manifest.lock */,
+				C5B995E95E82AA45C89CA88A /* [CP] Check Pods Manifest.lock */,
 				00E356EA1AD99517003FC87E /* Sources */,
 				00E356EB1AD99517003FC87E /* Frameworks */,
 				00E356EC1AD99517003FC87E /* Resources */,
-				96F29378CAD503EE24A4238F /* [CP] Copy Pods Resources */,
+				64450B67A8B6ADA8A8D3C47D /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -218,13 +219,13 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ReactNativeChat" */;
 			buildPhases = (
-				C918B32D93F5DFC09167D222 /* [CP] Check Pods Manifest.lock */,
+				A3E5D3E0B3D2144C5839A88A /* [CP] Check Pods Manifest.lock */,
 				FD10A7F022414F080027D42C /* Start Packager */,
 				13B07F871A680F5B00A75B9A /* Sources */,
 				13B07F8C1A680F5B00A75B9A /* Frameworks */,
 				13B07F8E1A680F5B00A75B9A /* Resources */,
 				00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
-				2BEB5135F1A6A0AFC39DEE60 /* [CP] Copy Pods Resources */,
+				9BDBE41A2934927B5C02A879 /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -318,7 +319,7 @@
 			shellPath = /bin/sh;
 			shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh";
 		};
-		2BEB5135F1A6A0AFC39DEE60 /* [CP] Copy Pods Resources */ = {
+		64450B67A8B6ADA8A8D3C47D /* [CP] Copy Pods Resources */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -446,47 +447,7 @@
 			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
-		4AA825B12E7B3F55B87F5C30 /* [CP] Check Pods Manifest.lock */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputPaths = (
-				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh",
-				"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
-			);
-			name = "[CP] Copy Pods Resources";
-			outputPaths = (
-				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh\"\n";
-			showEnvVarsInLog = 0;
-		};
-		C918B32D93F5DFC09167D222 /* [CP] Check Pods Manifest.lock */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputFileListPaths = (
-			);
-			inputPaths = (
-				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
-				"${PODS_ROOT}/Manifest.lock",
-			);
-			name = "[CP] Check Pods Manifest.lock";
-			outputFileListPaths = (
-			);
-			outputPaths = (
-				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-checkManifestLockResult.txt",
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
-			showEnvVarsInLog = 0;
-		};
-		ECEBF6D927AD6913C7D49FAA /* [CP] Check Pods Manifest.lock */ = {
+		9BDBE41A2934927B5C02A879 /* [CP] Copy Pods Resources */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -550,9 +511,7 @@
 				"${PODS_ROOT}/Airship/Airship/AirshipMessageCenter/Resources/UAInbox.xcdatamodeld",
 				"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
 			);
-			name = "[CP] Check Pods Manifest.lock";
-			outputFileListPaths = (
-			);
+			name = "[CP] Copy Pods Resources";
 			outputPaths = (
 				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAAutomationActions.plist",
 				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/UAInAppMessageBannerContentView.nib",
@@ -613,6 +572,50 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		A3E5D3E0B3D2144C5839A88A /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
+		C5B995E95E82AA45C89CA88A /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-ReactNativeChatTests-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
 			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
 			showEnvVarsInLog = 0;
 		};
@@ -668,7 +671,7 @@
 /* Begin XCBuildConfiguration section */
 		00E356F61AD99517003FC87E /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = BB3B578785B6B7D4E739A8C8 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */;
+			baseConfigurationReference = D8D3A45F65070233676930CF /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
@@ -692,7 +695,7 @@
 		};
 		00E356F71AD99517003FC87E /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = B81A8F09B6065AF7D0A5D2CD /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */;
+			baseConfigurationReference = 547FB1575833AEDE29F25896 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
@@ -713,7 +716,7 @@
 		};
 		13B07F941A680F5B00A75B9A /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 66FC9F8F0AF1190BE595FC04 /* Pods-ReactNativeChat.debug.xcconfig */;
+			baseConfigurationReference = 84BEA7302209B03FEC158B9A /* Pods-ReactNativeChat.debug.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
@@ -743,7 +746,7 @@
 		};
 		13B07F951A680F5B00A75B9A /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 2061509B833DB0123E4416F5 /* Pods-ReactNativeChat.release.xcconfig */;
+			baseConfigurationReference = BF057013BA158EE4172DAA52 /* Pods-ReactNativeChat.release.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
diff --git a/src/page/home/HomePage.js b/src/page/home/HomePage.js
index 487db2fe446c..c27b4fe973d9 100644
--- a/src/page/home/HomePage.js
+++ b/src/page/home/HomePage.js
@@ -163,7 +163,7 @@ class App extends React.Component {
         }
 
         // If the hamburger currently is not shown, we want to make it visible before the animation
-        if (!this.state.hamburgerShown) {
+        if (!this.props.hamburgerShown) {
             Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, true);
             return;
         }

From a6ef9432625b0d214fe1e94c22727364bbdc490c Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Mon, 12 Oct 2020 12:35:09 -0700
Subject: [PATCH 58/70] rename hamburgerShown prop to sidebarShown

---
 src/page/home/HomePage.js | 30 ++++++++++++++----------------
 1 file changed, 14 insertions(+), 16 deletions(-)

diff --git a/src/page/home/HomePage.js b/src/page/home/HomePage.js
index c27b4fe973d9..53eb50cdec04 100644
--- a/src/page/home/HomePage.js
+++ b/src/page/home/HomePage.js
@@ -26,13 +26,11 @@ import withIon from '../../components/withIon';
 const windowSize = Dimensions.get('window');
 const widthBreakPoint = 1000;
 
-// There are times where we need to be able to toggle the sidebar view from elsewhere in the application,
-// so this prop mirrors the internal state variable hamburgerShown, but can be modified via Ion
 const propTypes = {
-    hamburgerShown: PropTypes.bool,
+    sidebarShown: PropTypes.bool,
 };
 const defaultProps = {
-    hamburgerShown: true,
+    sidebarShown: true,
 };
 
 class App extends React.Component {
@@ -50,9 +48,9 @@ class App extends React.Component {
         this.dismissHamburger = this.dismissHamburger.bind(this);
         this.showHamburger = this.showHamburger.bind(this);
         this.toggleHamburgerBasedOnDimensions = this.toggleHamburgerBasedOnDimensions.bind(this);
-        this.animationTranslateX = new Animated.Value(!props.hamburgerShown ? -300 : 0);
+        this.animationTranslateX = new Animated.Value(!props.sidebarShown ? -300 : 0);
 
-        Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, props.hamburgerShown);
+        Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, props.sidebarShown);
     }
 
     componentDidMount() {
@@ -75,7 +73,7 @@ class App extends React.Component {
     }
 
     componentDidUpdate(prevProps) {
-        if (this.props.hamburgerShown === prevProps.hamburgerShown) {
+        if (this.props.sidebarShown === prevProps.sidebarShown) {
             // Nothing has changed
             return;
         }
@@ -86,7 +84,7 @@ class App extends React.Component {
             return;
         }
 
-        this.animateHamburger(prevProps.hamburgerShown);
+        this.animateHamburger(prevProps.sidebarShown);
     }
 
     componentWillUnmount() {
@@ -99,9 +97,9 @@ class App extends React.Component {
      */
     toggleHamburgerBasedOnDimensions({window: changedWindow}) {
         this.setState({isHamburgerEnabled: changedWindow.width <= widthBreakPoint});
-        if (!this.props.hamburgerShown && changedWindow.width > widthBreakPoint) {
+        if (!this.props.sidebarShown && changedWindow.width > widthBreakPoint) {
             Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, true);
-        } else if (this.props.hamburgerShown && changedWindow.width < widthBreakPoint) {
+        } else if (this.props.sidebarShown && changedWindow.width < widthBreakPoint) {
             Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, false);
         }
     }
@@ -112,7 +110,7 @@ class App extends React.Component {
      * Only changes hamburger state on small screens (e.g. Mobile and mWeb)
      */
     dismissHamburger() {
-        if (!this.props.hamburgerShown) {
+        if (!this.props.sidebarShown) {
             return;
         }
 
@@ -125,7 +123,7 @@ class App extends React.Component {
      * Only changes hamburger state on smaller screens (e.g. Mobile and mWeb)
      */
     showHamburger() {
-        if (this.props.hamburgerShown) {
+        if (this.props.sidebarShown) {
             return;
         }
 
@@ -163,7 +161,7 @@ class App extends React.Component {
         }
 
         // If the hamburger currently is not shown, we want to make it visible before the animation
-        if (!this.props.hamburgerShown) {
+        if (!this.props.sidebarShown) {
             Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, true);
             return;
         }
@@ -173,9 +171,9 @@ class App extends React.Component {
     }
 
     render() {
-        const hamburgerStyle = this.state.isHamburgerEnabled && this.props.hamburgerShown
+        const hamburgerStyle = this.state.isHamburgerEnabled && this.props.sidebarShown
             ? styles.hamburgerOpenAbsolute : styles.hamburgerOpen;
-        const visibility = this.props.hamburgerShown ? styles.dFlex : styles.dNone;
+        const visibility = this.props.sidebarShown ? styles.dFlex : styles.dNone;
         const appContentWrapperStyle = !this.state.isHamburgerEnabled ? styles.appContentWrapperLarge : null;
         const appContentStyle = !this.state.isHamburgerEnabled ? styles.appContentRounded : null;
         return (
@@ -229,7 +227,7 @@ App.defaultProps = defaultProps;
 
 export default withIon(
     {
-        hamburgerShown: {
+        sidebarShown: {
             key: IONKEYS.IS_SIDEBAR_SHOWN
         },
     },

From 3aa3db993c2d5ca8fc29573962bdc5d1e42d6db7 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Mon, 12 Oct 2020 12:42:03 -0700
Subject: [PATCH 59/70] use actions instead of ion

---
 src/lib/actions/Report.js  |  2 +-
 src/lib/actions/Sidebar.js | 11 ++++++++++-
 src/page/home/HomePage.js  | 16 +++++++++-------
 3 files changed, 20 insertions(+), 9 deletions(-)

diff --git a/src/lib/actions/Report.js b/src/lib/actions/Report.js
index 0e77ed574ed8..68c88484dbba 100644
--- a/src/lib/actions/Report.js
+++ b/src/lib/actions/Report.js
@@ -16,7 +16,7 @@ import * as ActiveClientManager from '../ActiveClientManager';
 import Visibility from '../Visibility';
 import ROUTES from '../../ROUTES';
 import NetworkConnection from '../NetworkConnection';
-import hideSidebar from './Sidebar';
+import {hide as hideSidebar} from './Sidebar';
 
 let currentUserEmail;
 let currentUserAccountID;
diff --git a/src/lib/actions/Sidebar.js b/src/lib/actions/Sidebar.js
index 867fff90016d..b863877172b2 100644
--- a/src/lib/actions/Sidebar.js
+++ b/src/lib/actions/Sidebar.js
@@ -16,4 +16,13 @@ function hide() {
     }
 }
 
-export default hide;
+function show() {
+    if (!isSidebarShown) {
+        Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, true);
+    }
+}
+
+export {
+    hide,
+    show,
+};
diff --git a/src/page/home/HomePage.js b/src/page/home/HomePage.js
index 53eb50cdec04..efff5c72565f 100644
--- a/src/page/home/HomePage.js
+++ b/src/page/home/HomePage.js
@@ -13,13 +13,13 @@ import styles, {getSafeAreaPadding} from '../../style/StyleSheet';
 import Header from './HeaderView';
 import Sidebar from './sidebar/SidebarView';
 import Main from './MainView';
+import {hide as hideSidebar, show as showSidebar} from '../../lib/actions/Sidebar';
 import {subscribeToReportCommentEvents, fetchAll as fetchAllReports} from '../../lib/actions/Report';
 import {fetch as fetchPersonalDetails} from '../../lib/actions/PersonalDetails';
 import * as Pusher from '../../lib/Pusher/pusher';
 import UnreadIndicatorUpdater from '../../lib/UnreadIndicatorUpdater';
 import ROUTES from '../../ROUTES';
 import NetworkConnection from '../../lib/NetworkConnection';
-import Ion from '../../lib/Ion';
 import IONKEYS from '../../IONKEYS';
 import withIon from '../../components/withIon';
 
@@ -49,8 +49,6 @@ class App extends React.Component {
         this.showHamburger = this.showHamburger.bind(this);
         this.toggleHamburgerBasedOnDimensions = this.toggleHamburgerBasedOnDimensions.bind(this);
         this.animationTranslateX = new Animated.Value(!props.sidebarShown ? -300 : 0);
-
-        Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, props.sidebarShown);
     }
 
     componentDidMount() {
@@ -98,9 +96,9 @@ class App extends React.Component {
     toggleHamburgerBasedOnDimensions({window: changedWindow}) {
         this.setState({isHamburgerEnabled: changedWindow.width <= widthBreakPoint});
         if (!this.props.sidebarShown && changedWindow.width > widthBreakPoint) {
-            Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, true);
+            showSidebar();
         } else if (this.props.sidebarShown && changedWindow.width < widthBreakPoint) {
-            Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, false);
+            hideSidebar();
         }
     }
 
@@ -146,7 +144,11 @@ class App extends React.Component {
         }).start(({finished}) => {
             if (finished && hamburgerIsShown) {
                 this.isCompletedAnimation = true;
-                Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, !hamburgerIsShown);
+                if (hamburgerIsShown) {
+                    hideSidebar();
+                } else {
+                    showSidebar();
+                }
             }
         });
     }
@@ -162,7 +164,7 @@ class App extends React.Component {
 
         // If the hamburger currently is not shown, we want to make it visible before the animation
         if (!this.props.sidebarShown) {
-            Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, true);
+            showSidebar();
             return;
         }
 

From e01231bb7f9d0b93319df07fd48efaab7fb40938 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Mon, 12 Oct 2020 16:42:18 -0700
Subject: [PATCH 60/70] rename prop to match ion key

---
 src/page/home/HomePage.js | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/src/page/home/HomePage.js b/src/page/home/HomePage.js
index efff5c72565f..a97437be4300 100644
--- a/src/page/home/HomePage.js
+++ b/src/page/home/HomePage.js
@@ -27,10 +27,10 @@ const windowSize = Dimensions.get('window');
 const widthBreakPoint = 1000;
 
 const propTypes = {
-    sidebarShown: PropTypes.bool,
+    isSidebarShown: PropTypes.bool,
 };
 const defaultProps = {
-    sidebarShown: true,
+    isSidebarShown: true,
 };
 
 class App extends React.Component {
@@ -48,7 +48,7 @@ class App extends React.Component {
         this.dismissHamburger = this.dismissHamburger.bind(this);
         this.showHamburger = this.showHamburger.bind(this);
         this.toggleHamburgerBasedOnDimensions = this.toggleHamburgerBasedOnDimensions.bind(this);
-        this.animationTranslateX = new Animated.Value(!props.sidebarShown ? -300 : 0);
+        this.animationTranslateX = new Animated.Value(!props.isSidebarShown ? -300 : 0);
     }
 
     componentDidMount() {
@@ -71,7 +71,7 @@ class App extends React.Component {
     }
 
     componentDidUpdate(prevProps) {
-        if (this.props.sidebarShown === prevProps.sidebarShown) {
+        if (this.props.isSidebarShown === prevProps.isSidebarShown) {
             // Nothing has changed
             return;
         }
@@ -82,7 +82,7 @@ class App extends React.Component {
             return;
         }
 
-        this.animateHamburger(prevProps.sidebarShown);
+        this.animateHamburger(prevProps.isSidebarShown);
     }
 
     componentWillUnmount() {
@@ -95,9 +95,9 @@ class App extends React.Component {
      */
     toggleHamburgerBasedOnDimensions({window: changedWindow}) {
         this.setState({isHamburgerEnabled: changedWindow.width <= widthBreakPoint});
-        if (!this.props.sidebarShown && changedWindow.width > widthBreakPoint) {
+        if (!this.props.isSidebarShown && changedWindow.width > widthBreakPoint) {
             showSidebar();
-        } else if (this.props.sidebarShown && changedWindow.width < widthBreakPoint) {
+        } else if (this.props.isSidebarShown && changedWindow.width < widthBreakPoint) {
             hideSidebar();
         }
     }
@@ -108,7 +108,7 @@ class App extends React.Component {
      * Only changes hamburger state on small screens (e.g. Mobile and mWeb)
      */
     dismissHamburger() {
-        if (!this.props.sidebarShown) {
+        if (!this.props.isSidebarShown) {
             return;
         }
 
@@ -121,7 +121,7 @@ class App extends React.Component {
      * Only changes hamburger state on smaller screens (e.g. Mobile and mWeb)
      */
     showHamburger() {
-        if (this.props.sidebarShown) {
+        if (this.props.isSidebarShown) {
             return;
         }
 
@@ -163,7 +163,7 @@ class App extends React.Component {
         }
 
         // If the hamburger currently is not shown, we want to make it visible before the animation
-        if (!this.props.sidebarShown) {
+        if (!this.props.isSidebarShown) {
             showSidebar();
             return;
         }
@@ -173,9 +173,9 @@ class App extends React.Component {
     }
 
     render() {
-        const hamburgerStyle = this.state.isHamburgerEnabled && this.props.sidebarShown
+        const hamburgerStyle = this.state.isHamburgerEnabled && this.props.isSidebarShown
             ? styles.hamburgerOpenAbsolute : styles.hamburgerOpen;
-        const visibility = this.props.sidebarShown ? styles.dFlex : styles.dNone;
+        const visibility = this.props.isSidebarShown ? styles.dFlex : styles.dNone;
         const appContentWrapperStyle = !this.state.isHamburgerEnabled ? styles.appContentWrapperLarge : null;
         const appContentStyle = !this.state.isHamburgerEnabled ? styles.appContentRounded : null;
         return (
@@ -229,7 +229,7 @@ App.defaultProps = defaultProps;
 
 export default withIon(
     {
-        sidebarShown: {
+        isSidebarShown: {
             key: IONKEYS.IS_SIDEBAR_SHOWN
         },
     },

From 0bcc2e971d5d693b710fefe4e12356de58c906f2 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Mon, 12 Oct 2020 16:56:42 -0700
Subject: [PATCH 61/70] get rid of unnecessary check in componentDidUpdate

---
 src/page/home/HomePage.js | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/src/page/home/HomePage.js b/src/page/home/HomePage.js
index a97437be4300..b7e2d0421053 100644
--- a/src/page/home/HomePage.js
+++ b/src/page/home/HomePage.js
@@ -71,11 +71,6 @@ class App extends React.Component {
     }
 
     componentDidUpdate(prevProps) {
-        if (this.props.isSidebarShown === prevProps.isSidebarShown) {
-            // Nothing has changed
-            return;
-        }
-
         // If an animation just completed, don't trigger a new one
         if (this.isCompletedAnimation) {
             this.isCompletedAnimation = false;

From 76613041b771925a4ce164a20768f1d5194a9979 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Tue, 13 Oct 2020 11:19:09 -0700
Subject: [PATCH 62/70] get rid of isCompletedAnimation flag and fix issue with
 desktop load

---
 src/page/home/HomePage.js | 14 +++-----------
 1 file changed, 3 insertions(+), 11 deletions(-)

diff --git a/src/page/home/HomePage.js b/src/page/home/HomePage.js
index b7e2d0421053..d42c1522c0b5 100644
--- a/src/page/home/HomePage.js
+++ b/src/page/home/HomePage.js
@@ -41,9 +41,6 @@ class App extends React.Component {
             isHamburgerEnabled: windowSize.width <= widthBreakPoint,
         };
 
-        // This flag differentiates between a completed animation and external modification of IONKEYS.SIDEBAR_SHOWN
-        this.isCompletedAnimation = false;
-
         this.toggleHamburger = this.toggleHamburger.bind(this);
         this.dismissHamburger = this.dismissHamburger.bind(this);
         this.showHamburger = this.showHamburger.bind(this);
@@ -71,12 +68,10 @@ class App extends React.Component {
     }
 
     componentDidUpdate(prevProps) {
-        // If an animation just completed, don't trigger a new one
-        if (this.isCompletedAnimation) {
-            this.isCompletedAnimation = false;
+        if (this.props.isSidebarShown === prevProps.isSidebarShown) {
+            // Nothing changed, don't trigger animation or re-render
             return;
         }
-
         this.animateHamburger(prevProps.isSidebarShown);
     }
 
@@ -138,11 +133,8 @@ class App extends React.Component {
             useNativeDriver: false
         }).start(({finished}) => {
             if (finished && hamburgerIsShown) {
-                this.isCompletedAnimation = true;
                 if (hamburgerIsShown) {
                     hideSidebar();
-                } else {
-                    showSidebar();
                 }
             }
         });
@@ -170,7 +162,7 @@ class App extends React.Component {
     render() {
         const hamburgerStyle = this.state.isHamburgerEnabled && this.props.isSidebarShown
             ? styles.hamburgerOpenAbsolute : styles.hamburgerOpen;
-        const visibility = this.props.isSidebarShown ? styles.dFlex : styles.dNone;
+        const visibility = !this.state.isHamburgerEnabled || this.props.isSidebarShown ? styles.dFlex : styles.dNone;
         const appContentWrapperStyle = !this.state.isHamburgerEnabled ? styles.appContentWrapperLarge : null;
         const appContentStyle = !this.state.isHamburgerEnabled ? styles.appContentRounded : null;
         return (

From fd2a44a7813b079920792d2e796ef383f5e38612 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Tue, 13 Oct 2020 17:37:05 -0700
Subject: [PATCH 63/70] add method doc and remove redundancies

---
 src/lib/actions/Sidebar.js | 3 +++
 src/page/home/HomePage.js  | 6 ++----
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/src/lib/actions/Sidebar.js b/src/lib/actions/Sidebar.js
index b863877172b2..625b1a382421 100644
--- a/src/lib/actions/Sidebar.js
+++ b/src/lib/actions/Sidebar.js
@@ -16,6 +16,9 @@ function hide() {
     }
 }
 
+/**
+ * Show the sidebar, if it is hidden.
+ */
 function show() {
     if (!isSidebarShown) {
         Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, true);
diff --git a/src/page/home/HomePage.js b/src/page/home/HomePage.js
index d42c1522c0b5..8dceac46acb0 100644
--- a/src/page/home/HomePage.js
+++ b/src/page/home/HomePage.js
@@ -133,9 +133,7 @@ class App extends React.Component {
             useNativeDriver: false
         }).start(({finished}) => {
             if (finished && hamburgerIsShown) {
-                if (hamburgerIsShown) {
-                    hideSidebar();
-                }
+                hideSidebar();
             }
         });
     }
@@ -162,7 +160,7 @@ class App extends React.Component {
     render() {
         const hamburgerStyle = this.state.isHamburgerEnabled && this.props.isSidebarShown
             ? styles.hamburgerOpenAbsolute : styles.hamburgerOpen;
-        const visibility = !this.state.isHamburgerEnabled || this.props.isSidebarShown ? styles.dFlex : styles.dNone;
+        const visibility = this.props.isSidebarShown ? styles.dFlex : styles.dNone;
         const appContentWrapperStyle = !this.state.isHamburgerEnabled ? styles.appContentWrapperLarge : null;
         const appContentStyle = !this.state.isHamburgerEnabled ? styles.appContentRounded : null;
         return (

From 73218300ccd549d41e8ab9bb8471cd35040e5027 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Tue, 13 Oct 2020 17:40:05 -0700
Subject: [PATCH 64/70] move setupEventListeners into register

---
 .../PushNotification/index.native.js          | 60 +++++++++----------
 1 file changed, 27 insertions(+), 33 deletions(-)

diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index f4e96b333262..bd0a87b87188 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -5,34 +5,6 @@ import NotificationType from './NotificationType';
 
 const notificationEventActionMap = {};
 
-/**
- * Register this device for push notifications for the given accountID.
- *
- * @param {string|int} accountID
- */
-function register(accountID) {
-    // Get permissions to display push notifications (prompts user on iOS, but not Android)
-    UrbanAirship.enableUserPushNotifications()
-        .then((isEnabled) => {
-            if (!isEnabled) {
-                console.debug('[PUSH_NOTIFICATIONS] User has disabled visible push notifications for this app.');
-            }
-        });
-
-    // Register this device as a named user in AirshipAPI.
-    // Regardless of the user's opt-in status, we still want to receive silent push notifications.
-    console.debug(`[PUSH_NOTIFICATIONS] Subscribing to notifications for account ID ${accountID}`);
-    UrbanAirship.setNamedUser(accountID.toString());
-}
-
-/**
- * Deregister this device from push notifications.
- */
-function deregister() {
-    console.debug('[PUSH_NOTIFICATIONS] Unsubscribing from push notifications.');
-    UrbanAirship.setNamedUser(null);
-}
-
 /**
  * Handle a push notification event, and trigger and bound actions.
  *
@@ -78,9 +50,25 @@ function pushNotificationEventCallback(eventType, notification) {
 }
 
 /**
- * Setup listener for push notification events.
+ * Register this device for push notifications for the given accountID.
+ *
+ * @param {string|int} accountID
  */
-function setupEventListeners() {
+function register(accountID) {
+    // Get permissions to display push notifications (prompts user on iOS, but not Android)
+    UrbanAirship.enableUserPushNotifications()
+        .then((isEnabled) => {
+            if (!isEnabled) {
+                console.debug('[PUSH_NOTIFICATIONS] User has disabled visible push notifications for this app.');
+            }
+        });
+
+    // Register this device as a named user in AirshipAPI.
+    // Regardless of the user's opt-in status, we still want to receive silent push notifications.
+    console.debug(`[PUSH_NOTIFICATIONS] Subscribing to notifications for account ID ${accountID}`);
+    UrbanAirship.setNamedUser(accountID.toString());
+
+    // Setup event listeners
     UrbanAirship.addListener(EventType.PushReceived, (notification) => {
         pushNotificationEventCallback(EventType.PushReceived, notification);
     });
@@ -92,6 +80,15 @@ function setupEventListeners() {
     });
 }
 
+/**
+ * Deregister this device from push notifications.
+ */
+function deregister() {
+    console.debug('[PUSH_NOTIFICATIONS] Unsubscribing from push notifications.');
+    UrbanAirship.setNamedUser(null);
+    UrbanAirship.removeAllListeners();
+}
+
 /**
  * Bind a callback to a push notification of a given type.
  * See https://github.com/Expensify/Web-Expensify/blob/master/lib/MobilePushNotifications.php for the various
@@ -132,9 +129,6 @@ function onSelected(notificationType, callback) {
     bind(notificationType, callback, EventType.NotificationResponse);
 }
 
-// Setup the listeners when this module first loads
-setupEventListeners();
-
 export default {
     register,
     deregister,

From 9eb059889358cb234f612e7298b547f4786a7bc7 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Tue, 13 Oct 2020 18:49:11 -0700
Subject: [PATCH 65/70] revert desktop changes and fix android bug

---
 src/lib/Notification/PushNotification/index.native.js | 9 +++++++--
 src/page/home/HomePage.js                             | 2 +-
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index bd0a87b87188..a4e71ea3525a 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -1,3 +1,4 @@
+import _ from 'underscore';
 import {AppState} from 'react-native';
 import {UrbanAirship, EventType} from 'urbanairship-react-native';
 import lodashGet from 'lodash.get';
@@ -13,7 +14,10 @@ const notificationEventActionMap = {};
  */
 function pushNotificationEventCallback(eventType, notification) {
     const actionMap = notificationEventActionMap[eventType] || {};
-    const payload = lodashGet(notification, 'extras.payload');
+    let payload = lodashGet(notification, 'extras.payload');
+    if (_.isString(payload)) {
+        payload = JSON.parse(payload);
+    }
 
     console.debug(`[PUSH_NOTIFICATION] ${eventType}`, {
         title: notification.title,
@@ -86,7 +90,8 @@ function register(accountID) {
 function deregister() {
     console.debug('[PUSH_NOTIFICATIONS] Unsubscribing from push notifications.');
     UrbanAirship.setNamedUser(null);
-    UrbanAirship.removeAllListeners();
+    UrbanAirship.removeAllListeners(EventType.PushReceived);
+    UrbanAirship.removeAllListeners(EventType.NotificationResponse);
 }
 
 /**
diff --git a/src/page/home/HomePage.js b/src/page/home/HomePage.js
index 8dceac46acb0..68b26644fb9c 100644
--- a/src/page/home/HomePage.js
+++ b/src/page/home/HomePage.js
@@ -160,7 +160,7 @@ class App extends React.Component {
     render() {
         const hamburgerStyle = this.state.isHamburgerEnabled && this.props.isSidebarShown
             ? styles.hamburgerOpenAbsolute : styles.hamburgerOpen;
-        const visibility = this.props.isSidebarShown ? styles.dFlex : styles.dNone;
+        const visibility = !this.state.isHamburgerEnabled || this.props.isSidebarShown ? styles.dFlex : styles.dNone;
         const appContentWrapperStyle = !this.state.isHamburgerEnabled ? styles.appContentWrapperLarge : null;
         const appContentStyle = !this.state.isHamburgerEnabled ? styles.appContentRounded : null;
         return (

From 479b045f232b9bbee14f61a75c2328e944dac374 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 14 Oct 2020 16:15:16 -0700
Subject: [PATCH 66/70] fix desktop initial render bug and signin/signout bug

---
 ios/ReactNativeChat.xcodeproj/project.pbxproj | 142 +++++++++---------
 .../PushNotification/index.native.js          |   2 +
 src/lib/actions/Sidebar.js                    |  14 +-
 src/page/home/HomePage.js                     |   8 +-
 4 files changed, 83 insertions(+), 83 deletions(-)

diff --git a/ios/ReactNativeChat.xcodeproj/project.pbxproj b/ios/ReactNativeChat.xcodeproj/project.pbxproj
index fba4798478f9..d8c2eb0613bc 100644
--- a/ios/ReactNativeChat.xcodeproj/project.pbxproj
+++ b/ios/ReactNativeChat.xcodeproj/project.pbxproj
@@ -19,11 +19,12 @@
 		1E76D5252522316A005A268F /* GTAmericaExp-Thin.otf in Resources */ = {isa = PBXBuildFile; fileRef = A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */; };
 		425866037F4C482AAB46CB8B /* GTAmericaExp-BdIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = A8D6F2F722FD4E66A38EBBB6 /* GTAmericaExp-BdIt.otf */; };
 		6856B78873B64C44A92E51DB /* GTAmericaExp-MdIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = DB5A1365442D4419AF6F08E5 /* GTAmericaExp-MdIt.otf */; };
-		7EF88515CE8703E46E771C2D /* (null) in Frameworks */ = {isa = PBXBuildFile; };
+		7EF88515CE8703E46E771C2D /* BuildFile in Frameworks */ = {isa = PBXBuildFile; };
 		81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
 		8821A238A081483FA947BC4E /* GTAmericaExp-RgIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 918D7FEFF96242E6B5F5E14D /* GTAmericaExp-RgIt.otf */; };
+		AB606A6D424AE2B476BEFD07 /* libPods-ReactNativeChat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A5E21B3B4603003659AA9250 /* libPods-ReactNativeChat.a */; };
+		E1866E081AB386749BB5FE39 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BF94FAF26294857A369B1127 /* libPods-ReactNativeChat-ReactNativeChatTests.a */; };
 		E9DF872D2525201700607FDC /* AirshipConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9DF872C2525201700607FDC /* AirshipConfig.plist */; };
-		F574082BD3E243B7DA37B0BA /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8012DB2DB4A4578220958B92 /* libPods-ReactNativeChat-ReactNativeChatTests.a */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -47,21 +48,21 @@
 		13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = ReactNativeChat/Images.xcassets; sourceTree = "<group>"; };
 		13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ReactNativeChat/Info.plist; sourceTree = "<group>"; };
 		13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ReactNativeChat/main.m; sourceTree = "<group>"; };
-		547FB1575833AEDE29F25896 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; sourceTree = "<group>"; };
+		20AC44D93509707BC89B1013 /* Pods-ReactNativeChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.release.xcconfig"; sourceTree = "<group>"; };
+		3CF68E161AFD219803827BFA /* Pods-ReactNativeChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.debug.xcconfig"; sourceTree = "<group>"; };
+		4836FA3D8151BC83FFA2F12F /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; sourceTree = "<group>"; };
 		67D5C3A6A7FA417C8A853FC1 /* GTAmericaExp-Light.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Light.otf"; path = "../assets/fonts/GTAmericaExp-Light.otf"; sourceTree = "<group>"; };
-		8012DB2DB4A4578220958B92 /* libPods-ReactNativeChat-ReactNativeChatTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat-ReactNativeChatTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		72FEF6AF05EAD544ED1EB5F4 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; sourceTree = "<group>"; };
 		81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = ReactNativeChat/LaunchScreen.storyboard; sourceTree = "<group>"; };
 		8437A5A38F2047E0BCCD7C2F /* GTAmericaExpMono-Rg.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExpMono-Rg.otf"; path = "../assets/fonts/GTAmericaExpMono-Rg.otf"; sourceTree = "<group>"; };
-		84BEA7302209B03FEC158B9A /* Pods-ReactNativeChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.debug.xcconfig"; sourceTree = "<group>"; };
 		8C7003903C1E4957824899BB /* GTAmericaExp-Regular.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Regular.otf"; path = "../assets/fonts/GTAmericaExp-Regular.otf"; sourceTree = "<group>"; };
 		918D7FEFF96242E6B5F5E14D /* GTAmericaExp-RgIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-RgIt.otf"; path = "../assets/fonts/GTAmericaExp-RgIt.otf"; sourceTree = "<group>"; };
 		A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Thin.otf"; path = "../assets/fonts/GTAmericaExp-Thin.otf"; sourceTree = "<group>"; };
 		A5AAD008CBD84A6CAEB9AC97 /* GTAmericaExp-Bold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Bold.otf"; path = "../assets/fonts/GTAmericaExp-Bold.otf"; sourceTree = "<group>"; };
+		A5E21B3B4603003659AA9250 /* libPods-ReactNativeChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		A8D6F2F722FD4E66A38EBBB6 /* GTAmericaExp-BdIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-BdIt.otf"; path = "../assets/fonts/GTAmericaExp-BdIt.otf"; sourceTree = "<group>"; };
 		AE65058949E14DA5A2D5435D /* GTAmericaExp-Medium.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Medium.otf"; path = "../assets/fonts/GTAmericaExp-Medium.otf"; sourceTree = "<group>"; };
-		BC4434CE76865567DAD7D7FB /* libPods-ReactNativeChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat.a"; sourceTree = BUILT_PRODUCTS_DIR; };
-		BF057013BA158EE4172DAA52 /* Pods-ReactNativeChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.release.xcconfig"; sourceTree = "<group>"; };
-		D8D3A45F65070233676930CF /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; sourceTree = "<group>"; };
+		BF94FAF26294857A369B1127 /* libPods-ReactNativeChat-ReactNativeChatTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat-ReactNativeChatTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		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 = "<group>"; };
 		E9DF86F625251E4A00607FDC /* Chat.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Chat.entitlements; path = ReactNativeChat/Chat.entitlements; sourceTree = "<group>"; };
 		E9DF872C2525201700607FDC /* AirshipConfig.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = AirshipConfig.plist; sourceTree = "<group>"; };
@@ -74,7 +75,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				F574082BD3E243B7DA37B0BA /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */,
+				E1866E081AB386749BB5FE39 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -82,7 +83,8 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				7EF88515CE8703E46E771C2D /* (null) in Frameworks */,
+				7EF88515CE8703E46E771C2D /* BuildFile in Frameworks */,
+				AB606A6D424AE2B476BEFD07 /* libPods-ReactNativeChat.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -127,8 +129,8 @@
 			children = (
 				ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
 				ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
-				BC4434CE76865567DAD7D7FB /* libPods-ReactNativeChat.a */,
-				8012DB2DB4A4578220958B92 /* libPods-ReactNativeChat-ReactNativeChatTests.a */,
+				A5E21B3B4603003659AA9250 /* libPods-ReactNativeChat.a */,
+				BF94FAF26294857A369B1127 /* libPods-ReactNativeChat-ReactNativeChatTests.a */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
@@ -184,10 +186,10 @@
 		EC29677F0A49C2946A495A33 /* Pods */ = {
 			isa = PBXGroup;
 			children = (
-				84BEA7302209B03FEC158B9A /* Pods-ReactNativeChat.debug.xcconfig */,
-				BF057013BA158EE4172DAA52 /* Pods-ReactNativeChat.release.xcconfig */,
-				D8D3A45F65070233676930CF /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */,
-				547FB1575833AEDE29F25896 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */,
+				3CF68E161AFD219803827BFA /* Pods-ReactNativeChat.debug.xcconfig */,
+				20AC44D93509707BC89B1013 /* Pods-ReactNativeChat.release.xcconfig */,
+				4836FA3D8151BC83FFA2F12F /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */,
+				72FEF6AF05EAD544ED1EB5F4 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */,
 			);
 			path = Pods;
 			sourceTree = "<group>";
@@ -199,11 +201,11 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "ReactNativeChatTests" */;
 			buildPhases = (
-				C5B995E95E82AA45C89CA88A /* [CP] Check Pods Manifest.lock */,
+				DBAA892DE9208CEF9F09C62B /* [CP] Check Pods Manifest.lock */,
 				00E356EA1AD99517003FC87E /* Sources */,
 				00E356EB1AD99517003FC87E /* Frameworks */,
 				00E356EC1AD99517003FC87E /* Resources */,
-				64450B67A8B6ADA8A8D3C47D /* [CP] Copy Pods Resources */,
+				2CE4CED3438C7F03D4D6D7D0 /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -219,13 +221,13 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ReactNativeChat" */;
 			buildPhases = (
-				A3E5D3E0B3D2144C5839A88A /* [CP] Check Pods Manifest.lock */,
+				6CC15D6E15FF4B1CCD49A418 /* [CP] Check Pods Manifest.lock */,
 				FD10A7F022414F080027D42C /* Start Packager */,
 				13B07F871A680F5B00A75B9A /* Sources */,
 				13B07F8C1A680F5B00A75B9A /* Frameworks */,
 				13B07F8E1A680F5B00A75B9A /* Resources */,
 				00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
-				9BDBE41A2934927B5C02A879 /* [CP] Copy Pods Resources */,
+				E58EE7CFA8E729776B644F2A /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -319,7 +321,7 @@
 			shellPath = /bin/sh;
 			shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh";
 		};
-		64450B67A8B6ADA8A8D3C47D /* [CP] Copy Pods Resources */ = {
+		2CE4CED3438C7F03D4D6D7D0 /* [CP] Copy Pods Resources */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -447,7 +449,51 @@
 			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
-		9BDBE41A2934927B5C02A879 /* [CP] Copy Pods Resources */ = {
+		6CC15D6E15FF4B1CCD49A418 /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
+		DBAA892DE9208CEF9F09C62B /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-ReactNativeChatTests-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
+		E58EE7CFA8E729776B644F2A /* [CP] Copy Pods Resources */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -575,50 +621,6 @@
 			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
-		A3E5D3E0B3D2144C5839A88A /* [CP] Check Pods Manifest.lock */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputFileListPaths = (
-			);
-			inputPaths = (
-				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
-				"${PODS_ROOT}/Manifest.lock",
-			);
-			name = "[CP] Check Pods Manifest.lock";
-			outputFileListPaths = (
-			);
-			outputPaths = (
-				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-checkManifestLockResult.txt",
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
-			showEnvVarsInLog = 0;
-		};
-		C5B995E95E82AA45C89CA88A /* [CP] Check Pods Manifest.lock */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputFileListPaths = (
-			);
-			inputPaths = (
-				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
-				"${PODS_ROOT}/Manifest.lock",
-			);
-			name = "[CP] Check Pods Manifest.lock";
-			outputFileListPaths = (
-			);
-			outputPaths = (
-				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-ReactNativeChatTests-checkManifestLockResult.txt",
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
-			showEnvVarsInLog = 0;
-		};
 		FD10A7F022414F080027D42C /* Start Packager */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
@@ -671,7 +673,7 @@
 /* Begin XCBuildConfiguration section */
 		00E356F61AD99517003FC87E /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = D8D3A45F65070233676930CF /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */;
+			baseConfigurationReference = 4836FA3D8151BC83FFA2F12F /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
@@ -695,7 +697,7 @@
 		};
 		00E356F71AD99517003FC87E /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 547FB1575833AEDE29F25896 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */;
+			baseConfigurationReference = 72FEF6AF05EAD544ED1EB5F4 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
@@ -716,7 +718,7 @@
 		};
 		13B07F941A680F5B00A75B9A /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 84BEA7302209B03FEC158B9A /* Pods-ReactNativeChat.debug.xcconfig */;
+			baseConfigurationReference = 3CF68E161AFD219803827BFA /* Pods-ReactNativeChat.debug.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
@@ -746,7 +748,7 @@
 		};
 		13B07F951A680F5B00A75B9A /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = BF057013BA158EE4172DAA52 /* Pods-ReactNativeChat.release.xcconfig */;
+			baseConfigurationReference = 20AC44D93509707BC89B1013 /* Pods-ReactNativeChat.release.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index a4e71ea3525a..37bd6eeb01a5 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -15,6 +15,8 @@ const notificationEventActionMap = {};
 function pushNotificationEventCallback(eventType, notification) {
     const actionMap = notificationEventActionMap[eventType] || {};
     let payload = lodashGet(notification, 'extras.payload');
+
+    // On Android, some notification payloads are sent as a JSON string rather than an object
     if (_.isString(payload)) {
         payload = JSON.parse(payload);
     }
diff --git a/src/lib/actions/Sidebar.js b/src/lib/actions/Sidebar.js
index 625b1a382421..5c8789dd058d 100644
--- a/src/lib/actions/Sidebar.js
+++ b/src/lib/actions/Sidebar.js
@@ -1,28 +1,18 @@
 import Ion from '../Ion';
 import IONKEYS from '../../IONKEYS';
 
-let isSidebarShown;
-Ion.connect({
-    key: IONKEYS.IS_SIDEBAR_SHOWN,
-    callback: val => isSidebarShown = val,
-});
-
 /**
  * Hide the sidebar, if it is shown.
  */
 function hide() {
-    if (isSidebarShown) {
-        Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, false);
-    }
+    Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, false);
 }
 
 /**
  * Show the sidebar, if it is hidden.
  */
 function show() {
-    if (!isSidebarShown) {
-        Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, true);
-    }
+    Ion.set(IONKEYS.IS_SIDEBAR_SHOWN, true);
 }
 
 export {
diff --git a/src/page/home/HomePage.js b/src/page/home/HomePage.js
index 68b26644fb9c..ed2c84a5f5b6 100644
--- a/src/page/home/HomePage.js
+++ b/src/page/home/HomePage.js
@@ -7,6 +7,7 @@ import {
     Animated,
     Easing
 } from 'react-native';
+import _ from 'underscore';
 import {SafeAreaInsetsContext, SafeAreaProvider} from 'react-native-safe-area-context';
 import {Route} from '../../lib/Router';
 import styles, {getSafeAreaPadding} from '../../style/StyleSheet';
@@ -45,7 +46,12 @@ class App extends React.Component {
         this.dismissHamburger = this.dismissHamburger.bind(this);
         this.showHamburger = this.showHamburger.bind(this);
         this.toggleHamburgerBasedOnDimensions = this.toggleHamburgerBasedOnDimensions.bind(this);
-        this.animationTranslateX = new Animated.Value(!props.isSidebarShown ? -300 : 0);
+
+        // Note: This null check is only necessary because withIon passes null for bound props
+        //       that are null-initialized initialized in Ion, and defaultProps only replaces for `undefined` values
+        this.animationTranslateX = new Animated.Value(
+            !_.isNull(props.isSidebarShown) && !props.isSidebarShown ? -300 : 0
+        );
     }
 
     componentDidMount() {

From 6e0665cb314114ef745984682ebe9e48969c65ee Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 14 Oct 2020 16:21:48 -0700
Subject: [PATCH 67/70] improve debug logs and move deregister

---
 src/lib/Notification/PushNotification/index.native.js | 2 +-
 src/lib/actions/Session.js                            | 2 --
 src/lib/actions/SignInRedirect.js                     | 2 ++
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index 37bd6eeb01a5..9c1a6768b06f 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -40,7 +40,7 @@ function pushNotificationEventCallback(eventType, notification) {
     }
 
     if (!payload.type) {
-        console.debug('[PUSH_NOTIFICATION] Notification of unknown type received.');
+        console.debug('[PUSH_NOTIFICATION] No type value provided in payload, not executing any callback.');
         return;
     }
 
diff --git a/src/lib/actions/Session.js b/src/lib/actions/Session.js
index e22e00da09bb..542b9fb83fbd 100644
--- a/src/lib/actions/Session.js
+++ b/src/lib/actions/Session.js
@@ -2,7 +2,6 @@ import Ion from '../Ion';
 import * as API from '../API';
 import IONKEYS from '../../IONKEYS';
 import redirectToSignIn from './SignInRedirect';
-import PushNotification from '../Notification/PushNotification';
 
 let credentials;
 Ion.connect({
@@ -31,7 +30,6 @@ function signIn(partnerUserID, partnerUserSecret, twoFactorAuthCode = '', exitTo
  * Clears the Ion store and redirects user to the sign in page
  */
 function signOut() {
-    PushNotification.deregister();
     redirectToSignIn();
     if (!credentials || !credentials.login) {
         return;
diff --git a/src/lib/actions/SignInRedirect.js b/src/lib/actions/SignInRedirect.js
index 973a6461014f..53f9f4e7ddb3 100644
--- a/src/lib/actions/SignInRedirect.js
+++ b/src/lib/actions/SignInRedirect.js
@@ -5,6 +5,7 @@ import {redirect} from './App';
 import * as Pusher from '../Pusher/pusher';
 import NetworkConnection from '../NetworkConnection';
 import UnreadIndicatorUpdater from '../UnreadIndicatorUpdater';
+import PushNotification from '../Notification/PushNotification/index.native';
 
 let currentURL;
 Ion.connect({
@@ -21,6 +22,7 @@ Ion.connect({
 function redirectToSignIn(errorMessage) {
     NetworkConnection.stopListeningForReconnect();
     UnreadIndicatorUpdater.stopListeningForReportChanges();
+    PushNotification.deregister();
     Pusher.disconnect();
     Ion.clear()
         .then(() => {

From 344343f4006c3dc44fc31904864401fc28c85ce8 Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 14 Oct 2020 16:57:41 -0700
Subject: [PATCH 68/70] clean pods (but leave manual signing

---
 ios/ReactNativeChat.xcodeproj/project.pbxproj | 113 +++++++++---------
 1 file changed, 58 insertions(+), 55 deletions(-)

diff --git a/ios/ReactNativeChat.xcodeproj/project.pbxproj b/ios/ReactNativeChat.xcodeproj/project.pbxproj
index 9765abe288bf..669b9faafb32 100644
--- a/ios/ReactNativeChat.xcodeproj/project.pbxproj
+++ b/ios/ReactNativeChat.xcodeproj/project.pbxproj
@@ -22,8 +22,10 @@
 		7EF88515CE8703E46E771C2D /* BuildFile in Frameworks */ = {isa = PBXBuildFile; };
 		81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
 		8821A238A081483FA947BC4E /* GTAmericaExp-RgIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 918D7FEFF96242E6B5F5E14D /* GTAmericaExp-RgIt.otf */; };
-		AB606A6D424AE2B476BEFD07 /* libPods-ReactNativeChat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A5E21B3B4603003659AA9250 /* libPods-ReactNativeChat.a */; };
-		E1866E081AB386749BB5FE39 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BF94FAF26294857A369B1127 /* libPods-ReactNativeChat-ReactNativeChatTests.a */; };
+		A028A315DD6EAA352B7250BE /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7CCC5FC5C47E5E52E71FA8D8 /* libPods-ReactNativeChat-ReactNativeChatTests.a */; };
+		AB606A6D424AE2B476BEFD07 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; };
+		C0CC7F55133DEA21C251C717 /* libPods-ReactNativeChat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B6AD207AA9F2D4448677FE14 /* libPods-ReactNativeChat.a */; };
+		E1866E081AB386749BB5FE39 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; };
 		E9DF872D2525201700607FDC /* AirshipConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9DF872C2525201700607FDC /* AirshipConfig.plist */; };
 /* End PBXBuildFile section */
 
@@ -48,11 +50,11 @@
 		13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = ReactNativeChat/Images.xcassets; sourceTree = "<group>"; };
 		13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ReactNativeChat/Info.plist; sourceTree = "<group>"; };
 		13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ReactNativeChat/main.m; sourceTree = "<group>"; };
-		20AC44D93509707BC89B1013 /* Pods-ReactNativeChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.release.xcconfig"; sourceTree = "<group>"; };
-		3CF68E161AFD219803827BFA /* Pods-ReactNativeChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.debug.xcconfig"; sourceTree = "<group>"; };
-		4836FA3D8151BC83FFA2F12F /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; sourceTree = "<group>"; };
+		1B8231DB3CDDEA2CA2EABD88 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; sourceTree = "<group>"; };
+		2E0F1E80E8C3C753376FF91D /* Pods-ReactNativeChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.debug.xcconfig"; sourceTree = "<group>"; };
+		41639FC76F0F95E48756CD94 /* Pods-ReactNativeChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat.release.xcconfig"; sourceTree = "<group>"; };
 		67D5C3A6A7FA417C8A853FC1 /* GTAmericaExp-Light.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Light.otf"; path = "../assets/fonts/GTAmericaExp-Light.otf"; sourceTree = "<group>"; };
-		72FEF6AF05EAD544ED1EB5F4 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; sourceTree = "<group>"; };
+		7CCC5FC5C47E5E52E71FA8D8 /* libPods-ReactNativeChat-ReactNativeChatTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat-ReactNativeChatTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = ReactNativeChat/LaunchScreen.storyboard; sourceTree = "<group>"; };
 		8437A5A38F2047E0BCCD7C2F /* GTAmericaExpMono-Rg.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExpMono-Rg.otf"; path = "../assets/fonts/GTAmericaExpMono-Rg.otf"; sourceTree = "<group>"; };
 		8C7003903C1E4957824899BB /* GTAmericaExp-Regular.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Regular.otf"; path = "../assets/fonts/GTAmericaExp-Regular.otf"; sourceTree = "<group>"; };
@@ -61,12 +63,11 @@
 		A5AAD008CBD84A6CAEB9AC97 /* GTAmericaExp-Bold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Bold.otf"; path = "../assets/fonts/GTAmericaExp-Bold.otf"; sourceTree = "<group>"; };
 		A8D6F2F722FD4E66A38EBBB6 /* GTAmericaExp-BdIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-BdIt.otf"; path = "../assets/fonts/GTAmericaExp-BdIt.otf"; sourceTree = "<group>"; };
 		AE65058949E14DA5A2D5435D /* GTAmericaExp-Medium.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Medium.otf"; path = "../assets/fonts/GTAmericaExp-Medium.otf"; sourceTree = "<group>"; };
-		B2E64A28C6D8EC058C5C3144 /* libPods-ReactNativeChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat.a"; sourceTree = BUILT_PRODUCTS_DIR; };
-		B81A8F09B6065AF7D0A5D2CD /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig"; sourceTree = "<group>"; };
-		BB3B578785B6B7D4E739A8C8 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; sourceTree = "<group>"; };
+		B66F0F1B5EACCBA697F33676 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig"; sourceTree = "<group>"; };
+		B6AD207AA9F2D4448677FE14 /* libPods-ReactNativeChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeChat.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		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 = "<group>"; };
 		E9DF86F625251E4A00607FDC /* Chat.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Chat.entitlements; path = ReactNativeChat/Chat.entitlements; sourceTree = "<group>"; };
-        E9DF872C2525201700607FDC /* AirshipConfig.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = AirshipConfig.plist; sourceTree = "<group>"; };
+		E9DF872C2525201700607FDC /* AirshipConfig.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = AirshipConfig.plist; sourceTree = "<group>"; };
 		ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
 		ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; };
 /* End PBXFileReference section */
@@ -76,7 +77,8 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				E1866E081AB386749BB5FE39 /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */,
+				E1866E081AB386749BB5FE39 /* BuildFile in Frameworks */,
+				A028A315DD6EAA352B7250BE /* libPods-ReactNativeChat-ReactNativeChatTests.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -85,7 +87,8 @@
 			buildActionMask = 2147483647;
 			files = (
 				7EF88515CE8703E46E771C2D /* BuildFile in Frameworks */,
-				AB606A6D424AE2B476BEFD07 /* libPods-ReactNativeChat.a in Frameworks */,
+				AB606A6D424AE2B476BEFD07 /* BuildFile in Frameworks */,
+				C0CC7F55133DEA21C251C717 /* libPods-ReactNativeChat.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -130,8 +133,8 @@
 			children = (
 				ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
 				ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
-				A5E21B3B4603003659AA9250 /* libPods-ReactNativeChat.a */,
-				BF94FAF26294857A369B1127 /* libPods-ReactNativeChat-ReactNativeChatTests.a */,
+				B6AD207AA9F2D4448677FE14 /* libPods-ReactNativeChat.a */,
+				7CCC5FC5C47E5E52E71FA8D8 /* libPods-ReactNativeChat-ReactNativeChatTests.a */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
@@ -187,10 +190,10 @@
 		EC29677F0A49C2946A495A33 /* Pods */ = {
 			isa = PBXGroup;
 			children = (
-				3CF68E161AFD219803827BFA /* Pods-ReactNativeChat.debug.xcconfig */,
-				20AC44D93509707BC89B1013 /* Pods-ReactNativeChat.release.xcconfig */,
-				4836FA3D8151BC83FFA2F12F /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */,
-				72FEF6AF05EAD544ED1EB5F4 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */,
+				2E0F1E80E8C3C753376FF91D /* Pods-ReactNativeChat.debug.xcconfig */,
+				41639FC76F0F95E48756CD94 /* Pods-ReactNativeChat.release.xcconfig */,
+				B66F0F1B5EACCBA697F33676 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */,
+				1B8231DB3CDDEA2CA2EABD88 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */,
 			);
 			path = Pods;
 			sourceTree = "<group>";
@@ -202,11 +205,11 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "ReactNativeChatTests" */;
 			buildPhases = (
-				DBAA892DE9208CEF9F09C62B /* [CP] Check Pods Manifest.lock */,
+				1EF4D2BC509C3A93DEF601C9 /* [CP] Check Pods Manifest.lock */,
 				00E356EA1AD99517003FC87E /* Sources */,
 				00E356EB1AD99517003FC87E /* Frameworks */,
 				00E356EC1AD99517003FC87E /* Resources */,
-				2CE4CED3438C7F03D4D6D7D0 /* [CP] Copy Pods Resources */,
+				D77A0DBFFAB15266AB126F03 /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -222,13 +225,13 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ReactNativeChat" */;
 			buildPhases = (
-				6CC15D6E15FF4B1CCD49A418 /* [CP] Check Pods Manifest.lock */,
+				A41F363287CE986510E292BD /* [CP] Check Pods Manifest.lock */,
 				FD10A7F022414F080027D42C /* Start Packager */,
 				13B07F871A680F5B00A75B9A /* Sources */,
 				13B07F8C1A680F5B00A75B9A /* Frameworks */,
 				13B07F8E1A680F5B00A75B9A /* Resources */,
 				00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
-				E58EE7CFA8E729776B644F2A /* [CP] Copy Pods Resources */,
+				39007FD9C5095C865D8E51BB /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -322,13 +325,35 @@
 			shellPath = /bin/sh;
 			shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh";
 		};
-		2CE4CED3438C7F03D4D6D7D0 /* [CP] Copy Pods Resources */ = {
+		1EF4D2BC509C3A93DEF601C9 /* [CP] Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 			);
+			inputFileListPaths = (
+			);
 			inputPaths = (
-				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh",
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-ReactNativeChatTests-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
+		39007FD9C5095C865D8E51BB /* [CP] Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAAutomationActions.plist",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerContentView.xib",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerView.xib",
@@ -447,10 +472,10 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh\"\n";
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
-		6CC15D6E15FF4B1CCD49A418 /* [CP] Check Pods Manifest.lock */ = {
+		A41F363287CE986510E292BD /* [CP] Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -472,35 +497,13 @@
 			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
 			showEnvVarsInLog = 0;
 		};
-		DBAA892DE9208CEF9F09C62B /* [CP] Check Pods Manifest.lock */ = {
+		D77A0DBFFAB15266AB126F03 /* [CP] Copy Pods Resources */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 			);
-			inputFileListPaths = (
-			);
 			inputPaths = (
-				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
-				"${PODS_ROOT}/Manifest.lock",
-			);
-			name = "[CP] Check Pods Manifest.lock";
-			outputFileListPaths = (
-			);
-			outputPaths = (
-				"$(DERIVED_FILE_DIR)/Pods-ReactNativeChat-ReactNativeChatTests-checkManifestLockResult.txt",
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
-			showEnvVarsInLog = 0;
-		};
-		E58EE7CFA8E729776B644F2A /* [CP] Copy Pods Resources */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputPaths = (
-				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh",
+				"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAAutomationActions.plist",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerContentView.xib",
 				"${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerView.xib",
@@ -619,7 +622,7 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat/Pods-ReactNativeChat-resources.sh\"\n";
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeChat-ReactNativeChatTests/Pods-ReactNativeChat-ReactNativeChatTests-resources.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
 		FD10A7F022414F080027D42C /* Start Packager */ = {
@@ -674,7 +677,7 @@
 /* Begin XCBuildConfiguration section */
 		00E356F61AD99517003FC87E /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 4836FA3D8151BC83FFA2F12F /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */;
+			baseConfigurationReference = B66F0F1B5EACCBA697F33676 /* Pods-ReactNativeChat-ReactNativeChatTests.debug.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
@@ -698,7 +701,7 @@
 		};
 		00E356F71AD99517003FC87E /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 72FEF6AF05EAD544ED1EB5F4 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */;
+			baseConfigurationReference = 1B8231DB3CDDEA2CA2EABD88 /* Pods-ReactNativeChat-ReactNativeChatTests.release.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
@@ -719,7 +722,7 @@
 		};
 		13B07F941A680F5B00A75B9A /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 3CF68E161AFD219803827BFA /* Pods-ReactNativeChat.debug.xcconfig */;
+			baseConfigurationReference = 2E0F1E80E8C3C753376FF91D /* Pods-ReactNativeChat.debug.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
@@ -749,7 +752,7 @@
 		};
 		13B07F951A680F5B00A75B9A /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 20AC44D93509707BC89B1013 /* Pods-ReactNativeChat.release.xcconfig */;
+			baseConfigurationReference = 41639FC76F0F95E48756CD94 /* Pods-ReactNativeChat.release.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;

From b49e7d434e7e1b5a945fbd712ec253483aeeb69c Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Wed, 14 Oct 2020 17:17:22 -0700
Subject: [PATCH 69/70] remove native import bug

---
 src/lib/actions/SignInRedirect.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/lib/actions/SignInRedirect.js b/src/lib/actions/SignInRedirect.js
index 53f9f4e7ddb3..a7c8f14e6302 100644
--- a/src/lib/actions/SignInRedirect.js
+++ b/src/lib/actions/SignInRedirect.js
@@ -5,7 +5,7 @@ import {redirect} from './App';
 import * as Pusher from '../Pusher/pusher';
 import NetworkConnection from '../NetworkConnection';
 import UnreadIndicatorUpdater from '../UnreadIndicatorUpdater';
-import PushNotification from '../Notification/PushNotification/index.native';
+import PushNotification from '../Notification/PushNotification';
 
 let currentURL;
 Ion.connect({

From 27553a9c286c8d8d7bddecff2a816df948b90ccb Mon Sep 17 00:00:00 2001
From: Rory Abraham <rory@expensify.com>
Date: Tue, 20 Oct 2020 14:07:38 -0700
Subject: [PATCH 70/70] allow notification response callbacks when app is in
 foreground

---
 src/lib/Notification/PushNotification/index.native.js | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/src/lib/Notification/PushNotification/index.native.js b/src/lib/Notification/PushNotification/index.native.js
index 9c1a6768b06f..2f48f8f92191 100644
--- a/src/lib/Notification/PushNotification/index.native.js
+++ b/src/lib/Notification/PushNotification/index.native.js
@@ -33,9 +33,13 @@ function pushNotificationEventCallback(eventType, notification) {
     }
 
     // If a push notification is received while the app is in foreground,
-    // we'll assume pusher is connected so we'll ignore this push notification
-    if (AppState.currentState === 'active') {
-        console.debug('[PUSH_NOTIFICATION] App is in foreground, not executing any callback.');
+    // we'll assume pusher is connected so we'll ignore is and not fetch the same data twice.
+    // However, we will allow NotificationResponse events through, so that tapping on a foreground notification
+    // will take you to the relevant report.
+    // Note: We hope to prevent foreground notifications from appearing in the near future,
+    // so when that happens we can go back to ignoring all push notification callbacks when the app is in the foreground
+    if (AppState.currentState === 'active' && eventType === EventType.PushReceived) {
+        console.debug('[PUSH_NOTIFICATION] Push received while app is in foreground, not executing any callback.');
         return;
     }
 
