Add halving countdown widget to main dashboard

This commit is contained in:
Mononaut 2024-03-25 08:32:50 +00:00
parent 1b21cd89a3
commit 0ece8bb2a6
No known key found for this signature in database
GPG Key ID: A3F058E41374C04E
4 changed files with 112 additions and 4 deletions

View File

@ -56,7 +56,7 @@
</div> </div>
</ng-template> </ng-template>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,7 +1,15 @@
<div *ngIf="showTitle" class="main-title" i18n="dashboard.difficulty-adjustment">Difficulty Adjustment</div> <div *ngIf="showTitle && mode === 'difficulty'" class="main-title" i18n="dashboard.difficulty-adjustment">Difficulty Adjustment</div>
<div *ngIf="showTitle && mode === 'halving'" class="main-title" i18n="dashboard.halving-countdown">Halving Countdown</div>
<div class="card-wrapper"> <div class="card-wrapper">
<div class="card"> <div class="card">
<div class="card-body more-padding"> <div class="widget-toggler">
<a href="" (click)="setMode('difficulty')" class="toggler-option"
[ngClass]="{'inactive': mode === 'difficulty'}"><small i18n="statistics.average-small">difficulty</small></a>
<span style="color: #ffffff66; font-size: 8px"> | </span>
<a href="" (click)="setMode('halving')" class="toggler-option"
[ngClass]="{'inactive': mode === 'halving'}"><small i18n="statistics.median-small">halving</small></a>
</div>
<div *ngIf="mode === 'difficulty'; else halving" class="card-body more-padding">
<div class="difficulty-adjustment-container" *ngIf="(isLoadingWebSocket$ | async) === false && (difficultyEpoch$ | async) as epochData; else loadingDifficulty"> <div class="difficulty-adjustment-container" *ngIf="(isLoadingWebSocket$ | async) === false && (difficultyEpoch$ | async) as epochData; else loadingDifficulty">
<div class="epoch-progress"> <div class="epoch-progress">
<svg #epochSvg class="epoch-blocks" height="22px" width="100%" viewBox="0 0 224 9" shape-rendering="crispEdges" preserveAspectRatio="none"> <svg #epochSvg class="epoch-blocks" height="22px" width="100%" viewBox="0 0 224 9" shape-rendering="crispEdges" preserveAspectRatio="none">
@ -76,6 +84,52 @@
</div> </div>
</div> </div>
<ng-template #halving>
<div class="card-body more-padding">
<div class="difficulty-adjustment-container halving" *ngIf="(isLoadingWebSocket$ | async) === false && (difficultyEpoch$ | async) as epochData; else loadingDifficulty">
<div class="halving-progress">
<div class="background"></div>
<div class="remaining" [style]="{ left: ((210000 - epochData.blocksUntilHalving) / 2100).toFixed(2) + '%' }"></div>
<div class="label">
{{ ((210000 - epochData.blocksUntilHalving) / 2100).toFixed(2) }}%
</div>
</div>
<div class="difficulty-stats">
<div class="item">
<div class="card-text bigger">
<app-btc [satoshis]="312500000"></app-btc>
</div>
<div class="symbol">
<span i18n="difficulty-box.new-subsidy">New subsidy</span>
</div>
</div>
<div class="item">
<div class="card-text">
{{ epochData.blocksUntilHalving | number }}
</div>
<div class="symbol">
<span *ngIf="epochData.blocksUntilHalving > 1" i18n="shared.blocks-remaining">Blocks remaining</span>
<span *ngIf="epochData.blocksUntilHalving === 1" i18n="shared.block-remaining">Block remaining</span>
</div>
</div>
<div class="item">
<div class="card-text" i18n-ngbTooltip="mining.average-fee" placement="bottom">
<span>{{ epochData.timeUntilHalving | date }}</span>
</div>
<div class="symbol" *ngIf="epochData.blocksUntilHalving === 1; else approxTime">
<app-time kind="until" [time]="epochData.adjustedTimeAvg + now" [fastRender]="false" [fixedRender]="true" [precision]="1" minUnit="minute"></app-time>
</div>
<ng-template #approxTime>
<div class="symbol">
<app-time kind="until" [time]="epochData.timeUntilHalving" [fastRender]="false" [fixedRender]="true" [precision]="0" [numUnits]="2" [units]="['year', 'day', 'hour', 'minute']"></app-time>
</div>
</ng-template>
</div>
</div>
</div>
</div>
</ng-template>
<ng-template #loadingDifficulty> <ng-template #loadingDifficulty>
<div class="epoch-progress"> <div class="epoch-progress">
<div class="skeleton-loader"></div> <div class="skeleton-loader"></div>

View File

@ -168,7 +168,7 @@
white-space: nowrap; white-space: nowrap;
} }
.epoch-progress { .epoch-progress, .halving-progress {
width: 100%; width: 100%;
height: 22px; height: 22px;
margin-bottom: 12px; margin-bottom: 12px;
@ -212,4 +212,43 @@
} }
.blocks-behind { .blocks-behind {
color: #D81B60; color: #D81B60;
}
.halving-progress {
position: relative;
.background, .remaining {
position: absolute;
top: 0;
bottom: 0;
height: 100%;
}
.background {
background: linear-gradient(to right, #105fb0, #9339f4);
left: 0;
right: 0;
}
.remaining {
background: #2d3348;
right: 0;
}
.label {
position: relative;
margin: auto;
}
}
.widget-toggler {
font-size: 12px;
position: absolute;
top: -20px;
right: 3px;
text-align: right;
}
.toggler-option {
text-decoration: none;
}
.inactive {
color: #ffffff66;
} }

View File

@ -51,6 +51,10 @@ export class DifficultyComponent implements OnInit {
isLoadingWebSocket$: Observable<boolean>; isLoadingWebSocket$: Observable<boolean>;
difficultyEpoch$: Observable<EpochProgress>; difficultyEpoch$: Observable<EpochProgress>;
mode: 'difficulty' | 'halving' = 'difficulty';
userSelectedMode: boolean = false;
now: number = Date.now();
epochStart: number; epochStart: number;
currentHeight: number; currentHeight: number;
currentIndex: number; currentIndex: number;
@ -101,6 +105,11 @@ export class DifficultyComponent implements OnInit {
const timeUntilHalving = new Date().getTime() + (blocksUntilHalving * 600000); const timeUntilHalving = new Date().getTime() + (blocksUntilHalving * 600000);
const newEpochStart = Math.floor(this.stateService.latestBlockHeight / EPOCH_BLOCK_LENGTH) * EPOCH_BLOCK_LENGTH; const newEpochStart = Math.floor(this.stateService.latestBlockHeight / EPOCH_BLOCK_LENGTH) * EPOCH_BLOCK_LENGTH;
const newExpectedHeight = Math.floor(newEpochStart + da.expectedBlocks); const newExpectedHeight = Math.floor(newEpochStart + da.expectedBlocks);
this.now = new Date().getTime();
if (blocksUntilHalving < da.remainingBlocks && !this.userSelectedMode) {
this.mode = 'halving';
}
if (newEpochStart !== this.epochStart || newExpectedHeight !== this.expectedHeight || this.currentHeight !== this.stateService.latestBlockHeight) { if (newEpochStart !== this.epochStart || newExpectedHeight !== this.expectedHeight || this.currentHeight !== this.stateService.latestBlockHeight) {
this.epochStart = newEpochStart; this.epochStart = newEpochStart;
@ -194,6 +203,12 @@ export class DifficultyComponent implements OnInit {
return shapes; return shapes;
} }
setMode(mode: 'difficulty' | 'halving'): boolean {
this.mode = mode;
this.userSelectedMode = true;
return false;
}
@HostListener('pointerdown', ['$event']) @HostListener('pointerdown', ['$event'])
onPointerDown(event): void { onPointerDown(event): void {
if (this.epochSvgElement?.nativeElement?.contains(event.target)) { if (this.epochSvgElement?.nativeElement?.contains(event.target)) {