Merge branch 'master' into simon/load-more-mempool-txs

This commit is contained in:
wiz
2023-07-14 19:30:15 +09:00
committed by GitHub
31 changed files with 298 additions and 138 deletions

View File

@@ -1,15 +1,17 @@
<div class="block-overview-graph">
<canvas class="block-overview-canvas" [class.clickable]="!!hoverTx" #blockCanvas></canvas>
<div class="loader-wrapper" [class.hidden]="(!isLoading || disableSpinner) && !unavailable">
<div *ngIf="isLoading" class="spinner-border ml-3 loading" role="status"></div>
<div *ngIf="!isLoading && unavailable" class="ml-3" i18n="block.not-available">not available</div>
</div>
<app-block-overview-tooltip
[tx]="selectedTx || hoverTx"
[cursorPosition]="tooltipPosition"
[clickable]="!!selectedTx"
[auditEnabled]="auditHighlighting"
[blockConversion]="blockConversion"
></app-block-overview-tooltip>
<div class="grid-align" [style.gridTemplateColumns]="'repeat(auto-fit, ' + resolution + 'px)'">
<div class="block-overview-graph">
<canvas class="block-overview-canvas" [class.clickable]="!!hoverTx" #blockCanvas></canvas>
<div class="loader-wrapper" [class.hidden]="(!isLoading || disableSpinner) && !unavailable">
<div *ngIf="isLoading" class="spinner-border ml-3 loading" role="status"></div>
<div *ngIf="!isLoading && unavailable" class="ml-3" i18n="block.not-available">not available</div>
</div>
<app-block-overview-tooltip
[tx]="selectedTx || hoverTx"
[cursorPosition]="tooltipPosition"
[clickable]="!!selectedTx"
[auditEnabled]="auditHighlighting"
[blockConversion]="blockConversion"
></app-block-overview-tooltip>
</div>
</div>

View File

@@ -6,8 +6,16 @@
display: flex;
justify-content: center;
align-items: center;
grid-column: 1/-1;
}
.grid-align {
position: relative;
width: 100%;
display: grid;
grid-template-columns: repeat(auto-fit, 75px);
justify-content: center;
}
.block-overview-canvas {
position: absolute;

View File

@@ -6,6 +6,8 @@ import TxSprite from './tx-sprite';
import TxView from './tx-view';
import { Position } from './sprite-types';
import { Price } from '../../services/price.service';
import { StateService } from '../../services/state.service';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-block-overview-graph',
@@ -23,7 +25,6 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
@Input() unavailable: boolean = false;
@Input() auditHighlighting: boolean = false;
@Input() blockConversion: Price;
@Input() pixelAlign: boolean = false;
@Output() txClickEvent = new EventEmitter<{ tx: TransactionStripped, keyModifier: boolean}>();
@Output() txHoverEvent = new EventEmitter<string>();
@Output() readyEvent = new EventEmitter();
@@ -44,16 +45,25 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
scene: BlockScene;
hoverTx: TxView | void;
selectedTx: TxView | void;
highlightTx: TxView | void;
mirrorTx: TxView | void;
tooltipPosition: Position;
readyNextFrame = false;
searchText: string;
searchSubscription: Subscription;
constructor(
readonly ngZone: NgZone,
readonly elRef: ElementRef,
private stateService: StateService,
) {
this.vertexArray = new FastVertexArray(512, TxSprite.dataSize);
this.searchSubscription = this.stateService.searchText$.subscribe((text) => {
this.searchText = text;
this.updateSearchHighlight();
});
}
ngAfterViewInit(): void {
@@ -109,6 +119,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
this.scene.setup(transactions);
this.readyNextFrame = true;
this.start();
this.updateSearchHighlight();
}
}
@@ -116,6 +127,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
if (this.scene) {
this.scene.enter(transactions, direction);
this.start();
this.updateSearchHighlight();
}
}
@@ -123,6 +135,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
if (this.scene) {
this.scene.exit(direction);
this.start();
this.updateSearchHighlight();
}
}
@@ -130,6 +143,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
if (this.scene) {
this.scene.replace(transactions || [], direction, sort);
this.start();
this.updateSearchHighlight();
}
}
@@ -137,6 +151,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
if (this.scene) {
this.scene.update(add, remove, change, direction, resetLayout);
this.start();
this.updateSearchHighlight();
}
}
@@ -203,7 +218,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
} else {
this.scene = new BlockScene({ width: this.displayWidth, height: this.displayHeight, resolution: this.resolution,
blockLimit: this.blockLimit, orientation: this.orientation, flip: this.flip, vertexArray: this.vertexArray,
highlighting: this.auditHighlighting, pixelAlign: this.pixelAlign });
highlighting: this.auditHighlighting });
this.start();
}
}
@@ -406,6 +421,19 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
}
}
updateSearchHighlight(): void {
if (this.highlightTx && this.highlightTx.txid !== this.searchText && this.scene) {
this.scene.setHighlight(this.highlightTx, false);
this.start();
} else if (this.scene?.txs && this.searchText && this.searchText.length === 64) {
this.highlightTx = this.scene.txs[this.searchText];
if (this.highlightTx) {
this.scene.setHighlight(this.highlightTx, true);
this.start();
}
}
}
setHighlightingEnabled(enabled: boolean): void {
if (this.scene) {
this.scene.setHighlighting(enabled);

View File

@@ -15,7 +15,6 @@ export default class BlockScene {
gridWidth: number;
gridHeight: number;
gridSize: number;
pixelAlign: boolean;
vbytesPerUnit: number;
unitPadding: number;
unitWidth: number;
@@ -24,24 +23,19 @@ export default class BlockScene {
animateUntil = 0;
dirty: boolean;
constructor({ width, height, resolution, blockLimit, orientation, flip, vertexArray, highlighting, pixelAlign }:
constructor({ width, height, resolution, blockLimit, orientation, flip, vertexArray, highlighting }:
{ width: number, height: number, resolution: number, blockLimit: number,
orientation: string, flip: boolean, vertexArray: FastVertexArray, highlighting: boolean, pixelAlign: boolean }
orientation: string, flip: boolean, vertexArray: FastVertexArray, highlighting: boolean }
) {
this.init({ width, height, resolution, blockLimit, orientation, flip, vertexArray, highlighting, pixelAlign });
this.init({ width, height, resolution, blockLimit, orientation, flip, vertexArray, highlighting });
}
resize({ width = this.width, height = this.height, animate = true }: { width?: number, height?: number, animate: boolean }): void {
this.width = width;
this.height = height;
this.gridSize = this.width / this.gridWidth;
if (this.pixelAlign) {
this.unitPadding = Math.max(1, Math.floor(this.gridSize / 2.5));
this.unitWidth = this.gridSize - (this.unitPadding);
} else {
this.unitPadding = width / 500;
this.unitWidth = this.gridSize - (this.unitPadding * 2);
}
this.unitPadding = Math.max(1, Math.floor(this.gridSize / 5));
this.unitWidth = this.gridSize - (this.unitPadding * 2);
this.dirty = true;
if (this.initialised && this.scene) {
@@ -215,15 +209,18 @@ export default class BlockScene {
this.animateUntil = Math.max(this.animateUntil, tx.setHover(value));
}
private init({ width, height, resolution, blockLimit, orientation, flip, vertexArray, highlighting, pixelAlign }:
setHighlight(tx: TxView, value: boolean): void {
this.animateUntil = Math.max(this.animateUntil, tx.setHighlight(value));
}
private init({ width, height, resolution, blockLimit, orientation, flip, vertexArray, highlighting }:
{ width: number, height: number, resolution: number, blockLimit: number,
orientation: string, flip: boolean, vertexArray: FastVertexArray, highlighting: boolean, pixelAlign: boolean }
orientation: string, flip: boolean, vertexArray: FastVertexArray, highlighting: boolean }
): void {
this.orientation = orientation;
this.flip = flip;
this.vertexArray = vertexArray;
this.highlightingEnabled = highlighting;
this.pixelAlign = pixelAlign;
this.scene = {
count: 0,
@@ -349,12 +346,7 @@ export default class BlockScene {
private gridToScreen(position: Square | void): Square {
if (position) {
const slotSize = (position.s * this.gridSize);
let squareSize;
if (this.pixelAlign) {
squareSize = slotSize - (this.unitPadding);
} else {
squareSize = slotSize - (this.unitPadding * 2);
}
const squareSize = slotSize - (this.unitPadding * 2);
// The grid is laid out notionally left-to-right, bottom-to-top,
// so we rotate and/or flip the y axis to match the target configuration.
@@ -430,7 +422,7 @@ export default class BlockScene {
// calculates and returns the size of the tx in multiples of the grid size
private txSize(tx: TxView): number {
const scale = Math.max(1, Math.round(Math.sqrt(tx.vsize / this.vbytesPerUnit)));
const scale = Math.max(1, Math.round(Math.sqrt(1.1 * tx.vsize / this.vbytesPerUnit)));
return Math.min(this.gridWidth, Math.max(1, scale)); // bound between 1 and the max displayable size (just in case!)
}

View File

@@ -7,6 +7,7 @@ import BlockScene from './block-scene';
const hoverTransitionTime = 300;
const defaultHoverColor = hexToColor('1bd8f4');
const defaultHighlightColor = hexToColor('800080');
const feeColors = mempoolFeeColors.map(hexToColor);
const auditFeeColors = feeColors.map((color) => darken(desaturate(color, 0.3), 0.9));
@@ -44,8 +45,10 @@ export default class TxView implements TransactionStripped {
initialised: boolean;
vertexArray: FastVertexArray;
hover: boolean;
highlight: boolean;
sprite: TxSprite;
hoverColor: Color | void;
highlightColor: Color | void;
screenPosition: Square;
gridPosition: Square | void;
@@ -150,8 +153,40 @@ export default class TxView implements TransactionStripped {
} else {
this.hover = false;
this.hoverColor = null;
if (this.sprite) {
this.sprite.resume(hoverTransitionTime);
if (this.highlight) {
this.setHighlight(true, this.highlightColor);
} else {
if (this.sprite) {
this.sprite.resume(hoverTransitionTime);
}
}
}
this.dirty = false;
return performance.now() + hoverTransitionTime;
}
// Temporarily override the tx color
// returns minimum transition end time
setHighlight(highlightOn: boolean, color: Color | void = defaultHighlightColor): number {
if (highlightOn) {
this.highlight = true;
this.highlightColor = color;
this.sprite.update({
...this.highlightColor,
duration: hoverTransitionTime,
adjust: false,
temp: true
});
} else {
this.highlight = false;
this.highlightColor = null;
if (this.hover) {
this.setHover(true, this.hoverColor);
} else {
if (this.sprite) {
this.sprite.resume(hoverTransitionTime);
}
}
}
this.dirty = false;

View File

@@ -71,7 +71,7 @@
<app-block-overview-graph
#blockGraph
[isLoading]="false"
[resolution]="75"
[resolution]="80"
[blockLimit]="stateService.blockVSize"
[orientation]="'top'"
[flip]="false"

View File

@@ -52,8 +52,8 @@
.chart-container {
flex-grow: 0;
flex-shrink: 0;
width: 470px;
min-width: 470px;
width: 480px;
min-width: 480px;
padding: 0;
margin-right: 15px;
}

View File

@@ -100,7 +100,7 @@
</tbody>
</table>
</div>
<div class="col-sm">
<div class="col-sm" [class.graph-col]="webGlEnabled && !showAudit">
<table class="table table-borderless table-striped" *ngIf="!isMobile && !(webGlEnabled && !showAudit)">
<tbody>
<ng-container *ngTemplateOutlet="restOfTable"></ng-container>
@@ -110,7 +110,7 @@
<app-block-overview-graph
#blockGraphActual
[isLoading]="isLoadingOverview"
[resolution]="75"
[resolution]="86"
[blockLimit]="stateService.blockVSize"
[orientation]="'top'"
[flip]="false"
@@ -227,7 +227,7 @@
<div class="col-sm">
<h3 class="block-subtitle" *ngIf="!isMobile"><ng-container i18n="block.expected-block">Expected Block</ng-container> <span class="badge badge-pill badge-warning beta" i18n="beta">beta</span></h3>
<div class="block-graph-wrapper">
<app-block-overview-graph #blockGraphProjected [isLoading]="isLoadingOverview" [resolution]="75"
<app-block-overview-graph #blockGraphProjected [isLoading]="isLoadingOverview" [resolution]="86"
[blockLimit]="stateService.blockVSize" [orientation]="'top'" [flip]="false" [mirrorTxid]="hoverTx" [auditHighlighting]="showAudit"
(txClickEvent)="onTxClick($event)" (txHoverEvent)="onTxHover($event)" [unavailable]="!isMobile && !showAudit"></app-block-overview-graph>
<ng-container *ngIf="!isMobile || mode !== 'actual'; else emptyBlockInfo"></ng-container>
@@ -239,7 +239,7 @@
<div class="col-sm" *ngIf="!isMobile">
<h3 class="block-subtitle actual" *ngIf="!isMobile"><ng-container i18n="block.actual-block">Actual Block</ng-container> <a class="info-link" [routerLink]="['/docs/faq' | relativeUrl ]" fragment="how-do-block-audits-work"><fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true"></fa-icon></a></h3>
<div class="block-graph-wrapper">
<app-block-overview-graph #blockGraphActual [isLoading]="isLoadingOverview" [resolution]="75"
<app-block-overview-graph #blockGraphActual [isLoading]="isLoadingOverview" [resolution]="86"
[blockLimit]="stateService.blockVSize" [orientation]="'top'" [flip]="false" [mirrorTxid]="hoverTx" mode="mined" [auditHighlighting]="showAudit"
(txClickEvent)="onTxClick($event)" (txHoverEvent)="onTxHover($event)" [unavailable]="isMobile && !showAudit"></app-block-overview-graph>
<ng-container *ngTemplateOutlet="emptyBlockInfo"></ng-container>

View File

@@ -239,6 +239,7 @@ h1 {
.nav-tabs {
border-color: white;
border-width: 1px;
margin-bottom: 1em;
}
.nav-tabs .nav-link {
@@ -293,3 +294,7 @@ h1 {
margin-top: 0.75rem;
}
}
.graph-col {
flex-grow: 1.11;
}

View File

@@ -25,7 +25,7 @@
</ng-container>
<ng-template #mempoolMode>
<div class="block-sizer" [style]="blockSizerStyle">
<app-mempool-block-overview [index]="blockIndex" [pixelAlign]="true"></app-mempool-block-overview>
<app-mempool-block-overview [index]="blockIndex"></app-mempool-block-overview>
</div>
</ng-template>
<div class="fader"></div>

View File

@@ -130,6 +130,7 @@ export class HashrateChartComponent implements OnInit {
});
++hashIndex;
}
diffIndex++;
break;
}

View File

@@ -1,10 +1,9 @@
<app-block-overview-graph
#blockGraph
[isLoading]="isLoading$ | async"
[resolution]="75"
[resolution]="86"
[blockLimit]="stateService.blockVSize"
[orientation]="timeLtr ? 'right' : 'left'"
[flip]="true"
[pixelAlign]="pixelAlign"
(txClickEvent)="onTxClick($event)"
></app-block-overview-graph>

View File

@@ -16,7 +16,6 @@ import { Router } from '@angular/router';
})
export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChanges, AfterViewInit {
@Input() index: number;
@Input() pixelAlign: boolean = false;
@Output() txPreviewEvent = new EventEmitter<TransactionStripped | void>();
@ViewChild('blockGraph') blockGraph: BlockOverviewGraphComponent;

View File

@@ -80,6 +80,9 @@ export class SearchFormComponent implements OnInit {
}
return text.trim();
}),
tap((text) => {
this.stateService.searchText$.next(text);
}),
distinctUntilChanged(),
);