diff --git a/.github/workflows/deployExpensifyHelp.yml b/.github/workflows/deployExpensifyHelp.yml index ebf43c7af42b..25b1b0dad341 100644 --- a/.github/workflows/deployExpensifyHelp.yml +++ b/.github/workflows/deployExpensifyHelp.yml @@ -53,10 +53,10 @@ jobs: directory: ./docs/_site - name: Setup Cloudflare CLI - run: pip3 install cloudflare + run: pip3 install cloudflare==2.17.0 - name: Purge Cloudflare cache - run: /home/runner/.local/bin/cli4 --delete hosts=["help.expensify.com"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache + run: /home/runner/.local/bin/cli4 --verbose --delete hosts=["help.expensify.com"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache env: CF_API_KEY: ${{ secrets.CLOUDFLARE_TOKEN }} @@ -66,7 +66,7 @@ jobs: with: token: ${{ secrets.OS_BOTIFY_TOKEN }} body: ${{ format('A preview of your ExpensifyHelp changes have been deployed to {0} ⚡️', steps.deploy.outputs.alias) }} - + - name: Reindex google search if: ${{ github.event_name == 'push' }} run : | diff --git a/.github/workflows/e2ePerformanceTests.yml b/.github/workflows/e2ePerformanceTests.yml index 04fdb7e80d6a..cc107614593d 100644 --- a/.github/workflows/e2ePerformanceTests.yml +++ b/.github/workflows/e2ePerformanceTests.yml @@ -212,7 +212,7 @@ jobs: status: custom custom_payload: | { - channel: '#expensify-margelo', + channel: '#e2e-announce', attachments: [{ color: 'danger', text: `💥 ${process.env.AS_REPO} E2E Test run failed failed on workflow 💥`, diff --git a/.github/workflows/platformDeploy.yml b/.github/workflows/platformDeploy.yml index 813c341caaf6..df3c2b3617bb 100644 --- a/.github/workflows/platformDeploy.yml +++ b/.github/workflows/platformDeploy.yml @@ -301,7 +301,7 @@ jobs: uses: ./.github/actions/composite/setupNode - name: Setup Cloudflare CLI - run: pip3 install cloudflare + run: pip3 install cloudflare==2.17.0 - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 @@ -338,13 +338,13 @@ jobs: - name: Purge production Cloudflare cache if: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} - run: /home/runner/.local/bin/cli4 --delete hosts=["new.expensify.com"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache + run: /home/runner/.local/bin/cli4 --verbose --delete hosts=["new.expensify.com"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache env: CF_API_KEY: ${{ secrets.CLOUDFLARE_TOKEN }} - name: Purge staging Cloudflare cache if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} - run: /home/runner/.local/bin/cli4 --delete hosts=["staging.new.expensify.com"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache + run: /home/runner/.local/bin/cli4 --verbose --delete hosts=["staging.new.expensify.com"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache env: CF_API_KEY: ${{ secrets.CLOUDFLARE_TOKEN }} diff --git a/.gitignore b/.gitignore index a065ebfb8be3..aeee5f730bfc 100644 --- a/.gitignore +++ b/.gitignore @@ -113,6 +113,11 @@ tsconfig.tsbuildinfo .yalc yalc.lock +# Expo +.expo +dist/ +web-build/ + # Local https certificate/key config/webpack/*.pem diff --git a/__mocks__/@react-native-camera-roll/camera-roll.js b/__mocks__/@react-native-camera-roll/camera-roll.js deleted file mode 100644 index 4274cd531a85..000000000000 --- a/__mocks__/@react-native-camera-roll/camera-roll.js +++ /dev/null @@ -1,5 +0,0 @@ -export default { - CameraRoll: { - save: jest.fn(), - }, -}; diff --git a/__mocks__/@react-native-camera-roll/camera-roll.ts b/__mocks__/@react-native-camera-roll/camera-roll.ts new file mode 100644 index 000000000000..5b5084254d2e --- /dev/null +++ b/__mocks__/@react-native-camera-roll/camera-roll.ts @@ -0,0 +1,15 @@ +import type {CameraRoll} from '@react-native-camera-roll/camera-roll'; + +type CameraRollMock = { + CameraRoll: { + save: typeof CameraRoll.save; + }; +}; + +const cameraRollMock: CameraRollMock = { + CameraRoll: { + save: jest.fn(), + }, +}; + +export default cameraRollMock; diff --git a/android/app/build.gradle b/android/app/build.gradle index 72cf2bbc9b6d..c41f78e5aa31 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -98,8 +98,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001044102 - versionName "1.4.41-2" + versionCode 1001044201 + versionName "1.4.42-1" } flavorDimensions "default" diff --git a/android/app/src/main/java/com/expensify/chat/customairshipextender/CustomNotificationProvider.java b/android/app/src/main/java/com/expensify/chat/customairshipextender/CustomNotificationProvider.java index c57819d19d03..3d16e607be49 100644 --- a/android/app/src/main/java/com/expensify/chat/customairshipextender/CustomNotificationProvider.java +++ b/android/app/src/main/java/com/expensify/chat/customairshipextender/CustomNotificationProvider.java @@ -98,6 +98,7 @@ protected NotificationCompat.Builder onExtendBuilder(@NonNull Context context, @ // Improve notification delivery by categorising as a time-critical message builder.setCategory(CATEGORY_MESSAGE); + builder.setVisibility(NotificationCompat.VISIBILITY_PRIVATE); // Configure the notification channel or priority to ensure it shows in foreground if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { diff --git a/assets/animations/Desk.lottie b/assets/animations/Desk.lottie new file mode 100644 index 000000000000..15e6caa6be0c Binary files /dev/null and b/assets/animations/Desk.lottie differ diff --git a/assets/emojis/common.ts b/assets/emojis/common.ts index b23383590c51..c19d958812d1 100644 --- a/assets/emojis/common.ts +++ b/assets/emojis/common.ts @@ -2174,62 +2174,62 @@ const emojis: PickerEmojis = [ { name: 'people_holding_hands', code: '🧑‍🤝‍🧑', - types: ['🧑🏿‍🤝‍🧑🏿', '🧑🏿‍🤝‍🧑🏾', '🧑🏿‍🤝‍🧑🏽', '🧑🏿‍🤝‍🧑🏼', '🧑🏿‍🤝‍🧑🏻', '🧑🏾‍🤝‍🧑🏿', '🧑🏾‍🤝‍🧑🏾', '🧑🏾‍🤝‍🧑🏽', '🧑🏾‍🤝‍🧑🏼', '🧑🏾‍🤝‍🧑🏻', '🧑🏽‍🤝‍🧑🏿', '🧑🏽‍🤝‍🧑🏾', '🧑🏽‍🤝‍🧑🏽', '🧑🏽‍🤝‍🧑🏼', '🧑🏽‍🤝‍🧑🏻', '🧑🏼‍🤝‍🧑🏿', '🧑🏼‍🤝‍🧑🏾', '🧑🏼‍🤝‍🧑🏽', '🧑🏼‍🤝‍🧑🏼', '🧑🏼‍🤝‍🧑🏻', '🧑🏻‍🤝‍🧑🏿', '🧑🏻‍🤝‍🧑🏾', '🧑🏻‍🤝‍🧑🏽', '🧑🏻‍🤝‍🧑🏼', '🧑🏻‍🤝‍🧑🏻'], + types: ['🧑🏿‍🤝‍🧑🏿', '🧑🏾‍🤝‍🧑🏾', '🧑🏽‍🤝‍🧑🏽', '🧑🏼‍🤝‍🧑🏼', '🧑🏻‍🤝‍🧑🏻'], }, { name: 'two_women_holding_hands', code: '👭', - types: ['👩🏿‍🤝‍👩🏾', '👩🏿‍🤝‍👩🏽', '👩🏿‍🤝‍👩🏼', '👩🏿‍🤝‍👩🏻', '👩🏾‍🤝‍👩🏿', '👩🏾‍🤝‍👩🏽', '👩🏾‍🤝‍👩🏼', '👩🏾‍🤝‍👩🏻', '👩🏽‍🤝‍👩🏿', '👩🏽‍🤝‍👩🏾', '👩🏽‍🤝‍👩🏼', '👩🏽‍🤝‍👩🏻', '👩🏼‍🤝‍👩🏿', '👩🏼‍🤝‍👩🏾', '👩🏼‍🤝‍👩🏽', '👩🏼‍🤝‍👩🏻', '👩🏻‍🤝‍👩🏿', '👩🏻‍🤝‍👩🏾', '👩🏻‍🤝‍👩🏽', '👩🏻‍🤝‍👩🏼', '👭🏿', '👭🏾', '👭🏽', '👭🏼', '👭🏻'], + types: ['👭🏿', '👭🏾', '👭🏽', '👭🏼', '👭🏻'], }, { name: 'couple', code: '👫', - types: ['👩🏿‍🤝‍👨🏾', '👩🏿‍🤝‍👨🏽', '👩🏿‍🤝‍👨🏼', '👩🏿‍🤝‍👨🏻', '👩🏾‍🤝‍👨🏿', '👩🏾‍🤝‍👨🏽', '👩🏾‍🤝‍👨🏼', '👩🏾‍🤝‍👨🏻', '👩🏽‍🤝‍👨🏿', '👩🏽‍🤝‍👨🏾', '👩🏽‍🤝‍👨🏼', '👩🏽‍🤝‍👨🏻', '👩🏼‍🤝‍👨🏿', '👩🏼‍🤝‍👨🏾', '👩🏼‍🤝‍👨🏽', '👩🏼‍🤝‍👨🏻', '👩🏻‍🤝‍👨🏿', '👩🏻‍🤝‍👨🏾', '👩🏻‍🤝‍👨🏽', '👩🏻‍🤝‍👨🏼', '👫🏿', '👫🏾', '👫🏽', '👫🏼', '👫🏻'], + types: ['👫🏿', '👫🏾', '👫🏽', '👫🏼', '👫🏻'], }, { name: 'two_men_holding_hands', code: '👬', - types: ['👨🏿‍🤝‍👨🏾', '👨🏿‍🤝‍👨🏽', '👨🏿‍🤝‍👨🏼', '👨🏿‍🤝‍👨🏻', '👨🏾‍🤝‍👨🏿', '👨🏾‍🤝‍👨🏽', '👨🏾‍🤝‍👨🏼', '👨🏾‍🤝‍👨🏻', '👨🏽‍🤝‍👨🏿', '👨🏽‍🤝‍👨🏾', '👨🏽‍🤝‍👨🏼', '👨🏽‍🤝‍👨🏻', '👨🏼‍🤝‍👨🏿', '👨🏼‍🤝‍👨🏾', '👨🏼‍🤝‍👨🏽', '👨🏼‍🤝‍👨🏻', '👨🏻‍🤝‍👨🏿', '👨🏻‍🤝‍👨🏾', '👨🏻‍🤝‍👨🏽', '👨🏻‍🤝‍👨🏼', '👬🏿', '👬🏾', '👬🏽', '👬🏼', '👬🏻'], + types: ['👬🏿', '👬🏾', '👬🏽', '👬🏼', '👬🏻'], }, { name: 'couplekiss', code: '💏', - types: ['🧑🏿‍❤️‍💋‍🧑🏾', '🧑🏿‍❤️‍💋‍🧑🏽', '🧑🏿‍❤️‍💋‍🧑🏼', '🧑🏿‍❤️‍💋‍🧑🏻', '🧑🏾‍❤️‍💋‍🧑🏿', '🧑🏾‍❤️‍💋‍🧑🏽', '🧑🏾‍❤️‍💋‍🧑🏼', '🧑🏾‍❤️‍💋‍🧑🏻', '🧑🏽‍❤️‍💋‍🧑🏿', '🧑🏽‍❤️‍💋‍🧑🏾', '🧑🏽‍❤️‍💋‍🧑🏼', '🧑🏽‍❤️‍💋‍🧑🏻', '🧑🏼‍❤️‍💋‍🧑🏿', '🧑🏼‍❤️‍💋‍🧑🏾', '🧑🏼‍❤️‍💋‍🧑🏽', '🧑🏼‍❤️‍💋‍🧑🏻', '🧑🏻‍❤️‍💋‍🧑🏿', '🧑🏻‍❤️‍💋‍🧑🏾', '🧑🏻‍❤️‍💋‍🧑🏽', '🧑🏻‍❤️‍💋‍🧑🏼', '💏🏿', '💏🏾', '💏🏽', '💏🏼', '💏🏻'], + types: ['💏🏿', '💏🏾', '💏🏽', '💏🏼', '💏🏻'], }, { name: 'couplekiss_man_woman', code: '👩‍❤️‍💋‍👨', - types: ['👩🏿‍❤️‍💋‍👨🏿', '👩🏿‍❤️‍💋‍👨🏾', '👩🏿‍❤️‍💋‍👨🏽', '👩🏿‍❤️‍💋‍👨🏼', '👩🏿‍❤️‍💋‍👨🏻', '👩🏾‍❤️‍💋‍👨🏿', '👩🏾‍❤️‍💋‍👨🏾', '👩🏾‍❤️‍💋‍👨🏽', '👩🏾‍❤️‍💋‍👨🏼', '👩🏾‍❤️‍💋‍👨🏻', '👩🏽‍❤️‍💋‍👨🏿', '👩🏽‍❤️‍💋‍👨🏾', '👩🏽‍❤️‍💋‍👨🏽', '👩🏽‍❤️‍💋‍👨🏼', '👩🏽‍❤️‍💋‍👨🏻', '👩🏼‍❤️‍💋‍👨🏿', '👩🏼‍❤️‍💋‍👨🏾', '👩🏼‍❤️‍💋‍👨🏽', '👩🏼‍❤️‍💋‍👨🏼', '👩🏼‍❤️‍💋‍👨🏻', '👩🏻‍❤️‍💋‍👨🏿', '👩🏻‍❤️‍💋‍👨🏾', '👩🏻‍❤️‍💋‍👨🏽', '👩🏻‍❤️‍💋‍👨🏼', '👩🏻‍❤️‍💋‍👨🏻'], + types: ['👩🏿‍❤️‍💋‍👨🏿', '👩🏾‍❤️‍💋‍👨🏾', '👩🏽‍❤️‍💋‍👨🏽', '👩🏼‍❤️‍💋‍👨🏼', '👩🏻‍❤️‍💋‍👨🏻'], }, { name: 'couplekiss_man_man', code: '👨‍❤️‍💋‍👨', - types: ['👨🏿‍❤️‍💋‍👨🏿', '👨🏿‍❤️‍💋‍👨🏾', '👨🏿‍❤️‍💋‍👨🏽', '👨🏿‍❤️‍💋‍👨🏼', '👨🏿‍❤️‍💋‍👨🏻', '👨🏾‍❤️‍💋‍👨🏿', '👨🏾‍❤️‍💋‍👨🏾', '👨🏾‍❤️‍💋‍👨🏽', '👨🏾‍❤️‍💋‍👨🏼', '👨🏾‍❤️‍💋‍👨🏻', '👨🏽‍❤️‍💋‍👨🏿', '👨🏽‍❤️‍💋‍👨🏾', '👨🏽‍❤️‍💋‍👨🏽', '👨🏽‍❤️‍💋‍👨🏼', '👨🏽‍❤️‍💋‍👨🏻', '👨🏼‍❤️‍💋‍👨🏿', '👨🏼‍❤️‍💋‍👨🏾', '👨🏼‍❤️‍💋‍👨🏽', '👨🏼‍❤️‍💋‍👨🏼', '👨🏼‍❤️‍💋‍👨🏻', '👨🏻‍❤️‍💋‍👨🏿', '👨🏻‍❤️‍💋‍👨🏾', '👨🏻‍❤️‍💋‍👨🏽', '👨🏻‍❤️‍💋‍👨🏼', '👨🏻‍❤️‍💋‍👨🏻'], + types: ['👨🏿‍❤️‍💋‍👨🏿', '👨🏾‍❤️‍💋‍👨🏾', '👨🏽‍❤️‍💋‍👨🏽', '👨🏼‍❤️‍💋‍👨🏼', '👨🏻‍❤️‍💋‍👨🏻'], }, { name: 'couplekiss_woman_woman', code: '👩‍❤️‍💋‍👩', - types: ['👩🏿‍❤️‍💋‍👩🏿', '👩🏿‍❤️‍💋‍👩🏾', '👩🏿‍❤️‍💋‍👩🏽', '👩🏿‍❤️‍💋‍👩🏼', '👩🏿‍❤️‍💋‍👩🏻', '👩🏾‍❤️‍💋‍👩🏿', '👩🏾‍❤️‍💋‍👩🏾', '👩🏾‍❤️‍💋‍👩🏽', '👩🏾‍❤️‍💋‍👩🏼', '👩🏾‍❤️‍💋‍👩🏻', '👩🏽‍❤️‍💋‍👩🏿', '👩🏽‍❤️‍💋‍👩🏾', '👩🏽‍❤️‍💋‍👩🏽', '👩🏽‍❤️‍💋‍👩🏼', '👩🏽‍❤️‍💋‍👩🏻', '👩🏼‍❤️‍💋‍👩🏿', '👩🏼‍❤️‍💋‍👩🏾', '👩🏼‍❤️‍💋‍👩🏽', '👩🏼‍❤️‍💋‍👩🏼', '👩🏼‍❤️‍💋‍👩🏻', '👩🏻‍❤️‍💋‍👩🏿', '👩🏻‍❤️‍💋‍👩🏾', '👩🏻‍❤️‍💋‍👩🏽', '👩🏻‍❤️‍💋‍👩🏼', '👩🏻‍❤️‍💋‍👩🏻'], + types: ['👩🏿‍❤️‍💋‍👩🏿', '👩🏾‍❤️‍💋‍👩🏾', '👩🏽‍❤️‍💋‍👩🏽', '👩🏼‍❤️‍💋‍👩🏼', '👩🏻‍❤️‍💋‍👩🏻'], }, { name: 'couple_with_heart', code: '💑', - types: ['🧑🏿‍❤️‍🧑🏾', '🧑🏿‍❤️‍🧑🏽', '🧑🏿‍❤️‍🧑🏼', '🧑🏿‍❤️‍🧑🏻', '🧑🏾‍❤️‍🧑🏿', '🧑🏾‍❤️‍🧑🏽', '🧑🏾‍❤️‍🧑🏼', '🧑🏾‍❤️‍🧑🏻', '🧑🏽‍❤️‍🧑🏿', '🧑🏽‍❤️‍🧑🏾', '🧑🏽‍❤️‍🧑🏼', '🧑🏽‍❤️‍🧑🏻', '🧑🏼‍❤️‍🧑🏿', '🧑🏼‍❤️‍🧑🏾', '🧑🏼‍❤️‍🧑🏽', '🧑🏼‍❤️‍🧑🏻', '🧑🏻‍❤️‍🧑🏿', '🧑🏻‍❤️‍🧑🏾', '🧑🏻‍❤️‍🧑🏽', '🧑🏻‍❤️‍🧑🏼', '💑🏿', '💑🏾', '💑🏽', '💑🏼', '💑🏻'], + types: ['💑🏿', '💑🏾', '💑🏽', '💑🏼', '💑🏻'], }, { name: 'couple_with_heart_woman_man', code: '👩‍❤️‍👨', - types: ['👩🏿‍❤️‍👨🏿', '👩🏿‍❤️‍👨🏾', '👩🏿‍❤️‍👨🏽', '👩🏿‍❤️‍👨🏼', '👩🏿‍❤️‍👨🏻', '👩🏾‍❤️‍👨🏿', '👩🏾‍❤️‍👨🏾', '👩🏾‍❤️‍👨🏽', '👩🏾‍❤️‍👨🏼', '👩🏾‍❤️‍👨🏻', '👩🏽‍❤️‍👨🏿', '👩🏽‍❤️‍👨🏾', '👩🏽‍❤️‍👨🏽', '👩🏽‍❤️‍👨🏼', '👩🏽‍❤️‍👨🏻', '👩🏼‍❤️‍👨🏿', '👩🏼‍❤️‍👨🏾', '👩🏼‍❤️‍👨🏽', '👩🏼‍❤️‍👨🏼', '👩🏼‍❤️‍👨🏻', '👩🏻‍❤️‍👨🏿', '👩🏻‍❤️‍👨🏾', '👩🏻‍❤️‍👨🏽', '👩🏻‍❤️‍👨🏼', '👩🏻‍❤️‍👨🏻'], + types: ['👩🏿‍❤️‍👨🏿', '👩🏾‍❤️‍👨🏾', '👩🏽‍❤️‍👨🏽', '👩🏼‍❤️‍👨🏼', '👩🏻‍❤️‍👨🏻'], }, { name: 'couple_with_heart_man_man', code: '👨‍❤️‍👨', - types: ['👨🏿‍❤️‍👨🏿', '👨🏿‍❤️‍👨🏾', '👨🏿‍❤️‍👨🏽', '👨🏿‍❤️‍👨🏼', '👨🏿‍❤️‍👨🏻', '👨🏾‍❤️‍👨🏿', '👨🏾‍❤️‍👨🏾', '👨🏾‍❤️‍👨🏽', '👨🏾‍❤️‍👨🏼', '👨🏾‍❤️‍👨🏻', '👨🏽‍❤️‍👨🏿', '👨🏽‍❤️‍👨🏾', '👨🏽‍❤️‍👨🏽', '👨🏽‍❤️‍👨🏼', '👨🏽‍❤️‍👨🏻', '👨🏼‍❤️‍👨🏿', '👨🏼‍❤️‍👨🏾', '👨🏼‍❤️‍👨🏽', '👨🏼‍❤️‍👨🏼', '👨🏼‍❤️‍👨🏻', '👨🏻‍❤️‍👨🏿', '👨🏻‍❤️‍👨🏾', '👨🏻‍❤️‍👨🏽', '👨🏻‍❤️‍👨🏼', '👨🏻‍❤️‍👨🏻'], + types: ['👨🏿‍❤️‍👨🏿', '👨🏾‍❤️‍👨🏾', '👨🏽‍❤️‍👨🏽', '👨🏼‍❤️‍👨🏼', '👨🏻‍❤️‍👨🏻'], }, { name: 'couple_with_heart_woman_woman', code: '👩‍❤️‍👩', - types: ['👩🏿‍❤️‍👩🏿', '👩🏿‍❤️‍👩🏾', '👩🏿‍❤️‍👩🏽', '👩🏿‍❤️‍👩🏼', '👩🏿‍❤️‍👩🏻', '👩🏾‍❤️‍👩🏿', '👩🏾‍❤️‍👩🏾', '👩🏾‍❤️‍👩🏽', '👩🏾‍❤️‍👩🏼', '👩🏾‍❤️‍👩🏻', '👩🏽‍❤️‍👩🏿', '👩🏽‍❤️‍👩🏾', '👩🏽‍❤️‍👩🏽', '👩🏽‍❤️‍👩🏼', '👩🏽‍❤️‍👩🏻', '👩🏼‍❤️‍👩🏿', '👩🏼‍❤️‍👩🏾', '👩🏼‍❤️‍👩🏽', '👩🏼‍❤️‍👩🏼', '👩🏼‍❤️‍👩🏻', '👩🏻‍❤️‍👩🏿', '👩🏻‍❤️‍👩🏾', '👩🏻‍❤️‍👩🏽', '👩🏻‍❤️‍👩🏼', '👩🏻‍❤️‍👩🏻'], + types: ['👩🏿‍❤️‍👩🏿', '👩🏾‍❤️‍👩🏾', '👩🏽‍❤️‍👩🏽', '👩🏼‍❤️‍👩🏼', '👩🏻‍❤️‍👩🏻'], }, { name: 'family', diff --git a/assets/images/expensify-logo-new.svg b/assets/images/expensify-logo-new.svg new file mode 100644 index 000000000000..89102ecbc5e4 --- /dev/null +++ b/assets/images/expensify-logo-new.svg @@ -0,0 +1 @@ + diff --git a/assets/images/fullscreen.svg b/assets/images/fullscreen.svg new file mode 100644 index 000000000000..6ae05ed9ad16 --- /dev/null +++ b/assets/images/fullscreen.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/images/meter.svg b/assets/images/meter.svg new file mode 100644 index 000000000000..4808c3e6870a --- /dev/null +++ b/assets/images/meter.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/mute.svg b/assets/images/mute.svg new file mode 100644 index 000000000000..afe5e802e640 --- /dev/null +++ b/assets/images/mute.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/new-expensify.svg b/assets/images/new-expensify.svg index 89102ecbc5e4..3b2e77807026 100644 --- a/assets/images/new-expensify.svg +++ b/assets/images/new-expensify.svg @@ -1 +1 @@ - + diff --git a/assets/images/pause.svg b/assets/images/pause.svg new file mode 100644 index 000000000000..8546a7e5a0ee --- /dev/null +++ b/assets/images/pause.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/play.svg b/assets/images/play.svg new file mode 100644 index 000000000000..1695800f8498 --- /dev/null +++ b/assets/images/play.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/assets/images/simple-illustrations/simple-illustration__house.svg b/assets/images/simple-illustrations/simple-illustration__house.svg new file mode 100644 index 000000000000..1915ffc57ecf --- /dev/null +++ b/assets/images/simple-illustrations/simple-illustration__house.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/volume-high.svg b/assets/images/volume-high.svg new file mode 100644 index 000000000000..90eb27e24e85 --- /dev/null +++ b/assets/images/volume-high.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/images/volume-low.svg b/assets/images/volume-low.svg new file mode 100644 index 000000000000..d0d4224f0660 --- /dev/null +++ b/assets/images/volume-low.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/workspace-profile.png b/assets/images/workspace-profile.png new file mode 100644 index 000000000000..335568f3bd23 Binary files /dev/null and b/assets/images/workspace-profile.png differ diff --git a/config/webpack/webpack.common.js b/config/webpack/webpack.common.js index 8ad574d3b2e0..209a4c0c2753 100644 --- a/config/webpack/webpack.common.js +++ b/config/webpack/webpack.common.js @@ -22,6 +22,8 @@ const includeModules = [ 'react-native-google-places-autocomplete', 'react-native-qrcode-svg', 'react-native-view-shot', + '@react-native/assets', + 'expo-av', ].join('|'); const envToLogoSuffixMap = { @@ -97,6 +99,7 @@ const webpackConfig = ({envFile = '.env', platform = 'web'}) => ({ {from: 'web/apple-touch-icon.png'}, {from: 'assets/images/expensify-app-icon.svg'}, {from: 'web/manifest.json'}, + {from: 'web/gtm.js'}, {from: 'assets/css', to: 'css'}, {from: 'assets/fonts/web', to: 'fonts'}, {from: 'assets/sounds', to: 'sounds'}, @@ -240,6 +243,7 @@ const webpackConfig = ({envFile = '.env', platform = 'web'}) => ({ ], fallback: { 'process/browser': require.resolve('process/browser'), + crypto: false, }, }, diff --git a/contributingGuides/FORMS.md b/contributingGuides/FORMS.md index d87f9f889090..b2f912277dc5 100644 --- a/contributingGuides/FORMS.md +++ b/contributingGuides/FORMS.md @@ -10,7 +10,7 @@ Any form input needs to be wrapped in [InputWrapper](https://github.com/Expensif @@ -248,7 +248,7 @@ function onSubmit(values) { @@ -263,7 +263,7 @@ const BankAccountForm = () => ( @@ -271,7 +271,7 @@ const BankAccountForm = () => ( diff --git a/docs/articles/expensify-classic/settings/account-settings/Add-profile-photo.md b/docs/articles/expensify-classic/settings/account-settings/Add-profile-photo.md new file mode 100644 index 000000000000..dc33ad76cdce --- /dev/null +++ b/docs/articles/expensify-classic/settings/account-settings/Add-profile-photo.md @@ -0,0 +1,30 @@ +--- +title: Add profile photo +description: Add a photo to your Expensify account +--- +
+ +{% include selector.html values="desktop, mobile" %} + +{% include option.html value="desktop" %} +1. Click the profile image at the top of the main menu. +2. Hover over the profile picture and click **Change**. +3. Click **Add Photo** and select a photo from your computer. +{% include end-option.html %} + +{% include option.html value="mobile" %} +1. Tap the ☰ menu icon in the top left. +2. Tap the profile picture icon. +3. Tap the Edit icon next to your name +4. Tap **Upload Photo** and either: + - Tap the capture button to take a new photo. + - Tap the photo icon on the left to select a saved photo. +{% include end-option.html %} + +{% include end-selector.html %} + +# FAQs + +**Why do I already have a profile picture that I didn’t add?** + +Expensify automatically adds an image to your account if it finds one that is associated with your email address. diff --git a/docs/expensify-classic/hubs/settings/account-settings.html b/docs/expensify-classic/hubs/settings/account-settings.html new file mode 100644 index 000000000000..e53f92312b7c --- /dev/null +++ b/docs/expensify-classic/hubs/settings/account-settings.html @@ -0,0 +1,6 @@ +--- +layout: default +title: Account Settings +--- + +{% include section.html %} diff --git a/ios/NewExpensify.xcodeproj/project.pbxproj b/ios/NewExpensify.xcodeproj/project.pbxproj index 9b1451b2bf94..acfc4d933954 100644 --- a/ios/NewExpensify.xcodeproj/project.pbxproj +++ b/ios/NewExpensify.xcodeproj/project.pbxproj @@ -7,7 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 059DC4EFD39EF39437E6823D /* libPods-NotificationServiceExtension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A997AA8204EA3D90907FA80 /* libPods-NotificationServiceExtension.a */; }; 083353EB2B5AB22A00C603C0 /* attention.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 083353E72B5AB22900C603C0 /* attention.mp3 */; }; 083353EC2B5AB22A00C603C0 /* done.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 083353E82B5AB22900C603C0 /* done.mp3 */; }; 083353ED2B5AB22A00C603C0 /* receive.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 083353E92B5AB22900C603C0 /* receive.mp3 */; }; @@ -26,23 +25,23 @@ 26AF3C3540374A9FACB6C19E /* ExpensifyMono-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = DCF33E34FFEC48128CDD41D4 /* ExpensifyMono-Bold.otf */; }; 2A9F8CDA983746B0B9204209 /* ExpensifyNeue-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 52796131E6554494B2DDB056 /* ExpensifyNeue-Bold.otf */; }; 30581EA8AAFD4FCE88C5D191 /* ExpensifyNeue-Italic.otf in Resources */ = {isa = PBXBuildFile; fileRef = BF6A4C5167244B9FB8E4D4E3 /* ExpensifyNeue-Italic.otf */; }; - 3661A1374980E5F6804511FE /* libPods-NewExpensify-NewExpensifyTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 076FD9E41E08971BBF51D580 /* libPods-NewExpensify-NewExpensifyTests.a */; }; 374FB8D728A133FE000D84EF /* OriginImageRequestHandler.mm in Sources */ = {isa = PBXBuildFile; fileRef = 374FB8D628A133FE000D84EF /* OriginImageRequestHandler.mm */; }; + 5B8996A7D8B007ECC41919E1 /* libPods-NewExpensify.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00D7E69E5ADD16FD4C44221B /* libPods-NewExpensify.a */; }; 7041848526A8E47D00E09F4D /* RCTStartupTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 7041848426A8E47D00E09F4D /* RCTStartupTimer.m */; }; 7041848626A8E47D00E09F4D /* RCTStartupTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 7041848426A8E47D00E09F4D /* RCTStartupTimer.m */; }; 70CF6E82262E297300711ADC /* BootSplash.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 70CF6E81262E297300711ADC /* BootSplash.storyboard */; }; + 716815DBCE9F49D420334791 /* libPods-NewExpensify-NewExpensifyTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B68AEB9429D8BB73F25A188C /* libPods-NewExpensify-NewExpensifyTests.a */; }; 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 */; }; + B8A1CD44D011AD7AE180DE14 /* libPods-NotificationServiceExtension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B944D5699A54FD5197A63866 /* libPods-NotificationServiceExtension.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 */; }; DDCB2E57F334C143AC462B43 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D20D83B0E39BA6D21761E72 /* ExpoModulesProvider.swift */; }; 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 */ @@ -80,9 +79,10 @@ /* Begin PBXFileReference section */ 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; }; + 00D7E69E5ADD16FD4C44221B /* libPods-NewExpensify.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-NewExpensify.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 00E356EE1AD99517003FC87E /* NewExpensifyTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NewExpensifyTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 076FD9E41E08971BBF51D580 /* libPods-NewExpensify-NewExpensifyTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-NewExpensify-NewExpensifyTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 030F99CEB3AEF1F11B001798 /* Pods-NewExpensify-NewExpensifyTests.debugproduction.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify-NewExpensifyTests.debugproduction.xcconfig"; path = "Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests.debugproduction.xcconfig"; sourceTree = ""; }; 083353E72B5AB22900C603C0 /* attention.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = attention.mp3; path = ../assets/sounds/attention.mp3; sourceTree = ""; }; 083353E82B5AB22900C603C0 /* done.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = done.mp3; path = ../assets/sounds/done.mp3; sourceTree = ""; }; 083353E92B5AB22900C603C0 /* receive.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = receive.mp3; path = ../assets/sounds/receive.mp3; sourceTree = ""; }; @@ -92,72 +92,55 @@ 0F5BE0CD252686320097D869 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 0F5E534E263B73D5004CA14F /* EnvironmentChecker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EnvironmentChecker.h; sourceTree = ""; }; 0F5E534F263B73FD004CA14F /* EnvironmentChecker.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EnvironmentChecker.m; sourceTree = ""; }; + 0FF2BC0D01CA2C62CE94229E /* 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 = ""; }; + 11B2BA236BB72A603FBB7B99 /* 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 = ""; }; + 12EB734390650799DB8AC627 /* 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 = ""; }; 13B07F961A680F5B00A75B9A /* New Expensify Dev.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "New Expensify Dev.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = NewExpensify/AppDelegate.h; sourceTree = ""; }; 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 = ""; }; + 3328C9B8861CA667C42B47F3 /* 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 = ""; }; 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 = ""; }; 44BF435285B94E5B95F90994 /* ExpensifyNewKansas-Medium.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "ExpensifyNewKansas-Medium.otf"; path = "../assets/fonts/native/ExpensifyNewKansas-Medium.otf"; sourceTree = ""; }; + 466D03D63F4B48E009C04FA3 /* 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 = ""; }; 4D20D83B0E39BA6D21761E72 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-NewExpensify/ExpoModulesProvider.swift"; sourceTree = ""; }; - 4E9593A0EE1C84B8A8EC062F /* 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 = ""; }; 52796131E6554494B2DDB056 /* ExpensifyNeue-Bold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "ExpensifyNeue-Bold.otf"; path = "../assets/fonts/native/ExpensifyNeue-Bold.otf"; sourceTree = ""; }; - 52E63EFD054926BFEA3EC143 /* Pods-NewExpensify-NewExpensifyTests.debugproduction.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify-NewExpensifyTests.debugproduction.xcconfig"; path = "Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests.debugproduction.xcconfig"; sourceTree = ""; }; - 68F4F270A8D1414FC14F356F /* 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 = ""; }; + 5A1F158A9A6CBE170EC19D9C /* 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 = ""; }; 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 = ""; }; + 7312B334B72E8BE41A811FAB /* 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 = ""; }; 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 = ""; }; + 802CB9E7554756F188C79554 /* 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 = ""; }; 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; }; + 96ADA7C82BA6A08C4A56344A /* 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 = ""; }; + 972584042DB4782F830B063A /* 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 = ""; }; + 9EADA69D62F2E7B3D96E5B1C /* 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 = ""; }; + AC9422F6C8A49AE701481721 /* 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 = ""; }; + B4CF7147C89747459BAC5BB7 /* 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 = ""; }; + B68AEB9429D8BB73F25A188C /* libPods-NewExpensify-NewExpensifyTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-NewExpensify-NewExpensifyTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + B944D5699A54FD5197A63866 /* libPods-NotificationServiceExtension.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-NotificationServiceExtension.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 = ""; }; + CB32BB7E082E2450F04DA6E7 /* 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 = ""; }; + D15262BE5F713CDB4DA576AE /* 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 = ""; }; 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 = ""; }; + E750C93A45B47BDC5149C5AA /* 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 = ""; }; 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 = ""; }; + F98306ABF3F272DF04DF65CC /* 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 = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -165,7 +148,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 3661A1374980E5F6804511FE /* libPods-NewExpensify-NewExpensifyTests.a in Frameworks */, + 716815DBCE9F49D420334791 /* libPods-NewExpensify-NewExpensifyTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -174,9 +157,8 @@ buildActionMask = 2147483647; files = ( E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */, - 976CCB5F8C921482E6AEAE71 /* libPods-NewExpensify.a in Frameworks */, E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */, - EEAE4F8907465429AA5B5520 /* libPods-NewExpensify.a in Frameworks */, + 5B8996A7D8B007ECC41919E1 /* libPods-NewExpensify.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -184,7 +166,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 059DC4EFD39EF39437E6823D /* libPods-NotificationServiceExtension.a in Frameworks */, + B8A1CD44D011AD7AE180DE14 /* libPods-NotificationServiceExtension.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -230,10 +212,9 @@ children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, ED2971642150620600B7C4FE /* JavaScriptCore.framework */, - AEFE6CD54912D427D19133C7 /* libPods-NewExpensify.a */, - 1A997AA8204EA3D90907FA80 /* libPods-NotificationServiceExtension.a */, - AB40AC8872A3DD6EF53D8B94 /* libPods-NewExpensify.a */, - 076FD9E41E08971BBF51D580 /* libPods-NewExpensify-NewExpensifyTests.a */, + 00D7E69E5ADD16FD4C44221B /* libPods-NewExpensify.a */, + B68AEB9429D8BB73F25A188C /* libPods-NewExpensify-NewExpensifyTests.a */, + B944D5699A54FD5197A63866 /* libPods-NotificationServiceExtension.a */, ); name = Frameworks; sourceTree = ""; @@ -338,39 +319,24 @@ 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 */, - 76BE68DA894BB75DDFE278DC /* Pods-NewExpensify.releasedevelopment.xcconfig */, - 68F4F270A8D1414FC14F356F /* Pods-NewExpensify.releaseadhoc.xcconfig */, - 32181F72DC539FFD1D1F0CA4 /* Pods-NewExpensify.releaseproduction.xcconfig */, - 3BBA44B891E03FAB8255E6F1 /* Pods-NewExpensify-NewExpensifyTests.debugdevelopment.xcconfig */, - 4E9593A0EE1C84B8A8EC062F /* Pods-NewExpensify-NewExpensifyTests.debugadhoc.xcconfig */, - 52E63EFD054926BFEA3EC143 /* Pods-NewExpensify-NewExpensifyTests.debugproduction.xcconfig */, - E681F80D97E6E4BB26194246 /* Pods-NewExpensify-NewExpensifyTests.releasedevelopment.xcconfig */, - FF0EADDA6099EF76253FA7AB /* Pods-NewExpensify-NewExpensifyTests.releaseadhoc.xcconfig */, - E2F78D2A9B3DB96F0524690B /* Pods-NewExpensify-NewExpensifyTests.releaseproduction.xcconfig */, + 0FF2BC0D01CA2C62CE94229E /* Pods-NewExpensify.debugdevelopment.xcconfig */, + 5A1F158A9A6CBE170EC19D9C /* Pods-NewExpensify.debugadhoc.xcconfig */, + B4CF7147C89747459BAC5BB7 /* Pods-NewExpensify.releasedevelopment.xcconfig */, + 7312B334B72E8BE41A811FAB /* Pods-NewExpensify.releaseadhoc.xcconfig */, + 9EADA69D62F2E7B3D96E5B1C /* Pods-NewExpensify.releaseproduction.xcconfig */, + E750C93A45B47BDC5149C5AA /* Pods-NewExpensify-NewExpensifyTests.debugdevelopment.xcconfig */, + F98306ABF3F272DF04DF65CC /* Pods-NewExpensify-NewExpensifyTests.debugadhoc.xcconfig */, + 972584042DB4782F830B063A /* Pods-NewExpensify-NewExpensifyTests.releasedevelopment.xcconfig */, + D15262BE5F713CDB4DA576AE /* Pods-NewExpensify-NewExpensifyTests.releaseadhoc.xcconfig */, + 466D03D63F4B48E009C04FA3 /* Pods-NewExpensify-NewExpensifyTests.releaseproduction.xcconfig */, + 12EB734390650799DB8AC627 /* Pods-NotificationServiceExtension.debugdevelopment.xcconfig */, + 802CB9E7554756F188C79554 /* Pods-NotificationServiceExtension.debugadhoc.xcconfig */, + CB32BB7E082E2450F04DA6E7 /* Pods-NotificationServiceExtension.debugproduction.xcconfig */, + 11B2BA236BB72A603FBB7B99 /* Pods-NotificationServiceExtension.releasedevelopment.xcconfig */, + AC9422F6C8A49AE701481721 /* Pods-NotificationServiceExtension.releaseadhoc.xcconfig */, + 96ADA7C82BA6A08C4A56344A /* Pods-NotificationServiceExtension.releaseproduction.xcconfig */, + 3328C9B8861CA667C42B47F3 /* Pods-NewExpensify.debugproduction.xcconfig */, + 030F99CEB3AEF1F11B001798 /* Pods-NewExpensify-NewExpensifyTests.debugproduction.xcconfig */, ); path = Pods; sourceTree = ""; @@ -382,13 +348,13 @@ isa = PBXNativeTarget; buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "NewExpensifyTests" */; buildPhases = ( - A3D1E02743106A34295E533A /* [CP] Check Pods Manifest.lock */, + D128CAF2A1070B0F2B3F12E4 /* [CP] Check Pods Manifest.lock */, 04B99F6AA578E2A877802F05 /* [Expo] Configure project */, 00E356EA1AD99517003FC87E /* Sources */, 00E356EB1AD99517003FC87E /* Frameworks */, 00E356EC1AD99517003FC87E /* Resources */, - 822809AAD6B368BF9F9BA00E /* [CP] Embed Pods Frameworks */, - 5CC6761AF98472E1C710DB80 /* [CP] Copy Pods Resources */, + CE4668E6E6809DEA0626B5B3 /* [CP] Copy Pods Resources */, + 47778FAEB28E5E04DCCD0D39 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -404,7 +370,7 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "NewExpensify" */; buildPhases = ( - 468C095F6D4C79E555B55A4F /* [CP] Check Pods Manifest.lock */, + 5DDD2A7C43E9B381CD68A232 /* [CP] Check Pods Manifest.lock */, FD10A7F022414F080027D42C /* Start Packager */, 5CF45ABA52C0BB0D7B9D139A /* [Expo] Configure project */, 13B07F871A680F5B00A75B9A /* Sources */, @@ -412,10 +378,10 @@ 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - CB8E29994749C6913C3FA05D /* [CP] Embed Pods Frameworks */, - F6E16E41F88F567A8CDD037C /* [CP] Copy Pods Resources */, - 04A2B3BE14CFE4961BE987E8 /* [CP-User] [RNFB] Core Configuration */, - 2D8F47B51A8E72FBA2BA4874 /* [CP-User] [RNFB] Crashlytics Configuration */, + 50067B6C26F88BAB5F0B478A /* [CP] Embed Pods Frameworks */, + 4E1386759AEE7859543483C9 /* [CP] Copy Pods Resources */, + EEA310C4723D0EF8581FAA1D /* [CP-User] [RNFB] Core Configuration */, + A9A9BDEB11952C562415526C /* [CP-User] [RNFB] Crashlytics Configuration */, ); buildRules = ( ); @@ -431,7 +397,7 @@ isa = PBXNativeTarget; buildConfigurationList = 7FD73CAA2B23CE9500420AF3 /* Build configuration list for PBXNativeTarget "NotificationServiceExtension" */; buildPhases = ( - F3D35ED760B830954BD8A7BB /* [CP] Check Pods Manifest.lock */, + 7B34459944ACFD30D85D5F85 /* [CP] Check Pods Manifest.lock */, 7FD73C972B23CE9500420AF3 /* Sources */, 7FD73C982B23CE9500420AF3 /* Frameworks */, 7FD73C992B23CE9500420AF3 /* Resources */, @@ -549,19 +515,6 @@ shellPath = /bin/sh; shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\nif [[ \"$CONFIGURATION\" = *Debug* ]]; then\n export SKIP_BUNDLING=1\nfi\nif [[ -z \"$ENTRY_FILE\" ]]; then\n # Set the entry JS file using the bundler's entry resolution.\n export ENTRY_FILE=\"$(\"$NODE_BINARY\" -e \"require('expo/scripts/resolveAppEntry')\" \"$PROJECT_ROOT\" ios relative | tail -n 1)\"\nfi\n\nif [[ -z \"$CLI_PATH\" ]]; then\n # Use Expo CLI\n export CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require.resolve('@expo/cli')\")\"\nfi\nif [[ -z \"$BUNDLE_COMMAND\" ]]; then\n # Default Expo CLI command for bundling\n export BUNDLE_COMMAND=\"export:embed\"\nfi\n\n`\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n"; }; - 04A2B3BE14CFE4961BE987E8 /* [CP-User] [RNFB] Core Configuration */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)", - ); - name = "[CP-User] [RNFB] Core Configuration"; - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nset -e\n\n_MAX_LOOKUPS=2;\n_SEARCH_RESULT=''\n_RN_ROOT_EXISTS=''\n_CURRENT_LOOKUPS=1\n_JSON_ROOT=\"'react-native'\"\n_JSON_FILE_NAME='firebase.json'\n_JSON_OUTPUT_BASE64='e30=' # { }\n_CURRENT_SEARCH_DIR=${PROJECT_DIR}\n_PLIST_BUDDY=/usr/libexec/PlistBuddy\n_TARGET_PLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n_DSYM_PLIST=\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist\"\n\n# plist arrays\n_PLIST_ENTRY_KEYS=()\n_PLIST_ENTRY_TYPES=()\n_PLIST_ENTRY_VALUES=()\n\nfunction setPlistValue {\n echo \"info: setting plist entry '$1' of type '$2' in file '$4'\"\n ${_PLIST_BUDDY} -c \"Add :$1 $2 '$3'\" $4 || echo \"info: '$1' already exists\"\n}\n\nfunction getFirebaseJsonKeyValue () {\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n ruby -e \"require 'rubygems';require 'json'; output=JSON.parse('$1'); puts output[$_JSON_ROOT]['$2']\"\n else\n echo \"\"\n fi;\n}\n\nfunction jsonBoolToYesNo () {\n if [[ $1 == \"false\" ]]; then\n echo \"NO\"\n elif [[ $1 == \"true\" ]]; then\n echo \"YES\"\n else echo \"NO\"\n fi\n}\n\necho \"info: -> RNFB build script started\"\necho \"info: 1) Locating ${_JSON_FILE_NAME} file:\"\n\nif [[ -z ${_CURRENT_SEARCH_DIR} ]]; then\n _CURRENT_SEARCH_DIR=$(pwd)\nfi;\n\nwhile true; do\n _CURRENT_SEARCH_DIR=$(dirname \"$_CURRENT_SEARCH_DIR\")\n if [[ \"$_CURRENT_SEARCH_DIR\" == \"/\" ]] || [[ ${_CURRENT_LOOKUPS} -gt ${_MAX_LOOKUPS} ]]; then break; fi;\n echo \"info: ($_CURRENT_LOOKUPS of $_MAX_LOOKUPS) Searching in '$_CURRENT_SEARCH_DIR' for a ${_JSON_FILE_NAME} file.\"\n _SEARCH_RESULT=$(find \"$_CURRENT_SEARCH_DIR\" -maxdepth 2 -name ${_JSON_FILE_NAME} -print | /usr/bin/head -n 1)\n if [[ ${_SEARCH_RESULT} ]]; then\n echo \"info: ${_JSON_FILE_NAME} found at $_SEARCH_RESULT\"\n break;\n fi;\n _CURRENT_LOOKUPS=$((_CURRENT_LOOKUPS+1))\ndone\n\nif [[ ${_SEARCH_RESULT} ]]; then\n _JSON_OUTPUT_RAW=$(cat \"${_SEARCH_RESULT}\")\n _RN_ROOT_EXISTS=$(ruby -e \"require 'rubygems';require 'json'; output=JSON.parse('$_JSON_OUTPUT_RAW'); puts output[$_JSON_ROOT]\" || echo '')\n\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n _JSON_OUTPUT_BASE64=$(python -c 'import json,sys,base64;print(base64.b64encode(json.dumps(json.loads(open('\"'${_SEARCH_RESULT}'\"').read())['${_JSON_ROOT}'])))' || echo \"e30=\")\n fi\n\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n\n # config.app_data_collection_default_enabled\n _APP_DATA_COLLECTION_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_data_collection_default_enabled\")\n if [[ $_APP_DATA_COLLECTION_ENABLED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseDataCollectionDefaultEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_DATA_COLLECTION_ENABLED\")\")\n fi\n\n # config.analytics_auto_collection_enabled\n _ANALYTICS_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_auto_collection_enabled\")\n if [[ $_ANALYTICS_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_COLLECTION\")\")\n fi\n\n # config.analytics_collection_deactivated\n _ANALYTICS_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_collection_deactivated\")\n if [[ $_ANALYTICS_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_DEACTIVATED\")\")\n fi\n\n # config.analytics_idfv_collection_enabled\n _ANALYTICS_IDFV_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_idfv_collection_enabled\")\n if [[ $_ANALYTICS_IDFV_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_IDFV_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_IDFV_COLLECTION\")\")\n fi\n\n # config.analytics_default_allow_ad_personalization_signals\n _ANALYTICS_PERSONALIZATION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_personalization_signals\")\n if [[ $_ANALYTICS_PERSONALIZATION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_PERSONALIZATION_SIGNALS\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_PERSONALIZATION\")\")\n fi\n\n # config.perf_auto_collection_enabled\n _PERF_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_auto_collection_enabled\")\n if [[ $_PERF_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_enabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_AUTO_COLLECTION\")\")\n fi\n\n # config.perf_collection_deactivated\n _PERF_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_collection_deactivated\")\n if [[ $_PERF_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_deactivated\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_DEACTIVATED\")\")\n fi\n\n # config.messaging_auto_init_enabled\n _MESSAGING_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"messaging_auto_init_enabled\")\n if [[ $_MESSAGING_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseMessagingAutoInitEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_MESSAGING_AUTO_INIT\")\")\n fi\n\n # config.in_app_messaging_auto_colllection_enabled\n _FIAM_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"in_app_messaging_auto_collection_enabled\")\n if [[ $_FIAM_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseInAppMessagingAutomaticDataCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_FIAM_AUTO_INIT\")\")\n fi\n\n # config.app_check_token_auto_refresh\n _APP_CHECK_TOKEN_AUTO_REFRESH=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_check_token_auto_refresh\")\n if [[ $_APP_CHECK_TOKEN_AUTO_REFRESH ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAppCheckTokenAutoRefreshEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_CHECK_TOKEN_AUTO_REFRESH\")\")\n fi\n\n # config.crashlytics_disable_auto_disabler - undocumented for now - mainly for debugging, document if becomes useful\n _CRASHLYTICS_AUTO_DISABLE_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"crashlytics_disable_auto_disabler\")\n if [[ $_CRASHLYTICS_AUTO_DISABLE_ENABLED == \"true\" ]]; then\n echo \"Disabled Crashlytics auto disabler.\" # do nothing\n else\n _PLIST_ENTRY_KEYS+=(\"FirebaseCrashlyticsCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"NO\")\n fi\nelse\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n echo \"warning: A firebase.json file was not found, whilst this file is optional it is recommended to include it to configure firebase services in React Native Firebase.\"\nfi;\n\necho \"info: 2) Injecting Info.plist entries: \"\n\n# Log out the keys we're adding\nfor i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n echo \" -> $i) ${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\"\ndone\n\nfor plist in \"${_TARGET_PLIST}\" \"${_DSYM_PLIST}\" ; do\n if [[ -f \"${plist}\" ]]; then\n\n # paths with spaces break the call to setPlistValue. temporarily modify\n # the shell internal field separator variable (IFS), which normally\n # includes spaces, to consist only of line breaks\n oldifs=$IFS\n IFS=\"\n\"\n\n for i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n setPlistValue \"${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\" \"${plist}\"\n done\n\n # restore the original internal field separator value\n IFS=$oldifs\n else\n echo \"warning: A Info.plist build output file was not found (${plist})\"\n fi\ndone\n\necho \"info: <- RNFB build script finished\"\n"; - }; 04B99F6AA578E2A877802F05 /* [Expo] Configure project */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -581,57 +534,59 @@ shellPath = /bin/sh; shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-NewExpensify-NewExpensifyTests/expo-configure-project.sh\"\n"; }; - 2D8F47B51A8E72FBA2BA4874 /* [CP-User] [RNFB] Crashlytics Configuration */ = { + 47778FAEB28E5E04DCCD0D39 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${TARGET_NAME}", - "$(SRCROOT)/$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)", - ); - name = "[CP-User] [RNFB] Crashlytics Configuration"; - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nset -e\n\nif [[ ${PODS_ROOT} ]]; then\n echo \"info: Exec FirebaseCrashlytics Run from Pods\"\n \"${PODS_ROOT}/FirebaseCrashlytics/run\"\nelse\n echo \"info: Exec FirebaseCrashlytics Run from framework\"\n \"${PROJECT_DIR}/FirebaseCrashlytics.framework/run\"\nfi\n"; - }; - 468C095F6D4C79E555B55A4F /* [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 = ( + "${PODS_ROOT}/Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/MapboxMaps/MapboxMaps.framework", + "${BUILT_PRODUCTS_DIR}/Turf/Turf.framework", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/Flipper-DoubleConversion/double-conversion.framework/double-conversion", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/Flipper-Glog/glog.framework/glog", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/MapboxCommon/MapboxCommon.framework/MapboxCommon", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/MapboxCoreMaps/MapboxCoreMaps.framework/MapboxCoreMaps", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/MapboxMobileEvents/MapboxMobileEvents.framework/MapboxMobileEvents", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/Onfido/Onfido.framework/Onfido", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL-Universal/OpenSSL.framework/OpenSSL", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/Plaid/LinkKit.framework/LinkKit", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", ); + name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-NewExpensify-checkManifestLockResult.txt", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxMaps.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Turf.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/double-conversion.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/glog.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxCommon.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxCoreMaps.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxMobileEvents.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Onfido.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/LinkKit.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 5CC6761AF98472E1C710DB80 /* [CP] Copy Pods Resources */ = { + 4E1386759AEE7859543483C9 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests-resources.sh", + "${PODS_ROOT}/Target Support Files/Pods-NewExpensify/Pods-NewExpensify-resources.sh", "${PODS_CONFIGURATION_BUILD_DIR}/Airship/AirshipAutomationResources.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/Airship/AirshipCoreResources.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/Airship/AirshipExtendedActionsResources.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/Airship/AirshipMessageCenterResources.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/Airship/AirshipPreferenceCenterResources.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/GoogleSignIn/GoogleSignIn.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle", "${PODS_ROOT}/../../node_modules/@expensify/react-native-live-markdown/parser/react-native-live-markdown-parser.js", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( @@ -641,40 +596,21 @@ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AirshipMessageCenterResources.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AirshipPreferenceCenterResources.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleSignIn.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/react-native-live-markdown-parser.js", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NewExpensify/Pods-NewExpensify-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 5CF45ABA52C0BB0D7B9D139A /* [Expo] Configure project */ = { + 50067B6C26F88BAB5F0B478A /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( - ); - name = "[Expo] Configure project"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-NewExpensify/expo-configure-project.sh\"\n"; - }; - 822809AAD6B368BF9F9BA00E /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests-frameworks.sh", + "${PODS_ROOT}/Target Support Files/Pods-NewExpensify/Pods-NewExpensify-frameworks.sh", "${BUILT_PRODUCTS_DIR}/MapboxMaps/MapboxMaps.framework", "${BUILT_PRODUCTS_DIR}/Turf/Turf.framework", "${PODS_XCFRAMEWORKS_BUILD_DIR}/Flipper-DoubleConversion/double-conversion.framework/double-conversion", @@ -703,70 +639,51 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NewExpensify/Pods-NewExpensify-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - A3D1E02743106A34295E533A /* [CP] Check Pods Manifest.lock */ = { + 5CF45ABA52C0BB0D7B9D139A /* [Expo] Configure project */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", ); - name = "[CP] Check Pods Manifest.lock"; + name = "[Expo] Configure project"; outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-NewExpensify-NewExpensifyTests-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; + shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-NewExpensify/expo-configure-project.sh\"\n"; }; - CB8E29994749C6913C3FA05D /* [CP] Embed Pods Frameworks */ = { + 5DDD2A7C43E9B381CD68A232 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-NewExpensify/Pods-NewExpensify-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/MapboxMaps/MapboxMaps.framework", - "${BUILT_PRODUCTS_DIR}/Turf/Turf.framework", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/Flipper-DoubleConversion/double-conversion.framework/double-conversion", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/Flipper-Glog/glog.framework/glog", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/MapboxCommon/MapboxCommon.framework/MapboxCommon", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/MapboxCoreMaps/MapboxCoreMaps.framework/MapboxCoreMaps", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/MapboxMobileEvents/MapboxMobileEvents.framework/MapboxMobileEvents", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/Onfido/Onfido.framework/Onfido", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL-Universal/OpenSSL.framework/OpenSSL", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/Plaid/LinkKit.framework/LinkKit", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); - name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxMaps.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Turf.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/double-conversion.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/glog.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxCommon.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxCoreMaps.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxMobileEvents.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Onfido.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/LinkKit.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", + "$(DERIVED_FILE_DIR)/Pods-NewExpensify-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NewExpensify/Pods-NewExpensify-frameworks.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - F3D35ED760B830954BD8A7BB /* [CP] Check Pods Manifest.lock */ = { + 7B34459944ACFD30D85D5F85 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -788,21 +705,35 @@ 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 */ = { + A9A9BDEB11952C562415526C /* [CP-User] [RNFB] Crashlytics Configuration */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-NewExpensify/Pods-NewExpensify-resources.sh", + "${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${TARGET_NAME}", + "$(SRCROOT)/$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)", + ); + name = "[CP-User] [RNFB] Crashlytics Configuration"; + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nset -e\n\nif [[ ${PODS_ROOT} ]]; then\n echo \"info: Exec FirebaseCrashlytics Run from Pods\"\n \"${PODS_ROOT}/FirebaseCrashlytics/run\"\nelse\n echo \"info: Exec FirebaseCrashlytics Run from framework\"\n \"${PROJECT_DIR}/FirebaseCrashlytics.framework/run\"\nfi\n"; + }; + CE4668E6E6809DEA0626B5B3 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests-resources.sh", "${PODS_CONFIGURATION_BUILD_DIR}/Airship/AirshipAutomationResources.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/Airship/AirshipCoreResources.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/Airship/AirshipExtendedActionsResources.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/Airship/AirshipMessageCenterResources.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/Airship/AirshipPreferenceCenterResources.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/GoogleSignIn/GoogleSignIn.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle", "${PODS_ROOT}/../../node_modules/@expensify/react-native-live-markdown/parser/react-native-live-markdown-parser.js", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( @@ -812,14 +743,49 @@ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AirshipMessageCenterResources.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AirshipPreferenceCenterResources.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleSignIn.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/react-native-live-markdown-parser.js", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NewExpensify/Pods-NewExpensify-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests-resources.sh\"\n"; showEnvVarsInLog = 0; }; + D128CAF2A1070B0F2B3F12E4 /* [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-NewExpensify-NewExpensifyTests-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; + }; + EEA310C4723D0EF8581FAA1D /* [CP-User] [RNFB] Core Configuration */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)", + ); + name = "[CP-User] [RNFB] Core Configuration"; + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nset -e\n\n_MAX_LOOKUPS=2;\n_SEARCH_RESULT=''\n_RN_ROOT_EXISTS=''\n_CURRENT_LOOKUPS=1\n_JSON_ROOT=\"'react-native'\"\n_JSON_FILE_NAME='firebase.json'\n_JSON_OUTPUT_BASE64='e30=' # { }\n_CURRENT_SEARCH_DIR=${PROJECT_DIR}\n_PLIST_BUDDY=/usr/libexec/PlistBuddy\n_TARGET_PLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n_DSYM_PLIST=\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist\"\n\n# plist arrays\n_PLIST_ENTRY_KEYS=()\n_PLIST_ENTRY_TYPES=()\n_PLIST_ENTRY_VALUES=()\n\nfunction setPlistValue {\n echo \"info: setting plist entry '$1' of type '$2' in file '$4'\"\n ${_PLIST_BUDDY} -c \"Add :$1 $2 '$3'\" $4 || echo \"info: '$1' already exists\"\n}\n\nfunction getFirebaseJsonKeyValue () {\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n ruby -e \"require 'rubygems';require 'json'; output=JSON.parse('$1'); puts output[$_JSON_ROOT]['$2']\"\n else\n echo \"\"\n fi;\n}\n\nfunction jsonBoolToYesNo () {\n if [[ $1 == \"false\" ]]; then\n echo \"NO\"\n elif [[ $1 == \"true\" ]]; then\n echo \"YES\"\n else echo \"NO\"\n fi\n}\n\necho \"info: -> RNFB build script started\"\necho \"info: 1) Locating ${_JSON_FILE_NAME} file:\"\n\nif [[ -z ${_CURRENT_SEARCH_DIR} ]]; then\n _CURRENT_SEARCH_DIR=$(pwd)\nfi;\n\nwhile true; do\n _CURRENT_SEARCH_DIR=$(dirname \"$_CURRENT_SEARCH_DIR\")\n if [[ \"$_CURRENT_SEARCH_DIR\" == \"/\" ]] || [[ ${_CURRENT_LOOKUPS} -gt ${_MAX_LOOKUPS} ]]; then break; fi;\n echo \"info: ($_CURRENT_LOOKUPS of $_MAX_LOOKUPS) Searching in '$_CURRENT_SEARCH_DIR' for a ${_JSON_FILE_NAME} file.\"\n _SEARCH_RESULT=$(find \"$_CURRENT_SEARCH_DIR\" -maxdepth 2 -name ${_JSON_FILE_NAME} -print | /usr/bin/head -n 1)\n if [[ ${_SEARCH_RESULT} ]]; then\n echo \"info: ${_JSON_FILE_NAME} found at $_SEARCH_RESULT\"\n break;\n fi;\n _CURRENT_LOOKUPS=$((_CURRENT_LOOKUPS+1))\ndone\n\nif [[ ${_SEARCH_RESULT} ]]; then\n _JSON_OUTPUT_RAW=$(cat \"${_SEARCH_RESULT}\")\n _RN_ROOT_EXISTS=$(ruby -e \"require 'rubygems';require 'json'; output=JSON.parse('$_JSON_OUTPUT_RAW'); puts output[$_JSON_ROOT]\" || echo '')\n\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n _JSON_OUTPUT_BASE64=$(python -c 'import json,sys,base64;print(base64.b64encode(json.dumps(json.loads(open('\"'${_SEARCH_RESULT}'\"').read())['${_JSON_ROOT}'])))' || echo \"e30=\")\n fi\n\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n\n # config.app_data_collection_default_enabled\n _APP_DATA_COLLECTION_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_data_collection_default_enabled\")\n if [[ $_APP_DATA_COLLECTION_ENABLED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseDataCollectionDefaultEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_DATA_COLLECTION_ENABLED\")\")\n fi\n\n # config.analytics_auto_collection_enabled\n _ANALYTICS_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_auto_collection_enabled\")\n if [[ $_ANALYTICS_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_COLLECTION\")\")\n fi\n\n # config.analytics_collection_deactivated\n _ANALYTICS_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_collection_deactivated\")\n if [[ $_ANALYTICS_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_DEACTIVATED\")\")\n fi\n\n # config.analytics_idfv_collection_enabled\n _ANALYTICS_IDFV_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_idfv_collection_enabled\")\n if [[ $_ANALYTICS_IDFV_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_IDFV_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_IDFV_COLLECTION\")\")\n fi\n\n # config.analytics_default_allow_ad_personalization_signals\n _ANALYTICS_PERSONALIZATION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_personalization_signals\")\n if [[ $_ANALYTICS_PERSONALIZATION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_PERSONALIZATION_SIGNALS\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_PERSONALIZATION\")\")\n fi\n\n # config.perf_auto_collection_enabled\n _PERF_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_auto_collection_enabled\")\n if [[ $_PERF_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_enabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_AUTO_COLLECTION\")\")\n fi\n\n # config.perf_collection_deactivated\n _PERF_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_collection_deactivated\")\n if [[ $_PERF_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_deactivated\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_DEACTIVATED\")\")\n fi\n\n # config.messaging_auto_init_enabled\n _MESSAGING_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"messaging_auto_init_enabled\")\n if [[ $_MESSAGING_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseMessagingAutoInitEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_MESSAGING_AUTO_INIT\")\")\n fi\n\n # config.in_app_messaging_auto_colllection_enabled\n _FIAM_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"in_app_messaging_auto_collection_enabled\")\n if [[ $_FIAM_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseInAppMessagingAutomaticDataCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_FIAM_AUTO_INIT\")\")\n fi\n\n # config.app_check_token_auto_refresh\n _APP_CHECK_TOKEN_AUTO_REFRESH=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_check_token_auto_refresh\")\n if [[ $_APP_CHECK_TOKEN_AUTO_REFRESH ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAppCheckTokenAutoRefreshEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_CHECK_TOKEN_AUTO_REFRESH\")\")\n fi\n\n # config.crashlytics_disable_auto_disabler - undocumented for now - mainly for debugging, document if becomes useful\n _CRASHLYTICS_AUTO_DISABLE_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"crashlytics_disable_auto_disabler\")\n if [[ $_CRASHLYTICS_AUTO_DISABLE_ENABLED == \"true\" ]]; then\n echo \"Disabled Crashlytics auto disabler.\" # do nothing\n else\n _PLIST_ENTRY_KEYS+=(\"FirebaseCrashlyticsCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"NO\")\n fi\nelse\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n echo \"warning: A firebase.json file was not found, whilst this file is optional it is recommended to include it to configure firebase services in React Native Firebase.\"\nfi;\n\necho \"info: 2) Injecting Info.plist entries: \"\n\n# Log out the keys we're adding\nfor i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n echo \" -> $i) ${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\"\ndone\n\nfor plist in \"${_TARGET_PLIST}\" \"${_DSYM_PLIST}\" ; do\n if [[ -f \"${plist}\" ]]; then\n\n # paths with spaces break the call to setPlistValue. temporarily modify\n # the shell internal field separator variable (IFS), which normally\n # includes spaces, to consist only of line breaks\n oldifs=$IFS\n IFS=\"\n\"\n\n for i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n setPlistValue \"${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\" \"${plist}\"\n done\n\n # restore the original internal field separator value\n IFS=$oldifs\n else\n echo \"warning: A Info.plist build output file was not found (${plist})\"\n fi\ndone\n\necho \"info: <- RNFB build script finished\"\n"; + }; FD10A7F022414F080027D42C /* Start Packager */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -895,7 +861,7 @@ /* Begin XCBuildConfiguration section */ 00E356F61AD99517003FC87E /* DebugDevelopment */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 3BBA44B891E03FAB8255E6F1 /* Pods-NewExpensify-NewExpensifyTests.debugdevelopment.xcconfig */; + baseConfigurationReference = E750C93A45B47BDC5149C5AA /* Pods-NewExpensify-NewExpensifyTests.debugdevelopment.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; @@ -925,7 +891,7 @@ }; 00E356F71AD99517003FC87E /* ReleaseDevelopment */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E681F80D97E6E4BB26194246 /* Pods-NewExpensify-NewExpensifyTests.releasedevelopment.xcconfig */; + baseConfigurationReference = 972584042DB4782F830B063A /* Pods-NewExpensify-NewExpensifyTests.releasedevelopment.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -952,7 +918,7 @@ }; 13B07F941A680F5B00A75B9A /* DebugDevelopment */ = { isa = XCBuildConfiguration; - baseConfigurationReference = EA58D43E81BC49541F7FC7E7 /* Pods-NewExpensify.debugdevelopment.xcconfig */; + baseConfigurationReference = 0FF2BC0D01CA2C62CE94229E /* Pods-NewExpensify.debugdevelopment.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIconDev; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; @@ -991,7 +957,7 @@ }; 13B07F951A680F5B00A75B9A /* ReleaseDevelopment */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 76BE68DA894BB75DDFE278DC /* Pods-NewExpensify.releasedevelopment.xcconfig */; + baseConfigurationReference = B4CF7147C89747459BAC5BB7 /* Pods-NewExpensify.releasedevelopment.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIconDev; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; @@ -1028,7 +994,7 @@ }; 7FD73CA42B23CE9500420AF3 /* DebugDevelopment */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FBEBA6FBED49FB41D6F93896 /* Pods-NotificationServiceExtension.debugdevelopment.xcconfig */; + baseConfigurationReference = 12EB734390650799DB8AC627 /* Pods-NotificationServiceExtension.debugdevelopment.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; @@ -1114,7 +1080,7 @@ }; 7FD73CA52B23CE9500420AF3 /* DebugAdHoc */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D3F458C994019E6A571461B7 /* Pods-NotificationServiceExtension.debugadhoc.xcconfig */; + baseConfigurationReference = 802CB9E7554756F188C79554 /* Pods-NotificationServiceExtension.debugadhoc.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; @@ -1199,7 +1165,7 @@ }; 7FD73CA62B23CE9500420AF3 /* DebugProduction */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C3FF914C045A138C061D306E /* Pods-NotificationServiceExtension.debugproduction.xcconfig */; + baseConfigurationReference = CB32BB7E082E2450F04DA6E7 /* Pods-NotificationServiceExtension.debugproduction.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; @@ -1285,7 +1251,7 @@ }; 7FD73CA72B23CE9500420AF3 /* ReleaseDevelopment */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F082D95EE104912B48EA98BA /* Pods-NotificationServiceExtension.releasedevelopment.xcconfig */; + baseConfigurationReference = 11B2BA236BB72A603FBB7B99 /* Pods-NotificationServiceExtension.releasedevelopment.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; @@ -1364,7 +1330,7 @@ }; 7FD73CA82B23CE9500420AF3 /* ReleaseAdHoc */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E61AD6D2DE65B6FB14945CDF /* Pods-NotificationServiceExtension.releaseadhoc.xcconfig */; + baseConfigurationReference = AC9422F6C8A49AE701481721 /* Pods-NotificationServiceExtension.releaseadhoc.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; @@ -1442,7 +1408,7 @@ }; 7FD73CA92B23CE9500420AF3 /* ReleaseProduction */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 90E08F0C8C924EDA018C8866 /* Pods-NotificationServiceExtension.releaseproduction.xcconfig */; + baseConfigurationReference = 96ADA7C82BA6A08C4A56344A /* Pods-NotificationServiceExtension.releaseproduction.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; @@ -1737,7 +1703,7 @@ }; CF9AF93F29EE9276001FA527 /* DebugProduction */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C3788801E65E896FA7C77298 /* Pods-NewExpensify.debugproduction.xcconfig */; + baseConfigurationReference = 3328C9B8861CA667C42B47F3 /* Pods-NewExpensify.debugproduction.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; @@ -1775,7 +1741,7 @@ }; CF9AF94029EE9276001FA527 /* DebugProduction */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 52E63EFD054926BFEA3EC143 /* Pods-NewExpensify-NewExpensifyTests.debugproduction.xcconfig */; + baseConfigurationReference = 030F99CEB3AEF1F11B001798 /* Pods-NewExpensify-NewExpensifyTests.debugproduction.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; @@ -1879,7 +1845,7 @@ }; CF9AF94529EE927A001FA527 /* DebugAdHoc */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7B318CF669A0F7FE948D5CED /* Pods-NewExpensify.debugadhoc.xcconfig */; + baseConfigurationReference = 5A1F158A9A6CBE170EC19D9C /* Pods-NewExpensify.debugadhoc.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIconAdHoc; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; @@ -1917,7 +1883,7 @@ }; CF9AF94629EE927A001FA527 /* DebugAdHoc */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 4E9593A0EE1C84B8A8EC062F /* Pods-NewExpensify-NewExpensifyTests.debugadhoc.xcconfig */; + baseConfigurationReference = F98306ABF3F272DF04DF65CC /* Pods-NewExpensify-NewExpensifyTests.debugadhoc.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; @@ -2015,7 +1981,7 @@ }; CF9AF94829EE928E001FA527 /* ReleaseProduction */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 32181F72DC539FFD1D1F0CA4 /* Pods-NewExpensify.releaseproduction.xcconfig */; + baseConfigurationReference = 9EADA69D62F2E7B3D96E5B1C /* Pods-NewExpensify.releaseproduction.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; @@ -2051,7 +2017,7 @@ }; CF9AF94929EE928E001FA527 /* ReleaseProduction */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E2F78D2A9B3DB96F0524690B /* Pods-NewExpensify-NewExpensifyTests.releaseproduction.xcconfig */; + baseConfigurationReference = 466D03D63F4B48E009C04FA3 /* Pods-NewExpensify-NewExpensifyTests.releaseproduction.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; @@ -2147,7 +2113,7 @@ }; CF9AF94E29EE9293001FA527 /* ReleaseAdHoc */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 68F4F270A8D1414FC14F356F /* Pods-NewExpensify.releaseadhoc.xcconfig */; + baseConfigurationReference = 7312B334B72E8BE41A811FAB /* Pods-NewExpensify.releaseadhoc.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIconAdHoc; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; @@ -2183,7 +2149,7 @@ }; CF9AF94F29EE9293001FA527 /* ReleaseAdHoc */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FF0EADDA6099EF76253FA7AB /* Pods-NewExpensify-NewExpensifyTests.releaseadhoc.xcconfig */; + baseConfigurationReference = D15262BE5F713CDB4DA576AE /* Pods-NewExpensify-NewExpensifyTests.releaseadhoc.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index ffc0769a9571..79a54cfd702c 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.4.41 + 1.4.42 CFBundleSignature ???? CFBundleURLTypes @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.41.2 + 1.4.42.1 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 86825c7cc4a3..d3084c0e07eb 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.4.41 + 1.4.42 CFBundleSignature ???? CFBundleVersion - 1.4.41.2 + 1.4.42.1 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 06238fb247c9..93e1f6e20d17 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -11,9 +11,9 @@ CFBundleName $(PRODUCT_NAME) CFBundleShortVersionString - 1.4.41 + 1.4.42 CFBundleVersion - 1.4.41.2 + 1.4.42.1 NSExtension NSExtensionPointIdentifier diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 19a579b846e8..c45fa52877ec 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -32,6 +32,9 @@ PODS: - React-Core - CocoaAsyncSocket (7.6.5) - DoubleConversion (1.1.6) + - EXAV (13.10.4): + - ExpoModulesCore + - ReactCommon/turbomodule/core - Expo (50.0.4): - ExpoModulesCore - ExpoImage (1.10.1): @@ -1172,7 +1175,7 @@ PODS: - React-Core - react-native-image-manipulator (1.0.5): - React - - react-native-image-picker (5.1.0): + - react-native-image-picker (7.0.3): - React-Core - react-native-key-command (1.0.6): - React-Core @@ -1195,7 +1198,7 @@ PODS: - React-Core - react-native-render-html (6.3.1): - React-Core - - react-native-safe-area-context (4.7.4): + - react-native-safe-area-context (4.8.2): - React-Core - react-native-view-shot (3.8.0): - React-Core @@ -1427,7 +1430,7 @@ PODS: - Turf - RNPermissions (3.9.3): - React-Core - - RNReactNativeHapticFeedback (1.14.0): + - RNReactNativeHapticFeedback (2.2.0): - React-Core - RNReanimated (3.6.1): - glog @@ -1443,7 +1446,7 @@ PODS: - RNSound/Core (= 0.11.2) - RNSound/Core (0.11.2): - React-Core - - RNSVG (14.0.0): + - RNSVG (14.1.0): - React-Core - SDWebImage (5.17.0): - SDWebImage/Core (= 5.17.0) @@ -1458,7 +1461,7 @@ PODS: - SDWebImage/Core (~> 5.17) - SocketRocket (0.6.1) - Turf (2.7.0) - - VisionCamera (2.16.5): + - VisionCamera (2.16.8): - React - React-callinvoker - React-Core @@ -1469,6 +1472,7 @@ DEPENDENCIES: - 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`) + - EXAV (from `../node_modules/expo-av/ios`) - Expo (from `../node_modules/expo`) - ExpoImage (from `../node_modules/expo-image/ios`) - ExpoModulesCore (from `../node_modules/expo-modules-core`) @@ -1650,6 +1654,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-linear-gradient" DoubleConversion: :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" + EXAV: + :path: "../node_modules/expo-av/ios" Expo: :path: "../node_modules/expo" ExpoImage: @@ -1851,6 +1857,7 @@ SPEC CHECKSUMS: BVLinearGradient: 421743791a59d259aec53f4c58793aad031da2ca CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 DoubleConversion: fea03f2699887d960129cc54bba7e52542b6f953 + EXAV: 09a4d87fa6b113fbb0ada3aade6799f78271cb44 Expo: 1e3bcf9dd99de57a636127057f6b488f0609681a ExpoImage: 1cdaa65a6a70bb01067e21ad1347ff2d973885f5 ExpoModulesCore: 96d1751929ad10622773bb729ab28a8423f0dd0c @@ -1927,7 +1934,7 @@ SPEC CHECKSUMS: react-native-document-picker: 3599b238843369026201d2ef466df53f77ae0452 react-native-geolocation: 0f7fe8a4c2de477e278b0365cce27d089a8c5903 react-native-image-manipulator: c48f64221cfcd46e9eec53619c4c0374f3328a56 - react-native-image-picker: c33d4e79f0a14a2b66e5065e14946ae63749660b + react-native-image-picker: 2381c008bbb09e72395a2d043c147b11bd1523d9 react-native-key-command: 5af6ee30ff4932f78da6a2109017549042932aa5 react-native-launch-arguments: 5f41e0abf88a15e3c5309b8875d6fd5ac43df49d react-native-netinfo: 8a7fd3f7130ef4ad2fb4276d5c9f8d3f28d2df3d @@ -1937,7 +1944,7 @@ SPEC CHECKSUMS: react-native-plaid-link-sdk: df1618a85a615d62ff34e34b76abb7a56497fbc1 react-native-quick-sqlite: bcc7a7a250a40222f18913a97cd356bf82d0a6c4 react-native-render-html: 96c979fe7452a0a41559685d2f83b12b93edac8c - react-native-safe-area-context: 2cd91d532de12acdb0a9cbc8d43ac72a8e4c897c + react-native-safe-area-context: 0ee144a6170530ccc37a0fd9388e28d06f516a89 react-native-view-shot: 6b7ed61d77d88580fed10954d45fad0eb2d47688 react-native-webview: 88293a0f23eca8465c0433c023ec632930e644d0 React-nativeconfig: d753fbbc8cecc8ae413d615599ac378bbf6999bb @@ -1978,19 +1985,19 @@ SPEC CHECKSUMS: RNLocalize: d4b8af4e442d4bcca54e68fc687a2129b4d71a81 rnmapbox-maps: 6f638ec002aa6e906a6f766d69cd45f968d98e64 RNPermissions: 9b086c8f05b2e2faa587fdc31f4c5ab4509728aa - RNReactNativeHapticFeedback: 1e3efeca9628ff9876ee7cdd9edec1b336913f8c + RNReactNativeHapticFeedback: ec56a5f81c3941206fd85625fa669ffc7b4545f9 RNReanimated: 57f436e7aa3d277fbfed05e003230b43428157c0 RNScreens: b582cb834dc4133307562e930e8fa914b8c04ef2 RNSound: 6c156f925295bdc83e8e422e7d8b38d33bc71852 - RNSVG: 255767813dac22db1ec2062c8b7e7b856d4e5ae6 + RNSVG: ba3e7232f45e34b7b47e74472386cf4e1a676d0a SDWebImage: 750adf017a315a280c60fde706ab1e552a3ae4e9 SDWebImageAVIFCoder: 8348fef6d0ec69e129c66c9fe4d74fbfbf366112 SDWebImageSVGCoder: 15a300a97ec1c8ac958f009c02220ac0402e936c SDWebImageWebPCoder: af09429398d99d524cae2fe00f6f0f6e491ed102 SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 Turf: 13d1a92d969ca0311bbc26e8356cca178ce95da2 - VisionCamera: fda554d8751e395effcc87749f8b7c198c1031be - Yoga: 13c8ef87792450193e117976337b8527b49e8c03 + VisionCamera: 0a6794d1974aed5d653d0d0cb900493e2583e35a + Yoga: e64aa65de36c0832d04e8c7bd614396c77a80047 PODFILE CHECKSUM: 0ccbb4f2406893c6e9f266dc1e7470dcd72885d2 diff --git a/package-lock.json b/package-lock.json index 5db1d6cfe86d..8dbd94f470eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.41-2", + "version": "1.4.42-1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.41-2", + "version": "1.4.42-1", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -52,8 +52,9 @@ "date-fns-tz": "^2.0.0", "dom-serializer": "^0.2.2", "domhandler": "^4.3.0", - "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#7aa1d42600d2f59565c1ff962f691b494ccc2813", + "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#a8ed0f8e1be3a1e09016e07a74cfd13c85bbc167", "expo": "^50.0.3", + "expo-av": "~13.10.4", "expo-image": "1.10.1", "fbjs": "^3.0.2", "htmlparser2": "^7.2.0", @@ -76,7 +77,7 @@ "react-error-boundary": "^4.0.11", "react-map-gl": "^7.1.3", "react-native": "0.73.2", - "react-native-android-location-enabler": "^1.2.2", + "react-native-android-location-enabler": "^2.0.1", "react-native-blob-util": "0.19.4", "react-native-collapsible": "^1.6.1", "react-native-config": "^1.4.5", @@ -87,9 +88,9 @@ "react-native-fs": "^2.20.0", "react-native-gesture-handler": "2.14.1", "react-native-google-places-autocomplete": "2.5.6", - "react-native-haptic-feedback": "^1.13.0", + "react-native-haptic-feedback": "^2.2.0", "react-native-image-pan-zoom": "^2.1.12", - "react-native-image-picker": "^5.1.0", + "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#8393b7e58df6ff65fd41f60aee8ece8822c91e2b", "react-native-key-command": "^1.0.6", "react-native-launch-arguments": "^4.0.2", @@ -31236,8 +31237,8 @@ }, "node_modules/expensify-common": { "version": "1.0.0", - "resolved": "git+ssh://git@github.com/Expensify/expensify-common.git#7aa1d42600d2f59565c1ff962f691b494ccc2813", - "integrity": "sha512-f5zeUthFwmWFiGO1as+ARuYv7TbXVktnKAjoFKhaVVSTZvgbojSnzGClCCh4uwbdl9HDRv7aiXfRN1nzuIFOTg==", + "resolved": "git+ssh://git@github.com/Expensify/expensify-common.git#a8ed0f8e1be3a1e09016e07a74cfd13c85bbc167", + "integrity": "sha512-3d/JHWgeS+LFPRahCAXdLwnBYQk4XUYybtgCm7VsdmMDtCeGUTksLsEY7F1Zqm+ULqZjmCtYwAi8IPKy0fsSOw==", "license": "MIT", "dependencies": { "classnames": "2.4.0", @@ -31354,6 +31355,14 @@ "md5-file": "^3.2.3" } }, + "node_modules/expo-av": { + "version": "13.10.4", + "resolved": "https://registry.npmjs.org/expo-av/-/expo-av-13.10.4.tgz", + "integrity": "sha512-Qlj+ILJ8wVK7J8DT9e6B1FyIcI3kM0iPhgmt7vnSqAWAbexOJRK7IP3MlKqKvHAhwm2OxIU3qJkShF7hvk20xA==", + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-constants": { "version": "15.4.5", "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-15.4.5.tgz", @@ -44811,11 +44820,15 @@ } }, "node_modules/react-native-android-location-enabler": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/react-native-android-location-enabler/-/react-native-android-location-enabler-1.2.2.tgz", - "integrity": "sha512-CC5ghRoK3jkGNK8jdIiYIc3l0XZuQuMt2KEfldDpnMCkNz2aAfUWyLCoOniFLqtdD9poA3az+kCmUzTvLAyTiA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/react-native-android-location-enabler/-/react-native-android-location-enabler-2.0.1.tgz", + "integrity": "sha512-hDNfQL4gImrmc6K6J44kd2iKrpPHc23V4stujNIg3I1LvYLT+kwWmfTGapeY0hl6EXACfaFdm/wBb4ggtNcnPA==", + "engines": { + "node": ">= 16.0.0" + }, "peerDependencies": { - "react-native": ">= 0.60.0" + "react": ">= 18.2.0", + "react-native": ">= 0.71.0" } }, "node_modules/react-native-animatable": { @@ -45010,10 +45023,9 @@ } }, "node_modules/react-native-haptic-feedback": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/react-native-haptic-feedback/-/react-native-haptic-feedback-1.14.0.tgz", - "integrity": "sha512-dSXZ6gAzl+W/L7BPjOpnT0bx0cgQiSr0sB3DjyDJbGIdVr4ISaktZC6gC9xYFTv2kMq0+KtbKi+dpd0WtxYZMw==", - "license": "MIT", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/react-native-haptic-feedback/-/react-native-haptic-feedback-2.2.0.tgz", + "integrity": "sha512-3tqJOjCguWhIrX0nkURn4yw6kXdsSDjjrvZCRjKXYGlL28hdQmoW2okAHduDTD9FWj9lA+lHgwFWgGs4aFNN7A==", "peerDependencies": { "react-native": ">=0.60.0" } @@ -45029,8 +45041,9 @@ } }, "node_modules/react-native-image-picker": { - "version": "5.1.0", - "license": "MIT", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/react-native-image-picker/-/react-native-image-picker-7.0.3.tgz", + "integrity": "sha512-mFbu8AzZ8tysO7xHsQ+TmxYYJYaBCL66s4AujjgCBEAyTufm2nhjhVOw2uq1zvMDVtGoyekzCJfH3JqVmXK/3w==", "peerDependencies": { "react": "*", "react-native": "*" diff --git a/package.json b/package.json index be8e65d59b70..e0e0d030cb4a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.41-2", + "version": "1.4.42-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.", @@ -100,8 +100,9 @@ "date-fns-tz": "^2.0.0", "dom-serializer": "^0.2.2", "domhandler": "^4.3.0", - "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#7aa1d42600d2f59565c1ff962f691b494ccc2813", + "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#a8ed0f8e1be3a1e09016e07a74cfd13c85bbc167", "expo": "^50.0.3", + "expo-av": "~13.10.4", "expo-image": "1.10.1", "fbjs": "^3.0.2", "htmlparser2": "^7.2.0", @@ -124,7 +125,7 @@ "react-error-boundary": "^4.0.11", "react-map-gl": "^7.1.3", "react-native": "0.73.2", - "react-native-android-location-enabler": "^1.2.2", + "react-native-android-location-enabler": "^2.0.1", "react-native-blob-util": "0.19.4", "react-native-collapsible": "^1.6.1", "react-native-config": "^1.4.5", @@ -135,9 +136,9 @@ "react-native-fs": "^2.20.0", "react-native-gesture-handler": "2.14.1", "react-native-google-places-autocomplete": "2.5.6", - "react-native-haptic-feedback": "^1.13.0", + "react-native-haptic-feedback": "^2.2.0", "react-native-image-pan-zoom": "^2.1.12", - "react-native-image-picker": "^5.1.0", + "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#8393b7e58df6ff65fd41f60aee8ece8822c91e2b", "react-native-key-command": "^1.0.6", "react-native-launch-arguments": "^4.0.2", diff --git a/patches/@react-native+assets-registry+0.73.1.patch b/patches/@react-native+assets-registry+0.73.1.patch new file mode 100644 index 000000000000..fc931b4ab52d --- /dev/null +++ b/patches/@react-native+assets-registry+0.73.1.patch @@ -0,0 +1,36 @@ +diff --git a/node_modules/@react-native/assets-registry/registry.js b/node_modules/@react-native/assets-registry/registry.js +index 02470da..3851d92 100644 +--- a/node_modules/@react-native/assets-registry/registry.js ++++ b/node_modules/@react-native/assets-registry/registry.js +@@ -10,28 +10,15 @@ + + 'use strict'; + +-export type PackagerAsset = { +- +__packager_asset: boolean, +- +fileSystemLocation: string, +- +httpServerLocation: string, +- +width: ?number, +- +height: ?number, +- +scales: Array, +- +hash: string, +- +name: string, +- +type: string, +- ... +-}; ++const assets = []; + +-const assets: Array = []; +- +-function registerAsset(asset: PackagerAsset): number { ++function registerAsset(asset) { + // `push` returns new array length, so the first asset will + // get id 1 (not 0) to make the value truthy + return assets.push(asset); + } + +-function getAssetByID(assetId: number): PackagerAsset { ++function getAssetByID(assetId) { + return assets[assetId - 1]; + } + diff --git a/patches/react-native-config+1.4.6.patch b/patches/react-native-config+1.4.6.patch new file mode 100644 index 000000000000..880c723ddc37 --- /dev/null +++ b/patches/react-native-config+1.4.6.patch @@ -0,0 +1,44 @@ +diff --git a/node_modules/react-native-config/android/dotenv.gradle b/node_modules/react-native-config/android/dotenv.gradle +index 2225375..48f94ca 100644 +--- a/node_modules/react-native-config/android/dotenv.gradle ++++ b/node_modules/react-native-config/android/dotenv.gradle +@@ -41,7 +41,8 @@ def loadDotEnv(flavor = getCurrentFlavor()) { + def env = [:] + println("Reading env from: $envFile") + +- File f = new File("$project.rootDir/../$envFile"); ++ def reactNativeProjectRoot = project.hasProperty('reactNativeProject') ? project.reactNativeProject : ".." ++ File f = new File("$project.rootDir/$reactNativeProjectRoot/$envFile"); + if (!f.exists()) { + f = new File("$envFile"); + } +diff --git a/node_modules/react-native-config/react-native-config.podspec b/node_modules/react-native-config/react-native-config.podspec +index 54985dd..c394ec7 100644 +--- a/node_modules/react-native-config/react-native-config.podspec ++++ b/node_modules/react-native-config/react-native-config.podspec +@@ -3,6 +3,7 @@ + require 'json' + + package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) ++REACT_NATIVE_DIR = ENV["REACT_NATIVE_DIR"] || ".." + + Pod::Spec.new do |s| + s.name = 'react-native-config' +@@ -21,7 +22,7 @@ Pod::Spec.new do |s| + name: 'Config codegen', + script: %( + set -ex +-HOST_PATH="$SRCROOT/../.." ++HOST_PATH="$SRCROOT/../#{REACT_NATIVE_DIR}" + "${PODS_TARGET_SRCROOT}/ios/ReactNativeConfig/BuildDotenvConfig.rb" "$HOST_PATH" "${PODS_TARGET_SRCROOT}/ios/ReactNativeConfig" + ), + execution_position: :before_compile, +@@ -43,7 +44,7 @@ HOST_PATH="$SRCROOT/../.." + name: 'Config codegen', + script: %( + set -ex +- HOST_PATH="$SRCROOT/../.." ++ HOST_PATH="$SRCROOT/../#{REACT_NATIVE_DIR}" + "${PODS_TARGET_SRCROOT}/ios/ReactNativeConfig/BuildDotenvConfig.rb" "$HOST_PATH" "${PODS_TARGET_SRCROOT}/ios/ReactNativeConfig" + ), + execution_position: :before_compile, diff --git a/patches/react-native-image-picker+5.1.0.patch b/patches/react-native-image-picker+7.0.3+001+allowedMimeTypes.patch similarity index 100% rename from patches/react-native-image-picker+5.1.0.patch rename to patches/react-native-image-picker+7.0.3+001+allowedMimeTypes.patch diff --git a/src/App.tsx b/src/App.tsx index 52baedc79421..9562ea647e25 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -21,6 +21,9 @@ import SafeArea from './components/SafeArea'; import ThemeIllustrationsProvider from './components/ThemeIllustrationsProvider'; import ThemeProvider from './components/ThemeProvider'; import ThemeStylesProvider from './components/ThemeStylesProvider'; +import {PlaybackContextProvider} from './components/VideoPlayerContexts/PlaybackContext'; +import {VideoPopoverMenuContextProvider} from './components/VideoPlayerContexts/VideoPopoverMenuContext'; +import {VolumeContextProvider} from './components/VideoPlayerContexts/VolumeContext'; import {CurrentReportIDContextProvider} from './components/withCurrentReportID'; import {EnvironmentProvider} from './components/withEnvironment'; import {KeyboardStateProvider} from './components/withKeyboardState'; @@ -81,6 +84,9 @@ function App({url}: AppProps) { CustomStatusBarAndBackgroundContextProvider, ActiveElementRoleProvider, ActiveWorkspaceContextProvider, + PlaybackContextProvider, + VolumeContextProvider, + VideoPopoverMenuContextProvider, ]} > diff --git a/src/CONST.ts b/src/CONST.ts index a939f0942839..fae319325f13 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -103,7 +103,7 @@ const CONST = { }, REPORT_DESCRIPTION: { - MAX_LENGTH: 1024, + MAX_LENGTH: 500, }, PULL_REQUEST_NUMBER, @@ -195,45 +195,6 @@ const CONST = { DOMAIN: '@expensify.sms', }, BANK_ACCOUNT: { - BANK_INFO_STEP: { - INPUT_KEY: { - ROUTING_NUMBER: 'routingNumber', - ACCOUNT_NUMBER: 'accountNumber', - PLAID_MASK: 'plaidMask', - IS_SAVINGS: 'isSavings', - BANK_NAME: 'bankName', - PLAID_ACCOUNT_ID: 'plaidAccountID', - PLAID_ACCESS_TOKEN: 'plaidAccessToken', - }, - }, - PERSONAL_INFO_STEP: { - INPUT_KEY: { - FIRST_NAME: 'firstName', - LAST_NAME: 'lastName', - DOB: 'dob', - SSN_LAST_4: 'ssnLast4', - STREET: 'requestorAddressStreet', - CITY: 'requestorAddressCity', - STATE: 'requestorAddressState', - ZIP_CODE: 'requestorAddressZipCode', - }, - }, - BUSINESS_INFO_STEP: { - INPUT_KEY: { - COMPANY_NAME: 'companyName', - COMPANY_TAX_ID: 'companyTaxID', - COMPANY_WEBSITE: 'website', - COMPANY_PHONE: 'companyPhone', - STREET: 'addressStreet', - CITY: 'addressCity', - STATE: 'addressState', - ZIP_CODE: 'addressZipCode', - INCORPORATION_TYPE: 'incorporationType', - INCORPORATION_DATE: 'incorporationDate', - INCORPORATION_STATE: 'incorporationState', - HAS_NO_CONNECTION_TO_CANNABIS: 'hasNoConnectionToCannabis', - }, - }, BENEFICIAL_OWNER_INFO_STEP: { SUBSTEP: { IS_USER_UBO: 1, @@ -242,11 +203,6 @@ const CONST = { ARE_THERE_MORE_UBOS: 4, UBOS_LIST: 5, }, - INPUT_KEY: { - OWNS_MORE_THAN_25_PERCENT: 'ownsMoreThan25Percent', - HAS_OTHER_BENEFICIAL_OWNERS: 'hasOtherBeneficialOwners', - BENEFICIAL_OWNERS: 'beneficialOwners', - }, BENEFICIAL_OWNER_DATA: { BENEFICIAL_OWNER_KEYS: 'beneficialOwnerKeys', PREFIX: 'beneficialOwner', @@ -270,13 +226,6 @@ const CONST = { EXIT: 'EXIT', }, }, - COMPLETE_VERIFICATION: { - INPUT_KEY: { - IS_AUTHORIZED_TO_USE_BANK_ACCOUNT: 'isAuthorizedToUseBankAccount', - CERTIFY_TRUE_INFORMATION: 'certifyTrueInformation', - ACCEPT_TERMS_AND_CONDITIONS: 'acceptTermsAndConditions', - }, - }, ERROR: { MISSING_ROUTING_NUMBER: '402 Missing routingNumber', MAX_ROUTING_NUMBER: '402 Maximum Size Exceeded routingNumber', @@ -682,6 +631,7 @@ const CONST = { UPDATE_MAX_EXPENSE_AMOUNT: 'POLICYCHANGELOG_UPDATE_MAX_EXPENSE_AMOUNT', UPDATE_MAX_EXPENSE_AMOUNT_NO_RECEIPT: 'POLICYCHANGELOG_UPDATE_MAX_EXPENSE_AMOUNT_NO_RECEIPT', UPDATE_NAME: 'POLICYCHANGELOG_UPDATE_NAME', + UPDATE_DESCRIPTION: 'POLICYCHANGELOG_UPDATE_DESCRIPTION', UPDATE_OWNERSHIP: 'POLICYCHANGELOG_UPDATE_OWNERSHIP', UPDATE_REIMBURSEMENT_CHOICE: 'POLICYCHANGELOG_UPDATE_REIMBURSEMENT_CHOICE', UPDATE_REPORT_FIELD: 'POLICYCHANGELOG_UPDATE_REPORT_FIELD', @@ -701,6 +651,9 @@ const CONST = { }, THREAD_DISABLED: ['CREATED'], }, + CANCEL_PAYMENT_REASONS: { + ADMIN: 'CANCEL_REASON_ADMIN', + }, ACTIONABLE_MENTION_WHISPER_RESOLUTION: { INVITE: 'invited', NOTHING: 'nothing', @@ -1036,6 +989,10 @@ const CONST = { ATTACHMENT_PREVIEW_ATTRIBUTE: 'src', ATTACHMENT_ORIGINAL_FILENAME_ATTRIBUTE: 'data-name', ATTACHMENT_LOCAL_URL_PREFIX: ['blob:', 'file:'], + ATTACHMENT_THUMBNAIL_URL_ATTRIBUTE: 'data-expensify-thumbnail-url', + ATTACHMENT_THUMBNAIL_WIDTH_ATTRIBUTE: 'data-expensify-width', + ATTACHMENT_THUMBNAIL_HEIGHT_ATTRIBUTE: 'data-expensify-height', + ATTACHMENT_DURATION_ATTRIBUTE: 'data-expensify-duration', ATTACHMENT_PICKER_TYPE: { FILE: 'file', @@ -1634,6 +1591,15 @@ const CONST = { ]; }, + // Emails that profile view is prohibited + get RESTRICTED_EMAILS(): readonly string[] { + return [this.EMAIL.NOTIFICATIONS]; + }, + // Account IDs that profile view is prohibited + get RESTRICTED_ACCOUNT_IDS() { + return [this.ACCOUNT_ID.NOTIFICATIONS]; + }, + // Auth limit is 60k for the column but we store edits and other metadata along the html so let's use a lower limit to accommodate for it. MAX_COMMENT_LENGTH: 10000, @@ -3248,6 +3214,29 @@ const CONST = { REPORT: 'REPORT', }, + THUMBNAIL_IMAGE: { + SMALL_SCREEN: { + SIZE: 250, + }, + WIDE_SCREEN: { + SIZE: 350, + }, + NAN_ASPECT_RATIO: 1.5, + }, + + VIDEO_PLAYER: { + POPOVER_Y_OFFSET: -30, + PLAYBACK_SPEEDS: [0.25, 0.5, 1, 1.5, 2], + HIDE_TIME_TEXT_WIDTH: 250, + MIN_WIDTH: 170, + MIN_HEIGHT: 120, + CONTROLS_POSITION: { + NATIVE: 32, + NORMAL: 8, + }, + DEFAULT_VIDEO_DIMENSIONS: {width: 1900, height: 1400}, + }, + INTRO_CHOICES: { TRACK: 'newDotTrack', SUBMIT: 'newDotSubmit', @@ -3273,6 +3262,14 @@ const CONST = { REPORT_FIELD_TITLE_FIELD_ID: 'text_title', + DEBUG_CONSOLE: { + LEVELS: { + INFO: 'INFO', + ERROR: 'ERROR', + RESULT: 'RESULT', + DEBUG: 'DEBUG', + }, + }, REIMBURSEMENT_ACCOUNT_SUBSTEP_INDEX: { BANK_ACCOUNT: { ACCOUNT_NUMBERS: 0, diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 751ee105ceb7..0208e7bcd3e5 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -1,7 +1,9 @@ import type {OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import type CONST from './CONST'; +import type * as FormTypes from './types/form'; import type * as OnyxTypes from './types/onyx'; +import type AssertTypesEqual from './types/utils/AssertTypesEqual'; import type DeepValueOf from './types/utils/DeepValueOf'; /** @@ -237,9 +239,6 @@ const ONYXKEYS = { // The last update ID that was applied to the client ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT: 'OnyxUpdatesLastUpdateIDAppliedToClient', - // Receipt upload modal - RECEIPT_MODAL: 'receiptModal', - // The access token to be used with the Mapbox library MAPBOX_ACCESS_TOKEN: 'mapboxAccessToken', @@ -261,6 +260,12 @@ const ONYXKEYS = { /** Indicates whether an forced upgrade is required */ UPDATE_REQUIRED: 'updateRequired', + /** Stores the logs of the app for debugging purposes */ + LOGS: 'logs', + + /** Indicates whether we should store logs or not */ + SHOULD_STORE_LOGS: 'shouldStoreLogs', + /** Collection Keys */ COLLECTION: { DOWNLOAD: 'download_', @@ -312,6 +317,8 @@ const ONYXKEYS = { ADD_DEBIT_CARD_FORM_DRAFT: 'addDebitCardFormDraft', WORKSPACE_SETTINGS_FORM: 'workspaceSettingsForm', WORKSPACE_SETTINGS_FORM_DRAFT: 'workspaceSettingsFormDraft', + WORKSPACE_DESCRIPTION_FORM: 'workspaceDescriptionForm', + WORKSPACE_DESCRIPTION_FORM_DRAFT: 'workspaceDescriptionFormDraft', WORKSPACE_RATE_AND_UNIT_FORM: 'workspaceRateAndUnitForm', WORKSPACE_RATE_AND_UNIT_FORM_DRAFT: 'workspaceRateAndUnitFormDraft', CLOSE_ACCOUNT_FORM: 'closeAccount', @@ -379,12 +386,87 @@ const ONYXKEYS = { }, } as const; -type OnyxKeysMap = typeof ONYXKEYS; -type OnyxCollectionKey = ValueOf; -type OnyxKey = DeepValueOf>; -type OnyxFormKey = ValueOf; +type AllOnyxKeys = DeepValueOf; + +type OnyxFormValuesMapping = { + [ONYXKEYS.FORMS.ADD_DEBIT_CARD_FORM]: FormTypes.AddDebitCardForm; + [ONYXKEYS.FORMS.WORKSPACE_SETTINGS_FORM]: FormTypes.WorkspaceSettingsForm; + [ONYXKEYS.FORMS.WORKSPACE_DESCRIPTION_FORM]: FormTypes.WorkspaceProfileDescriptionForm; + [ONYXKEYS.FORMS.WORKSPACE_RATE_AND_UNIT_FORM]: FormTypes.WorkspaceRateAndUnitForm; + [ONYXKEYS.FORMS.CLOSE_ACCOUNT_FORM]: FormTypes.CloseAccountForm; + [ONYXKEYS.FORMS.PROFILE_SETTINGS_FORM]: FormTypes.Form; + [ONYXKEYS.FORMS.DISPLAY_NAME_FORM]: FormTypes.DisplayNameForm; + [ONYXKEYS.FORMS.ROOM_NAME_FORM]: FormTypes.RoomNameForm; + [ONYXKEYS.FORMS.REPORT_DESCRIPTION_FORM]: FormTypes.Form; + [ONYXKEYS.FORMS.LEGAL_NAME_FORM]: FormTypes.Form; + [ONYXKEYS.FORMS.WORKSPACE_INVITE_MESSAGE_FORM]: FormTypes.Form; + [ONYXKEYS.FORMS.DATE_OF_BIRTH_FORM]: FormTypes.DateOfBirthForm; + [ONYXKEYS.FORMS.HOME_ADDRESS_FORM]: FormTypes.Form; + [ONYXKEYS.FORMS.NEW_ROOM_FORM]: FormTypes.NewRoomForm; + [ONYXKEYS.FORMS.ROOM_SETTINGS_FORM]: FormTypes.Form; + [ONYXKEYS.FORMS.NEW_TASK_FORM]: FormTypes.Form; + [ONYXKEYS.FORMS.EDIT_TASK_FORM]: FormTypes.Form; + [ONYXKEYS.FORMS.MONEY_REQUEST_DESCRIPTION_FORM]: FormTypes.Form; + [ONYXKEYS.FORMS.MONEY_REQUEST_MERCHANT_FORM]: FormTypes.Form; + [ONYXKEYS.FORMS.MONEY_REQUEST_AMOUNT_FORM]: FormTypes.Form; + [ONYXKEYS.FORMS.MONEY_REQUEST_DATE_FORM]: FormTypes.Form; + [ONYXKEYS.FORMS.NEW_CONTACT_METHOD_FORM]: FormTypes.Form; + [ONYXKEYS.FORMS.WAYPOINT_FORM]: FormTypes.Form; + [ONYXKEYS.FORMS.SETTINGS_STATUS_SET_FORM]: FormTypes.Form; + [ONYXKEYS.FORMS.SETTINGS_STATUS_CLEAR_DATE_FORM]: FormTypes.Form; + [ONYXKEYS.FORMS.SETTINGS_STATUS_SET_CLEAR_AFTER_FORM]: FormTypes.Form; + [ONYXKEYS.FORMS.PRIVATE_NOTES_FORM]: FormTypes.PrivateNotesForm; + [ONYXKEYS.FORMS.I_KNOW_A_TEACHER_FORM]: FormTypes.IKnowTeacherForm; + [ONYXKEYS.FORMS.INTRO_SCHOOL_PRINCIPAL_FORM]: FormTypes.IntroSchoolPrincipalForm; + [ONYXKEYS.FORMS.REPORT_VIRTUAL_CARD_FRAUD]: FormTypes.Form; + [ONYXKEYS.FORMS.REPORT_PHYSICAL_CARD_FORM]: FormTypes.Form; + [ONYXKEYS.FORMS.GET_PHYSICAL_CARD_FORM]: FormTypes.GetPhysicalCardForm; + [ONYXKEYS.FORMS.REPORT_FIELD_EDIT_FORM]: FormTypes.ReportFieldEditForm; + [ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM]: FormTypes.ReimbursementAccountForm; + [ONYXKEYS.FORMS.PERSONAL_BANK_ACCOUNT]: FormTypes.PersonalBankAccountForm; + [ONYXKEYS.FORMS.WORKSPACE_DESCRIPTION_FORM]: FormTypes.WorkspaceDescriptionForm; +}; -type OnyxValues = { +type OnyxFormDraftValuesMapping = { + [K in keyof OnyxFormValuesMapping as `${K}Draft`]: OnyxFormValuesMapping[K]; +}; + +type OnyxCollectionValuesMapping = { + [ONYXKEYS.COLLECTION.DOWNLOAD]: OnyxTypes.Download; + [ONYXKEYS.COLLECTION.POLICY]: OnyxTypes.Policy; + [ONYXKEYS.COLLECTION.POLICY_DRAFTS]: OnyxTypes.Policy; + [ONYXKEYS.COLLECTION.POLICY_CATEGORIES]: OnyxTypes.PolicyCategories; + [ONYXKEYS.COLLECTION.POLICY_TAGS]: OnyxTypes.PolicyTagList; + [ONYXKEYS.COLLECTION.POLICY_MEMBERS]: OnyxTypes.PolicyMembers; + [ONYXKEYS.COLLECTION.POLICY_MEMBERS_DRAFTS]: OnyxTypes.PolicyMember; + [ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES]: OnyxTypes.RecentlyUsedCategories; + [ONYXKEYS.COLLECTION.POLICY_REPORT_FIELDS]: OnyxTypes.PolicyReportFields; + [ONYXKEYS.COLLECTION.DEPRECATED_POLICY_MEMBER_LIST]: OnyxTypes.PolicyMembers; + [ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT]: OnyxTypes.InvitedEmailsToAccountIDs; + [ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MESSAGE_DRAFT]: string; + [ONYXKEYS.COLLECTION.REPORT]: OnyxTypes.Report; + [ONYXKEYS.COLLECTION.REPORT_METADATA]: OnyxTypes.ReportMetadata; + [ONYXKEYS.COLLECTION.REPORT_ACTIONS]: OnyxTypes.ReportActions; + [ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS]: OnyxTypes.ReportActionsDrafts; + [ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS]: OnyxTypes.ReportActionReactions; + [ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT]: string; + [ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT_NUMBER_OF_LINES]: number; + [ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE]: boolean; + [ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING]: OnyxTypes.ReportUserIsTyping; + [ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM]: boolean; + [ONYXKEYS.COLLECTION.SECURITY_GROUP]: OnyxTypes.SecurityGroup; + [ONYXKEYS.COLLECTION.TRANSACTION]: OnyxTypes.Transaction; + [ONYXKEYS.COLLECTION.TRANSACTION_DRAFT]: OnyxTypes.Transaction; + [ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS]: OnyxTypes.TransactionViolations; + [ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT]: OnyxTypes.Transaction; + [ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS]: OnyxTypes.RecentlyUsedTags; + [ONYXKEYS.COLLECTION.SELECTED_TAB]: string; + [ONYXKEYS.COLLECTION.PRIVATE_NOTES_DRAFT]: string; + [ONYXKEYS.COLLECTION.NEXT_STEP]: OnyxTypes.ReportNextStep; + [ONYXKEYS.COLLECTION.POLICY_TAX_RATE]: string[]; +}; + +type OnyxValuesMapping = { [ONYXKEYS.ACCOUNT]: OnyxTypes.Account; [ONYXKEYS.ACCOUNT_MANAGER_REPORT_ID]: string; [ONYXKEYS.NVP_IS_FIRST_TIME_NEW_EXPENSIFY_USER]: boolean; @@ -465,113 +547,25 @@ type OnyxValues = { [ONYXKEYS.LAST_VISITED_PATH]: string | undefined; [ONYXKEYS.RECENTLY_USED_REPORT_FIELDS]: OnyxTypes.RecentlyUsedReportFields; [ONYXKEYS.UPDATE_REQUIRED]: boolean; + [ONYXKEYS.PLAID_CURRENT_EVENT]: string; + [ONYXKEYS.LOGS]: Record; + [ONYXKEYS.SHOULD_STORE_LOGS]: boolean; +}; - // Collections - [ONYXKEYS.COLLECTION.DOWNLOAD]: OnyxTypes.Download; - [ONYXKEYS.COLLECTION.POLICY]: OnyxTypes.Policy; - [ONYXKEYS.COLLECTION.POLICY_DRAFTS]: OnyxTypes.Policy; - [ONYXKEYS.COLLECTION.POLICY_CATEGORIES]: OnyxTypes.PolicyCategories; - [ONYXKEYS.COLLECTION.POLICY_TAGS]: OnyxTypes.PolicyTags; - [ONYXKEYS.COLLECTION.POLICY_MEMBERS]: OnyxTypes.PolicyMembers; - [ONYXKEYS.COLLECTION.POLICY_MEMBERS_DRAFTS]: OnyxTypes.PolicyMember; - [ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES]: OnyxTypes.RecentlyUsedCategories; - [ONYXKEYS.COLLECTION.POLICY_REPORT_FIELDS]: OnyxTypes.PolicyReportFields; - [ONYXKEYS.COLLECTION.DEPRECATED_POLICY_MEMBER_LIST]: OnyxTypes.PolicyMembers; - [ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT]: OnyxTypes.InvitedEmailsToAccountIDs | undefined; - [ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MESSAGE_DRAFT]: string | undefined; - [ONYXKEYS.COLLECTION.REPORT]: OnyxTypes.Report; - [ONYXKEYS.COLLECTION.REPORT_METADATA]: OnyxTypes.ReportMetadata; - [ONYXKEYS.COLLECTION.REPORT_ACTIONS]: OnyxTypes.ReportActions; - [ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS]: OnyxTypes.ReportActionsDrafts; - [ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS]: OnyxTypes.ReportActionReactions; - [ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT]: string; - [ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT_NUMBER_OF_LINES]: number; - [ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE]: boolean; - [ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING]: OnyxTypes.ReportUserIsTyping; - [ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM]: boolean; - [ONYXKEYS.COLLECTION.SECURITY_GROUP]: OnyxTypes.SecurityGroup; - [ONYXKEYS.COLLECTION.TRANSACTION]: OnyxTypes.Transaction; - [ONYXKEYS.COLLECTION.TRANSACTION_DRAFT]: OnyxTypes.Transaction; - [ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS]: OnyxTypes.TransactionViolations; - [ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT]: OnyxTypes.Transaction; - [ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS]: OnyxTypes.RecentlyUsedTags; - [ONYXKEYS.COLLECTION.SELECTED_TAB]: string; - [ONYXKEYS.COLLECTION.PRIVATE_NOTES_DRAFT]: string; - [ONYXKEYS.COLLECTION.NEXT_STEP]: OnyxTypes.ReportNextStep; +type OnyxValues = OnyxValuesMapping & OnyxCollectionValuesMapping & OnyxFormValuesMapping & OnyxFormDraftValuesMapping; - // Forms - [ONYXKEYS.FORMS.ADD_DEBIT_CARD_FORM]: OnyxTypes.AddDebitCardForm; - [ONYXKEYS.FORMS.ADD_DEBIT_CARD_FORM_DRAFT]: OnyxTypes.AddDebitCardForm; - [ONYXKEYS.FORMS.WORKSPACE_SETTINGS_FORM]: OnyxTypes.WorkspaceSettingsForm; - [ONYXKEYS.FORMS.WORKSPACE_SETTINGS_FORM_DRAFT]: OnyxTypes.WorkspaceSettingsForm; - [ONYXKEYS.FORMS.WORKSPACE_RATE_AND_UNIT_FORM]: OnyxTypes.Form; - [ONYXKEYS.FORMS.WORKSPACE_RATE_AND_UNIT_FORM_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.CLOSE_ACCOUNT_FORM]: OnyxTypes.CloseAccountForm; - [ONYXKEYS.FORMS.CLOSE_ACCOUNT_FORM_DRAFT]: OnyxTypes.CloseAccountForm; - [ONYXKEYS.FORMS.PROFILE_SETTINGS_FORM]: OnyxTypes.Form; - [ONYXKEYS.FORMS.PROFILE_SETTINGS_FORM_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.DISPLAY_NAME_FORM]: OnyxTypes.DisplayNameForm; - [ONYXKEYS.FORMS.DISPLAY_NAME_FORM_DRAFT]: OnyxTypes.DisplayNameForm; - [ONYXKEYS.FORMS.ROOM_NAME_FORM]: OnyxTypes.RoomNameForm; - [ONYXKEYS.FORMS.ROOM_NAME_FORM_DRAFT]: OnyxTypes.RoomNameForm; - [ONYXKEYS.FORMS.REPORT_DESCRIPTION_FORM]: OnyxTypes.Form; - [ONYXKEYS.FORMS.REPORT_DESCRIPTION_FORM_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.LEGAL_NAME_FORM]: OnyxTypes.Form; - [ONYXKEYS.FORMS.LEGAL_NAME_FORM_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.WORKSPACE_INVITE_MESSAGE_FORM]: OnyxTypes.Form; - [ONYXKEYS.FORMS.WORKSPACE_INVITE_MESSAGE_FORM_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.DATE_OF_BIRTH_FORM]: OnyxTypes.DateOfBirthForm; - [ONYXKEYS.FORMS.DATE_OF_BIRTH_FORM_DRAFT]: OnyxTypes.DateOfBirthForm; - [ONYXKEYS.FORMS.HOME_ADDRESS_FORM]: OnyxTypes.Form; - [ONYXKEYS.FORMS.HOME_ADDRESS_FORM_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.NEW_ROOM_FORM]: OnyxTypes.NewRoomForm; - [ONYXKEYS.FORMS.NEW_ROOM_FORM_DRAFT]: OnyxTypes.NewRoomForm; - [ONYXKEYS.FORMS.ROOM_SETTINGS_FORM]: OnyxTypes.Form; - [ONYXKEYS.FORMS.ROOM_SETTINGS_FORM_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.NEW_TASK_FORM]: OnyxTypes.Form; - [ONYXKEYS.FORMS.NEW_TASK_FORM_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.EDIT_TASK_FORM]: OnyxTypes.Form; - [ONYXKEYS.FORMS.EDIT_TASK_FORM_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.MONEY_REQUEST_DESCRIPTION_FORM]: OnyxTypes.Form; - [ONYXKEYS.FORMS.MONEY_REQUEST_DESCRIPTION_FORM_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.MONEY_REQUEST_MERCHANT_FORM]: OnyxTypes.Form; - [ONYXKEYS.FORMS.MONEY_REQUEST_MERCHANT_FORM_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.MONEY_REQUEST_AMOUNT_FORM]: OnyxTypes.Form; - [ONYXKEYS.FORMS.MONEY_REQUEST_AMOUNT_FORM_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.MONEY_REQUEST_DATE_FORM]: OnyxTypes.Form; - [ONYXKEYS.FORMS.MONEY_REQUEST_DATE_FORM_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.NEW_CONTACT_METHOD_FORM]: OnyxTypes.Form; - [ONYXKEYS.FORMS.NEW_CONTACT_METHOD_FORM_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.WAYPOINT_FORM]: OnyxTypes.Form; - [ONYXKEYS.FORMS.WAYPOINT_FORM_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.SETTINGS_STATUS_SET_FORM]: OnyxTypes.Form; - [ONYXKEYS.FORMS.SETTINGS_STATUS_SET_FORM_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.SETTINGS_STATUS_CLEAR_DATE_FORM]: OnyxTypes.Form; - [ONYXKEYS.FORMS.SETTINGS_STATUS_CLEAR_DATE_FORM_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.SETTINGS_STATUS_SET_CLEAR_AFTER_FORM]: OnyxTypes.Form; - [ONYXKEYS.FORMS.SETTINGS_STATUS_SET_CLEAR_AFTER_FORM_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.PRIVATE_NOTES_FORM]: OnyxTypes.PrivateNotesForm; - [ONYXKEYS.FORMS.PRIVATE_NOTES_FORM_DRAFT]: OnyxTypes.PrivateNotesForm; - [ONYXKEYS.FORMS.I_KNOW_A_TEACHER_FORM]: OnyxTypes.IKnowATeacherForm; - [ONYXKEYS.FORMS.I_KNOW_A_TEACHER_FORM_DRAFT]: OnyxTypes.IKnowATeacherForm; - [ONYXKEYS.FORMS.INTRO_SCHOOL_PRINCIPAL_FORM]: OnyxTypes.IntroSchoolPrincipalForm; - [ONYXKEYS.FORMS.INTRO_SCHOOL_PRINCIPAL_FORM_DRAFT]: OnyxTypes.IntroSchoolPrincipalForm; - [ONYXKEYS.FORMS.REPORT_VIRTUAL_CARD_FRAUD]: OnyxTypes.Form; - [ONYXKEYS.FORMS.REPORT_VIRTUAL_CARD_FRAUD_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.REPORT_PHYSICAL_CARD_FORM]: OnyxTypes.Form; - [ONYXKEYS.FORMS.REPORT_PHYSICAL_CARD_FORM_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.GET_PHYSICAL_CARD_FORM]: OnyxTypes.GetPhysicalCardForm; - [ONYXKEYS.FORMS.GET_PHYSICAL_CARD_FORM_DRAFT]: OnyxTypes.GetPhysicalCardForm; - [ONYXKEYS.FORMS.REPORT_FIELD_EDIT_FORM]: OnyxTypes.ReportFieldEditForm; - [ONYXKEYS.FORMS.REPORT_FIELD_EDIT_FORM_DRAFT]: OnyxTypes.Form; - // @ts-expect-error Different values are defined under the same key: ReimbursementAccount and ReimbursementAccountForm - [ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM]: OnyxTypes.ReimbursementAccountForm; - [ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM_DRAFT]: OnyxTypes.ReimbursementAccountForm; - [ONYXKEYS.FORMS.PERSONAL_BANK_ACCOUNT]: OnyxTypes.PersonalBankAccount; - [ONYXKEYS.FORMS.PERSONAL_BANK_ACCOUNT_DRAFT]: OnyxTypes.PersonalBankAccount; -}; +type OnyxCollectionKey = keyof OnyxCollectionValuesMapping; +type OnyxFormKey = keyof OnyxFormValuesMapping; +type OnyxFormDraftKey = keyof OnyxFormDraftValuesMapping; +type OnyxValueKey = keyof OnyxValuesMapping; + +type OnyxKey = OnyxValueKey | OnyxCollectionKey | OnyxFormKey | OnyxFormDraftKey; +type OnyxValue = OnyxEntry; -type OnyxKeyValue = OnyxEntry; +type MissingOnyxKeysError = `Error: Types don't match, OnyxKey type is missing: ${Exclude}`; +/** If this type errors, it means that the `OnyxKey` type is missing some keys. */ +// eslint-disable-next-line @typescript-eslint/no-unused-vars +type AssertOnyxKeys = AssertTypesEqual; export default ONYXKEYS; -export type {OnyxKey, OnyxCollectionKey, OnyxValues, OnyxKeyValue, OnyxFormKey, OnyxKeysMap}; +export type {OnyxValues, OnyxKey, OnyxCollectionKey, OnyxValue, OnyxValueKey, OnyxFormKey, OnyxFormValuesMapping, OnyxFormDraftKey}; diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 082cea49d771..03682a19bff4 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -153,6 +153,12 @@ const ROUTES = { SETTINGS_STATUS_CLEAR_AFTER: 'settings/profile/status/clear-after', SETTINGS_STATUS_CLEAR_AFTER_DATE: 'settings/profile/status/clear-after/date', SETTINGS_STATUS_CLEAR_AFTER_TIME: 'settings/profile/status/clear-after/time', + SETTINGS_TROUBLESHOOT: 'settings/troubleshoot', + SETTINGS_CONSOLE: 'settings/troubleshoot/console', + SETTINGS_SHARE_LOG: { + route: 'settings/troubleshoot/console/share-log', + getRoute: (source: string) => `settings/troubleshoot/console/share-log?source=${encodeURI(source)}` as const, + }, KEYBOARD_SHORTCUTS: 'keyboard-shortcuts', @@ -170,8 +176,9 @@ const ROUTES = { getRoute: (reportID: string) => `r/${reportID}/avatar` as const, }, EDIT_REQUEST: { - route: 'r/:threadReportID/edit/:field', - getRoute: (threadReportID: string, field: ValueOf) => `r/${threadReportID}/edit/${field}` as const, + route: 'r/:threadReportID/edit/:field/:tagIndex?', + getRoute: (threadReportID: string, field: ValueOf, tagIndex?: number) => + `r/${threadReportID}/edit/${field}${typeof tagIndex === 'number' ? `/${tagIndex}` : ''}` as const, }, EDIT_CURRENCY_REQUEST: { route: 'r/:threadReportID/edit/currency', @@ -195,7 +202,7 @@ const ROUTES = { }, REPORT_WITH_ID_DETAILS: { route: 'r/:reportID/details', - getRoute: (reportID: string) => `r/${reportID}/details` as const, + getRoute: (reportID: string, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/details`, backTo), }, REPORT_SETTINGS: { route: 'r/:reportID/settings', @@ -218,8 +225,9 @@ const ROUTES = { getRoute: (reportID: string, reportActionID: string) => `r/${reportID}/split/${reportActionID}` as const, }, EDIT_SPLIT_BILL: { - route: `r/:reportID/split/:reportActionID/edit/:field`, - getRoute: (reportID: string, reportActionID: string, field: ValueOf) => `r/${reportID}/split/${reportActionID}/edit/${field}` as const, + route: `r/:reportID/split/:reportActionID/edit/:field/:tagIndex?`, + getRoute: (reportID: string, reportActionID: string, field: ValueOf, tagIndex?: number) => + `r/${reportID}/split/${reportActionID}/edit/${field}${typeof tagIndex === 'number' ? `/${tagIndex}` : ''}` as const, }, EDIT_SPLIT_BILL_CURRENCY: { route: 'r/:reportID/split/:reportActionID/edit/currency', @@ -348,9 +356,9 @@ const ROUTES = { getUrlWithBackToParam(`create/${iouType}/distance/${transactionID}/${reportID}`, backTo), }, MONEY_REQUEST_STEP_MERCHANT: { - route: 'create/:iouType/merchant/:transactionID/:reportID', - getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`create/${iouType}/merchant/${transactionID}/${reportID}`, backTo), + route: ':action/:iouType/merchant/:transactionID/:reportID', + getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => + getUrlWithBackToParam(`${action}/${iouType}/merchant/${transactionID}/${reportID}`, backTo), }, MONEY_REQUEST_STEP_PARTICIPANTS: { route: 'create/:iouType/participants/:transactionID/:reportID', @@ -363,9 +371,9 @@ const ROUTES = { getUrlWithBackToParam(`${action}/${iouType}/scan/${transactionID}/${reportID}`, backTo), }, MONEY_REQUEST_STEP_TAG: { - route: ':action/:iouType/tag/:transactionID/:reportID', - getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`${action}/${iouType}/tag/${transactionID}/${reportID}`, backTo), + route: ':action/:iouType/tag/:tagIndex/:transactionID/:reportID', + getRoute: (action: ValueOf, iouType: ValueOf, tagIndex: number, transactionID: string, reportID: string, backTo = '') => + getUrlWithBackToParam(`${action}/${iouType}/tag/${tagIndex}/${transactionID}/${reportID}`, backTo), }, MONEY_REQUEST_STEP_WAYPOINT: { route: ':action/:iouType/waypoint/:transactionID/:reportID/:pageIndex', @@ -444,6 +452,10 @@ const ROUTES = { route: 'workspace/:policyID/profile/name', getRoute: (policyID: string) => `workspace/${policyID}/profile/name` as const, }, + WORKSPACE_PROFILE_DESCRIPTION: { + route: 'workspace/:policyID/profile/description', + getRoute: (policyID: string) => `workspace/${policyID}/profile/description` as const, + }, WORKSPACE_AVATAR: { route: 'workspace/:policyID/avatar', getRoute: (policyID: string) => `workspace/${policyID}/avatar` as const, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 1626fdbd1898..949adcba4d44 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -35,6 +35,9 @@ const SCREENS = { CLOSE: 'Settings_Close', TWO_FACTOR_AUTH: 'Settings_TwoFactorAuth', REPORT_CARD_LOST_OR_DAMAGED: 'Settings_ReportCardLostOrDamaged', + TROUBLESHOOT: 'Settings_Troubleshoot', + CONSOLE: 'Settings_Console', + SHARE_LOG: 'Share_Log', PROFILE: { ROOT: 'Settings_Profile', @@ -147,7 +150,6 @@ const SCREENS = { CONFIRMATION: 'Money_Request_Confirmation', CURRENCY: 'Money_Request_Currency', CATEGORY: 'Money_Request_Category', - MERCHANT: 'Money_Request_Merchant', WAYPOINT: 'Money_Request_Waypoint', EDIT_WAYPOINT: 'Money_Request_Edit_Waypoint', DISTANCE: 'Money_Request_Distance', @@ -206,6 +208,7 @@ const SCREENS = { INVITE: 'Workspace_Invite', INVITE_MESSAGE: 'Workspace_Invite_Message', CURRENCY: 'Workspace_Profile_Currency', + DESCRIPTION: 'Workspace_Profile_Description', NAME: 'Workspace_Profile_Name', }, diff --git a/src/components/AddressForm.js b/src/components/AddressForm.js index aee1b652b22c..1bd55004074a 100644 --- a/src/components/AddressForm.js +++ b/src/components/AddressForm.js @@ -9,6 +9,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import * as ValidationUtils from '@libs/ValidationUtils'; import CONST from '@src/CONST'; +import INPUT_IDS from '@src/types/form/HomeAddressForm'; import AddressSearch from './AddressSearch'; import CountrySelector from './CountrySelector'; import FormProvider from './Form/FormProvider'; @@ -127,7 +128,7 @@ function AddressForm({city, country, formID, onAddressChanged, onSubmit, shouldS { onAddressChanged(data, key); @@ -136,12 +137,12 @@ function AddressForm({city, country, formID, onAddressChanged, onSubmit, shouldS }} defaultValue={street1 || ''} renamedInputKeys={{ - street: 'addressLine1', - street2: 'addressLine2', - city: 'city', - state: 'state', - zipCode: 'zipPostCode', - country: 'country', + street: INPUT_IDS.ADDRESS_LINE_1, + street2: INPUT_IDS.ADDRESS_LINE_2, + city: INPUT_IDS.CITY, + state: INPUT_IDS.STATE, + zipCode: INPUT_IDS.ZIP_POST_CODE, + country: INPUT_IDS.COUNTRY, }} maxInputLength={CONST.FORM_CHARACTER_LIMIT} shouldSaveDraft={shouldSaveDraft} @@ -150,7 +151,7 @@ function AddressForm({city, country, formID, onAddressChanged, onSubmit, shouldS @@ -173,7 +174,7 @@ function AddressForm({city, country, formID, onAddressChanged, onSubmit, shouldS void; + + /** Whether the currency symbol is pressable */ + isCurrencyPressable?: boolean; }; /** @@ -47,13 +51,13 @@ const NUM_PAD_CONTAINER_VIEW_ID = 'numPadContainerView'; const NUM_PAD_VIEW_ID = 'numPadView'; function AmountForm( - {value: amount, currency = CONST.CURRENCY.USD, extraDecimals = 0, errorText, onInputChange, onCurrencyButtonPress}: AmountFormProps, - forwardedRef: ForwardedRef, + {value: amount, currency = CONST.CURRENCY.USD, extraDecimals = 0, errorText, onInputChange, onCurrencyButtonPress, isCurrencyPressable = true}: AmountFormProps, + forwardedRef: ForwardedRef, ) { const styles = useThemeStyles(); const {toLocaleDigit, numberFormat} = useLocalize(); - const textInput = useRef(null); + const textInput = useRef(null); const decimals = CurrencyUtils.getCurrencyDecimals(currency) + extraDecimals; const currentAmount = useMemo(() => (typeof amount === 'string' ? amount : ''), [amount]); @@ -187,12 +191,11 @@ function AmountForm( style={[styles.moneyRequestAmountContainer, styles.flex1, styles.flexRow, styles.w100, styles.alignItemsCenter, styles.justifyContentCenter]} > { + ref={(ref: BaseTextInputRef) => { if (typeof forwardedRef === 'function') { forwardedRef(ref); } else if (forwardedRef && 'current' in forwardedRef) { @@ -210,10 +213,11 @@ function AmountForm( setSelection(e.nativeEvent.selection); }} onKeyPress={textInputKeyPress} + isCurrencyPressable={isCurrencyPressable} /> {!!errorText && ( diff --git a/src/components/AmountTextInput.tsx b/src/components/AmountTextInput.tsx index 05080fcdd21c..fc7fddc094f5 100644 --- a/src/components/AmountTextInput.tsx +++ b/src/components/AmountTextInput.tsx @@ -1,6 +1,6 @@ import React from 'react'; import type {ForwardedRef} from 'react'; -import type {StyleProp, TextStyle, ViewStyle} from 'react-native'; +import type {NativeSyntheticEvent, StyleProp, TextInputKeyPressEventData, TextInputSelectionChangeEventData, TextStyle, ViewStyle} from 'react-native'; import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; import type {TextSelection} from './Composer/types'; @@ -21,7 +21,7 @@ type AmountTextInputProps = { selection?: TextSelection; /** Function to call when selection in text input is changed */ - onSelectionChange?: () => void; + onSelectionChange?: (event: NativeSyntheticEvent) => void; /** Style for the input */ style?: StyleProp; @@ -30,7 +30,7 @@ type AmountTextInputProps = { touchableInputWrapperStyle?: StyleProp; /** Function to call to handle key presses in the text input */ - onKeyPress?: () => void; + onKeyPress?: (event: NativeSyntheticEvent) => void; }; function AmountTextInput( @@ -54,7 +54,7 @@ function AmountTextInput( selection={selection} onSelectionChange={onSelectionChange} role={CONST.ROLE.PRESENTATION} - onKeyPress={onKeyPress} + onKeyPress={onKeyPress as (event: NativeSyntheticEvent) => void} touchableInputWrapperStyle={touchableInputWrapperStyle} /> ); diff --git a/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx b/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx index 33409f855bec..47a481e19d79 100644 --- a/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx +++ b/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; import AttachmentView from '@components/Attachments/AttachmentView'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import {ShowContextMenuContext, showContextMenuForReport} from '@components/ShowContextMenuContext'; diff --git a/src/components/Attachments/AttachmentCarousel/CarouselItem.js b/src/components/Attachments/AttachmentCarousel/CarouselItem.js index 5552f15320f3..edc8ab11fd27 100644 --- a/src/components/Attachments/AttachmentCarousel/CarouselItem.js +++ b/src/components/Attachments/AttachmentCarousel/CarouselItem.js @@ -35,26 +35,25 @@ const propTypes = { /** The id of the transaction related to the attachment */ transactionID: PropTypes.string, - }).isRequired, - - /** Whether there is only one element in the attachment carousel */ - isSingleItem: PropTypes.bool.isRequired, - - /** The index of the carousel item */ - index: PropTypes.number.isRequired, - /** The index of the currently active carousel item */ - activeIndex: PropTypes.number.isRequired, + duration: PropTypes.number, + }).isRequired, /** onPress callback */ onPress: PropTypes.func, + + isModalHovered: PropTypes.bool, + + /** Whether the attachment is currently being viewed in the carousel */ + isFocused: PropTypes.bool.isRequired, }; const defaultProps = { onPress: undefined, + isModalHovered: false, }; -function CarouselItem({item, index, activeIndex, isSingleItem, onPress}) { +function CarouselItem({item, onPress, isFocused, isModalHovered}) { const styles = useThemeStyles(); const {translate} = useLocalize(); const {isAttachmentHidden} = useContext(ReportAttachmentsContext); @@ -104,12 +103,11 @@ function CarouselItem({item, index, activeIndex, isSingleItem, onPress}) { source={item.source} file={item.file} isAuthTokenRequired={item.isAuthTokenRequired} - isUsedInCarousel - isSingleCarouselItem={isSingleItem} - carouselItemIndex={index} - carouselActiveItemIndex={activeIndex} onPress={onPress} transactionID={item.transactionID} + isHovered={isModalHovered} + isFocused={isFocused} + optionalVideoDuration={item.duration} /> diff --git a/src/components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPagerContext.ts b/src/components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPagerContext.ts index 270e0b04909c..fd9b57511cc4 100644 --- a/src/components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPagerContext.ts +++ b/src/components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPagerContext.ts @@ -3,7 +3,24 @@ import {createContext} from 'react'; import type PagerView from 'react-native-pager-view'; import type {SharedValue} from 'react-native-reanimated'; +/** The pager items array is used within the pager to render and navigate between the images */ +type AttachmentCarouselPagerItems = { + /** The source of the image is used to identify each attachment/page in the pager */ + source: string; + + /** The index of the pager item determines the order of the images in the pager */ + index: number; + + /** The active state of the pager item determines whether the image is currently transformable with pinch, pan and tap gestures */ + isActive: boolean; +}; + type AttachmentCarouselPagerContextValue = { + /** The list of items that are shown in the pager */ + pagerItems: AttachmentCarouselPagerItems[]; + + /** The index of the active page */ + activePage: number; pagerRef: ForwardedRef; isPagerScrolling: SharedValue; isScrollEnabled: SharedValue; diff --git a/src/components/Attachments/AttachmentCarousel/Pager/index.tsx b/src/components/Attachments/AttachmentCarousel/Pager/index.tsx index 490afb6614ac..8704584c3e18 100644 --- a/src/components/Attachments/AttachmentCarousel/Pager/index.tsx +++ b/src/components/Attachments/AttachmentCarousel/Pager/index.tsx @@ -6,6 +6,7 @@ import {createNativeWrapper} from 'react-native-gesture-handler'; import type {PagerViewProps} from 'react-native-pager-view'; import PagerView from 'react-native-pager-view'; import Animated, {useAnimatedProps, useSharedValue} from 'react-native-reanimated'; +import CarouselItem from '@components/Attachments/AttachmentCarousel/CarouselItem'; import useThemeStyles from '@hooks/useThemeStyles'; import AttachmentCarouselPagerContext from './AttachmentCarouselPagerContext'; import usePageScrollHandler from './usePageScrollHandler'; @@ -19,21 +20,31 @@ type AttachmentCarouselPagerHandle = { setPage: (selectedPage: number) => void; }; -type PagerItem = { - key: string; - url: string; +type Attachment = { source: string; }; type AttachmentCarouselPagerProps = { - items: PagerItem[]; - renderItem: (props: {item: PagerItem; index: number; isActive: boolean}) => React.ReactNode; - initialIndex: number; + /** The attachments to be rendered in the pager. */ + items: Attachment[]; + + /** The source (URL) of the currently active attachment. */ + activeSource: string; + + /** The index of the initial page to be rendered. */ + initialPage: number; + + /** A callback to be called when the page is changed. */ onPageSelected: () => void; + + /** + * A callback that can be used to toggle the attachment carousel arrows, when the scale of the image changes. + * @param showArrows If set, it will show/hide the arrows. If not set, it will toggle the arrows. + */ onRequestToggleArrows: (showArrows?: boolean) => void; }; -function AttachmentCarouselPager({items, renderItem, initialIndex, onPageSelected, onRequestToggleArrows}: AttachmentCarouselPagerProps, ref: ForwardedRef) { +function AttachmentCarouselPager({items, activeSource, initialPage, onPageSelected, onRequestToggleArrows}: AttachmentCarouselPagerProps, ref: ForwardedRef) { const styles = useThemeStyles(); const pagerRef = useRef(null); @@ -41,8 +52,8 @@ function AttachmentCarouselPager({items, renderItem, initialIndex, onPageSelecte const isPagerScrolling = useSharedValue(false); const isScrollEnabled = useSharedValue(true); - const activePage = useSharedValue(initialIndex); - const [activePageState, setActivePageState] = useState(initialIndex); + const activePage = useSharedValue(initialPage); + const [activePageIndex, setActivePageIndex] = useState(initialPage); const pageScrollHandler = usePageScrollHandler((e) => { 'worklet'; @@ -52,9 +63,12 @@ function AttachmentCarouselPager({items, renderItem, initialIndex, onPageSelecte }, []); useEffect(() => { - setActivePageState(initialIndex); - activePage.value = initialIndex; - }, [activePage, initialIndex]); + setActivePageIndex(initialPage); + activePage.value = initialPage; + }, [activePage, initialPage]); + + /** The `pagerItems` object that passed down to the context. Later used to detect current page, whether it's a single image gallery etc. */ + const pagerItems = useMemo(() => items.map((item, index) => ({source: item.source, index, isActive: index === activePageIndex})), [activePageIndex, items]); /** * This callback is passed to the MultiGestureCanvas/Lightbox through the AttachmentCarouselPagerContext. @@ -94,13 +108,15 @@ function AttachmentCarouselPager({items, renderItem, initialIndex, onPageSelecte const contextValue = useMemo( () => ({ - pagerRef, + pagerItems, + activePage: activePageIndex, isPagerScrolling, isScrollEnabled, + pagerRef, onTap: handleTap, onScaleChanged: handleScaleChange, }), - [isPagerScrolling, isScrollEnabled, handleTap, handleScaleChange], + [pagerItems, activePageIndex, isPagerScrolling, isScrollEnabled, handleTap, handleScaleChange], ); const animatedProps = useAnimatedProps(() => ({ @@ -121,6 +137,21 @@ function AttachmentCarouselPager({items, renderItem, initialIndex, onPageSelecte [], ); + const carouselItems = items.map((item, index) => ( + + + + )); + return ( - {items.map((item, index) => ( - - {renderItem({item, index, isActive: index === activePageState})} - - ))} + {carouselItems} ); diff --git a/src/components/Attachments/AttachmentCarousel/extractAttachmentsFromReport.js b/src/components/Attachments/AttachmentCarousel/extractAttachmentsFromReport.js index 6994a7a210bf..b934bdfdd738 100644 --- a/src/components/Attachments/AttachmentCarousel/extractAttachmentsFromReport.js +++ b/src/components/Attachments/AttachmentCarousel/extractAttachmentsFromReport.js @@ -18,24 +18,36 @@ function extractAttachmentsFromReport(parentReportAction, reportActions) { const htmlParser = new HtmlParser({ onopentag: (name, attribs) => { - if (name !== 'img' || !attribs.src) { + if (name === 'video') { + const splittedUrl = attribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE].split('/'); + attachments.unshift({ + reportActionID: null, + source: tryResolveUrlFromApiRoot(attribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE]), + isAuthTokenRequired: Boolean(attribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE]), + file: {name: splittedUrl[splittedUrl.length - 1]}, + duration: Number(attribs[CONST.ATTACHMENT_DURATION_ATTRIBUTE]), + isReceipt: false, + hasBeenFlagged: false, + }); return; } - const expensifySource = attribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE]; - const source = tryResolveUrlFromApiRoot(expensifySource || attribs.src); - const fileName = attribs[CONST.ATTACHMENT_ORIGINAL_FILENAME_ATTRIBUTE] || FileUtils.getFileName(`${source}`); - - // By iterating actions in chronological order and prepending each attachment - // we ensure correct order of attachments even across actions with multiple attachments. - attachments.unshift({ - reportActionID: attribs['data-id'], - source, - isAuthTokenRequired: Boolean(expensifySource), - file: {name: fileName}, - isReceipt: false, - hasBeenFlagged: attribs['data-flagged'] === 'true', - }); + if (name === 'img' && attribs.src) { + const expensifySource = attribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE]; + const source = tryResolveUrlFromApiRoot(expensifySource || attribs.src); + const fileName = attribs[CONST.ATTACHMENT_ORIGINAL_FILENAME_ATTRIBUTE] || FileUtils.getFileName(`${source}`); + + // By iterating actions in chronological order and prepending each attachment + // we ensure correct order of attachments even across actions with multiple attachments. + attachments.unshift({ + reportActionID: attribs['data-id'], + source, + isAuthTokenRequired: Boolean(expensifySource), + file: {name: fileName}, + isReceipt: false, + hasBeenFlagged: attribs['data-flagged'] === 'true', + }); + } }, }); diff --git a/src/components/Attachments/AttachmentCarousel/index.js b/src/components/Attachments/AttachmentCarousel/index.js index 9be1322e5ba8..ef6a11f6e67c 100644 --- a/src/components/Attachments/AttachmentCarousel/index.js +++ b/src/components/Attachments/AttachmentCarousel/index.js @@ -49,6 +49,10 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, const initialPage = _.findIndex(attachmentsFromReport, compareImage); + if (_.isEqual(attachments, attachmentsFromReport)) { + return; + } + // Dismiss the modal when deleting an attachment during its display in preview. if (initialPage === -1 && _.find(attachments, compareImage)) { Navigation.dismissModal(); @@ -64,8 +68,7 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, onNavigate(attachmentsFromReport[initialPage]); } } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [reportActions, parentReportActions, compareImage]); + }, [attachments, reportActions, parentReportActions, compareImage, report.parentReportActionID, setDownloadButtonVisibility, onNavigate]); /** * Updates the page state when the user navigates between attachments @@ -137,15 +140,18 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, * @returns {JSX.Element} */ const renderItem = useCallback( - ({item}) => ( + ({item, index}) => ( setShouldShowArrows(!shouldShowArrows) : undefined} + onPress={canUseTouchScreen ? () => setShouldShowArrows((oldState) => !oldState) : undefined} + isModalHovered={shouldShowArrows} + index={index} + activeIndex={page} /> ), - [activeSource, attachments.length, canUseTouchScreen, setShouldShowArrows, shouldShowArrows], + [activeSource, attachments.length, canUseTouchScreen, page, setShouldShowArrows, shouldShowArrows], ); return ( diff --git a/src/components/Attachments/AttachmentCarousel/index.native.js b/src/components/Attachments/AttachmentCarousel/index.native.js index fa24ccd0ef53..228f0d597a32 100644 --- a/src/components/Attachments/AttachmentCarousel/index.native.js +++ b/src/components/Attachments/AttachmentCarousel/index.native.js @@ -13,7 +13,6 @@ import variables from '@styles/variables'; import ONYXKEYS from '@src/ONYXKEYS'; import {defaultProps, propTypes} from './attachmentCarouselPropTypes'; import CarouselButtons from './CarouselButtons'; -import CarouselItem from './CarouselItem'; import extractAttachmentsFromReport from './extractAttachmentsFromReport'; import AttachmentCarouselPager from './Pager'; import useCarouselArrows from './useCarouselArrows'; @@ -103,24 +102,6 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, [setShouldShowArrows], ); - /** - * Defines how a single attachment should be rendered - * @param {{ reportActionID: String, isAuthTokenRequired: Boolean, source: String, file: { name: String }, hasBeenFlagged: Boolean }} item - * @returns {JSX.Element} - */ - const renderItem = useCallback( - ({item, index, isActive}) => ( - - ), - [activeSource, attachments.length, page], - ); - return ( {page == null ? ( @@ -148,8 +129,8 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, updatePage(newPage)} ref={pagerRef} diff --git a/src/components/Attachments/AttachmentView/AttachmentViewImage/index.js b/src/components/Attachments/AttachmentView/AttachmentViewImage/index.js index 14c60458b044..67f87b1733d3 100755 --- a/src/components/Attachments/AttachmentView/AttachmentViewImage/index.js +++ b/src/components/Attachments/AttachmentView/AttachmentViewImage/index.js @@ -12,21 +12,7 @@ const propTypes = { ...withLocalizePropTypes, }; -function AttachmentViewImage({ - url, - file, - isAuthTokenRequired, - isUsedInCarousel, - isSingleCarouselItem, - carouselItemIndex, - carouselActiveItemIndex, - isFocused, - loadComplete, - onPress, - onError, - isImage, - translate, -}) { +function AttachmentViewImage({url, file, isAuthTokenRequired, isFocused, loadComplete, onPress, onError, isImage, translate}) { const styles = useThemeStyles(); const children = ( ); diff --git a/src/components/Attachments/AttachmentView/AttachmentViewVideo/index.js b/src/components/Attachments/AttachmentView/AttachmentViewVideo/index.js new file mode 100644 index 000000000000..2b71e799beed --- /dev/null +++ b/src/components/Attachments/AttachmentView/AttachmentViewVideo/index.js @@ -0,0 +1,44 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import VideoPlayer from '@components/VideoPlayer'; +import useThemeStyles from '@hooks/useThemeStyles'; +import useWindowDimensions from '@hooks/useWindowDimensions'; + +const propTypes = { + /** Video file source URL */ + source: PropTypes.string.isRequired, + + /** Whether the video is currently being hovered over */ + isHovered: PropTypes.bool, + + shouldUseSharedVideoElement: PropTypes.bool, + + videoDuration: PropTypes.number, +}; + +const defaultProps = { + isHovered: false, + shouldUseSharedVideoElement: false, + videoDuration: 0, +}; + +function AttachmentViewVideo({source, isHovered, shouldUseSharedVideoElement, videoDuration}) { + const {isSmallScreen} = useWindowDimensions(); + const styles = useThemeStyles(); + + return ( + + ); +} + +AttachmentViewVideo.propTypes = propTypes; +AttachmentViewVideo.defaultProps = defaultProps; +AttachmentViewVideo.displayName = 'AttachmentViewVideo'; + +export default React.memo(AttachmentViewVideo); diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index 33eab13f3851..d02957aa8137 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -1,6 +1,6 @@ import Str from 'expensify-common/lib/str'; import PropTypes from 'prop-types'; -import React, {memo, useState} from 'react'; +import React, {memo, useEffect, useState} from 'react'; import {ActivityIndicator, ScrollView, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; @@ -11,6 +11,7 @@ import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import Text from '@components/Text'; import Tooltip from '@components/Tooltip'; +import {usePlaybackContext} from '@components/VideoPlayerContexts/PlaybackContext'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import useNetwork from '@hooks/useNetwork'; import useStyleUtils from '@hooks/useStyleUtils'; @@ -23,6 +24,7 @@ import variables from '@styles/variables'; import ONYXKEYS from '@src/ONYXKEYS'; import AttachmentViewImage from './AttachmentViewImage'; import AttachmentViewPdf from './AttachmentViewPdf'; +import AttachmentViewVideo from './AttachmentViewVideo'; import {attachmentViewDefaultProps, attachmentViewPropTypes} from './propTypes'; const propTypes = { @@ -54,6 +56,10 @@ const propTypes = { /** The id of the transaction related to the attachment */ // eslint-disable-next-line react/no-unused-prop-types transactionID: PropTypes.string, + + isHovered: PropTypes.bool, + + optionalVideoDuration: PropTypes.number, }; const defaultProps = { @@ -65,6 +71,8 @@ const defaultProps = { isWorkspaceAvatar: false, maybeIcon: false, transactionID: '', + isHovered: false, + optionalVideoDuration: 0, }; function AttachmentView({ @@ -79,19 +87,28 @@ function AttachmentView({ translate, isFocused, isUsedInCarousel, - isSingleCarouselItem, - carouselItemIndex, - carouselActiveItemIndex, isUsedInAttachmentModal, isWorkspaceAvatar, maybeIcon, fallbackSource, transaction, + isHovered, + optionalVideoDuration, }) { + const {updateCurrentlyPlayingURL} = usePlaybackContext(); const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const [loadComplete, setLoadComplete] = useState(false); + const isVideo = Str.isVideo(source); + + useEffect(() => { + if (!isFocused) { + return; + } + updateCurrentlyPlayingURL(isVideo ? source : null); + }, [isFocused, isVideo, source, updateCurrentlyPlayingURL]); + const [imageError, setImageError] = useState(false); useNetwork({onReconnect: () => setImageError(false)}); @@ -147,8 +164,6 @@ function AttachmentView({ isFocused={isFocused} isAuthTokenRequired={isAuthTokenRequired} encryptedSourceUrl={encryptedSourceUrl} - carouselItemIndex={carouselItemIndex} - carouselActiveItemIndex={carouselActiveItemIndex} onPress={onPress} onToggleKeyboard={onToggleKeyboard} onLoadComplete={() => !loadComplete && setLoadComplete(true)} @@ -177,9 +192,6 @@ function AttachmentView({ loadComplete={loadComplete} isFocused={isFocused} isUsedInCarousel={isUsedInCarousel} - isSingleCarouselItem={isSingleCarouselItem} - carouselItemIndex={carouselItemIndex} - carouselActiveItemIndex={carouselActiveItemIndex} isImage={isImage} onPress={onPress} onError={() => { @@ -189,6 +201,17 @@ function AttachmentView({ ); } + if (isVideo || (file && Str.isVideo(file.name))) { + return ( + + ); + } + return ( diff --git a/src/components/Attachments/AttachmentView/propTypes.js b/src/components/Attachments/AttachmentView/propTypes.js index d78bed8526b8..0a0d654912d3 100644 --- a/src/components/Attachments/AttachmentView/propTypes.js +++ b/src/components/Attachments/AttachmentView/propTypes.js @@ -14,18 +14,6 @@ const attachmentViewPropTypes = { /** Whether this AttachmentView is shown as part of a AttachmentCarousel */ isUsedInCarousel: PropTypes.bool, - /** When "isUsedInCarousel" is set to true, determines whether there is only one item in the carousel */ - isSingleCarouselItem: PropTypes.bool, - - /** Whether this AttachmentView is shown as part of an AttachmentModal */ - isUsedInAttachmentModal: PropTypes.bool, - - /** The index of the carousel item */ - carouselItemIndex: PropTypes.number, - - /** The index of the currently active carousel item */ - carouselActiveItemIndex: PropTypes.number, - /** Function for handle on press */ onPress: PropTypes.func, @@ -39,11 +27,8 @@ const attachmentViewDefaultProps = { name: '', }, isFocused: false, - isUsedInCarousel: false, - isSingleCarouselItem: false, - carouselItemIndex: 0, - carouselActiveItemIndex: 0, isSingleElement: false, + isUsedInCarousel: false, isUsedInAttachmentModal: false, onPress: undefined, onScaleChanged: () => {}, diff --git a/src/components/AvatarWithImagePicker.js b/src/components/AvatarWithImagePicker.js index f55db3dd0620..26d41ea82e00 100644 --- a/src/components/AvatarWithImagePicker.js +++ b/src/components/AvatarWithImagePicker.js @@ -100,6 +100,9 @@ const propTypes = { horizontal: PropTypes.oneOf(_.values(CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL)), vertical: PropTypes.oneOf(_.values(CONST.MODAL.ANCHOR_ORIGIN_VERTICAL)), }), + + /** Allows to open an image without Attachment Picker. */ + enablePreview: PropTypes.bool, }; const defaultProps = { @@ -127,6 +130,7 @@ const defaultProps = { horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP, }, + enablePreview: false, }; function AvatarWithImagePicker({ @@ -152,6 +156,7 @@ function AvatarWithImagePicker({ avatarStyle, disabled, onViewPhotoPress, + enablePreview, }) { const theme = useTheme(); const styles = useThemeStyles(); @@ -330,10 +335,16 @@ function AvatarWithImagePicker({ text={translate('avatarWithImagePicker.editImage')} > setIsMenuVisible((prev) => !prev)} + onPress={() => { + if (disabled && enablePreview && onViewPhotoPress) { + onViewPhotoPress(); + return; + } + setIsMenuVisible((prev) => !prev); + }} accessibilityRole={CONST.ACCESSIBILITY_ROLE.IMAGEBUTTON} accessibilityLabel={translate('avatarWithImagePicker.editImage')} - disabled={isAvatarCropModalOpen || disabled} + disabled={isAvatarCropModalOpen || (disabled && !enablePreview)} disabledStyle={disabledStyle} ref={anchorRef} > diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index 1961829b6aa7..1777b239e714 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -320,7 +320,7 @@ function Button( shouldRemoveRightBorderRadius ? styles.noRightBorderRadius : undefined, shouldRemoveLeftBorderRadius ? styles.noLeftBorderRadius : undefined, // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - 'text' in rest && (rest?.icon || rest?.shouldShowRightIcon) ? styles.alignItemsStretch : undefined, + 'text' in rest && rest?.shouldShowRightIcon ? styles.alignItemsStretch : undefined, innerStyles, ]} hoverStyle={[ diff --git a/src/components/ConfirmedRoute.tsx b/src/components/ConfirmedRoute.tsx index c38241b275ba..aaa43e33744d 100644 --- a/src/components/ConfirmedRoute.tsx +++ b/src/components/ConfirmedRoute.tsx @@ -15,14 +15,9 @@ import type IconAsset from '@src/types/utils/IconAsset'; import DistanceMapView from './DistanceMapView'; import * as Expensicons from './Icon/Expensicons'; import ImageSVG from './ImageSVG'; +import type {WayPoint} from './MapView/MapViewTypes'; import PendingMapView from './MapView/PendingMapView'; -type WayPoint = { - id: string; - coordinate: [number, number]; - markerComponent: () => ReactNode; -}; - type ConfirmedRoutePropsOnyxProps = { /** Data about Mapbox token for calling Mapbox API */ mapboxAccessToken: OnyxEntry; diff --git a/src/components/CurrencySymbolButton.tsx b/src/components/CurrencySymbolButton.tsx index e913b415c328..2b736c2ef527 100644 --- a/src/components/CurrencySymbolButton.tsx +++ b/src/components/CurrencySymbolButton.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -15,13 +16,16 @@ type CurrencySymbolButtonProps = { /** Function to call when currency button is pressed */ onCurrencyButtonPress: () => void; + + /** Whether the currency button is pressable or not */ + isCurrencyPressable?: boolean; }; -function CurrencySymbolButton({onCurrencyButtonPress, currencySymbol}: CurrencySymbolButtonProps) { +function CurrencySymbolButton({onCurrencyButtonPress, currencySymbol, isCurrencyPressable = true}: CurrencySymbolButtonProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const theme = useTheme(); - return ( + return isCurrencyPressable ? ( {currencySymbol} + ) : ( + + {currencySymbol} + ); } diff --git a/src/components/DatePicker/CalendarPicker/ArrowIcon.js b/src/components/DatePicker/CalendarPicker/ArrowIcon.tsx similarity index 61% rename from src/components/DatePicker/CalendarPicker/ArrowIcon.js rename to src/components/DatePicker/CalendarPicker/ArrowIcon.tsx index 11455cb4f78a..5c58ed22f6fa 100644 --- a/src/components/DatePicker/CalendarPicker/ArrowIcon.js +++ b/src/components/DatePicker/CalendarPicker/ArrowIcon.tsx @@ -1,6 +1,6 @@ -import PropTypes from 'prop-types'; import React from 'react'; import {View} from 'react-native'; +import type {ValueOf} from 'type-fest'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import useStyleUtils from '@hooks/useStyleUtils'; @@ -8,25 +8,20 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; -const propTypes = { +type ArrowIconProps = { /** Specifies if the arrow icon should be disabled or not. */ - disabled: PropTypes.bool, + disabled?: boolean; /** Specifies direction of icon */ - direction: PropTypes.oneOf([CONST.DIRECTION.LEFT, CONST.DIRECTION.RIGHT]), + direction?: ValueOf; }; -const defaultProps = { - disabled: false, - direction: CONST.DIRECTION.RIGHT, -}; - -function ArrowIcon(props) { +function ArrowIcon({disabled = false, direction = CONST.DIRECTION.RIGHT}: ArrowIconProps) { const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); return ( - + void; /** Function to call when the user closes the year picker */ - onClose: PropTypes.func, + onClose?: () => void; }; -const defaultProps = { - currentYear: new Date().getFullYear(), - onYearChange: () => {}, - onClose: () => {}, -}; - -function YearPickerModal(props) { +function YearPickerModal({isVisible, years, currentYear = new Date().getFullYear(), onYearChange, onClose}: YearPickerModalProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const [searchText, setSearchText] = useState(''); const {sections, headerMessage} = useMemo(() => { - const yearsList = searchText === '' ? props.years : _.filter(props.years, (year) => year.text.includes(searchText)); + const yearsList = searchText === '' ? years : years.filter((year) => year.text.includes(searchText)); return { headerMessage: !yearsList.length ? translate('common.noResultsFound') : '', sections: [{data: yearsList, indexOffset: 0}], }; - }, [props.years, searchText, translate]); + }, [years, searchText, translate]); useEffect(() => { - if (props.isVisible) { + if (isVisible) { return; } setSearchText(''); - }, [props.isVisible]); + }, [isVisible]); return ( onClose?.()} + onModalHide={onClose} hideModalContentWhileAnimating useNativeDriver > @@ -69,7 +61,7 @@ function YearPickerModal(props) { > props.onYearChange(option.value)} - initiallyFocusedOptionKey={props.currentYear.toString()} + onSelectRow={(option: CalendarPickerRadioItem) => { + onYearChange?.(option.value); + }} + initiallyFocusedOptionKey={currentYear.toString()} showScrollIndicator shouldStopPropagation /> @@ -90,8 +84,6 @@ function YearPickerModal(props) { ); } -YearPickerModal.propTypes = propTypes; -YearPickerModal.defaultProps = defaultProps; YearPickerModal.displayName = 'YearPickerModal'; export default YearPickerModal; diff --git a/src/components/DatePicker/CalendarPicker/generateMonthMatrix.js b/src/components/DatePicker/CalendarPicker/generateMonthMatrix.ts similarity index 71% rename from src/components/DatePicker/CalendarPicker/generateMonthMatrix.js rename to src/components/DatePicker/CalendarPicker/generateMonthMatrix.ts index ecf338d36424..21e7ff752794 100644 --- a/src/components/DatePicker/CalendarPicker/generateMonthMatrix.js +++ b/src/components/DatePicker/CalendarPicker/generateMonthMatrix.ts @@ -4,20 +4,14 @@ import DateUtils from '@libs/DateUtils'; /** * Generates a matrix representation of a month's calendar given the year and month. * - * @param {Number} year - The year for which to generate the month matrix. - * @param {Number} month - The month (0-indexed) for which to generate the month matrix. - * @returns {Array>} - A 2D array of the month's calendar days, with null values representing days outside the current month. + * @param year - The year for which to generate the month matrix. + * @param month - The month (0-indexed) for which to generate the month matrix. + * @returns A 2D array of the month's calendar days, with null values representing days outside the current month. */ -export default function generateMonthMatrix(year, month) { - if (typeof year !== 'number') { - throw new TypeError('Year must be a number'); - } +export default function generateMonthMatrix(year: number, month: number) { if (year < 0) { throw new Error('Year cannot be less than 0'); } - if (typeof month !== 'number') { - throw new TypeError('Month must be a number'); - } if (month < 0) { throw new Error('Month cannot be less than 0'); } @@ -51,14 +45,14 @@ export default function generateMonthMatrix(year, month) { // Add null values for days after the last day of the month if (currentWeek.length > 0) { for (let i = currentWeek.length; i < 7; i++) { - currentWeek.push(null); + currentWeek.push(undefined); } 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); + matrix[0].unshift(undefined); } return matrix; diff --git a/src/components/DatePicker/CalendarPicker/index.js b/src/components/DatePicker/CalendarPicker/index.js deleted file mode 100644 index f10af5e4a5a7..000000000000 --- a/src/components/DatePicker/CalendarPicker/index.js +++ /dev/null @@ -1,296 +0,0 @@ -import {addMonths, endOfDay, endOfMonth, format, getYear, isSameDay, parseISO, setDate, setYear, startOfDay, startOfMonth, subMonths} from 'date-fns'; -import Str from 'expensify-common/lib/str'; -import PropTypes from 'prop-types'; -import React from 'react'; -import {View} from 'react-native'; -import _ from 'underscore'; -import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; -import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; -import Text from '@components/Text'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; -import withStyleUtils, {withStyleUtilsPropTypes} from '@components/withStyleUtils'; -import withThemeStyles, {withThemeStylesPropTypes} from '@components/withThemeStyles'; -import compose from '@libs/compose'; -import DateUtils from '@libs/DateUtils'; -import getButtonState from '@libs/getButtonState'; -import CONST from '@src/CONST'; -import ArrowIcon from './ArrowIcon'; -import generateMonthMatrix from './generateMonthMatrix'; -import YearPickerModal from './YearPickerModal'; - -const propTypes = { - /** An initial value of date string */ - value: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]), - - /** A minimum date (oldest) allowed to select */ - minDate: PropTypes.instanceOf(Date), - - /** A maximum date (earliest) allowed to select */ - maxDate: PropTypes.instanceOf(Date), - - /** A function called when the date is selected */ - onSelected: PropTypes.func, - - ...withLocalizePropTypes, - ...withThemeStylesPropTypes, - ...withStyleUtilsPropTypes, -}; - -const defaultProps = { - value: new Date(), - minDate: setYear(new Date(), CONST.CALENDAR_PICKER.MIN_YEAR), - maxDate: setYear(new Date(), CONST.CALENDAR_PICKER.MAX_YEAR), - onSelected: () => {}, -}; - -class CalendarPicker extends React.PureComponent { - constructor(props) { - super(props); - - if (props.minDate >= props.maxDate) { - throw new Error('Minimum date cannot be greater than the maximum date.'); - } - 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) { - currentDateView = props.minDate; - } - - const minYear = getYear(new Date(this.props.minDate)); - const maxYear = getYear(new Date(this.props.maxDate)); - - this.state = { - currentDateView, - isYearPickerVisible: false, - years: _.map( - Array.from({length: maxYear - minYear + 1}, (v, i) => i + minYear), - (value) => ({ - text: value.toString(), - value, - keyForList: value.toString(), - isSelected: value === currentDateView.getFullYear(), - }), - ), - }; - - this.moveToPrevMonth = this.moveToPrevMonth.bind(this); - this.moveToNextMonth = this.moveToNextMonth.bind(this); - this.onDayPressed = this.onDayPressed.bind(this); - this.onYearSelected = this.onYearSelected.bind(this); - } - - onYearSelected(year) { - this.setState((prev) => { - const newCurrentDateView = setYear(new Date(prev.currentDateView), year); - - return { - currentDateView: newCurrentDateView, - isYearPickerVisible: false, - years: _.map(prev.years, (item) => ({ - ...item, - isSelected: item.value === newCurrentDateView.getFullYear(), - })), - }; - }); - } - - /** - * Calls the onSelected function with the selected date. - * @param {Number} day - The day of the month that was selected. - */ - onDayPressed(day) { - this.setState( - (prev) => ({ - currentDateView: setDate(new Date(prev.currentDateView), day), - }), - () => this.props.onSelected(format(new Date(this.state.currentDateView), CONST.DATE.FNS_FORMAT_STRING)), - ); - } - - /** - * Handles the user pressing the previous month arrow of the calendar picker. - */ - moveToPrevMonth() { - this.setState((prev) => { - const prevMonth = subMonths(new Date(prev.currentDateView), 1); - // if year is subtracted, we need to update the years list - let newYears = prev.years; - if (prevMonth.getFullYear() < prev.currentDateView.getFullYear()) { - newYears = _.map(prev.years, (item) => ({ - ...item, - isSelected: item.value === prevMonth.getFullYear(), - })); - } - - return { - currentDateView: prevMonth, - years: newYears, - }; - }); - } - - /** - * Handles the user pressing the next month arrow of the calendar picker. - */ - moveToNextMonth() { - this.setState((prev) => { - const nextMonth = addMonths(new Date(prev.currentDateView), 1); - // if year is added, we need to update the years list - let newYears = prev.years; - if (nextMonth.getFullYear() > prev.currentDateView.getFullYear()) { - newYears = _.map(prev.years, (item) => ({ - ...item, - isSelected: item.value === nextMonth.getFullYear(), - })); - } - - return { - currentDateView: nextMonth, - years: newYears, - }; - }); - } - - render() { - const monthNames = _.map(DateUtils.getMonthNames(this.props.preferredLocale), Str.recapitalize); - const daysOfWeek = _.map(DateUtils.getDaysOfWeek(this.props.preferredLocale), (day) => day.toUpperCase()); - const currentMonthView = this.state.currentDateView.getMonth(); - const currentYearView = this.state.currentDateView.getFullYear(); - const calendarDaysMatrix = generateMonthMatrix(currentYearView, currentMonthView); - const hasAvailableDatesNextMonth = startOfDay(new Date(this.props.maxDate)) > endOfMonth(new Date(this.state.currentDateView)); - const hasAvailableDatesPrevMonth = endOfDay(new Date(this.props.minDate)) < startOfMonth(new Date(this.state.currentDateView)); - - return ( - - - this.setState({isYearPickerVisible: true})} - style={[this.props.themeStyles.alignItemsCenter, this.props.themeStyles.flexRow, this.props.themeStyles.flex1, this.props.themeStyles.justifyContentStart]} - wrapperStyle={[this.props.themeStyles.alignItemsCenter]} - hoverDimmingValue={1} - testID="currentYearButton" - accessibilityLabel={this.props.translate('common.currentYear')} - > - - {currentYearView} - - - - - - {monthNames[currentMonthView]} - - - - - - - - - - - {_.map(daysOfWeek, (dayOfWeek) => ( - - {dayOfWeek[0]} - - ))} - - {_.map(calendarDaysMatrix, (week) => ( - - {_.map(week, (day, index) => { - const currentDate = new Date(currentYearView, currentMonthView, day); - const isBeforeMinDate = currentDate < startOfDay(new Date(this.props.minDate)); - const isAfterMaxDate = currentDate > startOfDay(new Date(this.props.maxDate)); - const isDisabled = !day || isBeforeMinDate || isAfterMaxDate; - const isSelected = !!day && isSameDay(parseISO(this.props.value), new Date(currentYearView, currentMonthView, day)); - return ( - this.onDayPressed(day)} - style={this.props.themeStyles.calendarDayRoot} - accessibilityLabel={day ? day.toString() : undefined} - tabIndex={day ? 0 : -1} - accessible={Boolean(day)} - dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}} - > - {({hovered, pressed}) => ( - - {day} - - )} - - ); - })} - - ))} - this.setState({isYearPickerVisible: false})} - /> - - ); - } -} - -CalendarPicker.propTypes = propTypes; -CalendarPicker.defaultProps = defaultProps; - -export default compose(withLocalize, withThemeStyles, withStyleUtils)(CalendarPicker); diff --git a/src/components/DatePicker/CalendarPicker/index.tsx b/src/components/DatePicker/CalendarPicker/index.tsx new file mode 100644 index 000000000000..60673f9a28d2 --- /dev/null +++ b/src/components/DatePicker/CalendarPicker/index.tsx @@ -0,0 +1,268 @@ +import {addMonths, endOfDay, endOfMonth, format, getYear, isSameDay, parseISO, setDate, setYear, startOfDay, startOfMonth, subMonths} from 'date-fns'; +import Str from 'expensify-common/lib/str'; +import React, {useState} from 'react'; +import {View} from 'react-native'; +import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; +import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useThemeStyles from '@hooks/useThemeStyles'; +import DateUtils from '@libs/DateUtils'; +import getButtonState from '@libs/getButtonState'; +import CONST from '@src/CONST'; +import ArrowIcon from './ArrowIcon'; +import generateMonthMatrix from './generateMonthMatrix'; +import type CalendarPickerRadioItem from './types'; +import YearPickerModal from './YearPickerModal'; + +type CalendarPickerProps = { + /** An initial value of date string */ + value?: Date | string; + + /** A minimum date (oldest) allowed to select */ + minDate?: Date; + + /** A maximum date (earliest) allowed to select */ + maxDate?: Date; + + /** A function called when the date is selected */ + onSelected?: (selectedDate: string) => void; +}; + +function getInitialCurrentDateView(value: Date | string, minDate: Date, maxDate: Date) { + let initialCurrentDateView = typeof value === 'string' ? parseISO(value) : new Date(value); + + if (maxDate < initialCurrentDateView) { + initialCurrentDateView = maxDate; + } else if (minDate > initialCurrentDateView) { + initialCurrentDateView = minDate; + } + + return initialCurrentDateView; +} + +function CalendarPicker({ + value = new Date(), + minDate = setYear(new Date(), CONST.CALENDAR_PICKER.MIN_YEAR), + maxDate = setYear(new Date(), CONST.CALENDAR_PICKER.MAX_YEAR), + onSelected, +}: CalendarPickerProps) { + const themeStyles = useThemeStyles(); + const StyleUtils = useStyleUtils(); + const {preferredLocale, translate} = useLocalize(); + + const [currentDateView, setCurrentDateView] = useState(getInitialCurrentDateView(value, minDate, maxDate)); + + const [isYearPickerVisible, setIsYearPickerVisible] = useState(false); + + const minYear = getYear(new Date(minDate)); + const maxYear = getYear(new Date(maxDate)); + + const [years, setYears] = useState( + Array.from({length: maxYear - minYear + 1}, (v, i) => i + minYear).map((year) => ({ + text: year.toString(), + value: year, + keyForList: year.toString(), + isSelected: year === currentDateView.getFullYear(), + })), + ); + + const onYearSelected = (year: number) => { + setIsYearPickerVisible(false); + setCurrentDateView((prev) => { + const newCurrentDateView = setYear(new Date(prev), year); + setYears((prevYears) => + prevYears.map((item) => ({ + ...item, + isSelected: item.value === newCurrentDateView.getFullYear(), + })), + ); + return newCurrentDateView; + }); + }; + + /** + * Calls the onSelected function with the selected date. + * @param day - The day of the month that was selected. + */ + const onDayPressed = (day: number) => { + setCurrentDateView((prev) => { + const newCurrentDateView = setDate(new Date(prev), day); + onSelected?.(format(new Date(newCurrentDateView), CONST.DATE.FNS_FORMAT_STRING)); + return newCurrentDateView; + }); + }; + + /** + * Handles the user pressing the previous month arrow of the calendar picker. + */ + const moveToPrevMonth = () => { + setCurrentDateView((prev) => { + const prevMonth = subMonths(new Date(prev), 1); + // if year is subtracted, we need to update the years list + if (prevMonth.getFullYear() < prev.getFullYear()) { + setYears((prevYears) => + prevYears.map((item) => ({ + ...item, + isSelected: item.value === prevMonth.getFullYear(), + })), + ); + } + return prevMonth; + }); + }; + + /** + * Handles the user pressing the next month arrow of the calendar picker. + */ + const moveToNextMonth = () => { + setCurrentDateView((prev) => { + const nextMonth = addMonths(new Date(prev), 1); + // if year is added, we need to update the years list + if (nextMonth.getFullYear() > prev.getFullYear()) { + setYears((prevYears) => + prevYears.map((item) => ({ + ...item, + isSelected: item.value === nextMonth.getFullYear(), + })), + ); + } + + return nextMonth; + }); + }; + + const monthNames = DateUtils.getMonthNames(preferredLocale).map((month) => Str.recapitalize(month)); + const daysOfWeek = DateUtils.getDaysOfWeek(preferredLocale).map((day) => day.toUpperCase()); + const currentMonthView = currentDateView.getMonth(); + const currentYearView = currentDateView.getFullYear(); + const calendarDaysMatrix = generateMonthMatrix(currentYearView, currentMonthView); + const hasAvailableDatesNextMonth = startOfDay(new Date(maxDate)) > endOfMonth(new Date(currentDateView)); + const hasAvailableDatesPrevMonth = endOfDay(new Date(minDate)) < startOfMonth(new Date(currentDateView)); + + return ( + + + setIsYearPickerVisible(true)} + style={[themeStyles.alignItemsCenter, themeStyles.flexRow, themeStyles.flex1, themeStyles.justifyContentStart]} + wrapperStyle={[themeStyles.alignItemsCenter]} + hoverDimmingValue={1} + testID="currentYearButton" + accessibilityLabel={translate('common.currentYear')} + > + + {currentYearView} + + + + + + {monthNames[currentMonthView]} + + + + + + + + + + + {daysOfWeek.map((dayOfWeek) => ( + + {dayOfWeek[0]} + + ))} + + {calendarDaysMatrix.map((week) => ( + + {week.map((day, index) => { + const currentDate = new Date(currentYearView, currentMonthView, day); + const isBeforeMinDate = currentDate < startOfDay(new Date(minDate)); + const isAfterMaxDate = currentDate > startOfDay(new Date(maxDate)); + const isDisabled = !day || isBeforeMinDate || isAfterMaxDate; + const isSelected = !!day && isSameDay(parseISO(value.toString()), new Date(currentYearView, currentMonthView, day)); + const handleOnPress = () => { + if (!day) { + return; + } + + onDayPressed(day); + }; + const key = `${index}_day-${day}`; + return ( + + {({hovered, pressed}) => ( + + {day} + + )} + + ); + })} + + ))} + setIsYearPickerVisible(false)} + /> + + ); +} + +export default CalendarPicker; diff --git a/src/components/DatePicker/CalendarPicker/types.ts b/src/components/DatePicker/CalendarPicker/types.ts new file mode 100644 index 000000000000..ce5e16d50535 --- /dev/null +++ b/src/components/DatePicker/CalendarPicker/types.ts @@ -0,0 +1,8 @@ +import type {RadioItem} from '@components/SelectionList/types'; + +type CalendarPickerRadioItem = RadioItem & { + /** The value representing a year in the CalendarPicker */ + value: number; +}; + +export default CalendarPickerRadioItem; diff --git a/src/components/DatePicker/index.js b/src/components/DatePicker/index.tsx similarity index 57% rename from src/components/DatePicker/index.js rename to src/components/DatePicker/index.tsx index 759908a4647f..b7c871d4a938 100644 --- a/src/components/DatePicker/index.js +++ b/src/components/DatePicker/index.tsx @@ -1,105 +1,90 @@ import {setYear} from 'date-fns'; -import _ from 'lodash'; -import PropTypes from 'prop-types'; +import type {ForwardedRef} from 'react'; import React, {forwardRef, useEffect, useState} from 'react'; import {View} from 'react-native'; import * as Expensicons from '@components/Icon/Expensicons'; -import refPropTypes from '@components/refPropTypes'; import TextInput from '@components/TextInput'; -import {propTypes as baseTextInputPropTypes, defaultProps as defaultBaseTextInputPropTypes} from '@components/TextInput/BaseTextInput/baseTextInputPropTypes'; +import type {BaseTextInputProps, BaseTextInputRef} from '@components/TextInput/BaseTextInput/types'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import useWindowDimensions from '@hooks/useWindowDimensions'; import * as FormActions from '@userActions/FormActions'; import CONST from '@src/CONST'; +import type {OnyxFormValuesMapping} from '@src/ONYXKEYS'; import CalendarPicker from './CalendarPicker'; -const propTypes = { - /** React ref being forwarded to the DatePicker input */ - forwardedRef: refPropTypes, - +type DatePickerProps = { /** * The datepicker supports any value that `new Date()` can parse. * `onInputChange` would always be called with a Date (or null) */ - value: PropTypes.string, + value?: string; /** * The datepicker supports any defaultValue that `new Date()` can parse. * `onInputChange` would always be called with a Date (or null) */ - defaultValue: PropTypes.string, + defaultValue?: string; - inputID: PropTypes.string.isRequired, + inputID: string; /** A minimum date of calendar to select */ - minDate: PropTypes.objectOf(Date), + minDate?: Date; /** A maximum date of calendar to select */ - maxDate: PropTypes.objectOf(Date), + maxDate?: Date; /** A function that is passed by FormWrapper */ - onInputChange: PropTypes.func.isRequired, + onInputChange: (value: Date) => void; /** A function that is passed by FormWrapper */ - onTouched: PropTypes.func.isRequired, + onTouched: () => void; /** Saves a draft of the input value when used in a form */ - shouldSaveDraft: PropTypes.bool, + shouldSaveDraft?: boolean; /** ID of the wrapping form */ - formID: PropTypes.string, - - ...baseTextInputPropTypes, -}; - -const datePickerDefaultProps = { - ...defaultBaseTextInputPropTypes, - minDate: setYear(new Date(), CONST.CALENDAR_PICKER.MIN_YEAR), - maxDate: setYear(new Date(), CONST.CALENDAR_PICKER.MAX_YEAR), - value: undefined, - shouldSaveDraft: false, - formID: '', -}; - -function DatePicker({ - forwardedRef, - containerStyles, - defaultValue, - disabled, - errorText, - inputID, - isSmallScreenWidth, - label, - maxDate, - minDate, - onInputChange, - onTouched, - placeholder, - value, - shouldSaveDraft, - formID, -}) { + formID?: keyof OnyxFormValuesMapping; +} & BaseTextInputProps; + +function DatePicker( + { + containerStyles, + defaultValue, + disabled, + errorText, + inputID, + label, + maxDate = setYear(new Date(), CONST.CALENDAR_PICKER.MAX_YEAR), + minDate = setYear(new Date(), CONST.CALENDAR_PICKER.MIN_YEAR), + onInputChange, + onTouched, + placeholder, + value, + shouldSaveDraft = false, + formID, + }: DatePickerProps, + ref: ForwardedRef, +) { const styles = useThemeStyles(); const {translate} = useLocalize(); + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const [selectedDate, setSelectedDate] = useState(value || defaultValue || undefined); + const {isSmallScreenWidth} = useWindowDimensions(); - const onSelected = (newValue) => { - if (_.isFunction(onTouched)) { - onTouched(); - } - if (_.isFunction(onInputChange)) { - onInputChange(newValue); - } + const onSelected = (newValue: string) => { + onTouched?.(); + onInputChange?.(newValue); setSelectedDate(newValue); }; useEffect(() => { // Value is provided to input via props and onChange never fires. We have to save draft manually. - if (shouldSaveDraft && formID !== '') { + if (shouldSaveDraft && !!formID) { FormActions.setDraftValues(formID, {[inputID]: selectedDate}); } - if (selectedDate === value || _.isUndefined(value)) { + if (selectedDate === value || !value) { return; } @@ -110,7 +95,7 @@ function DatePicker({ ( - -)); - -DatePickerWithRef.displayName = 'DatePickerWithRef'; - -export default DatePickerWithRef; +export default forwardRef(DatePicker); diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.tsx similarity index 73% rename from src/components/DistanceEReceipt.js rename to src/components/DistanceEReceipt.tsx index 794c1c9aa53f..9846cfa04257 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.tsx @@ -1,51 +1,43 @@ -import lodashGet from 'lodash/get'; import React, {useMemo} from 'react'; import {ScrollView, View} from 'react-native'; -import _ from 'underscore'; import EReceiptBackground from '@assets/images/eReceipt_background.svg'; import useLocalize from '@hooks/useLocalize'; -import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import * as ReceiptUtils from '@libs/ReceiptUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import tryResolveUrlFromApiRoot from '@libs/tryResolveUrlFromApiRoot'; +import type {TranslationPaths} from '@src/languages/types'; +import type {Transaction} from '@src/types/onyx'; +import type {WaypointCollection} from '@src/types/onyx/Transaction'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; import ImageSVG from './ImageSVG'; import PendingMapView from './MapView/PendingMapView'; import Text from './Text'; import ThumbnailImage from './ThumbnailImage'; -import transactionPropTypes from './transactionPropTypes'; -const propTypes = { +type DistanceEReceiptProps = { /** The transaction for the distance request */ - transaction: transactionPropTypes, + transaction: Transaction; }; -const defaultProps = { - transaction: {}, -}; - -function DistanceEReceipt({transaction}) { - const theme = useTheme(); +function DistanceEReceipt({transaction}: DistanceEReceiptProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const {thumbnail} = TransactionUtils.hasReceipt(transaction) ? ReceiptUtils.getThumbnailAndImageURIs(transaction) : {}; - const {amount: transactionAmount, currency: transactionCurrency, merchant: transactionMerchant, created: transactionDate} = ReportUtils.getTransactionDetails(transaction); + const thumbnail = TransactionUtils.hasReceipt(transaction) ? ReceiptUtils.getThumbnailAndImageURIs(transaction).thumbnail : null; + const {amount: transactionAmount, currency: transactionCurrency, merchant: transactionMerchant, created: transactionDate} = ReportUtils.getTransactionDetails(transaction) ?? {}; const formattedTransactionAmount = CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency); - const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || ''); - const waypoints = lodashGet(transaction, 'comment.waypoints', {}); - const sortedWaypoints = useMemo( + const thumbnailSource = tryResolveUrlFromApiRoot((thumbnail as string) || ''); + const waypoints = useMemo(() => transaction?.comment?.waypoints ?? {}, [transaction?.comment?.waypoints]); + const sortedWaypoints = useMemo( () => // The waypoint keys are sometimes out of order - _.chain(waypoints) - .keys() + Object.keys(waypoints) .sort((keyA, keyB) => TransactionUtils.getWaypointIndex(keyA) - TransactionUtils.getWaypointIndex(keyB)) .map((key) => ({[key]: waypoints[key]})) - .reduce((result, obj) => (obj ? _.assign(result, obj) : result), {}) - .value(), + .reduce((result, obj) => (obj ? Object.assign(result, obj) : result), {}), [waypoints], ); return ( @@ -78,24 +70,23 @@ function DistanceEReceipt({transaction}) { {transactionMerchant} - {_.map(sortedWaypoints, (waypoint, key) => { + {Object.entries(sortedWaypoints).map(([key, waypoint]) => { const index = TransactionUtils.getWaypointIndex(key); - let descriptionKey = 'distance.waypointDescription.'; + let descriptionKey: TranslationPaths = 'distance.waypointDescription.stop'; if (index === 0) { - descriptionKey += 'start'; - } else if (index === _.size(waypoints) - 1) { - descriptionKey += 'finish'; - } else { - descriptionKey += 'stop'; + descriptionKey = 'distance.waypointDescription.start'; + } else if (index === Object.keys(waypoints).length - 1) { + descriptionKey = 'distance.waypointDescription.finish'; } + return ( {translate(descriptionKey)} - {waypoint.name && {waypoint.name}} - {waypoint.address && {waypoint.address}} + {!!waypoint?.name && {waypoint.name}} + {!!waypoint?.address && {waypoint.address}} ); })} @@ -108,7 +99,6 @@ function DistanceEReceipt({transaction}) { @@ -120,7 +110,6 @@ function DistanceEReceipt({transaction}) { ); } -export default DistanceEReceipt; DistanceEReceipt.displayName = 'DistanceEReceipt'; -DistanceEReceipt.propTypes = propTypes; -DistanceEReceipt.defaultProps = defaultProps; + +export default DistanceEReceipt; diff --git a/src/components/DistanceRequest/DistanceRequestFooter.js b/src/components/DistanceRequest/DistanceRequestFooter.tsx similarity index 65% rename from src/components/DistanceRequest/DistanceRequestFooter.js rename to src/components/DistanceRequest/DistanceRequestFooter.tsx index cb164917cd57..357074478bd8 100644 --- a/src/components/DistanceRequest/DistanceRequestFooter.js +++ b/src/components/DistanceRequest/DistanceRequestFooter.tsx @@ -1,77 +1,61 @@ -import lodashGet from 'lodash/get'; -import lodashIsNil from 'lodash/isNil'; -import PropTypes from 'prop-types'; import React, {useMemo} from 'react'; +import type {ReactNode} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; +import type {OnyxEntry} from 'react-native-onyx'; import Button from '@components/Button'; import DistanceMapView from '@components/DistanceMapView'; import * as Expensicons from '@components/Icon/Expensicons'; import ImageSVG from '@components/ImageSVG'; -import transactionPropTypes from '@components/transactionPropTypes'; +import type {WayPoint} from '@components/MapView/MapViewTypes'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import * as TransactionUtils from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {MapboxAccessToken} from '@src/types/onyx'; +import type {WaypointCollection} from '@src/types/onyx/Transaction'; +import type Transaction from '@src/types/onyx/Transaction'; +import type IconAsset from '@src/types/utils/IconAsset'; const MAX_WAYPOINTS = 25; -const propTypes = { +type DistanceRequestFooterOnyxProps = { + /** Data about Mapbox token for calling Mapbox API */ + mapboxAccessToken: OnyxEntry; +}; + +type DistanceRequestFooterProps = DistanceRequestFooterOnyxProps & { /** The waypoints for the distance request */ - waypoints: PropTypes.objectOf( - PropTypes.shape({ - lat: PropTypes.number, - lng: PropTypes.number, - address: PropTypes.string, - name: PropTypes.string, - }), - ), + waypoints?: WaypointCollection; /** Function to call when the user wants to add a new waypoint */ - navigateToWaypointEditPage: PropTypes.func.isRequired, - - /** Data about Mapbox token for calling Mapbox API */ - mapboxAccessToken: PropTypes.shape({ - /** Temporary token for Mapbox API */ - token: PropTypes.string, - - /** Time when the token will expire in ISO 8601 */ - expiration: PropTypes.string, - }), + navigateToWaypointEditPage: (index: number) => void; /** The transaction being interacted with */ - transaction: transactionPropTypes, + transaction: OnyxEntry; }; -const defaultProps = { - waypoints: {}, - mapboxAccessToken: { - token: '', - }, - transaction: {}, -}; -function DistanceRequestFooter({waypoints, transaction, mapboxAccessToken, navigateToWaypointEditPage}) { +function DistanceRequestFooter({waypoints, transaction, mapboxAccessToken, navigateToWaypointEditPage}: DistanceRequestFooterProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); - const numberOfWaypoints = _.size(waypoints); - const numberOfFilledWaypoints = _.size(_.filter(waypoints, (waypoint) => !_.isEmpty(waypoint))); + const numberOfWaypoints = Object.keys(waypoints ?? {}).length; + const numberOfFilledWaypoints = Object.values(waypoints ?? {}).filter((waypoint) => Object.keys(waypoint).length).length; const lastWaypointIndex = numberOfWaypoints - 1; const waypointMarkers = useMemo( () => - _.filter( - _.map(waypoints, (waypoint, key) => { - if (!waypoint || lodashIsNil(waypoint.lat) || lodashIsNil(waypoint.lng)) { + Object.entries(waypoints ?? {}) + .map(([key, waypoint]) => { + if (!waypoint?.lat || !waypoint?.lng) { return; } const index = TransactionUtils.getWaypointIndex(key); - let MarkerComponent; + let MarkerComponent: IconAsset; if (index === 0) { MarkerComponent = Expensicons.DotIndicatorUnfilled; } else if (index === lastWaypointIndex) { @@ -82,8 +66,8 @@ function DistanceRequestFooter({waypoints, transaction, mapboxAccessToken, navig return { id: `${waypoint.lng},${waypoint.lat},${index}`, - coordinate: [waypoint.lng, waypoint.lat], - markerComponent: () => ( + coordinate: [waypoint.lng, waypoint.lat] as const, + markerComponent: (): ReactNode => ( ), }; - }), - (waypoint) => waypoint, - ), + }) + .filter((waypoint): waypoint is WayPoint => !!waypoint), [waypoints, lastWaypointIndex, theme.icon], ); @@ -105,7 +88,7 @@ function DistanceRequestFooter({waypoints, transaction, mapboxAccessToken, navig