From 85cdec6a5b03a5b0ae4f64ba223c5ea48b7a5701 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 28 Dec 2024 17:10:52 -0600 Subject: [PATCH] Fetch relationships in chunks of 20 --- src/actions/accounts.ts | 36 ++++++++++++++++------ src/api/hooks/accounts/useRelationships.ts | 25 +++++++++++++-- 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/src/actions/accounts.ts b/src/actions/accounts.ts index b4a3c49fa..94de46651 100644 --- a/src/actions/accounts.ts +++ b/src/actions/accounts.ts @@ -1,6 +1,7 @@ import { HTTPError } from 'soapbox/api/HTTPError.ts'; import { importEntities } from 'soapbox/entity-store/actions.ts'; import { Entities } from 'soapbox/entity-store/entities.ts'; +import { relationshipSchema } from 'soapbox/schemas/relationship.ts'; import { selectAccount } from 'soapbox/selectors/index.ts'; import { isLoggedIn } from 'soapbox/utils/auth.ts'; import { getFeatures, parseVersion, PLEROMA } from 'soapbox/utils/features.ts'; @@ -15,7 +16,7 @@ import { import type { Map as ImmutableMap } from 'immutable'; import type { AppDispatch, RootState } from 'soapbox/store.ts'; -import type { APIEntity, Status } from 'soapbox/types/entities.ts'; +import type { APIEntity, Relationship, Status } from 'soapbox/types/entities.ts'; import type { History } from 'soapbox/types/history.ts'; const ACCOUNT_CREATE_REQUEST = 'ACCOUNT_CREATE_REQUEST'; @@ -609,7 +610,7 @@ const expandFollowingFail = (id: string, error: unknown) => ({ }); const fetchRelationships = (accountIds: string[]) => - (dispatch: AppDispatch, getState: () => RootState) => { + async (dispatch: AppDispatch, getState: () => RootState) => { if (!isLoggedIn(getState)) return null; const loadedRelationships = getState().relationships; @@ -621,15 +622,32 @@ const fetchRelationships = (accountIds: string[]) => dispatch(fetchRelationshipsRequest(newAccountIds)); - return api(getState) - .get(`/api/v1/accounts/relationships?${newAccountIds.map(id => `id[]=${id}`).join('&')}`) - .then((response) => response.json()).then((data) => { - dispatch(importEntities(data, Entities.RELATIONSHIPS)); - dispatch(fetchRelationshipsSuccess(data)); - }) - .catch(error => dispatch(fetchRelationshipsFail(error))); + const results: Relationship[] = []; + + try { + for (const ids of chunkArray(newAccountIds, 20)) { + const response = await api(getState).get('/api/v1/accounts/relationships', { searchParams: { id: ids } }); + const json = await response.json(); + const data = relationshipSchema.array().parse(json); + + results.push(...data); + } + + dispatch(importEntities(results, Entities.RELATIONSHIPS)); + dispatch(fetchRelationshipsSuccess(results)); + } catch (error) { + dispatch(fetchRelationshipsFail(error)); + } }; +function* chunkArray(array: T[], chunkSize: number): Iterable { + if (chunkSize <= 0) throw new Error('Chunk size must be greater than zero.'); + + for (let i = 0; i < array.length; i += chunkSize) { + yield array.slice(i, i + chunkSize); + } +} + const fetchRelationshipsRequest = (ids: string[]) => ({ type: RELATIONSHIPS_FETCH_REQUEST, ids, diff --git a/src/api/hooks/accounts/useRelationships.ts b/src/api/hooks/accounts/useRelationships.ts index fbded4a59..b9f18ba7f 100644 --- a/src/api/hooks/accounts/useRelationships.ts +++ b/src/api/hooks/accounts/useRelationships.ts @@ -1,3 +1,4 @@ +import { MastodonResponse } from 'soapbox/api/MastodonResponse.ts'; import { Entities } from 'soapbox/entity-store/entities.ts'; import { useBatchedEntities } from 'soapbox/entity-store/hooks/useBatchedEntities.ts'; import { useApi } from 'soapbox/hooks/useApi.ts'; @@ -8,9 +9,19 @@ function useRelationships(listKey: string[], ids: string[]) { const api = useApi(); const { isLoggedIn } = useLoggedIn(); - function fetchRelationships(ids: string[]) { - const q = ids.map((id) => `id[]=${id}`).join('&'); - return api.get(`/api/v1/accounts/relationships?${q}`); + async function fetchRelationships(ids: string[]) { + const results: Relationship[] = []; + + for (const id of chunkArray(ids, 20)) { + const response = await api.get('/api/v1/accounts/relationships', { searchParams: { id } }); + const json = await response.json(); + + results.push(...json); + } + + return new MastodonResponse(JSON.stringify(results), { + headers: { 'Content-Type': 'application/json' }, + }); } const { entityMap: relationships, ...result } = useBatchedEntities( @@ -23,4 +34,12 @@ function useRelationships(listKey: string[], ids: string[]) { return { relationships, ...result }; } +function* chunkArray(array: T[], chunkSize: number): Iterable { + if (chunkSize <= 0) throw new Error('Chunk size must be greater than zero.'); + + for (let i = 0; i < array.length; i += chunkSize) { + yield array.slice(i, i + chunkSize); + } +} + export { useRelationships }; \ No newline at end of file