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

[HOLD Pending BE API] Frontend: Private Domain Onboarding Check #54186

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,7 @@ const CONST = {
CATEGORY_AND_TAG_APPROVERS: 'categoryAndTagApprovers',
PER_DIEM: 'newDotPerDiem',
PRODUCT_TRAINING: 'productTraining',
PRIVATE_DOMAIN_ONBOARDING_CHECK: 'privateDomainOnboardingCheck',
},
BUTTON_STATES: {
DEFAULT: 'default',
Expand Down
3 changes: 3 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,8 @@ const ONYXKEYS = {
RULES_MAX_EXPENSE_AGE_FORM_DRAFT: 'rulesMaxExpenseAgeFormDraft',
DEBUG_DETAILS_FORM: 'debugDetailsForm',
DEBUG_DETAILS_FORM_DRAFT: 'debugDetailsFormDraft',
ONBOARDING_WORK_EMAIL_FORM: 'onboardingWorkEmailForm',
ONBOARDING_WORK_EMAIL_FORM_DRAFT: 'onboardingWorkEmailFormDraft',
WORKSPACE_PER_DIEM_FORM: 'workspacePerDiemForm',
WORKSPACE_PER_DIEM_FORM_DRAFT: 'workspacePerDiemFormDraft',
},
Expand Down Expand Up @@ -816,6 +818,7 @@ type OnyxFormValuesMapping = {
[ONYXKEYS.FORMS.RULES_MAX_EXPENSE_AGE_FORM]: FormTypes.RulesMaxExpenseAgeForm;
[ONYXKEYS.FORMS.SEARCH_SAVED_SEARCH_RENAME_FORM]: FormTypes.SearchSavedSearchRenameForm;
[ONYXKEYS.FORMS.DEBUG_DETAILS_FORM]: FormTypes.DebugReportForm | FormTypes.DebugReportActionForm | FormTypes.DebugTransactionForm | FormTypes.DebugTransactionViolationForm;
[ONYXKEYS.FORMS.ONBOARDING_WORK_EMAIL_FORM]: FormTypes.OnboardingWorkEmailForm;
[ONYXKEYS.FORMS.WORKSPACE_PER_DIEM_FORM]: FormTypes.WorkspacePerDiemForm;
};

Expand Down
8 changes: 8 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1426,6 +1426,14 @@ const ROUTES = {
route: 'onboarding/join-workspaces',
getRoute: (backTo?: string) => getUrlWithBackToParam(`onboarding/join-workspaces`, backTo),
},
ONBOARDING_WORK_EMAIL: {
route: 'onboarding/work-email',
getRoute: (backTo?: string) => getUrlWithBackToParam(`onboarding/work-email`, backTo),
},
ONBOARDING_WORK_EMAIL_VALIDATION: {
route: 'onboarding/work-email-validation',
getRoute: (backTo?: string) => getUrlWithBackToParam(`onboarding/work-email-validation`, backTo),
},
WELCOME_VIDEO_ROOT: 'onboarding/welcome-video',
EXPLANATION_MODAL_ROOT: 'onboarding/explanation',
MIGRATED_USER_WELCOME_MODAL: 'onboarding/migrated-user-welcome',
Expand Down
2 changes: 2 additions & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,8 @@ const SCREENS = {
EMPLOYEES: 'Onboarding_Employees',
ACCOUNTING: 'Onboarding_Accounting',
WORKSPACES: 'Onboarding_Workspaces',
WORK_EMAIL: 'Onboarding_Work_Email',
WORK_EMAIL_VALIDATION: 'Onboarding_Work_Email_Validation',
},

WELCOME_VIDEO: {
Expand Down
6 changes: 6 additions & 0 deletions src/components/Form/FormWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ type FormWrapperProps = ChildrenProps &

/** Callback to submit the form */
onSubmit: () => void;

/** should render the extra button above submit button */
shouldRenderFooterAboveSubmit?: boolean;
};

function FormWrapper({
Expand All @@ -57,6 +60,7 @@ function FormWrapper({
shouldHideFixErrorsAlert = false,
disablePressOnEnter = false,
isSubmitDisabled = false,
shouldRenderFooterAboveSubmit = false,
}: FormWrapperProps) {
const styles = useThemeStyles();
const {paddingBottom: safeAreaInsetPaddingBottom} = useStyledSafeAreaInsets();
Expand Down Expand Up @@ -122,6 +126,7 @@ function FormWrapper({
isSubmitActionDangerous={isSubmitActionDangerous}
disablePressOnEnter={disablePressOnEnter}
enterKeyEventListenerPriority={1}
shouldRenderFooterAboveSubmit={shouldRenderFooterAboveSubmit}
/>
)}
</FormElement>
Expand Down Expand Up @@ -151,6 +156,7 @@ function FormWrapper({
enabledWhenOffline,
isSubmitActionDangerous,
disablePressOnEnter,
shouldRenderFooterAboveSubmit,
],
);

Expand Down
3 changes: 3 additions & 0 deletions src/components/Form/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ type FormProps<TFormID extends OnyxFormKey = OnyxFormKey> = {

/** Disable press on enter for submit button */
disablePressOnEnter?: boolean;

/** Render extra button above submit button */
shouldRenderFooterAboveSubmit?: boolean;
};

type FormRef<TFormID extends OnyxFormKey = OnyxFormKey> = {
Expand Down
7 changes: 6 additions & 1 deletion src/components/FormAlertWithSubmitButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ type FormAlertWithSubmitButtonProps = {

/** The priority to assign the enter key event listener to buttons. 0 is the highest priority. */
enterKeyEventListenerPriority?: number;

/** should render the extra button above submit button */
shouldRenderFooterAboveSubmit?: boolean;
};

function FormAlertWithSubmitButton({
Expand All @@ -83,6 +86,7 @@ function FormAlertWithSubmitButton({
useSmallerSubmitButtonSize = false,
errorMessageStyle,
enterKeyEventListenerPriority = 0,
shouldRenderFooterAboveSubmit = false,
}: FormAlertWithSubmitButtonProps) {
const styles = useThemeStyles();
const style = [!footerContent ? {} : styles.mb3, buttonStyles];
Expand All @@ -104,6 +108,7 @@ function FormAlertWithSubmitButton({
>
{(isOffline: boolean | undefined) => (
<View>
{shouldRenderFooterAboveSubmit && footerContent}
{isOffline && !enabledWhenOffline ? (
<Button
success
Expand All @@ -130,7 +135,7 @@ function FormAlertWithSubmitButton({
large={!useSmallerSubmitButtonSize}
/>
)}
{footerContent}
{!shouldRenderFooterAboveSubmit && footerContent}
</View>
)}
</FormAlertWrapper>
Expand Down
9 changes: 7 additions & 2 deletions src/hooks/useOnboardingFlow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as OnboardingFlow from '@userActions/Welcome/OnboardingFlow';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
import usePermissions from './usePermissions';

/**
* Hook to handle redirection to the onboarding flow based on the user's onboarding status
Expand All @@ -29,6 +30,9 @@ function useOnboardingFlowRouter() {
const isPrivateDomain = Session.isUserOnPrivateDomain();

const [isSingleNewDotEntry, isSingleNewDotEntryMetadata] = useOnyx(ONYXKEYS.IS_SINGLE_NEW_DOT_ENTRY);

const {canUsePrivateDomainOnboardingCheck} = usePermissions();

useEffect(() => {
// This should delay opening the onboarding modal so it does not interfere with the ongoing ReportScreen params changes
InteractionManager.runAfterInteractions(() => {
Expand Down Expand Up @@ -62,13 +66,13 @@ function useOnboardingFlowRouter() {
// But if the hybrid app onboarding is completed, but NewDot onboarding is not completed, we start NewDot onboarding flow
// This is a special case when user created an account from NewDot without finishing the onboarding flow and then logged in from OldDot
if (isHybridAppOnboardingCompleted === true && isOnboardingCompleted === false) {
OnboardingFlow.startOnboardingFlow(isPrivateDomain);
OnboardingFlow.startOnboardingFlow(isPrivateDomain, canUsePrivateDomainOnboardingCheck);
}
}

// If the user is not transitioning from OldDot to NewDot, we should start NewDot onboarding flow if it's not completed yet
if (!NativeModules.HybridAppModule && isOnboardingCompleted === false) {
OnboardingFlow.startOnboardingFlow(isPrivateDomain);
OnboardingFlow.startOnboardingFlow(isPrivateDomain, canUsePrivateDomainOnboardingCheck);
}
});
}, [
Expand All @@ -83,6 +87,7 @@ function useOnboardingFlowRouter() {
dismissedProductTraining?.migratedUserWelcomeModal,
dismissedProductTraining,
isPrivateDomain,
canUsePrivateDomainOnboardingCheck,
]);

return {isOnboardingCompleted, isHybridAppOnboardingCompleted};
Expand Down
11 changes: 11 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,7 @@ const translations = {
skip: 'Skip',
chatWithAccountManager: ({accountManagerDisplayName}: ChatWithAccountManagerParams) => `Need something specific? Chat with your account manager, ${accountManagerDisplayName}.`,
chatNow: 'Chat now',
workEmail: 'Work email',
},
supportalNoAccess: {
title: 'Not so fast',
Expand Down Expand Up @@ -1796,6 +1797,16 @@ const translations = {
error: {
requiredFirstName: 'Please input your first name to continue.',
},
workEmail: {
title: 'What’s your work email?',
subtitle: 'Expensify works best when you connect your work email.',
explanationModal: {
descriptionOne: 'Forward to [email protected] for scanning',
descriptionTwo: 'Join your colleagues already using Expensify',
descriptionThree: 'Enjoy a more customized experience',
},
addWorkEmail: 'Add work email',
},
},
featureTraining: {
doNotShowAgain: "Don't show me this again",
Expand Down
11 changes: 11 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,7 @@ const translations = {
minuteAbbreviation: 'm',
chatWithAccountManager: ({accountManagerDisplayName}: ChatWithAccountManagerParams) => `¿Necesitas algo específico? Habla con tu gerente de cuenta, ${accountManagerDisplayName}.`,
chatNow: 'Chatear ahora',
workEmail: 'correo electrónico de trabajo',
},
supportalNoAccess: {
title: 'No tan rápido',
Expand Down Expand Up @@ -1799,6 +1800,16 @@ const translations = {
error: {
requiredFirstName: 'Introduce tu nombre para continuar.',
},
workEmail: {
title: 'Cuál es tu correo electrónico de trabajo',
subtitle: 'Expensify funciona mejor cuando conectas tu correo electrónico de trabajo.',
explanationModal: {
descriptionOne: 'Reenvía a [email protected] para escanear',
descriptionTwo: 'Únete a tus compañeros de trabajo que ya están usando Expensify',
descriptionThree: 'Disfruta de una experiencia más personalizada',
},
addWorkEmail: 'Añadir correo electrónico de trabajo',
},
},
featureTraining: {
doNotShowAgain: 'No muestres esto otra vez',
Expand Down
5 changes: 5 additions & 0 deletions src/libs/API/parameters/AddWorkEmailParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type AddWorkEmailParams = {
workEmail: string;
};

export default AddWorkEmailParams;
7 changes: 7 additions & 0 deletions src/libs/API/parameters/MergeIntoAccountAndLoginParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
type MergeIntoAccountAndLoginParams = {
workEmail: string;
validateCode: string;
accountID: string;
};

export default MergeIntoAccountAndLoginParams;
2 changes: 2 additions & 0 deletions src/libs/API/parameters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -358,4 +358,6 @@ export type {default as ImportPerDiemRatesParams} from './ImportPerDiemRatesPara
export type {default as ExportPerDiemCSVParams} from './ExportPerDiemCSVParams';
export type {default as UpdateWorkspaceCustomUnitParams} from './UpdateWorkspaceCustomUnitParams';
export type {default as DismissProductTrainingParams} from './DismissProductTraining';
export type {default as AddWorkEmailParams} from './AddWorkEmailParams';
export type {default as MergeIntoAccountAndLoginParams} from './MergeIntoAccountAndLoginParams';
export type {default as OpenWorkspacePlanPageParams} from './OpenWorkspacePlanPage';
5 changes: 5 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,8 @@ const WRITE_COMMANDS = {
UPDATE_WORKSPACE_CUSTOM_UNIT: 'UpdateWorkspaceCustomUnit',
VALIDATE_USER_AND_GET_ACCESSIBLE_POLICIES: 'ValidateUserAndGetAccessiblePolicies',
DISMISS_PRODUCT_TRAINING: 'DismissProductTraining',
ADD_WORK_EMAIL: 'AddWorkEmail',
MERGE_INTO_ACCOUNT_AND_LOGIN: 'MergeIntoAccountAndLogin',
} as const;

type WriteCommand = ValueOf<typeof WRITE_COMMANDS>;
Expand Down Expand Up @@ -899,6 +901,9 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.JOIN_ACCESSIBLE_POLICY]: Parameters.JoinAccessiblePolicyParams;
// Dismis Product Training
[WRITE_COMMANDS.DISMISS_PRODUCT_TRAINING]: Parameters.DismissProductTrainingParams;

[WRITE_COMMANDS.ADD_WORK_EMAIL]: Parameters.AddWorkEmailParams;
[WRITE_COMMANDS.MERGE_INTO_ACCOUNT_AND_LOGIN]: Parameters.MergeIntoAccountAndLoginParams;
};

const READ_COMMANDS = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import OnboardingEmployees from '@pages/OnboardingEmployees';
import OnboardingPersonalDetails from '@pages/OnboardingPersonalDetails';
import OnboardingPrivateDomain from '@pages/OnboardingPrivateDomain';
import OnboardingPurpose from '@pages/OnboardingPurpose';
import OnboardingWorkEmail from '@pages/OnboardingWorkEmail';
import OnboardingWorkEmailValidation from '@pages/OnboardingWorkEmailValidation';
import OnboardingWorkspaces from '@pages/OnboardingWorkspaces';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
Expand All @@ -36,7 +38,7 @@ function OnboardingModalNavigator() {
const styles = useThemeStyles();
const {onboardingIsMediumOrLargerScreenWidth} = useResponsiveLayout();
const outerViewRef = React.useRef<View>(null);
const [accountID] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.accountID ?? 0});
const [accountID] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.accountID ?? CONST.DEFAULT_NUMBER_ID});

// Publish a sign_up event when we start the onboarding flow. This should track basic sign ups
// as well as Google and Apple SSO.
Expand Down Expand Up @@ -68,6 +70,14 @@ function OnboardingModalNavigator() {
style={styles.OnboardingNavigatorInnerView(onboardingIsMediumOrLargerScreenWidth)}
>
<Stack.Navigator screenOptions={defaultScreenOptions}>
<Stack.Screen
name={SCREENS.ONBOARDING.WORK_EMAIL}
component={OnboardingWorkEmail}
/>
<Stack.Screen
name={SCREENS.ONBOARDING.WORK_EMAIL_VALIDATION}
component={OnboardingWorkEmailValidation}
/>
<Stack.Screen
name={SCREENS.ONBOARDING.PURPOSE}
component={OnboardingPurpose}
Expand Down
5 changes: 4 additions & 1 deletion src/libs/Navigation/NavigationRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {useOnyx} from 'react-native-onyx';
import {ScrollOffsetContext} from '@components/ScrollOffsetContextProvider';
import useActiveWorkspace from '@hooks/useActiveWorkspace';
import useCurrentReportID from '@hooks/useCurrentReportID';
import usePermissions from '@hooks/usePermissions';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useTheme from '@hooks/useTheme';
import useThemePreference from '@hooks/useThemePreference';
Expand Down Expand Up @@ -99,6 +100,8 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady, sh
selector: hasCompletedGuidedSetupFlowSelector,
});

const {canUsePrivateDomainOnboardingCheck} = usePermissions();

const initialState = useMemo(() => {
if (!user || user.isFromPublicDomain) {
return;
Expand All @@ -107,7 +110,7 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady, sh
// If the user haven't completed the flow, we want to always redirect them to the onboarding flow.
// We also make sure that the user is authenticated.
if (!NativeModules.HybridAppModule && !isOnboardingCompleted && authenticated && !shouldShowRequire2FAModal) {
const {adaptedState} = getAdaptedStateFromPath(getOnboardingInitialPath(isPrivateDomain), linkingConfig.config);
const {adaptedState} = getAdaptedStateFromPath(getOnboardingInitialPath(isPrivateDomain, canUsePrivateDomainOnboardingCheck), linkingConfig.config);
return adaptedState;
}

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 @@ -120,6 +120,14 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
// the onboarding purpose page will be briefly visible.
path: ROUTES.ONBOARDING_ROOT.route,
screens: {
[SCREENS.ONBOARDING.WORK_EMAIL]: {
path: ROUTES.ONBOARDING_WORK_EMAIL.route,
exact: true,
},
[SCREENS.ONBOARDING.WORK_EMAIL_VALIDATION]: {
path: ROUTES.ONBOARDING_WORK_EMAIL_VALIDATION.route,
exact: true,
},
[SCREENS.ONBOARDING.PURPOSE]: {
path: ROUTES.ONBOARDING_PURPOSE.route,
exact: true,
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 @@ -1570,6 +1570,12 @@ type OnboardingModalNavigatorParamList = {
[SCREENS.ONBOARDING.ACCOUNTING]: {
backTo?: string;
};
[SCREENS.ONBOARDING.WORK_EMAIL]: {
backTo?: string;
};
[SCREENS.ONBOARDING.WORK_EMAIL_VALIDATION]: {
backTo?: string;
};
};

type WelcomeVideoModalNavigatorParamList = {
Expand Down
2 changes: 2 additions & 0 deletions src/libs/NavigationUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const ONBOARDING_SCREEN_NAMES = new Set([
SCREENS.ONBOARDING.ACCOUNTING,
SCREENS.ONBOARDING.PRIVATE_DOMAIN,
SCREENS.ONBOARDING.WORKSPACES,
SCREENS.ONBOARDING.WORK_EMAIL,
SCREENS.ONBOARDING.WORK_EMAIL_VALIDATION,
]);

const removePolicyIDParamFromState = (state: State<RootStackParamList>) => {
Expand Down
5 changes: 5 additions & 0 deletions src/libs/Permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ function canUseAllBetas(betas: OnyxEntry<Beta[]>): boolean {
return !!betas?.includes(CONST.BETAS.ALL);
}

function canUsePrivateDomainOnboardingCheck(betas: OnyxEntry<Beta[]>): boolean {
return !!betas?.includes(CONST.BETAS.PRIVATE_DOMAIN_ONBOARDING_CHECK) || canUseAllBetas(betas);
}

function canUseDefaultRooms(betas: OnyxEntry<Beta[]>): boolean {
return !!betas?.includes(CONST.BETAS.DEFAULT_ROOMS) || canUseAllBetas(betas);
}
Expand Down Expand Up @@ -43,6 +47,7 @@ function canUseLinkPreviews(): boolean {
}

export default {
canUsePrivateDomainOnboardingCheck,
canUseDefaultRooms,
canUseLinkPreviews,
canUseSpotnanaTravel,
Expand Down
Loading
Loading