From eeb74479886f1d0f451baeee716d39a49c61e37e Mon Sep 17 00:00:00 2001 From: softsimon Date: Sat, 27 Feb 2021 04:19:56 +0700 Subject: [PATCH] Bisq markets dashboard. Base views. WIP. --- frontend/package-lock.json | 27 ++++++++ frontend/package.json | 1 + frontend/src/app/bisq/bisq-api.service.ts | 17 +++++ .../bisq-dashboard.component.html | 34 ++++++++++ .../bisq-dashboard.component.scss | 0 .../bisq-dashboard.component.ts | 43 ++++++++++++ .../bisq-market/bisq-market.component.html | 49 +++++++++++++ .../bisq-market/bisq-market.component.scss | 0 .../bisq/bisq-market/bisq-market.component.ts | 64 +++++++++++++++++ frontend/src/app/bisq/bisq.module.ts | 6 ++ frontend/src/app/bisq/bisq.routing.module.ts | 10 +++ .../lightweight-charts.component.scss | 0 .../lightweight-charts.component.ts | 68 +++++++++++++++++++ .../master-page/master-page.component.html | 10 +-- frontend/tslint.json | 1 + 15 files changed, 325 insertions(+), 5 deletions(-) create mode 100644 frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.html create mode 100644 frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.scss create mode 100644 frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.ts create mode 100644 frontend/src/app/bisq/bisq-market/bisq-market.component.html create mode 100644 frontend/src/app/bisq/bisq-market/bisq-market.component.scss create mode 100644 frontend/src/app/bisq/bisq-market/bisq-market.component.ts create mode 100644 frontend/src/app/bisq/lightweight-charts/lightweight-charts.component.scss create mode 100644 frontend/src/app/bisq/lightweight-charts/lightweight-charts.component.ts diff --git a/frontend/package-lock.json b/frontend/package-lock.json index eea9f09e6..ed1937bdd 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -31,6 +31,7 @@ "clipboard": "^2.0.4", "domino": "^2.1.6", "express": "^4.15.2", + "lightweight-charts": "^3.3.0", "ngx-bootrap-multiselect": "^2.0.0", "ngx-infinite-scroll": "^9.0.0", "qrcode": "^1.4.4", @@ -6857,6 +6858,11 @@ "node >=0.6.0" ] }, + "node_modules/fancy-canvas": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fancy-canvas/-/fancy-canvas-0.2.2.tgz", + "integrity": "sha512-50qi8xA0QkHbjmb8h7XQ6k2fvD7y/yMfiUw9YTarJ7rWrq6o5/3CCXPouYk+XSLASvvxtjyiQLRBFt3qkE3oyA==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -9668,6 +9674,14 @@ "immediate": "~3.0.5" } }, + "node_modules/lightweight-charts": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lightweight-charts/-/lightweight-charts-3.3.0.tgz", + "integrity": "sha512-W5jeBrXcHG8eHnIQ0L2CB9TLkrrsjNPlQq5SICPO8PnJ3dJ8jZkLCAwemZ7Ym7ZGCfKCz6ow1EPbyzNYxblnkw==", + "dependencies": { + "fancy-canvas": "0.2.2" + } + }, "node_modules/limiter": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", @@ -24769,6 +24783,11 @@ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", "dev": true }, + "fancy-canvas": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fancy-canvas/-/fancy-canvas-0.2.2.tgz", + "integrity": "sha512-50qi8xA0QkHbjmb8h7XQ6k2fvD7y/yMfiUw9YTarJ7rWrq6o5/3CCXPouYk+XSLASvvxtjyiQLRBFt3qkE3oyA==" + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -27006,6 +27025,14 @@ "immediate": "~3.0.5" } }, + "lightweight-charts": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lightweight-charts/-/lightweight-charts-3.3.0.tgz", + "integrity": "sha512-W5jeBrXcHG8eHnIQ0L2CB9TLkrrsjNPlQq5SICPO8PnJ3dJ8jZkLCAwemZ7Ym7ZGCfKCz6ow1EPbyzNYxblnkw==", + "requires": { + "fancy-canvas": "0.2.2" + } + }, "limiter": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", diff --git a/frontend/package.json b/frontend/package.json index dd2bba86b..a72a5c940 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -61,6 +61,7 @@ "clipboard": "^2.0.4", "domino": "^2.1.6", "express": "^4.15.2", + "lightweight-charts": "^3.3.0", "ngx-bootrap-multiselect": "^2.0.0", "ngx-infinite-scroll": "^9.0.0", "qrcode": "^1.4.4", diff --git a/frontend/src/app/bisq/bisq-api.service.ts b/frontend/src/app/bisq/bisq-api.service.ts index 0f9eb0868..9967269cf 100644 --- a/frontend/src/app/bisq/bisq-api.service.ts +++ b/frontend/src/app/bisq/bisq-api.service.ts @@ -42,4 +42,21 @@ export class BisqApiService { getAddress$(address: string): Observable { return this.httpClient.get(API_BASE_URL + '/address/' + address); } + + getMarkets$(): Observable { + return this.httpClient.get(API_BASE_URL + '/markets/markets'); + } + + getMarketsTicker$(): Observable { + return this.httpClient.get(API_BASE_URL + '/markets/ticker'); + } + + getMarketsCurrencies$(): Observable { + return this.httpClient.get(API_BASE_URL + '/markets/currencies'); + } + + getMarketsHloc$(market: string, interval: 'minute' | 'half_hour' | 'hour' | 'half_day' | 'day' + | 'week' | 'month' | 'year' | 'auto'): Observable { + return this.httpClient.get(API_BASE_URL + '/markets/hloc?market=' + market + '&interval=' + interval); + } } diff --git a/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.html b/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.html new file mode 100644 index 000000000..92fff717b --- /dev/null +++ b/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.html @@ -0,0 +1,34 @@ +
+

Active Markets

+ + + + + + + + + + + + + + + + + +
CoinPairPrice24h volumeVolume %
{{ ticker.market.lname }}{{ ticker.pair }} + + {{ ticker.last | currency: ticker.market.rsymbol }} +
+ +
+ +
+
+ + + + + + \ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.scss b/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.ts b/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.ts new file mode 100644 index 000000000..bc1303fd6 --- /dev/null +++ b/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.ts @@ -0,0 +1,43 @@ +import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { Observable, combineLatest } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { BisqApiService } from '../bisq-api.service'; + +@Component({ + selector: 'app-bisq-dashboard', + templateUrl: './bisq-dashboard.component.html', + styleUrls: ['./bisq-dashboard.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class BisqDashboardComponent implements OnInit { + tickers$: Observable; + + constructor( + private bisqApiService: BisqApiService, + ) { } + + ngOnInit(): void { + this.tickers$ = combineLatest([ + this.bisqApiService.getMarketsTicker$(), + this.bisqApiService.getMarkets$() + ]) + .pipe( + map(([tickers, markets]) => { + const newTickers = []; + for (const t in tickers) { + tickers[t].pair_url = t; + tickers[t].pair = t.replace('_', '/').toUpperCase(); + tickers[t].market = markets[t]; + newTickers.push(tickers[t]); + } + console.log(newTickers); + return newTickers; + }) + ); + } + + trackByFn(index: number) { + return index; + } + +} diff --git a/frontend/src/app/bisq/bisq-market/bisq-market.component.html b/frontend/src/app/bisq/bisq-market/bisq-market.component.html new file mode 100644 index 000000000..f7d9f9df2 --- /dev/null +++ b/frontend/src/app/bisq/bisq-market/bisq-market.component.html @@ -0,0 +1,49 @@ +
+ + + + +

{{ currency.market.lname }} - {{ currency.pair }}

+
+ {{ hlocData[hlocData.length - 1].close | currency: currency.market.rsymbol }} +
+ +
+ +
+
+ + + + + + + + + +
+
+ + +
+
diff --git a/frontend/src/app/bisq/bisq-market/bisq-market.component.scss b/frontend/src/app/bisq/bisq-market/bisq-market.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/bisq/bisq-market/bisq-market.component.ts b/frontend/src/app/bisq/bisq-market/bisq-market.component.ts new file mode 100644 index 000000000..54f9b8b43 --- /dev/null +++ b/frontend/src/app/bisq/bisq-market/bisq-market.component.ts @@ -0,0 +1,64 @@ +import { Component, 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 { BisqApiService } from '../bisq-api.service'; + +@Component({ + selector: 'app-bisq-market', + templateUrl: './bisq-market.component.html', + styleUrls: ['./bisq-market.component.scss'] +}) +export class BisqMarketComponent implements OnInit { + hlocData$: Observable; + currency$: Observable; + radioGroupForm: FormGroup; + defaultInterval = 'half_hour'; + + constructor( + private route: ActivatedRoute, + private bisqApiService: BisqApiService, + private formBuilder: FormBuilder, + ) { } + + ngOnInit(): void { + this.radioGroupForm = this.formBuilder.group({ + interval: [this.defaultInterval], + }); + + this.currency$ = this.bisqApiService.getMarkets$() + .pipe( + switchMap((markets) => combineLatest([of(markets), this.route.paramMap])), + map(([markets, routeParams]) => { + const pair = routeParams.get('pair'); + console.log(markets); + return { + pair: pair.replace('_', '/').toUpperCase(), + market: markets[pair], + }; + }) + ); + + this.hlocData$ = combineLatest([ + this.route.paramMap, + merge(this.radioGroupForm.get('interval').valueChanges, of(this.defaultInterval)), + ]) + .pipe( + switchMap(([routeParams, interval]) => { + const pair = routeParams.get('pair'); + return this.bisqApiService.getMarketsHloc$(pair, interval); + }), + map((hloc) => { + return hloc.map((h) => { + h.time = h.period_start; + return h; + }); + }), + tap((data) => { + console.log(data); + }), + ); + } + +} diff --git a/frontend/src/app/bisq/bisq.module.ts b/frontend/src/app/bisq/bisq.module.ts index 9c8bfca0f..b469b4076 100644 --- a/frontend/src/app/bisq/bisq.module.ts +++ b/frontend/src/app/bisq/bisq.module.ts @@ -3,10 +3,13 @@ import { BisqRoutingModule } from './bisq.routing.module'; import { SharedModule } from '../shared/shared.module'; import { NgxBootstrapMultiselectModule } from 'ngx-bootrap-multiselect'; +import { LightweightChartsComponent } from './lightweight-charts/lightweight-charts.component'; +import { BisqMarketComponent } from './bisq-market/bisq-market.component'; import { BisqTransactionsComponent } from './bisq-transactions/bisq-transactions.component'; import { NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap'; import { BisqTransactionComponent } from './bisq-transaction/bisq-transaction.component'; import { BisqBlockComponent } from './bisq-block/bisq-block.component'; +import { BisqDashboardComponent } from './bisq-dashboard/bisq-dashboard.component'; import { BisqIconComponent } from './bisq-icon/bisq-icon.component'; import { BisqTransactionDetailsComponent } from './bisq-transaction-details/bisq-transaction-details.component'; import { BisqTransfersComponent } from './bisq-transfers/bisq-transfers.component'; @@ -34,6 +37,9 @@ import { BsqAmountComponent } from './bsq-amount/bsq-amount.component'; BisqAddressComponent, BisqStatsComponent, BsqAmountComponent, + LightweightChartsComponent, + BisqDashboardComponent, + BisqMarketComponent, ], imports: [ BisqRoutingModule, diff --git a/frontend/src/app/bisq/bisq.routing.module.ts b/frontend/src/app/bisq/bisq.routing.module.ts index 90bec63ce..3487ef48b 100644 --- a/frontend/src/app/bisq/bisq.routing.module.ts +++ b/frontend/src/app/bisq/bisq.routing.module.ts @@ -9,6 +9,8 @@ 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'; +import { BisqDashboardComponent } from './bisq-dashboard/bisq-dashboard.component'; +import { BisqMarketComponent } from './bisq-market/bisq-market.component'; const routes: Routes = [ { @@ -17,8 +19,16 @@ const routes: Routes = [ children: [ { path: '', + component: BisqDashboardComponent, + }, + { + path: 'transactions', component: BisqTransactionsComponent }, + { + path: 'market/:pair', + component: BisqMarketComponent, + }, { path: 'tx/:id', component: BisqTransactionComponent diff --git a/frontend/src/app/bisq/lightweight-charts/lightweight-charts.component.scss b/frontend/src/app/bisq/lightweight-charts/lightweight-charts.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/bisq/lightweight-charts/lightweight-charts.component.ts b/frontend/src/app/bisq/lightweight-charts/lightweight-charts.component.ts new file mode 100644 index 000000000..2e4d11e69 --- /dev/null +++ b/frontend/src/app/bisq/lightweight-charts/lightweight-charts.component.ts @@ -0,0 +1,68 @@ +import { createChart, CrosshairMode } from 'lightweight-charts'; +import { AfterViewInit, Component, ElementRef, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-lightweight-charts', + template: '', + styleUrls: ['./lightweight-charts.component.scss'] +}) +export class LightweightChartsComponent implements AfterViewInit, OnChanges, OnDestroy { + @Input() data: any; + lineSeries: any; + chart: any; + + constructor( + private element: ElementRef, + ) { + this.chart = createChart(this.element.nativeElement, { + width: 1110, + height: 500, + layout: { + backgroundColor: '#000000', + textColor: '#d1d4dc', + }, + crosshair: { + mode: CrosshairMode.Normal, + }, + grid: { + vertLines: { + visible: true, + color: 'rgba(42, 46, 57, 0.5)', + }, + horzLines: { + color: 'rgba(42, 46, 57, 0.5)', + }, + }, + }); + this.lineSeries = this.chart.addCandlestickSeries(); + } + + ngAfterViewInit(): void { + /* + lineSeries.setData([ + { time: '2019-04-11', value: 80.01 }, + { time: '2019-04-12', value: 96.63 }, + { time: '2019-04-13', value: 76.64 }, + { time: '2019-04-14', value: 81.89 }, + { time: '2019-04-15', value: 74.43 }, + { time: '2019-04-16', value: 80.01 }, + { time: '2019-04-17', value: 96.63 }, + { time: '2019-04-18', value: 76.64 }, + { time: '2019-04-19', value: 81.89 }, + { time: '2019-04-20', value: 74.43 }, + ]); + */ + } + + ngOnChanges() { + if (!this.data) { + return; + } + this.lineSeries.setData(this.data); + } + + ngOnDestroy() { + this.chart.remove(); + } + +} 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 084038b75..89a92b44a 100644 --- a/frontend/src/app/components/master-page/master-page.component.html +++ b/frontend/src/app/components/master-page/master-page.component.html @@ -27,21 +27,21 @@