Merge branch 'master' into simon/support-maxmind-lite

This commit is contained in:
wiz 2022-11-22 17:02:19 +09:00 committed by GitHub
commit dc86f41e03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 134 additions and 43 deletions

View File

@ -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>

View File

@ -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;
} }
} }
} }

View File

@ -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>

View File

@ -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 {

View File

@ -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 {