import { configureStore, Middleware } from '@reduxjs/toolkit'; import { offline } from '@redux-offline/redux-offline'; import offlineConfig from '@redux-offline/redux-offline/lib/defaults'; import localforage from 'localforage'; // Slices import offlineReducer from './slices/offlineSlice'; import activitiesReducer from './slices/activitiesSlice'; import childrenReducer from './slices/childrenSlice'; import networkReducer from './slices/networkSlice'; // Middleware import { offlineMiddleware } from './middleware/offlineMiddleware'; import { syncMiddleware } from './middleware/syncMiddleware'; // Configure localforage for IndexedDB storage localforage.config({ name: 'maternal-app', storeName: 'offline_data', driver: [localforage.INDEXEDDB, localforage.LOCALSTORAGE], }); // Custom offline configuration const customOfflineConfig = { ...offlineConfig, persistOptions: { blacklist: ['_persist'], // Don't persist the persist state }, // Effect function - how to execute side effects effect: async (effect: any, action: any) => { const { url, method = 'GET', body, headers = {} } = effect; try { const response = await fetch(url, { method, body: body ? JSON.stringify(body) : undefined, headers: { 'Content-Type': 'application/json', ...headers, }, }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return await response.json(); } catch (error) { throw error; } }, // Discard function - when to discard failed actions discard: (error: any, action: any, retries: number) => { // Discard after 5 retries or if it's a 4xx error const is4xxError = error.message?.includes('HTTP 4'); return retries >= 5 || is4xxError; }, // Retry function - calculate retry delay with exponential backoff retry: (action: any, retries: number) => { // Exponential backoff: 1s, 2s, 4s, 8s, 16s return Math.min(1000 * Math.pow(2, retries), 16000); }, persistCallback: () => { console.log('[Redux Offline] State persisted to storage'); }, persistAutoRehydrate: true, }; export const store = configureStore({ reducer: { offline: offlineReducer, activities: activitiesReducer, children: childrenReducer, network: networkReducer, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { // Ignore these action types for serialization check ignoredActions: ['persist/PERSIST', 'persist/REHYDRATE'], // Ignore these field paths in all actions ignoredActionPaths: ['meta.arg', 'payload.timestamp'], // Ignore these paths in the state ignoredPaths: ['items.dates'], }, }).concat( offlineMiddleware as Middleware, syncMiddleware as Middleware, // Add redux-offline middleware offline(customOfflineConfig).middleware as Middleware ), enhancers: (getDefaultEnhancers) => getDefaultEnhancers().concat( // Add redux-offline enhancer offline(customOfflineConfig).enhancer ), }); // Infer the `RootState` and `AppDispatch` types from the store itself export type RootState = ReturnType; export type AppDispatch = typeof store.dispatch; // Export store instance export default store;