diff --git a/lib/hooks/relationship-prompt.js b/lib/hooks/relationship-prompt.js index 30cf569ae..7b87f345b 100644 --- a/lib/hooks/relationship-prompt.js +++ b/lib/hooks/relationship-prompt.js @@ -1,127 +1,171 @@ // @flow import invariant from 'invariant'; import * as React from 'react'; import { updateRelationships as serverUpdateRelationships, updateRelationshipsActionTypes, } from '../actions/relationship-actions.js'; import { useLegacyAshoatKeyserverCall } from '../keyserver-conn/legacy-keyserver-call.js'; import { getSingleOtherUser } from '../shared/thread-utils.js'; import type { ThreadInfo } from '../types/minimally-encoded-thread-permissions-types.js'; import { relationshipActions, type TraditionalRelationshipAction, } from '../types/relationship-types.js'; import type { UserInfo } from '../types/user-types.js'; import { useDispatchActionPromise } from '../utils/redux-promise-utils.js'; import { useSelector } from '../utils/redux-utils.js'; type RelationshipCallbacks = { +blockUser: () => void, +unblockUser: () => void, +friendUser: () => void, +unfriendUser: () => void, }; +type RelationshipLoadingState = { + +isLoadingBlockUser: boolean, + +isLoadingUnblockUser: boolean, + +isLoadingFriendUser: boolean, + +isLoadingUnfriendUser: boolean, +}; + type RelationshipPromptData = { +otherUserInfo: ?UserInfo, +callbacks: RelationshipCallbacks, + +loadingState: RelationshipLoadingState, }; function useRelationshipPrompt( threadInfo: ThreadInfo, onErrorCallback?: () => void, pendingPersonalThreadUserInfo?: ?UserInfo, ): RelationshipPromptData { // We're fetching the info from state because we need the most recent // relationship status. Additionally, member info does not contain info // about relationship. const otherUserInfo = useSelector(state => { const otherUserID = getSingleOtherUser(threadInfo, state.currentUserInfo?.id) ?? pendingPersonalThreadUserInfo?.id; const { userInfos } = state.userStore; return otherUserID && userInfos[otherUserID] ? userInfos[otherUserID] : pendingPersonalThreadUserInfo; }); - const callbacks = useRelationshipCallbacks( + const { callbacks, loadingState } = useRelationshipCallbacks( otherUserInfo?.id, onErrorCallback, ); return React.useMemo( () => ({ otherUserInfo, callbacks, + loadingState, }), - [callbacks, otherUserInfo], + [callbacks, loadingState, otherUserInfo], ); } function useRelationshipCallbacks( otherUserID?: string, onErrorCallback?: () => void, -): RelationshipCallbacks { +): { + +callbacks: RelationshipCallbacks, + +loadingState: RelationshipLoadingState, +} { + const [isLoadingBlockUser, setIsLoadingBlockUser] = React.useState(false); + const [isLoadingUnblockUser, setIsLoadingUnblockUser] = React.useState(false); + const [isLoadingFriendUser, setIsLoadingFriendUser] = React.useState(false); + const [isLoadingUnfriendUser, setIsLoadingUnfriendUser] = + React.useState(false); + const callUpdateRelationships = useLegacyAshoatKeyserverCall( serverUpdateRelationships, ); const updateRelationship = React.useCallback( - async (action: TraditionalRelationshipAction) => { + async ( + action: TraditionalRelationshipAction, + setInProgress: boolean => mixed, + ) => { try { + setInProgress(true); invariant(otherUserID, 'Other user info id should be present'); return await callUpdateRelationships({ action, userIDs: [otherUserID], }); } catch (e) { onErrorCallback?.(); throw e; + } finally { + setInProgress(false); } }, [callUpdateRelationships, onErrorCallback, otherUserID], ); const dispatchActionPromise = useDispatchActionPromise(); const onButtonPress = React.useCallback( - (action: TraditionalRelationshipAction) => { + ( + action: TraditionalRelationshipAction, + setInProgress: boolean => mixed, + ) => { void dispatchActionPromise( updateRelationshipsActionTypes, - updateRelationship(action), + updateRelationship(action, setInProgress), ); }, [dispatchActionPromise, updateRelationship], ); const blockUser = React.useCallback( - () => onButtonPress(relationshipActions.BLOCK), + () => onButtonPress(relationshipActions.BLOCK, setIsLoadingBlockUser), [onButtonPress], ); const unblockUser = React.useCallback( - () => onButtonPress(relationshipActions.UNBLOCK), + () => onButtonPress(relationshipActions.UNBLOCK, setIsLoadingUnblockUser), [onButtonPress], ); const friendUser = React.useCallback( - () => onButtonPress(relationshipActions.FRIEND), + () => onButtonPress(relationshipActions.FRIEND, setIsLoadingFriendUser), [onButtonPress], ); const unfriendUser = React.useCallback( - () => onButtonPress(relationshipActions.UNFRIEND), + () => onButtonPress(relationshipActions.UNFRIEND, setIsLoadingUnfriendUser), [onButtonPress], ); return React.useMemo( () => ({ + callbacks: { + blockUser, + unblockUser, + friendUser, + unfriendUser, + }, + loadingState: { + isLoadingBlockUser, + isLoadingUnblockUser, + isLoadingFriendUser, + isLoadingUnfriendUser, + }, + }), + [ blockUser, - unblockUser, friendUser, + isLoadingBlockUser, + isLoadingFriendUser, + isLoadingUnblockUser, + isLoadingUnfriendUser, + unblockUser, unfriendUser, - }), - [blockUser, friendUser, unblockUser, unfriendUser], + ], ); } export { useRelationshipPrompt, useRelationshipCallbacks }; diff --git a/web/settings/relationship/block-list-row.react.js b/web/settings/relationship/block-list-row.react.js index c88314a40..94c90a1f4 100644 --- a/web/settings/relationship/block-list-row.react.js +++ b/web/settings/relationship/block-list-row.react.js @@ -1,43 +1,45 @@ // @flow import * as React from 'react'; import SWMansionIcon from 'lib/components/swmansion-icon.react.js'; import { useRelationshipCallbacks } from 'lib/hooks/relationship-prompt.js'; import css from './user-list-row.css'; import type { UserRowProps } from './user-list.react.js'; import UserAvatar from '../../avatars/user-avatar.react.js'; import MenuItem from '../../components/menu-item.react.js'; import Menu from '../../components/menu.react.js'; import { usePushUserProfileModal } from '../../modals/user-profile/user-profile-utils.js'; function BlockListRow(props: UserRowProps): React.Node { const { userInfo, onMenuVisibilityChange } = props; - const { unblockUser } = useRelationshipCallbacks(userInfo.id); + const { + callbacks: { unblockUser }, + } = useRelationshipCallbacks(userInfo.id); const editIcon = ; const pushUserProfileModal = usePushUserProfileModal(userInfo.id); return (
{userInfo.username}
); } export default BlockListRow; diff --git a/web/settings/relationship/friend-list-row.react.js b/web/settings/relationship/friend-list-row.react.js index 1d2cf97ab..dfa7f0c6a 100644 --- a/web/settings/relationship/friend-list-row.react.js +++ b/web/settings/relationship/friend-list-row.react.js @@ -1,118 +1,120 @@ // @flow import * as React from 'react'; import SWMansionIcon from 'lib/components/swmansion-icon.react.js'; import { useRelationshipCallbacks } from 'lib/hooks/relationship-prompt.js'; import { userRelationshipStatus } from 'lib/types/relationship-types.js'; import css from './user-list-row.css'; import type { UserRowProps } from './user-list.react.js'; import UserAvatar from '../../avatars/user-avatar.react.js'; import Button from '../../components/button.react.js'; import MenuItem from '../../components/menu-item.react.js'; import Menu from '../../components/menu.react.js'; import { usePushUserProfileModal } from '../../modals/user-profile/user-profile-utils.js'; const dangerButtonColor = { color: 'var(--btn-bg-danger)', }; function FriendListRow(props: UserRowProps): React.Node { const { userInfo, onMenuVisibilityChange } = props; - const { friendUser, unfriendUser } = useRelationshipCallbacks(userInfo.id); + const { + callbacks: { friendUser, unfriendUser }, + } = useRelationshipCallbacks(userInfo.id); const friendUserCallback = React.useCallback( (event: SyntheticEvent) => { event.stopPropagation(); friendUser(); }, [friendUser], ); const unfriendUserCallback = React.useCallback( (event: SyntheticEvent) => { event.stopPropagation(); unfriendUser(); }, [unfriendUser], ); const buttons = React.useMemo(() => { if (userInfo.relationshipStatus === userRelationshipStatus.REQUEST_SENT) { return ( ); } if ( userInfo.relationshipStatus === userRelationshipStatus.REQUEST_RECEIVED ) { return ( <> ); } if (userInfo.relationshipStatus === userRelationshipStatus.FRIEND) { const editIcon = ; return (
); } return undefined; }, [ userInfo.relationshipStatus, unfriendUserCallback, friendUserCallback, onMenuVisibilityChange, unfriendUser, ]); const pushUserProfileModal = usePushUserProfileModal(userInfo.id); return (
{userInfo.username}
{buttons}
); } export default FriendListRow;