Merge branch 'master' into simon/block-mining-pool-logos
This commit is contained in:
		
						commit
						bf95938be8
					
				@ -1,3 +1,4 @@
 | 
				
			|||||||
 | 
					@if (tx.status.confirmed) {
 | 
				
			||||||
<div class="acceleration-timeline box">
 | 
					<div class="acceleration-timeline box">
 | 
				
			||||||
  <div class="timeline-wrapper">
 | 
					  <div class="timeline-wrapper">
 | 
				
			||||||
    <div class="timeline">
 | 
					    <div class="timeline">
 | 
				
			||||||
@ -11,68 +12,141 @@
 | 
				
			|||||||
        <div class="node-spacer"></div>
 | 
					        <div class="node-spacer"></div>
 | 
				
			||||||
        <div class="interval">
 | 
					        <div class="interval">
 | 
				
			||||||
          <div class="interval-time">
 | 
					          <div class="interval-time">
 | 
				
			||||||
            @if (eta) {
 | 
					            <app-time [time]="tx.status.block_time - acceleratedAt"></app-time>
 | 
				
			||||||
            ~<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>
 | 
					        </div>
 | 
				
			||||||
        <div class="node-spacer"></div>
 | 
					        <div class="node-spacer"></div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
      <div class="nodes">
 | 
					      <div class="nodes">
 | 
				
			||||||
        <div class="node" [id]="'first-seen'">
 | 
					        <div class="node" [id]="'first-seen'">
 | 
				
			||||||
        <div class="seen-to-acc right" [class.loading]="!tx.acceleration && !tx.status.confirmed"></div>
 | 
					          <div class="seen-to-acc right"></div>
 | 
				
			||||||
        <a class="shape-border" [class.sent-selected]="!tx.status.confirmed && !tx.acceleration">
 | 
					          <div class="shape-border">
 | 
				
			||||||
            <div class="shape"></div>
 | 
					            <div class="shape"></div>
 | 
				
			||||||
        </a>
 | 
					          </div>
 | 
				
			||||||
        <div class="status"><span class="badge badge-primary">Sent</span></div>
 | 
					          <div class="status"><span class="badge badge-primary" i18n="transaction.first-seen|Transaction first seen">First seen</span></div>
 | 
				
			||||||
          <div class="time">
 | 
					          <div class="time">
 | 
				
			||||||
            <app-time *ngIf="transactionTime > 0" kind="since" [time]="transactionTime"></app-time>
 | 
					            <app-time *ngIf="transactionTime > 0" kind="since" [time]="transactionTime"></app-time>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div class="interval-spacer">
 | 
					        <div class="interval-spacer">
 | 
				
			||||||
        <div class="seen-to-acc" [class.loading]="!tx.acceleration && !tx.status.confirmed"></div>
 | 
					          <div class="seen-to-acc"></div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div class="node" [id]="'accelerated'">
 | 
					        <div class="node" [id]="'accelerated'">
 | 
				
			||||||
        <div class="seen-to-acc left" [class.loading]="!tx.acceleration && !tx.status.confirmed"></div>
 | 
					          <div class="seen-to-acc left"></div>
 | 
				
			||||||
        <div class="acc-to-confirmed right" [class.loading]="tx.acceleration && !tx.status.confirmed"></div>
 | 
					          <div class="acc-to-confirmed right"></div>
 | 
				
			||||||
        <a class="shape-border" [class.accelerated-selected]="tx.acceleration && !tx.status.confirmed">
 | 
					          <div class="shape-border">
 | 
				
			||||||
            <div class="shape"></div>
 | 
					            <div class="shape"></div>
 | 
				
			||||||
        </a>
 | 
					          </div>
 | 
				
			||||||
        <div class="status" [style]="!tx.acceleration && !tx.status.confirmed ? 'opacity: 0.5' : ''"><span class="badge badge-accelerated">Accelerated</span></div>
 | 
					          <div class="status"><span class="badge badge-accelerated" i18n="transaction.audit.accelerated">Accelerated</span></div>
 | 
				
			||||||
          <div class="time">
 | 
					          <div class="time">
 | 
				
			||||||
            <app-time *ngIf="acceleratedAt" kind="since" [time]="acceleratedAt"></app-time>
 | 
					            <app-time *ngIf="acceleratedAt" kind="since" [time]="acceleratedAt"></app-time>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div class="interval-spacer">
 | 
					        <div class="interval-spacer">
 | 
				
			||||||
        <div class="acc-to-confirmed" [class.loading]="tx.acceleration && !tx.status.confirmed"></div>
 | 
					          <div class="acc-to-confirmed"></div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      <div class="node" [id]="'confirmed'" [class.mined]="tx.status.confirmed">
 | 
					        <div class="node mined" [id]="'confirmed'" >
 | 
				
			||||||
        <div class="acc-to-confirmed left" [class.loading]="tx.acceleration && !tx.status.confirmed"></div>
 | 
					          <div class="acc-to-confirmed left" ></div>
 | 
				
			||||||
        <a class="shape-border" [class.mined-selected]="tx.status.confirmed">
 | 
					          <div class="shape-border mined-selected">
 | 
				
			||||||
            <div class="shape"></div>
 | 
					            <div class="shape"></div>
 | 
				
			||||||
        </a>
 | 
					          </div>
 | 
				
			||||||
        <div class="status" [style]="!tx.status.confirmed ? 'opacity: 0.5' : ''"><span class="badge badge-success">Mined</span></div>
 | 
					          <div class="status"><span class="badge badge-success" i18n="transaction.rbf.mined">Mined</span></div>
 | 
				
			||||||
          <div class="time">
 | 
					          <div class="time">
 | 
				
			||||||
          @if (tx.status.block_time) {
 | 
					 | 
				
			||||||
            <app-time kind="since" [time]="tx.status.block_time"></app-time>
 | 
					            <app-time kind="since" [time]="tx.status.block_time"></app-time>
 | 
				
			||||||
          } @else if (eta) {
 | 
					          </div>
 | 
				
			||||||
            <app-time kind="until" [time]="eta?.time"></app-time>
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>  
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					} @else if (acceleratedETA) { <!-- Not yet accelerated; to be shown only in acceleration checkout -->
 | 
				
			||||||
 | 
					} @else if (standardETA) { <!-- Accelerated -->
 | 
				
			||||||
 | 
					  <div class="acceleration-timeline box">
 | 
				
			||||||
 | 
					    <div class="timeline-wrapper">
 | 
				
			||||||
 | 
					      <div class="timeline">
 | 
				
			||||||
 | 
					        <div class="intervals">
 | 
				
			||||||
 | 
					          <div class="node-spacer"></div>
 | 
				
			||||||
 | 
					          <div class="interval-spacer"></div>
 | 
				
			||||||
 | 
					          <div class="node-spacer"></div>
 | 
				
			||||||
 | 
					          <div class="interval">
 | 
				
			||||||
 | 
					            <div class="interval-time">
 | 
				
			||||||
 | 
					              @if (eta) {
 | 
				
			||||||
 | 
					                ~<app-time [time]="eta?.wait / 1000"></app-time> <span *ngIf="accelerateRatio > 1" class="compare"> ({{ accelerateRatio }}x faster)</span>
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
  </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  <ng-template #nodeSpacer>
 | 
					 | 
				
			||||||
          <div class="node-spacer"></div>
 | 
					          <div class="node-spacer"></div>
 | 
				
			||||||
  </ng-template>
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="nodes">
 | 
				
			||||||
  <ng-template #intervalSpacer>
 | 
					          <div class="node-spacer"></div>
 | 
				
			||||||
          <div class="interval-spacer"></div>
 | 
					          <div class="interval-spacer"></div>
 | 
				
			||||||
  </ng-template>
 | 
					          <div class="node">
 | 
				
			||||||
 | 
					            <div class="acc-to-confirmed loading right"></div>
 | 
				
			||||||
</div>
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div class="interval-spacer">
 | 
				
			||||||
 | 
					            <div class="acc-to-confirmed loading"></div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div class="node" [id]="'confirmed'">
 | 
				
			||||||
 | 
					            <div class="acc-to-confirmed loading left"></div>
 | 
				
			||||||
 | 
					            <div class="shape-border waiting">
 | 
				
			||||||
 | 
					              <div class="shape animate"></div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="status"><span class="badge badge-waiting" i18n="transaction.rbf.mined">Mined</span></div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <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">
 | 
				
			||||||
 | 
					                ~<app-time [time]="standardETA / 1000 - now"></app-time>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div class="node-spacer"></div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="nodes">
 | 
				
			||||||
 | 
					          <div class="node" [id]="'first-seen'">
 | 
				
			||||||
 | 
					            <div class="seen-to-acc right"></div>
 | 
				
			||||||
 | 
					            <div class="shape-border">
 | 
				
			||||||
 | 
					              <div class="shape"></div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="status"><span class="badge badge-primary" i18n="transaction.first-seen|Transaction first seen">First seen</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"></div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div class="node" [id]="'accelerated'">
 | 
				
			||||||
 | 
					            <div class="seen-to-acc left"></div>
 | 
				
			||||||
 | 
					            <div class="seen-to-acc right"></div>
 | 
				
			||||||
 | 
					            <div  class="shape-border accelerated-selected">
 | 
				
			||||||
 | 
					              <div class="shape accelerating"></div>
 | 
				
			||||||
 | 
					              <div class="connector down loading"></div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="time" style="margin-top: 3px;">
 | 
				
			||||||
 | 
					              <span i18n="transaction.audit.accelerated">Accelerated</span> <app-time *ngIf="acceleratedAt" kind="since" [time]="acceleratedAt"></app-time>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div class="interval-spacer">
 | 
				
			||||||
 | 
					            <div class="seen-to-acc"></div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div class="node" [id]="'confirmed'">
 | 
				
			||||||
 | 
					            <div class="seen-to-acc left"></div>
 | 
				
			||||||
 | 
					            <div class="shape-border waiting">
 | 
				
			||||||
 | 
					              <div class="shape"></div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
.acceleration-timeline {
 | 
					.acceleration-timeline {
 | 
				
			||||||
  position: relative;
 | 
					  position: relative;
 | 
				
			||||||
  width: 100%;
 | 
					  width: 100%;
 | 
				
			||||||
  padding: 1em 0;
 | 
					  padding: 0.5em 0 1em;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &::after, &::before {
 | 
					  &::after, &::before {
 | 
				
			||||||
    content: '';
 | 
					    content: '';
 | 
				
			||||||
@ -69,6 +69,15 @@
 | 
				
			|||||||
      font-size: 12px;
 | 
					      font-size: 12px;
 | 
				
			||||||
      line-height: 16px;
 | 
					      line-height: 16px;
 | 
				
			||||||
      white-space: nowrap;
 | 
					      white-space: nowrap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      .compare {
 | 
				
			||||||
 | 
					        font-style: italic;
 | 
				
			||||||
 | 
					        color: var(--mainnet-alt);
 | 
				
			||||||
 | 
					        font-weight: 600;
 | 
				
			||||||
 | 
					        @media (max-width: 600px) {
 | 
				
			||||||
 | 
					          display: none;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -84,10 +93,6 @@
 | 
				
			|||||||
      background: var(--primary);
 | 
					      background: var(--primary);
 | 
				
			||||||
      border-radius: 5px;
 | 
					      border-radius: 5px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      &.loading {
 | 
					 | 
				
			||||||
        animation: standardPulse 1s infinite;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      &.left {
 | 
					      &.left {
 | 
				
			||||||
        right: 50%;
 | 
					        right: 50%;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@ -119,6 +124,27 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .connector {
 | 
				
			||||||
 | 
					      position: absolute;
 | 
				
			||||||
 | 
					      height: 88px;
 | 
				
			||||||
 | 
					      width: 10px;
 | 
				
			||||||
 | 
					      left: -5px;
 | 
				
			||||||
 | 
					      top: -73px;
 | 
				
			||||||
 | 
					      transform: translateX(120%);
 | 
				
			||||||
 | 
					      background: var(--tertiary);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      &.down {
 | 
				
			||||||
 | 
					        border-top-left-radius: 10px; 
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      &.up {
 | 
				
			||||||
 | 
					        border-top-right-radius: 10px; 
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      &.loading {
 | 
				
			||||||
 | 
					        animation: acceleratePulse 1s infinite;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  .nodes {
 | 
					  .nodes {
 | 
				
			||||||
@ -134,20 +160,20 @@
 | 
				
			|||||||
        transform: translateY(-50%);
 | 
					        transform: translateY(-50%);
 | 
				
			||||||
        border-radius: 50%;
 | 
					        border-radius: 50%;
 | 
				
			||||||
        padding: 2px;
 | 
					        padding: 2px;
 | 
				
			||||||
        background: transparent;
 | 
					 | 
				
			||||||
        transition: background-color 300ms, padding 300ms;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        .shape {
 | 
					        .shape {
 | 
				
			||||||
          width: 100%;
 | 
					          width: 100%;
 | 
				
			||||||
          height: 100%;
 | 
					          height: 100%;
 | 
				
			||||||
          border-radius: 50%;
 | 
					          border-radius: 50%;
 | 
				
			||||||
          background: white;
 | 
					          background: white;
 | 
				
			||||||
          transition: background-color 300ms, border 300ms;
 | 
					          &.accelerating {
 | 
				
			||||||
 | 
					            animation: acceleratePulse 1s infinite;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        &.sent-selected {
 | 
					        &.waiting {
 | 
				
			||||||
          .shape {
 | 
					          .shape {
 | 
				
			||||||
            background: var(--primary);
 | 
					            background: var(--grey);
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -167,6 +193,12 @@
 | 
				
			|||||||
      .status {
 | 
					      .status {
 | 
				
			||||||
        margin-top: -64px;
 | 
					        margin-top: -64px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .badge.badge-waiting {
 | 
				
			||||||
 | 
					          opacity: 0.5;
 | 
				
			||||||
 | 
					          background-color: var(--grey);
 | 
				
			||||||
 | 
					          color: white;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        .badge.badge-accelerated {
 | 
					        .badge.badge-accelerated {
 | 
				
			||||||
          background-color: var(--tertiary);
 | 
					          background-color: var(--tertiary);
 | 
				
			||||||
          color: white;
 | 
					          color: white;
 | 
				
			||||||
@ -188,10 +220,3 @@
 | 
				
			|||||||
  50% { background-color: var(--mainnet-alt) }
 | 
					  50% { background-color: var(--mainnet-alt) }
 | 
				
			||||||
  100% { background-color: var(--tertiary) }
 | 
					  100% { background-color: var(--tertiary) }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
@keyframes standardPulse {
 | 
					 | 
				
			||||||
  0% { background-color: var(--primary) }
 | 
					 | 
				
			||||||
  50% { background-color: var(--secondary) }
 | 
					 | 
				
			||||||
  100% { background-color: var(--primary) }
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { Component, Input, OnInit, OnChanges, Inject, LOCALE_ID } from '@angular/core';
 | 
					import { Component, Input, OnInit, OnChanges } from '@angular/core';
 | 
				
			||||||
import { ETA } from '../../services/eta.service';
 | 
					import { ETA } from '../../services/eta.service';
 | 
				
			||||||
import { Transaction } from '../../interfaces/electrs.interface';
 | 
					import { Transaction } from '../../interfaces/electrs.interface';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -11,23 +11,31 @@ export class AccelerationTimelineComponent implements OnInit, OnChanges {
 | 
				
			|||||||
  @Input() transactionTime: number;
 | 
					  @Input() transactionTime: number;
 | 
				
			||||||
  @Input() tx: Transaction;
 | 
					  @Input() tx: Transaction;
 | 
				
			||||||
  @Input() eta: ETA;
 | 
					  @Input() eta: ETA;
 | 
				
			||||||
 | 
					  // A mined transaction has standard ETA and accelerated ETA undefined
 | 
				
			||||||
 | 
					  // A transaction in mempool has either standardETA defined (if accelerated) or acceleratedETA defined (if not accelerated yet)
 | 
				
			||||||
 | 
					  @Input() standardETA: number;
 | 
				
			||||||
 | 
					  @Input() acceleratedETA: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  acceleratedAt: number;
 | 
					  acceleratedAt: number;
 | 
				
			||||||
  dir: 'rtl' | 'ltr' = 'ltr';
 | 
					  now: number;
 | 
				
			||||||
 | 
					  accelerateRatio: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(
 | 
					  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 {
 | 
					  ngOnInit(): void {
 | 
				
			||||||
    this.acceleratedAt = this.tx.acceleratedAt ?? new Date().getTime() / 1000;
 | 
					    this.acceleratedAt = this.tx.acceleratedAt ?? new Date().getTime() / 1000;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnChanges(changes): void {
 | 
					  ngOnChanges(changes): void {
 | 
				
			||||||
 | 
					    this.now = Math.floor(new Date().getTime() / 1000);
 | 
				
			||||||
 | 
					    if (changes?.eta?.currentValue || changes?.standardETA?.currentValue || changes?.acceleratedETA?.currentValue) {
 | 
				
			||||||
 | 
					      if (changes?.eta?.currentValue) {
 | 
				
			||||||
 | 
					        if (changes?.acceleratedETA?.currentValue) {
 | 
				
			||||||
 | 
					          this.accelerateRatio = Math.floor((Math.floor(changes.eta.currentValue.time / 1000) - this.now) / (Math.floor(changes.acceleratedETA.currentValue / 1000) - this.now));
 | 
				
			||||||
 | 
					        } else if (changes?.standardETA?.currentValue) {
 | 
				
			||||||
 | 
					          this.accelerateRatio = Math.floor((Math.floor(changes.standardETA.currentValue / 1000) - this.now) / (Math.floor(changes.eta.currentValue.time / 1000) - this.now));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -153,15 +153,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    <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 Timeline">RBF Timeline</h2>
 | 
					        <h2 id="rbf" i18n="transaction.rbf-history|RBF Timeline">RBF Timeline</h2>
 | 
				
			||||||
@ -171,6 +162,15 @@
 | 
				
			|||||||
      <br>
 | 
					      <br>
 | 
				
			||||||
    </ng-container>
 | 
					    </ng-container>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <ng-container *ngIf="transactionTime && 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)" [standardETA]="(standardETA$ | async)?.time"></app-acceleration-timeline>
 | 
				
			||||||
 | 
					      <br>
 | 
				
			||||||
 | 
					    </ng-container>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <ng-container *ngIf="flowEnabled; else flowPlaceholder">
 | 
					    <ng-container *ngIf="flowEnabled; else flowPlaceholder">
 | 
				
			||||||
      <div class="title float-left">
 | 
					      <div class="title float-left">
 | 
				
			||||||
        <h2 id="flow" i18n="transaction.flow|Transaction flow">Flow</h2>
 | 
					        <h2 id="flow" i18n="transaction.flow|Transaction flow">Flow</h2>
 | 
				
			||||||
 | 
				
			|||||||
@ -65,6 +65,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
 | 
				
			|||||||
  txId: string;
 | 
					  txId: string;
 | 
				
			||||||
  txInBlockIndex: number;
 | 
					  txInBlockIndex: number;
 | 
				
			||||||
  mempoolPosition: MempoolPosition;
 | 
					  mempoolPosition: MempoolPosition;
 | 
				
			||||||
 | 
					  gotInitialPosition = false;
 | 
				
			||||||
  accelerationPositions: AccelerationPosition[];
 | 
					  accelerationPositions: AccelerationPosition[];
 | 
				
			||||||
  isLoadingTx = true;
 | 
					  isLoadingTx = true;
 | 
				
			||||||
  error: any = undefined;
 | 
					  error: any = undefined;
 | 
				
			||||||
@ -112,6 +113,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
 | 
				
			|||||||
  txChanged$ = new BehaviorSubject<boolean>(false); // triggered whenever this.tx changes (long term, we should refactor to make this.tx an observable itself)
 | 
					  txChanged$ = new BehaviorSubject<boolean>(false); // triggered whenever this.tx changes (long term, we should refactor to make this.tx an observable itself)
 | 
				
			||||||
  isAccelerated$ = new BehaviorSubject<boolean>(false); // refactor this to make isAccelerated an observable itself
 | 
					  isAccelerated$ = new BehaviorSubject<boolean>(false); // refactor this to make isAccelerated an observable itself
 | 
				
			||||||
  ETA$: Observable<ETA | null>;
 | 
					  ETA$: Observable<ETA | null>;
 | 
				
			||||||
 | 
					  standardETA$: Observable<ETA | null>;
 | 
				
			||||||
  isCached: boolean = false;
 | 
					  isCached: boolean = false;
 | 
				
			||||||
  now = Date.now();
 | 
					  now = Date.now();
 | 
				
			||||||
  da$: Observable<DifficultyAdjustment>;
 | 
					  da$: Observable<DifficultyAdjustment>;
 | 
				
			||||||
@ -431,9 +433,13 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
 | 
				
			|||||||
              if (txPosition.position?.block > 0 && this.tx.weight < 4000) {
 | 
					              if (txPosition.position?.block > 0 && this.tx.weight < 4000) {
 | 
				
			||||||
                this.cashappEligible = true;
 | 
					                this.cashappEligible = true;
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
 | 
					              if (!this.gotInitialPosition && txPosition.position?.block === 0 && txPosition.position?.vsize < 750_000) {
 | 
				
			||||||
 | 
					                this.accelerationFlowCompleted = true;
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this.gotInitialPosition = true;
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        this.mempoolPosition = null;
 | 
					        this.mempoolPosition = null;
 | 
				
			||||||
        this.accelerationPositions = null;
 | 
					        this.accelerationPositions = null;
 | 
				
			||||||
@ -809,6 +815,21 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
 | 
				
			|||||||
        this.miningStats = stats;
 | 
					        this.miningStats = stats;
 | 
				
			||||||
        this.isAccelerated$.next(this.isAcceleration); // hack to trigger recalculation of ETA without adding another source observable
 | 
					        this.isAccelerated$.next(this.isAcceleration); // hack to trigger recalculation of ETA without adding another source observable
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					      if (!this.tx.status?.confirmed) {
 | 
				
			||||||
 | 
					        this.standardETA$ = combineLatest([
 | 
				
			||||||
 | 
					          this.stateService.mempoolBlocks$.pipe(startWith(null)),
 | 
				
			||||||
 | 
					          this.stateService.difficultyAdjustment$.pipe(startWith(null)),
 | 
				
			||||||
 | 
					        ]).pipe(
 | 
				
			||||||
 | 
					          map(([mempoolBlocks, da]) => {
 | 
				
			||||||
 | 
					            return this.etaService.calculateUnacceleratedETA(
 | 
				
			||||||
 | 
					              this.tx,
 | 
				
			||||||
 | 
					              mempoolBlocks,
 | 
				
			||||||
 | 
					              da,
 | 
				
			||||||
 | 
					              this.cpfpInfo,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.isAccelerated$.next(this.isAcceleration);
 | 
					    this.isAccelerated$.next(this.isAcceleration);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -864,6 +885,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  resetTransaction() {
 | 
					  resetTransaction() {
 | 
				
			||||||
    this.firstLoad = false;
 | 
					    this.firstLoad = false;
 | 
				
			||||||
 | 
					    this.gotInitialPosition = false;
 | 
				
			||||||
    this.error = undefined;
 | 
					    this.error = undefined;
 | 
				
			||||||
    this.tx = null;
 | 
					    this.tx = null;
 | 
				
			||||||
    this.txChanged$.next(true);
 | 
					    this.txChanged$.next(true);
 | 
				
			||||||
 | 
				
			|||||||
@ -225,4 +225,58 @@ export class EtaService {
 | 
				
			|||||||
        blocks: Math.ceil(eta / da.adjustedTimeAvg),
 | 
					        blocks: Math.ceil(eta / da.adjustedTimeAvg),
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  calculateUnacceleratedETA(
 | 
				
			||||||
 | 
					    tx: Transaction,
 | 
				
			||||||
 | 
					    mempoolBlocks: MempoolBlock[],
 | 
				
			||||||
 | 
					    da: DifficultyAdjustment,
 | 
				
			||||||
 | 
					    cpfpInfo: CpfpInfo | null,
 | 
				
			||||||
 | 
					  ): ETA | null {
 | 
				
			||||||
 | 
					    if (!tx || !mempoolBlocks) {
 | 
				
			||||||
 | 
					      return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const now = Date.now();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // use known projected position, or fall back to feerate-based estimate
 | 
				
			||||||
 | 
					    const mempoolPosition = this.mempoolPositionFromFees(this.getFeeRateFromCpfpInfo(tx, cpfpInfo), mempoolBlocks);
 | 
				
			||||||
 | 
					    if (!mempoolPosition) {
 | 
				
			||||||
 | 
					      return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // difficulty adjustment estimate is required to know avg block time on non-Liquid networks
 | 
				
			||||||
 | 
					    if (!da) {
 | 
				
			||||||
 | 
					      return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const blocks = mempoolPosition.block + 1;
 | 
				
			||||||
 | 
					    const wait = da.adjustedTimeAvg * (mempoolPosition.block + 1);
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      now,
 | 
				
			||||||
 | 
					      time: wait + now + da.timeOffset,
 | 
				
			||||||
 | 
					      wait,
 | 
				
			||||||
 | 
					      blocks,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getFeeRateFromCpfpInfo(tx: Transaction, cpfpInfo: CpfpInfo | null): number {
 | 
				
			||||||
 | 
					    if (!cpfpInfo) {
 | 
				
			||||||
 | 
					      return tx.fee / (tx.weight / 4);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const relatives = [...(cpfpInfo.ancestors || []), ...(cpfpInfo.descendants || [])];
 | 
				
			||||||
 | 
					    if (cpfpInfo.bestDescendant && !cpfpInfo.descendants?.length) {
 | 
				
			||||||
 | 
					      relatives.push(cpfpInfo.bestDescendant);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!!relatives.length) {
 | 
				
			||||||
 | 
					      const totalWeight = tx.weight + relatives.reduce((prev, val) => prev + val.weight, 0);
 | 
				
			||||||
 | 
					      const totalFees = tx.fee + relatives.reduce((prev, val) => prev + val.fee, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return totalFees / (totalWeight / 4);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return tx.fee / (tx.weight / 4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user