Merge branch 'master' into mononaut/fix-mobile-bottom-nav
This commit is contained in:
@@ -6,17 +6,16 @@
|
||||
<div class="col-md">
|
||||
<div class="row d-flex justify-content-between">
|
||||
<div class="title-wrapper">
|
||||
<h1 class="title truncated"><span class="first">{{addressString.slice(0,-4)}}</span><span class="last-four">{{addressString.slice(-4)}}</span></h1>
|
||||
<h1 class="title"><app-truncate [text]="addressString"></app-truncate></h1>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr *ngIf="addressInfo && addressInfo.unconfidential">
|
||||
<td i18n="address.unconfidential">Unconfidential</td>
|
||||
<td><a [routerLink]="['/address/' | relativeUrl, addressInfo.unconfidential]">
|
||||
<span class="d-inline d-lg-none">{{ addressInfo.unconfidential | shortenString : 14 }}</span>
|
||||
<span class="d-none d-lg-inline">{{ addressInfo.unconfidential }}</span>
|
||||
</a> <app-clipboard [text]="addressInfo.unconfidential"></app-clipboard></td>
|
||||
<td>
|
||||
<app-truncate [text]="addressInfo.unconfidential" [lastChars]="7" [link]="['/address/' | relativeUrl, addressInfo.unconfidential]"></app-truncate>
|
||||
</td>
|
||||
</tr>
|
||||
<ng-template [ngIf]="!address.electrum">
|
||||
<tr>
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
<div class="title-address">
|
||||
<h1 i18n="shared.address">Address</h1>
|
||||
<div class="tx-link">
|
||||
<a [routerLink]="['/address/' | relativeUrl, addressString]" >
|
||||
<span class="d-inline d-lg-none">{{ addressString | shortenString : 18 }}</span>
|
||||
<span class="d-none d-lg-inline">{{ addressString }}</span>
|
||||
</a>
|
||||
<app-clipboard [text]="addressString"></app-clipboard>
|
||||
<app-truncate [text]="addressString" [lastChars]="8" [link]="['/address/' | relativeUrl, addressString]">
|
||||
<app-clipboard [text]="addressString"></app-clipboard>
|
||||
</app-truncate>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -21,10 +19,11 @@
|
||||
<tbody>
|
||||
<tr *ngIf="addressInfo && addressInfo.unconfidential">
|
||||
<td i18n="address.unconfidential">Unconfidential</td>
|
||||
<td><a [routerLink]="['/address/' | relativeUrl, addressInfo.unconfidential]">
|
||||
<span class="d-inline d-lg-none">{{ addressInfo.unconfidential | shortenString : 14 }}</span>
|
||||
<span class="d-none d-lg-inline">{{ addressInfo.unconfidential }}</span>
|
||||
</a> <app-clipboard [text]="addressInfo.unconfidential"></app-clipboard></td>
|
||||
<td>
|
||||
<app-truncate [text]="addressInfo.unconfidential" [lastChars]="8" [link]="['/address/' | relativeUrl, addressInfo.unconfidential]">
|
||||
<app-clipboard [text]="addressInfo.unconfidential"></app-clipboard>
|
||||
</app-truncate>
|
||||
</td>
|
||||
</tr>
|
||||
<ng-template [ngIf]="!address.electrum">
|
||||
<tr>
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
<div class="title-asset">
|
||||
<h1 i18n="asset|Liquid Asset page title">Asset</h1>
|
||||
<div class="tx-link">
|
||||
<a [routerLink]="['/assets/asset/' | relativeUrl, assetString]">
|
||||
<span class="d-inline d-lg-none">{{ assetString | shortenString : 24 }}</span>
|
||||
<span class="d-none d-lg-inline">{{ assetString }}</span>
|
||||
</a>
|
||||
<app-clipboard [text]="assetString"></app-clipboard>
|
||||
<app-truncate [text]="assetString" [lastChars]="8" [link]="['/assets/asset/' | relativeUrl, assetString]">
|
||||
<app-clipboard [text]="assetString"></app-clipboard>
|
||||
</app-truncate>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -213,6 +213,7 @@ export class BlockFeeRatesGraphComponent implements OnInit {
|
||||
},
|
||||
},
|
||||
legend: (data.series.length === 0) ? undefined : {
|
||||
padding: [10, 75],
|
||||
data: data.legends,
|
||||
selected: JSON.parse(this.storageService.getValue('fee_rates_legend')) ?? {
|
||||
'Min': true,
|
||||
|
||||
@@ -10,12 +10,13 @@ const defaultHoverColor = hexToColor('1bd8f4');
|
||||
|
||||
const feeColors = mempoolFeeColors.map(hexToColor);
|
||||
const auditFeeColors = feeColors.map((color) => darken(desaturate(color, 0.3), 0.9));
|
||||
const marginalFeeColors = feeColors.map((color) => darken(desaturate(color, 0.8), 1.1));
|
||||
const auditColors = {
|
||||
censored: hexToColor('f344df'),
|
||||
missing: darken(desaturate(hexToColor('f344df'), 0.3), 0.7),
|
||||
added: hexToColor('0099ff'),
|
||||
selected: darken(desaturate(hexToColor('0099ff'), 0.3), 0.7),
|
||||
}
|
||||
};
|
||||
|
||||
// convert from this class's update format to TxSprite's update format
|
||||
function toSpriteUpdate(params: ViewUpdateParams): SpriteUpdateParams {
|
||||
@@ -161,13 +162,13 @@ export default class TxView implements TransactionStripped {
|
||||
case 'censored':
|
||||
return auditColors.censored;
|
||||
case 'missing':
|
||||
return auditColors.missing;
|
||||
return marginalFeeColors[feeLevelIndex] || marginalFeeColors[mempoolFeeColors.length - 1];
|
||||
case 'fresh':
|
||||
return auditColors.missing;
|
||||
case 'added':
|
||||
return auditColors.added;
|
||||
case 'selected':
|
||||
return auditColors.selected;
|
||||
return marginalFeeColors[feeLevelIndex] || marginalFeeColors[mempoolFeeColors.length - 1];
|
||||
case 'found':
|
||||
if (this.context === 'projected') {
|
||||
return auditFeeColors[feeLevelIndex] || auditFeeColors[mempoolFeeColors.length - 1];
|
||||
|
||||
@@ -35,12 +35,12 @@
|
||||
<tr *ngIf="tx && tx.status && tx.status.length">
|
||||
<td class="td-width" i18n="transaction.audit-status">Audit status</td>
|
||||
<ng-container [ngSwitch]="tx?.status">
|
||||
<td *ngSwitchCase="'found'" i18n="transaction.audit.match">match</td>
|
||||
<td *ngSwitchCase="'censored'" i18n="transaction.audit.removed">removed</td>
|
||||
<td *ngSwitchCase="'missing'" i18n="transaction.audit.marginal">marginal fee rate</td>
|
||||
<td *ngSwitchCase="'fresh'" i18n="transaction.audit.recently-broadcast">recently broadcast</td>
|
||||
<td *ngSwitchCase="'added'" i18n="transaction.audit.added">added</td>
|
||||
<td *ngSwitchCase="'selected'" i18n="transaction.audit.marginal">marginal fee rate</td>
|
||||
<td *ngSwitchCase="'found'" i18n="transaction.audit.match">Match</td>
|
||||
<td *ngSwitchCase="'censored'" i18n="transaction.audit.removed">Removed</td>
|
||||
<td *ngSwitchCase="'missing'" i18n="transaction.audit.marginal">Marginal fee rate</td>
|
||||
<td *ngSwitchCase="'fresh'" i18n="transaction.audit.recently-broadcasted">Recently broadcasted</td>
|
||||
<td *ngSwitchCase="'added'" i18n="transaction.audit.added">Added</td>
|
||||
<td *ngSwitchCase="'selected'" i18n="transaction.audit.marginal">Marginal fee rate</td>
|
||||
</ng-container>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -4,8 +4,7 @@ import { StateService } from '../../services/state.service';
|
||||
import { specialBlocks } from '../../app.constants';
|
||||
import { BlockExtended } from '../../interfaces/node-api.interface';
|
||||
import { Location } from '@angular/common';
|
||||
import { config } from 'process';
|
||||
import { CacheService } from 'src/app/services/cache.service';
|
||||
import { CacheService } from '../../services/cache.service';
|
||||
|
||||
interface BlockchainBlock extends BlockExtended {
|
||||
placeholder?: boolean;
|
||||
|
||||
@@ -64,26 +64,6 @@
|
||||
white-space: nowrap;
|
||||
margin: 0;
|
||||
display: inline-block;
|
||||
|
||||
&.truncated {
|
||||
text-overflow: unset;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: baseline;
|
||||
|
||||
.first {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin-right: -2px;
|
||||
}
|
||||
|
||||
.last-four {
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::ng-deep .title-wrapper {
|
||||
|
||||
@@ -13,21 +13,21 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<h5 class="card-title" i18n="mining.rewards-per-tx" i18n-ngbTooltip="mining.rewards-per-tx"
|
||||
ngbTooltip="Reward Per Tx" placement="bottom" #rewardspertx [disableTooltip]="!isEllipsisActive(rewardspertx)">Reward Per Tx</h5>
|
||||
<div class="card-text" i18n-ngbTooltip="mining.rewards-per-tx-desc" ngbTooltip="Average miners' reward per transaction in the past 144 blocks" placement="bottom">
|
||||
<h5 class="card-title" i18n="mining.fees-per-block" i18n-ngbTooltip="mining.fees-per-block"
|
||||
ngbTooltip="Avg Block Fees" placement="bottom" #rewardsperblock [disableTooltip]="!isEllipsisActive(rewardsperblock)">Avg Block Fees</h5>
|
||||
<div class="card-text" i18n-ngbTooltip="mining.fees-per-block-desc" ngbTooltip="Average fees per block in the past 144 blocks" placement="bottom">
|
||||
<div class="fee-text">
|
||||
{{ rewardStats.rewardPerTx | amountShortener: 2 }}
|
||||
<span i18n="shared.sat-vbyte|sat/vB">sats/tx</span>
|
||||
{{ (rewardStats.feePerBlock / 100000000) | amountShortener: 4 }}
|
||||
<span i18n="shared.btc-block|BTC/block">BTC/block</span>
|
||||
</div>
|
||||
<span class="fiat">
|
||||
<app-fiat [value]="rewardStats.rewardPerTx"></app-fiat>
|
||||
<app-fiat [value]="rewardStats.feePerBlock"></app-fiat>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<h5 class="card-title" i18n="mining.average-fee" i18n-ngbTooltip="mining.average-fee"
|
||||
ngbTooltip="Average Fee" placement="bottom" #averagefee [disableTooltip]="!isEllipsisActive(averagefee)">Average Fee</h5>
|
||||
ngbTooltip="Avg Tx Fee" placement="bottom" #averagefee [disableTooltip]="!isEllipsisActive(averagefee)">Avg Tx Fee</h5>
|
||||
<div class="card-text" i18n-ngbTooltip="mining.average-fee" ngbTooltip="Fee paid on average for each transaction in the past 144 blocks" placement="bottom">
|
||||
<div class="fee-text">{{ rewardStats.feePerTx | amountShortener: 2 }}
|
||||
<span i18n="shared.sat-vbyte|sat/vB">sats/tx</span>
|
||||
|
||||
@@ -42,8 +42,8 @@ export class RewardStatsComponent implements OnInit {
|
||||
map((stats) => {
|
||||
return {
|
||||
totalReward: stats.totalReward,
|
||||
rewardPerTx: stats.totalReward / stats.totalTx,
|
||||
feePerTx: stats.totalFee / stats.totalTx,
|
||||
feePerBlock: stats.totalFee / 144,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
</app-preview-title>
|
||||
<div class="row d-flex justify-content-between full-width-row">
|
||||
<div class="title-wrapper">
|
||||
<h1 class="title truncated"><span class="first">{{txId.slice(0,-4)}}</span><span class="last-four">{{txId.slice(-4)}}</span></h1>
|
||||
<h1 class="title truncated"><app-truncate [text]="txId"></app-truncate></h1>
|
||||
</div>
|
||||
<div *ngIf="network !== 'liquid' && network !== 'liquidtestnet'" class="features">
|
||||
<app-tx-features [tx]="tx"></app-tx-features>
|
||||
|
||||
@@ -3,21 +3,25 @@
|
||||
<div class="title-block">
|
||||
<div *ngIf="rbfTransaction" class="alert alert-mempool" role="alert">
|
||||
<span i18n="transaction.rbf.replacement|RBF replacement">This transaction has been replaced by:</span>
|
||||
<a class="alert-link" [routerLink]="['/tx/' | relativeUrl, rbfTransaction.txid]">
|
||||
<span class="d-inline d-lg-none">{{ rbfTransaction.txid | shortenString : 24 }}</span>
|
||||
<span class="d-none d-lg-inline">{{ rbfTransaction.txid }}</span>
|
||||
</a>
|
||||
<app-truncate [text]="rbfTransaction.txid" [lastChars]="12" [link]="['/tx/' | relativeUrl, rbfTransaction.txid]"></app-truncate>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="!rbfTransaction || rbfTransaction?.size">
|
||||
<div *ngIf="rbfReplaces?.length" class="alert alert-mempool" role="alert">
|
||||
<span i18n="transaction.rbf.replaced|RBF replaced">This transaction replaced:</span>
|
||||
<div class="tx-list">
|
||||
<app-truncate [text]="replaced" [lastChars]="12" *ngFor="let replaced of rbfReplaces" [link]="['/tx/' | relativeUrl, replaced]"></app-truncate>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="!rbfTransaction || rbfTransaction?.size || tx">
|
||||
<h1 i18n="shared.transaction">Transaction</h1>
|
||||
|
||||
<span class="tx-link float-left">
|
||||
<a [routerLink]="['/tx/' | relativeUrl, txId]">
|
||||
<span class="d-inline d-lg-none">{{ txId | shortenString : 24 }}</span>
|
||||
<span class="d-none d-lg-inline">{{ txId }}</span>
|
||||
</a>
|
||||
<app-clipboard [text]="txId"></app-clipboard>
|
||||
<span class="tx-link">
|
||||
<span class="txid">
|
||||
<app-truncate [text]="txId" [lastChars]="12" [link]="['/tx/' | relativeUrl, txId]">
|
||||
<app-clipboard [text]="txId"></app-clipboard>
|
||||
</app-truncate>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<div class="container-buttons">
|
||||
@@ -28,7 +32,10 @@
|
||||
<ng-template #confirmationPlural let-i i18n="shared.confirmation-count.plural|Transaction plural confirmation count">{{ i }} confirmations</ng-template>
|
||||
</button>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="tx && !tx?.status.confirmed">
|
||||
<ng-template [ngIf]="tx && !tx?.status?.confirmed && replaced">
|
||||
<button type="button" class="btn btn-sm btn-danger" i18n="transaction.unconfirmed|Transaction unconfirmed state">Replaced</button>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="tx && !tx?.status?.confirmed && !replaced">
|
||||
<button type="button" class="btn btn-sm btn-danger" i18n="transaction.unconfirmed|Transaction unconfirmed state">Unconfirmed</button>
|
||||
</ng-template>
|
||||
</div>
|
||||
@@ -91,7 +98,7 @@
|
||||
<div class="col-sm">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<ng-template [ngIf]="transactionTime !== 0">
|
||||
<ng-template [ngIf]="transactionTime !== 0 && !replaced">
|
||||
<tr *ngIf="transactionTime === -1; else firstSeenTmpl">
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
@@ -103,7 +110,7 @@
|
||||
</tr>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
<tr>
|
||||
<tr *ngIf="!replaced">
|
||||
<td class="td-width" i18n="transaction.eta|Transaction ETA">ETA</td>
|
||||
<td>
|
||||
<ng-template [ngIf]="txInBlockIndex === undefined" [ngIfElse]="estimationTmpl">
|
||||
@@ -144,12 +151,12 @@
|
||||
<br>
|
||||
|
||||
<h2 class="text-left">CPFP <fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true" size="xs"></fa-icon></h2>
|
||||
<div class="box">
|
||||
<table class="table table-borderless table-striped">
|
||||
<div class="box cpfp-details">
|
||||
<table class="table table-fixed table-borderless table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th i18n="transactions-list.vout.scriptpubkey-type">Type</th>
|
||||
<th i18n="dashboard.latest-transactions.txid">TXID</th>
|
||||
<th class="txids" i18n="dashboard.latest-transactions.txid">TXID</th>
|
||||
<th class="d-none d-lg-table-cell" i18n="transaction.vsize|Transaction Virtual Size">Virtual size</th>
|
||||
<th i18n="transaction.fee-rate|Transaction fee rate">Fee rate</th>
|
||||
<th class="d-none d-lg-table-cell"></th>
|
||||
@@ -159,10 +166,8 @@
|
||||
<ng-template [ngIf]="cpfpInfo?.descendants?.length">
|
||||
<tr *ngFor="let cpfpTx of cpfpInfo.descendants">
|
||||
<td><span class="badge badge-primary" i18n="transaction.descendant|Descendant">Descendant</span></td>
|
||||
<td><a [routerLink]="['/tx' | relativeUrl, cpfpTx.txid]">
|
||||
<span class="d-inline d-lg-none">{{ cpfpTx.txid | shortenString : 8 }}</span>
|
||||
<span class="d-none d-lg-inline">{{ cpfpTx.txid }}</span>
|
||||
</a>
|
||||
<td>
|
||||
<app-truncate [text]="cpfpTx.txid" [link]="['/tx' | relativeUrl, cpfpTx.txid]"></app-truncate>
|
||||
</td>
|
||||
<td class="d-none d-lg-table-cell" [innerHTML]="cpfpTx.weight / 4 | vbytes: 2"></td>
|
||||
<td>{{ cpfpTx.fee / (cpfpTx.weight / 4) | feeRounding }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span></td>
|
||||
@@ -172,11 +177,8 @@
|
||||
<ng-template [ngIf]="cpfpInfo?.bestDescendant">
|
||||
<tr>
|
||||
<td><span class="badge badge-success" i18n="transaction.descendant|Descendant">Descendant</span></td>
|
||||
<td>
|
||||
<a [routerLink]="['/tx' | relativeUrl, cpfpInfo.bestDescendant.txid]">
|
||||
<span class="d-inline d-lg-none">{{ cpfpInfo.bestDescendant.txid | shortenString : 8 }}</span>
|
||||
<span class="d-none d-lg-inline">{{ cpfpInfo.bestDescendant.txid }}</span>
|
||||
</a>
|
||||
<td class="txids">
|
||||
<app-truncate [text]="cpfpInfo.bestDescendant.txid" [link]="['/tx' | relativeUrl, cpfpInfo.bestDescendant.txid]"></app-truncate>
|
||||
</td>
|
||||
<td class="d-none d-lg-table-cell" [innerHTML]="cpfpInfo.bestDescendant.weight / 4 | vbytes: 2"></td>
|
||||
<td>{{ cpfpInfo.bestDescendant.fee / (cpfpInfo.bestDescendant.weight / 4) | feeRounding }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span></td>
|
||||
@@ -186,10 +188,8 @@
|
||||
<ng-template [ngIf]="cpfpInfo?.ancestors?.length">
|
||||
<tr *ngFor="let cpfpTx of cpfpInfo.ancestors">
|
||||
<td><span class="badge badge-primary" i18n="transaction.ancestor|Transaction Ancestor">Ancestor</span></td>
|
||||
<td><a [routerLink]="['/tx' | relativeUrl, cpfpTx.txid]">
|
||||
<span class="d-inline d-lg-none">{{ cpfpTx.txid | shortenString : 8 }}</span>
|
||||
<span class="d-none d-lg-inline">{{ cpfpTx.txid }}</span>
|
||||
</a>
|
||||
<td class="txids">
|
||||
<app-truncate [text]="cpfpTx.txid" [link]="['/tx' | relativeUrl, cpfpTx.txid]"></app-truncate>
|
||||
</td>
|
||||
<td class="d-none d-lg-table-cell" [innerHTML]="cpfpTx.weight / 4 | vbytes: 2"></td>
|
||||
<td>{{ cpfpTx.fee / (cpfpTx.weight / 4) | feeRounding }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span></td>
|
||||
|
||||
@@ -19,22 +19,32 @@
|
||||
}
|
||||
}
|
||||
.tx-link {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: baseline;
|
||||
width: 0;
|
||||
max-width: 100%;
|
||||
margin-right: 0px;
|
||||
margin-bottom: 0px;
|
||||
margin-top: 8px;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
flex-shrink: 0;
|
||||
@media (min-width: 651px) {
|
||||
display: flex;
|
||||
width: auto;
|
||||
flex-grow: 1;
|
||||
margin-bottom: 0px;
|
||||
margin-right: 1em;
|
||||
top: 1px;
|
||||
position: relative;
|
||||
}
|
||||
@media (max-width: 650px) {
|
||||
width: 100%;
|
||||
order: 3;
|
||||
}
|
||||
|
||||
.txid {
|
||||
width: 200px;
|
||||
min-width: 200px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.td-width {
|
||||
@@ -188,4 +198,16 @@
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cpfp-details {
|
||||
.txids {
|
||||
width: 60%;
|
||||
}
|
||||
}
|
||||
|
||||
.tx-list {
|
||||
.alert-link {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@@ -40,15 +40,21 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
transactionTime = -1;
|
||||
subscription: Subscription;
|
||||
fetchCpfpSubscription: Subscription;
|
||||
fetchRbfSubscription: Subscription;
|
||||
fetchCachedTxSubscription: Subscription;
|
||||
txReplacedSubscription: Subscription;
|
||||
blocksSubscription: Subscription;
|
||||
queryParamsSubscription: Subscription;
|
||||
urlFragmentSubscription: Subscription;
|
||||
fragmentParams: URLSearchParams;
|
||||
rbfTransaction: undefined | Transaction;
|
||||
replaced: boolean = false;
|
||||
rbfReplaces: string[];
|
||||
cpfpInfo: CpfpInfo | null;
|
||||
showCpfpDetails = false;
|
||||
fetchCpfp$ = new Subject<string>();
|
||||
fetchRbfHistory$ = new Subject<string>();
|
||||
fetchCachedTx$ = new Subject<string>();
|
||||
now = new Date().getTime();
|
||||
timeAvg$: Observable<number>;
|
||||
liquidUnblinding = new LiquidUnblinding();
|
||||
@@ -122,7 +128,11 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
}
|
||||
}),
|
||||
delay(2000)
|
||||
)))
|
||||
)),
|
||||
catchError(() => {
|
||||
return of(null);
|
||||
})
|
||||
)
|
||||
),
|
||||
catchError(() => {
|
||||
return of(null);
|
||||
@@ -155,6 +165,49 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.cpfpInfo = cpfpInfo;
|
||||
});
|
||||
|
||||
this.fetchRbfSubscription = this.fetchRbfHistory$
|
||||
.pipe(
|
||||
switchMap((txId) =>
|
||||
this.apiService
|
||||
.getRbfHistory$(txId)
|
||||
),
|
||||
catchError(() => {
|
||||
return of([]);
|
||||
})
|
||||
).subscribe((replaces) => {
|
||||
this.rbfReplaces = replaces;
|
||||
});
|
||||
|
||||
this.fetchCachedTxSubscription = this.fetchCachedTx$
|
||||
.pipe(
|
||||
switchMap((txId) =>
|
||||
this.apiService
|
||||
.getRbfCachedTx$(txId)
|
||||
),
|
||||
catchError(() => {
|
||||
return of(null);
|
||||
})
|
||||
).subscribe((tx) => {
|
||||
if (!tx) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.tx = tx;
|
||||
if (tx.fee === undefined) {
|
||||
this.tx.fee = 0;
|
||||
}
|
||||
this.tx.feePerVsize = tx.fee / (tx.weight / 4);
|
||||
this.isLoadingTx = false;
|
||||
this.error = undefined;
|
||||
this.waitingForTransaction = false;
|
||||
this.graphExpanded = false;
|
||||
this.setupGraph();
|
||||
|
||||
if (!this.tx?.status?.confirmed) {
|
||||
this.fetchRbfHistory$.next(this.tx.txid);
|
||||
}
|
||||
});
|
||||
|
||||
this.subscription = this.route.paramMap
|
||||
.pipe(
|
||||
switchMap((params: ParamMap) => {
|
||||
@@ -268,6 +321,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
} else {
|
||||
this.fetchCpfp$.next(this.tx.txid);
|
||||
}
|
||||
this.fetchRbfHistory$.next(this.tx.txid);
|
||||
}
|
||||
setTimeout(() => { this.applyFragment(); }, 0);
|
||||
},
|
||||
@@ -299,6 +353,10 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
}
|
||||
this.rbfTransaction = rbfTransaction;
|
||||
this.cacheService.setTxCache([this.rbfTransaction]);
|
||||
this.replaced = true;
|
||||
if (rbfTransaction && !this.tx) {
|
||||
this.fetchCachedTx$.next(this.txId);
|
||||
}
|
||||
});
|
||||
|
||||
this.queryParamsSubscription = this.route.queryParams.subscribe((params) => {
|
||||
@@ -364,8 +422,10 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.waitingForTransaction = false;
|
||||
this.isLoadingTx = true;
|
||||
this.rbfTransaction = undefined;
|
||||
this.replaced = false;
|
||||
this.transactionTime = -1;
|
||||
this.cpfpInfo = null;
|
||||
this.rbfReplaces = [];
|
||||
this.showCpfpDetails = false;
|
||||
document.body.scrollTo(0, 0);
|
||||
this.leaveTransaction();
|
||||
@@ -431,6 +491,8 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
ngOnDestroy() {
|
||||
this.subscription.unsubscribe();
|
||||
this.fetchCpfpSubscription.unsubscribe();
|
||||
this.fetchRbfSubscription.unsubscribe();
|
||||
this.fetchCachedTxSubscription.unsubscribe();
|
||||
this.txReplacedSubscription.unsubscribe();
|
||||
this.blocksSubscription.unsubscribe();
|
||||
this.queryParamsSubscription.unsubscribe();
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
<ng-container *ngFor="let tx of transactions; let i = index; trackBy: trackByFn">
|
||||
<div *ngIf="!transactionPage" class="header-bg box tx-page-container">
|
||||
<a class="float-left" [routerLink]="['/tx/' | relativeUrl, tx.txid]">
|
||||
<span style="float: left;" class="d-block d-md-none">{{ tx.txid | shortenString : 16 }}</span>
|
||||
<span style="float: left;" class="d-none d-md-block">{{ tx.txid }}</span>
|
||||
<a class="tx-link" [routerLink]="['/tx/' | relativeUrl, tx.txid]">
|
||||
<app-truncate [text]="tx.txid"></app-truncate>
|
||||
</a>
|
||||
<div class="float-right">
|
||||
<div>
|
||||
<ng-template [ngIf]="tx.status.confirmed">‎{{ tx.status.block_time * 1000 | date:'yyyy-MM-dd HH:mm' }}</ng-template>
|
||||
<ng-template [ngIf]="!tx.status.confirmed && tx.firstSeen">
|
||||
<i><app-time-since [time]="tx.firstSeen" [fastRender]="true"></app-time-since></i>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<div class="header-bg box" infiniteScroll [alwaysCallback]="true" [infiniteScrollDistance]="2" [infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50" (scrolled)="onScroll()" [attr.data-cy]="'tx-' + i">
|
||||
@@ -18,7 +16,7 @@
|
||||
<div *ngIf="errorUnblinded" class="error-unblinded">{{ errorUnblinded }}</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table class="table table-borderless smaller-text table-sm table-tx-vin">
|
||||
<table class="table table-fixed table-borderless smaller-text table-sm table-tx-vin">
|
||||
<tbody>
|
||||
<ng-template ngFor let-vin let-vindex="index" [ngForOf]="tx.vin.slice(0, getVinLimit(tx))" [ngForTrackBy]="trackByIndexFn">
|
||||
<tr [ngClass]="{
|
||||
@@ -49,7 +47,7 @@
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</td>
|
||||
<td>
|
||||
<td class="address-cell">
|
||||
<div [ngSwitch]="true">
|
||||
<ng-container *ngSwitchCase="vin.is_coinbase"><span i18n="transactions-list.coinbase">Coinbase</span><ng-template [ngIf]="network !== 'liquid' && network !== 'liquidtestnet'"> <span i18n="transactions-list.newly-generated-coins">(Newly Generated Coins)</span></ng-template><br /><a placement="bottom" [ngbTooltip]="vin.scriptsig | hex2ascii"><span class="badge badge-secondary scriptmessage longer">{{ vin.scriptsig | hex2ascii }}</span></a></ng-container>
|
||||
<ng-container *ngSwitchCase="vin.is_pegin">
|
||||
@@ -66,12 +64,8 @@
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
<ng-template #defaultAddress>
|
||||
<a class="shortable-address" *ngIf="vin.prevout.scriptpubkey_address; else vinScriptPubkeyType" [routerLink]="['/address/' | relativeUrl, vin.prevout.scriptpubkey_address]" title="{{ vin.prevout.scriptpubkey_address }}">
|
||||
<span class="d-block d-lg-none">{{ vin.prevout.scriptpubkey_address | shortenString : 16 }}</span>
|
||||
<span class="d-none d-lg-inline-flex justify-content-start">
|
||||
<span class="addr-left flex-grow-1" [style]="vin.prevout.scriptpubkey_address.length > 40 ? 'max-width: 235px' : ''">{{ vin.prevout.scriptpubkey_address }}</span>
|
||||
<span *ngIf="vin.prevout.scriptpubkey_address.length > 40" class="addr-right">{{ vin.prevout.scriptpubkey_address | capAddress: 40: 10 }}</span>
|
||||
</span>
|
||||
<a class="address" *ngIf="vin.prevout.scriptpubkey_address; else vinScriptPubkeyType" [routerLink]="['/address/' | relativeUrl, vin.prevout.scriptpubkey_address]" title="{{ vin.prevout.scriptpubkey_address }}">
|
||||
<app-truncate [text]="vin.prevout.scriptpubkey_address" [lastChars]="8"></app-truncate>
|
||||
</a>
|
||||
<ng-template #vinScriptPubkeyType>
|
||||
{{ vin.prevout.scriptpubkey_type?.toUpperCase() }}
|
||||
@@ -100,7 +94,7 @@
|
||||
</tr>
|
||||
<tr *ngIf="(showDetails$ | async) === true">
|
||||
<td colspan="3" class="details-container" >
|
||||
<table class="table table-striped table-borderless details-table mb-3">
|
||||
<table class="table table-striped table-fixed table-borderless details-table mb-3">
|
||||
<tbody>
|
||||
<ng-template [ngIf]="vin.scriptsig">
|
||||
<tr>
|
||||
@@ -112,9 +106,23 @@
|
||||
<td style="text-align: left;">{{ vin.scriptsig }}</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
<tr *ngIf="vin.witness">
|
||||
<tr *ngIf="vin.witness" class="vin-witness">
|
||||
<td i18n="transactions-list.witness">Witness</td>
|
||||
<td style="text-align: left;">{{ vin.witness.join(' ') }}</td>
|
||||
<td style="text-align: left;">
|
||||
<ng-container *ngFor="let witness of vin.witness; index as i">
|
||||
<input type="checkbox" [id]="'tx' + vindex + 'witness' + i" style="display: none;">
|
||||
<p class="witness-item" [class.accordioned]="witness.length > 1000">
|
||||
{{ witness }}
|
||||
</p>
|
||||
<div class="witness-toggle" *ngIf="witness.length > 1000">
|
||||
<span class="ellipsis">...</span>
|
||||
<label [for]="'tx' + vindex + 'witness' + i" class="btn btn-sm btn-primary mt-2">
|
||||
<span class="show-all" i18n="show-all">Show all</span>
|
||||
<span class="show-less" i18n="show-less">Show less</span>
|
||||
</label>
|
||||
</div>
|
||||
</ng-container>
|
||||
</td>
|
||||
</tr>
|
||||
<tr *ngIf="vin.inner_redeemscript_asm">
|
||||
<td i18n="transactions-list.p2sh-redeem-script">P2SH redeem script</td>
|
||||
@@ -153,7 +161,7 @@
|
||||
<ng-template #showMoreInputsLabel>
|
||||
<span i18n="show-more">Show more</span>
|
||||
</ng-template>
|
||||
({{ tx.vin.length - getVinLimit(tx) }} <span i18n="inputs-remaining">remaining</span>)
|
||||
(<ng-container *ngTemplateOutlet="xRemaining; context: {$implicit: tx.vin.length - getVinLimit(tx)}"></ng-container>)
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -162,20 +170,16 @@
|
||||
</div>
|
||||
<div class="w-100 d-block d-md-none"></div>
|
||||
<div class="col mobile-bottomcol">
|
||||
<table class="table table-borderless smaller-text table-sm table-tx-vout">
|
||||
<table class="table table-fixed table-borderless smaller-text table-sm table-tx-vout">
|
||||
<tbody>
|
||||
<ng-template ngFor let-vout let-vindex="index" [ngForOf]="tx.vout.slice(0, getVoutLimit(tx))" [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 !== ''
|
||||
}">
|
||||
<td>
|
||||
<a class="shortable-address" *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>
|
||||
<span class="d-none d-lg-inline-flex justify-content-start">
|
||||
<span class="addr-left flex-grow-1" [style]="vout.scriptpubkey_address.length > 40 ? 'max-width: 235px' : ''">{{ vout.scriptpubkey_address }}</span>
|
||||
<span *ngIf="vout.scriptpubkey_address.length > 40" class="addr-right">{{ vout.scriptpubkey_address | capAddress: 40: 10 }}</span>
|
||||
</span>
|
||||
<td class="address-cell">
|
||||
<a class="address" *ngIf="vout.scriptpubkey_address; else scriptpubkey_type" [routerLink]="['/address/' | relativeUrl, vout.scriptpubkey_address]" title="{{ vout.scriptpubkey_address }}">
|
||||
<app-truncate [text]="vout.scriptpubkey_address" [lastChars]="8"></app-truncate>
|
||||
</a>
|
||||
<div>
|
||||
<app-address-labels [vout]="vout" [channel]="tx._channels && tx._channels.outputs[vindex] ? tx._channels.outputs[vindex] : null"></app-address-labels>
|
||||
@@ -185,13 +189,11 @@
|
||||
<ng-container i18n="transactions-list.peg-out-to">Peg-out to <ng-container *ngTemplateOutlet="pegOutLink"></ng-container></ng-container>
|
||||
<ng-template #pegOutLink>
|
||||
<a *ngIf="stateService.env.BASE_MODULE === 'liquid'; else localPegoutLink" [attr.href]="'https://mempool.space/address/' + vout.pegout.scriptpubkey_address" title="{{ vout.pegout.scriptpubkey_address }}">
|
||||
<span class="d-block d-lg-none">{{ vout.pegout.scriptpubkey_address | shortenString : 16 }}</span>
|
||||
<span class="d-none d-lg-block">{{ vout.pegout.scriptpubkey_address | shortenString : 35 }}</span>
|
||||
<app-truncate [text]="vout.pegout.scriptpubkey_address"></app-truncate>
|
||||
</a>
|
||||
<ng-template #localPegoutLink>
|
||||
<a [routerLink]="['/address/', vout.pegout.scriptpubkey_address]" title="{{ vout.pegout.scriptpubkey_address }}">
|
||||
<span class="d-block d-lg-none">{{ vout.pegout.scriptpubkey_address | shortenString : 16 }}</span>
|
||||
<span class="d-none d-lg-block">{{ vout.pegout.scriptpubkey_address | shortenString : 35 }}</span>
|
||||
<app-truncate [text]="vout.pegout.scriptpubkey_address"></app-truncate>
|
||||
</a>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
@@ -270,7 +272,7 @@
|
||||
<ng-template #showMoreOutputsLabel>
|
||||
<span i18n="show-more">Show more</span>
|
||||
</ng-template>
|
||||
({{ tx.vout.length - getVoutLimit(tx) }} <span i18n="outputs-remaining">remaining</span>)
|
||||
(<ng-container *ngTemplateOutlet="xRemaining; context: {$implicit: tx.vout.length - getVoutLimit(tx)}"></ng-container>)
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -324,3 +326,5 @@
|
||||
<br />
|
||||
<a [routerLink]="['/assets/asset/' | relativeUrl, item.asset]">{{ item.asset | shortenString : 13 }}</a>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #xRemaining let-x i18n="x-remaining">{{ x }} remaining</ng-template>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
.arrow-td {
|
||||
width: 20px;
|
||||
width: 30px;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
@@ -45,6 +45,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
td.amount {
|
||||
width: 32.5%;
|
||||
}
|
||||
|
||||
.extra-info {
|
||||
display: none;
|
||||
@media (min-width: 576px) {
|
||||
@@ -81,6 +85,10 @@
|
||||
}
|
||||
|
||||
.tx-page-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: baseline;
|
||||
white-space: nowrap;
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 10px;
|
||||
@@ -97,9 +105,7 @@
|
||||
&:first-child {
|
||||
color: #ffffff66;
|
||||
white-space: pre-wrap;
|
||||
@media (min-width: 476px) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
width: 150px;
|
||||
}
|
||||
&:nth-child(2) {
|
||||
word-break: break-all;
|
||||
@@ -130,14 +136,7 @@ h2 {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.addr-left {
|
||||
font-family: monospace;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin-right: -7px
|
||||
}
|
||||
|
||||
.addr-right {
|
||||
.address {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
@@ -146,3 +145,50 @@ h2 {
|
||||
font-style: italic;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tx-link {
|
||||
width: 0;
|
||||
flex-grow: 1;
|
||||
margin-inline-end: 2em;
|
||||
}
|
||||
|
||||
.vin-witness {
|
||||
.witness-item.accordioned {
|
||||
max-height: 300px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
input:checked + .witness-item.accordioned {
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.witness-toggle {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 1em;
|
||||
|
||||
.show-all {
|
||||
display: inline;
|
||||
}
|
||||
.show-less {
|
||||
display: none;
|
||||
}
|
||||
.ellipsis {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
input:checked ~ .witness-toggle {
|
||||
.show-all {
|
||||
display: none;
|
||||
}
|
||||
.show-less {
|
||||
display: inline;
|
||||
}
|
||||
.ellipsis {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,8 +31,7 @@
|
||||
<p *ngIf="!isConnector">Peg Out</p>
|
||||
<p *ngIf="line.value != null"><app-amount [satoshis]="line.value"></app-amount></p>
|
||||
<p class="address">
|
||||
<span class="first">{{ line.pegout.slice(0, -4) }}</span>
|
||||
<span class="last-four">{{ line.pegout.slice(-4) }}</span>
|
||||
<app-truncate [text]="line.pegout"></app-truncate>
|
||||
</p>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
@@ -49,8 +48,7 @@
|
||||
<ng-container *ngIf="isConnector && line.txid">
|
||||
<p>
|
||||
<span i18n="transaction">Transaction</span>
|
||||
<span class="first">{{ line.txid.slice(0, 8) }}</span>...
|
||||
<span class="last-four">{{ line.txid.slice(-4) }}</span>
|
||||
<app-truncate [text]="line.txid"></app-truncate>
|
||||
</p>
|
||||
<ng-container [ngSwitch]="line.type">
|
||||
<p *ngSwitchCase="'input'"><span i18n="transaction.output">Output</span> #{{ line.vout + 1 }}</p>
|
||||
@@ -60,8 +58,7 @@
|
||||
<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>
|
||||
<p *ngIf="line.type !== 'fee' && line.address" class="address">
|
||||
<span class="first">{{ line.address.slice(0, -4) }}</span>
|
||||
<span class="last-four">{{ line.address.slice(-4) }}</span>
|
||||
<app-truncate [text]="line.address"></app-truncate>
|
||||
</p>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
@@ -17,22 +17,5 @@
|
||||
.address {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: baseline;
|
||||
justify-content: flex-start;
|
||||
|
||||
.first {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin-right: -2px;
|
||||
}
|
||||
|
||||
.last-four {
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user