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

[HybridApp] Add explanation modal #39074

Merged
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
2e99d95
Add explanation modal
mateuuszzzzz Mar 27, 2024
7926287
Add missing dot
mateuuszzzzz Mar 27, 2024
2716e4e
Fix spanish translations
mateuuszzzzz Mar 28, 2024
93c811d
Add logic for handling first time users from hybrid app
mateuuszzzzz Mar 28, 2024
14b5be5
Remove TO DO comment
mateuuszzzzz Mar 28, 2024
803fd67
Merge branch 'main' into hybrid-app-explanation-modal
mateuuszzzzz Apr 10, 2024
1826659
Add explanation modal trigger to TestToolMenu
mateuuszzzzz Apr 10, 2024
b00c83b
Update TestToolMenu logic
mateuuszzzzz Apr 10, 2024
9280612
Refactor code
mateuuszzzzz Apr 12, 2024
5bbeb12
Merge branch 'main' into hybrid-app-explanation-modal
mateuuszzzzz Apr 12, 2024
ca740c2
Resolve conflicts
mateuuszzzzz Apr 17, 2024
dbf674c
Resolve conflicts
mateuuszzzzz Apr 22, 2024
629be39
Add changes to properly open Explanation Modal and onboarding
mateuuszzzzz Jun 3, 2024
acf4a00
Add secondary description and clean code
mateuuszzzzz Jun 4, 2024
3d09521
Merge branch 'main' into hybrid-app-explanation-modal
mateuuszzzzz Jun 4, 2024
3f670e9
Add missing onConfirm to navigate to onboarding
mateuuszzzzz Jun 4, 2024
59d5870
Merge branch 'main' into hybrid-app-explanation-modal
mateuuszzzzz Jun 14, 2024
e1b4137
Add missing ROUTES
mateuuszzzzz Jun 14, 2024
d7f718f
Adjust most of the logic to backend changes
mateuuszzzzz Jun 14, 2024
d7a4110
Add optimistic and failure data, adjust API specification
mateuuszzzzz Jun 17, 2024
e93b4b7
Update Welcome.ts naming conventions
mateuuszzzzz Jun 17, 2024
3dc9af4
Remove old type
mateuuszzzzz Jun 17, 2024
320b5d6
Merge branch 'main' into hybrid-app-explanation-modal
mateuuszzzzz Jun 17, 2024
1cb3d13
Add fix for complicated union in ts
mateuuszzzzz Jun 17, 2024
3ca8cc0
Format code from diff
mateuuszzzzz Jun 17, 2024
c6b6763
Format TryNewDot
mateuuszzzzz Jun 17, 2024
fa916bb
Merge branch 'main' into hybrid-app-explanation-modal
mateuuszzzzz Jun 24, 2024
dba4182
Add comment
mateuuszzzzz Jun 24, 2024
8a18cde
Create separate onboarding flow entry point for HybridApp
mateuuszzzzz Jun 25, 2024
6653421
Remove redundant navigation
mateuuszzzzz Jun 25, 2024
e8fedf4
Change naming conventions and remove isNavigationReady
mateuuszzzzz Jun 25, 2024
e6d7448
Change naming convention
mateuuszzzzz Jun 25, 2024
f5d56a9
Merge branch 'main' into hybrid-app-explanation-modal
mateuuszzzzz Jun 25, 2024
017cdde
Handle string values
mateuuszzzzz Jun 25, 2024
22e6b6c
Merge branch 'main' into hybrid-app-explanation-modal
mateuuszzzzz Jun 25, 2024
5cfd1e1
Remove Log
mateuuszzzzz Jun 25, 2024
020572b
Merge branch 'main' into hybrid-app-explanation-modal
mateuuszzzzz Jun 26, 2024
0986551
Change condition
mateuuszzzzz Jun 26, 2024
a1e044e
Merge branch 'main' into hybrid-app-explanation-modal
mateuuszzzzz Jun 27, 2024
26a850b
Format types
mateuuszzzzz Jun 27, 2024
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
1 change: 1 addition & 0 deletions src/NAVIGATORS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ export default {
ONBOARDING_MODAL_NAVIGATOR: 'OnboardingModalNavigator',
FEATURE_TRANING_MODAL_NAVIGATOR: 'FeatureTrainingModalNavigator',
WELCOME_VIDEO_MODAL_NAVIGATOR: 'WelcomeVideoModalNavigator',
EXPLANATION_MODAL_NAVIGATOR: 'ExplanationModalNavigator',
FULL_SCREEN_NAVIGATOR: 'FullScreenNavigator',
} as const;
9 changes: 9 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ const ONYXKEYS = {
/** This NVP contains information about whether the onboarding flow was completed or not */
NVP_ONBOARDING: 'nvp_onboarding',

/** This NVP contains data associated with HybridApp */
NVP_TRYNEWDOT: 'nvp_tryNewDot',

/** Contains the user preference for the LHN priority mode */
NVP_PRIORITY_MODE: 'nvp_priorityMode',

Expand Down Expand Up @@ -154,6 +157,8 @@ const ONYXKEYS = {
/** Whether the user has dismissed the hold educational interstitial */
NVP_DISMISSED_HOLD_USE_EXPLANATION: 'nvp_dismissedHoldUseExplanation',

/** Whether the user has seen HybridApp explanation modal */
NVP_SEEN_NEW_USER_MODAL: 'nvp_seen_new_user_modal',
/** Store the state of the subscription */
NVP_PRIVATE_SUBSCRIPTION: 'nvp_private_subscription',

Expand Down Expand Up @@ -631,6 +636,9 @@ type OnyxValuesMapping = {
// NVP_ONBOARDING is an array for old users.
[ONYXKEYS.NVP_ONBOARDING]: Onboarding | [];

// ONYXKEYS.NVP_TRYNEWDOT is HybridApp onboarding data
[ONYXKEYS.NVP_TRYNEWDOT]: OnyxTypes.TryNewDot;

[ONYXKEYS.ACTIVE_CLIENTS]: string[];
[ONYXKEYS.DEVICE_ID]: string;
[ONYXKEYS.IS_SIDEBAR_LOADED]: boolean;
Expand Down Expand Up @@ -673,6 +681,7 @@ type OnyxValuesMapping = {
[ONYXKEYS.NVP_RECENT_WAYPOINTS]: OnyxTypes.RecentWaypoint[];
[ONYXKEYS.NVP_INTRO_SELECTED]: OnyxTypes.IntroSelected;
[ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES]: OnyxTypes.LastSelectedDistanceRates;
[ONYXKEYS.NVP_SEEN_NEW_USER_MODAL]: boolean;
[ONYXKEYS.PUSH_NOTIFICATIONS_ENABLED]: boolean;
[ONYXKEYS.PLAID_DATA]: OnyxTypes.PlaidData;
[ONYXKEYS.IS_PLAID_DISABLED]: boolean;
Expand Down
1 change: 1 addition & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,7 @@ const ROUTES = {
ONBOARDING_WORK: 'onboarding/work',
ONBOARDING_PURPOSE: 'onboarding/purpose',
WELCOME_VIDEO_ROOT: 'onboarding/welcome-video',
EXPLANATION_MODAL_ROOT: 'onboarding/explanation',

TRANSACTION_RECEIPT: {
route: 'r/:reportID/transaction/:transactionID/receipt',
Expand Down
4 changes: 4 additions & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,10 @@ const SCREENS = {
ROOT: 'Welcome_Video_Root',
},

EXPLANATION_MODAL: {
ROOT: 'Explanation_Modal_Root',
},

I_KNOW_A_TEACHER: 'I_Know_A_Teacher',
INTRO_SCHOOL_PRINCIPAL: 'Intro_School_Principal',
I_AM_A_TEACHER: 'I_Am_A_Teacher',
Expand Down
41 changes: 41 additions & 0 deletions src/components/ExplanationModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React, {useCallback} from 'react';
import useLocalize from '@hooks/useLocalize';
import Navigation from '@libs/Navigation/Navigation';
import variables from '@styles/variables';
import * as Welcome from '@userActions/Welcome';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import FeatureTrainingModal from './FeatureTrainingModal';

function ExplanationModal() {
const {translate} = useLocalize();

const onConfirm = useCallback(() => {
Welcome.completeHybridAppOnboarding();

// We need to check if standard NewDot onboarding is completed.
Welcome.isOnboardingFlowCompleted({
onNotCompleted: () => {
setTimeout(() => {
Navigation.isNavigationReady().then(() => {
Navigation.navigate(ROUTES.ONBOARDING_ROOT);
});
}, variables.welcomeVideoDelay);
},
});
}, []);

return (
<FeatureTrainingModal
title={translate('onboarding.explanationModal.title')}
description={translate('onboarding.explanationModal.description')}
secondaryDescription={translate('onboarding.explanationModal.secondaryDescription')}
confirmText={translate('footer.getStarted')}
videoURL={CONST.WELCOME_VIDEO_URL}
onConfirm={onConfirm}
/>
);
}

ExplanationModal.displayName = 'ExplanationModal';
export default ExplanationModal;
5 changes: 5 additions & 0 deletions src/components/FeatureTrainingModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ type FeatureTrainingModalProps = {
/** Describe what is showing */
description?: string;

/** Secondary description rendered with additional space */
secondaryDescription?: string;

/** Whether to show `Don't show me this again` option */
shouldShowDismissModalOption?: boolean;

Expand All @@ -73,6 +76,7 @@ function FeatureTrainingModal({
videoAspectRatio: videoAspectRatioProp,
title = '',
description = '',
secondaryDescription = '',
shouldShowDismissModalOption = false,
confirmText = '',
onConfirm = () => {},
Expand Down Expand Up @@ -199,6 +203,7 @@ function FeatureTrainingModal({
<View style={[shouldUseNarrowLayout ? [styles.gap1, styles.mb8] : [styles.mb10]]}>
<Text style={[styles.textHeadlineH1]}>{title}</Text>
<Text style={styles.textSupporting}>{description}</Text>
{secondaryDescription.length > 0 && <Text style={[styles.textSupporting, styles.mt4]}>{secondaryDescription}</Text>}
</View>
)}
{shouldShowDismissModalOption && (
Expand Down
1 change: 0 additions & 1 deletion src/components/OnboardingWelcomeVideo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,4 @@ function OnboardingWelcomeVideo() {
}

OnboardingWelcomeVideo.displayName = 'OnboardingWelcomeVideo';

export default OnboardingWelcomeVideo;
24 changes: 24 additions & 0 deletions src/components/TestToolMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as ApiUtils from '@libs/ApiUtils';
import Navigation from '@libs/Navigation/Navigation';
import variables from '@styles/variables';
import * as Network from '@userActions/Network';
import * as Report from '@userActions/Report';
import * as Session from '@userActions/Session';
import * as User from '@userActions/User';
import CONFIG from '@src/CONFIG';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {Network as NetworkOnyx, User as UserOnyx} from '@src/types/onyx';
import Button from './Button';
import {withNetwork} from './OnyxProvider';
Expand All @@ -30,6 +35,7 @@ const USER_DEFAULT: UserOnyx = {shouldUseStagingServer: undefined, isSubscribedT
function TestToolMenu({user = USER_DEFAULT, network}: TestToolMenuProps) {
const shouldUseStagingServer = user?.shouldUseStagingServer ?? ApiUtils.isUsingStagingApi();
const styles = useThemeStyles();
const {isSmallScreenWidth} = useWindowDimensions();
const {translate} = useLocalize();

return (
Expand Down Expand Up @@ -88,6 +94,24 @@ function TestToolMenu({user = USER_DEFAULT, network}: TestToolMenuProps) {
onPress={() => Session.invalidateCredentials()}
/>
</TestToolRow>
{/* Navigate to the Explanation Modal. This button is temporary to test Explanation Modal flow without HybridApp native module. */}
<TestToolRow title="Explanation modal">
<Button
small
text="Navigate"
onPress={() => {
Navigation.dismissModal();
if (isSmallScreenWidth) {
Navigation.navigate(ROUTES.HOME);
} else {
Report.navigateToConciergeChat();
}
setTimeout(() => {
Navigation.navigate(ROUTES.EXPLANATION_MODAL_ROOT);
}, variables.welcomeVideoDelay);
}}
/>
</TestToolRow>
</>
);
}
Expand Down
6 changes: 6 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1413,6 +1413,12 @@ export default {
notYou: ({user}: NotYouParams) => `Not ${user}?`,
},
onboarding: {
welcome: 'Welcome!',
explanationModal: {
title: 'Welcome to Expensify',
description: 'Request and send money is just as easy as sending a message. The new era of expensing is upon us.',
secondaryDescription: 'To switch back to Expensify Classic, just tap your profile picture > Go to Expensify Classic.',
},
welcomeVideo: {
title: 'Welcome to Expensify',
description: 'One app to handle all your business and personal spend in a chat. Built for your business, your team, and your friends.',
Expand Down
6 changes: 6 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1414,6 +1414,12 @@ export default {
notYou: ({user}: NotYouParams) => `¿No eres ${user}?`,
},
onboarding: {
welcome: '¡Bienvenido!',
explanationModal: {
title: 'Bienvenido a Expensify',
description: 'Recibir pagos es tan fácil como mandar un mensaje',
secondaryDescription: 'Para volver a Expensify Classic, simplemente haz click en tu foto de perfil > Ir a Expensify Classic.',
},
welcomeVideo: {
title: 'Bienvenido a Expensify',
description: 'Una aplicación para gestionar todos tus gastos de empresa y personales en un chat. Pensada para tu empresa, tu equipo y tus amigos.',
Expand Down
3 changes: 3 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type {ValueOf} from 'type-fest';
import type CONST from '@src/CONST';
import type { EmptyObject } from '@src/types/utils/EmptyObject';
import type * as Parameters from './parameters';
import type SignInUserParams from './parameters/SignInUserParams';
import type UpdateBeneficialOwnersForBankAccountParams from './parameters/UpdateBeneficialOwnersForBankAccountParams';
Expand Down Expand Up @@ -141,6 +142,7 @@ const WRITE_COMMANDS = {
REOPEN_TASK: 'ReopenTask',
COMPLETE_TASK: 'CompleteTask',
COMPLETE_GUIDED_SETUP: 'CompleteGuidedSetup',
COMPLETE_HYBRID_APP_ONBOARDING: 'CompleteHybridAppOnboarding',
SET_NAME_VALUE_PAIR: 'SetNameValuePair',
SET_REPORT_FIELD: 'Report_SetFields',
DELETE_REPORT_FIELD: 'RemoveReportField',
Expand Down Expand Up @@ -361,6 +363,7 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.REOPEN_TASK]: Parameters.ReopenTaskParams;
[WRITE_COMMANDS.COMPLETE_TASK]: Parameters.CompleteTaskParams;
[WRITE_COMMANDS.COMPLETE_GUIDED_SETUP]: Parameters.CompleteGuidedSetupParams;
[WRITE_COMMANDS.COMPLETE_HYBRID_APP_ONBOARDING]: EmptyObject;
[WRITE_COMMANDS.SET_NAME_VALUE_PAIR]: Parameters.SetNameValuePairParams;
[WRITE_COMMANDS.SET_REPORT_FIELD]: Parameters.SetReportFieldParams;
[WRITE_COMMANDS.SET_REPORT_NAME]: Parameters.SetReportNameParams;
Expand Down
6 changes: 6 additions & 0 deletions src/libs/Navigation/AppNavigator/AuthScreens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import createCustomStackNavigator from './createCustomStackNavigator';
import defaultScreenOptions from './defaultScreenOptions';
import getRootNavigatorScreenOptions from './getRootNavigatorScreenOptions';
import BottomTabNavigator from './Navigators/BottomTabNavigator';
import ExplanationModalNavigator from './Navigators/ExplanationModalNavigator';
import FeatureTrainingModalNavigator from './Navigators/FeatureTrainingModalNavigator';
import FullScreenNavigator from './Navigators/FullScreenNavigator';
import LeftModalNavigator from './Navigators/LeftModalNavigator';
Expand Down Expand Up @@ -416,6 +417,11 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
options={screenOptions.fullScreen}
component={DesktopSignInRedirectPage}
/>
<RootStack.Screen
name={NAVIGATORS.EXPLANATION_MODAL_NAVIGATOR}
options={onboardingModalScreenOptions}
component={ExplanationModalNavigator}
/>
<RootStack.Screen
name={NAVIGATORS.FEATURE_TRANING_MODAL_NAVIGATOR}
options={onboardingModalScreenOptions}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {createStackNavigator} from '@react-navigation/stack';
import React from 'react';
import {View} from 'react-native';
import NoDropZone from '@components/DragAndDrop/NoDropZone';
import ExplanationModal from '@components/ExplanationModal';
import type {ExplanationModalNavigatorParamList} from '@libs/Navigation/types';
import SCREENS from '@src/SCREENS';

const Stack = createStackNavigator<ExplanationModalNavigatorParamList>();

function ExplanationModalNavigator() {
return (
<NoDropZone>
<View>
<Stack.Navigator screenOptions={{headerShown: false, animationEnabled: true}}>
<Stack.Screen
name={SCREENS.EXPLANATION_MODAL.ROOT}
component={ExplanationModal}
/>
</Stack.Navigator>
</View>
</NoDropZone>
);
}

ExplanationModalNavigator.displayName = 'ExplanationModalNavigator';

export default ExplanationModalNavigator;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {useNavigation, useNavigationState} from '@react-navigation/native';
import React, {memo, useCallback, useEffect} from 'react';
import {View} from 'react-native';
import {NativeModules, View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import Icon from '@components/Icon';
Expand Down Expand Up @@ -52,6 +52,11 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps
return;
}

// HybridApp has own entry point when we decide whether to display onboarding and explanation modal.
if (NativeModules.HybridAppModule) {
return;
}

Welcome.isOnboardingFlowCompleted({onNotCompleted: () => Navigation.navigate(ROUTES.ONBOARDING_ROOT)});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isLoadingApp]);
Expand Down
8 changes: 8 additions & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
},
},
},
[NAVIGATORS.EXPLANATION_MODAL_NAVIGATOR]: {
screens: {
[SCREENS.EXPLANATION_MODAL.ROOT]: {
path: ROUTES.EXPLANATION_MODAL_ROOT,
exact: true,
},
},
},
[NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR]: {
path: ROUTES.ONBOARDING_ROOT,
initialRouteName: SCREENS.ONBOARDING.PURPOSE,
Expand Down
6 changes: 6 additions & 0 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,10 @@ type WelcomeVideoModalNavigatorParamList = {
[SCREENS.WELCOME_VIDEO.ROOT]: undefined;
};

type ExplanationModalNavigatorParamList = {
[SCREENS.EXPLANATION_MODAL.ROOT]: undefined;
};

type BottomTabNavigatorParamList = {
[SCREENS.HOME]: {policyID?: string};
[SCREENS.SEARCH.BOTTOM_TAB]: {
Expand Down Expand Up @@ -944,6 +948,7 @@ type AuthScreensParamList = CentralPaneScreensParamList &
[NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR]: NavigatorScreenParams<OnboardingModalNavigatorParamList>;
[NAVIGATORS.FEATURE_TRANING_MODAL_NAVIGATOR]: NavigatorScreenParams<FeatureTrainingNavigatorParamList>;
[NAVIGATORS.WELCOME_VIDEO_MODAL_NAVIGATOR]: NavigatorScreenParams<WelcomeVideoModalNavigatorParamList>;
[NAVIGATORS.EXPLANATION_MODAL_NAVIGATOR]: NavigatorScreenParams<ExplanationModalNavigatorParamList>;
[SCREENS.DESKTOP_SIGN_IN_REDIRECT]: undefined;
[SCREENS.TRANSACTION_RECEIPT]: {
reportID: string;
Expand Down Expand Up @@ -990,6 +995,7 @@ export type {
DetailsNavigatorParamList,
EditRequestNavigatorParamList,
EnablePaymentsNavigatorParamList,
ExplanationModalNavigatorParamList,
FlagCommentNavigatorParamList,
FullScreenName,
FullScreenNavigatorParamList,
Expand Down
Loading
Loading