Restyle transaction preview diagram

This commit is contained in:
Mononaut 2022-08-24 18:54:11 +00:00
parent fafe40cef0
commit 626b4e61cd
No known key found for this signature in database
GPG Key ID: 61B952CAF4838F94
6 changed files with 145 additions and 45 deletions

View File

@ -2,6 +2,9 @@
<div class="page-title"> <div class="page-title">
<h1 i18n="shared.transaction">Transaction</h1> <h1 i18n="shared.transaction">Transaction</h1>
<a class="tx-link" [routerLink]="['/tx/' | relativeUrl, txId]">
<span class="truncated">{{txId.slice(0,-4)}}</span><span class="last-four">{{txId.slice(-4)}}</span>
</a>
<div *ngIf="network !== 'liquid' && network !== 'liquidtestnet'" class="features"> <div *ngIf="network !== 'liquid' && network !== 'liquidtestnet'" class="features">
<app-tx-features [tx]="tx"></app-tx-features> <app-tx-features [tx]="tx"></app-tx-features>
<span *ngIf="cpfpInfo && cpfpInfo.bestDescendant" class="badge badge-primary mr-1"> <span *ngIf="cpfpInfo && cpfpInfo.bestDescendant" class="badge badge-primary mr-1">
@ -13,21 +16,24 @@
</div> </div>
</div> </div>
<p class="text-center mb-0"> <div class="top-data row">
<a [routerLink]="['/tx/' | relativeUrl, txId]" class="tx-link"> <span class="field col-sm-4 text-left">
{{ txId }} <ng-template [ngIf]="isLiquid && haveBlindedOutputValues(tx)" [ngIfElse]="defaultAmount" i18n="shared.confidential">Confidential</ng-template>
</a> <ng-template #defaultAmount>
</p> <app-amount [satoshis]="totalValue"></app-amount>
</ng-template>
</span>
<span class="field col-sm-4 text-center">&lrm;{{ (tx.status.confirmed ? tx.status.block_time : transactionTime) * 1000 | date:'yyyy-MM-dd HH:mm' }}</span>
<span class="field col-sm-4 text-right"><span class="label" i18n="transaction.fee|Transaction fee">Fee </span>{{ tx.fee | number }} <span class="symbol" i18n="shared.sat|sat">sat</span></span>
</div>
<div class="row graph-wrapper"> <div class="row graph-wrapper">
<tx-bowtie-graph [tx]="tx" [width]="1112" [height]="346" [isLiquid]="isLiquid"></tx-bowtie-graph> <tx-bowtie-graph [tx]="tx" [width]="1112" [height]="346" [network]="network"></tx-bowtie-graph>
<div class="above-bow"> <div class="above-bow">
<p class="field"> <p class="field pair">
<ng-template [ngIf]="isLiquid && haveBlindedOutputValues(tx)" [ngIfElse]="defaultAmount" i18n="shared.confidential">Confidential</ng-template> <span [innerHTML]="'&lrm;' + (tx.size | bytes: 2)"></span>
<ng-template #defaultAmount> <span [innerHTML]="'&lrm;' + (tx.weight | wuBytes: 2)"></span>
<app-amount [satoshis]="totalValue"></app-amount>
</ng-template>
</p> </p>
<p class="field" *ngIf="!isCoinbase(tx)"> <p class="field" *ngIf="!isCoinbase(tx)">
{{ tx.feePerVsize | feeRounding }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span> {{ tx.feePerVsize | feeRounding }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span>

View File

@ -10,26 +10,10 @@
font-size: 28px; font-size: 28px;
} }
.btn-small-height {
line-height: 1.1;
}
.arrow-green {
color: #1a9436;
}
.arrow-red {
color: #dc3545;
}
.row { .row {
flex-direction: row; flex-direction: row;
} }
.effective-fee-container {
display: inline-block;
}
.title { .title {
h2 { h2 {
line-height: 1; line-height: 1;
@ -46,8 +30,9 @@
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: baseline;
margin-bottom: 10px; margin-bottom: 2px;
max-width: 100%;
h1 { h1 {
font-size: 52px; font-size: 52px;
@ -58,6 +43,43 @@
.features { .features {
font-size: 24px; font-size: 24px;
} }
& > * {
flex-grow: 0;
flex-shrink: 0;
}
.tx-link {
flex-grow: 1;
flex-shrink: 1;
margin: 0 1em;
overflow: hidden;
white-space: nowrap;
display: flex;
flex-direction: row;
align-items: baseline;
.truncated {
flex-grow: 1;
flex-shrink: 1;
overflow: hidden;
text-overflow: ellipsis;
margin-right: -2px;
}
.last-four {
flex-shrink: 0;
flex-grow: 0;
}
}
.features {
align-self: center;
}
}
.top-data {
font-size: 28px;
} }
.table { .table {
@ -68,6 +90,23 @@
} }
} }
.field {
font-size: 32px;
margin: 0;
::ng-deep .symbol {
font-size: 24px;
}
.label {
color: #ffffff66;
}
&.pair > *:first-child {
margin-right: 1em;
}
}
.tx-link { .tx-link {
display: inline; display: inline;
font-size: 28px; font-size: 28px;
@ -87,15 +126,6 @@
right: 0; right: 0;
margin: auto; margin: auto;
text-align: center; text-align: center;
.field {
font-size: 32px;
margin: 0;
::ng-deep .symbol {
font-size: 24px;
}
}
} }
.overlaid { .overlaid {

View File

@ -29,6 +29,7 @@ export class TransactionPreviewComponent implements OnInit, OnDestroy {
isLoadingTx = true; isLoadingTx = true;
error: any = undefined; error: any = undefined;
errorUnblinded: any = undefined; errorUnblinded: any = undefined;
transactionTime = -1;
subscription: Subscription; subscription: Subscription;
fetchCpfpSubscription: Subscription; fetchCpfpSubscription: Subscription;
cpfpInfo: CpfpInfo | null; cpfpInfo: CpfpInfo | null;
@ -163,6 +164,12 @@ export class TransactionPreviewComponent implements OnInit, OnDestroy {
this.opReturns = this.getOpReturns(this.tx); this.opReturns = this.getOpReturns(this.tx);
this.extraData = this.chooseExtraData(); this.extraData = this.chooseExtraData();
if (!tx.status.confirmed && tx.firstSeen) {
this.transactionTime = tx.firstSeen;
} else {
this.getTransactionTime();
}
if (!this.tx.status.confirmed) { if (!this.tx.status.confirmed) {
if (tx.cpfpChecked) { if (tx.cpfpChecked) {
this.cpfpInfo = { this.cpfpInfo = {
@ -185,10 +192,26 @@ export class TransactionPreviewComponent implements OnInit, OnDestroy {
); );
} }
getTransactionTime() {
this.openGraphService.waitFor('tx-time');
this.apiService
.getTransactionTimes$([this.tx.txid])
.pipe(
catchError((err) => {
return of(0);
})
)
.subscribe((transactionTimes) => {
this.transactionTime = transactionTimes[0];
this.openGraphService.waitOver('tx-time');
});
}
resetTransaction() { resetTransaction() {
this.error = undefined; this.error = undefined;
this.tx = null; this.tx = null;
this.isLoadingTx = true; this.isLoadingTx = true;
this.transactionTime = -1;
this.cpfpInfo = null; this.cpfpInfo = null;
this.showCpfpDetails = false; this.showCpfpDetails = false;
} }

View File

@ -5,14 +5,14 @@
markerUnits="strokeWidth" markerUnits="strokeWidth"
markerWidth="1.5" markerHeight="1" markerWidth="1.5" markerHeight="1"
orient="auto"> orient="auto">
<path d="M -5 -5 L 0 0 L -5 5 L 1 5 L 1 -5 Z" stroke="white" stroke-width="0" fill="white"/> <path d="M -5 -5 L 0 0 L -5 5 L 1 5 L 1 -5 Z" stroke-width="0" [attr.fill]="gradient[0]"/>
</marker> </marker>
<marker id="output-arrow" viewBox="-5 -5 10 10" <marker id="output-arrow" viewBox="-5 -5 10 10"
refX="0" refY="0" refX="0" refY="0"
markerUnits="strokeWidth" markerUnits="strokeWidth"
markerWidth="1.5" markerHeight="1" markerWidth="1.5" markerHeight="1"
orient="auto"> orient="auto">
<path d="M 1 -5 L 0 -5 L -5 0 L 0 5 L 1 5 Z" stroke="white" stroke-width="0" fill="white"/> <path d="M 1 -5 L 0 -5 L -5 0 L 0 5 L 1 5 Z" stroke-width="0" [attr.fill]="gradient[0]"/>
</marker> </marker>
<marker id="fee-arrow" viewBox="-5 -5 10 10" <marker id="fee-arrow" viewBox="-5 -5 10 10"
refX="0" refY="0" refX="0" refY="0"
@ -20,9 +20,17 @@
markerWidth="1.5" markerHeight="1" markerWidth="1.5" markerHeight="1"
orient="auto"> orient="auto">
</marker> </marker>
<linearGradient id="input-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" [attr.stop-color]="gradient[0]" />
<stop offset="100%" [attr.stop-color]="gradient[1]" />
</linearGradient>
<linearGradient id="output-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" [attr.stop-color]="gradient[1]" />
<stop offset="100%" [attr.stop-color]="gradient[0]" />
</linearGradient>
<linearGradient id="fee-gradient" x1="0%" y1="0%" x2="100%" y2="0%"> <linearGradient id="fee-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="white" /> <stop offset="0%" [attr.stop-color]="gradient[1]" />
<stop offset="50%" stop-color="white" /> <stop offset="50%" [attr.stop-color]="gradient[1]" />
<stop offset="100%" stop-color="transparent" /> <stop offset="100%" stop-color="transparent" />
</linearGradient> </linearGradient>
</defs> </defs>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -1,6 +1,15 @@
.bowtie { .bowtie {
.line { .line {
stroke: white;
fill: none; fill: none;
&.input {
stroke: url(#input-gradient);
}
&.output {
stroke: url(#output-gradient);
}
&.fee {
stroke: url(#fee-gradient);
}
} }
} }

View File

@ -14,7 +14,7 @@ interface SvgLine {
}) })
export class TxBowtieGraphComponent implements OnInit, OnChanges { export class TxBowtieGraphComponent implements OnInit, OnChanges {
@Input() tx: Transaction; @Input() tx: Transaction;
@Input() isLiquid: boolean = false; @Input() network: string;
@Input() width = 1200; @Input() width = 1200;
@Input() height = 600; @Input() height = 600;
@Input() combinedWeight = 100; @Input() combinedWeight = 100;
@ -24,12 +24,32 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
inputs: SvgLine[]; inputs: SvgLine[];
outputs: SvgLine[]; outputs: SvgLine[];
middle: SvgLine; middle: SvgLine;
isLiquid: boolean = false;
gradientColors = {
'': ['#9339f4', '#105fb0'],
bisq: ['#9339f4', '#105fb0'],
// liquid: ['#116761', '#183550'],
liquid: ['#09a197', '#0f62af'],
// 'liquidtestnet': ['#494a4a', '#272e46'],
'liquidtestnet': ['#d2d2d2', '#979797'],
// testnet: ['#1d486f', '#183550'],
testnet: ['#4edf77', '#10a0af'],
// signet: ['#6f1d5d', '#471850'],
signet: ['#d24fc8', '#a84fd2'],
};
gradient: string[] = ['#105fb0', '#105fb0'];
ngOnInit(): void { ngOnInit(): void {
this.isLiquid = (this.network === 'liquid' || this.network === 'liquidtestnet');
this.gradient = this.gradientColors[this.network];
this.initGraph(); this.initGraph();
} }
ngOnChanges(): void { ngOnChanges(): void {
this.isLiquid = (this.network === 'liquid' || this.network === 'liquidtestnet');
this.gradient = this.gradientColors[this.network];
this.initGraph(); this.initGraph();
} }
@ -46,7 +66,7 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
this.middle = { this.middle = {
path: `M ${(this.width / 2) - 50} ${(this.height / 2) + 0.5} L ${(this.width / 2) + 50} ${(this.height / 2) + 0.5}`, path: `M ${(this.width / 2) - 50} ${(this.height / 2) + 0.5} L ${(this.width / 2) + 50} ${(this.height / 2) + 0.5}`,
style: `stroke-width: ${this.combinedWeight + 0.5}` style: `stroke-width: ${this.combinedWeight + 0.5}; stroke: ${this.gradient[1]}`
}; };
} }
@ -132,6 +152,10 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
const start = side === 'in' ? (weight * 0.5) : this.width - (weight * 0.5); const start = side === 'in' ? (weight * 0.5) : this.width - (weight * 0.5);
const center = this.width / 2 + (side === 'in' ? -45 : 45 ); const center = this.width / 2 + (side === 'in' ? -45 : 45 );
const midpoint = (start + center) / 2; const midpoint = (start + center) / 2;
// correct for svg horizontal gradient bug
if (Math.round(outer) === Math.round(inner)) {
outer -= 1;
}
return `M ${start} ${outer} C ${midpoint} ${outer}, ${midpoint} ${inner}, ${center} ${inner}`; return `M ${start} ${outer} C ${midpoint} ${outer}, ${midpoint} ${inner}, ${center} ${inner}`;
} }