diff --git a/packages/redux-devtools-rtk-query-monitor/src/components/RTKQueryActions.tsx b/packages/redux-devtools-rtk-query-monitor/src/components/RTKQueryActions.tsx index 8210fc48..fcae3b8a 100644 --- a/packages/redux-devtools-rtk-query-monitor/src/components/RTKQueryActions.tsx +++ b/packages/redux-devtools-rtk-query-monitor/src/components/RTKQueryActions.tsx @@ -1,223 +1,45 @@ -import { AnyAction, Dispatch } from 'redux'; -import React from 'react'; import { Button, Toolbar } from '@redux-devtools/ui'; -import { RtkResourceInfo } from '../types'; +import React from 'react'; +import { AnyAction, Dispatch } from 'redux'; interface RTKActionButtonsProps { dispatch?: Dispatch; liftedDispatch?: Dispatch; - resInfo?: RtkResourceInfo | null; - actionsOfQuery?: AnyAction[]; + data: Record; } const ACTION_TYPES = { REFRESH: 'REFRESH', FULFILLED: 'FULFILLED', - TRIGGER_LOADING: 'TRIGGER_LOADING', + TRIGGER_FETCHING: 'TRIGGER_FETCHING', } as const; -const createFulfilledMeta = ( - requestStatus: string, - requestId: string, - baseEndpoint: string, - parsedArgs: any, - queryKey: string, -) => ({ - arg: { - type: 'query', - subscribe: true, - forceRefetch: true, - subscriptionOptions: { - pollingInterval: 0, - skipPollingIfUnfocused: false, - }, - endpointName: baseEndpoint, - originalArgs: parsedArgs || {}, - queryCacheKey: queryKey, - }, - requestId, - requestStatus, - fulfilledTimeStamp: Date.now(), - baseQueryMeta: { - request: {}, - response: {}, - }, - RTK_autoBatch: true, -}); - -const createPendingMeta = ( - requestStatus: string, - requestId: string, - baseEndpoint: string, - parsedArgs: any, - queryKey: string, -) => ({ - arg: { - type: 'query', - subscribe: true, - forceRefetch: true, - subscriptionOptions: { - pollingInterval: 0, - skipPollingIfUnfocused: false, - }, - endpointName: baseEndpoint, - originalArgs: parsedArgs || {}, - queryCacheKey: queryKey, - }, - requestId, - requestStatus, - baseQueryMeta: { - request: {}, - response: {}, - }, - RTK_autoBatch: true, -}); - -export const createRtkQueryActions = ( - reducerPath: string, - queryKey: string, - endpointName?: string, - currentData?: any, - originalArgs?: any, -) => { - const baseEndpoint = endpointName || queryKey.split('(')[0]; - let parsedArgs = originalArgs; - if (!parsedArgs && queryKey.includes('(')) { - const argsString = queryKey.split('(')[1]?.split(')')[0]; - if ( - argsString === undefined || - argsString === '' || - argsString === 'undefined' - ) { - parsedArgs = undefined; - } else { - try { - parsedArgs = JSON.parse(argsString); - } catch (e) { - parsedArgs = undefined; - } - } - } - const actualPayload = currentData || { - message: `No data available for ${baseEndpoint}`, - timestamp: Date.now(), - endpointName: baseEndpoint, - queryKey: queryKey, - }; - - return { - refresh: { - type: `${reducerPath}/executeQuery/pending`, - payload: undefined, - meta: createPendingMeta( - 'pending', - `refresh-${Date.now()}`, - baseEndpoint, - parsedArgs, - queryKey, - ), - }, - fulfilled: { - type: `${reducerPath}/executeQuery/fulfilled`, - payload: actualPayload, - meta: createFulfilledMeta( - 'fulfilled', - `fulfilled-${Date.now()}`, - baseEndpoint, - parsedArgs, - queryKey, - ), - }, - triggerLoading: { - type: `${reducerPath}/executeQuery/pending`, - payload: undefined, - meta: createPendingMeta( - 'pending', - `loading-${Date.now()}`, - baseEndpoint, - parsedArgs, - queryKey, - ), - }, - }; -}; +const REFRESH_TIMEOUT_MS = 1000; export const RTKActionButtons: React.FC = ({ dispatch, liftedDispatch, - resInfo, - actionsOfQuery, + data, }) => { - // Extract current data from query actions if available - let currentData: any; - if (actionsOfQuery && actionsOfQuery.length > 0) { - // Look for the most recent fulfilled action to get current data - const fulfilledActions = actionsOfQuery.filter( - (action) => action.type?.includes('/fulfilled') && action.payload, - ); - if (fulfilledActions.length > 0) { - currentData = fulfilledActions[fulfilledActions.length - 1].payload; - console.log('📊 Found current data from actions:', currentData); - } - } - - if (!resInfo) { - return ( -
- No query selected -
- ); - } - let originalArgs; - let lastPendingAction; - - if (actionsOfQuery && actionsOfQuery.length > 0) { - const queryAction = actionsOfQuery.find( - (action) => - action.type?.includes('/executeQuery/') || - action.type?.includes('/pending') || - action.type?.includes('/fulfilled'), - ); - if (queryAction?.meta?.arg?.originalArgs) { - originalArgs = queryAction.meta.arg.originalArgs; - } - const pendingActions = actionsOfQuery.filter( - (action) => action.type?.includes('/pending') && action.meta?.requestId, - ); - if (pendingActions.length > 0) { - lastPendingAction = pendingActions[pendingActions.length - 1]; - } - } - - const actions = createRtkQueryActions( - resInfo.reducerPath, - resInfo.queryKey, - resInfo.queryKey.split('(')[0], - currentData, - originalArgs, + const [isLoading, setIsLoading] = React.useState(false); + const pending = Object.values(data).filter( + (action) => action.meta?.requestStatus === 'pending', ); - if (lastPendingAction) { - actions.fulfilled.meta.requestId = lastPendingAction.meta.requestId; + const fulfilled = Object.values(data).filter( + (action) => action.meta?.requestStatus === 'fulfilled', + ); - actions.fulfilled.meta.arg.queryCacheKey = - lastPendingAction.meta.arg.queryCacheKey; - - actions.triggerLoading.meta.requestId = lastPendingAction.meta.requestId; - - actions.triggerLoading.meta.arg.queryCacheKey = - lastPendingAction.meta.arg.queryCacheKey; - - actions.refresh.meta.requestId = lastPendingAction.meta.requestId; - - actions.refresh.meta.arg.queryCacheKey = - lastPendingAction.meta.arg.queryCacheKey; - } + // Get the most recent actions + const recentPending = pending[pending.length - 1]; + const recentFulfilled = fulfilled[fulfilled.length - 1]; const handleAction = ( actionType: keyof typeof ACTION_TYPES, action: AnyAction, + fulfilledAction?: AnyAction, ) => { + setIsLoading(true); if (liftedDispatch) { try { const performAction = { @@ -227,37 +49,46 @@ export const RTKActionButtons: React.FC = ({ }; liftedDispatch(performAction); - if (actionType === ACTION_TYPES.REFRESH && currentData) { + // For refresh actions, automatically dispatch fulfilled action after delay + if (actionType === ACTION_TYPES.REFRESH && fulfilledAction) { setTimeout(() => { const resolveAction = { type: 'PERFORM_ACTION' as const, - action: actions.fulfilled, + action: fulfilledAction, timestamp: Date.now(), }; liftedDispatch(resolveAction); - }, 1000); + setIsLoading(false); + }, REFRESH_TIMEOUT_MS); } - } catch (e) { - console.error('Error in liftedDispatch:', e); + } catch (error) { + console.error('Error in liftedDispatch:', error); } } if (dispatch) { try { dispatch(action); - if (actionType === ACTION_TYPES.REFRESH && currentData) { + + // For refresh actions, automatically dispatch fulfilled action after delay + if (actionType === ACTION_TYPES.REFRESH && fulfilledAction) { setTimeout(() => { - dispatch(actions.fulfilled); - }, 1000); + dispatch(fulfilledAction); + setIsLoading(false); + }, REFRESH_TIMEOUT_MS); } - } catch (e) { - console.error('Error in dispatch:', e); + } catch (error) { + console.error('Error in dispatch:', error); } } }; return ( - + + + +