From 90784deacc1b7d2830ab3c93acf16f11d2baaa9a Mon Sep 17 00:00:00 2001 From: softsimon Date: Tue, 11 May 2021 04:15:11 +0400 Subject: [PATCH] New Bisq Markets Dashboard Design fixes #476 --- .../bisq-dashboard.component.html | 8 +- .../bisq-main-dashboard.component.html | 124 +++++++++++ .../bisq-main-dashboard.component.scss | 84 ++++++++ .../bisq-main-dashboard.component.ts | 192 ++++++++++++++++++ .../bisq-trades/bisq-trades.component.html | 6 +- .../bisq/bisq-trades/bisq-trades.component.ts | 13 +- frontend/src/app/bisq/bisq.module.ts | 2 + frontend/src/app/bisq/bisq.routing.module.ts | 7 +- .../lightweight-charts-area.component.ts | 23 ++- .../lightweight-charts.component.ts | 31 ++- 10 files changed, 461 insertions(+), 29 deletions(-) create mode 100644 frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.html create mode 100644 frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.scss create mode 100644 frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.ts diff --git a/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.html b/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.html index 1f4e6c896..e42829e13 100644 --- a/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.html +++ b/frontend/src/app/bisq/bisq-dashboard/bisq-dashboard.component.html @@ -8,11 +8,9 @@
-
- - - -
+ + +

diff --git a/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.html b/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.html new file mode 100644 index 000000000..14fb03f78 --- /dev/null +++ b/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.html @@ -0,0 +1,124 @@ +
+ +
+ +
+
+
+
+
Bisq Market Price
+
+ {{ bisqMarketPrice | currency:'USD':'symbol':'1.2-2' }} +
+
+
+
+
+
+
+
Bisq Price Index
+
+ {{ usdPrice | currency:'USD':'symbol':'1.2-2' }} +
+
+
+
+
+ +
+
+
+
+
US Dollar - BTC/USD
+
+ + +
+ +
+
+
+
+
+
+
+
+
Bisq Trading Volume
+
+ + +
+ +
+
+
+
+
+
+ +
+ + +
+
+
+
+ Markets + Bitcoin Markets +
+ +
+ + + + + + + + + + + + + +
Currency PriceTrades (7d)
{{ ticker.name }}) + + + {{ ticker.last | currency: ticker.market.rsymbol }} + + {{ ticker.volume?.num_trades ? ticker.volume?.num_trades : 0 }}
+
+ + +
+
+
+
+
+
+
Latest Trades
+ + +
+
+
+
+
+ +
+ + + + + + + + +
+
+
+
+ + +
+
\ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.scss b/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.scss new file mode 100644 index 000000000..1e749c062 --- /dev/null +++ b/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.scss @@ -0,0 +1,84 @@ +#volumeHolder { + height: 500px; + background-color: #000; + overflow: hidden; + display: flex; + justify-content: center; +} + +.table { + max-width: 100%; + overflow: scroll; +} + +.loadingGraphs { + position: relative; + top: 45%; + z-index: 100; +} + +.table-container { + font-size: 13px; + @media(min-width: 576px){ + font-size: 16px; + } + &::-webkit-scrollbar { + display: none; + } +} + +.chart-container { + height: 350px; +} + +.big-fiat { + color: #3bcc49; + font-size: 26px; +} + + + .card { + background-color: #1d1f31; + height: 100%; + } + + .card-title { + color: #4a68b9; + font-size: 1rem; + } + + .info-block { + float: left; + width: 350px; + line-height: 25px; + } + + .progress { + display: inline-flex; + width: 100%; + background-color: #2d3348; + height: 1.1rem; + } + + .bg-warning { + background-color: #b58800 !important; + } + + .skeleton-loader { + max-width: 100%; + &.shorter { + max-width: 150px; + } + } + + .more-padding { + padding: 1.25rem 2rem 1.25rem 2rem; + } + + .graph-card { + height: 100%; + @media (min-width: 992px) { + height: 385px; + } + } + \ No newline at end of file diff --git a/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.ts b/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.ts new file mode 100644 index 000000000..9897d1e03 --- /dev/null +++ b/frontend/src/app/bisq/bisq-main-dashboard/bisq-main-dashboard.component.ts @@ -0,0 +1,192 @@ +import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { Observable, combineLatest, BehaviorSubject, of } from 'rxjs'; +import { map, share, switchMap } from 'rxjs/operators'; +import { SeoService } from 'src/app/services/seo.service'; +import { StateService } from 'src/app/services/state.service'; +import { WebsocketService } from 'src/app/services/websocket.service'; +import { BisqApiService } from '../bisq-api.service'; +import { Trade } from '../bisq.interfaces'; + +@Component({ + selector: 'app-main-bisq-dashboard', + templateUrl: './bisq-main-dashboard.component.html', + styleUrls: ['./bisq-main-dashboard.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class BisqMainDashboardComponent implements OnInit { + tickers$: Observable; + volumes$: Observable; + trades$: Observable; + sort$ = new BehaviorSubject('trades'); + hlocData$: Observable; + usdPrice$: Observable; + isLoadingGraph = true; + bisqMarketPrice = 0; + + allowCryptoCoins = ['usdc', 'l-btc', 'bsq']; + + constructor( + private websocketService: WebsocketService, + private bisqApiService: BisqApiService, + public stateService: StateService, + private seoService: SeoService, + ) { } + + ngOnInit(): void { + this.seoService.setTitle(`Markets`); + this.websocketService.want(['blocks']); + + this.usdPrice$ = this.stateService.conversions$.asObservable().pipe( + map((conversions) => conversions.USD) + ); + + this.volumes$ = this.bisqApiService.getAllVolumesDay$() + .pipe( + map((volumes) => { + const data = volumes.map((volume) => { + return { + time: volume.period_start, + value: volume.volume, + }; + }); + + const linesData = volumes.map((volume) => { + return { + time: volume.period_start, + value: volume.num_trades, + }; + }); + + return { + data: data, + linesData: linesData, + }; + }) + ); + + const getMarkets = this.bisqApiService.getMarkets$().pipe(share()); + + this.tickers$ = combineLatest([ + this.bisqApiService.getMarketsTicker$(), + getMarkets, + this.bisqApiService.getMarketVolumesByTime$('7d'), + ]) + .pipe( + map(([tickers, markets, volumes]) => { + + const newTickers = []; + for (const t in tickers) { + + if (!this.stateService.env.OFFICIAL_BISQ_MARKETS) { + const pair = t.split('_'); + if (pair[1] === 'btc' && this.allowCryptoCoins.indexOf(pair[0]) === -1) { + continue; + } + } + + const mappedTicker: any = tickers[t]; + + mappedTicker.pair_url = t; + mappedTicker.pair = t.replace('_', '/').toUpperCase(); + mappedTicker.market = markets[t]; + mappedTicker.volume = volumes[t]; + mappedTicker.name = `${mappedTicker.market.rtype === 'crypto' ? mappedTicker.market.lname : mappedTicker.market.rname} (${mappedTicker.market.rtype === 'crypto' ? mappedTicker.market.lsymbol : mappedTicker.market.rsymbol}`; + newTickers.push(mappedTicker); + } + return newTickers; + }), + switchMap((tickers) => combineLatest([this.sort$, of(tickers)])), + map(([sort, tickers]) => { + if (sort === 'trades') { + tickers.sort((a, b) => (b.volume && b.volume.num_trades || 0) - (a.volume && a.volume.num_trades || 0)); + } else if (sort === 'volumes') { + tickers.sort((a, b) => (b.volume && b.volume.volume || 0) - (a.volume && a.volume.volume || 0)); + } else if (sort === 'name') { + tickers.sort((a, b) => a.name.localeCompare(b.name)); + } + return tickers.slice(0, 10); + }) + ); + + this.trades$ = combineLatest([ + this.bisqApiService.getMarketTrades$('all'), + getMarkets, + ]) + .pipe( + map(([trades, markets]) => { + if (!this.stateService.env.OFFICIAL_BISQ_MARKETS) { + trades = trades.filter((trade) => { + const pair = trade.market.split('_'); + return !(pair[1] === 'btc' && this.allowCryptoCoins.indexOf(pair[0]) === -1); + }); + } + return trades.map((trade => { + trade._market = markets[trade.market]; + return trade; + })).slice(0, 10); + }) + ); + + this.hlocData$ = this.bisqApiService.getMarketsHloc$('btc_usd', 'day') + .pipe( + map((hlocData) => { + this.isLoadingGraph = false; + + hlocData = hlocData.map((h) => { + h.time = h.period_start; + return h; + }); + + const hlocVolume = hlocData.map((h) => { + return { + time: h.time, + value: h.volume_right, + color: h.close > h.avg ? 'rgba(0, 41, 74, 0.7)' : 'rgba(0, 41, 74, 1)', + }; + }); + + // Add whitespace + if (hlocData.length > 1) { + const newHloc = []; + newHloc.push(hlocData[0]); + + const period = 86400; + let periods = 0; + const startingDate = hlocData[0].period_start; + let index = 1; + while (true) { + periods++; + if (hlocData[index].period_start > startingDate + period * periods) { + newHloc.push({ + time: startingDate + period * periods, + }); + } else { + newHloc.push(hlocData[index]); + index++; + if (!hlocData[index]) { + break; + } + } + } + hlocData = newHloc; + } + + this.bisqMarketPrice = hlocData[hlocData.length - 1].close; + + return { + hloc: hlocData, + volume: hlocVolume, + }; + }), + ); + } + + trackByFn(index: number) { + return index; + } + + sort(by: string) { + this.sort$.next(by); + } + +} diff --git a/frontend/src/app/bisq/bisq-trades/bisq-trades.component.html b/frontend/src/app/bisq/bisq-trades/bisq-trades.component.html index 3f0b89db0..4ffffddc8 100644 --- a/frontend/src/app/bisq/bisq-trades/bisq-trades.component.html +++ b/frontend/src/app/bisq/bisq-trades/bisq-trades.component.html @@ -2,7 +2,7 @@ - + - @@ -39,7 +39,7 @@ - + diff --git a/frontend/src/app/bisq/bisq-trades/bisq-trades.component.ts b/frontend/src/app/bisq/bisq-trades/bisq-trades.component.ts index ac9f7c5f8..b984e715c 100644 --- a/frontend/src/app/bisq/bisq-trades/bisq-trades.component.ts +++ b/frontend/src/app/bisq/bisq-trades/bisq-trades.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnChanges, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; @Component({ @@ -7,7 +7,16 @@ import { Observable } from 'rxjs'; styleUrls: ['./bisq-trades.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class BisqTradesComponent { +export class BisqTradesComponent implements OnChanges { @Input() trades$: Observable; @Input() market: any; + @Input() view: 'all' | 'small' = 'all'; + + loadingColumns = [1, 2, 3, 4]; + + ngOnChanges() { + if (this.view === 'small') { + this.loadingColumns = [1, 2, 3]; + } + } } diff --git a/frontend/src/app/bisq/bisq.module.ts b/frontend/src/app/bisq/bisq.module.ts index 1ad40c850..77b0a26e8 100644 --- a/frontend/src/app/bisq/bisq.module.ts +++ b/frontend/src/app/bisq/bisq.module.ts @@ -11,6 +11,7 @@ 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 { BisqMainDashboardComponent } from './bisq-main-dashboard/bisq-main-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'; @@ -42,6 +43,7 @@ import { BisqTradesComponent } from './bisq-trades/bisq-trades.component'; BisqDashboardComponent, BisqMarketComponent, BisqTradesComponent, + BisqMainDashboardComponent, ], imports: [ BisqRoutingModule, diff --git a/frontend/src/app/bisq/bisq.routing.module.ts b/frontend/src/app/bisq/bisq.routing.module.ts index 7794b61bd..99f13909d 100644 --- a/frontend/src/app/bisq/bisq.routing.module.ts +++ b/frontend/src/app/bisq/bisq.routing.module.ts @@ -10,10 +10,15 @@ 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'; +import { BisqMainDashboardComponent } from './bisq-main-dashboard/bisq-main-dashboard.component'; const routes: Routes = [ - { + { path: '', + component: BisqMainDashboardComponent, + }, + { + path: 'markets', component: BisqDashboardComponent, }, { diff --git a/frontend/src/app/bisq/lightweight-charts-area/lightweight-charts-area.component.ts b/frontend/src/app/bisq/lightweight-charts-area/lightweight-charts-area.component.ts index 9c0cf3242..f2b1dc1c6 100644 --- a/frontend/src/app/bisq/lightweight-charts-area/lightweight-charts-area.component.ts +++ b/frontend/src/app/bisq/lightweight-charts-area/lightweight-charts-area.component.ts @@ -1,5 +1,5 @@ import { createChart, CrosshairMode, isBusinessDay } from 'lightweight-charts'; -import { ChangeDetectionStrategy, Component, ElementRef, Input, OnChanges, OnDestroy } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core'; @Component({ selector: 'app-lightweight-charts-area', @@ -7,10 +7,11 @@ import { ChangeDetectionStrategy, Component, ElementRef, Input, OnChanges, OnDes styleUrls: ['./lightweight-charts-area.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class LightweightChartsAreaComponent implements OnChanges, OnDestroy { +export class LightweightChartsAreaComponent implements OnInit, OnChanges, OnDestroy { @Input() data: any; @Input() lineData: any; @Input() precision: number; + @Input() height = 500; areaSeries: any; volumeSeries: any; @@ -18,12 +19,14 @@ export class LightweightChartsAreaComponent implements OnChanges, OnDestroy { lineSeries: any; container: any; - width = 1110; - height = 500; + width: number; constructor( private element: ElementRef, - ) { + ) { } + + ngOnInit() { + this.width = this.element.nativeElement.parentElement.offsetWidth; this.container = document.createElement('div'); const chartholder = this.element.nativeElement.appendChild(this.container); @@ -112,16 +115,22 @@ export class LightweightChartsAreaComponent implements OnChanges, OnDestroy { toolTip.style.left = left + 'px'; toolTip.style.top = top + 'px'; }); + + this.updateData(); } businessDayToString(businessDay) { return businessDay.year + '-' + businessDay.month + '-' + businessDay.day; } - ngOnChanges() { - if (!this.data) { + ngOnChanges(changes: SimpleChanges) { + if (!changes.value || changes.value.isFirstChange()){ return; } + this.updateData(); + } + + updateData() { this.areaSeries.setData(this.data); this.lineSeries.setData(this.lineData); } diff --git a/frontend/src/app/bisq/lightweight-charts/lightweight-charts.component.ts b/frontend/src/app/bisq/lightweight-charts/lightweight-charts.component.ts index 928030a49..38e1454c5 100644 --- a/frontend/src/app/bisq/lightweight-charts/lightweight-charts.component.ts +++ b/frontend/src/app/bisq/lightweight-charts/lightweight-charts.component.ts @@ -1,5 +1,5 @@ import { createChart, CrosshairMode } from 'lightweight-charts'; -import { ChangeDetectionStrategy, Component, ElementRef, Input, OnChanges, OnDestroy } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core'; @Component({ selector: 'app-lightweight-charts', @@ -7,10 +7,11 @@ import { ChangeDetectionStrategy, Component, ElementRef, Input, OnChanges, OnDes styleUrls: ['./lightweight-charts.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class LightweightChartsComponent implements OnChanges, OnDestroy { +export class LightweightChartsComponent implements OnInit, OnChanges, OnDestroy { @Input() data: any; @Input() volumeData: any; @Input() precision: number; + @Input() height = 500; lineSeries: any; volumeSeries: any; @@ -18,10 +19,12 @@ export class LightweightChartsComponent implements OnChanges, OnDestroy { constructor( private element: ElementRef, - ) { + ) { } + + ngOnInit() { this.chart = createChart(this.element.nativeElement, { - width: 1110, - height: 500, + width: this.element.nativeElement.parentElement.offsetWidth, + height: this.height, layout: { backgroundColor: '#000000', textColor: '#d1d4dc', @@ -52,12 +55,22 @@ export class LightweightChartsComponent implements OnChanges, OnDestroy { bottom: 0, }, }); + + this.updateData(); } - ngOnChanges() { - if (!this.data) { + ngOnChanges(changes: SimpleChanges) { + if (!changes.value || changes.value.isFirstChange()){ return; } + this.updateData(); + } + + ngOnDestroy() { + this.chart.remove(); + } + + updateData() { this.lineSeries.setData(this.data); this.volumeSeries.setData(this.volumeData); @@ -70,8 +83,4 @@ export class LightweightChartsComponent implements OnChanges, OnDestroy { }); } - ngOnDestroy() { - this.chart.remove(); - } - }
DatePricePrice @@ -14,7 +14,7 @@ {{ trade.trade_date | date:'yyyy-MM-dd HH:mm' }} + {{ trade.price | currency: (trade._market || market).rsymbol }} {{ trade.price | number: '1.2-' + (trade._market || market).rprecision }} {{ (trade._market || market).rsymbol }}