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 @@ -5,6 +5,7 @@ import uuid from 'uuid'; import { useChatMentionContext } from './chat-mention-hooks.js'; +import { useGetCommFCUsersForFIDs } from './user-identities-hooks.js'; import genesis from '../facts/genesis.js'; import { extractKeyserverIDFromID } from '../keyserver-conn/keyserver-call-utils.js'; import { useKeyserverCall } from '../keyserver-conn/keyserver-call.js'; @@ -51,7 +52,7 @@ } from '../types/thread-types.js'; import { values } from '../utils/objects.js'; import { useDispatchActionPromise } from '../utils/redux-promise-utils.js'; -import { useSelector } from '../utils/redux-utils.js'; +import { useSelector, useDispatch } from '../utils/redux-utils.js'; function useChildThreadInfosMap(): { +[id: string]: $ReadOnlyArray, @@ -238,6 +239,8 @@ useUpdateFarcasterGroupNameAndDescription(); const fetchFarcasterConversation = useFetchFarcasterConversation(); const fetchFarcasterMessages = useFetchFarcasterMessages(); + const fetchUsersByFIDs = useGetCommFCUsersForFIDs(); + const dispatch = useDispatch(); return React.useCallback( async (input: UseChangeThreadSettingsInput) => @@ -249,6 +252,8 @@ updateFarcasterGroupNameAndDescription, fetchFarcasterConversation, fetchFarcasterMessages, + fetchUsersByFIDs, + dispatch, }, ), [ @@ -258,6 +263,8 @@ updateFarcasterGroupNameAndDescription, viewerID, fetchFarcasterMessages, + fetchUsersByFIDs, + dispatch, ], ); } diff --git a/lib/shared/farcaster/farcaster-hooks.js b/lib/shared/farcaster/farcaster-hooks.js --- a/lib/shared/farcaster/farcaster-hooks.js +++ b/lib/shared/farcaster/farcaster-hooks.js @@ -11,14 +11,21 @@ useSendFarcasterTextMessage, } from './farcaster-api.js'; import type { FarcasterConversation } from './farcaster-conversation-types.js'; -import type { FarcasterMessage } from './farcaster-messages-types'; +import { + farcasterMessageValidator, + type FarcasterMessage, +} from './farcaster-messages-types.js'; +import { processNewUserIDsActionType } from '../../actions/user-actions.js'; +import { useGetCommFCUsersForFIDs } from '../../hooks/user-identities-hooks.js'; import { messageTruncationStatus } from '../../types/message-types.js'; import { updateTypes } from '../../types/update-types-enum.js'; +import { extractFarcasterIDsFromPayload } from '../../utils/conversion-utils.js'; import { convertFarcasterMessageToCommMessages } from '../../utils/convert-farcaster-message-to-comm-messages.js'; import { createFarcasterRawThreadInfo } from '../../utils/create-farcaster-raw-thread-info.js'; import { useSetFarcasterDCsLoaded } from '../../utils/farcaster-utils.js'; import { useDispatch } from '../../utils/redux-utils.js'; import { useSendDMOperationUtils } from '../dm-ops/dm-op-utils.js'; +import { userIDFromFID } from '../id-utils.js'; async function processInBatches( items: $ReadOnlyArray, @@ -86,6 +93,7 @@ messagesNumberLimit?: number, ) => Promise { const fetchFarcasterMessages = useFetchFarcasterMessages(); + const fetchUsersByFIDs = useGetCommFCUsersForFIDs(); const dispatch = useDispatch(); return React.useCallback( @@ -117,10 +125,29 @@ if (messagesResult) { const farcasterMessages = messagesResult.result.messages; - const rawMessageInfos = farcasterMessages.flatMap( - convertFarcasterMessageToCommMessages, + + const userFIDs = farcasterMessages.flatMap(message => + extractFarcasterIDsFromPayload( + farcasterMessageValidator, + message, + ), + ); + const fcUserInfos = await fetchUsersByFIDs(userFIDs); + + const rawMessageInfos = farcasterMessages.flatMap(message => + convertFarcasterMessageToCommMessages(message, fcUserInfos), ); + if (fcUserInfos.size > 0) { + const newUserIDs = Array.from(fcUserInfos.entries()).map( + ([fid, user]) => user?.userID ?? userIDFromFID(fid), + ); + dispatch({ + type: processNewUserIDsActionType, + payload: { userIDs: newUserIDs }, + }); + } + if (rawMessageInfos.length > 0) { const payload = totalMessagesFetched === 0 @@ -146,7 +173,7 @@ console.error(`Failed fetching messages for ${conversationID}:`, e); } }, - [fetchFarcasterMessages, dispatch], + [fetchFarcasterMessages, fetchUsersByFIDs, dispatch], ); } diff --git a/lib/shared/id-utils.js b/lib/shared/id-utils.js --- a/lib/shared/id-utils.js +++ b/lib/shared/id-utils.js @@ -148,6 +148,20 @@ return threadID.replace(farcasterIDPrefix, ''); } +const farcasterUserIDPrefix = 'FID#'; + +function userIDFromFID(farcasterID: string): string { + return `${farcasterUserIDPrefix}${farcasterID}`; +} + +function extractFIDFromUserID(userID: string): ?string { + if (!userID.includes(farcasterUserIDPrefix)) { + return null; + } + + return userID.replace(farcasterUserIDPrefix, ''); +} + export { getNextLocalID, getOldestNonLocalMessageID, @@ -162,4 +176,6 @@ farcasterIDPrefix, farcasterThreadIDFromConversationID, conversationIDFromFarcasterThreadID, + userIDFromFID, + extractFIDFromUserID, }; 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 @@ -37,10 +37,15 @@ } from '../../../types/thread-types.js'; import { updateTypes } from '../../../types/update-types-enum.js'; import { type ThreadUpdateInfo } from '../../../types/update-types.js'; +import { extractFarcasterIDsFromPayload } from '../../../utils/conversion-utils.js'; import { convertFarcasterMessageToCommMessages } from '../../../utils/convert-farcaster-message-to-comm-messages.js'; import { createFarcasterRawThreadInfo } from '../../../utils/create-farcaster-raw-thread-info.js'; import { farcasterThreadIDRegExp } from '../../../utils/validation-utils.js'; -import { conversationIDFromFarcasterThreadID } from '../../id-utils.js'; +import { farcasterMessageValidator } from '../../farcaster/farcaster-messages-types.js'; +import { + conversationIDFromFarcasterThreadID, + userIDFromFID, +} from '../../id-utils.js'; import { messageNotifyTypes } from '../../messages/message-spec.js'; import { getSingleOtherUser } from '../../thread-utils.js'; import type { @@ -124,6 +129,8 @@ updateFarcasterGroupNameAndDescription, fetchFarcasterConversation, fetchFarcasterMessages, + fetchUsersByFIDs, + dispatch, } = utils; invariant( !changes.color, @@ -178,11 +185,29 @@ const messagesResponse = await fetchFarcasterMessages({ conversationId, }); - const newMessageInfos = messagesResponse.result.messages.flatMap( - farcasterMessage => - convertFarcasterMessageToCommMessages(farcasterMessage), + const farcasterMessages = messagesResponse.result.messages; + const userFIDs = farcasterMessages.flatMap(message => + extractFarcasterIDsFromPayload(farcasterMessageValidator, message), + ); + + const fcUserInfos = await fetchUsersByFIDs(userFIDs); + + const newMessageInfos = farcasterMessages.flatMap(farcasterMessage => + convertFarcasterMessageToCommMessages(farcasterMessage, fcUserInfos), ); + if (fcUserInfos.size > 0) { + const newUserIDs = Array.from(fcUserInfos.entries()).map( + ([fid, user]) => user?.userID ?? userIDFromFID(fid), + ); + dispatch({ + // TODO: importing processNewUserIDsActionType causes circular + // dependencies that are hard to resolve + type: 'PROCESS_NEW_USER_IDS', + payload: { userIDs: newUserIDs }, + }); + } + return ({ threadID: threadInfo.id, updatesResult: { newUpdates: [update] }, 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 @@ -23,6 +23,7 @@ LeaveThreadInput, LeaveThreadResult, } from '../../hooks/thread-hooks.js'; +import type { GetCommFCUsersForFIDs } from '../../hooks/user-identities-hooks.js'; import type { RolePermissionBlobs } from '../../permissions/thread-permissions.js'; import type { ProcessOutboundP2PMessagesResult } from '../../tunnelbroker/peer-to-peer-context.js'; import type { TunnelbrokerSocketState } from '../../tunnelbroker/tunnelbroker-context.js'; @@ -162,6 +163,8 @@ +updateFarcasterGroupNameAndDescription: UpdateFarcasterGroupNameAndDescriptionInput => Promise, +fetchFarcasterConversation: FetchFarcasterConversationInput => Promise, +fetchFarcasterMessages: FetchFarcasterMessageInput => Promise, + +fetchUsersByFIDs: GetCommFCUsersForFIDs, + +dispatch: Dispatch, }; export type ProtocolCreateEntryInput = { 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 @@ -1,16 +1,24 @@ // @flow import uuid from 'uuid'; +import type { FCUserInfos } from '../hooks/user-identities-hooks.js'; import type { FarcasterMessage } from '../shared/farcaster/farcaster-messages-types.js'; -import { farcasterThreadIDFromConversationID } from '../shared/id-utils.js'; +import { + farcasterThreadIDFromConversationID, + userIDFromFID, +} from '../shared/id-utils.js'; import type { Media } from '../types/media-types.js'; import { messageTypes } from '../types/message-types-enum.js'; import type { RawMessageInfo } from '../types/message-types.js'; function convertFarcasterMessageToCommMessages( farcasterMessage: FarcasterMessage, + fcUserInfos?: FCUserInfos, ): $ReadOnlyArray { - const creatorID = `${farcasterMessage.senderFid}`; + const senderFid = farcasterMessage.senderFid.toString(); + const creatorID = + fcUserInfos?.get(senderFid)?.userID ?? userIDFromFID(senderFid); + const threadID = farcasterThreadIDFromConversationID( farcasterMessage.conversationId, ); @@ -18,7 +26,10 @@ farcasterMessage.type === 'group_membership_addition' && farcasterMessage.actionTargetUserContext?.fid ) { - const addedUser = `${farcasterMessage.actionTargetUserContext.fid}`; + const addedUserFID = + farcasterMessage.actionTargetUserContext.fid.toString(); + const addedUser = + fcUserInfos?.get(addedUserFID)?.userID ?? userIDFromFID(addedUserFID); return [ { id: farcasterMessage.messageId,