Optionally use Link header for search pagination

This commit is contained in:
oakes 2023-06-15 09:05:55 -04:00
parent 82ff60efaa
commit a985348bf1
2 changed files with 35 additions and 17 deletions

View file

@ -1,4 +1,4 @@
import api from '../api';
import api, { getLinks } from '../api';
import { fetchRelationships } from './accounts';
import { importFetchedAccounts, importFetchedStatuses } from './importer';
@ -83,7 +83,9 @@ const submitSearch = (filter?: SearchFilter) =>
dispatch(importFetchedStatuses(response.data.statuses));
}
dispatch(fetchSearchSuccess(response.data, value, type));
const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(fetchSearchSuccess(response.data, value, type, next ? next.uri : null));
dispatch(fetchRelationships(response.data.accounts.map((item: APIEntity) => item.id)));
}).catch(error => {
dispatch(fetchSearchFail(error));
@ -95,11 +97,12 @@ const fetchSearchRequest = (value: string) => ({
value,
});
const fetchSearchSuccess = (results: APIEntity[], searchTerm: string, searchType: SearchFilter) => ({
const fetchSearchSuccess = (results: APIEntity[], searchTerm: string, searchType: SearchFilter, next: string | null) => ({
type: SEARCH_FETCH_SUCCESS,
results,
searchTerm,
searchType,
next,
});
const fetchSearchFail = (error: AxiosError) => ({
@ -125,17 +128,26 @@ const expandSearch = (type: SearchFilter) => (dispatch: AppDispatch, getState: (
dispatch(expandSearchRequest(type));
const params: Record<string, any> = {
q: value,
type,
offset,
};
let url = getState().search.next as string;
let params: Record<string, any> = {};
if (accountId) params.account_id = accountId;
// if no URL was extracted from the Link header,
// fall back on querying with the offset
if (!url) {
url = '/api/v2/search';
params = {
q: value,
type,
offset,
};
if (accountId) params.account_id = accountId;
}
api(getState).get('/api/v2/search', {
api(getState).get(url, {
params,
}).then(({ data }) => {
}).then(response => {
const data = response.data;
if (data.accounts) {
dispatch(importFetchedAccounts(data.accounts));
}
@ -144,7 +156,9 @@ const expandSearch = (type: SearchFilter) => (dispatch: AppDispatch, getState: (
dispatch(importFetchedStatuses(data.statuses));
}
dispatch(expandSearchSuccess(data, value, type));
const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(expandSearchSuccess(data, value, type, next ? next.uri : null));
dispatch(fetchRelationships(data.accounts.map((item: APIEntity) => item.id)));
}).catch(error => {
dispatch(expandSearchFail(error));
@ -156,11 +170,12 @@ const expandSearchRequest = (searchType: SearchFilter) => ({
searchType,
});
const expandSearchSuccess = (results: APIEntity[], searchTerm: string, searchType: SearchFilter) => ({
const expandSearchSuccess = (results: APIEntity[], searchTerm: string, searchType: SearchFilter, next: string | null) => ({
type: SEARCH_EXPAND_SUCCESS,
results,
searchTerm,
searchType,
next,
});
const expandSearchFail = (error: AxiosError) => ({

View file

@ -47,6 +47,7 @@ const ReducerRecord = ImmutableRecord({
results: ResultsRecord(),
filter: 'accounts' as SearchFilter,
accountId: null as string | null,
next: null as string | null,
});
type State = ReturnType<typeof ReducerRecord>;
@ -57,7 +58,7 @@ const toIds = (items: APIEntities = []) => {
return ImmutableOrderedSet(items.map(item => item.id));
};
const importResults = (state: State, results: APIEntity, searchTerm: string, searchType: SearchFilter) => {
const importResults = (state: State, results: APIEntity, searchTerm: string, searchType: SearchFilter, next: string | null) => {
return state.withMutations(state => {
if (state.value === searchTerm && state.filter === searchType) {
state.set('results', ResultsRecord({
@ -76,15 +77,17 @@ const importResults = (state: State, results: APIEntity, searchTerm: string, sea
}));
state.set('submitted', true);
state.set('next', next);
}
});
};
const paginateResults = (state: State, searchType: SearchFilter, results: APIEntity, searchTerm: string) => {
const paginateResults = (state: State, searchType: SearchFilter, results: APIEntity, searchTerm: string, next: string | null) => {
return state.withMutations(state => {
if (state.value === searchTerm) {
state.setIn(['results', `${searchType}HasMore`], results[searchType].length >= 20);
state.setIn(['results', `${searchType}Loaded`], true);
state.set('next', next);
state.updateIn(['results', searchType], items => {
const data = results[searchType];
// Hashtags are a list of maps. Others are IDs.
@ -129,13 +132,13 @@ export default function search(state = ReducerRecord(), action: AnyAction) {
case SEARCH_FETCH_REQUEST:
return handleSubmitted(state, action.value);
case SEARCH_FETCH_SUCCESS:
return importResults(state, action.results, action.searchTerm, action.searchType);
return importResults(state, action.results, action.searchTerm, action.searchType, action.next);
case SEARCH_FILTER_SET:
return state.set('filter', action.value);
case SEARCH_EXPAND_REQUEST:
return state.setIn(['results', `${action.searchType}Loaded`], false);
case SEARCH_EXPAND_SUCCESS:
return paginateResults(state, action.searchType, action.results, action.searchTerm);
return paginateResults(state, action.searchType, action.results, action.searchTerm, action.next);
case SEARCH_ACCOUNT_SET:
if (!action.accountId) return state.merge({
results: ResultsRecord(),