diff --git a/apps/tangle-cloud/src/app/app.tsx b/apps/tangle-cloud/src/app/app.tsx index dd7ee9987..88564efcb 100644 --- a/apps/tangle-cloud/src/app/app.tsx +++ b/apps/tangle-cloud/src/app/app.tsx @@ -1,6 +1,7 @@ import { Navigate, Route, Routes } from 'react-router'; import { lazy, Suspense, type FC, type ReactNode } from 'react'; import Layout from '../components/Layout'; +import ScrollToTop from '../components/ScrollToTop'; import Providers from './providers'; import { PagePath } from '../types'; import { Skeleton } from '@tangle-network/sandbox-ui/primitives'; @@ -70,6 +71,7 @@ const withLayout = (LayoutCmp: FC<{ children: ReactNode }>, Page: FC) => ( const App: FC = () => { return ( + ` is path-aware but + * scroll-naive: navigating from a long /blueprints listing to /rewards + * lands the next page already scrolled wherever the previous page was, + * which makes transitions feel broken (the user has to scroll up to + * read the heading of the new page). + * + * Drop this component anywhere inside the router tree — it renders + * `null` and only runs an effect. + * + * NOTE: pathname-only dependency is deliberate. We do NOT scroll on + * search-param or hash changes, because some pages use those for in-page + * tab state and an unsolicited scroll would yank the viewport away. + */ +const ScrollToTop = () => { + const { pathname } = useLocation(); + + useEffect(() => { + // Use 'instant' (not 'smooth') so the new page is at the top before + // the first paint — smooth-scroll across route transitions looks + // janky because the previous page's content briefly remains visible. + window.scrollTo({ top: 0, left: 0, behavior: 'instant' }); + }, [pathname]); + + return null; +}; + +export default ScrollToTop; diff --git a/apps/tangle-cloud/src/components/Sidebar.tsx b/apps/tangle-cloud/src/components/Sidebar.tsx index 634d42918..b81de4ac4 100644 --- a/apps/tangle-cloud/src/components/Sidebar.tsx +++ b/apps/tangle-cloud/src/components/Sidebar.tsx @@ -66,7 +66,7 @@ const SIDEBAR_ITEMS: SidebarItem[] = [ Icon: CoinsLineIcon, }, { - name: 'Payments', + name: 'Private Payments', href: PagePath.PAYMENTS_POOL, isInternal: true, Icon: ShieldKeyholeLineIcon, diff --git a/apps/tangle-cloud/src/pages/rewards/page.tsx b/apps/tangle-cloud/src/pages/rewards/page.tsx index ee5da966d..e0f702f7f 100644 --- a/apps/tangle-cloud/src/pages/rewards/page.tsx +++ b/apps/tangle-cloud/src/pages/rewards/page.tsx @@ -415,7 +415,18 @@ const RewardsPage: FC = () => { ) : rewardHistoryError ? ( - Could not load claim history. + // Render an empty-state instead of a red error when the + // indexer is simply absent (e.g. `testnet` env with no + // VITE_ENVIO_TESTNET_ENDPOINT configured). The original + // `` here surfaced an alarming + // "Could not load claim history" banner on every page + // load — that's the UX equivalent of a 500, but the + // actual cause is "no historical data source", which is + // a legitimate empty state. + ) : rewardHistory?.length ? (