From 2d973c9cd01a913f9515aa1eb2870255f4e37870 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 1 Feb 2025 22:05:51 -0600 Subject: [PATCH] Simplify/consolidate latest accounts code, fix suggestions pagination --- src/features/follow-recommendations/index.tsx | 32 +++---- .../ui/components/latest-accounts-panel.tsx | 17 +++- src/features/ui/index.tsx | 5 +- src/queries/suggestions.ts | 88 +++++-------------- 4 files changed, 51 insertions(+), 91 deletions(-) diff --git a/src/features/follow-recommendations/index.tsx b/src/features/follow-recommendations/index.tsx index 20e1992b8..1078edf2c 100644 --- a/src/features/follow-recommendations/index.tsx +++ b/src/features/follow-recommendations/index.tsx @@ -1,41 +1,33 @@ import { debounce } from 'es-toolkit'; -import { useEffect } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; -import { fetchSuggestions } from 'soapbox/actions/suggestions.ts'; import ScrollableList from 'soapbox/components/scrollable-list.tsx'; import { Column } from 'soapbox/components/ui/column.tsx'; import Stack from 'soapbox/components/ui/stack.tsx'; import Text from 'soapbox/components/ui/text.tsx'; import AccountContainer from 'soapbox/containers/account-container.tsx'; -import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts'; -import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts'; +import { useSuggestions } from 'soapbox/queries/suggestions.ts'; const messages = defineMessages({ heading: { id: 'follow_recommendations.heading', defaultMessage: 'Suggested Profiles' }, }); -const FollowRecommendations: React.FC = () => { - const dispatch = useAppDispatch(); +interface IFollowRecommendations { + local?: boolean; +} + +const FollowRecommendations: React.FC = ({ local = false }) => { const intl = useIntl(); - const suggestions = useAppSelector((state) => state.suggestions.items); - const hasMore = useAppSelector((state) => !!state.suggestions.next); - const isLoading = useAppSelector((state) => state.suggestions.isLoading); + const { data: suggestions, fetchNextPage, hasNextPage, isLoading, isFetchingNextPage } = useSuggestions({ local }); const handleLoadMore = debounce(() => { - if (isLoading) { - return null; + if (hasNextPage && !isFetchingNextPage) { + fetchNextPage(); } + }, 1000); - return dispatch(fetchSuggestions({ limit: 20 })); - }, 300); - - useEffect(() => { - dispatch(fetchSuggestions({ limit: 20 })); - }, []); - - if (suggestions.size === 0 && !isLoading) { + if (suggestions.length === 0 && !isLoading) { return ( @@ -52,7 +44,7 @@ const FollowRecommendations: React.FC = () => { isLoading={isLoading} scrollKey='suggestions' onLoadMore={handleLoadMore} - hasMore={hasMore} + hasMore={hasNextPage} itemClassName='pb-4' > {suggestions.map((suggestion) => ( diff --git a/src/features/ui/components/latest-accounts-panel.tsx b/src/features/ui/components/latest-accounts-panel.tsx index c1221defe..8f09d3c08 100644 --- a/src/features/ui/components/latest-accounts-panel.tsx +++ b/src/features/ui/components/latest-accounts-panel.tsx @@ -1,11 +1,13 @@ import xIcon from '@tabler/icons/outline/x.svg'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; +import { Link } from 'react-router-dom'; +import Text from 'soapbox/components/ui/text.tsx'; import Widget from 'soapbox/components/ui/widget.tsx'; import AccountContainer from 'soapbox/containers/account-container.tsx'; import PlaceholderSidebarSuggestions from 'soapbox/features/placeholder/components/placeholder-sidebar-suggestions.tsx'; import { useOwnAccount } from 'soapbox/hooks/useOwnAccount.ts'; -import { useDismissSuggestion, useLocalSuggestions } from 'soapbox/queries/suggestions.ts'; +import { useDismissSuggestion, useSuggestions } from 'soapbox/queries/suggestions.ts'; import type { Account as AccountEntity } from 'soapbox/types/entities.ts'; @@ -21,7 +23,7 @@ const LatestAccountsPanel: React.FC = ({ limit }) => { const intl = useIntl(); const { account } = useOwnAccount(); - const { data: suggestions, isFetching } = useLocalSuggestions(); + const { data: suggestions, isFetching } = useSuggestions({ local: true }); const dismissSuggestion = useDismissSuggestion(); const suggestionsToRender = suggestions.slice(0, limit); @@ -35,7 +37,16 @@ const LatestAccountsPanel: React.FC = ({ limit }) => { } return ( - }> + } + action={ + + + + + + } + > {isFetching ? ( ) : ( diff --git a/src/features/ui/index.tsx b/src/features/ui/index.tsx index b5e23a0e5..d6422624e 100644 --- a/src/features/ui/index.tsx +++ b/src/features/ui/index.tsx @@ -262,8 +262,9 @@ const SwitchingColumnsArea: React.FC = ({ children }) => - {features.suggestions && } - {features.profileDirectory && } + {features.suggestionsLocal && } + {features.suggestions && } + {features.profileDirectory && } {features.events && } {features.chats && } diff --git a/src/queries/suggestions.ts b/src/queries/suggestions.ts index c8e1624f4..c45fa4e57 100644 --- a/src/queries/suggestions.ts +++ b/src/queries/suggestions.ts @@ -7,14 +7,15 @@ import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts'; import { PaginatedResult, removePageItem } from '../utils/queries.ts'; -import type { IAccount } from './accounts.ts'; +import type { Account } from 'soapbox/schemas/account.ts'; type Suggestion = { - source: 'staff'; - account: IAccount; + source: string; + account: Account; } type Result = { + source: string; account: string; } @@ -27,45 +28,48 @@ const SuggestionKeys = { localSuggestions: ['suggestions', 'local'] as const, }; -const useSuggestions = () => { +interface UseSuggestionsOpts { + local?: boolean; +} + +const useSuggestions = (opts?: UseSuggestionsOpts) => { const api = useApi(); const dispatch = useAppDispatch(); + const local = opts?.local ?? false; - const getV2Suggestions = async (pageParam: PageParam): Promise> => { - const endpoint = pageParam?.link || '/api/v2/suggestions'; + const getV2Suggestions = async (pageParam?: PageParam): Promise> => { + const endpoint = pageParam?.link || (local ? '/api/v2/ditto/suggestions/local' : '/api/v2/suggestions'); const response = await api.get(endpoint); const next = response.next(); - const hasMore = !!next; const data: Suggestion[] = await response.json(); const accounts = data.map(({ account }) => account); const accountIds = accounts.map((account) => account.id); + dispatch(importFetchedAccounts(accounts)); dispatch(fetchRelationships(accountIds)); return { result: data.map(x => ({ ...x, account: x.account.id })), link: next ?? undefined, - hasMore, + hasMore: !!next, }; }; const result = useInfiniteQuery({ - queryKey: SuggestionKeys.suggestions, - queryFn: ({ pageParam }: any) => getV2Suggestions(pageParam), + queryKey: local ? SuggestionKeys.localSuggestions : SuggestionKeys.suggestions, + queryFn: ({ pageParam }) => getV2Suggestions(pageParam), placeholderData: keepPreviousData, - initialPageParam: { nextLink: undefined }, - getNextPageParam: (config) => { + initialPageParam: undefined as PageParam | undefined, + getNextPageParam: (config): PageParam | undefined => { if (config?.hasMore) { - return { nextLink: config?.link }; + return { link: config?.link }; } - - return undefined; }, }); - const data: any = result.data?.pages.reduce( - (prev: any, curr: any) => [...prev, ...curr.result], + const data = result.data?.pages.reduce( + (prev, curr) => [...prev, ...curr.result], [], ); @@ -134,53 +138,5 @@ function useOnboardingSuggestions() { }; } -const useLocalSuggestions = () => { - const api = useApi(); - const dispatch = useAppDispatch(); - const getLocalSuggestions = async (pageParam: PageParam): Promise> => { - const endpoint = pageParam?.link || '/api/v2/ditto/suggestions/local'; - const response = await api.get(endpoint); - const next = response.next(); - const hasMore = !!next; - - const data: Suggestion[] = await response.json(); - const accounts = data.map(({ account }) => account); - const accountIds = accounts.map((account) => account.id); - dispatch(importFetchedAccounts(accounts)); - dispatch(fetchRelationships(accountIds)); - - return { - result: data.map(x => ({ ...x, account: x.account.id })), - link: next ?? undefined, - hasMore, - }; - }; - - const result = useInfiniteQuery({ - queryKey: SuggestionKeys.localSuggestions, - queryFn: ({ pageParam }: any) => getLocalSuggestions(pageParam), - placeholderData: keepPreviousData, - initialPageParam: { nextLink: undefined }, - getNextPageParam: (config) => { - if (config?.hasMore) { - return { nextLink: config?.link }; - } - - return undefined; - }, - }); - - const data: any = result.data?.pages.reduce( - (prev: any, curr: any) => [...prev, ...curr.result], - [], - ); - - return { - ...result, - data: data || [], - }; -}; - - -export { useOnboardingSuggestions, useSuggestions, useDismissSuggestion, useLocalSuggestions }; \ No newline at end of file +export { useOnboardingSuggestions, useSuggestions, useDismissSuggestion }; \ No newline at end of file