mirror of
https://github.com/mempool/mempool.git
synced 2025-03-29 11:12:16 +01:00
Merge branch 'master' into nymkappa/unix-socket
This commit is contained in:
commit
ba8cca6ba5
@ -1,12 +1,14 @@
|
||||
import { Application, Request, Response } from "express";
|
||||
import config from "../../config";
|
||||
import axios from "axios";
|
||||
import logger from "../../logger";
|
||||
import { Application, Request, Response } from 'express';
|
||||
import config from '../../config';
|
||||
import axios from 'axios';
|
||||
import logger from '../../logger';
|
||||
import mempool from '../mempool';
|
||||
import AccelerationRepository from '../../repositories/AccelerationRepository';
|
||||
|
||||
class AccelerationRoutes {
|
||||
private tag = 'Accelerator';
|
||||
|
||||
public initRoutes(app: Application) {
|
||||
public initRoutes(app: Application): void {
|
||||
app
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'services/accelerator/accelerations', this.$getAcceleratorAccelerations.bind(this))
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'services/accelerator/accelerations/history', this.$getAcceleratorAccelerationsHistory.bind(this))
|
||||
@ -15,41 +17,33 @@ class AccelerationRoutes {
|
||||
;
|
||||
}
|
||||
|
||||
private async $getAcceleratorAccelerations(req: Request, res: Response) {
|
||||
const url = `${config.MEMPOOL_SERVICES.API}/${req.originalUrl.replace('/api/v1/services/', '')}`;
|
||||
try {
|
||||
const response = await axios.get(url, { responseType: 'stream', timeout: 10000 });
|
||||
for (const key in response.headers) {
|
||||
res.setHeader(key, response.headers[key]);
|
||||
}
|
||||
response.data.pipe(res);
|
||||
} catch (e) {
|
||||
logger.err(`Unable to get current accelerations from ${url} in $getAcceleratorAccelerations(), ${e}`, this.tag);
|
||||
res.status(500).end();
|
||||
}
|
||||
private async $getAcceleratorAccelerations(req: Request, res: Response): Promise<void> {
|
||||
const accelerations = mempool.getAccelerations();
|
||||
res.status(200).send(Object.values(accelerations));
|
||||
}
|
||||
|
||||
private async $getAcceleratorAccelerationsHistory(req: Request, res: Response) {
|
||||
const url = `${config.MEMPOOL_SERVICES.API}/${req.originalUrl.replace('/api/v1/services/', '')}`;
|
||||
try {
|
||||
const response = await axios.get(url, { responseType: 'stream', timeout: 10000 });
|
||||
for (const key in response.headers) {
|
||||
res.setHeader(key, response.headers[key]);
|
||||
}
|
||||
response.data.pipe(res);
|
||||
} catch (e) {
|
||||
logger.err(`Unable to get acceleration history from ${url} in $getAcceleratorAccelerationsHistory(), ${e}`, this.tag);
|
||||
res.status(500).end();
|
||||
}
|
||||
private async $getAcceleratorAccelerationsHistory(req: Request, res: Response): Promise<void> {
|
||||
const history = await AccelerationRepository.$getAccelerationInfo(null, req.query.blockHeight ? parseInt(req.query.blockHeight as string, 10) : null);
|
||||
res.status(200).send(history.map(accel => ({
|
||||
txid: accel.txid,
|
||||
added: accel.added,
|
||||
status: 'completed',
|
||||
effectiveFee: accel.effective_fee,
|
||||
effectiveVsize: accel.effective_vsize,
|
||||
boostRate: accel.boost_rate,
|
||||
boostCost: accel.boost_cost,
|
||||
blockHeight: accel.height,
|
||||
pools: [accel.pool],
|
||||
})));
|
||||
}
|
||||
|
||||
private async $getAcceleratorAccelerationsHistoryAggregated(req: Request, res: Response) {
|
||||
private async $getAcceleratorAccelerationsHistoryAggregated(req: Request, res: Response): Promise<void> {
|
||||
const url = `${config.MEMPOOL_SERVICES.API}/${req.originalUrl.replace('/api/v1/services/', '')}`;
|
||||
try {
|
||||
const response = await axios.get(url, { responseType: 'stream', timeout: 10000 });
|
||||
for (const key in response.headers) {
|
||||
res.setHeader(key, response.headers[key]);
|
||||
}
|
||||
res.setHeader(key, response.headers[key]);
|
||||
}
|
||||
response.data.pipe(res);
|
||||
} catch (e) {
|
||||
logger.err(`Unable to get aggregated acceleration history from ${url} in $getAcceleratorAccelerationsHistoryAggregated(), ${e}`, this.tag);
|
||||
@ -57,13 +51,13 @@ class AccelerationRoutes {
|
||||
}
|
||||
}
|
||||
|
||||
private async $getAcceleratorAccelerationsStats(req: Request, res: Response) {
|
||||
private async $getAcceleratorAccelerationsStats(req: Request, res: Response): Promise<void> {
|
||||
const url = `${config.MEMPOOL_SERVICES.API}/${req.originalUrl.replace('/api/v1/services/', '')}`;
|
||||
try {
|
||||
const response = await axios.get(url, { responseType: 'stream', timeout: 10000 });
|
||||
for (const key in response.headers) {
|
||||
res.setHeader(key, response.headers[key]);
|
||||
}
|
||||
res.setHeader(key, response.headers[key]);
|
||||
}
|
||||
response.data.pipe(res);
|
||||
} catch (e) {
|
||||
logger.err(`Unable to get acceleration stats from ${url} in $getAcceleratorAccelerationsStats(), ${e}`, this.tag);
|
||||
|
@ -29,6 +29,7 @@ import websocketHandler from './websocket-handler';
|
||||
import redisCache from './redis-cache';
|
||||
import rbfCache from './rbf-cache';
|
||||
import { calcBitsDifference } from './difficulty-adjustment';
|
||||
import AccelerationRepository from '../repositories/AccelerationRepository';
|
||||
|
||||
class Blocks {
|
||||
private blocks: BlockExtended[] = [];
|
||||
@ -872,6 +873,7 @@ class Blocks {
|
||||
await BlocksRepository.$deleteBlocksFrom(lastBlock.height - 10);
|
||||
await HashratesRepository.$deleteLastEntries();
|
||||
await cpfpRepository.$deleteClustersFrom(lastBlock.height - 10);
|
||||
await AccelerationRepository.$deleteAccelerationsFrom(lastBlock.height - 10);
|
||||
this.blocks = this.blocks.slice(0, -10);
|
||||
this.updateTimerProgress(timer, `rolled back chain divergence from ${this.currentBlockHeight}`);
|
||||
for (let i = 10; i >= 0; --i) {
|
||||
@ -974,6 +976,9 @@ class Blocks {
|
||||
if (this.blocks.length > config.MEMPOOL.INITIAL_BLOCKS_AMOUNT * 4) {
|
||||
this.blocks = this.blocks.slice(-config.MEMPOOL.INITIAL_BLOCKS_AMOUNT * 4);
|
||||
}
|
||||
blockSummary.transactions.forEach(tx => {
|
||||
delete tx.acc;
|
||||
});
|
||||
this.blockSummaries.push(blockSummary);
|
||||
if (this.blockSummaries.length > config.MEMPOOL.INITIAL_BLOCKS_AMOUNT * 4) {
|
||||
this.blockSummaries = this.blockSummaries.slice(-config.MEMPOOL.INITIAL_BLOCKS_AMOUNT * 4);
|
||||
@ -1117,6 +1122,7 @@ class Blocks {
|
||||
}
|
||||
return {
|
||||
txid: tx.txid,
|
||||
time: tx.firstSeen,
|
||||
fee: tx.fee || 0,
|
||||
vsize: tx.vsize,
|
||||
value: Math.round(tx.vout.reduce((acc, vout) => acc + (vout.value ? vout.value : 0), 0)),
|
||||
|
@ -7,7 +7,7 @@ import cpfpRepository from '../repositories/CpfpRepository';
|
||||
import { RowDataPacket } from 'mysql2';
|
||||
|
||||
class DatabaseMigration {
|
||||
private static currentVersion = 76;
|
||||
private static currentVersion = 77;
|
||||
private queryTimeout = 3600_000;
|
||||
private statisticsAddedIndexed = false;
|
||||
private uniqueLogs: string[] = [];
|
||||
@ -664,6 +664,11 @@ class DatabaseMigration {
|
||||
await this.$executeQuery('ALTER TABLE `blocks_audits` ADD prioritized_txs JSON DEFAULT "[]"');
|
||||
await this.updateToSchemaVersion(76);
|
||||
}
|
||||
|
||||
if (databaseSchemaVersion < 77 && config.MEMPOOL.NETWORK === 'mainnet') {
|
||||
await this.$executeQuery('ALTER TABLE `accelerations` ADD requested datetime DEFAULT NULL');
|
||||
await this.updateToSchemaVersion(77);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,6 +5,9 @@ import axios from 'axios';
|
||||
|
||||
export interface Acceleration {
|
||||
txid: string,
|
||||
added: number,
|
||||
effectiveVsize: number,
|
||||
effectiveFee: number,
|
||||
feeDelta: number,
|
||||
pools: number[],
|
||||
};
|
||||
|
@ -6,7 +6,7 @@ import { IEsploraApi } from '../api/bitcoin/esplora-api.interface';
|
||||
import { Common } from '../api/common';
|
||||
import config from '../config';
|
||||
import blocks from '../api/blocks';
|
||||
import accelerationApi, { Acceleration } from '../api/services/acceleration';
|
||||
import accelerationApi, { Acceleration, AccelerationHistory } from '../api/services/acceleration';
|
||||
import accelerationCosts from '../api/acceleration/acceleration';
|
||||
import bitcoinApi from '../api/bitcoin/bitcoin-api-factory';
|
||||
import transactionUtils from '../api/transaction-utils';
|
||||
@ -15,6 +15,7 @@ import { BlockExtended, MempoolTransactionExtended } from '../mempool.interfaces
|
||||
export interface PublicAcceleration {
|
||||
txid: string,
|
||||
height: number,
|
||||
added: number,
|
||||
pool: {
|
||||
id: number,
|
||||
slug: string,
|
||||
@ -29,15 +30,20 @@ export interface PublicAcceleration {
|
||||
class AccelerationRepository {
|
||||
private bidBoostV2Activated = 831580;
|
||||
|
||||
public async $saveAcceleration(acceleration: AccelerationInfo, block: IEsploraApi.Block, pool_id: number): Promise<void> {
|
||||
public async $saveAcceleration(acceleration: AccelerationInfo, block: IEsploraApi.Block, pool_id: number, accelerationData: Acceleration[]): Promise<void> {
|
||||
const accelerationMap: { [txid: string]: Acceleration } = {};
|
||||
for (const acc of accelerationData) {
|
||||
accelerationMap[acc.txid] = acc;
|
||||
}
|
||||
try {
|
||||
await DB.query(`
|
||||
INSERT INTO accelerations(txid, added, height, pool, effective_vsize, effective_fee, boost_rate, boost_cost)
|
||||
VALUE (?, FROM_UNIXTIME(?), ?, ?, ?, ?, ?, ?)
|
||||
INSERT INTO accelerations(txid, requested, added, height, pool, effective_vsize, effective_fee, boost_rate, boost_cost)
|
||||
VALUE (?, FROM_UNIXTIME(?), FROM_UNIXTIME(?), ?, ?, ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
height = ?
|
||||
`, [
|
||||
acceleration.txSummary.txid,
|
||||
accelerationMap[acceleration.txSummary.txid].added,
|
||||
block.timestamp,
|
||||
block.height,
|
||||
pool_id,
|
||||
@ -64,7 +70,7 @@ class AccelerationRepository {
|
||||
}
|
||||
|
||||
let query = `
|
||||
SELECT * FROM accelerations
|
||||
SELECT *, UNIX_TIMESTAMP(requested) as requested_timestamp, UNIX_TIMESTAMP(added) as block_timestamp FROM accelerations
|
||||
JOIN pools on pools.unique_id = accelerations.pool
|
||||
`;
|
||||
let params: any[] = [];
|
||||
@ -99,6 +105,7 @@ class AccelerationRepository {
|
||||
return rows.map(row => ({
|
||||
txid: row.txid,
|
||||
height: row.height,
|
||||
added: row.requested_timestamp || row.block_timestamp,
|
||||
pool: {
|
||||
id: row.id,
|
||||
slug: row.slug,
|
||||
@ -202,7 +209,7 @@ class AccelerationRepository {
|
||||
const tx = blockTxs[acc.txid];
|
||||
const accelerationInfo = accelerationCosts.getAccelerationInfo(tx, boostRate, transactions);
|
||||
accelerationInfo.cost = Math.max(0, Math.min(acc.feeDelta, accelerationInfo.cost));
|
||||
this.$saveAcceleration(accelerationInfo, block, block.extras.pool.id);
|
||||
this.$saveAcceleration(accelerationInfo, block, block.extras.pool.id, successfulAccelerations);
|
||||
}
|
||||
}
|
||||
const lastSyncedHeight = await this.$getLastSyncedHeight();
|
||||
@ -230,7 +237,7 @@ class AccelerationRepository {
|
||||
logger.debug(`Fetching accelerations between block ${lastSyncedHeight} and ${currentHeight}`);
|
||||
|
||||
// Fetch accelerations from mempool.space since the last synced block;
|
||||
const accelerationsByBlock = {};
|
||||
const accelerationsByBlock: {[height: number]: AccelerationHistory[]} = {};
|
||||
const blockHashes = {};
|
||||
let done = false;
|
||||
let page = 1;
|
||||
@ -297,12 +304,16 @@ class AccelerationRepository {
|
||||
const feeStats = Common.calcEffectiveFeeStatistics(template);
|
||||
boostRate = feeStats.medianFee;
|
||||
}
|
||||
const accelerationSummaries = accelerations.map(acc => ({
|
||||
...acc,
|
||||
pools: acc.pools.map(pool => pool.pool_unique_id),
|
||||
}))
|
||||
for (const acc of accelerations) {
|
||||
if (blockTxs[acc.txid]) {
|
||||
const tx = blockTxs[acc.txid];
|
||||
const accelerationInfo = accelerationCosts.getAccelerationInfo(tx, boostRate, transactions);
|
||||
accelerationInfo.cost = Math.max(0, Math.min(acc.feeDelta, accelerationInfo.cost));
|
||||
await this.$saveAcceleration(accelerationInfo, block, block.extras.pool.id);
|
||||
await this.$saveAcceleration(accelerationInfo, block, block.extras.pool.id, accelerationSummaries);
|
||||
}
|
||||
}
|
||||
await this.$setLastSyncedHeight(height);
|
||||
@ -317,6 +328,26 @@ class AccelerationRepository {
|
||||
|
||||
logger.debug(`Indexing accelerations completed`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete accelerations from the database above blockHeight
|
||||
*/
|
||||
public async $deleteAccelerationsFrom(blockHeight: number): Promise<void> {
|
||||
logger.info(`Delete newer accelerations from height ${blockHeight} from the database`);
|
||||
try {
|
||||
const currentSyncedHeight = await this.$getLastSyncedHeight();
|
||||
if (currentSyncedHeight >= blockHeight) {
|
||||
await DB.query(`
|
||||
UPDATE state
|
||||
SET number = ?
|
||||
WHERE name = 'last_acceleration_block'
|
||||
`, [blockHeight - 1]);
|
||||
}
|
||||
await DB.query(`DELETE FROM accelerations where height >= ${blockHeight}`);
|
||||
} catch (e) {
|
||||
logger.err('Cannot delete indexed accelerations. Reason: ' + (e instanceof Error ? e.message : e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new AccelerationRepository();
|
||||
|
3
contributors/daweilv.txt
Normal file
3
contributors/daweilv.txt
Normal file
@ -0,0 +1,3 @@
|
||||
I hereby accept the terms of the Contributor License Agreement in the CONTRIBUTING.md file of the mempool/mempool git repository as of April 7, 2024.
|
||||
|
||||
Signed: daweilv
|
@ -170,6 +170,11 @@
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss",
|
||||
{
|
||||
"input": "src/theme-contrast.scss",
|
||||
"bundleName": "contrast",
|
||||
"inject": false
|
||||
},
|
||||
"node_modules/@fortawesome/fontawesome-svg-core/styles.css"
|
||||
],
|
||||
"vendorChunk": true,
|
||||
|
@ -1,4 +1,4 @@
|
||||
export const mempoolFeeColors = [
|
||||
export const defaultMempoolFeeColors = [
|
||||
'557d00',
|
||||
'5d7d01',
|
||||
'637d02',
|
||||
@ -39,6 +39,47 @@ export const mempoolFeeColors = [
|
||||
'ae005b',
|
||||
];
|
||||
|
||||
export const contrastMempoolFeeColors = [
|
||||
'0082e6',
|
||||
'0984df',
|
||||
'1285d9',
|
||||
'1a87d2',
|
||||
'2388cb',
|
||||
'2c8ac5',
|
||||
'358bbe',
|
||||
'3e8db7',
|
||||
'468eb0',
|
||||
'4f90aa',
|
||||
'5892a3',
|
||||
'61939c',
|
||||
'6a9596',
|
||||
'72968f',
|
||||
'7b9888',
|
||||
'849982',
|
||||
'8d9b7b',
|
||||
'959c74',
|
||||
'9e9e6e',
|
||||
'a79f67',
|
||||
'b0a160',
|
||||
'b9a35a',
|
||||
'c1a453',
|
||||
'caa64c',
|
||||
'd3a745',
|
||||
'dca93f',
|
||||
'e5aa38',
|
||||
'edac31',
|
||||
'f6ad2b',
|
||||
'ffaf24',
|
||||
'ffb01e',
|
||||
'ffb118',
|
||||
'ffb212',
|
||||
'ffb30c',
|
||||
'ffb406',
|
||||
'ffb500',
|
||||
'ffb600',
|
||||
'ffb700',
|
||||
];
|
||||
|
||||
export const chartColors = [
|
||||
"#D81B60",
|
||||
"#8E24AA",
|
||||
|
@ -19,6 +19,7 @@ import { SharedModule } from './shared/shared.module';
|
||||
import { StorageService } from './services/storage.service';
|
||||
import { HttpCacheInterceptor } from './services/http-cache.interceptor';
|
||||
import { LanguageService } from './services/language.service';
|
||||
import { ThemeService } from './services/theme.service';
|
||||
import { FiatShortenerPipe } from './shared/pipes/fiat-shortener.pipe';
|
||||
import { FiatCurrencyPipe } from './shared/pipes/fiat-currency.pipe';
|
||||
import { ShortenStringPipe } from './shared/pipes/shorten-string-pipe/shorten-string.pipe';
|
||||
@ -38,6 +39,7 @@ const providers = [
|
||||
StorageService,
|
||||
EnterpriseService,
|
||||
LanguageService,
|
||||
ThemeService,
|
||||
ShortenStringPipe,
|
||||
FiatShortenerPipe,
|
||||
FiatCurrencyPipe,
|
||||
|
@ -14,7 +14,7 @@
|
||||
}
|
||||
|
||||
.become-sponsor {
|
||||
background-color: #1d1f31;
|
||||
background-color: var(--bg);
|
||||
border-radius: 16px;
|
||||
padding: 12px 20px;
|
||||
width: 400px;
|
||||
|
@ -92,7 +92,7 @@
|
||||
|
||||
&.target {
|
||||
.fill {
|
||||
background: #653b9c;
|
||||
background: var(--tertiary);
|
||||
}
|
||||
.fee {
|
||||
position: absolute;
|
||||
@ -114,7 +114,7 @@
|
||||
}
|
||||
&.active, &:hover {
|
||||
.fill {
|
||||
background: #105fb0;
|
||||
background: var(--primary);
|
||||
}
|
||||
.line {
|
||||
.fee-rate .label {
|
||||
|
@ -65,24 +65,26 @@
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<h5 i18n="accelerator.pay-how-much">How much more are you willing to pay?</h5>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<small class="form-text text-muted mb-2" i18n="accelerator.transaction-fee-description">Choose the maximum extra transaction fee you're willing to pay to get into the next block.</small>
|
||||
<div class="form-group">
|
||||
<div class="fee-card">
|
||||
<div class="d-flex mb-0">
|
||||
<ng-container *ngFor="let option of maxRateOptions">
|
||||
<button type="button" class="btn btn-primary flex-grow-1 btn-border btn-sm feerate" [class]="{active: selectFeeRateIndex === option.index}" (click)="setUserBid(option)">
|
||||
<span class="fee">{{ option.fee + estimate.mempoolBaseFee + estimate.vsizeFee | number }} <span class="symbol" i18n="shared.sats">sats</span></span>
|
||||
<span class="rate">~<app-fee-rate [fee]="option.rate" rounding="1.0-0"></app-fee-rate></span>
|
||||
</button>
|
||||
</ng-container>
|
||||
@if (paymentType !== 'cashapp') {
|
||||
<h5 i18n="accelerator.pay-how-much">How much more are you willing to pay?</h5>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<small class="form-text text-muted mb-2" i18n="accelerator.transaction-fee-description">Choose the maximum extra transaction fee you're willing to pay to get into the next block.</small>
|
||||
<div class="form-group">
|
||||
<div class="fee-card">
|
||||
<div class="d-flex mb-0">
|
||||
<ng-container *ngFor="let option of maxRateOptions">
|
||||
<button type="button" class="btn btn-primary flex-grow-1 btn-border btn-sm feerate" [class]="{active: selectFeeRateIndex === option.index}" (click)="setUserBid(option)">
|
||||
<span class="fee">{{ option.fee + estimate.mempoolBaseFee + estimate.vsizeFee | number }} <span class="symbol" i18n="shared.sats">sats</span></span>
|
||||
<span class="rate">~<app-fee-rate [fee]="option.rate" rounding="1.0-0"></app-fee-rate></span>
|
||||
</button>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<h5>Acceleration summary</h5>
|
||||
<div class="row mb-3">
|
||||
@ -90,27 +92,51 @@
|
||||
<table class="table table-borderless table-border table-dark table-accelerator">
|
||||
<tbody>
|
||||
<!-- ESTIMATED FEE -->
|
||||
<ng-container>
|
||||
<tr class="group-first">
|
||||
<td class="item" i18n="accelerator.next-block-rate">Next block market rate</td>
|
||||
<td class="amt" style="font-size: 16px">
|
||||
{{ estimate.targetFeeRate | number : '1.0-0' }}
|
||||
</td>
|
||||
<td class="units"><span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span></td>
|
||||
</tr>
|
||||
<tr class="info">
|
||||
<td class="info">
|
||||
<i><small i18n="accelerator.estimated-extra-fee-required">Estimated extra fee required</small></i>
|
||||
</td>
|
||||
<td class="amt">
|
||||
{{ math.max(0, estimate.nextBlockFee - estimate.txSummary.effectiveFee) | number }}
|
||||
</td>
|
||||
<td class="units">
|
||||
<span class="symbol" i18n="shared.sats">sats</span>
|
||||
<span class="fiat ml-1"><app-fiat [value]="math.max(0, estimate.nextBlockFee - estimate.txSummary.effectiveFee)"></app-fiat></span>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
@if (paymentType === 'cashapp') {
|
||||
<ng-container>
|
||||
<tr class="group-first">
|
||||
<td class="item" i18n="accelerator.boost-rate">Boost rate</td>
|
||||
<td class="amt" style="font-size: 16px">
|
||||
{{ maxRateOptions[selectFeeRateIndex].rate | number : '1.0-0' }}
|
||||
</td>
|
||||
<td class="units"><span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span></td>
|
||||
</tr>
|
||||
<tr class="info">
|
||||
<td class="info">
|
||||
<i><small i18n="accelerator.estimated-extra-fee-required">Boost fee</small></i>
|
||||
</td>
|
||||
<td class="amt">
|
||||
{{ maxRateOptions[selectFeeRateIndex].fee | number }}
|
||||
</td>
|
||||
<td class="units">
|
||||
<span class="symbol" i18n="shared.sats">sats</span>
|
||||
<span class="fiat ml-1"><app-fiat [value]="maxRateOptions[selectFeeRateIndex].fee"></app-fiat></span>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
} @else {
|
||||
<ng-container>
|
||||
<tr class="group-first">
|
||||
<td class="item" i18n="accelerator.next-block-rate">Next block market rate</td>
|
||||
<td class="amt" style="font-size: 16px">
|
||||
{{ estimate.targetFeeRate | number : '1.0-0' }}
|
||||
</td>
|
||||
<td class="units"><span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span></td>
|
||||
</tr>
|
||||
<tr class="info">
|
||||
<td class="info">
|
||||
<i><small i18n="accelerator.estimated-extra-fee-required">Estimated extra fee required</small></i>
|
||||
</td>
|
||||
<td class="amt">
|
||||
{{ math.max(0, estimate.nextBlockFee - estimate.txSummary.effectiveFee) | number }}
|
||||
</td>
|
||||
<td class="units">
|
||||
<span class="symbol" i18n="shared.sats">sats</span>
|
||||
<span class="fiat ml-1"><app-fiat [value]="math.max(0, estimate.nextBlockFee - estimate.txSummary.effectiveFee)"></app-fiat></span>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
}
|
||||
|
||||
<!-- MEMPOOL BASE FEE -->
|
||||
<tr>
|
||||
@ -141,53 +167,76 @@
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- NEXT BLOCK ESTIMATE -->
|
||||
<ng-container>
|
||||
<tr class="group-first" style="border-top: 1px dashed grey; border-collapse: collapse;">
|
||||
<td class="item">
|
||||
<b style="background-color: #5E35B1" class="p-1 pl-0" i18n="accelerator.estimated-cost">Estimated acceleration cost</b>
|
||||
</td>
|
||||
<td class="amt">
|
||||
<span style="background-color: #5E35B1" class="p-1 pl-0">
|
||||
{{ estimate.cost + estimate.mempoolBaseFee + estimate.vsizeFee | number }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="units">
|
||||
<span class="symbol" i18n="shared.sats">sats</span>
|
||||
<span class="fiat ml-1"><app-fiat [value]="estimate.cost + estimate.mempoolBaseFee + estimate.vsizeFee"></app-fiat></span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="info group-last" style="border-bottom: 1px solid lightgrey">
|
||||
<td class="info" colspan=3>
|
||||
<i><small><ng-container *ngTemplateOutlet="acceleratedTo; context: {$implicit: estimate.targetFeeRate }"></ng-container></small></i>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
|
||||
@if (paymentType === 'cashapp') {
|
||||
<!-- FIXED COST -->
|
||||
<ng-container>
|
||||
<tr class="group-first group-last" style="border-top: 1px solid lightgrey; border-collapse: collapse;">
|
||||
<td class="item">
|
||||
<b style="background-color: #105fb0;" class="p-1 pl-0" i18n="accelerator.total-cost">Total cost</b>
|
||||
</td>
|
||||
<td class="amt">
|
||||
<span style="background-color: #105fb0" class="p-1 pl-0">
|
||||
{{ maxCost | number }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="units">
|
||||
<span class="symbol" i18n="shared.sats">sats</span>
|
||||
<span class="fiat ml-1">
|
||||
<app-fiat [value]="maxCost" [colorClass]="'green-color'"></app-fiat>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
} @else {
|
||||
<!-- NEXT BLOCK ESTIMATE -->
|
||||
<ng-container>
|
||||
<tr class="group-first" style="border-top: 1px dashed grey; border-collapse: collapse;">
|
||||
<td class="item">
|
||||
<b style="background-color: #5E35B1" class="p-1 pl-0" i18n="accelerator.estimated-cost">Estimated acceleration cost</b>
|
||||
</td>
|
||||
<td class="amt">
|
||||
<span style="background-color: #5E35B1" class="p-1 pl-0">
|
||||
{{ estimate.cost + estimate.mempoolBaseFee + estimate.vsizeFee | number }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="units">
|
||||
<span class="symbol" i18n="shared.sats">sats</span>
|
||||
<span class="fiat ml-1"><app-fiat [value]="estimate.cost + estimate.mempoolBaseFee + estimate.vsizeFee"></app-fiat></span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="info group-last" style="border-bottom: 1px solid lightgrey">
|
||||
<td class="info" colspan=3>
|
||||
<i><small><ng-container *ngTemplateOutlet="acceleratedTo; context: {$implicit: estimate.targetFeeRate }"></ng-container></small></i>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
|
||||
<!-- MAX COST -->
|
||||
<ng-container>
|
||||
<tr class="group-first">
|
||||
<td class="item">
|
||||
<b style="background-color: #105fb0;" class="p-1 pl-0" i18n="accelerator.maximum-cost">Maximum acceleration cost</b>
|
||||
</td>
|
||||
<td class="amt">
|
||||
<span style="background-color: #105fb0" class="p-1 pl-0">
|
||||
{{ maxCost | number }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="units">
|
||||
<span class="symbol" i18n="shared.sats">sats</span>
|
||||
<span class="fiat ml-1">
|
||||
<app-fiat [value]="maxCost" [colorClass]="estimate.userBalance < maxCost ? 'red-color' : 'green-color'"></app-fiat>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="info group-last">
|
||||
<td class="info" colspan=3>
|
||||
<i><small><ng-container *ngTemplateOutlet="acceleratedTo; context: {$implicit: (estimate.txSummary.effectiveFee + userBid) / estimate.txSummary.effectiveVsize }"></ng-container></small></i>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
<!-- MAX COST -->
|
||||
<ng-container>
|
||||
<tr class="group-first">
|
||||
<td class="item">
|
||||
<b style="background-color: var(--primary);" class="p-1 pl-0" i18n="accelerator.maximum-cost">Maximum acceleration cost</b>
|
||||
</td>
|
||||
<td class="amt">
|
||||
<span style="background-color: var(--primary)" class="p-1 pl-0">
|
||||
{{ maxCost | number }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="units">
|
||||
<span class="symbol" i18n="shared.sats">sats</span>
|
||||
<span class="fiat ml-1">
|
||||
<app-fiat [value]="maxCost" [colorClass]="estimate.userBalance < maxCost ? 'red-color' : 'green-color'"></app-fiat>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="info group-last">
|
||||
<td class="info" colspan=3>
|
||||
<i><small><ng-container *ngTemplateOutlet="acceleratedTo; context: {$implicit: (estimate.txSummary.effectiveFee + userBid) / estimate.txSummary.effectiveVsize }"></ng-container></small></i>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
}
|
||||
|
||||
<!-- USER BALANCE -->
|
||||
<ng-container *ngIf="isLoggedIn() && estimate.userBalance < maxCost">
|
||||
@ -237,14 +286,17 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row d-flex justify-content-end align-items-center mr-1" style="height: 48px" *ngIf="!hideCashApp && paymentType === 'cashapp'">
|
||||
<div [style]="showSpinner ? 'opacity: 0' : 'opacity: 1'" class="p-2">Accelerate with</div>
|
||||
<div id="cash-app-pay" style="max-width: 320px" [style]="showSpinner ? 'opacity: 0' : 'opacity: 1'"></div>
|
||||
<div *ngIf="showSpinner" class="d-flex align-items-center">
|
||||
<span class="mr-2">Loading</span>
|
||||
<div class="spinner-border text-light" style="width: 25px; height: 25px"></div>
|
||||
@if (!hideCashApp && paymentType === 'cashapp') {
|
||||
<div #cashappCTA class="cashapp-placeholder {{ stickyCTA }}"></div>
|
||||
<div class="d-flex justify-content-center align-items-center cashapp-cta {{ stickyCTA }}" (click)="submitCashappPay()">
|
||||
<div [style]="showSpinner ? 'opacity: 0' : 'opacity: 1'" class="p-2">Accelerate for <app-fiat [value]="maxCost" [colorClass]="estimate.userBalance < maxCost ? 'red-color' : 'green-color'"></app-fiat> with</div>
|
||||
<div id="cash-app-pay" style="max-width: 320px" [style]="showSpinner ? 'opacity: 0' : 'opacity: 1'"></div>
|
||||
<div *ngIf="showSpinner" class="d-flex align-items-center">
|
||||
<span class="mr-2">Loading</span>
|
||||
<div class="spinner-border text-light" style="width: 25px; height: 25px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
</ng-container>
|
||||
|
@ -1,6 +1,6 @@
|
||||
.fee-card {
|
||||
padding: 15px;
|
||||
background-color: #1d1f31;
|
||||
background-color: var(--bg);
|
||||
|
||||
.feerate {
|
||||
display: flex;
|
||||
@ -23,7 +23,7 @@
|
||||
}
|
||||
|
||||
.feerate.active {
|
||||
background-color: #105fb0 !important;
|
||||
background-color: var(--primary) !important;
|
||||
opacity: 1;
|
||||
border: 1px solid #007fff !important;
|
||||
}
|
||||
@ -109,4 +109,61 @@
|
||||
|
||||
.item {
|
||||
white-space: initial;
|
||||
}
|
||||
|
||||
.cashapp-cta {
|
||||
width: 100%;
|
||||
height: 54px;
|
||||
background: #653b9c;
|
||||
position: relative;
|
||||
bottom: initial;
|
||||
top: initial;
|
||||
border-radius: 3px;
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
padding: 4px 6px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0px 0px 15px 0px #000;
|
||||
|
||||
&.sticky-top {
|
||||
position: fixed;
|
||||
width: calc(100vw - 30px - 1.5rem);
|
||||
margin: auto;
|
||||
z-index: 50;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 102px;
|
||||
@media (min-width: 573px) {
|
||||
top: 62px;
|
||||
}
|
||||
}
|
||||
&.sticky-bottom {
|
||||
position: fixed;
|
||||
width: calc(100vw - 30px - 1.5rem);
|
||||
margin: auto;
|
||||
z-index: 50;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 50px;
|
||||
@media (min-width: 430px) {
|
||||
bottom: 56px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
width: calc(100% + 1.5rem);
|
||||
margin: 0 -0.75rem;
|
||||
&.sticky-top, &.sticky-bottom {
|
||||
width: calc(100vw - 30px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cashapp-placeholder {
|
||||
height: 54px;
|
||||
|
||||
&.non-stick {
|
||||
height: 0px;
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { Component, OnInit, Input, OnDestroy, OnChanges, SimpleChanges, HostListener, ChangeDetectorRef } from '@angular/core';
|
||||
import { Component, OnInit, Input, OnDestroy, OnChanges, SimpleChanges, HostListener, ChangeDetectorRef, ViewChild, ElementRef } from '@angular/core';
|
||||
import { Subscription, catchError, of, tap } from 'rxjs';
|
||||
import { StorageService } from '../../services/storage.service';
|
||||
import { Transaction } from '../../interfaces/electrs.interface';
|
||||
@ -43,6 +43,9 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
||||
@Input() tx: Transaction | undefined;
|
||||
@Input() scrollEvent: boolean;
|
||||
|
||||
@ViewChild('cashappCTA')
|
||||
cashappCTA: ElementRef;
|
||||
|
||||
math = Math;
|
||||
error = '';
|
||||
showSuccess = false;
|
||||
@ -56,9 +59,11 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
||||
defaultBid = 0;
|
||||
maxCost = 0;
|
||||
userBid = 0;
|
||||
accelerationUUID: string;
|
||||
selectFeeRateIndex = 1;
|
||||
isMobile: boolean = window.innerWidth <= 767.98;
|
||||
user: any = undefined;
|
||||
stickyCTA: string = 'non-stick';
|
||||
|
||||
maxRateOptions: RateOption[] = [];
|
||||
|
||||
@ -66,6 +71,7 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
||||
paymentType: 'bitcoin' | 'cashapp' = 'bitcoin';
|
||||
cashAppSubscription: Subscription;
|
||||
conversionsSubscription: Subscription;
|
||||
cashappSubmit: any;
|
||||
payments: any;
|
||||
showSpinner = false;
|
||||
square: any;
|
||||
@ -80,7 +86,10 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
||||
private cd: ChangeDetectorRef
|
||||
) {
|
||||
if (this.stateService.ref === 'https://cash.app/') {
|
||||
this.paymentType = 'cashapp';
|
||||
this.insertSquare();
|
||||
} else {
|
||||
this.paymentType = 'bitcoin';
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,21 +103,23 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.accelerationUUID = window.crypto.randomUUID();
|
||||
if (this.stateService.ref === 'https://cash.app/') {
|
||||
this.paymentType = 'cashapp';
|
||||
this.stateService.ref = '';
|
||||
} else {
|
||||
this.paymentType = 'bitcoin';
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes.scrollEvent) {
|
||||
if (changes.scrollEvent && this.paymentType !== 'cashapp' && this.stateService.ref !== 'https://cash.app/') {
|
||||
this.scrollToPreview('acceleratePreviewAnchor', 'start');
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.onScroll();
|
||||
|
||||
if (this.paymentType === 'cashapp') {
|
||||
this.showSpinner = true;
|
||||
}
|
||||
@ -173,10 +184,15 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
||||
this.maxCost = this.userBid + this.estimate.mempoolBaseFee + this.estimate.vsizeFee;
|
||||
|
||||
if (!this.error) {
|
||||
this.scrollToPreview('acceleratePreviewAnchor', 'start');
|
||||
if (this.paymentType === 'cashapp') {
|
||||
this.setupSquare();
|
||||
}
|
||||
} else {
|
||||
this.scrollToPreview('acceleratePreviewAnchor', 'start');
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
this.onScroll();
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
}),
|
||||
@ -231,7 +247,8 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
||||
}
|
||||
this.accelerationSubscription = this.servicesApiService.accelerate$(
|
||||
this.tx.txid,
|
||||
this.userBid
|
||||
this.userBid,
|
||||
this.accelerationUUID
|
||||
).subscribe({
|
||||
next: () => {
|
||||
this.audioService.playSound('ascend-chime-cartoon');
|
||||
@ -301,6 +318,10 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
||||
|
||||
this.conversionsSubscription = this.stateService.conversions$.subscribe(
|
||||
async (conversions) => {
|
||||
if (this.cashAppPay) {
|
||||
this.cashAppPay.destroy();
|
||||
}
|
||||
|
||||
const maxCostUsd = this.maxCost / 100_000_000 * conversions.USD;
|
||||
const paymentRequest = this.payments.paymentRequest({
|
||||
countryCode: 'US',
|
||||
@ -310,13 +331,15 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
||||
label: 'Total',
|
||||
pending: true,
|
||||
productUrl: `https://mempool.space/tx/${this.tx.txid}`,
|
||||
}
|
||||
},
|
||||
button: { shape: 'semiround', size: 'small', theme: 'light'}
|
||||
});
|
||||
this.cashAppPay = await this.payments.cashAppPay(paymentRequest, {
|
||||
redirectURL: `https://mempool.space/tx/${this.tx.txid}`,
|
||||
referenceId: `accelerator-${this.tx.txid.substring(0, 15)}-${Math.round(new Date().getTime() / 1000)}`,
|
||||
button: { shape: 'semiround', size: 'small', theme: 'light'}
|
||||
});
|
||||
await this.cashAppPay.attach('#cash-app-pay');
|
||||
const renderPromise = this.cashAppPay.CashAppPayInstance.render('#cash-app-pay', { button: { theme: 'light', size: 'small', shape: 'semiround' }, manage: false });
|
||||
this.showSpinner = false;
|
||||
|
||||
const that = this;
|
||||
@ -332,7 +355,8 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
||||
that.userBid,
|
||||
tokenResult.token,
|
||||
tokenResult.details.cashAppPay.cashtag,
|
||||
tokenResult.details.cashAppPay.referenceId
|
||||
tokenResult.details.cashAppPay.referenceId,
|
||||
that.accelerationUUID
|
||||
).subscribe({
|
||||
next: () => {
|
||||
that.audioService.playSound('ascend-chime-cartoon');
|
||||
@ -351,13 +375,19 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.cashappSubmit = await renderPromise;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
insertSquare(): void {
|
||||
let statsUrl = 'https://sandbox.web.squarecdn.com/v1/square.js';
|
||||
if (document.location.hostname === 'mempool-staging.tk7.mempool.space' || document.location.hostname === 'mempool.space') {
|
||||
if (document.location.hostname === 'mempool-staging.fmt.mempool.space' ||
|
||||
document.location.hostname === 'mempool-staging.va1.mempool.space' ||
|
||||
document.location.hostname === 'mempool-staging.fra.mempool.space' ||
|
||||
document.location.hostname === 'mempool-staging.tk7.mempool.space' ||
|
||||
document.location.hostname === 'mempool.space') {
|
||||
statsUrl = 'https://web.squarecdn.com/v1/square.js';
|
||||
}
|
||||
|
||||
@ -367,4 +397,34 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
||||
g.type='text/javascript'; g.src=statsUrl; s.parentNode.insertBefore(g, s);
|
||||
})();
|
||||
}
|
||||
|
||||
submitCashappPay(): void {
|
||||
if (this.cashappSubmit) {
|
||||
this.cashappSubmit?.begin();
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('window:scroll', ['$event']) // for window scroll events
|
||||
onScroll() {
|
||||
if (this.estimate && !this.cashappCTA?.nativeElement) {
|
||||
setTimeout(() => {
|
||||
this.onScroll();
|
||||
}, 200);
|
||||
return;
|
||||
}
|
||||
if (!this.cashappCTA?.nativeElement || this.paymentType !== 'cashapp' || !this.isMobile) {
|
||||
return;
|
||||
}
|
||||
const cta = this.cashappCTA.nativeElement;
|
||||
const rect = cta.getBoundingClientRect();
|
||||
const topOffset = window.innerWidth <= 572 ? 102 : 62;
|
||||
const bottomOffset = window.innerWidth < 430 ? 50 : 56;
|
||||
if (rect.top < topOffset) {
|
||||
this.stickyCTA = 'sticky-top';
|
||||
} else if (rect.top > window.innerHeight - (bottomOffset + 54)) {
|
||||
this.stickyCTA = 'sticky-bottom';
|
||||
} else {
|
||||
this.stickyCTA = 'non-stick';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div [class.chart]="!widget" [class.chart-widget]="widget" *browserOnly echarts [initOpts]="chartInitOptions" [options]="chartOptions"
|
||||
<div [class.chart]="!widget" [class.chart-widget]="widget" *browserOnly [style]="{ height: widget ? ((height + 20) + 'px') : null}" echarts [initOpts]="chartInitOptions" [options]="chartOptions"
|
||||
(chartInit)="onChartInit($event)">
|
||||
</div>
|
||||
<div class="text-center loadingGraphs" *ngIf="!stateService.isBrowser || isLoading">
|
||||
|
@ -62,7 +62,7 @@ h5 {
|
||||
|
||||
.card-title {
|
||||
font-size: 1rem;
|
||||
color: #4a68b9;
|
||||
color: var(--title-fg);
|
||||
}
|
||||
|
||||
.disabled {
|
||||
|
@ -1,5 +1,5 @@
|
||||
.card-title {
|
||||
color: #4a68b9;
|
||||
color: var(--title-fg);
|
||||
font-size: 10px;
|
||||
margin-bottom: 4px;
|
||||
font-size: 1rem;
|
||||
|
@ -39,10 +39,10 @@
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!pending">
|
||||
<td *ngIf="acceleration.feePaid" class="fee text-right">
|
||||
{{ (acceleration.boost) | number }} <span class="symbol" i18n="shared.sat|sat">sat</span>
|
||||
<td *ngIf="acceleration.boost != null" class="fee text-right">
|
||||
{{ acceleration.boost | number }} <span class="symbol" i18n="shared.sat|sat">sat</span>
|
||||
</td>
|
||||
<td *ngIf="!acceleration.feePaid" class="fee text-right">
|
||||
<td *ngIf="acceleration.boost == null" class="fee text-right">
|
||||
~
|
||||
</td>
|
||||
<td class="block text-right">
|
||||
|
@ -59,7 +59,7 @@ tr, td, th {
|
||||
}
|
||||
|
||||
.progress {
|
||||
background-color: #2d3348;
|
||||
background-color: var(--secondary);
|
||||
}
|
||||
|
||||
.txid {
|
||||
@ -148,7 +148,7 @@ tr, td, th {
|
||||
|
||||
.tooltip-custom .tooltiptext {
|
||||
visibility: hidden;
|
||||
color: #fff;
|
||||
color: var(--fg);
|
||||
text-align: center;
|
||||
padding: 5px 0;
|
||||
border-radius: 6px;
|
||||
|
@ -58,7 +58,7 @@ export class AccelerationsListComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
for (const acc of accelerations) {
|
||||
acc.boost = acc.feePaid - acc.baseFee - acc.vsizeFee;
|
||||
acc.boost = acc.boostCost != null ? acc.boostCost : (acc.feePaid - acc.baseFee - acc.vsizeFee);
|
||||
}
|
||||
if (this.widget) {
|
||||
return of(accelerations.slice(0, 6));
|
||||
|
@ -40,7 +40,7 @@
|
||||
<a class="title-link" href="" [routerLink]="['/mempool-block/0' | relativeUrl]">
|
||||
<h5 class="card-title d-inline">Mempool Goggles™ : <ng-container i18n="accelerator.accelerations">Accelerations</ng-container></h5>
|
||||
<span> </span>
|
||||
<fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="vertical-align: 'text-top'; font-size: 13px; color: #4a68b9"></fa-icon>
|
||||
<fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="vertical-align: 'text-top'; font-size: 13px; color: var(--title-fg)"></fa-icon>
|
||||
</a>
|
||||
<div class="mempool-block-wrapper" *ngIf="webGlEnabled">
|
||||
<app-mempool-block-overview [index]="0" [overrideColors]="getAcceleratorColor"></app-mempool-block-overview>
|
||||
@ -85,7 +85,7 @@
|
||||
<a class="title-link" href="" [routerLink]="['/acceleration/list' | relativeUrl]">
|
||||
<h5 class="card-title d-inline" i18n="dashboard.recent-accelerations">Recent Accelerations</h5>
|
||||
<span> </span>
|
||||
<fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="vertical-align: 'text-top'; font-size: 13px; color: #4a68b9"></fa-icon>
|
||||
<fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="vertical-align: 'text-top'; font-size: 13px; color: var(--title-fg)"></fa-icon>
|
||||
</a>
|
||||
<app-accelerations-list [attr.data-cy]="'recent-accelerations'" [widget]=true [accelerations$]="minedAccelerations$"></app-accelerations-list>
|
||||
</div>
|
||||
|
@ -7,7 +7,7 @@
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #1d1f31;
|
||||
background-color: var(--bg);
|
||||
}
|
||||
|
||||
.graph-card {
|
||||
@ -29,10 +29,10 @@
|
||||
|
||||
.card-title {
|
||||
font-size: 1rem;
|
||||
color: #4a68b9;
|
||||
color: var(--title-fg);
|
||||
}
|
||||
.card-title > a {
|
||||
color: #4a68b9;
|
||||
color: var(--title-fg);
|
||||
}
|
||||
|
||||
.card-body.pool-ranking {
|
||||
|
@ -8,13 +8,15 @@ import { Observable, catchError, combineLatest, distinctUntilChanged, interval,
|
||||
import { Color } from '../../block-overview-graph/sprite-types';
|
||||
import { hexToColor } from '../../block-overview-graph/utils';
|
||||
import TxView from '../../block-overview-graph/tx-view';
|
||||
import { feeLevels, mempoolFeeColors } from '../../../app.constants';
|
||||
import { feeLevels, defaultMempoolFeeColors, contrastMempoolFeeColors } from '../../../app.constants';
|
||||
import { ServicesApiServices } from '../../../services/services-api.service';
|
||||
import { detectWebGL } from '../../../shared/graphs.utils';
|
||||
import { AudioService } from '../../../services/audio.service';
|
||||
import { ThemeService } from '../../../services/theme.service';
|
||||
|
||||
const acceleratedColor: Color = hexToColor('8F5FF6');
|
||||
const normalColors = mempoolFeeColors.map(hex => hexToColor(hex.slice(0,6) + '5F'));
|
||||
const normalColors = defaultMempoolFeeColors.map(hex => hexToColor(hex + '5F'));
|
||||
const contrastColors = contrastMempoolFeeColors.map(hex => hexToColor(hex.slice(0,6) + '5F'));
|
||||
|
||||
interface AccelerationBlock extends BlockExtended {
|
||||
accelerationCount: number,
|
||||
@ -37,6 +39,7 @@ export class AcceleratorDashboardComponent implements OnInit {
|
||||
firstLoad = true;
|
||||
|
||||
graphHeight: number = 300;
|
||||
theme: ThemeService;
|
||||
|
||||
constructor(
|
||||
private seoService: SeoService,
|
||||
@ -116,15 +119,15 @@ export class AcceleratorDashboardComponent implements OnInit {
|
||||
switchMap(([accelerations, blocks]) => {
|
||||
const blockMap = {};
|
||||
for (const block of blocks) {
|
||||
blockMap[block.id] = block;
|
||||
blockMap[block.height] = block;
|
||||
}
|
||||
const accelerationsByBlock: { [ hash: string ]: Acceleration[] } = {};
|
||||
const accelerationsByBlock: { [ height: number ]: Acceleration[] } = {};
|
||||
for (const acceleration of accelerations) {
|
||||
if (['completed_provisional', 'failed_provisional', 'completed'].includes(acceleration.status) && acceleration.pools.includes(blockMap[acceleration.blockHash]?.extras.pool.id)) {
|
||||
if (!accelerationsByBlock[acceleration.blockHash]) {
|
||||
accelerationsByBlock[acceleration.blockHash] = [];
|
||||
if (['completed_provisional', 'failed_provisional', 'completed'].includes(acceleration.status) && acceleration.pools.includes(blockMap[acceleration.blockHeight]?.extras.pool.id)) {
|
||||
if (!accelerationsByBlock[acceleration.blockHeight]) {
|
||||
accelerationsByBlock[acceleration.blockHeight] = [];
|
||||
}
|
||||
accelerationsByBlock[acceleration.blockHash].push(acceleration);
|
||||
accelerationsByBlock[acceleration.blockHeight].push(acceleration);
|
||||
}
|
||||
}
|
||||
return of(blocks.slice(0, 6).map(block => {
|
||||
@ -141,7 +144,7 @@ export class AcceleratorDashboardComponent implements OnInit {
|
||||
} else {
|
||||
const rate = tx.fee / tx.vsize; // color by simple single-tx fee rate
|
||||
const feeLevelIndex = feeLevels.findIndex((feeLvl) => Math.max(1, rate) < feeLvl) - 1;
|
||||
return normalColors[feeLevelIndex] || normalColors[mempoolFeeColors.length - 1];
|
||||
return this.theme.theme === 'contrast' ? contrastColors[feeLevelIndex] || contrastColors[contrastColors.length - 1] : normalColors[feeLevelIndex] || normalColors[normalColors.length - 1];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
.card-title {
|
||||
color: #4a68b9;
|
||||
color: var(--title-fg);
|
||||
font-size: 10px;
|
||||
margin-bottom: 4px;
|
||||
font-size: 1rem;
|
||||
|
@ -6,6 +6,7 @@ import { ChainStats } from '../../interfaces/electrs.interface';
|
||||
import { ElectrsApiService } from '../../services/electrs-api.service';
|
||||
import { AmountShortenerPipe } from '../../shared/pipes/amount-shortener.pipe';
|
||||
import { Router } from '@angular/router';
|
||||
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-address-graph',
|
||||
@ -46,6 +47,7 @@ export class AddressGraphComponent implements OnChanges {
|
||||
private router: Router,
|
||||
private amountShortenerPipe: AmountShortenerPipe,
|
||||
private cd: ChangeDetectorRef,
|
||||
private relativeUrlPipe: RelativeUrlPipe,
|
||||
) {}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
@ -122,7 +124,7 @@ export class AddressGraphComponent implements OnChanges {
|
||||
</div>
|
||||
<span>${date}</span>
|
||||
</div>
|
||||
`;
|
||||
`;
|
||||
}.bind(this)
|
||||
},
|
||||
xAxis: {
|
||||
@ -159,7 +161,7 @@ export class AddressGraphComponent implements OnChanges {
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: $localize`Balance:Balance`,
|
||||
name: $localize`:@@7e69426bd97a606d8ae6026762858e6e7c86a1fd:Balance`,
|
||||
showSymbol: false,
|
||||
symbol: 'circle',
|
||||
symbolSize: 8,
|
||||
@ -178,7 +180,7 @@ export class AddressGraphComponent implements OnChanges {
|
||||
|
||||
onChartClick(e) {
|
||||
if (this.hoverData?.length && this.hoverData[0]?.[2]?.txid) {
|
||||
this.router.navigate(['/tx/', this.hoverData[0][2].txid]);
|
||||
this.router.navigate([this.relativeUrlPipe.transform('/tx/'), this.hoverData[0][2].txid]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
}
|
||||
|
||||
.qr-wrapper {
|
||||
background-color: #FFF;
|
||||
background-color: var(--fg);
|
||||
padding: 10px;
|
||||
padding-bottom: 5px;
|
||||
display: inline-block;
|
||||
|
@ -1,5 +1,5 @@
|
||||
.qr-wrapper {
|
||||
background-color: #FFF;
|
||||
background-color: var(--fg);
|
||||
padding: 10px;
|
||||
padding-bottom: 5px;
|
||||
display: inline-block;
|
||||
|
@ -1,3 +1,3 @@
|
||||
.green-color {
|
||||
color: #3bcc49;
|
||||
color: var(--green);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
.qr-wrapper {
|
||||
background-color: #FFF;
|
||||
background-color: var(--fg);
|
||||
padding: 10px;
|
||||
padding-bottom: 5px;
|
||||
display: inline-block;
|
||||
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #1d1f31;
|
||||
background-color: var(--bg);
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
align-items: center;
|
||||
|
@ -7,7 +7,7 @@
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #1d1f31;
|
||||
background-color: var(--bg);
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
align-items: center;
|
||||
|
@ -95,7 +95,7 @@
|
||||
}
|
||||
.card-title {
|
||||
font-size: 1rem;
|
||||
color: #4a68b9;
|
||||
color: var(--title-fg);
|
||||
}
|
||||
.card-text {
|
||||
font-size: 18px;
|
||||
|
@ -233,7 +233,7 @@ export class BlockFeeRatesGraphComponent implements OnInit {
|
||||
borderRadius: 4,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
textStyle: {
|
||||
color: '#b1b1b1',
|
||||
color: 'var(--tooltip-grey)',
|
||||
align: 'left',
|
||||
},
|
||||
borderColor: '#000',
|
||||
@ -309,7 +309,7 @@ export class BlockFeeRatesGraphComponent implements OnInit {
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: 'dotted',
|
||||
color: '#ffffff66',
|
||||
color: 'var(--transparent-fg)',
|
||||
opacity: 0.25,
|
||||
}
|
||||
},
|
||||
@ -376,7 +376,7 @@ export class BlockFeeRatesGraphComponent implements OnInit {
|
||||
const now = new Date();
|
||||
// @ts-ignore
|
||||
this.chartOptions.grid.bottom = 40;
|
||||
this.chartOptions.backgroundColor = '#11131f';
|
||||
this.chartOptions.backgroundColor = 'var(--active-bg)';
|
||||
this.chartInstance.setOption(this.chartOptions);
|
||||
download(this.chartInstance.getDataURL({
|
||||
pixelRatio: 2,
|
||||
|
@ -151,7 +151,7 @@ export class BlockFeesGraphComponent implements OnInit {
|
||||
borderRadius: 4,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
textStyle: {
|
||||
color: '#b1b1b1',
|
||||
color: 'var(--tooltip-grey)',
|
||||
align: 'left',
|
||||
},
|
||||
borderColor: '#000',
|
||||
@ -214,7 +214,7 @@ export class BlockFeesGraphComponent implements OnInit {
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: 'dotted',
|
||||
color: '#ffffff66',
|
||||
color: 'var(--transparent-fg)',
|
||||
opacity: 0.25,
|
||||
}
|
||||
},
|
||||
@ -305,7 +305,7 @@ export class BlockFeesGraphComponent implements OnInit {
|
||||
const now = new Date();
|
||||
// @ts-ignore
|
||||
this.chartOptions.grid.bottom = 40;
|
||||
this.chartOptions.backgroundColor = '#11131f';
|
||||
this.chartOptions.backgroundColor = 'var(--active-bg)';
|
||||
this.chartInstance.setOption(this.chartOptions);
|
||||
download(this.chartInstance.getDataURL({
|
||||
pixelRatio: 2,
|
||||
|
@ -71,7 +71,7 @@
|
||||
.filter-tag {
|
||||
font-size: 0.9em;
|
||||
background: #181b2daf;
|
||||
border: solid 1px #105fb0;
|
||||
border: solid 1px var(--primary);
|
||||
color: white;
|
||||
border-radius: 0.2rem;
|
||||
padding: 0.2em 0.5em;
|
||||
@ -80,15 +80,15 @@
|
||||
pointer-events: all;
|
||||
|
||||
&.selected {
|
||||
background-color: #105fb0;
|
||||
background-color: var(--primary);
|
||||
}
|
||||
}
|
||||
|
||||
&.any-mode {
|
||||
.filter-tag {
|
||||
border: solid 1px #1a9436;
|
||||
border: solid 1px var(--success);
|
||||
&.selected {
|
||||
background-color: #1a9436;
|
||||
background-color: var(--success);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -114,15 +114,15 @@
|
||||
}
|
||||
|
||||
&.blue {
|
||||
border: solid 1px #105fb0;
|
||||
border: solid 1px var(--primary);
|
||||
&.active {
|
||||
background: #105fb0;
|
||||
background: var(--primary);
|
||||
}
|
||||
}
|
||||
&.green {
|
||||
border: solid 1px #1a9436;
|
||||
border: solid 1px var(--success);
|
||||
&.active {
|
||||
background: #1a9436;
|
||||
background: var(--success);
|
||||
}
|
||||
}
|
||||
&.yellow {
|
||||
|
@ -131,7 +131,7 @@ export class BlockHealthGraphComponent implements OnInit {
|
||||
borderRadius: 4,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
textStyle: {
|
||||
color: '#b1b1b1',
|
||||
color: 'var(--tooltip-grey)',
|
||||
align: 'left',
|
||||
},
|
||||
borderColor: '#000',
|
||||
@ -178,7 +178,7 @@ export class BlockHealthGraphComponent implements OnInit {
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: 'dotted',
|
||||
color: '#ffffff66',
|
||||
color: 'var(--transparent-fg)',
|
||||
opacity: 0.25,
|
||||
}
|
||||
},
|
||||
@ -290,7 +290,7 @@ export class BlockHealthGraphComponent implements OnInit {
|
||||
const now = new Date();
|
||||
// @ts-ignore
|
||||
this.chartOptions.grid.bottom = 40;
|
||||
this.chartOptions.backgroundColor = '#11131f';
|
||||
this.chartOptions.backgroundColor = 'var(--active-bg)';
|
||||
this.chartInstance.setOption(this.chartOptions);
|
||||
download(this.chartInstance.getDataURL({
|
||||
pixelRatio: 2,
|
||||
|
@ -2,7 +2,7 @@
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding-bottom: 100%;
|
||||
background: #181b2d;
|
||||
background: var(--stat-box-bg);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
@ -7,8 +7,9 @@ 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 '../../services/theme.service';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { defaultColorFunction, setOpacity, defaultAuditColors, defaultColors, ageColorFunction } from './utils';
|
||||
import { defaultColorFunction, setOpacity, defaultAuditColors, defaultColors, ageColorFunction, contrastColorFunction, contrastAuditColors, contrastColors } from './utils';
|
||||
import { ActiveFilter, FilterMode, toFlags } from '../../shared/filters.utils';
|
||||
import { detectWebGL } from '../../shared/graphs.utils';
|
||||
|
||||
@ -20,6 +21,13 @@ const unmatchedAuditColors = {
|
||||
prioritized: setOpacity(defaultAuditColors.prioritized, unmatchedOpacity),
|
||||
accelerated: setOpacity(defaultAuditColors.accelerated, unmatchedOpacity),
|
||||
};
|
||||
const unmatchedContrastAuditColors = {
|
||||
censored: setOpacity(contrastAuditColors.censored, unmatchedOpacity),
|
||||
missing: setOpacity(contrastAuditColors.missing, unmatchedOpacity),
|
||||
added: setOpacity(contrastAuditColors.added, unmatchedOpacity),
|
||||
prioritized: setOpacity(contrastAuditColors.prioritized, unmatchedOpacity),
|
||||
accelerated: setOpacity(contrastAuditColors.accelerated, unmatchedOpacity),
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: 'app-block-overview-graph',
|
||||
@ -53,6 +61,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
|
||||
|
||||
@ViewChild('blockCanvas')
|
||||
canvas: ElementRef<HTMLCanvasElement>;
|
||||
themeChangedSubscription: Subscription;
|
||||
|
||||
gl: WebGLRenderingContext;
|
||||
animationFrameRequest: number;
|
||||
@ -84,6 +93,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);
|
||||
@ -102,6 +112,9 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
|
||||
if (this.gl) {
|
||||
this.initCanvas();
|
||||
this.resizeCanvas();
|
||||
this.themeChangedSubscription = this.themeService.themeChanged$.subscribe(() => {
|
||||
this.scene.setColorFunction(this.getColorFunction());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -148,6 +161,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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -293,7 +307,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();
|
||||
@ -563,14 +577,27 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
|
||||
getFilterColorFunction(flags: bigint, gradient: 'fee' | 'age'): ((tx: TxView) => Color) {
|
||||
return (tx: TxView) => {
|
||||
if ((this.filterMode === 'and' && (tx.bigintFlags & flags) === flags) || (this.filterMode === 'or' && (flags === 0n || (tx.bigintFlags & flags) > 0n))) {
|
||||
return (gradient === 'age') ? ageColorFunction(tx, defaultColors.fee, defaultAuditColors, this.relativeTime || (Date.now() / 1000)) : defaultColorFunction(tx, defaultColors.fee, defaultAuditColors, this.relativeTime || (Date.now() / 1000));
|
||||
if (this.themeService.theme !== 'contrast') {
|
||||
return (gradient === 'age') ? ageColorFunction(tx, defaultColors.fee, defaultAuditColors, this.relativeTime || (Date.now() / 1000)) : defaultColorFunction(tx, defaultColors.fee, defaultAuditColors, this.relativeTime || (Date.now() / 1000));
|
||||
} else {
|
||||
return (gradient === 'age') ? ageColorFunction(tx, contrastColors.fee, contrastAuditColors, this.relativeTime || (Date.now() / 1000)) : contrastColorFunction(tx, contrastColors.fee, contrastAuditColors, this.relativeTime || (Date.now() / 1000));
|
||||
}
|
||||
} else {
|
||||
return (gradient === 'age') ? { r: 1, g: 1, b: 1, a: 0.05 } : defaultColorFunction(
|
||||
tx,
|
||||
defaultColors.unmatchedfee,
|
||||
unmatchedAuditColors,
|
||||
this.relativeTime || (Date.now() / 1000)
|
||||
);
|
||||
if (this.themeService.theme !== 'contrast') {
|
||||
return (gradient === 'age') ? { r: 1, g: 1, b: 1, a: 0.05 } : defaultColorFunction(
|
||||
tx,
|
||||
defaultColors.unmatchedfee,
|
||||
unmatchedAuditColors,
|
||||
this.relativeTime || (Date.now() / 1000)
|
||||
);
|
||||
} else {
|
||||
return (gradient === 'age') ? { r: 1, g: 1, b: 1, a: 0.05 } : contrastColorFunction(
|
||||
tx,
|
||||
contrastColors.unmatchedfee,
|
||||
unmatchedContrastAuditColors,
|
||||
this.relativeTime || (Date.now() / 1000)
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -2,13 +2,15 @@ import { FastVertexArray } from './fast-vertex-array';
|
||||
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 { defaultColorFunction, contrastColorFunction } from './utils';
|
||||
import { ThemeService } from '../../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 {
|
||||
@ -67,7 +69,7 @@ export default class BlockScene {
|
||||
}
|
||||
|
||||
setColorFunction(colorFunction: ((tx: TxView) => Color) | null): void {
|
||||
this.getColor = colorFunction || defaultColorFunction;
|
||||
this.theme.theme !== 'default' ? this.getColor = colorFunction || contrastColorFunction : this.getColor = colorFunction || defaultColorFunction;
|
||||
this.updateAllColors();
|
||||
}
|
||||
|
||||
@ -197,6 +199,7 @@ export default class BlockScene {
|
||||
this.txs[tx.txid].feerate = tx.rate || (this.txs[tx.txid].fee / this.txs[tx.txid].vsize);
|
||||
this.txs[tx.txid].rate = tx.rate;
|
||||
this.txs[tx.txid].dirty = true;
|
||||
this.updateColor(this.txs[tx.txid], startTime, 50, true);
|
||||
}
|
||||
});
|
||||
|
||||
@ -232,9 +235,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;
|
||||
@ -243,7 +246,8 @@ export default class BlockScene {
|
||||
this.flip = flip;
|
||||
this.vertexArray = vertexArray;
|
||||
this.highlightingEnabled = highlighting;
|
||||
this.getColor = colorFunction || defaultColorFunction;
|
||||
theme.theme !== 'default' ? this.getColor = colorFunction || contrastColorFunction : this.getColor = colorFunction || defaultColorFunction;
|
||||
this.theme = theme;
|
||||
|
||||
this.scene = {
|
||||
count: 0,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { feeLevels, mempoolFeeColors } from '../../app.constants';
|
||||
import { feeLevels, defaultMempoolFeeColors, contrastMempoolFeeColors } from '../../app.constants';
|
||||
import { Color } from './sprite-types';
|
||||
import TxView from './tx-view';
|
||||
|
||||
@ -47,7 +47,7 @@ interface ColorPalette {
|
||||
// precomputed colors
|
||||
const defaultColors: { [key: string]: ColorPalette } = {
|
||||
fee: {
|
||||
base: mempoolFeeColors.map(hexToColor),
|
||||
base: defaultMempoolFeeColors.map(hexToColor),
|
||||
audit: [],
|
||||
marginal: [],
|
||||
baseLevel: (tx: TxView, rate: number) => feeLevels.findIndex((feeLvl) => Math.max(1, rate) < feeLvl) - 1
|
||||
@ -72,7 +72,37 @@ export const defaultAuditColors = {
|
||||
missing: darken(desaturate(hexToColor('f344df'), 0.3), 0.7),
|
||||
added: hexToColor('0099ff'),
|
||||
prioritized: darken(desaturate(hexToColor('0099ff'), 0.3), 0.7),
|
||||
accelerated: hexToColor('8F5FF6'),
|
||||
accelerated: hexToColor('8f5ff6'),
|
||||
};
|
||||
|
||||
const contrastColors: { [key: string]: ColorPalette } = {
|
||||
fee: {
|
||||
base: contrastMempoolFeeColors.map(hexToColor),
|
||||
audit: [],
|
||||
marginal: [],
|
||||
baseLevel: (tx: TxView, rate: number) => feeLevels.findIndex((feeLvl) => Math.max(1, rate) < feeLvl) - 1
|
||||
},
|
||||
}
|
||||
for (const key in contrastColors) {
|
||||
const base = contrastColors[key].base;
|
||||
contrastColors[key].audit = base.map((color) => darken(desaturate(color, 0.3), 0.9));
|
||||
contrastColors[key].marginal = base.map((color) => darken(desaturate(color, 0.8), 1.1));
|
||||
contrastColors['unmatched' + key] = {
|
||||
base: contrastColors[key].base.map(c => setOpacity(c, 0.2)),
|
||||
audit: contrastColors[key].audit.map(c => setOpacity(c, 0.2)),
|
||||
marginal: contrastColors[key].marginal.map(c => setOpacity(c, 0.2)),
|
||||
baseLevel: contrastColors[key].baseLevel,
|
||||
};
|
||||
}
|
||||
|
||||
export { contrastColors as contrastColors };
|
||||
|
||||
export const contrastAuditColors = {
|
||||
censored: hexToColor('ffa8ff'),
|
||||
missing: darken(desaturate(hexToColor('ffa8ff'), 0.3), 0.7),
|
||||
added: hexToColor('00bb98'),
|
||||
prioritized: darken(desaturate(hexToColor('00bb98'), 0.3), 0.7),
|
||||
accelerated: hexToColor('8f5ff6'),
|
||||
};
|
||||
|
||||
export function defaultColorFunction(
|
||||
@ -83,7 +113,7 @@ export function defaultColorFunction(
|
||||
): Color {
|
||||
const rate = tx.fee / tx.vsize; // color by simple single-tx fee rate
|
||||
const levelIndex = colors.baseLevel(tx, rate, relativeTime || (Date.now() / 1000));
|
||||
const levelColor = colors.base[levelIndex] || colors.base[mempoolFeeColors.length - 1];
|
||||
const levelColor = colors.base[levelIndex] || colors.base[defaultMempoolFeeColors.length - 1];
|
||||
// Normal mode
|
||||
if (!tx.scene?.highlightingEnabled) {
|
||||
if (tx.acc) {
|
||||
@ -100,7 +130,7 @@ export function defaultColorFunction(
|
||||
case 'missing':
|
||||
case 'sigop':
|
||||
case 'rbf':
|
||||
return colors.marginal[levelIndex] || colors.marginal[mempoolFeeColors.length - 1];
|
||||
return colors.marginal[levelIndex] || colors.marginal[defaultMempoolFeeColors.length - 1];
|
||||
case 'fresh':
|
||||
case 'freshcpfp':
|
||||
return auditColors.missing;
|
||||
@ -109,12 +139,12 @@ export function defaultColorFunction(
|
||||
case 'prioritized':
|
||||
return auditColors.prioritized;
|
||||
case 'selected':
|
||||
return colors.marginal[levelIndex] || colors.marginal[mempoolFeeColors.length - 1];
|
||||
return colors.marginal[levelIndex] || colors.marginal[defaultMempoolFeeColors.length - 1];
|
||||
case 'accelerated':
|
||||
return auditColors.accelerated;
|
||||
case 'found':
|
||||
if (tx.context === 'projected') {
|
||||
return colors.audit[levelIndex] || colors.audit[mempoolFeeColors.length - 1];
|
||||
return colors.audit[levelIndex] || colors.audit[defaultMempoolFeeColors.length - 1];
|
||||
} else {
|
||||
return levelColor;
|
||||
}
|
||||
@ -127,17 +157,27 @@ export function defaultColorFunction(
|
||||
}
|
||||
}
|
||||
|
||||
export function contrastColorFunction(
|
||||
tx: TxView,
|
||||
colors: { base: Color[], audit: Color[], marginal: Color[], baseLevel: (tx: TxView, rate: number, time: number) => number } = contrastColors.fee,
|
||||
auditColors: { [status: string]: Color } = contrastAuditColors,
|
||||
relativeTime?: number,
|
||||
): Color {
|
||||
return defaultColorFunction(tx, colors, auditColors, relativeTime);
|
||||
}
|
||||
|
||||
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,
|
||||
theme?: string,
|
||||
): Color {
|
||||
if (tx.acc || tx.status === 'accelerated') {
|
||||
return auditColors.accelerated;
|
||||
}
|
||||
|
||||
const color = defaultColorFunction(tx, colors, auditColors, relativeTime);
|
||||
const color = theme !== 'contrast' ? defaultColorFunction(tx, colors, auditColors, relativeTime) : contrastColorFunction(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 {
|
||||
@ -146,4 +186,4 @@ export function ageColorFunction(
|
||||
b: color.b,
|
||||
a: color.a * (1 - ageLevel)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
background: rgba(#11131f, 0.95);
|
||||
border-radius: 4px;
|
||||
box-shadow: 1px 1px 10px rgba(0,0,0,0.5);
|
||||
color: #b1b1b1;
|
||||
color: var(--tooltip-grey);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
@ -30,7 +30,7 @@ th, td {
|
||||
}
|
||||
|
||||
.badge.badge-accelerated {
|
||||
background-color: #653b9c;
|
||||
background-color: var(--tertiary);
|
||||
box-shadow: #ad7de57f 0px 0px 12px -2px;
|
||||
color: white;
|
||||
animation: acceleratePulse 1s infinite;
|
||||
@ -51,27 +51,27 @@ th, td {
|
||||
|
||||
.filter-tag {
|
||||
background: #181b2daf;
|
||||
border: solid 1px #105fb0;
|
||||
border: solid 1px var(--primary);
|
||||
color: white;
|
||||
transition: background-color 300ms;
|
||||
|
||||
&.matching {
|
||||
background-color: #105fb0;
|
||||
background-color: var(--primary);
|
||||
}
|
||||
}
|
||||
|
||||
&.any-mode {
|
||||
.filter-tag {
|
||||
border: solid 1px #1a9436;
|
||||
border: solid 1px var(--success);
|
||||
&.matching {
|
||||
background-color: #1a9436;
|
||||
background-color: var(--success);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes acceleratePulse {
|
||||
0% { background-color: #653b9c; box-shadow: #ad7de57f 0px 0px 12px -2px; }
|
||||
0% { background-color: var(--tertiary); box-shadow: #ad7de57f 0px 0px 12px -2px; }
|
||||
50% { background-color: #8457bb; box-shadow: #ad7de5 0px 0px 18px -2px;}
|
||||
100% { background-color: #653b9c; box-shadow: #ad7de57f 0px 0px 12px -2px; }
|
||||
100% { background-color: var(--tertiary); box-shadow: #ad7de57f 0px 0px 12px -2px; }
|
||||
}
|
@ -150,7 +150,7 @@ export class BlockRewardsGraphComponent implements OnInit {
|
||||
borderRadius: 4,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
textStyle: {
|
||||
color: '#b1b1b1',
|
||||
color: 'var(--tooltip-grey)',
|
||||
align: 'left',
|
||||
},
|
||||
borderColor: '#000',
|
||||
@ -219,7 +219,7 @@ export class BlockRewardsGraphComponent implements OnInit {
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: 'dotted',
|
||||
color: '#ffffff66',
|
||||
color: 'var(--transparent-fg)',
|
||||
opacity: 0.25,
|
||||
}
|
||||
},
|
||||
@ -315,7 +315,7 @@ export class BlockRewardsGraphComponent implements OnInit {
|
||||
const now = new Date();
|
||||
// @ts-ignore
|
||||
this.chartOptions.grid.bottom = 40;
|
||||
this.chartOptions.backgroundColor = '#11131f';
|
||||
this.chartOptions.backgroundColor = 'var(--active-bg)';
|
||||
this.chartInstance.setOption(this.chartOptions);
|
||||
download(this.chartInstance.getDataURL({
|
||||
pixelRatio: 2,
|
||||
|
@ -146,7 +146,7 @@ export class BlockSizesWeightsGraphComponent implements OnInit {
|
||||
borderRadius: 4,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
textStyle: {
|
||||
color: '#b1b1b1',
|
||||
color: 'var(--tooltip-grey)',
|
||||
align: 'left',
|
||||
},
|
||||
borderColor: '#000',
|
||||
@ -230,7 +230,7 @@ export class BlockSizesWeightsGraphComponent implements OnInit {
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: 'dotted',
|
||||
color: '#ffffff66',
|
||||
color: 'var(--transparent-fg)',
|
||||
opacity: 0.25,
|
||||
}
|
||||
},
|
||||
@ -252,7 +252,7 @@ export class BlockSizesWeightsGraphComponent implements OnInit {
|
||||
symbol: 'none',
|
||||
lineStyle: {
|
||||
type: 'solid',
|
||||
color: '#ffffff66',
|
||||
color: 'var(--transparent-fg)',
|
||||
opacity: 1,
|
||||
width: 1,
|
||||
},
|
||||
@ -342,7 +342,7 @@ export class BlockSizesWeightsGraphComponent implements OnInit {
|
||||
const now = new Date();
|
||||
// @ts-ignore
|
||||
this.chartOptions.grid.bottom = 40;
|
||||
this.chartOptions.backgroundColor = '#11131f';
|
||||
this.chartOptions.backgroundColor = 'var(--active-bg)';
|
||||
this.chartInstance.setOption(this.chartOptions);
|
||||
download(this.chartInstance.getDataURL({
|
||||
pixelRatio: 2,
|
||||
|
@ -136,7 +136,7 @@ export class BlockPreviewComponent implements OnInit, OnDestroy {
|
||||
return of(transactions);
|
||||
})
|
||||
),
|
||||
this.stateService.env.ACCELERATOR === true && block.height > 819500 ? this.servicesApiService.getAccelerationHistory$({ blockHash: block.id }) : of([])
|
||||
this.stateService.env.ACCELERATOR === true && block.height > 819500 ? this.servicesApiService.getAccelerationHistory$({ blockHeight: block.height }) : of([])
|
||||
]);
|
||||
}
|
||||
),
|
||||
|
@ -22,7 +22,7 @@
|
||||
}
|
||||
|
||||
.qr-wrapper {
|
||||
background-color: #FFF;
|
||||
background-color: var(--fg);
|
||||
padding: 10px;
|
||||
padding-bottom: 5px;
|
||||
display: inline-block;
|
||||
@ -175,9 +175,7 @@ h1 {
|
||||
}
|
||||
|
||||
a {
|
||||
color: #1ad8f4;
|
||||
&:hover, &:focus {
|
||||
color: #09a3ba;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
@ -254,7 +252,7 @@ h1 {
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
background: #24273e;
|
||||
background: var(--box-bg);
|
||||
}
|
||||
|
||||
&.active, &:hover {
|
||||
|
@ -345,7 +345,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
return of(null);
|
||||
})
|
||||
),
|
||||
this.stateService.env.ACCELERATOR === true && block.height > 819500 ? this.servicesApiService.getAccelerationHistory$({ blockHash: block.id }) : of([])
|
||||
this.stateService.env.ACCELERATOR === true && block.height > 819500 ? this.servicesApiService.getAccelerationHistory$({ blockHeight: block.height }) : of([])
|
||||
]);
|
||||
})
|
||||
)
|
||||
@ -358,11 +358,15 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
|
||||
const acceleratedInBlock = {};
|
||||
for (const acc of accelerations) {
|
||||
acceleratedInBlock[acc.txid] = acc;
|
||||
if (acc.pools?.some(pool => pool === this.block?.extras?.pool.id || pool?.['pool_unique_id'] === this.block?.extras?.pool.id)) {
|
||||
acceleratedInBlock[acc.txid] = acc;
|
||||
}
|
||||
}
|
||||
for (const tx of transactions) {
|
||||
if (acceleratedInBlock[tx.txid]) {
|
||||
tx.acc = true;
|
||||
} else {
|
||||
tx.acc = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,7 +117,7 @@
|
||||
}
|
||||
|
||||
.black-background {
|
||||
background-color: #11131f;
|
||||
background-color: var(--active-bg);
|
||||
z-index: 100;
|
||||
position: relative;
|
||||
}
|
||||
@ -144,7 +144,7 @@
|
||||
}
|
||||
|
||||
.loading .bitcoin-block.mined-block {
|
||||
background: #2d3348;
|
||||
background: var(--secondary);
|
||||
}
|
||||
|
||||
@keyframes opacityPulse {
|
||||
|
@ -63,11 +63,11 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
||||
blockPadding: number = 30;
|
||||
|
||||
gradientColors = {
|
||||
'': ['#9339f4', '#105fb0'],
|
||||
liquid: ['#116761', '#183550'],
|
||||
'liquidtestnet': ['#494a4a', '#272e46'],
|
||||
testnet: ['#1d486f', '#183550'],
|
||||
signet: ['#6f1d5d', '#471850'],
|
||||
'': ['var(--mainnet-alt)', 'var(--primary)'],
|
||||
liquid: ['var(--liquid)', 'var(--testnet-alt)'],
|
||||
'liquidtestnet': ['var(--liquidtestnet)', 'var(--liquidtestnet-alt)'],
|
||||
testnet: ['var(--testnet)', 'var(--testnet-alt)'],
|
||||
signet: ['var(--signet)', 'var(--signet-alt)'],
|
||||
};
|
||||
|
||||
constructor(
|
||||
@ -330,7 +330,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
||||
left: addLeft + this.blockOffset * index + 'px',
|
||||
background: `repeating-linear-gradient(
|
||||
#2d3348,
|
||||
#2d3348 ${greenBackgroundHeight}%,
|
||||
var(--secondary) ${greenBackgroundHeight}%,
|
||||
${this.gradientColors[this.network][0]} ${Math.max(greenBackgroundHeight, 0)}%,
|
||||
${this.gradientColors[this.network][1]} 100%
|
||||
)`,
|
||||
@ -366,7 +366,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
||||
|
||||
return {
|
||||
left: addLeft + this.blockOffset * this.emptyBlocks.indexOf(block) + 'px',
|
||||
background: "#2d3348",
|
||||
background: "var(--secondary)",
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@
|
||||
}
|
||||
|
||||
.black-background {
|
||||
background-color: #11131f;
|
||||
background-color: var(--active-bg);
|
||||
z-index: 100;
|
||||
position: relative;
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ tr, td, th {
|
||||
}
|
||||
|
||||
.progress {
|
||||
background-color: #2d3348;
|
||||
background-color: var(--secondary);
|
||||
}
|
||||
|
||||
.pool {
|
||||
@ -266,7 +266,7 @@ tr, td, th {
|
||||
|
||||
.tooltip-custom .tooltiptext {
|
||||
visibility: hidden;
|
||||
color: #fff;
|
||||
color: var(--fg);
|
||||
text-align: center;
|
||||
padding: 5px 0;
|
||||
border-radius: 6px;
|
||||
|
@ -14,7 +14,7 @@
|
||||
height: 100%;
|
||||
|
||||
.face {
|
||||
fill: #11131f;
|
||||
fill: var(--active-bg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,8 +29,8 @@
|
||||
}
|
||||
|
||||
&.hour {
|
||||
fill: #105fb0;
|
||||
stroke: #105fb0;
|
||||
fill: var(--primary);
|
||||
stroke: var(--primary);
|
||||
stroke-width: 6px;
|
||||
}
|
||||
}
|
||||
|
@ -161,7 +161,7 @@
|
||||
}
|
||||
|
||||
.side.bottom {
|
||||
background: #105fb0;
|
||||
background: var(--primary);
|
||||
transform: rotateX(-90deg);
|
||||
margin-top: var(--half-side);
|
||||
}
|
||||
|
@ -40,7 +40,7 @@
|
||||
<h5 class="card-title" i18n="difficulty-box.current-period">Current Period</h5>
|
||||
<div class="card-text">{{ epochData.progress | number: '1.2-2' }} <span class="symbol">%</span></div>
|
||||
<div class="progress small-bar">
|
||||
<div class="progress-bar" role="progressbar" style="width: 15%; background-color: #105fb0" [ngStyle]="{'width': epochData.base}"> </div>
|
||||
<div class="progress-bar" role="progressbar" style="width: 15%; background-color: var(--primary)" [ngStyle]="{'width': epochData.base}"> </div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item" *ngIf="showHalving">
|
||||
|
@ -79,12 +79,12 @@
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #1d1f31;
|
||||
background-color: var(--bg);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
color: #4a68b9;
|
||||
color: var(--title-fg);
|
||||
font-size: 1rem;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@ -94,7 +94,7 @@
|
||||
.progress {
|
||||
display: inline-flex;
|
||||
width: 100%;
|
||||
background-color: #2d3348;
|
||||
background-color: var(--secondary);
|
||||
height: 1.1rem;
|
||||
max-width: 180px;
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
justify-content: space-around;
|
||||
height: 50.5px;
|
||||
.shared-block {
|
||||
color: #ffffff66;
|
||||
color: var(--transparent-fg);
|
||||
font-size: 12px;
|
||||
}
|
||||
.item {
|
||||
@ -91,19 +91,19 @@
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #1d1f31;
|
||||
background-color: var(--bg);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
color: #4a68b9;
|
||||
color: var(--title-fg);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.progress {
|
||||
display: inline-flex;
|
||||
width: 100%;
|
||||
background-color: #2d3348;
|
||||
background-color: var(--secondary);
|
||||
height: 1.1rem;
|
||||
max-width: 180px;
|
||||
}
|
||||
@ -177,10 +177,10 @@
|
||||
.epoch-blocks {
|
||||
display: block;
|
||||
width: 100%;
|
||||
background: #2d3348;
|
||||
background: var(--secondary);
|
||||
|
||||
.rect {
|
||||
fill: #2d3348;
|
||||
fill: var(--secondary);
|
||||
|
||||
&.behind {
|
||||
fill: #D81B60;
|
||||
@ -189,7 +189,7 @@
|
||||
fill: url(#diff-gradient);
|
||||
}
|
||||
&.ahead {
|
||||
fill: #1a9436;
|
||||
fill: var(--success);
|
||||
}
|
||||
|
||||
&.hover {
|
||||
@ -223,12 +223,12 @@
|
||||
height: 100%;
|
||||
}
|
||||
.background {
|
||||
background: linear-gradient(to right, #105fb0, #9339f4);
|
||||
background: linear-gradient(to right, var(--primary), #9339f4);
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
.remaining {
|
||||
background: #2d3348;
|
||||
background: var(--secondary);
|
||||
right: 0;
|
||||
}
|
||||
.label {
|
||||
|
@ -82,24 +82,24 @@ export class DifficultyComponent implements OnInit {
|
||||
.pipe(
|
||||
map(([blocks, da]) => {
|
||||
const maxHeight = blocks.reduce((max, block) => Math.max(max, block.height), 0);
|
||||
let colorAdjustments = '#ffffff66';
|
||||
let colorAdjustments = 'var(--transparent-fg)';
|
||||
if (da.difficultyChange > 0) {
|
||||
colorAdjustments = '#3bcc49';
|
||||
colorAdjustments = 'var(--green)';
|
||||
}
|
||||
if (da.difficultyChange < 0) {
|
||||
colorAdjustments = '#dc3545';
|
||||
colorAdjustments = 'var(--red)';
|
||||
}
|
||||
|
||||
let colorPreviousAdjustments = '#dc3545';
|
||||
let colorPreviousAdjustments = 'var(--red)';
|
||||
if (da.previousRetarget) {
|
||||
if (da.previousRetarget >= 0) {
|
||||
colorPreviousAdjustments = '#3bcc49';
|
||||
colorPreviousAdjustments = 'var(--green)';
|
||||
}
|
||||
if (da.previousRetarget === 0) {
|
||||
colorPreviousAdjustments = '#ffffff66';
|
||||
colorPreviousAdjustments = 'var(--transparent-fg)';
|
||||
}
|
||||
} else {
|
||||
colorPreviousAdjustments = '#ffffff66';
|
||||
colorPreviousAdjustments = 'var(--transparent-fg)';
|
||||
}
|
||||
|
||||
const blocksUntilHalving = 210000 - (maxHeight % 210000);
|
||||
|
@ -128,7 +128,7 @@ export class FeeDistributionGraphComponent implements OnInit, OnChanges, OnDestr
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: 'dotted',
|
||||
color: '#ffffff66',
|
||||
color: 'var(--transparent-fg)',
|
||||
opacity: 0.25,
|
||||
}
|
||||
},
|
||||
|
@ -1,5 +1,5 @@
|
||||
.card-title {
|
||||
color: #4a68b9;
|
||||
color: var(--title-fg);
|
||||
font-size: 10px;
|
||||
margin-bottom: 4px;
|
||||
font-size: 1rem;
|
||||
@ -36,7 +36,7 @@
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.card-text span {
|
||||
color: #ffffff66;
|
||||
color: var(--transparent-fg);
|
||||
font-size: 12px;
|
||||
top: 0px;
|
||||
}
|
||||
@ -79,6 +79,7 @@
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
transition: background-color 1s;
|
||||
color: var(--color-fg);
|
||||
&.priority {
|
||||
@media (767px < width < 992px), (width < 576px) {
|
||||
width: 100%;
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { Component, OnInit, ChangeDetectionStrategy, OnDestroy, ChangeDetectorRef } from '@angular/core';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { Observable, combineLatest } from 'rxjs';
|
||||
import { Observable, combineLatest, Subscription } 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 '../../services/theme.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-fees-box',
|
||||
@ -11,14 +12,18 @@ import { map, startWith, tap } from 'rxjs/operators';
|
||||
styleUrls: ['./fees-box.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class FeesBoxComponent implements OnInit {
|
||||
export class FeesBoxComponent implements OnInit, OnDestroy {
|
||||
isLoading$: Observable<boolean>;
|
||||
recommendedFees$: Observable<Recommendedfees>;
|
||||
themeSubscription: Subscription;
|
||||
gradient = 'linear-gradient(to right, #2e324e, #2e324e)';
|
||||
noPriority = '#2e324e';
|
||||
fees: Recommendedfees;
|
||||
|
||||
constructor(
|
||||
private stateService: StateService
|
||||
private stateService: StateService,
|
||||
private themeService: ThemeService,
|
||||
private cd: ChangeDetectorRef,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
@ -31,18 +36,32 @@ export class FeesBoxComponent implements OnInit {
|
||||
this.recommendedFees$ = this.stateService.recommendedFees$
|
||||
.pipe(
|
||||
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]);
|
||||
|
||||
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]);
|
||||
|
||||
this.gradient = `linear-gradient(to right, ${startColor}, ${endColor})`;
|
||||
this.noPriority = startColor;
|
||||
this.fees = fees;
|
||||
this.setFeeGradient();
|
||||
}
|
||||
)
|
||||
);
|
||||
this.themeSubscription = this.themeService.themeChanged$.subscribe(() => {
|
||||
this.setFeeGradient();
|
||||
})
|
||||
}
|
||||
|
||||
setFeeGradient() {
|
||||
let feeLevelIndex = feeLevels.slice().reverse().findIndex((feeLvl) => this.fees.minimumFee >= feeLvl);
|
||||
feeLevelIndex = feeLevelIndex >= 0 ? feeLevels.length - feeLevelIndex : feeLevelIndex;
|
||||
const startColor = '#' + (this.themeService.mempoolFeeColors[feeLevelIndex - 1] || this.themeService.mempoolFeeColors[this.themeService.mempoolFeeColors.length - 1]);
|
||||
|
||||
feeLevelIndex = feeLevels.slice().reverse().findIndex((feeLvl) => this.fees.fastestFee >= feeLvl);
|
||||
feeLevelIndex = feeLevelIndex >= 0 ? feeLevels.length - feeLevelIndex : feeLevelIndex;
|
||||
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;
|
||||
|
||||
this.cd.markForCheck();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.themeSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
background-color: #1d1f31;
|
||||
background-color: var(--bg);
|
||||
box-shadow: 15px 15px 15px 15px #000;
|
||||
z-index: 10;
|
||||
|
||||
@ -40,16 +40,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
.txPerSecond {
|
||||
color: #4a9ff4;
|
||||
}
|
||||
|
||||
.mempoolSize {
|
||||
color: #4a68b9;
|
||||
}
|
||||
|
||||
.unconfirmedTx {
|
||||
color: #f14d80;
|
||||
color: var(--title-fg);
|
||||
}
|
||||
|
||||
.info-block {
|
||||
@ -61,7 +53,7 @@
|
||||
.progress {
|
||||
display: inline-flex;
|
||||
width: 160px;
|
||||
background-color: #2d3348;
|
||||
background-color: var(--secondary);
|
||||
height: 1.1rem;
|
||||
}
|
||||
|
||||
|
@ -94,12 +94,12 @@
|
||||
}
|
||||
.card-title {
|
||||
font-size: 1rem;
|
||||
color: #4a68b9;
|
||||
color: var(--title-fg);
|
||||
}
|
||||
.card-text {
|
||||
font-size: 18px;
|
||||
span {
|
||||
color: #ffffff66;
|
||||
color: var(--transparent-fg);
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
@ -242,7 +242,7 @@ export class HashrateChartComponent implements OnInit {
|
||||
borderRadius: 4,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
textStyle: {
|
||||
color: '#b1b1b1',
|
||||
color: 'var(--tooltip-grey)',
|
||||
align: 'left',
|
||||
},
|
||||
borderColor: '#000',
|
||||
@ -354,7 +354,7 @@ export class HashrateChartComponent implements OnInit {
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: 'dotted',
|
||||
color: '#ffffff66',
|
||||
color: 'var(--transparent-fg)',
|
||||
opacity: 0.25,
|
||||
}
|
||||
},
|
||||
@ -472,7 +472,7 @@ export class HashrateChartComponent implements OnInit {
|
||||
const now = new Date();
|
||||
// @ts-ignore
|
||||
this.chartOptions.grid.bottom = 30;
|
||||
this.chartOptions.backgroundColor = '#11131f';
|
||||
this.chartOptions.backgroundColor = 'var(--active-bg)';
|
||||
this.chartInstance.setOption(this.chartOptions);
|
||||
download(this.chartInstance.getDataURL({
|
||||
pixelRatio: 2,
|
||||
|
@ -225,7 +225,7 @@ export class HashrateChartPoolsComponent implements OnInit {
|
||||
borderRadius: 4,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
textStyle: {
|
||||
color: '#b1b1b1',
|
||||
color: 'var(--tooltip-grey)',
|
||||
align: 'left',
|
||||
},
|
||||
borderColor: '#000',
|
||||
@ -308,7 +308,7 @@ export class HashrateChartPoolsComponent implements OnInit {
|
||||
const now = new Date();
|
||||
// @ts-ignore
|
||||
this.chartOptions.grid.bottom = 30;
|
||||
this.chartOptions.backgroundColor = '#11131f';
|
||||
this.chartOptions.backgroundColor = 'var(--active-bg)';
|
||||
this.chartInstance.setOption(this.chartOptions);
|
||||
download(this.chartInstance.getDataURL({
|
||||
pixelRatio: 2,
|
||||
|
@ -272,7 +272,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges, On
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: 'dotted',
|
||||
color: '#ffffff66',
|
||||
color: 'var(--transparent-fg)',
|
||||
opacity: 0.25,
|
||||
}
|
||||
}
|
||||
@ -332,7 +332,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges, On
|
||||
const now = new Date();
|
||||
// @ts-ignore
|
||||
this.mempoolStatsChartOption.grid.height = prevHeight + 20;
|
||||
this.mempoolStatsChartOption.backgroundColor = '#11131f';
|
||||
this.mempoolStatsChartOption.backgroundColor = 'var(--active-bg)';
|
||||
this.chartInstance.setOption(this.mempoolStatsChartOption);
|
||||
download(this.chartInstance.getDataURL({
|
||||
pixelRatio: 2,
|
||||
|
@ -7,7 +7,7 @@
|
||||
}
|
||||
|
||||
li.nav-item.active {
|
||||
background-color: #653b9c;
|
||||
background-color: var(--tertiary);
|
||||
}
|
||||
|
||||
fa-icon {
|
||||
@ -47,7 +47,7 @@ li.nav-item {
|
||||
}
|
||||
|
||||
.navbar-nav {
|
||||
background: #212121;
|
||||
background: var(--navbar-bg);
|
||||
bottom: 0;
|
||||
box-shadow: 0px 0px 15px 0px #000;
|
||||
flex-direction: row;
|
||||
@ -91,6 +91,10 @@ li.nav-item {
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-container {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
nav {
|
||||
box-shadow: 0px 0px 15px 0px #000;
|
||||
}
|
||||
@ -108,23 +112,23 @@ nav {
|
||||
}
|
||||
|
||||
.mainnet.active {
|
||||
background-color: #653b9c;
|
||||
background-color: var(--tertiary);
|
||||
}
|
||||
|
||||
.liquid.active {
|
||||
background-color: #116761;
|
||||
background-color: var(--liquid);
|
||||
}
|
||||
|
||||
.liquidtestnet.active {
|
||||
background-color: #494a4a;
|
||||
background-color: var(--liquidtestnet);
|
||||
}
|
||||
|
||||
.testnet.active {
|
||||
background-color: #1d486f;
|
||||
background-color: var(--testnet);
|
||||
}
|
||||
|
||||
.signet.active {
|
||||
background-color: #6f1d5d;
|
||||
background-color: var(--signet);
|
||||
}
|
||||
|
||||
.dropdown-divider {
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
<div class="item">
|
||||
<a class="title-link" [routerLink]="['/audit/wallet/utxos' | relativeUrl]" [fragment]="'expired'">
|
||||
<h5 class="card-title"><ng-container i18n="liquid.total-expired">Total Expired</ng-container> <fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="font-size: 13px; color: #4a68b9"></fa-icon></h5>
|
||||
<h5 class="card-title"><ng-container i18n="liquid.total-expired">Total Expired</ng-container> <fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="font-size: 13px; color: var(--title-fg)"></fa-icon></h5>
|
||||
</a>
|
||||
<div *ngIf="(stats$ | async) as expiredStats; else loadingData" class="card-text">
|
||||
<div class="fee-text" i18n-ngbTooltip="liquid.expired-utxos" ngbTooltip="Total amount of BTC held in Federation UTXOs that have expired timelocks" placement="top">{{ (+expiredStats.all.total) / 100000000 | number: '1.5-5' }} <span style="color: #b86d12;">BTC</span></div>
|
||||
|
@ -14,7 +14,7 @@
|
||||
}
|
||||
|
||||
.card-title {
|
||||
color: #4a68b9;
|
||||
color: var(--title-fg);
|
||||
font-size: 10px;
|
||||
margin-bottom: 4px;
|
||||
font-size: 1rem;
|
||||
|
@ -33,7 +33,7 @@ tr, td, th {
|
||||
}
|
||||
|
||||
.progress {
|
||||
background-color: #2d3348;
|
||||
background-color: var(--secondary);
|
||||
}
|
||||
|
||||
.address {
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div class="fee-estimation-container">
|
||||
<div class="item">
|
||||
<a class="title-link" [routerLink]="['/audit/wallet/addresses' | relativeUrl]">
|
||||
<h5 class="card-title"><ng-container i18n="liquid.federation-wallet">Liquid Federation Wallet</ng-container> <fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="font-size: 13px; color: #4a68b9"></fa-icon></h5>
|
||||
<h5 class="card-title"><ng-container i18n="liquid.federation-wallet">Liquid Federation Wallet</ng-container> <fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="font-size: 13px; color: var(--title-fg)"></fa-icon></h5>
|
||||
</a>
|
||||
<div class="card-text">
|
||||
<div class="fee-text">{{ federationWalletStats.address_count }} <span i18n="shared.addresses">addresses</span></div>
|
||||
@ -16,7 +16,7 @@
|
||||
<div class="fee-estimation-container loading-container">
|
||||
<div class="item">
|
||||
<a class="title-link" [routerLink]="['/audit/wallet/addresses' | relativeUrl]">
|
||||
<h5 class="card-title"><ng-container i18n="liquid.federation-wallet">Liquid Federation Wallet</ng-container> <fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="font-size: 13px; color: #4a68b9"></fa-icon></h5>
|
||||
<h5 class="card-title"><ng-container i18n="liquid.federation-wallet">Liquid Federation Wallet</ng-container> <fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="font-size: 13px; color: var(--title-fg)"></fa-icon></h5>
|
||||
</a>
|
||||
<div class="card-text">
|
||||
<div class="skeleton-loader"></div>
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
.card-title {
|
||||
margin: 0;
|
||||
color: #4a68b9;
|
||||
color: var(--title-fg);
|
||||
font-size: 10px;
|
||||
font-size: 1rem;
|
||||
white-space: nowrap;
|
||||
|
@ -24,7 +24,7 @@ tr, td, th {
|
||||
}
|
||||
|
||||
.progress {
|
||||
background-color: #2d3348;
|
||||
background-color: var(--secondary);
|
||||
}
|
||||
|
||||
.txid {
|
||||
|
@ -27,7 +27,7 @@ tr, td, th {
|
||||
}
|
||||
|
||||
.progress {
|
||||
background-color: #2d3348;
|
||||
background-color: var(--secondary);
|
||||
}
|
||||
|
||||
.transaction {
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div class="fee-estimation-container">
|
||||
<div class="item">
|
||||
<a class="title-link" [routerLink]="['/audit/pegs' | relativeUrl]">
|
||||
<h5 class="card-title"><ng-container i18n="liquid.recent-pegs">Recent Peg-In / Out's</ng-container> <fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="font-size: 13px; color: #4a68b9"></fa-icon></h5>
|
||||
<h5 class="card-title"><ng-container i18n="liquid.recent-pegs">Recent Peg-In / Out's</ng-container> <fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="font-size: 13px; color: var(--title-fg)"></fa-icon></h5>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@ -26,7 +26,7 @@
|
||||
<div class="fee-estimation-container loading-container">
|
||||
<div class="item">
|
||||
<a class="title-link" [routerLink]="['/audit/pegs' | relativeUrl]">
|
||||
<h5 class="card-title"><ng-container i18n="liquid.recent-pegs">Recent Peg-In / Out's</ng-container> <fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="font-size: 13px; color: #4a68b9"></fa-icon></h5>
|
||||
<h5 class="card-title"><ng-container i18n="liquid.recent-pegs">Recent Peg-In / Out's</ng-container> <fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="font-size: 13px; color: var(--title-fg)"></fa-icon></h5>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
.card-title {
|
||||
margin: 0;
|
||||
color: #4a68b9;
|
||||
color: var(--title-fg);
|
||||
font-size: 10px;
|
||||
font-size: 1rem;
|
||||
white-space: nowrap;
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
<div class="item">
|
||||
<!-- <a class="title-link" [routerLink]="['/audit/emergency-spends' | relativeUrl]">
|
||||
<h5 class="card-title"><ng-container i18n="liquid.forfeited-utxos">Forfeited UTXOs</ng-container> <fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="font-size: 13px; color: #4a68b9"></fa-icon></h5>
|
||||
<h5 class="card-title"><ng-container i18n="liquid.forfeited-utxos">Forfeited UTXOs</ng-container> <fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="font-size: 13px; color: var(--title-fg)"></fa-icon></h5>
|
||||
</a> -->
|
||||
<h5 class="card-title" i18n="liquid.emergency-keys">Emergency Keys</h5>
|
||||
<div *ngIf="(emergencyUtxosStats$ | async) as emergencyUtxosStats; else loadingData" class="card-text">
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
.card-title {
|
||||
margin-bottom: 4px;
|
||||
color: #4a68b9;
|
||||
color: var(--title-fg);
|
||||
font-size: 10px;
|
||||
font-size: 1rem;
|
||||
white-space: nowrap;
|
||||
|
@ -141,7 +141,7 @@ export class ReservesRatioComponent implements OnInit, OnChanges {
|
||||
show: true,
|
||||
offsetCenter: [0, '-127%'],
|
||||
fontSize: 18,
|
||||
color: '#4a68b9',
|
||||
color: 'var(--title-fg)',
|
||||
fontFamily: 'inherit',
|
||||
fontWeight: 500,
|
||||
},
|
||||
|
@ -13,7 +13,7 @@
|
||||
}
|
||||
|
||||
.card-title {
|
||||
color: #4a68b9;
|
||||
color: var(--title-fg);
|
||||
font-size: 10px;
|
||||
margin-bottom: 4px;
|
||||
font-size: 1rem;
|
||||
|
@ -18,7 +18,7 @@
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background: #11131f;
|
||||
background: var(--active-bg);
|
||||
text-align: start;
|
||||
font-size: 1.8em;
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
}
|
||||
|
||||
li.nav-item.active {
|
||||
background-color: #653b9c;
|
||||
background-color: var(--tertiary);
|
||||
}
|
||||
|
||||
fa-icon {
|
||||
@ -58,7 +58,7 @@ li.nav-item {
|
||||
}
|
||||
|
||||
.navbar-nav {
|
||||
background: #212121;
|
||||
background: var(--navbar-bg);
|
||||
bottom: 0;
|
||||
box-shadow: 0px 0px 15px 0px #000;
|
||||
flex-direction: row;
|
||||
@ -139,23 +139,23 @@ nav {
|
||||
}
|
||||
|
||||
.mainnet.active {
|
||||
background-color: #653b9c;
|
||||
background-color: var(--tertiary);
|
||||
}
|
||||
|
||||
.liquid.active {
|
||||
background-color: #116761;
|
||||
background-color: var(--liquid);
|
||||
}
|
||||
|
||||
.liquidtestnet.active {
|
||||
background-color: #494a4a;
|
||||
background-color: var(--liquidtestnet);
|
||||
}
|
||||
|
||||
.testnet.active {
|
||||
background-color: #1d486f;
|
||||
background-color: var(--testnet);
|
||||
}
|
||||
|
||||
.signet.active {
|
||||
background-color: #6f1d5d;
|
||||
background-color: var(--signet);
|
||||
}
|
||||
|
||||
.dropdown-divider {
|
||||
|
@ -1,5 +1,5 @@
|
||||
.progress {
|
||||
background-color: #2d3348;
|
||||
background-color: var(--secondary);
|
||||
position: relative;
|
||||
top: 5px;
|
||||
}
|
||||
|
@ -106,7 +106,7 @@
|
||||
}
|
||||
|
||||
.black-background {
|
||||
background-color: #11131f;
|
||||
background-color: var(--active-bg);
|
||||
z-index: 100;
|
||||
position: relative;
|
||||
}
|
||||
|
@ -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 '../../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) => {
|
||||
|
@ -432,7 +432,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: 'dotted',
|
||||
color: '#ffffff66',
|
||||
color: 'var(--transparent-fg)',
|
||||
opacity: 0.25,
|
||||
}
|
||||
}
|
||||
@ -500,7 +500,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
const now = new Date();
|
||||
// @ts-ignore
|
||||
this.mempoolVsizeFeesOptions.grid.height = prevHeight + 20;
|
||||
this.mempoolVsizeFeesOptions.backgroundColor = '#11131f';
|
||||
this.mempoolVsizeFeesOptions.backgroundColor = 'var(--active-bg)';
|
||||
this.chartInstance.setOption(this.mempoolVsizeFeesOptions);
|
||||
download(this.chartInstance.getDataURL({
|
||||
pixelRatio: 2,
|
||||
|
@ -34,7 +34,7 @@
|
||||
|
||||
.sidenav.open {
|
||||
display: block;
|
||||
background-color: #1d1f31;
|
||||
background-color: var(--bg);
|
||||
}
|
||||
|
||||
:host-context(.ltr-layout) .sidenav.open {
|
||||
@ -56,7 +56,7 @@
|
||||
.sidenav nav {
|
||||
width: 100%;
|
||||
height: calc(100vh - 65px);
|
||||
background-color: #1d1f31;
|
||||
background-color: var(--bg);
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
padding-top: 20px;
|
||||
@ -75,7 +75,7 @@
|
||||
}
|
||||
|
||||
.badge-og {
|
||||
background-color: #4a68b9;
|
||||
background-color: var(--title-fg);
|
||||
}
|
||||
|
||||
.badge-pleb {
|
||||
@ -87,7 +87,7 @@
|
||||
}
|
||||
|
||||
.badge-whale {
|
||||
background-color: #653b9c;
|
||||
background-color: var(--tertiary);
|
||||
}
|
||||
|
||||
.badge-silver {
|
||||
@ -99,5 +99,5 @@
|
||||
}
|
||||
|
||||
.badge-platinum {
|
||||
background-color: #653b9c;
|
||||
background-color: var(--tertiary);
|
||||
}
|
@ -55,7 +55,7 @@
|
||||
<a class="title-link" href="" [routerLink]="['/blocks' | relativeUrl]">
|
||||
<h5 class="card-title d-inline" i18n="dashboard.recent-blocks">Recent Blocks</h5>
|
||||
<span> </span>
|
||||
<fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="vertical-align: text-top; font-size: 13px; color: #4a68b9"></fa-icon>
|
||||
<fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="vertical-align: text-top; font-size: 13px; color: var(--title-fg)"></fa-icon>
|
||||
</a>
|
||||
<app-blocks-list [attr.data-cy]="'latest-blocks'" [widget]=true></app-blocks-list>
|
||||
</div>
|
||||
@ -69,7 +69,7 @@
|
||||
<a class="title-link" href="" [routerLink]="['/graphs/mining/hashrate-difficulty' | relativeUrl]">
|
||||
<h5 class="card-title d-inline" i18n="dashboard.adjustments">Adjustments</h5>
|
||||
<span> </span>
|
||||
<fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="vertical-align: text-top; font-size: 13px; color: #4a68b9"></fa-icon>
|
||||
<fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="vertical-align: text-top; font-size: 13px; color: var(--title-fg)"></fa-icon>
|
||||
</a>
|
||||
<app-difficulty-adjustments-table [attr.data-cy]="'difficulty-adjustments-table'"></app-difficulty-adjustments-table>
|
||||
</div>
|
||||
|
@ -7,7 +7,7 @@
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #1d1f31;
|
||||
background-color: var(--bg);
|
||||
}
|
||||
|
||||
.graph-card {
|
||||
@ -32,10 +32,10 @@
|
||||
|
||||
.card-title {
|
||||
font-size: 1rem;
|
||||
color: #4a68b9;
|
||||
color: var(--title-fg);
|
||||
}
|
||||
.card-title > a {
|
||||
color: #4a68b9;
|
||||
color: var(--title-fg);
|
||||
}
|
||||
|
||||
.card-body.pool-ranking {
|
||||
|
@ -99,7 +99,7 @@
|
||||
}
|
||||
.card-title {
|
||||
font-size: 1rem;
|
||||
color: #4a68b9;
|
||||
color: var(--title-fg);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
@ -107,7 +107,7 @@
|
||||
.card-text {
|
||||
font-size: 18px;
|
||||
span {
|
||||
color: #ffffff66;
|
||||
color: var(--transparent-fg);
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ export class PoolRankingComponent implements OnInit {
|
||||
name: pool.name + ((isMobile() || this.widget) ? `` : ` (${pool.share}%)`),
|
||||
label: {
|
||||
overflow: 'none',
|
||||
color: '#b1b1b1',
|
||||
color: 'var(--tooltip-grey)',
|
||||
alignTo: 'edge',
|
||||
edgeDistance: edgeDistance,
|
||||
},
|
||||
@ -156,7 +156,7 @@ export class PoolRankingComponent implements OnInit {
|
||||
borderRadius: 4,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
textStyle: {
|
||||
color: '#b1b1b1',
|
||||
color: 'var(--tooltip-grey)',
|
||||
},
|
||||
borderColor: '#000',
|
||||
formatter: () => {
|
||||
@ -186,7 +186,7 @@ export class PoolRankingComponent implements OnInit {
|
||||
name: $localize`Other (${percentage})`,
|
||||
label: {
|
||||
overflow: 'none',
|
||||
color: '#b1b1b1',
|
||||
color: 'var(--tooltip-grey)',
|
||||
alignTo: 'edge',
|
||||
edgeDistance: edgeDistance
|
||||
},
|
||||
@ -195,7 +195,7 @@ export class PoolRankingComponent implements OnInit {
|
||||
borderRadius: 4,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
textStyle: {
|
||||
color: '#b1b1b1',
|
||||
color: 'var(--tooltip-grey)',
|
||||
},
|
||||
borderColor: '#000',
|
||||
formatter: () => {
|
||||
@ -306,7 +306,7 @@ export class PoolRankingComponent implements OnInit {
|
||||
|
||||
onSaveChart() {
|
||||
const now = new Date();
|
||||
this.chartOptions.backgroundColor = '#11131f';
|
||||
this.chartOptions.backgroundColor = 'var(--active-bg)';
|
||||
this.chartInstance.setOption(this.chartOptions);
|
||||
download(this.chartInstance.getDataURL({
|
||||
pixelRatio: 2,
|
||||
|
@ -17,7 +17,7 @@
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
margin-left: 15px;
|
||||
background: #181b2d;
|
||||
background: var(--stat-box-bg);
|
||||
padding: 0.75rem;
|
||||
width: 0;
|
||||
flex-grow: 1;
|
||||
@ -43,7 +43,7 @@
|
||||
.chart {
|
||||
width: 100%;
|
||||
height: 315px;
|
||||
background: #181b2d;
|
||||
background: var(--stat-box-bg);
|
||||
}
|
||||
|
||||
.row {
|
||||
@ -65,7 +65,7 @@
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
background: #24273e;
|
||||
background: var(--box-bg);
|
||||
|
||||
&.noimg {
|
||||
opacity: 0;
|
||||
|
@ -88,7 +88,7 @@ div.scrollable {
|
||||
}
|
||||
|
||||
.progress {
|
||||
background-color: #2d3348;
|
||||
background-color: var(--secondary);
|
||||
}
|
||||
|
||||
.coinbase {
|
||||
@ -190,7 +190,7 @@ div.scrollable {
|
||||
}
|
||||
|
||||
.data-title {
|
||||
color: #4a68b9;
|
||||
color: var(--title-fg);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
|
@ -175,7 +175,7 @@ export class PoolComponent implements OnInit {
|
||||
borderRadius: 4,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
textStyle: {
|
||||
color: '#b1b1b1',
|
||||
color: 'var(--tooltip-grey)',
|
||||
align: 'left',
|
||||
},
|
||||
borderColor: '#000',
|
||||
|
@ -25,7 +25,7 @@
|
||||
}
|
||||
|
||||
.timeline-wrapper.mined {
|
||||
border: solid 4px #1a9436;
|
||||
border: solid 4px var(--success);
|
||||
}
|
||||
|
||||
.no-replacements {
|
||||
|
@ -15,12 +15,12 @@
|
||||
|
||||
&::before {
|
||||
left: 0;
|
||||
background: linear-gradient(to right, #24273e, #24273e, transparent);
|
||||
background: linear-gradient(to right, var(--box-bg), var(--box-bg), transparent);
|
||||
}
|
||||
|
||||
&::after {
|
||||
right: 0;
|
||||
background: linear-gradient(to left, #24273e, #24273e, transparent);
|
||||
background: linear-gradient(to left, var(--box-bg), var(--box-bg), transparent);
|
||||
}
|
||||
|
||||
.timeline-wrapper {
|
||||
@ -45,7 +45,7 @@
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
top: -70px;
|
||||
background: linear-gradient(to bottom, rgba(36, 39, 62, 0) 0%, rgba(36, 39, 62, 1) 100%);
|
||||
background: linear-gradient(to bottom, var(--fade-out-box-bg-start), var(--fade-out-box-bg-end));
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
@ -101,7 +101,7 @@
|
||||
right: -5px;
|
||||
top: 0;
|
||||
transform: translateY(-50%);
|
||||
background: #105fb0;
|
||||
background: var(--primary);
|
||||
border-radius: 5px;
|
||||
|
||||
&.left {
|
||||
@ -112,7 +112,7 @@
|
||||
}
|
||||
|
||||
&.fullrbf {
|
||||
background: #1bd8f4;
|
||||
background: var(--info);
|
||||
}
|
||||
}
|
||||
&.first-node {
|
||||
@ -165,20 +165,20 @@
|
||||
|
||||
&.mined {
|
||||
.shape-border {
|
||||
background: #1a9436;
|
||||
background: var(--success);
|
||||
}
|
||||
}
|
||||
|
||||
.shape-border:hover {
|
||||
padding: 0px;
|
||||
.shape {
|
||||
background: #1bd8f4;
|
||||
background: var(--info);
|
||||
}
|
||||
}
|
||||
|
||||
&.selected.mined {
|
||||
.shape-border {
|
||||
background: #1a9436;
|
||||
background: var(--success);
|
||||
height: calc(1em + 16px);
|
||||
width: calc(1em + 16px);
|
||||
|
||||
@ -190,7 +190,7 @@
|
||||
padding: 4px;
|
||||
.shape {
|
||||
border-width: 1px;
|
||||
border-color: #1bd8f4
|
||||
border-color: var(--info)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -207,9 +207,9 @@
|
||||
width: 20px;
|
||||
height: 108px;
|
||||
bottom: 50%;
|
||||
border-right: solid 10px #105fb0;
|
||||
border-right: solid 10px var(--primary);
|
||||
&.fullrbf {
|
||||
border-right: solid 10px #1bd8f4;
|
||||
border-right: solid 10px var(--info);
|
||||
}
|
||||
&.last-pipe {
|
||||
height: 150px;
|
||||
@ -218,10 +218,10 @@
|
||||
}
|
||||
|
||||
.corner {
|
||||
border-bottom: solid 10px #105fb0;
|
||||
border-bottom: solid 10px var(--primary);
|
||||
border-bottom-right-radius: 10px;
|
||||
&.fullrbf {
|
||||
border-bottom: solid 10px #1bd8f4;
|
||||
border-bottom: solid 10px var(--info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
.card-title {
|
||||
color: #4a68b9;
|
||||
color: var(--title-fg);
|
||||
font-size: 10px;
|
||||
margin-bottom: 4px;
|
||||
font-size: 1rem;
|
||||
@ -50,7 +50,7 @@
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.card-text span {
|
||||
color: #ffffff66;
|
||||
color: var(--transparent-fg);
|
||||
font-size: 12px;
|
||||
top: 0px;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
.card-title {
|
||||
color: #4a68b9;
|
||||
color: var(--title-fg);
|
||||
font-size: 10px;
|
||||
margin-bottom: 4px;
|
||||
font-size: 1rem;
|
||||
|
@ -32,7 +32,7 @@
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
transition: opacity 500ms;
|
||||
background: radial-gradient(#1d1f31 0%, transparent 50%);
|
||||
background: radial-gradient(var(--bg) 0%, transparent 50%);
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
|
@ -117,7 +117,7 @@
|
||||
}
|
||||
.inactive {
|
||||
.square {
|
||||
background-color: #ffffff66 !important;
|
||||
background-color: var(--transparent-fg) !important;
|
||||
}
|
||||
.fee-text {
|
||||
text-decoration: line-through;
|
||||
|
@ -0,0 +1,6 @@
|
||||
<div [formGroup]="themeForm" class="text-small text-center">
|
||||
<select formControlName="theme" class="custom-select custom-select-sm form-control-secondary form-control mx-auto" (change)="changeTheme()">
|
||||
<option value="default" i18n="theme.mempool-theme">Classic</option>
|
||||
<option value="contrast" i18n="theme.high-contrast">BlueMatt</option>
|
||||
</select>
|
||||
</div>
|
@ -0,0 +1,3 @@
|
||||
.custom-select {
|
||||
width: 100px;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user