⚠ This page is served via a proxy. Original site: https://github.com
This service does not collect credentials or authentication data.
Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8030,6 +8030,12 @@ const CONST = {
EXPENSIFY_ADMIN_ACCESS_PREFIX: 'expensify_adminPermissions_',
/** Onyx prefix for domain security groups */
DOMAIN_SECURITY_GROUP_PREFIX: 'domain_securityGroup_',

PRIMARY_ACTIONS: {},

MEMBERS_BULK_ACTION_TYPES: {
CLOSE_ACCOUNT: 'closeAccount',
},
},
} as const;

Expand Down
3 changes: 3 additions & 0 deletions src/components/ButtonWithDropdownMenu/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ type WorkspaceMemberBulkActionType = DeepValueOf<typeof CONST.POLICY.MEMBERS_BUL

type RoomMemberBulkActionType = DeepValueOf<typeof CONST.REPORT.ROOM_MEMBERS_BULK_ACTION_TYPES>;

type DomainMemberBulkActionType = DeepValueOf<typeof CONST.DOMAIN.MEMBERS_BULK_ACTION_TYPES>;

type WorkspaceDistanceRatesBulkActionType = DeepValueOf<typeof CONST.POLICY.BULK_ACTION_TYPES>;

type WorkspaceTaxRatesBulkActionType = DeepValueOf<typeof CONST.POLICY.BULK_ACTION_TYPES>;
Expand Down Expand Up @@ -167,6 +169,7 @@ type ButtonWithDropdownMenuRef = {
export type {
PaymentType,
WorkspaceMemberBulkActionType,
DomainMemberBulkActionType,
RoomMemberBulkActionType,
WorkspaceDistanceRatesBulkActionType,
DropdownOption,
Expand Down
4 changes: 4 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7925,6 +7925,10 @@ const translations = {
members: {
title: 'Members',
findMember: 'Find member',
closeAccount: () => ({
one: 'Close account',
other: 'Close accounts',
}),
},
},
};
Expand Down
124 changes: 102 additions & 22 deletions src/pages/domain/BaseDomainMembersPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, {useCallback, useState} from 'react';
import {View} from 'react-native';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
Expand Down Expand Up @@ -62,6 +62,27 @@ type BaseDomainMembersPageProps = {

/** Callback fired when the user dismisses an error message for a specific row */
onDismissError?: (item: MemberOption) => void;

/**
* Allow multiple members to be selected at the same time.
* Defaults to false.
*/
canSelectMultiple?: boolean;

/**
* **Controlled selected members**.
* Should be provided from the parent component.
* If this is set, `controlledSetSelectedMembers` **must** also be provided.
*/
controlledSelectedMembers?: string[];

/**
* **Setter for controlled selected members**.
* Should be provided from the parent component.
* Works like the setter returned by `useState`.
* If this is set, `controlledSelectedMembers` **must** also be provided.
*/
controlledSetSelectedMembers?: React.Dispatch<React.SetStateAction<string[]>>;
};

function BaseDomainMembersPage({
Expand All @@ -75,12 +96,20 @@ function BaseDomainMembersPage({
getCustomRightElement,
getCustomRowProps,
onDismissError,
controlledSelectedMembers,
controlledSetSelectedMembers,
canSelectMultiple = false,
}: BaseDomainMembersPageProps) {
const {formatPhoneNumber, localeCompare} = useLocalize();
const styles = useThemeStyles();
const {shouldUseNarrowLayout} = useResponsiveLayout();
const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: true});
const icons = useMemoizedLazyExpensifyIcons(['FallbackAvatar']);
const [internalSelectedMembers, setInternalSelectedMembers] = useState<string[]>([]);

const selectedMembers = controlledSelectedMembers ?? internalSelectedMembers;

const setSelectedMembers = controlledSetSelectedMembers ?? setInternalSelectedMembers;

const data: MemberOption[] = accountIDs.map((accountID) => {
const details = personalDetails?.[accountID];
Expand Down Expand Up @@ -119,13 +148,39 @@ function BaseDomainMembersPage({

const [inputValue, setInputValue, filteredData] = useSearchResults(data, filterMember, sortMembers);

const toggleAllUsers = () => {
const enabledAccounts = filteredData.filter((member) => !member.isDisabled && !member.isDisabledCheckbox);
const enabledAccountIDs = enabledAccounts.map((member) => member.keyForList);
const everySelected = enabledAccountIDs.every((accountID) => selectedMembers.includes(accountID));

if (everySelected) {
setSelectedMembers((prevSelected) => prevSelected.filter((accountID) => !enabledAccountIDs.includes(accountID)));
} else {
setSelectedMembers((prevSelected) => {
const newSelected = new Set([...prevSelected, ...enabledAccountIDs]);
return Array.from(newSelected);
});
}
};

const toggleUser = useCallback(
(member: MemberOption) => {
if (selectedMembers.includes(member.keyForList)) {
setSelectedMembers((prevSelected) => prevSelected.filter((accountID) => accountID !== member.keyForList));
} else {
setSelectedMembers((prevSelected) => [...prevSelected, member.keyForList]);
}
},
[selectedMembers],
);

const getCustomListHeader = () => {
if (filteredData.length === 0) {
return null;
}
return (
<CustomListHeader
canSelectMultiple={false}
canSelectMultiple
leftHeaderText={headerTitle}
/>
);
Expand Down Expand Up @@ -160,26 +215,51 @@ function BaseDomainMembersPage({

{shouldUseNarrowLayout && !!headerContent && <View style={[styles.pl5, styles.pr5, styles.flexRow, styles.gap2]}>{headerContent}</View>}

<SelectionList
data={filteredData}
shouldShowRightCaret
canSelectMultiple={false}
style={{
containerStyle: styles.flex1,
listHeaderWrapperStyle: [styles.ph9, styles.pv3, styles.pb5],
listItemTitleContainerStyles: shouldUseNarrowLayout ? undefined : styles.pr3,
}}
ListItem={TableListItem}
onSelectRow={onSelectRow}
onDismissError={onDismissError}
showListEmptyContent={false}
showScrollIndicator={false}
addBottomSafeAreaPadding
shouldHeaderBeInsideList
customListHeader={getCustomListHeader()}
customListHeaderContent={listHeaderContent}
disableMaintainingScrollPosition
/>
{canSelectMultiple ? (
<SelectionList
data={filteredData}
shouldShowRightCaret
canSelectMultiple
style={{
containerStyle: styles.flex1,
listHeaderWrapperStyle: [styles.ph9, styles.pv3, styles.pb5],
listItemTitleContainerStyles: shouldUseNarrowLayout ? undefined : styles.pr3,
}}
ListItem={TableListItem}
onSelectRow={onSelectRow}
onSelectAll={toggleAllUsers}
onCheckboxPress={toggleUser}
selectedItems={selectedMembers}
onDismissError={onDismissError}
showListEmptyContent={false}
showScrollIndicator={false}
addBottomSafeAreaPadding
shouldHeaderBeInsideList
customListHeader={getCustomListHeader()}
customListHeaderContent={listHeaderContent}
disableMaintainingScrollPosition
/>
) : (
<SelectionList
data={filteredData}
shouldShowRightCaret
style={{
containerStyle: styles.flex1,
listHeaderWrapperStyle: [styles.ph9, styles.pv3, styles.pb5],
listItemTitleContainerStyles: shouldUseNarrowLayout ? undefined : styles.pr3,
}}
ListItem={TableListItem}
onSelectRow={onSelectRow}
onDismissError={onDismissError}
showListEmptyContent={false}
showScrollIndicator={false}
addBottomSafeAreaPadding
shouldHeaderBeInsideList
customListHeader={getCustomListHeader()}
customListHeaderContent={listHeaderContent}
disableMaintainingScrollPosition
/>
)}
</ScreenWrapper>
</DomainNotFoundPageWrapper>
);
Expand Down
61 changes: 59 additions & 2 deletions src/pages/domain/Members/DomainMembersPage.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import {memberAccountIDsSelector} from '@selectors/Domain';
import React from 'react';
import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset';
import React, {useState} from 'react';
import {View} from 'react-native';
import Button from '@components/Button';

Check failure on line 4 in src/pages/domain/Members/DomainMembersPage.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

'Button' is defined but never used

Check failure on line 4 in src/pages/domain/Members/DomainMembersPage.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

'Button' is defined but never used

Check failure on line 4 in src/pages/domain/Members/DomainMembersPage.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'Button' is defined but never used

Check failure on line 4 in src/pages/domain/Members/DomainMembersPage.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'Button' is defined but never used
import ButtonWithDropdownMenu from '@components/ButtonWithDropdownMenu';
import type {DomainMemberBulkActionType, DropdownOption, WorkspaceMemberBulkActionType} from '@components/ButtonWithDropdownMenu/types';

Check failure on line 6 in src/pages/domain/Members/DomainMembersPage.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

'WorkspaceMemberBulkActionType' is defined but never used

Check failure on line 6 in src/pages/domain/Members/DomainMembersPage.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

'WorkspaceMemberBulkActionType' is defined but never used

Check failure on line 6 in src/pages/domain/Members/DomainMembersPage.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'WorkspaceMemberBulkActionType' is defined but never used

Check failure on line 6 in src/pages/domain/Members/DomainMembersPage.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'WorkspaceMemberBulkActionType' is defined but never used
import {Plus} from '@components/Icon/Expensicons';

Check failure on line 7 in src/pages/domain/Members/DomainMembersPage.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

'Plus' is defined but never used

Check failure on line 7 in src/pages/domain/Members/DomainMembersPage.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

'Plus' is defined but never used

Check failure on line 7 in src/pages/domain/Members/DomainMembersPage.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'Plus' is defined but never used

Check warning on line 7 in src/pages/domain/Members/DomainMembersPage.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'@components/Icon/Expensicons' import is restricted from being used by a pattern. Direct imports from Icon/Expensicons are deprecated. Please use lazy loading hooks instead. Use `useMemoizedLazyExpensifyIcons` from @hooks/useLazyAsset. See docs/LAZY_ICONS_AND_ILLUSTRATIONS.md for details

Check warning on line 7 in src/pages/domain/Members/DomainMembersPage.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'@components/Icon/Expensicons' import is restricted from being used. Direct imports from @components/Icon/Expensicons are deprecated. Please use lazy loading hooks instead. Use `useMemoizedLazyExpensifyIcons` from @hooks/useLazyAsset. See docs/LAZY_ICONS_AND_ILLUSTRATIONS.md for details

Check failure on line 7 in src/pages/domain/Members/DomainMembersPage.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'Plus' is defined but never used

Check warning on line 7 in src/pages/domain/Members/DomainMembersPage.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'@components/Icon/Expensicons' import is restricted from being used by a pattern. Direct imports from Icon/Expensicons are deprecated. Please use lazy loading hooks instead. Use `useMemoizedLazyExpensifyIcons` from @hooks/useLazyAsset. See docs/LAZY_ICONS_AND_ILLUSTRATIONS.md for details

Check warning on line 7 in src/pages/domain/Members/DomainMembersPage.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'@components/Icon/Expensicons' import is restricted from being used. Direct imports from @components/Icon/Expensicons are deprecated. Please use lazy loading hooks instead. Use `useMemoizedLazyExpensifyIcons` from @hooks/useLazyAsset. See docs/LAZY_ICONS_AND_ILLUSTRATIONS.md for details
import {useMemoizedLazyExpensifyIcons, useMemoizedLazyIllustrations} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@navigation/Navigation';
import type {PlatformStackScreenProps} from '@navigation/PlatformStackNavigation/types';
import type {DomainSplitNavigatorParamList} from '@navigation/types';
import BaseDomainMembersPage from '@pages/domain/BaseDomainMembersPage';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
Expand All @@ -16,13 +24,58 @@
function DomainMembersPage({route}: DomainMembersPageProps) {
const {domainAccountID} = route.params;
const {translate} = useLocalize();
const styles = useThemeStyles();
const illustrations = useMemoizedLazyIllustrations(['Profile']);
const [controlledSelectedMembers, controlledSetSelectedMembers] = useState<string[]>([]);
const {shouldUseNarrowLayout, isSmallScreenWidth} = useResponsiveLayout();

Check failure on line 30 in src/pages/domain/Members/DomainMembersPage.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

'isSmallScreenWidth' is assigned a value but never used

Check failure on line 30 in src/pages/domain/Members/DomainMembersPage.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

'isSmallScreenWidth' is assigned a value but never used

Check failure on line 30 in src/pages/domain/Members/DomainMembersPage.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'isSmallScreenWidth' is assigned a value but never used

Check warning on line 30 in src/pages/domain/Members/DomainMembersPage.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Prefer using `shouldUseNarrowLayout` instead of `isSmallScreenWidth` from `useResponsiveLayout`

Check failure on line 30 in src/pages/domain/Members/DomainMembersPage.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'isSmallScreenWidth' is assigned a value but never used

Check warning on line 30 in src/pages/domain/Members/DomainMembersPage.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Prefer using `shouldUseNarrowLayout` instead of `isSmallScreenWidth` from `useResponsiveLayout`
const icons = useMemoizedLazyExpensifyIcons(['RemoveMembers']);

const [memberIDs] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
canBeMissing: true,
selector: memberAccountIDsSelector,
});

const getBulkActionsButtonOptions = () => {
const options: Array<DropdownOption<DomainMemberBulkActionType>> = [
{
text: translate('domain.members.closeAccount', {count: controlledSetSelectedMembers.length}),
value: CONST.DOMAIN.MEMBERS_BULK_ACTION_TYPES.CLOSE_ACCOUNT,
icon: icons.RemoveMembers,
onSelected: () => {},
},
];

return options;
};

const getHeaderButtons = () => {
return controlledSelectedMembers.length > 0 ? (
<ButtonWithDropdownMenu<DomainMemberBulkActionType>
shouldAlwaysShowDropdownMenu
customText={translate('workspace.common.selected', {count: controlledSelectedMembers.length})}
buttonSize={CONST.DROPDOWN_BUTTON_SIZE.MEDIUM}
onPress={() => null}
options={getBulkActionsButtonOptions()}
isSplitButton={false}
style={[shouldUseNarrowLayout && styles.flexGrow1, shouldUseNarrowLayout && styles.mb3]}
isDisabled={!controlledSelectedMembers.length}
testID="DomainMembersPage-header-dropdown-menu-button"
/>
) : (
<View style={[styles.flexRow, styles.gap2]}>
<ButtonWithDropdownMenu
success={false}
onPress={() => {}}
shouldAlwaysShowDropdownMenu
customText={translate('common.more')}
options={[]}
isSplitButton={false}
wrapperStyle={styles.flexGrow0}
/>
</View>
);
};

return (
<BaseDomainMembersPage
domainAccountID={domainAccountID}
Expand All @@ -31,6 +84,10 @@
searchPlaceholder={translate('domain.members.findMember')}
onSelectRow={(item) => Navigation.navigate(ROUTES.DOMAIN_MEMBER_DETAILS.getRoute(domainAccountID, item.accountID))}
headerIcon={illustrations.Profile}
headerContent={getHeaderButtons()}
canSelectMultiple
controlledSelectedMembers={controlledSelectedMembers}
controlledSetSelectedMembers={controlledSetSelectedMembers}
/>
);
}
Expand Down
Loading