diff --git a/lib/selectors/nav-selectors.js b/lib/selectors/nav-selectors.js --- a/lib/selectors/nav-selectors.js +++ b/lib/selectors/nav-selectors.js @@ -6,6 +6,7 @@ import { useResolvableNames } from '../hooks/names-cache.js'; import SearchIndex from '../shared/search-index.js'; import { threadTypeIsPrivate } from '../shared/threads/thread-specs.js'; +import { ensNameForFarcasterUsername } from '../shared/user-utils.js'; import type { Platform } from '../types/device-types.js'; import { type CalendarQuery, @@ -81,16 +82,24 @@ // recaculation just once for every update of the underlying Redux data. const useResolvableNamesOptions = { allAtOnce: true }; +type SearchIndexUserInfo = { + +id: string, + +username: ?string, + +fid?: ?string, + +farcasterUsername?: ?string, + ... +}; + function useUserSearchIndex( userInfos: $ReadOnlyArray, ): SearchIndex { - const membersWithENSNames = useResolvableNames( + const membersWithENSNames = useResolvableNames( userInfos, useResolvableNamesOptions, ); const memberMap = React.useMemo(() => { - const result = new Map(); + const result = new Map(); for (const userInfo of membersWithENSNames) { result.set(userInfo.id, userInfo); } @@ -114,6 +123,17 @@ searchTextArray.push(resolvedUsername); } + const resolvedFarcasterName = resolvedUserInfo?.farcasterUsername; + if ( + resolvedFarcasterName && + resolvedFarcasterName !== rawUsername && + resolvedFarcasterName !== resolvedUsername + ) { + searchTextArray.push( + ensNameForFarcasterUsername(resolvedFarcasterName), + ); + } + searchIndex.addEntry(userInfo.id, searchTextArray.join(' ')); } diff --git a/lib/shared/search-utils.js b/lib/shared/search-utils.js --- a/lib/shared/search-utils.js +++ b/lib/shared/search-utils.js @@ -26,6 +26,7 @@ import { useUsersSupportFarcasterDCs, useUsersSupportThickThreads, + useGetCommFCUsersForFIDs, } from '../hooks/user-identities-hooks.js'; import { useIdentitySearch } from '../identity-search/identity-search-context.js'; import { useLegacyAshoatKeyserverCall } from '../keyserver-conn/legacy-keyserver-call.js'; @@ -364,6 +365,7 @@ const usersSupportFarcasterDCs = useUsersSupportFarcasterDCs(); const usersSupportThickThreads = useUsersSupportThickThreads(); + const getFarcasterUsers = useGetCommFCUsersForFIDs(); const [potentialMembers, setPotentialMembers] = React.useState< UserListItem[], @@ -371,17 +373,35 @@ React.useEffect(() => { void (async () => { const usersIDs = sortedMembers.map(user => user.id); + const fids = usersIDs.map(extractFIDFromUserID).filter(Boolean); + const fcUserInfos = await getFarcasterUsers(fids); + const fcCommUserIDs = Array.from(fcUserInfos.keys()); + + const userIDsSet = new Set([...usersIDs, ...fcCommUserIDs]); + const allUserIDs = [...userIDsSet]; + const [thickThreadUsers, farcasterUsers] = await Promise.all([ - usersSupportThickThreads(usersIDs), - usersSupportFarcasterDCs(usersIDs), + usersSupportThickThreads(allUserIDs), + usersSupportFarcasterDCs(allUserIDs), ]); const usersWithProtocol = sortedMembers .map(member => { + let memberID = member.id; + const fid = extractFIDFromUserID(memberID); + if (fid) { + const fcCommUser = fcUserInfos.get(fid); + if (fcCommUser) { + // replace Farcaster-only userID with actual Comm + // userID when possible + memberID = fcCommUser.userID; + } + } + const supportedProtocols: Array = []; - if (thickThreadUsers.get(member.id)) { + if (thickThreadUsers.get(memberID)) { supportedProtocols.push(protocolNames.COMM_DM); } - if (viewerID !== member.id && farcasterUsers.get(member.id)) { + if (viewerID !== memberID && farcasterUsers.get(memberID)) { supportedProtocols.push(protocolNames.FARCASTER_DC); } if ( @@ -392,6 +412,7 @@ } return { ...member, + id: memberID, supportedProtocols: [...supportedProtocols], }; }) @@ -403,6 +424,7 @@ sortedMembers, usersSupportFarcasterDCs, usersSupportThickThreads, + getFarcasterUsers, viewerID, ]); diff --git a/lib/types/user-types.js b/lib/types/user-types.js --- a/lib/types/user-types.js +++ b/lib/types/user-types.js @@ -126,4 +126,5 @@ }, +avatar?: ?ClientAvatar, +supportedProtocols: Array, + +farcasterUsername?: ?string, }; diff --git a/native/components/user-list.react.js b/native/components/user-list.react.js --- a/native/components/user-list.react.js +++ b/native/components/user-list.react.js @@ -37,7 +37,7 @@ } static keyExtractor = (userInfo: UserListItem): string => { - return userInfo.id; + return `${userInfo.id}:${userInfo.username}`; }; renderItem = (row: { +item: UserListItem, ... }): React.Node => { diff --git a/web/chat/chat-thread-composer.react.js b/web/chat/chat-thread-composer.react.js --- a/web/chat/chat-thread-composer.react.js +++ b/web/chat/chat-thread-composer.react.js @@ -200,7 +200,10 @@ icon = ; } return ( -
  • +