diff --git a/.eslintrc.js b/.eslintrc.js
index e0ebd2252c3d..5c23c7be0839 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -175,6 +175,7 @@ module.exports = {
'es/no-optional-chaining': 'off',
'valid-jsdoc': 'off',
'jsdoc/no-types': 'error',
+ 'rulesdir/no-default-props': 'error',
'import/no-extraneous-dependencies': 'off',
'rulesdir/prefer-underscore-method': 'off',
'rulesdir/prefer-import-module-contents': 'off',
diff --git a/.github/scripts/verifyPodfile.sh b/.github/scripts/verifyPodfile.sh
index ec2709a25786..cd94a49bb091 100755
--- a/.github/scripts/verifyPodfile.sh
+++ b/.github/scripts/verifyPodfile.sh
@@ -12,7 +12,7 @@ declare EXIT_CODE=0
# Check Provisioning Style. If automatic signing is enabled, iOS builds will fail, so ensure we always have the proper profile specified
info "Verifying that automatic signing is not enabled"
-if grep -q 'PROVISIONING_PROFILE_SPECIFIER = chat_expensify_appstore' ios/NewExpensify.xcodeproj/project.pbxproj; then
+if grep -q 'PROVISIONING_PROFILE_SPECIFIER = "(NewApp) AppStore"' ios/NewExpensify.xcodeproj/project.pbxproj; then
success "Automatic signing not enabled"
else
error "Error: Automatic provisioning style is not allowed!"
diff --git a/.github/workflows/README.md b/.github/workflows/README.md
index d940d99d9cde..b2cc58fa4a6a 100644
--- a/.github/workflows/README.md
+++ b/.github/workflows/README.md
@@ -83,8 +83,10 @@ The GitHub workflows require a large list of secrets to deploy, notify and test
1. `LARGE_SECRET_PASSPHRASE` - decrypts secrets stored in various encrypted files stored in GitHub repository. To create updated versions of these encrypted files, refer to steps 1-4 of [this encrypted secrets help page](https://docs.github.com/en/actions/reference/encrypted-secrets#limits-for-secrets) using the `LARGE_SECRET_PASSPHRASE`.
1. `android/app/my-upload-key.keystore.gpg`
1. `android/app/android-fastlane-json-key.json.gpg`
- 1. `ios/expensify_chat_adhoc.mobileprovision.gpg`
- 1. `ios/chat_expensify_appstore.mobileprovision.gpg`
+ 1. `ios/NewApp_AdHoc.mobileprovision`
+ 1. `ios/NewApp_AdHoc_Notification_Service.mobileprovision`
+ 1. `ios/NewApp_AppStore.mobileprovision.gpg`
+ 1. `ios/NewApp_AppStore_Notification_Service.mobileprovision.gpg`
1. `ios/Certificates.p12.gpg`
1. `SLACK_WEBHOOK` - Sends Slack notifications via Slack WebHook https://expensify.slack.com/services/B01AX48D7MM
1. `OS_BOTIFY_TOKEN` - Personal access token for @OSBotify user in GitHub
diff --git a/.github/workflows/platformDeploy.yml b/.github/workflows/platformDeploy.yml
index faf06cacc62f..4c5dfdb14627 100644
--- a/.github/workflows/platformDeploy.yml
+++ b/.github/workflows/platformDeploy.yml
@@ -211,8 +211,13 @@ jobs:
max_attempts: 5
command: cd ios && bundle exec pod install
- - name: Decrypt profile
- run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output chat_expensify_appstore.mobileprovision chat_expensify_appstore.mobileprovision.gpg
+ - name: Decrypt AppStore profile
+ run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output NewApp_AppStore.mobileprovision NewApp_AppStore.mobileprovision.gpg
+ env:
+ LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }}
+
+ - name: Decrypt AppStore Notification Service profile
+ run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output NewApp_AppStore_Notification_Service.mobileprovision NewApp_AppStore_Notification_Service.mobileprovision.gpg
env:
LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }}
diff --git a/.github/workflows/testBuild.yml b/.github/workflows/testBuild.yml
index 196d7115bfc4..5d860b41b786 100644
--- a/.github/workflows/testBuild.yml
+++ b/.github/workflows/testBuild.yml
@@ -187,8 +187,13 @@ jobs:
max_attempts: 5
command: cd ios && bundle exec pod install
- - name: Decrypt profile
- run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output expensify_chat_adhoc.mobileprovision expensify_chat_adhoc.mobileprovision.gpg
+ - name: Decrypt AdHoc profile
+ run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output NewApp_AdHoc.mobileprovision NewApp_AdHoc.mobileprovision.gpg
+ env:
+ LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }}
+
+ - name: Decrypt AdHoc Notification Service profile
+ run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output NewApp_AdHoc_Notification_Service.mobileprovision NewApp_AdHoc_Notification_Service.mobileprovision.gpg
env:
LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }}
diff --git a/README.md b/README.md
index b2d51b2a1709..3b9010695760 100644
--- a/README.md
+++ b/README.md
@@ -159,6 +159,9 @@ to help run our Unit tests.
* To run the **Jest unit tests**: `npm run test`
+## Performance tests
+We use Reassure for monitoring performance regression. More detailed information can be found [here](tests/perf-test/README.md):
+
----
# Debugging
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 8e4daef2e6cb..341c9ff046a1 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -96,8 +96,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
- versionCode 1001041707
- versionName "1.4.17-7"
+ versionCode 1001041803
+ versionName "1.4.18-3"
}
flavorDimensions "default"
diff --git a/assets/images/simple-illustrations/simple-illustration__bigrocket.svg b/assets/images/simple-illustrations/simple-illustration__bigrocket.svg
new file mode 100644
index 000000000000..1afd5f66b6ea
--- /dev/null
+++ b/assets/images/simple-illustrations/simple-illustration__bigrocket.svg
@@ -0,0 +1,100 @@
+
diff --git a/assets/images/simple-illustrations/simple-illustration__handcard.svg b/assets/images/simple-illustrations/simple-illustration__handcard.svg
new file mode 100644
index 000000000000..7419b33d425c
--- /dev/null
+++ b/assets/images/simple-illustrations/simple-illustration__handcard.svg
@@ -0,0 +1,41 @@
+
+
+
diff --git a/assets/images/simple-illustrations/simple-illustration__hotdogstand.svg b/assets/images/simple-illustrations/simple-illustration__hotdogstand.svg
index 732c16a75a2b..471b978bb97e 100644
--- a/assets/images/simple-illustrations/simple-illustration__hotdogstand.svg
+++ b/assets/images/simple-illustrations/simple-illustration__hotdogstand.svg
@@ -1 +1,98 @@
-
\ No newline at end of file
+
diff --git a/assets/images/simple-illustrations/simple-illustration__mailbox.svg b/assets/images/simple-illustrations/simple-illustration__mailbox.svg
new file mode 100644
index 000000000000..81b1f508fb52
--- /dev/null
+++ b/assets/images/simple-illustrations/simple-illustration__mailbox.svg
@@ -0,0 +1,71 @@
+
+
+
diff --git a/assets/images/simple-illustrations/simple-illustration__smallrocket.svg b/assets/images/simple-illustrations/simple-illustration__smallrocket.svg
new file mode 100644
index 000000000000..0f8f166c849f
--- /dev/null
+++ b/assets/images/simple-illustrations/simple-illustration__smallrocket.svg
@@ -0,0 +1,45 @@
+
diff --git a/fastlane/Fastfile b/fastlane/Fastfile
index 2b5c15146eb3..b7d3334c902f 100644
--- a/fastlane/Fastfile
+++ b/fastlane/Fastfile
@@ -161,19 +161,23 @@ platform :ios do
)
install_provisioning_profile(
- path: "./ios/expensify_chat_adhoc.mobileprovision"
+ path: "./ios/NewApp_AdHoc.mobileprovision"
+ )
+
+ install_provisioning_profile(
+ path: "./ios/NewApp_AdHoc_Notification_Service.mobileprovision"
)
build_app(
workspace: "./ios/NewExpensify.xcworkspace",
skip_profile_detection: true,
scheme: "New Expensify AdHoc",
- xcargs: { :PROVISIONING_PROFILE_SPECIFIER => "expensify_chat_adhoc", },
export_method: "ad-hoc",
export_options: {
method: "ad-hoc",
provisioningProfiles: {
- "com.expensify.chat.adhoc" => "expensify_chat_adhoc",
+ "com.expensify.chat.adhoc" => "(NewApp) AdHoc",
+ "com.expensify.chat.adhoc.NotificationServiceExtension" => "(NewApp) AdHoc: Notification Service",
},
manageAppVersionAndBuildNumber: false
}
@@ -215,7 +219,11 @@ platform :ios do
)
install_provisioning_profile(
- path: "./ios/chat_expensify_appstore.mobileprovision"
+ path: "./ios/NewApp_AppStore.mobileprovision"
+ )
+
+ install_provisioning_profile(
+ path: "./ios/NewApp_AppStore_Notification_Service.mobileprovision"
)
build_app(
@@ -223,6 +231,10 @@ platform :ios do
scheme: "New Expensify",
output_name: "New Expensify.ipa",
export_options: {
+ provisioningProfiles: {
+ "com.chat.expensify.chat" => "(NewApp) AppStore",
+ "com.chat.expensify.chat.NotificationServiceExtension" => "(NewApp) AppStore: Notification Service",
+ },
manageAppVersionAndBuildNumber: false
}
)
diff --git a/ios/NewApp_AdHoc.mobileprovision.gpg b/ios/NewApp_AdHoc.mobileprovision.gpg
new file mode 100644
index 000000000000..e5ee52210866
Binary files /dev/null and b/ios/NewApp_AdHoc.mobileprovision.gpg differ
diff --git a/ios/NewApp_AdHoc_Notification_Service.mobileprovision.gpg b/ios/NewApp_AdHoc_Notification_Service.mobileprovision.gpg
new file mode 100644
index 000000000000..ba9258840638
Binary files /dev/null and b/ios/NewApp_AdHoc_Notification_Service.mobileprovision.gpg differ
diff --git a/ios/NewApp_AppStore.mobileprovision.gpg b/ios/NewApp_AppStore.mobileprovision.gpg
new file mode 100644
index 000000000000..2e73cc7661e3
Binary files /dev/null and b/ios/NewApp_AppStore.mobileprovision.gpg differ
diff --git a/ios/NewApp_AppStore_Notification_Service.mobileprovision.gpg b/ios/NewApp_AppStore_Notification_Service.mobileprovision.gpg
new file mode 100644
index 000000000000..4af7b16c5041
Binary files /dev/null and b/ios/NewApp_AppStore_Notification_Service.mobileprovision.gpg differ
diff --git a/ios/NewExpensify.xcodeproj/project.pbxproj b/ios/NewExpensify.xcodeproj/project.pbxproj
index 6b57929cfc2c..c239f4da183f 100644
--- a/ios/NewExpensify.xcodeproj/project.pbxproj
+++ b/ios/NewExpensify.xcodeproj/project.pbxproj
@@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
+ 059DC4EFD39EF39437E6823D /* libPods-NotificationServiceExtension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A997AA8204EA3D90907FA80 /* libPods-NotificationServiceExtension.a */; };
0C7C65547D7346EB923BE808 /* ExpensifyMono-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = E704648954784DDFBAADF568 /* ExpensifyMono-Regular.otf */; };
0CDA8E34287DD650004ECBEC /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0CDA8E33287DD650004ECBEC /* AppDelegate.mm */; };
0CDA8E35287DD650004ECBEC /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0CDA8E33287DD650004ECBEC /* AppDelegate.mm */; };
@@ -27,6 +28,9 @@
7041848626A8E47D00E09F4D /* RCTStartupTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 7041848426A8E47D00E09F4D /* RCTStartupTimer.m */; };
70CF6E82262E297300711ADC /* BootSplash.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 70CF6E81262E297300711ADC /* BootSplash.storyboard */; };
7F5E81F06BCCF61AD02CEA06 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD444BEDDB0AF1745B39049 /* ExpoModulesProvider.swift */; };
+ 7F9DD8DA2B2A445B005E3AFA /* ExpError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F9DD8D92B2A445B005E3AFA /* ExpError.swift */; };
+ 7FD73C9E2B23CE9500420AF3 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FD73C9D2B23CE9500420AF3 /* NotificationService.swift */; };
+ 7FD73CA22B23CE9500420AF3 /* NotificationServiceExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 7FD73C9B2B23CE9500420AF3 /* NotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
976CCB5F8C921482E6AEAE71 /* libPods-NewExpensify.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AB40AC8872A3DD6EF53D8B94 /* libPods-NewExpensify.a */; };
BDB853621F354EBB84E619C2 /* ExpensifyNewKansas-MediumItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = D2AFB39EC1D44BF9B91D3227 /* ExpensifyNewKansas-MediumItalic.otf */; };
DD79042B2792E76D004484B4 /* RCTBootSplash.m in Sources */ = {isa = PBXBuildFile; fileRef = DD79042A2792E76D004484B4 /* RCTBootSplash.m */; };
@@ -34,6 +38,7 @@
E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */ = {isa = PBXBuildFile; };
E9DF872D2525201700607FDC /* AirshipConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9DF872C2525201700607FDC /* AirshipConfig.plist */; };
ED222ED90E074A5481A854FA /* ExpensifyNeue-BoldItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = 8B28D84EF339436DBD42A203 /* ExpensifyNeue-BoldItalic.otf */; };
+ EEAE4F8907465429AA5B5520 /* libPods-NewExpensify.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AEFE6CD54912D427D19133C7 /* libPods-NewExpensify.a */; };
F0C450EA2705020500FD2970 /* colors.json in Resources */ = {isa = PBXBuildFile; fileRef = F0C450E92705020500FD2970 /* colors.json */; };
FF941A8D48F849269AB85C9A /* ExpensifyNewKansas-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = 44BF435285B94E5B95F90994 /* ExpensifyNewKansas-Medium.otf */; };
/* End PBXBuildFile section */
@@ -46,8 +51,29 @@
remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
remoteInfo = NewExpensify;
};
+ 7FD73CA02B23CE9500420AF3 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 7FD73C9A2B23CE9500420AF3;
+ remoteInfo = NotificationServiceExtension;
+ };
/* End PBXContainerItemProxy section */
+/* Begin PBXCopyFilesBuildPhase section */
+ 7FD73CA32B23CE9500420AF3 /* Embed Foundation Extensions */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 13;
+ files = (
+ 7FD73CA22B23CE9500420AF3 /* NotificationServiceExtension.appex in Embed Foundation Extensions */,
+ );
+ name = "Embed Foundation Extensions";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
/* Begin PBXFileReference section */
008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; };
00E356EE1AD99517003FC87E /* NewExpensifyTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NewExpensifyTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -63,7 +89,12 @@
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = NewExpensify/Info.plist; sourceTree = ""; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = NewExpensify/main.m; sourceTree = ""; };
18D050DF262400AF000D658B /* BridgingFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BridgingFile.swift; sourceTree = ""; };
+ 1A997AA8204EA3D90907FA80 /* libPods-NotificationServiceExtension.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-NotificationServiceExtension.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 1DDE5449979A136852B939B5 /* Pods-NewExpensify.release adhoc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify.release adhoc.xcconfig"; path = "Target Support Files/Pods-NewExpensify/Pods-NewExpensify.release adhoc.xcconfig"; sourceTree = ""; };
+ 25A4587E168FD67CF890B448 /* Pods-NewExpensify-NewExpensifyTests.debugadhoc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify-NewExpensifyTests.debugadhoc.xcconfig"; path = "Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests.debugadhoc.xcconfig"; sourceTree = ""; };
+ 30FFBD291B71222A393D9CC9 /* Pods-NewExpensify.releasedevelopment.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify.releasedevelopment.xcconfig"; path = "Target Support Files/Pods-NewExpensify/Pods-NewExpensify.releasedevelopment.xcconfig"; sourceTree = ""; };
32181F72DC539FFD1D1F0CA4 /* Pods-NewExpensify.releaseproduction.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify.releaseproduction.xcconfig"; path = "Target Support Files/Pods-NewExpensify/Pods-NewExpensify.releaseproduction.xcconfig"; sourceTree = ""; };
+ 34A8FDD1F9AA58B8F15C8380 /* Pods-NewExpensify.release production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify.release production.xcconfig"; path = "Target Support Files/Pods-NewExpensify/Pods-NewExpensify.release production.xcconfig"; sourceTree = ""; };
374FB8D528A133A7000D84EF /* OriginImageRequestHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = OriginImageRequestHandler.h; path = NewExpensify/OriginImageRequestHandler.h; sourceTree = ""; };
374FB8D628A133FE000D84EF /* OriginImageRequestHandler.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = OriginImageRequestHandler.mm; path = NewExpensify/OriginImageRequestHandler.mm; sourceTree = ""; };
3BBA44B891E03FAB8255E6F1 /* Pods-NewExpensify-NewExpensifyTests.debugdevelopment.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify-NewExpensifyTests.debugdevelopment.xcconfig"; path = "Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests.debugdevelopment.xcconfig"; sourceTree = ""; };
@@ -76,26 +107,48 @@
7041848326A8E40900E09F4D /* RCTStartupTimer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RCTStartupTimer.h; path = NewExpensify/RCTStartupTimer.h; sourceTree = ""; };
7041848426A8E47D00E09F4D /* RCTStartupTimer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = RCTStartupTimer.m; path = NewExpensify/RCTStartupTimer.m; sourceTree = ""; };
70CF6E81262E297300711ADC /* BootSplash.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = BootSplash.storyboard; path = NewExpensify/BootSplash.storyboard; sourceTree = ""; };
+ 75CABB0D0ABB0082FE0EB600 /* Pods-NewExpensify.release staging.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify.release staging.xcconfig"; path = "Target Support Files/Pods-NewExpensify/Pods-NewExpensify.release staging.xcconfig"; sourceTree = ""; };
76BE68DA894BB75DDFE278DC /* Pods-NewExpensify.releasedevelopment.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify.releasedevelopment.xcconfig"; path = "Target Support Files/Pods-NewExpensify/Pods-NewExpensify.releasedevelopment.xcconfig"; sourceTree = ""; };
7B318CF669A0F7FE948D5CED /* Pods-NewExpensify.debugadhoc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify.debugadhoc.xcconfig"; path = "Target Support Files/Pods-NewExpensify/Pods-NewExpensify.debugadhoc.xcconfig"; sourceTree = ""; };
+ 7F9DD8D92B2A445B005E3AFA /* ExpError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpError.swift; sourceTree = ""; };
+ 7FD73C9B2B23CE9500420AF3 /* NotificationServiceExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationServiceExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
+ 7FD73C9D2B23CE9500420AF3 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; };
+ 7FD73C9F2B23CE9500420AF3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 8709DF3C8D91F0FC1581CDD7 /* Pods-NewExpensify-NewExpensifyTests.debugdevelopment.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify-NewExpensifyTests.debugdevelopment.xcconfig"; path = "Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests.debugdevelopment.xcconfig"; sourceTree = ""; };
8B28D84EF339436DBD42A203 /* ExpensifyNeue-BoldItalic.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "ExpensifyNeue-BoldItalic.otf"; path = "../assets/fonts/native/ExpensifyNeue-BoldItalic.otf"; sourceTree = ""; };
+ 8D3B36BF88E773E3C1A383FA /* Pods-NewExpensify.debug staging.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify.debug staging.xcconfig"; path = "Target Support Files/Pods-NewExpensify/Pods-NewExpensify.debug staging.xcconfig"; sourceTree = ""; };
+ 90E08F0C8C924EDA018C8866 /* Pods-NotificationServiceExtension.releaseproduction.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationServiceExtension.releaseproduction.xcconfig"; path = "Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.releaseproduction.xcconfig"; sourceTree = ""; };
+ 96552D489D9F09B6A5ABD81B /* Pods-NewExpensify-NewExpensifyTests.release production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify-NewExpensifyTests.release production.xcconfig"; path = "Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests.release production.xcconfig"; sourceTree = ""; };
AB40AC8872A3DD6EF53D8B94 /* libPods-NewExpensify.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-NewExpensify.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ AEFE6CD54912D427D19133C7 /* libPods-NewExpensify.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-NewExpensify.a"; sourceTree = BUILT_PRODUCTS_DIR; };
BCD444BEDDB0AF1745B39049 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-NewExpensify-NewExpensifyTests/ExpoModulesProvider.swift"; sourceTree = ""; };
+ BD6E1BA27D6ABE0AC9D70586 /* Pods-NewExpensify-NewExpensifyTests.release development.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify-NewExpensifyTests.release development.xcconfig"; path = "Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests.release development.xcconfig"; sourceTree = ""; };
+ BD8828A882E2D6B51362AAC3 /* Pods-NewExpensify.releaseadhoc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify.releaseadhoc.xcconfig"; path = "Target Support Files/Pods-NewExpensify/Pods-NewExpensify.releaseadhoc.xcconfig"; sourceTree = ""; };
BF6A4C5167244B9FB8E4D4E3 /* ExpensifyNeue-Italic.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "ExpensifyNeue-Italic.otf"; path = "../assets/fonts/native/ExpensifyNeue-Italic.otf"; sourceTree = ""; };
C3788801E65E896FA7C77298 /* Pods-NewExpensify.debugproduction.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify.debugproduction.xcconfig"; path = "Target Support Files/Pods-NewExpensify/Pods-NewExpensify.debugproduction.xcconfig"; sourceTree = ""; };
+ C3FF914C045A138C061D306E /* Pods-NotificationServiceExtension.debugproduction.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationServiceExtension.debugproduction.xcconfig"; path = "Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.debugproduction.xcconfig"; sourceTree = ""; };
+ CE2F84BEE9A6DCC228AF7E42 /* Pods-NewExpensify.debugproduction.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify.debugproduction.xcconfig"; path = "Target Support Files/Pods-NewExpensify/Pods-NewExpensify.debugproduction.xcconfig"; sourceTree = ""; };
+ CECC4CBB97A55705A33BEA9E /* Pods-NewExpensify.debug development.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify.debug development.xcconfig"; path = "Target Support Files/Pods-NewExpensify/Pods-NewExpensify.debug development.xcconfig"; sourceTree = ""; };
D2AFB39EC1D44BF9B91D3227 /* ExpensifyNewKansas-MediumItalic.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "ExpensifyNewKansas-MediumItalic.otf"; path = "../assets/fonts/native/ExpensifyNewKansas-MediumItalic.otf"; sourceTree = ""; };
+ D3F458C994019E6A571461B7 /* Pods-NotificationServiceExtension.debugadhoc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationServiceExtension.debugadhoc.xcconfig"; path = "Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.debugadhoc.xcconfig"; sourceTree = ""; };
+ DB76E0D5C670190A0997C71E /* Pods-NewExpensify-NewExpensifyTests.debug production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify-NewExpensifyTests.debug production.xcconfig"; path = "Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests.debug production.xcconfig"; sourceTree = ""; };
DCF33E34FFEC48128CDD41D4 /* ExpensifyMono-Bold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "ExpensifyMono-Bold.otf"; path = "../assets/fonts/native/ExpensifyMono-Bold.otf"; sourceTree = ""; };
DD7904292792E76D004484B4 /* RCTBootSplash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RCTBootSplash.h; path = NewExpensify/RCTBootSplash.h; sourceTree = ""; };
DD79042A2792E76D004484B4 /* RCTBootSplash.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RCTBootSplash.m; path = NewExpensify/RCTBootSplash.m; sourceTree = ""; };
+ E2C8555C607612465A7473F8 /* Pods-NewExpensify-NewExpensifyTests.releaseadhoc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify-NewExpensifyTests.releaseadhoc.xcconfig"; path = "Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests.releaseadhoc.xcconfig"; sourceTree = ""; };
+ E2F1036F70CBFE39E9352674 /* Pods-NewExpensify-NewExpensifyTests.debug development.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify-NewExpensifyTests.debug development.xcconfig"; path = "Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests.debug development.xcconfig"; sourceTree = ""; };
E2F78D2A9B3DB96F0524690B /* Pods-NewExpensify-NewExpensifyTests.releaseproduction.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify-NewExpensifyTests.releaseproduction.xcconfig"; path = "Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests.releaseproduction.xcconfig"; sourceTree = ""; };
+ E61AD6D2DE65B6FB14945CDF /* Pods-NotificationServiceExtension.releaseadhoc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationServiceExtension.releaseadhoc.xcconfig"; path = "Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.releaseadhoc.xcconfig"; sourceTree = ""; };
E681F80D97E6E4BB26194246 /* Pods-NewExpensify-NewExpensifyTests.releasedevelopment.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify-NewExpensifyTests.releasedevelopment.xcconfig"; path = "Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests.releasedevelopment.xcconfig"; sourceTree = ""; };
E704648954784DDFBAADF568 /* ExpensifyMono-Regular.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "ExpensifyMono-Regular.otf"; path = "../assets/fonts/native/ExpensifyMono-Regular.otf"; sourceTree = ""; };
E9DF872C2525201700607FDC /* AirshipConfig.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = AirshipConfig.plist; sourceTree = ""; };
EA58D43E81BC49541F7FC7E7 /* Pods-NewExpensify.debugdevelopment.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify.debugdevelopment.xcconfig"; path = "Target Support Files/Pods-NewExpensify/Pods-NewExpensify.debugdevelopment.xcconfig"; sourceTree = ""; };
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; };
+ F082D95EE104912B48EA98BA /* Pods-NotificationServiceExtension.releasedevelopment.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationServiceExtension.releasedevelopment.xcconfig"; path = "Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.releasedevelopment.xcconfig"; sourceTree = ""; };
F0C450E92705020500FD2970 /* colors.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = colors.json; path = ../colors.json; sourceTree = ""; };
F4F8A052A22040339996324B /* ExpensifyNeue-Regular.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "ExpensifyNeue-Regular.otf"; path = "../assets/fonts/native/ExpensifyNeue-Regular.otf"; sourceTree = ""; };
+ FBEBA6FBED49FB41D6F93896 /* Pods-NotificationServiceExtension.debugdevelopment.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationServiceExtension.debugdevelopment.xcconfig"; path = "Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.debugdevelopment.xcconfig"; sourceTree = ""; };
FF0EADDA6099EF76253FA7AB /* Pods-NewExpensify-NewExpensifyTests.releaseadhoc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify-NewExpensifyTests.releaseadhoc.xcconfig"; path = "Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests.releaseadhoc.xcconfig"; sourceTree = ""; };
/* End PBXFileReference section */
@@ -114,6 +167,16 @@
files = (
E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */,
976CCB5F8C921482E6AEAE71 /* libPods-NewExpensify.a in Frameworks */,
+ E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */,
+ EEAE4F8907465429AA5B5520 /* libPods-NewExpensify.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 7FD73C982B23CE9500420AF3 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 059DC4EFD39EF39437E6823D /* libPods-NotificationServiceExtension.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -159,6 +222,8 @@
children = (
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
+ AEFE6CD54912D427D19133C7 /* libPods-NewExpensify.a */,
+ 1A997AA8204EA3D90907FA80 /* libPods-NotificationServiceExtension.a */,
AB40AC8872A3DD6EF53D8B94 /* libPods-NewExpensify.a */,
076FD9E41E08971BBF51D580 /* libPods-NewExpensify-NewExpensifyTests.a */,
);
@@ -182,6 +247,16 @@
name = ExpoModulesProviders;
sourceTree = "";
};
+ 7FD73C9C2B23CE9500420AF3 /* NotificationServiceExtension */ = {
+ isa = PBXGroup;
+ children = (
+ 7FD73C9D2B23CE9500420AF3 /* NotificationService.swift */,
+ 7FD73C9F2B23CE9500420AF3 /* Info.plist */,
+ 7F9DD8D92B2A445B005E3AFA /* ExpError.swift */,
+ );
+ path = NotificationServiceExtension;
+ sourceTree = "";
+ };
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
isa = PBXGroup;
children = (
@@ -203,6 +278,7 @@
13B07FAE1A68108700A75B9A /* NewExpensify */,
832341AE1AAA6A7D00B99B32 /* Libraries */,
00E356EF1AD99517003FC87E /* NewExpensifyTests */,
+ 7FD73C9C2B23CE9500420AF3 /* NotificationServiceExtension */,
83CBBA001A601CBA00E9B192 /* Products */,
2D16E6871FA4F8E400B85C8A /* Frameworks */,
EC29677F0A49C2946A495A33 /* Pods */,
@@ -219,6 +295,7 @@
children = (
13B07F961A680F5B00A75B9A /* New Expensify Dev.app */,
00E356EE1AD99517003FC87E /* NewExpensifyTests.xctest */,
+ 7FD73C9B2B23CE9500420AF3 /* NotificationServiceExtension.appex */,
);
name = Products;
sourceTree = "";
@@ -249,6 +326,27 @@
EC29677F0A49C2946A495A33 /* Pods */ = {
isa = PBXGroup;
children = (
+ CECC4CBB97A55705A33BEA9E /* Pods-NewExpensify.debug development.xcconfig */,
+ 8D3B36BF88E773E3C1A383FA /* Pods-NewExpensify.debug staging.xcconfig */,
+ 1DDE5449979A136852B939B5 /* Pods-NewExpensify.release adhoc.xcconfig */,
+ 75CABB0D0ABB0082FE0EB600 /* Pods-NewExpensify.release staging.xcconfig */,
+ 34A8FDD1F9AA58B8F15C8380 /* Pods-NewExpensify.release production.xcconfig */,
+ E2F1036F70CBFE39E9352674 /* Pods-NewExpensify-NewExpensifyTests.debug development.xcconfig */,
+ DB76E0D5C670190A0997C71E /* Pods-NewExpensify-NewExpensifyTests.debug production.xcconfig */,
+ BD6E1BA27D6ABE0AC9D70586 /* Pods-NewExpensify-NewExpensifyTests.release development.xcconfig */,
+ 96552D489D9F09B6A5ABD81B /* Pods-NewExpensify-NewExpensifyTests.release production.xcconfig */,
+ CE2F84BEE9A6DCC228AF7E42 /* Pods-NewExpensify.debugproduction.xcconfig */,
+ 30FFBD291B71222A393D9CC9 /* Pods-NewExpensify.releasedevelopment.xcconfig */,
+ BD8828A882E2D6B51362AAC3 /* Pods-NewExpensify.releaseadhoc.xcconfig */,
+ 8709DF3C8D91F0FC1581CDD7 /* Pods-NewExpensify-NewExpensifyTests.debugdevelopment.xcconfig */,
+ 25A4587E168FD67CF890B448 /* Pods-NewExpensify-NewExpensifyTests.debugadhoc.xcconfig */,
+ E2C8555C607612465A7473F8 /* Pods-NewExpensify-NewExpensifyTests.releaseadhoc.xcconfig */,
+ FBEBA6FBED49FB41D6F93896 /* Pods-NotificationServiceExtension.debugdevelopment.xcconfig */,
+ D3F458C994019E6A571461B7 /* Pods-NotificationServiceExtension.debugadhoc.xcconfig */,
+ C3FF914C045A138C061D306E /* Pods-NotificationServiceExtension.debugproduction.xcconfig */,
+ F082D95EE104912B48EA98BA /* Pods-NotificationServiceExtension.releasedevelopment.xcconfig */,
+ E61AD6D2DE65B6FB14945CDF /* Pods-NotificationServiceExtension.releaseadhoc.xcconfig */,
+ 90E08F0C8C924EDA018C8866 /* Pods-NotificationServiceExtension.releaseproduction.xcconfig */,
EA58D43E81BC49541F7FC7E7 /* Pods-NewExpensify.debugdevelopment.xcconfig */,
7B318CF669A0F7FE948D5CED /* Pods-NewExpensify.debugadhoc.xcconfig */,
C3788801E65E896FA7C77298 /* Pods-NewExpensify.debugproduction.xcconfig */,
@@ -298,6 +396,7 @@
FD10A7F022414F080027D42C /* Start Packager */,
5CF45ABA52C0BB0D7B9D139A /* [Expo] Configure project */,
13B07F871A680F5B00A75B9A /* Sources */,
+ 7FD73CA32B23CE9500420AF3 /* Embed Foundation Extensions */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
@@ -309,18 +408,38 @@
buildRules = (
);
dependencies = (
+ 7FD73CA12B23CE9500420AF3 /* PBXTargetDependency */,
);
name = NewExpensify;
productName = NewExpensify;
productReference = 13B07F961A680F5B00A75B9A /* New Expensify Dev.app */;
productType = "com.apple.product-type.application";
};
+ 7FD73C9A2B23CE9500420AF3 /* NotificationServiceExtension */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 7FD73CAA2B23CE9500420AF3 /* Build configuration list for PBXNativeTarget "NotificationServiceExtension" */;
+ buildPhases = (
+ F3D35ED760B830954BD8A7BB /* [CP] Check Pods Manifest.lock */,
+ 7FD73C972B23CE9500420AF3 /* Sources */,
+ 7FD73C982B23CE9500420AF3 /* Frameworks */,
+ 7FD73C992B23CE9500420AF3 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = NotificationServiceExtension;
+ productName = NotificationServiceExtension;
+ productReference = 7FD73C9B2B23CE9500420AF3 /* NotificationServiceExtension.appex */;
+ productType = "com.apple.product-type.app-extension";
+ };
/* End PBXNativeTarget section */
/* Begin PBXProject section */
83CBB9F71A601CBA00E9B192 /* Project object */ = {
isa = PBXProject;
attributes = {
+ LastSwiftUpdateCheck = 1500;
LastUpgradeCheck = 1130;
TargetAttributes = {
00E356ED1AD99517003FC87E = {
@@ -334,6 +453,11 @@
LastSwiftMigration = 1230;
ProvisioningStyle = Manual;
};
+ 7FD73C9A2B23CE9500420AF3 = {
+ CreatedOnToolsVersion = 15.0.1;
+ DevelopmentTeam = 368M544MTT;
+ ProvisioningStyle = Manual;
+ };
};
};
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "NewExpensify" */;
@@ -351,6 +475,7 @@
targets = (
13B07F861A680F5B00A75B9A /* NewExpensify */,
00E356ED1AD99517003FC87E /* NewExpensifyTests */,
+ 7FD73C9A2B23CE9500420AF3 /* NotificationServiceExtension */,
);
};
/* End PBXProject section */
@@ -384,6 +509,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 7FD73C992B23CE9500420AF3 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
@@ -616,6 +748,28 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NewExpensify/Pods-NewExpensify-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
+ F3D35ED760B830954BD8A7BB /* [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-NotificationServiceExtension-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;
+ };
F6E16E41F88F567A8CDD037C /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -694,6 +848,15 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 7FD73C972B23CE9500420AF3 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 7F9DD8DA2B2A445B005E3AFA /* ExpError.swift in Sources */,
+ 7FD73C9E2B23CE9500420AF3 /* NotificationService.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
@@ -702,6 +865,11 @@
target = 13B07F861A680F5B00A75B9A /* NewExpensify */;
targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
};
+ 7FD73CA12B23CE9500420AF3 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 7FD73C9A2B23CE9500420AF3 /* NotificationServiceExtension */;
+ targetProxy = 7FD73CA02B23CE9500420AF3 /* PBXContainerItemProxy */;
+ };
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
@@ -739,7 +907,6 @@
isa = XCBuildConfiguration;
baseConfigurationReference = E681F80D97E6E4BB26194246 /* Pods-NewExpensify-NewExpensifyTests.releasedevelopment.xcconfig */;
buildSettings = {
- ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
@@ -772,9 +939,11 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = NewExpensify/Chat.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = 368M544MTT;
+ "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 368M544MTT;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "$(SRCROOT)/NewExpensify/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
@@ -791,7 +960,8 @@
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = com.expensify.chat.dev;
PRODUCT_NAME = "New Expensify Dev";
- PROVISIONING_PROFILE_SPECIFIER = expensify_chat_dev;
+ PROVISIONING_PROFILE_SPECIFIER = "(NewApp) Development";
+ "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "(NewApp) Development";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -808,9 +978,11 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = NewExpensify/Chat.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = 368M544MTT;
+ "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 368M544MTT;
INFOPLIST_FILE = NewExpensify/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
@@ -826,13 +998,519 @@
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE -D EXPO_CONFIGURATION_DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = com.expensify.chat.dev;
PRODUCT_NAME = "New Expensify Dev";
- PROVISIONING_PROFILE_SPECIFIER = expensify_chat_dev;
+ PROVISIONING_PROFILE_SPECIFIER = "(NewApp) Development";
+ "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "(NewApp) Development";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
};
name = ReleaseDevelopment;
};
+ 7FD73CA42B23CE9500420AF3 /* DebugDevelopment */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = FBEBA6FBED49FB41D6F93896 /* Pods-NotificationServiceExtension.debugdevelopment.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "iPhone Distribution";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ CODE_SIGN_STYLE = Manual;
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 1;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ DEVELOPMENT_TEAM = "";
+ "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 368M544MTT;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = NotificationServiceExtension/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = NotificationServiceExtension;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@executable_path/../../Frameworks",
+ );
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MARKETING_VERSION = 1.0;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
+ PRODUCT_BUNDLE_IDENTIFIER = com.expensify.chat.dev.NotificationServiceExtension;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "(NewApp) Development: Notification Service";
+ SDKROOT = iphoneos;
+ SKIP_INSTALL = YES;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = DebugDevelopment;
+ };
+ 7FD73CA52B23CE9500420AF3 /* DebugAdHoc */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D3F458C994019E6A571461B7 /* Pods-NotificationServiceExtension.debugadhoc.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "iPhone Distribution";
+ CODE_SIGN_STYLE = Manual;
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 1;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ DEVELOPMENT_TEAM = "";
+ "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 368M544MTT;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = NotificationServiceExtension/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = NotificationServiceExtension;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@executable_path/../../Frameworks",
+ );
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MARKETING_VERSION = 1.0;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
+ PRODUCT_BUNDLE_IDENTIFIER = com.expensify.chat.adhoc.NotificationServiceExtension;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "(NewApp) AdHoc: Notification Service";
+ SDKROOT = iphoneos;
+ SKIP_INSTALL = YES;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = DebugAdHoc;
+ };
+ 7FD73CA62B23CE9500420AF3 /* DebugProduction */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = C3FF914C045A138C061D306E /* Pods-NotificationServiceExtension.debugproduction.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "iPhone Distribution";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ CODE_SIGN_STYLE = Manual;
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 1;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ DEVELOPMENT_TEAM = "";
+ "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 368M544MTT;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = NotificationServiceExtension/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = NotificationServiceExtension;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@executable_path/../../Frameworks",
+ );
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MARKETING_VERSION = 1.0;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
+ PRODUCT_BUNDLE_IDENTIFIER = com.chat.expensify.chat.NotificationServiceExtension;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "(NewApp) Development: Notification Service";
+ SDKROOT = iphoneos;
+ SKIP_INSTALL = YES;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = DebugProduction;
+ };
+ 7FD73CA72B23CE9500420AF3 /* ReleaseDevelopment */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F082D95EE104912B48EA98BA /* Pods-NotificationServiceExtension.releasedevelopment.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "iPhone Distribution";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ CODE_SIGN_STYLE = Manual;
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 1;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ DEVELOPMENT_TEAM = "";
+ "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 368M544MTT;
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = NotificationServiceExtension/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = NotificationServiceExtension;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@executable_path/../../Frameworks",
+ );
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MARKETING_VERSION = 1.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
+ PRODUCT_BUNDLE_IDENTIFIER = com.expensify.chat.dev.NotificationServiceExtension;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "(NewApp) Development: Notification Service";
+ SDKROOT = iphoneos;
+ SKIP_INSTALL = YES;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = ReleaseDevelopment;
+ };
+ 7FD73CA82B23CE9500420AF3 /* ReleaseAdHoc */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = E61AD6D2DE65B6FB14945CDF /* Pods-NotificationServiceExtension.releaseadhoc.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "iPhone Distribution";
+ CODE_SIGN_STYLE = Manual;
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 1;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ DEVELOPMENT_TEAM = "";
+ "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 368M544MTT;
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = NotificationServiceExtension/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = NotificationServiceExtension;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@executable_path/../../Frameworks",
+ );
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MARKETING_VERSION = 1.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
+ PRODUCT_BUNDLE_IDENTIFIER = com.expensify.chat.adhoc.NotificationServiceExtension;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "(NewApp) AdHoc: Notification Service";
+ SDKROOT = iphoneos;
+ SKIP_INSTALL = YES;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = ReleaseAdHoc;
+ };
+ 7FD73CA92B23CE9500420AF3 /* ReleaseProduction */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 90E08F0C8C924EDA018C8866 /* Pods-NotificationServiceExtension.releaseproduction.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "iPhone Distribution";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
+ CODE_SIGN_STYLE = Manual;
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 1;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ DEVELOPMENT_TEAM = "";
+ "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 368M544MTT;
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = NotificationServiceExtension/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = NotificationServiceExtension;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@executable_path/../../Frameworks",
+ );
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MARKETING_VERSION = 1.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
+ PRODUCT_BUNDLE_IDENTIFIER = com.chat.expensify.chat.NotificationServiceExtension;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "(NewApp) AppStore: Notification Service";
+ SDKROOT = iphoneos;
+ SKIP_INSTALL = YES;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = ReleaseProduction;
+ };
83CBBA201A601CBA00E9B192 /* DebugDevelopment */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -1043,6 +1721,7 @@
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = 368M544MTT;
+ "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 368M544MTT;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "$(SRCROOT)/NewExpensify/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
@@ -1059,7 +1738,8 @@
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = com.chat.expensify.chat;
PRODUCT_NAME = "New Expensify";
- PROVISIONING_PROFILE_SPECIFIER = chat_expensify_appstore;
+ PROVISIONING_PROFILE_SPECIFIER = "(NewApp) AppStore";
+ "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "(NewApp) AppStore";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -1177,6 +1857,7 @@
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = 368M544MTT;
+ "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 368M544MTT;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "$(SRCROOT)/NewExpensify/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
@@ -1193,7 +1874,8 @@
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = com.expensify.chat.adhoc;
PRODUCT_NAME = "New Expensify AdHoc";
- PROVISIONING_PROFILE_SPECIFIER = chat_expensify_appstore;
+ PROVISIONING_PROFILE_SPECIFIER = "(NewApp) AdHoc";
+ "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "(NewApp) AdHoc";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -1305,6 +1987,7 @@
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = 368M544MTT;
+ "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 368M544MTT;
INFOPLIST_FILE = NewExpensify/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
@@ -1320,7 +2003,8 @@
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE -D EXPO_CONFIGURATION_DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = com.chat.expensify.chat;
PRODUCT_NAME = "New Expensify";
- PROVISIONING_PROFILE_SPECIFIER = chat_expensify_appstore;
+ PROVISIONING_PROFILE_SPECIFIER = "(NewApp) AppStore";
+ "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "(NewApp) AppStore";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
@@ -1429,6 +2113,7 @@
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = 368M544MTT;
+ "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 368M544MTT;
INFOPLIST_FILE = NewExpensify/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
@@ -1444,7 +2129,8 @@
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE -D EXPO_CONFIGURATION_DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = com.expensify.chat.adhoc;
PRODUCT_NAME = "New Expensify AdHoc";
- PROVISIONING_PROFILE_SPECIFIER = chat_expensify_appstore;
+ PROVISIONING_PROFILE_SPECIFIER = "(NewApp) AdHoc";
+ "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "(NewApp) AdHoc";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
@@ -1508,6 +2194,19 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = DebugDevelopment;
};
+ 7FD73CAA2B23CE9500420AF3 /* Build configuration list for PBXNativeTarget "NotificationServiceExtension" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 7FD73CA42B23CE9500420AF3 /* DebugDevelopment */,
+ 7FD73CA52B23CE9500420AF3 /* DebugAdHoc */,
+ 7FD73CA62B23CE9500420AF3 /* DebugProduction */,
+ 7FD73CA72B23CE9500420AF3 /* ReleaseDevelopment */,
+ 7FD73CA82B23CE9500420AF3 /* ReleaseAdHoc */,
+ 7FD73CA92B23CE9500420AF3 /* ReleaseProduction */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = DebugDevelopment;
+ };
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "NewExpensify" */ = {
isa = XCConfigurationList;
buildConfigurations = (
diff --git a/ios/NewExpensify/Chat.entitlements b/ios/NewExpensify/Chat.entitlements
index 5300e35eadbf..165745a3c1a0 100644
--- a/ios/NewExpensify/Chat.entitlements
+++ b/ios/NewExpensify/Chat.entitlements
@@ -14,5 +14,7 @@
applinks:staging.new.expensify.com
webcredentials:new.expensify.com
+ com.apple.developer.usernotifications.communication
+
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index c89aa0f1a00f..51de98b49720 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -19,7 +19,7 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 1.4.17
+ 1.4.18
CFBundleSignature
????
CFBundleURLTypes
@@ -40,7 +40,7 @@
CFBundleVersion
- 1.4.17.7
+ 1.4.18.3
ITSAppUsesNonExemptEncryption
LSApplicationQueriesSchemes
@@ -85,6 +85,10 @@
Your camera roll is used to store chat attachments.
NSPhotoLibraryUsageDescription
Your photos are used to create chat attachments.
+ NSUserActivityTypes
+
+ INSendMessageIntent
+
UIAppFonts
ExpensifyNewKansas-Medium.otf
diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist
index c7e17b965e17..c68c221f7add 100644
--- a/ios/NewExpensifyTests/Info.plist
+++ b/ios/NewExpensifyTests/Info.plist
@@ -15,10 +15,10 @@
CFBundlePackageType
BNDL
CFBundleShortVersionString
- 1.4.17
+ 1.4.18
CFBundleSignature
????
CFBundleVersion
- 1.4.17.7
+ 1.4.18.3
diff --git a/ios/NotificationServiceExtension/ExpError.swift b/ios/NotificationServiceExtension/ExpError.swift
new file mode 100644
index 000000000000..147618fb26d9
--- /dev/null
+++ b/ios/NotificationServiceExtension/ExpError.swift
@@ -0,0 +1,12 @@
+//
+// ExpError.swift
+// NotificationServiceExtension
+//
+// Created by Andrew Rosiclair on 12/13/23.
+//
+
+import Foundation
+
+enum ExpError: Error {
+ case runtimeError(String)
+}
diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist
new file mode 100644
index 000000000000..57421ebf9b75
--- /dev/null
+++ b/ios/NotificationServiceExtension/Info.plist
@@ -0,0 +1,13 @@
+
+
+
+
+ NSExtension
+
+ NSExtensionPointIdentifier
+ com.apple.usernotifications.service
+ NSExtensionPrincipalClass
+ $(PRODUCT_MODULE_NAME).NotificationService
+
+
+
diff --git a/ios/NotificationServiceExtension/NotificationService.swift b/ios/NotificationServiceExtension/NotificationService.swift
new file mode 100644
index 000000000000..c4eb01981bf2
--- /dev/null
+++ b/ios/NotificationServiceExtension/NotificationService.swift
@@ -0,0 +1,239 @@
+//
+// NotificationService.swift
+// NotificationServiceExtension
+//
+// Created by Andrew Rosiclair on 12/8/23.
+//
+
+import AirshipServiceExtension
+import os.log
+import Intents
+
+class NotificationService: UANotificationServiceExtension {
+
+ var contentHandler: ((UNNotificationContent) -> Void)?
+ var bestAttemptContent: UNMutableNotificationContent?
+ let log = OSLog(subsystem: Bundle.main.bundleIdentifier ?? "com.expensify.chat.dev.NotificationServiceExtension", category: "NotificationService")
+
+ override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
+ os_log("[NotificationService] didReceive() - received notification", log: log)
+
+ self.contentHandler = contentHandler
+ guard let bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) else {
+ contentHandler(request.content)
+ return
+ }
+
+ if #available(iOSApplicationExtension 15.0, *) {
+ configureCommunicationNotification(notificationContent: bestAttemptContent, contentHandler: contentHandler)
+ } else {
+ contentHandler(bestAttemptContent)
+ }
+ }
+
+ /**
+ * Parses the notification content and modifies it to be a Communication Notification. More info here: https://developer.apple.com/documentation/usernotifications/implementing_communication_notifications
+ */
+ @available(iOSApplicationExtension 15.0, *)
+ func configureCommunicationNotification(notificationContent: UNMutableNotificationContent, contentHandler: @escaping (UNNotificationContent) -> Void) {
+ var notificationData: NotificationData
+ do {
+ notificationData = try parsePayload(notificationContent: notificationContent)
+ } catch ExpError.runtimeError(let errorMessage) {
+ os_log("[NotificationService] configureCommunicationNotification() - couldn't parse the payload '%@'", log: log, type: .error, errorMessage)
+ contentHandler(notificationContent)
+ return
+ } catch {
+ os_log("[NotificationService] configureCommunicationNotification() - unexpected error while parsing payload", log: log, type: .error)
+ contentHandler(notificationContent)
+ return
+ }
+
+ // Create an intent for the incoming communication message
+ let intent: INSendMessageIntent = createMessageIntent(notificationData: notificationData)
+
+ // Use the intent to initialize the interaction.
+ let interaction = INInteraction(intent: intent, response: nil)
+
+
+ // Interaction direction is incoming because the user is
+ // receiving this message.
+ interaction.direction = .incoming
+
+
+ // Donate the interaction before updating notification content.
+ interaction.donate { error in
+ if error != nil {
+ os_log("[NotificationService] configureCommunicationNotification() - failed to donate the message intent", log: self.log, type: .error)
+ contentHandler(notificationContent)
+ return
+ }
+
+ // After donation, update the notification content.
+ do {
+ // Update notification content before displaying the
+ // communication notification.
+ let updatedContent = try notificationContent.updating(from: intent)
+
+ // Call the content handler with the updated content
+ // to display the communication notification.
+ contentHandler(updatedContent)
+ } catch {
+ os_log("[NotificationService] configureCommunicationNotification() - failed to update the notification with send message intent", log: self.log, type: .error)
+ contentHandler(notificationContent)
+ }
+ }
+ }
+
+ func parsePayload(notificationContent: UNMutableNotificationContent) throws -> NotificationData {
+ guard let payload = notificationContent.userInfo["payload"] as? NSDictionary else {
+ throw ExpError.runtimeError("payload missing")
+ }
+
+ guard let reportID = payload["reportID"] as? Int64 else {
+ throw ExpError.runtimeError("payload.reportID missing")
+ }
+
+ guard let reportActionID = payload["reportActionID"] as? String else {
+ throw ExpError.runtimeError("payload.reportActionID missing")
+ }
+
+ guard let onyxData = payload["onyxData"] as? NSArray else {
+ throw ExpError.runtimeError("payload.onyxData missing" + reportActionID)
+ }
+
+ guard let reportActionOnyxUpdate = onyxData[1] as? NSDictionary else {
+ throw ExpError.runtimeError("payload.onyxData[1] missing" + reportActionID)
+ }
+
+ guard let reportActionCollection = reportActionOnyxUpdate["value"] as? NSDictionary else {
+ throw ExpError.runtimeError("payload.onyxData[1].value (report action onyx update) missing" + reportActionID)
+ }
+
+ guard let reportAction = reportActionCollection[reportActionID] as? NSDictionary else {
+ throw ExpError.runtimeError("payload.onyxData[1].value['\(reportActionID)'] (report action) missing" + reportActionID)
+ }
+
+ guard let avatarURL = reportAction["avatar"] as? String else {
+ throw ExpError.runtimeError("reportAction.avatar missing. reportActionID: " + reportActionID)
+ }
+
+ guard let accountID = reportAction["actorAccountID"] as? Int else {
+ throw ExpError.runtimeError("reportAction.actorAccountID missing. reportActionID: " + reportActionID)
+ }
+
+ guard let person = reportAction["person"] as? NSArray else {
+ throw ExpError.runtimeError("reportAction.person missing. reportActionID: " + reportActionID)
+ }
+
+ guard let personObject = person[0] as? NSDictionary else {
+ throw ExpError.runtimeError("reportAction.person[0] missing. reportActionID: " + reportActionID)
+ }
+
+ guard let userName = personObject["text"] as? String else {
+ throw ExpError.runtimeError("reportAction.person[0].text missing. reportActionID: " + reportActionID)
+ }
+
+ return NotificationData(
+ reportID: reportID,
+ reportActionID: reportActionID,
+ avatarURL: avatarURL,
+ accountID: accountID,
+ userName: userName,
+ title: notificationContent.title,
+ messageText: notificationContent.body,
+ roomName: payload["roomName"] as? String
+ )
+ }
+
+ @available(iOSApplicationExtension 14.0, *)
+ func createMessageIntent(notificationData: NotificationData) -> INSendMessageIntent {
+ // Initialize only the sender for a one-to-one message intent.
+ let handle = INPersonHandle(value: String(notificationData.accountID), type: .unknown)
+ let avatar = fetchINImage(imageURL: notificationData.avatarURL, reportActionID: notificationData.reportActionID)
+ let sender = INPerson(personHandle: handle,
+ nameComponents: nil,
+ displayName: notificationData.userName,
+ image: avatar,
+ contactIdentifier: nil,
+ customIdentifier: nil)
+
+ // Configure the group/room name if there is one
+ var speakableGroupName: INSpeakableString? = nil
+ var recipients: [INPerson]? = nil
+ if (notificationData.roomName != nil) {
+ speakableGroupName = INSpeakableString(spokenPhrase: notificationData.roomName ?? "")
+
+ // To add the group name subtitle there must be multiple recipients set. However, we do not have
+ // data on the participatns in the room/group chat so we just add a placeholder here. This shouldn't
+ // appear anywhere in the UI
+ let placeholderPerson = INPerson(personHandle: INPersonHandle(value: "placeholder", type: .unknown),
+ nameComponents: nil,
+ displayName: "placeholder",
+ image: nil,
+ contactIdentifier: nil,
+ customIdentifier: nil)
+ recipients = [sender, placeholderPerson]
+ }
+
+ // Because this communication is incoming, you can infer that the current user is
+ // a recipient. Don't include the current user when initializing the intent.
+ let intent = INSendMessageIntent(recipients: recipients,
+ outgoingMessageType: .outgoingMessageText,
+ content: notificationData.messageText,
+ speakableGroupName: speakableGroupName,
+ conversationIdentifier: String(notificationData.reportID),
+ serviceName: nil,
+ sender: sender,
+ attachments: nil)
+
+ // If the group name is set, we force the avatar to just be the sender's avatar
+ intent.setImage(avatar, forParameterNamed: \.speakableGroupName)
+
+ return intent
+ }
+
+ override func serviceExtensionTimeWillExpire() {
+ // Called just before the extension will be terminated by the system.
+ // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
+ if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
+ contentHandler(bestAttemptContent)
+ }
+ }
+
+ func fetchINImage(imageURL: String, reportActionID: String) -> INImage? {
+ guard let url = URL(string: imageURL) else {
+ return nil
+ }
+
+ do {
+ let data = try Data(contentsOf: url)
+ return INImage(imageData: data)
+ } catch {
+ os_log("[NotificationService] fetchINImage() - failed to fetch avatar. reportActionID: %@", log: self.log, type: .error, reportActionID)
+ return nil
+ }
+ }
+}
+
+class NotificationData {
+ public var reportID: Int64
+ public var reportActionID: String
+ public var avatarURL: String
+ public var accountID: Int
+ public var userName: String
+ public var title: String
+ public var messageText: String
+ public var roomName: String?
+
+ public init (reportID: Int64, reportActionID: String, avatarURL: String, accountID: Int, userName: String, title: String, messageText: String, roomName: String?) {
+ self.reportID = reportID
+ self.reportActionID = reportActionID
+ self.avatarURL = avatarURL
+ self.accountID = accountID
+ self.userName = userName
+ self.title = title
+ self.messageText = messageText
+ self.roomName = roomName
+ }
+}
diff --git a/ios/Podfile b/ios/Podfile
index 307e2c46108c..c12596d3191e 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -129,3 +129,7 @@ target 'NewExpensify' do
end
end
end
+
+target 'NotificationServiceExtension' do
+ pod 'AirshipServiceExtension'
+end
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index a19ea5b77df0..318d62f0a944 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -20,6 +20,7 @@ PODS:
- Airship (= 16.12.1)
- Airship/MessageCenter (= 16.12.1)
- Airship/PreferenceCenter (= 16.12.1)
+ - AirshipServiceExtension (16.12.5)
- AppAuth (1.6.2):
- AppAuth/Core (= 1.6.2)
- AppAuth/ExternalUserAgent (= 1.6.2)
@@ -857,6 +858,7 @@ PODS:
- Yoga (~> 1.14)
DEPENDENCIES:
+ - AirshipServiceExtension
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
- BVLinearGradient (from `../node_modules/react-native-linear-gradient`)
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
@@ -974,6 +976,7 @@ SPEC REPOS:
trunk:
- Airship
- AirshipFrameworkProxy
+ - AirshipServiceExtension
- AppAuth
- CocoaAsyncSocket
- Firebase
@@ -1205,6 +1208,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
Airship: 2f4510b497a8200780752a5e0304a9072bfffb6d
AirshipFrameworkProxy: ea1b6c665c798637b93c465b5e505be3011f1d9d
+ AirshipServiceExtension: 89c6e25a69f3458d9dbd581c700cffb196b61930
AppAuth: 3bb1d1cd9340bd09f5ed189fb00b1cc28e1e8570
boost: 57d2868c099736d80fcd648bf211b4431e51a558
BVLinearGradient: 421743791a59d259aec53f4c58793aad031da2ca
@@ -1342,6 +1346,6 @@ SPEC CHECKSUMS:
Yoga: 3efc43e0d48686ce2e8c60f99d4e6bd349aff981
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
-PODFILE CHECKSUM: bfe134fd8d3bb1d9232f59a7601623c7193bca3e
+PODFILE CHECKSUM: c403784ee1fdf281bcc552696504207f3022cb66
COCOAPODS: 1.12.1
diff --git a/ios/chat_expensify_appstore.mobileprovision.gpg b/ios/chat_expensify_appstore.mobileprovision.gpg
deleted file mode 100644
index 246f5f0ec99e..000000000000
Binary files a/ios/chat_expensify_appstore.mobileprovision.gpg and /dev/null differ
diff --git a/ios/expensify_chat_adhoc.mobileprovision.gpg b/ios/expensify_chat_adhoc.mobileprovision.gpg
deleted file mode 100644
index f4691df10d67..000000000000
Binary files a/ios/expensify_chat_adhoc.mobileprovision.gpg and /dev/null differ
diff --git a/package-lock.json b/package-lock.json
index af50c41dcb0a..bcf1b3650227 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "new.expensify",
- "version": "1.4.17-7",
+ "version": "1.4.18-3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "new.expensify",
- "version": "1.4.17-7",
+ "version": "1.4.18-3",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@@ -199,7 +199,7 @@
"electron-builder": "24.6.4",
"eslint": "^7.6.0",
"eslint-config-airbnb-typescript": "^17.1.0",
- "eslint-config-expensify": "^2.0.42",
+ "eslint-config-expensify": "^2.0.43",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-jest": "^24.1.0",
"eslint-plugin-jsdoc": "^46.2.6",
@@ -29995,9 +29995,9 @@
}
},
"node_modules/eslint-config-expensify": {
- "version": "2.0.42",
- "resolved": "https://registry.npmjs.org/eslint-config-expensify/-/eslint-config-expensify-2.0.42.tgz",
- "integrity": "sha512-TNwbfIGjOp4EjT6HKEpp10mr6dkBNCNMTeMmpuQyS0Nqv1tRGJltoU3GFmUHJywrLkEmu21iC0NNMmoJ1XzmLg==",
+ "version": "2.0.43",
+ "resolved": "https://registry.npmjs.org/eslint-config-expensify/-/eslint-config-expensify-2.0.43.tgz",
+ "integrity": "sha512-kLd6NyYbyb3mCB6VH6vu49/RllwNo0rdXcLUUGB7JGny+2N19jOmBJ4/GLKsbpFzvEZEghXfn7BITPRkxVJcgg==",
"dev": true,
"dependencies": {
"@lwc/eslint-plugin-lwc": "^0.11.0",
@@ -78014,9 +78014,9 @@
}
},
"eslint-config-expensify": {
- "version": "2.0.42",
- "resolved": "https://registry.npmjs.org/eslint-config-expensify/-/eslint-config-expensify-2.0.42.tgz",
- "integrity": "sha512-TNwbfIGjOp4EjT6HKEpp10mr6dkBNCNMTeMmpuQyS0Nqv1tRGJltoU3GFmUHJywrLkEmu21iC0NNMmoJ1XzmLg==",
+ "version": "2.0.43",
+ "resolved": "https://registry.npmjs.org/eslint-config-expensify/-/eslint-config-expensify-2.0.43.tgz",
+ "integrity": "sha512-kLd6NyYbyb3mCB6VH6vu49/RllwNo0rdXcLUUGB7JGny+2N19jOmBJ4/GLKsbpFzvEZEghXfn7BITPRkxVJcgg==",
"dev": true,
"requires": {
"@lwc/eslint-plugin-lwc": "^0.11.0",
diff --git a/package.json b/package.json
index f48b1e0fda2e..3f2029118438 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "1.4.17-7",
+ "version": "1.4.18-3",
"author": "Expensify, Inc.",
"homepage": "https://new.expensify.com",
"description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
@@ -247,7 +247,7 @@
"electron-builder": "24.6.4",
"eslint": "^7.6.0",
"eslint-config-airbnb-typescript": "^17.1.0",
- "eslint-config-expensify": "^2.0.42",
+ "eslint-config-expensify": "^2.0.43",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-jest": "^24.1.0",
"eslint-plugin-jsdoc": "^46.2.6",
diff --git a/src/CONST.ts b/src/CONST.ts
index 812fb4d5335d..34720adf8a21 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -549,6 +549,7 @@ const CONST = {
INDIVIDUAL_BUDGET_NOTIFICATION: 'POLICYCHANGELOG_INDIVIDUAL_BUDGET_NOTIFICATION',
INVITE_TO_ROOM: 'POLICYCHANGELOG_INVITETOROOM',
REMOVE_FROM_ROOM: 'POLICYCHANGELOG_REMOVEFROMROOM',
+ REPLACE_CATEGORIES: 'POLICYCHANGELOG_REPLACE_CATEGORIES',
SET_AUTOREIMBURSEMENT: 'POLICYCHANGELOG_SET_AUTOREIMBURSEMENT',
SET_AUTO_JOIN: 'POLICYCHANGELOG_SET_AUTO_JOIN',
SET_CATEGORY_NAME: 'POLICYCHANGELOG_SET_CATEGORY_NAME',
@@ -820,6 +821,7 @@ const CONST = {
MAX_PENDING_TIME_MS: 10 * 1000,
MAX_REQUEST_RETRIES: 10,
},
+ WEEK_STARTS_ON: 1, // Monday
DEFAULT_TIME_ZONE: {automatic: true, selected: 'America/Los_Angeles'},
DEFAULT_ACCOUNT_DATA: {errors: null, success: '', isLoading: false},
DEFAULT_CLOSE_ACCOUNT_DATA: {errors: null, success: '', isLoading: false},
@@ -1296,6 +1298,12 @@ const CONST = {
USE_EXPENSIFY_FEES: 'use.expensify.com/fees',
},
+ LAYOUT_WIDTH: {
+ WIDE: 'wide',
+ NARROW: 'narrow',
+ NONE: 'none',
+ },
+
ICON_TYPE_ICON: 'icon',
ICON_TYPE_AVATAR: 'avatar',
ICON_TYPE_WORKSPACE: 'workspace',
diff --git a/src/components/ConfirmContent.js b/src/components/ConfirmContent.js
deleted file mode 100644
index 13685c7392bb..000000000000
--- a/src/components/ConfirmContent.js
+++ /dev/null
@@ -1,173 +0,0 @@
-import PropTypes from 'prop-types';
-import React from 'react';
-import {View} from 'react-native';
-import _ from 'underscore';
-import useLocalize from '@hooks/useLocalize';
-import useNetwork from '@hooks/useNetwork';
-import useTheme from '@hooks/useTheme';
-import useThemeStyles from '@hooks/useThemeStyles';
-import variables from '@styles/variables';
-import Button from './Button';
-import Header from './Header';
-import Icon from './Icon';
-import sourcePropTypes from './Image/sourcePropTypes';
-import Text from './Text';
-
-const propTypes = {
- /** Title of the modal */
- title: PropTypes.string.isRequired,
-
- /** A callback to call when the form has been submitted */
- onConfirm: PropTypes.func.isRequired,
-
- /** A callback to call when the form has been closed */
- onCancel: PropTypes.func,
-
- /** Confirm button text */
- confirmText: PropTypes.string,
-
- /** Cancel button text */
- cancelText: PropTypes.string,
-
- /** Modal content text/element */
- prompt: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
-
- /** Whether we should use the success button color */
- success: PropTypes.bool,
-
- /** Whether we should use the danger button color. Use if the action is destructive */
- danger: PropTypes.bool,
-
- /** Whether we should disable the confirm button when offline */
- shouldDisableConfirmButtonWhenOffline: PropTypes.bool,
-
- /** Whether we should show the cancel button */
- shouldShowCancelButton: PropTypes.bool,
-
- /** Icon to display above the title */
- iconSource: PropTypes.oneOfType([PropTypes.string, sourcePropTypes]),
-
- /** Whether to center the icon / text content */
- shouldCenterContent: PropTypes.bool,
-
- /** Whether to stack the buttons */
- shouldStackButtons: PropTypes.bool,
-
- /** Styles for title */
- // eslint-disable-next-line react/forbid-prop-types
- titleStyles: PropTypes.arrayOf(PropTypes.object),
-
- /** Styles for prompt */
- // eslint-disable-next-line react/forbid-prop-types
- promptStyles: PropTypes.arrayOf(PropTypes.object),
-
- /** Styles for view */
- // eslint-disable-next-line react/forbid-prop-types
- contentStyles: PropTypes.arrayOf(PropTypes.object),
-
- /** Styles for icon */
- // eslint-disable-next-line react/forbid-prop-types
- iconAdditionalStyles: PropTypes.arrayOf(PropTypes.object),
-};
-
-const defaultProps = {
- confirmText: '',
- cancelText: '',
- prompt: '',
- success: true,
- danger: false,
- onCancel: () => {},
- shouldDisableConfirmButtonWhenOffline: false,
- shouldShowCancelButton: true,
- contentStyles: [],
- iconSource: null,
- shouldCenterContent: false,
- shouldStackButtons: true,
- titleStyles: [],
- promptStyles: [],
- iconAdditionalStyles: [],
-};
-
-function ConfirmContent(props) {
- const styles = useThemeStyles();
- const theme = useTheme();
- const {translate} = useLocalize();
- const {isOffline} = useNetwork();
-
- const isCentered = props.shouldCenterContent;
-
- return (
-
-
- {!_.isEmpty(props.iconSource) ||
- (_.isFunction(props.iconSource) && (
-
-
-
- ))}
-
-
-
-
-
- {_.isString(props.prompt) ? {props.prompt} : props.prompt}
-
-
- {props.shouldStackButtons ? (
- <>
-
- {props.shouldShowCancelButton && (
-
- )}
- >
- ) : (
-
- {props.shouldShowCancelButton && (
-
- )}
-
-
- )}
-
- );
-}
-
-ConfirmContent.propTypes = propTypes;
-ConfirmContent.defaultProps = defaultProps;
-ConfirmContent.displayName = 'ConfirmContent';
-export default ConfirmContent;
diff --git a/src/components/ConfirmContent.tsx b/src/components/ConfirmContent.tsx
new file mode 100644
index 000000000000..9e4ffa8720da
--- /dev/null
+++ b/src/components/ConfirmContent.tsx
@@ -0,0 +1,163 @@
+import React, {ReactNode} from 'react';
+import {StyleProp, TextStyle, View, ViewStyle} from 'react-native';
+import useLocalize from '@hooks/useLocalize';
+import useNetwork from '@hooks/useNetwork';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
+import variables from '@styles/variables';
+import IconAsset from '@src/types/utils/IconAsset';
+import Button from './Button';
+import Header from './Header';
+import Icon from './Icon';
+import Text from './Text';
+
+type ConfirmContentProps = {
+ /** Title of the modal */
+ title: string;
+
+ /** A callback to call when the form has been submitted */
+ onConfirm: () => void;
+
+ /** A callback to call when the form has been closed */
+ onCancel?: () => void;
+
+ /** Confirm button text */
+ confirmText?: string;
+
+ /** Cancel button text */
+ cancelText?: string;
+
+ /** Modal content text/element */
+ prompt?: string | ReactNode;
+
+ /** Whether we should use the success button color */
+ success?: boolean;
+
+ /** Whether we should use the danger button color. Use if the action is destructive */
+ danger?: boolean;
+
+ /** Whether we should disable the confirm button when offline */
+ shouldDisableConfirmButtonWhenOffline?: boolean;
+
+ /** Whether we should show the cancel button */
+ shouldShowCancelButton?: boolean;
+
+ /** Icon to display above the title */
+ iconSource?: IconAsset;
+
+ /** Whether to center the icon / text content */
+ shouldCenterContent?: boolean;
+
+ /** Whether to stack the buttons */
+ shouldStackButtons?: boolean;
+
+ /** Styles for title */
+ titleStyles?: StyleProp;
+
+ /** Styles for prompt */
+ promptStyles?: StyleProp;
+
+ /** Styles for view */
+ contentStyles?: StyleProp;
+
+ /** Styles for icon */
+ iconAdditionalStyles?: StyleProp;
+};
+
+function ConfirmContent({
+ title,
+ onConfirm,
+ onCancel = () => {},
+ confirmText = '',
+ cancelText = '',
+ prompt = '',
+ success = true,
+ danger = false,
+ shouldDisableConfirmButtonWhenOffline = false,
+ shouldShowCancelButton = false,
+ iconSource,
+ shouldCenterContent = false,
+ shouldStackButtons = true,
+ titleStyles,
+ promptStyles,
+ contentStyles,
+ iconAdditionalStyles,
+}: ConfirmContentProps) {
+ const styles = useThemeStyles();
+ const {translate} = useLocalize();
+ const theme = useTheme();
+ const {isOffline} = useNetwork();
+
+ const isCentered = shouldCenterContent;
+
+ return (
+
+
+ {typeof iconSource === 'function' && (
+
+
+
+ )}
+
+
+
+ {typeof prompt === 'string' ? {prompt} : prompt}
+
+
+ {shouldStackButtons ? (
+ <>
+
+ {shouldShowCancelButton && (
+
+ )}
+ >
+ ) : (
+
+ {shouldShowCancelButton && (
+
+ )}
+
+
+ )}
+
+ );
+}
+
+ConfirmContent.displayName = 'ConfirmContent';
+
+export default ConfirmContent;
diff --git a/src/components/DatePicker/CalendarPicker/generateMonthMatrix.js b/src/components/DatePicker/CalendarPicker/generateMonthMatrix.js
index a3497654feec..ecf338d36424 100644
--- a/src/components/DatePicker/CalendarPicker/generateMonthMatrix.js
+++ b/src/components/DatePicker/CalendarPicker/generateMonthMatrix.js
@@ -1,4 +1,5 @@
import {addDays, format, getDay, getDaysInMonth, startOfMonth} from 'date-fns';
+import DateUtils from '@libs/DateUtils';
/**
* Generates a matrix representation of a month's calendar given the year and month.
@@ -24,6 +25,9 @@ export default function generateMonthMatrix(year, month) {
throw new Error('Month cannot be greater than 11');
}
+ // Get the week day for the end of week
+ const weekEndsOn = DateUtils.getWeekEndsOn();
+
// Get the number of days in the month and the first day of the month
const firstDayOfMonth = startOfMonth(new Date(year, month, 1));
const daysInMonth = getDaysInMonth(firstDayOfMonth);
@@ -32,18 +36,13 @@ export default function generateMonthMatrix(year, month) {
const matrix = [];
let currentWeek = [];
- // Add null values for days before the first day of the month
- for (let i = 0; i < getDay(firstDayOfMonth); i++) {
- currentWeek.push(null);
- }
-
// Add calendar days to the matrix
for (let i = 1; i <= daysInMonth; i++) {
const currentDate = addDays(firstDayOfMonth, i - 1);
currentWeek.push(Number(format(currentDate, 'd')));
// Start a new row when the current week is full
- if (getDay(currentDate) === 6) {
+ if (getDay(currentDate) === weekEndsOn) {
matrix.push(currentWeek);
currentWeek = [];
}
@@ -56,5 +55,11 @@ export default function generateMonthMatrix(year, month) {
}
matrix.push(currentWeek);
}
+
+ // Add null values for days before the first day of the month
+ for (let i = matrix[0].length; i < 7; i++) {
+ matrix[0].unshift(null);
+ }
+
return matrix;
}
diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js
index 05016a25394a..f60377c842ea 100644
--- a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js
+++ b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js
@@ -45,8 +45,8 @@ function ImageRenderer(props) {
const previewSource = tryResolveUrlFromApiRoot(htmlAttribs.src);
const source = tryResolveUrlFromApiRoot(isAttachmentOrReceipt ? htmlAttribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE] : htmlAttribs.src);
- const imageWidth = htmlAttribs['data-expensify-width'] ? parseInt(htmlAttribs['data-expensify-width'], 10) : undefined;
- const imageHeight = htmlAttribs['data-expensify-height'] ? parseInt(htmlAttribs['data-expensify-height'], 10) : undefined;
+ const imageWidth = (htmlAttribs['data-expensify-width'] && parseInt(htmlAttribs['data-expensify-width'], 10)) || undefined;
+ const imageHeight = (htmlAttribs['data-expensify-height'] && parseInt(htmlAttribs['data-expensify-height'], 10)) || undefined;
const imagePreviewModalDisabled = htmlAttribs['data-expensify-preview-modal-disabled'] === 'true';
return imagePreviewModalDisabled ? (
diff --git a/src/components/Icon/Illustrations.ts b/src/components/Icon/Illustrations.ts
index c8a14c7aba03..b0690211d6c5 100644
--- a/src/components/Icon/Illustrations.ts
+++ b/src/components/Icon/Illustrations.ts
@@ -27,6 +27,7 @@ import TadaBlue from '@assets/images/product-illustrations/tada--blue.svg';
import TadaYellow from '@assets/images/product-illustrations/tada--yellow.svg';
import ToddBehindCloud from '@assets/images/product-illustrations/todd-behind-cloud.svg';
import BankArrow from '@assets/images/simple-illustrations/simple-illustration__bank-arrow.svg';
+import BigRocket from '@assets/images/simple-illustrations/simple-illustration__bigrocket.svg';
import PinkBill from '@assets/images/simple-illustrations/simple-illustration__bill.svg';
import ChatBubbles from '@assets/images/simple-illustrations/simple-illustration__chatbubbles.svg';
import CoffeeMug from '@assets/images/simple-illustrations/simple-illustration__coffeemug.svg';
@@ -34,11 +35,13 @@ import ConciergeBubble from '@assets/images/simple-illustrations/simple-illustra
import ConciergeNew from '@assets/images/simple-illustrations/simple-illustration__concierge.svg';
import CreditCardsNew from '@assets/images/simple-illustrations/simple-illustration__credit-cards.svg';
import EmailAddress from '@assets/images/simple-illustrations/simple-illustration__email-address.svg';
+import HandCard from '@assets/images/simple-illustrations/simple-illustration__handcard.svg';
import HandEarth from '@assets/images/simple-illustrations/simple-illustration__handearth.svg';
import HotDogStand from '@assets/images/simple-illustrations/simple-illustration__hotdogstand.svg';
import InvoiceBlue from '@assets/images/simple-illustrations/simple-illustration__invoice.svg';
import LockOpen from '@assets/images/simple-illustrations/simple-illustration__lockopen.svg';
import Luggage from '@assets/images/simple-illustrations/simple-illustration__luggage.svg';
+import Mailbox from '@assets/images/simple-illustrations/simple-illustration__mailbox.svg';
import MoneyReceipts from '@assets/images/simple-illustrations/simple-illustration__money-receipts.svg';
import MoneyBadge from '@assets/images/simple-illustrations/simple-illustration__moneybadge.svg';
import MoneyIntoWallet from '@assets/images/simple-illustrations/simple-illustration__moneyintowallet.svg';
@@ -46,6 +49,7 @@ import MoneyWings from '@assets/images/simple-illustrations/simple-illustration_
import OpenSafe from '@assets/images/simple-illustrations/simple-illustration__opensafe.svg';
import SanFrancisco from '@assets/images/simple-illustrations/simple-illustration__sanfrancisco.svg';
import ShieldYellow from '@assets/images/simple-illustrations/simple-illustration__shield.svg';
+import SmallRocket from '@assets/images/simple-illustrations/simple-illustration__smallrocket.svg';
import ThumbsUpStars from '@assets/images/simple-illustrations/simple-illustration__thumbsupstars.svg';
import TrackShoe from '@assets/images/simple-illustrations/simple-illustration__track-shoe.svg';
import TreasureChest from '@assets/images/simple-illustrations/simple-illustration__treasurechest.svg';
@@ -55,12 +59,14 @@ export {
BankArrowPink,
BankMouseGreen,
BankUserGreen,
+ BigRocket,
ChatBubbles,
CoffeeMug,
ConciergeBlue,
ConciergeExclamation,
CreditCardsBlue,
EmailAddress,
+ HandCard,
HotDogStand,
InvoiceOrange,
JewelBoxBlue,
@@ -69,6 +75,7 @@ export {
JewelBoxPink,
JewelBoxYellow,
MagicCode,
+ Mailbox,
MoneyEnvelopeBlue,
MoneyMousePink,
ReceiptsSearchYellow,
@@ -77,6 +84,7 @@ export {
RocketOrange,
SanFrancisco,
SafeBlue,
+ SmallRocket,
TadaYellow,
TadaBlue,
ToddBehindCloud,
diff --git a/src/components/MentionSuggestions.tsx b/src/components/MentionSuggestions.tsx
index 72dfd177dd1e..a20cdcff4e10 100644
--- a/src/components/MentionSuggestions.tsx
+++ b/src/components/MentionSuggestions.tsx
@@ -100,7 +100,7 @@ function MentionSuggestions({prefix, mentions, highlightedMentionIndex = 0, onSe
{text}
@@ -119,7 +119,7 @@ function MentionSuggestions({prefix, mentions, highlightedMentionIndex = 0, onSe
styles.flexShrink1,
styles.flex1,
styles.mentionSuggestionsDisplayName,
- styles.mentionSuggestionsHandle,
+ styles.textSupporting,
theme.success,
StyleUtils,
],
diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx
index 89640c56f5ef..e03c32f6bfcc 100644
--- a/src/components/Modal/BaseModal.tsx
+++ b/src/components/Modal/BaseModal.tsx
@@ -181,7 +181,7 @@ function BaseModal(
onModalHide={hideModal}
onModalWillShow={() => ComposerFocusManager.resetReadyToFocus()}
onDismiss={handleDismissModal}
- onSwipeComplete={onClose}
+ onSwipeComplete={() => onClose?.()}
swipeDirection={swipeDirection}
isVisible={isVisible}
backdropColor={theme.overlay}
diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts
index 172965b45fb2..461a5935eda9 100644
--- a/src/components/Modal/types.ts
+++ b/src/components/Modal/types.ts
@@ -23,7 +23,7 @@ type BaseModalProps = WindowDimensionsProps &
shouldSetModalVisibility?: boolean;
/** Callback method fired when the user requests to close the modal */
- onClose: () => void;
+ onClose: (ref?: React.RefObject) => void;
/** State that determines whether to display the modal or not */
isVisible: boolean;
diff --git a/src/components/OptionRow.tsx b/src/components/OptionRow.tsx
index 41b48e702c56..5a2f6902c4c0 100644
--- a/src/components/OptionRow.tsx
+++ b/src/components/OptionRow.tsx
@@ -162,7 +162,7 @@ function OptionRow({
{(hovered) => (
{
if (!onSelectRow) {
diff --git a/src/components/OptionsList/BaseOptionsList.js b/src/components/OptionsList/BaseOptionsList.js
index 81c0d99c20dd..bd3695eb7aa9 100644
--- a/src/components/OptionsList/BaseOptionsList.js
+++ b/src/components/OptionsList/BaseOptionsList.js
@@ -291,6 +291,7 @@ function BaseOptionsList({
onViewableItemsChanged={onViewableItemsChanged}
bounces={bounces}
ListFooterComponent={renderFooterContent}
+ testID="options-list"
/>
>
)}
diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js
index 3c40b3cf1144..792073b72613 100755
--- a/src/components/OptionsSelector/BaseOptionsSelector.js
+++ b/src/components/OptionsSelector/BaseOptionsSelector.js
@@ -303,7 +303,7 @@ class BaseOptionsSelector extends Component {
}
selectFocusedOption(e) {
- const focusedItemKey = lodashGet(e, ['target', 'attributes', 'data-testid', 'value']);
+ const focusedItemKey = lodashGet(e, ['target', 'attributes', 'id', 'value']);
const focusedOption = focusedItemKey ? _.find(this.state.allOptions, (option) => option.keyForList === focusedItemKey) : this.state.allOptions[this.state.focusedIndex];
if (!focusedOption || !this.props.isFocused) {
@@ -492,6 +492,7 @@ class BaseOptionsSelector extends Component {
spellCheck={false}
shouldInterceptSwipe={this.props.shouldTextInputInterceptSwipe}
isLoading={this.props.isLoadingNewOptions}
+ testID="options-selector-input"
/>
);
const optionsList = (
diff --git a/src/components/PopoverMenu/index.js b/src/components/PopoverMenu/index.js
index 9cf890410017..597105173b4c 100644
--- a/src/components/PopoverMenu/index.js
+++ b/src/components/PopoverMenu/index.js
@@ -18,21 +18,9 @@ const propTypes = {
...createMenuPropTypes,
...windowDimensionsPropTypes,
- /** The horizontal and vertical anchors points for the popover */
- anchorPosition: PropTypes.shape({
- horizontal: PropTypes.number.isRequired,
- vertical: PropTypes.number.isRequired,
- }).isRequired,
-
/** Ref of the anchor */
anchorRef: refPropTypes,
- /** Where the popover should be positioned relative to the anchor points. */
- anchorAlignment: PropTypes.shape({
- horizontal: PropTypes.oneOf(_.values(CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL)),
- vertical: PropTypes.oneOf(_.values(CONST.MODAL.ANCHOR_ORIGIN_VERTICAL)),
- }),
-
withoutOverlay: PropTypes.bool,
/** Should we announce the Modal visibility changes? */
diff --git a/src/components/PopoverMenu/popoverMenuPropTypes.js b/src/components/PopoverMenu/popoverMenuPropTypes.js
index 092c2f2703c8..53eeb63b05e7 100644
--- a/src/components/PopoverMenu/popoverMenuPropTypes.js
+++ b/src/components/PopoverMenu/popoverMenuPropTypes.js
@@ -1,4 +1,5 @@
import PropTypes from 'prop-types';
+import _ from 'underscore';
import sourcePropTypes from '@components/Image/sourcePropTypes';
import CONST from '@src/CONST';
@@ -34,6 +35,12 @@ const propTypes = {
left: PropTypes.number,
}).isRequired,
+ /** Where the popover should be positioned relative to the anchor points. */
+ anchorAlignment: PropTypes.shape({
+ horizontal: PropTypes.oneOf(_.values(CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL)),
+ vertical: PropTypes.oneOf(_.values(CONST.MODAL.ANCHOR_ORIGIN_VERTICAL)),
+ }),
+
/** The anchor reference of the CreateMenu popover */
anchorRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]).isRequired,
diff --git a/src/components/PopoverWithoutOverlay/index.js b/src/components/PopoverWithoutOverlay/index.tsx
similarity index 67%
rename from src/components/PopoverWithoutOverlay/index.js
rename to src/components/PopoverWithoutOverlay/index.tsx
index 43ca1c0de818..f83949bcbe9d 100644
--- a/src/components/PopoverWithoutOverlay/index.js
+++ b/src/components/PopoverWithoutOverlay/index.tsx
@@ -1,32 +1,74 @@
-import React, {useMemo} from 'react';
+import React, {ForwardedRef, forwardRef, useContext, useEffect, useMemo} from 'react';
import {View} from 'react-native';
import ColorSchemeWrapper from '@components/ColorSchemeWrapper';
-import {defaultProps, propTypes} from '@components/Popover/popoverPropTypes';
import {PopoverContext} from '@components/PopoverProvider';
-import withWindowDimensions from '@components/withWindowDimensions';
import useSafeAreaInsets from '@hooks/useSafeAreaInsets';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';
+import useWindowDimensions from '@hooks/useWindowDimensions';
import * as Modal from '@userActions/Modal';
+import PopoverWithoutOverlayProps from './types';
-function Popover(props) {
+function PopoverWithoutOverlay(
+ {
+ anchorPosition = {},
+ anchorRef,
+ withoutOverlayRef,
+ innerContainerStyle = {},
+ outerStyle,
+ onModalShow = () => {},
+ isVisible,
+ onClose,
+ onModalHide = () => {},
+ children,
+ }: PopoverWithoutOverlayProps,
+ ref: ForwardedRef,
+) {
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
- const {onOpen, close} = React.useContext(PopoverContext);
+ const {onOpen, close} = useContext(PopoverContext);
+ const {windowWidth, windowHeight} = useWindowDimensions();
const insets = useSafeAreaInsets();
const {modalStyle, modalContainerStyle, shouldAddTopSafeAreaMargin, shouldAddBottomSafeAreaMargin, shouldAddTopSafeAreaPadding, shouldAddBottomSafeAreaPadding} =
StyleUtils.getModalStyles(
'popover',
{
- windowWidth: props.windowWidth,
- windowHeight: props.windowHeight,
+ windowWidth,
+ windowHeight,
isSmallScreenWidth: false,
},
- props.anchorPosition,
- props.innerContainerStyle,
- props.outerStyle,
+ anchorPosition,
+ innerContainerStyle,
+ outerStyle,
);
+ useEffect(() => {
+ let removeOnClose: () => void;
+ if (isVisible) {
+ onModalShow();
+ onOpen?.({
+ ref: withoutOverlayRef,
+ close: onClose,
+ anchorRef,
+ });
+ removeOnClose = Modal.setCloseModal(() => onClose(anchorRef));
+ } else {
+ onModalHide();
+ close(anchorRef);
+ Modal.onModalDidClose();
+ }
+ Modal.willAlertModalBecomeVisible(isVisible);
+
+ return () => {
+ if (!removeOnClose) {
+ return;
+ }
+ removeOnClose();
+ };
+ // We want this effect to run strictly ONLY when isVisible prop changes
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [isVisible]);
+
const {
paddingTop: safeAreaPaddingTop,
paddingBottom: safeAreaPaddingBottom,
@@ -69,41 +111,14 @@ function Popover(props) {
],
);
- React.useEffect(() => {
- let removeOnClose;
- if (props.isVisible) {
- props.onModalShow();
- onOpen({
- ref: props.withoutOverlayRef,
- close: props.onClose,
- anchorRef: props.anchorRef,
- });
- removeOnClose = Modal.setCloseModal(() => props.onClose(props.anchorRef));
- } else {
- props.onModalHide();
- close(props.anchorRef);
- Modal.onModalDidClose();
- }
- Modal.willAlertModalBecomeVisible(props.isVisible);
-
- return () => {
- if (!removeOnClose) {
- return;
- }
- removeOnClose();
- };
- // We want this effect to run strictly ONLY when isVisible prop changes
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [props.isVisible]);
-
- if (!props.isVisible) {
+ if (!isVisible) {
return null;
}
return (
- {props.children}
+ {children}
);
}
-Popover.propTypes = propTypes;
-Popover.defaultProps = defaultProps;
-Popover.displayName = 'Popover';
+PopoverWithoutOverlay.displayName = 'PopoverWithoutOverlay';
-export default withWindowDimensions(Popover);
+export default forwardRef(PopoverWithoutOverlay);
diff --git a/src/components/PopoverWithoutOverlay/types.ts b/src/components/PopoverWithoutOverlay/types.ts
new file mode 100644
index 000000000000..0d70c4a5facb
--- /dev/null
+++ b/src/components/PopoverWithoutOverlay/types.ts
@@ -0,0 +1,28 @@
+import {View} from 'react-native';
+import BaseModalProps from '@components/Modal/types';
+import ChildrenProps from '@src/types/utils/ChildrenProps';
+
+type PopoverWithoutOverlayProps = ChildrenProps &
+ Omit & {
+ /** The anchor position of the popover */
+ anchorPosition?: {
+ top?: number;
+ right?: number;
+ bottom?: number;
+ left?: number;
+ };
+
+ /** The anchor ref of the popover */
+ anchorRef: React.RefObject;
+
+ /** A react-native-animatable animation timing for the modal display animation */
+ animationInTiming?: number;
+
+ /** Whether disable the animations */
+ disableAnimation?: boolean;
+
+ /** The ref of the popover */
+ withoutOverlayRef: React.RefObject;
+ };
+
+export default PopoverWithoutOverlayProps;
diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js
index 6a121d343eaa..4eae450a4e86 100644
--- a/src/components/ReportActionItem/ReportPreview.js
+++ b/src/components/ReportActionItem/ReportPreview.js
@@ -14,6 +14,7 @@ import {showContextMenuForReport} from '@components/ShowContextMenuContext';
import Text from '@components/Text';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import useLocalize from '@hooks/useLocalize';
+import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
@@ -28,6 +29,7 @@ import * as ReportUtils from '@libs/ReportUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
import reportActionPropTypes from '@pages/home/report/reportActionPropTypes';
import reportPropTypes from '@pages/reportPropTypes';
+import variables from '@styles/variables';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
@@ -120,6 +122,7 @@ const defaultProps = {
function ReportPreview(props) {
const theme = useTheme();
const styles = useThemeStyles();
+ const {getLineHeightStyle} = useStyleUtils();
const {translate} = useLocalize();
const [hasMissingSmartscanFields, sethasMissingSmartscanFields] = useState(false);
@@ -270,7 +273,7 @@ function ReportPreview(props) {
- {getPreviewMessage()}
+ {getPreviewMessage()}
{!iouSettled && hasErrors && (
{
+ /**
+ * We are only passing navigation as prop from
+ * ReportScreenWrapper -> ReportScreen -> ScreenWrapper
+ *
+ * so in other places where ScreenWrapper is used, we need to
+ * fallback to useNavigation.
+ */
+ const navigationFallback = useNavigation();
+ const navigation = navigationProp || navigationFallback;
const {windowHeight, isSmallScreenWidth} = useWindowDimensions();
const {initialHeight} = useInitialDimensions();
const styles = useThemeStyles();
const keyboardState = useKeyboardState();
const {isDevelopment} = useEnvironment();
const {isOffline} = useNetwork();
- const navigation = useNavigation();
const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false);
const maxHeight = shouldEnableMaxHeight ? windowHeight : undefined;
const minHeight = shouldEnableMinHeight && !Browser.isSafari() ? initialHeight : undefined;
diff --git a/src/components/SelectionList/BaseListItem.js b/src/components/SelectionList/BaseListItem.js
index ac679f32d103..443b930d5e7a 100644
--- a/src/components/SelectionList/BaseListItem.js
+++ b/src/components/SelectionList/BaseListItem.js
@@ -49,7 +49,7 @@ function BaseListItem({
hoverStyle={styles.hoveredComponentBG}
dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}}
onMouseDown={shouldPreventDefaultFocusOnSelectRow ? (e) => e.preventDefault() : undefined}
- testID={keyForList}
+ nativeID={keyForList}
>
{
- const focusedItemKey = lodashGet(e, ['target', 'attributes', 'data-testid', 'value']);
+ const focusedItemKey = lodashGet(e, ['target', 'attributes', 'id', 'value']);
const focusedOption = focusedItemKey ? _.find(flattenedSections.allOptions, (option) => option.keyForList === focusedItemKey) : flattenedSections.allOptions[focusedIndex];
if (!focusedOption || focusedOption.isDisabled) {
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 9186d81bde3e..98e955bca13e 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -1475,6 +1475,7 @@ export default {
invoices: 'Invoices',
travel: 'Travel',
members: 'Members',
+ plan: 'Plan',
bankAccount: 'Bank account',
connectBankAccount: 'Connect bank account',
testTransactions: 'Test transactions',
@@ -1489,6 +1490,11 @@ export default {
workspaceAvatar: 'Workspace avatar',
mustBeOnlineToViewMembers: 'You must be online in order to view members of this workspace.',
},
+ type: {
+ free: 'Free',
+ control: 'Control',
+ collect: 'Collect',
+ },
emptyWorkspace: {
title: 'Create a workspace',
subtitle: 'Manage business expenses, issue cards, send invoices, and more.',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 37caa0ebcb86..eb31ca065119 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -1498,6 +1498,7 @@ export default {
invoices: 'Enviar facturas',
travel: 'Viajes',
members: 'Miembros',
+ plan: 'Plan',
bankAccount: 'Cuenta bancaria',
connectBankAccount: 'Conectar cuenta bancaria',
testTransactions: 'Transacciones de prueba',
@@ -1512,6 +1513,11 @@ export default {
workspaceAvatar: 'Espacio de trabajo avatar',
mustBeOnlineToViewMembers: 'Debes estar en línea para poder ver los miembros de este espacio de trabajo.',
},
+ type: {
+ free: 'Gratis',
+ control: 'Control',
+ collect: 'Recolectar',
+ },
emptyWorkspace: {
title: 'Crea un espacio de trabajo',
subtitle: 'Administra gastos de empresa, emite tarjetas, envía facturas y mucho más.',
diff --git a/src/libs/DateUtils.ts b/src/libs/DateUtils.ts
index 17739d63c9c7..4bd717eba6c0 100644
--- a/src/libs/DateUtils.ts
+++ b/src/libs/DateUtils.ts
@@ -38,6 +38,7 @@ import Log from './Log';
type CustomStatusTypes = (typeof CONST.CUSTOM_STATUS_TYPES)[keyof typeof CONST.CUSTOM_STATUS_TYPES];
type TimePeriod = 'AM' | 'PM';
type Locale = ValueOf;
+type WeekDay = 0 | 1 | 2 | 3 | 4 | 5 | 6;
let currentUserAccountID: number | undefined;
Onyx.connect({
@@ -69,6 +70,22 @@ Onyx.connect({
},
});
+/**
+ * Get the day of the week that the week starts on
+ */
+function getWeekStartsOn(): WeekDay {
+ return CONST.WEEK_STARTS_ON;
+}
+
+/**
+ * Get the day of the week that the week ends on
+ */
+function getWeekEndsOn(): WeekDay {
+ const weekStartsOn = getWeekStartsOn();
+
+ return weekStartsOn === 0 ? 6 : ((weekStartsOn - 1) as WeekDay);
+}
+
/**
* Gets the locale string and setting default locale for date-fns
*/
@@ -163,9 +180,10 @@ function datetimeToCalendarTime(locale: Locale, datetime: string, includeTimeZon
let tomorrowAt = Localize.translate(locale, 'common.tomorrowAt');
let yesterdayAt = Localize.translate(locale, 'common.yesterdayAt');
const at = Localize.translate(locale, 'common.conjunctionAt');
+ const weekStartsOn = getWeekStartsOn();
- const startOfCurrentWeek = startOfWeek(new Date(), {weekStartsOn: 1}); // Assuming Monday is the start of the week
- const endOfCurrentWeek = endOfWeek(new Date(), {weekStartsOn: 1}); // Assuming Monday is the start of the week
+ const startOfCurrentWeek = startOfWeek(new Date(), {weekStartsOn});
+ const endOfCurrentWeek = endOfWeek(new Date(), {weekStartsOn});
if (isLowercase) {
todayAt = todayAt.toLowerCase();
@@ -302,8 +320,9 @@ function getDaysOfWeek(preferredLocale: Locale): string[] {
if (preferredLocale) {
setLocale(preferredLocale);
}
- const startOfCurrentWeek = startOfWeek(new Date(), {weekStartsOn: 1}); // Assuming Monday is the start of the week
- const endOfCurrentWeek = endOfWeek(new Date(), {weekStartsOn: 1}); // Assuming Monday is the start of the week
+ const weekStartsOn = getWeekStartsOn();
+ const startOfCurrentWeek = startOfWeek(new Date(), {weekStartsOn});
+ const endOfCurrentWeek = endOfWeek(new Date(), {weekStartsOn});
const daysOfWeek = eachDayOfInterval({start: startOfCurrentWeek, end: endOfCurrentWeek});
// eslint-disable-next-line rulesdir/prefer-underscore-method
@@ -717,6 +736,8 @@ const DateUtils = {
getMonthNames,
getDaysOfWeek,
formatWithUTCTimeZone,
+ getWeekStartsOn,
+ getWeekEndsOn,
isTimeAtLeastOneMinuteInFuture,
};
diff --git a/src/libs/Navigation/AppNavigator/ReportScreenWrapper.tsx b/src/libs/Navigation/AppNavigator/ReportScreenWrapper.tsx
index 20922fd785ce..f3295aadb888 100644
--- a/src/libs/Navigation/AppNavigator/ReportScreenWrapper.tsx
+++ b/src/libs/Navigation/AppNavigator/ReportScreenWrapper.tsx
@@ -12,8 +12,11 @@ function ReportScreenWrapper({route, navigation}: ReportScreenWrapperProps) {
// until the reportID is loaded and set in the route param
return (
<>
- {/* @ts-expect-error Error will be resolved after ReportScreen migration to TypeScript */}
-
+
`${CONST.UNICODE.LTR}${text}`;
export default convertToLTR;
diff --git a/src/libs/convertToLTR/index.ts b/src/libs/convertToLTR/index.ts
index 0dca0d0b3ace..58d8be93836e 100644
--- a/src/libs/convertToLTR/index.ts
+++ b/src/libs/convertToLTR/index.ts
@@ -1,4 +1,3 @@
-// The Android platform has to handle switching between LTR and RTL languages a bit differently (https://developer.android.com/training/basics/supporting-devices/languages). For all other platforms, these can simply be no-op functions.
import ConvertToLTR from './types';
const convertToLTR: ConvertToLTR = (text) => text;
diff --git a/src/libs/convertToLTRForComposer/index.android.ts b/src/libs/convertToLTRForComposer/index.android.ts
index 0d68baa80e3a..09e7f2e5cd87 100644
--- a/src/libs/convertToLTRForComposer/index.android.ts
+++ b/src/libs/convertToLTRForComposer/index.android.ts
@@ -1,69 +1,8 @@
-import CONST from '@src/CONST';
import ConvertToLTRForComposer from './types';
-/**
- * Android only - The composer can be converted to LTR if its content is the LTR character followed by an @ or space
- * because to mention sugggestion works the @ character must not have any character at the beginning e.g.: \u2066@ doesn't work
- * also to avoid sending empty messages the unicode character with space could enable the send button.
- */
-function canComposerBeConvertedToLTR(text: string): boolean {
- // This regex handles the case when a user only types spaces into the composer.
- const containOnlySpaces = /^\s*$/;
- // This regex handles the case where someone has RTL enabled and they began typing an @mention for someone.
- const startsWithLTRAndAt = new RegExp(`^${CONST.UNICODE.LTR}@$`);
- // This regex handles the case where the composer can contain multiple lines of whitespace
- const startsWithLTRAndSpace = new RegExp(`${CONST.UNICODE.LTR}\\s*$`);
- const emptyExpressions = [containOnlySpaces, startsWithLTRAndAt, startsWithLTRAndSpace];
- return emptyExpressions.some((exp) => exp.test(text));
-}
-
-/**
- * Android only - We should remove the LTR unicode when the input is empty to prevent:
- * Sending an empty message;
- * Mention suggestions not works if @ or \s (at or space) is the first character;
- * Placeholder is not displayed if the unicode character is the only character remaining;
- *
- * @param {String} newComment - the comment written by the user
- * @param {Boolean} force - always remove the LTR unicode, going to be used when composer is consider as empty
- * @return {String}
- */
-
-const resetLTRWhenEmpty = (newComment: string, force?: boolean) => {
- const result = newComment.length <= 1 || force ? newComment.replaceAll(CONST.UNICODE.LTR, '') : newComment;
- return result;
-};
-
/**
* Android only - Do not convert RTL text to a LTR text for input box using Unicode controls.
* Android does not properly support bidirectional text for mixed content for input box
*/
-const convertToLTRForComposer: ConvertToLTRForComposer = (text, isComposerEmpty) => {
- const shouldComposerMaintainAsLTR = canComposerBeConvertedToLTR(text);
- const newText = resetLTRWhenEmpty(text, shouldComposerMaintainAsLTR);
- if (shouldComposerMaintainAsLTR) {
- return newText;
- }
- return isComposerEmpty ? `${CONST.UNICODE.LTR}${newText}` : newText;
-};
-
-/**
- * This is necessary to convert the input to LTR, there is a delay that causes the cursor not to go to the end of the input line when pasting text or typing fast. The delay is caused for the time that takes the input to convert from RTL to LTR and viceversa.
- */
-const moveCursorToEndOfLine = (
- commentLength: number,
- setSelection: (
- value: React.SetStateAction<{
- start: number;
- end: number;
- }>,
- ) => void,
-) => {
- setSelection({
- start: commentLength + 1,
- end: commentLength + 1,
- });
-};
-
-export {moveCursorToEndOfLine};
-
+const convertToLTRForComposer: ConvertToLTRForComposer = (text) => text;
export default convertToLTRForComposer;
diff --git a/src/libs/convertToLTRForComposer/index.ts b/src/libs/convertToLTRForComposer/index.ts
index 9286a41a6712..dd6ee50d862e 100644
--- a/src/libs/convertToLTRForComposer/index.ts
+++ b/src/libs/convertToLTRForComposer/index.ts
@@ -30,9 +30,4 @@ const convertToLTRForComposer: ConvertToLTRForComposer = (text) => {
// Add the LTR marker to the beginning of the text.
return `${CONST.UNICODE.LTR}${text}`;
};
-
-const moveCursorToEndOfLine = (commentLength: number) => commentLength;
-
-export {moveCursorToEndOfLine};
-
export default convertToLTRForComposer;
diff --git a/src/libs/convertToLTRForComposer/types.ts b/src/libs/convertToLTRForComposer/types.ts
index 88f468ef1843..c6edeaaba446 100644
--- a/src/libs/convertToLTRForComposer/types.ts
+++ b/src/libs/convertToLTRForComposer/types.ts
@@ -1,3 +1,3 @@
-type ConvertToLTRForComposer = (text: string, isComposerEmpty?: boolean) => string;
+type ConvertToLTRForComposer = (text: string) => string;
export default ConvertToLTRForComposer;
diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js
index 6afe7f92075b..7259ede3f480 100644
--- a/src/pages/home/ReportScreen.js
+++ b/src/pages/home/ReportScreen.js
@@ -153,6 +153,7 @@ function ReportScreen({
errors,
userLeavingStatus,
currentReportID,
+ navigation,
}) {
const styles = useThemeStyles();
const {translate} = useLocalize();
@@ -434,6 +435,7 @@ function ReportScreen({
{
@@ -224,33 +223,18 @@ function ComposerWithSuggestions({
debouncedUpdateFrequentlyUsedEmojis();
}
}
-
- let newCommentConvertedToLTR = newComment;
- const prevComment = commentRef.current;
-
- // This prevent the double execution of setting input value that could affect the place holder and could send an empty message or draft messages in android
- if (prevComment !== newComment) {
- newCommentConvertedToLTR = convertToLTRForComposer(newCommentConvertedToLTR, composerIsEmpty.current);
- setValue(newCommentConvertedToLTR);
- moveCursorToEndOfLine(newComment.length, setSelection);
- composerIsEmpty.current = false;
- }
-
- const isNewCommentEmpty = !!newCommentConvertedToLTR.match(/^(\s)*$/);
- const isPrevCommentEmpty = !!prevComment.match(/^(\s)*$/);
+ const newCommentConverted = convertToLTRForComposer(newComment);
+ const isNewCommentEmpty = !!newCommentConverted.match(/^(\s)*$/);
+ const isPrevCommentEmpty = !!commentRef.current.match(/^(\s)*$/);
/** Only update isCommentEmpty state if it's different from previous one */
if (isNewCommentEmpty !== isPrevCommentEmpty) {
setIsCommentEmpty(isNewCommentEmpty);
- if (isNewCommentEmpty) {
- composerIsEmpty.current = true;
- }
}
-
emojisPresentBefore.current = emojis;
-
+ setValue(newCommentConverted);
if (commentValue !== newComment) {
- const position = Math.max(selection.end + (newComment.length - prevComment.length), cursorPosition || 0);
+ const position = Math.max(selection.end + (newComment.length - commentRef.current.length), cursorPosition || 0);
setSelection({
start: position,
end: position,
@@ -258,22 +242,22 @@ function ComposerWithSuggestions({
}
// Indicate that draft has been created.
- if (prevComment.length === 0 && newCommentConvertedToLTR.length !== 0) {
+ if (commentRef.current.length === 0 && newCommentConverted.length !== 0) {
Report.setReportWithDraft(reportID, true);
}
// The draft has been deleted.
- if (newCommentConvertedToLTR.length === 0) {
+ if (newCommentConverted.length === 0) {
Report.setReportWithDraft(reportID, false);
}
- commentRef.current = newCommentConvertedToLTR;
+ commentRef.current = newCommentConverted;
if (shouldDebounceSaveComment) {
- debouncedSaveReportComment(reportID, newCommentConvertedToLTR);
+ debouncedSaveReportComment(reportID, newCommentConverted);
} else {
- Report.saveReportComment(reportID, newCommentConvertedToLTR || '');
+ Report.saveReportComment(reportID, newCommentConverted || '');
}
- if (newCommentConvertedToLTR) {
+ if (newCommentConverted) {
debouncedBroadcastUserIsTyping(reportID);
}
},
@@ -287,7 +271,6 @@ function ComposerWithSuggestions({
raiseIsScrollLikelyLayoutTriggered,
debouncedSaveReportComment,
selection.end,
- composerIsEmpty,
],
);
diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js
index 2c8362268ef8..2009dc9a102d 100644
--- a/src/pages/home/report/ReportActionsList.js
+++ b/src/pages/home/report/ReportActionsList.js
@@ -159,6 +159,12 @@ function ReportActionsList({
const lastVisibleActionCreatedRef = useRef(report.lastVisibleActionCreated);
const lastReadTimeRef = useRef(report.lastReadTime);
+ const sortedVisibleReportActions = _.filter(sortedReportActions, (s) => isOffline || s.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || s.errors);
+ const lastActionIndex = lodashGet(sortedVisibleReportActions, [0, 'reportActionID']);
+ const reportActionSize = useRef(sortedVisibleReportActions.length);
+
+ const previousLastIndex = useRef(lastActionIndex);
+
const linkedReportActionID = lodashGet(route, 'params.reportActionID', '');
// This state is used to force a re-render when the user manually marks a message as unread
@@ -173,6 +179,14 @@ function ReportActionsList({
opacity.value = withTiming(1, {duration: 100});
}, [opacity]);
+ useEffect(() => {
+ if (previousLastIndex.current !== lastActionIndex && reportActionSize.current > sortedVisibleReportActions.length) {
+ reportScrollManager.scrollToBottom();
+ }
+ previousLastIndex.current = lastActionIndex;
+ reportActionSize.current = sortedVisibleReportActions.length;
+ }, [lastActionIndex, sortedVisibleReportActions.length, reportScrollManager]);
+
useEffect(() => {
// If the reportID changes, we reset the userActiveSince to null, we need to do it because
// the parent component is sending the previous reportID even when the user isn't active
diff --git a/src/pages/workspace/WorkspacesListRow.tsx b/src/pages/workspace/WorkspacesListRow.tsx
new file mode 100755
index 000000000000..1a28ff47bbde
--- /dev/null
+++ b/src/pages/workspace/WorkspacesListRow.tsx
@@ -0,0 +1,177 @@
+import React, {useMemo} from 'react';
+import {View} from 'react-native';
+import {ValueOf} from 'type-fest';
+import Avatar from '@components/Avatar';
+import Icon from '@components/Icon';
+import * as Illustrations from '@components/Icon/Illustrations';
+import {MenuItemProps} from '@components/MenuItem';
+import Text from '@components/Text';
+import ThreeDotsMenu from '@components/ThreeDotsMenu';
+import withCurrentUserPersonalDetails, {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails';
+import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
+import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
+import {AvatarSource} from '@libs/UserUtils';
+import variables from '@styles/variables';
+import CONST from '@src/CONST';
+import IconAsset from '@src/types/utils/IconAsset';
+
+type WorkspacesListRowProps = WithCurrentUserPersonalDetailsProps & {
+ /** Name of the workspace */
+ title: string;
+
+ /** Account ID of the workspace's owner */
+ ownerAccountID?: number;
+
+ /** Type of workspace. Type personal is not valid in this context so it's omitted */
+ workspaceType: typeof CONST.POLICY.TYPE.FREE | typeof CONST.POLICY.TYPE.CORPORATE | typeof CONST.POLICY.TYPE.TEAM;
+
+ /** Icon to show next to the workspace name */
+ workspaceIcon?: AvatarSource;
+
+ /** Icon to be used when workspaceIcon is not present */
+ fallbackWorkspaceIcon?: AvatarSource;
+
+ /** Items for the three dots menu */
+ menuItems: MenuItemProps[];
+
+ /** Renders the component using big screen layout or small screen layout. When layoutWidth === WorkspaceListRowLayout.NONE,
+ * component will return null to prevent layout from jumping on initial render and when parent width changes. */
+ layoutWidth?: ValueOf;
+};
+
+const workspaceTypeIcon = (workspaceType: WorkspacesListRowProps['workspaceType']): IconAsset => {
+ switch (workspaceType) {
+ case CONST.POLICY.TYPE.FREE:
+ return Illustrations.HandCard;
+ case CONST.POLICY.TYPE.CORPORATE:
+ return Illustrations.ShieldYellow;
+ case CONST.POLICY.TYPE.TEAM:
+ return Illustrations.Mailbox;
+ default:
+ throw new Error(`Don't know which icon to serve for workspace type`);
+ }
+};
+
+function WorkspacesListRow({
+ title,
+ menuItems,
+ workspaceIcon,
+ fallbackWorkspaceIcon,
+ ownerAccountID,
+ workspaceType,
+ currentUserPersonalDetails,
+ layoutWidth = CONST.LAYOUT_WIDTH.NONE,
+}: WorkspacesListRowProps) {
+ const styles = useThemeStyles();
+ const {translate} = useLocalize();
+
+ const ownerDetails = ownerAccountID && PersonalDetailsUtils.getPersonalDetailsByIDs([ownerAccountID], currentUserPersonalDetails.accountID)[0];
+
+ const userFriendlyWorkspaceType = useMemo(() => {
+ switch (workspaceType) {
+ case CONST.POLICY.TYPE.FREE:
+ return translate('workspace.type.free');
+ case CONST.POLICY.TYPE.CORPORATE:
+ return translate('workspace.type.control');
+ case CONST.POLICY.TYPE.TEAM:
+ return translate('workspace.type.collect');
+ default:
+ throw new Error(`Don't know a friendly workspace name for this workspace type`);
+ }
+ }, [workspaceType, translate]);
+
+ if (layoutWidth === CONST.LAYOUT_WIDTH.NONE) {
+ // To prevent layout from jumping or rendering for a split second, when
+ // isWide is undefined we don't assume anything and simply return null.
+ return null;
+ }
+
+ const isWide = layoutWidth === CONST.LAYOUT_WIDTH.WIDE;
+ const isNarrow = layoutWidth === CONST.LAYOUT_WIDTH.NARROW;
+
+ return (
+
+
+
+
+ {title}
+
+ {isNarrow && (
+
+ )}
+
+
+ {!!ownerDetails && (
+ <>
+
+
+
+ {PersonalDetailsUtils.getDisplayNameOrDefault(ownerDetails.displayName)}
+
+
+ {ownerDetails.login}
+
+
+ >
+ )}
+
+
+
+
+
+ {userFriendlyWorkspaceType}
+
+
+ {translate('workspace.common.plan')}
+
+
+
+ {isWide && (
+
+ )}
+
+ );
+}
+
+WorkspacesListRow.displayName = 'WorkspacesListRow';
+
+export default withCurrentUserPersonalDetails(WorkspacesListRow);
diff --git a/src/styles/index.ts b/src/styles/index.ts
index 0d1b47235347..cfd9b797de0b 100644
--- a/src/styles/index.ts
+++ b/src/styles/index.ts
@@ -296,7 +296,7 @@ const styles = (theme: ThemeColors) =>
fontWeight: fontWeightBold,
},
- mentionSuggestionsHandle: {
+ textSupporting: {
color: theme.textSupporting,
},
@@ -424,7 +424,7 @@ const styles = (theme: ThemeColors) =>
...whiteSpace.preWrap,
color: theme.heading,
fontSize: variables.fontSizeXLarge,
- lineHeight: variables.lineHeightXXLarge,
+ lineHeight: variables.lineHeightXXXLarge,
},
textHeadlineH1: {
@@ -1148,6 +1148,13 @@ const styles = (theme: ThemeColors) =>
noOutline: addOutlineWidth(theme, {}, 0),
+ labelStrong: {
+ fontFamily: fontFamily.EXP_NEUE,
+ fontWeight: 'bold',
+ fontSize: variables.fontSizeLabel,
+ lineHeight: variables.lineHeightNormal,
+ },
+
textLabelSupporting: {
fontFamily: fontFamily.EXP_NEUE,
fontSize: variables.fontSizeLabel,
@@ -1579,7 +1586,7 @@ const styles = (theme: ThemeColors) =>
optionDisplayName: {
fontFamily: fontFamily.EXP_NEUE,
minHeight: variables.alternateTextHeight,
- lineHeight: variables.lineHeightXLarge,
+ lineHeight: variables.lineHeightXXLarge,
...whiteSpace.noWrap,
},
@@ -1599,7 +1606,7 @@ const styles = (theme: ThemeColors) =>
optionAlternateText: {
minHeight: variables.alternateTextHeight,
- lineHeight: variables.lineHeightXLarge,
+ lineHeight: variables.lineHeightXXLarge,
},
optionAlternateTextCompact: {
@@ -1727,7 +1734,7 @@ const styles = (theme: ThemeColors) =>
fontFamily: fontFamily.EXP_NEUE_BOLD,
fontSize: variables.fontSizeNormal,
fontWeight: fontWeightBold,
- lineHeight: variables.lineHeightXLarge,
+ lineHeight: variables.lineHeightXXLarge,
...wordBreak.breakWord,
},
@@ -2874,6 +2881,14 @@ const styles = (theme: ThemeColors) =>
bottom: -4,
},
+ workspaceOwnerAvatarWrapper: {
+ margin: 6,
+ },
+
+ workspaceTypeWrapper: {
+ margin: 3,
+ },
+
autoGrowHeightMultilineInput: {
maxHeight: 115,
},
@@ -3684,6 +3699,7 @@ const styles = (theme: ThemeColors) =>
fontFamily: isSelected ? fontFamily.EXP_NEUE_BOLD : fontFamily.EXP_NEUE,
fontWeight: isSelected ? fontWeightBold : '400',
color: isSelected ? theme.text : theme.textSupporting,
+ lineHeight: 14,
} satisfies TextStyle),
tabBackground: (hovered: boolean, isFocused: boolean, background: string | Animated.AnimatedInterpolation) => ({
diff --git a/src/styles/utils/borders.ts b/src/styles/utils/borders.ts
index 5d5110f858e4..9cd02dcd22ae 100644
--- a/src/styles/utils/borders.ts
+++ b/src/styles/utils/borders.ts
@@ -12,6 +12,10 @@ export default {
borderRadius: 8,
},
+ br3: {
+ borderRadius: 12,
+ },
+
br4: {
borderRadius: 16,
},
diff --git a/src/styles/variables.ts b/src/styles/variables.ts
index 3bf83545bc4a..9bc467615706 100644
--- a/src/styles/variables.ts
+++ b/src/styles/variables.ts
@@ -178,6 +178,7 @@ export default {
reportActionItemImagesMoreCornerTriangleWidth: 40,
bankCardWidth: 40,
bankCardHeight: 26,
+ workspaceTypeIconWidth: 34,
// The height of the empty list is 14px (2px for borders and 12px for vertical padding)
// This is calculated based on the values specified in the 'getGoogleListViewStyle' function of the 'StyleUtils' utility
diff --git a/tests/perf-test/OptionsSelector.perf-test.js b/tests/perf-test/OptionsSelector.perf-test.js
new file mode 100644
index 000000000000..da706d7bb629
--- /dev/null
+++ b/tests/perf-test/OptionsSelector.perf-test.js
@@ -0,0 +1,130 @@
+import {fireEvent} from '@testing-library/react-native';
+import React from 'react';
+import {measurePerformance} from 'reassure';
+import _ from 'underscore';
+import OptionsSelector from '@src/components/OptionsSelector';
+import CONST from '@src/CONST';
+import variables from '@src/styles/variables';
+
+jest.mock('../../src/components/withLocalize', () => (Component) => {
+ function WrappedComponent(props) {
+ return (
+ ''}
+ />
+ );
+ }
+ WrappedComponent.displayName = `WrappedComponent`;
+ return WrappedComponent;
+});
+
+jest.mock('../../src/components/withNavigationFocus', () => (Component) => {
+ function WithNavigationFocus(props) {
+ return (
+
+ );
+ }
+
+ WithNavigationFocus.displayName = 'WithNavigationFocus';
+
+ return WithNavigationFocus;
+});
+
+const generateSections = (sectionConfigs) =>
+ _.map(sectionConfigs, ({numItems, indexOffset, shouldShow = true}) => ({
+ data: Array.from({length: numItems}, (_v, i) => ({
+ text: `Item ${i + indexOffset}`,
+ keyForList: `item-${i + indexOffset}`,
+ })),
+ indexOffset,
+ shouldShow,
+ }));
+
+const singleSectionSConfig = [{numItems: 1000, indexOffset: 0}];
+
+const mutlipleSectionsConfig = [
+ {numItems: 1000, indexOffset: 0},
+ {numItems: 100, indexOffset: 70},
+];
+
+function OptionsSelectorWrapper(args) {
+ const sections = generateSections(singleSectionSConfig);
+ return (
+
+ );
+}
+
+const runs = CONST.PERFORMANCE_TESTS.RUNS;
+
+test('[OptionsSelector] should render text input with interactions', () => {
+ const scenario = (screen) => {
+ const textInput = screen.getByTestId('options-selector-input');
+ fireEvent.changeText(textInput, 'test');
+ fireEvent.changeText(textInput, 'test2');
+ fireEvent.changeText(textInput, 'test3');
+ };
+
+ measurePerformance(, {scenario, runs});
+});
+
+test('[OptionsSelector] should render 1 section', () => {
+ measurePerformance(, {runs});
+});
+
+test('[OptionsSelector] should render mutliple sections', () => {
+ const sections = generateSections(mutlipleSectionsConfig);
+ measurePerformance(, {runs});
+});
+
+test('[OptionsSelector] should press a list items', () => {
+ const scenario = (screen) => {
+ fireEvent.press(screen.getByText('Item 1'));
+ fireEvent.press(screen.getByText('Item 5'));
+ fireEvent.press(screen.getByText('Item 10'));
+ };
+
+ measurePerformance(, {scenario, runs});
+});
+
+test('[OptionsSelector] should scroll and press few items', () => {
+ const sections = generateSections(mutlipleSectionsConfig);
+
+ const generateEventData = (numOptions, optionRowHeight) => ({
+ nativeEvent: {
+ contentOffset: {
+ y: optionRowHeight * numOptions,
+ },
+ contentSize: {
+ height: optionRowHeight * 10,
+ width: 100,
+ },
+ layoutMeasurement: {
+ height: optionRowHeight * 5,
+ width: 100,
+ },
+ },
+ });
+
+ const eventData = generateEventData(100, variables.optionRowHeight);
+ const eventData2 = generateEventData(200, variables.optionRowHeight);
+ const scenario = (screen) => {
+ fireEvent.press(screen.getByText('Item 10'));
+ fireEvent.scroll(screen.getByTestId('options-list'), eventData);
+ fireEvent.press(screen.getByText('Item 100'));
+ fireEvent.scroll(screen.getByTestId('options-list'), eventData2);
+ fireEvent.press(screen.getByText('Item 200'));
+ };
+
+ measurePerformance(, {scenario, runs});
+});
diff --git a/tests/perf-test/README.md b/tests/perf-test/README.md
new file mode 100644
index 000000000000..e7391612f071
--- /dev/null
+++ b/tests/perf-test/README.md
@@ -0,0 +1,69 @@
+# Performance testing
+
+We use Reassure for monitoring performance regression. It helps us check if our app is getting faster and quickly spot any issues we need to fix.
+
+## How does Reassure work?
+
+- Reassure builds on the existing React Testing Library setup and adds a performance measurement functionality. It's intended to be used on local machine and on a remote server as part of your continuous integration setup.
+- To make sure the results are reliable and consistent, Reassure runs tests twice – once for the current branch and once for the base branch.
+
+## Performance Testing Strategy (`measurePerformance`)
+
+- The primary focus is on testing business cases rather than small, reusable parts that typically don't introduce regressions, although some tests in that area are still necessary.
+- To achieve this goal, it's recommended to stay relatively high up in the React tree, targeting whole screens to recreate real-life scenarios that users may encounter.
+- For example, consider scenarios where an additional `useMemo` call could impact performance negatively.
+
+## `measureFunction` API approach
+
+- Identifying functions with heavy calculations.
+- Targeting functions that are frequently used throughout the app.
+
+## Running tests locally
+
+- Checkout your base environment, eg. `git checkout main`.
+- Collect baseline metrics with `npx reassure --baseline`.
+- Apply any desired changes (for testing purposes you can eg. try to slow down a list).
+- Collect current metrics with `npx reassure`.
+- Open up the resulting `output.md` / `output.json` (see console output) to compare the results.
+- With all that information, Reassure can present the render duration times as statistically significant or meaningless.
+
+## Metrics for Regression Detection
+
+- **Render count**: If the number of renders increases by one compared to the baseline, it will be considered a performance regression, leading to a failed test. This metric helps detect unexpected changes in component rendering behavior. *NOTE: sometimes regressions are intentional. For instance, if a new functionality is added to the tested component, causing an additional re-render, this regression is expected.*
+- **Render duration**: A performance regression will occur if the measured rendering time is 20% higher than the baseline, resulting in a failed test. This threshold allows for reasonable fluctuations and accounts for changes that may lead to longer rendering times.
+
+
+## Tips for Performance Testing with Reassure
+
+- Before you start using Reassure, take a bit of time to learn what it does [docs](https://callstack.github.io/reassure/).
+- Mocking could initially be challenging, but preparing utilities for handling collections of data simplified the process. We’ve already prepared base for utilities for mocking collections. More information and instructions can be found [here](https://github.com/Expensify/App/tree/main/tests#mocking-collections--collection-items). *Feel free to adjust/ add more helper methods if you think it’s needed*.
+- Mocking is a crucial part of performance testing. To achieve more accurate and meaningful results, mock and use as much data as possible.
+- Inside each test, there is a defined scenario function that represents the specific user interaction you want to measure (HINT: there is no need to add assertions in performance tests).
+- More runs generally lead to better and more reliable results by averaging out variations. Additionally, consider adjusting the number of runs per series for each specific test to achieve more granular insights.
+
+## What can be tested (scenarios)
+
+- Rendering lists with multiple items (try adding as much data as possible to get a more reliable result when it comes to potential regressions).
+- Scrolling performance.
+- onPress/onSelect action.
+- Text input interactions.
+
+## Example Test
+
+```javascript
+test('Count increments on press', async () => {
+ const scenario = async (screen) => {
+ const button = screen.getByText('Action');
+
+ await screen.findByText('Count: 0');
+ fireEvent.press(button);
+ fireEvent.press(button);
+ await screen.findByText('Count: 2');
+ };
+
+ await measurePerformance(
+ ,
+ { scenario, runs: 20 }
+ );
+});
+```
diff --git a/tests/perf-test/ReportScreen.perf-test.js b/tests/perf-test/ReportScreen.perf-test.js
index 96514112cd05..a82903762631 100644
--- a/tests/perf-test/ReportScreen.perf-test.js
+++ b/tests/perf-test/ReportScreen.perf-test.js
@@ -1,7 +1,8 @@
-import {fireEvent, screen} from '@testing-library/react-native';
+import {act, fireEvent, screen} from '@testing-library/react-native';
import React from 'react';
import Onyx from 'react-native-onyx';
import {measurePerformance} from 'reassure';
+import _ from 'underscore';
import ComposeProviders from '../../src/components/ComposeProviders';
import DragAndDropProvider from '../../src/components/DragAndDrop/Provider';
import {LocaleContextProvider} from '../../src/components/LocaleContextProvider';
@@ -101,6 +102,33 @@ afterEach(() => {
PusherHelper.teardown();
});
+/**
+ * This is a helper function to create a mock for the addListener function of the react-navigation library.
+ * The reason we need this is because we need to trigger the transitionEnd event in our tests to simulate
+ * the transitionEnd event that is triggered when the screen transition animation is completed.
+ *
+ * P.S: This can't be moved to a utils file because Jest wants any external function to stay in the scope.
+ *
+ * @returns {Object} An object with two functions: triggerTransitionEnd and addListener
+ */
+const createAddListenerMock = () => {
+ const transitionEndListeners = [];
+ const triggerTransitionEnd = () => {
+ transitionEndListeners.forEach((transitionEndListener) => transitionEndListener());
+ };
+
+ const addListener = jest.fn().mockImplementation((listener, callback) => {
+ if (listener === 'transitionEnd') {
+ transitionEndListeners.push(callback);
+ }
+ return () => {
+ _.filter(transitionEndListeners, (cb) => cb !== callback);
+ };
+ });
+
+ return {triggerTransitionEnd, addListener};
+};
+
function ReportScreenWrapper(args) {
return (
);
@@ -125,7 +154,19 @@ function ReportScreenWrapper(args) {
const runs = CONST.PERFORMANCE_TESTS.RUNS;
test('[ReportScreen] should render ReportScreen with composer interactions', () => {
+ const {triggerTransitionEnd, addListener} = createAddListenerMock();
const scenario = async () => {
+ /**
+ * First make sure ReportScreen is mounted, so that we can trigger
+ * the transitionEnd event manually.
+ *
+ * If we don't do that, then the transitionEnd event will be triggered
+ * before the ReportScreen is mounted, and the test will fail.
+ */
+ await screen.findByTestId('ReportScreen');
+
+ await act(triggerTransitionEnd);
+
// Query for the report list
await screen.findByTestId('report-actions-list');
@@ -158,6 +199,8 @@ test('[ReportScreen] should render ReportScreen with composer interactions', ()
const reportActions = ReportTestUtils.getMockedReportActionsMap(1000);
const mockRoute = {params: {reportID: '1'}};
+ const navigation = {addListener};
+
return waitForBatchedUpdates()
.then(() =>
Onyx.multiSet({
@@ -172,11 +215,31 @@ test('[ReportScreen] should render ReportScreen with composer interactions', ()
},
}),
)
- .then(() => measurePerformance(, {scenario, runs}));
+ .then(() =>
+ measurePerformance(
+ ,
+ {scenario, runs},
+ ),
+ );
});
test('[ReportScreen] should press of the report item', () => {
+ const {triggerTransitionEnd, addListener} = createAddListenerMock();
const scenario = async () => {
+ /**
+ * First make sure ReportScreen is mounted, so that we can trigger
+ * the transitionEnd event manually.
+ *
+ * If we don't do that, then the transitionEnd event will be triggered
+ * before the ReportScreen is mounted, and the test will fail.
+ */
+ await screen.findByTestId('ReportScreen');
+
+ await act(triggerTransitionEnd);
+
// Query for the report list
await screen.findByTestId('report-actions-list');
@@ -201,6 +264,8 @@ test('[ReportScreen] should press of the report item', () => {
const reportActions = ReportTestUtils.getMockedReportActionsMap(1000);
const mockRoute = {params: {reportID: '2'}};
+ const navigation = {addListener};
+
return waitForBatchedUpdates()
.then(() =>
Onyx.multiSet({
@@ -215,5 +280,13 @@ test('[ReportScreen] should press of the report item', () => {
},
}),
)
- .then(() => measurePerformance(, {scenario, runs}));
+ .then(() =>
+ measurePerformance(
+ ,
+ {scenario, runs},
+ ),
+ );
});
diff --git a/tests/unit/generateMonthMatrixTest.js b/tests/unit/generateMonthMatrixTest.js
index e65543b82f17..67dd65e6b1fd 100644
--- a/tests/unit/generateMonthMatrixTest.js
+++ b/tests/unit/generateMonthMatrixTest.js
@@ -3,39 +3,72 @@ import generateMonthMatrix from '../../src/components/DatePicker/CalendarPicker/
describe('generateMonthMatrix', () => {
it('returns the correct matrix for January 2022', () => {
const expected = [
- [null, null, null, null, null, null, 1],
- [2, 3, 4, 5, 6, 7, 8],
- [9, 10, 11, 12, 13, 14, 15],
- [16, 17, 18, 19, 20, 21, 22],
- [23, 24, 25, 26, 27, 28, 29],
- [30, 31, null, null, null, null, null],
+ [null, null, null, null, null, 1, 2],
+ [3, 4, 5, 6, 7, 8, 9],
+ [10, 11, 12, 13, 14, 15, 16],
+ [17, 18, 19, 20, 21, 22, 23],
+ [24, 25, 26, 27, 28, 29, 30],
+ [31, null, null, null, null, null, null],
];
expect(generateMonthMatrix(2022, 0)).toEqual(expected);
});
it('returns the correct matrix for February 2022', () => {
const expected = [
- [null, null, 1, 2, 3, 4, 5],
- [6, 7, 8, 9, 10, 11, 12],
- [13, 14, 15, 16, 17, 18, 19],
- [20, 21, 22, 23, 24, 25, 26],
- [27, 28, null, null, null, null, null],
+ [null, 1, 2, 3, 4, 5, 6],
+ [7, 8, 9, 10, 11, 12, 13],
+ [14, 15, 16, 17, 18, 19, 20],
+ [21, 22, 23, 24, 25, 26, 27],
+ [28, null, null, null, null, null, null],
];
expect(generateMonthMatrix(2022, 1)).toEqual(expected);
});
it('returns the correct matrix for leap year February 2020', () => {
const expected = [
- [null, null, null, null, null, null, 1],
- [2, 3, 4, 5, 6, 7, 8],
- [9, 10, 11, 12, 13, 14, 15],
- [16, 17, 18, 19, 20, 21, 22],
- [23, 24, 25, 26, 27, 28, 29],
+ [null, null, null, null, null, 1, 2],
+ [3, 4, 5, 6, 7, 8, 9],
+ [10, 11, 12, 13, 14, 15, 16],
+ [17, 18, 19, 20, 21, 22, 23],
+ [24, 25, 26, 27, 28, 29, null],
];
expect(generateMonthMatrix(2020, 1)).toEqual(expected);
});
it('returns the correct matrix for March 2022', () => {
+ const expected = [
+ [null, 1, 2, 3, 4, 5, 6],
+ [7, 8, 9, 10, 11, 12, 13],
+ [14, 15, 16, 17, 18, 19, 20],
+ [21, 22, 23, 24, 25, 26, 27],
+ [28, 29, 30, 31, null, null, null],
+ ];
+ expect(generateMonthMatrix(2022, 2)).toEqual(expected);
+ });
+
+ it('returns the correct matrix for April 2022', () => {
+ const expected = [
+ [null, null, null, null, 1, 2, 3],
+ [4, 5, 6, 7, 8, 9, 10],
+ [11, 12, 13, 14, 15, 16, 17],
+ [18, 19, 20, 21, 22, 23, 24],
+ [25, 26, 27, 28, 29, 30, null],
+ ];
+ expect(generateMonthMatrix(2022, 3)).toEqual(expected);
+ });
+
+ it('returns the correct matrix for December 2022', () => {
+ const expected = [
+ [null, null, null, 1, 2, 3, 4],
+ [5, 6, 7, 8, 9, 10, 11],
+ [12, 13, 14, 15, 16, 17, 18],
+ [19, 20, 21, 22, 23, 24, 25],
+ [26, 27, 28, 29, 30, 31, null],
+ ];
+ expect(generateMonthMatrix(2022, 11)).toEqual(expected);
+ });
+
+ it('returns the correct matrix for January 2025', () => {
const expected = [
[null, null, 1, 2, 3, 4, 5],
[6, 7, 8, 9, 10, 11, 12],
@@ -43,29 +76,41 @@ describe('generateMonthMatrix', () => {
[20, 21, 22, 23, 24, 25, 26],
[27, 28, 29, 30, 31, null, null],
];
- expect(generateMonthMatrix(2022, 2)).toEqual(expected);
+ expect(generateMonthMatrix(2025, 0)).toEqual(expected);
});
- it('returns the correct matrix for April 2022', () => {
+ it('returns the correct matrix for February 2025', () => {
const expected = [
[null, null, null, null, null, 1, 2],
[3, 4, 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14, 15, 16],
[17, 18, 19, 20, 21, 22, 23],
- [24, 25, 26, 27, 28, 29, 30],
+ [24, 25, 26, 27, 28, null, null],
];
- expect(generateMonthMatrix(2022, 3)).toEqual(expected);
+ expect(generateMonthMatrix(2025, 1)).toEqual(expected);
});
- it('returns the correct matrix for December 2022', () => {
+ it('returns the correct matrix for June 2025', () => {
const expected = [
- [null, null, null, null, 1, 2, 3],
- [4, 5, 6, 7, 8, 9, 10],
- [11, 12, 13, 14, 15, 16, 17],
- [18, 19, 20, 21, 22, 23, 24],
- [25, 26, 27, 28, 29, 30, 31],
+ [null, null, null, null, null, null, 1],
+ [2, 3, 4, 5, 6, 7, 8],
+ [9, 10, 11, 12, 13, 14, 15],
+ [16, 17, 18, 19, 20, 21, 22],
+ [23, 24, 25, 26, 27, 28, 29],
+ [30, null, null, null, null, null, null],
];
- expect(generateMonthMatrix(2022, 11)).toEqual(expected);
+ expect(generateMonthMatrix(2025, 5)).toEqual(expected);
+ });
+
+ it('returns the correct matrix for December 2025', () => {
+ const expected = [
+ [1, 2, 3, 4, 5, 6, 7],
+ [8, 9, 10, 11, 12, 13, 14],
+ [15, 16, 17, 18, 19, 20, 21],
+ [22, 23, 24, 25, 26, 27, 28],
+ [29, 30, 31, null, null, null, null],
+ ];
+ expect(generateMonthMatrix(2025, 11)).toEqual(expected);
});
it('throws an error if month is less than 0', () => {
diff --git a/workflow_tests/assertions/platformDeployAssertions.js b/workflow_tests/assertions/platformDeployAssertions.js
index 3aa00da33cf5..a4c597621577 100644
--- a/workflow_tests/assertions/platformDeployAssertions.js
+++ b/workflow_tests/assertions/platformDeployAssertions.js
@@ -170,7 +170,8 @@ const assertIOSJobExecuted = (workflowResult, didExecute = true, isProduction =
{key: 'max_attempts', value: '5'},
{key: 'command', value: 'cd ios && bundle exec pod install'},
]),
- utils.createStepAssertion('Decrypt profile', true, null, 'IOS', 'Decrypting profile', null, [{key: 'LARGE_SECRET_PASSPHRASE', value: '***'}]),
+ utils.createStepAssertion('Decrypt AppStore profile', true, null, 'IOS', 'Decrypting profile', null, [{key: 'LARGE_SECRET_PASSPHRASE', value: '***'}]),
+ utils.createStepAssertion('Decrypt AppStore Notification Service profile', true, null, 'IOS', 'Decrypting profile', null, [{key: 'LARGE_SECRET_PASSPHRASE', value: '***'}]),
utils.createStepAssertion('Decrypt certificate', true, null, 'IOS', 'Decrypting certificate', null, [{key: 'LARGE_SECRET_PASSPHRASE', value: '***'}]),
utils.createStepAssertion('Decrypt App Store Connect API key', true, null, 'IOS', 'Decrypting App Store API key', null, [{key: 'LARGE_SECRET_PASSPHRASE', value: '***'}]),
];
diff --git a/workflow_tests/assertions/testBuildAssertions.js b/workflow_tests/assertions/testBuildAssertions.js
index 06f8ae617509..3fe963f3aa35 100644
--- a/workflow_tests/assertions/testBuildAssertions.js
+++ b/workflow_tests/assertions/testBuildAssertions.js
@@ -186,7 +186,16 @@ const assertIOSJobExecuted = (workflowResult, ref = '', didExecute = true, fails
],
[],
),
- utils.createStepAssertion('Decrypt profile', true, null, 'IOS', 'Decrypt profile', [], [{key: 'LARGE_SECRET_PASSPHRASE', value: '***'}]),
+ utils.createStepAssertion('Decrypt AdHoc profile', true, null, 'IOS', 'Decrypt AdHoc profile', [], [{key: 'LARGE_SECRET_PASSPHRASE', value: '***'}]),
+ utils.createStepAssertion(
+ 'Decrypt AdHoc Notification Service profile',
+ true,
+ null,
+ 'IOS',
+ 'Decrypt AdHoc Notification Service profile',
+ [],
+ [{key: 'LARGE_SECRET_PASSPHRASE', value: '***'}],
+ ),
utils.createStepAssertion('Decrypt certificate', true, null, 'IOS', 'Decrypt certificate', [], [{key: 'LARGE_SECRET_PASSPHRASE', value: '***'}]),
utils.createStepAssertion(
'Configure AWS Credentials',
diff --git a/workflow_tests/mocks/platformDeployMocks.js b/workflow_tests/mocks/platformDeployMocks.js
index 3a5fe639952d..3fe8de8cca3d 100644
--- a/workflow_tests/mocks/platformDeployMocks.js
+++ b/workflow_tests/mocks/platformDeployMocks.js
@@ -123,7 +123,10 @@ const PLATFORM_DEPLOY__IOS__COMPARE_PODFILE_AND_MANIFEST__STEP_MOCK = utils.crea
{IS_PODFILE_SAME_AS_MANIFEST: false},
);
const PLATFORM_DEPLOY__IOS__COCOAPODS__STEP_MOCK = utils.createMockStep('Install cocoapods', 'Installing cocoapods', 'IOS', ['timeout_minutes', 'max_attempts', 'command']);
-const PLATFORM_DEPLOY__IOS__DECRYPT_PROFILE__STEP_MOCK = utils.createMockStep('Decrypt profile', 'Decrypting profile', 'IOS', null, ['LARGE_SECRET_PASSPHRASE']);
+const PLATFORM_DEPLOY__IOS__DECRYPT_APPSTORE_PROFILE__STEP_MOCK = utils.createMockStep('Decrypt AppStore profile', 'Decrypting profile', 'IOS', null, ['LARGE_SECRET_PASSPHRASE']);
+const PLATFORM_DEPLOY__IOS__DECRYPT_APPSTORE_NSE_PROFILE__STEP_MOCK = utils.createMockStep('Decrypt AppStore Notification Service profile', 'Decrypting profile', 'IOS', null, [
+ 'LARGE_SECRET_PASSPHRASE',
+]);
const PLATFORM_DEPLOY__IOS__DECRYPT_CERTIFICATE__STEP_MOCK = utils.createMockStep('Decrypt certificate', 'Decrypting certificate', 'IOS', null, ['LARGE_SECRET_PASSPHRASE']);
const PLATFORM_DEPLOY__IOS__DECRYPT_APP_STORE_API_KEY__STEP_MOCK = utils.createMockStep('Decrypt App Store Connect API key', 'Decrypting App Store API key', 'IOS', null, [
'LARGE_SECRET_PASSPHRASE',
@@ -159,7 +162,8 @@ const PLATFORM_DEPLOY__IOS__STEP_MOCKS = [
PLATFORM_DEPLOY__IOS__CACHE_POD_DEPENDENCIES__STEP_MOCK,
PLATFORM_DEPLOY__IOS__COMPARE_PODFILE_AND_MANIFEST__STEP_MOCK,
PLATFORM_DEPLOY__IOS__COCOAPODS__STEP_MOCK,
- PLATFORM_DEPLOY__IOS__DECRYPT_PROFILE__STEP_MOCK,
+ PLATFORM_DEPLOY__IOS__DECRYPT_APPSTORE_PROFILE__STEP_MOCK,
+ PLATFORM_DEPLOY__IOS__DECRYPT_APPSTORE_NSE_PROFILE__STEP_MOCK,
PLATFORM_DEPLOY__IOS__DECRYPT_CERTIFICATE__STEP_MOCK,
PLATFORM_DEPLOY__IOS__DECRYPT_APP_STORE_API_KEY__STEP_MOCK,
PLATFORM_DEPLOY__IOS__FASTLANE__STEP_MOCK,
diff --git a/workflow_tests/mocks/testBuildMocks.js b/workflow_tests/mocks/testBuildMocks.js
index 270f8b5d8a06..37bcc5fb6fac 100644
--- a/workflow_tests/mocks/testBuildMocks.js
+++ b/workflow_tests/mocks/testBuildMocks.js
@@ -116,7 +116,14 @@ const TESTBUILD__IOS__COMPARE_PODFILE_AND_MANIFEST__STEP_MOCK = utils.createMock
IS_PODFILE_SAME_AS_MANIFEST: false,
});
const TESTBUILD__IOS__INSTALL_COCOAPODS__STEP_MOCK = utils.createMockStep('Install cocoapods', 'Install cocoapods', 'IOS', ['timeout_minutes', 'max_attempts', 'command'], []);
-const TESTBUILD__IOS__DECRYPT_PROFILE__STEP_MOCK = utils.createMockStep('Decrypt profile', 'Decrypt profile', 'IOS', [], ['LARGE_SECRET_PASSPHRASE']);
+const TESTBUILD__IOS__DECRYPT_ADHOC_PROFILE__STEP_MOCK = utils.createMockStep('Decrypt AdHoc profile', 'Decrypt AdHoc profile', 'IOS', [], ['LARGE_SECRET_PASSPHRASE']);
+const TESTBUILD__IOS__DECRYPT_ADHOC_NSE_PROFILE__STEP_MOCK = utils.createMockStep(
+ 'Decrypt AdHoc Notification Service profile',
+ 'Decrypt AdHoc Notification Service profile',
+ 'IOS',
+ [],
+ ['LARGE_SECRET_PASSPHRASE'],
+);
const TESTBUILD__IOS__DECRYPT_CERTIFICATE__STEP_MOCK = utils.createMockStep('Decrypt certificate', 'Decrypt certificate', 'IOS', [], ['LARGE_SECRET_PASSPHRASE']);
const TESTBUILD__IOS__CONFIGURE_AWS_CREDENTIALS__STEP_MOCK = utils.createMockStep(
'Configure AWS Credentials',
@@ -137,7 +144,8 @@ const TESTBUILD__IOS__STEP_MOCKS = [
TESTBUILD__IOS__CACHE_POD_DEPENDENCIES__STEP_MOCK,
TESTBUILD__IOS__COMPARE_PODFILE_AND_MANIFEST__STEP_MOCK,
TESTBUILD__IOS__INSTALL_COCOAPODS__STEP_MOCK,
- TESTBUILD__IOS__DECRYPT_PROFILE__STEP_MOCK,
+ TESTBUILD__IOS__DECRYPT_ADHOC_PROFILE__STEP_MOCK,
+ TESTBUILD__IOS__DECRYPT_ADHOC_NSE_PROFILE__STEP_MOCK,
TESTBUILD__IOS__DECRYPT_CERTIFICATE__STEP_MOCK,
TESTBUILD__IOS__CONFIGURE_AWS_CREDENTIALS__STEP_MOCK,
TESTBUILD__IOS__RUN_FASTLANE__STEP_MOCK,