interface Action { type: T; } interface AnyAction extends Action { [extraProps: string]: any; } type Reducer = ( state?: S, action?: A ) => S; type Dispatch = ( action: T, ...extraArgs: unknown[] ) => T; type ExtendState = [Extension] extends [never] ? State : State & Extension; type Unsubscribe = () => void; interface Store< S = unknown, A extends Action = AnyAction, StateExt = never, Ext = Record > { dispatch: Dispatch; getState(): S; subscribe(listener: () => void): Unsubscribe; replaceReducer( nextReducer: Reducer ): Store, NewActions, StateExt, Ext> & Ext; } declare const $CombinedState: unique symbol; type CombinedState = { readonly [$CombinedState]?: undefined } & S; type PreloadedState = Required extends { [$CombinedState]: undefined; } ? S extends CombinedState ? { [K in keyof S1]?: S1[K] extends Record ? PreloadedState : S1[K]; } : never : { [K in keyof S]: S[K] extends string | number | boolean | symbol ? S[K] : PreloadedState; }; type StoreEnhancerStoreCreator< Ext = Record, StateExt = never > = ( reducer: Reducer, preloadedState?: PreloadedState ) => Store, A, StateExt, Ext> & Ext; export interface ReactNativeFeedbackReporterEnhancerOptions { stateTransformer(state: S | undefined): (S & any) | null; actionTransformer(action?: AnyAction): AnyAction | null; } const defaultOptions: ReactNativeFeedbackReporterEnhancerOptions = { actionTransformer: (action) => action!!, stateTransformer: (state) => state || null, }; const timeline: unknown[] = []; let preloadedState: Object | undefined; let currentReduxState: any = {}; export function createReduxEnhancer( enhancerOptions?: Partial ): any { // Note: We return an any type as to not have type conflicts. const options = { ...defaultOptions, ...enhancerOptions, }; return (next: StoreEnhancerStoreCreator): StoreEnhancerStoreCreator => < S = any, A extends Action = AnyAction >( reducer: Reducer, initialState?: PreloadedState ) => { const ReactNativeFeedbackReporterReducer: Reducer = ( state, action ): S => { const newState = reducer(state, action); const transformedAction = options.actionTransformer(action); const transformedState = options.stateTransformer(newState); if (!preloadedState) { preloadedState = transformedState; } if ( transformedAction?.type !== '@@INIT' && // Skip init action // @ts-ignore !transformedAction?.type.startsWith('persist') // Skip redux persist related actions ) { // @ts-ignore timeline.push(transformedAction); } currentReduxState = newState; return newState; }; return next(ReactNativeFeedbackReporterReducer, initialState); }; } export const getExportContent = () => timeline.length ? JSON.stringify({ payload: JSON.stringify(timeline), preloadedState: JSON.stringify(preloadedState), }) : null; export const getCurrentReduxState = () => currentReduxState;