Merge pull request #2859 from mempool/mononaut/block-health-ux

display block health as badge & add loaders
This commit is contained in:
wiz 2022-12-26 20:51:47 +09:00 committed by GitHub
commit 869d7df844
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 20 deletions

View File

@ -57,8 +57,21 @@
<tr *ngIf="auditEnabled">
<td i18n="block.health">Block health</td>
<td>
<span *ngIf="blockAudit?.matchRate != null">{{ blockAudit.matchRate }}%</span>
<span *ngIf="blockAudit?.matchRate === null" i18n="unknown">Unknown</span>
<span
class="health-badge badge"
[class.badge-success]="blockAudit?.matchRate >= 99"
[class.badge-warning]="blockAudit?.matchRate >= 75 && blockAudit?.matchRate < 99"
[class.badge-danger]="blockAudit?.matchRate < 75"
*ngIf="blockAudit?.matchRate != null; else nullHealth"
>{{ blockAudit?.matchRate }}%</span>
<ng-template #nullHealth>
<ng-container *ngIf="!isLoadingAudit; else loadingHealth">
<span class="health-badge badge badge-secondary" i18n="unknown">Unknown</span>
</ng-container>
</ng-template>
<ng-template #loadingHealth>
<span class="skeleton-loader" style="max-width: 60px"></span>
</ng-template>
</td>
</tr>
<ng-container *ngIf="webGlEnabled && (auditDataMissing || !indexingAvailable)">

View File

@ -43,6 +43,7 @@ export class BlockComponent implements OnInit, OnDestroy {
strippedTransactions: TransactionStripped[];
overviewTransitionDirection: string;
isLoadingOverview = true;
isLoadingAudit = true;
error: any;
blockSubsidy: number;
fees: number;
@ -297,13 +298,18 @@ export class BlockComponent implements OnInit, OnDestroy {
this.auditSubscription = block$.pipe(
startWith(null),
pairwise(),
switchMap(([prevBlock, block]) => this.apiService.getBlockAudit$(block.id)
.pipe(
catchError((err) => {
this.overviewError = err;
return of([]);
})
)
switchMap(([prevBlock, block]) => {
this.isLoadingAudit = true;
this.blockAudit = null;
return this.apiService.getBlockAudit$(block.id)
.pipe(
catchError((err) => {
this.overviewError = err;
this.isLoadingAudit = false;
return of([]);
})
);
}
),
filter((response) => response != null),
map((response) => {
@ -375,12 +381,14 @@ export class BlockComponent implements OnInit, OnDestroy {
console.log(err);
this.error = err;
this.isLoadingOverview = false;
this.isLoadingAudit = false;
return of(null);
}),
).subscribe((blockAudit) => {
this.blockAudit = blockAudit;
this.setupBlockGraphs();
this.isLoadingOverview = false;
this.isLoadingAudit = false;
});
}

View File

@ -14,7 +14,7 @@
i18n-ngbTooltip="mining.pool-name" ngbTooltip="Pool" placement="bottom" #miningpool [disableTooltip]="!isEllipsisActive(miningpool)">Pool</th>
<th class="timestamp" i18n="latest-blocks.timestamp" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">Timestamp</th>
<th class="mined" i18n="latest-blocks.mined" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">Mined</th>
<th *ngIf="indexingAvailable" class="health text-left" i18n="latest-blocks.health" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}"
<th *ngIf="indexingAvailable" class="health text-right" i18n="latest-blocks.health" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}"
i18n-ngbTooltip="latest-blocks.health" ngbTooltip="Health" placement="bottom" #health [disableTooltip]="!isEllipsisActive(health)">Health</th>
<th *ngIf="indexingAvailable" class="reward text-right" i18n="latest-blocks.reward" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}"
i18n-ngbTooltip="latest-blocks.reward" ngbTooltip="Reward" placement="bottom" #reward [disableTooltip]="!isEllipsisActive(reward)">Reward</th>
@ -46,16 +46,22 @@
<app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since>
</td>
<td *ngIf="indexingAvailable" class="health text-right" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">
<a class="clear-link" [routerLink]="auditScores[block.id] != null ? ['/block/' | relativeUrl, block.id] : null">
<div class="progress progress-health">
<div class="progress-bar progress-bar-health" role="progressbar"
[ngStyle]="{'width': (100 - (auditScores[block.id] || 0)) + '%' }"></div>
<div class="progress-text">
<span *ngIf="auditScores[block.id] != null;">{{ auditScores[block.id] }}%</span>
<span *ngIf="auditScores[block.id] == null">~</span>
</div>
</div>
</a>
<a
class="health-badge badge"
[class.badge-success]="auditScores[block.id] >= 99"
[class.badge-warning]="auditScores[block.id] >= 75 && auditScores[block.id] < 99"
[class.badge-danger]="auditScores[block.id] < 75"
[routerLink]="auditScores[block.id] != null ? ['/block/' | relativeUrl, block.id] : null"
*ngIf="auditScores[block.id] != null; else nullHealth"
>{{ auditScores[block.id] }}%</a>
<ng-template #nullHealth>
<ng-container *ngIf="!loadingScores; else loadingHealth">
<span class="health-badge badge badge-secondary" i18n="unknown">Unknown</span>
</ng-container>
</ng-template>
<ng-template #loadingHealth>
<span class="skeleton-loader" style="max-width: 60px"></span>
</ng-template>
</td>
<td *ngIf="indexingAvailable" class="reward text-right" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">
<app-amount [satoshis]="block.extras.reward" [noFiat]="true" digitsInfo="1.2-2"></app-amount>

View File

@ -23,6 +23,7 @@ export class BlocksList implements OnInit, OnDestroy {
indexingAvailable = false;
isLoading = true;
loadingScores = true;
fromBlockHeight = undefined;
paginationMaxSize: number;
page = 1;
@ -113,6 +114,7 @@ export class BlocksList implements OnInit, OnDestroy {
if (this.indexingAvailable) {
this.auditScoreSubscription = this.fromHeightSubject.pipe(
switchMap((fromBlockHeight) => {
this.loadingScores = true;
return this.apiService.getBlockAuditScores$(this.page === 1 ? undefined : fromBlockHeight)
.pipe(
catchError(() => {
@ -124,6 +126,7 @@ export class BlocksList implements OnInit, OnDestroy {
Object.values(scores).forEach(score => {
this.auditScores[score.hash] = score?.matchRate != null ? score.matchRate : null;
});
this.loadingScores = false;
});
this.latestScoreSubscription = this.stateService.blocks$.pipe(