kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
Merge branch 'entity-pos' into 'develop'
EntityStore: allow customizing import position See merge request soapbox-pub/soapbox!2430environments/review-develop-3zknud/deployments/3137
commit
75045a5eff
|
@ -110,7 +110,7 @@ test('import entities with override', () => {
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
|
||||||
const action = entitiesFetchSuccess(entities, 'TestEntity', 'thingies', {
|
const action = entitiesFetchSuccess(entities, 'TestEntity', 'thingies', 'end', {
|
||||||
next: undefined,
|
next: undefined,
|
||||||
prev: undefined,
|
prev: undefined,
|
||||||
totalCount: 2,
|
totalCount: 2,
|
||||||
|
|
|
@ -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_IMPORT = 'ENTITIES_IMPORT' as const;
|
||||||
const ENTITIES_DELETE = 'ENTITIES_DELETE' 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;
|
const ENTITIES_INVALIDATE_LIST = 'ENTITIES_INVALIDATE_LIST' as const;
|
||||||
|
|
||||||
/** Action to import entities into the cache. */
|
/** 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 {
|
return {
|
||||||
type: ENTITIES_IMPORT,
|
type: ENTITIES_IMPORT,
|
||||||
entityType,
|
entityType,
|
||||||
entities,
|
entities,
|
||||||
listKey,
|
listKey,
|
||||||
|
pos,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +63,7 @@ function entitiesFetchSuccess(
|
||||||
entities: Entity[],
|
entities: Entity[],
|
||||||
entityType: string,
|
entityType: string,
|
||||||
listKey?: string,
|
listKey?: string,
|
||||||
|
pos?: ImportPosition,
|
||||||
newState?: EntityListState,
|
newState?: EntityListState,
|
||||||
overwrite = false,
|
overwrite = false,
|
||||||
) {
|
) {
|
||||||
|
@ -70,6 +72,7 @@ function entitiesFetchSuccess(
|
||||||
entityType,
|
entityType,
|
||||||
entities,
|
entities,
|
||||||
listKey,
|
listKey,
|
||||||
|
pos,
|
||||||
newState,
|
newState,
|
||||||
overwrite,
|
overwrite,
|
||||||
};
|
};
|
||||||
|
|
|
@ -30,7 +30,7 @@ function useCreateEntity<TEntity extends Entity = Entity, Data = unknown>(
|
||||||
const entity = schema.parse(result.data);
|
const entity = schema.parse(result.data);
|
||||||
|
|
||||||
// TODO: optimistic updating
|
// TODO: optimistic updating
|
||||||
dispatch(importEntities([entity], entityType, listKey));
|
dispatch(importEntities([entity], entityType, listKey, 'start'));
|
||||||
|
|
||||||
if (callbacks.onSuccess) {
|
if (callbacks.onSuccess) {
|
||||||
callbacks.onSuccess(entity);
|
callbacks.onSuccess(entity);
|
||||||
|
|
|
@ -54,7 +54,7 @@ function useEntities<TEntity extends Entity>(
|
||||||
const next = useListState(path, 'next');
|
const next = useListState(path, 'next');
|
||||||
const prev = useListState(path, 'prev');
|
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.
|
// Get `isFetching` state from the store again to prevent race conditions.
|
||||||
const isFetching = selectListState(getState(), path, 'fetching');
|
const isFetching = selectListState(getState(), path, 'fetching');
|
||||||
if (isFetching) return;
|
if (isFetching) return;
|
||||||
|
@ -67,7 +67,7 @@ function useEntities<TEntity extends Entity>(
|
||||||
const parsedCount = realNumberSchema.safeParse(response.headers['x-total-count']);
|
const parsedCount = realNumberSchema.safeParse(response.headers['x-total-count']);
|
||||||
const totalCount = parsedCount.success ? parsedCount.data : undefined;
|
const totalCount = parsedCount.success ? parsedCount.data : undefined;
|
||||||
|
|
||||||
dispatch(entitiesFetchSuccess(entities, entityType, listKey, {
|
dispatch(entitiesFetchSuccess(entities, entityType, listKey, pos, {
|
||||||
next: getNextLink(response),
|
next: getNextLink(response),
|
||||||
prev: getPrevLink(response),
|
prev: getPrevLink(response),
|
||||||
totalCount: Number(totalCount) >= entities.length ? totalCount : undefined,
|
totalCount: Number(totalCount) >= entities.length ? totalCount : undefined,
|
||||||
|
@ -83,18 +83,18 @@ function useEntities<TEntity extends Entity>(
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchEntities = async(): Promise<void> => {
|
const fetchEntities = async(): Promise<void> => {
|
||||||
await fetchPage(entityFn, true);
|
await fetchPage(entityFn, 'end', true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchNextPage = async(): Promise<void> => {
|
const fetchNextPage = async(): Promise<void> => {
|
||||||
if (next) {
|
if (next) {
|
||||||
await fetchPage(() => api.get(next));
|
await fetchPage(() => api.get(next), 'end');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchPreviousPage = async(): Promise<void> => {
|
const fetchPreviousPage = async(): Promise<void> => {
|
||||||
if (prev) {
|
if (prev) {
|
||||||
await fetchPage(() => api.get(prev));
|
await fetchPage(() => api.get(prev), 'start');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {
|
||||||
import { createCache, createList, updateStore, updateList } from './utils';
|
import { createCache, createList, updateStore, updateList } from './utils';
|
||||||
|
|
||||||
import type { DeleteEntitiesOpts } from './actions';
|
import type { DeleteEntitiesOpts } from './actions';
|
||||||
import type { Entity, EntityCache, EntityListState } from './types';
|
import type { Entity, EntityCache, EntityListState, ImportPosition } from './types';
|
||||||
|
|
||||||
enableMapSet();
|
enableMapSet();
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ const importEntities = (
|
||||||
entityType: string,
|
entityType: string,
|
||||||
entities: Entity[],
|
entities: Entity[],
|
||||||
listKey?: string,
|
listKey?: string,
|
||||||
|
pos?: ImportPosition,
|
||||||
newState?: EntityListState,
|
newState?: EntityListState,
|
||||||
overwrite = false,
|
overwrite = false,
|
||||||
): State => {
|
): State => {
|
||||||
|
@ -43,7 +44,7 @@ const importEntities = (
|
||||||
list.ids = new Set();
|
list.ids = new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
list = updateList(list, entities);
|
list = updateList(list, entities, pos);
|
||||||
|
|
||||||
if (newState) {
|
if (newState) {
|
||||||
list.state = newState;
|
list.state = newState;
|
||||||
|
@ -159,7 +160,7 @@ const invalidateEntityList = (state: State, entityType: string, listKey: string)
|
||||||
function reducer(state: Readonly<State> = {}, action: EntityAction): State {
|
function reducer(state: Readonly<State> = {}, action: EntityAction): State {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ENTITIES_IMPORT:
|
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:
|
case ENTITIES_DELETE:
|
||||||
return deleteEntities(state, action.entityType, action.ids, action.opts);
|
return deleteEntities(state, action.entityType, action.ids, action.opts);
|
||||||
case ENTITIES_DISMISS:
|
case ENTITIES_DISMISS:
|
||||||
|
@ -167,7 +168,7 @@ function reducer(state: Readonly<State> = {}, action: EntityAction): State {
|
||||||
case ENTITIES_INCREMENT:
|
case ENTITIES_INCREMENT:
|
||||||
return incrementEntities(state, action.entityType, action.listKey, action.diff);
|
return incrementEntities(state, action.entityType, action.listKey, action.diff);
|
||||||
case ENTITIES_FETCH_SUCCESS:
|
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:
|
case ENTITIES_FETCH_REQUEST:
|
||||||
return setFetching(state, action.entityType, action.listKey, true);
|
return setFetching(state, action.entityType, action.listKey, true);
|
||||||
case ENTITIES_FETCH_FAIL:
|
case ENTITIES_FETCH_FAIL:
|
||||||
|
|
|
@ -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 {
|
export {
|
||||||
Entity,
|
Entity,
|
||||||
EntityStore,
|
EntityStore,
|
||||||
EntityList,
|
EntityList,
|
||||||
EntityListState,
|
EntityListState,
|
||||||
EntityCache,
|
EntityCache,
|
||||||
|
ImportPosition,
|
||||||
};
|
};
|
|
@ -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. */
|
/** Insert the entities into the store. */
|
||||||
const updateStore = (store: EntityStore, entities: Entity[]): EntityStore => {
|
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. */
|
/** 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 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') {
|
if (typeof list.state.totalCount === 'number') {
|
||||||
const sizeDiff = ids.size - list.ids.size;
|
const sizeDiff = ids.size - list.ids.size;
|
||||||
|
|
Ładowanie…
Reference in New Issue