From 7be8218f0c1fa73a525a5a19215a83d591856a3f Mon Sep 17 00:00:00 2001 From: Chewbacca Date: Mon, 13 Mar 2023 16:08:42 -0400 Subject: [PATCH] Convert popular/suggested Groups to use Entities --- app/soapbox/entity-store/entities.ts | 3 +- app/soapbox/entity-store/hooks/useEntities.ts | 20 ++++++--- app/soapbox/entity-store/types.ts | 2 + app/soapbox/entity-store/utils.ts | 3 +- .../components/discover/popular-groups.tsx | 2 +- .../components/discover/suggested-groups.tsx | 2 +- .../panels/suggested-groups-panel.tsx | 2 +- app/soapbox/hooks/api/usePopularGroups.ts | 40 +++++++++++++++++ app/soapbox/hooks/api/useSuggestedGroups.ts | 40 +++++++++++++++++ app/soapbox/hooks/useGroups.ts | 2 +- app/soapbox/queries/groups.ts | 44 ------------------- 11 files changed, 104 insertions(+), 56 deletions(-) create mode 100644 app/soapbox/hooks/api/usePopularGroups.ts create mode 100644 app/soapbox/hooks/api/useSuggestedGroups.ts diff --git a/app/soapbox/entity-store/entities.ts b/app/soapbox/entity-store/entities.ts index b196baadf..886c60113 100644 --- a/app/soapbox/entity-store/entities.ts +++ b/app/soapbox/entity-store/entities.ts @@ -1,3 +1,4 @@ export enum Entities { - GROUP_MEMBERSHIPS = 'GroupMemberships' + GROUP_MEMBERSHIPS = 'GroupMemberships', + POPULAR_GROUPS = 'PopularGroups' } \ No newline at end of file diff --git a/app/soapbox/entity-store/hooks/useEntities.ts b/app/soapbox/entity-store/hooks/useEntities.ts index 57e544faa..3b84f07ca 100644 --- a/app/soapbox/entity-store/hooks/useEntities.ts +++ b/app/soapbox/entity-store/hooks/useEntities.ts @@ -31,6 +31,8 @@ interface UseEntitiesOpts { * It is 1 minute by default, and can be set to `Infinity` to opt-out of automatic fetching. */ staleTime?: number + /** A flag to potentially disable sending requests to the API. */ + enabled?: boolean } /** A hook for fetching and displaying API entities. */ @@ -51,8 +53,11 @@ function useEntities( const entities = useAppSelector(state => selectEntities(state, path)); + const isEnabled = opts.enabled ?? true; const isFetching = useListState(path, 'fetching'); const lastFetchedAt = useListState(path, 'lastFetchedAt'); + const isFetched = useListState(path, 'fetched'); + const isError = !!useListState(path, 'error'); const next = useListState(path, 'next'); const prev = useListState(path, 'prev'); @@ -72,6 +77,7 @@ function useEntities( next: getNextLink(response), prev: getPrevLink(response), fetching: false, + fetched: true, error: null, lastFetchedAt: new Date(), })); @@ -101,20 +107,22 @@ function useEntities( const staleTime = opts.staleTime ?? 60000; useEffect(() => { - if (!isFetching && (!lastFetchedAt || lastFetchedAt.getTime() + staleTime <= Date.now())) { + if (isEnabled && !isFetching && (!lastFetchedAt || lastFetchedAt.getTime() + staleTime <= Date.now())) { fetchEntities(); } - }, [endpoint]); + }, [endpoint, isEnabled]); return { entities, fetchEntities, - isFetching, - isLoading: isFetching && entities.length === 0, - hasNextPage: !!next, - hasPreviousPage: !!prev, fetchNextPage, fetchPreviousPage, + hasNextPage: !!next, + hasPreviousPage: !!prev, + isError, + isFetched, + isFetching, + isLoading: isFetching && entities.length === 0, }; } diff --git a/app/soapbox/entity-store/types.ts b/app/soapbox/entity-store/types.ts index eb2a306a0..0e34b62a5 100644 --- a/app/soapbox/entity-store/types.ts +++ b/app/soapbox/entity-store/types.ts @@ -25,6 +25,8 @@ interface EntityListState { prev: string | undefined /** Error returned from the API, if any. */ error: any + /** Whether data has already been fetched */ + fetched: boolean /** Whether data for this list is currently being fetched. */ fetching: boolean /** Date of the last API fetch for this list. */ diff --git a/app/soapbox/entity-store/utils.ts b/app/soapbox/entity-store/utils.ts index 22e0f0c5b..6e96a9ec6 100644 --- a/app/soapbox/entity-store/utils.ts +++ b/app/soapbox/entity-store/utils.ts @@ -29,8 +29,9 @@ const createList = (): EntityList => ({ state: { next: undefined, prev: undefined, - fetching: false, error: null, + fetched: false, + fetching: false, lastFetchedAt: undefined, }, }); diff --git a/app/soapbox/features/groups/components/discover/popular-groups.tsx b/app/soapbox/features/groups/components/discover/popular-groups.tsx index 63b9067e0..79024466b 100644 --- a/app/soapbox/features/groups/components/discover/popular-groups.tsx +++ b/app/soapbox/features/groups/components/discover/popular-groups.tsx @@ -3,7 +3,7 @@ import { FormattedMessage } from 'react-intl'; import { Carousel, Stack, Text } from 'soapbox/components/ui'; import PlaceholderGroupDiscover from 'soapbox/features/placeholder/components/placeholder-group-discover'; -import { usePopularGroups } from 'soapbox/queries/groups'; +import { usePopularGroups } from 'soapbox/hooks/api/usePopularGroups'; import GroupGridItem from './group-grid-item'; diff --git a/app/soapbox/features/groups/components/discover/suggested-groups.tsx b/app/soapbox/features/groups/components/discover/suggested-groups.tsx index 1372f2f43..20686a7cc 100644 --- a/app/soapbox/features/groups/components/discover/suggested-groups.tsx +++ b/app/soapbox/features/groups/components/discover/suggested-groups.tsx @@ -3,7 +3,7 @@ import { FormattedMessage } from 'react-intl'; import { Carousel, Stack, Text } from 'soapbox/components/ui'; import PlaceholderGroupDiscover from 'soapbox/features/placeholder/components/placeholder-group-discover'; -import { useSuggestedGroups } from 'soapbox/queries/groups'; +import { useSuggestedGroups } from 'soapbox/hooks/api/useSuggestedGroups'; import GroupGridItem from './group-grid-item'; diff --git a/app/soapbox/features/ui/components/panels/suggested-groups-panel.tsx b/app/soapbox/features/ui/components/panels/suggested-groups-panel.tsx index a3c5a44d5..395a53f9c 100644 --- a/app/soapbox/features/ui/components/panels/suggested-groups-panel.tsx +++ b/app/soapbox/features/ui/components/panels/suggested-groups-panel.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { Widget } from 'soapbox/components/ui'; import GroupListItem from 'soapbox/features/groups/components/discover/group-list-item'; import PlaceholderGroupSearch from 'soapbox/features/placeholder/components/placeholder-group-search'; -import { useSuggestedGroups } from 'soapbox/queries/groups'; +import { useSuggestedGroups } from 'soapbox/hooks/api/useSuggestedGroups'; const SuggestedGroupsPanel = () => { const { groups, isFetching, isFetched, isError } = useSuggestedGroups(); diff --git a/app/soapbox/hooks/api/usePopularGroups.ts b/app/soapbox/hooks/api/usePopularGroups.ts new file mode 100644 index 000000000..b4b74c7f4 --- /dev/null +++ b/app/soapbox/hooks/api/usePopularGroups.ts @@ -0,0 +1,40 @@ +import { Entities } from 'soapbox/entity-store/entities'; +import { useEntities } from 'soapbox/entity-store/hooks'; +import { Group, groupSchema } from 'soapbox/schemas'; + +import { useFeatures } from '../useFeatures'; +import { useGroupRelationships } from '../useGroups'; + +function usePopularGroups() { + const features = useFeatures(); + + const { entities, ...result } = useEntities( + [Entities.POPULAR_GROUPS, ''], + '/api/mock/groups', // '/api/v1/truth/trends/groups' + { + parser: parseGroup, + enabled: features.groupsDiscovery, + }, + ); + + const { relationships } = useGroupRelationships(entities.map(entity => entity.id)); + + const groups = entities.map((group) => ({ + ...group, + relationship: relationships[group.id] || null, + })); + + return { + ...result, + groups, + }; +} + +const parseGroup = (entity: unknown) => { + const result = groupSchema.safeParse(entity); + if (result.success) { + return result.data; + } +}; + +export { usePopularGroups }; \ No newline at end of file diff --git a/app/soapbox/hooks/api/useSuggestedGroups.ts b/app/soapbox/hooks/api/useSuggestedGroups.ts new file mode 100644 index 000000000..1022a45b0 --- /dev/null +++ b/app/soapbox/hooks/api/useSuggestedGroups.ts @@ -0,0 +1,40 @@ +import { Entities } from 'soapbox/entity-store/entities'; +import { useEntities } from 'soapbox/entity-store/hooks'; +import { Group, groupSchema } from 'soapbox/schemas'; + +import { useFeatures } from '../useFeatures'; +import { useGroupRelationships } from '../useGroups'; + +function useSuggestedGroups() { + const features = useFeatures(); + + const { entities, ...result } = useEntities( + [Entities.POPULAR_GROUPS, ''], + '/api/mock/groups', // '/api/v1/truth/suggestions/groups' + { + parser: parseGroup, + enabled: features.groupsDiscovery, + }, + ); + + const { relationships } = useGroupRelationships(entities.map(entity => entity.id)); + + const groups = entities.map((group) => ({ + ...group, + relationship: relationships[group.id] || null, + })); + + return { + ...result, + groups, + }; +} + +const parseGroup = (entity: unknown) => { + const result = groupSchema.safeParse(entity); + if (result.success) { + return result.data; + } +}; + +export { useSuggestedGroups }; \ No newline at end of file diff --git a/app/soapbox/hooks/useGroups.ts b/app/soapbox/hooks/useGroups.ts index 7873c4f5d..4759973c8 100644 --- a/app/soapbox/hooks/useGroups.ts +++ b/app/soapbox/hooks/useGroups.ts @@ -44,4 +44,4 @@ function useGroupRelationships(groupIds: string[]) { }; } -export { useGroup, useGroups }; \ No newline at end of file +export { useGroup, useGroups, useGroupRelationships }; diff --git a/app/soapbox/queries/groups.ts b/app/soapbox/queries/groups.ts index fd986bf02..4ac826b4e 100644 --- a/app/soapbox/queries/groups.ts +++ b/app/soapbox/queries/groups.ts @@ -149,48 +149,6 @@ const usePendingGroups = () => { }; }; -const usePopularGroups = () => { - const features = useFeatures(); - const { fetchGroups } = useGroupsApi(); - - const getQuery = async () => { - const { groups } = await fetchGroups('/api/v1/truth/trends/groups'); - - return groups; - }; - - const queryInfo = useQuery(GroupKeys.popularGroups, getQuery, { - enabled: features.groupsDiscovery, - placeholderData: [], - }); - - return { - groups: queryInfo.data || [], - ...queryInfo, - }; -}; - -const useSuggestedGroups = () => { - const features = useFeatures(); - const { fetchGroups } = useGroupsApi(); - - const getQuery = async () => { - const { groups } = await fetchGroups('/api/v1/truth/suggestions/groups'); - - return groups; - }; - - const queryInfo = useQuery(GroupKeys.suggestedGroups, getQuery, { - enabled: features.groupsDiscovery, - placeholderData: [], - }); - - return { - groups: queryInfo.data || [], - ...queryInfo, - }; -}; - const useGroup = (id: string) => { const features = useFeatures(); const { fetchGroups } = useGroupsApi(); @@ -256,6 +214,4 @@ export { useJoinGroup, useLeaveGroup, usePendingGroups, - usePopularGroups, - useSuggestedGroups, };