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..a1548b942 100644 --- a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts +++ b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts @@ -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,15 @@ 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$.subscribe((mode: 'size' | 'fees') => { + if (mode !== this.blockDisplayMode) { + this.applyAnimation(mode); + } + }); this.timeLtrSubscription = this.stateService.timeLtr.subscribe((ltr) => { this.timeLtr = !!ltr; @@ -204,6 +200,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); } @@ -243,6 +240,29 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { } } + applyAnimation(mode: 'size' | 'fees') { + this.blockTransformation = this.timeLtr ? { + transform: 'scaleX(-1) rotateX(90deg)', + transition: 'transform 0.375s' + } : { + transform: 'rotateX(90deg)', + transition: 'transform 0.375s' + }; + setTimeout(() => { + this.blockDisplayMode = mode; + this.blockTransformation = this.timeLtr ? { + transform: 'scaleX(-1)', + transition: 'transform 0.375s' + } : { + transition: 'transform 0.375s' + }; + this.cd.markForCheck(); + setTimeout(() => { + this.blockTransformation = {}; + }, 375); + }, 375); + } + trackByBlocksFn(index: number, item: BlockchainBlock) { return item.height; } 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..994d7ac6c 100644 --- a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts +++ b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts @@ -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,12 @@ 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$.subscribe((mode: 'size' | 'fees') => { + if (mode !== this.blockDisplayMode) { + this.applyAnimation(mode); + } + }); this.timeLtrSubscription = this.stateService.timeLtr.subscribe((ltr) => { this.timeLtr = !this.forceRtl && !!ltr; @@ -262,6 +268,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(); @@ -445,6 +452,23 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy { this.rightPosition = Math.min(this.maxArrowPosition, this.rightPosition); } + applyAnimation(mode: 'size' | 'fees') { + this.blockTransformation = { + transform: 'rotateX(90deg)', + transition: 'transform 0.375s' + }; + setTimeout(() => { + this.blockDisplayMode = mode; + this.blockTransformation = { + transition: 'transform 0.375s' + }; + this.cd.markForCheck(); + setTimeout(() => { + this.blockTransformation = {}; + }, 375); + }, 375); + } + mountEmptyBlocks() { const emptyBlocks = []; const numberOfBlocks = this.stateService.env.MEMPOOL_BLOCKS_AMOUNT; 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; });