Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Binary file not shown.
2 changes: 2 additions & 0 deletions apps/cowswap-frontend/src/cow-react/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
Updaters,
WithLDProvider,
} from 'modules/application'
import { setupCowSoundUnlock } from 'modules/sounds'

import { hashHistory } from 'common/constants/routes'
import { loadActiveLocaleMessages } from 'lib/localeMessages'
Expand All @@ -61,6 +62,7 @@ function HydrateQueryClient({ children }: { children: ReactNode }): ReactNode {
// Node removeChild hackaround
// based on: https://github.com/facebook/react/issues/11538#issuecomment-417504600
nodeRemoveChildFix()
setupCowSoundUnlock()

// Disable MetaMask network auto-refresh; ignore when window.ethereum is read-only (e.g. another extension set it with a getter).
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { UiOrderType } from '@cowprotocol/types'

import { AnyAction, Dispatch, MiddlewareAPI } from 'redux'
import { instance, mock, resetCalls, when } from 'ts-mockito'

import { getCowSoundError, getCowSoundSend, getCowSoundSuccess } from 'modules/sounds'
import { getCowSoundError, getCowSoundReceiptBundle, getCowSoundSend, getCowSoundSuccess } from 'modules/sounds'

import { getIsBridgeOrder } from 'common/utils/getIsBridgeOrder'
import { getUiOrderType } from 'utils/orderUtils/getUiOrderType'

import { soundMiddleware } from './soundMiddleware'

Expand All @@ -12,6 +17,17 @@ const nextMock = jest.fn()
const actionMock = mock<AnyAction>()

jest.mock('modules/sounds')
jest.mock('common/utils/getIsBridgeOrder')
jest.mock('utils/orderUtils/getUiOrderType')

const getIsBridgeOrderMock = jest.mocked(getIsBridgeOrder)
const getUiOrderTypeMock = jest.mocked(getUiOrderType)
const receiptPlayMock = jest.fn().mockResolvedValue(undefined)
const successPlayMock = jest.fn().mockResolvedValue(undefined)
const otherPlayMock = jest.fn().mockResolvedValue(undefined)
const receiptSoundMock = { currentTime: 1, play: receiptPlayMock } as unknown as HTMLAudioElement
const successSoundMock = { currentTime: 1, play: successPlayMock } as unknown as HTMLAudioElement
const otherSoundMock = { currentTime: 1, play: otherPlayMock } as unknown as HTMLAudioElement

// TODO: Break down this large function into smaller functions

Expand All @@ -27,6 +43,15 @@ describe('soundMiddleware', () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any)
jest.clearAllMocks()
getIsBridgeOrderMock.mockReturnValue(false)
getUiOrderTypeMock.mockReturnValue(UiOrderType.SWAP)
receiptSoundMock.currentTime = 1
successSoundMock.currentTime = 1
otherSoundMock.currentTime = 1
jest.mocked(getCowSoundReceiptBundle).mockReturnValue([successSoundMock, receiptSoundMock])
jest.mocked(getCowSoundError).mockReturnValue(otherSoundMock)
jest.mocked(getCowSoundSend).mockReturnValue(otherSoundMock)
jest.mocked(getCowSoundSuccess).mockReturnValue(successSoundMock)
})

describe('batch order action', () => {
Expand Down Expand Up @@ -92,16 +117,47 @@ describe('soundMiddleware', () => {
})
})
describe('fulfill order action', () => {
it('should play a sound', () => {
when(actionMock.payload).thenReturn({ chainId: 1, orders: ['some data'] })
it('layers the success and receipt sounds for a fulfilled, non-bridge swap', () => {
when(actionMock.payload).thenReturn({ chainId: 1, orders: [{}] })
when(actionMock.type).thenReturn('order/fullfillOrdersBatch')

soundMiddleware(instance(mockStore))(nextMock)(instance(actionMock))

expect(getCowSoundSuccess).toHaveBeenCalledTimes(1)
expect(getCowSoundReceiptBundle).toHaveBeenCalledTimes(1)
expect(getCowSoundSuccess).toHaveBeenCalledTimes(0)
expect(receiptSoundMock.currentTime).toBe(0)
expect(successSoundMock.currentTime).toBe(0)
expect(receiptPlayMock).toHaveBeenCalledTimes(1)
expect(successPlayMock).toHaveBeenCalledTimes(1)
expect(getCowSoundError).toHaveBeenCalledTimes(0)
expect(getCowSoundSend).toHaveBeenCalledTimes(0)
})

it('keeps the existing success sound for a bridge swap leg', () => {
getIsBridgeOrderMock.mockReturnValue(true)
when(actionMock.payload).thenReturn({ chainId: 1, orders: [{}] })
when(actionMock.type).thenReturn('order/fullfillOrdersBatch')

soundMiddleware(instance(mockStore))(nextMock)(instance(actionMock))

expect(getCowSoundReceiptBundle).toHaveBeenCalledTimes(0)
expect(getCowSoundSuccess).toHaveBeenCalledTimes(1)
expect(receiptPlayMock).toHaveBeenCalledTimes(0)
expect(successPlayMock).toHaveBeenCalledTimes(1)
})

it('keeps the existing success sound for non-swap orders', () => {
getUiOrderTypeMock.mockReturnValue(UiOrderType.LIMIT)
when(actionMock.payload).thenReturn({ chainId: 1, orders: [{}] })
when(actionMock.type).thenReturn('order/fullfillOrdersBatch')

soundMiddleware(instance(mockStore))(nextMock)(instance(actionMock))

expect(getCowSoundReceiptBundle).toHaveBeenCalledTimes(0)
expect(getCowSoundSuccess).toHaveBeenCalledTimes(1)
expect(receiptPlayMock).toHaveBeenCalledTimes(0)
expect(successPlayMock).toHaveBeenCalledTimes(1)
})
})
describe('batch expire order action', () => {
it('should play a sound when order is not hidden', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
// On each Pending, Expired, Fulfilled order action a corresponding sound is dispatched

import { UiOrderType } from '@cowprotocol/types'

import { isAnyOf } from '@reduxjs/toolkit'
import { AnyAction, Dispatch, Middleware, MiddlewareAPI } from 'redux'

import { getCowSoundError, getCowSoundSend, getCowSoundSuccess } from 'modules/sounds'
import { getCowSoundError, getCowSoundReceiptBundle, getCowSoundSend, getCowSoundSuccess } from 'modules/sounds'

import { getIsBridgeOrder } from 'common/utils/getIsBridgeOrder'
import { getUiOrderType } from 'utils/orderUtils/getUiOrderType'

import { AppState } from '../../index'
import { AddPendingOrderParams, BatchOrdersUpdateParams, UpdateOrderParams } from '../actions'
import { AddPendingOrderParams, BatchOrdersUpdateParams, FulfillOrdersBatchParams, UpdateOrderParams } from '../actions'
import * as OrderActions from '../actions'
import { getOrderByIdFromState } from '../helpers'
import { OrdersState } from '../reducer'
Expand All @@ -25,7 +30,6 @@ const isBatchFulfillOrderAction = isAnyOf(OrderActions.fulfillOrdersBatch)
const isBatchExpireOrderAction = isAnyOf(OrderActions.expireOrdersBatch)
const isBatchCancelOrderAction = isAnyOf(OrderActions.cancelOrdersBatch)
// const isBatchPresignOrders = isAnyOf(OrderActions.preSignOrders)
const isFulfillOrderAction = isAnyOf(OrderActions.addPendingOrder, OrderActions.fulfillOrdersBatch)

// TODO: Reduce function complexity by extracting logic
// eslint-disable-next-line complexity
Expand All @@ -50,28 +54,30 @@ export const soundMiddleware: Middleware<Record<string, unknown>, AppState> = (s
}
}

let cowSound
let cowSounds: HTMLAudioElement[] = []
if (isPendingOrderAction(action)) {
if (_shouldPlayPendingOrderSound(action.payload)) {
cowSound = getCowSoundSend()
cowSounds = [getCowSoundSend()]
}
} else if (isFulfillOrderAction(action)) {
cowSound = getCowSoundSuccess()
} else if (isBatchFulfillOrderAction(action)) {
cowSounds = _shouldPlayReceiptSound(action.payload.orders) ? getCowSoundReceiptBundle() : [getCowSoundSuccess()]
} else if (isBatchExpireOrderAction(action)) {
if (_shouldPlayExpiredOrderSound(action.payload, store)) {
cowSound = getCowSoundError()
cowSounds = [getCowSoundError()]
}
} else if (isBatchCancelOrderAction(action)) {
cowSound = getCowSoundError()
cowSounds = [getCowSoundError()]
} else if (isUpdateOrderAction(action)) {
cowSound = _getUpdatedOrderSound(action.payload)
const cowSound = _getUpdatedOrderSound(action.payload)
cowSounds = cowSound ? [cowSound] : []
}

if (cowSound) {
cowSounds.forEach((cowSound) => {
cowSound.currentTime = 0
cowSound.play().catch((e) => {
console.error('🐮 Moooooo sound cannot be played', e)
})
}
})

return result
}
Expand Down Expand Up @@ -104,3 +110,7 @@ function _shouldPlayPendingOrderSound(payload: AddPendingOrderParams): boolean {
// Only play COW sound if added pending order is not hidden
return !payload.order.isHidden
}

function _shouldPlayReceiptSound(orders: FulfillOrdersBatchParams['orders']): boolean {
return orders.some((order) => getUiOrderType(order) === UiOrderType.SWAP && !getIsBridgeOrder(order))
}
63 changes: 54 additions & 9 deletions apps/cowswap-frontend/src/locales/en-US.po
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,10 @@ msgstr "<0>By adding this list you are implicitly trusting that the data is corr
#~ msgid "As soon as anything important or interesting happens, we will definitely let you know."
#~ msgstr "As soon as anything important or interesting happens, we will definitely let you know."

#: apps/cowswap-frontend/src/modules/orderProgressBar/pure/PrintedOrderReceipt/PrintedOrderReceipt.pure.tsx
msgid "Thanks for swapping"
msgstr "Thanks for swapping"

#: apps/cowswap-frontend/src/modules/twap/containers/TwapFormWarnings/warnings/SwapPriceDifferenceWarning.tsx
msgid "SWAP order"
msgstr "SWAP order"
Expand Down Expand Up @@ -924,6 +928,7 @@ msgstr "Unsupported Token"
#~ msgid "Limit price (incl. costs)"
#~ msgstr "Limit price (incl. costs)"

#: apps/cowswap-frontend/src/modules/orderProgressBar/pure/PrintedOrderReceipt/PrintedOrderReceipt.pure.tsx
#: apps/cowswap-frontend/src/modules/ordersTable/pure/ContextMenu/OrderContextMenu.pure.tsx
msgid "Order receipt"
msgstr "Order receipt"
Expand Down Expand Up @@ -1217,6 +1222,10 @@ msgstr "Available COW balance"
msgid "Bridge explorer"
msgstr "Bridge explorer"

#: apps/cowswap-frontend/src/modules/orderProgressBar/pure/PrintedOrderReceipt/PrintedOrderReceipt.pure.tsx
msgid "Completed swap receipt"
msgstr "Completed swap receipt"

#: apps/cowswap-frontend/src/modules/hooksStore/dapps/AirdropHookApp/hooks/useClaimData.ts
msgid "You possibly have other items to claim, but not Airdrops"
msgstr "You possibly have other items to claim, but not Airdrops"
Expand Down Expand Up @@ -2672,6 +2681,10 @@ msgstr "(via WalletConnect)"
msgid "<0>Select an {accountProxyLabelString}</0> and then select a token you want to recover from CoW Shed."
msgstr "<0>Select an {accountProxyLabelString}</0> and then select a token you want to recover from CoW Shed."

#: apps/cowswap-frontend/src/modules/orderProgressBar/pure/PrintedOrderReceipt/PrintedOrderReceipt.pure.tsx
msgid "Settlement"
msgstr "Settlement"

#: apps/cowswap-frontend/src/modules/tradeFormValidation/pure/QuoteErrorsButton/quoteErrors.utils.ts
msgid "Error loading price. Try again later."
msgstr "Error loading price. Try again later."
Expand Down Expand Up @@ -3528,9 +3541,9 @@ msgstr "Learn how"
msgid "Account overview"
msgstr "Account overview"

#: apps/cowswap-frontend/src/modules/affiliate/pure/AffiliateTraderActivityTable.tsx
#~ msgid "Network"
#~ msgstr "Network"
#: apps/cowswap-frontend/src/modules/orderProgressBar/pure/PrintedOrderReceipt/PrintedOrderReceipt.pure.tsx
msgid "Network"
msgstr "Network"

#: apps/cowswap-frontend/src/pages/Account/Delegate.tsx
msgid "Delegate your"
Expand Down Expand Up @@ -3978,6 +3991,7 @@ msgid "Safe confirmed signatures"
msgstr "Safe confirmed signatures"

#: apps/cowswap-frontend/src/modules/bridge/pure/contents/SwapResultContent/contents.tsx
#: apps/cowswap-frontend/src/modules/orderProgressBar/pure/PrintedOrderReceipt/PrintedOrderReceipt.pure.tsx
#: apps/cowswap-frontend/src/modules/orderProgressBar/pure/steps/FinishedStep.tsx
msgid "Winning solver"
msgstr "Winning solver"
Expand Down Expand Up @@ -4330,6 +4344,7 @@ msgid "Token is temporarily suspended from trading"
msgstr "Token is temporarily suspended from trading"

#: apps/cowswap-frontend/src/legacy/state/orders/helpers.tsx
#: apps/cowswap-frontend/src/modules/orderProgressBar/pure/PrintedOrderReceipt/PrintedOrderReceipt.pure.tsx
#: apps/cowswap-frontend/src/modules/orderProgressBar/pure/TransactionSubmittedContent/index.tsx
#: apps/cowswap-frontend/src/modules/orders/pure/OrderNotificationContent/index.tsx
msgid "Order"
Expand Down Expand Up @@ -4854,8 +4869,8 @@ msgstr "Order Receipt"
#~ msgstr "The swap adapter contracts integrate Aave Flash Loans and CoW Swap to facilitate advanced actions like swapping debt assets. Learn more at https://aave.com/docs/developers/smart-contracts/swap-features."

#: apps/cowswap-frontend/src/modules/orderProgressBar/pure/steps/FinishedStep.tsx
msgid "Transaction completed!"
msgstr "Transaction completed!"
#~ msgid "Transaction completed!"
#~ msgstr "Transaction completed!"

#: apps/cowswap-frontend/src/legacy/components/Header/NotificationAlertPopover/NotificationAlertPopover.pure.tsx
msgid "When orders fill or expire"
Expand Down Expand Up @@ -5184,6 +5199,10 @@ msgstr "Current network costs make up <0><1>{formattedFeePercentage}%</1></0> of
msgid "Switch network"
msgstr "Switch network"

#: apps/cowswap-frontend/src/modules/orderProgressBar/pure/PrintedOrderReceipt/PrintedOrderReceipt.pure.tsx
#~ msgid "Swap complete"
#~ msgstr "Swap complete"

#: apps/cowswap-frontend/src/modules/ordersTable/pure/OrderEstimatedExecutionPrice/OrderEstimatedExecutionPrice.pure.tsx
#: apps/cowswap-frontend/src/modules/ordersTable/pure/OrderEstimatedExecutionPrice/OrderEstimatedExecutionPrice.pure.tsx
#: apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTable/Row/WarningTooltip/WarningTooltip.pure.tsx
Expand Down Expand Up @@ -5673,6 +5692,10 @@ msgstr "Version"
msgid "All tokens"
msgstr "All tokens"

#: apps/cowswap-frontend/src/modules/orderProgressBar/pure/PrintedOrderReceipt/PrintedOrderReceipt.pure.tsx
msgid "Received by"
msgstr "Received by"

#: apps/cowswap-frontend/src/common/utils/tradeSettingsTooltips.tsx
msgid "When selling {aNativeCurrency}, the minimum slippage tolerance is set to {minimumETHFlowSlippage}% or higher{amountRange} to ensure a high likelihood of order matching, even in volatile market conditions."
msgstr "When selling {aNativeCurrency}, the minimum slippage tolerance is set to {minimumETHFlowSlippage}% or higher{amountRange} to ensure a high likelihood of order matching, even in volatile market conditions."
Expand Down Expand Up @@ -5955,6 +5978,10 @@ msgstr "Links/codes don't reveal your wallet."
msgid "Save & lock code"
msgstr "Save & lock code"

#: apps/cowswap-frontend/src/modules/orderProgressBar/pure/PrintedOrderReceipt/PrintedOrderReceipt.pure.tsx
msgid "Price improvement"
msgstr "Price improvement"

#: apps/cowswap-frontend/src/modules/twap/utils/buildEoaTwapConfirmationPendingSteps.tsx
msgid "Sign in your wallet. We'll submit the funding order automatically."
msgstr "Sign in your wallet. We'll submit the funding order automatically."
Expand Down Expand Up @@ -6435,6 +6462,7 @@ msgstr "Enter valid list location"
msgid "View"
msgstr "View"

#: apps/cowswap-frontend/src/modules/orderProgressBar/pure/PrintedOrderReceipt/PrintedOrderReceipt.pure.tsx
#: apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTable/Header/ordersTableHeader.constants.tsx
#: apps/cowswap-frontend/src/modules/ordersTable/pure/ReceiptModal/ReceiptModal.modal.tsx
msgid "Execution price"
Expand Down Expand Up @@ -6521,8 +6549,8 @@ msgid "Refreshing quote..."
msgstr "Refreshing quote..."

#: apps/cowswap-frontend/src/modules/orderProgressBar/pure/steps/FinishedStep.tsx
msgid "was sent to"
msgstr "was sent to"
#~ msgid "was sent to"
#~ msgstr "was sent to"

#: apps/cowswap-frontend/src/modules/yield/containers/TradeButtons/index.tsx
msgid "Deposit"
Expand Down Expand Up @@ -7198,6 +7226,10 @@ msgstr "Try Again"
#~ msgid "Unwrap {inputAmountStr} {wrapped} to {receiveAmountStr} {native}"
#~ msgstr "Unwrap {inputAmountStr} {wrapped} to {receiveAmountStr} {native}"

#: apps/cowswap-frontend/src/modules/orderProgressBar/pure/PrintedOrderReceipt/PrintedOrderReceipt.pure.tsx
msgid "You sold"
msgstr "You sold"

#: apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTable/Row/WarningTooltip/WarningTooltip.pure.tsx
msgid "The order remains open. Execution requires adequate allowance for"
msgstr "The order remains open. Execution requires adequate allowance for"
Expand Down Expand Up @@ -7857,6 +7889,10 @@ msgstr "Review hook request"
msgid "Invalid parameters"
msgstr "Invalid parameters"

#: apps/cowswap-frontend/src/modules/orderProgressBar/pure/PrintedOrderReceipt/PrintedOrderReceipt.pure.tsx
msgid "You received"
msgstr "You received"

#: apps/cowswap-frontend/src/modules/trade/pure/PartnerFeeRow/index.tsx
msgid "{label}"
msgstr "{label}"
Expand Down Expand Up @@ -8065,6 +8101,10 @@ msgstr "This requires connecting a different wallet."
#~ msgid "Waiting for settlement"
#~ msgstr "Waiting for settlement"

#: apps/cowswap-frontend/src/modules/orderProgressBar/pure/PrintedOrderReceipt/PrintedOrderReceipt.pure.tsx
msgid "Trade succeeded"
msgstr "Trade succeeded"

#: apps/cowswap-frontend/src/modules/accountProxy/containers/AccountProxyPage/AccountProxyPage.container.tsx
#: apps/cowswap-frontend/src/modules/accountProxy/containers/AccountProxyPage/AccountProxyPage.container.tsx
msgid "Invalid proxy address"
Expand All @@ -8084,8 +8124,8 @@ msgstr "Exec. price"
#~ msgstr "This wallet has already traded on CoW Swap. <0/> Referral rewards are for new wallets only."

#: apps/cowswap-frontend/src/modules/orderProgressBar/pure/steps/FinishedStep.tsx
msgid "You sold <0/>"
msgstr "You sold <0/>"
#~ msgid "You sold <0/>"
#~ msgstr "You sold <0/>"

#: apps/cowswap-frontend/src/modules/twap/containers/TwapConfirmModal/TwapTradeConfirmationDetails.tsx
msgid "Order details"
Expand Down Expand Up @@ -8352,6 +8392,10 @@ msgstr "yield over average"
msgid "Uh oh! The market price has moved outside of your slippage tolerance. You can wait for prices to change{cancellationModal}"
msgstr "Uh oh! The market price has moved outside of your slippage tolerance. You can wait for prices to change{cancellationModal}"

#: apps/cowswap-frontend/src/modules/orderProgressBar/pure/PrintedOrderReceipt/PrintedOrderReceipt.pure.tsx
msgid "Time to fill"
msgstr "Time to fill"

#: apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTable/LoadMore/Section/LoadMoreOrdersSection.tsx
msgid "Found {totalOpenOrders} open orders in the {limit} most recent ones."
msgstr "Found {totalOpenOrders} open orders in the {limit} most recent ones."
Expand Down Expand Up @@ -8381,6 +8425,7 @@ msgid "Add token"
msgstr "Add token"

#: apps/cowswap-frontend/src/modules/account/containers/Transaction/StatusDetails.tsx
#: apps/cowswap-frontend/src/modules/orderProgressBar/pure/PrintedOrderReceipt/PrintedOrderReceipt.pure.tsx
#: apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTable/Header/ordersTableHeader.constants.tsx
#: apps/cowswap-frontend/src/modules/ordersTable/pure/OrderStatusBox/getOrderStatusTitleAndColor.ts
#: apps/cowswap-frontend/src/modules/ordersTable/pure/ReceiptModal/ReceiptModal.modal.tsx
Expand Down
Loading
Loading