Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Binary file added assets/images/fast-track-cover.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5091,6 +5091,11 @@ const CONST = {
},
SELECTION_LIST_WITH_MODAL_TEST_ID: 'selectionListWithModalMenuItem',

IMAGE_TEST_ID: 'Image',
IMAGE_SVG_TEST_ID: 'ImageSVG',
VIDEO_PLAYER_TEST_ID: 'VideoPlayer',
LOTTIE_VIEW_TEST_ID: 'LottieView',

CHAT_HEADER_LOADER_HEIGHT: 36,

HORIZONTAL_SPACER: {
Expand Down Expand Up @@ -5134,6 +5139,7 @@ const CONST = {
VIDEO_URL: `${CLOUDFRONT_URL}/videos/guided-setup-track-business-v2.mp4`,
LEARN_MORE_LINK: `${USE_EXPENSIFY_URL}/track-expenses`,
},
TEST_DRIVE_COVER_ASPECT_RATIO: 500 / 300,
},

/**
Expand Down
1 change: 1 addition & 0 deletions src/NAVIGATORS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export default {
WELCOME_VIDEO_MODAL_NAVIGATOR: 'WelcomeVideoModalNavigator',
EXPLANATION_MODAL_NAVIGATOR: 'ExplanationModalNavigator',
MIGRATED_USER_MODAL_NAVIGATOR: 'MigratedUserModalNavigator',
TEST_DRIVE_MODAL_NAVIGATOR: 'TestDriveModalNavigator',
REPORTS_SPLIT_NAVIGATOR: 'ReportsSplitNavigator',
SETTINGS_SPLIT_NAVIGATOR: 'SettingsSplitNavigator',
WORKSPACE_SPLIT_NAVIGATOR: 'WorkspaceSplitNavigator',
Expand Down
1 change: 1 addition & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1893,6 +1893,7 @@ const ROUTES = {
},
WELCOME_VIDEO_ROOT: 'onboarding/welcome-video',
EXPLANATION_MODAL_ROOT: 'onboarding/explanation',
TEST_DRIVE_MODAL_ROOT: 'onboarding/test-drive',
WORKSPACE_CONFIRMATION: {
route: 'workspace/confirmation',
getRoute: (backTo?: string) => getUrlWithBackToParam(`workspace/confirmation`, backTo),
Expand Down
4 changes: 4 additions & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,10 @@ const SCREENS = {
ROOT: 'MigratedUserWelcomeModal_Root',
},

TEST_DRIVE_MODAL: {
ROOT: 'TestDrive_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
47 changes: 31 additions & 16 deletions src/components/FeatureTrainingModal.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type {VideoReadyForDisplayEvent} from 'expo-av';
import type {ImageContentFit} from 'expo-image';
import React, {useCallback, useEffect, useLayoutEffect, useState} from 'react';
import {InteractionManager, View} from 'react-native';
import type {StyleProp, ViewStyle} from 'react-native';
import {Image, InteractionManager, View} from 'react-native';
import type {ImageResizeMode, ImageSourcePropType, StyleProp, ViewStyle} from 'react-native';
import {GestureHandlerRootView} from 'react-native-gesture-handler';
import type {MergeExclusive} from 'type-fest';
import useLocalize from '@hooks/useLocalize';
Expand Down Expand Up @@ -96,6 +96,9 @@ type BaseFeatureTrainingModalProps = {

/** Whether to disable the modal */
isModalDisabled?: boolean;

/** Whether the modal image is a SVG */
shouldRenderSVG?: boolean;
};

type FeatureTrainingModalVideoProps = {
Expand Down Expand Up @@ -152,6 +155,7 @@ function FeatureTrainingModal({
imageWidth,
imageHeight,
isModalDisabled = true,
shouldRenderSVG = true,
}: FeatureTrainingModalProps) {
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
Expand Down Expand Up @@ -215,14 +219,23 @@ function FeatureTrainingModal({
(!!videoURL || !!image) && {aspectRatio},
]}
>
{!!image && (
<ImageSVG
src={image}
contentFit={contentFitImage}
width={imageWidth}
height={imageHeight}
/>
)}
{!!image &&
(shouldRenderSVG ? (
<ImageSVG
src={image}
contentFit={contentFitImage}
width={imageWidth}
height={imageHeight}
testID={CONST.IMAGE_SVG_TEST_ID}
/>
) : (
<Image
source={image as ImageSourcePropType}
resizeMode={contentFitImage as ImageResizeMode}
style={styles.featureTrainingModalImage}
testID={CONST.IMAGE_TEST_ID}
/>
))}
{!!videoURL && videoStatus === 'video' && (
<GestureHandlerRootView>
<VideoPlayer
Expand Down Expand Up @@ -250,23 +263,25 @@ function FeatureTrainingModal({
</View>
);
}, [
image,
imageHeight,
imageWidth,
contentFitImage,
illustrationAspectRatio,
styles.w100,
styles.featureTrainingModalImage,
styles.onboardingVideoPlayer,
styles.flex1,
styles.alignItemsCenter,
styles.justifyContentCenter,
styles.h100,
videoStatus,
illustrationInnerContainerStyle,
videoURL,
image,
shouldRenderSVG,
contentFitImage,
imageWidth,
imageHeight,
videoStatus,
animationStyle,
animation,
shouldUseNarrowLayout,
illustrationInnerContainerStyle,
]);

const toggleWillShowAgain = useCallback(() => setWillShowAgain((prevWillShowAgain) => !prevWillShowAgain), []);
Expand Down
3 changes: 3 additions & 0 deletions src/components/ImageSVG/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ type ImageSVGProps = {

/** The preserveAspectRatio attribute indicates how an element with a viewBox providing a given aspect ratio must fit into a viewport with a different aspect ratio. */
preserveAspectRatio?: string;

/** TestID for test */
testID?: string;
};

export default ImageSVGProps;
8 changes: 7 additions & 1 deletion src/components/Lottie/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,12 @@ function Lottie({source, webStyle, shouldLoadAfterInteractions, ...props}: Props
// 1. heavy rendering, see issues: https://github.com/Expensify/App/issues/34696 and https://github.com/Expensify/App/issues/47273
// 2. lag on react navigation transitions, see issue: https://github.com/Expensify/App/issues/44812
if (isError || appState.isBackground || !animationFile || splashScreenState !== CONST.BOOT_SPLASH_STATE.HIDDEN || (!isInteractionComplete && shouldLoadAfterInteractions)) {
return <View style={[aspectRatioStyle, props.style]} />;
return (
<View
style={[aspectRatioStyle, props.style]}
testID={CONST.LOTTIE_VIEW_TEST_ID}
/>
);
}

return (
Expand All @@ -116,6 +121,7 @@ function Lottie({source, webStyle, shouldLoadAfterInteractions, ...props}: Props
style={[aspectRatioStyle, props.style]}
webStyle={{...aspectRatioStyle, ...webStyle}}
onAnimationFailure={() => setIsError(true)}
testID={CONST.LOTTIE_VIEW_TEST_ID}
/>
);
}
Expand Down
38 changes: 38 additions & 0 deletions src/components/TestDriveModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import FastTrack from '@assets/images/fast-track-cover.jpg';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import CONST from '@src/CONST';
import FeatureTrainingModal from './FeatureTrainingModal';

function TestDriveModal() {
const styles = useThemeStyles();
const {translate} = useLocalize();

const closeModal = () => {
Navigation.dismissModal();
};

const navigateTestDriveDemo = () => {};

return (
<FeatureTrainingModal
image={FastTrack}
illustrationOuterContainerStyle={styles.p0}
illustrationAspectRatio={CONST.FEATURE_TRAINING.TEST_DRIVE_COVER_ASPECT_RATIO}
title={translate('testDrive.modal.title')}
description={translate('testDrive.modal.description')}
helpText={translate('testDrive.modal.helpText')}
confirmText={translate('testDrive.modal.confirmText')}
onHelp={closeModal}
onConfirm={navigateTestDriveDemo}
shouldRenderSVG={false}
modalInnerContainerStyle={styles.testDriveModalContainer}
/>
);
}

TestDriveModal.displayName = 'TestDriveModal';

export default TestDriveModal;
1 change: 1 addition & 0 deletions src/components/VideoPlayer/BaseVideoPlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,7 @@ function BaseVideoPlayer({
onError={() => {
setHasError(true);
}}
testID={CONST.VIDEO_PLAYER_TEST_ID}
/>
</View>
)}
Expand Down
8 changes: 8 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6226,6 +6226,14 @@ const translations = {
talkToConcierge: 'Talk to Concierge',
hangUp: 'Hang up',
},
testDrive: {
modal: {
title: 'Take us for a test drive',
description: 'Take a quick product tour to get up to speed fast. No pit stops required!',
confirmText: 'Start test drive',
helpText: 'Skip',
},
},
};

export default translations satisfies TranslationDeepObject<typeof translations>;
8 changes: 8 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6749,6 +6749,14 @@ const translations = {
talkToConcierge: 'Habla con Concierge',
hangUp: 'Colgar',
},
testDrive: {
modal: {
title: 'Haz una prueba con nosotros',
description: 'Haz un recorrido rápido por el producto para ponerte al día rápidamente. ¡No se requieren paradas!',
confirmText: 'Iniciar prueba',
helpText: 'Omitir',
},
},
};

export default translations satisfies TranslationDeepObject<typeof en>;
6 changes: 6 additions & 0 deletions src/libs/Navigation/AppNavigator/AuthScreens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import LeftModalNavigator from './Navigators/LeftModalNavigator';
import MigratedUserWelcomeModalNavigator from './Navigators/MigratedUserWelcomeModalNavigator';
import OnboardingModalNavigator from './Navigators/OnboardingModalNavigator';
import RightModalNavigator from './Navigators/RightModalNavigator';
import TestDriveModalNavigator from './Navigators/TestDriveModalNavigator';
import WelcomeVideoModalNavigator from './Navigators/WelcomeVideoModalNavigator';
import useRootNavigatorScreenOptions from './useRootNavigatorScreenOptions';

Expand Down Expand Up @@ -630,6 +631,11 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
options={rootNavigatorScreenOptions.basicModalNavigator}
component={MigratedUserWelcomeModalNavigator}
/>
<RootStack.Screen
name={NAVIGATORS.TEST_DRIVE_MODAL_NAVIGATOR}
options={rootNavigatorScreenOptions.basicModalNavigator}
component={TestDriveModalNavigator}
/>
<RootStack.Screen
name={NAVIGATORS.FEATURE_TRANING_MODAL_NAVIGATOR}
options={rootNavigatorScreenOptions.basicModalNavigator}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';
import {View} from 'react-native';
import NoDropZone from '@components/DragAndDrop/NoDropZone';
import TestDriveModal from '@components/TestDriveModal';
import createPlatformStackNavigator from '@libs/Navigation/PlatformStackNavigation/createPlatformStackNavigator';
import Animations from '@libs/Navigation/PlatformStackNavigation/navigationOptions/animation';
import type {TestDriveModalNavigatorParamList} from '@libs/Navigation/types';
import SCREENS from '@src/SCREENS';

const Stack = createPlatformStackNavigator<TestDriveModalNavigatorParamList>();

function TestDriveModalNavigator() {
return (
<NoDropZone>
<View>
<Stack.Navigator screenOptions={{headerShown: false, animation: Animations.SLIDE_FROM_RIGHT}}>
<Stack.Screen
name={SCREENS.TEST_DRIVE_MODAL.ROOT}
component={TestDriveModal}
/>
</Stack.Navigator>
</View>
</NoDropZone>
);
}

TestDriveModalNavigator.displayName = 'TestDriveModalNavigator';

export default TestDriveModalNavigator;
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const MODAL_ROUTES_TO_DISMISS: string[] = [
NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR,
NAVIGATORS.FEATURE_TRANING_MODAL_NAVIGATOR,
NAVIGATORS.SHARE_MODAL_NAVIGATOR,
NAVIGATORS.TEST_DRIVE_MODAL_NAVIGATOR,
SCREENS.NOT_FOUND,
SCREENS.ATTACHMENTS,
SCREENS.TRANSACTION_RECEIPT,
Expand Down
9 changes: 9 additions & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@ const config: LinkingOptions<RootNavigatorParamList>['config'] = {
},
},

[NAVIGATORS.TEST_DRIVE_MODAL_NAVIGATOR]: {
screens: {
[SCREENS.TEST_DRIVE_MODAL.ROOT]: {
path: ROUTES.TEST_DRIVE_MODAL_ROOT,
exact: true,
},
},
},

[NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR]: {
// Don't set the initialRouteName, because when the user continues from the last visited onboarding page,
// the onboarding purpose page will be briefly visible.
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 @@ -1778,6 +1778,10 @@ type MigratedUserModalNavigatorParamList = {
[SCREENS.MIGRATED_USER_WELCOME_MODAL.ROOT]: undefined;
};

type TestDriveModalNavigatorParamList = {
[SCREENS.TEST_DRIVE_MODAL.ROOT]: undefined;
};

type SharedScreensParamList = {
[NAVIGATORS.REPORTS_SPLIT_NAVIGATOR]: NavigatorScreenParams<ReportsSplitNavigatorParamList>;
[SCREENS.TRANSITION_BETWEEN_APPS]: {
Expand Down Expand Up @@ -1859,6 +1863,7 @@ type AuthScreensParamList = SharedScreensParamList & {
[NAVIGATORS.WELCOME_VIDEO_MODAL_NAVIGATOR]: NavigatorScreenParams<WelcomeVideoModalNavigatorParamList>;
[NAVIGATORS.EXPLANATION_MODAL_NAVIGATOR]: NavigatorScreenParams<ExplanationModalNavigatorParamList>;
[NAVIGATORS.MIGRATED_USER_MODAL_NAVIGATOR]: NavigatorScreenParams<MigratedUserModalNavigatorParamList>;
[NAVIGATORS.TEST_DRIVE_MODAL_NAVIGATOR]: NavigatorScreenParams<TestDriveModalNavigatorParamList>;
[NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR]: NavigatorScreenParams<SearchFullscreenNavigatorParamList>;
[SCREENS.DESKTOP_SIGN_IN_REDIRECT]: undefined;
[SCREENS.TRANSACTION_RECEIPT]: {
Expand Down Expand Up @@ -2054,6 +2059,7 @@ export type {
WorkspaceConfirmationNavigatorParamList,
TwoFactorAuthNavigatorParamList,
ConsoleNavigatorParamList,
TestDriveModalNavigatorParamList,
WorkspaceScreenName,
SettingsTabScreenName,
};
14 changes: 14 additions & 0 deletions src/styles/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5662,6 +5662,20 @@ const styles = (theme: ThemeColors) =>
left: 0,
right: 0,
},

testDriveModalContainer: {
// On small/medium screens, we need to remove the top padding
paddingTop: 0,
// On medium screens, we need to prevent the modal from becoming too big
maxWidth: 500,
},

featureTrainingModalImage: {
width: '100%',
height: '100%',
borderTopLeftRadius: variables.componentBorderRadiusLarge,
borderTopRightRadius: variables.componentBorderRadiusLarge,
},
} satisfies Styles);

type ThemeStyles = ReturnType<typeof styles>;
Expand Down
Loading
Loading