Merge pull request #5004 from mempool/natsoni/toggle-block-fees
Add block display mode toggle
This commit is contained in:
		
						commit
						a66920c51e
					
				| @ -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)"> | ||||
|         <a draggable="false" [routerLink]="[getHref(i, block) | relativeUrl]" [state]="{ data: { block: block } }" | ||||
|           class="blockLink" [ngClass]="{'disabled': (this.stateService.blockScrolling$ | async)}"> </a> | ||||
| @ -40,7 +41,7 @@ | ||||
|                 <app-fee-rate unitClass=""></app-fee-rate> | ||||
|               </div> | ||||
|             </ng-template> | ||||
|             <div [attr.data-cy]="'bitcoin-block-' + offset + '-index-' + i + '-total-fees'" *ngIf="showMiningInfo$ | async; else noMiningInfo" | ||||
|             <div [attr.data-cy]="'bitcoin-block-' + offset + '-index-' + i + '-total-fees'" *ngIf="blockDisplayMode === 'fees'; else noMiningInfo" | ||||
|               class="block-size"> | ||||
|               <app-amount [satoshis]="block.extras?.totalFees ?? 0" digitsInfo="1.2-3" [noFiat]="true"></app-amount> | ||||
|             </div> | ||||
|  | ||||
| @ -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<boolean>; | ||||
|   showMiningInfo$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(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); | ||||
|   } | ||||
|  | ||||
| @ -10,6 +10,7 @@ | ||||
|         </ng-container> | ||||
|       </div> | ||||
|       <div id="divider" [hidden]="pageIndex > 0"> | ||||
|         <button class="block-display-toggle" (click)="toggleBlockDisplayMode()"><fa-icon [icon]="['fas', 'exchange-alt']" [fixedWidth]="true"></fa-icon></button> | ||||
|         <button class="time-toggle" (click)="toggleTimeDirection()"><fa-icon [icon]="['fas', 'exchange-alt']" [fixedWidth]="true"></fa-icon></button> | ||||
|       </div> | ||||
|     </span> | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -7,7 +7,7 @@ | ||||
|           class="spotlight-bottom" | ||||
|           [style.right]="mempoolBlockStyles[i].right" | ||||
|         ></div> | ||||
|         <div @blockEntryTrigger [@.disabled]="i > 0 || !animateEntry" [attr.data-cy]="'mempool-block-' + i" class="bitcoin-block text-center mempool-block" [class.hide-block]="count && i >= count" id="mempool-block-{{ i }}" [ngStyle]="mempoolBlockStyles[i]" [class.blink-bg]="projectedBlock.blink"> | ||||
|         <div @blockEntryTrigger [@.disabled]="i > 0 || !animateEntry" [attr.data-cy]="'mempool-block-' + i" class="bitcoin-block text-center mempool-block" [class.hide-block]="count && i >= count" id="mempool-block-{{ i }}" [ngStyle]="mempoolBlockStyles[i]" [class.blink-bg]="projectedBlock.blink" [style]="blockTransformation"> | ||||
|           <a draggable="false" [routerLink]="[getHref(i) | relativeUrl]" | ||||
|             class="blockLink" [ngClass]="{'disabled': (this.stateService.blockScrolling$ | async)}"> </a> | ||||
|           <div class="block-body"> | ||||
| @ -20,7 +20,7 @@ | ||||
|                 - | ||||
|                 <app-fee-rate [fee]="projectedBlock.feeRange[projectedBlock.feeRange.length - 1]" rounding="1.0-0" unitClass=""></app-fee-rate> | ||||
|               </div> | ||||
|               <div *ngIf="showMiningInfo$ | async; else noMiningInfo" class="block-size"> | ||||
|               <div *ngIf="blockDisplayMode === 'fees'; else noMiningInfo" class="block-size"> | ||||
|                 <app-amount [attr.data-cy]="'mempool-block-' + i + '-total-fees'" [satoshis]="projectedBlock.totalFees" digitsInfo="1.2-3" [noFiat]="true"></app-amount> | ||||
|               </div> | ||||
|               <ng-template #noMiningInfo> | ||||
|  | ||||
| @ -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<MempoolBlock[]>; | ||||
|   difficultyAdjustments$: Observable<DifficultyAdjustment>; | ||||
|   loadingBlocks$: Observable<boolean>; | ||||
|   showMiningInfo$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(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(); | ||||
|  | ||||
| @ -151,7 +151,7 @@ export class StateService { | ||||
|   hideAudit: BehaviorSubject<boolean>; | ||||
|   fiatCurrency$: BehaviorSubject<string>; | ||||
|   rateUnits$: BehaviorSubject<string>; | ||||
|   showMiningInfo$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); | ||||
|   blockDisplayMode$: BehaviorSubject<string> = new BehaviorSubject<string>('size'); | ||||
| 
 | ||||
|   searchFocus$: Subject<boolean> = new Subject<boolean>(); | ||||
|   menuOpen$: BehaviorSubject<boolean> = new BehaviorSubject(false); | ||||
| @ -259,6 +259,9 @@ export class StateService { | ||||
|     const rateUnitPreference = this.storageService.getValue('rate-unit-preference'); | ||||
|     this.rateUnits$ = new BehaviorSubject<string>(rateUnitPreference || 'vb'); | ||||
| 
 | ||||
|     const blockDisplayModePreference = this.storageService.getValue('block-display-mode-preference'); | ||||
|     this.blockDisplayMode$ = new BehaviorSubject<string>(blockDisplayModePreference || 'size'); | ||||
| 
 | ||||
|     this.backend$.subscribe(backend => { | ||||
|       this.backend = backend; | ||||
|     }); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user