Bisq markets dashboard: Market backend tracking. WIP.

This commit is contained in:
softsimon 2021-03-05 15:38:46 +07:00
parent 2fca34faaa
commit d99fd5d59a
No known key found for this signature in database
GPG key ID: 488D7DCFB5A430D7
17 changed files with 101 additions and 78 deletions

View file

@ -96,6 +96,14 @@ class WebsocketHandler {
client['track-donation'] = parsedMessage['track-donation'];
}
if (parsedMessage['track-bisq-market']) {
if (/^[a-z]{3}_[a-z]{3}$/.test(parsedMessage['track-bisq-market'])) {
client['track-bisq-market'] = parsedMessage['track-bisq-market'];
} else {
client['track-bisq-market'] = null;
}
}
if (Object.keys(response).length) {
client.send(JSON.stringify(response));
}

View file

@ -122,6 +122,7 @@ export interface WebsocketResponse {
'track-tx': string;
'track-address': string;
'watch-mempool': boolean;
'track-bisq-market': string;
}
export interface VbytesPerSecond {

View file

@ -5,6 +5,7 @@ import { ParamMap, ActivatedRoute } from '@angular/router';
import { Subscription, of } from 'rxjs';
import { BisqTransaction } from '../bisq.interfaces';
import { BisqApiService } from '../bisq-api.service';
import { WebsocketService } from 'src/app/services/websocket.service';
@Component({
selector: 'app-bisq-address',
@ -22,12 +23,15 @@ export class BisqAddressComponent implements OnInit, OnDestroy {
totalSent = 0;
constructor(
private websocketService: WebsocketService,
private route: ActivatedRoute,
private seoService: SeoService,
private bisqApiService: BisqApiService,
) { }
ngOnInit() {
this.websocketService.want(['blocks']);
this.mainSubscription = this.route.paramMap
.pipe(
switchMap((params: ParamMap) => {

View file

@ -8,6 +8,7 @@ import { switchMap, catchError } from 'rxjs/operators';
import { SeoService } from 'src/app/services/seo.service';
import { ElectrsApiService } from 'src/app/services/electrs-api.service';
import { HttpErrorResponse } from '@angular/common/http';
import { WebsocketService } from 'src/app/services/websocket.service';
@Component({
selector: 'app-bisq-block',
@ -23,6 +24,7 @@ export class BisqBlockComponent implements OnInit, OnDestroy {
error: HttpErrorResponse | null;
constructor(
private websocketService: WebsocketService,
private bisqApiService: BisqApiService,
private route: ActivatedRoute,
private seoService: SeoService,
@ -32,6 +34,8 @@ export class BisqBlockComponent implements OnInit, OnDestroy {
) { }
ngOnInit(): void {
this.websocketService.want(['blocks']);
this.subscription = this.route.paramMap
.pipe(
switchMap((params: ParamMap) => {

View file

@ -5,6 +5,7 @@ import { Observable } from 'rxjs';
import { BisqBlock, BisqOutput, BisqTransaction } from '../bisq.interfaces';
import { SeoService } from 'src/app/services/seo.service';
import { ActivatedRoute, Router } from '@angular/router';
import { WebsocketService } from 'src/app/services/websocket.service';
@Component({
selector: 'app-bisq-blocks',
@ -25,6 +26,7 @@ export class BisqBlocksComponent implements OnInit {
paginationMaxSize = 10;
constructor(
private websocketService: WebsocketService,
private bisqApiService: BisqApiService,
private seoService: SeoService,
private route: ActivatedRoute,
@ -32,6 +34,7 @@ export class BisqBlocksComponent implements OnInit {
) { }
ngOnInit(): void {
this.websocketService.want(['blocks']);
this.seoService.setTitle($localize`:@@8a7b4bd44c0ac71b2e72de0398b303257f7d2f54:Blocks`);
this.itemsPerPage = Math.max(Math.round(this.contentSpace / this.fiveItemsPxSize) * 5, 10);
this.loadingItems = Array(this.itemsPerPage);

View file

@ -1,6 +1,7 @@
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { Observable, combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';
import { WebsocketService } from 'src/app/services/websocket.service';
import { BisqApiService } from '../bisq-api.service';
@Component({
@ -13,10 +14,13 @@ export class BisqDashboardComponent implements OnInit {
tickers$: Observable<any>;
constructor(
private websocketService: WebsocketService,
private bisqApiService: BisqApiService,
) { }
ngOnInit(): void {
this.websocketService.want(['blocks']);
this.tickers$ = combineLatest([
this.bisqApiService.getMarketsTicker$(),
this.bisqApiService.getMarkets$(),

View file

@ -1 +0,0 @@
<router-outlet></router-outlet>

View file

@ -1,18 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { WebsocketService } from 'src/app/services/websocket.service';
@Component({
selector: 'app-bisq-explorer',
templateUrl: './bisq-explorer.component.html',
styleUrls: ['./bisq-explorer.component.scss']
})
export class BisqExplorerComponent implements OnInit {
constructor(
private websocketService: WebsocketService,
) { }
ngOnInit(): void {
this.websocketService.want(['blocks']);
}
}

View file

@ -1,8 +1,9 @@
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import { combineLatest, merge, Observable, of } from 'rxjs';
import { filter, map, mergeAll, switchMap, tap } from 'rxjs/operators';
import { WebsocketService } from 'src/app/services/websocket.service';
import { BisqApiService } from '../bisq-api.service';
@Component({
@ -11,7 +12,7 @@ import { BisqApiService } from '../bisq-api.service';
styleUrls: ['./bisq-market.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class BisqMarketComponent implements OnInit {
export class BisqMarketComponent implements OnInit, OnDestroy {
hlocData$: Observable<any>;
currency$: Observable<any>;
offers$: Observable<any>;
@ -19,6 +20,7 @@ export class BisqMarketComponent implements OnInit {
defaultInterval = 'half_hour';
constructor(
private websocketService: WebsocketService,
private route: ActivatedRoute,
private bisqApiService: BisqApiService,
private formBuilder: FormBuilder,
@ -44,6 +46,7 @@ export class BisqMarketComponent implements OnInit {
this.offers$ = this.route.paramMap
.pipe(
map(routeParams => routeParams.get('pair')),
tap((marketPair) => this.websocketService.startTrackBisqMarket(marketPair)),
switchMap((marketPair) => this.bisqApiService.getMarketOffers$(marketPair)),
map((offers) => {
return offers[Object.keys(offers)[0]];
@ -68,4 +71,8 @@ export class BisqMarketComponent implements OnInit {
);
}
ngOnDestroy(): void {
this.websocketService.stopTrackingBisqMarket();
}
}

View file

@ -3,6 +3,7 @@ import { BisqApiService } from '../bisq-api.service';
import { BisqStats } from '../bisq.interfaces';
import { SeoService } from 'src/app/services/seo.service';
import { StateService } from 'src/app/services/state.service';
import { WebsocketService } from 'src/app/services/websocket.service';
@Component({
selector: 'app-bisq-stats',
@ -15,12 +16,15 @@ export class BisqStatsComponent implements OnInit {
price: number;
constructor(
private websocketService: WebsocketService,
private bisqApiService: BisqApiService,
private seoService: SeoService,
private stateService: StateService,
) { }
ngOnInit() {
this.websocketService.want(['blocks']);
this.seoService.setTitle($localize`:@@2a30a4cdb123a03facc5ab8c5b3e6d8b8dbbc3d4:BSQ statistics`);
this.stateService.bsqPrice$
.subscribe((bsqPrice) => {

View file

@ -9,6 +9,7 @@ import { BisqApiService } from '../bisq-api.service';
import { SeoService } from 'src/app/services/seo.service';
import { ElectrsApiService } from 'src/app/services/electrs-api.service';
import { HttpErrorResponse } from '@angular/common/http';
import { WebsocketService } from 'src/app/services/websocket.service';
@Component({
selector: 'app-bisq-transaction',
@ -27,6 +28,7 @@ export class BisqTransactionComponent implements OnInit, OnDestroy {
subscription: Subscription;
constructor(
private websocketService: WebsocketService,
private route: ActivatedRoute,
private bisqApiService: BisqApiService,
private electrsApiService: ElectrsApiService,
@ -36,6 +38,8 @@ export class BisqTransactionComponent implements OnInit, OnDestroy {
) { }
ngOnInit(): void {
this.websocketService.want(['blocks']);
this.subscription = this.route.paramMap.pipe(
switchMap((params: ParamMap) => {
this.isLoading = true;

View file

@ -8,6 +8,7 @@ import { SeoService } from 'src/app/services/seo.service';
import { FormGroup, FormBuilder } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router';
import { IMultiSelectOption, IMultiSelectSettings, IMultiSelectTexts } from 'ngx-bootrap-multiselect';
import { WebsocketService } from 'src/app/services/websocket.service';
@Component({
selector: 'app-bisq-transactions',
@ -65,6 +66,7 @@ export class BisqTransactionsComponent implements OnInit {
'PROOF_OF_BURN', 'PROPOSAL', 'REIMBURSEMENT_REQUEST', 'TRANSFER_BSQ', 'UNLOCK', 'VOTE_REVEAL', 'IRREGULAR'];
constructor(
private websocketService: WebsocketService,
private bisqApiService: BisqApiService,
private seoService: SeoService,
private formBuilder: FormBuilder,
@ -74,6 +76,7 @@ export class BisqTransactionsComponent implements OnInit {
) { }
ngOnInit(): void {
this.websocketService.want(['blocks']);
this.seoService.setTitle($localize`:@@add4cd82e3e38a3110fe67b3c7df56e9602644ee:Transactions`);
this.radioGroupForm = this.formBuilder.group({

View file

@ -17,7 +17,6 @@ import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontaweso
import { faLeaf, faQuestion, faExclamationTriangle, faRocket, faRetweet, faFileAlt, faMoneyBill,
faEye, faEyeSlash, faLock, faLockOpen, faExclamationCircle } from '@fortawesome/free-solid-svg-icons';
import { BisqBlocksComponent } from './bisq-blocks/bisq-blocks.component';
import { BisqExplorerComponent } from './bisq-explorer/bisq-explorer.component';
import { BisqApiService } from './bisq-api.service';
import { BisqAddressComponent } from './bisq-address/bisq-address.component';
import { BisqStatsComponent } from './bisq-stats/bisq-stats.component';
@ -33,7 +32,6 @@ import { BsqAmountComponent } from './bsq-amount/bsq-amount.component';
BisqTransactionDetailsComponent,
BisqTransfersComponent,
BisqBlocksComponent,
BisqExplorerComponent,
BisqAddressComponent,
BisqStatsComponent,
BsqAmountComponent,

View file

@ -5,7 +5,6 @@ import { BisqTransactionsComponent } from './bisq-transactions/bisq-transactions
import { BisqTransactionComponent } from './bisq-transaction/bisq-transaction.component';
import { BisqBlockComponent } from './bisq-block/bisq-block.component';
import { BisqBlocksComponent } from './bisq-blocks/bisq-blocks.component';
import { BisqExplorerComponent } from './bisq-explorer/bisq-explorer.component';
import { BisqAddressComponent } from './bisq-address/bisq-address.component';
import { BisqStatsComponent } from './bisq-stats/bisq-stats.component';
import { ApiDocsComponent } from '../components/api-docs/api-docs.component';
@ -14,56 +13,50 @@ import { BisqMarketComponent } from './bisq-market/bisq-market.component';
const routes: Routes = [
{
path: '',
component: BisqExplorerComponent,
children: [
{
path: '',
component: BisqDashboardComponent,
},
{
path: 'transactions',
component: BisqTransactionsComponent
},
{
path: 'market/:pair',
component: BisqMarketComponent,
},
{
path: 'tx/:id',
component: BisqTransactionComponent
},
{
path: 'blocks',
children: [],
component: BisqBlocksComponent
},
{
path: 'block/:id',
component: BisqBlockComponent,
},
{
path: 'address/:id',
component: BisqAddressComponent,
},
{
path: 'stats',
component: BisqStatsComponent,
},
{
path: 'about',
component: AboutComponent,
},
{
path: 'api',
component: ApiDocsComponent,
},
{
path: '**',
redirectTo: ''
}
]
}
path: '',
component: BisqDashboardComponent,
},
{
path: 'transactions',
component: BisqTransactionsComponent
},
{
path: 'market/:pair',
component: BisqMarketComponent,
},
{
path: 'tx/:id',
component: BisqTransactionComponent
},
{
path: 'blocks',
children: [],
component: BisqBlocksComponent
},
{
path: 'block/:id',
component: BisqBlockComponent,
},
{
path: 'address/:id',
component: BisqAddressComponent,
},
{
path: 'stats',
component: BisqStatsComponent,
},
{
path: 'about',
component: AboutComponent,
},
{
path: 'api',
component: ApiDocsComponent,
},
{
path: '**',
redirectTo: ''
}
];
@NgModule({

View file

@ -20,6 +20,7 @@ export interface WebsocketResponse {
'track-address'?: string;
'track-asset'?: string;
'watch-mempool'?: boolean;
'track-bisq-market'?: string;
}
export interface MempoolBlock {

View file

@ -23,7 +23,7 @@ export class WebsocketService {
private websocketSubject: WebSocketSubject<WebsocketResponse>;
private goneOffline = false;
private lastWant: string[] | null = null;
private lastWant: string | null = null;
private isTrackingTx = false;
private latestGitCommit = '';
private onlineCheckTimeout: number;
@ -95,7 +95,7 @@ export class WebsocketService {
if (this.goneOffline === true) {
this.goneOffline = false;
if (this.lastWant) {
this.want(this.lastWant, true);
this.want(JSON.parse(this.lastWant), true);
}
this.stateService.connectionState$.next(2);
}
@ -150,6 +150,14 @@ export class WebsocketService {
this.websocketSubject.next({ 'track-asset': 'stop' });
}
startTrackBisqMarket(market: string) {
this.websocketSubject.next({ 'track-bisq-market': market });
}
stopTrackingBisqMarket() {
this.websocketSubject.next({ 'track-bisq-market': 'stop' });
}
fetchStatistics(historicalDate: string) {
this.websocketSubject.next({ historicalDate });
}
@ -158,11 +166,11 @@ export class WebsocketService {
if (!this.stateService.isBrowser) {
return;
}
if (data === this.lastWant && !force) {
if (JSON.stringify(data) === this.lastWant && !force) {
return;
}
this.websocketSubject.next({action: 'want', data: data});
this.lastWant = data;
this.lastWant = JSON.stringify(data);
}
goOffline() {