diff --git a/packages/dapp/src/App.tsx b/packages/dapp/src/App.tsx index 3c33c5f..61999b9 100644 --- a/packages/dapp/src/App.tsx +++ b/packages/dapp/src/App.tsx @@ -44,6 +44,9 @@ export default function App() { const [connectError, setConnectError] = useState(null); const [reconnecting, setReconnecting] = useState(false); const [dashboardTab, setDashboardTab] = useState(readTabFromHash); + // Token address to pre-select when opening the Transfer tab from a balances + // row's "Send →". Null on plain nav so Transfer falls back to the first token. + const [transferToken, setTransferToken] = useState(null); // Mirror dashboardTab into the URL hash so a refresh on /#balances stays on // Balances instead of snapping back to Profile. Only mutate the hash while @@ -120,10 +123,24 @@ export default function App() { mm.disconnect(); setProfile(null); clearAllSessions(); + setTransferToken(null); setDashboardTab("profile"); setPage("landing"); } + // Plain tab navigation (sidebar). Clears any pending pre-selection so the + // Transfer tab only honours a token when reached via a "Send →" row action. + const handleTabChange = useCallback((tab: DashboardTab) => { + setTransferToken(null); + setDashboardTab(tab); + }, []); + + // "Send →" on a balances row: pre-select that token, then open Transfer. + const handleSendToken = useCallback((tokenAddress: string) => { + setTransferToken(tokenAddress); + setDashboardTab("transfer"); + }, []); + const handleCustodial = useCallback(() => { setMode("custodial"); setPage("custodial-pending"); @@ -330,16 +347,28 @@ export default function App() { const sharedProps = { address, activeTab: dashboardTab, - onTabChange: setDashboardTab, + onTabChange: handleTabChange, onDisconnect: handleDisconnect, }; if (dashboardTab === "balances") { - return ; + return ( + + ); } if (dashboardTab === "transfer") { - return ; + return ( + + ); } if (dashboardTab === "activity") { diff --git a/packages/dapp/src/pages/DashboardBalancesPage.tsx b/packages/dapp/src/pages/DashboardBalancesPage.tsx index bccc12e..8d3243b 100644 --- a/packages/dapp/src/pages/DashboardBalancesPage.tsx +++ b/packages/dapp/src/pages/DashboardBalancesPage.tsx @@ -36,6 +36,8 @@ interface Props { onTabChange: (tab: DashboardTab) => void; onDisconnect: () => void; keyMode: "custodial" | "external"; + /** Open the Transfer tab with this token pre-selected. */ + onSendToken: (tokenAddress: string) => void; } type OfferRowState = "idle" | "preparing" | "signing" | "executing"; @@ -176,6 +178,7 @@ export function DashboardBalancesPage({ onTabChange, onDisconnect, keyMode, + onSendToken, }: Props) { const [fetchState, setFetchState] = useState(null); const [offers, setOffers] = useState(null); @@ -470,7 +473,10 @@ export function DashboardBalancesPage({

{token.symbol}

- diff --git a/packages/dapp/src/pages/TransferPage.tsx b/packages/dapp/src/pages/TransferPage.tsx index 2bad065..f6f53a6 100644 --- a/packages/dapp/src/pages/TransferPage.tsx +++ b/packages/dapp/src/pages/TransferPage.tsx @@ -42,6 +42,8 @@ interface Props { onTabChange: (tab: DashboardTab) => void; onDisconnect: () => void; keyMode: "custodial" | "external"; + /** Token address to pre-select on mount (set when opened via a balances "Send →"). */ + preselectTokenAddress?: string | null; } function TokenAvatar({ symbol }: { symbol: string }) { @@ -229,7 +231,14 @@ function TokenDropdown({ tokens, selected, balances, onSelect }: TokenDropdownPr ); } -export function TransferPage({ address, activeTab, onTabChange, onDisconnect, keyMode }: Props) { +export function TransferPage({ + address, + activeTab, + onTabChange, + onDisconnect, + keyMode, + preselectTokenAddress, +}: Props) { const [step, setStep] = useState("details"); const [tokens, setTokens] = useState([]); const [tokensLoading, setTokensLoading] = useState(true); @@ -248,6 +257,12 @@ export function TransferPage({ address, activeTab, onTabChange, onDisconnect, ke const snap = useSnap(); const isNonCustodial = keyMode === "external"; + // Pre-selection captured at mount — it's fixed for the lifetime of this + // page (only set when navigating in from a balances "Send →"). Held in a + // ref so the fetch effect can honour it without taking it as a dependency, + // which would otherwise refetch the token list/balances on every change. + const preselectRef = useRef(preselectTokenAddress); + // Load token list then fetch all balances useEffect(() => { let cancelled = false; @@ -259,7 +274,13 @@ export function TransferPage({ address, activeTab, onTabChange, onDisconnect, ke const list = await getTokens(NETWORK.middlewareUrl); if (cancelled) return; setTokens(list); - if (list.length > 0) setSelectedToken(list[0]); + if (list.length > 0) { + const pre = preselectRef.current; + const match = pre + ? list.find((t) => t.address.toLowerCase() === pre.toLowerCase()) + : undefined; + setSelectedToken(match ?? list[0]); + } const entries = await Promise.all( list.map(async (t) => { try {