11import {
2+ createAction ,
23 createSlice ,
3- createAsyncThunk ,
44 createEntityAdapter ,
55 createSelector ,
6+ isAnyOf ,
67} from '@reduxjs/toolkit'
78
8- import { client } from '../../api/client'
99import { forceGenerateNotifications } from '../../api/server'
1010import { apiSlice } from '../api/apiSlice'
1111
12+ const notificationsReceived = createAction (
13+ 'notifications/notificationsReceived'
14+ )
15+
1216export const extendedApi = apiSlice . injectEndpoints ( {
1317 endpoints : ( builder ) => ( {
1418 getNotifications : builder . query ( {
1519 query : ( ) => '/notifications' ,
16- transformResponse : ( res ) => res . notifications ,
1720 async onCacheEntryAdded (
1821 arg ,
19- { updateCachedData, cacheDataLoaded, cacheEntryRemoved }
22+ { updateCachedData, cacheDataLoaded, cacheEntryRemoved, dispatch }
2023 ) {
2124 // create a websocket connection when the cache subscription starts
2225 const ws = new WebSocket ( 'ws://localhost' )
@@ -36,6 +39,8 @@ export const extendedApi = apiSlice.injectEndpoints({
3639 draft . push ( ...message . payload )
3740 draft . sort ( ( a , b ) => b . date . localeCompare ( a . date ) )
3841 } )
42+ // Dispatch an additional action so we can track "read" state
43+ dispatch ( notificationsReceived ( message . payload ) )
3944 break
4045 }
4146 default :
@@ -76,21 +81,11 @@ export const fetchNotificationsWebsocket = () => (dispatch, getState) => {
7681 forceGenerateNotifications ( latestTimestamp )
7782}
7883
79- const notificationsAdapter = createEntityAdapter ( {
80- sortComparer : ( a , b ) => b . date . localeCompare ( a . date ) ,
81- } )
84+ const notificationsAdapter = createEntityAdapter ( )
8285
83- export const fetchNotifications = createAsyncThunk (
84- 'notifications/fetchNotifications' ,
85- async ( _ , { getState } ) => {
86- const allNotifications = selectAllNotifications ( getState ( ) )
87- const [ latestNotification ] = allNotifications
88- const latestTimestamp = latestNotification ? latestNotification . date : ''
89- const response = await client . get (
90- `/fakeApi/notifications?since=${ latestTimestamp } `
91- )
92- return response . data
93- }
86+ const matchNotificationsReceived = isAnyOf (
87+ notificationsReceived ,
88+ extendedApi . endpoints . getNotifications . matchFulfilled
9489)
9590
9691const notificationsSlice = createSlice ( {
@@ -104,10 +99,10 @@ const notificationsSlice = createSlice({
10499 } ,
105100 } ,
106101 extraReducers ( builder ) {
107- builder . addCase ( fetchNotifications . fulfilled , ( state , action ) => {
102+ builder . addMatcher ( matchNotificationsReceived , ( state , action ) => {
108103 // Add client-side metadata for tracking new notifications
109- const notificationsWithMetadata = action . payload . map ( ( notification ) => ( {
110- ... notification ,
104+ const notificationsMetadata = action . payload . map ( ( notification ) => ( {
105+ id : notification . id ,
111106 read : false ,
112107 isNew : true ,
113108 } ) )
@@ -117,7 +112,7 @@ const notificationsSlice = createSlice({
117112 notification . isNew = ! notification . read
118113 } )
119114
120- notificationsAdapter . upsertMany ( state , notificationsWithMetadata )
115+ notificationsAdapter . upsertMany ( state , notificationsMetadata )
121116 } )
122117 } ,
123118} )
@@ -127,5 +122,6 @@ export const { allNotificationsRead } = notificationsSlice.actions
127122export default notificationsSlice . reducer
128123
129124export const {
130- selectAll : selectAllNotifications ,
125+ selectAll : selectNotificationsMetadata ,
126+ selectEntities : selectMetadataEntities ,
131127} = notificationsAdapter . getSelectors ( ( state ) => state . notifications )
0 commit comments