diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js
index 39c533bd2c3c..345680e809f3 100755
--- a/src/components/OptionsSelector/BaseOptionsSelector.js
+++ b/src/components/OptionsSelector/BaseOptionsSelector.js
@@ -41,6 +41,9 @@ const propTypes = {
/** Whether referral CTA should be displayed */
shouldShowReferralCTA: PropTypes.bool,
+ /** A method triggered when the user closes the call to action banner */
+ onCallToActionClosed: PropTypes.func,
+
/** Referral content type */
referralContentType: PropTypes.string,
@@ -53,6 +56,7 @@ const propTypes = {
const defaultProps = {
shouldDelayFocus: false,
shouldShowReferralCTA: false,
+ onCallToActionClosed: () => {},
referralContentType: CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND,
safeAreaPaddingBottomStyle: {},
contentContainerStyles: [],
@@ -68,7 +72,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.closeReferralModal = this.closeReferralModal.bind(this);
this.selectFocusedOption = this.selectFocusedOption.bind(this);
this.addToSelection = this.addToSelection.bind(this);
this.updateSearchValue = this.updateSearchValue.bind(this);
@@ -262,8 +266,9 @@ class BaseOptionsSelector extends Component {
this.props.onChangeText(value);
}
- handleReferralModal() {
+ closeReferralModal() {
this.setState((prevState) => ({shouldShowReferralModal: !prevState.shouldShowReferralModal}));
+ this.props.onCallToActionClosed(this.props.referralContentType);
}
handleFocusIn() {
@@ -652,7 +657,7 @@ class BaseOptionsSelector extends Component {
)}
diff --git a/src/libs/API/parameters/DismissReferralBannerParams.ts b/src/libs/API/parameters/DismissReferralBannerParams.ts
new file mode 100644
index 000000000000..dbda2d894244
--- /dev/null
+++ b/src/libs/API/parameters/DismissReferralBannerParams.ts
@@ -0,0 +1,10 @@
+import type {ValueOf} from 'type-fest';
+import type CONST from '@src/CONST';
+
+type ContentTypes = ValueOf;
+
+type DismissReferralBannerParams = {
+ type: ContentTypes;
+};
+
+export default DismissReferralBannerParams;
diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts
index 8c0c2fde17cf..b7c3dff7c342 100644
--- a/src/libs/API/parameters/index.ts
+++ b/src/libs/API/parameters/index.ts
@@ -14,6 +14,7 @@ export type {default as ConnectBankAccountWithPlaidParams} from './ConnectBankAc
export type {default as DeleteContactMethodParams} from './DeleteContactMethodParams';
export type {default as DeletePaymentBankAccountParams} from './DeletePaymentBankAccountParams';
export type {default as DeletePaymentCardParams} from './DeletePaymentCardParams';
+export type {default as DismissReferralBannerParams} from './DismissReferralBannerParams';
export type {default as ExpandURLPreviewParams} from './ExpandURLPreviewParams';
export type {default as GetMissingOnyxMessagesParams} from './GetMissingOnyxMessagesParams';
export type {default as GetNewerActionsParams} from './GetNewerActionsParams';
diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts
index 05b658ee0702..c011fa395f0f 100644
--- a/src/libs/API/types.ts
+++ b/src/libs/API/types.ts
@@ -8,6 +8,7 @@ import type UpdateBeneficialOwnersForBankAccountParams from './parameters/Update
type ApiRequest = ValueOf;
const WRITE_COMMANDS = {
+ DISMISS_REFERRAL_BANNER: 'DismissReferralBanner',
UPDATE_PREFERRED_LOCALE: 'UpdatePreferredLocale',
RECONNECT_APP: 'ReconnectApp',
OPEN_PROFILE: 'OpenProfile',
@@ -120,6 +121,7 @@ const WRITE_COMMANDS = {
type WriteCommand = ValueOf;
type WriteCommandParameters = {
+ [WRITE_COMMANDS.DISMISS_REFERRAL_BANNER]: Parameters.DismissReferralBannerParams;
[WRITE_COMMANDS.UPDATE_PREFERRED_LOCALE]: Parameters.UpdatePreferredLocaleParams;
[WRITE_COMMANDS.RECONNECT_APP]: Parameters.ReconnectAppParams;
[WRITE_COMMANDS.OPEN_PROFILE]: Parameters.OpenProfileParams;
diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts
index a8ef33a92e38..93739ce68e47 100644
--- a/src/libs/actions/User.ts
+++ b/src/libs/actions/User.ts
@@ -843,9 +843,31 @@ function clearDraftCustomStatus() {
Onyx.merge(ONYXKEYS.CUSTOM_STATUS_DRAFT, {text: '', emojiCode: '', clearAfter: ''});
}
+function dismissReferralBanner(type: ValueOf) {
+ const optimisticData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: ONYXKEYS.ACCOUNT,
+ value: {
+ dismissedReferralBanners: {
+ [type]: true,
+ },
+ },
+ },
+ ];
+ API.write(
+ WRITE_COMMANDS.DISMISS_REFERRAL_BANNER,
+ {type},
+ {
+ optimisticData,
+ },
+ );
+}
+
export {
clearFocusModeNotification,
closeAccount,
+ dismissReferralBanner,
resendValidateCode,
requestContactMethodValidateCode,
updateNewsletterSubscription,
diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js
index f6cf0dc39561..44131de01fa6 100755
--- a/src/pages/NewChatPage.js
+++ b/src/pages/NewChatPage.js
@@ -21,6 +21,7 @@ import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as ReportUtils from '@libs/ReportUtils';
import variables from '@styles/variables';
import * as Report from '@userActions/Report';
+import * as User from '@userActions/User';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import personalDetailsPropType from './personalDetailsPropType';
@@ -36,6 +37,9 @@ const propTypes = {
/** All reports shared with the user */
reports: PropTypes.objectOf(reportPropTypes),
+ /** An object that holds data about which referral banners have been dismissed */
+ dismissedReferralBanners: PropTypes.objectOf(PropTypes.bool),
+
...windowDimensionsPropTypes,
...withLocalizePropTypes,
@@ -46,6 +50,7 @@ const propTypes = {
const defaultProps = {
betas: [],
+ dismissedReferralBanners: {},
personalDetails: {},
reports: {},
isSearchingForReports: false,
@@ -53,7 +58,7 @@ const defaultProps = {
const excludedGroupEmails = _.without(CONST.EXPENSIFY_EMAILS, CONST.EMAIL.CONCIERGE);
-function NewChatPage({betas, isGroupChat, personalDetails, reports, translate, isSearchingForReports}) {
+function NewChatPage({betas, isGroupChat, personalDetails, reports, translate, isSearchingForReports, dismissedReferralBanners}) {
const styles = useThemeStyles();
const [searchTerm, setSearchTerm] = useState('');
const [filteredRecentReports, setFilteredRecentReports] = useState([]);
@@ -230,6 +235,10 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, translate, i
updateOptions();
}, [didScreenTransitionEnd, updateOptions]);
+ const dismissCallToAction = (referralContentType) => {
+ User.dismissReferralBanner(referralContentType);
+ };
+
const {inputCallbackRef} = useAutoFocusInput();
return (
@@ -265,8 +274,9 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, translate, i
shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()}
shouldShowOptions={isOptionsDataReady && didScreenTransitionEnd}
shouldShowConfirmButton
- shouldShowReferralCTA
+ shouldShowReferralCTA={!dismissedReferralBanners[CONST.REFERRAL_PROGRAM.CONTENT_TYPES.START_CHAT]}
referralContentType={CONST.REFERRAL_PROGRAM.CONTENT_TYPES.START_CHAT}
+ onCallToActionClosed={dismissCallToAction}
confirmButtonText={selectedOptions.length > 1 ? translate('newChatPage.createGroup') : translate('newChatPage.createChat')}
textInputAlert={isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''}
onConfirmSelection={createGroup}
@@ -291,6 +301,10 @@ export default compose(
withLocalize,
withWindowDimensions,
withOnyx({
+ dismissedReferralBanners: {
+ key: ONYXKEYS.ACCOUNT,
+ selector: (data) => data.dismissedReferralBanners || {},
+ },
reports: {
key: ONYXKEYS.COLLECTION.REPORT,
},
diff --git a/src/pages/SearchPage/SearchPageFooter.tsx b/src/pages/SearchPage/SearchPageFooter.tsx
index fb3644d8e570..8e23c658f4aa 100644
--- a/src/pages/SearchPage/SearchPageFooter.tsx
+++ b/src/pages/SearchPage/SearchPageFooter.tsx
@@ -1,20 +1,32 @@
import React, {useState} from 'react';
import {View} from 'react-native';
+import {withOnyx} from 'react-native-onyx';
import ReferralProgramCTA from '@components/ReferralProgramCTA';
import useThemeStyles from '@hooks/useThemeStyles';
+import * as User from '@userActions/User';
import CONST from '@src/CONST';
+import ONYXKEYS from '@src/ONYXKEYS';
+import type {DismissedReferralBanners} from '@src/types/onyx/Account';
-function SearchPageFooter() {
- const [shouldShowReferralCTA, setShouldShowReferralCTA] = useState(true);
+type SearchPageFooterOnyxProps = {
+ dismissedReferralBanners: DismissedReferralBanners;
+};
+function SearchPageFooter({dismissedReferralBanners}: SearchPageFooterOnyxProps) {
+ const [shouldShowReferralCTA, setShouldShowReferralCTA] = useState(!dismissedReferralBanners[CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND]);
const themeStyles = useThemeStyles();
+ const closeCallToActionBanner = () => {
+ setShouldShowReferralCTA(false);
+ User.dismissReferralBanner(CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND);
+ };
+
return (
<>
{shouldShowReferralCTA && (
setShouldShowReferralCTA(false)}
+ onCloseButtonPress={closeCallToActionBanner}
/>
)}
@@ -24,4 +36,9 @@ function SearchPageFooter() {
SearchPageFooter.displayName = 'SearchPageFooter';
-export default SearchPageFooter;
+export default withOnyx({
+ dismissedReferralBanners: {
+ key: ONYXKEYS.ACCOUNT,
+ selector: (data) => data?.dismissedReferralBanners ?? {},
+ },
+})(SearchPageFooter);
diff --git a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js
index 9df1e0203b85..99335b062f52 100644
--- a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js
+++ b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js
@@ -19,6 +19,7 @@ import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as ReportUtils from '@libs/ReportUtils';
import reportPropTypes from '@pages/reportPropTypes';
+import * as User from '@userActions/User';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
@@ -26,6 +27,9 @@ const propTypes = {
/** Beta features list */
betas: PropTypes.arrayOf(PropTypes.string),
+ /** An object that holds data about which referral banners have been dismissed */
+ dismissedReferralBanners: PropTypes.objectOf(PropTypes.bool),
+
/** Callback to request parent modal to go to next step, which should be split */
onFinish: PropTypes.func.isRequired,
@@ -64,6 +68,7 @@ const defaultProps = {
safeAreaPaddingBottomStyle: {},
reports: {},
betas: [],
+ dismissedReferralBanners: {},
didScreenTransitionEnd: false,
};
@@ -76,12 +81,14 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({
safeAreaPaddingBottomStyle,
iouType,
iouRequestType,
+ dismissedReferralBanners,
didScreenTransitionEnd,
}) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const [searchTerm, setSearchTerm] = useState('');
- const [shouldShowReferralCTA, setShouldShowReferralCTA] = useState(true);
+ const referralContentType = iouType === CONST.IOU.TYPE.SEND ? CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SEND_MONEY : CONST.REFERRAL_PROGRAM.CONTENT_TYPES.MONEY_REQUEST;
+ const [shouldShowReferralCTA, setShouldShowReferralCTA] = useState(!dismissedReferralBanners[referralContentType]);
const {isOffline} = useNetwork();
const personalDetails = usePersonalDetails();
@@ -251,7 +258,6 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({
const hasPolicyExpenseChatParticipant = _.some(participants, (participant) => participant.isPolicyExpenseChat);
const shouldShowSplitBillErrorMessage = participants.length > 1 && hasPolicyExpenseChatParticipant;
const isAllowedToSplit = iouRequestType !== CONST.IOU.REQUEST_TYPE.DISTANCE;
- const referralContentType = iouType === CONST.IOU.TYPE.SEND ? CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SEND_MONEY : CONST.REFERRAL_PROGRAM.CONTENT_TYPES.MONEY_REQUEST;
const handleConfirmSelection = useCallback(() => {
if (shouldShowSplitBillErrorMessage) {
@@ -261,6 +267,11 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({
onFinish();
}, [shouldShowSplitBillErrorMessage, onFinish]);
+ const closeCallToActionBanner = useCallback(() => {
+ setShouldShowReferralCTA(false);
+ User.dismissReferralBanner(referralContentType);
+ }, [referralContentType]);
+
const footerContent = useMemo(
() => (
@@ -268,7 +279,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({
setShouldShowReferralCTA(false)}
+ onCloseButtonPress={closeCallToActionBanner}
/>
)}
@@ -292,7 +303,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({
)}
),
- [handleConfirmSelection, participants.length, referralContentType, shouldShowSplitBillErrorMessage, shouldShowReferralCTA, styles, translate],
+ [handleConfirmSelection, participants.length, referralContentType, shouldShowSplitBillErrorMessage, shouldShowReferralCTA, styles, translate, closeCallToActionBanner],
);
const itemRightSideComponent = useCallback(
@@ -356,6 +367,10 @@ MoneyTemporaryForRefactorRequestParticipantsSelector.defaultProps = defaultProps
MoneyTemporaryForRefactorRequestParticipantsSelector.displayName = 'MoneyTemporaryForRefactorRequestParticipantsSelector';
export default withOnyx({
+ dismissedReferralBanners: {
+ key: ONYXKEYS.ACCOUNT,
+ selector: (data) => data.dismissedReferralBanners || {},
+ },
reports: {
key: ONYXKEYS.COLLECTION.REPORT,
},
diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js
index daaa63aae147..3cf39d98426f 100755
--- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js
+++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js
@@ -18,6 +18,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import reportPropTypes from '@pages/reportPropTypes';
+import * as User from '@userActions/User';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
@@ -34,6 +35,9 @@ const propTypes = {
/** Callback to add participants in MoneyRequestModal */
onAddParticipants: PropTypes.func.isRequired,
+ /** An object that holds data about which referral banners have been dismissed */
+ dismissedReferralBanners: PropTypes.objectOf(PropTypes.bool),
+
/** Selected participants from MoneyRequestModal with login */
participants: PropTypes.arrayOf(
PropTypes.shape({
@@ -62,6 +66,7 @@ const propTypes = {
};
const defaultProps = {
+ dismissedReferralBanners: {},
participants: [],
safeAreaPaddingBottomStyle: {},
reports: {},
@@ -72,6 +77,7 @@ const defaultProps = {
function MoneyRequestParticipantsSelector({
betas,
+ dismissedReferralBanners,
participants,
reports,
navigateToRequest,
@@ -85,7 +91,8 @@ function MoneyRequestParticipantsSelector({
const {translate} = useLocalize();
const styles = useThemeStyles();
const [searchTerm, setSearchTerm] = useState('');
- const [shouldShowReferralCTA, setShouldShowReferralCTA] = useState(true);
+ const referralContentType = iouType === CONST.IOU.TYPE.SEND ? CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SEND_MONEY : CONST.REFERRAL_PROGRAM.CONTENT_TYPES.MONEY_REQUEST;
+ const [shouldShowReferralCTA, setShouldShowReferralCTA] = useState(!dismissedReferralBanners[referralContentType]);
const {isOffline} = useNetwork();
const personalDetails = usePersonalDetails();
@@ -276,7 +283,10 @@ function MoneyRequestParticipantsSelector({
navigateToSplit();
}, [shouldShowSplitBillErrorMessage, navigateToSplit]);
- const referralContentType = iouType === CONST.IOU.TYPE.SEND ? CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SEND_MONEY : CONST.REFERRAL_PROGRAM.CONTENT_TYPES.MONEY_REQUEST;
+ const closeCallToActionBanner = useCallback(() => {
+ setShouldShowReferralCTA(false);
+ User.dismissReferralBanner(referralContentType);
+ }, [referralContentType]);
const footerContent = useMemo(
() => (
@@ -285,7 +295,7 @@ function MoneyRequestParticipantsSelector({
setShouldShowReferralCTA(false)}
+ onCloseButtonPress={closeCallToActionBanner}
/>
)}
@@ -309,7 +319,7 @@ function MoneyRequestParticipantsSelector({
)}
),
- [handleConfirmSelection, participants.length, referralContentType, shouldShowSplitBillErrorMessage, shouldShowReferralCTA, styles, translate],
+ [handleConfirmSelection, participants.length, referralContentType, shouldShowSplitBillErrorMessage, shouldShowReferralCTA, styles, translate, closeCallToActionBanner],
);
const itemRightSideComponent = useCallback(
@@ -368,6 +378,10 @@ MoneyRequestParticipantsSelector.displayName = 'MoneyRequestParticipantsSelector
MoneyRequestParticipantsSelector.defaultProps = defaultProps;
export default withOnyx({
+ dismissedReferralBanners: {
+ key: ONYXKEYS.ACCOUNT,
+ selector: (data) => data.dismissedReferralBanners || {},
+ },
reports: {
key: ONYXKEYS.COLLECTION.REPORT,
},
diff --git a/src/types/onyx/Account.ts b/src/types/onyx/Account.ts
index 4e7c5396b649..379a3ae091e0 100644
--- a/src/types/onyx/Account.ts
+++ b/src/types/onyx/Account.ts
@@ -4,6 +4,14 @@ import type * as OnyxCommon from './OnyxCommon';
type TwoFactorAuthStep = ValueOf | '';
+type DismissedReferralBanners = {
+ [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.MONEY_REQUEST]?: boolean;
+ [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.START_CHAT]?: boolean;
+ [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SEND_MONEY]?: boolean;
+ [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND]?: boolean;
+ [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SHARE_CODE]?: boolean;
+};
+
type Account = {
/** URL to the assigned guide's appointment booking calendar */
guideCalendarLink?: string;
@@ -54,7 +62,8 @@ type Account = {
success?: string;
codesAreCopied?: boolean;
twoFactorAuthStep?: TwoFactorAuthStep;
+ dismissedReferralBanners?: DismissedReferralBanners;
};
export default Account;
-export type {TwoFactorAuthStep};
+export type {TwoFactorAuthStep, DismissedReferralBanners};