import { isForbiddenPropertyName } from '@msinternal/botframework-webchat-base/utils'; import type { ActionFromReducersMapObject, combineReducers, Reducer, StateFromReducersMapObject } from 'redux'; import type { GlobalScopePonyfill } from '../../types/GlobalScopePonyfill'; import type { RestrictedStoreDebugAPI } from '../../types/StoreDebugAPI'; import type { WebChatActivity } from '../../types/WebChatActivity'; import createGroupedActivitiesReducer, { type GroupedActivitiesAction, type GroupedActivitiesState } from './createGroupedActivitiesReducer'; type ActivitiesState = { activities: readonly WebChatActivity[]; groupedActivities: GroupedActivitiesState; }; /** * Creates a reducer by combining slice `activities` and `groupedActivities` to an existing sliced reducer. * * @param ponyfill * @param existingSlicedReducer * @returns */ export default function combineActivitiesReducer( ponyfill: GlobalScopePonyfill, restrictedStoreDebugAPI: RestrictedStoreDebugAPI, existingSlicedReducer: ReturnType> ): Reducer & ActivitiesState, ActionFromReducersMapObject & GroupedActivitiesAction> { type ExistingState = StateFromReducersMapObject; type ExistingAction = ActionFromReducersMapObject; const groupedActivitiesReducer = createGroupedActivitiesReducer(ponyfill, restrictedStoreDebugAPI); return function ( state: (ExistingState & ActivitiesState) | undefined, action: ExistingAction & GroupedActivitiesAction ): ExistingState & ActivitiesState { const { activities: _activities, groupedActivities, ...existingState } = state ?? {}; const nextState = existingSlicedReducer(existingState as ExistingState, action); const nextGroupedActivities = groupedActivitiesReducer(groupedActivities, action); const existingStateEntries = Object.entries(existingState); const nextStateEntries = Object.entries(nextState); const hasChanged = !state || !Object.is(state.groupedActivities, nextGroupedActivities) || existingStateEntries.length !== nextStateEntries.length || existingStateEntries.some( // Denylisting forbidden property names. // eslint-disable-next-line security/detect-object-injection ([key, value]) => !Object.is(value, isForbiddenPropertyName(key) ? undefined : (nextState as any)[key]) ); return hasChanged ? { ...nextState, activities: nextGroupedActivities.sortedActivities, groupedActivities: nextGroupedActivities } : state; }; }