Merge pull request #2627 from mononaut/fix-coinbase-flow-nav
Fix flow diagram navigation for coinbases & peg-ins
This commit is contained in:
		
						commit
						5c6060780b
					
				@ -210,8 +210,6 @@
 | 
				
			|||||||
            [network]="network"
 | 
					            [network]="network"
 | 
				
			||||||
            [tooltip]="true"
 | 
					            [tooltip]="true"
 | 
				
			||||||
            [inputIndex]="inputIndex" [outputIndex]="outputIndex"
 | 
					            [inputIndex]="inputIndex" [outputIndex]="outputIndex"
 | 
				
			||||||
            (selectInput)="selectInput($event)"
 | 
					 | 
				
			||||||
            (selectOutput)="selectOutput($event)"
 | 
					 | 
				
			||||||
          >
 | 
					          >
 | 
				
			||||||
          </tx-bowtie-graph>
 | 
					          </tx-bowtie-graph>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
				
			|||||||
@ -18,6 +18,7 @@ import { ApiService } from '../../services/api.service';
 | 
				
			|||||||
import { SeoService } from '../../services/seo.service';
 | 
					import { SeoService } from '../../services/seo.service';
 | 
				
			||||||
import { BlockExtended, CpfpInfo } from '../../interfaces/node-api.interface';
 | 
					import { BlockExtended, CpfpInfo } from '../../interfaces/node-api.interface';
 | 
				
			||||||
import { LiquidUnblinding } from './liquid-ublinding';
 | 
					import { LiquidUnblinding } from './liquid-ublinding';
 | 
				
			||||||
 | 
					import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-transaction',
 | 
					  selector: 'app-transaction',
 | 
				
			||||||
@ -40,6 +41,8 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
 | 
				
			|||||||
  txReplacedSubscription: Subscription;
 | 
					  txReplacedSubscription: Subscription;
 | 
				
			||||||
  blocksSubscription: Subscription;
 | 
					  blocksSubscription: Subscription;
 | 
				
			||||||
  queryParamsSubscription: Subscription;
 | 
					  queryParamsSubscription: Subscription;
 | 
				
			||||||
 | 
					  urlFragmentSubscription: Subscription;
 | 
				
			||||||
 | 
					  fragmentParams: URLSearchParams;
 | 
				
			||||||
  rbfTransaction: undefined | Transaction;
 | 
					  rbfTransaction: undefined | Transaction;
 | 
				
			||||||
  cpfpInfo: CpfpInfo | null;
 | 
					  cpfpInfo: CpfpInfo | null;
 | 
				
			||||||
  showCpfpDetails = false;
 | 
					  showCpfpDetails = false;
 | 
				
			||||||
@ -67,6 +70,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
 | 
				
			|||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    private route: ActivatedRoute,
 | 
					    private route: ActivatedRoute,
 | 
				
			||||||
    private router: Router,
 | 
					    private router: Router,
 | 
				
			||||||
 | 
					    private relativeUrlPipe: RelativeUrlPipe,
 | 
				
			||||||
    private electrsApiService: ElectrsApiService,
 | 
					    private electrsApiService: ElectrsApiService,
 | 
				
			||||||
    private stateService: StateService,
 | 
					    private stateService: StateService,
 | 
				
			||||||
    private websocketService: WebsocketService,
 | 
					    private websocketService: WebsocketService,
 | 
				
			||||||
@ -93,6 +97,14 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
 | 
				
			|||||||
        map((da) => da.timeAvg)
 | 
					        map((da) => da.timeAvg)
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.urlFragmentSubscription = this.route.fragment.subscribe((fragment) => {
 | 
				
			||||||
 | 
					      this.fragmentParams = new URLSearchParams(fragment || '');
 | 
				
			||||||
 | 
					      const vin = parseInt(this.fragmentParams.get('vin'), 10);
 | 
				
			||||||
 | 
					      const vout = parseInt(this.fragmentParams.get('vout'), 10);
 | 
				
			||||||
 | 
					      this.inputIndex = (!isNaN(vin) && vin >= 0) ? vin : null;
 | 
				
			||||||
 | 
					      this.outputIndex = (!isNaN(vout) && vout >= 0) ? vout : null;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.fetchCpfpSubscription = this.fetchCpfp$
 | 
					    this.fetchCpfpSubscription = this.fetchCpfp$
 | 
				
			||||||
      .pipe(
 | 
					      .pipe(
 | 
				
			||||||
        switchMap((txId) =>
 | 
					        switchMap((txId) =>
 | 
				
			||||||
@ -132,13 +144,29 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
 | 
				
			|||||||
        switchMap((params: ParamMap) => {
 | 
					        switchMap((params: ParamMap) => {
 | 
				
			||||||
          const urlMatch = (params.get('id') || '').split(':');
 | 
					          const urlMatch = (params.get('id') || '').split(':');
 | 
				
			||||||
          if (urlMatch.length === 2 && urlMatch[1].length === 64) {
 | 
					          if (urlMatch.length === 2 && urlMatch[1].length === 64) {
 | 
				
			||||||
            this.inputIndex = parseInt(urlMatch[0], 10);
 | 
					            const vin = parseInt(urlMatch[0], 10);
 | 
				
			||||||
            this.outputIndex = null;
 | 
					 | 
				
			||||||
            this.txId = urlMatch[1];
 | 
					            this.txId = urlMatch[1];
 | 
				
			||||||
 | 
					            // rewrite legacy vin syntax
 | 
				
			||||||
 | 
					            if (!isNaN(vin)) {
 | 
				
			||||||
 | 
					              this.fragmentParams.set('vin', vin.toString());
 | 
				
			||||||
 | 
					              this.fragmentParams.delete('vout');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            this.router.navigate([this.relativeUrlPipe.transform('/tx'), this.txId], {
 | 
				
			||||||
 | 
					              queryParamsHandling: 'merge',
 | 
				
			||||||
 | 
					              fragment: this.fragmentParams.toString(),
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
            this.txId = urlMatch[0];
 | 
					            this.txId = urlMatch[0];
 | 
				
			||||||
            this.outputIndex = urlMatch[1] === undefined ? null : parseInt(urlMatch[1], 10);
 | 
					            const vout = parseInt(urlMatch[1], 10);
 | 
				
			||||||
            this.inputIndex = null;
 | 
					            if (urlMatch.length > 1 && !isNaN(vout)) {
 | 
				
			||||||
 | 
					              // rewrite legacy vout syntax
 | 
				
			||||||
 | 
					              this.fragmentParams.set('vout', vout.toString());
 | 
				
			||||||
 | 
					              this.fragmentParams.delete('vin');
 | 
				
			||||||
 | 
					              this.router.navigate([this.relativeUrlPipe.transform('/tx'), this.txId], {
 | 
				
			||||||
 | 
					                queryParamsHandling: 'merge',
 | 
				
			||||||
 | 
					                fragment: this.fragmentParams.toString(),
 | 
				
			||||||
 | 
					              });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          this.seoService.setTitle(
 | 
					          this.seoService.setTitle(
 | 
				
			||||||
            $localize`:@@bisq.transaction.browser-title:Transaction: ${this.txId}:INTERPOLATION:`
 | 
					            $localize`:@@bisq.transaction.browser-title:Transaction: ${this.txId}:INTERPOLATION:`
 | 
				
			||||||
@ -222,6 +250,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
 | 
				
			|||||||
              this.fetchCpfp$.next(this.tx.txid);
 | 
					              this.fetchCpfp$.next(this.tx.txid);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					          setTimeout(() => { this.applyFragment(); }, 0);
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        (error) => {
 | 
					        (error) => {
 | 
				
			||||||
          this.error = error;
 | 
					          this.error = error;
 | 
				
			||||||
@ -359,14 +388,15 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
 | 
				
			|||||||
    this.graphExpanded = false;
 | 
					    this.graphExpanded = false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  selectInput(input) {
 | 
					  // simulate normal anchor fragment behavior
 | 
				
			||||||
    this.inputIndex = input;
 | 
					  applyFragment(): void {
 | 
				
			||||||
    this.outputIndex = null;
 | 
					    const anchor = Array.from(this.fragmentParams.entries()).find(([frag, value]) => value === '');
 | 
				
			||||||
  }
 | 
					    if (anchor) {
 | 
				
			||||||
 | 
					      const anchorElement = document.getElementById(anchor[0]);
 | 
				
			||||||
  selectOutput(output) {
 | 
					      if (anchorElement) {
 | 
				
			||||||
    this.outputIndex = output;
 | 
					        anchorElement.scrollIntoView();
 | 
				
			||||||
    this.inputIndex = null;
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @HostListener('window:resize', ['$event'])
 | 
					  @HostListener('window:resize', ['$event'])
 | 
				
			||||||
@ -383,6 +413,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
 | 
				
			|||||||
    this.blocksSubscription.unsubscribe();
 | 
					    this.blocksSubscription.unsubscribe();
 | 
				
			||||||
    this.queryParamsSubscription.unsubscribe();
 | 
					    this.queryParamsSubscription.unsubscribe();
 | 
				
			||||||
    this.flowPrefSubscription.unsubscribe();
 | 
					    this.flowPrefSubscription.unsubscribe();
 | 
				
			||||||
 | 
					    this.urlFragmentSubscription.unsubscribe();
 | 
				
			||||||
    this.leaveTransaction();
 | 
					    this.leaveTransaction();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -43,7 +43,7 @@
 | 
				
			|||||||
                      </ng-template>
 | 
					                      </ng-template>
 | 
				
			||||||
                    </ng-template>
 | 
					                    </ng-template>
 | 
				
			||||||
                    <ng-template #defaultPrevout>
 | 
					                    <ng-template #defaultPrevout>
 | 
				
			||||||
                      <a [routerLink]="['/tx/' | relativeUrl, vin.txid + ':' + vin.vout]" class="red">
 | 
					                      <a [routerLink]="['/tx/' | relativeUrl, vin.txid]" [fragment]="'vout=' + vin.vout" class="red">
 | 
				
			||||||
                        <fa-icon [icon]="['fas', 'arrow-alt-circle-right']" [fixedWidth]="true"></fa-icon>
 | 
					                        <fa-icon [icon]="['fas', 'arrow-alt-circle-right']" [fixedWidth]="true"></fa-icon>
 | 
				
			||||||
                      </a>
 | 
					                      </a>
 | 
				
			||||||
                    </ng-template>
 | 
					                    </ng-template>
 | 
				
			||||||
@ -220,7 +220,7 @@
 | 
				
			|||||||
                      <fa-icon [icon]="['fas', 'arrow-alt-circle-right']" [fixedWidth]="true"></fa-icon>
 | 
					                      <fa-icon [icon]="['fas', 'arrow-alt-circle-right']" [fixedWidth]="true"></fa-icon>
 | 
				
			||||||
                    </span>
 | 
					                    </span>
 | 
				
			||||||
                    <ng-template #spent>
 | 
					                    <ng-template #spent>
 | 
				
			||||||
                      <a *ngIf="tx._outspends[vindex].txid else outputNoTxId" [routerLink]="['/tx/' | relativeUrl, tx._outspends[vindex].vin + ':' + tx._outspends[vindex].txid]" class="red">
 | 
					                      <a *ngIf="tx._outspends[vindex].txid else outputNoTxId" [routerLink]="['/tx/' | relativeUrl, tx._outspends[vindex].txid]"  [fragment]="'vin=' + tx._outspends[vindex].vin" class="red">
 | 
				
			||||||
                        <fa-icon [icon]="['fas', 'arrow-alt-circle-right']" [fixedWidth]="true"></fa-icon>
 | 
					                        <fa-icon [icon]="['fas', 'arrow-alt-circle-right']" [fixedWidth]="true"></fa-icon>
 | 
				
			||||||
                      </a>
 | 
					                      </a>
 | 
				
			||||||
                      <ng-template #outputNoTxId>
 | 
					                      <ng-template #outputNoTxId>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { Component, OnInit, Input, Output, EventEmitter, OnChanges, HostListener } from '@angular/core';
 | 
					import { Component, OnInit, Input, OnChanges, HostListener } from '@angular/core';
 | 
				
			||||||
import { StateService } from '../../services/state.service';
 | 
					import { StateService } from '../../services/state.service';
 | 
				
			||||||
import { Outspend, Transaction } from '../../interfaces/electrs.interface';
 | 
					import { Outspend, Transaction } from '../../interfaces/electrs.interface';
 | 
				
			||||||
import { Router } from '@angular/router';
 | 
					import { Router } from '@angular/router';
 | 
				
			||||||
@ -43,9 +43,6 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
 | 
				
			|||||||
  @Input() inputIndex: number;
 | 
					  @Input() inputIndex: number;
 | 
				
			||||||
  @Input() outputIndex: number;
 | 
					  @Input() outputIndex: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Output() selectInput = new EventEmitter<number>();
 | 
					 | 
				
			||||||
  @Output() selectOutput = new EventEmitter<number>();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  inputData: Xput[];
 | 
					  inputData: Xput[];
 | 
				
			||||||
  outputData: Xput[];
 | 
					  outputData: Xput[];
 | 
				
			||||||
  inputs: SvgLine[];
 | 
					  inputs: SvgLine[];
 | 
				
			||||||
@ -368,24 +365,42 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
 | 
				
			|||||||
  onClick(event, side, index): void {
 | 
					  onClick(event, side, index): void {
 | 
				
			||||||
    if (side === 'input') {
 | 
					    if (side === 'input') {
 | 
				
			||||||
      const input = this.tx.vin[index];
 | 
					      const input = this.tx.vin[index];
 | 
				
			||||||
      if (input && input.txid && input.vout != null) {
 | 
					      if (input && !input.is_coinbase && !input.is_pegin && input.txid && input.vout != null) {
 | 
				
			||||||
        this.router.navigate([this.relativeUrlPipe.transform('/tx'), input.txid + ':' + input.vout], {
 | 
					        this.router.navigate([this.relativeUrlPipe.transform('/tx'), input.txid], {
 | 
				
			||||||
          queryParamsHandling: 'merge',
 | 
					          queryParamsHandling: 'merge',
 | 
				
			||||||
          fragment: 'flow'
 | 
					          fragment: (new URLSearchParams({
 | 
				
			||||||
 | 
					            flow: '',
 | 
				
			||||||
 | 
					            vout: input.vout.toString(),
 | 
				
			||||||
 | 
					          })).toString(),
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      } else if (index != null) {
 | 
				
			||||||
 | 
					        this.router.navigate([this.relativeUrlPipe.transform('/tx'), this.tx.txid], {
 | 
				
			||||||
 | 
					          queryParamsHandling: 'merge',
 | 
				
			||||||
 | 
					          fragment: (new URLSearchParams({
 | 
				
			||||||
 | 
					            flow: '',
 | 
				
			||||||
 | 
					            vin: index.toString(),
 | 
				
			||||||
 | 
					          })).toString(),
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        this.selectInput.emit(index);
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      const output = this.tx.vout[index];
 | 
					      const output = this.tx.vout[index];
 | 
				
			||||||
      const outspend = this.outspends[index];
 | 
					      const outspend = this.outspends[index];
 | 
				
			||||||
      if (output && outspend && outspend.spent && outspend.txid) {
 | 
					      if (output && outspend && outspend.spent && outspend.txid) {
 | 
				
			||||||
        this.router.navigate([this.relativeUrlPipe.transform('/tx'), outspend.vin + ':' + outspend.txid], {
 | 
					        this.router.navigate([this.relativeUrlPipe.transform('/tx'), outspend.txid], {
 | 
				
			||||||
          queryParamsHandling: 'merge',
 | 
					          queryParamsHandling: 'merge',
 | 
				
			||||||
          fragment: 'flow'
 | 
					          fragment: (new URLSearchParams({
 | 
				
			||||||
 | 
					            flow: '',
 | 
				
			||||||
 | 
					            vin: outspend.vin.toString(),
 | 
				
			||||||
 | 
					          })).toString(),
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      } else if (index != null) {
 | 
				
			||||||
 | 
					        this.router.navigate([this.relativeUrlPipe.transform('/tx'), this.tx.txid], {
 | 
				
			||||||
 | 
					          queryParamsHandling: 'merge',
 | 
				
			||||||
 | 
					          fragment: (new URLSearchParams({
 | 
				
			||||||
 | 
					            flow: '',
 | 
				
			||||||
 | 
					            vout: index.toString(),
 | 
				
			||||||
 | 
					          })).toString(),
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        this.selectOutput.emit(index);
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,8 @@
 | 
				
			|||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "lib": [
 | 
					    "lib": [
 | 
				
			||||||
      "es2018",
 | 
					      "es2018",
 | 
				
			||||||
      "dom"
 | 
					      "dom",
 | 
				
			||||||
 | 
					      "dom.iterable"
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "angularCompilerOptions": {
 | 
					  "angularCompilerOptions": {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user