diff --git a/lib/shared/messages/text-message-spec.js b/lib/shared/messages/text-message-spec.js --- a/lib/shared/messages/text-message-spec.js +++ b/lib/shared/messages/text-message-spec.js @@ -28,7 +28,10 @@ import type { RelativeUserInfo } from '../../types/user-types.js'; import { ET } from '../../utils/entity-text.js'; import { useDispatchActionPromise } from '../../utils/redux-promise-utils.js'; +import { useSelector } from '../../utils/redux-utils.js'; import { useAddDMThreadMembers } from '../dm-ops/dm-op-utils.js'; +import { useModifyFarcasterMembershipInput } from '../farcaster/farcaster-api.js'; +import { useRefreshFarcasterConversation } from '../farcaster/farcaster-hooks.js'; import { type ASTNode, type SingleASTNode, @@ -294,6 +297,9 @@ const dispatchActionPromise = useDispatchActionPromise(); const callChangeThreadSettings = useChangeThreadSettings(); const callAddDMThreadMembers = useAddDMThreadMembers(); + const modifyFarcasterMembership = useModifyFarcasterMembershipInput(); + const refreshFarcasterConversation = useRefreshFarcasterConversation(); + const auxUserStore = useSelector(state => state.auxUserStore); return async ( messageInfo: RawTextMessageInfo, threadInfo: ThreadInfo, @@ -322,6 +328,9 @@ dmAddThreadMembers: callAddDMThreadMembers, changeThreadSettings: callChangeThreadSettings, dispatchActionPromise, + modifyFarcasterMembership, + refreshFarcasterConversation, + auxUserStore, }, ); }; diff --git a/lib/shared/threads/protocols/farcaster-thread-protocol.js b/lib/shared/threads/protocols/farcaster-thread-protocol.js --- a/lib/shared/threads/protocols/farcaster-thread-protocol.js +++ b/lib/shared/threads/protocols/farcaster-thread-protocol.js @@ -5,6 +5,7 @@ import { fetchMessagesBeforeCursorActionTypes } from '../../../actions/message-actions.js'; import { changeThreadMemberRolesActionTypes, + changeThreadSettingsActionTypes, leaveThreadActionTypes, removeUsersFromThreadActionTypes, } from '../../../actions/thread-action-types.js'; @@ -83,6 +84,8 @@ UpdateSubscriptionUtils, ProtocolCreatePendingThreadInput, CreateRealThreadParameters, + ProtocolAddThreadMembersInput, + AddThreadMembersUtils, ProtocolChangeThreadMemberRolesInput, ChangeThreadMemberRolesUtils, ProtocolRemoveUsersFromThreadInput, @@ -287,8 +290,45 @@ throw new Error('sendReaction method is not yet implemented'); }, - addThreadMembers: async (): Promise => { - throw new Error('addThreadMembers method is not yet implemented'); + addThreadMembers: async ( + input: ProtocolAddThreadMembersInput, + utils: AddThreadMembersUtils, + ): Promise => { + const { threadInfo, newMemberIDs } = input; + const { + modifyFarcasterMembership, + refreshFarcasterConversation, + dispatchActionPromise, + auxUserStore, + } = utils; + + const conversationId = conversationIDFromFarcasterThreadID(threadInfo.id); + + const promise = (async () => { + const targetFids = newMemberIDs + .map(memberID => { + const targetFid = + auxUserStore.auxUserInfos[memberID]?.fid ?? + extractFIDFromUserID(memberID); + return targetFid ? parseInt(targetFid, 10) : null; + }) + .filter(Boolean); + + if (targetFids.length > 0) { + const modifyFarcasterMembershipInput: ModifyFarcasterMembershipInput = { + conversationId, + action: 'add', + targetFids, + }; + + await modifyFarcasterMembership(modifyFarcasterMembershipInput); + await refreshFarcasterConversation(conversationId); + } + })(); + + void dispatchActionPromise(changeThreadSettingsActionTypes, promise); + + await promise; }, changeThreadMemberRoles: async ( 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 @@ -238,6 +238,11 @@ ) => Promise, +changeThreadSettings: UseChangeThreadSettingsInput => Promise, +dispatchActionPromise: DispatchActionPromise, + +modifyFarcasterMembership: ( + input: ModifyFarcasterMembershipInput, + ) => Promise, + +refreshFarcasterConversation: (conversationId: string) => Promise, + +auxUserStore: AuxUserStore, }; export type ProtocolChangeThreadMemberRolesInput = { diff --git a/native/chat/settings/add-users-modal.react.js b/native/chat/settings/add-users-modal.react.js --- a/native/chat/settings/add-users-modal.react.js +++ b/native/chat/settings/add-users-modal.react.js @@ -10,6 +10,8 @@ import { threadInfoSelector } from 'lib/selectors/thread-selectors.js'; import { userInfoSelectorForPotentialMembers } from 'lib/selectors/user-selectors.js'; import { useAddDMThreadMembers } from 'lib/shared/dm-ops/dm-op-utils.js'; +import { useModifyFarcasterMembershipInput } from 'lib/shared/farcaster/farcaster-api.js'; +import { useRefreshFarcasterConversation } from 'lib/shared/farcaster/farcaster-hooks.js'; import { usePotentialMemberItems } from 'lib/shared/search-utils.js'; import { threadActualMembers } from 'lib/shared/thread-utils.js'; import { threadSpecs } from 'lib/shared/threads/thread-specs.js'; @@ -78,6 +80,9 @@ const dispatchActionPromise = useDispatchActionPromise(); const userInfoInputArrayEmpty = inputLength === 0; const addDMThreadMembers = useAddDMThreadMembers(); + const modifyFarcasterMembership = useModifyFarcasterMembershipInput(); + const refreshFarcasterConversation = useRefreshFarcasterConversation(); + const auxUserStore = useSelector(state => state.auxUserStore); const onPressAdd = React.useCallback(async () => { if (userInfoInputArrayEmpty) { @@ -91,6 +96,9 @@ dmAddThreadMembers: addDMThreadMembers, changeThreadSettings: callChangeThreadSettings, dispatchActionPromise, + modifyFarcasterMembership, + refreshFarcasterConversation, + auxUserStore, }, ); close(); @@ -105,10 +113,13 @@ }, [ userInfoInputArrayEmpty, threadInfo, - dispatchActionPromise, - addDMThreadMembers, userInfoInputIDs, + addDMThreadMembers, callChangeThreadSettings, + dispatchActionPromise, + modifyFarcasterMembership, + refreshFarcasterConversation, + auxUserStore, close, onUnknownErrorAlertAcknowledged, ]); diff --git a/web/modals/threads/members/add-members-modal.react.js b/web/modals/threads/members/add-members-modal.react.js --- a/web/modals/threads/members/add-members-modal.react.js +++ b/web/modals/threads/members/add-members-modal.react.js @@ -5,6 +5,8 @@ import { useChangeThreadSettings } from 'lib/hooks/thread-hooks.js'; import { threadInfoSelector } from 'lib/selectors/thread-selectors.js'; import { useAddDMThreadMembers } from 'lib/shared/dm-ops/dm-op-utils.js'; +import { useModifyFarcasterMembershipInput } from 'lib/shared/farcaster/farcaster-api.js'; +import { useRefreshFarcasterConversation } from 'lib/shared/farcaster/farcaster-hooks.js'; import { threadSpecs } from 'lib/shared/threads/thread-specs.js'; import { useDispatchActionPromise } from 'lib/utils/redux-promise-utils.js'; import { useSelector } from 'lib/utils/redux-utils.js'; @@ -62,6 +64,9 @@ const callChangeThreadSettings = useChangeThreadSettings(); const addDMThreadMembers = useAddDMThreadMembers(); const threadInfo = useSelector(state => threadInfoSelector(state)[threadID]); + const modifyFarcasterMembership = useModifyFarcasterMembershipInput(); + const refreshFarcasterConversation = useRefreshFarcasterConversation(); + const auxUserStore = useSelector(state => state.auxUserStore); const addUsers = React.useCallback(() => { const newMemberIDs = Array.from(pendingUsersToAdd.keys()); @@ -72,15 +77,21 @@ dmAddThreadMembers: addDMThreadMembers, changeThreadSettings: callChangeThreadSettings, dispatchActionPromise, + modifyFarcasterMembership, + refreshFarcasterConversation, + auxUserStore, }, ); onClose(); }, [ addDMThreadMembers, + auxUserStore, callChangeThreadSettings, dispatchActionPromise, + modifyFarcasterMembership, onClose, pendingUsersToAdd, + refreshFarcasterConversation, threadInfo, ]);