diff --git a/assets/images/product-illustrations/payment-hands.svg b/assets/images/product-illustrations/payment-hands.svg
new file mode 100644
index 000000000000..7d64d8572b30
--- /dev/null
+++ b/assets/images/product-illustrations/payment-hands.svg
@@ -0,0 +1,317 @@
+
+
+
diff --git a/src/CONST.ts b/src/CONST.ts
index eda1dd34cc50..102faca5a70b 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -469,7 +469,6 @@ const CONST = {
ONFIDO_FACIAL_SCAN_POLICY_URL: 'https://onfido.com/facial-scan-policy-and-release/',
ONFIDO_PRIVACY_POLICY_URL: 'https://onfido.com/privacy/',
ONFIDO_TERMS_OF_SERVICE_URL: 'https://onfido.com/terms-of-service/',
-
// Use Environment.getEnvironmentURL to get the complete URL with port number
DEV_NEW_EXPENSIFY_URL: 'https://dev.new.expensify.com:',
@@ -2810,6 +2809,18 @@ const CONST = {
* The count of characters we'll allow the user to type after reaching SEARCH_MAX_LENGTH in an input.
*/
ADDITIONAL_ALLOWED_CHARACTERS: 20,
+
+ REFERRAL_PROGRAM: {
+ CONTENT_TYPES: {
+ MONEY_REQUEST: 'request',
+ START_CHAT: 'startChat',
+ SEND_MONEY: 'sendMoney',
+ REFER_FRIEND: 'referralFriend',
+ },
+ REVENUE: 250,
+ LEARN_MORE_LINK: 'https://help.expensify.com/articles/new-expensify/getting-started/Referral-Program',
+ LINK: 'https://join.my.expensify.com',
+ },
} as const;
export default CONST;
diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index ed9cc6ae987c..57d4eb8187ec 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -358,6 +358,11 @@ export default {
route: 'workspace/:policyID/members',
getRoute: (policyID: string) => `workspace/${policyID}/members`,
},
+ // Referral program promotion
+ REFERRAL_DETAILS_MODAL: {
+ route: 'referral/:contentType',
+ getRoute: (contentType: string) => `referral/${contentType}`,
+ },
// These are some one-off routes that will be removed once they're no longer needed (see GH issues for details)
SAASTR: 'saastr',
diff --git a/src/components/CopyTextToClipboard.js b/src/components/CopyTextToClipboard.js
index ac396c6cedf4..acd3f08f2b22 100644
--- a/src/components/CopyTextToClipboard.js
+++ b/src/components/CopyTextToClipboard.js
@@ -12,18 +12,19 @@ const propTypes = {
/** Styles to apply to the text */
// eslint-disable-next-line react/forbid-prop-types
textStyles: PropTypes.arrayOf(PropTypes.object),
-
+ urlToCopy: PropTypes.string,
...withLocalizePropTypes,
};
const defaultProps = {
textStyles: [],
+ urlToCopy: null,
};
function CopyTextToClipboard(props) {
const copyToClipboard = useCallback(() => {
- Clipboard.setString(props.text);
- }, [props.text]);
+ Clipboard.setString(props.urlToCopy || props.text);
+ }, [props.text, props.urlToCopy]);
return (
({shouldShowReferralModal: !prevState.shouldShowReferralModal}));
+ }
+
subscribeToKeyboardShortcut() {
const enterConfig = CONST.KEYBOARD_SHORTCUTS.ENTER;
this.unsubscribeEnter = KeyboardShortcut.subscribe(
@@ -495,6 +516,34 @@ class BaseOptionsSelector extends Component {
>
)}
+ {this.props.shouldShowReferralCTA && (
+
+ {
+ Navigation.navigate(ROUTES.REFERRAL_DETAILS_MODAL.getRoute(this.props.referralContentType));
+ }}
+ style={[styles.p5, styles.w100, styles.br2, styles.highlightBG, styles.flexRow, styles.justifyContentBetween, styles.alignItemsCenter, {gap: 10}]}
+ accessibilityLabel="referral"
+ role={CONST.ACCESSIBILITY_ROLE.BUTTON}
+ >
+
+ {this.props.translate(`referralProgram.${this.props.referralContentType}.buttonText1`)}
+
+ {this.props.translate(`referralProgram.${this.props.referralContentType}.buttonText2`)}
+
+
+
+
+
+ )}
+
{shouldShowFooter && (
{shouldShowDefaultConfirmButton && (
diff --git a/src/languages/en.ts b/src/languages/en.ts
index fe867efc27c0..fc43524672ee 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -117,6 +117,7 @@ export default {
twoFactorCode: 'Two-factor code',
workspaces: 'Workspaces',
profile: 'Profile',
+ referral: 'Referral',
payments: 'Payments',
wallet: 'Wallet',
preferences: 'Preferences',
@@ -1908,4 +1909,31 @@ export default {
guaranteed: 'Guaranteed eReceipt',
transactionDate: 'Transaction date',
},
+ referralProgram: {
+ [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.START_CHAT]: {
+ buttonText1: 'Start a chat, ',
+ buttonText2: `get $${CONST.REFERRAL_PROGRAM.REVENUE}.`,
+ header: `Start a chat, get $${CONST.REFERRAL_PROGRAM.REVENUE}`,
+ body: `Start a chat with a new Expensify account. Get $${CONST.REFERRAL_PROGRAM.REVENUE} once they start an annual subscription with two or more active members and make the first two payments toward their Expensify bill.`,
+ },
+ [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.MONEY_REQUEST]: {
+ buttonText1: 'Request money, ',
+ buttonText2: `get $${CONST.REFERRAL_PROGRAM.REVENUE}.`,
+ header: `Request money, get $${CONST.REFERRAL_PROGRAM.REVENUE}`,
+ body: `Request money from a new Expensify account. Get $${CONST.REFERRAL_PROGRAM.REVENUE} once they start an annual subscription with two or more active members and make the first two payments toward their Expensify bill.`,
+ },
+ [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SEND_MONEY]: {
+ buttonText1: 'Send money, ',
+ buttonText2: `get $${CONST.REFERRAL_PROGRAM.REVENUE}.`,
+ header: `Send money, get $${CONST.REFERRAL_PROGRAM.REVENUE}`,
+ body: `Send money to a new Expensify account. Get $${CONST.REFERRAL_PROGRAM.REVENUE} once they start an annual subscription with two or more active members and make the first two payments toward their Expensify bill.`,
+ },
+ [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND]: {
+ buttonText1: 'Refer a friend, ',
+ buttonText2: `get $${CONST.REFERRAL_PROGRAM.REVENUE}.`,
+ header: `Refer a friend, get $${CONST.REFERRAL_PROGRAM.REVENUE}`,
+ body: `Send your Expensify referral link to a friend or anyone else you know who spends too much time on expenses. When they start an annual subscription, you'll get $${CONST.REFERRAL_PROGRAM.REVENUE}.`,
+ },
+ copyReferralLink: 'Copy referral link',
+ },
} satisfies TranslationBase;
diff --git a/src/languages/es.ts b/src/languages/es.ts
index d86b712104fd..606bbb8bfa51 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -107,6 +107,7 @@ export default {
twoFactorCode: 'Autenticación de dos factores',
workspaces: 'Espacios de trabajo',
profile: 'Perfil',
+ referral: 'Remisión',
payments: 'Pagos',
wallet: 'Billetera',
preferences: 'Preferencias',
@@ -2392,4 +2393,31 @@ export default {
guaranteed: 'eRecibo garantizado',
transactionDate: 'Fecha de transacción',
},
+ referralProgram: {
+ [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.START_CHAT]: {
+ buttonText1: 'Inicia un chat y ',
+ buttonText2: `recibe $${CONST.REFERRAL_PROGRAM.REVENUE}`,
+ header: `Inicia un chat y recibe $${CONST.REFERRAL_PROGRAM.REVENUE}`,
+ body: `Inicia un chat con una cuenta nueva de Expensify. Obtiene $${CONST.REFERRAL_PROGRAM.REVENUE} una vez que configuren una suscripción anual con dos o más miembros activos y realicen los dos primeros pagos de su factura Expensify.`,
+ },
+ [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.MONEY_REQUEST]: {
+ buttonText1: 'Pide dinero, ',
+ buttonText2: `recibe $${CONST.REFERRAL_PROGRAM.REVENUE}`,
+ header: `Pide dinero y recibe $${CONST.REFERRAL_PROGRAM.REVENUE}`,
+ body: `Pide dinero a una cuenta nueva de Expensify. Obtiene $${CONST.REFERRAL_PROGRAM.REVENUE} una vez que configuren una suscripción anual con dos o más miembros activos y realicen los dos primeros pagos de su factura Expensify.`,
+ },
+ [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SEND_MONEY]: {
+ buttonText1: 'Envía dinero, ',
+ buttonText2: `recibe $${CONST.REFERRAL_PROGRAM.REVENUE}`,
+ header: `Envía dinero y recibe $${CONST.REFERRAL_PROGRAM.REVENUE}`,
+ body: `Envía dinero a una cuenta nueva de Expensify. Obtiene $${CONST.REFERRAL_PROGRAM.REVENUE} una vez que configuren una suscripción anual con dos o más miembros activos y realicen los dos primeros pagos de su factura Expensify.`,
+ },
+ [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND]: {
+ buttonText1: 'Recomienda a un amigo y ',
+ buttonText2: `recibe $${CONST.REFERRAL_PROGRAM.REVENUE}`,
+ header: `Recomienda a un amigo y recibe $${CONST.REFERRAL_PROGRAM.REVENUE}`,
+ body: `Envía tu enlace de invitación de Expensify a un amigo o a cualquier otra persona que conozcas que dedique demasiado tiempo a los gastos. Cuando comiencen una suscripción anual, obtendrás $${CONST.REFERRAL_PROGRAM.REVENUE}.`,
+ },
+ copyReferralLink: 'Copiar enlace de invitación',
+ },
} satisfies EnglishTranslation;
diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js
index 2f0a75a02cc3..a2f9bdd7a903 100644
--- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js
+++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js
@@ -222,6 +222,9 @@ const PrivateNotesModalStackNavigator = createModalStackNavigator({
const SignInModalStackNavigator = createModalStackNavigator({
SignIn_Root: () => require('../../../pages/signin/SignInModal').default,
});
+const ReferralModalStackNavigator = createModalStackNavigator({
+ Referral_Details: () => require('../../../pages/ReferralDetailsPage').default,
+});
export {
MoneyRequestModalStackNavigator,
@@ -248,4 +251,5 @@ export {
SignInModalStackNavigator,
RoomMembersModalStackNavigator,
RoomInviteModalStackNavigator,
+ ReferralModalStackNavigator,
};
diff --git a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.js b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.js
index de3fb9e79659..670a9a9b2793 100644
--- a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.js
+++ b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.js
@@ -118,6 +118,10 @@ function RightModalNavigator(props) {
name="SignIn"
component={ModalStackNavigators.SignInModalStackNavigator}
/>
+
1 ? translate('newChatPage.createGroup') : translate('newChatPage.createChat')}
textInputAlert={isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''}
onConfirmSelection={createGroup}
diff --git a/src/pages/ReferralDetailsPage.js b/src/pages/ReferralDetailsPage.js
new file mode 100644
index 000000000000..282e85fe0237
--- /dev/null
+++ b/src/pages/ReferralDetailsPage.js
@@ -0,0 +1,104 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import {View} from 'react-native';
+import {withOnyx} from 'react-native-onyx';
+import _ from 'underscore';
+import Button from '@components/Button';
+import CopyTextToClipboard from '@components/CopyTextToClipboard';
+import FixedFooter from '@components/FixedFooter';
+import HeaderWithBackButton from '@components/HeaderWithBackButton';
+import Icon from '@components/Icon';
+import {PaymentHands} from '@components/Icon/Illustrations';
+import ScreenWrapper from '@components/ScreenWrapper';
+import Text from '@components/Text';
+import TextLink from '@components/TextLink';
+import useLocalize from '@hooks/useLocalize';
+import Navigation from '@libs/Navigation/Navigation';
+import styles from '@styles/styles';
+import CONST from '@src/CONST';
+import ONYXKEYS from '@src/ONYXKEYS';
+
+const propTypes = {
+ /** Navigation route context info provided by react navigation */
+ route: PropTypes.shape({
+ params: PropTypes.shape({
+ /** The type of the content from where CTA was called */
+ contentType: PropTypes.string,
+ }),
+ }).isRequired,
+
+ /** The details about the account that the user is signing in with */
+ account: PropTypes.shape({
+ /** The primaryLogin associated with the account */
+ primaryLogin: PropTypes.string,
+ }),
+};
+
+const defaultProps = {
+ account: null,
+};
+
+function ReferralDetailsPage({route, account}) {
+ const {translate} = useLocalize();
+ let {contentType} = route.params;
+
+ if (!_.includes(_.values(CONST.REFERRAL_PROGRAM.CONTENT_TYPES), contentType)) {
+ contentType = CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND;
+ }
+ const contentHeader = translate(`referralProgram.${contentType}.header`);
+ const contentBody = translate(`referralProgram.${contentType}.body`);
+
+ function generateReferralURL(email) {
+ return `${CONST.REFERRAL_PROGRAM.LINK}/?thanks=${encodeURIComponent(email)}`;
+ }
+
+ return (
+
+ Navigation.goBack()}
+ />
+
+
+ {contentHeader}
+ {contentBody}
+ {contentType === CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND && (
+
+
+
+ )}
+ {translate('requestorStep.learnMore')}
+
+
+
+
+ );
+}
+
+ReferralDetailsPage.displayName = 'ReferralDetailsPage';
+ReferralDetailsPage.propTypes = propTypes;
+ReferralDetailsPage.defaultProps = defaultProps;
+
+export default withOnyx({
+ account: {key: ONYXKEYS.ACCOUNT},
+})(ReferralDetailsPage);
diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js
index 3e7731efc7b2..c8d07b0fc7ae 100755
--- a/src/pages/SearchPage.js
+++ b/src/pages/SearchPage.js
@@ -203,6 +203,8 @@ class SearchPage extends Component {
textInputAlert={
this.props.network.isOffline ? `${this.props.translate('common.youAppearToBeOffline')} ${this.props.translate('search.resultsAreLimited')}` : ''
}
+ shouldShowReferralCTA
+ referralContentType={CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND}
onLayout={this.searchRendered}
safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle}
autoFocus
diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js
index 553ba6ae5170..2e22cae3bbb5 100755
--- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js
+++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js
@@ -319,6 +319,8 @@ function MoneyRequestParticipantsSelector({
textInputAlert={isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''}
safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle}
shouldShowOptions={isOptionsDataReady}
+ shouldShowReferralCTA
+ referralContentType={iouType === 'send' ? CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SEND_MONEY : CONST.REFERRAL_PROGRAM.CONTENT_TYPES.MONEY_REQUEST}
shouldPreventDefaultFocusOnSelectRow={!Browser.isMobile()}
shouldDelayFocus
footerContent={isAllowedToSplit && footerContent}