Skip to content

Commit f1c5b22

Browse files
authored
refactor: streamline order progress state management and enhance tests (#6511)
* refactor: streamline order progress state management and enhance tests * feat: add cancellation tracking for order progress and improve related hooks and tests * fix: update order ID handling to allow optional values in progress bar hooks * fix: ensure consistent order ID usage in progress bar step name updates
1 parent 9d9220e commit f1c5b22

File tree

5 files changed

+90
-14
lines changed

5 files changed

+90
-14
lines changed

apps/cowswap-frontend/src/modules/orderProgressBar/hooks/useOrderProgressBarProps.ts

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ function useOrderBaseProgressBarProps(params: UseOrderProgressBarPropsParams): U
133133
// Do not build progress bar data when these conditions are set
134134
const disableProgressBar = widgetDisabled || isCreating || isFailed || isPresignaturePending || featureFlagDisabled
135135

136-
const orderId = order?.id || ''
136+
const orderId = order?.id
137137

138138
const getCancelOrder = useCancelOrder()
139139
const showCancellationModal = order && getCancelOrder ? getCancelOrder(order) : null
@@ -254,9 +254,9 @@ function getDoNotQueryStatusEndpoint(
254254

255255
const DEFAULT_STATE = {}
256256

257-
function useGetExecutingOrderState(orderId: string): OrderProgressBarState {
257+
function useGetExecutingOrderState(orderId?: string): OrderProgressBarState {
258258
const fullState = useAtomValue(ordersProgressBarStateAtom)
259-
const singleState = fullState[orderId]
259+
const singleState = orderId ? fullState[orderId] : undefined
260260

261261
return useMemo(() => singleState || DEFAULT_STATE, [singleState])
262262
}
@@ -281,14 +281,18 @@ function useSetExecutingOrderProgressBarStepNameCallback(): (orderId: string, va
281281
// local updaters
282282

283283
function useCountdownStartUpdater(
284-
orderId: string,
284+
orderId: string | undefined,
285285
countdown: OrderProgressBarState['countdown'],
286286
backendApiStatus: OrderProgressBarState['backendApiStatus'],
287287
shouldDisableCountdown: boolean,
288288
): void {
289289
const setCountdown = useSetExecutingOrderCountdownCallback()
290290

291291
useEffect(() => {
292+
if (!orderId) {
293+
return
294+
}
295+
292296
if (shouldDisableCountdown) {
293297
// Loose `!= null` on purpose: both null and undefined should reset the countdown, but 0 must stay; strict `!== null` would let undefined slip through
294298
if (countdown != null) {
@@ -308,17 +312,21 @@ function useCountdownStartUpdater(
308312
}, [backendApiStatus, setCountdown, countdown, orderId, shouldDisableCountdown])
309313
}
310314

311-
function useCancellingOrderUpdater(orderId: string, isCancelling: boolean): void {
315+
function useCancellingOrderUpdater(orderId: string | undefined, isCancelling: boolean): void {
312316
const setCancellationTriggered = useSetAtom(setOrderProgressBarCancellationTriggered)
313317

314318
useEffect(() => {
315-
if (isCancelling) setCancellationTriggered(orderId)
319+
if (!orderId || !isCancelling) {
320+
return
321+
}
322+
323+
setCancellationTriggered(orderId)
316324
}, [orderId, isCancelling, setCancellationTriggered])
317325
}
318326

319327
// TODO: Break down this large function into smaller functions
320328
function useProgressBarStepNameUpdater(
321-
orderId: string,
329+
orderId: string | undefined,
322330
isUnfillable: boolean,
323331
isCancelled: boolean,
324332
isExpired: boolean,
@@ -352,8 +360,14 @@ function useProgressBarStepNameUpdater(
352360

353361
// Update state with new step name
354362
useEffect(() => {
363+
if (!orderId) {
364+
return
365+
}
366+
367+
const ensuredOrderId = orderId
368+
355369
function updateStepName(name: OrderProgressBarStepName): void {
356-
setProgressBarStepName(orderId, name || DEFAULT_STEP_NAME)
370+
setProgressBarStepName(ensuredOrderId, name || DEFAULT_STEP_NAME)
357371
}
358372

359373
let timer: NodeJS.Timeout | undefined
@@ -487,7 +501,11 @@ const BACKEND_TYPE_TO_PROGRESS_BAR_STEP_NAME: Record<CompetitionOrderStatus.type
487501
[CompetitionOrderStatus.type.CANCELLED]: OrderProgressBarStepName.INITIAL, // TODO: maybe add another state for finished with error?
488502
}
489503

490-
function useBackendApiStatusUpdater(chainId: SupportedChainId, orderId: string, doNotQuery: boolean): void {
504+
function useBackendApiStatusUpdater(
505+
chainId: SupportedChainId,
506+
orderId: string | undefined,
507+
doNotQuery: boolean,
508+
): void {
491509
const setAtom = useSetAtom(updateOrderProgressBarBackendInfo)
492510
const [stopQuerying, setStopQuerying] = useState(false)
493511
const { type: backendApiStatus, value } = usePendingOrderStatus(chainId, orderId, stopQuerying) || {}
@@ -525,7 +543,7 @@ const POOLING_SWR_OPTIONS = {
525543

526544
function usePendingOrderStatus(
527545
chainId: SupportedChainId,
528-
orderId: string,
546+
orderId: string | undefined,
529547
doNotQuery?: boolean,
530548
): CompetitionOrderStatus | undefined {
531549
return useSWR(

apps/cowswap-frontend/src/modules/orderProgressBar/state/atoms.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { createStore } from 'jotai'
22

33
import {
4+
cancellationTrackedOrderIdsAtom,
45
ordersProgressBarStateAtom,
56
pruneOrdersProgressBarState,
67
updateOrderProgressBarCountdown,
@@ -49,6 +50,7 @@ describe('pruneOrdersProgressBarState', () => {
4950

5051
expect(store.get(ordersProgressBarStateAtom)).toBe(initialState)
5152
})
53+
5254
})
5355

5456
describe('updateOrderProgressBarCountdown', () => {
@@ -124,3 +126,16 @@ describe('updateOrderProgressBarCountdown', () => {
124126
expect(store.get(ordersProgressBarStateAtom)).toEqual({})
125127
})
126128
})
129+
130+
describe('cancellationTrackedOrderIdsAtom', () => {
131+
it('returns ids with cancellationTriggered flag set', () => {
132+
const store = createStore()
133+
store.set(ordersProgressBarStateAtom, {
134+
a: { cancellationTriggered: true },
135+
b: {},
136+
c: { cancellationTriggered: true },
137+
})
138+
139+
expect(store.get(cancellationTrackedOrderIdsAtom)).toEqual(['a', 'c'])
140+
})
141+
})

apps/cowswap-frontend/src/modules/orderProgressBar/state/atoms.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export const pruneOrdersProgressBarState = atom(null, (get, set, trackedOrderIds
3232
return acc
3333
}, {})
3434

35-
if (!changed && trackedOrderIds.every((orderId) => orderId in fullState)) {
35+
if (!changed) {
3636
return
3737
}
3838

@@ -213,3 +213,11 @@ export const setOrderProgressBarCancellationTriggered = atom(null, (get, set, or
213213

214214
set(ordersProgressBarStateAtom, { ...fullState, [orderId]: singleState })
215215
})
216+
217+
export const cancellationTrackedOrderIdsAtom = atom((get) => {
218+
const fullState = get(ordersProgressBarStateAtom)
219+
220+
return Object.entries(fullState)
221+
.filter(([, state]) => state?.cancellationTriggered)
222+
.map(([orderId]) => orderId)
223+
})

apps/cowswap-frontend/src/modules/orderProgressBar/updaters/OrderProgressStateUpdater.test.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ jest.mock('modules/trade', () => ({
3636
}))
3737

3838
const mockPruneOrders = jest.fn()
39+
const mockCancellationIds = jest.fn()
3940

4041
jest.mock('jotai', () => {
4142
const actual = jest.requireActual('jotai')
@@ -51,6 +52,15 @@ jest.mock('jotai', () => {
5152

5253
return actual.useSetAtom(atom)
5354
}),
55+
useAtomValue: jest.fn((atom: PrimitiveAtom<unknown>) => {
56+
const { cancellationTrackedOrderIdsAtom } = jest.requireActual('../state/atoms')
57+
58+
if (atom === cancellationTrackedOrderIdsAtom) {
59+
return mockCancellationIds()
60+
}
61+
62+
return actual.useAtomValue(atom)
63+
}),
5464
}
5565
})
5666

@@ -72,6 +82,7 @@ describe('OrderProgressStateUpdater', () => {
7282
})
7383
useSurplusQueueOrderIdsMock.mockReturnValue([])
7484
useTradeConfirmStateMock.mockReturnValue({ transactionHash: null } as never)
85+
mockCancellationIds.mockReturnValue([])
7586
})
7687

7788
afterEach(() => {
@@ -154,4 +165,17 @@ describe('OrderProgressStateUpdater', () => {
154165

155166
expect(mockPruneOrders).toHaveBeenLastCalledWith(['1', '2', '3'])
156167
})
168+
169+
it('keeps cancellation-triggered orders even if they are not pending anymore', () => {
170+
useWalletInfoMock.mockReturnValue({
171+
chainId: undefined,
172+
account: undefined,
173+
} as unknown as WalletInfo)
174+
useOnlyPendingOrdersMock.mockReturnValue([])
175+
mockCancellationIds.mockReturnValue(['abc'])
176+
177+
render(<OrderProgressStateUpdater />)
178+
179+
expect(mockPruneOrders).toHaveBeenLastCalledWith(['abc'])
180+
})
157181
})

apps/cowswap-frontend/src/modules/orderProgressBar/updaters/OrderProgressStateUpdater.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useSetAtom } from 'jotai'
1+
import { useAtomValue, useSetAtom } from 'jotai'
22
import { ReactNode, useEffect, useMemo } from 'react'
33

44
import { OrderClass, SupportedChainId } from '@cowprotocol/cow-sdk'
@@ -12,7 +12,7 @@ import { useOnlyPendingOrders } from 'legacy/state/orders/hooks'
1212
import { useTradeConfirmState } from 'modules/trade'
1313

1414
import { useOrderProgressBarProps } from '../hooks/useOrderProgressBarProps'
15-
import { pruneOrdersProgressBarState } from '../state/atoms'
15+
import { cancellationTrackedOrderIdsAtom, pruneOrdersProgressBarState } from '../state/atoms'
1616

1717
function OrderProgressStateObserver({ chainId, order }: { chainId: SupportedChainId; order: Order }): null {
1818
useOrderProgressBarProps(chainId, order)
@@ -24,6 +24,7 @@ export function OrderProgressStateUpdater(): ReactNode {
2424
const pruneProgressState = useSetAtom(pruneOrdersProgressBarState)
2525
const { transactionHash } = useTradeConfirmState()
2626
const surplusQueueOrderIds = useSurplusQueueOrderIds()
27+
const cancellationTrackedOrderIds = useAtomValue(cancellationTrackedOrderIdsAtom)
2728

2829
const pendingOrders = useOnlyPendingOrders(chainId as SupportedChainId, account)
2930
const marketOrders = useMemo(
@@ -46,8 +47,18 @@ export function OrderProgressStateUpdater(): ReactNode {
4647
trackedIdsSet.add(transactionHash)
4748
}
4849

50+
cancellationTrackedOrderIds.forEach((orderId) => trackedIdsSet.add(orderId))
51+
4952
pruneProgressState(Array.from(trackedIdsSet))
50-
}, [account, chainId, marketOrders, pruneProgressState, surplusQueueOrderIds, transactionHash])
53+
}, [
54+
account,
55+
cancellationTrackedOrderIds,
56+
chainId,
57+
marketOrders,
58+
pruneProgressState,
59+
surplusQueueOrderIds,
60+
transactionHash,
61+
])
5162

5263
if (!chainId || !account) {
5364
return null

0 commit comments

Comments
 (0)