Merge remote-tracking branch 'origin/master' into mononaut/seo-ssr

This commit is contained in:
wiz
2023-08-05 18:23:12 +09:00
476 changed files with 57113 additions and 27203 deletions

View File

@@ -1,13 +1,15 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http';
import { CpfpInfo, OptimizedMempoolStats, AddressInformation, LiquidPegs, ITranslators,
PoolStat, BlockExtended, TransactionStripped, RewardStats, AuditScore, BlockSizesAndWeights } from '../interfaces/node-api.interface';
PoolStat, BlockExtended, TransactionStripped, RewardStats, AuditScore, BlockSizesAndWeights, RbfTree, BlockAudit } from '../interfaces/node-api.interface';
import { Observable } from 'rxjs';
import { StateService } from './state.service';
import { WebsocketResponse } from '../interfaces/websocket.interface';
import { Outspend, Transaction } from '../interfaces/electrs.interface';
import { Conversion } from './price.service';
const SERVICES_API_PREFIX = `/api/v1/services`;
@Injectable({
providedIn: 'root'
})
@@ -72,6 +74,10 @@ export class ApiService {
return this.httpClient.get<OptimizedMempoolStats[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/statistics/4y');
}
listAllTimeStatistics$(): Observable<OptimizedMempoolStats[]> {
return this.httpClient.get<OptimizedMempoolStats[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/statistics/all');
}
getTransactionTimes$(txIds: string[]): Observable<number[]> {
let params = new HttpParams();
txIds.forEach((txId: string) => {
@@ -88,15 +94,11 @@ export class ApiService {
return this.httpClient.get<Outspend[][]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/outspends', { params });
}
requestDonation$(amount: number, orderId: string): Observable<any> {
const params = {
amount: amount,
orderId: orderId,
};
return this.httpClient.post<any>(this.apiBaseUrl + '/api/v1/donations', params);
getAboutPageProfiles$(): Observable<any[]> {
return this.httpClient.get<any[]>(this.apiBaseUrl + '/api/v1/about-page');
}
getDonation$(): Observable<any[]> {
getOgs$(): Observable<any> {
return this.httpClient.get<any[]>(this.apiBaseUrl + '/api/v1/donations');
}
@@ -108,10 +110,6 @@ export class ApiService {
return this.httpClient.get<any[]>(this.apiBaseUrl + '/api/v1/contributors');
}
checkDonation$(orderId: string): Observable<any[]> {
return this.httpClient.get<any[]>(this.apiBaseUrl + '/api/v1/donations/check?order_id=' + orderId);
}
getInitData$(): Observable<WebsocketResponse> {
return this.httpClient.get<WebsocketResponse>(this.apiBaseUrl + this.apiBasePath + '/api/v1/init-data');
}
@@ -124,14 +122,18 @@ export class ApiService {
return this.httpClient.get<AddressInformation>(this.apiBaseUrl + this.apiBasePath + '/api/v1/validate-address/' + address);
}
getRbfHistory$(txid: string): Observable<string[]> {
return this.httpClient.get<string[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/tx/' + txid + '/replaces');
getRbfHistory$(txid: string): Observable<{ replacements: RbfTree, replaces: string[] }> {
return this.httpClient.get<{ replacements: RbfTree, replaces: string[] }>(this.apiBaseUrl + this.apiBasePath + '/api/v1/tx/' + txid + '/rbf');
}
getRbfCachedTx$(txid: string): Observable<Transaction> {
return this.httpClient.get<Transaction>(this.apiBaseUrl + this.apiBasePath + '/api/v1/tx/' + txid + '/cached');
}
getRbfList$(fullRbf: boolean, after?: string): Observable<RbfTree[]> {
return this.httpClient.get<RbfTree[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/' + (fullRbf ? 'fullrbf/' : '') + 'replacements/' + (after || ''));
}
listLiquidPegsMonth$(): Observable<LiquidPegs[]> {
return this.httpClient.get<LiquidPegs[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/pegs/month');
}
@@ -234,16 +236,16 @@ export class ApiService {
);
}
getHistoricalBlockPrediction$(interval: string | undefined) : Observable<any> {
getHistoricalBlocksHealth$(interval: string | undefined) : Observable<any> {
return this.httpClient.get<any[]>(
this.apiBaseUrl + this.apiBasePath + `/api/v1/mining/blocks/predictions` +
(interval !== undefined ? `/${interval}` : ''), { observe: 'response' }
);
}
getBlockAudit$(hash: string) : Observable<any> {
return this.httpClient.get<any>(
this.apiBaseUrl + this.apiBasePath + `/api/v1/block/${hash}/audit-summary`, { observe: 'response' }
getBlockAudit$(hash: string) : Observable<BlockAudit> {
return this.httpClient.get<BlockAudit>(
this.apiBaseUrl + this.apiBasePath + `/api/v1/block/${hash}/audit-summary`
);
}
@@ -315,4 +317,13 @@ export class ApiService {
(timestamp ? `?timestamp=${timestamp}` : '')
);
}
/**
* Services
*/
getNodeOwner$(publicKey: string) {
let params = new HttpParams()
.set('node_public_key', publicKey);
return this.httpClient.get<any>(`${SERVICES_API_PREFIX}/lightning/claim/current`, { params, observe: 'response' });
}
}

View File

@@ -18,6 +18,7 @@ export class CacheService {
txCache: { [txid: string]: Transaction } = {};
network: string;
blockHashCache: { [hash: string]: BlockExtended } = {};
blockCache: { [height: number]: BlockExtended } = {};
blockLoading: { [height: number]: boolean } = {};
copiesInBlockQueue: { [height: number]: number } = {};
@@ -27,8 +28,10 @@ export class CacheService {
private stateService: StateService,
private apiService: ApiService,
) {
this.stateService.blocks$.subscribe(([block]) => {
this.addBlockToCache(block);
this.stateService.blocks$.subscribe((blocks) => {
for (const block of blocks) {
this.addBlockToCache(block);
}
this.clearBlocks();
});
this.stateService.chainTip$.subscribe((height) => {
@@ -56,8 +59,11 @@ export class CacheService {
}
addBlockToCache(block: BlockExtended) {
this.blockCache[block.height] = block;
this.bumpBlockPriority(block.height);
if (!this.blockHashCache[block.id]) {
this.blockHashCache[block.id] = block;
this.blockCache[block.height] = block;
this.bumpBlockPriority(block.height);
}
}
async loadBlock(height) {
@@ -105,7 +111,9 @@ export class CacheService {
} else if ((this.tip - height) < KEEP_RECENT_BLOCKS) {
this.bumpBlockPriority(height);
} else {
const block = this.blockCache[height];
delete this.blockCache[height];
delete this.blockHashCache[block.id];
delete this.copiesInBlockQueue[height];
}
}
@@ -113,6 +121,7 @@ export class CacheService {
// remove all blocks from the cache
resetBlockCache() {
this.blockHashCache = {};
this.blockCache = {};
this.blockLoading = {};
this.copiesInBlockQueue = {};

View File

@@ -1,9 +1,10 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Transaction, Address, Outspend, Recent, Asset } from '../interfaces/electrs.interface';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable, from, of, switchMap } from 'rxjs';
import { Transaction, Address, Outspend, Recent, Asset, ScriptHash } from '../interfaces/electrs.interface';
import { StateService } from './state.service';
import { BlockExtended } from '../interfaces/node-api.interface';
import { calcScriptHash$ } from '../bitcoin.utils';
@Injectable({
providedIn: 'root'
@@ -65,12 +66,41 @@ export class ElectrsApiService {
return this.httpClient.get<Address>(this.apiBaseUrl + this.apiBasePath + '/api/address/' + address);
}
getAddressTransactions$(address: string): Observable<Transaction[]> {
return this.httpClient.get<Transaction[]>(this.apiBaseUrl + this.apiBasePath + '/api/address/' + address + '/txs');
getPubKeyAddress$(pubkey: string): Observable<Address> {
const scriptpubkey = (pubkey.length === 130 ? '41' : '21') + pubkey + 'ac';
return this.getScriptHash$(scriptpubkey).pipe(
switchMap((scripthash: ScriptHash) => {
return of({
...scripthash,
address: pubkey,
is_pubkey: true,
});
})
);
}
getAddressTransactionsFromHash$(address: string, txid: string): Observable<Transaction[]> {
return this.httpClient.get<Transaction[]>(this.apiBaseUrl + this.apiBasePath + '/api/address/' + address + '/txs/chain/' + txid);
getScriptHash$(script: string): Observable<ScriptHash> {
return from(calcScriptHash$(script)).pipe(
switchMap(scriptHash => this.httpClient.get<ScriptHash>(this.apiBaseUrl + this.apiBasePath + '/api/scripthash/' + scriptHash))
);
}
getAddressTransactions$(address: string, txid?: string): Observable<Transaction[]> {
let params = new HttpParams();
if (txid) {
params = params.append('after_txid', txid);
}
return this.httpClient.get<Transaction[]>(this.apiBaseUrl + this.apiBasePath + '/api/address/' + address + '/txs', { params });
}
getScriptHashTransactions$(script: string, txid?: string): Observable<Transaction[]> {
let params = new HttpParams();
if (txid) {
params = params.append('after_txid', txid);
}
return from(calcScriptHash$(script)).pipe(
switchMap(scriptHash => this.httpClient.get<Transaction[]>(this.apiBaseUrl + this.apiBasePath + '/api/scripthash/' + scriptHash + '/txs', { params })),
);
}
getAsset$(assetId: string): Observable<Asset> {

View File

@@ -96,7 +96,7 @@ export class MiningService {
share: parseFloat((poolStat.blockCount / stats.blockCount * 100).toFixed(2)),
lastEstimatedHashrate: (poolStat.blockCount / stats.blockCount * stats.lastEstimatedHashrate / hashrateDivider).toFixed(2),
emptyBlockRatio: (poolStat.emptyBlocks / poolStat.blockCount * 100).toFixed(2),
logo: `/resources/mining-pools/` + poolStat.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg',
logo: `/resources/mining-pools/` + poolStat.slug + '.svg',
...poolStat
};
});

View File

@@ -1,17 +1,20 @@
import { Inject, Injectable, PLATFORM_ID, LOCALE_ID } from '@angular/core';
import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable } from 'rxjs';
import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable, merge } from 'rxjs';
import { Transaction } from '../interfaces/electrs.interface';
import { IBackendInfo, MempoolBlock, MempoolBlockWithTransactions, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, TransactionStripped } from '../interfaces/websocket.interface';
import { BlockExtended, DifficultyAdjustment, OptimizedMempoolStats } from '../interfaces/node-api.interface';
import { IBackendInfo, MempoolBlock, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, ReplacementInfo, TransactionStripped } from '../interfaces/websocket.interface';
import { BlockExtended, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, RbfTree } from '../interfaces/node-api.interface';
import { Router, NavigationStart } from '@angular/router';
import { isPlatformBrowser } from '@angular/common';
import { map, shareReplay } from 'rxjs/operators';
import { filter, map, scan, shareReplay } from 'rxjs/operators';
import { StorageService } from './storage.service';
import { hasTouchScreen } from '../shared/pipes/bytes-pipe/utils';
interface MarkBlockState {
export interface MarkBlockState {
blockHeight?: number;
txid?: string;
mempoolBlockIndex?: number;
txFeePerVSize?: number;
mempoolPosition?: MempoolPosition;
}
export interface ILoadingIndicators { [name: string]: number; }
@@ -44,6 +47,7 @@ export interface Env {
TESTNET_BLOCK_AUDIT_START_HEIGHT: number;
SIGNET_BLOCK_AUDIT_START_HEIGHT: number;
HISTORICAL_PRICE: boolean;
ACCELERATOR: boolean;
}
const defaultEnv: Env = {
@@ -74,6 +78,7 @@ const defaultEnv: Env = {
'TESTNET_BLOCK_AUDIT_START_HEIGHT': 0,
'SIGNET_BLOCK_AUDIT_START_HEIGHT': 0,
'HISTORICAL_PRICE': true,
'ACCELERATOR': false,
};
@Injectable({
@@ -86,10 +91,12 @@ export class StateService {
blockVSize: number;
env: Env;
latestBlockHeight = -1;
blocks: BlockExtended[] = [];
networkChanged$ = new ReplaySubject<string>(1);
lightningChanged$ = new ReplaySubject<boolean>(1);
blocks$: ReplaySubject<[BlockExtended, boolean]>;
blocksSubject$ = new BehaviorSubject<BlockExtended[]>([]);
blocks$: Observable<BlockExtended[]>;
transactions$ = new ReplaySubject<TransactionStripped>(6);
conversions$ = new ReplaySubject<any>(1);
bsqPrice$ = new ReplaySubject<number>(1);
@@ -97,12 +104,19 @@ export class StateService {
mempoolBlocks$ = new ReplaySubject<MempoolBlock[]>(1);
mempoolBlockTransactions$ = new Subject<TransactionStripped[]>();
mempoolBlockDelta$ = new Subject<MempoolBlockDelta>();
liveMempoolBlockTransactions$: Observable<{ [txid: string]: TransactionStripped}>;
txConfirmed$ = new Subject<[string, BlockExtended]>();
txReplaced$ = new Subject<ReplacedTransaction>();
txRbfInfo$ = new Subject<RbfTree>();
rbfLatest$ = new Subject<RbfTree[]>();
rbfLatestSummary$ = new Subject<ReplacementInfo[]>();
utxoSpent$ = new Subject<object>();
difficultyAdjustment$ = new ReplaySubject<DifficultyAdjustment>(1);
mempoolTransactions$ = new Subject<Transaction>();
mempoolTxPosition$ = new Subject<{ txid: string, position: MempoolPosition}>();
blockTransactions$ = new Subject<Transaction>();
isLoadingWebSocket$ = new ReplaySubject<boolean>(1);
isLoadingMempool$ = new BehaviorSubject<boolean>(true);
vbytesPerSecond$ = new ReplaySubject<number>(1);
previousRetarget$ = new ReplaySubject<number>(1);
backendInfo$ = new ReplaySubject<IBackendInfo>(1);
@@ -118,12 +132,17 @@ export class StateService {
markBlock$ = new BehaviorSubject<MarkBlockState>({});
keyNavigation$ = new Subject<KeyboardEvent>();
searchText$ = new BehaviorSubject<string>('');
blockScrolling$: Subject<boolean> = new Subject<boolean>();
resetScroll$: Subject<boolean> = new Subject<boolean>();
timeLtr: BehaviorSubject<boolean>;
hideFlow: BehaviorSubject<boolean>;
hideAudit: BehaviorSubject<boolean>;
fiatCurrency$: BehaviorSubject<string>;
rateUnits$: BehaviorSubject<string>;
searchFocus$: Subject<boolean> = new Subject<boolean>();
constructor(
@Inject(PLATFORM_ID) private platformId: any,
@@ -157,15 +176,44 @@ export class StateService {
}
});
this.blocks$ = new ReplaySubject<[BlockExtended, boolean]>(this.env.KEEP_BLOCKS_AMOUNT);
this.liveMempoolBlockTransactions$ = merge(
this.mempoolBlockTransactions$.pipe(map(transactions => { return { transactions }; })),
this.mempoolBlockDelta$.pipe(map(delta => { return { delta }; })),
).pipe(scan((transactions: { [txid: string]: TransactionStripped }, change: any): { [txid: string]: TransactionStripped } => {
if (change.transactions) {
const txMap = {}
change.transactions.forEach(tx => {
txMap[tx.txid] = tx;
})
return txMap;
} else {
change.delta.changed.forEach(tx => {
transactions[tx.txid].rate = tx.rate;
})
change.delta.removed.forEach(txid => {
delete transactions[txid];
});
change.delta.added.forEach(tx => {
transactions[tx.txid] = tx;
});
return transactions;
}
}, {}));
if (this.env.BASE_MODULE === 'bisq') {
this.network = this.env.BASE_MODULE;
this.networkChanged$.next(this.env.BASE_MODULE);
}
this.networkChanged$.subscribe((network) => {
this.transactions$ = new ReplaySubject<TransactionStripped>(6);
this.blocksSubject$.next([]);
});
this.blockVSize = this.env.BLOCK_WEIGHT_UNITS / 4;
this.blocks$ = this.blocksSubject$.pipe(filter(blocks => blocks != null && blocks.length > 0));
const savedTimePreference = this.storageService.getValue('time-preference-ltr');
const rtlLanguage = (this.locale.startsWith('ar') || this.locale.startsWith('fa') || this.locale.startsWith('he'));
// default time direction is right-to-left, unless locale is a RTL language
@@ -192,6 +240,9 @@ export class StateService {
const fiatPreference = this.storageService.getValue('fiat-preference');
this.fiatCurrency$ = new BehaviorSubject<string>(fiatPreference || 'USD');
const rateUnitPreference = this.storageService.getValue('rate-unit-preference');
this.rateUnits$ = new BehaviorSubject<string>(rateUnitPreference || 'vb');
}
setNetworkBasedonUrl(url: string) {
@@ -299,4 +350,21 @@ export class StateService {
this.chainTip$.next(height);
}
}
resetBlocks(blocks: BlockExtended[]): void {
this.blocks = blocks.reverse();
this.blocksSubject$.next(blocks);
}
addBlock(block: BlockExtended): void {
this.blocks.unshift(block);
this.blocks = this.blocks.slice(0, this.env.KEEP_BLOCKS_AMOUNT);
this.blocksSubject$.next(this.blocks);
}
focusSearchInputDesktop() {
if (!hasTouchScreen()) {
this.searchFocus$.next(true);
}
}
}

View File

@@ -12,20 +12,22 @@ export class StorageService {
setDefaultValueIfNeeded(key: string, defaultValue: string) {
const graphWindowPreference: string = this.getValue(key);
const fragment = window.location.hash.replace('#', '');
if (graphWindowPreference === null) { // First visit to mempool.space
if (this.router.url.includes('graphs') && key === 'graphWindowPreference' ||
this.router.url.includes('pools') && key === 'miningWindowPreference'
if (window.location.pathname.includes('graphs') && key === 'graphWindowPreference' ||
window.location.pathname.includes('pools') && key === 'miningWindowPreference'
) {
this.setValue(key, this.route.snapshot.fragment ? this.route.snapshot.fragment : defaultValue);
this.setValue(key, fragment ? fragment : defaultValue);
} else {
this.setValue(key, defaultValue);
}
} else if (this.router.url.includes('graphs') && key === 'graphWindowPreference' ||
this.router.url.includes('pools') && key === 'miningWindowPreference'
} else if (window.location.pathname.includes('graphs') && key === 'graphWindowPreference' ||
window.location.pathname.includes('pools') && key === 'miningWindowPreference'
) {
// Visit a different graphs#fragment from last visit
if (this.route.snapshot.fragment !== null && graphWindowPreference !== this.route.snapshot.fragment) {
this.setValue(key, this.route.snapshot.fragment);
if (fragment !== null && graphWindowPreference !== fragment) {
this.setValue(key, fragment);
}
}
}

View File

@@ -1,16 +1,16 @@
import { Injectable } from '@angular/core';
import { webSocket, WebSocketSubject } from 'rxjs/webSocket';
import { WebsocketResponse, IBackendInfo } from '../interfaces/websocket.interface';
import { WebsocketResponse } from '../interfaces/websocket.interface';
import { StateService } from './state.service';
import { Transaction } from '../interfaces/electrs.interface';
import { Subscription } from 'rxjs';
import { ApiService } from './api.service';
import { take } from 'rxjs/operators';
import { TransferState, makeStateKey } from '@angular/platform-browser';
import { BlockExtended } from '../interfaces/node-api.interface';
import { CacheService } from './cache.service';
const OFFLINE_RETRY_AFTER_MS = 1000;
const OFFLINE_PING_CHECK_AFTER_MS = 10000;
const OFFLINE_RETRY_AFTER_MS = 2000;
const OFFLINE_PING_CHECK_AFTER_MS = 30000;
const EXPECT_PING_RESPONSE_AFTER_MS = 5000;
const initData = makeStateKey('/api/v1/init-data');
@@ -28,6 +28,9 @@ export class WebsocketService {
private isTrackingTx = false;
private trackingTxId: string;
private isTrackingMempoolBlock = false;
private isTrackingRbf: 'all' | 'fullRbf' | false = false;
private isTrackingRbfSummary = false;
private isTrackingAddress: string | false = false;
private trackingMempoolBlock: number;
private latestGitCommit = '';
private onlineCheckTimeout: number;
@@ -39,6 +42,7 @@ export class WebsocketService {
private stateService: StateService,
private apiService: ApiService,
private transferState: TransferState,
private cacheService: CacheService,
) {
if (!this.stateService.isBrowser) {
// @ts-ignore
@@ -107,10 +111,19 @@ export class WebsocketService {
if (this.isTrackingMempoolBlock) {
this.startTrackMempoolBlock(this.trackingMempoolBlock);
}
if (this.isTrackingRbf) {
this.startTrackRbf(this.isTrackingRbf);
}
if (this.isTrackingRbfSummary) {
this.startTrackRbfSummary();
}
if (this.isTrackingAddress) {
this.startTrackAddress(this.isTrackingAddress);
}
this.stateService.connectionState$.next(2);
}
if (this.stateService.connectionState$.value === 1) {
if (this.stateService.connectionState$.value !== 2) {
this.stateService.connectionState$.next(2);
}
@@ -118,7 +131,7 @@ export class WebsocketService {
},
(err: Error) => {
console.log(err);
console.log(`WebSocket error, trying to reconnect in ${OFFLINE_RETRY_AFTER_MS} seconds`);
console.log(`WebSocket error`);
this.goOffline();
});
}
@@ -148,10 +161,12 @@ export class WebsocketService {
startTrackAddress(address: string) {
this.websocketSubject.next({ 'track-address': address });
this.isTrackingAddress = address;
}
stopTrackingAddress() {
this.websocketSubject.next({ 'track-address': 'stop' });
this.isTrackingAddress = false;
}
startTrackAsset(asset: string) {
@@ -173,6 +188,26 @@ export class WebsocketService {
this.isTrackingMempoolBlock = false
}
startTrackRbf(mode: 'all' | 'fullRbf') {
this.websocketSubject.next({ 'track-rbf': mode });
this.isTrackingRbf = mode;
}
stopTrackRbf() {
this.websocketSubject.next({ 'track-rbf': 'stop' });
this.isTrackingRbf = false;
}
startTrackRbfSummary() {
this.websocketSubject.next({ 'track-rbf-summary': true });
this.isTrackingRbfSummary = true;
}
stopTrackRbfSummary() {
this.websocketSubject.next({ 'track-rbf-summary': false });
this.isTrackingRbfSummary = false;
}
startTrackBisqMarket(market: string) {
this.websocketSubject.next({ 'track-bisq-market': market });
}
@@ -197,11 +232,13 @@ export class WebsocketService {
}
goOffline() {
const retryDelay = OFFLINE_RETRY_AFTER_MS + (Math.random() * OFFLINE_RETRY_AFTER_MS);
console.log(`trying to reconnect websocket in ${retryDelay} seconds`);
this.goneOffline = true;
this.stateService.connectionState$.next(0);
window.setTimeout(() => {
this.startSubscription(true);
}, OFFLINE_RETRY_AFTER_MS);
}, retryDelay);
}
startOnlineCheck() {
@@ -212,7 +249,7 @@ export class WebsocketService {
this.websocketSubject.next({action: 'ping'});
this.onlineCheckTimeoutTwo = window.setTimeout(() => {
if (!this.goneOffline) {
console.log('WebSocket response timeout, force closing, trying to reconnect in 10 seconds');
console.log('WebSocket response timeout, force closing');
this.websocketSubject.complete();
this.subscription.unsubscribe();
this.goOffline();
@@ -222,15 +259,12 @@ export class WebsocketService {
}
handleResponse(response: WebsocketResponse) {
let reinitBlocks = false;
if (response.blocks && response.blocks.length) {
const blocks = response.blocks;
let maxHeight = 0;
blocks.forEach((block: BlockExtended) => {
if (block.height > this.stateService.latestBlockHeight) {
maxHeight = Math.max(maxHeight, block.height);
this.stateService.blocks$.next([block, false]);
}
});
this.stateService.resetBlocks(blocks);
const maxHeight = blocks.reduce((max, block) => Math.max(max, block.height), this.stateService.latestBlockHeight);
this.stateService.updateChainTip(maxHeight);
}
@@ -238,10 +272,17 @@ export class WebsocketService {
this.stateService.mempoolTransactions$.next(response.tx);
}
if (response['txPosition']) {
this.stateService.mempoolTxPosition$.next(response['txPosition']);
}
if (response.block) {
if (response.block.height > this.stateService.latestBlockHeight) {
if (response.block.height === this.stateService.latestBlockHeight + 1) {
this.stateService.updateChainTip(response.block.height);
this.stateService.blocks$.next([response.block, !!response.txConfirmed]);
this.stateService.addBlock(response.block);
this.stateService.txConfirmed$.next([response.txConfirmed, response.block]);
} else if (response.block.height > this.stateService.latestBlockHeight + 1) {
reinitBlocks = true;
}
if (response.txConfirmed) {
@@ -257,6 +298,18 @@ export class WebsocketService {
this.stateService.txReplaced$.next(response.rbfTransaction);
}
if (response.rbfInfo) {
this.stateService.txRbfInfo$.next(response.rbfInfo);
}
if (response.rbfLatest) {
this.stateService.rbfLatest$.next(response.rbfLatest);
}
if (response.rbfLatestSummary) {
this.stateService.rbfLatestSummary$.next(response.rbfLatestSummary);
}
if (response.txReplaced) {
this.stateService.txReplaced$.next(response.txReplaced);
}
@@ -327,6 +380,11 @@ export class WebsocketService {
if (response.loadingIndicators) {
this.stateService.loadingIndicators$.next(response.loadingIndicators);
if (response.loadingIndicators.mempool != null && response.loadingIndicators.mempool < 100) {
this.stateService.isLoadingMempool$.next(true);
} else {
this.stateService.isLoadingMempool$.next(false);
}
}
if (response.mempoolInfo) {
@@ -344,5 +402,9 @@ export class WebsocketService {
if (response['git-commit']) {
this.stateService.backendInfo$.next(response['git-commit']);
}
if (reinitBlocks) {
this.websocketSubject.next({'refresh-blocks': true});
}
}
}