Convert popular/suggested Groups to use Entities
This commit is contained in:
parent
50dadeb1b8
commit
7be8218f0c
|
@ -1,3 +1,4 @@
|
||||||
export enum Entities {
|
export enum Entities {
|
||||||
GROUP_MEMBERSHIPS = 'GroupMemberships'
|
GROUP_MEMBERSHIPS = 'GroupMemberships',
|
||||||
|
POPULAR_GROUPS = 'PopularGroups'
|
||||||
}
|
}
|
|
@ -31,6 +31,8 @@ interface UseEntitiesOpts<TEntity extends Entity> {
|
||||||
* It is 1 minute by default, and can be set to `Infinity` to opt-out of automatic fetching.
|
* It is 1 minute by default, and can be set to `Infinity` to opt-out of automatic fetching.
|
||||||
*/
|
*/
|
||||||
staleTime?: number
|
staleTime?: number
|
||||||
|
/** A flag to potentially disable sending requests to the API. */
|
||||||
|
enabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A hook for fetching and displaying API entities. */
|
/** A hook for fetching and displaying API entities. */
|
||||||
|
@ -51,8 +53,11 @@ function useEntities<TEntity extends Entity>(
|
||||||
|
|
||||||
const entities = useAppSelector(state => selectEntities<TEntity>(state, path));
|
const entities = useAppSelector(state => selectEntities<TEntity>(state, path));
|
||||||
|
|
||||||
|
const isEnabled = opts.enabled ?? true;
|
||||||
const isFetching = useListState(path, 'fetching');
|
const isFetching = useListState(path, 'fetching');
|
||||||
const lastFetchedAt = useListState(path, 'lastFetchedAt');
|
const lastFetchedAt = useListState(path, 'lastFetchedAt');
|
||||||
|
const isFetched = useListState(path, 'fetched');
|
||||||
|
const isError = !!useListState(path, 'error');
|
||||||
|
|
||||||
const next = useListState(path, 'next');
|
const next = useListState(path, 'next');
|
||||||
const prev = useListState(path, 'prev');
|
const prev = useListState(path, 'prev');
|
||||||
|
@ -72,6 +77,7 @@ function useEntities<TEntity extends Entity>(
|
||||||
next: getNextLink(response),
|
next: getNextLink(response),
|
||||||
prev: getPrevLink(response),
|
prev: getPrevLink(response),
|
||||||
fetching: false,
|
fetching: false,
|
||||||
|
fetched: true,
|
||||||
error: null,
|
error: null,
|
||||||
lastFetchedAt: new Date(),
|
lastFetchedAt: new Date(),
|
||||||
}));
|
}));
|
||||||
|
@ -101,20 +107,22 @@ function useEntities<TEntity extends Entity>(
|
||||||
const staleTime = opts.staleTime ?? 60000;
|
const staleTime = opts.staleTime ?? 60000;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isFetching && (!lastFetchedAt || lastFetchedAt.getTime() + staleTime <= Date.now())) {
|
if (isEnabled && !isFetching && (!lastFetchedAt || lastFetchedAt.getTime() + staleTime <= Date.now())) {
|
||||||
fetchEntities();
|
fetchEntities();
|
||||||
}
|
}
|
||||||
}, [endpoint]);
|
}, [endpoint, isEnabled]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
entities,
|
entities,
|
||||||
fetchEntities,
|
fetchEntities,
|
||||||
isFetching,
|
|
||||||
isLoading: isFetching && entities.length === 0,
|
|
||||||
hasNextPage: !!next,
|
|
||||||
hasPreviousPage: !!prev,
|
|
||||||
fetchNextPage,
|
fetchNextPage,
|
||||||
fetchPreviousPage,
|
fetchPreviousPage,
|
||||||
|
hasNextPage: !!next,
|
||||||
|
hasPreviousPage: !!prev,
|
||||||
|
isError,
|
||||||
|
isFetched,
|
||||||
|
isFetching,
|
||||||
|
isLoading: isFetching && entities.length === 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,8 @@ interface EntityListState {
|
||||||
prev: string | undefined
|
prev: string | undefined
|
||||||
/** Error returned from the API, if any. */
|
/** Error returned from the API, if any. */
|
||||||
error: any
|
error: any
|
||||||
|
/** Whether data has already been fetched */
|
||||||
|
fetched: boolean
|
||||||
/** Whether data for this list is currently being fetched. */
|
/** Whether data for this list is currently being fetched. */
|
||||||
fetching: boolean
|
fetching: boolean
|
||||||
/** Date of the last API fetch for this list. */
|
/** Date of the last API fetch for this list. */
|
||||||
|
|
|
@ -29,8 +29,9 @@ const createList = (): EntityList => ({
|
||||||
state: {
|
state: {
|
||||||
next: undefined,
|
next: undefined,
|
||||||
prev: undefined,
|
prev: undefined,
|
||||||
fetching: false,
|
|
||||||
error: null,
|
error: null,
|
||||||
|
fetched: false,
|
||||||
|
fetching: false,
|
||||||
lastFetchedAt: undefined,
|
lastFetchedAt: undefined,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { Carousel, Stack, Text } from 'soapbox/components/ui';
|
import { Carousel, Stack, Text } from 'soapbox/components/ui';
|
||||||
import PlaceholderGroupDiscover from 'soapbox/features/placeholder/components/placeholder-group-discover';
|
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';
|
import GroupGridItem from './group-grid-item';
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { Carousel, Stack, Text } from 'soapbox/components/ui';
|
import { Carousel, Stack, Text } from 'soapbox/components/ui';
|
||||||
import PlaceholderGroupDiscover from 'soapbox/features/placeholder/components/placeholder-group-discover';
|
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';
|
import GroupGridItem from './group-grid-item';
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import React from 'react';
|
||||||
import { Widget } from 'soapbox/components/ui';
|
import { Widget } from 'soapbox/components/ui';
|
||||||
import GroupListItem from 'soapbox/features/groups/components/discover/group-list-item';
|
import GroupListItem from 'soapbox/features/groups/components/discover/group-list-item';
|
||||||
import PlaceholderGroupSearch from 'soapbox/features/placeholder/components/placeholder-group-search';
|
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 SuggestedGroupsPanel = () => {
|
||||||
const { groups, isFetching, isFetched, isError } = useSuggestedGroups();
|
const { groups, isFetching, isFetched, isError } = useSuggestedGroups();
|
||||||
|
|
|
@ -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<Group>(
|
||||||
|
[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 };
|
|
@ -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<Group>(
|
||||||
|
[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 };
|
|
@ -44,4 +44,4 @@ function useGroupRelationships(groupIds: string[]) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export { useGroup, useGroups };
|
export { useGroup, useGroups, useGroupRelationships };
|
||||||
|
|
|
@ -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<Group[]>(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<Group[]>(GroupKeys.suggestedGroups, getQuery, {
|
|
||||||
enabled: features.groupsDiscovery,
|
|
||||||
placeholderData: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
groups: queryInfo.data || [],
|
|
||||||
...queryInfo,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const useGroup = (id: string) => {
|
const useGroup = (id: string) => {
|
||||||
const features = useFeatures();
|
const features = useFeatures();
|
||||||
const { fetchGroups } = useGroupsApi();
|
const { fetchGroups } = useGroupsApi();
|
||||||
|
@ -256,6 +214,4 @@ export {
|
||||||
useJoinGroup,
|
useJoinGroup,
|
||||||
useLeaveGroup,
|
useLeaveGroup,
|
||||||
usePendingGroups,
|
usePendingGroups,
|
||||||
usePopularGroups,
|
|
||||||
useSuggestedGroups,
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue