Skip to content

Commit

Permalink
Exclude initial persist & RTK actions from state sync
Browse files Browse the repository at this point in the history
  • Loading branch information
ivangabriele committed Sep 18, 2024
1 parent db9156a commit d610488
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 39 deletions.
82 changes: 45 additions & 37 deletions frontend/src/libs/ReduxStateSync/index.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
import { BroadcastChannel } from 'broadcast-channel'
import { BroadcastChannel, type BroadcastChannelOptions } from 'broadcast-channel'

import type { AnyObject } from '@mtes-mct/monitor-ui'
import type { MainAppDispatch } from '@store'

let lastUuid = 0
export const GET_INIT_STATE = '&_GET_INIT_STATE'
export const SEND_INIT_STATE = '&_SEND_INIT_STATE'
export const RECEIVE_INIT_STATE = '&_RECEIVE_INIT_STATE'
export const INIT_MESSAGE_LISTENER = '&_INIT_MESSAGE_LISTENER'

const defaultConfig = {
blacklist: [],
broadcastChannelOption: undefined,
channel: 'redux_state_sync',
predicate: null,
const BROADCAST_CHANNEL_NAME = 'monitorfish-channel'

interface ReduxStateSyncOptions {
actionFilter: (action: { type: string }) => boolean
broadcastChannelOptions: BroadcastChannelOptions
prepareState: <State extends AnyObject>(state: State) => State
receiveState: <State extends AnyObject>(_prevState: State, nextState: State) => State
}

const DEFAULT_OPTIONS: ReduxStateSyncOptions = {
actionFilter: () => true,
broadcastChannelOptions: {},
prepareState: state => state,
receiveState: (_prevState, nextState) => nextState,
whitelist: []
receiveState: (_prevState, nextState) => nextState
}

const getIniteState = () => ({ type: GET_INIT_STATE })
Expand Down Expand Up @@ -41,26 +50,19 @@ export function generateUuidForAction(action) {

return stampedAction
}
// export for test
export function isActionAllowed({ blacklist, predicate, whitelist }) {
let allowed: any = () => true

if (predicate && typeof predicate === 'function') {
allowed = predicate
} else if (Array.isArray(blacklist)) {
allowed = action => blacklist.indexOf(action.type) < 0
} else if (Array.isArray(whitelist)) {
allowed = action => whitelist.indexOf(action.type) >= 0
}

return allowed
}
// export for test
export function isActionSynced(action) {
return !!action.$isSync
}
// export for test
export function MessageListener(this: any, { allowed, channel, dispatch }) {
function MessageListener(
this: any,
{
actionFilter,
channel,
dispatch
}: {
actionFilter: (action: { type: string }) => boolean
channel: BroadcastChannel
dispatch: MainAppDispatch
}
) {
let isSynced = false
const tabs = {}
this.handleOnMessage = stampedAction => {
Expand All @@ -82,7 +84,7 @@ export function MessageListener(this: any, { allowed, channel, dispatch }) {
isSynced = true
dispatch(receiveIniteState(stampedAction.payload))
}
} else if (allowed(stampedAction)) {
} else if (actionFilter(stampedAction)) {
lastUuid = stampedAction.$uuid
dispatch(
Object.assign(stampedAction, {
Expand All @@ -96,33 +98,39 @@ export function MessageListener(this: any, { allowed, channel, dispatch }) {
this.messageChannel.onmessage = this.handleOnMessage
}

export const createStateSyncMiddleware = (config = defaultConfig) => {
const allowed = isActionAllowed(config)
const channel = new BroadcastChannel(config.channel, config.broadcastChannelOption)
const prepareState = config.prepareState || defaultConfig.prepareState
export const createStateSyncMiddleware = (options: Partial<ReduxStateSyncOptions>) => {
const controlledOptions = { ...DEFAULT_OPTIONS, ...options }

const channel = new BroadcastChannel(BROADCAST_CHANNEL_NAME, controlledOptions.broadcastChannelOptions)
const prepareState = options.prepareState ?? controlledOptions.prepareState
let messageListener = null

return ({ dispatch, getState }) =>
next =>
action => {
// create message receiver
if (!messageListener) {
messageListener = new MessageListener({ allowed, channel, dispatch })
messageListener = new MessageListener({
actionFilter: controlledOptions.actionFilter,
channel,
dispatch
})
}
// post messages

if (action && !action.$uuid) {
const stampedAction = generateUuidForAction(action)
lastUuid = stampedAction.$uuid
try {
if (action.type === SEND_INIT_STATE) {
if (getState()) {
stampedAction.payload = prepareState(getState())

channel.postMessage(stampedAction)
}

return next(action)
}
if (allowed(stampedAction) || action.type === GET_INIT_STATE) {

if (controlledOptions.actionFilter(stampedAction) || action.type === GET_INIT_STATE) {
channel.postMessage(stampedAction)
}
} catch (e) {
Expand All @@ -140,7 +148,7 @@ export const createStateSyncMiddleware = (config = defaultConfig) => {

// eslint-disable-next-line max-len
export const createReduxStateSync =
(appReducer, receiveState = defaultConfig.receiveState) =>
(appReducer, receiveState = DEFAULT_OPTIONS.receiveState) =>
(state, action) => {
let initState = state
if (action.type === RECEIVE_INIT_STATE) {
Expand Down
9 changes: 7 additions & 2 deletions frontend/src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,16 @@ export const mainStore = configureStore({
// TODO Replace all Redux state Dates by strings & Error by a strict-typed POJO.
serializableCheck: false
}).concat(
createStateSyncMiddleware(),
monitorenvApi.middleware,
monitorfishApi.middleware,
monitorfishPublicApi.middleware,
monitorfishLightApi.middleware
monitorfishLightApi.middleware,
createStateSyncMiddleware({
actionFilter: action =>
!['persist/PERSIST'].includes(action.type) &&
!action.type.startsWith('monitorfishApi/') &&
!action.type.startsWith('monitorfishPublicApi/')
})
),
reducer: persistedMainReducer
})
Expand Down

0 comments on commit d610488

Please sign in to comment.