Block visualization color-by-age mode
This commit is contained in:
		
							parent
							
								
									0813592a6d
								
							
						
					
					
						commit
						dcf78fab06
					
				| @ -39,6 +39,8 @@ export const mempoolFeeColors = [ | |||||||
|   'ae005b', |   'ae005b', | ||||||
| ]; | ]; | ||||||
| 
 | 
 | ||||||
|  | export const mempoolAgeColors = [  '28007d', '21017d', '1c027d', '14047d', '0d057d', '06067d', '081186',  '09188c', '0b2395', '0c2a9b', '0e37a6', '0f3caa', '1045b2', '114fbb',  '1254bf', '155cbf', '1965bf', '1e70be', '2076be', '2581bd', '2889bd',  '2d94bc', '309dbc', '34a6bc', '39b1bb', '3cbabb', '37bbb3', '32baa9',  '2bb99c', '25b993', '21b88c', '1db785', '19b67e', '14b475', '0eb36c',  '08b162', '02b059', '00ae53']; | ||||||
|  | 
 | ||||||
| export const chartColors = [ | export const chartColors = [ | ||||||
|   "#D81B60", |   "#D81B60", | ||||||
|   "#8E24AA", |   "#8E24AA", | ||||||
|  | |||||||
| @ -14,6 +14,8 @@ | |||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
|   <div class="filter-menu" *ngIf="menuOpen && cssWidth > 280"> |   <div class="filter-menu" *ngIf="menuOpen && cssWidth > 280"> | ||||||
|  |     <div class="filter-row"> | ||||||
|  |       <div class="filter-element"> | ||||||
|         <h5>Match</h5> |         <h5>Match</h5> | ||||||
|         <div class="btn-group btn-group-toggle"> |         <div class="btn-group btn-group-toggle"> | ||||||
|           <label class="btn btn-xs blue mode-toggle" [class.active]="filterMode === 'and'"> |           <label class="btn btn-xs blue mode-toggle" [class.active]="filterMode === 'and'"> | ||||||
| @ -23,6 +25,19 @@ | |||||||
|             <input type="radio" [value]="'any'" fragment="any" (click)="setFilterMode('or')">Any |             <input type="radio" [value]="'any'" fragment="any" (click)="setFilterMode('or')">Any | ||||||
|           </label> |           </label> | ||||||
|         </div> |         </div> | ||||||
|  |       </div> | ||||||
|  |       <div class="filter-element"> | ||||||
|  |         <h5>Gradient</h5> | ||||||
|  |         <div class="btn-group btn-group-toggle"> | ||||||
|  |           <label class="btn btn-xs yellow mode-toggle" [class.active]="gradientMode === 'fee'"> | ||||||
|  |             <input type="radio" [value]="'fee'" fragment="fee" (click)="setGradientMode('fee')">Fee | ||||||
|  |           </label> | ||||||
|  |           <label class="btn btn-xs blue mode-toggle" [class.active]="gradientMode === 'age'"> | ||||||
|  |             <input type="radio" [value]="'age'" fragment="age" (click)="setGradientMode('age')">Age | ||||||
|  |           </label> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|     <ng-container *ngFor="let group of filterGroups;"> |     <ng-container *ngFor="let group of filterGroups;"> | ||||||
|       <h5>{{ group.label }}</h5> |       <h5>{{ group.label }}</h5> | ||||||
|       <div class="filter-group"> |       <div class="filter-group"> | ||||||
|  | |||||||
| @ -45,6 +45,13 @@ | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   .filter-menu { |   .filter-menu { | ||||||
|  |     .filter-row { | ||||||
|  |       display: flex; | ||||||
|  |       flex-direction: row; | ||||||
|  |       justify-content: start; | ||||||
|  |       align-items: baseline; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     h5 { |     h5 { | ||||||
|       font-size: 0.8rem; |       font-size: 0.8rem; | ||||||
|       color: white; |       color: white; | ||||||
| @ -118,6 +125,12 @@ | |||||||
|         background: #1a9436; |         background: #1a9436; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |     &.yellow { | ||||||
|  |       border: solid 1px #bf7815; | ||||||
|  |       &.active { | ||||||
|  |         background: #bf7815; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   :host-context(.block-overview-graph:hover) &, &:hover, &:active { |   :host-context(.block-overview-graph:hover) &, &:hover, &:active { | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| import { Component, EventEmitter, Output, HostListener, Input, ChangeDetectorRef, OnChanges, SimpleChanges, OnInit, OnDestroy } from '@angular/core'; | import { Component, EventEmitter, Output, HostListener, Input, ChangeDetectorRef, OnChanges, SimpleChanges, OnInit, OnDestroy } from '@angular/core'; | ||||||
| import { ActiveFilter, FilterGroups, FilterMode, TransactionFilters } from '../../shared/filters.utils'; | import { ActiveFilter, FilterGroups, FilterMode, GradientMode, TransactionFilters } from '../../shared/filters.utils'; | ||||||
| import { StateService } from '../../services/state.service'; | import { StateService } from '../../services/state.service'; | ||||||
| import { Subscription } from 'rxjs'; | import { Subscription } from 'rxjs'; | ||||||
| 
 | 
 | ||||||
| @ -22,6 +22,7 @@ export class BlockFiltersComponent implements OnInit, OnChanges, OnDestroy { | |||||||
|   activeFilters: string[] = []; |   activeFilters: string[] = []; | ||||||
|   filterFlags: { [key: string]: boolean } = {}; |   filterFlags: { [key: string]: boolean } = {}; | ||||||
|   filterMode: FilterMode = 'and'; |   filterMode: FilterMode = 'and'; | ||||||
|  |   gradientMode: GradientMode = 'fee'; | ||||||
|   menuOpen: boolean = false; |   menuOpen: boolean = false; | ||||||
| 
 | 
 | ||||||
|   constructor( |   constructor( | ||||||
| @ -32,6 +33,7 @@ export class BlockFiltersComponent implements OnInit, OnChanges, OnDestroy { | |||||||
|   ngOnInit(): void { |   ngOnInit(): void { | ||||||
|     this.filterSubscription = this.stateService.activeGoggles$.subscribe((active: ActiveFilter) => { |     this.filterSubscription = this.stateService.activeGoggles$.subscribe((active: ActiveFilter) => { | ||||||
|       this.filterMode = active.mode; |       this.filterMode = active.mode; | ||||||
|  |       this.gradientMode = active.gradient; | ||||||
|       for (const key of Object.keys(this.filterFlags)) { |       for (const key of Object.keys(this.filterFlags)) { | ||||||
|         this.filterFlags[key] = false; |         this.filterFlags[key] = false; | ||||||
|       } |       } | ||||||
| @ -39,7 +41,7 @@ export class BlockFiltersComponent implements OnInit, OnChanges, OnDestroy { | |||||||
|         this.filterFlags[key] = !this.disabledFilters[key]; |         this.filterFlags[key] = !this.disabledFilters[key]; | ||||||
|       } |       } | ||||||
|       this.activeFilters = [...active.filters.filter(key => !this.disabledFilters[key])]; |       this.activeFilters = [...active.filters.filter(key => !this.disabledFilters[key])]; | ||||||
|       this.onFilterChanged.emit({ mode: active.mode, filters: this.activeFilters }); |       this.onFilterChanged.emit({ mode: active.mode, filters: this.activeFilters, gradient: this.gradientMode }); | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -57,8 +59,14 @@ export class BlockFiltersComponent implements OnInit, OnChanges, OnDestroy { | |||||||
| 
 | 
 | ||||||
|   setFilterMode(mode): void { |   setFilterMode(mode): void { | ||||||
|     this.filterMode = mode; |     this.filterMode = mode; | ||||||
|     this.onFilterChanged.emit({ mode: this.filterMode, filters: this.activeFilters }); |     this.onFilterChanged.emit({ mode: this.filterMode, filters: this.activeFilters, gradient: this.gradientMode }); | ||||||
|     this.stateService.activeGoggles$.next({ mode: this.filterMode, filters: [...this.activeFilters] }); |     this.stateService.activeGoggles$.next({ mode: this.filterMode, filters: [...this.activeFilters], gradient: this.gradientMode }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   setGradientMode(mode): void { | ||||||
|  |     this.gradientMode = mode; | ||||||
|  |     this.onFilterChanged.emit({ mode: this.filterMode, filters: this.activeFilters, gradient: this.gradientMode }); | ||||||
|  |     this.stateService.activeGoggles$.next({ mode: this.filterMode, filters: [...this.activeFilters], gradient: this.gradientMode }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   toggleFilter(key): void { |   toggleFilter(key): void { | ||||||
| @ -81,8 +89,8 @@ export class BlockFiltersComponent implements OnInit, OnChanges, OnDestroy { | |||||||
|       this.activeFilters = this.activeFilters.filter(f => f != key); |       this.activeFilters = this.activeFilters.filter(f => f != key); | ||||||
|     } |     } | ||||||
|     const booleanFlags = this.getBooleanFlags(); |     const booleanFlags = this.getBooleanFlags(); | ||||||
|     this.onFilterChanged.emit({ mode: this.filterMode, filters: this.activeFilters }); |     this.onFilterChanged.emit({ mode: this.filterMode, filters: this.activeFilters, gradient: this.gradientMode }); | ||||||
|     this.stateService.activeGoggles$.next({ mode: this.filterMode, filters: [...this.activeFilters] }); |     this.stateService.activeGoggles$.next({ mode: this.filterMode, filters: [...this.activeFilters], gradient: this.gradientMode }); | ||||||
|   } |   } | ||||||
|    |    | ||||||
|   getBooleanFlags(): bigint | null { |   getBooleanFlags(): bigint | null { | ||||||
|  | |||||||
| @ -8,14 +8,11 @@ import { Color, Position } from './sprite-types'; | |||||||
| import { Price } from '../../services/price.service'; | import { Price } from '../../services/price.service'; | ||||||
| import { StateService } from '../../services/state.service'; | import { StateService } from '../../services/state.service'; | ||||||
| import { Subscription } from 'rxjs'; | import { Subscription } from 'rxjs'; | ||||||
| import { defaultColorFunction, setOpacity, defaultFeeColors, defaultAuditFeeColors, defaultMarginalFeeColors, defaultAuditColors } from './utils'; | import { defaultColorFunction, setOpacity, defaultAuditColors, defaultColors } from './utils'; | ||||||
| import { ActiveFilter, FilterMode, toFlags } from '../../shared/filters.utils'; | import { ActiveFilter, FilterMode, toFlags } from '../../shared/filters.utils'; | ||||||
| import { detectWebGL } from '../../shared/graphs.utils'; | import { detectWebGL } from '../../shared/graphs.utils'; | ||||||
| 
 | 
 | ||||||
| const unmatchedOpacity = 0.2; | const unmatchedOpacity = 0.2; | ||||||
| const unmatchedFeeColors = defaultFeeColors.map(c => setOpacity(c, unmatchedOpacity)); |  | ||||||
| const unmatchedAuditFeeColors = defaultAuditFeeColors.map(c => setOpacity(c, unmatchedOpacity)); |  | ||||||
| const unmatchedMarginalFeeColors = defaultMarginalFeeColors.map(c => setOpacity(c, unmatchedOpacity)); |  | ||||||
| const unmatchedAuditColors = { | const unmatchedAuditColors = { | ||||||
|   censored: setOpacity(defaultAuditColors.censored, unmatchedOpacity), |   censored: setOpacity(defaultAuditColors.censored, unmatchedOpacity), | ||||||
|   missing: setOpacity(defaultAuditColors.missing, unmatchedOpacity), |   missing: setOpacity(defaultAuditColors.missing, unmatchedOpacity), | ||||||
| @ -46,6 +43,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On | |||||||
|   @Input() excludeFilters: string[] = []; |   @Input() excludeFilters: string[] = []; | ||||||
|   @Input() filterFlags: bigint | null = null; |   @Input() filterFlags: bigint | null = null; | ||||||
|   @Input() filterMode: FilterMode = 'and'; |   @Input() filterMode: FilterMode = 'and'; | ||||||
|  |   @Input() gradientMode: 'fee' | 'age' = 'fee'; | ||||||
|   @Input() relativeTime: number | null; |   @Input() relativeTime: number | null; | ||||||
|   @Input() blockConversion: Price; |   @Input() blockConversion: Price; | ||||||
|   @Input() overrideColors: ((tx: TxView) => Color) | null = null; |   @Input() overrideColors: ((tx: TxView) => Color) | null = null; | ||||||
| @ -121,21 +119,22 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On | |||||||
|       this.setHighlightingEnabled(this.auditHighlighting); |       this.setHighlightingEnabled(this.auditHighlighting); | ||||||
|     } |     } | ||||||
|     if (changes.overrideColor && this.scene) { |     if (changes.overrideColor && this.scene) { | ||||||
|       this.scene.setColorFunction(this.overrideColors); |       this.scene.setColorFunction(this.getFilterColorFunction(0n, this.gradientMode)); | ||||||
|     } |     } | ||||||
|     if ((changes.filterFlags || changes.showFilters || changes.filterMode)) { |     if ((changes.filterFlags || changes.showFilters || changes.filterMode || changes.gradientMode)) { | ||||||
|       this.setFilterFlags(); |       this.setFilterFlags(); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   setFilterFlags(goggle?: ActiveFilter): void { |   setFilterFlags(goggle?: ActiveFilter): void { | ||||||
|     this.filterMode = goggle?.mode || this.filterMode; |     this.filterMode = goggle?.mode || this.filterMode; | ||||||
|  |     this.gradientMode = goggle?.gradient || this.gradientMode; | ||||||
|     this.activeFilterFlags = goggle?.filters ? toFlags(goggle.filters) : this.filterFlags; |     this.activeFilterFlags = goggle?.filters ? toFlags(goggle.filters) : this.filterFlags; | ||||||
|     if (this.scene) { |     if (this.scene) { | ||||||
|       if (this.activeFilterFlags != null && this.filtersAvailable) { |       if (this.activeFilterFlags != null && this.filtersAvailable) { | ||||||
|         this.scene.setColorFunction(this.getFilterColorFunction(this.activeFilterFlags)); |         this.scene.setColorFunction(this.getFilterColorFunction(this.activeFilterFlags, this.gradientMode)); | ||||||
|       } else { |       } else { | ||||||
|         this.scene.setColorFunction(this.overrideColors); |         this.scene.setColorFunction(this.getFilterColorFunction(0n, this.gradientMode)); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     this.start(); |     this.start(); | ||||||
| @ -212,6 +211,9 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On | |||||||
|       remove = remove.filter(txid => this.scene.txs[txid]); |       remove = remove.filter(txid => this.scene.txs[txid]); | ||||||
|       change = change.filter(tx => this.scene.txs[tx.txid]); |       change = change.filter(tx => this.scene.txs[tx.txid]); | ||||||
| 
 | 
 | ||||||
|  |       if (this.gradientMode === 'age') { | ||||||
|  |         this.scene.updateAllColors(); | ||||||
|  |       } | ||||||
|       this.scene.update(add, remove, change, direction, resetLayout); |       this.scene.update(add, remove, change, direction, resetLayout); | ||||||
|       this.start(); |       this.start(); | ||||||
|       this.updateSearchHighlight(); |       this.updateSearchHighlight(); | ||||||
| @ -548,25 +550,24 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On | |||||||
| 
 | 
 | ||||||
|   getColorFunction(): ((tx: TxView) => Color) { |   getColorFunction(): ((tx: TxView) => Color) { | ||||||
|     if (this.filterFlags) { |     if (this.filterFlags) { | ||||||
|       return this.getFilterColorFunction(this.filterFlags); |       return this.getFilterColorFunction(this.filterFlags, this.gradientMode); | ||||||
|     } else if (this.activeFilterFlags) { |     } else if (this.activeFilterFlags) { | ||||||
|       return this.getFilterColorFunction(this.activeFilterFlags); |       return this.getFilterColorFunction(this.activeFilterFlags, this.gradientMode); | ||||||
|     } else { |     } else { | ||||||
|       return this.overrideColors; |       return this.getFilterColorFunction(0n, this.gradientMode); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   getFilterColorFunction(flags: bigint): ((tx: TxView) => Color) { |   getFilterColorFunction(flags: bigint, gradient: 'fee' | 'age'): ((tx: TxView) => Color) { | ||||||
|     return (tx: TxView) => { |     return (tx: TxView) => { | ||||||
|       if ((this.filterMode === 'and' && (tx.bigintFlags & flags) === flags) || (this.filterMode === 'or' && (flags === 0n || (tx.bigintFlags & flags) > 0n))) { |       if ((this.filterMode === 'and' && (tx.bigintFlags & flags) === flags) || (this.filterMode === 'or' && (flags === 0n || (tx.bigintFlags & flags) > 0n))) { | ||||||
|         return defaultColorFunction(tx); |         return defaultColorFunction(tx, defaultColors[gradient], defaultAuditColors, this.relativeTime || (Date.now() / 1000)); | ||||||
|       } else { |       } else { | ||||||
|         return defaultColorFunction( |         return defaultColorFunction( | ||||||
|           tx, |           tx, | ||||||
|           unmatchedFeeColors, |           defaultColors['unmatched' + gradient], | ||||||
|           unmatchedAuditFeeColors, |           unmatchedAuditColors, | ||||||
|           unmatchedMarginalFeeColors, |           this.relativeTime || (Date.now() / 1000) | ||||||
|           unmatchedAuditColors |  | ||||||
|         ); |         ); | ||||||
|       } |       } | ||||||
|     }; |     }; | ||||||
|  | |||||||
| @ -68,6 +68,10 @@ export default class BlockScene { | |||||||
| 
 | 
 | ||||||
|   setColorFunction(colorFunction: ((tx: TxView) => Color) | null): void { |   setColorFunction(colorFunction: ((tx: TxView) => Color) | null): void { | ||||||
|     this.getColor = colorFunction || defaultColorFunction; |     this.getColor = colorFunction || defaultColorFunction; | ||||||
|  |     this.updateAllColors(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   updateAllColors(): void { | ||||||
|     this.dirty = true; |     this.dirty = true; | ||||||
|     if (this.initialised && this.scene) { |     if (this.initialised && this.scene) { | ||||||
|       this.updateColors(performance.now(), 50); |       this.updateColors(performance.now(), 50); | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import { feeLevels, mempoolFeeColors } from '../../app.constants'; | import { feeLevels, mempoolAgeColors, mempoolFeeColors } from '../../app.constants'; | ||||||
| import { Color } from './sprite-types'; | import { Color } from './sprite-types'; | ||||||
| import TxView from './tx-view'; | import TxView from './tx-view'; | ||||||
| 
 | 
 | ||||||
| @ -37,10 +37,42 @@ export function setOpacity(color: Color, opacity: number): Color { | |||||||
|   }; |   }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | interface ColorPalette { | ||||||
|  |   base: Color[], | ||||||
|  |   audit: Color[], | ||||||
|  |   marginal: Color[], | ||||||
|  |   baseLevel: (tx: TxView, rate: number, time: number) => number, | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // precomputed colors
 | // precomputed colors
 | ||||||
| export const defaultFeeColors = mempoolFeeColors.map(hexToColor); | const defaultColors: { [key: string]: ColorPalette } = { | ||||||
| export const defaultAuditFeeColors = defaultFeeColors.map((color) => darken(desaturate(color, 0.3), 0.9)); |   fee: { | ||||||
| export const defaultMarginalFeeColors = defaultFeeColors.map((color) => darken(desaturate(color, 0.8), 1.1)); |     base: mempoolFeeColors.map(hexToColor), | ||||||
|  |     audit: [], | ||||||
|  |     marginal: [], | ||||||
|  |     baseLevel: (tx: TxView, rate: number) => feeLevels.findIndex((feeLvl) => Math.max(1, rate) < feeLvl) - 1 | ||||||
|  |   }, | ||||||
|  |   age: { | ||||||
|  |     base: mempoolAgeColors.map(hexToColor), | ||||||
|  |     audit: [], | ||||||
|  |     marginal: [], | ||||||
|  |     baseLevel: (tx: TxView, rate: number, relativeTime: number) => (!tx.time ? 0 : Math.max(0, Math.round(1.25 * Math.log2((Math.max(1, relativeTime - tx.time)))))) | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | for (const key in defaultColors) { | ||||||
|  |   const base = defaultColors[key].base; | ||||||
|  |   defaultColors[key].audit = base.map((color) => darken(desaturate(color, 0.3), 0.9)); | ||||||
|  |   defaultColors[key].marginal = base.map((color) => darken(desaturate(color, 0.8), 1.1)); | ||||||
|  |   defaultColors['unmatched' + key] = { | ||||||
|  |     base: defaultColors[key].base.map(c => setOpacity(c, 0.2)), | ||||||
|  |     audit: defaultColors[key].audit.map(c => setOpacity(c, 0.2)), | ||||||
|  |     marginal: defaultColors[key].marginal.map(c => setOpacity(c, 0.2)), | ||||||
|  |     baseLevel: defaultColors[key].baseLevel, | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export { defaultColors as defaultColors }; | ||||||
|  | 
 | ||||||
| export const defaultAuditColors = { | export const defaultAuditColors = { | ||||||
|   censored: hexToColor('f344df'), |   censored: hexToColor('f344df'), | ||||||
|   missing: darken(desaturate(hexToColor('f344df'), 0.3), 0.7), |   missing: darken(desaturate(hexToColor('f344df'), 0.3), 0.7), | ||||||
| @ -51,22 +83,21 @@ export const defaultAuditColors = { | |||||||
| 
 | 
 | ||||||
| export function defaultColorFunction( | export function defaultColorFunction( | ||||||
|   tx: TxView, |   tx: TxView, | ||||||
|   feeColors: Color[] = defaultFeeColors, |   colors: { base: Color[], audit: Color[], marginal: Color[], baseLevel: (tx: TxView, rate: number, time: number) => number } = defaultColors.fee, | ||||||
|   auditFeeColors: Color[] = defaultAuditFeeColors, |   auditColors: { [status: string]: Color } = defaultAuditColors, | ||||||
|   marginalFeeColors: Color[] = defaultMarginalFeeColors, |   relativeTime?: number, | ||||||
|   auditColors: { [status: string]: Color } = defaultAuditColors |  | ||||||
| ): Color { | ): Color { | ||||||
|   const rate = tx.fee / tx.vsize; // color by simple single-tx fee rate
 |   const rate = tx.fee / tx.vsize; // color by simple single-tx fee rate
 | ||||||
|   const feeLevelIndex = feeLevels.findIndex((feeLvl) => Math.max(1, rate) < feeLvl) - 1; |   const levelIndex = colors.baseLevel(tx, rate, relativeTime || (Date.now() / 1000)); | ||||||
|   const feeLevelColor = feeColors[feeLevelIndex] || feeColors[mempoolFeeColors.length - 1]; |   const levelColor = colors.base[levelIndex] || colors.base[mempoolFeeColors.length - 1]; | ||||||
|   // Normal mode
 |   // Normal mode
 | ||||||
|   if (!tx.scene?.highlightingEnabled) { |   if (!tx.scene?.highlightingEnabled) { | ||||||
|     if (tx.acc) { |     if (tx.acc) { | ||||||
|       return auditColors.accelerated; |       return auditColors.accelerated; | ||||||
|     } else { |     } else { | ||||||
|       return feeLevelColor; |       return levelColor; | ||||||
|     } |     } | ||||||
|     return feeLevelColor; |     return levelColor; | ||||||
|   } |   } | ||||||
|   // Block audit
 |   // Block audit
 | ||||||
|   switch(tx.status) { |   switch(tx.status) { | ||||||
| @ -75,7 +106,7 @@ export function defaultColorFunction( | |||||||
|     case 'missing': |     case 'missing': | ||||||
|     case 'sigop': |     case 'sigop': | ||||||
|     case 'rbf': |     case 'rbf': | ||||||
|       return marginalFeeColors[feeLevelIndex] || marginalFeeColors[mempoolFeeColors.length - 1]; |       return colors.marginal[levelIndex] || colors.marginal[mempoolFeeColors.length - 1]; | ||||||
|     case 'fresh': |     case 'fresh': | ||||||
|     case 'freshcpfp': |     case 'freshcpfp': | ||||||
|       return auditColors.missing; |       return auditColors.missing; | ||||||
| @ -84,20 +115,20 @@ export function defaultColorFunction( | |||||||
|     case 'prioritized': |     case 'prioritized': | ||||||
|       return auditColors.prioritized; |       return auditColors.prioritized; | ||||||
|     case 'selected': |     case 'selected': | ||||||
|       return marginalFeeColors[feeLevelIndex] || marginalFeeColors[mempoolFeeColors.length - 1]; |       return colors.marginal[levelIndex] || colors.marginal[mempoolFeeColors.length - 1]; | ||||||
|     case 'accelerated': |     case 'accelerated': | ||||||
|       return auditColors.accelerated; |       return auditColors.accelerated; | ||||||
|     case 'found': |     case 'found': | ||||||
|       if (tx.context === 'projected') { |       if (tx.context === 'projected') { | ||||||
|         return auditFeeColors[feeLevelIndex] || auditFeeColors[mempoolFeeColors.length - 1]; |         return colors.audit[levelIndex] || colors.audit[mempoolFeeColors.length - 1]; | ||||||
|       } else { |       } else { | ||||||
|         return feeLevelColor; |         return levelColor; | ||||||
|       } |       } | ||||||
|     default: |     default: | ||||||
|       if (tx.acc) { |       if (tx.acc) { | ||||||
|         return auditColors.accelerated; |         return auditColors.accelerated; | ||||||
|       } else { |       } else { | ||||||
|         return feeLevelColor; |         return levelColor; | ||||||
|       } |       } | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @ -7,7 +7,7 @@ import { ApiService } from '../services/api.service'; | |||||||
| import { StateService } from '../services/state.service'; | import { StateService } from '../services/state.service'; | ||||||
| import { WebsocketService } from '../services/websocket.service'; | import { WebsocketService } from '../services/websocket.service'; | ||||||
| import { SeoService } from '../services/seo.service'; | import { SeoService } from '../services/seo.service'; | ||||||
| import { ActiveFilter, FilterMode, toFlags } from '../shared/filters.utils'; | import { ActiveFilter, FilterMode, GradientMode, toFlags } from '../shared/filters.utils'; | ||||||
| import { detectWebGL } from '../shared/graphs.utils'; | import { detectWebGL } from '../shared/graphs.utils'; | ||||||
| 
 | 
 | ||||||
| interface MempoolBlocksData { | interface MempoolBlocksData { | ||||||
| @ -74,14 +74,15 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit { | |||||||
|   private lastReservesBlockUpdate: number = 0; |   private lastReservesBlockUpdate: number = 0; | ||||||
| 
 | 
 | ||||||
|   goggleResolution = 82; |   goggleResolution = 82; | ||||||
|   goggleCycle: { index: number, name: string, mode: FilterMode, filters: string[] }[] = [ |   goggleCycle: { index: number, name: string, mode: FilterMode, filters: string[], gradient: GradientMode }[] = [ | ||||||
|     { index: 0, name: 'All', mode: 'and', filters: [] }, |     { index: 0, name: 'All', mode: 'and', filters: [], gradient: 'fee' }, | ||||||
|     { index: 1, name: 'Consolidation', mode: 'and', filters: ['consolidation'] }, |     { index: 1, name: 'Consolidation', mode: 'and', filters: ['consolidation'], gradient: 'fee' }, | ||||||
|     { index: 2, name: 'Coinjoin', mode: 'and', filters: ['coinjoin'] }, |     { index: 2, name: 'Coinjoin', mode: 'and', filters: ['coinjoin'], gradient: 'fee' }, | ||||||
|     { index: 3, name: 'Data', mode: 'or', filters: ['inscription', 'fake_pubkey', 'op_return'] }, |     { index: 3, name: 'Data', mode: 'or', filters: ['inscription', 'fake_pubkey', 'op_return'], gradient: 'fee' }, | ||||||
|   ]; |   ]; | ||||||
|   goggleFlags = 0n; |   goggleFlags = 0n; | ||||||
|   goggleMode: FilterMode = 'and'; |   goggleMode: FilterMode = 'and'; | ||||||
|  |   gradientMode: GradientMode = 'fee'; | ||||||
|   goggleIndex = 0; |   goggleIndex = 0; | ||||||
| 
 | 
 | ||||||
|   private destroy$ = new Subject(); |   private destroy$ = new Subject(); | ||||||
| @ -131,6 +132,7 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit { | |||||||
|             this.goggleIndex = goggle.index; |             this.goggleIndex = goggle.index; | ||||||
|             this.goggleFlags = toFlags(goggle.filters); |             this.goggleFlags = toFlags(goggle.filters); | ||||||
|             this.goggleMode = goggle.mode; |             this.goggleMode = goggle.mode; | ||||||
|  |             this.gradientMode = goggle.gradient; | ||||||
|             return; |             return; | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
| @ -140,6 +142,7 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit { | |||||||
|         name: 'Custom', |         name: 'Custom', | ||||||
|         mode: active.mode, |         mode: active.mode, | ||||||
|         filters: active.filters, |         filters: active.filters, | ||||||
|  |         gradient: active.gradient, | ||||||
|       }); |       }); | ||||||
|       this.goggleIndex = this.goggleCycle.length - 1; |       this.goggleIndex = this.goggleCycle.length - 1; | ||||||
|       this.goggleFlags = toFlags(active.filters); |       this.goggleFlags = toFlags(active.filters); | ||||||
|  | |||||||
| @ -154,7 +154,7 @@ export class StateService { | |||||||
|   searchFocus$: Subject<boolean> = new Subject<boolean>(); |   searchFocus$: Subject<boolean> = new Subject<boolean>(); | ||||||
|   menuOpen$: BehaviorSubject<boolean> = new BehaviorSubject(false); |   menuOpen$: BehaviorSubject<boolean> = new BehaviorSubject(false); | ||||||
| 
 | 
 | ||||||
|   activeGoggles$: BehaviorSubject<ActiveFilter> = new BehaviorSubject({ mode: 'and', filters: [] }); |   activeGoggles$: BehaviorSubject<ActiveFilter> = new BehaviorSubject({ mode: 'and', filters: [], gradient: 'fee' }); | ||||||
| 
 | 
 | ||||||
|   constructor( |   constructor( | ||||||
|     @Inject(PLATFORM_ID) private platformId: any, |     @Inject(PLATFORM_ID) private platformId: any, | ||||||
|  | |||||||
| @ -11,9 +11,12 @@ export interface Filter { | |||||||
| 
 | 
 | ||||||
| export type FilterMode = 'and' | 'or'; | export type FilterMode = 'and' | 'or'; | ||||||
| 
 | 
 | ||||||
|  | export type GradientMode = 'fee' | 'age'; | ||||||
|  | 
 | ||||||
| export interface ActiveFilter { | export interface ActiveFilter { | ||||||
|   mode: FilterMode, |   mode: FilterMode, | ||||||
|   filters: string[], |   filters: string[], | ||||||
|  |   gradient: GradientMode, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // binary flags for transaction classification
 | // binary flags for transaction classification
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user