Skip to content
1 change: 1 addition & 0 deletions src/components/AttachmentModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,7 @@ function AttachmentModal({
threeDotsMenuItems={threeDotsMenuItems}
shouldOverlayDots
subTitleLink={currentAttachmentLink ?? ''}
shouldDisplayHelpButton={false}
/>
<View style={styles.imageModalImageCenterContainer}>
{isLoading && <FullScreenLoadingIndicator />}
Expand Down
23 changes: 7 additions & 16 deletions src/components/ConnectToQuickbooksOnlineFlow/index.native.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,30 @@
import React, {useEffect, useRef, useState} from 'react';
import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import {useOnyx} from 'react-native-onyx';
import {WebView} from 'react-native-webview';
import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView';
import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import Modal from '@components/Modal';
import useLocalize from '@hooks/useLocalize';
import {getQuickbooksOnlineSetupLink} from '@libs/actions/connections/QuickbooksOnline';
import * as PolicyAction from '@userActions/Policy/Policy';
import {enablePolicyTaxes} from '@userActions/Policy/Policy';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Session} from '@src/types/onyx';
import type {ConnectToQuickbooksOnlineFlowProps} from './types';

type ConnectToQuickbooksOnlineFlowOnyxProps = {
/** Session info for the currently logged in user. */
session: OnyxEntry<Session>;
};

const renderLoading = () => <FullScreenLoadingIndicator />;

function ConnectToQuickbooksOnlineFlow({policyID, session}: ConnectToQuickbooksOnlineFlowProps & ConnectToQuickbooksOnlineFlowOnyxProps) {
function ConnectToQuickbooksOnlineFlow({policyID}: ConnectToQuickbooksOnlineFlowProps) {
const {translate} = useLocalize();
const webViewRef = useRef<WebView>(null);
const [isWebViewOpen, setWebViewOpen] = useState(false);
const [session] = useOnyx(ONYXKEYS.SESSION);

const authToken = session?.authToken ?? null;

useEffect(() => {
// Since QBO doesn't support Taxes, we should disable them from the LHN when connecting to QBO
PolicyAction.enablePolicyTaxes(policyID, false);
enablePolicyTaxes(policyID, false);
setWebViewOpen(true);
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
}, []);
Expand All @@ -46,6 +40,7 @@ function ConnectToQuickbooksOnlineFlow({policyID, session}: ConnectToQuickbooksO
<HeaderWithBackButton
title={translate('workspace.accounting.title')}
onBackButtonPress={() => setWebViewOpen(false)}
shouldDisplayHelpButton={false}
/>
<FullPageOfflineBlockingView>
<WebView
Expand All @@ -68,8 +63,4 @@ function ConnectToQuickbooksOnlineFlow({policyID, session}: ConnectToQuickbooksO

ConnectToQuickbooksOnlineFlow.displayName = 'ConnectToQuickbooksOnlineFlow';

export default withOnyx<ConnectToQuickbooksOnlineFlowProps & ConnectToQuickbooksOnlineFlowOnyxProps, ConnectToQuickbooksOnlineFlowOnyxProps>({
session: {
key: ONYXKEYS.SESSION,
},
})(ConnectToQuickbooksOnlineFlow);
export default ConnectToQuickbooksOnlineFlow;
1 change: 1 addition & 0 deletions src/components/ConnectToXeroFlow/index.native.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ function ConnectToXeroFlow({policyID}: ConnectToXeroFlowProps) {
<HeaderWithBackButton
title={translate('workspace.accounting.title')}
onBackButtonPress={() => setWebViewOpen(false)}
shouldDisplayHelpButton={false}
/>
<FullPageOfflineBlockingView>
<WebView
Expand Down
7 changes: 5 additions & 2 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@ type HeaderProps = {

/** The URL link associated with the attachment's subtitle, if available */
subTitleLink?: string;

/** Line number for the title */
numberOfTitleLines?: number;
};

function Header({title = '', subtitle = '', textStyles = [], style, containerStyles = [], shouldShowEnvironmentBadge = false, subTitleLink = ''}: HeaderProps) {
function Header({title = '', subtitle = '', textStyles = [], style, containerStyles = [], shouldShowEnvironmentBadge = false, subTitleLink = '', numberOfTitleLines = 2}: HeaderProps) {
const styles = useThemeStyles();
const renderedSubtitle = useMemo(
() => (
Expand Down Expand Up @@ -72,7 +75,7 @@ function Header({title = '', subtitle = '', textStyles = [], style, containerSty
{typeof title === 'string'
? !!title && (
<Text
numberOfLines={2}
numberOfLines={numberOfTitleLines}
style={[styles.headerText, styles.textLarge, textStyles]}
>
{title}
Expand Down
75 changes: 39 additions & 36 deletions src/components/HeaderWithBackButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ function HeaderWithBackButton({
subtitle={stepCounter ? translate('stepCounter', stepCounter) : subtitle}
textStyles={[titleColor ? StyleUtils.getTextColorStyle(titleColor) : {}, shouldUseHeadlineHeader && styles.textHeadlineH2]}
subTitleLink={subTitleLink}
numberOfTitleLines={1}
/>
);
}, [
Expand Down Expand Up @@ -143,12 +144,12 @@ function HeaderWithBackButton({
// progressBarPercentage can be 0 which would
// be falsey, hence using !== undefined explicitly
progressBarPercentage !== undefined && styles.pl0,
shouldShowBackButton && [styles.pl2, styles.pr2],
shouldShowBackButton && [styles.pl2],
shouldOverlay && StyleSheet.absoluteFillObject,
style,
]}
>
<View style={[styles.dFlex, styles.flexRow, styles.alignItemsCenter, styles.flexGrow1, styles.justifyContentBetween, styles.overflowHidden]}>
<View style={[styles.dFlex, styles.flexRow, styles.alignItemsCenter, styles.flexGrow1, styles.justifyContentBetween, styles.overflowHidden, styles.mr3]}>
{shouldShowBackButton && (
<Tooltip text={translate('common.back')}>
<PressableWithoutFeedback
Expand Down Expand Up @@ -194,42 +195,44 @@ function HeaderWithBackButton({
/>
)}
{middleContent}
<View style={[styles.reportOptions, styles.flexRow, styles.pr5, styles.alignItemsCenter]}>
{children}
{shouldShowDownloadButton &&
(!isDownloading ? (
<Tooltip text={translate('common.download')}>
<PressableWithoutFeedback
onPress={(event) => {
// Blur the pressable in case this button triggers a Growl notification
// We do not want to overlap Growl with the Tooltip (#15271)
(event?.currentTarget as HTMLElement)?.blur();
<View style={[styles.reportOptions, styles.flexRow, styles.alignItemsCenter]}>
<View style={[styles.pr2]}>
{children}
{shouldShowDownloadButton &&
(!isDownloading ? (
<Tooltip text={translate('common.download')}>
<PressableWithoutFeedback
onPress={(event) => {
// Blur the pressable in case this button triggers a Growl notification
// We do not want to overlap Growl with the Tooltip (#15271)
(event?.currentTarget as HTMLElement)?.blur();

if (!isDownloadButtonActive) {
return;
}
if (!isDownloadButtonActive) {
return;
}

onDownloadButtonPress();
temporarilyDisableDownloadButton();
}}
onDownloadButtonPress();
temporarilyDisableDownloadButton();
}}
style={[styles.touchableButtonImage]}
role="button"
accessibilityLabel={translate('common.download')}
>
<Icon
src={Expensicons.Download}
fill={iconFill ?? StyleUtils.getIconFillColor(getButtonState(false, false, !isDownloadButtonActive))}
/>
</PressableWithoutFeedback>
</Tooltip>
) : (
<ActivityIndicator
style={[styles.touchableButtonImage]}
role="button"
accessibilityLabel={translate('common.download')}
>
<Icon
src={Expensicons.Download}
fill={iconFill ?? StyleUtils.getIconFillColor(getButtonState(false, false, !isDownloadButtonActive))}
/>
</PressableWithoutFeedback>
</Tooltip>
) : (
<ActivityIndicator
style={[styles.touchableButtonImage]}
size="small"
color={theme.spinner}
/>
))}
{shouldShowPinButton && !!report && <PinButton report={report} />}
size="small"
color={theme.spinner}
/>
))}
{shouldShowPinButton && !!report && <PinButton report={report} />}
</View>
{shouldShowThreeDotsButton && (
<ThreeDotsMenu
icon={threeDotsMenuIcon}
Expand Down Expand Up @@ -259,7 +262,7 @@ function HeaderWithBackButton({
)}
</View>
{shouldDisplayHelpButton && <HelpButton />}
{shouldDisplaySearchRouter && <SearchButton style={styles.mr5} />}
{shouldDisplaySearchRouter && <SearchButton />}
</View>
</View>
);
Expand Down
12 changes: 6 additions & 6 deletions src/components/Navigation/TopBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ type TopBarProps = {
breadcrumbLabel: string;
activeWorkspaceID?: string;
shouldDisplaySearch?: boolean;
shouldDisplaySidePane?: boolean;
shouldDisplayHelpButton?: boolean;
cancelSearch?: () => void;
};

function TopBar({breadcrumbLabel, activeWorkspaceID, shouldDisplaySearch = true, shouldDisplaySidePane = true, cancelSearch}: TopBarProps) {
function TopBar({breadcrumbLabel, activeWorkspaceID, shouldDisplaySearch = true, shouldDisplayHelpButton = true, cancelSearch}: TopBarProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const policy = usePolicy(activeWorkspaceID);
Expand All @@ -44,10 +44,10 @@ function TopBar({breadcrumbLabel, activeWorkspaceID, shouldDisplaySearch = true,
return (
<View style={[styles.w100, styles.zIndex10]}>
<View
style={[styles.flexRow, styles.gap4, styles.mh5, styles.mv5, styles.alignItemsCenter, styles.justifyContentBetween]}
style={[styles.flexRow, styles.ml5, styles.mr3, styles.mv5, styles.alignItemsCenter, styles.justifyContentBetween]}
dataSet={{dragArea: true}}
>
<View style={[styles.flex1, styles.flexRow, styles.alignItemsCenter]}>
<View style={[styles.flex1, styles.flexRow, styles.alignItemsCenter, styles.pr2]}>
<WorkspaceSwitcherButton policy={policy} />

<View style={[styles.ml3, styles.flex1]}>
Expand All @@ -65,15 +65,15 @@ function TopBar({breadcrumbLabel, activeWorkspaceID, shouldDisplaySearch = true,
{!!cancelSearch && (
<PressableWithoutFeedback
accessibilityLabel={translate('common.cancel')}
style={[styles.textBlue]}
style={[styles.textBlue, styles.ph2]}
onPress={() => {
cancelSearch();
}}
>
<Text style={[styles.textBlue]}>{translate('common.cancel')}</Text>
</PressableWithoutFeedback>
)}
{shouldDisplaySidePane && <HelpButton />}
{shouldDisplayHelpButton && <HelpButton />}
{displaySearch && <SearchButton />}
</View>
<LoadingBar shouldShow={isLoadingReportData ?? false} />
Expand Down
83 changes: 44 additions & 39 deletions src/components/Search/SearchPageHeader/SearchPageHeaderInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type {SearchQueryJSON, SearchQueryString} from '@components/Search/types'
import {isSearchQueryItem} from '@components/SelectionList/Search/SearchQueryListItem';
import type {SearchQueryItem} from '@components/SelectionList/Search/SearchQueryListItem';
import type {SelectionListHandle} from '@components/SelectionList/types';
import HelpButton from '@components/SidePane/HelpButton';
import useLocalize from '@hooks/useLocalize';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
Expand Down Expand Up @@ -291,60 +292,64 @@ function SearchPageHeaderInput({queryJSON, searchRouterListVisible, hideSearchRo
setIsAutocompleteListVisible(true);
};
// we need `- BORDER_WIDTH` to achieve the effect that the input will not "jump"
const popoverHorizontalPosition = 12 - BORDER_WIDTH;
const leftPopoverHorizontalPosition = 12 - BORDER_WIDTH;
const rightPopoverHorizontalPosition = 4 - BORDER_WIDTH;
const autocompleteInputStyle = isAutocompleteListVisible
? [
styles.border,
styles.borderRadiusComponentLarge,
styles.pAbsolute,
styles.pt2,
{top: 8 - BORDER_WIDTH, left: popoverHorizontalPosition, right: popoverHorizontalPosition},
{top: 8 - BORDER_WIDTH, left: leftPopoverHorizontalPosition, right: rightPopoverHorizontalPosition},
{boxShadow: variables.popoverMenuShadow},
]
: [styles.pt4];
const inputWrapperActiveStyle = isAutocompleteListVisible ? styles.ph2 : null;

return (
<View
dataSet={{dragArea: false}}
style={[styles.searchResultsHeaderBar, isAutocompleteListVisible && styles.ph3]}
>
<View style={[styles.appBG, ...autocompleteInputStyle]}>
<SearchInputSelectionWrapper
value={textInputValue}
onSearchQueryChange={onSearchQueryChange}
isFullWidth
onSubmit={() => {
const focusedOption = listRef.current?.getFocusedOption();
if (focusedOption) {
return;
}
submitSearch(textInputValue);
}}
autoFocus={false}
onFocus={showAutocompleteList}
onBlur={hideAutocompleteList}
wrapperStyle={{...styles.searchAutocompleteInputResults, ...styles.br2}}
wrapperFocusedStyle={styles.searchAutocompleteInputResultsFocused}
outerWrapperStyle={[inputWrapperActiveStyle, styles.pb2]}
rightComponent={inputRightComponent}
autocompleteListRef={listRef}
ref={textInputRef}
selection={selection}
substitutionMap={autocompleteSubstitutions}
/>
<View style={[styles.mh65vh, !isAutocompleteListVisible && styles.dNone]}>
<SearchAutocompleteList
autocompleteQueryValue={autocompleteQueryValue}
searchQueryItem={searchQueryItem}
onListItemPress={onListItemPress}
setTextQuery={setTextAndUpdateSelection}
updateAutocompleteSubstitutions={updateAutocompleteSubstitutions}
ref={listRef}
shouldSubscribeToArrowKeyEvents={isAutocompleteListVisible}
<View style={[styles.flexRow, styles.alignItemsCenter, styles.zIndex10, styles.mr3]}>
<View
dataSet={{dragArea: false}}
style={[styles.searchResultsHeaderBar, styles.flex1, isAutocompleteListVisible && styles.pr1, isAutocompleteListVisible && styles.pl3]}
>
<View style={[styles.appBG, ...autocompleteInputStyle]}>
<SearchInputSelectionWrapper
value={textInputValue}
onSearchQueryChange={onSearchQueryChange}
isFullWidth
onSubmit={() => {
const focusedOption = listRef.current?.getFocusedOption();
if (focusedOption) {
return;
}
submitSearch(textInputValue);
}}
autoFocus={false}
onFocus={showAutocompleteList}
onBlur={hideAutocompleteList}
wrapperStyle={{...styles.searchAutocompleteInputResults, ...styles.br2}}
wrapperFocusedStyle={styles.searchAutocompleteInputResultsFocused}
outerWrapperStyle={[inputWrapperActiveStyle, styles.pb2]}
rightComponent={inputRightComponent}
autocompleteListRef={listRef}
ref={textInputRef}
selection={selection}
substitutionMap={autocompleteSubstitutions}
/>
<View style={[styles.mh65vh, !isAutocompleteListVisible && styles.dNone]}>
<SearchAutocompleteList
autocompleteQueryValue={autocompleteQueryValue}
searchQueryItem={searchQueryItem}
onListItemPress={onListItemPress}
setTextQuery={setTextAndUpdateSelection}
updateAutocompleteSubstitutions={updateAutocompleteSubstitutions}
ref={listRef}
shouldSubscribeToArrowKeyEvents={isAutocompleteListVisible}
/>
</View>
</View>
</View>
<HelpButton style={[styles.mt1Half]} />
</View>
);
}
Expand Down
1 change: 1 addition & 0 deletions src/components/Search/SearchRouter/SearchRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ function SearchRouter({onRouterClose, shouldHideInputCaret}: SearchRouterProps,
<HeaderWithBackButton
title={translate('common.search')}
onBackButtonPress={() => onRouterClose()}
shouldDisplayHelpButton={false}
/>
)}
{isRecentSearchesDataLoaded && (
Expand Down
Loading
Loading