From ad3f8acbe5c43cab3134520245bee0f59ee4b27f Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 23 Mar 2023 14:14:53 -0500 Subject: [PATCH] EntityStore: allow passing an EntityRequest object to useEntities --- app/soapbox/api/index.ts | 1 + app/soapbox/entity-store/hooks/types.ts | 8 ++++++++ app/soapbox/entity-store/hooks/useEntities.ts | 16 +++++++--------- app/soapbox/entity-store/hooks/utils.ts | 16 ++++++++++++++-- app/soapbox/hooks/useGroups.ts | 5 ++--- 5 files changed, 32 insertions(+), 14 deletions(-) diff --git a/app/soapbox/api/index.ts b/app/soapbox/api/index.ts index fc19e7c41..520c3b7ad 100644 --- a/app/soapbox/api/index.ts +++ b/app/soapbox/api/index.ts @@ -15,6 +15,7 @@ import { getAccessToken, getAppToken, isURL, parseBaseURL } from 'soapbox/utils/ import type MockAdapter from 'axios-mock-adapter'; + /** Parse Link headers, mostly for pagination. @see {@link https://www.npmjs.com/package/http-link-header} diff --git a/app/soapbox/entity-store/hooks/types.ts b/app/soapbox/entity-store/hooks/types.ts index 7ce99fd82..8bd788d93 100644 --- a/app/soapbox/entity-store/hooks/types.ts +++ b/app/soapbox/entity-store/hooks/types.ts @@ -1,4 +1,5 @@ import type { Entity } from '../types'; +import type { AxiosRequestConfig } from 'axios'; import type z from 'zod'; type EntitySchema = z.ZodType; @@ -24,9 +25,16 @@ type EntitiesPath = [entityType: string, listKey: string] /** Used to look up a single entity by its ID. */ type EntityPath = [entityType: string, entityId: string] +/** + * Passed into hooks to make requests. + * Can be a URL for GET requests, or a request object. + */ +type EntityRequest = string | URL | AxiosRequestConfig; + export type { EntitySchema, ExpandedEntitiesPath, EntitiesPath, EntityPath, + EntityRequest, }; \ 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 8e679709a..b424ed1f6 100644 --- a/app/soapbox/entity-store/hooks/useEntities.ts +++ b/app/soapbox/entity-store/hooks/useEntities.ts @@ -8,10 +8,10 @@ import { realNumberSchema } from 'soapbox/utils/numbers'; import { entitiesFetchFail, entitiesFetchRequest, entitiesFetchSuccess, invalidateEntityList } from '../actions'; -import { parseEntitiesPath } from './utils'; +import { parseEntitiesPath, toAxiosRequest } from './utils'; import type { Entity, EntityListState } from '../types'; -import type { EntitiesPath, EntitySchema, ExpandedEntitiesPath } from './types'; +import type { EntitiesPath, EntityRequest, EntitySchema, ExpandedEntitiesPath } from './types'; import type { RootState } from 'soapbox/store'; /** Additional options for the hook. */ @@ -32,7 +32,7 @@ function useEntities( /** Tells us where to find/store the entity in the cache. */ expandedPath: ExpandedEntitiesPath, /** API route to GET, eg `'/api/v1/notifications'`. If undefined, nothing will be fetched. */ - endpoint: string | undefined, + request: EntityRequest, /** Additional options for the hook. */ opts: UseEntitiesOpts = {}, ) { @@ -54,14 +54,14 @@ function useEntities( const next = useListState(path, 'next'); const prev = useListState(path, 'prev'); - const fetchPage = async(url: string, overwrite = false): Promise => { + const fetchPage = async(req: EntityRequest, overwrite = false): Promise => { // Get `isFetching` state from the store again to prevent race conditions. const isFetching = selectListState(getState(), path, 'fetching'); if (isFetching) return; dispatch(entitiesFetchRequest(entityType, listKey)); try { - const response = await api.get(url); + const response = await api.request(toAxiosRequest(req)); const schema = opts.schema || z.custom(); const entities = filteredArray(schema).parse(response.data); const parsedCount = realNumberSchema.safeParse(response.headers['x-total-count']); @@ -82,9 +82,7 @@ function useEntities( }; const fetchEntities = async(): Promise => { - if (endpoint) { - await fetchPage(endpoint, true); - } + await fetchPage(request, true); }; const fetchNextPage = async(): Promise => { @@ -114,7 +112,7 @@ function useEntities( if (isInvalid || isUnset || isStale) { fetchEntities(); } - }, [endpoint, isEnabled]); + }, [request, isEnabled]); return { entities, diff --git a/app/soapbox/entity-store/hooks/utils.ts b/app/soapbox/entity-store/hooks/utils.ts index d137ca1fb..741a5cf13 100644 --- a/app/soapbox/entity-store/hooks/utils.ts +++ b/app/soapbox/entity-store/hooks/utils.ts @@ -1,4 +1,5 @@ -import type { EntitiesPath, ExpandedEntitiesPath } from './types'; +import type { EntitiesPath, EntityRequest, ExpandedEntitiesPath } from './types'; +import type { AxiosRequestConfig } from 'axios'; function parseEntitiesPath(expandedPath: ExpandedEntitiesPath) { const [entityType, ...listKeys] = expandedPath; @@ -12,4 +13,15 @@ function parseEntitiesPath(expandedPath: ExpandedEntitiesPath) { }; } -export { parseEntitiesPath }; \ No newline at end of file +function toAxiosRequest(req: EntityRequest): AxiosRequestConfig { + if (typeof req === 'string' || req instanceof URL) { + return { + method: 'get', + url: req.toString(), + }; + } + + return req; +} + +export { parseEntitiesPath, toAxiosRequest }; \ No newline at end of file diff --git a/app/soapbox/hooks/useGroups.ts b/app/soapbox/hooks/useGroups.ts index d143b5f99..832d852a5 100644 --- a/app/soapbox/hooks/useGroups.ts +++ b/app/soapbox/hooks/useGroups.ts @@ -52,11 +52,10 @@ function useGroupRelationship(groupId: string) { function useGroupRelationships(groupIds: string[]) { const q = groupIds.map(id => `id[]=${id}`).join('&'); - const endpoint = groupIds.length ? `/api/v1/groups/relationships?${q}` : undefined; const { entities, ...result } = useEntities( [Entities.GROUP_RELATIONSHIPS, ...groupIds], - endpoint, - { schema: groupRelationshipSchema }, + `/api/v1/groups/relationships?${q}`, + { schema: groupRelationshipSchema, enabled: groupIds.length > 0 }, ); const relationships = entities.reduce>((map, relationship) => {