Show unaccelerated ETA in acceleration timeline
This commit is contained in:
		
							parent
							
								
									a0992f6091
								
							
						
					
					
						commit
						bf51e3e1c9
					
				| @ -1,3 +1,151 @@ | |||||||
|  | @if (tx.status.confirmed) { | ||||||
|  | <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"> | ||||||
|  |             <app-time [time]="tx.status.block_time - acceleratedAt"></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> | ||||||
|  |           <a class="shape-border"> | ||||||
|  |             <div class="shape"></div> | ||||||
|  |           </a> | ||||||
|  |           <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="acc-to-confirmed right"></div> | ||||||
|  |           <a class="shape-border"> | ||||||
|  |             <div class="shape"></div> | ||||||
|  |           </a> | ||||||
|  |           <div class="status"><span class="badge badge-accelerated" i18n="transaction.audit.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"></div> | ||||||
|  |         </div> | ||||||
|  |         <div class="node mined" [id]="'confirmed'" > | ||||||
|  |           <div class="acc-to-confirmed left" ></div> | ||||||
|  |           <a class="shape-border mined-selected"> | ||||||
|  |             <div class="shape"></div> | ||||||
|  |           </a> | ||||||
|  |           <div class="status"><span class="badge badge-success" i18n="transaction.rbf.mined">Mined</span></div> | ||||||
|  |           <div class="time"> | ||||||
|  |             <app-time kind="since" [time]="tx.status.block_time"></app-time> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </div>   | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </div> | ||||||
|  | } @else if (acceleratedETA) { <!-- Not yet accelerated; to be shown only in acceleration checkout --> | ||||||
|  | <div class="acceleration-timeline"> | ||||||
|  |   <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]="now - transactionTime"></app-time> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |         <div class="node-spacer"></div> | ||||||
|  |         <div class="interval"> | ||||||
|  |           <div class="interval-time"> | ||||||
|  |             ~<app-time [time]="acceleratedETA / 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> | ||||||
|  |           <a class="shape-border"> | ||||||
|  |             <div class="shape"></div> | ||||||
|  |           </a> | ||||||
|  |           <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="acc-to-confirmed right"></div> | ||||||
|  |           <a class="shape-border waiting"> | ||||||
|  |             <div class="shape accelerating"></div> | ||||||
|  |           </a> | ||||||
|  |           <div class="status"><span class="badge badge-waiting" i18n="transaction.audit.accelerated">Accelerated</span></div> | ||||||
|  |           <div class="time"> | ||||||
|  |             <span i18n="date.now">Now</span> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |         <div class="interval-spacer"> | ||||||
|  |           <div class="acc-to-confirmed"></div> | ||||||
|  |         </div> | ||||||
|  |         <div class="node" [id]="'confirmed'"> | ||||||
|  |           <div class="acc-to-confirmed left"></div> | ||||||
|  |           <div class="corner-up"></div> | ||||||
|  |           <a class="shape-border waiting"> | ||||||
|  |             <div class="shape"></div> | ||||||
|  |           </a> | ||||||
|  |           <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-spacer"></div> | ||||||
|  |         <div class="node-spacer"></div> | ||||||
|  |         <div class="interval"> | ||||||
|  |           <div class="interval-time"> | ||||||
|  |             ~<app-time [time]="eta.time / 1000 - now"></app-time> <span *ngIf="accelerateRatio > 1" style="font-style: italic; color: var(--transparent-fg);"> ({{ accelerateRatio }}x slower)</span> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |         <div class="node-spacer"></div> | ||||||
|  |       </div> | ||||||
|  |       <div class="nodes"> | ||||||
|  |         <div class="node-spacer"></div> | ||||||
|  |         <div class="interval-spacer"></div> | ||||||
|  |         <div class="node-spacer"> | ||||||
|  |           <div class="connector"><div class="corner-down"></div></div> | ||||||
|  |           <div class="seen-to-acc right"></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> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </div> | ||||||
|  | } @else if (standardETA) { <!-- Accelerated, to be mined --> | ||||||
|   <div class="acceleration-timeline box"> |   <div class="acceleration-timeline box"> | ||||||
|     <div class="timeline-wrapper"> |     <div class="timeline-wrapper"> | ||||||
|       <div class="timeline"> |       <div class="timeline"> | ||||||
| @ -13,66 +161,76 @@ | |||||||
|             <div class="interval-time"> |             <div class="interval-time"> | ||||||
|               @if (eta) { |               @if (eta) { | ||||||
|                 ~<app-time [time]="eta?.wait / 1000"></app-time> |                 ~<app-time [time]="eta?.wait / 1000"></app-time> | ||||||
|             } @else if (tx.status.block_time) { |  | ||||||
|             <app-time [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]="!isAcceleration && !tx.status.confirmed"></div> |             <div class="seen-to-acc right"></div> | ||||||
|         <a class="shape-border" [class.sent-selected]="!tx.status.confirmed && !isAcceleration"> |             <a class="shape-border"> | ||||||
|               <div class="shape"></div> |               <div class="shape"></div> | ||||||
|             </a> |             </a> | ||||||
|         <div class="status"><span class="badge badge-primary" i18n="accelerator.sent-state">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]="!isAcceleration && !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]="!isAcceleration && !tx.status.confirmed"></div> |             <div class="seen-to-acc left"></div> | ||||||
|         <div class="acc-to-confirmed right" [class.loading]="isAcceleration && !tx.status.confirmed"></div> |             <div class="acc-to-confirmed right loading"></div> | ||||||
|         <a class="shape-border" [class.accelerated-selected]="isAcceleration && !tx.status.confirmed" [class.waiting]="!isAcceleration && !tx.status.confirmed"> |             <a class="shape-border accelerated-selected"> | ||||||
|           <div class="shape"></div> |               <div class="shape accelerating"></div> | ||||||
|             </a> |             </a> | ||||||
|         <div class="status"><span class="badge" [class]="tx.status.confirmed || isAcceleration ? 'badge-accelerated' : 'badge-waiting'" i18n="transaction.audit.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 sm-margin"> | ||||||
|               <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]="isAcceleration && !tx.status.confirmed"></div> |             <div class="acc-to-confirmed loading"></div> | ||||||
|           </div> |           </div> | ||||||
|       <div class="node" [id]="'confirmed'" [class.mined]="tx.status.confirmed"> |           <div class="node" [id]="'confirmed'"> | ||||||
|         <div class="acc-to-confirmed left" [class.loading]="isAcceleration && !tx.status.confirmed"></div> |             <div class="acc-to-confirmed left loading"></div> | ||||||
|         <a class="shape-border" [class.mined-selected]="tx.status.confirmed" [class.waiting]="!tx.status.confirmed"> |             <div class="corner-up"></div> | ||||||
|  |             <a class="shape-border waiting"> | ||||||
|               <div class="shape"></div> |               <div class="shape"></div> | ||||||
|             </a> |             </a> | ||||||
|         <div class="status"><span class="badge" [class]="tx.status.confirmed ? 'badge-success' : '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 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> | ||||||
|       </div> |       </div> | ||||||
|   </div> |       <div class="timeline"> | ||||||
| 
 |         <div class="intervals"> | ||||||
|   <ng-template #nodeSpacer> |  | ||||||
|           <div class="node-spacer"></div> |           <div class="node-spacer"></div> | ||||||
|   </ng-template> |  | ||||||
| 
 |  | ||||||
|   <ng-template #intervalSpacer> |  | ||||||
|           <div class="interval-spacer"></div> |           <div class="interval-spacer"></div> | ||||||
|   </ng-template> |           <div class="node-spacer"></div> | ||||||
| 
 |           <div class="interval"> | ||||||
|  |             <div class="interval-time"> | ||||||
|  |               ~<app-time [time]="standardETA / 1000 - now"></app-time> <span *ngIf="accelerateRatio > 1" style="font-style: italic; color: var(--transparent-fg);"> ({{ accelerateRatio }}x slower)</span> | ||||||
|             </div> |             </div> | ||||||
|  |           </div> | ||||||
|  |           <div class="node-spacer"></div> | ||||||
|  |         </div> | ||||||
|  |         <div class="nodes"> | ||||||
|  |           <div class="node-spacer"></div> | ||||||
|  |           <div class="interval-spacer"></div> | ||||||
|  |           <div class="node-spacer"> | ||||||
|  |             <div class="connector"><div class="corner-down"></div></div> | ||||||
|  |             <div class="seen-to-acc right"></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> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | } | ||||||
| @ -84,10 +84,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 +115,26 @@ | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     .corner-up { | ||||||
|  |       position: absolute; | ||||||
|  |       left: -5px; | ||||||
|  |       left: 48.5%; | ||||||
|  |       height: 86px; | ||||||
|  |       border-left: solid 10px var(--primary); | ||||||
|  |       border-bottom: solid 10px var(--primary); | ||||||
|  |       border-bottom-right-radius: 10px; | ||||||
|  |       // horrible css: | ||||||
|  |       @media (max-width: 1030px) { | ||||||
|  |         left: 48%;  | ||||||
|  |       } | ||||||
|  |       @media (max-width: 850px) { | ||||||
|  |         left: 47%; | ||||||
|  |       } | ||||||
|  |       @media (max-width: 700px) { | ||||||
|  |         left: 46%; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |      | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   .nodes { |   .nodes { | ||||||
| @ -142,6 +158,9 @@ | |||||||
|           height: 100%; |           height: 100%; | ||||||
|           border-radius: 50%; |           border-radius: 50%; | ||||||
|           background: white; |           background: white; | ||||||
|  |           &.accelerating { | ||||||
|  |             animation: acceleratePulse 1s infinite; | ||||||
|  |           } | ||||||
|           transition: background-color 300ms, border 300ms; |           transition: background-color 300ms, border 300ms; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -151,12 +170,6 @@ | |||||||
|           } |           } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         &.sent-selected { |  | ||||||
|           .shape { |  | ||||||
|             background: var(--primary); |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         &.accelerated-selected { |         &.accelerated-selected { | ||||||
|           .shape { |           .shape { | ||||||
|             background: var(--tertiary); |             background: var(--tertiary); | ||||||
| @ -190,6 +203,30 @@ | |||||||
|         font-size: 12px; |         font-size: 12px; | ||||||
|         line-height: 16px; |         line-height: 16px; | ||||||
|         white-space: nowrap; |         white-space: nowrap; | ||||||
|  | 
 | ||||||
|  |         &.sm-margin { | ||||||
|  |           @media (max-width: 650px) { | ||||||
|  |             margin-left: 20px; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .connector { | ||||||
|  |       position: relative; | ||||||
|  |       height: 10px; | ||||||
|  |      | ||||||
|  |       .corner-down { | ||||||
|  |         position: absolute; | ||||||
|  |         @media (max-width: 650px) { | ||||||
|  |           width: 223px; | ||||||
|  |         } | ||||||
|  |         width: 290px; | ||||||
|  |         height: 90px; | ||||||
|  |         bottom: 50%; | ||||||
|  |         border-left: solid 10px var(--primary); | ||||||
|  |         border-bottom: solid 10px var(--primary); | ||||||
|  |         border-bottom-left-radius: 10px; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @ -201,9 +238,8 @@ | |||||||
|   100% { background-color: var(--tertiary) } |   100% { background-color: var(--tertiary) } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @keyframes standardPulse { | @keyframes textPulse { | ||||||
|   0% { background-color: var(--primary) } |   0% { color: var(--tertiary) } | ||||||
|   50% { background-color: var(--secondary) } |   50% { color: var(--mainnet-alt) } | ||||||
|   100% { background-color: var(--primary) } |   100% { color: var(--tertiary) } | ||||||
|    |  | ||||||
| } | } | ||||||
| @ -11,9 +11,14 @@ export class AccelerationTimelineComponent implements OnInit, OnChanges { | |||||||
|   @Input() transactionTime: number; |   @Input() transactionTime: number; | ||||||
|   @Input() tx: Transaction; |   @Input() tx: Transaction; | ||||||
|   @Input() eta: ETA; |   @Input() eta: ETA; | ||||||
|   @Input() isAcceleration: boolean; |   // 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; | ||||||
|  |   now: number; | ||||||
|  |   accelerateRatio: number; | ||||||
| 
 | 
 | ||||||
|   constructor() {} |   constructor() {} | ||||||
| 
 | 
 | ||||||
| @ -22,6 +27,15 @@ export class AccelerationTimelineComponent implements OnInit, OnChanges { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   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)); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -152,15 +152,6 @@ | |||||||
| 
 | 
 | ||||||
|     <br> |     <br> | ||||||
| 
 | 
 | ||||||
|     <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)" [isAcceleration]="isAcceleration"></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> | ||||||
| @ -170,6 +161,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> | ||||||
|  | |||||||
| @ -112,6 +112,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>; | ||||||
| @ -809,6 +810,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); | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -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