Scroll to input/output when clicked in tx diagram
This commit is contained in:
		
							parent
							
								
									54c44565fb
								
							
						
					
					
						commit
						c10ace8fb5
					
				@ -208,7 +208,10 @@
 | 
			
		||||
            [lineLimit]="inOutLimit"
 | 
			
		||||
            [maxStrands]="graphExpanded ? maxInOut : 24"
 | 
			
		||||
            [network]="network"
 | 
			
		||||
            [tooltip]="true">
 | 
			
		||||
            [tooltip]="true"
 | 
			
		||||
            (selectInput)="selectInput($event)"
 | 
			
		||||
            (selectOutput)="selectOutput($event)"
 | 
			
		||||
          >
 | 
			
		||||
          </tx-bowtie-graph>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="toggle-wrapper" *ngIf="maxInOut > 24">
 | 
			
		||||
@ -240,7 +243,7 @@
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    <app-transactions-list #txList [transactions]="[tx]" [errorUnblinded]="errorUnblinded" [outputIndex]="outputIndex" [transactionPage]="true"></app-transactions-list>
 | 
			
		||||
    <app-transactions-list #txList [transactions]="[tx]" [errorUnblinded]="errorUnblinded" [inputIndex]="inputIndex" [outputIndex]="outputIndex" [transactionPage]="true"></app-transactions-list>
 | 
			
		||||
 | 
			
		||||
    <div class="title text-left">
 | 
			
		||||
      <h2 i18n="transaction.details">Details</h2>
 | 
			
		||||
 | 
			
		||||
@ -47,6 +47,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
 | 
			
		||||
  now = new Date().getTime();
 | 
			
		||||
  timeAvg$: Observable<number>;
 | 
			
		||||
  liquidUnblinding = new LiquidUnblinding();
 | 
			
		||||
  inputIndex: number;
 | 
			
		||||
  outputIndex: number;
 | 
			
		||||
  showFlow: boolean = true;
 | 
			
		||||
  graphExpanded: boolean = false;
 | 
			
		||||
@ -334,6 +335,16 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
 | 
			
		||||
    this.graphExpanded = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  selectInput(input) {
 | 
			
		||||
    this.inputIndex = input;
 | 
			
		||||
    this.outputIndex = null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  selectOutput(output) {
 | 
			
		||||
    this.outputIndex = output;
 | 
			
		||||
    this.inputIndex = null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @HostListener('window:resize', ['$event'])
 | 
			
		||||
  setGraphSize(): void {
 | 
			
		||||
    if (this.graphContainer) {
 | 
			
		||||
 | 
			
		||||
@ -20,9 +20,9 @@
 | 
			
		||||
      <div class="col">
 | 
			
		||||
        <table class="table table-borderless smaller-text table-sm table-tx-vin">
 | 
			
		||||
          <tbody>
 | 
			
		||||
            <ng-template ngFor let-vin let-vindex="index" [ngForOf]="tx['@vinLimit'] ? ((tx.vin.length > rowLimit) ? tx.vin.slice(0, rowLimit - 2) : tx.vin.slice(0, rowLimit)) : tx.vin" [ngForTrackBy]="trackByIndexFn">
 | 
			
		||||
            <ng-template ngFor let-vin let-vindex="index" [ngForOf]="tx['@vinLimit'] ? ((tx.vin.length > inputRowLimit) ? tx.vin.slice(0, inputRowLimit - 2) : tx.vin.slice(0, inputRowLimit)) : tx.vin" [ngForTrackBy]="trackByIndexFn">
 | 
			
		||||
              <tr [ngClass]="{
 | 
			
		||||
                'assetBox': assetsMinimal && vin.prevout && assetsMinimal[vin.prevout.asset] && !vin.is_coinbase && vin.prevout.scriptpubkey_address && tx._unblinded,
 | 
			
		||||
                'assetBox': (assetsMinimal && vin.prevout && assetsMinimal[vin.prevout.asset] && !vin.is_coinbase && vin.prevout.scriptpubkey_address && tx._unblinded) || inputIndex === vindex,
 | 
			
		||||
                'highlight': vin.prevout?.scriptpubkey_address === this.address && this.address !== ''
 | 
			
		||||
              }">
 | 
			
		||||
                <td class="arrow-td">
 | 
			
		||||
@ -146,7 +146,7 @@
 | 
			
		||||
                </td>
 | 
			
		||||
              </tr>
 | 
			
		||||
            </ng-template>
 | 
			
		||||
            <tr *ngIf="tx.vin.length > rowLimit && tx['@vinLimit']">
 | 
			
		||||
            <tr *ngIf="tx.vin.length > inputRowLimit && tx['@vinLimit']">
 | 
			
		||||
              <td colspan="3" class="text-center">
 | 
			
		||||
                <button class="btn btn-sm btn-primary mt-2" (click)="loadMoreInputs(tx);"><span i18n="show-all">Show all</span> ({{ tx.vin.length }})</button>
 | 
			
		||||
              </td>
 | 
			
		||||
@ -158,7 +158,7 @@
 | 
			
		||||
      <div class="col mobile-bottomcol">
 | 
			
		||||
        <table class="table table-borderless smaller-text table-sm table-tx-vout">
 | 
			
		||||
          <tbody>
 | 
			
		||||
            <ng-template ngFor let-vout let-vindex="index" [ngForOf]="tx['@voutLimit'] && !outputIndex ? ((tx.vout.length > rowLimit) ? tx.vout.slice(0, rowLimit - 2) : tx.vout.slice(0, rowLimit)) : tx.vout" [ngForTrackBy]="trackByIndexFn">
 | 
			
		||||
            <ng-template ngFor let-vout let-vindex="index" [ngForOf]="tx['@voutLimit'] ? ((tx.vout.length > outputRowLimit) ? tx.vout.slice(0, outputRowLimit - 2) : tx.vout.slice(0, outputRowLimit)) : tx.vout" [ngForTrackBy]="trackByIndexFn">
 | 
			
		||||
              <tr [ngClass]="{
 | 
			
		||||
                'assetBox': assetsMinimal && assetsMinimal[vout.asset] && vout.scriptpubkey_address && tx.vin && !tx.vin[0].is_coinbase && tx._unblinded || outputIndex === vindex,
 | 
			
		||||
                'highlight': vout.scriptpubkey_address === this.address && this.address !== ''
 | 
			
		||||
@ -257,7 +257,7 @@
 | 
			
		||||
                </td>
 | 
			
		||||
              </tr>
 | 
			
		||||
            </ng-template>
 | 
			
		||||
            <tr *ngIf="tx.vout.length > rowLimit && tx['@voutLimit'] && !outputIndex">
 | 
			
		||||
            <tr *ngIf="tx.vout.length > outputRowLimit && tx['@voutLimit']">
 | 
			
		||||
              <td colspan="3" class="text-center">
 | 
			
		||||
                <button class="btn btn-sm btn-primary mt-2" (click)="tx['@voutLimit'] = false;"><span i18n="show-all">Show all</span> ({{ tx.vout.length }})</button>
 | 
			
		||||
              </td>
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,7 @@ export class TransactionsListComponent implements OnInit, OnChanges {
 | 
			
		||||
  @Input() transactionPage = false;
 | 
			
		||||
  @Input() errorUnblinded = false;
 | 
			
		||||
  @Input() paginated = false;
 | 
			
		||||
  @Input() inputIndex: number;
 | 
			
		||||
  @Input() outputIndex: number;
 | 
			
		||||
  @Input() address: string = '';
 | 
			
		||||
  @Input() rowLimit = 12;
 | 
			
		||||
@ -37,6 +38,8 @@ export class TransactionsListComponent implements OnInit, OnChanges {
 | 
			
		||||
  showDetails$ = new BehaviorSubject<boolean>(false);
 | 
			
		||||
  assetsMinimal: any;
 | 
			
		||||
  transactionsLength: number = 0;
 | 
			
		||||
  inputRowLimit: number = 12;
 | 
			
		||||
  outputRowLimit: number = 12;
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    public stateService: StateService,
 | 
			
		||||
@ -97,50 +100,57 @@ export class TransactionsListComponent implements OnInit, OnChanges {
 | 
			
		||||
    ).subscribe(() => this.ref.markForCheck());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnChanges(): void {
 | 
			
		||||
    if (!this.transactions || !this.transactions.length) {
 | 
			
		||||
      return;
 | 
			
		||||
  ngOnChanges(changes): void {
 | 
			
		||||
    if (changes.inputIndex || changes.outputIndex || changes.rowLimit) {
 | 
			
		||||
      this.inputRowLimit = Math.max(this.rowLimit, (this.inputIndex || 0) + 3);
 | 
			
		||||
      this.outputRowLimit = Math.max(this.rowLimit, (this.outputIndex || 0) + 3);
 | 
			
		||||
      if (this.inputIndex || this.outputIndex) {
 | 
			
		||||
        setTimeout(() => {
 | 
			
		||||
          const assetBoxElements = document.getElementsByClassName('assetBox');
 | 
			
		||||
          if (assetBoxElements && assetBoxElements[0]) {
 | 
			
		||||
            assetBoxElements[0].scrollIntoView({block: "center"});
 | 
			
		||||
          }
 | 
			
		||||
        }, 10);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.transactionsLength = this.transactions.length;
 | 
			
		||||
    if (this.outputIndex) {
 | 
			
		||||
      setTimeout(() => {
 | 
			
		||||
        const assetBoxElements = document.getElementsByClassName('assetBox');
 | 
			
		||||
        if (assetBoxElements && assetBoxElements[0]) {
 | 
			
		||||
          assetBoxElements[0].scrollIntoView();
 | 
			
		||||
        }
 | 
			
		||||
      }, 10);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.transactions.forEach((tx) => {
 | 
			
		||||
      tx['@voutLimit'] = true;
 | 
			
		||||
      tx['@vinLimit'] = true;
 | 
			
		||||
      if (tx['addressValue'] !== undefined) {
 | 
			
		||||
    if (changes.transactions || changes.address) {
 | 
			
		||||
      if (!this.transactions || !this.transactions.length) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (this.address) {
 | 
			
		||||
        const addressIn = tx.vout
 | 
			
		||||
          .filter((v: Vout) => v.scriptpubkey_address === this.address)
 | 
			
		||||
          .map((v: Vout) => v.value || 0)
 | 
			
		||||
          .reduce((a: number, b: number) => a + b, 0);
 | 
			
		||||
      this.transactionsLength = this.transactions.length;
 | 
			
		||||
 | 
			
		||||
        const addressOut = tx.vin
 | 
			
		||||
          .filter((v: Vin) => v.prevout && v.prevout.scriptpubkey_address === this.address)
 | 
			
		||||
          .map((v: Vin) => v.prevout.value || 0)
 | 
			
		||||
          .reduce((a: number, b: number) => a + b, 0);
 | 
			
		||||
 | 
			
		||||
        tx['addressValue'] = addressIn - addressOut;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    const txIds = this.transactions.filter((tx) => !tx._outspends).map((tx) => tx.txid);
 | 
			
		||||
    if (txIds.length) {
 | 
			
		||||
      this.refreshOutspends$.next(txIds);
 | 
			
		||||
    }
 | 
			
		||||
    if (this.stateService.env.LIGHTNING) {
 | 
			
		||||
      const txIds = this.transactions.filter((tx) => !tx._channels).map((tx) => tx.txid);
 | 
			
		||||
      this.transactions.forEach((tx) => {
 | 
			
		||||
        tx['@voutLimit'] = true;
 | 
			
		||||
        tx['@vinLimit'] = true;
 | 
			
		||||
        if (tx['addressValue'] !== undefined) {
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.address) {
 | 
			
		||||
          const addressIn = tx.vout
 | 
			
		||||
            .filter((v: Vout) => v.scriptpubkey_address === this.address)
 | 
			
		||||
            .map((v: Vout) => v.value || 0)
 | 
			
		||||
            .reduce((a: number, b: number) => a + b, 0);
 | 
			
		||||
 | 
			
		||||
          const addressOut = tx.vin
 | 
			
		||||
            .filter((v: Vin) => v.prevout && v.prevout.scriptpubkey_address === this.address)
 | 
			
		||||
            .map((v: Vin) => v.prevout.value || 0)
 | 
			
		||||
            .reduce((a: number, b: number) => a + b, 0);
 | 
			
		||||
 | 
			
		||||
          tx['addressValue'] = addressIn - addressOut;
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
      const txIds = this.transactions.filter((tx) => !tx._outspends).map((tx) => tx.txid);
 | 
			
		||||
      if (txIds.length) {
 | 
			
		||||
        this.refreshChannels$.next(txIds);
 | 
			
		||||
        this.refreshOutspends$.next(txIds);
 | 
			
		||||
      }
 | 
			
		||||
      if (this.stateService.env.LIGHTNING) {
 | 
			
		||||
        const txIds = this.transactions.filter((tx) => !tx._channels).map((tx) => tx.txid);
 | 
			
		||||
        if (txIds.length) {
 | 
			
		||||
          this.refreshChannels$.next(txIds);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -44,7 +44,7 @@
 | 
			
		||||
          <span *ngSwitchCase="'output'" i18n="transaction.output">Output</span>
 | 
			
		||||
          <span *ngSwitchCase="'fee'" i18n="transaction.fee">Fee</span>
 | 
			
		||||
        </ng-container>
 | 
			
		||||
        <span *ngIf="line.type !== 'fee'"> #{{ line.index }}</span>
 | 
			
		||||
        <span *ngIf="line.type !== 'fee'"> #{{ line.index + 1 }}</span>
 | 
			
		||||
      </p>
 | 
			
		||||
      <p *ngIf="line.value == null && line.confidential" i18n="shared.confidential">Confidential</p>
 | 
			
		||||
      <p *ngIf="line.value != null"><app-amount [satoshis]="line.value"></app-amount></p>
 | 
			
		||||
 | 
			
		||||
@ -60,6 +60,7 @@
 | 
			
		||||
        attr.marker-start="url(#{{input.class}}-arrow)"
 | 
			
		||||
        (pointerover)="onHover($event, 'input', i);"
 | 
			
		||||
        (pointerout)="onBlur($event, 'input', i);"
 | 
			
		||||
        (click)="onClick($event, 'input', inputData[i].index);"
 | 
			
		||||
      />
 | 
			
		||||
    </ng-container>
 | 
			
		||||
    <ng-container *ngFor="let output of outputs; let i = index">
 | 
			
		||||
@ -70,6 +71,7 @@
 | 
			
		||||
        attr.marker-start="url(#{{output.class}}-arrow)"
 | 
			
		||||
        (pointerover)="onHover($event, 'output', i);"
 | 
			
		||||
        (pointerout)="onBlur($event, 'output', i);"
 | 
			
		||||
        (click)="onClick($event, 'output', outputData[i].index);"
 | 
			
		||||
      />
 | 
			
		||||
    </ng-container>
 | 
			
		||||
  </svg>
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { Component, OnInit, Input, OnChanges, HostListener } from '@angular/core';
 | 
			
		||||
import { Component, OnInit, Input, Output, EventEmitter, OnChanges, HostListener } from '@angular/core';
 | 
			
		||||
import { Transaction } from '../../interfaces/electrs.interface';
 | 
			
		||||
 | 
			
		||||
interface SvgLine {
 | 
			
		||||
@ -35,6 +35,9 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
 | 
			
		||||
  @Input() maxStrands = 24; // number of inputs/outputs to keep fully on-screen.
 | 
			
		||||
  @Input() tooltip = false;
 | 
			
		||||
 | 
			
		||||
  @Output() selectInput = new EventEmitter<number>();
 | 
			
		||||
  @Output() selectOutput = new EventEmitter<number>();
 | 
			
		||||
 | 
			
		||||
  inputData: Xput[];
 | 
			
		||||
  outputData: Xput[];
 | 
			
		||||
  inputs: SvgLine[];
 | 
			
		||||
@ -76,11 +79,12 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
 | 
			
		||||
    this.combinedWeight = Math.min(this.maxCombinedWeight, Math.floor((this.width - (2 * this.midWidth)) / 6));
 | 
			
		||||
 | 
			
		||||
    const totalValue = this.calcTotalValue(this.tx);
 | 
			
		||||
    let voutWithFee = this.tx.vout.map(v => {
 | 
			
		||||
    let voutWithFee = this.tx.vout.map((v, i) => {
 | 
			
		||||
      return {
 | 
			
		||||
        type: v.scriptpubkey_type === 'fee' ? 'fee' : 'output',
 | 
			
		||||
        value: v?.value,
 | 
			
		||||
        address: v?.scriptpubkey_address || v?.scriptpubkey_type?.toUpperCase(),
 | 
			
		||||
        index: i,
 | 
			
		||||
        pegout: v?.pegout?.scriptpubkey_address,
 | 
			
		||||
        confidential: (this.isLiquid && v?.value === undefined),
 | 
			
		||||
      } as Xput;
 | 
			
		||||
@ -91,11 +95,12 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
 | 
			
		||||
    }
 | 
			
		||||
    const outputCount = voutWithFee.length;
 | 
			
		||||
 | 
			
		||||
    let truncatedInputs = this.tx.vin.map(v => {
 | 
			
		||||
    let truncatedInputs = this.tx.vin.map((v, i) => {
 | 
			
		||||
      return {
 | 
			
		||||
        type: 'input',
 | 
			
		||||
        value: v?.prevout?.value,
 | 
			
		||||
        address: v?.prevout?.scriptpubkey_address || v?.prevout?.scriptpubkey_type?.toUpperCase(),
 | 
			
		||||
        index: i,
 | 
			
		||||
        coinbase: v?.is_coinbase,
 | 
			
		||||
        pegin: v?.is_pegin,
 | 
			
		||||
        confidential: (this.isLiquid && v?.prevout?.value === undefined),
 | 
			
		||||
@ -306,8 +311,7 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
 | 
			
		||||
      };
 | 
			
		||||
    } else {
 | 
			
		||||
      this.hoverLine = {
 | 
			
		||||
        ...this.outputData[index],
 | 
			
		||||
        index
 | 
			
		||||
        ...this.outputData[index]
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@ -315,4 +319,12 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
 | 
			
		||||
  onBlur(event, side, index): void {
 | 
			
		||||
    this.hoverLine = null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onClick(event, side, index): void {
 | 
			
		||||
    if (side === 'input') {
 | 
			
		||||
      this.selectInput.emit(index);
 | 
			
		||||
    } else {
 | 
			
		||||
      this.selectOutput.emit(index);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user