Skip to content

Commit

Permalink
Merge pull request #39865 from brunovjk/fix/37144
Browse files Browse the repository at this point in the history
Use 'isSearchingForReports' to control green circular loading
  • Loading branch information
lakchote authored May 3, 2024
2 parents aa0eae0 + 9590ff1 commit 7483d10
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 45 deletions.
2 changes: 1 addition & 1 deletion src/pages/ChatFinderPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ function ChatFinderPage({betas, isSearchingForReports, navigation}: ChatFinderPa
onSelectRow={selectReport}
showLoadingPlaceholder={!areOptionsInitialized || !isScreenTransitionEnd}
footerContent={!isDismissed && ChatFinderPageFooterInstance}
isLoadingNewOptions={isSearchingForReports ?? undefined}
isLoadingNewOptions={!!isSearchingForReports}
/>
</ScreenWrapper>
);
Expand Down
30 changes: 20 additions & 10 deletions src/pages/RoomInvitePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Str from 'expensify-common/lib/str';
import React, {useCallback, useEffect, useMemo, useState} from 'react';
import type {SectionListData} from 'react-native';
import {View} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
import FormAlertWithSubmitButton from '@components/FormAlertWithSubmitButton';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
Expand All @@ -13,8 +14,10 @@ import type {Section} from '@components/SelectionList/types';
import UserListItem from '@components/SelectionList/UserListItem';
import withNavigationTransitionEnd from '@components/withNavigationTransitionEnd';
import type {WithNavigationTransitionEndProps} from '@components/withNavigationTransitionEnd';
import useDebouncedState from '@hooks/useDebouncedState';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as ReportActions from '@libs/actions/Report';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import * as LoginUtils from '@libs/LoginUtils';
import Navigation from '@libs/Navigation/Navigation';
Expand All @@ -26,6 +29,7 @@ import * as ReportUtils from '@libs/ReportUtils';
import type {RoomInviteNavigatorParamList} from '@navigation/types';
import * as Report from '@userActions/Report';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
import type {Policy} from '@src/types/onyx';
Expand All @@ -37,7 +41,6 @@ import SearchInputManager from './workspace/SearchInputManager';
type RoomInvitePageProps = WithReportOrNotFoundProps & WithNavigationTransitionEndProps & StackScreenProps<RoomInviteNavigatorParamList, typeof SCREENS.ROOM_INVITE_ROOT>;

type Sections = Array<SectionListData<OptionsListUtils.MemberForList, Section<OptionsListUtils.MemberForList>>>;

function RoomInvitePage({
betas,
report,
Expand All @@ -48,14 +51,16 @@ function RoomInvitePage({
}: RoomInvitePageProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const [searchTerm, setSearchTerm] = useState('');
const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState('');
const [selectedOptions, setSelectedOptions] = useState<ReportUtils.OptionData[]>([]);
const [invitePersonalDetails, setInvitePersonalDetails] = useState<ReportUtils.OptionData[]>([]);
const [userToInvite, setUserToInvite] = useState<ReportUtils.OptionData | null>(null);
const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false});
const {options, areOptionsInitialized} = useOptionsList();

useEffect(() => {
setSearchTerm(SearchInputManager.searchInput);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

// Any existing participants and Expensify emails should not be eligible for invitation
Expand All @@ -68,7 +73,7 @@ function RoomInvitePage({
);

useEffect(() => {
const inviteOptions = OptionsListUtils.getMemberInviteOptions(options.personalDetails, betas ?? [], searchTerm, excludedUsers);
const inviteOptions = OptionsListUtils.getMemberInviteOptions(options.personalDetails, betas ?? [], debouncedSearchTerm, excludedUsers);

// Update selectedOptions with the latest personalDetails information
const detailsMap: Record<string, OptionsListUtils.MemberForList> = {};
Expand All @@ -87,7 +92,7 @@ function RoomInvitePage({
setInvitePersonalDetails(inviteOptions.personalDetails);
setSelectedOptions(newSelectedOptions);
// eslint-disable-next-line react-hooks/exhaustive-deps -- we don't want to recalculate when selectedOptions change
}, [betas, searchTerm, excludedUsers, options.personalDetails]);
}, [betas, debouncedSearchTerm, excludedUsers, options.personalDetails]);

const sections = useMemo(() => {
const sectionsArr: Sections = [];
Expand All @@ -98,12 +103,12 @@ function RoomInvitePage({

// Filter all options that is a part of the search term or in the personal details
let filterSelectedOptions = selectedOptions;
if (searchTerm !== '') {
if (debouncedSearchTerm !== '') {
filterSelectedOptions = selectedOptions.filter((option) => {
const accountID = option?.accountID;
const isOptionInPersonalDetails = invitePersonalDetails.some((personalDetail) => accountID && personalDetail?.accountID === accountID);
const parsedPhoneNumber = PhoneNumber.parsePhoneNumber(LoginUtils.appendCountryCode(Str.removeSMSDomain(searchTerm)));
const searchValue = parsedPhoneNumber.possible && parsedPhoneNumber.number ? parsedPhoneNumber.number.e164 : searchTerm.toLowerCase();
const parsedPhoneNumber = PhoneNumber.parsePhoneNumber(LoginUtils.appendCountryCode(Str.removeSMSDomain(debouncedSearchTerm)));
const searchValue = parsedPhoneNumber.possible && parsedPhoneNumber.number ? parsedPhoneNumber.number.e164 : debouncedSearchTerm.toLowerCase();
const isPartOfSearchTerm = (option.text?.toLowerCase() ?? '').includes(searchValue) || (option.login?.toLowerCase() ?? '').includes(searchValue);
return isPartOfSearchTerm || isOptionInPersonalDetails;
});
Expand Down Expand Up @@ -134,7 +139,7 @@ function RoomInvitePage({
}

return sectionsArr;
}, [areOptionsInitialized, selectedOptions, searchTerm, invitePersonalDetails, userToInvite, translate]);
}, [areOptionsInitialized, selectedOptions, debouncedSearchTerm, invitePersonalDetails, userToInvite, translate]);

const toggleOption = useCallback(
(option: OptionsListUtils.MemberForList) => {
Expand Down Expand Up @@ -193,7 +198,7 @@ function RoomInvitePage({
}, [role, reportID, backRoute]);

const headerMessage = useMemo(() => {
const searchValue = searchTerm.trim().toLowerCase();
const searchValue = debouncedSearchTerm.trim().toLowerCase();
const expensifyEmails = CONST.EXPENSIFY_EMAILS as string[];
if (!userToInvite && expensifyEmails.includes(searchValue)) {
return translate('messages.errorMessageInvalidEmail');
Expand All @@ -209,7 +214,11 @@ function RoomInvitePage({
return translate('messages.userIsAlreadyMember', {login: searchValue, name: reportName});
}
return OptionsListUtils.getHeaderMessage(invitePersonalDetails.length !== 0, Boolean(userToInvite), searchValue);
}, [searchTerm, userToInvite, excludedUsers, invitePersonalDetails, translate, reportName]);
}, [debouncedSearchTerm, userToInvite, excludedUsers, invitePersonalDetails, translate, reportName]);

useEffect(() => {
ReportActions.searchInServer(debouncedSearchTerm);
}, [debouncedSearchTerm]);

return (
<ScreenWrapper
Expand Down Expand Up @@ -243,6 +252,7 @@ function RoomInvitePage({
showScrollIndicator
shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()}
showLoadingPlaceholder={!areOptionsInitialized}
isLoadingNewOptions={!!isSearchingForReports}
/>
<View style={[styles.flexShrink0]}>
<FormAlertWithSubmitButton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF
const {didScreenTransitionEnd} = useScreenWrapperTranstionStatus();
const [betas] = useOnyx(ONYXKEYS.BETAS);
const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID);
const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false});
const {options, areOptionsInitialized} = useOptionsList({
shouldInitialize: didScreenTransitionEnd,
});
Expand Down Expand Up @@ -342,6 +343,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF
headerMessage={headerMessage}
showLoadingPlaceholder={!areOptionsInitialized || !didScreenTransitionEnd}
canSelectMultiple={isIOUSplit && isAllowedToSplit}
isLoadingNewOptions={!!isSearchingForReports}
/>
);
}
Expand Down
11 changes: 10 additions & 1 deletion src/pages/settings/AboutPage/ShareLogList/BaseShareLogList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, {useMemo} from 'react';
import React, {useEffect, useMemo} from 'react';
import {useOnyx} from 'react-native-onyx';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import {useBetas} from '@components/OnyxProvider';
import {useOptionsList} from '@components/OptionListContextProvider';
Expand All @@ -8,10 +9,12 @@ import UserListItem from '@components/SelectionList/UserListItem';
import useDebouncedState from '@hooks/useDebouncedState';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import * as ReportActions from '@libs/actions/Report';
import * as FileUtils from '@libs/fileDownload/FileUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {Report} from '@src/types/onyx';
import type {BaseShareLogListProps} from './types';
Expand All @@ -21,6 +24,7 @@ function BaseShareLogList({onAttachLogToReport}: BaseShareLogListProps) {
const {isOffline} = useNetwork();
const {translate} = useLocalize();
const betas = useBetas();
const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false});
const {options, areOptionsInitialized} = useOptionsList();

const searchOptions = useMemo(() => {
Expand Down Expand Up @@ -82,6 +86,10 @@ function BaseShareLogList({onAttachLogToReport}: BaseShareLogListProps) {
onAttachLogToReport(option.reportID, filename);
};

useEffect(() => {
ReportActions.searchInServer(debouncedSearchValue);
}, [debouncedSearchValue]);

return (
<ScreenWrapper
testID={BaseShareLogList.displayName}
Expand All @@ -103,6 +111,7 @@ function BaseShareLogList({onAttachLogToReport}: BaseShareLogListProps) {
textInputLabel={translate('optionsSelector.nameEmailOrPhoneNumber')}
textInputHint={isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''}
showLoadingPlaceholder={!didScreenTransitionEnd}
isLoadingNewOptions={!!isSearchingForReports}
/>
</>
)}
Expand Down
19 changes: 11 additions & 8 deletions src/pages/tasks/TaskAssigneeSelectorModal.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/* eslint-disable es/no-optional-chaining */
import type {RouteProp} from '@react-navigation/native';
import {useRoute} from '@react-navigation/native';
import React, {useCallback, useMemo, useState} from 'react';
import React, {useCallback, useEffect, useMemo, useState} from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import {useOnyx, withOnyx} from 'react-native-onyx';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
Expand All @@ -21,6 +21,7 @@ import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'
import useDebouncedState from '@hooks/useDebouncedState';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as ReportActions from '@libs/actions/Report';
import Navigation from '@libs/Navigation/Navigation';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as ReportUtils from '@libs/ReportUtils';
Expand Down Expand Up @@ -94,12 +95,9 @@ function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalPro
const route = useRoute<RouteProp<TaskDetailsNavigatorParamList, typeof SCREENS.TASK.ASSIGNEE>>();
const {translate} = useLocalize();
const session = useSession();
const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false});
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
const {userToInvite, recentReports, personalDetails, currentUserOption, searchValue, setSearchValue, headerMessage, areOptionsInitialized} = useOptions();

const onChangeText = (newSearchTerm = '') => {
setSearchValue(newSearchTerm);
};
const {userToInvite, recentReports, personalDetails, currentUserOption, searchValue, debouncedSearchValue, setSearchValue, headerMessage, areOptionsInitialized} = useOptions();

const report: OnyxEntry<Report> = useMemo(() => {
if (!route.params?.reportID) {
Expand Down Expand Up @@ -200,6 +198,10 @@ function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalPro
const canModifyTask = TaskActions.canModifyTask(report, currentUserPersonalDetails.accountID);
const isTaskNonEditable = ReportUtils.isTaskReport(report) && (!canModifyTask || !isOpen);

useEffect(() => {
ReportActions.searchInServer(debouncedSearchValue);
}, [debouncedSearchValue]);

return (
<ScreenWrapper
includeSafeAreaPaddingBottom={false}
Expand All @@ -215,11 +217,12 @@ function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalPro
sections={areOptionsInitialized ? sections : []}
ListItem={UserListItem}
onSelectRow={selectReport}
onChangeText={onChangeText}
onChangeText={setSearchValue}
textInputValue={searchValue}
headerMessage={headerMessage}
textInputLabel={translate('optionsSelector.nameEmailOrPhoneNumber')}
showLoadingPlaceholder={!areOptionsInitialized}
isLoadingNewOptions={!!isSearchingForReports}
/>
</View>
</FullPageNotFoundView>
Expand Down
21 changes: 5 additions & 16 deletions src/pages/tasks/TaskShareDestinationSelectorModal.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React, {useEffect, useMemo, useState} from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import {useOnyx} from 'react-native-onyx';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import {useOptionsList} from '@components/OptionListContextProvider';
import ScreenWrapper from '@components/ScreenWrapper';
Expand All @@ -21,12 +20,6 @@ import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {Report} from '@src/types/onyx';

type TaskShareDestinationSelectorModalOnyxProps = {
isSearchingForReports: OnyxEntry<boolean>;
};

type TaskShareDestinationSelectorModalProps = TaskShareDestinationSelectorModalOnyxProps;

const selectReportHandler = (option: unknown) => {
const optionItem = option as ReportUtils.OptionData;

Expand All @@ -47,12 +40,13 @@ const reportFilter = (reportOptions: Array<OptionsListUtils.SearchOption<Report>
return filtered;
}, []);

function TaskShareDestinationSelectorModal({isSearchingForReports}: TaskShareDestinationSelectorModalProps) {
function TaskShareDestinationSelectorModal() {
const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false);
const styles = useThemeStyles();
const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState('');
const {translate} = useLocalize();
const {isOffline} = useNetwork();
const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false});
const {options: optionList, areOptionsInitialized} = useOptionsList({
shouldInitialize: didScreenTransitionEnd,
});
Expand Down Expand Up @@ -116,7 +110,7 @@ function TaskShareDestinationSelectorModal({isSearchingForReports}: TaskShareDes
headerMessage={options.headerMessage}
textInputLabel={translate('optionsSelector.nameEmailOrPhoneNumber')}
showLoadingPlaceholder={areOptionsInitialized && debouncedSearchValue.trim() === '' ? options.sections.length === 0 : !didScreenTransitionEnd}
isLoadingNewOptions={isSearchingForReports ?? undefined}
isLoadingNewOptions={!!isSearchingForReports}
textInputHint={textInputHint}
/>
</View>
Expand All @@ -127,9 +121,4 @@ function TaskShareDestinationSelectorModal({isSearchingForReports}: TaskShareDes

TaskShareDestinationSelectorModal.displayName = 'TaskShareDestinationSelectorModal';

export default withOnyx<TaskShareDestinationSelectorModalProps, TaskShareDestinationSelectorModalOnyxProps>({
isSearchingForReports: {
key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS,
initWithStoredValues: false,
},
})(TaskShareDestinationSelectorModal);
export default TaskShareDestinationSelectorModal;
Loading

0 comments on commit 7483d10

Please sign in to comment.