145 lines
4.4 KiB
TypeScript
Raw Normal View History

2024-04-04 06:56:37 +00:00
import { feeLevels, mempoolFeeColors } from '../../app.constants';
2023-12-07 11:12:20 +00:00
import { Color } from './sprite-types';
2023-12-13 10:59:28 +00:00
import TxView from './tx-view';
2023-12-07 11:12:20 +00:00
export function hexToColor(hex: string): Color {
return {
r: parseInt(hex.slice(0, 2), 16) / 255,
g: parseInt(hex.slice(2, 4), 16) / 255,
b: parseInt(hex.slice(4, 6), 16) / 255,
a: hex.length > 6 ? parseInt(hex.slice(6, 8), 16) / 255 : 1
};
}
export 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,
};
}
export function darken(color: Color, amount: number): Color {
return {
r: color.r * amount,
g: color.g * amount,
b: color.b * amount,
a: color.a,
2023-12-13 10:59:28 +00:00
};
}
export function setOpacity(color: Color, opacity: number): Color {
return {
...color,
a: opacity
};
}
2024-03-31 03:54:11 +00:00
interface ColorPalette {
base: Color[],
audit: Color[],
marginal: Color[],
baseLevel: (tx: TxView, rate: number, time: number) => number,
}
2023-12-13 10:59:28 +00:00
// precomputed colors
2024-03-31 03:54:11 +00:00
const defaultColors: { [key: string]: ColorPalette } = {
fee: {
base: mempoolFeeColors.map(hexToColor),
audit: [],
marginal: [],
baseLevel: (tx: TxView, rate: number) => feeLevels.findIndex((feeLvl) => Math.max(1, rate) < feeLvl) - 1
},
}
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 };
2023-12-13 10:59:28 +00:00
export const defaultAuditColors = {
censored: hexToColor('f344df'),
missing: darken(desaturate(hexToColor('f344df'), 0.3), 0.7),
added: hexToColor('0099ff'),
2024-04-02 02:02:17 +00:00
prioritized: darken(desaturate(hexToColor('0099ff'), 0.3), 0.7),
2023-12-13 10:59:28 +00:00
accelerated: hexToColor('8F5FF6'),
};
export function defaultColorFunction(
tx: TxView,
2024-03-31 03:54:11 +00:00
colors: { base: Color[], audit: Color[], marginal: Color[], baseLevel: (tx: TxView, rate: number, time: number) => number } = defaultColors.fee,
auditColors: { [status: string]: Color } = defaultAuditColors,
relativeTime?: number,
2023-12-13 10:59:28 +00:00
): Color {
const rate = tx.fee / tx.vsize; // color by simple single-tx fee rate
2024-03-31 03:54:11 +00:00
const levelIndex = colors.baseLevel(tx, rate, relativeTime || (Date.now() / 1000));
const levelColor = colors.base[levelIndex] || colors.base[mempoolFeeColors.length - 1];
2023-12-13 10:59:28 +00:00
// Normal mode
if (!tx.scene?.highlightingEnabled) {
if (tx.acc) {
return auditColors.accelerated;
} else {
2024-03-31 03:54:11 +00:00
return levelColor;
2023-12-13 10:59:28 +00:00
}
2024-03-31 03:54:11 +00:00
return levelColor;
2023-12-13 10:59:28 +00:00
}
// Block audit
switch(tx.status) {
case 'censored':
return auditColors.censored;
case 'missing':
case 'sigop':
case 'rbf':
2024-03-31 03:54:11 +00:00
return colors.marginal[levelIndex] || colors.marginal[mempoolFeeColors.length - 1];
2023-12-13 10:59:28 +00:00
case 'fresh':
case 'freshcpfp':
return auditColors.missing;
case 'added':
return auditColors.added;
2024-04-02 02:02:17 +00:00
case 'prioritized':
return auditColors.prioritized;
2023-12-13 10:59:28 +00:00
case 'selected':
2024-03-31 03:54:11 +00:00
return colors.marginal[levelIndex] || colors.marginal[mempoolFeeColors.length - 1];
2023-12-13 10:59:28 +00:00
case 'accelerated':
return auditColors.accelerated;
case 'found':
if (tx.context === 'projected') {
2024-03-31 03:54:11 +00:00
return colors.audit[levelIndex] || colors.audit[mempoolFeeColors.length - 1];
2023-12-13 10:59:28 +00:00
} else {
2024-03-31 03:54:11 +00:00
return levelColor;
2023-12-13 10:59:28 +00:00
}
default:
if (tx.acc) {
return auditColors.accelerated;
} else {
2024-03-31 03:54:11 +00:00
return levelColor;
2023-12-13 10:59:28 +00:00
}
2023-12-07 11:12:20 +00:00
}
2024-04-04 06:56:37 +00:00
}
export function ageColorFunction(
tx: TxView,
colors: { base: Color[], audit: Color[], marginal: Color[], baseLevel: (tx: TxView, rate: number, time: number) => number } = defaultColors.fee,
auditColors: { [status: string]: Color } = defaultAuditColors,
relativeTime?: number,
): Color {
const color = defaultColorFunction(tx, colors, auditColors, relativeTime);
const ageLevel = (!tx.time ? 0 : (0.8 * Math.tanh((1 / 15) * Math.log2((Math.max(1, 0.6 * ((relativeTime - tx.time) - 60)))))));
return {
r: color.r,
g: color.g,
b: color.b,
a: color.a * (1 - ageLevel)
};
2023-12-07 11:12:20 +00:00
}