diff --git a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.html b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.html index ac29524bb..603c7e4c0 100644 --- a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.html +++ b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.html @@ -12,6 +12,7 @@ class="text-center bitcoin-block mined-block blockchain-blocks-offset-{{ offset }}-index-{{ i }}" [class.offscreen]="!static && count && i >= count" id="bitcoin-block-{{ block.height }}" [ngStyle]="blockStyles[i]" + [style]="blockTransformation" [class.blink-bg]="isSpecial(block.height)">   @@ -40,7 +41,7 @@ -
diff --git a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts index ca79b68a6..35499f162 100644 --- a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts +++ b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef, Input, OnChanges, SimpleChanges } from '@angular/core'; -import { BehaviorSubject, Observable, Subscription } from 'rxjs'; +import { Observable, Subscription, delay, filter, tap } from 'rxjs'; import { StateService } from '../../services/state.service'; import { specialBlocks } from '../../app.constants'; import { BlockExtended } from '../../interfaces/node-api.interface'; @@ -45,7 +45,10 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { markBlockSubscription: Subscription; txConfirmedSubscription: Subscription; loadingBlocks$: Observable; - showMiningInfo$: BehaviorSubject = new BehaviorSubject(false); + showMiningInfoSubscription: Subscription; + blockDisplayModeSubscription: Subscription; + blockDisplayMode: 'size' | 'fees'; + blockTransformation = {}; blockStyles = []; emptyBlockStyles = []; interval: any; @@ -78,22 +81,38 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { ) { } - enabledMiningInfoIfNeeded(url) { - const urlParts = url.split('/'); - const onDashboard = ['','testnet','signet','mining','acceleration'].includes(urlParts[urlParts.length - 1]); - if (onDashboard) { // Only update showMiningInfo if we are on the main, mining or acceleration dashboards - this.stateService.showMiningInfo$.next(url.includes('/mining') || url.includes('/acceleration')); - } - } - ngOnInit() { this.dynamicBlocksAmount = Math.min(8, this.stateService.env.KEEP_BLOCKS_AMOUNT); - if (['', 'testnet', 'signet'].includes(this.stateService.network)) { - this.enabledMiningInfoIfNeeded(this.location.path()); - this.location.onUrlChange((url) => this.enabledMiningInfoIfNeeded(url)); - this.showMiningInfo$ = this.stateService.showMiningInfo$; - } + this.blockDisplayMode = this.stateService.blockDisplayMode$.value as 'size' | 'fees'; + this.blockDisplayModeSubscription = this.stateService.blockDisplayMode$ + .pipe( + filter((mode: 'size' | 'fees') => mode !== this.blockDisplayMode), + tap(() => { + this.blockTransformation = this.timeLtr ? { + transform: 'scaleX(-1) rotateX(90deg)', + transition: 'transform 0.375s' + } : { + transform: 'rotateX(90deg)', + transition: 'transform 0.375s' + }; + }), + delay(375), + tap((mode) => { + this.blockDisplayMode = mode; + this.blockTransformation = this.timeLtr ? { + transform: 'scaleX(-1)', + transition: 'transform 0.375s' + } : { + transition: 'transform 0.375s' + }; + this.cd.markForCheck(); + }), + delay(375), + ) + .subscribe(() => { + this.blockTransformation = {}; + }); this.timeLtrSubscription = this.stateService.timeLtr.subscribe((ltr) => { this.timeLtr = !!ltr; @@ -204,6 +223,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { this.networkSubscription.unsubscribe(); this.tabHiddenSubscription.unsubscribe(); this.markBlockSubscription.unsubscribe(); + this.blockDisplayModeSubscription.unsubscribe(); this.timeLtrSubscription.unsubscribe(); clearInterval(this.interval); } diff --git a/frontend/src/app/components/blockchain/blockchain.component.html b/frontend/src/app/components/blockchain/blockchain.component.html index 5f625e4b3..af3bf52b1 100644 --- a/frontend/src/app/components/blockchain/blockchain.component.html +++ b/frontend/src/app/components/blockchain/blockchain.component.html @@ -10,6 +10,7 @@
+
diff --git a/frontend/src/app/components/blockchain/blockchain.component.scss b/frontend/src/app/components/blockchain/blockchain.component.scss index a8ecc6aba..b0a589a04 100644 --- a/frontend/src/app/components/blockchain/blockchain.component.scss +++ b/frontend/src/app/components/blockchain/blockchain.component.scss @@ -67,9 +67,24 @@ padding: 0; } +.block-display-toggle { + color: white; + font-size: 0.8rem; + position: absolute; + bottom: 15.8em; + left: 1px; + transform: translateX(-50%) rotate(90deg); + background: none; + border: none; + outline: none; + margin: 0; + padding: 0; +} + .blockchain-wrapper.ltr-transition .blocks-wrapper, .blockchain-wrapper.ltr-transition .position-container, -.blockchain-wrapper.ltr-transition .time-toggle { +.blockchain-wrapper.ltr-transition .time-toggle, +.blockchain-wrapper.ltr-transition .block-display-toggle { transition: transform 1s; } @@ -81,6 +96,10 @@ .time-toggle { transform: translateX(-50%) scaleX(-1); } + + .block-display-toggle { + transform: translateX(-50%) scaleX(-1) rotate(90deg); + } } :host-context(.ltr-layout) { diff --git a/frontend/src/app/components/blockchain/blockchain.component.ts b/frontend/src/app/components/blockchain/blockchain.component.ts index 2293b9479..60dc22e12 100644 --- a/frontend/src/app/components/blockchain/blockchain.component.ts +++ b/frontend/src/app/components/blockchain/blockchain.component.ts @@ -1,6 +1,7 @@ import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, Input, Output, EventEmitter, ChangeDetectorRef, OnChanges, SimpleChanges } from '@angular/core'; import { firstValueFrom, Subscription } from 'rxjs'; import { StateService } from '../../services/state.service'; +import { StorageService } from '../../services/storage.service'; @Component({ selector: 'app-blockchain', @@ -26,15 +27,18 @@ export class BlockchainComponent implements OnInit, OnDestroy, OnChanges { connectionStateSubscription: Subscription; loadingTip: boolean = true; connected: boolean = true; + blockDisplayMode: 'size' | 'fees'; dividerOffset: number | null = null; mempoolOffset: number | null = null; positionStyle = { transform: "translateX(1280px)", }; + blockDisplayToggleStyle = {}; constructor( public stateService: StateService, + public StorageService: StorageService, private cd: ChangeDetectorRef, ) {} @@ -51,6 +55,7 @@ export class BlockchainComponent implements OnInit, OnDestroy, OnChanges { firstValueFrom(this.stateService.chainTip$).then(() => { this.loadingTip = false; }); + this.blockDisplayMode = this.StorageService.getValue('block-display-mode-preference') as 'size' | 'fees' || 'size'; } ngOnDestroy(): void { @@ -84,6 +89,13 @@ export class BlockchainComponent implements OnInit, OnDestroy, OnChanges { }, 0); } + toggleBlockDisplayMode(): void { + if (this.blockDisplayMode === 'size') this.blockDisplayMode = 'fees'; + else this.blockDisplayMode = 'size'; + this.StorageService.setValue('block-display-mode-preference', this.blockDisplayMode); + this.stateService.blockDisplayMode$.next(this.blockDisplayMode); + } + onMempoolWidthChange(width): void { if (this.flipping) { return; diff --git a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.html b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.html index aa2043af2..24f229598 100644 --- a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.html +++ b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.html @@ -7,7 +7,7 @@ class="spotlight-bottom" [style.right]="mempoolBlockStyles[i].right" > -
+
 
@@ -20,7 +20,7 @@ -
-
+
diff --git a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts index f403eac71..dee770cd8 100644 --- a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts +++ b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts @@ -1,9 +1,9 @@ import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef, HostListener, Input, OnChanges, SimpleChanges, Output, EventEmitter } from '@angular/core'; -import { Subscription, Observable, of, combineLatest, BehaviorSubject } from 'rxjs'; +import { Subscription, Observable, of, combineLatest } from 'rxjs'; import { MempoolBlock } from '../../interfaces/websocket.interface'; import { StateService } from '../../services/state.service'; import { Router } from '@angular/router'; -import { map, switchMap, tap } from 'rxjs/operators'; +import { delay, filter, map, switchMap, tap } from 'rxjs/operators'; import { feeLevels } from '../../app.constants'; import { specialBlocks } from '../../app.constants'; import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; @@ -43,7 +43,10 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy { mempoolBlocks$: Observable; difficultyAdjustments$: Observable; loadingBlocks$: Observable; - showMiningInfo$: BehaviorSubject = new BehaviorSubject(false); + showMiningInfoSubscription: Subscription; + blockDisplayModeSubscription: Subscription; + blockDisplayMode: 'size' | 'fees'; + blockTransformation = {}; blocksSubscription: Subscription; mempoolBlocksFull: MempoolBlock[] = []; @@ -99,9 +102,29 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy { this.mempoolWidth = width; this.widthChange.emit(this.mempoolWidth); - if (['', 'testnet', 'signet'].includes(this.stateService.network)) { - this.showMiningInfo$ = this.stateService.showMiningInfo$; - } + this.blockDisplayMode = this.stateService.blockDisplayMode$.value as 'size' | 'fees'; + this.blockDisplayModeSubscription = this.stateService.blockDisplayMode$ + .pipe( + filter((mode: 'size' | 'fees') => mode !== this.blockDisplayMode), + tap(() => { + this.blockTransformation = { + transform: 'rotateX(90deg)', + transition: 'transform 0.375s' + }; + }), + delay(375), + tap((mode) => { + this.blockDisplayMode = mode; + this.blockTransformation = { + transition: 'transform 0.375s' + }; + this.cd.markForCheck(); + }), + delay(375), + ) + .subscribe(() => { + this.blockTransformation = {}; + }); this.timeLtrSubscription = this.stateService.timeLtr.subscribe((ltr) => { this.timeLtr = !this.forceRtl && !!ltr; @@ -262,6 +285,7 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy { this.markBlocksSubscription.unsubscribe(); this.blockSubscription.unsubscribe(); this.networkSubscription.unsubscribe(); + this.blockDisplayModeSubscription.unsubscribe(); this.timeLtrSubscription.unsubscribe(); this.chainTipSubscription.unsubscribe(); this.keySubscription.unsubscribe(); diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index 970bfcac3..ebc4786d1 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -151,7 +151,7 @@ export class StateService { hideAudit: BehaviorSubject; fiatCurrency$: BehaviorSubject; rateUnits$: BehaviorSubject; - showMiningInfo$: BehaviorSubject = new BehaviorSubject(false); + blockDisplayMode$: BehaviorSubject = new BehaviorSubject('size'); searchFocus$: Subject = new Subject(); menuOpen$: BehaviorSubject = new BehaviorSubject(false); @@ -259,6 +259,9 @@ export class StateService { const rateUnitPreference = this.storageService.getValue('rate-unit-preference'); this.rateUnits$ = new BehaviorSubject(rateUnitPreference || 'vb'); + const blockDisplayModePreference = this.storageService.getValue('block-display-mode-preference'); + this.blockDisplayMode$ = new BehaviorSubject(blockDisplayModePreference || 'size'); + this.backend$.subscribe(backend => { this.backend = backend; });