diff --git a/lib/hooks/thread-hooks.js b/lib/hooks/thread-hooks.js --- a/lib/hooks/thread-hooks.js +++ b/lib/hooks/thread-hooks.js @@ -6,6 +6,7 @@ import { useChatMentionContext } from './chat-mention-hooks.js'; import { useGetCommFCUsersForFIDs } from './user-identities-hooks.js'; +import { removeUsersFromThreadActionTypes } from '../actions/thread-action-types.js'; import genesis from '../facts/genesis.js'; import { extractKeyserverIDFromID } from '../keyserver-conn/keyserver-call-utils.js'; import { useKeyserverCall } from '../keyserver-conn/keyserver-call.js'; @@ -272,7 +273,7 @@ } export type RemoveUsersFromThreadInput = { - +threadID: string, + +threadInfo: ThreadInfo, +memberIDs: $ReadOnlyArray, }; const removeMembersFromThreadEndpointOptions = { @@ -285,8 +286,15 @@ input: RemoveUsersFromThreadInput, ) => Promise) => async input => { - const keyserverID = extractKeyserverIDFromID(input.threadID); - const requests = { [keyserverID]: input }; + const { threadInfo, memberIDs } = input; + const threadID = threadInfo.id; + const keyserverID = extractKeyserverIDFromID(threadID); + const requests = { + [keyserverID]: { + threadID, + memberIDs, + }, + }; const responses = await callKeyserverEndpoint( 'remove_members', @@ -295,7 +303,7 @@ ); const response = responses[keyserverID]; return { - threadID: input.threadID, + threadID, updatesResult: response.updatesResult, newMessageInfos: response.newMessageInfos, }; @@ -304,7 +312,37 @@ function useRemoveUsersFromThread(): ( input: RemoveUsersFromThreadInput, ) => Promise { - return useKeyserverCall(removeUsersFromThread); + const keyserverCall = useKeyserverCall(removeUsersFromThread); + const dispatchActionPromise = useDispatchActionPromise(); + return React.useCallback( + async (input: RemoveUsersFromThreadInput) => { + const { threadInfo, memberIDs } = input; + const protocol = threadSpecs[threadInfo.type].protocol(); + invariant( + protocol.removeUsersFromThread, + `removeUsersFromThread not supported for thread type ` + + `${threadInfo.type}`, + ); + + const customKeyName = + memberIDs.length > 0 + ? `${removeUsersFromThreadActionTypes.started}:${memberIDs[0]}` + : undefined; + + return protocol.removeUsersFromThread( + { + threadInfo, + memberIDs, + customKeyName, + }, + { + keyserverCall, + dispatchActionPromise, + }, + ); + }, + [dispatchActionPromise, keyserverCall], + ); } export type ChangeThreadMemberRolesInput = { diff --git a/lib/shared/thread-actions-utils.js b/lib/shared/thread-actions-utils.js deleted file mode 100644 --- a/lib/shared/thread-actions-utils.js +++ /dev/null @@ -1,31 +0,0 @@ -// @flow - -import { removeUsersFromThreadActionTypes } from '../actions/thread-action-types.js'; -import type { RemoveUsersFromThreadInput } from '../hooks/thread-hooks.js'; -import type { - RelativeMemberInfo, - ThreadInfo, -} from '../types/minimally-encoded-thread-permissions-types.js'; -import type { ChangeThreadSettingsPayload } from '../types/thread-types.js'; -import type { DispatchActionPromise } from '../utils/redux-promise-utils.js'; - -function removeMemberFromThread( - threadInfo: ThreadInfo, - memberInfo: RelativeMemberInfo, - dispatchActionPromise: DispatchActionPromise, - removeUserFromThreadServerCall: ( - input: RemoveUsersFromThreadInput, - ) => Promise, -) { - const customKeyName = `${removeUsersFromThreadActionTypes.started}:${memberInfo.id}`; - void dispatchActionPromise( - removeUsersFromThreadActionTypes, - removeUserFromThreadServerCall({ - threadID: threadInfo.id, - memberIDs: [memberInfo.id], - }), - { customKeyName }, - ); -} - -export { removeMemberFromThread }; diff --git a/lib/shared/threads/protocols/keyserver-thread-protocol.js b/lib/shared/threads/protocols/keyserver-thread-protocol.js --- a/lib/shared/threads/protocols/keyserver-thread-protocol.js +++ b/lib/shared/threads/protocols/keyserver-thread-protocol.js @@ -16,6 +16,7 @@ changeThreadSettingsActionTypes, leaveThreadActionTypes, newThreadActionTypes, + removeUsersFromThreadActionTypes, } from '../../../actions/thread-action-types.js'; import { type MediaMetadataReassignmentAction, @@ -119,6 +120,8 @@ ProtocolJoinThreadInput, ProtocolChangeThreadMemberRolesInput, ChangeThreadMemberRolesUtils, + ProtocolRemoveUsersFromThreadInput, + RemoveUsersFromThreadUtils, } from '../thread-spec.js'; import { threadTypeIsSidebar } from '../thread-specs.js'; @@ -396,6 +399,26 @@ return await promise; }, + removeUsersFromThread: async ( + input: ProtocolRemoveUsersFromThreadInput, + utils: RemoveUsersFromThreadUtils, + ) => { + const { threadInfo, memberIDs, customKeyName } = input; + + const promise = utils.keyserverCall({ + threadInfo, + memberIDs, + }); + + void utils.dispatchActionPromise( + removeUsersFromThreadActionTypes, + promise, + customKeyName ? { customKeyName } : undefined, + ); + + return await promise; + }, + updateSubscription: ( protocolInput: ProtocolUpdateSubscriptionInput, utils: UpdateSubscriptionUtils, diff --git a/lib/shared/threads/thread-spec.js b/lib/shared/threads/thread-spec.js --- a/lib/shared/threads/thread-spec.js +++ b/lib/shared/threads/thread-spec.js @@ -22,6 +22,7 @@ UseChangeThreadSettingsInput, LeaveThreadInput, LeaveThreadResult, + RemoveUsersFromThreadInput, } from '../../hooks/thread-hooks.js'; import type { GetCommFCUsersForFIDs } from '../../hooks/user-identities-hooks.js'; import type { RolePermissionBlobs } from '../../permissions/thread-permissions.js'; @@ -254,6 +255,16 @@ +auxUserStore: AuxUserStore, }; +export type ProtocolRemoveUsersFromThreadInput = { + +threadInfo: ThreadInfo, + +memberIDs: $ReadOnlyArray, + +customKeyName?: string, +}; +export type RemoveUsersFromThreadUtils = { + +keyserverCall: RemoveUsersFromThreadInput => Promise, + +dispatchActionPromise: DispatchActionPromise, +}; + export type ProtocolUpdateSubscriptionInput = { +input: UseUpdateSubscriptionInput, +viewerID: ?string, @@ -394,6 +405,10 @@ input: ProtocolChangeThreadMemberRolesInput, utils: ChangeThreadMemberRolesUtils, ) => Promise, + +removeUsersFromThread?: ( + input: ProtocolRemoveUsersFromThreadInput, + utils: RemoveUsersFromThreadUtils, + ) => Promise, +updateSubscription: ( input: ProtocolUpdateSubscriptionInput, utils: UpdateSubscriptionUtils, diff --git a/native/chat/settings/thread-settings-member-tooltip-modal.react.js b/native/chat/settings/thread-settings-member-tooltip-modal.react.js --- a/native/chat/settings/thread-settings-member-tooltip-modal.react.js +++ b/native/chat/settings/thread-settings-member-tooltip-modal.react.js @@ -3,13 +3,11 @@ import * as React from 'react'; import { useRemoveUsersFromThread } from 'lib/hooks/thread-hooks.js'; -import { removeMemberFromThread } from 'lib/shared/thread-actions-utils.js'; import { stringForUser } from 'lib/shared/user-utils.js'; import type { RelativeMemberInfo, ThreadInfo, } from 'lib/types/minimally-encoded-thread-permissions-types.js'; -import { useDispatchActionPromise } from 'lib/utils/redux-promise-utils.js'; import ThreadSettingsMemberTooltipButton from './thread-settings-member-tooltip-button.react.js'; import type { AppNavigationProp } from '../../navigation/app-navigator.react'; @@ -32,18 +30,15 @@ route: TooltipRoute<'ThreadSettingsMemberTooltipModal'>, ) { const { memberInfo, threadInfo } = route.params; - const boundRemoveUsersFromThread = useRemoveUsersFromThread(); - const dispatchActionPromise = useDispatchActionPromise(); + const removeUsersFromThread = useRemoveUsersFromThread(); const onConfirmRemoveUser = React.useCallback( () => - removeMemberFromThread( + void removeUsersFromThread({ threadInfo, - memberInfo, - dispatchActionPromise, - boundRemoveUsersFromThread, - ), - [threadInfo, memberInfo, dispatchActionPromise, boundRemoveUsersFromThread], + memberIDs: [memberInfo.id], + }), + [threadInfo, memberInfo.id, removeUsersFromThread], ); const userText = stringForUser(memberInfo); diff --git a/web/modals/threads/members/member.react.js b/web/modals/threads/members/member.react.js --- a/web/modals/threads/members/member.react.js +++ b/web/modals/threads/members/member.react.js @@ -5,7 +5,6 @@ import { useModalContext } from 'lib/components/modal-provider.react.js'; import SWMansionIcon from 'lib/components/swmansion-icon.react.js'; import { useRemoveUsersFromThread } from 'lib/hooks/thread-hooks.js'; -import { removeMemberFromThread } from 'lib/shared/thread-actions-utils.js'; import { useAvailableThreadMemberActions } from 'lib/shared/thread-utils.js'; import { stringForUser } from 'lib/shared/user-utils.js'; import type { SetState } from 'lib/types/hook-types.js'; @@ -13,7 +12,6 @@ RelativeMemberInfo, ThreadInfo, } from 'lib/types/minimally-encoded-thread-permissions-types.js'; -import { useDispatchActionPromise } from 'lib/utils/redux-promise-utils.js'; import { useRolesFromCommunityThreadInfo } from 'lib/utils/role-utils.js'; import ChangeMemberRoleModal from './change-member-role-modal.react.js'; @@ -53,18 +51,15 @@ [memberInfo.id, setOpenMenu], ); - const dispatchActionPromise = useDispatchActionPromise(); - const boundRemoveUsersFromThread = useRemoveUsersFromThread(); + const removeUsersFromThread = useRemoveUsersFromThread(); const onClickRemoveUser = React.useCallback( () => - removeMemberFromThread( + void removeUsersFromThread({ threadInfo, - memberInfo, - dispatchActionPromise, - boundRemoveUsersFromThread, - ), - [boundRemoveUsersFromThread, dispatchActionPromise, memberInfo, threadInfo], + memberIDs: [memberInfo.id], + }), + [removeUsersFromThread, memberInfo.id, threadInfo], ); const onClickChangeRole = React.useCallback(() => {