UI/UX: Fix blockchain-blocks skeleton. (#697)
* Fix blockchain-blocks skeleton. * Fix blockchain skeleton background. * Fix mempool blockchain skeleton. * Add e2e testing. Add tsconfig. Fix mempool fit to screen. * Fix wrong return. Fix e2e testing. * Fix blockchainblocks connectionstate. Add init action to websocket mock. Add e2e testing for droping websock connection. * Ref e2e code for websocket connection. Fix blockchain blocks skeleton. * Fix state connections. Remove .only e2e tests. * Fix mempool blocks skeleton. * Add fit screen to empty blocks.
This commit is contained in:
		
							parent
							
								
									35d4abcc25
								
							
						
					
					
						commit
						6dffbd40f3
					
				@ -1,4 +1,4 @@
 | 
				
			|||||||
import { emitMempoolInfo, emitWithoutMempoolInfo } from "../../support/websocket";
 | 
					import { emitMempoolInfo, dropWebSocket } from "../../support/websocket";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('Mainnet', () => {
 | 
					describe('Mainnet', () => {
 | 
				
			||||||
    beforeEach(() => {
 | 
					    beforeEach(() => {
 | 
				
			||||||
@ -31,6 +31,23 @@ describe('Mainnet', () => {
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('loads dashboard, drop websocket and reconnect', () => {
 | 
				
			||||||
 | 
					        cy.viewport('macbook-16');
 | 
				
			||||||
 | 
					        cy.mockMempoolSocket();
 | 
				
			||||||
 | 
					        cy.visit('/');
 | 
				
			||||||
 | 
					        cy.get('.badge').should('not.exist');
 | 
				
			||||||
 | 
					        dropWebSocket();
 | 
				
			||||||
 | 
					        cy.get('.badge').should('be.visible');        
 | 
				
			||||||
 | 
					        cy.get('.badge', {timeout: 25000}).should('not.exist');
 | 
				
			||||||
 | 
					        emitMempoolInfo({
 | 
				
			||||||
 | 
					            'params': {
 | 
				
			||||||
 | 
					              loaded: true
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        cy.get(':nth-child(1) > #bitcoin-block-0').should('not.exist');
 | 
				
			||||||
 | 
					        cy.get(':nth-child(2) > #bitcoin-block-0').should('not.exist');
 | 
				
			||||||
 | 
					        cy.get(':nth-child(3) > #bitcoin-block-0').should('not.exist');
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('loads the dashboard', () => {
 | 
					    it('loads the dashboard', () => {
 | 
				
			||||||
        cy.visit('/');
 | 
					        cy.visit('/');
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										10
									
								
								frontend/cypress/support/index.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								frontend/cypress/support/index.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					/// <reference types="cypress" />
 | 
				
			||||||
 | 
					declare namespace Cypress {
 | 
				
			||||||
 | 
					    interface Chainable<Subject> {
 | 
				
			||||||
 | 
					        waitForSkeletonGone(): Chainable<any>
 | 
				
			||||||
 | 
					        waitForPageIdle(): Chainable<any>
 | 
				
			||||||
 | 
					        mockMempoolSocket(): Chainable<any>
 | 
				
			||||||
 | 
					        changeNetwork(network: "testnet"|"signet"|"liquid"|"bisq"|"mainnet"): Chainable<any>
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -38,6 +38,7 @@ export const mockWebSocket = () => {
 | 
				
			|||||||
				win.mockServer = server;
 | 
									win.mockServer = server;
 | 
				
			||||||
				win.mockServer.on('connection', (socket) => {
 | 
									win.mockServer.on('connection', (socket) => {
 | 
				
			||||||
					win.mockSocket = socket;
 | 
										win.mockSocket = socket;
 | 
				
			||||||
 | 
					                    win.mockSocket.send('{"action":"init"}');
 | 
				
			||||||
				});
 | 
									});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                win.mockServer.on('message', (message) => {
 | 
					                win.mockServer.on('message', (message) => {
 | 
				
			||||||
@ -81,4 +82,11 @@ export const emitMempoolInfo = ({
 | 
				
			|||||||
	});
 | 
						});
 | 
				
			||||||
    cy.waitForSkeletonGone();
 | 
					    cy.waitForSkeletonGone();
 | 
				
			||||||
    return cy.get('#mempool-block-0');
 | 
					    return cy.get('#mempool-block-0');
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const dropWebSocket = (() => {
 | 
				
			||||||
 | 
					    cy.window().then((win) => {
 | 
				
			||||||
 | 
					        win.mockServer.simulate("error");
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    return cy.wait(500);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@ -2,7 +2,9 @@
 | 
				
			|||||||
  "extends": "../tsconfig.json",
 | 
					  "extends": "../tsconfig.json",
 | 
				
			||||||
  "include": ["**/*.ts"],
 | 
					  "include": ["**/*.ts"],
 | 
				
			||||||
  "compilerOptions": {
 | 
					  "compilerOptions": {
 | 
				
			||||||
    "sourceMap": false,
 | 
					    "types": ["cypress"],
 | 
				
			||||||
    "types": ["cypress"]
 | 
					    "lib": ["es2015", "dom"],
 | 
				
			||||||
 | 
					    "allowJs": true,
 | 
				
			||||||
 | 
					    "noEmit": true,
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
<div class="blocks-container" *ngIf="!loadingBlocks; else loadingBlocksTemplate">
 | 
					<div class="blocks-container" *ngIf="(loadingBlocks$ | async) === false; else loadingBlocksTemplate">
 | 
				
			||||||
  <div *ngFor="let block of blocks; let i = index; trackBy: trackByBlocksFn" >
 | 
					  <div *ngFor="let block of blocks; let i = index; trackBy: trackByBlocksFn" >
 | 
				
			||||||
    <div class="text-center bitcoin-block mined-block" id="bitcoin-block-{{ block.height }}" [ngStyle]="blockStyles[i]">
 | 
					    <div class="text-center bitcoin-block mined-block" id="bitcoin-block-{{ block.height }}" [ngStyle]="blockStyles[i]">
 | 
				
			||||||
      <a [routerLink]="['/block/' | relativeUrl, block.id]" [state]="{ data: { block: block } }" class="blockLink"> </a>
 | 
					      <a [routerLink]="['/block/' | relativeUrl, block.id]" [state]="{ data: { block: block } }" class="blockLink"> </a>
 | 
				
			||||||
@ -28,8 +28,8 @@
 | 
				
			|||||||
<ng-template #loadingBlocksTemplate >
 | 
					<ng-template #loadingBlocksTemplate >
 | 
				
			||||||
  <div class="blocks-container">
 | 
					  <div class="blocks-container">
 | 
				
			||||||
    <div class="flashing">
 | 
					    <div class="flashing">
 | 
				
			||||||
      <div *ngFor="let block of blocks; let i = index; trackBy: trackByBlocksFn" >
 | 
					      <div *ngFor="let block of emptyBlocks; let i = index; trackBy: trackByBlocksFn" >
 | 
				
			||||||
        <div class="text-center bitcoin-block mined-block" id="bitcoin-block-{{ block.height }}" [ngStyle]="blockStyles[i]"></div>
 | 
					        <div class="text-center bitcoin-block mined-block" id="bitcoin-block-{{ block.height }}" [ngStyle]="emptyBlockStyles[i]"></div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef, Input } from '@angular/core';
 | 
					import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef, Input } from '@angular/core';
 | 
				
			||||||
import { Subscription } from 'rxjs';
 | 
					import { Observable, 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';
 | 
				
			||||||
@ -13,17 +13,18 @@ import { Router } from '@angular/router';
 | 
				
			|||||||
export class BlockchainBlocksComponent implements OnInit, OnDestroy {
 | 
					export class BlockchainBlocksComponent implements OnInit, OnDestroy {
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  network = '';
 | 
					  network = '';
 | 
				
			||||||
  blocks: Block[] = this.mountEmptyBlocks();
 | 
					  blocks: Block[] = [];
 | 
				
			||||||
 | 
					  emptyBlocks: Block[] = this.mountEmptyBlocks();
 | 
				
			||||||
  markHeight: number;
 | 
					  markHeight: number;
 | 
				
			||||||
  blocksSubscription: Subscription;
 | 
					  blocksSubscription: Subscription;
 | 
				
			||||||
  networkSubscription: Subscription;
 | 
					  networkSubscription: Subscription;
 | 
				
			||||||
  tabHiddenSubscription: Subscription;
 | 
					  tabHiddenSubscription: Subscription;
 | 
				
			||||||
  markBlockSubscription: Subscription;
 | 
					  markBlockSubscription: Subscription;
 | 
				
			||||||
  isLoadingWebsocketSubscription: Subscription;
 | 
					  loadingBlocks$: Observable<boolean>;
 | 
				
			||||||
  blockStyles = [];
 | 
					  blockStyles = [];
 | 
				
			||||||
 | 
					  emptyBlockStyles = [];
 | 
				
			||||||
  interval: any;
 | 
					  interval: any;
 | 
				
			||||||
  tabHidden = false;
 | 
					  tabHidden = false;
 | 
				
			||||||
  loadingBlocks = false;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  arrowVisible = false;
 | 
					  arrowVisible = false;
 | 
				
			||||||
  arrowLeftPx = 30;
 | 
					  arrowLeftPx = 30;
 | 
				
			||||||
@ -45,11 +46,10 @@ export class BlockchainBlocksComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
  ) { }
 | 
					  ) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit() {
 | 
					  ngOnInit() {
 | 
				
			||||||
    this.blocks.forEach((b) => this.blockStyles.push(this.getStyleForBlock(b)));
 | 
					    this.emptyBlocks.forEach((b) => this.emptyBlockStyles.push(this.getStyleForEmptyBlock(b)));
 | 
				
			||||||
    this.isLoadingWebsocketSubscription = this.stateService.isLoadingWebSocket$.subscribe((loading) => this.loadingBlocks = loading);
 | 
					    this.loadingBlocks$ = this.stateService.isLoadingWebSocket$;
 | 
				
			||||||
    this.networkSubscription = this.stateService.networkChanged$.subscribe((network) => this.network = network);
 | 
					    this.networkSubscription = this.stateService.networkChanged$.subscribe((network) => this.network = network);
 | 
				
			||||||
    this.tabHiddenSubscription = 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]) => {
 | 
				
			||||||
        if (this.blocks.some((b) => b.height === block.height)) {
 | 
					        if (this.blocks.some((b) => b.height === block.height)) {
 | 
				
			||||||
@ -75,15 +75,13 @@ export class BlockchainBlocksComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
          this.moveArrowToPosition(true, false);
 | 
					          this.moveArrowToPosition(true, false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!this.loadingBlocks) {
 | 
					        this.blockStyles = [];
 | 
				
			||||||
 | 
					        this.blocks.forEach((b) => this.blockStyles.push(this.getStyleForBlock(b)));
 | 
				
			||||||
 | 
					        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)));
 | 
				
			||||||
          setTimeout(() => {
 | 
					          this.cd.markForCheck();
 | 
				
			||||||
            this.blockStyles = [];
 | 
					        }, 50);
 | 
				
			||||||
            this.blocks.forEach((b) => this.blockStyles.push(this.getStyleForBlock(b)));
 | 
					 | 
				
			||||||
            this.cd.markForCheck();
 | 
					 | 
				
			||||||
          }, 50);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (this.blocks.length === this.stateService.env.KEEP_BLOCKS_AMOUNT) {
 | 
					        if (this.blocks.length === this.stateService.env.KEEP_BLOCKS_AMOUNT) {
 | 
				
			||||||
          this.blocksFilled = true;
 | 
					          this.blocksFilled = true;
 | 
				
			||||||
@ -129,7 +127,6 @@ export class BlockchainBlocksComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
    this.networkSubscription.unsubscribe();
 | 
					    this.networkSubscription.unsubscribe();
 | 
				
			||||||
    this.tabHiddenSubscription.unsubscribe();
 | 
					    this.tabHiddenSubscription.unsubscribe();
 | 
				
			||||||
    this.markBlockSubscription.unsubscribe();
 | 
					    this.markBlockSubscription.unsubscribe();
 | 
				
			||||||
    this.isLoadingWebsocketSubscription.unsubscribe();
 | 
					 | 
				
			||||||
    clearInterval(this.interval);
 | 
					    clearInterval(this.interval);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -186,9 +183,24 @@ export class BlockchainBlocksComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
      )`,
 | 
					      )`,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getStyleForEmptyBlock(block: Block) {
 | 
				
			||||||
 | 
					    let addLeft = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (block.stage === 1) {
 | 
				
			||||||
 | 
					      block.stage = 2;
 | 
				
			||||||
 | 
					      addLeft = -205;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      left: addLeft + 155 * this.emptyBlocks.indexOf(block) + 'px',
 | 
				
			||||||
 | 
					      background: "#2d3348",
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mountEmptyBlocks() {
 | 
					  mountEmptyBlocks() {
 | 
				
			||||||
    const emptyBlocks = [];
 | 
					    const emptyBlocks = [];
 | 
				
			||||||
    for (let i = 0; i < 9; i++) {
 | 
					    for (let i = 0; i < 8; i++) {
 | 
				
			||||||
      emptyBlocks.push({
 | 
					      emptyBlocks.push({
 | 
				
			||||||
        id: '',
 | 
					        id: '',
 | 
				
			||||||
        height: 0,
 | 
					        height: 0,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,47 +1,51 @@
 | 
				
			|||||||
<div class="mempool-blocks-container">
 | 
					<ng-container *ngIf="(loadingBlocks$ | async) === false; else loadingBlocks">
 | 
				
			||||||
  <div class="flashing" *ngIf="!loadingMempoolBlocks && (timeAvg$ | async) as timeAvg;">      
 | 
					  <div class="mempool-blocks-container" *ngIf="(timeAvg$ | async) as timeAvg;">
 | 
				
			||||||
    <ng-template ngFor let-projectedBlock [ngForOf]="mempoolBlocks$ | async" let-i="index" [ngForTrackBy]="trackByFn">
 | 
					    <div class="flashing">      
 | 
				
			||||||
      <div class="bitcoin-block text-center mempool-block" id="mempool-block-{{ i }}" [ngStyle]="mempoolBlockStyles[i]">
 | 
					      <ng-template ngFor let-projectedBlock [ngForOf]="mempoolBlocks$ | async" let-i="index" [ngForTrackBy]="trackByFn">
 | 
				
			||||||
        <a [routerLink]="['/mempool-block/' | relativeUrl, i]" class="blockLink"> </a>
 | 
					        <div class="bitcoin-block text-center mempool-block" id="mempool-block-{{ i }}" [ngStyle]="mempoolBlockStyles[i]">
 | 
				
			||||||
        <div class="block-body">
 | 
					          <a [routerLink]="['/mempool-block/' | relativeUrl, i]" class="blockLink"> </a>
 | 
				
			||||||
          <div class="fees">
 | 
					          <div class="block-body">
 | 
				
			||||||
            ~{{ projectedBlock.medianFee | number:'1.0-0' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
 | 
					            <div class="fees">
 | 
				
			||||||
          </div>
 | 
					              ~{{ projectedBlock.medianFee | number:'1.0-0' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
 | 
				
			||||||
          <div class="fee-span">
 | 
					 | 
				
			||||||
            {{ projectedBlock.feeRange[0] | number:'1.0-0' }} - {{ projectedBlock.feeRange[projectedBlock.feeRange.length - 1] | number:'1.0-0' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
          <div class="block-size" [innerHTML]="projectedBlock.blockSize | bytes: 2">‎</div>
 | 
					 | 
				
			||||||
          <div class="transaction-count">
 | 
					 | 
				
			||||||
            <ng-container *ngTemplateOutlet="projectedBlock.nTx === 1 ? transactionsSingular : transactionsPlural; context: {$implicit: projectedBlock.nTx | number}"></ng-container>
 | 
					 | 
				
			||||||
            <ng-template #transactionsSingular let-i i18n="shared.transaction-count.singular">{{ i }} transaction</ng-template>
 | 
					 | 
				
			||||||
            <ng-template #transactionsPlural let-i i18n="shared.transaction-count.plural">{{ i }} transactions</ng-template>
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
          <div class="time-difference" *ngIf="projectedBlock.blockVSize <= 1000000; else mergedBlock">
 | 
					 | 
				
			||||||
            <ng-template [ngIf]="network === 'liquid'" [ngIfElse]="timeDiffMainnet">
 | 
					 | 
				
			||||||
              <app-time-until [time]="(1 * i) + now + 61000" [fastRender]="false" [fixedRender]="true"></app-time-until>
 | 
					 | 
				
			||||||
            </ng-template>
 | 
					 | 
				
			||||||
            <ng-template #timeDiffMainnet>
 | 
					 | 
				
			||||||
              <app-time-until [time]="(timeAvg * i) + now + timeAvg" [fastRender]="false" [fixedRender]="true" [forceFloorOnTimeIntervals]="['hour']"></app-time-until>
 | 
					 | 
				
			||||||
            </ng-template>
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
          <ng-template #mergedBlock>
 | 
					 | 
				
			||||||
            <div class="time-difference">
 | 
					 | 
				
			||||||
              <b>(<ng-container *ngTemplateOutlet="blocksPlural; context: {$implicit: projectedBlock.blockVSize / 1000000 | ceil }"></ng-container>)</b>
 | 
					 | 
				
			||||||
              <ng-template #blocksPlural let-i i18n="shared.blocks">{{ i }} <span class="shared-block">blocks</span></ng-template>
 | 
					 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
          </ng-template>
 | 
					            <div class="fee-span">
 | 
				
			||||||
 | 
					              {{ projectedBlock.feeRange[0] | number:'1.0-0' }} - {{ projectedBlock.feeRange[projectedBlock.feeRange.length - 1] | number:'1.0-0' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="block-size" [innerHTML]="projectedBlock.blockSize | bytes: 2">‎</div>
 | 
				
			||||||
 | 
					            <div class="transaction-count">
 | 
				
			||||||
 | 
					              <ng-container *ngTemplateOutlet="projectedBlock.nTx === 1 ? transactionsSingular : transactionsPlural; context: {$implicit: projectedBlock.nTx | number}"></ng-container>
 | 
				
			||||||
 | 
					              <ng-template #transactionsSingular let-i i18n="shared.transaction-count.singular">{{ i }} transaction</ng-template>
 | 
				
			||||||
 | 
					              <ng-template #transactionsPlural let-i i18n="shared.transaction-count.plural">{{ i }} transactions</ng-template>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="time-difference" *ngIf="projectedBlock.blockVSize <= 1000000; else mergedBlock">
 | 
				
			||||||
 | 
					              <ng-template [ngIf]="network === 'liquid'" [ngIfElse]="timeDiffMainnet">
 | 
				
			||||||
 | 
					                <app-time-until [time]="(1 * i) + now + 61000" [fastRender]="false" [fixedRender]="true"></app-time-until>
 | 
				
			||||||
 | 
					              </ng-template>
 | 
				
			||||||
 | 
					              <ng-template #timeDiffMainnet>
 | 
				
			||||||
 | 
					                <app-time-until [time]="(timeAvg * i) + now + timeAvg" [fastRender]="false" [fixedRender]="true" [forceFloorOnTimeIntervals]="['hour']"></app-time-until>
 | 
				
			||||||
 | 
					              </ng-template>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <ng-template #mergedBlock>
 | 
				
			||||||
 | 
					              <div class="time-difference">
 | 
				
			||||||
 | 
					                <b>(<ng-container *ngTemplateOutlet="blocksPlural; context: {$implicit: projectedBlock.blockVSize / 1000000 | ceil }"></ng-container>)</b>
 | 
				
			||||||
 | 
					                <ng-template #blocksPlural let-i i18n="shared.blocks">{{ i }} <span class="shared-block">blocks</span></ng-template>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            </ng-template>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <span class="animated-border"></span>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <span class="animated-border"></span>
 | 
					      </ng-template>
 | 
				
			||||||
      </div>
 | 
					    </div>
 | 
				
			||||||
    </ng-template>
 | 
					    <div *ngIf="arrowVisible" id="arrow-up" [ngStyle]="{'right': rightPosition + 75 + 'px', transition: transition }"></div>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
  <div *ngIf="arrowVisible" id="arrow-up" [ngStyle]="{'right': rightPosition + 75 + 'px', transition: transition }"></div>
 | 
					</ng-container>
 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="mempool-blocks-container" *ngIf="loadingMempoolBlocks === true">
 | 
					<ng-template #loadingBlocks>
 | 
				
			||||||
  <div class="flashing">
 | 
					  <div class="mempool-blocks-container">
 | 
				
			||||||
    <ng-template ngFor let-projectedBlock [ngForOf]="mempoolBlocks" let-i="index" [ngForTrackBy]="trackByFn">
 | 
					    <div class="flashing">
 | 
				
			||||||
      <div class="bitcoin-block text-center mempool-block" id="mempool-block-{{ i }}" [ngStyle]="mempoolBlockStyles[i]"></div>
 | 
					      <ng-template ngFor let-projectedBlock [ngForOf]="mempoolEmptyBlocks" let-i="index" [ngForTrackBy]="trackByFn">
 | 
				
			||||||
    </ng-template>
 | 
					        <div class="bitcoin-block text-center mempool-block" id="mempool-block-{{ i }}" [ngStyle]="mempoolEmptyBlockStyles[i]"></div>
 | 
				
			||||||
 | 
					      </ng-template>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
</div>
 | 
					</ng-template>
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@ import { Subscription, Observable, fromEvent, merge, of, combineLatest, timer }
 | 
				
			|||||||
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, switchMap } from 'rxjs/operators';
 | 
					import { take, map, switchMap, share } from 'rxjs/operators';
 | 
				
			||||||
import { feeLevels, mempoolFeeColors } from 'src/app/app.constants';
 | 
					import { feeLevels, mempoolFeeColors } from 'src/app/app.constants';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
@ -14,12 +14,15 @@ import { feeLevels, mempoolFeeColors } from 'src/app/app.constants';
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
export class MempoolBlocksComponent implements OnInit, OnDestroy {
 | 
					export class MempoolBlocksComponent implements OnInit, OnDestroy {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mempoolBlocks: MempoolBlock[] = this.mountEmptyBlocks();
 | 
					  mempoolBlocks: MempoolBlock[] = [];
 | 
				
			||||||
 | 
					  mempoolEmptyBlocks: MempoolBlock[] = this.mountEmptyBlocks();
 | 
				
			||||||
  mempoolBlocks$: Observable<MempoolBlock[]>;
 | 
					  mempoolBlocks$: Observable<MempoolBlock[]>;
 | 
				
			||||||
  timeAvg$: Observable<number>;
 | 
					  timeAvg$: Observable<number>;
 | 
				
			||||||
 | 
					  loadingBlocks$: Observable<boolean>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mempoolBlocksFull: MempoolBlock[] = this.mountEmptyBlocks();
 | 
					  mempoolBlocksFull: MempoolBlock[] = [];
 | 
				
			||||||
  mempoolBlockStyles = [];
 | 
					  mempoolBlockStyles = [];
 | 
				
			||||||
 | 
					  mempoolEmptyBlockStyles = [];
 | 
				
			||||||
  markBlocksSubscription: Subscription;
 | 
					  markBlocksSubscription: Subscription;
 | 
				
			||||||
  isLoadingWebsocketSubscription: Subscription;
 | 
					  isLoadingWebsocketSubscription: Subscription;
 | 
				
			||||||
  blockSubscription: Subscription;
 | 
					  blockSubscription: Subscription;
 | 
				
			||||||
@ -31,7 +34,6 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
  blockPadding = 30;
 | 
					  blockPadding = 30;
 | 
				
			||||||
  arrowVisible = false;
 | 
					  arrowVisible = false;
 | 
				
			||||||
  tabHidden = false;
 | 
					  tabHidden = false;
 | 
				
			||||||
  loadingMempoolBlocks = true;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  rightPosition = 0;
 | 
					  rightPosition = 0;
 | 
				
			||||||
  transition = '2s';
 | 
					  transition = '2s';
 | 
				
			||||||
@ -50,17 +52,18 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
  ) { }
 | 
					  ) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit() {
 | 
					  ngOnInit() {
 | 
				
			||||||
 | 
					    this.mempoolEmptyBlocks.forEach((b) => {
 | 
				
			||||||
 | 
					      this.mempoolEmptyBlockStyles.push(this.getStyleForMempoolEmptyBlock(b.index));
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    this.reduceMempoolBlocksToFitScreen(this.mempoolEmptyBlocks);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.mempoolBlocks.map(() => {
 | 
					    this.mempoolBlocks.map(() => {
 | 
				
			||||||
      this.updateMempoolBlockStyles();
 | 
					      this.updateMempoolBlockStyles();
 | 
				
			||||||
      this.calculateTransactionPosition();
 | 
					      this.calculateTransactionPosition();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    this.reduceMempoolBlocksToFitScreen(this.mempoolBlocks);
 | 
					    this.reduceMempoolBlocksToFitScreen(this.mempoolBlocks);
 | 
				
			||||||
    this.stateService.isTabHidden$.subscribe((tabHidden) => this.tabHidden = tabHidden);
 | 
					    this.stateService.isTabHidden$.subscribe((tabHidden) => this.tabHidden = tabHidden);
 | 
				
			||||||
    
 | 
					    this.loadingBlocks$ = this.stateService.isLoadingWebSocket$;
 | 
				
			||||||
    this.isLoadingWebsocketSubscription = this.stateService.isLoadingWebSocket$.subscribe((loading) => {
 | 
					 | 
				
			||||||
      this.loadingMempoolBlocks = loading;
 | 
					 | 
				
			||||||
      this.cd.markForCheck();
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.mempoolBlocks$ = merge(
 | 
					    this.mempoolBlocks$ = merge(
 | 
				
			||||||
      of(true),
 | 
					      of(true),
 | 
				
			||||||
@ -111,8 +114,7 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
            timeAvgMins += Math.abs(timeAvgDiff);
 | 
					            timeAvgMins += Math.abs(timeAvgDiff);
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
          this.loadingMempoolBlocks = false;
 | 
					 | 
				
			||||||
          return timeAvgMins * 60 * 1000;
 | 
					          return timeAvgMins * 60 * 1000;
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
@ -170,7 +172,6 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
    this.markBlocksSubscription.unsubscribe();
 | 
					    this.markBlocksSubscription.unsubscribe();
 | 
				
			||||||
    this.blockSubscription.unsubscribe();
 | 
					    this.blockSubscription.unsubscribe();
 | 
				
			||||||
    this.networkSubscription.unsubscribe();
 | 
					    this.networkSubscription.unsubscribe();
 | 
				
			||||||
    this.isLoadingWebsocketSubscription.unsubscribe();
 | 
					 | 
				
			||||||
    clearTimeout(this.resetTransitionTimeout);
 | 
					    clearTimeout(this.resetTransitionTimeout);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -237,6 +238,13 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getStyleForMempoolEmptyBlock(index: number) {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      'right': 40 + index * 155 + 'px',
 | 
				
			||||||
 | 
					      'background': '#554b45',
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  calculateTransactionPosition() {
 | 
					  calculateTransactionPosition() {
 | 
				
			||||||
    if ((!this.txFeePerVSize && (this.markIndex === undefined || this.markIndex === -1)) || !this.mempoolBlocks) {
 | 
					    if ((!this.txFeePerVSize && (this.markIndex === undefined || this.markIndex === -1)) || !this.mempoolBlocks) {
 | 
				
			||||||
      this.arrowVisible = false;
 | 
					      this.arrowVisible = false;
 | 
				
			||||||
@ -289,7 +297,7 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
        blockSize: 0,
 | 
					        blockSize: 0,
 | 
				
			||||||
        blockVSize: 0,
 | 
					        blockVSize: 0,
 | 
				
			||||||
        feeRange: [],
 | 
					        feeRange: [],
 | 
				
			||||||
        index: 0,
 | 
					        index: i,
 | 
				
			||||||
        medianFee: 0,
 | 
					        medianFee: 0,
 | 
				
			||||||
        nTx: 0,
 | 
					        nTx: 0,
 | 
				
			||||||
        totalFees: 0
 | 
					        totalFees: 0
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user