diff --git a/web/components/button.react.js b/web/components/button.react.js --- a/web/components/button.react.js +++ b/web/components/button.react.js @@ -5,10 +5,12 @@ import css from './button.css'; +export type ButtonVariant = 'primary' | 'secondary' | 'danger' | 'round'; + type Props = { +onClick: (event: SyntheticEvent<HTMLButtonElement>) => mixed, +children: React.Node, - +variant?: 'primary' | 'secondary' | 'danger' | 'round', + +variant?: ButtonVariant, +type?: string, +disabled?: boolean, +className?: string, diff --git a/web/settings/relationship/add-users-list.css b/web/settings/relationship/add-users-list.css --- a/web/settings/relationship/add-users-list.css +++ b/web/settings/relationship/add-users-list.css @@ -1,5 +1,5 @@ .container { - height: 580px; + height: 625px; display: flex; flex-direction: column; } @@ -8,6 +8,7 @@ display: flex; flex-wrap: wrap; gap: 6px; + margin: 8px; } .userRowsContainer { @@ -15,6 +16,7 @@ display: flex; flex-direction: column; flex: 1; + margin-bottom: 8px; } .addUserButton { @@ -34,3 +36,19 @@ overflow: hidden; text-overflow: ellipsis; } + +.buttons { + display: flex; + justify-content: space-between; +} + +.confirmButtonContainer { + display: flex; + flex-direction: column; + align-items: center; +} + +.hidden { + visibility: hidden; + height: 0; +} diff --git a/web/settings/relationship/add-users-list.react.js b/web/settings/relationship/add-users-list.react.js --- a/web/settings/relationship/add-users-list.react.js +++ b/web/settings/relationship/add-users-list.react.js @@ -2,24 +2,53 @@ import * as React from 'react'; +import { + updateRelationships, + updateRelationshipsActionTypes, +} from 'lib/actions/relationship-actions.js'; import { searchUsers } from 'lib/actions/user-actions.js'; +import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js'; import { userStoreSearchIndex as userStoreSearchIndexSelector } from 'lib/selectors/user-selectors.js'; -import type { UserRelationshipStatus } from 'lib/types/relationship-types.js'; +import type { + UserRelationshipStatus, + RelationshipAction, +} from 'lib/types/relationship-types.js'; import type { GlobalAccountUserInfo } from 'lib/types/user-types.js'; -import { useServerCall } from 'lib/utils/action-utils.js'; +import { + useDispatchActionPromise, + useServerCall, +} from 'lib/utils/action-utils.js'; +import Button from '../../components/button.react.js'; +import type { ButtonVariant } from '../../components/button.react.js'; import Label from '../../components/label.react.js'; +import LoadingIndicator from '../../loading-indicator.react.js'; import { useSelector } from '../../redux/redux-utils.js'; import AddUsersListItem from './add-users-list-item.react.js'; import css from './add-users-list.css'; +const loadingStatusSelector = createLoadingStatusSelector( + updateRelationshipsActionTypes, +); + type Props = { +searchText: string, +excludedStatuses?: $ReadOnlySet<UserRelationshipStatus>, + +closeModal: () => void, + +confirmButtonContent: React.Node, + +confirmButtonVariant: ButtonVariant, + +relationshipAction: RelationshipAction, }; function AddUsersList(props: Props): React.Node { - const { searchText, excludedStatuses = new Set() } = props; + const { + searchText, + excludedStatuses = new Set(), + closeModal, + confirmButtonContent, + confirmButtonVariant, + relationshipAction, + } = props; const userStoreSearchIndex = useSelector(userStoreSearchIndexSelector); const [userStoreSearchResults, setUserStoreSearchResults] = React.useState< @@ -155,10 +184,52 @@ )), [filteredUsers, selectUser], ); + + const callUpdateRelationships = useServerCall(updateRelationships); + const dispatchActionPromise = useDispatchActionPromise(); + const confirmSelection = React.useCallback(async () => { + await dispatchActionPromise( + updateRelationshipsActionTypes, + callUpdateRelationships({ + action: relationshipAction, + userIDs: Array.from(pendingUserIDs), + }), + ); + closeModal(); + }, [ + callUpdateRelationships, + dispatchActionPromise, + closeModal, + pendingUserIDs, + relationshipAction, + ]); + const loadingStatus = useSelector(loadingStatusSelector); + let buttonContent = confirmButtonContent; + if (loadingStatus === 'loading') { + buttonContent = ( + <> + <div className={css.hidden}>{confirmButtonContent}</div> + <LoadingIndicator status="loading" /> + </> + ); + } + return ( <div className={css.container}> {userTags} <div className={css.userRowsContainer}>{userRows}</div> + <div className={css.buttons}> + <Button variant="secondary" onClick={closeModal}> + Cancel + </Button> + <Button + onClick={confirmSelection} + disabled={pendingUserIDs.size === 0 || loadingStatus === 'loading'} + variant={confirmButtonVariant} + > + <div className={css.confirmButtonContainer}>{buttonContent}</div> + </Button> + </div> </div> ); }