import { Inject, Injectable, PLATFORM_ID } from '@angular/core'; import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable } from 'rxjs'; import { Block, Transaction } from '../interfaces/electrs.interface'; import { MempoolBlock, MempoolInfo, TransactionStripped } from '../interfaces/websocket.interface'; import { OptimizedMempoolStats } from '../interfaces/node-api.interface'; import { Router, NavigationStart } from '@angular/router'; import { isPlatformBrowser } from '@angular/common'; import { map, shareReplay } from 'rxjs/operators'; interface MarkBlockState { blockHeight?: number; mempoolBlockIndex?: number; txFeePerVSize?: number; } export interface ILoadingIndicators { [name: string]: number; } export interface Env { TESTNET_ENABLED: boolean; LIQUID_ENABLED: boolean; BISQ_ENABLED: boolean; BISQ_SEPARATE_BACKEND: boolean; SPONSORS_ENABLED: boolean; ELECTRS_ITEMS_PER_PAGE: number; KEEP_BLOCKS_AMOUNT: number; NGINX_PROTOCOL?: string; NGINX_HOSTNAME?: string; NGINX_PORT?: string; } const defaultEnv: Env = { 'TESTNET_ENABLED': false, 'LIQUID_ENABLED': false, 'BISQ_ENABLED': false, 'BISQ_SEPARATE_BACKEND': false, 'SPONSORS_ENABLED': false, 'ELECTRS_ITEMS_PER_PAGE': 25, 'KEEP_BLOCKS_AMOUNT': 8, 'NGINX_PROTOCOL': 'http', 'NGINX_HOSTNAME': '127.0.0.1', 'NGINX_PORT': '81', }; @Injectable({ providedIn: 'root' }) export class StateService { isBrowser: boolean = isPlatformBrowser(this.platformId); network = ''; env: Env; latestBlockHeight = 0; networkChanged$ = new ReplaySubject(1); blocks$: ReplaySubject<[Block, boolean]>; transactions$ = new ReplaySubject(6); conversions$ = new ReplaySubject(1); bsqPrice$ = new ReplaySubject(1); mempoolInfo$ = new ReplaySubject(1); mempoolBlocks$ = new ReplaySubject(1); txReplaced$ = new Subject(); mempoolTransactions$ = new Subject(); blockTransactions$ = new Subject(); isLoadingWebSocket$ = new ReplaySubject(1); vbytesPerSecond$ = new ReplaySubject(1); lastDifficultyAdjustment$ = new ReplaySubject(1); gitCommit$ = new ReplaySubject(1); donationConfirmed$ = new Subject(); loadingIndicators$ = new ReplaySubject(1); live2Chart$ = new Subject(); viewFiat$ = new BehaviorSubject(false); connectionState$ = new BehaviorSubject<0 | 1 | 2>(2); isTabHidden$: Observable; markBlock$ = new ReplaySubject(); keyNavigation$ = new Subject(); constructor( @Inject(PLATFORM_ID) private platformId: any, private router: Router, ) { this.router.events.subscribe((event) => { if (event instanceof NavigationStart) { this.setNetworkBasedonUrl(event.url); } }); if (this.isBrowser) { this.setNetworkBasedonUrl(window.location.pathname); this.isTabHidden$ = fromEvent(document, 'visibilitychange').pipe(map(() => this.isHidden()), shareReplay()); } else { this.setNetworkBasedonUrl('/'); this.isTabHidden$ = new BehaviorSubject(false); } const browserWindow = window || {}; // @ts-ignore const browserWindowEnv = browserWindow.__env || {}; this.env = Object.assign(defaultEnv, browserWindowEnv); this.blocks$ = new ReplaySubject<[Block, boolean]>(this.env.KEEP_BLOCKS_AMOUNT); } setNetworkBasedonUrl(url: string) { switch (url.split(/\/|\?|#/)[1]) { case 'liquid': if (this.network !== 'liquid') { this.network = 'liquid'; this.networkChanged$.next('liquid'); } return; case 'testnet': if (this.network !== 'testnet') { this.network = 'testnet'; this.networkChanged$.next('testnet'); } return; case 'bisq': if (this.network !== 'bisq') { this.network = 'bisq'; this.networkChanged$.next('bisq'); } return; default: if (this.network !== '') { this.network = ''; this.networkChanged$.next(''); } } } getHiddenProp(){ const prefixes = ['webkit', 'moz', 'ms', 'o']; if ('hidden' in document) { return 'hidden'; } for (const prefix of prefixes) { if ((prefix + 'Hidden') in document) { return prefix + 'Hidden'; } } return null; } isHidden() { const prop = this.getHiddenProp(); if (!prop) { return false; } return document[prop]; } }