diff --git a/lib/shared/farcaster/farcaster-api.js b/lib/shared/farcaster/farcaster-api.js --- a/lib/shared/farcaster/farcaster-api.js +++ b/lib/shared/farcaster/farcaster-api.js @@ -541,6 +541,33 @@ ); } +export type SendReactionInput = { + +conversationId: string, + +messageId: string, + +reaction: string, + +action: 'remove_reaction' | 'add_reaction', +}; + +function useSendFarcasterReaction(): ( + input: SendReactionInput, +) => Promise { + const { sendFarcasterRequest } = useTunnelbroker(); + return React.useCallback( + async (input: SendReactionInput) => { + const { action, ...payload } = input; + const method = + action === 'remove_reaction' ? { type: 'DELETE' } : { type: 'PUT' }; + await sendFarcasterRequest({ + apiVersion: 'v2', + endpoint: 'direct-cast-message-reaction', + method, + payload: JSON.stringify(payload), + }); + }, + [sendFarcasterRequest], + ); +} + export { useSendFarcasterTextMessage, useFetchFarcasterMessages, @@ -553,4 +580,5 @@ useCreateFarcasterGroup, useGetFarcasterDirectCastUsers, useModifyFarcasterMembershipInput, + useSendFarcasterReaction, }; diff --git a/lib/shared/reaction-utils.js b/lib/shared/reaction-utils.js --- a/lib/shared/reaction-utils.js +++ b/lib/shared/reaction-utils.js @@ -5,6 +5,7 @@ import * as React from 'react'; import { useProcessAndSendDMOperation } from './dm-ops/process-dm-ops.js'; +import { useSendFarcasterReaction } from './farcaster/farcaster-api.js'; import { relationshipBlockedInEitherDirection } from './relationship-utils.js'; import { useThreadHasPermission } from './thread-utils.js'; import { threadSpecs } from './threads/thread-specs.js'; @@ -125,6 +126,7 @@ const callSendReactionMessage = useSendReactionMessage(); const dispatchActionPromise = useDispatchActionPromise(); const processAndSendDMOperation = useProcessAndSendDMOperation(); + const farcasterSendReaction = useSendFarcasterReaction(); return React.useCallback( reaction => { @@ -152,6 +154,7 @@ processAndSendDMOperation, keyserverSendReaction: callSendReactionMessage, dispatchActionPromise, + farcasterSendReaction, }, ); }, @@ -160,10 +163,11 @@ viewerID, reactions, threadInfo, - dispatchActionPromise, + showErrorAlert, processAndSendDMOperation, callSendReactionMessage, - showErrorAlert, + dispatchActionPromise, + farcasterSendReaction, ], ); } 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 @@ -48,7 +48,10 @@ import { updateTypes } from '../../../types/update-types-enum.js'; import { farcasterThreadIDRegExp } from '../../../utils/validation-utils.js'; import { generatePendingThreadColor } from '../../color-utils.js'; -import { type ModifyFarcasterMembershipInput } from '../../farcaster/farcaster-api.js'; +import { + type ModifyFarcasterMembershipInput, + type SendReactionInput, +} from '../../farcaster/farcaster-api.js'; import { conversationIDFromFarcasterThreadID, extractFIDFromUserID, @@ -88,6 +91,8 @@ LeaveThreadUtils, ProtocolSendMultimediaMessageInput, SendMultimediaMessageUtils, + ProtocolSendReactionInput, + SendReactionUtils, } from '../thread-spec.js'; const farcasterThreadProtocol: ThreadProtocol = { @@ -316,8 +321,20 @@ }; }, - sendReaction: async (): Promise => { - throw new Error('sendReaction method is not yet implemented'); + sendReaction: async ( + input: ProtocolSendReactionInput, + utils: SendReactionUtils, + ) => { + const { threadInfo, reaction, action, messageID } = input; + const conversationId = conversationIDFromFarcasterThreadID(threadInfo.id); + const payload: SendReactionInput = { + conversationId, + messageId: messageID, + reaction, + action, + }; + const { farcasterSendReaction } = utils; + await farcasterSendReaction(payload); }, addThreadMembers: 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 @@ -96,6 +96,7 @@ CreateFarcasterGroupResult, CreateFarcasterGroupInput, ModifyFarcasterMembershipInput, + SendReactionInput, } from '../farcaster/farcaster-api.js'; import type { FarcasterConversation } from '../farcaster/farcaster-conversation-types.js'; import type { FetchMessagesFromDBType } from '../message-utils.js'; @@ -222,6 +223,7 @@ export type SendReactionUtils = { +processAndSendDMOperation: OutboundDMOperationSpecification => Promise, +keyserverSendReaction: SendReactionMessageRequest => Promise, + +farcasterSendReaction: SendReactionInput => Promise, +dispatchActionPromise: DispatchActionPromise, }; diff --git a/lib/types/tunnelbroker/farcaster-messages-types.js b/lib/types/tunnelbroker/farcaster-messages-types.js --- a/lib/types/tunnelbroker/farcaster-messages-types.js +++ b/lib/types/tunnelbroker/farcaster-messages-types.js @@ -15,7 +15,8 @@ } | { +type: 'PUT' } | { +type: 'POST' } - | { +type: 'STREAM' }; + | { +type: 'STREAM' } + | { +type: 'DELETE' }; export type FarcasterAPIRequest = { +type: 'FarcasterAPIRequest',