Merge branch 'relationships-chunk' into 'main'

Fetch relationships in chunks of 20

See merge request soapbox-pub/soapbox!3308
This commit is contained in:
Alex Gleason 2024-12-28 23:13:15 +00:00
commit 59698cff90
2 changed files with 49 additions and 12 deletions

View File

@ -1,6 +1,7 @@
import { HTTPError } from 'soapbox/api/HTTPError.ts'; import { HTTPError } from 'soapbox/api/HTTPError.ts';
import { importEntities } from 'soapbox/entity-store/actions.ts'; import { importEntities } from 'soapbox/entity-store/actions.ts';
import { Entities } from 'soapbox/entity-store/entities.ts'; import { Entities } from 'soapbox/entity-store/entities.ts';
import { relationshipSchema } from 'soapbox/schemas/relationship.ts';
import { selectAccount } from 'soapbox/selectors/index.ts'; import { selectAccount } from 'soapbox/selectors/index.ts';
import { isLoggedIn } from 'soapbox/utils/auth.ts'; import { isLoggedIn } from 'soapbox/utils/auth.ts';
import { getFeatures, parseVersion, PLEROMA } from 'soapbox/utils/features.ts'; import { getFeatures, parseVersion, PLEROMA } from 'soapbox/utils/features.ts';
@ -15,7 +16,7 @@ import {
import type { Map as ImmutableMap } from 'immutable'; import type { Map as ImmutableMap } from 'immutable';
import type { AppDispatch, RootState } from 'soapbox/store.ts'; 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'; import type { History } from 'soapbox/types/history.ts';
const ACCOUNT_CREATE_REQUEST = 'ACCOUNT_CREATE_REQUEST'; const ACCOUNT_CREATE_REQUEST = 'ACCOUNT_CREATE_REQUEST';
@ -609,7 +610,7 @@ const expandFollowingFail = (id: string, error: unknown) => ({
}); });
const fetchRelationships = (accountIds: string[]) => const fetchRelationships = (accountIds: string[]) =>
(dispatch: AppDispatch, getState: () => RootState) => { async (dispatch: AppDispatch, getState: () => RootState) => {
if (!isLoggedIn(getState)) return null; if (!isLoggedIn(getState)) return null;
const loadedRelationships = getState().relationships; const loadedRelationships = getState().relationships;
@ -621,15 +622,32 @@ const fetchRelationships = (accountIds: string[]) =>
dispatch(fetchRelationshipsRequest(newAccountIds)); dispatch(fetchRelationshipsRequest(newAccountIds));
return api(getState) const results: Relationship[] = [];
.get(`/api/v1/accounts/relationships?${newAccountIds.map(id => `id[]=${id}`).join('&')}`)
.then((response) => response.json()).then((data) => { try {
dispatch(importEntities(data, Entities.RELATIONSHIPS)); for (const ids of chunkArray(newAccountIds, 20)) {
dispatch(fetchRelationshipsSuccess(data)); const response = await api(getState).get('/api/v1/accounts/relationships', { searchParams: { id: ids } });
}) const json = await response.json();
.catch(error => dispatch(fetchRelationshipsFail(error))); const data = relationshipSchema.array().parse(json);
results.push(...data);
}
dispatch(importEntities(results, Entities.RELATIONSHIPS));
dispatch(fetchRelationshipsSuccess(results));
} catch (error) {
dispatch(fetchRelationshipsFail(error));
}
}; };
function* chunkArray<T>(array: T[], chunkSize: number): Iterable<T[]> {
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[]) => ({ const fetchRelationshipsRequest = (ids: string[]) => ({
type: RELATIONSHIPS_FETCH_REQUEST, type: RELATIONSHIPS_FETCH_REQUEST,
ids, ids,

View File

@ -1,3 +1,4 @@
import { MastodonResponse } from 'soapbox/api/MastodonResponse.ts';
import { Entities } from 'soapbox/entity-store/entities.ts'; import { Entities } from 'soapbox/entity-store/entities.ts';
import { useBatchedEntities } from 'soapbox/entity-store/hooks/useBatchedEntities.ts'; import { useBatchedEntities } from 'soapbox/entity-store/hooks/useBatchedEntities.ts';
import { useApi } from 'soapbox/hooks/useApi.ts'; import { useApi } from 'soapbox/hooks/useApi.ts';
@ -8,9 +9,19 @@ function useRelationships(listKey: string[], ids: string[]) {
const api = useApi(); const api = useApi();
const { isLoggedIn } = useLoggedIn(); const { isLoggedIn } = useLoggedIn();
function fetchRelationships(ids: string[]) { async function fetchRelationships(ids: string[]) {
const q = ids.map((id) => `id[]=${id}`).join('&'); const results: Relationship[] = [];
return api.get(`/api/v1/accounts/relationships?${q}`);
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<Relationship>( const { entityMap: relationships, ...result } = useBatchedEntities<Relationship>(
@ -23,4 +34,12 @@ function useRelationships(listKey: string[], ids: string[]) {
return { relationships, ...result }; return { relationships, ...result };
} }
function* chunkArray<T>(array: T[], chunkSize: number): Iterable<T[]> {
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 }; export { useRelationships };