Skip to content

Commit

Permalink
Merge branch 'main' into perf/native-stack-v2
Browse files Browse the repository at this point in the history
  • Loading branch information
chrispader committed Sep 30, 2024
2 parents cb2687a + fbe4025 commit 300922a
Show file tree
Hide file tree
Showing 21 changed files with 187 additions and 269 deletions.
10 changes: 0 additions & 10 deletions src/Expensify.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import {updateLastRoute} from './libs/actions/App';
import * as EmojiPickerAction from './libs/actions/EmojiPickerAction';
import * as Report from './libs/actions/Report';
import * as User from './libs/actions/User';
import {handleHybridAppOnboarding} from './libs/actions/Welcome';
import * as ActiveClientManager from './libs/ActiveClientManager';
import FS from './libs/Fullstory';
import * as Growl from './libs/Growl';
Expand Down Expand Up @@ -99,7 +98,6 @@ function Expensify({
const [account] = useOnyx(ONYXKEYS.ACCOUNT);
const [session] = useOnyx(ONYXKEYS.SESSION);
const [lastRoute] = useOnyx(ONYXKEYS.LAST_ROUTE);
const [tryNewDotData] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT);
const [shouldShowRequire2FAModal, setShouldShowRequire2FAModal] = useState(false);

useEffect(() => {
Expand All @@ -118,14 +116,6 @@ function Expensify({
setAttemptedToOpenPublicRoom(true);
}, [isCheckingPublicRoom]);

useEffect(() => {
if (splashScreenState !== CONST.BOOT_SPLASH_STATE.HIDDEN || tryNewDotData === undefined) {
return;
}

handleHybridAppOnboarding();
}, [splashScreenState, tryNewDotData]);

const isAuthenticated = useMemo(() => !!(session?.authToken ?? null), [session]);
const autoAuthState = useMemo(() => session?.autoAuthState ?? '', [session]);

Expand Down
16 changes: 15 additions & 1 deletion src/components/Composer/index.native.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import type {MarkdownStyle} from '@expensify/react-native-live-markdown';
import mimeDb from 'mime-db';
import type {ForwardedRef} from 'react';
import React, {useCallback, useEffect, useMemo, useRef} from 'react';
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import type {NativeSyntheticEvent, TextInput, TextInputChangeEventData, TextInputPasteEventData} from 'react-native';
import {StyleSheet} from 'react-native';
import type {FileObject} from '@components/AttachmentModal';
import type {AnimatedMarkdownTextInputRef} from '@components/RNMarkdownTextInput';
import RNMarkdownTextInput from '@components/RNMarkdownTextInput';
import useAutoFocusInput from '@hooks/useAutoFocusInput';
import useKeyboardState from '@hooks/useKeyboardState';
import useMarkdownStyle from '@hooks/useMarkdownStyle';
import useResetComposerFocus from '@hooks/useResetComposerFocus';
import useStyleUtils from '@hooks/useStyleUtils';
Expand Down Expand Up @@ -37,6 +38,7 @@ function Composer(
selection,
value,
isGroupPolicyReport = false,
showSoftInputOnFocus,
...props
}: ComposerProps,
ref: ForwardedRef<TextInput>,
Expand All @@ -48,8 +50,11 @@ function Composer(
const markdownStyle = useMarkdownStyle(value, !isGroupPolicyReport ? excludeReportMentionStyle : excludeNoStyles);
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const [contextMenuHidden, setContextMenuHidden] = useState(!showSoftInputOnFocus);

const {inputCallbackRef, inputRef: autoFocusInputRef} = useAutoFocusInput();
const keyboardState = useKeyboardState();
const isKeyboardShown = keyboardState?.isKeyboardShown ?? false;

useEffect(() => {
if (autoFocus === !!autoFocusInputRef.current) {
Expand Down Expand Up @@ -109,6 +114,13 @@ function Composer(
const maxHeightStyle = useMemo(() => StyleUtils.getComposerMaxHeightStyle(maxLines, isComposerFullSize), [StyleUtils, isComposerFullSize, maxLines]);
const composerStyle = useMemo(() => StyleSheet.flatten([style, textContainsOnlyEmojis ? styles.onlyEmojisTextLineHeight : {}]), [style, textContainsOnlyEmojis, styles]);

useEffect(() => {
if (!showSoftInputOnFocus || !isKeyboardShown) {
return;
}
setContextMenuHidden(false);
}, [showSoftInputOnFocus, isKeyboardShown]);

return (
<RNMarkdownTextInput
multiline
Expand All @@ -135,6 +147,8 @@ function Composer(
props?.onBlur?.(e);
}}
onClear={onClear}
showSoftInputOnFocus={showSoftInputOnFocus}
contextMenuHidden={contextMenuHidden}
/>
);
}
Expand Down
2 changes: 2 additions & 0 deletions src/components/Composer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ function Composer(
isComposerFullSize = false,
shouldContainScroll = true,
isGroupPolicyReport = false,
showSoftInputOnFocus = true,
...props
}: ComposerProps,
ref: ForwardedRef<TextInput | HTMLInputElement>,
Expand Down Expand Up @@ -388,6 +389,7 @@ function Composer(
value={value}
defaultValue={defaultValue}
autoFocus={autoFocus}
inputMode={!showSoftInputOnFocus ? 'none' : 'text'}
/* eslint-disable-next-line react/jsx-props-no-spreading */
{...props}
onSelectionChange={addCursorPositionToSelectionChange}
Expand Down
2 changes: 2 additions & 0 deletions src/components/Composer/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ type ComposerProps = Omit<TextInputProps, 'onClear'> & {

/** Indicates whether the composer is in a group policy report. Used for disabling report mentioning style in markdown input */
isGroupPolicyReport?: boolean;

showSoftInputOnFocus?: boolean;
};

export type {TextSelection, ComposerProps, CustomSelectionChangeEvent};
22 changes: 2 additions & 20 deletions src/components/ExplanationModal.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,20 @@
import React, {useCallback} from 'react';
import React 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 * as OnboardingFlow from '@userActions/Welcome/OnboardingFlow';
import CONST from '@src/CONST';
import FeatureTrainingModal from './FeatureTrainingModal';

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

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

// We need to check if standard NewDot onboarding is completed.
Welcome.isOnboardingFlowCompleted({
onNotCompleted: () => {
setTimeout(() => {
Navigation.isNavigationReady().then(() => {
OnboardingFlow.startOnboardingFlow();
});
}, 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}
onClose={onClose}
onClose={Welcome.completeHybridAppOnboarding}
/>
);
}
Expand Down
11 changes: 3 additions & 8 deletions src/components/LHNOptionsList/OptionRowLHN.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import DateUtils from '@libs/DateUtils';
import DomUtils from '@libs/DomUtils';
import hasCompletedGuidedSetupFlowSelector from '@libs/hasCompletedGuidedSetupFlowSelector';
import {hasCompletedGuidedSetupFlowSelector} from '@libs/onboardingSelectors';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import Parser from '@libs/Parser';
import Performance from '@libs/Performance';
Expand All @@ -47,7 +47,7 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${optionItem?.reportID || -1}`);
const [isFirstTimeNewExpensifyUser] = useOnyx(ONYXKEYS.NVP_IS_FIRST_TIME_NEW_EXPENSIFY_USER);
const [hasCompletedGuidedSetupFlow] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {
const [isOnboardingCompleted = true] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {
selector: hasCompletedGuidedSetupFlowSelector,
});
const [shouldHideGBRTooltip] = useOnyx(ONYXKEYS.NVP_SHOULD_HIDE_GBR_TOOLTIP, {initialValue: true});
Expand Down Expand Up @@ -171,12 +171,7 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti
>
<EducationalTooltip
shouldRender={
isFirstTimeNewExpensifyUser &&
!shouldHideGBRTooltip &&
hasCompletedGuidedSetupFlow &&
isScreenFocused &&
shouldUseNarrowLayout &&
ReportUtils.isConciergeChatReport(report)
isFirstTimeNewExpensifyUser && !shouldHideGBRTooltip && isOnboardingCompleted && isScreenFocused && shouldUseNarrowLayout && ReportUtils.isConciergeChatReport(report)
}
renderTooltipContent={renderGBRTooltip}
anchorAlignment={{
Expand Down
46 changes: 46 additions & 0 deletions src/hooks/useOnboardingFlow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {useEffect} from 'react';
import {NativeModules} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import Navigation from '@libs/Navigation/Navigation';
import {hasCompletedGuidedSetupFlowSelector, hasCompletedHybridAppOnboardingFlowSelector} from '@libs/onboardingSelectors';
import * as OnboardingFlow from '@userActions/Welcome/OnboardingFlow';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';

/**
* Hook to handle redirection to the onboarding flow based on the user's onboarding status
*
* Warning: This hook should be used only once in the app
*/
function useOnboardingFlowRouter() {
const [isOnboardingCompleted] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {
selector: hasCompletedGuidedSetupFlowSelector,
});
const [isHybridAppOnboardingCompleted] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT, {
selector: hasCompletedHybridAppOnboardingFlowSelector,
});

useEffect(() => {
if (NativeModules.HybridAppModule) {
// When user is transitioning from OldDot to NewDot, we usually show the explanation modal
if (isHybridAppOnboardingCompleted === false) {
Navigation.navigate(ROUTES.EXPLANATION_MODAL_ROOT);
}

// 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();
}
}

// 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();
}
}, [isOnboardingCompleted, isHybridAppOnboardingCompleted]);

return {isOnboardingCompleted, isHybridAppOnboardingCompleted};
}

export default useOnboardingFlowRouter;
27 changes: 15 additions & 12 deletions src/libs/Navigation/AppNavigator/AuthScreens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import OptionsListContextProvider from '@components/OptionListContextProvider';
import {SearchContextProvider} from '@components/Search/SearchContext';
import SearchRouter from '@components/Search/SearchRouter/SearchRouter';
import useActiveWorkspace from '@hooks/useActiveWorkspace';
import useOnboardingFlowRouter from '@hooks/useOnboardingFlow';
import usePermissions from '@hooks/usePermissions';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useStyleUtils from '@hooks/useStyleUtils';
Expand Down Expand Up @@ -237,7 +238,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
);
const modal = useRef<OnyxTypes.Modal>({});
const [didPusherInit, setDidPusherInit] = useState(false);

const {isOnboardingCompleted} = useOnboardingFlowRouter();
let initialReportID: string | undefined;
const isInitialRender = useRef(true);
if (isInitialRender.current) {
Expand Down Expand Up @@ -526,17 +527,19 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
options={onboardingModalScreenOptions}
component={WelcomeVideoModalNavigator}
/>
<RootStack.Screen
name={NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR}
options={onboardingScreenOptions}
component={OnboardingModalNavigator}
listeners={{
focus: () => {
Modal.setDisableDismissOnEscape(true);
},
beforeRemove: () => Modal.setDisableDismissOnEscape(false),
}}
/>
{isOnboardingCompleted === false && (
<RootStack.Screen
name={NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR}
options={onboardingScreenOptions}
component={OnboardingModalNavigator}
listeners={{
focus: () => {
Modal.setDisableDismissOnEscape(true);
},
beforeRemove: () => Modal.setDisableDismissOnEscape(false),
}}
/>
)}
<RootStack.Screen
name={SCREENS.WORKSPACE_JOIN_USER}
options={{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
import {createStackNavigator} from '@react-navigation/stack';
import React, {useCallback, useEffect} from 'react';
import React, {useCallback} from 'react';
import {View} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import NoDropZone from '@components/DragAndDrop/NoDropZone';
import FocusTrapForScreens from '@components/FocusTrap/FocusTrapForScreen';
import useKeyboardShortcut from '@hooks/useKeyboardShortcut';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import hasCompletedGuidedSetupFlowSelector from '@libs/hasCompletedGuidedSetupFlowSelector';
import OnboardingModalNavigatorScreenOptions from '@libs/Navigation/AppNavigator/OnboardingModalNavigatorScreenOptions';
import Navigation from '@libs/Navigation/Navigation';
import type {OnboardingModalNavigatorParamList} from '@libs/Navigation/types';
import OnboardingRefManager from '@libs/OnboardingRefManager';
import OnboardingPersonalDetails from '@pages/OnboardingPersonalDetails';
import OnboardingPurpose from '@pages/OnboardingPurpose';
import OnboardingWork from '@pages/OnboardingWork';
import * as Report from '@userActions/Report';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import SCREENS from '@src/SCREENS';
import Overlay from './Overlay';

Expand All @@ -27,29 +21,6 @@ const Stack = createStackNavigator<OnboardingModalNavigatorParamList>();
function OnboardingModalNavigator() {
const styles = useThemeStyles();
const {onboardingIsMediumOrLargerScreenWidth} = useResponsiveLayout();
const [hasCompletedGuidedSetupFlow] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {
selector: hasCompletedGuidedSetupFlowSelector,
});
const {shouldUseNarrowLayout} = useResponsiveLayout();

useEffect(() => {
if (!hasCompletedGuidedSetupFlow) {
return;
}
Navigation.isNavigationReady().then(() => {
// On small screens, pop all navigation states and go back to HOME.
// On large screens, need to go back to previous route and then redirect to Concierge,
// otherwise going back on Concierge will go to onboarding and then redirected to Concierge again
if (shouldUseNarrowLayout) {
Navigation.setShouldPopAllStateOnUP(true);
Navigation.goBack(ROUTES.HOME, true, true);
} else {
Navigation.goBack();
Report.navigateToConciergeChat();
}
});
}, [hasCompletedGuidedSetupFlow, shouldUseNarrowLayout]);

const outerViewRef = React.useRef<View>(null);

const handleOuterClick = useCallback(() => {
Expand All @@ -58,9 +29,6 @@ function OnboardingModalNavigator() {

useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.ESCAPE, handleOuterClick, {shouldBubble: true});

if (hasCompletedGuidedSetupFlow) {
return null;
}
return (
<NoDropZone>
<Overlay />
Expand Down
Loading

0 comments on commit 300922a

Please sign in to comment.