Subscription refactoring to increase performance.

This commit is contained in:
softsimon 2020-07-30 17:01:13 +07:00
parent bb6272469d
commit 58a6bbd88b
No known key found for this signature in database
GPG Key ID: 488D7DCFB5A430D7
8 changed files with 89 additions and 60 deletions

View File

@ -1,24 +1,27 @@
import { Component, OnInit, OnDestroy } from '@angular/core'; import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { Block } from 'src/app/interfaces/electrs.interface'; import { Block } from 'src/app/interfaces/electrs.interface';
import { StateService } from 'src/app/services/state.service'; import { StateService } from 'src/app/services/state.service';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { AudioService } from 'src/app/services/audio.service';
import { env } from 'src/app/app.constants'; import { env } from 'src/app/app.constants';
@Component({ @Component({
selector: 'app-blockchain-blocks', selector: 'app-blockchain-blocks',
templateUrl: './blockchain-blocks.component.html', templateUrl: './blockchain-blocks.component.html',
styleUrls: ['./blockchain-blocks.component.scss'] styleUrls: ['./blockchain-blocks.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class BlockchainBlocksComponent implements OnInit, OnDestroy { export class BlockchainBlocksComponent implements OnInit, OnDestroy {
network = ''; network = '';
blocks: Block[] = []; blocks: Block[] = [];
markHeight: number; markHeight: number;
blocksSubscription: Subscription; blocksSubscription: Subscription;
networkSubscriotion: Subscription;
tabHiddenSubscription: Subscription;
markBlockSubscription: Subscription;
blockStyles = []; blockStyles = [];
interval: any; interval: any;
tabHidden = true; tabHidden = false;
arrowVisible = false; arrowVisible = false;
arrowLeftPx = 30; arrowLeftPx = 30;
@ -35,11 +38,12 @@ export class BlockchainBlocksComponent implements OnInit, OnDestroy {
constructor( constructor(
private stateService: StateService, private stateService: StateService,
private router: Router, private router: Router,
private cd: ChangeDetectorRef,
) { } ) { }
ngOnInit() { ngOnInit() {
this.stateService.networkChanged$.subscribe((network) => this.network = network); this.networkSubscriotion = this.stateService.networkChanged$.subscribe((network) => this.network = network);
this.stateService.isTabHidden$.subscribe((tabHidden) => this.tabHidden = tabHidden); this.tabHiddenSubscription = this.stateService.isTabHidden$.subscribe((tabHidden) => this.tabHidden = tabHidden);
this.blocksSubscription = this.stateService.blocks$ this.blocksSubscription = this.stateService.blocks$
.subscribe(([block, txConfirmed]) => { .subscribe(([block, txConfirmed]) => {
@ -71,20 +75,23 @@ export class BlockchainBlocksComponent implements OnInit, OnDestroy {
setTimeout(() => { setTimeout(() => {
this.blockStyles = []; this.blockStyles = [];
this.blocks.forEach((b) => this.blockStyles.push(this.getStyleForBlock(b))); this.blocks.forEach((b) => this.blockStyles.push(this.getStyleForBlock(b)));
this.cd.markForCheck();
}, 50); }, 50);
if (this.blocks.length === env.KEEP_BLOCKS_AMOUNT) { if (this.blocks.length === env.KEEP_BLOCKS_AMOUNT) {
this.blocksFilled = true; this.blocksFilled = true;
} }
this.cd.markForCheck();
}); });
this.stateService.markBlock$ this.markBlockSubscription = this.stateService.markBlock$
.subscribe((state) => { .subscribe((state) => {
this.markHeight = undefined; this.markHeight = undefined;
if (state.blockHeight) { if (state.blockHeight) {
this.markHeight = state.blockHeight; this.markHeight = state.blockHeight;
} }
this.moveArrowToPosition(false); this.moveArrowToPosition(false);
this.cd.markForCheck();
}); });
this.stateService.keyNavigation$.subscribe((event) => { this.stateService.keyNavigation$.subscribe((event) => {
@ -112,6 +119,9 @@ export class BlockchainBlocksComponent implements OnInit, OnDestroy {
ngOnDestroy() { ngOnDestroy() {
this.blocksSubscription.unsubscribe(); this.blocksSubscription.unsubscribe();
this.networkSubscriotion.unsubscribe();
this.tabHiddenSubscription.unsubscribe();
this.markBlockSubscription.unsubscribe();
clearInterval(this.interval); clearInterval(this.interval);
} }
@ -131,11 +141,15 @@ export class BlockchainBlocksComponent implements OnInit, OnDestroy {
setTimeout(() => { setTimeout(() => {
this.transition = '2s'; this.transition = '2s';
this.arrowLeftPx = blockindex * 155 + 30; this.arrowLeftPx = blockindex * 155 + 30;
this.cd.markForCheck();
}, 50); }, 50);
} else { } else {
this.arrowLeftPx = blockindex * 155 + 30; this.arrowLeftPx = blockindex * 155 + 30;
if (!animate) { if (!animate) {
setTimeout(() => this.transition = '2s'); setTimeout(() => {
this.transition = '2s';
this.cd.markForCheck();
});
} }
} }
} }

View File

@ -1,12 +1,12 @@
<div class="text-center" class="blockchain-wrapper"> <div class="text-center" class="blockchain-wrapper">
<div class="position-container" [hidden]="isLoading"> <div class="position-container" [hidden]="(isLoading$ | async) === true">
<span> <span>
<app-mempool-blocks></app-mempool-blocks> <app-mempool-blocks></app-mempool-blocks>
<app-blockchain-blocks></app-blockchain-blocks> <app-blockchain-blocks></app-blockchain-blocks>
<div id="divider"></div> <div id="divider"></div>
</span> </span>
</div> </div>
<div *ngIf="isLoading" class="position-container loading"> <div *ngIf="(isLoading$ | async) === true" class="position-container loading">
<div class="loading-block"> <div class="loading-block">
<h3>Waiting for blocks...</h3> <h3>Waiting for blocks...</h3>
<div class="spinner-border text-light mt-2"></div> <div class="spinner-border text-light mt-2"></div>

View File

@ -1,28 +1,21 @@
import { Component, OnInit, OnDestroy } from '@angular/core'; import { Component, OnInit, OnDestroy, ChangeDetectionStrategy } from '@angular/core';
import { StateService } from 'src/app/services/state.service'; import { StateService } from 'src/app/services/state.service';
import { Subscription } from 'rxjs'; import { Subscription, Observable } from 'rxjs';
@Component({ @Component({
selector: 'app-blockchain', selector: 'app-blockchain',
templateUrl: './blockchain.component.html', templateUrl: './blockchain.component.html',
styleUrls: ['./blockchain.component.scss'] styleUrls: ['./blockchain.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class BlockchainComponent implements OnInit, OnDestroy { export class BlockchainComponent implements OnInit {
txTrackingLoading = false; isLoading$: Observable<boolean>;
txShowTxNotFound = false;
isLoading = true;
subscription: Subscription;
constructor( constructor(
private stateService: StateService, private stateService: StateService,
) {} ) {}
ngOnInit() { ngOnInit() {
this.subscription = this.stateService.isLoadingWebSocket$ this.isLoading$ = this.stateService.isLoadingWebSocket$;
.subscribe((isLoading) => this.isLoading = isLoading);
}
ngOnDestroy() {
this.subscription.unsubscribe();
} }
} }

View File

@ -1,10 +1,11 @@
import { Component, Input, OnChanges } from '@angular/core'; import { Component, Input, OnChanges, ChangeDetectionStrategy } from '@angular/core';
import * as Chartist from 'chartist'; import * as Chartist from 'chartist';
@Component({ @Component({
selector: 'app-fee-distribution-graph', selector: 'app-fee-distribution-graph',
templateUrl: './fee-distribution-graph.component.html', templateUrl: './fee-distribution-graph.component.html',
styleUrls: ['./fee-distribution-graph.component.scss'] styleUrls: ['./fee-distribution-graph.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class FeeDistributionGraphComponent implements OnChanges { export class FeeDistributionGraphComponent implements OnChanges {
@Input() feeRange; @Input() feeRange;

View File

@ -1,6 +1,6 @@
<div class="mempool-blocks-container"> <div class="mempool-blocks-container">
<div class="flashing"> <div class="flashing">
<ng-template ngFor let-projectedBlock [ngForOf]="mempoolBlocks" let-i="index" [ngForTrackBy]="trackByFn"> <ng-template ngFor let-projectedBlock [ngForOf]="mempoolBlocks$ | async" let-i="index" [ngForTrackBy]="trackByFn">
<div class="bitcoin-block text-center mempool-block" id="mempool-block-{{ i }}" [ngStyle]="mempoolBlockStyles[i]"> <div class="bitcoin-block text-center mempool-block" id="mempool-block-{{ i }}" [ngStyle]="mempoolBlockStyles[i]">
<a [routerLink]="['/mempool-block/' | relativeUrl, i]" class="blockLink">&nbsp;</a> <a [routerLink]="['/mempool-block/' | relativeUrl, i]" class="blockLink">&nbsp;</a>
<div class="block-body"> <div class="block-body">

View File

@ -1,27 +1,32 @@
import { Component, OnInit, OnDestroy, HostListener } from '@angular/core'; import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
import { Subscription, pipe } from 'rxjs'; import { Subscription, Observable, fromEvent, merge, of } from 'rxjs';
import { MempoolBlock } from 'src/app/interfaces/websocket.interface'; import { MempoolBlock } from 'src/app/interfaces/websocket.interface';
import { StateService } from 'src/app/services/state.service'; import { StateService } from 'src/app/services/state.service';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { take, map } from 'rxjs/operators'; import { take, map, switchMap } from 'rxjs/operators';
import { feeLevels, mempoolFeeColors } from 'src/app/app.constants'; import { feeLevels, mempoolFeeColors } from 'src/app/app.constants';
@Component({ @Component({
selector: 'app-mempool-blocks', selector: 'app-mempool-blocks',
templateUrl: './mempool-blocks.component.html', templateUrl: './mempool-blocks.component.html',
styleUrls: ['./mempool-blocks.component.scss'], styleUrls: ['./mempool-blocks.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class MempoolBlocksComponent implements OnInit, OnDestroy { export class MempoolBlocksComponent implements OnInit, OnDestroy {
mempoolBlocks: MempoolBlock[]; mempoolBlocks: MempoolBlock[];
mempoolBlocks$: Observable<MempoolBlock[]>;
mempoolBlocksFull: MempoolBlock[]; mempoolBlocksFull: MempoolBlock[];
mempoolBlockStyles = []; mempoolBlockStyles = [];
mempoolBlocksSubscription: Subscription; markBlocksSubscription: Subscription;
blockSubscription: Subscription;
networkSubscription: Subscription;
network = ''; network = '';
blockWidth = 125; blockWidth = 125;
blockPadding = 30; blockPadding = 30;
arrowVisible = false; arrowVisible = false;
tabHidden = true; tabHidden = false;
rightPosition = 0; rightPosition = 0;
transition = '2s'; transition = '2s';
@ -36,21 +41,25 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
constructor( constructor(
private router: Router, private router: Router,
private stateService: StateService, private stateService: StateService,
private cd: ChangeDetectorRef,
) { } ) { }
ngOnInit() { ngOnInit() {
this.stateService.isTabHidden$.subscribe((tabHidden) => this.tabHidden = tabHidden); this.stateService.isTabHidden$.subscribe((tabHidden) => this.tabHidden = tabHidden);
this.mempoolBlocksSubscription = this.stateService.mempoolBlocks$ this.mempoolBlocks$ = merge(
of(true),
fromEvent(window, 'resize')
)
.pipe( .pipe(
switchMap(() => this.stateService.mempoolBlocks$),
map((blocks) => { map((blocks) => {
if (!blocks.length) { if (!blocks.length) {
return [{ index: 0, blockSize: 0, blockVSize: 0, feeRange: [0, 0], medianFee: 0, nTx: 0, totalFees: 0 }]; return [{ index: 0, blockSize: 0, blockVSize: 0, feeRange: [0, 0], medianFee: 0, nTx: 0, totalFees: 0 }];
} }
return blocks; return blocks;
}), }),
) map((blocks) => {
.subscribe((blocks) => {
blocks.forEach((block, i) => { blocks.forEach((block, i) => {
block.index = this.blockIndex + i; block.index = this.blockIndex + i;
}); });
@ -59,9 +68,11 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
this.mempoolBlocks = this.reduceMempoolBlocksToFitScreen(JSON.parse(stringifiedBlocks)); this.mempoolBlocks = this.reduceMempoolBlocksToFitScreen(JSON.parse(stringifiedBlocks));
this.updateMempoolBlockStyles(); this.updateMempoolBlockStyles();
this.calculateTransactionPosition(); this.calculateTransactionPosition();
}); return this.mempoolBlocks;
})
);
this.stateService.markBlock$ this.markBlocksSubscription = this.stateService.markBlock$
.subscribe((state) => { .subscribe((state) => {
this.markIndex = undefined; this.markIndex = undefined;
this.txFeePerVSize = undefined; this.txFeePerVSize = undefined;
@ -72,16 +83,17 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
this.txFeePerVSize = state.txFeePerVSize; this.txFeePerVSize = state.txFeePerVSize;
} }
this.calculateTransactionPosition(); this.calculateTransactionPosition();
this.cd.markForCheck();
}); });
this.stateService.blocks$ this.blockSubscription = this.stateService.blocks$
.subscribe(([block]) => { .subscribe(([block]) => {
if (block.matchRate >= 66 && !this.tabHidden) { if (block.matchRate >= 66 && !this.tabHidden) {
this.blockIndex++; this.blockIndex++;
} }
}); });
this.stateService.networkChanged$ this.networkSubscription = this.stateService.networkChanged$
.subscribe((network) => this.network = network); .subscribe((network) => this.network = network);
this.stateService.keyNavigation$.subscribe((event) => { this.stateService.keyNavigation$.subscribe((event) => {
@ -109,15 +121,11 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
}); });
} }
@HostListener('window:resize', ['$event'])
onResize() {
if (this.mempoolBlocks && this.mempoolBlocks.length) {
this.mempoolBlocks = this.reduceMempoolBlocksToFitScreen(JSON.parse(JSON.stringify(this.mempoolBlocksFull)));
}
}
ngOnDestroy() { ngOnDestroy() {
this.mempoolBlocksSubscription.unsubscribe(); this.markBlocksSubscription.unsubscribe();
this.blockSubscription.unsubscribe();
this.networkSubscription.unsubscribe();
clearTimeout(this.resetTransitionTimeout);
} }
trackByFn(index: number, block: MempoolBlock) { trackByFn(index: number, block: MempoolBlock) {
@ -192,7 +200,11 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
this.transition = 'inherit'; this.transition = 'inherit';
this.rightPosition = this.markIndex * (this.blockWidth + this.blockPadding) + 0.5 * this.blockWidth; this.rightPosition = this.markIndex * (this.blockWidth + this.blockPadding) + 0.5 * this.blockWidth;
this.arrowVisible = true; this.arrowVisible = true;
this.resetTransitionTimeout = window.setTimeout(() => this.transition = '2s', 100);
this.resetTransitionTimeout = window.setTimeout(() => {
this.transition = '2s';
this.cd.markForCheck();
}, 100);
return; return;
} }

View File

@ -1,7 +1,7 @@
import { Component, OnInit, OnDestroy } from '@angular/core'; import { Component, OnInit, OnDestroy } from '@angular/core';
import { ElectrsApiService } from '../../services/electrs-api.service'; import { ElectrsApiService } from '../../services/electrs-api.service';
import { ActivatedRoute, ParamMap } from '@angular/router'; import { ActivatedRoute, ParamMap } from '@angular/router';
import { switchMap, filter, take, catchError, flatMap } from 'rxjs/operators'; import { switchMap, filter, catchError } from 'rxjs/operators';
import { Transaction, Block } from '../../interfaces/electrs.interface'; import { Transaction, Block } from '../../interfaces/electrs.interface';
import { of, merge, Subscription, Observable } from 'rxjs'; import { of, merge, Subscription, Observable } from 'rxjs';
import { StateService } from '../../services/state.service'; import { StateService } from '../../services/state.service';
@ -9,7 +9,6 @@ import { WebsocketService } from '../../services/websocket.service';
import { AudioService } from 'src/app/services/audio.service'; import { AudioService } from 'src/app/services/audio.service';
import { ApiService } from 'src/app/services/api.service'; import { ApiService } from 'src/app/services/api.service';
import { SeoService } from 'src/app/services/seo.service'; import { SeoService } from 'src/app/services/seo.service';
import { BisqTransaction } from 'src/app/bisq/bisq.interfaces';
@Component({ @Component({
selector: 'app-transaction', selector: 'app-transaction',

View File

@ -1,15 +1,19 @@
import { Component, ChangeDetectionStrategy, OnChanges, Input, OnInit, ChangeDetectorRef } from '@angular/core'; import { Component, ChangeDetectionStrategy, OnChanges, Input, OnInit, ChangeDetectorRef, OnDestroy } from '@angular/core';
import { Transaction, Block } from 'src/app/interfaces/electrs.interface'; import { Transaction, Block } from 'src/app/interfaces/electrs.interface';
import { StateService } from 'src/app/services/state.service'; import { StateService } from 'src/app/services/state.service';
import { Subscription } from 'rxjs';
@Component({ @Component({
selector: 'app-tx-fee-rating', selector: 'app-tx-fee-rating',
templateUrl: './tx-fee-rating.component.html', templateUrl: './tx-fee-rating.component.html',
styleUrls: ['./tx-fee-rating.component.scss'], styleUrls: ['./tx-fee-rating.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class TxFeeRatingComponent implements OnInit, OnChanges { export class TxFeeRatingComponent implements OnInit, OnChanges, OnDestroy {
@Input() tx: Transaction; @Input() tx: Transaction;
blocksSubscription: Subscription;
medianFeeNeeded: number; medianFeeNeeded: number;
overpaidTimes: number; overpaidTimes: number;
feeRating: number; feeRating: number;
@ -18,13 +22,15 @@ export class TxFeeRatingComponent implements OnInit, OnChanges {
constructor( constructor(
private stateService: StateService, private stateService: StateService,
private cd: ChangeDetectorRef,
) { } ) { }
ngOnInit() { ngOnInit() {
this.stateService.blocks$.subscribe(([block]) => { this.blocksSubscription = this.stateService.blocks$.subscribe(([block]) => {
this.blocks.push(block); this.blocks.push(block);
if (this.tx.status.confirmed && this.tx.status.block_height === block.height) { if (this.tx.status.confirmed && this.tx.status.block_height === block.height) {
this.calculateRatings(block); this.calculateRatings(block);
this.cd.markForCheck();
} }
}); });
} }
@ -41,6 +47,10 @@ export class TxFeeRatingComponent implements OnInit, OnChanges {
} }
} }
ngOnDestroy() {
this.blocksSubscription.unsubscribe();
}
calculateRatings(block: Block) { calculateRatings(block: Block) {
const feePervByte = this.tx.fee / (this.tx.weight / 4); const feePervByte = this.tx.fee / (this.tx.weight / 4);
this.medianFeeNeeded = Math.round(block.feeRange[Math.round(block.feeRange.length * 0.5)]); this.medianFeeNeeded = Math.round(block.feeRange[Math.round(block.feeRange.length * 0.5)]);