diff --git a/backend/mempool-config.sample.json b/backend/mempool-config.sample.json index ce403e943..7ad25dff0 100644 --- a/backend/mempool-config.sample.json +++ b/backend/mempool-config.sample.json @@ -27,6 +27,7 @@ "AUTOMATIC_POOLS_UPDATE": false, "POOLS_JSON_URL": "https://raw.githubusercontent.com/mempool/mining-pools/master/pools-v2.json", "POOLS_JSON_TREE_URL": "https://api.github.com/repos/mempool/mining-pools/git/trees/master", + "POOLS_UPDATE_DELAY": 604800, "AUDIT": false, "RUST_GBT": true, "LIMIT_GBT": false, @@ -45,7 +46,8 @@ "PASSWORD": "mempool", "TIMEOUT": 60000, "COOKIE": false, - "COOKIE_PATH": "/path/to/bitcoin/.cookie" + "COOKIE_PATH": "/path/to/bitcoin/.cookie", + "DEBUG_LOG_PATH": "/path/to/bitcoin/debug.log" }, "ELECTRUM": { "HOST": "127.0.0.1", diff --git a/backend/package-lock.json b/backend/package-lock.json index 7696eddd6..e0d28bfc9 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -16,7 +16,7 @@ "axios": "1.7.2", "bitcoinjs-lib": "~6.1.3", "crypto-js": "~4.2.0", - "express": "~4.21.0", + "express": "~4.21.1", "maxmind": "~4.3.11", "mysql2": "~3.11.0", "redis": "^4.7.0", @@ -2827,9 +2827,9 @@ "dev": true }, "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "engines": { "node": ">= 0.6" } @@ -3461,16 +3461,16 @@ } }, "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -9865,9 +9865,9 @@ "dev": true }, "cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==" + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==" }, "cookie-signature": { "version": "1.0.6", @@ -10319,16 +10319,16 @@ } }, "express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", diff --git a/backend/package.json b/backend/package.json index c18974021..9ac3f9199 100644 --- a/backend/package.json +++ b/backend/package.json @@ -45,7 +45,7 @@ "axios": "1.7.2", "bitcoinjs-lib": "~6.1.3", "crypto-js": "~4.2.0", - "express": "~4.21.0", + "express": "~4.21.1", "maxmind": "~4.3.11", "mysql2": "~3.11.0", "rust-gbt": "file:./rust-gbt", diff --git a/backend/src/__fixtures__/mempool-config.template.json b/backend/src/__fixtures__/mempool-config.template.json index 3796b7f22..a9f246767 100644 --- a/backend/src/__fixtures__/mempool-config.template.json +++ b/backend/src/__fixtures__/mempool-config.template.json @@ -28,6 +28,7 @@ "INDEXING_BLOCKS_AMOUNT": 14, "POOLS_JSON_TREE_URL": "__MEMPOOL_POOLS_JSON_TREE_URL__", "POOLS_JSON_URL": "__MEMPOOL_POOLS_JSON_URL__", + "POOLS_UPDATE_DELAY": 604800, "AUDIT": true, "RUST_GBT": false, "LIMIT_GBT": false, @@ -46,7 +47,8 @@ "PASSWORD": "__CORE_RPC_PASSWORD__", "TIMEOUT": 1000, "COOKIE": false, - "COOKIE_PATH": "__CORE_RPC_COOKIE_PATH__" + "COOKIE_PATH": "__CORE_RPC_COOKIE_PATH__", + "DEBUG_LOG_PATH": "__CORE_RPC_DEBUG_LOG_PATH__" }, "ELECTRUM": { "HOST": "__ELECTRUM_HOST__", diff --git a/backend/src/__tests__/config.test.ts b/backend/src/__tests__/config.test.ts index 53ab546a8..b3cf7e2a7 100644 --- a/backend/src/__tests__/config.test.ts +++ b/backend/src/__tests__/config.test.ts @@ -41,6 +41,7 @@ describe('Mempool Backend Config', () => { STDOUT_LOG_MIN_PRIORITY: 'debug', POOLS_JSON_TREE_URL: 'https://api.github.com/repos/mempool/mining-pools/git/trees/master', POOLS_JSON_URL: 'https://raw.githubusercontent.com/mempool/mining-pools/master/pools-v2.json', + POOLS_UPDATE_DELAY: 604800, AUDIT: false, RUST_GBT: true, LIMIT_GBT: false, @@ -73,7 +74,8 @@ describe('Mempool Backend Config', () => { PASSWORD: 'mempool', TIMEOUT: 60000, COOKIE: false, - COOKIE_PATH: '/bitcoin/.cookie' + COOKIE_PATH: '/bitcoin/.cookie', + DEBUG_LOG_PATH: '', }); expect(config.SECOND_CORE_RPC).toStrictEqual({ diff --git a/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts b/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts index a08f43238..e246f249d 100644 --- a/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts +++ b/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts @@ -1,4 +1,4 @@ -import { IBitcoinApi, TestMempoolAcceptResult } from './bitcoin-api.interface'; +import { IBitcoinApi, SubmitPackageResult, TestMempoolAcceptResult } from './bitcoin-api.interface'; import { IEsploraApi } from './esplora-api.interface'; export interface AbstractBitcoinApi { @@ -23,12 +23,14 @@ export interface AbstractBitcoinApi { $getScriptHashTransactions(address: string, lastSeenTxId: string): Promise; $sendRawTransaction(rawTransaction: string): Promise; $testMempoolAccept(rawTransactions: string[], maxfeerate?: number): Promise; + $submitPackage(rawTransactions: string[], maxfeerate?: number, maxburnamount?: number): Promise; $getOutspend(txId: string, vout: number): Promise; $getOutspends(txId: string): Promise; $getBatchedOutspends(txId: string[]): Promise; $getBatchedOutspendsInternal(txId: string[]): Promise; $getOutSpendsByOutpoint(outpoints: { txid: string, vout: number }[]): Promise; $getCoinbaseTx(blockhash: string): Promise; + $getAddressTransactionSummary(address: string): Promise; startHealthChecks(): void; getHealthStatus(): HealthCheckHost[]; diff --git a/backend/src/api/bitcoin/bitcoin-api.interface.ts b/backend/src/api/bitcoin/bitcoin-api.interface.ts index 6e8583f6f..5d8371d27 100644 --- a/backend/src/api/bitcoin/bitcoin-api.interface.ts +++ b/backend/src/api/bitcoin/bitcoin-api.interface.ts @@ -218,3 +218,21 @@ export interface TestMempoolAcceptResult { }, ['reject-reason']?: string, } + +export interface SubmitPackageResult { + package_msg: string; + "tx-results": { [wtxid: string]: TxResult }; + "replaced-transactions"?: string[]; +} + +export interface TxResult { + txid: string; + "other-wtxid"?: string; + vsize?: number; + fees?: { + base: number; + "effective-feerate"?: number; + "effective-includes"?: string[]; + }; + error?: string; +} diff --git a/backend/src/api/bitcoin/bitcoin-api.ts b/backend/src/api/bitcoin/bitcoin-api.ts index 7fa431db6..b78c15bf2 100644 --- a/backend/src/api/bitcoin/bitcoin-api.ts +++ b/backend/src/api/bitcoin/bitcoin-api.ts @@ -1,6 +1,6 @@ import * as bitcoinjs from 'bitcoinjs-lib'; import { AbstractBitcoinApi, HealthCheckHost } from './bitcoin-api-abstract-factory'; -import { IBitcoinApi, TestMempoolAcceptResult } from './bitcoin-api.interface'; +import { IBitcoinApi, SubmitPackageResult, TestMempoolAcceptResult } from './bitcoin-api.interface'; import { IEsploraApi } from './esplora-api.interface'; import blocks from '../blocks'; import mempool from '../mempool'; @@ -196,6 +196,10 @@ class BitcoinApi implements AbstractBitcoinApi { } } + $submitPackage(rawTransactions: string[], maxfeerate?: number, maxburnamount?: number): Promise { + return this.bitcoindClient.submitPackage(rawTransactions, maxfeerate ?? undefined, maxburnamount ?? undefined); + } + async $getOutspend(txId: string, vout: number): Promise { const txOut = await this.bitcoindClient.getTxOut(txId, vout, false); return { @@ -251,6 +255,10 @@ class BitcoinApi implements AbstractBitcoinApi { return this.$getRawTransaction(txids[0]); } + async $getAddressTransactionSummary(address: string): Promise { + throw new Error('Method getAddressTransactionSummary not supported by the Bitcoin RPC API.'); + } + $getEstimatedHashrate(blockHeight: number): Promise { // 120 is the default block span in Core return this.bitcoindClient.getNetworkHashPs(120, blockHeight); diff --git a/backend/src/api/bitcoin/bitcoin-core.routes.ts b/backend/src/api/bitcoin/bitcoin-core.routes.ts index 7933dc17b..2c3dd08f6 100644 --- a/backend/src/api/bitcoin/bitcoin-core.routes.ts +++ b/backend/src/api/bitcoin/bitcoin-core.routes.ts @@ -1,6 +1,7 @@ import { Application, NextFunction, Request, Response } from 'express'; import logger from '../../logger'; import bitcoinClient from './bitcoin-client'; +import config from '../../config'; /** * Define a set of routes used by the accelerator server @@ -11,15 +12,15 @@ class BitcoinBackendRoutes { public initRoutes(app: Application) { app - .get('/api/internal/bitcoin-core/' + 'get-mempool-entry', this.disableCache, this.$getMempoolEntry) - .post('/api/internal/bitcoin-core/' + 'decode-raw-transaction', this.disableCache, this.$decodeRawTransaction) - .get('/api/internal/bitcoin-core/' + 'get-raw-transaction', this.disableCache, this.$getRawTransaction) - .post('/api/internal/bitcoin-core/' + 'send-raw-transaction', this.disableCache, this.$sendRawTransaction) - .post('/api/internal/bitcoin-core/' + 'test-mempool-accept', this.disableCache, this.$testMempoolAccept) - .get('/api/internal/bitcoin-core/' + 'get-mempool-ancestors', this.disableCache, this.$getMempoolAncestors) - .get('/api/internal/bitcoin-core/' + 'get-block', this.disableCache, this.$getBlock) - .get('/api/internal/bitcoin-core/' + 'get-block-hash', this.disableCache, this.$getBlockHash) - .get('/api/internal/bitcoin-core/' + 'get-block-count', this.disableCache, this.$getBlockCount) + .get(config.MEMPOOL.API_URL_PREFIX + 'internal/bitcoin-core/' + 'get-mempool-entry', this.disableCache, this.$getMempoolEntry) + .post(config.MEMPOOL.API_URL_PREFIX + 'internal/bitcoin-core/' + 'decode-raw-transaction', this.disableCache, this.$decodeRawTransaction) + .get(config.MEMPOOL.API_URL_PREFIX + 'internal/bitcoin-core/' + 'get-raw-transaction', this.disableCache, this.$getRawTransaction) + .post(config.MEMPOOL.API_URL_PREFIX + 'internal/bitcoin-core/' + 'send-raw-transaction', this.disableCache, this.$sendRawTransaction) + .post(config.MEMPOOL.API_URL_PREFIX + 'internal/bitcoin-core/' + 'test-mempool-accept', this.disableCache, this.$testMempoolAccept) + .get(config.MEMPOOL.API_URL_PREFIX + 'internal/bitcoin-core/' + 'get-mempool-ancestors', this.disableCache, this.$getMempoolAncestors) + .get(config.MEMPOOL.API_URL_PREFIX + 'internal/bitcoin-core/' + 'get-block', this.disableCache, this.$getBlock) + .get(config.MEMPOOL.API_URL_PREFIX + 'internal/bitcoin-core/' + 'get-block-hash', this.disableCache, this.$getBlockHash) + .get(config.MEMPOOL.API_URL_PREFIX + 'internal/bitcoin-core/' + 'get-block-count', this.disableCache, this.$getBlockCount) ; } diff --git a/backend/src/api/bitcoin/bitcoin.routes.ts b/backend/src/api/bitcoin/bitcoin.routes.ts index 498003d98..3b33c1ead 100644 --- a/backend/src/api/bitcoin/bitcoin.routes.ts +++ b/backend/src/api/bitcoin/bitcoin.routes.ts @@ -48,6 +48,8 @@ class BitcoinRoutes { .post(config.MEMPOOL.API_URL_PREFIX + 'psbt/addparents', this.postPsbtCompletion) .get(config.MEMPOOL.API_URL_PREFIX + 'blocks-bulk/:from', this.getBlocksByBulk.bind(this)) .get(config.MEMPOOL.API_URL_PREFIX + 'blocks-bulk/:from/:to', this.getBlocksByBulk.bind(this)) + // Temporarily add txs/package endpoint for all backends until esplora supports it + .post(config.MEMPOOL.API_URL_PREFIX + 'txs/package', this.$submitPackage) ; if (config.MEMPOOL.BACKEND !== 'esplora') { @@ -794,6 +796,19 @@ class BitcoinRoutes { } } + private async $submitPackage(req: Request, res: Response) { + try { + const rawTxs = Common.getTransactionsFromRequest(req); + const maxfeerate = parseFloat(req.query.maxfeerate as string); + const maxburnamount = parseFloat(req.query.maxburnamount as string); + const result = await bitcoinClient.submitPackage(rawTxs, maxfeerate ?? undefined, maxburnamount ?? undefined); + res.send(result); + } catch (e: any) { + handleError(req, res, 400, e.message && e.code ? 'submitpackage RPC error: ' + JSON.stringify({ code: e.code, message: e.message }) + : (e.message || 'Error')); + } + } + } export default new BitcoinRoutes(); diff --git a/backend/src/api/bitcoin/esplora-api.interface.ts b/backend/src/api/bitcoin/esplora-api.interface.ts index 6e6860a41..13fb3526d 100644 --- a/backend/src/api/bitcoin/esplora-api.interface.ts +++ b/backend/src/api/bitcoin/esplora-api.interface.ts @@ -179,4 +179,11 @@ export namespace IEsploraApi { burn_count: number; } + export interface AddressTxSummary { + txid: string; + value: number; + height: number; + time: number; + tx_position?: number; + } } diff --git a/backend/src/api/bitcoin/esplora-api.ts b/backend/src/api/bitcoin/esplora-api.ts index b4ae35da9..9a4b7706a 100644 --- a/backend/src/api/bitcoin/esplora-api.ts +++ b/backend/src/api/bitcoin/esplora-api.ts @@ -5,7 +5,7 @@ import { AbstractBitcoinApi, HealthCheckHost } from './bitcoin-api-abstract-fact import { IEsploraApi } from './esplora-api.interface'; import logger from '../../logger'; import { Common } from '../common'; -import { TestMempoolAcceptResult } from './bitcoin-api.interface'; +import { SubmitPackageResult, TestMempoolAcceptResult } from './bitcoin-api.interface'; interface FailoverHost { host: string, @@ -305,7 +305,7 @@ class ElectrsApi implements AbstractBitcoinApi { } $getAddress(address: string): Promise { - throw new Error('Method getAddress not implemented.'); + return this.failoverRouter.$get('/address/' + address); } $getAddressTransactions(address: string, txId?: string): Promise { @@ -332,6 +332,10 @@ class ElectrsApi implements AbstractBitcoinApi { throw new Error('Method not implemented.'); } + $submitPackage(rawTransactions: string[]): Promise { + throw new Error('Method not implemented.'); + } + $getOutspend(txId: string, vout: number): Promise { return this.failoverRouter.$get('/tx/' + txId + '/outspend/' + vout); } @@ -357,6 +361,10 @@ class ElectrsApi implements AbstractBitcoinApi { return this.failoverRouter.$get('/tx/' + txid); } + async $getAddressTransactionSummary(address: string): Promise { + return this.failoverRouter.$get('/address/' + address + '/txs/summary'); + } + public startHealthChecks(): void { this.failoverRouter.startHealthChecks(); } diff --git a/backend/src/api/database-migration.ts b/backend/src/api/database-migration.ts index 95f8c8707..ee9df9151 100644 --- a/backend/src/api/database-migration.ts +++ b/backend/src/api/database-migration.ts @@ -7,7 +7,7 @@ import cpfpRepository from '../repositories/CpfpRepository'; import { RowDataPacket } from 'mysql2'; class DatabaseMigration { - private static currentVersion = 82; + private static currentVersion = 83; private queryTimeout = 3600_000; private statisticsAddedIndexed = false; private uniqueLogs: string[] = []; @@ -705,6 +705,11 @@ class DatabaseMigration { await this.$fixBadV1AuditBlocks(); await this.updateToSchemaVersion(82); } + + if (databaseSchemaVersion < 83 && isBitcoin === true) { + await this.$executeQuery('ALTER TABLE `blocks` ADD first_seen datetime(6) DEFAULT NULL'); + await this.updateToSchemaVersion(83); + } } /** diff --git a/backend/src/api/mining/mining-routes.ts b/backend/src/api/mining/mining-routes.ts index 69e6d95d4..f53630ce2 100644 --- a/backend/src/api/mining/mining-routes.ts +++ b/backend/src/api/mining/mining-routes.ts @@ -183,7 +183,7 @@ class MiningRoutes { private async $getHistoricalHashrate(req: Request, res: Response) { let currentHashrate = 0, currentDifficulty = 0; try { - currentHashrate = await bitcoinClient.getNetworkHashPs(); + currentHashrate = await bitcoinClient.getNetworkHashPs(1008); currentDifficulty = await bitcoinClient.getDifficulty(); } catch (e) { logger.debug('Bitcoin Core is not available, using zeroed value for current hashrate and difficulty'); diff --git a/backend/src/api/services/services-routes.ts b/backend/src/api/services/services-routes.ts new file mode 100644 index 000000000..cff163174 --- /dev/null +++ b/backend/src/api/services/services-routes.ts @@ -0,0 +1,26 @@ +import { Application, Request, Response } from 'express'; +import config from '../../config'; +import WalletApi from './wallets'; + +class ServicesRoutes { + public initRoutes(app: Application): void { + app + .get(config.MEMPOOL.API_URL_PREFIX + 'wallet/:walletId', this.$getWallet) + ; + } + + private async $getWallet(req: Request, res: Response): Promise { + try { + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 5).toUTCString()); + const walletId = req.params.walletId; + const wallet = await WalletApi.getWallet(walletId); + res.status(200).send(wallet); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } +} + +export default new ServicesRoutes(); diff --git a/backend/src/api/services/wallets.ts b/backend/src/api/services/wallets.ts new file mode 100644 index 000000000..dd4d7ebc9 --- /dev/null +++ b/backend/src/api/services/wallets.ts @@ -0,0 +1,153 @@ +import config from '../../config'; +import logger from '../../logger'; +import { IEsploraApi } from '../bitcoin/esplora-api.interface'; +import bitcoinApi from '../bitcoin/bitcoin-api-factory'; +import axios from 'axios'; +import { TransactionExtended } from '../../mempool.interfaces'; + +interface WalletAddress { + address: string; + active: boolean; + stats: { + funded_txo_count: number; + funded_txo_sum: number; + spent_txo_count: number; + spent_txo_sum: number; + tx_count: number; + }; + transactions: IEsploraApi.AddressTxSummary[]; + lastSync: number; +} + +interface Wallet { + name: string; + addresses: Record; + lastPoll: number; +} + +const POLL_FREQUENCY = 5 * 60 * 1000; // 5 minutes + +class WalletApi { + private wallets: Record = {}; + private syncing = false; + + constructor() { + this.wallets = config.WALLETS.ENABLED ? (config.WALLETS.WALLETS as string[]).reduce((acc, wallet) => { + acc[wallet] = { name: wallet, addresses: {}, lastPoll: 0 }; + return acc; + }, {} as Record) : {}; + } + + public getWallet(wallet: string): Record { + return this.wallets?.[wallet]?.addresses || {}; + } + + // resync wallet addresses from the services backend + async $syncWallets(): Promise { + if (!config.WALLETS.ENABLED || this.syncing) { + return; + } + this.syncing = true; + for (const walletKey of Object.keys(this.wallets)) { + const wallet = this.wallets[walletKey]; + if (wallet.lastPoll < (Date.now() - POLL_FREQUENCY)) { + try { + const response = await axios.get(config.MEMPOOL_SERVICES.API + `/wallets/${wallet.name}`); + const addresses: Record = response.data; + const addressList: WalletAddress[] = Object.values(addresses); + // sync all current addresses + for (const address of addressList) { + await this.$syncWalletAddress(wallet, address); + } + // remove old addresses + for (const address of Object.keys(wallet.addresses)) { + if (!addresses[address]) { + delete wallet.addresses[address]; + } + } + wallet.lastPoll = Date.now(); + logger.debug(`Synced ${Object.keys(wallet.addresses).length} addresses for wallet ${wallet.name}`); + } catch (e) { + logger.err(`Error syncing wallet ${wallet.name}: ${(e instanceof Error ? e.message : e)}`); + } + } + } + this.syncing = false; + } + + // resync address transactions from esplora + async $syncWalletAddress(wallet: Wallet, address: WalletAddress): Promise { + // fetch full transaction data if the address is new or still active and hasn't been synced in the last hour + const refreshTransactions = !wallet.addresses[address.address] || (address.active && (Date.now() - wallet.addresses[address.address].lastSync) > 60 * 60 * 1000); + if (refreshTransactions) { + try { + const summary = await bitcoinApi.$getAddressTransactionSummary(address.address); + const addressInfo = await bitcoinApi.$getAddress(address.address); + const walletAddress: WalletAddress = { + address: address.address, + active: address.active, + transactions: summary, + stats: addressInfo.chain_stats, + lastSync: Date.now(), + }; + wallet.addresses[address.address] = walletAddress; + } catch (e) { + logger.err(`Error syncing wallet address ${address.address}: ${(e instanceof Error ? e.message : e)}`); + } + } + } + + // check a new block for transactions that affect wallet address balances, and add relevant transactions to wallets + processBlock(block: IEsploraApi.Block, blockTxs: TransactionExtended[]): Record { + const walletTransactions: Record = {}; + for (const walletKey of Object.keys(this.wallets)) { + const wallet = this.wallets[walletKey]; + walletTransactions[walletKey] = []; + for (const tx of blockTxs) { + const funded: Record = {}; + const spent: Record = {}; + const fundedCount: Record = {}; + const spentCount: Record = {}; + let anyMatch = false; + for (const vin of tx.vin) { + const address = vin.prevout?.scriptpubkey_address; + if (address && wallet.addresses[address]) { + anyMatch = true; + spent[address] = (spent[address] ?? 0) + (vin.prevout?.value ?? 0); + spentCount[address] = (spentCount[address] ?? 0) + 1; + } + } + for (const vout of tx.vout) { + const address = vout.scriptpubkey_address; + if (address && wallet.addresses[address]) { + anyMatch = true; + funded[address] = (funded[address] ?? 0) + (vout.value ?? 0); + fundedCount[address] = (fundedCount[address] ?? 0) + 1; + } + } + for (const address of Object.keys({ ...funded, ...spent })) { + // update address stats + wallet.addresses[address].stats.tx_count++; + wallet.addresses[address].stats.funded_txo_count += fundedCount[address] || 0; + wallet.addresses[address].stats.spent_txo_count += spentCount[address] || 0; + wallet.addresses[address].stats.funded_txo_sum += funded[address] || 0; + wallet.addresses[address].stats.spent_txo_sum += spent[address] || 0; + // add tx to summary + const txSummary: IEsploraApi.AddressTxSummary = { + txid: tx.txid, + value: (funded[address] ?? 0) - (spent[address] ?? 0), + height: block.height, + time: block.timestamp, + }; + wallet.addresses[address].transactions?.push(txSummary); + } + if (anyMatch) { + walletTransactions[walletKey].push(tx); + } + } + } + return walletTransactions; + } +} + +export default new WalletApi(); \ No newline at end of file diff --git a/backend/src/api/websocket-handler.ts b/backend/src/api/websocket-handler.ts index 2a047472e..75b3abbcb 100644 --- a/backend/src/api/websocket-handler.ts +++ b/backend/src/api/websocket-handler.ts @@ -16,6 +16,7 @@ import transactionUtils from './transaction-utils'; import rbfCache, { ReplacementInfo } from './rbf-cache'; import difficultyAdjustment from './difficulty-adjustment'; import feeApi from './fee-api'; +import BlocksRepository from '../repositories/BlocksRepository'; import BlocksAuditsRepository from '../repositories/BlocksAuditsRepository'; import BlocksSummariesRepository from '../repositories/BlocksSummariesRepository'; import Audit from './audit'; @@ -26,6 +27,7 @@ import mempool from './mempool'; import statistics from './statistics/statistics'; import accelerationRepository from '../repositories/AccelerationRepository'; import bitcoinApi from './bitcoin/bitcoin-api-factory'; +import walletApi from './services/wallets'; interface AddressTransactions { mempool: MempoolTransactionExtended[], @@ -34,6 +36,7 @@ interface AddressTransactions { } import bitcoinSecondClient from './bitcoin/bitcoin-second-client'; import { calculateMempoolTxCpfp } from './cpfp'; +import { getRecentFirstSeen } from '../utils/file-read'; // valid 'want' subscriptions const wantable = [ @@ -305,6 +308,14 @@ class WebsocketHandler { } } + if (parsedMessage && parsedMessage['track-wallet']) { + if (parsedMessage['track-wallet'] === 'stop') { + client['track-wallet'] = null; + } else { + client['track-wallet'] = parsedMessage['track-wallet']; + } + } + if (parsedMessage && parsedMessage['track-asset']) { if (/^[a-fA-F0-9]{64}$/.test(parsedMessage['track-asset'])) { client['track-asset'] = parsedMessage['track-asset']; @@ -1028,6 +1039,14 @@ class WebsocketHandler { } } + if (config.CORE_RPC.DEBUG_LOG_PATH && block.extras) { + const firstSeen = getRecentFirstSeen(block.id); + if (firstSeen) { + BlocksRepository.$saveFirstSeenTime(block.id, firstSeen); + block.extras.firstSeen = firstSeen; + } + } + const confirmedTxids: { [txid: string]: boolean } = {}; // Update mempool to remove transactions included in the new block @@ -1102,6 +1121,9 @@ class WebsocketHandler { replaced: replacedTransactions, }; + // check for wallet transactions + const walletTransactions = config.WALLETS.ENABLED ? walletApi.processBlock(block, transactions) : []; + const responseCache = { ...this.socketData }; function getCachedResponse(key, data): string { if (!responseCache[key]) { @@ -1306,6 +1328,11 @@ class WebsocketHandler { response['mempool-transactions'] = getCachedResponse('mempool-transactions', mempoolDelta); } + if (client['track-wallet']) { + const trackedWallet = client['track-wallet']; + response['wallet-transactions'] = getCachedResponse(`wallet-transactions-${trackedWallet}`, walletTransactions[trackedWallet] ?? {}); + } + if (Object.keys(response).length) { client.send(this.serializeResponse(response)); } diff --git a/backend/src/config.ts b/backend/src/config.ts index a3e0a40af..794421551 100644 --- a/backend/src/config.ts +++ b/backend/src/config.ts @@ -32,6 +32,7 @@ interface IConfig { AUTOMATIC_POOLS_UPDATE: boolean; POOLS_JSON_URL: string, POOLS_JSON_TREE_URL: string, + POOLS_UPDATE_DELAY: number, AUDIT: boolean; RUST_GBT: boolean; LIMIT_GBT: boolean; @@ -85,6 +86,7 @@ interface IConfig { TIMEOUT: number; COOKIE: boolean; COOKIE_PATH: string; + DEBUG_LOG_PATH: string; }; SECOND_CORE_RPC: { HOST: string; @@ -160,6 +162,10 @@ interface IConfig { PAID: boolean; API_KEY: string; }, + WALLETS: { + ENABLED: boolean; + WALLETS: string[]; + } } const defaults: IConfig = { @@ -192,6 +198,7 @@ const defaults: IConfig = { 'AUTOMATIC_POOLS_UPDATE': false, 'POOLS_JSON_URL': 'https://raw.githubusercontent.com/mempool/mining-pools/master/pools-v2.json', 'POOLS_JSON_TREE_URL': 'https://api.github.com/repos/mempool/mining-pools/git/trees/master', + 'POOLS_UPDATE_DELAY': 604800, // in seconds, default is one week 'AUDIT': false, 'RUST_GBT': true, 'LIMIT_GBT': false, @@ -225,7 +232,8 @@ const defaults: IConfig = { 'PASSWORD': 'mempool', 'TIMEOUT': 60000, 'COOKIE': false, - 'COOKIE_PATH': '/bitcoin/.cookie' + 'COOKIE_PATH': '/bitcoin/.cookie', + 'DEBUG_LOG_PATH': '', }, 'SECOND_CORE_RPC': { 'HOST': '127.0.0.1', @@ -320,6 +328,10 @@ const defaults: IConfig = { 'PAID': false, 'API_KEY': '', }, + 'WALLETS': { + 'ENABLED': false, + 'WALLETS': [], + }, }; class Config implements IConfig { @@ -341,6 +353,7 @@ class Config implements IConfig { MEMPOOL_SERVICES: IConfig['MEMPOOL_SERVICES']; REDIS: IConfig['REDIS']; FIAT_PRICE: IConfig['FIAT_PRICE']; + WALLETS: IConfig['WALLETS']; constructor() { const configs = this.merge(configFromFile, defaults); @@ -362,6 +375,7 @@ class Config implements IConfig { this.MEMPOOL_SERVICES = configs.MEMPOOL_SERVICES; this.REDIS = configs.REDIS; this.FIAT_PRICE = configs.FIAT_PRICE; + this.WALLETS = configs.WALLETS; } merge = (...objects: object[]): IConfig => { diff --git a/backend/src/index.ts b/backend/src/index.ts index 1d83c56a3..080f21335 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -32,6 +32,7 @@ import pricesRoutes from './api/prices/prices.routes'; import miningRoutes from './api/mining/mining-routes'; import liquidRoutes from './api/liquid/liquid.routes'; import bitcoinRoutes from './api/bitcoin/bitcoin.routes'; +import servicesRoutes from './api/services/services-routes'; import fundingTxFetcher from './tasks/lightning/sync-tasks/funding-tx-fetcher'; import forensicsService from './tasks/lightning/forensics.service'; import priceUpdater from './tasks/price-updater'; @@ -46,6 +47,7 @@ import bitcoinSecondClient from './api/bitcoin/bitcoin-second-client'; import accelerationRoutes from './api/acceleration/acceleration.routes'; import aboutRoutes from './api/about.routes'; import mempoolBlocks from './api/mempool-blocks'; +import walletApi from './api/services/wallets'; class Server { private wss: WebSocket.Server | undefined; @@ -211,6 +213,8 @@ class Server { } }); } + + poolsUpdater.$startService(); } async runMainUpdateLoop(): Promise { @@ -236,6 +240,10 @@ class Server { await memPool.$updateMempool(newMempool, newAccelerations, minFeeMempool, minFeeTip, pollRate); } indexer.$run(); + if (config.WALLETS.ENABLED) { + // might take a while, so run in the background + walletApi.$syncWallets(); + } if (config.FIAT_PRICE.ENABLED) { priceUpdater.$run(); } @@ -333,6 +341,9 @@ class Server { if (config.MEMPOOL_SERVICES.ACCELERATIONS) { accelerationRoutes.initRoutes(this.app); } + if (config.WALLETS.ENABLED) { + servicesRoutes.initRoutes(this.app); + } if (!config.MEMPOOL.OFFICIAL) { aboutRoutes.initRoutes(this.app); } diff --git a/backend/src/mempool.interfaces.ts b/backend/src/mempool.interfaces.ts index 6eee1a9ee..dc703af21 100644 --- a/backend/src/mempool.interfaces.ts +++ b/backend/src/mempool.interfaces.ts @@ -320,6 +320,7 @@ export interface BlockExtension { segwitTotalSize: number; segwitTotalWeight: number; header: string; + firstSeen: number | null; utxoSetChange: number; // Requires coinstatsindex, will be set to NULL otherwise utxoSetSize: number | null; diff --git a/backend/src/repositories/BlocksRepository.ts b/backend/src/repositories/BlocksRepository.ts index f958e5c8b..112cb1903 100644 --- a/backend/src/repositories/BlocksRepository.ts +++ b/backend/src/repositories/BlocksRepository.ts @@ -57,6 +57,7 @@ interface DatabaseBlock { utxoSetChange: number; utxoSetSize: number; totalInputAmt: number; + firstSeen: number; } const BLOCK_DB_FIELDS = ` @@ -99,7 +100,8 @@ const BLOCK_DB_FIELDS = ` blocks.header, blocks.utxoset_change AS utxoSetChange, blocks.utxoset_size AS utxoSetSize, - blocks.total_input_amt AS totalInputAmt + blocks.total_input_amt AS totalInputAmt, + UNIX_TIMESTAMP(blocks.first_seen) AS firstSeen `; class BlocksRepository { @@ -1021,6 +1023,24 @@ class BlocksRepository { } } + /** + * Save block first seen time + * + * @param id + */ + public async $saveFirstSeenTime(id: string, firstSeen: number): Promise { + try { + await DB.query(` + UPDATE blocks SET first_seen = FROM_UNIXTIME(?) + WHERE hash = ?`, + [firstSeen, id] + ); + } catch (e) { + logger.err(`Cannot update block first seen time. Reason: ` + (e instanceof Error ? e.message : e)); + throw e; + } + } + /** * Convert a mysql row block into a BlockExtended. Note that you * must provide the correct field into dbBlk object param @@ -1078,6 +1098,7 @@ class BlocksRepository { extras.utxoSetSize = dbBlk.utxoSetSize; extras.totalInputAmt = dbBlk.totalInputAmt; extras.virtualSize = dbBlk.weight / 4.0; + extras.firstSeen = dbBlk.firstSeen; // Re-org can happen after indexing so we need to always get the // latest state from core diff --git a/backend/src/rpc-api/commands.ts b/backend/src/rpc-api/commands.ts index 85675230b..89ab9cfe6 100644 --- a/backend/src/rpc-api/commands.ts +++ b/backend/src/rpc-api/commands.ts @@ -83,6 +83,7 @@ module.exports = { signRawTransaction: 'signrawtransaction', // bitcoind v0.7.0+ stop: 'stop', submitBlock: 'submitblock', // bitcoind v0.7.0+ + submitPackage: 'submitpackage', validateAddress: 'validateaddress', verifyChain: 'verifychain', // bitcoind v0.9.0+ verifyMessage: 'verifymessage', diff --git a/backend/src/tasks/pools-updater.ts b/backend/src/tasks/pools-updater.ts index a3a3265c6..652383a2a 100644 --- a/backend/src/tasks/pools-updater.ts +++ b/backend/src/tasks/pools-updater.ts @@ -6,16 +6,30 @@ import backendInfo from '../api/backend-info'; import logger from '../logger'; import { SocksProxyAgent } from 'socks-proxy-agent'; import * as https from 'https'; +import { Common } from '../api/common'; /** * Maintain the most recent version of pools-v2.json */ class PoolsUpdater { + tag = 'PoolsUpdater'; + lastRun: number = 0; currentSha: string | null = null; poolsUrl: string = config.MEMPOOL.POOLS_JSON_URL; treeUrl: string = config.MEMPOOL.POOLS_JSON_TREE_URL; + public async $startService(): Promise { + while ('Bitcoin is still alive') { + try { + await this.updatePoolsJson(); + } catch (e: any) { + logger.info(`Exception ${e} in PoolsUpdater::$startService. Code: ${e.code}. Message: ${e.message}`, this.tag); + } + await Common.sleep$(10000); + } + } + public async updatePoolsJson(): Promise { if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false || config.MEMPOOL.ENABLED === false @@ -23,11 +37,8 @@ class PoolsUpdater { return; } - const oneWeek = 604800; - const oneDay = 86400; - const now = new Date().getTime() / 1000; - if (now - this.lastRun < oneWeek) { // Execute the PoolsUpdate only once a week, or upon restart + if (now - this.lastRun < config.MEMPOOL.POOLS_UPDATE_DELAY) { // Execute the PoolsUpdate only once a week, or upon restart return; } @@ -43,7 +54,7 @@ class PoolsUpdater { this.currentSha = await this.getShaFromDb(); } - logger.debug(`pools-v2.json sha | Current: ${this.currentSha} | Github: ${githubSha}`); + logger.debug(`pools-v2.json sha | Current: ${this.currentSha} | Github: ${githubSha}`, this.tag); if (this.currentSha !== null && this.currentSha === githubSha) { return; } @@ -53,16 +64,16 @@ class PoolsUpdater { config.MEMPOOL.AUTOMATIC_POOLS_UPDATE !== true && // Automatic pools update is disabled !process.env.npm_config_update_pools // We're not manually updating mining pool ) { - logger.warn(`Updated mining pools data is available (${githubSha}) but AUTOMATIC_POOLS_UPDATE is disabled`); - logger.info(`You can update your mining pools using the --update-pools command flag. You may want to clear your nginx cache as well if applicable`); + logger.warn(`Updated mining pools data is available (${githubSha}) but AUTOMATIC_POOLS_UPDATE is disabled`, this.tag); + logger.info(`You can update your mining pools using the --update-pools command flag. You may want to clear your nginx cache as well if applicable`, this.tag); return; } const network = config.SOCKS5PROXY.ENABLED ? 'tor' : 'clearnet'; if (this.currentSha === null) { - logger.info(`Downloading pools-v2.json for the first time from ${this.poolsUrl} over ${network}`, logger.tags.mining); + logger.info(`Downloading pools-v2.json for the first time from ${this.poolsUrl} over ${network}`, this.tag); } else { - logger.warn(`pools-v2.json is outdated, fetching latest from ${this.poolsUrl} over ${network}`, logger.tags.mining); + logger.warn(`pools-v2.json is outdated, fetching latest from ${this.poolsUrl} over ${network}`, this.tag); } const poolsJson = await this.query(this.poolsUrl); if (poolsJson === undefined) { @@ -71,7 +82,7 @@ class PoolsUpdater { poolsParser.setMiningPools(poolsJson); if (config.DATABASE.ENABLED === false) { // Don't run db operations - logger.info(`Mining pools-v2.json (${githubSha}) import completed (no database)`); + logger.info(`Mining pools-v2.json (${githubSha}) import completed (no database)`, this.tag); return; } @@ -81,14 +92,14 @@ class PoolsUpdater { await this.updateDBSha(githubSha); await DB.query('COMMIT;'); } catch (e) { - logger.err(`Could not migrate mining pools, rolling back. Exception: ${JSON.stringify(e)}`, logger.tags.mining); + logger.err(`Could not migrate mining pools, rolling back. Exception: ${JSON.stringify(e)}`, this.tag); await DB.query('ROLLBACK;'); } - logger.info(`Mining pools-v2.json (${githubSha}) import completed`); + logger.info(`Mining pools-v2.json (${githubSha}) import completed`, this.tag); } catch (e) { - this.lastRun = now - (oneWeek - oneDay); // Try again in 24h instead of waiting next week - logger.err(`PoolsUpdater failed. Will try again in 24h. Exception: ${JSON.stringify(e)}`, logger.tags.mining); + this.lastRun = now - 600; // Try again in 10 minutes + logger.err(`PoolsUpdater failed. Will try again in 10 minutes. Exception: ${JSON.stringify(e)}`, this.tag); } } @@ -102,7 +113,7 @@ class PoolsUpdater { await DB.query('DELETE FROM state where name="pools_json_sha"'); await DB.query(`INSERT INTO state VALUES('pools_json_sha', NULL, '${githubSha}')`); } catch (e) { - logger.err('Cannot save github pools-v2.json sha into the db. Reason: ' + (e instanceof Error ? e.message : e), logger.tags.mining); + logger.err('Cannot save github pools-v2.json sha into the db. Reason: ' + (e instanceof Error ? e.message : e), this.tag); } } } @@ -115,7 +126,7 @@ class PoolsUpdater { const [rows]: any[] = await DB.query('SELECT string FROM state WHERE name="pools_json_sha"'); return (rows.length > 0 ? rows[0].string : null); } catch (e) { - logger.err('Cannot fetch pools-v2.json sha from db. Reason: ' + (e instanceof Error ? e.message : e), logger.tags.mining); + logger.err('Cannot fetch pools-v2.json sha from db. Reason: ' + (e instanceof Error ? e.message : e), this.tag); return null; } } @@ -134,7 +145,7 @@ class PoolsUpdater { } } - logger.err(`Cannot find "pools-v2.json" in git tree (${this.treeUrl})`, logger.tags.mining); + logger.err(`Cannot find "pools-v2.json" in git tree (${this.treeUrl})`, this.tag); return null; } @@ -186,7 +197,7 @@ class PoolsUpdater { } return data.data; } catch (e) { - logger.err('Could not connect to Github. Reason: ' + (e instanceof Error ? e.message : e)); + logger.err('Could not connect to Github. Reason: ' + (e instanceof Error ? e.message : e), this.tag); retry++; } await setDelay(config.MEMPOOL.EXTERNAL_RETRY_INTERVAL); diff --git a/backend/src/utils/file-read.ts b/backend/src/utils/file-read.ts new file mode 100644 index 000000000..ddf8660c4 --- /dev/null +++ b/backend/src/utils/file-read.ts @@ -0,0 +1,58 @@ +import * as fs from 'fs'; +import logger from '../logger'; +import config from '../config'; + +function readFile(filePath: string, bufferSize?: number): string[] { + const fileSize = fs.statSync(filePath).size; + const chunkSize = bufferSize || fileSize; + const fileDescriptor = fs.openSync(filePath, 'r'); + const buffer = Buffer.alloc(chunkSize); + + fs.readSync(fileDescriptor, buffer, 0, chunkSize, fileSize - chunkSize); + fs.closeSync(fileDescriptor); + + const lines = buffer.toString('utf8', 0, chunkSize).split('\n'); + return lines; +} + +function extractDateFromLogLine(line: string): number | undefined { + // Extract time from log: "2021-08-31T12:34:56Z" or "2021-08-31T12:34:56.123456Z" + const dateMatch = line.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{6})?Z/); + if (!dateMatch) { + return undefined; + } + + const dateStr = dateMatch[0]; + const date = new Date(dateStr); + let timestamp = Math.floor(date.getTime() / 1000); // Remove decimal (microseconds are added later) + + const timePart = dateStr.split('T')[1]; + const microseconds = timePart.split('.')[1] || ''; + + if (!microseconds) { + return timestamp; + } + + return parseFloat(timestamp + '.' + microseconds); +} + +export function getRecentFirstSeen(hash: string): number | undefined { + const debugLogPath = config.CORE_RPC.DEBUG_LOG_PATH; + if (debugLogPath) { + try { + // Read the last few lines of debug.log + const lines = readFile(debugLogPath, 2048); + + for (let i = lines.length - 1; i >= 0; i--) { + const line = lines[i]; + if (line && line.includes(`Saw new header hash=${hash}`)) { + return extractDateFromLogLine(line); + } + } + } catch (e) { + logger.err(`Cannot parse block first seen time from Core logs. Reason: ` + (e instanceof Error ? e.message : e)); + } + } + + return undefined; +} diff --git a/docker/README.md b/docker/README.md index ce1548e91..2658914eb 100644 --- a/docker/README.md +++ b/docker/README.md @@ -109,6 +109,7 @@ Below we list all settings from `mempool-config.json` and the corresponding over "AUTOMATIC_POOLS_UPDATE": false, "POOLS_JSON_URL": "https://raw.githubusercontent.com/mempool/mining-pools/master/pools-v2.json", "POOLS_JSON_TREE_URL": "https://api.github.com/repos/mempool/mining-pools/git/trees/master", + "POOLS_UPDATE_DELAY": 604800, "CPFP_INDEXING": false, "MAX_BLOCKS_BULK_QUERY": 0, "DISK_CACHE_BLOCK_INTERVAL": 6, @@ -140,6 +141,7 @@ Corresponding `docker-compose.yml` overrides: MEMPOOL_AUTOMATIC_POOLS_UPDATE: "" MEMPOOL_POOLS_JSON_URL: "" MEMPOOL_POOLS_JSON_TREE_URL: "" + MEMPOOL_POOLS_UPDATE_DELAY: "" MEMPOOL_CPFP_INDEXING: "" MEMPOOL_MAX_BLOCKS_BULK_QUERY: "" MEMPOOL_DISK_CACHE_BLOCK_INTERVAL: "" diff --git a/docker/backend/mempool-config.json b/docker/backend/mempool-config.json index 79cd14644..c7ade9b7b 100644 --- a/docker/backend/mempool-config.json +++ b/docker/backend/mempool-config.json @@ -36,6 +36,7 @@ "ALLOW_UNREACHABLE": __MEMPOOL_ALLOW_UNREACHABLE__, "POOLS_JSON_TREE_URL": "__MEMPOOL_POOLS_JSON_TREE_URL__", "POOLS_JSON_URL": "__MEMPOOL_POOLS_JSON_URL__", + "POOLS_UPDATE_DELAY": __MEMPOOL_POOLS_UPDATE_DELAY__, "PRICE_UPDATES_PER_HOUR": __MEMPOOL_PRICE_UPDATES_PER_HOUR__, "MAX_TRACKED_ADDRESSES": __MEMPOOL_MAX_TRACKED_ADDRESSES__ }, @@ -46,7 +47,8 @@ "PASSWORD": "__CORE_RPC_PASSWORD__", "TIMEOUT": __CORE_RPC_TIMEOUT__, "COOKIE": __CORE_RPC_COOKIE__, - "COOKIE_PATH": "__CORE_RPC_COOKIE_PATH__" + "COOKIE_PATH": "__CORE_RPC_COOKIE_PATH__", + "DEBUG_LOG_PATH": "__CORE_RPC_DEBUG_LOG_PATH__" }, "ELECTRUM": { "HOST": "__ELECTRUM_HOST__", diff --git a/docker/backend/start.sh b/docker/backend/start.sh index 39ca95a2a..d4765972e 100755 --- a/docker/backend/start.sh +++ b/docker/backend/start.sh @@ -29,6 +29,7 @@ __MEMPOOL_STDOUT_LOG_MIN_PRIORITY__=${MEMPOOL_STDOUT_LOG_MIN_PRIORITY:=info} __MEMPOOL_AUTOMATIC_POOLS_UPDATE__=${MEMPOOL_AUTOMATIC_POOLS_UPDATE:=false} __MEMPOOL_POOLS_JSON_URL__=${MEMPOOL_POOLS_JSON_URL:=https://raw.githubusercontent.com/mempool/mining-pools/master/pools-v2.json} __MEMPOOL_POOLS_JSON_TREE_URL__=${MEMPOOL_POOLS_JSON_TREE_URL:=https://api.github.com/repos/mempool/mining-pools/git/trees/master} +__MEMPOOL_POOLS_UPDATE_DELAY__=${MEMPOOL_POOLS_UPDATE_DELAY:=604800} __MEMPOOL_AUDIT__=${MEMPOOL_AUDIT:=false} __MEMPOOL_RUST_GBT__=${MEMPOOL_RUST_GBT:=true} __MEMPOOL_LIMIT_GBT__=${MEMPOOL_LIMIT_GBT:=false} @@ -48,6 +49,7 @@ __CORE_RPC_PASSWORD__=${CORE_RPC_PASSWORD:=mempool} __CORE_RPC_TIMEOUT__=${CORE_RPC_TIMEOUT:=60000} __CORE_RPC_COOKIE__=${CORE_RPC_COOKIE:=false} __CORE_RPC_COOKIE_PATH__=${CORE_RPC_COOKIE_PATH:=""} +__CORE_RPC_DEBUG_LOG_PATH__=${CORE_RPC_DEBUG_LOG_PATH:=""} # ELECTRUM __ELECTRUM_HOST__=${ELECTRUM_HOST:=127.0.0.1} @@ -187,6 +189,7 @@ sed -i "s!__MEMPOOL_STDOUT_LOG_MIN_PRIORITY__!${__MEMPOOL_STDOUT_LOG_MIN_PRIORIT sed -i "s!__MEMPOOL_AUTOMATIC_POOLS_UPDATE__!${__MEMPOOL_AUTOMATIC_POOLS_UPDATE__}!g" mempool-config.json sed -i "s!__MEMPOOL_POOLS_JSON_URL__!${__MEMPOOL_POOLS_JSON_URL__}!g" mempool-config.json sed -i "s!__MEMPOOL_POOLS_JSON_TREE_URL__!${__MEMPOOL_POOLS_JSON_TREE_URL__}!g" mempool-config.json +sed -i "s!__MEMPOOL_POOLS_UPDATE_DELAY__!${__MEMPOOL_POOLS_UPDATE_DELAY__}!g" mempool-config.json sed -i "s!__MEMPOOL_AUDIT__!${__MEMPOOL_AUDIT__}!g" mempool-config.json sed -i "s!__MEMPOOL_RUST_GBT__!${__MEMPOOL_RUST_GBT__}!g" mempool-config.json sed -i "s!__MEMPOOL_LIMIT_GBT__!${__MEMPOOL_LIMIT_GBT__}!g" mempool-config.json @@ -205,6 +208,7 @@ sed -i "s!__CORE_RPC_PASSWORD__!${__CORE_RPC_PASSWORD__}!g" mempool-config.json sed -i "s!__CORE_RPC_TIMEOUT__!${__CORE_RPC_TIMEOUT__}!g" mempool-config.json sed -i "s!__CORE_RPC_COOKIE__!${__CORE_RPC_COOKIE__}!g" mempool-config.json sed -i "s!__CORE_RPC_COOKIE_PATH__!${__CORE_RPC_COOKIE_PATH__}!g" mempool-config.json +sed -i "s!__CORE_RPC_DEBUG_LOG_PATH__!${__CORE_RPC_DEBUG_LOG_PATH__}!g" mempool-config.json sed -i "s!__ELECTRUM_HOST__!${__ELECTRUM_HOST__}!g" mempool-config.json sed -i "s!__ELECTRUM_PORT__!${__ELECTRUM_PORT__}!g" mempool-config.json diff --git a/frontend/custom-bitb-config.json b/frontend/custom-bitb-config.json new file mode 100644 index 000000000..4938034fe --- /dev/null +++ b/frontend/custom-bitb-config.json @@ -0,0 +1,48 @@ +{ + "theme": "wiz", + "enterprise": "bitb", + "branding": { + "name": "bitb", + "title": "BITB", + "site_id": 20, + "header_img": "/resources/bitblogo.svg", + "footer_img": "/resources/bitblogo.svg" + }, + "dashboard": { + "widgets": [ + { + "component": "fees", + "mobileOrder": 4 + }, + { + "component": "walletBalance", + "mobileOrder": 1, + "props": { + "wallet": "BITB" + } + }, + { + "component": "goggles", + "mobileOrder": 5 + }, + { + "component": "wallet", + "mobileOrder": 2, + "props": { + "wallet": "BITB", + "period": "all" + } + }, + { + "component": "blocks" + }, + { + "component": "walletTransactions", + "mobileOrder": 3, + "props": { + "wallet": "BITB" + } + } + ] + } +} \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 9d4e018ef..a27bffcb4 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -42,7 +42,7 @@ "rxjs": "~7.8.1", "tinyify": "^4.0.0", "tlite": "^0.1.9", - "tslib": "~2.7.0", + "tslib": "~2.8.0", "zone.js": "~0.14.4" }, "devDependencies": { @@ -51,7 +51,7 @@ "@types/node": "^18.11.9", "@typescript-eslint/eslint-plugin": "^7.4.0", "@typescript-eslint/parser": "^7.4.0", - "browser-sync": "^3.0.0", + "browser-sync": "^3.0.3", "eslint": "^8.57.0", "http-proxy-middleware": "~2.0.6", "prettier": "^3.0.0", @@ -62,7 +62,7 @@ "optionalDependencies": { "@cypress/schematic": "^2.5.0", "@types/cypress": "^1.1.3", - "cypress": "^13.14.0", + "cypress": "^13.15.0", "cypress-fail-on-console-error": "~5.1.0", "cypress-wait-until": "^2.0.1", "mock-socket": "~9.3.1", @@ -3113,9 +3113,9 @@ } }, "node_modules/@cypress/request": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.1.tgz", - "integrity": "sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.5.tgz", + "integrity": "sha512-v+XHd9XmWbufxF1/bTaVm2yhbxY+TB4YtWRqF2zaXBlDNMkls34KiATz0AVDLavL3iB6bQk9/7n3oY1EoLSWGA==", "optional": true, "dependencies": { "aws-sign2": "~0.7.0", @@ -3124,14 +3124,14 @@ "combined-stream": "~1.0.6", "extend": "~3.0.2", "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "http-signature": "~1.3.6", + "form-data": "~4.0.0", + "http-signature": "~1.4.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.19", "performance-now": "^2.1.0", - "qs": "6.10.4", + "qs": "6.13.0", "safe-buffer": "^5.1.2", "tough-cookie": "^4.1.3", "tunnel-agent": "^0.6.0", @@ -4313,9 +4313,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", - "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", + "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", "cpu": [ "arm" ], @@ -4325,9 +4325,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", - "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", + "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", "cpu": [ "arm64" ], @@ -4337,9 +4337,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", - "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", + "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", "cpu": [ "arm64" ], @@ -4349,9 +4349,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", - "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", + "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", "cpu": [ "x64" ], @@ -4361,9 +4361,21 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", - "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", + "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", + "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", "cpu": [ "arm" ], @@ -4373,9 +4385,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", - "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", + "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", "cpu": [ "arm64" ], @@ -4385,9 +4397,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", - "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", + "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", "cpu": [ "arm64" ], @@ -4396,10 +4408,22 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", + "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", - "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", + "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", "cpu": [ "riscv64" ], @@ -4408,10 +4432,22 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", + "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", - "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", + "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", "cpu": [ "x64" ], @@ -4421,9 +4457,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", - "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", + "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", "cpu": [ "x64" ], @@ -4433,9 +4469,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", - "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", + "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", "cpu": [ "arm64" ], @@ -4445,9 +4481,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", - "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", + "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", "cpu": [ "ia32" ], @@ -4457,9 +4493,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", - "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", + "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", "cpu": [ "x64" ], @@ -4764,9 +4800,9 @@ "devOptional": true }, "node_modules/@types/cors": { - "version": "2.8.13", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", - "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", "devOptional": true, "dependencies": { "@types/node": "*" @@ -4801,9 +4837,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" }, "node_modules/@types/express": { "version": "4.17.13", @@ -5797,9 +5833,9 @@ } }, "node_modules/aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", "optional": true }, "node_modules/axios": { @@ -6065,20 +6101,6 @@ "node": ">= 0.8" } }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/bonjour-service": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", @@ -6187,13 +6209,13 @@ } }, "node_modules/browser-sync": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-3.0.2.tgz", - "integrity": "sha512-PC9c7aWJFVR4IFySrJxOqLwB9ENn3/TaXCXtAa0SzLwocLN3qMjN+IatbjvtCX92BjNXsY6YWg9Eb7F3Wy255g==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-3.0.3.tgz", + "integrity": "sha512-91hoBHKk1C4pGeD+oE9Ld222k2GNQEAsI5AElqR8iLLWNrmZR2LPP8B0h8dpld9u7kro5IEUB3pUb0DJ3n1cRQ==", "devOptional": true, "dependencies": { - "browser-sync-client": "^3.0.2", - "browser-sync-ui": "^3.0.2", + "browser-sync-client": "^3.0.3", + "browser-sync-ui": "^3.0.3", "bs-recipes": "1.3.4", "chalk": "4.1.2", "chokidar": "^3.5.1", @@ -6207,15 +6229,15 @@ "fs-extra": "3.0.1", "http-proxy": "^1.18.1", "immutable": "^3", - "micromatch": "^4.0.2", + "micromatch": "^4.0.8", "opn": "5.3.0", "portscanner": "2.2.0", "raw-body": "^2.3.2", "resp-modifier": "6.0.2", "rx": "4.1.0", - "send": "0.16.2", - "serve-index": "1.9.1", - "serve-static": "1.13.2", + "send": "^0.19.0", + "serve-index": "^1.9.1", + "serve-static": "^1.16.2", "server-destroy": "1.0.1", "socket.io": "^4.4.1", "ua-parser-js": "^1.0.33", @@ -6229,9 +6251,9 @@ } }, "node_modules/browser-sync-client": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-3.0.2.tgz", - "integrity": "sha512-tBWdfn9L0wd2Pjuz/NWHtNEKthVb1Y67vg8/qyGNtCqetNz5lkDkFnrsx5UhPNPYUO8vci50IWC/BhYaQskDiQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-3.0.3.tgz", + "integrity": "sha512-TOEXaMgYNjBYIcmX5zDlOdjEqCeCN/d7opf/fuyUD/hhGVCfP54iQIDhENCi012AqzYZm3BvuFl57vbwSTwkSQ==", "devOptional": true, "dependencies": { "etag": "1.8.1", @@ -6243,9 +6265,9 @@ } }, "node_modules/browser-sync-ui": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-3.0.2.tgz", - "integrity": "sha512-V3FwWAI+abVbFLTyJjXJlCMBwjc3GXf/BPGfwO2fMFACWbIGW9/4SrBOFYEOOtqzCjQE0Di+U3VIb7eES4omNA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-3.0.3.tgz", + "integrity": "sha512-FcGWo5lP5VodPY6O/f4pXQy5FFh4JK0f2/fTBsp0Lx1NtyBWs/IfPPJbW8m1ujTW/2r07oUXKTF2LYZlCZktjw==", "devOptional": true, "dependencies": { "async-each-series": "0.1.1", @@ -6390,30 +6412,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "devOptional": true }, - "node_modules/browser-sync/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "devOptional": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/browser-sync/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "devOptional": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/browser-sync/node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==", - "devOptional": true - }, "node_modules/browser-sync/node_modules/fs-extra": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", @@ -6434,27 +6432,6 @@ "node": ">=8" } }, - "node_modules/browser-sync/node_modules/http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", - "devOptional": true, - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/browser-sync/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "devOptional": true - }, "node_modules/browser-sync/node_modules/jsonfile": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", @@ -6464,75 +6441,6 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/browser-sync/node_modules/mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", - "devOptional": true, - "bin": { - "mime": "cli.js" - } - }, - "node_modules/browser-sync/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "devOptional": true - }, - "node_modules/browser-sync/node_modules/send": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", - "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", - "devOptional": true, - "dependencies": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.6.2", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/browser-sync/node_modules/serve-static": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", - "devOptional": true, - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.2", - "send": "0.16.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/browser-sync/node_modules/setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "devOptional": true - }, - "node_modules/browser-sync/node_modules/statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", - "devOptional": true, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/browser-sync/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -7673,9 +7581,9 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "engines": { "node": ">= 0.6" } @@ -8045,13 +7953,13 @@ "peer": true }, "node_modules/cypress": { - "version": "13.14.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.14.0.tgz", - "integrity": "sha512-r0+nhd033x883YL6068futewUsl02Q7rWiinyAAIBDW/OOTn+UMILWgNuCiY3vtJjd53efOqq5R9dctQk/rKiw==", + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.15.0.tgz", + "integrity": "sha512-53aO7PwOfi604qzOkCSzNlWquCynLlKE/rmmpSPcziRH6LNfaDUAklQT6WJIsD8ywxlIy+uVZsnTMCCQVd2kTw==", "hasInstallScript": true, "optional": true, "dependencies": { - "@cypress/request": "^3.0.1", + "@cypress/request": "^3.0.4", "@cypress/xvfb": "^1.2.4", "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", @@ -8884,9 +8792,9 @@ } }, "node_modules/engine.io": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", - "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz", + "integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==", "devOptional": true, "dependencies": { "@types/cookie": "^0.4.1", @@ -8894,7 +8802,7 @@ "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", - "cookie": "~0.4.1", + "cookie": "~0.7.2", "cors": "~2.8.5", "debug": "~4.3.1", "engine.io-parser": "~5.2.1", @@ -8905,16 +8813,16 @@ } }, "node_modules/engine.io-client": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.4.tgz", - "integrity": "sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==", + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.1.tgz", + "integrity": "sha512-aYuoak7I+R83M/BBPIOs2to51BmFIpC1wZe6zZzMrT2llVsHy5cvcmdsJgP2Qz6smHu+sD9oexiSUAVd8OfBPw==", "devOptional": true, "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1", "engine.io-parser": "~5.2.1", "ws": "~8.17.1", - "xmlhttprequest-ssl": "~2.0.0" + "xmlhttprequest-ssl": "~2.1.1" } }, "node_modules/engine.io-parser": { @@ -8927,9 +8835,9 @@ } }, "node_modules/engine.io/node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "devOptional": true, "engines": { "node": ">= 0.6" @@ -9824,16 +9732,16 @@ "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==" }, "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -9896,20 +9804,6 @@ "node": ">= 0.8" } }, - "node_modules/express/node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/express/node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -10305,17 +10199,17 @@ } }, "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "optional": true, "dependencies": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", + "combined-stream": "^1.0.8", "mime-types": "^2.1.12" }, "engines": { - "node": ">= 0.12" + "node": ">= 6" } }, "node_modules/forwarded": { @@ -10957,14 +10851,14 @@ } }, "node_modules/http-signature": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", - "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.4.0.tgz", + "integrity": "sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==", "optional": true, "dependencies": { "assert-plus": "^1.0.0", "jsprim": "^2.0.2", - "sshpk": "^1.14.1" + "sshpk": "^1.18.0" }, "engines": { "node": ">=0.10" @@ -14737,12 +14631,11 @@ } }, "node_modules/qs": { - "version": "6.10.4", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.4.tgz", - "integrity": "sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==", - "optional": true, + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -15198,11 +15091,11 @@ } }, "node_modules/rollup": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", - "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", + "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.6" }, "bin": { "rollup": "dist/bin/rollup" @@ -15212,19 +15105,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.13.0", - "@rollup/rollup-android-arm64": "4.13.0", - "@rollup/rollup-darwin-arm64": "4.13.0", - "@rollup/rollup-darwin-x64": "4.13.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", - "@rollup/rollup-linux-arm64-gnu": "4.13.0", - "@rollup/rollup-linux-arm64-musl": "4.13.0", - "@rollup/rollup-linux-riscv64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-musl": "4.13.0", - "@rollup/rollup-win32-arm64-msvc": "4.13.0", - "@rollup/rollup-win32-ia32-msvc": "4.13.0", - "@rollup/rollup-win32-x64-msvc": "4.13.0", + "@rollup/rollup-android-arm-eabi": "4.24.0", + "@rollup/rollup-android-arm64": "4.24.0", + "@rollup/rollup-darwin-arm64": "4.24.0", + "@rollup/rollup-darwin-x64": "4.24.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", + "@rollup/rollup-linux-arm-musleabihf": "4.24.0", + "@rollup/rollup-linux-arm64-gnu": "4.24.0", + "@rollup/rollup-linux-arm64-musl": "4.24.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", + "@rollup/rollup-linux-riscv64-gnu": "4.24.0", + "@rollup/rollup-linux-s390x-gnu": "4.24.0", + "@rollup/rollup-linux-x64-gnu": "4.24.0", + "@rollup/rollup-linux-x64-musl": "4.24.0", + "@rollup/rollup-win32-arm64-msvc": "4.24.0", + "@rollup/rollup-win32-ia32-msvc": "4.24.0", + "@rollup/rollup-win32-x64-msvc": "4.24.0", "fsevents": "~2.3.2" } }, @@ -15883,21 +15779,21 @@ } }, "node_modules/socket.io": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.1.tgz", - "integrity": "sha512-W+utHys2w//dhFjy7iQQu9sGd3eokCjGbl2r59tyLqNiJJBdIebn3GAKEXBr3osqHTObJi2die/25bCx2zsaaw==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.0.tgz", + "integrity": "sha512-8U6BEgGjQOfGz3HHTYaC/L1GaxDCJ/KM0XTkJly0EhZ5U/du9uNEZy4ZgYzEzIqlx2CMm25CrCqr1ck899eLNA==", "devOptional": true, "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", "cors": "~2.8.5", "debug": "~4.3.2", - "engine.io": "~6.5.0", + "engine.io": "~6.6.0", "socket.io-adapter": "~2.5.2", "socket.io-parser": "~4.2.4" }, "engines": { - "node": ">=10.0.0" + "node": ">=10.2.0" } }, "node_modules/socket.io-adapter": { @@ -15911,14 +15807,14 @@ } }, "node_modules/socket.io-client": { - "version": "4.7.5", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz", - "integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.0.tgz", + "integrity": "sha512-C0jdhD5yQahMws9alf/yvtsMGTaIDBnZ8Rb5HU56svyq0l5LIrGzIDZZD5pHQlmzxLuU91Gz+VpQMKgCTNYtkw==", "devOptional": true, "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.2", - "engine.io-client": "~6.5.2", + "engine.io-client": "~6.6.1", "socket.io-parser": "~4.2.4" }, "engines": { @@ -16129,9 +16025,9 @@ "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" }, "node_modules/sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", "optional": true, "dependencies": { "asn1": "~0.2.3", @@ -16725,9 +16621,9 @@ } }, "node_modules/tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", "optional": true, "dependencies": { "psl": "^1.1.33", @@ -16893,9 +16789,9 @@ } }, "node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", + "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==" }, "node_modules/tuf-js": { "version": "2.2.0", @@ -17799,20 +17695,6 @@ "proxy-from-env": "^1.1.0" } }, - "node_modules/wait-on/node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "optional": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/wait-on/node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -18294,9 +18176,9 @@ } }, "node_modules/xmlhttprequest-ssl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", - "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.1.tgz", + "integrity": "sha512-ptjR8YSJIXoA3Mbv5po7RtSYHO6mZr8s7i5VGmEk7QY2pQWyT1o0N+W1gKbOyJPUCGXGnuw0wqe8f0L6Y0ny7g==", "devOptional": true, "engines": { "node": ">=0.4.0" @@ -20466,9 +20348,9 @@ } }, "@cypress/request": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.1.tgz", - "integrity": "sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.5.tgz", + "integrity": "sha512-v+XHd9XmWbufxF1/bTaVm2yhbxY+TB4YtWRqF2zaXBlDNMkls34KiATz0AVDLavL3iB6bQk9/7n3oY1EoLSWGA==", "optional": true, "requires": { "aws-sign2": "~0.7.0", @@ -20477,14 +20359,14 @@ "combined-stream": "~1.0.6", "extend": "~3.0.2", "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "http-signature": "~1.3.6", + "form-data": "~4.0.0", + "http-signature": "~1.4.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.19", "performance-now": "^2.1.0", - "qs": "6.10.4", + "qs": "6.13.0", "safe-buffer": "^5.1.2", "tough-cookie": "^4.1.3", "tunnel-agent": "^0.6.0", @@ -21229,81 +21111,99 @@ "peer": true }, "@rollup/rollup-android-arm-eabi": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", - "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", + "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", "optional": true }, "@rollup/rollup-android-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", - "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", + "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", "optional": true }, "@rollup/rollup-darwin-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", - "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", + "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", "optional": true }, "@rollup/rollup-darwin-x64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", - "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", + "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", "optional": true }, "@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", - "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", + "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", + "optional": true + }, + "@rollup/rollup-linux-arm-musleabihf": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", + "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", "optional": true }, "@rollup/rollup-linux-arm64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", - "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", + "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", "optional": true }, "@rollup/rollup-linux-arm64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", - "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", + "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", + "optional": true + }, + "@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", + "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", "optional": true }, "@rollup/rollup-linux-riscv64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", - "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", + "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", + "optional": true + }, + "@rollup/rollup-linux-s390x-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", + "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", "optional": true }, "@rollup/rollup-linux-x64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", - "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", + "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", "optional": true }, "@rollup/rollup-linux-x64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", - "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", + "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", "optional": true }, "@rollup/rollup-win32-arm64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", - "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", + "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", "optional": true }, "@rollup/rollup-win32-ia32-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", - "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", + "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", "optional": true }, "@rollup/rollup-win32-x64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", - "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", + "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", "optional": true }, "@schematics/angular": { @@ -21571,9 +21471,9 @@ "devOptional": true }, "@types/cors": { - "version": "2.8.13", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", - "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", "devOptional": true, "requires": { "@types/node": "*" @@ -21607,9 +21507,9 @@ } }, "@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" }, "@types/express": { "version": "4.17.13", @@ -22369,9 +22269,9 @@ "optional": true }, "aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", "optional": true }, "axios": { @@ -22583,14 +22483,6 @@ "requires": { "ee-first": "1.1.1" } - }, - "qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "requires": { - "side-channel": "^1.0.6" - } } } }, @@ -22680,13 +22572,13 @@ } }, "browser-sync": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-3.0.2.tgz", - "integrity": "sha512-PC9c7aWJFVR4IFySrJxOqLwB9ENn3/TaXCXtAa0SzLwocLN3qMjN+IatbjvtCX92BjNXsY6YWg9Eb7F3Wy255g==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-3.0.3.tgz", + "integrity": "sha512-91hoBHKk1C4pGeD+oE9Ld222k2GNQEAsI5AElqR8iLLWNrmZR2LPP8B0h8dpld9u7kro5IEUB3pUb0DJ3n1cRQ==", "devOptional": true, "requires": { - "browser-sync-client": "^3.0.2", - "browser-sync-ui": "^3.0.2", + "browser-sync-client": "^3.0.3", + "browser-sync-ui": "^3.0.3", "bs-recipes": "1.3.4", "chalk": "4.1.2", "chokidar": "^3.5.1", @@ -22700,15 +22592,15 @@ "fs-extra": "3.0.1", "http-proxy": "^1.18.1", "immutable": "^3", - "micromatch": "^4.0.2", + "micromatch": "^4.0.8", "opn": "5.3.0", "portscanner": "2.2.0", "raw-body": "^2.3.2", "resp-modifier": "6.0.2", "rx": "4.1.0", - "send": "0.16.2", - "serve-index": "1.9.1", - "serve-static": "1.13.2", + "send": "^0.19.0", + "serve-index": "^1.9.1", + "serve-static": "^1.16.2", "server-destroy": "1.0.1", "socket.io": "^4.4.1", "ua-parser-js": "^1.0.33", @@ -22760,27 +22652,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "devOptional": true }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "devOptional": true, - "requires": { - "ms": "2.0.0" - } - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "devOptional": true - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==", - "devOptional": true - }, "fs-extra": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", @@ -22798,24 +22669,6 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "devOptional": true }, - "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", - "devOptional": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "devOptional": true - }, "jsonfile": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", @@ -22825,63 +22678,6 @@ "graceful-fs": "^4.1.6" } }, - "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", - "devOptional": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "devOptional": true - }, - "send": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", - "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", - "devOptional": true, - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.6.2", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.4.0" - } - }, - "serve-static": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", - "devOptional": true, - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.2", - "send": "0.16.2" - } - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "devOptional": true - }, - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", - "devOptional": true - }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -22944,9 +22740,9 @@ } }, "browser-sync-client": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-3.0.2.tgz", - "integrity": "sha512-tBWdfn9L0wd2Pjuz/NWHtNEKthVb1Y67vg8/qyGNtCqetNz5lkDkFnrsx5UhPNPYUO8vci50IWC/BhYaQskDiQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-3.0.3.tgz", + "integrity": "sha512-TOEXaMgYNjBYIcmX5zDlOdjEqCeCN/d7opf/fuyUD/hhGVCfP54iQIDhENCi012AqzYZm3BvuFl57vbwSTwkSQ==", "devOptional": true, "requires": { "etag": "1.8.1", @@ -22955,9 +22751,9 @@ } }, "browser-sync-ui": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-3.0.2.tgz", - "integrity": "sha512-V3FwWAI+abVbFLTyJjXJlCMBwjc3GXf/BPGfwO2fMFACWbIGW9/4SrBOFYEOOtqzCjQE0Di+U3VIb7eES4omNA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-3.0.3.tgz", + "integrity": "sha512-FcGWo5lP5VodPY6O/f4pXQy5FFh4JK0f2/fTBsp0Lx1NtyBWs/IfPPJbW8m1ujTW/2r07oUXKTF2LYZlCZktjw==", "devOptional": true, "requires": { "async-each-series": "0.1.1", @@ -23827,9 +23623,9 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, "cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==" + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==" }, "cookie-signature": { "version": "1.0.6", @@ -24100,12 +23896,12 @@ "peer": true }, "cypress": { - "version": "13.14.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.14.0.tgz", - "integrity": "sha512-r0+nhd033x883YL6068futewUsl02Q7rWiinyAAIBDW/OOTn+UMILWgNuCiY3vtJjd53efOqq5R9dctQk/rKiw==", + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.15.0.tgz", + "integrity": "sha512-53aO7PwOfi604qzOkCSzNlWquCynLlKE/rmmpSPcziRH6LNfaDUAklQT6WJIsD8ywxlIy+uVZsnTMCCQVd2kTw==", "optional": true, "requires": { - "@cypress/request": "^3.0.1", + "@cypress/request": "^3.0.4", "@cypress/xvfb": "^1.2.4", "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", @@ -24765,9 +24561,9 @@ } }, "engine.io": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", - "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz", + "integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==", "devOptional": true, "requires": { "@types/cookie": "^0.4.1", @@ -24775,7 +24571,7 @@ "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", - "cookie": "~0.4.1", + "cookie": "~0.7.2", "cors": "~2.8.5", "debug": "~4.3.1", "engine.io-parser": "~5.2.1", @@ -24783,24 +24579,24 @@ }, "dependencies": { "cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "devOptional": true } } }, "engine.io-client": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.4.tgz", - "integrity": "sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==", + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.1.tgz", + "integrity": "sha512-aYuoak7I+R83M/BBPIOs2to51BmFIpC1wZe6zZzMrT2llVsHy5cvcmdsJgP2Qz6smHu+sD9oexiSUAVd8OfBPw==", "devOptional": true, "requires": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1", "engine.io-parser": "~5.2.1", "ws": "~8.17.1", - "xmlhttprequest-ssl": "~2.0.0" + "xmlhttprequest-ssl": "~2.1.1" } }, "engine.io-parser": { @@ -25491,16 +25287,16 @@ "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==" }, "express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -25554,14 +25350,6 @@ "ee-first": "1.1.1" } }, - "qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "requires": { - "side-channel": "^1.0.6" - } - }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -25853,13 +25641,13 @@ "optional": true }, "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "optional": true, "requires": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", + "combined-stream": "^1.0.8", "mime-types": "^2.1.12" } }, @@ -26321,14 +26109,14 @@ } }, "http-signature": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", - "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.4.0.tgz", + "integrity": "sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==", "optional": true, "requires": { "assert-plus": "^1.0.0", "jsprim": "^2.0.2", - "sshpk": "^1.14.1" + "sshpk": "^1.18.0" } }, "https-browserify": { @@ -29098,12 +28886,11 @@ } }, "qs": { - "version": "6.10.4", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.4.tgz", - "integrity": "sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==", - "optional": true, + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "requires": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" } }, "querystring": { @@ -29456,24 +29243,27 @@ } }, "rollup": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", - "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", + "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", "requires": { - "@rollup/rollup-android-arm-eabi": "4.13.0", - "@rollup/rollup-android-arm64": "4.13.0", - "@rollup/rollup-darwin-arm64": "4.13.0", - "@rollup/rollup-darwin-x64": "4.13.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", - "@rollup/rollup-linux-arm64-gnu": "4.13.0", - "@rollup/rollup-linux-arm64-musl": "4.13.0", - "@rollup/rollup-linux-riscv64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-musl": "4.13.0", - "@rollup/rollup-win32-arm64-msvc": "4.13.0", - "@rollup/rollup-win32-ia32-msvc": "4.13.0", - "@rollup/rollup-win32-x64-msvc": "4.13.0", - "@types/estree": "1.0.5", + "@rollup/rollup-android-arm-eabi": "4.24.0", + "@rollup/rollup-android-arm64": "4.24.0", + "@rollup/rollup-darwin-arm64": "4.24.0", + "@rollup/rollup-darwin-x64": "4.24.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", + "@rollup/rollup-linux-arm-musleabihf": "4.24.0", + "@rollup/rollup-linux-arm64-gnu": "4.24.0", + "@rollup/rollup-linux-arm64-musl": "4.24.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", + "@rollup/rollup-linux-riscv64-gnu": "4.24.0", + "@rollup/rollup-linux-s390x-gnu": "4.24.0", + "@rollup/rollup-linux-x64-gnu": "4.24.0", + "@rollup/rollup-linux-x64-musl": "4.24.0", + "@rollup/rollup-win32-arm64-msvc": "4.24.0", + "@rollup/rollup-win32-ia32-msvc": "4.24.0", + "@rollup/rollup-win32-x64-msvc": "4.24.0", + "@types/estree": "1.0.6", "fsevents": "~2.3.2" } }, @@ -29962,16 +29752,16 @@ "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==" }, "socket.io": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.1.tgz", - "integrity": "sha512-W+utHys2w//dhFjy7iQQu9sGd3eokCjGbl2r59tyLqNiJJBdIebn3GAKEXBr3osqHTObJi2die/25bCx2zsaaw==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.0.tgz", + "integrity": "sha512-8U6BEgGjQOfGz3HHTYaC/L1GaxDCJ/KM0XTkJly0EhZ5U/du9uNEZy4ZgYzEzIqlx2CMm25CrCqr1ck899eLNA==", "devOptional": true, "requires": { "accepts": "~1.3.4", "base64id": "~2.0.0", "cors": "~2.8.5", "debug": "~4.3.2", - "engine.io": "~6.5.0", + "engine.io": "~6.6.0", "socket.io-adapter": "~2.5.2", "socket.io-parser": "~4.2.4" } @@ -29987,14 +29777,14 @@ } }, "socket.io-client": { - "version": "4.7.5", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz", - "integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.0.tgz", + "integrity": "sha512-C0jdhD5yQahMws9alf/yvtsMGTaIDBnZ8Rb5HU56svyq0l5LIrGzIDZZD5pHQlmzxLuU91Gz+VpQMKgCTNYtkw==", "devOptional": true, "requires": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.2", - "engine.io-client": "~6.5.2", + "engine.io-client": "~6.6.1", "socket.io-parser": "~4.2.4" } }, @@ -30167,9 +29957,9 @@ "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" }, "sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", "optional": true, "requires": { "asn1": "~0.2.3", @@ -30615,9 +30405,9 @@ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, "tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", "optional": true, "requires": { "psl": "^1.1.33", @@ -30724,9 +30514,9 @@ } }, "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", + "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==" }, "tuf-js": { "version": "2.2.0", @@ -31248,17 +31038,6 @@ "proxy-from-env": "^1.1.0" } }, - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "optional": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, "proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -31584,9 +31363,9 @@ "integrity": "sha512-sID0rrVCqkVNUn8t6xuv9+6FViXjUVXq8H5rWOH2rz9fDNQEd4g0EA2XlcEdJXRz5BMEn4O1pJFdT+z4YHhoWw==" }, "xmlhttprequest-ssl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", - "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.1.tgz", + "integrity": "sha512-ptjR8YSJIXoA3Mbv5po7RtSYHO6mZr8s7i5VGmEk7QY2pQWyT1o0N+W1gKbOyJPUCGXGnuw0wqe8f0L6Y0ny7g==", "devOptional": true }, "xtend": { diff --git a/frontend/package.json b/frontend/package.json index 415ac74fe..6a0d7dc12 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -95,7 +95,7 @@ "esbuild": "^0.24.0", "tinyify": "^4.0.0", "tlite": "^0.1.9", - "tslib": "~2.7.0", + "tslib": "~2.8.0", "zone.js": "~0.14.4" }, "devDependencies": { @@ -105,7 +105,7 @@ "@typescript-eslint/eslint-plugin": "^7.4.0", "@typescript-eslint/parser": "^7.4.0", "eslint": "^8.57.0", - "browser-sync": "^3.0.0", + "browser-sync": "^3.0.3", "http-proxy-middleware": "~2.0.6", "prettier": "^3.0.0", "source-map-support": "^0.5.21", @@ -115,7 +115,7 @@ "optionalDependencies": { "@cypress/schematic": "^2.5.0", "@types/cypress": "^1.1.3", - "cypress": "^13.14.0", + "cypress": "^13.15.0", "cypress-fail-on-console-error": "~5.1.0", "cypress-wait-until": "^2.0.1", "mock-socket": "~9.3.1", diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 1f2e3f531..d1748312d 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -1,15 +1,15 @@ import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; -import { AppPreloadingStrategy } from './app.preloading-strategy' -import { BlockViewComponent } from './components/block-view/block-view.component'; -import { EightBlocksComponent } from './components/eight-blocks/eight-blocks.component'; -import { MempoolBlockViewComponent } from './components/mempool-block-view/mempool-block-view.component'; -import { ClockComponent } from './components/clock/clock.component'; -import { StatusViewComponent } from './components/status-view/status-view.component'; -import { AddressGroupComponent } from './components/address-group/address-group.component'; -import { TrackerComponent } from './components/tracker/tracker.component'; -import { AccelerateCheckout } from './components/accelerate-checkout/accelerate-checkout.component'; -import { TrackerGuard } from './route-guards'; +import { AppPreloadingStrategy } from '@app/app.preloading-strategy' +import { BlockViewComponent } from '@components/block-view/block-view.component'; +import { EightBlocksComponent } from '@components/eight-blocks/eight-blocks.component'; +import { MempoolBlockViewComponent } from '@components/mempool-block-view/mempool-block-view.component'; +import { ClockComponent } from '@components/clock/clock.component'; +import { StatusViewComponent } from '@components/status-view/status-view.component'; +import { AddressGroupComponent } from '@components/address-group/address-group.component'; +import { TrackerComponent } from '@components/tracker/tracker.component'; +import { AccelerateCheckout } from '@components/accelerate-checkout/accelerate-checkout.component'; +import { TrackerGuard } from '@app/route-guards'; const browserWindow = window || {}; // @ts-ignore @@ -22,16 +22,16 @@ let routes: Routes = [ { path: '', pathMatch: 'full', - loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), + loadChildren: () => import('@app/bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), data: { preload: true }, }, { path: '', - loadChildren: () => import('./master-page.module').then(m => m.MasterPageModule), + loadChildren: () => import('@app/master-page.module').then(m => m.MasterPageModule), data: { preload: true }, }, { - path: 'wallet', + path: 'widget/wallet', children: [], component: AddressGroupComponent, data: { @@ -45,7 +45,7 @@ let routes: Routes = [ }, { path: '', - loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), + loadChildren: () => import('@app/bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), data: { preload: true }, }, { @@ -60,12 +60,12 @@ let routes: Routes = [ { path: '', pathMatch: 'full', - loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), + loadChildren: () => import('@app/bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), data: { preload: true }, }, { path: '', - loadChildren: () => import('./master-page.module').then(m => m.MasterPageModule), + loadChildren: () => import('@app/master-page.module').then(m => m.MasterPageModule), data: { preload: true }, }, { @@ -83,7 +83,7 @@ let routes: Routes = [ }, { path: '', - loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), + loadChildren: () => import('@app/bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), data: { preload: true }, }, { @@ -103,16 +103,16 @@ let routes: Routes = [ { path: '', pathMatch: 'full', - loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), + loadChildren: () => import('@app/bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), data: { preload: true }, }, { path: '', - loadChildren: () => import('./master-page.module').then(m => m.MasterPageModule), + loadChildren: () => import('@app/master-page.module').then(m => m.MasterPageModule), data: { preload: true }, }, { - path: 'wallet', + path: 'widget/wallet', children: [], component: AddressGroupComponent, data: { @@ -126,7 +126,7 @@ let routes: Routes = [ }, { path: '', - loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), + loadChildren: () => import('@app/bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), data: { preload: true }, }, { @@ -138,22 +138,22 @@ let routes: Routes = [ { path: '', pathMatch: 'full', - loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), + loadChildren: () => import('@app/bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), data: { preload: true }, }, { path: 'tx', canMatch: [TrackerGuard], runGuardsAndResolvers: 'always', - loadChildren: () => import('./components/tracker/tracker.module').then(m => m.TrackerModule), + loadChildren: () => import('@components/tracker/tracker.module').then(m => m.TrackerModule), }, { path: '', - loadChildren: () => import('./master-page.module').then(m => m.MasterPageModule), + loadChildren: () => import('@app/master-page.module').then(m => m.MasterPageModule), data: { preload: true }, }, { - path: 'wallet', + path: 'widget/wallet', children: [], component: AddressGroupComponent, data: { @@ -165,19 +165,19 @@ let routes: Routes = [ children: [ { path: '', - loadChildren: () => import('./previews.module').then(m => m.PreviewsModule) + loadChildren: () => import('@app/previews.module').then(m => m.PreviewsModule) }, { path: 'testnet', - loadChildren: () => import('./previews.module').then(m => m.PreviewsModule) + loadChildren: () => import('@app/previews.module').then(m => m.PreviewsModule) }, { path: 'testnet4', - loadChildren: () => import('./previews.module').then(m => m.PreviewsModule) + loadChildren: () => import('@app/previews.module').then(m => m.PreviewsModule) }, { path: 'signet', - loadChildren: () => import('./previews.module').then(m => m.PreviewsModule) + loadChildren: () => import('@app/previews.module').then(m => m.PreviewsModule) }, ], }, @@ -212,7 +212,7 @@ let routes: Routes = [ }, { path: '', - loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), + loadChildren: () => import('@app/bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), data: { preload: true }, }, ]; @@ -225,16 +225,16 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { { path: '', pathMatch: 'full', - loadChildren: () => import('./liquid/liquid-graphs.module').then(m => m.LiquidGraphsModule), + loadChildren: () => import('@app/liquid/liquid-graphs.module').then(m => m.LiquidGraphsModule), data: { preload: true }, }, { path: '', - loadChildren: () => import ('./liquid/liquid-master-page.module').then(m => m.LiquidMasterPageModule), + loadChildren: () => import ('@app/liquid/liquid-master-page.module').then(m => m.LiquidMasterPageModule), data: { preload: true }, }, { - path: 'wallet', + path: 'widget/wallet', children: [], component: AddressGroupComponent, data: { @@ -248,7 +248,7 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { }, { path: '', - loadChildren: () => import('./liquid/liquid-graphs.module').then(m => m.LiquidGraphsModule), + loadChildren: () => import('@app/liquid/liquid-graphs.module').then(m => m.LiquidGraphsModule), data: { preload: true }, }, { @@ -260,16 +260,16 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { { path: '', pathMatch: 'full', - loadChildren: () => import('./liquid/liquid-graphs.module').then(m => m.LiquidGraphsModule), + loadChildren: () => import('@app/liquid/liquid-graphs.module').then(m => m.LiquidGraphsModule), data: { preload: true }, }, { path: '', - loadChildren: () => import ('./liquid/liquid-master-page.module').then(m => m.LiquidMasterPageModule), + loadChildren: () => import ('@app/liquid/liquid-master-page.module').then(m => m.LiquidMasterPageModule), data: { preload: true }, }, { - path: 'wallet', + path: 'widget/wallet', children: [], component: AddressGroupComponent, data: { @@ -281,11 +281,11 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { children: [ { path: '', - loadChildren: () => import('./previews.module').then(m => m.PreviewsModule) + loadChildren: () => import('@app/previews.module').then(m => m.PreviewsModule) }, { path: 'testnet', - loadChildren: () => import('./previews.module').then(m => m.PreviewsModule) + loadChildren: () => import('@app/previews.module').then(m => m.PreviewsModule) }, ], }, @@ -296,7 +296,7 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { }, { path: '', - loadChildren: () => import('./liquid/liquid-graphs.module').then(m => m.LiquidGraphsModule), + loadChildren: () => import('@app/liquid/liquid-graphs.module').then(m => m.LiquidGraphsModule), data: { preload: true }, }, ]; diff --git a/frontend/src/app/app.module.server.ts b/frontend/src/app/app.module.server.ts index 4149fa593..44cfe674d 100644 --- a/frontend/src/app/app.module.server.ts +++ b/frontend/src/app/app.module.server.ts @@ -2,11 +2,11 @@ import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { NgModule } from '@angular/core'; import { ServerModule } from '@angular/platform-server'; -import { ZONE_SERVICE } from './injection-tokens'; -import { AppModule } from './app.module'; -import { AppComponent } from './components/app/app.component'; -import { HttpCacheInterceptor } from './services/http-cache.interceptor'; -import { ZoneService } from './services/zone.service'; +import { ZONE_SERVICE } from '@app/injection-tokens'; +import { AppModule } from '@app/app.module'; +import { AppComponent } from '@components/app/app.component'; +import { HttpCacheInterceptor } from '@app/services/http-cache.interceptor'; +import { ZoneService } from '@app/services/zone.service'; @NgModule({ @@ -20,4 +20,4 @@ import { ZoneService } from './services/zone.service'; ], bootstrap: [AppComponent], }) -export class AppServerModule {} \ No newline at end of file +export class AppServerModule {} diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index d1129a602..7a97c27e3 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -2,36 +2,38 @@ import { BrowserModule } from '@angular/platform-browser'; import { ModuleWithProviders, NgModule } from '@angular/core'; import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { ZONE_SERVICE } from './injection-tokens'; -import { AppRoutingModule } from './app-routing.module'; -import { AppComponent } from './components/app/app.component'; -import { ElectrsApiService } from './services/electrs-api.service'; -import { StateService } from './services/state.service'; -import { CacheService } from './services/cache.service'; -import { PriceService } from './services/price.service'; -import { EnterpriseService } from './services/enterprise.service'; -import { WebsocketService } from './services/websocket.service'; -import { AudioService } from './services/audio.service'; -import { PreloadService } from './services/preload.service'; -import { SeoService } from './services/seo.service'; -import { OpenGraphService } from './services/opengraph.service'; -import { ZoneService } from './services/zone-shim.service'; -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 { TimeService } from './services/time.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'; -import { CapAddressPipe } from './shared/pipes/cap-address-pipe/cap-address-pipe'; -import { AppPreloadingStrategy } from './app.preloading-strategy'; -import { ServicesApiServices } from './services/services-api.service'; +import { ZONE_SERVICE } from '@app/injection-tokens'; +import { AppRoutingModule } from '@app/app-routing.module'; +import { AppComponent } from '@components/app/app.component'; +import { ElectrsApiService } from '@app/services/electrs-api.service'; +import { OrdApiService } from '@app/services/ord-api.service'; +import { StateService } from '@app/services/state.service'; +import { CacheService } from '@app/services/cache.service'; +import { PriceService } from '@app/services/price.service'; +import { EnterpriseService } from '@app/services/enterprise.service'; +import { WebsocketService } from '@app/services/websocket.service'; +import { AudioService } from '@app/services/audio.service'; +import { PreloadService } from '@app/services/preload.service'; +import { SeoService } from '@app/services/seo.service'; +import { OpenGraphService } from '@app/services/opengraph.service'; +import { ZoneService } from '@app/services/zone-shim.service'; +import { SharedModule } from '@app/shared/shared.module'; +import { StorageService } from '@app/services/storage.service'; +import { HttpCacheInterceptor } from '@app/services/http-cache.interceptor'; +import { LanguageService } from '@app/services/language.service'; +import { ThemeService } from '@app/services/theme.service'; +import { TimeService } from '@app/services/time.service'; +import { FiatShortenerPipe } from '@app/shared/pipes/fiat-shortener.pipe'; +import { FiatCurrencyPipe } from '@app/shared/pipes/fiat-currency.pipe'; +import { ShortenStringPipe } from '@app/shared/pipes/shorten-string-pipe/shorten-string.pipe'; +import { CapAddressPipe } from '@app/shared/pipes/cap-address-pipe/cap-address-pipe'; +import { AppPreloadingStrategy } from '@app/app.preloading-strategy'; +import { ServicesApiServices } from '@app/services/services-api.service'; import { DatePipe } from '@angular/common'; const providers = [ ElectrsApiService, + OrdApiService, StateService, CacheService, PriceService, diff --git a/frontend/src/app/bitcoin-graphs.module.ts b/frontend/src/app/bitcoin-graphs.module.ts index 710743245..f5b1557b1 100644 --- a/frontend/src/app/bitcoin-graphs.module.ts +++ b/frontend/src/app/bitcoin-graphs.module.ts @@ -1,13 +1,13 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Routes, RouterModule } from '@angular/router'; -import { MasterPageComponent } from './components/master-page/master-page.component'; +import { MasterPageComponent } from '@components/master-page/master-page.component'; const routes: Routes = [ { path: '', component: MasterPageComponent, - loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule), + loadChildren: () => import('@app/graphs/graphs.module').then(m => m.GraphsModule), data: { preload: true }, } ]; diff --git a/frontend/src/app/bitcoin.utils.ts b/frontend/src/app/bitcoin.utils.ts index ae522121c..b949cde3c 100644 --- a/frontend/src/app/bitcoin.utils.ts +++ b/frontend/src/app/bitcoin.utils.ts @@ -1,5 +1,5 @@ -import { Transaction, Vin } from './interfaces/electrs.interface'; -import { Hash } from './shared/sha256'; +import { Transaction, Vin } from '@interfaces/electrs.interface'; +import { Hash } from '@app/shared/sha256'; const P2SH_P2WPKH_COST = 21 * 4; // the WU cost for the non-witness part of P2SH-P2WPKH const P2SH_P2WSH_COST = 35 * 4; // the WU cost for the non-witness part of P2SH-P2WSH @@ -303,4 +303,4 @@ export async function calcScriptHash$(script: string): Promise { return hashArray .map((bytes) => bytes.toString(16).padStart(2, '0')) .join(''); -} \ No newline at end of file +} diff --git a/frontend/src/app/components/about/about-sponsors.component.ts b/frontend/src/app/components/about/about-sponsors.component.ts index 6a47c3bd4..f42944173 100644 --- a/frontend/src/app/components/about/about-sponsors.component.ts +++ b/frontend/src/app/components/about/about-sponsors.component.ts @@ -1,5 +1,5 @@ import { Component, Input } from '@angular/core'; -import { EnterpriseService } from '../../services/enterprise.service'; +import { EnterpriseService } from '@app/services/enterprise.service'; @Component({ selector: 'app-about-sponsors', diff --git a/frontend/src/app/components/about/about.component.html b/frontend/src/app/components/about/about.component.html index 406835572..40d6e1914 100644 --- a/frontend/src/app/components/about/about.component.html +++ b/frontend/src/app/components/about/about.component.html @@ -201,12 +201,17 @@ Leather + + + + Taproot Wizards +
-
+

Whale Sponsors

diff --git a/frontend/src/app/components/about/about.component.scss b/frontend/src/app/components/about/about.component.scss index 6a20239cc..6a76bf299 100644 --- a/frontend/src/app/components/about/about.component.scss +++ b/frontend/src/app/components/about/about.component.scss @@ -92,6 +92,13 @@ } } + .whale-sponsor { + img { + width: 70px; + height: 70px; + } + } + .alliances { margin-bottom: 100px; a { diff --git a/frontend/src/app/components/about/about.component.ts b/frontend/src/app/components/about/about.component.ts index 44bee5828..5963c371c 100644 --- a/frontend/src/app/components/about/about.component.ts +++ b/frontend/src/app/components/about/about.component.ts @@ -1,16 +1,16 @@ import { ChangeDetectionStrategy, Component, ElementRef, Inject, LOCALE_ID, OnInit, ViewChild } from '@angular/core'; -import { WebsocketService } from '../../services/websocket.service'; -import { SeoService } from '../../services/seo.service'; -import { OpenGraphService } from '../../services/opengraph.service'; -import { StateService } from '../../services/state.service'; +import { WebsocketService } from '@app/services/websocket.service'; +import { SeoService } from '@app/services/seo.service'; +import { OpenGraphService } from '@app/services/opengraph.service'; +import { StateService } from '@app/services/state.service'; import { Observable } from 'rxjs'; -import { ApiService } from '../../services/api.service'; -import { IBackendInfo } from '../../interfaces/websocket.interface'; +import { ApiService } from '@app/services/api.service'; +import { IBackendInfo } from '@interfaces/websocket.interface'; import { Router, ActivatedRoute } from '@angular/router'; import { map, share, tap } from 'rxjs/operators'; -import { ITranslators } from '../../interfaces/node-api.interface'; +import { ITranslators } from '@interfaces/node-api.interface'; import { DOCUMENT } from '@angular/common'; -import { EnterpriseService } from '../../services/enterprise.service'; +import { EnterpriseService } from '@app/services/enterprise.service'; @Component({ selector: 'app-about', diff --git a/frontend/src/app/components/about/about.module.ts b/frontend/src/app/components/about/about.module.ts index 7e8ed42d0..8324876b1 100644 --- a/frontend/src/app/components/about/about.module.ts +++ b/frontend/src/app/components/about/about.module.ts @@ -1,9 +1,9 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Routes, RouterModule } from '@angular/router'; -import { AboutComponent } from './about.component'; -import { AboutSponsorsComponent } from './about-sponsors.component'; -import { SharedModule } from '../../shared/shared.module'; +import { AboutComponent } from '@components/about/about.component'; +import { AboutSponsorsComponent } from '@components/about/about-sponsors.component'; +import { SharedModule } from '@app/shared/shared.module'; const routes: Routes = [ { diff --git a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts index 162594cd6..9d2d2ad46 100644 --- a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts +++ b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts @@ -1,16 +1,16 @@ /* eslint-disable no-console */ import { Component, OnInit, OnDestroy, Output, EventEmitter, Input, ChangeDetectorRef, SimpleChanges, HostListener } from '@angular/core'; import { Subscription, tap, of, catchError, Observable, switchMap } from 'rxjs'; -import { ServicesApiServices } from '../../services/services-api.service'; -import { md5, insecureRandomUUID } from '../../shared/common.utils'; -import { StateService } from '../../services/state.service'; -import { AudioService } from '../../services/audio.service'; -import { ETA, EtaService } from '../../services/eta.service'; -import { Transaction } from '../../interfaces/electrs.interface'; -import { MiningStats } from '../../services/mining.service'; -import { IAuth, AuthServiceMempool } from '../../services/auth.service'; -import { EnterpriseService } from '../../services/enterprise.service'; -import { ApiService } from '../../services/api.service'; +import { ServicesApiServices } from '@app/services/services-api.service'; +import { md5, insecureRandomUUID } from '@app/shared/common.utils'; +import { StateService } from '@app/services/state.service'; +import { AudioService } from '@app/services/audio.service'; +import { ETA, EtaService } from '@app/services/eta.service'; +import { Transaction } from '@interfaces/electrs.interface'; +import { MiningStats } from '@app/services/mining.service'; +import { IAuth, AuthServiceMempool } from '@app/services/auth.service'; +import { EnterpriseService } from '@app/services/enterprise.service'; +import { ApiService } from '@app/services/api.service'; import { isDevMode } from '@angular/core'; export type PaymentMethod = 'balance' | 'bitcoin' | 'cashapp' | 'applePay' | 'googlePay'; @@ -84,13 +84,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { timePaid: number = 0; // time acceleration requested math = Math; isMobile: boolean = window.innerWidth <= 767.98; - isProdDomain = ['mempool.space', - 'mempool-staging.va1.mempool.space', - 'mempool-staging.fmt.mempool.space', - 'mempool-staging.fra.mempool.space', - 'mempool-staging.tk7.mempool.space', - 'mempool-staging.sg1.mempool.space' - ].indexOf(document.location.hostname) > -1; + isProdDomain = false; private _step: CheckoutStep = 'summary'; simpleMode: boolean = true; @@ -143,6 +137,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { private authService: AuthServiceMempool, private enterpriseService: EnterpriseService, ) { + this.isProdDomain = this.stateService.env.PROD_DOMAINS.indexOf(document.location.hostname) > -1; this.accelerationUUID = insecureRandomUUID(); // Check if Apple Pay available @@ -374,6 +369,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { this.selectFeeRateIndex = index; this.userBid = Math.max(0, fee); this.cost = this.userBid + this.estimate.mempoolBaseFee + this.estimate.vsizeFee; + this.validateChoice(); } } @@ -525,7 +521,8 @@ export class AccelerateCheckout implements OnInit, OnDestroy { tokenResult.token, cardTag, `accelerator-${this.tx.txid.substring(0, 15)}-${Math.round(new Date().getTime() / 1000)}`, - this.accelerationUUID + this.accelerationUUID, + costUSD ).subscribe({ next: () => { this.processing = false; @@ -624,7 +621,8 @@ export class AccelerateCheckout implements OnInit, OnDestroy { tokenResult.token, cardTag, `accelerator-${this.tx.txid.substring(0, 15)}-${Math.round(new Date().getTime() / 1000)}`, - this.accelerationUUID + this.accelerationUUID, + costUSD ).subscribe({ next: () => { this.processing = false; @@ -714,7 +712,8 @@ export class AccelerateCheckout implements OnInit, OnDestroy { tokenResult.token, tokenResult.details.cashAppPay.cashtag, tokenResult.details.cashAppPay.referenceId, - this.accelerationUUID + this.accelerationUUID, + costUSD ).subscribe({ next: () => { this.processing = false; diff --git a/frontend/src/app/components/accelerate-checkout/accelerate-fee-graph.component.ts b/frontend/src/app/components/accelerate-checkout/accelerate-fee-graph.component.ts index 393add6ca..5890e6582 100644 --- a/frontend/src/app/components/accelerate-checkout/accelerate-fee-graph.component.ts +++ b/frontend/src/app/components/accelerate-checkout/accelerate-fee-graph.component.ts @@ -1,6 +1,6 @@ import { Component, Input, Output, OnChanges, EventEmitter, HostListener, OnInit, ViewChild, ElementRef, AfterViewInit, OnDestroy, ChangeDetectorRef } from '@angular/core'; -import { Transaction } from '../../interfaces/electrs.interface'; -import { AccelerationEstimate, RateOption } from './accelerate-checkout.component'; +import { Transaction } from '@interfaces/electrs.interface'; +import { AccelerationEstimate, RateOption } from '@components/accelerate-checkout/accelerate-checkout.component'; interface GraphBar { rate: number; diff --git a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html index 935a4ce90..67953ce9a 100644 --- a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html +++ b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.html @@ -9,7 +9,7 @@
@if (eta) { - ~ + ~ }
@@ -48,8 +48,6 @@
- } @else if (standardETA && !tx.status.confirmed) { - }
diff --git a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.ts b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.ts index b79a7f20f..0e84d0192 100644 --- a/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.ts +++ b/frontend/src/app/components/acceleration-timeline/acceleration-timeline.component.ts @@ -1,8 +1,8 @@ import { Component, Input, OnInit, OnChanges, HostListener } from '@angular/core'; -import { ETA } from '../../services/eta.service'; -import { Transaction } from '../../interfaces/electrs.interface'; -import { Acceleration, SinglePoolStats } from '../../interfaces/node-api.interface'; -import { MiningService } from '../../services/mining.service'; +import { ETA } from '@app/services/eta.service'; +import { Transaction } from '@interfaces/electrs.interface'; +import { Acceleration, SinglePoolStats } from '@interfaces/node-api.interface'; +import { MiningService } from '@app/services/mining.service'; @Component({ selector: 'app-acceleration-timeline', @@ -11,19 +11,14 @@ import { MiningService } from '../../services/mining.service'; }) export class AccelerationTimelineComponent implements OnInit, OnChanges { @Input() transactionTime: number; + @Input() acceleratedAt: number; @Input() tx: Transaction; @Input() accelerationInfo: Acceleration; @Input() eta: ETA; - // A mined transaction has standard ETA and accelerated ETA undefined - // A transaction in mempool has either standardETA defined (if accelerated) or acceleratedETA defined (if not accelerated yet) - @Input() standardETA: number; - @Input() acceleratedETA: number; - acceleratedAt: number; now: number; accelerateRatio: number; useAbsoluteTime: boolean = false; - interval: number; firstSeenToAccelerated: number; acceleratedToMined: number; @@ -36,30 +31,17 @@ export class AccelerationTimelineComponent implements OnInit, OnChanges { ) {} ngOnInit(): void { - this.acceleratedAt = this.tx.acceleratedAt ?? new Date().getTime() / 1000; + this.updateTimes(); this.miningService.getPools().subscribe(pools => { for (const pool of pools) { this.poolsData[pool.unique_id] = pool; } }); - - this.updateTimes(); - this.interval = window.setInterval(this.updateTimes.bind(this), 60000); } ngOnChanges(changes): void { - // Hide standard ETA while we don't have a proper standard ETA calculation, see https://github.com/mempool/mempool/issues/65 - - // if (changes?.eta?.currentValue || changes?.standardETA?.currentValue || changes?.acceleratedETA?.currentValue) { - // if (changes?.eta?.currentValue) { - // if (changes?.acceleratedETA?.currentValue) { - // this.accelerateRatio = Math.floor((Math.floor(changes.eta.currentValue.time / 1000) - this.now) / (Math.floor(changes.acceleratedETA.currentValue / 1000) - this.now)); - // } else if (changes?.standardETA?.currentValue) { - // this.accelerateRatio = Math.floor((Math.floor(changes.standardETA.currentValue / 1000) - this.now) / (Math.floor(changes.eta.currentValue.time / 1000) - this.now)); - // } - // } - // } + this.updateTimes(); } updateTimes(): void { @@ -68,10 +50,6 @@ export class AccelerationTimelineComponent implements OnInit, OnChanges { this.firstSeenToAccelerated = Math.max(0, this.acceleratedAt - this.transactionTime); this.acceleratedToMined = Math.max(0, this.tx.status.block_time - this.acceleratedAt); } - - ngOnDestroy(): void { - clearInterval(this.interval); - } onHover(event, status: string): void { this.tooltipPosition = { x: event.clientX, y: event.clientY }; diff --git a/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.ts b/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.ts index 68a2bdd52..6a99edbf1 100644 --- a/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.ts +++ b/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.ts @@ -1,18 +1,18 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, NgZone, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core'; -import { EChartsOption } from '../../../graphs/echarts'; +import { EChartsOption } from '@app/graphs/echarts'; import { Observable, Subject, Subscription, combineLatest, fromEvent, merge, share } from 'rxjs'; import { startWith, switchMap, tap } from 'rxjs/operators'; -import { SeoService } from '../../../services/seo.service'; +import { SeoService } from '@app/services/seo.service'; import { formatNumber } from '@angular/common'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { download, formatterXAxis, formatterXAxisLabel, formatterXAxisTimeCategory } from '../../../shared/graphs.utils'; -import { StorageService } from '../../../services/storage.service'; -import { MiningService } from '../../../services/mining.service'; +import { download, formatterXAxis, formatterXAxisLabel, formatterXAxisTimeCategory } from '@app/shared/graphs.utils'; +import { StorageService } from '@app/services/storage.service'; +import { MiningService } from '@app/services/mining.service'; import { ActivatedRoute, Router } from '@angular/router'; -import { Acceleration } from '../../../interfaces/node-api.interface'; -import { ServicesApiServices } from '../../../services/services-api.service'; -import { StateService } from '../../../services/state.service'; -import { RelativeUrlPipe } from '../../../shared/pipes/relative-url/relative-url.pipe'; +import { Acceleration } from '@interfaces/node-api.interface'; +import { ServicesApiServices } from '@app/services/services-api.service'; +import { StateService } from '@app/services/state.service'; +import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe'; @Component({ selector: 'app-acceleration-fees-graph', diff --git a/frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.ts b/frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.ts index 392f1392b..65a1e4eb5 100644 --- a/frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.ts +++ b/frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, Input, OnChanges, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; -import { ServicesApiServices } from '../../../services/services-api.service'; +import { ServicesApiServices } from '@app/services/services-api.service'; export type AccelerationStats = { totalRequested: number; diff --git a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html index ffd8e9c3d..5ac288b2e 100644 --- a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html +++ b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html @@ -64,7 +64,7 @@ Pending Completed ⌛ Mined ⌛ - Failed ⌛ + Canceled ⌛ diff --git a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.ts b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.ts index a334f096a..ee5303530 100644 --- a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.ts +++ b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.ts @@ -1,12 +1,12 @@ import { Component, OnInit, ChangeDetectionStrategy, Input, ChangeDetectorRef, OnDestroy, Inject, LOCALE_ID } from '@angular/core'; import { BehaviorSubject, Observable, Subscription, catchError, combineLatest, filter, of, switchMap, tap, throttleTime, timer } from 'rxjs'; -import { Acceleration, BlockExtended, SinglePoolStats } from '../../../interfaces/node-api.interface'; -import { StateService } from '../../../services/state.service'; -import { WebsocketService } from '../../../services/websocket.service'; -import { ServicesApiServices } from '../../../services/services-api.service'; -import { SeoService } from '../../../services/seo.service'; +import { Acceleration, BlockExtended, SinglePoolStats } from '@interfaces/node-api.interface'; +import { StateService } from '@app/services/state.service'; +import { WebsocketService } from '@app/services/websocket.service'; +import { ServicesApiServices } from '@app/services/services-api.service'; +import { SeoService } from '@app/services/seo.service'; import { ActivatedRoute, Router } from '@angular/router'; -import { MiningService } from '../../../services/mining.service'; +import { MiningService } from '@app/services/mining.service'; @Component({ selector: 'app-accelerations-list', @@ -151,4 +151,4 @@ export class AccelerationsListComponent implements OnInit, OnDestroy { this.paramSubscription?.unsubscribe(); this.keyNavigationSubscription?.unsubscribe(); } -} \ No newline at end of file +} diff --git a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts index d84c6e97c..ab7d9122e 100644 --- a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts +++ b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts @@ -1,18 +1,18 @@ import { ChangeDetectionStrategy, Component, HostListener, Inject, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core'; -import { SeoService } from '../../../services/seo.service'; -import { OpenGraphService } from '../../../services/opengraph.service'; -import { WebsocketService } from '../../../services/websocket.service'; -import { Acceleration, BlockExtended } from '../../../interfaces/node-api.interface'; -import { StateService } from '../../../services/state.service'; +import { SeoService } from '@app/services/seo.service'; +import { OpenGraphService } from '@app/services/opengraph.service'; +import { WebsocketService } from '@app/services/websocket.service'; +import { Acceleration, BlockExtended } from '@interfaces/node-api.interface'; +import { StateService } from '@app/services/state.service'; import { Observable, Subscription, catchError, combineLatest, distinctUntilChanged, map, of, share, switchMap, tap } from 'rxjs'; -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, 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'; +import { Color } from '@components/block-overview-graph/sprite-types'; +import { hexToColor } from '@components/block-overview-graph/utils'; +import TxView from '@components/block-overview-graph/tx-view'; +import { feeLevels, defaultMempoolFeeColors, contrastMempoolFeeColors } from '@app/app.constants'; +import { ServicesApiServices } from '@app/services/services-api.service'; +import { detectWebGL } from '@app/shared/graphs.utils'; +import { AudioService } from '@app/services/audio.service'; +import { ThemeService } from '@app/services/theme.service'; const acceleratedColor: Color = hexToColor('8F5FF6'); const normalColors = defaultMempoolFeeColors.map(hex => hexToColor(hex + '5F')); diff --git a/frontend/src/app/components/acceleration/active-acceleration-box/active-acceleration-box.component.ts b/frontend/src/app/components/acceleration/active-acceleration-box/active-acceleration-box.component.ts index fb727c1a4..739760017 100644 --- a/frontend/src/app/components/acceleration/active-acceleration-box/active-acceleration-box.component.ts +++ b/frontend/src/app/components/acceleration/active-acceleration-box/active-acceleration-box.component.ts @@ -1,8 +1,8 @@ import { Component, ChangeDetectionStrategy, Input, Output, OnChanges, SimpleChanges, EventEmitter, ChangeDetectorRef } from '@angular/core'; -import { Transaction } from '../../../interfaces/electrs.interface'; -import { Acceleration, SinglePoolStats } from '../../../interfaces/node-api.interface'; -import { EChartsOption, PieSeriesOption } from '../../../graphs/echarts'; -import { MiningStats } from '../../../services/mining.service'; +import { Transaction } from '@interfaces/electrs.interface'; +import { Acceleration, SinglePoolStats } from '@interfaces/node-api.interface'; +import { EChartsOption, PieSeriesOption } from '@app/graphs/echarts'; +import { MiningStats } from '@app/services/mining.service'; function lighten(color, p): { r, g, b } { return { @@ -76,15 +76,21 @@ export class ActiveAccelerationBox implements OnChanges { acceleratingPools.forEach((poolId, index) => { const pool = pools[poolId]; const poolShare = ((pool.lastEstimatedHashrate / this.miningStats.lastEstimatedHashrate) * 100).toFixed(1); + let color = 'white'; + if (index >= firstSignificantPool) { + if (numSignificantPools > 1) { + color = toRGB(lighten({ r: 147, g: 57, b: 244 }, 1 - (index - firstSignificantPool) / Math.max((numSignificantPools - 1), 1))); + } else { + color = toRGB({ r: 147, g: 57, b: 244 }); + } + } data.push(getDataItem( pool.lastEstimatedHashrate, - index >= firstSignificantPool - ? toRGB(lighten({ r: 147, g: 57, b: 244 }, 1 - (index - firstSignificantPool) / (numSignificantPools - 1))) - : 'white', + color, `${pool.name} (${poolShare}%)`, true, ) as PieSeriesOption); - }) + }); this.acceleratedByPercentage = ((totalAcceleratedHashrate / this.miningStats.lastEstimatedHashrate) * 100).toFixed(1) + '%'; const notAcceleratedByPercentage = ((1 - (totalAcceleratedHashrate / this.miningStats.lastEstimatedHashrate)) * 100).toFixed(1) + '%'; data.push(getDataItem( @@ -148,4 +154,4 @@ export class ActiveAccelerationBox implements OnChanges { onToggleCpfp(): void { this.toggleCpfp.emit(); } -} \ No newline at end of file +} diff --git a/frontend/src/app/components/acceleration/pending-stats/pending-stats.component.ts b/frontend/src/app/components/acceleration/pending-stats/pending-stats.component.ts index 568e60d7e..ed63ad098 100644 --- a/frontend/src/app/components/acceleration/pending-stats/pending-stats.component.ts +++ b/frontend/src/app/components/acceleration/pending-stats/pending-stats.component.ts @@ -1,9 +1,9 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { Observable, of } from 'rxjs'; import { switchMap } from 'rxjs/operators'; -import { Acceleration } from '../../../interfaces/node-api.interface'; -import { StateService } from '../../../services/state.service'; -import { WebsocketService } from '../../../services/websocket.service'; +import { Acceleration } from '@interfaces/node-api.interface'; +import { StateService } from '@app/services/state.service'; +import { WebsocketService } from '@app/services/websocket.service'; @Component({ selector: 'app-pending-stats', diff --git a/frontend/src/app/components/address-graph/address-graph.component.ts b/frontend/src/app/components/address-graph/address-graph.component.ts index 6d40a8ebb..e8762fbec 100644 --- a/frontend/src/app/components/address-graph/address-graph.component.ts +++ b/frontend/src/app/components/address-graph/address-graph.component.ts @@ -1,16 +1,16 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, NgZone, OnChanges, OnDestroy, SimpleChanges } from '@angular/core'; -import { echarts, EChartsOption } from '../../graphs/echarts'; +import { echarts, EChartsOption } from '@app/graphs/echarts'; import { BehaviorSubject, Observable, Subscription, combineLatest, of } from 'rxjs'; import { catchError, map, switchMap, tap } from 'rxjs/operators'; -import { AddressTxSummary, ChainStats } from '../../interfaces/electrs.interface'; -import { ElectrsApiService } from '../../services/electrs-api.service'; -import { AmountShortenerPipe } from '../../shared/pipes/amount-shortener.pipe'; +import { AddressTxSummary, ChainStats } from '@interfaces/electrs.interface'; +import { ElectrsApiService } from '@app/services/electrs-api.service'; +import { AmountShortenerPipe } from '@app/shared/pipes/amount-shortener.pipe'; import { Router } from '@angular/router'; -import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; -import { StateService } from '../../services/state.service'; -import { PriceService } from '../../services/price.service'; -import { FiatCurrencyPipe } from '../../shared/pipes/fiat-currency.pipe'; -import { FiatShortenerPipe } from '../../shared/pipes/fiat-shortener.pipe'; +import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe'; +import { StateService } from '@app/services/state.service'; +import { PriceService } from '@app/services/price.service'; +import { FiatCurrencyPipe } from '@app/shared/pipes/fiat-currency.pipe'; +import { FiatShortenerPipe } from '@app/shared/pipes/fiat-shortener.pipe'; const periodSeconds = { '1d': (60 * 60 * 24), @@ -83,7 +83,7 @@ export class AddressGraphComponent implements OnChanges, OnDestroy { ngOnChanges(changes: SimpleChanges): void { this.isLoading = true; - if (!this.address || !this.stats) { + if (!this.addressSummary$ && (!this.address || !this.stats)) { return; } if (changes.address || changes.isPubkey || changes.addressSummary$ || changes.stats) { @@ -144,15 +144,16 @@ export class AddressGraphComponent implements OnChanges, OnDestroy { } prepareChartOptions(summary: AddressTxSummary[]) { - if (!summary || !this.stats) { + if (!summary) { return; } - let total = (this.stats.funded_txo_sum - this.stats.spent_txo_sum); + const total = this.stats ? (this.stats.funded_txo_sum - this.stats.spent_txo_sum) : summary.reduce((acc, tx) => acc + tx.value, 0); + let runningTotal = total; const processData = summary.map(d => { - const balance = total; - const fiatBalance = total * d.price / 100_000_000; - total -= d.value; + const balance = runningTotal; + const fiatBalance = runningTotal * d.price / 100_000_000; + runningTotal -= d.value; return { time: d.time * 1000, balance, @@ -172,7 +173,7 @@ export class AddressGraphComponent implements OnChanges, OnDestroy { this.fiatData = this.fiatData.filter(d => d[0] >= startFiat); } this.data.push( - {value: [now, this.stats.funded_txo_sum - this.stats.spent_txo_sum], symbol: 'none', tooltip: { show: false }} + {value: [now, total], symbol: 'none', tooltip: { show: false }} ); const maxValue = this.data.reduce((acc, d) => Math.max(acc, Math.abs(d[1] ?? d.value[1])), 0); diff --git a/frontend/src/app/components/address-group/address-group.component.ts b/frontend/src/app/components/address-group/address-group.component.ts index 30bee7543..560308592 100644 --- a/frontend/src/app/components/address-group/address-group.component.ts +++ b/frontend/src/app/components/address-group/address-group.component.ts @@ -1,15 +1,15 @@ import { Component, OnInit, OnDestroy, ChangeDetectorRef, HostListener } from '@angular/core'; import { ActivatedRoute, ParamMap } from '@angular/router'; -import { ElectrsApiService } from '../../services/electrs-api.service'; +import { ElectrsApiService } from '@app/services/electrs-api.service'; import { switchMap, catchError } from 'rxjs/operators'; -import { Address, Transaction } from '../../interfaces/electrs.interface'; -import { WebsocketService } from '../../services/websocket.service'; -import { StateService } from '../../services/state.service'; -import { AudioService } from '../../services/audio.service'; -import { ApiService } from '../../services/api.service'; +import { Address, Transaction } from '@interfaces/electrs.interface'; +import { WebsocketService } from '@app/services/websocket.service'; +import { StateService } from '@app/services/state.service'; +import { AudioService } from '@app/services/audio.service'; +import { ApiService } from '@app/services/api.service'; import { of, Subscription, forkJoin } from 'rxjs'; -import { SeoService } from '../../services/seo.service'; -import { AddressInformation } from '../../interfaces/node-api.interface'; +import { SeoService } from '@app/services/seo.service'; +import { AddressInformation } from '@interfaces/node-api.interface'; @Component({ selector: 'app-address-group', diff --git a/frontend/src/app/components/address-labels/address-labels.component.ts b/frontend/src/app/components/address-labels/address-labels.component.ts index ff3c27240..0669a22e4 100644 --- a/frontend/src/app/components/address-labels/address-labels.component.ts +++ b/frontend/src/app/components/address-labels/address-labels.component.ts @@ -1,7 +1,7 @@ import { Component, ChangeDetectionStrategy, Input, OnChanges } from '@angular/core'; -import { Vin, Vout } from '../../interfaces/electrs.interface'; -import { StateService } from '../../services/state.service'; -import { AddressType, AddressTypeInfo } from '../../shared/address-utils'; +import { Vin, Vout } from '@interfaces/electrs.interface'; +import { StateService } from '@app/services/state.service'; +import { AddressType, AddressTypeInfo } from '@app/shared/address-utils'; @Component({ selector: 'app-address-labels', diff --git a/frontend/src/app/components/address-transactions-widget/address-transactions-widget.component.html b/frontend/src/app/components/address-transactions-widget/address-transactions-widget.component.html index c1c999d6f..ea055a96f 100644 --- a/frontend/src/app/components/address-transactions-widget/address-transactions-widget.component.html +++ b/frontend/src/app/components/address-transactions-widget/address-transactions-widget.component.html @@ -12,7 +12,7 @@ - + diff --git a/frontend/src/app/components/address-transactions-widget/address-transactions-widget.component.ts b/frontend/src/app/components/address-transactions-widget/address-transactions-widget.component.ts index 998d269ba..ab9b124c3 100644 --- a/frontend/src/app/components/address-transactions-widget/address-transactions-widget.component.ts +++ b/frontend/src/app/components/address-transactions-widget/address-transactions-widget.component.ts @@ -1,9 +1,9 @@ import { Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core'; -import { StateService } from '../../services/state.service'; -import { Address, AddressTxSummary } from '../../interfaces/electrs.interface'; -import { ElectrsApiService } from '../../services/electrs-api.service'; +import { StateService } from '@app/services/state.service'; +import { Address, AddressTxSummary } from '@interfaces/electrs.interface'; +import { ElectrsApiService } from '@app/services/electrs-api.service'; import { Observable, Subscription, catchError, map, of, switchMap, zip } from 'rxjs'; -import { PriceService } from '../../services/price.service'; +import { PriceService } from '@app/services/price.service'; @Component({ selector: 'app-address-transactions-widget', @@ -43,7 +43,7 @@ export class AddressTransactionsWidgetComponent implements OnInit, OnChanges, On startAddressSubscription(): void { this.isLoading = true; - if (!this.address || !this.addressInfo) { + if (!this.addressSummary$ && (!this.address || !this.addressInfo)) { return; } this.transactions$ = (this.addressSummary$ || (this.isPubkey @@ -55,7 +55,7 @@ export class AddressTransactionsWidgetComponent implements OnInit, OnChanges, On }) )).pipe( map(summary => { - return summary?.slice(0, 6); + return summary?.filter(tx => Math.abs(tx.value) >= 1000000)?.slice(0, 6); }), switchMap(txs => { return (zip(txs.map(tx => this.priceService.getBlockPrice$(tx.time, txs.length < 3, this.currency).pipe( @@ -68,6 +68,12 @@ export class AddressTransactionsWidgetComponent implements OnInit, OnChanges, On )))); }) ); + + } + + getAmountDigits(value: number): string { + const decimals = Math.max(0, 4 - Math.ceil(Math.log10(Math.abs(value / 100_000_000)))); + return `1.${decimals}-${decimals}`; } ngOnDestroy(): void { diff --git a/frontend/src/app/components/address/address-preview.component.ts b/frontend/src/app/components/address/address-preview.component.ts index 9bc6e967f..bcc328787 100644 --- a/frontend/src/app/components/address/address-preview.component.ts +++ b/frontend/src/app/components/address/address-preview.component.ts @@ -1,16 +1,16 @@ import { Component, OnInit, OnDestroy } from '@angular/core'; import { ActivatedRoute, ParamMap } from '@angular/router'; -import { ElectrsApiService } from '../../services/electrs-api.service'; +import { ElectrsApiService } from '@app/services/electrs-api.service'; import { switchMap, filter, catchError, map, tap } from 'rxjs/operators'; -import { Address, Transaction } from '../../interfaces/electrs.interface'; -import { StateService } from '../../services/state.service'; -import { OpenGraphService } from '../../services/opengraph.service'; -import { AudioService } from '../../services/audio.service'; -import { ApiService } from '../../services/api.service'; +import { Address, Transaction } from '@interfaces/electrs.interface'; +import { StateService } from '@app/services/state.service'; +import { OpenGraphService } from '@app/services/opengraph.service'; +import { AudioService } from '@app/services/audio.service'; +import { ApiService } from '@app/services/api.service'; import { of, merge, Subscription, Observable } from 'rxjs'; -import { SeoService } from '../../services/seo.service'; -import { seoDescriptionNetwork } from '../../shared/common.utils'; -import { AddressInformation } from '../../interfaces/node-api.interface'; +import { SeoService } from '@app/services/seo.service'; +import { seoDescriptionNetwork } from '@app/shared/common.utils'; +import { AddressInformation } from '@interfaces/node-api.interface'; @Component({ selector: 'app-address-preview', diff --git a/frontend/src/app/components/address/address.component.html b/frontend/src/app/components/address/address.component.html index b893d7e22..41d8c151f 100644 --- a/frontend/src/app/components/address/address.component.html +++ b/frontend/src/app/components/address/address.component.html @@ -117,7 +117,7 @@
- +
diff --git a/frontend/src/app/components/address/address.component.ts b/frontend/src/app/components/address/address.component.ts index be6d531c0..8786f46ee 100644 --- a/frontend/src/app/components/address/address.component.ts +++ b/frontend/src/app/components/address/address.component.ts @@ -1,17 +1,17 @@ import { Component, OnInit, OnDestroy, HostListener } from '@angular/core'; import { ActivatedRoute, ParamMap } from '@angular/router'; -import { ElectrsApiService } from '../../services/electrs-api.service'; +import { ElectrsApiService } from '@app/services/electrs-api.service'; import { switchMap, filter, catchError, map, tap } from 'rxjs/operators'; -import { Address, ChainStats, Transaction, Utxo, Vin } from '../../interfaces/electrs.interface'; -import { WebsocketService } from '../../services/websocket.service'; -import { StateService } from '../../services/state.service'; -import { AudioService } from '../../services/audio.service'; -import { ApiService } from '../../services/api.service'; +import { Address, ChainStats, Transaction, Utxo, Vin } from '@interfaces/electrs.interface'; +import { WebsocketService } from '@app/services/websocket.service'; +import { StateService } from '@app/services/state.service'; +import { AudioService } from '@app/services/audio.service'; +import { ApiService } from '@app/services/api.service'; import { of, merge, Subscription, Observable, forkJoin } from 'rxjs'; -import { SeoService } from '../../services/seo.service'; -import { seoDescriptionNetwork } from '../../shared/common.utils'; -import { AddressInformation } from '../../interfaces/node-api.interface'; -import { AddressTypeInfo } from '../../shared/address-utils'; +import { SeoService } from '@app/services/seo.service'; +import { seoDescriptionNetwork } from '@app/shared/common.utils'; +import { AddressInformation } from '@interfaces/node-api.interface'; +import { AddressTypeInfo } from '@app/shared/address-utils'; class AddressStats implements ChainStats { address: string; @@ -219,11 +219,11 @@ export class AddressComponent implements OnInit, OnDestroy { address.is_pubkey ? this.electrsApiService.getScriptHashTransactions$((address.address.length === 66 ? '21' : '41') + address.address + 'ac') : this.electrsApiService.getAddressTransactions$(address.address), - (utxoCount >= 2 && utxoCount <= 500 ? (address.is_pubkey + (utxoCount > 2 && utxoCount <= 500 ? (address.is_pubkey ? this.electrsApiService.getScriptHashUtxos$((address.address.length === 66 ? '21' : '41') + address.address + 'ac') - : this.electrsApiService.getAddressUtxos$(address.address)) : of([])).pipe( + : this.electrsApiService.getAddressUtxos$(address.address)) : of(null)).pipe( catchError(() => { - return of([]); + return of(null); }) ) ]); @@ -350,27 +350,29 @@ export class AddressComponent implements OnInit, OnDestroy { } // update utxos in-place - let utxosChanged = false; - for (const vin of transaction.vin) { - const utxoIndex = this.utxos.findIndex((utxo) => utxo.txid === vin.txid && utxo.vout === vin.vout); - if (utxoIndex !== -1) { - this.utxos.splice(utxoIndex, 1); - utxosChanged = true; + if (this.utxos != null) { + let utxosChanged = false; + for (const vin of transaction.vin) { + const utxoIndex = this.utxos.findIndex((utxo) => utxo.txid === vin.txid && utxo.vout === vin.vout); + if (utxoIndex !== -1) { + this.utxos.splice(utxoIndex, 1); + utxosChanged = true; + } } - } - for (const [index, vout] of transaction.vout.entries()) { - if (vout.scriptpubkey_address === this.address.address) { - this.utxos.push({ - txid: transaction.txid, - vout: index, - value: vout.value, - status: JSON.parse(JSON.stringify(transaction.status)), - }); - utxosChanged = true; + for (const [index, vout] of transaction.vout.entries()) { + if (vout.scriptpubkey_address === this.address.address) { + this.utxos.push({ + txid: transaction.txid, + vout: index, + value: vout.value, + status: JSON.parse(JSON.stringify(transaction.status)), + }); + utxosChanged = true; + } + } + if (utxosChanged) { + this.utxos = this.utxos.slice(); } - } - if (utxosChanged) { - this.utxos = this.utxos.slice(); } return true; } @@ -385,29 +387,31 @@ export class AddressComponent implements OnInit, OnDestroy { this.transactions = this.transactions.slice(); // update utxos in-place - let utxosChanged = false; - for (const vin of transaction.vin) { - if (vin.prevout?.scriptpubkey_address === this.address.address) { - this.utxos.push({ - txid: vin.txid, - vout: vin.vout, - value: vin.prevout.value, - status: { confirmed: true }, // Assuming the input was confirmed - }); - utxosChanged = true; - } - } - for (const [index, vout] of transaction.vout.entries()) { - if (vout.scriptpubkey_address === this.address.address) { - const utxoIndex = this.utxos.findIndex((utxo) => utxo.txid === transaction.txid && utxo.vout === index); - if (utxoIndex !== -1) { - this.utxos.splice(utxoIndex, 1); + if (this.utxos != null) { + let utxosChanged = false; + for (const vin of transaction.vin) { + if (vin.prevout?.scriptpubkey_address === this.address.address) { + this.utxos.push({ + txid: vin.txid, + vout: vin.vout, + value: vin.prevout.value, + status: { confirmed: true }, // Assuming the input was confirmed + }); utxosChanged = true; } } - } - if (utxosChanged) { - this.utxos = this.utxos.slice(); + for (const [index, vout] of transaction.vout.entries()) { + if (vout.scriptpubkey_address === this.address.address) { + const utxoIndex = this.utxos.findIndex((utxo) => utxo.txid === transaction.txid && utxo.vout === index); + if (utxoIndex !== -1) { + this.utxos.splice(utxoIndex, 1); + utxosChanged = true; + } + } + } + if (utxosChanged) { + this.utxos = this.utxos.slice(); + } } return true; @@ -415,27 +419,29 @@ export class AddressComponent implements OnInit, OnDestroy { confirmTransaction(transaction: Transaction): void { // update utxos in-place - let utxosChanged = false; - for (const vin of transaction.vin) { - if (vin.prevout?.scriptpubkey_address === this.address.address) { - const utxoIndex = this.utxos.findIndex((utxo) => utxo.txid === vin.txid && utxo.vout === vin.vout); - if (utxoIndex !== -1) { - this.utxos[utxoIndex].status = JSON.parse(JSON.stringify(transaction.status)); - utxosChanged = true; + if (this.utxos != null) { + let utxosChanged = false; + for (const vin of transaction.vin) { + if (vin.prevout?.scriptpubkey_address === this.address.address) { + const utxoIndex = this.utxos.findIndex((utxo) => utxo.txid === vin.txid && utxo.vout === vin.vout); + if (utxoIndex !== -1) { + this.utxos[utxoIndex].status = JSON.parse(JSON.stringify(transaction.status)); + utxosChanged = true; + } } } - } - for (const [index, vout] of transaction.vout.entries()) { - if (vout.scriptpubkey_address === this.address.address) { - const utxoIndex = this.utxos.findIndex((utxo) => utxo.txid === transaction.txid && utxo.vout === index); - if (utxoIndex !== -1) { - this.utxos[utxoIndex].status = JSON.parse(JSON.stringify(transaction.status)); - utxosChanged = true; + for (const [index, vout] of transaction.vout.entries()) { + if (vout.scriptpubkey_address === this.address.address) { + const utxoIndex = this.utxos.findIndex((utxo) => utxo.txid === transaction.txid && utxo.vout === index); + if (utxoIndex !== -1) { + this.utxos[utxoIndex].status = JSON.parse(JSON.stringify(transaction.status)); + utxosChanged = true; + } } } - } - if (utxosChanged) { - this.utxos = this.utxos.slice(); + if (utxosChanged) { + this.utxos = this.utxos.slice(); + } } } diff --git a/frontend/src/app/components/addresses-treemap/addresses-treemap.component.html b/frontend/src/app/components/addresses-treemap/addresses-treemap.component.html new file mode 100644 index 000000000..1c44f9aa3 --- /dev/null +++ b/frontend/src/app/components/addresses-treemap/addresses-treemap.component.html @@ -0,0 +1,10 @@ +
+
+
+
+
+ +
+
+
+
diff --git a/frontend/src/app/components/addresses-treemap/addresses-treemap.component.scss b/frontend/src/app/components/addresses-treemap/addresses-treemap.component.scss new file mode 100644 index 000000000..78510203f --- /dev/null +++ b/frontend/src/app/components/addresses-treemap/addresses-treemap.component.scss @@ -0,0 +1,17 @@ +.node-channels-container { + position: relative; +} + +.loading-spinner { + position: absolute; + top: 0; + left: 0; + right: 0; + width: 100%; + z-index: 100; +} + +.spinner-border { + position: relative; + top: 225px; +} \ No newline at end of file diff --git a/frontend/src/app/components/addresses-treemap/addresses-treemap.component.ts b/frontend/src/app/components/addresses-treemap/addresses-treemap.component.ts new file mode 100644 index 000000000..5ff3cf502 --- /dev/null +++ b/frontend/src/app/components/addresses-treemap/addresses-treemap.component.ts @@ -0,0 +1,150 @@ +import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, NgZone, OnChanges } from '@angular/core'; +import { Router } from '@angular/router'; +import { EChartsOption, TreemapSeriesOption } from '@app/graphs/echarts'; +import { lerpColor } from '@app/shared/graphs.utils'; +import { AmountShortenerPipe } from '@app/shared/pipes/amount-shortener.pipe'; +import { LightningApiService } from '@app/lightning/lightning-api.service'; +import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe'; +import { StateService } from '@app/services/state.service'; +import { Address } from '@interfaces/electrs.interface'; +import { formatNumber } from '@angular/common'; + +@Component({ + selector: 'app-addresses-treemap', + templateUrl: './addresses-treemap.component.html', + styleUrls: ['./addresses-treemap.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AddressesTreemap implements OnChanges { + @Input() addresses: Address[]; + @Input() isLoading: boolean = false; + + chartInstance: any; + chartOptions: EChartsOption = {}; + chartInitOptions = { + renderer: 'svg', + }; + + constructor( + @Inject(LOCALE_ID) public locale: string, + private lightningApiService: LightningApiService, + private amountShortenerPipe: AmountShortenerPipe, + private zone: NgZone, + private router: Router, + public stateService: StateService, + ) {} + + ngOnChanges(): void { + this.prepareChartOptions(); + } + + prepareChartOptions(): void { + const data = this.addresses.map(address => ({ + address: address.address, + value: address.chain_stats.funded_txo_sum - address.chain_stats.spent_txo_sum, + stats: address.chain_stats, + })); + // only consider visible items for the color gradient + const totalValue = data.reduce((acc, address) => acc + address.value, 0); + const maxTxs = data.filter(address => address.value > (totalValue / 2000)).reduce((max, address) => Math.max(max, address.stats.tx_count), 0); + const dataItems = data.map(address => ({ + ...address, + itemStyle: { + color: lerpColor('#1E88E5', '#D81B60', address.stats.tx_count / maxTxs), + } + })); + this.chartOptions = { + tooltip: { + trigger: 'item', + textStyle: { + align: 'left', + } + }, + series: [ + { + height: 300, + left: 0, + right: 0, + bottom: 0, + top: 0, + roam: false, + type: 'treemap', + data: dataItems, + nodeClick: 'link', + progressive: 100, + tooltip: { + show: true, + backgroundColor: 'rgba(17, 19, 31, 1)', + borderRadius: 4, + shadowColor: 'rgba(0, 0, 0, 0.5)', + textStyle: { + color: '#b1b1b1', + }, + borderColor: '#000', + formatter: (value): string => { + if (!value.data.address) { + return ''; + } + return ` + + + + + + + + + + + + + + + + + + + + + + +
${value.data.address}
Received${this.formatValue(value.data.stats.funded_txo_sum)}
Sent${this.formatValue(value.data.stats.spent_txo_sum)}
Balance${this.formatValue(value.data.stats.funded_txo_sum - value.data.stats.spent_txo_sum)}
Transaction count${value.data.stats.tx_count}
+ `; + } + }, + itemStyle: { + borderColor: 'black', + borderWidth: 1, + }, + breadcrumb: { + show: false, + } + } + ] + }; + } + + formatValue(sats: number): string { + if (sats > 100000000) { + return formatNumber(sats / 100000000, this.locale, '1.2-2') + ' BTC'; + } else { + return this.amountShortenerPipe.transform(sats, 2) + ' sats'; + } + } + + onChartInit(ec: any): void { + this.chartInstance = ec; + + this.chartInstance.on('click', (e) => { + //@ts-ignore + if (!e.data.address) { + return; + } + this.zone.run(() => { + //@ts-ignore + const url = new RelativeUrlPipe(this.stateService).transform(`/address/${e.data.address}`); + this.router.navigate([url]); + }); + }); + } +} diff --git a/frontend/src/app/components/amount-selector/amount-selector.component.ts b/frontend/src/app/components/amount-selector/amount-selector.component.ts index 144b0f1db..e22542eb3 100644 --- a/frontend/src/app/components/amount-selector/amount-selector.component.ts +++ b/frontend/src/app/components/amount-selector/amount-selector.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { StorageService } from '../../services/storage.service'; -import { StateService } from '../../services/state.service'; +import { StorageService } from '@app/services/storage.service'; +import { StateService } from '@app/services/state.service'; @Component({ selector: 'app-amount-selector', diff --git a/frontend/src/app/components/amount/amount.component.html b/frontend/src/app/components/amount/amount.component.html index b513c89d2..cbbdb2dd9 100644 --- a/frontend/src/app/components/amount/amount.component.html +++ b/frontend/src/app/components/amount/amount.component.html @@ -30,7 +30,7 @@ @if (digitsInfo === '1.8-8') { ‎{{ addPlus && satoshis >= 0 ? '+' : '' }}{{ satoshis | number }} } @else { - ‎{{ addPlus && satoshis >= 0 ? '+' : '' }}{{ satoshis | amountShortener : satoshis < 1000 && satoshis > -1000 ? 0 : 1 }} + ‎{{ addPlus && satoshis >= 0 ? '+' : '' }}{{ satoshis | amountShortener : (satoshis < 1000 && satoshis > -1000 ? 0 : 1) : undefined : true }} } sats diff --git a/frontend/src/app/components/amount/amount.component.ts b/frontend/src/app/components/amount/amount.component.ts index 93715f3c0..bf40a7567 100644 --- a/frontend/src/app/components/amount/amount.component.ts +++ b/frontend/src/app/components/amount/amount.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit, OnDestroy, Input, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core'; -import { StateService } from '../../services/state.service'; +import { StateService } from '@app/services/state.service'; import { Observable, Subscription } from 'rxjs'; -import { Price } from '../../services/price.service'; +import { Price } from '@app/services/price.service'; @Component({ selector: 'app-amount', diff --git a/frontend/src/app/components/app/app.component.ts b/frontend/src/app/components/app/app.component.ts index 453276966..365c23972 100644 --- a/frontend/src/app/components/app/app.component.ts +++ b/frontend/src/app/components/app/app.component.ts @@ -1,11 +1,11 @@ import { Location } from '@angular/common'; import { Component, HostListener, OnInit, Inject, LOCALE_ID, HostBinding } from '@angular/core'; import { Router, NavigationEnd } from '@angular/router'; -import { StateService } from '../../services/state.service'; -import { OpenGraphService } from '../../services/opengraph.service'; +import { StateService } from '@app/services/state.service'; +import { OpenGraphService } from '@app/services/opengraph.service'; import { NgbTooltipConfig } from '@ng-bootstrap/ng-bootstrap'; -import { ThemeService } from '../../services/theme.service'; -import { SeoService } from '../../services/seo.service'; +import { ThemeService } from '@app/services/theme.service'; +import { SeoService } from '@app/services/seo.service'; @Component({ selector: 'app-root', diff --git a/frontend/src/app/components/asset-circulation/asset-circulation.component.ts b/frontend/src/app/components/asset-circulation/asset-circulation.component.ts index cc09c4809..ab41492b0 100644 --- a/frontend/src/app/components/asset-circulation/asset-circulation.component.ts +++ b/frontend/src/app/components/asset-circulation/asset-circulation.component.ts @@ -1,10 +1,10 @@ import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'; import { combineLatest, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import { moveDec } from '../../bitcoin.utils'; -import { AssetsService } from '../../services/assets.service'; -import { ElectrsApiService } from '../../services/electrs-api.service'; -import { environment } from '../../../environments/environment'; +import { moveDec } from '@app/bitcoin.utils'; +import { AssetsService } from '@app/services/assets.service'; +import { ElectrsApiService } from '@app/services/electrs-api.service'; +import { environment } from '@environments/environment'; @Component({ selector: 'app-asset-circulation', diff --git a/frontend/src/app/components/asset/asset.component.ts b/frontend/src/app/components/asset/asset.component.ts index dd09468cc..9a3a73eea 100644 --- a/frontend/src/app/components/asset/asset.component.ts +++ b/frontend/src/app/components/asset/asset.component.ts @@ -1,17 +1,17 @@ import { Component, OnInit, OnDestroy } from '@angular/core'; import { ActivatedRoute, ParamMap } from '@angular/router'; -import { ElectrsApiService } from '../../services/electrs-api.service'; +import { ElectrsApiService } from '@app/services/electrs-api.service'; import { switchMap, filter, catchError, take } from 'rxjs/operators'; -import { Asset, Transaction } from '../../interfaces/electrs.interface'; -import { WebsocketService } from '../../services/websocket.service'; -import { StateService } from '../../services/state.service'; -import { AudioService } from '../../services/audio.service'; -import { ApiService } from '../../services/api.service'; +import { Asset, Transaction } from '@interfaces/electrs.interface'; +import { WebsocketService } from '@app/services/websocket.service'; +import { StateService } from '@app/services/state.service'; +import { AudioService } from '@app/services/audio.service'; +import { ApiService } from '@app/services/api.service'; import { of, merge, Subscription, combineLatest } from 'rxjs'; -import { SeoService } from '../../services/seo.service'; -import { environment } from '../../../environments/environment'; -import { AssetsService } from '../../services/assets.service'; -import { moveDec } from '../../bitcoin.utils'; +import { SeoService } from '@app/services/seo.service'; +import { environment } from '@app/../environments/environment'; +import { AssetsService } from '@app/services/assets.service'; +import { moveDec } from '@app/bitcoin.utils'; @Component({ selector: 'app-asset', diff --git a/frontend/src/app/components/assets/asset-group/asset-group.component.ts b/frontend/src/app/components/assets/asset-group/asset-group.component.ts index 27e048558..3294eed70 100644 --- a/frontend/src/app/components/assets/asset-group/asset-group.component.ts +++ b/frontend/src/app/components/assets/asset-group/asset-group.component.ts @@ -2,8 +2,8 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, ParamMap } from '@angular/router'; import { combineLatest, Observable } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; -import { ApiService } from '../../../services/api.service'; -import { AssetsService } from '../../../services/assets.service'; +import { ApiService } from '@app/services/api.service'; +import { AssetsService } from '@app/services/assets.service'; @Component({ selector: 'app-asset-group', diff --git a/frontend/src/app/components/assets/assets-featured/assets-featured.component.ts b/frontend/src/app/components/assets/assets-featured/assets-featured.component.ts index a9bf305f6..de6a0e524 100644 --- a/frontend/src/app/components/assets/assets-featured/assets-featured.component.ts +++ b/frontend/src/app/components/assets/assets-featured/assets-featured.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; -import { ApiService } from '../../../services/api.service'; -import { StateService } from '../../../services/state.service'; +import { ApiService } from '@app/services/api.service'; +import { StateService } from '@app/services/state.service'; @Component({ selector: 'app-assets-featured', diff --git a/frontend/src/app/components/assets/assets-nav/assets-nav.component.ts b/frontend/src/app/components/assets/assets-nav/assets-nav.component.ts index c9b044b34..fb280631a 100644 --- a/frontend/src/app/components/assets/assets-nav/assets-nav.component.ts +++ b/frontend/src/app/components/assets/assets-nav/assets-nav.component.ts @@ -4,12 +4,12 @@ import { Router } from '@angular/router'; import { NgbTypeahead } from '@ng-bootstrap/ng-bootstrap'; import { merge, Observable, of, Subject } from 'rxjs'; import { distinctUntilChanged, filter, map, switchMap } from 'rxjs/operators'; -import { AssetExtended } from '../../../interfaces/electrs.interface'; -import { AssetsService } from '../../../services/assets.service'; -import { SeoService } from '../../../services/seo.service'; -import { StateService } from '../../../services/state.service'; -import { RelativeUrlPipe } from '../../../shared/pipes/relative-url/relative-url.pipe'; -import { environment } from '../../../../environments/environment'; +import { AssetExtended } from '@interfaces/electrs.interface'; +import { AssetsService } from '@app/services/assets.service'; +import { SeoService } from '@app/services/seo.service'; +import { StateService } from '@app/services/state.service'; +import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe'; +import { environment } from '@environments/environment'; @Component({ selector: 'app-assets-nav', diff --git a/frontend/src/app/components/assets/assets.component.ts b/frontend/src/app/components/assets/assets.component.ts index 85d236bca..6a573fcd6 100644 --- a/frontend/src/app/components/assets/assets.component.ts +++ b/frontend/src/app/components/assets/assets.component.ts @@ -1,13 +1,13 @@ import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core'; -import { AssetsService } from '../../services/assets.service'; -import { environment } from '../../../environments/environment'; +import { AssetsService } from '@app/services/assets.service'; +import { environment } from '@environments/environment'; import { UntypedFormGroup } from '@angular/forms'; import { filter, map, switchMap, take } from 'rxjs/operators'; import { ActivatedRoute, Router } from '@angular/router'; import { combineLatest, Observable } from 'rxjs'; -import { AssetExtended } from '../../interfaces/electrs.interface'; -import { SeoService } from '../../services/seo.service'; -import { StateService } from '../../services/state.service'; +import { AssetExtended } from '@interfaces/electrs.interface'; +import { SeoService } from '@app/services/seo.service'; +import { StateService } from '@app/services/state.service'; @Component({ selector: 'app-assets', diff --git a/frontend/src/app/components/balance-widget/balance-widget.component.html b/frontend/src/app/components/balance-widget/balance-widget.component.html index 4923a2c06..87f14de53 100644 --- a/frontend/src/app/components/balance-widget/balance-widget.component.html +++ b/frontend/src/app/components/balance-widget/balance-widget.component.html @@ -4,10 +4,10 @@
BTC Holdings
- {{ ((addressInfo.chain_stats.funded_txo_sum - addressInfo.chain_stats.spent_txo_sum) / 100_000_000) | number: '1.2-2' }} BTC + {{ ((total) / 100_000_000) | number: '1.2-2' }} BTC
- +
diff --git a/frontend/src/app/components/balance-widget/balance-widget.component.ts b/frontend/src/app/components/balance-widget/balance-widget.component.ts index 8e1d3f442..bd92a2eb9 100644 --- a/frontend/src/app/components/balance-widget/balance-widget.component.ts +++ b/frontend/src/app/components/balance-widget/balance-widget.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; -import { StateService } from '../../services/state.service'; -import { Address, AddressTxSummary } from '../../interfaces/electrs.interface'; -import { ElectrsApiService } from '../../services/electrs-api.service'; +import { StateService } from '@app/services/state.service'; +import { Address, AddressTxSummary } from '@interfaces/electrs.interface'; +import { ElectrsApiService } from '@app/services/electrs-api.service'; import { Observable, catchError, of } from 'rxjs'; @Component({ @@ -19,6 +19,7 @@ export class BalanceWidgetComponent implements OnInit, OnChanges { isLoading: boolean = true; error: any; + total: number = 0; delta7d: number = 0; delta30d: number = 0; @@ -34,7 +35,7 @@ export class BalanceWidgetComponent implements OnInit, OnChanges { ngOnChanges(changes: SimpleChanges): void { this.isLoading = true; - if (!this.address || !this.addressInfo) { + if (!this.addressSummary$ && (!this.address || !this.addressInfo)) { return; } (this.addressSummary$ || (this.isPubkey @@ -57,6 +58,7 @@ export class BalanceWidgetComponent implements OnInit, OnChanges { calculateStats(summary: AddressTxSummary[]): void { let weekTotal = 0; let monthTotal = 0; + this.total = this.addressInfo ? this.addressInfo.chain_stats.funded_txo_sum - this.addressInfo.chain_stats.spent_txo_sum : summary.reduce((acc, tx) => acc + tx.value, 0); const weekAgo = (new Date(new Date().setHours(0, 0, 0, 0) - (7 * 24 * 60 * 60 * 1000)).getTime()) / 1000; const monthAgo = (new Date(new Date().setHours(0, 0, 0, 0) - (30 * 24 * 60 * 60 * 1000)).getTime()) / 1000; diff --git a/frontend/src/app/components/bitcoin-invoice/bitcoin-invoice.component.ts b/frontend/src/app/components/bitcoin-invoice/bitcoin-invoice.component.ts index 067061678..f931f2c31 100644 --- a/frontend/src/app/components/bitcoin-invoice/bitcoin-invoice.component.ts +++ b/frontend/src/app/components/bitcoin-invoice/bitcoin-invoice.component.ts @@ -4,7 +4,7 @@ import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; import { ActivatedRoute } from '@angular/router'; import { Subscription, of, timer } from 'rxjs'; import { filter, repeat, retry, switchMap, take, tap } from 'rxjs/operators'; -import { ServicesApiServices } from '../../services/services-api.service'; +import { ServicesApiServices } from '@app/services/services-api.service'; @Component({ selector: 'app-bitcoin-invoice', diff --git a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts index c533626e7..07361ef42 100644 --- a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts +++ b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts @@ -1,17 +1,17 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, NgZone, OnInit } from '@angular/core'; -import { echarts, EChartsOption } from '../../graphs/echarts'; +import { echarts, EChartsOption } from '@app/graphs/echarts'; import { Observable, combineLatest, of } from 'rxjs'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; -import { ApiService } from '../../services/api.service'; -import { SeoService } from '../../services/seo.service'; +import { ApiService } from '@app/services/api.service'; +import { SeoService } from '@app/services/seo.service'; import { formatNumber } from '@angular/common'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { download, formatterXAxis, formatterXAxisLabel, formatterXAxisTimeCategory } from '../../shared/graphs.utils'; -import { StorageService } from '../../services/storage.service'; -import { MiningService } from '../../services/mining.service'; -import { selectPowerOfTen } from '../../bitcoin.utils'; -import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; -import { StateService } from '../../services/state.service'; +import { download, formatterXAxis, formatterXAxisLabel, formatterXAxisTimeCategory } from '@app/shared/graphs.utils'; +import { StorageService } from '@app/services/storage.service'; +import { MiningService } from '@app/services/mining.service'; +import { selectPowerOfTen } from '@app/bitcoin.utils'; +import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe'; +import { StateService } from '@app/services/state.service'; import { ActivatedRoute, Router } from '@angular/router'; @Component({ diff --git a/frontend/src/app/components/block-fees-graph/block-fees-graph.component.ts b/frontend/src/app/components/block-fees-graph/block-fees-graph.component.ts index 33e3eb19e..c2dea11aa 100644 --- a/frontend/src/app/components/block-fees-graph/block-fees-graph.component.ts +++ b/frontend/src/app/components/block-fees-graph/block-fees-graph.component.ts @@ -1,18 +1,18 @@ import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'; -import { echarts, EChartsOption } from '../../graphs/echarts'; +import { echarts, EChartsOption } from '@app/graphs/echarts'; import { Observable } from 'rxjs'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; -import { ApiService } from '../../services/api.service'; -import { SeoService } from '../../services/seo.service'; +import { ApiService } from '@app/services/api.service'; +import { SeoService } from '@app/services/seo.service'; import { formatNumber } from '@angular/common'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { download, formatterXAxis } from '../../shared/graphs.utils'; -import { StorageService } from '../../services/storage.service'; -import { MiningService } from '../../services/mining.service'; +import { download, formatterXAxis } from '@app/shared/graphs.utils'; +import { StorageService } from '@app/services/storage.service'; +import { MiningService } from '@app/services/mining.service'; import { ActivatedRoute } from '@angular/router'; -import { FiatShortenerPipe } from '../../shared/pipes/fiat-shortener.pipe'; -import { FiatCurrencyPipe } from '../../shared/pipes/fiat-currency.pipe'; -import { StateService } from '../../services/state.service'; +import { FiatShortenerPipe } from '@app/shared/pipes/fiat-shortener.pipe'; +import { FiatCurrencyPipe } from '@app/shared/pipes/fiat-currency.pipe'; +import { StateService } from '@app/services/state.service'; @Component({ selector: 'app-block-fees-graph', diff --git a/frontend/src/app/components/block-fees-subsidy-graph/block-fees-subsidy-graph.component.ts b/frontend/src/app/components/block-fees-subsidy-graph/block-fees-subsidy-graph.component.ts index 42d05510f..deba874a7 100644 --- a/frontend/src/app/components/block-fees-subsidy-graph/block-fees-subsidy-graph.component.ts +++ b/frontend/src/app/components/block-fees-subsidy-graph/block-fees-subsidy-graph.component.ts @@ -1,19 +1,19 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostListener, Inject, Input, LOCALE_ID, NgZone, OnInit } from '@angular/core'; -import { EChartsOption } from '../../graphs/echarts'; +import { EChartsOption } from '@app/graphs/echarts'; import { Observable } from 'rxjs'; import { catchError, map, share, startWith, switchMap, tap } from 'rxjs/operators'; -import { ApiService } from '../../services/api.service'; -import { SeoService } from '../../services/seo.service'; +import { ApiService } from '@app/services/api.service'; +import { SeoService } from '@app/services/seo.service'; import { formatNumber } from '@angular/common'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { download, formatterXAxis } from '../../shared/graphs.utils'; +import { download, formatterXAxis } from '@app/shared/graphs.utils'; import { ActivatedRoute, Router } from '@angular/router'; -import { FiatShortenerPipe } from '../../shared/pipes/fiat-shortener.pipe'; -import { FiatCurrencyPipe } from '../../shared/pipes/fiat-currency.pipe'; -import { StateService } from '../../services/state.service'; -import { MiningService } from '../../services/mining.service'; -import { StorageService } from '../../services/storage.service'; -import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; +import { FiatShortenerPipe } from '@app/shared/pipes/fiat-shortener.pipe'; +import { FiatCurrencyPipe } from '@app/shared/pipes/fiat-currency.pipe'; +import { StateService } from '@app/services/state.service'; +import { MiningService } from '@app/services/mining.service'; +import { StorageService } from '@app/services/storage.service'; +import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe'; @Component({ selector: 'app-block-fees-subsidy-graph', diff --git a/frontend/src/app/components/block-filters/block-filters.component.ts b/frontend/src/app/components/block-filters/block-filters.component.ts index 7f997617c..2a0c0772a 100644 --- a/frontend/src/app/components/block-filters/block-filters.component.ts +++ b/frontend/src/app/components/block-filters/block-filters.component.ts @@ -1,6 +1,6 @@ import { Component, EventEmitter, Output, HostListener, Input, ChangeDetectorRef, OnChanges, SimpleChanges, OnInit, OnDestroy } from '@angular/core'; -import { ActiveFilter, FilterGroups, FilterMode, GradientMode, TransactionFilters } from '../../shared/filters.utils'; -import { StateService } from '../../services/state.service'; +import { ActiveFilter, FilterGroups, FilterMode, GradientMode, TransactionFilters } from '@app/shared/filters.utils'; +import { StateService } from '@app/services/state.service'; import { Subscription } from 'rxjs'; @@ -115,4 +115,4 @@ export class BlockFiltersComponent implements OnInit, OnChanges, OnDestroy { ngOnDestroy(): void { this.filterSubscription.unsubscribe(); } -} \ No newline at end of file +} diff --git a/frontend/src/app/components/block-health-graph/block-health-graph.component.ts b/frontend/src/app/components/block-health-graph/block-health-graph.component.ts index 6a7168d6b..8d893a85f 100644 --- a/frontend/src/app/components/block-health-graph/block-health-graph.component.ts +++ b/frontend/src/app/components/block-health-graph/block-health-graph.component.ts @@ -1,16 +1,16 @@ import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, NgZone, OnInit } from '@angular/core'; -import { EChartsOption } from '../../graphs/echarts'; +import { EChartsOption } from '@app/graphs/echarts'; import { Observable } from 'rxjs'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; -import { ApiService } from '../../services/api.service'; -import { SeoService } from '../../services/seo.service'; +import { ApiService } from '@app/services/api.service'; +import { SeoService } from '@app/services/seo.service'; import { formatNumber } from '@angular/common'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { download, formatterXAxis, formatterXAxisLabel, formatterXAxisTimeCategory } from '../../shared/graphs.utils'; -import { StorageService } from '../../services/storage.service'; +import { download, formatterXAxis, formatterXAxisLabel, formatterXAxisTimeCategory } from '@app/shared/graphs.utils'; +import { StorageService } from '@app/services/storage.service'; import { ActivatedRoute, Router } from '@angular/router'; -import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; -import { StateService } from '../../services/state.service'; +import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe'; +import { StateService } from '@app/services/state.service'; @Component({ selector: 'app-block-health-graph', diff --git a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts index 3be0692a5..d59e38c13 100644 --- a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts +++ b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts @@ -1,17 +1,17 @@ import { Component, ElementRef, ViewChild, HostListener, Input, Output, EventEmitter, NgZone, AfterViewInit, OnDestroy, OnChanges } from '@angular/core'; -import { TransactionStripped } from '../../interfaces/node-api.interface'; -import { FastVertexArray } from './fast-vertex-array'; -import BlockScene from './block-scene'; -import TxSprite from './tx-sprite'; -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 { TransactionStripped } from '@interfaces/node-api.interface'; +import { FastVertexArray } from '@components/block-overview-graph/fast-vertex-array'; +import BlockScene from '@components/block-overview-graph/block-scene'; +import TxSprite from '@components/block-overview-graph/tx-sprite'; +import TxView from '@components/block-overview-graph/tx-view'; +import { Color, Position } from '@components/block-overview-graph/sprite-types'; +import { Price } from '@app/services/price.service'; +import { StateService } from '@app/services/state.service'; +import { ThemeService } from '@app/services/theme.service'; import { Subscription } from 'rxjs'; -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'; +import { defaultColorFunction, setOpacity, defaultAuditColors, defaultColors, ageColorFunction, contrastColorFunction, contrastAuditColors, contrastColors } from '@components/block-overview-graph/utils'; +import { ActiveFilter, FilterMode, toFlags } from '@app/shared/filters.utils'; +import { detectWebGL } from '@app/shared/graphs.utils'; const unmatchedOpacity = 0.2; const unmatchedAuditColors = { diff --git a/frontend/src/app/components/block-overview-graph/block-scene.ts b/frontend/src/app/components/block-overview-graph/block-scene.ts index 4f07818a5..575f45bd6 100644 --- a/frontend/src/app/components/block-overview-graph/block-scene.ts +++ b/frontend/src/app/components/block-overview-graph/block-scene.ts @@ -1,9 +1,9 @@ -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, contrastColorFunction } from './utils'; -import { ThemeService } from '../../services/theme.service'; +import { FastVertexArray } from '@components/block-overview-graph/fast-vertex-array'; +import TxView from '@components/block-overview-graph/tx-view'; +import { TransactionStripped } from '@interfaces/node-api.interface'; +import { Color, Position, Square, ViewUpdateParams } from '@components/block-overview-graph/sprite-types'; +import { defaultColorFunction, contrastColorFunction } from '@components/block-overview-graph/utils'; +import { ThemeService } from '@app/services/theme.service'; export default class BlockScene { scene: { count: number, offset: { x: number, y: number}}; @@ -917,4 +917,4 @@ class BlockLayout { function feeRateDescending(a: TxView, b: TxView) { return b.feerate - a.feerate; -} \ No newline at end of file +} diff --git a/frontend/src/app/components/block-overview-graph/fast-vertex-array.ts b/frontend/src/app/components/block-overview-graph/fast-vertex-array.ts index bc0900238..42439ef8d 100644 --- a/frontend/src/app/components/block-overview-graph/fast-vertex-array.ts +++ b/frontend/src/app/components/block-overview-graph/fast-vertex-array.ts @@ -8,7 +8,7 @@ or compacting into a smaller Float32Array when there's space to do so. */ -import TxSprite from './tx-sprite'; +import TxSprite from '@components/block-overview-graph/tx-sprite'; export class FastVertexArray { length: number; diff --git a/frontend/src/app/components/block-overview-graph/tx-sprite.ts b/frontend/src/app/components/block-overview-graph/tx-sprite.ts index 75c1577fc..d713cbd77 100644 --- a/frontend/src/app/components/block-overview-graph/tx-sprite.ts +++ b/frontend/src/app/components/block-overview-graph/tx-sprite.ts @@ -1,5 +1,5 @@ -import { FastVertexArray } from './fast-vertex-array'; -import { InterpolatedAttribute, Attributes, OptionalAttributes, SpriteUpdateParams, Update } from './sprite-types'; +import { FastVertexArray } from '@components/block-overview-graph/fast-vertex-array'; +import { InterpolatedAttribute, Attributes, OptionalAttributes, SpriteUpdateParams, Update } from '@components/block-overview-graph/sprite-types'; const attribKeys = ['a', 'b', 't', 'v']; const updateKeys = ['x', 'y', 's', 'r', 'g', 'b', 'a']; diff --git a/frontend/src/app/components/block-overview-graph/tx-view.ts b/frontend/src/app/components/block-overview-graph/tx-view.ts index f612368f4..53ce684ed 100644 --- a/frontend/src/app/components/block-overview-graph/tx-view.ts +++ b/frontend/src/app/components/block-overview-graph/tx-view.ts @@ -1,10 +1,10 @@ -import TxSprite from './tx-sprite'; -import { FastVertexArray } from './fast-vertex-array'; -import { SpriteUpdateParams, Square, Color, ViewUpdateParams } from './sprite-types'; -import { hexToColor } from './utils'; -import BlockScene from './block-scene'; -import { TransactionStripped } from '../../interfaces/node-api.interface'; -import { TransactionFlags } from '../../shared/filters.utils'; +import TxSprite from '@components/block-overview-graph/tx-sprite'; +import { FastVertexArray } from '@components/block-overview-graph/fast-vertex-array'; +import { SpriteUpdateParams, Square, Color, ViewUpdateParams } from '@components/block-overview-graph/sprite-types'; +import { hexToColor } from '@components/block-overview-graph/utils'; +import BlockScene from '@components/block-overview-graph/block-scene'; +import { TransactionStripped } from '@interfaces/node-api.interface'; +import { TransactionFlags } from '@app/shared/filters.utils'; const hoverTransitionTime = 300; const defaultHoverColor = hexToColor('1bd8f4'); diff --git a/frontend/src/app/components/block-overview-graph/utils.ts b/frontend/src/app/components/block-overview-graph/utils.ts index 287c4bf34..f051e9d51 100644 --- a/frontend/src/app/components/block-overview-graph/utils.ts +++ b/frontend/src/app/components/block-overview-graph/utils.ts @@ -1,6 +1,6 @@ -import { feeLevels, defaultMempoolFeeColors, contrastMempoolFeeColors } from '../../app.constants'; -import { Color } from './sprite-types'; -import TxView from './tx-view'; +import { feeLevels, defaultMempoolFeeColors, contrastMempoolFeeColors } from '@app/app.constants'; +import { Color } from '@components/block-overview-graph/sprite-types'; +import TxView from '@components/block-overview-graph/tx-view'; export function hexToColor(hex: string): Color { return { diff --git a/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.ts b/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.ts index 0a606983e..ffff1b5ed 100644 --- a/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.ts +++ b/frontend/src/app/components/block-overview-tooltip/block-overview-tooltip.component.ts @@ -1,9 +1,9 @@ import { Component, ElementRef, ViewChild, Input, OnChanges, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core'; -import { Position } from '../../components/block-overview-graph/sprite-types.js'; -import { Price } from '../../services/price.service'; -import { TransactionStripped } from '../../interfaces/node-api.interface.js'; -import { Filter, FilterMode, TransactionFlags, toFilters } from '../../shared/filters.utils'; -import { Block } from '../../interfaces/electrs.interface.js'; +import { Position } from '@components/block-overview-graph/sprite-types.js'; +import { Price } from '@app/services/price.service'; +import { TransactionStripped } from '@interfaces/node-api.interface.js'; +import { Filter, FilterMode, TransactionFlags, toFilters } from '@app/shared/filters.utils'; +import { Block } from '@interfaces/electrs.interface.js'; @Component({ selector: 'app-block-overview-tooltip', diff --git a/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.ts b/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.ts index 63a543674..15dafb151 100644 --- a/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.ts +++ b/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.ts @@ -1,18 +1,18 @@ import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'; -import { echarts, EChartsOption } from '../../graphs/echarts'; +import { echarts, EChartsOption } from '@app/graphs/echarts'; import { Observable } from 'rxjs'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; -import { ApiService } from '../../services/api.service'; -import { SeoService } from '../../services/seo.service'; +import { ApiService } from '@app/services/api.service'; +import { SeoService } from '@app/services/seo.service'; import { formatNumber } from '@angular/common'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { download, formatterXAxis } from '../../shared/graphs.utils'; -import { MiningService } from '../../services/mining.service'; -import { StorageService } from '../../services/storage.service'; +import { download, formatterXAxis } from '@app/shared/graphs.utils'; +import { MiningService } from '@app/services/mining.service'; +import { StorageService } from '@app/services/storage.service'; import { ActivatedRoute } from '@angular/router'; -import { FiatShortenerPipe } from '../../shared/pipes/fiat-shortener.pipe'; -import { FiatCurrencyPipe } from '../../shared/pipes/fiat-currency.pipe'; -import { StateService } from '../../services/state.service'; +import { FiatShortenerPipe } from '@app/shared/pipes/fiat-shortener.pipe'; +import { FiatCurrencyPipe } from '@app/shared/pipes/fiat-currency.pipe'; +import { StateService } from '@app/services/state.service'; @Component({ selector: 'app-block-rewards-graph', diff --git a/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.ts b/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.ts index b0069dca2..2cc0f0098 100644 --- a/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.ts +++ b/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.ts @@ -1,16 +1,16 @@ import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core'; -import { EChartsOption} from '../../graphs/echarts'; +import { EChartsOption} from '@app/graphs/echarts'; import { Observable } from 'rxjs'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; -import { ApiService } from '../../services/api.service'; -import { SeoService } from '../../services/seo.service'; +import { ApiService } from '@app/services/api.service'; +import { SeoService } from '@app/services/seo.service'; import { formatNumber } from '@angular/common'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { StorageService } from '../../services/storage.service'; -import { MiningService } from '../../services/mining.service'; +import { StorageService } from '@app/services/storage.service'; +import { MiningService } from '@app/services/mining.service'; import { ActivatedRoute } from '@angular/router'; -import { download, formatterXAxis } from '../../shared/graphs.utils'; -import { StateService } from '../../services/state.service'; +import { download, formatterXAxis } from '@app/shared/graphs.utils'; +import { StateService } from '@app/services/state.service'; @Component({ selector: 'app-block-sizes-weights-graph', diff --git a/frontend/src/app/components/block-view/block-view.component.ts b/frontend/src/app/components/block-view/block-view.component.ts index 5c3b7719c..b5d5256ee 100644 --- a/frontend/src/app/components/block-view/block-view.component.ts +++ b/frontend/src/app/components/block-view/block-view.component.ts @@ -1,15 +1,15 @@ import { Component, OnInit, OnDestroy, ViewChild, HostListener } from '@angular/core'; import { ActivatedRoute, ParamMap, Router } from '@angular/router'; -import { ElectrsApiService } from '../../services/electrs-api.service'; +import { ElectrsApiService } from '@app/services/electrs-api.service'; import { switchMap, tap, catchError, shareReplay, filter } from 'rxjs/operators'; import { of, Subscription } from 'rxjs'; -import { StateService } from '../../services/state.service'; -import { SeoService } from '../../services/seo.service'; -import { BlockExtended, TransactionStripped } from '../../interfaces/node-api.interface'; -import { ApiService } from '../../services/api.service'; -import { seoDescriptionNetwork } from '../../shared/common.utils'; -import { BlockOverviewGraphComponent } from '../block-overview-graph/block-overview-graph.component'; -import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; +import { StateService } from '@app/services/state.service'; +import { SeoService } from '@app/services/seo.service'; +import { BlockExtended, TransactionStripped } from '@interfaces/node-api.interface'; +import { ApiService } from '@app/services/api.service'; +import { seoDescriptionNetwork } from '@app/shared/common.utils'; +import { BlockOverviewGraphComponent } from '@components/block-overview-graph/block-overview-graph.component'; +import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe'; function bestFitResolution(min, max, n): number { const target = (min + max) / 2; diff --git a/frontend/src/app/components/block/block-preview.component.ts b/frontend/src/app/components/block/block-preview.component.ts index 572f91a38..b2fc3fb6f 100644 --- a/frontend/src/app/components/block/block-preview.component.ts +++ b/frontend/src/app/components/block/block-preview.component.ts @@ -1,16 +1,16 @@ import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core'; import { ActivatedRoute, ParamMap } from '@angular/router'; -import { ElectrsApiService } from '../../services/electrs-api.service'; +import { ElectrsApiService } from '@app/services/electrs-api.service'; import { switchMap, tap, throttleTime, catchError, shareReplay, startWith, pairwise, filter } from 'rxjs/operators'; import { of, Subscription, asyncScheduler, forkJoin } from 'rxjs'; -import { StateService } from '../../services/state.service'; -import { SeoService } from '../../services/seo.service'; -import { OpenGraphService } from '../../services/opengraph.service'; -import { BlockExtended, TransactionStripped } from '../../interfaces/node-api.interface'; -import { ApiService } from '../../services/api.service'; -import { seoDescriptionNetwork } from '../../shared/common.utils'; -import { BlockOverviewGraphComponent } from '../../components/block-overview-graph/block-overview-graph.component'; -import { ServicesApiServices } from '../../services/services-api.service'; +import { StateService } from '@app/services/state.service'; +import { SeoService } from '@app/services/seo.service'; +import { OpenGraphService } from '@app/services/opengraph.service'; +import { BlockExtended, TransactionStripped } from '@interfaces/node-api.interface'; +import { ApiService } from '@app/services/api.service'; +import { seoDescriptionNetwork } from '@app/shared/common.utils'; +import { BlockOverviewGraphComponent } from '@components/block-overview-graph/block-overview-graph.component'; +import { ServicesApiServices } from '@app/services/services-api.service'; @Component({ selector: 'app-block-preview', diff --git a/frontend/src/app/components/block/block-transactions.component.ts b/frontend/src/app/components/block/block-transactions.component.ts index c0cda6c4f..170d8297d 100644 --- a/frontend/src/app/components/block/block-transactions.component.ts +++ b/frontend/src/app/components/block/block-transactions.component.ts @@ -1,10 +1,10 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; -import { StateService } from '../../services/state.service'; -import { Transaction, Vout } from '../../interfaces/electrs.interface'; +import { StateService } from '@app/services/state.service'; +import { Transaction, Vout } from '@interfaces/electrs.interface'; import { Observable, Subscription, catchError, combineLatest, map, of, startWith, switchMap, tap } from 'rxjs'; import { ActivatedRoute, Router } from '@angular/router'; -import { ElectrsApiService } from '../../services/electrs-api.service'; -import { PreloadService } from '../../services/preload.service'; +import { ElectrsApiService } from '@app/services/electrs-api.service'; +import { PreloadService } from '@app/services/preload.service'; @Component({ selector: 'app-block-transactions', diff --git a/frontend/src/app/components/block/block.component.html b/frontend/src/app/components/block/block.component.html index 09c3a5d23..105cdf31a 100644 --- a/frontend/src/app/components/block/block.component.html +++ b/frontend/src/app/components/block/block.component.html @@ -66,10 +66,10 @@ [class.badge-success]="blockAudit?.matchRate >= 99" [class.badge-warning]="blockAudit?.matchRate >= 75 && blockAudit?.matchRate < 99" [class.badge-danger]="blockAudit?.matchRate < 75" - *ngIf="blockAudit?.matchRate != null; else nullHealth" + *ngIf="blockAudit?.matchRate != null && blockAudit?.id === block.id; else nullHealth" >{{ blockAudit?.matchRate }}% - + Unknown diff --git a/frontend/src/app/components/block/block.component.ts b/frontend/src/app/components/block/block.component.ts index baf583744..dab3c00fa 100644 --- a/frontend/src/app/components/block/block.component.ts +++ b/frontend/src/app/components/block/block.component.ts @@ -1,23 +1,23 @@ import { Component, OnInit, OnDestroy, ViewChildren, QueryList, ChangeDetectorRef } from '@angular/core'; import { Location } from '@angular/common'; import { ActivatedRoute, ParamMap, Router } from '@angular/router'; -import { ElectrsApiService } from '../../services/electrs-api.service'; +import { ElectrsApiService } from '@app/services/electrs-api.service'; import { switchMap, tap, throttleTime, catchError, map, shareReplay, startWith, filter } from 'rxjs/operators'; import { Observable, of, Subscription, asyncScheduler, EMPTY, combineLatest, forkJoin } from 'rxjs'; -import { StateService } from '../../services/state.service'; -import { SeoService } from '../../services/seo.service'; -import { WebsocketService } from '../../services/websocket.service'; -import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; -import { Acceleration, BlockAudit, BlockExtended, TransactionStripped } from '../../interfaces/node-api.interface'; -import { ApiService } from '../../services/api.service'; -import { BlockOverviewGraphComponent } from '../../components/block-overview-graph/block-overview-graph.component'; -import { detectWebGL } from '../../shared/graphs.utils'; -import { seoDescriptionNetwork } from '../../shared/common.utils'; -import { PriceService, Price } from '../../services/price.service'; -import { CacheService } from '../../services/cache.service'; -import { ServicesApiServices } from '../../services/services-api.service'; -import { PreloadService } from '../../services/preload.service'; -import { identifyPrioritizedTransactions } from '../../shared/transaction.utils'; +import { StateService } from '@app/services/state.service'; +import { SeoService } from '@app/services/seo.service'; +import { WebsocketService } from '@app/services/websocket.service'; +import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe'; +import { Acceleration, BlockAudit, BlockExtended, TransactionStripped } from '@interfaces/node-api.interface'; +import { ApiService } from '@app/services/api.service'; +import { BlockOverviewGraphComponent } from '@components/block-overview-graph/block-overview-graph.component'; +import { detectWebGL } from '@app/shared/graphs.utils'; +import { seoDescriptionNetwork } from '@app/shared/common.utils'; +import { PriceService, Price } from '@app/services/price.service'; +import { CacheService } from '@app/services/cache.service'; +import { ServicesApiServices } from '@app/services/services-api.service'; +import { PreloadService } from '@app/services/preload.service'; +import { identifyPrioritizedTransactions } from '@app/shared/transaction.utils'; @Component({ selector: 'app-block', @@ -822,4 +822,4 @@ export class BlockComponent implements OnInit, OnDestroy { this.fees = blockReward; } } -} \ No newline at end of file +} diff --git a/frontend/src/app/components/block/block.module.ts b/frontend/src/app/components/block/block.module.ts index 661e52dcf..206492e3f 100644 --- a/frontend/src/app/components/block/block.module.ts +++ b/frontend/src/app/components/block/block.module.ts @@ -1,9 +1,9 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Routes, RouterModule } from '@angular/router'; -import { BlockComponent } from './block.component'; -import { BlockTransactionsComponent } from './block-transactions.component'; -import { SharedModule } from '../../shared/shared.module'; +import { BlockComponent } from '@components/block/block.component'; +import { BlockTransactionsComponent } from '@components/block/block-transactions.component'; +import { SharedModule } from '@app/shared/shared.module'; const routes: Routes = [ { diff --git a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts index 1a7598079..008ab1052 100644 --- a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts +++ b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts @@ -1,10 +1,10 @@ import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef, Input, OnChanges, SimpleChanges } from '@angular/core'; import { Observable, Subscription, delay, filter, tap } from 'rxjs'; -import { StateService } from '../../services/state.service'; -import { specialBlocks } from '../../app.constants'; -import { BlockExtended } from '../../interfaces/node-api.interface'; +import { StateService } from '@app/services/state.service'; +import { specialBlocks } from '@app/app.constants'; +import { BlockExtended } from '@interfaces/node-api.interface'; import { Location } from '@angular/common'; -import { CacheService } from '../../services/cache.service'; +import { CacheService } from '@app/services/cache.service'; interface BlockchainBlock extends BlockExtended { placeholder?: boolean; diff --git a/frontend/src/app/components/blockchain/blockchain.component.ts b/frontend/src/app/components/blockchain/blockchain.component.ts index d70e788a2..2e3224a9c 100644 --- a/frontend/src/app/components/blockchain/blockchain.component.ts +++ b/frontend/src/app/components/blockchain/blockchain.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, Input, Output, EventEmitter, ChangeDetectorRef, OnChanges, SimpleChanges } from '@angular/core'; import { firstValueFrom, Subscription } from 'rxjs'; -import { StateService } from '../../services/state.service'; -import { StorageService } from '../../services/storage.service'; +import { StateService } from '@app/services/state.service'; +import { StorageService } from '@app/services/storage.service'; @Component({ selector: 'app-blockchain', diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.html b/frontend/src/app/components/blocks-list/blocks-list.component.html index e2cd0a665..807d429bf 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.html +++ b/frontend/src/app/components/blocks-list/blocks-list.component.html @@ -4,8 +4,8 @@

Blocks

+
-
diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.scss b/frontend/src/app/components/blocks-list/blocks-list.component.scss index 2315844ae..9e4465cf1 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.scss +++ b/frontend/src/app/components/blocks-list/blocks-list.component.scss @@ -1,7 +1,9 @@ .spinner-border { height: 25px; width: 25px; - margin-top: 13px; + margin-top: -10px; + margin-left: -13px; + flex-shrink: 0; } .container-xl { diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.ts b/frontend/src/app/components/blocks-list/blocks-list.component.ts index 5270ee7be..a5e6486a6 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.ts +++ b/frontend/src/app/components/blocks-list/blocks-list.component.ts @@ -2,13 +2,13 @@ import { Component, OnInit, ChangeDetectionStrategy, Input, ChangeDetectorRef, I import { ActivatedRoute, Router } from '@angular/router'; import { BehaviorSubject, combineLatest, Observable, timer, of, Subscription } from 'rxjs'; import { debounceTime, delayWhen, filter, map, retryWhen, scan, skip, switchMap, tap, throttleTime } from 'rxjs/operators'; -import { BlockExtended } from '../../interfaces/node-api.interface'; -import { ApiService } from '../../services/api.service'; -import { StateService } from '../../services/state.service'; -import { WebsocketService } from '../../services/websocket.service'; -import { SeoService } from '../../services/seo.service'; -import { OpenGraphService } from '../../services/opengraph.service'; -import { seoDescriptionNetwork } from '../../shared/common.utils'; +import { BlockExtended } from '@interfaces/node-api.interface'; +import { ApiService } from '@app/services/api.service'; +import { StateService } from '@app/services/state.service'; +import { WebsocketService } from '@app/services/websocket.service'; +import { SeoService } from '@app/services/seo.service'; +import { OpenGraphService } from '@app/services/opengraph.service'; +import { seoDescriptionNetwork } from '@app/shared/common.utils'; @Component({ selector: 'app-blocks-list', diff --git a/frontend/src/app/components/calculator/calculator.component.ts b/frontend/src/app/components/calculator/calculator.component.ts index a6f10c049..c276db323 100644 --- a/frontend/src/app/components/calculator/calculator.component.ts +++ b/frontend/src/app/components/calculator/calculator.component.ts @@ -2,8 +2,8 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; import { combineLatest, Observable } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; -import { StateService } from '../../services/state.service'; -import { WebsocketService } from '../../services/websocket.service'; +import { StateService } from '@app/services/state.service'; +import { WebsocketService } from '@app/services/websocket.service'; @Component({ selector: 'app-calculator', diff --git a/frontend/src/app/components/clock-face/clock-face.component.ts b/frontend/src/app/components/clock-face/clock-face.component.ts index eec0fa98c..a13594597 100644 --- a/frontend/src/app/components/clock-face/clock-face.component.ts +++ b/frontend/src/app/components/clock-face/clock-face.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { Subscription, tap, timer } from 'rxjs'; -import { StateService } from '../../services/state.service'; +import { StateService } from '@app/services/state.service'; @Component({ selector: 'app-clock-face', diff --git a/frontend/src/app/components/clock/clock.component.ts b/frontend/src/app/components/clock/clock.component.ts index 4a9b19e78..90b3d5d26 100644 --- a/frontend/src/app/components/clock/clock.component.ts +++ b/frontend/src/app/components/clock/clock.component.ts @@ -1,11 +1,11 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostListener, Input, OnInit } from '@angular/core'; import { Observable, Subscription, of, switchMap, tap } from 'rxjs'; -import { StateService } from '../../services/state.service'; -import { BlockExtended } from '../../interfaces/node-api.interface'; -import { WebsocketService } from '../../services/websocket.service'; -import { MempoolInfo, Recommendedfees } from '../../interfaces/websocket.interface'; +import { StateService } from '@app/services/state.service'; +import { BlockExtended } from '@interfaces/node-api.interface'; +import { WebsocketService } from '@app/services/websocket.service'; +import { MempoolInfo, Recommendedfees } from '@interfaces/websocket.interface'; import { ActivatedRoute, ParamMap, Router } from '@angular/router'; -import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; +import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe'; @Component({ selector: 'app-clock', diff --git a/frontend/src/app/components/clockchain/clockchain.component.ts b/frontend/src/app/components/clockchain/clockchain.component.ts index c17b1e0ae..41faa897b 100644 --- a/frontend/src/app/components/clockchain/clockchain.component.ts +++ b/frontend/src/app/components/clockchain/clockchain.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, Input, OnChanges, ChangeDetectorRef } from '@angular/core'; import { firstValueFrom, Subscription } from 'rxjs'; -import { StateService } from '../../services/state.service'; +import { StateService } from '@app/services/state.service'; @Component({ selector: 'app-clockchain', diff --git a/frontend/src/app/components/custom-dashboard/custom-dashboard.component.html b/frontend/src/app/components/custom-dashboard/custom-dashboard.component.html index bf72aab69..13f49c5df 100644 --- a/frontend/src/app/components/custom-dashboard/custom-dashboard.component.html +++ b/frontend/src/app/components/custom-dashboard/custom-dashboard.component.html @@ -257,6 +257,38 @@
} + @case ('walletBalance') { +
+
Treasury
+ +
+ } + @case ('wallet') { + + } + @case ('walletTransactions') { +
+
+
+ +
Treasury Transactions
+
+ +
+
+
+ } @case ('twitter') {
diff --git a/frontend/src/app/components/custom-dashboard/custom-dashboard.component.ts b/frontend/src/app/components/custom-dashboard/custom-dashboard.component.ts index fbaf7be74..59bbc32ef 100644 --- a/frontend/src/app/components/custom-dashboard/custom-dashboard.component.ts +++ b/frontend/src/app/components/custom-dashboard/custom-dashboard.component.ts @@ -1,16 +1,16 @@ import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, HostListener, Inject, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core'; import { combineLatest, merge, Observable, of, Subject, Subscription } from 'rxjs'; import { catchError, filter, map, scan, share, shareReplay, startWith, switchMap, tap } from 'rxjs/operators'; -import { BlockExtended, OptimizedMempoolStats, TransactionStripped } from '../../interfaces/node-api.interface'; -import { MempoolInfo, ReplacementInfo } from '../../interfaces/websocket.interface'; -import { ApiService } from '../../services/api.service'; -import { StateService } from '../../services/state.service'; -import { WebsocketService } from '../../services/websocket.service'; -import { SeoService } from '../../services/seo.service'; -import { ActiveFilter, FilterMode, GradientMode, toFlags } from '../../shared/filters.utils'; -import { detectWebGL } from '../../shared/graphs.utils'; -import { Address, AddressTxSummary } from '../../interfaces/electrs.interface'; -import { ElectrsApiService } from '../../services/electrs-api.service'; +import { BlockExtended, OptimizedMempoolStats, TransactionStripped } from '@interfaces/node-api.interface'; +import { MempoolInfo, ReplacementInfo } from '@interfaces/websocket.interface'; +import { ApiService } from '@app/services/api.service'; +import { StateService } from '@app/services/state.service'; +import { WebsocketService } from '@app/services/websocket.service'; +import { SeoService } from '@app/services/seo.service'; +import { ActiveFilter, FilterMode, GradientMode, toFlags } from '@app/shared/filters.utils'; +import { detectWebGL } from '@app/shared/graphs.utils'; +import { Address, AddressTxSummary } from '@interfaces/electrs.interface'; +import { ElectrsApiService } from '@app/services/electrs-api.service'; interface MempoolBlocksData { blocks: number; @@ -62,8 +62,10 @@ export class CustomDashboardComponent implements OnInit, OnDestroy, AfterViewIni widgets; addressSubscription: Subscription; + walletSubscription: Subscription; blockTxSubscription: Subscription; addressSummary$: Observable; + walletSummary$: Observable; address: Address; goggleResolution = 82; @@ -107,6 +109,10 @@ export class CustomDashboardComponent implements OnInit, OnDestroy, AfterViewIni this.websocketService.stopTrackingAddress(); this.address = null; } + if (this.walletSubscription) { + this.walletSubscription.unsubscribe(); + this.websocketService.stopTrackingWallet(); + } this.destroy$.next(1); this.destroy$.complete(); } @@ -260,6 +266,7 @@ export class CustomDashboardComponent implements OnInit, OnDestroy, AfterViewIni }); this.startAddressSubscription(); + this.startWalletSubscription(); } handleNewMempoolData(mempoolStats: OptimizedMempoolStats[]) { @@ -358,6 +365,75 @@ export class CustomDashboardComponent implements OnInit, OnDestroy, AfterViewIni } } + startWalletSubscription(): void { + if (this.stateService.env.customize && this.stateService.env.customize.dashboard.widgets.some(w => w.props?.wallet)) { + const walletName = this.stateService.env.customize.dashboard.widgets.find(w => w.props?.wallet).props.wallet; + this.websocketService.startTrackingWallet(walletName); + + this.walletSummary$ = this.apiService.getWallet$(walletName).pipe( + catchError(e => { + return of({}); + }), + switchMap(wallet => this.stateService.walletTransactions$.pipe( + startWith([]), + scan((summaries, newTransactions) => { + const newSummaries: AddressTxSummary[] = []; + for (const tx of newTransactions) { + const funded: Record = {}; + const spent: Record = {}; + const fundedCount: Record = {}; + const spentCount: Record = {}; + for (const vin of tx.vin) { + const address = vin.prevout?.scriptpubkey_address; + if (address && wallet[address]) { + spent[address] = (spent[address] ?? 0) + (vin.prevout?.value ?? 0); + spentCount[address] = (spentCount[address] ?? 0) + 1; + } + } + for (const vout of tx.vout) { + const address = vout.scriptpubkey_address; + if (address && wallet[address]) { + funded[address] = (funded[address] ?? 0) + (vout.value ?? 0); + fundedCount[address] = (fundedCount[address] ?? 0) + 1; + } + } + for (const address of Object.keys({ ...funded, ...spent })) { + // add tx to summary + const txSummary: AddressTxSummary = { + txid: tx.txid, + value: (funded[address] ?? 0) - (spent[address] ?? 0), + height: tx.status.block_height, + time: tx.status.block_time, + }; + wallet[address].transactions?.push(txSummary); + newSummaries.push(txSummary); + } + } + return this.deduplicateWalletTransactions([...summaries, ...newSummaries]); + }, this.deduplicateWalletTransactions(Object.values(wallet).flatMap(address => address.transactions))) + )), + share(), + ); + } + } + + deduplicateWalletTransactions(walletTransactions: AddressTxSummary[]): AddressTxSummary[] { + const transactions = new Map(); + for (const tx of walletTransactions) { + if (transactions.has(tx.txid)) { + transactions.get(tx.txid).value += tx.value; + } else { + transactions.set(tx.txid, tx); + } + } + return Array.from(transactions.values()).sort((a, b) => { + if (a.height === b.height) { + return b.tx_position - a.tx_position; + } + return b.height - a.height; + }); + } + @HostListener('window:resize', ['$event']) onResize(): void { if (window.innerWidth >= 992) { diff --git a/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.components.ts b/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.components.ts index 438a50f74..1257a233a 100644 --- a/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.components.ts +++ b/frontend/src/app/components/difficulty-adjustments-table/difficulty-adjustments-table.components.ts @@ -1,10 +1,10 @@ import { Component, Inject, LOCALE_ID, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import { ApiService } from '../../services/api.service'; +import { ApiService } from '@app/services/api.service'; import { formatNumber } from '@angular/common'; -import { selectPowerOfTen } from '../../bitcoin.utils'; -import { StateService } from '../../services/state.service'; +import { selectPowerOfTen } from '@app/bitcoin.utils'; +import { StateService } from '@app/services/state.service'; @Component({ selector: 'app-difficulty-adjustments-table', diff --git a/frontend/src/app/components/difficulty-mining/difficulty-mining.component.ts b/frontend/src/app/components/difficulty-mining/difficulty-mining.component.ts index e19f510b5..84912c8dc 100644 --- a/frontend/src/app/components/difficulty-mining/difficulty-mining.component.ts +++ b/frontend/src/app/components/difficulty-mining/difficulty-mining.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { combineLatest, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import { StateService } from '../../services/state.service'; +import { StateService } from '@app/services/state.service'; interface EpochProgress { base: string; diff --git a/frontend/src/app/components/difficulty/difficulty.component.ts b/frontend/src/app/components/difficulty/difficulty.component.ts index 6a99aecef..3737754df 100644 --- a/frontend/src/app/components/difficulty/difficulty.component.ts +++ b/frontend/src/app/components/difficulty/difficulty.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostListener, ElementRef, ViewChild, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'; import { combineLatest, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import { StateService } from '../..//services/state.service'; +import { StateService } from '@app/services/state.service'; interface EpochProgress { base: string; @@ -247,4 +247,4 @@ function getNextBlockSubsidy(height: number): number { // Subsidy is cut in half every 210,000 blocks which will occur approximately every 4 years. subsidy >>= BigInt(halvings); return Number(subsidy); -} \ No newline at end of file +} diff --git a/frontend/src/app/components/eight-blocks/eight-blocks.component.ts b/frontend/src/app/components/eight-blocks/eight-blocks.component.ts index 81dcc4c5b..8ca8437ac 100644 --- a/frontend/src/app/components/eight-blocks/eight-blocks.component.ts +++ b/frontend/src/app/components/eight-blocks/eight-blocks.component.ts @@ -2,15 +2,15 @@ import { Component, OnInit, OnDestroy, ViewChildren, QueryList } from '@angular/ import { ActivatedRoute, Router } from '@angular/router'; import { catchError, startWith } from 'rxjs/operators'; import { Subject, Subscription, of } from 'rxjs'; -import { StateService } from '../../services/state.service'; -import { WebsocketService } from '../../services/websocket.service'; -import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; -import { BlockExtended, TransactionStripped } from '../../interfaces/node-api.interface'; -import { ApiService } from '../../services/api.service'; -import { BlockOverviewGraphComponent } from '../block-overview-graph/block-overview-graph.component'; -import { detectWebGL } from '../../shared/graphs.utils'; +import { StateService } from '@app/services/state.service'; +import { WebsocketService } from '@app/services/websocket.service'; +import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe'; +import { BlockExtended, TransactionStripped } from '@interfaces/node-api.interface'; +import { ApiService } from '@app/services/api.service'; +import { BlockOverviewGraphComponent } from '@components/block-overview-graph/block-overview-graph.component'; +import { detectWebGL } from '@app/shared/graphs.utils'; import { animate, style, transition, trigger } from '@angular/animations'; -import { BytesPipe } from '../../shared/pipes/bytes-pipe/bytes.pipe'; +import { BytesPipe } from '@app/shared/pipes/bytes-pipe/bytes.pipe'; function bestFitResolution(min, max, n): number { const target = (min + max) / 2; diff --git a/frontend/src/app/components/faucet/faucet.component.html b/frontend/src/app/components/faucet/faucet.component.html index 0f0307e54..3165ae9a7 100644 --- a/frontend/src/app/components/faucet/faucet.component.html +++ b/frontend/src/app/components/faucet/faucet.component.html @@ -27,6 +27,14 @@
} + @else if (user && user.status === 'pending' && !user.email && user.snsId) { +
+ + + Please verify your account by providing a valid email address. To mitigate spam, we delete unverified accounts at regular intervals. + +
+ } @else if (error === 'not_available') {
diff --git a/frontend/src/app/components/faucet/faucet.component.ts b/frontend/src/app/components/faucet/faucet.component.ts index 566a3b970..33d9a849e 100644 --- a/frontend/src/app/components/faucet/faucet.component.ts +++ b/frontend/src/app/components/faucet/faucet.component.ts @@ -1,13 +1,12 @@ -import { Component, OnDestroy, OnInit, ChangeDetectorRef } from "@angular/core"; -import { FormBuilder, FormGroup, Validators, ValidatorFn, AbstractControl, ValidationErrors } from "@angular/forms"; -import { Subscription } from "rxjs"; -import { StorageService } from "../../services/storage.service"; -import { ServicesApiServices } from "../../services/services-api.service"; -import { getRegex } from "../../shared/regex.utils"; -import { StateService } from "../../services/state.service"; -import { WebsocketService } from "../../services/websocket.service"; -import { AudioService } from "../../services/audio.service"; -import { HttpErrorResponse } from "@angular/common/http"; +import { Component, OnDestroy, OnInit, ChangeDetectorRef } from '@angular/core'; +import { FormBuilder, FormGroup, Validators, ValidatorFn, AbstractControl, ValidationErrors } from '@angular/forms'; +import { Subscription } from 'rxjs'; +import { ServicesApiServices } from '@app/services/services-api.service'; +import { getRegex } from '@app/shared/regex.utils'; +import { StateService } from '@app/services/state.service'; +import { WebsocketService } from '@app/services/websocket.service'; +import { AudioService } from '@app/services/audio.service'; +import { HttpErrorResponse } from '@angular/common/http'; @Component({ selector: 'app-faucet', @@ -34,7 +33,6 @@ export class FaucetComponent implements OnInit, OnDestroy { constructor( private cd: ChangeDetectorRef, - private storageService: StorageService, private servicesApiService: ServicesApiServices, private formBuilder: FormBuilder, private stateService: StateService, @@ -56,14 +54,17 @@ export class FaucetComponent implements OnInit, OnDestroy { } ngOnInit() { - this.user = this.storageService.getAuth()?.user ?? null; - if (!this.user) { - this.loading = false; - return; - } - - // Setup form - this.updateFaucetStatus(); + this.servicesApiService.userSubject$.subscribe(user => { + this.user = user; + if (!user) { + this.loading = false; + this.cd.markForCheck(); + return; + } + // Setup form + this.updateFaucetStatus(); + this.cd.markForCheck(); + }); // Track transaction this.websocketService.want(['blocks', 'mempool-blocks']); @@ -145,9 +146,6 @@ export class FaucetComponent implements OnInit, OnDestroy { 'address': ['', [Validators.required, Validators.pattern(getRegex('address', 'testnet4')), this.getNotFaucetAddressValidator(faucetAddress)]], 'satoshis': [min, [Validators.required, Validators.min(min), Validators.max(max)]] }); - - this.loading = false; - this.cd.markForCheck(); } updateForm(min, max, faucetAddress: string): void { @@ -160,6 +158,8 @@ export class FaucetComponent implements OnInit, OnDestroy { this.faucetForm.get('satoshis').updateValueAndValidity(); this.faucetForm.get('satoshis').markAsDirty(); } + this.loading = false; + this.cd.markForCheck(); } setAmount(value: number): void { diff --git a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts index c26aae31a..aa57f92d9 100644 --- a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts +++ b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts @@ -1,9 +1,9 @@ import { HostListener, OnChanges, OnDestroy } from '@angular/core'; import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core'; -import { TransactionStripped } from '../../interfaces/node-api.interface'; -import { StateService } from '../../services/state.service'; -import { VbytesPipe } from '../../shared/pipes/bytes-pipe/vbytes.pipe'; -import { selectPowerOfTen } from '../../bitcoin.utils'; +import { TransactionStripped } from '@interfaces/node-api.interface'; +import { StateService } from '@app/services/state.service'; +import { VbytesPipe } from '@app/shared/pipes/bytes-pipe/vbytes.pipe'; +import { selectPowerOfTen } from '@app/bitcoin.utils'; import { Subscription } from 'rxjs'; @Component({ diff --git a/frontend/src/app/components/fees-box/fees-box.component.ts b/frontend/src/app/components/fees-box/fees-box.component.ts index 78fd102ca..b8689bd3c 100644 --- a/frontend/src/app/components/fees-box/fees-box.component.ts +++ b/frontend/src/app/components/fees-box/fees-box.component.ts @@ -1,10 +1,10 @@ import { Component, OnInit, ChangeDetectionStrategy, OnDestroy, ChangeDetectorRef } from '@angular/core'; -import { StateService } from '../../services/state.service'; +import { StateService } from '@app/services/state.service'; import { Observable, combineLatest, Subscription } from 'rxjs'; -import { Recommendedfees } from '../../interfaces/websocket.interface'; -import { feeLevels } from '../../app.constants'; +import { Recommendedfees } from '@interfaces/websocket.interface'; +import { feeLevels } from '@app/app.constants'; import { map, startWith, tap } from 'rxjs/operators'; -import { ThemeService } from '../../services/theme.service'; +import { ThemeService } from '@app/services/theme.service'; @Component({ selector: 'app-fees-box', diff --git a/frontend/src/app/components/fiat-selector/fiat-selector.component.ts b/frontend/src/app/components/fiat-selector/fiat-selector.component.ts index 732c6e862..a9d4b06a3 100644 --- a/frontend/src/app/components/fiat-selector/fiat-selector.component.ts +++ b/frontend/src/app/components/fiat-selector/fiat-selector.component.ts @@ -1,8 +1,8 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { StorageService } from '../../services/storage.service'; -import { fiatCurrencies } from '../../app.constants'; -import { StateService } from '../../services/state.service'; +import { StorageService } from '@app/services/storage.service'; +import { fiatCurrencies } from '@app/app.constants'; +import { StateService } from '@app/services/state.service'; @Component({ selector: 'app-fiat-selector', diff --git a/frontend/src/app/components/footer/footer.component.ts b/frontend/src/app/components/footer/footer.component.ts index a78d1e195..4001a3875 100644 --- a/frontend/src/app/components/footer/footer.component.ts +++ b/frontend/src/app/components/footer/footer.component.ts @@ -1,8 +1,8 @@ import { Component, OnInit, ChangeDetectionStrategy, Input } from '@angular/core'; -import { StateService } from '../../services/state.service'; +import { StateService } from '@app/services/state.service'; import { Observable, combineLatest } from 'rxjs'; import { map } from 'rxjs/operators'; -import { MempoolInfo } from '../../interfaces/websocket.interface'; +import { MempoolInfo } from '@interfaces/websocket.interface'; interface MempoolBlocksData { blocks: number; diff --git a/frontend/src/app/components/graphs/graphs.component.ts b/frontend/src/app/components/graphs/graphs.component.ts index d6dcddb2e..c8c620f54 100644 --- a/frontend/src/app/components/graphs/graphs.component.ts +++ b/frontend/src/app/components/graphs/graphs.component.ts @@ -1,6 +1,8 @@ import { Component, OnInit } from '@angular/core'; -import { StateService } from '../../services/state.service'; -import { WebsocketService } from '../../services/websocket.service'; +import { StateService } from '@app/services/state.service'; +import { WebsocketService } from '@app/services/websocket.service'; +import { Router, ActivatedRoute } from '@angular/router'; +import { handleDemoRedirect } from '../../shared/common.utils'; @Component({ selector: 'app-graphs', @@ -13,7 +15,9 @@ export class GraphsComponent implements OnInit { constructor( public stateService: StateService, - private websocketService: WebsocketService + private websocketService: WebsocketService, + private router: Router, + private route: ActivatedRoute ) { } ngOnInit(): void { @@ -22,5 +26,7 @@ export class GraphsComponent implements OnInit { if (this.stateService.env.ACCELERATOR === true && (this.stateService.env.MINING_DASHBOARD === true || this.stateService.env.LIGHTNING === true)) { this.flexWrap = true; } + + handleDemoRedirect(this.route, this.router); } } diff --git a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html index b50389ce8..b8a720743 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html @@ -5,7 +5,7 @@
-
Hashrate
+
Hashrate (1w)

{{ hashrates.currentHashrate | amountShortener: 1 : 'H/s' }}

diff --git a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts index 0d193514d..d53916b97 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts @@ -1,18 +1,18 @@ import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core'; -import { echarts, EChartsOption } from '../../graphs/echarts'; +import { echarts, EChartsOption } from '@app/graphs/echarts'; import { combineLatest, fromEvent, merge, Observable, of } from 'rxjs'; import { map, mergeMap, share, startWith, switchMap, tap } from 'rxjs/operators'; -import { ApiService } from '../../services/api.service'; -import { SeoService } from '../../services/seo.service'; +import { ApiService } from '@app/services/api.service'; +import { SeoService } from '@app/services/seo.service'; import { formatNumber } from '@angular/common'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { selectPowerOfTen } from '../../bitcoin.utils'; -import { StorageService } from '../../services/storage.service'; -import { MiningService } from '../../services/mining.service'; -import { download } from '../../shared/graphs.utils'; +import { selectPowerOfTen } from '@app/bitcoin.utils'; +import { StorageService } from '@app/services/storage.service'; +import { MiningService } from '@app/services/mining.service'; +import { download } from '@app/shared/graphs.utils'; import { ActivatedRoute } from '@angular/router'; -import { StateService } from '../../services/state.service'; -import { seoDescriptionNetwork } from '../../shared/common.utils'; +import { StateService } from '@app/services/state.service'; +import { seoDescriptionNetwork } from '@app/shared/common.utils'; @Component({ selector: 'app-hashrate-chart', diff --git a/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.ts b/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.ts index 3fca15bf3..f93cf460d 100644 --- a/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.ts +++ b/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.ts @@ -1,16 +1,16 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core'; -import { EChartsOption } from '../../graphs/echarts'; +import { EChartsOption } from '@app/graphs/echarts'; import { Observable } from 'rxjs'; import { delay, map, retryWhen, share, startWith, switchMap, tap } from 'rxjs/operators'; -import { ApiService } from '../../services/api.service'; -import { SeoService } from '../../services/seo.service'; +import { ApiService } from '@app/services/api.service'; +import { SeoService } from '@app/services/seo.service'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { chartColors, poolsColor } from '../../app.constants'; -import { StorageService } from '../../services/storage.service'; -import { MiningService } from '../../services/mining.service'; -import { download } from '../../shared/graphs.utils'; +import { chartColors, poolsColor } from '@app/app.constants'; +import { StorageService } from '@app/services/storage.service'; +import { MiningService } from '@app/services/mining.service'; +import { download } from '@app/shared/graphs.utils'; import { ActivatedRoute } from '@angular/router'; -import { StateService } from '../../services/state.service'; +import { StateService } from '@app/services/state.service'; interface Hashrate { timestamp: number; diff --git a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts index 3487d6fb0..754d5bdde 100644 --- a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts +++ b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts @@ -1,10 +1,10 @@ import { Component, Input, Inject, LOCALE_ID, ChangeDetectionStrategy, OnInit, OnDestroy } from '@angular/core'; -import { EChartsOption } from '../../graphs/echarts'; +import { EChartsOption } from '@app/graphs/echarts'; import { OnChanges } from '@angular/core'; -import { StorageService } from '../../services/storage.service'; -import { download, formatterXAxis, formatterXAxisLabel } from '../../shared/graphs.utils'; +import { StorageService } from '@app/services/storage.service'; +import { download, formatterXAxis, formatterXAxisLabel } from '@app/shared/graphs.utils'; import { formatNumber } from '@angular/common'; -import { StateService } from '../../services/state.service'; +import { StateService } from '@app/services/state.service'; import { Subscription } from 'rxjs'; const OUTLIERS_MEDIAN_MULTIPLIER = 4; diff --git a/frontend/src/app/components/language-selector/language-selector.component.ts b/frontend/src/app/components/language-selector/language-selector.component.ts index 2b9e559f0..b6df5599a 100644 --- a/frontend/src/app/components/language-selector/language-selector.component.ts +++ b/frontend/src/app/components/language-selector/language-selector.component.ts @@ -1,8 +1,8 @@ import { DOCUMENT } from '@angular/common'; import { ChangeDetectionStrategy, Component, Inject, OnInit } from '@angular/core'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { languages } from '../../app.constants'; -import { LanguageService } from '../../services/language.service'; +import { languages } from '@app/app.constants'; +import { LanguageService } from '@app/services/language.service'; @Component({ selector: 'app-language-selector', diff --git a/frontend/src/app/components/lbtc-pegs-graph/lbtc-pegs-graph.component.ts b/frontend/src/app/components/lbtc-pegs-graph/lbtc-pegs-graph.component.ts index a8ec36bec..063280898 100644 --- a/frontend/src/app/components/lbtc-pegs-graph/lbtc-pegs-graph.component.ts +++ b/frontend/src/app/components/lbtc-pegs-graph/lbtc-pegs-graph.component.ts @@ -1,7 +1,7 @@ import { Component, Inject, LOCALE_ID, ChangeDetectionStrategy, Input, OnChanges, OnInit } from '@angular/core'; import { formatDate, formatNumber } from '@angular/common'; -import { EChartsOption } from '../../graphs/echarts'; -import { StateService } from '../../services/state.service'; +import { EChartsOption } from '@app/graphs/echarts'; +import { StateService } from '@app/services/state.service'; @Component({ selector: 'app-lbtc-pegs-graph', diff --git a/frontend/src/app/components/liquid-master-page/liquid-master-page.component.ts b/frontend/src/app/components/liquid-master-page/liquid-master-page.component.ts index 07929d894..be4815f28 100644 --- a/frontend/src/app/components/liquid-master-page/liquid-master-page.component.ts +++ b/frontend/src/app/components/liquid-master-page/liquid-master-page.component.ts @@ -1,9 +1,9 @@ import { Component, OnInit } from '@angular/core'; -import { Env, StateService } from '../../services/state.service'; +import { Env, StateService } from '@app/services/state.service'; import { merge, Observable, of} from 'rxjs'; -import { LanguageService } from '../../services/language.service'; -import { EnterpriseService } from '../../services/enterprise.service'; -import { NavigationService } from '../../services/navigation.service'; +import { LanguageService } from '@app/services/language.service'; +import { EnterpriseService } from '@app/services/enterprise.service'; +import { NavigationService } from '@app/services/navigation.service'; @Component({ selector: 'app-liquid-master-page', diff --git a/frontend/src/app/components/liquid-reserves-audit/expired-utxos-stats/expired-utxos-stats.component.ts b/frontend/src/app/components/liquid-reserves-audit/expired-utxos-stats/expired-utxos-stats.component.ts index 90a737275..e9de3cce3 100644 --- a/frontend/src/app/components/liquid-reserves-audit/expired-utxos-stats/expired-utxos-stats.component.ts +++ b/frontend/src/app/components/liquid-reserves-audit/expired-utxos-stats/expired-utxos-stats.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { Observable, map, of } from 'rxjs'; -import { FederationUtxo } from '../../../interfaces/node-api.interface'; +import { FederationUtxo } from '@interfaces/node-api.interface'; @Component({ selector: 'app-expired-utxos-stats', diff --git a/frontend/src/app/components/liquid-reserves-audit/federation-addresses-list/federation-addresses-list.component.ts b/frontend/src/app/components/liquid-reserves-audit/federation-addresses-list/federation-addresses-list.component.ts index caeac1987..e098dfc34 100644 --- a/frontend/src/app/components/liquid-reserves-audit/federation-addresses-list/federation-addresses-list.component.ts +++ b/frontend/src/app/components/liquid-reserves-audit/federation-addresses-list/federation-addresses-list.component.ts @@ -1,10 +1,10 @@ import { Component, OnInit, ChangeDetectionStrategy, Input } from '@angular/core'; import { Observable, Subject, combineLatest, of, timer } from 'rxjs'; import { delayWhen, filter, map, share, shareReplay, switchMap, takeUntil, tap, throttleTime } from 'rxjs/operators'; -import { ApiService } from '../../../services/api.service'; -import { Env, StateService } from '../../../services/state.service'; -import { AuditStatus, CurrentPegs, FederationAddress } from '../../../interfaces/node-api.interface'; -import { WebsocketService } from '../../../services/websocket.service'; +import { ApiService } from '@app/services/api.service'; +import { Env, StateService } from '@app/services/state.service'; +import { AuditStatus, CurrentPegs, FederationAddress } from '@interfaces/node-api.interface'; +import { WebsocketService } from '@app/services/websocket.service'; @Component({ selector: 'app-federation-addresses-list', diff --git a/frontend/src/app/components/liquid-reserves-audit/federation-utxos-list/federation-utxos-list.component.ts b/frontend/src/app/components/liquid-reserves-audit/federation-utxos-list/federation-utxos-list.component.ts index e41c49643..44d0e44f8 100644 --- a/frontend/src/app/components/liquid-reserves-audit/federation-utxos-list/federation-utxos-list.component.ts +++ b/frontend/src/app/components/liquid-reserves-audit/federation-utxos-list/federation-utxos-list.component.ts @@ -2,10 +2,10 @@ import { Component, OnInit, ChangeDetectionStrategy, Input } from '@angular/core import { ActivatedRoute, Router } from '@angular/router'; import { BehaviorSubject, Observable, Subject, combineLatest, of, timer } from 'rxjs'; import { delayWhen, filter, map, share, shareReplay, switchMap, takeUntil, tap, throttleTime } from 'rxjs/operators'; -import { ApiService } from '../../../services/api.service'; -import { Env, StateService } from '../../../services/state.service'; -import { AuditStatus, CurrentPegs, FederationUtxo } from '../../../interfaces/node-api.interface'; -import { WebsocketService } from '../../../services/websocket.service'; +import { ApiService } from '@app/services/api.service'; +import { Env, StateService } from '@app/services/state.service'; +import { AuditStatus, CurrentPegs, FederationUtxo } from '@interfaces/node-api.interface'; +import { WebsocketService } from '@app/services/websocket.service'; @Component({ selector: 'app-federation-utxos-list', diff --git a/frontend/src/app/components/liquid-reserves-audit/federation-wallet/federation-wallet.component.ts b/frontend/src/app/components/liquid-reserves-audit/federation-wallet/federation-wallet.component.ts index 51a4cedc2..1c87a8783 100644 --- a/frontend/src/app/components/liquid-reserves-audit/federation-wallet/federation-wallet.component.ts +++ b/frontend/src/app/components/liquid-reserves-audit/federation-wallet/federation-wallet.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { SeoService } from '../../../services/seo.service'; +import { SeoService } from '@app/services/seo.service'; @Component({ selector: 'app-federation-wallet', diff --git a/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.ts b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.ts index d809f2fa0..f11e03a28 100644 --- a/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.ts +++ b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.ts @@ -2,11 +2,11 @@ import { ActivatedRoute, Router } from '@angular/router'; import { Component, OnInit, ChangeDetectionStrategy, Input, Inject, LOCALE_ID, ChangeDetectorRef } from '@angular/core'; import { BehaviorSubject, Observable, Subject, Subscription, combineLatest, of, timer } from 'rxjs'; import { delayWhen, filter, map, share, shareReplay, switchMap, take, takeUntil, tap, throttleTime } from 'rxjs/operators'; -import { ApiService } from '../../../services/api.service'; -import { Env, StateService } from '../../../services/state.service'; -import { AuditStatus, CurrentPegs, RecentPeg } from '../../../interfaces/node-api.interface'; -import { WebsocketService } from '../../../services/websocket.service'; -import { SeoService } from '../../../services/seo.service'; +import { ApiService } from '@app/services/api.service'; +import { Env, StateService } from '@app/services/state.service'; +import { AuditStatus, CurrentPegs, RecentPeg } from '@interfaces/node-api.interface'; +import { WebsocketService } from '@app/services/websocket.service'; +import { SeoService } from '@app/services/seo.service'; @Component({ selector: 'app-recent-pegs-list', diff --git a/frontend/src/app/components/liquid-reserves-audit/recent-pegs-stats/recent-pegs-stats.component.ts b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-stats/recent-pegs-stats.component.ts index 7bf8e6910..29033b848 100644 --- a/frontend/src/app/components/liquid-reserves-audit/recent-pegs-stats/recent-pegs-stats.component.ts +++ b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-stats/recent-pegs-stats.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; -import { PegsVolume } from '../../../interfaces/node-api.interface'; +import { PegsVolume } from '@interfaces/node-api.interface'; @Component({ selector: 'app-recent-pegs-stats', diff --git a/frontend/src/app/components/liquid-reserves-audit/reserves-ratio/reserves-ratio.component.ts b/frontend/src/app/components/liquid-reserves-audit/reserves-ratio/reserves-ratio.component.ts index 45cd63db0..770940325 100644 --- a/frontend/src/app/components/liquid-reserves-audit/reserves-ratio/reserves-ratio.component.ts +++ b/frontend/src/app/components/liquid-reserves-audit/reserves-ratio/reserves-ratio.component.ts @@ -1,6 +1,6 @@ import { Component, ChangeDetectionStrategy, Input, OnChanges, OnInit, HostListener } from '@angular/core'; -import { EChartsOption } from '../../../graphs/echarts'; -import { CurrentPegs } from '../../../interfaces/node-api.interface'; +import { EChartsOption } from '@app/graphs/echarts'; +import { CurrentPegs } from '@interfaces/node-api.interface'; @Component({ diff --git a/frontend/src/app/components/liquid-reserves-audit/reserves-supply-stats/reserves-supply-stats.component.ts b/frontend/src/app/components/liquid-reserves-audit/reserves-supply-stats/reserves-supply-stats.component.ts index 61f2deb8c..97d1b3da0 100644 --- a/frontend/src/app/components/liquid-reserves-audit/reserves-supply-stats/reserves-supply-stats.component.ts +++ b/frontend/src/app/components/liquid-reserves-audit/reserves-supply-stats/reserves-supply-stats.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; -import { Env, StateService } from '../../../services/state.service'; -import { CurrentPegs } from '../../../interfaces/node-api.interface'; +import { Env, StateService } from '@app/services/state.service'; +import { CurrentPegs } from '@interfaces/node-api.interface'; @Component({ selector: 'app-reserves-supply-stats', diff --git a/frontend/src/app/components/loading-indicator/loading-indicator.component.ts b/frontend/src/app/components/loading-indicator/loading-indicator.component.ts index 83a5ccc72..9cdb0bd06 100644 --- a/frontend/src/app/components/loading-indicator/loading-indicator.component.ts +++ b/frontend/src/app/components/loading-indicator/loading-indicator.component.ts @@ -1,8 +1,8 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import { StateService } from '../../services/state.service'; -import { WebsocketService } from '../../services/websocket.service'; +import { StateService } from '@app/services/state.service'; +import { WebsocketService } from '@app/services/websocket.service'; @Component({ selector: 'app-loading-indicator', diff --git a/frontend/src/app/components/master-page-preview/master-page-preview.component.html b/frontend/src/app/components/master-page-preview/master-page-preview.component.html index 8f3204ec4..01995906f 100644 --- a/frontend/src/app/components/master-page-preview/master-page-preview.component.html +++ b/frontend/src/app/components/master-page-preview/master-page-preview.component.html @@ -6,7 +6,7 @@ } @if (enterpriseInfo?.header_img) { - enterpriseInfo.title + } @else { diff --git a/frontend/src/app/components/master-page-preview/master-page-preview.component.ts b/frontend/src/app/components/master-page-preview/master-page-preview.component.ts index 64bdcfda2..c9db2b143 100644 --- a/frontend/src/app/components/master-page-preview/master-page-preview.component.ts +++ b/frontend/src/app/components/master-page-preview/master-page-preview.component.ts @@ -1,8 +1,8 @@ import { Component, OnInit } from '@angular/core'; -import { StateService } from '../../services/state.service'; +import { StateService } from '@app/services/state.service'; import { Observable, Subscription, merge, of } from 'rxjs'; -import { LanguageService } from '../../services/language.service'; -import { EnterpriseService } from '../../services/enterprise.service'; +import { LanguageService } from '@app/services/language.service'; +import { EnterpriseService } from '@app/services/enterprise.service'; @Component({ selector: 'app-master-page-preview', diff --git a/frontend/src/app/components/master-page-preview/preview-title.component.ts b/frontend/src/app/components/master-page-preview/preview-title.component.ts index a26368c89..07883475b 100644 --- a/frontend/src/app/components/master-page-preview/preview-title.component.ts +++ b/frontend/src/app/components/master-page-preview/preview-title.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { StateService } from '../../services/state.service'; +import { StateService } from '@app/services/state.service'; import { Observable, merge, of } from 'rxjs'; @Component({ diff --git a/frontend/src/app/components/master-page/master-page.component.html b/frontend/src/app/components/master-page/master-page.component.html index 1aa13e309..557529eef 100644 --- a/frontend/src/app/components/master-page/master-page.component.html +++ b/frontend/src/app/components/master-page/master-page.component.html @@ -19,7 +19,7 @@ @if (enterpriseInfo?.header_img) { - enterpriseInfo.title + } @else {
@@ -39,7 +39,7 @@ @if (enterpriseInfo?.header_img) { - enterpriseInfo.title + } @else {
@@ -49,7 +49,7 @@ @if (enterpriseInfo?.header_img) { - enterpriseInfo.title + } @else { diff --git a/frontend/src/app/components/master-page/master-page.component.ts b/frontend/src/app/components/master-page/master-page.component.ts index e351e9196..d8f7edda4 100644 --- a/frontend/src/app/components/master-page/master-page.component.ts +++ b/frontend/src/app/components/master-page/master-page.component.ts @@ -1,12 +1,12 @@ import { Component, OnInit, OnDestroy, Input, ViewChild } from '@angular/core'; import { Router } from '@angular/router'; -import { Env, StateService } from '../../services/state.service'; +import { Env, StateService } from '@app/services/state.service'; import { Observable, merge, of, Subscription } from 'rxjs'; -import { LanguageService } from '../../services/language.service'; -import { EnterpriseService } from '../../services/enterprise.service'; -import { NavigationService } from '../../services/navigation.service'; -import { MenuComponent } from '../menu/menu.component'; -import { StorageService } from '../../services/storage.service'; +import { LanguageService } from '@app/services/language.service'; +import { EnterpriseService } from '@app/services/enterprise.service'; +import { NavigationService } from '@app/services/navigation.service'; +import { MenuComponent } from '@components/menu/menu.component'; +import { StorageService } from '@app/services/storage.service'; @Component({ selector: 'app-master-page', diff --git a/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts b/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts index 50f8b650f..fca8b279c 100644 --- a/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts +++ b/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts @@ -1,16 +1,16 @@ import { Component, ViewChild, Input, Output, EventEmitter, OnInit, OnDestroy, OnChanges, ChangeDetectionStrategy, ChangeDetectorRef, AfterViewInit } from '@angular/core'; -import { StateService } from '../../services/state.service'; -import { MempoolBlockDelta, isMempoolDelta } from '../../interfaces/websocket.interface'; -import { TransactionStripped } from '../../interfaces/node-api.interface'; -import { BlockOverviewGraphComponent } from '../../components/block-overview-graph/block-overview-graph.component'; +import { StateService } from '@app/services/state.service'; +import { MempoolBlockDelta, isMempoolDelta } from '@interfaces/websocket.interface'; +import { TransactionStripped } from '@interfaces/node-api.interface'; +import { BlockOverviewGraphComponent } from '@components/block-overview-graph/block-overview-graph.component'; import { Subscription, BehaviorSubject } from 'rxjs'; -import { WebsocketService } from '../../services/websocket.service'; -import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; +import { WebsocketService } from '@app/services/websocket.service'; +import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe'; import { Router } from '@angular/router'; -import { Color } from '../block-overview-graph/sprite-types'; -import TxView from '../block-overview-graph/tx-view'; -import { FilterMode, GradientMode } from '../../shared/filters.utils'; +import { Color } from '@components/block-overview-graph/sprite-types'; +import TxView from '@components/block-overview-graph/tx-view'; +import { FilterMode, GradientMode } from '@app/shared/filters.utils'; @Component({ selector: 'app-mempool-block-overview', diff --git a/frontend/src/app/components/mempool-block-view/mempool-block-view.component.ts b/frontend/src/app/components/mempool-block-view/mempool-block-view.component.ts index a671033cf..4d2a21064 100644 --- a/frontend/src/app/components/mempool-block-view/mempool-block-view.component.ts +++ b/frontend/src/app/components/mempool-block-view/mempool-block-view.component.ts @@ -1,8 +1,8 @@ import { Component, OnInit, OnDestroy, HostListener } from '@angular/core'; import { ActivatedRoute, ParamMap } from '@angular/router'; import { Subscription, filter, map, switchMap, tap } from 'rxjs'; -import { StateService } from '../../services/state.service'; -import { WebsocketService } from '../../services/websocket.service'; +import { StateService } from '@app/services/state.service'; +import { WebsocketService } from '@app/services/websocket.service'; function bestFitResolution(min, max, n): number { const target = (min + max) / 2; diff --git a/frontend/src/app/components/mempool-block/mempool-block.component.ts b/frontend/src/app/components/mempool-block/mempool-block.component.ts index d2e658302..029f9c616 100644 --- a/frontend/src/app/components/mempool-block/mempool-block.component.ts +++ b/frontend/src/app/components/mempool-block/mempool-block.component.ts @@ -1,14 +1,14 @@ import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, Inject, PLATFORM_ID, ChangeDetectorRef } from '@angular/core'; -import { detectWebGL } from '../../shared/graphs.utils'; -import { StateService } from '../../services/state.service'; +import { detectWebGL } from '@app/shared/graphs.utils'; +import { StateService } from '@app/services/state.service'; import { ActivatedRoute, ParamMap } from '@angular/router'; import { switchMap, map, tap, filter } from 'rxjs/operators'; -import { MempoolBlock } from '../../interfaces/websocket.interface'; -import { TransactionStripped } from '../../interfaces/node-api.interface'; +import { MempoolBlock } from '@interfaces/websocket.interface'; +import { TransactionStripped } from '@interfaces/node-api.interface'; import { Observable, BehaviorSubject } from 'rxjs'; -import { SeoService } from '../../services/seo.service'; -import { seoDescriptionNetwork } from '../../shared/common.utils'; -import { WebsocketService } from '../../services/websocket.service'; +import { SeoService } from '@app/services/seo.service'; +import { seoDescriptionNetwork } from '@app/shared/common.utils'; +import { WebsocketService } from '@app/services/websocket.service'; @Component({ selector: 'app-mempool-block', diff --git a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts index af5a91c65..72d73769c 100644 --- a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts +++ b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts @@ -1,17 +1,17 @@ import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef, HostListener, Input, OnChanges, SimpleChanges, Output, EventEmitter, ViewChild, ElementRef } from '@angular/core'; import { Subscription, Observable, of, combineLatest } from 'rxjs'; -import { MempoolBlock } from '../../interfaces/websocket.interface'; -import { StateService } from '../../services/state.service'; -import { EtaService } from '../../services/eta.service'; +import { MempoolBlock } from '@interfaces/websocket.interface'; +import { StateService } from '@app/services/state.service'; +import { EtaService } from '@app/services/eta.service'; import { Router } from '@angular/router'; import { delay, filter, map, switchMap, tap } from 'rxjs/operators'; -import { feeLevels } from '../../app.constants'; -import { specialBlocks } from '../../app.constants'; -import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; +import { feeLevels } from '@app/app.constants'; +import { specialBlocks } from '@app/app.constants'; +import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe'; import { Location } from '@angular/common'; -import { DifficultyAdjustment, MempoolPosition } from '../../interfaces/node-api.interface'; +import { DifficultyAdjustment, MempoolPosition } from '@interfaces/node-api.interface'; import { animate, style, transition, trigger } from '@angular/animations'; -import { ThemeService } from '../../services/theme.service'; +import { ThemeService } from '@app/services/theme.service'; @Component({ selector: 'app-mempool-blocks', @@ -472,4 +472,4 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy { } return emptyBlocks; } -} \ No newline at end of file +} diff --git a/frontend/src/app/components/mempool-graph/mempool-graph.component.ts b/frontend/src/app/components/mempool-graph/mempool-graph.component.ts index 3a707987f..15e6c6f7a 100644 --- a/frontend/src/app/components/mempool-graph/mempool-graph.component.ts +++ b/frontend/src/app/components/mempool-graph/mempool-graph.component.ts @@ -1,14 +1,14 @@ import { Component, OnInit, Input, Inject, LOCALE_ID, ChangeDetectionStrategy, OnChanges } from '@angular/core'; -import { VbytesPipe } from '../../shared/pipes/bytes-pipe/vbytes.pipe'; -import { WuBytesPipe } from '../../shared/pipes/bytes-pipe/wubytes.pipe'; -import { AmountShortenerPipe } from '../../shared/pipes/amount-shortener.pipe'; +import { VbytesPipe } from '@app/shared/pipes/bytes-pipe/vbytes.pipe'; +import { WuBytesPipe } from '@app/shared/pipes/bytes-pipe/wubytes.pipe'; +import { AmountShortenerPipe } from '@app/shared/pipes/amount-shortener.pipe'; import { formatNumber } from '@angular/common'; -import { OptimizedMempoolStats } from '../../interfaces/node-api.interface'; -import { StateService } from '../../services/state.service'; -import { StorageService } from '../../services/storage.service'; -import { EChartsOption } from '../../graphs/echarts'; -import { feeLevels, chartColors } from '../../app.constants'; -import { download, formatterXAxis, formatterXAxisLabel } from '../../shared/graphs.utils'; +import { OptimizedMempoolStats } from '@interfaces/node-api.interface'; +import { StateService } from '@app/services/state.service'; +import { StorageService } from '@app/services/storage.service'; +import { EChartsOption } from '@app/graphs/echarts'; +import { feeLevels, chartColors } from '@app/app.constants'; +import { download, formatterXAxis, formatterXAxisLabel } from '@app/shared/graphs.utils'; @Component({ selector: 'app-mempool-graph', diff --git a/frontend/src/app/components/menu/menu.component.html b/frontend/src/app/components/menu/menu.component.html index 23605ce55..848f505a1 100644 --- a/frontend/src/app/components/menu/menu.component.html +++ b/frontend/src/app/components/menu/menu.component.html @@ -12,9 +12,15 @@ OG #{{ user.ogRank }} - - {{ user.subscription_tag.toUpperCase() }} - + @if (user.subscription_tag !== 'free') { + + {{ user.subscription_tag.toUpperCase() }} + + } @else if (user.type === 'mining_pool') { + + MINING POOL + + } diff --git a/frontend/src/app/components/menu/menu.component.ts b/frontend/src/app/components/menu/menu.component.ts index 719495bb0..278ec46a1 100644 --- a/frontend/src/app/components/menu/menu.component.ts +++ b/frontend/src/app/components/menu/menu.component.ts @@ -1,11 +1,11 @@ import { Component, OnInit, Input, Output, EventEmitter, HostListener, OnDestroy } from '@angular/core'; import { Observable } from 'rxjs'; -import { MenuGroup } from '../../interfaces/services.interface'; -import { StorageService } from '../../services/storage.service'; +import { MenuGroup } from '@interfaces/services.interface'; +import { StorageService } from '@app/services/storage.service'; import { Router, NavigationStart } from '@angular/router'; -import { StateService } from '../../services/state.service'; -import { IUser, ServicesApiServices } from '../../services/services-api.service'; -import { AuthServiceMempool } from '../../services/auth.service'; +import { StateService } from '@app/services/state.service'; +import { IUser, ServicesApiServices } from '@app/services/services-api.service'; +import { AuthServiceMempool } from '@app/services/auth.service'; @Component({ selector: 'app-menu', diff --git a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts index 0e0974808..464866c40 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts @@ -1,8 +1,8 @@ import { AfterViewInit, ChangeDetectionStrategy, Component, HostListener, OnInit } from '@angular/core'; -import { SeoService } from '../../services/seo.service'; -import { OpenGraphService } from '../../services/opengraph.service'; -import { WebsocketService } from '../../services/websocket.service'; -import { StateService } from '../../services/state.service'; +import { SeoService } from '@app/services/seo.service'; +import { OpenGraphService } from '@app/services/opengraph.service'; +import { WebsocketService } from '@app/services/websocket.service'; +import { StateService } from '@app/services/state.service'; import { EventType, NavigationStart, Router } from '@angular/router'; @Component({ diff --git a/frontend/src/app/components/ngx-bootstrap-multiselect/ngx-bootstrap-multiselect.component.ts b/frontend/src/app/components/ngx-bootstrap-multiselect/ngx-bootstrap-multiselect.component.ts index 8c5dcbfcb..bc835b4d2 100644 --- a/frontend/src/app/components/ngx-bootstrap-multiselect/ngx-bootstrap-multiselect.component.ts +++ b/frontend/src/app/components/ngx-bootstrap-multiselect/ngx-bootstrap-multiselect.component.ts @@ -24,8 +24,8 @@ import { } from '@angular/forms'; import { takeUntil } from 'rxjs/operators'; -import { MultiSelectSearchFilter } from './search-filter.pipe'; -import { IMultiSelectOption, IMultiSelectSettings, IMultiSelectTexts, } from './types'; +import { MultiSelectSearchFilter } from '@components/ngx-bootstrap-multiselect/search-filter.pipe'; +import { IMultiSelectOption, IMultiSelectSettings, IMultiSelectTexts, } from '@components/ngx-bootstrap-multiselect/types'; import { Subject, Observable } from 'rxjs'; const MULTISELECT_VALUE_ACCESSOR: any = { diff --git a/frontend/src/app/components/ngx-bootstrap-multiselect/search-filter.pipe.ts b/frontend/src/app/components/ngx-bootstrap-multiselect/search-filter.pipe.ts index 1dfb57ffd..8c9232501 100644 --- a/frontend/src/app/components/ngx-bootstrap-multiselect/search-filter.pipe.ts +++ b/frontend/src/app/components/ngx-bootstrap-multiselect/search-filter.pipe.ts @@ -1,5 +1,5 @@ import { Pipe, PipeTransform } from '@angular/core'; -import { IMultiSelectOption } from './types'; +import { IMultiSelectOption } from '@components/ngx-bootstrap-multiselect/types'; interface StringHashMap { [k: string]: T; diff --git a/frontend/src/app/components/ord-data/ord-data.component.html b/frontend/src/app/components/ord-data/ord-data.component.html new file mode 100644 index 000000000..8e58e66a4 --- /dev/null +++ b/frontend/src/app/components/ord-data/ord-data.component.html @@ -0,0 +1,65 @@ +@if (minted) { + + Mint + {{ minted >= 100000 ? (minted | amountShortener:undefined:undefined:true) : minted }} + + +} +@if (runestone?.etching?.supply) { + @if (runestone?.etching.premine > 0) { + + Premine + {{ getAmount(runestone.etching.premine, runestone.etching.divisibility || 0) >= 100000 ? (getAmount(runestone.etching.premine, runestone.etching.divisibility || 0) | amountShortener:undefined:undefined:true) : getAmount(runestone.etching.premine, runestone.etching.divisibility || 0) }} + {{ runestone.etching.symbol }} + {{ runestone.etching.spacedName }} + ({{ toNumber(runestone.etching.premine) / toNumber(runestone.etching.supply) * 100 | amountShortener:0}}% of total supply) + + } @else { + + Etching of + {{ runestone.etching.symbol }} + {{ runestone.etching.spacedName }} + + } +} +@if (transferredRunes?.length && type === 'vout') { +
+ + Transfer + + +
+} + +@if (inscriptions?.length && type === 'vin') { +
+} + +@if (!runestone && type === 'vout') { +
+} + +@if ((runestone && !minted && !runestone.etching?.supply && !transferredRunes?.length && type === 'vout') || (!inscriptions?.length && type === 'vin')) { + Error decoding data +} + + + {{ runeInfo[id]?.etching.symbol || '' }} + + {{ runeInfo[id]?.etching.spacedName }} + + \ No newline at end of file diff --git a/frontend/src/app/components/ord-data/ord-data.component.scss b/frontend/src/app/components/ord-data/ord-data.component.scss new file mode 100644 index 000000000..b218359d9 --- /dev/null +++ b/frontend/src/app/components/ord-data/ord-data.component.scss @@ -0,0 +1,35 @@ +.amount { + font-weight: bold; +} + +a.rune-link { + color: inherit; + &:hover { + text-decoration: underline; + text-decoration-color: var(--transparent-fg); + } +} + +a.disabled { + text-decoration: none; +} + +.name { + color: var(--transparent-fg); + font-weight: 700; +} + +.badge-ord { + background-color: var(--grey); + position: relative; + top: -2px; + font-size: 81%; + &.primary { + background-color: var(--primary); + } +} + +pre { + margin-top: 5px; + max-height: 200px; +} \ No newline at end of file diff --git a/frontend/src/app/components/ord-data/ord-data.component.ts b/frontend/src/app/components/ord-data/ord-data.component.ts new file mode 100644 index 000000000..4c0318718 --- /dev/null +++ b/frontend/src/app/components/ord-data/ord-data.component.ts @@ -0,0 +1,87 @@ +import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core'; +import { Runestone, Etching } from '@app/shared/ord/rune.utils'; +import { Inscription } from '@app/shared/ord/inscription.utils'; + +@Component({ + selector: 'app-ord-data', + templateUrl: './ord-data.component.html', + styleUrls: ['./ord-data.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class OrdDataComponent implements OnChanges { + @Input() inscriptions: Inscription[]; + @Input() runestone: Runestone; + @Input() runeInfo: { [id: string]: { etching: Etching; txid: string } }; + @Input() type: 'vin' | 'vout'; + + toNumber = (value: bigint): number => Number(value); + + // Inscriptions + inscriptionsData: { [key: string]: { count: number, totalSize: number, text?: string; json?: JSON; tag?: string; delegate?: string } }; + // Rune mints + minted: number; + // Rune transfers + transferredRunes: { key: string; etching: Etching; txid: string }[] = []; + + constructor() { } + + ngOnChanges(changes: SimpleChanges): void { + if (changes.runestone && this.runestone) { + if (this.runestone.mint && this.runeInfo[this.runestone.mint.toString()]) { + const mint = this.runestone.mint.toString(); + const terms = this.runeInfo[mint].etching.terms; + const amount = terms?.amount; + const divisibility = this.runeInfo[mint].etching.divisibility; + if (amount) { + this.minted = this.getAmount(amount, divisibility); + } + } + + this.runestone.edicts.forEach(edict => { + if (this.runeInfo[edict.id.toString()]) { + this.transferredRunes.push({ key: edict.id.toString(), ...this.runeInfo[edict.id.toString()] }); + } + }); + } + + if (changes.inscriptions && this.inscriptions) { + + if (this.inscriptions?.length) { + this.inscriptionsData = {}; + this.inscriptions.forEach((inscription) => { + // General: count, total size, delegate + const key = inscription.content_type_str || 'undefined'; + if (!this.inscriptionsData[key]) { + this.inscriptionsData[key] = { count: 0, totalSize: 0 }; + } + this.inscriptionsData[key].count++; + this.inscriptionsData[key].totalSize += inscription.body_length; + if (inscription.delegate_txid && !this.inscriptionsData[key].delegate) { + this.inscriptionsData[key].delegate = inscription.delegate_txid; + } + + // Text / JSON data + if ((key.includes('text') || key.includes('json')) && !inscription.is_cropped && !this.inscriptionsData[key].text && !this.inscriptionsData[key].json) { + const decoder = new TextDecoder('utf-8'); + const text = decoder.decode(inscription.body); + try { + this.inscriptionsData[key].json = JSON.parse(text); + if (this.inscriptionsData[key].json['p']) { + this.inscriptionsData[key].tag = this.inscriptionsData[key].json['p'].toUpperCase(); + } + } catch (e) { + this.inscriptionsData[key].text = text; + } + } + }); + } + } + } + + getAmount(amount: bigint, divisibility: number): number { + const divisor = BigInt(10) ** BigInt(divisibility); + const result = amount / divisor; + + return result <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(result) : Number.MAX_SAFE_INTEGER; + } +} diff --git a/frontend/src/app/components/pool-ranking/pool-ranking.component.ts b/frontend/src/app/components/pool-ranking/pool-ranking.component.ts index 2e8a820be..27a219ed3 100644 --- a/frontend/src/app/components/pool-ranking/pool-ranking.component.ts +++ b/frontend/src/app/components/pool-ranking/pool-ranking.component.ts @@ -1,17 +1,17 @@ import { ChangeDetectionStrategy, Component, Input, NgZone, OnInit, HostBinding } from '@angular/core'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; -import { EChartsOption, PieSeriesOption } from '../../graphs/echarts'; +import { EChartsOption, PieSeriesOption } from '@app/graphs/echarts'; import { merge, Observable } from 'rxjs'; import { map, shareReplay, startWith, switchMap, tap } from 'rxjs/operators'; -import { SeoService } from '../../services/seo.service'; -import { StorageService } from '../..//services/storage.service'; -import { MiningService, MiningStats } from '../../services/mining.service'; -import { StateService } from '../../services/state.service'; -import { chartColors, poolsColor } from '../../app.constants'; -import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; -import { download } from '../../shared/graphs.utils'; -import { isMobile } from '../../shared/common.utils'; +import { SeoService } from '@app/services/seo.service'; +import { StorageService } from '@app//services/storage.service'; +import { MiningService, MiningStats } from '@app/services/mining.service'; +import { StateService } from '@app/services/state.service'; +import { chartColors, poolsColor } from '@app/app.constants'; +import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe'; +import { download } from '@app/shared/graphs.utils'; +import { isMobile } from '@app/shared/common.utils'; @Component({ selector: 'app-pool-ranking', diff --git a/frontend/src/app/components/pool/pool-preview.component.ts b/frontend/src/app/components/pool/pool-preview.component.ts index e0c786082..93077120d 100644 --- a/frontend/src/app/components/pool/pool-preview.component.ts +++ b/frontend/src/app/components/pool/pool-preview.component.ts @@ -1,14 +1,14 @@ import { ChangeDetectionStrategy, Component, Inject, LOCALE_ID, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { echarts, EChartsOption } from '../../graphs/echarts'; +import { echarts, EChartsOption } from '@app/graphs/echarts'; import { Observable, of } from 'rxjs'; import { map, switchMap, catchError } from 'rxjs/operators'; -import { PoolStat } from '../../interfaces/node-api.interface'; -import { ApiService } from '../../services/api.service'; -import { StateService } from '../../services/state.service'; +import { PoolStat } from '@interfaces/node-api.interface'; +import { ApiService } from '@app/services/api.service'; +import { StateService } from '@app/services/state.service'; import { formatNumber } from '@angular/common'; -import { SeoService } from '../../services/seo.service'; -import { OpenGraphService } from '../../services/opengraph.service'; +import { SeoService } from '@app/services/seo.service'; +import { OpenGraphService } from '@app/services/opengraph.service'; @Component({ selector: 'app-pool-preview', diff --git a/frontend/src/app/components/pool/pool.component.ts b/frontend/src/app/components/pool/pool.component.ts index 6564a5dd9..1893f0a48 100644 --- a/frontend/src/app/components/pool/pool.component.ts +++ b/frontend/src/app/components/pool/pool.component.ts @@ -1,14 +1,14 @@ import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { echarts, EChartsOption } from '../../graphs/echarts'; +import { echarts, EChartsOption } from '@app/graphs/echarts'; import { BehaviorSubject, Observable, Subscription, combineLatest, of } from 'rxjs'; import { catchError, distinctUntilChanged, filter, map, share, switchMap, tap } from 'rxjs/operators'; -import { BlockExtended, PoolStat } from '../../interfaces/node-api.interface'; -import { ApiService } from '../../services/api.service'; -import { StateService } from '../../services/state.service'; -import { selectPowerOfTen } from '../../bitcoin.utils'; +import { BlockExtended, PoolStat } from '@interfaces/node-api.interface'; +import { ApiService } from '@app/services/api.service'; +import { StateService } from '@app/services/state.service'; +import { selectPowerOfTen } from '@app/bitcoin.utils'; import { formatNumber } from '@angular/common'; -import { SeoService } from '../../services/seo.service'; +import { SeoService } from '@app/services/seo.service'; import { HttpErrorResponse } from '@angular/common/http'; interface AccelerationTotal { diff --git a/frontend/src/app/components/privacy-policy/privacy-policy.component.ts b/frontend/src/app/components/privacy-policy/privacy-policy.component.ts index 05f77c063..339028cd2 100644 --- a/frontend/src/app/components/privacy-policy/privacy-policy.component.ts +++ b/frontend/src/app/components/privacy-policy/privacy-policy.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; -import { Env, StateService } from '../../services/state.service'; -import { SeoService } from '../../services/seo.service'; -import { OpenGraphService } from '../../services/opengraph.service'; +import { Env, StateService } from '@app/services/state.service'; +import { SeoService } from '@app/services/seo.service'; +import { OpenGraphService } from '@app/services/opengraph.service'; @Component({ selector: 'app-privacy-policy', diff --git a/frontend/src/app/components/privacy-policy/privacy-policy.module.ts b/frontend/src/app/components/privacy-policy/privacy-policy.module.ts index 6d279d80a..385fe15c2 100644 --- a/frontend/src/app/components/privacy-policy/privacy-policy.module.ts +++ b/frontend/src/app/components/privacy-policy/privacy-policy.module.ts @@ -1,8 +1,8 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Routes, RouterModule } from '@angular/router'; -import { PrivacyPolicyComponent } from './privacy-policy.component'; -import { SharedModule } from '../../shared/shared.module'; +import { PrivacyPolicyComponent } from '@components/privacy-policy/privacy-policy.component'; +import { SharedModule } from '@app/shared/shared.module'; const routes: Routes = [ { diff --git a/frontend/src/app/components/push-transaction/push-transaction.component.html b/frontend/src/app/components/push-transaction/push-transaction.component.html index dff79afbb..8d8402fd3 100644 --- a/frontend/src/app/components/push-transaction/push-transaction.component.html +++ b/frontend/src/app/components/push-transaction/push-transaction.component.html @@ -9,4 +9,66 @@

{{ error }}

{{ txId }} + @if (network === '' || network === 'testnet' || network === 'testnet4' || network === 'signet') { +
+

Submit Package

+ +
+
+ +
+ + + + +
+ +

{{ errorPackage }}

+

{{ packageMessage }}

+ +
+ +
+ +
+ + + + + + + + + + + + + + + + + +
Allowed?TXIDEffective fee rateRejection reason
+ @if (result.error == null) { + + } + @else { + + } + + @if (!result.error) { + + } @else { + + } + + + - + + {{ result.error || '-' }} +
+
+ }
\ No newline at end of file diff --git a/frontend/src/app/components/push-transaction/push-transaction.component.ts b/frontend/src/app/components/push-transaction/push-transaction.component.ts index 03a050dfa..221333edb 100644 --- a/frontend/src/app/components/push-transaction/push-transaction.component.ts +++ b/frontend/src/app/components/push-transaction/push-transaction.component.ts @@ -1,12 +1,13 @@ import { Component, OnInit } from '@angular/core'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; -import { ApiService } from '../../services/api.service'; -import { StateService } from '../../services/state.service'; -import { SeoService } from '../../services/seo.service'; -import { OpenGraphService } from '../../services/opengraph.service'; -import { seoDescriptionNetwork } from '../../shared/common.utils'; +import { ApiService } from '@app/services/api.service'; +import { StateService } from '@app/services/state.service'; +import { SeoService } from '@app/services/seo.service'; +import { OpenGraphService } from '@app/services/opengraph.service'; +import { seoDescriptionNetwork } from '@app/shared/common.utils'; import { ActivatedRoute, Router } from '@angular/router'; -import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; +import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe'; +import { TxResult } from '@interfaces/node-api.interface'; @Component({ selector: 'app-push-transaction', @@ -19,6 +20,16 @@ export class PushTransactionComponent implements OnInit { txId: string = ''; isLoading = false; + submitTxsForm: UntypedFormGroup; + errorPackage: string = ''; + packageMessage: string = ''; + results: TxResult[] = []; + invalidMaxfeerate = false; + invalidMaxburnamount = false; + isLoadingPackage = false; + + network = this.stateService.network; + constructor( private formBuilder: UntypedFormBuilder, private apiService: ApiService, @@ -35,6 +46,14 @@ export class PushTransactionComponent implements OnInit { txHash: ['', Validators.required], }); + this.submitTxsForm = this.formBuilder.group({ + txs: ['', Validators.required], + maxfeerate: ['', Validators.min(0)], + maxburnamount: ['', Validators.min(0)], + }); + + this.stateService.networkChanged$.subscribe((network) => this.network = network); + this.seoService.setTitle($localize`:@@meta.title.push-tx:Broadcast Transaction`); this.seoService.setDescription($localize`:@@meta.description.push-tx:Broadcast a transaction to the ${this.stateService.network==='liquid'||this.stateService.network==='liquidtestnet'?'Liquid':'Bitcoin'}${seoDescriptionNetwork(this.stateService.network)} network using the transaction's hash.`); this.ogService.setManualOgImage('tx-push.jpg'); @@ -59,7 +78,7 @@ export class PushTransactionComponent implements OnInit { }, (error) => { if (typeof error.error === 'string') { - const matchText = error.error.match('"message":"(.*?)"'); + const matchText = error.error.replace(/\\/g, '').match('"message":"(.*?)"'); this.error = 'Failed to broadcast transaction, reason: ' + (matchText && matchText[1] || error.error); } else if (error.message) { this.error = 'Failed to broadcast transaction, reason: ' + error.message; @@ -70,6 +89,67 @@ export class PushTransactionComponent implements OnInit { }); } + submitTxs() { + let txs: string[] = []; + try { + txs = (this.submitTxsForm.get('txs')?.value as string).split(',').map(hex => hex.trim()); + if (txs?.length === 1) { + this.pushTxForm.get('txHash').setValue(txs[0]); + this.submitTxsForm.get('txs').setValue(''); + this.postTx(); + return; + } + } catch (e) { + this.errorPackage = e?.message; + return; + } + + let maxfeerate; + let maxburnamount; + this.invalidMaxfeerate = false; + this.invalidMaxburnamount = false; + try { + const maxfeerateVal = this.submitTxsForm.get('maxfeerate')?.value; + if (maxfeerateVal != null && maxfeerateVal !== '') { + maxfeerate = parseFloat(maxfeerateVal) / 100_000; + } + } catch (e) { + this.invalidMaxfeerate = true; + } + try { + const maxburnamountVal = this.submitTxsForm.get('maxburnamount')?.value; + if (maxburnamountVal != null && maxburnamountVal !== '') { + maxburnamount = parseInt(maxburnamountVal) / 100_000_000; + } + } catch (e) { + this.invalidMaxburnamount = true; + } + + this.isLoadingPackage = true; + this.errorPackage = ''; + this.results = []; + this.apiService.submitPackage$(txs, maxfeerate === 0.1 ? null : maxfeerate, maxburnamount === 0 ? null : maxburnamount) + .subscribe((result) => { + this.isLoadingPackage = false; + + this.packageMessage = result['package_msg']; + for (let wtxid in result['tx-results']) { + this.results.push(result['tx-results'][wtxid]); + } + + this.submitTxsForm.reset(); + }, + (error) => { + if (typeof error.error?.error === 'string') { + const matchText = error.error.error.replace(/\\/g, '').match('"message":"(.*?)"'); + this.errorPackage = matchText && matchText[1] || error.error.error; + } else if (error.message) { + this.errorPackage = error.message; + } + this.isLoadingPackage = false; + }); + } + private async handleColdcardPushTx(fragmentParams: URLSearchParams): Promise { // maybe conforms to Coldcard nfc-pushtx spec if (fragmentParams && fragmentParams.get('t')) { diff --git a/frontend/src/app/components/qrcode/qrcode.component.ts b/frontend/src/app/components/qrcode/qrcode.component.ts index f377895c0..061625eed 100644 --- a/frontend/src/app/components/qrcode/qrcode.component.ts +++ b/frontend/src/app/components/qrcode/qrcode.component.ts @@ -1,6 +1,6 @@ import { Component, Input, AfterViewInit, ViewChild, ElementRef, ChangeDetectionStrategy } from '@angular/core'; import * as QRCode from 'qrcode'; -import { StateService } from '../../services/state.service'; +import { StateService } from '@app/services/state.service'; @Component({ selector: 'app-qrcode', diff --git a/frontend/src/app/components/rate-unit-selector/rate-unit-selector.component.ts b/frontend/src/app/components/rate-unit-selector/rate-unit-selector.component.ts index a7d94cec2..5e6b324bf 100644 --- a/frontend/src/app/components/rate-unit-selector/rate-unit-selector.component.ts +++ b/frontend/src/app/components/rate-unit-selector/rate-unit-selector.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { StorageService } from '../../services/storage.service'; -import { StateService } from '../../services/state.service'; +import { StorageService } from '@app/services/storage.service'; +import { StateService } from '@app/services/state.service'; import { Subscription } from 'rxjs'; @Component({ diff --git a/frontend/src/app/components/rbf-list/rbf-list.component.ts b/frontend/src/app/components/rbf-list/rbf-list.component.ts index 25f7dea2e..d835b4a59 100644 --- a/frontend/src/app/components/rbf-list/rbf-list.component.ts +++ b/frontend/src/app/components/rbf-list/rbf-list.component.ts @@ -2,13 +2,13 @@ import { Component, OnInit, ChangeDetectionStrategy, OnDestroy } from '@angular/ import { ActivatedRoute, Router } from '@angular/router'; import { BehaviorSubject, EMPTY, merge, Observable, Subscription } from 'rxjs'; import { catchError, switchMap, tap } from 'rxjs/operators'; -import { WebsocketService } from '../../services/websocket.service'; -import { RbfTree } from '../../interfaces/node-api.interface'; -import { ApiService } from '../../services/api.service'; -import { StateService } from '../../services/state.service'; -import { SeoService } from '../../services/seo.service'; -import { OpenGraphService } from '../../services/opengraph.service'; -import { seoDescriptionNetwork } from '../../shared/common.utils'; +import { WebsocketService } from '@app/services/websocket.service'; +import { RbfTree } from '@interfaces/node-api.interface'; +import { ApiService } from '@app/services/api.service'; +import { StateService } from '@app/services/state.service'; +import { SeoService } from '@app/services/seo.service'; +import { OpenGraphService } from '@app/services/opengraph.service'; +import { seoDescriptionNetwork } from '@app/shared/common.utils'; @Component({ selector: 'app-rbf-list', diff --git a/frontend/src/app/components/rbf-timeline/rbf-timeline-tooltip.component.ts b/frontend/src/app/components/rbf-timeline/rbf-timeline-tooltip.component.ts index fc3748f32..3368eeaf3 100644 --- a/frontend/src/app/components/rbf-timeline/rbf-timeline-tooltip.component.ts +++ b/frontend/src/app/components/rbf-timeline/rbf-timeline-tooltip.component.ts @@ -1,5 +1,5 @@ import { Component, ElementRef, ViewChild, Input, OnChanges } from '@angular/core'; -import { RbfTree } from '../../interfaces/node-api.interface'; +import { RbfTree } from '@interfaces/node-api.interface'; @Component({ selector: 'app-rbf-timeline-tooltip', diff --git a/frontend/src/app/components/rbf-timeline/rbf-timeline.component.ts b/frontend/src/app/components/rbf-timeline/rbf-timeline.component.ts index 83654a137..8bf5a0694 100644 --- a/frontend/src/app/components/rbf-timeline/rbf-timeline.component.ts +++ b/frontend/src/app/components/rbf-timeline/rbf-timeline.component.ts @@ -1,8 +1,8 @@ import { Component, Input, OnInit, OnChanges, Inject, LOCALE_ID, HostListener } from '@angular/core'; import { Router } from '@angular/router'; -import { RbfTree, RbfTransaction } from '../../interfaces/node-api.interface'; -import { StateService } from '../../services/state.service'; -import { ApiService } from '../../services/api.service'; +import { RbfTree, RbfTransaction } from '@interfaces/node-api.interface'; +import { StateService } from '@app/services/state.service'; +import { ApiService } from '@app/services/api.service'; type Connector = 'pipe' | 'corner'; diff --git a/frontend/src/app/components/reward-stats/reward-stats.component.ts b/frontend/src/app/components/reward-stats/reward-stats.component.ts index 5aac641b0..34dc55222 100644 --- a/frontend/src/app/components/reward-stats/reward-stats.component.ts +++ b/frontend/src/app/components/reward-stats/reward-stats.component.ts @@ -1,8 +1,8 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { concat, Observable } from 'rxjs'; import { map, switchMap, tap } from 'rxjs/operators'; -import { ApiService } from '../../services/api.service'; -import { StateService } from '../../services/state.service'; +import { ApiService } from '@app/services/api.service'; +import { StateService } from '@app/services/state.service'; @Component({ selector: 'app-reward-stats', diff --git a/frontend/src/app/components/search-form/search-form.component.ts b/frontend/src/app/components/search-form/search-form.component.ts index 3f48861d5..c0654c372 100644 --- a/frontend/src/app/components/search-form/search-form.component.ts +++ b/frontend/src/app/components/search-form/search-form.component.ts @@ -1,15 +1,15 @@ import { Component, OnInit, ChangeDetectionStrategy, EventEmitter, Output, ViewChild, HostListener, ElementRef, Input } from '@angular/core'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { EventType, NavigationStart, Router } from '@angular/router'; -import { AssetsService } from '../../services/assets.service'; -import { Env, StateService } from '../../services/state.service'; +import { AssetsService } from '@app/services/assets.service'; +import { Env, StateService } from '@app/services/state.service'; import { Observable, of, Subject, zip, BehaviorSubject, combineLatest } from 'rxjs'; import { debounceTime, distinctUntilChanged, switchMap, catchError, map, startWith, tap } from 'rxjs/operators'; -import { ElectrsApiService } from '../../services/electrs-api.service'; -import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; -import { ApiService } from '../../services/api.service'; -import { SearchResultsComponent } from './search-results/search-results.component'; -import { Network, findOtherNetworks, getRegex, getTargetUrl, needBaseModuleChange } from '../../shared/regex.utils'; +import { ElectrsApiService } from '@app/services/electrs-api.service'; +import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe'; +import { ApiService } from '@app/services/api.service'; +import { SearchResultsComponent } from '@components/search-form/search-results/search-results.component'; +import { Network, findOtherNetworks, getRegex, getTargetUrl, needBaseModuleChange } from '@app/shared/regex.utils'; @Component({ selector: 'app-search-form', diff --git a/frontend/src/app/components/search-form/search-results/search-results.component.ts b/frontend/src/app/components/search-form/search-results/search-results.component.ts index 04976028b..6a4efcd87 100644 --- a/frontend/src/app/components/search-form/search-results/search-results.component.ts +++ b/frontend/src/app/components/search-form/search-results/search-results.component.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'; -import { StateService } from '../../../services/state.service'; +import { StateService } from '@app/services/state.service'; @Component({ selector: 'app-search-results', diff --git a/frontend/src/app/components/server-health/server-health.component.ts b/frontend/src/app/components/server-health/server-health.component.ts index 37e23f12a..68dd7f349 100644 --- a/frontend/src/app/components/server-health/server-health.component.ts +++ b/frontend/src/app/components/server-health/server-health.component.ts @@ -1,8 +1,8 @@ import { Component, OnInit, ChangeDetectionStrategy, SecurityContext, ChangeDetectorRef } from '@angular/core'; -import { WebsocketService } from '../../services/websocket.service'; +import { WebsocketService } from '@app/services/websocket.service'; import { Observable, Subject, map } from 'rxjs'; -import { StateService } from '../../services/state.service'; -import { HealthCheckHost } from '../../interfaces/websocket.interface'; +import { StateService } from '@app/services/state.service'; +import { HealthCheckHost } from '@interfaces/websocket.interface'; import { DomSanitizer } from '@angular/platform-browser'; @Component({ diff --git a/frontend/src/app/components/server-health/server-status.component.ts b/frontend/src/app/components/server-health/server-status.component.ts index e1300a68d..7941d326d 100644 --- a/frontend/src/app/components/server-health/server-status.component.ts +++ b/frontend/src/app/components/server-health/server-status.component.ts @@ -1,8 +1,8 @@ import { Component, OnInit, ChangeDetectionStrategy, SecurityContext, OnDestroy, ChangeDetectorRef } from '@angular/core'; -import { WebsocketService } from '../../services/websocket.service'; +import { WebsocketService } from '@app/services/websocket.service'; import { Observable, Subject, Subscription, map, tap } from 'rxjs'; -import { StateService } from '../../services/state.service'; -import { HealthCheckHost } from '../../interfaces/websocket.interface'; +import { StateService } from '@app/services/state.service'; +import { HealthCheckHost } from '@interfaces/websocket.interface'; import { DomSanitizer } from '@angular/platform-browser'; @Component({ diff --git a/frontend/src/app/components/start/start.component.ts b/frontend/src/app/components/start/start.component.ts index 78c31cde5..31317cab5 100644 --- a/frontend/src/app/components/start/start.component.ts +++ b/frontend/src/app/components/start/start.component.ts @@ -1,8 +1,10 @@ import { Component, ElementRef, HostListener, OnInit, OnDestroy, ViewChild, Input, ChangeDetectorRef, ChangeDetectionStrategy, AfterViewChecked } from '@angular/core'; import { Subscription } from 'rxjs'; -import { MarkBlockState, StateService } from '../../services/state.service'; -import { specialBlocks } from '../../app.constants'; -import { BlockExtended } from '../../interfaces/node-api.interface'; +import { MarkBlockState, StateService } from '@app/services/state.service'; +import { specialBlocks } from '@app/app.constants'; +import { BlockExtended } from '@interfaces/node-api.interface'; +import { Router, ActivatedRoute } from '@angular/router'; +import { handleDemoRedirect } from '../../shared/common.utils'; @Component({ selector: 'app-start', @@ -61,6 +63,8 @@ export class StartComponent implements OnInit, AfterViewChecked, OnDestroy { constructor( public stateService: StateService, private cd: ChangeDetectorRef, + private router: Router, + private route: ActivatedRoute ) { this.isiOS = ['iPhone','iPod','iPad'].includes((navigator as any)?.userAgentData?.platform || navigator.platform); if (this.stateService.network === '') { @@ -69,6 +73,8 @@ export class StartComponent implements OnInit, AfterViewChecked, OnDestroy { } ngOnInit() { + handleDemoRedirect(this.route, this.router); + this.firstPageWidth = 40 + (this.blockWidth * this.dynamicBlocksAmount); this.blockCounterSubscription = this.stateService.blocks$.subscribe((blocks) => { this.blockCount = blocks.length; diff --git a/frontend/src/app/components/statistics/statistics.component.ts b/frontend/src/app/components/statistics/statistics.component.ts index 835b74227..9dda3c496 100644 --- a/frontend/src/app/components/statistics/statistics.component.ts +++ b/frontend/src/app/components/statistics/statistics.component.ts @@ -4,16 +4,16 @@ import { UntypedFormGroup, UntypedFormBuilder } from '@angular/forms'; import { of, merge} from 'rxjs'; import { switchMap } from 'rxjs/operators'; -import { OptimizedMempoolStats } from '../../interfaces/node-api.interface'; -import { WebsocketService } from '../../services/websocket.service'; -import { ApiService } from '../../services/api.service'; +import { OptimizedMempoolStats } from '@interfaces/node-api.interface'; +import { WebsocketService } from '@app/services/websocket.service'; +import { ApiService } from '@app/services/api.service'; -import { StateService } from '../../services/state.service'; -import { SeoService } from '../../services/seo.service'; -import { StorageService } from '../../services/storage.service'; -import { feeLevels, chartColors } from '../../app.constants'; -import { MempoolGraphComponent } from '../mempool-graph/mempool-graph.component'; -import { IncomingTransactionsGraphComponent } from '../incoming-transactions-graph/incoming-transactions-graph.component'; +import { StateService } from '@app/services/state.service'; +import { SeoService } from '@app/services/seo.service'; +import { StorageService } from '@app/services/storage.service'; +import { feeLevels, chartColors } from '@app/app.constants'; +import { MempoolGraphComponent } from '@components/mempool-graph/mempool-graph.component'; +import { IncomingTransactionsGraphComponent } from '@components/incoming-transactions-graph/incoming-transactions-graph.component'; @Component({ selector: 'app-statistics', diff --git a/frontend/src/app/components/status-view/status-view.component.ts b/frontend/src/app/components/status-view/status-view.component.ts index 46e2347c7..4a9a75fec 100644 --- a/frontend/src/app/components/status-view/status-view.component.ts +++ b/frontend/src/app/components/status-view/status-view.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { WebsocketService } from '../../services/websocket.service'; +import { WebsocketService } from '@app/services/websocket.service'; @Component({ selector: 'app-status-view', diff --git a/frontend/src/app/components/television/television.component.ts b/frontend/src/app/components/television/television.component.ts index 40f4b7192..1507f3d97 100644 --- a/frontend/src/app/components/television/television.component.ts +++ b/frontend/src/app/components/television/television.component.ts @@ -1,9 +1,9 @@ import { Component, OnInit, OnDestroy } from '@angular/core'; -import { WebsocketService } from '../../services/websocket.service'; -import { OptimizedMempoolStats } from '../../interfaces/node-api.interface'; -import { StateService } from '../../services/state.service'; -import { ApiService } from '../../services/api.service'; -import { SeoService } from '../../services/seo.service'; +import { WebsocketService } from '@app/services/websocket.service'; +import { OptimizedMempoolStats } from '@interfaces/node-api.interface'; +import { StateService } from '@app/services/state.service'; +import { ApiService } from '@app/services/api.service'; +import { SeoService } from '@app/services/seo.service'; import { ActivatedRoute } from '@angular/router'; import { map, scan, startWith, switchMap, tap } from 'rxjs/operators'; import { interval, merge, Observable, Subscription } from 'rxjs'; diff --git a/frontend/src/app/components/terms-of-service/terms-of-service.component.ts b/frontend/src/app/components/terms-of-service/terms-of-service.component.ts index 71a86c759..5eb90c0d9 100644 --- a/frontend/src/app/components/terms-of-service/terms-of-service.component.ts +++ b/frontend/src/app/components/terms-of-service/terms-of-service.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; -import { Env, StateService } from '../../services/state.service'; -import { SeoService } from '../../services/seo.service'; -import { OpenGraphService } from '../../services/opengraph.service'; +import { Env, StateService } from '@app/services/state.service'; +import { SeoService } from '@app/services/seo.service'; +import { OpenGraphService } from '@app/services/opengraph.service'; @Component({ selector: 'app-terms-of-service', diff --git a/frontend/src/app/components/terms-of-service/terms-of-service.module.ts b/frontend/src/app/components/terms-of-service/terms-of-service.module.ts index 2ab139d8b..8a758b8de 100644 --- a/frontend/src/app/components/terms-of-service/terms-of-service.module.ts +++ b/frontend/src/app/components/terms-of-service/terms-of-service.module.ts @@ -1,8 +1,8 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Routes, RouterModule } from '@angular/router'; -import { TermsOfServiceComponent } from './terms-of-service.component'; -import { SharedModule } from '../../shared/shared.module'; +import { TermsOfServiceComponent } from '@components/terms-of-service/terms-of-service.component'; +import { SharedModule } from '@app/shared/shared.module'; const routes: Routes = [ { diff --git a/frontend/src/app/components/test-transactions/test-transactions.component.ts b/frontend/src/app/components/test-transactions/test-transactions.component.ts index c9abeed62..22a0951ea 100644 --- a/frontend/src/app/components/test-transactions/test-transactions.component.ts +++ b/frontend/src/app/components/test-transactions/test-transactions.component.ts @@ -1,10 +1,10 @@ import { Component, OnInit } from '@angular/core'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; -import { ApiService } from '../../services/api.service'; -import { StateService } from '../../services/state.service'; -import { SeoService } from '../../services/seo.service'; -import { OpenGraphService } from '../../services/opengraph.service'; -import { TestMempoolAcceptResult } from '../../interfaces/node-api.interface'; +import { ApiService } from '@app/services/api.service'; +import { StateService } from '@app/services/state.service'; +import { SeoService } from '@app/services/seo.service'; +import { OpenGraphService } from '@app/services/opengraph.service'; +import { TestMempoolAcceptResult } from '@interfaces/node-api.interface'; @Component({ selector: 'app-test-transactions', @@ -74,7 +74,7 @@ export class TestTransactionsComponent implements OnInit { }, (error) => { if (typeof error.error === 'string') { - const matchText = error.error.match('"message":"(.*?)"'); + const matchText = error.error.replace(/\\/g, '').match('"message":"(.*?)"'); this.error = matchText && matchText[1] || error.error; } else if (error.message) { this.error = error.message; diff --git a/frontend/src/app/components/theme-selector/theme-selector.component.ts b/frontend/src/app/components/theme-selector/theme-selector.component.ts index be207910c..ca9c5788d 100644 --- a/frontend/src/app/components/theme-selector/theme-selector.component.ts +++ b/frontend/src/app/components/theme-selector/theme-selector.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { ThemeService } from '../../services/theme.service'; +import { ThemeService } from '@app/services/theme.service'; import { Subscription } from 'rxjs'; @Component({ diff --git a/frontend/src/app/components/time/time.component.ts b/frontend/src/app/components/time/time.component.ts index 6360bca4a..9ae893d74 100644 --- a/frontend/src/app/components/time/time.component.ts +++ b/frontend/src/app/components/time/time.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, Input, ChangeDetectorRef, OnChanges } from '@angular/core'; -import { StateService } from '../../services/state.service'; -import { TimeService } from '../../services/time.service'; +import { StateService } from '@app/services/state.service'; +import { TimeService } from '@app/services/time.service'; @Component({ selector: 'app-time', diff --git a/frontend/src/app/components/tracker/tracker.component.html b/frontend/src/app/components/tracker/tracker.component.html index 4e222479b..2d9bd4982 100644 --- a/frontend/src/app/components/tracker/tracker.component.html +++ b/frontend/src/app/components/tracker/tracker.component.html @@ -4,7 +4,7 @@ - - - @if (tx?.status?.confirmed) { - - - } @else { - - - } - - @if (tx?.status?.confirmed) { - - } - - - - - - - @if (!isLoadingTx && !tx?.status?.confirmed && isAcceleration && ((cpfpInfo && hasEffectiveFeeRate) || accelerationInfo)) { - - } @else { - - } - @if (tx?.status?.confirmed) { - - } - - - - @if (!isLoadingTx) { - - Timestamp - - ‎{{ tx.status.block_time * 1000 | date:'yyyy-MM-dd HH:mm:ss' }} -
- () -
- - - } @else { - - } -
- - - @if (!isLoadingTx) { - @if (transactionTime > 0) { - - Confirmed - - - } - } @else { - - } - - - - @if (isLoadingTx) { - - } @else if (transactionTime > 0) { - - First seen - - - } @else if (isLoadingFirstSeen) { - - First seen - - - } - - - - @if (network !== 'liquid' && network !== 'liquidtestnet') { - @if (!isLoadingTx) { - @if (featuresEnabled) { - - Features - - - - - } - } @else { - - } - } - - - - @if (network === '') { - @if (!isLoadingTx) { - @if (auditStatus) { - - Audit - - - @if (auditStatus.coinbase) { - Coinbase - } @else if (auditStatus.expected) { - Expected in Block - } @else if (auditStatus.seen) { - Seen in Mempool - } @else if (!auditStatus.conflict) { - Not seen in Mempool - } - @if (auditStatus.added) { - Added - } - @if (auditStatus.prioritized) { - Prioritized - } - @if (auditStatus.conflict) { - Conflict - } - - - - } - } @else { - - } - } - - - - @if (!isLoadingTx) { - @if (!replaced && !isCached) { - - ETA - - - @if (network === 'liquid' || network === 'liquidtestnet') { - - } @else { - - @if (eta.blocks >= 7) { - Not any time soon - } @else { - - } - @if (!tx?.acceleration && acceleratorAvailable && accelerateCtaType === 'button' && !showAccelerationSummary && notAcceleratedOnLoad) { - - } - - } - - - - - - - } - } @else { - - } - - - - @if (!isLoadingTx) { - @if (isAcceleration || filters.length) { - - - - - - @if (isAcceleration) { - Accelerated - } - - {{ filter.label }} - - - - } - } - - - - @if (!isLoadingTx) { - - Fee - {{ tx.fee | number }} sats - @if (accelerationInfo?.bidBoost ?? tx.feeDelta > 0) { - +{{ accelerationInfo?.bidBoost ?? tx.feeDelta | number }} sats - } - - - - } @else { - - } - - - - @if (!isLoadingTx) { - - Fee rate - - - @if (tx?.status?.confirmed && tx.fee && !hasEffectiveFeeRate && !accelerationInfo) { -   - - } - - - } @else { - - } - - - - @if (!isLoadingTx) { - @if ((cpfpInfo && hasEffectiveFeeRate) || accelerationInfo) { - - @if (isAcceleration) { - Accelerated fee rate - } @else { - Effective fee rate - } - -
- @if (accelerationInfo?.acceleratedFeeRate && (!tx.effectiveFeePerVsize || accelerationInfo.acceleratedFeeRate >= tx.effectiveFeePerVsize || tx.acceleration)) { - - } @else { - - } - - @if (tx?.status?.confirmed && !tx.acceleration && !accelerationInfo && tx.fee && tx.effectiveFeePerVsize) { - - } -
- @if (hasCpfp) { - - } - - - } - } @else { - - } -
- - - - - - - - - - - - @if (network === '') { - @if (!isLoadingTx) { - - Miner - @if (pool) { - - - - @if (pool.minerNames[1].length > 16) { - {{ pool.minerNames[1].slice(0, 15) }}… - } @else { - {{ pool.minerNames[1] }} - } - - - {{ pool.name }} - - - } @else { - - - - } - - } @else { - - } - } - - - - - - - \ No newline at end of file +
\ No newline at end of file diff --git a/frontend/src/app/components/transaction/transaction.component.scss b/frontend/src/app/components/transaction/transaction.component.scss index 42325a1b4..7125a6b46 100644 --- a/frontend/src/app/components/transaction/transaction.component.scss +++ b/frontend/src/app/components/transaction/transaction.component.scss @@ -18,6 +18,7 @@ line-height: 1; } } + .tx-link { display: flex; flex-direction: row; @@ -60,19 +61,6 @@ top: -1px; } -.miner-name { - margin-right: 4px; - vertical-align: top; -} - -.pool-logo { - width: 25px; - height: 25px; - position: relative; - top: -1px; - margin-right: 2px; -} - .badge.badge-accelerated { background-color: var(--tertiary); color: white; @@ -94,7 +82,7 @@ margin-bottom: 40px; } -.row{ +.row { flex-direction: column; @media (min-width: 850px) { flex-direction: row; @@ -337,4 +325,4 @@ .disabled { opacity: 0.5; pointer-events: none; -} \ No newline at end of file +} diff --git a/frontend/src/app/components/transaction/transaction.component.ts b/frontend/src/app/components/transaction/transaction.component.ts index 1306c432d..5f5ef4fa7 100644 --- a/frontend/src/app/components/transaction/transaction.component.ts +++ b/frontend/src/app/components/transaction/transaction.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit, AfterViewInit, OnDestroy, HostListener, ViewChild, ElementRef, Inject, ChangeDetectorRef } from '@angular/core'; -import { ElectrsApiService } from '../../services/electrs-api.service'; +import { ElectrsApiService } from '@app/services/electrs-api.service'; import { ActivatedRoute, ParamMap, Router } from '@angular/router'; import { switchMap, @@ -15,30 +15,30 @@ import { repeat, take } from 'rxjs/operators'; -import { Transaction } from '../../interfaces/electrs.interface'; +import { Transaction } from '@interfaces/electrs.interface'; import { of, merge, Subscription, Observable, Subject, from, throwError, combineLatest, BehaviorSubject } from 'rxjs'; -import { StateService } from '../../services/state.service'; -import { CacheService } from '../../services/cache.service'; -import { WebsocketService } from '../../services/websocket.service'; -import { AudioService } from '../../services/audio.service'; -import { ApiService } from '../../services/api.service'; -import { SeoService } from '../../services/seo.service'; -import { StorageService } from '../../services/storage.service'; -import { seoDescriptionNetwork } from '../../shared/common.utils'; -import { getTransactionFlags, getUnacceleratedFeeRate } from '../../shared/transaction.utils'; -import { Filter, TransactionFlags, toFilters } from '../../shared/filters.utils'; -import { BlockExtended, CpfpInfo, RbfTree, MempoolPosition, DifficultyAdjustment, Acceleration, AccelerationPosition } from '../../interfaces/node-api.interface'; -import { LiquidUnblinding } from './liquid-ublinding'; -import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; -import { PriceService } from '../../services/price.service'; -import { isFeatureActive } from '../../bitcoin.utils'; -import { ServicesApiServices } from '../../services/services-api.service'; -import { EnterpriseService } from '../../services/enterprise.service'; -import { ZONE_SERVICE } from '../../injection-tokens'; -import { MiningService, MiningStats } from '../../services/mining.service'; -import { ETA, EtaService } from '../../services/eta.service'; +import { StateService } from '@app/services/state.service'; +import { CacheService } from '@app/services/cache.service'; +import { WebsocketService } from '@app/services/websocket.service'; +import { AudioService } from '@app/services/audio.service'; +import { ApiService } from '@app/services/api.service'; +import { SeoService } from '@app/services/seo.service'; +import { StorageService } from '@app/services/storage.service'; +import { seoDescriptionNetwork } from '@app/shared/common.utils'; +import { getTransactionFlags, getUnacceleratedFeeRate } from '@app/shared/transaction.utils'; +import { Filter, TransactionFlags, toFilters } from '@app/shared/filters.utils'; +import { BlockExtended, CpfpInfo, RbfTree, MempoolPosition, DifficultyAdjustment, Acceleration, AccelerationPosition } from '@interfaces/node-api.interface'; +import { LiquidUnblinding } from '@components/transaction/liquid-ublinding'; +import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe'; +import { PriceService } from '@app/services/price.service'; +import { isFeatureActive } from '@app/bitcoin.utils'; +import { ServicesApiServices } from '@app/services/services-api.service'; +import { EnterpriseService } from '@app/services/enterprise.service'; +import { ZONE_SERVICE } from '@app/injection-tokens'; +import { MiningService, MiningStats } from '@app/services/mining.service'; +import { ETA, EtaService } from '@app/services/eta.service'; -interface Pool { +export interface Pool { id: number; name: string; slug: string; @@ -119,7 +119,6 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { txChanged$ = new BehaviorSubject(false); // triggered whenever this.tx changes (long term, we should refactor to make this.tx an observable itself) isAccelerated$ = new BehaviorSubject(false); // refactor this to make isAccelerated an observable itself ETA$: Observable; - standardETA$: Observable; isCached: boolean = false; now = Date.now(); da$: Observable; @@ -883,21 +882,6 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { this.miningStats = stats; this.isAccelerated$.next(this.isAcceleration); // hack to trigger recalculation of ETA without adding another source observable }); - if (!this.tx.status?.confirmed) { - this.standardETA$ = combineLatest([ - this.stateService.mempoolBlocks$.pipe(startWith(null)), - this.stateService.difficultyAdjustment$.pipe(startWith(null)), - ]).pipe( - map(([mempoolBlocks, da]) => { - return this.etaService.calculateUnacceleratedETA( - this.tx, - mempoolBlocks, - da, - this.cpfpInfo, - ); - }) - ) - } } this.isAccelerated$.next(this.isAcceleration); } diff --git a/frontend/src/app/components/transaction/transaction.module.ts b/frontend/src/app/components/transaction/transaction.module.ts index b98c33e2a..80de0cf40 100644 --- a/frontend/src/app/components/transaction/transaction.module.ts +++ b/frontend/src/app/components/transaction/transaction.module.ts @@ -1,12 +1,14 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Routes, RouterModule } from '@angular/router'; -import { TransactionComponent } from './transaction.component'; -import { SharedModule } from '../../shared/shared.module'; -import { TxBowtieModule } from '../tx-bowtie-graph/tx-bowtie.module'; -import { GraphsModule } from '../../graphs/graphs.module'; -import { AccelerateCheckout } from '../accelerate-checkout/accelerate-checkout.component'; -import { AccelerateFeeGraphComponent } from '../accelerate-checkout/accelerate-fee-graph.component'; +import { TransactionComponent } from '@components/transaction/transaction.component'; +import { TransactionDetailsComponent } from '@components/transaction/transaction-details/transaction-details.component'; +import { SharedModule } from '@app/shared/shared.module'; +import { TxBowtieModule } from '@components/tx-bowtie-graph/tx-bowtie.module'; +import { TransactionExtrasModule } from '@components/transaction/transaction-extras.module'; +import { GraphsModule } from '@app/graphs/graphs.module'; +import { AccelerateCheckout } from '@components/accelerate-checkout/accelerate-checkout.component'; +import { AccelerateFeeGraphComponent } from '@components/accelerate-checkout/accelerate-fee-graph.component'; const routes: Routes = [ { @@ -40,14 +42,17 @@ export class TransactionRoutingModule { } SharedModule, GraphsModule, TxBowtieModule, + TransactionExtrasModule, ], declarations: [ TransactionComponent, + TransactionDetailsComponent, AccelerateCheckout, AccelerateFeeGraphComponent, ], exports: [ TransactionComponent, + TransactionDetailsComponent, AccelerateCheckout, AccelerateFeeGraphComponent, ] diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.html b/frontend/src/app/components/transactions-list/transactions-list.component.html index 9b88678b4..5ad1c798c 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.html +++ b/frontend/src/app/components/transactions-list/transactions-list.component.html @@ -23,7 +23,7 @@ @@ -81,7 +81,8 @@
- + +
@@ -96,6 +97,15 @@ + + + + + + @@ -204,7 +214,7 @@ + + + +
@@ -236,7 +246,12 @@ - OP_RETURN {{ vout.scriptpubkey_asm | hex2ascii }} + OP_RETURN  + @if (vout.isRunestone) { + + } @else { + {{ vout.scriptpubkey_asm | hex2ascii }} + } {{ vout.scriptpubkey_type | scriptpubkeyType }} @@ -276,6 +291,15 @@
+ +
@@ -329,7 +353,7 @@ -
+ + + + + + + + + + + + + + + + + + + + +
Addresses + + + + +
+
+ +
+ +
+
Confirmed balance
Confirmed UTXOs{{ walletStats.utxos }}
Total received
+ +
+
+ +
+ + + + +
+
+

Balance History

+
+
+
+
+ +
+
+
+
+ + + + +
+
+
+ + + + + + + + + + + + +
+
+
+
+ +
+
+
+ +
+ + +
+ +
+ Error loading wallet data. +

+ ({{ error | httpErrorMsg }}) +
+
+ + + Error loading wallet data. + + +
+ + + +
+ + +
+ +
+
diff --git a/frontend/src/app/components/wallet/wallet.component.scss b/frontend/src/app/components/wallet/wallet.component.scss new file mode 100644 index 000000000..6723cffbc --- /dev/null +++ b/frontend/src/app/components/wallet/wallet.component.scss @@ -0,0 +1,117 @@ +.qr-wrapper { + background-color: #FFF; + padding: 10px; + padding-bottom: 5px; + display: inline-block; +} + +.treemap-col { + width: 45%; + height: 300px; +} + +.fiat { + display: block; + @media (min-width: 992px){ + display: inline-block; + margin-left: 10px; + } +} + +.table { + tr td { + &:last-child { + text-align: right; + @media (min-width: 576px) { + text-align: left; + } + } + } +} + +.address-list { + width: 100%; + max-width: 200px; +} + +h1 { + margin: 0px; + padding: 0px; + margin-right: 10px; + font-size: 1.9rem; + @media (min-width: 576px) { + font-size: 2rem; + float: left; + } + @media (min-width: 768px) { + font-size: 2.5rem; + } +} + +.title-address { + align-items: baseline; +} + +.address-link { + line-height: 56px; + margin-left: 0px; + top: -2px; + position: relative; + @media (min-width: 768px) { + line-height: 69px; + } +} + +.row{ + flex-direction: column; + @media (min-width: 576px) { + flex-direction: row; + } +} + +@media (max-width: 767.98px) { + .mobile-bottomcol { + margin-top: 15px; + } + .details-table td:first-child { + white-space: pre-wrap; + } +} + +.tx-link { + display: block; + height: 100%; + top: 9px; + position: relative; + @media (min-width: 576px) { + top: 11px; + } + @media (min-width: 768px) { + max-width: calc(100% - 180px); + top: 17px; + } +} + +.title-tx { + h2 { + line-height: 1; + margin-bottom: 10px; + } +} + +.liquid-address { + .address-table { + table-layout: fixed; + + tr td:first-child { + width: 170px; + } + tr td:last-child { + width: 80%; + } + } + + .qrcode-col { + flex-grow: 0.5; + } +} diff --git a/frontend/src/app/components/wallet/wallet.component.ts b/frontend/src/app/components/wallet/wallet.component.ts new file mode 100644 index 000000000..ce44250e9 --- /dev/null +++ b/frontend/src/app/components/wallet/wallet.component.ts @@ -0,0 +1,303 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { ActivatedRoute, ParamMap } from '@angular/router'; +import { switchMap, catchError, map, tap, shareReplay, startWith, scan } from 'rxjs/operators'; +import { Address, AddressTxSummary, ChainStats, Transaction } from '@interfaces/electrs.interface'; +import { WebsocketService } from '@app/services/websocket.service'; +import { StateService } from '@app/services/state.service'; +import { ApiService } from '@app/services/api.service'; +import { of, Observable, Subscription } from 'rxjs'; +import { SeoService } from '@app/services/seo.service'; +import { seoDescriptionNetwork } from '@app/shared/common.utils'; +import { WalletAddress } from '@interfaces/node-api.interface'; + +class WalletStats implements ChainStats { + addresses: string[]; + funded_txo_count: number; + funded_txo_sum: number; + spent_txo_count: number; + spent_txo_sum: number; + tx_count: number; + + constructor (stats: ChainStats[], addresses: string[]) { + Object.assign(this, stats.reduce((acc, stat) => { + acc.funded_txo_count += stat.funded_txo_count; + acc.funded_txo_sum += stat.funded_txo_sum; + acc.spent_txo_count += stat.spent_txo_count; + acc.spent_txo_sum += stat.spent_txo_sum; + return acc; + }, { + funded_txo_count: 0, + funded_txo_sum: 0, + spent_txo_count: 0, + spent_txo_sum: 0, + tx_count: 0, + }) + ); + this.addresses = addresses; + } + + public addTx(tx: Transaction): void { + for (const vin of tx.vin) { + if (this.addresses.includes(vin.prevout?.scriptpubkey_address)) { + this.spendTxo(vin.prevout.value); + } + } + for (const vout of tx.vout) { + if (this.addresses.includes(vout.scriptpubkey_address)) { + this.fundTxo(vout.value); + } + } + this.tx_count++; + } + + public removeTx(tx: Transaction): void { + for (const vin of tx.vin) { + if (this.addresses.includes(vin.prevout?.scriptpubkey_address)) { + this.unspendTxo(vin.prevout.value); + } + } + for (const vout of tx.vout) { + if (this.addresses.includes(vout.scriptpubkey_address)) { + this.unfundTxo(vout.value); + } + } + this.tx_count--; + } + + private fundTxo(value: number): void { + this.funded_txo_sum += value; + this.funded_txo_count++; + } + + private unfundTxo(value: number): void { + this.funded_txo_sum -= value; + this.funded_txo_count--; + } + + private spendTxo(value: number): void { + this.spent_txo_sum += value; + this.spent_txo_count++; + } + + private unspendTxo(value: number): void { + this.spent_txo_sum -= value; + this.spent_txo_count--; + } + + get balance(): number { + return this.funded_txo_sum - this.spent_txo_sum; + } + + get totalReceived(): number { + return this.funded_txo_sum; + } + + get utxos(): number { + return this.funded_txo_count - this.spent_txo_count; + } +} + +@Component({ + selector: 'app-wallet', + templateUrl: './wallet.component.html', + styleUrls: ['./wallet.component.scss'] +}) +export class WalletComponent implements OnInit, OnDestroy { + network = ''; + + addresses: Address[] = []; + addressStrings: string[] = []; + walletName: string; + isLoadingWallet = true; + wallet$: Observable>; + walletAddresses$: Observable>; + walletSummary$: Observable; + walletStats$: Observable; + error: any; + walletSubscription: Subscription; + + collapseAddresses: boolean = true; + + fullyLoaded = false; + txCount = 0; + received = 0; + sent = 0; + chainBalance = 0; + + constructor( + private route: ActivatedRoute, + private websocketService: WebsocketService, + private stateService: StateService, + private apiService: ApiService, + private seoService: SeoService, + ) { } + + ngOnInit(): void { + this.stateService.networkChanged$.subscribe((network) => this.network = network); + this.websocketService.want(['blocks']); + this.wallet$ = this.route.paramMap.pipe( + map((params: ParamMap) => params.get('wallet') as string), + tap((walletName: string) => { + this.walletName = walletName; + this.websocketService.startTrackingWallet(walletName); + this.seoService.setTitle($localize`:@@wallet.component.browser-title:Wallet: ${walletName}:INTERPOLATION:`); + this.seoService.setDescription($localize`:@@meta.description.bitcoin.wallet:See mempool transactions, confirmed transactions, balance, and more for ${this.stateService.network==='liquid'||this.stateService.network==='liquidtestnet'?'Liquid':'Bitcoin'}${seoDescriptionNetwork(this.stateService.network)} wallet ${walletName}:INTERPOLATION:.`); + }), + switchMap((walletName: string) => this.apiService.getWallet$(walletName).pipe( + catchError((err) => { + this.error = err; + this.seoService.logSoft404(); + console.log(err); + return of({}); + }) + )), + shareReplay(1), + ); + + this.walletAddresses$ = this.wallet$.pipe( + map(wallet => { + const walletInfo: Record = {}; + for (const address of Object.keys(wallet)) { + walletInfo[address] = { + address, + chain_stats: wallet[address].stats, + mempool_stats: { + funded_txo_count: 0, + funded_txo_sum: 0, + spent_txo_count: 0, spent_txo_sum: 0, tx_count: 0 + }, + }; + } + return walletInfo; + }), + switchMap(initial => this.stateService.walletTransactions$.pipe( + startWith(null), + scan((wallet, walletTransactions) => { + for (const tx of (walletTransactions || [])) { + const funded: Record = {}; + const spent: Record = {}; + const fundedCount: Record = {}; + const spentCount: Record = {}; + for (const vin of tx.vin) { + const address = vin.prevout?.scriptpubkey_address; + if (address && wallet[address]) { + spent[address] = (spent[address] ?? 0) + (vin.prevout?.value ?? 0); + spentCount[address] = (spentCount[address] ?? 0) + 1; + } + } + for (const vout of tx.vout) { + const address = vout.scriptpubkey_address; + if (address && wallet[address]) { + funded[address] = (funded[address] ?? 0) + (vout.value ?? 0); + fundedCount[address] = (fundedCount[address] ?? 0) + 1; + } + } + for (const address of Object.keys({ ...funded, ...spent })) { + // update address stats + wallet[address].chain_stats.tx_count++; + wallet[address].chain_stats.funded_txo_count += fundedCount[address] || 0; + wallet[address].chain_stats.spent_txo_count += spentCount[address] || 0; + wallet[address].chain_stats.funded_txo_sum += funded[address] || 0; + wallet[address].chain_stats.spent_txo_sum += spent[address] || 0; + } + } + return wallet; + }, initial) + )), + tap(() => { + this.isLoadingWallet = false; + }) + ); + + this.walletSubscription = this.walletAddresses$.subscribe(wallet => { + this.addressStrings = Object.keys(wallet); + this.addresses = Object.values(wallet); + }); + + this.walletSummary$ = this.wallet$.pipe( + switchMap(wallet => this.stateService.walletTransactions$.pipe( + startWith([]), + scan((summaries, newTransactions) => { + const newSummaries: AddressTxSummary[] = []; + for (const tx of newTransactions) { + const funded: Record = {}; + const spent: Record = {}; + const fundedCount: Record = {}; + const spentCount: Record = {}; + for (const vin of tx.vin) { + const address = vin.prevout?.scriptpubkey_address; + if (address && wallet[address]) { + spent[address] = (spent[address] ?? 0) + (vin.prevout?.value ?? 0); + spentCount[address] = (spentCount[address] ?? 0) + 1; + } + } + for (const vout of tx.vout) { + const address = vout.scriptpubkey_address; + if (address && wallet[address]) { + funded[address] = (funded[address] ?? 0) + (vout.value ?? 0); + fundedCount[address] = (fundedCount[address] ?? 0) + 1; + } + } + for (const address of Object.keys({ ...funded, ...spent })) { + // add tx to summary + const txSummary: AddressTxSummary = { + txid: tx.txid, + value: (funded[address] ?? 0) - (spent[address] ?? 0), + height: tx.status.block_height, + time: tx.status.block_time, + }; + wallet[address].transactions?.push(txSummary); + newSummaries.push(txSummary); + } + } + return this.deduplicateWalletTransactions([...summaries, ...newSummaries]); + }, this.deduplicateWalletTransactions(Object.values(wallet).flatMap(address => address.transactions))) + )) + ); + + this.walletStats$ = this.wallet$.pipe( + switchMap(wallet => { + const walletStats = new WalletStats(Object.values(wallet).map(w => w.stats), Object.keys(wallet)); + return this.stateService.walletTransactions$.pipe( + startWith([]), + scan((stats, newTransactions) => { + for (const tx of newTransactions) { + stats.addTx(tx); + } + return stats; + }, walletStats), + ); + }), + ); + } + + deduplicateWalletTransactions(walletTransactions: AddressTxSummary[]): AddressTxSummary[] { + const transactions = new Map(); + for (const tx of walletTransactions) { + if (transactions.has(tx.txid)) { + transactions.get(tx.txid).value += tx.value; + } else { + transactions.set(tx.txid, tx); + } + } + return Array.from(transactions.values()).sort((a, b) => { + if (a.height === b.height) { + return b.tx_position - a.tx_position; + } + return b.height - a.height; + }); + } + + normalizeAddress(address: string): string { + if (/^[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100}|04[a-fA-F0-9]{128}|(02|03)[a-fA-F0-9]{64}$/.test(address)) { + return address.toLowerCase(); + } else { + return address; + } + } + + ngOnDestroy(): void { + this.websocketService.stopTrackingWallet(); + this.walletSubscription.unsubscribe(); + } +} diff --git a/frontend/src/app/dashboard/dashboard.component.ts b/frontend/src/app/dashboard/dashboard.component.ts index 6bedaafb0..79b0118a9 100644 --- a/frontend/src/app/dashboard/dashboard.component.ts +++ b/frontend/src/app/dashboard/dashboard.component.ts @@ -1,14 +1,14 @@ import { AfterViewInit, ChangeDetectionStrategy, Component, HostListener, Inject, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core'; import { combineLatest, EMPTY, fromEvent, interval, merge, Observable, of, Subject, Subscription, timer } from 'rxjs'; import { catchError, delayWhen, distinctUntilChanged, filter, map, scan, share, shareReplay, startWith, switchMap, takeUntil, tap, throttleTime } from 'rxjs/operators'; -import { AuditStatus, BlockExtended, CurrentPegs, FederationAddress, FederationUtxo, OptimizedMempoolStats, PegsVolume, RecentPeg, TransactionStripped } from '../interfaces/node-api.interface'; -import { MempoolInfo, ReplacementInfo } from '../interfaces/websocket.interface'; -import { ApiService } from '../services/api.service'; -import { StateService } from '../services/state.service'; -import { WebsocketService } from '../services/websocket.service'; -import { SeoService } from '../services/seo.service'; -import { ActiveFilter, FilterMode, GradientMode, toFlags } from '../shared/filters.utils'; -import { detectWebGL } from '../shared/graphs.utils'; +import { AuditStatus, BlockExtended, CurrentPegs, FederationAddress, FederationUtxo, OptimizedMempoolStats, PegsVolume, RecentPeg, TransactionStripped } from '@interfaces/node-api.interface'; +import { MempoolInfo, ReplacementInfo } from '@interfaces/websocket.interface'; +import { ApiService } from '@app/services/api.service'; +import { StateService } from '@app/services/state.service'; +import { WebsocketService } from '@app/services/websocket.service'; +import { SeoService } from '@app/services/seo.service'; +import { ActiveFilter, FilterMode, GradientMode, toFlags } from '@app/shared/filters.utils'; +import { detectWebGL } from '@app/shared/graphs.utils'; interface MempoolBlocksData { blocks: number; diff --git a/frontend/src/app/data-cy.directive.ts b/frontend/src/app/data-cy.directive.ts index 1e8e9aec7..7bee94346 100644 --- a/frontend/src/app/data-cy.directive.ts +++ b/frontend/src/app/data-cy.directive.ts @@ -1,5 +1,5 @@ import { Directive, ElementRef, Renderer2 } from '@angular/core'; -import { environment } from '../environments/environment'; +import { environment } from '@environments/environment'; @Directive({ selector: '[data-cy]' diff --git a/frontend/src/app/docs/api-docs/api-docs-data.ts b/frontend/src/app/docs/api-docs/api-docs-data.ts index 12bb96166..cad4b47bf 100644 --- a/frontend/src/app/docs/api-docs/api-docs-data.ts +++ b/frontend/src/app/docs/api-docs/api-docs-data.ts @@ -9163,11 +9163,13 @@ export const restApiDocsData = [ Filters can be applied:
  • status: all, requested, accelerating, mined, completed, failed
  • timeframe: 24h, 3d, 1w, 1m, 3m, 6m, 1y, 2y, 3y, 4y, all
  • -
  • poolUniqueId: any id from https://github.com/mempool/mining-pools/blob/master/pools-v2.json. Note: This will return all acceleration requests accepted by the pool but the the listed transactions may have been mined by another pool. +
  • minedByPoolUniqueId: any id from pools-v2.json
  • blockHash: a block hash
  • blockHeight: a block height
  • page: the requested page number if using pagination (min: 1)
  • pageLength: the page lenght if using pagination (min: 1, max: 50) +
  • from: unix timestamp (overrides timeframe) +
  • to: unix timestamp (overrides timeframe)

` }, urlString: "/v1/services/accelerator/accelerations/history", @@ -9187,21 +9189,22 @@ export const restApiDocsData = [ headers: '', response: `[ { - "txid": "d7e1796d8eb4a09d4e6c174e36cfd852f1e6e6c9f7df4496339933cd32cbdd1d", - "status": "completed", - "added": 1707421053, - "lastUpdated": 1719134667, - "effectiveFee": 146, - "effectiveVsize": 141, - "feeDelta": 14000, - "blockHash": "00000000000000000000482f0746d62141694b9210a813b97eb8445780a32003", - "blockHeight": 829559, - "bidBoost": 3239, - "boostVersion": "v1", + "txid": "f829900985aad885c13fb90555d27514b05a338202c7ef5d694f4813ad474487", + "status": "completed_provisional", + "added": 1728111527, + "lastUpdated": 1728112113, + "effectiveFee": 1385, + "effectiveVsize": 276, + "feeDelta": 3000, + "blockHash": "00000000000000000000cde89e34036ece454ca2d07ddd7f71ab46307ca87423", + "blockHeight": 864248, + "bidBoost": 65, + "boostVersion": "v2", "pools": [ - 111 + 111, + 115, ], - "minedByPoolUniqueId": 111 + "minedByPoolUniqueId": 115 } ]`, }, diff --git a/frontend/src/app/docs/api-docs/api-docs-nav.component.ts b/frontend/src/app/docs/api-docs/api-docs-nav.component.ts index 1abc506e5..11e39b518 100644 --- a/frontend/src/app/docs/api-docs/api-docs-nav.component.ts +++ b/frontend/src/app/docs/api-docs/api-docs-nav.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; -import { Env, StateService } from '../../services/state.service'; -import { restApiDocsData } from './api-docs-data'; -import { faqData } from './api-docs-data'; +import { Env, StateService } from '@app/services/state.service'; +import { restApiDocsData } from '@app/docs/api-docs/api-docs-data'; +import { faqData } from '@app/docs/api-docs/api-docs-data'; @Component({ selector: 'app-api-docs-nav', diff --git a/frontend/src/app/docs/api-docs/api-docs.component.ts b/frontend/src/app/docs/api-docs/api-docs.component.ts index efdc80d94..0298fc9f3 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.ts +++ b/frontend/src/app/docs/api-docs/api-docs.component.ts @@ -1,10 +1,10 @@ import { Component, OnInit, Input, QueryList, AfterViewInit, ViewChildren } from '@angular/core'; -import { Env, StateService } from '../../services/state.service'; +import { Env, StateService } from '@app/services/state.service'; import { Observable, merge, of, Subject, Subscription } from 'rxjs'; import { tap, takeUntil } from 'rxjs/operators'; import { ActivatedRoute } from "@angular/router"; -import { faqData, restApiDocsData, wsApiDocsData } from './api-docs-data'; -import { FaqTemplateDirective } from '../faq-template/faq-template.component'; +import { faqData, restApiDocsData, wsApiDocsData } from '@app/docs/api-docs/api-docs-data'; +import { FaqTemplateDirective } from '@app/docs/faq-template/faq-template.component'; @Component({ selector: 'app-api-docs', diff --git a/frontend/src/app/docs/code-template/code-template.component.ts b/frontend/src/app/docs/code-template/code-template.component.ts index b31def01c..f112e23f3 100644 --- a/frontend/src/app/docs/code-template/code-template.component.ts +++ b/frontend/src/app/docs/code-template/code-template.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnInit } from '@angular/core'; -import { Env, StateService } from '../../services/state.service'; +import { Env, StateService } from '@app/services/state.service'; @Component({ selector: 'app-code-template', diff --git a/frontend/src/app/docs/docs.module.ts b/frontend/src/app/docs/docs.module.ts index 70f22a9a6..7f94135aa 100644 --- a/frontend/src/app/docs/docs.module.ts +++ b/frontend/src/app/docs/docs.module.ts @@ -1,12 +1,12 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { SharedModule } from '../shared/shared.module'; -import { ApiDocsComponent } from './/api-docs/api-docs.component'; -import { DocsComponent } from './docs/docs.component'; -import { ApiDocsNavComponent } from './api-docs/api-docs-nav.component'; -import { CodeTemplateComponent } from './code-template/code-template.component'; -import { DocsRoutingModule } from './docs.routing.module'; -import { FaqTemplateDirective } from './faq-template/faq-template.component'; +import { SharedModule } from '@app/shared/shared.module'; +import { ApiDocsComponent } from '@app/docs/api-docs/api-docs.component'; +import { DocsComponent } from '@app/docs/docs/docs.component'; +import { ApiDocsNavComponent } from '@app/docs/api-docs/api-docs-nav.component'; +import { CodeTemplateComponent } from '@app/docs/code-template/code-template.component'; +import { DocsRoutingModule } from '@app/docs/docs.routing.module'; +import { FaqTemplateDirective } from '@app/docs/faq-template/faq-template.component'; @NgModule({ declarations: [ ApiDocsComponent, diff --git a/frontend/src/app/docs/docs.routing.module.ts b/frontend/src/app/docs/docs.routing.module.ts index 3c0eb961d..7185a7181 100644 --- a/frontend/src/app/docs/docs.routing.module.ts +++ b/frontend/src/app/docs/docs.routing.module.ts @@ -1,6 +1,6 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { DocsComponent } from './docs/docs.component'; +import { DocsComponent } from '@app/docs/docs/docs.component'; const browserWindow = window || {}; // @ts-ignore diff --git a/frontend/src/app/docs/docs/docs.component.ts b/frontend/src/app/docs/docs/docs.component.ts index 35080a19f..6d3ff4723 100644 --- a/frontend/src/app/docs/docs/docs.component.ts +++ b/frontend/src/app/docs/docs/docs.component.ts @@ -1,9 +1,9 @@ import { Component, OnInit, HostBinding } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { Env, StateService } from '../../services/state.service'; -import { WebsocketService } from '../../services/websocket.service'; -import { SeoService } from '../../services/seo.service'; -import { OpenGraphService } from '../../services/opengraph.service'; +import { Env, StateService } from '@app/services/state.service'; +import { WebsocketService } from '@app/services/websocket.service'; +import { SeoService } from '@app/services/seo.service'; +import { OpenGraphService } from '@app/services/opengraph.service'; @Component({ selector: 'app-docs', diff --git a/frontend/src/app/fiat/fiat.component.ts b/frontend/src/app/fiat/fiat.component.ts index 93752039b..95f019723 100644 --- a/frontend/src/app/fiat/fiat.component.ts +++ b/frontend/src/app/fiat/fiat.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit, ChangeDetectionStrategy, Input, ChangeDetectorRef, OnDestroy } from '@angular/core'; import { Observable, Subscription } from 'rxjs'; -import { Price } from '../services/price.service'; -import { StateService } from '../services/state.service'; +import { Price } from '@app/services/price.service'; +import { StateService } from '@app/services/state.service'; @Component({ selector: 'app-fiat', diff --git a/frontend/src/app/graphs/graphs.module.ts b/frontend/src/app/graphs/graphs.module.ts index ee51069c5..4e6b00637 100644 --- a/frontend/src/app/graphs/graphs.module.ts +++ b/frontend/src/app/graphs/graphs.module.ts @@ -1,43 +1,45 @@ import { NgModule } from '@angular/core'; import { NgxEchartsModule } from 'ngx-echarts'; -import { GraphsRoutingModule } from './graphs.routing.module'; -import { SharedModule } from '../shared/shared.module'; +import { GraphsRoutingModule } from '@app/graphs/graphs.routing.module'; +import { SharedModule } from '@app/shared/shared.module'; -import { AccelerationFeesGraphComponent } from '../components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component'; -import { BlockFeesGraphComponent } from '../components/block-fees-graph/block-fees-graph.component'; -import { BlockFeesSubsidyGraphComponent } from '../components/block-fees-subsidy-graph/block-fees-subsidy-graph.component'; -import { BlockRewardsGraphComponent } from '../components/block-rewards-graph/block-rewards-graph.component'; -import { BlockFeeRatesGraphComponent } from '../components/block-fee-rates-graph/block-fee-rates-graph.component'; -import { BlockSizesWeightsGraphComponent } from '../components/block-sizes-weights-graph/block-sizes-weights-graph.component'; -import { FeeDistributionGraphComponent } from '../components/fee-distribution-graph/fee-distribution-graph.component'; -import { IncomingTransactionsGraphComponent } from '../components/incoming-transactions-graph/incoming-transactions-graph.component'; -import { MempoolGraphComponent } from '../components/mempool-graph/mempool-graph.component'; -import { LbtcPegsGraphComponent } from '../components/lbtc-pegs-graph/lbtc-pegs-graph.component'; -import { ReservesSupplyStatsComponent } from '../components/liquid-reserves-audit/reserves-supply-stats/reserves-supply-stats.component'; -import { ExpiredUtxosStatsComponent } from '../components/liquid-reserves-audit/expired-utxos-stats/expired-utxos-stats.component'; -import { ReservesRatioStatsComponent } from '../components/liquid-reserves-audit/reserves-ratio-stats/reserves-ratio-stats.component'; -import { ReservesRatioComponent } from '../components/liquid-reserves-audit/reserves-ratio/reserves-ratio.component'; -import { RecentPegsStatsComponent } from '../components/liquid-reserves-audit/recent-pegs-stats/recent-pegs-stats.component'; -import { RecentPegsListComponent } from '../components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component'; -import { FederationAddressesStatsComponent } from '../components/liquid-reserves-audit/federation-addresses-stats/federation-addresses-stats.component'; -import { FederationAddressesListComponent } from '../components/liquid-reserves-audit/federation-addresses-list/federation-addresses-list.component'; -import { GraphsComponent } from '../components/graphs/graphs.component'; -import { StatisticsComponent } from '../components/statistics/statistics.component'; -import { MempoolBlockComponent } from '../components/mempool-block/mempool-block.component'; -import { PoolRankingComponent } from '../components/pool-ranking/pool-ranking.component'; -import { PoolComponent } from '../components/pool/pool.component'; -import { TelevisionComponent } from '../components/television/television.component'; -import { DashboardComponent } from '../dashboard/dashboard.component'; -import { CustomDashboardComponent } from '../components/custom-dashboard/custom-dashboard.component'; -import { MiningDashboardComponent } from '../components/mining-dashboard/mining-dashboard.component'; -import { AcceleratorDashboardComponent } from '../components/acceleration/accelerator-dashboard/accelerator-dashboard.component'; -import { HashrateChartComponent } from '../components/hashrate-chart/hashrate-chart.component'; -import { HashrateChartPoolsComponent } from '../components/hashrates-chart-pools/hashrate-chart-pools.component'; -import { BlockHealthGraphComponent } from '../components/block-health-graph/block-health-graph.component'; -import { AddressComponent } from '../components/address/address.component'; -import { AddressGraphComponent } from '../components/address-graph/address-graph.component'; -import { UtxoGraphComponent } from '../components/utxo-graph/utxo-graph.component'; -import { ActiveAccelerationBox } from '../components/acceleration/active-acceleration-box/active-acceleration-box.component'; +import { AccelerationFeesGraphComponent } from '@components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component'; +import { BlockFeesGraphComponent } from '@components/block-fees-graph/block-fees-graph.component'; +import { BlockFeesSubsidyGraphComponent } from '@components/block-fees-subsidy-graph/block-fees-subsidy-graph.component'; +import { BlockRewardsGraphComponent } from '@components/block-rewards-graph/block-rewards-graph.component'; +import { BlockFeeRatesGraphComponent } from '@components/block-fee-rates-graph/block-fee-rates-graph.component'; +import { BlockSizesWeightsGraphComponent } from '@components/block-sizes-weights-graph/block-sizes-weights-graph.component'; +import { FeeDistributionGraphComponent } from '@components/fee-distribution-graph/fee-distribution-graph.component'; +import { IncomingTransactionsGraphComponent } from '@components/incoming-transactions-graph/incoming-transactions-graph.component'; +import { MempoolGraphComponent } from '@components/mempool-graph/mempool-graph.component'; +import { LbtcPegsGraphComponent } from '@components/lbtc-pegs-graph/lbtc-pegs-graph.component'; +import { ReservesSupplyStatsComponent } from '@components/liquid-reserves-audit/reserves-supply-stats/reserves-supply-stats.component'; +import { ExpiredUtxosStatsComponent } from '@components/liquid-reserves-audit/expired-utxos-stats/expired-utxos-stats.component'; +import { ReservesRatioStatsComponent } from '@components/liquid-reserves-audit/reserves-ratio-stats/reserves-ratio-stats.component'; +import { ReservesRatioComponent } from '@components/liquid-reserves-audit/reserves-ratio/reserves-ratio.component'; +import { RecentPegsStatsComponent } from '@components/liquid-reserves-audit/recent-pegs-stats/recent-pegs-stats.component'; +import { RecentPegsListComponent } from '@components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component'; +import { FederationAddressesStatsComponent } from '@components/liquid-reserves-audit/federation-addresses-stats/federation-addresses-stats.component'; +import { FederationAddressesListComponent } from '@components/liquid-reserves-audit/federation-addresses-list/federation-addresses-list.component'; +import { GraphsComponent } from '@components/graphs/graphs.component'; +import { StatisticsComponent } from '@components/statistics/statistics.component'; +import { MempoolBlockComponent } from '@components/mempool-block/mempool-block.component'; +import { PoolRankingComponent } from '@components/pool-ranking/pool-ranking.component'; +import { PoolComponent } from '@components/pool/pool.component'; +import { TelevisionComponent } from '@components/television/television.component'; +import { DashboardComponent } from '@app/dashboard/dashboard.component'; +import { CustomDashboardComponent } from '@components/custom-dashboard/custom-dashboard.component'; +import { MiningDashboardComponent } from '@components/mining-dashboard/mining-dashboard.component'; +import { AcceleratorDashboardComponent } from '@components/acceleration/accelerator-dashboard/accelerator-dashboard.component'; +import { HashrateChartComponent } from '@components/hashrate-chart/hashrate-chart.component'; +import { HashrateChartPoolsComponent } from '@components/hashrates-chart-pools/hashrate-chart-pools.component'; +import { BlockHealthGraphComponent } from '@components/block-health-graph/block-health-graph.component'; +import { AddressComponent } from '@components/address/address.component'; +import { WalletComponent } from '@components/wallet/wallet.component'; +import { AddressGraphComponent } from '@components/address-graph/address-graph.component'; +import { UtxoGraphComponent } from '@components/utxo-graph/utxo-graph.component'; +import { ActiveAccelerationBox } from '@components/acceleration/active-acceleration-box/active-acceleration-box.component'; +import { AddressesTreemap } from '@components/addresses-treemap/addresses-treemap.component'; import { CommonModule } from '@angular/common'; @NgModule({ @@ -46,6 +48,7 @@ import { CommonModule } from '@angular/common'; CustomDashboardComponent, MempoolBlockComponent, AddressComponent, + WalletComponent, MiningDashboardComponent, AcceleratorDashboardComponent, @@ -79,13 +82,14 @@ import { CommonModule } from '@angular/common'; AddressGraphComponent, UtxoGraphComponent, ActiveAccelerationBox, + AddressesTreemap, ], imports: [ CommonModule, SharedModule, GraphsRoutingModule, NgxEchartsModule.forRoot({ - echarts: () => import('./echarts').then(m => m.echarts), + echarts: () => import('@app/graphs/echarts').then(m => m.echarts), }) ], exports: [ diff --git a/frontend/src/app/graphs/graphs.routing.module.ts b/frontend/src/app/graphs/graphs.routing.module.ts index 40bf64144..886d55072 100644 --- a/frontend/src/app/graphs/graphs.routing.module.ts +++ b/frontend/src/app/graphs/graphs.routing.module.ts @@ -1,27 +1,28 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { BlockHealthGraphComponent } from '../components/block-health-graph/block-health-graph.component'; -import { BlockFeeRatesGraphComponent } from '../components/block-fee-rates-graph/block-fee-rates-graph.component'; -import { BlockFeesGraphComponent } from '../components/block-fees-graph/block-fees-graph.component'; -import { BlockFeesSubsidyGraphComponent } from '../components/block-fees-subsidy-graph/block-fees-subsidy-graph.component'; -import { BlockRewardsGraphComponent } from '../components/block-rewards-graph/block-rewards-graph.component'; -import { BlockSizesWeightsGraphComponent } from '../components/block-sizes-weights-graph/block-sizes-weights-graph.component'; -import { GraphsComponent } from '../components/graphs/graphs.component'; -import { HashrateChartComponent } from '../components/hashrate-chart/hashrate-chart.component'; -import { HashrateChartPoolsComponent } from '../components/hashrates-chart-pools/hashrate-chart-pools.component'; -import { MempoolBlockComponent } from '../components/mempool-block/mempool-block.component'; -import { MiningDashboardComponent } from '../components/mining-dashboard/mining-dashboard.component'; -import { AcceleratorDashboardComponent } from '../components/acceleration/accelerator-dashboard/accelerator-dashboard.component'; -import { PoolRankingComponent } from '../components/pool-ranking/pool-ranking.component'; -import { PoolComponent } from '../components/pool/pool.component'; -import { StartComponent } from '../components/start/start.component'; -import { StatisticsComponent } from '../components/statistics/statistics.component'; -import { TelevisionComponent } from '../components/television/television.component'; -import { DashboardComponent } from '../dashboard/dashboard.component'; -import { CustomDashboardComponent } from '../components/custom-dashboard/custom-dashboard.component'; -import { AccelerationFeesGraphComponent } from '../components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component'; -import { AccelerationsListComponent } from '../components/acceleration/accelerations-list/accelerations-list.component'; -import { AddressComponent } from '../components/address/address.component'; +import { BlockHealthGraphComponent } from '@components/block-health-graph/block-health-graph.component'; +import { BlockFeeRatesGraphComponent } from '@components/block-fee-rates-graph/block-fee-rates-graph.component'; +import { BlockFeesGraphComponent } from '@components/block-fees-graph/block-fees-graph.component'; +import { BlockFeesSubsidyGraphComponent } from '@components/block-fees-subsidy-graph/block-fees-subsidy-graph.component'; +import { BlockRewardsGraphComponent } from '@components/block-rewards-graph/block-rewards-graph.component'; +import { BlockSizesWeightsGraphComponent } from '@components/block-sizes-weights-graph/block-sizes-weights-graph.component'; +import { GraphsComponent } from '@components/graphs/graphs.component'; +import { HashrateChartComponent } from '@components/hashrate-chart/hashrate-chart.component'; +import { HashrateChartPoolsComponent } from '@components/hashrates-chart-pools/hashrate-chart-pools.component'; +import { MempoolBlockComponent } from '@components/mempool-block/mempool-block.component'; +import { MiningDashboardComponent } from '@components/mining-dashboard/mining-dashboard.component'; +import { AcceleratorDashboardComponent } from '@components/acceleration/accelerator-dashboard/accelerator-dashboard.component'; +import { PoolRankingComponent } from '@components/pool-ranking/pool-ranking.component'; +import { PoolComponent } from '@components/pool/pool.component'; +import { StartComponent } from '@components/start/start.component'; +import { StatisticsComponent } from '@components/statistics/statistics.component'; +import { TelevisionComponent } from '@components/television/television.component'; +import { DashboardComponent } from '@app/dashboard/dashboard.component'; +import { CustomDashboardComponent } from '@components/custom-dashboard/custom-dashboard.component'; +import { AccelerationFeesGraphComponent } from '@components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component'; +import { AccelerationsListComponent } from '@components/acceleration/accelerations-list/accelerations-list.component'; +import { AddressComponent } from '@components/address/address.component'; +import { WalletComponent } from '@components/wallet/wallet.component'; const browserWindow = window || {}; // @ts-ignore @@ -88,6 +89,15 @@ const routes: Routes = [ networkSpecific: true, } }, + { + path: 'wallet/:wallet', + children: [], + component: WalletComponent, + data: { + ogImage: true, + networkSpecific: true, + } + }, { path: 'graphs', data: { networks: ['bitcoin', 'liquid'] }, @@ -146,7 +156,7 @@ const routes: Routes = [ { path: 'lightning', data: { preload: true, networks: ['bitcoin'] }, - loadChildren: () => import ('./lightning-graphs.module').then(m => m.LightningGraphsModule), + loadChildren: () => import ('@app/graphs/lightning-graphs.module').then(m => m.LightningGraphsModule), }, { path: '', diff --git a/frontend/src/app/graphs/lightning-graphs.module.ts b/frontend/src/app/graphs/lightning-graphs.module.ts index ac123be33..869ce94af 100644 --- a/frontend/src/app/graphs/lightning-graphs.module.ts +++ b/frontend/src/app/graphs/lightning-graphs.module.ts @@ -1,13 +1,13 @@ import { NgModule } from '@angular/core'; -import { SharedModule } from '../shared/shared.module'; +import { SharedModule } from '@app/shared/shared.module'; import { CommonModule } from '@angular/common'; import { RouterModule, Routes } from '@angular/router'; -import { NodesNetworksChartComponent } from '../lightning/nodes-networks-chart/nodes-networks-chart.component'; -import { LightningStatisticsChartComponent } from '../lightning/statistics-chart/lightning-statistics-chart.component'; -import { NodesPerISPChartComponent } from '../lightning/nodes-per-isp-chart/nodes-per-isp-chart.component'; -import { NodesPerCountryChartComponent } from '../lightning/nodes-per-country-chart/nodes-per-country-chart.component'; -import { NodesMap } from '../lightning/nodes-map/nodes-map.component'; -import { NodesChannelsMap } from '../lightning/nodes-channels-map/nodes-channels-map.component'; +import { NodesNetworksChartComponent } from '@app/lightning/nodes-networks-chart/nodes-networks-chart.component'; +import { LightningStatisticsChartComponent } from '@app/lightning/statistics-chart/lightning-statistics-chart.component'; +import { NodesPerISPChartComponent } from '@app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component'; +import { NodesPerCountryChartComponent } from '@app/lightning/nodes-per-country-chart/nodes-per-country-chart.component'; +import { NodesMap } from '@app/lightning/nodes-map/nodes-map.component'; +import { NodesChannelsMap } from '@app/lightning/nodes-channels-map/nodes-channels-map.component'; const routes: Routes = [ { diff --git a/frontend/src/app/interfaces/electrs.interface.ts b/frontend/src/app/interfaces/electrs.interface.ts index 5bc5bfc1d..5a707d889 100644 --- a/frontend/src/app/interfaces/electrs.interface.ts +++ b/frontend/src/app/interfaces/electrs.interface.ts @@ -1,5 +1,5 @@ -import { Price } from '../services/price.service'; -import { IChannel } from './node-api.interface'; +import { Price } from '@app/services/price.service'; +import { IChannel } from '@interfaces/node-api.interface'; export interface Transaction { txid: string; @@ -74,6 +74,8 @@ export interface Vin { issuance?: Issuance; // Custom lazy?: boolean; + // Ord + isInscription?: boolean; } interface Issuance { @@ -98,6 +100,8 @@ export interface Vout { valuecommitment?: number; asset?: string; pegout?: Pegout; + // Ord + isRunestone?: boolean; } interface Pegout { @@ -160,6 +164,7 @@ export interface AddressTxSummary { height: number; time: number; price?: number; + tx_position?: number; } export interface ChainStats { @@ -239,4 +244,4 @@ export interface Utxo { vout: number; value: number; status: Status; -} \ No newline at end of file +} diff --git a/frontend/src/app/interfaces/node-api.interface.ts b/frontend/src/app/interfaces/node-api.interface.ts index 4c7796590..0091262e1 100644 --- a/frontend/src/app/interfaces/node-api.interface.ts +++ b/frontend/src/app/interfaces/node-api.interface.ts @@ -1,4 +1,4 @@ -import { Block, Transaction } from "./electrs.interface"; +import { AddressTxSummary, Block, ChainStats, Transaction } from "./electrs.interface"; export interface OptimizedMempoolStats { added: number; @@ -452,4 +452,28 @@ export interface TestMempoolAcceptResult { "effective-includes": string[], }, ['reject-reason']?: string, -} \ No newline at end of file +} + +export interface SubmitPackageResult { + package_msg: string; + "tx-results": { [wtxid: string]: TxResult }; + "replaced-transactions"?: string[]; +} + +export interface TxResult { + txid: string; + "other-wtxid"?: string; + vsize?: number; + fees?: { + base: number; + "effective-feerate"?: number; + "effective-includes"?: string[]; + }; + error?: string; +} +export interface WalletAddress { + address: string; + active: boolean; + stats: ChainStats; + transactions: AddressTxSummary[]; +} diff --git a/frontend/src/app/interfaces/websocket.interface.ts b/frontend/src/app/interfaces/websocket.interface.ts index 7552224f5..89c8e3884 100644 --- a/frontend/src/app/interfaces/websocket.interface.ts +++ b/frontend/src/app/interfaces/websocket.interface.ts @@ -1,7 +1,7 @@ import { SafeResourceUrl } from '@angular/platform-browser'; -import { ILoadingIndicators } from '../services/state.service'; -import { Transaction } from './electrs.interface'; -import { Acceleration, BlockExtended, DifficultyAdjustment, RbfTree, TransactionStripped } from './node-api.interface'; +import { ILoadingIndicators } from '@app/services/state.service'; +import { Transaction } from '@interfaces/electrs.interface'; +import { Acceleration, BlockExtended, DifficultyAdjustment, RbfTree, TransactionStripped } from '@interfaces/node-api.interface'; export interface WebsocketResponse { backend?: 'esplora' | 'electrum' | 'none'; @@ -36,6 +36,7 @@ export interface WebsocketResponse { 'track-rbf'?: string; 'track-rbf-summary'?: boolean; 'track-accelerations'?: boolean; + 'track-wallet'?: string; 'watch-mempool'?: boolean; 'refresh-blocks'?: boolean; } @@ -143,4 +144,4 @@ export interface HealthCheckHost { link?: string; statusPage?: SafeResourceUrl; flag?: string; -} \ No newline at end of file +} diff --git a/frontend/src/app/lightning/channel/channel-box/channel-box.component.spec.ts b/frontend/src/app/lightning/channel/channel-box/channel-box.component.spec.ts index ae9463a6c..22dd36f75 100644 --- a/frontend/src/app/lightning/channel/channel-box/channel-box.component.spec.ts +++ b/frontend/src/app/lightning/channel/channel-box/channel-box.component.spec.ts @@ -1,6 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ChannelBoxComponent } from './channel-box.component'; +import { ChannelBoxComponent } from '@components/channel-box.component'; describe('ChannelBoxComponent', () => { let component: ChannelBoxComponent; diff --git a/frontend/src/app/lightning/channel/channel-close-box/channel-close-box.component.spec.ts b/frontend/src/app/lightning/channel/channel-close-box/channel-close-box.component.spec.ts index eea4ee99c..9a63d7f59 100644 --- a/frontend/src/app/lightning/channel/channel-close-box/channel-close-box.component.spec.ts +++ b/frontend/src/app/lightning/channel/channel-close-box/channel-close-box.component.spec.ts @@ -1,6 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ChannelCloseBoxComponent } from './channel-close-box.component'; +import { ChannelCloseBoxComponent } from '@components/channel-close-box.component'; describe('ChannelCloseBoxComponent', () => { let component: ChannelCloseBoxComponent; diff --git a/frontend/src/app/lightning/channel/channel-preview.component.ts b/frontend/src/app/lightning/channel/channel-preview.component.ts index 7e3152513..84a85f9c6 100644 --- a/frontend/src/app/lightning/channel/channel-preview.component.ts +++ b/frontend/src/app/lightning/channel/channel-preview.component.ts @@ -2,9 +2,9 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ActivatedRoute, ParamMap } from '@angular/router'; import { Observable, of } from 'rxjs'; import { catchError, switchMap, tap } from 'rxjs/operators'; -import { SeoService } from '../../services/seo.service'; -import { OpenGraphService } from '../../services/opengraph.service'; -import { LightningApiService } from '../lightning-api.service'; +import { SeoService } from '@app/services/seo.service'; +import { OpenGraphService } from '@app/services/opengraph.service'; +import { LightningApiService } from '@app/lightning/lightning-api.service'; @Component({ selector: 'app-channel-preview', diff --git a/frontend/src/app/lightning/channel/channel.component.ts b/frontend/src/app/lightning/channel/channel.component.ts index a26101bdb..501fc74f6 100644 --- a/frontend/src/app/lightning/channel/channel.component.ts +++ b/frontend/src/app/lightning/channel/channel.component.ts @@ -2,10 +2,10 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ActivatedRoute, ParamMap } from '@angular/router'; import { Observable, of, zip } from 'rxjs'; import { catchError, map, shareReplay, switchMap, tap } from 'rxjs/operators'; -import { IChannel } from '../../interfaces/node-api.interface'; -import { ElectrsApiService } from '../../services/electrs-api.service'; -import { SeoService } from '../../services/seo.service'; -import { LightningApiService } from '../lightning-api.service'; +import { IChannel } from '@interfaces/node-api.interface'; +import { ElectrsApiService } from '@app/services/electrs-api.service'; +import { SeoService } from '@app/services/seo.service'; +import { LightningApiService } from '@app/lightning/lightning-api.service'; @Component({ selector: 'app-channel', diff --git a/frontend/src/app/lightning/channels-list/channels-list.component.ts b/frontend/src/app/lightning/channels-list/channels-list.component.ts index d083178c0..3439bb2b4 100644 --- a/frontend/src/app/lightning/channels-list/channels-list.component.ts +++ b/frontend/src/app/lightning/channels-list/channels-list.component.ts @@ -2,8 +2,8 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, OnI import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { BehaviorSubject, merge, Observable } from 'rxjs'; import { map, switchMap, tap } from 'rxjs/operators'; -import { isMobile } from '../../shared/common.utils'; -import { LightningApiService } from '../lightning-api.service'; +import { isMobile } from '@app/shared/common.utils'; +import { LightningApiService } from '@app/lightning/lightning-api.service'; @Component({ selector: 'app-channels-list', diff --git a/frontend/src/app/lightning/channels-statistics/channels-statistics.component.ts b/frontend/src/app/lightning/channels-statistics/channels-statistics.component.ts index f2b78f53c..ee3e13d10 100644 --- a/frontend/src/app/lightning/channels-statistics/channels-statistics.component.ts +++ b/frontend/src/app/lightning/channels-statistics/channels-statistics.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; -import { INodesStatistics } from '../../interfaces/node-api.interface'; +import { INodesStatistics } from '@interfaces/node-api.interface'; @Component({ selector: 'app-channels-statistics', diff --git a/frontend/src/app/lightning/group/group-preview.component.ts b/frontend/src/app/lightning/group/group-preview.component.ts index 35bcb6e0f..4b8f5ed77 100644 --- a/frontend/src/app/lightning/group/group-preview.component.ts +++ b/frontend/src/app/lightning/group/group-preview.component.ts @@ -1,10 +1,10 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, ParamMap } from '@angular/router'; import { map, switchMap, Observable, catchError, of } from 'rxjs'; -import { SeoService } from '../../services/seo.service'; -import { OpenGraphService } from '../../services/opengraph.service'; -import { GeolocationData } from '../../shared/components/geolocation/geolocation.component'; -import { LightningApiService } from '../lightning-api.service'; +import { SeoService } from '@app/services/seo.service'; +import { OpenGraphService } from '@app/services/opengraph.service'; +import { GeolocationData } from '@app/shared/components/geolocation/geolocation.component'; +import { LightningApiService } from '@app/lightning/lightning-api.service'; interface NodeGroup { name: string; diff --git a/frontend/src/app/lightning/group/group.component.ts b/frontend/src/app/lightning/group/group.component.ts index ea83653c2..76f9ef080 100644 --- a/frontend/src/app/lightning/group/group.component.ts +++ b/frontend/src/app/lightning/group/group.component.ts @@ -1,9 +1,9 @@ import { Component, OnInit } from '@angular/core'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { map, Observable, share } from 'rxjs'; -import { SeoService } from '../../services/seo.service'; -import { GeolocationData } from '../../shared/components/geolocation/geolocation.component'; -import { LightningApiService } from '../lightning-api.service'; +import { SeoService } from '@app/services/seo.service'; +import { GeolocationData } from '@app/shared/components/geolocation/geolocation.component'; +import { LightningApiService } from '@app/lightning/lightning-api.service'; @Component({ selector: 'app-group', diff --git a/frontend/src/app/lightning/justice-list/justice-list.component.ts b/frontend/src/app/lightning/justice-list/justice-list.component.ts index d48cdb7ea..e075aed93 100644 --- a/frontend/src/app/lightning/justice-list/justice-list.component.ts +++ b/frontend/src/app/lightning/justice-list/justice-list.component.ts @@ -1,9 +1,9 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { map, Observable, of, Subject, Subscription, switchMap, tap, zip } from 'rxjs'; -import { IChannel } from '../../interfaces/node-api.interface'; -import { LightningApiService } from '../lightning-api.service'; -import { Transaction } from '../../interfaces/electrs.interface'; -import { ElectrsApiService } from '../../services/electrs-api.service'; +import { IChannel } from '@interfaces/node-api.interface'; +import { LightningApiService } from '@app/lightning/lightning-api.service'; +import { Transaction } from '@interfaces/electrs.interface'; +import { ElectrsApiService } from '@app/services/electrs-api.service'; @Component({ selector: 'app-justice-list', diff --git a/frontend/src/app/lightning/lightning-api.service.ts b/frontend/src/app/lightning/lightning-api.service.ts index 74f048d68..14276dc12 100644 --- a/frontend/src/app/lightning/lightning-api.service.ts +++ b/frontend/src/app/lightning/lightning-api.service.ts @@ -1,8 +1,8 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpParams } from '@angular/common/http'; import { BehaviorSubject, Observable, catchError, filter, of, shareReplay, take, tap } from 'rxjs'; -import { StateService } from '../services/state.service'; -import { IChannel, INodesRanking, IOldestNodes, ITopNodesPerCapacity, ITopNodesPerChannels } from '../interfaces/node-api.interface'; +import { StateService } from '@app/services/state.service'; +import { IChannel, INodesRanking, IOldestNodes, ITopNodesPerCapacity, ITopNodesPerChannels } from '@interfaces/node-api.interface'; @Injectable({ providedIn: 'root' diff --git a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts index fd72cddfe..47b7700a4 100644 --- a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts +++ b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts @@ -1,11 +1,11 @@ import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, HostListener, OnInit } from '@angular/core'; import { Observable, merge } from 'rxjs'; import { share } from 'rxjs/operators'; -import { INodesRanking, INodesStatistics } from '../../interfaces/node-api.interface'; -import { SeoService } from '../../services/seo.service'; -import { OpenGraphService } from '../../services/opengraph.service'; -import { StateService } from '../../services/state.service'; -import { LightningApiService } from '../lightning-api.service'; +import { INodesRanking, INodesStatistics } from '@interfaces/node-api.interface'; +import { SeoService } from '@app/services/seo.service'; +import { OpenGraphService } from '@app/services/opengraph.service'; +import { StateService } from '@app/services/state.service'; +import { LightningApiService } from '@app/lightning/lightning-api.service'; @Component({ selector: 'app-lightning-dashboard', diff --git a/frontend/src/app/lightning/lightning-previews.module.ts b/frontend/src/app/lightning/lightning-previews.module.ts index c41ba8d20..5efac798f 100644 --- a/frontend/src/app/lightning/lightning-previews.module.ts +++ b/frontend/src/app/lightning/lightning-previews.module.ts @@ -1,15 +1,15 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { SharedModule } from '../shared/shared.module'; +import { SharedModule } from '@app/shared/shared.module'; import { RouterModule } from '@angular/router'; -import { GraphsModule } from '../graphs/graphs.module'; -import { LightningModule } from './lightning.module'; -import { LightningApiService } from './lightning-api.service'; -import { NodePreviewComponent } from './node/node-preview.component'; -import { LightningPreviewsRoutingModule } from './lightning-previews.routing.module'; -import { ChannelPreviewComponent } from './channel/channel-preview.component'; -import { NodesPerISPPreview } from './nodes-per-isp/nodes-per-isp-preview.component'; -import { GroupPreviewComponent } from './group/group-preview.component'; +import { GraphsModule } from '@app/graphs/graphs.module'; +import { LightningModule } from '@app/lightning/lightning.module'; +import { LightningApiService } from '@app/lightning/lightning-api.service'; +import { NodePreviewComponent } from '@app/lightning/node/node-preview.component'; +import { LightningPreviewsRoutingModule } from '@app/lightning/lightning-previews.routing.module'; +import { ChannelPreviewComponent } from '@app/lightning/channel/channel-preview.component'; +import { NodesPerISPPreview } from '@app/lightning/nodes-per-isp/nodes-per-isp-preview.component'; +import { GroupPreviewComponent } from '@app/lightning/group/group-preview.component'; @NgModule({ declarations: [ NodePreviewComponent, diff --git a/frontend/src/app/lightning/lightning-previews.routing.module.ts b/frontend/src/app/lightning/lightning-previews.routing.module.ts index 6cce90766..d899e559b 100644 --- a/frontend/src/app/lightning/lightning-previews.routing.module.ts +++ b/frontend/src/app/lightning/lightning-previews.routing.module.ts @@ -1,9 +1,9 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { NodePreviewComponent } from './node/node-preview.component'; -import { ChannelPreviewComponent } from './channel/channel-preview.component'; -import { NodesPerISPPreview } from './nodes-per-isp/nodes-per-isp-preview.component'; -import { GroupPreviewComponent } from './group/group-preview.component'; +import { NodePreviewComponent } from '@app/lightning/node/node-preview.component'; +import { ChannelPreviewComponent } from '@app/lightning/channel/channel-preview.component'; +import { NodesPerISPPreview } from '@app/lightning/nodes-per-isp/nodes-per-isp-preview.component'; +import { GroupPreviewComponent } from '@app/lightning/group/group-preview.component'; const routes: Routes = [ { diff --git a/frontend/src/app/lightning/lightning-wrapper/lightning-wrapper.component.ts b/frontend/src/app/lightning/lightning-wrapper/lightning-wrapper.component.ts index 11bf4dc5d..220e7eebd 100644 --- a/frontend/src/app/lightning/lightning-wrapper/lightning-wrapper.component.ts +++ b/frontend/src/app/lightning/lightning-wrapper/lightning-wrapper.component.ts @@ -1,5 +1,7 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; -import { WebsocketService } from '../../services/websocket.service'; +import { WebsocketService } from '@app/services/websocket.service'; +import { Router, ActivatedRoute } from '@angular/router'; +import { handleDemoRedirect } from '../../shared/common.utils'; @Component({ selector: 'app-lightning-wrapper', @@ -11,10 +13,14 @@ export class LightningWrapperComponent implements OnInit { constructor( private websocketService: WebsocketService, + private router: Router, + private route: ActivatedRoute ) { } ngOnInit() { this.websocketService.want(['blocks']); + + handleDemoRedirect(this.route, this.router); } } diff --git a/frontend/src/app/lightning/lightning.module.ts b/frontend/src/app/lightning/lightning.module.ts index 0b824ad78..405900e19 100644 --- a/frontend/src/app/lightning/lightning.module.ts +++ b/frontend/src/app/lightning/lightning.module.ts @@ -1,39 +1,39 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { SharedModule } from '../shared/shared.module'; -import { LightningDashboardComponent } from './lightning-dashboard/lightning-dashboard.component'; -import { LightningApiService } from './lightning-api.service'; -import { NodesListComponent } from './nodes-list/nodes-list.component'; +import { SharedModule } from '@app/shared/shared.module'; +import { LightningDashboardComponent } from '@app/lightning/lightning-dashboard/lightning-dashboard.component'; +import { LightningApiService } from '@app/lightning/lightning-api.service'; +import { NodesListComponent } from '@app/lightning/nodes-list/nodes-list.component'; import { RouterModule } from '@angular/router'; -import { NodeStatisticsComponent } from './node-statistics/node-statistics.component'; -import { NodeComponent } from './node/node.component'; -import { LightningRoutingModule } from './lightning.routing.module'; -import { ChannelsListComponent } from './channels-list/channels-list.component'; -import { ChannelComponent } from './channel/channel.component'; -import { LightningWrapperComponent } from './lightning-wrapper/lightning-wrapper.component'; -import { ChannelBoxComponent } from './channel/channel-box/channel-box.component'; -import { ChannelCloseBoxComponent } from './channel/channel-close-box/channel-close-box.component'; -import { ClosingTypeComponent } from './channel/closing-type/closing-type.component'; -import { LightningStatisticsChartComponent } from './statistics-chart/lightning-statistics-chart.component'; -import { NodeStatisticsChartComponent } from './node-statistics-chart/node-statistics-chart.component'; -import { NodeFeeChartComponent } from './node-fee-chart/node-fee-chart.component'; -import { GraphsModule } from '../graphs/graphs.module'; -import { NodesNetworksChartComponent } from './nodes-networks-chart/nodes-networks-chart.component'; -import { ChannelsStatisticsComponent } from './channels-statistics/channels-statistics.component'; -import { NodesPerISPChartComponent } from './nodes-per-isp-chart/nodes-per-isp-chart.component'; -import { NodesPerCountry } from './nodes-per-country/nodes-per-country.component'; -import { NodesPerISP } from './nodes-per-isp/nodes-per-isp.component'; -import { NodesPerCountryChartComponent } from '../lightning/nodes-per-country-chart/nodes-per-country-chart.component'; -import { NodesMap } from '../lightning/nodes-map/nodes-map.component'; -import { NodesChannelsMap } from '../lightning/nodes-channels-map/nodes-channels-map.component'; -import { NodesRanking } from '../lightning/nodes-ranking/nodes-ranking.component'; -import { TopNodesPerChannels } from '../lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component'; -import { TopNodesPerCapacity } from '../lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component'; -import { JusticeList } from '../lightning/justice-list/justice-list.component'; -import { OldestNodes } from '../lightning/nodes-ranking/oldest-nodes/oldest-nodes.component'; -import { NodesRankingsDashboard } from '../lightning/nodes-rankings-dashboard/nodes-rankings-dashboard.component'; -import { NodeChannels } from '../lightning/nodes-channels/node-channels.component'; -import { GroupComponent } from './group/group.component'; +import { NodeStatisticsComponent } from '@app/lightning/node-statistics/node-statistics.component'; +import { NodeComponent } from '@app/lightning/node/node.component'; +import { LightningRoutingModule } from '@app/lightning/lightning.routing.module'; +import { ChannelsListComponent } from '@app/lightning/channels-list/channels-list.component'; +import { ChannelComponent } from '@app/lightning/channel/channel.component'; +import { LightningWrapperComponent } from '@app/lightning/lightning-wrapper/lightning-wrapper.component'; +import { ChannelBoxComponent } from '@app/lightning/channel/channel-box/channel-box.component'; +import { ChannelCloseBoxComponent } from '@app/lightning/channel/channel-close-box/channel-close-box.component'; +import { ClosingTypeComponent } from '@app/lightning/channel/closing-type/closing-type.component'; +import { LightningStatisticsChartComponent } from '@app/lightning/statistics-chart/lightning-statistics-chart.component'; +import { NodeStatisticsChartComponent } from '@app/lightning/node-statistics-chart/node-statistics-chart.component'; +import { NodeFeeChartComponent } from '@app/lightning/node-fee-chart/node-fee-chart.component'; +import { GraphsModule } from '@app/graphs/graphs.module'; +import { NodesNetworksChartComponent } from '@app/lightning/nodes-networks-chart/nodes-networks-chart.component'; +import { ChannelsStatisticsComponent } from '@app/lightning/channels-statistics/channels-statistics.component'; +import { NodesPerISPChartComponent } from '@app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component'; +import { NodesPerCountry } from '@app/lightning/nodes-per-country/nodes-per-country.component'; +import { NodesPerISP } from '@app/lightning/nodes-per-isp/nodes-per-isp.component'; +import { NodesPerCountryChartComponent } from '@app/lightning/nodes-per-country-chart/nodes-per-country-chart.component'; +import { NodesMap } from '@app/lightning/nodes-map/nodes-map.component'; +import { NodesChannelsMap } from '@app/lightning/nodes-channels-map/nodes-channels-map.component'; +import { NodesRanking } from '@app/lightning/nodes-ranking/nodes-ranking.component'; +import { TopNodesPerChannels } from '@app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component'; +import { TopNodesPerCapacity } from '@app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component'; +import { JusticeList } from '@app/lightning/justice-list/justice-list.component'; +import { OldestNodes } from '@app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component'; +import { NodesRankingsDashboard } from '@app/lightning/nodes-rankings-dashboard/nodes-rankings-dashboard.component'; +import { NodeChannels } from '@app/lightning/nodes-channels/node-channels.component'; +import { GroupComponent } from '@app/lightning/group/group.component'; @NgModule({ declarations: [ diff --git a/frontend/src/app/lightning/lightning.routing.module.ts b/frontend/src/app/lightning/lightning.routing.module.ts index 8b8041181..c7f167308 100644 --- a/frontend/src/app/lightning/lightning.routing.module.ts +++ b/frontend/src/app/lightning/lightning.routing.module.ts @@ -1,15 +1,15 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { LightningDashboardComponent } from './lightning-dashboard/lightning-dashboard.component'; -import { LightningWrapperComponent } from './lightning-wrapper/lightning-wrapper.component'; -import { NodeComponent } from './node/node.component'; -import { ChannelComponent } from './channel/channel.component'; -import { NodesPerCountry } from './nodes-per-country/nodes-per-country.component'; -import { NodesPerISP } from './nodes-per-isp/nodes-per-isp.component'; -import { NodesRanking } from './nodes-ranking/nodes-ranking.component'; -import { NodesRankingsDashboard } from './nodes-rankings-dashboard/nodes-rankings-dashboard.component'; -import { GroupComponent } from './group/group.component'; -import { JusticeList } from './justice-list/justice-list.component'; +import { LightningDashboardComponent } from '@app/lightning/lightning-dashboard/lightning-dashboard.component'; +import { LightningWrapperComponent } from '@app/lightning/lightning-wrapper/lightning-wrapper.component'; +import { NodeComponent } from '@app/lightning/node/node.component'; +import { ChannelComponent } from '@app/lightning/channel/channel.component'; +import { NodesPerCountry } from '@app/lightning/nodes-per-country/nodes-per-country.component'; +import { NodesPerISP } from '@app/lightning/nodes-per-isp/nodes-per-isp.component'; +import { NodesRanking } from '@app/lightning/nodes-ranking/nodes-ranking.component'; +import { NodesRankingsDashboard } from '@app/lightning/nodes-rankings-dashboard/nodes-rankings-dashboard.component'; +import { GroupComponent } from '@app/lightning/group/group.component'; +import { JusticeList } from '@app/lightning/justice-list/justice-list.component'; const routes: Routes = [ { diff --git a/frontend/src/app/lightning/node-fee-chart/node-fee-chart.component.ts b/frontend/src/app/lightning/node-fee-chart/node-fee-chart.component.ts index aa0837fce..65a12cf38 100644 --- a/frontend/src/app/lightning/node-fee-chart/node-fee-chart.component.ts +++ b/frontend/src/app/lightning/node-fee-chart/node-fee-chart.component.ts @@ -1,11 +1,11 @@ import { Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core'; -import { EChartsOption } from '../../graphs/echarts'; +import { EChartsOption } from '@app/graphs/echarts'; import { switchMap } from 'rxjs/operators'; -import { download } from '../../shared/graphs.utils'; -import { LightningApiService } from '../lightning-api.service'; +import { download } from '@app/shared/graphs.utils'; +import { LightningApiService } from '@app/lightning/lightning-api.service'; import { ActivatedRoute, ParamMap } from '@angular/router'; -import { AmountShortenerPipe } from '../../shared/pipes/amount-shortener.pipe'; -import { StateService } from '../../services/state.service'; +import { AmountShortenerPipe } from '@app/shared/pipes/amount-shortener.pipe'; +import { StateService } from '@app/services/state.service'; @Component({ selector: 'app-node-fee-chart', diff --git a/frontend/src/app/lightning/node-statistics-chart/node-statistics-chart.component.ts b/frontend/src/app/lightning/node-statistics-chart/node-statistics-chart.component.ts index 91813400d..de9711d01 100644 --- a/frontend/src/app/lightning/node-statistics-chart/node-statistics-chart.component.ts +++ b/frontend/src/app/lightning/node-statistics-chart/node-statistics-chart.component.ts @@ -1,14 +1,14 @@ import { Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core'; -import { EChartsOption } from '../../graphs/echarts'; +import { EChartsOption } from '@app/graphs/echarts'; import { Observable } from 'rxjs'; import { switchMap, tap } from 'rxjs/operators'; import { formatNumber } from '@angular/common'; import { UntypedFormGroup } from '@angular/forms'; -import { StorageService } from '../../services/storage.service'; -import { download } from '../../shared/graphs.utils'; -import { LightningApiService } from '../lightning-api.service'; +import { StorageService } from '@app/services/storage.service'; +import { download } from '@app/shared/graphs.utils'; +import { LightningApiService } from '@app/lightning/lightning-api.service'; import { ActivatedRoute, ParamMap } from '@angular/router'; -import { StateService } from '../../services/state.service'; +import { StateService } from '@app/services/state.service'; @Component({ selector: 'app-node-statistics-chart', diff --git a/frontend/src/app/lightning/node-statistics/node-statistics.component.ts b/frontend/src/app/lightning/node-statistics/node-statistics.component.ts index 338e17ab8..a4b061e38 100644 --- a/frontend/src/app/lightning/node-statistics/node-statistics.component.ts +++ b/frontend/src/app/lightning/node-statistics/node-statistics.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; -import { INodesStatistics } from '../../interfaces/node-api.interface'; +import { INodesStatistics } from '@interfaces/node-api.interface'; @Component({ selector: 'app-node-statistics', diff --git a/frontend/src/app/lightning/node/node-preview.component.ts b/frontend/src/app/lightning/node/node-preview.component.ts index d47a8c5ad..259313de6 100644 --- a/frontend/src/app/lightning/node/node-preview.component.ts +++ b/frontend/src/app/lightning/node/node-preview.component.ts @@ -2,11 +2,11 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ActivatedRoute, ParamMap } from '@angular/router'; import { Observable } from 'rxjs'; import { catchError, map, switchMap } from 'rxjs/operators'; -import { SeoService } from '../../services/seo.service'; -import { OpenGraphService } from '../../services/opengraph.service'; -import { getFlagEmoji } from '../../shared/common.utils'; -import { LightningApiService } from '../lightning-api.service'; -import { isMobile } from '../../shared/common.utils'; +import { SeoService } from '@app/services/seo.service'; +import { OpenGraphService } from '@app/services/opengraph.service'; +import { getFlagEmoji } from '@app/shared/common.utils'; +import { LightningApiService } from '@app/lightning/lightning-api.service'; +import { isMobile } from '@app/shared/common.utils'; @Component({ selector: 'app-node-preview', diff --git a/frontend/src/app/lightning/node/node.component.ts b/frontend/src/app/lightning/node/node.component.ts index cc7c8e5b1..4ad455dee 100644 --- a/frontend/src/app/lightning/node/node.component.ts +++ b/frontend/src/app/lightning/node/node.component.ts @@ -2,13 +2,13 @@ import { ChangeDetectionStrategy, Component, OnInit, ChangeDetectorRef } from '@ import { ActivatedRoute, ParamMap } from '@angular/router'; import { Observable, of } from 'rxjs'; import { catchError, map, switchMap, tap, share } from 'rxjs/operators'; -import { SeoService } from '../../services/seo.service'; -import { ApiService } from '../../services/api.service'; -import { LightningApiService } from '../lightning-api.service'; -import { GeolocationData } from '../../shared/components/geolocation/geolocation.component'; -import { ILiquidityAd, parseLiquidityAdHex } from './liquidity-ad'; -import { haversineDistance, kmToMiles } from '../../../app/shared/common.utils'; -import { ServicesApiServices } from '../../services/services-api.service'; +import { SeoService } from '@app/services/seo.service'; +import { ApiService } from '@app/services/api.service'; +import { LightningApiService } from '@app/lightning/lightning-api.service'; +import { GeolocationData } from '@app/shared/components/geolocation/geolocation.component'; +import { ILiquidityAd, parseLiquidityAdHex } from '@app/lightning/node/liquidity-ad'; +import { haversineDistance, kmToMiles } from '@app/shared/common.utils'; +import { ServicesApiServices } from '@app/services/services-api.service'; interface CustomRecord { type: string; diff --git a/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts b/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts index 3447348be..a1efdc144 100644 --- a/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts +++ b/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts @@ -1,16 +1,16 @@ import { ChangeDetectionStrategy, Component, Input, Output, EventEmitter, NgZone, OnInit } from '@angular/core'; -import { SeoService } from '../../services/seo.service'; -import { ApiService } from '../../services/api.service'; +import { SeoService } from '@app/services/seo.service'; +import { ApiService } from '@app/services/api.service'; import { delay, Observable, of, switchMap, tap, zip } from 'rxjs'; -import { AssetsService } from '../../services/assets.service'; +import { AssetsService } from '@app/services/assets.service'; import { ActivatedRoute, ParamMap, Router } from '@angular/router'; -import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; -import { StateService } from '../../services/state.service'; -import { EChartsOption, echarts } from '../../graphs/echarts'; -import { isMobile } from '../../shared/common.utils'; -import { AmountShortenerPipe } from '../../shared/pipes/amount-shortener.pipe'; -import { getFlagEmoji } from '../../shared/common.utils'; -import { lerpColor } from '../../shared/graphs.utils'; +import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe'; +import { StateService } from '@app/services/state.service'; +import { EChartsOption, echarts } from '@app/graphs/echarts'; +import { isMobile } from '@app/shared/common.utils'; +import { AmountShortenerPipe } from '@app/shared/pipes/amount-shortener.pipe'; +import { getFlagEmoji } from '@app/shared/common.utils'; +import { lerpColor } from '@app/shared/graphs.utils'; @Component({ selector: 'app-nodes-channels-map', diff --git a/frontend/src/app/lightning/nodes-channels/node-channels.component.ts b/frontend/src/app/lightning/nodes-channels/node-channels.component.ts index fce014e77..96f74bfed 100644 --- a/frontend/src/app/lightning/nodes-channels/node-channels.component.ts +++ b/frontend/src/app/lightning/nodes-channels/node-channels.component.ts @@ -1,13 +1,13 @@ import { formatNumber } from '@angular/common'; import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, NgZone, OnChanges } from '@angular/core'; import { Router } from '@angular/router'; -import { EChartsOption, TreemapSeriesOption } from '../../graphs/echarts'; +import { EChartsOption, TreemapSeriesOption } from '@app/graphs/echarts'; import { Observable, share, switchMap, tap } from 'rxjs'; -import { lerpColor } from '../../shared/graphs.utils'; -import { AmountShortenerPipe } from '../../shared/pipes/amount-shortener.pipe'; -import { LightningApiService } from '../lightning-api.service'; -import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; -import { StateService } from '../../services/state.service'; +import { lerpColor } from '@app/shared/graphs.utils'; +import { AmountShortenerPipe } from '@app/shared/pipes/amount-shortener.pipe'; +import { LightningApiService } from '@app/lightning/lightning-api.service'; +import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe'; +import { StateService } from '@app/services/state.service'; @Component({ selector: 'app-node-channels', @@ -144,4 +144,4 @@ export class NodeChannels implements OnChanges { }); }); } -} \ No newline at end of file +} diff --git a/frontend/src/app/lightning/nodes-map/nodes-map.component.ts b/frontend/src/app/lightning/nodes-map/nodes-map.component.ts index 50df2f986..c4d56676c 100644 --- a/frontend/src/app/lightning/nodes-map/nodes-map.component.ts +++ b/frontend/src/app/lightning/nodes-map/nodes-map.component.ts @@ -1,15 +1,15 @@ import { ChangeDetectionStrategy, Component, Inject, Input, Output, EventEmitter, LOCALE_ID, NgZone, OnDestroy, OnInit, OnChanges } from '@angular/core'; -import { SeoService } from '../../services/seo.service'; -import { ApiService } from '../../services/api.service'; +import { SeoService } from '@app/services/seo.service'; +import { ApiService } from '@app/services/api.service'; import { Observable, BehaviorSubject, switchMap, tap, combineLatest } from 'rxjs'; -import { AssetsService } from '../../services/assets.service'; -import { EChartsOption, echarts } from '../../graphs/echarts'; -import { lerpColor } from '../../shared/graphs.utils'; +import { AssetsService } from '@app/services/assets.service'; +import { EChartsOption, echarts } from '@app/graphs/echarts'; +import { lerpColor } from '@app/shared/graphs.utils'; import { Router } from '@angular/router'; -import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; -import { StateService } from '../../services/state.service'; -import { AmountShortenerPipe } from '../../shared/pipes/amount-shortener.pipe'; -import { getFlagEmoji } from '../../shared/common.utils'; +import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe'; +import { StateService } from '@app/services/state.service'; +import { AmountShortenerPipe } from '@app/shared/pipes/amount-shortener.pipe'; +import { getFlagEmoji } from '@app/shared/common.utils'; @Component({ selector: 'app-nodes-map', diff --git a/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.ts b/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.ts index d947bb327..a5d0d076a 100644 --- a/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.ts +++ b/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.ts @@ -1,17 +1,17 @@ import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding, OnChanges, SimpleChanges } from '@angular/core'; -import { echarts, EChartsOption, LineSeriesOption } from '../../graphs/echarts'; +import { echarts, EChartsOption, LineSeriesOption } from '@app/graphs/echarts'; import { Observable } from 'rxjs'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; import { formatNumber } from '@angular/common'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { StorageService } from '../../services/storage.service'; -import { MiningService } from '../../services/mining.service'; -import { download } from '../../shared/graphs.utils'; -import { SeoService } from '../../services/seo.service'; -import { LightningApiService } from '../lightning-api.service'; -import { AmountShortenerPipe } from '../../shared/pipes/amount-shortener.pipe'; -import { isMobile } from '../../shared/common.utils'; -import { StateService } from '../../services/state.service'; +import { StorageService } from '@app/services/storage.service'; +import { MiningService } from '@app/services/mining.service'; +import { download } from '@app/shared/graphs.utils'; +import { SeoService } from '@app/services/seo.service'; +import { LightningApiService } from '@app/lightning/lightning-api.service'; +import { AmountShortenerPipe } from '@app/shared/pipes/amount-shortener.pipe'; +import { isMobile } from '@app/shared/common.utils'; +import { StateService } from '@app/services/state.service'; @Component({ selector: 'app-nodes-networks-chart', diff --git a/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.ts b/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.ts index 7eba4f9b7..986484d8a 100644 --- a/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.ts +++ b/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.ts @@ -1,15 +1,15 @@ import { ChangeDetectionStrategy, Component, OnInit, HostBinding, NgZone } from '@angular/core'; import { Router } from '@angular/router'; -import { EChartsOption, PieSeriesOption } from '../../graphs/echarts'; +import { EChartsOption, PieSeriesOption } from '@app/graphs/echarts'; import { map, Observable, share, tap } from 'rxjs'; -import { chartColors } from '../../app.constants'; -import { ApiService } from '../../services/api.service'; -import { SeoService } from '../../services/seo.service'; -import { StateService } from '../../services/state.service'; -import { download } from '../../shared/graphs.utils'; -import { AmountShortenerPipe } from '../../shared/pipes/amount-shortener.pipe'; -import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; -import { getFlagEmoji } from '../../shared/common.utils'; +import { chartColors } from '@app/app.constants'; +import { ApiService } from '@app/services/api.service'; +import { SeoService } from '@app/services/seo.service'; +import { StateService } from '@app/services/state.service'; +import { download } from '@app/shared/graphs.utils'; +import { AmountShortenerPipe } from '@app/shared/pipes/amount-shortener.pipe'; +import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe'; +import { getFlagEmoji } from '@app/shared/common.utils'; @Component({ selector: 'app-nodes-per-country-chart', diff --git a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts index 619ee01c0..b2b9e1f5c 100644 --- a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts +++ b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts @@ -1,10 +1,10 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { BehaviorSubject, combineLatest, map, Observable, share, tap } from 'rxjs'; -import { ApiService } from '../../services/api.service'; -import { SeoService } from '../../services/seo.service'; -import { getFlagEmoji } from '../../shared/common.utils'; -import { GeolocationData } from '../../shared/components/geolocation/geolocation.component'; +import { ApiService } from '@app/services/api.service'; +import { SeoService } from '@app/services/seo.service'; +import { getFlagEmoji } from '@app/shared/common.utils'; +import { GeolocationData } from '@app/shared/components/geolocation/geolocation.component'; @Component({ selector: 'app-nodes-per-country', diff --git a/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts b/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts index 429d7ee36..56a03a524 100644 --- a/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts +++ b/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts @@ -1,15 +1,15 @@ import { ChangeDetectionStrategy, Component, OnInit, HostBinding, NgZone, Input } from '@angular/core'; import { Router } from '@angular/router'; -import { EChartsOption, PieSeriesOption } from '../../graphs/echarts'; +import { EChartsOption, PieSeriesOption } from '@app/graphs/echarts'; import { combineLatest, map, Observable, share, startWith, Subject, switchMap, tap } from 'rxjs'; -import { chartColors } from '../../app.constants'; -import { ApiService } from '../../services/api.service'; -import { SeoService } from '../../services/seo.service'; -import { StateService } from '../../services/state.service'; -import { isMobile } from '../../shared/common.utils'; -import { download } from '../../shared/graphs.utils'; -import { AmountShortenerPipe } from '../../shared/pipes/amount-shortener.pipe'; -import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; +import { chartColors } from '@app/app.constants'; +import { ApiService } from '@app/services/api.service'; +import { SeoService } from '@app/services/seo.service'; +import { StateService } from '@app/services/state.service'; +import { isMobile } from '@app/shared/common.utils'; +import { download } from '@app/shared/graphs.utils'; +import { AmountShortenerPipe } from '@app/shared/pipes/amount-shortener.pipe'; +import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe'; @Component({ selector: 'app-nodes-per-isp-chart', diff --git a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp-preview.component.ts b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp-preview.component.ts index 313353ab8..9fc071eb5 100644 --- a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp-preview.component.ts +++ b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp-preview.component.ts @@ -1,11 +1,11 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ActivatedRoute, ParamMap } from '@angular/router'; import { catchError, map, switchMap, Observable, share, of } from 'rxjs'; -import { ApiService } from '../../services/api.service'; -import { SeoService } from '../../services/seo.service'; -import { OpenGraphService } from '../../services/opengraph.service'; -import { getFlagEmoji } from '../../shared/common.utils'; -import { GeolocationData } from '../../shared/components/geolocation/geolocation.component'; +import { ApiService } from '@app/services/api.service'; +import { SeoService } from '@app/services/seo.service'; +import { OpenGraphService } from '@app/services/opengraph.service'; +import { getFlagEmoji } from '@app/shared/common.utils'; +import { GeolocationData } from '@app/shared/components/geolocation/geolocation.component'; @Component({ selector: 'app-nodes-per-isp-preview', diff --git a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.ts b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.ts index f6c61a9f6..1c8ad3e1b 100644 --- a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.ts +++ b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp.component.ts @@ -1,10 +1,10 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { BehaviorSubject, combineLatest, map, Observable, share, tap } from 'rxjs'; -import { ApiService } from '../../services/api.service'; -import { SeoService } from '../../services/seo.service'; -import { getFlagEmoji } from '../../shared/common.utils'; -import { GeolocationData } from '../../shared/components/geolocation/geolocation.component'; +import { ApiService } from '@app/services/api.service'; +import { SeoService } from '@app/services/seo.service'; +import { getFlagEmoji } from '@app/shared/common.utils'; +import { GeolocationData } from '@app/shared/components/geolocation/geolocation.component'; @Component({ selector: 'app-nodes-per-isp', diff --git a/frontend/src/app/lightning/nodes-ranking/nodes-ranking.component.ts b/frontend/src/app/lightning/nodes-ranking/nodes-ranking.component.ts index 8a1eae3dc..569bd8827 100644 --- a/frontend/src/app/lightning/nodes-ranking/nodes-ranking.component.ts +++ b/frontend/src/app/lightning/nodes-ranking/nodes-ranking.component.ts @@ -1,9 +1,9 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { LightningApiService } from '../lightning-api.service'; +import { LightningApiService } from '@app/lightning/lightning-api.service'; import { share } from 'rxjs/operators'; import { Observable } from 'rxjs'; -import { INodesStatistics } from '../../interfaces/node-api.interface'; +import { INodesStatistics } from '@interfaces/node-api.interface'; @Component({ selector: 'app-nodes-ranking', diff --git a/frontend/src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.ts b/frontend/src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.ts index d83f3db0a..a554341b9 100644 --- a/frontend/src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.ts +++ b/frontend/src/app/lightning/nodes-ranking/oldest-nodes/oldest-nodes.component.ts @@ -1,9 +1,9 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { map, Observable } from 'rxjs'; -import { GeolocationData } from '../../../shared/components/geolocation/geolocation.component'; -import { SeoService } from '../../../services/seo.service'; -import { IOldestNodes } from '../../../interfaces/node-api.interface'; -import { LightningApiService } from '../../lightning-api.service'; +import { GeolocationData } from '@app/shared/components/geolocation/geolocation.component'; +import { SeoService } from '@app/services/seo.service'; +import { IOldestNodes } from '@interfaces/node-api.interface'; +import { LightningApiService } from '@app/lightning/lightning-api.service'; @Component({ selector: 'app-oldest-nodes', diff --git a/frontend/src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.ts b/frontend/src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.ts index 0b8c03bbd..24c8757b6 100644 --- a/frontend/src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.ts +++ b/frontend/src/app/lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component.ts @@ -1,10 +1,10 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { combineLatest, map, Observable } from 'rxjs'; -import { INodesRanking, INodesStatistics, ITopNodesPerCapacity } from '../../../interfaces/node-api.interface'; -import { SeoService } from '../../../services/seo.service'; -import { StateService } from '../../../services/state.service'; -import { GeolocationData } from '../../../shared/components/geolocation/geolocation.component'; -import { LightningApiService } from '../../lightning-api.service'; +import { INodesRanking, INodesStatistics, ITopNodesPerCapacity } from '@interfaces/node-api.interface'; +import { SeoService } from '@app/services/seo.service'; +import { StateService } from '@app/services/state.service'; +import { GeolocationData } from '@app/shared/components/geolocation/geolocation.component'; +import { LightningApiService } from '@app/lightning/lightning-api.service'; @Component({ selector: 'app-top-nodes-per-capacity', diff --git a/frontend/src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.ts b/frontend/src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.ts index 56d55a5d3..6dbcb9c3e 100644 --- a/frontend/src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.ts +++ b/frontend/src/app/lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component.ts @@ -1,10 +1,10 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { combineLatest, map, Observable } from 'rxjs'; -import { INodesRanking, INodesStatistics, ITopNodesPerChannels } from '../../../interfaces/node-api.interface'; -import { SeoService } from '../../../services/seo.service'; -import { StateService } from '../../../services/state.service'; -import { GeolocationData } from '../../../shared/components/geolocation/geolocation.component'; -import { LightningApiService } from '../../lightning-api.service'; +import { INodesRanking, INodesStatistics, ITopNodesPerChannels } from '@interfaces/node-api.interface'; +import { SeoService } from '@app/services/seo.service'; +import { StateService } from '@app/services/state.service'; +import { GeolocationData } from '@app/shared/components/geolocation/geolocation.component'; +import { LightningApiService } from '@app/lightning/lightning-api.service'; @Component({ selector: 'app-top-nodes-per-channels', diff --git a/frontend/src/app/lightning/nodes-rankings-dashboard/nodes-rankings-dashboard.component.ts b/frontend/src/app/lightning/nodes-rankings-dashboard/nodes-rankings-dashboard.component.ts index 178ca783c..5561d086e 100644 --- a/frontend/src/app/lightning/nodes-rankings-dashboard/nodes-rankings-dashboard.component.ts +++ b/frontend/src/app/lightning/nodes-rankings-dashboard/nodes-rankings-dashboard.component.ts @@ -1,8 +1,8 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { Observable, share } from 'rxjs'; -import { INodesRanking } from '../../interfaces/node-api.interface'; -import { SeoService } from '../../services/seo.service'; -import { LightningApiService } from '../lightning-api.service'; +import { INodesRanking } from '@interfaces/node-api.interface'; +import { SeoService } from '@app/services/seo.service'; +import { LightningApiService } from '@app/lightning/lightning-api.service'; @Component({ selector: 'app-nodes-rankings-dashboard', diff --git a/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.ts b/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.ts index 77ce42f26..08523088c 100644 --- a/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.ts +++ b/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.ts @@ -1,17 +1,17 @@ import { Component, Inject, Input, LOCALE_ID, OnInit, HostBinding, OnChanges, SimpleChanges } from '@angular/core'; -import { echarts, EChartsOption } from '../../graphs/echarts'; +import { echarts, EChartsOption } from '@app/graphs/echarts'; import { Observable, combineLatest, fromEvent } from 'rxjs'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; -import { SeoService } from '../../services/seo.service'; +import { SeoService } from '@app/services/seo.service'; import { formatNumber } from '@angular/common'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { StorageService } from '../../services/storage.service'; -import { MiningService } from '../../services/mining.service'; -import { download } from '../../shared/graphs.utils'; -import { LightningApiService } from '../lightning-api.service'; -import { AmountShortenerPipe } from '../../shared/pipes/amount-shortener.pipe'; -import { isMobile } from '../../shared/common.utils'; -import { StateService } from '../../services/state.service'; +import { StorageService } from '@app/services/storage.service'; +import { MiningService } from '@app/services/mining.service'; +import { download } from '@app/shared/graphs.utils'; +import { LightningApiService } from '@app/lightning/lightning-api.service'; +import { AmountShortenerPipe } from '@app/shared/pipes/amount-shortener.pipe'; +import { isMobile } from '@app/shared/common.utils'; +import { StateService } from '@app/services/state.service'; @Component({ selector: 'app-lightning-statistics-chart', diff --git a/frontend/src/app/liquid/liquid-graphs.module.ts b/frontend/src/app/liquid/liquid-graphs.module.ts index 3da93fc9d..9cfd7da83 100644 --- a/frontend/src/app/liquid/liquid-graphs.module.ts +++ b/frontend/src/app/liquid/liquid-graphs.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Routes, RouterModule } from '@angular/router'; -import { LiquidMasterPageComponent } from '../components/liquid-master-page/liquid-master-page.component'; +import { LiquidMasterPageComponent } from '@components/liquid-master-page/liquid-master-page.component'; const routes: Routes = [ { diff --git a/frontend/src/app/liquid/liquid-master-page.module.ts b/frontend/src/app/liquid/liquid-master-page.module.ts index 5d5f0b8dd..17c2c8c41 100644 --- a/frontend/src/app/liquid/liquid-master-page.module.ts +++ b/frontend/src/app/liquid/liquid-master-page.module.ts @@ -1,25 +1,25 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Routes, RouterModule } from '@angular/router'; -import { SharedModule } from '../shared/shared.module'; +import { SharedModule } from '@app/shared/shared.module'; import { NgxEchartsModule } from 'ngx-echarts'; -import { LiquidMasterPageComponent } from '../components/liquid-master-page/liquid-master-page.component'; +import { LiquidMasterPageComponent } from '@components/liquid-master-page/liquid-master-page.component'; -import { StartComponent } from '../components/start/start.component'; -import { PushTransactionComponent } from '../components/push-transaction/push-transaction.component'; -import { BlocksList } from '../components/blocks-list/blocks-list.component'; -import { AssetGroupComponent } from '../components/assets/asset-group/asset-group.component'; -import { AssetsComponent } from '../components/assets/assets.component'; -import { AssetsFeaturedComponent } from '../components/assets/assets-featured/assets-featured.component' -import { AssetComponent } from '../components/asset/asset.component'; -import { AssetsNavComponent } from '../components/assets/assets-nav/assets-nav.component'; -import { RecentPegsListComponent } from '../components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component'; -import { FederationWalletComponent } from '../components/liquid-reserves-audit/federation-wallet/federation-wallet.component'; -import { FederationUtxosListComponent } from '../components/liquid-reserves-audit/federation-utxos-list/federation-utxos-list.component'; -import { FederationAddressesListComponent } from '../components/liquid-reserves-audit/federation-addresses-list/federation-addresses-list.component'; -import { ServerHealthComponent } from '../components/server-health/server-health.component'; -import { ServerStatusComponent } from '../components/server-health/server-status.component'; +import { StartComponent } from '@components/start/start.component'; +import { PushTransactionComponent } from '@components/push-transaction/push-transaction.component'; +import { BlocksList } from '@components/blocks-list/blocks-list.component'; +import { AssetGroupComponent } from '@components/assets/asset-group/asset-group.component'; +import { AssetsComponent } from '@components/assets/assets.component'; +import { AssetsFeaturedComponent } from '@components/assets/assets-featured/assets-featured.component' +import { AssetComponent } from '@components/asset/asset.component'; +import { AssetsNavComponent } from '@components/assets/assets-nav/assets-nav.component'; +import { RecentPegsListComponent } from '@components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component'; +import { FederationWalletComponent } from '@components/liquid-reserves-audit/federation-wallet/federation-wallet.component'; +import { FederationUtxosListComponent } from '@components/liquid-reserves-audit/federation-utxos-list/federation-utxos-list.component'; +import { FederationAddressesListComponent } from '@components/liquid-reserves-audit/federation-addresses-list/federation-addresses-list.component'; +import { ServerHealthComponent } from '@components/server-health/server-health.component'; +import { ServerStatusComponent } from '@components/server-health/server-status.component'; const routes: Routes = [ { @@ -32,7 +32,7 @@ const routes: Routes = [ }, { path: 'about', - loadChildren: () => import('../components/about/about.module').then(m => m.AboutModule), + loadChildren: () => import('@components/about/about.module').then(m => m.AboutModule), }, { path: 'blocks/:page', @@ -44,27 +44,27 @@ const routes: Routes = [ }, { path: 'terms-of-service', - loadChildren: () => import('../components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule), + loadChildren: () => import('@components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule), }, { path: 'privacy-policy', - loadChildren: () => import('../components/privacy-policy/privacy-policy.module').then(m => m.PrivacyPolicyModule), + loadChildren: () => import('@components/privacy-policy/privacy-policy.module').then(m => m.PrivacyPolicyModule), }, { path: 'trademark-policy', - loadChildren: () => import('../components/trademark-policy/trademark-policy.module').then(m => m.TrademarkModule), + loadChildren: () => import('@components/trademark-policy/trademark-policy.module').then(m => m.TrademarkModule), }, { path: 'tx', component: StartComponent, data: { preload: true, networkSpecific: true }, - loadChildren: () => import('../components/transaction/transaction.module').then(m => m.TransactionModule), + loadChildren: () => import('@components/transaction/transaction.module').then(m => m.TransactionModule), }, { path: 'block', component: StartComponent, data: { preload: true, networkSpecific: true }, - loadChildren: () => import('../components/block/block.module').then(m => m.BlockModule), + loadChildren: () => import('@components/block/block.module').then(m => m.BlockModule), }, { path: 'audit/wallet', @@ -178,4 +178,4 @@ export class LiquidRoutingModule { } FederationUtxosListComponent, ] }) -export class LiquidMasterPageModule { } \ No newline at end of file +export class LiquidMasterPageModule { } diff --git a/frontend/src/app/master-page.module.ts b/frontend/src/app/master-page.module.ts index 510756cbc..2ee2e0bd8 100644 --- a/frontend/src/app/master-page.module.ts +++ b/frontend/src/app/master-page.module.ts @@ -1,18 +1,18 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Routes, RouterModule, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; -import { MasterPageComponent } from './components/master-page/master-page.component'; -import { SharedModule } from './shared/shared.module'; +import { MasterPageComponent } from '@components/master-page/master-page.component'; +import { SharedModule } from '@app/shared/shared.module'; -import { StartComponent } from './components/start/start.component'; -import { PushTransactionComponent } from './components/push-transaction/push-transaction.component'; -import { TestTransactionsComponent } from './components/test-transactions/test-transactions.component'; -import { CalculatorComponent } from './components/calculator/calculator.component'; -import { BlocksList } from './components/blocks-list/blocks-list.component'; -import { RbfList } from './components/rbf-list/rbf-list.component'; -import { ServerHealthComponent } from './components/server-health/server-health.component'; -import { ServerStatusComponent } from './components/server-health/server-status.component'; -import { FaucetComponent } from './components/faucet/faucet.component' +import { StartComponent } from '@components/start/start.component'; +import { PushTransactionComponent } from '@components/push-transaction/push-transaction.component'; +import { TestTransactionsComponent } from '@components/test-transactions/test-transactions.component'; +import { CalculatorComponent } from '@components/calculator/calculator.component'; +import { BlocksList } from '@components/blocks-list/blocks-list.component'; +import { RbfList } from '@components/rbf-list/rbf-list.component'; +import { ServerHealthComponent } from '@components/server-health/server-health.component'; +import { ServerStatusComponent } from '@components/server-health/server-status.component'; +import { FaucetComponent } from '@components/faucet/faucet.component' const browserWindow = window || {}; // @ts-ignore @@ -42,7 +42,7 @@ const routes: Routes = [ }, { path: 'about', - loadChildren: () => import('./components/about/about.module').then(m => m.AboutModule), + loadChildren: () => import('@components/about/about.module').then(m => m.AboutModule), }, { path: 'blocks/:page', @@ -58,40 +58,40 @@ const routes: Routes = [ }, { path: 'terms-of-service', - loadChildren: () => import('./components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule), + loadChildren: () => import('@components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule), }, { path: 'privacy-policy', - loadChildren: () => import('./components/privacy-policy/privacy-policy.module').then(m => m.PrivacyPolicyModule), + loadChildren: () => import('@components/privacy-policy/privacy-policy.module').then(m => m.PrivacyPolicyModule), }, { path: 'trademark-policy', - loadChildren: () => import('./components/trademark-policy/trademark-policy.module').then(m => m.TrademarkModule), + loadChildren: () => import('@components/trademark-policy/trademark-policy.module').then(m => m.TrademarkModule), }, { path: 'tx', component: StartComponent, data: { preload: true, networkSpecific: true }, - loadChildren: () => import('./components/transaction/transaction.module').then(m => m.TransactionModule), + loadChildren: () => import('@components/transaction/transaction.module').then(m => m.TransactionModule), }, { path: 'block', component: StartComponent, data: { preload: true, networkSpecific: true }, - loadChildren: () => import('./components/block/block.module').then(m => m.BlockModule), + loadChildren: () => import('@components/block/block.module').then(m => m.BlockModule), }, { path: 'docs', - loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule), + loadChildren: () => import('@app/docs/docs.module').then(m => m.DocsModule), data: { preload: true }, }, { path: 'api', - loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) + loadChildren: () => import('@app/docs/docs.module').then(m => m.DocsModule) }, { path: 'lightning', - loadChildren: () => import('./lightning/lightning.module').then(m => m.LightningModule), + loadChildren: () => import('@app/lightning/lightning.module').then(m => m.LightningModule), data: { preload: browserWindowEnv && browserWindowEnv.LIGHTNING === true, networks: ['bitcoin'] }, }, { diff --git a/frontend/src/app/previews.module.ts b/frontend/src/app/previews.module.ts index 95124f232..42ea7ac22 100644 --- a/frontend/src/app/previews.module.ts +++ b/frontend/src/app/previews.module.ts @@ -1,15 +1,15 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { SharedModule } from './shared/shared.module'; +import { SharedModule } from '@app/shared/shared.module'; import { RouterModule } from '@angular/router'; -import { GraphsModule } from './graphs/graphs.module'; -import { PreviewsRoutingModule } from './previews.routing.module'; -import { TransactionPreviewComponent } from './components/transaction/transaction-preview.component'; -import { BlockPreviewComponent } from './components/block/block-preview.component'; -import { AddressPreviewComponent } from './components/address/address-preview.component'; -import { PoolPreviewComponent } from './components/pool/pool-preview.component'; -import { MasterPagePreviewComponent } from './components/master-page-preview/master-page-preview.component'; -import { TxBowtieModule } from './components/tx-bowtie-graph/tx-bowtie.module'; +import { GraphsModule } from '@app/graphs/graphs.module'; +import { PreviewsRoutingModule } from '@app/previews.routing.module'; +import { TransactionPreviewComponent } from '@components/transaction/transaction-preview.component'; +import { BlockPreviewComponent } from '@components/block/block-preview.component'; +import { AddressPreviewComponent } from '@components/address/address-preview.component'; +import { PoolPreviewComponent } from '@components/pool/pool-preview.component'; +import { MasterPagePreviewComponent } from '@components/master-page-preview/master-page-preview.component'; +import { TxBowtieModule } from '@components/tx-bowtie-graph/tx-bowtie.module'; @NgModule({ declarations: [ TransactionPreviewComponent, diff --git a/frontend/src/app/previews.routing.module.ts b/frontend/src/app/previews.routing.module.ts index 6ac44a370..92ea113b8 100644 --- a/frontend/src/app/previews.routing.module.ts +++ b/frontend/src/app/previews.routing.module.ts @@ -1,10 +1,10 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { TransactionPreviewComponent } from './components/transaction/transaction-preview.component'; -import { BlockPreviewComponent } from './components/block/block-preview.component'; -import { AddressPreviewComponent } from './components/address/address-preview.component'; -import { PoolPreviewComponent } from './components/pool/pool-preview.component'; -import { MasterPagePreviewComponent } from './components/master-page-preview/master-page-preview.component'; +import { TransactionPreviewComponent } from '@components/transaction/transaction-preview.component'; +import { BlockPreviewComponent } from '@components/block/block-preview.component'; +import { AddressPreviewComponent } from '@components/address/address-preview.component'; +import { PoolPreviewComponent } from '@components/pool/pool-preview.component'; +import { MasterPagePreviewComponent } from '@components/master-page-preview/master-page-preview.component'; const routes: Routes = [ { @@ -31,7 +31,7 @@ const routes: Routes = [ }, { path: 'lightning', - loadChildren: () => import('./lightning/lightning-previews.module').then(m => m.LightningPreviewsModule), + loadChildren: () => import('@app/lightning/lightning-previews.module').then(m => m.LightningPreviewsModule), data: { preload: true }, }, ], diff --git a/frontend/src/app/route-guards.ts b/frontend/src/app/route-guards.ts index 7ed44176a..780e997db 100644 --- a/frontend/src/app/route-guards.ts +++ b/frontend/src/app/route-guards.ts @@ -1,6 +1,6 @@ import { Injectable, inject } from '@angular/core'; import { CanMatchFn, Route, Router, UrlSegment } from '@angular/router'; -import { NavigationService } from './services/navigation.service'; +import { NavigationService } from '@app/services/navigation.service'; @Injectable({ providedIn: 'root' @@ -20,4 +20,4 @@ class GuardService { export const TrackerGuard: CanMatchFn = (route: Route, segments: UrlSegment[]): boolean => { return inject(GuardService).trackerGuard(route, segments); -}; \ No newline at end of file +}; diff --git a/frontend/src/app/services/api.service.ts b/frontend/src/app/services/api.service.ts index fa52ec707..08251ddae 100644 --- a/frontend/src/app/services/api.service.ts +++ b/frontend/src/app/services/api.service.ts @@ -1,14 +1,14 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http'; import { CpfpInfo, OptimizedMempoolStats, AddressInformation, LiquidPegs, ITranslators, PoolStat, BlockExtended, TransactionStripped, RewardStats, AuditScore, BlockSizesAndWeights, - RbfTree, BlockAudit, CurrentPegs, AuditStatus, FederationAddress, FederationUtxo, RecentPeg, PegsVolume, AccelerationInfo, TestMempoolAcceptResult } from '../interfaces/node-api.interface'; + RbfTree, BlockAudit, CurrentPegs, AuditStatus, FederationAddress, FederationUtxo, RecentPeg, PegsVolume, AccelerationInfo, TestMempoolAcceptResult, WalletAddress, SubmitPackageResult } from '../interfaces/node-api.interface'; import { BehaviorSubject, Observable, catchError, filter, map, of, shareReplay, take, tap } from 'rxjs'; -import { StateService } from './state.service'; -import { Transaction } from '../interfaces/electrs.interface'; -import { Conversion } from './price.service'; -import { StorageService } from './storage.service'; -import { WebsocketResponse } from '../interfaces/websocket.interface'; -import { TxAuditStatus } from '../components/transaction/transaction.component'; +import { StateService } from '@app/services/state.service'; +import { Transaction } from '@interfaces/electrs.interface'; +import { Conversion } from '@app/services/price.service'; +import { StorageService } from '@app/services/storage.service'; +import { WebsocketResponse } from '@interfaces/websocket.interface'; +import { TxAuditStatus } from '@components/transaction/transaction.component'; @Injectable({ providedIn: 'root' @@ -244,6 +244,19 @@ export class ApiService { return this.httpClient.post(this.apiBaseUrl + this.apiBasePath + `/api/txs/test${maxfeerate != null ? '?maxfeerate=' + maxfeerate.toFixed(8) : ''}`, rawTxs); } + submitPackage$(rawTxs: string[], maxfeerate?: number, maxburnamount?: number): Observable { + const queryParams = []; + + if (maxfeerate) { + queryParams.push(`maxfeerate=${maxfeerate}`); + } + + if (maxburnamount) { + queryParams.push(`maxburnamount=${maxburnamount}`); + } + return this.httpClient.post(this.apiBaseUrl + this.apiBasePath + '/api/v1/txs/package' + (queryParams.length > 0 ? `?${queryParams.join('&')}` : ''), rawTxs); + } + getTransactionStatus$(txid: string): Observable { return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/tx/' + txid + '/status'); } @@ -504,6 +517,12 @@ export class ApiService { ); } + getWallet$(walletName: string): Observable> { + return this.httpClient.get>( + this.apiBaseUrl + this.apiBasePath + `/api/v1/wallet/${walletName}` + ); + } + getAccelerationsByPool$(slug: string): Observable { return this.httpClient.get( this.apiBaseUrl + this.apiBasePath + `/api/v1/accelerations/pool/${slug}` diff --git a/frontend/src/app/services/assets.service.ts b/frontend/src/app/services/assets.service.ts index 9c6b5dba0..bb8756bd7 100644 --- a/frontend/src/app/services/assets.service.ts +++ b/frontend/src/app/services/assets.service.ts @@ -2,9 +2,9 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { map, shareReplay, switchMap } from 'rxjs/operators'; -import { StateService } from './state.service'; -import { environment } from '../../../src/environments/environment'; -import { AssetExtended } from '../interfaces/electrs.interface'; +import { StateService } from '@app/services/state.service'; +import { environment } from '@environments/environment'; +import { AssetExtended } from '@interfaces/electrs.interface'; @Injectable({ providedIn: 'root' diff --git a/frontend/src/app/services/auth.service.ts b/frontend/src/app/services/auth.service.ts index 100ddbdc4..db910779e 100644 --- a/frontend/src/app/services/auth.service.ts +++ b/frontend/src/app/services/auth.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; import { catchError, map, Observable, of, ReplaySubject, switchMap, tap } from 'rxjs'; -import { ServicesApiServices } from './services-api.service'; +import { ServicesApiServices } from '@app/services/services-api.service'; export interface IAuth { token: string; diff --git a/frontend/src/app/services/cache.service.ts b/frontend/src/app/services/cache.service.ts index f15154b46..246008043 100644 --- a/frontend/src/app/services/cache.service.ts +++ b/frontend/src/app/services/cache.service.ts @@ -1,9 +1,9 @@ import { Injectable } from '@angular/core'; import { firstValueFrom, Subject, Subscription} from 'rxjs'; -import { Transaction } from '../interfaces/electrs.interface'; -import { BlockExtended } from '../interfaces/node-api.interface'; -import { StateService } from './state.service'; -import { ApiService } from './api.service'; +import { Transaction } from '@interfaces/electrs.interface'; +import { BlockExtended } from '@interfaces/node-api.interface'; +import { StateService } from '@app/services/state.service'; +import { ApiService } from '@app/services/api.service'; const BLOCK_CACHE_SIZE = 500; const KEEP_RECENT_BLOCKS = 50; @@ -133,4 +133,4 @@ export class CacheService { getCachedBlock(height) { return this.blockCache[height]; } -} \ No newline at end of file +} diff --git a/frontend/src/app/services/electrs-api.service.ts b/frontend/src/app/services/electrs-api.service.ts index 8e991782b..3cd5b5abd 100644 --- a/frontend/src/app/services/electrs-api.service.ts +++ b/frontend/src/app/services/electrs-api.service.ts @@ -2,9 +2,9 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpParams } from '@angular/common/http'; import { BehaviorSubject, Observable, catchError, filter, from, of, shareReplay, switchMap, take, tap } from 'rxjs'; import { Transaction, Address, Outspend, Recent, Asset, ScriptHash, AddressTxSummary, Utxo } from '../interfaces/electrs.interface'; -import { StateService } from './state.service'; -import { BlockExtended } from '../interfaces/node-api.interface'; -import { calcScriptHash$ } from '../bitcoin.utils'; +import { StateService } from '@app/services/state.service'; +import { BlockExtended } from '@interfaces/node-api.interface'; +import { calcScriptHash$ } from '@app/bitcoin.utils'; @Injectable({ providedIn: 'root' @@ -107,6 +107,10 @@ export class ElectrsApiService { return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/block-height/' + height, {responseType: 'text'}); } + getBlockTxId$(hash: string, index: number): Observable { + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/block/' + hash + '/txid/' + index, { responseType: 'text' }); + } + getAddress$(address: string): Observable
{ return this.httpClient.get
(this.apiBaseUrl + this.apiBasePath + '/api/address/' + address); } @@ -138,6 +142,14 @@ export class ElectrsApiService { return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/address/' + address + '/txs', { params }); } + getAddressesTransactions$(addresses: string[], txid?: string): Observable { + let params = new HttpParams(); + if (txid) { + params = params.append('after_txid', txid); + } + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + `/api/addresses/txs?addresses=${addresses.join(',')}`, { params }); + } + getAddressSummary$(address: string, txid?: string): Observable { let params = new HttpParams(); if (txid) { @@ -146,6 +158,14 @@ export class ElectrsApiService { return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/address/' + address + '/txs/summary', { params }); } + getAddressesSummary$(addresses: string[], txid?: string): Observable { + let params = new HttpParams(); + if (txid) { + params = params.append('after_txid', txid); + } + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + `/api/addresses/txs/summary?addresses=${addresses.join(',')}`, { params }); + } + getScriptHashTransactions$(script: string, txid?: string): Observable { let params = new HttpParams(); if (txid) { @@ -156,6 +176,16 @@ export class ElectrsApiService { ); } + getScriptHashesTransactions$(scripts: string[], txid?: string): Observable { + let params = new HttpParams(); + if (txid) { + params = params.append('after_txid', txid); + } + return from(Promise.all(scripts.map(script => calcScriptHash$(script)))).pipe( + switchMap(scriptHashes => this.httpClient.get(this.apiBaseUrl + this.apiBasePath + `/api/scripthashes/txs?scripthashes=${scriptHashes.join(',')}`, { params })), + ); + } + getScriptHashSummary$(script: string, txid?: string): Observable { let params = new HttpParams(); if (txid) { @@ -176,6 +206,16 @@ export class ElectrsApiService { ); } + getScriptHashesSummary$(scripts: string[], txid?: string): Observable { + let params = new HttpParams(); + if (txid) { + params = params.append('after_txid', txid); + } + return from(Promise.all(scripts.map(script => calcScriptHash$(script)))).pipe( + switchMap(scriptHashes => this.httpClient.get(this.apiBaseUrl + this.apiBasePath + `/api/scripthashes/txs/summary?scripthashes=${scriptHashes.join(',')}`, { params })), + ); + } + getAsset$(assetId: string): Observable { return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/asset/' + assetId); } diff --git a/frontend/src/app/services/enterprise.service.ts b/frontend/src/app/services/enterprise.service.ts index 4ea890f1f..d7695b94f 100644 --- a/frontend/src/app/services/enterprise.service.ts +++ b/frontend/src/app/services/enterprise.service.ts @@ -1,8 +1,8 @@ import { DOCUMENT } from '@angular/common'; import { Inject, Injectable } from '@angular/core'; -import { ApiService } from './api.service'; -import { SeoService } from './seo.service'; -import { StateService } from './state.service'; +import { ApiService } from '@app/services/api.service'; +import { SeoService } from '@app/services/seo.service'; +import { StateService } from '@app/services/state.service'; import { ActivatedRoute } from '@angular/router'; import { BehaviorSubject } from 'rxjs'; diff --git a/frontend/src/app/services/eta.service.ts b/frontend/src/app/services/eta.service.ts index 6834237b6..cf7719327 100644 --- a/frontend/src/app/services/eta.service.ts +++ b/frontend/src/app/services/eta.service.ts @@ -1,11 +1,11 @@ import { Injectable } from '@angular/core'; -import { AccelerationPosition, CpfpInfo, DifficultyAdjustment, MempoolPosition, SinglePoolStats } from '../interfaces/node-api.interface'; -import { StateService } from './state.service'; -import { MempoolBlock } from '../interfaces/websocket.interface'; -import { Transaction } from '../interfaces/electrs.interface'; -import { MiningService, MiningStats } from './mining.service'; -import { getUnacceleratedFeeRate } from '../shared/transaction.utils'; -import { AccelerationEstimate } from '../components/accelerate-checkout/accelerate-checkout.component'; +import { AccelerationPosition, CpfpInfo, DifficultyAdjustment, MempoolPosition, SinglePoolStats } from '@interfaces/node-api.interface'; +import { StateService } from '@app/services/state.service'; +import { MempoolBlock } from '@interfaces/websocket.interface'; +import { Transaction } from '@interfaces/electrs.interface'; +import { MiningService, MiningStats } from '@app/services/mining.service'; +import { getUnacceleratedFeeRate } from '@app/shared/transaction.utils'; +import { AccelerationEstimate } from '@components/accelerate-checkout/accelerate-checkout.component'; import { Observable, combineLatest, map, of, share, shareReplay, tap } from 'rxjs'; export interface ETA { diff --git a/frontend/src/app/services/language.service.ts b/frontend/src/app/services/language.service.ts index 58ba94a00..b0d2f247c 100644 --- a/frontend/src/app/services/language.service.ts +++ b/frontend/src/app/services/language.service.ts @@ -1,6 +1,6 @@ import { DOCUMENT, getLocaleId } from '@angular/common'; import { LOCALE_ID, Inject, Injectable } from '@angular/core'; -import { languages } from '../app.constants'; +import { languages } from '@app/app.constants'; @Injectable({ providedIn: 'root' diff --git a/frontend/src/app/services/mining.service.ts b/frontend/src/app/services/mining.service.ts index 0dffa0b66..87e9374ea 100644 --- a/frontend/src/app/services/mining.service.ts +++ b/frontend/src/app/services/mining.service.ts @@ -1,10 +1,10 @@ import { Injectable } from '@angular/core'; import { Observable, of } from 'rxjs'; import { map, tap } from 'rxjs/operators'; -import { PoolsStats, SinglePoolStats } from '../interfaces/node-api.interface'; -import { ApiService } from '../services/api.service'; -import { StateService } from './state.service'; -import { StorageService } from './storage.service'; +import { PoolsStats, SinglePoolStats } from '@interfaces/node-api.interface'; +import { ApiService } from '@app/services/api.service'; +import { StateService } from '@app/services/state.service'; +import { StorageService } from '@app/services/storage.service'; export interface MiningUnits { hashrateDivider: number; diff --git a/frontend/src/app/services/navigation.service.ts b/frontend/src/app/services/navigation.service.ts index 2a3215121..c1b32739a 100644 --- a/frontend/src/app/services/navigation.service.ts +++ b/frontend/src/app/services/navigation.service.ts @@ -2,8 +2,8 @@ import { Injectable } from '@angular/core'; import { Router, NavigationEnd, ActivatedRouteSnapshot } from '@angular/router'; import { BehaviorSubject } from 'rxjs'; import { filter, map } from 'rxjs/operators'; -import { StateService } from './state.service'; -import { RelativeUrlPipe } from '../shared/pipes/relative-url/relative-url.pipe'; +import { StateService } from '@app/services/state.service'; +import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe'; @Injectable({ providedIn: 'root' diff --git a/frontend/src/app/services/opengraph.service.ts b/frontend/src/app/services/opengraph.service.ts index 5e429ed70..e969dd07a 100644 --- a/frontend/src/app/services/opengraph.service.ts +++ b/frontend/src/app/services/opengraph.service.ts @@ -3,8 +3,8 @@ import { Meta } from '@angular/platform-browser'; import { Router, ActivatedRoute, NavigationEnd } from '@angular/router'; import { filter, map, switchMap } from 'rxjs/operators'; import { combineLatest } from 'rxjs'; -import { StateService } from './state.service'; -import { LanguageService } from './language.service'; +import { StateService } from '@app/services/state.service'; +import { LanguageService } from '@app/services/language.service'; @Injectable({ providedIn: 'root' diff --git a/frontend/src/app/services/ord-api.service.ts b/frontend/src/app/services/ord-api.service.ts new file mode 100644 index 000000000..ae0076906 --- /dev/null +++ b/frontend/src/app/services/ord-api.service.ts @@ -0,0 +1,100 @@ +import { Injectable } from '@angular/core'; +import { catchError, forkJoin, map, Observable, of, switchMap, tap } from 'rxjs'; +import { Inscription } from '@app/shared/ord/inscription.utils'; +import { Transaction } from '@interfaces/electrs.interface'; +import { getNextInscriptionMark, hexToBytes, extractInscriptionData } from '@app/shared/ord/inscription.utils'; +import { decipherRunestone, Runestone, Etching, UNCOMMON_GOODS } from '@app/shared/ord/rune.utils'; +import { ElectrsApiService } from '@app/services/electrs-api.service'; + + +@Injectable({ + providedIn: 'root' +}) +export class OrdApiService { + + constructor( + private electrsApiService: ElectrsApiService, + ) { } + + decodeRunestone$(tx: Transaction): Observable<{ runestone: Runestone, runeInfo: { [id: string]: { etching: Etching; txid: string; } } }> { + const runestone = decipherRunestone(tx); + const runeInfo: { [id: string]: { etching: Etching; txid: string; } } = {}; + + if (runestone) { + const runesToFetch: Set = new Set(); + + if (runestone.mint) { + runesToFetch.add(runestone.mint.toString()); + } + + if (runestone.edicts.length) { + runestone.edicts.forEach(edict => { + runesToFetch.add(edict.id.toString()); + }); + } + + if (runesToFetch.size) { + const runeEtchingObservables = Array.from(runesToFetch).map(runeId => this.getEtchingFromRuneId$(runeId)); + + return forkJoin(runeEtchingObservables).pipe( + map((etchings) => { + etchings.forEach((el) => { + if (el) { + runeInfo[el.runeId] = { etching: el.etching, txid: el.txid }; + } + }); + return { runestone: runestone, runeInfo }; + }) + ); + } + return of({ runestone: runestone, runeInfo }); + } else { + return of({ runestone: null, runeInfo: {} }); + } + } + + // Get etching from runeId by looking up the transaction that etched the rune + getEtchingFromRuneId$(runeId: string): Observable<{ runeId: string; etching: Etching; txid: string; }> { + if (runeId === '1:0') { + return of({ runeId, etching: UNCOMMON_GOODS, txid: '0000000000000000000000000000000000000000000000000000000000000000' }); + } else { + const [blockNumber, txIndex] = runeId.split(':'); + return this.electrsApiService.getBlockHashFromHeight$(parseInt(blockNumber)).pipe( + switchMap(blockHash => this.electrsApiService.getBlockTxId$(blockHash, parseInt(txIndex))), + switchMap(txId => this.electrsApiService.getTransaction$(txId)), + switchMap(tx => { + const runestone = decipherRunestone(tx); + if (runestone) { + const etching = runestone.etching; + if (etching) { + return of({ runeId, etching, txid: tx.txid }); + } + } + return of(null); + }), + catchError(() => of(null)) + ); + } + } + + decodeInscriptions(witness: string): Inscription[] | null { + + const inscriptions: Inscription[] = []; + const raw = hexToBytes(witness); + let startPosition = 0; + + while (true) { + const pointer = getNextInscriptionMark(raw, startPosition); + if (pointer === -1) break; + + const inscription = extractInscriptionData(raw, pointer); + if (inscription) { + inscriptions.push(inscription); + } + + startPosition = pointer; + } + + return inscriptions; + } +} diff --git a/frontend/src/app/services/preload.service.ts b/frontend/src/app/services/preload.service.ts index 386d4deb4..2fce9b98c 100644 --- a/frontend/src/app/services/preload.service.ts +++ b/frontend/src/app/services/preload.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { ElectrsApiService } from '../services/electrs-api.service'; import { Subject, debounceTime, switchMap } from 'rxjs'; -import { ApiService } from './api.service'; +import { ApiService } from '@app/services/api.service'; @Injectable({ providedIn: 'root' diff --git a/frontend/src/app/services/price.service.ts b/frontend/src/app/services/price.service.ts index c342796e0..e5a0c86c8 100644 --- a/frontend/src/app/services/price.service.ts +++ b/frontend/src/app/services/price.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { map, Observable, of, share, shareReplay, tap } from 'rxjs'; -import { ApiService } from './api.service'; -import { StateService } from './state.service'; +import { ApiService } from '@app/services/api.service'; +import { StateService } from '@app/services/state.service'; // nodejs backend interfaces export interface ApiPrice { @@ -320,4 +320,4 @@ export class PriceService { return prices; })); } -} \ No newline at end of file +} diff --git a/frontend/src/app/services/seo.service.ts b/frontend/src/app/services/seo.service.ts index af12e8b47..e5ede4db3 100644 --- a/frontend/src/app/services/seo.service.ts +++ b/frontend/src/app/services/seo.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { Title, Meta } from '@angular/platform-browser'; import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; import { filter, map, switchMap } from 'rxjs'; -import { StateService } from './state.service'; +import { StateService } from '@app/services/state.service'; @Injectable({ providedIn: 'root' diff --git a/frontend/src/app/services/services-api.service.ts b/frontend/src/app/services/services-api.service.ts index c87044781..2b0f884ff 100644 --- a/frontend/src/app/services/services-api.service.ts +++ b/frontend/src/app/services/services-api.service.ts @@ -1,21 +1,20 @@ import { Router, NavigationStart } from '@angular/router'; import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; -import { StateService } from './state.service'; -import { StorageService } from './storage.service'; -import { MenuGroup } from '../interfaces/services.interface'; +import { StateService } from '@app/services/state.service'; +import { StorageService } from '@app/services/storage.service'; +import { MenuGroup } from '@interfaces/services.interface'; import { Observable, of, ReplaySubject, tap, catchError, share, filter, switchMap, map } from 'rxjs'; -import { IBackendInfo } from '../interfaces/websocket.interface'; -import { Acceleration, AccelerationHistoryParams } from '../interfaces/node-api.interface'; -import { AccelerationStats } from '../components/acceleration/acceleration-stats/acceleration-stats.component'; +import { IBackendInfo } from '@interfaces/websocket.interface'; +import { Acceleration, AccelerationHistoryParams } from '@interfaces/node-api.interface'; +import { AccelerationStats } from '@components/acceleration/acceleration-stats/acceleration-stats.component'; -export type ProductType = 'enterprise' | 'community' | 'mining_pool' | 'custom'; export interface IUser { username: string; email: string | null; passwordIsSet: boolean; snsId: string; - type: ProductType; + type: 'enterprise' | 'community' | 'mining_pool'; subscription_tag: string; status: 'pending' | 'verified' | 'disabled'; features: string | null; @@ -136,16 +135,16 @@ export class ServicesApiServices { return this.httpClient.post(`${this.stateService.env.SERVICES_API}/accelerator/accelerate`, { txInput: txInput, userBid: userBid, accelerationUUID: accelerationUUID }); } - accelerateWithCashApp$(txInput: string, token: string, cashtag: string, referenceId: string, accelerationUUID: string) { - return this.httpClient.post(`${this.stateService.env.SERVICES_API}/accelerator/accelerate/cashapp`, { txInput: txInput, token: token, cashtag: cashtag, referenceId: referenceId, accelerationUUID: accelerationUUID }); + accelerateWithCashApp$(txInput: string, token: string, cashtag: string, referenceId: string, accelerationUUID: string, userApprovedUSD: number) { + return this.httpClient.post(`${this.stateService.env.SERVICES_API}/accelerator/accelerate/cashapp`, { txInput: txInput, token: token, cashtag: cashtag, referenceId: referenceId, accelerationUUID: accelerationUUID, userApprovedUSD: userApprovedUSD }); } - accelerateWithApplePay$(txInput: string, token: string, cardTag: string, referenceId: string, accelerationUUID: string) { - return this.httpClient.post(`${this.stateService.env.SERVICES_API}/accelerator/accelerate/applePay`, { txInput: txInput, cardTag: cardTag, token: token, referenceId: referenceId, accelerationUUID: accelerationUUID }); + accelerateWithApplePay$(txInput: string, token: string, cardTag: string, referenceId: string, accelerationUUID: string, userApprovedUSD: number) { + return this.httpClient.post(`${this.stateService.env.SERVICES_API}/accelerator/accelerate/applePay`, { txInput: txInput, cardTag: cardTag, token: token, referenceId: referenceId, accelerationUUID: accelerationUUID, userApprovedUSD: userApprovedUSD }); } - accelerateWithGooglePay$(txInput: string, token: string, cardTag: string, referenceId: string, accelerationUUID: string) { - return this.httpClient.post(`${this.stateService.env.SERVICES_API}/accelerator/accelerate/googlePay`, { txInput: txInput, cardTag: cardTag, token: token, referenceId: referenceId, accelerationUUID: accelerationUUID }); + accelerateWithGooglePay$(txInput: string, token: string, cardTag: string, referenceId: string, accelerationUUID: string, userApprovedUSD: number) { + return this.httpClient.post(`${this.stateService.env.SERVICES_API}/accelerator/accelerate/googlePay`, { txInput: txInput, cardTag: cardTag, token: token, referenceId: referenceId, accelerationUUID: accelerationUUID, userApprovedUSD: userApprovedUSD }); } getAccelerations$(): Observable { diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index 13ffc7fc5..8dd17cf75 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -1,14 +1,14 @@ import { Inject, Injectable, PLATFORM_ID, LOCALE_ID } from '@angular/core'; import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable } from 'rxjs'; -import { Transaction } from '../interfaces/electrs.interface'; -import { AccelerationDelta, HealthCheckHost, IBackendInfo, MempoolBlock, MempoolBlockUpdate, MempoolInfo, Recommendedfees, ReplacedTransaction, ReplacementInfo, isMempoolState } from '../interfaces/websocket.interface'; -import { Acceleration, AccelerationPosition, BlockExtended, CpfpInfo, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, RbfTree, TransactionStripped } from '../interfaces/node-api.interface'; +import { AddressTxSummary, Transaction } from '@interfaces/electrs.interface'; +import { AccelerationDelta, HealthCheckHost, IBackendInfo, MempoolBlock, MempoolBlockUpdate, MempoolInfo, Recommendedfees, ReplacedTransaction, ReplacementInfo, isMempoolState } from '@interfaces/websocket.interface'; +import { Acceleration, AccelerationPosition, BlockExtended, CpfpInfo, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, RbfTree, TransactionStripped } from '@interfaces/node-api.interface'; import { Router, NavigationStart } from '@angular/router'; import { isPlatformBrowser } from '@angular/common'; import { filter, map, scan, share, shareReplay } from 'rxjs/operators'; -import { StorageService } from './storage.service'; -import { hasTouchScreen } from '../shared/pipes/bytes-pipe/utils'; -import { ActiveFilter } from '../shared/filters.utils'; +import { StorageService } from '@app/services/storage.service'; +import { hasTouchScreen } from '@app/shared/pipes/bytes-pipe/utils'; +import { ActiveFilter } from '@app/shared/filters.utils'; export interface MarkBlockState { blockHeight?: number; @@ -78,6 +78,7 @@ export interface Env { PACKAGE_JSON_VERSION_MEMPOOL_SPACE?: string; SERVICES_API?: string; customize?: Customization; + PROD_DOMAINS: string[]; } const defaultEnv: Env = { @@ -113,6 +114,7 @@ const defaultEnv: Env = { 'PUBLIC_ACCELERATIONS': false, 'ADDITIONAL_CURRENCIES': false, 'SERVICES_API': 'https://mempool.space/api/v1/services', + 'PROD_DOMAINS': [], }; @Injectable({ @@ -159,6 +161,7 @@ export class StateService { mempoolRemovedTransactions$ = new Subject(); multiAddressTransactions$ = new Subject<{ [address: string]: { mempool: Transaction[], confirmed: Transaction[], removed: Transaction[] }}>(); blockTransactions$ = new Subject(); + walletTransactions$ = new Subject(); isLoadingWebSocket$ = new ReplaySubject(1); isLoadingMempool$ = new BehaviorSubject(true); vbytesPerSecond$ = new ReplaySubject(1); @@ -205,6 +208,10 @@ export class StateService { const browserWindow = window || {}; // @ts-ignore const browserWindowEnv = browserWindow.__env || {}; + if (browserWindowEnv.PROD_DOMAINS && typeof(browserWindowEnv.PROD_DOMAINS) === 'string') { + browserWindowEnv.PROD_DOMAINS = browserWindowEnv.PROD_DOMAINS.split(','); + } + this.env = Object.assign(defaultEnv, browserWindowEnv); if (defaultEnv.BASE_MODULE !== 'mempool') { diff --git a/frontend/src/app/services/theme.service.ts b/frontend/src/app/services/theme.service.ts index 0a0fdc505..6ed9c1027 100644 --- a/frontend/src/app/services/theme.service.ts +++ b/frontend/src/app/services/theme.service.ts @@ -1,8 +1,8 @@ import { Injectable } from '@angular/core'; import { Subject } from 'rxjs'; -import { defaultMempoolFeeColors, contrastMempoolFeeColors } from '../app.constants'; -import { StorageService } from './storage.service'; -import { StateService } from './state.service'; +import { defaultMempoolFeeColors, contrastMempoolFeeColors } from '@app/app.constants'; +import { StorageService } from '@app/services/storage.service'; +import { StateService } from '@app/services/state.service'; @Injectable({ providedIn: 'root' diff --git a/frontend/src/app/services/time.service.ts b/frontend/src/app/services/time.service.ts index 6f7978774..e4804deab 100644 --- a/frontend/src/app/services/time.service.ts +++ b/frontend/src/app/services/time.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { DatePipe } from '@angular/common'; -import { dates } from '../shared/i18n/dates'; +import { dates } from '@app/shared/i18n/dates'; const intervals = { year: 31536000, diff --git a/frontend/src/app/services/websocket.service.ts b/frontend/src/app/services/websocket.service.ts index 39e9d1af3..5ec13c03f 100644 --- a/frontend/src/app/services/websocket.service.ts +++ b/frontend/src/app/services/websocket.service.ts @@ -1,14 +1,14 @@ import { Injectable } from '@angular/core'; import { webSocket, WebSocketSubject } from 'rxjs/webSocket'; -import { WebsocketResponse } from '../interfaces/websocket.interface'; -import { StateService } from './state.service'; -import { Transaction } from '../interfaces/electrs.interface'; +import { WebsocketResponse } from '@interfaces/websocket.interface'; +import { StateService } from '@app/services/state.service'; +import { Transaction } from '@interfaces/electrs.interface'; import { firstValueFrom, Subscription } from 'rxjs'; -import { ApiService } from './api.service'; +import { ApiService } from '@app/services/api.service'; import { take } from 'rxjs/operators'; import { TransferState, makeStateKey } from '@angular/core'; -import { CacheService } from './cache.service'; -import { uncompressDeltaChange, uncompressTx } from '../shared/common.utils'; +import { CacheService } from '@app/services/cache.service'; +import { uncompressDeltaChange, uncompressTx } from '@app/shared/common.utils'; const OFFLINE_RETRY_AFTER_MS = 2000; const OFFLINE_PING_CHECK_AFTER_MS = 30000; @@ -34,6 +34,8 @@ export class WebsocketService { private isTrackingAddress: string | false = false; private isTrackingAddresses: string[] | false = false; private isTrackingAccelerations: boolean = false; + private isTrackingWallet: boolean = false; + private trackingWalletName: string; private trackingMempoolBlock: number; private stoppingTrackMempoolBlock: any | null = null; private latestGitCommit = ''; @@ -137,6 +139,9 @@ export class WebsocketService { if (this.isTrackingAccelerations) { this.startTrackAccelerations(); } + if (this.isTrackingWallet) { + this.startTrackingWallet(this.trackingWalletName); + } this.stateService.connectionState$.next(2); } @@ -196,6 +201,18 @@ export class WebsocketService { this.isTrackingAddresses = false; } + startTrackingWallet(walletName: string) { + this.websocketSubject.next({ 'track-wallet': walletName }); + this.isTrackingWallet = true; + this.trackingWalletName = walletName; + } + + stopTrackingWallet() { + this.websocketSubject.next({ 'track-wallet': 'stop' }); + this.isTrackingWallet = false; + this.trackingWalletName = ''; + } + startTrackAsset(asset: string) { this.websocketSubject.next({ 'track-asset': asset }); } @@ -452,6 +469,10 @@ export class WebsocketService { } } + if (response['wallet-transactions']) { + this.stateService.walletTransactions$.next(response['wallet-transactions']); + } + if (response['accelerations']) { if (response['accelerations'].accelerations) { this.stateService.accelerations$.next({ diff --git a/frontend/src/app/shared/address-utils.ts b/frontend/src/app/shared/address-utils.ts index 59c85014b..0a7f2df02 100644 --- a/frontend/src/app/shared/address-utils.ts +++ b/frontend/src/app/shared/address-utils.ts @@ -1,7 +1,7 @@ import '@angular/localize/init'; -import { ScriptInfo } from './script.utils'; -import { Vin, Vout } from '../interfaces/electrs.interface'; -import { BECH32_CHARS_LW, BASE58_CHARS, HEX_CHARS } from './regex.utils'; +import { ScriptInfo } from '@app/shared/script.utils'; +import { Vin, Vout } from '@interfaces/electrs.interface'; +import { BECH32_CHARS_LW, BASE58_CHARS, HEX_CHARS } from '@app/shared/regex.utils'; export type AddressType = 'fee' | 'empty' @@ -217,4 +217,4 @@ export class AddressTypeInfo { this.isMultisig = { m: script.template['m'], n: script.template['n'] }; } } -} \ No newline at end of file +} diff --git a/frontend/src/app/shared/common.utils.ts b/frontend/src/app/shared/common.utils.ts index 5ccb369f6..f329b55e4 100644 --- a/frontend/src/app/shared/common.utils.ts +++ b/frontend/src/app/shared/common.utils.ts @@ -1,6 +1,7 @@ import { MempoolBlockDelta, MempoolBlockDeltaCompressed, MempoolDeltaChange, TransactionCompressed } from "../interfaces/websocket.interface"; -import { TransactionStripped } from "../interfaces/node-api.interface"; -import { AmountShortenerPipe } from "./pipes/amount-shortener.pipe"; +import { TransactionStripped } from "@interfaces/node-api.interface"; +import { AmountShortenerPipe } from "@app/shared/pipes/amount-shortener.pipe"; +import { Router, ActivatedRoute } from '@angular/router'; const amountShortenerPipe = new AmountShortenerPipe(); export function isMobile(): boolean { @@ -226,6 +227,29 @@ export function insecureRandomUUID(): string { return uuid.slice(0, -1); } +export function sleep$(ms: number): Promise { + return new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, ms); + }); +} + +export function handleDemoRedirect(route: ActivatedRoute, router: Router) { + route.queryParams + .subscribe(params => { + if (params.next) { + const path = ['/', '/acceleration', '/mining', '/lightning']; + const index = path.indexOf(params.next); + if (index >= 0) { + const nextPath = path[(index + 1) % path.length]; + setTimeout(() => { window.location.replace(`${params.next}?next=${nextPath}`) }, 15000); + } + } + } + ); +} + // https://stackoverflow.com/a/60467595 export function md5(inputString): string { var hc="0123456789abcdef"; diff --git a/frontend/src/app/shared/components/address-type/address-type.component.ts b/frontend/src/app/shared/components/address-type/address-type.component.ts index 1a2456c07..d0ae7a8f1 100644 --- a/frontend/src/app/shared/components/address-type/address-type.component.ts +++ b/frontend/src/app/shared/components/address-type/address-type.component.ts @@ -1,5 +1,5 @@ import { Component, Input } from '@angular/core'; -import { AddressTypeInfo } from '../../address-utils'; +import { AddressTypeInfo } from '@app/shared/address-utils'; @Component({ selector: 'app-address-type', diff --git a/frontend/src/app/shared/components/btc/btc.component.ts b/frontend/src/app/shared/components/btc/btc.component.ts index 4e62b07ff..a87be7a4f 100644 --- a/frontend/src/app/shared/components/btc/btc.component.ts +++ b/frontend/src/app/shared/components/btc/btc.component.ts @@ -1,6 +1,6 @@ import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; import { Subscription } from 'rxjs'; -import { StateService } from '../../../services/state.service'; +import { StateService } from '@app/services/state.service'; @Component({ selector: 'app-btc', diff --git a/frontend/src/app/shared/components/fee-rate/fee-rate.component.ts b/frontend/src/app/shared/components/fee-rate/fee-rate.component.ts index b1d143e7f..9a2565cec 100644 --- a/frontend/src/app/shared/components/fee-rate/fee-rate.component.ts +++ b/frontend/src/app/shared/components/fee-rate/fee-rate.component.ts @@ -1,6 +1,6 @@ import { Component, Input, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; -import { StateService } from '../../../services/state.service'; +import { StateService } from '@app/services/state.service'; @Component({ selector: 'app-fee-rate', diff --git a/frontend/src/app/shared/components/geolocation/geolocation.component.ts b/frontend/src/app/shared/components/geolocation/geolocation.component.ts index 1a498a1b2..2d9aa684b 100644 --- a/frontend/src/app/shared/components/geolocation/geolocation.component.ts +++ b/frontend/src/app/shared/components/geolocation/geolocation.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnChanges } from '@angular/core'; -import { convertRegion, getFlagEmoji } from '../../common.utils'; +import { convertRegion, getFlagEmoji } from '@app/shared/common.utils'; export interface GeolocationData { country: string; @@ -70,6 +70,12 @@ export class GeolocationComponent implements OnChanges { if (this.type === 'node') { const city = this.data.city ? this.data.city : ''; + // Handle city-states like Singapore or Hong Kong + if (city && city === this.data?.country) { + this.formattedLocation = `${this.data.country} ${getFlagEmoji(this.data.iso)}`; + return; + } + // City this.formattedLocation = `${city}`; diff --git a/frontend/src/app/shared/components/global-footer/global-footer.component.html b/frontend/src/app/shared/components/global-footer/global-footer.component.html index 6e0e46300..d82bb8062 100644 --- a/frontend/src/app/shared/components/global-footer/global-footer.component.html +++ b/frontend/src/app/shared/components/global-footer/global-footer.component.html @@ -5,7 +5,7 @@