From 1a2844ffdf7b53237629b8fb21c192744d9a80ec Mon Sep 17 00:00:00 2001 From: natsee Date: Fri, 26 Jan 2024 20:43:34 +0100 Subject: [PATCH] Liquid dashboard: Fix double api call --- .../federation-addresses-list.component.ts | 20 ++--- .../federation-utxos-list.component.ts | 20 ++--- .../reserves-audit-dashboard.component.ts | 20 ++--- .../src/app/dashboard/dashboard.component.ts | 90 ++++++++----------- 4 files changed, 65 insertions(+), 85 deletions(-) diff --git a/frontend/src/app/components/liquid-reserves-audit/federation-addresses-list/federation-addresses-list.component.ts b/frontend/src/app/components/liquid-reserves-audit/federation-addresses-list/federation-addresses-list.component.ts index 8796ecf27..67bb68193 100644 --- a/frontend/src/app/components/liquid-reserves-audit/federation-addresses-list/federation-addresses-list.component.ts +++ b/frontend/src/app/components/liquid-reserves-audit/federation-addresses-list/federation-addresses-list.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit, ChangeDetectionStrategy, Input } from '@angular/core'; -import { Observable, combineLatest, concat, of } from 'rxjs'; -import { delay, filter, map, share, skip, switchMap, tap, throttleTime } from 'rxjs/operators'; +import { Observable, combineLatest, of, timer } from 'rxjs'; +import { delayWhen, filter, map, share, shareReplay, switchMap, tap, throttleTime } from 'rxjs/operators'; import { ApiService } from '../../../services/api.service'; import { Env, StateService } from '../../../services/state.service'; import { AuditStatus, CurrentPegs, FederationAddress } from '../../../interfaces/node-api.interface'; @@ -28,6 +28,7 @@ export class FederationAddressesListComponent implements OnInit { currentPeg$: Observable; lastPegBlockUpdate: number = 0; lastPegAmount: string = ''; + isLoad: boolean = true; constructor( private apiService: ApiService, @@ -42,15 +43,12 @@ export class FederationAddressesListComponent implements OnInit { this.skeletonLines = this.widget === true ? [...Array(5).keys()] : [...Array(15).keys()]; if (!this.widget) { this.websocketService.want(['blocks']); - this.auditStatus$ = concat( - this.apiService.federationAuditSynced$().pipe(share()), - this.stateService.blocks$.pipe( - skip(1), - throttleTime(40000), - delay(2000), - switchMap(() => this.apiService.federationAuditSynced$()), - share() - ) + this.auditStatus$ = this.stateService.blocks$.pipe( + throttleTime(40000), + delayWhen(_ => this.isLoad ? timer(0) : timer(2000)), + tap(() => this.isLoad = false), + switchMap(() => this.apiService.federationAuditSynced$()), + shareReplay(1) ); this.currentPeg$ = this.auditStatus$.pipe( diff --git a/frontend/src/app/components/liquid-reserves-audit/federation-utxos-list/federation-utxos-list.component.ts b/frontend/src/app/components/liquid-reserves-audit/federation-utxos-list/federation-utxos-list.component.ts index cbeae4d49..39c2c577e 100644 --- a/frontend/src/app/components/liquid-reserves-audit/federation-utxos-list/federation-utxos-list.component.ts +++ b/frontend/src/app/components/liquid-reserves-audit/federation-utxos-list/federation-utxos-list.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit, ChangeDetectionStrategy, Input } from '@angular/core'; -import { Observable, combineLatest, concat, of } from 'rxjs'; -import { delay, filter, map, share, skip, switchMap, tap, throttleTime } from 'rxjs/operators'; +import { Observable, combineLatest, of, timer } from 'rxjs'; +import { delayWhen, filter, map, share, shareReplay, switchMap, tap, throttleTime } from 'rxjs/operators'; import { ApiService } from '../../../services/api.service'; import { Env, StateService } from '../../../services/state.service'; import { AuditStatus, CurrentPegs, FederationUtxo } from '../../../interfaces/node-api.interface'; @@ -28,6 +28,7 @@ export class FederationUtxosListComponent implements OnInit { currentPeg$: Observable; lastPegBlockUpdate: number = 0; lastPegAmount: string = ''; + isLoad: boolean = true; constructor( private apiService: ApiService, @@ -42,15 +43,12 @@ export class FederationUtxosListComponent implements OnInit { this.skeletonLines = this.widget === true ? [...Array(5).keys()] : [...Array(15).keys()]; if (!this.widget) { this.websocketService.want(['blocks']); - this.auditStatus$ = concat( - this.apiService.federationAuditSynced$().pipe(share()), - this.stateService.blocks$.pipe( - skip(1), - throttleTime(40000), - delay(2000), - switchMap(() => this.apiService.federationAuditSynced$()), - share() - ) + this.auditStatus$ = this.stateService.blocks$.pipe( + throttleTime(40000), + delayWhen(_ => this.isLoad ? timer(0) : timer(2000)), + tap(() => this.isLoad = false), + switchMap(() => this.apiService.federationAuditSynced$()), + shareReplay(1) ); this.currentPeg$ = this.auditStatus$.pipe( diff --git a/frontend/src/app/components/liquid-reserves-audit/reserves-audit-dashboard/reserves-audit-dashboard.component.ts b/frontend/src/app/components/liquid-reserves-audit/reserves-audit-dashboard/reserves-audit-dashboard.component.ts index 18735299f..cb5439165 100644 --- a/frontend/src/app/components/liquid-reserves-audit/reserves-audit-dashboard/reserves-audit-dashboard.component.ts +++ b/frontend/src/app/components/liquid-reserves-audit/reserves-audit-dashboard/reserves-audit-dashboard.component.ts @@ -2,9 +2,9 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { SeoService } from '../../../services/seo.service'; import { WebsocketService } from '../../../services/websocket.service'; import { StateService } from '../../../services/state.service'; -import { Observable, combineLatest, concat, delay, filter, interval, map, mergeMap, of, share, skip, startWith, switchMap, tap, throttleTime } from 'rxjs'; +import { Observable, combineLatest, delayWhen, filter, interval, map, of, share, shareReplay, startWith, switchMap, tap, throttleTime, timer } from 'rxjs'; import { ApiService } from '../../../services/api.service'; -import { AuditStatus, CurrentPegs, FederationAddress, FederationUtxo, LiquidPegs } from '../../../interfaces/node-api.interface'; +import { AuditStatus, CurrentPegs, FederationAddress, FederationUtxo } from '../../../interfaces/node-api.interface'; @Component({ selector: 'app-reserves-audit-dashboard', @@ -24,6 +24,7 @@ export class ReservesAuditDashboardComponent implements OnInit { liquidPegsMonth$: Observable; liquidReservesMonth$: Observable; fullHistory$: Observable; + isLoad: boolean = true; private lastPegBlockUpdate: number = 0; private lastPegAmount: string = ''; private lastReservesBlockUpdate: number = 0; @@ -41,15 +42,12 @@ export class ReservesAuditDashboardComponent implements OnInit { ngOnInit(): void { this.websocketService.want(['blocks', 'mempool-blocks']); - this.auditStatus$ = concat( - this.apiService.federationAuditSynced$().pipe(share()), - this.stateService.blocks$.pipe( - skip(1), - throttleTime(40000), - delay(2000), - switchMap(() => this.apiService.federationAuditSynced$()), - share() - ) + this.auditStatus$ = this.stateService.blocks$.pipe( + throttleTime(40000), + delayWhen(_ => this.isLoad ? timer(0) : timer(2000)), + tap(() => this.isLoad = false), + switchMap(() => this.apiService.federationAuditSynced$()), + shareReplay(1) ); this.currentPeg$ = this.auditStatus$.pipe( diff --git a/frontend/src/app/dashboard/dashboard.component.ts b/frontend/src/app/dashboard/dashboard.component.ts index afb172aad..4ade4a9b3 100644 --- a/frontend/src/app/dashboard/dashboard.component.ts +++ b/frontend/src/app/dashboard/dashboard.component.ts @@ -1,6 +1,6 @@ import { AfterViewInit, ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; -import { combineLatest, concat, EMPTY, interval, merge, Observable, of, Subscription } from 'rxjs'; -import { catchError, delay, filter, map, mergeMap, scan, share, skip, startWith, switchMap, tap, throttleTime } from 'rxjs/operators'; +import { combineLatest, EMPTY, merge, Observable, of, Subscription, timer } from 'rxjs'; +import { catchError, delayWhen, filter, map, scan, share, shareReplay, startWith, switchMap, tap, throttleTime } from 'rxjs/operators'; import { AuditStatus, BlockExtended, CurrentPegs, OptimizedMempoolStats } from '../interfaces/node-api.interface'; import { MempoolInfo, TransactionStripped, ReplacementInfo } from '../interfaces/websocket.interface'; import { ApiService } from '../services/api.service'; @@ -53,6 +53,7 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit { liquidReservesMonth$: Observable; currentReserves$: Observable; fullHistory$: Observable; + isLoad: boolean = true; currencySubscription: Subscription; currency: string; private lastPegBlockUpdate: number = 0; @@ -213,56 +214,44 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit { ); if (this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet') { - ////////// Pegs historical data ////////// - this.liquidPegsMonth$ = interval(60 * 60 * 1000) - .pipe( - startWith(0), - switchMap(() => this.apiService.listLiquidPegsMonth$()), - map((pegs) => { - const labels = pegs.map(stats => stats.date); - const series = pegs.map(stats => parseFloat(stats.amount) / 100000000); - series.reduce((prev, curr, i) => series[i] = prev + curr, 0); - return { - series, - labels - }; - }), - share(), - ); + this.auditStatus$ = this.stateService.blocks$.pipe( + throttleTime(40000), + delayWhen(_ => this.isLoad ? timer(0) : timer(2000)), + tap(() => this.isLoad = false), + switchMap(() => this.apiService.federationAuditSynced$()), + shareReplay(1), + share() + ); - this.currentPeg$ = concat( - // We fetch the current peg when the page load and - // wait for the API response before listening to websocket blocks - this.apiService.liquidPegs$() - .pipe( - tap((currentPeg) => this.lastPegBlockUpdate = currentPeg.lastBlockUpdate) - ), - // Or when we receive a newer block, we wait 2 seconds so that the backend updates and we fetch the current peg - this.stateService.blocks$ - .pipe( - skip(1), - throttleTime(40000), - delay(2000), - switchMap((_) => this.apiService.liquidPegs$()), - filter((currentPeg) => currentPeg.lastBlockUpdate >= this.lastPegBlockUpdate), - tap((currentPeg) => this.lastPegBlockUpdate = currentPeg.lastBlockUpdate) + ////////// Pegs historical data ////////// + this.liquidPegsMonth$ = this.auditStatus$.pipe( + throttleTime(60 * 60 * 1000), + switchMap(() => this.apiService.listLiquidPegsMonth$()), + map((pegs) => { + const labels = pegs.map(stats => stats.date); + const series = pegs.map(stats => parseFloat(stats.amount) / 100000000); + series.reduce((prev, curr, i) => series[i] = prev + curr, 0); + return { + series, + labels + }; + }), + share(), + ); + + this.currentPeg$ = this.auditStatus$.pipe( + switchMap(_ => + this.apiService.liquidPegs$().pipe( + filter((currentPegs) => currentPegs.lastBlockUpdate >= this.lastPegBlockUpdate), + tap((currentPegs) => { + this.lastPegBlockUpdate = currentPegs.lastBlockUpdate; + }) ) - ).pipe( + ), share() ); ////////// BTC Reserves historical data ////////// - this.auditStatus$ = concat( - this.apiService.federationAuditSynced$().pipe(share()), - this.stateService.blocks$.pipe( - skip(1), - throttleTime(40000), - delay(2000), - switchMap(() => this.apiService.federationAuditSynced$()), - share() - ) - ); - this.auditUpdated$ = combineLatest([ this.auditStatus$, this.currentPeg$ @@ -271,20 +260,17 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit { map(([auditStatus, currentPeg]) => ({ lastBlockAudit: auditStatus.lastBlockAudit, currentPegAmount: currentPeg.amount - })), + })), switchMap(({ lastBlockAudit, currentPegAmount }) => { - console.log(lastBlockAudit, this.lastReservesBlockUpdate, currentPegAmount, this.lastPegAmount) const blockAuditCheck = lastBlockAudit > this.lastReservesBlockUpdate; const amountCheck = currentPegAmount !== this.lastPegAmount; this.lastPegAmount = currentPegAmount; - console.log(blockAuditCheck || amountCheck) return of(blockAuditCheck || amountCheck); }) ); - this.liquidReservesMonth$ = interval(60 * 60 * 1000).pipe( - startWith(0), - mergeMap(() => this.apiService.federationAuditSynced$()), + this.liquidReservesMonth$ = this.auditStatus$.pipe( + throttleTime(60 * 60 * 1000), switchMap((auditStatus) => { return auditStatus.isAuditSynced ? this.apiService.listLiquidReservesMonth$() : EMPTY; }),