Liquid and Testnet now accessable from the main site

fixes #35
This commit is contained in:
softsimon
2020-05-09 20:37:50 +07:00
parent 4932d6f706
commit 7974cbbc83
45 changed files with 452 additions and 162 deletions

View File

@@ -2,43 +2,52 @@ import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { OptimizedMempoolStats } from '../interfaces/node-api.interface';
import { Observable } from 'rxjs';
import { StateService } from './state.service';
const API_BASE_URL = '/api/v1';
const API_BASE_URL = '/api{network}/v1';
@Injectable({
providedIn: 'root'
})
export class ApiService {
apiBaseUrl: string;
constructor(
private httpClient: HttpClient,
) { }
private stateService: StateService,
) {
this.apiBaseUrl = API_BASE_URL.replace('{network}', '');
this.stateService.networkChanged$.subscribe((network) => {
this.apiBaseUrl = API_BASE_URL.replace('{network}', network ? '/' + network : '');
});
}
list2HStatistics$(): Observable<OptimizedMempoolStats[]> {
return this.httpClient.get<OptimizedMempoolStats[]>(API_BASE_URL + '/statistics/2h');
return this.httpClient.get<OptimizedMempoolStats[]>(this.apiBaseUrl + '/statistics/2h');
}
list24HStatistics$(): Observable<OptimizedMempoolStats[]> {
return this.httpClient.get<OptimizedMempoolStats[]>(API_BASE_URL + '/statistics/24h');
return this.httpClient.get<OptimizedMempoolStats[]>(this.apiBaseUrl + '/statistics/24h');
}
list1WStatistics$(): Observable<OptimizedMempoolStats[]> {
return this.httpClient.get<OptimizedMempoolStats[]>(API_BASE_URL + '/statistics/1w');
return this.httpClient.get<OptimizedMempoolStats[]>(this.apiBaseUrl + '/statistics/1w');
}
list1MStatistics$(): Observable<OptimizedMempoolStats[]> {
return this.httpClient.get<OptimizedMempoolStats[]>(API_BASE_URL + '/statistics/1m');
return this.httpClient.get<OptimizedMempoolStats[]>(this.apiBaseUrl + '/statistics/1m');
}
list3MStatistics$(): Observable<OptimizedMempoolStats[]> {
return this.httpClient.get<OptimizedMempoolStats[]>(API_BASE_URL + '/statistics/3m');
return this.httpClient.get<OptimizedMempoolStats[]>(this.apiBaseUrl + '/statistics/3m');
}
list6MStatistics$(): Observable<OptimizedMempoolStats[]> {
return this.httpClient.get<OptimizedMempoolStats[]>(API_BASE_URL + '/statistics/6m');
return this.httpClient.get<OptimizedMempoolStats[]>(this.apiBaseUrl + '/statistics/6m');
}
list1YStatistics$(): Observable<OptimizedMempoolStats[]> {
return this.httpClient.get<OptimizedMempoolStats[]>(API_BASE_URL + '/statistics/1y');
return this.httpClient.get<OptimizedMempoolStats[]>(this.apiBaseUrl + '/statistics/1y');
}
getTransactionTimes$(txIds: string[]): Observable<number[]> {
@@ -46,6 +55,6 @@ export class ApiService {
txIds.forEach((txId: string) => {
params = params.append('txId[]', txId);
});
return this.httpClient.get<number[]>(API_BASE_URL + '/transaction-times', { params });
return this.httpClient.get<number[]>(this.apiBaseUrl + '/transaction-times', { params });
}
}

View File

@@ -1,15 +1,12 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { ReplaySubject, Observable } from 'rxjs';
import { environment } from 'src/environments/environment';
import { Observable } from 'rxjs';
import { shareReplay } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class AssetsService {
network = environment.network;
getAssetsJson$: Observable<any>;
getAssetsMinimalJson$: Observable<any>;

View File

@@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Block, Transaction, Address, Outspend, Recent, Asset } from '../interfaces/electrs.interface';
import { StateService } from './state.service';
const API_BASE_URL = document.location.protocol + '//' + document.location.hostname + ':' + document.location.port + '/electrs';
@@ -9,61 +10,68 @@ const API_BASE_URL = document.location.protocol + '//' + document.location.hostn
providedIn: 'root'
})
export class ElectrsApiService {
apiBaseUrl: string;
constructor(
private httpClient: HttpClient,
private stateService: StateService,
) {
this.apiBaseUrl = API_BASE_URL;
this.stateService.networkChanged$.subscribe((network) => {
this.apiBaseUrl = API_BASE_URL + '/' + network;
});
}
getBlock$(hash: string): Observable<Block> {
return this.httpClient.get<Block>(API_BASE_URL + '/block/' + hash);
return this.httpClient.get<Block>(this.apiBaseUrl + '/block/' + hash);
}
listBlocks$(height?: number): Observable<Block[]> {
return this.httpClient.get<Block[]>(API_BASE_URL + '/blocks/' + (height || ''));
return this.httpClient.get<Block[]>(this.apiBaseUrl + '/blocks/' + (height || ''));
}
getTransaction$(txId: string): Observable<Transaction> {
return this.httpClient.get<Transaction>(API_BASE_URL + '/tx/' + txId);
return this.httpClient.get<Transaction>(this.apiBaseUrl + '/tx/' + txId);
}
getRecentTransaction$(): Observable<Recent[]> {
return this.httpClient.get<Recent[]>(API_BASE_URL + '/mempool/recent');
return this.httpClient.get<Recent[]>(this.apiBaseUrl + '/mempool/recent');
}
getOutspend$(hash: string, vout: number): Observable<Outspend> {
return this.httpClient.get<Outspend>(API_BASE_URL + '/tx/' + hash + '/outspend/' + vout);
return this.httpClient.get<Outspend>(this.apiBaseUrl + '/tx/' + hash + '/outspend/' + vout);
}
getOutspends$(hash: string): Observable<Outspend[]> {
return this.httpClient.get<Outspend[]>(API_BASE_URL + '/tx/' + hash + '/outspends');
return this.httpClient.get<Outspend[]>(this.apiBaseUrl + '/tx/' + hash + '/outspends');
}
getBlockTransactions$(hash: string, index: number = 0): Observable<Transaction[]> {
return this.httpClient.get<Transaction[]>(API_BASE_URL + '/block/' + hash + '/txs/' + index);
return this.httpClient.get<Transaction[]>(this.apiBaseUrl + '/block/' + hash + '/txs/' + index);
}
getAddress$(address: string): Observable<Address> {
return this.httpClient.get<Address>(API_BASE_URL + '/address/' + address);
return this.httpClient.get<Address>(this.apiBaseUrl + '/address/' + address);
}
getAddressTransactions$(address: string): Observable<Transaction[]> {
return this.httpClient.get<Transaction[]>(API_BASE_URL + '/address/' + address + '/txs');
return this.httpClient.get<Transaction[]>(this.apiBaseUrl + '/address/' + address + '/txs');
}
getAddressTransactionsFromHash$(address: string, txid: string): Observable<Transaction[]> {
return this.httpClient.get<Transaction[]>(API_BASE_URL + '/address/' + address + '/txs/chain/' + txid);
return this.httpClient.get<Transaction[]>(this.apiBaseUrl + '/address/' + address + '/txs/chain/' + txid);
}
getAsset$(assetId: string): Observable<Asset> {
return this.httpClient.get<Asset>(API_BASE_URL + '/asset/' + assetId);
return this.httpClient.get<Asset>(this.apiBaseUrl + '/asset/' + assetId);
}
getAssetTransactions$(assetId: string): Observable<Transaction[]> {
return this.httpClient.get<Transaction[]>(API_BASE_URL + '/asset/' + assetId + '/txs');
return this.httpClient.get<Transaction[]>(this.apiBaseUrl + '/asset/' + assetId + '/txs');
}
getAssetTransactionsFromHash$(assetId: string, txid: string): Observable<Transaction[]> {
return this.httpClient.get<Transaction[]>(API_BASE_URL + '/asset/' + assetId + '/txs/chain/' + txid);
return this.httpClient.get<Transaction[]>(this.apiBaseUrl + '/asset/' + assetId + '/txs/chain/' + txid);
}
}

View File

@@ -1,17 +1,20 @@
import { Injectable } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { environment } from 'src/environments/environment';
import { StateService } from './state.service';
@Injectable({
providedIn: 'root'
})
export class SeoService {
network = environment.network;
network = '';
defaultTitle = 'mempool - Bitcoin Explorer';
constructor(
private titleService: Title,
) { }
private stateService: StateService,
) {
this.stateService.networkChanged$.subscribe((network) => this.network = network);
}
setTitle(newTitle: string, prependNetwork = false) {
let networkName = '';

View File

@@ -3,6 +3,7 @@ import { ReplaySubject, BehaviorSubject, Subject } from 'rxjs';
import { Block, Transaction } from '../interfaces/electrs.interface';
import { MempoolBlock, MemPoolState } from '../interfaces/websocket.interface';
import { OptimizedMempoolStats } from '../interfaces/node-api.interface';
import { Router, NavigationStart } from '@angular/router';
interface MarkBlockState {
blockHeight?: number;
@@ -14,10 +15,13 @@ interface MarkBlockState {
providedIn: 'root'
})
export class StateService {
network = '';
latestBlockHeight = 0;
networkChanged$ = new ReplaySubject<string>(1);
blocks$ = new ReplaySubject<Block>(8);
conversions$ = new ReplaySubject<any>(1);
mempoolStats$ = new ReplaySubject<MemPoolState>();
mempoolStats$ = new ReplaySubject<MemPoolState>(1);
mempoolBlocks$ = new ReplaySubject<MempoolBlock[]>(1);
txConfirmed$ = new Subject<Block>();
mempoolTransactions$ = new Subject<Transaction>();
@@ -30,4 +34,34 @@ export class StateService {
connectionState$ = new BehaviorSubject<0 | 1 | 2>(2);
markBlock$ = new Subject<MarkBlockState>();
constructor(
private router: Router,
) {
this.router.events.subscribe((event) => {
if (event instanceof NavigationStart) {
switch (event.url.split('/')[1]) {
case 'liquid':
case 'liquid-tv':
if (this.network !== 'liquid') {
this.network = 'liquid';
this.networkChanged$.next('liquid');
}
return;
case 'testnet':
case 'testnet-tv':
if (this.network !== 'testnet') {
this.network = 'testnet';
this.networkChanged$.next('testnet');
}
return;
default:
if (this.network !== '') {
this.network = '';
this.networkChanged$.next('');
}
}
}
});
}
}

View File

@@ -16,7 +16,7 @@ const EXPECT_PING_RESPONSE_AFTER_MS = 1000;
providedIn: 'root'
})
export class WebsocketService {
private websocketSubject: WebSocketSubject<WebsocketResponse> = webSocket<WebsocketResponse | any>(WEB_SOCKET_URL);
private websocketSubject: WebSocketSubject<WebsocketResponse>;
private goneOffline = false;
private lastWant: string[] | null = null;
private isTrackingTx = false;
@@ -28,7 +28,22 @@ export class WebsocketService {
constructor(
private stateService: StateService,
) {
this.websocketSubject = webSocket<WebsocketResponse | any>(WEB_SOCKET_URL + '/' + this.stateService.network);
this.startSubscription();
this.stateService.networkChanged$.subscribe((network) => {
clearTimeout(this.onlineCheckTimeout);
clearTimeout(this.onlineCheckTimeoutTwo);
this.stateService.latestBlockHeight = 0;
this.websocketSubject.complete();
this.subscription.unsubscribe();
this.websocketSubject = webSocket<WebsocketResponse | any>(WEB_SOCKET_URL + '/' + network);
this.startSubscription();
});
}
startSubscription(retrying = false) {