Merge branch 'master' into feature/mempool-blocks-reward
This commit is contained in:
@@ -29,6 +29,8 @@ import { AssetsComponent } from './components/assets/assets.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 { HashrateChartComponent } from './components/hashrate-chart/hashrate-chart.component';
|
||||
import { MiningStartComponent } from './components/mining-start/mining-start.component';
|
||||
|
||||
let routes: Routes = [
|
||||
{
|
||||
@@ -70,16 +72,35 @@ let routes: Routes = [
|
||||
component: LatestBlocksComponent,
|
||||
},
|
||||
{
|
||||
path: 'mining/difficulty',
|
||||
component: DifficultyChartComponent,
|
||||
},
|
||||
{
|
||||
path: 'mining/pools',
|
||||
component: PoolRankingComponent,
|
||||
},
|
||||
{
|
||||
path: 'mining/pool/:poolId',
|
||||
component: PoolComponent,
|
||||
path: 'mining',
|
||||
component: MiningStartComponent,
|
||||
children: [
|
||||
{
|
||||
path: 'difficulty',
|
||||
component: DifficultyChartComponent,
|
||||
},
|
||||
{
|
||||
path: 'hashrate',
|
||||
component: HashrateChartComponent,
|
||||
},
|
||||
{
|
||||
path: 'pools',
|
||||
component: PoolRankingComponent,
|
||||
},
|
||||
{
|
||||
path: 'pool',
|
||||
children: [
|
||||
{
|
||||
path: ':poolId',
|
||||
component: PoolComponent,
|
||||
},
|
||||
{
|
||||
path: ':poolId/hashrate',
|
||||
component: HashrateChartComponent,
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'graphs',
|
||||
@@ -170,16 +191,35 @@ let routes: Routes = [
|
||||
component: LatestBlocksComponent,
|
||||
},
|
||||
{
|
||||
path: 'mining/difficulty',
|
||||
component: DifficultyChartComponent,
|
||||
},
|
||||
{
|
||||
path: 'mining/pools',
|
||||
component: PoolRankingComponent,
|
||||
},
|
||||
{
|
||||
path: 'mining/pool/:poolId',
|
||||
component: PoolComponent,
|
||||
path: 'mining',
|
||||
component: MiningStartComponent,
|
||||
children: [
|
||||
{
|
||||
path: 'difficulty',
|
||||
component: DifficultyChartComponent,
|
||||
},
|
||||
{
|
||||
path: 'hashrate',
|
||||
component: HashrateChartComponent,
|
||||
},
|
||||
{
|
||||
path: 'pools',
|
||||
component: PoolRankingComponent,
|
||||
},
|
||||
{
|
||||
path: 'pool',
|
||||
children: [
|
||||
{
|
||||
path: ':poolId',
|
||||
component: PoolComponent,
|
||||
},
|
||||
{
|
||||
path: ':poolId/hashrate',
|
||||
component: HashrateChartComponent,
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'graphs',
|
||||
@@ -264,16 +304,35 @@ let routes: Routes = [
|
||||
component: LatestBlocksComponent,
|
||||
},
|
||||
{
|
||||
path: 'mining/difficulty',
|
||||
component: DifficultyChartComponent,
|
||||
},
|
||||
{
|
||||
path: 'mining/pools',
|
||||
component: PoolRankingComponent,
|
||||
},
|
||||
{
|
||||
path: 'mining/pool/:poolId',
|
||||
component: PoolComponent,
|
||||
path: 'mining',
|
||||
component: MiningStartComponent,
|
||||
children: [
|
||||
{
|
||||
path: 'difficulty',
|
||||
component: DifficultyChartComponent,
|
||||
},
|
||||
{
|
||||
path: 'hashrate',
|
||||
component: HashrateChartComponent,
|
||||
},
|
||||
{
|
||||
path: 'pools',
|
||||
component: PoolRankingComponent,
|
||||
},
|
||||
{
|
||||
path: 'pool',
|
||||
children: [
|
||||
{
|
||||
path: ':poolId',
|
||||
component: PoolComponent,
|
||||
},
|
||||
{
|
||||
path: ':poolId/hashrate',
|
||||
component: HashrateChartComponent,
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'graphs',
|
||||
|
||||
@@ -71,6 +71,8 @@ import { AssetGroupComponent } from './components/assets/asset-group/asset-group
|
||||
import { AssetCirculationComponent } from './components/asset-circulation/asset-circulation.component';
|
||||
import { MiningDashboardComponent } from './components/mining-dashboard/mining-dashboard.component';
|
||||
import { DifficultyChartComponent } from './components/difficulty-chart/difficulty-chart.component';
|
||||
import { HashrateChartComponent } from './components/hashrate-chart/hashrate-chart.component';
|
||||
import { MiningStartComponent } from './components/mining-start/mining-start.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -124,6 +126,8 @@ import { DifficultyChartComponent } from './components/difficulty-chart/difficul
|
||||
AssetCirculationComponent,
|
||||
MiningDashboardComponent,
|
||||
DifficultyChartComponent,
|
||||
HashrateChartComponent,
|
||||
MiningStartComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule.withServerTransition({ appId: 'serverApp' }),
|
||||
|
||||
@@ -130,3 +130,32 @@ export const formatNumber = (s, precision = null) => {
|
||||
// Utilities for segwitFeeGains
|
||||
const witnessSize = (vin: Vin) => vin.witness.reduce((S, w) => S + (w.length / 2), 0);
|
||||
const scriptSigSize = (vin: Vin) => vin.scriptsig ? vin.scriptsig.length / 2 : 0;
|
||||
|
||||
// Power of ten wrapper
|
||||
export function selectPowerOfTen(val: number) {
|
||||
const powerOfTen = {
|
||||
exa: Math.pow(10, 18),
|
||||
peta: Math.pow(10, 15),
|
||||
terra: Math.pow(10, 12),
|
||||
giga: Math.pow(10, 9),
|
||||
mega: Math.pow(10, 6),
|
||||
kilo: Math.pow(10, 3),
|
||||
};
|
||||
|
||||
let selectedPowerOfTen;
|
||||
if (val < powerOfTen.mega) {
|
||||
selectedPowerOfTen = { divider: 1, unit: '' }; // no scaling
|
||||
} else if (val < powerOfTen.giga) {
|
||||
selectedPowerOfTen = { divider: powerOfTen.mega, unit: 'M' };
|
||||
} else if (val < powerOfTen.terra) {
|
||||
selectedPowerOfTen = { divider: powerOfTen.giga, unit: 'G' };
|
||||
} else if (val < powerOfTen.peta) {
|
||||
selectedPowerOfTen = { divider: powerOfTen.terra, unit: 'T' };
|
||||
} else if (val < powerOfTen.exa) {
|
||||
selectedPowerOfTen = { divider: powerOfTen.peta, unit: 'P' };
|
||||
} else {
|
||||
selectedPowerOfTen = { divider: powerOfTen.exa, unit: 'E' };
|
||||
}
|
||||
|
||||
return selectedPowerOfTen;
|
||||
}
|
||||
@@ -1,10 +1,5 @@
|
||||
<div [class]="widget === false ? 'container-xl' : ''">
|
||||
|
||||
<div *ngIf="difficultyObservable$ | async" class="" echarts [initOpts]="chartInitOptions" [options]="chartOptions"></div>
|
||||
<div class="text-center loadingGraphs" *ngIf="isLoading">
|
||||
<div class="spinner-border text-light"></div>
|
||||
</div>
|
||||
|
||||
<div class="card-header mb-0 mb-lg-4" [style]="widget ? 'display:none' : ''">
|
||||
<form [formGroup]="radioGroupForm" class="formRadioGroup" *ngIf="(difficultyObservable$ | async) as diffChanges">
|
||||
<div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan">
|
||||
@@ -30,6 +25,11 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div *ngIf="difficultyObservable$ | async" class="mb-5" echarts [initOpts]="chartInitOptions" [options]="chartOptions"></div>
|
||||
<div class="text-center loadingGraphs" *ngIf="isLoading">
|
||||
<div class="spinner-border text-light"></div>
|
||||
</div>
|
||||
|
||||
<table class="table table-borderless table-sm text-center" *ngIf="!widget">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@@ -8,3 +8,20 @@
|
||||
text-align: center;
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
|
||||
.formRadioGroup {
|
||||
margin-top: 6px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@media (min-width: 830px) {
|
||||
flex-direction: row;
|
||||
float: right;
|
||||
margin-top: 0px;
|
||||
}
|
||||
.btn-sm {
|
||||
font-size: 9px;
|
||||
@media (min-width: 830px) {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core';
|
||||
import { EChartsOption } from 'echarts';
|
||||
import { EChartsOption, graphic } from 'echarts';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, share, startWith, switchMap, tap } from 'rxjs/operators';
|
||||
import { ApiService } from 'src/app/services/api.service';
|
||||
import { SeoService } from 'src/app/services/seo.service';
|
||||
import { formatNumber } from '@angular/common';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { selectPowerOfTen } from 'src/app/bitcoin.utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-difficulty-chart',
|
||||
@@ -46,13 +47,6 @@ export class DifficultyChartComponent implements OnInit {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
const powerOfTen = {
|
||||
terra: Math.pow(10, 12),
|
||||
giga: Math.pow(10, 9),
|
||||
mega: Math.pow(10, 6),
|
||||
kilo: Math.pow(10, 3),
|
||||
}
|
||||
|
||||
this.difficultyObservable$ = this.radioGroupForm.get('dateSpan').valueChanges
|
||||
.pipe(
|
||||
startWith('1y'),
|
||||
@@ -69,16 +63,9 @@ export class DifficultyChartComponent implements OnInit {
|
||||
) / 3600 / 24;
|
||||
|
||||
const tableData = [];
|
||||
for (let i = 0; i < data.adjustments.length - 1; ++i) {
|
||||
const change = (data.adjustments[i].difficulty / data.adjustments[i + 1].difficulty - 1) * 100;
|
||||
let selectedPowerOfTen = { divider: powerOfTen.terra, unit: 'T' };
|
||||
if (data.adjustments[i].difficulty < powerOfTen.mega) {
|
||||
selectedPowerOfTen = { divider: 1, unit: '' }; // no scaling
|
||||
} else if (data.adjustments[i].difficulty < powerOfTen.giga) {
|
||||
selectedPowerOfTen = { divider: powerOfTen.mega, unit: 'M' };
|
||||
} else if (data.adjustments[i].difficulty < powerOfTen.terra) {
|
||||
selectedPowerOfTen = { divider: powerOfTen.giga, unit: 'G' };
|
||||
}
|
||||
for (let i = data.adjustments.length - 1; i > 0; --i) {
|
||||
const selectedPowerOfTen: any = selectPowerOfTen(data.adjustments[i].difficulty);
|
||||
const change = (data.adjustments[i].difficulty / data.adjustments[i - 1].difficulty - 1) * 100;
|
||||
|
||||
tableData.push(Object.assign(data.adjustments[i], {
|
||||
change: change,
|
||||
@@ -100,6 +87,13 @@ export class DifficultyChartComponent implements OnInit {
|
||||
|
||||
prepareChartOptions(data) {
|
||||
this.chartOptions = {
|
||||
color: new graphic.LinearGradient(0, 0, 0, 0.65, [
|
||||
{ offset: 0, color: '#D81B60' },
|
||||
{ offset: 0.25, color: '#8E24AA' },
|
||||
{ offset: 0.5, color: '#5E35B1' },
|
||||
{ offset: 0.75, color: '#3949AB' },
|
||||
{ offset: 1, color: '#1E88E5' }
|
||||
]),
|
||||
title: {
|
||||
text: this.widget? '' : $localize`:@@mining.difficulty:Difficulty`,
|
||||
left: 'center',
|
||||
@@ -110,6 +104,17 @@ export class DifficultyChartComponent implements OnInit {
|
||||
tooltip: {
|
||||
show: true,
|
||||
trigger: 'axis',
|
||||
backgroundColor: 'rgba(17, 19, 31, 1)',
|
||||
borderRadius: 4,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
textStyle: {
|
||||
color: '#b1b1b1',
|
||||
},
|
||||
borderColor: '#000',
|
||||
formatter: params => {
|
||||
return `<b style="color: white">${params[0].axisValueLabel}</b><br>
|
||||
${params[0].marker} ${formatNumber(params[0].value[1], this.locale, '1.0-0')}`
|
||||
}
|
||||
},
|
||||
axisPointer: {
|
||||
type: 'line',
|
||||
@@ -122,8 +127,9 @@ export class DifficultyChartComponent implements OnInit {
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
formatter: (val) => {
|
||||
const diff = val / Math.pow(10, 12); // terra
|
||||
return diff.toString() + 'T';
|
||||
const selectedPowerOfTen: any = selectPowerOfTen(val);
|
||||
const diff = val / selectedPowerOfTen.divider;
|
||||
return `${diff} ${selectedPowerOfTen.unit}`;
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
@@ -134,17 +140,40 @@ export class DifficultyChartComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: data,
|
||||
type: 'line',
|
||||
smooth: false,
|
||||
lineStyle: {
|
||||
width: 3,
|
||||
},
|
||||
areaStyle: {}
|
||||
series: {
|
||||
showSymbol: false,
|
||||
data: data,
|
||||
type: 'line',
|
||||
smooth: false,
|
||||
lineStyle: {
|
||||
width: 2,
|
||||
},
|
||||
],
|
||||
},
|
||||
dataZoom: this.widget ? null : [{
|
||||
type: 'inside',
|
||||
realtime: true,
|
||||
zoomLock: true,
|
||||
zoomOnMouseWheel: true,
|
||||
moveOnMouseMove: true,
|
||||
maxSpan: 100,
|
||||
minSpan: 10,
|
||||
}, {
|
||||
showDetail: false,
|
||||
show: true,
|
||||
type: 'slider',
|
||||
brushSelect: false,
|
||||
realtime: true,
|
||||
bottom: 0,
|
||||
selectedDataBackground: {
|
||||
lineStyle: {
|
||||
color: '#fff',
|
||||
opacity: 0.45,
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0,
|
||||
}
|
||||
},
|
||||
}],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<div [class]="widget === false ? 'full-container' : ''">
|
||||
|
||||
<div class="card-header mb-0 mb-lg-4" [style]="widget ? 'display:none' : ''">
|
||||
<form [formGroup]="radioGroupForm" class="formRadioGroup" *ngIf="(hashrateObservable$ | async) as hashrates">
|
||||
<div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan">
|
||||
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="hashrates.availableTimespanDay >= 90">
|
||||
<input ngbButton type="radio" [value]="'3m'"> 3M
|
||||
</label>
|
||||
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="hashrates.availableTimespanDay >= 180">
|
||||
<input ngbButton type="radio" [value]="'6m'"> 6M
|
||||
</label>
|
||||
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="hashrates.availableTimespanDay >= 365">
|
||||
<input ngbButton type="radio" [value]="'1y'"> 1Y
|
||||
</label>
|
||||
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="hashrates.availableTimespanDay >= 730">
|
||||
<input ngbButton type="radio" [value]="'2y'"> 2Y
|
||||
</label>
|
||||
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="hashrates.availableTimespanDay >= 1095">
|
||||
<input ngbButton type="radio" [value]="'3y'"> 3Y
|
||||
</label>
|
||||
<label ngbButtonLabel class="btn-primary btn-sm">
|
||||
<input ngbButton type="radio" [value]="'all'"> ALL
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div *ngIf="hashrateObservable$ | async" [class]="widget === false ? 'chart' : ''" echarts [initOpts]="chartInitOptions" [options]="chartOptions"></div>
|
||||
<div class="text-center loadingGraphs" *ngIf="isLoading">
|
||||
<div class="spinner-border text-light"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,45 @@
|
||||
.main-title {
|
||||
position: relative;
|
||||
color: #ffffff91;
|
||||
margin-top: -13px;
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
|
||||
.full-container {
|
||||
width: 100%;
|
||||
height: calc(100% - 100px);
|
||||
@media (max-width: 992px) {
|
||||
height: calc(100% - 140px);
|
||||
};
|
||||
@media (max-width: 576px) {
|
||||
height: calc(100% - 180px);
|
||||
};
|
||||
}
|
||||
|
||||
.chart {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding-bottom: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.formRadioGroup {
|
||||
margin-top: 6px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@media (min-width: 830px) {
|
||||
flex-direction: row;
|
||||
float: right;
|
||||
margin-top: 0px;
|
||||
}
|
||||
.btn-sm {
|
||||
font-size: 9px;
|
||||
@media (min-width: 830px) {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
import { Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core';
|
||||
import { EChartsOption, graphic } from 'echarts';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, share, startWith, switchMap, tap } from 'rxjs/operators';
|
||||
import { ApiService } from 'src/app/services/api.service';
|
||||
import { SeoService } from 'src/app/services/seo.service';
|
||||
import { formatNumber } from '@angular/common';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { selectPowerOfTen } from 'src/app/bitcoin.utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-hashrate-chart',
|
||||
templateUrl: './hashrate-chart.component.html',
|
||||
styleUrls: ['./hashrate-chart.component.scss'],
|
||||
styles: [`
|
||||
.loadingGraphs {
|
||||
position: absolute;
|
||||
top: 38%;
|
||||
left: calc(50% - 15px);
|
||||
z-index: 100;
|
||||
}
|
||||
`],
|
||||
})
|
||||
export class HashrateChartComponent implements OnInit {
|
||||
@Input() widget: boolean = false;
|
||||
@Input() right: number | string = 10;
|
||||
@Input() left: number | string = 75;
|
||||
|
||||
radioGroupForm: FormGroup;
|
||||
|
||||
chartOptions: EChartsOption = {};
|
||||
chartInitOptions = {
|
||||
renderer: 'svg',
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
};
|
||||
|
||||
hashrateObservable$: Observable<any>;
|
||||
isLoading = true;
|
||||
formatNumber = formatNumber;
|
||||
|
||||
constructor(
|
||||
@Inject(LOCALE_ID) public locale: string,
|
||||
private seoService: SeoService,
|
||||
private apiService: ApiService,
|
||||
private formBuilder: FormBuilder,
|
||||
) {
|
||||
this.seoService.setTitle($localize`:@@mining.hashrate:hashrate`);
|
||||
this.radioGroupForm = this.formBuilder.group({ dateSpan: '1y' });
|
||||
this.radioGroupForm.controls.dateSpan.setValue('1y');
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.hashrateObservable$ = this.radioGroupForm.get('dateSpan').valueChanges
|
||||
.pipe(
|
||||
startWith('1y'),
|
||||
switchMap((timespan) => {
|
||||
return this.apiService.getHistoricalHashrate$(timespan)
|
||||
.pipe(
|
||||
tap(data => {
|
||||
this.prepareChartOptions(data.hashrates.map(val => [val.timestamp * 1000, val.avgHashrate]));
|
||||
this.isLoading = false;
|
||||
}),
|
||||
map(data => {
|
||||
const availableTimespanDay = (
|
||||
(new Date().getTime() / 1000) - (data.oldestIndexedBlockTimestamp / 1000)
|
||||
) / 3600 / 24;
|
||||
return {
|
||||
availableTimespanDay: availableTimespanDay,
|
||||
data: data.hashrates
|
||||
};
|
||||
}),
|
||||
);
|
||||
}),
|
||||
share()
|
||||
);
|
||||
}
|
||||
|
||||
prepareChartOptions(data) {
|
||||
this.chartOptions = {
|
||||
color: new graphic.LinearGradient(0, 0, 0, 0.65, [
|
||||
{ offset: 0, color: '#F4511E' },
|
||||
{ offset: 0.25, color: '#FB8C00' },
|
||||
{ offset: 0.5, color: '#FFB300' },
|
||||
{ offset: 0.75, color: '#FDD835' },
|
||||
{ offset: 1, color: '#7CB342' }
|
||||
]),
|
||||
grid: {
|
||||
right: this.right,
|
||||
left: this.left,
|
||||
},
|
||||
title: {
|
||||
text: this.widget ? '' : $localize`:@@mining.hashrate:Hashrate`,
|
||||
left: 'center',
|
||||
textStyle: {
|
||||
color: '#FFF',
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
trigger: 'axis',
|
||||
backgroundColor: 'rgba(17, 19, 31, 1)',
|
||||
borderRadius: 4,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
textStyle: {
|
||||
color: '#b1b1b1',
|
||||
},
|
||||
borderColor: '#000',
|
||||
formatter: params => {
|
||||
return `<b style="color: white">${params[0].axisValueLabel}</b><br>
|
||||
${params[0].marker} ${formatNumber(params[0].value[1], this.locale, '1.0-0')} H/s`
|
||||
}
|
||||
},
|
||||
axisPointer: {
|
||||
type: 'line',
|
||||
},
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
splitNumber: this.isMobile() ? 5 : 10,
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
formatter: (val) => {
|
||||
const selectedPowerOfTen: any = selectPowerOfTen(val);
|
||||
const newVal = val / selectedPowerOfTen.divider;
|
||||
return `${newVal} ${selectedPowerOfTen.unit}H/s`
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: 'dotted',
|
||||
color: '#ffffff66',
|
||||
opacity: 0.25,
|
||||
}
|
||||
},
|
||||
},
|
||||
series: {
|
||||
showSymbol: false,
|
||||
data: data,
|
||||
type: 'line',
|
||||
smooth: false,
|
||||
lineStyle: {
|
||||
width: 2,
|
||||
},
|
||||
},
|
||||
dataZoom: this.widget ? null : [{
|
||||
type: 'inside',
|
||||
realtime: true,
|
||||
zoomLock: true,
|
||||
zoomOnMouseWheel: true,
|
||||
moveOnMouseMove: true,
|
||||
maxSpan: 100,
|
||||
minSpan: 10,
|
||||
}, {
|
||||
showDetail: false,
|
||||
show: true,
|
||||
type: 'slider',
|
||||
brushSelect: false,
|
||||
realtime: true,
|
||||
bottom: 0,
|
||||
selectedDataBackground: {
|
||||
lineStyle: {
|
||||
color: '#fff',
|
||||
opacity: 0.45,
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0,
|
||||
}
|
||||
},
|
||||
}],
|
||||
};
|
||||
}
|
||||
|
||||
isMobile() {
|
||||
return (window.innerWidth <= 767.98);
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@
|
||||
<li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}" id="btn-home">
|
||||
<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 class="nav-item" routerLinkActive="active" id="btn-pools" *ngIf="stateService.env.MINING_DASHBOARD">
|
||||
<li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}" id="btn-pools" *ngIf="stateService.env.MINING_DASHBOARD">
|
||||
<a class="nav-link" [routerLink]="['/mining' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'hammer']" [fixedWidth]="true" i18n-title="master-page.mining-dashboard" title="Mining Dashboard"></fa-icon></a>
|
||||
</li>
|
||||
<li class="nav-item" routerLinkActive="active" id="btn-blocks" *ngIf="!stateService.env.MINING_DASHBOARD">
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
<!-- pool distribution -->
|
||||
<div class="col">
|
||||
<div class="main-title" i18n="mining.pool-share">Mining Pools Share (1w)</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title" i18n="mining.pool-share">Mining Pools Share (1w)</h5>
|
||||
<app-pool-ranking [widget]=true></app-pool-ranking>
|
||||
<div class="text-center"><a href="" [routerLink]="['/mining/pools' | relativeUrl]" i18n="dashboard.view-more">View more
|
||||
»</a></div>
|
||||
@@ -14,11 +14,23 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- difficulty -->
|
||||
<!-- hashrate -->
|
||||
<div class="col">
|
||||
<div class="main-title" i18n="mining.difficulty">Difficulty (1y)</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title" i18n="mining.hashrate">Hashrate (1y)</h5>
|
||||
<app-hashrate-chart [widget]=true></app-hashrate-chart>
|
||||
<div class="text-center"><a href="" [routerLink]="['/mining/hashrate' | relativeUrl]" i18n="dashboard.view-more">View more
|
||||
»</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- difficulty -->
|
||||
<div class="col">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title" i18n="ning.difficulty">Difficulty (1y)</h5>
|
||||
<app-difficulty-chart [widget]=true></app-difficulty-chart>
|
||||
<div class="text-center"><a href="" [routerLink]="['/mining/difficulty' | relativeUrl]" i18n="dashboard.view-more">View more
|
||||
»</a></div>
|
||||
|
||||
@@ -15,6 +15,11 @@
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
color: #4a68b9;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.card-wrapper {
|
||||
.card {
|
||||
height: auto !important;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<router-outlet></router-outlet>
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-mining-start',
|
||||
templateUrl: './mining-start.component.html',
|
||||
})
|
||||
export class MiningStartComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -121,21 +121,24 @@ export class PoolRankingComponent implements OnInit {
|
||||
value: pool.share,
|
||||
name: pool.name + (this.isMobile() ? `` : ` (${pool.share}%)`),
|
||||
label: {
|
||||
color: '#FFFFFF',
|
||||
color: '#b1b1b1',
|
||||
overflow: 'break',
|
||||
},
|
||||
tooltip: {
|
||||
backgroundColor: '#282d47',
|
||||
backgroundColor: 'rgba(17, 19, 31, 1)',
|
||||
borderRadius: 4,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
textStyle: {
|
||||
color: '#FFFFFF',
|
||||
color: '#b1b1b1',
|
||||
},
|
||||
borderColor: '#000',
|
||||
formatter: () => {
|
||||
if (this.poolsWindowPreference === '24h') {
|
||||
return `<u><b>${pool.name} (${pool.share}%)</b></u><br>` +
|
||||
return `<b style="color: white">${pool.name} (${pool.share}%)</b><br>` +
|
||||
pool.lastEstimatedHashrate.toString() + ' PH/s' +
|
||||
`<br>` + pool.blockCount.toString() + ` blocks`;
|
||||
} else {
|
||||
return `<u><b>${pool.name} (${pool.share}%)</b></u><br>` +
|
||||
return `<b style="color: white">${pool.name} (${pool.share}%)</b><br>` +
|
||||
pool.blockCount.toString() + ` blocks`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,4 +156,11 @@ export class ApiService {
|
||||
(interval !== undefined ? `/${interval}` : '')
|
||||
);
|
||||
}
|
||||
|
||||
getHistoricalHashrate$(interval: string | undefined): Observable<any> {
|
||||
return this.httpClient.get<any[]>(
|
||||
this.apiBaseUrl + this.apiBasePath + `/api/v1/mining/hashrate` +
|
||||
(interval !== undefined ? `/${interval}` : '')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user