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/e2ePerformanceTests.yml b/.github/workflows/e2ePerformanceTests.yml
index 09113bdcc548..75c0b8497bdd 100644
--- a/.github/workflows/e2ePerformanceTests.yml
+++ b/.github/workflows/e2ePerformanceTests.yml
@@ -194,7 +194,7 @@ jobs:
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- aws-region: us-west-1
+ aws-region: us-west-2
- name: Schedule AWS Device Farm test run on main branch
uses: realm/aws-devicefarm/test-application@7b9a91236c456c97e28d384c9e476035d5ea686b
diff --git a/.github/workflows/platformDeploy.yml b/.github/workflows/platformDeploy.yml
index faf06cacc62f..6aeecb3b4e05 100644
--- a/.github/workflows/platformDeploy.yml
+++ b/.github/workflows/platformDeploy.yml
@@ -10,7 +10,7 @@ on:
env:
SHOULD_DEPLOY_PRODUCTION: ${{ github.event_name == 'release' }}
- DEVELOPER_DIR: /Applications/Xcode_14.1.app/Contents/Developer
+ DEVELOPER_DIR: /Applications/Xcode_15.0.1.app/Contents/Developer
concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}
@@ -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..8b18b8aa5d53 100644
--- a/.github/workflows/testBuild.yml
+++ b/.github/workflows/testBuild.yml
@@ -11,7 +11,7 @@ on:
branches: ['*ci-test/**']
env:
- DEVELOPER_DIR: /Applications/Xcode_14.2.app/Contents/Developer
+ DEVELOPER_DIR: /Applications/Xcode_15.0.1.app/Contents/Developer
jobs:
validateActor:
@@ -159,7 +159,7 @@ jobs:
uses: ./.github/actions/composite/setupNode
- name: Setup XCode
- run: sudo xcode-select -switch /Applications/Xcode_14.2.app
+ run: sudo xcode-select -switch /Applications/Xcode_15.0.1.app
- name: Setup Ruby
uses: ruby/setup-ruby@a05e47355e80e57b9a67566a813648fa67d92011
@@ -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/.github/workflows/testGithubActionsWorkflows.yml b/.github/workflows/testGithubActionsWorkflows.yml
index 58de3ba2d9f3..d052b343cf0c 100644
--- a/.github/workflows/testGithubActionsWorkflows.yml
+++ b/.github/workflows/testGithubActionsWorkflows.yml
@@ -4,7 +4,7 @@ on:
workflow_dispatch:
workflow_call:
pull_request:
- types: [opened, reopened, edited, synchronize]
+ types: [opened, reopened, synchronize]
branches-ignore: [staging, production]
paths: ['.github/**']
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..e93150b48fca 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 1001042101
+ versionName "1.4.21-1"
}
flavorDimensions "default"
diff --git a/assets/images/product-illustrations/telescope.svg b/assets/images/product-illustrations/telescope.svg
new file mode 100644
index 000000000000..95617c801789
--- /dev/null
+++ b/assets/images/product-illustrations/telescope.svg
@@ -0,0 +1,79 @@
+
+
+
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/docs/_data/_routes.yml b/docs/_data/_routes.yml
index 4246b23982ec..0887e90dcc8b 100644
--- a/docs/_data/_routes.yml
+++ b/docs/_data/_routes.yml
@@ -113,8 +113,8 @@ platforms:
icon: /assets/images/hand-card.svg
description: Explore how the Expensify Card combines convenience and security to enhance everyday business transactions. Discover how to apply for, oversee, and maximize your card perks here.
- - href: get-paid-back
- title: Get Paid Back
+ - href: payments
+ title: Payments
icon: /assets/images/money-into-wallet.svg
description: Whether you submit an expense report or an invoice, find out here how to ensure a smooth and timely payback process every time.
diff --git a/docs/articles/expensify-classic/integrations/accounting-integrations/Certinia.md b/docs/articles/expensify-classic/integrations/accounting-integrations/Certinia.md
index 65361ba1af9a..0856e2694340 100644
--- a/docs/articles/expensify-classic/integrations/accounting-integrations/Certinia.md
+++ b/docs/articles/expensify-classic/integrations/accounting-integrations/Certinia.md
@@ -3,7 +3,7 @@ title: Certinia
description: Guide to connecting Expensify and Certinia FFA and PSA/SRP (formerly known as FinancialForce)
---
# Overview
-[Cetinia](https://use.expensify.com/financialforce) (Formerly known as FinancialForce)is a cloud-based software solution that provides a range of financial management and accounting applications built on the Salesforce platform. There are two versions: PSA/SRP and FFA and we support both.
+[Cetinia](https://use.expensify.com/financialforce) (formerly known as FinancialForce) is a cloud-based software solution that provides a range of financial management and accounting applications built on the Salesforce platform. There are two versions: PSA/SRP and FFA and we support both.
# Before connecting to Certinia
Install the Expensify bundle in Certinia using the relevant installer:
diff --git a/docs/articles/new-expensify/get-paid-back/Distance-Requests.md b/docs/articles/new-expensify/payments/Distance-Requests.md
similarity index 100%
rename from docs/articles/new-expensify/get-paid-back/Distance-Requests.md
rename to docs/articles/new-expensify/payments/Distance-Requests.md
diff --git a/docs/articles/new-expensify/get-paid-back/Referral-Program.md b/docs/articles/new-expensify/payments/Referral-Program.md
similarity index 100%
rename from docs/articles/new-expensify/get-paid-back/Referral-Program.md
rename to docs/articles/new-expensify/payments/Referral-Program.md
diff --git a/docs/articles/new-expensify/get-paid-back/Request-Money.md b/docs/articles/new-expensify/payments/Request-Money.md
similarity index 100%
rename from docs/articles/new-expensify/get-paid-back/Request-Money.md
rename to docs/articles/new-expensify/payments/Request-Money.md
diff --git a/docs/new-expensify/hubs/get-paid-back/index.html b/docs/new-expensify/hubs/payments/index.html
similarity index 69%
rename from docs/new-expensify/hubs/get-paid-back/index.html
rename to docs/new-expensify/hubs/payments/index.html
index 1f84c1510b92..d22f7e375f2e 100644
--- a/docs/new-expensify/hubs/get-paid-back/index.html
+++ b/docs/new-expensify/hubs/payments/index.html
@@ -1,6 +1,6 @@
---
layout: default
-title: Get Paid Back
+title: Payments
---
{% include hub.html %}
\ No newline at end of file
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..f267261a49c0 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -19,7 +19,7 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 1.4.17
+ 1.4.21
CFBundleSignature
????
CFBundleURLTypes
@@ -40,7 +40,7 @@
CFBundleVersion
- 1.4.17.7
+ 1.4.21.1
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..f95a3f871d4c 100644
--- a/ios/NewExpensifyTests/Info.plist
+++ b/ios/NewExpensifyTests/Info.plist
@@ -15,10 +15,10 @@
CFBundlePackageType
BNDL
CFBundleShortVersionString
- 1.4.17
+ 1.4.21
CFBundleSignature
????
CFBundleVersion
- 1.4.17.7
+ 1.4.21.1
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..77c390c46416 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)
@@ -801,35 +802,10 @@ PODS:
- React-Core
- RNReactNativeHapticFeedback (1.14.0):
- React-Core
- - RNReanimated (3.5.4):
- - DoubleConversion
- - FBLazyVector
- - glog
- - hermes-engine
- - RCT-Folly
- - RCTRequired
- - RCTTypeSafety
- - React-callinvoker
+ - RNReanimated (3.6.1):
+ - RCT-Folly (= 2021.07.22.00)
- React-Core
- - React-Core/DevSupport
- - React-Core/RCTWebSocket
- - React-CoreModules
- - React-cxxreact
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-jsinspector
- - React-RCTActionSheet
- - React-RCTAnimation
- - React-RCTAppDelegate
- - React-RCTBlob
- - React-RCTImage
- - React-RCTLinking
- - React-RCTNetwork
- - React-RCTSettings
- - React-RCTText
- ReactCommon/turbomodule/core
- - Yoga
- RNScreens (3.21.0):
- React-Core
- React-RCTImage
@@ -857,6 +833,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 +951,7 @@ SPEC REPOS:
trunk:
- Airship
- AirshipFrameworkProxy
+ - AirshipServiceExtension
- AppAuth
- CocoaAsyncSocket
- Firebase
@@ -1205,6 +1183,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
Airship: 2f4510b497a8200780752a5e0304a9072bfffb6d
AirshipFrameworkProxy: ea1b6c665c798637b93c465b5e505be3011f1d9d
+ AirshipServiceExtension: 89c6e25a69f3458d9dbd581c700cffb196b61930
AppAuth: 3bb1d1cd9340bd09f5ed189fb00b1cc28e1e8570
boost: 57d2868c099736d80fcd648bf211b4431e51a558
BVLinearGradient: 421743791a59d259aec53f4c58793aad031da2ca
@@ -1329,7 +1308,7 @@ SPEC CHECKSUMS:
rnmapbox-maps: 6f638ec002aa6e906a6f766d69cd45f968d98e64
RNPermissions: 9b086c8f05b2e2faa587fdc31f4c5ab4509728aa
RNReactNativeHapticFeedback: 1e3efeca9628ff9876ee7cdd9edec1b336913f8c
- RNReanimated: ab2e96c6d5591c3dfbb38a464f54c8d17fb34a87
+ RNReanimated: fdbaa9c964bbab7fac50c90862b6cc5f041679b9
RNScreens: d037903436160a4b039d32606668350d2a808806
RNSVG: d00c8f91c3cbf6d476451313a18f04d220d4f396
SDWebImage: 750adf017a315a280c60fde706ab1e552a3ae4e9
@@ -1342,6 +1321,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..c8b4b2ba2082 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "new.expensify",
- "version": "1.4.17-7",
+ "version": "1.4.21-1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "new.expensify",
- "version": "1.4.17-7",
+ "version": "1.4.21-1",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@@ -103,7 +103,7 @@
"react-native-plaid-link-sdk": "10.8.0",
"react-native-qrcode-svg": "^6.2.0",
"react-native-quick-sqlite": "^8.0.0-beta.2",
- "react-native-reanimated": "3.5.4",
+ "react-native-reanimated": "^3.6.1",
"react-native-render-html": "6.3.1",
"react-native-safe-area-context": "4.4.1",
"react-native-screens": "3.21.0",
@@ -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",
@@ -47677,9 +47677,9 @@
}
},
"node_modules/react-native-reanimated": {
- "version": "3.5.4",
- "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.5.4.tgz",
- "integrity": "sha512-8we9LLDO1o4Oj9/DICeEJ2K1tjfqkJagqQUglxeUAkol/HcEJ6PGxIrpBcNryLqCDYEcu6FZWld/FzizBIw6bg==",
+ "version": "3.6.1",
+ "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.6.1.tgz",
+ "integrity": "sha512-F4vG9Yf9PKmE3GaWtVGUpzj3SM6YY2cx1yRHCwiMd1uY7W0gU017LfcVUorboJnj0y5QZqEriEK1Usq2Y8YZqg==",
"dependencies": {
"@babel/plugin-transform-object-assign": "^7.16.7",
"@babel/preset-typescript": "^7.16.7",
@@ -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",
@@ -90489,9 +90489,9 @@
"requires": {}
},
"react-native-reanimated": {
- "version": "3.5.4",
- "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.5.4.tgz",
- "integrity": "sha512-8we9LLDO1o4Oj9/DICeEJ2K1tjfqkJagqQUglxeUAkol/HcEJ6PGxIrpBcNryLqCDYEcu6FZWld/FzizBIw6bg==",
+ "version": "3.6.1",
+ "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.6.1.tgz",
+ "integrity": "sha512-F4vG9Yf9PKmE3GaWtVGUpzj3SM6YY2cx1yRHCwiMd1uY7W0gU017LfcVUorboJnj0y5QZqEriEK1Usq2Y8YZqg==",
"requires": {
"@babel/plugin-transform-object-assign": "^7.16.7",
"@babel/preset-typescript": "^7.16.7",
diff --git a/package.json b/package.json
index f48b1e0fda2e..bd4b8ffacedf 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "1.4.17-7",
+ "version": "1.4.21-1",
"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.",
@@ -151,7 +151,7 @@
"react-native-plaid-link-sdk": "10.8.0",
"react-native-qrcode-svg": "^6.2.0",
"react-native-quick-sqlite": "^8.0.0-beta.2",
- "react-native-reanimated": "3.5.4",
+ "react-native-reanimated": "^3.6.1",
"react-native-render-html": "6.3.1",
"react-native-safe-area-context": "4.4.1",
"react-native-screens": "3.21.0",
@@ -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 3527902dff40..cd4b4f693fa0 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',
@@ -593,6 +594,7 @@ const CONST = {
JOIN_ROOM: 'JOINROOM',
},
},
+ THREAD_DISABLED: ['CREATED'],
},
ARCHIVE_REASON: {
DEFAULT: 'default',
@@ -717,6 +719,7 @@ const CONST = {
TRIE_INITIALIZATION: 'trie_initialization',
COMMENT_LENGTH_DEBOUNCE_TIME: 500,
SEARCH_OPTION_LIST_DEBOUNCE_TIME: 300,
+ RESIZE_DEBOUNCE_TIME: 100,
},
PRIORITY_MODE: {
GSD: 'gsd',
@@ -820,6 +823,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},
@@ -850,7 +854,7 @@ const CONST = {
// It's copied here so that the same regex pattern can be used in form validations to be consistent with the server.
VALIDATE_FOR_HTML_TAG_REGEX: /<([^>\s]+)(?:[^>]*?)>/g,
- VALIDATE_FOR_LEADINGSPACES_HTML_TAG_REGEX: /<([\s]+[\s\w~!@#$%^&*(){}[\];':"`|?.,/\\+\-=<]+.*[\s]*)>/g,
+ VALIDATE_FOR_LEADINGSPACES_HTML_TAG_REGEX: /<([\s]+.+[\s]*)>/g,
WHITELISTED_TAGS: [/<>/, /< >/, /<->/, /<-->/, /
/, /
/],
@@ -976,6 +980,7 @@ const CONST = {
CHAT_FOOTER_SECONDARY_ROW_HEIGHT: 15,
CHAT_FOOTER_SECONDARY_ROW_PADDING: 5,
CHAT_FOOTER_MIN_HEIGHT: 65,
+ CHAT_FOOTER_HORIZONTAL_PADDING: 40,
CHAT_SKELETON_VIEW: {
AVERAGE_ROW_HEIGHT: 80,
HEIGHT_FOR_ROW_COUNT: {
@@ -1171,6 +1176,7 @@ const CONST = {
EXPENSIFY: 'Expensify',
VBBA: 'ACH',
},
+ DEFAULT_AMOUNT: 0,
TYPE: {
SEND: 'send',
SPLIT: 'split',
@@ -1296,6 +1302,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/ROUTES.ts b/src/ROUTES.ts
index 8bcbeb6c18d6..90c0114eaa56 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -372,6 +372,12 @@ const ROUTES = {
getRoute: (iouType: ValueOf, transactionID: string, reportID: string, pageIndex = '', backTo = '') =>
getUrlWithBackToParam(`create/${iouType}/waypoint/${transactionID}/${reportID}/${pageIndex}`, backTo),
},
+ // This URL is used as a redirect to one of the create tabs below. This is so that we can message users with a link
+ // straight to those flows without needing to have optimistic transaction and report IDs.
+ MONEY_REQUEST_START: {
+ route: 'start/:iouType/:iouRequestType',
+ getRoute: (iouType: ValueOf, iouRequestType: ValueOf) => `start/${iouType}/${iouRequestType}` as const,
+ },
MONEY_REQUEST_CREATE_TAB_DISTANCE: {
route: 'create/:iouType/start/:transactionID/:reportID/distance',
getRoute: (iouType: ValueOf, transactionID: string, reportID: string) => `create/${iouType}/start/${transactionID}/${reportID}/distance` as const,
diff --git a/src/SCREENS.ts b/src/SCREENS.ts
index 2cd263237866..c1d2059cd3b0 100644
--- a/src/SCREENS.ts
+++ b/src/SCREENS.ts
@@ -118,6 +118,7 @@ const SCREENS = {
DISTANCE_TAB: 'distance',
CREATE: 'Money_Request_Create',
STEP_CONFIRMATION: 'Money_Request_Step_Confirmation',
+ START: 'Money_Request_Start',
STEP_AMOUNT: 'Money_Request_Step_Amount',
STEP_CATEGORY: 'Money_Request_Step_Category',
STEP_CURRENCY: 'Money_Request_Step_Currency',
diff --git a/src/components/AddressSearch/index.js b/src/components/AddressSearch/index.js
index d9e4ef2c0f6e..31b04a3d954f 100644
--- a/src/components/AddressSearch/index.js
+++ b/src/components/AddressSearch/index.js
@@ -111,6 +111,9 @@ const propTypes = {
/** Information about the network */
network: networkPropTypes.isRequired,
+ /** Location bias for querying search results. */
+ locationBias: PropTypes.string,
+
...withLocalizePropTypes,
};
@@ -138,6 +141,7 @@ const defaultProps = {
maxInputLength: undefined,
predefinedPlaces: [],
resultTypes: 'address',
+ locationBias: undefined,
};
function AddressSearch({
@@ -162,6 +166,7 @@ function AddressSearch({
shouldSaveDraft,
translate,
value,
+ locationBias,
}) {
const theme = useTheme();
const styles = useThemeStyles();
@@ -179,11 +184,11 @@ function AddressSearch({
language: preferredLocale,
types: resultTypes,
components: isLimitedToUSA ? 'country:us' : undefined,
+ ...(locationBias && {locationbias: locationBias}),
}),
- [preferredLocale, resultTypes, isLimitedToUSA],
+ [preferredLocale, resultTypes, isLimitedToUSA, locationBias],
);
const shouldShowCurrentLocationButton = canUseCurrentLocation && searchValue.trim().length === 0 && isFocused;
-
const saveLocationDetails = (autocompleteData, details) => {
const addressComponents = details.address_components;
if (!addressComponents) {
@@ -192,7 +197,7 @@ function AddressSearch({
// amount of data massaging needs to happen for what the parent expects to get from this function.
if (_.size(details)) {
onPress({
- address: lodashGet(details, 'description'),
+ address: autocompleteData.description || lodashGet(details, 'description', ''),
lat: lodashGet(details, 'geometry.location.lat', 0),
lng: lodashGet(details, 'geometry.location.lng', 0),
name: lodashGet(details, 'name'),
@@ -261,7 +266,7 @@ function AddressSearch({
lat: lodashGet(details, 'geometry.location.lat', 0),
lng: lodashGet(details, 'geometry.location.lng', 0),
- address: lodashGet(details, 'formatted_address', ''),
+ address: autocompleteData.description || lodashGet(details, 'formatted_address', ''),
};
// If the address is not in the US, use the full length state name since we're displaying the address's
diff --git a/src/components/ArchivedReportFooter.tsx b/src/components/ArchivedReportFooter.tsx
index 8604d20130c7..2dae84106971 100644
--- a/src/components/ArchivedReportFooter.tsx
+++ b/src/components/ArchivedReportFooter.tsx
@@ -30,14 +30,14 @@ function ArchivedReportFooter({report, reportClosedAction, personalDetails = {}}
const originalMessage = reportClosedAction?.actionName === CONST.REPORT.ACTIONS.TYPE.CLOSED ? reportClosedAction.originalMessage : null;
const archiveReason = originalMessage?.reason ?? CONST.REPORT.ARCHIVE_REASON.DEFAULT;
- let displayName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails?.[report?.ownerAccountID ?? 0]?.displayName);
+ let displayName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails?.[report?.ownerAccountID ?? 0]);
let oldDisplayName: string | undefined;
if (archiveReason === CONST.REPORT.ARCHIVE_REASON.ACCOUNT_MERGED) {
const newAccountID = originalMessage?.newAccountID;
const oldAccountID = originalMessage?.oldAccountID;
- displayName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails?.[newAccountID ?? 0]?.displayName);
- oldDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails?.[oldAccountID ?? 0]?.displayName);
+ displayName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails?.[newAccountID ?? 0]);
+ oldDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails?.[oldAccountID ?? 0]);
}
const shouldRenderHTML = archiveReason !== CONST.REPORT.ARCHIVE_REASON.DEFAULT;
diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js
index 863e59aa4474..51912c04eb31 100755
--- a/src/components/AttachmentModal.js
+++ b/src/components/AttachmentModal.js
@@ -358,6 +358,14 @@ function AttachmentModal(props) {
setIsModalOpen(true);
}, []);
+ useEffect(() => {
+ setSource(props.source);
+ }, [props.source]);
+
+ useEffect(() => {
+ setIsAuthTokenRequired(props.isAuthTokenRequired);
+ }, [props.isAuthTokenRequired]);
+
const sourceForAttachmentView = props.source || source;
const threeDotsMenuItems = useMemo(() => {
@@ -368,7 +376,7 @@ function AttachmentModal(props) {
const parentReportAction = props.parentReportActions[props.report.parentReportActionID];
const canEdit =
- ReportUtils.canEditFieldOfMoneyRequest(parentReportAction, props.parentReport.reportID, CONST.EDIT_REQUEST_FIELD.RECEIPT) &&
+ ReportUtils.canEditFieldOfMoneyRequest(parentReportAction, props.parentReport.reportID, CONST.EDIT_REQUEST_FIELD.RECEIPT, props.transaction) &&
!TransactionUtils.isDistanceRequest(props.transaction);
if (canEdit) {
menuItems.push({
@@ -396,7 +404,7 @@ function AttachmentModal(props) {
}
return menuItems;
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [props.isReceiptAttachment, props.parentReport, props.parentReportActions, props.policy, props.transaction, file]);
+ }, [props.isReceiptAttachment, props.parentReport, props.parentReportActions, props.policy, props.transaction, file, source]);
// There are a few things that shouldn't be set until we absolutely know if the file is a receipt or an attachment.
// props.isReceiptAttachment will be null until its certain what the file is, in which case it will then be true|false.
diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js
index c01843c7bcb3..b0060afdb813 100755
--- a/src/components/Attachments/AttachmentView/index.js
+++ b/src/components/Attachments/AttachmentView/index.js
@@ -146,6 +146,7 @@ function AttachmentView({
onLoadComplete={() => !loadComplete && setLoadComplete(true)}
errorLabelStyles={isUsedInAttachmentModal ? [styles.textLabel, styles.textLarge] : [styles.cursorAuto]}
style={isUsedInAttachmentModal ? styles.imageModalPDF : styles.flex1}
+ isUsedInCarousel={isUsedInCarousel}
/>
);
diff --git a/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx b/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx
index c2320f7c0202..b72662f989dc 100644
--- a/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx
+++ b/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx
@@ -1,5 +1,5 @@
import {FlashList} from '@shopify/flash-list';
-import React, {ForwardedRef, forwardRef, ReactElement, useCallback, useEffect, useRef} from 'react';
+import React, {ForwardedRef, forwardRef, ReactElement, useCallback, useEffect, useMemo, useRef} from 'react';
import {View} from 'react-native';
// We take ScrollView from this package to properly handle the scrolling of AutoCompleteSuggestions in chats since one scroll is nested inside another
import {ScrollView} from 'react-native-gesture-handler';
@@ -8,6 +8,8 @@ import ColorSchemeWrapper from '@components/ColorSchemeWrapper';
import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';
+import useWindowDimensions from '@hooks/useWindowDimensions';
+import variables from '@styles/variables';
import CONST from '@src/CONST';
import viewForwardedRef from '@src/types/utils/viewForwardedRef';
import type {AutoCompleteSuggestionsProps, RenderSuggestionMenuItemProps} from './types';
@@ -39,6 +41,7 @@ function BaseAutoCompleteSuggestions(
}: AutoCompleteSuggestionsProps,
ref: ForwardedRef,
) {
+ const {windowWidth, isLargeScreenWidth} = useWindowDimensions();
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const rowHeight = useSharedValue(0);
@@ -64,7 +67,13 @@ function BaseAutoCompleteSuggestions(
const innerHeight = CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT * suggestions.length;
const animatedStyles = useAnimatedStyle(() => StyleUtils.getAutoCompleteSuggestionContainerStyle(rowHeight.value));
-
+ const estimatedListSize = useMemo(
+ () => ({
+ height: CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT * suggestions.length,
+ width: (isLargeScreenWidth ? windowWidth - variables.sideBarWidth : windowWidth) - CONST.CHAT_FOOTER_HORIZONTAL_PADDING,
+ }),
+ [isLargeScreenWidth, suggestions.length, windowWidth],
+ );
useEffect(() => {
rowHeight.value = withTiming(measureHeightOfSuggestionRows(suggestions.length, isSuggestionPickerLarge), {
duration: 100,
@@ -88,6 +97,7 @@ function BaseAutoCompleteSuggestions(
| ImageSourcePropType;
/** Color for the icon (should be from theme) */
- iconColor: PropTypes.string,
+ iconColor?: string;
/** Title message below the icon */
- title: PropTypes.string.isRequired,
+ title: string;
/** Subtitle message below the title */
- subtitle: PropTypes.string,
+ subtitle?: string;
/** Link message below the subtitle */
- linkKey: PropTypes.string,
+ linkKey?: TranslationPaths;
/** Whether we should show a link to navigate elsewhere */
- shouldShowLink: PropTypes.bool,
+ shouldShowLink?: boolean;
/** The custom icon width */
- iconWidth: PropTypes.number,
+ iconWidth?: number;
/** The custom icon height */
- iconHeight: PropTypes.number,
+ iconHeight?: number;
/** Function to call when pressing the navigation link */
- onLinkPress: PropTypes.func,
+ onLinkPress?: () => void;
/** Whether we should embed the link with subtitle */
- shouldEmbedLinkWithSubtitle: PropTypes.bool,
+ shouldEmbedLinkWithSubtitle?: boolean;
};
-const defaultProps = {
- iconColor: null,
- subtitle: '',
- shouldShowLink: false,
- linkKey: 'notFound.goBackHome',
- iconWidth: variables.iconSizeSuperLarge,
- iconHeight: variables.iconSizeSuperLarge,
- onLinkPress: () => Navigation.dismissModal(),
- shouldEmbedLinkWithSubtitle: false,
-};
-
-function BlockingView(props) {
+function BlockingView({
+ icon,
+ iconColor,
+ title,
+ subtitle = '',
+ linkKey = 'notFound.goBackHome',
+ shouldShowLink = false,
+ iconWidth = variables.iconSizeSuperLarge,
+ iconHeight = variables.iconSizeSuperLarge,
+ onLinkPress = () => Navigation.dismissModal(),
+ shouldEmbedLinkWithSubtitle = false,
+}: BlockingViewProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
function renderContent() {
@@ -62,14 +62,14 @@ function BlockingView(props) {
<>
- {props.shouldShowLink ? (
+ {shouldShowLink ? (
- {translate(props.linkKey)}
+ {translate(linkKey)}
) : null}
>
@@ -79,14 +79,14 @@ function BlockingView(props) {
return (
- {props.title}
+ {title}
- {props.shouldEmbedLinkWithSubtitle ? (
+ {shouldEmbedLinkWithSubtitle ? (
{renderContent()}
) : (
{renderContent()}
@@ -95,8 +95,6 @@ function BlockingView(props) {
);
}
-BlockingView.propTypes = propTypes;
-BlockingView.defaultProps = defaultProps;
BlockingView.displayName = 'BlockingView';
export default BlockingView;
diff --git a/src/components/BlockingViews/FullPageNotFoundView.js b/src/components/BlockingViews/FullPageNotFoundView.tsx
similarity index 69%
rename from src/components/BlockingViews/FullPageNotFoundView.js
rename to src/components/BlockingViews/FullPageNotFoundView.tsx
index ce76b96c0eb0..6d7f838bf6c2 100644
--- a/src/components/BlockingViews/FullPageNotFoundView.js
+++ b/src/components/BlockingViews/FullPageNotFoundView.tsx
@@ -1,4 +1,3 @@
-import PropTypes from 'prop-types';
import React from 'react';
import {View} from 'react-native';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
@@ -7,54 +6,54 @@ import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import variables from '@styles/variables';
+import {TranslationPaths} from '@src/languages/types';
import ROUTES from '@src/ROUTES';
import BlockingView from './BlockingView';
-const propTypes = {
+type FullPageNotFoundViewProps = {
/** Child elements */
- children: PropTypes.node,
+ children?: React.ReactNode;
/** If true, child components are replaced with a blocking "not found" view */
- shouldShow: PropTypes.bool,
+ shouldShow?: boolean;
/** The key in the translations file to use for the title */
- titleKey: PropTypes.string,
+ titleKey?: TranslationPaths;
/** The key in the translations file to use for the subtitle */
- subtitleKey: PropTypes.string,
+ subtitleKey?: TranslationPaths;
/** Whether we should show a link to navigate elsewhere */
- shouldShowLink: PropTypes.bool,
+ shouldShowLink?: boolean;
/** Whether we should show the back button on the header */
- shouldShowBackButton: PropTypes.bool,
+ shouldShowBackButton?: boolean;
/** The key in the translations file to use for the go back link */
- linkKey: PropTypes.string,
+ linkKey?: TranslationPaths;
/** Method to trigger when pressing the back button of the header */
- onBackButtonPress: PropTypes.func,
+ onBackButtonPress: () => void;
/** Function to call when pressing the navigation link */
- onLinkPress: PropTypes.func,
-};
-
-const defaultProps = {
- children: null,
- shouldShow: false,
- titleKey: 'notFound.notHere',
- subtitleKey: 'notFound.pageNotFound',
- linkKey: 'notFound.goBackHome',
- onBackButtonPress: () => Navigation.goBack(ROUTES.HOME),
- shouldShowLink: true,
- shouldShowBackButton: true,
- onLinkPress: () => Navigation.dismissModal(),
+ onLinkPress: () => void;
};
// eslint-disable-next-line rulesdir/no-negated-variables
-function FullPageNotFoundView({children, shouldShow, titleKey, subtitleKey, linkKey, onBackButtonPress, shouldShowLink, shouldShowBackButton, onLinkPress}) {
+function FullPageNotFoundView({
+ children = null,
+ shouldShow = false,
+ titleKey = 'notFound.notHere',
+ subtitleKey = 'notFound.pageNotFound',
+ linkKey = 'notFound.goBackHome',
+ onBackButtonPress = () => Navigation.goBack(ROUTES.HOME),
+ shouldShowLink = true,
+ shouldShowBackButton = true,
+ onLinkPress = () => Navigation.dismissModal(),
+}: FullPageNotFoundViewProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
+
if (shouldShow) {
return (
<>
@@ -81,8 +80,6 @@ function FullPageNotFoundView({children, shouldShow, titleKey, subtitleKey, link
return children;
}
-FullPageNotFoundView.propTypes = propTypes;
-FullPageNotFoundView.defaultProps = defaultProps;
FullPageNotFoundView.displayName = 'FullPageNotFoundView';
export default FullPageNotFoundView;
diff --git a/src/components/BlockingViews/FullPageOfflineBlockingView.js b/src/components/BlockingViews/FullPageOfflineBlockingView.js
deleted file mode 100644
index adbda21456dc..000000000000
--- a/src/components/BlockingViews/FullPageOfflineBlockingView.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import PropTypes from 'prop-types';
-import React from 'react';
-import * as Expensicons from '@components/Icon/Expensicons';
-import networkPropTypes from '@components/networkPropTypes';
-import {withNetwork} from '@components/OnyxProvider';
-import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
-import useTheme from '@hooks/useTheme';
-import compose from '@libs/compose';
-import BlockingView from './BlockingView';
-
-const propTypes = {
- /** Child elements */
- children: PropTypes.node.isRequired,
-
- /** Props to fetch translation features */
- ...withLocalizePropTypes,
-
- /** Props to detect online status */
- network: networkPropTypes.isRequired,
-};
-
-function FullPageOfflineBlockingView(props) {
- const theme = useTheme();
-
- if (props.network.isOffline) {
- return (
-
- );
- }
-
- return props.children;
-}
-
-FullPageOfflineBlockingView.propTypes = propTypes;
-FullPageOfflineBlockingView.displayName = 'FullPageOfflineBlockingView';
-
-export default compose(withLocalize, withNetwork())(FullPageOfflineBlockingView);
diff --git a/src/components/BlockingViews/FullPageOfflineBlockingView.tsx b/src/components/BlockingViews/FullPageOfflineBlockingView.tsx
new file mode 100644
index 000000000000..a9ebcf969ae5
--- /dev/null
+++ b/src/components/BlockingViews/FullPageOfflineBlockingView.tsx
@@ -0,0 +1,31 @@
+import React from 'react';
+import * as Expensicons from '@components/Icon/Expensicons';
+import useLocalize from '@hooks/useLocalize';
+import useNetwork from '@hooks/useNetwork';
+import useTheme from '@hooks/useTheme';
+import ChildrenProps from '@src/types/utils/ChildrenProps';
+import BlockingView from './BlockingView';
+
+function FullPageOfflineBlockingView({children}: ChildrenProps) {
+ const {translate} = useLocalize();
+ const {isOffline} = useNetwork();
+
+ const theme = useTheme();
+
+ if (isOffline) {
+ return (
+
+ );
+ }
+
+ return children;
+}
+
+FullPageOfflineBlockingView.displayName = 'FullPageOfflineBlockingView';
+
+export default FullPageOfflineBlockingView;
diff --git a/src/components/Checkbox.tsx b/src/components/Checkbox.tsx
index 715603ea362e..ac18b550501d 100644
--- a/src/components/Checkbox.tsx
+++ b/src/components/Checkbox.tsx
@@ -1,4 +1,4 @@
-import React, {ForwardedRef, forwardRef, KeyboardEvent as ReactKeyboardEvent} from 'react';
+import React, {type ForwardedRef, forwardRef, type MouseEventHandler, type KeyboardEvent as ReactKeyboardEvent} from 'react';
import {GestureResponderEvent, StyleProp, View, ViewStyle} from 'react-native';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
@@ -29,7 +29,7 @@ type CheckboxProps = Partial & {
containerStyle?: StyleProp;
/** Callback that is called when mousedown is triggered. */
- onMouseDown?: () => void;
+ onMouseDown?: MouseEventHandler;
/** The size of the checkbox container */
containerSize?: number;
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/CustomStatusBarAndBackground/index.tsx b/src/components/CustomStatusBarAndBackground/index.tsx
index b84f9c6a6630..f66a0204ac5e 100644
--- a/src/components/CustomStatusBarAndBackground/index.tsx
+++ b/src/components/CustomStatusBarAndBackground/index.tsx
@@ -1,4 +1,5 @@
import React, {useCallback, useContext, useEffect, useRef, useState} from 'react';
+import {interpolateColor, runOnJS, useAnimatedReaction, useSharedValue, withDelay, withTiming} from 'react-native-reanimated';
import useTheme from '@hooks/useTheme';
import {navigationRef} from '@libs/Navigation/Navigation';
import StatusBar from '@libs/StatusBar';
@@ -33,7 +34,27 @@ function CustomStatusBarAndBackground({isNested = false}: CustomStatusBarAndBack
};
}, [disableRootStatusBar, isNested]);
+ const prevStatusBarBackgroundColor = useRef(theme.appBG);
+ const statusBarBackgroundColor = useRef(theme.appBG);
+ const statusBarAnimation = useSharedValue(0);
+
+ useAnimatedReaction(
+ () => statusBarAnimation.value,
+ (current, previous) => {
+ // Do not run if either of the animated value is null
+ // or previous animated value is greater than or equal to the current one
+ if (previous === null || current === null || current <= previous) {
+ return;
+ }
+ const backgroundColor = interpolateColor(statusBarAnimation.value, [0, 1], [prevStatusBarBackgroundColor.current, statusBarBackgroundColor.current]);
+ runOnJS(updateStatusBarAppearance)({backgroundColor});
+ },
+ );
+
const listenerCount = useRef(0);
+
+ // Updates the status bar style and background color depending on the current route and theme
+ // This callback is triggered everytime the route changes or the theme changes
const updateStatusBarStyle = useCallback(
(listenerId?: number) => {
// Check if this function is either called through the current navigation listener or the general useEffect which listens for theme changes.
@@ -49,27 +70,40 @@ function CustomStatusBarAndBackground({isNested = false}: CustomStatusBarAndBack
currentRoute = navigationRef.getCurrentRoute();
}
- let currentScreenBackgroundColor = theme.appBG;
let newStatusBarStyle = theme.statusBarStyle;
+ let currentScreenBackgroundColor = theme.appBG;
if (currentRoute && 'name' in currentRoute && currentRoute.name in theme.PAGE_THEMES) {
- const screenTheme = theme.PAGE_THEMES[currentRoute.name];
- currentScreenBackgroundColor = screenTheme.backgroundColor;
- newStatusBarStyle = screenTheme.statusBarStyle;
+ const pageTheme = theme.PAGE_THEMES[currentRoute.name];
+
+ newStatusBarStyle = pageTheme.statusBarStyle;
+
+ const backgroundColorFromRoute =
+ currentRoute?.params && 'backgroundColor' in currentRoute.params && typeof currentRoute.params.backgroundColor === 'string' && currentRoute.params.backgroundColor;
+
+ // It's possible for backgroundColorFromRoute to be empty string, so we must use "||" to fallback to backgroundColorFallback.
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ currentScreenBackgroundColor = backgroundColorFromRoute || pageTheme.backgroundColor;
+ }
+
+ prevStatusBarBackgroundColor.current = statusBarBackgroundColor.current;
+ statusBarBackgroundColor.current = currentScreenBackgroundColor;
+
+ if (currentScreenBackgroundColor !== theme.appBG || prevStatusBarBackgroundColor.current !== theme.appBG) {
+ statusBarAnimation.value = 0;
+ statusBarAnimation.value = withDelay(300, withTiming(1));
}
// Don't update the status bar style if it's the same as the current one, to prevent flashing.
- if (newStatusBarStyle === statusBarStyle) {
- updateStatusBarAppearance({backgroundColor: currentScreenBackgroundColor});
- } else {
- updateStatusBarAppearance({backgroundColor: currentScreenBackgroundColor, statusBarStyle: newStatusBarStyle});
+ if (newStatusBarStyle !== statusBarStyle) {
+ updateStatusBarAppearance({statusBarStyle: newStatusBarStyle});
setStatusBarStyle(newStatusBarStyle);
}
},
- [statusBarStyle, theme.PAGE_THEMES, theme.appBG, theme.statusBarStyle],
+ [statusBarAnimation, statusBarStyle, theme.PAGE_THEMES, theme.appBG, theme.statusBarStyle],
);
// Add navigation state listeners to update the status bar every time the route changes
- // We have to pass a count as the listener id, because "react-navigation" somehow doesn't remove listeners properyl
+ // We have to pass a count as the listener id, because "react-navigation" somehow doesn't remove listeners properly
useEffect(() => {
if (isDisabled) {
return;
@@ -82,15 +116,6 @@ function CustomStatusBarAndBackground({isNested = false}: CustomStatusBarAndBack
return () => navigationRef.removeListener('state', listener);
}, [isDisabled, theme.appBG, updateStatusBarStyle]);
- // Update the status bar style everytime the theme changes
- useEffect(() => {
- if (isDisabled) {
- return;
- }
-
- updateStatusBarStyle();
- }, [isDisabled, theme, updateStatusBarStyle]);
-
// Update the global background (on web) everytime the theme changes.
// The background of the html element needs to be updated, otherwise you will see a big contrast when resizing the window or when the keyboard is open on iOS web.
useEffect(() => {
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/DatePicker/CalendarPicker/index.js b/src/components/DatePicker/CalendarPicker/index.js
index bbdeda6ef84f..571ddc820d43 100644
--- a/src/components/DatePicker/CalendarPicker/index.js
+++ b/src/components/DatePicker/CalendarPicker/index.js
@@ -50,7 +50,7 @@ class CalendarPicker extends React.PureComponent {
if (props.minDate >= props.maxDate) {
throw new Error('Minimum date cannot be greater than the maximum date.');
}
- let currentDateView = new Date(props.value);
+ let currentDateView = typeof props.value === 'string' ? parseISO(props.value) : new Date(props.value);
if (props.maxDate < currentDateView) {
currentDateView = props.maxDate;
} else if (props.minDate > currentDateView) {
diff --git a/src/components/DistanceMapView/distanceMapViewPropTypes.js b/src/components/DistanceMapView/distanceMapViewPropTypes.js
deleted file mode 100644
index f7a3bab1879e..000000000000
--- a/src/components/DistanceMapView/distanceMapViewPropTypes.js
+++ /dev/null
@@ -1,56 +0,0 @@
-import PropTypes from 'prop-types';
-import sourcePropTypes from '@components/Image/sourcePropTypes';
-
-const propTypes = {
- // Public access token to be used to fetch map data from Mapbox.
- accessToken: PropTypes.string.isRequired,
-
- // Style applied to MapView component. Note some of the View Style props are not available on ViewMap
- style: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]),
-
- // Link to the style JSON document.
- styleURL: PropTypes.string,
-
- // Whether map can tilt in the vertical direction.
- pitchEnabled: PropTypes.bool,
-
- // Padding to apply when the map is adjusted to fit waypoints and directions
- mapPadding: PropTypes.number,
-
- // Initial coordinate and zoom level
- initialState: PropTypes.shape({
- location: PropTypes.arrayOf(PropTypes.number).isRequired,
- zoom: PropTypes.number.isRequired,
- }),
-
- // Locations on which to put markers
- waypoints: PropTypes.arrayOf(
- PropTypes.shape({
- id: PropTypes.string,
- coordinate: PropTypes.arrayOf(PropTypes.number),
- markerComponent: sourcePropTypes,
- }),
- ),
-
- // List of coordinates which together forms a direction.
- directionCoordinates: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.number)),
-
- // Callback to call when the map is idle / ready
- onMapReady: PropTypes.func,
-
- // Optional additional styles to be applied to the overlay
- overlayStyle: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]),
-};
-
-const defaultProps = {
- styleURL: undefined,
- pitchEnabled: false,
- mapPadding: 0,
- initialState: undefined,
- waypoints: undefined,
- directionCoordinates: undefined,
- onMapReady: () => {},
- overlayStyle: undefined,
-};
-
-export {propTypes, defaultProps};
diff --git a/src/components/DistanceMapView/index.android.js b/src/components/DistanceMapView/index.android.tsx
similarity index 73%
rename from src/components/DistanceMapView/index.android.js
rename to src/components/DistanceMapView/index.android.tsx
index fa40bd50673e..38e92163c3eb 100644
--- a/src/components/DistanceMapView/index.android.js
+++ b/src/components/DistanceMapView/index.android.tsx
@@ -1,18 +1,15 @@
import React, {useState} from 'react';
import {View} from 'react-native';
-import _ from 'underscore';
import BlockingView from '@components/BlockingViews/BlockingView';
import * as Expensicons from '@components/Icon/Expensicons';
import MapView from '@components/MapView';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
-import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';
-import * as distanceMapViewPropTypes from './distanceMapViewPropTypes';
+import DistanceMapViewProps from './types';
-function DistanceMapView(props) {
+function DistanceMapView({overlayStyle, ...rest}: DistanceMapViewProps) {
const styles = useThemeStyles();
- const StyleUtils = useStyleUtils();
const [isMapReady, setIsMapReady] = useState(false);
const {isOffline} = useNetwork();
const {translate} = useLocalize();
@@ -21,7 +18,7 @@ function DistanceMapView(props) {
<>
{
if (isMapReady) {
return;
@@ -30,7 +27,7 @@ function DistanceMapView(props) {
}}
/>
{!isMapReady && (
-
+
;
-}
-
-DistanceMapView.propTypes = distanceMapViewPropTypes.propTypes;
-DistanceMapView.defaultProps = distanceMapViewPropTypes.defaultProps;
-DistanceMapView.displayName = 'DistanceMapView';
-
-export default DistanceMapView;
diff --git a/src/components/DistanceMapView/index.tsx b/src/components/DistanceMapView/index.tsx
new file mode 100644
index 000000000000..2abdc29865b0
--- /dev/null
+++ b/src/components/DistanceMapView/index.tsx
@@ -0,0 +1,12 @@
+import React from 'react';
+import MapView from '@components/MapView';
+import DistanceMapViewProps from './types';
+
+function DistanceMapView({overlayStyle, ...rest}: DistanceMapViewProps) {
+ // eslint-disable-next-line react/jsx-props-no-spreading
+ return ;
+}
+
+DistanceMapView.displayName = 'DistanceMapView';
+
+export default DistanceMapView;
diff --git a/src/components/DistanceMapView/types.ts b/src/components/DistanceMapView/types.ts
new file mode 100644
index 000000000000..5ab3dbd8238e
--- /dev/null
+++ b/src/components/DistanceMapView/types.ts
@@ -0,0 +1,8 @@
+import {StyleProp, ViewStyle} from 'react-native';
+import type {MapViewProps} from '@components/MapView/MapViewTypes';
+
+type DistanceMapViewProps = MapViewProps & {
+ overlayStyle?: StyleProp;
+};
+
+export default DistanceMapViewProps;
diff --git a/src/components/FloatingActionButton.js b/src/components/FloatingActionButton.js
index 791eb150f8c9..59e741001063 100644
--- a/src/components/FloatingActionButton.js
+++ b/src/components/FloatingActionButton.js
@@ -1,132 +1,130 @@
import PropTypes from 'prop-types';
-import React, {PureComponent} from 'react';
-import {Animated, Easing, View} from 'react-native';
-import compose from '@libs/compose';
-import Icon from './Icon';
-import * as Expensicons from './Icon/Expensicons';
+import React, {useEffect, useRef} from 'react';
+import {Platform, View} from 'react-native';
+import Animated, {createAnimatedPropAdapter, Easing, interpolateColor, processColor, useAnimatedProps, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
+import Svg, {Path} from 'react-native-svg';
+import useLocalize from '@hooks/useLocalize';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
+import variables from '@styles/variables';
import PressableWithFeedback from './Pressable/PressableWithFeedback';
import Tooltip from './Tooltip/PopoverAnchorTooltip';
-import withLocalize, {withLocalizePropTypes} from './withLocalize';
-import withStyleUtils, {withStyleUtilsPropTypes} from './withStyleUtils';
-import withTheme, {withThemePropTypes} from './withTheme';
-import withThemeStyles, {withThemeStylesPropTypes} from './withThemeStyles';
-const AnimatedIcon = Animated.createAnimatedComponent(Icon);
-AnimatedIcon.displayName = 'AnimatedIcon';
+const AnimatedPath = Animated.createAnimatedComponent(Path);
+AnimatedPath.displayName = 'AnimatedPath';
const AnimatedPressable = Animated.createAnimatedComponent(PressableWithFeedback);
AnimatedPressable.displayName = 'AnimatedPressable';
+const adapter = createAnimatedPropAdapter(
+ (props) => {
+ // eslint-disable-next-line rulesdir/prefer-underscore-method
+ if (Object.keys(props).includes('fill')) {
+ // eslint-disable-next-line no-param-reassign
+ props.fill = {type: 0, payload: processColor(props.fill)};
+ }
+ // eslint-disable-next-line rulesdir/prefer-underscore-method
+ if (Object.keys(props).includes('stroke')) {
+ // eslint-disable-next-line no-param-reassign
+ props.stroke = {type: 0, payload: processColor(props.stroke)};
+ }
+ },
+ ['fill', 'stroke'],
+);
+adapter.propTypes = {
+ fill: PropTypes.string,
+ stroke: PropTypes.string,
+};
+
const propTypes = {
- // Callback to fire on request to toggle the FloatingActionButton
+ /* Callback to fire on request to toggle the FloatingActionButton */
onPress: PropTypes.func.isRequired,
- // Current state (active or not active) of the component
+ /* Current state (active or not active) of the component */
isActive: PropTypes.bool.isRequired,
- // Ref for the button
- buttonRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
-
- ...withLocalizePropTypes,
- ...withThemePropTypes,
- ...withThemeStylesPropTypes,
- ...withStyleUtilsPropTypes,
-};
+ /* An accessibility label for the button */
+ accessibilityLabel: PropTypes.string.isRequired,
-const defaultProps = {
- buttonRef: () => {},
+ /* An accessibility role for the button */
+ role: PropTypes.string.isRequired,
};
-class FloatingActionButton extends PureComponent {
- constructor(props) {
- super(props);
- this.animatedValue = new Animated.Value(props.isActive ? 1 : 0);
- }
-
- componentDidUpdate(prevProps) {
- if (prevProps.isActive === this.props.isActive) {
- return;
- }
-
- this.animateFloatingActionButton();
- }
-
- /**
- * Animates the floating action button
- * Method is called when the isActive prop changes
- */
- animateFloatingActionButton() {
- const animationFinalValue = this.props.isActive ? 1 : 0;
-
- Animated.timing(this.animatedValue, {
- toValue: animationFinalValue,
+const FloatingActionButton = React.forwardRef(({onPress, isActive, accessibilityLabel, role}, ref) => {
+ const {success, buttonDefaultBG, textLight, textDark} = useTheme();
+ const styles = useThemeStyles();
+ const borderRadius = styles.floatingActionButton.borderRadius;
+ const {translate} = useLocalize();
+ const fabPressable = useRef(null);
+ const sharedValue = useSharedValue(isActive ? 1 : 0);
+ const buttonRef = ref;
+
+ useEffect(() => {
+ sharedValue.value = withTiming(isActive ? 1 : 0, {
duration: 340,
easing: Easing.inOut(Easing.ease),
- useNativeDriver: false,
- }).start();
- }
-
- render() {
- const rotate = this.animatedValue.interpolate({
- inputRange: [0, 1],
- outputRange: ['0deg', '135deg'],
- });
-
- const backgroundColor = this.animatedValue.interpolate({
- inputRange: [0, 1],
- outputRange: [this.props.theme.success, this.props.theme.buttonDefaultBG],
- });
-
- const fill = this.animatedValue.interpolate({
- inputRange: [0, 1],
- outputRange: [this.props.theme.textLight, this.props.theme.textDark],
});
-
- return (
-
-
- {
- this.fabPressable = el;
- if (this.props.buttonRef) {
- this.props.buttonRef.current = el;
- }
- }}
- accessibilityLabel={this.props.accessibilityLabel}
- role={this.props.role}
- pressDimmingValue={1}
- onPress={(e) => {
- // Drop focus to avoid blue focus ring.
- this.fabPressable.blur();
- this.props.onPress(e);
- }}
- onLongPress={() => {}}
- style={[this.props.themeStyles.floatingActionButton, this.props.StyleUtils.getAnimatedFABStyle(rotate, backgroundColor)]}
+ }, [isActive, sharedValue]);
+
+ const animatedStyle = useAnimatedStyle(() => {
+ const backgroundColor = interpolateColor(sharedValue.value, [0, 1], [success, buttonDefaultBG]);
+
+ return {
+ transform: [{rotate: `${sharedValue.value * 135}deg`}],
+ backgroundColor,
+ borderRadius,
+ };
+ });
+
+ const animatedProps = useAnimatedProps(
+ () => {
+ const fill = interpolateColor(sharedValue.value, [0, 1], [textLight, textDark]);
+
+ return {
+ fill,
+ };
+ },
+ undefined,
+ Platform.OS === 'web' ? undefined : adapter,
+ );
+
+ return (
+
+
+ {
+ fabPressable.current = el;
+ if (buttonRef) {
+ buttonRef.current = el;
+ }
+ }}
+ accessibilityLabel={accessibilityLabel}
+ role={role}
+ pressDimmingValue={1}
+ onPress={(e) => {
+ // Drop focus to avoid blue focus ring.
+ fabPressable.current.blur();
+ onPress(e);
+ }}
+ onLongPress={() => {}}
+ style={[styles.floatingActionButton, animatedStyle]}
+ >
+
-
-
- );
- }
-}
+
+
+
+
+ );
+});
FloatingActionButton.propTypes = propTypes;
-FloatingActionButton.defaultProps = defaultProps;
-
-const FloatingActionButtonWithLocalize = withLocalize(FloatingActionButton);
-
-const FloatingActionButtonWithLocalizeWithRef = React.forwardRef((props, ref) => (
-
-));
-
-FloatingActionButtonWithLocalizeWithRef.displayName = 'FloatingActionButtonWithLocalizeWithRef';
+FloatingActionButton.displayName = 'FloatingActionButton';
-export default compose(withThemeStyles, withTheme, withStyleUtils)(FloatingActionButtonWithLocalizeWithRef);
+export default FloatingActionButton;
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/Header.tsx b/src/components/Header.tsx
index c02f21d7c6f2..4eac2c7a6994 100644
--- a/src/components/Header.tsx
+++ b/src/components/Header.tsx
@@ -1,4 +1,4 @@
-import React, {ReactElement} from 'react';
+import React, {ReactNode} from 'react';
import {StyleProp, TextStyle, View} from 'react-native';
import useThemeStyles from '@hooks/useThemeStyles';
import EnvironmentBadge from './EnvironmentBadge';
@@ -6,10 +6,10 @@ import Text from './Text';
type HeaderProps = {
/** Title of the Header */
- title?: string | ReactElement;
+ title?: ReactNode;
/** Subtitle of the header */
- subtitle?: string | ReactElement;
+ subtitle?: ReactNode;
/** Should we show the environment badge (dev/stg)? */
shouldShowEnvironmentBadge?: boolean;
diff --git a/src/components/HeaderWithBackButton/index.js b/src/components/HeaderWithBackButton/index.tsx
similarity index 91%
rename from src/components/HeaderWithBackButton/index.js
rename to src/components/HeaderWithBackButton/index.tsx
index 738afbfaeeba..9ec8bca55a95 100755
--- a/src/components/HeaderWithBackButton/index.js
+++ b/src/components/HeaderWithBackButton/index.tsx
@@ -19,18 +19,18 @@ import getButtonState from '@libs/getButtonState';
import Navigation from '@libs/Navigation/Navigation';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
-import headerWithBackButtonPropTypes from './headerWithBackButtonPropTypes';
+import HeaderWithBackButtonProps from './types';
function HeaderWithBackButton({
- iconFill = null,
+ iconFill,
guidesCallTaskID = '',
onBackButtonPress = () => Navigation.goBack(ROUTES.HOME),
onCloseButtonPress = () => Navigation.dismissModal(),
onDownloadButtonPress = () => {},
onThreeDotsButtonPress = () => {},
report = null,
- policy = {},
- personalDetails = {},
+ policy,
+ personalDetails = null,
shouldShowAvatarWithDisplay = false,
shouldShowBackButton = true,
shouldShowBorderBottom = false,
@@ -41,10 +41,10 @@ function HeaderWithBackButton({
shouldShowPinButton = false,
shouldShowThreeDotsButton = false,
shouldDisableThreeDotsButton = false,
- stepCounter = null,
+ stepCounter,
subtitle = '',
title = '',
- titleColor = undefined,
+ titleColor,
threeDotsAnchorPosition = {
vertical: 0,
horizontal: 0,
@@ -55,14 +55,16 @@ function HeaderWithBackButton({
shouldOverlay = false,
singleExecution = (func) => func,
shouldNavigateToTopMostReport = false,
-}) {
+}: HeaderWithBackButtonProps) {
const theme = useTheme();
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const [isDownloadButtonActive, temporarilyDisableDownloadButton] = useThrottledButtonState();
const {translate} = useLocalize();
+ // @ts-expect-error TODO: Remove this once useKeyboardState (https://github.com/Expensify/App/issues/24941) is migrated to TypeScript.
const {isKeyboardShown} = useKeyboardState();
const waitForNavigate = useWaitForNavigation();
+
return (
@@ -116,17 +118,17 @@ function HeaderWithBackButton({
{shouldShowDownloadButton && (
{
+ onPress={(event) => {
// Blur the pressable in case this button triggers a Growl notification
// We do not want to overlap Growl with the Tooltip (#15271)
- e.currentTarget.blur();
+ (event?.currentTarget as HTMLElement)?.blur();
if (!isDownloadButtonActive) {
return;
}
onDownloadButtonPress();
- temporarilyDisableDownloadButton(true);
+ temporarilyDisableDownloadButton();
}}
style={[styles.touchableButtonImage]}
role="button"
@@ -134,7 +136,7 @@ function HeaderWithBackButton({
>
@@ -150,12 +152,12 @@ function HeaderWithBackButton({
>
)}
- {shouldShowPinButton && }
+ {shouldShowPinButton && !!report && }
{shouldShowThreeDotsButton && (
@@ -186,7 +188,6 @@ function HeaderWithBackButton({
);
}
-HeaderWithBackButton.propTypes = headerWithBackButtonPropTypes;
HeaderWithBackButton.displayName = 'HeaderWithBackButton';
export default HeaderWithBackButton;
diff --git a/src/components/HeaderWithBackButton/types.ts b/src/components/HeaderWithBackButton/types.ts
new file mode 100644
index 000000000000..99e93e8d18d2
--- /dev/null
+++ b/src/components/HeaderWithBackButton/types.ts
@@ -0,0 +1,113 @@
+import {ReactNode} from 'react';
+import {OnyxCollection, OnyxEntry} from 'react-native-onyx';
+import type {Action} from '@hooks/useSingleExecution';
+import type {StepCounterParams} from '@src/languages/types';
+import type {AnchorPosition} from '@src/styles';
+import type {PersonalDetails, Policy, Report} from '@src/types/onyx';
+import type ChildrenProps from '@src/types/utils/ChildrenProps';
+import type IconAsset from '@src/types/utils/IconAsset';
+
+type ThreeDotsMenuItems = {
+ /** An icon element displayed on the left side */
+ icon?: IconAsset;
+
+ /** Text label */
+ text: string;
+
+ /** A callback triggered when the item is selected */
+ onSelected: () => void;
+};
+
+type HeaderWithBackButtonProps = Partial & {
+ /** Title of the Header */
+ title?: string;
+
+ /** Subtitle of the header */
+ subtitle?: ReactNode;
+
+ /** Title color */
+ titleColor?: string;
+
+ /** Method to trigger when pressing download button of the header */
+ onDownloadButtonPress?: () => void;
+
+ /** Method to trigger when pressing close button of the header */
+ onCloseButtonPress?: () => void;
+
+ /** Method to trigger when pressing back button of the header */
+ onBackButtonPress?: () => void;
+
+ /** Method to trigger when pressing more options button of the header */
+ onThreeDotsButtonPress?: () => void;
+
+ /** Whether we should show a border on the bottom of the Header */
+ shouldShowBorderBottom?: boolean;
+
+ /** Whether we should show a download button */
+ shouldShowDownloadButton?: boolean;
+
+ /** Whether we should show a get assistance (question mark) button */
+ shouldShowGetAssistanceButton?: boolean;
+
+ /** Whether we should disable the get assistance button */
+ shouldDisableGetAssistanceButton?: boolean;
+
+ /** Whether we should show a pin button */
+ shouldShowPinButton?: boolean;
+
+ /** Whether we should show a more options (threedots) button */
+ shouldShowThreeDotsButton?: boolean;
+
+ /** Whether we should disable threedots button */
+ shouldDisableThreeDotsButton?: boolean;
+
+ /** List of menu items for more(three dots) menu */
+ threeDotsMenuItems?: ThreeDotsMenuItems[];
+
+ /** The anchor position of the menu */
+ threeDotsAnchorPosition?: AnchorPosition;
+
+ /** Whether we should show a close button */
+ shouldShowCloseButton?: boolean;
+
+ /** Whether we should show a back button */
+ shouldShowBackButton?: boolean;
+
+ /** The guides call taskID to associate with the get assistance button, if we show it */
+ guidesCallTaskID?: string;
+
+ /** Data to display a step counter in the header */
+ stepCounter?: StepCounterParams;
+
+ /** Whether we should show an avatar */
+ shouldShowAvatarWithDisplay?: boolean;
+
+ /** Parent report, if provided it will override props.report for AvatarWithDisplay */
+ parentReport?: OnyxEntry;
+
+ /** Report, if we're showing the details for one and using AvatarWithDisplay */
+ report?: OnyxEntry;
+
+ /** The report's policy, if we're showing the details for a report and need info about it for AvatarWithDisplay */
+ policy?: OnyxEntry;
+
+ /** Policies, if we're showing the details for a report and need participant details for AvatarWithDisplay */
+ personalDetails?: OnyxCollection;
+
+ /** Single execution function to prevent concurrent navigation actions */
+ singleExecution?: (action: Action) => Action;
+
+ /** Whether we should navigate to report page when the route have a topMostReport */
+ shouldNavigateToTopMostReport?: boolean;
+
+ /** The fill color for the icon. Can be hex, rgb, rgba, or valid react-native named color such as 'red' or 'blue'. */
+ iconFill?: string;
+
+ /** Whether the popover menu should overlay the current view */
+ shouldOverlay?: boolean;
+
+ /** Whether we should enable detail page navigation */
+ shouldEnableDetailPageNavigation?: boolean;
+};
+
+export default HeaderWithBackButtonProps;
diff --git a/src/components/Icon/Illustrations.ts b/src/components/Icon/Illustrations.ts
index c8a14c7aba03..1e574504001d 100644
--- a/src/components/Icon/Illustrations.ts
+++ b/src/components/Icon/Illustrations.ts
@@ -25,8 +25,10 @@ import SafeBlue from '@assets/images/product-illustrations/safe.svg';
import SmartScan from '@assets/images/product-illustrations/simple-illustration__smartscan.svg';
import TadaBlue from '@assets/images/product-illustrations/tada--blue.svg';
import TadaYellow from '@assets/images/product-illustrations/tada--yellow.svg';
+import TeleScope from '@assets/images/product-illustrations/telescope.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 +36,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 +50,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 +60,14 @@ export {
BankArrowPink,
BankMouseGreen,
BankUserGreen,
+ BigRocket,
ChatBubbles,
CoffeeMug,
ConciergeBlue,
ConciergeExclamation,
CreditCardsBlue,
EmailAddress,
+ HandCard,
HotDogStand,
InvoiceOrange,
JewelBoxBlue,
@@ -69,6 +76,7 @@ export {
JewelBoxPink,
JewelBoxYellow,
MagicCode,
+ Mailbox,
MoneyEnvelopeBlue,
MoneyMousePink,
ReceiptsSearchYellow,
@@ -77,6 +85,7 @@ export {
RocketOrange,
SanFrancisco,
SafeBlue,
+ SmallRocket,
TadaYellow,
TadaBlue,
ToddBehindCloud,
@@ -102,4 +111,5 @@ export {
Hands,
HandEarth,
SmartScan,
+ TeleScope,
};
diff --git a/src/components/Icon/index.tsx b/src/components/Icon/index.tsx
index e71d21077eda..732fe90deae2 100644
--- a/src/components/Icon/index.tsx
+++ b/src/components/Icon/index.tsx
@@ -1,15 +1,14 @@
import {ImageContentFit} from 'expo-image';
-import React, {PureComponent} from 'react';
+import React from 'react';
import {StyleProp, View, ViewStyle} from 'react-native';
import ImageSVG from '@components/ImageSVG';
-import withStyleUtils, {WithStyleUtilsProps} from '@components/withStyleUtils';
-import withTheme, {WithThemeProps} from '@components/withTheme';
-import withThemeStyles, {type WithThemeStylesProps} from '@components/withThemeStyles';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
import IconAsset from '@src/types/utils/IconAsset';
import IconWrapperStyles from './IconWrapperStyles';
-type IconBaseProps = {
+type IconProps = {
/** The asset to render. */
src: IconAsset;
@@ -43,68 +42,65 @@ type IconBaseProps = {
/** Determines how the image should be resized to fit its container */
contentFit?: ImageContentFit;
};
-type IconProps = IconBaseProps & WithThemeStylesProps & WithThemeProps & WithStyleUtilsProps;
-
-// We must use a class component to create an animatable component with the Animated API
-// eslint-disable-next-line react/prefer-stateless-function
-class Icon extends PureComponent {
- // eslint-disable-next-line react/static-property-placement
- public static defaultProps: Partial = {
- width: variables.iconSizeNormal,
- height: variables.iconSizeNormal,
- fill: undefined,
- small: false,
- inline: false,
- additionalStyles: [],
- hovered: false,
- pressed: false,
- testID: '',
- contentFit: 'cover',
- };
-
- render() {
- const width = this.props.small ? variables.iconSizeSmall : this.props.width;
- const height = this.props.small ? variables.iconSizeSmall : this.props.height;
- const iconStyles = [this.props.StyleUtils.getWidthAndHeightStyle(width ?? 0, height), IconWrapperStyles, this.props.themeStyles.pAbsolute, this.props.additionalStyles];
-
- if (this.props.inline) {
- return (
-
-
-
-
-
- );
- }
+function Icon({
+ src,
+ width = variables.iconSizeNormal,
+ height = variables.iconSizeNormal,
+ fill = undefined,
+ small = false,
+ inline = false,
+ additionalStyles = [],
+ hovered = false,
+ pressed = false,
+ testID = '',
+ contentFit = 'cover',
+}: IconProps) {
+ const StyleUtils = useStyleUtils();
+ const styles = useThemeStyles();
+ const iconWidth = small ? variables.iconSizeSmall : width;
+ const iconHeight = small ? variables.iconSizeSmall : height;
+ const iconStyles = [StyleUtils.getWidthAndHeightStyle(width ?? 0, height), IconWrapperStyles, styles.pAbsolute, additionalStyles];
+
+ if (inline) {
return (
-
+
+
+
);
}
+
+ return (
+
+
+
+ );
}
-export default withTheme(withThemeStyles(withStyleUtils(Icon)));
+Icon.displayName = 'Icon';
+
+export default Icon;
diff --git a/src/components/InlineCodeBlock/WrappedText.tsx b/src/components/InlineCodeBlock/WrappedText.tsx
index f0bb4e3ffd53..1c66cef234ed 100644
--- a/src/components/InlineCodeBlock/WrappedText.tsx
+++ b/src/components/InlineCodeBlock/WrappedText.tsx
@@ -31,6 +31,13 @@ function getTextMatrix(text: string): string[][] {
return text.split('\n').map((row) => row.split(CONST.REGEX.SPACE_OR_EMOJI).filter((value) => value !== ''));
}
+/**
+ * Validates if the text contains any emoji
+ */
+function containsEmoji(text: string): boolean {
+ return CONST.REGEX.EMOJI.test(text);
+}
+
function WrappedText({children, wordStyles, textStyles}: WrappedTextProps) {
const styles = useThemeStyles();
@@ -53,7 +60,7 @@ function WrappedText({children, wordStyles, textStyles}: WrappedTextProps) {
style={styles.codeWordWrapper}
>
- {colText}
+ {colText}
))}
diff --git a/src/components/InvertedFlatList/BaseInvertedFlatList.js b/src/components/InvertedFlatList/BaseInvertedFlatList.js
deleted file mode 100644
index 4206d5086a9e..000000000000
--- a/src/components/InvertedFlatList/BaseInvertedFlatList.js
+++ /dev/null
@@ -1,38 +0,0 @@
-import PropTypes from 'prop-types';
-import React, {forwardRef} from 'react';
-import FlatList from '@components/FlatList';
-
-const AUTOSCROLL_TO_TOP_THRESHOLD = 128;
-
-const propTypes = {
- /** Same as FlatList can be any array of anything */
- // eslint-disable-next-line react/forbid-prop-types
- data: PropTypes.arrayOf(PropTypes.any),
-
- /** Same as FlatList although we wrap it in a measuring helper before passing to the actual FlatList component */
- renderItem: PropTypes.func.isRequired,
-};
-
-const defaultProps = {
- data: [],
-};
-
-const BaseInvertedFlatList = forwardRef((props, ref) => (
-
-));
-
-BaseInvertedFlatList.propTypes = propTypes;
-BaseInvertedFlatList.defaultProps = defaultProps;
-BaseInvertedFlatList.displayName = 'BaseInvertedFlatList';
-
-export default BaseInvertedFlatList;
diff --git a/src/components/InvertedFlatList/BaseInvertedFlatList.tsx b/src/components/InvertedFlatList/BaseInvertedFlatList.tsx
new file mode 100644
index 000000000000..08d990583572
--- /dev/null
+++ b/src/components/InvertedFlatList/BaseInvertedFlatList.tsx
@@ -0,0 +1,26 @@
+import React, {ForwardedRef, forwardRef} from 'react';
+import {FlatListProps} from 'react-native';
+import FlatList from '@components/FlatList';
+
+const AUTOSCROLL_TO_TOP_THRESHOLD = 128;
+const WINDOW_SIZE = 15;
+
+function BaseInvertedFlatList(props: FlatListProps, ref: ForwardedRef) {
+ return (
+
+ );
+}
+
+BaseInvertedFlatList.displayName = 'BaseInvertedFlatList';
+
+export default forwardRef(BaseInvertedFlatList);
diff --git a/src/components/InvertedFlatList/CellRendererComponent.js b/src/components/InvertedFlatList/CellRendererComponent.tsx
similarity index 62%
rename from src/components/InvertedFlatList/CellRendererComponent.js
rename to src/components/InvertedFlatList/CellRendererComponent.tsx
index 2b2d214000bf..252d47989064 100644
--- a/src/components/InvertedFlatList/CellRendererComponent.js
+++ b/src/components/InvertedFlatList/CellRendererComponent.tsx
@@ -1,20 +1,12 @@
-import PropTypes from 'prop-types';
import React from 'react';
-import {View} from 'react-native';
+import {StyleProp, View, ViewProps} from 'react-native';
-const propTypes = {
- /** Position index of the list item in a list view */
- index: PropTypes.number.isRequired,
-
- /** Styles that are passed to the component */
- style: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]),
-};
-
-const defaultProps = {
- style: {},
+type CellRendererComponentProps = ViewProps & {
+ index: number;
+ style?: StyleProp;
};
-function CellRendererComponent(props) {
+function CellRendererComponent(props: CellRendererComponentProps) {
return (
{
+function BaseInvertedFlatListWithRef(props: FlatListProps, ref: ForwardedRef) {
const styles = useThemeStyles();
return (
{
removeClippedSubviews={false}
/>
);
-});
+}
BaseInvertedFlatListWithRef.displayName = 'BaseInvertedFlatListWithRef';
-export default BaseInvertedFlatListWithRef;
+export default forwardRef(BaseInvertedFlatListWithRef);
diff --git a/src/components/InvertedFlatList/index.js b/src/components/InvertedFlatList/index.tsx
similarity index 53%
rename from src/components/InvertedFlatList/index.js
rename to src/components/InvertedFlatList/index.tsx
index 815b58ad8308..6871b010a385 100644
--- a/src/components/InvertedFlatList/index.js
+++ b/src/components/InvertedFlatList/index.tsx
@@ -1,58 +1,37 @@
-import PropTypes from 'prop-types';
-import React, {forwardRef, useEffect, useRef} from 'react';
-import {DeviceEventEmitter, FlatList} from 'react-native';
+import React, {ForwardedRef, forwardRef, useEffect, useRef} from 'react';
+import {DeviceEventEmitter, FlatList, FlatListProps, NativeScrollEvent, NativeSyntheticEvent} from 'react-native';
import CONST from '@src/CONST';
import BaseInvertedFlatList from './BaseInvertedFlatList';
-const propTypes = {
- /** Passed via forwardRef so we can access the FlatList ref */
- innerRef: PropTypes.shape({
- current: PropTypes.instanceOf(FlatList),
- }).isRequired,
-
- /** Any additional styles to apply */
- // eslint-disable-next-line react/forbid-prop-types
- contentContainerStyle: PropTypes.any,
-
- /** Same as for FlatList */
- onScroll: PropTypes.func,
-};
-
// This is adapted from https://codesandbox.io/s/react-native-dsyse
// It's a HACK alert since FlatList has inverted scrolling on web
-function InvertedFlatList(props) {
- const {innerRef, contentContainerStyle} = props;
-
- const lastScrollEvent = useRef(null);
- const scrollEndTimeout = useRef(null);
- const updateInProgress = useRef(false);
- const eventHandler = useRef(null);
+function InvertedFlatList({onScroll: onScrollProp = () => {}, contentContainerStyle, ...props}: FlatListProps, ref: ForwardedRef) {
+ const lastScrollEvent = useRef(null);
+ const scrollEndTimeout = useRef(null);
+ const updateInProgress = useRef(false);
useEffect(
() => () => {
- if (scrollEndTimeout.current) {
- clearTimeout(scrollEndTimeout.current);
- }
-
- if (eventHandler.current) {
- eventHandler.current.remove();
+ if (!scrollEndTimeout.current) {
+ return;
}
+ clearTimeout(scrollEndTimeout.current);
},
- [innerRef],
+ [ref],
);
/**
* Emits when the scrolling is in progress. Also,
* invokes the onScroll callback function from props.
*
- * @param {Event} event - The onScroll event from the FlatList
+ * @param event - The onScroll event from the FlatList
*/
- const onScroll = (event) => {
- props.onScroll(event);
+ const onScroll = (event: NativeSyntheticEvent) => {
+ onScrollProp(event);
if (!updateInProgress.current) {
updateInProgress.current = true;
- eventHandler.current = DeviceEventEmitter.emit(CONST.EVENTS.SCROLLING, true);
+ DeviceEventEmitter.emit(CONST.EVENTS.SCROLLING, true);
}
};
@@ -60,7 +39,7 @@ function InvertedFlatList(props) {
* Emits when the scrolling has ended.
*/
const onScrollEnd = () => {
- eventHandler.current = DeviceEventEmitter.emit(CONST.EVENTS.SCROLLING, false);
+ DeviceEventEmitter.emit(CONST.EVENTS.SCROLLING, false);
updateInProgress.current = false;
};
@@ -77,9 +56,8 @@ function InvertedFlatList(props) {
* This workaround is taken from below and refactored to fit our needs:
* https://github.com/necolas/react-native-web/issues/1021#issuecomment-984151185
*
- * @param {Event} event - The onScroll event from the FlatList
*/
- const handleScroll = (event) => {
+ const handleScroll = (event: NativeSyntheticEvent) => {
onScroll(event);
const timestamp = Date.now();
@@ -105,28 +83,13 @@ function InvertedFlatList(props) {
);
}
-InvertedFlatList.propTypes = propTypes;
-InvertedFlatList.defaultProps = {
- contentContainerStyle: {},
- onScroll: () => {},
-};
InvertedFlatList.displayName = 'InvertedFlatList';
-const InvertedFlatListWithRef = forwardRef((props, ref) => (
-
-));
-
-InvertedFlatListWithRef.displayName = 'InvertedFlatListWithRef';
-
-export default InvertedFlatListWithRef;
+export default forwardRef(InvertedFlatList);
diff --git a/src/components/LHNOptionsList/OptionRowLHN.js b/src/components/LHNOptionsList/OptionRowLHN.js
index f75e3390136a..fc4f05eefd22 100644
--- a/src/components/LHNOptionsList/OptionRowLHN.js
+++ b/src/components/LHNOptionsList/OptionRowLHN.js
@@ -135,7 +135,7 @@ function OptionRowLHN(props) {
props.reportID,
'0',
props.reportID,
- '',
+ undefined,
() => {},
() => setIsContextMenuActive(false),
false,
diff --git a/src/components/MapView/index.tsx b/src/components/MapView/index.tsx
index f273845fe4c0..d9da7f1dfea3 100644
--- a/src/components/MapView/index.tsx
+++ b/src/components/MapView/index.tsx
@@ -1,8 +1,8 @@
import React from 'react';
import MapView from './MapView';
-import {ComponentProps} from './types';
+import type {MapViewProps} from './MapViewTypes';
-function MapViewComponent(props: ComponentProps) {
+function MapViewComponent(props: MapViewProps) {
// eslint-disable-next-line react/jsx-props-no-spreading
return ;
}
diff --git a/src/components/MentionSuggestions.tsx b/src/components/MentionSuggestions.tsx
index 72dfd177dd1e..3e235a2fc88a 100644
--- a/src/components/MentionSuggestions.tsx
+++ b/src/components/MentionSuggestions.tsx
@@ -72,7 +72,7 @@ function MentionSuggestions({prefix, mentions, highlightedMentionIndex = 0, onSe
size={isIcon ? CONST.AVATAR_SIZE.MENTION_ICON : CONST.AVATAR_SIZE.SMALLER}
name={item.icons[0].name}
type={item.icons[0].type}
- fill={theme.success}
+ fill={isIcon ? theme.success : undefined}
fallbackIcon={item.icons[0].fallbackIcon}
/>
@@ -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/MoneyReportHeader.js b/src/components/MoneyReportHeader.js
index a559e876af18..8ed6d0746438 100644
--- a/src/components/MoneyReportHeader.js
+++ b/src/components/MoneyReportHeader.js
@@ -44,6 +44,9 @@ const propTypes = {
/** The role of the current user in the policy */
role: PropTypes.string,
+
+ /** Whether Scheduled Submit is turned on for this policy */
+ isHarvestingEnabled: PropTypes.bool,
}),
/** The chat report this report is linked to */
@@ -70,7 +73,9 @@ const defaultProps = {
session: {
email: null,
},
- policy: {},
+ policy: {
+ isHarvestingEnabled: false,
+ },
};
function MoneyReportHeader({session, personalDetails, policy, chatReport, nextStep, report: moneyRequestReport, isSmallScreenWidth}) {
@@ -108,6 +113,12 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt
const formattedAmount = CurrencyUtils.convertToDisplayString(reimbursableTotal, moneyRequestReport.currency);
const isMoreContentShown = shouldShowNextStep || (shouldShowAnyButton && isSmallScreenWidth);
+ // The submit button should be success green colour only if the user is submitter and the policy does not have Scheduled Submit turned on
+ const isWaitingForSubmissionFromCurrentUser = useMemo(
+ () => chatReport.isOwnPolicyExpenseChat && !policy.isHarvestingEnabled,
+ [chatReport.isOwnPolicyExpenseChat, policy.isHarvestingEnabled],
+ );
+
const threeDotsMenuItems = [HeaderUtils.getPinMenuItem(moneyRequestReport)];
if (!ReportUtils.isArchivedRoom(chatReport)) {
threeDotsMenuItems.push({
@@ -164,7 +175,7 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt