Add acceleration timeline
This commit is contained in:
parent
815dcbd4ce
commit
6b481d5a07
@ -0,0 +1,78 @@
|
|||||||
|
<div class="acceleration-timeline box">
|
||||||
|
<div class="timeline-wrapper">
|
||||||
|
<div class="timeline">
|
||||||
|
<div class="intervals">
|
||||||
|
<div class="node-spacer"></div>
|
||||||
|
<div class="interval">
|
||||||
|
<div class="interval-time">
|
||||||
|
<app-time [time]="acceleratedAt - transactionTime"></app-time>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="node-spacer"></div>
|
||||||
|
<div class="interval">
|
||||||
|
<div class="interval-time">
|
||||||
|
@if (eta) {
|
||||||
|
~<app-time kind="plain" [time]="eta?.wait / 1000"></app-time>
|
||||||
|
} @else if (tx.status.block_time) {
|
||||||
|
<app-time kind="plain" [time]="tx.status.block_time - acceleratedAt"></app-time>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="node-spacer"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="nodes">
|
||||||
|
<div class="node" [id]="'first-seen'">
|
||||||
|
<div class="seen-to-acc right" [class.loading]="!tx.acceleration && !tx.status.confirmed"></div>
|
||||||
|
<a class="shape-border" [class.sent-selected]="!tx.status.confirmed && !tx.acceleration">
|
||||||
|
<div class="shape"></div>
|
||||||
|
</a>
|
||||||
|
<div class="status"><span class="badge badge-primary">Sent</span></div>
|
||||||
|
<div class="time">
|
||||||
|
<app-time *ngIf="transactionTime > 0" kind="since" [time]="transactionTime"></app-time>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="interval-spacer">
|
||||||
|
<div class="seen-to-acc" [class.loading]="!tx.acceleration && !tx.status.confirmed"></div>
|
||||||
|
</div>
|
||||||
|
<div class="node" [id]="'accelerated'">
|
||||||
|
<div class="seen-to-acc left" [class.loading]="!tx.acceleration && !tx.status.confirmed"></div>
|
||||||
|
<div class="acc-to-confirmed right" [class.loading]="tx.acceleration && !tx.status.confirmed"></div>
|
||||||
|
<a class="shape-border" [class.accelerated-selected]="tx.acceleration && !tx.status.confirmed">
|
||||||
|
<div class="shape"></div>
|
||||||
|
</a>
|
||||||
|
<div class="status" [style]="!tx.acceleration && !tx.status.confirmed ? 'opacity: 0.5' : ''"><span class="badge badge-accelerated">Accelerated</span></div>
|
||||||
|
<div class="time">
|
||||||
|
<app-time *ngIf="acceleratedAt" kind="since" [time]="acceleratedAt"></app-time>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="interval-spacer">
|
||||||
|
<div class="acc-to-confirmed" [class.loading]="tx.acceleration && !tx.status.confirmed"></div>
|
||||||
|
</div>
|
||||||
|
<div class="node" [id]="'confirmed'" [class.mined]="tx.status.confirmed">
|
||||||
|
<div class="acc-to-confirmed left" [class.loading]="tx.acceleration && !tx.status.confirmed"></div>
|
||||||
|
<a class="shape-border" [class.mined-selected]="tx.status.confirmed">
|
||||||
|
<div class="shape"></div>
|
||||||
|
</a>
|
||||||
|
<div class="status" [style]="!tx.status.confirmed ? 'opacity: 0.5' : ''"><span class="badge badge-success">Mined</span></div>
|
||||||
|
<div class="time">
|
||||||
|
@if (tx.status.block_time) {
|
||||||
|
<app-time kind="since" [time]="tx.status.block_time"></app-time>
|
||||||
|
} @else if (eta) {
|
||||||
|
<app-time kind="until" [time]="eta?.time"></app-time>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-template #nodeSpacer>
|
||||||
|
<div class="node-spacer"></div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template #intervalSpacer>
|
||||||
|
<div class="interval-spacer"></div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
</div>
|
@ -0,0 +1,197 @@
|
|||||||
|
.acceleration-timeline {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
padding: 1em 0;
|
||||||
|
|
||||||
|
&::after, &::before {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 2em;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
left: 0;
|
||||||
|
background: linear-gradient(to right, var(--box-bg), var(--box-bg), transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
right: 0;
|
||||||
|
background: linear-gradient(to left, var(--box-bg), var(--box-bg), transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-wrapper {
|
||||||
|
position: relative;
|
||||||
|
width: calc(100% - 2em);
|
||||||
|
margin: auto;
|
||||||
|
overflow-x: auto;
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
scrollbar-width: none;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.intervals, .nodes {
|
||||||
|
min-width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.node, .node-spacer {
|
||||||
|
width: 6em;
|
||||||
|
min-width: 6em;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.interval, .interval-spacer {
|
||||||
|
width: 8em;
|
||||||
|
min-width: 5em;
|
||||||
|
max-width: 8em;
|
||||||
|
height: 32px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.interval {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.interval-time {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 16px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.node, .interval-spacer {
|
||||||
|
position: relative;
|
||||||
|
.seen-to-acc {
|
||||||
|
position: absolute;
|
||||||
|
height: 10px;
|
||||||
|
left: -5px;
|
||||||
|
right: -5px;
|
||||||
|
top: 0;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
background: var(--primary);
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
&.loading {
|
||||||
|
animation: standardPulse 1s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.left {
|
||||||
|
right: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.right {
|
||||||
|
left: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.acc-to-confirmed {
|
||||||
|
position: absolute;
|
||||||
|
height: 10px;
|
||||||
|
left: -5px;
|
||||||
|
right: -5px;
|
||||||
|
top: 0;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
background: var(--tertiary);
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
&.loading {
|
||||||
|
animation: acceleratePulse 1s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.left {
|
||||||
|
right: 50%;
|
||||||
|
}
|
||||||
|
&.right {
|
||||||
|
left: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodes {
|
||||||
|
position: relative;
|
||||||
|
margin-top: 1em;
|
||||||
|
.node {
|
||||||
|
.shape-border {
|
||||||
|
display: block;
|
||||||
|
margin: auto;
|
||||||
|
height: calc(1em + 8px);
|
||||||
|
width: calc(1em + 8px);
|
||||||
|
margin-bottom: -8px;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 2px;
|
||||||
|
background: transparent;
|
||||||
|
transition: background-color 300ms, padding 300ms;
|
||||||
|
|
||||||
|
.shape {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: white;
|
||||||
|
transition: background-color 300ms, border 300ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.sent-selected {
|
||||||
|
.shape {
|
||||||
|
background: var(--primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.accelerated-selected {
|
||||||
|
.shape {
|
||||||
|
background: var(--tertiary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mined-selected {
|
||||||
|
.shape {
|
||||||
|
background: var(--success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
margin-top: -64px;
|
||||||
|
|
||||||
|
.badge.badge-accelerated {
|
||||||
|
background-color: var(--tertiary);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.time {
|
||||||
|
margin-top: 33px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 16px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes acceleratePulse {
|
||||||
|
0% { background-color: var(--tertiary) }
|
||||||
|
50% { background-color: var(--mainnet-alt) }
|
||||||
|
100% { background-color: var(--tertiary) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes standardPulse {
|
||||||
|
0% { background-color: var(--primary) }
|
||||||
|
50% { background-color: var(--secondary) }
|
||||||
|
100% { background-color: var(--primary) }
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
import { Component, Input, OnInit, OnChanges, Inject, LOCALE_ID } from '@angular/core';
|
||||||
|
import { ETA } from '../../services/eta.service';
|
||||||
|
import { Transaction } from '../../interfaces/electrs.interface';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-acceleration-timeline',
|
||||||
|
templateUrl: './acceleration-timeline.component.html',
|
||||||
|
styleUrls: ['./acceleration-timeline.component.scss'],
|
||||||
|
})
|
||||||
|
export class AccelerationTimelineComponent implements OnInit, OnChanges {
|
||||||
|
@Input() transactionTime: number;
|
||||||
|
@Input() tx: Transaction;
|
||||||
|
@Input() eta: ETA;
|
||||||
|
|
||||||
|
acceleratedAt: number;
|
||||||
|
dir: 'rtl' | 'ltr' = 'ltr';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@Inject(LOCALE_ID) private locale: string,
|
||||||
|
) {
|
||||||
|
if (this.locale.startsWith('ar') || this.locale.startsWith('fa') || this.locale.startsWith('he')) {
|
||||||
|
this.dir = 'rtl';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.acceleratedAt = this.tx.acceleratedAt ?? new Date().getTime() / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -152,6 +152,15 @@
|
|||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
<ng-container *ngIf="transactionTime && (tx.acceleration || isAcceleration)">
|
||||||
|
<div class="title float-left">
|
||||||
|
<h2 id="acceleration-timeline" i18n="transaction.acceleration-timeline|Acceleration Timeline">Acceleration Timeline</h2>
|
||||||
|
</div>
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
<app-acceleration-timeline [transactionTime]="transactionTime" [tx]="tx" [eta]="(ETA$ | async)"></app-acceleration-timeline>
|
||||||
|
<br>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngIf="rbfInfo">
|
<ng-container *ngIf="rbfInfo">
|
||||||
<div class="title float-left">
|
<div class="title float-left">
|
||||||
<h2 id="rbf" i18n="transaction.rbf-history|RBF History">RBF History</h2>
|
<h2 id="rbf" i18n="transaction.rbf-history|RBF History">RBF History</h2>
|
||||||
|
@ -66,6 +66,7 @@ import { DifficultyMiningComponent } from '../components/difficulty-mining/diffi
|
|||||||
import { BalanceWidgetComponent } from '../components/balance-widget/balance-widget.component';
|
import { BalanceWidgetComponent } from '../components/balance-widget/balance-widget.component';
|
||||||
import { AddressTransactionsWidgetComponent } from '../components/address-transactions-widget/address-transactions-widget.component';
|
import { AddressTransactionsWidgetComponent } from '../components/address-transactions-widget/address-transactions-widget.component';
|
||||||
import { RbfTimelineComponent } from '../components/rbf-timeline/rbf-timeline.component';
|
import { RbfTimelineComponent } from '../components/rbf-timeline/rbf-timeline.component';
|
||||||
|
import { AccelerationTimelineComponent } from '../components/acceleration-timeline/acceleration-timeline.component';
|
||||||
import { RbfTimelineTooltipComponent } from '../components/rbf-timeline/rbf-timeline-tooltip.component';
|
import { RbfTimelineTooltipComponent } from '../components/rbf-timeline/rbf-timeline-tooltip.component';
|
||||||
import { PushTransactionComponent } from '../components/push-transaction/push-transaction.component';
|
import { PushTransactionComponent } from '../components/push-transaction/push-transaction.component';
|
||||||
import { TestTransactionsComponent } from '../components/test-transactions/test-transactions.component';
|
import { TestTransactionsComponent } from '../components/test-transactions/test-transactions.component';
|
||||||
@ -177,6 +178,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir
|
|||||||
BalanceWidgetComponent,
|
BalanceWidgetComponent,
|
||||||
AddressTransactionsWidgetComponent,
|
AddressTransactionsWidgetComponent,
|
||||||
RbfTimelineComponent,
|
RbfTimelineComponent,
|
||||||
|
AccelerationTimelineComponent,
|
||||||
RbfTimelineTooltipComponent,
|
RbfTimelineTooltipComponent,
|
||||||
PushTransactionComponent,
|
PushTransactionComponent,
|
||||||
TestTransactionsComponent,
|
TestTransactionsComponent,
|
||||||
@ -316,6 +318,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir
|
|||||||
BalanceWidgetComponent,
|
BalanceWidgetComponent,
|
||||||
AddressTransactionsWidgetComponent,
|
AddressTransactionsWidgetComponent,
|
||||||
RbfTimelineComponent,
|
RbfTimelineComponent,
|
||||||
|
AccelerationTimelineComponent,
|
||||||
RbfTimelineTooltipComponent,
|
RbfTimelineTooltipComponent,
|
||||||
PushTransactionComponent,
|
PushTransactionComponent,
|
||||||
TestTransactionsComponent,
|
TestTransactionsComponent,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user