Create basic layout for mining dashboard page - Show miner in blocks

This commit is contained in:
nymkappa 2022-02-16 17:32:12 +09:00
parent 4a86699199
commit 923a2ce7f6
No known key found for this signature in database
GPG Key ID: E155910B16E8BD04
12 changed files with 161 additions and 26 deletions

View File

@ -27,6 +27,7 @@ import { AssetGroupComponent } from './components/assets/asset-group/asset-group
import { AssetsFeaturedComponent } from './components/assets/assets-featured/assets-featured.component'; import { AssetsFeaturedComponent } from './components/assets/assets-featured/assets-featured.component';
import { AssetsComponent } from './components/assets/assets.component'; import { AssetsComponent } from './components/assets/assets.component';
import { PoolComponent } from './components/pool/pool.component'; import { PoolComponent } from './components/pool/pool.component';
import { MiningDashboardComponent } from './components/mining-dashboard/mining-dashboard.component';
import { DifficultyChartComponent } from './components/difficulty-chart/difficulty-chart.component'; import { DifficultyChartComponent } from './components/difficulty-chart/difficulty-chart.component';
let routes: Routes = [ let routes: Routes = [
@ -72,6 +73,10 @@ let routes: Routes = [
path: 'mining/pools', path: 'mining/pools',
component: PoolRankingComponent, component: PoolRankingComponent,
}, },
{
path: 'mining/dashboard',
component: MiningDashboardComponent,
},
{ {
path: 'mining/pool/:poolId', path: 'mining/pool/:poolId',
component: PoolComponent, component: PoolComponent,
@ -168,6 +173,10 @@ let routes: Routes = [
path: 'mining/pools', path: 'mining/pools',
component: PoolRankingComponent, component: PoolRankingComponent,
}, },
{
path: 'mining/dashboard',
component: MiningDashboardComponent,
},
{ {
path: 'mining/pool/:poolId', path: 'mining/pool/:poolId',
component: PoolComponent, component: PoolComponent,
@ -258,6 +267,10 @@ let routes: Routes = [
path: 'mining/pools', path: 'mining/pools',
component: PoolRankingComponent, component: PoolRankingComponent,
}, },
{
path: 'mining/dashboard',
component: MiningDashboardComponent,
},
{ {
path: 'mining/pool/:poolId', path: 'mining/pool/:poolId',
component: PoolComponent, component: PoolComponent,

View File

@ -68,6 +68,7 @@ import { PushTransactionComponent } from './components/push-transaction/push-tra
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { AssetsFeaturedComponent } from './components/assets/assets-featured/assets-featured.component'; import { AssetsFeaturedComponent } from './components/assets/assets-featured/assets-featured.component';
import { AssetGroupComponent } from './components/assets/asset-group/asset-group.component'; import { AssetGroupComponent } from './components/assets/asset-group/asset-group.component';
import { MiningDashboardComponent } from './components/mining-dashboard/mining-dashboard.component';
import { DifficultyChartComponent } from './components/difficulty-chart/difficulty-chart.component'; import { DifficultyChartComponent } from './components/difficulty-chart/difficulty-chart.component';
@NgModule({ @NgModule({
@ -119,6 +120,7 @@ import { DifficultyChartComponent } from './components/difficulty-chart/difficul
AssetsNavComponent, AssetsNavComponent,
AssetsFeaturedComponent, AssetsFeaturedComponent,
AssetGroupComponent, AssetGroupComponent,
MiningDashboardComponent,
DifficultyChartComponent, DifficultyChartComponent,
], ],
imports: [ imports: [

View File

@ -21,9 +21,13 @@
</div> </div>
<div class="time-difference"><app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since></div> <div class="time-difference"><app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since></div>
</div> </div>
<div class="" *ngIf="miningInfo === true">
<a class="badge badge-primary" [routerLink]="[('/mining/pool/' + block.extras.pool.id) | relativeUrl]">
{{ block.extras.pool.name}}</a>
</div>
</div> </div>
</div> </div>
<div [hidden]="!arrowVisible" id="arrow-up" [style.transition]="transition" [ngStyle]="{'left': arrowLeftPx + 'px' }"></div> <div [hidden]="!arrowVisible" id="arrow-up" [style.transition]="transition" [ngStyle]="{'left': arrowLeftPx + 'px' }"></div>
</div> </div>
<ng-template #loadingBlocksTemplate> <ng-template #loadingBlocksTemplate>

View File

@ -124,3 +124,9 @@
50% {opacity: 1.0;} 50% {opacity: 1.0;}
100% {opacity: 0.7;} 100% {opacity: 0.7;}
} }
.badge {
position: relative;
top: 15px;
z-index: 101;
}

View File

@ -1,4 +1,4 @@
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core'; import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef, Input } from '@angular/core';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
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';
@ -12,6 +12,7 @@ import { BlockExtended } from 'src/app/interfaces/node-api.interface';
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class BlockchainBlocksComponent implements OnInit, OnDestroy { export class BlockchainBlocksComponent implements OnInit, OnDestroy {
@Input() miningInfo: boolean = false;
specialBlocks = specialBlocks; specialBlocks = specialBlocks;
network = ''; network = '';
blocks: BlockExtended[] = []; blocks: BlockExtended[] = [];

View File

@ -32,7 +32,7 @@
<a class="nav-link" [routerLink]="['/' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'tachometer-alt']" [fixedWidth]="true" i18n-title="master-page.dashboard" title="Dashboard"></fa-icon></a> <a class="nav-link" [routerLink]="['/' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'tachometer-alt']" [fixedWidth]="true" i18n-title="master-page.dashboard" title="Dashboard"></fa-icon></a>
</li> </li>
<li class="nav-item" routerLinkActive="active" id="btn-pools" *ngIf="stateService.env.MINING_DASHBOARD"> <li class="nav-item" routerLinkActive="active" id="btn-pools" *ngIf="stateService.env.MINING_DASHBOARD">
<a class="nav-link" [routerLink]="['/mining/pools' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'hammer']" [fixedWidth]="true" i18n-title="master-page.mining-pools" title="Mining Pools"></fa-icon></a> <a class="nav-link" [routerLink]="['/mining/dashboard' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'hammer']" [fixedWidth]="true" i18n-title="master-page.mining-dashboard" title="Mining Dashboard"></fa-icon></a>
</li> </li>
<li class="nav-item" routerLinkActive="active" id="btn-blocks" *ngIf="!stateService.env.MINING_DASHBOARD"> <li class="nav-item" routerLinkActive="active" id="btn-blocks" *ngIf="!stateService.env.MINING_DASHBOARD">
<a class="nav-link" [routerLink]="['/blocks' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'cubes']" [fixedWidth]="true" i18n-title="master-page.blocks" title="Blocks"></fa-icon></a> <a class="nav-link" [routerLink]="['/blocks' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'cubes']" [fixedWidth]="true" i18n-title="master-page.blocks" title="Blocks"></fa-icon></a>

View File

@ -0,0 +1,36 @@
<div class="container-xl dashboard-container">
<!-- lastest blocks -->
<div class="text-center" class="blockchain-wrapper fade-border">
<div class="position-container">
<span>
<app-blockchain-blocks [miningInfo]="true"></app-blockchain-blocks>
</span>
</div>
</div>
<div class="row row-cols-1 row-cols-md-2 mt-3">
<!-- pool distribution -->
<div class="col">
<div class="card">
<div class="card-body">
<app-pool-ranking [widget]=true></app-pool-ranking>
<div class=""><a href="" [routerLink]="['/mining/pools' | relativeUrl]" i18n="dashboard.view-more">View more
&raquo;</a></div>
</div>
</div>
</div>
<!-- difficulty -->
<div class="col">
<div class="card">
<div class="card-body">
<app-difficulty [showProgress]=false [showHalving]=true></app-difficulty>
Imagine more stuff down there
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,50 @@
.dashboard-container {
padding-bottom: 60px;
text-align: center;
margin-top: 0.5rem;
@media (min-width: 992px) {
padding-bottom: 0px;
}
.col {
margin-bottom: 1.5rem;
}
}
.card {
background-color: #1d1f31;
height: 100%;
}
.card-wrapper {
.card {
height: auto !important;
}
.card-body {
display: flex;
flex: inherit;
text-align: center;
flex-direction: column;
justify-content: space-around;
padding: 22px 20px;
}
}
.blockchain-wrapper {
position: relative;
overflow: hidden;
height: 250px;
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+/Edge */
user-select: none; /* Standard */
}
.position-container {
position: absolute;
top: 75px;
}
.fade-border {
-webkit-mask-image: linear-gradient(90deg, #000 70%, transparent);
}

View File

@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-mining-dashboard',
templateUrl: './mining-dashboard.component.html',
styleUrls: ['./mining-dashboard.component.scss']
})
export class MiningDashboardComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}

View File

@ -1,49 +1,48 @@
<div class="container-xl"> <div [class]="widget === false ? 'container-xl' : ''">
<!-- <app-difficulty [showProgress]=false [showHalving]=true></app-difficulty> -->
<div class="hashrate-pie" echarts [initOpts]="chartInitOptions" [options]="chartOptions" (chartInit)="onChartInit($event)"></div> <div class="hashrate-pie" echarts [initOpts]="chartInitOptions" [options]="chartOptions" (chartInit)="onChartInit($event)"></div>
<div class="text-center loadingGraphs" *ngIf="isLoading"> <div class="text-center loadingGraphs" *ngIf="isLoading">
<div class="spinner-border text-light"></div> <div class="spinner-border text-light"></div>
</div> </div>
<div class="card-header mb-0 mb-lg-4"> <div class="card-header mb-0 mb-lg-4" [style]="widget === true ? 'display:none' : ''">
<form [formGroup]="radioGroupForm" class="formRadioGroup" *ngIf="(miningStatsObservable$ | async) as miningStats"> <form [formGroup]="radioGroupForm" class="formRadioGroup" *ngIf="(miningStatsObservable$ | async) as miningStats">
<div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan"> <div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan">
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="miningStats.availableTimespanDay >= 1"> <label ngbButtonLabel class="btn-primary btn-sm" *ngIf="miningStats.availableTimespanDay >= 1">
<input ngbButton type="radio" [value]="'24h'" [routerLink]="['/mining/pools' | relativeUrl]" fragment="24h"> 24h <input ngbButton type="radio" [value]="'24h'" fragment="24h"> 24h
</label> </label>
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="miningStats.availableTimespanDay >= 3"> <label ngbButtonLabel class="btn-primary btn-sm" *ngIf="miningStats.availableTimespanDay >= 3">
<input ngbButton type="radio" [value]="'3d'" [routerLink]="['/mining/pools' | relativeUrl]" fragment="3d"> 3D <input ngbButton type="radio" [value]="'3d'" fragment="3d"> 3D
</label> </label>
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="miningStats.availableTimespanDay >= 7"> <label ngbButtonLabel class="btn-primary btn-sm" *ngIf="miningStats.availableTimespanDay >= 7">
<input ngbButton type="radio" [value]="'1w'" [routerLink]="['/mining/pools' | relativeUrl]" fragment="1w"> 1W <input ngbButton type="radio" [value]="'1w'" fragment="1w"> 1W
</label> </label>
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="miningStats.availableTimespanDay >= 30"> <label ngbButtonLabel class="btn-primary btn-sm" *ngIf="miningStats.availableTimespanDay >= 30">
<input ngbButton type="radio" [value]="'1m'" [routerLink]="['/mining/pools' | relativeUrl]" fragment="1m"> 1M <input ngbButton type="radio" [value]="'1m'" fragment="1m"> 1M
</label> </label>
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="miningStats.availableTimespanDay >= 90"> <label ngbButtonLabel class="btn-primary btn-sm" *ngIf="miningStats.availableTimespanDay >= 90">
<input ngbButton type="radio" [value]="'3m'" [routerLink]="['/mining/pools' | relativeUrl]" fragment="3m"> 3M <input ngbButton type="radio" [value]="'3m'" fragment="3m"> 3M
</label> </label>
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="miningStats.availableTimespanDay >= 180"> <label ngbButtonLabel class="btn-primary btn-sm" *ngIf="miningStats.availableTimespanDay >= 180">
<input ngbButton type="radio" [value]="'6m'" [routerLink]="['/mining/pools' | relativeUrl]" fragment="6m"> 6M <input ngbButton type="radio" [value]="'6m'" fragment="6m"> 6M
</label> </label>
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="miningStats.availableTimespanDay >= 365"> <label ngbButtonLabel class="btn-primary btn-sm" *ngIf="miningStats.availableTimespanDay >= 365">
<input ngbButton type="radio" [value]="'1y'" [routerLink]="['/mining/pools' | relativeUrl]" fragment="1y"> 1Y <input ngbButton type="radio" [value]="'1y'" fragment="1y"> 1Y
</label> </label>
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="miningStats.availableTimespanDay >= 730"> <label ngbButtonLabel class="btn-primary btn-sm" *ngIf="miningStats.availableTimespanDay >= 730">
<input ngbButton type="radio" [value]="'2y'" [routerLink]="['/mining/pools' | relativeUrl]" fragment="2y"> 2Y <input ngbButton type="radio" [value]="'2y'" fragment="2y"> 2Y
</label> </label>
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="miningStats.availableTimespanDay >= 1095"> <label ngbButtonLabel class="btn-primary btn-sm" *ngIf="miningStats.availableTimespanDay >= 1095">
<input ngbButton type="radio" [value]="'3y'" [routerLink]="['/mining/pools' | relativeUrl]" fragment="3y"> 3Y <input ngbButton type="radio" [value]="'3y'" fragment="3y"> 3Y
</label> </label>
<label ngbButtonLabel class="btn-primary btn-sm"> <label ngbButtonLabel class="btn-primary btn-sm">
<input ngbButton type="radio" [value]="'all'" [routerLink]="['/mining/pools' | relativeUrl]" fragment="all"> ALL <input ngbButton type="radio" [value]="'all'" fragment="all"> ALL
</label> </label>
</div> </div>
</form> </form>
</div> </div>
<table class="table table-borderless text-center pools-table"> <table *ngIf="widget === false" class="table table-borderless text-center pools-table">
<thead> <thead>
<tr> <tr>
<th class="d-none d-md-block" i18n="mining.rank">Rank</th> <th class="d-none d-md-block" i18n="mining.rank">Rank</th>
@ -64,8 +63,8 @@
<td class="d-none d-md-block">{{ pool.emptyBlocks }} ({{ pool.emptyBlockRatio }}%)</td> <td class="d-none d-md-block">{{ pool.emptyBlocks }} ({{ pool.emptyBlockRatio }}%)</td>
</tr> </tr>
<tr style="border-top: 1px solid #555"> <tr style="border-top: 1px solid #555">
<td class="d-none d-md-block">-</td> <td class="d-none d-md-block"></td>
<td class="text-right"><img width="25" height="25" src="./resources/mining-pools/default.svg"></td> <td class="text-right"></td>
<td class="" i18n="mining.all-miners"><b>All miners</b></td> <td class="" i18n="mining.all-miners"><b>All miners</b></td>
<td class="" *ngIf="this.poolsWindowPreference === '24h'"><b>{{ miningStats.lastEstimatedHashrate}} {{ miningStats.miningUnits.hashrateUnit }}</b></td> <td class="" *ngIf="this.poolsWindowPreference === '24h'"><b>{{ miningStats.lastEstimatedHashrate}} {{ miningStats.miningUnits.hashrateUnit }}</b></td>
<td class=""><b>{{ miningStats.blockCount }}</b></td> <td class=""><b>{{ miningStats.blockCount }}</b></td>

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms'; import { FormBuilder, FormGroup } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { EChartsOption, PieSeriesOption } from 'echarts'; import { EChartsOption, PieSeriesOption } from 'echarts';
@ -25,6 +25,8 @@ import { chartColors } from 'src/app/app.constants';
`], `],
}) })
export class PoolRankingComponent implements OnInit { export class PoolRankingComponent implements OnInit {
@Input() widget: boolean = false;
poolsWindowPreference: string; poolsWindowPreference: string;
radioGroupForm: FormGroup; radioGroupForm: FormGroup;
@ -46,12 +48,17 @@ export class PoolRankingComponent implements OnInit {
private router: Router, private router: Router,
) { ) {
this.seoService.setTitle($localize`:@@mining.mining-pools:Mining Pools`); this.seoService.setTitle($localize`:@@mining.mining-pools:Mining Pools`);
this.poolsWindowPreference = this.storageService.getValue('poolsWindowPreference') ? this.storageService.getValue('poolsWindowPreference') : '1w';
this.radioGroupForm = this.formBuilder.group({ dateSpan: this.poolsWindowPreference });
this.radioGroupForm.controls.dateSpan.setValue(this.poolsWindowPreference);
} }
ngOnInit(): void { ngOnInit(): void {
if (this.widget) {
this.poolsWindowPreference = '1w';
} else {
this.poolsWindowPreference = this.storageService.getValue('poolsWindowPreference') ? this.storageService.getValue('poolsWindowPreference') : '1w';
}
this.radioGroupForm = this.formBuilder.group({ dateSpan: this.poolsWindowPreference });
this.radioGroupForm.controls.dateSpan.setValue(this.poolsWindowPreference);
// When... // When...
this.miningStatsObservable$ = combineLatest([ this.miningStatsObservable$ = combineLatest([
// ...a new block is mined // ...a new block is mined
@ -65,7 +72,9 @@ export class PoolRankingComponent implements OnInit {
.pipe( .pipe(
startWith(this.poolsWindowPreference), // (trigger when the page loads) startWith(this.poolsWindowPreference), // (trigger when the page loads)
tap((value) => { tap((value) => {
this.storageService.setValue('poolsWindowPreference', value); if (!this.widget) {
this.storageService.setValue('poolsWindowPreference', value);
}
this.poolsWindowPreference = value; this.poolsWindowPreference = value;
}) })
) )
@ -147,7 +156,7 @@ export class PoolRankingComponent implements OnInit {
this.chartOptions = { this.chartOptions = {
title: { title: {
text: $localize`:@@mining.pool-chart-title:${network}:NETWORK: mining pools share`, text: $localize`:@@mining.pool-chart-title:${network}:NETWORK: mining pools share`,
subtext: $localize`:@@mining.pool-chart-sub-title:Estimated from the # of blocks mined`, subtext: $localize`:@@mining.pool-chart-sub-title:Estimated from the # of blocks mined (${this.poolsWindowPreference})`,
left: 'center', left: 'center',
textStyle: { textStyle: {
color: '#FFF', color: '#FFF',