feat: 🎸 useNewsDetails(child, news)
Returns a richer version of a news item
This commit is contained in:
parent
1826b80d4b
commit
5d4f7515cd
|
@ -19,7 +19,7 @@
|
||||||
"publish-package": "npm publish --access public"
|
"publish-package": "npm publish --access public"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@skolplattformen/embedded-api": "^0.20.0",
|
"@skolplattformen/embedded-api": "^0.22.0",
|
||||||
"react-redux": "^7.2.2",
|
"react-redux": "^7.2.2",
|
||||||
"redux": "^4.0.5"
|
"redux": "^4.0.5"
|
||||||
},
|
},
|
||||||
|
|
|
@ -34,6 +34,7 @@ const hook = <T>(
|
||||||
selector: StoreSelector<T>,
|
selector: StoreSelector<T>,
|
||||||
apiCaller: (api: Api) => ApiCall<T>,
|
apiCaller: (api: Api) => ApiCall<T>,
|
||||||
): EntityHookResult<T> => {
|
): EntityHookResult<T> => {
|
||||||
|
const getState = (): EntityStoreRootState => store.getState() as unknown as EntityStoreRootState
|
||||||
const select = (storeState: EntityStoreRootState) => {
|
const select = (storeState: EntityStoreRootState) => {
|
||||||
const stateMap = selector(storeState) || {}
|
const stateMap = selector(storeState) || {}
|
||||||
const state = stateMap[key] || { status: 'pending', data: defaultValue }
|
const state = stateMap[key] || { status: 'pending', data: defaultValue }
|
||||||
|
@ -42,7 +43,7 @@ const hook = <T>(
|
||||||
const {
|
const {
|
||||||
api, isLoggedIn, reporter, storage,
|
api, isLoggedIn, reporter, storage,
|
||||||
} = useApi()
|
} = useApi()
|
||||||
const initialState = select(store.getState() as EntityStoreRootState)
|
const initialState = select(getState())
|
||||||
const [state, setState] = useState(initialState)
|
const [state, setState] = useState(initialState)
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
|
|
||||||
|
@ -70,7 +71,7 @@ const hook = <T>(
|
||||||
useEffect(() => { load() }, [isLoggedIn])
|
useEffect(() => { load() }, [isLoggedIn])
|
||||||
|
|
||||||
const listener = () => {
|
const listener = () => {
|
||||||
const newState = select(store.getState() as EntityStoreRootState)
|
const newState = select(getState())
|
||||||
if (newState.status !== state.status
|
if (newState.status !== state.status
|
||||||
|| newState.data !== state.data
|
|| newState.data !== state.data
|
||||||
|| newState.error !== state.error) {
|
|| newState.error !== state.error) {
|
||||||
|
@ -138,7 +139,6 @@ export const useNewsDetails = (child: Child, news: NewsItem) => hook<NewsItem>(
|
||||||
(api) => () => api.getNewsDetails(child, news),
|
(api) => () => api.getNewsDetails(child, news),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
export const useNotifications = (child: Child) => hook<Notification[]>(
|
export const useNotifications = (child: Child) => hook<Notification[]>(
|
||||||
'NOTIFICATIONS',
|
'NOTIFICATIONS',
|
||||||
`notifications_${child.id}`,
|
`notifications_${child.id}`,
|
||||||
|
|
30
src/types.ts
30
src/types.ts
|
@ -43,22 +43,22 @@ export interface ExtraActionProps<T> {
|
||||||
saveToCache?: (value: string) => Promise<void>
|
saveToCache?: (value: string) => Promise<void>
|
||||||
}
|
}
|
||||||
export type EntityActionType = 'GET_FROM_API'
|
export type EntityActionType = 'GET_FROM_API'
|
||||||
| 'RESULT_FROM_API'
|
| 'RESULT_FROM_API'
|
||||||
| 'API_ERROR'
|
| 'API_ERROR'
|
||||||
| 'GET_FROM_CACHE'
|
| 'GET_FROM_CACHE'
|
||||||
| 'RESULT_FROM_CACHE'
|
| 'RESULT_FROM_CACHE'
|
||||||
| 'STORE_IN_CACHE'
|
| 'STORE_IN_CACHE'
|
||||||
| 'CLEAR'
|
| 'CLEAR'
|
||||||
export type EntityName = 'USER'
|
export type EntityName = 'USER'
|
||||||
| 'CHILDREN'
|
| 'CHILDREN'
|
||||||
| 'CALENDAR'
|
| 'CALENDAR'
|
||||||
| 'CLASSMATES'
|
| 'CLASSMATES'
|
||||||
| 'MENU'
|
| 'MENU'
|
||||||
| 'NEWS'
|
| 'NEWS'
|
||||||
| 'NEWS_DETAILS'
|
| 'NEWS_DETAILS'
|
||||||
| 'NOTIFICATIONS'
|
| 'NOTIFICATIONS'
|
||||||
| 'SCHEDULE'
|
| 'SCHEDULE'
|
||||||
| 'ALL'
|
| 'ALL'
|
||||||
export interface EntityAction<T> extends Action<EntityActionType> {
|
export interface EntityAction<T> extends Action<EntityActionType> {
|
||||||
entity: EntityName
|
entity: EntityName
|
||||||
data?: T
|
data?: T
|
||||||
|
|
|
@ -13,6 +13,7 @@ describe('useNewsDetails(child, newsItem)', () => {
|
||||||
let api
|
let api
|
||||||
let storage
|
let storage
|
||||||
let response
|
let response
|
||||||
|
let cached
|
||||||
let child
|
let child
|
||||||
let newsItem
|
let newsItem
|
||||||
const wrapper = ({ children }) => (
|
const wrapper = ({ children }) => (
|
||||||
|
@ -25,7 +26,8 @@ describe('useNewsDetails(child, newsItem)', () => {
|
||||||
</ApiProvider>
|
</ApiProvider>
|
||||||
)
|
)
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
response = { id: '1337', modified: 'now' }
|
cached = { id: '1337', modified: 'yesterday', body: 'rich and old' }
|
||||||
|
response = { id: '1337', modified: 'now', body: 'rich and new' }
|
||||||
api = init()
|
api = init()
|
||||||
api.getNewsDetails.mockImplementation(() => (
|
api.getNewsDetails.mockImplementation(() => (
|
||||||
new Promise((res) => {
|
new Promise((res) => {
|
||||||
|
@ -33,10 +35,10 @@ describe('useNewsDetails(child, newsItem)', () => {
|
||||||
})
|
})
|
||||||
))
|
))
|
||||||
storage = createStorage({
|
storage = createStorage({
|
||||||
news_details_1337: { id: '1337', modified: 'yesterday' },
|
news_details_1337: { ...cached },
|
||||||
}, 2)
|
}, 2)
|
||||||
child = { id: 10 }
|
child = { id: 10 }
|
||||||
newsItem = { id: '1337', modified: 'this morning' }
|
newsItem = { id: '1337', modified: 'now', body: 'simple' }
|
||||||
})
|
})
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
|
@ -86,7 +88,7 @@ describe('useNewsDetails(child, newsItem)', () => {
|
||||||
await waitForNextUpdate()
|
await waitForNextUpdate()
|
||||||
await waitForNextUpdate()
|
await waitForNextUpdate()
|
||||||
|
|
||||||
expect(result.current.data).toEqual({ id: '1337' })
|
expect(result.current.data).toEqual(cached)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('updates status to loading', async () => {
|
it('updates status to loading', async () => {
|
||||||
|
@ -124,7 +126,7 @@ describe('useNewsDetails(child, newsItem)', () => {
|
||||||
await waitForNextUpdate()
|
await waitForNextUpdate()
|
||||||
await pause(20)
|
await pause(20)
|
||||||
|
|
||||||
expect(storage.cache.news_1).toEqual('{"id":"1337"}')
|
expect(storage.cache.news_details_1337).toEqual(JSON.stringify(response))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('does not store in cache if fake', async () => {
|
it('does not store in cache if fake', async () => {
|
||||||
|
@ -138,7 +140,7 @@ describe('useNewsDetails(child, newsItem)', () => {
|
||||||
await waitForNextUpdate()
|
await waitForNextUpdate()
|
||||||
await pause(20)
|
await pause(20)
|
||||||
|
|
||||||
expect(storage.cache.news_2).toEqual('{"id":"1337"}')
|
expect(storage.cache.news_details_1337).toEqual(JSON.stringify(cached))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('retries if api fails', async () => {
|
it('retries if api fails', async () => {
|
||||||
|
@ -155,7 +157,7 @@ describe('useNewsDetails(child, newsItem)', () => {
|
||||||
|
|
||||||
expect(result.current.error).toEqual(error)
|
expect(result.current.error).toEqual(error)
|
||||||
expect(result.current.status).toEqual('loading')
|
expect(result.current.status).toEqual('loading')
|
||||||
expect(result.current.data).toEqual({ id: '1337' })
|
expect(result.current.data).toEqual({ ...cached })
|
||||||
|
|
||||||
jest.advanceTimersToNextTimer()
|
jest.advanceTimersToNextTimer()
|
||||||
|
|
||||||
|
@ -164,7 +166,7 @@ describe('useNewsDetails(child, newsItem)', () => {
|
||||||
await waitForNextUpdate()
|
await waitForNextUpdate()
|
||||||
|
|
||||||
expect(result.current.status).toEqual('loaded')
|
expect(result.current.status).toEqual('loaded')
|
||||||
expect(result.current.data).toEqual({ id: '1337' })
|
expect(result.current.data).toEqual({ ...response })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('gives up after 3 retries', async () => {
|
it('gives up after 3 retries', async () => {
|
||||||
|
@ -183,7 +185,7 @@ describe('useNewsDetails(child, newsItem)', () => {
|
||||||
|
|
||||||
expect(result.current.error).toEqual(error)
|
expect(result.current.error).toEqual(error)
|
||||||
expect(result.current.status).toEqual('loading')
|
expect(result.current.status).toEqual('loading')
|
||||||
expect(result.current.data).toEqual({ id: '1337' })
|
expect(result.current.data).toEqual({ ...cached })
|
||||||
|
|
||||||
jest.advanceTimersToNextTimer()
|
jest.advanceTimersToNextTimer()
|
||||||
|
|
||||||
|
@ -193,7 +195,7 @@ describe('useNewsDetails(child, newsItem)', () => {
|
||||||
|
|
||||||
expect(result.current.error).toEqual(error)
|
expect(result.current.error).toEqual(error)
|
||||||
expect(result.current.status).toEqual('error')
|
expect(result.current.status).toEqual('error')
|
||||||
expect(result.current.data).toEqual({ id: '1337' })
|
expect(result.current.data).toEqual({ ...cached })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('reports if api fails', async () => {
|
it('reports if api fails', async () => {
|
||||||
|
|
Loading…
Reference in New Issue