Merge branch 'master' into recvd-htlcs
This commit is contained in:
@@ -48,7 +48,8 @@ import { DashboardComponent } from './dashboard/dashboard.component';
|
||||
import { DifficultyComponent } from './components/difficulty/difficulty.component';
|
||||
import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
|
||||
import { faFilter, faAngleDown, faAngleUp, faAngleRight, faAngleLeft, faBolt, faChartArea, faCogs, faCubes, faHammer, faDatabase, faExchangeAlt, faInfoCircle,
|
||||
faLink, faList, faSearch, faCaretUp, faCaretDown, faTachometerAlt, faThList, faTint, faTv, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown, faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl } from '@fortawesome/free-solid-svg-icons';
|
||||
faLink, faList, faSearch, faCaretUp, faCaretDown, faTachometerAlt, faThList, faTint, faTv, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown,
|
||||
faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl, faDownload } from '@fortawesome/free-solid-svg-icons';
|
||||
import { TermsOfServiceComponent } from './components/terms-of-service/terms-of-service.component';
|
||||
import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-policy.component';
|
||||
import { TrademarkPolicyComponent } from './components/trademark-policy/trademark-policy.component';
|
||||
@@ -75,6 +76,8 @@ import { DataCyDirective } from './data-cy.directive';
|
||||
import { BlockFeesGraphComponent } from './components/block-fees-graph/block-fees-graph.component';
|
||||
import { BlockRewardsGraphComponent } from './components/block-rewards-graph/block-rewards-graph.component';
|
||||
import { BlockFeeRatesGraphComponent } from './components/block-fee-rates-graph/block-fee-rates-graph.component';
|
||||
import { LoadingIndicatorComponent } from './components/loading-indicator/loading-indicator.component';
|
||||
import { IndexingProgressComponent } from './components/indexing-progress/indexing-progress.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -131,6 +134,8 @@ import { BlockFeeRatesGraphComponent } from './components/block-fee-rates-graph/
|
||||
BlockFeesGraphComponent,
|
||||
BlockRewardsGraphComponent,
|
||||
BlockFeeRatesGraphComponent,
|
||||
LoadingIndicatorComponent,
|
||||
IndexingProgressComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule.withServerTransition({ appId: 'serverApp' }),
|
||||
@@ -195,5 +200,6 @@ export class AppModule {
|
||||
library.addIcons(faAngleLeft);
|
||||
library.addIcons(faBook);
|
||||
library.addIcons(faListUl);
|
||||
library.addIcons(faDownload);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@
|
||||
<ng-template #displayServerError><i class="small">({{ error.error }})</i></ng-template>
|
||||
<ng-template [ngIf]="error.status === 413 || error.status === 405" [ngIfElse]="displayServerError">
|
||||
<ng-container i18n="Electrum server limit exceeded error">
|
||||
<i>The number of transactions on this address exceeds the Electrum server limit</i>
|
||||
<i>There many transactions on this address, more than your backend can handle. See more on <a href="/docs/faq#address-lookup-issues">setting up a stronger backend</a>.</i>
|
||||
<br><br>
|
||||
Consider viewing this address on the official Mempool website instead:
|
||||
</ng-container>
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
<app-indexing-progress></app-indexing-progress>
|
||||
|
||||
<div class="full-container">
|
||||
<div class="card-header mb-0 mb-md-4">
|
||||
<span i18n="mining.block-fee-rates">Block fee rates</span>
|
||||
<button class="btn" style="margin: 0 0 4px 0px" (click)="onSaveChart()">
|
||||
<fa-icon [icon]="['fas', 'download']" [fixedWidth]="true"></fa-icon>
|
||||
</button>
|
||||
|
||||
<form [formGroup]="radioGroupForm" class="formRadioGroup" *ngIf="(statsObservable$ | async) as stats">
|
||||
<div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan">
|
||||
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="stats.availableTimespanDay >= 1">
|
||||
|
||||
@@ -59,11 +59,11 @@
|
||||
margin-top: 6px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@media (min-width: 1130px) {
|
||||
@media (min-width: 991px) {
|
||||
position: relative;
|
||||
top: -65px;
|
||||
}
|
||||
@media (min-width: 830px) and (max-width: 1130px) {
|
||||
@media (min-width: 830px) and (max-width: 991px) {
|
||||
position: relative;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ 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 { formatterXAxis, formatterXAxisLabel, formatterXAxisTimeCategory } from 'src/app/shared/graphs.utils';
|
||||
import { download, formatterXAxis, formatterXAxisLabel, formatterXAxisTimeCategory } from 'src/app/shared/graphs.utils';
|
||||
import { StorageService } from 'src/app/services/storage.service';
|
||||
import { MiningService } from 'src/app/services/mining.service';
|
||||
import { selectPowerOfTen } from 'src/app/bitcoin.utils';
|
||||
@@ -297,4 +297,22 @@ export class BlockFeeRatesGraphComponent implements OnInit {
|
||||
isMobile() {
|
||||
return (window.innerWidth <= 767.98);
|
||||
}
|
||||
|
||||
onSaveChart() {
|
||||
// @ts-ignore
|
||||
const prevBottom = this.chartOptions.grid.bottom;
|
||||
const now = new Date();
|
||||
// @ts-ignore
|
||||
this.chartOptions.grid.bottom = 40;
|
||||
this.chartOptions.backgroundColor = '#11131f';
|
||||
this.chartInstance.setOption(this.chartOptions);
|
||||
download(this.chartInstance.getDataURL({
|
||||
pixelRatio: 2,
|
||||
excludeComponents: ['dataZoom'],
|
||||
}), `block-fee-rates-${this.timespan}-${Math.round(now.getTime() / 1000)}.svg`);
|
||||
// @ts-ignore
|
||||
this.chartOptions.grid.bottom = prevBottom;
|
||||
this.chartOptions.backgroundColor = 'none';
|
||||
this.chartInstance.setOption(this.chartOptions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
<app-indexing-progress></app-indexing-progress>
|
||||
|
||||
<div class="full-container">
|
||||
<div class="card-header mb-0 mb-md-4">
|
||||
<span i18n="mining.block-fees">Block fees</span>
|
||||
<button class="btn" style="margin: 0 0 4px 0px" (click)="onSaveChart()">
|
||||
<fa-icon [icon]="['fas', 'download']" [fixedWidth]="true"></fa-icon>
|
||||
</button>
|
||||
|
||||
<form [formGroup]="radioGroupForm" class="formRadioGroup" *ngIf="(statsObservable$ | async) as stats">
|
||||
<div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan">
|
||||
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="stats.blockCount >= 144">
|
||||
@@ -37,7 +43,8 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="chart" echarts [initOpts]="chartInitOptions" [options]="chartOptions">
|
||||
<div class="chart" echarts [initOpts]="chartInitOptions" [options]="chartOptions"
|
||||
(chartInit)="onChartInit($event)">
|
||||
</div>
|
||||
<div class="text-center loadingGraphs" *ngIf="isLoading">
|
||||
<div class="spinner-border text-light"></div>
|
||||
|
||||
@@ -59,11 +59,11 @@
|
||||
margin-top: 6px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@media (min-width: 1130px) {
|
||||
@media (min-width: 991px) {
|
||||
position: relative;
|
||||
top: -65px;
|
||||
}
|
||||
@media (min-width: 830px) and (max-width: 1130px) {
|
||||
@media (min-width: 830px) and (max-width: 991px) {
|
||||
position: relative;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ 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 { formatterXAxisLabel } from 'src/app/shared/graphs.utils';
|
||||
import { download, formatterXAxisLabel } from 'src/app/shared/graphs.utils';
|
||||
import { StorageService } from 'src/app/services/storage.service';
|
||||
import { MiningService } from 'src/app/services/mining.service';
|
||||
|
||||
@@ -40,6 +40,7 @@ export class BlockFeesGraphComponent implements OnInit {
|
||||
isLoading = true;
|
||||
formatNumber = formatNumber;
|
||||
timespan = '';
|
||||
chartInstance: any = undefined;
|
||||
|
||||
constructor(
|
||||
@Inject(LOCALE_ID) public locale: string,
|
||||
@@ -194,7 +195,29 @@ export class BlockFeesGraphComponent implements OnInit {
|
||||
};
|
||||
}
|
||||
|
||||
onChartInit(ec) {
|
||||
this.chartInstance = ec;
|
||||
}
|
||||
|
||||
isMobile() {
|
||||
return (window.innerWidth <= 767.98);
|
||||
}
|
||||
|
||||
onSaveChart() {
|
||||
// @ts-ignore
|
||||
const prevBottom = this.chartOptions.grid.bottom;
|
||||
const now = new Date();
|
||||
// @ts-ignore
|
||||
this.chartOptions.grid.bottom = 40;
|
||||
this.chartOptions.backgroundColor = '#11131f';
|
||||
this.chartInstance.setOption(this.chartOptions);
|
||||
download(this.chartInstance.getDataURL({
|
||||
pixelRatio: 2,
|
||||
excludeComponents: ['dataZoom'],
|
||||
}), `block-fees-${this.timespan}-${Math.round(now.getTime() / 1000)}.svg`);
|
||||
// @ts-ignore
|
||||
this.chartOptions.grid.bottom = prevBottom;
|
||||
this.chartOptions.backgroundColor = 'none';
|
||||
this.chartInstance.setOption(this.chartOptions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
<app-indexing-progress></app-indexing-progress>
|
||||
|
||||
<div class="full-container">
|
||||
|
||||
<div class="card-header mb-0 mb-md-4">
|
||||
<span i18n="mining.block-rewards">Block rewards</span>
|
||||
<button class="btn" style="margin: 0 0 4px 0px" (click)="onSaveChart()">
|
||||
<fa-icon [icon]="['fas', 'download']" [fixedWidth]="true"></fa-icon>
|
||||
</button>
|
||||
|
||||
<form [formGroup]="radioGroupForm" class="formRadioGroup" *ngIf="(statsObservable$ | async) as stats">
|
||||
<div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan">
|
||||
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="stats.blockCount >= 144">
|
||||
@@ -38,7 +44,8 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="chart" echarts [initOpts]="chartInitOptions" [options]="chartOptions">
|
||||
<div class="chart" echarts [initOpts]="chartInitOptions" [options]="chartOptions"
|
||||
(chartInit)="onChartInit($event)">
|
||||
</div>
|
||||
<div class="text-center loadingGraphs" *ngIf="isLoading">
|
||||
<div class="spinner-border text-light"></div>
|
||||
|
||||
@@ -59,11 +59,11 @@
|
||||
margin-top: 6px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@media (min-width: 1130px) {
|
||||
@media (min-width: 991px) {
|
||||
position: relative;
|
||||
top: -65px;
|
||||
}
|
||||
@media (min-width: 830px) and (max-width: 1130px) {
|
||||
@media (min-width: 830px) and (max-width: 991px) {
|
||||
position: relative;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ 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 { formatterXAxisLabel } from 'src/app/shared/graphs.utils';
|
||||
import { download, formatterXAxisLabel } from 'src/app/shared/graphs.utils';
|
||||
import { MiningService } from 'src/app/services/mining.service';
|
||||
import { StorageService } from 'src/app/services/storage.service';
|
||||
|
||||
@@ -40,6 +40,7 @@ export class BlockRewardsGraphComponent implements OnInit {
|
||||
isLoading = true;
|
||||
formatNumber = formatNumber;
|
||||
timespan = '';
|
||||
chartInstance: any = undefined;
|
||||
|
||||
constructor(
|
||||
@Inject(LOCALE_ID) public locale: string,
|
||||
@@ -194,7 +195,29 @@ export class BlockRewardsGraphComponent implements OnInit {
|
||||
};
|
||||
}
|
||||
|
||||
onChartInit(ec) {
|
||||
this.chartInstance = ec;
|
||||
}
|
||||
|
||||
isMobile() {
|
||||
return (window.innerWidth <= 767.98);
|
||||
}
|
||||
|
||||
onSaveChart() {
|
||||
// @ts-ignore
|
||||
const prevBottom = this.chartOptions.grid.bottom;
|
||||
const now = new Date();
|
||||
// @ts-ignore
|
||||
this.chartOptions.grid.bottom = 40;
|
||||
this.chartOptions.backgroundColor = '#11131f';
|
||||
this.chartInstance.setOption(this.chartOptions);
|
||||
download(this.chartInstance.getDataURL({
|
||||
pixelRatio: 2,
|
||||
excludeComponents: ['dataZoom'],
|
||||
}), `block-rewards-${this.timespan}-${Math.round(now.getTime() / 1000)}.svg`);
|
||||
// @ts-ignore
|
||||
this.chartOptions.grid.bottom = prevBottom;
|
||||
this.chartOptions.backgroundColor = 'none';
|
||||
this.chartInstance.setOption(this.chartOptions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
<app-indexing-progress *ngIf="!widget"></app-indexing-progress>
|
||||
|
||||
<div class="container-xl" [class]="widget ? 'widget' : 'full-height'">
|
||||
<h1 *ngIf="!widget" class="float-left" i18n="latest-blocks.blocks">Blocks</h1>
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
<app-indexing-progress *ngIf="!widget"></app-indexing-progress>
|
||||
|
||||
<div [class]="widget === false ? 'full-container' : ''">
|
||||
|
||||
<div *ngIf="widget">
|
||||
@@ -19,6 +21,10 @@
|
||||
|
||||
<div class="card-header mb-0 mb-md-4" [style]="widget ? 'display:none' : ''">
|
||||
<span i18n="mining.hashrate-difficulty">Hashrate & Difficulty</span>
|
||||
<button class="btn" style="margin: 0 0 4px 0px" (click)="onSaveChart()">
|
||||
<fa-icon [icon]="['fas', 'download']" [fixedWidth]="true"></fa-icon>
|
||||
</button>
|
||||
|
||||
<form [formGroup]="radioGroupForm" class="formRadioGroup" *ngIf="(hashrateObservable$ | async) as stats">
|
||||
<div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan">
|
||||
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="stats.blockCount >= 4320">
|
||||
@@ -46,7 +52,8 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div [class]="!widget ? 'chart' : 'chart-widget'" echarts [initOpts]="chartInitOptions" [options]="chartOptions">
|
||||
<div [class]="!widget ? 'chart' : 'chart-widget'" echarts [initOpts]="chartInitOptions" [options]="chartOptions"
|
||||
(chartInit)="onChartInit($event)">
|
||||
</div>
|
||||
<div class="text-center loadingGraphs" *ngIf="isLoading">
|
||||
<div class="spinner-border text-light"></div>
|
||||
|
||||
@@ -59,11 +59,11 @@
|
||||
margin-top: 6px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@media (min-width: 1130px) {
|
||||
@media (min-width: 991px) {
|
||||
position: relative;
|
||||
top: -65px;
|
||||
}
|
||||
@media (min-width: 830px) and (max-width: 1130px) {
|
||||
@media (min-width: 830px) and (max-width: 991px) {
|
||||
position: relative;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { selectPowerOfTen } from 'src/app/bitcoin.utils';
|
||||
import { StorageService } from 'src/app/services/storage.service';
|
||||
import { MiningService } from 'src/app/services/mining.service';
|
||||
import { download } from 'src/app/shared/graphs.utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-hashrate-chart',
|
||||
@@ -43,6 +44,8 @@ export class HashrateChartComponent implements OnInit {
|
||||
hashrateObservable$: Observable<any>;
|
||||
isLoading = true;
|
||||
formatNumber = formatNumber;
|
||||
timespan = '';
|
||||
chartInstance: any = undefined;
|
||||
|
||||
constructor(
|
||||
@Inject(LOCALE_ID) public locale: string,
|
||||
@@ -74,6 +77,7 @@ export class HashrateChartComponent implements OnInit {
|
||||
if (!this.widget && !firstRun) {
|
||||
this.storageService.setValue('miningWindowPreference', timespan);
|
||||
}
|
||||
this.timespan = timespan;
|
||||
firstRun = false;
|
||||
this.miningWindowPreference = timespan;
|
||||
this.isLoading = true;
|
||||
@@ -132,16 +136,12 @@ export class HashrateChartComponent implements OnInit {
|
||||
prepareChartOptions(data) {
|
||||
let title: object;
|
||||
if (data.hashrates.length === 0) {
|
||||
const lastBlock = new Date(data.timestamp * 1000);
|
||||
const dd = String(lastBlock.getDate()).padStart(2, '0');
|
||||
const mm = String(lastBlock.getMonth() + 1).padStart(2, '0'); // January is 0!
|
||||
const yyyy = lastBlock.getFullYear();
|
||||
title = {
|
||||
textStyle: {
|
||||
color: 'grey',
|
||||
fontSize: 15
|
||||
},
|
||||
text: `Indexing in progess - ${yyyy}-${mm}-${dd}`,
|
||||
text: `Indexing in progess`,
|
||||
left: 'center',
|
||||
top: 'center'
|
||||
};
|
||||
@@ -340,7 +340,29 @@ export class HashrateChartComponent implements OnInit {
|
||||
};
|
||||
}
|
||||
|
||||
onChartInit(ec) {
|
||||
this.chartInstance = ec;
|
||||
}
|
||||
|
||||
isMobile() {
|
||||
return (window.innerWidth <= 767.98);
|
||||
}
|
||||
|
||||
onSaveChart() {
|
||||
// @ts-ignore
|
||||
const prevBottom = this.chartOptions.grid.bottom;
|
||||
const now = new Date();
|
||||
// @ts-ignore
|
||||
this.chartOptions.grid.bottom = 30;
|
||||
this.chartOptions.backgroundColor = '#11131f';
|
||||
this.chartInstance.setOption(this.chartOptions);
|
||||
download(this.chartInstance.getDataURL({
|
||||
pixelRatio: 2,
|
||||
excludeComponents: ['dataZoom'],
|
||||
}), `hashrate-difficulty-${this.timespan}-${Math.round(now.getTime() / 1000)}.svg`);
|
||||
// @ts-ignore
|
||||
this.chartOptions.grid.bottom = prevBottom;
|
||||
this.chartOptions.backgroundColor = 'none';
|
||||
this.chartInstance.setOption(this.chartOptions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
<app-indexing-progress></app-indexing-progress>
|
||||
|
||||
<div class="full-container">
|
||||
|
||||
<div class="card-header mb-0 mb-md-4">
|
||||
<span i18n="mining.pools-dominance">Mining pools dominance</span>
|
||||
<button class="btn" style="margin: 0 0 4px 0px" (click)="onSaveChart()">
|
||||
<fa-icon [icon]="['fas', 'download']" [fixedWidth]="true"></fa-icon>
|
||||
</button>
|
||||
|
||||
<form [formGroup]="radioGroupForm" class="formRadioGroup" *ngIf="(hashrateObservable$ | async) as stats">
|
||||
<div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan">
|
||||
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="stats.blockCount >= 4320">
|
||||
@@ -29,8 +35,9 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="chart"
|
||||
echarts [initOpts]="chartInitOptions" [options]="chartOptions"></div>
|
||||
<div class="chart" echarts [initOpts]="chartInitOptions" [options]="chartOptions"
|
||||
(chartInit)="onChartInit($event)">
|
||||
</div>
|
||||
<div class="text-center loadingGraphs" *ngIf="isLoading">
|
||||
<div class="spinner-border text-light"></div>
|
||||
</div>
|
||||
|
||||
@@ -53,11 +53,11 @@
|
||||
margin-top: 6px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@media (min-width: 1130px) {
|
||||
@media (min-width: 991px) {
|
||||
position: relative;
|
||||
top: -65px;
|
||||
}
|
||||
@media (min-width: 830px) and (max-width: 1130px) {
|
||||
@media (min-width: 830px) and (max-width: 991px) {
|
||||
position: relative;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { poolsColor } from 'src/app/app.constants';
|
||||
import { StorageService } from 'src/app/services/storage.service';
|
||||
import { MiningService } from 'src/app/services/mining.service';
|
||||
import { download } from 'src/app/shared/graphs.utils';
|
||||
import { time } from 'console';
|
||||
|
||||
@Component({
|
||||
selector: 'app-hashrate-chart-pools',
|
||||
@@ -39,6 +41,8 @@ export class HashrateChartPoolsComponent implements OnInit {
|
||||
|
||||
hashrateObservable$: Observable<any>;
|
||||
isLoading = true;
|
||||
timespan = '';
|
||||
chartInstance: any = undefined;
|
||||
|
||||
constructor(
|
||||
@Inject(LOCALE_ID) public locale: string,
|
||||
@@ -68,6 +72,7 @@ export class HashrateChartPoolsComponent implements OnInit {
|
||||
if (!firstRun) {
|
||||
this.storageService.setValue('miningWindowPreference', timespan);
|
||||
}
|
||||
this.timespan = timespan;
|
||||
firstRun = false;
|
||||
this.isLoading = true;
|
||||
return this.apiService.getHistoricalPoolsHashrate$(timespan)
|
||||
@@ -145,16 +150,12 @@ export class HashrateChartPoolsComponent implements OnInit {
|
||||
prepareChartOptions(data) {
|
||||
let title: object;
|
||||
if (data.series.length === 0) {
|
||||
const lastBlock = new Date(data.timestamp * 1000);
|
||||
const dd = String(lastBlock.getDate()).padStart(2, '0');
|
||||
const mm = String(lastBlock.getMonth() + 1).padStart(2, '0'); // January is 0!
|
||||
const yyyy = lastBlock.getFullYear();
|
||||
title = {
|
||||
textStyle: {
|
||||
color: 'grey',
|
||||
fontSize: 15
|
||||
},
|
||||
text: `Indexing in progess - ${yyyy}-${mm}-${dd}`,
|
||||
text: `Indexing in progess`,
|
||||
left: 'center',
|
||||
top: 'center',
|
||||
};
|
||||
@@ -247,7 +248,29 @@ export class HashrateChartPoolsComponent implements OnInit {
|
||||
};
|
||||
}
|
||||
|
||||
onChartInit(ec) {
|
||||
this.chartInstance = ec;
|
||||
}
|
||||
|
||||
isMobile() {
|
||||
return (window.innerWidth <= 767.98);
|
||||
}
|
||||
|
||||
onSaveChart() {
|
||||
// @ts-ignore
|
||||
const prevBottom = this.chartOptions.grid.bottom;
|
||||
const now = new Date();
|
||||
// @ts-ignore
|
||||
this.chartOptions.grid.bottom = 30;
|
||||
this.chartOptions.backgroundColor = '#11131f';
|
||||
this.chartInstance.setOption(this.chartOptions);
|
||||
download(this.chartInstance.getDataURL({
|
||||
pixelRatio: 2,
|
||||
excludeComponents: ['dataZoom'],
|
||||
}), `pools-dominance-${this.timespan}-${Math.round(now.getTime() / 1000)}.svg`);
|
||||
// @ts-ignore
|
||||
this.chartOptions.grid.bottom = prevBottom;
|
||||
this.chartOptions.backgroundColor = 'none';
|
||||
this.chartInstance.setOption(this.chartOptions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<div class="echarts" echarts [initOpts]="mempoolStatsChartInitOption" [options]="mempoolStatsChartOption" (chartRendered)="rendered()"></div>
|
||||
<div class="echarts" echarts [initOpts]="mempoolStatsChartInitOption" [options]="mempoolStatsChartOption" (chartRendered)="rendered()"
|
||||
(chartInit)="onChartInit($event)">
|
||||
</div>
|
||||
<div class="text-center loadingGraphs" *ngIf="isLoading">
|
||||
<div class="spinner-border text-light"></div>
|
||||
</div>
|
||||
@@ -2,7 +2,7 @@ import { Component, Input, Inject, LOCALE_ID, ChangeDetectionStrategy, OnInit }
|
||||
import { EChartsOption } from 'echarts';
|
||||
import { OnChanges } from '@angular/core';
|
||||
import { StorageService } from 'src/app/services/storage.service';
|
||||
import { formatterXAxis, formatterXAxisLabel } from 'src/app/shared/graphs.utils';
|
||||
import { download, formatterXAxis, formatterXAxisLabel } from 'src/app/shared/graphs.utils';
|
||||
import { formatNumber } from '@angular/common';
|
||||
|
||||
@Component({
|
||||
@@ -34,6 +34,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges {
|
||||
renderer: 'svg'
|
||||
};
|
||||
windowPreference: string;
|
||||
chartInstance: any = undefined;
|
||||
|
||||
constructor(
|
||||
@Inject(LOCALE_ID) private locale: string,
|
||||
@@ -224,7 +225,29 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges {
|
||||
};
|
||||
}
|
||||
|
||||
onChartInit(ec) {
|
||||
this.chartInstance = ec;
|
||||
}
|
||||
|
||||
isMobile() {
|
||||
return window.innerWidth <= 767.98;
|
||||
}
|
||||
|
||||
onSaveChart(timespan) {
|
||||
// @ts-ignore
|
||||
const prevHeight = this.mempoolStatsChartOption.grid.height;
|
||||
const now = new Date();
|
||||
// @ts-ignore
|
||||
this.mempoolStatsChartOption.grid.height = prevHeight + 20;
|
||||
this.mempoolStatsChartOption.backgroundColor = '#11131f';
|
||||
this.chartInstance.setOption(this.mempoolStatsChartOption);
|
||||
download(this.chartInstance.getDataURL({
|
||||
pixelRatio: 2,
|
||||
excludeComponents: ['dataZoom'],
|
||||
}), `incoming-vbytes-${timespan}-${Math.round(now.getTime() / 1000)}.svg`);
|
||||
// @ts-ignore
|
||||
this.mempoolStatsChartOption.grid.height = prevHeight;
|
||||
this.mempoolStatsChartOption.backgroundColor = 'none';
|
||||
this.chartInstance.setOption(this.mempoolStatsChartOption);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<app-loading-indicator [name]="'block-indexing'" i18n-label label="Indexing blocks"></app-loading-indicator>
|
||||
<app-loading-indicator [name]="'daily-hashrate-indexing'" i18n-label label="Indexing network hashrate"></app-loading-indicator>
|
||||
<app-loading-indicator [name]="'weekly-hashrate-indexing'" i18n-label label="Indexing pools hashrate"></app-loading-indicator>
|
||||
@@ -0,0 +1,14 @@
|
||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-indexing-progress',
|
||||
templateUrl: './indexing-progress.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class IndexingProgressComponent implements OnInit {
|
||||
constructor(
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<div *ngIf="this.indexingProgress$ | async as progress" class="sticky-loading">
|
||||
<span *ngIf="progress >= 0" class="mr-auto badge badge-pill badge-warning">{{ this.label }} ({{ progress }}%)</span>
|
||||
</div>
|
||||
@@ -0,0 +1,18 @@
|
||||
.sticky-loading {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
z-index: 100;
|
||||
font-size: 14px;
|
||||
@media (width >= 992px) {
|
||||
left: 32px;
|
||||
top: 55px;
|
||||
}
|
||||
@media (576px <= width < 992px ) {
|
||||
left: 18px;
|
||||
top: 55px;
|
||||
}
|
||||
@media (width <= 575px) {
|
||||
left: 18px;
|
||||
top: 100px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { StateService } from 'src/app/services/state.service';
|
||||
import { WebsocketService } from 'src/app/services/websocket.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-loading-indicator',
|
||||
templateUrl: './loading-indicator.component.html',
|
||||
styleUrls: ['./loading-indicator.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class LoadingIndicatorComponent implements OnInit {
|
||||
@Input() name: string;
|
||||
@Input() label: string;
|
||||
|
||||
public indexingProgress$: Observable<number>;
|
||||
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
private websocketService: WebsocketService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.indexingProgress$ = this.stateService.loadingIndicators$
|
||||
.pipe(
|
||||
map((indicators) => indicators[this.name] ?? -1)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Component, OnInit, Input, Inject, LOCALE_ID, ChangeDetectionStrategy, OnChanges } from '@angular/core';
|
||||
import { VbytesPipe } from 'src/app/shared/pipes/bytes-pipe/vbytes.pipe';
|
||||
import { formatNumber } from "@angular/common";
|
||||
import { formatNumber } from '@angular/common';
|
||||
import { OptimizedMempoolStats } from 'src/app/interfaces/node-api.interface';
|
||||
import { StateService } from 'src/app/services/state.service';
|
||||
import { StorageService } from 'src/app/services/storage.service';
|
||||
import { EChartsOption } from 'echarts';
|
||||
import { feeLevels, chartColors } from 'src/app/app.constants';
|
||||
import { formatterXAxis, formatterXAxisLabel } from 'src/app/shared/graphs.utils';
|
||||
import { download, formatterXAxis, formatterXAxisLabel } from 'src/app/shared/graphs.utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-mempool-graph',
|
||||
@@ -45,6 +45,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
feeLevelsOrdered = [];
|
||||
chartColorsOrdered = chartColors;
|
||||
inverted: boolean;
|
||||
chartInstance: any = undefined;
|
||||
|
||||
constructor(
|
||||
private vbytesPipe: VbytesPipe,
|
||||
@@ -83,6 +84,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
this.hoverIndexSerie = e.target.parent.parent.__ecComponentInfo.index;
|
||||
}
|
||||
});
|
||||
this.chartInstance = myChart;
|
||||
}
|
||||
|
||||
handleNewMempoolData(mempoolStats: OptimizedMempoolStats[]) {
|
||||
@@ -99,7 +101,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
generateArray(mempoolStats: OptimizedMempoolStats[]) {
|
||||
const finalArray: number[][][] = [];
|
||||
let feesArray: number[][] = [];
|
||||
let limitFeesTemplate = this.template === 'advanced' ? 26 : 20;
|
||||
const limitFeesTemplate = this.template === 'advanced' ? 26 : 20;
|
||||
for (let index = limitFeesTemplate; index > -1; index--) {
|
||||
feesArray = [];
|
||||
mempoolStats.forEach((stats) => {
|
||||
@@ -387,5 +389,23 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
isMobile() {
|
||||
return window.innerWidth <= 767.98;
|
||||
}
|
||||
|
||||
onSaveChart(timespan) {
|
||||
// @ts-ignore
|
||||
const prevHeight = this.mempoolVsizeFeesOptions.grid.height;
|
||||
const now = new Date();
|
||||
// @ts-ignore
|
||||
this.mempoolVsizeFeesOptions.grid.height = prevHeight + 20;
|
||||
this.mempoolVsizeFeesOptions.backgroundColor = '#11131f';
|
||||
this.chartInstance.setOption(this.mempoolVsizeFeesOptions);
|
||||
download(this.chartInstance.getDataURL({
|
||||
pixelRatio: 2,
|
||||
excludeComponents: ['dataZoom'],
|
||||
}), `mempool-graph-${timespan}-${Math.round(now.getTime() / 1000)}.svg`);
|
||||
// @ts-ignore
|
||||
this.mempoolVsizeFeesOptions.grid.height = prevHeight;
|
||||
this.mempoolVsizeFeesOptions.backgroundColor = 'none';
|
||||
this.chartInstance.setOption(this.mempoolVsizeFeesOptions);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
<app-indexing-progress></app-indexing-progress>
|
||||
|
||||
<div class="container-xl dashboard-container">
|
||||
|
||||
<div class="row row-cols-1 row-cols-md-2">
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { SeoService } from 'src/app/services/seo.service';
|
||||
import { StateService } from 'src/app/services/state.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { WebsocketService } from 'src/app/services/websocket.service';
|
||||
|
||||
@Component({
|
||||
@@ -12,8 +9,6 @@ import { WebsocketService } from 'src/app/services/websocket.service';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class MiningDashboardComponent implements OnInit {
|
||||
private blocks = [];
|
||||
|
||||
constructor(
|
||||
private seoService: SeoService,
|
||||
private websocketService: WebsocketService,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
<app-indexing-progress *ngIf="!widget"></app-indexing-progress>
|
||||
|
||||
<div [class]="widget === false ? 'full-container' : ''">
|
||||
|
||||
<div *ngIf="widget">
|
||||
@@ -25,6 +27,10 @@
|
||||
|
||||
<div class="card-header" *ngIf="!widget">
|
||||
<span i18n="mining.mining-pool-share">Mining pools share</span>
|
||||
<button class="btn" style="margin: 0 0 4px 0px" (click)="onSaveChart()">
|
||||
<fa-icon [icon]="['fas', 'download']" [fixedWidth]="true"></fa-icon>
|
||||
</button>
|
||||
|
||||
<form [formGroup]="radioGroupForm" class="formRadioGroup"
|
||||
*ngIf="!widget && (miningStatsObservable$ | async) as stats">
|
||||
<div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan">
|
||||
|
||||
@@ -37,11 +37,11 @@
|
||||
margin-top: 6px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@media (min-width: 1130px) {
|
||||
@media (min-width: 991px) {
|
||||
position: relative;
|
||||
top: -65px;
|
||||
}
|
||||
@media (min-width: 830px) and (max-width: 1130px) {
|
||||
@media (min-width: 830px) and (max-width: 991px) {
|
||||
position: relative;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { MiningService, MiningStats } from '../../services/mining.service';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { chartColors, poolsColor } from 'src/app/app.constants';
|
||||
import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe';
|
||||
import { download } from 'src/app/shared/graphs.utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-pool-ranking',
|
||||
@@ -29,6 +30,7 @@ export class PoolRankingComponent implements OnInit {
|
||||
chartInitOptions = {
|
||||
renderer: 'svg',
|
||||
};
|
||||
timespan = '';
|
||||
chartInstance: any = undefined;
|
||||
|
||||
@HostBinding('attr.dir') dir = 'ltr';
|
||||
@@ -69,6 +71,7 @@ export class PoolRankingComponent implements OnInit {
|
||||
.pipe(
|
||||
startWith(this.miningWindowPreference), // (trigger when the page loads)
|
||||
tap((value) => {
|
||||
this.timespan = value;
|
||||
if (!this.widget) {
|
||||
this.storageService.setValue('miningWindowPreference', value);
|
||||
}
|
||||
@@ -283,5 +286,17 @@ export class PoolRankingComponent implements OnInit {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
onSaveChart() {
|
||||
const now = new Date();
|
||||
this.chartOptions.backgroundColor = '#11131f';
|
||||
this.chartInstance.setOption(this.chartOptions);
|
||||
download(this.chartInstance.getDataURL({
|
||||
pixelRatio: 2,
|
||||
excludeComponents: ['dataZoom'],
|
||||
}), `pools-ranking-${this.timespan}-${Math.round(now.getTime() / 1000)}.svg`);
|
||||
this.chartOptions.backgroundColor = 'none';
|
||||
this.chartInstance.setOption(this.chartOptions);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
<app-indexing-progress></app-indexing-progress>
|
||||
|
||||
<div class="container-xl">
|
||||
|
||||
<!-- Pool overview -->
|
||||
|
||||
@@ -111,7 +111,7 @@ export class PoolComponent implements OnInit {
|
||||
color: 'grey',
|
||||
fontSize: 15
|
||||
},
|
||||
text: `No data`,
|
||||
text: `Indexing in progress`,
|
||||
left: 'center',
|
||||
top: 'center'
|
||||
};
|
||||
@@ -164,14 +164,14 @@ export class PoolComponent implements OnInit {
|
||||
`;
|
||||
}.bind(this)
|
||||
},
|
||||
xAxis: {
|
||||
xAxis: data.length === 0 ? undefined : {
|
||||
type: 'time',
|
||||
splitNumber: (this.isMobile()) ? 5 : 10,
|
||||
axisLabel: {
|
||||
hideOverlap: true,
|
||||
}
|
||||
},
|
||||
yAxis: [
|
||||
yAxis: data.length === 0 ? undefined : [
|
||||
{
|
||||
min: (value) => {
|
||||
return value.min * 0.9;
|
||||
@@ -190,7 +190,7 @@ export class PoolComponent implements OnInit {
|
||||
}
|
||||
},
|
||||
],
|
||||
series: [
|
||||
series: data.length === 0 ? undefined : [
|
||||
{
|
||||
zlevel: 0,
|
||||
name: 'Hashrate',
|
||||
|
||||
@@ -3,14 +3,22 @@
|
||||
<div>
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<i class="fa fa-area-chart"></i> <span i18n="statistics.memory-by-vBytes">Mempool by vBytes (sat/vByte)</span>
|
||||
<form [formGroup]="radioGroupForm" class="formRadioGroup" [class]="stateService.env.MINING_DASHBOARD ? 'mining' : ''" (click)="saveGraphPreference()">
|
||||
<i class="fa fa-area-chart"></i>
|
||||
<span i18n="statistics.memory-by-vBytes">Mempool by vBytes (sat/vByte)</span>
|
||||
<button class="btn" style="margin: 0 0 4px 0px" (click)="onSaveChart('mempool')">
|
||||
<fa-icon [icon]="['fas', 'download']" [fixedWidth]="true"></fa-icon>
|
||||
</button>
|
||||
|
||||
<form [formGroup]="radioGroupForm" class="formRadioGroup"
|
||||
[class]="stateService.env.MINING_DASHBOARD ? 'mining' : ''" (click)="saveGraphPreference()">
|
||||
<div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan">
|
||||
<label ngbButtonLabel class="btn-primary btn-sm">
|
||||
<input ngbButton type="radio" [value]="'2h'" [routerLink]="['/graphs' | relativeUrl]" fragment="2h"> 2H (LIVE)
|
||||
<input ngbButton type="radio" [value]="'2h'" [routerLink]="['/graphs' | relativeUrl]" fragment="2h"> 2H
|
||||
(LIVE)
|
||||
</label>
|
||||
<label ngbButtonLabel class="btn-primary btn-sm">
|
||||
<input ngbButton type="radio" [value]="'24h'" [routerLink]="['/graphs' | relativeUrl]" fragment="24h"> 24H
|
||||
<input ngbButton type="radio" [value]="'24h'" [routerLink]="['/graphs' | relativeUrl]" fragment="24h">
|
||||
24H
|
||||
</label>
|
||||
<label ngbButtonLabel class="btn-primary btn-sm">
|
||||
<input ngbButton type="radio" [value]="'1w'" [routerLink]="['/graphs' | relativeUrl]" fragment="1w"> 1W
|
||||
@@ -37,39 +45,37 @@
|
||||
<div class="small-buttons">
|
||||
<div ngbDropdown #myDrop="ngbDropdown">
|
||||
<button class="btn btn-primary btn-sm" id="dropdownFees" ngbDropdownAnchor (click)="myDrop.toggle()">
|
||||
<fa-icon [icon]="['fas', 'filter']" [fixedWidth]="true" i18n-title="statistics.component-filter.title" title="Filter"></fa-icon>
|
||||
<fa-icon [icon]="['fas', 'filter']" [fixedWidth]="true" i18n-title="statistics.component-filter.title"
|
||||
title="Filter"></fa-icon>
|
||||
</button>
|
||||
<div class="dropdown-fees" ngbDropdownMenu aria-labelledby="dropdownFees">
|
||||
<ul>
|
||||
<ng-template ngFor let-feeData let-i="index" [ngForOf]="feeLevelDropdownData">
|
||||
<ng-template [ngIf]="feeData.fee <= 400">
|
||||
<li (click)="filterFeeIndex = feeData.fee" [class]="filterFeeIndex > feeData.fee ? 'inactive' : ''">
|
||||
<li (click)="filterFeeIndex = feeData.fee"
|
||||
[class]="filterFeeIndex > feeData.fee ? 'inactive' : ''">
|
||||
<span class="square" [ngStyle]="{'backgroundColor': feeData.color}"></span>
|
||||
<span class="fee-text">{{ feeData.range }}</span>
|
||||
</li>
|
||||
</li>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button (click)="invertGraph()" class="btn btn-primary btn-sm"><fa-icon [icon]="['fas', 'exchange-alt']" [rotate]="90" [fixedWidth]="true" i18n-title="statistics.component-invert.title" title="Invert"></fa-icon></button>
|
||||
<button (click)="invertGraph()" class="btn btn-primary btn-sm">
|
||||
<fa-icon [icon]="['fas', 'exchange-alt']" [rotate]="90" [fixedWidth]="true"
|
||||
i18n-title="statistics.component-invert.title" title="Invert"></fa-icon>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="spinner-border text-light bootstrap-spinner" *ngIf="spinnerLoading && mempoolStats.length"></div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="incoming-transactions-graph">
|
||||
<app-mempool-graph
|
||||
dir="ltr"
|
||||
[template]="'advanced'"
|
||||
[limitFee]="500"
|
||||
[limitFilterFee]="filterFeeIndex"
|
||||
[height]="500"
|
||||
[left]="65"
|
||||
[right]="10"
|
||||
[data]="mempoolStats && mempoolStats.length ? mempoolStats : null"
|
||||
></app-mempool-graph>
|
||||
<app-mempool-graph #mempoolgraph dir="ltr" [template]="'advanced'" [limitFee]="500"
|
||||
[limitFilterFee]="filterFeeIndex" [height]="500" [left]="65" [right]="10"
|
||||
[data]="mempoolStats && mempoolStats.length ? mempoolStats : null"></app-mempool-graph>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -78,19 +84,20 @@
|
||||
<div>
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<i class="fa fa-area-chart"></i> <span i18n="statistics.transaction-vbytes-per-second">Transaction vBytes per second (vB/s)</span>
|
||||
<i class="fa fa-area-chart"></i>
|
||||
<span i18n="statistics.transaction-vbytes-per-second">Transaction vBytes per second (vB/s)</span>
|
||||
<button class="btn" style="margin: 0 0 4px 0px" (click)="onSaveChart('incoming')">
|
||||
<fa-icon [icon]="['fas', 'download']" [fixedWidth]="true"></fa-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="incoming-transactions-graph">
|
||||
<app-incoming-transactions-graph
|
||||
[height]="500"
|
||||
[left]="65"
|
||||
[template]="'advanced'"
|
||||
[data]="mempoolTransactionsWeightPerSecondData"
|
||||
></app-incoming-transactions-graph>
|
||||
<app-incoming-transactions-graph #incominggraph [height]="500" [left]="65" [template]="'advanced'"
|
||||
[data]="mempoolTransactionsWeightPerSecondData"></app-incoming-transactions-graph>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -53,11 +53,11 @@
|
||||
}
|
||||
}
|
||||
.formRadioGroup.mining {
|
||||
@media (min-width: 1130px) {
|
||||
@media (min-width: 991px) {
|
||||
position: relative;
|
||||
top: -65px;
|
||||
}
|
||||
@media (min-width: 830px) and (max-width: 1130px) {
|
||||
@media (min-width: 830px) and (max-width: 991px) {
|
||||
position: relative;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, OnInit, LOCALE_ID, Inject } from '@angular/core';
|
||||
import { Component, OnInit, LOCALE_ID, Inject, ViewChild, ElementRef } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { FormGroup, FormBuilder } from '@angular/forms';
|
||||
import { of, merge} from 'rxjs';
|
||||
@@ -12,6 +12,8 @@ import { StateService } from 'src/app/services/state.service';
|
||||
import { SeoService } from 'src/app/services/seo.service';
|
||||
import { StorageService } from 'src/app/services/storage.service';
|
||||
import { feeLevels, chartColors } from 'src/app/app.constants';
|
||||
import { MempoolGraphComponent } from '../mempool-graph/mempool-graph.component';
|
||||
import { IncomingTransactionsGraphComponent } from '../incoming-transactions-graph/incoming-transactions-graph.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-statistics',
|
||||
@@ -19,6 +21,9 @@ import { feeLevels, chartColors } from 'src/app/app.constants';
|
||||
styleUrls: ['./statistics.component.scss']
|
||||
})
|
||||
export class StatisticsComponent implements OnInit {
|
||||
@ViewChild('mempoolgraph') mempoolGraph: MempoolGraphComponent;
|
||||
@ViewChild('incominggraph') incomingGraph: IncomingTransactionsGraphComponent;
|
||||
|
||||
network = '';
|
||||
|
||||
loading = true;
|
||||
@@ -38,6 +43,7 @@ export class StatisticsComponent implements OnInit {
|
||||
graphWindowPreference: string;
|
||||
inverted: boolean;
|
||||
feeLevelDropdownData = [];
|
||||
timespan = '';
|
||||
|
||||
constructor(
|
||||
@Inject(LOCALE_ID) private locale: string,
|
||||
@@ -75,6 +81,7 @@ export class StatisticsComponent implements OnInit {
|
||||
)
|
||||
.pipe(
|
||||
switchMap(() => {
|
||||
this.timespan = this.radioGroupForm.controls.dateSpan.value;
|
||||
this.spinnerLoading = true;
|
||||
if (this.radioGroupForm.controls.dateSpan.value === '2h') {
|
||||
this.websocketService.want(['blocks', 'live-2h-chart']);
|
||||
@@ -195,4 +202,12 @@ export class StatisticsComponent implements OnInit {
|
||||
stat.vbytes_per_second = Math.min(median * capRatio, stat.vbytes_per_second);
|
||||
}
|
||||
}
|
||||
|
||||
onSaveChart(name) {
|
||||
if (name === 'mempool') {
|
||||
this.mempoolGraph.onSaveChart(this.timespan);
|
||||
} else if (name === 'incoming') {
|
||||
this.incomingGraph.onSaveChart(this.timespan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4583,5 +4583,13 @@ export const faqData = [
|
||||
fragment: "install-mempool-with-docker",
|
||||
title: "Can I install Mempool using Docker?",
|
||||
answer: "Yes, we publish Docker images (or you can build your own). Check out <a href='https://github.com/mempool/mempool/tree/master/docker' target='_blank'>the documentation</a> for details."
|
||||
},
|
||||
{
|
||||
type: "endpoint",
|
||||
category: "advanced",
|
||||
showConditions: bitcoinNetworks,
|
||||
fragment: "address-lookup-issues",
|
||||
title: "Why do I get an error for certain address lookups on my Mempool instance?",
|
||||
answer: "<p>If you're getting errors when doing address lookups, it's probably because of your Electrum server backend.</p><p>Mempool uses an Electrum server to do address lookups. There are several implementations of the Electrum server protocol, and Mempool can use any of them, but the implementation you use affects performance:</p><ol><li><a href='https://github.com/romanz/electrs' target='_blank'>romanz/electrs</a>. This is a common choice for its low resource requirements, and most full-node distros use it. But while this implementation works great for basic queries, it will struggle with heavier ones (e.g. looking up addresses with many transactions)—especially when running on low-power hardware like a Raspberry Pi.</li><li><a href='https://github.com/cculianu/Fulcrum' target='_blank'>Fulcrum</a>. Fulcrum requires more resources than romanz/electrs but it can still run on a Raspberry Pi, and it handles heavy queries much more efficiently. If you're having issues with romanz/electrs, Fulcrum is worth a try.</li><li><a href='https://github.com/Blockstream/electrs' target='_blank'>blockstream/electrs</a>. If you have stronger hardware, consider running Blockstream's electrs implementation. It's the backend mempool.space uses, and is also what powers blockstream.info.</li></ol>"
|
||||
}
|
||||
];
|
||||
|
||||
@@ -77,3 +77,12 @@ export const formatterXAxisTimeCategory = (
|
||||
return date.toLocaleDateString(locale, { year: 'numeric', month: 'long' });
|
||||
}
|
||||
};
|
||||
|
||||
export const download = (href, name) => {
|
||||
const a = document.createElement('a');
|
||||
a.download = name;
|
||||
a.href = href;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user