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')} + + +