EntityStore: proper pagination support

This commit is contained in:
Alex Gleason
2022-12-04 17:53:56 -06:00
parent 52059f6f37
commit f7bfc40b70
4 changed files with 48 additions and 23 deletions

View File

@ -29,6 +29,10 @@ export const getNextLink = (response: AxiosResponse): string | undefined => {
return getLinks(response).refs.find(link => link.rel === 'next')?.uri;
};
export const getPrevLink = (response: AxiosResponse): string | undefined => {
return getLinks(response).refs.find(link => link.rel === 'prev')?.uri;
};
const getToken = (state: RootState, authType: string) => {
return authType === 'app' ? getAppToken(state) : getAccessToken(state);
};

View File

@ -1,4 +1,4 @@
import type { Entity } from './types';
import type { Entity, EntityListState } from './types';
const ENTITIES_IMPORT = 'ENTITIES_IMPORT' as const;
const ENTITIES_FETCH_REQUEST = 'ENTITIES_FETCH_REQUEST' as const;
@ -23,20 +23,22 @@ function entitiesFetchRequest(entityType: string, listKey?: string) {
};
}
function entitiesFetchSuccess(entities: Entity[], entityType: string, listKey?: string) {
function entitiesFetchSuccess(entities: Entity[], entityType: string, listKey?: string, newState?: EntityListState) {
return {
type: ENTITIES_FETCH_SUCCESS,
entityType,
entities,
listKey,
newState,
};
}
function entitiesFetchFail(entityType: string, listKey?: string) {
function entitiesFetchFail(entityType: string, listKey: string | undefined, error: any) {
return {
type: ENTITIES_FETCH_FAIL,
entityType,
listKey,
error,
};
}

View File

@ -1,12 +1,13 @@
import { getNextLink, getPrevLink } from 'soapbox/api';
import { useApi, useAppDispatch, useAppSelector } from 'soapbox/hooks';
import { importEntities } from '../actions';
import { entitiesFetchFail, entitiesFetchRequest, entitiesFetchSuccess } from '../actions';
import type { Entity } from '../types';
type EntityPath = [entityType: string, listKey: string]
function useEntities<TEntity extends Entity>(path: EntityPath, url: string) {
function useEntities<TEntity extends Entity>(path: EntityPath, endpoint: string) {
const api = useApi();
const dispatch = useAppDispatch();
@ -32,26 +33,38 @@ function useEntities<TEntity extends Entity>(path: EntityPath, url: string) {
const hasNextPage = Boolean(list?.state.next);
const hasPreviousPage = Boolean(list?.state.prev);
const fetchEntities = async() => {
const { data } = await api.get(url);
dispatch(importEntities(data, entityType, listKey));
};
const fetchNextPage = async() => {
const next = list?.state.next;
if (next) {
const { data } = await api.get(next);
dispatch(importEntities(data, entityType, listKey));
const fetchPage = async(url: string): Promise<void> => {
dispatch(entitiesFetchRequest(entityType, listKey));
try {
const response = await api.get(url);
dispatch(entitiesFetchSuccess(response.data, entityType, listKey, {
next: getNextLink(response),
prev: getPrevLink(response),
fetching: false,
error: null,
}));
} catch (error) {
dispatch(entitiesFetchFail(entityType, listKey, error));
}
};
const fetchPreviousPage = async() => {
const fetchEntities = async(): Promise<void> => {
await fetchPage(endpoint);
};
const fetchNextPage = async(): Promise<void> => {
const next = list?.state.next;
if (next) {
await fetchPage(next);
}
};
const fetchPreviousPage = async(): Promise<void> => {
const prev = list?.state.prev;
if (prev) {
const { data } = await api.get(prev);
dispatch(importEntities(data, entityType, listKey));
await fetchPage(prev);
}
};

View File

@ -9,7 +9,7 @@ import {
} from './actions';
import { createCache, createList, updateStore, updateList } from './utils';
import type { Entity, EntityCache } from './types';
import type { Entity, EntityCache, EntityListState } from './types';
enableMapSet();
@ -22,14 +22,19 @@ const importEntities = (
entityType: string,
entities: Entity[],
listKey?: string,
newState?: EntityListState,
): State => {
return produce(state, draft => {
const cache = draft.get(entityType) ?? createCache();
cache.store = updateStore(cache.store, entities);
if (listKey) {
const list = cache.lists.get(listKey) ?? createList();
cache.lists.set(listKey, updateList(list, entities));
let list = cache.lists.get(listKey) ?? createList();
list = updateList(list, entities);
if (newState) {
list.state = newState;
}
cache.lists.set(listKey, list);
}
return draft.set(entityType, cache);
@ -59,8 +64,9 @@ const setFetching = (
function reducer(state: Readonly<State> = new Map(), action: EntityAction): State {
switch (action.type) {
case ENTITIES_IMPORT:
case ENTITIES_FETCH_SUCCESS:
return importEntities(state, action.entityType, action.entities, action.listKey);
case ENTITIES_FETCH_SUCCESS:
return importEntities(state, action.entityType, action.entities, action.listKey, action.newState);
case ENTITIES_FETCH_REQUEST:
return setFetching(state, action.entityType, action.listKey, true);
case ENTITIES_FETCH_FAIL: