diff --git a/native/avatars/thread-avatar.react.js b/native/avatars/thread-avatar.react.js --- a/native/avatars/thread-avatar.react.js +++ b/native/avatars/thread-avatar.react.js @@ -23,10 +23,11 @@ type Props = { +threadInfo: RawThreadInfo | ThreadInfo | ResolvedThreadInfo, +size: AvatarSize, + +farcasterChannelID?: ?string, }; function ThreadAvatar(props: Props): React.Node { - const { threadInfo, size } = props; + const { threadInfo, size, farcasterChannelID } = props; const avatarInfo = useAvatarForThread(threadInfo); @@ -66,7 +67,9 @@ const resolvedThreadAvatar = useResolvedThreadAvatar(avatarInfo, { userProfileInfo: displayUser, - channelInfo: { fcChannelID: communityInfo?.farcasterChannelID }, + channelInfo: { + fcChannelID: farcasterChannelID ?? communityInfo?.farcasterChannelID, + }, }); return <Avatar size={size} avatarInfo={resolvedThreadAvatar} />; diff --git a/native/components/community-joiner-modal.react.js b/native/components/community-joiner-modal.react.js --- a/native/components/community-joiner-modal.react.js +++ b/native/components/community-joiner-modal.react.js @@ -65,30 +65,45 @@ [communities], ); - const generateThreadInfos = React.useCallback( + const generateThreadInfosAndFCChannelIDs = React.useCallback( (communityList: $ReadOnlyArray<ClientCommunityInfoWithCommunityName>) => communityList - .map(community => - community.threadInfo - ? threadInfoFromRawThreadInfo( - community.threadInfo, - viewerID, - userInfos, - ) - : null, - ) + .map(community => { + const { farcasterChannelID, threadInfo } = community; + if (!threadInfo) { + return null; + } + return { + threadInfo: threadInfoFromRawThreadInfo( + threadInfo, + viewerID, + userInfos, + ), + farcasterChannelID, + }; + }) .filter(Boolean), [userInfos, viewerID], ); + const generalThreadInfosAndFCChannelIDs = React.useMemo( + () => generateThreadInfosAndFCChannelIDs(generalCommunities), + [generateThreadInfosAndFCChannelIDs, generalCommunities], + ); + + const cryptoThreadInfosAndFCChannelIDs = React.useMemo( + () => generateThreadInfosAndFCChannelIDs(cryptoCommunities), + [generateThreadInfosAndFCChannelIDs, cryptoCommunities], + ); + const generalThreadInfos = React.useMemo( - () => generateThreadInfos(generalCommunities), - [generateThreadInfos, generalCommunities], + () => generalThreadInfosAndFCChannelIDs.map(item => item.threadInfo), + [generalThreadInfosAndFCChannelIDs], ); const cryptoThreadInfos = React.useMemo( - () => generateThreadInfos(cryptoCommunities), - [generateThreadInfos, cryptoCommunities], + () => cryptoThreadInfosAndFCChannelIDs.map(item => item.threadInfo), + [cryptoThreadInfosAndFCChannelIDs], ); const generalIndex = useThreadSearchIndex(generalThreadInfos); @@ -97,23 +112,23 @@ const renderGeneralTab = React.useCallback( () => ( <CommunityList - threadInfos={generalThreadInfos} + threadInfosAndFCChannelIDs={generalThreadInfosAndFCChannelIDs} itemStyle={styles.threadListItem} searchIndex={generalIndex} /> ), - [generalIndex, generalThreadInfos, styles.threadListItem], + [generalIndex, generalThreadInfosAndFCChannelIDs, styles.threadListItem], ); const renderCryptoTab = React.useCallback( () => ( <CommunityList - threadInfos={cryptoThreadInfos} + threadInfosAndFCChannelIDs={cryptoThreadInfosAndFCChannelIDs} itemStyle={styles.threadListItem} searchIndex={cryptoIndex} /> ), - [cryptoIndex, cryptoThreadInfos, styles.threadListItem], + [cryptoIndex, cryptoThreadInfosAndFCChannelIDs, styles.threadListItem], ); const [index, setIndex] = React.useState(0); diff --git a/native/components/community-list-item.react.js b/native/components/community-list-item.react.js --- a/native/components/community-list-item.react.js +++ b/native/components/community-list-item.react.js @@ -36,10 +36,11 @@ type Props = { +threadInfo: ThreadInfo, +style: ViewStyle, + +farcasterChannelID?: ?string, }; function CommunityListItem(props: Props): React.Node { - const { threadInfo: initialThreadInfo, style } = props; + const { threadInfo: initialThreadInfo, style, farcasterChannelID } = props; // `initialThreadInfo` will not update if the user leaves or joins the thread, // so we also need `reduxThreadInfo` to track thread membership and @@ -183,7 +184,11 @@ return ( <View style={containerStyle}> - <ThreadAvatar size="S" threadInfo={resolvedThreadInfo} /> + <ThreadAvatar + size="S" + threadInfo={resolvedThreadInfo} + farcasterChannelID={farcasterChannelID} + /> <SingleLine style={singleLineTextStyle}> {resolvedThreadInfo.uiName} </SingleLine> diff --git a/native/components/community-list.react.js b/native/components/community-list.react.js --- a/native/components/community-list.react.js +++ b/native/components/community-list.react.js @@ -13,6 +13,11 @@ import { useIndicatorStyle, useStyles } from '../themes/colors.js'; import type { ViewStyle } from '../types/styles.js'; +export type ThreadInfoAndFarcasterChannelID = { + +threadInfo: ThreadInfo, + +farcasterChannelID?: ?string, +}; + const unboundStyles = { search: { marginBottom: 8, @@ -20,22 +25,24 @@ }; type Props = { - +threadInfos: $ReadOnlyArray<ThreadInfo>, + +threadInfosAndFCChannelIDs: $ReadOnlyArray<ThreadInfoAndFarcasterChannelID>, +itemStyle: ViewStyle, +searchIndex: SearchIndex, }; -const keyExtractor = (threadInfo: ThreadInfo): string => threadInfo.id; +const keyExtractor = ( + threadInfoAndFarcasterChannelID: ThreadInfoAndFarcasterChannelID, +): string => threadInfoAndFarcasterChannelID.threadInfo.id; const getItemLayout = ( - data: ?$ReadOnlyArray<ThreadInfo>, + data: ?$ReadOnlyArray<ThreadInfoAndFarcasterChannelID>, index: number, ): { length: number, offset: number, index: number } => { return { length: 24, offset: 24 * index, index }; }; function CommunityList(props: Props): React.Node { - const { threadInfos, itemStyle, searchIndex } = props; + const { threadInfosAndFCChannelIDs, itemStyle, searchIndex } = props; const styles = useStyles(unboundStyles); const indicatorStyle = useIndicatorStyle(); @@ -45,14 +52,17 @@ >([]); const listData = React.useMemo( - () => (searchText ? searchResults : threadInfos), - [searchText, searchResults, threadInfos], + () => (searchText ? searchResults : threadInfosAndFCChannelIDs), + [searchText, searchResults, threadInfosAndFCChannelIDs], ); const onChangeSearchText = React.useCallback( (text: string) => { invariant(searchIndex, 'searchIndex should be set'); const results = searchIndex.getSearchResults(text); + const threadInfos = threadInfosAndFCChannelIDs.map( + item => item.threadInfo, + ); const threadInfoResults = reorderThreadSearchResults( threadInfos, results, @@ -60,12 +70,16 @@ setSearchText(text); setSearchResults(threadInfoResults); }, - [searchIndex, threadInfos], + [searchIndex, threadInfosAndFCChannelIDs], ); const renderItem = React.useCallback( - ({ item }: { +item: ThreadInfo, ... }): React.Node => ( - <CommunityListItem threadInfo={item} style={itemStyle} /> + ({ item }: { +item: ThreadInfoAndFarcasterChannelID, ... }): React.Node => ( + <CommunityListItem + threadInfo={item.threadInfo} + style={itemStyle} + farcasterChannelID={item.farcasterChannelID} + /> ), [itemStyle], ); diff --git a/native/navigation/community-drawer-content.react.js b/native/navigation/community-drawer-content.react.js --- a/native/navigation/community-drawer-content.react.js +++ b/native/navigation/community-drawer-content.react.js @@ -17,6 +17,7 @@ import { useChildThreadInfosMap } from 'lib/hooks/thread-hooks.js'; import { useLegacyAshoatKeyserverCall } from 'lib/keyserver-conn/legacy-keyserver-call.js'; import { communityThreadSelector } from 'lib/selectors/thread-selectors.js'; +import { viewerIsMember } from 'lib/shared/thread-utils.js'; import type { ClientCommunityInfoWithCommunityName, ClientFetchAllCommunityInfosWithNamesResponse, @@ -87,7 +88,11 @@ ); void (async () => { const response = await getAllCommunityInfosWithNamesPromise; - setFetchedCommunitiesWithNames(response.allCommunityInfosWithNames); + setFetchedCommunitiesWithNames( + response.allCommunityInfosWithNames.filter( + community => !viewerIsMember(community.threadInfo), + ), + ); })(); }, [ callFetchPrimaryLinks,