Skip to content

Toast confirmation + responsive fix, wallet refresh, App offline/404 handling, and sim/sequence/account-switch fixes - #987

Open
Themancalledpg wants to merge 5 commits into
Sorokit:mainfrom
Themancalledpg:fix/sorokit-ui-assigned-batch
Open

Themancalledpg wants to merge 5 commits into
Sorokit:mainfrom
Themancalledpg:fix/sorokit-ui-assigned-batch

Conversation

@Themancalledpg

Copy link
Copy Markdown
Contributor

Summary

Batches four assigned issues: a copy-confirmation toast and responsive overlap fix for InfoCell/LabelledValue, a manual and pull-to-refresh flow for WalletScreen (plus a real pre-existing focus-restore bug found while building it), an offline banner/skip-link/404 fallback for App, and three real missing behaviors in SorobanPanel/TransactionPanel around wallet disconnect, sequence desync, and account switching.

closes #964
closes #965
closes #966
closes #967

Changes

  • Add copy action and responsive label wrapping in InfoCell & LabelledValue #964 - Copy toast and responsive overlap fix: InfoCell and LabelledValue now show a success toast ("X Copied") after a successful clipboard copy, using the existing useToast() context. Root-caused the reported "label overlaps value on narrow screens" to AccountCard's non-responsive grid-cols-2 combined with missing min-w-0 on the grid children; fixed the grid to fall back to a single column below 360px and added min-w-0/break-words inside both components so they are robust regardless of their container.

  • Add pull-to-refresh on mobile and focus trap in WalletScreen #965 - Manual + pull-to-refresh on WalletScreen: Added a Refresh button next to Disconnect (shows isLoadingAccount via the existing Button loading prop) and a new usePullToRefresh hook implementing a touch-based pull-to-refresh gesture, armed only when the scroll container is already at scrollTop === 0. Both call refreshAccount(). While writing the focus-trap test for the existing QR modal, found the "Show QR" button was never registered as the Radix Dialog's trigger (it only drove open externally via onOpenChange), so Dialog's own onCloseAutoFocus handler pre-empted FocusScope's restore-on-close and silently left focus on document.body instead of returning it to the button that opened the modal. Fixed by wrapping the trigger in <Dialog.Trigger asChild>. Also discovered and fixed a regression this toast change caused in NetworkScreen's two test files (InfoCell now calls useToast(), which they rendered without mocking).

  • Add global offline status banner and skip-to-content link in App.tsx #966 - Offline banner, skip link, 404 fallback in App: Added an OfflineBanner listening to the browser's online/offline events, a visually-hidden "Skip to main content" link that is the first focusable element on the page, and a NotFoundCard for any URL that does not match a known section path. sectionForPath previously fell back to "wallet" silently for literally any unmapped path, so an unknown route silently rendered the wallet section rather than erroring loudly or showing nothing; added isKnownPath to nav-routes.ts so App can tell the two cases apart. The 404 card takes priority over both the connect screen and the dashboard so it shows regardless of connection state.

  • Handle wallet disconnect during simulation and ledger desync in wallet-flow.test.tsx #967 - Disconnect-during-simulation, sequence resync, account-switch draft reset: All three acceptance criteria described real missing behavior, not just missing tests.

    • SorobanPanel: added a "disconnected" state. An effect watching isConnected aborts the in-flight simulate/invoke AbortController and shows a reconnect prompt (with a "Reconnect wallet" button) if the wallet disconnects while a request is pending, instead of leaving a stale loading spinner or a misleading generic error.
    • TransactionPanel: factored isSequenceError() out of friendlyError's existing tx_bad_seq/"bad sequence" substring check, and now calls refreshAccount() automatically when a submission fails with a stale sequence, instead of only telling the user to refresh manually.
    • TransactionPanel: the draft payment form (destination/amount/memo/asset) was never cleared on an account change. This app has no in-place "switch account" primitive (address only ever changes via disconnect to null, then reconnecting as a possibly different account), so the new effect clears the draft on any address change, including through a disconnect, rather than risking a stale draft being submitted against the newly active account.

Test plan

  • src/components/ui/InfoCell.test.tsx / LabelledValue.test.tsx: added toast-confirmation tests and a responsive wrapping (#964) describe block (26 tests total, all passing).
  • src/screens/WalletScreen.test.tsx: added manual refresh (#965), pull-to-refresh (#965), and QR modal accessibility (#965) describe blocks (21 tests total, all passing, including the focus-trap/restore test against the real Radix behavior).
  • src/screens/NetworkScreen.test.tsx / NetworkScreen.race.test.tsx: added a useToast mock to fix the regression from Add copy action and responsive label wrapping in InfoCell & LabelledValue #964 (both files fully passing again).
  • src/App.test.tsx: added offline banner (#966), skip-to-content link (#966), and 404 fallback for unknown routes (#966) describe blocks (17 tests total, all passing).
  • src/__tests__/wallet-flow.test.tsx: added three new integration tests rendering SorobanPanel/TransactionPanel against a real SorokitProvider and mock client (not just mocked hook state) for each Handle wallet disconnect during simulation and ledger desync in wallet-flow.test.tsx #967 scenario: disconnect mid-simulate, tx_bad_seq auto-refetch, and account-switch draft reset.
  • Full suite: npx vitest run passes 1916/1916 tests across all 100 test files. npx tsc --noEmit -p . and npx eslint src/ are both clean.

Pre-existing, unrelated to this PR: build:content and check:links are known to be red on a clean checkout of main in this org's sibling repos; not applicable here since this repo has no such scripts, but noting per standing disclosure practice. No other pre-existing failures were found; the full test suite was green before and after this batch aside from the one regression (#965/NetworkScreen) that this PR itself fixes.

…ell/LabelledValue (Sorokit#964)

Adds a success toast when copying a value from InfoCell/LabelledValue, and
fixes the label/value overlap on screens narrower than 360px by giving the
grid in AccountCard a single-column fallback and adding min-w-0/break-words
to both components so their text can shrink and wrap instead of overflowing.
…s restore (Sorokit#965)

Adds a manual Refresh button and a touch-based pull-to-refresh gesture (new
usePullToRefresh hook) to WalletScreen, both calling refreshAccount().

While wiring this up, found that the QR modal's "Show QR" button was never
registered as the Radix Dialog's trigger (open/close was driven externally
via onOpenChange with no <Dialog.Trigger>), so Dialog's own onCloseAutoFocus
handler pre-empted FocusScope's restore and focus silently fell back to
document.body on close instead of returning to the button that opened it.
Fixed by wrapping the trigger button in <Dialog.Trigger asChild>.

Also mocks @/context/ToastContext in NetworkScreen's test files, which
started rendering InfoCell without a ToastProvider after Sorokit#964 added a
useToast() call inside InfoCell.
…pp (Sorokit#966)

Adds an OfflineBanner that listens for the browser's online/offline events
and shows a persistent notice while connectivity is down, a visually-hidden
"Skip to main content" link that's the first focusable element on the page,
and a NotFoundCard shown for any URL that doesn't match a known dashboard
path (sectionForPath previously fell back to "wallet" silently for any
unmapped path, which is what a visitor landing on an unknown route actually
saw instead of a blank layout). Added isKnownPath to nav-routes.ts so App
can tell "unmapped path" apart from a normal section route.

The 404 card takes priority over both the connect screen and the dashboard,
so it shows for an unknown path whether or not the wallet is connected.
…count-switch draft reset (Sorokit#967)

Three integration-test gaps from the issue each pointed to a real missing
behavior, not just missing coverage:

- SorobanPanel: disconnecting the wallet while a simulate/invoke call was
  in flight had no handling at all, so the stale request would land with
  no reconnect path other than refreshing the page. Added a "disconnected"
  state: an effect watching isConnected aborts the in-flight AbortController
  and shows a reconnect prompt instead.

- TransactionPanel: a tx_bad_seq submission error only ever showed a
  friendly message asking the user to "refresh and try again" manually.
  Added isSequenceError() (factored out of friendlyError's existing
  substring check) and call refreshAccount() automatically when it matches.

- TransactionPanel: the draft payment form (destination/amount/memo) was
  never cleared when the active account changed. This app has no in-place
  "switch account" primitive, since address only ever changes via
  disconnect (to null) then reconnecting as a possibly-different account,
  so the new effect clears the draft on any address change, including
  through a disconnect, rather than risking a stale draft getting
  submitted against the new account.

Added integration tests for all three in wallet-flow.test.tsx, rendering
SorobanPanel/TransactionPanel against a real SorokitProvider and mock
client rather than asserting against mocked hook state.
@drips-wave

drips-wave Bot commented Oct 16, 2026

Copy link
Copy Markdown

@Themancalledpg Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants