From fcecf305d6855bab824b44decd5eba71a303a1ec Mon Sep 17 00:00:00 2001 From: Fionna Chan <13184582+fionnachan@users.noreply.github.com> Date: Fri, 29 Nov 2024 19:13:39 +0800 Subject: [PATCH] feat: new tx history layout (#2072) --- .../public/icons/history.svg | 5 + .../public/icons/wallet.svg | 3 + .../src/components/App/AppContext.tsx | 26 +- .../components/MainContent/MainContent.tsx | 80 +++--- .../components/Sidebar/AccountMenuItem.tsx | 7 - .../src/components/Sidebar/AppSidebar.tsx | 2 +- .../src/components/TopNavBar.tsx | 48 ++++ .../TransactionHistory/TransactionHistory.tsx | 65 +++-- .../TransactionHistoryTable.tsx | 241 +++++++++--------- .../TransactionStatusInfo.tsx | 85 ++---- .../TransactionsTableRow.tsx | 2 +- .../useTransactionReminderInfo.ts | 60 +++++ .../TransferPanel/TransferPanel.tsx | 9 +- .../common/HeaderAccountPopover.tsx | 15 +- .../src/components/common/Layout.tsx | 2 +- .../src/hooks/useAccountMenu.ts | 9 - .../arb-token-bridge-ui/tailwind.config.js | 5 + .../tests/e2e/cypress.d.ts | 8 +- .../tests/e2e/specs/batchDeposit.cy.ts | 4 +- .../tests/e2e/specs/depositCctp.cy.ts | 1 - .../tests/e2e/specs/depositERC20.cy.ts | 4 +- .../tests/e2e/specs/depositNativeToken.cy.ts | 4 +- .../tests/e2e/specs/readClassicDeposits.cy.ts | 30 ++- .../tests/e2e/specs/redeemRetryable.cy.ts | 7 +- .../tests/e2e/specs/txHistory.cy.ts | 4 +- .../tests/e2e/specs/withdrawCctp.cy.ts | 1 - .../tests/e2e/specs/withdrawERC20.cy.ts | 10 +- .../tests/e2e/specs/withdrawNativeToken.cy.ts | 10 +- .../tests/support/commands.ts | 47 ++-- packages/scripts/package.json | 2 +- yarn.lock | 11 - 31 files changed, 409 insertions(+), 398 deletions(-) create mode 100644 packages/arb-token-bridge-ui/public/icons/history.svg create mode 100644 packages/arb-token-bridge-ui/public/icons/wallet.svg create mode 100644 packages/arb-token-bridge-ui/src/components/TopNavBar.tsx create mode 100644 packages/arb-token-bridge-ui/src/components/TransactionHistory/useTransactionReminderInfo.ts diff --git a/packages/arb-token-bridge-ui/public/icons/history.svg b/packages/arb-token-bridge-ui/public/icons/history.svg new file mode 100644 index 0000000000..0e766b0c49 --- /dev/null +++ b/packages/arb-token-bridge-ui/public/icons/history.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/arb-token-bridge-ui/public/icons/wallet.svg b/packages/arb-token-bridge-ui/public/icons/wallet.svg new file mode 100644 index 0000000000..6623ec3f47 --- /dev/null +++ b/packages/arb-token-bridge-ui/public/icons/wallet.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/arb-token-bridge-ui/src/components/App/AppContext.tsx b/packages/arb-token-bridge-ui/src/components/App/AppContext.tsx index e70d0713f0..8faa126b8e 100644 --- a/packages/arb-token-bridge-ui/src/components/App/AppContext.tsx +++ b/packages/arb-token-bridge-ui/src/components/App/AppContext.tsx @@ -9,15 +9,13 @@ type AppContextState = { layout: { isTransferPanelVisible: boolean isTransferring: boolean - isTransactionHistoryPanelVisible: boolean } } const initialState: AppContextState = { layout: { isTransferPanelVisible: true, - isTransferring: false, - isTransactionHistoryPanelVisible: false + isTransferring: false } } @@ -29,7 +27,6 @@ const AppContext = createContext([initialState, () => {}]) type Action = | { type: 'layout.set_is_transfer_panel_visible'; payload: boolean } | { type: 'layout.set_is_transferring'; payload: boolean } - | { type: 'layout.set_txhistory_panel_visible'; payload: boolean } function reducer(state: AppContextState, action: Action) { switch (action.type) { @@ -39,15 +36,6 @@ function reducer(state: AppContextState, action: Action) { layout: { ...state.layout, isTransferPanelVisible: action.payload } } - case 'layout.set_txhistory_panel_visible': - return { - ...state, - layout: { - ...state.layout, - isTransactionHistoryPanelVisible: action.payload - } - } - case 'layout.set_is_transferring': return { ...state, @@ -88,17 +76,7 @@ export const useAppContextActions = (dispatchOverride?: Dispatch) => { dispatch({ type: 'layout.set_is_transferring', payload }) } - const openTransactionHistoryPanel = () => { - dispatch({ type: 'layout.set_txhistory_panel_visible', payload: true }) - } - - const closeTransactionHistoryPanel = () => { - dispatch({ type: 'layout.set_txhistory_panel_visible', payload: false }) - } - return { - setTransferring, - openTransactionHistoryPanel, - closeTransactionHistoryPanel + setTransferring } } diff --git a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx index 158308db54..b8d2718dd7 100644 --- a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx +++ b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx @@ -1,68 +1,54 @@ -import { useEffect, useMemo } from 'react' -import { useAccount } from 'wagmi' import { useLocalStorage } from '@uidotdev/usehooks' +import { Tab } from '@headlessui/react' +import { create } from 'zustand' import { TransferPanel } from '../TransferPanel/TransferPanel' -import { SidePanel } from '../common/SidePanel' -import { useAppContextActions, useAppContextState } from '../App/AppContext' import { ArbitrumStats, statsLocalStorageKey } from './ArbitrumStats' import { SettingsDialog } from '../common/SettingsDialog' import { TransactionHistory } from '../TransactionHistory/TransactionHistory' -import { useTransactionHistory } from '../../hooks/useTransactionHistory' -import { isTxPending } from '../TransactionHistory/helpers' -import { TransactionStatusInfo } from '../TransactionHistory/TransactionStatusInfo' +import { TopNavBar } from '../TopNavBar' -function TransactionHistorySidePanel() { - const { closeTransactionHistoryPanel } = useAppContextActions() - const { - layout: { isTransactionHistoryPanelVisible } - } = useAppContextState() - const { address } = useAccount() - - const transactionHistoryProps = useTransactionHistory(address, { - runFetcher: true - }) - - const { transactions, updatePendingTransaction } = transactionHistoryProps - - const pendingTransactions = useMemo(() => { - return transactions.filter(isTxPending) - }, [transactions]) - - useEffect(() => { - const interval = setInterval(() => { - pendingTransactions.forEach(updatePendingTransaction) - }, 10_000) - - return () => clearInterval(interval) - }, [pendingTransactions, updatePendingTransaction]) +enum MainContentTabs { + Bridge = 0, + TransactionHistory = 1 +} - return ( - - - - ) +type MainContentTabStore = { + selectedTab: MainContentTabs + setSelectedTab: (index: MainContentTabs) => void + switchToBridgeTab: () => void + switchToTransactionHistoryTab: () => void } +export const useMainContentTabs = create(set => ({ + selectedTab: MainContentTabs.Bridge, + setSelectedTab: (index: MainContentTabs) => set({ selectedTab: index }), + switchToBridgeTab: () => set({ selectedTab: MainContentTabs.Bridge }), + switchToTransactionHistoryTab: () => + set({ selectedTab: MainContentTabs.TransactionHistory }) +})) + export function MainContent() { const [isArbitrumStatsVisible] = useLocalStorage(statsLocalStorageKey) + const { selectedTab, setSelectedTab } = useMainContentTabs() return ( <> -
- - - +
+ + + + + + + + + + +
- - {/* Settings panel */} diff --git a/packages/arb-token-bridge-ui/src/components/Sidebar/AccountMenuItem.tsx b/packages/arb-token-bridge-ui/src/components/Sidebar/AccountMenuItem.tsx index 96d648990e..07e8cc4f7b 100644 --- a/packages/arb-token-bridge-ui/src/components/Sidebar/AccountMenuItem.tsx +++ b/packages/arb-token-bridge-ui/src/components/Sidebar/AccountMenuItem.tsx @@ -17,7 +17,6 @@ export const AccountMenuItem = () => { accountShort, ensName, ensAvatar, - openTransactionHistory, disconnect, udInfo, chain, @@ -36,12 +35,6 @@ export const AccountMenuItem = () => { /> } > - } - onClick={openTransactionHistory} - isMobile - /> {chain && ( { const posthog = usePostHog() return ( -
+
) diff --git a/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx b/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx new file mode 100644 index 0000000000..9e2105d68d --- /dev/null +++ b/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx @@ -0,0 +1,48 @@ +import { Tab } from '@headlessui/react' +import { PaperAirplaneIcon } from '@heroicons/react/24/outline' +import { PropsWithChildren } from 'react' +import { twMerge } from 'tailwind-merge' +import Image from 'next/image' +import { useTransactionReminderInfo } from './TransactionHistory/useTransactionReminderInfo' + +function StyledTab({ children, ...props }: PropsWithChildren) { + return ( + + {children} + + ) +} + +StyledTab.displayName = 'StyledTab' + +export function TopNavBar() { + const { colorClassName } = useTransactionReminderInfo() + + return ( + + + + Bridge + + + history icon + Txn History{' '} + + + + ) +} diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx index a51d4e9656..f1dbc7bb2e 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx @@ -1,10 +1,11 @@ import dayjs from 'dayjs' -import { useMemo } from 'react' +import { useEffect, useMemo } from 'react' import { Tab } from '@headlessui/react' import { create } from 'zustand' +import { useAccount } from 'wagmi' -import { UseTransactionHistoryResult } from '../../hooks/useTransactionHistory' import { TransactionHistoryTable } from './TransactionHistoryTable' +import { TransactionStatusInfo } from '../TransactionHistory/TransactionStatusInfo' import { isTxClaimable, isTxCompleted, @@ -15,7 +16,31 @@ import { import { MergedTransaction } from '../../state/app/state' import { TabButton } from '../common/Tab' import { TransactionsTableDetails } from './TransactionsTableDetails' -import { Address } from '../../util/AddressUtils' +import { useTransactionHistory } from '../../hooks/useTransactionHistory' + +function useTransactionHistoryUpdater() { + const { address } = useAccount() + + const transactionHistoryProps = useTransactionHistory(address, { + runFetcher: true + }) + + const { transactions, updatePendingTransaction } = transactionHistoryProps + + const pendingTransactions = useMemo(() => { + return transactions.filter(isTxPending) + }, [transactions]) + + useEffect(() => { + const interval = setInterval(() => { + pendingTransactions.forEach(updatePendingTransaction) + }, 10_000) + + return () => clearInterval(interval) + }, [pendingTransactions, updatePendingTransaction]) + + return transactionHistoryProps +} const tabClasses = 'text-white px-3 mr-2 border-b-2 ui-selected:border-white ui-not-selected:border-transparent ui-not-selected:text-white/80 arb-hover' @@ -46,12 +71,10 @@ export const useTxDetailsStore = create(set => ({ reset: () => set({ tx: null }) })) -export const TransactionHistory = ({ - props -}: { - props: UseTransactionHistoryResult & { address: Address | undefined } -}) => { - const { transactions, address } = props +export const TransactionHistory = () => { + const { address } = useAccount() + const props = useTransactionHistoryUpdater() + const { transactions } = props const oldestTxTimeAgoString = useMemo(() => { return dayjs(transactions[transactions.length - 1]?.createdAt).toNow(true) @@ -94,25 +117,33 @@ export const TransactionHistory = ({ const settledTransactions = groupedTransactions.settled return ( - <> - +
+
+ +
+ + - Pending transactions + Pending transactions - Settled transactions + Settled transactions - - + + - + - +
) } diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx index 98d1043210..830fa9f36d 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx @@ -4,10 +4,10 @@ import { useEffect, useMemo, useRef, - useState + forwardRef } from 'react' import { twMerge } from 'tailwind-merge' -import { AutoSizer, Column, Table } from 'react-virtualized' +import { Column, Table } from 'react-virtualized' import { ExclamationCircleIcon, PlusCircleIcon @@ -15,11 +15,7 @@ import { import dayjs from 'dayjs' import { getProviderForChainId } from '@/token-bridge-sdk/utils' -import { - getStandardizedDate, - getStandardizedTime, - isTokenDeposit -} from '../../state/app/utils' +import { isTokenDeposit } from '../../state/app/utils' import { ChainPair, UseTransactionHistoryResult @@ -50,21 +46,25 @@ export const BatchTransferNativeTokenTooltip = ({ ) } -export const ContentWrapper = ({ - children, - className = '' -}: PropsWithChildren<{ className?: string }>) => { +export const ContentWrapper = forwardRef< + HTMLDivElement, + PropsWithChildren<{ className?: string }> +>(({ children, className = '', ...props }, ref) => { return (
{children}
) -} +}) + +ContentWrapper.displayName = 'ContentWrapper' const TableHeader = ({ children, @@ -151,14 +151,29 @@ export const TransactionHistoryTable = ( oldestTxTimeAgoString } = props - const contentAboveTable = useRef(null) - + const TABLE_HEADER_HEIGHT = 52 + const TABLE_ROW_HEIGHT = 60 const isTxHistoryEmpty = transactions.length === 0 const isPendingTab = selectedTabIndex === 0 const paused = !loading && !completed - const [tableHeight, setTableHeight] = useState(0) + const contentWrapperRef = useRef(null) + const tableRef = useRef(null) + + const tableHeight = useMemo(() => { + if (window.innerWidth < 768) { + return TABLE_ROW_HEIGHT * (transactions.length + 1) + TABLE_HEADER_HEIGHT + } + const SIDE_PANEL_HEADER_HEIGHT = 125 + const viewportHeight = window.innerHeight + const contentWrapperOffsetTop = contentWrapperRef.current?.offsetTop ?? 0 + return Math.max( + // we subtract a little padding at the end so that the table doesn't end at the edge of the screen + viewportHeight - contentWrapperOffsetTop - SIDE_PANEL_HEADER_HEIGHT, + 0 + ) + }, [contentWrapperRef.current?.offsetTop, transactions.length]) const pendingTokenDepositsCount = useMemo(() => { return transactions.filter(tx => isTokenDeposit(tx) && isTxPending(tx)) @@ -169,36 +184,15 @@ export const TransactionHistoryTable = ( return transactions.filter(isTxPending)[0]?.txId }, [transactions]) - // TODO: look into https://www.npmjs.com/package/react-intersection-observer that could simplify this + // recalculate table height when tx number changes, or when user selects different tab useEffect(() => { - // Calculate table height to be passed to the React Virtualized Table - const currentRef = contentAboveTable.current - const SIDE_PANEL_HEADER_HEIGHT = 125 - - // Adjust the table size whenever the content above it is resized - const observer = new ResizeObserver(entries => { - if (entries[0]) { - const aboveHeight = entries[0].contentRect.height - const viewportHeight = window.innerHeight - const newTableHeight = Math.max( - // we subtract a little padding at the end so that the table doesn't end at the edge of the screen - viewportHeight - aboveHeight - SIDE_PANEL_HEADER_HEIGHT - 20, - 0 - ) - setTableHeight(newTableHeight) - } - }) - - if (currentRef) { - observer.observe(currentRef) - } - - return () => { - if (currentRef) { - observer.unobserve(currentRef) - } - } - }, [transactions.length]) + tableRef.current?.recomputeRowHeights() + }, [ + transactions.length, + selectedTabIndex, + isTxHistoryEmpty, + contentWrapperRef.current?.offsetTop + ]) if (isTxHistoryEmpty) { return ( @@ -213,13 +207,15 @@ export const TransactionHistoryTable = ( } return ( - +
{loading ? (
@@ -227,7 +223,7 @@ export const TransactionHistoryTable = (
) : ( -
+
@@ -243,85 +239,82 @@ export const TransactionHistoryTable = (
{pendingTokenDepositsCount > 0 && }
- - {() => ( -
( -
- {props.columns} -
- )} - className="table-auto last:border-b-0" - rowGetter={({ index }) => transactions[index]} - rowRenderer={({ index, style }) => { - const tx = transactions[index] +
( +
+ {props.columns} +
+ )} + className="table-auto last:border-b-0" + rowGetter={({ index }) => transactions[index]} + rowRenderer={({ index, style }) => { + const tx = transactions[index] - if (!tx) { - return null - } + if (!tx) { + return null + } - const isLastRow = index + 1 === transactions.length - const key = `${tx.parentChainId}-${tx.childChainId}-${tx.txId}` - const secondsPassed = dayjs().diff(dayjs(tx.createdAt), 'second') + const isLastRow = index + 1 === transactions.length + const key = `${tx.parentChainId}-${tx.childChainId}-${tx.txId}` + const secondsPassed = dayjs().diff(dayjs(tx.createdAt), 'second') - // only blink the topmost tx, in case many txs are queued in a short amount of time - const isTopmostPendingTx = - topmostPendingTxId && topmostPendingTxId === tx.txId + // only blink the topmost tx, in case many txs are queued in a short amount of time + const isTopmostPendingTx = + topmostPendingTxId && topmostPendingTxId === tx.txId - return ( -
- -
- ) - }} - > - TIME} - /> - TOKEN} - /> - FROM} - /> - TO} - /> - STATUS} - /> -
- )} - + return ( +
+ +
+ ) + }} + > + TIME} + /> + TOKEN} + /> + FROM} + /> + TO} + /> + STATUS} + /> + ) } diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionStatusInfo.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionStatusInfo.tsx index 88ab67d208..fb4ee1f9d7 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionStatusInfo.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionStatusInfo.tsx @@ -3,21 +3,12 @@ Format: "You have [X] deposits to retry and [Y] withdrawals ready to claim. [CTA]" */ -import { useAccount } from 'wagmi' -import { useMemo } from 'react' -import { - DocumentTextIcon, - ExclamationTriangleIcon -} from '@heroicons/react/24/outline' +import { ExclamationTriangleIcon } from '@heroicons/react/24/outline' import { twMerge } from 'tailwind-merge' import Image from 'next/image' import ArrowsIcon from '@/images/arrows.svg' -import { isDepositReadyToRedeem } from '../../state/app/utils' -import { useAppContextActions } from '../App/AppContext' -import { useTransactionHistory } from '../../hooks/useTransactionHistory' -import { Button } from '../common/Button' -import { isTxClaimable, isTxPending } from './helpers' +import { useTransactionReminderInfo } from './useTransactionReminderInfo' const Content = ({ numClaimableTransactions, @@ -87,79 +78,37 @@ const Content = ({ ) } - return ( -
- - See transaction history -
- ) + return null } export const TransactionStatusInfo = () => { - const { address } = useAccount() - const { openTransactionHistoryPanel } = useAppContextActions() - const { transactions } = useTransactionHistory(address) - const { numClaimableTransactions, numRetryablesToRedeem, - numPendingTransactions - } = useMemo(() => { - return transactions.reduce( - (acc, tx) => { - // standard bridge withdrawal - if (isTxClaimable(tx)) { - acc.numClaimableTransactions += 1 - } - // failed retryable - if (isDepositReadyToRedeem(tx)) { - acc.numRetryablesToRedeem += 1 - } - // all pending - if (isTxPending(tx)) { - acc.numPendingTransactions += 1 - } - return acc - }, - { - numClaimableTransactions: 0, - numRetryablesToRedeem: 0, - numPendingTransactions: 0 - } - ) - }, [transactions]) + numPendingTransactions, + colorClassName + } = useTransactionReminderInfo() - const buttonClassName = useMemo(() => { - if (numRetryablesToRedeem > 0) { - return 'bg-red-700' - } - if (numClaimableTransactions > 0) { - return 'bg-lime-dark' - } - if (numPendingTransactions > 0) { - return 'bg-cyan-dark' - } - return 'bg-gray-1 text-white/70' - }, [numClaimableTransactions, numPendingTransactions, numRetryablesToRedeem]) + if ( + numClaimableTransactions === 0 && + numRetryablesToRedeem === 0 && + numPendingTransactions === 0 + ) { + return null + } return ( - +
) } diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableRow.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableRow.tsx index 515ba807c6..ea4a54db0f 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableRow.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableRow.tsx @@ -189,7 +189,7 @@ export function TransactionsTableRow({
diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/useTransactionReminderInfo.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/useTransactionReminderInfo.ts new file mode 100644 index 0000000000..7efe41e359 --- /dev/null +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/useTransactionReminderInfo.ts @@ -0,0 +1,60 @@ +import { useMemo } from 'react' +import { useAccount } from 'wagmi' + +import { useTransactionHistory } from '../../hooks/useTransactionHistory' +import { isTxClaimable, isTxPending } from './helpers' +import { isDepositReadyToRedeem } from '../../state/app/utils' + +export function useTransactionReminderInfo() { + const { address } = useAccount() + const { transactions } = useTransactionHistory(address) + + const { + numClaimableTransactions, + numRetryablesToRedeem, + numPendingTransactions + } = useMemo(() => { + return transactions.reduce( + (acc, tx) => { + // standard bridge withdrawal + if (isTxClaimable(tx)) { + acc.numClaimableTransactions += 1 + } + // failed retryable + if (isDepositReadyToRedeem(tx)) { + acc.numRetryablesToRedeem += 1 + } + // all pending + if (isTxPending(tx)) { + acc.numPendingTransactions += 1 + } + return acc + }, + { + numClaimableTransactions: 0, + numRetryablesToRedeem: 0, + numPendingTransactions: 0 + } + ) + }, [transactions]) + + const colorClassName = useMemo(() => { + if (numRetryablesToRedeem > 0) { + return { dark: 'bg-red-700', light: 'bg-retry' } + } + if (numClaimableTransactions > 0) { + return { dark: 'bg-lime-dark', light: 'bg-claim' } + } + if (numPendingTransactions > 0) { + return { dark: 'bg-cyan-dark', light: 'bg-pending' } + } + return { dark: 'bg-gray-1 text-white/70', light: '' } + }, [numClaimableTransactions, numPendingTransactions, numRetryablesToRedeem]) + + return { + numClaimableTransactions, + numRetryablesToRedeem, + numPendingTransactions, + colorClassName + } +} diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx index b3b08daae4..5710d4ba57 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx @@ -75,6 +75,7 @@ import { MoveFundsButton } from './MoveFundsButton' import { ProjectsListing } from '../common/ProjectsListing' import { useAmountBigNumber } from './hooks/useAmountBigNumber' import { useSourceChainNativeCurrencyDecimals } from '../../hooks/useSourceChainNativeCurrencyDecimals' +import { useMainContentTabs } from '../MainContent/MainContent' const signerUndefinedError = 'Signer is undefined' const transferNotAllowedError = 'Transfer not allowed' @@ -140,8 +141,8 @@ export function TransferPanel() { chainId: networks.sourceChain.id }) - const { openTransactionHistoryPanel, setTransferring } = - useAppContextActions() + const { setTransferring } = useAppContextActions() + const { switchToTransactionHistoryTab } = useMainContentTabs() const { addPendingTransaction } = useTransactionHistory(walletAddress) const isCctpTransfer = useIsCctpTransfer() @@ -497,7 +498,7 @@ export function TransferPanel() { } addPendingTransaction(newTransfer) - openTransactionHistoryPanel() + switchToTransactionHistoryTab() setTransferring(false) clearAmountInput() } catch (e) { @@ -836,7 +837,7 @@ export function TransferPanel() { ) } - openTransactionHistoryPanel() + switchToTransactionHistoryTab() setTransferring(false) clearAmountInput() diff --git a/packages/arb-token-bridge-ui/src/components/common/HeaderAccountPopover.tsx b/packages/arb-token-bridge-ui/src/components/common/HeaderAccountPopover.tsx index 14a265f973..11e9ba28ee 100644 --- a/packages/arb-token-bridge-ui/src/components/common/HeaderAccountPopover.tsx +++ b/packages/arb-token-bridge-ui/src/components/common/HeaderAccountPopover.tsx @@ -4,8 +4,7 @@ import { ArrowTopRightOnSquareIcon, ChevronDownIcon, Cog6ToothIcon, - DocumentDuplicateIcon, - DocumentTextIcon + DocumentDuplicateIcon } from '@heroicons/react/24/outline' import { useState } from 'react' import { useCopyToClipboard, useMedia } from 'react-use' @@ -31,7 +30,6 @@ export function HeaderAccountPopover({ accountShort, ensName, ensAvatar, - openTransactionHistory, disconnect, udInfo, chain, @@ -130,17 +128,6 @@ export function HeaderAccountPopover({
- {/* Transactions button */} - {isCorrectNetworkConnected && ( - - )} - {/* Explorer button */} {isCorrectNetworkConnected && chain && (
-
+
diff --git a/packages/arb-token-bridge-ui/src/hooks/useAccountMenu.ts b/packages/arb-token-bridge-ui/src/hooks/useAccountMenu.ts index d94d0ac0bd..0c894fafe9 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useAccountMenu.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useAccountMenu.ts @@ -11,9 +11,7 @@ import { useEnsAvatar } from 'wagmi' import { useArbQueryParams } from './useArbQueryParams' -import { trackEvent } from '../util/AnalyticsUtils' import { shortenAddress } from '../util/CommonUtils' -import { useAppContextActions } from '../components/App/AppContext' import { onDisconnectHandler } from '../util/walletConnectUtils' type UDInfo = { name: string | null } @@ -53,7 +51,6 @@ export const useAccountMenu = () => { }) const { chain } = useNetwork() - const { openTransactionHistoryPanel } = useAppContextActions() const [, setQueryParams] = useArbQueryParams() const [udInfo, setUDInfo] = useState(udInfoDefaults) @@ -101,17 +98,11 @@ export const useAccountMenu = () => { resolveUdName() }, [address, l1Provider]) - function openTransactionHistory() { - openTransactionHistoryPanel() - trackEvent('Open Transaction History Click', { pageElement: 'Header' }) - } - return { address, accountShort, ensName, ensAvatar, - openTransactionHistory, disconnect, udInfo, chain, diff --git a/packages/arb-token-bridge-ui/tailwind.config.js b/packages/arb-token-bridge-ui/tailwind.config.js index 687d267831..355dd9a77c 100644 --- a/packages/arb-token-bridge-ui/tailwind.config.js +++ b/packages/arb-token-bridge-ui/tailwind.config.js @@ -32,6 +32,11 @@ module.exports = { 'orange-dark': '#60461F', 'lime-dark': '#31572A', + // TRANSACTION STATUS COLORS + claim: '#6AD28A', + retry: '#CD0000', + pending: '#CCB069', + // NEUTRAL (GRAYS) 'gray-1': '#191919', 'gray-2': '#E5E5E5', diff --git a/packages/arb-token-bridge-ui/tests/e2e/cypress.d.ts b/packages/arb-token-bridge-ui/tests/e2e/cypress.d.ts index 267a499bf2..b10ab68d97 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/cypress.d.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/cypress.d.ts @@ -25,8 +25,9 @@ import { findClaimButton, selectTransactionsPanelTab, confirmSpending, - closeTransactionHistoryPanel, - claimCctp + claimCctp, + switchToTransferPanelTab, + switchToTransactionHistoryTab } from '../support/commands' import { NetworkType, NetworkName } from '../support/common' @@ -69,7 +70,8 @@ declare global { findSelectTokenButton: typeof findSelectTokenButton openTransactionDetails: typeof openTransactionDetails closeTransactionDetails: typeof closeTransactionDetails - closeTransactionHistoryPanel: typeof closeTransactionHistoryPanel + switchToTransferPanelTab: typeof switchToTransferPanelTab + switchToTransactionHistoryTab: typeof switchToTransactionHistoryTab findTransactionDetailsCustomDestinationAddress: typeof findTransactionDetailsCustomDestinationAddress findTransactionInTransactionHistory: typeof findTransactionInTransactionHistory findClaimButton: typeof findClaimButton diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/batchDeposit.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/batchDeposit.cy.ts index b8661d2da5..6319838063 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/batchDeposit.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/batchDeposit.cy.ts @@ -151,7 +151,7 @@ describe('Batch Deposit', () => { duration: 'a few seconds ago', ...txData }) - cy.closeTransactionHistoryPanel() + cy.switchToTransferPanelTab() }) context('funds should reach destination account successfully', () => { @@ -266,7 +266,7 @@ describe('Batch Deposit', () => { Cypress.env('CUSTOM_DESTINATION_ADDRESS') ) cy.closeTransactionDetails() - cy.closeTransactionHistoryPanel() + cy.switchToTransferPanelTab() }) context('transfer panel amount should be reset', () => { diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/depositCctp.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/depositCctp.cy.ts index f655a787ce..40b26f6fd6 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/depositCctp.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/depositCctp.cy.ts @@ -113,7 +113,6 @@ describe('Deposit USDC through CCTP', () => { it('should claim deposit', () => { cy.claimCctp(0.00014, { accept: false }) - cy.closeTransactionHistoryPanel() cy.claimCctp(0.00015, { accept: false }) }) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts index 27894707c3..8e1a2c6e43 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts @@ -98,7 +98,7 @@ describe('Deposit Token', () => { }) context('transfer panel amount should be reset', () => { - cy.closeTransactionHistoryPanel() + cy.switchToTransferPanelTab() cy.findAmountInput().should('have.value', '') cy.findMoveFundsButton().should('be.disabled') }) @@ -182,7 +182,7 @@ describe('Deposit Token', () => { context('funds should reach destination account successfully', () => { // close transaction history - cy.closeTransactionHistoryPanel() + cy.switchToTransferPanelTab() // the custom destination address should now have some balance greater than zero cy.findByLabelText(`${testCase.symbol} balance amount on childChain`) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/depositNativeToken.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/depositNativeToken.cy.ts index 7a57a14504..65c81e0cd1 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/depositNativeToken.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/depositNativeToken.cy.ts @@ -37,7 +37,7 @@ describe('Deposit native token', () => { amount: ETHAmountToDeposit, symbol: nativeTokenSymbol }) - cy.closeTransactionHistoryPanel() + cy.switchToTransferPanelTab() cy.findAmountInput().should('have.value', '') cy.findMoveFundsButton().should('be.disabled') }) @@ -75,7 +75,7 @@ describe('Deposit native token', () => { ) cy.closeTransactionDetails() - cy.closeTransactionHistoryPanel() + cy.switchToTransferPanelTab() cy.findAmountInput().should('have.value', '') cy.findMoveFundsButton().should('be.disabled') }) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/readClassicDeposits.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/readClassicDeposits.cy.ts index 727778240c..fc06ea750a 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/readClassicDeposits.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/readClassicDeposits.cy.ts @@ -46,11 +46,8 @@ describe('Read classic deposit messages', () => { context('User has classic native token deposit transaction', () => { it('can read successful native token deposit', () => { - // log in to metamask - cy.login({ - networkType: 'parentChain', - networkName: 'mainnet' - }) + cy.visit('/') + window.localStorage.setItem( `arbitrum:bridge:deposits-${Cypress.env('ADDRESS').toLowerCase()}`, JSON.stringify([ @@ -61,7 +58,13 @@ describe('Read classic deposit messages', () => { ]) ) - cy.openTransactionsPanel('settled') + // log in to metamask + cy.login({ + networkType: 'parentChain', + networkName: 'mainnet' + }) + + cy.switchToTransactionHistoryTab('settled') const destinationTxHash = '0xd3ff2a70a115411e1ae4917351dca49281368684394d0dcac136fa08d9d9b436' @@ -75,11 +78,8 @@ describe('Read classic deposit messages', () => { context('User has classic ERC-20 deposit transaction', () => { it('can read successful ERC-20 deposit', () => { - // log in to metamask - cy.login({ - networkType: 'parentChain', - networkName: 'mainnet' - }) + cy.visit('/') + window.localStorage.setItem( `arbitrum:bridge:deposits-${Cypress.env('ADDRESS').toLowerCase()}`, JSON.stringify([ @@ -92,7 +92,13 @@ describe('Read classic deposit messages', () => { ]) ) - cy.openTransactionsPanel('settled') + // log in to metamask + cy.login({ + networkType: 'parentChain', + networkName: 'mainnet' + }) + + cy.switchToTransactionHistoryTab('settled') const destinationTxHash = '0x6cecd3bfc3ec73181c4ac0253d3f51e5aa8d26157ca7439ff9ab465de14a436f' diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts index 4aea7f5971..0473ee9391 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts @@ -69,7 +69,10 @@ describe('Redeem ERC20 Deposit', () => { context('deposit should be redeemed', () => { // open transaction history and wait for deposit to fetch data - cy.openTransactionsPanel('pending') + cy.switchToTransactionHistoryTab('pending') + + // give ci more time to fetch the transactions + cy.wait(15_000) // find the Retry button and the amount in the row cy.findTransactionInTransactionHistory({ @@ -94,7 +97,7 @@ describe('Redeem ERC20 Deposit', () => { symbol: 'WETH' }) - cy.closeTransactionHistoryPanel() + cy.switchToTransferPanelTab() // wait for the destination balance to update cy.wait(5_000) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/txHistory.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/txHistory.cy.ts index c9f1c0c53a..e1608e2962 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/txHistory.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/txHistory.cy.ts @@ -13,7 +13,7 @@ describe('Transaction History', () => { }) // open tx history panel context('open transactions history panel', () => { - cy.openTransactionsPanel('pending') + cy.switchToTransactionHistoryTab('pending') cy.findAllByTestId(CLAIMABLE_ROW_IDENTIFIER) .its('length') .should('be.gt', 0) @@ -30,7 +30,7 @@ describe('Transaction History', () => { } }) context('open transactions history panel', () => { - cy.openTransactionsPanel('settled') + cy.switchToTransactionHistoryTab('settled') cy.findAllByTestId(CLAIMABLE_ROW_IDENTIFIER) .its('length') .should('be.gt', 0) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawCctp.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawCctp.cy.ts index e1e062f1c4..7c38d25a8b 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawCctp.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawCctp.cy.ts @@ -97,7 +97,6 @@ describe('Withdraw USDC through CCTP', () => { it('should claim deposit', () => { cy.changeMetamaskNetwork('sepolia') cy.claimCctp(0.00012, { accept: true }) - cy.closeTransactionHistoryPanel() cy.claimCctp(0.00013, { accept: true }) }) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts index bbc0ff696f..a1ac1fb75e 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts @@ -142,7 +142,7 @@ describe('Withdraw ERC20 Token', () => { }) context('transfer panel amount should be reset', () => { - cy.closeTransactionHistoryPanel() + cy.switchToTransferPanelTab() cy.findAmountInput().should('have.value', '') cy.findMoveFundsButton().should('be.disabled') }) @@ -153,9 +153,7 @@ describe('Withdraw ERC20 Token', () => { cy.login({ networkType: 'parentChain' }) // login to L1 to claim the funds (otherwise would need to change network after clicking on claim) - cy.findByLabelText('Open Transaction History') - .should('be.visible') - .click() + cy.switchToTransactionHistoryTab('pending') cy.findClaimButton( formatAmount(ERC20AmountToSend, { @@ -175,7 +173,7 @@ describe('Withdraw ERC20 Token', () => { })}` ).should('be.visible') - cy.closeTransactionHistoryPanel() + cy.switchToTransferPanelTab() cy.searchAndSelectToken({ tokenName: testCase.symbol, @@ -265,7 +263,7 @@ describe('Withdraw ERC20 Token', () => { // close popup cy.closeTransactionDetails() - cy.closeTransactionHistoryPanel() + cy.switchToTransferPanelTab() // the balance on the source chain should not be the same as before cy.findByLabelText(`${testCase.symbol} balance amount on childChain`) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawNativeToken.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawNativeToken.cy.ts index 6bb2fd751d..5de34a32de 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawNativeToken.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawNativeToken.cy.ts @@ -96,7 +96,7 @@ describe('Withdraw native token', () => { }) context('transfer panel amount should be reset', () => { - cy.closeTransactionHistoryPanel() + cy.switchToTransferPanelTab() cy.findAmountInput().should('have.value', '') cy.findMoveFundsButton().should('be.disabled') }) @@ -106,9 +106,7 @@ describe('Withdraw native token', () => { // increase the timeout for this test as claim button can take ~(20 blocks *10 blocks/sec) to activate cy.login({ networkType: 'parentChain' }) // login to L1 to claim the funds (otherwise would need to change network after clicking on claim) - cy.findByLabelText('Open Transaction History') - .should('be.visible') - .click() + cy.switchToTransactionHistoryTab('pending') cy.findClaimButton( formatAmount(ETHToWithdraw, { @@ -128,7 +126,7 @@ describe('Withdraw native token', () => { })}` ).should('be.visible') - cy.closeTransactionHistoryPanel() + cy.switchToTransferPanelTab() // the balance on the destination chain should not be the same as before cy.findByLabelText( @@ -194,7 +192,7 @@ describe('Withdraw native token', () => { cy.closeTransactionDetails() context('transfer panel amount should be reset', () => { - cy.closeTransactionHistoryPanel() + cy.switchToTransferPanelTab() cy.findAmountInput().should('have.value', '') cy.findMoveFundsButton().should('be.disabled') }) diff --git a/packages/arb-token-bridge-ui/tests/support/commands.ts b/packages/arb-token-bridge-ui/tests/support/commands.ts index 7010ca34d3..f6fe7efa28 100644 --- a/packages/arb-token-bridge-ui/tests/support/commands.ts +++ b/packages/arb-token-bridge-ui/tests/support/commands.ts @@ -108,31 +108,6 @@ export const selectTransactionsPanelTab = (tab: 'pending' | 'settled') => { .and('equal', 'selected') } -export const openTransactionsPanel = (tab: 'pending' | 'settled') => { - cy.log(`opening transactions panel on ${tab}`) - cy.findByRole('button', { name: /account header button/i }) - .should('be.visible') - .click() - cy.findByRole('button', { name: /transactions/i }) - .should('be.visible') - .click() - - cy.selectTransactionsPanelTab(tab) - - // Waiting for transactions to be fetched - return cy.waitUntil( - () => - cy - .findByText(/Showing \d+ \w+ transactions made in/) - .should('be.visible'), - { - errorMsg: 'Failed to fetch transactions.', - timeout: 120_000, - interval: 500 - } - ) -} - export const searchAndSelectToken = ({ tokenName, tokenAddress @@ -263,8 +238,20 @@ export function findSelectTokenButton( .should('have.text', text) } -export function closeTransactionHistoryPanel() { - cy.findByLabelText('Close side panel').click() +export function switchToTransferPanelTab() { + return cy.findByLabelText('Switch to Bridge Tab').click() +} + +export function switchToTransactionHistoryTab(tab: 'pending' | 'settled') { + cy.log(`opening transactions panel on ${tab}`) + + cy.findByLabelText('Switch to Transaction History Tab').click() + + cy.selectTransactionsPanelTab(tab) + + cy.findByText(/Showing \d+ \w+ transactions made in/, { + timeout: 120_000 + }).should('be.visible') } export function openTransactionDetails({ @@ -372,7 +359,7 @@ export function claimCctp(amount: number, options: { accept: boolean }) { const formattedAmount = formatAmount(amount, { symbol: 'USDC' }) - cy.openTransactionsPanel('pending') + cy.switchToTransactionHistoryTab('pending') cy.findTransactionInTransactionHistory({ amount, symbol: 'USDC' @@ -391,7 +378,6 @@ Cypress.Commands.addAll({ connectToApp, login, logout, - openTransactionsPanel, selectTransactionsPanelTab, searchAndSelectToken, fillCustomDestinationAddress, @@ -406,7 +392,8 @@ Cypress.Commands.addAll({ findMoveFundsButton, startTransfer, findSelectTokenButton, - closeTransactionHistoryPanel, + switchToTransferPanelTab, + switchToTransactionHistoryTab, openTransactionDetails, closeTransactionDetails, findTransactionInTransactionHistory, diff --git a/packages/scripts/package.json b/packages/scripts/package.json index 13cb924a31..9e7591a891 100644 --- a/packages/scripts/package.json +++ b/packages/scripts/package.json @@ -17,7 +17,7 @@ "dependencies": { "@actions/core": "^1.10.1", "@actions/github": "^6.0.0", - "@arbitrum/sdk": "^4.0.1", + "@arbitrum/sdk": "^4.0.2", "@octokit/rest": "^21.0.2", "axios": "^1.7.7", "commander": "^12.1.0", diff --git a/yarn.lock b/yarn.lock index c1346b3d9a..da1ddc8a1f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -78,17 +78,6 @@ tslib "^2.3.0" zen-observable-ts "^1.2.5" -"@arbitrum/sdk@^4.0.1": - version "4.0.1" - resolved "https://registry.npmjs.org/@arbitrum/sdk/-/sdk-4.0.1.tgz" - integrity sha512-uW0Pe/oICbmlHpIpYOaHHWsNQRG+3UbCa3s0SJsp2O1Kt9b0M0CX/fEdFOFLyAi3OxHonNEfzhfvQrALy9C3Yw== - dependencies: - "@ethersproject/address" "^5.0.8" - "@ethersproject/bignumber" "^5.1.1" - "@ethersproject/bytes" "^5.0.8" - async-mutex "^0.4.0" - ethers "^5.1.0" - "@arbitrum/sdk@^4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@arbitrum/sdk/-/sdk-4.0.2.tgz#23555858f49e2b237b94a65bd486c65edb7b1690"