Merge branch 'master' into simon/support-maxmind-lite
This commit is contained in:
commit
dc86f41e03
@ -41,24 +41,20 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="overlaid">
|
<div class="overlaid">
|
||||||
<ng-container [ngSwitch]="extraData">
|
<ng-container [ngSwitch]="extraData">
|
||||||
<table class="opreturns" *ngSwitchCase="'coinbase'">
|
<div class="opreturns" *ngSwitchCase="'coinbase'">
|
||||||
<tbody>
|
<div class="opreturn-row">
|
||||||
<tr>
|
<span class="label">Coinbase</span>
|
||||||
<td class="label">Coinbase</td>
|
<span class="message">{{ tx.vin[0].scriptsig | hex2ascii }}</span>
|
||||||
<td class="message">{{ tx.vin[0].scriptsig | hex2ascii }}</td>
|
</div>
|
||||||
</tr>
|
</div>
|
||||||
</tbody>
|
<div class="opreturns" *ngSwitchCase="'opreturn'">
|
||||||
</table>
|
|
||||||
<table class="opreturns" *ngSwitchCase="'opreturn'">
|
|
||||||
<tbody>
|
|
||||||
<ng-container *ngFor="let vout of opReturns.slice(0,3)">
|
<ng-container *ngFor="let vout of opReturns.slice(0,3)">
|
||||||
<tr>
|
<div class="opreturn-row">
|
||||||
<td class="label">OP_RETURN</td>
|
<span class="label">OP_RETURN</span>
|
||||||
<td *ngIf="vout.scriptpubkey_asm !== 'OP_RETURN'" class="message">{{ vout.scriptpubkey_asm | hex2ascii }}</td>
|
<span *ngIf="vout.scriptpubkey_asm !== 'OP_RETURN'" class="message">{{ vout.scriptpubkey_asm | hex2ascii }}</span>
|
||||||
</tr>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</tbody>
|
</div>
|
||||||
</table>
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -29,6 +29,8 @@
|
|||||||
.features {
|
.features {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
margin-left: 1em;
|
margin-left: 1em;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
margin-right: -4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-data {
|
.top-data {
|
||||||
@ -60,6 +62,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.top-data .field {
|
||||||
|
&:first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.tx-link {
|
.tx-link {
|
||||||
display: inline;
|
display: inline;
|
||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
@ -92,26 +103,37 @@
|
|||||||
max-width: 90%;
|
max-width: 90%;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
.opreturns {
|
.opreturns {
|
||||||
|
display: inline-block;
|
||||||
width: auto;
|
width: auto;
|
||||||
|
max-width: 100%;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
table-layout: auto;
|
table-layout: auto;
|
||||||
background: #2d3348af;
|
background: #2d3348af;
|
||||||
border-top-left-radius: 5px;
|
border-top-left-radius: 5px;
|
||||||
border-top-right-radius: 5px;
|
border-top-right-radius: 5px;
|
||||||
|
|
||||||
td {
|
.opreturn-row {
|
||||||
padding: 10px 10px;
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
&.message {
|
.label {
|
||||||
overflow: hidden;
|
margin-right: 1em;
|
||||||
display: inline-block;
|
}
|
||||||
vertical-align: bottom;
|
|
||||||
text-overflow: ellipsis;
|
.message {
|
||||||
white-space: nowrap;
|
flex-shrink: 1;
|
||||||
text-align: left;
|
white-space: nowrap;
|
||||||
}
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div class="bowtie-graph">
|
<div class="bowtie-graph">
|
||||||
<svg *ngIf="inputs && outputs" class="bowtie" [attr.height]="(height + 10) + 'px'" [attr.width]="width + 'px'">
|
<svg *ngIf="inputs && outputs" class="bowtie" [class.rtl]="dir === 'rtl'" [attr.height]="(height + 10) + 'px'" [attr.width]="width + 'px'">
|
||||||
<defs>
|
<defs>
|
||||||
<marker id="input-arrow" viewBox="-5 -5 10 10"
|
<marker id="input-arrow" viewBox="-5 -5 10 10"
|
||||||
refX="0" refY="0"
|
refX="0" refY="0"
|
||||||
@ -21,6 +21,15 @@
|
|||||||
markerWidth="1.5" markerHeight="1"
|
markerWidth="1.5" markerHeight="1"
|
||||||
orient="auto">
|
orient="auto">
|
||||||
</marker>
|
</marker>
|
||||||
|
<radialGradient id="gradient0" x1="0%" y1="0%" x2="100%" y2="100%" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop [attr.stop-color]="gradient[0]" />
|
||||||
|
</radialGradient>
|
||||||
|
<radialGradient id="gradient1" x1="0%" y1="0%" x2="100%" y2="100%" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop [attr.stop-color]="gradient[1]" />
|
||||||
|
</radialGradient>
|
||||||
|
<radialGradient id="gradient2" x1="0%" y1="0%" x2="100%" y2="100%" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop [attr.stop-color]="gradient[2]" />
|
||||||
|
</radialGradient>
|
||||||
<linearGradient id="input-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
<linearGradient id="input-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||||
<stop offset="0%" [attr.stop-color]="gradient[0]" />
|
<stop offset="0%" [attr.stop-color]="gradient[0]" />
|
||||||
<stop offset="100%" [attr.stop-color]="gradient[1]" />
|
<stop offset="100%" [attr.stop-color]="gradient[1]" />
|
||||||
@ -117,7 +126,7 @@
|
|||||||
(pointerout)="onBlur($event, 'output-connector', i);"
|
(pointerout)="onBlur($event, 'output-connector', i);"
|
||||||
(click)="onClick($event, 'output-connector', outputData[i].index);"
|
(click)="onClick($event, 'output-connector', outputData[i].index);"
|
||||||
/>
|
/>
|
||||||
<path
|
<path *ngIf="!output.zeroValue"
|
||||||
[attr.d]="output.markerPath"
|
[attr.d]="output.markerPath"
|
||||||
class="output marker-target {{output.class}}"
|
class="output marker-target {{output.class}}"
|
||||||
[class.highlight]="outputData[i].index === outputIndex"
|
[class.highlight]="outputData[i].index === outputIndex"
|
||||||
@ -125,7 +134,7 @@
|
|||||||
(pointerout)="onBlur($event, 'output', i);"
|
(pointerout)="onBlur($event, 'output', i);"
|
||||||
(click)="onClick($event, 'output', outputData[i].index);"
|
(click)="onClick($event, 'output', outputData[i].index);"
|
||||||
/>
|
/>
|
||||||
<path
|
<path *ngIf="!output.zeroValue"
|
||||||
[attr.d]="output.path"
|
[attr.d]="output.path"
|
||||||
class="line {{output.class}}"
|
class="line {{output.class}}"
|
||||||
[class.highlight]="outputIndex != null && outputData[i].index === outputIndex"
|
[class.highlight]="outputIndex != null && outputData[i].index === outputIndex"
|
||||||
@ -135,6 +144,16 @@
|
|||||||
(pointerout)="onBlur($event, 'output', i);"
|
(pointerout)="onBlur($event, 'output', i);"
|
||||||
(click)="onClick($event, 'output', outputData[i].index);"
|
(click)="onClick($event, 'output', outputData[i].index);"
|
||||||
/>
|
/>
|
||||||
|
<path *ngIf="output.zeroValue"
|
||||||
|
[attr.d]="output.path"
|
||||||
|
class="line {{output.class}} zerovalue"
|
||||||
|
[class.highlight]="outputIndex != null && outputData[i].index === outputIndex"
|
||||||
|
[class.zerovalue]="output.zeroValue"
|
||||||
|
[style]="output.style"
|
||||||
|
(pointerover)="onHover($event, 'output', i);"
|
||||||
|
(pointerout)="onBlur($event, 'output', i);"
|
||||||
|
(click)="onClick($event, 'output', outputData[i].index);"
|
||||||
|
/>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
.bowtie {
|
.bowtie {
|
||||||
|
&.rtl {
|
||||||
|
transform: scale(-1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
.line {
|
.line {
|
||||||
fill: none;
|
fill: none;
|
||||||
|
|
||||||
@ -11,6 +15,10 @@
|
|||||||
&.fee {
|
&.fee {
|
||||||
stroke: url(#fee-gradient);
|
stroke: url(#fee-gradient);
|
||||||
}
|
}
|
||||||
|
&.zerovalue {
|
||||||
|
stroke: url(#gradient0);
|
||||||
|
stroke-linecap: round;
|
||||||
|
}
|
||||||
|
|
||||||
&.highlight {
|
&.highlight {
|
||||||
z-index: 8;
|
z-index: 8;
|
||||||
@ -21,6 +29,9 @@
|
|||||||
&.output {
|
&.output {
|
||||||
stroke: url(#output-highlight-gradient);
|
stroke: url(#output-highlight-gradient);
|
||||||
}
|
}
|
||||||
|
&.zerovalue {
|
||||||
|
stroke: #1bd8f4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,6 +47,9 @@
|
|||||||
&.fee {
|
&.fee {
|
||||||
stroke: url(#fee-hover-gradient);
|
stroke: url(#fee-hover-gradient);
|
||||||
}
|
}
|
||||||
|
&.zerovalue {
|
||||||
|
stroke: white;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.connector {
|
.connector {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, OnInit, Input, OnChanges, HostListener } from '@angular/core';
|
import { Component, OnInit, Input, OnChanges, HostListener, Inject, LOCALE_ID } 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';
|
||||||
@ -13,6 +13,7 @@ interface SvgLine {
|
|||||||
class?: string;
|
class?: string;
|
||||||
connectorPath?: string;
|
connectorPath?: string;
|
||||||
markerPath?: string;
|
markerPath?: string;
|
||||||
|
zeroValue?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Xput {
|
interface Xput {
|
||||||
@ -49,6 +50,8 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
|
|||||||
@Input() inputIndex: number;
|
@Input() inputIndex: number;
|
||||||
@Input() outputIndex: number;
|
@Input() outputIndex: number;
|
||||||
|
|
||||||
|
dir: 'rtl' | 'ltr' = 'ltr';
|
||||||
|
|
||||||
inputData: Xput[];
|
inputData: Xput[];
|
||||||
outputData: Xput[];
|
outputData: Xput[];
|
||||||
inputs: SvgLine[];
|
inputs: SvgLine[];
|
||||||
@ -63,6 +66,8 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
|
|||||||
hoverConnector: boolean = false;
|
hoverConnector: boolean = false;
|
||||||
tooltipPosition = { x: 0, y: 0 };
|
tooltipPosition = { x: 0, y: 0 };
|
||||||
outspends: Outspend[] = [];
|
outspends: Outspend[] = [];
|
||||||
|
zeroValueWidth = 60;
|
||||||
|
zeroValueThickness = 20;
|
||||||
|
|
||||||
outspendsSubscription: Subscription;
|
outspendsSubscription: Subscription;
|
||||||
refreshOutspends$: ReplaySubject<string> = new ReplaySubject();
|
refreshOutspends$: ReplaySubject<string> = new ReplaySubject();
|
||||||
@ -87,7 +92,12 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
|
|||||||
private relativeUrlPipe: RelativeUrlPipe,
|
private relativeUrlPipe: RelativeUrlPipe,
|
||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
) { }
|
@Inject(LOCALE_ID) private locale: string,
|
||||||
|
) {
|
||||||
|
if (this.locale.startsWith('ar') || this.locale.startsWith('fa') || this.locale.startsWith('he')) {
|
||||||
|
this.dir = 'rtl';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.initGraph();
|
this.initGraph();
|
||||||
@ -130,6 +140,7 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
|
|||||||
this.txWidth = this.connectors ? Math.max(this.width - 200, this.width * 0.8) : this.width - 20;
|
this.txWidth = this.connectors ? Math.max(this.width - 200, this.width * 0.8) : this.width - 20;
|
||||||
this.combinedWeight = Math.min(this.maxCombinedWeight, Math.floor((this.txWidth - (2 * this.midWidth)) / 6));
|
this.combinedWeight = Math.min(this.maxCombinedWeight, Math.floor((this.txWidth - (2 * this.midWidth)) / 6));
|
||||||
this.connectorWidth = (this.width - this.txWidth) / 2;
|
this.connectorWidth = (this.width - this.txWidth) / 2;
|
||||||
|
this.zeroValueWidth = Math.max(20, Math.min((this.txWidth / 2) - this.midWidth - 110, 60));
|
||||||
|
|
||||||
const totalValue = this.calcTotalValue(this.tx);
|
const totalValue = this.calcTotalValue(this.tx);
|
||||||
let voutWithFee = this.tx.vout.map((v, i) => {
|
let voutWithFee = this.tx.vout.map((v, i) => {
|
||||||
@ -236,10 +247,10 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
linesFromWeights(side: 'in' | 'out', xputs: Xput[], weights: number[], maxVisibleStrands: number): SvgLine[] {
|
linesFromWeights(side: 'in' | 'out', xputs: Xput[], weights: number[], maxVisibleStrands: number): SvgLine[] {
|
||||||
const lineParams = weights.map((w) => {
|
const lineParams = weights.map((w, i) => {
|
||||||
return {
|
return {
|
||||||
weight: w,
|
weight: w,
|
||||||
thickness: Math.max(this.minWeight - 1, w) + 1,
|
thickness: xputs[i].value === 0 ? this.zeroValueThickness : Math.max(this.minWeight - 1, w) + 1,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
innerY: 0,
|
innerY: 0,
|
||||||
outerY: 0,
|
outerY: 0,
|
||||||
@ -256,7 +267,7 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
|
|||||||
let lastOuter = 0;
|
let lastOuter = 0;
|
||||||
let lastInner = innerTop;
|
let lastInner = innerTop;
|
||||||
// gap between strands
|
// gap between strands
|
||||||
const spacing = (this.height - visibleWeight) / gaps;
|
const spacing = Math.max(4, (this.height - visibleWeight) / gaps);
|
||||||
|
|
||||||
// curve adjustments to prevent overlaps
|
// curve adjustments to prevent overlaps
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
@ -265,6 +276,12 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
|
|||||||
let lastWeight = 0;
|
let lastWeight = 0;
|
||||||
let pad = 0;
|
let pad = 0;
|
||||||
lineParams.forEach((line, i) => {
|
lineParams.forEach((line, i) => {
|
||||||
|
if (xputs[i].value === 0) {
|
||||||
|
line.outerY = lastOuter + (this.zeroValueThickness / 2);
|
||||||
|
lastOuter += this.zeroValueThickness + spacing;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// set the vertical position of the (center of the) outer side of the line
|
// set the vertical position of the (center of the) outer side of the line
|
||||||
line.outerY = lastOuter + (line.thickness / 2);
|
line.outerY = lastOuter + (line.thickness / 2);
|
||||||
line.innerY = Math.min(innerBottom + (line.thickness / 2), Math.max(innerTop + (line.thickness / 2), lastInner + (line.weight / 2)));
|
line.innerY = Math.min(innerBottom + (line.thickness / 2), Math.max(innerTop + (line.thickness / 2), lastInner + (line.weight / 2)));
|
||||||
@ -318,19 +335,28 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
|
|||||||
maxOffset -= minOffset;
|
maxOffset -= minOffset;
|
||||||
|
|
||||||
return lineParams.map((line, i) => {
|
return lineParams.map((line, i) => {
|
||||||
return {
|
if (xputs[i].value === 0) {
|
||||||
path: this.makePath(side, line.outerY, line.innerY, line.thickness, line.offset, pad + maxOffset),
|
return {
|
||||||
style: this.makeStyle(line.thickness, xputs[i].type),
|
path: this.makeZeroValuePath(side, line.outerY),
|
||||||
class: xputs[i].type,
|
style: this.makeStyle(this.zeroValueThickness, xputs[i].type),
|
||||||
connectorPath: this.connectors ? this.makeConnectorPath(side, line.outerY, line.innerY, line.thickness): null,
|
class: xputs[i].type,
|
||||||
markerPath: this.makeMarkerPath(side, line.outerY, line.innerY, line.thickness),
|
zeroValue: true,
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
path: this.makePath(side, line.outerY, line.innerY, line.thickness, line.offset, pad + maxOffset),
|
||||||
|
style: this.makeStyle(line.thickness, xputs[i].type),
|
||||||
|
class: xputs[i].type,
|
||||||
|
connectorPath: this.connectors ? this.makeConnectorPath(side, line.outerY, line.innerY, line.thickness): null,
|
||||||
|
markerPath: this.makeMarkerPath(side, line.outerY, line.innerY, line.thickness),
|
||||||
|
};
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
makePath(side: 'in' | 'out', outer: number, inner: number, weight: number, offset: number, pad: number): string {
|
makePath(side: 'in' | 'out', outer: number, inner: number, weight: number, offset: number, pad: number): string {
|
||||||
const start = (weight * 0.5) + this.connectorWidth;
|
const start = (weight * 0.5) + this.connectorWidth;
|
||||||
const curveStart = Math.max(start + 1, pad - offset);
|
const curveStart = Math.max(start + 5, pad - offset);
|
||||||
const end = this.width / 2 - (this.midWidth * 0.9) + 1;
|
const end = this.width / 2 - (this.midWidth * 0.9) + 1;
|
||||||
const curveEnd = end - offset - 10;
|
const curveEnd = end - offset - 10;
|
||||||
const midpoint = (curveStart + curveEnd) / 2;
|
const midpoint = (curveStart + curveEnd) / 2;
|
||||||
@ -347,6 +373,16 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
makeZeroValuePath(side: 'in' | 'out', y: number): string {
|
||||||
|
const offset = this.zeroValueThickness / 2;
|
||||||
|
const start = (this.connectorWidth / 2) + 10;
|
||||||
|
if (side === 'in') {
|
||||||
|
return `M ${start + offset} ${y} L ${start + this.zeroValueWidth + offset} ${y}`;
|
||||||
|
} else { // mirrored in y-axis for the right hand side
|
||||||
|
return `M ${this.width - start - offset} ${y} L ${this.width - start - this.zeroValueWidth - offset} ${y}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
makeConnectorPath(side: 'in' | 'out', y: number, inner, weight: number): string {
|
makeConnectorPath(side: 'in' | 'out', y: number, inner, weight: number): string {
|
||||||
const halfWidth = weight * 0.5;
|
const halfWidth = weight * 0.5;
|
||||||
const offset = 10; //Math.max(2, halfWidth * 0.2);
|
const offset = 10; //Math.max(2, halfWidth * 0.2);
|
||||||
@ -391,7 +427,11 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
|
|||||||
|
|
||||||
@HostListener('pointermove', ['$event'])
|
@HostListener('pointermove', ['$event'])
|
||||||
onPointerMove(event) {
|
onPointerMove(event) {
|
||||||
this.tooltipPosition = { x: event.offsetX, y: event.offsetY };
|
if (this.dir === 'rtl') {
|
||||||
|
this.tooltipPosition = { x: this.width - event.offsetX, y: event.offsetY };
|
||||||
|
} else {
|
||||||
|
this.tooltipPosition = { x: event.offsetX, y: event.offsetY };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onHover(event, side, index): void {
|
onHover(event, side, index): void {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user