97 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			97 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import { Component, OnInit, Input, Output, OnChanges, EventEmitter, HostListener, Inject, LOCALE_ID } from '@angular/core';
 | |
| import { StateService } from '../../services/state.service';
 | |
| import { Outspend, Transaction, Vin, Vout } from '../../interfaces/electrs.interface';
 | |
| import { Router } from '@angular/router';
 | |
| import { ReplaySubject, merge, Subscription, of } from 'rxjs';
 | |
| import { tap, switchMap } from 'rxjs/operators';
 | |
| import { ApiService } from '../../services/api.service';
 | |
| import { AccelerationEstimate, RateOption } from './accelerate-preview.component';
 | |
| 
 | |
| interface GraphBar {
 | |
|   rate: number;
 | |
|   style: any;
 | |
|   class: 'tx' | 'target' | 'max';
 | |
|   label: string;
 | |
|   active?: boolean;
 | |
|   rateIndex?: number;
 | |
|   fee?: number;
 | |
| }
 | |
| 
 | |
| @Component({
 | |
|   selector: 'app-accelerate-fee-graph',
 | |
|   templateUrl: './accelerate-fee-graph.component.html',
 | |
|   styleUrls: ['./accelerate-fee-graph.component.scss'],
 | |
| })
 | |
| export class AccelerateFeeGraphComponent implements OnInit, OnChanges {
 | |
|   @Input() tx: Transaction;
 | |
|   @Input() estimate: AccelerationEstimate;
 | |
|   @Input() maxRateOptions: RateOption[] = [];
 | |
|   @Input() maxRateIndex: number = 0;
 | |
|   @Output() setUserBid = new EventEmitter<{ fee: number, index: number }>();
 | |
| 
 | |
|   bars: GraphBar[] = [];
 | |
|   tooltipPosition = { x: 0, y: 0 };
 | |
| 
 | |
|   ngOnInit(): void {
 | |
|     this.initGraph();
 | |
|   }
 | |
| 
 | |
|   ngOnChanges(): void {
 | |
|     this.initGraph();
 | |
|   }
 | |
| 
 | |
|   initGraph(): void {
 | |
|     if (!this.tx || !this.estimate) {
 | |
|       return;
 | |
|     }
 | |
|     const maxRate = Math.max(...this.maxRateOptions.map(option => option.rate));
 | |
|     const baseRate = this.estimate.txSummary.effectiveFee / this.estimate.txSummary.effectiveVsize;
 | |
|     const baseHeight = baseRate / maxRate;
 | |
|     const bars: GraphBar[] = this.maxRateOptions.slice().reverse().map(option => {
 | |
|       return {
 | |
|         rate: option.rate,
 | |
|         style: this.getStyle(option.rate, maxRate, baseHeight),
 | |
|         class: 'max',
 | |
|         label: 'maximum',
 | |
|         active: option.index === this.maxRateIndex,
 | |
|         rateIndex: option.index,
 | |
|         fee: option.fee,
 | |
|       }
 | |
|     });
 | |
|     bars.push({
 | |
|       rate: this.estimate.targetFeeRate,
 | |
|       style: this.getStyle(this.estimate.targetFeeRate, maxRate, baseHeight),
 | |
|       class: 'target',
 | |
|       label: 'next block',
 | |
|       fee: this.estimate.nextBlockFee - this.estimate.txSummary.effectiveFee
 | |
|     });
 | |
|     bars.push({
 | |
|       rate: baseRate,
 | |
|       style: this.getStyle(baseRate, maxRate, 0),
 | |
|       class: 'tx',
 | |
|       label: '',
 | |
|       fee: this.estimate.txSummary.effectiveFee,
 | |
|     });
 | |
|     this.bars = bars;
 | |
|   }
 | |
| 
 | |
|   getStyle(rate, maxRate, base) {
 | |
|     const top = (rate / maxRate);
 | |
|     return {
 | |
|       height: `${(top - base) * 100}%`,
 | |
|       bottom: base ? `${base * 100}%` : '0',
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   onClick(event, bar): void {
 | |
|     if (bar.rateIndex != null) {
 | |
|       this.setUserBid.emit({ fee: bar.fee, index: bar.rateIndex });
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   @HostListener('pointermove', ['$event'])
 | |
|   onPointerMove(event) {
 | |
|     this.tooltipPosition = { x: event.offsetX, y: event.offsetY };
 | |
|   }
 | |
| }
 |