Skip to content

[RELEASE] chore: parallel UX polish on tangle-dapp + leaderboard (indexer-unavailable handling)#3211

Merged
drewstone merged 3 commits into
developfrom
chore/dapp-leaderboard-polish
May 14, 2026
Merged

[RELEASE] chore: parallel UX polish on tangle-dapp + leaderboard (indexer-unavailable handling)#3211
drewstone merged 3 commits into
developfrom
chore/dapp-leaderboard-polish

Conversation

@tangletools
Copy link
Copy Markdown
Contributor

Parallel polish pass on tangle-dapp and leaderboard so both apps stop reading as "broken" when the Envio indexer is offline (current state of `mainnet-gql.tangle.tools` and `testnet-gql.tangle.tools` → CF 520/521). Same playbook as the tangle-cloud polish in #3210 — graceful empty states, honest copy, no fake "Locked" pseudo-grids — applied here.

Two parallel subagents wrote the two app passes concurrently; this PR squashes them together.

tangle-dapp

  • `ProtocolStatisticCard` ("Now Live: Season 1" hero) now wires `error` + `refetch` from `useOperators` + `useDelegatorCount`. When unhealthy, renders "Network data temporarily unavailable. Retrying automatically." with an `IconButton` retry, instead of blank "—" stats. Hooks an `useIndexerStatus` heartbeat through too.
  • `StatsItem` no longer prints the literal string `"Error"` (was rendering `error.name` directly).
  • `UserStakingOverview` empty state split by `useAccount().isConnected` — connected users see "No active positions — deposit a supported asset below"; disconnected users see "Connect a wallet to view your positions" with a note that the asset catalog below still loads without a wallet.
  • `StakingAssetsTable` no longer claims "No staking assets are configured for this network" when the indexer is just unhealthy — falls back to the right empty state via `IndexerStatusContext`.
  • Stayed entirely inside `@tangle-network/ui-components` (`Card`, `Typography`, `IconButton`, `TableStatus`) — no sandbox-ui imports added under `apps/tangle-dapp/`.

leaderboard

  • `executeEnvioGraphQL` now throws a typed `EnvioRequestError` carrying the HTTP status (`0` for DNS / CORS / connection-refused / abort). Exports `isEnvioUnavailableError(err)` — `true` for status `0`, `>= 500`, or raw fetch `TypeError`.
  • `useLeaderboard` / `useRoleAccounts` / `useRoleCounts` / `useIndexingProgress` now `retry: shouldRetryEnvioQuery` — immediately give up on indexer-unavailable errors instead of burning the default ~7s of exponential backoff. `refetchInterval` is paused while in an error state, then re-arms on the next successful fetch.
  • `LeaderboardTable` empty-state branching reordered so the error branch wins over `isPending`: the spinner can no longer get stuck behind a query that's effectively in TanStack's "pending while error" state. Indexer-unavailable errors now render "Leaderboard data is temporarily unavailable" with the raw error message + a wired-up Retry button.
  • Fixed a real bug while there: Launch-dApp button had `target="blank"` (literal string, not `_blank`) — switched to `target="_blank" rel="noopener noreferrer"`.

Verification

  • `yarn typecheck` ✓
  • `yarn lint` ✓
  • `yarn build:tangle-dapp` ✓
  • `yarn build:leaderboard` ✓

`[RELEASE]` tag → auto-sync workflow promotes to master on merge.

drewstone added 3 commits May 14, 2026 08:31
…d states on dashboard

When the Envio indexer returns 5xx (current Cloudflare 520/521 condition on
mainnet-gql / testnet-gql), the dashboard's "Now Live: Season 1" card rendered
a row of EMPTY_VALUE_PLACEHOLDER dashes for Stakers / Operators, the
"Your Position" card showed a single-line "No positions yet" with no context,
and the "Stake Assets" table fell back to "No staking assets are configured
for this network" which reads as a misconfiguration rather than a transient
indexer outage. Three surfaces, one root cause, three honest empty states.

ProtocolStatisticCard
- Read `error`/`refetch` from `useOperators` + `useDelegatorCount` and
  `isHealthy`/`checkHealth` from `IndexerStatusContext`. When the indexer is
  unhealthy (or the delegator count query errored, or operators errored with
  an empty on-chain fallback), surface a single muted line "Network data
  temporarily unavailable. Retrying automatically." with an IconButton retry
  that re-runs the health check and both queries.
- Pass `error` through to the Stakers/Operators `StatsItem`s so their values
  render as EMPTY_VALUE_PLACEHOLDER in the muted color rather than the bad
  `error.name` fallback (which previously printed literal "Error").

StatsItem
- Replace the `error.name` value with a muted EMPTY_VALUE_PLACEHOLDER so a
  failed stat reads as "unknown" rather than printing the class name.

UserStakingOverview
- Split the wallet-connected vs wallet-disconnected empty state: connected
  users see "No active positions — deposit a supported asset below to start
  earning"; disconnected users see "Connect a wallet to view your positions"
  with a clarifying note that the asset catalog below loads without a wallet.
  Both wrap a WalletLineIcon-prefixed card consistent with the rest of the
  dashboard's glass styling — no four-cell pseudo-grid.

StakingAssetsTable
- When `tableData` is empty AND `IndexerStatusContext.isHealthy === false`,
  render a "Network data temporarily unavailable" status with copy that
  differs by wallet-connected state (connected: just wait; disconnected:
  connect a wallet to read directly from chain). Keeps the "No stake assets"
  message only for the genuine misconfiguration case.

Verified
- `yarn nx run tangle-dapp:typecheck --skip-nx-cache` clean
- `yarn nx run tangle-dapp:lint --skip-nx-cache` clean
- `yarn nx run tangle-dapp:build --skip-nx-cache` clean; new strings
  ("Network data temporarily unavailable", "Retrying automatically",
  "Connect a wallet to view your positions") present in the production bundle
…f perpetual spinner

When the Envio indexer is unreachable (Cloudflare 5xx, network failure,
DNS, CORS, etc.) the leaderboard now renders an actionable empty state
with the underlying error and a Retry button, rather than spinning on
"Loading..." while React Query burns retry budget against a dead origin.

- executeEnvioGraphQL throws a typed EnvioRequestError that captures the
  HTTP status (0 for network failure) so callers can short-circuit retry
  loops on origin outages.
- useLeaderboard, useRoleAccounts, useRoleCounts, and useIndexingProgress
  skip retries on EnvioRequestError-style failures and pause their
  refetchInterval once the cached query is in an error state. Polls
  resume on the next successful fetch (i.e. the user-triggered Retry).
- LeaderboardTable detects the unavailable-indexer error class and
  swaps the empty-state copy from a generic "try again later" to
  "Leaderboard data is temporarily unavailable" with the captured
  message; the inline stale-data overlay uses the same wording.
- Drive-by: fix the leaderboard Navbar "Launch dApp" anchor so it uses
  target="_blank" with rel="noopener noreferrer" (was target="blank").
…exer-unavailable handling)

Wrapper [RELEASE] commit so auto-sync promotes both agent commits below to
master in one go. Two parallel agents shipped this work concurrently:

- 1e90ca8 chore(tangle-dapp): graceful indexer-unavailable + wallet-disconnected
  states on dashboard. Wires error/refetch from useOperators + useDelegatorCount
  through ProtocolStatisticCard so the 'Now Live: Season 1' card reads as
  'transient outage' instead of blank '— / —' when the Envio indexer is offline.
  Splits UserStakingOverview + StakingAssetsTable empty states by isConnected so
  disconnected visitors see useful copy, not just '0 positions'. Stayed in
  @tangle-network/ui-components (Card/Typography/IconButton/TableStatus) — no
  sandbox-ui leaked into apps/tangle-dapp/.

- 5414be2 chore(leaderboard): surface indexer-unavailable empty state instead
  of perpetual spinner. executeEnvioGraphQL now throws a typed EnvioRequestError
  with HTTP status (0 for network/CORS/DNS failures); useLeaderboard /
  useRoleAccounts / useRoleCounts / useIndexingProgress skip retry and pause
  refetchInterval on 5xx/network errors instead of burning 7s of exp-backoff.
  LeaderboardTable error branch now wins over isPending so the spinner can no
  longer get stuck behind a perpetually-pending query. Fixed a real bug while
  there: Launch-dApp button had target='blank' (literal string) instead of
  target='_blank' rel='noopener noreferrer'.

Verified: yarn typecheck, yarn lint, yarn build:tangle-dapp, yarn build:leaderboard
all clean on the post-commit tree.
@tangletools tangletools requested a review from AtelyPham as a code owner May 14, 2026 14:33
@drewstone drewstone merged commit 5555f46 into develop May 14, 2026
8 checks passed
@drewstone drewstone deleted the chore/dapp-leaderboard-polish branch May 14, 2026 15:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants