Skip to content
2 changes: 2 additions & 0 deletions src/components/SelectionList/BaseSelectionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ function BaseSelectionList<TItem extends ListItem>({
showScrollIndicator = true,
canSelectMultiple = false,
disableKeyboardShortcuts = false,
disableMaintainingScrollPosition = false,
shouldUseUserSkeletonView,
shouldShowTooltips = true,
shouldIgnoreFocus = false,
Expand Down Expand Up @@ -410,6 +411,7 @@ function BaseSelectionList<TItem extends ListItem>({
style={style?.listStyle as ViewStyle}
initialScrollIndex={initialFocusedIndex}
onScrollBeginDrag={onScrollBeginDrag}
maintainVisibleContentPosition={{disabled: disableMaintainingScrollPosition}}
ListHeaderComponent={
<>
{customListHeaderContent}
Expand Down
8 changes: 8 additions & 0 deletions src/components/SelectionList/ListItem/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,13 @@ type InviteMemberListItemProps<TItem extends ListItem> = UserListItemProps<TItem
sectionIndex?: number;
};

type WorkspaceListItemType = {
text: string;
policyID?: string;
isPolicyAdmin?: boolean;
brickRoadIndicator?: BrickRoad;
} & ListItem;

type TravelDomainListItemProps<TItem extends ListItem> = BaseListItemProps<
TItem & {
/** Value of the domain */
Expand All @@ -310,4 +317,5 @@ export type {
SpendCategorySelectorListItemProps,
UserListItemProps,
InviteMemberListItemProps,
WorkspaceListItemType,
};
14 changes: 11 additions & 3 deletions src/components/SelectionList/components/TextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type TextInputProps = {
onKeyPress?: (event: TextInputKeyPressEvent) => void;

/** Function called when the text input focus changes */
onFocusChange?: (focused: boolean) => void;
onFocusChange: (focused: boolean) => void;

/** Whether to show the text input */
shouldShowTextInput?: boolean;
Expand Down Expand Up @@ -103,6 +103,14 @@ function TextInput({
}, [shouldShowTextInput, disableAutoFocus, focusTextInput]),
);

const handleFocus = useCallback(() => {
onFocusChange(true);
}, [onFocusChange]);

const handleBlur = useCallback(() => {
onFocusChange(false);
}, [onFocusChange]);

if (!shouldShowTextInput) {
return null;
}
Expand All @@ -113,8 +121,8 @@ function TextInput({
<BaseTextInput
ref={mergedRef}
onKeyPress={onKeyPress}
onFocus={() => onFocusChange?.(true)}
onBlur={() => onFocusChange?.(false)}
onFocus={handleFocus}
onBlur={handleBlur}
label={label}
accessibilityLabel={accessibilityLabel}
hint={hint}
Expand Down
3 changes: 3 additions & 0 deletions src/components/SelectionList/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ type SelectionListProps<TItem extends ListItem> = {
/** Whether keyboard shortcuts should be disabled */
disableKeyboardShortcuts?: boolean;

/** Whether scroll position should change when focused item changes */
disableMaintainingScrollPosition?: boolean;

/** Whether to use the user skeleton view */
shouldUseUserSkeletonView?: boolean;

Expand Down
21 changes: 8 additions & 13 deletions src/hooks/useWorkspaceList.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
import {useMemo} from 'react';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import * as Expensicons from '@components/Icon/Expensicons';
import type {LocaleContextProps} from '@components/LocaleContextProvider';
import type {ListItem, SectionListDataType} from '@components/SelectionListWithSections/types';
import type {WorkspaceListItemType as WorkspaceListItem} from '@components/SelectionList/ListItem/types';
import type {SectionListDataType} from '@components/SelectionListWithSections/types';
import {isPolicyAdmin, shouldShowPolicy, sortWorkspacesBySelected} from '@libs/PolicyUtils';
import {getDefaultWorkspaceAvatar} from '@libs/ReportUtils';
import tokenizedSearch from '@libs/tokenizedSearch';
import type {BrickRoad} from '@libs/WorkspacesSettingsUtils';
import CONST from '@src/CONST';
import type {Policy} from '@src/types/onyx';
import {isEmptyObject} from '@src/types/utils/EmptyObject';

type WorkspaceListItem = {
text: string;
policyID?: string;
isPolicyAdmin?: boolean;
brickRoadIndicator?: BrickRoad;
} & ListItem;
import {useMemoizedLazyExpensifyIcons} from './useLazyAsset';

type UseWorkspaceListParams = {
policies: OnyxCollection<Policy>;
Expand All @@ -29,6 +22,7 @@ type UseWorkspaceListParams = {
};

function useWorkspaceList({policies, currentUserLogin, selectedPolicyIDs, searchTerm, shouldShowPendingDeletePolicy, localeCompare, additionalFilter}: UseWorkspaceListParams) {
const icons = useMemoizedLazyExpensifyIcons(['FallbackWorkspaceAvatar'] as const);
const usersWorkspaces = useMemo(() => {
if (!policies || isEmptyObject(policies)) {
return [];
Expand All @@ -48,17 +42,17 @@ function useWorkspaceList({policies, currentUserLogin, selectedPolicyIDs, search
icons: [
{
source: policy?.avatarURL ? policy.avatarURL : getDefaultWorkspaceAvatar(policy?.name),
fallbackIcon: Expensicons.FallbackWorkspaceAvatar,
fallbackIcon: icons.FallbackWorkspaceAvatar,
name: policy?.name,
type: CONST.ICON_TYPE_WORKSPACE,
id: policy?.id,
},
],
keyForList: policy?.id,
keyForList: `${policy?.id}`,
isPolicyAdmin: isPolicyAdmin(policy),
isSelected: policy?.id && selectedPolicyIDs ? selectedPolicyIDs.includes(policy.id) : false,
}));
}, [policies, shouldShowPendingDeletePolicy, currentUserLogin, additionalFilter, selectedPolicyIDs]);
}, [policies, shouldShowPendingDeletePolicy, currentUserLogin, additionalFilter, icons.FallbackWorkspaceAvatar, selectedPolicyIDs]);

const filteredAndSortedUserWorkspaces = useMemo<WorkspaceListItem[]>(
() =>
Expand All @@ -83,6 +77,7 @@ function useWorkspaceList({policies, currentUserLogin, selectedPolicyIDs, search
const shouldShowSearchInput = usersWorkspaces.length >= CONST.STANDARD_LIST_ITEM_LIMIT;

return {
data: filteredAndSortedUserWorkspaces,
sections,
shouldShowNoResultsFoundMessage,
shouldShowSearchInput,
Expand Down
32 changes: 20 additions & 12 deletions src/pages/ReportChangeWorkspacePage.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import React, {useCallback} from 'react';
import React, {useCallback, useMemo} from 'react';
import type {OnyxEntry} from 'react-native-onyx';
import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import {useSession} from '@components/OnyxListItemProvider';
import ScreenWrapper from '@components/ScreenWrapper';
import SelectionList from '@components/SelectionListWithSections';
import UserListItem from '@components/SelectionListWithSections/UserListItem';
import SelectionList from '@components/SelectionList';
import type {WorkspaceListItemType} from '@components/SelectionList/ListItem/types';
import UserListItem from '@components/SelectionList/ListItem/UserListItem';
import useDebouncedState from '@hooks/useDebouncedState';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import useOnyx from '@hooks/useOnyx';
import usePermissions from '@hooks/usePermissions';
import useReportIsArchived from '@hooks/useReportIsArchived';
import useThemeStyles from '@hooks/useThemeStyles';
import type {WorkspaceListItem} from '@hooks/useWorkspaceList';
import useWorkspaceList from '@hooks/useWorkspaceList';
import {changeReportPolicy, changeReportPolicyAndInviteSubmitter, moveIOUReportToPolicy, moveIOUReportToPolicyAndInviteSubmitter} from '@libs/actions/Report';
import Navigation from '@libs/Navigation/Navigation';
Expand Down Expand Up @@ -125,7 +125,7 @@ function ReportChangeWorkspacePage({report, route}: ReportChangeWorkspacePagePro
],
);

const {sections, shouldShowNoResultsFoundMessage, shouldShowSearchInput} = useWorkspaceList({
const {data, shouldShowNoResultsFoundMessage, shouldShowSearchInput} = useWorkspaceList({
policies,
currentUserLogin: session?.email,
shouldShowPendingDeletePolicy: false,
Expand All @@ -135,6 +135,16 @@ function ReportChangeWorkspacePage({report, route}: ReportChangeWorkspacePagePro
additionalFilter: (newPolicy) => isWorkspaceEligibleForReportChange(submitterEmail, newPolicy),
});

const textInputOptions = useMemo(
() => ({
label: shouldShowSearchInput ? translate('common.search') : undefined,
value: searchTerm,
onChangeText: setSearchTerm,
headerMessage: shouldShowNoResultsFoundMessage ? translate('common.noResultsFound') : '',
}),
[searchTerm, setSearchTerm, shouldShowNoResultsFoundMessage, shouldShowSearchInput, translate],
);

if (!isMoneyRequestReport(report) || isMoneyRequestReportPendingDeletion(report)) {
return <NotFoundPage />;
}
Expand All @@ -157,16 +167,14 @@ function ReportChangeWorkspacePage({report, route}: ReportChangeWorkspacePagePro
{shouldShowLoadingIndicator ? (
<FullScreenLoadingIndicator style={[styles.flex1, styles.pRelative]} />
) : (
<SelectionList<WorkspaceListItem>
<SelectionList<WorkspaceListItemType>
ListItem={UserListItem}
sections={sections}
data={data}
onSelectRow={(option) => selectPolicy(option.policyID)}
textInputLabel={shouldShowSearchInput ? translate('common.search') : undefined}
textInputValue={searchTerm}
onChangeText={setSearchTerm}
headerMessage={shouldShowNoResultsFoundMessage ? translate('common.noResultsFound') : ''}
initiallyFocusedOptionKey={report.policyID}
textInputOptions={textInputOptions}
initiallyFocusedItemKey={report.policyID}
showLoadingPlaceholder={fetchStatus.status === 'loading' || !didScreenTransitionEnd}
disableMaintainingScrollPosition
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an enabled by default option in FlashList that the scroll position stays the same. Here we expect that after clearing input the scroll goes to the beginning of the list.
So I add a possibility to disable this option. In all others usages of FlashList it's also disabled 😉

/>
)}
</>
Expand Down
Loading