Merge branch 'entity-pos' into 'develop'

EntityStore: allow customizing import position

See merge request soapbox-pub/soapbox!2430
environments/review-develop-3zknud/deployments/3137
Alex Gleason 2023-04-11 15:51:16 +00:00
commit 75045a5eff
7 zmienionych plików z 25 dodań i 16 usunięć

Wyświetl plik

@ -110,7 +110,7 @@ test('import entities with override', () => {
const now = new Date();
const action = entitiesFetchSuccess(entities, 'TestEntity', 'thingies', {
const action = entitiesFetchSuccess(entities, 'TestEntity', 'thingies', 'end', {
next: undefined,
prev: undefined,
totalCount: 2,

Wyświetl plik

@ -1,4 +1,4 @@
import type { Entity, EntityListState } from './types';
import type { Entity, EntityListState, ImportPosition } from './types';
const ENTITIES_IMPORT = 'ENTITIES_IMPORT' as const;
const ENTITIES_DELETE = 'ENTITIES_DELETE' as const;
@ -10,12 +10,13 @@ const ENTITIES_FETCH_FAIL = 'ENTITIES_FETCH_FAIL' as const;
const ENTITIES_INVALIDATE_LIST = 'ENTITIES_INVALIDATE_LIST' as const;
/** Action to import entities into the cache. */
function importEntities(entities: Entity[], entityType: string, listKey?: string) {
function importEntities(entities: Entity[], entityType: string, listKey?: string, pos?: ImportPosition) {
return {
type: ENTITIES_IMPORT,
entityType,
entities,
listKey,
pos,
};
}
@ -62,6 +63,7 @@ function entitiesFetchSuccess(
entities: Entity[],
entityType: string,
listKey?: string,
pos?: ImportPosition,
newState?: EntityListState,
overwrite = false,
) {
@ -70,6 +72,7 @@ function entitiesFetchSuccess(
entityType,
entities,
listKey,
pos,
newState,
overwrite,
};

Wyświetl plik

@ -30,7 +30,7 @@ function useCreateEntity<TEntity extends Entity = Entity, Data = unknown>(
const entity = schema.parse(result.data);
// TODO: optimistic updating
dispatch(importEntities([entity], entityType, listKey));
dispatch(importEntities([entity], entityType, listKey, 'start'));
if (callbacks.onSuccess) {
callbacks.onSuccess(entity);

Wyświetl plik

@ -54,7 +54,7 @@ function useEntities<TEntity extends Entity>(
const next = useListState(path, 'next');
const prev = useListState(path, 'prev');
const fetchPage = async(req: EntityFn<void>, overwrite = false): Promise<void> => {
const fetchPage = async(req: EntityFn<void>, pos: 'start' | 'end', overwrite = false): Promise<void> => {
// Get `isFetching` state from the store again to prevent race conditions.
const isFetching = selectListState(getState(), path, 'fetching');
if (isFetching) return;
@ -67,7 +67,7 @@ function useEntities<TEntity extends Entity>(
const parsedCount = realNumberSchema.safeParse(response.headers['x-total-count']);
const totalCount = parsedCount.success ? parsedCount.data : undefined;
dispatch(entitiesFetchSuccess(entities, entityType, listKey, {
dispatch(entitiesFetchSuccess(entities, entityType, listKey, pos, {
next: getNextLink(response),
prev: getPrevLink(response),
totalCount: Number(totalCount) >= entities.length ? totalCount : undefined,
@ -83,18 +83,18 @@ function useEntities<TEntity extends Entity>(
};
const fetchEntities = async(): Promise<void> => {
await fetchPage(entityFn, true);
await fetchPage(entityFn, 'end', true);
};
const fetchNextPage = async(): Promise<void> => {
if (next) {
await fetchPage(() => api.get(next));
await fetchPage(() => api.get(next), 'end');
}
};
const fetchPreviousPage = async(): Promise<void> => {
if (prev) {
await fetchPage(() => api.get(prev));
await fetchPage(() => api.get(prev), 'start');
}
};

Wyświetl plik

@ -14,7 +14,7 @@ import {
import { createCache, createList, updateStore, updateList } from './utils';
import type { DeleteEntitiesOpts } from './actions';
import type { Entity, EntityCache, EntityListState } from './types';
import type { Entity, EntityCache, EntityListState, ImportPosition } from './types';
enableMapSet();
@ -29,6 +29,7 @@ const importEntities = (
entityType: string,
entities: Entity[],
listKey?: string,
pos?: ImportPosition,
newState?: EntityListState,
overwrite = false,
): State => {
@ -43,7 +44,7 @@ const importEntities = (
list.ids = new Set();
}
list = updateList(list, entities);
list = updateList(list, entities, pos);
if (newState) {
list.state = newState;
@ -159,7 +160,7 @@ const invalidateEntityList = (state: State, entityType: string, listKey: string)
function reducer(state: Readonly<State> = {}, action: EntityAction): State {
switch (action.type) {
case ENTITIES_IMPORT:
return importEntities(state, action.entityType, action.entities, action.listKey);
return importEntities(state, action.entityType, action.entities, action.listKey, action.pos);
case ENTITIES_DELETE:
return deleteEntities(state, action.entityType, action.ids, action.opts);
case ENTITIES_DISMISS:
@ -167,7 +168,7 @@ function reducer(state: Readonly<State> = {}, action: EntityAction): State {
case ENTITIES_INCREMENT:
return incrementEntities(state, action.entityType, action.listKey, action.diff);
case ENTITIES_FETCH_SUCCESS:
return importEntities(state, action.entityType, action.entities, action.listKey, action.newState, action.overwrite);
return importEntities(state, action.entityType, action.entities, action.listKey, action.pos, action.newState, action.overwrite);
case ENTITIES_FETCH_REQUEST:
return setFetching(state, action.entityType, action.listKey, true);
case ENTITIES_FETCH_FAIL:

Wyświetl plik

@ -47,10 +47,14 @@ interface EntityCache<TEntity extends Entity = Entity> {
}
}
/** Whether to import items at the start or end of the list. */
type ImportPosition = 'start' | 'end'
export {
Entity,
EntityStore,
EntityList,
EntityListState,
EntityCache,
ImportPosition,
};

Wyświetl plik

@ -1,4 +1,4 @@
import type { Entity, EntityStore, EntityList, EntityCache, EntityListState } from './types';
import type { Entity, EntityStore, EntityList, EntityCache, EntityListState, ImportPosition } from './types';
/** Insert the entities into the store. */
const updateStore = (store: EntityStore, entities: Entity[]): EntityStore => {
@ -9,9 +9,10 @@ const updateStore = (store: EntityStore, entities: Entity[]): EntityStore => {
};
/** Update the list with new entity IDs. */
const updateList = (list: EntityList, entities: Entity[]): EntityList => {
const updateList = (list: EntityList, entities: Entity[], pos: ImportPosition = 'end'): EntityList => {
const newIds = entities.map(entity => entity.id);
const ids = new Set([...newIds, ...Array.from(list.ids)]);
const oldIds = Array.from(list.ids);
const ids = new Set(pos === 'start' ? [...newIds, ...oldIds] : [...oldIds, ...newIds]);
if (typeof list.state.totalCount === 'number') {
const sizeDiff = ids.size - list.ids.size;