Loading indicator service indicating mempool sync status.
This commit is contained in:
parent
f46728080d
commit
632c243b34
32
backend/src/api/loading-indicators.ts
Normal file
32
backend/src/api/loading-indicators.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { ILoadingIndicators } from '../mempool.interfaces';
|
||||||
|
|
||||||
|
class LoadingIndicators {
|
||||||
|
private loadingIndicators: ILoadingIndicators = {
|
||||||
|
'mempool': 0,
|
||||||
|
};
|
||||||
|
private progressChangedCallback: ((loadingIndicators: ILoadingIndicators) => void) | undefined;
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
public setProgressChangedCallback(fn: (loadingIndicators: ILoadingIndicators) => void) {
|
||||||
|
this.progressChangedCallback = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setProgress(name: string, progressPercent: number) {
|
||||||
|
const newProgress = Math.round(progressPercent);
|
||||||
|
if (newProgress >= 100) {
|
||||||
|
delete this.loadingIndicators[name];
|
||||||
|
} else {
|
||||||
|
this.loadingIndicators[name] = newProgress;
|
||||||
|
}
|
||||||
|
if (this.progressChangedCallback) {
|
||||||
|
this.progressChangedCallback(this.loadingIndicators);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLoadingIndicators() {
|
||||||
|
return this.loadingIndicators;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new LoadingIndicators();
|
@ -6,6 +6,7 @@ import { Common } from './common';
|
|||||||
import transactionUtils from './transaction-utils';
|
import transactionUtils from './transaction-utils';
|
||||||
import { IBitcoinApi } from './bitcoin/bitcoin-api.interface';
|
import { IBitcoinApi } from './bitcoin/bitcoin-api.interface';
|
||||||
import bitcoinBaseApi from './bitcoin/bitcoin-base.api';
|
import bitcoinBaseApi from './bitcoin/bitcoin-base.api';
|
||||||
|
import loadingIndicators from './loading-indicators';
|
||||||
|
|
||||||
class Mempool {
|
class Mempool {
|
||||||
private inSync: boolean = false;
|
private inSync: boolean = false;
|
||||||
@ -90,6 +91,10 @@ class Mempool {
|
|||||||
const diff = transactions.length - currentMempoolSize;
|
const diff = transactions.length - currentMempoolSize;
|
||||||
const newTransactions: TransactionExtended[] = [];
|
const newTransactions: TransactionExtended[] = [];
|
||||||
|
|
||||||
|
if (!this.inSync) {
|
||||||
|
loadingIndicators.setProgress('mempool', Object.keys(this.mempoolCache).length / transactions.length * 100);
|
||||||
|
}
|
||||||
|
|
||||||
for (const txid of transactions) {
|
for (const txid of transactions) {
|
||||||
if (!this.mempoolCache[txid]) {
|
if (!this.mempoolCache[txid]) {
|
||||||
const transaction = await transactionUtils.$getTransactionExtended(txid, true);
|
const transaction = await transactionUtils.$getTransactionExtended(txid, true);
|
||||||
@ -162,6 +167,7 @@ class Mempool {
|
|||||||
if (!this.inSync && transactions.length === Object.keys(newMempool).length) {
|
if (!this.inSync && transactions.length === Object.keys(newMempool).length) {
|
||||||
this.inSync = true;
|
this.inSync = true;
|
||||||
logger.info('The mempool is now in sync!');
|
logger.info('The mempool is now in sync!');
|
||||||
|
loadingIndicators.setProgress('mempool', 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.mempoolChangedCallback && (hasChange || deletedTransactions.length)) {
|
if (this.mempoolChangedCallback && (hasChange || deletedTransactions.length)) {
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import logger from '../logger';
|
import logger from '../logger';
|
||||||
import * as WebSocket from 'ws';
|
import * as WebSocket from 'ws';
|
||||||
import { BlockExtended, TransactionExtended, WebsocketResponse, MempoolBlock, OptimizedStatistic } from '../mempool.interfaces';
|
import { BlockExtended, TransactionExtended, WebsocketResponse, MempoolBlock, OptimizedStatistic, ILoadingIndicators } from '../mempool.interfaces';
|
||||||
import blocks from './blocks';
|
import blocks from './blocks';
|
||||||
import memPool from './mempool';
|
import memPool from './mempool';
|
||||||
import backendInfo from './backend-info';
|
import backendInfo from './backend-info';
|
||||||
import mempoolBlocks from './mempool-blocks';
|
import mempoolBlocks from './mempool-blocks';
|
||||||
import fiatConversion from './fiat-conversion';
|
import fiatConversion from './fiat-conversion';
|
||||||
import { Common } from './common';
|
import { Common } from './common';
|
||||||
|
import loadingIndicators from './loading-indicators';
|
||||||
|
|
||||||
class WebsocketHandler {
|
class WebsocketHandler {
|
||||||
private wss: WebSocket.Server | undefined;
|
private wss: WebSocket.Server | undefined;
|
||||||
@ -117,6 +118,19 @@ class WebsocketHandler {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleLoadingChanged(indicators: ILoadingIndicators) {
|
||||||
|
if (!this.wss) {
|
||||||
|
throw new Error('WebSocket.Server is not set');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.wss.clients.forEach((client: WebSocket) => {
|
||||||
|
if (client.readyState !== WebSocket.OPEN) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
client.send(JSON.stringify({ loadingIndicators: indicators }));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getInitData(_blocks?: BlockExtended[]) {
|
getInitData(_blocks?: BlockExtended[]) {
|
||||||
if (!_blocks) {
|
if (!_blocks) {
|
||||||
_blocks = blocks.getBlocks().slice(-8);
|
_blocks = blocks.getBlocks().slice(-8);
|
||||||
@ -131,6 +145,7 @@ class WebsocketHandler {
|
|||||||
'transactions': memPool.getLatestTransactions(),
|
'transactions': memPool.getLatestTransactions(),
|
||||||
'git-commit': backendInfo.gitCommitHash,
|
'git-commit': backendInfo.gitCommitHash,
|
||||||
'hostname': backendInfo.hostname,
|
'hostname': backendInfo.hostname,
|
||||||
|
'loadingIndicators': loadingIndicators.getLoadingIndicators(),
|
||||||
...this.extraInitProperties
|
...this.extraInitProperties
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import bisqMarkets from './api/bisq/markets';
|
|||||||
import donations from './api/donations';
|
import donations from './api/donations';
|
||||||
import logger from './logger';
|
import logger from './logger';
|
||||||
import backendInfo from './api/backend-info';
|
import backendInfo from './api/backend-info';
|
||||||
|
import loadingIndicators from './api/loading-indicators';
|
||||||
|
|
||||||
class Server {
|
class Server {
|
||||||
private wss: WebSocket.Server | undefined;
|
private wss: WebSocket.Server | undefined;
|
||||||
@ -135,6 +136,7 @@ class Server {
|
|||||||
blocks.setNewBlockCallback(websocketHandler.handleNewBlock.bind(websocketHandler));
|
blocks.setNewBlockCallback(websocketHandler.handleNewBlock.bind(websocketHandler));
|
||||||
memPool.setMempoolChangedCallback(websocketHandler.handleMempoolChange.bind(websocketHandler));
|
memPool.setMempoolChangedCallback(websocketHandler.handleMempoolChange.bind(websocketHandler));
|
||||||
donations.setNotfyDonationStatusCallback(websocketHandler.handleNewDonation.bind(websocketHandler));
|
donations.setNotfyDonationStatusCallback(websocketHandler.handleNewDonation.bind(websocketHandler));
|
||||||
|
loadingIndicators.setProgressChangedCallback(websocketHandler.handleLoadingChanged.bind(websocketHandler));
|
||||||
}
|
}
|
||||||
|
|
||||||
setUpHttpApiRoutes() {
|
setUpHttpApiRoutes() {
|
||||||
|
@ -136,3 +136,4 @@ interface RequiredParams {
|
|||||||
types: ('@string' | '@number' | '@boolean' | string)[];
|
types: ('@string' | '@number' | '@boolean' | string)[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ILoadingIndicators { [name: string]: number; }
|
||||||
|
@ -179,8 +179,8 @@
|
|||||||
<ng-template #txPerSecond let-mempoolInfoData>
|
<ng-template #txPerSecond let-mempoolInfoData>
|
||||||
<h5 class="card-title" i18n="dashboard.incoming-transactions">Incoming transactions</h5>
|
<h5 class="card-title" i18n="dashboard.incoming-transactions">Incoming transactions</h5>
|
||||||
<ng-template [ngIf]="mempoolInfoData.value" [ngIfElse]="loading">
|
<ng-template [ngIf]="mempoolInfoData.value" [ngIfElse]="loading">
|
||||||
<span *ngIf="mempoolInfoData.value.vBytesPerSecond === 0; else inSync">
|
<span *ngIf="(mempoolLoadingStatus$ | async) !== 100; else inSync">
|
||||||
<span class="badge badge-pill badge-warning" i18n="dashboard.backend-is-synchronizing">Backend is synchronizing</span>
|
<span class="badge badge-pill badge-warning"><ng-container i18n="dashboard.backend-is-synchronizing">Backend is synchronizing</ng-container> ({{ mempoolLoadingStatus$ | async }}%)</span>
|
||||||
</span>
|
</span>
|
||||||
<ng-template #inSync>
|
<ng-template #inSync>
|
||||||
<div class="progress sub-text" style="max-width: 250px;">
|
<div class="progress sub-text" style="max-width: 250px;">
|
||||||
|
@ -50,6 +50,7 @@ export class DashboardComponent implements OnInit {
|
|||||||
mempoolBlocksData$: Observable<MempoolBlocksData>;
|
mempoolBlocksData$: Observable<MempoolBlocksData>;
|
||||||
mempoolInfoData$: Observable<MempoolInfoData>;
|
mempoolInfoData$: Observable<MempoolInfoData>;
|
||||||
difficultyEpoch$: Observable<EpochProgress>;
|
difficultyEpoch$: Observable<EpochProgress>;
|
||||||
|
mempoolLoadingStatus$: Observable<number>;
|
||||||
vBytesPerSecondLimit = 1667;
|
vBytesPerSecondLimit = 1667;
|
||||||
blocks$: Observable<Block[]>;
|
blocks$: Observable<Block[]>;
|
||||||
transactions$: Observable<TransactionStripped[]>;
|
transactions$: Observable<TransactionStripped[]>;
|
||||||
@ -77,6 +78,9 @@ export class DashboardComponent implements OnInit {
|
|||||||
this.websocketService.want(['blocks', 'stats', 'mempool-blocks', 'live-2h-chart']);
|
this.websocketService.want(['blocks', 'stats', 'mempool-blocks', 'live-2h-chart']);
|
||||||
this.network$ = merge(of(''), this.stateService.networkChanged$);
|
this.network$ = merge(of(''), this.stateService.networkChanged$);
|
||||||
this.collapseLevel = this.storageService.getValue('dashboard-collapsed') || 'one';
|
this.collapseLevel = this.storageService.getValue('dashboard-collapsed') || 'one';
|
||||||
|
this.mempoolLoadingStatus$ = this.stateService.loadingIndicators$.pipe(
|
||||||
|
map((indicators) => indicators.mempool !== undefined ? indicators.mempool : 100)
|
||||||
|
);
|
||||||
|
|
||||||
this.languageForm = this.formBuilder.group({
|
this.languageForm = this.formBuilder.group({
|
||||||
language: ['']
|
language: ['']
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { ILoadingIndicators } from '../services/state.service';
|
||||||
import { Block, Transaction } from './electrs.interface';
|
import { Block, Transaction } from './electrs.interface';
|
||||||
|
|
||||||
export interface WebsocketResponse {
|
export interface WebsocketResponse {
|
||||||
@ -15,6 +16,7 @@ export interface WebsocketResponse {
|
|||||||
rbfTransaction?: Transaction;
|
rbfTransaction?: Transaction;
|
||||||
transactions?: TransactionStripped[];
|
transactions?: TransactionStripped[];
|
||||||
donationConfirmed?: boolean;
|
donationConfirmed?: boolean;
|
||||||
|
loadingIndicators?: ILoadingIndicators;
|
||||||
'track-tx'?: string;
|
'track-tx'?: string;
|
||||||
'track-address'?: string;
|
'track-address'?: string;
|
||||||
'track-asset'?: string;
|
'track-asset'?: string;
|
||||||
|
@ -13,6 +13,8 @@ interface MarkBlockState {
|
|||||||
txFeePerVSize?: number;
|
txFeePerVSize?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ILoadingIndicators { [name: string]: number; }
|
||||||
|
|
||||||
export interface Env {
|
export interface Env {
|
||||||
TESTNET_ENABLED: boolean;
|
TESTNET_ENABLED: boolean;
|
||||||
LIQUID_ENABLED: boolean;
|
LIQUID_ENABLED: boolean;
|
||||||
@ -63,6 +65,7 @@ export class StateService {
|
|||||||
lastDifficultyAdjustment$ = new ReplaySubject<number>(1);
|
lastDifficultyAdjustment$ = new ReplaySubject<number>(1);
|
||||||
gitCommit$ = new ReplaySubject<string>(1);
|
gitCommit$ = new ReplaySubject<string>(1);
|
||||||
donationConfirmed$ = new Subject();
|
donationConfirmed$ = new Subject();
|
||||||
|
loadingIndicators$ = new ReplaySubject<ILoadingIndicators>(1);
|
||||||
|
|
||||||
live2Chart$ = new Subject<OptimizedMempoolStats>();
|
live2Chart$ = new Subject<OptimizedMempoolStats>();
|
||||||
|
|
||||||
|
@ -270,6 +270,10 @@ export class WebsocketService {
|
|||||||
this.stateService.live2Chart$.next(response['live-2h-chart']);
|
this.stateService.live2Chart$.next(response['live-2h-chart']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (response.loadingIndicators) {
|
||||||
|
this.stateService.loadingIndicators$.next(response.loadingIndicators);
|
||||||
|
}
|
||||||
|
|
||||||
if (response.mempoolInfo) {
|
if (response.mempoolInfo) {
|
||||||
this.stateService.mempoolInfo$.next(response.mempoolInfo);
|
this.stateService.mempoolInfo$.next(response.mempoolInfo);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user