Repository: Expensify/App (language: typescript)

Issue:
[Improvement] Add expensify.cash to iOS and Android native share menus
If you haven’t already, check out our [contributing guidelines](https://github.com/Expensify/ReactNativeChat/blob/master/CONTRIBUTING.md) for onboarding!
___

**Platform - version:**  
iOS, Android - All versions

## Problem

A common action when using a mobile device is to share something (a web page, an image, a tweet, etc) with another person. Both iOS and Android have a native "share" button that allows you to share things via apps that you have installed on your phone.

Here's an example of what the native iOS share menu looks like: 
![image](https://user-images.githubusercontent.com/1371996/103492514-0fa71f80-4df1-11eb-9eb0-b803669edc9d.png)

## Why is this important?
The native share function is a common flow and one of the easier ways to share something with someone. For example, I find it to be easier than copying the url of a web page from my mobile browser, switching apps, and pasting it in to the messenger app. It's much easier to just hit the share button, choose the messenger app, and select who I want to send the url to. 

## Solution
Add support for sharing directly to our app. Here is an [example react native library](https://github.com/meedan/react-native-share-menu) that helps with this, though I haven't done any research into it. I think the way we would implement it is: 
1. Expensify.cash shows up as an option in the native share menu
2. When Expensify.cash is selected, we bring up your Expensify.cash home screen (Screenshot is below)
3. You can select a chat 
4. Whatever you are sharing (url, image, etc) is sent to that chat

This is very similar to the flow when using the native share functionality with WhatsApp. 

Screenshot of Expensify.cash home screen:

![image](https://user-images.githubusercontent.com/5997400/105258369-7bea8880-5b3e-11eb-9949-7f6a9c315784.png)

Upwork Job Link: https://www.upwork.com/jobs/~01fb534e5bfc655563


Discussion hints:
Is this task still available? I would like to work on this issue.

Pull request: Add Expensify.cash to iOS and Android native share menus
### Details
Uses [react-native-share-menu](https://github.com/meedan/react-native-share-menu) to add Expensify.cash to the native share menu on mobile platforms and enables sharing to the app.

Chat selection is made on separate page, which reuses components from Home page. To achieve this I had to modify some sidebar related components and functions, mainly to add callbacks for getting selected chat id.

### Fixed Issues
Fixes #1294

### Tests
1. Open some other app on iOS or Android, for example image gallery or web browser.
2. Activate share menu for the element that needs to be shared.
3. Confirm that Expensify.cash is present in Share menu.
4. Select Expensify.cash.
5. Confirm that Expensify.cash is opened and routes to `Send to...` page.
6. Select or create and select some chat.
7. Confirm that shared item is posted to selected chat.

### Tested On

- [ ] Web
- [ ] Mobile Web
- [ ] Desktop
- [x] iOS
- [x] Android

### Screenshots
#### iOS
https://user-images.githubusercontent.com/130532/106333690-e499d980-6291-11eb-9d99-68ea9473a482.mp4

#### Android
https://user-images.githubusercontent.com/130532/106398712-3327af00-641d-11eb-9106-fe0def954ad9.mp4


Test command: NODE_OPTIONS='--experimental-vm-modules --max-old-space-size=4096' npx --no-install jest tests/unit/loginTest.js --json --outputFile=/tmp/test-results.json --passWithNoTests 2>&1

Fix patch:
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 68c269fe1bbe..12e96677ec23 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -19,11 +19,18 @@
         android:label="@string/app_name"
         android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
         android:launchMode="singleTask"
+        android:documentLaunchMode="never"
         android:windowSoftInputMode="adjustResize">
         <intent-filter>
             <action android:name="android.intent.action.MAIN" />
             <category android:name="android.intent.category.LAUNCHER" />
         </intent-filter>
+        <intent-filter>
+            <action android:name="android.intent.action.SEND" />
+            <category android:name="android.intent.category.DEFAULT" />
+            <data android:mimeType="*/*" />
+            <!-- Any other mime types you want to support -->
+        </intent-filter>
       </activity>
       <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
     </application>
diff --git a/ios/ExpensifyCash.xcodeproj/project.pbxproj b/ios/ExpensifyCash.xcodeproj/project.pbxproj
index 1cce13f3aa8e..dc707d8df42e 100644
--- a/ios/ExpensifyCash.xcodeproj/project.pbxproj
+++ b/ios/ExpensifyCash.xcodeproj/project.pbxproj
@@ -23,6 +23,10 @@
 		81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
 		8821A238A081483FA947BC4E /* GTAmericaExp-RgIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 918D7FEFF96242E6B5F5E14D /* GTAmericaExp-RgIt.otf */; };
 		8C86654500DCC843A74147B5 /* libPods-ExpensifyCash-ExpensifyCashTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ED2AB27DDDFCCE3CD100EA0C /* libPods-ExpensifyCash-ExpensifyCashTests.a */; };
+		9842B1200DD5443D16989CD2 /* libPods-ShareExtension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 981BE962FCA2842553D2AA78 /* libPods-ShareExtension.a */; };
+		B23ED19125C1A46900CA34C5 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B23ED18F25C1A46900CA34C5 /* MainInterface.storyboard */; };
+		B23ED19525C1A46900CA34C5 /* ShareExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = B23ED18B25C1A46900CA34C5 /* ShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
+		B2E844AF25C3A34300909139 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2E844AE25C3A34300909139 /* ShareViewController.swift */; };
 		BB6CECBDA023256B6B955321 /* libPods-ExpensifyCash.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F2C8BDCC1FF0B64AE2DFC9B /* libPods-ExpensifyCash.a */; };
 		E9DF872D2525201700607FDC /* AirshipConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9DF872C2525201700607FDC /* AirshipConfig.plist */; };
 /* End PBXBuildFile section */
@@ -35,8 +39,29 @@
 			remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
 			remoteInfo = ExpensifyCash;
 		};
+		B23ED19325C1A46900CA34C5 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = B23ED18A25C1A46900CA34C5;
+			remoteInfo = ShareExtension;
+		};
 /* End PBXContainerItemProxy section */
 
+/* Begin PBXCopyFilesBuildPhase section */
+		B23ED19625C1A46900CA34C5 /* Embed App Extensions */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 13;
+			files = (
+				B23ED19525C1A46900CA34C5 /* ShareExtension.appex in Embed App Extensions */,
+			);
+			name = "Embed App Extensions";
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
 /* Begin PBXFileReference section */
 		008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = "<group>"; };
 		00E356EE1AD99517003FC87E /* ExpensifyCashTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ExpensifyCashTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -54,15 +79,24 @@
 		81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = ExpensifyCash/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>"; };
+		915BA207DEFC455EEAC9EE2A /* Pods-ShareExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.release.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.release.xcconfig"; 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>"; };
+		981BE962FCA2842553D2AA78 /* libPods-ShareExtension.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ShareExtension.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		A13EE2CFAF952F935D201D2F /* Pods-ExpensifyCash.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpensifyCash.release.xcconfig"; path = "Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash.release.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>"; };
 		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>"; };
 		AF728B3FCBC8C2731C4DA7B4 /* Pods-ExpensifyCash-ExpensifyCashTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpensifyCash-ExpensifyCashTests.debug.xcconfig"; path = "Target Support Files/Pods-ExpensifyCash-ExpensifyCashTests/Pods-ExpensifyCash-ExpensifyCashTests.debug.xcconfig"; sourceTree = "<group>"; };
+		B23ED18B25C1A46900CA34C5 /* ShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
+		B23ED19025C1A46900CA34C5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
+		B23ED19225C1A46900CA34C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		B2E844AE25C3A34300909139 /* ShareViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ShareViewController.swift; path = "../../node_modules/react-native-share-menu/ios/ShareViewController.swift"; sourceTree = "<group>"; };
+		B2E844B325C3A5D200909139 /* ShareExtension-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ShareExtension-Bridging-Header.h"; sourceTree = "<group>"; };
+		B2E9EC3725C1E5CD00BD6AE5 /* ShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ShareExtension.entitlements; 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>"; };
 		E7967C67752432EA2031954F /* Pods-ExpensifyCash-ExpensifyCashTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpensifyCash-ExpensifyCashTests.release.xcconfig"; path = "Target Support Files/Pods-ExpensifyCash-ExpensifyCashTests/Pods-ExpensifyCash-ExpensifyCashTests.release.xcconfig"; sourceTree = "<group>"; };
+		E7FF6451784072419C0107A6 /* Pods-ShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.debug.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.debug.xcconfig"; 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; };
@@ -87,6 +121,14 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		B23ED18825C1A46900CA34C5 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				9842B1200DD5443D16989CD2 /* libPods-ShareExtension.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
@@ -130,6 +172,7 @@
 				ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
 				6F2C8BDCC1FF0B64AE2DFC9B /* libPods-ExpensifyCash.a */,
 				ED2AB27DDDFCCE3CD100EA0C /* libPods-ExpensifyCash-ExpensifyCashTests.a */,
+				981BE962FCA2842553D2AA78 /* libPods-ShareExtension.a */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
@@ -147,6 +190,7 @@
 				13B07FAE1A68108700A75B9A /* ExpensifyCash */,
 				832341AE1AAA6A7D00B99B32 /* Libraries */,
 				00E356EF1AD99517003FC87E /* ExpensifyCashTests */,
+				B23ED18C25C1A46900CA34C5 /* ShareExtension */,
 				83CBBA001A601CBA00E9B192 /* Products */,
 				2D16E6871FA4F8E400B85C8A /* Frameworks */,
 				EC29677F0A49C2946A495A33 /* Pods */,
@@ -162,6 +206,7 @@
 			children = (
 				13B07F961A680F5B00A75B9A /* Expensify.cash.app */,
 				00E356EE1AD99517003FC87E /* ExpensifyCashTests.xctest */,
+				B23ED18B25C1A46900CA34C5 /* ShareExtension.appex */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -182,6 +227,18 @@
 			name = Resources;
 			sourceTree = "<group>";
 		};
+		B23ED18C25C1A46900CA34C5 /* ShareExtension */ = {
+			isa = PBXGroup;
+			children = (
+				B2E844AE25C3A34300909139 /* ShareViewController.swift */,
+				B2E9EC3725C1E5CD00BD6AE5 /* ShareExtension.entitlements */,
+				B23ED18F25C1A46900CA34C5 /* MainInterface.storyboard */,
+				B23ED19225C1A46900CA34C5 /* Info.plist */,
+				B2E844B325C3A5D200909139 /* ShareExtension-Bridging-Header.h */,
+			);
+			path = ShareExtension;
+			sourceTree = "<group>";
+		};
 		EC29677F0A49C2946A495A33 /* Pods */ = {
 			isa = PBXGroup;
 			children = (
@@ -189,6 +246,8 @@
 				A13EE2CFAF952F935D201D2F /* Pods-ExpensifyCash.release.xcconfig */,
 				AF728B3FCBC8C2731C4DA7B4 /* Pods-ExpensifyCash-ExpensifyCashTests.debug.xcconfig */,
 				E7967C67752432EA2031954F /* Pods-ExpensifyCash-ExpensifyCashTests.release.xcconfig */,
+				E7FF6451784072419C0107A6 /* Pods-ShareExtension.debug.xcconfig */,
+				915BA207DEFC455EEAC9EE2A /* Pods-ShareExtension.release.xcconfig */,
 			);
 			path = Pods;
 			sourceTree = "<group>";
@@ -229,22 +288,44 @@
 				C9071365B664DE0D85DC5195 /* [CP] Copy Pods Resources */,
 				CF6259710B5A341870372EA2 /* [CP-User] [RNFB] Core Configuration */,
 				ED5B8E90A3384FC6A128FB95 /* [CP-User] [RNFB] Crashlytics Configuration */,
+				B23ED19625C1A46900CA34C5 /* Embed App Extensions */,
 			);
 			buildRules = (
 			);
 			dependencies = (
+				B23ED19425C1A46900CA34C5 /* PBXTargetDependency */,
 			);
 			name = ExpensifyCash;
 			productName = ExpensifyCash;
 			productReference = 13B07F961A680F5B00A75B9A /* Expensify.cash.app */;
 			productType = "com.apple.product-type.application";
 		};
+		B23ED18A25C1A46900CA34C5 /* ShareExtension */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = B23ED19925C1A46900CA34C5 /* Build configuration list for PBXNativeTarget "ShareExtension" */;
+			buildPhases = (
+				C55D1D9B7EB40F37CFB79F72 /* [CP] Check Pods Manifest.lock */,
+				B23ED18725C1A46900CA34C5 /* Sources */,
+				B23ED18825C1A46900CA34C5 /* Frameworks */,
+				B23ED18925C1A46900CA34C5 /* Resources */,
+				CAD20EC45A7B56F8F8F2BDE9 /* [CP] Copy Pods Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = ShareExtension;
+			productName = ShareExtension;
+			productReference = B23ED18B25C1A46900CA34C5 /* ShareExtension.appex */;
+			productType = "com.apple.product-type.app-extension";
+		};
 /* End PBXNativeTarget section */
 
 /* Begin PBXProject section */
 		83CBB9F71A601CBA00E9B192 /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
+				LastSwiftUpdateCheck = 1230;
 				LastUpgradeCheck = 1130;
 				TargetAttributes = {
 					00E356ED1AD99517003FC87E = {
@@ -257,6 +338,12 @@
 						LastSwiftMigration = 1120;
 						ProvisioningStyle = Manual;
 					};
+					B23ED18A25C1A46900CA34C5 = {
+						CreatedOnToolsVersion = 12.3;
+						DevelopmentTeam = 368M544MTT;
+						LastSwiftMigration = 1230;
+						ProvisioningStyle = Manual;
+					};
 				};
 			};
 			buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "ExpensifyCash" */;
@@ -274,6 +361,7 @@
 			targets = (
 				13B07F861A680F5B00A75B9A /* ExpensifyCash */,
 				00E356ED1AD99517003FC87E /* ExpensifyCashTests */,
+				B23ED18A25C1A46900CA34C5 /* ShareExtension */,
 			);
 		};
 /* End PBXProject section */
@@ -306,6 +394,14 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		B23ED18925C1A46900CA34C5 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				B23ED19125C1A46900CA34C5 /* MainInterface.storyboard in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXShellScriptBuildPhase section */
@@ -367,6 +463,28 @@
 			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;
 		};
+		C55D1D9B7EB40F37CFB79F72 /* [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-ShareExtension-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;
+		};
 		C9071365B664DE0D85DC5195 /* [CP] Copy Pods Resources */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
@@ -495,6 +613,24 @@
 			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash-resources.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
+		CAD20EC45A7B56F8F8F2BDE9 /* [CP] Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-ShareExtension/Pods-ShareExtension-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-ShareExtension/Pods-ShareExtension-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
 		CF6259710B5A341870372EA2 /* [CP-User] [RNFB] Core Configuration */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
@@ -682,6 +818,14 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		B23ED18725C1A46900CA34C5 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				B2E844AF25C3A34300909139 /* ShareViewController.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXSourcesBuildPhase section */
 
 /* Begin PBXTargetDependency section */
@@ -690,8 +834,24 @@
 			target = 13B07F861A680F5B00A75B9A /* ExpensifyCash */;
 			targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
 		};
+		B23ED19425C1A46900CA34C5 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = B23ED18A25C1A46900CA34C5 /* ShareExtension */;
+			targetProxy = B23ED19325C1A46900CA34C5 /* PBXContainerItemProxy */;
+		};
 /* End PBXTargetDependency section */
 
+/* Begin PBXVariantGroup section */
+		B23ED18F25C1A46900CA34C5 /* MainInterface.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				B23ED19025C1A46900CA34C5 /* Base */,
+			);
+			name = MainInterface.storyboard;
+			sourceTree = "<group>";
+		};
+/* End PBXVariantGroup section */
+
 /* Begin XCBuildConfiguration section */
 		00E356F61AD99517003FC87E /* Debug */ = {
 			isa = XCBuildConfiguration;
@@ -743,6 +903,7 @@
 			isa = XCBuildConfiguration;
 			baseConfigurationReference = F4BC0E78FF1E9BD8B2D38C66 /* Pods-ExpensifyCash.debug.xcconfig */;
 			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_ENTITLEMENTS = ExpensifyCash/Chat.entitlements;
@@ -775,6 +936,7 @@
 			isa = XCBuildConfiguration;
 			baseConfigurationReference = A13EE2CFAF952F935D201D2F /* Pods-ExpensifyCash.release.xcconfig */;
 			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_ENTITLEMENTS = ExpensifyCash/Chat.entitlements;
@@ -914,6 +1076,81 @@
 			};
 			name = Release;
 		};
+		B23ED19725C1A46900CA34C5 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = E7FF6451784072419C0107A6 /* Pods-ShareExtension.debug.xcconfig */;
+			buildSettings = {
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
+				CODE_SIGN_IDENTITY = "iPhone Distribution";
+				CODE_SIGN_STYLE = Manual;
+				CURRENT_PROJECT_VERSION = 386;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				DEVELOPMENT_TEAM = 368M544MTT;
+				"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				INFOPLIST_FILE = ShareExtension/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
+				MARKETING_VERSION = 1.0.1;
+				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+				MTL_FAST_MATH = YES;
+				PRODUCT_BUNDLE_IDENTIFIER = com.chat.expensify.chat.ShareExtension;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE_SPECIFIER = chat_expensify_appstore;
+				SKIP_INSTALL = YES;
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+				SWIFT_OBJC_BRIDGING_HEADER = "ShareExtension/ShareExtension-Bridging-Header.h";
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Debug;
+		};
+		B23ED19825C1A46900CA34C5 /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 915BA207DEFC455EEAC9EE2A /* Pods-ShareExtension.release.xcconfig */;
+			buildSettings = {
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
+				CODE_SIGN_IDENTITY = "iPhone Distribution";
+				CODE_SIGN_STYLE = Manual;
+				COPY_PHASE_STRIP = NO;
+				CURRENT_PROJECT_VERSION = 386;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				DEVELOPMENT_TEAM = 368M544MTT;
+				"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				INFOPLIST_FILE = ShareExtension/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
+				MARKETING_VERSION = 1.0.1;
+				MTL_FAST_MATH = YES;
+				PRODUCT_BUNDLE_IDENTIFIER = com.chat.expensify.chat.ShareExtension;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE_SPECIFIER = chat_expensify_appstore;
+				SKIP_INSTALL = YES;
+				SWIFT_OBJC_BRIDGING_HEADER = "ShareExtension/ShareExtension-Bridging-Header.h";
+				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Release;
+		};
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
@@ -944,6 +1181,15 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
+		B23ED19925C1A46900CA34C5 /* Build configuration list for PBXNativeTarget "ShareExtension" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				B23ED19725C1A46900CA34C5 /* Debug */,
+				B23ED19825C1A46900CA34C5 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 /* End XCConfigurationList section */
 	};
 	rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
diff --git a/ios/ExpensifyCash/AppDelegate.m b/ios/ExpensifyCash/AppDelegate.m
index 63afe3635692..23a8f024b08e 100644
--- a/ios/ExpensifyCash/AppDelegate.m
+++ b/ios/ExpensifyCash/AppDelegate.m
@@ -6,6 +6,7 @@
 #import <Firebase.h>
 
 #import <UserNotifications/UserNotifications.h>
+#import <RNShareMenu/ShareMenuManager.h>
 
 #ifdef FB_SONARKIT_ENABLED
 #import <FlipperKit/FlipperClient.h>
@@ -66,4 +67,11 @@ - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
 #endif
 }
 
+- (BOOL)application:(UIApplication *)app
+            openURL:(NSURL *)url
+            options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
+{
+  return [ShareMenuManager application:app openURL:url options:options];
+}
+
 @end
diff --git a/ios/ExpensifyCash/Chat.entitlements b/ios/ExpensifyCash/Chat.entitlements
index 903def2af530..68c025067d14 100644
--- a/ios/ExpensifyCash/Chat.entitlements
+++ b/ios/ExpensifyCash/Chat.entitlements
@@ -4,5 +4,9 @@
 <dict>
 	<key>aps-environment</key>
 	<string>development</string>
+	<key>com.apple.security.application-groups</key>
+	<array>
+	    <string>group.com.chat.expensify.chat</string>
+	</array>
 </dict>
 </plist>
diff --git a/ios/ExpensifyCash/Info.plist b/ios/ExpensifyCash/Info.plist
index d75c8f14396c..dcf8bf10a2bc 100644
--- a/ios/ExpensifyCash/Info.plist
+++ b/ios/ExpensifyCash/Info.plist
@@ -48,6 +48,17 @@
 			</dict>
 		</dict>
 	</dict>
+    <key>CFBundleURLTypes</key>
+    <array>
+        <dict>
+            <key>CFBundleTypeRole</key>
+            <string>Editor</string>
+            <key>CFBundleURLSchemes</key>
+            <array>
+                <string>expensify</string>
+            </array>
+        </dict>
+    </array>
 	<key>NSCameraUsageDescription</key>
 	<string>Your camera is used to create chat attachments.</string>
 	<key>NSLocationWhenInUseUsageDescription</key>
diff --git a/ios/Podfile b/ios/Podfile
index b8f5255f0274..1122ac1c6fb2 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -21,8 +21,18 @@ target 'ExpensifyCash' do
   post_install do |installer|
     flipper_post_install(installer)
 
-    installer.pods_project.build_configurations.each do |config|
-      config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64"
+    installer.pods_project.targets.each do |target|
+      target.build_configurations.each do |config|
+        config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64"
+        config.build_settings['APPLICATION_EXTENSION_API_ONLY'] = 'NO'
+      end
     end
   end
 end
+
+target 'ShareExtension' do
+  use_react_native!
+
+  pod 'RNShareMenu', :path => '../node_modules/react-native-share-menu'
+  # Manually link packages here to keep your extension bundle size minimal
+end
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 47595c78ddd9..43bab77c6399 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -415,6 +415,8 @@ PODS:
     - Firebase/Crashlytics (~> 6.34.0)
     - React-Core
     - RNFBApp
+  - RNShareMenu (5.0.3):
+    - React
   - RNSVG (12.1.0):
     - React
   - urbanairship-react-native (10.0.0):
@@ -485,6 +487,7 @@ DEPENDENCIES:
   - "RNFBAnalytics (from `../node_modules/@react-native-firebase/analytics`)"
   - "RNFBApp (from `../node_modules/@react-native-firebase/app`)"
   - "RNFBCrashlytics (from `../node_modules/@react-native-firebase/crashlytics`)"
+  - RNShareMenu (from `../node_modules/react-native-share-menu`)
   - RNSVG (from `../node_modules/react-native-svg`)
   - urbanairship-react-native (from `../node_modules/urbanairship-react-native`)
   - Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
@@ -595,6 +598,8 @@ EXTERNAL SOURCES:
     :path: "../node_modules/@react-native-firebase/app"
   RNFBCrashlytics:
     :path: "../node_modules/@react-native-firebase/crashlytics"
+  RNShareMenu:
+    :path: "../node_modules/react-native-share-menu"
   RNSVG:
     :path: "../node_modules/react-native-svg"
   urbanairship-react-native:
@@ -665,11 +670,12 @@ SPEC CHECKSUMS:
   RNFBAnalytics: 2dc4dd9e2445faffca041b10447a23a71dcdabf8
   RNFBApp: 7eacc7da7ab19f96c05e434017d44a9f09410da8
   RNFBCrashlytics: 4870c14cf8833053b6b5648911abefe1923854d2
+  RNShareMenu: 555df30344da0d60947ccc781b0447ee9e5cd48f
   RNSVG: ce9d996113475209013317e48b05c21ee988d42e
   urbanairship-react-native: dfb6dc22b2f41ccaadd636b73d51b448cd1b2bbc
   Yoga: 7d13633d129fd179e01b8953d38d47be90db185a
   YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
 
-PODFILE CHECKSUM: 41b806c7f131f87b716be1f1f9377532d6c9e43a
+PODFILE CHECKSUM: dc731cc579bb3ba93e3ed267d05524db4ca67bc6
 
-COCOAPODS: 1.10.0
+COCOAPODS: 1.10.1
diff --git a/ios/ShareExtension/Base.lproj/MainInterface.storyboard b/ios/ShareExtension/Base.lproj/MainInterface.storyboard
new file mode 100644
index 000000000000..286a50894d87
--- /dev/null
+++ b/ios/ShareExtension/Base.lproj/MainInterface.storyboard
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="j1y-V4-xli">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--Share View Controller-->
+        <scene sceneID="ceB-am-kn3">
+            <objects>
+                <viewController id="j1y-V4-xli" customClass="ShareViewController" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" opaque="NO" contentMode="scaleToFill" id="wbc-yd-nQP">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
+                        <viewLayoutGuide key="safeArea" id="1Xd-am-t49"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="CEy-Cv-SGf" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+        </scene>
+    </scenes>
+</document>
diff --git a/ios/ShareExtension/Info.plist b/ios/ShareExtension/Info.plist
new file mode 100644
index 000000000000..61844631945e
--- /dev/null
+++ b/ios/ShareExtension/Info.plist
@@ -0,0 +1,60 @@
+<?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>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleDisplayName</key>
+	<string>ShareExtension</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
+	<key>CFBundleShortVersionString</key>
+	<string>$(MARKETING_VERSION)</string>
+	<key>CFBundleVersion</key>
+	<string>$(CURRENT_PROJECT_VERSION)</string>
+	<key>NSExtension</key>
+	<dict>
+		<key>NSExtensionAttributes</key>
+		<dict>
+			<key>NSExtensionActivationRule</key>
+			<string>TRUEPREDICATE</string>
+		</dict>
+		<key>NSExtensionMainStoryboard</key>
+		<string>MainInterface</string>
+		<key>NSExtensionPointIdentifier</key>
+		<string>com.apple.share-services</string>
+	</dict>
+    <key>HostAppBundleIdentifier</key>
+    <string>com.chat.expensify.chat</string>
+    <key>HostAppURLScheme</key>
+    <string>expensify://</string>
+    <key>NSExtension</key>
+    <dict>
+        <key>NSExtensionAttributes</key>
+        <dict>
+            <key>NSExtensionActivationRule</key>
+            <dict>
+                <!-- For a full list of available options, visit https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/AppExtensionKeys.html#//apple_ref/doc/uid/TP40014212-SW10 -->
+                <key>NSExtensionActivationSupportsImageWithMaxCount</key>
+                <integer>1</integer>
+                <key>NSExtensionActivationSupportsText</key>
+                <true/>
+                <key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
+                <integer>1</integer>
+            </dict>
+        </dict>
+        <key>NSExtensionMainStoryboard</key>
+        <string>MainInterface</string>
+        <key>NSExtensionPointIdentifier</key>
+        <string>com.apple.share-services</string>
+    </dict>
+</dict>
+</plist>
diff --git a/ios/ShareExtension/ShareExtension-Bridging-Header.h b/ios/ShareExtension/ShareExtension-Bridging-Header.h
new file mode 100644
index 000000000000..bf52ff6e17cd
--- /dev/null
+++ b/ios/ShareExtension/ShareExtension-Bridging-Header.h
@@ -0,0 +1,4 @@
+#import <React/RCTBridge.h>
+#import <React/RCTBundleURLProvider.h>
+#import <React/RCTBridgeDelegate.h>
+#import <React/RCTRootView.h>
diff --git a/ios/ShareExtension/ShareExtension.entitlements b/ios/ShareExtension/ShareExtension.entitlements
new file mode 100644
index 000000000000..4151cc679255
--- /dev/null
+++ b/ios/ShareExtension/ShareExtension.entitlements
@@ -0,0 +1,10 @@
+<?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>com.apple.security.application-groups</key>
+	<array>
+	    <string>group.com.chat.expensify.chat</string>
+	</array>
+</dict>
+</plist>
diff --git a/package-lock.json b/package-lock.json
index ca9ebecb54f3..64aac94825c1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -21302,6 +21302,11 @@
       "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-3.1.8.tgz",
       "integrity": "sha512-9gUlsDZ96QwT9AKzA6aVWM/NX5rlJgauZ9HgCDVzKbe29UQYT1740QJnnaI2GExmkFGp6o7ZLNhCXZW95eYVFA=="
     },
+    "react-native-share-menu": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/react-native-share-menu/-/react-native-share-menu-5.0.3.tgz",
+      "integrity": "sha512-NtL/jLbcApe6WalXLFCNYo8yFfhLGIIbi9AJgSpRLaf1+V141d7vtoJnd1fYObSGxcZJcKLSW2bkeJrpfbmZnw=="
+    },
     "react-native-svg": {
       "version": "12.1.0",
       "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-12.1.0.tgz",
diff --git a/package.json b/package.json
index 5e0bf86c880b..8204d3336219 100644
--- a/package.json
+++ b/package.json
@@ -67,6 +67,7 @@
     "react-native-pdf": "^6.2.2",
     "react-native-render-html": "^6.0.0-alpha.10",
     "react-native-safe-area-context": "^3.1.4",
+    "react-native-share-menu": "^5.0.3",
     "react-native-svg": "^12.1.0",
     "react-native-web": "^0.14.1",
     "react-native-web-webview": "^1.0.2",
diff --git a/src/Expensify.js b/src/Expensify.js
index a97751414b12..895759f8d4d9 100644
--- a/src/Expensify.js
+++ b/src/Expensify.js
@@ -7,8 +7,10 @@ import HomePage from './pages/home/HomePage';
 import NotFoundPage from './pages/NotFound';
 import SetPasswordPage from './pages/SetPasswordPage';
 import SignInPage from './pages/signin/SignInPage';
+import SharePage from './pages/SharePage';
 import listenToStorageEvents from './libs/listenToStorageEvents';
 import * as ActiveClientManager from './libs/ActiveClientManager';
+import ShareManager from './libs/ShareManager';
 import ONYXKEYS from './ONYXKEYS';
 
 import styles from './styles/styles';
@@ -76,6 +78,9 @@ class Expensify extends Component {
             key: ONYXKEYS.SESSION,
             callback: this.removeLoadingState,
         });
+
+        // Subscribe to share events
+        ShareManager.register();
     }
 
     componentDidUpdate(prevProps, prevState) {
@@ -84,6 +89,10 @@ class Expensify extends Component {
         }
     }
 
+    componentWillUnmount() {
+        ShareManager.deregister();
+    }
+
     /**
      * When the authToken is updated, the app should remove the loading state and handle the authToken
      *
@@ -130,6 +139,7 @@ class Expensify extends Component {
                     <Route path={[ROUTES.SET_PASSWORD]} component={SetPasswordPage} />
                     <Route path={[ROUTES.NOT_FOUND]} component={NotFoundPage} />
                     <Route path={[ROUTES.SIGNIN_WITH_EXITTO, ROUTES.SIGNIN]} component={SignInPage} />
+                    <Route path={[ROUTES.SHARE]} component={SharePage} />
                     <Route
                         path={[ROUTES.HOME, ROUTES.ROOT]}
                         render={match => (
diff --git a/src/ONYXKEYS.js b/src/ONYXKEYS.js
index 33dc21e1617e..603a848cba43 100644
--- a/src/ONYXKEYS.js
+++ b/src/ONYXKEYS.js
@@ -41,6 +41,9 @@ export default {
     IS_SIDEBAR_ANIMATING: 'isSidebarAnimating',
     BETAS: 'betas',
 
+    // Data shared to the app from other apps
+    SHARED_ITEM: 'sharedItem',
+
     // Collection Keys
     COLLECTION: {
         REPORT: 'report_',
diff --git a/src/ROUTES.js b/src/ROUTES.js
index 2c367474d55a..a4032180441b 100644
--- a/src/ROUTES.js
+++ b/src/ROUTES.js
@@ -7,6 +7,7 @@ export default {
     NEW_GROUP: '/new/group',
     REPORT: '/r/:reportID',
     getReportRoute: reportID => `/r/${reportID}`,
+    SHARE: '/share',
     ROOT: '/',
     SET_PASSWORD: '/setpassword/:validateCode',
     SIGNIN: '/signin',
diff --git a/src/libs/ShareManager/ShareType.js b/src/libs/ShareManager/ShareType.js
new file mode 100644
index 000000000000..b05ccf28278a
--- /dev/null
+++ b/src/libs/ShareManager/ShareType.js
@@ -0,0 +1,7 @@
+/**
+ * Types of shared content.
+ */
+export default {
+    FILE: 'file',
+    TEXT: 'text',
+};
diff --git a/src/libs/ShareManager/index.js b/src/libs/ShareManager/index.js
new file mode 100644
index 000000000000..571e5b7dfea2
--- /dev/null
+++ b/src/libs/ShareManager/index.js
@@ -0,0 +1,9 @@
+// This is a no-op component for desktop and web,
+// because sharing to the app is only supported on mobile
+import ShareType from './ShareType';
+
+export default {
+    register: () => {},
+    deregister: () => {},
+    TYPE: ShareType,
+};
diff --git a/src/libs/ShareManager/index.native.js b/src/libs/ShareManager/index.native.js
new file mode 100644
index 000000000000..3a503bb41022
--- /dev/null
+++ b/src/libs/ShareManager/index.native.js
@@ -0,0 +1,70 @@
+import ShareMenu from 'react-native-share-menu';
+import {redirect} from '../actions/App';
+import {set as setSharedItem} from '../actions/SharedItem';
+import ROUTES from '../../ROUTES';
+import ShareType from './ShareType';
+
+const TEXT_MIME_TYPE = /^text\//i;
+
+// Share event listener
+let listener;
+
+/**
+ * Prepare shared item for posting
+ *
+ * @param {Object} sharedItem Shared item object
+ * @returns {Object} Prepared shared item structure
+ */
+function prepareSharedItem(sharedItem) {
+    if (sharedItem.mimeType.match(TEXT_MIME_TYPE)) {
+        // Shared item is text or HTML
+        return {
+            type: ShareType.TEXT,
+            data: sharedItem.data,
+        };
+    }
+
+    // Shared item is file attachment
+    return {
+        type: ShareType.FILE,
+        data: {
+            name: 'chat_attachment',
+            type: sharedItem.mimeType,
+            uri: sharedItem.data,
+        },
+    };
+}
+
+/**
+ * Saves shared item and redirects to Share Page.
+ *
+ * @param {Object} sharedItem
+ */
+function handleShare(sharedItem) {
+    if (sharedItem) {
+        const preparedSharedItem = prepareSharedItem(sharedItem);
+        setSharedItem(preparedSharedItem);
+        redirect(ROUTES.SHARE);
+    }
+}
+
+/**
+ * Subscribes to share events.
+ */
+function register() {
+    ShareMenu.getInitialShare(handleShare);
+    listener = ShareMenu.addNewShareListener(handleShare);
+}
+
+/**
+ * Removes share events listener.
+ */
+function deregister() {
+    listener.remove();
+}
+
+export default {
+    register,
+    deregister,
+    TYPE: ShareType,
+};
diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js
index 7fbd3b5b45c9..d3046cf520ab 100644
--- a/src/libs/actions/Report.js
+++ b/src/libs/actions/Report.js
@@ -504,6 +504,7 @@ function fetchAll(shouldRedirectToReport = true, shouldFetchActions = false, sho
  * set of participants
  *
  * @param {String[]} participants
+ * @returns {Promise} reportID
  */
 function fetchOrCreateChatReport(participants) {
     let reportID;
@@ -512,7 +513,7 @@ function fetchOrCreateChatReport(participants) {
         throw new Error('fetchOrCreateChatReport() must have at least two participants');
     }
 
-    API.CreateChatReport({
+    return API.CreateChatReport({
         emailList: participants.join(','),
     })
 
@@ -557,6 +558,8 @@ function fetchOrCreateChatReport(participants) {
 
             // Redirect the logged in person to the new report
             redirect(ROUTES.getReportRoute(reportID));
+
+            return reportID;
         });
 }
 
diff --git a/src/libs/actions/SharedItem.js b/src/libs/actions/SharedItem.js
new file mode 100644
index 000000000000..a6fabf8e5a06
--- /dev/null
+++ b/src/libs/actions/SharedItem.js
@@ -0,0 +1,23 @@
+import Onyx from 'react-native-onyx';
+import ONYXKEYS from '../../ONYXKEYS';
+
+/**
+ * Saves shared item to storage.
+ *
+ * @param {Object} sharedItem
+ */
+function set(sharedItem) {
+    Onyx.set(ONYXKEYS.SHARED_ITEM, sharedItem);
+}
+
+/**
+ * Clears shared item from storage.
+ */
+function clear() {
+    Onyx.set(ONYXKEYS.SHARED_ITEM, null);
+}
+
+export {
+    set,
+    clear,
+};
diff --git a/src/pages/SharePage.js b/src/pages/SharePage.js
new file mode 100644
index 000000000000..a30a4832cb5d
--- /dev/null
+++ b/src/pages/SharePage.js
@@ -0,0 +1,176 @@
+import React from 'react';
+import {View} from 'react-native';
+import {SafeAreaProvider} from 'react-native-safe-area-context/src/SafeAreaContext';
+import {SafeAreaInsetsContext} from 'react-native-safe-area-context';
+import {withOnyx} from 'react-native-onyx';
+import PropTypes from 'prop-types';
+import styles from '../styles/styles';
+import ONYXKEYS from '../ONYXKEYS';
+import ShareManager from '../libs/ShareManager';
+import {redirect} from '../libs/actions/App';
+import {clear as clearSharedItem} from '../libs/actions/SharedItem';
+import {hide as hideSidebar} from '../libs/actions/Sidebar';
+import {addAction} from '../libs/actions/Report';
+import ROUTES from '../ROUTES';
+import CustomStatusBar from '../components/CustomStatusBar';
+import SidebarLinks from './home/sidebar/SidebarLinks';
+import FAB from '../components/FAB';
+import CreateMenu from '../components/CreateMenu';
+import * as ChatSwitcher from '../libs/actions/ChatSwitcher';
+
+const propTypes = {
+    // Currently viewed reportID
+    currentlyViewedReportID: PropTypes.string,
+
+    // Shared item object
+    sharedItem: PropTypes.shape({
+        // Shared item type (one of the `ShareType.*`)
+        type: PropTypes.string.isRequired,
+
+        // Shared item data. Differs depending on type
+        data: PropTypes.oneOfType([
+            // Text or HTML for `ShareType.TEXT`
+            PropTypes.string,
+
+            // File data structure for `ShareType.FILE`
+            PropTypes.shape({
+                // File name
+                name: PropTypes.string.isRequired,
+
+                // File MIME type
+                type: PropTypes.string.isRequired,
+
+                // File URI
+                uri: PropTypes.string.isRequired,
+            }),
+        ]).isRequired,
+    }),
+};
+
+const defaultProps = {
+    currentlyViewedReportID: '',
+    sharedItem: null,
+};
+
+class SharePage extends React.Component {
+    constructor(props) {
+        super(props);
+
+        this.state = {
+            isCreateMenuActive: false,
+        };
+
+        this.onCloseButtonClick = this.onCloseButtonClick.bind(this);
+        this.onLinkClick = this.onLinkClick.bind(this);
+        this.onCreateMenuItemSelected = this.onCreateMenuItemSelected.bind(this);
+        this.toggleCreateMenu = this.toggleCreateMenu.bind(this);
+        this.addSharedItemToReport = this.addSharedItemToReport.bind(this);
+    }
+
+    /**
+     * Clears the shared item and closes Share Page.
+     */
+    onCloseButtonClick() {
+        clearSharedItem();
+        redirect(this.props.currentlyViewedReportID !== ''
+            ? ROUTES.getReportRoute(this.props.currentlyViewedReportID)
+            : ROUTES.HOME);
+    }
+
+    /**
+     * Hides navigation menu on redirect to report page.
+     */
+    onLinkClick() {
+        hideSidebar();
+    }
+
+    /**
+     * Method called when a Create Menu item is selected.
+     */
+    onCreateMenuItemSelected() {
+        this.toggleCreateMenu();
+        ChatSwitcher.show();
+    }
+
+    /**
+     * Posts shared item to selected report
+     *
+     * @param {Number} reportID report id
+     */
+    addSharedItemToReport(reportID) {
+        if (!this.props.sharedItem) {
+            return;
+        }
+
+        switch (this.props.sharedItem.type) {
+            case ShareManager.TYPE.TEXT:
+                addAction(reportID, this.props.sharedItem.data);
+                break;
+            case ShareManager.TYPE.FILE:
+                addAction(reportID, '', this.props.sharedItem.data);
+                break;
+            default:
+                break;
+        }
+
+        clearSharedItem();
+    }
+
+    /**
+     * Method called when we click the floating action button
+     *
+     * Method called either when:
+     * Pressing the floating action button to open the CreateMenu modal
+     * Selecting an item on CreateMenu or closing it by clicking outside of the modal component
+     */
+    toggleCreateMenu() {
+        this.setState(state => ({
+            isCreateMenuActive: !state.isCreateMenuActive,
+        }));
+    }
+
+    render() {
+        return (
+            <SafeAreaProvider>
+                <CustomStatusBar />
+                <SafeAreaInsetsContext.Consumer style={[styles.flex1]}>
+                    {insets => (
+                        <>
+                            <View style={[styles.flex1, styles.sidebar]}>
+                                <SidebarLinks
+                                    title="Send to..."
+                                    showCloseButton
+                                    insets={insets}
+                                    onCloseButtonClick={this.onCloseButtonClick}
+                                    onLinkClick={this.onLinkClick}
+                                    onReportSelected={this.addSharedItemToReport}
+                                />
+                                <FAB
+                                    isActive={this.state.isCreateMenuActive}
+                                    onPress={this.toggleCreateMenu}
+                                />
+                            </View>
+                            <CreateMenu
+                                onClose={this.toggleCreateMenu}
+                                isVisible={this.state.isCreateMenuActive}
+                                onItemSelected={this.onCreateMenuItemSelected}
+                            />
+                        </>
+                    )}
+                </SafeAreaInsetsContext.Consumer>
+            </SafeAreaProvider>
+        );
+    }
+}
+
+SharePage.propTypes = propTypes;
+SharePage.defaultProps = defaultProps;
+
+export default withOnyx({
+    currentlyViewedReportID: {
+        key: ONYXKEYS.CURRENTLY_VIEWED_REPORTID,
+    },
+    sharedItem: {
+        key: ONYXKEYS.SHARED_ITEM,
+    },
+})(SharePage);
diff --git a/src/pages/home/sidebar/ChatSwitcherView.js b/src/pages/home/sidebar/ChatSwitcherView.js
index 2bc222c4970a..7f669eef34a4 100644
--- a/src/pages/home/sidebar/ChatSwitcherView.js
+++ b/src/pages/home/sidebar/ChatSwitcherView.js
@@ -35,6 +35,9 @@ const propTypes = {
     // Toggles the navigation menu open and closed
     onLinkClick: PropTypes.func.isRequired,
 
+    // Called when report is selected
+    onReportSelected: PropTypes.func,
+
     /* Onyx Props */
 
     // All of the personal details for everyone
@@ -60,6 +63,7 @@ const propTypes = {
     isSidebarAnimating: PropTypes.bool,
 };
 const defaultProps = {
+    onReportSelected: () => {},
     personalDetails: {},
     reports: {},
     session: null,
@@ -241,7 +245,8 @@ class ChatSwitcherView extends React.Component {
      */
     startGroupChat() {
         const userLogins = _.map(this.state.usersToStartGroupReportWith, option => option.login);
-        fetchOrCreateChatReport([this.props.session.email, ...userLogins]);
+        fetchOrCreateChatReport([this.props.session.email, ...userLogins])
+            .then(this.props.onReportSelected);
         this.props.onLinkClick();
         this.reset();
     }
@@ -257,9 +262,11 @@ class ChatSwitcherView extends React.Component {
         // the user that was just selected and everyone in the list
         if (this.state.usersToStartGroupReportWith.length > 0) {
             const userLogins = _.map(this.state.usersToStartGroupReportWith, option => option.login);
-            fetchOrCreateChatReport([this.props.session.email, ...userLogins, selectedOption.login]);
+            fetchOrCreateChatReport([this.props.session.email, ...userLogins, selectedOption.login])
+                .then(this.props.onReportSelected);
         } else {
-            fetchOrCreateChatReport([this.props.session.email, selectedOption.login]);
+            fetchOrCreateChatReport([this.props.session.email, selectedOption.login])
+                .then(this.props.onReportSelected);
         }
 
         this.props.onLinkClick();
diff --git a/src/pages/home/sidebar/SidebarLinks.js b/src/pages/home/sidebar/SidebarLinks.js
index 9e685c93abe2..f8da9ee091b7 100644
--- a/src/pages/home/sidebar/SidebarLinks.js
+++ b/src/pages/home/sidebar/SidebarLinks.js
@@ -13,7 +13,7 @@ import {redirect} from '../../../libs/actions/App';
 import ROUTES from '../../../ROUTES';
 import * as ChatSwitcher from '../../../libs/actions/ChatSwitcher';
 import Icon from '../../../components/Icon';
-import {MagnifyingGlass} from '../../../components/Icon/Expensicons';
+import {Close, MagnifyingGlass} from '../../../components/Icon/Expensicons';
 import Header from '../../../components/Header';
 import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton';
 import AvatarWithIndicator from '../../../components/AvatarWithIndicator';
@@ -26,11 +26,26 @@ const propTypes = {
     onLinkClick: PropTypes.func.isRequired,
 
     // navigates to settings and hides sidebar
-    onAvatarClick: PropTypes.func.isRequired,
+    onAvatarClick: PropTypes.func,
+
+    // Navigates from page
+    onCloseButtonClick: PropTypes.func,
+
+    // Called when report is selected
+    onReportSelected: PropTypes.func,
 
     // Safe area insets required for mobile devices margins
     insets: SafeAreaInsetPropTypes.isRequired,
 
+    // Title of the Header
+    title: PropTypes.string,
+
+    // Should we show the avatar
+    showAvatar: PropTypes.bool,
+
+    // Should we show the close button
+    showCloseButton: PropTypes.bool,
+
     /* Onyx Props */
     // List of reports
     reports: PropTypes.objectOf(PropTypes.shape({
@@ -72,6 +87,12 @@ const propTypes = {
 };
 
 const defaultProps = {
+    title: 'Chats',
+    showAvatar: false,
+    showCloseButton: false,
+    onAvatarClick: () => console.error('`onAvatarClick` is not defined'),
+    onCloseButtonClick: () => console.error('`onCloseButtonClick` is not defined'),
+    onReportSelected: () => {},
     reports: {},
     isChatSwitcherActive: false,
     draftComments: {},
@@ -150,6 +171,7 @@ class SidebarLinks extends React.Component {
                         />
                         <ChatSwitcherView
                             onLinkClick={this.props.onLinkClick}
+                            onReportSelected={this.props.onReportSelected}
                         />
                     </Animated.View>
                 )}
@@ -166,21 +188,31 @@ class SidebarLinks extends React.Component {
                             styles.alignItemsCenter,
                         ]}
                         >
-                            <Header textSize="large" title="Chats" />
+                            <Header textSize="large" title={this.props.title} />
                             <TouchableOpacity
                                 style={[styles.flexRow, styles.ph5]}
                                 onPress={() => ChatSwitcher.show()}
                             >
                                 <Icon src={MagnifyingGlass} />
                             </TouchableOpacity>
-                            <TouchableOpacity
-                                onPress={this.props.onAvatarClick}
-                            >
-                                <AvatarWithIndicator
-                                    source={this.props.myPersonalDetails.avatarURL}
-                                    isActive={this.props.network && !this.props.network.isOffline}
-                                />
-                            </TouchableOpacity>
+                            {this.props.showCloseButton && (
+                                <TouchableOpacity
+                                    style={[styles.flexRow, styles.sidebarCloseButton]}
+                                    onPress={this.props.onCloseButtonClick}
+                                >
+                                    <Icon src={Close} />
+                                </TouchableOpacity>
+                            )}
+                            {this.props.showAvatar && (
+                                <TouchableOpacity
+                                    onPress={this.props.onAvatarClick}
+                                >
+                                    <AvatarWithIndicator
+                                        source={this.props.myPersonalDetails.avatarURL}
+                                        isActive={this.props.network && !this.props.network.isOffline}
+                                    />
+                                </TouchableOpacity>
+                            )}
                         </View>
                         <OptionsList
                             contentContainerStyles={[
@@ -192,6 +224,7 @@ class SidebarLinks extends React.Component {
                                 option => option.reportID === activeReportID
                             ))}
                             onSelectRow={(option) => {
+                                this.props.onReportSelected(option.reportID);
                                 redirect(ROUTES.getReportRoute(option.reportID));
                                 this.props.onLinkClick();
                             }}
diff --git a/src/pages/home/sidebar/SidebarView.js b/src/pages/home/sidebar/SidebarView.js
index 5b7ca98ebbb8..7aef284c29e0 100644
--- a/src/pages/home/sidebar/SidebarView.js
+++ b/src/pages/home/sidebar/SidebarView.js
@@ -33,6 +33,7 @@ const SidebarView = props => (
             <SidebarLinks
                 onLinkClick={props.onLinkClick}
                 insets={props.insets}
+                showAvatar
                 onAvatarClick={props.onAvatarClick}
             />
             <FAB
diff --git a/src/styles/styles.js b/src/styles/styles.js
index 5d23922b64a5..31d77abe1b9d 100644
--- a/src/styles/styles.js
+++ b/src/styles/styles.js
@@ -352,6 +352,12 @@ const styles = {
         width: variables.componentSizeNormal,
     },
 
+    sidebarCloseButton: {
+        paddingLeft: 20,
+        paddingRight: 0,
+        paddingVertical: 12,
+    },
+
     statusIndicator: {
         borderColor: themeColors.sidebar,
         borderRadius: 6,


Test patch:
diff --git a/tests/unit/loginTest.js b/tests/unit/loginTest.js
index 8bc0ec19d6a1..be22eef363f7 100644
--- a/tests/unit/loginTest.js
+++ b/tests/unit/loginTest.js
@@ -10,6 +10,13 @@ import renderer from 'react-test-renderer';
 import App from '../../src/App';
 import Expensify from '../../src/Expensify';
 
+// Set up manual mocks for methods used in the actions so our test does not fail.
+jest.mock('../../src/libs/ShareManager', () => ({
+    // There is no need for a jest.fn() since we don't need to make assertions against it.
+    register: () => {},
+    deregister: () => {},
+}));
+
 describe('AppComponent', () => {
     it('renders correctly', () => {
         renderer.create(<App />);