Sitelet https://github.com/ChainSafe/canton-snap/commit/b76a02a9568a2fb04e3b540bf4f5a907966e87e6
Skip to content

Commit b76a02a

Browse files
authored
fix(dapp): pre-select the clicked token on Transfer (#72)
* fix(dapp): pre-select the clicked token on the Transfer page A balances row's "Send →" opened Transfer but always defaulted to the first token in the list rather than the one clicked. Thread the chosen token address from the balances row through App into TransferPage, which now pre-selects the matching token (falling back to the first token when opened via plain sidebar navigation). Closes #66 * refactor(dapp): decouple token pre-selection from the fetch effect Address review feedback: reading preselectTokenAddress inside the main token-fetch effect's dependency array refetched the whole token list and balances whenever it changed. Capture it in a ref (it's fixed for the page's lifetime) and read it inside the async load, so deps stay [address] and a pre-selection no longer triggers a refetch or loading flash. Also stop tracking .claude/ (accidental scheduled_tasks.lock) and ignore it.
1 parent 2ad94cb commit b76a02a

3 files changed

Lines changed: 62 additions & 6 deletions

File tree

‎packages/dapp/src/App.tsx‎

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ export default function App() {
4444
const [connectError, setConnectError] = useState<string | null>(null);
4545
const [reconnecting, setReconnecting] = useState(false);
4646
const [dashboardTab, setDashboardTab] = useState<DashboardTab>(readTabFromHash);
47+
// Token address to pre-select when opening the Transfer tab from a balances
48+
// row's "Send →". Null on plain nav so Transfer falls back to the first token.
49+
const [transferToken, setTransferToken] = useState<string | null>(null);
4750

4851
// Mirror dashboardTab into the URL hash so a refresh on /#balances stays on
4952
// Balances instead of snapping back to Profile. Only mutate the hash while
@@ -120,10 +123,24 @@ export default function App() {
120123
mm.disconnect();
121124
setProfile(null);
122125
clearAllSessions();
126+
setTransferToken(null);
123127
setDashboardTab("profile");
124128
setPage("landing");
125129
}
126130

131+
// Plain tab navigation (sidebar). Clears any pending pre-selection so the
132+
// Transfer tab only honours a token when reached via a "Send →" row action.
133+
const handleTabChange = useCallback((tab: DashboardTab) => {
134+
setTransferToken(null);
135+
setDashboardTab(tab);
136+
}, []);
137+
138+
// "Send →" on a balances row: pre-select that token, then open Transfer.
139+
const handleSendToken = useCallback((tokenAddress: string) => {
140+
setTransferToken(tokenAddress);
141+
setDashboardTab("transfer");
142+
}, []);
143+
127144
const handleCustodial = useCallback(() => {
128145
setMode("custodial");
129146
setPage("custodial-pending");
@@ -330,16 +347,28 @@ export default function App() {
330347
const sharedProps = {
331348
address,
332349
activeTab: dashboardTab,
333-
onTabChange: setDashboardTab,
350+
onTabChange: handleTabChange,
334351
onDisconnect: handleDisconnect,
335352
};
336353

337354
if (dashboardTab === "balances") {
338-
return <DashboardBalancesPage {...sharedProps} keyMode={profile.keyMode} />;
355+
return (
356+
<DashboardBalancesPage
357+
{...sharedProps}
358+
keyMode={profile.keyMode}
359+
onSendToken={handleSendToken}
360+
/>
361+
);
339362
}
340363

341364
if (dashboardTab === "transfer") {
342-
return <TransferPage {...sharedProps} keyMode={profile.keyMode} />;
365+
return (
366+
<TransferPage
367+
{...sharedProps}
368+
keyMode={profile.keyMode}
369+
preselectTokenAddress={transferToken}
370+
/>
371+
);
343372
}
344373

345374
if (dashboardTab === "activity") {

‎packages/dapp/src/pages/DashboardBalancesPage.tsx‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ interface Props {
3636
onTabChange: (tab: DashboardTab) => void;
3737
onDisconnect: () => void;
3838
keyMode: "custodial" | "external";
39+
/** Open the Transfer tab with this token pre-selected. */
40+
onSendToken: (tokenAddress: string) => void;
3941
}
4042

4143
type OfferRowState = "idle" | "preparing" | "signing" | "executing";
@@ -176,6 +178,7 @@ export function DashboardBalancesPage({
176178
onTabChange,
177179
onDisconnect,
178180
keyMode,
181+
onSendToken,
179182
}: Props) {
180183
const [fetchState, setFetchState] = useState<FetchState | null>(null);
181184
const [offers, setOffers] = useState<OffersState | null>(null);
@@ -470,7 +473,10 @@ export function DashboardBalancesPage({
470473
</p>
471474
<p className={styles.amountLabel}>{token.symbol}</p>
472475
</div>
473-
<button className={styles.sendRowBtn} onClick={() => onTabChange("transfer")}>
476+
<button
477+
className={styles.sendRowBtn}
478+
onClick={() => onSendToken(token.address)}
479+
>
474480
Send →
475481
</button>
476482
</div>

‎packages/dapp/src/pages/TransferPage.tsx‎

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ interface Props {
4242
onTabChange: (tab: DashboardTab) => void;
4343
onDisconnect: () => void;
4444
keyMode: "custodial" | "external";
45+
/** Token address to pre-select on mount (set when opened via a balances "Send →"). */
46+
preselectTokenAddress?: string | null;
4547
}
4648

4749
function TokenAvatar({ symbol }: { symbol: string }) {
@@ -229,7 +231,14 @@ function TokenDropdown({ tokens, selected, balances, onSelect }: TokenDropdownPr
229231
);
230232
}
231233

232-
export function TransferPage({ address, activeTab, onTabChange, onDisconnect, keyMode }: Props) {
234+
export function TransferPage({
235+
address,
236+
activeTab,
237+
onTabChange,
238+
onDisconnect,
239+
keyMode,
240+
preselectTokenAddress,
241+
}: Props) {
233242
const [step, setStep] = useState<Step>("details");
234243
const [tokens, setTokens] = useState<TokenConfig[]>([]);
235244
const [tokensLoading, setTokensLoading] = useState(true);
@@ -248,6 +257,12 @@ export function TransferPage({ address, activeTab, onTabChange, onDisconnect, ke
248257
const snap = useSnap();
249258
const isNonCustodial = keyMode === "external";
250259

260+
// Pre-selection captured at mount — it's fixed for the lifetime of this
261+
// page (only set when navigating in from a balances "Send →"). Held in a
262+
// ref so the fetch effect can honour it without taking it as a dependency,
263+
// which would otherwise refetch the token list/balances on every change.
264+
const preselectRef = useRef(preselectTokenAddress);
265+
251266
// Load token list then fetch all balances
252267
useEffect(() => {
253268
let cancelled = false;
@@ -259,7 +274,13 @@ export function TransferPage({ address, activeTab, onTabChange, onDisconnect, ke
259274
const list = await getTokens(NETWORK.middlewareUrl);
260275
if (cancelled) return;
261276
setTokens(list);
262-
if (list.length > 0) setSelectedToken(list[0]);
277+
if (list.length > 0) {
278+
const pre = preselectRef.current;
279+
const match = pre
280+
? list.find((t) => t.address.toLowerCase() === pre.toLowerCase())
281+
: undefined;
282+
setSelectedToken(match ?? list[0]);
283+
}
263284
const entries = await Promise.all(
264285
list.map(async (t) => {
265286
try {

0 commit comments

Comments
 (0)