Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature : Added Promote referral program messaging to OptionsSelector component #30372

Merged
merged 20 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
317 changes: 317 additions & 0 deletions assets/images/product-illustrations/payment-hands.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 12 additions & 1 deletion src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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:',

Expand Down Expand Up @@ -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',
REFERRAL_FRIEND: 'referralFriend',
waterim marked this conversation as resolved.
Show resolved Hide resolved
},
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;
5 changes: 5 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
7 changes: 4 additions & 3 deletions src/components/CopyTextToClipboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<PressableWithDelayToggle
Expand Down
2 changes: 2 additions & 0 deletions src/components/Icon/Illustrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import Lounge from '@assets/images/product-illustrations/lounge.svg';
import MagicCode from '@assets/images/product-illustrations/magic-code.svg';
import MoneyEnvelopeBlue from '@assets/images/product-illustrations/money-envelope--blue.svg';
import MoneyMousePink from '@assets/images/product-illustrations/money-mouse--pink.svg';
import PaymentHands from '@assets/images/product-illustrations/payment-hands.svg';
import ReceiptYellow from '@assets/images/product-illustrations/receipt--yellow.svg';
import ReceiptsSearchYellow from '@assets/images/product-illustrations/receipts-search--yellow.svg';
import RocketBlue from '@assets/images/product-illustrations/rocket--blue.svg';
Expand Down Expand Up @@ -62,6 +63,7 @@ export {
InvoiceOrange,
JewelBoxBlue,
JewelBoxGreen,
PaymentHands,
JewelBoxPink,
JewelBoxYellow,
MagicCode,
Expand Down
48 changes: 48 additions & 0 deletions src/components/OptionsSelector/BaseOptionsSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,23 @@ import ArrowKeyFocusManager from '@components/ArrowKeyFocusManager';
import Button from '@components/Button';
import FixedFooter from '@components/FixedFooter';
import FormHelpMessage from '@components/FormHelpMessage';
import Icon from '@components/Icon';
import {Info} from '@components/Icon/Expensicons';
import OptionsList from '@components/OptionsList';
import {PressableWithoutFeedback} from '@components/Pressable';
import Text from '@components/Text';
import TextInput from '@components/TextInput';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import withNavigationFocus from '@components/withNavigationFocus';
import compose from '@libs/compose';
import getPlatform from '@libs/getPlatform';
import KeyboardShortcut from '@libs/KeyboardShortcut';
import Navigation from '@libs/Navigation/Navigation';
import setSelection from '@libs/setSelection';
import colors from '@styles/colors';
import styles from '@styles/styles';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import {defaultProps as optionsSelectorDefaultProps, propTypes as optionsSelectorPropTypes} from './optionsSelectorPropTypes';

const propTypes = {
Expand All @@ -35,12 +42,19 @@ const propTypes = {
/** Whether navigation is focused */
isFocused: PropTypes.bool.isRequired,

/** Whether referral CTA should be displayed */
shouldShowReferralCTA: PropTypes.bool,

/** Referral content type */
referralContentType: PropTypes.string,
waterim marked this conversation as resolved.
Show resolved Hide resolved

...optionsSelectorPropTypes,
...withLocalizePropTypes,
};

const defaultProps = {
shouldDelayFocus: false,
shouldShowReferralCTA: false,
safeAreaPaddingBottomStyle: {},
contentContainerStyles: [],
listContainerStyles: [styles.flex1],
Expand All @@ -55,6 +69,7 @@ class BaseOptionsSelector extends Component {
this.updateFocusedIndex = this.updateFocusedIndex.bind(this);
this.scrollToIndex = this.scrollToIndex.bind(this);
this.selectRow = this.selectRow.bind(this);
this.handleReferralModal = this.handleReferralModal.bind(this);
this.selectFocusedOption = this.selectFocusedOption.bind(this);
this.addToSelection = this.addToSelection.bind(this);
this.updateSearchValue = this.updateSearchValue.bind(this);
Expand All @@ -67,6 +82,7 @@ class BaseOptionsSelector extends Component {
allOptions,
focusedIndex,
shouldDisableRowSelection: false,
shouldShowReferralModal: false,
errorMessage: '',
};
}
Expand Down Expand Up @@ -180,6 +196,10 @@ class BaseOptionsSelector extends Component {
this.props.onChangeText(value);
}

handleReferralModal() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there! I'm refactoring this whole component and curious about its purpose. It looks like we never call it.

this.setState((prevState) => ({shouldShowReferralModal: !prevState.shouldShowReferralModal}));
}

subscribeToKeyboardShortcut() {
const enterConfig = CONST.KEYBOARD_SHORTCUTS.ENTER;
this.unsubscribeEnter = KeyboardShortcut.subscribe(
Expand Down Expand Up @@ -495,6 +515,34 @@ class BaseOptionsSelector extends Component {
</>
)}
</View>
{this.props.shouldShowReferralCTA && (
<View style={[styles.ph5, styles.pb5, styles.flexShrink0]}>
<PressableWithoutFeedback
onPress={() => {
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"
accessibilityRole={CONST.ACCESSIBILITY_ROLE.BUTTON}
waterim marked this conversation as resolved.
Show resolved Hide resolved
>
<Text>
{this.props.translate(`referralProgram.${this.props.referralContentType}.buttonText1`)}
<Text
color={colors.green400}
style={styles.textStrong}
>
{this.props.translate(`referralProgram.${this.props.referralContentType}.buttonText2`)}
</Text>
</Text>
<Icon
src={Info}
height={20}
width={20}
/>
</PressableWithoutFeedback>
</View>
)}

{shouldShowFooter && (
<FixedFooter>
{shouldShowDefaultConfirmButton && (
Expand Down
28 changes: 28 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export default {
twoFactorCode: 'Two-factor code',
workspaces: 'Workspaces',
profile: 'Profile',
referral: 'Referral',
payments: 'Payments',
wallet: 'Wallet',
preferences: 'Preferences',
Expand Down Expand Up @@ -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.`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.`,
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.`,

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyone can raise quick fix for this typo?

},
[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.REFERRAL_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;
28 changes: 28 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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.REFERRAL_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;
4 changes: 4 additions & 0 deletions src/libs/Navigation/AppNavigator/ModalStackNavigators.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -248,4 +251,5 @@ export {
SignInModalStackNavigator,
RoomMembersModalStackNavigator,
RoomInviteModalStackNavigator,
ReferralModalStackNavigator,
};
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ function RightModalNavigator(props) {
name="SignIn"
component={ModalStackNavigators.SignInModalStackNavigator}
/>
<Stack.Screen
name="Referral"
component={ModalStackNavigators.ReferralModalStackNavigator}
/>
<Stack.Screen
name="Private_Notes"
component={ModalStackNavigators.PrivateNotesModalStackNavigator}
Expand Down
5 changes: 5 additions & 0 deletions src/libs/Navigation/linkingConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,11 @@ export default {
SignIn_Root: ROUTES.SIGN_IN_MODAL,
},
},
Referral: {
screens: {
Referral_Details: ROUTES.REFERRAL_DETAILS_MODAL.route,
},
},
},
},
},
Expand Down
2 changes: 2 additions & 0 deletions src/pages/NewChatPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,8 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, translate, i
shouldPreventDefaultFocusOnSelectRow={!Browser.isMobile()}
shouldShowOptions={isOptionsDataReady}
shouldShowConfirmButton
shouldShowReferralCTA
referralContentType={CONST.REFERRAL_PROGRAM.CONTENT_TYPES.START_CHAT}
confirmButtonText={selectedOptions.length > 1 ? translate('newChatPage.createGroup') : translate('newChatPage.createChat')}
textInputAlert={isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''}
onConfirmSelection={createGroup}
Expand Down
Loading
Loading