EntityStore: add support for X-Total-Count from the API
This commit is contained in:
parent
1eed61c386
commit
a256665aad
|
@ -42,7 +42,8 @@ test('import entities into a list', () => {
|
||||||
const cache = result.TestEntity as EntityCache<TestEntity>;
|
const cache = result.TestEntity as EntityCache<TestEntity>;
|
||||||
|
|
||||||
expect(cache.store['2']!.msg).toBe('benis');
|
expect(cache.store['2']!.msg).toBe('benis');
|
||||||
expect(cache.lists.thingies?.ids.size).toBe(3);
|
expect(cache.lists.thingies!.ids.size).toBe(3);
|
||||||
|
expect(cache.lists.thingies!.state.totalCount).toBe(3);
|
||||||
|
|
||||||
// Now try adding an additional item.
|
// Now try adding an additional item.
|
||||||
const entities2: TestEntity[] = [
|
const entities2: TestEntity[] = [
|
||||||
|
@ -54,7 +55,8 @@ test('import entities into a list', () => {
|
||||||
const cache2 = result2.TestEntity as EntityCache<TestEntity>;
|
const cache2 = result2.TestEntity as EntityCache<TestEntity>;
|
||||||
|
|
||||||
expect(cache2.store['4']!.msg).toBe('hehe');
|
expect(cache2.store['4']!.msg).toBe('hehe');
|
||||||
expect(cache2.lists.thingies?.ids.size).toBe(4);
|
expect(cache2.lists.thingies!.ids.size).toBe(4);
|
||||||
|
expect(cache2.lists.thingies!.state.totalCount).toBe(4);
|
||||||
|
|
||||||
// Finally, update an item.
|
// Finally, update an item.
|
||||||
const entities3: TestEntity[] = [
|
const entities3: TestEntity[] = [
|
||||||
|
@ -66,7 +68,8 @@ test('import entities into a list', () => {
|
||||||
const cache3 = result3.TestEntity as EntityCache<TestEntity>;
|
const cache3 = result3.TestEntity as EntityCache<TestEntity>;
|
||||||
|
|
||||||
expect(cache3.store['2']!.msg).toBe('yolofam');
|
expect(cache3.store['2']!.msg).toBe('yolofam');
|
||||||
expect(cache3.lists.thingies?.ids.size).toBe(4); // unchanged
|
expect(cache3.lists.thingies!.ids.size).toBe(4); // unchanged
|
||||||
|
expect(cache3.lists.thingies!.state.totalCount).toBe(4);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('fetching updates the list state', () => {
|
test('fetching updates the list state', () => {
|
||||||
|
@ -92,7 +95,7 @@ test('deleting items', () => {
|
||||||
lists: {
|
lists: {
|
||||||
'': {
|
'': {
|
||||||
ids: new Set(['1', '2', '3']),
|
ids: new Set(['1', '2', '3']),
|
||||||
state: createListState(),
|
state: { ...createListState(), totalCount: 3 },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -103,6 +106,7 @@ test('deleting items', () => {
|
||||||
|
|
||||||
expect(result.TestEntity!.store).toMatchObject({ '2': { id: '2' } });
|
expect(result.TestEntity!.store).toMatchObject({ '2': { id: '2' } });
|
||||||
expect([...result.TestEntity!.lists['']!.ids]).toEqual(['2']);
|
expect([...result.TestEntity!.lists['']!.ids]).toEqual(['2']);
|
||||||
|
expect(result.TestEntity!.lists['']!.state.totalCount).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('dismiss items', () => {
|
test('dismiss items', () => {
|
||||||
|
@ -112,7 +116,7 @@ test('dismiss items', () => {
|
||||||
lists: {
|
lists: {
|
||||||
'yolo': {
|
'yolo': {
|
||||||
ids: new Set(['1', '2', '3']),
|
ids: new Set(['1', '2', '3']),
|
||||||
state: createListState(),
|
state: { ...createListState(), totalCount: 3 },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -123,4 +127,5 @@ test('dismiss items', () => {
|
||||||
|
|
||||||
expect(result.TestEntity!.store).toMatchObject(state.TestEntity!.store);
|
expect(result.TestEntity!.store).toMatchObject(state.TestEntity!.store);
|
||||||
expect([...result.TestEntity!.lists.yolo!.ids]).toEqual(['2']);
|
expect([...result.TestEntity!.lists.yolo!.ids]).toEqual(['2']);
|
||||||
|
expect(result.TestEntity!.lists.yolo!.state.totalCount).toBe(1);
|
||||||
});
|
});
|
|
@ -47,6 +47,7 @@ function useEntities<TEntity extends Entity>(
|
||||||
const lastFetchedAt = useListState(path, 'lastFetchedAt');
|
const lastFetchedAt = useListState(path, 'lastFetchedAt');
|
||||||
const isFetched = useListState(path, 'fetched');
|
const isFetched = useListState(path, 'fetched');
|
||||||
const isError = !!useListState(path, 'error');
|
const isError = !!useListState(path, 'error');
|
||||||
|
const totalCount = useListState(path, 'totalCount');
|
||||||
|
|
||||||
const next = useListState(path, 'next');
|
const next = useListState(path, 'next');
|
||||||
const prev = useListState(path, 'prev');
|
const prev = useListState(path, 'prev');
|
||||||
|
@ -61,10 +62,12 @@ function useEntities<TEntity extends Entity>(
|
||||||
const response = await api.get(url);
|
const response = await api.get(url);
|
||||||
const schema = opts.schema || z.custom<TEntity>();
|
const schema = opts.schema || z.custom<TEntity>();
|
||||||
const entities = filteredArray(schema).parse(response.data);
|
const entities = filteredArray(schema).parse(response.data);
|
||||||
|
const numItems = (selectList(getState(), path)?.ids.size || 0) + entities.length;
|
||||||
|
|
||||||
dispatch(entitiesFetchSuccess(entities, entityType, listKey, {
|
dispatch(entitiesFetchSuccess(entities, entityType, listKey, {
|
||||||
next: getNextLink(response),
|
next: getNextLink(response),
|
||||||
prev: getPrevLink(response),
|
prev: getPrevLink(response),
|
||||||
|
totalCount: Number(response.headers['x-total-count'] ?? numItems) || 0,
|
||||||
fetching: false,
|
fetching: false,
|
||||||
fetched: true,
|
fetched: true,
|
||||||
error: null,
|
error: null,
|
||||||
|
@ -108,6 +111,7 @@ function useEntities<TEntity extends Entity>(
|
||||||
fetchPreviousPage,
|
fetchPreviousPage,
|
||||||
hasNextPage: !!next,
|
hasNextPage: !!next,
|
||||||
hasPreviousPage: !!prev,
|
hasPreviousPage: !!prev,
|
||||||
|
totalCount,
|
||||||
isError,
|
isError,
|
||||||
isFetched,
|
isFetched,
|
||||||
isFetching,
|
isFetching,
|
||||||
|
|
|
@ -60,7 +60,10 @@ const deleteEntities = (
|
||||||
|
|
||||||
if (!opts?.preserveLists) {
|
if (!opts?.preserveLists) {
|
||||||
for (const list of Object.values(cache.lists)) {
|
for (const list of Object.values(cache.lists)) {
|
||||||
list?.ids.delete(id);
|
if (list) {
|
||||||
|
list.ids.delete(id);
|
||||||
|
list.state.totalCount--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,6 +85,7 @@ const dismissEntities = (
|
||||||
if (list) {
|
if (list) {
|
||||||
for (const id of ids) {
|
for (const id of ids) {
|
||||||
list.ids.delete(id);
|
list.ids.delete(id);
|
||||||
|
list.state.totalCount--;
|
||||||
}
|
}
|
||||||
|
|
||||||
draft[entityType] = cache;
|
draft[entityType] = cache;
|
||||||
|
|
|
@ -23,6 +23,8 @@ interface EntityListState {
|
||||||
next: string | undefined
|
next: string | undefined
|
||||||
/** Previous URL for pagination, if any. */
|
/** Previous URL for pagination, if any. */
|
||||||
prev: string | undefined
|
prev: string | undefined
|
||||||
|
/** Total number of items according to the API. */
|
||||||
|
totalCount: number
|
||||||
/** Error returned from the API, if any. */
|
/** Error returned from the API, if any. */
|
||||||
error: any
|
error: any
|
||||||
/** Whether data has already been fetched */
|
/** Whether data has already been fetched */
|
||||||
|
|
|
@ -11,9 +11,14 @@ 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[]): EntityList => {
|
||||||
const newIds = entities.map(entity => entity.id);
|
const newIds = entities.map(entity => entity.id);
|
||||||
|
const ids = new Set([...Array.from(list.ids), ...newIds]);
|
||||||
|
|
||||||
|
const sizeDiff = ids.size - list.ids.size;
|
||||||
|
list.state.totalCount += sizeDiff;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...list,
|
...list,
|
||||||
ids: new Set([...Array.from(list.ids), ...newIds]),
|
ids,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -33,6 +38,7 @@ const createList = (): EntityList => ({
|
||||||
const createListState = (): EntityListState => ({
|
const createListState = (): EntityListState => ({
|
||||||
next: undefined,
|
next: undefined,
|
||||||
prev: undefined,
|
prev: undefined,
|
||||||
|
totalCount: 0,
|
||||||
error: null,
|
error: null,
|
||||||
fetched: false,
|
fetched: false,
|
||||||
fetching: false,
|
fetching: false,
|
||||||
|
|
Loading…
Reference in New Issue