diff --git a/android/app/build.gradle b/android/app/build.gradle
index aee47932ef02..76e6e8fbba5b 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -106,8 +106,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
- versionCode 1001030101
- versionName "1.3.1-1"
+ versionCode 1001030202
+ versionName "1.3.2-2"
}
splits {
diff --git a/assets/images/task.svg b/assets/images/task.svg
new file mode 100644
index 000000000000..20412f771b69
--- /dev/null
+++ b/assets/images/task.svg
@@ -0,0 +1,15 @@
+
+
+
diff --git a/contributingGuides/CONTRIBUTING.md b/contributingGuides/CONTRIBUTING.md
index e4264a5cb56e..3c6bcd30e6bc 100644
--- a/contributingGuides/CONTRIBUTING.md
+++ b/contributingGuides/CONTRIBUTING.md
@@ -111,7 +111,7 @@ Additionally if you want to discuss an idea with the open source community witho
3. If you cannot reproduce the problem, pause on this step and add a comment to the issue explaining where you are stuck or that you don't think the issue can be reproduced.
#### Propose a solution for the job
-4. Do not propose solutions to jobs without the `Help Wanted` label applied. Any proposals submitted when that label is not present will not be reviewed.
+4. You can propose solutions on any issue at at any time, but if you propose solutions to jobs before the `Help Wanted` label is applied, you do so at your own risk. Proposals will not be reviewed until the label is added and there is always a chance that we might not add the label or hire an external contributor for the job.
5. After you reproduce the issue, complete the [proposal template here](./PROPOSAL_TEMPLATE.md) and post it as a comment in the corresponding GitHub issue (linked in the Upwork job).
- Note: Before submitting a proposal on an issue, be sure to read any other existing proposals. ALL NEW PROPOSALS MUST BE DIFFERENT FROM EXISTING PROPOSALS. The *difference* should be important, meaningful or considerable.
6. Refrain from leaving additional comments until someone from the Contributor-Plus team and / or someone from Expensify provides feedback on your proposal (do not create a pull request yet).
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index 4d69d9105585..374c409a088a 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -17,7 +17,7 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 1.3.1
+ 1.3.2
CFBundleSignature
????
CFBundleURLTypes
@@ -30,7 +30,7 @@
CFBundleVersion
- 1.3.1.1
+ 1.3.2.2
ITSAppUsesNonExemptEncryption
LSApplicationQueriesSchemes
diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist
index eb369c6b82e8..eb4f6d0326a7 100644
--- a/ios/NewExpensifyTests/Info.plist
+++ b/ios/NewExpensifyTests/Info.plist
@@ -15,10 +15,10 @@
CFBundlePackageType
BNDL
CFBundleShortVersionString
- 1.3.1
+ 1.3.2
CFBundleSignature
????
CFBundleVersion
- 1.3.1.1
+ 1.3.2.2
diff --git a/package-lock.json b/package-lock.json
index ca90e80bd9cf..b4291421f19c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "new.expensify",
- "version": "1.3.1-1",
+ "version": "1.3.2-2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "new.expensify",
- "version": "1.3.1-1",
+ "version": "1.3.2-2",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@@ -34,6 +34,7 @@
"@react-navigation/native": "6.0.13",
"@react-navigation/stack": "6.3.1",
"@ua/react-native-airship": "^15.2.0",
+ "awesome-phonenumber": "^5.4.0",
"babel-plugin-transform-remove-console": "^6.9.4",
"babel-polyfill": "^6.26.0",
"dom-serializer": "^0.2.2",
@@ -16726,6 +16727,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/awesome-phonenumber": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/awesome-phonenumber/-/awesome-phonenumber-5.4.0.tgz",
+ "integrity": "sha512-jf6E+GHKRIMobCKygQhZ9kHmdxZ8hvXUlVhLyesP/k8JVpmWAyNa5TzWDS0hKe480tmT419yRtGLmTpD0k8pUQ==",
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/axe-core": {
"version": "4.4.3",
"dev": true,
@@ -52421,6 +52430,11 @@
"version": "1.0.5",
"dev": true
},
+ "awesome-phonenumber": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/awesome-phonenumber/-/awesome-phonenumber-5.4.0.tgz",
+ "integrity": "sha512-jf6E+GHKRIMobCKygQhZ9kHmdxZ8hvXUlVhLyesP/k8JVpmWAyNa5TzWDS0hKe480tmT419yRtGLmTpD0k8pUQ=="
+ },
"axe-core": {
"version": "4.4.3",
"dev": true
diff --git a/package.json b/package.json
index 1320678f43db..2315146aed39 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "1.3.1-1",
+ "version": "1.3.2-2",
"author": "Expensify, Inc.",
"homepage": "https://new.expensify.com",
"description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
@@ -65,6 +65,7 @@
"@react-navigation/native": "6.0.13",
"@react-navigation/stack": "6.3.1",
"@ua/react-native-airship": "^15.2.0",
+ "awesome-phonenumber": "^5.4.0",
"babel-plugin-transform-remove-console": "^6.9.4",
"babel-polyfill": "^6.26.0",
"dom-serializer": "^0.2.2",
diff --git a/src/CONST.js b/src/CONST.js
index 54bfc6c53fbf..5ee576483cff 100755
--- a/src/CONST.js
+++ b/src/CONST.js
@@ -191,6 +191,7 @@ const CONST = {
POLICY_ROOMS: 'policyRooms',
POLICY_EXPENSE_CHAT: 'policyExpenseChat',
PASSWORDLESS: 'passwordless',
+ TASKS: 'tasks',
},
BUTTON_STATES: {
DEFAULT: 'default',
diff --git a/src/ONYXKEYS.js b/src/ONYXKEYS.js
index 49d5435c8ebd..b0526ae4b61a 100755
--- a/src/ONYXKEYS.js
+++ b/src/ONYXKEYS.js
@@ -188,6 +188,7 @@ export default {
HOME_ADDRESS_FORM: 'homeAddressForm',
NEW_ROOM_FORM: 'newRoomForm',
ROOM_SETTINGS_FORM: 'roomSettingsForm',
+ NEW_TASK_FORM: 'newTaskForm',
MONEY_REQUEST_DESCRIPTION_FORM: 'moneyRequestDescriptionForm',
},
diff --git a/src/ROUTES.js b/src/ROUTES.js
index 423169d6dd3e..117cd0e6c85a 100644
--- a/src/ROUTES.js
+++ b/src/ROUTES.js
@@ -13,6 +13,7 @@ const IOU_DETAILS = 'iou/details';
const IOU_REQUEST_CURRENCY = `${IOU_REQUEST}/currency`;
const IOU_BILL_CURRENCY = `${IOU_BILL}/currency`;
const IOU_SEND_CURRENCY = `${IOU_SEND}/currency`;
+const NEW_TASK = 'new/task';
const SETTINGS_PERSONAL_DETAILS = 'settings/profile/personal-details';
const SETTINGS_CONTACT_METHODS = 'settings/profile/contact-methods';
@@ -55,6 +56,7 @@ export default {
SETTINGS_NEW_CONTACT_METHOD: `${SETTINGS_CONTACT_METHODS}/new`,
NEW_GROUP: 'new/group',
NEW_CHAT: 'new/chat',
+ NEW_TASK,
REPORT,
REPORT_WITH_ID: 'r/:reportID',
getReportRoute: reportID => `r/${reportID}`,
@@ -89,6 +91,9 @@ export default {
IOU_DETAILS_ENABLE_PAYMENTS: `${IOU_DETAILS}/enable-payments`,
IOU_DETAILS_WITH_IOU_REPORT_ID: `${IOU_DETAILS}/:chatReportID/:iouReportID/`,
getIouDetailsRoute: (chatReportID, iouReportID) => `iou/details/${chatReportID}/${iouReportID}`,
+ getNewTaskRoute: reportID => `${NEW_TASK}/${reportID}`,
+ NEW_TASK_WITH_REPORT_ID: `${NEW_TASK}/:reportID?`,
+ getTaskDetailsRoute: taskID => `task/details/${taskID}`,
SEARCH: 'search',
SET_PASSWORD_WITH_VALIDATE_CODE: 'setpassword/:accountID/:validateCode',
DETAILS: 'details',
diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.js b/src/components/EmojiPicker/EmojiPickerMenu/index.js
index cb0f71cd1d7c..112ad8ffa7cc 100755
--- a/src/components/EmojiPicker/EmojiPickerMenu/index.js
+++ b/src/components/EmojiPicker/EmojiPickerMenu/index.js
@@ -494,7 +494,7 @@ class EmojiPickerMenu extends Component {
pointerEvents={this.state.arePointerEventsDisabled ? 'none' : 'auto'}
>
{!this.props.isSmallScreenWidth && (
-
+
diff --git a/src/components/Icon/Expensicons.js b/src/components/Icon/Expensicons.js
index 976d846dcb81..d3c7c2f4bc57 100644
--- a/src/components/Icon/Expensicons.js
+++ b/src/components/Icon/Expensicons.js
@@ -109,6 +109,7 @@ import Podcast from '../../../assets/images/social-podcast.svg';
import Linkedin from '../../../assets/images/social-linkedin.svg';
import Instagram from '../../../assets/images/social-instagram.svg';
import AddReaction from '../../../assets/images/add-reaction.svg';
+import Task from '../../../assets/images/task.svg';
export {
ActiveRoomAvatar,
@@ -204,6 +205,7 @@ export {
Send,
Shield,
Sync,
+ Task,
ThreeDots,
Transfer,
Trashcan,
diff --git a/src/components/MenuItem.js b/src/components/MenuItem.js
index 070b639c2796..c8dc826a316c 100644
--- a/src/components/MenuItem.js
+++ b/src/components/MenuItem.js
@@ -1,6 +1,8 @@
import _ from 'underscore';
import React from 'react';
-import {View} from 'react-native';
+import {
+ View, Pressable,
+} from 'react-native';
import Text from './Text';
import styles from '../styles/styles';
import * as StyleUtils from '../styles/StyleUtils';
@@ -16,14 +18,9 @@ import colors from '../styles/colors';
import variables from '../styles/variables';
import MultipleAvatars from './MultipleAvatars';
import * as defaultWorkspaceAvatars from './Icon/WorkspaceDefaultAvatars';
-import PressableWithSecondaryInteraction from './PressableWithSecondaryInteraction';
-import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions';
-import * as DeviceCapabilities from '../libs/DeviceCapabilities';
-import ControlSelection from '../libs/ControlSelection';
const propTypes = {
...menuItemPropTypes,
- ...windowDimensionsPropTypes,
};
const defaultProps = {
@@ -49,13 +46,11 @@ const defaultProps = {
subtitle: undefined,
iconType: CONST.ICON_TYPE_ICON,
onPress: () => {},
- onSecondaryInteraction: undefined,
interactive: true,
fallbackIcon: Expensicons.FallbackAvatar,
brickRoadIndicator: '',
floatRightAvatars: [],
shouldStackHorizontally: false,
- shouldBlockSelection: false,
};
const MenuItem = (props) => {
@@ -76,7 +71,7 @@ const MenuItem = (props) => {
]);
return (
- {
if (props.disabled) {
return;
@@ -88,9 +83,6 @@ const MenuItem = (props) => {
props.onPress(e);
}}
- onPressIn={() => props.shouldBlockSelection && props.isSmallScreenWidth && DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()}
- onPressOut={ControlSelection.unblock}
- onSecondaryInteraction={props.onSecondaryInteraction}
style={({hovered, pressed}) => ([
props.style,
StyleUtils.getButtonBackgroundColorStyle(getButtonState(props.focused || hovered, pressed, props.success, props.disabled, props.interactive), true),
@@ -98,7 +90,6 @@ const MenuItem = (props) => {
styles.popoverMaxWidth,
])}
disabled={props.disabled}
- ref={props.forwardedRef}
>
{({hovered, pressed}) => (
<>
@@ -221,14 +212,12 @@ const MenuItem = (props) => {
>
)}
-
+
);
};
MenuItem.propTypes = propTypes;
MenuItem.defaultProps = defaultProps;
MenuItem.displayName = 'MenuItem';
-export default withWindowDimensions(React.forwardRef((props, ref) => (
- // eslint-disable-next-line react/jsx-props-no-spreading
-
-)));
+
+export default MenuItem;
diff --git a/src/components/MenuItemList.js b/src/components/MenuItemList.js
index fe7d40b582f8..e442d3766893 100644
--- a/src/components/MenuItemList.js
+++ b/src/components/MenuItemList.js
@@ -3,8 +3,6 @@ import _ from 'underscore';
import PropTypes from 'prop-types';
import MenuItem from './MenuItem';
import menuItemPropTypes from './menuItemPropTypes';
-import * as ReportActionContextMenu from '../pages/home/report/ContextMenu/ReportActionContextMenu';
-import {CONTEXT_MENU_TYPES} from '../pages/home/report/ContextMenu/ContextMenuActions';
const propTypes = {
/** An array of props that are pass to individual MenuItem components */
@@ -14,38 +12,17 @@ const defaultProps = {
menuItems: [],
};
-const MenuItemList = (props) => {
- let popoverAnchor;
-
- /**
- * Handle the secondary interaction for a menu item.
- *
- * @param {*} link the menu item link or function to get the link
- * @param {Event} e the interaction event
- */
- const secondaryInteraction = (link, e) => {
- if (typeof link === 'function') {
- link().then(url => ReportActionContextMenu.showContextMenu(CONTEXT_MENU_TYPES.LINK, e, url, popoverAnchor));
- } else if (!_.isEmpty(link)) {
- ReportActionContextMenu.showContextMenu(CONTEXT_MENU_TYPES.LINK, e, link, popoverAnchor);
- }
- };
-
- return (
- <>
- {_.map(props.menuItems, menuItemProps => (
-
diff --git a/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.js b/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.js
index ba5c0e5fbb5b..a2aa35f6beff 100644
--- a/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.js
+++ b/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.js
@@ -156,6 +156,12 @@ class ContactMethodDetailsPage extends Component {
render() {
const contactMethod = this.getContactMethod();
+
+ // replacing spaces with "hard spaces" to prevent breaking the number
+ const formattedContactMethod = Str.isSMSLogin(contactMethod)
+ ? this.props.formatPhoneNumber(contactMethod).replace(/ /g, '\u00A0')
+ : contactMethod;
+
const loginData = this.props.loginList[contactMethod];
if (!contactMethod || !loginData) {
return ;
@@ -169,7 +175,7 @@ class ContactMethodDetailsPage extends Component {
return (
Navigation.navigate(ROUTES.SETTINGS_CONTACT_METHODS)}
onCloseButtonPress={() => Navigation.dismissModal(true)}
@@ -191,7 +197,7 @@ class ContactMethodDetailsPage extends Component {
- {this.props.translate('contacts.enterMagicCode', {contactMethod})}
+ {this.props.translate('contacts.enterMagicCode', {contactMethod: formattedContactMethod})}
diff --git a/src/pages/settings/Profile/Contacts/ContactMethodsPage.js b/src/pages/settings/Profile/Contacts/ContactMethodsPage.js
index 7d8e362a438e..ce7711e58de2 100644
--- a/src/pages/settings/Profile/Contacts/ContactMethodsPage.js
+++ b/src/pages/settings/Profile/Contacts/ContactMethodsPage.js
@@ -83,13 +83,15 @@ const ContactMethodsPage = (props) => {
// Default to using login key if we deleted login.partnerUserID optimistically
// but still need to show the pending login being deleted while offline.
const partnerUserID = login.partnerUserID || loginName;
+ const menuItemTitle = Str.isSMSLogin(partnerUserID) ? props.formatPhoneNumber(partnerUserID) : partnerUserID;
+
return (
Navigation.navigate(ROUTES.getEditContactMethodRoute(partnerUserID))}
brickRoadIndicator={indicator}
diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js
index 3ea4f9abb9f7..88890bcd0f99 100755
--- a/src/pages/settings/Profile/ProfilePage.js
+++ b/src/pages/settings/Profile/ProfilePage.js
@@ -1,4 +1,3 @@
-import Str from 'expensify-common/lib/str';
import lodashGet from 'lodash/get';
import React from 'react';
import {View} from 'react-native';
@@ -65,7 +64,7 @@ const ProfilePage = (props) => {
},
{
description: props.translate('contacts.contactMethod'),
- title: Str.removeSMSDomain(lodashGet(currentUserDetails, 'login', '')),
+ title: props.formatPhoneNumber(lodashGet(currentUserDetails, 'login', '')),
pageRoute: ROUTES.SETTINGS_CONTACT_METHODS,
brickRoadIndicator: contactMethodBrickRoadIndicator,
},
diff --git a/src/pages/settings/Security/CloseAccountPage.js b/src/pages/settings/Security/CloseAccountPage.js
index 56a5c27e12a0..b80ab2687bac 100644
--- a/src/pages/settings/Security/CloseAccountPage.js
+++ b/src/pages/settings/Security/CloseAccountPage.js
@@ -85,7 +85,7 @@ class CloseAccountPage extends Component {
}
render() {
- const userEmailOrPhone = Str.removeSMSDomain(this.props.session.email);
+ const userEmailOrPhone = this.props.formatPhoneNumber(this.props.session.email);
return (
(
{!_.isEmpty(props.credentials.login) && (
- {props.translate('loginForm.notYou', {user: Str.removeSMSDomain(props.credentials.login)})}
+ {props.translate('loginForm.notYou', {user: props.formatPhoneNumber(props.credentials.login)})}
)}
{
const isSMSLogin = Str.isSMSLogin(props.credentials.login);
- const login = isSMSLogin ? props.toLocalPhone(Str.removeSMSDomain(props.credentials.login)) : props.credentials.login;
+
+ // replacing spaces with "hard spaces" to prevent breaking the number
+ const login = isSMSLogin
+ ? props.formatPhoneNumber(props.credentials.login).replace(/ /g, '\u00A0')
+ : props.credentials.login;
+
const loginType = (isSMSLogin ? props.translate('common.phone') : props.translate('common.email')).toLowerCase();
return (
diff --git a/src/pages/signin/SignInPage.js b/src/pages/signin/SignInPage.js
index f83f467106ee..3282e16d50b2 100644
--- a/src/pages/signin/SignInPage.js
+++ b/src/pages/signin/SignInPage.js
@@ -102,16 +102,18 @@ class SignInPage extends Component {
} else {
const userLogin = Str.removeSMSDomain(lodashGet(this.props, 'credentials.login', ''));
+ // replacing spaces with "hard spaces" to prevent breaking the number
+ const userLoginToDisplay = Str.isSMSLogin(userLogin) ? this.props.formatPhoneNumber(userLogin).replace(/ /g, '\u00A0') : userLogin;
if (this.props.account.validated) {
welcomeHeader = this.props.isSmallScreenWidth ? '' : this.props.translate('welcomeText.welcomeBack');
welcomeText = this.props.isSmallScreenWidth
- ? `${this.props.translate('welcomeText.welcomeBack')} ${this.props.translate('welcomeText.welcomeEnterMagicCode', {login: userLogin})}`
- : this.props.translate('welcomeText.welcomeEnterMagicCode', {login: userLogin});
+ ? `${this.props.translate('welcomeText.welcomeBack')} ${this.props.translate('welcomeText.welcomeEnterMagicCode', {login: userLoginToDisplay})}`
+ : this.props.translate('welcomeText.welcomeEnterMagicCode', {login: userLoginToDisplay});
} else {
welcomeHeader = this.props.isSmallScreenWidth ? '' : this.props.translate('welcomeText.welcome');
welcomeText = this.props.isSmallScreenWidth
- ? `${this.props.translate('welcomeText.welcome')} ${this.props.translate('welcomeText.newFaceEnterMagicCode', {login: userLogin})}`
- : this.props.translate('welcomeText.newFaceEnterMagicCode', {login: userLogin});
+ ? `${this.props.translate('welcomeText.welcome')} ${this.props.translate('welcomeText.newFaceEnterMagicCode', {login: userLoginToDisplay})}`
+ : this.props.translate('welcomeText.newFaceEnterMagicCode', {login: userLoginToDisplay});
}
}
} else if (showPasswordForm) {
diff --git a/src/pages/signin/SignInPageHero.js b/src/pages/signin/SignInPageHero.js
index 36edb388283a..633a709758d1 100644
--- a/src/pages/signin/SignInPageHero.js
+++ b/src/pages/signin/SignInPageHero.js
@@ -17,7 +17,7 @@ const SignInPageHero = props => (
StyleUtils.getMinimumHeight(variables.signInContentMinHeight),
props.windowWidth <= variables.tabletResponsiveWidthBreakpoint ? styles.flexColumn : styles.flexColumn,
styles.pt20,
- StyleUtils.getMaximumWidth(variables.signInContextMaxWidth),
+ StyleUtils.getMaximumWidth(variables.signInHeroContextMaxWidth),
styles.alignSelfCenter,
]}
>
diff --git a/src/pages/workspace/WorkspaceMembersPage.js b/src/pages/workspace/WorkspaceMembersPage.js
index a10e39b03293..132b848af0e5 100644
--- a/src/pages/workspace/WorkspaceMembersPage.js
+++ b/src/pages/workspace/WorkspaceMembersPage.js
@@ -6,7 +6,6 @@ import {
} from 'react-native';
import PropTypes from 'prop-types';
import {withOnyx} from 'react-native-onyx';
-import Str from 'expensify-common/lib/str';
import styles from '../../styles/styles';
import ONYXKEYS from '../../ONYXKEYS';
import HeaderWithCloseButton from '../../components/HeaderWithCloseButton';
@@ -289,8 +288,8 @@ class WorkspaceMembersPage extends React.Component {
onSelectRow={() => this.toggleUser(item.login, item.pendingAction)}
boldStyle
option={{
- text: Str.removeSMSDomain(item.displayName),
- alternateText: Str.removeSMSDomain(item.login),
+ text: this.props.formatPhoneNumber(item.displayName),
+ alternateText: this.props.formatPhoneNumber(item.login),
participantsList: [item],
icons: [{
source: ReportUtils.getAvatar(item.avatar, item.login),
@@ -409,7 +408,7 @@ class WorkspaceMembersPage extends React.Component {
{data.length > 0 ? (
@@ -439,7 +438,7 @@ class WorkspaceMembersPage extends React.Component {
) : (
- {this.props.translate('common.noResultsFound')}
+ {this.props.translate('workspace.common.memberNotFound')}
)}
diff --git a/src/pages/workspace/bills/WorkspaceBillsFirstSection.js b/src/pages/workspace/bills/WorkspaceBillsFirstSection.js
index 15397ea96776..f97b6ba946ae 100644
--- a/src/pages/workspace/bills/WorkspaceBillsFirstSection.js
+++ b/src/pages/workspace/bills/WorkspaceBillsFirstSection.js
@@ -42,7 +42,6 @@ const defaultProps = {
const WorkspaceBillsFirstSection = (props) => {
const emailDomain = Str.extractEmailDomain(props.session.email);
- const manageYourBillsUrl = `reports?policyID=${props.policyID}&from=all&type=bill&showStates=Open,Processing,Approved,Reimbursed,Archived&isAdvancedFilterMode=true`;
return (
{
{
title: props.translate('workspace.bills.viewAllBills'),
onPress: () => (
- Link.openOldDotLink(manageYourBillsUrl)
+ Link.openOldDotLink(`reports?policyID=${props.policyID}&from=all&type=bill&showStates=Open,Processing,Approved,Reimbursed,Archived&isAdvancedFilterMode=true`)
),
icon: Expensicons.Bill,
shouldShowRightIcon: true,
iconRight: Expensicons.NewWindow,
wrapperStyle: [styles.cardMenuItem],
- link: () => Link.buildOldDotURL(manageYourBillsUrl),
},
]}
containerStyles={[styles.cardSection]}
diff --git a/src/pages/workspace/bills/WorkspaceBillsVBAView.js b/src/pages/workspace/bills/WorkspaceBillsVBAView.js
index dcb625777c7d..3731a9cc7b8f 100644
--- a/src/pages/workspace/bills/WorkspaceBillsVBAView.js
+++ b/src/pages/workspace/bills/WorkspaceBillsVBAView.js
@@ -17,35 +17,30 @@ const propTypes = {
...withLocalizePropTypes,
};
-const WorkspaceBillsVBAView = (props) => {
- const reportsUrl = `reports?policyID=${props.policyID}&from=all&type=bill&showStates=Processing,Approved&isAdvancedFilterMode=true`;
+const WorkspaceBillsVBAView = props => (
+ <>
+
- return (
- <>
-
-
- Link.openOldDotLink(reportsUrl),
- icon: Expensicons.Bill,
- shouldShowRightIcon: true,
- iconRight: Expensicons.NewWindow,
- wrapperStyle: [styles.cardMenuItem],
- link: () => Link.buildOldDotURL(reportsUrl),
- },
- ]}
- >
-
- {props.translate('workspace.bills.VBACopy')}
-
-
- >
- );
-};
+ Link.openOldDotLink(`reports?policyID=${props.policyID}&from=all&type=bill&showStates=Processing,Approved&isAdvancedFilterMode=true`),
+ icon: Expensicons.Bill,
+ shouldShowRightIcon: true,
+ iconRight: Expensicons.NewWindow,
+ wrapperStyle: [styles.cardMenuItem],
+ },
+ ]}
+ >
+
+ {props.translate('workspace.bills.VBACopy')}
+
+
+ >
+);
WorkspaceBillsVBAView.propTypes = propTypes;
WorkspaceBillsVBAView.displayName = 'WorkspaceBillsVBAView';
diff --git a/src/pages/workspace/card/WorkspaceCardVBAWithECardView.js b/src/pages/workspace/card/WorkspaceCardVBAWithECardView.js
index 4c1213800bac..a9a551f23131 100644
--- a/src/pages/workspace/card/WorkspaceCardVBAWithECardView.js
+++ b/src/pages/workspace/card/WorkspaceCardVBAWithECardView.js
@@ -13,40 +13,31 @@ const propTypes = {
...withLocalizePropTypes,
};
-const MENU_LINKS = {
- ISSUE_AND_MANAGE_CARDS: 'domain_companycards',
- RECONCILE_CARDS: encodeURI('domain_companycards?param={"section":"cardReconciliation"}'),
- SETTLEMENT_FREQUENCY: encodeURI('domain_companycards?param={"section":"configureSettings"}'),
-};
-
const WorkspaceCardVBAWithECardView = (props) => {
const menuItems = [
{
title: props.translate('workspace.common.issueAndManageCards'),
- onPress: () => Link.openOldDotLink(MENU_LINKS.ISSUE_AND_MANAGE_CARDS),
+ onPress: () => Link.openOldDotLink('domain_companycards'),
icon: Expensicons.ExpensifyCard,
shouldShowRightIcon: true,
iconRight: Expensicons.NewWindow,
wrapperStyle: [styles.cardMenuItem],
- link: () => Link.buildOldDotURL(MENU_LINKS.ISSUE_AND_MANAGE_CARDS),
},
{
title: props.translate('workspace.common.reconcileCards'),
- onPress: () => Link.openOldDotLink(MENU_LINKS.RECONCILE_CARDS),
+ onPress: () => Link.openOldDotLink(encodeURI('domain_companycards?param={"section":"cardReconciliation"}')),
icon: Expensicons.ReceiptSearch,
shouldShowRightIcon: true,
iconRight: Expensicons.NewWindow,
wrapperStyle: [styles.cardMenuItem],
- link: () => Link.buildOldDotURL(MENU_LINKS.RECONCILE_CARDS),
},
{
title: props.translate('workspace.common.settlementFrequency'),
- onPress: () => Link.openOldDotLink(MENU_LINKS.SETTLEMENT_FREQUENCY),
+ onPress: () => Link.openOldDotLink(encodeURI('domain_companycards?param={"section":"configureSettings"}')),
icon: Expensicons.Gear,
shouldShowRightIcon: true,
iconRight: Expensicons.NewWindow,
wrapperStyle: [styles.cardMenuItem],
- link: () => Link.buildOldDotURL(MENU_LINKS.SETTLEMENT_FREQUENCY),
},
];
diff --git a/src/pages/workspace/invoices/WorkspaceInvoicesFirstSection.js b/src/pages/workspace/invoices/WorkspaceInvoicesFirstSection.js
index 9f3c84d28ff1..4d027542b155 100644
--- a/src/pages/workspace/invoices/WorkspaceInvoicesFirstSection.js
+++ b/src/pages/workspace/invoices/WorkspaceInvoicesFirstSection.js
@@ -16,46 +16,39 @@ const propTypes = {
...withLocalizePropTypes,
};
-const WorkspaceInvoicesFirstSection = (props) => {
- const sendInvoiceUrl = encodeURI('reports?param={"createInvoice":true}');
- const viewAllInvoicesUrl = `reports?policyID=${props.policyID}&from=all&type=invoice&showStates=Open,Processing,Approved,Reimbursed,Archived&isAdvancedFilterMode=true`;
-
- return (
- Link.openOldDotLink(sendInvoiceUrl),
- icon: Expensicons.Send,
- shouldShowRightIcon: true,
- iconRight: Expensicons.NewWindow,
- wrapperStyle: [styles.cardMenuItem],
- link: () => Link.buildOldDotURL(sendInvoiceUrl),
- },
- {
- title: props.translate('workspace.invoices.viewAllInvoices'),
- onPress: () => (
- Link.openOldDotLink(viewAllInvoicesUrl)
- ),
- icon: Expensicons.Invoice,
- shouldShowRightIcon: true,
- iconRight: Expensicons.NewWindow,
- wrapperStyle: [styles.cardMenuItem],
- link: () => Link.buildOldDotURL(viewAllInvoicesUrl),
- },
- ]}
- containerStyles={[styles.cardSection]}
- >
-
-
- {props.translate('workspace.invoices.invoiceFirstSectionCopy')}
-
-
-
- );
-};
+const WorkspaceInvoicesFirstSection = props => (
+ Link.openOldDotLink(encodeURI('reports?param={"createInvoice":true}')),
+ icon: Expensicons.Send,
+ shouldShowRightIcon: true,
+ iconRight: Expensicons.NewWindow,
+ wrapperStyle: [styles.cardMenuItem],
+ },
+ {
+ title: props.translate('workspace.invoices.viewAllInvoices'),
+ onPress: () => (
+ Link.openOldDotLink(`reports?policyID=${props.policyID}&from=all&type=invoice&showStates=Open,Processing,Approved,Reimbursed,Archived&isAdvancedFilterMode=true`)
+ ),
+ icon: Expensicons.Invoice,
+ shouldShowRightIcon: true,
+ iconRight: Expensicons.NewWindow,
+ wrapperStyle: [styles.cardMenuItem],
+ },
+ ]}
+ containerStyles={[styles.cardSection]}
+ >
+
+
+ {props.translate('workspace.invoices.invoiceFirstSectionCopy')}
+
+
+
+);
WorkspaceInvoicesFirstSection.propTypes = propTypes;
WorkspaceInvoicesFirstSection.displayName = 'WorkspaceInvoicesFirstSection';
diff --git a/src/pages/workspace/invoices/WorkspaceInvoicesVBAView.js b/src/pages/workspace/invoices/WorkspaceInvoicesVBAView.js
index c4f50bb9d49d..c88a61eef18b 100644
--- a/src/pages/workspace/invoices/WorkspaceInvoicesVBAView.js
+++ b/src/pages/workspace/invoices/WorkspaceInvoicesVBAView.js
@@ -17,35 +17,30 @@ const propTypes = {
...withLocalizePropTypes,
};
-const WorkspaceInvoicesVBAView = (props) => {
- const viewUnpaidInvoicesUrl = `reports?policyID=${props.policyID}&from=all&type=invoice&showStates=Processing&isAdvancedFilterMode=true`;
+const WorkspaceInvoicesVBAView = props => (
+ <>
+
- return (
- <>
-
-
- Link.openOldDotLink(viewUnpaidInvoicesUrl),
- icon: Expensicons.Hourglass,
- shouldShowRightIcon: true,
- iconRight: Expensicons.NewWindow,
- wrapperStyle: [styles.cardMenuItem],
- link: () => Link.buildOldDotURL(viewUnpaidInvoicesUrl),
- },
- ]}
- >
-
- {props.translate('workspace.invoices.unlockVBACopy')}
-
-
- >
- );
-};
+ Link.openOldDotLink(`reports?policyID=${props.policyID}&from=all&type=invoice&showStates=Processing&isAdvancedFilterMode=true`),
+ icon: Expensicons.Hourglass,
+ shouldShowRightIcon: true,
+ iconRight: Expensicons.NewWindow,
+ wrapperStyle: [styles.cardMenuItem],
+ },
+ ]}
+ >
+
+ {props.translate('workspace.invoices.unlockVBACopy')}
+
+
+ >
+);
WorkspaceInvoicesVBAView.propTypes = propTypes;
WorkspaceInvoicesVBAView.displayName = 'WorkspaceInvoicesVBAView';
diff --git a/src/pages/workspace/reimburse/WorkspaceReimburseSection.js b/src/pages/workspace/reimburse/WorkspaceReimburseSection.js
index 3928816fb686..20f443bf5671 100644
--- a/src/pages/workspace/reimburse/WorkspaceReimburseSection.js
+++ b/src/pages/workspace/reimburse/WorkspaceReimburseSection.js
@@ -57,7 +57,6 @@ class WorkspaceReimburseSection extends React.Component {
render() {
const achState = lodashGet(this.props.reimbursementAccount, 'achData.state', '');
const hasVBA = achState === BankAccount.STATE.OPEN;
- const reimburseReceiptsUrl = `reports?policyID=${this.props.policy.id}&from=all&type=expense&showStates=Archived&isAdvancedFilterMode=true`;
if (this.props.network.isOffline) {
return (
@@ -94,12 +93,11 @@ class WorkspaceReimburseSection extends React.Component {
menuItems={[
{
title: this.props.translate('workspace.reimburse.reimburseReceipts'),
- onPress: () => Link.openOldDotLink(reimburseReceiptsUrl),
+ onPress: () => Link.openOldDotLink(`reports?policyID=${this.props.policy.id}&from=all&type=expense&showStates=Archived&isAdvancedFilterMode=true`),
icon: Expensicons.Bank,
shouldShowRightIcon: true,
iconRight: Expensicons.NewWindow,
wrapperStyle: [styles.cardMenuItem],
- link: () => Link.buildOldDotURL(reimburseReceiptsUrl),
},
]}
>
diff --git a/src/pages/workspace/reimburse/WorkspaceReimburseView.js b/src/pages/workspace/reimburse/WorkspaceReimburseView.js
index ccb40d053e7f..d103121b9e2a 100644
--- a/src/pages/workspace/reimburse/WorkspaceReimburseView.js
+++ b/src/pages/workspace/reimburse/WorkspaceReimburseView.js
@@ -214,8 +214,6 @@ class WorkspaceReimburseView extends React.Component {
}
render() {
- const viewAllReceiptsUrl = `expenses?policyIDList=${this.props.policy.id}&billableReimbursable=reimbursable&submitterEmail=%2B%2B`;
-
return (
<>
Link.openOldDotLink(viewAllReceiptsUrl),
+ onPress: () => Link.openOldDotLink(`expenses?policyIDList=${this.props.policy.id}&billableReimbursable=reimbursable&submitterEmail=%2B%2B`),
icon: Expensicons.Receipt,
shouldShowRightIcon: true,
iconRight: Expensicons.NewWindow,
wrapperStyle: [styles.cardMenuItem],
- link: () => Link.buildOldDotURL(viewAllReceiptsUrl),
},
]}
>
diff --git a/src/pages/workspace/travel/WorkspaceTravelVBAView.js b/src/pages/workspace/travel/WorkspaceTravelVBAView.js
index 79b256136028..9c79b55c90b4 100644
--- a/src/pages/workspace/travel/WorkspaceTravelVBAView.js
+++ b/src/pages/workspace/travel/WorkspaceTravelVBAView.js
@@ -8,7 +8,6 @@ import * as Illustrations from '../../../components/Icon/Illustrations';
import Section from '../../../components/Section';
import * as Link from '../../../libs/actions/Link';
import * as Report from '../../../libs/actions/Report';
-import CONST from '../../../CONST';
const propTypes = {
...withLocalizePropTypes,
@@ -26,7 +25,6 @@ const WorkspaceTravelVBAView = props => (
shouldShowRightIcon: true,
iconRight: Expensicons.NewWindow,
wrapperStyle: [styles.cardMenuItem],
- link: () => Link.buildOldDotURL('domain_companycards'),
},
{
title: props.translate('workspace.travel.bookTravelWithConcierge'),
@@ -39,12 +37,11 @@ const WorkspaceTravelVBAView = props => (
},
{
title: props.translate('requestorStep.learnMore'),
- onPress: () => Link.openExternalLink(CONST.CONCIERGE_TRAVEL_URL),
+ onPress: () => Link.openExternalLink('https://community.expensify.com/discussion/7066/introducing-concierge-travel'),
icon: Expensicons.Info,
shouldShowRightIcon: true,
iconRight: Expensicons.NewWindow,
wrapperStyle: [styles.cardMenuItem],
- link: CONST.CONCIERGE_TRAVEL_URL,
},
]}
>
diff --git a/src/styles/StyleUtils.js b/src/styles/StyleUtils.js
index f614ab69185d..f78e56839e5b 100644
--- a/src/styles/StyleUtils.js
+++ b/src/styles/StyleUtils.js
@@ -440,15 +440,19 @@ function getModalPaddingStyles({
modalContainerStyleMarginBottom,
modalContainerStylePaddingTop,
modalContainerStylePaddingBottom,
+ insets,
}) {
+ // use fallback value for safeAreaPaddingBottom to keep padding bottom consistent with padding top.
+ // More info: issue #17376
+ const safeAreaPaddingBottomWithFallback = insets.bottom === 0 ? (modalContainerStylePaddingTop || 0) : safeAreaPaddingBottom;
return {
marginTop: (modalContainerStyleMarginTop || 0) + (shouldAddTopSafeAreaMargin ? safeAreaPaddingTop : 0),
- marginBottom: (modalContainerStyleMarginBottom || 0) + (shouldAddBottomSafeAreaMargin ? safeAreaPaddingBottom : 0),
+ marginBottom: (modalContainerStyleMarginBottom || 0) + (shouldAddBottomSafeAreaMargin ? safeAreaPaddingBottomWithFallback : 0),
paddingTop: shouldAddTopSafeAreaPadding
? (modalContainerStylePaddingTop || 0) + safeAreaPaddingTop
: modalContainerStylePaddingTop || 0,
paddingBottom: shouldAddBottomSafeAreaPadding
- ? (modalContainerStylePaddingBottom || 0) + safeAreaPaddingBottom
+ ? (modalContainerStylePaddingBottom || 0) + safeAreaPaddingBottomWithFallback
: modalContainerStylePaddingBottom || 0,
paddingLeft: safeAreaPaddingLeft || 0,
paddingRight: safeAreaPaddingRight || 0,
diff --git a/src/styles/stylePropTypes.js b/src/styles/stylePropTypes.js
index edc5d0383a75..4c7e825a8848 100644
--- a/src/styles/stylePropTypes.js
+++ b/src/styles/stylePropTypes.js
@@ -3,5 +3,4 @@ import PropTypes from 'prop-types';
export default PropTypes.oneOfType([
PropTypes.object,
PropTypes.arrayOf(PropTypes.object),
- PropTypes.func,
]);
diff --git a/src/styles/variables.js b/src/styles/variables.js
index b97aa3e0e1bc..97fb157020fc 100644
--- a/src/styles/variables.js
+++ b/src/styles/variables.js
@@ -118,7 +118,7 @@ export default {
signInHeroBackgroundWidth: 2000,
signInHeroBackgroundWidthMobile: 800,
signInContentMaxWidth: 1360,
- signInHeroContextMaxWidth: 740,
+ signInHeroContextMaxWidth: 680,
signInContentMinHeight: 800,
signInLogoHeightSmallScreen: 28,
signInLogoHeight: 34,
diff --git a/tests/unit/IOUUtilsTest.js b/tests/unit/IOUUtilsTest.js
index 989b33a42359..4ae4f3c5bc4b 100644
--- a/tests/unit/IOUUtilsTest.js
+++ b/tests/unit/IOUUtilsTest.js
@@ -1,6 +1,10 @@
+import Onyx from 'react-native-onyx';
import CONST from '../../src/CONST';
import * as IOUUtils from '../../src/libs/IOUUtils';
import * as ReportUtils from '../../src/libs/ReportUtils';
+import ONYXKEYS from '../../src/ONYXKEYS';
+import waitForPromisesToResolve from '../utils/waitForPromisesToResolve';
+import currencyList from './currencyList.json';
let iouReport;
let reportActions;
@@ -38,27 +42,37 @@ function cancelMoneyRequest(moneyRequestAction, {isOnline} = {}) {
);
}
-beforeEach(() => {
- reportActions = [];
- const chatReportID = ReportUtils.generateReportID();
- const amount = 1000;
- const currency = 'USD';
-
- iouReport = ReportUtils.buildOptimisticIOUReport(
- ownerEmail,
- managerEmail,
- amount,
- chatReportID,
- currency,
- CONST.LOCALES.EN,
- );
-
- // The starting point of all tests is the IOUReport containing a single non-pending transaction in USD
- // All requests in the tests are assumed to be offline, unless isOnline is specified
- createIOUReportAction('create', amount, currency, {IOUTransactionID: '', isOnline: true});
-});
+function initCurrencyList() {
+ Onyx.init({
+ keys: ONYXKEYS,
+ initialKeyStates: {
+ [ONYXKEYS.CURRENCY_LIST]: currencyList,
+ },
+ });
+ return waitForPromisesToResolve();
+}
describe('isIOUReportPendingCurrencyConversion', () => {
+ beforeEach(() => {
+ reportActions = [];
+ const chatReportID = ReportUtils.generateReportID();
+ const amount = 1000;
+ const currency = 'USD';
+
+ iouReport = ReportUtils.buildOptimisticIOUReport(
+ ownerEmail,
+ managerEmail,
+ amount,
+ chatReportID,
+ currency,
+ CONST.LOCALES.EN,
+ );
+
+ // The starting point of all tests is the IOUReport containing a single non-pending transaction in USD
+ // All requests in the tests are assumed to be offline, unless isOnline is specified
+ createIOUReportAction('create', amount, currency, {IOUTransactionID: '', isOnline: true});
+ });
+
test('Requesting money offline in a different currency will show the pending conversion message', () => {
// Request money offline in AED
createIOUReportAction('create', 100, 'AED');
@@ -132,3 +146,57 @@ describe('isIOUReportPendingCurrencyConversion', () => {
});
});
+describe('getCurrencyDecimals', () => {
+ beforeAll(() => initCurrencyList());
+ test('Currency decimals smaller than or equal 2', () => {
+ expect(IOUUtils.getCurrencyDecimals('JPY')).toBe(0);
+ expect(IOUUtils.getCurrencyDecimals('USD')).toBe(2);
+ });
+
+ test('Currency decimals larger than 2 should return 2', () => {
+ // Actual: 3
+ expect(IOUUtils.getCurrencyDecimals('LYD')).toBe(2);
+
+ // Actual: 4
+ expect(IOUUtils.getCurrencyDecimals('UYW')).toBe(2);
+ });
+});
+
+describe('getCurrencyUnit', () => {
+ beforeAll(() => initCurrencyList());
+ test('Currency with decimals smaller than or equal 2', () => {
+ expect(IOUUtils.getCurrencyUnit('JPY')).toBe(1);
+ expect(IOUUtils.getCurrencyUnit('USD')).toBe(100);
+ });
+
+ test('Currency with decimals larger than 2 should be floor to 2', () => {
+ expect(IOUUtils.getCurrencyUnit('LYD')).toBe(100);
+ });
+});
+
+describe('calculateAmount', () => {
+ beforeAll(() => initCurrencyList());
+ test('103 JPY split among 3 participants including the default user should be [35, 34, 34]', () => {
+ const participants = ['tonystark@expensify.com', 'reedrichards@expensify.com'];
+ expect(IOUUtils.calculateAmount(participants, 103, 'JPY', true)).toBe(3500);
+ expect(IOUUtils.calculateAmount(participants, 103, 'JPY')).toBe(3400);
+ });
+
+ test('10 AFN split among 4 participants including the default user should be [1, 3, 3, 3]', () => {
+ const participants = ['tonystark@expensify.com', 'reedrichards@expensify.com', 'suestorm@expensify.com'];
+ expect(IOUUtils.calculateAmount(participants, 10, 'AFN', true)).toBe(100);
+ expect(IOUUtils.calculateAmount(participants, 10, 'AFN')).toBe(300);
+ });
+
+ test('10 BHD split among 3 participants including the default user should be [334, 333, 333]', () => {
+ const participants = ['tonystark@expensify.com', 'reedrichards@expensify.com'];
+ expect(IOUUtils.calculateAmount(participants, 10, 'BHD', true)).toBe(334);
+ expect(IOUUtils.calculateAmount(participants, 10, 'BHD')).toBe(333);
+ });
+
+ test('0.02 USD split among 4 participants including the default user should be [-1, 1, 1, 1]', () => {
+ const participants = ['tonystark@expensify.com', 'reedrichards@expensify.com', 'suestorm@expensify.com'];
+ expect(IOUUtils.calculateAmount(participants, 0.02, 'USD', true)).toBe(-1);
+ expect(IOUUtils.calculateAmount(participants, 0.02, 'USD')).toBe(1);
+ });
+});
diff --git a/tests/unit/LocalePhoneNumberTest.js b/tests/unit/LocalePhoneNumberTest.js
index bb2c250205ad..40d6bcc7bf69 100644
--- a/tests/unit/LocalePhoneNumberTest.js
+++ b/tests/unit/LocalePhoneNumberTest.js
@@ -1,89 +1,49 @@
-const localePhoneNumber = require('../../src/libs/LocalePhoneNumber');
-const CONST = require('../../src/CONST').default;
+import Onyx from 'react-native-onyx';
+import ONYXKEYS from '../../src/ONYXKEYS';
+import * as LocalePhoneNumber from '../../src/libs/LocalePhoneNumber';
+import waitForPromisesToResolve from '../utils/waitForPromisesToResolve';
-describe('localePhoneNumber', () => {
- it('Test to local Number Conversion by locale', () => {
- expect(localePhoneNumber.toLocalPhone(CONST.LOCALES.ES_ES, '34547474747474')).toBe('547474747474');
- });
-
- // Failing due to the use of Trimstart.
- it('Test to local Number Conversion by locale', () => {
- expect(localePhoneNumber.toLocalPhone(CONST.LOCALES.ES_ES, '343434343434')).toBe('3434343434');
- });
-
- it('Test to local Number Conversion by locale', () => {
- expect(localePhoneNumber.toLocalPhone(CONST.LOCALES.ES_ES, '+34547474747474')).toBe('547474747474');
- });
-
- it('Test to local Number Conversion by locale', () => {
- expect(localePhoneNumber.toLocalPhone(CONST.LOCALES.ES_ES, '547474747474')).toBe('547474747474');
- });
-
- it('Test to local Number Conversion by locale', () => {
- expect(localePhoneNumber.toLocalPhone(CONST.LOCALES.ES_ES, '+17474747474')).toBe('+17474747474');
- });
-
- it('Test to local Number Conversion by locale', () => {
- expect(localePhoneNumber.toLocalPhone(CONST.LOCALES.EN, '+1547474747474')).toBe('547474747474');
- });
-
- it('Test to local Number Conversion by locale', () => {
- expect(localePhoneNumber.toLocalPhone(CONST.LOCALES.EN, '1547474747474')).toBe('547474747474');
- });
-
- it('Test to local Number Conversion by locale', () => {
- expect(localePhoneNumber.toLocalPhone(CONST.LOCALES.EN, '547474747474')).toBe('547474747474');
- });
+const ES_NUMBER = '+34702474537';
+const US_NUMBER = '+18332403627';
- it('Test to local Number Conversion by locale', () => {
- expect(localePhoneNumber.toLocalPhone(CONST.LOCALES.EN, '+347474747474')).toBe('+347474747474');
- });
-
- it('Test to local Number Conversion by locale', () => {
- expect(localePhoneNumber.toLocalPhone(CONST.LOCALES.EN, '+34 747 474 7474')).toBe('+34 747 474 7474');
- });
+describe('LocalePhoneNumber utils', () => {
+ beforeAll(() => Onyx.init({
+ keys: ONYXKEYS,
+ }));
- it('Test to local Number Conversion by locale', () => {
- expect(localePhoneNumber.toLocalPhone('en-EN', '+17474747474')).toBe('+17474747474');
- });
+ describe('formatPhoneNumber function - when the current user has a phone number', () => {
+ beforeEach(() => Onyx.multiSet({
+ [ONYXKEYS.SESSION]: {email: 'current@user.com'},
+ [ONYXKEYS.COUNTRY_CODE]: 34,
+ [ONYXKEYS.PERSONAL_DETAILS]: {'current@user.com': {phoneNumber: US_NUMBER}},
+ }).then(waitForPromisesToResolve));
- it('Test to international Number Conversion by locale', () => {
- expect(localePhoneNumber.fromLocalPhone(CONST.LOCALES.ES_ES, '34547474747474')).toBe('+34547474747474');
- });
+ afterEach(() => Onyx.clear());
- it('Test to international Number Conversion by locale', () => {
- expect(localePhoneNumber.fromLocalPhone(CONST.LOCALES.ES_ES, '+34547474747474')).toBe('+34547474747474');
- });
+ it('should display a number from the same region formatted locally', () => {
+ expect(LocalePhoneNumber.formatPhoneNumber(US_NUMBER)).toBe('(833) 240-3627');
+ });
- it('Test to international Number Conversion by locale', () => {
- expect(localePhoneNumber.fromLocalPhone(CONST.LOCALES.ES_ES, '547474747474')).toBe('+34547474747474');
+ it('should display a number from another region formatted internationally', () => {
+ expect(LocalePhoneNumber.formatPhoneNumber(ES_NUMBER)).toBe('+34 702 47 45 37');
+ });
});
- it('Test to international Number Conversion by locale', () => {
- expect(localePhoneNumber.fromLocalPhone(CONST.LOCALES.ES_ES, '+17474747474')).toBe('+3417474747474');
- });
+ describe('formatPhoneNumber function - when the current user does not have a phone number', () => {
+ beforeEach(() => Onyx.multiSet({
+ [ONYXKEYS.SESSION]: {email: 'current@user.com'},
+ [ONYXKEYS.COUNTRY_CODE]: 34,
+ [ONYXKEYS.PERSONAL_DETAILS]: {'current@user.com': {phoneNumber: ''}},
+ }).then(waitForPromisesToResolve));
- it('Test to international Number Conversion by locale', () => {
- expect(localePhoneNumber.fromLocalPhone(CONST.LOCALES.EN, '+1547474747474')).toBe('+1547474747474');
- });
+ afterEach(() => Onyx.clear());
- it('Test to international Number Conversion by locale', () => {
- expect(localePhoneNumber.fromLocalPhone(CONST.LOCALES.EN, '1547474747474')).toBe('+1547474747474');
- });
-
- it('Test to international Number Conversion by locale', () => {
- expect(localePhoneNumber.fromLocalPhone(CONST.LOCALES.EN, '547474747474')).toBe('+1547474747474');
- });
-
- it('Test to international Number Conversion by locale', () => {
- expect(localePhoneNumber.fromLocalPhone(CONST.LOCALES.EN, '+347474747474')).toBe('+1347474747474');
- });
-
- it('Test to international Number Conversion by locale', () => {
- expect(localePhoneNumber.fromLocalPhone(CONST.LOCALES.EN, ' + 34 747 474 7474 ')).toBe('+1 34 747 474 7474');
- });
+ it('should display a number from the same region formatted locally', () => {
+ expect(LocalePhoneNumber.formatPhoneNumber(ES_NUMBER)).toBe('702 47 45 37');
+ });
- it('Test to international Number Conversion by locale', () => {
- expect(localePhoneNumber.fromLocalPhone('en-EN', '+17474747474')).toBe('+17474747474');
+ it('should display a number from another region formatted internationally', () => {
+ expect(LocalePhoneNumber.formatPhoneNumber(US_NUMBER)).toBe('+1 833-240-3627');
+ });
});
});
diff --git a/tests/unit/ReportUtilsTest.js b/tests/unit/ReportUtilsTest.js
index c9fcca5a6a7d..d610b740135f 100644
--- a/tests/unit/ReportUtilsTest.js
+++ b/tests/unit/ReportUtilsTest.js
@@ -25,8 +25,8 @@ const participantsPersonalDetails = {
login: 'lagertha@vikings.net',
pronouns: 'She/her',
},
- '+12223334444@expensify.sms': {
- login: '+12223334444@expensify.sms',
+ '+18332403627@expensify.sms': {
+ login: '+18332403627@expensify.sms',
},
};
const policy = {
@@ -64,8 +64,8 @@ describe('ReportUtils', () => {
pronouns: 'She/her',
},
{
- displayName: '2223334444',
- tooltip: '+12223334444',
+ displayName: '(833) 240-3627',
+ tooltip: '+18332403627',
pronouns: undefined,
},
]);
@@ -89,8 +89,8 @@ describe('ReportUtils', () => {
pronouns: 'She/her',
},
{
- displayName: '2223334444',
- tooltip: '+12223334444',
+ displayName: '(833) 240-3627',
+ tooltip: '+18332403627',
pronouns: undefined,
},
]);
@@ -113,15 +113,15 @@ describe('ReportUtils', () => {
test('SMS', () => {
expect(ReportUtils.getReportName({
- participants: [currentUserEmail, '+12223334444@expensify.sms'],
- })).toBe('2223334444');
+ participants: [currentUserEmail, '+18332403627@expensify.sms'],
+ })).toBe('(833) 240-3627');
});
});
test('Group DM', () => {
expect(ReportUtils.getReportName({
- participants: [currentUserEmail, 'ragnar@vikings.net', 'floki@vikings.net', 'lagertha@vikings.net', '+12223334444@expensify.sms'],
- })).toBe('Ragnar, floki@vikings.net, Lagertha, 2223334444');
+ participants: [currentUserEmail, 'ragnar@vikings.net', 'floki@vikings.net', 'lagertha@vikings.net', '+18332403627@expensify.sms'],
+ })).toBe('Ragnar, floki@vikings.net, Lagertha, (833) 240-3627');
});
describe('Default Policy Room', () => {
diff --git a/tests/unit/currencyList.json b/tests/unit/currencyList.json
index 740b3caf2b28..c6eda7bdd766 100644
--- a/tests/unit/currencyList.json
+++ b/tests/unit/currencyList.json
@@ -7,11 +7,13 @@
"AFN": {
"symbol": "Af",
"name": "Afghan Afghani",
+ "decimals": 0,
"ISO4217": "971"
},
"ALL": {
"symbol": "ALL",
"name": "Albanian Lek",
+ "decimals": 0,
"ISO4217": "008"
},
"AMD": {
@@ -123,6 +125,7 @@
"BYR": {
"symbol": "BR",
"name": "Belarus Ruble",
+ "decimals": 0,
"retired": true,
"retirementDate": "2016-07-01",
"ISO4217": "974"
@@ -330,11 +333,13 @@
"IQD": {
"symbol": "IQD",
"name": "Iraqi Dinar",
+ "decimals": 0,
"ISO4217": "368"
},
"IRR": {
"symbol": "﷼",
"name": "Iran Rial",
+ "decimals": 0,
"ISO4217": "364"
},
"ISK": {
@@ -377,16 +382,19 @@
"KMF": {
"symbol": "CF",
"name": "Comoros Franc",
+ "decimals": 0,
"ISO4217": "174"
},
"KPW": {
"symbol": "KP₩",
"name": "North Korean Won",
+ "decimals": 0,
"ISO4217": "408"
},
"KRW": {
"symbol": "₩",
"name": "Korean Won",
+ "decimals": 0,
"ISO4217": "410"
},
"KWD": {
@@ -407,11 +415,13 @@
"LAK": {
"symbol": "₭",
"name": "Lao Kip",
+ "decimals": 0,
"ISO4217": "418"
},
"LBP": {
"symbol": "LBP",
"name": "Lebanese Pound",
+ "decimals": 0,
"ISO4217": "422"
},
"LKR": {
@@ -460,6 +470,7 @@
"MGA": {
"symbol": "MGA",
"name": "Malagasy Ariary",
+ "decimals": 0,
"ISO4217": "969"
},
"MKD": {
@@ -470,6 +481,7 @@
"MMK": {
"symbol": "Ks",
"name": "Myanmar Kyat",
+ "decimals": 0,
"ISO4217": "104"
},
"MNT": {
@@ -594,6 +606,7 @@
"PYG": {
"symbol": "₲",
"name": "Paraguayan Guarani",
+ "decimals": 0,
"ISO4217": "600"
},
"QAR": {
@@ -609,6 +622,7 @@
"RSD": {
"symbol": "РСД",
"name": "Serbian Dinar",
+ "decimals": 0,
"ISO4217": "941"
},
"RUB": {
@@ -660,11 +674,13 @@
"SLL": {
"symbol": "Le",
"name": "Sierra Leone Leone",
+ "decimals": 0,
"ISO4217": "694"
},
"SOS": {
"symbol": "So.",
"name": "Somali Shilling",
+ "decimals": 0,
"ISO4217": "706"
},
"SRD": {
@@ -675,6 +691,7 @@
"STD": {
"symbol": "Db",
"name": "Sao Tome Dobra",
+ "decimals": 0,
"retired": true,
"retirementDate": "2018-07-11",
"ISO4217": "678"
@@ -692,6 +709,7 @@
"SYP": {
"symbol": "SYP",
"name": "Syrian Pound",
+ "decimals": 0,
"ISO4217": "760"
},
"SZL": {
@@ -798,6 +816,7 @@
"VUV": {
"symbol": "Vt",
"name": "Vanuatu Vatu",
+ "decimals": 0,
"ISO4217": "548"
},
"WST": {
@@ -830,7 +849,8 @@
},
"YER": {
"symbol": "YER",
- "name": "Yemen Riyal",
+ "name": "Yemen Riyal",
+ "decimals": 0,
"ISO4217": "886"
},
"ZAR": {
@@ -841,6 +861,7 @@
"ZMK": {
"symbol": "ZK",
"name": "Zambian Kwacha",
+ "decimals": 0,
"retired": true,
"retirementDate": "2013-01-01",
"ISO4217": "894"
@@ -852,4 +873,3 @@
"ISO4217": "967"
}
}
-
\ No newline at end of file