diff --git a/.github/ISSUE_TEMPLATE/Standard.md b/.github/ISSUE_TEMPLATE/Standard.md index 7d71ac94e667..39d1c38fa56f 100644 --- a/.github/ISSUE_TEMPLATE/Standard.md +++ b/.github/ISSUE_TEMPLATE/Standard.md @@ -34,6 +34,7 @@ Which of our officially supported platforms is this issue occurring on? **Version Number:** **Reproducible in staging?:** **Reproducible in production?:** +**If this was caught during regression testing, add the test name, ID and link from TestRail:** **Email or phone of affected tester (no customers):** **Logs:** https://stackoverflow.com/c/expensify/questions/4856 **Notes/Photos/Videos:** Any additional supporting documentation diff --git a/.github/actions/javascript/authorChecklist/index.js b/.github/actions/javascript/authorChecklist/index.js index ca221c043b50..e972c95f72d9 100644 --- a/.github/actions/javascript/authorChecklist/index.js +++ b/.github/actions/javascript/authorChecklist/index.js @@ -417,7 +417,7 @@ class GithubUtils { // eslint-disable-next-line max-len issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`; // eslint-disable-next-line max-len - issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`; + issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; return issueBody; diff --git a/.github/actions/javascript/awaitStagingDeploys/index.js b/.github/actions/javascript/awaitStagingDeploys/index.js index 911df2ed8deb..e8ca8378422d 100644 --- a/.github/actions/javascript/awaitStagingDeploys/index.js +++ b/.github/actions/javascript/awaitStagingDeploys/index.js @@ -448,7 +448,7 @@ class GithubUtils { // eslint-disable-next-line max-len issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`; // eslint-disable-next-line max-len - issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`; + issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; return issueBody; diff --git a/.github/actions/javascript/checkDeployBlockers/index.js b/.github/actions/javascript/checkDeployBlockers/index.js index 60656b6b3bad..97aa393de8d9 100644 --- a/.github/actions/javascript/checkDeployBlockers/index.js +++ b/.github/actions/javascript/checkDeployBlockers/index.js @@ -418,7 +418,7 @@ class GithubUtils { // eslint-disable-next-line max-len issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`; // eslint-disable-next-line max-len - issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`; + issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; return issueBody; diff --git a/.github/actions/javascript/createOrUpdateStagingDeploy/index.js b/.github/actions/javascript/createOrUpdateStagingDeploy/index.js index 6b5a4269aaa2..05af05a99544 100644 --- a/.github/actions/javascript/createOrUpdateStagingDeploy/index.js +++ b/.github/actions/javascript/createOrUpdateStagingDeploy/index.js @@ -646,7 +646,7 @@ class GithubUtils { // eslint-disable-next-line max-len issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`; // eslint-disable-next-line max-len - issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`; + issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; return issueBody; diff --git a/.github/actions/javascript/getDeployPullRequestList/index.js b/.github/actions/javascript/getDeployPullRequestList/index.js index 2942c761ff95..f85b4dab7098 100644 --- a/.github/actions/javascript/getDeployPullRequestList/index.js +++ b/.github/actions/javascript/getDeployPullRequestList/index.js @@ -579,7 +579,7 @@ class GithubUtils { // eslint-disable-next-line max-len issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`; // eslint-disable-next-line max-len - issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`; + issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; return issueBody; diff --git a/.github/actions/javascript/getPullRequestDetails/index.js b/.github/actions/javascript/getPullRequestDetails/index.js index 9001790a2f72..bcea1f3cfcf5 100644 --- a/.github/actions/javascript/getPullRequestDetails/index.js +++ b/.github/actions/javascript/getPullRequestDetails/index.js @@ -493,7 +493,7 @@ class GithubUtils { // eslint-disable-next-line max-len issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`; // eslint-disable-next-line max-len - issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`; + issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; return issueBody; diff --git a/.github/actions/javascript/getReleaseBody/index.js b/.github/actions/javascript/getReleaseBody/index.js index d97a13e312b3..8abf35ad71b6 100644 --- a/.github/actions/javascript/getReleaseBody/index.js +++ b/.github/actions/javascript/getReleaseBody/index.js @@ -407,7 +407,7 @@ class GithubUtils { // eslint-disable-next-line max-len issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`; // eslint-disable-next-line max-len - issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`; + issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; return issueBody; diff --git a/.github/actions/javascript/isPullRequestMergeable/index.js b/.github/actions/javascript/isPullRequestMergeable/index.js index bc2ba093780a..17045ee9bd73 100644 --- a/.github/actions/javascript/isPullRequestMergeable/index.js +++ b/.github/actions/javascript/isPullRequestMergeable/index.js @@ -439,7 +439,7 @@ class GithubUtils { // eslint-disable-next-line max-len issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`; // eslint-disable-next-line max-len - issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`; + issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; return issueBody; diff --git a/.github/actions/javascript/isStagingDeployLocked/index.js b/.github/actions/javascript/isStagingDeployLocked/index.js index 269d34c04009..afad2e24a68e 100644 --- a/.github/actions/javascript/isStagingDeployLocked/index.js +++ b/.github/actions/javascript/isStagingDeployLocked/index.js @@ -371,7 +371,7 @@ class GithubUtils { // eslint-disable-next-line max-len issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`; // eslint-disable-next-line max-len - issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`; + issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; return issueBody; diff --git a/.github/actions/javascript/markPullRequestsAsDeployed/index.js b/.github/actions/javascript/markPullRequestsAsDeployed/index.js index 85e2903cb3b6..ed302cea44dc 100644 --- a/.github/actions/javascript/markPullRequestsAsDeployed/index.js +++ b/.github/actions/javascript/markPullRequestsAsDeployed/index.js @@ -540,7 +540,7 @@ class GithubUtils { // eslint-disable-next-line max-len issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`; // eslint-disable-next-line max-len - issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`; + issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; return issueBody; diff --git a/.github/actions/javascript/reopenIssueWithComment/index.js b/.github/actions/javascript/reopenIssueWithComment/index.js index 79fbd049fcdb..7ce459811275 100644 --- a/.github/actions/javascript/reopenIssueWithComment/index.js +++ b/.github/actions/javascript/reopenIssueWithComment/index.js @@ -382,7 +382,7 @@ class GithubUtils { // eslint-disable-next-line max-len issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`; // eslint-disable-next-line max-len - issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`; + issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; return issueBody; diff --git a/.github/actions/javascript/reviewerChecklist/index.js b/.github/actions/javascript/reviewerChecklist/index.js index 968159ce7184..41c7e522fe11 100644 --- a/.github/actions/javascript/reviewerChecklist/index.js +++ b/.github/actions/javascript/reviewerChecklist/index.js @@ -451,7 +451,7 @@ class GithubUtils { // eslint-disable-next-line max-len issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`; // eslint-disable-next-line max-len - issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`; + issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; return issueBody; diff --git a/.github/actions/javascript/triggerWorkflowAndWait/index.js b/.github/actions/javascript/triggerWorkflowAndWait/index.js index 5c225c3ee8ee..d785d9a78a72 100644 --- a/.github/actions/javascript/triggerWorkflowAndWait/index.js +++ b/.github/actions/javascript/triggerWorkflowAndWait/index.js @@ -551,7 +551,7 @@ class GithubUtils { // eslint-disable-next-line max-len issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`; // eslint-disable-next-line max-len - issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`; + issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; return issueBody; diff --git a/.github/actions/javascript/verifySignedCommits/index.js b/.github/actions/javascript/verifySignedCommits/index.js index d74280e4beca..48aea0842b79 100644 --- a/.github/actions/javascript/verifySignedCommits/index.js +++ b/.github/actions/javascript/verifySignedCommits/index.js @@ -371,7 +371,7 @@ class GithubUtils { // eslint-disable-next-line max-len issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`; // eslint-disable-next-line max-len - issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`; + issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; return issueBody; diff --git a/.github/libs/GithubUtils.js b/.github/libs/GithubUtils.js index d3f419258f04..7fb44052b51b 100644 --- a/.github/libs/GithubUtils.js +++ b/.github/libs/GithubUtils.js @@ -331,7 +331,7 @@ class GithubUtils { // eslint-disable-next-line max-len issueBody += `\r\n- [${isTimingDashboardChecked ? 'x' : ' '}] I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.`; // eslint-disable-next-line max-len - issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.`; + issueBody += `\r\n- [${isFirebaseChecked ? 'x' : ' '}] I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; return issueBody; diff --git a/.github/workflows/preDeploy.yml b/.github/workflows/preDeploy.yml index b86a94532396..af42e68bdabb 100644 --- a/.github/workflows/preDeploy.yml +++ b/.github/workflows/preDeploy.yml @@ -312,8 +312,13 @@ jobs: cleanup: true - name: Unzip AWS Device Farm results + if: ${{ always() }} run: unzip Customer\ Artifacts.zip + - name: Print AWS Device Farm run results + if: ${{ always() }} + run: cat "./Host_Machine_Files/\$WORKING_DIRECTORY/debug.log" + - name: Set output of AWS Device Farm into GitHub ENV run: | { echo 'OUTPUT<> "$GITHUB_ENV" @@ -332,7 +337,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Check if test failed, if so leave a deploy blocker label - if: ${{ !contains(env.OUTPUT, 'There are no entries') }} + if: ${{ contains(env.OUTPUT, '🔴') }} run: | gh pr edit ${{ steps.getMergedPullRequest.outputs.number }} --add-label 'DeployBlockerCash' gh pr comment ${{ steps.getMergedPullRequest.outputs.number }} -b "@Expensify/mobile-deployers 📣 Please look into this performance regression as it's a deploy blocker." diff --git a/android/app/build.gradle b/android/app/build.gradle index 5b7b7b5c3794..e7ceca59582b 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -156,8 +156,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001024802 - versionName "1.2.48-2" + versionCode 1001025013 + versionName "1.2.50-13" buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() if (isNewArchitectureEnabled()) { diff --git a/android/app/src/main/assets/fonts/ExpensifyNewKansas-Medium.otf b/android/app/src/main/assets/fonts/ExpensifyNewKansas-Medium.otf old mode 100755 new mode 100644 index a7277555fad9..916184cdd50a Binary files a/android/app/src/main/assets/fonts/ExpensifyNewKansas-Medium.otf and b/android/app/src/main/assets/fonts/ExpensifyNewKansas-Medium.otf differ diff --git a/android/app/src/main/assets/fonts/ExpensifyNewKansas-MediumItalic.otf b/android/app/src/main/assets/fonts/ExpensifyNewKansas-MediumItalic.otf old mode 100755 new mode 100644 index 984ef4bfa2d4..4bc82a0f4141 Binary files a/android/app/src/main/assets/fonts/ExpensifyNewKansas-MediumItalic.otf and b/android/app/src/main/assets/fonts/ExpensifyNewKansas-MediumItalic.otf differ diff --git a/android/link-assets-manifest.json b/android/link-assets-manifest.json index 300d198bd626..3b9e10ad3b7f 100644 --- a/android/link-assets-manifest.json +++ b/android/link-assets-manifest.json @@ -27,11 +27,11 @@ }, { "path": "assets/fonts/native/ExpensifyNewKansas-Medium.otf", - "sha1": "b07181fac8d0602d4b18c339bdbef9589c67c9f9" + "sha1": "6c670679ccb67975edba5e8dd59dc883ff402b25" }, { "path": "assets/fonts/native/ExpensifyNewKansas-MediumItalic.otf", - "sha1": "00636ab2038c9690a123985b70a944c42cf65b8a" + "sha1": "42b8a320808e1f0aed6b65fd06edd51f6cf5515c" } ] } diff --git a/assets/css/fonts.css b/assets/css/fonts.css index 6e48be0e2b56..7834a0ebb861 100644 --- a/assets/css/fonts.css +++ b/assets/css/fonts.css @@ -40,6 +40,20 @@ src: url('/fonts/ExpensifyMono-Bold.woff2') format('woff2'), url('/fonts/ExpensifyMono-Bold.woff') format('woff'); } +@font-face { + font-family: ExpensifyNewKansas-Medium; + font-weight: 500; + font-style: normal; + src: url('/fonts/ExpensifyNewKansas-Medium.woff2') format('woff2'), url('/fonts/ExpensifyNewKansas-Medium.woff') format('woff'); +} + +@font-face { + font-family: ExpensifyNewKansas-MediumItalic; + font-weight: 500; + font-style: italic; + src: url('/fonts/ExpensifyNewKansas-MediumItalic.woff2') format('woff2'), url('/fonts/ExpensifyNewKansas-MediumItalic.woff') format('woff'); +} + * { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; diff --git a/assets/fonts/native/ExpensifyNewKansas-Medium.otf b/assets/fonts/native/ExpensifyNewKansas-Medium.otf old mode 100755 new mode 100644 index a7277555fad9..916184cdd50a Binary files a/assets/fonts/native/ExpensifyNewKansas-Medium.otf and b/assets/fonts/native/ExpensifyNewKansas-Medium.otf differ diff --git a/assets/fonts/native/ExpensifyNewKansas-MediumItalic.otf b/assets/fonts/native/ExpensifyNewKansas-MediumItalic.otf old mode 100755 new mode 100644 index 984ef4bfa2d4..4bc82a0f4141 Binary files a/assets/fonts/native/ExpensifyNewKansas-MediumItalic.otf and b/assets/fonts/native/ExpensifyNewKansas-MediumItalic.otf differ diff --git a/assets/fonts/web/ExpensifyNewKansas-Medium.otf b/assets/fonts/web/ExpensifyNewKansas-Medium.otf deleted file mode 100755 index a7277555fad9..000000000000 Binary files a/assets/fonts/web/ExpensifyNewKansas-Medium.otf and /dev/null differ diff --git a/assets/fonts/web/ExpensifyNewKansas-Medium.woff b/assets/fonts/web/ExpensifyNewKansas-Medium.woff new file mode 100644 index 000000000000..bd842c5ecb1d Binary files /dev/null and b/assets/fonts/web/ExpensifyNewKansas-Medium.woff differ diff --git a/assets/fonts/web/ExpensifyNewKansas-Medium.woff2 b/assets/fonts/web/ExpensifyNewKansas-Medium.woff2 new file mode 100644 index 000000000000..dba1df7e971e Binary files /dev/null and b/assets/fonts/web/ExpensifyNewKansas-Medium.woff2 differ diff --git a/assets/fonts/web/ExpensifyNewKansas-MediumItalic.otf b/assets/fonts/web/ExpensifyNewKansas-MediumItalic.otf deleted file mode 100755 index 984ef4bfa2d4..000000000000 Binary files a/assets/fonts/web/ExpensifyNewKansas-MediumItalic.otf and /dev/null differ diff --git a/assets/fonts/web/ExpensifyNewKansas-MediumItalic.woff b/assets/fonts/web/ExpensifyNewKansas-MediumItalic.woff new file mode 100644 index 000000000000..d3e7d9e82e15 Binary files /dev/null and b/assets/fonts/web/ExpensifyNewKansas-MediumItalic.woff differ diff --git a/assets/fonts/web/ExpensifyNewKansas-MediumItalic.woff2 b/assets/fonts/web/ExpensifyNewKansas-MediumItalic.woff2 new file mode 100644 index 000000000000..94a0e04fa3b2 Binary files /dev/null and b/assets/fonts/web/ExpensifyNewKansas-MediumItalic.woff2 differ diff --git a/config/webpack/webpack.common.js b/config/webpack/webpack.common.js index ec4f4656fddc..4d98e4d83c90 100644 --- a/config/webpack/webpack.common.js +++ b/config/webpack/webpack.common.js @@ -189,7 +189,7 @@ const webpackConfig = ({envFile = '.env', platform = 'web'}) => ({ use: ['style-loader', 'css-loader'], }, { - test: /\.(woff|woff2|otf)$/i, + test: /\.(woff|woff2)$/i, type: 'asset', }, { diff --git a/contributingGuides/CONTRIBUTING.md b/contributingGuides/CONTRIBUTING.md index e952613f1c9f..3df7637a0fca 100644 --- a/contributingGuides/CONTRIBUTING.md +++ b/contributingGuides/CONTRIBUTING.md @@ -33,7 +33,7 @@ All contributors should be a member of **two** Slack channels: 1. #expensify-open-source -- used to ask **general questions**, facilitate **discussions**, and make **feature requests**. 2. #expensify-bugs -- used to discuss or report **bugs** specifically. -To request an invite to these two Slack channels, just email contributors@expensify.com with the subject `Slack Channel Invites` and **include a link to your Upwork profile**. We'll send you an invite! +Before requesting an invite to Slack please ensure your Upwork account is active, since we only pay via Upwork (see [below](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#payment-for-contributions)). To request an invite to these two Slack channels, email contributors@expensify.com with the subject `Slack Channel Invites`. We'll send you an invite! Note: the Expensify team will not be able to respond to direct messages in Slack. @@ -110,7 +110,6 @@ Additionally if you want to discuss an idea with the open source community witho #### Propose a solution for the job 4. After you reproduce the issue, make a proposal for your solution and post it as a comment in the corresponding GitHub issue (linked in the Upwork job). Your solution proposal should include a brief written technical explanation of the changes you will make. Include "Proposal" as the first word in your comment. - - Note: Issues that have not had the `External` label applied have not yet been approved for implementation. This means, if you propose a solution to an issue without the `External` label (which you are allowed to do) it is possible that the issue will be fixed internally. If the `External` label has not yet been applied, Expensify has the right to use your proposal to fix said issue, without providing compensation for your solution. This process covers the very rare instance where we need or want to fix an issue internally. - Note: Before submitting a proposal on an issue, be sure to read any other existing proposals. Any new proposal should be substantively different from existing proposals. 5. Pause at this step until someone from the Contributor-Plus team and / or someone from Expensify provides feedback on your proposal (do not create a pull request yet). 6. If your solution proposal is accepted by the Expensify engineer assigned to the issue, Expensify will hire you on Upwork and assign the GitHub issue to you. diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index c51a81d0b60d..de66a3dba148 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -253,6 +253,7 @@ GEM zeitwerk (2.6.0) PLATFORMS + ruby x86_64-darwin-20 x86_64-darwin-21 diff --git a/docs/_config.yml b/docs/_config.yml index fdf6a696e6c7..7c43ba2115e3 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -12,7 +12,7 @@ defaults: values: layout: "default" -exclude: [README.md, TEMPLATE.md] +exclude: [README.md, TEMPLATE.md, vendor] plugins: - jekyll-seo-tag diff --git a/docs/_data/routes.yml b/docs/_data/routes.yml index 5a2956f1b3d2..9a50200a9576 100644 --- a/docs/_data/routes.yml +++ b/docs/_data/routes.yml @@ -8,7 +8,7 @@ hubs: - href: send-money title: Send money description: With only a couple of clicks, send money to your friends or coworkers. - icon: /assets/images/send.svg + icon: /assets/images/paper-airplane.svg sections: - href: workspaces title: Workspaces @@ -23,7 +23,7 @@ hubs: - href: request-money title: Request money - icon: /assets/images/money-circle.svg + icon: /assets/images/money-case.svg description: Request money for work expenses, bills, or a night out with friends. articles: - href: Request-and-Send-Money @@ -32,7 +32,7 @@ hubs: - href: other title: Other description: Everything else you're looking for is right here. - icon: /assets/images/users.svg + icon: /assets/images/lightbulb.svg articles: - href: Your-Expensify-Account-Manager title: Your Expensify Account Manager diff --git a/docs/_layouts/default.html b/docs/_layouts/default.html index ad40909a805d..37a6242a409b 100644 --- a/docs/_layouts/default.html +++ b/docs/_layouts/default.html @@ -4,7 +4,7 @@ Expensify Help - + diff --git a/docs/_sass/_colors.scss b/docs/_sass/_colors.scss index 1b046f3c6e64..07ccae1c8be1 100644 --- a/docs/_sass/_colors.scss +++ b/docs/_sass/_colors.scss @@ -1,5 +1,3 @@ -$color-blue: #0185FF; -$color-blueHover: #006FD6; $color-dark: #0B1B34; $color-gray1: #FAFAFA; $color-gray2: #ECECEC; @@ -7,3 +5,10 @@ $color-gray3: #C6C9CA; $color-green: #07d973; $color-pink: #F68DFE; $color-white: #FFFFFF; + +$color-light-grey-green: #C9D3C5; +$color-gray-green: #8B9C8F; +$color-dark-green: #1B5744; +$color-darker-green: #002E22; +$color-super-dark-green: #061B09; +$color-light-blue: #8DC8FF; diff --git a/docs/_sass/_fonts.scss b/docs/_sass/_fonts.scss index ad6eaf466609..47c89c48b7a0 100644 --- a/docs/_sass/_fonts.scss +++ b/docs/_sass/_fonts.scss @@ -1,92 +1,15 @@ @font-face { - font-family: GTAmericaExp-Regular; - font-weight: 100; - font-style: normal; - src: url("https://www.expensify.com/font/GT-America-Standard-Light.eot") format("embedded-opentype"), url("https://www.expensify.com/font/GT-America-Standard-Light.woff") format("woff"), url("https://www.expensify.com/font/GT-America-Standard-Light.woff2") format("woff2"); -} - -@font-face { - font-family: GTAmericaExp-Regular; - font-weight: 200; - font-style: normal; - src: url("https://www.expensify.com/font/GT-America-Standard-Thin.eot") format("embedded-opentype"), url("https://www.expensify.com/font/GT-America-Standard-Thin.woff") format("woff"), url("https://www.expensify.com/font/GT-America-Standard-Thin.woff2") format("woff2"); -} - -@font-face { - font-family: GTAmericaExp-Regular; - font-weight: 400; - font-style: normal; - src: url("https://www.expensify.com/font/GT-America-Standard-Regular.eot") format("embedded-opentype"), url("https://www.expensify.com/font/GT-America-Standard-Regular.woff") format("woff"), url("https://www.expensify.com/font/GT-America-Standard-Regular.woff2") format("woff2"); -} - -@font-face { - font-family: GTAmericaExp-Regular; - font-weight: 500; - font-style: normal; - src: url("https://www.expensify.com/font/GT-America-Standard-Medium.eot") format("embedded-opentype"), url("https://www.expensify.com/font/GT-America-Standard-Medium.woff") format("woff"), url("https://www.expensify.com/font/GT-America-Standard-Medium.woff2") format("woff2"); -} - -@font-face { - font-family: GTAmericaExp-Regular; - font-weight: 600; - font-style: normal; - src: url("https://www.expensify.com/font/GT-America-Standard-Medium.eot") format("embedded-opentype"), url("https://www.expensify.com/font/GT-America-Standard-Medium.woff") format("woff"), url("https://www.expensify.com/font/GT-America-Standard-Medium.woff2") format("woff2"); -} - -@font-face { - font-family: GTAmericaExp-Regular; - font-weight: 700; - font-style: normal; - src: url("https://www.expensify.com/font/GT-America-Standard-Bold.eot") format("embedded-opentype"), url("https://www.expensify.com/font/GT-America-Standard-Bold.woff") format("woff"), url("https://www.expensify.com/font/GT-America-Standard-Bold.woff2") format("woff2"); -} - -@font-face { - font-family: GTAmericaExp-RgIt; + font-family: ExpensifyNeue; font-weight: 400; - font-style: italic; - src: url("https://www.expensify.com/font/GT-America-Standard-Regular-Italic.eot") format("embedded-opentype"), url("https://www.expensify.com/font/GT-America-Standard-Regular-Italic.woff") format("woff"), url("https://www.expensify.com/font/GT-America-Standard-Regular-Italic.woff2") format("woff2"); -} - -@font-face { - font-family: GTAmericaExp-MdIt; - font-weight: 500; - font-style: italic; - src: url("https://www.expensify.com/font/GT-America-Standard-Medium-Italic.eot") format("embedded-opentype"), url("https://www.expensify.com/font/GT-America-Standard-Medium-Italic.woff") format("woff"), url("https://www.expensify.com/font/GT-America-Standard-Medium-Italic.woff2") format("woff2"); -} - -@font-face { - font-family: GTAmericaExp-BdIt; - font-weight: 700; - font-style: italic; - src: url("https://www.expensify.com/font/GT-America-Standard-Bold-Italic.eot") format("embedded-opentype"), url("https://www.expensify.com/font/GT-America-Standard-Bold-Italic.woff") format("woff"), url("https://www.expensify.com/font/GT-America-Standard-Bold-Italic.woff2") format("woff2"); -} - -@font-face { - font-family: GTAmericaExpMono-Rg; - font-weight: 400; - font-style: normal; - src: url('https://www.expensify.com/font/GT-America-Exp-Mono-Regular.eot') format('embedded-opentype'), url('https://www.expensify.com/font/GT-America-Exp-Mono-Regular.woff') format('woff'), url('https://www.expensify.com/font/GT-America-Exp-Mono-Regular.woff2') format('woff2'); -} - -@font-face { - font-family: GTAmericaExpMono-RgIt; - font-weight: 400; - font-style: italic; - src: url('https://www.expensify.com/font/GT-America-Exp-Mono-Regular-Italic.eot') format('embedded-opentype'), url('https://www.expensify.com/font/GT-America-Exp-Mono-Regular-Italic.woff') format('woff'), url('https://www.expensify.com/font/GT-America-Exp-Mono-Regular-Italic.woff2') format('woff2'); -} - -@font-face { - font-family: GTAmericaExpMono-Bd; - font-weight: 700; font-style: normal; - src: url('https://www.expensify.com/font/GT-America-Exp-Mono-Bold.eot') format('embedded-opentype'), url('https://www.expensify.com/font/GT-America-Exp-Mono-Bold.woff') format('woff'), url('https://www.expensify.com/font/GT-America-Exp-Mono-Bold.woff2') format('woff2'); + src: url('/assets/fonts/ExpensifyNeue-Regular.woff2') format('woff2'), url('/assets/fonts/ExpensifyNeue-Regular.woff') format('woff'); } @font-face { - font-family: GTAmericaExpMono-BdIt; + font-family: ExpensifyNeue; font-weight: 700; - font-style: italic; - src: url('https://www.expensify.com/font/GT-America-Exp-Mono-Bold-Italic.eot') format('embedded-opentype'), url('https://www.expensify.com/font/GT-America-Exp-Mono-Bold-Italic.woff') format('woff'), url('https://www.expensify.com/font/GT-America-Exp-Mono-Bold-Italic.woff2') format('woff2'); + font-style: bold; + src: url('/assets/fonts/ExpensifyNeue-Bold.woff2') format('woff2'), url('/assets/fonts/ExpensifyNeue-Bold.woff') format('woff'); } * { diff --git a/docs/_sass/_main.scss b/docs/_sass/_main.scss index 87ce5c717e87..d1d71d2fca32 100644 --- a/docs/_sass/_main.scss +++ b/docs/_sass/_main.scss @@ -63,11 +63,11 @@ html, body { height: 100%; min-height: 100%; - background: $color-white; + background: $color-darker-green; } hr { - background: $color-blue; + background: $color-light-blue; border: none; display: inline-block; width: 24px; @@ -84,13 +84,9 @@ em { } a { - color: $color-blue; + color: $color-light-blue; text-decoration: none; - &:hover { - color: $color-blueHover; - } - img { display: block; } @@ -102,7 +98,7 @@ h3, h4, h5, h6 { - color: $color-dark; + color: $color-white; font-weight: bold; padding-bottom: 12px; } @@ -134,15 +130,15 @@ select, textarea { line-height: 1.4; font-weight: 400; - font-family: "GTAmericaExp-Regular", "Helvetica Neue", "Helvetica", Arial, sans-serif; + font-family: "ExpensifyNeue", "Helvetica Neue", "Helvetica", Arial, sans-serif; font-size: 16px; - color: $color-dark; + color: $color-white; } button { border-radius: 12px; padding: 12px; - font-family: "GTAmericaExp-Bold", "Helvetica Neue", "Helvetica", Arial, sans-serif; + font-family: "ExpensifyNeue", "Helvetica Neue", "Helvetica", Arial, sans-serif; font-size: 15px; font-weight: bold; @@ -150,6 +146,9 @@ button { background-color: $color-green; color: $color-white; width: 100%; + border-radius: 100px; + padding-left: 20px; + padding-right: 20px; &:hover { background-color: desaturate($color-green, 15%); @@ -173,9 +172,9 @@ button { #lhn { position: fixed; - background-color: $color-gray1; + background-color: $color-darker-green; box-sizing: border-box; - border-right-color: $color-gray2; + border-right-color: $color-dark-green; border-right-width: 1px; border-style: solid; width: 100%; @@ -273,7 +272,7 @@ button { .selected { cursor: auto; font-weight: bold; - color: $color-dark; + color: $color-white; } .hide { @@ -285,6 +284,7 @@ button { #content-area { display: flex; flex-direction: column; + background-color: $color-super-dark-green; min-height: 100vh; margin-left: 0; padding: 80px 24px 24px 24px; @@ -301,10 +301,10 @@ button { } @include breakpoint($breakpoint-wide) { - /* Center content area for bigger screens */ - margin-left: calc(420px + (100vw - 1000px - 420px)/2); - padding: 52px 0; - max-width: 1000px; + margin-left: 420px; + /* On wide screens, the padding needs to be equal to + the view width, minus the content size, minus the lhn size, divided by two. */ + padding: 52px calc((100vw - 1000px - 420px)/2); } ul, @@ -350,12 +350,8 @@ button { .link { display: inline; - color: $color-blue; + color: $color-light-blue; cursor: pointer; - - &:hover { - color: $color-blueHover; - } } .lhn-items { @@ -383,7 +379,7 @@ button { .selected-article { font-weight: bold; - color: $color-dark; + color: $color-white; } .home-link { @@ -408,13 +404,13 @@ button { flex-wrap: wrap; border-radius: 16px; padding: 28px; - font-weight: bold; + font-weight: 700; cursor: pointer; - color: $color-dark; - background-color: $color-gray1; + color: $color-white; + background-color: $color-darker-green; &:hover { - background-color: darken($color-gray1, 1%); + background-color: darken($color-darker-green, 1%); } .row { @@ -428,7 +424,7 @@ button { padding-right: 28px; img { - width: 32px; + width: 64px; } } @@ -484,7 +480,7 @@ button { } .icon { - color: $color-gray3; + color: $color-gray-green; font-size: larger; display: inline; diff --git a/docs/assets/fonts/ExpensifyNeue-Bold.woff b/docs/assets/fonts/ExpensifyNeue-Bold.woff new file mode 100755 index 000000000000..387c232dffcc Binary files /dev/null and b/docs/assets/fonts/ExpensifyNeue-Bold.woff differ diff --git a/docs/assets/fonts/ExpensifyNeue-Bold.woff2 b/docs/assets/fonts/ExpensifyNeue-Bold.woff2 new file mode 100755 index 000000000000..36ba41100e56 Binary files /dev/null and b/docs/assets/fonts/ExpensifyNeue-Bold.woff2 differ diff --git a/docs/assets/fonts/ExpensifyNeue-Regular.woff b/docs/assets/fonts/ExpensifyNeue-Regular.woff new file mode 100755 index 000000000000..a8a311d59e77 Binary files /dev/null and b/docs/assets/fonts/ExpensifyNeue-Regular.woff differ diff --git a/docs/assets/fonts/ExpensifyNeue-Regular.woff2 b/docs/assets/fonts/ExpensifyNeue-Regular.woff2 new file mode 100755 index 000000000000..cdec638e361b Binary files /dev/null and b/docs/assets/fonts/ExpensifyNeue-Regular.woff2 differ diff --git a/docs/assets/images/expensify-help.svg b/docs/assets/images/expensify-help.svg index f81c561eb17a..7aa084e0fc0c 100644 --- a/docs/assets/images/expensify-help.svg +++ b/docs/assets/images/expensify-help.svg @@ -1,35 +1,51 @@ - + + viewBox="0 0 189 30" style="enable-background:new 0 0 189 30;" xml:space="preserve"> - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + diff --git a/docs/assets/images/expensify-logo-round.png b/docs/assets/images/expensify-logo-round.png new file mode 100644 index 000000000000..59d29ed09530 Binary files /dev/null and b/docs/assets/images/expensify-logo-round.png differ diff --git a/docs/assets/images/lightbulb.svg b/docs/assets/images/lightbulb.svg new file mode 100644 index 000000000000..a704c9731b9c --- /dev/null +++ b/docs/assets/images/lightbulb.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/docs/assets/images/money-case.svg b/docs/assets/images/money-case.svg new file mode 100644 index 000000000000..bc2202d7fa3e --- /dev/null +++ b/docs/assets/images/money-case.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/assets/images/paper-airplane.svg b/docs/assets/images/paper-airplane.svg new file mode 100644 index 000000000000..3eb5ef96d5b9 --- /dev/null +++ b/docs/assets/images/paper-airplane.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fastlane/Fastfile b/fastlane/Fastfile index e60b41583cb8..70f3cf223647 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -18,6 +18,7 @@ platform :android do lane :build_e2e do ENV["ENVFILE"]="tests/e2e/.env.e2e" ENV["ENTRY_FILE"]="src/libs/E2E/reactNativeLaunchingTest.js" + ENV["E2E_TESTING"]="true" gradle( project_dir: './android', @@ -52,8 +53,8 @@ platform :android do bucket: ENV['S3_BUCKET'], region: ENV['S3_REGION'], - apk: lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH], - app_directory: "android/#{ENV['PULL_REQUEST_NUMBER']}", + apk: lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH], + app_directory: "android/#{ENV['PULL_REQUEST_NUMBER']}", ) sh("echo '{\"apk_path\": \"#{lane_context[SharedValues::S3_APK_OUTPUT_PATH]}\",\"html_path\": \"#{lane_context[SharedValues::S3_HTML_OUTPUT_PATH]}\"}' > ../android_paths.json") diff --git a/ios/NewExpensify.xcodeproj/project.pbxproj b/ios/NewExpensify.xcodeproj/project.pbxproj index 390ba358e87d..b3dab9cee754 100644 --- a/ios/NewExpensify.xcodeproj/project.pbxproj +++ b/ios/NewExpensify.xcodeproj/project.pbxproj @@ -17,23 +17,23 @@ 0F5E5351263B73FD004CA14F /* EnvironmentChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F5E534F263B73FD004CA14F /* EnvironmentChecker.m */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 18D050E0262400AF000D658B /* BridgingFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18D050DF262400AF000D658B /* BridgingFile.swift */; }; + 1D9EC75069BF4FDB88DF2BA7 /* ExpensifyMono-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 1B3F09A4E4EA4CFFA5E4E7CD /* ExpensifyMono-Regular.otf */; }; 374FB8D728A133FE000D84EF /* OriginImageRequestHandler.mm in Sources */ = {isa = PBXBuildFile; fileRef = 374FB8D628A133FE000D84EF /* OriginImageRequestHandler.mm */; }; + 5E272EE622CC4C768F4EC64D /* ExpensifyNeue-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 6BEDED270C49437581EBE50D /* ExpensifyNeue-Regular.otf */; }; 5E2EE3856BE7BC98F354B110 /* libPods-NewExpensify.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3EDF186626B8D2CBA203E08D /* libPods-NewExpensify.a */; }; + 63D866D568A14A61AD27E20A /* ExpensifyNeue-Italic.otf in Resources */ = {isa = PBXBuildFile; fileRef = 1977066010294D51AEB35F3B /* ExpensifyNeue-Italic.otf */; }; 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 */; }; + 9316CFCA15184920AE24E3CC /* ExpensifyMono-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 177D06D4BF2346EB90E37D3D /* ExpensifyMono-Bold.otf */; }; + 95C1920F4FFC403E9C16013C /* ExpensifyNeue-BoldItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = 38E61473EAA34C598CB6B345 /* ExpensifyNeue-BoldItalic.otf */; }; + A5158169CD604A77A22AE02A /* ExpensifyNeue-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = EDFC169F9D7A43BDB924151F /* ExpensifyNeue-Bold.otf */; }; + BDB853621F354EBB84E619C2 /* ExpensifyNewKansas-MediumItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = D2AFB39EC1D44BF9B91D3227 /* ExpensifyNewKansas-MediumItalic.otf */; }; DD79042B2792E76D004484B4 /* RCTBootSplash.m in Sources */ = {isa = PBXBuildFile; fileRef = DD79042A2792E76D004484B4 /* RCTBootSplash.m */; }; E3B48DC1ED4CC64F660388A8 /* libPods-NewExpensify-NewExpensifyTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A2695CF895D81C31AFB4A074 /* libPods-NewExpensify-NewExpensifyTests.a */; }; E9DF872D2525201700607FDC /* AirshipConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9DF872C2525201700607FDC /* AirshipConfig.plist */; }; F0C450EA2705020500FD2970 /* colors.json in Resources */ = {isa = PBXBuildFile; fileRef = F0C450E92705020500FD2970 /* colors.json */; }; - 9316CFCA15184920AE24E3CC /* ExpensifyMono-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 177D06D4BF2346EB90E37D3D /* ExpensifyMono-Bold.otf */; }; - 1D9EC75069BF4FDB88DF2BA7 /* ExpensifyMono-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 1B3F09A4E4EA4CFFA5E4E7CD /* ExpensifyMono-Regular.otf */; }; - A5158169CD604A77A22AE02A /* ExpensifyNeue-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = EDFC169F9D7A43BDB924151F /* ExpensifyNeue-Bold.otf */; }; - 95C1920F4FFC403E9C16013C /* ExpensifyNeue-BoldItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = 38E61473EAA34C598CB6B345 /* ExpensifyNeue-BoldItalic.otf */; }; - 63D866D568A14A61AD27E20A /* ExpensifyNeue-Italic.otf in Resources */ = {isa = PBXBuildFile; fileRef = 1977066010294D51AEB35F3B /* ExpensifyNeue-Italic.otf */; }; - 5E272EE622CC4C768F4EC64D /* ExpensifyNeue-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 6BEDED270C49437581EBE50D /* ExpensifyNeue-Regular.otf */; }; - 4FB4804CA0154A0382C7EAE1 /* ExpensifyNewKansas-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = 2DEF941BCD3B464CA78A8188 /* ExpensifyNewKansas-Medium.otf */; }; - 07C7A32C41FC466C85598ADD /* ExpensifyNewKansas-MediumItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = 71272DD3D80F4E56884B87CE /* ExpensifyNewKansas-MediumItalic.otf */; }; + FF941A8D48F849269AB85C9A /* ExpensifyNewKansas-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = 44BF435285B94E5B95F90994 /* ExpensifyNewKansas-Medium.otf */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -63,29 +63,29 @@ 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 = ""; }; + 177D06D4BF2346EB90E37D3D /* ExpensifyMono-Bold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "ExpensifyMono-Bold.otf"; path = "../assets/fonts/native/ExpensifyMono-Bold.otf"; sourceTree = ""; }; 18D050DF262400AF000D658B /* BridgingFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BridgingFile.swift; sourceTree = ""; }; + 1977066010294D51AEB35F3B /* ExpensifyNeue-Italic.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "ExpensifyNeue-Italic.otf"; path = "../assets/fonts/native/ExpensifyNeue-Italic.otf"; sourceTree = ""; }; + 1B3F09A4E4EA4CFFA5E4E7CD /* ExpensifyMono-Regular.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "ExpensifyMono-Regular.otf"; path = "../assets/fonts/native/ExpensifyMono-Regular.otf"; 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 = ""; }; + 38E61473EAA34C598CB6B345 /* ExpensifyNeue-BoldItalic.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "ExpensifyNeue-BoldItalic.otf"; path = "../assets/fonts/native/ExpensifyNeue-BoldItalic.otf"; sourceTree = ""; }; 3EDF186626B8D2CBA203E08D /* libPods-NewExpensify.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-NewExpensify.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 44BF435285B94E5B95F90994 /* ExpensifyNewKansas-Medium.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "ExpensifyNewKansas-Medium.otf"; path = "../assets/fonts/native/ExpensifyNewKansas-Medium.otf"; sourceTree = ""; }; + 6BEDED270C49437581EBE50D /* ExpensifyNeue-Regular.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "ExpensifyNeue-Regular.otf"; path = "../assets/fonts/native/ExpensifyNeue-Regular.otf"; 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 = ""; }; A2695CF895D81C31AFB4A074 /* libPods-NewExpensify-NewExpensifyTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-NewExpensify-NewExpensifyTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; B66D52EC75F78B8A06F1E035 /* Pods-NewExpensify.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify.debug.xcconfig"; path = "Target Support Files/Pods-NewExpensify/Pods-NewExpensify.debug.xcconfig"; sourceTree = ""; }; + D2AFB39EC1D44BF9B91D3227 /* ExpensifyNewKansas-MediumItalic.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "ExpensifyNewKansas-MediumItalic.otf"; path = "../assets/fonts/native/ExpensifyNewKansas-MediumItalic.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 = ""; }; E9DF872C2525201700607FDC /* AirshipConfig.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = AirshipConfig.plist; 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; }; + EDFC169F9D7A43BDB924151F /* ExpensifyNeue-Bold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "ExpensifyNeue-Bold.otf"; path = "../assets/fonts/native/ExpensifyNeue-Bold.otf"; sourceTree = ""; }; F0C450E92705020500FD2970 /* colors.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = colors.json; path = ../colors.json; sourceTree = ""; }; - 177D06D4BF2346EB90E37D3D /* ExpensifyMono-Bold.otf */ = {isa = PBXFileReference; name = "ExpensifyMono-Bold.otf"; path = "../assets/fonts/native/ExpensifyMono-Bold.otf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 1B3F09A4E4EA4CFFA5E4E7CD /* ExpensifyMono-Regular.otf */ = {isa = PBXFileReference; name = "ExpensifyMono-Regular.otf"; path = "../assets/fonts/native/ExpensifyMono-Regular.otf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - EDFC169F9D7A43BDB924151F /* ExpensifyNeue-Bold.otf */ = {isa = PBXFileReference; name = "ExpensifyNeue-Bold.otf"; path = "../assets/fonts/native/ExpensifyNeue-Bold.otf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 38E61473EAA34C598CB6B345 /* ExpensifyNeue-BoldItalic.otf */ = {isa = PBXFileReference; name = "ExpensifyNeue-BoldItalic.otf"; path = "../assets/fonts/native/ExpensifyNeue-BoldItalic.otf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 1977066010294D51AEB35F3B /* ExpensifyNeue-Italic.otf */ = {isa = PBXFileReference; name = "ExpensifyNeue-Italic.otf"; path = "../assets/fonts/native/ExpensifyNeue-Italic.otf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 6BEDED270C49437581EBE50D /* ExpensifyNeue-Regular.otf */ = {isa = PBXFileReference; name = "ExpensifyNeue-Regular.otf"; path = "../assets/fonts/native/ExpensifyNeue-Regular.otf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 2DEF941BCD3B464CA78A8188 /* ExpensifyNewKansas-Medium.otf */ = {isa = PBXFileReference; name = "ExpensifyNewKansas-Medium.otf"; path = "../assets/fonts/native/ExpensifyNewKansas-Medium.otf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 71272DD3D80F4E56884B87CE /* ExpensifyNewKansas-MediumItalic.otf */ = {isa = PBXFileReference; name = "ExpensifyNewKansas-MediumItalic.otf"; path = "../assets/fonts/native/ExpensifyNewKansas-MediumItalic.otf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -203,8 +203,8 @@ 38E61473EAA34C598CB6B345 /* ExpensifyNeue-BoldItalic.otf */, 1977066010294D51AEB35F3B /* ExpensifyNeue-Italic.otf */, 6BEDED270C49437581EBE50D /* ExpensifyNeue-Regular.otf */, - 2DEF941BCD3B464CA78A8188 /* ExpensifyNewKansas-Medium.otf */, - 71272DD3D80F4E56884B87CE /* ExpensifyNewKansas-MediumItalic.otf */, + 44BF435285B94E5B95F90994 /* ExpensifyNewKansas-Medium.otf */, + D2AFB39EC1D44BF9B91D3227 /* ExpensifyNewKansas-MediumItalic.otf */, ); name = Resources; sourceTree = ""; @@ -331,8 +331,8 @@ 95C1920F4FFC403E9C16013C /* ExpensifyNeue-BoldItalic.otf in Resources */, 63D866D568A14A61AD27E20A /* ExpensifyNeue-Italic.otf in Resources */, 5E272EE622CC4C768F4EC64D /* ExpensifyNeue-Regular.otf in Resources */, - 4FB4804CA0154A0382C7EAE1 /* ExpensifyNewKansas-Medium.otf in Resources */, - 07C7A32C41FC466C85598ADD /* ExpensifyNewKansas-MediumItalic.otf in Resources */, + FF941A8D48F849269AB85C9A /* ExpensifyNewKansas-Medium.otf in Resources */, + BDB853621F354EBB84E619C2 /* ExpensifyNewKansas-MediumItalic.otf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index ec08c61314c0..761b5b93ee43 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.2.48 + 1.2.50 CFBundleSignature ???? CFBundleURLTypes @@ -30,7 +30,7 @@ CFBundleVersion - 1.2.48.2 + 1.2.50.13 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes @@ -77,7 +77,6 @@ Your photos are used to create chat attachments. UIAppFonts - ExpensifyNewKansas-Meduim.otf ExpensifyMono-Bold.otf ExpensifyMono-Regular.otf ExpensifyNeue-Bold.otf diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index f9f00671b7b0..d621f8a5443e 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.2.48 + 1.2.50 CFBundleSignature ???? CFBundleVersion - 1.2.48.2 + 1.2.50.13 diff --git a/ios/link-assets-manifest.json b/ios/link-assets-manifest.json index 300d198bd626..3b9e10ad3b7f 100644 --- a/ios/link-assets-manifest.json +++ b/ios/link-assets-manifest.json @@ -27,11 +27,11 @@ }, { "path": "assets/fonts/native/ExpensifyNewKansas-Medium.otf", - "sha1": "b07181fac8d0602d4b18c339bdbef9589c67c9f9" + "sha1": "6c670679ccb67975edba5e8dd59dc883ff402b25" }, { "path": "assets/fonts/native/ExpensifyNewKansas-MediumItalic.otf", - "sha1": "00636ab2038c9690a123985b70a944c42cf65b8a" + "sha1": "42b8a320808e1f0aed6b65fd06edd51f6cf5515c" } ] } diff --git a/package-lock.json b/package-lock.json index 14fd8b48654b..4bfaec2b2432 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.2.48-2", + "version": "1.2.50-13", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.2.48-2", + "version": "1.2.50-13", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -38,7 +38,7 @@ "babel-polyfill": "^6.26.0", "dom-serializer": "^0.2.2", "domhandler": "^4.3.0", - "expensify-common": "git+https://github.com/Expensify/expensify-common.git#e67235baa887dcbe9dc4bf41ddf1925f19a1e8ad", + "expensify-common": "git+https://github.com/Expensify/expensify-common.git#55c208315e04246b548a4009d35466c100d9de7c", "fbjs": "^3.0.2", "file-loader": "^6.0.0", "html-entities": "^1.3.1", @@ -23874,8 +23874,8 @@ }, "node_modules/expensify-common": { "version": "1.0.0", - "resolved": "git+ssh://git@github.com/Expensify/expensify-common.git#e67235baa887dcbe9dc4bf41ddf1925f19a1e8ad", - "integrity": "sha512-dfYAKmDhJYqbSlo11NCcaKSko0d6hRD4ZgRvEC0PBfHy+zRGw8aaZurZ1Q2KUHAmGNfv79pdA/R7XwFYDqhHxw==", + "resolved": "git+ssh://git@github.com/Expensify/expensify-common.git#55c208315e04246b548a4009d35466c100d9de7c", + "integrity": "sha512-oR/BHwfBZnO8wmghyew3qCAhS1oFZglrMFjjgpjjWPBOvcK2PyrSz/twn1Kn0v210xZdDqtvSPAXSu9zJBJkEQ==", "license": "MIT", "dependencies": { "classnames": "2.3.1", @@ -60866,9 +60866,9 @@ } }, "expensify-common": { - "version": "git+ssh://git@github.com/Expensify/expensify-common.git#e67235baa887dcbe9dc4bf41ddf1925f19a1e8ad", - "integrity": "sha512-dfYAKmDhJYqbSlo11NCcaKSko0d6hRD4ZgRvEC0PBfHy+zRGw8aaZurZ1Q2KUHAmGNfv79pdA/R7XwFYDqhHxw==", - "from": "expensify-common@git+https://github.com/Expensify/expensify-common.git#e67235baa887dcbe9dc4bf41ddf1925f19a1e8ad", + "version": "git+ssh://git@github.com/Expensify/expensify-common.git#55c208315e04246b548a4009d35466c100d9de7c", + "integrity": "sha512-oR/BHwfBZnO8wmghyew3qCAhS1oFZglrMFjjgpjjWPBOvcK2PyrSz/twn1Kn0v210xZdDqtvSPAXSu9zJBJkEQ==", + "from": "expensify-common@git+https://github.com/Expensify/expensify-common.git#55c208315e04246b548a4009d35466c100d9de7c", "requires": { "classnames": "2.3.1", "clipboard": "2.0.4", diff --git a/package.json b/package.json index 1019d3d51b18..5a642f45a39c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.2.48-2", + "version": "1.2.50-13", "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.", @@ -69,7 +69,7 @@ "babel-polyfill": "^6.26.0", "dom-serializer": "^0.2.2", "domhandler": "^4.3.0", - "expensify-common": "git+https://github.com/Expensify/expensify-common.git#e67235baa887dcbe9dc4bf41ddf1925f19a1e8ad", + "expensify-common": "git+https://github.com/Expensify/expensify-common.git#55c208315e04246b548a4009d35466c100d9de7c", "fbjs": "^3.0.2", "file-loader": "^6.0.0", "html-entities": "^1.3.1", diff --git a/src/CONST.js b/src/CONST.js index 46c40add7b4b..5d3c462ce99f 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -753,6 +753,7 @@ const CONST = { CARD_SECURITY_CODE: /^[0-9]{3,4}$/, CARD_EXPIRATION_DATE: /^(0[1-9]|1[0-2])([^0-9])?([0-9]{4}|([0-9]{2}))$/, PAYPAL_ME_USERNAME: /^[a-zA-Z0-9]+$/, + ROOM_NAME: /^#[a-z0-9-]{1,80}$/, // Adapted from: https://gist.github.com/dperini/729294 // eslint-disable-next-line max-len diff --git a/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly.js b/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly.js index 77cc720445f9..e48e68e329f5 100644 --- a/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly.js +++ b/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly.js @@ -8,7 +8,7 @@ import PressableWithSecondaryInteraction from '../PressableWithSecondaryInteract import * as ReportActionContextMenu from '../../pages/home/report/ContextMenu/ReportActionContextMenu'; import * as ContextMenuActions from '../../pages/home/report/ContextMenu/ContextMenuActions'; import Tooltip from '../Tooltip'; -import canUseTouchScreen from '../../libs/canUseTouchscreen'; +import * as DeviceCapabilities from '../../libs/DeviceCapabilities'; import styles from '../../styles/styles'; import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions'; import {propTypes as anchorForCommentsOnlyPropTypes, defaultProps} from './anchorForCommentsOnlyPropTypes'; @@ -30,7 +30,7 @@ const BaseAnchorForCommentsOnly = (props) => { } else { linkProps.href = props.href; } - const defaultTextStyle = canUseTouchScreen() || props.isSmallScreenWidth ? {} : styles.userSelectText; + const defaultTextStyle = DeviceCapabilities.canUseTouchScreen() || props.isSmallScreenWidth ? {} : styles.userSelectText; return ( { const initializeImageContainer = useCallback((event) => { setIsImageContainerInitialized(true); const {height, width} = event.nativeEvent.layout; - setImageContainerSize(Math.min(height - styles.imageCropRotateButton.height, width)); + setImageContainerSize(Math.floor(Math.min(height - styles.imageCropRotateButton.height, width))); }, [props.isSmallScreenWidth]); // An onLayout callback, that initializes the slider container size, for proper render of a slider @@ -293,8 +293,9 @@ const AvatarCropModal = (props) => { if (!isPressableEnabled.value) { return; } - const newScale = newScaleValue(locationX, sliderContainerSize); - translateSlider.value = locationX; + const newSliderValue = clamp(locationX, [0, sliderContainerSize]); + const newScale = newScaleValue(newSliderValue, sliderContainerSize); + translateSlider.value = newSliderValue; const differential = newScale / scale.value; scale.value = newScale; const newX = translateX.value * differential; diff --git a/src/components/Banner.js b/src/components/Banner.js index 4ef13aad42b8..748c8b489f9f 100644 --- a/src/components/Banner.js +++ b/src/components/Banner.js @@ -32,9 +32,11 @@ const propTypes = { /** Callback called when the message is pressed */ onPress: PropTypes.func, + /** Styles to be assigned to the Banner container */ // eslint-disable-next-line react/forbid-prop-types containerStyles: PropTypes.arrayOf(PropTypes.object), + /** Styles to be assigned to the Banner text */ // eslint-disable-next-line react/forbid-prop-types textStyles: PropTypes.arrayOf(PropTypes.object), diff --git a/src/components/BlockingViews/BlockingView.js b/src/components/BlockingViews/BlockingView.js index 5241714bea22..2cf6ff8262c4 100644 --- a/src/components/BlockingViews/BlockingView.js +++ b/src/components/BlockingViews/BlockingView.js @@ -35,7 +35,7 @@ const BlockingView = props => ( width={variables.iconSizeSuperLarge} height={variables.iconSizeSuperLarge} /> - {props.title} + {props.title} {props.subtitle} ); diff --git a/src/components/ConfirmationPage.js b/src/components/ConfirmationPage.js index 1162c81506a9..7c27313720af 100644 --- a/src/components/ConfirmationPage.js +++ b/src/components/ConfirmationPage.js @@ -43,13 +43,7 @@ const ConfirmationPage = props => ( source={{uri: props.illustration}} style={styles.confirmationAnimation} /> - + {props.heading} diff --git a/src/components/CurrentWalletBalance.js b/src/components/CurrentWalletBalance.js index 8b1ee33c5e81..5bc7f251066b 100644 --- a/src/components/CurrentWalletBalance.js +++ b/src/components/CurrentWalletBalance.js @@ -46,7 +46,7 @@ const CurrentWalletBalance = (props) => { ); return ( {`${formattedBalance}`} diff --git a/src/components/DatePicker/index.android.js b/src/components/DatePicker/index.android.js index ec1f23f70bd6..73f68d948b15 100644 --- a/src/components/DatePicker/index.android.js +++ b/src/components/DatePicker/index.android.js @@ -1,4 +1,5 @@ import React from 'react'; +import {Keyboard} from 'react-native'; import RNDatePicker from '@react-native-community/datetimepicker'; import moment from 'moment'; import _ from 'underscore'; @@ -35,6 +36,7 @@ class DatePicker extends React.Component { * @param {Event} event */ showPicker(event) { + Keyboard.dismiss(); this.setState({isPickerVisible: true}); event.preventDefault(); } diff --git a/src/components/DatePicker/index.ios.js b/src/components/DatePicker/index.ios.js index c9d076b7836b..d0945e0607aa 100644 --- a/src/components/DatePicker/index.ios.js +++ b/src/components/DatePicker/index.ios.js @@ -1,6 +1,6 @@ import React from 'react'; // eslint-disable-next-line no-restricted-imports -import {Button, View} from 'react-native'; +import {Button, View, Keyboard} from 'react-native'; import RNDatePicker from '@react-native-community/datetimepicker'; import moment from 'moment'; import _ from 'underscore'; @@ -36,6 +36,7 @@ class DatePicker extends React.Component { * @param {Event} event */ showPicker(event) { + Keyboard.dismiss(); this.initialValue = this.state.selectedDate; this.setState({isPickerVisible: true}); event.preventDefault(); diff --git a/src/components/EnvironmentBadge.js b/src/components/EnvironmentBadge.js index 12cc13255366..4365bfab1a11 100644 --- a/src/components/EnvironmentBadge.js +++ b/src/components/EnvironmentBadge.js @@ -2,6 +2,7 @@ import React from 'react'; import CONST from '../CONST'; import withEnvironment, {environmentPropTypes} from './withEnvironment'; import Badge from './Badge'; +import styles from '../styles/styles'; const ENVIRONMENT_SHORT_FORM = { [CONST.ENVIRONMENT.DEV]: 'DEV', @@ -20,6 +21,7 @@ const EnvironmentBadge = (props) => { success={props.environment === CONST.ENVIRONMENT.STAGING} error={props.environment !== CONST.ENVIRONMENT.STAGING} text={ENVIRONMENT_SHORT_FORM[props.environment]} + badgeStyles={[styles.alignSelfCenter]} /> ); }; diff --git a/src/components/HTMLEngineProvider/index.js b/src/components/HTMLEngineProvider/index.js index 627eb081c8d8..6c3a55966e91 100755 --- a/src/components/HTMLEngineProvider/index.js +++ b/src/components/HTMLEngineProvider/index.js @@ -2,12 +2,12 @@ import React from 'react'; import BaseHTMLEngineProvider from './BaseHTMLEngineProvider'; import {defaultProps, propTypes} from './htmlEnginePropTypes'; import withWindowDimensions from '../withWindowDimensions'; -import canUseTouchScreen from '../../libs/canUseTouchscreen'; +import * as DeviceCapabilities from '../../libs/DeviceCapabilities'; const HTMLEngineProvider = props => ( {props.children} diff --git a/src/components/Header.js b/src/components/Header.js index afbdf1fa778a..a69317122aad 100644 --- a/src/components/Header.js +++ b/src/components/Header.js @@ -15,16 +15,21 @@ const propTypes = { /** Should we show the environment badge (dev/stg)? */ shouldShowEnvironmentBadge: PropTypes.bool, + + /** Additional text styles */ + // eslint-disable-next-line react/forbid-prop-types + textStyles: PropTypes.arrayOf(PropTypes.object), }; const defaultProps = { shouldShowEnvironmentBadge: false, subtitle: '', + textStyles: [], }; const Header = props => ( - + {props.title} {/* If there's no subtitle then display a fragment to avoid an empty space which moves the main title */} diff --git a/src/components/Hoverable/hoverablePropTypes.js b/src/components/Hoverable/hoverablePropTypes.js index 07b8a8741efb..c17fa804b601 100644 --- a/src/components/Hoverable/hoverablePropTypes.js +++ b/src/components/Hoverable/hoverablePropTypes.js @@ -19,9 +19,6 @@ const propTypes = { /** Function that executes when the mouse leaves the children. */ onHoverOut: PropTypes.func, - - // If the mouse clicks outside, should we dismiss hover? - resetsOnClickOutside: PropTypes.bool, }; const defaultProps = { @@ -29,7 +26,6 @@ const defaultProps = { containerStyles: [], onHoverIn: () => {}, onHoverOut: () => {}, - resetsOnClickOutside: false, }; export { diff --git a/src/components/Hoverable/index.js b/src/components/Hoverable/index.js index f06ed5602744..ef4bc2bf532d 100644 --- a/src/components/Hoverable/index.js +++ b/src/components/Hoverable/index.js @@ -72,10 +72,6 @@ class Hoverable extends Component { if (!this.state.isHovered) { return; } - if (this.props.resetsOnClickOutside) { - this.setIsHovered(false); - return; - } if (this.wrapperView && !this.wrapperView.contains(event.target)) { this.setIsHovered(false); } @@ -93,8 +89,24 @@ class Hoverable extends Component { ref(el); } }, - onMouseEnter: () => this.setIsHovered(true), - onMouseLeave: () => this.setIsHovered(false), + onMouseEnter: (el) => { + this.setIsHovered(true); + + // Call the original onMouseEnter, if any + const {onMouseEnter} = this.props.children; + if (_.isFunction(onMouseEnter)) { + onMouseEnter(el); + } + }, + onMouseLeave: (el) => { + this.setIsHovered(false); + + // Call the original onMouseLeave, if any + const {onMouseLeave} = this.props.children; + if (_.isFunction(onMouseLeave)) { + onMouseLeave(el); + } + }, }); } return ( diff --git a/src/components/ImageView/index.js b/src/components/ImageView/index.js index d313d5dfb117..cd9969258fb8 100644 --- a/src/components/ImageView/index.js +++ b/src/components/ImageView/index.js @@ -5,7 +5,7 @@ import { } from 'react-native'; import styles from '../../styles/styles'; import * as StyleUtils from '../../styles/StyleUtils'; -import canUseTouchScreen from '../../libs/canUseTouchscreen'; +import * as DeviceCapabilities from '../../libs/DeviceCapabilities'; import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions'; import FullscreenLoadingIndicator from '../FullscreenLoadingIndicator'; @@ -19,7 +19,7 @@ class ImageView extends PureComponent { constructor(props) { super(props); this.scrollableRef = null; - this.canUseTouchScreen = canUseTouchScreen(); + this.canUseTouchScreen = DeviceCapabilities.canUseTouchScreen(); this.onContainerLayoutChanged = this.onContainerLayoutChanged.bind(this); this.onContainerPressIn = this.onContainerPressIn.bind(this); this.onContainerPress = this.onContainerPress.bind(this); diff --git a/src/components/Modal/BaseModal.js b/src/components/Modal/BaseModal.js index 323df19c7720..580b8f9abc47 100644 --- a/src/components/Modal/BaseModal.js +++ b/src/components/Modal/BaseModal.js @@ -8,6 +8,7 @@ import * as StyleUtils from '../../styles/StyleUtils'; import themeColors from '../../styles/themes/default'; import {propTypes as modalPropTypes, defaultProps as modalDefaultProps} from './modalPropTypes'; import * as Modal from '../../libs/actions/Modal'; +import DomUtils from '../../libs/DomUtils'; import getModalStyles from '../../styles/getModalStyles'; import variables from '../../styles/variables'; @@ -90,6 +91,7 @@ class BaseModal extends PureComponent { // Note: Escape key on web/desktop will trigger onBackButtonPress callback // eslint-disable-next-line react/jsx-props-no-multi-spaces onBackButtonPress={this.props.onClose} + onModalWillShow={DomUtils.blurActiveElement} onModalShow={() => { if (this.props.shouldSetModalVisibility) { Modal.setModalVisibility(true); diff --git a/src/components/OptionsList/index.js b/src/components/OptionsList/index.js index e43656f7e6bb..31128766f6d1 100644 --- a/src/components/OptionsList/index.js +++ b/src/components/OptionsList/index.js @@ -3,8 +3,8 @@ import {Keyboard} from 'react-native'; import _ from 'underscore'; import BaseOptionsList from './BaseOptionsList'; import withWindowDimensions from '../withWindowDimensions'; -import canUseTouchscreen from '../../libs/canUseTouchscreen'; import {propTypes, defaultProps} from './optionsListPropTypes'; +import * as DeviceCapabilities from '../../libs/DeviceCapabilities'; class OptionsList extends Component { constructor(props) { @@ -15,7 +15,7 @@ class OptionsList extends Component { } componentDidMount() { - if (!canUseTouchscreen()) { + if (!DeviceCapabilities.canUseTouchScreen()) { return; } @@ -26,7 +26,7 @@ class OptionsList extends Component { } componentWillUnmount() { - if (!canUseTouchscreen()) { + if (!DeviceCapabilities.canUseTouchScreen()) { return; } diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js index 8e2668be2fa3..b928abef11d4 100755 --- a/src/components/OptionsSelector/BaseOptionsSelector.js +++ b/src/components/OptionsSelector/BaseOptionsSelector.js @@ -265,6 +265,7 @@ class BaseOptionsSelector extends Component { showTitleTooltip={this.props.showTitleTooltip} isDisabled={this.props.isDisabled} shouldHaveOptionSeparator={this.props.shouldHaveOptionSeparator} + onLayout={this.props.onLayout} /> ) : ; return ( diff --git a/src/components/PDFView/PDFInfoMessage.js b/src/components/PDFView/PDFInfoMessage.js index 5d583db40ea5..bb683900a0c2 100644 --- a/src/components/PDFView/PDFInfoMessage.js +++ b/src/components/PDFView/PDFInfoMessage.js @@ -23,7 +23,7 @@ const PDFInfoMessage = props => ( width={variables.iconSizeSuperLarge} height={variables.iconSizeSuperLarge} /> - + {props.translate('attachmentView.pdfPasswordForm.title')} {props.translate('attachmentView.pdfPasswordForm.infoText')} diff --git a/src/components/PressableWithSecondaryInteraction/index.js b/src/components/PressableWithSecondaryInteraction/index.js index a6b21a59e1cb..d9e1629d33d6 100644 --- a/src/components/PressableWithSecondaryInteraction/index.js +++ b/src/components/PressableWithSecondaryInteraction/index.js @@ -4,7 +4,7 @@ import {Pressable} from 'react-native'; import {LongPressGestureHandler, State} from 'react-native-gesture-handler'; import * as pressableWithSecondaryInteractionPropTypes from './pressableWithSecondaryInteractionPropTypes'; import styles from '../../styles/styles'; -import hasHoverSupport from '../../libs/hasHoverSupport'; +import * as DeviceCapabilities from '../../libs/DeviceCapabilities'; /** * This is a special Pressable that calls onSecondaryInteraction when LongPressed, or right-clicked. @@ -31,7 +31,7 @@ class PressableWithSecondaryInteraction extends Component { * @param {Object} e */ callSecondaryInteractionWithMappedEvent(e) { - if ((e.nativeEvent.state !== State.ACTIVE) || hasHoverSupport()) { + if ((e.nativeEvent.state !== State.ACTIVE) || DeviceCapabilities.hasHoverSupport()) { return; } diff --git a/src/components/PressableWithSecondaryInteraction/index.native.js b/src/components/PressableWithSecondaryInteraction/index.native.js index efb68e52eb79..3b6edfc92198 100644 --- a/src/components/PressableWithSecondaryInteraction/index.native.js +++ b/src/components/PressableWithSecondaryInteraction/index.native.js @@ -21,9 +21,21 @@ const PressableWithSecondaryInteraction = (props) => { if (e.nativeEvent.state !== State.ACTIVE) { return; } + + // Map gesture event to normal Responder event + const { + absoluteX, absoluteY, locationX, locationY, + } = e.nativeEvent; + const mapEvent = { + ...e, + nativeEvent: { + ...e.nativeEvent, pageX: absoluteX, pageY: absoluteY, x: locationX, y: locationY, + }, + }; + e.preventDefault(); HapticFeedback.trigger(); - props.onSecondaryInteraction(e); + props.onSecondaryInteraction(mapEvent); }} > { && Boolean(props.action.originalMessage.IOUReportID) && props.chatReport.hasOutstandingIOU) || props.action.originalMessage.type === 'pay'; + let shouldShowPendingConversionMessage = false; + if ( + props.iouReport + && props.chatReport.hasOutstandingIOU + && props.isMostRecentIOUReportAction + && props.action.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD + && props.network.isOffline + ) { + shouldShowPendingConversionMessage = IOUUtils.isIOUReportPendingCurrencyConversion(props.reportActions, props.iouReport); + } + return ( <> { `${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, - }, -})(IOUAction); +export default compose( + withOnyx({ + chatReport: { + key: ({chatReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, + }, + iouReport: { + key: ({iouReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`, + }, + reportActions: { + key: ({chatReportID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReportID}`, + canEvict: false, + }, + }), + withNetwork(), +)(IOUAction); diff --git a/src/components/ReportActionItem/IOUPreview.js b/src/components/ReportActionItem/IOUPreview.js index ceec76c279c6..1c997119d6d7 100644 --- a/src/components/ReportActionItem/IOUPreview.js +++ b/src/components/ReportActionItem/IOUPreview.js @@ -175,11 +175,18 @@ const IOUPreview = (props) => { ) : ( - - {props.iouReport.hasOutstandingIOU - ? props.translate('iou.owesyou', {manager: managerName}) - : props.translate('iou.paidyou', {manager: managerName})} - + <> + + {props.iouReport.hasOutstandingIOU + ? props.translate('iou.owesyou', {manager: managerName}) + : props.translate('iou.paidyou', {manager: managerName})} + + {props.shouldShowPendingConversionMessage && ( + + {props.translate('iou.pendingConversionMessage')} + + )} + )} {(isCurrentUserManager && !props.shouldHidePayButton diff --git a/src/components/RoomNameInput/index.js b/src/components/RoomNameInput/index.js index 2c27fca0e24b..0ecd55ce1668 100644 --- a/src/components/RoomNameInput/index.js +++ b/src/components/RoomNameInput/index.js @@ -73,6 +73,7 @@ class RoomNameInput extends Component { autoCapitalize="none" onBlur={this.props.onBlur} autoFocus={this.props.autoFocus} + maxLength={CONST.REPORT.MAX_ROOM_NAME_LENGTH} /> ); } diff --git a/src/components/Section.js b/src/components/Section.js index 83c8900a30a2..cbed28c3de12 100644 --- a/src/components/Section.js +++ b/src/components/Section.js @@ -43,7 +43,7 @@ const Section = (props) => { - {props.title} + {props.title} {props.icon && } diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index 0fed14ea4317..824d32c76596 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -66,7 +66,8 @@ class BaseTextInput extends Component { } if (this.props.shouldDelayFocus) { - return setTimeout(() => this.input.focus(), CONST.ANIMATED_TRANSITION); + this.focusTimeout = setTimeout(() => this.input.focus(), CONST.ANIMATED_TRANSITION); + return; } this.input.focus(); } @@ -94,6 +95,10 @@ class BaseTextInput extends Component { } componentWillUnmount() { + if (this.focusTimeout) { + clearTimeout(this.focusTimeout); + } + if (!this.props.disableKeyboard || !this.appStateSubscription) { return; } @@ -242,7 +247,13 @@ class BaseTextInput extends Component { /> ) : null} - + {Boolean(this.props.prefixCharacter) && ( - - this.textRef = ref}>{this.props.text} - - - - - , - document.querySelector('body'), + return ( + + + + { + // Once the text for the tooltip first renders, update the width of the tooltip dynamically to fit the width of the text. + // Note that we can't have this code in componentDidMount because the ref for the text won't be set until after the first render + if (this.textRef) { + return; + } + + this.textRef = ref; + this.updateTooltipTextWidth(); + }} + > + {this.props.text} + + + + + + + ); } } diff --git a/src/components/Tooltip/index.js b/src/components/Tooltip/index.js index 8d3a345492ac..88c8d7401430 100644 --- a/src/components/Tooltip/index.js +++ b/src/components/Tooltip/index.js @@ -7,6 +7,7 @@ import withWindowDimensions from '../withWindowDimensions'; import {propTypes, defaultProps} from './tooltipPropTypes'; import TooltipSense from './TooltipSense'; import makeCancellablePromise from '../../libs/MakeCancellablePromise'; +import * as DeviceCapabilities from '../../libs/DeviceCapabilities'; class Tooltip extends PureComponent { constructor(props) { @@ -31,6 +32,7 @@ class Tooltip extends PureComponent { this.isTooltipSenseInitiator = false; this.shouldStartShowAnimation = false; this.animation = new Animated.Value(0); + this.hasHoverSupport = DeviceCapabilities.hasHoverSupport(); this.getWrapperPosition = this.getWrapperPosition.bind(this); this.showTooltip = this.showTooltip.bind(this); @@ -63,7 +65,7 @@ class Tooltip extends PureComponent { getWrapperPosition() { return new Promise(((resolve) => { // Make sure the wrapper is mounted before attempting to measure it. - if (this.wrapperView) { + if (this.wrapperView && _.isFunction(this.wrapperView.measureInWindow)) { this.wrapperView.measureInWindow((x, y, width, height) => resolve({ x, y, width, height, })); @@ -140,14 +142,16 @@ class Tooltip extends PureComponent { } render() { - // Skip the tooltip and return the children, if the text is empty. - if (_.isEmpty(this.props.text)) { + // Skip the tooltip and return the children if the text is empty or the device does not support hovering + if (_.isEmpty(this.props.text) || !this.hasHoverSupport) { return this.props.children; } let child = ( this.wrapperView = el} style={this.props.containerStyles} + onBlur={this.hideTooltip} + focusable > {this.props.children} @@ -156,15 +160,24 @@ class Tooltip extends PureComponent { if (this.props.absolute && React.isValidElement(this.props.children)) { child = React.cloneElement(React.Children.only(this.props.children), { ref: (el) => { - // Keep your own reference this.wrapperView = el; // Call the original ref, if any const {ref} = this.props.children; - if (typeof ref === 'function') { + if (_.isFunction(ref)) { ref(el); } }, + onBlur: (el) => { + this.hideTooltip(); + + // Call the original onBlur, if any + const {onBlur} = this.props.children; + if (_.isFunction(onBlur)) { + onBlur(el); + } + }, + focusable: true, }); } return ( @@ -189,7 +202,6 @@ class Tooltip extends PureComponent { containerStyles={this.props.containerStyles} onHoverIn={this.showTooltip} onHoverOut={this.hideTooltip} - resetsOnClickOutside > {child} diff --git a/src/components/TouchableDismissKeyboard/index.ios.js b/src/components/TouchableDismissKeyboard/index.ios.js new file mode 100644 index 000000000000..d4d95334f969 --- /dev/null +++ b/src/components/TouchableDismissKeyboard/index.ios.js @@ -0,0 +1,37 @@ +/** + * On iOS sometimes the keyboard doesn't dismiss itself + * when you tap outside of the input that pulled it up. + * Therefore this component is necessary to ensure users + * can still dismiss the keyboard on iOS. + */ +import React from 'react'; +import {TouchableWithoutFeedback, Keyboard} from 'react-native'; +import PropTypes from 'prop-types'; +import withKeyboardState, {keyboardStatePropTypes} from '../withKeyboardState'; + +const propTypes = { + /** The children which should be contained in this wrapper component. */ + children: PropTypes.node.isRequired, + + ...keyboardStatePropTypes, +}; + +const TouchableDismissKeyboard = (props) => { + const dismissKeyboardWhenTappedOutsideOfInput = () => { + if (!props.isKeyboardShown) { + return; + } + Keyboard.dismiss(); + }; + + return ( + + {props.children} + + ); +}; + +TouchableDismissKeyboard.propTypes = propTypes; +TouchableDismissKeyboard.displayName = 'TouchableDismissKeyboard'; + +export default withKeyboardState(TouchableDismissKeyboard); diff --git a/src/components/TouchableDismissKeyboard/index.js b/src/components/TouchableDismissKeyboard/index.js new file mode 100644 index 000000000000..e076bfff1448 --- /dev/null +++ b/src/components/TouchableDismissKeyboard/index.js @@ -0,0 +1,13 @@ +import PropTypes from 'prop-types'; + +const propTypes = { + /** The children which should be contained in this wrapper component. */ + children: PropTypes.node.isRequired, +}; + +const TouchableDismissKeyboard = props => props.children; + +TouchableDismissKeyboard.propTypes = propTypes; +TouchableDismissKeyboard.displayName = 'TouchableDismissKeyboard'; + +export default TouchableDismissKeyboard; diff --git a/src/languages/en.js b/src/languages/en.js index e2d7dc476c28..7d97121f798c 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -260,6 +260,7 @@ export default { split: ({amount}) => `Split ${amount}`, send: ({amount}) => `Send ${amount}`, noReimbursableExpenses: 'This report has an invalid amount', + pendingConversionMessage: 'Total will update when you\'re back online', error: { invalidSplit: 'Split amounts do not equal total amount', other: 'Unexpected error, please try again later', @@ -384,7 +385,8 @@ export default { reasonForLeavingPrompt: 'We’d hate to see you go! Would you kindly tell us why, so we can improve?', enterMessageHere: 'Enter message here', closeAccountWarning: 'Closing your account cannot be undone.', - closeAccountPermanentlyDeleteData: 'This will permanently delete all of your unsubmitted expense data. Type your phone number or email address to confirm.', + closeAccountPermanentlyDeleteData: 'This will permanently delete all of your unsubmitted expense data and will cancel and decline any outstanding money requests. Are you sure you want to delete the account?', + enterDefaultContactToConfirm: 'Please type your default contact method to confirm you wish to close your account. Your default contact method is:', enterDefaultContact: 'Enter your default contact method', defaultContact: 'Default contact method:', enterYourDefaultContactMethod: 'Please enter your default contact method to close your account.', @@ -1027,6 +1029,7 @@ export default { createRoom: 'Create Room', roomAlreadyExistsError: 'A room with this name already exists', roomNameReservedError: 'A room on this workspace already uses this name', + roomNameInvalidError: 'Room names can only contain letters, numbers and hyphens', pleaseEnterRoomName: 'Please enter a room name', pleaseSelectWorkspace: 'Please select a workspace', renamedRoomAction: ({oldName, newName}) => ` renamed this room from ${oldName} to ${newName}`, diff --git a/src/languages/es.js b/src/languages/es.js index bb6771930d80..9a0e0e6178cc 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -260,6 +260,7 @@ export default { split: ({amount}) => `Dividir ${amount}`, send: ({amount}) => `Enviar ${amount}`, noReimbursableExpenses: 'El monto de este informe es inválido', + pendingConversionMessage: 'El total se actualizará cuando estés online', error: { invalidSplit: 'La suma de las partes no equivale al monto total', other: 'Error inesperado, por favor inténtalo más tarde', @@ -384,7 +385,8 @@ export default { reasonForLeavingPrompt: '¡Lamentamos verte partir! ¿Serías tan amable de decirnos por qué, para que podamos mejorar?', enterMessageHere: 'Ingresa el mensaje aquí', closeAccountWarning: 'Una vez cerrada tu cuenta no se puede revertir.', - closeAccountPermanentlyDeleteData: 'Esta acción eliminará permanentemente toda la información de tus gastos no enviados. Escribe tu número de teléfono o correo electrónico para confirmar', + closeAccountPermanentlyDeleteData: 'Esta acción eliminará permanentemente toda la información de tus gastos no enviados y cancelará o rechazará cualquier solicitud de dinero pendiente. ¿Estás seguro de que quieres eliminar tu cuenta?', + enterDefaultContactToConfirm: 'Por favor escribe tu método de contacto predeterminado para confirmar que deseas eliminar tu cuenta. Tu método de contacto predeterminado es:', enterDefaultContact: 'Tu método de contacto predeterminado', defaultContact: 'Método de contacto predeterminado:', enterYourDefaultContactMethod: 'Por favor ingresa tu método de contacto predeterminado para cerrar tu cuenta.', @@ -1029,6 +1031,7 @@ export default { createRoom: 'Crea una sala de chat', roomAlreadyExistsError: 'Ya existe una sala con este nombre', roomNameReservedError: 'Una sala en este espacio de trabajo ya usa este nombre', + roomNameInvalidError: 'Los nombres de las salas solo pueden contener letras, números y guiones', pleaseEnterRoomName: 'Por favor escribe el nombre de una sala', pleaseSelectWorkspace: 'Por favor, selecciona un espacio de trabajo', renamedRoomAction: ({oldName, newName}) => ` cambió el nombre de la sala de ${oldName} a ${newName}`, diff --git a/src/libs/canUseTouchscreen/index.js b/src/libs/DeviceCapabilities/canUseTouchScreen/index.js similarity index 100% rename from src/libs/canUseTouchscreen/index.js rename to src/libs/DeviceCapabilities/canUseTouchScreen/index.js diff --git a/src/libs/canUseTouchscreen/index.native.js b/src/libs/DeviceCapabilities/canUseTouchScreen/index.native.js similarity index 100% rename from src/libs/canUseTouchscreen/index.native.js rename to src/libs/DeviceCapabilities/canUseTouchScreen/index.native.js diff --git a/src/libs/hasHoverSupport/index.js b/src/libs/DeviceCapabilities/hasHoverSupport/index.js similarity index 57% rename from src/libs/hasHoverSupport/index.js rename to src/libs/DeviceCapabilities/hasHoverSupport/index.js index 5053c2b496f9..75ed1ccd3b0c 100644 --- a/src/libs/hasHoverSupport/index.js +++ b/src/libs/DeviceCapabilities/hasHoverSupport/index.js @@ -3,10 +3,8 @@ * * @returns {Boolean} */ - -import * as Browser from '../Browser'; - -const hasHoverSupport = () => !Browser.isMobile(); +function hasHoverSupport() { + return !window.matchMedia('(hover: none)').matches; +} export default hasHoverSupport; - diff --git a/src/libs/hasHoverSupport/index.native.js b/src/libs/DeviceCapabilities/hasHoverSupport/index.native.js similarity index 100% rename from src/libs/hasHoverSupport/index.native.js rename to src/libs/DeviceCapabilities/hasHoverSupport/index.native.js diff --git a/src/libs/DeviceCapabilities/index.js b/src/libs/DeviceCapabilities/index.js new file mode 100644 index 000000000000..216962038659 --- /dev/null +++ b/src/libs/DeviceCapabilities/index.js @@ -0,0 +1,7 @@ +import canUseTouchScreen from './canUseTouchScreen'; +import hasHoverSupport from './hasHoverSupport'; + +export { + canUseTouchScreen, + hasHoverSupport, +}; diff --git a/src/libs/DomUtils/index.js b/src/libs/DomUtils/index.js new file mode 100644 index 000000000000..4e58ea3a08fb --- /dev/null +++ b/src/libs/DomUtils/index.js @@ -0,0 +1,7 @@ +function blurActiveElement() { + document.activeElement.blur(); +} + +export default { + blurActiveElement, +}; diff --git a/src/libs/DomUtils/index.native.js b/src/libs/DomUtils/index.native.js new file mode 100644 index 000000000000..0e796bc40b54 --- /dev/null +++ b/src/libs/DomUtils/index.native.js @@ -0,0 +1,5 @@ +function blurActiveElement() {} + +export default { + blurActiveElement, +}; diff --git a/src/libs/E2E/API.mock.js b/src/libs/E2E/API.mock.js index b8fd0c260d7d..69d94f73a951 100644 --- a/src/libs/E2E/API.mock.js +++ b/src/libs/E2E/API.mock.js @@ -3,37 +3,24 @@ import _ from 'underscore'; import Onyx from 'react-native-onyx'; import Log from '../Log'; +// mock functions +import mockBeginSignin from './apiMocks/beginSignin'; +import mockSigninUser from './apiMocks/signinUser'; +import mockAuthenticatePusher from './apiMocks/authenticatePusher'; +import mockOpenApp from './apiMocks/openApp'; +import mockOpenReport from './apiMocks/openReport'; + /** * A dictionary which has the name of a API command as key, and a function which * receives the api command parameters as value and is expected to return a response * object. */ const mocks = { - BeginSignIn: ({email}) => { - const response = require('../E2E/apiMocks/beginSignin.json'); - response.onyxData.forEach((data) => { - if (data.key !== 'credentials') { - return; - } - // eslint-disable-next-line no-param-reassign - data.value.login = email; - }); - return response; - }, - SigninUser: ({email}) => { - const response = require('../E2E/apiMocks/signinUser.json'); - response.onyxData.forEach((data) => { - if (data.key !== 'session') { - return; - } - // eslint-disable-next-line no-param-reassign - data.value.email = email; - }); - return response; - }, - OpenApp: () => require('../E2E/apiMocks/openApp.json'), - OpenReport: () => require('../E2E/apiMocks/openReport.json'), - AuthenticatePusher: () => require('../E2E/apiMocks/authenticatePusher.json'), + BeginSignIn: mockBeginSignin, + SigninUser: mockSigninUser, + OpenApp: mockOpenApp, + OpenReport: mockOpenReport, + AuthenticatePusher: mockAuthenticatePusher, }; function mockCall(command, apiCommandParameters, tag) { diff --git a/src/libs/E2E/actions/e2eLogin.js b/src/libs/E2E/actions/e2eLogin.js index 67743611cb25..6fd850e6cdb9 100644 --- a/src/libs/E2E/actions/e2eLogin.js +++ b/src/libs/E2E/actions/e2eLogin.js @@ -12,7 +12,7 @@ import * as Session from '../../actions/Session'; * @param {String} password * @return {Promise} Resolved true when the user was actually signed in. Returns false if the user was already logged in. */ -export default function (email, password) { +export default function (email = 'fake@email.com', password = 'Password123') { const waitForBeginSignInToFinish = () => new Promise((resolve) => { const id = Onyx.connect({ key: ONYXKEYS.CREDENTIALS, diff --git a/src/libs/E2E/apiMocks/authenticatePusher.js b/src/libs/E2E/apiMocks/authenticatePusher.js new file mode 100644 index 000000000000..d3d7fcfc829e --- /dev/null +++ b/src/libs/E2E/apiMocks/authenticatePusher.js @@ -0,0 +1,7 @@ +export default () => ({ + auth: 'auth', + shared_secret: 'secret', + jsonCode: 200, + requestID: '783ef7fc3991969a-SJC', +} +); diff --git a/src/libs/E2E/apiMocks/authenticatePusher.json b/src/libs/E2E/apiMocks/authenticatePusher.json deleted file mode 100644 index c95a298da79e..000000000000 --- a/src/libs/E2E/apiMocks/authenticatePusher.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "auth": "auth", - "shared_secret": "secret", - "jsonCode": 200, - "requestID": "783ef7fc3991969a-SJC" -} diff --git a/src/libs/E2E/apiMocks/beginSignin.js b/src/libs/E2E/apiMocks/beginSignin.js new file mode 100644 index 000000000000..b0581568cdb3 --- /dev/null +++ b/src/libs/E2E/apiMocks/beginSignin.js @@ -0,0 +1,28 @@ +export default ({email}) => ({ + onyxData: [ + { + onyxMethod: 'merge', + key: 'credentials', + value: { + login: email, + }, + }, + { + onyxMethod: 'merge', + key: 'account', + value: { + validated: true, + }, + }, + { + onyxMethod: 'set', + key: 'betas', + value: [ + 'passwordless', + ], + }, + ], + jsonCode: 200, + requestID: '783e54ef4b38cff5-SJC', +} +); diff --git a/src/libs/E2E/apiMocks/beginSignin.json b/src/libs/E2E/apiMocks/beginSignin.json deleted file mode 100644 index d5563204381a..000000000000 --- a/src/libs/E2E/apiMocks/beginSignin.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "onyxData": [ - { - "onyxMethod": "merge", - "key": "credentials", - "value": { - "login": "thisemailwillgetreplaced@duringsign.com" - } - }, - { - "onyxMethod": "merge", - "key": "account", - "value": { - "validated": true - } - }, - { - "onyxMethod": "set", - "key": "betas", - "value": [ - "passwordless" - ] - } - ], - "jsonCode": 200, - "requestID": "783e54ef4b38cff5-SJC" -} diff --git a/src/libs/E2E/apiMocks/openApp.js b/src/libs/E2E/apiMocks/openApp.js new file mode 100644 index 000000000000..d742823aa883 --- /dev/null +++ b/src/libs/E2E/apiMocks/openApp.js @@ -0,0 +1,2505 @@ +export default () => ({ + onyxData: [ + { + onyxMethod: 'merge', + key: 'user', + value: { + isFromPublicDomain: false, + }, + }, + { + onyxMethod: 'merge', + key: 'currencyList', + value: { + AED: { + symbol: 'Dhs', + name: 'UAE Dirham', + ISO4217: '784', + }, + AFN: { + symbol: 'Af', + name: 'Afghan Afghani', + ISO4217: '971', + }, + ALL: { + symbol: 'ALL', + name: 'Albanian Lek', + ISO4217: '008', + }, + AMD: { + symbol: '\u0564\u0580', + name: 'Armenian Dram', + ISO4217: '051', + }, + ANG: { + symbol: 'NA\u0192', + name: 'Neth Antilles Guilder', + ISO4217: '532', + }, + AOA: { + symbol: 'Kz', + name: 'Angolan Kwanza', + ISO4217: '973', + }, + ARS: { + symbol: 'AR$', + name: 'Argentine Peso', + ISO4217: '032', + }, + AUD: { + symbol: 'A$', + name: 'Australian Dollar', + ISO4217: '036', + }, + AWG: { + symbol: '\u0192', + name: 'Aruba Florin', + ISO4217: '533', + }, + AZN: { + symbol: 'man', + name: 'Azerbaijani Manat', + ISO4217: '944', + }, + BAM: { + symbol: 'KM', + name: 'Bosnia And Herzegovina Convertible Mark', + ISO4217: '977', + }, + BBD: { + symbol: 'Bds$', + name: 'Barbados Dollar', + ISO4217: '052', + }, + BDT: { + symbol: 'Tk', + name: 'Bangladesh Taka', + ISO4217: '050', + }, + BGN: { + symbol: '\u043b\u0432', + name: 'Bulgarian Lev', + ISO4217: '975', + }, + BHD: { + symbol: 'BHD', + name: 'Bahraini Dinar', + ISO4217: '048', + }, + BIF: { + symbol: 'FBu', + name: 'Burundi Franc', + decimals: 0, + ISO4217: '108', + }, + BMD: { + symbol: 'BD$', + name: 'Bermuda Dollar', + ISO4217: '060', + }, + BND: { + symbol: 'BN$', + name: 'Brunei Dollar', + ISO4217: '096', + }, + BOB: { + symbol: 'Bs', + name: 'Bolivian Boliviano', + ISO4217: '068', + }, + BRL: { + symbol: 'R$', + name: 'Brazilian Real', + ISO4217: '986', + }, + BSD: { + symbol: 'BS$', + name: 'Bahamian Dollar', + ISO4217: '044', + }, + BTN: { + symbol: 'Nu.', + name: 'Bhutan Ngultrum', + ISO4217: '064', + }, + BWP: { + symbol: 'P', + name: 'Botswana Pula', + ISO4217: '072', + }, + BYN: { + symbol: 'BR', + name: 'Belarus Ruble', + ISO4217: '933', + }, + BYR: { + symbol: 'BR', + name: 'Belarus Ruble', + retired: true, + retirementDate: '2016-07-01', + ISO4217: '974', + }, + BZD: { + symbol: 'BZ$', + name: 'Belize Dollar', + ISO4217: '084', + }, + CAD: { + symbol: 'C$', + name: 'Canadian Dollar', + ISO4217: '124', + }, + CDF: { + symbol: 'CDF', + name: 'Congolese Franc', + ISO4217: '976', + }, + CHF: { + symbol: 'CHF', + name: 'Swiss Franc', + ISO4217: '756', + }, + CLP: { + symbol: 'Ch$', + name: 'Chilean Peso', + decimals: 0, + ISO4217: '152', + }, + CNY: { + symbol: '\u00a5', + name: 'Chinese Yuan', + ISO4217: '156', + }, + COP: { + symbol: 'Col$', + name: 'Colombian Peso', + decimals: 0, + ISO4217: '170', + }, + CRC: { + symbol: 'CR\u20a1', + name: 'Costa Rica Colon', + ISO4217: '188', + }, + CUC: { + symbol: 'CUC', + name: 'Cuban Convertible Peso', + ISO4217: '931', + }, + CUP: { + symbol: '$MN', + name: 'Cuban Peso', + ISO4217: '192', + }, + CVE: { + symbol: 'Esc', + name: 'Cape Verde Escudo', + ISO4217: '132', + }, + CZK: { + symbol: 'K\u010d', + name: 'Czech Koruna', + ISO4217: '203', + }, + DJF: { + symbol: 'Fdj', + name: 'Dijibouti Franc', + decimals: 0, + ISO4217: '262', + }, + DKK: { + symbol: 'Dkr', + name: 'Danish Krone', + ISO4217: '208', + }, + DOP: { + symbol: 'RD$', + name: 'Dominican Peso', + ISO4217: '214', + }, + DZD: { + symbol: 'DZD', + name: 'Algerian Dinar', + ISO4217: '012', + }, + EEK: { + symbol: 'KR', + name: 'Estonian Kroon', + ISO4217: '', + retired: true, + }, + EGP: { + symbol: 'EGP', + name: 'Egyptian Pound', + ISO4217: '818', + }, + ERN: { + symbol: 'Nfk', + name: 'Eritrea Nakfa', + ISO4217: '232', + }, + ETB: { + symbol: 'Br', + name: 'Ethiopian Birr', + ISO4217: '230', + }, + EUR: { + symbol: '\u20ac', + name: 'Euro', + ISO4217: '978', + }, + FJD: { + symbol: 'FJ$', + name: 'Fiji Dollar', + ISO4217: '242', + }, + FKP: { + symbol: 'FK\u00a3', + name: 'Falkland Islands Pound', + ISO4217: '238', + }, + GBP: { + symbol: '\u00a3', + name: 'British Pound', + ISO4217: '826', + }, + GEL: { + symbol: '\u10da', + name: 'Georgian Lari', + ISO4217: '981', + }, + GHS: { + symbol: '\u20b5', + name: 'Ghanaian Cedi', + ISO4217: '936', + }, + GIP: { + symbol: '\u00a3G', + name: 'Gibraltar Pound', + ISO4217: '292', + }, + GMD: { + symbol: 'D', + name: 'Gambian Dalasi', + ISO4217: '270', + }, + GNF: { + symbol: 'FG', + name: 'Guinea Franc', + decimals: 0, + ISO4217: '324', + }, + GTQ: { + symbol: 'Q', + name: 'Guatemala Quetzal', + ISO4217: '320', + }, + GYD: { + symbol: 'GY$', + name: 'Guyana Dollar', + ISO4217: '328', + }, + HKD: { + symbol: 'HK$', + name: 'Hong Kong Dollar', + ISO4217: '344', + }, + HNL: { + symbol: 'HNL', + name: 'Honduras Lempira', + ISO4217: '340', + }, + HRK: { + symbol: 'kn', + name: 'Croatian Kuna', + ISO4217: '191', + }, + HTG: { + symbol: 'G', + name: 'Haiti Gourde', + ISO4217: '332', + }, + HUF: { + symbol: 'Ft', + name: 'Hungarian Forint', + ISO4217: '348', + }, + IDR: { + symbol: 'Rp', + name: 'Indonesian Rupiah', + ISO4217: '360', + }, + ILS: { + symbol: '\u20aa', + name: 'Israeli Shekel', + ISO4217: '376', + }, + INR: { + symbol: '\u20b9', + name: 'Indian Rupee', + ISO4217: '356', + }, + IQD: { + symbol: 'IQD', + name: 'Iraqi Dinar', + ISO4217: '368', + }, + IRR: { + symbol: '\ufdfc', + name: 'Iran Rial', + ISO4217: '364', + }, + ISK: { + symbol: 'kr', + name: 'Iceland Krona', + decimals: 0, + ISO4217: '352', + }, + JMD: { + symbol: 'J$', + name: 'Jamaican Dollar', + ISO4217: '388', + }, + JOD: { + symbol: 'JOD', + name: 'Jordanian Dinar', + ISO4217: '400', + }, + JPY: { + symbol: '\u00a5', + name: 'Japanese Yen', + decimals: 0, + ISO4217: '392', + }, + KES: { + symbol: 'KSh', + name: 'Kenyan Shilling', + ISO4217: '404', + }, + KGS: { + symbol: 'KGS', + name: 'Kyrgyzstani Som', + ISO4217: '417', + }, + KHR: { + symbol: 'KHR', + name: 'Cambodia Riel', + ISO4217: '116', + }, + KMF: { + symbol: 'CF', + name: 'Comoros Franc', + ISO4217: '174', + }, + KPW: { + symbol: 'KP\u20a9', + name: 'North Korean Won', + ISO4217: '408', + }, + KRW: { + symbol: '\u20a9', + name: 'Korean Won', + ISO4217: '410', + }, + KWD: { + symbol: 'KWD', + name: 'Kuwaiti Dinar', + ISO4217: '414', + }, + KYD: { + symbol: 'CI$', + name: 'Cayman Islands Dollar', + ISO4217: '136', + }, + KZT: { + symbol: '\u3012', + name: 'Kazakhstan Tenge', + ISO4217: '398', + }, + LAK: { + symbol: '\u20ad', + name: 'Lao Kip', + ISO4217: '418', + }, + LBP: { + symbol: 'LBP', + name: 'Lebanese Pound', + ISO4217: '422', + }, + LKR: { + symbol: 'SL\u20a8', + name: 'Sri Lanka Rupee', + ISO4217: '144', + }, + LRD: { + symbol: 'L$', + name: 'Liberian Dollar', + ISO4217: '430', + }, + LSL: { + symbol: 'M', + name: 'Lesotho Loti', + ISO4217: '426', + }, + LTL: { + symbol: 'Lt', + name: 'Lithuanian Lita', + retirementDate: '2015-08-22', + retired: true, + ISO4217: '440', + }, + LVL: { + symbol: 'Ls', + name: 'Latvian Lat', + ISO4217: '428', + retired: true, + }, + LYD: { + symbol: 'LYD', + name: 'Libyan Dinar', + ISO4217: '434', + }, + MAD: { + symbol: 'MAD', + name: 'Moroccan Dirham', + ISO4217: '504', + }, + MDL: { + symbol: 'MDL', + name: 'Moldovan Leu', + ISO4217: '498', + }, + MGA: { + symbol: 'MGA', + name: 'Malagasy Ariary', + ISO4217: '969', + }, + MKD: { + symbol: '\u0434\u0435\u043d', + name: 'Macedonian Denar', + ISO4217: '807', + }, + MMK: { + symbol: 'Ks', + name: 'Myanmar Kyat', + ISO4217: '104', + }, + MNT: { + symbol: '\u20ae', + name: 'Mongolian Tugrik', + ISO4217: '496', + }, + MOP: { + symbol: 'MOP$', + name: 'Macau Pataca', + ISO4217: '446', + }, + MRO: { + symbol: 'UM', + name: 'Mauritania Ougulya', + decimals: 0, + retired: true, + retirementDate: '2018-07-11', + ISO4217: '478', + }, + MRU: { + symbol: 'UM', + name: 'Mauritania Ougulya', + decimals: 0, + ISO4217: '', + }, + MUR: { + symbol: 'Rs', + name: 'Mauritius Rupee', + ISO4217: '480', + }, + MVR: { + symbol: 'Rf', + name: 'Maldives Rufiyaa', + ISO4217: '462', + }, + MWK: { + symbol: 'MK', + name: 'Malawi Kwacha', + ISO4217: '454', + }, + MXN: { + symbol: 'Mex$', + name: 'Mexican Peso', + ISO4217: '484', + }, + MYR: { + symbol: 'RM', + name: 'Malaysian Ringgit', + ISO4217: '458', + }, + MZN: { + symbol: 'MTn', + name: 'Mozambican Metical', + ISO4217: '943', + }, + NAD: { + symbol: 'N$', + name: 'Namibian Dollar', + ISO4217: '516', + }, + NGN: { + symbol: '\u20a6', + name: 'Nigerian Naira', + ISO4217: '566', + }, + NIO: { + symbol: 'NIO', + name: 'Nicaragua Cordoba', + ISO4217: '558', + }, + NOK: { + symbol: 'Nkr', + name: 'Norwegian Krone', + ISO4217: '578', + }, + NPR: { + symbol: '\u20a8', + name: 'Nepalese Rupee', + ISO4217: '524', + }, + NZD: { + symbol: 'NZ$', + name: 'New Zealand Dollar', + ISO4217: '554', + }, + OMR: { + symbol: 'OMR', + name: 'Omani Rial', + ISO4217: '512', + }, + PAB: { + symbol: 'B', + name: 'Panama Balboa', + ISO4217: '590', + }, + PEN: { + symbol: 'S/.', + name: 'Peruvian Nuevo Sol', + ISO4217: '604', + }, + PGK: { + symbol: 'K', + name: 'Papua New Guinea Kina', + ISO4217: '598', + }, + PHP: { + symbol: '\u20b1', + name: 'Philippine Peso', + ISO4217: '608', + }, + PKR: { + symbol: 'Rs', + name: 'Pakistani Rupee', + ISO4217: '586', + }, + PLN: { + symbol: 'z\u0142', + name: 'Polish Zloty', + ISO4217: '985', + }, + PYG: { + symbol: '\u20b2', + name: 'Paraguayan Guarani', + ISO4217: '600', + }, + QAR: { + symbol: 'QAR', + name: 'Qatar Rial', + ISO4217: '634', + }, + RON: { + symbol: 'RON', + name: 'Romanian New Leu', + ISO4217: '946', + }, + RSD: { + symbol: '\u0420\u0421\u0414', + name: 'Serbian Dinar', + ISO4217: '941', + }, + RUB: { + symbol: '\u20bd', + name: 'Russian Rouble', + ISO4217: '643', + }, + RWF: { + symbol: 'RF', + name: 'Rwanda Franc', + decimals: 0, + ISO4217: '646', + }, + SAR: { + symbol: 'SAR', + name: 'Saudi Arabian Riyal', + ISO4217: '682', + }, + SBD: { + symbol: 'SI$', + name: 'Solomon Islands Dollar', + ISO4217: '090', + }, + SCR: { + symbol: 'SR', + name: 'Seychelles Rupee', + ISO4217: '690', + }, + SDG: { + symbol: 'SDG', + name: 'Sudanese Pound', + ISO4217: '938', + }, + SEK: { + symbol: 'Skr', + name: 'Swedish Krona', + ISO4217: '752', + }, + SGD: { + symbol: 'S$', + name: 'Singapore Dollar', + ISO4217: '702', + }, + SHP: { + symbol: '\u00a3S', + name: 'St Helena Pound', + ISO4217: '654', + }, + SLL: { + symbol: 'Le', + name: 'Sierra Leone Leone', + ISO4217: '694', + }, + SOS: { + symbol: 'So.', + name: 'Somali Shilling', + ISO4217: '706', + }, + SRD: { + symbol: 'SRD', + name: 'Surinamese Dollar', + ISO4217: '968', + }, + STD: { + symbol: 'Db', + name: 'Sao Tome Dobra', + retired: true, + retirementDate: '2018-07-11', + ISO4217: '678', + }, + STN: { + symbol: 'Db', + name: 'Sao Tome Dobra', + ISO4217: '', + }, + SVC: { + symbol: 'SVC', + name: 'El Salvador Colon', + ISO4217: '222', + }, + SYP: { + symbol: 'SYP', + name: 'Syrian Pound', + ISO4217: '760', + }, + SZL: { + symbol: 'E', + name: 'Swaziland Lilageni', + ISO4217: '748', + }, + THB: { + symbol: '\u0e3f', + name: 'Thai Baht', + ISO4217: '764', + }, + TJS: { + symbol: 'TJS', + name: 'Tajikistani Somoni', + ISO4217: '972', + }, + TMT: { + symbol: 'm', + name: 'Turkmenistani Manat', + ISO4217: '934', + }, + TND: { + symbol: 'TND', + name: 'Tunisian Dinar', + ISO4217: '788', + }, + TOP: { + symbol: 'T$', + name: "Tonga Pa'ang", + ISO4217: '776', + }, + TRY: { + symbol: 'TL', + name: 'Turkish Lira', + ISO4217: '949', + }, + TTD: { + symbol: 'TT$', + name: 'Trinidad & Tobago Dollar', + ISO4217: '780', + }, + TWD: { + symbol: 'NT$', + name: 'Taiwan Dollar', + ISO4217: '901', + }, + TZS: { + symbol: 'TZS', + name: 'Tanzanian Shilling', + ISO4217: '834', + }, + UAH: { + symbol: '\u20b4', + name: 'Ukraine Hryvnia', + ISO4217: '980', + }, + UGX: { + symbol: 'USh', + name: 'Ugandan Shilling', + decimals: 0, + ISO4217: '800', + }, + USD: { + symbol: '$', + name: 'United States Dollar', + ISO4217: '840', + }, + UYU: { + symbol: '$U', + name: 'Uruguayan New Peso', + ISO4217: '858', + }, + UZS: { + symbol: 'UZS', + name: 'Uzbekistani Som', + ISO4217: '860', + }, + VEB: { + symbol: 'Bs.', + name: 'Venezuelan Bolivar', + retired: true, + retirementDate: '2008-02-01', + ISO4217: '', + }, + VEF: { + symbol: 'Bs.F', + name: 'Venezuelan Bolivar Fuerte', + retired: true, + retirementDate: '2018-08-20', + ISO4217: '937', + }, + VES: { + symbol: 'Bs.S', + name: 'Venezuelan Bolivar Soberano', + ISO4217: '928', + }, + VND: { + symbol: '\u20ab', + name: 'Vietnam Dong', + decimals: 0, + ISO4217: '704', + }, + VUV: { + symbol: 'Vt', + name: 'Vanuatu Vatu', + ISO4217: '548', + }, + WST: { + symbol: 'WS$', + name: 'Samoa Tala', + ISO4217: '882', + }, + XAF: { + symbol: 'FCFA', + name: 'CFA Franc (BEAC)', + decimals: 0, + ISO4217: '950', + }, + XCD: { + symbol: 'EC$', + name: 'East Caribbean Dollar', + ISO4217: '951', + }, + XOF: { + symbol: 'CFA', + name: 'CFA Franc (BCEAO)', + decimals: 0, + ISO4217: '952', + }, + XPF: { + symbol: 'XPF', + name: 'Pacific Franc', + decimals: 0, + ISO4217: '953', + }, + YER: { + symbol: 'YER', + name: 'Yemen Riyal', + ISO4217: '886', + }, + ZAR: { + symbol: 'R', + name: 'South African Rand', + ISO4217: '710', + }, + ZMK: { + symbol: 'ZK', + name: 'Zambian Kwacha', + retired: true, + retirementDate: '2013-01-01', + ISO4217: '894', + }, + ZMW: { + symbol: 'ZMW', + name: 'Zambian Kwacha', + cacheBurst: 1, + ISO4217: '967', + }, + }, + }, + { + onyxMethod: 'merge', + key: 'nvp_priorityMode', + value: 'default', + }, + { + onyxMethod: 'merge', + key: 'isFirstTimeNewExpensifyUser', + value: false, + }, + { + onyxMethod: 'merge', + key: 'preferredLocale', + value: 'en', + }, + { + onyxMethod: 'merge', + key: 'nvp_paypalMeAddress', + value: '', + }, + { + onyxMethod: 'merge', + key: 'preferredEmojiSkinTone', + value: 'default', + }, + { + onyxMethod: 'set', + key: 'frequentlyUsedEmojis', + value: [ + { + code: '\ud83e\udd11', + count: 155, + keywords: [ + 'rich', + 'money_mouth_face', + 'face', + 'money', + 'mouth', + ], + lastUpdatedAt: 1669657594, + name: 'money_mouth_face', + }, + { + code: '\ud83e\udd17', + count: 91, + keywords: [ + 'hugs', + 'face', + 'hug', + 'hugging', + ], + lastUpdatedAt: 1669660894, + name: 'hugs', + }, + { + code: '\ud83d\ude0d', + count: 68, + keywords: [ + 'love', + 'crush', + 'heart_eyes', + 'eye', + 'face', + 'heart', + 'smile', + ], + lastUpdatedAt: 1669659126, + name: 'heart_eyes', + }, + { + code: '\ud83e\udd14', + count: 56, + keywords: [ + 'thinking', + 'face', + ], + lastUpdatedAt: 1669661008, + name: 'thinking', + }, + { + code: '\ud83d\ude02', + count: 55, + keywords: [ + 'tears', + 'joy', + 'face', + 'laugh', + 'tear', + ], + lastUpdatedAt: 1670346435, + name: 'joy', + }, + { + code: '\ud83d\ude05', + count: 41, + keywords: [ + 'hot', + 'sweat_smile', + 'cold', + 'face', + 'open', + 'smile', + 'sweat', + ], + lastUpdatedAt: 1670346845, + name: 'sweat_smile', + }, + { + code: '\ud83d\ude04', + count: 37, + keywords: [ + 'happy', + 'joy', + 'laugh', + 'pleased', + 'smile', + 'eye', + 'face', + 'mouth', + 'open', + ], + lastUpdatedAt: 1669659306, + name: 'smile', + }, + { + code: '\ud83d\ude18', + count: 27, + keywords: [ + 'face', + 'heart', + 'kiss', + ], + lastUpdatedAt: 1670346848, + name: 'kissing_heart', + }, + { + code: '\ud83e\udd23', + count: 25, + keywords: [ + 'lol', + 'laughing', + 'rofl', + 'face', + 'floor', + 'laugh', + 'rolling', + ], + lastUpdatedAt: 1669659311, + name: 'rofl', + }, + { + code: '\ud83d\ude0b', + count: 18, + keywords: [ + 'tongue', + 'lick', + 'yum', + 'delicious', + 'face', + 'savouring', + 'smile', + 'um', + ], + lastUpdatedAt: 1669658204, + name: 'yum', + }, + { + code: '\ud83d\ude0a', + count: 17, + keywords: [ + 'proud', + 'blush', + 'eye', + 'face', + 'smile', + ], + lastUpdatedAt: 1669661018, + name: 'blush', + }, + { + code: '\ud83d\ude06', + count: 17, + keywords: [ + 'happy', + 'haha', + 'laughing', + 'satisfied', + 'face', + 'laugh', + 'mouth', + 'open', + 'smile', + ], + lastUpdatedAt: 1669659070, + name: 'laughing', + }, + { + code: '\ud83d\ude10', + count: 17, + keywords: [ + 'deadpan', + 'face', + 'neutral', + ], + lastUpdatedAt: 1669658922, + name: 'neutral_face', + }, + { + code: '\ud83d\ude03', + count: 17, + keywords: [ + 'happy', + 'joy', + 'haha', + 'smiley', + 'face', + 'mouth', + 'open', + 'smile', + ], + lastUpdatedAt: 1669636981, + name: 'smiley', + }, + { + code: '\ud83d\ude17', + count: 15, + keywords: [ + 'face', + 'kiss', + ], + lastUpdatedAt: 1669639079, + }, + { + code: '\ud83d\ude1a', + count: 14, + keywords: [ + 'kissing_closed_eyes', + 'closed', + 'eye', + 'face', + 'kiss', + ], + lastUpdatedAt: 1669660248, + name: 'kissing_closed_eyes', + }, + { + code: '\ud83d\ude19', + count: 12, + keywords: [ + 'kissing_smiling_eyes', + 'eye', + 'face', + 'kiss', + 'smile', + ], + lastUpdatedAt: 1669658208, + name: 'kissing_smiling_eyes', + }, + { + code: '\ud83e\udd10', + count: 11, + keywords: [ + 'face', + 'mouth', + 'zipper', + ], + lastUpdatedAt: 1670346432, + }, + { + code: '\ud83d\ude25', + count: 11, + keywords: [ + 'disappointed', + 'face', + 'relieved', + 'whew', + ], + lastUpdatedAt: 1669660257, + }, + { + code: '\ud83d\ude0e', + count: 11, + keywords: [ + 'bright', + 'cool', + 'eye', + 'eyewear', + 'face', + 'glasses', + 'smile', + 'sun', + 'sunglasses', + 'weather', + ], + lastUpdatedAt: 1669660252, + }, + { + code: '\ud83d\ude36', + count: 11, + keywords: [ + 'face', + 'mouth', + 'quiet', + 'silent', + ], + lastUpdatedAt: 1669659075, + name: 'no_mouth', + }, + { + code: '\ud83d\ude11', + count: 11, + keywords: [ + 'expressionless', + 'face', + 'inexpressive', + 'unexpressive', + ], + lastUpdatedAt: 1669640332, + }, + { + code: '\ud83d\ude0f', + count: 11, + keywords: [ + 'face', + 'smirk', + ], + lastUpdatedAt: 1666207075, + }, + { + code: '\ud83e\udd70', + count: 1, + keywords: [ + 'love', + 'smiling_face_with_three_hearts', + ], + lastUpdatedAt: 1670581230, + name: 'smiling_face_with_three_hearts', + }, + ], + }, + { + onyxMethod: 'merge', + key: 'private_blockedFromConcierge', + value: { + + }, + }, + { + onyxMethod: 'merge', + key: 'user', + value: { + isSubscribedToNewsletter: true, + validated: true, + isUsingExpensifyCard: true, + }, + }, + { + onyxMethod: 'set', + key: 'loginList', + value: { + 'applausetester+perf2@applause.expensifail.com': { + partnerName: 'expensify.com', + partnerUserID: 'applausetester+perf2@applause.expensifail.com', + validatedDate: '2022-08-01 05:00:48', + }, + }, + }, + { + onyxMethod: 'merge', + key: 'personalDetails', + value: { + 'fake2@gmail.com': { + login: 'fake2@gmail.com', + avatar: 'https://d1wpcgnaa73g0y.cloudfront.net/7a1fd3cdd41564cf04f4305140372b59d1dcd495_128.jpeg', + displayName: 'fake2@gmail.com', + pronouns: '__predefined_zeHirHirs', + timezone: { + automatic: false, + selected: 'Europe/Monaco', + }, + firstName: '', + lastName: '', + payPalMeAddress: 'qwerty', + phoneNumber: '', + validated: true, + }, + 'fake1@gmail.com': { + login: 'fake1@gmail.com', + avatar: 'https://d1wpcgnaa73g0y.cloudfront.net/d76dfb6912a0095cbfd2a02f64f4d9d2d9c33c29_128.jpeg', + displayName: '"Chat N Laz"', + pronouns: '__predefined_theyThemTheirs', + timezone: { + automatic: true, + selected: 'Europe/Athens', + }, + firstName: '"Chat N', + lastName: 'Laz"', + payPalMeAddress: '', + phoneNumber: '', + validated: true, + }, + 'fake4@gmail.com': { + login: 'fake4@gmail.com', + avatar: 'https://d1wpcgnaa73g0y.cloudfront.net/e769e0edf5fd0bc11cfa7c39ec2605c5310d26de_128.jpeg', + displayName: 'fake4@gmail.com', + pronouns: '', + timezone: { + automatic: true, + selected: 'Europe/Kiev', + }, + firstName: '', + lastName: '', + payPalMeAddress: '', + phoneNumber: '', + validated: true, + }, + 'fake3@gmail.com': { + login: 'fake3@gmail.com', + avatar: 'https://d1wpcgnaa73g0y.cloudfront.net/301e37631eca9e3127d6b668822e3a53771551f6_128.jpeg', + displayName: '123 Ios', + pronouns: '__predefined_perPers', + timezone: { + automatic: false, + selected: 'Europe/Helsinki', + }, + firstName: '123', + lastName: 'Ios', + payPalMeAddress: 'Wwerty', + phoneNumber: '', + validated: true, + }, + 'fake5@gmail.com': { + login: 'fake5@gmail.com', + avatar: 'https://d1wpcgnaa73g0y.cloudfront.net/2810a38b66d9a60fe41a9cf39c9fd6ecbe2cb35f_128.jpeg', + displayName: 'Qqq Qqq', + pronouns: '__predefined_sheHerHers', + timezone: { + automatic: false, + selected: 'Europe/Lisbon', + }, + firstName: 'Qqq', + lastName: 'Qqq', + payPalMeAddress: '', + phoneNumber: '', + validated: true, + }, + 'andreylazutkinutest@gmail.com': { + login: 'andreylazutkinutest@gmail.com', + avatar: 'https://d1wpcgnaa73g0y.cloudfront.net/2af13161ffcc95fc807769bb22c013c32280f338_128.jpeg', + displayName: 'Main Ios🏴󠁧󠁢󠁳󠁣󠁴󠁿ios', + pronouns: '__predefined_heHimHis', + timezone: { + automatic: false, + selected: 'Europe/Belfast', + }, + firstName: 'Main', + lastName: 'Ios🏴󠁧󠁢󠁳󠁣󠁴󠁿ios', + payPalMeAddress: '', + phoneNumber: '', + validated: true, + }, + 'applausetester+0604lsn@applause.expensifail.com': { + login: 'applausetester+0604lsn@applause.expensifail.com', + avatar: 'https://d1wpcgnaa73g0y.cloudfront.net/ad20184a011ed54383d69e4fe68658522583cbb8_128.jpeg', + displayName: '0604 Lsn', + pronouns: '__predefined_zeHirHirs', + timezone: { + automatic: false, + selected: 'America/Costa_Rica', + }, + firstName: '0604', + lastName: 'Lsn', + payPalMeAddress: '12345', + phoneNumber: '', + validated: true, + }, + 'applausetester+0704sveta@applause.expensifail.com': { + login: 'applausetester+0704sveta@applause.expensifail.com', + avatar: 'https://d1wpcgnaa73g0y.cloudfront.net/63cc4a392cc64ba1c8f6a1b90d5f1441a23270d1_128.jpeg', + displayName: '07 04 0704 Lsn lsn', + pronouns: '__predefined_callMeByMyName', + timezone: { + automatic: false, + selected: 'Africa/Freetown', + }, + firstName: '07 04 0704', + lastName: 'Lsn lsn', + payPalMeAddress: '', + phoneNumber: '', + validated: true, + }, + 'applausetester+0707abb@applause.expensifail.com': { + login: 'applausetester+0707abb@applause.expensifail.com', + avatar: 'https://d2k5nsl2zxldvw.cloudfront.net/images/avatars/avatar_5.png', + displayName: 'Katya Becciv', + pronouns: '__predefined_sheHerHers', + timezone: { + automatic: false, + selected: 'US/Eastern', + }, + firstName: 'Katya', + lastName: 'Becciv', + payPalMeAddress: 'testing', + phoneNumber: '', + validated: true, + }, + 'applausetester+0901abb@applause.expensifail.com': { + login: 'applausetester+0901abb@applause.expensifail.com', + avatar: 'https://d1wpcgnaa73g0y.cloudfront.net/0f6e999ba61695599f092b7652c1e159aee62c65_128.jpeg', + displayName: 'Katie Becciv', + pronouns: '__predefined_faeFaer', + timezone: { + automatic: false, + selected: 'Africa/Accra', + }, + firstName: 'Katie', + lastName: 'Becciv', + payPalMeAddress: 'kbecciv', + phoneNumber: '', + validated: true, + }, + 'applausetester+1904lsn@applause.expensifail.com': { + login: 'applausetester+1904lsn@applause.expensifail.com', + avatar: 'https://d2k5nsl2zxldvw.cloudfront.net/images/avatars/avatar_5.png', + displayName: '11 11', + pronouns: '', + timezone: { + automatic: true, + selected: 'Europe/Athens', + }, + firstName: '11', + lastName: '11', + payPalMeAddress: '', + phoneNumber: '', + validated: true, + }, + 'applausetester+42222abb@applause.expensifail.com': { + login: 'applausetester+42222abb@applause.expensifail.com', + avatar: 'https://d1wpcgnaa73g0y.cloudfront.net/d166c112f300a6e30bc70752cd394c3fde099e4f_128.jpeg', + displayName: '"First"', + pronouns: '', + timezone: { + automatic: true, + selected: 'America/New_York', + }, + firstName: '"First"', + lastName: '', + payPalMeAddress: '444555', + phoneNumber: '', + validated: true, + }, + 'applausetester+bernardo@applause.expensifail.com': { + login: 'applausetester+bernardo@applause.expensifail.com', + avatar: 'https://d1wpcgnaa73g0y.cloudfront.net/803733b7038bbd5e543315fa9c6c0118eda227af_128.jpeg', + displayName: 'bernardo utest', + pronouns: '', + timezone: { + automatic: true, + selected: 'America/Los_Angeles', + }, + firstName: 'bernardo', + lastName: 'utest', + payPalMeAddress: '', + phoneNumber: '', + validated: false, + }, + 'applausetester+ihchat4@applause.expensifail.com': { + login: 'applausetester+ihchat4@applause.expensifail.com', + avatar: 'https://d1wpcgnaa73g0y.cloudfront.net/1008dcaadc12badbddf4720dcb7ad99b7384c613_128.jpeg', + displayName: 'Chat HT', + pronouns: '__predefined_callMeByMyName', + timezone: { + automatic: true, + selected: 'Europe/Kiev', + }, + firstName: 'Chat', + lastName: 'HT', + payPalMeAddress: '111', + phoneNumber: '', + validated: true, + }, + 'applausetester+pd1005@applause.expensifail.com': { + login: 'applausetester+pd1005@applause.expensifail.com', + avatar: 'https://d1wpcgnaa73g0y.cloudfront.net/86c9b7dce35aea83b69c6e825a4b3d00a87389b7_128.jpeg', + displayName: 'applausetester+pd1005@applause.expensifail.com', + pronouns: '', + timezone: { + automatic: 'true', + selected: 'Europe/Lisbon', + }, + firstName: '', + lastName: '', + payPalMeAddress: '', + phoneNumber: '', + validated: true, + }, + 'fake6@gmail.com': { + login: 'fake6@gmail.com', + avatar: 'https://d2k5nsl2zxldvw.cloudfront.net/images/avatars/avatar_7.png', + displayName: 'fake6@gmail.com', + pronouns: '', + timezone: { + automatic: true, + selected: 'Europe/Warsaw', + }, + firstName: '', + lastName: '', + payPalMeAddress: '', + phoneNumber: '', + validated: true, + }, + 'applausetester+perf2@applause.expensifail.com': { + login: 'applausetester+perf2@applause.expensifail.com', + avatar: 'https://d1wpcgnaa73g0y.cloudfront.net/1486f9cc6367d8c399ee453ad5b686d157bb4dda_128.jpeg', + displayName: 'applausetester+perf2@applause.expensifail.com', + pronouns: '', + timezone: { + automatic: true, + selected: 'America/Los_Angeles', + }, + firstName: '', + lastName: '', + payPalMeAddress: '', + phoneNumber: '', + validated: true, + localCurrencyCode: 'USD', + }, + 'fake7@gmail.com': { + login: 'fake7@gmail.com', + avatar: 'https://d2k5nsl2zxldvw.cloudfront.net/images/avatars/avatar_2.png', + displayName: 'fake7@gmail.com', + pronouns: '', + timezone: { + automatic: true, + selected: 'America/Toronto', + }, + firstName: '', + lastName: '', + payPalMeAddress: '', + phoneNumber: '', + validated: true, + }, + 'fake8@gmail.com': { + login: 'fake8@gmail.com', + avatar: 'https://d1wpcgnaa73g0y.cloudfront.net/7b0a9cf9c93987053be9d6cc707cb1f091a1ef46_128.jpeg', + displayName: 'fake8@gmail.com', + pronouns: '', + timezone: { + automatic: true, + selected: 'Europe/Paris', + }, + firstName: '', + lastName: '', + payPalMeAddress: '', + phoneNumber: '', + validated: true, + }, + 'applausetester@applause.expensifail.com': { + login: 'applausetester@applause.expensifail.com', + avatar: 'https://d1wpcgnaa73g0y.cloudfront.net/8ddbb1a4675883ea12b3021f698a8b2dcfc18d42_128.jpeg', + displayName: 'Applause Main Account', + pronouns: '__predefined_coCos', + timezone: { + automatic: 'true', + selected: 'Europe/Kiev', + }, + firstName: 'Applause', + lastName: 'Main Account', + payPalMeAddress: 'ss', + phoneNumber: '', + validated: true, + }, + 'christoph+hightraffic@margelo.io': { + login: 'christoph+hightraffic@margelo.io', + avatar: 'https://d2k5nsl2zxldvw.cloudfront.net/images/avatars/avatar_1.png', + displayName: 'Christoph Pader', + pronouns: '', + timezone: { + automatic: true, + selected: 'Europe/Vienna', + }, + firstName: 'Christoph', + lastName: 'Pader', + payPalMeAddress: '', + phoneNumber: '', + validated: true, + }, + 'concierge@expensify.com': { + login: 'concierge@expensify.com', + avatar: 'https://d2k5nsl2zxldvw.cloudfront.net/images/icons/concierge_2022.png', + displayName: 'Concierge', + pronouns: '', + timezone: { + automatic: true, + selected: 'Europe/Moscow', + }, + firstName: 'Concierge', + lastName: '', + payPalMeAddress: '', + phoneNumber: '', + validated: true, + }, + 'svetlanalazutkinautest+0211@gmail.com': { + login: 'svetlanalazutkinautest+0211@gmail.com', + avatar: 'https://d2k5nsl2zxldvw.cloudfront.net/images/avatars/avatar_6.png', + displayName: 'Chat S', + pronouns: '', + timezone: { + automatic: true, + selected: 'Europe/Kiev', + }, + firstName: 'Chat S', + lastName: '', + payPalMeAddress: '', + phoneNumber: '', + validated: true, + }, + 'tayla.lay@team.expensify.com': { + login: 'tayla.lay@team.expensify.com', + avatar: 'https://d1wpcgnaa73g0y.cloudfront.net/d3196c27ed6bdb2df741af29a3ccfdb0f9919c41_128.jpeg', + displayName: 'Tayla Simmons', + pronouns: '__predefined_sheHerHers', + timezone: { + automatic: true, + selected: 'America/Chicago', + }, + firstName: 'Tayla', + lastName: 'Simmons', + payPalMeAddress: '', + phoneNumber: '', + validated: true, + }, + }, + }, + { + onyxMethod: 'set', + key: 'betas', + value: [ + 'all', + 'pdfMetaStore', + 'reportActionContextMenu', + 'submitPolicy', + 'attendees', + 'autoExport', + 'autoExportIntacct', + 'autoExportQbo', + 'autoExportXero', + 'autoJoinPolicy', + 'automatedTaxExemption', + 'billPay', + 'categoryDefaultTax', + 'collectableDepositAccounts', + 'conciergeTravel', + 'connectedCards', + 'discrepancy', + 'domainContactBilling', + 'domainTwoFactorAuth', + 'duplicateDetection', + 'emailSuppressionBeta', + 'expensesV2', + 'expensifyCard', + 'expensifyCardIntacctReconciliation', + 'expensifyCardNetSuiteReconciliation', + 'expensifyCardQBOReconciliation', + 'expensifyCardRapidIncreaseFraud', + 'expensifyCardXeroReconciliation', + 'expensifyOrg', + 'fixViolationPushNotification', + 'freePlan', + 'freePlanFullLaunch', + 'freePlanSoftLaunch', + 'gusto', + 'inboxCache', + 'inboxHiddenTasks', + 'indirectIntegrationSetup', + 'IOU', + 'joinPolicy', + 'loadPolicyAsync', + 'mapReceipt', + 'mergeAPI', + 'mobileRealtimeReportComments', + 'mobileSecureReceipts', + 'monthlySettlement', + 'namesAndAvatars', + 'nativeChat', + 'newPricing', + 'newsletterThree', + 'nextSteps', + 'openFaceHamburger', + 'pdfMetaStore', + 'perDiem', + 'perDiemInternational', + 'pricingCopyChanges', + 'qboInvoices', + 'quickbooksDesktopV2', + 'realtimeReportComments', + 's2wAnnouncement', + 'scheduledAutoReporting', + 'secureReceipts', + 'secureReceiptsReports', + 'selfServiceHardLaunch', + 'sendMoney', + 'smartScanUserDisputes', + 'smsSignUp', + 'stripeConnect', + 'submitPolicy', + 'summaryEmail', + 'swipeToWin', + 'taxForMileage', + 'twoFactorAuth', + 'venmoIntegration', + 'zenefitsIntegration', + ], + }, + { + onyxMethod: 'merge', + key: 'countryCode', + value: 1, + }, + { + onyxMethod: 'merge', + key: 'account', + value: { + requiresTwoFactorAuth: false, + }, + }, + { + onyxMethod: 'mergecollection', + key: 'policy_', + value: { + policy_28493C792FA01DAE: { + isFromFullPolicy: false, + id: '28493C792FA01DAE', + name: "applausetester+perf2's Expenses", + role: 'admin', + type: 'personal', + owner: 'applausetester+perf2@applause.expensifail.com', + outputCurrency: 'USD', + avatar: '', + employeeList: [ + + ], + }, + policy_A6511FF8D2EE7661: { + isFromFullPolicy: false, + id: 'A6511FF8D2EE7661', + name: "Applause's Workspace", + role: 'admin', + type: 'free', + owner: 'applausetester+perf2@applause.expensifail.com', + outputCurrency: 'INR', + avatar: '', + employeeList: [ + + ], + }, + }, + }, + { + onyxMethod: 'mergecollection', + key: 'report_', + value: { + report_98258097: { + reportID: '98258097', + reportName: 'Chat Report', + chatType: '', + ownerEmail: '__fake__', + policyID: '_FAKE_', + maxSequenceNumber: 2, + participants: [ + 'concierge@expensify.com', + ], + isPinned: true, + lastVisitedTimestamp: 1671126234191, + lastReadTimestamp: 1671126234191, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 2, + lastActionCreated: '2022-08-03 06:45:00', + lastMessageTimestamp: 1659509100000, + lastMessageText: 'You can easily track, approve, and pay bills in Expensify with your custom compa', + lastActorEmail: 'concierge@expensify.com', + notificationPreference: 'always', + stateNum: 0, + statusNum: 0, + oldPolicyName: '', + visibility: null, + isOwnPolicyExpenseChat: false, + lastMessageHtml: 'You can easily track, approve, and pay bills in Expensify with your custom company bill pay email address: ' + + 'applause.expensifail.com@expensify.cash. Learn more ' + + 'here.' + + ' For questions, just reply to this message.', + hasOutstandingIOU: false, + }, + report_98258458: { + reportID: '98258458', + reportName: '', + chatType: 'policyExpenseChat', + ownerEmail: 'applausetester+perf2@applause.expensifail.com', + policyID: 'C28C2634DD7226B8', + maxSequenceNumber: 1, + participants: [ + 'applausetester@applause.expensifail.com', + 'applausetester+perf2@applause.expensifail.com', + ], + isPinned: false, + lastVisitedTimestamp: 1669129206943, + lastReadTimestamp: 1669129206943, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 1, + lastActionCreated: '2022-11-03 20:30:55.599', + lastMessageTimestamp: 1667507455599, + lastMessageText: '', + lastActorEmail: 'applausetester@applause.expensifail.com', + notificationPreference: 'always', + stateNum: 2, + statusNum: 2, + oldPolicyName: 'Crowded Policy - Definitive Edition', + visibility: null, + isOwnPolicyExpenseChat: true, + lastMessageHtml: '', + hasOutstandingIOU: false, + }, + report_98344717: { + reportID: '98344717', + reportName: 'Chat Report', + chatType: '', + ownerEmail: '__fake__', + policyID: '_FAKE_', + maxSequenceNumber: 8, + participants: [ + 'applausetester+ihchat4@applause.expensifail.com', + ], + isPinned: false, + lastVisitedTimestamp: 1671205050152, + lastReadTimestamp: 1671205050152, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 8, + lastActionCreated: '2022-08-02 20:03:42', + lastMessageTimestamp: 1659470622000, + lastMessageText: 'Requested \u20b41.67 from applausetester+perf2@applause.expensifail.com', + lastActorEmail: 'applausetester+ihchat4@applause.expensifail.com', + notificationPreference: 'always', + stateNum: 0, + statusNum: 0, + oldPolicyName: '', + visibility: null, + isOwnPolicyExpenseChat: false, + lastMessageHtml: 'Requested \u20b41.67 from applausetester+perf2@applause.expensifail.com', + hasOutstandingIOU: false, + }, + report_98345050: { + reportID: '98345050', + reportName: 'Chat Report', + chatType: '', + ownerEmail: '__fake__', + policyID: '_FAKE_', + maxSequenceNumber: 17, + participants: [ + 'fake3@gmail.com', + ], + isPinned: false, + lastVisitedTimestamp: 1671210740419, + lastReadTimestamp: 1671210740419, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 17, + lastActionCreated: '2022-11-04 21:18:00.038', + lastMessageTimestamp: 1667596680038, + lastMessageText: 'Cancelled the \u20b440.00 request', + lastActorEmail: 'fake3@gmail.com', + notificationPreference: 'always', + stateNum: 0, + statusNum: 0, + oldPolicyName: '', + visibility: null, + isOwnPolicyExpenseChat: false, + lastMessageHtml: 'Cancelled the \u20b440.00 request', + hasOutstandingIOU: false, + }, + report_98345315: { + reportID: '98345315', + reportName: 'Chat Report', + chatType: '', + ownerEmail: '__fake__', + policyID: '_FAKE_', + maxSequenceNumber: 2, + participants: [ + 'fake3@gmail.com', + 'fake6@gmail.com', + 'fake7@gmail.com', + 'fake8@gmail.com', + ], + isPinned: false, + lastVisitedTimestamp: 1671209362667, + lastReadTimestamp: 1671209362667, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 2, + lastActionCreated: '2022-08-01 20:48:16', + lastMessageTimestamp: 1659386896000, + lastMessageText: 'applausetester+perf2@applause.expensifail.com', + lastActorEmail: 'fake3@gmail.com', + notificationPreference: 'always', + stateNum: 0, + statusNum: 0, + oldPolicyName: '', + visibility: null, + isOwnPolicyExpenseChat: false, + lastMessageHtml: 'applausetester+perf2@applause.expensifail.com', + hasOutstandingIOU: false, + }, + report_98345625: { + reportID: '98345625', + reportName: 'Chat Report', + chatType: '', + ownerEmail: '__fake__', + policyID: '_FAKE_', + maxSequenceNumber: 1, + participants: [ + 'fake1@gmail.com', + 'fake2@gmail.com', + 'fake3@gmail.com', + 'fake4@gmail.com', + 'fake5@gmail.com', + 'fake6@gmail.com', + 'fake7@gmail.com', + 'fake8@gmail.com', + ], + isPinned: false, + lastVisitedTimestamp: 1671470568415, + lastReadTimestamp: 1671470568415, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 1, + lastActionCreated: '2022-08-01 20:49:11', + lastMessageTimestamp: 1659386951000, + lastMessageText: 'Say hello\ud83d\ude10', + lastActorEmail: 'fake3@gmail.com', + notificationPreference: 'always', + stateNum: 0, + statusNum: 0, + oldPolicyName: '', + visibility: null, + isOwnPolicyExpenseChat: false, + lastMessageHtml: 'Say hello\ud83d\ude10', + hasOutstandingIOU: false, + }, + report_98345679: { + reportID: '98345679', + reportName: '', + chatType: 'policyExpenseChat', + ownerEmail: 'applausetester+perf2@applause.expensifail.com', + policyID: '1CE001C4B9F3CA54', + maxSequenceNumber: 2, + participants: [ + 'fake3@gmail.com', + 'applausetester+perf2@applause.expensifail.com', + ], + isPinned: false, + lastVisitedTimestamp: 1664363369565, + lastReadTimestamp: 1664363369565, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 2, + lastActionCreated: '2022-08-16 12:30:57', + lastMessageTimestamp: 1660653057000, + lastMessageText: '', + lastActorEmail: 'fake3@gmail.com', + notificationPreference: 'always', + stateNum: 2, + statusNum: 2, + oldPolicyName: "Andreylazutkinutest+123's workspace", + visibility: null, + isOwnPolicyExpenseChat: true, + lastMessageHtml: '', + hasOutstandingIOU: false, + }, + report_98414813: { + reportID: '98414813', + reportName: 'Chat Report', + chatType: '', + ownerEmail: '__fake__', + policyID: '_FAKE_', + maxSequenceNumber: 1, + participants: [ + 'applausetester+ihchat4@applause.expensifail.com', + 'fake6@gmail.com', + ], + isPinned: false, + lastVisitedTimestamp: 1669197163626, + lastReadTimestamp: 1669197163626, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 1, + lastActionCreated: '2022-08-02 20:03:41', + lastMessageTimestamp: 1659470621000, + lastMessageText: 'Split \u20b45.00 with applausetester+perf2@applause.expensifail.com and applauseteste', + lastActorEmail: 'applausetester+ihchat4@applause.expensifail.com', + notificationPreference: 'always', + stateNum: 0, + statusNum: 0, + oldPolicyName: '', + visibility: null, + isOwnPolicyExpenseChat: false, + lastMessageHtml: 'Split \u20b45.00 with applausetester+perf2@applause.expensifail.com and fake6@gmail.com', + hasOutstandingIOU: false, + }, + report_98817646: { + reportID: '98817646', + reportName: 'Chat Report', + chatType: '', + ownerEmail: '__fake__', + policyID: '_FAKE_', + maxSequenceNumber: 1787, + participants: [ + 'fake6@gmail.com', + ], + isPinned: false, + lastVisitedTimestamp: 1671214557025, + lastReadTimestamp: 1671214557025, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 1787, + lastActionCreated: '2022-12-09 10:17:18.362', + lastMessageTimestamp: 1670581038362, + lastMessageText: 'RR', + lastActorEmail: 'applausetester+perf2@applause.expensifail.com', + notificationPreference: 'always', + stateNum: 0, + statusNum: 0, + oldPolicyName: '', + visibility: null, + isOwnPolicyExpenseChat: false, + lastMessageHtml: 'RR', + hasOutstandingIOU: true, + iouReportID: 2543745284790730, + }, + report_358751490033727: { + reportID: '358751490033727', + reportName: '#digimobileroom', + chatType: 'policyRoom', + ownerEmail: '__fake__', + policyID: 'C28C2634DD7226B8', + maxSequenceNumber: 3, + participants: [ + 'applausetester+pd1005@applause.expensifail.com', + ], + isPinned: false, + lastVisitedTimestamp: 1669298874528, + lastReadTimestamp: 1669298874528, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 3, + lastActionCreated: '2022-10-12 17:47:45.228', + lastMessageTimestamp: 1665596865228, + lastMessageText: 'STAGING_CHAT_MESSAGE_A2C534B7-3509-416E-A0AD-8463831C29DD', + lastActorEmail: 'applausetester+fachat1@applause.expensifail.com', + notificationPreference: 'daily', + stateNum: 0, + statusNum: 0, + oldPolicyName: '', + visibility: 'restricted', + isOwnPolicyExpenseChat: false, + lastMessageHtml: 'STAGING_CHAT_MESSAGE_A2C534B7-3509-416E-A0AD-8463831C29DD', + hasOutstandingIOU: false, + }, + report_663424408122117: { + reportID: '663424408122117', + reportName: '#announce', + chatType: 'policyAnnounce', + ownerEmail: '__fake__', + policyID: 'A6511FF8D2EE7661', + maxSequenceNumber: 0, + participants: [ + 'applausetester+perf2@applause.expensifail.com', + ], + isPinned: false, + lastVisitedTimestamp: 0, + lastReadTimestamp: 0, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 0, + lastActionCreated: '', + lastMessageTimestamp: 0, + lastMessageText: '', + lastActorEmail: '', + notificationPreference: 'daily', + stateNum: 0, + statusNum: 0, + oldPolicyName: '', + visibility: null, + isOwnPolicyExpenseChat: false, + lastMessageHtml: '', + hasOutstandingIOU: false, + }, + report_944123936554214: { + reportID: '944123936554214', + reportName: '', + chatType: 'policyExpenseChat', + ownerEmail: 'applausetester+perf2@applause.expensifail.com', + policyID: 'A6511FF8D2EE7661', + maxSequenceNumber: 0, + participants: [ + 'applausetester+perf2@applause.expensifail.com', + ], + isPinned: false, + lastVisitedTimestamp: 1669122367932, + lastReadTimestamp: 1669122367932, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 0, + lastActionCreated: '', + lastMessageTimestamp: 0, + lastMessageText: '', + lastActorEmail: '', + notificationPreference: 'always', + stateNum: 0, + statusNum: 0, + oldPolicyName: '', + visibility: null, + isOwnPolicyExpenseChat: true, + lastMessageHtml: '', + hasOutstandingIOU: false, + }, + report_2242399088152511: { + reportID: '2242399088152511', + reportName: 'Chat Report', + chatType: '', + ownerEmail: '__fake__', + policyID: '_FAKE_', + maxSequenceNumber: 3, + participants: [ + 'concierge@expensify.com', + 'applausetester+0901abb@applause.expensifail.com', + 'andreylazutkinutest@gmail.com', + 'applausetester+0704sveta@applause.expensifail.com', + 'fake3@gmail.com', + ], + isPinned: false, + lastVisitedTimestamp: 1671211239096, + lastReadTimestamp: 1671211239096, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 3, + lastActionCreated: '2022-11-03 20:48:58.815', + lastMessageTimestamp: 1667508538815, + lastMessageText: 'Hi there, thanks for reaching out! How may I help?', + lastActorEmail: 'concierge@expensify.com', + notificationPreference: 'always', + stateNum: 0, + statusNum: 0, + oldPolicyName: '', + visibility: null, + isOwnPolicyExpenseChat: false, + lastMessageHtml: '

Hi there, thanks for reaching out! How may I help?

', + hasOutstandingIOU: false, + }, + report_2576922422943214: { + reportID: '2576922422943214', + reportName: 'Chat Report', + chatType: '', + ownerEmail: '__fake__', + policyID: '_FAKE_', + maxSequenceNumber: 1, + participants: [ + 'applausetester+42222abb@applause.expensifail.com', + ], + isPinned: false, + lastVisitedTimestamp: 1671213666675, + lastReadTimestamp: 1671213666675, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 1, + lastActionCreated: '2022-12-01 08:05:11.009', + lastMessageTimestamp: 1669881911009, + lastMessageText: 'Test', + lastActorEmail: 'applausetester+perf2@applause.expensifail.com', + notificationPreference: 'always', + stateNum: 0, + statusNum: 0, + oldPolicyName: '', + visibility: null, + isOwnPolicyExpenseChat: false, + lastMessageHtml: 'Test', + hasOutstandingIOU: false, + }, + report_2752461403207161: { + reportID: '2752461403207161', + reportName: 'Chat Report', + chatType: '', + ownerEmail: '__fake__', + policyID: '_FAKE_', + maxSequenceNumber: 0, + participants: [ + 'fake1@gmail.com', + ], + isPinned: false, + lastVisitedTimestamp: 1669300587843, + lastReadTimestamp: 1669300587843, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 0, + lastActionCreated: '', + lastMessageTimestamp: 0, + lastMessageText: '', + lastActorEmail: '', + notificationPreference: 'always', + stateNum: 0, + statusNum: 0, + oldPolicyName: '', + visibility: null, + isOwnPolicyExpenseChat: false, + lastMessageHtml: '', + hasOutstandingIOU: false, + }, + report_3785654888638968: { + reportID: '3785654888638968', + reportName: '#jack', + chatType: 'policyRoom', + ownerEmail: '__fake__', + policyID: 'C28C2634DD7226B8', + maxSequenceNumber: 3, + participants: [ + 'applausetester+pd1005@applause.expensifail.com', + ], + isPinned: false, + lastVisitedTimestamp: 1669881965738, + lastReadTimestamp: 1669881965738, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 3, + lastActionCreated: '2022-10-12 12:20:00.668', + lastMessageTimestamp: 1665577200668, + lastMessageText: 'Room renamed to #jack', + lastActorEmail: 'applausetester+pd1005@applause.expensifail.com', + notificationPreference: 'daily', + stateNum: 0, + statusNum: 0, + oldPolicyName: '', + visibility: 'restricted', + isOwnPolicyExpenseChat: false, + lastMessageHtml: 'Room renamed to #jack', + hasOutstandingIOU: false, + }, + report_4867098979334014: { + reportID: '4867098979334014', + reportName: 'Chat Report', + chatType: '', + ownerEmail: '__fake__', + policyID: '_FAKE_', + maxSequenceNumber: 2, + participants: [ + 'christoph+hightraffic@margelo.io', + ], + isPinned: false, + lastVisitedTimestamp: 1671214566347, + lastReadTimestamp: 1671214566347, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 2, + lastActionCreated: '2022-12-16 18:14:00.208', + lastMessageTimestamp: 1671214440208, + lastMessageText: 'Requested \u20ac200.00 from Christoph for Essen mit Kunden', + lastActorEmail: 'applausetester+perf2@applause.expensifail.com', + notificationPreference: 'always', + stateNum: 0, + statusNum: 0, + oldPolicyName: '', + visibility: null, + isOwnPolicyExpenseChat: false, + lastMessageHtml: 'Requested \u20ac200.00 from Christoph for Essen mit Kunden', + hasOutstandingIOU: true, + iouReportID: 4249286573496381, + }, + report_5277760851229035: { + reportID: '5277760851229035', + reportName: '#kasper_tha_cat', + chatType: 'policyRoom', + ownerEmail: '__fake__', + policyID: 'C28C2634DD7226B8', + maxSequenceNumber: 13, + participants: [ + 'applausetester+pd1005@applause.expensifail.com', + ], + isPinned: false, + lastVisitedTimestamp: 1670955487510, + lastReadTimestamp: 1670955487510, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 13, + lastActionCreated: '2022-11-29 12:38:15.985', + lastMessageTimestamp: 1669725495985, + lastMessageText: 'fff', + lastActorEmail: 'fake6@gmail.com', + notificationPreference: 'daily', + stateNum: 0, + statusNum: 0, + oldPolicyName: '', + visibility: 'restricted', + isOwnPolicyExpenseChat: false, + lastMessageHtml: 'fff
f
f
f
f
f
f
f
f

f
f
f
f

f
' + + 'f
f
f
f
f

f
f
f
f
f
ff', + hasOutstandingIOU: false, + }, + report_5324367938904284: { + reportID: '5324367938904284', + reportName: '#applause.expensifail.com', + chatType: 'domainAll', + ownerEmail: '+@applause.expensifail.com', + policyID: '_FAKE_', + maxSequenceNumber: 17, + participants: [ + 'applausetester+bernardo@applause.expensifail.com', + ], + isPinned: false, + lastVisitedTimestamp: 1669634659097, + lastReadTimestamp: 1669634659097, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 14, + lastActionCreated: '2022-11-29 21:08:00.793', + lastMessageTimestamp: 1669756080793, + lastMessageText: 'Iviviviv8b', + lastActorEmail: 'applausetester+0901abb@applause.expensifail.com', + notificationPreference: 'daily', + stateNum: 0, + statusNum: 0, + oldPolicyName: '', + visibility: null, + isOwnPolicyExpenseChat: false, + lastMessageHtml: 'Iviviviv8b', + hasOutstandingIOU: false, + }, + report_5654270288238256: { + reportID: '5654270288238256', + reportName: 'Chat Report', + chatType: '', + ownerEmail: '__fake__', + policyID: '_FAKE_', + maxSequenceNumber: 0, + participants: [ + 'andreylazutkinutest@gmail.com', + 'fake1@gmail.com', + 'applausetester+0707abb@applause.expensifail.com', + 'fake3@gmail.com', + 'fake5@gmail.com', + 'applausetester+0604lsn@applause.expensifail.com', + 'andreylazutkinutest+0904@gmail.com', + 'applausetester+1904lsn@applause.expensifail.com', + ], + isPinned: false, + lastVisitedTimestamp: 1669129467258, + lastReadTimestamp: 1669129467258, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 0, + lastActionCreated: '', + lastMessageTimestamp: 0, + lastMessageText: '', + lastActorEmail: '', + notificationPreference: 'always', + stateNum: 0, + statusNum: 0, + oldPolicyName: '', + visibility: null, + isOwnPolicyExpenseChat: false, + lastMessageHtml: '', + hasOutstandingIOU: false, + }, + report_6194900075541844: { + reportID: '6194900075541844', + reportName: '#admins', + chatType: 'policyAdmins', + ownerEmail: '__fake__', + policyID: 'A6511FF8D2EE7661', + maxSequenceNumber: 0, + participants: [ + 'applausetester+perf2@applause.expensifail.com', + ], + isPinned: false, + lastVisitedTimestamp: 0, + lastReadTimestamp: 0, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 0, + lastActionCreated: '', + lastMessageTimestamp: 0, + lastMessageText: '', + lastActorEmail: '', + notificationPreference: 'always', + stateNum: 0, + statusNum: 0, + oldPolicyName: '', + visibility: null, + isOwnPolicyExpenseChat: false, + lastMessageHtml: '', + hasOutstandingIOU: false, + }, + report_6801643744224146: { + reportID: '6801643744224146', + reportName: 'Chat Report', + chatType: '', + ownerEmail: '__fake__', + policyID: '_FAKE_', + maxSequenceNumber: 1, + participants: [ + 'concierge@expensify.com', + 'andreylazutkinutest@gmail.com', + 'fake1@gmail.com', + 'svetlanalazutkinautest+0211@gmail.com', + 'applausetester+0707abb@applause.expensifail.com', + 'fake3@gmail.com', + 'fake5@gmail.com', + 'applausetester+0604lsn@applause.expensifail.com', + ], + isPinned: false, + lastVisitedTimestamp: 1671211247254, + lastReadTimestamp: 1671211247254, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 1, + lastActionCreated: '2022-09-15 12:57:59.526', + lastMessageTimestamp: 1663246679526, + lastMessageText: "\ud83d\udc4b Welcome to Expensify! I'm Concierge. Is there anything I can help with? Click ", + lastActorEmail: 'concierge@expensify.com', + notificationPreference: 'always', + stateNum: 0, + statusNum: 0, + oldPolicyName: '', + visibility: null, + isOwnPolicyExpenseChat: false, + lastMessageHtml: "\ud83d\udc4b Welcome to Expensify! I'm Concierge. Is there anything I can help with? Click the + icon on the homescreen to explore the features you" + + ' can use.', + hasOutstandingIOU: false, + }, + report_7658708888047100: { + reportID: '7658708888047100', + reportName: 'Chat Report', + chatType: '', + ownerEmail: '__fake__', + policyID: '_FAKE_', + maxSequenceNumber: 3, + participants: [ + 'concierge@expensify.com', + 'andreylazutkinutest@gmail.com', + 'fake3@gmail.com', + 'fake5@gmail.com', + 'tayla.lay@team.expensify.com', + 'andreylazutkinutest+160956@gmail.com', + ], + isPinned: false, + lastVisitedTimestamp: 1669634649909, + lastReadTimestamp: 1669634649909, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 3, + lastActionCreated: '2022-09-16 11:12:46.739', + lastMessageTimestamp: 1663326766739, + lastMessageText: 'Hi there! How can I help?\u00a0', + lastActorEmail: 'concierge@expensify.com', + notificationPreference: 'always', + stateNum: 0, + statusNum: 0, + oldPolicyName: '', + visibility: null, + isOwnPolicyExpenseChat: false, + lastMessageHtml: 'Hi there! How can I help?\u00a0', + hasOutstandingIOU: false, + }, + report_7756405299640824: { + reportID: '7756405299640824', + reportName: '#jackd23', + chatType: 'policyRoom', + ownerEmail: '__fake__', + policyID: 'C28C2634DD7226B8', + maxSequenceNumber: 1, + participants: [ + 'applausetester+pd1005@applause.expensifail.com', + ], + isPinned: false, + lastVisitedTimestamp: 1669197883208, + lastReadTimestamp: 1669197883208, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 1, + lastActionCreated: '2022-10-12 12:46:43.577', + lastMessageTimestamp: 1665578803577, + lastMessageText: 'Room renamed to #jackd23', + lastActorEmail: 'applausetester+pd1005@applause.expensifail.com', + notificationPreference: 'daily', + stateNum: 0, + statusNum: 0, + oldPolicyName: '', + visibility: 'restricted', + isOwnPolicyExpenseChat: false, + lastMessageHtml: 'Room renamed to #jackd23', + hasOutstandingIOU: false, + }, + report_7819732651025410: { + reportID: '7819732651025410', + reportName: 'Chat Report', + chatType: '', + ownerEmail: '__fake__', + policyID: '_FAKE_', + maxSequenceNumber: 0, + participants: [ + 'fake5@gmail.com', + ], + isPinned: false, + lastVisitedTimestamp: 1671205430161, + lastReadTimestamp: 1671205430161, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 0, + lastActionCreated: '', + lastMessageTimestamp: 0, + lastMessageText: '', + lastActorEmail: '', + notificationPreference: 'always', + stateNum: 0, + statusNum: 0, + oldPolicyName: '', + visibility: null, + isOwnPolicyExpenseChat: false, + lastMessageHtml: '', + hasOutstandingIOU: false, + }, + report_2543745284790730: { + reportID: '2543745284790730', + ownerEmail: 'applausetester+perf2@applause.expensifail.com', + managerEmail: 'fake6@gmail.com', + currency: 'USD', + chatReportID: '98817646', + state: 'SUBMITTED', + cachedTotal: '($1,473.11)', + total: 147311, + stateNum: 1, + submitterPayPalMeAddress: '', + hasOutstandingIOU: true, + }, + report_4249286573496381: { + reportID: '4249286573496381', + ownerEmail: 'applausetester+perf2@applause.expensifail.com', + managerEmail: 'christoph+hightraffic@margelo.io', + currency: 'USD', + chatReportID: '4867098979334014', + state: 'SUBMITTED', + cachedTotal: '($212.78)', + total: 21278, + stateNum: 1, + submitterPayPalMeAddress: '', + hasOutstandingIOU: true, + }, + }, + }, + ], + jsonCode: 200, + requestID: '783ef7fac81f969a-SJC', +} +); diff --git a/src/libs/E2E/apiMocks/openApp.json b/src/libs/E2E/apiMocks/openApp.json deleted file mode 100644 index 05b4f3d5c676..000000000000 --- a/src/libs/E2E/apiMocks/openApp.json +++ /dev/null @@ -1,2531 +0,0 @@ -{ - "onyxData":[ - { - "onyxMethod":"merge", - "key":"user", - "value":{ - "isFromPublicDomain":false - } - }, - { - "onyxMethod":"merge", - "key":"currencyList", - "value":{ - "AED":{ - "symbol":"Dhs", - "name":"UAE Dirham", - "ISO4217":"784" - }, - "AFN":{ - "symbol":"Af", - "name":"Afghan Afghani", - "ISO4217":"971" - }, - "ALL":{ - "symbol":"ALL", - "name":"Albanian Lek", - "ISO4217":"008" - }, - "AMD":{ - "symbol":"\u0564\u0580", - "name":"Armenian Dram", - "ISO4217":"051" - }, - "ANG":{ - "symbol":"NA\u0192", - "name":"Neth Antilles Guilder", - "ISO4217":"532" - }, - "AOA":{ - "symbol":"Kz", - "name":"Angolan Kwanza", - "ISO4217":"973" - }, - "ARS":{ - "symbol":"AR$", - "name":"Argentine Peso", - "ISO4217":"032" - }, - "AUD":{ - "symbol":"A$", - "name":"Australian Dollar", - "ISO4217":"036" - }, - "AWG":{ - "symbol":"\u0192", - "name":"Aruba Florin", - "ISO4217":"533" - }, - "AZN":{ - "symbol":"man", - "name":"Azerbaijani Manat", - "ISO4217":"944" - }, - "BAM":{ - "symbol":"KM", - "name":"Bosnia And Herzegovina Convertible Mark", - "ISO4217":"977" - }, - "BBD":{ - "symbol":"Bds$", - "name":"Barbados Dollar", - "ISO4217":"052" - }, - "BDT":{ - "symbol":"Tk", - "name":"Bangladesh Taka", - "ISO4217":"050" - }, - "BGN":{ - "symbol":"\u043b\u0432", - "name":"Bulgarian Lev", - "ISO4217":"975" - }, - "BHD":{ - "symbol":"BHD", - "name":"Bahraini Dinar", - "ISO4217":"048" - }, - "BIF":{ - "symbol":"FBu", - "name":"Burundi Franc", - "decimals":0, - "ISO4217":"108" - }, - "BMD":{ - "symbol":"BD$", - "name":"Bermuda Dollar", - "ISO4217":"060" - }, - "BND":{ - "symbol":"BN$", - "name":"Brunei Dollar", - "ISO4217":"096" - }, - "BOB":{ - "symbol":"Bs", - "name":"Bolivian Boliviano", - "ISO4217":"068" - }, - "BRL":{ - "symbol":"R$", - "name":"Brazilian Real", - "ISO4217":"986" - }, - "BSD":{ - "symbol":"BS$", - "name":"Bahamian Dollar", - "ISO4217":"044" - }, - "BTN":{ - "symbol":"Nu.", - "name":"Bhutan Ngultrum", - "ISO4217":"064" - }, - "BWP":{ - "symbol":"P", - "name":"Botswana Pula", - "ISO4217":"072" - }, - "BYN":{ - "symbol":"BR", - "name":"Belarus Ruble", - "ISO4217":"933" - }, - "BYR":{ - "symbol":"BR", - "name":"Belarus Ruble", - "retired":true, - "retirementDate":"2016-07-01", - "ISO4217":"974" - }, - "BZD":{ - "symbol":"BZ$", - "name":"Belize Dollar", - "ISO4217":"084" - }, - "CAD":{ - "symbol":"C$", - "name":"Canadian Dollar", - "ISO4217":"124" - }, - "CDF":{ - "symbol":"CDF", - "name":"Congolese Franc", - "ISO4217":"976" - }, - "CHF":{ - "symbol":"CHF", - "name":"Swiss Franc", - "ISO4217":"756" - }, - "CLP":{ - "symbol":"Ch$", - "name":"Chilean Peso", - "decimals":0, - "ISO4217":"152" - }, - "CNY":{ - "symbol":"\u00a5", - "name":"Chinese Yuan", - "ISO4217":"156" - }, - "COP":{ - "symbol":"Col$", - "name":"Colombian Peso", - "decimals":0, - "ISO4217":"170" - }, - "CRC":{ - "symbol":"CR\u20a1", - "name":"Costa Rica Colon", - "ISO4217":"188" - }, - "CUC":{ - "symbol":"CUC", - "name":"Cuban Convertible Peso", - "ISO4217":"931" - }, - "CUP":{ - "symbol":"$MN", - "name":"Cuban Peso", - "ISO4217":"192" - }, - "CVE":{ - "symbol":"Esc", - "name":"Cape Verde Escudo", - "ISO4217":"132" - }, - "CZK":{ - "symbol":"K\u010d", - "name":"Czech Koruna", - "ISO4217":"203" - }, - "DJF":{ - "symbol":"Fdj", - "name":"Dijibouti Franc", - "decimals":0, - "ISO4217":"262" - }, - "DKK":{ - "symbol":"Dkr", - "name":"Danish Krone", - "ISO4217":"208" - }, - "DOP":{ - "symbol":"RD$", - "name":"Dominican Peso", - "ISO4217":"214" - }, - "DZD":{ - "symbol":"DZD", - "name":"Algerian Dinar", - "ISO4217":"012" - }, - "EEK":{ - "symbol":"KR", - "name":"Estonian Kroon", - "ISO4217":"", - "retired":true - }, - "EGP":{ - "symbol":"EGP", - "name":"Egyptian Pound", - "ISO4217":"818" - }, - "ERN":{ - "symbol":"Nfk", - "name":"Eritrea Nakfa", - "ISO4217":"232" - }, - "ETB":{ - "symbol":"Br", - "name":"Ethiopian Birr", - "ISO4217":"230" - }, - "EUR":{ - "symbol":"\u20ac", - "name":"Euro", - "ISO4217":"978" - }, - "FJD":{ - "symbol":"FJ$", - "name":"Fiji Dollar", - "ISO4217":"242" - }, - "FKP":{ - "symbol":"FK\u00a3", - "name":"Falkland Islands Pound", - "ISO4217":"238" - }, - "GBP":{ - "symbol":"\u00a3", - "name":"British Pound", - "ISO4217":"826" - }, - "GEL":{ - "symbol":"\u10da", - "name":"Georgian Lari", - "ISO4217":"981" - }, - "GHS":{ - "symbol":"\u20b5", - "name":"Ghanaian Cedi", - "ISO4217":"936" - }, - "GIP":{ - "symbol":"\u00a3G", - "name":"Gibraltar Pound", - "ISO4217":"292" - }, - "GMD":{ - "symbol":"D", - "name":"Gambian Dalasi", - "ISO4217":"270" - }, - "GNF":{ - "symbol":"FG", - "name":"Guinea Franc", - "decimals":0, - "ISO4217":"324" - }, - "GTQ":{ - "symbol":"Q", - "name":"Guatemala Quetzal", - "ISO4217":"320" - }, - "GYD":{ - "symbol":"GY$", - "name":"Guyana Dollar", - "ISO4217":"328" - }, - "HKD":{ - "symbol":"HK$", - "name":"Hong Kong Dollar", - "ISO4217":"344" - }, - "HNL":{ - "symbol":"HNL", - "name":"Honduras Lempira", - "ISO4217":"340" - }, - "HRK":{ - "symbol":"kn", - "name":"Croatian Kuna", - "ISO4217":"191" - }, - "HTG":{ - "symbol":"G", - "name":"Haiti Gourde", - "ISO4217":"332" - }, - "HUF":{ - "symbol":"Ft", - "name":"Hungarian Forint", - "ISO4217":"348" - }, - "IDR":{ - "symbol":"Rp", - "name":"Indonesian Rupiah", - "ISO4217":"360" - }, - "ILS":{ - "symbol":"\u20aa", - "name":"Israeli Shekel", - "ISO4217":"376" - }, - "INR":{ - "symbol":"\u20b9", - "name":"Indian Rupee", - "ISO4217":"356" - }, - "IQD":{ - "symbol":"IQD", - "name":"Iraqi Dinar", - "ISO4217":"368" - }, - "IRR":{ - "symbol":"\ufdfc", - "name":"Iran Rial", - "ISO4217":"364" - }, - "ISK":{ - "symbol":"kr", - "name":"Iceland Krona", - "decimals":0, - "ISO4217":"352" - }, - "JMD":{ - "symbol":"J$", - "name":"Jamaican Dollar", - "ISO4217":"388" - }, - "JOD":{ - "symbol":"JOD", - "name":"Jordanian Dinar", - "ISO4217":"400" - }, - "JPY":{ - "symbol":"\u00a5", - "name":"Japanese Yen", - "decimals":0, - "ISO4217":"392" - }, - "KES":{ - "symbol":"KSh", - "name":"Kenyan Shilling", - "ISO4217":"404" - }, - "KGS":{ - "symbol":"KGS", - "name":"Kyrgyzstani Som", - "ISO4217":"417" - }, - "KHR":{ - "symbol":"KHR", - "name":"Cambodia Riel", - "ISO4217":"116" - }, - "KMF":{ - "symbol":"CF", - "name":"Comoros Franc", - "ISO4217":"174" - }, - "KPW":{ - "symbol":"KP\u20a9", - "name":"North Korean Won", - "ISO4217":"408" - }, - "KRW":{ - "symbol":"\u20a9", - "name":"Korean Won", - "ISO4217":"410" - }, - "KWD":{ - "symbol":"KWD", - "name":"Kuwaiti Dinar", - "ISO4217":"414" - }, - "KYD":{ - "symbol":"CI$", - "name":"Cayman Islands Dollar", - "ISO4217":"136" - }, - "KZT":{ - "symbol":"\u3012", - "name":"Kazakhstan Tenge", - "ISO4217":"398" - }, - "LAK":{ - "symbol":"\u20ad", - "name":"Lao Kip", - "ISO4217":"418" - }, - "LBP":{ - "symbol":"LBP", - "name":"Lebanese Pound", - "ISO4217":"422" - }, - "LKR":{ - "symbol":"SL\u20a8", - "name":"Sri Lanka Rupee", - "ISO4217":"144" - }, - "LRD":{ - "symbol":"L$", - "name":"Liberian Dollar", - "ISO4217":"430" - }, - "LSL":{ - "symbol":"M", - "name":"Lesotho Loti", - "ISO4217":"426" - }, - "LTL":{ - "symbol":"Lt", - "name":"Lithuanian Lita", - "retirementDate":"2015-08-22", - "retired":true, - "ISO4217":"440" - }, - "LVL":{ - "symbol":"Ls", - "name":"Latvian Lat", - "ISO4217":"428", - "retired":true - }, - "LYD":{ - "symbol":"LYD", - "name":"Libyan Dinar", - "ISO4217":"434" - }, - "MAD":{ - "symbol":"MAD", - "name":"Moroccan Dirham", - "ISO4217":"504" - }, - "MDL":{ - "symbol":"MDL", - "name":"Moldovan Leu", - "ISO4217":"498" - }, - "MGA":{ - "symbol":"MGA", - "name":"Malagasy Ariary", - "ISO4217":"969" - }, - "MKD":{ - "symbol":"\u0434\u0435\u043d", - "name":"Macedonian Denar", - "ISO4217":"807" - }, - "MMK":{ - "symbol":"Ks", - "name":"Myanmar Kyat", - "ISO4217":"104" - }, - "MNT":{ - "symbol":"\u20ae", - "name":"Mongolian Tugrik", - "ISO4217":"496" - }, - "MOP":{ - "symbol":"MOP$", - "name":"Macau Pataca", - "ISO4217":"446" - }, - "MRO":{ - "symbol":"UM", - "name":"Mauritania Ougulya", - "decimals":0, - "retired":true, - "retirementDate":"2018-07-11", - "ISO4217":"478" - }, - "MRU":{ - "symbol":"UM", - "name":"Mauritania Ougulya", - "decimals":0, - "ISO4217":"" - }, - "MUR":{ - "symbol":"Rs", - "name":"Mauritius Rupee", - "ISO4217":"480" - }, - "MVR":{ - "symbol":"Rf", - "name":"Maldives Rufiyaa", - "ISO4217":"462" - }, - "MWK":{ - "symbol":"MK", - "name":"Malawi Kwacha", - "ISO4217":"454" - }, - "MXN":{ - "symbol":"Mex$", - "name":"Mexican Peso", - "ISO4217":"484" - }, - "MYR":{ - "symbol":"RM", - "name":"Malaysian Ringgit", - "ISO4217":"458" - }, - "MZN":{ - "symbol":"MTn", - "name":"Mozambican Metical", - "ISO4217":"943" - }, - "NAD":{ - "symbol":"N$", - "name":"Namibian Dollar", - "ISO4217":"516" - }, - "NGN":{ - "symbol":"\u20a6", - "name":"Nigerian Naira", - "ISO4217":"566" - }, - "NIO":{ - "symbol":"NIO", - "name":"Nicaragua Cordoba", - "ISO4217":"558" - }, - "NOK":{ - "symbol":"Nkr", - "name":"Norwegian Krone", - "ISO4217":"578" - }, - "NPR":{ - "symbol":"\u20a8", - "name":"Nepalese Rupee", - "ISO4217":"524" - }, - "NZD":{ - "symbol":"NZ$", - "name":"New Zealand Dollar", - "ISO4217":"554" - }, - "OMR":{ - "symbol":"OMR", - "name":"Omani Rial", - "ISO4217":"512" - }, - "PAB":{ - "symbol":"B", - "name":"Panama Balboa", - "ISO4217":"590" - }, - "PEN":{ - "symbol":"S\/.", - "name":"Peruvian Nuevo Sol", - "ISO4217":"604" - }, - "PGK":{ - "symbol":"K", - "name":"Papua New Guinea Kina", - "ISO4217":"598" - }, - "PHP":{ - "symbol":"\u20b1", - "name":"Philippine Peso", - "ISO4217":"608" - }, - "PKR":{ - "symbol":"Rs", - "name":"Pakistani Rupee", - "ISO4217":"586" - }, - "PLN":{ - "symbol":"z\u0142", - "name":"Polish Zloty", - "ISO4217":"985" - }, - "PYG":{ - "symbol":"\u20b2", - "name":"Paraguayan Guarani", - "ISO4217":"600" - }, - "QAR":{ - "symbol":"QAR", - "name":"Qatar Rial", - "ISO4217":"634" - }, - "RON":{ - "symbol":"RON", - "name":"Romanian New Leu", - "ISO4217":"946" - }, - "RSD":{ - "symbol":"\u0420\u0421\u0414", - "name":"Serbian Dinar", - "ISO4217":"941" - }, - "RUB":{ - "symbol":"\u20bd", - "name":"Russian Rouble", - "ISO4217":"643" - }, - "RWF":{ - "symbol":"RF", - "name":"Rwanda Franc", - "decimals":0, - "ISO4217":"646" - }, - "SAR":{ - "symbol":"SAR", - "name":"Saudi Arabian Riyal", - "ISO4217":"682" - }, - "SBD":{ - "symbol":"SI$", - "name":"Solomon Islands Dollar", - "ISO4217":"090" - }, - "SCR":{ - "symbol":"SR", - "name":"Seychelles Rupee", - "ISO4217":"690" - }, - "SDG":{ - "symbol":"SDG", - "name":"Sudanese Pound", - "ISO4217":"938" - }, - "SEK":{ - "symbol":"Skr", - "name":"Swedish Krona", - "ISO4217":"752" - }, - "SGD":{ - "symbol":"S$", - "name":"Singapore Dollar", - "ISO4217":"702" - }, - "SHP":{ - "symbol":"\u00a3S", - "name":"St Helena Pound", - "ISO4217":"654" - }, - "SLL":{ - "symbol":"Le", - "name":"Sierra Leone Leone", - "ISO4217":"694" - }, - "SOS":{ - "symbol":"So.", - "name":"Somali Shilling", - "ISO4217":"706" - }, - "SRD":{ - "symbol":"SRD", - "name":"Surinamese Dollar", - "ISO4217":"968" - }, - "STD":{ - "symbol":"Db", - "name":"Sao Tome Dobra", - "retired":true, - "retirementDate":"2018-07-11", - "ISO4217":"678" - }, - "STN":{ - "symbol":"Db", - "name":"Sao Tome Dobra", - "ISO4217":"" - }, - "SVC":{ - "symbol":"SVC", - "name":"El Salvador Colon", - "ISO4217":"222" - }, - "SYP":{ - "symbol":"SYP", - "name":"Syrian Pound", - "ISO4217":"760" - }, - "SZL":{ - "symbol":"E", - "name":"Swaziland Lilageni", - "ISO4217":"748" - }, - "THB":{ - "symbol":"\u0e3f", - "name":"Thai Baht", - "ISO4217":"764" - }, - "TJS":{ - "symbol":"TJS", - "name":"Tajikistani Somoni", - "ISO4217":"972" - }, - "TMT":{ - "symbol":"m", - "name":"Turkmenistani Manat", - "ISO4217":"934" - }, - "TND":{ - "symbol":"TND", - "name":"Tunisian Dinar", - "ISO4217":"788" - }, - "TOP":{ - "symbol":"T$", - "name":"Tonga Pa'ang", - "ISO4217":"776" - }, - "TRY":{ - "symbol":"TL", - "name":"Turkish Lira", - "ISO4217":"949" - }, - "TTD":{ - "symbol":"TT$", - "name":"Trinidad & Tobago Dollar", - "ISO4217":"780" - }, - "TWD":{ - "symbol":"NT$", - "name":"Taiwan Dollar", - "ISO4217":"901" - }, - "TZS":{ - "symbol":"TZS", - "name":"Tanzanian Shilling", - "ISO4217":"834" - }, - "UAH":{ - "symbol":"\u20b4", - "name":"Ukraine Hryvnia", - "ISO4217":"980" - }, - "UGX":{ - "symbol":"USh", - "name":"Ugandan Shilling", - "decimals":0, - "ISO4217":"800" - }, - "USD":{ - "symbol":"$", - "name":"United States Dollar", - "ISO4217":"840" - }, - "UYU":{ - "symbol":"$U", - "name":"Uruguayan New Peso", - "ISO4217":"858" - }, - "UZS":{ - "symbol":"UZS", - "name":"Uzbekistani Som", - "ISO4217":"860" - }, - "VEB":{ - "symbol":"Bs.", - "name":"Venezuelan Bolivar", - "retired":true, - "retirementDate":"2008-02-01", - "ISO4217":"" - }, - "VEF":{ - "symbol":"Bs.F", - "name":"Venezuelan Bolivar Fuerte", - "retired":true, - "retirementDate":"2018-08-20", - "ISO4217":"937" - }, - "VES":{ - "symbol":"Bs.S", - "name":"Venezuelan Bolivar Soberano", - "ISO4217":"928" - }, - "VND":{ - "symbol":"\u20ab", - "name":"Vietnam Dong", - "decimals":0, - "ISO4217":"704" - }, - "VUV":{ - "symbol":"Vt", - "name":"Vanuatu Vatu", - "ISO4217":"548" - }, - "WST":{ - "symbol":"WS$", - "name":"Samoa Tala", - "ISO4217":"882" - }, - "XAF":{ - "symbol":"FCFA", - "name":"CFA Franc (BEAC)", - "decimals":0, - "ISO4217":"950" - }, - "XCD":{ - "symbol":"EC$", - "name":"East Caribbean Dollar", - "ISO4217":"951" - }, - "XOF":{ - "symbol":"CFA", - "name":"CFA Franc (BCEAO)", - "decimals":0, - "ISO4217":"952" - }, - "XPF":{ - "symbol":"XPF", - "name":"Pacific Franc", - "decimals":0, - "ISO4217":"953" - }, - "YER":{ - "symbol":"YER", - "name":"Yemen Riyal", - "ISO4217":"886" - }, - "ZAR":{ - "symbol":"R", - "name":"South African Rand", - "ISO4217":"710" - }, - "ZMK":{ - "symbol":"ZK", - "name":"Zambian Kwacha", - "retired":true, - "retirementDate":"2013-01-01", - "ISO4217":"894" - }, - "ZMW":{ - "symbol":"ZMW", - "name":"Zambian Kwacha", - "cacheBurst":1, - "ISO4217":"967" - } - } - }, - { - "onyxMethod":"merge", - "key":"nvp_priorityMode", - "value":"default" - }, - { - "onyxMethod":"merge", - "key":"isFirstTimeNewExpensifyUser", - "value":false - }, - { - "onyxMethod":"merge", - "key":"preferredLocale", - "value":"en" - }, - { - "onyxMethod":"merge", - "key":"nvp_paypalMeAddress", - "value":"" - }, - { - "onyxMethod":"merge", - "key":"preferredEmojiSkinTone", - "value":"default" - }, - { - "onyxMethod":"set", - "key":"frequentlyUsedEmojis", - "value":[ - { - "code":"\ud83e\udd11", - "count":155, - "keywords":[ - "rich", - "money_mouth_face", - "face", - "money", - "mouth" - ], - "lastUpdatedAt":1669657594, - "name":"money_mouth_face" - }, - { - "code":"\ud83e\udd17", - "count":91, - "keywords":[ - "hugs", - "face", - "hug", - "hugging" - ], - "lastUpdatedAt":1669660894, - "name":"hugs" - }, - { - "code":"\ud83d\ude0d", - "count":68, - "keywords":[ - "love", - "crush", - "heart_eyes", - "eye", - "face", - "heart", - "smile" - ], - "lastUpdatedAt":1669659126, - "name":"heart_eyes" - }, - { - "code":"\ud83e\udd14", - "count":56, - "keywords":[ - "thinking", - "face" - ], - "lastUpdatedAt":1669661008, - "name":"thinking" - }, - { - "code":"\ud83d\ude02", - "count":55, - "keywords":[ - "tears", - "joy", - "face", - "laugh", - "tear" - ], - "lastUpdatedAt":1670346435, - "name":"joy" - }, - { - "code":"\ud83d\ude05", - "count":41, - "keywords":[ - "hot", - "sweat_smile", - "cold", - "face", - "open", - "smile", - "sweat" - ], - "lastUpdatedAt":1670346845, - "name":"sweat_smile" - }, - { - "code":"\ud83d\ude04", - "count":37, - "keywords":[ - "happy", - "joy", - "laugh", - "pleased", - "smile", - "eye", - "face", - "mouth", - "open" - ], - "lastUpdatedAt":1669659306, - "name":"smile" - }, - { - "code":"\ud83d\ude18", - "count":27, - "keywords":[ - "face", - "heart", - "kiss" - ], - "lastUpdatedAt":1670346848, - "name":"kissing_heart" - }, - { - "code":"\ud83e\udd23", - "count":25, - "keywords":[ - "lol", - "laughing", - "rofl", - "face", - "floor", - "laugh", - "rolling" - ], - "lastUpdatedAt":1669659311, - "name":"rofl" - }, - { - "code":"\ud83d\ude0b", - "count":18, - "keywords":[ - "tongue", - "lick", - "yum", - "delicious", - "face", - "savouring", - "smile", - "um" - ], - "lastUpdatedAt":1669658204, - "name":"yum" - }, - { - "code":"\ud83d\ude0a", - "count":17, - "keywords":[ - "proud", - "blush", - "eye", - "face", - "smile" - ], - "lastUpdatedAt":1669661018, - "name":"blush" - }, - { - "code":"\ud83d\ude06", - "count":17, - "keywords":[ - "happy", - "haha", - "laughing", - "satisfied", - "face", - "laugh", - "mouth", - "open", - "smile" - ], - "lastUpdatedAt":1669659070, - "name":"laughing" - }, - { - "code":"\ud83d\ude10", - "count":17, - "keywords":[ - "deadpan", - "face", - "neutral" - ], - "lastUpdatedAt":1669658922, - "name":"neutral_face" - }, - { - "code":"\ud83d\ude03", - "count":17, - "keywords":[ - "happy", - "joy", - "haha", - "smiley", - "face", - "mouth", - "open", - "smile" - ], - "lastUpdatedAt":1669636981, - "name":"smiley" - }, - { - "code":"\ud83d\ude17", - "count":15, - "keywords":[ - "face", - "kiss" - ], - "lastUpdatedAt":1669639079 - }, - { - "code":"\ud83d\ude1a", - "count":14, - "keywords":[ - "kissing_closed_eyes", - "closed", - "eye", - "face", - "kiss" - ], - "lastUpdatedAt":1669660248, - "name":"kissing_closed_eyes" - }, - { - "code":"\ud83d\ude19", - "count":12, - "keywords":[ - "kissing_smiling_eyes", - "eye", - "face", - "kiss", - "smile" - ], - "lastUpdatedAt":1669658208, - "name":"kissing_smiling_eyes" - }, - { - "code":"\ud83e\udd10", - "count":11, - "keywords":[ - "face", - "mouth", - "zipper" - ], - "lastUpdatedAt":1670346432 - }, - { - "code":"\ud83d\ude25", - "count":11, - "keywords":[ - "disappointed", - "face", - "relieved", - "whew" - ], - "lastUpdatedAt":1669660257 - }, - { - "code":"\ud83d\ude0e", - "count":11, - "keywords":[ - "bright", - "cool", - "eye", - "eyewear", - "face", - "glasses", - "smile", - "sun", - "sunglasses", - "weather" - ], - "lastUpdatedAt":1669660252 - }, - { - "code":"\ud83d\ude36", - "count":11, - "keywords":[ - "face", - "mouth", - "quiet", - "silent" - ], - "lastUpdatedAt":1669659075, - "name":"no_mouth" - }, - { - "code":"\ud83d\ude11", - "count":11, - "keywords":[ - "expressionless", - "face", - "inexpressive", - "unexpressive" - ], - "lastUpdatedAt":1669640332 - }, - { - "code":"\ud83d\ude0f", - "count":11, - "keywords":[ - "face", - "smirk" - ], - "lastUpdatedAt":1666207075 - }, - { - "code":"\ud83e\udd70", - "count":1, - "keywords":[ - "love", - "smiling_face_with_three_hearts" - ], - "lastUpdatedAt":1670581230, - "name":"smiling_face_with_three_hearts" - } - ] - }, - { - "onyxMethod":"merge", - "key":"private_blockedFromConcierge", - "value":{ - - } - }, - { - "onyxMethod":"merge", - "key":"user", - "value":{ - "isSubscribedToNewsletter":true, - "validated":true, - "isUsingExpensifyCard":true - } - }, - { - "onyxMethod":"set", - "key":"loginList", - "value":{ - "applausetester+perf2@applause.expensifail.com":{ - "partnerName":"expensify.com", - "partnerUserID":"applausetester+perf2@applause.expensifail.com", - "validatedDate":"2022-08-01 05:00:48" - } - } - }, - { - "onyxMethod":"merge", - "key":"personalDetails", - "value":{ - "fake2@gmail.com":{ - "login":"fake2@gmail.com", - "avatar":"https://d1wpcgnaa73g0y.cloudfront.net/7a1fd3cdd41564cf04f4305140372b59d1dcd495_128.jpeg", - "displayName":"fake2@gmail.com", - "pronouns":"__predefined_zeHirHirs", - "timezone":{ - "automatic":false, - "selected":"Europe/Monaco" - }, - "firstName":"", - "lastName":"", - "payPalMeAddress":"qwerty", - "phoneNumber":"", - "validated":true - }, - "fake1@gmail.com":{ - "login":"fake1@gmail.com", - "avatar":"https://d1wpcgnaa73g0y.cloudfront.net/d76dfb6912a0095cbfd2a02f64f4d9d2d9c33c29_128.jpeg", - "displayName":"\"Chat N Laz\"", - "pronouns":"__predefined_theyThemTheirs", - "timezone":{ - "automatic":true, - "selected":"Europe/Athens" - }, - "firstName":"\"Chat N", - "lastName":"Laz\"", - "payPalMeAddress":"", - "phoneNumber":"", - "validated":true - }, - "fake4@gmail.com":{ - "login":"fake4@gmail.com", - "avatar":"https://d1wpcgnaa73g0y.cloudfront.net/e769e0edf5fd0bc11cfa7c39ec2605c5310d26de_128.jpeg", - "displayName":"fake4@gmail.com", - "pronouns":"", - "timezone":{ - "automatic":true, - "selected":"Europe/Kiev" - }, - "firstName":"", - "lastName":"", - "payPalMeAddress":"", - "phoneNumber":"", - "validated":true - }, - "fake3@gmail.com":{ - "login":"fake3@gmail.com", - "avatar":"https://d1wpcgnaa73g0y.cloudfront.net/301e37631eca9e3127d6b668822e3a53771551f6_128.jpeg", - "displayName":"123 Ios", - "pronouns":"__predefined_perPers", - "timezone":{ - "automatic":false, - "selected":"Europe/Helsinki" - }, - "firstName":"123", - "lastName":"Ios", - "payPalMeAddress":"Wwerty", - "phoneNumber":"", - "validated":true - }, - "fake5@gmail.com":{ - "login":"fake5@gmail.com", - "avatar":"https://d1wpcgnaa73g0y.cloudfront.net/2810a38b66d9a60fe41a9cf39c9fd6ecbe2cb35f_128.jpeg", - "displayName":"Qqq Qqq", - "pronouns":"__predefined_sheHerHers", - "timezone":{ - "automatic":false, - "selected":"Europe/Lisbon" - }, - "firstName":"Qqq", - "lastName":"Qqq", - "payPalMeAddress":"", - "phoneNumber":"", - "validated":true - }, - "andreylazutkinutest@gmail.com":{ - "login":"andreylazutkinutest@gmail.com", - "avatar":"https://d1wpcgnaa73g0y.cloudfront.net/2af13161ffcc95fc807769bb22c013c32280f338_128.jpeg", - "displayName":"Main Ios🏴󠁧󠁢󠁳󠁣󠁴󠁿ios", - "pronouns":"__predefined_heHimHis", - "timezone":{ - "automatic":false, - "selected":"Europe/Belfast" - }, - "firstName":"Main", - "lastName":"Ios🏴󠁧󠁢󠁳󠁣󠁴󠁿ios", - "payPalMeAddress":"", - "phoneNumber":"", - "validated":true - }, - "applausetester+0604lsn@applause.expensifail.com":{ - "login":"applausetester+0604lsn@applause.expensifail.com", - "avatar":"https://d1wpcgnaa73g0y.cloudfront.net/ad20184a011ed54383d69e4fe68658522583cbb8_128.jpeg", - "displayName":"0604 Lsn", - "pronouns":"__predefined_zeHirHirs", - "timezone":{ - "automatic":false, - "selected":"America/Costa_Rica" - }, - "firstName":"0604", - "lastName":"Lsn", - "payPalMeAddress":"12345", - "phoneNumber":"", - "validated":true - }, - "applausetester+0704sveta@applause.expensifail.com":{ - "login":"applausetester+0704sveta@applause.expensifail.com", - "avatar":"https://d1wpcgnaa73g0y.cloudfront.net/63cc4a392cc64ba1c8f6a1b90d5f1441a23270d1_128.jpeg", - "displayName":"07 04 0704 Lsn lsn", - "pronouns":"__predefined_callMeByMyName", - "timezone":{ - "automatic":false, - "selected":"Africa/Freetown" - }, - "firstName":"07 04 0704", - "lastName":"Lsn lsn", - "payPalMeAddress":"", - "phoneNumber":"", - "validated":true - }, - "applausetester+0707abb@applause.expensifail.com":{ - "login":"applausetester+0707abb@applause.expensifail.com", - "avatar":"https://d2k5nsl2zxldvw.cloudfront.net/images/avatars/avatar_5.png", - "displayName":"Katya Becciv", - "pronouns":"__predefined_sheHerHers", - "timezone":{ - "automatic":false, - "selected":"US/Eastern" - }, - "firstName":"Katya", - "lastName":"Becciv", - "payPalMeAddress":"testing", - "phoneNumber":"", - "validated":true - }, - "applausetester+0901abb@applause.expensifail.com":{ - "login":"applausetester+0901abb@applause.expensifail.com", - "avatar":"https://d1wpcgnaa73g0y.cloudfront.net/0f6e999ba61695599f092b7652c1e159aee62c65_128.jpeg", - "displayName":"Katie Becciv", - "pronouns":"__predefined_faeFaer", - "timezone":{ - "automatic":false, - "selected":"Africa/Accra" - }, - "firstName":"Katie", - "lastName":"Becciv", - "payPalMeAddress":"kbecciv", - "phoneNumber":"", - "validated":true - }, - "applausetester+1904lsn@applause.expensifail.com":{ - "login":"applausetester+1904lsn@applause.expensifail.com", - "avatar":"https://d2k5nsl2zxldvw.cloudfront.net/images/avatars/avatar_5.png", - "displayName":"11 11", - "pronouns":"", - "timezone":{ - "automatic":true, - "selected":"Europe/Athens" - }, - "firstName":"11", - "lastName":"11", - "payPalMeAddress":"", - "phoneNumber":"", - "validated":true - }, - "applausetester+42222abb@applause.expensifail.com":{ - "login":"applausetester+42222abb@applause.expensifail.com", - "avatar":"https://d1wpcgnaa73g0y.cloudfront.net/d166c112f300a6e30bc70752cd394c3fde099e4f_128.jpeg", - "displayName":"\"First\"", - "pronouns":"", - "timezone":{ - "automatic":true, - "selected":"America/New_York" - }, - "firstName":"\"First\"", - "lastName":"", - "payPalMeAddress":"444555", - "phoneNumber":"", - "validated":true - }, - "applausetester+bernardo@applause.expensifail.com":{ - "login":"applausetester+bernardo@applause.expensifail.com", - "avatar":"https://d1wpcgnaa73g0y.cloudfront.net/803733b7038bbd5e543315fa9c6c0118eda227af_128.jpeg", - "displayName":"bernardo utest", - "pronouns":"", - "timezone":{ - "automatic":true, - "selected":"America/Los_Angeles" - }, - "firstName":"bernardo", - "lastName":"utest", - "payPalMeAddress":"", - "phoneNumber":"", - "validated":false - }, - "applausetester+ihchat4@applause.expensifail.com":{ - "login":"applausetester+ihchat4@applause.expensifail.com", - "avatar":"https://d1wpcgnaa73g0y.cloudfront.net/1008dcaadc12badbddf4720dcb7ad99b7384c613_128.jpeg", - "displayName":"Chat HT", - "pronouns":"__predefined_callMeByMyName", - "timezone":{ - "automatic":true, - "selected":"Europe/Kiev" - }, - "firstName":"Chat", - "lastName":"HT", - "payPalMeAddress":"111", - "phoneNumber":"", - "validated":true - }, - "applausetester+pd1005@applause.expensifail.com":{ - "login":"applausetester+pd1005@applause.expensifail.com", - "avatar":"https://d1wpcgnaa73g0y.cloudfront.net/86c9b7dce35aea83b69c6e825a4b3d00a87389b7_128.jpeg", - "displayName":"applausetester+pd1005@applause.expensifail.com", - "pronouns":"", - "timezone":{ - "automatic":"true", - "selected":"Europe/Lisbon" - }, - "firstName":"", - "lastName":"", - "payPalMeAddress":"", - "phoneNumber":"", - "validated":true - }, - "fake6@gmail.com":{ - "login":"fake6@gmail.com", - "avatar":"https://d2k5nsl2zxldvw.cloudfront.net/images/avatars/avatar_7.png", - "displayName":"fake6@gmail.com", - "pronouns":"", - "timezone":{ - "automatic":true, - "selected":"Europe/Warsaw" - }, - "firstName":"", - "lastName":"", - "payPalMeAddress":"", - "phoneNumber":"", - "validated":true - }, - "applausetester+perf2@applause.expensifail.com":{ - "login":"applausetester+perf2@applause.expensifail.com", - "avatar":"https://d1wpcgnaa73g0y.cloudfront.net/1486f9cc6367d8c399ee453ad5b686d157bb4dda_128.jpeg", - "displayName":"applausetester+perf2@applause.expensifail.com", - "pronouns":"", - "timezone":{ - "automatic":true, - "selected":"America/Los_Angeles" - }, - "firstName":"", - "lastName":"", - "payPalMeAddress":"", - "phoneNumber":"", - "validated":true, - "localCurrencyCode":"USD" - }, - "fake7@gmail.com":{ - "login":"fake7@gmail.com", - "avatar":"https://d2k5nsl2zxldvw.cloudfront.net/images/avatars/avatar_2.png", - "displayName":"fake7@gmail.com", - "pronouns":"", - "timezone":{ - "automatic":true, - "selected":"America/Toronto" - }, - "firstName":"", - "lastName":"", - "payPalMeAddress":"", - "phoneNumber":"", - "validated":true - }, - "fake8@gmail.com":{ - "login":"fake8@gmail.com", - "avatar":"https://d1wpcgnaa73g0y.cloudfront.net/7b0a9cf9c93987053be9d6cc707cb1f091a1ef46_128.jpeg", - "displayName":"fake8@gmail.com", - "pronouns":"", - "timezone":{ - "automatic":true, - "selected":"Europe/Paris" - }, - "firstName":"", - "lastName":"", - "payPalMeAddress":"", - "phoneNumber":"", - "validated":true - }, - "applausetester@applause.expensifail.com":{ - "login":"applausetester@applause.expensifail.com", - "avatar":"https://d1wpcgnaa73g0y.cloudfront.net/8ddbb1a4675883ea12b3021f698a8b2dcfc18d42_128.jpeg", - "displayName":"Applause Main Account", - "pronouns":"__predefined_coCos", - "timezone":{ - "automatic":"true", - "selected":"Europe/Kiev" - }, - "firstName":"Applause", - "lastName":"Main Account", - "payPalMeAddress":"ss", - "phoneNumber":"", - "validated":true - }, - "christoph+hightraffic@margelo.io":{ - "login":"christoph+hightraffic@margelo.io", - "avatar":"https://d2k5nsl2zxldvw.cloudfront.net/images/avatars/avatar_1.png", - "displayName":"Christoph Pader", - "pronouns":"", - "timezone":{ - "automatic":true, - "selected":"Europe/Vienna" - }, - "firstName":"Christoph", - "lastName":"Pader", - "payPalMeAddress":"", - "phoneNumber":"", - "validated":true - }, - "concierge@expensify.com":{ - "login":"concierge@expensify.com", - "avatar":"https://d2k5nsl2zxldvw.cloudfront.net/images/icons/concierge_2022.png", - "displayName":"Concierge", - "pronouns":"", - "timezone":{ - "automatic":true, - "selected":"Europe/Moscow" - }, - "firstName":"Concierge", - "lastName":"", - "payPalMeAddress":"", - "phoneNumber":"", - "validated":true - }, - "svetlanalazutkinautest+0211@gmail.com":{ - "login":"svetlanalazutkinautest+0211@gmail.com", - "avatar":"https://d2k5nsl2zxldvw.cloudfront.net/images/avatars/avatar_6.png", - "displayName":"Chat S", - "pronouns":"", - "timezone":{ - "automatic":true, - "selected":"Europe/Kiev" - }, - "firstName":"Chat S", - "lastName":"", - "payPalMeAddress":"", - "phoneNumber":"", - "validated":true - }, - "tayla.lay@team.expensify.com":{ - "login":"tayla.lay@team.expensify.com", - "avatar":"https://d1wpcgnaa73g0y.cloudfront.net/d3196c27ed6bdb2df741af29a3ccfdb0f9919c41_128.jpeg", - "displayName":"Tayla Simmons", - "pronouns":"__predefined_sheHerHers", - "timezone":{ - "automatic":true, - "selected":"America/Chicago" - }, - "firstName":"Tayla", - "lastName":"Simmons", - "payPalMeAddress":"", - "phoneNumber":"", - "validated":true - } - } - }, - { - "onyxMethod":"set", - "key":"betas", - "value":[ - "all", - "pdfMetaStore", - "reportActionContextMenu", - "submitPolicy", - "attendees", - "autoExport", - "autoExportIntacct", - "autoExportQbo", - "autoExportXero", - "autoJoinPolicy", - "automatedTaxExemption", - "billPay", - "categoryDefaultTax", - "collectableDepositAccounts", - "conciergeTravel", - "connectedCards", - "discrepancy", - "domainContactBilling", - "domainTwoFactorAuth", - "duplicateDetection", - "emailSuppressionBeta", - "expensesV2", - "expensifyCard", - "expensifyCardIntacctReconciliation", - "expensifyCardNetSuiteReconciliation", - "expensifyCardQBOReconciliation", - "expensifyCardRapidIncreaseFraud", - "expensifyCardXeroReconciliation", - "expensifyOrg", - "fixViolationPushNotification", - "freePlan", - "freePlanFullLaunch", - "freePlanSoftLaunch", - "gusto", - "inboxCache", - "inboxHiddenTasks", - "indirectIntegrationSetup", - "IOU", - "joinPolicy", - "loadPolicyAsync", - "mapReceipt", - "mergeAPI", - "mobileRealtimeReportComments", - "mobileSecureReceipts", - "monthlySettlement", - "namesAndAvatars", - "nativeChat", - "newPricing", - "newsletterThree", - "nextSteps", - "openFaceHamburger", - "pdfMetaStore", - "perDiem", - "perDiemInternational", - "pricingCopyChanges", - "qboInvoices", - "quickbooksDesktopV2", - "realtimeReportComments", - "s2wAnnouncement", - "scheduledAutoReporting", - "secureReceipts", - "secureReceiptsReports", - "selfServiceHardLaunch", - "sendMoney", - "smartScanUserDisputes", - "smsSignUp", - "stripeConnect", - "submitPolicy", - "summaryEmail", - "swipeToWin", - "taxForMileage", - "twoFactorAuth", - "venmoIntegration", - "zenefitsIntegration" - ] - }, - { - "onyxMethod":"merge", - "key":"countryCode", - "value":1 - }, - { - "onyxMethod":"merge", - "key":"account", - "value":{ - "requiresTwoFactorAuth":false - } - }, - { - "onyxMethod":"mergecollection", - "key":"policy_", - "value":{ - "policy_28493C792FA01DAE":{ - "isFromFullPolicy":false, - "id":"28493C792FA01DAE", - "name":"applausetester+perf2's Expenses", - "role":"admin", - "type":"personal", - "owner":"applausetester+perf2@applause.expensifail.com", - "outputCurrency":"USD", - "avatar":"", - "employeeList":[ - - ] - }, - "policy_A6511FF8D2EE7661":{ - "isFromFullPolicy":false, - "id":"A6511FF8D2EE7661", - "name":"Applause's Workspace", - "role":"admin", - "type":"free", - "owner":"applausetester+perf2@applause.expensifail.com", - "outputCurrency":"INR", - "avatar":"", - "employeeList":[ - - ] - } - } - }, - { - "onyxMethod":"mergecollection", - "key":"report_", - "value":{ - "report_98258097":{ - "reportID":"98258097", - "reportName":"Chat Report", - "chatType":"", - "ownerEmail":"__fake__", - "policyID":"_FAKE_", - "maxSequenceNumber":2, - "participants":[ - "concierge@expensify.com" - ], - "isPinned":true, - "lastVisitedTimestamp":1671126234191, - "lastReadTimestamp":1671126234191, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":2, - "lastActionCreated":"2022-08-03 06:45:00", - "lastMessageTimestamp":1659509100000, - "lastMessageText":"You can easily track, approve, and pay bills in Expensify with your custom compa", - "lastActorEmail":"concierge@expensify.com", - "notificationPreference":"always", - "stateNum":0, - "statusNum":0, - "oldPolicyName":"", - "visibility":null, - "isOwnPolicyExpenseChat":false, - "lastMessageHtml":"You can easily track, approve, and pay bills in Expensify with your custom company bill pay email address: applause.expensifail.com@expensify.cash<\/a>. Learn more here.<\/a> For questions, just reply to this message.", - "hasOutstandingIOU":false - }, - "report_98258458":{ - "reportID":"98258458", - "reportName":"", - "chatType":"policyExpenseChat", - "ownerEmail":"applausetester+perf2@applause.expensifail.com", - "policyID":"C28C2634DD7226B8", - "maxSequenceNumber":1, - "participants":[ - "applausetester@applause.expensifail.com", - "applausetester+perf2@applause.expensifail.com" - ], - "isPinned":false, - "lastVisitedTimestamp":1669129206943, - "lastReadTimestamp":1669129206943, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":1, - "lastActionCreated":"2022-11-03 20:30:55.599", - "lastMessageTimestamp":1667507455599, - "lastMessageText":"", - "lastActorEmail":"applausetester@applause.expensifail.com", - "notificationPreference":"always", - "stateNum":2, - "statusNum":2, - "oldPolicyName":"Crowded Policy - Definitive Edition", - "visibility":null, - "isOwnPolicyExpenseChat":true, - "lastMessageHtml":"", - "hasOutstandingIOU":false - }, - "report_98344717":{ - "reportID":"98344717", - "reportName":"Chat Report", - "chatType":"", - "ownerEmail":"__fake__", - "policyID":"_FAKE_", - "maxSequenceNumber":8, - "participants":[ - "applausetester+ihchat4@applause.expensifail.com" - ], - "isPinned":false, - "lastVisitedTimestamp":1671205050152, - "lastReadTimestamp":1671205050152, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":8, - "lastActionCreated":"2022-08-02 20:03:42", - "lastMessageTimestamp":1659470622000, - "lastMessageText":"Requested \u20b41.67 from applausetester+perf2@applause.expensifail.com", - "lastActorEmail":"applausetester+ihchat4@applause.expensifail.com", - "notificationPreference":"always", - "stateNum":0, - "statusNum":0, - "oldPolicyName":"", - "visibility":null, - "isOwnPolicyExpenseChat":false, - "lastMessageHtml":"Requested \u20b41.67 from applausetester+perf2@applause.expensifail.com", - "hasOutstandingIOU":false - }, - "report_98345050":{ - "reportID":"98345050", - "reportName":"Chat Report", - "chatType":"", - "ownerEmail":"__fake__", - "policyID":"_FAKE_", - "maxSequenceNumber":17, - "participants":[ - "fake3@gmail.com" - ], - "isPinned":false, - "lastVisitedTimestamp":1671210740419, - "lastReadTimestamp":1671210740419, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":17, - "lastActionCreated":"2022-11-04 21:18:00.038", - "lastMessageTimestamp":1667596680038, - "lastMessageText":"Cancelled the \u20b440.00 request", - "lastActorEmail":"fake3@gmail.com", - "notificationPreference":"always", - "stateNum":0, - "statusNum":0, - "oldPolicyName":"", - "visibility":null, - "isOwnPolicyExpenseChat":false, - "lastMessageHtml":"Cancelled the \u20b440.00 request", - "hasOutstandingIOU":false - }, - "report_98345315":{ - "reportID":"98345315", - "reportName":"Chat Report", - "chatType":"", - "ownerEmail":"__fake__", - "policyID":"_FAKE_", - "maxSequenceNumber":2, - "participants":[ - "fake3@gmail.com", - "fake6@gmail.com", - "fake7@gmail.com", - "fake8@gmail.com" - ], - "isPinned":false, - "lastVisitedTimestamp":1671209362667, - "lastReadTimestamp":1671209362667, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":2, - "lastActionCreated":"2022-08-01 20:48:16", - "lastMessageTimestamp":1659386896000, - "lastMessageText":"applausetester+perf2@applause.expensifail.com", - "lastActorEmail":"fake3@gmail.com", - "notificationPreference":"always", - "stateNum":0, - "statusNum":0, - "oldPolicyName":"", - "visibility":null, - "isOwnPolicyExpenseChat":false, - "lastMessageHtml":"applausetester+perf2@applause.expensifail.com<\/a>", - "hasOutstandingIOU":false - }, - "report_98345625":{ - "reportID":"98345625", - "reportName":"Chat Report", - "chatType":"", - "ownerEmail":"__fake__", - "policyID":"_FAKE_", - "maxSequenceNumber":1, - "participants":[ - "fake1@gmail.com", - "fake2@gmail.com", - "fake3@gmail.com", - "fake4@gmail.com", - "fake5@gmail.com", - "fake6@gmail.com", - "fake7@gmail.com", - "fake8@gmail.com" - ], - "isPinned":false, - "lastVisitedTimestamp":1671470568415, - "lastReadTimestamp":1671470568415, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":1, - "lastActionCreated":"2022-08-01 20:49:11", - "lastMessageTimestamp":1659386951000, - "lastMessageText":"Say hello\ud83d\ude10", - "lastActorEmail":"fake3@gmail.com", - "notificationPreference":"always", - "stateNum":0, - "statusNum":0, - "oldPolicyName":"", - "visibility":null, - "isOwnPolicyExpenseChat":false, - "lastMessageHtml":"Say hello\ud83d\ude10", - "hasOutstandingIOU":false - }, - "report_98345679":{ - "reportID":"98345679", - "reportName":"", - "chatType":"policyExpenseChat", - "ownerEmail":"applausetester+perf2@applause.expensifail.com", - "policyID":"1CE001C4B9F3CA54", - "maxSequenceNumber":2, - "participants":[ - "fake3@gmail.com", - "applausetester+perf2@applause.expensifail.com" - ], - "isPinned":false, - "lastVisitedTimestamp":1664363369565, - "lastReadTimestamp":1664363369565, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":2, - "lastActionCreated":"2022-08-16 12:30:57", - "lastMessageTimestamp":1660653057000, - "lastMessageText":"", - "lastActorEmail":"fake3@gmail.com", - "notificationPreference":"always", - "stateNum":2, - "statusNum":2, - "oldPolicyName":"Andreylazutkinutest+123's workspace", - "visibility":null, - "isOwnPolicyExpenseChat":true, - "lastMessageHtml":"", - "hasOutstandingIOU":false - }, - "report_98414813":{ - "reportID":"98414813", - "reportName":"Chat Report", - "chatType":"", - "ownerEmail":"__fake__", - "policyID":"_FAKE_", - "maxSequenceNumber":1, - "participants":[ - "applausetester+ihchat4@applause.expensifail.com", - "fake6@gmail.com" - ], - "isPinned":false, - "lastVisitedTimestamp":1669197163626, - "lastReadTimestamp":1669197163626, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":1, - "lastActionCreated":"2022-08-02 20:03:41", - "lastMessageTimestamp":1659470621000, - "lastMessageText":"Split \u20b45.00 with applausetester+perf2@applause.expensifail.com and applauseteste", - "lastActorEmail":"applausetester+ihchat4@applause.expensifail.com", - "notificationPreference":"always", - "stateNum":0, - "statusNum":0, - "oldPolicyName":"", - "visibility":null, - "isOwnPolicyExpenseChat":false, - "lastMessageHtml":"Split \u20b45.00 with applausetester+perf2@applause.expensifail.com and fake6@gmail.com", - "hasOutstandingIOU":false - }, - "report_98418120":{ - "reportID":"98418120", - "reportName":"Chat Report", - "chatType":"", - "ownerEmail":"__fake__", - "policyID":"_FAKE_", - "maxSequenceNumber":8, - "participants":[ - "fake3@gmail.com", - "fake5@gmail.com", - "applausetester+0604lsn@applause.expensifail.com", - "fake6@gmail.com", - "fake7@gmail.com" - ], - "isPinned":false, - "lastVisitedTimestamp":1671205456193, - "lastReadTimestamp":1671205456193, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":8, - "lastActionCreated":"2022-10-03 12:09:38.707", - "lastMessageTimestamp":1664798978707, - "lastMessageText":"It\u2019s like I got this music signing its gonna be alright, cause the players gonna", - "lastActorEmail":"applausetester+perf2@applause.expensifail.com", - "notificationPreference":"always", - "stateNum":0, - "statusNum":0, - "oldPolicyName":"", - "visibility":null, - "isOwnPolicyExpenseChat":false, - "lastMessageHtml":"It\u2019s like I got this music signing its gonna be alright, cause the players gonna play baby I am just gonna shake shake shake shack shake it off yeah crash the JS thread whooooop[add jaws asjhd Ladd ksdfksjdhf add k skiff skiff skid fkjsdhf skfkjsfdsklajkhf maladjusted lashed k skadhfhkh aksdhfskdahfaksjhdf ahefhashdsjkfk dhfskjhdfakehf I had fkjjhsadkfhjsak DualShock shfkashdf kahsdf marsh fhfwaeiu Audie with I the JS and UI thread is literally dying while typing, and I am here to fix that. However when typing realistically fast I still think the numbers are okay ish. However, they shouldn\u2019t be laggy at all I guess !", - "hasOutstandingIOU":false - }, - "report_98817646":{ - "reportID":"98817646", - "reportName":"Chat Report", - "chatType":"", - "ownerEmail":"__fake__", - "policyID":"_FAKE_", - "maxSequenceNumber":1787, - "participants":[ - "fake6@gmail.com" - ], - "isPinned":false, - "lastVisitedTimestamp":1671214557025, - "lastReadTimestamp":1671214557025, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":1787, - "lastActionCreated":"2022-12-09 10:17:18.362", - "lastMessageTimestamp":1670581038362, - "lastMessageText":"RR", - "lastActorEmail":"applausetester+perf2@applause.expensifail.com", - "notificationPreference":"always", - "stateNum":0, - "statusNum":0, - "oldPolicyName":"", - "visibility":null, - "isOwnPolicyExpenseChat":false, - "lastMessageHtml":"RR", - "hasOutstandingIOU":true, - "iouReportID":2543745284790730 - }, - "report_358751490033727":{ - "reportID":"358751490033727", - "reportName":"#digimobileroom", - "chatType":"policyRoom", - "ownerEmail":"__fake__", - "policyID":"C28C2634DD7226B8", - "maxSequenceNumber":3, - "participants":[ - "applausetester+pd1005@applause.expensifail.com" - ], - "isPinned":false, - "lastVisitedTimestamp":1669298874528, - "lastReadTimestamp":1669298874528, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":3, - "lastActionCreated":"2022-10-12 17:47:45.228", - "lastMessageTimestamp":1665596865228, - "lastMessageText":"STAGING_CHAT_MESSAGE_A2C534B7-3509-416E-A0AD-8463831C29DD", - "lastActorEmail":"applausetester+fachat1@applause.expensifail.com", - "notificationPreference":"daily", - "stateNum":0, - "statusNum":0, - "oldPolicyName":"", - "visibility":"restricted", - "isOwnPolicyExpenseChat":false, - "lastMessageHtml":"STAGING_CHAT_MESSAGE_A2C534B7-3509-416E-A0AD-8463831C29DD", - "hasOutstandingIOU":false - }, - "report_663424408122117":{ - "reportID":"663424408122117", - "reportName":"#announce", - "chatType":"policyAnnounce", - "ownerEmail":"__fake__", - "policyID":"A6511FF8D2EE7661", - "maxSequenceNumber":0, - "participants":[ - "applausetester+perf2@applause.expensifail.com" - ], - "isPinned":false, - "lastVisitedTimestamp":0, - "lastReadTimestamp":0, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":0, - "lastActionCreated":"", - "lastMessageTimestamp":0, - "lastMessageText":"", - "lastActorEmail":"", - "notificationPreference":"daily", - "stateNum":0, - "statusNum":0, - "oldPolicyName":"", - "visibility":null, - "isOwnPolicyExpenseChat":false, - "lastMessageHtml":"", - "hasOutstandingIOU":false - }, - "report_944123936554214":{ - "reportID":"944123936554214", - "reportName":"", - "chatType":"policyExpenseChat", - "ownerEmail":"applausetester+perf2@applause.expensifail.com", - "policyID":"A6511FF8D2EE7661", - "maxSequenceNumber":0, - "participants":[ - "applausetester+perf2@applause.expensifail.com" - ], - "isPinned":false, - "lastVisitedTimestamp":1669122367932, - "lastReadTimestamp":1669122367932, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":0, - "lastActionCreated":"", - "lastMessageTimestamp":0, - "lastMessageText":"", - "lastActorEmail":"", - "notificationPreference":"always", - "stateNum":0, - "statusNum":0, - "oldPolicyName":"", - "visibility":null, - "isOwnPolicyExpenseChat":true, - "lastMessageHtml":"", - "hasOutstandingIOU":false - }, - "report_2242399088152511":{ - "reportID":"2242399088152511", - "reportName":"Chat Report", - "chatType":"", - "ownerEmail":"__fake__", - "policyID":"_FAKE_", - "maxSequenceNumber":3, - "participants":[ - "concierge@expensify.com", - "applausetester+0901abb@applause.expensifail.com", - "andreylazutkinutest@gmail.com", - "applausetester+0704sveta@applause.expensifail.com", - "fake3@gmail.com" - ], - "isPinned":false, - "lastVisitedTimestamp":1671211239096, - "lastReadTimestamp":1671211239096, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":3, - "lastActionCreated":"2022-11-03 20:48:58.815", - "lastMessageTimestamp":1667508538815, - "lastMessageText":"Hi there, thanks for reaching out! How may I help?", - "lastActorEmail":"concierge@expensify.com", - "notificationPreference":"always", - "stateNum":0, - "statusNum":0, - "oldPolicyName":"", - "visibility":null, - "isOwnPolicyExpenseChat":false, - "lastMessageHtml":"

Hi there, thanks for reaching out! How may I help?<\/p>", - "hasOutstandingIOU":false - }, - "report_2576922422943214":{ - "reportID":"2576922422943214", - "reportName":"Chat Report", - "chatType":"", - "ownerEmail":"__fake__", - "policyID":"_FAKE_", - "maxSequenceNumber":1, - "participants":[ - "applausetester+42222abb@applause.expensifail.com" - ], - "isPinned":false, - "lastVisitedTimestamp":1671213666675, - "lastReadTimestamp":1671213666675, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":1, - "lastActionCreated":"2022-12-01 08:05:11.009", - "lastMessageTimestamp":1669881911009, - "lastMessageText":"Test", - "lastActorEmail":"applausetester+perf2@applause.expensifail.com", - "notificationPreference":"always", - "stateNum":0, - "statusNum":0, - "oldPolicyName":"", - "visibility":null, - "isOwnPolicyExpenseChat":false, - "lastMessageHtml":"Test", - "hasOutstandingIOU":false - }, - "report_2752461403207161":{ - "reportID":"2752461403207161", - "reportName":"Chat Report", - "chatType":"", - "ownerEmail":"__fake__", - "policyID":"_FAKE_", - "maxSequenceNumber":0, - "participants":[ - "fake1@gmail.com" - ], - "isPinned":false, - "lastVisitedTimestamp":1669300587843, - "lastReadTimestamp":1669300587843, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":0, - "lastActionCreated":"", - "lastMessageTimestamp":0, - "lastMessageText":"", - "lastActorEmail":"", - "notificationPreference":"always", - "stateNum":0, - "statusNum":0, - "oldPolicyName":"", - "visibility":null, - "isOwnPolicyExpenseChat":false, - "lastMessageHtml":"", - "hasOutstandingIOU":false - }, - "report_3785654888638968":{ - "reportID":"3785654888638968", - "reportName":"#jack", - "chatType":"policyRoom", - "ownerEmail":"__fake__", - "policyID":"C28C2634DD7226B8", - "maxSequenceNumber":3, - "participants":[ - "applausetester+pd1005@applause.expensifail.com" - ], - "isPinned":false, - "lastVisitedTimestamp":1669881965738, - "lastReadTimestamp":1669881965738, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":3, - "lastActionCreated":"2022-10-12 12:20:00.668", - "lastMessageTimestamp":1665577200668, - "lastMessageText":"Room renamed to #jack", - "lastActorEmail":"applausetester+pd1005@applause.expensifail.com", - "notificationPreference":"daily", - "stateNum":0, - "statusNum":0, - "oldPolicyName":"", - "visibility":"restricted", - "isOwnPolicyExpenseChat":false, - "lastMessageHtml":"Room renamed to #jack", - "hasOutstandingIOU":false - }, - "report_4867098979334014":{ - "reportID":"4867098979334014", - "reportName":"Chat Report", - "chatType":"", - "ownerEmail":"__fake__", - "policyID":"_FAKE_", - "maxSequenceNumber":2, - "participants":[ - "christoph+hightraffic@margelo.io" - ], - "isPinned":false, - "lastVisitedTimestamp":1671214566347, - "lastReadTimestamp":1671214566347, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":2, - "lastActionCreated":"2022-12-16 18:14:00.208", - "lastMessageTimestamp":1671214440208, - "lastMessageText":"Requested \u20ac200.00 from Christoph for Essen mit Kunden", - "lastActorEmail":"applausetester+perf2@applause.expensifail.com", - "notificationPreference":"always", - "stateNum":0, - "statusNum":0, - "oldPolicyName":"", - "visibility":null, - "isOwnPolicyExpenseChat":false, - "lastMessageHtml":"Requested \u20ac200.00 from Christoph for Essen mit Kunden", - "hasOutstandingIOU":true, - "iouReportID":4249286573496381 - }, - "report_5277760851229035":{ - "reportID":"5277760851229035", - "reportName":"#kasper_tha_cat", - "chatType":"policyRoom", - "ownerEmail":"__fake__", - "policyID":"C28C2634DD7226B8", - "maxSequenceNumber":13, - "participants":[ - "applausetester+pd1005@applause.expensifail.com" - ], - "isPinned":false, - "lastVisitedTimestamp":1670955487510, - "lastReadTimestamp":1670955487510, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":13, - "lastActionCreated":"2022-11-29 12:38:15.985", - "lastMessageTimestamp":1669725495985, - "lastMessageText":"fff", - "lastActorEmail":"fake6@gmail.com", - "notificationPreference":"daily", - "stateNum":0, - "statusNum":0, - "oldPolicyName":"", - "visibility":"restricted", - "isOwnPolicyExpenseChat":false, - "lastMessageHtml":"fff
f
f
f
f
f
f
f
f

f
f
f
f

f
f
f
f
f
f

f
f
f
f
f
ff", - "hasOutstandingIOU":false - }, - "report_5324367938904284":{ - "reportID":"5324367938904284", - "reportName":"#applause.expensifail.com", - "chatType":"domainAll", - "ownerEmail":"+@applause.expensifail.com", - "policyID":"_FAKE_", - "maxSequenceNumber":17, - "participants":[ - "applausetester+bernardo@applause.expensifail.com" - ], - "isPinned":false, - "lastVisitedTimestamp":1669634659097, - "lastReadTimestamp":1669634659097, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":14, - "lastActionCreated":"2022-11-29 21:08:00.793", - "lastMessageTimestamp":1669756080793, - "lastMessageText":"Iviviviv8b", - "lastActorEmail":"applausetester+0901abb@applause.expensifail.com", - "notificationPreference":"daily", - "stateNum":0, - "statusNum":0, - "oldPolicyName":"", - "visibility":null, - "isOwnPolicyExpenseChat":false, - "lastMessageHtml":"Iviviviv8b", - "hasOutstandingIOU":false - }, - "report_5654270288238256":{ - "reportID":"5654270288238256", - "reportName":"Chat Report", - "chatType":"", - "ownerEmail":"__fake__", - "policyID":"_FAKE_", - "maxSequenceNumber":0, - "participants":[ - "andreylazutkinutest@gmail.com", - "fake1@gmail.com", - "applausetester+0707abb@applause.expensifail.com", - "fake3@gmail.com", - "fake5@gmail.com", - "applausetester+0604lsn@applause.expensifail.com", - "andreylazutkinutest+0904@gmail.com", - "applausetester+1904lsn@applause.expensifail.com" - ], - "isPinned":false, - "lastVisitedTimestamp":1669129467258, - "lastReadTimestamp":1669129467258, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":0, - "lastActionCreated":"", - "lastMessageTimestamp":0, - "lastMessageText":"", - "lastActorEmail":"", - "notificationPreference":"always", - "stateNum":0, - "statusNum":0, - "oldPolicyName":"", - "visibility":null, - "isOwnPolicyExpenseChat":false, - "lastMessageHtml":"", - "hasOutstandingIOU":false - }, - "report_6194900075541844":{ - "reportID":"6194900075541844", - "reportName":"#admins", - "chatType":"policyAdmins", - "ownerEmail":"__fake__", - "policyID":"A6511FF8D2EE7661", - "maxSequenceNumber":0, - "participants":[ - "applausetester+perf2@applause.expensifail.com" - ], - "isPinned":false, - "lastVisitedTimestamp":0, - "lastReadTimestamp":0, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":0, - "lastActionCreated":"", - "lastMessageTimestamp":0, - "lastMessageText":"", - "lastActorEmail":"", - "notificationPreference":"always", - "stateNum":0, - "statusNum":0, - "oldPolicyName":"", - "visibility":null, - "isOwnPolicyExpenseChat":false, - "lastMessageHtml":"", - "hasOutstandingIOU":false - }, - "report_6801643744224146":{ - "reportID":"6801643744224146", - "reportName":"Chat Report", - "chatType":"", - "ownerEmail":"__fake__", - "policyID":"_FAKE_", - "maxSequenceNumber":1, - "participants":[ - "concierge@expensify.com", - "andreylazutkinutest@gmail.com", - "fake1@gmail.com", - "svetlanalazutkinautest+0211@gmail.com", - "applausetester+0707abb@applause.expensifail.com", - "fake3@gmail.com", - "fake5@gmail.com", - "applausetester+0604lsn@applause.expensifail.com" - ], - "isPinned":false, - "lastVisitedTimestamp":1671211247254, - "lastReadTimestamp":1671211247254, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":1, - "lastActionCreated":"2022-09-15 12:57:59.526", - "lastMessageTimestamp":1663246679526, - "lastMessageText":"\ud83d\udc4b Welcome to Expensify! I'm Concierge. Is there anything I can help with? Click ", - "lastActorEmail":"concierge@expensify.com", - "notificationPreference":"always", - "stateNum":0, - "statusNum":0, - "oldPolicyName":"", - "visibility":null, - "isOwnPolicyExpenseChat":false, - "lastMessageHtml":"\ud83d\udc4b Welcome to Expensify! I'm Concierge. Is there anything I can help with? Click the + icon on the homescreen to explore the features you can use.", - "hasOutstandingIOU":false - }, - "report_7658708888047100":{ - "reportID":"7658708888047100", - "reportName":"Chat Report", - "chatType":"", - "ownerEmail":"__fake__", - "policyID":"_FAKE_", - "maxSequenceNumber":3, - "participants":[ - "concierge@expensify.com", - "andreylazutkinutest@gmail.com", - "fake3@gmail.com", - "fake5@gmail.com", - "tayla.lay@team.expensify.com", - "andreylazutkinutest+160956@gmail.com" - ], - "isPinned":false, - "lastVisitedTimestamp":1669634649909, - "lastReadTimestamp":1669634649909, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":3, - "lastActionCreated":"2022-09-16 11:12:46.739", - "lastMessageTimestamp":1663326766739, - "lastMessageText":"Hi there! How can I help?\u00a0", - "lastActorEmail":"concierge@expensify.com", - "notificationPreference":"always", - "stateNum":0, - "statusNum":0, - "oldPolicyName":"", - "visibility":null, - "isOwnPolicyExpenseChat":false, - "lastMessageHtml":"Hi there! How can I help?\u00a0", - "hasOutstandingIOU":false - }, - "report_7756405299640824":{ - "reportID":"7756405299640824", - "reportName":"#jackd23", - "chatType":"policyRoom", - "ownerEmail":"__fake__", - "policyID":"C28C2634DD7226B8", - "maxSequenceNumber":1, - "participants":[ - "applausetester+pd1005@applause.expensifail.com" - ], - "isPinned":false, - "lastVisitedTimestamp":1669197883208, - "lastReadTimestamp":1669197883208, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":1, - "lastActionCreated":"2022-10-12 12:46:43.577", - "lastMessageTimestamp":1665578803577, - "lastMessageText":"Room renamed to #jackd23", - "lastActorEmail":"applausetester+pd1005@applause.expensifail.com", - "notificationPreference":"daily", - "stateNum":0, - "statusNum":0, - "oldPolicyName":"", - "visibility":"restricted", - "isOwnPolicyExpenseChat":false, - "lastMessageHtml":"Room renamed to #jackd23", - "hasOutstandingIOU":false - }, - "report_7819732651025410":{ - "reportID":"7819732651025410", - "reportName":"Chat Report", - "chatType":"", - "ownerEmail":"__fake__", - "policyID":"_FAKE_", - "maxSequenceNumber":0, - "participants":[ - "fake5@gmail.com" - ], - "isPinned":false, - "lastVisitedTimestamp":1671205430161, - "lastReadTimestamp":1671205430161, - "lastReadCreated":"1980-01-01 00:00:00.000", - "lastReadSequenceNumber":0, - "lastActionCreated":"", - "lastMessageTimestamp":0, - "lastMessageText":"", - "lastActorEmail":"", - "notificationPreference":"always", - "stateNum":0, - "statusNum":0, - "oldPolicyName":"", - "visibility":null, - "isOwnPolicyExpenseChat":false, - "lastMessageHtml":"", - "hasOutstandingIOU":false - }, - "report_2543745284790730":{ - "reportID":"2543745284790730", - "ownerEmail":"applausetester+perf2@applause.expensifail.com", - "managerEmail":"fake6@gmail.com", - "currency":"USD", - "chatReportID":"98817646", - "state":"SUBMITTED", - "cachedTotal":"($1,473.11)", - "total":147311, - "stateNum":1, - "submitterPayPalMeAddress":"", - "hasOutstandingIOU":true - }, - "report_4249286573496381":{ - "reportID":"4249286573496381", - "ownerEmail":"applausetester+perf2@applause.expensifail.com", - "managerEmail":"christoph+hightraffic@margelo.io", - "currency":"USD", - "chatReportID":"4867098979334014", - "state":"SUBMITTED", - "cachedTotal":"($212.78)", - "total":21278, - "stateNum":1, - "submitterPayPalMeAddress":"", - "hasOutstandingIOU":true - } - } - } - ], - "jsonCode":200, - "requestID":"783ef7fac81f969a-SJC" -} diff --git a/src/libs/E2E/apiMocks/openReport.js b/src/libs/E2E/apiMocks/openReport.js new file mode 100644 index 000000000000..22bd62fcc57d --- /dev/null +++ b/src/libs/E2E/apiMocks/openReport.js @@ -0,0 +1,115 @@ +export default () => ({ + onyxData: [ + { + onyxMethod: 'merge', + key: 'report_98345625', + value: { + reportID: '98345625', + reportName: 'Chat Report', + chatType: '', + ownerEmail: '__fake__', + policyID: '_FAKE_', + maxSequenceNumber: 1, + participants: [ + 'fake1@gmail.com', + 'fake2@gmail.com', + 'fake3@gmail.com', + 'fake4@gmail.com', + 'fake5@gmail.com', + 'fake6@gmail.com', + 'fake7@gmail.com', + 'fake8@gmail.com', + ], + isPinned: false, + lastVisitedTimestamp: 1671470568415, + lastReadTimestamp: 1671470568415, + lastReadCreated: '1980-01-01 00:00:00.000', + lastReadSequenceNumber: 1, + lastActionCreated: '2022-08-01 20:49:11', + lastMessageTimestamp: 1659386951000, + lastMessageText: 'Say hello\ud83d\ude10', + lastActorEmail: 'fake3@gmail.com', + notificationPreference: 'always', + stateNum: 0, + statusNum: 0, + oldPolicyName: '', + visibility: null, + isOwnPolicyExpenseChat: false, + lastMessageHtml: 'Say hello\ud83d\ude10', + hasOutstandingIOU: false, + }, + }, + { + onyxMethod: 'merge', + key: 'reportActions_98345625', + value: { + 0: { + reportActionID: '226245034', + actionName: 'CREATED', + created: '2022-08-01 20:48:58', + timestamp: 1659386938, + reportActionTimestamp: 0, + avatar: 'https://d2k5nsl2zxldvw.cloudfront.net/images/avatars/avatar_3.png', + message: [ + { + type: 'TEXT', + style: 'strong', + text: '__fake__', + }, + { + type: 'TEXT', + style: 'normal', + text: ' created this report', + }, + ], + person: [ + { + type: 'TEXT', + style: 'strong', + text: '__fake__', + }, + ], + automatic: false, + sequenceNumber: 0, + clientID: '', + shouldShow: true, + }, + 1: { + person: [ + { + type: 'TEXT', + style: 'strong', + text: '123 Ios', + }, + ], + actorEmail: 'fake3@gmail.com', + actorAccountID: 10773236, + message: [ + { + type: 'COMMENT', + html: 'Say hello\ud83d\ude10', + text: 'Say hello\ud83d\ude10', + isEdited: false, + }, + ], + originalMessage: { + clientID: '1659386951676734', + html: 'Say hello\ud83d\ude10', + }, + avatar: 'https://d1wpcgnaa73g0y.cloudfront.net/301e37631eca9e3127d6b668822e3a53771551f6_128.jpeg', + created: '2022-08-01 20:49:11', + timestamp: 1659386951, + reportActionTimestamp: 1659386951000, + automatic: false, + sequenceNumber: 1, + actionName: 'ADDCOMMENT', + shouldShow: true, + clientID: '1659386951676734', + reportActionID: '1082059149', + }, + }, + }, + ], + jsonCode: 200, + requestID: '783ef80a3fc5969a-SJC', +}); diff --git a/src/libs/E2E/apiMocks/openReport.json b/src/libs/E2E/apiMocks/openReport.json deleted file mode 100644 index 37e9b8d04a58..000000000000 --- a/src/libs/E2E/apiMocks/openReport.json +++ /dev/null @@ -1,115 +0,0 @@ -{ - "onyxData": [ - { - "onyxMethod": "merge", - "key": "report_98345625", - "value": { - "reportID": "98345625", - "reportName": "Chat Report", - "chatType": "", - "ownerEmail": "__fake__", - "policyID": "_FAKE_", - "maxSequenceNumber": 1, - "participants": [ - "fake1@gmail.com", - "fake2@gmail.com", - "fake3@gmail.com", - "fake4@gmail.com", - "fake5@gmail.com", - "fake6@gmail.com", - "fake7@gmail.com", - "fake8@gmail.com" - ], - "isPinned": false, - "lastVisitedTimestamp": 1671470568415, - "lastReadTimestamp": 1671470568415, - "lastReadCreated": "1980-01-01 00:00:00.000", - "lastReadSequenceNumber": 1, - "lastActionCreated": "2022-08-01 20:49:11", - "lastMessageTimestamp": 1659386951000, - "lastMessageText": "Say hello\ud83d\ude10", - "lastActorEmail": "fake3@gmail.com", - "notificationPreference": "always", - "stateNum": 0, - "statusNum": 0, - "oldPolicyName": "", - "visibility": null, - "isOwnPolicyExpenseChat": false, - "lastMessageHtml": "Say hello\ud83d\ude10", - "hasOutstandingIOU": false - } - }, - { - "onyxMethod": "merge", - "key": "reportActions_98345625", - "value": { - "0": { - "reportActionID": "226245034", - "actionName": "CREATED", - "created": "2022-08-01 20:48:58", - "timestamp": 1659386938, - "reportActionTimestamp": 0, - "avatar": "https:\/\/d2k5nsl2zxldvw.cloudfront.net\/images\/avatars\/avatar_3.png", - "message": [ - { - "type": "TEXT", - "style": "strong", - "text": "__fake__" - }, - { - "type": "TEXT", - "style": "normal", - "text": " created this report" - } - ], - "person": [ - { - "type": "TEXT", - "style": "strong", - "text": "__fake__" - } - ], - "automatic": false, - "sequenceNumber": 0, - "clientID": "", - "shouldShow": true - }, - "1": { - "person": [ - { - "type": "TEXT", - "style": "strong", - "text": "123 Ios" - } - ], - "actorEmail": "fake3@gmail.com", - "actorAccountID": 10773236, - "message": [ - { - "type": "COMMENT", - "html": "Say hello\ud83d\ude10", - "text": "Say hello\ud83d\ude10", - "isEdited": false - } - ], - "originalMessage": { - "clientID": "1659386951676734", - "html": "Say hello\ud83d\ude10" - }, - "avatar": "https:\/\/d1wpcgnaa73g0y.cloudfront.net\/301e37631eca9e3127d6b668822e3a53771551f6_128.jpeg", - "created": "2022-08-01 20:49:11", - "timestamp": 1659386951, - "reportActionTimestamp": 1659386951000, - "automatic": false, - "sequenceNumber": 1, - "actionName": "ADDCOMMENT", - "shouldShow": true, - "clientID": "1659386951676734", - "reportActionID": "1082059149" - } - } - } - ], - "jsonCode": 200, - "requestID": "783ef80a3fc5969a-SJC" -} diff --git a/src/libs/E2E/apiMocks/signinUser.js b/src/libs/E2E/apiMocks/signinUser.js new file mode 100644 index 000000000000..26203bc492cf --- /dev/null +++ b/src/libs/E2E/apiMocks/signinUser.js @@ -0,0 +1,123 @@ +export default ({email}) => ({ + onyxData: [ + { + onyxMethod: 'merge', + key: 'session', + value: { + authToken: 'fakeAuthToken', + accountID: 12313081, + email, + encryptedAuthToken: 'fakeEncryptedAuthToken', + }, + }, + { + onyxMethod: 'set', + key: 'shouldShowComposeInput', + value: true, + }, + { + onyxMethod: 'merge', + key: 'credentials', + value: { + autoGeneratedLogin: 'fake', + autoGeneratedPassword: 'fake', + }, + }, + { + onyxMethod: 'merge', + key: 'user', + value: { + isUsingExpensifyCard: false, + }, + }, + { + onyxMethod: 'set', + key: 'betas', + value: [ + 'all', + 'pdfMetaStore', + 'reportActionContextMenu', + 'submitPolicy', + 'attendees', + 'autoExport', + 'autoExportIntacct', + 'autoExportQbo', + 'autoExportXero', + 'autoJoinPolicy', + 'automatedTaxExemption', + 'billPay', + 'categoryDefaultTax', + 'collectableDepositAccounts', + 'conciergeTravel', + 'connectedCards', + 'discrepancy', + 'domainContactBilling', + 'domainTwoFactorAuth', + 'duplicateDetection', + 'emailSuppressionBeta', + 'expensesV2', + 'expensifyCard', + 'expensifyCardIntacctReconciliation', + 'expensifyCardNetSuiteReconciliation', + 'expensifyCardQBOReconciliation', + 'expensifyCardRapidIncreaseFraud', + 'expensifyCardXeroReconciliation', + 'expensifyOrg', + 'fixViolationPushNotification', + 'freePlan', + 'freePlanFullLaunch', + 'freePlanSoftLaunch', + 'gusto', + 'inboxCache', + 'inboxHiddenTasks', + 'indirectIntegrationSetup', + 'IOU', + 'joinPolicy', + 'loadPolicyAsync', + 'mapReceipt', + 'mergeAPI', + 'mobileRealtimeReportComments', + 'mobileSecureReceipts', + 'monthlySettlement', + 'namesAndAvatars', + 'nativeChat', + 'newPricing', + 'newsletterThree', + 'nextSteps', + 'openFaceHamburger', + 'pdfMetaStore', + 'perDiem', + 'perDiemInternational', + 'pricingCopyChanges', + 'qboInvoices', + 'quickbooksDesktopV2', + 'realtimeReportComments', + 's2wAnnouncement', + 'scheduledAutoReporting', + 'secureReceipts', + 'secureReceiptsReports', + 'selfServiceHardLaunch', + 'sendMoney', + 'smartScanUserDisputes', + 'smsSignUp', + 'stripeConnect', + 'submitPolicy', + 'summaryEmail', + 'swipeToWin', + 'taxForMileage', + 'twoFactorAuth', + 'venmoIntegration', + 'zenefitsIntegration', + ], + }, + { + onyxMethod: 'merge', + key: 'account', + value: { + requiresTwoFactorAuth: false, + }, + }, + ], + jsonCode: 200, + requestID: '783e5f3cadfbcfc0-SJC', +}); diff --git a/src/libs/E2E/apiMocks/signinUser.json b/src/libs/E2E/apiMocks/signinUser.json deleted file mode 100644 index 5a80b24b782c..000000000000 --- a/src/libs/E2E/apiMocks/signinUser.json +++ /dev/null @@ -1,123 +0,0 @@ -{ - "onyxData": [ - { - "onyxMethod": "merge", - "key": "session", - "value": { - "authToken": "fakeAuthToken", - "accountID": 12313081, - "email": "thisemailwillgetreplaced@duringsign.com", - "encryptedAuthToken": "fakeEncryptedAuthToken" - } - }, - { - "onyxMethod": "set", - "key": "shouldShowComposeInput", - "value": true - }, - { - "onyxMethod": "merge", - "key": "credentials", - "value": { - "autoGeneratedLogin": "fake", - "autoGeneratedPassword": "fake" - } - }, - { - "onyxMethod": "merge", - "key": "user", - "value": { - "isUsingExpensifyCard": false - } - }, - { - "onyxMethod": "set", - "key": "betas", - "value": [ - "all", - "pdfMetaStore", - "reportActionContextMenu", - "submitPolicy", - "attendees", - "autoExport", - "autoExportIntacct", - "autoExportQbo", - "autoExportXero", - "autoJoinPolicy", - "automatedTaxExemption", - "billPay", - "categoryDefaultTax", - "collectableDepositAccounts", - "conciergeTravel", - "connectedCards", - "discrepancy", - "domainContactBilling", - "domainTwoFactorAuth", - "duplicateDetection", - "emailSuppressionBeta", - "expensesV2", - "expensifyCard", - "expensifyCardIntacctReconciliation", - "expensifyCardNetSuiteReconciliation", - "expensifyCardQBOReconciliation", - "expensifyCardRapidIncreaseFraud", - "expensifyCardXeroReconciliation", - "expensifyOrg", - "fixViolationPushNotification", - "freePlan", - "freePlanFullLaunch", - "freePlanSoftLaunch", - "gusto", - "inboxCache", - "inboxHiddenTasks", - "indirectIntegrationSetup", - "IOU", - "joinPolicy", - "loadPolicyAsync", - "mapReceipt", - "mergeAPI", - "mobileRealtimeReportComments", - "mobileSecureReceipts", - "monthlySettlement", - "namesAndAvatars", - "nativeChat", - "newPricing", - "newsletterThree", - "nextSteps", - "openFaceHamburger", - "pdfMetaStore", - "perDiem", - "perDiemInternational", - "pricingCopyChanges", - "qboInvoices", - "quickbooksDesktopV2", - "realtimeReportComments", - "s2wAnnouncement", - "scheduledAutoReporting", - "secureReceipts", - "secureReceiptsReports", - "selfServiceHardLaunch", - "sendMoney", - "smartScanUserDisputes", - "smsSignUp", - "stripeConnect", - "submitPolicy", - "summaryEmail", - "swipeToWin", - "taxForMileage", - "twoFactorAuth", - "venmoIntegration", - "zenefitsIntegration" - ] - }, - { - "onyxMethod": "merge", - "key": "account", - "value": { - "requiresTwoFactorAuth": false - } - } - ], - "jsonCode": 200, - "requestID": "783e5f3cadfbcfc0-SJC" -} diff --git a/src/libs/E2E/reactNativeLaunchingTest.js b/src/libs/E2E/reactNativeLaunchingTest.js index f0fa7200a3c9..25938f341fb7 100644 --- a/src/libs/E2E/reactNativeLaunchingTest.js +++ b/src/libs/E2E/reactNativeLaunchingTest.js @@ -22,6 +22,7 @@ console.debug('=========================='); // import your test here, define its name and config first in e2e/config.js const tests = { [E2EConfig.TEST_NAMES.AppStartTime]: require('./tests/appStartTimeTest.e2e').default, + [E2EConfig.TEST_NAMES.OpenSearchPage]: require('./tests/openSearchPageTest.e2e').default, }; // Once we receive the TII measurement we know that the app is initialized and ready to be used: diff --git a/src/libs/E2E/tests/appStartTimeTest.e2e.js b/src/libs/E2E/tests/appStartTimeTest.e2e.js index d66d4577ce32..c53ba15b3061 100644 --- a/src/libs/E2E/tests/appStartTimeTest.e2e.js +++ b/src/libs/E2E/tests/appStartTimeTest.e2e.js @@ -4,13 +4,8 @@ import Performance from '../../Performance'; import E2EClient from '../client'; const test = () => { - const email = 'applausetester+perf2@applause.expensifail.com'; - const password = 'Password123'; - - console.debug('[E2E] App is ready, logging in…'); - // check for login (if already logged in the action will simply resolve) - E2ELogin(email, password).then((neededLogin) => { + E2ELogin().then((neededLogin) => { if (neededLogin) { // we don't want to submit the first login to the results return E2EClient.submitTestDone(); @@ -24,7 +19,7 @@ const test = () => { // underscore promises in sequence without for-loop Promise.all( _.map(metrics, metric => E2EClient.submitTestResults({ - name: metric.name, + name: `App start ${metric.name}`, duration: metric.duration, })), ).then(() => { diff --git a/src/libs/E2E/tests/openSearchPageTest.e2e.js b/src/libs/E2E/tests/openSearchPageTest.e2e.js new file mode 100644 index 000000000000..2f0f72f35bdd --- /dev/null +++ b/src/libs/E2E/tests/openSearchPageTest.e2e.js @@ -0,0 +1,31 @@ +import E2ELogin from '../actions/e2eLogin'; +import Performance from '../../Performance'; +import E2EClient from '../client'; +import Navigation from '../../Navigation/Navigation'; +import ROUTES from '../../../ROUTES'; +import CONST from '../../../CONST'; + +const test = () => { + // check for login (if already logged in the action will simply resolve) + E2ELogin().then((neededLogin) => { + if (neededLogin) { + // we don't want to submit the first login to the results + return E2EClient.submitTestDone(); + } + + Performance.subscribeToMeasurements((entry) => { + if (entry.name !== CONST.TIMING.SEARCH_RENDER) { + return; + } + + E2EClient.submitTestResults({ + name: 'Open Search Page TTI', + duration: entry.duration, + }).then(E2EClient.submitTestDone); + }); + + Navigation.navigate(ROUTES.SEARCH); + }); +}; + +export default test; diff --git a/src/libs/IOUUtils.js b/src/libs/IOUUtils.js index 440155f755f6..5c1538682ce3 100644 --- a/src/libs/IOUUtils.js +++ b/src/libs/IOUUtils.js @@ -1,3 +1,4 @@ +import _ from 'underscore'; import CONST from '../CONST'; /** @@ -65,7 +66,77 @@ function updateIOUOwnerAndTotal(iouReport, actorEmail, amount, currency, type = return iouReportUpdate; } +/** + * Returns the list of IOU actions depending on the type and whether or not they are pending. + * Used below so that we can decide if an IOU report is pending currency conversion. + * + * @param {Array} reportActions + * @param {Object} iouReport + * @param {String} type - iouReportAction type. Can be oneOf(create, decline, cancel, pay, split) + * @param {String} pendingAction + * @param {Boolean} filterRequestsInDifferentCurrency + * + * @returns {Array} + */ +function getIOUReportActions(reportActions, iouReport, type = '', pendingAction = '', filterRequestsInDifferentCurrency = false) { + return _.chain(reportActions) + .filter(action => action.originalMessage + && action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU + && action.originalMessage.IOUReportID.toString() === iouReport.reportID.toString()) + .filter(action => (!_.isEmpty(type) ? action.originalMessage.type === type : true)) + .filter(action => (!_.isEmpty(pendingAction) ? action.pendingAction === pendingAction : true)) + .filter(action => (filterRequestsInDifferentCurrency ? action.originalMessage.currency !== iouReport.currency : true)) + .value(); +} + +/** + * Returns whether or not an IOU report contains money requests in a different currency + * that are either created or cancelled offline, and thus haven't been converted to the report's currency yet + * + * @param {Array} reportActions + * @param {Object} iouReport + * + * @returns {Boolean} + */ +function isIOUReportPendingCurrencyConversion(reportActions, iouReport) { + // Pending money requests that are in a different currency + const pendingRequestsInDifferentCurrency = _.chain(getIOUReportActions( + reportActions, + iouReport, + CONST.IOU.REPORT_ACTION_TYPE.CREATE, + CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + true, + )).map(action => action.originalMessage.IOUTransactionID) + .sort() + .value(); + + // Pending cancelled money requests that are in a different currency + const pendingCancelledRequestsInDifferentCurrency = _.chain(getIOUReportActions( + reportActions, + iouReport, + CONST.IOU.REPORT_ACTION_TYPE.CANCEL, + CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + true, + )).map(action => action.originalMessage.IOUTransactionID) + .sort() + .value(); + + const hasPendingRequests = Boolean(pendingRequestsInDifferentCurrency.length || pendingCancelledRequestsInDifferentCurrency.length); + + // If we have pending money requests made offline, check if all of them have been cancelled offline + // In order to do that, we can grab transactionIDs of all the created and cancelled money requests and check if they're identical + if (hasPendingRequests && _.isEqual(pendingRequestsInDifferentCurrency, pendingCancelledRequestsInDifferentCurrency)) { + return false; + } + + // Not all requests made offline had been cancelled, + // simply return if we have any pending created or cancelled requests + return hasPendingRequests; +} + export { calculateAmount, updateIOUOwnerAndTotal, + getIOUReportActions, + isIOUReportPendingCurrencyConversion, }; diff --git a/src/libs/Navigation/NavigationRoot.js b/src/libs/Navigation/NavigationRoot.js index 7dea3a743bf3..0e0958e8a393 100644 --- a/src/libs/Navigation/NavigationRoot.js +++ b/src/libs/Navigation/NavigationRoot.js @@ -8,6 +8,7 @@ import AppNavigator from './AppNavigator'; import FullScreenLoadingIndicator from '../../components/FullscreenLoadingIndicator'; import themeColors from '../../styles/themes/default'; import styles from '../../styles/styles'; +import DomUtils from '../DomUtils'; import Log from '../Log'; // https://reactnavigation.org/docs/themes @@ -45,6 +46,12 @@ function parseAndLogRoute(state) { Log.info('Navigating to route', false, {path: currentPath}); } + // Clicking a button that does navigation will stay active even if it's out of view + // and it's tooltip will stay visible. + // We blur the element manually to fix that (especially for Safari). + // More info: https://github.com/Expensify/App/issues/13146 + DomUtils.blurActiveElement(); + Navigation.setIsNavigationReady(); } diff --git a/src/libs/Performance.js b/src/libs/Performance.js index 94fa8eb6eaa0..e07d509154e7 100644 --- a/src/libs/Performance.js +++ b/src/libs/Performance.js @@ -74,7 +74,7 @@ if (Metrics.canCapturePerformanceMetrics()) { requestAnimationFrame(() => { Performance.measureFailSafe('TTI', 'nativeLaunchStart', endMark); - // we don't want the alert to show on a e2e test sessio + // we don't want the alert to show on an e2e test session if (!isE2ETestSession()) { Performance.printPerformanceMetrics(); } diff --git a/src/libs/RoomNameInputUtils.js b/src/libs/RoomNameInputUtils.js index 5522096973de..6c2ee31775f9 100644 --- a/src/libs/RoomNameInputUtils.js +++ b/src/libs/RoomNameInputUtils.js @@ -1,18 +1,16 @@ import CONST from '../CONST'; /** - * Modifies the room name to follow our conventions: - * - Max length 80 characters - * - Cannot not include space or special characters, and we automatically apply an underscore for spaces - * - Must be lowercase + * Modifies the room name in the following ways: + * - Replaces spaces with dashes + * - Makes all letters lowercase + * * @param {String} roomName * @returns {String} */ function modifyRoomName(roomName) { const modifiedRoomNameWithoutHash = roomName - .replace(/ /g, '_') - .replace(/[^a-zA-Z\d_]/g, '') - .substr(0, CONST.REPORT.MAX_ROOM_NAME_LENGTH) + .replace(/ /g, '-') .toLowerCase(); return `${CONST.POLICY.ROOM_PREFIX}${modifiedRoomNameWithoutHash}`; diff --git a/src/libs/ValidationUtils.js b/src/libs/ValidationUtils.js index b496860a3d8b..87544c61f97b 100644 --- a/src/libs/ValidationUtils.js +++ b/src/libs/ValidationUtils.js @@ -391,6 +391,19 @@ function isExistingRoomName(roomName, reports, policyID) { ); } +/** + * Checks if a room name is valid by checking that: + * - It starts with a hash '#' + * - After the first character, it contains only lowercase letters, numbers, and dashes + * - It's between 1 and MAX_ROOM_NAME_LENGTH characters long + * + * @param {String} roomName + * @returns {Boolean} + */ +function isValidRoomName(roomName) { + return CONST.REGEX.ROOM_NAME.test(roomName); +} + /** * Checks if tax ID consists of 9 digits * @@ -428,6 +441,7 @@ export { doesFailCharacterLimitAfterTrim, isReservedRoomName, isExistingRoomName, + isValidRoomName, isValidTaxID, findInvalidSymbols, }; diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 1c8306fc797b..8ce416c39506 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -915,6 +915,7 @@ function addPolicyReport(policy, reportName, visibility) { // The room might contain all policy members so notifying always should be opt-in only. CONST.REPORT.NOTIFICATION_PREFERENCE.DAILY, ); + const createdReportAction = ReportUtils.buildOptimisticCreatedReportAction(policyReport.ownerEmail); // Onyx.set is used on the optimistic data so that it is present before navigating to the workspace room. With Onyx.merge the workspace room reportID is not present when // fetchReportIfNeeded is called on the ReportScreen, so openReport is called which is unnecessary since the optimistic data will be stored in Onyx. @@ -933,7 +934,7 @@ function addPolicyReport(policy, reportName, visibility) { { onyxMethod: CONST.ONYX.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${policyReport.reportID}`, - value: ReportUtils.buildOptimisticCreatedReportAction(policyReport.ownerEmail), + value: createdReportAction, }, ]; const successData = [ @@ -964,6 +965,7 @@ function addPolicyReport(policy, reportName, visibility) { reportName, visibility, reportID: policyReport.reportID, + createdReportActionID: createdReportAction.reportActionID, }, {optimisticData, successData}, ); diff --git a/src/libs/canFocusInputOnScreenFocus/index.js b/src/libs/canFocusInputOnScreenFocus/index.js index 47ed8354fb47..c930c0d944ec 100644 --- a/src/libs/canFocusInputOnScreenFocus/index.js +++ b/src/libs/canFocusInputOnScreenFocus/index.js @@ -1,3 +1,3 @@ -import canUseTouchScreen from '../canUseTouchscreen'; +import * as DeviceCapabilities from '../DeviceCapabilities'; -export default () => !canUseTouchScreen(); +export default () => !DeviceCapabilities.canUseTouchScreen(); diff --git a/src/pages/DetailsPage.js b/src/pages/DetailsPage.js index 1c0ebe9d178b..6b3ee422ade6 100755 --- a/src/pages/DetailsPage.js +++ b/src/pages/DetailsPage.js @@ -23,6 +23,7 @@ import MenuItem from '../components/MenuItem'; import AttachmentModal from '../components/AttachmentModal'; import PressableWithoutFocus from '../components/PressableWithoutFocus'; import * as Report from '../libs/actions/Report'; +import OfflineWithFeedback from '../components/OfflineWithFeedback'; import AutoUpdateTime from '../components/AutoUpdateTime'; const matchType = PropTypes.shape({ @@ -130,17 +131,21 @@ class DetailsPage extends React.PureComponent { style={styles.noOutline} onPress={show} > - + + + )} {details.displayName && ( - + {isSMSLogin ? this.props.toLocalPhone(details.displayName) : details.displayName} )} diff --git a/src/pages/EnablePayments/TermsPage/ShortTermsForm.js b/src/pages/EnablePayments/TermsPage/ShortTermsForm.js index 97169763b1f3..05b4c6fcff10 100644 --- a/src/pages/EnablePayments/TermsPage/ShortTermsForm.js +++ b/src/pages/EnablePayments/TermsPage/ShortTermsForm.js @@ -19,7 +19,7 @@ const ShortTermsForm = () => ( {Localize.translateLocal('termsStep.monthlyFee')} - {Localize.translateLocal('termsStep.feeAmountZero')} + {Localize.translateLocal('termsStep.feeAmountZero')} @@ -28,7 +28,7 @@ const ShortTermsForm = () => ( {Localize.translateLocal('termsStep.shortTermsForm.perPurchase')} - {Localize.translateLocal('termsStep.feeAmountZero')} + {Localize.translateLocal('termsStep.feeAmountZero')} @@ -40,7 +40,7 @@ const ShortTermsForm = () => ( {Localize.translateLocal('termsStep.shortTermsForm.atmWithdrawal')} - {Localize.translateLocal('common.na')} + {Localize.translateLocal('common.na')} @@ -48,7 +48,7 @@ const ShortTermsForm = () => ( - {Localize.translateLocal('common.na')} + {Localize.translateLocal('common.na')} @@ -62,7 +62,7 @@ const ShortTermsForm = () => ( {Localize.translateLocal('termsStep.shortTermsForm.cashReload')} - {Localize.translateLocal('common.na')} + {Localize.translateLocal('common.na')} diff --git a/src/pages/ErrorPage/GenericErrorPage.js b/src/pages/ErrorPage/GenericErrorPage.js index 711dae907d0a..81487e9a6af3 100644 --- a/src/pages/ErrorPage/GenericErrorPage.js +++ b/src/pages/ErrorPage/GenericErrorPage.js @@ -35,7 +35,7 @@ const GenericErrorPage = props => ( /> - + {props.translate('genericErrorPage.title')} diff --git a/src/pages/ReimbursementAccount/BankAccountManualStep.js b/src/pages/ReimbursementAccount/BankAccountManualStep.js index 25e767d24800..1a0679b3fe0e 100644 --- a/src/pages/ReimbursementAccount/BankAccountManualStep.js +++ b/src/pages/ReimbursementAccount/BankAccountManualStep.js @@ -87,7 +87,7 @@ class BankAccountManualStep extends React.Component { formID={ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM} onSubmit={this.submit} validate={this.validate} - submitButtonText={this.props.translate('common.saveAndContinue')} + submitButtonText={this.props.translate('common.continue')} style={[styles.mh5, styles.flexGrow1]} > @@ -136,6 +136,7 @@ class BankAccountManualStep extends React.Component { )} defaultValue={ReimbursementAccountUtils.getDefaultStateForField(this.props, 'acceptTerms', false)} + shouldSaveDraft /> diff --git a/src/pages/ReimbursementAccount/BankAccountStep.js b/src/pages/ReimbursementAccount/BankAccountStep.js index 7d1b8d3cdeec..186ab51aacd6 100644 --- a/src/pages/ReimbursementAccount/BankAccountStep.js +++ b/src/pages/ReimbursementAccount/BankAccountStep.js @@ -1,5 +1,7 @@ import React from 'react'; -import {View, ScrollView} from 'react-native'; +import { + View, ScrollView, TouchableWithoutFeedback, Linking, +} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import PropTypes from 'prop-types'; import lodashGet from 'lodash/get'; @@ -160,17 +162,22 @@ const BankAccountStep = (props) => { {props.translate('common.privacy')} - - - {props.translate('bankAccount.yourDataIsSecure')} - - - + Linking.openURL('https://community.expensify.com/discussion/5677/deep-dive-how-expensify-protects-your-information/')} + > + + + {props.translate('bankAccount.yourDataIsSecure')} + + + + - + diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index 6efd4bf724c4..62afb287ed0e 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -131,7 +131,7 @@ class ReportDetailsPage extends Component { displayNamesWithTooltips={displayNamesWithTooltips} tooltipEnabled numberOfLines={1} - textStyles={[styles.headerText, styles.mb2, styles.textAlignCenter]} + textStyles={[styles.textHeadline, styles.mb2, styles.textAlignCenter]} shouldUseFullTitle={isChatRoom || isPolicyExpenseChat} /> @@ -140,7 +140,7 @@ class ReportDetailsPage extends Component { styles.sidebarLinkText, styles.optionAlternateText, styles.textLabelSupporting, - styles.mb5, + styles.mb2, ]} numberOfLines={1} > diff --git a/src/pages/ReportSettingsPage.js b/src/pages/ReportSettingsPage.js index db5b9952c483..f9cc86372fe9 100644 --- a/src/pages/ReportSettingsPage.js +++ b/src/pages/ReportSettingsPage.js @@ -90,19 +90,19 @@ class ReportSettingsPage extends Component { return errors; } - // Show error if the room name already exists - if (ValidationUtils.isExistingRoomName(values.newRoomName, this.props.reports, this.props.report.policyID)) { - errors.newRoomName = this.props.translate('newRoomPage.roomAlreadyExistsError'); - } - - // We error if the user doesn't enter a room name or left blank + // The following validations are ordered by precedence. + // First priority: We error if the user doesn't enter a room name or left blank if (!values.newRoomName || values.newRoomName === CONST.POLICY.ROOM_PREFIX) { errors.newRoomName = this.props.translate('newRoomPage.pleaseEnterRoomName'); - } - - // Certain names are reserved for default rooms and should not be used for policy rooms. - if (ValidationUtils.isReservedRoomName(values.newRoomName)) { + } else if (ValidationUtils.isReservedRoomName(values.newRoomName)) { + // Second priority: Certain names are reserved for default rooms and should not be used for policy rooms. errors.newRoomName = this.props.translate('newRoomPage.roomNameReservedError'); + } else if (ValidationUtils.isExistingRoomName(values.newRoomName, this.props.reports, this.props.report.policyID)) { + // Third priority: Show error if the room name already exists + errors.newRoomName = this.props.translate('newRoomPage.roomAlreadyExistsError'); + } else if (!ValidationUtils.isValidRoomName(values.newRoomName)) { + // Fourth priority: We error if the room name has invalid characters + errors.newRoomName = this.props.translate('newRoomPage.roomNameInvalidError'); } return errors; diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index 0d1bf78064d8..3cefc6a98c3d 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -19,6 +19,7 @@ import withLocalize, {withLocalizePropTypes} from '../components/withLocalize'; import compose from '../libs/compose'; import personalDetailsPropType from './personalDetailsPropType'; import reportPropTypes from './reportPropTypes'; +import Performance from '../libs/Performance'; const propTypes = { /* Onyx Props */ @@ -48,7 +49,9 @@ class SearchPage extends Component { super(props); Timing.start(CONST.TIMING.SEARCH_RENDER); + Performance.markStart(CONST.TIMING.SEARCH_RENDER); + this.searchRendered = this.searchRendered.bind(this); this.selectReport = this.selectReport.bind(this); this.onChangeText = this.onChangeText.bind(this); this.debouncedUpdateOptions = _.debounce(this.updateOptions.bind(this), 75); @@ -72,10 +75,6 @@ class SearchPage extends Component { }; } - componentDidMount() { - Timing.end(CONST.TIMING.SEARCH_RENDER); - } - onChangeText(searchValue = '') { this.setState({searchValue}, this.debouncedUpdateOptions); } @@ -118,6 +117,11 @@ class SearchPage extends Component { return sections; } + searchRendered() { + Timing.end(CONST.TIMING.SEARCH_RENDER); + Performance.markEnd(CONST.TIMING.SEARCH_RENDER); + } + updateOptions() { const { recentReports, @@ -183,6 +187,7 @@ class SearchPage extends Component { showTitleTooltip shouldShowOptions={didScreenTransitionEnd} placeholderText={this.props.translate('optionsSelector.nameEmailOrPhoneNumber')} + onLayout={this.searchRendered} /> diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index 2e048ad18bd8..fa2680d10734 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -78,15 +78,15 @@ const HeaderView = (props) => { {props.isSmallScreenWidth && ( - - + + - - + + )} {Boolean(props.report && title) && ( { const newState = { - isCommentEmpty: !!newComment.match(/^(\s|`)*$/), + isCommentEmpty: !!newComment.match(/^(\s)*$/), value: newComment, }; if (comment !== newComment) { @@ -687,7 +687,7 @@ class ReportActionCompose extends React.Component { )} - {canUseTouchScreen() && this.props.isMediumScreenWidth ? null : ( + {DeviceCapabilities.canUseTouchScreen() && this.props.isMediumScreenWidth ? null : ( this.focus(true)} diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index b1a1b3790b05..02e9bed18135 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -19,7 +19,7 @@ import ReportActionItemCreated from './ReportActionItemCreated'; import compose from '../../../libs/compose'; import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions'; import ControlSelection from '../../../libs/ControlSelection'; -import canUseTouchScreen from '../../../libs/canUseTouchscreen'; +import * as DeviceCapabilities from '../../../libs/DeviceCapabilities'; import MiniReportActionContextMenu from './ContextMenu/MiniReportActionContextMenu'; import * as ReportActionContextMenu from './ContextMenu/ReportActionContextMenu'; import * as ContextMenuActions from './ContextMenu/ContextMenuActions'; @@ -142,6 +142,7 @@ class ReportActionItem extends Component { children = ( this.popoverAnchor = el} - onPressIn={() => this.props.isSmallScreenWidth && canUseTouchScreen() && ControlSelection.block()} + onPressIn={() => this.props.isSmallScreenWidth && DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()} onPressOut={() => ControlSelection.unblock()} onSecondaryInteraction={this.showPopover} preventDefaultContentMenu={!this.props.draftMessage} diff --git a/src/pages/home/report/ReportActionItemFragment.js b/src/pages/home/report/ReportActionItemFragment.js index bd1694acfee0..c4f593517df5 100644 --- a/src/pages/home/report/ReportActionItemFragment.js +++ b/src/pages/home/report/ReportActionItemFragment.js @@ -12,7 +12,7 @@ import Tooltip from '../../../components/Tooltip'; import * as EmojiUtils from '../../../libs/EmojiUtils'; import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions'; import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; -import canUseTouchScreen from '../../../libs/canUseTouchscreen'; +import * as DeviceCapabilities from '../../../libs/DeviceCapabilities'; import compose from '../../../libs/compose'; import * as StyleUtils from '../../../styles/StyleUtils'; @@ -120,7 +120,7 @@ const ReportActionItemFragment = (props) => { return ( {StyleUtils.convertToLTR(Str.htmlDecode(text))} diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js index a4958ab1b78b..73fc5781a079 100644 --- a/src/pages/home/report/ReportActionItemSingle.js +++ b/src/pages/home/report/ReportActionItemSingle.js @@ -1,3 +1,4 @@ +import lodashGet from 'lodash/get'; import React from 'react'; import {View, Pressable} from 'react-native'; import PropTypes from 'prop-types'; @@ -17,6 +18,7 @@ import ROUTES from '../../../ROUTES'; import {withPersonalDetails} from '../../../components/OnyxProvider'; import Tooltip from '../../../components/Tooltip'; import ControlSelection from '../../../libs/ControlSelection'; +import OfflineWithFeedback from '../../../components/OfflineWithFeedback'; const propTypes = { /** All the data of the action */ @@ -49,7 +51,12 @@ const showUserDetails = (email) => { }; const ReportActionItemSingle = (props) => { - const {avatar, displayName, login} = props.personalDetails[props.action.actorEmail] || {}; + const { + avatar, + displayName, + login, + pendingFields, + } = props.personalDetails[props.action.actorEmail] || {}; const avatarUrl = props.action.automatic ? CONST.CONCIERGE_ICON_URL @@ -72,10 +79,14 @@ const ReportActionItemSingle = (props) => { onPress={() => showUserDetails(props.action.actorEmail)} > - + + + diff --git a/src/pages/home/report/ReportDropUI.js b/src/pages/home/report/ReportDropUI.js index 88fdf202d56b..0ca66904e482 100644 --- a/src/pages/home/report/ReportDropUI.js +++ b/src/pages/home/report/ReportDropUI.js @@ -17,7 +17,7 @@ const ReportDropUI = props => ( - + {props.translate('reportActionCompose.dropToUpload')} diff --git a/src/pages/home/sidebar/SidebarLinks.js b/src/pages/home/sidebar/SidebarLinks.js index 00b0424844be..fe2813db03eb 100644 --- a/src/pages/home/sidebar/SidebarLinks.js +++ b/src/pages/home/sidebar/SidebarLinks.js @@ -1,3 +1,4 @@ +import lodashGet from 'lodash/get'; import React from 'react'; import {View, TouchableOpacity} from 'react-native'; import _ from 'underscore'; @@ -27,6 +28,7 @@ import reportActionPropTypes from '../report/reportActionPropTypes'; import LHNOptionsList from '../../../components/LHNOptionsList/LHNOptionsList'; import SidebarUtils from '../../../libs/SidebarUtils'; import reportPropTypes from '../../reportPropTypes'; +import OfflineWithFeedback from '../../../components/OfflineWithFeedback'; const propTypes = { /** Toggles the navigation menu open and closed */ @@ -142,11 +144,11 @@ class SidebarLinks extends React.Component { nativeID="drag-area" >

- + + + diff --git a/src/pages/iou/IOUTransactions.js b/src/pages/iou/IOUTransactions.js index 4b203e324c23..7b540336c3a5 100644 --- a/src/pages/iou/IOUTransactions.js +++ b/src/pages/iou/IOUTransactions.js @@ -86,6 +86,7 @@ class IOUTransactions extends Component { - {canUseTouchScreen() + {DeviceCapabilities.canUseTouchScreen() ? ( { {_.map(menuItems, item => ( props.isSmallScreenWidth && canUseTouchScreen() && ControlSelection.block()} + onPressIn={() => props.isSmallScreenWidth && DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()} onPressOut={() => ControlSelection.unblock()} onSecondaryInteraction={e => showPopover(e, item.link)} ref={el => popoverAnchor = el} diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index 7f78d11cc881..6ed18b7e8c58 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -1,3 +1,4 @@ +import lodashGet from 'lodash/get'; import React from 'react'; import {View, ScrollView, Pressable} from 'react-native'; import PropTypes from 'prop-types'; @@ -30,6 +31,7 @@ import * as Wallet from '../../libs/actions/Wallet'; import walletTermsPropTypes from '../EnablePayments/walletTermsPropTypes'; import * as PolicyUtils from '../../libs/PolicyUtils'; import ConfirmModal from '../../components/ConfirmModal'; +import OfflineWithFeedback from '../../components/OfflineWithFeedback'; const propTypes = { /* Onyx Props */ @@ -244,16 +246,20 @@ class InitialSettingsPage extends React.Component { - + + + - + {this.props.currentUserPersonalDetails.displayName ? this.props.currentUserPersonalDetails.displayName : Str.removeSMSDomain(this.props.session.email)} diff --git a/src/pages/settings/Payments/TransferBalancePage.js b/src/pages/settings/Payments/TransferBalancePage.js index 792866e56db4..b1c1a7dc29cf 100644 --- a/src/pages/settings/Payments/TransferBalancePage.js +++ b/src/pages/settings/Payments/TransferBalancePage.js @@ -186,7 +186,7 @@ class TransferBalancePage extends React.Component { onBackButtonPress={() => Navigation.goBack()} onCloseButtonPress={() => Navigation.dismissModal(true)} /> - + diff --git a/src/pages/settings/Security/CloseAccountPage.js b/src/pages/settings/Security/CloseAccountPage.js index 2871f76e4a35..fd34504f7cd3 100644 --- a/src/pages/settings/Security/CloseAccountPage.js +++ b/src/pages/settings/Security/CloseAccountPage.js @@ -19,6 +19,7 @@ import * as CloseAccount from '../../../libs/actions/CloseAccount'; import ONYXKEYS from '../../../ONYXKEYS'; import Form from '../../../components/Form'; import CONST from '../../../CONST'; +import ConfirmModal from '../../../components/ConfirmModal'; const propTypes = { @@ -35,17 +36,35 @@ class CloseAccountPage extends Component { constructor(props) { super(props); - this.onSubmit = this.onSubmit.bind(this); + this.onConfirm = this.onConfirm.bind(this); this.validate = this.validate.bind(this); + this.hideConfirmModal = this.hideConfirmModal.bind(this); + this.showConfirmModal = this.showConfirmModal.bind(this); CloseAccount.clearError(); + this.state = { + isConfirmModalVisible: false, + reasonForLeaving: '', + }; } componentWillUnmount() { CloseAccount.clearError(); } - onSubmit(values) { - User.closeAccount(values.reasonForLeaving); + onConfirm() { + User.closeAccount(this.state.reasonForLeaving); + this.hideConfirmModal(); + } + + showConfirmModal(values) { + this.setState({ + isConfirmModalVisible: true, + reasonForLeaving: values.reasonForLeaving, + }); + } + + hideConfirmModal() { + this.setState({isConfirmModalVisible: false}); } validate(values) { @@ -71,7 +90,7 @@ class CloseAccountPage extends Component {
- - {this.props.translate('closeAccountPage.closeAccountWarning')} - + {this.props.translate('closeAccountPage.enterDefaultContactToConfirm')} {' '} - {this.props.translate('closeAccountPage.closeAccountPermanentlyDeleteData')} - - - {this.props.translate('closeAccountPage.defaultContact')} + {userEmailOrPhone} - {' '} - {userEmailOrPhone} + . + diff --git a/src/pages/signin/SignInPageLayout/SignInPageContent.js b/src/pages/signin/SignInPageLayout/SignInPageContent.js index 5f3ccb8a268a..2bfc28fc3046 100755 --- a/src/pages/signin/SignInPageLayout/SignInPageContent.js +++ b/src/pages/signin/SignInPageLayout/SignInPageContent.js @@ -1,5 +1,5 @@ import React from 'react'; -import {View, TouchableWithoutFeedback, Keyboard} from 'react-native'; +import {View} from 'react-native'; import PropTypes from 'prop-types'; import {withSafeAreaInsets} from 'react-native-safe-area-context'; import styles from '../../../styles/styles'; @@ -10,9 +10,9 @@ import TermsAndLicenses from '../TermsAndLicenses'; import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; import SignInPageForm from '../../../components/SignInPageForm'; import compose from '../../../libs/compose'; -import withKeyboardState, {keyboardStatePropTypes} from '../../../components/withKeyboardState'; import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions'; import KeyboardAvoidingView from '../../../components/KeyboardAvoidingView'; +import TouchableDismissKeyboard from '../../../components/TouchableDismissKeyboard'; const propTypes = { /** The children to show inside the layout */ @@ -27,66 +27,56 @@ const propTypes = { ...withLocalizePropTypes, ...windowDimensionsPropTypes, - ...keyboardStatePropTypes, }; -const SignInPageContent = (props) => { - const dismissKeyboardWhenTappedOutsideOfInput = () => { - if (!props.isKeyboardShown) { - return; - } - Keyboard.dismiss(); - }; +const SignInPageContent = props => ( + + + - - - // This vertical offset is here to add some more margin above the keyboard. Without it, the TOS and footer stuff still hides behind the keyboard by a few pixels. - keyboardVerticalOffset={50} - > - {/* This empty view creates margin on the top of the sign in form which will shrink and grow depending on if the keyboard is open or not */} - - - - - - + + + + + + {props.shouldShowWelcomeText && ( + + + {props.welcomeText} + - {props.shouldShowWelcomeText && ( - - - {props.welcomeText} - - - )} - {props.children} - - - - - - - - - ); -}; + )} + {props.children} + + + + + + +
+ +); SignInPageContent.propTypes = propTypes; SignInPageContent.displayName = 'SignInPageContent'; @@ -94,8 +84,5 @@ SignInPageContent.displayName = 'SignInPageContent'; export default compose( withWindowDimensions, withLocalize, - - // KeyboardState HOC is needed to trigger recalculation of the UI when keyboard opens or closes - withKeyboardState, withSafeAreaInsets, )(SignInPageContent); diff --git a/src/pages/workspace/WorkspaceInitialPage.js b/src/pages/workspace/WorkspaceInitialPage.js index 27e8a3b897c2..9ee7b141c5bc 100644 --- a/src/pages/workspace/WorkspaceInitialPage.js +++ b/src/pages/workspace/WorkspaceInitialPage.js @@ -224,7 +224,7 @@ class WorkspaceInitialPage extends React.Component { diff --git a/src/pages/workspace/WorkspaceInvitePage.js b/src/pages/workspace/WorkspaceInvitePage.js index 348af36ba089..b8b2812b23ec 100644 --- a/src/pages/workspace/WorkspaceInvitePage.js +++ b/src/pages/workspace/WorkspaceInvitePage.js @@ -345,15 +345,11 @@ class WorkspaceInvitePage extends React.Component { href={CONST.PRIVACY_URL} style={[styles.mh5, styles.mv2, styles.alignSelfStart]} > - {({hovered, pressed}) => ( - - - {this.props.translate('common.privacyPolicy')} - - - )} + + + {this.props.translate('common.privacyPolicy')} + +
diff --git a/src/pages/workspace/WorkspaceNewRoomPage.js b/src/pages/workspace/WorkspaceNewRoomPage.js index 34d40d2000ec..04bd167022f0 100644 --- a/src/pages/workspace/WorkspaceNewRoomPage.js +++ b/src/pages/workspace/WorkspaceNewRoomPage.js @@ -81,19 +81,19 @@ class WorkspaceNewRoomPage extends React.Component { validate(values) { const errors = {}; - // We error if the user doesn't enter a room name or left blank + // The following validations are ordered by precedence. + // First priority: We error if the user doesn't enter a room name or left blank if (!values.roomName || values.roomName === CONST.POLICY.ROOM_PREFIX) { errors.roomName = this.props.translate('newRoomPage.pleaseEnterRoomName'); - } - - // We error if the room name already exists. - if (ValidationUtils.isExistingRoomName(values.roomName, this.props.reports, values.policyID)) { - errors.roomName = this.props.translate('newRoomPage.roomAlreadyExistsError'); - } - - // Certain names are reserved for default rooms and should not be used for policy rooms. - if (ValidationUtils.isReservedRoomName(values.roomName)) { + } else if (ValidationUtils.isReservedRoomName(values.roomName)) { + // Second priority: Certain names are reserved for default rooms and should not be used for policy rooms. errors.roomName = this.props.translate('newRoomPage.roomNameReservedError'); + } else if (ValidationUtils.isExistingRoomName(values.roomName, this.props.reports, values.policyID)) { + // Third priority: We error if the room name already exists. + errors.roomName = this.props.translate('newRoomPage.roomAlreadyExistsError'); + } else if (!ValidationUtils.isValidRoomName(values.roomName)) { + // Fourth priority: We error if the room name has invalid characters + errors.roomName = this.props.translate('newRoomPage.roomNameInvalidError'); } if (!values.policyID) { diff --git a/src/styles/fontFamily/index.js b/src/styles/fontFamily/index.js index a72945b7c838..3e258bbef7b1 100644 --- a/src/styles/fontFamily/index.js +++ b/src/styles/fontFamily/index.js @@ -5,6 +5,8 @@ const fontFamily = { EXP_NEUE_ITALIC: 'ExpensifyNeue-Italic', EXP_NEUE_BOLD: bold, EXP_NEUE: 'ExpensifyNeue-Regular', + EXP_NEW_KANSAS_MEDIUM: 'ExpensifyNewKansas-Medium', + EXP_NEW_KANSAS_MEDIUM_ITALIC: 'ExpensifyNewKansas-MediumItalic', SYSTEM: 'System', MONOSPACE: 'ExpensifyMono-Regular', MONOSPACE_ITALIC: 'ExpensifyMono-Regular', diff --git a/src/styles/getTooltipStyles.js b/src/styles/getTooltipStyles.js index 615f4fc1220e..c2816665a45b 100644 --- a/src/styles/getTooltipStyles.js +++ b/src/styles/getTooltipStyles.js @@ -104,6 +104,9 @@ export default function getTooltipStyles( ? tooltipTextWidth + (spacing.ph2.paddingHorizontal * 2) + 1 : maxWidth; + // Hide the tooltip entirely if it's position hasn't finished measuring yet. This prevents UI jank where the tooltip flashes in the top left corner of the screen. + const opacity = (xOffset === 0 && yOffset === 0) ? 0 : 1; + return { animationStyle: { // remember Transform causes a new Local cordinate system @@ -151,6 +154,8 @@ export default function getTooltipStyles( // 3) Add the horizontal shift (left or right) computed above to keep it out of the gutters. // 4) Lastly, add the manual horizontal shift passed in as a parameter. left: xOffset + ((componentWidth / 2) - (tooltipWidth / 2)) + horizontalShift + manualShiftHorizontal, + + opacity, }, tooltipTextStyle: { color: themeColors.textReversed, @@ -161,20 +166,15 @@ export default function getTooltipStyles( pointerWrapperStyle: { position: 'fixed', - // By default, the pointer's top-left will align with the top-left of the wrapped tooltip. + // By default, the pointer's top-left will align with the top-left of the tooltip wrapper. // // To align it vertically, we'll: + // If the pointer should be below the tooltip wrapper, shift the pointer down (+) by the tooltip height, + // so that the top of the pointer lines up with the bottom of the tooltip // - // Shift the pointer up (-) by component's height, so that the bottom of the pointer lines up - // with the top of the wrapped component. - // - // OR if it should show below: - // - // Shift the pointer down (+) by the component's height, - // so that the top of the pointer aligns with the bottom of the component. - // - // Always add the manual vertical shift passed in as a parameter. - top: shouldShowBelow ? (manualShiftVertical - POINTER_HEIGHT) : (tooltipHeight + manualShiftVertical), + // OR if the pointer should be above the tooltip wrapper, then the pointer up (-) by the pointer's height + // so that the bottom of the pointer lines up with the top of the tooltip + top: shouldShowBelow ? -POINTER_HEIGHT : tooltipHeight, // To align it horizontally, we'll: // 1) Shift the pointer to the right (+) by the half the tooltipWidth's width, @@ -184,6 +184,8 @@ export default function getTooltipStyles( // 3) Due to the tip start from the left edge of wrapper Tooltip so we have to remove the // horizontalShift which is added to adjust it into the Window left: -horizontalShift + ((tooltipWidth / 2) - (POINTER_WIDTH / 2)), + + opacity, }, pointerStyle: { width: 0, diff --git a/src/styles/styles.js b/src/styles/styles.js index 290ab6c7cb14..d27d4eb2f25d 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -48,6 +48,11 @@ const baseCodeTagStyles = { backgroundColor: themeColors.textBackground, }; +const headlineFont = { + fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, + fontWeight: '500', +}; + const webViewStyles = { // As of react-native-render-html v6, don't declare distinct styles for // custom renderers, the API for custom renderers has changed. Declare the @@ -169,10 +174,6 @@ const styles = { link, - linkHovered: { - color: themeColors.linkHover, - }, - linkMuted: { color: themeColors.textSupporting, textDecorationColor: themeColors.textSupporting, @@ -261,15 +262,16 @@ const styles = { fontSize: variables.fontSizeLarge, }, + textXLarge: { + fontSize: variables.fontSizeXLarge, + }, + textXXLarge: { fontSize: variables.fontSizeXXLarge, }, textXXXLarge: { - color: themeColors.heading, - fontFamily: fontFamily.EXP_NEUE_BOLD, fontSize: variables.fontSizeXXXLarge, - fontWeight: fontWeightBold, }, textStrong: { @@ -282,6 +284,12 @@ const styles = { fontStyle: 'italic', }, + textHeadline: { + ...headlineFont, + color: themeColors.heading, + fontSize: variables.fontSizeXLarge, + }, + textDecorationNoLine: { textDecorationLine: 'none', }, @@ -373,6 +381,9 @@ const styles = { // It is needed to unset the Lineheight. We don't need it for buttons as button always contains single line of text. // It allows to vertically center the text. lineHeight: undefined, + + // Add 1px to the Button text to give optical vertical alignment. + paddingBottom: 1, }, buttonSmall: { @@ -482,6 +493,10 @@ const styles = { buttonCTAIcon: { marginRight: 22, + + // Align vertically with the Button text + paddingBottom: 1, + paddingTop: 1, }, buttonConfirm: { @@ -761,6 +776,10 @@ const styles = { scrollPadding: '23px 0 0 0', }, + textInputMultilineContainer: { + paddingTop: 23, + }, + textInputAndIconContainer: { flex: 1, height: '100%', @@ -1119,7 +1138,6 @@ const styles = { popoverMenuText: { fontSize: variables.fontSizeNormal, color: themeColors.heading, - maxWidth: 240, }, menuItemTextContainer: { @@ -1905,10 +1923,11 @@ const styles = { }, notFoundTextHeader: { - color: themeColors.link, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - fontSize: 150, + ...headlineFont, + color: themeColors.heading, + fontSize: variables.fontSizeXLarge, + marginTop: 20, + marginBottom: 8, }, notFoundTextBody: { @@ -2021,13 +2040,6 @@ const styles = { position: 'absolute', }, - displayName: { - fontSize: variables.fontSizeLarge, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - color: themeColors.heading, - }, - pageWrapper: { width: '100%', alignItems: 'center', @@ -2195,16 +2207,14 @@ const styles = { }, iouAmountText: { - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, + ...headlineFont, fontSize: variables.iouAmountTextSize, color: themeColors.heading, lineHeight: variables.inputHeight, }, iouAmountTextInput: addOutlineWidth({ - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, + ...headlineFont, fontSize: variables.iouAmountTextSize, color: themeColors.heading, padding: 0, @@ -2572,18 +2582,18 @@ const styles = { shortTermsBorder: { borderWidth: 1, - borderColor: themeColors.shadow, + borderColor: themeColors.border, }, shortTermsHorizontalRule: { borderBottomWidth: 1, - borderColor: themeColors.shadow, + borderColor: themeColors.border, ...spacing.mh3, }, shortTermsLargeHorizontalRule: { borderWidth: 1, - borderColor: themeColors.shadow, + borderColor: themeColors.border, ...spacing.mh3, }, diff --git a/src/styles/variables.js b/src/styles/variables.js index 7d818f8be207..05808f7d6fff 100644 --- a/src/styles/variables.js +++ b/src/styles/variables.js @@ -42,6 +42,7 @@ export default { fontSizeLarge: getValueUsingPixelRatio(17, 19), fontSizeHero: 36, fontSizeh1: 19, + fontSizeXLarge: 22, fontSizeXXLarge: 28, fontSizeXXXLarge: 32, fontSizeNormalHeight: getValueUsingPixelRatio(20, 28), diff --git a/tests/e2e/README.md b/tests/e2e/README.md index 3262770bc55f..2aa9a2a84800 100644 --- a/tests/e2e/README.md +++ b/tests/e2e/README.md @@ -37,6 +37,7 @@ The tests can be run with the following CLI options: the existing native app with the new package. If there is no existing native app, it will fallback to mode "full" 3. **skip**: does not rebuild anything, and just runs the existing native app +- `--skipCheckout`: Won't checkout any baseline or comparison branch, and will just run the tests ## Available environment variables diff --git a/tests/e2e/TestSpec.yml b/tests/e2e/TestSpec.yml index 36a6e784727b..6ea1d9570ae9 100644 --- a/tests/e2e/TestSpec.yml +++ b/tests/e2e/TestSpec.yml @@ -20,7 +20,7 @@ phases: commands: - cd zip - npm install underscore - - node e2e/testRunner.js -- --skipInstallDeps --skipBuild + - node e2e/testRunner.js -- --skipInstallDeps --buildMode "skip" --skipCheckout artifacts: - $WORKING_DIRECTORY diff --git a/tests/e2e/config.js b/tests/e2e/config.js index 34f81467866d..d322fb970b2d 100644 --- a/tests/e2e/config.js +++ b/tests/e2e/config.js @@ -8,6 +8,7 @@ const OUTPUT_DIR = process.env.WORKING_DIRECTORY || './tests/e2e/results'; // add your test name here … const TEST_NAMES = { AppStartTime: 'App start time', + OpenSearchPage: 'Open search page TTI', }; /** @@ -65,5 +66,8 @@ module.exports = { // ... any additional config you might need }, + [TEST_NAMES.OpenSearchPage]: { + name: TEST_NAMES.OpenSearchPage, + }, }, }; diff --git a/tests/e2e/testRunner.js b/tests/e2e/testRunner.js index a82dc3a9212a..1f2158b91d9f 100644 --- a/tests/e2e/testRunner.js +++ b/tests/e2e/testRunner.js @@ -78,7 +78,7 @@ const restartApp = async () => { await launchApp('android'); }; -const runTestsOnBranch = async (branch, baselineOrCompare) => { +const runTestsOnBranch = async (baselineOrCompare, branch) => { if (args.includes('--buildMode')) { buildMode = args[args.indexOf('--buildMode') + 1]; } @@ -93,9 +93,11 @@ const runTestsOnBranch = async (branch, baselineOrCompare) => { } } - // Switch branch - Logger.log(`Preparing ${baselineOrCompare} tests on branch '${branch}'`); - await execAsync(`git checkout ${branch}`); + if (branch != null) { + // Switch branch + Logger.log(`Preparing ${baselineOrCompare} tests on branch '${branch}'`); + await execAsync(`git checkout ${branch}`); + } if (!args.includes('--skipInstallDeps')) { Logger.log(`Preparing ${baselineOrCompare} tests on branch '${branch}' - npm install`); @@ -113,7 +115,7 @@ const runTestsOnBranch = async (branch, baselineOrCompare) => { const tempDir = `${config.OUTPUT_DIR}/temp`; const tempBundlePath = `${tempDir}/index.android.bundle`; await execAsync(`rm -rf ${tempDir} && mkdir ${tempDir}`); - await execAsync(`npx react-native bundle --platform android --dev false --entry-file ${config.ENTRY_FILE} --bundle-output ${tempBundlePath}`); + await execAsync(`E2E_TESTING=true npx react-native bundle --platform android --dev false --entry-file ${config.ENTRY_FILE} --bundle-output ${tempBundlePath}`); // Repackage the existing native app with the new bundle const tempApkPath = `${tempDir}/app-release.apk`; @@ -152,7 +154,7 @@ const runTestsOnBranch = async (branch, baselineOrCompare) => { for (let testIndex = 0; testIndex < numOfTests; testIndex++) { const testConfig = _.values(config.TESTS_CONFIG)[testIndex]; - // check if we want to skip the text + // check if we want to skip the test if (args.includes('--includes')) { const includes = args[args.indexOf('--includes') + 1]; @@ -230,11 +232,13 @@ const runTests = async () => { Logger.info('Running e2e tests'); try { + const skipCheckout = args.includes('--skipCheckout'); + // Run tests on baseline branch - await runTestsOnBranch(baselineBranch, 'baseline'); + await runTestsOnBranch('baseline', skipCheckout ? null : baselineBranch); // Run tests on current branch - await runTestsOnBranch('-', 'compare'); + await runTestsOnBranch('compare', skipCheckout ? null : '-'); await compare(); diff --git a/tests/unit/GithubUtilsTest.js b/tests/unit/GithubUtilsTest.js index 61d2574ad41f..e374941bbad6 100644 --- a/tests/unit/GithubUtilsTest.js +++ b/tests/unit/GithubUtilsTest.js @@ -403,7 +403,7 @@ describe('GithubUtils', () => { // eslint-disable-next-line max-len const timingDashboardVerification = 'I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.'; // eslint-disable-next-line max-len - const firebaseVerification = 'I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.'; + const firebaseVerification = 'I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).'; // Valid output which will be reused in the deploy blocker tests const allVerifiedExpectedOutput = `${baseExpectedOutput}` diff --git a/tests/unit/IOUUtilsTest.js b/tests/unit/IOUUtilsTest.js new file mode 100644 index 000000000000..94120936631a --- /dev/null +++ b/tests/unit/IOUUtilsTest.js @@ -0,0 +1,134 @@ +import * as IOUUtils from '../../src/libs/IOUUtils'; +import * as ReportUtils from '../../src/libs/ReportUtils'; + +let iouReport; +let reportActions; +const ownerEmail = 'owner@iou.com'; +const managerEmail = 'manager@iou.com'; + +function createIOUReportAction(type, amount, currency, {IOUTransactionID, isOnline} = {}) { + const moneyRequestAction = ReportUtils.buildOptimisticIOUReportAction( + 1, + type, + amount, + currency, + 'Test comment', + [managerEmail], + '', + IOUTransactionID, + iouReport.reportID, + ); + + // Default is to create requests offline, if this is specified then we need to remove the pendingAction + moneyRequestAction.pendingAction = isOnline ? null : 'add'; + + reportActions.push(moneyRequestAction); + return moneyRequestAction; +} + +function cancelMoneyRequest(moneyRequestAction, {isOnline} = {}) { + createIOUReportAction( + 'cancel', + moneyRequestAction.originalMessage.amount, + moneyRequestAction.originalMessage.currency, + { + IOUTransactionID: moneyRequestAction.originalMessage.IOUTransactionID, + isOnline, + }, + ); +} + +beforeEach(() => { + reportActions = []; + const chatReportID = ReportUtils.generateReportID(); + const amount = 1000; + const currency = 'USD'; + + iouReport = ReportUtils.buildOptimisticIOUReport( + ownerEmail, + managerEmail, + amount, + chatReportID, + currency, + 'en', + ); + + // The starting point of all tests is the IOUReport containing a single non-pending transaction in USD + // All requests in the tests are assumed to be offline, unless isOnline is specified + createIOUReportAction('create', amount, currency, {IOUTransactionID: '', isOnline: true}); +}); + +describe('isIOUReportPendingCurrencyConversion', () => { + test('Requesting money offline in a different currency will show the pending conversion message', () => { + // Request money offline in AED + createIOUReportAction('create', 100, 'AED'); + + // We requested money offline in a different currency, we don't know the total of the iouReport until we're back online + expect(IOUUtils.isIOUReportPendingCurrencyConversion(reportActions, iouReport)).toBe(true); + }); + + test('IOUReport is not pending conversion when all requests made offline have been cancelled', () => { + // Create two requests offline + const moneyRequestA = createIOUReportAction('create', 1000, 'AED'); + const moneyRequestB = createIOUReportAction('create', 1000, 'AED'); + + // Cancel both requests + cancelMoneyRequest(moneyRequestA); + cancelMoneyRequest(moneyRequestB); + + // Both requests made offline have been cancelled, total won't update so no need to show a pending conversion message + expect(IOUUtils.isIOUReportPendingCurrencyConversion(reportActions, iouReport)).toBe(false); + }); + + test('Cancelling a request made online shows the preview', () => { + // Request money online in AED + const moneyRequest = createIOUReportAction('create', 1000, 'AED', {isOnline: true}); + + // Cancel it offline + cancelMoneyRequest(moneyRequest); + + // We don't know what the total is because we need to subtract the converted amount of the offline request from the total + expect(IOUUtils.isIOUReportPendingCurrencyConversion(reportActions, iouReport)).toBe(true); + }); + + test('Cancelling a request made offline while there\'s a previous one made online will not show the pending conversion message', () => { + // Request money online in AED + createIOUReportAction('create', 1000, 'AED', {isOnline: true}); + + // Another request offline + const moneyRequestOffline = createIOUReportAction('create', 1000, 'AED'); + + // Cancel the request made offline + cancelMoneyRequest(moneyRequestOffline); + + expect(IOUUtils.isIOUReportPendingCurrencyConversion(reportActions, iouReport)).toBe(false); + }); + + test('Cancelling a request made online while we have one made offline will show the pending conversion message', () => { + // Request money online in AED + const moneyRequestOnline = createIOUReportAction('create', 1000, 'AED', {isOnline: true}); + + // Requet money again but offline + createIOUReportAction('create', 1000, 'AED'); + + // Cancel the request made online + cancelMoneyRequest(moneyRequestOnline); + + // We don't know what the total is because we need to subtract the converted amount of the offline request from the total + expect(IOUUtils.isIOUReportPendingCurrencyConversion(reportActions, iouReport)).toBe(true); + }); + + test('Cancelling a request offline in the report\'s currency when we have requests in a different currency does not show the pending conversion message', () => { + // Request money in the report's curreny (USD) + const onlineMoneyRequestInUSD = createIOUReportAction('create', 1000, 'USD', {isOnline: true}); + + // Request money online in a different currency + createIOUReportAction('create', 2000, 'AED', {isOnline: true}); + + // Cancel the USD request offline + cancelMoneyRequest(onlineMoneyRequestInUSD); + + expect(IOUUtils.isIOUReportPendingCurrencyConversion(reportActions, iouReport)).toBe(false); + }); +}); + diff --git a/tests/unit/ValidationUtilsTest.js b/tests/unit/ValidationUtilsTest.js index 82f28a37664e..ca76e0727b33 100644 --- a/tests/unit/ValidationUtilsTest.js +++ b/tests/unit/ValidationUtilsTest.js @@ -22,4 +22,30 @@ describe('ValidationUtils', () => { expect(ValidationUtils.isValidTwoFactorCode('123$%^')).toBe(false); }); }); + + describe('isValidRoomName', () => { + test('room name without #', () => { + expect(ValidationUtils.isValidRoomName('test')).toBe(false); + }); + + test('room name with upper case letters', () => { + expect(ValidationUtils.isValidRoomName('#Test')).toBe(false); + }); + + test('room name with special character other than dash', () => { + expect(ValidationUtils.isValidRoomName('#test_room')).toBe(false); + }); + + test('room name with less than one character', () => { + expect(ValidationUtils.isValidRoomName('#')).toBe(false); + }); + + test('room name with 81 characters', () => { + expect(ValidationUtils.isValidRoomName('#123456789012345678901234567890123456789012345678901234567890123456789012345678901')).toBe(false); + }); + + test('room name with lowercase letters, numbers, and dashes', () => { + expect(ValidationUtils.isValidRoomName('#this-is-a-room1')).toBe(true); + }); + }); }); diff --git a/tests/unit/createOrUpdateStagingDeployTest.js b/tests/unit/createOrUpdateStagingDeployTest.js index fdb553fcfd87..f4c5d0bb9edf 100644 --- a/tests/unit/createOrUpdateStagingDeployTest.js +++ b/tests/unit/createOrUpdateStagingDeployTest.js @@ -105,7 +105,7 @@ const deployerVerificationsHeader = '**Deployer verifications:**'; // eslint-disable-next-line max-len const timingDashboardVerification = 'I checked the [App Timing Dashboard](https://graphs.expensify.com/grafana/d/yj2EobAGz/app-timing?orgId=1) and verified this release does not cause a noticeable performance regression.'; // eslint-disable-next-line max-len -const firebaseVerification = 'I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes.'; +const firebaseVerification = 'I checked [Firebase Crashlytics](https://console.firebase.google.com/u/0/project/expensify-chat/crashlytics/app/android:com.expensify.chat/issues?state=open&time=last-seven-days&tag=all) and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).'; const ccApplauseLeads = 'cc @Expensify/applauseleads\r\n'; const deployBlockerHeader = '**Deploy Blockers:**'; const lineBreak = '\r\n'; diff --git a/web/proxy.js b/web/proxy.js index c83ed3529a30..768963d0810c 100644 --- a/web/proxy.js +++ b/web/proxy.js @@ -31,6 +31,7 @@ const server = http.createServer((request, response) => { headers: { ...request.headers, host, + 'user-agent': request.headers['user-agent'].concat(' Development-NewDot/1.0'), }, port: 443, });