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 @@ -822,6 +822,42 @@ ); } +export type DeleteFarcasterMessageInput = { + +conversationId: string, + +messageId: string, +}; + +function useDeleteFarcasterMessage(): ( + input: DeleteFarcasterMessageInput, +) => Promise { + const { sendFarcasterRequest } = useTunnelbroker(); + const { addLog } = useDebugLogs(); + return React.useCallback( + async (input: DeleteFarcasterMessageInput) => { + try { + await sendFarcasterRequest({ + apiVersion: 'v2', + endpoint: 'delete-message', + method: { type: 'DELETE' }, + payload: JSON.stringify(input), + }); + } catch (error) { + addLog( + 'Farcaster API: Failed to delete message', + JSON.stringify({ + conversationId: input.conversationId, + messageId: input.messageId, + error: getMessageForException(error), + }), + new Set([logTypes.FARCASTER, logTypes.ERROR]), + ); + throw error; + } + }, + [addLog, sendFarcasterRequest], + ); +} + export type AcceptInviteInput = { +conversationId: string, }; @@ -902,6 +938,7 @@ useGetFarcasterDirectCastUsers, useModifyFarcasterMembershipInput, useSendFarcasterReaction, + useDeleteFarcasterMessage, useAcceptInvite, useFetchFarcasterConversationInvites, useAcceptOneOnOneInvite, 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 @@ -108,6 +108,8 @@ SendReactionUtils, JoinThreadUtils, ProtocolJoinThreadInput, + ProtocolDeleteMessageInput, + DeleteMessageUtils, } from '../thread-spec.js'; import { threadTypeIsPersonal } from '../thread-specs.js'; @@ -907,8 +909,34 @@ return getFarcasterRolePermissionsBlobs(farcasterThreadType); }, - deleteMessage: async (): Promise => { - throw new Error('deleteMessage method is not yet implemented'); + deleteMessage: async ( + input: ProtocolDeleteMessageInput, + utils: DeleteMessageUtils, + ): Promise => { + const { messageID, threadInfo, viewerID } = input; + const { farcasterDeleteMessage, dispatch } = utils; + + const conversationId = conversationIDFromFarcasterThreadID(threadInfo.id); + const time = Date.now(); + + const deleteMessage = { + type: messageTypes.DELETE_MESSAGE, + id: `${messageID}/delete`, + threadID: threadInfo.id, + creatorID: viewerID ?? '', + time, + targetMessageID: messageID, + }; + + await farcasterDeleteMessage({ + conversationId, + messageId: messageID, + }); + + dispatch({ + type: processFarcasterOpsActionType, + payload: { rawMessageInfos: [deleteMessage], updateInfos: [] }, + }); }, joinThread: async ( @@ -1004,7 +1032,6 @@ supportsThreadRefreshing: true, temporarilyDisabledFeatures: { changingThreadAvatar: true, - deletingMessages: true, pinningMessages: true, }, supportsMessageEdit: false, 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 @@ -102,6 +102,7 @@ SendReactionInput, AcceptInviteInput, AcceptOneOnOneInviteInput, + DeleteFarcasterMessageInput, } from '../farcaster/farcaster-api.js'; import type { FarcasterConversation } from '../farcaster/farcaster-conversation-types.js'; import type { FarcasterMessageFetchingContextType } from '../farcaster/farcaster-message-fetching-context.js'; @@ -368,6 +369,8 @@ +processAndSendDMOperation: OutboundDMOperationSpecification => Promise, +keyserverDeleteMessage: DeleteMessageRequest => Promise, +dispatchActionPromise: DispatchActionPromise, + +farcasterDeleteMessage: DeleteFarcasterMessageInput => Promise, + +dispatch: Dispatch, }; export type ProtocolJoinThreadInput = { @@ -552,7 +555,6 @@ +supportsThreadRefreshing: boolean, +temporarilyDisabledFeatures?: { +changingThreadAvatar?: boolean, - +deletingMessages?: boolean, +pinningMessages?: boolean, }, +supportsMessageEdit: boolean, diff --git a/lib/utils/convert-farcaster-message-to-comm-messages.js b/lib/utils/convert-farcaster-message-to-comm-messages.js --- a/lib/utils/convert-farcaster-message-to-comm-messages.js +++ b/lib/utils/convert-farcaster-message-to-comm-messages.js @@ -34,6 +34,18 @@ ); const result: Array = []; + if (farcasterMessage.isDeleted) { + result.push({ + type: messageTypes.DELETE_MESSAGE, + id: `${farcasterMessage.messageId}/delete`, + threadID, + creatorID, + time: parseInt(farcasterMessage.serverTimestamp, 10) + 2, + targetMessageID: farcasterMessage.messageId, + }); + return result; + } + if (farcasterMessage.reactions?.length > 0) { const viewerReactions = new Set( farcasterMessage.viewerContext?.reactions ?? [], diff --git a/lib/utils/delete-message-utils.js b/lib/utils/delete-message-utils.js --- a/lib/utils/delete-message-utils.js +++ b/lib/utils/delete-message-utils.js @@ -4,9 +4,10 @@ import * as React from 'react'; import { useDispatchActionPromise } from './redux-promise-utils.js'; -import { useSelector } from './redux-utils.js'; +import { useDispatch, useSelector } from './redux-utils.js'; import { useSendDeleteMessage } from '../hooks/message-hooks.js'; import { useProcessAndSendDMOperation } from '../shared/dm-ops/process-dm-ops.js'; +import { useDeleteFarcasterMessage } from '../shared/farcaster/farcaster-api.js'; import { useThreadHasPermission } from '../shared/thread-utils.js'; import { threadSpecs } from '../shared/threads/thread-specs.js'; import type { MessageInfo } from '../types/message-types.js'; @@ -20,6 +21,8 @@ const callDeleteMessage = useSendDeleteMessage(); const dispatchActionPromise = useDispatchActionPromise(); const processAndSendDMOperation = useProcessAndSendDMOperation(); + const farcasterDeleteMessage = useDeleteFarcasterMessage(); + const dispatch = useDispatch(); const viewerID = useSelector( state => state.currentUserInfo && state.currentUserInfo.id, ); @@ -36,6 +39,8 @@ keyserverDeleteMessage: callDeleteMessage, dispatchActionPromise, processAndSendDMOperation, + farcasterDeleteMessage, + dispatch, }, ); }, @@ -43,6 +48,8 @@ callDeleteMessage, dispatchActionPromise, processAndSendDMOperation, + farcasterDeleteMessage, + dispatch, threadInfos, viewerID, ], @@ -64,13 +71,6 @@ threadPermissions.DELETE_ALL_MESSAGES, ); - if ( - threadSpecs[threadInfo.type].protocol().temporarilyDisabledFeatures - ?.deletingMessages - ) { - return false; - } - if ( !targetMessageInfo || !targetMessageInfo.id ||