kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
				
				
				
			EntityStore: allow deleting entities
							rodzic
							
								
									6ac57910bf
								
							
						
					
					
						commit
						8f8807eb76
					
				|  | @ -1,5 +1,6 @@ | |||
| import { entitiesFetchFail, entitiesFetchRequest, importEntities } from '../actions'; | ||||
| import reducer from '../reducer'; | ||||
| import { deleteEntities, entitiesFetchFail, entitiesFetchRequest, importEntities } from '../actions'; | ||||
| import reducer, { State } from '../reducer'; | ||||
| import { createListState } from '../utils'; | ||||
| 
 | ||||
| import type { EntityCache } from '../types'; | ||||
| 
 | ||||
|  | @ -76,4 +77,24 @@ test('failure adds the error to the state', () => { | |||
|   const result = reducer(undefined, action); | ||||
| 
 | ||||
|   expect(result.TestEntity!.lists.thingies!.state.error).toBe(error); | ||||
| }); | ||||
| 
 | ||||
| test('deleting items', () => { | ||||
|   const state: State = { | ||||
|     TestEntity: { | ||||
|       store: { '1': { id: '1' }, '2': { id: '2' }, '3': { id: '3' } }, | ||||
|       lists: { | ||||
|         '': { | ||||
|           ids: new Set(['1', '2', '3']), | ||||
|           state: createListState(), | ||||
|         }, | ||||
|       }, | ||||
|     }, | ||||
|   }; | ||||
| 
 | ||||
|   const action = deleteEntities(['3', '1'], 'TestEntity'); | ||||
|   const result = reducer(state, action); | ||||
| 
 | ||||
|   expect(result.TestEntity!.store).toMatchObject({ '2': { id: '2' } }); | ||||
|   expect([...result.TestEntity!.lists['']!.ids]).toEqual(['2']); | ||||
| }); | ||||
|  | @ -1,6 +1,7 @@ | |||
| import type { Entity, EntityListState } from './types'; | ||||
| 
 | ||||
| const ENTITIES_IMPORT = 'ENTITIES_IMPORT' as const; | ||||
| const ENTITIES_DELETE = 'ENTITIES_DELETE' as const; | ||||
| const ENTITIES_FETCH_REQUEST = 'ENTITIES_FETCH_REQUEST' as const; | ||||
| const ENTITIES_FETCH_SUCCESS = 'ENTITIES_FETCH_SUCCESS' as const; | ||||
| const ENTITIES_FETCH_FAIL = 'ENTITIES_FETCH_FAIL' as const; | ||||
|  | @ -15,6 +16,14 @@ function importEntities(entities: Entity[], entityType: string, listKey?: string | |||
|   }; | ||||
| } | ||||
| 
 | ||||
| function deleteEntities(ids: Iterable<string>, entityType: string) { | ||||
|   return { | ||||
|     type: ENTITIES_DELETE, | ||||
|     ids, | ||||
|     entityType, | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| function entitiesFetchRequest(entityType: string, listKey?: string) { | ||||
|   return { | ||||
|     type: ENTITIES_FETCH_REQUEST, | ||||
|  | @ -45,16 +54,19 @@ function entitiesFetchFail(entityType: string, listKey: string | undefined, erro | |||
| /** Any action pertaining to entities. */ | ||||
| type EntityAction = | ||||
|   ReturnType<typeof importEntities> | ||||
|   | ReturnType<typeof deleteEntities> | ||||
|   | ReturnType<typeof entitiesFetchRequest> | ||||
|   | ReturnType<typeof entitiesFetchSuccess> | ||||
|   | ReturnType<typeof entitiesFetchFail>; | ||||
| 
 | ||||
| export { | ||||
|   ENTITIES_IMPORT, | ||||
|   ENTITIES_DELETE, | ||||
|   ENTITIES_FETCH_REQUEST, | ||||
|   ENTITIES_FETCH_SUCCESS, | ||||
|   ENTITIES_FETCH_FAIL, | ||||
|   importEntities, | ||||
|   deleteEntities, | ||||
|   entitiesFetchRequest, | ||||
|   entitiesFetchSuccess, | ||||
|   entitiesFetchFail, | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ import produce, { enableMapSet } from 'immer'; | |||
| 
 | ||||
| import { | ||||
|   ENTITIES_IMPORT, | ||||
|   ENTITIES_DELETE, | ||||
|   ENTITIES_FETCH_REQUEST, | ||||
|   ENTITIES_FETCH_SUCCESS, | ||||
|   ENTITIES_FETCH_FAIL, | ||||
|  | @ -43,6 +44,26 @@ const importEntities = ( | |||
|   }); | ||||
| }; | ||||
| 
 | ||||
| const deleteEntities = ( | ||||
|   state: State, | ||||
|   entityType: string, | ||||
|   ids: Iterable<string>, | ||||
| ) => { | ||||
|   return produce(state, draft => { | ||||
|     const cache = draft[entityType] ?? createCache(); | ||||
| 
 | ||||
|     for (const id of ids) { | ||||
|       delete cache.store[id]; | ||||
| 
 | ||||
|       for (const list of Object.values(cache.lists)) { | ||||
|         list?.ids.delete(id); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     draft[entityType] = cache; | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| const setFetching = ( | ||||
|   state: State, | ||||
|   entityType: string, | ||||
|  | @ -69,6 +90,8 @@ function reducer(state: Readonly<State> = {}, action: EntityAction): State { | |||
|   switch (action.type) { | ||||
|     case ENTITIES_IMPORT: | ||||
|       return importEntities(state, action.entityType, action.entities, action.listKey); | ||||
|     case ENTITIES_DELETE: | ||||
|       return deleteEntities(state, action.entityType, action.ids); | ||||
|     case ENTITIES_FETCH_SUCCESS: | ||||
|       return importEntities(state, action.entityType, action.entities, action.listKey, action.newState); | ||||
|     case ENTITIES_FETCH_REQUEST: | ||||
|  | @ -80,4 +103,5 @@ function reducer(state: Readonly<State> = {}, action: EntityAction): State { | |||
|   } | ||||
| } | ||||
| 
 | ||||
| export default reducer; | ||||
| export default reducer; | ||||
| export type { State }; | ||||
|  | @ -1,4 +1,4 @@ | |||
| import type { Entity, EntityStore, EntityList, EntityCache } from './types'; | ||||
| import type { Entity, EntityStore, EntityList, EntityCache, EntityListState } from './types'; | ||||
| 
 | ||||
| /** Insert the entities into the store. */ | ||||
| const updateStore = (store: EntityStore, entities: Entity[]): EntityStore => { | ||||
|  | @ -26,13 +26,16 @@ const createCache = (): EntityCache => ({ | |||
| /** Create an empty entity list. */ | ||||
| const createList = (): EntityList => ({ | ||||
|   ids: new Set(), | ||||
|   state: { | ||||
|     next: undefined, | ||||
|     prev: undefined, | ||||
|     fetching: false, | ||||
|     error: null, | ||||
|     lastFetchedAt: undefined, | ||||
|   }, | ||||
|   state: createListState(), | ||||
| }); | ||||
| 
 | ||||
| /** Create an empty entity list state. */ | ||||
| const createListState = (): EntityListState => ({ | ||||
|   next: undefined, | ||||
|   prev: undefined, | ||||
|   fetching: false, | ||||
|   error: null, | ||||
|   lastFetchedAt: undefined, | ||||
| }); | ||||
| 
 | ||||
| export { | ||||
|  | @ -40,4 +43,5 @@ export { | |||
|   updateList, | ||||
|   createCache, | ||||
|   createList, | ||||
|   createListState, | ||||
| }; | ||||
|  | @ -6,7 +6,7 @@ | |||
|     "strict": true, | ||||
|     "module": "es2022", | ||||
|     "lib": ["es2019", "es6", "dom", "webworker"], | ||||
|     "target": "es5", | ||||
|     "target": "es2015", | ||||
|     "jsx": "react", | ||||
|     "allowJs": true, | ||||
|     "moduleResolution": "node", | ||||
|  |  | |||
		Ładowanie…
	
		Reference in New Issue
	
	 Alex Gleason
						Alex Gleason