Merge branch 'master' into natsoni/use-adjusted-time-avg

This commit is contained in:
softsimon
2024-12-21 22:10:04 +07:00
committed by GitHub
375 changed files with 6272 additions and 2623 deletions

View File

@@ -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'
@@ -18,6 +18,7 @@ export class ApiService {
private apiBasePath: string; // network path is /testnet, etc. or '' for mainnet
private requestCache = new Map<string, { subject: BehaviorSubject<any>, expiry: number }>;
public blockSummaryLoaded: { [hash: string]: boolean } = {};
public blockAuditLoaded: { [hash: string]: boolean } = {};
constructor(
@@ -244,6 +245,19 @@ export class ApiService {
return this.httpClient.post<TestMempoolAcceptResult[]>(this.apiBaseUrl + this.apiBasePath + `/api/txs/test${maxfeerate != null ? '?maxfeerate=' + maxfeerate.toFixed(8) : ''}`, rawTxs);
}
submitPackage$(rawTxs: string[], maxfeerate?: number, maxburnamount?: number): Observable<SubmitPackageResult> {
const queryParams = [];
if (maxfeerate) {
queryParams.push(`maxfeerate=${maxfeerate}`);
}
if (maxburnamount) {
queryParams.push(`maxburnamount=${maxburnamount}`);
}
return this.httpClient.post<SubmitPackageResult>(this.apiBaseUrl + this.apiBasePath + '/api/v1/txs/package' + (queryParams.length > 0 ? `?${queryParams.join('&')}` : ''), rawTxs);
}
getTransactionStatus$(txid: string): Observable<any> {
return this.httpClient.get<any>(this.apiBaseUrl + this.apiBasePath + '/api/tx/' + txid + '/status');
}
@@ -305,9 +319,14 @@ export class ApiService {
}
getStrippedBlockTransactions$(hash: string): Observable<TransactionStripped[]> {
this.setBlockSummaryLoaded(hash);
return this.httpClient.get<TransactionStripped[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/block/' + hash + '/summary');
}
getStrippedBlockTransaction$(hash: string, txid: string): Observable<TransactionStripped> {
return this.httpClient.get<TransactionStripped>(this.apiBaseUrl + this.apiBasePath + '/api/v1/block/' + hash + '/tx/' + txid + '/summary');
}
getDifficultyAdjustments$(interval: string | undefined): Observable<any> {
return this.httpClient.get<any[]>(
this.apiBaseUrl + this.apiBasePath + `/api/v1/mining/difficulty-adjustments` +
@@ -504,6 +523,12 @@ export class ApiService {
);
}
getWallet$(walletName: string): Observable<Record<string, WalletAddress>> {
return this.httpClient.get<Record<string, WalletAddress>>(
this.apiBaseUrl + this.apiBasePath + `/api/v1/wallet/${walletName}`
);
}
getAccelerationsByPool$(slug: string): Observable<AccelerationInfo[]> {
return this.httpClient.get<AccelerationInfo[]>(
this.apiBaseUrl + this.apiBasePath + `/api/v1/accelerations/pool/${slug}`
@@ -548,4 +573,12 @@ export class ApiService {
getBlockAuditLoaded(hash) {
return this.blockAuditLoaded[hash];
}
async setBlockSummaryLoaded(hash: string) {
this.blockSummaryLoaded[hash] = true;
}
getBlockSummaryLoaded(hash) {
return this.blockSummaryLoaded[hash];
}
}

View File

@@ -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'

View File

@@ -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;

View File

@@ -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];
}
}
}

View File

@@ -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'
@@ -142,6 +142,14 @@ export class ElectrsApiService {
return this.httpClient.get<Transaction[]>(this.apiBaseUrl + this.apiBasePath + '/api/address/' + address + '/txs', { params });
}
getAddressesTransactions$(addresses: string[], txid?: string): Observable<Transaction[]> {
let params = new HttpParams();
if (txid) {
params = params.append('after_txid', txid);
}
return this.httpClient.get<Transaction[]>(this.apiBaseUrl + this.apiBasePath + `/api/addresses/txs?addresses=${addresses.join(',')}`, { params });
}
getAddressSummary$(address: string, txid?: string): Observable<AddressTxSummary[]> {
let params = new HttpParams();
if (txid) {
@@ -150,6 +158,14 @@ export class ElectrsApiService {
return this.httpClient.get<AddressTxSummary[]>(this.apiBaseUrl + this.apiBasePath + '/api/address/' + address + '/txs/summary', { params });
}
getAddressesSummary$(addresses: string[], txid?: string): Observable<AddressTxSummary[]> {
let params = new HttpParams();
if (txid) {
params = params.append('after_txid', txid);
}
return this.httpClient.get<AddressTxSummary[]>(this.apiBaseUrl + this.apiBasePath + `/api/addresses/txs/summary?addresses=${addresses.join(',')}`, { params });
}
getScriptHashTransactions$(script: string, txid?: string): Observable<Transaction[]> {
let params = new HttpParams();
if (txid) {
@@ -160,6 +176,16 @@ export class ElectrsApiService {
);
}
getScriptHashesTransactions$(scripts: string[], txid?: string): Observable<Transaction[]> {
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<Transaction[]>(this.apiBaseUrl + this.apiBasePath + `/api/scripthashes/txs?scripthashes=${scriptHashes.join(',')}`, { params })),
);
}
getScriptHashSummary$(script: string, txid?: string): Observable<AddressTxSummary[]> {
let params = new HttpParams();
if (txid) {
@@ -180,6 +206,16 @@ export class ElectrsApiService {
);
}
getScriptHashesSummary$(scripts: string[], txid?: string): Observable<AddressTxSummary[]> {
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<AddressTxSummary[]>(this.apiBaseUrl + this.apiBasePath + `/api/scripthashes/txs/summary?scripthashes=${scriptHashes.join(',')}`, { params })),
);
}
getAsset$(assetId: string): Observable<Asset> {
return this.httpClient.get<Asset>(this.apiBaseUrl + this.apiBasePath + '/api/asset/' + assetId);
}

View File

@@ -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';

View File

@@ -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 {

View File

@@ -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'

View File

@@ -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;
@@ -13,6 +13,8 @@ export interface MiningUnits {
export interface MiningStats {
lastEstimatedHashrate: number;
lastEstimatedHashrate3d: number;
lastEstimatedHashrate1w: number;
blockCount: number;
totalEmptyBlock: number;
totalEmptyBlockRatio: string;
@@ -129,6 +131,8 @@ export class MiningService {
return {
share: parseFloat((poolStat.blockCount / stats.blockCount * 100).toFixed(2)),
lastEstimatedHashrate: poolStat.blockCount / stats.blockCount * stats.lastEstimatedHashrate / hashrateDivider,
lastEstimatedHashrate3d: poolStat.blockCount / stats.blockCount * stats.lastEstimatedHashrate3d / hashrateDivider,
lastEstimatedHashrate1w: poolStat.blockCount / stats.blockCount * stats.lastEstimatedHashrate1w / hashrateDivider,
emptyBlockRatio: (poolStat.emptyBlocks / poolStat.blockCount * 100).toFixed(2),
logo: `/resources/mining-pools/` + poolStat.slug + '.svg',
...poolStat
@@ -137,6 +141,8 @@ export class MiningService {
return {
lastEstimatedHashrate: stats.lastEstimatedHashrate / hashrateDivider,
lastEstimatedHashrate3d: stats.lastEstimatedHashrate3d / hashrateDivider,
lastEstimatedHashrate1w: stats.lastEstimatedHashrate1w / hashrateDivider,
blockCount: stats.blockCount,
totalEmptyBlock: totalEmptyBlock,
totalEmptyBlockRatio: totalEmptyBlockRatio,

View File

@@ -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'

View File

@@ -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'

View File

@@ -1,10 +1,10 @@
import { Injectable } from '@angular/core';
import { catchError, forkJoin, map, Observable, of, switchMap, tap } from 'rxjs';
import { Inscription } from '../shared/ord/inscription.utils';
import { Transaction } from '../interfaces/electrs.interface';
import { getNextInscriptionMark, hexToBytes, extractInscriptionData } from '../shared/ord/inscription.utils';
import { decipherRunestone, Runestone, Etching, UNCOMMON_GOODS } from '../shared/ord/rune.utils';
import { ElectrsApiService } from './electrs-api.service';
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({

View File

@@ -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'

View File

@@ -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;
}));
}
}
}

View File

@@ -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'

View File

@@ -1,13 +1,13 @@
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 interface IUser {
username: string;
@@ -131,20 +131,20 @@ export class ServicesApiServices {
return this.httpClient.post<any>(`${this.stateService.env.SERVICES_API}/accelerator/estimate`, { txInput: txInput }, { observe: 'response' });
}
accelerate$(txInput: string, userBid: number, accelerationUUID: string) {
return this.httpClient.post<any>(`${this.stateService.env.SERVICES_API}/accelerator/accelerate`, { txInput: txInput, userBid: userBid, accelerationUUID: accelerationUUID });
accelerate$(txInput: string, userBid: number) {
return this.httpClient.post<any>(`${this.stateService.env.SERVICES_API}/accelerator/accelerate`, { txInput: txInput, userBid: userBid});
}
accelerateWithCashApp$(txInput: string, token: string, cashtag: string, referenceId: string, accelerationUUID: string) {
return this.httpClient.post<any>(`${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, userApprovedUSD: number) {
return this.httpClient.post<any>(`${this.stateService.env.SERVICES_API}/accelerator/accelerate/cashapp`, { txInput: txInput, token: token, cashtag: cashtag, referenceId: referenceId, userApprovedUSD: userApprovedUSD });
}
accelerateWithApplePay$(txInput: string, token: string, cardTag: string, referenceId: string, accelerationUUID: string) {
return this.httpClient.post<any>(`${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, userApprovedUSD: number) {
return this.httpClient.post<any>(`${this.stateService.env.SERVICES_API}/accelerator/accelerate/applePay`, { txInput: txInput, cardTag: cardTag, token: token, referenceId: referenceId, userApprovedUSD: userApprovedUSD });
}
accelerateWithGooglePay$(txInput: string, token: string, cardTag: string, referenceId: string, accelerationUUID: string) {
return this.httpClient.post<any>(`${this.stateService.env.SERVICES_API}/accelerator/accelerate/googlePay`, { txInput: txInput, cardTag: cardTag, token: token, referenceId: referenceId, accelerationUUID: accelerationUUID });
accelerateWithGooglePay$(txInput: string, token: string, verificationToken: string, cardTag: string, referenceId: string, userApprovedUSD: number) {
return this.httpClient.post<any>(`${this.stateService.env.SERVICES_API}/accelerator/accelerate/googlePay`, { txInput: txInput, cardTag: cardTag, token: token, verificationToken: verificationToken, referenceId: referenceId, userApprovedUSD: userApprovedUSD });
}
getAccelerations$(): Observable<Acceleration[]> {

View File

@@ -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;
@@ -68,7 +68,12 @@ export interface Env {
AUDIT: boolean;
MAINNET_BLOCK_AUDIT_START_HEIGHT: number;
TESTNET_BLOCK_AUDIT_START_HEIGHT: number;
TESTNET4_BLOCK_AUDIT_START_HEIGHT: number;
SIGNET_BLOCK_AUDIT_START_HEIGHT: number;
MAINNET_TX_FIRST_SEEN_START_HEIGHT: number;
TESTNET_TX_FIRST_SEEN_START_HEIGHT: number;
TESTNET4_TX_FIRST_SEEN_START_HEIGHT: number;
SIGNET_TX_FIRST_SEEN_START_HEIGHT: number;
HISTORICAL_PRICE: boolean;
ACCELERATOR: boolean;
ACCELERATOR_BUTTON: boolean;
@@ -78,6 +83,7 @@ export interface Env {
PACKAGE_JSON_VERSION_MEMPOOL_SPACE?: string;
SERVICES_API?: string;
customize?: Customization;
PROD_DOMAINS: string[];
}
const defaultEnv: Env = {
@@ -106,13 +112,19 @@ const defaultEnv: Env = {
'AUDIT': false,
'MAINNET_BLOCK_AUDIT_START_HEIGHT': 0,
'TESTNET_BLOCK_AUDIT_START_HEIGHT': 0,
'TESTNET4_BLOCK_AUDIT_START_HEIGHT': 0,
'SIGNET_BLOCK_AUDIT_START_HEIGHT': 0,
'MAINNET_TX_FIRST_SEEN_START_HEIGHT': 0,
'TESTNET_TX_FIRST_SEEN_START_HEIGHT': 0,
'TESTNET4_TX_FIRST_SEEN_START_HEIGHT': 0,
'SIGNET_TX_FIRST_SEEN_START_HEIGHT': 0,
'HISTORICAL_PRICE': true,
'ACCELERATOR': false,
'ACCELERATOR_BUTTON': true,
'PUBLIC_ACCELERATIONS': false,
'ADDITIONAL_CURRENCIES': false,
'SERVICES_API': 'https://mempool.space/api/v1/services',
'PROD_DOMAINS': [],
};
@Injectable({
@@ -159,6 +171,7 @@ export class StateService {
mempoolRemovedTransactions$ = new Subject<Transaction>();
multiAddressTransactions$ = new Subject<{ [address: string]: { mempool: Transaction[], confirmed: Transaction[], removed: Transaction[] }}>();
blockTransactions$ = new Subject<Transaction>();
walletTransactions$ = new Subject<Transaction[]>();
isLoadingWebSocket$ = new ReplaySubject<boolean>(1);
isLoadingMempool$ = new BehaviorSubject<boolean>(true);
vbytesPerSecond$ = new ReplaySubject<number>(1);
@@ -173,6 +186,7 @@ export class StateService {
live2Chart$ = new Subject<OptimizedMempoolStats>();
viewAmountMode$: BehaviorSubject<'btc' | 'sats' | 'fiat'>;
timezone$: BehaviorSubject<string>;
connectionState$ = new BehaviorSubject<0 | 1 | 2>(2);
isTabHidden$: Observable<boolean>;
@@ -205,6 +219,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') {
@@ -330,6 +348,9 @@ export class StateService {
const viewAmountModePreference = this.storageService.getValue('view-amount-mode') as 'btc' | 'sats' | 'fiat';
this.viewAmountMode$ = new BehaviorSubject<'btc' | 'sats' | 'fiat'>(viewAmountModePreference || 'btc');
const timezonePreference = this.storageService.getValue('timezone-preference');
this.timezone$ = new BehaviorSubject<string>(timezonePreference || 'local');
this.backend$.subscribe(backend => {
this.backend = backend;
});

View File

@@ -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'

View File

@@ -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,

View File

@@ -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,7 +34,10 @@ 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 trackingMempoolBlockNetwork: string;
private stoppingTrackMempoolBlock: any | null = null;
private latestGitCommit = '';
private onlineCheckTimeout: number;
@@ -137,6 +140,9 @@ export class WebsocketService {
if (this.isTrackingAccelerations) {
this.startTrackAccelerations();
}
if (this.isTrackingWallet) {
this.startTrackingWallet(this.trackingWalletName);
}
this.stateService.connectionState$.next(2);
}
@@ -196,6 +202,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 });
}
@@ -209,10 +227,11 @@ export class WebsocketService {
clearTimeout(this.stoppingTrackMempoolBlock);
}
// skip duplicate tracking requests
if (force || this.trackingMempoolBlock !== block) {
if (force || this.trackingMempoolBlock !== block || this.network !== this.trackingMempoolBlockNetwork) {
this.websocketSubject.next({ 'track-mempool-block': block });
this.isTrackingMempoolBlock = true;
this.trackingMempoolBlock = block;
this.trackingMempoolBlockNetwork = this.network;
return true;
}
return false;
@@ -452,6 +471,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({