implement theme switching service

This commit is contained in:
Mononaut
2023-01-02 12:26:10 -06:00
committed by natsoni
parent ee92f6639a
commit 4c205eb09d
8 changed files with 279 additions and 20 deletions

View File

@@ -7,6 +7,7 @@ import TxView from './tx-view';
import { Color, Position } from './sprite-types';
import { Price } from '../../services/price.service';
import { StateService } from '../../services/state.service';
import { ThemeService } from 'src/app/services/theme.service';
import { Subscription } from 'rxjs';
import { defaultColorFunction, setOpacity, defaultFeeColors, defaultAuditFeeColors, defaultMarginalFeeColors, defaultAuditColors } from './utils';
import { ActiveFilter, FilterMode, toFlags } from '../../shared/filters.utils';
@@ -55,6 +56,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
@ViewChild('blockCanvas')
canvas: ElementRef<HTMLCanvasElement>;
themeChangedSubscription: Subscription;
gl: WebGLRenderingContext;
animationFrameRequest: number;
@@ -86,6 +88,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
readonly ngZone: NgZone,
readonly elRef: ElementRef,
public stateService: StateService,
private themeService: ThemeService,
) {
this.webGlEnabled = this.stateService.isBrowser && detectWebGL();
this.vertexArray = new FastVertexArray(512, TxSprite.dataSize);
@@ -104,6 +107,10 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
if (this.gl) {
this.initCanvas();
this.resizeCanvas();
this.themeChangedSubscription = this.themeService.themeChanged$.subscribe(() => {
// force full re-render
this.resizeCanvas();
});
}
}
}
@@ -149,6 +156,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
if (this.canvas) {
this.canvas.nativeElement.removeEventListener('webglcontextlost', this.handleContextLost);
this.canvas.nativeElement.removeEventListener('webglcontextrestored', this.handleContextRestored);
this.themeChangedSubscription?.unsubscribe();
}
}
@@ -291,7 +299,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
this.start();
} else {
this.scene = new BlockScene({ width: this.displayWidth, height: this.displayHeight, resolution: this.resolution,
blockLimit: this.blockLimit, orientation: this.orientation, flip: this.flip, vertexArray: this.vertexArray,
blockLimit: this.blockLimit, orientation: this.orientation, flip: this.flip, vertexArray: this.vertexArray, theme: this.themeService,
highlighting: this.auditHighlighting, animationDuration: this.animationDuration, animationOffset: this.animationOffset,
colorFunction: this.getColorFunction() });
this.start();

View File

@@ -3,12 +3,14 @@ import TxView from './tx-view';
import { TransactionStripped } from '../../interfaces/node-api.interface';
import { Color, Position, Square, ViewUpdateParams } from './sprite-types';
import { defaultColorFunction } from './utils';
import { ThemeService } from 'src/app/services/theme.service';
export default class BlockScene {
scene: { count: number, offset: { x: number, y: number}};
vertexArray: FastVertexArray;
txs: { [key: string]: TxView };
getColor: ((tx: TxView) => Color) = defaultColorFunction;
theme: ThemeService;
orientation: string;
flip: boolean;
animationDuration: number = 900;
@@ -29,11 +31,11 @@ export default class BlockScene {
animateUntil = 0;
dirty: boolean;
constructor({ width, height, resolution, blockLimit, animationDuration, animationOffset, orientation, flip, vertexArray, highlighting, colorFunction }:
constructor({ width, height, resolution, blockLimit, animationDuration, animationOffset, orientation, flip, vertexArray, theme, highlighting, colorFunction }:
{ width: number, height: number, resolution: number, blockLimit: number, animationDuration: number, animationOffset: number,
orientation: string, flip: boolean, vertexArray: FastVertexArray, highlighting: boolean, colorFunction: ((tx: TxView) => Color) | null }
orientation: string, flip: boolean, vertexArray: FastVertexArray, theme: ThemeService, highlighting: boolean, colorFunction: ((tx: TxView) => Color) | null }
) {
this.init({ width, height, resolution, blockLimit, animationDuration, animationOffset, orientation, flip, vertexArray, highlighting, colorFunction });
this.init({ width, height, resolution, blockLimit, animationDuration, animationOffset, orientation, flip, vertexArray, theme, highlighting, colorFunction });
}
resize({ width = this.width, height = this.height, animate = true }: { width?: number, height?: number, animate: boolean }): void {
@@ -90,7 +92,7 @@ export default class BlockScene {
});
this.layout = new BlockLayout({ width: this.gridWidth, height: this.gridHeight });
txs.forEach(tx => {
const txView = new TxView(tx, this);
const txView = new TxView(tx, this, this.theme);
this.txs[tx.txid] = txView;
this.place(txView);
this.saveGridToScreenPosition(txView);
@@ -136,7 +138,7 @@ export default class BlockScene {
});
txs.forEach(tx => {
if (!this.txs[tx.txid]) {
this.txs[tx.txid] = new TxView(tx, this);
this.txs[tx.txid] = new TxView(tx, this, this.theme);
}
});
@@ -178,7 +180,7 @@ export default class BlockScene {
if (resetLayout) {
add.forEach(tx => {
if (!this.txs[tx.txid]) {
this.txs[tx.txid] = new TxView(tx, this);
this.txs[tx.txid] = new TxView(tx, this, this.theme);
}
});
this.layout = new BlockLayout({ width: this.gridWidth, height: this.gridHeight });
@@ -198,7 +200,7 @@ export default class BlockScene {
// try to insert new txs directly
const remaining = [];
add.map(tx => new TxView(tx, this)).sort(feeRateDescending).forEach(tx => {
add.map(tx => new TxView(tx, this, this.theme)).sort(feeRateDescending).forEach(tx => {
if (!this.tryInsertByFee(tx)) {
remaining.push(tx);
}
@@ -228,9 +230,9 @@ export default class BlockScene {
this.animateUntil = Math.max(this.animateUntil, tx.setHighlight(value));
}
private init({ width, height, resolution, blockLimit, animationDuration, animationOffset, orientation, flip, vertexArray, highlighting, colorFunction }:
private init({ width, height, resolution, blockLimit, animationDuration, animationOffset, orientation, flip, vertexArray, theme, highlighting, colorFunction }:
{ width: number, height: number, resolution: number, blockLimit: number, animationDuration: number, animationOffset: number,
orientation: string, flip: boolean, vertexArray: FastVertexArray, highlighting: boolean, colorFunction: ((tx: TxView) => Color) | null }
orientation: string, flip: boolean, vertexArray: FastVertexArray, theme: ThemeService, highlighting: boolean, colorFunction: ((tx: TxView) => Color) | null }
): void {
this.animationDuration = animationDuration || 1000;
this.configAnimationOffset = animationOffset;
@@ -240,6 +242,7 @@ export default class BlockScene {
this.vertexArray = vertexArray;
this.highlightingEnabled = highlighting;
this.getColor = colorFunction || defaultColorFunction;
this.theme = theme;
this.scene = {
count: 0,

View File

@@ -5,6 +5,8 @@ import { hexToColor } from './utils';
import BlockScene from './block-scene';
import { TransactionStripped } from '../../interfaces/node-api.interface';
import { TransactionFlags } from '../../shared/filters.utils';
import { feeLevels } from '../../app.constants';
import { ThemeService } from 'src/app/services/theme.service';
const hoverTransitionTime = 300;
const defaultHoverColor = hexToColor('1bd8f4');
@@ -36,6 +38,7 @@ export default class TxView implements TransactionStripped {
status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'prioritized' | 'censored' | 'selected' | 'rbf' | 'accelerated';
context?: 'projected' | 'actual';
scene?: BlockScene;
theme: ThemeService;
initialised: boolean;
vertexArray: FastVertexArray;
@@ -50,7 +53,7 @@ export default class TxView implements TransactionStripped {
dirty: boolean;
constructor(tx: TransactionStripped, scene: BlockScene) {
constructor(tx: TransactionStripped, scene: BlockScene, theme: ThemeService) {
this.scene = scene;
this.context = tx.context;
this.txid = tx.txid;
@@ -66,6 +69,7 @@ export default class TxView implements TransactionStripped {
this.bigintFlags = tx.flags ? (BigInt(tx.flags) | (this.acc ? TransactionFlags.acceleration : 0n)): 0n;
this.initialised = false;
this.vertexArray = scene.vertexArray;
this.theme = theme;
this.hover = false;
@@ -138,10 +142,10 @@ export default class TxView implements TransactionStripped {
// Temporarily override the tx color
// returns minimum transition end time
setHover(hoverOn: boolean, color: Color | void = defaultHoverColor): number {
setHover(hoverOn: boolean, color: Color | void): number {
if (hoverOn) {
this.hover = true;
this.hoverColor = color;
this.hoverColor = color || this.theme.defaultHoverColor;
this.sprite.update({
...this.hoverColor,
@@ -191,4 +195,30 @@ export default class TxView implements TransactionStripped {
this.dirty = false;
return performance.now() + hoverTransitionTime;
}
getColor(): Color {
const feeLevelIndex = feeLevels.findIndex((feeLvl) => Math.max(1, this.feerate) < feeLvl) - 1;
const feeLevelColor = this.theme.feeColors[feeLevelIndex] || this.theme.feeColors[this.theme.mempoolFeeColors.length - 1];
// Block audit
switch(this.status) {
case 'censored':
return this.theme.auditColors.censored;
case 'missing':
return this.theme.auditColors.missing;
case 'fresh':
return this.theme.auditColors.missing;
case 'added':
return this.theme.auditColors.added;
case 'selected':
return this.theme.auditColors.selected;
case 'found':
if (this.context === 'projected') {
return this.theme.auditFeeColors[feeLevelIndex] || this.theme.auditFeeColors[this.theme.mempoolFeeColors.length - 1];
} else {
return feeLevelColor;
}
default:
return feeLevelColor;
}
}
}

View File

@@ -2,8 +2,9 @@ import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
import { StateService } from '../../services/state.service';
import { Observable, combineLatest } from 'rxjs';
import { Recommendedfees } from '../../interfaces/websocket.interface';
import { feeLevels, mempoolFeeColors } from '../../app.constants';
import { feeLevels } from '../../app.constants';
import { map, startWith, tap } from 'rxjs/operators';
import { ThemeService } from 'src/app/services/theme.service';
@Component({
selector: 'app-fees-box',
@@ -18,7 +19,8 @@ export class FeesBoxComponent implements OnInit {
noPriority = '#2e324e';
constructor(
private stateService: StateService
private stateService: StateService,
private themeService: ThemeService,
) { }
ngOnInit(): void {
@@ -33,11 +35,11 @@ export class FeesBoxComponent implements OnInit {
tap((fees) => {
let feeLevelIndex = feeLevels.slice().reverse().findIndex((feeLvl) => fees.minimumFee >= feeLvl);
feeLevelIndex = feeLevelIndex >= 0 ? feeLevels.length - feeLevelIndex : feeLevelIndex;
const startColor = '#' + (mempoolFeeColors[feeLevelIndex - 1] || mempoolFeeColors[mempoolFeeColors.length - 1]);
const startColor = '#' + (this.themeService.mempoolFeeColors[feeLevelIndex - 1] || this.themeService.mempoolFeeColors[this.themeService.mempoolFeeColors.length - 1]);
feeLevelIndex = feeLevels.slice().reverse().findIndex((feeLvl) => fees.fastestFee >= feeLvl);
feeLevelIndex = feeLevelIndex >= 0 ? feeLevels.length - feeLevelIndex : feeLevelIndex;
const endColor = '#' + (mempoolFeeColors[feeLevelIndex - 1] || mempoolFeeColors[mempoolFeeColors.length - 1]);
const endColor = '#' + (this.themeService.mempoolFeeColors[feeLevelIndex - 1] || this.themeService.mempoolFeeColors[this.themeService.mempoolFeeColors.length - 1]);
this.gradient = `linear-gradient(to right, ${startColor}, ${endColor})`;
this.noPriority = startColor;

View File

@@ -4,12 +4,13 @@ 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 { feeLevels, mempoolFeeColors } from '../../app.constants';
import { feeLevels } from '../../app.constants';
import { specialBlocks } from '../../app.constants';
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
import { Location } from '@angular/common';
import { DifficultyAdjustment, MempoolPosition } from '../../interfaces/node-api.interface';
import { animate, style, transition, trigger } from '@angular/animations';
import { ThemeService } from 'src/app/services/theme.service';
@Component({
selector: 'app-mempool-blocks',
@@ -84,6 +85,7 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
constructor(
private router: Router,
public stateService: StateService,
private themeService: ThemeService,
private cd: ChangeDetectorRef,
private relativeUrlPipe: RelativeUrlPipe,
private location: Location,
@@ -354,7 +356,7 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
trimmedFeeRange.forEach((fee: number) => {
let feeLevelIndex = feeLevels.slice().reverse().findIndex((feeLvl) => fee >= feeLvl);
feeLevelIndex = feeLevelIndex >= 0 ? feeLevels.length - feeLevelIndex : feeLevelIndex;
gradientColors.push(mempoolFeeColors[feeLevelIndex - 1] || mempoolFeeColors[mempoolFeeColors.length - 1]);
gradientColors.push(this.themeService.mempoolFeeColors[feeLevelIndex - 1] || this.themeService.mempoolFeeColors[this.themeService.mempoolFeeColors.length - 1]);
});
gradientColors.forEach((color, i, gc) => {