diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index b0996a08895a..edaa48f2cf7a 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -287,7 +287,7 @@ function BaseSelectionList( showTooltip={showTooltip} canSelectMultiple={canSelectMultiple} onSelectRow={() => selectRow(item)} - onDismissError={onDismissError} + onDismissError={() => onDismissError?.(item)} shouldPreventDefaultFocusOnSelectRow={shouldPreventDefaultFocusOnSelectRow} rightHandSideComponent={rightHandSideComponent} keyForList={item.keyForList} diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index 403ccd91a26b..868e47309921 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -155,7 +155,7 @@ type BaseSelectionListProps = Partial & { onSelectAll?: () => void; /** Callback to fire when an error is dismissed */ - onDismissError?: () => void; + onDismissError?: (item: TItem) => void; /** Label for the text input */ textInputLabel?: string; diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index a85e97a4cf05..974ce88a03ec 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -93,7 +93,7 @@ function shouldShowPolicy(policy: OnyxEntry, isOffline: boolean): boolea ); } -function isExpensifyTeam(email: string): boolean { +function isExpensifyTeam(email: string | undefined): boolean { const emailDomain = Str.extractEmailDomain(email ?? ''); return emailDomain === CONST.EXPENSIFY_PARTNER_NAME || emailDomain === CONST.EMAIL.GUIDES_DOMAIN; } diff --git a/src/pages/workspace/WorkspaceMembersPage.js b/src/pages/workspace/WorkspaceMembersPage.tsx similarity index 57% rename from src/pages/workspace/WorkspaceMembersPage.js rename to src/pages/workspace/WorkspaceMembersPage.tsx index 62b96943453c..5554f6ad282b 100644 --- a/src/pages/workspace/WorkspaceMembersPage.js +++ b/src/pages/workspace/WorkspaceMembersPage.tsx @@ -1,10 +1,11 @@ import {useIsFocused} from '@react-navigation/native'; -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; +import type {StackScreenProps} from '@react-navigation/stack'; +import lodashIsEqual from 'lodash/isEqual'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import type {TextInput} from 'react-native'; import {InteractionManager, View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import Button from '@components/Button'; import ConfirmModal from '@components/ConfirmModal'; @@ -12,84 +13,75 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; import * as Illustrations from '@components/Icon/Illustrations'; import MessagesRow from '@components/MessagesRow'; -import networkPropTypes from '@components/networkPropTypes'; -import {withNetwork} from '@components/OnyxProvider'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; +import type {ListItem} from '@components/SelectionList/types'; import UserListItem from '@components/SelectionList/UserListItem'; import Text from '@components/Text'; -import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; -import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; +import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; +import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; +import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import compose from '@libs/compose'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; +import type {CentralPaneNavigatorParamList} from '@libs/Navigation/types'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as UserUtils from '@libs/UserUtils'; -import personalDetailsPropType from '@pages/personalDetailsPropType'; import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; +import type {PersonalDetailsList, PolicyMember, PolicyMembers, Session} from '@src/types/onyx'; +import type {Errors, PendingAction} from '@src/types/onyx/OnyxCommon'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import SearchInputManager from './SearchInputManager'; -import {policyDefaultProps, policyPropTypes} from './withPolicy'; +import type {WithPolicyAndFullscreenLoadingProps} from './withPolicyAndFullscreenLoading'; import withPolicyAndFullscreenLoading from './withPolicyAndFullscreenLoading'; -const propTypes = { - /** All personal details asssociated with user */ - personalDetails: PropTypes.objectOf(personalDetailsPropType), - - /** URL Route params */ - route: PropTypes.shape({ - /** Params from the URL path */ - params: PropTypes.shape({ - /** policyID passed via route: /workspace/:policyID/members */ - policyID: PropTypes.string, - }), - }).isRequired, - +type WorkspaceMembersPageOnyxProps = { + /** Personal details of all users */ + personalDetails: OnyxEntry; /** Session info for the currently logged in user. */ - session: PropTypes.shape({ - /** Currently logged in user accountID */ - accountID: PropTypes.number, - }), - - isLoadingReportData: PropTypes.bool, - ...policyPropTypes, - ...withLocalizePropTypes, - ...windowDimensionsPropTypes, - ...withCurrentUserPersonalDetailsPropTypes, - network: networkPropTypes.isRequired, + session: OnyxEntry; }; -const defaultProps = { - personalDetails: {}, - session: { - accountID: 0, - }, - isLoadingReportData: true, - ...policyDefaultProps, - ...withCurrentUserPersonalDetailsDefaultProps, -}; +type WorkspaceMembersPageProps = WithPolicyAndFullscreenLoadingProps & + WithCurrentUserPersonalDetailsProps & + WorkspaceMembersPageOnyxProps & + StackScreenProps; + +/** + * Inverts an object, equivalent of _.invert + */ +function invertObject(object: Record): Record { + const invertedEntries = Object.entries(object).map(([key, value]) => [value, key]); + const inverted: Record = Object.fromEntries(invertedEntries); + return inverted; +} + +type MemberOption = Omit & {accountID: number}; -function WorkspaceMembersPage(props) { +function WorkspaceMembersPage({policyMembers, personalDetails, route, policy, session, currentUserPersonalDetails, isLoadingReportData = true}: WorkspaceMembersPageProps) { const styles = useThemeStyles(); - const [selectedEmployees, setSelectedEmployees] = useState([]); + const [selectedEmployees, setSelectedEmployees] = useState([]); const [removeMembersConfirmModalVisible, setRemoveMembersConfirmModalVisible] = useState(false); const [errors, setErrors] = useState({}); const [searchValue, setSearchValue] = useState(''); - const prevIsOffline = usePrevious(props.network.isOffline); - const accountIDs = useMemo(() => _.map(_.keys(props.policyMembers), (accountID) => Number(accountID)), [props.policyMembers]); + const {isOffline} = useNetwork(); + const prevIsOffline = usePrevious(isOffline); + const accountIDs = useMemo(() => Object.keys(policyMembers ?? {}).map((accountID) => Number(accountID)), [policyMembers]); const prevAccountIDs = usePrevious(accountIDs); - const textInputRef = useRef(null); - const isOfflineAndNoMemberDataAvailable = _.isEmpty(props.policyMembers) && props.network.isOffline; - const prevPersonalDetails = usePrevious(props.personalDetails); + const textInputRef = useRef(null); + const isOfflineAndNoMemberDataAvailable = isEmptyObject(policyMembers) && isOffline; + const prevPersonalDetails = usePrevious(personalDetails); + const {translate, formatPhoneNumber, preferredLocale} = useLocalize(); const {isSmallScreenWidth} = useWindowDimensions(); const isFocusedScreen = useIsFocused(); @@ -98,51 +90,49 @@ function WorkspaceMembersPage(props) { setSearchValue(SearchInputManager.searchInput); }, [isFocusedScreen]); - useEffect(() => () => (SearchInputManager.searchInput = ''), []); + useEffect( + () => () => { + SearchInputManager.searchInput = ''; + }, + [], + ); /** * Get filtered personalDetails list with current policyMembers - * @param {Object} policyMembers - * @param {Object} personalDetails - * @returns {Object} */ - const filterPersonalDetails = (policyMembers, personalDetails) => - _.reduce( - _.keys(policyMembers), - (result, key) => { - if (personalDetails[key]) { - return { - ...result, - [key]: personalDetails[key], - }; - } - return result; - }, - {}, - ); + const filterPersonalDetails = (members: OnyxEntry, details: OnyxEntry): PersonalDetailsList => + Object.keys(members ?? {}).reduce((result, key) => { + if (details?.[key]) { + return { + ...result, + [key]: details[key], + }; + } + return result; + }, {}); /** * Get members for the current workspace */ const getWorkspaceMembers = useCallback(() => { - Policy.openWorkspaceMembersPage(props.route.params.policyID, _.keys(PolicyUtils.getMemberAccountIDsForWorkspace(props.policyMembers, props.personalDetails))); - }, [props.route.params.policyID, props.policyMembers, props.personalDetails]); + Policy.openWorkspaceMembersPage(route.params.policyID, Object.keys(PolicyUtils.getMemberAccountIDsForWorkspace(policyMembers, personalDetails))); + }, [route.params.policyID, policyMembers, personalDetails]); /** * Check if the current selection includes members that cannot be removed */ const validateSelection = useCallback(() => { - const newErrors = {}; - const ownerAccountID = _.first(PersonalDetailsUtils.getAccountIDsByLogins(props.policy.owner ? [props.policy.owner] : [])); - _.each(selectedEmployees, (member) => { - if (member !== ownerAccountID && member !== props.session.accountID) { + const newErrors: Errors = {}; + const ownerAccountID = PersonalDetailsUtils.getAccountIDsByLogins(policy?.owner ? [policy.owner] : [])[0]; + selectedEmployees.forEach((member) => { + if (member !== ownerAccountID && member !== session?.accountID) { return; } - newErrors[member] = props.translate('workspace.people.error.cannotRemove'); + newErrors[member] = translate('workspace.people.error.cannotRemove'); }); setErrors(newErrors); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedEmployees, props.policy.owner, props.session.accountID]); + }, [selectedEmployees, policy?.owner, session?.accountID]); useEffect(() => { getWorkspaceMembers(); @@ -151,40 +141,43 @@ function WorkspaceMembersPage(props) { useEffect(() => { validateSelection(); - }, [props.preferredLocale, validateSelection]); + }, [preferredLocale, validateSelection]); useEffect(() => { - if (removeMembersConfirmModalVisible && !_.isEqual(accountIDs, prevAccountIDs)) { + if (removeMembersConfirmModalVisible && !lodashIsEqual(accountIDs, prevAccountIDs)) { setRemoveMembersConfirmModalVisible(false); } setSelectedEmployees((prevSelected) => { // Filter all personal details in order to use the elements needed for the current workspace - const currentPersonalDetails = filterPersonalDetails(props.policyMembers, props.personalDetails); + const currentPersonalDetails = filterPersonalDetails(policyMembers, personalDetails); // We need to filter the previous selected employees by the new personal details, since unknown/new user id's change when transitioning from offline to online - const prevSelectedElements = _.map(prevSelected, (id) => { - const prevItem = lodashGet(prevPersonalDetails, id); - const res = _.find(_.values(currentPersonalDetails), (item) => lodashGet(prevItem, 'login') === lodashGet(item, 'login')); - return lodashGet(res, 'accountID', id); + const prevSelectedElements = prevSelected.map((id) => { + const prevItem = prevPersonalDetails?.id; + const res = Object.values(currentPersonalDetails).find((item) => prevItem?.login === item?.login); + return res?.accountID ?? id; }); - return _.intersection(prevSelectedElements, _.values(PolicyUtils.getMemberAccountIDsForWorkspace(props.policyMembers, props.personalDetails))); + // This is an equivalent of the lodash intersection function. The reduce method below is used to filter the items that exist in both arrays. + return [prevSelectedElements, Object.values(PolicyUtils.getMemberAccountIDsForWorkspace(policyMembers, personalDetails))].reduce((prev, members) => + prev.filter((item) => members.includes(item)), + ); }); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.policyMembers]); + }, [policyMembers]); useEffect(() => { - const isReconnecting = prevIsOffline && !props.network.isOffline; + const isReconnecting = prevIsOffline && !isOffline; if (!isReconnecting) { return; } getWorkspaceMembers(); - }, [props.network.isOffline, prevIsOffline, getWorkspaceMembers]); + }, [isOffline, prevIsOffline, getWorkspaceMembers]); /** * Open the modal to invite a user */ const inviteUser = () => { setSearchValue(''); - Navigation.navigate(ROUTES.WORKSPACE_INVITE.getRoute(props.route.params.policyID)); + Navigation.navigate(ROUTES.WORKSPACE_INVITE.getRoute(route.params.policyID)); }; /** @@ -192,14 +185,14 @@ function WorkspaceMembersPage(props) { * Please see https://github.com/Expensify/App/blob/main/README.md#Security for more details */ const removeUsers = () => { - if (!_.isEmpty(errors)) { + if (!isEmptyObject(errors)) { return; } // Remove the admin from the list - const accountIDsToRemove = _.without(selectedEmployees, props.session.accountID); + const accountIDsToRemove = session?.accountID ? selectedEmployees.filter((id) => id !== session.accountID) : selectedEmployees; - Policy.removeMembers(accountIDsToRemove, props.route.params.policyID); + Policy.removeMembers(accountIDsToRemove, route.params.policyID); setSelectedEmployees([]); setRemoveMembersConfirmModalVisible(false); }; @@ -208,7 +201,7 @@ function WorkspaceMembersPage(props) { * Show the modal to confirm removal of the selected members */ const askForConfirmationToRemove = () => { - if (!_.isEmpty(errors)) { + if (!isEmptyObject(errors)) { return; } setRemoveMembersConfirmModalVisible(true); @@ -216,16 +209,15 @@ function WorkspaceMembersPage(props) { /** * Add or remove all users passed from the selectedEmployees list - * @param {Object} memberList */ - const toggleAllUsers = (memberList) => { - const enabledAccounts = _.filter(memberList, (member) => !member.isDisabled); - const everyoneSelected = _.every(enabledAccounts, (member) => _.contains(selectedEmployees, member.accountID)); + const toggleAllUsers = (memberList: MemberOption[]) => { + const enabledAccounts = memberList.filter((member) => !member.isDisabled); + const everyoneSelected = enabledAccounts.every((member) => selectedEmployees.includes(member.accountID)); if (everyoneSelected) { setSelectedEmployees([]); } else { - const everyAccountId = _.map(enabledAccounts, (member) => member.accountID); + const everyAccountId = enabledAccounts.map((member) => member.accountID); setSelectedEmployees(everyAccountId); } @@ -234,11 +226,9 @@ function WorkspaceMembersPage(props) { /** * Add user from the selectedEmployees list - * - * @param {String} login */ const addUser = useCallback( - (accountID) => { + (accountID: number) => { setSelectedEmployees((prevSelected) => [...prevSelected, accountID]); validateSelection(); }, @@ -247,12 +237,10 @@ function WorkspaceMembersPage(props) { /** * Remove user from the selectedEmployees list - * - * @param {String} login */ const removeUser = useCallback( - (accountID) => { - setSelectedEmployees((prevSelected) => _.without(prevSelected, accountID)); + (accountID: number) => { + setSelectedEmployees((prevSelected) => prevSelected.filter((id) => id !== accountID)); validateSelection(); }, [validateSelection], @@ -260,19 +248,15 @@ function WorkspaceMembersPage(props) { /** * Toggle user from the selectedEmployees list - * - * @param {String} accountID - * @param {String} pendingAction - * */ const toggleUser = useCallback( - (accountID, pendingAction) => { + (accountID: number, pendingAction?: PendingAction) => { if (pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { return; } // Add or remove the user if the checkbox is enabled - if (_.contains(selectedEmployees, accountID)) { + if (selectedEmployees.includes(accountID)) { removeUser(accountID); } else { addUser(accountID); @@ -283,42 +267,39 @@ function WorkspaceMembersPage(props) { /** * Dismisses the errors on one item - * - * @param {Object} item */ const dismissError = useCallback( - (item) => { + (item: MemberOption) => { if (item.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { - Policy.clearDeleteMemberError(props.route.params.policyID, item.accountID); + Policy.clearDeleteMemberError(route.params.policyID, item.accountID); } else { - Policy.clearAddMemberError(props.route.params.policyID, item.accountID); + Policy.clearAddMemberError(route.params.policyID, item.accountID); } }, - [props.route.params.policyID], + [route.params.policyID], ); /** * Check if the policy member is deleted from the workspace - * - * @param {Object} policyMember - * @returns {Boolean} */ - const isDeletedPolicyMember = (policyMember) => !props.network.isOffline && policyMember.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && _.isEmpty(policyMember.errors); - const policyOwner = lodashGet(props.policy, 'owner'); - const currentUserLogin = lodashGet(props.currentUserPersonalDetails, 'login'); - const policyID = lodashGet(props.route, 'params.policyID'); - const invitedPrimaryToSecondaryLogins = _.invert(props.policy.primaryLoginsInvited); + const isDeletedPolicyMember = (policyMember: PolicyMember): boolean => + !isOffline && policyMember.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && isEmptyObject(policyMember.errors); + const policyOwner = policy?.owner; + const currentUserLogin = currentUserPersonalDetails.login; + const policyID = route.params.policyID; + + const invitedPrimaryToSecondaryLogins = invertObject(policy?.primaryLoginsInvited ?? {}); - const getMemberOptions = () => { - let result = []; + const getUsers = (): MemberOption[] => { + let result: MemberOption[] = []; - _.each(props.policyMembers, (policyMember, accountIDKey) => { + Object.entries(policyMembers ?? {}).forEach(([accountIDKey, policyMember]) => { const accountID = Number(accountIDKey); if (isDeletedPolicyMember(policyMember)) { return; } - const details = props.personalDetails[accountID]; + const details = personalDetails?.[accountID]; if (!details) { Log.hmmm(`[WorkspaceMembersPage] no personal details found for policy member with accountID: ${accountID}`); @@ -352,34 +333,34 @@ function WorkspaceMembersPage(props) { // If this policy is owned by Expensify then show all support (expensify.com or team.expensify.com) emails // We don't want to show guides as policy members unless the user is a guide. Some customers get confused when they // see random people added to their policy, but guides having access to the policies help set them up. - if (PolicyUtils.isExpensifyTeam(details.login || details.displayName)) { + if (PolicyUtils.isExpensifyTeam(details?.login ?? details?.displayName)) { if (policyOwner && currentUserLogin && !PolicyUtils.isExpensifyTeam(policyOwner) && !PolicyUtils.isExpensifyTeam(currentUserLogin)) { return; } } - const isAdmin = props.session.email === details.login || policyMember.role === CONST.POLICY.ROLE.ADMIN; + const isAdmin = session?.email === details.login || policyMember.role === CONST.POLICY.ROLE.ADMIN; result.push({ keyForList: accountIDKey, accountID, - isSelected: _.contains(selectedEmployees, accountID), + isSelected: selectedEmployees.includes(accountID), isDisabled: - accountID === props.session.accountID || - details.login === props.policy.owner || + accountID === session?.accountID || + details.login === policy?.owner || policyMember.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || - !_.isEmpty(policyMember.errors), - text: props.formatPhoneNumber(PersonalDetailsUtils.getDisplayNameOrDefault(details)), - alternateText: props.formatPhoneNumber(details.login), + !isEmptyObject(policyMember.errors), + text: formatPhoneNumber(PersonalDetailsUtils.getDisplayNameOrDefault(details)), + alternateText: formatPhoneNumber(details?.login ?? ''), rightElement: isAdmin ? ( - {props.translate('common.admin')} + {translate('common.admin')} - ) : null, + ) : undefined, icons: [ { source: UserUtils.getAvatar(details.avatar, accountID), - name: props.formatPhoneNumber(details.login), + name: formatPhoneNumber(details?.login ?? ''), type: CONST.ICON_TYPE_AVATAR, id: accountID, }, @@ -388,31 +369,32 @@ function WorkspaceMembersPage(props) { pendingAction: policyMember.pendingAction, // Note which secondary login was used to invite this primary login - invitedSecondaryLogin: invitedPrimaryToSecondaryLogins[details.login] || '', + invitedSecondaryLogin: details?.login ? invitedPrimaryToSecondaryLogins[details.login] ?? '' : '', }); }); - result = _.sortBy(result, (value) => value.text.toLowerCase()); + result = result.sort((a, b) => a.text.toLowerCase().localeCompare(b.text.toLowerCase())); return result; }; - const data = getMemberOptions(); + const data = getUsers(); const getHeaderMessage = () => { if (isOfflineAndNoMemberDataAvailable) { - return props.translate('workspace.common.mustBeOnlineToViewMembers'); + return translate('workspace.common.mustBeOnlineToViewMembers'); } - return searchValue.trim() && !data.length ? props.translate('workspace.common.memberNotFound') : ''; + return searchValue.trim() && !data.length ? translate('workspace.common.memberNotFound') : ''; }; const getHeaderContent = () => { - if (_.isEmpty(invitedPrimaryToSecondaryLogins)) { + if (isEmptyObject(invitedPrimaryToSecondaryLogins)) { return null; } return ( Policy.dismissAddedWithPrimaryLoginMessages(policyID)} /> @@ -425,7 +407,7 @@ function WorkspaceMembersPage(props) { medium success onPress={inviteUser} - text={props.translate('workspace.invite.member')} + text={translate('workspace.invite.member')} icon={Expensicons.Plus} iconStyles={{transform: [{scale: 0.6}]}} innerStyles={[isSmallScreenWidth && styles.alignItemsCenter]} @@ -436,7 +418,7 @@ function WorkspaceMembersPage(props) { danger style={[styles.ml2, isSmallScreenWidth && styles.w50]} isDisabled={selectedEmployees.length === 0} - text={props.translate('common.remove')} + text={translate('common.remove')} onPress={askForConfirmationToRemove} /> @@ -450,12 +432,12 @@ function WorkspaceMembersPage(props) { shouldShowOfflineIndicatorInWideScreen > Navigation.goBack(ROUTES.SETTINGS_WORKSPACES)} > { setSearchValue(''); @@ -469,28 +451,28 @@ function WorkspaceMembersPage(props) { {isSmallScreenWidth && {getHeaderButtons()}} setRemoveMembersConfirmModalVisible(false)} - prompt={props.translate('workspace.people.removeMembersPrompt')} - confirmText={props.translate('common.remove')} - cancelText={props.translate('common.cancel')} - onModalHide={() => + prompt={translate('workspace.people.removeMembersPrompt')} + confirmText={translate('common.remove')} + cancelText={translate('common.cancel')} + onModalHide={() => { InteractionManager.runAfterInteractions(() => { if (!textInputRef.current) { return; } textInputRef.current.focus(); - }) - } + }); + }} /> { SearchInputManager.searchInput = value; @@ -502,10 +484,10 @@ function WorkspaceMembersPage(props) { onSelectRow={(item) => toggleUser(item.accountID)} onSelectAll={() => toggleAllUsers(data)} onDismissError={dismissError} - showLoadingPlaceholder={!isOfflineAndNoMemberDataAvailable && (!OptionsListUtils.isPersonalDetailsReady(props.personalDetails) || _.isEmpty(props.policyMembers))} + showLoadingPlaceholder={!isOfflineAndNoMemberDataAvailable && (!OptionsListUtils.isPersonalDetailsReady(personalDetails) || isEmptyObject(policyMembers))} showScrollIndicator shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()} - inputRef={textInputRef} + ref={textInputRef} /> @@ -513,25 +495,17 @@ function WorkspaceMembersPage(props) { ); } -WorkspaceMembersPage.propTypes = propTypes; -WorkspaceMembersPage.defaultProps = defaultProps; WorkspaceMembersPage.displayName = 'WorkspaceMembersPage'; -export default compose( - withLocalize, - withWindowDimensions, - withPolicyAndFullscreenLoading, - withNetwork(), - withOnyx({ - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, - session: { - key: ONYXKEYS.SESSION, - }, - isLoadingReportData: { - key: ONYXKEYS.IS_LOADING_REPORT_DATA, - }, - }), - withCurrentUserPersonalDetails, -)(WorkspaceMembersPage); +export default withCurrentUserPersonalDetails( + withPolicyAndFullscreenLoading( + withOnyx({ + personalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + }, + session: { + key: ONYXKEYS.SESSION, + }, + })(WorkspaceMembersPage), + ), +);