Merge branch 'master' into mononaut/tracker-tx-routing
This commit is contained in:
		
						commit
						adea897e93
					
				| @ -165,6 +165,7 @@ class BitcoinRoutes { | |||||||
|           acceleration: tx.acceleration, |           acceleration: tx.acceleration, | ||||||
|           acceleratedBy: tx.acceleratedBy || undefined, |           acceleratedBy: tx.acceleratedBy || undefined, | ||||||
|           acceleratedAt: tx.acceleratedAt || undefined, |           acceleratedAt: tx.acceleratedAt || undefined, | ||||||
|  |           feeDelta: tx.feeDelta || undefined, | ||||||
|         }); |         }); | ||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
|  | |||||||
| @ -453,6 +453,7 @@ class MempoolBlocks { | |||||||
|             mempoolTx.acceleration = true; |             mempoolTx.acceleration = true; | ||||||
|             mempoolTx.acceleratedBy = isAcceleratedBy[txid] || acceleration?.pools; |             mempoolTx.acceleratedBy = isAcceleratedBy[txid] || acceleration?.pools; | ||||||
|             mempoolTx.acceleratedAt = acceleration?.added; |             mempoolTx.acceleratedAt = acceleration?.added; | ||||||
|  |             mempoolTx.feeDelta = acceleration?.feeDelta; | ||||||
|             for (const ancestor of mempoolTx.ancestors || []) { |             for (const ancestor of mempoolTx.ancestors || []) { | ||||||
|               if (!mempool[ancestor.txid].acceleration) { |               if (!mempool[ancestor.txid].acceleration) { | ||||||
|                 mempool[ancestor.txid].cpfpDirty = true; |                 mempool[ancestor.txid].cpfpDirty = true; | ||||||
| @ -460,6 +461,7 @@ class MempoolBlocks { | |||||||
|               mempool[ancestor.txid].acceleration = true; |               mempool[ancestor.txid].acceleration = true; | ||||||
|               mempool[ancestor.txid].acceleratedBy = mempoolTx.acceleratedBy; |               mempool[ancestor.txid].acceleratedBy = mempoolTx.acceleratedBy; | ||||||
|               mempool[ancestor.txid].acceleratedAt = mempoolTx.acceleratedAt; |               mempool[ancestor.txid].acceleratedAt = mempoolTx.acceleratedAt; | ||||||
|  |               mempool[ancestor.txid].feeDelta = mempoolTx.feeDelta; | ||||||
|               isAcceleratedBy[ancestor.txid] = mempoolTx.acceleratedBy; |               isAcceleratedBy[ancestor.txid] = mempoolTx.acceleratedBy; | ||||||
|             } |             } | ||||||
|           } else { |           } else { | ||||||
|  | |||||||
| @ -823,6 +823,7 @@ class WebsocketHandler { | |||||||
|               accelerated: mempoolTx.acceleration || undefined, |               accelerated: mempoolTx.acceleration || undefined, | ||||||
|               acceleratedBy: mempoolTx.acceleratedBy || undefined, |               acceleratedBy: mempoolTx.acceleratedBy || undefined, | ||||||
|               acceleratedAt: mempoolTx.acceleratedAt || undefined, |               acceleratedAt: mempoolTx.acceleratedAt || undefined, | ||||||
|  |               feeDelta: mempoolTx.feeDelta || undefined, | ||||||
|             }, |             }, | ||||||
|             accelerationPositions: memPool.getAccelerationPositions(mempoolTx.txid), |             accelerationPositions: memPool.getAccelerationPositions(mempoolTx.txid), | ||||||
|           }; |           }; | ||||||
| @ -864,6 +865,7 @@ class WebsocketHandler { | |||||||
|               accelerated: mempoolTx.acceleration || undefined, |               accelerated: mempoolTx.acceleration || undefined, | ||||||
|               acceleratedBy: mempoolTx.acceleratedBy || undefined, |               acceleratedBy: mempoolTx.acceleratedBy || undefined, | ||||||
|               acceleratedAt: mempoolTx.acceleratedAt || undefined, |               acceleratedAt: mempoolTx.acceleratedAt || undefined, | ||||||
|  |               feeDelta: mempoolTx.feeDelta || undefined, | ||||||
|             }; |             }; | ||||||
|             if (!mempoolTx.cpfpChecked) { |             if (!mempoolTx.cpfpChecked) { | ||||||
|               calculateMempoolTxCpfp(mempoolTx, newMempool); |               calculateMempoolTxCpfp(mempoolTx, newMempool); | ||||||
| @ -1138,6 +1140,7 @@ class WebsocketHandler { | |||||||
|                 accelerated: mempoolTx.acceleration || undefined, |                 accelerated: mempoolTx.acceleration || undefined, | ||||||
|                 acceleratedBy: mempoolTx.acceleratedBy || undefined, |                 acceleratedBy: mempoolTx.acceleratedBy || undefined, | ||||||
|                 acceleratedAt: mempoolTx.acceleratedAt || undefined, |                 acceleratedAt: mempoolTx.acceleratedAt || undefined, | ||||||
|  |                 feeDelta: mempoolTx.feeDelta || undefined, | ||||||
|               }, |               }, | ||||||
|               accelerationPositions: memPool.getAccelerationPositions(mempoolTx.txid), |               accelerationPositions: memPool.getAccelerationPositions(mempoolTx.txid), | ||||||
|             }); |             }); | ||||||
| @ -1160,6 +1163,7 @@ class WebsocketHandler { | |||||||
|                 accelerated: mempoolTx.acceleration || undefined, |                 accelerated: mempoolTx.acceleration || undefined, | ||||||
|                 acceleratedBy: mempoolTx.acceleratedBy || undefined, |                 acceleratedBy: mempoolTx.acceleratedBy || undefined, | ||||||
|                 acceleratedAt: mempoolTx.acceleratedAt || undefined, |                 acceleratedAt: mempoolTx.acceleratedAt || undefined, | ||||||
|  |                 feeDelta: mempoolTx.feeDelta || undefined, | ||||||
|               }; |               }; | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|  | |||||||
| @ -126,6 +126,7 @@ export interface TransactionExtended extends IEsploraApi.Transaction { | |||||||
|   acceleration?: boolean; |   acceleration?: boolean; | ||||||
|   acceleratedBy?: number[]; |   acceleratedBy?: number[]; | ||||||
|   acceleratedAt?: number; |   acceleratedAt?: number; | ||||||
|  |   feeDelta?: number; | ||||||
|   replacement?: boolean; |   replacement?: boolean; | ||||||
|   uid?: number; |   uid?: number; | ||||||
|   flags?: number; |   flags?: number; | ||||||
| @ -449,7 +450,7 @@ export interface OptimizedStatistic { | |||||||
| 
 | 
 | ||||||
| export interface TxTrackingInfo { | export interface TxTrackingInfo { | ||||||
|   replacedBy?: string, |   replacedBy?: string, | ||||||
|   position?: { block: number, vsize: number, accelerated?: boolean, acceleratedBy?: number[], acceleratedAt?: number }, |   position?: { block: number, vsize: number, accelerated?: boolean, acceleratedBy?: number[], acceleratedAt?: number, feeDelta?: number }, | ||||||
|   cpfp?: { |   cpfp?: { | ||||||
|     ancestors?: Ancestor[], |     ancestors?: Ancestor[], | ||||||
|     bestDescendant?: Ancestor | null, |     bestDescendant?: Ancestor | null, | ||||||
| @ -462,6 +463,7 @@ export interface TxTrackingInfo { | |||||||
|   accelerated?: boolean, |   accelerated?: boolean, | ||||||
|   acceleratedBy?: number[], |   acceleratedBy?: number[], | ||||||
|   acceleratedAt?: number, |   acceleratedAt?: number, | ||||||
|  |   feeDelta?: number, | ||||||
|   confirmed?: boolean |   confirmed?: boolean | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -0,0 +1,62 @@ | |||||||
|  | <div | ||||||
|  |   #tooltip | ||||||
|  |   *ngIf="accelerationInfo && tooltipPosition !== null" | ||||||
|  |   class="acceleration-tooltip" | ||||||
|  |   [style.left]="tooltipPosition.x + 'px'" | ||||||
|  |   [style.top]="tooltipPosition.y + 'px'" | ||||||
|  | > | ||||||
|  |   <table> | ||||||
|  |     <tbody> | ||||||
|  |       <tr> | ||||||
|  |         <td class="label" i18n="transaction.status|Transaction Status">Status</td> | ||||||
|  |         <td class="value"> | ||||||
|  |           @if (accelerationInfo.status === 'seen') { | ||||||
|  |           <span class="badge badge-primary" i18n="transaction.first-seen|Transaction first seen">First seen</span> | ||||||
|  |           } @else if (accelerationInfo.status === 'accelerated') { | ||||||
|  |           <span class="badge badge-accelerated" i18n="transaction.audit.accelerated">Accelerated</span> | ||||||
|  |           } @else if (accelerationInfo.status === 'mined') { | ||||||
|  |           <span class="badge badge-success" i18n="transaction.rbf.mined">Mined</span> | ||||||
|  |           } | ||||||
|  |         </td> | ||||||
|  |       </tr> | ||||||
|  |       <tr *ngIf="accelerationInfo.fee"> | ||||||
|  |         <td class="label" i18n="transaction.fee|Transaction fee">Fee</td> | ||||||
|  |         <td class="value">{{ accelerationInfo.fee | number }} <span class="symbol" i18n="shared.sat|sat">sat</span></td> | ||||||
|  |       </tr> | ||||||
|  |       <tr *ngIf="accelerationInfo.bidBoost >= 0 || accelerationInfo.feeDelta"> | ||||||
|  |         <td class="label" i18n="transaction.out-of-band-fees">Out-of-band fees</td> | ||||||
|  |         @if (accelerationInfo.status === 'accelerated') { | ||||||
|  |           <td style="color: #905cf4;" class="value">{{ accelerationInfo.feeDelta | number }} <span class="symbol" i18n="shared.sat|sat">sat</span></td> | ||||||
|  |         } @else { | ||||||
|  |           <td style="color: #905cf4;" class="value">{{ accelerationInfo.bidBoost | number }} <span class="symbol" i18n="shared.sat|sat">sat</span></td> | ||||||
|  |         } | ||||||
|  |       </tr> | ||||||
|  |       <tr *ngIf="accelerationInfo.fee && accelerationInfo.weight && (accelerationInfo.feeDelta || accelerationInfo.bidBoost)"> | ||||||
|  |         @if (accelerationInfo.status === 'seen') { | ||||||
|  |           <td class="label" i18n="transaction.fee-rate">Fee rate</td> | ||||||
|  |           <td class="value"><app-fee-rate [fee]="accelerationInfo.fee" [weight]="accelerationInfo.weight"></app-fee-rate></td> | ||||||
|  |         } @else if (accelerationInfo.status === 'accelerated' || accelerationInfo.status === 'mined') { | ||||||
|  |           <td class="label" i18n="transaction.accelerated-fee-rate|Accelerated transaction fee rate">Accelerated fee rate</td> | ||||||
|  |           @if (accelerationInfo.status === 'accelerated') { | ||||||
|  |             <td class="value"><app-fee-rate [fee]="accelerationInfo.fee + (accelerationInfo.feeDelta || 0)" [weight]="accelerationInfo.weight"></app-fee-rate></td> | ||||||
|  |           } @else { | ||||||
|  |             <td class="value"><app-fee-rate [fee]="accelerationInfo.fee + (accelerationInfo.bidBoost || 0)" [weight]="accelerationInfo.weight"></app-fee-rate></td> | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       </tr> | ||||||
|  |       <tr *ngIf="['accelerated', 'mined'].includes(accelerationInfo.status) && hasPoolsData()"> | ||||||
|  |         <td class="label" i18n="transaction.accelerated-by-hashrate|Accelerated to hashrate">Accelerated by</td> | ||||||
|  |         <td class="value" *ngIf="accelerationInfo.pools"> | ||||||
|  |           <ng-container *ngFor="let pool of accelerationInfo.pools"> | ||||||
|  |             <img *ngIf="accelerationInfo.poolsData[pool]"  | ||||||
|  |               class="pool-logo"  | ||||||
|  |               [class.highlight]="pool === accelerationInfo?.minedByPoolUniqueId" | ||||||
|  |               [src]="'/resources/mining-pools/' + accelerationInfo.poolsData[pool].slug + '.svg'"  | ||||||
|  |               onError="this.src = '/resources/mining-pools/default.svg'"  | ||||||
|  |               [alt]="'Logo of ' + pool.name + ' mining pool'"> | ||||||
|  |           </ng-container> | ||||||
|  |         </td> | ||||||
|  |       </tr> | ||||||
|  |     </tbody> | ||||||
|  |   </table> | ||||||
|  | </div> | ||||||
| @ -0,0 +1,52 @@ | |||||||
|  | .acceleration-tooltip { | ||||||
|  |   position: fixed; | ||||||
|  |   z-index: 3; | ||||||
|  |   background: color-mix(in srgb, var(--active-bg) 95%, transparent); | ||||||
|  |   border-radius: 4px; | ||||||
|  |   box-shadow: 1px 1px 10px rgba(0,0,0,0.5); | ||||||
|  |   color: var(--tooltip-grey); | ||||||
|  |   display: flex; | ||||||
|  |   flex-direction: column; | ||||||
|  |   justify-content: space-between; | ||||||
|  |   padding: 10px 15px; | ||||||
|  |   text-align: left; | ||||||
|  |   pointer-events: none; | ||||||
|  | 
 | ||||||
|  |   .badge.badge-accelerated { | ||||||
|  |     background-color: var(--tertiary); | ||||||
|  |     color: white; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .value { | ||||||
|  |     text-align: end; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .label { | ||||||
|  |     padding-right: 30px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .pool-logo { | ||||||
|  |     width: 22px; | ||||||
|  |     height: 22px; | ||||||
|  |     position: relative; | ||||||
|  |     top: -1px; | ||||||
|  |     margin-right: 3px; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .highlight { | ||||||
|  |     filter: drop-shadow(0 0 5px #905cf4); | ||||||
|  |     animation: pulse 1s infinite; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @keyframes pulse { | ||||||
|  |   0% { | ||||||
|  |     transform: scale(1); | ||||||
|  |   } | ||||||
|  |   50% { | ||||||
|  |     transform: scale(1.2); | ||||||
|  |   } | ||||||
|  |   100% { | ||||||
|  |     transform: scale(1); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,38 @@ | |||||||
|  | import { Component, ElementRef, ViewChild, Input, OnChanges } from '@angular/core'; | ||||||
|  | 
 | ||||||
|  | @Component({ | ||||||
|  |   selector: 'app-acceleration-timeline-tooltip', | ||||||
|  |   templateUrl: './acceleration-timeline-tooltip.component.html', | ||||||
|  |   styleUrls: ['./acceleration-timeline-tooltip.component.scss'], | ||||||
|  | }) | ||||||
|  | export class AccelerationTimelineTooltipComponent implements OnChanges { | ||||||
|  |   @Input() accelerationInfo: any; | ||||||
|  |   @Input() cursorPosition: { x: number, y: number }; | ||||||
|  | 
 | ||||||
|  |   tooltipPosition: any = null; | ||||||
|  | 
 | ||||||
|  |   @ViewChild('tooltip') tooltipElement: ElementRef<HTMLCanvasElement>; | ||||||
|  | 
 | ||||||
|  |   constructor() {} | ||||||
|  | 
 | ||||||
|  |   ngOnChanges(changes): void { | ||||||
|  |     if (changes.cursorPosition && changes.cursorPosition.currentValue) { | ||||||
|  |       let x = Math.max(10, changes.cursorPosition.currentValue.x - 50); | ||||||
|  |       let y = changes.cursorPosition.currentValue.y + 20; | ||||||
|  |       if (this.tooltipElement) { | ||||||
|  |         const elementBounds = this.tooltipElement.nativeElement.getBoundingClientRect(); | ||||||
|  |         if ((x + elementBounds.width) > (window.innerWidth - 10)) { | ||||||
|  |           x = Math.max(0, window.innerWidth - elementBounds.width - 10); | ||||||
|  |         } | ||||||
|  |         if (y + elementBounds.height > (window.innerHeight - 20)) { | ||||||
|  |           y = y - elementBounds.height - 20; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       this.tooltipPosition = { x, y }; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   hasPoolsData(): boolean { | ||||||
|  |     return Object.keys(this.accelerationInfo.poolsData).length > 0; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -26,7 +26,7 @@ | |||||||
|         <div class="node" [id]="'confirmed'"> |         <div class="node" [id]="'confirmed'"> | ||||||
|           <div class="acc-to-confirmed left go-faster"></div> |           <div class="acc-to-confirmed left go-faster"></div> | ||||||
|           <div class="shape-border waiting"> |           <div class="shape-border waiting"> | ||||||
|             <div class="shape animate"></div> |             <div class="shape"></div> | ||||||
|           </div> |           </div> | ||||||
|           <div class="status"><span class="badge badge-waiting" i18n="transaction.rbf.mined">Mined</span></div> |           <div class="status"><span class="badge badge-waiting" i18n="transaction.rbf.mined">Mined</span></div> | ||||||
|         </div> |         </div> | ||||||
| @ -58,7 +58,7 @@ | |||||||
|       <div class="nodes"> |       <div class="nodes"> | ||||||
|         <div class="node" [id]="'first-seen'"> |         <div class="node" [id]="'first-seen'"> | ||||||
|           <div class="seen-to-acc right"></div> |           <div class="seen-to-acc right"></div> | ||||||
|           <div class="shape-border"> |           <div class="shape-border hovering" (pointerover)="onHover($event, 'seen');" (pointerout)="onBlur($event);"> | ||||||
|             <div class="shape"></div> |             <div class="shape"></div> | ||||||
|           </div> |           </div> | ||||||
|           <div class="status"><span class="badge badge-primary" i18n="transaction.first-seen|Transaction first seen">First seen</span></div> |           <div class="status"><span class="badge badge-primary" i18n="transaction.first-seen|Transaction first seen">First seen</span></div> | ||||||
| @ -80,7 +80,7 @@ | |||||||
|           } @else { |           } @else { | ||||||
|           <div class="seen-to-acc right"></div> |           <div class="seen-to-acc right"></div> | ||||||
|           } |           } | ||||||
|           <div  class="shape-border"> |           <div  class="shape-border hovering" (pointerover)="onHover($event, 'accelerated');" (pointerout)="onBlur($event);"> | ||||||
|             <div class="shape"></div> |             <div class="shape"></div> | ||||||
|             @if (!tx.status.confirmed) { |             @if (!tx.status.confirmed) { | ||||||
|             <div class="connector down loading"></div> |             <div class="connector down loading"></div> | ||||||
| @ -113,7 +113,10 @@ | |||||||
|           } @else { |           } @else { | ||||||
|           <div class="seen-to-acc left"></div> |           <div class="seen-to-acc left"></div> | ||||||
|           } |           } | ||||||
|           <div class="shape-border" [class.waiting]="!tx.status.confirmed"> |           <div class="shape-border"  | ||||||
|  |               [ngClass]="{'waiting': !tx.status.confirmed, 'hovering': tx.status.confirmed}" | ||||||
|  |               (pointerover)="onHover($event, tx.status.confirmed ? 'mined' : null)"  | ||||||
|  |               (pointerout)="onBlur($event);"> | ||||||
|             <div class="shape"></div> |             <div class="shape"></div> | ||||||
|           </div> |           </div> | ||||||
|           @if (tx.status.confirmed) { |           @if (tx.status.confirmed) { | ||||||
| @ -130,4 +133,10 @@ | |||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
|  | 
 | ||||||
|  |   <app-acceleration-timeline-tooltip | ||||||
|  |   [accelerationInfo]="hoverInfo" | ||||||
|  |   [cursorPosition]="tooltipPosition" | ||||||
|  | ></app-acceleration-timeline-tooltip> | ||||||
|  | 
 | ||||||
| </div> | </div> | ||||||
|  | |||||||
| @ -152,9 +152,16 @@ | |||||||
|         margin-bottom: -8px; |         margin-bottom: -8px; | ||||||
|         transform: translateY(-50%); |         transform: translateY(-50%); | ||||||
|         border-radius: 50%; |         border-radius: 50%; | ||||||
|         cursor: pointer; |  | ||||||
|         padding: 4px; |         padding: 4px; | ||||||
|         background: transparent; |         background: transparent; | ||||||
|  |         transition: background-color 300ms, padding 300ms; | ||||||
|  | 
 | ||||||
|  |         &.hovering { | ||||||
|  |           cursor: pointer; | ||||||
|  |           &:hover { | ||||||
|  |             padding: 0px; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|      |      | ||||||
|         .shape { |         .shape { | ||||||
|           position: relative; |           position: relative; | ||||||
|  | |||||||
| @ -1,6 +1,8 @@ | |||||||
| import { Component, Input, OnInit, OnChanges } from '@angular/core'; | import { Component, Input, OnInit, OnChanges, HostListener } 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'; | ||||||
|  | import { Acceleration, SinglePoolStats } from '../../interfaces/node-api.interface'; | ||||||
|  | import { MiningService } from '../../services/mining.service'; | ||||||
| 
 | 
 | ||||||
| @Component({ | @Component({ | ||||||
|   selector: 'app-acceleration-timeline', |   selector: 'app-acceleration-timeline', | ||||||
| @ -10,6 +12,7 @@ import { Transaction } from '../../interfaces/electrs.interface'; | |||||||
| export class AccelerationTimelineComponent implements OnInit, OnChanges { | export class AccelerationTimelineComponent implements OnInit, OnChanges { | ||||||
|   @Input() transactionTime: number; |   @Input() transactionTime: number; | ||||||
|   @Input() tx: Transaction; |   @Input() tx: Transaction; | ||||||
|  |   @Input() accelerationInfo: Acceleration; | ||||||
|   @Input() eta: ETA; |   @Input() eta: ETA; | ||||||
|   // A mined transaction has standard ETA and accelerated ETA undefined
 |   // 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)
 |   // A transaction in mempool has either standardETA defined (if accelerated) or acceleratedETA defined (if not accelerated yet)
 | ||||||
| @ -22,13 +25,25 @@ export class AccelerationTimelineComponent implements OnInit, OnChanges { | |||||||
|   useAbsoluteTime: boolean = false; |   useAbsoluteTime: boolean = false; | ||||||
|   interval: number; |   interval: number; | ||||||
| 
 | 
 | ||||||
|   constructor() {} |   tooltipPosition = null; | ||||||
|  |   hoverInfo: any = null; | ||||||
|  |   poolsData: { [id: number]: SinglePoolStats } = {}; | ||||||
|  | 
 | ||||||
|  |   constructor( | ||||||
|  |     private miningService: MiningService, | ||||||
|  |   ) {} | ||||||
| 
 | 
 | ||||||
|   ngOnInit(): void { |   ngOnInit(): void { | ||||||
|     this.acceleratedAt = this.tx.acceleratedAt ?? new Date().getTime() / 1000; |     this.acceleratedAt = this.tx.acceleratedAt ?? new Date().getTime() / 1000; | ||||||
|     this.now = Math.floor(new Date().getTime() / 1000); |     this.now = Math.floor(new Date().getTime() / 1000); | ||||||
|     this.useAbsoluteTime = this.tx.status.block_time < this.now - 7 * 24 * 3600; |     this.useAbsoluteTime = this.tx.status.block_time < this.now - 7 * 24 * 3600; | ||||||
| 
 | 
 | ||||||
|  |     this.miningService.getPools().subscribe(pools => { | ||||||
|  |       for (const pool of pools) { | ||||||
|  |         this.poolsData[pool.unique_id] = pool; | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|     this.interval = window.setInterval(() => { |     this.interval = window.setInterval(() => { | ||||||
|       this.now = Math.floor(new Date().getTime() / 1000); |       this.now = Math.floor(new Date().getTime() / 1000); | ||||||
|       this.useAbsoluteTime = this.tx.status.block_time < this.now - 7 * 24 * 3600; |       this.useAbsoluteTime = this.tx.status.block_time < this.now - 7 * 24 * 3600; | ||||||
| @ -52,4 +67,42 @@ export class AccelerationTimelineComponent implements OnInit, OnChanges { | |||||||
|   ngOnDestroy(): void { |   ngOnDestroy(): void { | ||||||
|     clearInterval(this.interval); |     clearInterval(this.interval); | ||||||
|   } |   } | ||||||
|  |    | ||||||
|  |   onHover(event, status: string): void { | ||||||
|  |     if (status === 'seen') { | ||||||
|  |       this.hoverInfo = { | ||||||
|  |         status, | ||||||
|  |         fee: this.tx.fee, | ||||||
|  |         weight: this.tx.weight | ||||||
|  |       }; | ||||||
|  |     } else if (status === 'accelerated') { | ||||||
|  |       this.hoverInfo = { | ||||||
|  |         status, | ||||||
|  |         fee: this.accelerationInfo?.effectiveFee || this.tx.fee, | ||||||
|  |         weight: this.tx.weight, | ||||||
|  |         feeDelta: this.accelerationInfo?.feeDelta || this.tx.feeDelta, | ||||||
|  |         pools: this.tx.acceleratedBy || this.accelerationInfo?.pools, | ||||||
|  |         poolsData: this.poolsData | ||||||
|  |       }; | ||||||
|  |     } else if (status === 'mined') { | ||||||
|  |       this.hoverInfo = { | ||||||
|  |         status, | ||||||
|  |         fee: this.accelerationInfo?.effectiveFee, | ||||||
|  |         weight: this.tx.weight, | ||||||
|  |         bidBoost: this.accelerationInfo?.bidBoost, | ||||||
|  |         minedByPoolUniqueId: this.accelerationInfo?.minedByPoolUniqueId, | ||||||
|  |         pools: this.tx.acceleratedBy || this.accelerationInfo?.pools, | ||||||
|  |         poolsData: this.poolsData | ||||||
|  |       }; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   onBlur(event): void { | ||||||
|  |     this.hoverInfo = null; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @HostListener('pointermove', ['$event']) | ||||||
|  |   onPointerMove(event) { | ||||||
|  |     this.tooltipPosition = { x: event.clientX, y: event.clientY }; | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -167,7 +167,7 @@ | |||||||
|         <h2 id="acceleration-timeline" i18n="transaction.acceleration-timeline|Acceleration Timeline">Acceleration Timeline</h2> |         <h2 id="acceleration-timeline" i18n="transaction.acceleration-timeline|Acceleration Timeline">Acceleration Timeline</h2> | ||||||
|       </div> |       </div> | ||||||
|       <div class="clearfix"></div> |       <div class="clearfix"></div> | ||||||
|       <app-acceleration-timeline [transactionTime]="transactionTime" [tx]="tx" [eta]="(ETA$ | async)" [standardETA]="(standardETA$ | async)?.time"></app-acceleration-timeline> |       <app-acceleration-timeline [transactionTime]="transactionTime" [tx]="tx" [accelerationInfo]="accelerationInfo" [eta]="(ETA$ | async)" [standardETA]="(standardETA$ | async)?.time"></app-acceleration-timeline> | ||||||
|       <br> |       <br> | ||||||
|     </ng-container> |     </ng-container> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -823,6 +823,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { | |||||||
|       this.tx.acceleration = cpfpInfo.acceleration; |       this.tx.acceleration = cpfpInfo.acceleration; | ||||||
|       this.tx.acceleratedBy = cpfpInfo.acceleratedBy; |       this.tx.acceleratedBy = cpfpInfo.acceleratedBy; | ||||||
|       this.tx.acceleratedAt = cpfpInfo.acceleratedAt; |       this.tx.acceleratedAt = cpfpInfo.acceleratedAt; | ||||||
|  |       this.tx.feeDelta = cpfpInfo.feeDelta; | ||||||
|       this.setIsAccelerated(firstCpfp); |       this.setIsAccelerated(firstCpfp); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -22,6 +22,7 @@ export interface Transaction { | |||||||
|   acceleration?: boolean; |   acceleration?: boolean; | ||||||
|   acceleratedBy?: number[]; |   acceleratedBy?: number[]; | ||||||
|   acceleratedAt?: number; |   acceleratedAt?: number; | ||||||
|  |   feeDelta?: number; | ||||||
|   deleteAfter?: number; |   deleteAfter?: number; | ||||||
|   _unblinded?: any; |   _unblinded?: any; | ||||||
|   _deduced?: boolean; |   _deduced?: boolean; | ||||||
|  | |||||||
| @ -31,6 +31,7 @@ export interface CpfpInfo { | |||||||
|   acceleration?: boolean; |   acceleration?: boolean; | ||||||
|   acceleratedBy?: number[]; |   acceleratedBy?: number[]; | ||||||
|   acceleratedAt?: number; |   acceleratedAt?: number; | ||||||
|  |   feeDelta?: number; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface RbfInfo { | export interface RbfInfo { | ||||||
|  | |||||||
| @ -68,6 +68,7 @@ import { AddressTransactionsWidgetComponent } from '../components/address-transa | |||||||
| 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 { 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 { AccelerationTimelineTooltipComponent } from '../components/acceleration-timeline/acceleration-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'; | ||||||
| import { AssetsFeaturedComponent } from '../components/assets/assets-featured/assets-featured.component'; | import { AssetsFeaturedComponent } from '../components/assets/assets-featured/assets-featured.component'; | ||||||
| @ -180,6 +181,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir | |||||||
|     RbfTimelineComponent, |     RbfTimelineComponent, | ||||||
|     AccelerationTimelineComponent, |     AccelerationTimelineComponent, | ||||||
|     RbfTimelineTooltipComponent, |     RbfTimelineTooltipComponent, | ||||||
|  |     AccelerationTimelineTooltipComponent, | ||||||
|     PushTransactionComponent, |     PushTransactionComponent, | ||||||
|     TestTransactionsComponent, |     TestTransactionsComponent, | ||||||
|     AssetsNavComponent, |     AssetsNavComponent, | ||||||
| @ -320,6 +322,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir | |||||||
|     RbfTimelineComponent, |     RbfTimelineComponent, | ||||||
|     AccelerationTimelineComponent, |     AccelerationTimelineComponent, | ||||||
|     RbfTimelineTooltipComponent, |     RbfTimelineTooltipComponent, | ||||||
|  |     AccelerationTimelineTooltipComponent, | ||||||
|     PushTransactionComponent, |     PushTransactionComponent, | ||||||
|     TestTransactionsComponent, |     TestTransactionsComponent, | ||||||
|     AssetsNavComponent, |     AssetsNavComponent, | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user