Merge pull request #4619 from mempool/nymkappa/halving-widget

[mining] update halving widget to be more human readable
This commit is contained in:
softsimon 2024-01-28 17:31:05 +07:00 committed by GitHub
commit c7a38c78d2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 149 additions and 111 deletions

View File

@ -47,20 +47,30 @@
</div> </div>
</div> </div>
<div class="item" *ngIf="showHalving"> <div class="item" *ngIf="showHalving">
<h5 class="card-title" i18n="difficulty-box.next-halving" i18n-ngbTooltip="difficulty-box.next-halving" <h5 class="card-title" i18n="difficulty-box.next-halving">Next Halving</h5>
ngbTooltip="Next Halving" placement="bottom" #averagefee [disableTooltip]="!isEllipsisActive(averagefee)">Next Halving</h5> <div class="card-text" i18n-ngbTooltip="mining.average-fee" [ngbTooltip]="halvingBlocksLeft" [tooltipContext]="{ epochData: epochData }" placement="bottom">
<div class="card-text"> <span>{{ timeUntilHalving | date }}</span>
<ng-container *ngTemplateOutlet="epochData.blocksUntilHalving === 1 ? blocksSingular : blocksPlural; context: {$implicit: epochData.blocksUntilHalving }"></ng-container> <div class="symbol" *ngIf="blocksUntilHalving === 1; else approxTime">
<ng-template #blocksPlural let-i i18n="shared.blocks">{{ i }} <span class="shared-block">blocks</span></ng-template> <app-time kind="until" [time]="epochData.timeAvg + now" [fastRender]="false" [fixedRender]="true" [precision]="1" minUnit="minute"></app-time>
<ng-template #blocksSingular let-i i18n="shared.block">{{ i }} <span class="shared-block">block</span></ng-template> </div>
<ng-template #approxTime>
<div class="symbol">
<app-time kind="until" [time]="timeUntilHalving" [fastRender]="false" [fixedRender]="true" [precision]="0" [numUnits]="2" [units]="['year', 'day', 'hour', 'minute']"></app-time>
</div>
</ng-template>
</div>
</div> </div>
<div class="symbol"><app-time kind="until" [time]="epochData.timeUntilHalving" [fastRender]="true" [precision]="1"></app-time></div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<ng-template #halvingBlocksLeft let-epochData="epochData">
<ng-container *ngTemplateOutlet="epochData.blocksUntilHalving === 1 ? blocksSingular : blocksPlural; context: {$implicit: epochData.blocksUntilHalving }"></ng-container>
<ng-template #blocksPlural let-i i18n="shared.blocks">{{ i }} <span class="shared-block">blocks</span></ng-template>
<ng-template #blocksSingular let-i i18n="shared.block">{{ i }} <span class="shared-block">block</span></ng-template>
</ng-template>
<ng-template #loadingDifficulty> <ng-template #loadingDifficulty>
<div class="difficulty-skeleton loading-container"> <div class="difficulty-skeleton loading-container">
<div class="item"> <div class="item">

View File

@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
import { combineLatest, Observable, timer } from 'rxjs'; import { combineLatest, Observable } from 'rxjs';
import { map, switchMap } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { StateService } from '../../services/state.service'; import { StateService } from '../../services/state.service';
interface EpochProgress { interface EpochProgress {
@ -15,6 +15,7 @@ interface EpochProgress {
previousRetarget: number; previousRetarget: number;
blocksUntilHalving: number; blocksUntilHalving: number;
timeUntilHalving: number; timeUntilHalving: number;
timeAvg: number;
} }
@Component({ @Component({
@ -26,6 +27,9 @@ interface EpochProgress {
export class DifficultyMiningComponent implements OnInit { export class DifficultyMiningComponent implements OnInit {
isLoadingWebSocket$: Observable<boolean>; isLoadingWebSocket$: Observable<boolean>;
difficultyEpoch$: Observable<EpochProgress>; difficultyEpoch$: Observable<EpochProgress>;
blocksUntilHalving: number | null = null;
timeUntilHalving = 0;
now = new Date().getTime();
@Input() showProgress = true; @Input() showProgress = true;
@Input() showHalving = false; @Input() showHalving = false;
@ -64,8 +68,9 @@ export class DifficultyMiningComponent implements OnInit {
colorPreviousAdjustments = '#ffffff66'; colorPreviousAdjustments = '#ffffff66';
} }
const blocksUntilHalving = 210000 - (maxHeight % 210000); this.blocksUntilHalving = 210000 - (maxHeight % 210000);
const timeUntilHalving = new Date().getTime() + (blocksUntilHalving * 600000); this.timeUntilHalving = new Date().getTime() + (this.blocksUntilHalving * 600000);
this.now = new Date().getTime();
const data = { const data = {
base: `${da.progressPercent.toFixed(2)}%`, base: `${da.progressPercent.toFixed(2)}%`,
@ -77,8 +82,9 @@ export class DifficultyMiningComponent implements OnInit {
newDifficultyHeight: da.nextRetargetHeight, newDifficultyHeight: da.nextRetargetHeight,
estimatedRetargetDate: da.estimatedRetargetDate, estimatedRetargetDate: da.estimatedRetargetDate,
previousRetarget: da.previousRetarget, previousRetarget: da.previousRetarget,
blocksUntilHalving, blocksUntilHalving: this.blocksUntilHalving,
timeUntilHalving, timeUntilHalving: this.timeUntilHalving,
timeAvg: da.timeAvg,
}; };
return data; return data;
}) })

View File

@ -10,7 +10,6 @@ import { dates } from '../../shared/i18n/dates';
export class TimeComponent implements OnInit, OnChanges, OnDestroy { export class TimeComponent implements OnInit, OnChanges, OnDestroy {
interval: number; interval: number;
text: string; text: string;
units: string[] = ['year', 'month', 'week', 'day', 'hour', 'minute', 'second'];
precisionThresholds = { precisionThresholds = {
year: 100, year: 100,
month: 18, month: 18,
@ -29,6 +28,8 @@ export class TimeComponent implements OnInit, OnChanges, OnDestroy {
@Input() fixedRender = false; @Input() fixedRender = false;
@Input() relative = false; @Input() relative = false;
@Input() precision: number = 0; @Input() precision: number = 0;
@Input() numUnits: number = 1;
@Input() units: string[] = ['year', 'month', 'week', 'day', 'hour', 'minute', 'second'];
@Input() minUnit: 'year' | 'month' | 'week' | 'day' | 'hour' | 'minute' | 'second' = 'second'; @Input() minUnit: 'year' | 'month' | 'week' | 'day' | 'hour' | 'minute' | 'second' = 'second';
@Input() fractionDigits: number = 0; @Input() fractionDigits: number = 0;
@ -94,6 +95,8 @@ export class TimeComponent implements OnInit, OnChanges, OnDestroy {
} }
let counter: number; let counter: number;
const result = [];
let usedUnits = 0;
for (const [index, unit] of this.units.entries()) { for (const [index, unit] of this.units.entries()) {
let precisionUnit = this.units[Math.min(this.units.length - 1, index + this.precision)]; let precisionUnit = this.units[Math.min(this.units.length - 1, index + this.precision)];
counter = Math.floor(seconds / this.intervals[unit]); counter = Math.floor(seconds / this.intervals[unit]);
@ -105,107 +108,126 @@ export class TimeComponent implements OnInit, OnChanges, OnDestroy {
counter = Math.max(1, counter); counter = Math.max(1, counter);
} }
if (counter > 0) { if (counter > 0) {
let rounded = Math.round(seconds / this.intervals[precisionUnit]); let rounded;
if (this.fractionDigits) { const roundFactor = Math.pow(10,this.fractionDigits || 0);
const roundFactor = Math.pow(10,this.fractionDigits); if (this.kind === 'until' && usedUnits < this.numUnits) {
rounded = Math.floor((seconds / this.intervals[precisionUnit]) * roundFactor) / roundFactor;
} else {
rounded = Math.round((seconds / this.intervals[precisionUnit]) * roundFactor) / roundFactor; rounded = Math.round((seconds / this.intervals[precisionUnit]) * roundFactor) / roundFactor;
} }
const dateStrings = dates(rounded); if (this.kind !== 'until' || this.numUnits === 1) {
switch (this.kind) { return this.formatTime(this.kind, precisionUnit, rounded);
case 'since': } else {
if (rounded === 1) { if (!usedUnits) {
switch (precisionUnit) { // singular (1 day) result.push(this.formatTime(this.kind, precisionUnit, rounded));
case 'year': return $localize`:@@time-since:${dateStrings.i18nYear}:DATE: ago`; break; } else {
case 'month': return $localize`:@@time-since:${dateStrings.i18nMonth}:DATE: ago`; break; result.push(this.formatTime('', precisionUnit, rounded));
case 'week': return $localize`:@@time-since:${dateStrings.i18nWeek}:DATE: ago`; break; }
case 'day': return $localize`:@@time-since:${dateStrings.i18nDay}:DATE: ago`; break; seconds -= (rounded * this.intervals[precisionUnit]);
case 'hour': return $localize`:@@time-since:${dateStrings.i18nHour}:DATE: ago`; break; usedUnits++;
case 'minute': return $localize`:@@time-since:${dateStrings.i18nMinute}:DATE: ago`; break; if (usedUnits >= this.numUnits) {
case 'second': return $localize`:@@time-since:${dateStrings.i18nSecond}:DATE: ago`; break; return result.join(', ');
} }
} else {
switch (precisionUnit) { // plural (2 days)
case 'year': return $localize`:@@time-since:${dateStrings.i18nYears}:DATE: ago`; break;
case 'month': return $localize`:@@time-since:${dateStrings.i18nMonths}:DATE: ago`; break;
case 'week': return $localize`:@@time-since:${dateStrings.i18nWeeks}:DATE: ago`; break;
case 'day': return $localize`:@@time-since:${dateStrings.i18nDays}:DATE: ago`; break;
case 'hour': return $localize`:@@time-since:${dateStrings.i18nHours}:DATE: ago`; break;
case 'minute': return $localize`:@@time-since:${dateStrings.i18nMinutes}:DATE: ago`; break;
case 'second': return $localize`:@@time-since:${dateStrings.i18nSeconds}:DATE: ago`; break;
}
}
break;
case 'until':
if (rounded === 1) {
switch (precisionUnit) { // singular (In ~1 day)
case 'year': return $localize`:@@time-until:In ~${dateStrings.i18nYear}:DATE:`; break;
case 'month': return $localize`:@@time-until:In ~${dateStrings.i18nMonth}:DATE:`; break;
case 'week': return $localize`:@@time-until:In ~${dateStrings.i18nWeek}:DATE:`; break;
case 'day': return $localize`:@@time-until:In ~${dateStrings.i18nDay}:DATE:`; break;
case 'hour': return $localize`:@@time-until:In ~${dateStrings.i18nHour}:DATE:`; break;
case 'minute': return $localize`:@@time-until:In ~${dateStrings.i18nMinute}:DATE:`;
case 'second': return $localize`:@@time-until:In ~${dateStrings.i18nSecond}:DATE:`;
}
} else {
switch (precisionUnit) { // plural (In ~2 days)
case 'year': return $localize`:@@time-until:In ~${dateStrings.i18nYears}:DATE:`; break;
case 'month': return $localize`:@@time-until:In ~${dateStrings.i18nMonths}:DATE:`; break;
case 'week': return $localize`:@@time-until:In ~${dateStrings.i18nWeeks}:DATE:`; break;
case 'day': return $localize`:@@time-until:In ~${dateStrings.i18nDays}:DATE:`; break;
case 'hour': return $localize`:@@time-until:In ~${dateStrings.i18nHours}:DATE:`; break;
case 'minute': return $localize`:@@time-until:In ~${dateStrings.i18nMinutes}:DATE:`; break;
case 'second': return $localize`:@@time-until:In ~${dateStrings.i18nSeconds}:DATE:`; break;
}
}
break;
case 'span':
if (rounded === 1) {
switch (precisionUnit) { // singular (1 day)
case 'year': return $localize`:@@time-span:After ${dateStrings.i18nYear}:DATE:`; break;
case 'month': return $localize`:@@time-span:After ${dateStrings.i18nMonth}:DATE:`; break;
case 'week': return $localize`:@@time-span:After ${dateStrings.i18nWeek}:DATE:`; break;
case 'day': return $localize`:@@time-span:After ${dateStrings.i18nDay}:DATE:`; break;
case 'hour': return $localize`:@@time-span:After ${dateStrings.i18nHour}:DATE:`; break;
case 'minute': return $localize`:@@time-span:After ${dateStrings.i18nMinute}:DATE:`; break;
case 'second': return $localize`:@@time-span:After ${dateStrings.i18nSecond}:DATE:`; break;
}
} else {
switch (precisionUnit) { // plural (2 days)
case 'year': return $localize`:@@time-span:After ${dateStrings.i18nYears}:DATE:`; break;
case 'month': return $localize`:@@time-span:After ${dateStrings.i18nMonths}:DATE:`; break;
case 'week': return $localize`:@@time-span:After ${dateStrings.i18nWeeks}:DATE:`; break;
case 'day': return $localize`:@@time-span:After ${dateStrings.i18nDays}:DATE:`; break;
case 'hour': return $localize`:@@time-span:After ${dateStrings.i18nHours}:DATE:`; break;
case 'minute': return $localize`:@@time-span:After ${dateStrings.i18nMinutes}:DATE:`; break;
case 'second': return $localize`:@@time-span:After ${dateStrings.i18nSeconds}:DATE:`; break;
}
}
break;
default:
if (rounded === 1) {
switch (precisionUnit) { // singular (1 day)
case 'year': return dateStrings.i18nYear; break;
case 'month': return dateStrings.i18nMonth; break;
case 'week': return dateStrings.i18nWeek; break;
case 'day': return dateStrings.i18nDay; break;
case 'hour': return dateStrings.i18nHour; break;
case 'minute': return dateStrings.i18nMinute; break;
case 'second': return dateStrings.i18nSecond; break;
}
} else {
switch (precisionUnit) { // plural (2 days)
case 'year': return dateStrings.i18nYears; break;
case 'month': return dateStrings.i18nMonths; break;
case 'week': return dateStrings.i18nWeeks; break;
case 'day': return dateStrings.i18nDays; break;
case 'hour': return dateStrings.i18nHours; break;
case 'minute': return dateStrings.i18nMinutes; break;
case 'second': return dateStrings.i18nSeconds; break;
}
}
} }
} }
} }
return result.join(', ');
} }
private formatTime(kind, unit, number): string {
const dateStrings = dates(number);
switch (kind) {
case 'since':
if (number === 1) {
switch (unit) { // singular (1 day)
case 'year': return $localize`:@@time-since:${dateStrings.i18nYear}:DATE: ago`; break;
case 'month': return $localize`:@@time-since:${dateStrings.i18nMonth}:DATE: ago`; break;
case 'week': return $localize`:@@time-since:${dateStrings.i18nWeek}:DATE: ago`; break;
case 'day': return $localize`:@@time-since:${dateStrings.i18nDay}:DATE: ago`; break;
case 'hour': return $localize`:@@time-since:${dateStrings.i18nHour}:DATE: ago`; break;
case 'minute': return $localize`:@@time-since:${dateStrings.i18nMinute}:DATE: ago`; break;
case 'second': return $localize`:@@time-since:${dateStrings.i18nSecond}:DATE: ago`; break;
}
} else {
switch (unit) { // plural (2 days)
case 'year': return $localize`:@@time-since:${dateStrings.i18nYears}:DATE: ago`; break;
case 'month': return $localize`:@@time-since:${dateStrings.i18nMonths}:DATE: ago`; break;
case 'week': return $localize`:@@time-since:${dateStrings.i18nWeeks}:DATE: ago`; break;
case 'day': return $localize`:@@time-since:${dateStrings.i18nDays}:DATE: ago`; break;
case 'hour': return $localize`:@@time-since:${dateStrings.i18nHours}:DATE: ago`; break;
case 'minute': return $localize`:@@time-since:${dateStrings.i18nMinutes}:DATE: ago`; break;
case 'second': return $localize`:@@time-since:${dateStrings.i18nSeconds}:DATE: ago`; break;
}
}
break;
case 'until':
if (number === 1) {
switch (unit) { // singular (In ~1 day)
case 'year': return $localize`:@@time-until:In ~${dateStrings.i18nYear}:DATE:`; break;
case 'month': return $localize`:@@time-until:In ~${dateStrings.i18nMonth}:DATE:`; break;
case 'week': return $localize`:@@time-until:In ~${dateStrings.i18nWeek}:DATE:`; break;
case 'day': return $localize`:@@time-until:In ~${dateStrings.i18nDay}:DATE:`; break;
case 'hour': return $localize`:@@time-until:In ~${dateStrings.i18nHour}:DATE:`; break;
case 'minute': return $localize`:@@time-until:In ~${dateStrings.i18nMinute}:DATE:`;
case 'second': return $localize`:@@time-until:In ~${dateStrings.i18nSecond}:DATE:`;
}
} else {
switch (unit) { // plural (In ~2 days)
case 'year': return $localize`:@@time-until:In ~${dateStrings.i18nYears}:DATE:`; break;
case 'month': return $localize`:@@time-until:In ~${dateStrings.i18nMonths}:DATE:`; break;
case 'week': return $localize`:@@time-until:In ~${dateStrings.i18nWeeks}:DATE:`; break;
case 'day': return $localize`:@@time-until:In ~${dateStrings.i18nDays}:DATE:`; break;
case 'hour': return $localize`:@@time-until:In ~${dateStrings.i18nHours}:DATE:`; break;
case 'minute': return $localize`:@@time-until:In ~${dateStrings.i18nMinutes}:DATE:`; break;
case 'second': return $localize`:@@time-until:In ~${dateStrings.i18nSeconds}:DATE:`; break;
}
}
break;
case 'span':
if (number === 1) {
switch (unit) { // singular (1 day)
case 'year': return $localize`:@@time-span:After ${dateStrings.i18nYear}:DATE:`; break;
case 'month': return $localize`:@@time-span:After ${dateStrings.i18nMonth}:DATE:`; break;
case 'week': return $localize`:@@time-span:After ${dateStrings.i18nWeek}:DATE:`; break;
case 'day': return $localize`:@@time-span:After ${dateStrings.i18nDay}:DATE:`; break;
case 'hour': return $localize`:@@time-span:After ${dateStrings.i18nHour}:DATE:`; break;
case 'minute': return $localize`:@@time-span:After ${dateStrings.i18nMinute}:DATE:`; break;
case 'second': return $localize`:@@time-span:After ${dateStrings.i18nSecond}:DATE:`; break;
}
} else {
switch (unit) { // plural (2 days)
case 'year': return $localize`:@@time-span:After ${dateStrings.i18nYears}:DATE:`; break;
case 'month': return $localize`:@@time-span:After ${dateStrings.i18nMonths}:DATE:`; break;
case 'week': return $localize`:@@time-span:After ${dateStrings.i18nWeeks}:DATE:`; break;
case 'day': return $localize`:@@time-span:After ${dateStrings.i18nDays}:DATE:`; break;
case 'hour': return $localize`:@@time-span:After ${dateStrings.i18nHours}:DATE:`; break;
case 'minute': return $localize`:@@time-span:After ${dateStrings.i18nMinutes}:DATE:`; break;
case 'second': return $localize`:@@time-span:After ${dateStrings.i18nSeconds}:DATE:`; break;
}
}
break;
default:
if (number === 1) {
switch (unit) { // singular (1 day)
case 'year': return dateStrings.i18nYear; break;
case 'month': return dateStrings.i18nMonth; break;
case 'week': return dateStrings.i18nWeek; break;
case 'day': return dateStrings.i18nDay; break;
case 'hour': return dateStrings.i18nHour; break;
case 'minute': return dateStrings.i18nMinute; break;
case 'second': return dateStrings.i18nSecond; break;
}
} else {
switch (unit) { // plural (2 days)
case 'year': return dateStrings.i18nYears; break;
case 'month': return dateStrings.i18nMonths; break;
case 'week': return dateStrings.i18nWeeks; break;
case 'day': return dateStrings.i18nDays; break;
case 'hour': return dateStrings.i18nHours; break;
case 'minute': return dateStrings.i18nMinutes; break;
case 'second': return dateStrings.i18nSeconds; break;
}
}
}
}
} }