Skip to content

Commit

Permalink
Merge pull request Expensify#52526 from hungvu193/feat-51703
Browse files Browse the repository at this point in the history
Improve Switch to Expensify Classic flow
  • Loading branch information
mountiny authored Nov 18, 2024
2 parents b5dbb51 + e748e6c commit 7c90011
Show file tree
Hide file tree
Showing 14 changed files with 169 additions and 26 deletions.
6 changes: 6 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5867,6 +5867,12 @@ const CONST = {
DONT_UNDERSTAND: 'dontUnderstand',
PREFER_CLASSIC: 'preferClassic',
},
BENEFIT: {
CHATTING_DIRECTLY: 'chattingDirectly',
EVERYTHING_MOBILE: 'everythingMobile',
TRAVEL_EXPENSE: 'travelExpense',
},
BOOK_MEETING_LINK: 'https://calendly.com/d/cqsm-2gm-fxr/expensify-product-team',
},

SESSION_STORAGE_KEYS: {
Expand Down
9 changes: 8 additions & 1 deletion src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,14 @@ const ROUTES = {
getRoute: (source: string) => `settings/troubleshoot/console/share-log?source=${encodeURI(source)}` as const,
},

SETTINGS_EXIT_SURVEY_REASON: 'settings/exit-survey/reason',
SETTINGS_EXIT_SURVEY_REASON: {
route: 'settings/exit-survey/reason',
getRoute: (backTo?: string) => getUrlWithBackToParam('settings/exit-survey/reason', backTo),
},
SETTINGS_EXIT_SURVERY_BOOK_CALL: {
route: 'settings/exit-survey/book-call',
getRoute: (backTo?: string) => getUrlWithBackToParam('settings/exit-survey/book-call', backTo),
},
SETTINGS_EXIT_SURVEY_RESPONSE: {
route: 'settings/exit-survey/response',
getRoute: (reason?: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>, backTo?: string) =>
Expand Down
1 change: 1 addition & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ const SCREENS = {
},

EXIT_SURVEY: {
BOOK_CALL: 'Settings_ExitSurvey_Book_Call',
REASON: 'Settings_ExitSurvey_Reason',
RESPONSE: 'Settings_ExitSurvey_Response',
CONFIRM: 'Settings_ExitSurvey_Confirm',
Expand Down
13 changes: 13 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4976,6 +4976,19 @@ const translations = {
offlineTitle: "Looks like you're stuck here...",
offline:
"You appear to be offline. Unfortunately, Expensify Classic doesn't work offline, but New Expensify does. If you prefer to use Expensify Classic, try again when you have an internet connection.",
quickTip: 'Quick tip...',
quickTipSubTitle: 'You can go straight to Expensify Classic by visiting expensify.com. Bookmark it for an easy shortcut!',
bookACall: 'Book a call',
noThanks: 'No thanks',
bookACallTitle: 'Would you like to speak to a product manager?',
benefits: {
[CONST.EXIT_SURVEY.BENEFIT.CHATTING_DIRECTLY]: 'Chatting directly on expenses and reports',
[CONST.EXIT_SURVEY.BENEFIT.EVERYTHING_MOBILE]: 'Ability to do everything on mobile',
[CONST.EXIT_SURVEY.BENEFIT.TRAVEL_EXPENSE]: 'Travel and expense at the speed of chat',
},
bookACallTextTop: 'By switching to Expensify Classic, you will miss out on:',
bookACallTextBottom: 'We’d be excited to get on a call with you to understand why. You can book a call with one of our senior product managers to discuss your needs.',
takeMeToExpensifyClassic: 'Take me to Expensify Classic',
},
listBoundary: {
errorMessage: 'An error occurred while loading more messages.',
Expand Down
13 changes: 13 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5491,6 +5491,19 @@ const translations = {
offlineTitle: 'Parece que estás atrapado aquí...',
offline:
'Parece que estás desconectado. Desafortunadamente, Expensify Classic no funciona sin conexión, pero New Expensify sí. Si prefieres utilizar Expensify Classic, inténtalo de nuevo cuando tengas conexión a internet.',
quickTip: 'Consejo rápido...',
quickTipSubTitle: 'Puedes ir directamente a Expensify Classic visitando expensify.com. Márcalo como favorito para tener un acceso directo fácil.',
bookACall: 'Reservar una llamada',
noThanks: 'No, gracias',
bookACallTitle: '¿Desea hablar con un responsable de producto?',
benefits: {
[CONST.EXIT_SURVEY.BENEFIT.CHATTING_DIRECTLY]: 'Chat directo sobre gastos e informes',
[CONST.EXIT_SURVEY.BENEFIT.EVERYTHING_MOBILE]: 'Posibilidad de hacerlo todo desde el móvil',
[CONST.EXIT_SURVEY.BENEFIT.TRAVEL_EXPENSE]: 'Viajes y gastos a la velocidad del chat',
},
bookACallTextTop: 'Al cambiar a Expensify Classic, se perderá:',
bookACallTextBottom: 'Nos encantaría hablar con usted para entender por qué. Puede concertar una llamada con uno de nuestros jefes de producto para hablar de sus necesidades.',
takeMeToExpensifyClassic: 'Llévame a Expensify Classic',
},
listBoundary: {
errorMessage: 'Se ha producido un error al cargar más mensajes.',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,7 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
[SCREENS.SETTINGS.REPORT_CARD_LOST_OR_DAMAGED]: () => require<ReactComponentModule>('../../../../pages/settings/Wallet/ReportCardLostPage').default,
[SCREENS.KEYBOARD_SHORTCUTS]: () => require<ReactComponentModule>('../../../../pages/KeyboardShortcutsPage').default,
[SCREENS.SETTINGS.EXIT_SURVEY.REASON]: () => require<ReactComponentModule>('../../../../pages/settings/ExitSurvey/ExitSurveyReasonPage').default,
[SCREENS.SETTINGS.EXIT_SURVEY.BOOK_CALL]: () => require<ReactComponentModule>('../../../../pages/settings/ExitSurvey/ExitSurveyBookCall').default,
[SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE]: () => require<ReactComponentModule>('../../../../pages/settings/ExitSurvey/ExitSurveyResponsePage').default,
[SCREENS.SETTINGS.EXIT_SURVEY.CONFIRM]: () => require<ReactComponentModule>('../../../../pages/settings/ExitSurvey/ExitSurveyConfirmPage').default,
[SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_IMPORT]: () => require<ReactComponentModule>('../../../../pages/workspace/accounting/qbo/import/QuickbooksImportPage').default,
Expand Down
5 changes: 4 additions & 1 deletion src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -882,7 +882,10 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
path: ROUTES.SETTINGS_SHARE_CODE,
},
[SCREENS.SETTINGS.EXIT_SURVEY.REASON]: {
path: ROUTES.SETTINGS_EXIT_SURVEY_REASON,
path: ROUTES.SETTINGS_EXIT_SURVEY_REASON.route,
},
[SCREENS.SETTINGS.EXIT_SURVEY.BOOK_CALL]: {
path: ROUTES.SETTINGS_EXIT_SURVERY_BOOK_CALL.route,
},
[SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE]: {
path: ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.route,
Expand Down
1 change: 1 addition & 0 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,7 @@ type SettingsNavigatorParamList = {
};
[SCREENS.KEYBOARD_SHORTCUTS]: undefined;
[SCREENS.SETTINGS.EXIT_SURVEY.REASON]: undefined;
[SCREENS.SETTINGS.EXIT_SURVEY.BOOK_CALL]: undefined;
[SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE]: {
[EXIT_SURVEY_REASON_FORM_INPUT_IDS.REASON]: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>;
backTo: Routes;
Expand Down
83 changes: 83 additions & 0 deletions src/pages/settings/ExitSurvey/ExitSurveyBookCall.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React from 'react';
import {View} from 'react-native';
import Button from '@components/Button';
import FixedFooter from '@components/FixedFooter';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@navigation/Navigation';
import * as Link from '@userActions/Link';
import * as Expensicons from '@src/components/Icon/Expensicons';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import ExitSurveyOffline from './ExitSurveyOffline';

function ExitSurveyBookCallPage() {
const {translate} = useLocalize();
const {isOffline} = useNetwork();
const styles = useThemeStyles();

return (
<ScreenWrapper testID={ExitSurveyBookCallPage.displayName}>
<HeaderWithBackButton
title={translate('exitSurvey.header')}
onBackButtonPress={() => Navigation.goBack()}
/>
<View style={[styles.flex1, styles.mh5]}>
{isOffline && <ExitSurveyOffline />}
{!isOffline && (
<>
<Text style={[styles.headerAnonymousFooter, styles.mt3]}>{translate('exitSurvey.bookACallTitle')}</Text>
<Text style={[styles.mt2]}>{translate('exitSurvey.bookACallTextTop')}</Text>
<View style={styles.mv4}>
{Object.values(CONST.EXIT_SURVEY.BENEFIT).map((value) => {
return (
<View
key={value}
style={[styles.flexRow]}
>
<View style={styles.liDot} />
<Text>{translate(`exitSurvey.benefits.${value}`)}</Text>
</View>
);
})}
</View>
<Text>{translate('exitSurvey.bookACallTextBottom')}</Text>
</>
)}
</View>
<FixedFooter>
<Button
style={styles.mb3}
large
text={translate('exitSurvey.noThanks')}
onPress={() => {
Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_REASON.getRoute(ROUTES.SETTINGS_EXIT_SURVERY_BOOK_CALL.route));
}}
isDisabled={isOffline}
/>
<Button
success
large
iconRight={Expensicons.NewWindow}
shouldShowRightIcon
isContentCentered
text={translate('exitSurvey.bookACall')}
pressOnEnter
onPress={() => {
Navigation.dismissModal();
Link.openExternalLink(CONST.EXIT_SURVEY.BOOK_MEETING_LINK);
}}
isDisabled={isOffline}
/>
</FixedFooter>
</ScreenWrapper>
);
}

ExitSurveyBookCallPage.displayName = 'ExitSurveyBookCallPage';

export default ExitSurveyBookCallPage;
18 changes: 12 additions & 6 deletions src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
import type {ExitSurveyReasonForm} from '@src/types/form/ExitSurveyReasonForm';
import EXIT_SURVEY_REASON_INPUT_IDS from '@src/types/form/ExitSurveyReasonForm';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import ExitSurveyOffline from './ExitSurveyOffline';

type ExitSurveyConfirmPageProps = StackScreenProps<SettingsNavigatorParamList, typeof SCREENS.SETTINGS.EXIT_SURVEY.CONFIRM>;
Expand All @@ -32,18 +33,21 @@ function ExitSurveyConfirmPage({route, navigation}: ExitSurveyConfirmPageProps)
const {translate} = useLocalize();
const {isOffline} = useNetwork();
const styles = useThemeStyles();
const [tryNewDot] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT);
const [exitReason] = useOnyx(ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM, {selector: (value: OnyxEntry<ExitSurveyReasonForm>) => value?.[EXIT_SURVEY_REASON_INPUT_IDS.REASON] ?? null});
const shouldShowQuickTips =
isEmptyObject(tryNewDot) || tryNewDot?.classicRedirect?.dismissed === true || (!isEmptyObject(tryNewDot) && tryNewDot?.classicRedirect?.dismissed === undefined);

const getBackToParam = useCallback(() => {
if (isOffline) {
return ROUTES.SETTINGS;
}
if (exitReason) {
return ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.getRoute(exitReason, ROUTES.SETTINGS_EXIT_SURVEY_REASON);
return ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.getRoute(exitReason, ROUTES.SETTINGS_EXIT_SURVEY_REASON.route);
}
return ROUTES.SETTINGS;
}, [exitReason, isOffline]);
const {backTo} = route.params;
const {backTo} = route.params || {};
useEffect(() => {
const newBackTo = getBackToParam();
if (backTo === newBackTo) {
Expand All @@ -57,7 +61,7 @@ function ExitSurveyConfirmPage({route, navigation}: ExitSurveyConfirmPageProps)
return (
<ScreenWrapper testID={ExitSurveyConfirmPage.displayName}>
<HeaderWithBackButton
title={translate('exitSurvey.header')}
title={translate(shouldShowQuickTips ? 'exitSurvey.goToExpensifyClassic' : 'exitSurvey.header')}
onBackButtonPress={() => Navigation.goBack()}
/>
<View style={[styles.flex1, styles.justifyContentCenter, styles.alignItemsCenter, styles.mh5]}>
Expand All @@ -69,16 +73,18 @@ function ExitSurveyConfirmPage({route, navigation}: ExitSurveyConfirmPageProps)
width={variables.mushroomTopHatWidth}
height={variables.mushroomTopHatHeight}
/>
<Text style={[styles.headerAnonymousFooter, styles.mt5, styles.textAlignCenter]}>{translate('exitSurvey.thankYou')}</Text>
<Text style={[styles.mt2, styles.textAlignCenter]}>{translate('exitSurvey.thankYouSubtitle')}</Text>
<Text style={[styles.headerAnonymousFooter, styles.mt5, styles.textAlignCenter]}>
{translate(shouldShowQuickTips ? 'exitSurvey.quickTip' : 'exitSurvey.thankYou')}
</Text>
<Text style={[styles.mt2, styles.textAlignCenter]}>{translate(shouldShowQuickTips ? 'exitSurvey.quickTipSubTitle' : 'exitSurvey.thankYouSubtitle')}</Text>
</>
)}
</View>
<FixedFooter>
<Button
success
large
text={translate('exitSurvey.goToExpensifyClassic')}
text={translate(shouldShowQuickTips ? 'exitSurvey.takeMeToExpensifyClassic' : 'exitSurvey.goToExpensifyClassic')}
pressOnEnter
onPress={() => {
ExitSurvey.switchToOldDot();
Expand Down
23 changes: 8 additions & 15 deletions src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, {useEffect, useMemo, useState} from 'react';
import {withOnyx} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import {useOnyx} from 'react-native-onyx';
import FormProvider from '@components/Form/FormProvider';
import InputWrapper from '@components/Form/InputWrapper';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
Expand All @@ -15,21 +16,18 @@ import * as ExitSurvey from '@userActions/ExitSurvey';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {ExitReason} from '@src/types/form/ExitSurveyReasonForm';
import type {ExitReason, ExitSurveyReasonForm} from '@src/types/form/ExitSurveyReasonForm';
import INPUT_IDS from '@src/types/form/ExitSurveyReasonForm';
import type {Errors} from '@src/types/onyx/OnyxCommon';
import ExitSurveyOffline from './ExitSurveyOffline';

type ExitSurveyReasonPageOnyxProps = {
draftReason: ExitReason | null;
};

function ExitSurveyReasonPage({draftReason}: ExitSurveyReasonPageOnyxProps) {
function ExitSurveyReasonPage() {
const {translate} = useLocalize();
const styles = useThemeStyles();
const {isOffline} = useNetwork();
const [draftReason] = useOnyx(ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM_DRAFT, {selector: (value: OnyxEntry<ExitSurveyReasonForm>) => value?.[INPUT_IDS.REASON] ?? null});

const [reason, setReason] = useState<ExitReason | null>(draftReason);
const [reason, setReason] = useState<ExitReason | null>(draftReason ?? null);
useEffect(() => {
// disabling lint because || is fine to use as a logical operator (as opposed to being used to define a default value)
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
Expand Down Expand Up @@ -69,7 +67,7 @@ function ExitSurveyReasonPage({draftReason}: ExitSurveyReasonPageOnyxProps) {
return;
}
ExitSurvey.saveExitReason(reason);
Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.getRoute(reason, ROUTES.SETTINGS_EXIT_SURVEY_REASON));
Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.getRoute(reason, ROUTES.SETTINGS_EXIT_SURVEY_REASON.route));
}}
submitButtonText={translate('common.next')}
shouldValidateOnBlur
Expand Down Expand Up @@ -97,9 +95,4 @@ function ExitSurveyReasonPage({draftReason}: ExitSurveyReasonPageOnyxProps) {

ExitSurveyReasonPage.displayName = 'ExitSurveyReasonPage';

export default withOnyx<ExitSurveyReasonPageOnyxProps, ExitSurveyReasonPageOnyxProps>({
draftReason: {
key: ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM_DRAFT,
selector: (value) => value?.[INPUT_IDS.REASON] ?? null,
},
})(ExitSurveyReasonPage);
export default ExitSurveyReasonPage;
2 changes: 1 addition & 1 deletion src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ function ExitSurveyResponsePage({route, navigation}: ExitSurveyResponsePageProps
const {isOffline} = useNetwork({
onReconnect: () => {
navigation.setParams({
backTo: ROUTES.SETTINGS_EXIT_SURVEY_REASON,
backTo: ROUTES.SETTINGS_EXIT_SURVEY_REASON.route,
});
},
});
Expand Down
12 changes: 10 additions & 2 deletions src/pages/settings/InitialSettingsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr
const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST);
const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY);
const [privatePersonalDetails] = useOnyx(ONYXKEYS.PRIVATE_PERSONAL_DETAILS);
const [tryNewDot] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT);

const network = useNetwork();
const theme = useTheme();
Expand All @@ -101,6 +102,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr
const [shouldShowSignoutConfirmModal, setShouldShowSignoutConfirmModal] = useState(false);

const freeTrialText = SubscriptionUtils.getFreeTrialText(policies);
const shouldOpenBookACall = tryNewDot?.classicRedirect?.dismissed === false;

useEffect(() => {
Wallet.openInitialSettingsPage();
Expand Down Expand Up @@ -242,7 +244,13 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr
}
: {
action() {
resetExitSurveyForm(() => Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_REASON));
resetExitSurveyForm(() => {
if (shouldOpenBookACall) {
Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVERY_BOOK_CALL.route);
return;
}
Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_CONFIRM.route);
});
},
}),
},
Expand Down Expand Up @@ -270,7 +278,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr
},
],
};
}, [styles.pt4, signOut, setInitialURL]);
}, [styles.pt4, signOut, setInitialURL, shouldOpenBookACall]);

/**
* Retuns JSX.Element with menu items
Expand Down
8 changes: 8 additions & 0 deletions src/styles/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5313,6 +5313,14 @@ const styles = (theme: ThemeColors) =>
borderColor: theme.border,
padding: 16,
},
liDot: {
width: 4,
height: 4,
borderRadius: 4,
backgroundColor: theme.text,
marginHorizontal: 8,
alignSelf: 'center',
},
} satisfies Styles);

type ThemeStyles = ReturnType<typeof styles>;
Expand Down

0 comments on commit 7c90011

Please sign in to comment.