Skip to content

Commit 21e93a4

Browse files
authored
feat(minifront-v2): comprehensive UX polish (#2654)
# feat(minifront-v2): comprehensive UX polish ### real-time chain sync · persistent transaction toasts · skeleton loading · visual tweaks --- ## Description of Changes ### 1. Real-time chain-sync experience - **`StatusStore`** (MobX) tracks sync state. - **`SyncingDialog`** with progress %, ETA & graceful error handling. - **`StatusPopover`** now has three states (synced / syncing / error). - `PenumbraService` exposes `getStatus()` and a status streaming helper. - Animated spinning Penumbra illustration while syncing. - Header & mobile-nav styling fixes (proper `z-index`, smoother animations, shield background). ### 2. Transaction flow overhaul - **Persistent success/error toasts** covering _plan → build → broadcast → confirm_. - Shared toast utilities reused in `transfer-store`, `staking-store`, `withdraw-store`, IBC deposit/withdraw. - Auto-refresh of balances & transaction list after success. - Removed inline error blocks for a cleaner UI. ### 3. Transaction list visual polish - Card height constraints (`min-h: 120 px`, `max-h: 840 px`) with internal scrolling. - **Global slim / transparent scrollbar styling** (matches Veil design). - Removed `maxItems` cap on `ShieldingTransactionCard`; all transactions are shown. - Friendlier empty-state copy encourages a page refresh. ### 4. Skip deposit skeleton loader - Lightweight skeleton displayed while the external iframe loads for faster perceived performance. --- ## Motivation Brings **minifront-v2** nearly to feature-parity with the legacy front-end (only _Swap_ page is missing) * Users get immediate feedback during long operations (chain sync & transactions). * Eliminates layout jumps and scroll jank for a polished, professional feel. * Provides modern error-recovery mechanisms and UX refinements. --- ## Tested on mainnet 1. **Chain sync** - Switch from testnet to mainnet to trigger sync modal and bar 2. **Transaction feedback (Toast)** - Perform transfer / stake / manual deposit & withdraw (no Toast for skip deposits) - Observe persistent success toast and automatic balance + list refresh. 3. **Long transaction list** - Seed many transactions. - Ensure the list scrolls inside the card and global scrollbar styling is applied. 4. **Skip deposit** - Open deposit view, skeleton loader should show before iframe content. ## Checklist Before Requesting Review _No breaking changes; all updates are confined to minifront-v2 components, it's MobX stores, and styling._
1 parent 075d2bb commit 21e93a4

33 files changed

+1138
-233
lines changed

.changeset/wide-pianos-hang.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'minifront-v2': minor
3+
---
4+
5+
UX Polishment incl. new Toast component and SyncDialog

apps/minifront-v2/src/app/global.css

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,28 @@ body > section[aria-live='polite'] {
2828
background-repeat: no-repeat !important;
2929
background-attachment: fixed !important;
3030
}
31+
32+
/* Global scrollbar styling - matching veil app */
33+
::-webkit-scrollbar {
34+
width: 10px;
35+
}
36+
37+
::-webkit-scrollbar-track {
38+
background: transparent;
39+
backdrop-filter: blur(16px);
40+
}
41+
42+
::-webkit-scrollbar-thumb {
43+
background-color: #fafafa1a;
44+
border-left: 2px solid transparent;
45+
border-right: 2px solid transparent;
46+
background-clip: content-box;
47+
border-radius: 5px;
48+
transition: all 0.3s ease;
49+
}
50+
51+
::-webkit-scrollbar-thumb:hover {
52+
background-color: #a3a3a3;
53+
border-left: 1px solid transparent;
54+
border-right: 1px solid transparent;
55+
}

apps/minifront-v2/src/app/layout.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { HeadTag } from './head-tag';
44
import { Header, SyncBar } from '@/widgets/header';
55
import { BackgroundProvider } from '@/shared/contexts/background-context';
66
import { PageBackground } from '@/shared/components/page-background';
7+
import { SyncingDialog } from '@/widgets/sync-dialog';
78

89
export const Layout = () => {
910
return (
@@ -15,14 +16,13 @@ export const Layout = () => {
1516
<SyncBar />
1617

1718
<div className='flex w-full flex-col items-center'>
18-
<div className='w-full'>
19+
<div className='w-full pt-20'>
1920
<Header />
2021
<Outlet />
2122
</div>
2223
</div>
2324

24-
{/* TODO: add syncing dialog to v2 */}
25-
{/* <SyncingDialog /> */}
25+
<SyncingDialog />
2626
</Display>
2727
</BackgroundProvider>
2828
);

apps/minifront-v2/src/pages/portfolio/ui/portfolio.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ export const Portfolio = observer((): React.ReactNode => {
8383

8484
return (
8585
<div className='flex w-full flex-col items-center'>
86-
<div className='grid w-full flex-1 grid-cols-1 gap-4 md:grid-cols-2'>
86+
<div className='grid w-full flex-1 grid-cols-1 gap-4 md:grid-cols-2 max-w-screen-lg'>
8787
<div>
8888
<AssetCard accounts={accounts} showInfoButton={true} />
8989
</div>

apps/minifront-v2/src/pages/portfolio/ui/transactions/transaction-card.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ export const TransactionCard = observer(
146146
return (
147147
<div>
148148
<Card title={cardTitle} headerAction={headerAction} endContent={headerContent}>
149-
<div className='flex flex-col gap-1'>
149+
<div className='flex flex-col gap-1 max-h-[840px] min-h-[120px] overflow-y-auto'>
150150
{loadingTransactions ? (
151151
Array.from({ length: getSkeletonLength() }).map((_, i) => (
152152
<div key={i} className='h-16 w-full'>
@@ -213,7 +213,8 @@ export const TransactionCard = observer(
213213
<div className='text-muted-foreground flex min-h-[120px] flex-col items-center justify-center p-6 text-center'>
214214
<p className='text-sm'>You have no transactions yet.</p>
215215
<p className='mt-1 text-xs'>
216-
Send, receive, or trade assets to see your transaction history here.
216+
Send, receive, or trade assets to see your transaction history here or try
217+
refreshing the page in case they are not showing up.
217218
</p>
218219
</div>
219220
)}

apps/minifront-v2/src/pages/shielding/ui/deposit/deposit-form.tsx

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -451,15 +451,6 @@ const DepositFormInternal = observer(() => {
451451
/>
452452
</div>
453453

454-
{/* Error Display */}
455-
{depositState.error && (
456-
<div className='rounded-lg p-3'>
457-
<Text color='destructive.light' small>
458-
Failed: {depositState.error}
459-
</Text>
460-
</div>
461-
)}
462-
463454
{/* Submit Button */}
464455
<div className='mt-2'>
465456
<Density sparse>

apps/minifront-v2/src/pages/shielding/ui/shared/shielding-transaction-card.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ export const ShieldingTransactionCard = observer(() => {
2929
title='Your Recent Shielding Activity'
3030
showInfoButton={false}
3131
showSeeAllLink={false}
32-
maxItems={5}
3332
headerAction={headerAction}
3433
filteredTransactions={filteredTransactions}
3534
/>

apps/minifront-v2/src/pages/shielding/ui/skip-deposit/skip-deposit-form.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { observer } from 'mobx-react-lite';
2+
import { useState, useEffect } from 'react';
23
import { Widget } from '@skip-go/widget';
34
import { ArrowRightLeft } from 'lucide-react';
45
import { Button } from '@penumbra-zone/ui/Button';
56
import { Text } from '@penumbra-zone/ui/Text';
7+
import { Skeleton } from '@penumbra-zone/ui/Skeleton';
68
import { useIsConnected, useConnectWallet } from '@/shared/hooks/use-connection';
79

810
// Skip widget configuration based on legacy minifront
@@ -56,6 +58,20 @@ const theme = {
5658
export const SkipDepositTab = observer(() => {
5759
const isConnected = useIsConnected();
5860
const { connectWallet } = useConnectWallet();
61+
const [isWidgetLoading, setIsWidgetLoading] = useState(true);
62+
63+
// Show loading for initial widget load
64+
useEffect(() => {
65+
if (isConnected) {
66+
setIsWidgetLoading(true);
67+
// Set a minimum loading time for better UX
68+
const timer = setTimeout(() => {
69+
setIsWidgetLoading(false);
70+
}, 2000);
71+
72+
return () => clearTimeout(timer);
73+
}
74+
}, [isConnected]);
5975

6076
// If wallet is not connected, show connect wallet message
6177
if (!isConnected) {
@@ -76,6 +92,17 @@ export const SkipDepositTab = observer(() => {
7692
);
7793
}
7894

95+
// Show skeleton while widget is loading
96+
if (isWidgetLoading) {
97+
return (
98+
<div className='w-full py-6 flex flex-col gap-1'>
99+
<div className='h-[110px] w-full rounded-2xl bg-other-tonal-fill5 relative overflow-hidden before:absolute before:top-1/2 before:left-1/2 before:h-full before:w-full before:content-[""] before:-translate-x-1/2 before:-translate-y-1/2 before:animate-shimmer before:bg-linear-to-r before:from-transparent before:via-other-tonal-fill5 before:to-transparent' />
100+
<div className='h-[110px] w-full rounded-2xl bg-other-tonal-fill5 relative overflow-hidden before:absolute before:top-1/2 before:left-1/2 before:h-full before:w-full before:content-[""] before:-translate-x-1/2 before:-translate-y-1/2 before:animate-shimmer before:bg-linear-to-r before:from-transparent before:via-other-tonal-fill5 before:to-transparent' />
101+
<div className='h-[70px] w-full rounded-2xl bg-other-tonal-fill5 relative overflow-hidden before:absolute before:top-1/2 before:left-1/2 before:h-full before:w-full before:content-[""] before:-translate-x-1/2 before:-translate-y-1/2 before:animate-shimmer before:bg-linear-to-r before:from-transparent before:via-other-tonal-fill5 before:to-transparent' />
102+
</div>
103+
);
104+
}
105+
79106
return (
80107
<div className='w-full'>
81108
{/* Skip Widget */}

apps/minifront-v2/src/pages/shielding/ui/withdraw/withdraw-form.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ const WithdrawFormInternal: React.FC = observer(() => {
302302
)}
303303
</div>
304304

305+
{/* Error message - only show validation errors, transaction errors are handled by toasts */}
305306
{withdrawState.error && (
306307
<div className='rounded-lg p-3'>
307308
<Text color='destructive.light' small>

apps/minifront-v2/src/pages/stake/ui/delegate-dialog/index.tsx

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Dialog } from '@penumbra-zone/ui/Dialog';
33
import { Button } from '@penumbra-zone/ui/Button';
44
import { Text } from '@penumbra-zone/ui/Text';
55
import { Card } from '@penumbra-zone/ui/Card';
6-
import { Info, CircleX } from 'lucide-react';
6+
import { Info } from 'lucide-react';
77
import { useStakingStore, useBalancesStore } from '@/shared/stores/store-context';
88
import { ValidatorRow } from '../validator-row';
99
import { AssetValueInput } from '@penumbra-zone/ui/AssetValueInput';
@@ -161,15 +161,6 @@ export const DelegateDialog = observer(() => {
161161
</Text>
162162
</div>
163163
)}
164-
{/* Error Message */}
165-
{stakingStore.error && (
166-
<div className='flex items-center gap-3 rounded-sm bg-destructive-light p-3'>
167-
<CircleX size={22} className='text-destructive-dark' />
168-
<Text small color='destructive.dark'>
169-
{stakingStore.error}
170-
</Text>
171-
</div>
172-
)}
173164

174165
{/* Validator Info */}
175166
<div className='flex flex-col rounded-md overflow-hidden gap-1'>

0 commit comments

Comments
 (0)