parent
							
								
									800625d80e
								
							
						
					
					
						commit
						66630743f6
					
				@ -37,6 +37,7 @@ import { IncomingTransactionsGraphComponent } from './components/incoming-transa
 | 
			
		||||
import { TimeSpanComponent } from './components/time-span/time-span.component';
 | 
			
		||||
import { SeoService } from './services/seo.service';
 | 
			
		||||
import { MempoolGraphComponent } from './components/mempool-graph/mempool-graph.component';
 | 
			
		||||
import { LbtcPegsGraphComponent } from './components/lbtc-pegs-graph/lbtc-pegs-graph.component';
 | 
			
		||||
import { AssetComponent } from './components/asset/asset.component';
 | 
			
		||||
import { AssetsComponent } from './assets/assets.component';
 | 
			
		||||
import { StatusViewComponent } from './components/status-view/status-view.component';
 | 
			
		||||
@ -84,6 +85,7 @@ import { SponsorComponent } from './components/sponsor/sponsor.component';
 | 
			
		||||
    FeeDistributionGraphComponent,
 | 
			
		||||
    IncomingTransactionsGraphComponent,
 | 
			
		||||
    MempoolGraphComponent,
 | 
			
		||||
    LbtcPegsGraphComponent,
 | 
			
		||||
    AssetComponent,
 | 
			
		||||
    AssetsComponent,
 | 
			
		||||
    MinerComponent,
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1 @@
 | 
			
		||||
<div class="echarts" echarts [initOpts]="pegsChartInitOption" [options]="pegsChartOptions"></div>
 | 
			
		||||
@ -0,0 +1,137 @@
 | 
			
		||||
import { Component, OnInit, Inject, LOCALE_ID, ChangeDetectionStrategy, Output, EventEmitter, Input, OnChanges } from '@angular/core';
 | 
			
		||||
import { formatDate, formatNumber } from '@angular/common';
 | 
			
		||||
import { EChartsOption } from 'echarts';
 | 
			
		||||
import { ApiService } from 'src/app/services/api.service';
 | 
			
		||||
import { map } from 'rxjs/operators';
 | 
			
		||||
import { Observable } from 'rxjs';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-lbtc-pegs-graph',
 | 
			
		||||
  templateUrl: './lbtc-pegs-graph.component.html',
 | 
			
		||||
  changeDetection: ChangeDetectionStrategy.OnPush,
 | 
			
		||||
})
 | 
			
		||||
export class LbtcPegsGraphComponent implements OnChanges {
 | 
			
		||||
  @Input() data: any;
 | 
			
		||||
  pegsChartOptions: EChartsOption;
 | 
			
		||||
 | 
			
		||||
  height: number | string = '200';
 | 
			
		||||
  right: number | string = '10';
 | 
			
		||||
  top: number | string = '20';
 | 
			
		||||
  left: number | string = '50';
 | 
			
		||||
  template: ('widget' | 'advanced') = 'widget';
 | 
			
		||||
 | 
			
		||||
  pegsChartOption: EChartsOption = {};
 | 
			
		||||
  pegsChartInitOption = {
 | 
			
		||||
    renderer: 'svg'
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    @Inject(LOCALE_ID) private locale: string,
 | 
			
		||||
  ) { }
 | 
			
		||||
 | 
			
		||||
  ngOnChanges() {
 | 
			
		||||
    this.pegsChartOptions = this.createChartOptions(this.data.series, this.data.labels);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  createChartOptions(series: number[], labels: string[]): EChartsOption {
 | 
			
		||||
    return {
 | 
			
		||||
      grid: {
 | 
			
		||||
        height: this.height,
 | 
			
		||||
        right: this.right,
 | 
			
		||||
        top: this.top,
 | 
			
		||||
        left: this.left,
 | 
			
		||||
      },
 | 
			
		||||
      animation: false,
 | 
			
		||||
      dataZoom: [{
 | 
			
		||||
        type: 'inside',
 | 
			
		||||
        realtime: true,
 | 
			
		||||
        zoomOnMouseWheel: (this.template === 'advanced') ? true : false,
 | 
			
		||||
        maxSpan: 100,
 | 
			
		||||
        minSpan: 10,
 | 
			
		||||
      }, {
 | 
			
		||||
        show: (this.template === 'advanced') ? true : false,
 | 
			
		||||
        type: 'slider',
 | 
			
		||||
        brushSelect: false,
 | 
			
		||||
        realtime: true,
 | 
			
		||||
        selectedDataBackground: {
 | 
			
		||||
          lineStyle: {
 | 
			
		||||
            color: '#fff',
 | 
			
		||||
            opacity: 0.45,
 | 
			
		||||
          },
 | 
			
		||||
          areaStyle: {
 | 
			
		||||
            opacity: 0,
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }],
 | 
			
		||||
      tooltip: {
 | 
			
		||||
        trigger: 'axis',
 | 
			
		||||
        position: (pos, params, el, elRect, size) => {
 | 
			
		||||
          const obj = { top: -20 };
 | 
			
		||||
          obj[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 80;
 | 
			
		||||
          return obj;
 | 
			
		||||
        },
 | 
			
		||||
        extraCssText: `width: ${(this.template === 'widget') ? '125px' : '135px'};
 | 
			
		||||
                      background: transparent;
 | 
			
		||||
                      border: none;
 | 
			
		||||
                      box-shadow: none;`,
 | 
			
		||||
        axisPointer: {
 | 
			
		||||
          type: 'line',
 | 
			
		||||
        },
 | 
			
		||||
        formatter: (params: any) => {
 | 
			
		||||
          const colorSpan = (color: string) => `<span class="indicator" style="background-color: #116761;"></span>`;
 | 
			
		||||
          let itemFormatted = '<div class="title">' + params[0].axisValue + '</div>';
 | 
			
		||||
          params.map((item: any, index: number) => {
 | 
			
		||||
            if (index < 26) {
 | 
			
		||||
              itemFormatted += `<div class="item">
 | 
			
		||||
                <div class="indicator-container">${colorSpan(item.color)}</div>
 | 
			
		||||
                <div class="grow"></div>
 | 
			
		||||
                <div class="value">${formatNumber(item.value, this.locale, '1.2-2')} <span class="symbol">L-BTC</span></div>
 | 
			
		||||
              </div>`;
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
          return `<div class="tx-wrapper-tooltip-chart ${(this.template === 'advanced') ? 'tx-wrapper-tooltip-chart-advanced' : ''}">${itemFormatted}</div>`;
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      xAxis: {
 | 
			
		||||
        type: 'category',
 | 
			
		||||
        axisLabel: {
 | 
			
		||||
          align: 'center',
 | 
			
		||||
          fontSize: 11,
 | 
			
		||||
          lineHeight: 12
 | 
			
		||||
        },
 | 
			
		||||
        data: labels.map((value: any) => `${formatDate(value, 'MMM\ny', this.locale)}`),
 | 
			
		||||
      },
 | 
			
		||||
      yAxis: {
 | 
			
		||||
        type: 'value',
 | 
			
		||||
        axisLabel: {
 | 
			
		||||
          fontSize: 11,
 | 
			
		||||
        },
 | 
			
		||||
        splitLine: {
 | 
			
		||||
          lineStyle: {
 | 
			
		||||
            type: 'dotted',
 | 
			
		||||
            color: '#ffffff66',
 | 
			
		||||
            opacity: 0.25,
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      series: [
 | 
			
		||||
        {
 | 
			
		||||
          data: series,
 | 
			
		||||
          type: 'line',
 | 
			
		||||
          stack: 'total',
 | 
			
		||||
          smooth: false,
 | 
			
		||||
          showSymbol: false,
 | 
			
		||||
          areaStyle: {
 | 
			
		||||
            opacity: 0.2,
 | 
			
		||||
            color: '#116761',
 | 
			
		||||
          },
 | 
			
		||||
          lineStyle: {
 | 
			
		||||
            width: 3,
 | 
			
		||||
            color: '#116761',
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,7 @@
 | 
			
		||||
      <div class="col">
 | 
			
		||||
        <div class="card">
 | 
			
		||||
          <div class="card-body">
 | 
			
		||||
            <ng-container *ngTemplateOutlet="mempoolTable; context: { $implicit: mempoolInfoData }"></ng-container>
 | 
			
		||||
            <ng-container *ngTemplateOutlet="stateService.network === 'liquid' ? lbtcPegs : mempoolTable; context: { $implicit: mempoolInfoData }"></ng-container>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
@ -44,16 +44,21 @@
 | 
			
		||||
        <div class="card graph-card">
 | 
			
		||||
          <div class="card-body pl-0">
 | 
			
		||||
            <div style="padding-left: 1.25rem;">
 | 
			
		||||
              <ng-container *ngTemplateOutlet="mempoolTable; context: { $implicit: mempoolInfoData }"></ng-container>
 | 
			
		||||
              <ng-container *ngTemplateOutlet="stateService.network === 'liquid' ? lbtcPegs : mempoolTable; context: { $implicit: mempoolInfoData }"></ng-container>
 | 
			
		||||
              <hr>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="mempool-graph" *ngIf="(mempoolStats$ | async) as mempoolStats; else loadingSpinner">
 | 
			
		||||
              <app-mempool-graph
 | 
			
		||||
                [template]="'widget'"
 | 
			
		||||
                [limitFee]="150"
 | 
			
		||||
                [data]="mempoolStats.mempool"
 | 
			
		||||
              ></app-mempool-graph>
 | 
			
		||||
            <div class="mempool-graph" *ngIf="stateService.network === 'liquid'; else mempoolGraph">
 | 
			
		||||
              <app-lbtc-pegs-graph [data]="liquidPegsMonth$ | async"></app-lbtc-pegs-graph>
 | 
			
		||||
            </div>
 | 
			
		||||
            <ng-template #mempoolGraph>
 | 
			
		||||
              <div class="mempool-graph" *ngIf="(mempoolStats$ | async) as mempoolStats; else loadingSpinner">
 | 
			
		||||
                <app-mempool-graph
 | 
			
		||||
                  [template]="'widget'"
 | 
			
		||||
                  [limitFee]="150"
 | 
			
		||||
                  [data]="mempoolStats.mempool"
 | 
			
		||||
                ></app-mempool-graph>
 | 
			
		||||
              </div>
 | 
			
		||||
            </ng-template>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
@ -192,6 +197,17 @@
 | 
			
		||||
  </div>
 | 
			
		||||
</ng-template>
 | 
			
		||||
 | 
			
		||||
<ng-template #lbtcPegs let-mempoolInfoData>
 | 
			
		||||
  <div class="mempool-info-data">
 | 
			
		||||
    <div class="item">
 | 
			
		||||
      <h5 class="card-title" i18n="dashboard.lbtc-pegs-in-circulation">L-BTC in circulation</h5>
 | 
			
		||||
      <ng-container *ngIf="(liquidPegsMonth$ | async) as liquidPegsMonth; else loadingTransactions">
 | 
			
		||||
        <p class="card-text">{{ liquidPegsMonth.series.slice(-1)[0] | number: '1.2-2' }} <span>L-BTC</span></p>
 | 
			
		||||
      </ng-container>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</ng-template>
 | 
			
		||||
 | 
			
		||||
<ng-template #txPerSecond let-mempoolInfoData>
 | 
			
		||||
  <h5 class="card-title" i18n="dashboard.incoming-transactions">Incoming transactions</h5>
 | 
			
		||||
  <ng-template [ngIf]="(isLoadingWebSocket$ | async) === false && mempoolInfoData.value" [ngIfElse]="loadingTransactions">
 | 
			
		||||
@ -207,7 +223,6 @@
 | 
			
		||||
  </ng-template>
 | 
			
		||||
</ng-template>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<ng-template #difficultyEpoch>
 | 
			
		||||
  <div class="main-title" i18n="dashboard.difficulty-adjustment">Difficulty Adjustment</div>
 | 
			
		||||
  <div class="card-wrapper">
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, Inject, LOCALE_ID, OnInit } from '@
 | 
			
		||||
import { combineLatest, merge, Observable, of, timer } from 'rxjs';
 | 
			
		||||
import { filter, map, scan, share, switchMap, tap } from 'rxjs/operators';
 | 
			
		||||
import { Block } from '../interfaces/electrs.interface';
 | 
			
		||||
import { OptimizedMempoolStats } from '../interfaces/node-api.interface';
 | 
			
		||||
import { LiquidPegs, OptimizedMempoolStats } from '../interfaces/node-api.interface';
 | 
			
		||||
import { MempoolInfo, TransactionStripped } from '../interfaces/websocket.interface';
 | 
			
		||||
import { ApiService } from '../services/api.service';
 | 
			
		||||
import { StateService } from '../services/state.service';
 | 
			
		||||
@ -63,6 +63,7 @@ export class DashboardComponent implements OnInit {
 | 
			
		||||
  mempoolStats$: Observable<MempoolStatsData>;
 | 
			
		||||
  transactionsWeightPerSecondOptions: any;
 | 
			
		||||
  isLoadingWebSocket$: Observable<boolean>;
 | 
			
		||||
  liquidPegsMonth$: Observable<any>;
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    @Inject(LOCALE_ID) private locale: string,
 | 
			
		||||
@ -261,6 +262,22 @@ export class DashboardComponent implements OnInit {
 | 
			
		||||
        }),
 | 
			
		||||
        share(),
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
    if (this.stateService.network === 'liquid') {
 | 
			
		||||
      this.liquidPegsMonth$ = this.apiService.listLiquidPegsMonth$()
 | 
			
		||||
        .pipe(
 | 
			
		||||
          map((pegs) => {
 | 
			
		||||
            const labels = pegs.map(stats => stats.date);
 | 
			
		||||
            const series = pegs.map(stats => parseFloat(stats.amount) / 100000000);
 | 
			
		||||
            series.reduce((prev, curr, i) => series[i] = prev + curr, 0);
 | 
			
		||||
            return {
 | 
			
		||||
              series,
 | 
			
		||||
              labels
 | 
			
		||||
            };
 | 
			
		||||
          }),
 | 
			
		||||
          share(),
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleNewMempoolData(mempoolStats: OptimizedMempoolStats[]) {
 | 
			
		||||
 | 
			
		||||
@ -47,3 +47,8 @@ export interface AddressInformation {
 | 
			
		||||
  confidential_key?: string;       //  (string) Elements only
 | 
			
		||||
  unconfidential?: string;         //  (string) Elements only
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface LiquidPegs {
 | 
			
		||||
  amount: string;
 | 
			
		||||
  date: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import { HttpClient, HttpParams } from '@angular/common/http';
 | 
			
		||||
import { CpfpInfo, OptimizedMempoolStats, DifficultyAdjustment, AddressInformation } from '../interfaces/node-api.interface';
 | 
			
		||||
import { CpfpInfo, OptimizedMempoolStats, DifficultyAdjustment, AddressInformation, LiquidPegs } from '../interfaces/node-api.interface';
 | 
			
		||||
import { Observable } from 'rxjs';
 | 
			
		||||
import { StateService } from './state.service';
 | 
			
		||||
import { WebsocketResponse } from '../interfaces/websocket.interface';
 | 
			
		||||
@ -100,4 +100,8 @@ export class ApiService {
 | 
			
		||||
  validateAddress$(address: string): Observable<AddressInformation> {
 | 
			
		||||
    return this.httpClient.get<AddressInformation>(this.apiBaseUrl + this.apiBasePath + '/api/v1/validate-address/' + address);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  listLiquidPegsMonth$(): Observable<LiquidPegs[]> {
 | 
			
		||||
    return this.httpClient.get<LiquidPegs[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/pegs/month');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user