Skip to content

Commit 9898996

Browse files
committed
Refactored frontend data handling.
1 parent 1d65ae5 commit 9898996

File tree

14 files changed

+320
-195
lines changed

14 files changed

+320
-195
lines changed

frontend/src/app/app.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export class AppComponent implements OnInit {
2424
txId: ['', Validators.pattern('^[a-fA-F0-9]{64}$')],
2525
});
2626

27-
this.memPoolService.isOffline
27+
this.memPoolService.isOffline$
2828
.subscribe((state) => {
2929
this.isOffline = state;
3030
});
@@ -42,7 +42,7 @@ export class AppComponent implements OnInit {
4242
} else {
4343
this.router.navigate(['/tx/', txId]);
4444
}
45-
this.memPoolService.txIdSearch.next(txId);
45+
this.memPoolService.txIdSearch$.next(txId);
4646
this.searchForm.setValue({
4747
txId: '',
4848
});

frontend/src/app/blockchain-blocks/block-modal/block-modal.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export class BlockModalComponent implements OnInit {
5656
]
5757
};
5858

59-
this.memPoolService.conversions
59+
this.memPoolService.conversions$
6060
.subscribe((conversions) => {
6161
this.conversions = conversions;
6262
});

frontend/src/app/blockchain-blocks/blockchain-blocks.component.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,33 @@
1-
import { Component, Input } from '@angular/core';
1+
import { Component, OnInit, OnDestroy } from '@angular/core';
22
import { IBlock } from '../blockchain/interfaces';
33
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
44
import { BlockModalComponent } from './block-modal/block-modal.component';
5+
import { MemPoolService } from '../services/mem-pool.service';
6+
import { Subscription } from 'rxjs';
57

68
@Component({
79
selector: 'app-blockchain-blocks',
810
templateUrl: './blockchain-blocks.component.html',
911
styleUrls: ['./blockchain-blocks.component.scss']
1012
})
11-
export class BlockchainBlocksComponent {
12-
13-
@Input() blocks: IBlock[];
13+
export class BlockchainBlocksComponent implements OnInit, OnDestroy {
14+
blocks: IBlock[] = [];
15+
blocksSubscription: Subscription;
1416

1517
constructor(
1618
private modalService: NgbModal,
19+
private memPoolService: MemPoolService,
1720
) { }
1821

22+
ngOnInit() {
23+
this.blocksSubscription = this.memPoolService.blocks$
24+
.subscribe((block) => this.blocks.unshift(block));
25+
}
26+
27+
ngOnDestroy() {
28+
this.blocksSubscription.unsubscribe();
29+
}
30+
1931
getTimeSinceMined(block: IBlock): string {
2032
const minutes = ((new Date().getTime()) - (new Date(block.time * 1000).getTime())) / 1000 / 60;
2133
if (minutes >= 120) {

frontend/src/app/blockchain-projected-blocks/blockchain-projected-blocks.component.scss

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,6 @@
99
font-weight: bold;
1010
}
1111

12-
.blocks-container {
13-
position: absolute;
14-
top: 0px;
15-
left: 40px;
16-
}
17-
1812
.projected-blocks-container {
1913
position: absolute;
2014
top: 0px;

frontend/src/app/blockchain-projected-blocks/blockchain-projected-blocks.component.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,33 @@
1-
import { Component, Input } from '@angular/core';
1+
import { Component, OnInit, OnDestroy } from '@angular/core';
22
import { IProjectedBlock, IBlock } from '../blockchain/interfaces';
33
import { ProjectedBlockModalComponent } from './projected-block-modal/projected-block-modal.component';
44
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
5+
import { MemPoolService } from '../services/mem-pool.service';
6+
import { Subscription } from 'rxjs';
57

68
@Component({
79
selector: 'app-blockchain-projected-blocks',
810
templateUrl: './blockchain-projected-blocks.component.html',
911
styleUrls: ['./blockchain-projected-blocks.component.scss']
1012
})
11-
export class BlockchainProjectedBlocksComponent {
12-
13-
@Input() projectedBlocks: IProjectedBlock[];
13+
export class BlockchainProjectedBlocksComponent implements OnInit, OnDestroy {
14+
projectedBlocks: IProjectedBlock[];
15+
subscription: Subscription;
1416

1517
constructor(
1618
private modalService: NgbModal,
19+
private memPoolService: MemPoolService,
1720
) { }
1821

22+
ngOnInit() {
23+
this.subscription = this.memPoolService.projectedBlocks$
24+
.subscribe((projectedblocks) => this.projectedBlocks = projectedblocks);
25+
}
26+
27+
ngOnDestroy() {
28+
this.subscription.unsubscribe();
29+
}
30+
1931
trackByProjectedFn(index: number) {
2032
return index;
2133
}

frontend/src/app/blockchain-projected-blocks/projected-block-modal/projected-block-modal.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export class ProjectedBlockModalComponent implements OnInit {
5757
]
5858
};
5959

60-
this.memPoolService.conversions
60+
this.memPoolService.conversions$
6161
.subscribe((conversions) => {
6262
this.conversions = conversions;
6363
});
Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,24 @@
1-
<div *ngIf="blocks.length === 0" class="text-center">
1+
<div *ngIf="isLoading" class="text-center">
22
<h3>Loading blocks...</h3>
33
<br>
44
<div class="spinner-border text-light"></div>
55
</div>
6-
<div *ngIf="blocks.length !== 0 && txTrackingLoading" class="text-center black-background">
6+
<div *ngIf="!isLoading && txTrackingLoading" class="text-center black-background">
77
<h3>Locating transaction...</h3>
88
</div>
99
<div *ngIf="txShowTxNotFound" class="text-center black-background">
1010
<h3>Transaction not found!</h3>
1111
</div>
1212
<div class="text-center" class="blockchain-wrapper">
1313
<div class="position-container">
14+
<app-blockchain-projected-blocks></app-blockchain-projected-blocks>
15+
<app-blockchain-blocks></app-blockchain-blocks>
1416

15-
<app-blockchain-projected-blocks [projectedBlocks]="projectedBlocks"></app-blockchain-projected-blocks>
16-
<app-blockchain-blocks [blocks]="blocks"></app-blockchain-blocks>
17-
18-
<div id="divider" *ngIf="blocks.length"></div>
17+
<div id="divider" *ngIf="!isLoading"></div>
1918
</div>
2019

2120
</div>
2221

23-
<app-tx-bubble *ngIf="blocks?.length && txTrackingTx" [tx]="txTrackingTx" [arrowPosition]="txBubbleArrowPosition" [ngStyle]="txBubbleStyle" [latestBlockHeight]="blocks[0].height" [txTrackingBlockHeight]="txTrackingBlockHeight"></app-tx-bubble>
22+
<app-tx-bubble></app-tx-bubble>
2423

2524
<app-footer></app-footer>
Lines changed: 25 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,22 @@
1-
import { Component, OnInit, OnDestroy, Renderer2, HostListener } from '@angular/core';
2-
import { IMempoolDefaultResponse, IBlock, IProjectedBlock, ITransaction } from './interfaces';
3-
import { retryWhen, tap } from 'rxjs/operators';
4-
import { MemPoolService } from '../services/mem-pool.service';
1+
import { Component, OnInit, OnDestroy, Renderer2 } from '@angular/core';
2+
import { MemPoolService, ITxTracking } from '../services/mem-pool.service';
53
import { ApiService } from '../services/api.service';
64
import { ActivatedRoute, ParamMap } from '@angular/router';
5+
import { Subscription } from 'rxjs';
6+
import { take } from 'rxjs/operators';
77

88
@Component({
99
selector: 'app-blockchain',
1010
templateUrl: './blockchain.component.html',
1111
styleUrls: ['./blockchain.component.scss']
1212
})
1313
export class BlockchainComponent implements OnInit, OnDestroy {
14-
blocks: IBlock[] = [];
15-
projectedBlocks: IProjectedBlock[] = [];
16-
subscription: any;
17-
socket: any;
18-
txBubbleStyle: any = {};
14+
txTrackingSubscription: Subscription;
15+
blocksSubscription: Subscription;
1916

2017
txTrackingLoading = false;
21-
txTrackingEnabled = false;
22-
txTrackingTx: ITransaction | null = null;
23-
txTrackingBlockHeight = 0;
2418
txShowTxNotFound = false;
25-
txBubbleArrowPosition = 'top';
26-
27-
@HostListener('window:resize', ['$event'])
28-
onResize(event: Event) {
29-
this.moveTxBubbleToPosition();
30-
}
19+
isLoading = true;
3120

3221
constructor(
3322
private memPoolService: MemPoolService,
@@ -37,72 +26,15 @@ export class BlockchainComponent implements OnInit, OnDestroy {
3726
) {}
3827

3928
ngOnInit() {
40-
41-
this.txBubbleStyle = {
42-
'position': 'absolute',
43-
'top': '425px',
44-
'visibility': 'hidden',
45-
};
46-
this.socket = this.apiService.websocketSubject;
47-
this.subscription = this.socket
48-
.pipe(
49-
retryWhen((errors: any) => errors.pipe(
50-
tap(() => this.memPoolService.isOffline.next(true))))
51-
)
52-
.subscribe((response: IMempoolDefaultResponse) => {
53-
this.memPoolService.isOffline.next(false);
54-
if (response.mempoolInfo && response.txPerSecond !== undefined) {
55-
this.memPoolService.loaderSubject.next({
56-
memPoolInfo: response.mempoolInfo,
57-
txPerSecond: response.txPerSecond,
58-
vBytesPerSecond: response.vBytesPerSecond,
59-
});
60-
}
61-
if (response.blocks && response.blocks.length) {
62-
this.blocks = response.blocks;
63-
this.blocks.reverse();
64-
}
65-
if (response.block) {
66-
if (!this.blocks.some((block) => response.block !== undefined && response.block.height === block.height )) {
67-
this.blocks.unshift(response.block);
68-
if (this.blocks.length >= 8) {
69-
this.blocks.pop();
70-
}
71-
}
72-
}
73-
if (response.conversions) {
74-
this.memPoolService.conversions.next(response.conversions);
75-
}
76-
if (response.projectedBlocks) {
77-
this.projectedBlocks = response.projectedBlocks;
78-
const mempoolWeight = this.projectedBlocks.map((block) => block.blockWeight).reduce((a, b) => a + b);
79-
this.memPoolService.mempoolWeight.next(mempoolWeight);
80-
}
81-
if (response['track-tx']) {
82-
if (response['track-tx'].tracking) {
83-
this.txTrackingEnabled = true;
84-
this.txTrackingBlockHeight = response['track-tx'].blockHeight;
85-
if (response['track-tx'].tx) {
86-
this.txTrackingTx = response['track-tx'].tx;
87-
this.txTrackingLoading = false;
88-
}
89-
} else {
90-
this.txTrackingEnabled = false;
91-
this.txTrackingTx = null;
92-
this.txTrackingBlockHeight = 0;
93-
}
94-
if (response['track-tx'].message && response['track-tx'].message === 'not-found') {
95-
this.txTrackingLoading = false;
96-
this.txShowTxNotFound = true;
97-
setTimeout(() => { this.txShowTxNotFound = false; }, 2000);
98-
}
99-
setTimeout(() => {
100-
this.moveTxBubbleToPosition();
101-
});
29+
this.txTrackingSubscription = this.memPoolService.txTracking$
30+
.subscribe((response: ITxTracking) => {
31+
this.txTrackingLoading = false;
32+
this.txShowTxNotFound = response.notFound;
33+
if (this.txShowTxNotFound) {
34+
setTimeout(() => { this.txShowTxNotFound = false; }, 2000);
10235
}
103-
},
104-
(err: Error) => console.log(err)
105-
);
36+
});
37+
10638
this.renderer.addClass(document.body, 'disable-scroll');
10739

10840
this.route.paramMap
@@ -112,73 +44,27 @@ export class BlockchainComponent implements OnInit, OnDestroy {
11244
return;
11345
}
11446
this.txTrackingLoading = true;
115-
this.socket.next({'action': 'track-tx', 'txId': txId});
47+
this.apiService.sendWebSocket({'action': 'track-tx', 'txId': txId});
11648
});
11749

118-
this.memPoolService.txIdSearch
50+
this.memPoolService.txIdSearch$
11951
.subscribe((txId) => {
12052
if (txId) {
12153
this.txTrackingLoading = true;
122-
this.socket.next({'action': 'track-tx', 'txId': txId});
54+
this.apiService.sendWebSocket({'action': 'track-tx', 'txId': txId});
12355
}
12456
});
125-
}
126-
127-
moveTxBubbleToPosition() {
128-
let element: HTMLElement | null = null;
129-
if (this.txTrackingBlockHeight === 0) {
130-
const index = this.projectedBlocks.findIndex((pB) => pB.hasMytx);
131-
if (index > -1) {
132-
element = document.getElementById('projected-block-' + index);
133-
} else {
134-
return;
135-
}
136-
} else {
137-
element = document.getElementById('bitcoin-block-' + this.txTrackingBlockHeight);
138-
}
139-
140-
this.txBubbleStyle['visibility'] = 'visible';
141-
this.txBubbleStyle['position'] = 'absolute';
14257

143-
if (!element) {
144-
if (window.innerWidth <= 768) {
145-
this.txBubbleArrowPosition = 'bottom';
146-
this.txBubbleStyle['left'] = window.innerWidth / 2 - 50 + 'px';
147-
this.txBubbleStyle['bottom'] = '270px';
148-
this.txBubbleStyle['top'] = 'inherit';
149-
this.txBubbleStyle['position'] = 'fixed';
150-
} else {
151-
this.txBubbleStyle['left'] = window.innerWidth - 220 + 'px';
152-
this.txBubbleArrowPosition = 'right';
153-
this.txBubbleStyle['top'] = '425px';
154-
}
155-
} else {
156-
this.txBubbleArrowPosition = 'top';
157-
const domRect: DOMRect | ClientRect = element.getBoundingClientRect();
158-
this.txBubbleStyle['left'] = domRect.left - 50 + 'px';
159-
this.txBubbleStyle['top'] = domRect.top + 125 + window.scrollY + 'px';
160-
161-
if (domRect.left + 100 > window.innerWidth) {
162-
this.txBubbleStyle['left'] = window.innerWidth - 220 + 'px';
163-
this.txBubbleArrowPosition = 'right';
164-
} else if (domRect.left + 220 > window.innerWidth) {
165-
this.txBubbleStyle['left'] = window.innerWidth - 240 + 'px';
166-
this.txBubbleArrowPosition = 'top-right';
167-
} else {
168-
this.txBubbleStyle['left'] = domRect.left + 15 + 'px';
169-
}
170-
171-
if (domRect.left < 86) {
172-
this.txBubbleArrowPosition = 'top-left';
173-
this.txBubbleStyle['left'] = 125 + 'px';
174-
}
175-
}
58+
this.blocksSubscription = this.memPoolService.blocks$
59+
.pipe(
60+
take(1)
61+
)
62+
.subscribe((block) => this.isLoading = false);
17663
}
17764

17865
ngOnDestroy() {
179-
if (this.subscription) {
180-
this.subscription.unsubscribe();
181-
}
66+
this.blocksSubscription.unsubscribe();
67+
this.txTrackingSubscription.unsubscribe();
18268
this.renderer.removeClass(document.body, 'disable-scroll');
18369
}
18470
}

frontend/src/app/footer/footer.component.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { Component, OnInit } from '@angular/core';
2-
import { MemPoolService, MemPoolState } from '../services/mem-pool.service';
2+
import { MemPoolService, IMemPoolState } from '../services/mem-pool.service';
33

44
@Component({
55
selector: 'app-footer',
66
templateUrl: './footer.component.html',
77
styleUrls: ['./footer.component.scss']
88
})
99
export class FooterComponent implements OnInit {
10-
memPoolInfo: MemPoolState | undefined;
10+
memPoolInfo: IMemPoolState | undefined;
1111
mempoolBlocks = 0;
1212
progressWidth = '';
1313
progressClass: string;
@@ -17,12 +17,12 @@ export class FooterComponent implements OnInit {
1717
) { }
1818

1919
ngOnInit() {
20-
this.memPoolService.loaderSubject
20+
this.memPoolService.mempoolStats$
2121
.subscribe((mempoolState) => {
2222
this.memPoolInfo = mempoolState;
2323
this.updateProgress();
2424
});
25-
this.memPoolService.mempoolWeight
25+
this.memPoolService.mempoolWeight$
2626
.subscribe((mempoolWeight) => {
2727
this.mempoolBlocks = Math.ceil(mempoolWeight / 4000000);
2828
});

0 commit comments

Comments
 (0)