66@inject WalletStorageService WalletStorage
77@inject TransactionTrackerService TxTracker
88@inject NavigationManager Nav
9+ @inject QubicSettingsService Settings
10+ @inject AssetCacheService AssetCache
11+ @inject IJSRuntime JS
912@using Maui .Biometric
1013@implements IDisposable
1114
@@ -206,6 +209,8 @@ else if (!Seed.HasSeed)
206209else
207210{
208211 <!-- Dashboard — vault unlocked, seed active -->
212+ <div class =" pull-indicator" ><i class =" bi bi-arrow-clockwise pull-icon" ></i ></div >
213+
209214 <div class =" qb-balance" >
210215 <div class =" qb-balance-label" >
211216 @if (Seed .ActiveLabel != null )
@@ -227,12 +232,6 @@ else
227232 <i class =" bi bi-wifi me-1" ></i > Connect
228233 </button >
229234 }
230- else
231- {
232- <button class =" btn btn-outline-secondary btn-sm w-100 mb-3" @onclick =" RefreshNow" >
233- <i class =" bi bi-arrow-clockwise me-1" ></i > Refresh
234- </button >
235- }
236235
237236 <!-- Quick Actions -->
238237 <div class =" qb-actions" >
@@ -253,6 +252,39 @@ else
253252 History
254253 </a >
255254 </div >
255+
256+ <!-- Assets -->
257+ @if (AssetCache .Assets != null && AssetCache .Assets .Count > 0 )
258+ {
259+ <div class =" d-flex align-items-center justify-content-between mb-2" >
260+ <div class =" fw-bold small text-muted text-uppercase" style =" letter-spacing :0.05em " >Assets </div >
261+ <a href =" /assets" class =" small" style =" text-decoration :none " >Manage <i class =" bi bi-chevron-right" style =" font-size :0.6rem " ></i ></a >
262+ </div >
263+ @foreach ( var asset in AssetCache .Assets )
264+ {
265+ <div class =" card" >
266+ <div class =" card-body py-2" >
267+ <div class =" d-flex justify-content-between align-items-center" >
268+ <div >
269+ <div class =" fw-bold" >@asset.Name </div >
270+ <div class =" mono text-muted" style =" font-size :0.65rem " >@asset.Issuer [..Math .Min (16 , asset .Issuer .Length )].. .</div >
271+ </div >
272+ <div class =" fw-bold" >@asset.Amount.ToString( " N0" )</div >
273+ </div >
274+ </div >
275+ </div >
276+ }
277+ }
278+ else if (AssetCache .Assets != null && AssetCache .Assets .Count == 0 )
279+ {
280+ <div class =" d-flex align-items-center justify-content-between mb-2" >
281+ <div class =" fw-bold small text-muted text-uppercase" style =" letter-spacing :0.05em " >Assets </div >
282+ <a href =" /assets" class =" small" style =" text-decoration :none " >Manage <i class =" bi bi-chevron-right" style =" font-size :0.6rem " ></i ></a >
283+ </div >
284+ <div class =" text-center text-muted py-3 small" >
285+ <i class =" bi bi-coin" ></i > No assets found
286+ </div >
287+ }
256288}
257289
258290@code {
282314 long ? _balance ;
283315 bool _wasConnected ;
284316 System .Threading .Timer ? _refreshTimer ;
317+ DotNetObjectReference <Home >? _selfRef ;
285318
286319 const string BiometricPasswordKey = " vault_bio_password" ;
287320
@@ -293,9 +326,10 @@ else
293326
294327 protected override async Task OnInitializedAsync ()
295328 {
296- Seed .OnSeedChanged += OnStateChanged ;
329+ Seed .OnSeedChanged += OnSeedChanged ;
297330 TickMonitor .OnTickChanged += OnTickChanged ;
298331 Vault .OnVaultChanged += OnStateChanged ;
332+ AssetCache .OnAssetsChanged += OnStateChanged ;
299333
300334 // Check if biometric is available and a password is stored
301335 await CheckBiometricAvailability ();
@@ -307,6 +341,22 @@ else
307341 }
308342 }
309343
344+ protected override async Task OnAfterRenderAsync (bool firstRender )
345+ {
346+ if (firstRender )
347+ {
348+ _selfRef = DotNetObjectReference .Create (this );
349+ try { await JS .InvokeVoidAsync (" pullRefresh.init" , " qb-content" , _selfRef ); } catch { }
350+ }
351+ }
352+
353+ [JSInvokable ]
354+ public async Task OnPullRefresh ()
355+ {
356+ await FetchBalanceAndAssets ();
357+ await InvokeAsync (StateHasChanged );
358+ }
359+
310360 async Task CheckBiometricAvailability ()
311361 {
312362 try
@@ -523,17 +573,16 @@ else
523573 await TickMonitor .StartAsync ();
524574 }
525575
526- async Task RefreshNow ()
527- {
528- await FetchBalanceAndAssets ();
529- }
530-
531576 async Task FetchBalanceAndAssets ()
532577 {
533578 if (Seed .Identity == null || ! TickMonitor .IsConnected ) return ;
534579 try
535580 {
536- var info = await Backend .GetBalanceAsync (Seed .Identity .Value );
581+ var balanceTask = Backend .GetBalanceAsync (Seed .Identity .Value );
582+ var assetsTask = AssetCache .RefreshAsync ();
583+ await Task .WhenAll (balanceTask , assetsTask );
584+
585+ var info = await balanceTask ;
537586 if (info != null )
538587 _balance = info .Amount ;
539588 }
@@ -573,16 +622,27 @@ else
573622 _refreshTimer = null ;
574623 }
575624
625+ void OnSeedChanged ()
626+ {
627+ _balance = null ;
628+ if (TickMonitor .IsConnected )
629+ _ = FetchBalanceAndAssets ();
630+ InvokeAsync (StateHasChanged );
631+ }
632+
576633 void OnStateChanged ()
577634 {
578635 InvokeAsync (StateHasChanged );
579636 }
580637
581638 public void Dispose ()
582639 {
583- Seed .OnSeedChanged -= OnStateChanged ;
640+ Seed .OnSeedChanged -= OnSeedChanged ;
584641 TickMonitor .OnTickChanged -= OnTickChanged ;
585642 Vault .OnVaultChanged -= OnStateChanged ;
643+ AssetCache .OnAssetsChanged -= OnStateChanged ;
586644 StopRefreshTimer ();
645+ try { _ = JS .InvokeVoidAsync (" pullRefresh.dispose" , " qb-content" ); } catch { }
646+ _selfRef ? .Dispose ();
587647 }
588648}
0 commit comments