Compare commits

...

2 Commits

Author SHA1 Message Date
Mononaut
81669c39e9
Improve blockchain animations during IBD 2023-04-07 02:39:10 +09:00
Mononaut
00d94f5614
Improve blockchain animations while syncing backend 2023-04-07 01:43:15 +09:00
8 changed files with 55 additions and 33 deletions

View File

@ -22,6 +22,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
@Input() offset: number = 0; @Input() offset: number = 0;
@Input() height: number = 0; // max height of blocks in chunk (dynamic blocks only) @Input() height: number = 0; // max height of blocks in chunk (dynamic blocks only)
@Input() count: number = 8; // number of blocks in this chunk (dynamic blocks only) @Input() count: number = 8; // number of blocks in this chunk (dynamic blocks only)
@Input() dynamicBlockCount: number = 8; // number of blocks in the dynamic block chunk
@Input() loadingTip: boolean = false; @Input() loadingTip: boolean = false;
@Input() connected: boolean = true; @Input() connected: boolean = true;
@ -45,7 +46,6 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
feeRounding = '1.0-0'; feeRounding = '1.0-0';
arrowVisible = false; arrowVisible = false;
arrowLeftPx = 30; arrowLeftPx = 30;
blocksFilled = false;
arrowTransition = '1s'; arrowTransition = '1s';
showMiningInfo = false; showMiningInfo = false;
timeLtrSubscription: Subscription; timeLtrSubscription: Subscription;
@ -96,14 +96,13 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
this.tabHiddenSubscription = this.stateService.isTabHidden$.subscribe((tabHidden) => this.tabHidden = tabHidden); this.tabHiddenSubscription = this.stateService.isTabHidden$.subscribe((tabHidden) => this.tabHidden = tabHidden);
if (!this.static) { if (!this.static) {
this.blocksSubscription = this.stateService.blocks$ this.blocksSubscription = this.stateService.blocks$
.subscribe(([block, txConfirmed]) => { .subscribe(([block, txConfirmed, batch]) => {
if (this.blocks.some((b) => b.height === block.height)) { if (this.blocks.some((b) => b.height === block.height)) {
return; return;
} }
if (this.blocks.length && block.height !== this.blocks[0].height + 1) { if (this.blocks.length && block.height !== this.blocks[0].height + 1) {
this.blocks = []; this.blocks = [];
this.blocksFilled = false;
} }
this.blocks.unshift(block); this.blocks.unshift(block);
@ -117,20 +116,18 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
} }
this.blockStyles = []; this.blockStyles = [];
if (this.blocksFilled && block.height > this.chainTip) { this.blocks.forEach((b, i) => {
this.blocks.forEach((b, i) => this.blockStyles.push(this.getStyleForBlock(b, i, i ? -155 : -205))); if (i === 0 && !batch && block.height > this.chainTip) {
this.blockStyles.push(this.getStyleForBlock(b, i, -205));
setTimeout(() => { setTimeout(() => {
this.blockStyles = []; this.blockStyles = [];
this.blocks.forEach((b, i) => this.blockStyles.push(this.getStyleForBlock(b, i))); this.blocks.forEach((b, i) => this.blockStyles.push(this.getStyleForBlock(b, i)));
this.cd.markForCheck(); this.cd.markForCheck();
}, 50); }, 50);
} else { } else {
this.blocks.forEach((b, i) => this.blockStyles.push(this.getStyleForBlock(b, i))); this.blockStyles.push(this.getStyleForBlock(b, i));
}
if (this.blocks.length === this.dynamicBlocksAmount) {
this.blocksFilled = true;
} }
});
this.chainTip = Math.max(this.chainTip, block.height); this.chainTip = Math.max(this.chainTip, block.height);
this.cd.markForCheck(); this.cd.markForCheck();
@ -160,7 +157,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
if (this.static) { if (this.static) {
const animateSlide = changes.height && (changes.height.currentValue === changes.height.previousValue + 1); const animateSlide = (changes.dynamicBlockCount && changes.dynamicBlockCount.previousValue != null) || (changes.height && (changes.height.currentValue === changes.height.previousValue + 1));
this.updateStaticBlocks(animateSlide); this.updateStaticBlocks(animateSlide);
} }
} }
@ -227,8 +224,8 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
block = this.cacheService.getCachedBlock(height) || null; block = this.cacheService.getCachedBlock(height) || null;
} }
this.blocks.push(block || { this.blocks.push(block || {
placeholder: height < 0, placeholder: !isNaN(height) && height < 0,
loading: height >= 0, loading: isNaN(height) || height >= 0,
id: '', id: '',
height, height,
version: 0, version: 0,
@ -311,6 +308,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
return { return {
left: addLeft + (155 * index) + 'px', left: addLeft + (155 * index) + 'px',
background: "#2d3348", background: "#2d3348",
transition: animateEnterFrom ? 'background 2s, transform 1s' : null,
}; };
} }
@ -318,6 +316,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
const addLeft = animateEnterFrom || 0; const addLeft = animateEnterFrom || 0;
return { return {
left: addLeft + (155 * index) + 'px', left: addLeft + (155 * index) + 'px',
transition: animateEnterFrom ? 'background 2s, transform 1s' : null,
}; };
} }
@ -326,6 +325,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
return { return {
left: addLeft + 155 * this.emptyBlocks.indexOf(block) + 'px', left: addLeft + 155 * this.emptyBlocks.indexOf(block) + 'px',
transition: animateEnterFrom ? 'background 2s, transform 1s' : null,
background: "#2d3348", background: "#2d3348",
}; };
} }

View File

@ -6,7 +6,7 @@
<app-mempool-blocks [hidden]="pageIndex > 0"></app-mempool-blocks> <app-mempool-blocks [hidden]="pageIndex > 0"></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" [dynamicBlockCount]="dynamicBlockCount" [count]="blocksPerPage" [loadingTip]="loadingTip" [connected]="connected"></app-blockchain-blocks>
</ng-container> </ng-container>
</div> </div>
<div id="divider" [hidden]="pageIndex > 0"> <div id="divider" [hidden]="pageIndex > 0">

View File

@ -12,6 +12,7 @@ export class BlockchainComponent implements OnInit, OnDestroy {
@Input() pages: any[] = []; @Input() pages: any[] = [];
@Input() pageIndex: number; @Input() pageIndex: number;
@Input() blocksPerPage: number = 8; @Input() blocksPerPage: number = 8;
@Input() dynamicBlockCount: number = 8;
@Input() minScrollWidth: number = 0; @Input() minScrollWidth: number = 0;
network: string; network: string;

View File

@ -105,7 +105,6 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
}); });
this.reduceMempoolBlocksToFitScreen(this.mempoolBlocks); this.reduceMempoolBlocksToFitScreen(this.mempoolBlocks);
this.stateService.isTabHidden$.subscribe((tabHidden) => this.tabHidden = tabHidden); this.stateService.isTabHidden$.subscribe((tabHidden) => this.tabHidden = tabHidden);
this.loadingBlocks$ = this.stateService.isLoadingWebSocket$;
this.mempoolBlocks$ = merge( this.mempoolBlocks$ = merge(
of(true), of(true),
@ -141,6 +140,13 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
}) })
); );
this.loadingBlocks$ = combineLatest([
this.stateService.isLoadingWebSocket$,
this.mempoolBlocks$
]).pipe(map(([loading, mempoolBlocks]) => {
return loading || !mempoolBlocks.length;
}));
this.difficultyAdjustments$ = this.stateService.difficultyAdjustment$ this.difficultyAdjustments$ = this.stateService.difficultyAdjustment$
.pipe( .pipe(
map((da) => { map((da) => {

View File

@ -16,7 +16,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" [dynamicBlockCount]="dynamicBlocksAmount" [blocksPerPage]="blocksPerPage" [minScrollWidth]="minScrollWidth"></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

@ -2,6 +2,7 @@ import { Component, ElementRef, HostListener, OnInit, OnDestroy, ViewChild } fro
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { StateService } from '../../services/state.service'; import { StateService } from '../../services/state.service';
import { specialBlocks } from '../../app.constants'; import { specialBlocks } from '../../app.constants';
import { BlockExtended } from '../../interfaces/node-api.interface';
@Component({ @Component({
selector: 'app-start', selector: 'app-start',
@ -29,7 +30,8 @@ export class StartComponent implements OnInit, OnDestroy {
isMobile: boolean = false; isMobile: boolean = false;
isiOS: boolean = false; isiOS: boolean = false;
blockWidth = 155; blockWidth = 155;
dynamicBlocksAmount: number = 8; blocks: BlockExtended[] = [];
dynamicBlocksAmount: number;
blockCount: number = 0; blockCount: number = 0;
blocksPerPage: number = 1; blocksPerPage: number = 1;
pageWidth: number; pageWidth: number;
@ -49,15 +51,28 @@ export class StartComponent implements OnInit, OnDestroy {
} }
ngOnInit() { ngOnInit() {
this.firstPageWidth = 40 + (this.blockWidth * (this.dynamicBlocksAmount || 0));
this.blockCounterSubscription = this.stateService.blocks$.subscribe(([block, txConfirmed, batch]) => {
if (this.blocks.some((b) => b.height === block.height)) {
return;
}
if (this.blocks.length && block.height !== this.blocks[0].height + 1) {
this.blocks = [];
}
this.blocks.unshift(block);
this.blocks = this.blocks.slice(0, Math.min(8, this.stateService.env.KEEP_BLOCKS_AMOUNT));
this.dynamicBlocksAmount = this.blocks.length;
this.firstPageWidth = 40 + (this.blockWidth * this.dynamicBlocksAmount); this.firstPageWidth = 40 + (this.blockWidth * this.dynamicBlocksAmount);
this.blockCounterSubscription = this.stateService.blocks$.subscribe(() => {
this.blockCount++; if (this.blocks.length <= Math.min(8, this.stateService.env.KEEP_BLOCKS_AMOUNT)) {
this.dynamicBlocksAmount = Math.min(this.blockCount, this.stateService.env.KEEP_BLOCKS_AMOUNT, 8);
this.firstPageWidth = 40 + (this.blockWidth * this.dynamicBlocksAmount);
if (this.blockCount <= Math.min(8, this.stateService.env.KEEP_BLOCKS_AMOUNT)) {
this.onResize(); this.onResize();
} }
}); });
this.onResize(); this.onResize();
this.updatePages(); this.updatePages();
this.timeLtrSubscription = this.stateService.timeLtr.subscribe((ltr) => { this.timeLtrSubscription = this.stateService.timeLtr.subscribe((ltr) => {

View File

@ -89,7 +89,7 @@ export class StateService {
networkChanged$ = new ReplaySubject<string>(1); networkChanged$ = new ReplaySubject<string>(1);
lightningChanged$ = new ReplaySubject<boolean>(1); lightningChanged$ = new ReplaySubject<boolean>(1);
blocks$: ReplaySubject<[BlockExtended, boolean]>; blocks$: ReplaySubject<[BlockExtended, boolean, boolean]>;
transactions$ = new ReplaySubject<TransactionStripped>(6); transactions$ = new ReplaySubject<TransactionStripped>(6);
conversions$ = new ReplaySubject<any>(1); conversions$ = new ReplaySubject<any>(1);
bsqPrice$ = new ReplaySubject<number>(1); bsqPrice$ = new ReplaySubject<number>(1);
@ -157,7 +157,7 @@ export class StateService {
} }
}); });
this.blocks$ = new ReplaySubject<[BlockExtended, boolean]>(this.env.KEEP_BLOCKS_AMOUNT); this.blocks$ = new ReplaySubject<[BlockExtended, boolean, boolean]>(this.env.KEEP_BLOCKS_AMOUNT);
if (this.env.BASE_MODULE === 'bisq') { if (this.env.BASE_MODULE === 'bisq') {
this.network = this.env.BASE_MODULE; this.network = this.env.BASE_MODULE;

View File

@ -228,7 +228,7 @@ export class WebsocketService {
blocks.forEach((block: BlockExtended) => { blocks.forEach((block: BlockExtended) => {
if (block.height > this.stateService.latestBlockHeight) { if (block.height > this.stateService.latestBlockHeight) {
maxHeight = Math.max(maxHeight, block.height); maxHeight = Math.max(maxHeight, block.height);
this.stateService.blocks$.next([block, false]); this.stateService.blocks$.next([block, false, true]);
} }
}); });
this.stateService.updateChainTip(maxHeight); this.stateService.updateChainTip(maxHeight);
@ -241,7 +241,7 @@ export class WebsocketService {
if (response.block) { if (response.block) {
if (response.block.height > this.stateService.latestBlockHeight) { if (response.block.height > this.stateService.latestBlockHeight) {
this.stateService.updateChainTip(response.block.height); this.stateService.updateChainTip(response.block.height);
this.stateService.blocks$.next([response.block, !!response.txConfirmed]); this.stateService.blocks$.next([response.block, !!response.txConfirmed, false]);
} }
if (response.txConfirmed) { if (response.txConfirmed) {