scroll to see all mempool blocks

This commit is contained in:
Mononaut 2023-06-14 14:33:13 -04:00
parent 5f787db30d
commit d848ab4bef
No known key found for this signature in database
GPG Key ID: A3F058E41374C04E
6 changed files with 105 additions and 56 deletions

View File

@ -1,9 +1,9 @@
<div class="text-center" class="blockchain-wrapper" [class.time-ltr]="timeLtr" [class.ltr-transition]="ltrTransitionEnabled" #container> <div class="text-center" class="blockchain-wrapper" [class.time-ltr]="timeLtr" [class.ltr-transition]="ltrTransitionEnabled" #container>
<div class="position-container" [ngClass]="network ? network : ''"> <div class="position-container" [ngClass]="network ? network : ''" [style.--divider-offset]="dividerOffset + 'px'" [style.--mempool-offset]="mempoolOffset + 'px'">
<span> <span>
<div class="blocks-wrapper"> <div class="blocks-wrapper">
<div class="scroll-spacer" *ngIf="minScrollWidth" [style.left]="minScrollWidth + 'px'"></div> <div class="scroll-spacer" *ngIf="minScrollWidth" [style.left]="minScrollWidth + 'px'"></div>
<app-mempool-blocks [hidden]="pageIndex > 0"></app-mempool-blocks> <app-mempool-blocks [hidden]="pageIndex > 0" [allBlocks]="scrollableMempool" (widthChange)="onMempoolWidthChange($event)"></app-mempool-blocks>
<app-blockchain-blocks [hidden]="pageIndex > 0"></app-blockchain-blocks> <app-blockchain-blocks [hidden]="pageIndex > 0"></app-blockchain-blocks>
<ng-container *ngFor="let page of pages; trackBy: trackByPageFn"> <ng-container *ngFor="let page of pages; trackBy: trackByPageFn">
<app-blockchain-blocks [static]="true" [offset]="page.offset" [height]="page.height" [count]="blocksPerPage" [loadingTip]="loadingTip" [connected]="connected"></app-blockchain-blocks> <app-blockchain-blocks [static]="true" [offset]="page.offset" [height]="page.height" [count]="blocksPerPage" [loadingTip]="loadingTip" [connected]="connected"></app-blockchain-blocks>

View File

@ -26,43 +26,14 @@
position: absolute; position: absolute;
left: 0; left: 0;
top: 75px; top: 75px;
transform: translateX(50vw); --divider-offset: 50vw;
--mempool-offset: 0px;
transform: translateX(calc(var(--divider-offset) + var(--mempool-offset)));
} }
.position-container.liquid, .position-container.liquidtestnet { .blockchain-wrapper.time-ltr {
transform: translateX(420px); .position-container {
} transform: translateX(calc(100vw - var(--divider-offset) - var(--mempool-offset)));
@media (min-width: 768px) {
.blockchain-wrapper.time-ltr {
.position-container.liquid, .position-container.liquidtestnet {
transform: translateX(calc(100vw - 420px));
}
}
}
@media (max-width: 767.98px) {
.blockchain-wrapper {
.position-container {
transform: translateX(95vw);
}
.position-container.liquid, .position-container.liquidtestnet {
transform: translateX(50vw);
}
.position-container.loading {
transform: translateX(50vw);
}
}
.blockchain-wrapper.time-ltr {
.position-container {
transform: translateX(5vw);
}
.position-container.liquid, .position-container.liquidtestnet {
transform: translateX(50vw);
}
.position-container.loading {
transform: translateX(50vw);
}
} }
} }

View File

@ -1,4 +1,4 @@
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, Input, OnChanges, SimpleChanges } from '@angular/core'; import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, Input, Output, EventEmitter, HostListener, ChangeDetectorRef } from '@angular/core';
import { firstValueFrom, Subscription } from 'rxjs'; import { firstValueFrom, Subscription } from 'rxjs';
import { StateService } from '../../services/state.service'; import { StateService } from '../../services/state.service';
@ -13,43 +13,95 @@ export class BlockchainComponent implements OnInit, OnDestroy {
@Input() pageIndex: number; @Input() pageIndex: number;
@Input() blocksPerPage: number = 8; @Input() blocksPerPage: number = 8;
@Input() minScrollWidth: number = 0; @Input() minScrollWidth: number = 0;
@Input() scrollableMempool: boolean = false;
@Output() mempoolOffsetChange: EventEmitter<number> = new EventEmitter();
network: string; network: string;
timeLtrSubscription: Subscription; timeLtrSubscription: Subscription;
timeLtr: boolean = this.stateService.timeLtr.value; timeLtr: boolean = this.stateService.timeLtr.value;
ltrTransitionEnabled = false; ltrTransitionEnabled = false;
flipping = false;
connectionStateSubscription: Subscription; connectionStateSubscription: Subscription;
loadingTip: boolean = true; loadingTip: boolean = true;
connected: boolean = true; connected: boolean = true;
dividerOffset: number = 0;
mempoolOffset: number = 0;
constructor( constructor(
public stateService: StateService, public stateService: StateService,
private cd: ChangeDetectorRef,
) {} ) {}
ngOnInit() { ngOnInit(): void {
this.onResize();
this.network = this.stateService.network; this.network = this.stateService.network;
this.timeLtrSubscription = this.stateService.timeLtr.subscribe((ltr) => { this.timeLtrSubscription = this.stateService.timeLtr.subscribe((ltr) => {
this.timeLtr = !!ltr; this.timeLtr = !!ltr;
}); });
this.connectionStateSubscription = this.stateService.connectionState$.subscribe(state => { this.connectionStateSubscription = this.stateService.connectionState$.subscribe(state => {
this.connected = (state === 2); this.connected = (state === 2);
}) });
firstValueFrom(this.stateService.chainTip$).then(tip => { firstValueFrom(this.stateService.chainTip$).then(() => {
this.loadingTip = false; this.loadingTip = false;
}); });
} }
ngOnDestroy() { ngOnDestroy(): void {
this.timeLtrSubscription.unsubscribe(); this.timeLtrSubscription.unsubscribe();
this.connectionStateSubscription.unsubscribe(); this.connectionStateSubscription.unsubscribe();
} }
trackByPageFn(index: number, item: { index: number }) { trackByPageFn(index: number, item: { index: number }): number {
return item.index; return item.index;
} }
toggleTimeDirection() { toggleTimeDirection(): void {
this.ltrTransitionEnabled = true; this.ltrTransitionEnabled = false;
this.stateService.timeLtr.next(!this.timeLtr); const prevOffset = this.mempoolOffset;
this.mempoolOffset = 0;
this.mempoolOffsetChange.emit(0);
setTimeout(() => {
this.ltrTransitionEnabled = true;
this.flipping = true;
this.stateService.timeLtr.next(!this.timeLtr);
setTimeout(() => {
this.ltrTransitionEnabled = false;
this.flipping = false;
this.mempoolOffset = prevOffset;
this.mempoolOffsetChange.emit(this.mempoolOffset);
}, 1000);
}, 0);
this.cd.markForCheck();
}
onMempoolWidthChange(width): void {
if (this.flipping) {
return;
}
this.mempoolOffset = Math.max(0, width - this.dividerOffset);
this.cd.markForCheck();
setTimeout(() => {
this.mempoolOffsetChange.emit(this.mempoolOffset);
}, 0);
}
@HostListener('window:resize', ['$event'])
onResize(): void {
if (window.innerWidth >= 768) {
if (this.stateService.isLiquid()) {
this.dividerOffset = 420;
} else {
this.dividerOffset = window.innerWidth * 0.5;
}
} else {
if (this.stateService.isLiquid()) {
this.dividerOffset = window.innerWidth * 0.5;
} else {
this.dividerOffset = window.innerWidth * 0.95;
}
}
this.cd.markForCheck();
} }
} }

View File

@ -1,9 +1,9 @@
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef, HostListener, Input, OnChanges, SimpleChanges } from '@angular/core'; import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef, HostListener, Input, OnChanges, SimpleChanges, Output, EventEmitter } from '@angular/core';
import { Subscription, Observable, fromEvent, merge, of, combineLatest } from 'rxjs'; import { Subscription, Observable, fromEvent, merge, of, combineLatest } from 'rxjs';
import { MempoolBlock } from '../../interfaces/websocket.interface'; import { MempoolBlock } from '../../interfaces/websocket.interface';
import { StateService } from '../../services/state.service'; import { StateService } from '../../services/state.service';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { take, map, switchMap } from 'rxjs/operators'; import { take, map, switchMap, tap } from 'rxjs/operators';
import { feeLevels, mempoolFeeColors } from '../../app.constants'; import { feeLevels, mempoolFeeColors } from '../../app.constants';
import { specialBlocks } from '../../app.constants'; import { specialBlocks } from '../../app.constants';
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
@ -29,6 +29,9 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
@Input() count: number = null; @Input() count: number = null;
@Input() spotlight: number = 0; @Input() spotlight: number = 0;
@Input() getHref?: (index) => string = (index) => `/mempool-block/${index}`; @Input() getHref?: (index) => string = (index) => `/mempool-block/${index}`;
@Input() allBlocks: boolean = false;
@Output() widthChange: EventEmitter<number> = new EventEmitter();
specialBlocks = specialBlocks; specialBlocks = specialBlocks;
mempoolBlocks: MempoolBlock[] = []; mempoolBlocks: MempoolBlock[] = [];
@ -145,7 +148,12 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
this.updateMempoolBlockStyles(); this.updateMempoolBlockStyles();
this.calculateTransactionPosition(); this.calculateTransactionPosition();
return this.mempoolBlocks; return this.mempoolBlocks;
}),
tap(() => {
this.cd.markForCheck();
this.widthChange.emit(this.containerOffset + this.mempoolBlocks.length * this.blockOffset);
}) })
); );
@ -254,7 +262,10 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
reduceEmptyBlocksToFitScreen(blocks: MempoolBlock[]): MempoolBlock[] { reduceEmptyBlocksToFitScreen(blocks: MempoolBlock[]): MempoolBlock[] {
const innerWidth = this.stateService.env.BASE_MODULE !== 'liquid' && window.innerWidth <= 767.98 ? window.innerWidth : window.innerWidth / 2; const innerWidth = this.stateService.env.BASE_MODULE !== 'liquid' && window.innerWidth <= 767.98 ? window.innerWidth : window.innerWidth / 2;
const blocksAmount = Math.min(this.stateService.env.MEMPOOL_BLOCKS_AMOUNT, Math.floor(innerWidth / (this.blockWidth + this.blockPadding))); let blocksAmount = this.stateService.env.MEMPOOL_BLOCKS_AMOUNT;
if (!this.allBlocks) {
blocksAmount = Math.min(this.stateService.env.MEMPOOL_BLOCKS_AMOUNT, Math.floor(innerWidth / (this.blockWidth + this.blockPadding)));
}
while (blocks.length < blocksAmount) { while (blocks.length < blocksAmount) {
blocks.push({ blocks.push({
blockSize: 0, blockSize: 0,
@ -274,10 +285,10 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
reduceMempoolBlocksToFitScreen(blocks: MempoolBlock[]): MempoolBlock[] { reduceMempoolBlocksToFitScreen(blocks: MempoolBlock[]): MempoolBlock[] {
const innerWidth = this.stateService.env.BASE_MODULE !== 'liquid' && window.innerWidth <= 767.98 ? window.innerWidth : window.innerWidth / 2; const innerWidth = this.stateService.env.BASE_MODULE !== 'liquid' && window.innerWidth <= 767.98 ? window.innerWidth : window.innerWidth / 2;
let blocksAmount; let blocksAmount = this.stateService.env.MEMPOOL_BLOCKS_AMOUNT;
if (this.count) { if (this.count) {
blocksAmount = 8; blocksAmount = 8;
} else { } else if (!this.allBlocks) {
blocksAmount = Math.min(this.stateService.env.MEMPOOL_BLOCKS_AMOUNT, Math.floor(innerWidth / (this.blockWidth + this.blockPadding))); blocksAmount = Math.min(this.stateService.env.MEMPOOL_BLOCKS_AMOUNT, Math.floor(innerWidth / (this.blockWidth + this.blockPadding)));
} }
while (blocks.length > blocksAmount) { while (blocks.length > blocksAmount) {

View File

@ -18,7 +18,7 @@
(dragstart)="onDragStart($event)" (dragstart)="onDragStart($event)"
(scroll)="onScroll($event)" (scroll)="onScroll($event)"
> >
<app-blockchain [pageIndex]="pageIndex" [pages]="pages" [blocksPerPage]="blocksPerPage" [minScrollWidth]="minScrollWidth"></app-blockchain> <app-blockchain [pageIndex]="pageIndex" [pages]="pages" [blocksPerPage]="blocksPerPage" [minScrollWidth]="minScrollWidth" [scrollableMempool]="true" (mempoolOffsetChange)="onMempoolOffsetChange($event)"></app-blockchain>
</div> </div>
<div class="reset-scroll" [class.hidden]="pageIndex === 0" (click)="resetScroll()"> <div class="reset-scroll" [class.hidden]="pageIndex === 0" (click)="resetScroll()">
<fa-icon [icon]="['fas', 'circle-left']" [fixedWidth]="true"></fa-icon> <fa-icon [icon]="['fas', 'circle-left']" [fixedWidth]="true"></fa-icon>

View File

@ -44,6 +44,7 @@ export class StartComponent implements OnInit, OnDestroy {
lastUpdate: number = 0; lastUpdate: number = 0;
lastMouseX: number; lastMouseX: number;
velocity: number = 0; velocity: number = 0;
mempoolOffset: number = 0;
constructor( constructor(
private stateService: StateService, private stateService: StateService,
@ -117,6 +118,12 @@ export class StartComponent implements OnInit, OnDestroy {
}); });
} }
onMempoolOffsetChange(offset): void {
const delta = offset - this.mempoolOffset;
this.addConvertedScrollOffset(delta);
this.mempoolOffset = offset;
}
@HostListener('window:resize', ['$event']) @HostListener('window:resize', ['$event'])
onResize(): void { onResize(): void {
this.isMobile = window.innerWidth <= 767.98; this.isMobile = window.innerWidth <= 767.98;
@ -350,7 +357,7 @@ export class StartComponent implements OnInit, OnDestroy {
resetScroll(): void { resetScroll(): void {
this.scrollToBlock(this.chainTip); this.scrollToBlock(this.chainTip);
this.blockchainContainer.nativeElement.scrollLeft = 0; this.setScrollLeft(0);
} }
getPageIndexOf(height: number): number { getPageIndexOf(height: number): number {
@ -368,9 +375,17 @@ export class StartComponent implements OnInit, OnDestroy {
getConvertedScrollOffset(): number { getConvertedScrollOffset(): number {
if (this.timeLtr) { if (this.timeLtr) {
return -this.blockchainContainer?.nativeElement?.scrollLeft || 0; return -(this.blockchainContainer?.nativeElement?.scrollLeft || 0) - this.mempoolOffset;
} else { } else {
return this.blockchainContainer?.nativeElement?.scrollLeft || 0; return (this.blockchainContainer?.nativeElement?.scrollLeft || 0) - this.mempoolOffset;
}
}
setScrollLeft(offset: number): void {
if (this.timeLtr) {
this.blockchainContainer.nativeElement.scrollLeft = offset - this.mempoolOffset;
} else {
this.blockchainContainer.nativeElement.scrollLeft = offset + this.mempoolOffset;
} }
} }
@ -388,7 +403,7 @@ export class StartComponent implements OnInit, OnDestroy {
ngOnDestroy() { ngOnDestroy() {
if (this.blockchainContainer?.nativeElement) { if (this.blockchainContainer?.nativeElement) {
// clean up scroll position to prevent caching wrong scroll in Firefox // clean up scroll position to prevent caching wrong scroll in Firefox
this.blockchainContainer.nativeElement.scrollLeft = 0; this.setScrollLeft(0);
} }
this.timeLtrSubscription.unsubscribe(); this.timeLtrSubscription.unsubscribe();
this.chainTipSubscription.unsubscribe(); this.chainTipSubscription.unsubscribe();