[accelerator] improve rendering of acceleration fee rate graph
This commit is contained in:
		
							parent
							
								
									c43b567847
								
							
						
					
					
						commit
						9396a4bbae
					
				@ -1,4 +1,4 @@
 | 
			
		||||
<div class="fee-graph" *ngIf="tx && estimate">
 | 
			
		||||
<div class="fee-graph" *ngIf="tx && estimate" #feeGraph>
 | 
			
		||||
  <div class="column">
 | 
			
		||||
    <ng-container *ngFor="let bar of bars">
 | 
			
		||||
      <div class="bar {{ bar.class }}" [class.active]="bar.active" [style]="bar.style" (click)="onClick($event, bar);">
 | 
			
		||||
 | 
			
		||||
@ -1,20 +1,16 @@
 | 
			
		||||
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 { Component, Input, Output, OnChanges, EventEmitter, HostListener, OnInit, ViewChild, ElementRef, AfterViewInit, OnDestroy, ChangeDetectorRef } from '@angular/core';
 | 
			
		||||
import { Transaction } from '../../interfaces/electrs.interface';
 | 
			
		||||
import { AccelerationEstimate, RateOption } from './accelerate-checkout.component';
 | 
			
		||||
 | 
			
		||||
interface GraphBar {
 | 
			
		||||
  rate: number;
 | 
			
		||||
  style: any;
 | 
			
		||||
  style?: Record<string,string>;
 | 
			
		||||
  class: 'tx' | 'target' | 'max';
 | 
			
		||||
  label: string;
 | 
			
		||||
  active?: boolean;
 | 
			
		||||
  rateIndex?: number;
 | 
			
		||||
  fee?: number;
 | 
			
		||||
  height?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
@ -22,7 +18,7 @@ interface GraphBar {
 | 
			
		||||
  templateUrl: './accelerate-fee-graph.component.html',
 | 
			
		||||
  styleUrls: ['./accelerate-fee-graph.component.scss'],
 | 
			
		||||
})
 | 
			
		||||
export class AccelerateFeeGraphComponent implements OnInit, OnChanges {
 | 
			
		||||
export class AccelerateFeeGraphComponent implements OnInit, AfterViewInit, OnChanges, OnDestroy {
 | 
			
		||||
  @Input() tx: Transaction;
 | 
			
		||||
  @Input() estimate: AccelerationEstimate;
 | 
			
		||||
  @Input() showEstimate = false;
 | 
			
		||||
@ -30,13 +26,37 @@ export class AccelerateFeeGraphComponent implements OnInit, OnChanges {
 | 
			
		||||
  @Input() maxRateIndex: number = 0;
 | 
			
		||||
  @Output() setUserBid = new EventEmitter<{ fee: number, index: number }>();
 | 
			
		||||
 | 
			
		||||
  @ViewChild('feeGraph')
 | 
			
		||||
  container: ElementRef<HTMLDivElement>;
 | 
			
		||||
  height: number;
 | 
			
		||||
  observer: ResizeObserver;
 | 
			
		||||
  stopResizeLoop = false;
 | 
			
		||||
 | 
			
		||||
  bars: GraphBar[] = [];
 | 
			
		||||
  tooltipPosition = { x: 0, y: 0 };
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private cd: ChangeDetectorRef,
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    this.initGraph();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngAfterViewInit(): void {
 | 
			
		||||
    if (ResizeObserver) {
 | 
			
		||||
      this.observer = new ResizeObserver(entries => {
 | 
			
		||||
        for (const entry of entries) {
 | 
			
		||||
          this.height = entry.contentRect.height;
 | 
			
		||||
          this.initGraph();
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
      this.observer.observe(this.container.nativeElement);
 | 
			
		||||
    } else {
 | 
			
		||||
      this.startResizeFallbackLoop();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnChanges(): void {
 | 
			
		||||
    this.initGraph();
 | 
			
		||||
  }
 | 
			
		||||
@ -45,44 +65,61 @@ export class AccelerateFeeGraphComponent implements OnInit, OnChanges {
 | 
			
		||||
    if (!this.tx || !this.estimate) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    const hasNextBlockRate = (this.estimate.nextBlockFee > this.estimate.txSummary.effectiveFee);
 | 
			
		||||
    const numBars = hasNextBlockRate ? 4 : 3;
 | 
			
		||||
    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: this.showEstimate ? $localize`maximum` : $localize`:@@25fbf6e80a945703c906a5a7d8c92e8729c7ab21:accelerated`,
 | 
			
		||||
        active: option.index === this.maxRateIndex,
 | 
			
		||||
        rateIndex: option.index,
 | 
			
		||||
        fee: option.fee,
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    if (this.estimate.nextBlockFee > this.estimate.txSummary.effectiveFee) {
 | 
			
		||||
    let baseHeight = Math.max(this.height - (numBars * 30), this.height * (baseRate / maxRate));
 | 
			
		||||
    const bars: GraphBar[] = [];
 | 
			
		||||
    let lastHeight = 0;
 | 
			
		||||
    if (hasNextBlockRate) {
 | 
			
		||||
      lastHeight = Math.max(lastHeight + 30, (this.height * ((this.estimate.targetFeeRate - baseRate) / maxRate)));
 | 
			
		||||
      bars.push({
 | 
			
		||||
        rate: this.estimate.targetFeeRate,
 | 
			
		||||
        style: this.getStyle(this.estimate.targetFeeRate, maxRate, baseHeight),
 | 
			
		||||
        height: lastHeight,
 | 
			
		||||
        class: 'target',
 | 
			
		||||
        label: $localize`:@@bdf0e930eb22431140a2eaeacd809cc5f8ebd38c:Next Block`.toLowerCase(),
 | 
			
		||||
        fee: this.estimate.nextBlockFee - this.estimate.txSummary.effectiveFee
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
    this.maxRateOptions.forEach((option, index) => {
 | 
			
		||||
      lastHeight = Math.max(lastHeight + 30, (this.height * ((option.rate - baseRate) / maxRate)));
 | 
			
		||||
      bars.push({
 | 
			
		||||
        rate: option.rate,
 | 
			
		||||
        height: lastHeight,
 | 
			
		||||
        class: 'max',
 | 
			
		||||
        label: this.showEstimate ? $localize`maximum` : $localize`:@@25fbf6e80a945703c906a5a7d8c92e8729c7ab21:accelerated`,
 | 
			
		||||
        active: option.index === this.maxRateIndex,
 | 
			
		||||
        rateIndex: option.index,
 | 
			
		||||
        fee: option.fee,
 | 
			
		||||
      })
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    bars.reverse();
 | 
			
		||||
 | 
			
		||||
    baseHeight = this.height - lastHeight;
 | 
			
		||||
 | 
			
		||||
    for (const bar of bars) {
 | 
			
		||||
      bar.style = this.getStyle(bar.height, baseHeight);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bars.push({
 | 
			
		||||
      rate: baseRate,
 | 
			
		||||
      style: this.getStyle(baseRate, maxRate, 0),
 | 
			
		||||
      style: this.getStyle(baseHeight, 0),
 | 
			
		||||
      height: baseHeight,
 | 
			
		||||
      class: 'tx',
 | 
			
		||||
      label: '',
 | 
			
		||||
      fee: this.estimate.txSummary.effectiveFee,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    this.bars = bars;
 | 
			
		||||
    this.cd.detectChanges();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getStyle(rate, maxRate, base) {
 | 
			
		||||
    const top = (rate / maxRate);
 | 
			
		||||
  getStyle(height: number, base: number): Record<string,string> {
 | 
			
		||||
    return {
 | 
			
		||||
      height: `${(top - base) * 100}%`,
 | 
			
		||||
      bottom: base ? `${base * 100}%` : '0',
 | 
			
		||||
      height: `${height}px`,
 | 
			
		||||
      bottom: base ? `${base}px` : '0',
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -96,4 +133,20 @@ export class AccelerateFeeGraphComponent implements OnInit, OnChanges {
 | 
			
		||||
  onPointerMove(event) {
 | 
			
		||||
    this.tooltipPosition = { x: event.offsetX, y: event.offsetY };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  startResizeFallbackLoop(): void {
 | 
			
		||||
    if (this.stopResizeLoop) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    requestAnimationFrame(() => {
 | 
			
		||||
      this.height = this.container?.nativeElement?.clientHeight || 0;
 | 
			
		||||
      this.initGraph();
 | 
			
		||||
      this.startResizeFallbackLoop();
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnDestroy(): void {
 | 
			
		||||
    this.stopResizeLoop = true;
 | 
			
		||||
    this.observer.disconnect();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user