WIP: Bisq DAO support. Transactions list and details.
This commit is contained in:
@@ -0,0 +1 @@
|
||||
<fa-icon [icon]="iconProp" [fixedWidth]="true" [ngStyle]="{ 'color': '#' + color }"></fa-icon>
|
||||
81
frontend/src/app/components/bisq-icon/bisq-icon.component.ts
Normal file
81
frontend/src/app/components/bisq-icon/bisq-icon.component.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit, Input } from '@angular/core';
|
||||
import { IconPrefix, IconName } from '@fortawesome/fontawesome-common-types';
|
||||
|
||||
@Component({
|
||||
selector: 'app-bisq-icon',
|
||||
templateUrl: './bisq-icon.component.html',
|
||||
styleUrls: ['./bisq-icon.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class BisqIconComponent implements OnInit {
|
||||
@Input() txType: string;
|
||||
|
||||
iconProp: [IconPrefix, IconName] = ['fas', 'leaf'];
|
||||
color: string;
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() {
|
||||
switch (this.txType) {
|
||||
case 'UNVERIFIED':
|
||||
this.iconProp[1] = 'question';
|
||||
this.color = 'ffac00';
|
||||
break;
|
||||
case 'INVALID':
|
||||
this.iconProp[1] = 'exclamation-triangle';
|
||||
this.color = 'ff4500';
|
||||
break;
|
||||
case 'GENESIS':
|
||||
this.iconProp[1] = 'rocket';
|
||||
this.color = '25B135';
|
||||
break;
|
||||
case 'TRANSFER_BSQ':
|
||||
this.iconProp[1] = 'retweet';
|
||||
this.color = 'a3a3a3';
|
||||
break;
|
||||
case 'PAY_TRADE_FEE':
|
||||
this.iconProp[1] = 'leaf';
|
||||
this.color = '689f43';
|
||||
break;
|
||||
case 'PROPOSAL':
|
||||
this.iconProp[1] = 'file-alt';
|
||||
this.color = '6c8b3b';
|
||||
break;
|
||||
case 'COMPENSATION_REQUEST':
|
||||
this.iconProp[1] = 'money-bill';
|
||||
this.color = '689f43';
|
||||
break;
|
||||
case 'REIMBURSEMENT_REQUEST':
|
||||
this.iconProp[1] = 'money-bill';
|
||||
this.color = '04a908';
|
||||
break;
|
||||
case 'BLIND_VOTE':
|
||||
this.iconProp[1] = 'eye-slash';
|
||||
this.color = '07579a';
|
||||
break;
|
||||
case 'VOTE_REVEAL':
|
||||
this.iconProp[1] = 'eye';
|
||||
this.color = '4AC5FF';
|
||||
break;
|
||||
case 'LOCKUP':
|
||||
this.iconProp[1] = 'lock';
|
||||
this.color = '0056c4';
|
||||
break;
|
||||
case 'UNLOCK':
|
||||
this.iconProp[1] = 'lock-open';
|
||||
this.color = '1d965f';
|
||||
break;
|
||||
case 'ASSET_LISTING_FEE':
|
||||
this.iconProp[1] = 'file-alt';
|
||||
this.color = '6c8b3b';
|
||||
break;
|
||||
case 'PROOF_OF_BURN':
|
||||
this.iconProp[1] = 'file-alt';
|
||||
this.color = '6c8b3b';
|
||||
break;
|
||||
default:
|
||||
this.iconProp[1] = 'question';
|
||||
this.color = 'ffac00';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<div class="box">
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Inputs</td>
|
||||
<td>{{ totalInput / 100 | number: '1.2-2' }} BSQ</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Outputs</td>
|
||||
<td>{{ totalOutput / 100 | number: '1.2-2' }} BSQ</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Burnt</td>
|
||||
<td>{{ tx.burntFee / 100 | number: '1.2-2' }} BSQ</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Issuance</td>
|
||||
<td>{{ totalIssued / 100 | number: '1.2-2' }} BSQ</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Type</td>
|
||||
<td><app-bisq-icon class="mr-1" [txType]="tx.txType"></app-bisq-icon> {{ tx.txTypeDisplayString }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Version</td>
|
||||
<td>{{ tx.txVersion }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Component, ChangeDetectionStrategy, Input, OnChanges } from '@angular/core';
|
||||
import { BisqTransaction } from 'src/app/interfaces/bisq.interfaces';
|
||||
|
||||
@Component({
|
||||
selector: 'app-bisq-transaction-details',
|
||||
templateUrl: './bisq-transaction-details.component.html',
|
||||
styleUrls: ['./bisq-transaction-details.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class BisqTransactionDetailsComponent implements OnChanges {
|
||||
@Input() tx: BisqTransaction;
|
||||
|
||||
totalInput: number;
|
||||
totalOutput: number;
|
||||
totalIssued: number;
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnChanges() {
|
||||
this.totalInput = this.tx.inputs.filter((input) => input.isVerified).reduce((acc, input) => acc + input.bsqAmount, 0);
|
||||
this.totalOutput = this.tx.outputs.filter((output) => output.isVerified).reduce((acc, output) => acc + output.bsqAmount, 0);
|
||||
this.totalIssued = this.tx.outputs
|
||||
.filter((output) => output.isVerified && output.txOutputType === 'ISSUANCE_CANDIDATE_OUTPUT')
|
||||
.reduce((acc, output) => acc + output.bsqAmount, 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<div class="header-bg box">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table class="table table-borderless smaller-text table-xs" style="margin: 0;">
|
||||
<tbody>
|
||||
<ng-template ngFor let-input [ngForOf]="tx.inputs" [ngForTrackBy]="trackByIndexFn">
|
||||
<tr *ngIf="input.isVerified">
|
||||
<td class="arrow-td">
|
||||
<ng-template [ngIf]="input.spendingTxId === null" [ngIfElse]="hasPreoutput">
|
||||
<i class="arrow grey"></i>
|
||||
</ng-template>
|
||||
<ng-template #hasPreoutput>
|
||||
<a [routerLink]="['/tx/' | relativeUrl, input.spendingTxId]">
|
||||
<i class="arrow red"></i>
|
||||
</a>
|
||||
</ng-template>
|
||||
</td>
|
||||
<td>
|
||||
<a [routerLink]="['/address/' | relativeUrl, input.address]" title="{{ input.address }}">
|
||||
<span class="d-block d-lg-none">B{{ input.address | shortenString : 16 }}</span>
|
||||
<span class="d-none d-lg-block">B{{ input.address | shortenString : 35 }}</span>
|
||||
</a>
|
||||
</td>
|
||||
<td class="text-right nowrap">
|
||||
{{ input.bsqAmount / 100 | number: '1.2-2' }} BSQ
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="w-100 d-block d-md-none"></div>
|
||||
<div class="col mobile-bottomcol">
|
||||
<table class="table table-borderless smaller-text table-xs" style="margin: 0;">
|
||||
<tbody>
|
||||
<ng-template ngFor let-output [ngForOf]="tx.outputs" [ngForTrackBy]="trackByIndexFn">
|
||||
<tr *ngIf="output.isVerified && output.opReturn === undefined">
|
||||
<td>
|
||||
<a [routerLink]="['/address/' | relativeUrl, output.address]" title="{{ output.address }}">
|
||||
<span class="d-block d-lg-none">B{{ output.address | shortenString : 16 }}</span>
|
||||
<span class="d-none d-lg-block">B{{ output.address | shortenString : 35 }}</span>
|
||||
</a>
|
||||
</td>
|
||||
<td class="text-right nowrap">
|
||||
{{ output.bsqAmount / 100 | number: '1.2-2' }} BSQ
|
||||
</td>
|
||||
<td class="pl-1 arrow-td">
|
||||
<i *ngIf="!output.spentInfo; else spent" class="arrow green"></i>
|
||||
<ng-template #spent>
|
||||
<a [routerLink]="['/tx/' | relativeUrl, output.spentInfo.txId]"><i class="arrow red"></i></a>
|
||||
</ng-template>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,84 @@
|
||||
.arrow-td {
|
||||
width: 22px;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
display: inline-block!important;
|
||||
position: relative;
|
||||
width: 14px;
|
||||
height: 22px;
|
||||
box-sizing: content-box
|
||||
}
|
||||
|
||||
.arrow:before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
margin: auto;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: calc(-1*30px/3);
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 6.66px solid transparent;
|
||||
border-bottom: 6.66px solid transparent
|
||||
}
|
||||
|
||||
.arrow:after {
|
||||
position: absolute;
|
||||
content: '';
|
||||
margin: auto;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: calc(30px/6);
|
||||
width: calc(30px/3);
|
||||
height: calc(20px/3);
|
||||
background: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
.arrow.green:before {
|
||||
border-left: 10px solid #28a745;
|
||||
}
|
||||
.arrow.green:after {
|
||||
background-color:#28a745;
|
||||
}
|
||||
|
||||
.arrow.red:before {
|
||||
border-left: 10px solid #dc3545;
|
||||
}
|
||||
.arrow.red:after {
|
||||
background-color:#dc3545;
|
||||
}
|
||||
|
||||
.arrow.grey:before {
|
||||
border-left: 10px solid #6c757d;
|
||||
}
|
||||
|
||||
.arrow.grey:after {
|
||||
background-color:#6c757d;
|
||||
}
|
||||
|
||||
.scriptmessage {
|
||||
max-width: 280px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.scriptmessage.longer {
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.mobile-bottomcol {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.scriptmessage {
|
||||
max-width: 90px !important;
|
||||
}
|
||||
.scriptmessage.longer {
|
||||
max-width: 280px !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Component, OnInit, ChangeDetectionStrategy, Input } from '@angular/core';
|
||||
import { BisqTransaction } from 'src/app/interfaces/bisq.interfaces';
|
||||
|
||||
@Component({
|
||||
selector: 'app-bisq-transfers',
|
||||
templateUrl: './bisq-transfers.component.html',
|
||||
styleUrls: ['./bisq-transfers.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class BisqTransfersComponent {
|
||||
@Input() tx: BisqTransaction;
|
||||
|
||||
constructor() { }
|
||||
|
||||
trackByIndexFn(index: number) {
|
||||
return index;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,6 +26,7 @@ export class BlockchainBlocksComponent implements OnInit, OnDestroy {
|
||||
|
||||
gradientColors = {
|
||||
'': ['#9339f4', '#105fb0'],
|
||||
bisq: ['#9339f4', '#105fb0'],
|
||||
liquid: ['#116761', '#183550'],
|
||||
testnet: ['#1d486f', '#183550'],
|
||||
};
|
||||
|
||||
@@ -17,6 +17,5 @@ export class BlockchainComponent implements OnInit {
|
||||
|
||||
ngOnInit() {
|
||||
this.stateService.blocks$.subscribe(() => this.isLoading = false);
|
||||
this.stateService.networkChanged$.subscribe(() => this.isLoading = true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
<header>
|
||||
<nav class="navbar navbar-expand-md navbar-dark bg-dark">
|
||||
<a class="navbar-brand" routerLink="/" style="position: relative;">
|
||||
<a class="navbar-brand" [routerLink]="['/' | relativeUrl]" style="position: relative;">
|
||||
<img src="./resources/mempool-logo.png" height="35" width="140" class="logo" [ngStyle]="{'opacity': connectionState === 2 ? 1 : 0.5 }">
|
||||
<div class="badge badge-warning connection-badge" *ngIf="connectionState === 0">Offline</div>
|
||||
<div class="badge badge-warning connection-badge" style="left: 30px;" *ngIf="connectionState === 1">Reconnecting...</div>
|
||||
</a>
|
||||
|
||||
<div class="btn-group" style="margin-right: 16px;" *ngIf="env.TESTNET_ENABLED || env.LIQUID_ENABLED">
|
||||
<div class="btn-group" style="margin-right: 16px;" *ngIf="env.TESTNET_ENABLED || env.LIQUID_ENABLED || env.BISQ_ENABLED">
|
||||
<button type="button" (click)="networkDropdownHidden = !networkDropdownHidden" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="sr-only">Toggle Dropdown</span>
|
||||
</button>
|
||||
<div class="dropdown-menu" [class.d-block]="!networkDropdownHidden">
|
||||
<a class="dropdown-item mainnet" [class.active]="network === ''" routerLink="/"><img src="./resources/bitcoin-logo.png" style="width: 35.5px;"> Mainnet</a>
|
||||
<a class="dropdown-item mainnet" routerLink="/"><img src="./resources/bitcoin-logo.png" style="width: 35.5px;"> Mainnet</a>
|
||||
<a *ngIf="env.LIQUID_ENABLED" class="dropdown-item liquid" [class.active]="network === 'liquid'" routerLink="/liquid"><img src="./resources/liquid-logo.png" style="width: 35.5px;"> Liquid</a>
|
||||
<a *ngIf="env.BISQ_ENABLED" class="dropdown-item mainnet" [class.active]="network === 'bisq'" routerLink="/bisq"><img src="./resources/bisq-logo.png" style="width: 35.5px;"> Bisq</a>
|
||||
<a *ngIf="env.TESTNET_ENABLED" class="dropdown-item testnet" [class.active]="network === 'testnet'" routerLink="/testnet"><img src="./resources/testnet-logo.png" style="width: 35.5px;"> Testnet</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -37,14 +37,6 @@ export class MasterPageComponent implements OnInit {
|
||||
this.stateService.networkChanged$
|
||||
.subscribe((network) => {
|
||||
this.network = network;
|
||||
|
||||
if (network === 'testnet') {
|
||||
this.tvViewRoute = '/testnet-tv';
|
||||
} else if (network === 'liquid') {
|
||||
this.tvViewRoute = '/liquid-tv';
|
||||
} else {
|
||||
this.tvViewRoute = '/tv';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, OnInit, Input, Inject, LOCALE_ID, ChangeDetectionStrategy, OnChanges } from '@angular/core';
|
||||
import { formatDate } from '@angular/common';
|
||||
import { VbytesPipe } from 'src/app/pipes/bytes-pipe/vbytes.pipe';
|
||||
import { VbytesPipe } from 'src/app/shared/pipes/bytes-pipe/vbytes.pipe';
|
||||
import * as Chartist from 'chartist';
|
||||
import { OptimizedMempoolStats } from 'src/app/interfaces/node-api.interface';
|
||||
import { StateService } from 'src/app/services/state.service';
|
||||
|
||||
12
frontend/src/app/components/miner/miner.component.html
Normal file
12
frontend/src/app/components/miner/miner.component.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<ng-template [ngIf]="loading" [ngIfElse]="done">
|
||||
<span class="skeleton-loader"></span>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #done>
|
||||
<ng-template [ngIf]="miner" [ngIfElse]="unknownMiner">
|
||||
<a placement="bottom" [ngbTooltip]="title" [href]="url" target="_blank" class="badge badge-primary">{{ miner }}</a>
|
||||
</ng-template>
|
||||
<ng-template #unknownMiner>
|
||||
<span class="badge badge-secondary">Unknown</span>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
3
frontend/src/app/components/miner/miner.component.scss
Normal file
3
frontend/src/app/components/miner/miner.component.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
.badge {
|
||||
font-size: 14px;
|
||||
}
|
||||
69
frontend/src/app/components/miner/miner.component.ts
Normal file
69
frontend/src/app/components/miner/miner.component.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { Component, Input, OnChanges } from '@angular/core';
|
||||
import { AssetsService } from 'src/app/services/assets.service';
|
||||
import { Transaction } from 'src/app/interfaces/electrs.interface';
|
||||
|
||||
@Component({
|
||||
selector: 'app-miner',
|
||||
templateUrl: './miner.component.html',
|
||||
styleUrls: ['./miner.component.scss'],
|
||||
})
|
||||
export class MinerComponent implements OnChanges {
|
||||
@Input() coinbaseTransaction: Transaction;
|
||||
miner = '';
|
||||
title = '';
|
||||
url = '';
|
||||
loading = true;
|
||||
|
||||
constructor(
|
||||
private assetsService: AssetsService,
|
||||
) { }
|
||||
|
||||
ngOnChanges() {
|
||||
this.miner = '';
|
||||
this.loading = true;
|
||||
this.findMinerFromCoinbase();
|
||||
}
|
||||
|
||||
findMinerFromCoinbase() {
|
||||
if (this.coinbaseTransaction == null || this.coinbaseTransaction.vin == null || this.coinbaseTransaction.vin.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
this.assetsService.getMiningPools$.subscribe((pools) => {
|
||||
for (const vout of this.coinbaseTransaction.vout) {
|
||||
if (!vout.scriptpubkey_address) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pools.payout_addresses[vout.scriptpubkey_address]) {
|
||||
this.miner = pools.payout_addresses[vout.scriptpubkey_address].name;
|
||||
this.title = 'Identified by payout address: ' + vout.scriptpubkey_address;
|
||||
this.url = pools.payout_addresses[vout.scriptpubkey_address].link;
|
||||
break;
|
||||
}
|
||||
|
||||
for (const tag in pools.coinbase_tags) {
|
||||
if (pools.coinbase_tags.hasOwnProperty(tag)) {
|
||||
const coinbaseAscii = this.hex2ascii(this.coinbaseTransaction.vin[0].scriptsig);
|
||||
if (coinbaseAscii.indexOf(tag) > -1) {
|
||||
this.miner = pools.coinbase_tags[tag].name;
|
||||
this.title = 'Identified by coinbase tag: \'' + tag + '\'';
|
||||
this.url = pools.coinbase_tags[tag].link;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
hex2ascii(hex: string) {
|
||||
let str = '';
|
||||
for (let i = 0; i < hex.length; i += 2) {
|
||||
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
|
||||
}
|
||||
return str;
|
||||
}
|
||||
}
|
||||
@@ -148,6 +148,21 @@
|
||||
|
||||
<br>
|
||||
|
||||
<ng-template [ngIf]="bisqTx">
|
||||
<h2>BSQ Information</h2>
|
||||
|
||||
<app-bisq-transaction-details [tx]="bisqTx"></app-bisq-transaction-details>
|
||||
|
||||
<br>
|
||||
|
||||
<h2>BSQ transfers</h2>
|
||||
|
||||
<app-bisq-transfers [tx]="bisqTx"></app-bisq-transfers>
|
||||
|
||||
<br>
|
||||
|
||||
</ng-template>
|
||||
|
||||
<h2>Inputs & Outputs</h2>
|
||||
|
||||
<app-transactions-list [transactions]="[tx]" [transactionPage]="true"></app-transactions-list>
|
||||
|
||||
@@ -10,6 +10,7 @@ import { AudioService } from 'src/app/services/audio.service';
|
||||
import { ApiService } from 'src/app/services/api.service';
|
||||
import { SeoService } from 'src/app/services/seo.service';
|
||||
import { calcSegwitFeeGains } from 'src/app/bitcoin.utils';
|
||||
import { BisqTransaction } from 'src/app/interfaces/bisq.interfaces';
|
||||
|
||||
@Component({
|
||||
selector: 'app-transaction',
|
||||
@@ -37,6 +38,7 @@ export class TransactionComponent implements OnInit, OnDestroy {
|
||||
};
|
||||
isRbfTransaction: boolean;
|
||||
rbfTransaction: undefined | Transaction;
|
||||
bisqTx: BisqTransaction;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
@@ -90,6 +92,10 @@ export class TransactionComponent implements OnInit, OnDestroy {
|
||||
this.segwitGains = calcSegwitFeeGains(tx);
|
||||
this.isRbfTransaction = tx.vin.some((v) => v.sequence < 0xfffffffe);
|
||||
|
||||
if (this.network === 'bisq') {
|
||||
this.loadBisqTransaction();
|
||||
}
|
||||
|
||||
if (!tx.status.confirmed) {
|
||||
this.websocketService.startTrackTransaction(tx.txid);
|
||||
|
||||
@@ -133,6 +139,17 @@ export class TransactionComponent implements OnInit, OnDestroy {
|
||||
.subscribe((rbfTransaction) => this.rbfTransaction = rbfTransaction);
|
||||
}
|
||||
|
||||
loadBisqTransaction() {
|
||||
if (history.state.bsqTx) {
|
||||
this.bisqTx = history.state.bsqTx;
|
||||
} else {
|
||||
this.apiService.getBisqTransaction$(this.txId)
|
||||
.subscribe((tx) => {
|
||||
this.bisqTx = tx;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleLoadElectrsTransactionError(error: any): Observable<any> {
|
||||
if (error.status === 404 && /^[a-fA-F0-9]{64}$/.test(this.txId)) {
|
||||
this.websocketService.startMultiTrackTransaction(this.txId);
|
||||
@@ -204,6 +221,7 @@ export class TransactionComponent implements OnInit, OnDestroy {
|
||||
this.waitingForTransaction = false;
|
||||
this.isLoadingTx = true;
|
||||
this.rbfTransaction = undefined;
|
||||
this.bisqTx = undefined;
|
||||
this.transactionTime = -1;
|
||||
document.body.scrollTo(0, 0);
|
||||
this.leaveTransaction();
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<div class="col">
|
||||
<table class="table table-borderless smaller-text table-xs" style="margin: 0;">
|
||||
<tbody>
|
||||
<tr *ngFor="let vin of getFilteredTxVin(tx)">
|
||||
<tr *ngFor="let vin of getFilteredTxVin(tx); trackBy: trackByIndexFn">
|
||||
<td class="arrow-td">
|
||||
<ng-template [ngIf]="vin.prevout === null && !vin.is_pegin" [ngIfElse]="hasPrevout">
|
||||
<i class="arrow grey"></i>
|
||||
@@ -73,7 +73,7 @@
|
||||
<div class="col mobile-bottomcol">
|
||||
<table class="table table-borderless smaller-text table-xs" style="margin: 0;">
|
||||
<tbody>
|
||||
<tr *ngFor="let vout of getFilteredTxVout(tx); let vindex = index;">
|
||||
<tr *ngFor="let vout of getFilteredTxVout(tx); let vindex = index; trackBy: trackByIndexFn">
|
||||
<td>
|
||||
<a *ngIf="vout.scriptpubkey_address; else scriptpubkey_type" [routerLink]="['/address/' | relativeUrl, vout.scriptpubkey_address]" title="{{ vout.scriptpubkey_address }}">
|
||||
<span class="d-block d-lg-none">{{ vout.scriptpubkey_address | shortenString : 16 }}</span>
|
||||
|
||||
@@ -109,4 +109,8 @@ export class TransactionsListComponent implements OnInit, OnChanges {
|
||||
getFilteredTxVout(tx: Transaction) {
|
||||
return tx.vout.slice(0, tx['@voutLength']);
|
||||
}
|
||||
|
||||
trackByIndexFn(index: number) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user