Clean up block audit page & tweak color scheme
This commit is contained in:
		
							parent
							
								
									d86f045150
								
							
						
					
					
						commit
						b6343ddc2d
					
				| @ -238,6 +238,12 @@ class MiningRoutes { | ||||
|   public async $getBlockAudit(req: Request, res: Response) { | ||||
|     try { | ||||
|       const audit = await BlocksAuditsRepository.$getBlockAudit(req.params.hash); | ||||
| 
 | ||||
|       if (!audit) { | ||||
|         res.status(404).send(`This block has not been audited.`); | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       res.header('Pragma', 'public'); | ||||
|       res.header('Cache-control', 'public'); | ||||
|       res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24).toUTCString()); | ||||
|  | ||||
| @ -413,7 +413,7 @@ class WebsocketHandler { | ||||
| 
 | ||||
|     let mBlocks: undefined | MempoolBlock[]; | ||||
|     let mBlockDeltas: undefined | MempoolBlockDelta[]; | ||||
|     let matchRate = 0; | ||||
|     let matchRate; | ||||
|     const _memPool = memPool.getMempool(); | ||||
| 
 | ||||
|     if (Common.indexingEnabled()) { | ||||
|  | ||||
| @ -58,10 +58,12 @@ class BlocksAuditRepositories { | ||||
|         WHERE blocks_audits.hash = "${hash}" | ||||
|       `);
 | ||||
|        | ||||
|       rows[0].missingTxs = JSON.parse(rows[0].missingTxs); | ||||
|       rows[0].addedTxs = JSON.parse(rows[0].addedTxs); | ||||
|       rows[0].transactions = JSON.parse(rows[0].transactions); | ||||
|       rows[0].template = JSON.parse(rows[0].template); | ||||
|       if (rows.length) { | ||||
|         rows[0].missingTxs = JSON.parse(rows[0].missingTxs); | ||||
|         rows[0].addedTxs = JSON.parse(rows[0].addedTxs); | ||||
|         rows[0].transactions = JSON.parse(rows[0].transactions); | ||||
|         rows[0].template = JSON.parse(rows[0].template); | ||||
|       } | ||||
|              | ||||
|       return rows[0]; | ||||
|     } catch (e: any) { | ||||
|  | ||||
| @ -1,21 +1,22 @@ | ||||
| <div class="container-xl" (window:resize)="onResize($event)"> | ||||
| 
 | ||||
|   <div *ngIf="(auditObservable$ | async) as blockAudit; else skeleton"> | ||||
|     <div class="title-block" id="block"> | ||||
|       <h1> | ||||
|         <span class="next-previous-blocks"> | ||||
|           <span i18n="shared.block-title">Block </span> | ||||
|             | ||||
|           <a [routerLink]="['/block/' | relativeUrl, blockAudit.id]">{{ blockAudit.height }}</a> | ||||
|             | ||||
|           <span i18n="shared.template-vs-mined">Template vs Mined</span> | ||||
|         </span> | ||||
|       </h1> | ||||
|   <div class="title-block" id="block"> | ||||
|     <h1> | ||||
|       <span class="next-previous-blocks"> | ||||
|         <span i18n="shared.block-audit-title">Block Audit</span> | ||||
|           | ||||
|         <a *ngIf="blockAudit" [routerLink]="['/block/' | relativeUrl, blockHash]">{{ blockAudit.height }}</a> | ||||
|           | ||||
|       </span> | ||||
|     </h1> | ||||
| 
 | ||||
|       <div class="grow"></div> | ||||
|     <div class="grow"></div> | ||||
| 
 | ||||
|       <button [routerLink]="['/' | relativeUrl]" class="btn btn-sm">✕</button> | ||||
|     </div> | ||||
|     <button [routerLink]="['/block/' | relativeUrl, blockHash]" class="btn btn-sm">✕</button> | ||||
|   </div> | ||||
| 
 | ||||
|   <div *ngIf="!error && !isLoading"> | ||||
|      | ||||
| 
 | ||||
|     <!-- OVERVIEW --> | ||||
|     <div class="box mb-3"> | ||||
| @ -26,8 +27,8 @@ | ||||
|             <tbody> | ||||
|               <tr> | ||||
|                 <td class="td-width" i18n="block.hash">Hash</td> | ||||
|                 <td><a [routerLink]="['/block/' | relativeUrl, blockAudit.id]" title="{{ blockAudit.id }}">{{ blockAudit.id | shortenString : 13 }}</a> | ||||
|                   <app-clipboard class="d-none d-sm-inline-block" [text]="blockAudit.id"></app-clipboard> | ||||
|                 <td><a [routerLink]="['/block/' | relativeUrl, blockHash]" title="{{ blockHash }}">{{ blockHash | shortenString : 13 }}</a> | ||||
|                   <app-clipboard class="d-none d-sm-inline-block" [text]="blockHash"></app-clipboard> | ||||
|                 </td> | ||||
|               </tr> | ||||
|               <tr> | ||||
| @ -40,6 +41,10 @@ | ||||
|                   </div> | ||||
|                 </td> | ||||
|               </tr> | ||||
|               <tr> | ||||
|                 <td class="td-width" i18n="shared.transaction-count">Transactions</td> | ||||
|                 <td>{{ blockAudit.tx_count }}</td> | ||||
|               </tr> | ||||
|               <tr> | ||||
|                 <td i18n="blockAudit.size">Size</td> | ||||
|                 <td [innerHTML]="'‎' + (blockAudit.size | bytes: 2)"></td> | ||||
| @ -57,21 +62,25 @@ | ||||
|           <table class="table table-borderless table-striped"> | ||||
|             <tbody> | ||||
|               <tr> | ||||
|                 <td class="td-width" i18n="shared.transaction-count">Transactions</td> | ||||
|                 <td>{{ blockAudit.tx_count }}</td> | ||||
|               </tr> | ||||
|               <tr> | ||||
|                 <td i18n="block.match-rate">Match rate</td> | ||||
|                 <td i18n="block.health">Block health</td> | ||||
|                 <td>{{ blockAudit.matchRate }}%</td> | ||||
|               </tr> | ||||
|               <tr> | ||||
|                 <td i18n="block.missing-txs">Missing txs</td> | ||||
|                 <td i18n="block.missing-txs">Removed txs</td> | ||||
|                 <td>{{ blockAudit.missingTxs.length }}</td> | ||||
|               </tr> | ||||
|               <tr> | ||||
|                 <td i18n="block.missing-txs">Omitted txs</td> | ||||
|                 <td>{{ numMissing }}</td> | ||||
|               </tr> | ||||
|               <tr> | ||||
|                 <td i18n="block.added-txs">Added txs</td> | ||||
|                 <td>{{ blockAudit.addedTxs.length }}</td> | ||||
|               </tr> | ||||
|               <tr> | ||||
|                 <td i18n="block.missing-txs">Included txs</td> | ||||
|                 <td>{{ numUnexpected }}</td> | ||||
|               </tr> | ||||
|             </tbody> | ||||
|           </table> | ||||
|         </div> | ||||
| @ -79,33 +88,110 @@ | ||||
|     </div> <!-- box --> | ||||
| 
 | ||||
|     <!-- ADDED vs MISSING button --> | ||||
|     <div class="d-flex justify-content-center menu mt-3" *ngIf="isMobile"> | ||||
|       <a routerLinkActive="active" class="btn btn-primary w-50 mr-1 ml-1 menu-button" i18n="block.missing-txs" | ||||
|         fragment="missing" (click)="changeMode('missing')">Missing</a> | ||||
|       <a routerLinkActive="active" class="btn btn-primary w-50 mr-1 ml-1 menu-button" i18n="block.added-txs" | ||||
|         fragment="added" (click)="changeMode('added')">Added</a> | ||||
|     <div class="d-flex justify-content-center menu mt-3 mb-3" *ngIf="isMobile"> | ||||
|       <a class="btn btn-primary w-50 mr-1 ml-1 menu-button" [class.active]="mode === 'projected'" i18n="block.projected" | ||||
|         fragment="projected" (click)="changeMode('projected')">Projected</a> | ||||
|       <a class="btn btn-primary w-50 mr-1 ml-1 menu-button" [class.active]="mode === 'actual'" i18n="block.actual" | ||||
|         fragment="actual" (click)="changeMode('actual')">Actual</a> | ||||
|     </div> | ||||
|   </div> | ||||
| 
 | ||||
|   <ng-template [ngIf]="!error && isLoading"> | ||||
|     <div class="title-block" id="block"> | ||||
|       <h1> | ||||
|         <span class="next-previous-blocks"> | ||||
|           <span i18n="shared.block-audit-title">Block Audit</span> | ||||
|             | ||||
|           <a *ngIf="blockAudit" [routerLink]="['/block/' | relativeUrl, blockHash]">{{ blockAudit.height }}</a> | ||||
|             | ||||
|         </span> | ||||
|       </h1> | ||||
| 
 | ||||
|       <div class="grow"></div> | ||||
| 
 | ||||
|       <button [routerLink]="['/' | relativeUrl]" class="btn btn-sm">✕</button> | ||||
|     </div> | ||||
| 
 | ||||
|     <!-- OVERVIEW --> | ||||
|     <div class="box mb-3"> | ||||
|       <div class="row"> | ||||
|         <!-- LEFT COLUMN --> | ||||
|         <div class="col-sm"> | ||||
|           <table class="table table-borderless table-striped"> | ||||
|             <tbody> | ||||
|               <tr><td class="td-width" colspan="2"><span class="skeleton-loader"></span></td></tr> | ||||
|               <tr><td class="td-width" colspan="2"><span class="skeleton-loader"></span></td></tr> | ||||
|               <tr><td class="td-width" colspan="2"><span class="skeleton-loader"></span></td></tr> | ||||
|               <tr><td class="td-width" colspan="2"><span class="skeleton-loader"></span></td></tr> | ||||
|               <tr><td class="td-width" colspan="2"><span class="skeleton-loader"></span></td></tr> | ||||
|             </tbody> | ||||
|           </table> | ||||
|         </div> | ||||
| 
 | ||||
|         <!-- RIGHT COLUMN --> | ||||
|         <div class="col-sm"> | ||||
|           <table class="table table-borderless table-striped"> | ||||
|             <tbody> | ||||
|               <tr><td class="td-width" colspan="2"><span class="skeleton-loader"></span></td></tr> | ||||
|               <tr><td class="td-width" colspan="2"><span class="skeleton-loader"></span></td></tr> | ||||
|               <tr><td class="td-width" colspan="2"><span class="skeleton-loader"></span></td></tr> | ||||
|               <tr><td class="td-width" colspan="2"><span class="skeleton-loader"></span></td></tr> | ||||
|               <tr><td class="td-width" colspan="2"><span class="skeleton-loader"></span></td></tr> | ||||
|             </tbody> | ||||
|           </table> | ||||
|         </div> | ||||
|       </div> <!-- row --> | ||||
|     </div> <!-- box --> | ||||
| 
 | ||||
|     <!-- ADDED vs MISSING button --> | ||||
|     <div class="d-flex justify-content-center menu mt-3 mb-3" *ngIf="isMobile"> | ||||
|       <a class="btn btn-primary w-50 mr-1 ml-1 menu-button" [class.active]="mode === 'projected'" i18n="block.projected" | ||||
|         fragment="projected" (click)="changeMode('projected')">Projected</a> | ||||
|       <a class="btn btn-primary w-50 mr-1 ml-1 menu-button" [class.active]="mode === 'actual'" i18n="block.actual" | ||||
|         fragment="actual" (click)="changeMode('actual')">Actual</a> | ||||
|     </div> | ||||
|   </ng-template> | ||||
| 
 | ||||
|   <ng-template [ngIf]="error"> | ||||
|     <div *ngIf="error && error.status === 404; else generalError" class="text-center"> | ||||
|       <br> | ||||
|       <b i18n="error.audit-unavailable">audit unavailable</b> | ||||
|       <br><br> | ||||
|       <i>{{ error.error }}</i> | ||||
|       <br> | ||||
|       <br> | ||||
|     </div> | ||||
|     <ng-template #generalError> | ||||
|       <div class="text-center"> | ||||
|         <br> | ||||
|         <span i18n="error.general-loading-data">Error loading data.</span> | ||||
|         <br><br> | ||||
|         <i>{{ error }}</i> | ||||
|         <br> | ||||
|         <br> | ||||
|       </div> | ||||
|     </ng-template> | ||||
|   </ng-template> | ||||
| 
 | ||||
|   <!-- VISUALIZATIONS --> | ||||
|   <div class="box"> | ||||
|   <div class="box" *ngIf="!error"> | ||||
|     <div class="row"> | ||||
|       <!-- MISSING TX RENDERING --> | ||||
|       <div class="col-sm" *ngIf="webGlEnabled"> | ||||
|         <app-block-overview-graph #blockGraphTemplate [isLoading]="isLoading" [resolution]="75" | ||||
|         <h3 class="block-subtitle" *ngIf="!isMobile" i18n="block.projected-block">Projected Block</h3> | ||||
|         <app-block-overview-graph #blockGraphProjected [isLoading]="isLoading" [resolution]="75" | ||||
|           [blockLimit]="stateService.blockVSize" [orientation]="'top'" [flip]="false" | ||||
|           (txClickEvent)="onTxClick($event)"></app-block-overview-graph> | ||||
|       </div> | ||||
| 
 | ||||
|       <!-- ADDED TX RENDERING --> | ||||
|       <div class="col-sm" *ngIf="webGlEnabled && !isMobile"> | ||||
|         <app-block-overview-graph #blockGraphMined [isLoading]="isLoading" [resolution]="75" | ||||
|         <h3 class="block-subtitle" *ngIf="!isMobile" i18n="block.actual-block">Actual Block</h3> | ||||
|         <app-block-overview-graph #blockGraphActual [isLoading]="isLoading" [resolution]="75" | ||||
|           [blockLimit]="stateService.blockVSize" [orientation]="'top'" [flip]="false" | ||||
|           (txClickEvent)="onTxClick($event)"></app-block-overview-graph> | ||||
|       </div> | ||||
|     </div> <!-- row --> | ||||
|   </div> <!-- box --> | ||||
| 
 | ||||
|   <ng-template #skeleton></ng-template> | ||||
| 
 | ||||
| </div> | ||||
| @ -37,4 +37,8 @@ | ||||
|   @media (min-width: 768px) { | ||||
|     max-width: 150px; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .block-subtitle { | ||||
|   text-align: center; | ||||
| } | ||||
| @ -1,7 +1,7 @@ | ||||
| import { Component, OnDestroy, OnInit, AfterViewInit, ViewChildren, QueryList } from '@angular/core'; | ||||
| import { ActivatedRoute, ParamMap, Router } from '@angular/router'; | ||||
| import { Observable, Subscription, combineLatest } from 'rxjs'; | ||||
| import { map, share, switchMap, tap, startWith } from 'rxjs/operators'; | ||||
| import { map, switchMap, startWith, catchError } from 'rxjs/operators'; | ||||
| import { BlockAudit, TransactionStripped } from 'src/app/interfaces/node-api.interface'; | ||||
| import { ApiService } from 'src/app/services/api.service'; | ||||
| import { StateService } from 'src/app/services/state.service'; | ||||
| @ -25,21 +25,27 @@ import { BlockOverviewGraphComponent } from '../block-overview-graph/block-overv | ||||
| export class BlockAuditComponent implements OnInit, AfterViewInit, OnDestroy { | ||||
|   blockAudit: BlockAudit = undefined; | ||||
|   transactions: string[]; | ||||
|   auditObservable$: Observable<BlockAudit>; | ||||
|   auditSubscription: Subscription; | ||||
|   urlFragmentSubscription: Subscription; | ||||
| 
 | ||||
|   paginationMaxSize: number; | ||||
|   page = 1; | ||||
|   itemsPerPage: number; | ||||
| 
 | ||||
|   mode: 'missing' | 'added' = 'missing'; | ||||
|   mode: 'projected' | 'actual' = 'projected'; | ||||
|   error: any; | ||||
|   isLoading = true; | ||||
|   webGlEnabled = true; | ||||
|   isMobile = window.innerWidth <= 767.98; | ||||
| 
 | ||||
|   childChangeSubscription: Subscription; | ||||
| 
 | ||||
|   @ViewChildren('blockGraphTemplate') blockGraphTemplate: QueryList<BlockOverviewGraphComponent>; | ||||
|   @ViewChildren('blockGraphMined') blockGraphMined: QueryList<BlockOverviewGraphComponent>; | ||||
|   blockHash: string; | ||||
|   numMissing: number = 0; | ||||
|   numUnexpected: number = 0; | ||||
| 
 | ||||
|   @ViewChildren('blockGraphProjected') blockGraphProjected: QueryList<BlockOverviewGraphComponent>; | ||||
|   @ViewChildren('blockGraphActual') blockGraphActual: QueryList<BlockOverviewGraphComponent>; | ||||
| 
 | ||||
|   constructor( | ||||
|     private route: ActivatedRoute, | ||||
| @ -50,18 +56,31 @@ export class BlockAuditComponent implements OnInit, AfterViewInit, OnDestroy { | ||||
|     this.webGlEnabled = detectWebGL(); | ||||
|   } | ||||
| 
 | ||||
|   ngOnDestroy(): void { | ||||
|   ngOnDestroy() { | ||||
|     this.childChangeSubscription.unsubscribe(); | ||||
|     this.urlFragmentSubscription.unsubscribe(); | ||||
|   } | ||||
| 
 | ||||
|   ngOnInit(): void { | ||||
|     this.paginationMaxSize = window.matchMedia('(max-width: 670px)').matches ? 3 : 5; | ||||
|     this.itemsPerPage = this.stateService.env.ITEMS_PER_PAGE; | ||||
| 
 | ||||
|     this.auditObservable$ = this.route.paramMap.pipe( | ||||
|     this.urlFragmentSubscription = this.route.fragment.subscribe((fragment) => { | ||||
|       if (fragment === 'actual') { | ||||
|         this.mode = 'actual'; | ||||
|       } else { | ||||
|         this.mode = 'projected' | ||||
|       } | ||||
|       this.setupBlockGraphs(); | ||||
|     }); | ||||
| 
 | ||||
|     this.auditSubscription = this.route.paramMap.pipe( | ||||
|       switchMap((params: ParamMap) => { | ||||
|         const blockHash: string = params.get('id') || ''; | ||||
|         return this.apiService.getBlockAudit$(blockHash) | ||||
|         this.blockHash = params.get('id') || null; | ||||
|         if (!this.blockHash) { | ||||
|           return null; | ||||
|         } | ||||
|         return this.apiService.getBlockAudit$(this.blockHash) | ||||
|           .pipe( | ||||
|             map((response) => { | ||||
|               const blockAudit = response.body; | ||||
| @ -71,6 +90,8 @@ export class BlockAuditComponent implements OnInit, AfterViewInit, OnDestroy { | ||||
|               const isCensored = {}; | ||||
|               const isMissing = {}; | ||||
|               const isSelected = {}; | ||||
|               this.numMissing = 0; | ||||
|               this.numUnexpected = 0; | ||||
|               for (const tx of blockAudit.template) { | ||||
|                 inTemplate[tx.txid] = true; | ||||
|               } | ||||
| @ -92,6 +113,7 @@ export class BlockAuditComponent implements OnInit, AfterViewInit, OnDestroy { | ||||
|                 } else { | ||||
|                   tx.status = 'missing'; | ||||
|                   isMissing[tx.txid] = true; | ||||
|                   this.numMissing++; | ||||
|                 } | ||||
|               } | ||||
|               for (const [index, tx] of blockAudit.transactions.entries()) { | ||||
| @ -102,43 +124,46 @@ export class BlockAuditComponent implements OnInit, AfterViewInit, OnDestroy { | ||||
|                 } else { | ||||
|                   tx.status = 'selected'; | ||||
|                   isSelected[tx.txid] = true; | ||||
|                   this.numUnexpected++; | ||||
|                 } | ||||
|               } | ||||
|               for (const tx of blockAudit.transactions) { | ||||
|                 inBlock[tx.txid] = true; | ||||
|               } | ||||
|               return blockAudit; | ||||
|             }), | ||||
|             tap((blockAudit) => { | ||||
|               this.blockAudit = blockAudit; | ||||
|               this.changeMode(this.mode); | ||||
|               this.isLoading = false; | ||||
|             }), | ||||
|             }) | ||||
|           ); | ||||
|       }), | ||||
|       share() | ||||
|     ); | ||||
|       catchError((err) => { | ||||
|         console.log(err); | ||||
|         this.error = err; | ||||
|         this.isLoading = false; | ||||
|         return null; | ||||
|       }), | ||||
|     ).subscribe((blockAudit) => { | ||||
|       this.blockAudit = blockAudit; | ||||
|       this.setupBlockGraphs(); | ||||
|       this.isLoading = false; | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   ngAfterViewInit() { | ||||
|     this.childChangeSubscription = combineLatest([this.blockGraphTemplate.changes.pipe(startWith(null)), this.blockGraphMined.changes.pipe(startWith(null))]).subscribe(() => { | ||||
|       console.log('changed!'); | ||||
|     this.childChangeSubscription = combineLatest([this.blockGraphProjected.changes.pipe(startWith(null)), this.blockGraphActual.changes.pipe(startWith(null))]).subscribe(() => { | ||||
|       this.setupBlockGraphs(); | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   setupBlockGraphs() { | ||||
|     console.log('setting up block graphs') | ||||
|     if (this.blockAudit) { | ||||
|       this.blockGraphTemplate.forEach(graph => { | ||||
|       this.blockGraphProjected.forEach(graph => { | ||||
|         graph.destroy(); | ||||
|         if (this.isMobile && this.mode === 'added') { | ||||
|         if (this.isMobile && this.mode === 'actual') { | ||||
|           graph.setup(this.blockAudit.transactions); | ||||
|         } else { | ||||
|           graph.setup(this.blockAudit.template); | ||||
|         } | ||||
|       }) | ||||
|       this.blockGraphMined.forEach(graph => { | ||||
|       this.blockGraphActual.forEach(graph => { | ||||
|         graph.destroy(); | ||||
|         graph.setup(this.blockAudit.transactions); | ||||
|       }) | ||||
| @ -156,18 +181,12 @@ export class BlockAuditComponent implements OnInit, AfterViewInit, OnDestroy { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   changeMode(mode: 'missing' | 'added') { | ||||
|   changeMode(mode: 'projected' | 'actual') { | ||||
|     this.router.navigate([], { fragment: mode }); | ||||
|     this.mode = mode; | ||||
| 
 | ||||
|     this.setupBlockGraphs(); | ||||
|   } | ||||
| 
 | ||||
|   onTxClick(event: TransactionStripped): void { | ||||
|     const url = new RelativeUrlPipe(this.stateService).transform(`/tx/${event.txid}`); | ||||
|     this.router.navigate([url]); | ||||
|   } | ||||
| 
 | ||||
|   pageChange(page: number, target: HTMLElement) { | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -7,6 +7,15 @@ import { feeLevels, mempoolFeeColors } from '../../app.constants'; | ||||
| const hoverTransitionTime = 300; | ||||
| const defaultHoverColor = hexToColor('1bd8f4'); | ||||
| 
 | ||||
| const feeColors = mempoolFeeColors.map(hexToColor); | ||||
| const auditFeeColors = feeColors.map((color) => desaturate(color, 0.3)); | ||||
| const auditColors = { | ||||
|   censored: hexToColor('f344df'), | ||||
|   missing: darken(desaturate(hexToColor('f344df'), 0.3), 0.7), | ||||
|   added: hexToColor('03E1E5'), | ||||
|   selected: darken(desaturate(hexToColor('039BE5'), 0.3), 0.7), | ||||
| } | ||||
| 
 | ||||
| // convert from this class's update format to TxSprite's update format
 | ||||
| function toSpriteUpdate(params: ViewUpdateParams): SpriteUpdateParams { | ||||
|   return { | ||||
| @ -143,17 +152,19 @@ export default class TxView implements TransactionStripped { | ||||
| 
 | ||||
|   getColor(): Color { | ||||
|     const feeLevelIndex = feeLevels.findIndex((feeLvl) => Math.max(1, this.feerate) < feeLvl) - 1; | ||||
|     const feeLevelColor = hexToColor(mempoolFeeColors[feeLevelIndex] || mempoolFeeColors[mempoolFeeColors.length - 1]); | ||||
|     const feeLevelColor = feeColors[feeLevelIndex] || feeColors[mempoolFeeColors.length - 1]; | ||||
|     // Block audit
 | ||||
|     switch(this.status) { | ||||
|       case 'censored': | ||||
|         return hexToColor('D81BC2'); | ||||
|         return auditColors.censored; | ||||
|       case 'missing': | ||||
|         return hexToColor('8C1BD8'); | ||||
|         return auditColors.missing; | ||||
|       case 'added': | ||||
|         return hexToColor('03E1E5'); | ||||
|         return auditColors.added; | ||||
|       case 'selected': | ||||
|         return hexToColor('039BE5'); | ||||
|         return auditColors.selected; | ||||
|       case 'found': | ||||
|         return auditFeeColors[feeLevelIndex] || auditFeeColors[mempoolFeeColors.length - 1]; | ||||
|       default: | ||||
|         return feeLevelColor; | ||||
|     } | ||||
| @ -168,3 +179,22 @@ function hexToColor(hex: string): Color { | ||||
|     a: 1 | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| function desaturate(color: Color, amount: number): Color { | ||||
|   const gray = (color.r + color.g + color.b) / 6; | ||||
|   return { | ||||
|     r: color.r + ((gray - color.r) * amount), | ||||
|     g: color.g + ((gray - color.g) * amount), | ||||
|     b: color.b + ((gray - color.b) * amount), | ||||
|     a: color.a, | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| function darken(color: Color, amount: number): Color { | ||||
|   return { | ||||
|     r: color.r * amount, | ||||
|     g: color.g * amount, | ||||
|     b: color.b * amount, | ||||
|     a: color.a, | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -36,10 +36,10 @@ | ||||
|         <td class="td-width" i18n="transaction.audit-status">Audit status</td> | ||||
|         <ng-container [ngSwitch]="tx?.status"> | ||||
|           <td *ngSwitchCase="'found'" i18n="transaction.audit.match">match</td> | ||||
|           <td *ngSwitchCase="'censored'" i18n="transaction.audit.censored">censored</td> | ||||
|           <td *ngSwitchCase="'censored'" i18n="transaction.audit.removed">removed</td> | ||||
|           <td *ngSwitchCase="'missing'" i18n="transaction.audit.missing">missing</td> | ||||
|           <td *ngSwitchCase="'added'" i18n="transaction.audit.prioritized">prioritized</td> | ||||
|           <td *ngSwitchCase="'selected'" i18n="transaction.audit.unexpected">unexpected</td> | ||||
|           <td *ngSwitchCase="'added'" i18n="transaction.audit.added">added</td> | ||||
|           <td *ngSwitchCase="'selected'" i18n="transaction.audit.included">included</td> | ||||
|         </ng-container> | ||||
|       </tr> | ||||
|     </tbody> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user