diff --git a/web/settings/relationship/add-users-list.css b/web/settings/relationship/add-users-list.css index 233cd2016..8194bb7b0 100644 --- a/web/settings/relationship/add-users-list.css +++ b/web/settings/relationship/add-users-list.css @@ -1,61 +1,71 @@ .container { height: 625px; display: flex; flex-direction: column; } .userRowsContainer { overflow: auto; display: flex; flex-direction: column; flex: 1; } .userListItemContainer { display: flex; color: var(--relationship-modal-color); font-size: var(--l-font-18); line-height: var(--line-height-display); padding: 8px; } .userListItemContainer:hover { cursor: pointer; background-color: var(--listItem-background-primary-hover); border-radius: 8px; } .avatarContainer { margin: 0 8px 0 16px; } .username { color: var(--text-background-secondary-default); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .selectedUsername { color: var(--text-background-primary-default); } .confirmButtonContainer { display: flex; flex-direction: column; align-items: center; } .hidden { visibility: hidden; height: 0; } .error { padding-bottom: 8px; font-size: var(--s-font-14); line-height: var(--line-height-display); color: var(--error); padding-left: 6px; font-style: italic; } + +.listHeaderContainer { + display: flex; + justify-content: space-between; + margin: 16px 8px 8px; +} + +.selectionText { + color: var(--text-background-secondary-default); +} diff --git a/web/settings/relationship/add-users-list.react.js b/web/settings/relationship/add-users-list.react.js index f8be2c132..19747fde2 100644 --- a/web/settings/relationship/add-users-list.react.js +++ b/web/settings/relationship/add-users-list.react.js @@ -1,137 +1,152 @@ // @flow import * as React from 'react'; import { useENSNames } from 'lib/hooks/ens-cache.js'; import { useUserSearchIndex } from 'lib/selectors/nav-selectors.js'; import { useSearchUsers } from 'lib/shared/search-utils.js'; import type { SetState } from 'lib/types/hook-types.js'; import type { UserRelationshipStatus } from 'lib/types/relationship-types.js'; import type { GlobalAccountUserInfo, AccountUserInfo, } from 'lib/types/user-types.js'; import { values } from 'lib/utils/objects.js'; import AddUsersListItem from './add-users-list-item.react.js'; import css from './add-users-list.css'; import { useSelector } from '../../redux/redux-utils.js'; type Props = { +searchText: string, +excludedStatuses?: $ReadOnlySet, +pendingUsersToAdd: $ReadOnlySet, +setPendingUsersToAdd: SetState<$ReadOnlySet>, +errorMessage: string, }; function AddUsersList(props: Props): React.Node { const { searchText, excludedStatuses = new Set(), pendingUsersToAdd, setPendingUsersToAdd, errorMessage, } = props; const viewerID = useSelector(state => state.currentUserInfo?.id); const userInfos = useSelector(state => state.userStore.userInfos); const userInfosArray = React.useMemo(() => values(userInfos), [userInfos]); const userStoreSearchIndex = useUserSearchIndex(userInfosArray); const [userStoreSearchResults, setUserStoreSearchResults] = React.useState< $ReadOnlySet, >(new Set(userStoreSearchIndex.getSearchResults(searchText))); React.useEffect(() => { setUserStoreSearchResults( new Set(userStoreSearchIndex.getSearchResults(searchText)), ); }, [searchText, userStoreSearchIndex]); const serverSearchResults = useSearchUsers(searchText); const searchTextPresent = searchText.length > 0; const mergedUserInfos = React.useMemo(() => { const mergedInfos: { [string]: GlobalAccountUserInfo | AccountUserInfo } = {}; for (const userInfo of serverSearchResults) { mergedInfos[userInfo.id] = userInfo; } const userStoreUserIDs = searchTextPresent ? userStoreSearchResults : Object.keys(userInfos); for (const id of userStoreUserIDs) { const { username, relationshipStatus } = userInfos[id]; if (username) { mergedInfos[id] = { id, username, relationshipStatus }; } } return mergedInfos; }, [ searchTextPresent, serverSearchResults, userInfos, userStoreSearchResults, ]); const sortedUsers = React.useMemo( () => Object.keys(mergedUserInfos) .map(userID => mergedUserInfos[userID]) .filter( user => user.id !== viewerID && (!user.relationshipStatus || !excludedStatuses.has(user.relationshipStatus)), ) .sort((user1, user2) => user1.username.localeCompare(user2.username)), [excludedStatuses, mergedUserInfos, viewerID], ); const toggleUser = React.useCallback( (userID: string) => { setPendingUsersToAdd(pendingUsers => { const newPendingUsers = new Set(pendingUsers); if (!newPendingUsers.delete(userID)) { newPendingUsers.add(userID); } return newPendingUsers; }); }, [setPendingUsersToAdd], ); const sortedUsersWithENSNames = useENSNames(sortedUsers); const userRows = React.useMemo( () => sortedUsersWithENSNames.map(userInfo => ( )), [sortedUsersWithENSNames, toggleUser, pendingUsersToAdd], ); + const listHeader = React.useMemo(() => { + let selectionText = 'Select users'; + if (pendingUsersToAdd.size > 0) { + selectionText = `${pendingUsersToAdd.size} selected`; + } + + return ( +
+
{selectionText}
+ {/* {TODO: Add clear all button here} */} +
+ ); + }, [pendingUsersToAdd.size]); + let errors; if (errorMessage) { errors =
{errorMessage}
; } return (
+ {listHeader}
{userRows}
{errors}
); } export default AddUsersList;