diff --git a/.github/ISSUE_TEMPLATE/Accessibility.md b/.github/ISSUE_TEMPLATE/Accessibility.md
index 1323e2c17e78..97fc17d28a94 100644
--- a/.github/ISSUE_TEMPLATE/Accessibility.md
+++ b/.github/ISSUE_TEMPLATE/Accessibility.md
@@ -35,12 +35,12 @@ What can we do to fix the issue?
Check off any platforms that are affected by this issue
--->
Which of our officially supported platforms is this issue occurring on? Please only tick the box if you have provided a screen-recording in the thread for each platform:
-- [ ] Android / native
-- [ ] Android / Chrome
-- [ ] iOS / native
-- [ ] iOS / Safari
-- [ ] MacOS / Chrome / Safari
-- [ ] MacOS / Desktop
+- [ ] Android: Native
+- [ ] Android: mWeb Chrome
+- [ ] iOS: Native
+- [ ] iOS: mWeb Safari
+- [ ] MacOS: Chrome / Safari
+- [ ] MacOS: Desktop
**Version Number:**
**Reproducible in staging?:**
diff --git a/.github/ISSUE_TEMPLATE/Performance.md b/.github/ISSUE_TEMPLATE/Performance.md
index 67b2e6971874..bbb729e8af31 100644
--- a/.github/ISSUE_TEMPLATE/Performance.md
+++ b/.github/ISSUE_TEMPLATE/Performance.md
@@ -28,12 +28,12 @@ Note: These should be the same as the benchmarks collected before any changes.
Check off any platforms that are affected by this issue
--->
Which of our officially supported platforms is this issue occurring on?
-- [ ] Android / native
-- [ ] Android / Chrome
-- [ ] iOS / native
-- [ ] iOS / Safari
-- [ ] MacOS / Chrome / Safari
-- [ ] MacOS / Desktop
+- [ ] Android: Native
+- [ ] Android: mWeb Chrome
+- [ ] iOS: Native
+- [ ] iOS: mWeb Safari
+- [ ] MacOS: Chrome / Safari
+- [ ] MacOS: Desktop
**Version Number:**
**Reproducible in staging?:**
diff --git a/.github/ISSUE_TEMPLATE/Standard.md b/.github/ISSUE_TEMPLATE/Standard.md
index 39d1c38fa56f..5e0e3633f3bc 100644
--- a/.github/ISSUE_TEMPLATE/Standard.md
+++ b/.github/ISSUE_TEMPLATE/Standard.md
@@ -7,6 +7,16 @@ labels: Bug, Daily
If you haven’t already, check out our [contributing guidelines](https://github.com/Expensify/ReactNativeChat/blob/main/contributingGuides/CONTRIBUTING.md) for onboarding and email contributors@expensify.com to request to join our Slack channel!
___
+**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
+**Expensify/Expensify Issue URL:**
+**Issue reported by:**
+**Slack conversation:**
+
## Action Performed:
Break down in numbered steps
@@ -24,22 +34,54 @@ Can the user still use Expensify without this being fixed? Have you informed the
Check off any platforms that are affected by this issue
--->
Which of our officially supported platforms is this issue occurring on?
-- [ ] Android / native
-- [ ] Android / Chrome
-- [ ] iOS / native
-- [ ] iOS / Safari
-- [ ] MacOS / Chrome / Safari
-- [ ] MacOS / Desktop
+- [ ] Android: Native
+- [ ] Android: mWeb Chrome
+- [ ] iOS: Native
+- [ ] iOS: mWeb Safari
+- [ ] MacOS: Chrome / Safari
+- [ ] MacOS: Desktop
-**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
-**Expensify/Expensify Issue URL:**
-**Issue reported by:**
-**Slack conversation:**
+## Screenshots/Videos
+
+Android: Native
+
+
+
+
+
+
+Android: mWeb Chrome
+
+
+
+
+
+
+iOS: Native
+
+
+
+
+
+
+iOS: mWeb Safari
+
+
+
+
+
+
+MacOS: Chrome / Safari
+
+
+
+
+
+
+MacOS: Desktop
+
+
+
+
[View all open jobs on GitHub](https://github.com/Expensify/App/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22)
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 800888580518..0396a7543b50 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -70,12 +70,12 @@ This is a checklist for PR authors. Please make sure to complete all tasks and c
- [ ] I tested this PR with a [High Traffic account](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#high-traffic-accounts) against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
- [ ] I included screenshots or videos for tests on [all platforms](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#make-sure-you-can-test-on-all-platforms)
- [ ] I ran the tests on **all platforms** & verified they passed on:
- - [ ] Android / native
- - [ ] Android / Chrome
- - [ ] iOS / native
- - [ ] iOS / Safari
- - [ ] MacOS / Chrome / Safari
- - [ ] MacOS / Desktop
+ - [ ] Android: Native
+ - [ ] Android: mWeb Chrome
+ - [ ] iOS: Native
+ - [ ] iOS: mWeb Safari
+ - [ ] MacOS: Chrome / Safari
+ - [ ] MacOS: Desktop
- [ ] I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
- [ ] I followed proper code patterns (see [Reviewing the code](https://github.com/Expensify/App/blob/main/contributingGuides/PR_REVIEW_GUIDELINES.md#reviewing-the-code))
- [ ] I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. `toggleReport` and not `onIconClick`)
@@ -120,42 +120,42 @@ This is a checklist for PR authors. Please make sure to complete all tasks and c
### Screenshots/Videos
-Web
+Android: Native
-Mobile Web - Chrome
+Android: mWeb Chrome
-Mobile Web - Safari
+iOS: Native
-Desktop
+iOS: mWeb Safari
-iOS
+MacOS: Chrome / Safari
-Android
+MacOS: Desktop
diff --git a/assets/emojis/es.js b/assets/emojis/es.js
index c38aec7aa754..fda12f5f127c 100644
--- a/assets/emojis/es.js
+++ b/assets/emojis/es.js
@@ -6208,1044 +6208,1044 @@ const esEmojis = {
keywords: ['Bandera'],
},
'🇦🇩': {
- name: 'bandera-ad',
- keywords: ['Bandera'],
+ name: 'andorra',
+ keywords: ['bandera', 'bandera-ad'],
},
'🇦🇪': {
- name: 'bandera-ae',
- keywords: ['Bandera'],
+ name: 'emiratos_árabes_unidos',
+ keywords: ['bandera', 'bandera-ae'],
},
'🇦🇫': {
- name: 'bandera-af',
- keywords: ['Bandera'],
+ name: 'afganistán',
+ keywords: ['bandera', 'bandera-af'],
},
'🇦🇬': {
- name: 'bandera-ag',
- keywords: ['Bandera'],
+ name: 'antigua_y_barbuda',
+ keywords: ['bandera', 'bandera-ag'],
},
'🇦🇮': {
- name: 'bandera-ai',
- keywords: ['Bandera'],
+ name: 'anguila',
+ keywords: ['bandera', 'bandera-ai'],
},
'🇦🇱': {
- name: 'bandera-al',
- keywords: ['Bandera'],
+ name: 'albania',
+ keywords: ['bandera', 'bandera-al'],
},
'🇦🇲': {
- name: 'bandera-am',
- keywords: ['Bandera'],
+ name: 'armenia',
+ keywords: ['bandera', 'bandera-am'],
},
'🇦🇴': {
- name: 'bandera-ao',
- keywords: ['Bandera'],
+ name: 'angola',
+ keywords: ['bandera', 'bandera-ao'],
},
'🇦🇶': {
- name: 'bandera-aq',
- keywords: ['Bandera'],
+ name: 'antártida',
+ keywords: ['bandera', 'bandera-aq'],
},
'🇦🇷': {
- name: 'bandera-ar',
- keywords: ['Bandera'],
+ name: 'argentina',
+ keywords: ['bandera', 'bandera-ar'],
},
'🇦🇸': {
- name: 'bandera-as',
- keywords: ['Bandera'],
+ name: 'samoa_americana',
+ keywords: ['bandera', 'bandera-as'],
},
'🇦🇹': {
- name: 'bandera-at',
- keywords: ['Bandera'],
+ name: 'austria',
+ keywords: ['bandera', 'bandera-at'],
},
'🇦🇺': {
- name: 'bandera-au',
- keywords: ['Bandera'],
+ name: 'australia',
+ keywords: ['bandera', 'bandera-au'],
},
'🇦🇼': {
- name: 'bandera-aw',
- keywords: ['Bandera'],
+ name: 'aruba',
+ keywords: ['bandera', 'bandera-aw'],
},
'🇦🇽': {
- name: 'bandera-ax',
- keywords: ['Bandera'],
+ name: 'islas_de_åland',
+ keywords: ['bandera', 'bandera-ax'],
},
'🇦🇿': {
- name: 'bandera-az',
- keywords: ['Bandera'],
+ name: 'azerbaiyán',
+ keywords: ['bandera', 'bandera-az'],
},
'🇧🇦': {
- name: 'bandera-ba',
- keywords: ['Bandera'],
+ name: 'bosnia_y_herzegovina',
+ keywords: ['bandera', 'bandera-ba'],
},
'🇧🇧': {
- name: 'bandera-bb',
- keywords: ['Bandera'],
+ name: 'barbados',
+ keywords: ['bandera', 'bandera-bb'],
},
'🇧🇩': {
- name: 'bandera-bd',
- keywords: ['Bandera'],
+ name: 'bangladesh',
+ keywords: ['bandera', 'bandera-bd'],
},
'🇧🇪': {
- name: 'bandera-be',
- keywords: ['Bandera'],
+ name: 'bélgica',
+ keywords: ['bandera', 'bandera-be'],
},
'🇧🇫': {
- name: 'bandera-bf',
- keywords: ['Bandera'],
+ name: 'burkina_faso',
+ keywords: ['bandera', 'bandera-bf'],
},
'🇧🇬': {
- name: 'bandera-bg',
- keywords: ['Bandera'],
+ name: 'bulgaria',
+ keywords: ['bandera', 'bandera-bg'],
},
'🇧🇭': {
- name: 'bandera-bh',
- keywords: ['Bandera'],
+ name: 'bahrein',
+ keywords: ['bandera', 'bandera-bh'],
},
'🇧🇮': {
- name: 'bandera-bi',
- keywords: ['Bandera'],
+ name: 'burundi',
+ keywords: ['bandera', 'bandera-bi'],
},
'🇧🇯': {
- name: 'bandera-bj',
- keywords: ['Bandera'],
+ name: 'benin',
+ keywords: ['bandera', 'bandera-bj'],
},
'🇧🇱': {
- name: 'bandera-bl',
- keywords: ['Bandera'],
+ name: 'san_bartolomé',
+ keywords: ['bandera', 'bandera-bl'],
},
'🇧🇲': {
- name: 'bandera-bm',
- keywords: ['Bandera'],
+ name: 'islas_bermudas',
+ keywords: ['bandera', 'bandera-bm'],
},
'🇧🇳': {
- name: 'bandera-bn',
- keywords: ['Bandera'],
+ name: 'brunéi',
+ keywords: ['bandera', 'bandera-bn'],
},
'🇧🇴': {
- name: 'bandera-bo',
- keywords: ['Bandera'],
+ name: 'bolivia',
+ keywords: ['bandera', 'bandera-bo'],
},
'🇧🇶': {
- name: 'bandera-bq',
- keywords: ['Bandera'],
+ name: 'bonaire,_san_eustaquio_y_saba',
+ keywords: ['bandera', 'bandera-bq'],
},
'🇧🇷': {
- name: 'bandera-br',
- keywords: ['Bandera'],
+ name: 'brazil',
+ keywords: ['bandera', 'bandera-br'],
},
'🇧🇸': {
- name: 'bandera-bs',
- keywords: ['Bandera'],
+ name: 'bahamas',
+ keywords: ['bandera', 'bandera-bs'],
},
'🇧🇹': {
- name: 'bandera-bt',
- keywords: ['Bandera'],
+ name: 'bhután',
+ keywords: ['bandera', 'bandera-bt'],
},
'🇧🇻': {
name: 'bandera-bv',
- keywords: ['Bandera'],
+ keywords: ['Bandera', 'bandera-bv'],
},
'🇧🇼': {
- name: 'bandera-bw',
- keywords: ['Bandera'],
+ name: 'botsuana',
+ keywords: ['bandera', 'bandera-bw'],
},
'🇧🇾': {
- name: 'bandera-by',
- keywords: ['Bandera'],
+ name: 'bielorrusia',
+ keywords: ['bandera', 'bandera-by'],
},
'🇧🇿': {
- name: 'bandera-bz',
- keywords: ['Bandera'],
+ name: 'belice',
+ keywords: ['bandera', 'bandera-bz'],
},
'🇨🇦': {
- name: 'bandera-ca',
- keywords: ['Bandera'],
+ name: 'canadá',
+ keywords: ['bandera', 'bandera-ca'],
},
'🇨🇨': {
- name: 'bandera-cc',
- keywords: ['Bandera'],
+ name: 'islas_cocos_(keeling)',
+ keywords: ['bandera', 'bandera-cc'],
},
'🇨🇩': {
- name: 'bandera-cd',
- keywords: ['Bandera'],
+ name: 'república_democrática_del_congo',
+ keywords: ['bandera', 'bandera-cd'],
},
'🇨🇫': {
- name: 'bandera-cf',
- keywords: ['Bandera'],
+ name: 'república_centroafricana',
+ keywords: ['bandera', 'bandera-cf'],
},
'🇨🇬': {
- name: 'bandera-cg',
- keywords: ['Bandera'],
+ name: 'república_del_congo',
+ keywords: ['bandera', 'bandera-cg'],
},
'🇨🇭': {
- name: 'bandera-ch',
- keywords: ['Bandera'],
+ name: 'suiza',
+ keywords: ['bandera', 'bandera-ch'],
},
'🇨🇮': {
- name: 'bandera-ci',
- keywords: ['Bandera'],
+ name: 'costa_de_marfil',
+ keywords: ['bandera', 'bandera-ci'],
},
'🇨🇰': {
- name: 'bandera-ck',
- keywords: ['Bandera'],
+ name: 'islas_cook',
+ keywords: ['bandera', 'bandera-ck'],
},
'🇨🇱': {
- name: 'bandera-cl',
- keywords: ['Bandera'],
+ name: 'chile',
+ keywords: ['bandera', 'bandera-cl'],
},
'🇨🇲': {
- name: 'bandera-cm',
- keywords: ['Bandera'],
+ name: 'camerún',
+ keywords: ['bandera', 'bandera-cm'],
},
'🇨🇳': {
- name: 'cn',
- keywords: ['Bandera'],
+ name: 'china',
+ keywords: ['bandera', 'bandera-cn'],
},
'🇨🇴': {
- name: 'bandera-co',
- keywords: ['Bandera'],
+ name: 'colombia',
+ keywords: ['bandera', 'bandera-co'],
},
'🇨🇵': {
name: 'bandera-cp',
- keywords: ['Bandera'],
+ keywords: ['Bandera', 'bandera-cp'],
},
'🇨🇷': {
- name: 'bandera-cr',
- keywords: ['Bandera'],
+ name: 'costa_rica',
+ keywords: ['bandera', 'bandera-cr'],
},
'🇨🇺': {
- name: 'bandera-cu',
- keywords: ['Bandera'],
+ name: 'cuba',
+ keywords: ['bandera', 'bandera-cu'],
},
'🇨🇻': {
- name: 'bandera-cv',
- keywords: ['Bandera'],
+ name: 'cabo_verde',
+ keywords: ['bandera', 'bandera-cv'],
},
'🇨🇼': {
- name: 'bandera-cw',
- keywords: ['Bandera'],
+ name: 'curazao',
+ keywords: ['bandera', 'bandera-cw'],
},
'🇨🇽': {
- name: 'bandera-cx',
- keywords: ['Bandera'],
+ name: 'isla_de_navidad',
+ keywords: ['bandera', 'bandera-cx'],
},
'🇨🇾': {
- name: 'bandera-cy',
- keywords: ['Bandera'],
+ name: 'chipre',
+ keywords: ['bandera', 'bandera-cy'],
},
'🇨🇿': {
- name: 'bandera-cz',
- keywords: ['Bandera'],
+ name: 'república_checa',
+ keywords: ['bandera', 'bandera-cz'],
},
'🇩🇪': {
- name: 'de',
- keywords: ['Bandera'],
+ name: 'alemania',
+ keywords: ['bandera', 'bandera-de'],
},
'🇩🇬': {
name: 'bandera-dg',
- keywords: ['Bandera'],
+ keywords: ['Bandera', 'bandera-dg'],
},
'🇩🇯': {
- name: 'bandera-dj',
- keywords: ['Bandera'],
+ name: 'yibuti',
+ keywords: ['bandera', 'bandera-dj'],
},
'🇩🇰': {
- name: 'bandera-dk',
- keywords: ['Bandera'],
+ name: 'dinamarca',
+ keywords: ['bandera', 'bandera-dk'],
},
'🇩🇲': {
- name: 'bandera-dm',
- keywords: ['Bandera'],
+ name: 'dominica',
+ keywords: ['bandera', 'bandera-dm'],
},
'🇩🇴': {
- name: 'bandera-do',
- keywords: ['Bandera'],
+ name: 'república_dominicana',
+ keywords: ['bandera', 'bandera-do'],
},
'🇩🇿': {
- name: 'bandera-dz',
- keywords: ['Bandera'],
+ name: 'argelia',
+ keywords: ['bandera', 'bandera-dz'],
},
'🇪🇦': {
name: 'bandera-ea',
- keywords: ['Bandera'],
+ keywords: ['Bandera', 'bandera-ea'],
},
'🇪🇨': {
- name: 'bandera-ec',
- keywords: ['Bandera'],
+ name: 'ecuador',
+ keywords: ['bandera', 'bandera-ec'],
},
'🇪🇪': {
- name: 'bandera-ee',
- keywords: ['Bandera'],
+ name: 'estonia',
+ keywords: ['bandera', 'bandera-ee'],
},
'🇪🇬': {
- name: 'bandera-eg',
- keywords: ['Bandera'],
+ name: 'egipto',
+ keywords: ['bandera', 'bandera-eg'],
},
'🇪🇭': {
- name: 'bandera-eh',
- keywords: ['Bandera'],
+ name: 'sahara_occidental',
+ keywords: ['bandera', 'bandera-eh'],
},
'🇪🇷': {
- name: 'bandera-er',
- keywords: ['Bandera'],
+ name: 'eritrea',
+ keywords: ['bandera', 'bandera-er'],
},
'🇪🇸': {
- name: 'es',
- keywords: ['Bandera'],
+ name: 'españa',
+ keywords: ['bandera', 'bandera-es'],
},
'🇪🇹': {
- name: 'bandera-et',
- keywords: ['Bandera'],
+ name: 'etiopía',
+ keywords: ['bandera', 'bandera-et'],
},
'🇪🇺': {
name: 'bandera-eu',
- keywords: ['Bandera'],
+ keywords: ['Bandera', 'bandera-eu'],
},
'🇫🇮': {
- name: 'bandera-fi',
- keywords: ['Bandera'],
+ name: 'finlandia',
+ keywords: ['bandera', 'bandera-fi'],
},
'🇫🇯': {
- name: 'bandera-fj',
- keywords: ['Bandera'],
+ name: 'fiyi',
+ keywords: ['bandera', 'bandera-fj'],
},
'🇫🇰': {
- name: 'bandera-fk',
- keywords: ['Bandera'],
+ name: 'islas_malvinas',
+ keywords: ['bandera', 'bandera-fk'],
},
'🇫🇲': {
- name: 'bandera-fm',
- keywords: ['Bandera'],
+ name: 'micronesia',
+ keywords: ['bandera', 'bandera-fm'],
},
'🇫🇴': {
- name: 'bandera-fo',
- keywords: ['Bandera'],
+ name: 'islas_feroe',
+ keywords: ['bandera', 'bandera-fo'],
},
'🇫🇷': {
- name: 'fr',
- keywords: ['Bandera'],
+ name: 'francia',
+ keywords: ['bandera', 'bandera-fr'],
},
'🇬🇦': {
- name: 'bandera-ga',
- keywords: ['Bandera'],
+ name: 'gabón',
+ keywords: ['bandera', 'bandera-ga'],
},
'🇬🇧': {
- name: 'gb',
- keywords: ['Bandera'],
+ name: 'reino_unido',
+ keywords: ['bandera', 'bandera-gb'],
},
'🇬🇩': {
- name: 'bandera-gd',
- keywords: ['Bandera'],
+ name: 'granada',
+ keywords: ['bandera', 'bandera-gd'],
},
'🇬🇪': {
- name: 'bandera-ge',
- keywords: ['Bandera'],
+ name: 'georgia',
+ keywords: ['bandera', 'bandera-ge'],
},
'🇬🇫': {
- name: 'bandera-gf',
- keywords: ['Bandera'],
+ name: 'guayana_francesa',
+ keywords: ['bandera', 'bandera-gf'],
},
'🇬🇬': {
- name: 'bandera-gg',
- keywords: ['Bandera'],
+ name: 'guernsey',
+ keywords: ['bandera', 'bandera-gg'],
},
'🇬🇭': {
- name: 'bandera-gh',
- keywords: ['Bandera'],
+ name: 'ghana',
+ keywords: ['bandera', 'bandera-gh'],
},
'🇬🇮': {
- name: 'bandera-gi',
- keywords: ['Bandera'],
+ name: 'gibraltar',
+ keywords: ['bandera', 'bandera-gi'],
},
'🇬🇱': {
- name: 'bandera-gl',
- keywords: ['Bandera'],
+ name: 'groenlandia',
+ keywords: ['bandera', 'bandera-gl'],
},
'🇬🇲': {
- name: 'bandera-gm',
- keywords: ['Bandera'],
+ name: 'gambia',
+ keywords: ['bandera', 'bandera-gm'],
},
'🇬🇳': {
- name: 'bandera-gn',
- keywords: ['Bandera'],
+ name: 'guinea',
+ keywords: ['bandera', 'bandera-gn'],
},
'🇬🇵': {
- name: 'bandera-gp',
- keywords: ['Bandera'],
+ name: 'guadeloupe',
+ keywords: ['bandera', 'bandera-gp'],
},
'🇬🇶': {
- name: 'bandera-gq',
- keywords: ['Bandera'],
+ name: 'guinea_ecuatorial',
+ keywords: ['bandera', 'bandera-gq'],
},
'🇬🇷': {
- name: 'bandera-gr',
- keywords: ['Bandera'],
+ name: 'greece',
+ keywords: ['bandera', 'bandera-gr'],
},
'🇬🇸': {
- name: 'bandera-gs',
- keywords: ['Bandera'],
+ name: 'islas_georgias_del_sur_y_sandwich_del_sur',
+ keywords: ['bandera', 'bandera-gs'],
},
'🇬🇹': {
- name: 'bandera-gt',
- keywords: ['Bandera'],
+ name: 'guatemala',
+ keywords: ['bandera', 'bandera-gt'],
},
'🇬🇺': {
- name: 'bandera-gu',
- keywords: ['Bandera'],
+ name: 'guam',
+ keywords: ['bandera', 'bandera-gu'],
},
'🇬🇼': {
- name: 'bandera-gw',
- keywords: ['Bandera'],
+ name: 'guinea-bissau',
+ keywords: ['bandera', 'bandera-gw'],
},
'🇬🇾': {
- name: 'bandera-gy',
- keywords: ['Bandera'],
+ name: 'guyana',
+ keywords: ['bandera', 'bandera-gy'],
},
'🇭🇰': {
- name: 'bandera-hk',
- keywords: ['Bandera'],
+ name: 'hong_kong',
+ keywords: ['bandera', 'bandera-hk'],
},
'🇭🇲': {
name: 'bandera-hm',
- keywords: ['Bandera'],
+ keywords: ['Bandera', 'bandera-hm'],
},
'🇭🇳': {
- name: 'bandera-hn',
- keywords: ['Bandera'],
+ name: 'honduras',
+ keywords: ['bandera', 'bandera-hn'],
},
'🇭🇷': {
- name: 'bandera-hr',
- keywords: ['Bandera'],
+ name: 'croacia',
+ keywords: ['bandera', 'bandera-hr'],
},
'🇭🇹': {
- name: 'bandera-ht',
- keywords: ['Bandera'],
+ name: 'haiti',
+ keywords: ['bandera', 'bandera-ht'],
},
'🇭🇺': {
- name: 'bandera-hu',
- keywords: ['Bandera'],
+ name: 'hungría',
+ keywords: ['bandera', 'bandera-hu'],
},
'🇮🇨': {
name: 'bandera-ic',
- keywords: ['Bandera'],
+ keywords: ['Bandera', 'bandera-ic'],
},
'🇮🇩': {
- name: 'bandera-id',
- keywords: ['Bandera'],
+ name: 'indonesia',
+ keywords: ['bandera', 'bandera-id'],
},
'🇮🇪': {
- name: 'bandera-ie',
- keywords: ['Bandera'],
+ name: 'irlanda',
+ keywords: ['bandera', 'bandera-ie'],
},
'🇮🇱': {
- name: 'bandera-il',
- keywords: ['Bandera'],
+ name: 'israel',
+ keywords: ['bandera', 'bandera-il'],
},
'🇮🇲': {
- name: 'bandera-im',
- keywords: ['Bandera'],
+ name: 'isla_de_man',
+ keywords: ['bandera', 'bandera-im'],
},
'🇮🇳': {
- name: 'bandera-in',
- keywords: ['Bandera'],
+ name: 'india',
+ keywords: ['bandera', 'bandera-in'],
},
'🇮🇴': {
- name: 'bandera-io',
- keywords: ['Bandera'],
+ name: 'territorio_británico_del_océano_índico',
+ keywords: ['bandera', 'bandera-io'],
},
'🇮🇶': {
- name: 'bandera-iq',
- keywords: ['Bandera'],
+ name: 'irak',
+ keywords: ['bandera', 'bandera-iq'],
},
'🇮🇷': {
- name: 'bandera-ir',
- keywords: ['Bandera'],
+ name: 'irán',
+ keywords: ['bandera', 'bandera-ir'],
},
'🇮🇸': {
- name: 'bandera-is',
- keywords: ['Bandera'],
+ name: 'islandia',
+ keywords: ['bandera', 'bandera-is'],
},
'🇮🇹': {
- name: 'it',
- keywords: ['Bandera'],
+ name: 'italia',
+ keywords: ['bandera', 'bandera-it'],
},
'🇯🇪': {
- name: 'bandera-je',
- keywords: ['Bandera'],
+ name: 'jersey',
+ keywords: ['bandera', 'bandera-je'],
},
'🇯🇲': {
- name: 'bandera-jm',
- keywords: ['Bandera'],
+ name: 'jamaica',
+ keywords: ['bandera', 'bandera-jm'],
},
'🇯🇴': {
- name: 'bandera-jo',
- keywords: ['Bandera'],
+ name: 'jordania',
+ keywords: ['bandera', 'bandera-jo'],
},
'🇯🇵': {
- name: 'jp',
- keywords: ['Bandera'],
+ name: 'japón',
+ keywords: ['bandera', 'bandera-jp'],
},
'🇰🇪': {
- name: 'bandera-ke',
- keywords: ['Bandera'],
+ name: 'kenia',
+ keywords: ['bandera', 'bandera-ke'],
},
'🇰🇬': {
- name: 'bandera-kg',
- keywords: ['Bandera'],
+ name: 'kirguistán',
+ keywords: ['bandera', 'bandera-kg'],
},
'🇰🇭': {
- name: 'bandera-kh',
- keywords: ['Bandera'],
+ name: 'camboya',
+ keywords: ['bandera', 'bandera-kh'],
},
'🇰🇮': {
name: 'bandera-kl',
- keywords: ['Bandera'],
+ keywords: ['Bandera', 'bandera-kl'],
},
'🇰🇲': {
- name: 'bandera-km',
- keywords: ['Bandera'],
+ name: 'comoras',
+ keywords: ['bandera', 'bandera-km'],
},
'🇰🇳': {
- name: 'bandera-kn',
- keywords: ['Bandera'],
+ name: 'san_cristóbal_y_nieves',
+ keywords: ['bandera', 'bandera-kn'],
},
'🇰🇵': {
- name: 'bandera-kp',
- keywords: ['Bandera'],
+ name: 'corea_del_norte',
+ keywords: ['bandera', 'bandera-kp'],
},
'🇰🇷': {
- name: 'kr',
- keywords: ['Bandera'],
+ name: 'corea_del_sur',
+ keywords: ['bandera', 'bandera-kr'],
},
'🇰🇼': {
- name: 'bandera-kw',
- keywords: ['Bandera'],
+ name: 'kuwait',
+ keywords: ['bandera', 'bandera-kw'],
},
'🇰🇾': {
- name: 'bandera-ky',
- keywords: ['Bandera'],
+ name: 'islas_caimán',
+ keywords: ['bandera', 'bandera-ky'],
},
'🇰🇿': {
- name: 'bandera-kz',
- keywords: ['Bandera'],
+ name: 'kazajistán',
+ keywords: ['bandera', 'bandera-kz'],
},
'🇱🇦': {
- name: 'bandera-la',
- keywords: ['Bandera'],
+ name: 'laos',
+ keywords: ['bandera', 'bandera-la'],
},
'🇱🇧': {
- name: 'bandera-lb',
- keywords: ['Bandera'],
+ name: 'líbano',
+ keywords: ['bandera', 'bandera-lb'],
},
'🇱🇨': {
- name: 'bandera-lc',
- keywords: ['Bandera'],
+ name: 'santa_lucía',
+ keywords: ['bandera', 'bandera-lc'],
},
'🇱🇮': {
- name: 'bandera-li',
- keywords: ['Bandera'],
+ name: 'liechtenstein',
+ keywords: ['bandera', 'bandera-li'],
},
'🇱🇰': {
- name: 'bandera-lk',
- keywords: ['Bandera'],
+ name: 'sri_lanka',
+ keywords: ['bandera', 'bandera-lk'],
},
'🇱🇷': {
- name: 'bandera-lr',
- keywords: ['Bandera'],
+ name: 'liberia',
+ keywords: ['bandera', 'bandera-lr'],
},
'🇱🇸': {
- name: 'bandera-ls',
- keywords: ['Bandera'],
+ name: 'lesoto',
+ keywords: ['bandera', 'bandera-ls'],
},
'🇱🇹': {
- name: 'bandera-lt',
- keywords: ['Bandera'],
+ name: 'lituania',
+ keywords: ['bandera', 'bandera-lt'],
},
'🇱🇺': {
- name: 'bandera-lu',
- keywords: ['Bandera'],
+ name: 'luxemburgo',
+ keywords: ['bandera', 'bandera-lu'],
},
'🇱🇻': {
- name: 'bandera-lv',
- keywords: ['Bandera'],
+ name: 'letonia',
+ keywords: ['bandera', 'bandera-lv'],
},
'🇱🇾': {
- name: 'bandera-ly',
- keywords: ['Bandera'],
+ name: 'libia',
+ keywords: ['bandera', 'bandera-ly'],
},
'🇲🇦': {
- name: 'bandera-ma',
- keywords: ['Bandera'],
+ name: 'marruecos',
+ keywords: ['bandera', 'bandera-ma'],
},
'🇲🇨': {
- name: 'bandera-mc',
- keywords: ['Bandera'],
+ name: 'mónaco',
+ keywords: ['bandera', 'bandera-mc'],
},
'🇲🇩': {
- name: 'bandera-md',
- keywords: ['Bandera'],
+ name: 'moldavia',
+ keywords: ['bandera', 'bandera-md'],
},
'🇲🇪': {
- name: 'bandera-me',
- keywords: ['Bandera'],
+ name: 'montenegro',
+ keywords: ['bandera', 'bandera-me'],
},
'🇲🇫': {
- name: 'bandera-mf',
- keywords: ['Bandera'],
+ name: 'san_martín_(francia)',
+ keywords: ['bandera', 'bandera-mf'],
},
'🇲🇬': {
- name: 'bandera-mg',
- keywords: ['Bandera'],
+ name: 'madagascar',
+ keywords: ['bandera', 'bandera-mg'],
},
'🇲🇭': {
- name: 'bandera-mh',
- keywords: ['Bandera'],
+ name: 'islas_marshall',
+ keywords: ['bandera', 'bandera-mh'],
},
'🇲🇰': {
- name: 'bandera-mk',
- keywords: ['Bandera'],
+ name: 'macedônia',
+ keywords: ['bandera', 'bandera-mk'],
},
'🇲🇱': {
- name: 'bandera-ml',
- keywords: ['Bandera'],
+ name: 'mali',
+ keywords: ['bandera', 'bandera-ml'],
},
'🇲🇲': {
- name: 'bandera-mm',
- keywords: ['Bandera'],
+ name: 'birmania',
+ keywords: ['bandera', 'bandera-mm'],
},
'🇲🇳': {
- name: 'bandera-mn',
- keywords: ['Bandera'],
+ name: 'mongolia',
+ keywords: ['bandera', 'bandera-mn'],
},
'🇲🇴': {
- name: 'bandera-mo',
- keywords: ['Bandera'],
+ name: 'macao',
+ keywords: ['bandera', 'bandera-mo'],
},
'🇲🇵': {
- name: 'bandera-mp',
- keywords: ['Bandera'],
+ name: 'islas_marianas_del_norte',
+ keywords: ['bandera', 'bandera-mp'],
},
'🇲🇶': {
- name: 'bandera-mq',
- keywords: ['Bandera'],
+ name: 'martinica',
+ keywords: ['bandera', 'bandera-mq'],
},
'🇲🇷': {
- name: 'bandera-mr',
- keywords: ['Bandera'],
+ name: 'mauritania',
+ keywords: ['bandera', 'bandera-mr'],
},
'🇲🇸': {
- name: 'bandera-ms',
- keywords: ['Bandera'],
+ name: 'montserrat',
+ keywords: ['bandera', 'bandera-ms'],
},
'🇲🇹': {
- name: 'bandera-mt',
- keywords: ['Bandera'],
+ name: 'malta',
+ keywords: ['bandera', 'bandera-mt'],
},
'🇲🇺': {
- name: 'bandera-mu',
- keywords: ['Bandera'],
+ name: 'mauritius',
+ keywords: ['bandera', 'bandera-mu'],
},
'🇲🇻': {
- name: 'bandera-mv',
- keywords: ['Bandera'],
+ name: 'islas_maldivas',
+ keywords: ['bandera', 'bandera-mv'],
},
'🇲🇼': {
- name: 'bandera-mw',
- keywords: ['Bandera'],
+ name: 'malawi',
+ keywords: ['bandera', 'bandera-mw'],
},
'🇲🇽': {
- name: 'bandera-mx',
- keywords: ['Bandera'],
+ name: 'méxico',
+ keywords: ['bandera', 'bandera-mx'],
},
'🇲🇾': {
- name: 'bandera-my',
- keywords: ['Bandera'],
+ name: 'malasia',
+ keywords: ['bandera', 'bandera-my'],
},
'🇲🇿': {
- name: 'bandera-mz',
- keywords: ['Bandera'],
+ name: 'mozambique',
+ keywords: ['bandera', 'bandera-mz'],
},
'🇳🇦': {
- name: 'bandera-na',
- keywords: ['Bandera'],
+ name: 'namibia',
+ keywords: ['bandera', 'bandera-na'],
},
'🇳🇨': {
- name: 'bandera-nc',
- keywords: ['Bandera'],
+ name: 'nueva_caledonia',
+ keywords: ['bandera', 'bandera-nc'],
},
'🇳🇪': {
- name: 'bandera-ne',
- keywords: ['Bandera'],
+ name: 'niger',
+ keywords: ['bandera', 'bandera-ne'],
},
'🇳🇫': {
- name: 'bandera-nf',
- keywords: ['Bandera'],
+ name: 'isla_norfolk',
+ keywords: ['bandera', 'bandera-nf'],
},
'🇳🇬': {
- name: 'bandera-ng',
- keywords: ['Bandera'],
+ name: 'nigeria',
+ keywords: ['bandera', 'bandera-ng'],
},
'🇳🇮': {
- name: 'bandera-ni',
- keywords: ['Bandera'],
+ name: 'nicaragua',
+ keywords: ['bandera', 'bandera-ni'],
},
'🇳🇱': {
- name: 'bandera-nl',
- keywords: ['Bandera'],
+ name: 'países_bajos',
+ keywords: ['bandera', 'bandera-nl'],
},
'🇳🇴': {
- name: 'bandera-no',
- keywords: ['Bandera'],
+ name: 'noruega',
+ keywords: ['bandera', 'bandera-no'],
},
'🇳🇵': {
- name: 'bandera-np',
- keywords: ['Bandera'],
+ name: 'nepal',
+ keywords: ['bandera', 'bandera-np'],
},
'🇳🇷': {
- name: 'bandera-nr',
- keywords: ['Bandera'],
+ name: 'nauru',
+ keywords: ['bandera', 'bandera-nr'],
},
'🇳🇺': {
- name: 'bandera-nu',
- keywords: ['Bandera'],
+ name: 'niue',
+ keywords: ['bandera', 'bandera-nu'],
},
'🇳🇿': {
- name: 'bandera-nz',
- keywords: ['Bandera'],
+ name: 'nueva_zealand',
+ keywords: ['bandera', 'bandera-nz'],
},
'🇴🇲': {
- name: 'bandera-om',
- keywords: ['Bandera'],
+ name: 'omán',
+ keywords: ['bandera', 'bandera-om'],
},
'🇵🇦': {
- name: 'bandera-pa',
- keywords: ['Bandera'],
+ name: 'panamá',
+ keywords: ['bandera', 'bandera-pa'],
},
'🇵🇪': {
- name: 'bandera-pe',
- keywords: ['Bandera'],
+ name: 'perú',
+ keywords: ['bandera', 'bandera-pe'],
},
'🇵🇫': {
- name: 'bandera-pf',
- keywords: ['Bandera'],
+ name: 'polinesia_francesa',
+ keywords: ['bandera', 'bandera-pf'],
},
'🇵🇬': {
- name: 'bandera-pg',
- keywords: ['Bandera'],
+ name: 'papúa_nueva_guinea',
+ keywords: ['bandera', 'bandera-pg'],
},
'🇵🇭': {
- name: 'bandera-ph',
- keywords: ['Bandera'],
+ name: 'filipinas',
+ keywords: ['bandera', 'bandera-ph'],
},
'🇵🇰': {
- name: 'bandera-pk',
- keywords: ['Bandera'],
+ name: 'pakistán',
+ keywords: ['bandera', 'bandera-pk'],
},
'🇵🇱': {
- name: 'bandera-pl',
- keywords: ['Bandera'],
+ name: 'polonia',
+ keywords: ['bandera', 'bandera-pl'],
},
'🇵🇲': {
- name: 'bandera-pm',
- keywords: ['Bandera'],
+ name: 'san_pedro_y_miquelón',
+ keywords: ['bandera', 'bandera-pm'],
},
'🇵🇳': {
- name: 'bandera-pn',
- keywords: ['Bandera'],
+ name: 'islas_pitcairn',
+ keywords: ['bandera', 'bandera-pn'],
},
'🇵🇷': {
- name: 'bandera-pr',
- keywords: ['Bandera'],
+ name: 'puerto_rico',
+ keywords: ['bandera', 'bandera-pr'],
},
'🇵🇸': {
- name: 'bandera-ps',
- keywords: ['Bandera'],
+ name: 'palestina',
+ keywords: ['bandera', 'bandera-ps'],
},
'🇵🇹': {
- name: 'bandera-pt',
- keywords: ['Bandera'],
+ name: 'portugal',
+ keywords: ['bandera', 'bandera-pt'],
},
'🇵🇼': {
- name: 'bandera-pw',
- keywords: ['Bandera'],
+ name: 'palau',
+ keywords: ['bandera', 'bandera-pw'],
},
'🇵🇾': {
- name: 'bandera-py',
- keywords: ['Bandera'],
+ name: 'paraguay',
+ keywords: ['bandera', 'bandera-py'],
},
'🇶🇦': {
- name: 'bandera-qa',
- keywords: ['Bandera'],
+ name: 'qatar',
+ keywords: ['bandera', 'bandera-qa'],
},
'🇷🇪': {
- name: 'bandera-re',
- keywords: ['Bandera'],
+ name: 'reunión',
+ keywords: ['bandera', 'bandera-re'],
},
'🇷🇴': {
- name: 'bandera-ro',
- keywords: ['Bandera'],
+ name: 'rumanía',
+ keywords: ['bandera', 'bandera-ro'],
},
'🇷🇸': {
- name: 'bandera-rs',
- keywords: ['Bandera'],
+ name: 'serbia',
+ keywords: ['bandera', 'bandera-rs'],
},
'🇷🇺': {
- name: 'ru',
- keywords: ['Bandera'],
+ name: 'rusia',
+ keywords: ['bandera', 'bandera-ru'],
},
'🇷🇼': {
- name: 'bandera-rw',
- keywords: ['Bandera'],
+ name: 'ruanda',
+ keywords: ['bandera', 'bandera-rw'],
},
'🇸🇦': {
- name: 'bandera-sa',
- keywords: ['Bandera'],
+ name: 'arabia_saudita',
+ keywords: ['bandera', 'bandera-sa'],
},
'🇸🇧': {
- name: 'bandera-sb',
- keywords: ['Bandera'],
+ name: 'islas_salomón',
+ keywords: ['bandera', 'bandera-sb'],
},
'🇸🇨': {
- name: 'bandera-sc',
- keywords: ['Bandera'],
+ name: 'seychelles',
+ keywords: ['bandera', 'bandera-sc'],
},
'🇸🇩': {
- name: 'bandera-sd',
- keywords: ['Bandera'],
+ name: 'sudán',
+ keywords: ['bandera', 'bandera-sd'],
},
'🇸🇪': {
- name: 'bandera-se',
- keywords: ['Bandera'],
+ name: 'suecia',
+ keywords: ['bandera', 'bandera-se'],
},
'🇸🇬': {
- name: 'bandera-sg',
- keywords: ['Bandera'],
+ name: 'singapur',
+ keywords: ['bandera', 'bandera-sg'],
},
'🇸🇭': {
- name: 'bandera-sh',
- keywords: ['Bandera'],
+ name: 'santa_elena',
+ keywords: ['bandera', 'bandera-sh'],
},
'🇸🇮': {
- name: 'bandera-si',
- keywords: ['Bandera'],
+ name: 'eslovenia',
+ keywords: ['bandera', 'bandera-si'],
},
'🇸🇯': {
- name: 'bandera-sj',
- keywords: ['Bandera'],
+ name: 'svalbard_y_jan_mayen',
+ keywords: ['bandera', 'bandera-sj'],
},
'🇸🇰': {
- name: 'bandera-sk',
- keywords: ['Bandera'],
+ name: 'eslovaquia',
+ keywords: ['bandera', 'bandera-sk'],
},
'🇸🇱': {
- name: 'bandera-sl',
- keywords: ['Bandera'],
+ name: 'sierra_leona',
+ keywords: ['bandera', 'bandera-sl'],
},
'🇸🇲': {
- name: 'bandera-sm',
- keywords: ['Bandera'],
+ name: 'san_marino',
+ keywords: ['bandera', 'bandera-sm'],
},
'🇸🇳': {
- name: 'bandera-sn',
- keywords: ['Bandera'],
+ name: 'senegal',
+ keywords: ['bandera', 'bandera-sn'],
},
'🇸🇴': {
- name: 'bandera-so',
- keywords: ['Bandera'],
+ name: 'somalia',
+ keywords: ['bandera', 'bandera-so'],
},
'🇸🇷': {
- name: 'bandera-sr',
- keywords: ['Bandera'],
+ name: 'surinám',
+ keywords: ['bandera', 'bandera-sr'],
},
'🇸🇸': {
- name: 'bandera-ss',
- keywords: ['Bandera'],
+ name: 'república_de_sudán_del_sur',
+ keywords: ['bandera', 'bandera-ss'],
},
'🇸🇹': {
- name: 'bandera-st',
- keywords: ['Bandera'],
+ name: 'santo_tomé_y_príncipe',
+ keywords: ['bandera', 'bandera-st'],
},
'🇸🇻': {
- name: 'bandera-sv',
- keywords: ['Bandera'],
+ name: 'el_salvador',
+ keywords: ['bandera', 'bandera-sv'],
},
'🇸🇽': {
- name: 'bandera-sx',
- keywords: ['Bandera'],
+ name: 'sint_maarten',
+ keywords: ['bandera', 'bandera-sx'],
},
'🇸🇾': {
- name: 'bandera-sy',
- keywords: ['Bandera'],
+ name: 'siria',
+ keywords: ['bandera', 'bandera-sy'],
},
'🇸🇿': {
- name: 'bandera-sz',
- keywords: ['Bandera'],
+ name: 'swazilandia',
+ keywords: ['bandera', 'bandera-sz'],
},
'🇹🇦': {
- name: 'bandera-ta',
- keywords: ['Bandera'],
+ name: 'tristán_de_acuña',
+ keywords: ['bandera', 'bandera-ta'],
},
'🇹🇨': {
- name: 'bandera-tc',
- keywords: ['Bandera'],
+ name: 'islas_turcas_y_caicos',
+ keywords: ['bandera', 'bandera-tc'],
},
'🇹🇩': {
- name: 'bandera-td',
- keywords: ['Bandera'],
+ name: 'chad',
+ keywords: ['bandera', 'bandera-td'],
},
'🇹🇫': {
- name: 'bandera-tf',
- keywords: ['Bandera'],
+ name: 'territorios_australes_y_antárticas_franceses',
+ keywords: ['bandera', 'bandera-tf'],
},
'🇹🇬': {
- name: 'bandera-tg',
- keywords: ['Bandera'],
+ name: 'togo',
+ keywords: ['bandera', 'bandera-tg'],
},
'🇹🇭': {
- name: 'bandera-th',
- keywords: ['Bandera'],
+ name: 'tailandia',
+ keywords: ['bandera', 'bandera-th'],
},
'🇹🇯': {
- name: 'bandera-tj',
- keywords: ['Bandera'],
+ name: 'tayikistán',
+ keywords: ['bandera', 'bandera-tj'],
},
'🇹🇰': {
- name: 'bandera-tk',
- keywords: ['Bandera'],
+ name: 'tokelau',
+ keywords: ['bandera', 'bandera-tk'],
},
'🇹🇱': {
- name: 'bandera-tl',
- keywords: ['Bandera'],
+ name: 'timor_oriental',
+ keywords: ['bandera', 'bandera-tl'],
},
'🇹🇲': {
- name: 'bandera-tm',
- keywords: ['Bandera'],
+ name: 'turkmenistán',
+ keywords: ['bandera', 'bandera-tm'],
},
'🇹🇳': {
- name: 'bandera-tn',
- keywords: ['Bandera'],
+ name: 'tunez',
+ keywords: ['bandera', 'bandera-tn'],
},
'🇹🇴': {
- name: 'bandera-to',
- keywords: ['Bandera'],
+ name: 'tonga',
+ keywords: ['bandera', 'bandera-to'],
},
'🇹🇷': {
- name: 'bandera-tr',
- keywords: ['Bandera'],
+ name: 'turquía',
+ keywords: ['bandera', 'bandera-tr'],
},
'🇹🇹': {
- name: 'bandera-tt',
- keywords: ['Bandera'],
+ name: 'trinidad_y_tobago',
+ keywords: ['bandera', 'bandera-tt'],
},
'🇹🇻': {
- name: 'bandera-tv',
- keywords: ['Bandera'],
+ name: 'tuvalu',
+ keywords: ['bandera', 'bandera-tv'],
},
'🇹🇼': {
- name: 'bandera-tw',
- keywords: ['Bandera'],
+ name: 'taiwán',
+ keywords: ['bandera', 'bandera-tw'],
},
'🇹🇿': {
- name: 'bandera-tz',
- keywords: ['Bandera'],
+ name: 'tanzania',
+ keywords: ['bandera', 'bandera-tz'],
},
'🇺🇦': {
- name: 'bandera-ua',
- keywords: ['Bandera'],
+ name: 'ucrania',
+ keywords: ['bandera', 'bandera-ua'],
},
'🇺🇬': {
- name: 'bandera-ug',
- keywords: ['Bandera'],
+ name: 'uganda',
+ keywords: ['bandera', 'bandera-ug'],
},
'🇺🇲': {
- name: 'bandera-um',
- keywords: ['Bandera'],
+ name: 'islas_ultramarinas_menores_de_estados_unidos',
+ keywords: ['bandera', 'bandera-um'],
},
'🇺🇳': {
name: 'bandera-onu',
- keywords: ['Bandera'],
+ keywords: ['Bandera', 'bandera-onu'],
},
'🇺🇸': {
- name: 'us',
- keywords: ['Bandera'],
+ name: 'estados_unidos_de_américa',
+ keywords: ['bandera', 'bandera-us'],
},
'🇺🇾': {
- name: 'bandera-uy',
- keywords: ['Bandera'],
+ name: 'uruguay',
+ keywords: ['bandera', 'bandera-uy'],
},
'🇺🇿': {
- name: 'bandera-uz',
- keywords: ['Bandera'],
+ name: 'uzbekistan',
+ keywords: ['bandera', 'bandera-uz'],
},
'🇻🇦': {
- name: 'bandera-va',
- keywords: ['Bandera'],
+ name: 'ciudad_del_vaticano',
+ keywords: ['bandera', 'bandera-va'],
},
'🇻🇨': {
- name: 'bandera-vc',
- keywords: ['Bandera'],
+ name: 'san_vicente_y_las_granadinas',
+ keywords: ['bandera', 'bandera-vc'],
},
'🇻🇪': {
- name: 'bandera-ve',
- keywords: ['Bandera'],
+ name: 'venezuela',
+ keywords: ['bandera', 'bandera-ve'],
},
'🇻🇬': {
- name: 'bandera-vg',
- keywords: ['Bandera'],
+ name: 'islas_vírgenes_británicas',
+ keywords: ['bandera', 'bandera-vg'],
},
'🇻🇮': {
- name: 'bandera-vi',
- keywords: ['Bandera'],
+ name: 'islas_vírgenes_de_los_estados_unidos',
+ keywords: ['bandera', 'bandera-vi'],
},
'🇻🇳': {
- name: 'bandera-vn',
- keywords: ['Bandera'],
+ name: 'vietnam',
+ keywords: ['bandera', 'bandera-vn'],
},
'🇻🇺': {
- name: 'bandera-vu',
- keywords: ['Bandera'],
+ name: 'vanuatu',
+ keywords: ['bandera', 'bandera-vu'],
},
'🇼🇫': {
- name: 'bandera-wf',
- keywords: ['Bandera'],
+ name: 'wallis_y_futuna',
+ keywords: ['bandera', 'bandera-wf'],
},
'🇼🇸': {
- name: 'bandera-ws',
- keywords: ['Bandera'],
+ name: 'samoa',
+ keywords: ['bandera', 'bandera-ws'],
},
'🇽🇰': {
- name: 'bandera-xk',
- keywords: ['Bandera'],
+ name: 'kosovo',
+ keywords: ['bandera', 'bandera-xk'],
},
'🇾🇪': {
- name: 'bandera-ye',
- keywords: ['Bandera'],
+ name: 'yemen',
+ keywords: ['bandera', 'bandera-ye'],
},
'🇾🇹': {
- name: 'bandera-yt',
- keywords: ['Bandera'],
+ name: 'mayotte',
+ keywords: ['bandera', 'bandera-yt'],
},
'🇿🇦': {
- name: 'bandera-za',
- keywords: ['Bandera'],
+ name: 'sudáfrica',
+ keywords: ['bandera', 'bandera-za'],
},
'🇿🇲': {
- name: 'bandera-zm',
- keywords: ['Bandera'],
+ name: 'zambia',
+ keywords: ['bandera', 'bandera-zm'],
},
'🇿🇼': {
- name: 'bandera-zw',
- keywords: ['Bandera'],
+ name: 'zimbabue',
+ keywords: ['bandera', 'bandera-zw'],
},
'🏴': {
- name: 'bandera-inglaterra',
- keywords: ['Bandera'],
+ name: 'inglaterra',
+ keywords: ['bandera', 'bandera-inglaterra'],
},
'🏴': {
- name: 'bandera-escocia',
- keywords: ['Bandera'],
+ name: 'escocia',
+ keywords: ['bandera', 'bandera-escocia'],
},
'🏴': {
- name: 'bandera-gales',
- keywords: ['Bandera'],
+ name: 'gales',
+ keywords: ['bandera', 'bandera-gales'],
},
};
diff --git a/contributingGuides/REVIEWER_CHECKLIST.md b/contributingGuides/REVIEWER_CHECKLIST.md
index 6a5dffc8b073..d52d80a818bb 100644
--- a/contributingGuides/REVIEWER_CHECKLIST.md
+++ b/contributingGuides/REVIEWER_CHECKLIST.md
@@ -10,12 +10,12 @@
- [ ] I checked that screenshots or videos are included for tests on [all platforms](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#make-sure-you-can-test-on-all-platforms)
- [ ] I included screenshots or videos for tests on [all platforms](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#make-sure-you-can-test-on-all-platforms)
- [ ] I verified tests pass on **all platforms** & I tested again on:
- - [ ] Android / native
- - [ ] Android / Chrome
- - [ ] iOS / native
- - [ ] iOS / Safari
- - [ ] MacOS / Chrome / Safari
- - [ ] MacOS / Desktop
+ - [ ] Android: Native
+ - [ ] Android: mWeb Chrome
+ - [ ] iOS: Native
+ - [ ] iOS: mWeb Safari
+ - [ ] MacOS: Chrome / Safari
+ - [ ] MacOS: Desktop
- [ ] If there are any errors in the console that are unrelated to this PR, I either fixed them (preferred) or linked to where I reported them in Slack
- [ ] I verified proper code patterns were followed (see [Reviewing the code](https://github.com/Expensify/App/blob/main/contributingGuides/PR_REVIEW_GUIDELINES.md#reviewing-the-code))
- [ ] I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. `toggleReport` and not `onIconClick`).
diff --git a/docs/articles/expensify-classic/billing-and-subscriptions/Tax-Exempt.md b/docs/articles/expensify-classic/billing-and-subscriptions/Tax-Exempt.md
index c4948b5b3083..33fbec003a91 100644
--- a/docs/articles/expensify-classic/billing-and-subscriptions/Tax-Exempt.md
+++ b/docs/articles/expensify-classic/billing-and-subscriptions/Tax-Exempt.md
@@ -1,5 +1,20 @@
---
title: Tax Exempt
-description: Tax Exempt
+description: Tax-exempt status in Expensify for organizations recognized by the IRS or local tax authorities.
---
-## Resource Coming Soon!
+# Overview
+If your organization is recognized by the IRS or other local tax authorities as tax-exempt, that means you don’t need to pay any tax on your Expensify monthly bill. Please follow these instructions to request tax-exempt status.
+# How to request tax-exempt status in Expensify
+1. Go to **Settings > Account > Payments**.
+1. Click on the option that says **Request Tax-Exempt Status**.
+1. After you've requested tax-exempt status, Concierge (our support service) will start a conversation with you. They will ask you to upload a PDF of your tax-exempt documentation. This document should include your VAT number (or "RUT" in Chile). You can use one of the following documents: 501(c), ST-119, or a foreign tax-exempt declaration.
+1. Our team will review your document and let you know if we need any more information.
+1. Once everything is verified, we'll update your account accordingly.
+
+Once your account is marked as tax-exempt, the corresponding state tax will no longer be applied to future billing.
+
+If you need to remove your tax-exempt status, let your Account Manager know or contact Concierge.
+
+# FAQ
+## What happens to my past Expensify bills that incorrectly had tax added to them?
+Expensify can provide a refund for the tax you were charged on your previous bills. Please let your Account Manager know or contact Concierge if this is the case.
diff --git a/docs/articles/expensify-classic/integrations/accounting-integrations/QuickBooks-Desktop.md b/docs/articles/expensify-classic/integrations/accounting-integrations/QuickBooks-Desktop.md
index 3ee1c8656b4b..d8c7c145a670 100644
--- a/docs/articles/expensify-classic/integrations/accounting-integrations/QuickBooks-Desktop.md
+++ b/docs/articles/expensify-classic/integrations/accounting-integrations/QuickBooks-Desktop.md
@@ -1,5 +1,144 @@
---
-title: Coming Soon
-description: Coming Soon
+title: QuickBooks Desktop
+description: How to connect Expensify to QuickBooks Desktop and troubleshoot issues.
---
-## Resource Coming Soon!
+# Overview
+QuickBooks Desktop is an accounting package developed by Intuit. It is designed for small and medium-sized businesses to help them manage their financial and accounting tasks. You can connect Expensify to QuickBooks Desktop to make expense management seamless.
+To connect Expensify with QuickBooks Desktop, using Right Networks as your hosting platform is best. Right Networks is a cloud-based service we recommend for this integration. If you need a Right Networks account, complete [this form](https://info.rightnetworks.com/partner-expensify) and contact a Sales Consultant to start the process.
+
+# How to connect to QuickBooks Desktop
+Before you link your Expensify policy with QuickBooks Desktop, ensure you log in as an Admin in QuickBooks. Also, check that the company file you want to connect to is the only one open.
+
+## Set up submitters in QuickBooks Desktop
+For a seamless integration, here are the steps to follow:
+* Make sure all report submitters are set up as Vendors in QuickBooks Desktop and their Expensify email is in the "Main Email" field of their Vendor record. You can do this in the vendor section of QuickBooks.
+* If you want to export reports to your users' employee records instead of vendor records, select Check or Journal Entry as your reimbursable export option.
+* To set up Expensify users as employees, activate QuickBooks Desktop Payroll. This module is necessary to access the Employee Profile tab, where you can enter the submitter's email addresses.
+
+## Enable/install the Expensify Sync Manager
+Navigate to **Settings** > **Policies** > **Group** > _[Policy Name]_ > **Connections**, select the Connect to QuickBooks Desktop radio button and click Connect to QuickBooks.
+
+**Enable the Expensify Sync Manager in Right Networks (recommended)**
+*Please note: Single-user mode in QuickBooks Desktop is required.*
+If you don't yet have an account with Right Networks, you must first contact Right Networks [here](https://info.rightnetworks.com/partner-expensify). You can enable the Expensify Sync Manager yourself from your Right Networks portal's **My Account** section or contact Right Networks for assistance.
+
+**OR, install the Expensify Sync Manager on Your Third-Party Remote Desktop.**
+To download the Sync Manager to your desktop, you must contact your third-party remote desktop provider and request permission. They might have security restrictions, so it's best to communicate with them directly to avoid potential problems with the Sync Manager. Remember that the Sync Manager program file should be stored in the same location (i.e., the same drive) as your QuickBooks Desktop program.
+
+## Complete the connection
+1. Open QuickBooks and access your desired Company File using the QuickBooks Admin credentials. Admin credentials are necessary for creating the connection due to permission requirements, but you won't need to stay logged in as an admin for syncing or exporting.
+2. Navigate to your Expensify policy settings by going to **Settings** > **Policies** > **Group** > _[Policy Name]_ > **Connections**. Copy the Token by selecting the copy icon.
+3. While QuickBooks is still running, launch the Expensify Sync Manager. Paste your Token into the Sync Manager and click **Save**.
+4. Once the Sync Manager status displays **Connected**, return to Expensify and click the *Continue* button.
+
+## Allow access
+1. Return to QuickBooks, and you'll encounter an **Application Certificate** screen. On the first page of the Certificate screen, click **Yes, always; allow access even if QuickBooks is not running** and then click **Continue**.
+2. On the second page of the Certificate screen, choose the Admin user from the dropdown menu, and then click *Done* to complete this step. Note that selecting Admin here does not require you to be logged in as an admin to use this connection; it's simply selecting the appropriate permissions.
+3. Head back to Expensify and patiently wait for the sync process to finish, then move on to the configuration.
+
+# How to configure export settings for QuickBooks Desktop
+To Configure Settings, go to **Settings** > **Policies** > **Group** > _[Policy Name]_ > **Connections** and click **Configure**.
+
+## Preferred Exporter
+This person is used in QuickBooks Desktop as the export user. They will also receive notifications for errors.
+
+## Date
+Choose either the report's submitted date, the report's exported date, or the date of the last expense on the report when exporting reports to QuickBooks Desktop.
+
+## Use unique reference numbers
+Enable this to allow use of a unique reference number for each transaction. Disable this to use the same Report ID for all expenses from a certain report.
+
+## Reimbursable expenses
+* **Vendor Bill (recommended):** A single itemized vendor bill for each Expensify report. An A/P account is required to export to a vendor bill.
+* **Check:** A single itemized check for each Expensify report.
+* **Journal Entry:** A single itemized journal entry for each Expensify report.
+
+## Non-reimbursable expenses
+**Credit Card Expenses:**
+* Each expense will appear as a separate credit card transaction.
+* The posting date will match your credit card statement.
+* To display the merchant name in the payee field in QuickBooks Desktop, ensure that a matching Vendor exists in QuickBooks. Expensify searches for an exact match during export. If no match is found, the payee is mapped to a **Credit Card Misc.** Vendor created by Expensify.
+* If you're centrally managing company cards through Domain Control, you can export expenses from each card to a specific QuickBooks account (detailed instructions available).
+
+**Debit Card Expenses:**
+* Expenses export as individual itemized checks for each Expensify report.
+* The check is written to the "vendor," which is the person who created or submitted the report in Expensify.
+
+**Vendor Bill:**
+* Each Expensify report results in a single itemized vendor bill.
+* The bill is associated with the "vendor," which is the individual responsible for creating or submitting the report in Expensify.
+
+# How to configure coding for QuickBooks Desktop
+## Categories
+Expensify's integration with QuickBooks brings in your Chart of Accounts as Categories in Expensify automatically. Here's how to manage them:
+1. After connecting, go to **Settings** > **Policies** > **Group** > _[Policy Name]_ > **Categories** to view the accounts imported from QuickBooks Desktop.
+2. You can use the enable/disable button to choose which Categories your employees can access. Additionally, you can set specific rules for each Category via the blue settings cog.
+3. Expensify offers Auto-Categorization to automatically assign expenses to the appropriate expense categories.
+4. If needed, you can edit the names of the imported Categories to simplify expense coding for your employees. Keep in mind that if you make changes to these accounts in QuickBooks Desktop, the category names in Expensify will update to match them during the next sync.
+5. _**Important:**_ Each expense must have a category selected to export to QuickBooks Desktop. The selected category must be one imported from QuickBooks Desktop; you cannot manually create categories within Expensify policy settings.
+## Classes
+Classes can be imported from QuickBooks as either tags (line-item level) or report fields (header level).
+
+## Customers/Projects
+You can bring in Customers/Projects from QuickBooks into Expensify in two ways: as tags (at the line-item level) or as report fields (at the header level). If you're utilizing Billable Expenses in Expensify, here's what you need to know:
+* Customers/Projects must be enabled if you're using Billable Expenses.
+* Expenses marked as "Billable" need to be tagged with a Customer/Project to successfully export them to QuickBooks.
+
+## Items
+Items can be imported from QuickBooks as categories alongside your expense accounts.
+
+# FAQ
+## How do I sync my connection?
+1: Ensure that both the Expensify Sync Manager and QuickBooks Desktop are running.
+2: On the Expensify website, navigate to **Settings** > **Policies** > **Group** > _[Policy Name]_ > **Connections** > **QuickBooks Desktop**, and click **Sync now**.
+3: Wait for the syncing process to finish. Typically, this takes about 2-5 minutes, but it might take longer, depending on when you last synced and the size of your QuickBooks company file. The page will refresh automatically once syncing is complete.
+
+We recommend syncing at least once a week or whenever you make changes in QuickBooks Desktop that could impact how your reports export from Expensify. Changes could include adjustments to your Chart of Accounts, Vendors, Employees, Customers/Jobs, or Items. Remember, both the Sync Manager and QuickBooks Desktop need to be running for syncing or exporting to work.
+
+## Can I export negative expenses?
+Generally, you can export negative expenses to QuickBooks Desktop successfully, regardless of your option. However, please keep in mind that if you have *Check* selected as your export option, the report's total cannot be negative.
+
+## How does multi-currency work with QuickBooks Desktop?
+When using QuickBooks Desktop Multi-Currency, there are some limitations to consider based on your export options:
+1. **Vendor Bills and Checks:** The currency of the vendor and the currency of the account must match, but they do not have to be in the home currency.
+2. **Credit Card:** If an expense doesn't match an existing vendor in QuickBooks, it exports to the **Credit Card Misc.** vendor created by Expensify. When exporting a report in a currency other than your home currency, the transaction will be created under the vendor's currency with a 1:1 conversion. For example, a transaction in Expensify for $50 CAD will appear in QuickBooks as $50 USD.
+3. **Journal Entries:** Multi-currency exports will fail because the account currency must match both the vendor currency and the home currency.
+
+# Sync and export errors
+## Error: No Vendor Found For Email in QuickBooks
+To address this issue, ensure that each submitter's email is saved as the **Main Email** in their Vendor record within QuickBooks Desktop. Here's how to resolve it:
+1. Go to your Vendor section in QuickBooks.
+2. Verify that the email mentioned in the error matches the **Main Email** field in the respective vendor's record. It's important to note that this comparison is case-sensitive, so ensure that capitalization matches as well.
+3. If you prefer to export reports to your users' employee records instead of their vendor records, select either **Check** or **Journal Entry** as your reimbursable export option. If you are setting up Expensify users as employees, activate QuickBooks Desktop Payroll to access the Employee Profile tab where submitter email addresses need to be entered.
+4. Once you've added the correct email to the vendor record, save this change, and then sync your policy before attempting to export the report again.
+
+## Error: Do Not Have Permission to Access Company Data File
+To resolve this error, follow these steps:
+1. Log into QuickBooks Desktop as an Admin in single-user mode.
+2. Go to **Edit** > **Preferences** > **Integrated Applications** > **Company Preferences**.
+3. Select the Expensify Sync Manager and click on **Properties**.
+4. Ensure that **Allow this application to login automatically** is checked, and then click **OK**. Close all windows within QuickBooks.
+5. If you still encounter the error after following the above steps, go to **Edit** > **Preferences** > **Integrated Applications** > **Company Preferences**, and remove the Expensify Sync Manager from the list.
+6. Next, attempt to sync your policy again in Expensify. You'll be prompted to re-authorize the connection in QuickBooks.
+7. Click **Yes, always; allow access even if QuickBooks is not running.**
+8. From the dropdown, select the Admin user, and then click **Continue**. Note that selecting **Admin** here doesn't mean you always have to be logged in as an admin to use the connection; it's just required for setting up the connection.
+9. Click **Done** on the pop-up window and return to Expensify, where your policy should complete the syncing process.
+
+## Error: The Wrong QuickBooks Company is Open.
+This error suggests that the wrong company file is open in QuickBooks Desktop. To resolve this issue, follow these steps:
+1. First, go through the general troubleshooting steps as outlined.
+2. If you can confirm that the incorrect company file is open in QuickBooks, go to QuickBooks and select **File** > **Open or Restore Company** > _[Company Name]_ to open the correct company file. After doing this, try syncing your policy again.
+3. If the correct company file is open, but you're still encountering the error, completely close QuickBooks Desktop, reopen the desired company file and then attempt to sync again.
+4. If the error persists, log into QuickBooks as an admin in single-user mode. Then, go to **Edit** > **Preferences** > **Integrated Applications** > **Company Preferences** and remove the Expensify Sync Manager from the list.
+5. Next, try syncing your policy again in Expensify. You'll be prompted to re-authorize the connection in QuickBooks, allowing you to sync successfully.
+6. If the error continues even after trying the steps above, double-check that the token you see in the Sync Manager matches the token in your connection settings.
+
+## Error: The Expensify Sync Manager Could Not Be Reached.
+To resolve this error, follow these steps:
+*Note: You must be in single-user mode to sync.*
+
+1. Ensure that both the Sync Manager and QuickBooks Desktop are running.
+2. Confirm that the Sync Manager is installed in the correct location. It should be in the same location as your QuickBooks application. If QuickBooks is on your local desktop, the Sync Manager should be there, too. If QuickBooks is on a remote server, install the Sync Manager there.
+Verify that the Sync Manager's status is **Connected**.
+3. If the Sync Manager status is already **Connected**, click **Edit** and then *Save* to refresh the connection. Afterwards, try syncing your policy again.
+4. If the error persists, double-check that the token you see in the Sync Manager matches the token in your connection settings.
diff --git a/package-lock.json b/package-lock.json
index ddebbe8a3832..3b21c29578d8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -51,7 +51,7 @@
"date-fns-tz": "^2.0.0",
"dom-serializer": "^0.2.2",
"domhandler": "^4.3.0",
- "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#f76ff4badf0934e21ac6c3f195ebc5791bb72247",
+ "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#009c2ab79bf7ddeab0eea7a3a4c0d9cc4277c34b",
"fbjs": "^3.0.2",
"htmlparser2": "^7.2.0",
"idb-keyval": "^6.2.1",
@@ -30218,8 +30218,8 @@
},
"node_modules/expensify-common": {
"version": "1.0.0",
- "resolved": "git+ssh://git@github.com/Expensify/expensify-common.git#f76ff4badf0934e21ac6c3f195ebc5791bb72247",
- "integrity": "sha512-RCC3VBRIoW1ZF+wZktHT+0ht6EUjNfCKW4g44RR5570dRpN9SrJASGt2OnEbz6mrbohWRgAaKmB4+nfiQX5ndQ==",
+ "resolved": "git+ssh://git@github.com/Expensify/expensify-common.git#009c2ab79bf7ddeab0eea7a3a4c0d9cc4277c34b",
+ "integrity": "sha512-mD9p6Qj8FfvLdb6JLXvF0UNqLN6ymssUU67Fm37zmK18hd1Abw+vR/pQkNpHR2iv+KRs8HdcyuZ0vaOec4VrFQ==",
"license": "MIT",
"dependencies": {
"classnames": "2.3.1",
@@ -74887,9 +74887,9 @@
}
},
"expensify-common": {
- "version": "git+ssh://git@github.com/Expensify/expensify-common.git#f76ff4badf0934e21ac6c3f195ebc5791bb72247",
- "integrity": "sha512-RCC3VBRIoW1ZF+wZktHT+0ht6EUjNfCKW4g44RR5570dRpN9SrJASGt2OnEbz6mrbohWRgAaKmB4+nfiQX5ndQ==",
- "from": "expensify-common@git+ssh://git@github.com/Expensify/expensify-common.git#f76ff4badf0934e21ac6c3f195ebc5791bb72247",
+ "version": "git+ssh://git@github.com/Expensify/expensify-common.git#009c2ab79bf7ddeab0eea7a3a4c0d9cc4277c34b",
+ "integrity": "sha512-mD9p6Qj8FfvLdb6JLXvF0UNqLN6ymssUU67Fm37zmK18hd1Abw+vR/pQkNpHR2iv+KRs8HdcyuZ0vaOec4VrFQ==",
+ "from": "expensify-common@git+ssh://git@github.com/Expensify/expensify-common.git#009c2ab79bf7ddeab0eea7a3a4c0d9cc4277c34b",
"requires": {
"classnames": "2.3.1",
"clipboard": "2.0.4",
diff --git a/package.json b/package.json
index 9a3b9ed3af86..beb55085e8fd 100644
--- a/package.json
+++ b/package.json
@@ -94,7 +94,7 @@
"date-fns-tz": "^2.0.0",
"dom-serializer": "^0.2.2",
"domhandler": "^4.3.0",
- "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#f76ff4badf0934e21ac6c3f195ebc5791bb72247",
+ "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#009c2ab79bf7ddeab0eea7a3a4c0d9cc4277c34b",
"fbjs": "^3.0.2",
"htmlparser2": "^7.2.0",
"idb-keyval": "^6.2.1",
diff --git a/src/CONST.ts b/src/CONST.ts
index d3841189fce2..23957827d140 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -478,20 +478,21 @@ const CONST = {
ACTIONS: {
LIMIT: 50,
TYPE: {
- APPROVED: 'APPROVED',
ADDCOMMENT: 'ADDCOMMENT',
+ APPROVED: 'APPROVED',
+ CHRONOSOOOLIST: 'CHRONOSOOOLIST',
CLOSED: 'CLOSED',
CREATED: 'CREATED',
- TASKEDITED: 'TASKEDITED',
- TASKCANCELLED: 'TASKCANCELLED',
IOU: 'IOU',
MODIFIEDEXPENSE: 'MODIFIEDEXPENSE',
REIMBURSEMENTQUEUED: 'REIMBURSEMENTQUEUED',
RENAMED: 'RENAMED',
- CHRONOSOOOLIST: 'CHRONOSOOOLIST',
+ REPORTPREVIEW: 'REPORTPREVIEW',
+ SUBMITTED: 'SUBMITTED',
+ TASKCANCELLED: 'TASKCANCELLED',
TASKCOMPLETED: 'TASKCOMPLETED',
+ TASKEDITED: 'TASKEDITED',
TASKREOPENED: 'TASKREOPENED',
- REPORTPREVIEW: 'REPORTPREVIEW',
POLICYCHANGELOG: {
ADD_APPROVER_RULE: 'POLICYCHANGELOG_ADD_APPROVER_RULE',
ADD_CATEGORY: 'POLICYCHANGELOG_ADD_CATEGORY',
@@ -930,6 +931,7 @@ const CONST = {
RECEIPTS: 'receipts@expensify.com',
STUDENT_AMBASSADOR: 'studentambassadors@expensify.com',
SVFG: 'svfg@expensify.com',
+ EXPENSIFY_EMAIL_DOMAIN: '@expensify.com',
},
ACCOUNT_ID: {
@@ -1170,6 +1172,14 @@ const CONST = {
AUDITOR: 'auditor',
USER: 'user',
},
+ AUTO_REPORTING_FREQUENCIES: {
+ IMMEDIATE: 'immediate',
+ WEEKLY: 'weekly',
+ SEMI_MONTHLY: 'semimonthly',
+ MONTHLY: 'monthly',
+ TRIP: 'trip',
+ MANUAL: 'manual',
+ },
ROOM_PREFIX: '#',
CUSTOM_UNIT_RATE_BASE_OFFSET: 100,
OWNER_EMAIL_FAKE: '_FAKE_',
@@ -2725,6 +2735,8 @@ const CONST = {
SPEND: 'spend',
WORKSPACES: 'workspaces',
},
+
+ MISSING_TRANSLATION: 'MISSING TRANSLATION',
} as const;
export default CONST;
diff --git a/src/components/IFrame.js b/src/components/IFrame.js
new file mode 100644
index 000000000000..129af4254c42
--- /dev/null
+++ b/src/components/IFrame.js
@@ -0,0 +1,102 @@
+/* eslint-disable es/no-nullish-coalescing-operators */
+import React, {useEffect, useState} from 'react';
+
+function getNewDotURL(url) {
+ const urlObj = new URL(url);
+ const paramString = urlObj.searchParams.get('param') ?? '';
+ const pathname = urlObj.pathname.slice(1);
+
+ let params;
+ try {
+ params = JSON.parse(paramString);
+ } catch {
+ params = {};
+ }
+
+ if (pathname === 'inbox') {
+ return 'home';
+ }
+
+ if (pathname === 'expenses') {
+ return `${params.viewMode === 'charts' ? 'insights' : 'expenses'}${paramString ? `/?param=${paramString}` : ''}`;
+ }
+
+ if (pathname === 'admin_policies') {
+ const {section} = params;
+ return section === 'individual' ? 'individual_workspaces' : 'group_workspaces';
+ }
+
+ if (pathname === 'policy') {
+ const workspaceID = params.policyID || '';
+ const section = urlObj.hash.slice(1) || 'overview';
+
+ return `workspace/${workspaceID}/${section}`;
+ }
+
+ if (pathname === 'settings') {
+ const {section} = params;
+ return `settings/${section}`;
+ }
+
+ if (pathname.includes('domain')) {
+ return pathname;
+ }
+
+ return pathname;
+}
+
+function getOldDotURL(url) {
+ const urlObj = new URL(url);
+ const pathname = urlObj.pathname;
+ const paths = pathname.slice(1).split('/');
+
+ if (pathname === 'home') {
+ return 'inbox';
+ }
+
+ if (pathname === 'expenses' || pathname === 'insights') {
+ return `expenses/${urlObj.search}`;
+ }
+
+ if (pathname === 'individual_workspaces' || pathname === 'group_workspaces') {
+ const param = {section: pathname === 'individual_workspaces' ? 'individual' : 'group'};
+ return `admin_policies?param=${JSON.stringify(param)}`;
+ }
+
+ if (pathname === 'workspace') {
+ const [, workspaceID, section] = paths;
+ const param = {policyID: workspaceID};
+ return `policy/?param${JSON.stringify(param)}#${section}`;
+ }
+
+ if (pathname === 'settings') {
+ const [, section] = paths;
+ const param = {section};
+ return `settings?param=${JSON.stringify(param)}`;
+ }
+
+ return pathname;
+}
+
+export default function ReportScreen() {
+ const [oldDotURL, setOldDotURL] = useState('https://www.expensify.com.dev');
+
+ useEffect(() => {
+ setOldDotURL(`https://expensify.com.dev/${getOldDotURL(window.location.href)}`);
+
+ window.addEventListener('message', (event) => {
+ const url = event.data;
+ // TODO: use this value to navigate to a new path
+ // eslint-disable-next-line no-unused-vars
+ const newDotURL = getNewDotURL(url);
+ });
+ }, []);
+
+ return (
+
+ );
+}
diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js
index b71047e3ca36..e3329532f324 100755
--- a/src/components/MoneyRequestConfirmationList.js
+++ b/src/components/MoneyRequestConfirmationList.js
@@ -141,6 +141,9 @@ const propTypes = {
/** Whether the money request is a distance request */
isDistanceRequest: PropTypes.bool,
+ /** A flag for verifying that the current report is a sub-report of a workspace chat */
+ isPolicyExpenseChat: PropTypes.bool,
+
/* Onyx Props */
/** Collection of categories attached to a policy */
policyCategories: PropTypes.objectOf(categoryPropTypes),
@@ -176,6 +179,7 @@ const defaultProps = {
transaction: {},
mileageRate: {unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, rate: 0, currency: 'USD'},
isDistanceRequest: false,
+ isPolicyExpenseChat: false,
};
function MoneyRequestConfirmationList(props) {
@@ -193,11 +197,9 @@ function MoneyRequestConfirmationList(props) {
const distance = lodashGet(transaction, 'routes.route0.distance', 0);
const shouldCalculateDistanceAmount = props.isDistanceRequest && props.iouAmount === 0;
- // A flag for verifying that the current report is a sub-report of a workspace chat
- const isPolicyExpenseChat = useMemo(() => ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(ReportUtils.getReport(props.reportID))), [props.reportID]);
-
// A flag for showing the categories field
- const shouldShowCategories = isPolicyExpenseChat && Permissions.canUseCategories(props.betas) && OptionsListUtils.hasEnabledOptions(_.values(props.policyCategories));
+ const shouldShowCategories =
+ props.isPolicyExpenseChat && Permissions.canUseCategories(props.betas) && (props.iouCategory || OptionsListUtils.hasEnabledOptions(_.values(props.policyCategories)));
// Fetches the first tag list of the policy
const policyTag = PolicyUtils.getTag(props.policyTags);
@@ -205,7 +207,7 @@ function MoneyRequestConfirmationList(props) {
const policyTagListName = lodashGet(policyTag, 'name', translate('common.tag'));
const canUseTags = Permissions.canUseTags(props.betas);
// A flag for showing the tags field
- const shouldShowTags = isPolicyExpenseChat && canUseTags && OptionsListUtils.hasEnabledOptions(_.values(policyTagList));
+ const shouldShowTags = props.isPolicyExpenseChat && canUseTags && OptionsListUtils.hasEnabledOptions(_.values(policyTagList));
// A flag for showing the billable field
const shouldShowBillable = canUseTags && !lodashGet(props.policy, 'disabledFields.defaultBillable', true);
diff --git a/src/components/OptionRow.js b/src/components/OptionRow.js
index 70415ab03a13..47ab4fe45db1 100644
--- a/src/components/OptionRow.js
+++ b/src/components/OptionRow.js
@@ -282,6 +282,7 @@ class OptionRow extends Component {
) : (
this.props.onSelectedStatePressed(this.props.option)}
+ disabled={this.state.isDisabled}
accessibilityRole={CONST.ACCESSIBILITY_ROLE.CHECKBOX}
accessibilityLabel={CONST.ACCESSIBILITY_ROLE.CHECKBOX}
>
diff --git a/src/components/PopoverWithoutOverlay/index.js b/src/components/PopoverWithoutOverlay/index.js
index 7287f36e7f2c..d35637958f1d 100644
--- a/src/components/PopoverWithoutOverlay/index.js
+++ b/src/components/PopoverWithoutOverlay/index.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, {useRef} from 'react';
import {View} from 'react-native';
import {SafeAreaInsetsContext} from 'react-native-safe-area-context';
import {PopoverContext} from '../PopoverProvider';
@@ -8,9 +8,11 @@ import styles from '../../styles/styles';
import * as StyleUtils from '../../styles/StyleUtils';
import getModalStyles from '../../styles/getModalStyles';
import withWindowDimensions from '../withWindowDimensions';
+import usePrevious from '../../hooks/usePrevious';
function Popover(props) {
const {onOpen, close} = React.useContext(PopoverContext);
+ const firstRenderRef = useRef(true);
const {modalStyle, modalContainerStyle, shouldAddTopSafeAreaMargin, shouldAddBottomSafeAreaMargin, shouldAddTopSafeAreaPadding, shouldAddBottomSafeAreaPadding} = getModalStyles(
'popover',
{
@@ -23,6 +25,8 @@ function Popover(props) {
props.outerStyle,
);
+ const prevIsVisible = usePrevious(props.isVisible);
+
React.useEffect(() => {
if (props.isVisible) {
props.onModalShow();
@@ -37,11 +41,18 @@ function Popover(props) {
Modal.onModalDidClose();
}
Modal.willAlertModalBecomeVisible(props.isVisible);
+
+ // We prevent setting closeModal function to null when the component is invisible the first time it is rendered
+ if (prevIsVisible === props.isVisible && (!firstRenderRef.current || !props.isVisible)) {
+ firstRenderRef.current = false;
+ return;
+ }
+ firstRenderRef.current = false;
Modal.setCloseModal(props.isVisible ? () => props.onClose(props.anchorRef) : null);
// We want this effect to run strictly ONLY when isVisible prop changes
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [props.isVisible]);
+ }, [props.isVisible, prevIsVisible]);
if (!props.isVisible) {
return null;
diff --git a/src/components/TabSelector/TabSelector.js b/src/components/TabSelector/TabSelector.js
index 79cd1a6dd17d..4efa033c60d0 100644
--- a/src/components/TabSelector/TabSelector.js
+++ b/src/components/TabSelector/TabSelector.js
@@ -82,7 +82,6 @@ const getBackgroundColor = (position, routesLength, tabIndex) => {
function TabSelector({state, navigation, onTabPress, position}) {
const {translate} = useLocalize();
-
return (
{_.map(state.routes, (route, index) => {
@@ -120,6 +119,7 @@ function TabSelector({state, navigation, onTabPress, position}) {
activeOpacity={activeOpacity}
inactiveOpacity={inactiveOpacity}
backgroundColor={backgroundColor}
+ isFocused={isFocused}
/>
);
})}
diff --git a/src/components/TabSelector/TabSelectorItem.js b/src/components/TabSelector/TabSelectorItem.js
index d0ea8fcc773b..6611b8acf914 100644
--- a/src/components/TabSelector/TabSelectorItem.js
+++ b/src/components/TabSelector/TabSelectorItem.js
@@ -1,4 +1,4 @@
-import {Animated} from 'react-native';
+import {Animated, StyleSheet} from 'react-native';
import React from 'react';
import PropTypes from 'prop-types';
import styles from '../../styles/styles';
@@ -27,6 +27,9 @@ const propTypes = {
/** Animated opacity value while the label is in active state */
// eslint-disable-next-line
activeOpacity: PropTypes.any,
+
+ /** Whether this tab is active */
+ isFocused: PropTypes.bool,
};
const defaultProps = {
@@ -36,29 +39,32 @@ const defaultProps = {
backgroundColor: '',
inactiveOpacity: 1,
activeOpacity: 0,
+ isFocused: false,
};
-const AnimatedPressableWithFeedback = Animated.createAnimatedComponent(PressableWithFeedback);
-
-function TabSelectorItem({icon, title, onPress, backgroundColor, activeOpacity, inactiveOpacity}) {
+function TabSelectorItem({icon, title, onPress, backgroundColor, activeOpacity, inactiveOpacity, isFocused}) {
return (
-
-
-
-
+ {({hovered}) => (
+
+
+
+
+ )}
+
);
}
diff --git a/src/libs/Localize/index.js b/src/libs/Localize/index.js
index db371301f43f..a26c7d4ebc10 100644
--- a/src/libs/Localize/index.js
+++ b/src/libs/Localize/index.js
@@ -2,12 +2,27 @@ import _ from 'underscore';
import lodashGet from 'lodash/get';
import Str from 'expensify-common/lib/str';
import * as RNLocalize from 'react-native-localize';
+import Onyx from 'react-native-onyx';
import Log from '../Log';
import Config from '../../CONFIG';
import translations from '../../languages/translations';
import CONST from '../../CONST';
import LocaleListener from './LocaleListener';
import BaseLocaleListener from './LocaleListener/BaseLocaleListener';
+import ONYXKEYS from '../../ONYXKEYS';
+
+// Current user mail is needed for handling missing translations
+let userEmail = '';
+Onyx.connect({
+ key: ONYXKEYS.SESSION,
+ waitForCollectionCallback: true,
+ callback: (val) => {
+ if (!val) {
+ return;
+ }
+ userEmail = val.email;
+ },
+});
// Listener when an update in Onyx happens so we use the updated locale when translating/localizing items.
LocaleListener.connect();
@@ -70,11 +85,14 @@ function translate(desiredLanguage = CONST.LOCALES.DEFAULT, phraseKey, phrasePar
return Str.result(translatedPhrase, phraseParameters);
}
- // Phrase is not found in default language, on production log an alert to server
+ // Phrase is not found in default language, on production and staging log an alert to server
// on development throw an error
- if (Config.IS_IN_PRODUCTION) {
+ if (Config.IS_IN_PRODUCTION || Config.IS_IN_STAGING) {
const phraseString = _.isArray(phraseKey) ? phraseKey.join('.') : phraseKey;
Log.alert(`${phraseString} was not found in the en locale`);
+ if (userEmail.includes(CONST.EMAIL.EXPENSIFY_EMAIL_DOMAIN)) {
+ return CONST.MISSING_TRANSLATION;
+ }
return phraseString;
}
throw new Error(`${phraseKey} was not found in the default language`);
diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js
index 9dff18f94955..770f7b05ad3f 100644
--- a/src/libs/ReportUtils.js
+++ b/src/libs/ReportUtils.js
@@ -2202,6 +2202,9 @@ function getIOUReportActionMessage(iouReportID, type, total, comment, currency,
case CONST.REPORT.ACTIONS.TYPE.APPROVED:
iouMessage = `approved ${amount}`;
break;
+ case CONST.REPORT.ACTIONS.TYPE.SUBMITTED:
+ iouMessage = `submitted ${amount}`;
+ break;
case CONST.IOU.REPORT_ACTION_TYPE.CREATE:
iouMessage = `requested ${amount}${comment && ` for ${comment}`}`;
break;
@@ -2359,6 +2362,44 @@ function buildOptimisticApprovedReportAction(amount, currency, expenseReportID)
};
}
+/**
+ * Builds an optimistic SUBMITTED report action with a randomly generated reportActionID.
+ *
+ * @param {Number} amount
+ * @param {String} currency
+ * @param {Number} expenseReportID
+ *
+ * @returns {Object}
+ */
+function buildOptimisticSubmittedReportAction(amount, currency, expenseReportID) {
+ const originalMessage = {
+ amount,
+ currency,
+ expenseReportID,
+ };
+
+ return {
+ actionName: CONST.REPORT.ACTIONS.TYPE.SUBMITTED,
+ actorAccountID: currentUserAccountID,
+ automatic: false,
+ avatar: lodashGet(currentUserPersonalDetails, 'avatar', UserUtils.getDefaultAvatar(currentUserAccountID)),
+ isAttachment: false,
+ originalMessage,
+ message: getIOUReportActionMessage(expenseReportID, CONST.REPORT.ACTIONS.TYPE.SUBMITTED, Math.abs(amount), '', currency),
+ person: [
+ {
+ style: 'strong',
+ text: lodashGet(currentUserPersonalDetails, 'displayName', currentUserEmail),
+ type: 'TEXT',
+ },
+ ],
+ reportActionID: NumberUtils.rand64(),
+ shouldShow: true,
+ created: DateUtils.getDBTime(),
+ pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD,
+ };
+}
+
/**
* Builds an optimistic report preview action with a randomly generated reportActionID.
*
@@ -3539,6 +3580,15 @@ function getPolicyExpenseChatReportIDByOwner(policyOwner) {
return expenseChat.reportID;
}
+/**
+ * @param {String} policyID
+ * @param {Array} accountIDs
+ * @returns {Array}
+ */
+function getWorkspaceChats(policyID, accountIDs) {
+ return _.filter(allReports, (report) => isPolicyExpenseChat(report) && lodashGet(report, 'policyID', '') === policyID && _.contains(accountIDs, lodashGet(report, 'ownerAccountID', '')));
+}
+
/*
* @param {Object|null} report
* @returns {Boolean}
@@ -3837,6 +3887,7 @@ export {
buildOptimisticEditedTaskReportAction,
buildOptimisticIOUReport,
buildOptimisticApprovedReportAction,
+ buildOptimisticSubmittedReportAction,
buildOptimisticExpenseReport,
buildOptimisticIOUReportAction,
buildOptimisticReportPreview,
@@ -3903,6 +3954,7 @@ export {
isDM,
getPolicy,
getPolicyExpenseChatReportIDByOwner,
+ getWorkspaceChats,
shouldDisableSettings,
shouldDisableRename,
hasSingleParticipant,
diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js
index 07f61470f24d..2c046bfc2a24 100644
--- a/src/libs/actions/IOU.js
+++ b/src/libs/actions/IOU.js
@@ -22,6 +22,7 @@ import * as Report from './Report';
import * as NumberUtils from '../NumberUtils';
import ReceiptGeneric from '../../../assets/images/receipt-generic.png';
import * as LocalePhoneNumber from '../LocalePhoneNumber';
+import * as Policy from './Policy';
let allReports;
Onyx.connect({
@@ -44,13 +45,6 @@ Onyx.connect({
},
});
-let allRecentlyUsedCategories = {};
-Onyx.connect({
- key: ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES,
- waitForCollectionCallback: true,
- callback: (val) => (allRecentlyUsedCategories = val),
-});
-
let allRecentlyUsedTags = {};
Onyx.connect({
key: ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS,
@@ -137,7 +131,7 @@ function buildOnyxDataForMoneyRequest(
iouAction,
optimisticPersonalDetailListAction,
reportPreviewAction,
- optimisticRecentlyUsedCategories,
+ optimisticPolicyRecentlyUsedCategories,
optimisticPolicyRecentlyUsedTags,
isNewChatReport,
isNewIOUReport,
@@ -189,11 +183,11 @@ function buildOnyxDataForMoneyRequest(
},
];
- if (!_.isEmpty(optimisticRecentlyUsedCategories)) {
+ if (!_.isEmpty(optimisticPolicyRecentlyUsedCategories)) {
optimisticData.push({
onyxMethod: Onyx.METHOD.SET,
key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${iouReport.policyID}`,
- value: optimisticRecentlyUsedCategories,
+ value: optimisticPolicyRecentlyUsedCategories,
});
}
@@ -478,13 +472,7 @@ function getMoneyRequestInformation(
billable,
);
- const uniquePolicyRecentlyUsedCategories = allRecentlyUsedCategories
- ? _.filter(
- allRecentlyUsedCategories[`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${iouReport.policyID}`],
- (recentlyUsedPolicyCategory) => recentlyUsedPolicyCategory !== category,
- )
- : [];
- const optimisticPolicyRecentlyUsedCategories = [category, ...uniquePolicyRecentlyUsedCategories];
+ const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories(iouReport.policyID, category);
const optimisticPolicyRecentlyUsedTags = {};
const policyTags = allPolicyTags[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${iouReport.policyID}`];
@@ -882,11 +870,12 @@ function requestMoney(
* @param {Number} amount - always in the smallest unit of the currency
* @param {String} comment
* @param {String} currency
+ * @param {String} category
* @param {String} existingSplitChatReportID - the report ID where the split bill happens, could be a group chat or a workspace chat
*
* @return {Object}
*/
-function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, existingSplitChatReportID = '') {
+function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, category, existingSplitChatReportID = '') {
const currentUserEmailForIOUSplit = OptionsListUtils.addSMSDomainIfPhoneNumber(currentUserLogin);
const participantAccountIDs = _.map(participants, (participant) => Number(participant.accountID));
const existingSplitChatReport =
@@ -910,6 +899,10 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco
'',
'',
`${Localize.translateLocal('iou.splitBill')} ${Localize.translateLocal('common.with')} ${formattedParticipants} [${DateUtils.getDBTime().slice(0, 10)}]`,
+ undefined,
+ undefined,
+ undefined,
+ category,
);
// Note: The created action must be optimistically generated before the IOU action so there's no chance that the created action appears after the IOU action in the chat
@@ -1036,9 +1029,12 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco
const hasMultipleParticipants = participants.length > 1;
_.each(participants, (participant) => {
- // In case the participant is a worskapce, email & accountID should remain undefined and won't be used in the rest of this code
- const email = isOwnPolicyExpenseChat ? '' : OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login).toLowerCase();
- const accountID = isOwnPolicyExpenseChat ? 0 : Number(participant.accountID);
+ // In a case when a participant is a workspace, even when a current user is not an owner of the workspace
+ const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(participant);
+
+ // In case the participant is a workspace, email & accountID should remain undefined and won't be used in the rest of this code
+ const email = isOwnPolicyExpenseChat || isPolicyExpenseChat ? '' : OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login).toLowerCase();
+ const accountID = isOwnPolicyExpenseChat || isPolicyExpenseChat ? 0 : Number(participant.accountID);
if (email === currentUserEmailForIOUSplit) {
return;
}
@@ -1090,6 +1086,11 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco
'',
CONST.IOU.MONEY_REQUEST_TYPE.SPLIT,
splitTransaction.transactionID,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ category,
);
// STEP 4: Build optimistic reportActions. We need:
@@ -1131,6 +1132,12 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco
oneOnOneReportPreviewAction = ReportUtils.buildOptimisticReportPreview(oneOnOneChatReport, oneOnOneIOUReport);
}
+ // Add category to optimistic policy recently used categories when a participant is a workspace
+ let optimisticPolicyRecentlyUsedCategories = [];
+ if (isPolicyExpenseChat) {
+ optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories(participant.policyID, category);
+ }
+
// STEP 5: Build Onyx Data
const [oneOnOneOptimisticData, oneOnOneSuccessData, oneOnOneFailureData] = buildOnyxDataForMoneyRequest(
oneOnOneChatReport,
@@ -1141,7 +1148,7 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco
oneOnOneIOUAction,
oneOnOnePersonalDetailListAction,
oneOnOneReportPreviewAction,
- [],
+ optimisticPolicyRecentlyUsedCategories,
{},
isNewOneOnOneChatReport,
shouldCreateNewOneOnOneIOUReport,
@@ -1191,11 +1198,11 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco
* @param {Number} amount - always in smallest currency unit
* @param {String} comment
* @param {String} currency
+ * @param {String} category
* @param {String} existingSplitChatReportID - Either a group DM or a workspace chat
*/
-function splitBill(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, existingSplitChatReportID = '') {
- const {splitData, splits, onyxData} = createSplitsAndOnyxData(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, existingSplitChatReportID);
-
+function splitBill(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, category, existingSplitChatReportID = '') {
+ const {splitData, splits, onyxData} = createSplitsAndOnyxData(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, category, existingSplitChatReportID);
API.write(
'SplitBill',
{
@@ -1204,6 +1211,7 @@ function splitBill(participants, currentUserLogin, currentUserAccountID, amount,
splits: JSON.stringify(splits),
currency,
comment,
+ category,
transactionID: splitData.transactionID,
reportActionID: splitData.reportActionID,
createdReportActionID: splitData.createdReportActionID,
@@ -1224,9 +1232,10 @@ function splitBill(participants, currentUserLogin, currentUserAccountID, amount,
* @param {Number} amount - always in smallest currency unit
* @param {String} comment
* @param {String} currency
+ * @param {String} category
*/
-function splitBillAndOpenReport(participants, currentUserLogin, currentUserAccountID, amount, comment, currency) {
- const {splitData, splits, onyxData} = createSplitsAndOnyxData(participants, currentUserLogin, currentUserAccountID, amount, comment, currency);
+function splitBillAndOpenReport(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, category) {
+ const {splitData, splits, onyxData} = createSplitsAndOnyxData(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, category);
API.write(
'SplitBillAndOpenReport',
@@ -1236,6 +1245,7 @@ function splitBillAndOpenReport(participants, currentUserLogin, currentUserAccou
splits: JSON.stringify(splits),
currency,
comment,
+ category,
transactionID: splitData.transactionID,
reportActionID: splitData.reportActionID,
createdReportActionID: splitData.createdReportActionID,
@@ -2096,6 +2106,80 @@ function approveMoneyRequest(expenseReport) {
API.write('ApproveMoneyRequest', {reportID: expenseReport.reportID, approvedReportActionID: optimisticApprovedReportAction.reportActionID}, {optimisticData, successData, failureData});
}
+/**
+ * @param {Object} expenseReport
+ */
+function submitReport(expenseReport) {
+ const optimisticSubmittedReportAction = ReportUtils.buildOptimisticSubmittedReportAction(expenseReport.total, expenseReport.currency, expenseReport.reportID);
+
+ const optimisticReportActionsData = {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`,
+ value: {
+ [optimisticSubmittedReportAction.reportActionID]: {
+ ...optimisticSubmittedReportAction,
+ pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD,
+ },
+ },
+ };
+ const optimisticIOUReportData = {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`,
+ value: {
+ ...expenseReport,
+ lastMessageText: optimisticSubmittedReportAction.message[0].text,
+ lastMessageHtml: optimisticSubmittedReportAction.message[0].html,
+ state: CONST.REPORT.STATE.SUBMITTED,
+ stateNum: CONST.REPORT.STATE_NUM.PROCESSING,
+ statusNum: CONST.REPORT.STATUS.SUBMITTED,
+ },
+ };
+ const optimisticData = [optimisticIOUReportData, optimisticReportActionsData];
+
+ const successData = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`,
+ value: {
+ [optimisticSubmittedReportAction.reportActionID]: {
+ pendingAction: null,
+ },
+ },
+ },
+ ];
+
+ const failureData = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`,
+ value: {
+ [expenseReport.reportActionID]: {
+ errors: ErrorUtils.getMicroSecondOnyxError('iou.error.other'),
+ },
+ },
+ },
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`,
+ value: {
+ state: CONST.REPORT.STATE.OPEN,
+ stateNum: CONST.REPORT.STATE_NUM.OPEN,
+ },
+ },
+ ];
+
+ API.write(
+ 'SubmitReport',
+ {
+ reportID: expenseReport.reportID,
+ managerEmail: expenseReport.managerEmail,
+ managerAccountID: expenseReport.managerID,
+ reportActionID: optimisticSubmittedReportAction.reportActionID,
+ },
+ {optimisticData, successData, failureData},
+ );
+}
+
/**
* @param {String} paymentType
* @param {Object} chatReport
@@ -2302,8 +2386,19 @@ function navigateToNextPage(iou, iouType, report, path = '') {
Navigation.navigate(ROUTES.MONEY_REQUEST_PARTICIPANTS.getRoute(iouType));
}
-function submitReport() {
- // Will be implemented in https://github.com/Expensify/App/issues/28763
+/**
+ * When the money request or split bill creation flow is initialized via FAB, the reportID is not passed as a navigation
+ * parameter.
+ * Gets a report id from the first participant of the IOU object stored in Onyx.
+ * @param {Object} iou
+ * @param {Array} iou.participants
+ * @param {Object} route
+ * @param {Object} route.params
+ * @param {String} [route.params.reportID]
+ * @returns {String}
+ */
+function getIOUReportID(iou, route) {
+ return lodashGet(route, 'params.reportID') || lodashGet(iou, 'participants.0.reportID', '');
}
export {
@@ -2315,6 +2410,7 @@ export {
requestMoney,
sendMoneyElsewhere,
approveMoneyRequest,
+ submitReport,
payMoneyRequest,
sendMoneyWithWallet,
startMoneyRequest,
@@ -2336,5 +2432,5 @@ export {
navigateToNextPage,
updateDistanceRequest,
replaceReceipt,
- submitReport,
+ getIOUReportID,
};
diff --git a/src/libs/actions/Policy.js b/src/libs/actions/Policy.js
index fcce909c5582..1a73b148e100 100644
--- a/src/libs/actions/Policy.js
+++ b/src/libs/actions/Policy.js
@@ -1,6 +1,7 @@
import _ from 'underscore';
import Onyx from 'react-native-onyx';
import lodashGet from 'lodash/get';
+import lodashUnion from 'lodash/union';
import {PUBLIC_DOMAINS} from 'expensify-common/lib/CONST';
import Str from 'expensify-common/lib/str';
import {escapeRegExp} from 'lodash';
@@ -65,6 +66,13 @@ Onyx.connect({
callback: (val) => (allPersonalDetails = val),
});
+let allRecentlyUsedCategories = {};
+Onyx.connect({
+ key: ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES,
+ waitForCollectionCallback: true,
+ callback: (val) => (allRecentlyUsedCategories = val),
+});
+
/**
* Stores in Onyx the policy ID of the last workspace that was accessed by the user
* @param {String|null} policyID
@@ -196,14 +204,37 @@ function removeMembers(accountIDs, policyID) {
if (accountIDs.length === 0) {
return;
}
+
const membersListKey = `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`;
+ const policy = ReportUtils.getPolicy(policyID);
+ const workspaceChats = ReportUtils.getWorkspaceChats(policyID, accountIDs);
+ const optimisticClosedReportActions = _.map(workspaceChats, () =>
+ ReportUtils.buildOptimisticClosedReportAction(sessionEmail, policy.name, CONST.REPORT.ARCHIVE_REASON.REMOVED_FROM_POLICY),
+ );
+
const optimisticData = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: membersListKey,
value: _.object(accountIDs, Array(accountIDs.length).fill({pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE})),
},
+ ..._.map(workspaceChats, (report) => ({
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`,
+ value: {
+ statusNum: CONST.REPORT.STATUS.CLOSED,
+ stateNum: CONST.REPORT.STATE_NUM.SUBMITTED,
+ oldPolicyName: policy.name,
+ hasDraft: false,
+ },
+ })),
+ ..._.map(optimisticClosedReportActions, (reportAction, index) => ({
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChats[index].reportID}`,
+ value: {[reportAction.reportActionID]: reportAction},
+ })),
];
+
const successData = [
{
onyxMethod: Onyx.METHOD.MERGE,
@@ -217,6 +248,21 @@ function removeMembers(accountIDs, policyID) {
key: membersListKey,
value: _.object(accountIDs, Array(accountIDs.length).fill({errors: ErrorUtils.getMicroSecondOnyxError('workspace.people.error.genericRemove')})),
},
+ ..._.map(workspaceChats, ({reportID, stateNum, statusNum, hasDraft, oldPolicyName = null}) => ({
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
+ value: {
+ stateNum,
+ statusNum,
+ hasDraft,
+ oldPolicyName,
+ },
+ })),
+ ..._.map(optimisticClosedReportActions, (reportAction, index) => ({
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChats[index].reportID}`,
+ value: {[reportAction.reportActionID]: null},
+ })),
];
API.write(
'DeleteMembersFromWorkspace',
@@ -1168,6 +1214,21 @@ function clearErrors(policyID) {
hideWorkspaceAlertMessage(policyID);
}
+/**
+ * @param {String} policyID
+ * @param {String} category
+ * @returns {Object}
+ */
+function buildOptimisticPolicyRecentlyUsedCategories(policyID, category) {
+ if (!policyID || !category) {
+ return [];
+ }
+
+ const policyRecentlyUsedCategories = lodashGet(allRecentlyUsedCategories, `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${policyID}`, []);
+
+ return lodashUnion([category], policyRecentlyUsedCategories);
+}
+
export {
removeMembers,
addMembersToWorkspace,
@@ -1197,4 +1258,5 @@ export {
setWorkspaceInviteMembersDraft,
clearErrors,
openDraftWorkspaceRequest,
+ buildOptimisticPolicyRecentlyUsedCategories,
};
diff --git a/src/pages/iou/MoneyRequestCategoryPage.js b/src/pages/iou/MoneyRequestCategoryPage.js
index 5284b785304f..5055c9f90f8a 100644
--- a/src/pages/iou/MoneyRequestCategoryPage.js
+++ b/src/pages/iou/MoneyRequestCategoryPage.js
@@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import lodashGet from 'lodash/get';
import {withOnyx} from 'react-native-onyx';
+import compose from '../../libs/compose';
import ROUTES from '../../ROUTES';
import Navigation from '../../libs/Navigation/Navigation';
import useLocalize from '../../hooks/useLocalize';
@@ -83,11 +84,20 @@ MoneyRequestCategoryPage.displayName = 'MoneyRequestCategoryPage';
MoneyRequestCategoryPage.propTypes = propTypes;
MoneyRequestCategoryPage.defaultProps = defaultProps;
-export default withOnyx({
- report: {
- key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${lodashGet(route, 'params.reportID', '')}`,
- },
- iou: {
- key: ONYXKEYS.IOU,
- },
-})(MoneyRequestCategoryPage);
+export default compose(
+ withOnyx({
+ iou: {
+ key: ONYXKEYS.IOU,
+ },
+ }),
+ // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file
+ withOnyx({
+ report: {
+ key: ({route, iou}) => {
+ const reportID = IOU.getIOUReportID(iou, route);
+
+ return `${ONYXKEYS.COLLECTION.REPORT}${reportID}`;
+ },
+ },
+ }),
+)(MoneyRequestCategoryPage);
diff --git a/src/pages/iou/MoneyRequestTagPage.js b/src/pages/iou/MoneyRequestTagPage.js
index 43c0cd9d1480..2fc37d2c4271 100644
--- a/src/pages/iou/MoneyRequestTagPage.js
+++ b/src/pages/iou/MoneyRequestTagPage.js
@@ -98,14 +98,21 @@ MoneyRequestTagPage.defaultProps = defaultProps;
export default compose(
withOnyx({
- report: {
- key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${lodashGet(route, 'params.reportID')}`,
- },
iou: {
key: ONYXKEYS.IOU,
},
}),
// eslint-disable-next-line rulesdir/no-multiple-onyx-in-file
+ withOnyx({
+ report: {
+ key: ({route, iou}) => {
+ const reportID = IOU.getIOUReportID(iou, route);
+
+ return `${ONYXKEYS.COLLECTION.REPORT}${reportID}`;
+ },
+ },
+ }),
+ // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file
withOnyx({
policyTags: {
key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report ? report.policyID : '0'}`,
diff --git a/src/pages/iou/SplitBillDetailsPage.js b/src/pages/iou/SplitBillDetailsPage.js
index 23da92bcc9e7..6f7e37883c85 100644
--- a/src/pages/iou/SplitBillDetailsPage.js
+++ b/src/pages/iou/SplitBillDetailsPage.js
@@ -1,5 +1,6 @@
import React from 'react';
import _ from 'underscore';
+import lodashGet from 'lodash/get';
import {View} from 'react-native';
import PropTypes from 'prop-types';
import {withOnyx} from 'react-native-onyx';
@@ -69,7 +70,7 @@ function SplitBillDetailsPage(props) {
}
const payeePersonalDetails = props.personalDetails[reportAction.actorAccountID];
const participantsExcludingPayee = _.filter(participants, (participant) => participant.accountID !== reportAction.actorAccountID);
- const {amount: splitAmount, currency: splitCurrency, comment: splitComment} = ReportUtils.getTransactionDetails(transaction);
+ const {amount: splitAmount, currency: splitCurrency, comment: splitComment, category: splitCategory} = ReportUtils.getTransactionDetails(transaction);
return (
@@ -87,9 +88,11 @@ function SplitBillDetailsPage(props) {
iouAmount={splitAmount}
iouComment={splitComment}
iouCurrencyCode={splitCurrency}
+ iouCategory={splitCategory}
iouType={CONST.IOU.MONEY_REQUEST_TYPE.SPLIT}
isReadOnly
shouldShowFooter={false}
+ reportID={lodashGet(props.report, 'reportID', '')}
/>
)}
diff --git a/src/pages/iou/steps/MoneyRequestConfirmPage.js b/src/pages/iou/steps/MoneyRequestConfirmPage.js
index 3881221d5c52..907869c0e3a4 100644
--- a/src/pages/iou/steps/MoneyRequestConfirmPage.js
+++ b/src/pages/iou/steps/MoneyRequestConfirmPage.js
@@ -75,8 +75,14 @@ function MoneyRequestConfirmPage(props) {
}),
[props.iou.participants, props.personalDetails],
);
+ const isPolicyExpenseChat = useMemo(() => ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(props.report)), [props.report]);
const isManualRequestDM = props.selectedTab === CONST.TAB.MANUAL && iouType.current === CONST.IOU.MONEY_REQUEST_TYPE.REQUEST;
+ useEffect(() => {
+ IOU.resetMoneyRequestCategory();
+ IOU.resetMoneyRequestTag();
+ }, []);
+
useEffect(() => {
const policyExpenseChat = _.find(participants, (participant) => participant.isPolicyExpenseChat);
if (policyExpenseChat) {
@@ -198,6 +204,7 @@ function MoneyRequestConfirmPage(props) {
props.iou.amount,
trimmedComment,
props.iou.currency,
+ props.iou.category,
reportID.current,
);
return;
@@ -212,6 +219,7 @@ function MoneyRequestConfirmPage(props) {
props.iou.amount,
trimmedComment,
props.iou.currency,
+ props.iou.category,
);
return;
}
@@ -238,6 +246,7 @@ function MoneyRequestConfirmPage(props) {
props.currentUserPersonalDetails.login,
props.currentUserPersonalDetails.accountID,
props.iou.currency,
+ props.iou.category,
props.iou.receiptPath,
props.iou.receiptSource,
isDistanceRequest,
@@ -338,6 +347,7 @@ function MoneyRequestConfirmPage(props) {
receiptSource={props.iou.receiptSource}
iouType={iouType.current}
reportID={reportID.current}
+ isPolicyExpenseChat={isPolicyExpenseChat}
// The participants can only be modified when the action is initiated from directly within a group chat and not the floating-action-button.
// This is because when there is a group of people, say they are on a trip, and you have some shared expenses with some of the people,
// but not all of them (maybe someone skipped out on dinner). Then it's nice to be able to select/deselect people from the group chat bill
@@ -375,12 +385,8 @@ export default compose(
withOnyx({
report: {
key: ({route, iou}) => {
- let reportID = lodashGet(route, 'params.reportID', '');
- if (!reportID) {
- // When the money request creation flow is initialized on Global Create, the reportID is not passed as a navigation parameter.
- // Get the report id from the participants list on the IOU object stored in Onyx.
- reportID = lodashGet(iou, 'participants.0.reportID', '');
- }
+ const reportID = IOU.getIOUReportID(iou, route);
+
return `${ONYXKEYS.COLLECTION.REPORT}${reportID}`;
},
},
diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js
index 8d745903eb40..89c18efc4e76 100644
--- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js
+++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js
@@ -45,7 +45,6 @@ const defaultProps = {
function MoneyRequestParticipantsPage({iou, selectedTab, route}) {
const {translate} = useLocalize();
const prevMoneyRequestId = useRef(iou.id);
- const isNewReportIDSelectedLocally = useRef(false);
const optionsSelectorRef = useRef();
const iouType = useRef(lodashGet(route, 'params.iouType', ''));
const reportID = useRef(lodashGet(route, 'params.reportID', ''));
@@ -63,19 +62,7 @@ function MoneyRequestParticipantsPage({iou, selectedTab, route}) {
setHeaderTitle(_.isEmpty(iou.participants) ? translate('tabSelector.manual') : translate('iou.split'));
}, [iou.participants, isDistanceRequest, translate]);
- const navigateToRequestStep = (moneyRequestType, option) => {
- if (option.reportID) {
- isNewReportIDSelectedLocally.current = true;
- IOU.setMoneyRequestId(`${moneyRequestType}${option.reportID}`);
- Navigation.navigate(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(moneyRequestType, option.reportID));
- return;
- }
-
- IOU.setMoneyRequestId(moneyRequestType);
- Navigation.navigate(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(moneyRequestType, reportID.current));
- };
-
- const navigateToSplitStep = (moneyRequestType) => {
+ const navigateToConfirmationStep = (moneyRequestType) => {
IOU.setMoneyRequestId(moneyRequestType);
Navigation.navigate(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(moneyRequestType, reportID.current));
};
@@ -88,7 +75,7 @@ function MoneyRequestParticipantsPage({iou, selectedTab, route}) {
// ID in Onyx could change by initiating a new request in a separate browser tab or completing a request
if (prevMoneyRequestId.current !== iou.id) {
// The ID is cleared on completing a request. In that case, we will do nothing
- if (iou.id && !isDistanceRequest && !isSplitRequest && !isNewReportIDSelectedLocally.current) {
+ if (iou.id && !isDistanceRequest && !isSplitRequest) {
navigateBack(true);
}
return;
@@ -96,7 +83,7 @@ function MoneyRequestParticipantsPage({iou, selectedTab, route}) {
// Reset the money request Onyx if the ID in Onyx does not match the ID from params
const moneyRequestId = `${iouType.current}${reportID.current}`;
- const shouldReset = iou.id !== moneyRequestId && !isNewReportIDSelectedLocally.current;
+ const shouldReset = iou.id !== moneyRequestId;
if (shouldReset) {
IOU.resetMoneyRequestInfo(moneyRequestId);
}
@@ -126,8 +113,8 @@ function MoneyRequestParticipantsPage({iou, selectedTab, route}) {
ref={(el) => (optionsSelectorRef.current = el)}
participants={iou.participants}
onAddParticipants={IOU.setMoneyRequestParticipants}
- navigateToRequest={(option) => navigateToRequestStep(CONST.IOU.MONEY_REQUEST_TYPE.REQUEST, option)}
- navigateToSplit={() => navigateToSplitStep(CONST.IOU.MONEY_REQUEST_TYPE.SPLIT)}
+ navigateToRequest={() => navigateToConfirmationStep(CONST.IOU.MONEY_REQUEST_TYPE.REQUEST)}
+ navigateToSplit={() => navigateToConfirmationStep(CONST.IOU.MONEY_REQUEST_TYPE.SPLIT)}
safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle}
iouType={iouType.current}
isDistanceRequest={isDistanceRequest}
diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js
index 05b206ce4147..ac30bcf55787 100755
--- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js
+++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js
@@ -164,7 +164,7 @@ function MoneyRequestParticipantsSelector({
onAddParticipants([
{accountID: option.accountID, login: option.login, isPolicyExpenseChat: option.isPolicyExpenseChat, reportID: option.reportID, selected: true, searchText: option.searchText},
]);
- navigateToRequest(option);
+ navigateToRequest();
};
/**
diff --git a/src/styles/styles.js b/src/styles/styles.js
index f36e552c2ecd..d4aacfff96aa 100644
--- a/src/styles/styles.js
+++ b/src/styles/styles.js
@@ -3486,11 +3486,17 @@ const styles = (theme) => ({
tabText: (isSelected) => ({
marginLeft: 8,
- fontFamily: isSelected ? fontFamily.EXP_NEUE_BOLD : fontFamily.EXP_NEUE,
- fontWeight: isSelected ? fontWeightBold : 400,
+ fontFamily: fontFamily.EXP_NEUE_BOLD,
+ fontWeight: fontWeightBold,
color: isSelected ? theme.textLight : theme.textSupporting,
}),
+ tabBackground: (hovered, isFocused, background) => ({
+ backgroundColor: hovered && !isFocused ? theme.highlightBG : background,
+ }),
+
+ tabOpacity: (hovered, isFocused, activeOpacityValue, inactiveOpacityValue) => (hovered && !isFocused ? inactiveOpacityValue : activeOpacityValue),
+
/**
* @param {String} backgroundColor
* @param {Number} height
diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts
index 143b70127de5..e03775ee114e 100644
--- a/src/types/onyx/OriginalMessage.ts
+++ b/src/types/onyx/OriginalMessage.ts
@@ -53,6 +53,10 @@ type OriginalMessageAddComment = {
reactions?: Reaction[];
};
};
+type OriginalMessageSubmitted = {
+ actionName: typeof CONST.REPORT.ACTIONS.TYPE.SUBMITTED;
+ originalMessage: unknown;
+};
type OriginalMessageClosed = {
actionName: typeof CONST.REPORT.ACTIONS.TYPE.CLOSED;
@@ -127,6 +131,7 @@ type OriginalMessagePolicyTask = {
type OriginalMessage =
| OriginalMessageIOU
| OriginalMessageAddComment
+ | OriginalMessageSubmitted
| OriginalMessageClosed
| OriginalMessageCreated
| OriginalMessageRenamed
diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts
index df4a1364a894..12b4cb92024e 100644
--- a/src/types/onyx/Policy.ts
+++ b/src/types/onyx/Policy.ts
@@ -47,6 +47,12 @@ type Policy = {
/** Whether policy expense chats can be created and used on this policy. Enabled manually by CQ/JS snippet. Always true for free policies. */
isPolicyExpenseChatEnabled: boolean;
+
+ /** Whether the scheduled submit is enabled */
+ autoReporting: boolean;
+
+ /** The scheduled submit frequency set up on the this policy */
+ autoReportingFrequency: ValueOf;
};
export default Policy;