mirror of
https://github.com/mempool/mempool.git
synced 2025-02-24 22:58:30 +01:00
Merge pull request #2899 from mempool/mononaut/audit-toggle
toggle to enable/disable block audits
This commit is contained in:
commit
dd3d047aa8
7 changed files with 88 additions and 39 deletions
|
@ -28,8 +28,6 @@
|
||||||
|
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="box" *ngIf="!error">
|
<div class="box" *ngIf="!error">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm">
|
<div class="col-sm">
|
||||||
|
@ -54,7 +52,7 @@
|
||||||
<td i18n="block.weight">Weight</td>
|
<td i18n="block.weight">Weight</td>
|
||||||
<td [innerHTML]="'‎' + (block.weight | wuBytes: 2)"></td>
|
<td [innerHTML]="'‎' + (block.weight | wuBytes: 2)"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr *ngIf="!auditDataMissing && indexingAvailable">
|
<tr *ngIf="auditAvailable">
|
||||||
<td i18n="block.health">Block health</td>
|
<td i18n="block.health">Block health</td>
|
||||||
<td>
|
<td>
|
||||||
<span
|
<span
|
||||||
|
@ -88,21 +86,21 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="2"><span class="skeleton-loader"></span></td>
|
<td colspan="2"><span class="skeleton-loader"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr *ngIf="!auditDataMissing && indexingAvailable">
|
<tr *ngIf="showAudit">
|
||||||
<td colspan="2"><span class="skeleton-loader"></span></td>
|
<td colspan="2"><span class="skeleton-loader"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-container *ngIf="isMobile || (webGlEnabled && (auditDataMissing || !indexingAvailable)); then restOfTable;"></ng-container>
|
<ng-container *ngIf="isMobile || (webGlEnabled && !showAudit); then restOfTable;"></ng-container>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm">
|
<div class="col-sm">
|
||||||
<table class="table table-borderless table-striped" *ngIf="!isMobile && !(webGlEnabled && (auditDataMissing || !indexingAvailable))">
|
<table class="table table-borderless table-striped" *ngIf="!isMobile && !(webGlEnabled && !showAudit)">
|
||||||
<tbody>
|
<tbody>
|
||||||
<ng-container *ngTemplateOutlet="restOfTable"></ng-container>
|
<ng-container *ngTemplateOutlet="restOfTable"></ng-container>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="col-sm chart-container" *ngIf="webGlEnabled && (!indexingAvailable || auditDataMissing)">
|
<div class="col-sm chart-container" *ngIf="webGlEnabled && !showAudit">
|
||||||
<app-block-overview-graph
|
<app-block-overview-graph
|
||||||
#blockGraphActual
|
#blockGraphActual
|
||||||
[isLoading]="isLoadingOverview"
|
[isLoading]="isLoadingOverview"
|
||||||
|
@ -199,13 +197,14 @@
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-container *ngIf="showAudit">
|
||||||
<span id="overview"></span>
|
<span id="overview"></span>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<!-- VISUALIZATIONS -->
|
<!-- VISUALIZATIONS -->
|
||||||
<div class="box" *ngIf="!error && webGlEnabled && indexingAvailable && !auditDataMissing">
|
<div class="box" *ngIf="!error && webGlEnabled && showAudit">
|
||||||
<div class="nav nav-tabs" *ngIf="isMobile && auditEnabled">
|
<div class="nav nav-tabs" *ngIf="isMobile && showAudit">
|
||||||
<a class="nav-link" [class.active]="mode === 'projected'" i18n="block.projected"
|
<a class="nav-link" [class.active]="mode === 'projected'" i18n="block.projected"
|
||||||
fragment="projected" (click)="changeMode('projected')">Projected</a>
|
fragment="projected" (click)="changeMode('projected')">Projected</a>
|
||||||
<a class="nav-link" [class.active]="mode === 'actual'" i18n="block.actual"
|
<a class="nav-link" [class.active]="mode === 'actual'" i18n="block.actual"
|
||||||
|
@ -217,7 +216,7 @@
|
||||||
<div class="block-graph-wrapper">
|
<div class="block-graph-wrapper">
|
||||||
<app-block-overview-graph #blockGraphProjected [isLoading]="isLoadingOverview" [resolution]="75"
|
<app-block-overview-graph #blockGraphProjected [isLoading]="isLoadingOverview" [resolution]="75"
|
||||||
[blockLimit]="stateService.blockVSize" [orientation]="'top'" [flip]="false" [mirrorTxid]="hoverTx"
|
[blockLimit]="stateService.blockVSize" [orientation]="'top'" [flip]="false" [mirrorTxid]="hoverTx"
|
||||||
(txClickEvent)="onTxClick($event)" (txHoverEvent)="onTxHover($event)" [unavailable]="!isMobile && !auditEnabled"></app-block-overview-graph>
|
(txClickEvent)="onTxClick($event)" (txHoverEvent)="onTxHover($event)" [unavailable]="!isMobile && !showAudit"></app-block-overview-graph>
|
||||||
<ng-container *ngIf="!isMobile || mode !== 'actual'; else emptyBlockInfo"></ng-container>
|
<ng-container *ngIf="!isMobile || mode !== 'actual'; else emptyBlockInfo"></ng-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -226,7 +225,7 @@
|
||||||
<div class="block-graph-wrapper">
|
<div class="block-graph-wrapper">
|
||||||
<app-block-overview-graph #blockGraphActual [isLoading]="isLoadingOverview" [resolution]="75"
|
<app-block-overview-graph #blockGraphActual [isLoading]="isLoadingOverview" [resolution]="75"
|
||||||
[blockLimit]="stateService.blockVSize" [orientation]="'top'" [flip]="false" [mirrorTxid]="hoverTx" mode="mined"
|
[blockLimit]="stateService.blockVSize" [orientation]="'top'" [flip]="false" [mirrorTxid]="hoverTx" mode="mined"
|
||||||
(txClickEvent)="onTxClick($event)" (txHoverEvent)="onTxHover($event)" [unavailable]="isMobile && !auditEnabled"></app-block-overview-graph>
|
(txClickEvent)="onTxClick($event)" (txHoverEvent)="onTxHover($event)" [unavailable]="isMobile && !showAudit"></app-block-overview-graph>
|
||||||
<ng-container *ngTemplateOutlet="emptyBlockInfo"></ng-container>
|
<ng-container *ngTemplateOutlet="emptyBlockInfo"></ng-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -280,8 +279,22 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-right mt-3">
|
<div class="text-right mt-3 toggle-btns">
|
||||||
<button type="button" class="btn btn-outline-info btn-sm btn-details" (click)="toggleShowDetails()" i18n="transaction.details|Transaction Details">Details</button>
|
<button
|
||||||
|
*ngIf="webGlEnabled && auditAvailable"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-outline-info btn-sm btn-audit"
|
||||||
|
[class.active]="auditModeEnabled"
|
||||||
|
(click)="toggleAuditMode()"
|
||||||
|
i18n="block.toggle-audit|Toggle Audit"
|
||||||
|
>Audit</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-outline-info btn-sm btn-details"
|
||||||
|
[class.active]="showDetails"
|
||||||
|
(click)="toggleShowDetails()"
|
||||||
|
i18n="transaction.details|Transaction Details"
|
||||||
|
>Details</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div #blockTxTitle id="block-tx-title" class="block-tx-title">
|
<div #blockTxTitle id="block-tx-title" class="block-tx-title">
|
||||||
|
|
|
@ -75,14 +75,19 @@ h1 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-details {
|
.toggle-btns {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
top: 7px;
|
top: 7px;
|
||||||
@media (min-width: 550px) {
|
@media (min-width: 550px) {
|
||||||
top: 0px;
|
top: 0px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-audit {
|
||||||
|
margin-right: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
.block-tx-title {
|
.block-tx-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
|
@ -58,8 +58,9 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||||
overviewError: any = null;
|
overviewError: any = null;
|
||||||
webGlEnabled = true;
|
webGlEnabled = true;
|
||||||
indexingAvailable = false;
|
indexingAvailable = false;
|
||||||
auditEnabled = true;
|
auditModeEnabled: boolean = !this.stateService.hideAudit.value;
|
||||||
auditDataMissing: boolean;
|
auditAvailable = true;
|
||||||
|
showAudit: boolean;
|
||||||
isMobile = window.innerWidth <= 767.98;
|
isMobile = window.innerWidth <= 767.98;
|
||||||
hoverTx: string;
|
hoverTx: string;
|
||||||
numMissing: number = 0;
|
numMissing: number = 0;
|
||||||
|
@ -79,6 +80,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||||
timeLtrSubscription: Subscription;
|
timeLtrSubscription: Subscription;
|
||||||
timeLtr: boolean;
|
timeLtr: boolean;
|
||||||
childChangeSubscription: Subscription;
|
childChangeSubscription: Subscription;
|
||||||
|
auditPrefSubscription: Subscription;
|
||||||
|
|
||||||
@ViewChildren('blockGraphProjected') blockGraphProjected: QueryList<BlockOverviewGraphComponent>;
|
@ViewChildren('blockGraphProjected') blockGraphProjected: QueryList<BlockOverviewGraphComponent>;
|
||||||
@ViewChildren('blockGraphActual') blockGraphActual: QueryList<BlockOverviewGraphComponent>;
|
@ViewChildren('blockGraphActual') blockGraphActual: QueryList<BlockOverviewGraphComponent>;
|
||||||
|
@ -108,7 +110,12 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||||
});
|
});
|
||||||
|
|
||||||
this.indexingAvailable = (this.stateService.env.BASE_MODULE === 'mempool' && this.stateService.env.MINING_DASHBOARD === true);
|
this.indexingAvailable = (this.stateService.env.BASE_MODULE === 'mempool' && this.stateService.env.MINING_DASHBOARD === true);
|
||||||
this.auditEnabled = this.indexingAvailable;
|
this.setAuditAvailable(this.indexingAvailable);
|
||||||
|
|
||||||
|
this.auditPrefSubscription = this.stateService.hideAudit.subscribe((hide) => {
|
||||||
|
this.auditModeEnabled = !hide;
|
||||||
|
this.showAudit = this.auditAvailable && this.auditModeEnabled;
|
||||||
|
});
|
||||||
|
|
||||||
this.txsLoadingStatus$ = this.route.paramMap
|
this.txsLoadingStatus$ = this.route.paramMap
|
||||||
.pipe(
|
.pipe(
|
||||||
|
@ -138,11 +145,11 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||||
this.page = 1;
|
this.page = 1;
|
||||||
this.error = undefined;
|
this.error = undefined;
|
||||||
this.fees = undefined;
|
this.fees = undefined;
|
||||||
this.auditDataMissing = false;
|
this.stateService.markBlock$.next({});
|
||||||
|
|
||||||
if (history.state.data && history.state.data.blockHeight) {
|
if (history.state.data && history.state.data.blockHeight) {
|
||||||
this.blockHeight = history.state.data.blockHeight;
|
this.blockHeight = history.state.data.blockHeight;
|
||||||
this.updateAuditDataMissingFromBlockHeight(this.blockHeight);
|
this.updateAuditAvailableFromBlockHeight(this.blockHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
let isBlockHeight = false;
|
let isBlockHeight = false;
|
||||||
|
@ -155,7 +162,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
if (history.state.data && history.state.data.block) {
|
if (history.state.data && history.state.data.block) {
|
||||||
this.blockHeight = history.state.data.block.height;
|
this.blockHeight = history.state.data.block.height;
|
||||||
this.updateAuditDataMissingFromBlockHeight(this.blockHeight);
|
this.updateAuditAvailableFromBlockHeight(this.blockHeight);
|
||||||
return of(history.state.data.block);
|
return of(history.state.data.block);
|
||||||
} else {
|
} else {
|
||||||
this.isLoadingBlock = true;
|
this.isLoadingBlock = true;
|
||||||
|
@ -217,7 +224,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||||
this.apiService.getBlockAudit$(block.previousblockhash);
|
this.apiService.getBlockAudit$(block.previousblockhash);
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
this.updateAuditDataMissingFromBlockHeight(block.height);
|
this.updateAuditAvailableFromBlockHeight(block.height);
|
||||||
this.block = block;
|
this.block = block;
|
||||||
this.blockHeight = block.height;
|
this.blockHeight = block.height;
|
||||||
this.lastBlockHeight = this.blockHeight;
|
this.lastBlockHeight = this.blockHeight;
|
||||||
|
@ -369,10 +376,9 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||||
for (const tx of blockAudit.transactions) {
|
for (const tx of blockAudit.transactions) {
|
||||||
inBlock[tx.txid] = true;
|
inBlock[tx.txid] = true;
|
||||||
}
|
}
|
||||||
this.auditEnabled = true;
|
this.setAuditAvailable(true);
|
||||||
} else {
|
} else {
|
||||||
this.auditEnabled = false;
|
this.setAuditAvailable(false);
|
||||||
this.auditDataMissing = true;
|
|
||||||
}
|
}
|
||||||
return blockAudit;
|
return blockAudit;
|
||||||
}),
|
}),
|
||||||
|
@ -381,6 +387,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||||
this.error = err;
|
this.error = err;
|
||||||
this.isLoadingOverview = false;
|
this.isLoadingOverview = false;
|
||||||
this.isLoadingAudit = false;
|
this.isLoadingAudit = false;
|
||||||
|
this.setAuditAvailable(false);
|
||||||
return of(null);
|
return of(null);
|
||||||
}),
|
}),
|
||||||
).subscribe((blockAudit) => {
|
).subscribe((blockAudit) => {
|
||||||
|
@ -440,6 +447,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||||
this.networkChangedSubscription.unsubscribe();
|
this.networkChangedSubscription.unsubscribe();
|
||||||
this.queryParamsSubscription.unsubscribe();
|
this.queryParamsSubscription.unsubscribe();
|
||||||
this.timeLtrSubscription.unsubscribe();
|
this.timeLtrSubscription.unsubscribe();
|
||||||
|
this.auditSubscription.unsubscribe();
|
||||||
this.unsubscribeNextBlockSubscriptions();
|
this.unsubscribeNextBlockSubscriptions();
|
||||||
this.childChangeSubscription.unsubscribe();
|
this.childChangeSubscription.unsubscribe();
|
||||||
}
|
}
|
||||||
|
@ -595,21 +603,30 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateAuditDataMissingFromBlockHeight(blockHeight: number): void {
|
setAuditAvailable(available: boolean): void {
|
||||||
|
this.auditAvailable = available;
|
||||||
|
this.showAudit = this.auditAvailable && this.auditModeEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleAuditMode(): void {
|
||||||
|
this.stateService.hideAudit.next(this.auditModeEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAuditAvailableFromBlockHeight(blockHeight: number): void {
|
||||||
switch (this.stateService.network) {
|
switch (this.stateService.network) {
|
||||||
case 'testnet':
|
case 'testnet':
|
||||||
if (blockHeight < this.stateService.env.TESTNET_BLOCK_AUDIT_START_HEIGHT) {
|
if (blockHeight < this.stateService.env.TESTNET_BLOCK_AUDIT_START_HEIGHT) {
|
||||||
this.auditDataMissing = true;
|
this.setAuditAvailable(true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'signet':
|
case 'signet':
|
||||||
if (blockHeight < this.stateService.env.SIGNET_BLOCK_AUDIT_START_HEIGHT) {
|
if (blockHeight < this.stateService.env.SIGNET_BLOCK_AUDIT_START_HEIGHT) {
|
||||||
this.auditDataMissing = true;
|
this.setAuditAvailable(true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (blockHeight < this.stateService.env.MAINNET_BLOCK_AUDIT_START_HEIGHT) {
|
if (blockHeight < this.stateService.env.MAINNET_BLOCK_AUDIT_START_HEIGHT) {
|
||||||
this.auditDataMissing = true;
|
this.setAuditAvailable(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,6 +118,7 @@ export class StateService {
|
||||||
blockScrolling$: Subject<boolean> = new Subject<boolean>();
|
blockScrolling$: Subject<boolean> = new Subject<boolean>();
|
||||||
timeLtr: BehaviorSubject<boolean>;
|
timeLtr: BehaviorSubject<boolean>;
|
||||||
hideFlow: BehaviorSubject<boolean>;
|
hideFlow: BehaviorSubject<boolean>;
|
||||||
|
hideAudit: BehaviorSubject<boolean>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(PLATFORM_ID) private platformId: any,
|
@Inject(PLATFORM_ID) private platformId: any,
|
||||||
|
@ -177,6 +178,12 @@ export class StateService {
|
||||||
this.storageService.removeItem('flow-preference');
|
this.storageService.removeItem('flow-preference');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const savedAuditPreference = this.storageService.getValue('audit-preference');
|
||||||
|
this.hideAudit = new BehaviorSubject<boolean>(savedAuditPreference === 'hide');
|
||||||
|
this.hideAudit.subscribe((hide) => {
|
||||||
|
this.storageService.setValue('audit-preference', hide ? 'hide' : 'show');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setNetworkBasedonUrl(url: string) {
|
setNetworkBasedonUrl(url: string) {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<span style="margin-bottom: 0.5rem">{{ textLeft }}</span>
|
<span>{{ textLeft }}</span>
|
||||||
<label class="switch">
|
<label class="switch" style="margin-bottom: 0;">
|
||||||
<input type="checkbox" [checked]="checked" (change)="onToggleStatusChanged($event)">
|
<input type="checkbox" [checked]="checked" (change)="onToggleStatusChanged($event)">
|
||||||
<span class="slider round"></span>
|
<span class="slider round" [class.animate]="animate"></span>
|
||||||
</label>
|
</label>
|
||||||
<span style="margin-bottom: 0.5rem">{{ textRight }}</span>
|
<span>{{ textRight }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -22,8 +22,6 @@
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background-color: #ccc;
|
background-color: #ccc;
|
||||||
-webkit-transition: .4s;
|
|
||||||
transition: .4s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.slider:before {
|
.slider:before {
|
||||||
|
@ -34,6 +32,9 @@
|
||||||
left: 2px;
|
left: 2px;
|
||||||
bottom: 2px;
|
bottom: 2px;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider.animate, .slider.animate:before {
|
||||||
-webkit-transition: .4s;
|
-webkit-transition: .4s;
|
||||||
transition: .4s;
|
transition: .4s;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Component, Input, Output, ChangeDetectionStrategy, EventEmitter, AfterViewInit } from '@angular/core';
|
import { Component, Input, Output, ChangeDetectionStrategy, EventEmitter, AfterViewInit, ChangeDetectorRef } from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-toggle',
|
selector: 'app-toggle',
|
||||||
|
@ -11,9 +11,15 @@ export class ToggleComponent implements AfterViewInit {
|
||||||
@Input() textLeft: string;
|
@Input() textLeft: string;
|
||||||
@Input() textRight: string;
|
@Input() textRight: string;
|
||||||
@Input() checked: boolean = false;
|
@Input() checked: boolean = false;
|
||||||
|
animate: boolean = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private cd: ChangeDetectorRef,
|
||||||
|
) { }
|
||||||
|
|
||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
this.toggleStatusChanged.emit(false);
|
this.animate = true;
|
||||||
|
setTimeout(() => { this.cd.markForCheck()});
|
||||||
}
|
}
|
||||||
|
|
||||||
onToggleStatusChanged(e): void {
|
onToggleStatusChanged(e): void {
|
||||||
|
|
Loading…
Add table
Reference in a new issue