From 1a98a145416c31acaeb890bf589e3ac831e1a3b3 Mon Sep 17 00:00:00 2001 From: Miguel Medeiros Date: Mon, 16 Aug 2021 18:11:34 -0300 Subject: [PATCH 01/12] Add echart module. --- frontend/package-lock.json | 91 ++++++++++++++++++++++++++++++++++ frontend/package.json | 3 ++ frontend/src/app/app.module.ts | 4 ++ 3 files changed, 98 insertions(+) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 438f38aff..6e6c130f4 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -23,6 +23,7 @@ "@fortawesome/fontawesome-common-types": "^0.2.35", "@fortawesome/fontawesome-svg-core": "^1.2.35", "@fortawesome/free-solid-svg-icons": "^5.15.3", + "@juggle/resize-observer": "^3.3.1", "@mempool/chartist": "^0.11.4", "@mempool/mempool.js": "^2.2.4", "@ng-bootstrap/ng-bootstrap": "^7.0.0", @@ -32,9 +33,11 @@ "browserify": "^17.0.0", "clipboard": "^2.0.4", "domino": "^2.1.6", + "echarts": "^5.1.2", "express": "^4.17.1", "lightweight-charts": "^3.3.0", "ngx-bootrap-multiselect": "^2.0.0", + "ngx-echarts": "^7.0.1", "ngx-infinite-scroll": "^10.0.1", "qrcode": "^1.4.4", "rxjs": "^6.6.7", @@ -2252,6 +2255,11 @@ "schema-utils": "^2.7.0" } }, + "node_modules/@juggle/resize-observer": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.3.1.tgz", + "integrity": "sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw==" + }, "node_modules/@mempool/chartist": { "version": "0.11.4", "resolved": "https://registry.npmjs.org/@mempool/chartist/-/chartist-0.11.4.tgz", @@ -6931,6 +6939,20 @@ "safer-buffer": "^2.1.0" } }, + "node_modules/echarts": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.1.2.tgz", + "integrity": "sha512-okUhO4sw22vwZp+rTPNjd/bvTdpug4K4sHNHyrV8NdAncIX9/AarlolFqtJCAYKGFYhUBNjIWu1EznFrSWTFxg==", + "dependencies": { + "tslib": "2.0.3", + "zrender": "5.1.1" + } + }, + "node_modules/echarts/node_modules/tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -11916,6 +11938,18 @@ "tslib": "^2.0.0" } }, + "node_modules/ngx-echarts": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ngx-echarts/-/ngx-echarts-7.0.1.tgz", + "integrity": "sha512-DDRwEssQRjjgPElEjF1emORoUEnF6OMZ20xnQBLueSHitM7XnIUErYVe9GMmm/jCtI+iPvJPEedyxMPl62nHLw==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@juggle/resize-observer": ">=3.3.1", + "echarts": ">=5.0.0" + } + }, "node_modules/ngx-infinite-scroll": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/ngx-infinite-scroll/-/ngx-infinite-scroll-10.0.1.tgz", @@ -19926,6 +19960,19 @@ "dependencies": { "tslib": "^2.0.0" } + }, + "node_modules/zrender": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.1.1.tgz", + "integrity": "sha512-oeWlmUZPQdS9f5hK4pV21tHPqA3wgQ7CkKkw7l0CCBgWlJ/FP+lRgLFtUBW6yam4JX8y9CdHJo1o587VVrbcoQ==", + "dependencies": { + "tslib": "2.0.3" + } + }, + "node_modules/zrender/node_modules/tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" } }, "dependencies": { @@ -21914,6 +21961,11 @@ "schema-utils": "^2.7.0" } }, + "@juggle/resize-observer": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.3.1.tgz", + "integrity": "sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw==" + }, "@mempool/chartist": { "version": "0.11.4", "resolved": "https://registry.npmjs.org/@mempool/chartist/-/chartist-0.11.4.tgz", @@ -25975,6 +26027,22 @@ "safer-buffer": "^2.1.0" } }, + "echarts": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.1.2.tgz", + "integrity": "sha512-okUhO4sw22vwZp+rTPNjd/bvTdpug4K4sHNHyrV8NdAncIX9/AarlolFqtJCAYKGFYhUBNjIWu1EznFrSWTFxg==", + "requires": { + "tslib": "2.0.3", + "zrender": "5.1.1" + }, + "dependencies": { + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" + } + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -30136,6 +30204,14 @@ "tslib": "^2.0.0" } }, + "ngx-echarts": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ngx-echarts/-/ngx-echarts-7.0.1.tgz", + "integrity": "sha512-DDRwEssQRjjgPElEjF1emORoUEnF6OMZ20xnQBLueSHitM7XnIUErYVe9GMmm/jCtI+iPvJPEedyxMPl62nHLw==", + "requires": { + "tslib": "^2.0.0" + } + }, "ngx-infinite-scroll": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/ngx-infinite-scroll/-/ngx-infinite-scroll-10.0.1.tgz", @@ -36780,6 +36856,21 @@ "requires": { "tslib": "^2.0.0" } + }, + "zrender": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.1.1.tgz", + "integrity": "sha512-oeWlmUZPQdS9f5hK4pV21tHPqA3wgQ7CkKkw7l0CCBgWlJ/FP+lRgLFtUBW6yam4JX8y9CdHJo1o587VVrbcoQ==", + "requires": { + "tslib": "2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" + } + } } } } diff --git a/frontend/package.json b/frontend/package.json index 73e93b4d7..8d6edfb3d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -67,6 +67,7 @@ "@fortawesome/fontawesome-common-types": "^0.2.35", "@fortawesome/fontawesome-svg-core": "^1.2.35", "@fortawesome/free-solid-svg-icons": "^5.15.3", + "@juggle/resize-observer": "^3.3.1", "@mempool/chartist": "^0.11.4", "@mempool/mempool.js": "^2.2.4", "@ng-bootstrap/ng-bootstrap": "^7.0.0", @@ -76,9 +77,11 @@ "browserify": "^17.0.0", "clipboard": "^2.0.4", "domino": "^2.1.6", + "echarts": "^5.1.2", "express": "^4.17.1", "lightweight-charts": "^3.3.0", "ngx-bootrap-multiselect": "^2.0.0", + "ngx-echarts": "^7.0.1", "ngx-infinite-scroll": "^10.0.1", "qrcode": "^1.4.4", "rxjs": "^6.6.7", diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index b7719ebcf..d8b144847 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -3,6 +3,7 @@ import { NgModule } from '@angular/core'; import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { InfiniteScrollModule } from 'ngx-infinite-scroll'; +import { NgxEchartsModule } from 'ngx-echarts'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './components/app/app.component'; @@ -106,6 +107,9 @@ import { SponsorComponent } from './components/sponsor/sponsor.component'; NgbTypeaheadModule, FontAwesomeModule, SharedModule, + NgxEchartsModule.forRoot({ + echarts: () => import('echarts') + }) ], providers: [ ElectrsApiService, From 9b956ff88d15bd16c63c14b1aca3bec9ad5aa789 Mon Sep 17 00:00:00 2001 From: Miguel Medeiros Date: Sat, 21 Aug 2021 01:46:28 -0300 Subject: [PATCH 02/12] Add new component incoming-transactions-graph; Refactor component mempool-graph; Refactor component fee-distribution-graph; Add incoming-transactions-graph to dashboard; Add incoming-transactions-graph to statistics; Add incoming-transactions-graph to television; Add mempool-graph to dashboard; Add mempool-graph to statistics; Add mempool-graph to television; Remove chartist.component; --- frontend/package-lock.json | 14 - frontend/package.json | 1 - frontend/src/app/app.constants.ts | 40 + frontend/src/app/app.module.ts | 4 +- .../fee-distribution-graph.component.html | 8 +- .../fee-distribution-graph.component.scss | 0 .../fee-distribution-graph.component.ts | 124 +-- ...incoming-transactions-graph.component.html | 1 + .../incoming-transactions-graph.component.ts | 162 ++++ .../mempool-block.component.html | 6 +- .../mempool-block.component.scss | 16 +- .../mempool-graph.component.html | 7 +- .../mempool-graph/mempool-graph.component.ts | 219 ++++-- .../statistics/chartist.component.scss | 11 - .../statistics/chartist.component.ts | 740 ------------------ .../statistics/statistics.component.html | 14 +- .../statistics/statistics.component.scss | 6 +- .../statistics/statistics.component.ts | 40 - .../television/television.component.html | 8 +- .../television/television.component.scss | 21 +- .../app/dashboard/dashboard.component.html | 25 +- .../app/dashboard/dashboard.component.scss | 25 +- .../src/app/dashboard/dashboard.component.ts | 107 ++- frontend/src/styles.scss | 209 ++--- 24 files changed, 578 insertions(+), 1230 deletions(-) delete mode 100644 frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.scss create mode 100644 frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.html create mode 100644 frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts delete mode 100644 frontend/src/app/components/statistics/chartist.component.scss delete mode 100644 frontend/src/app/components/statistics/chartist.component.ts diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 6e6c130f4..e27b73b20 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -24,7 +24,6 @@ "@fortawesome/fontawesome-svg-core": "^1.2.35", "@fortawesome/free-solid-svg-icons": "^5.15.3", "@juggle/resize-observer": "^3.3.1", - "@mempool/chartist": "^0.11.4", "@mempool/mempool.js": "^2.2.4", "@ng-bootstrap/ng-bootstrap": "^7.0.0", "@nguniversal/express-engine": "11.2.1", @@ -2260,14 +2259,6 @@ "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.3.1.tgz", "integrity": "sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw==" }, - "node_modules/@mempool/chartist": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/@mempool/chartist/-/chartist-0.11.4.tgz", - "integrity": "sha512-wSemsw2NIWS7/SHxjDe9upSdUETxNRebY0ByaJzcONKUzJSUzMuSNmKEdD3kr/g02H++JvsXR2znLC6tYEAbPA==", - "engines": { - "node": ">=4.6.0" - } - }, "node_modules/@mempool/mempool.js": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/@mempool/mempool.js/-/mempool.js-2.2.4.tgz", @@ -21966,11 +21957,6 @@ "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.3.1.tgz", "integrity": "sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw==" }, - "@mempool/chartist": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/@mempool/chartist/-/chartist-0.11.4.tgz", - "integrity": "sha512-wSemsw2NIWS7/SHxjDe9upSdUETxNRebY0ByaJzcONKUzJSUzMuSNmKEdD3kr/g02H++JvsXR2znLC6tYEAbPA==" - }, "@mempool/mempool.js": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/@mempool/mempool.js/-/mempool.js-2.2.4.tgz", diff --git a/frontend/package.json b/frontend/package.json index 8d6edfb3d..5cf7e0c95 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -68,7 +68,6 @@ "@fortawesome/fontawesome-svg-core": "^1.2.35", "@fortawesome/free-solid-svg-icons": "^5.15.3", "@juggle/resize-observer": "^3.3.1", - "@mempool/chartist": "^0.11.4", "@mempool/mempool.js": "^2.2.4", "@ng-bootstrap/ng-bootstrap": "^7.0.0", "@nguniversal/express-engine": "11.2.1", diff --git a/frontend/src/app/app.constants.ts b/frontend/src/app/app.constants.ts index 74d4bf780..7197c37df 100644 --- a/frontend/src/app/app.constants.ts +++ b/frontend/src/app/app.constants.ts @@ -31,6 +31,46 @@ export const mempoolFeeColors = [ 'b9254b', ]; +export const chartColors = [ + "#D81B60", + "#8E24AA", + "#5E35B1", + "#3949AB", + "#1E88E5", + "#039BE5", + "#00ACC1", + "#00897B", + "#43A047", + "#7CB342", + "#C0CA33", + "#FDD835", + "#FFB300", + "#FB8C00", + "#F4511E", + "#6D4C41", + "#757575", + "#546E7A", + "#b71c1c", + "#880E4F", + "#4A148C", + "#311B92", + "#1A237E", + "#0D47A1", + "#01579B", + "#006064", + "#004D40", + "#1B5E20", + "#33691E", + "#827717", + "#F57F17", + "#FF6F00", + "#E65100", + "#BF360C", + "#3E2723", + "#212121", + "#263238", +]; + export const feeLevels = [1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 30, 40, 50, 60, 70, 80, 90, 100, 125, 150, 175, 200, 250, 300, 350, 400, 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000]; diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index d8b144847..d263db3ad 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -27,13 +27,13 @@ import { LiquidMasterPageComponent } from './components/liquid-master-page/liqui import { AboutComponent } from './components/about/about.component'; import { TelevisionComponent } from './components/television/television.component'; import { StatisticsComponent } from './components/statistics/statistics.component'; -import { ChartistComponent } from './components/statistics/chartist.component'; import { BlockchainBlocksComponent } from './components/blockchain-blocks/blockchain-blocks.component'; import { BlockchainComponent } from './components/blockchain/blockchain.component'; import { FooterComponent } from './components/footer/footer.component'; import { AudioService } from './services/audio.service'; import { MempoolBlockComponent } from './components/mempool-block/mempool-block.component'; import { FeeDistributionGraphComponent } from './components/fee-distribution-graph/fee-distribution-graph.component'; +import { IncomingTransactionsGraphComponent } from './components/incoming-transactions-graph/incoming-transactions-graph.component'; import { TimeSpanComponent } from './components/time-span/time-span.component'; import { SeoService } from './services/seo.service'; import { MempoolGraphComponent } from './components/mempool-graph/mempool-graph.component'; @@ -79,10 +79,10 @@ import { SponsorComponent } from './components/sponsor/sponsor.component'; TimeSpanComponent, AddressLabelsComponent, MempoolBlocksComponent, - ChartistComponent, FooterComponent, MempoolBlockComponent, FeeDistributionGraphComponent, + IncomingTransactionsGraphComponent, MempoolGraphComponent, AssetComponent, AssetsComponent, diff --git a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.html b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.html index 2fba6ec90..84cf34cfe 100644 --- a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.html +++ b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.html @@ -1,9 +1,5 @@ -
- - +
+
diff --git a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.scss b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts index 0d6ed9c74..873d42fb8 100644 --- a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts +++ b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts @@ -1,70 +1,80 @@ -import { Component, Input, OnChanges, ChangeDetectionStrategy } from '@angular/core'; -import * as Chartist from '@mempool/chartist'; +import { OnChanges } from '@angular/core'; +import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core'; @Component({ selector: 'app-fee-distribution-graph', templateUrl: './fee-distribution-graph.component.html', - styleUrls: ['./fee-distribution-graph.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class FeeDistributionGraphComponent implements OnChanges { - @Input() feeRange; +export class FeeDistributionGraphComponent implements OnInit, OnChanges { + @Input() data: any; + @Input() height: number | string = 210; + @Input() top: number | string = 20; + @Input() right: number | string = 22; + @Input() left: number | string = 30; - mempoolVsizeFeesData: any; mempoolVsizeFeesOptions: any; - feeLevels = [1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 30, 40, 50, 60, 70, 80, 90, 100, 125, 150, 175, 200, - 250, 300, 350, 400, 500]; + constructor() { } - constructor( - ) { } - - ngOnChanges() { - this.mempoolVsizeFeesOptions = { - showArea: true, - showLine: true, - fullWidth: true, - showPoint: true, - low: 0, - axisY: { - showLabel: false, - offset: 0 - }, - axisX: { - showGrid: true, - showLabel: false, - offset: 0 - }, - plugins: [ - Chartist.plugins.ctPointLabels({ - textAnchor: 'middle', - labelInterpolationFnc: (value) => Math.round(value) - }) - ] - }; - - const fees = this.feeRange; - const series = []; - - for (let i = 0; i < this.feeLevels.length; i++) { - let total = 0; - // for (let j = 0; j < fees.length; j++) { - for (const fee of fees) { - if (i === this.feeLevels.length - 1) { - if (fee >= this.feeLevels[i]) { - total += 1; - } - } else if (fee >= this.feeLevels[i] && fee < this.feeLevels[i + 1]) { - total += 1; - } - } - series.push(total); - } - - this.mempoolVsizeFeesData = { - series: [fees], - labels: fees.map((d, i) => i) - }; + ngOnInit() { + this.mountChart(); } + ngOnChanges() { + this.mountChart(); + } + + mountChart() { + this.mempoolVsizeFeesOptions = { + grid: { + height: '210', + right: '20', + top: '22', + left: '30', + }, + xAxis: { + type: 'category', + boundaryGap: false, + }, + yAxis: { + type: 'value', + splitLine: { + lineStyle: { + type: 'dotted', + color: '#ffffff66', + opacity: 0.25, + } + } + }, + series: [{ + data: this.data, + type: 'line', + label: { + show: true, + position: 'top', + color: '#ffffff', + textShadowBlur: 0, + formatter: (label: any) => { + return Math.floor(label.data); + }, + }, + smooth: true, + lineStyle: { + color: '#D81B60', + width: 4, + }, + itemStyle: { + color: '#b71c1c', + borderWidth: 10, + borderMiterLimit: 10, + opacity: 1, + }, + areaStyle: { + color: '#D81B60', + opacity: 1, + } + }] + }; + } } diff --git a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.html b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.html new file mode 100644 index 000000000..7c6de093e --- /dev/null +++ b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.html @@ -0,0 +1 @@ +
diff --git a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts new file mode 100644 index 000000000..dd4001821 --- /dev/null +++ b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts @@ -0,0 +1,162 @@ +import { Component, OnInit, Input, Inject, LOCALE_ID, ChangeDetectionStrategy } from '@angular/core'; +import { formatDate } from '@angular/common'; +import { EChartsOption } from 'echarts'; +import { OnChanges } from '@angular/core'; + +@Component({ + selector: 'app-incoming-transactions-graph', + templateUrl: './incoming-transactions-graph.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class IncomingTransactionsGraphComponent implements OnInit, OnChanges { + @Input() data: any; + @Input() theme: string; + @Input() height: number | string = '200'; + @Input() right: number | string = '10'; + @Input() top: number | string = '20'; + @Input() left: number | string = '50'; + + mempoolStatsChartOption: EChartsOption = {}; + + constructor( + @Inject(LOCALE_ID) private locale: string, + ) { } + + ngOnChanges(): void { + this.mountChart(); + } + + ngOnInit(): void { + this.mountChart(); + } + + mountChart(): void { + this.mempoolStatsChartOption = { + grid: { + height: this.height, + right: this.right, + top: this.top, + left: this.left, + }, + tooltip: { + trigger: 'axis', + position: (pos, params, el, elRect, size) => { + const obj = { top: -20 }; + obj[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 80; + return obj; + }, + extraCssText: `background: transparent; + border: none; + box-shadow: none;`, + axisPointer: { + type: 'cross', + label: { + formatter: (axis: any) => { + if (axis.axisDimension === 'y') { + return `${Math.floor(axis.value)}`; + } + if (axis.axisDimension === 'x') { + return axis.value; + } + }, + } + }, + formatter: (params: any) => { + const colorSpan = (color: string) => `
`; + let itemFormatted = '
' + params[0].axisValue + '
'; + params.map((item: any, index: number) => { + if (index < 26) { + itemFormatted += `
+ ${colorSpan(item.color)} +
+
${item.value}
+
`; + } + }); + if (this.theme !== '') { + return `
${itemFormatted}
`; + } + return `
${itemFormatted}
`; + } + }, + xAxis: { + type: 'category', + data: this.data.labels.map((value: any) => formatDate(value, 'HH:mm', this.locale)), + }, + yAxis: { + type: 'value', + splitLine: { + lineStyle: { + type: 'dotted', + color: '#ffffff66', + opacity: 0.25, + } + } + }, + series: [ + { + data: this.data.series[0], + type: 'line', + smooth: true, + showSymbol: false, + lineStyle: { + width: 3, + }, + markLine: { + silent: true, + symbol: 'none', + lineStyle: { + color: '#fff', + opacity: 0.75, + width: 2, + }, + data: [{ + yAxis: 1667, + label: { + show: false, + color: '#ffffff', + } + }], + } + }, + ], + visualMap: { + show: false, + top: 50, + right: 10, + pieces: [{ + gt: 0, + lte: 1667, + color: '#7CB342' + }, + { + gt: 1667, + lte: 2000, + color: '#FDD835' + }, + { + gt: 2000, + lte: 2500, + color: '#FFB300' + }, + { + gt: 2500, + lte: 3000, + color: '#FB8C00' + }, + { + gt: 3000, + lte: 3500, + color: '#F4511E' + }, + { + gt: 3500, + color: '#D81B60' + }], + outOfRange: { + color: '#999' + } + }, + }; + } +} diff --git a/frontend/src/app/components/mempool-block/mempool-block.component.html b/frontend/src/app/components/mempool-block/mempool-block.component.html index 744058614..2d72ff730 100644 --- a/frontend/src/app/components/mempool-block/mempool-block.component.html +++ b/frontend/src/app/components/mempool-block/mempool-block.component.html @@ -9,7 +9,7 @@
-
+
@@ -40,8 +40,8 @@
-
- +
+
diff --git a/frontend/src/app/components/mempool-block/mempool-block.component.scss b/frontend/src/app/components/mempool-block/mempool-block.component.scss index 4db5e0b10..f40515844 100644 --- a/frontend/src/app/components/mempool-block/mempool-block.component.scss +++ b/frontend/src/app/components/mempool-block/mempool-block.component.scss @@ -13,11 +13,8 @@ .fiat { font-size: 13px; - display: block; - @media (min-width: 992px) { - display: inline-block; - margin-left: 10px; - } + display: inline-block; + margin-left: 10px; } .table { @@ -38,4 +35,11 @@ h1 { float: left; margin-right: 10px; } -} \ No newline at end of file +} + +.chart-container{ + margin: 20px auto; + @media (min-width: 768px) { + margin: auto; + } +} diff --git a/frontend/src/app/components/mempool-graph/mempool-graph.component.html b/frontend/src/app/components/mempool-graph/mempool-graph.component.html index ff91f4a90..0ea3867a8 100644 --- a/frontend/src/app/components/mempool-graph/mempool-graph.component.html +++ b/frontend/src/app/components/mempool-graph/mempool-graph.component.html @@ -1,6 +1 @@ - - +
diff --git a/frontend/src/app/components/mempool-graph/mempool-graph.component.ts b/frontend/src/app/components/mempool-graph/mempool-graph.component.ts index b227c9884..2b384d7e8 100644 --- a/frontend/src/app/components/mempool-graph/mempool-graph.component.ts +++ b/frontend/src/app/components/mempool-graph/mempool-graph.component.ts @@ -1,10 +1,18 @@ import { Component, OnInit, Input, Inject, LOCALE_ID, ChangeDetectionStrategy, OnChanges } from '@angular/core'; import { formatDate } from '@angular/common'; import { VbytesPipe } from 'src/app/shared/pipes/bytes-pipe/vbytes.pipe'; -import * as Chartist from '@mempool/chartist'; import { OptimizedMempoolStats } from 'src/app/interfaces/node-api.interface'; import { StateService } from 'src/app/services/state.service'; import { StorageService } from 'src/app/services/storage.service'; +import { EChartsOption } from 'echarts'; +import { feeLevels, chartColors } from 'src/app/app.constants'; + +interface AxisObject { + axisDimension: string; + axisIndex: number; + seriesData: any; + value: string; +} @Component({ selector: 'app-mempool-graph', @@ -12,111 +20,50 @@ import { StorageService } from 'src/app/services/storage.service'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class MempoolGraphComponent implements OnInit, OnChanges { - @Input() data; + @Input() data: any[]; + @Input() limitFee = 300; + @Input() height: number | string = 200; + @Input() top: number | string = 20; + @Input() right: number | string = 10; + @Input() left: number | string = 75; @Input() dateSpan = '2h'; @Input() showLegend = true; - @Input() offsetX = 40; @Input() small = false; - mempoolVsizeFeesOptions: any; mempoolVsizeFeesData: any; + mempoolVsizeFeesOptions: EChartsOption; - isMobile = window.innerWidth <= 767.98; inverted: boolean; constructor( private vbytesPipe: VbytesPipe, private stateService: StateService, @Inject(LOCALE_ID) private locale: string, - private storageService: StorageService, ) { } ngOnInit(): void { - let labelHops = !this.showLegend ? 48 : 24; - if (this.small) { - labelHops = labelHops / 2; - } - - if (this.isMobile) { - labelHops = 96; - } - - const labelInterpolationFnc = (value: any, index: any) => { - switch (this.dateSpan) { - case '2h': - case '24h': - value = formatDate(value, 'HH:mm', this.locale); - break; - case '1w': - value = formatDate(value, 'dd/MM HH:mm', this.locale); - break; - case '1m': - case '3m': - case '6m': - case '1y': - value = formatDate(value, 'dd/MM', this.locale); - } - return index % labelHops === 0 ? value : null; - }; - - this.mempoolVsizeFeesOptions = { - showArea: true, - showLine: false, - fullWidth: true, - showPoint: false, - stackedLine: !this.inverted, - low: 0, - axisX: { - labelInterpolationFnc: labelInterpolationFnc, - offset: this.offsetX, - }, - axisY: { - labelInterpolationFnc: (value: number): any => this.vbytesPipe.transform(value, 2, 'vB', 'MvB', true), - offset: this.showLegend ? 160 : 60, - }, - plugins: this.inverted ? [Chartist.plugins.ctTargetLine({ value: this.stateService.blockVSize })] : [] - }; - - if (this.showLegend) { - const legendNames: string[] = [1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 30, 40, 50, 60, 70, 80, 90, 100, 125, 150, 175, 200, - 250, 300, 350, 400].map((sat, i, arr) => { - if (sat === 400) { - return '350+'; - } - if (i === 0) { - return '0 - 1'; - } - return arr[i - 1] + ' - ' + sat; - }); - // Only Liquid has lower than 1 sat/vb transactions - if (this.stateService.network !== 'liquid') { - legendNames.shift(); - } - this.mempoolVsizeFeesOptions.plugins.push( - Chartist.plugins.legend({ legendNames: legendNames }) - ); - } + this.mountFeeChart(); } ngOnChanges() { - this.inverted = this.storageService.getValue('inverted-graph') === 'true'; + // this.inverted = this.storageService.getValue('inverted-graph') === 'true'; this.mempoolVsizeFeesData = this.handleNewMempoolData(this.data.concat([])); + this.mountFeeChart(); } handleNewMempoolData(mempoolStats: OptimizedMempoolStats[]) { mempoolStats.reverse(); const labels = mempoolStats.map(stats => stats.added); - - const finalArrayVbyte = this.generateArray(mempoolStats); + const finalArrayVByte = this.generateArray(mempoolStats); // Only Liquid has lower than 1 sat/vb transactions if (this.stateService.network !== 'liquid') { - finalArrayVbyte.shift(); + finalArrayVByte.shift(); } return { labels: labels, - series: finalArrayVbyte + series: finalArrayVByte }; } @@ -134,12 +81,128 @@ export class MempoolGraphComponent implements OnInit, OnChanges { feesArray.push(0); } }); - if (this.inverted && finalArray.length) { - feesArray = feesArray.map((value, i) => value + finalArray[finalArray.length - 1][i]); - } + // if (this.inverted && finalArray.length) { + // feesArray = feesArray.map((value, i) => value + finalArray[finalArray.length - 1][i]); + // } finalArray.push(feesArray); } finalArray.reverse(); return finalArray; } + + mountFeeChart(){ + const { labels, series } = this.mempoolVsizeFeesData; + + const legendNames: string[] = feeLevels.map((sat, i, arr) => { + if (sat > this.limitFee) { return `${this.limitFee}+`; } + if (i === 0) { return '0 - 1'; } + return arr[i - 1] + ' - ' + sat; + }); + + const yAxisSeries = series.map((value: Array, index: number) => { + return { + name: labels[index].name, + type: 'line', + stack: 'total', + smooth: false, + lineStyle: { + width: 0, + opacity: 0, + }, + showSymbol: false, + areaStyle: { + opacity: 1, + color: chartColors[index], + }, + emphasis: { + focus: 'series' + }, + markLine: { + symbol: 'none', + itemStyle: { + borderWidth: 0, + borderColor: 'none', + color: '#fff', + }, + lineStyle: { + color: '#fff', + opacity: 0.75, + width: 2, + }, + }, + data: this.vbytesPipe.transform(value, 2, 'vB', 'MvB', true) + }; + }); + + this.mempoolVsizeFeesOptions = { + color: chartColors, + tooltip: { + trigger: 'axis', + position: (pos, params, el, elRect, size) => { + const positions = { top: -20 }; + positions[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 80; + return positions; + }, + extraCssText: `width: 150px; + background: transparent; + border: none; + box-shadow: none;`, + axisPointer: { + type: 'cross', + label: { + formatter: (axis: AxisObject) => { + if (axis.axisDimension === 'y') { + return `${this.vbytesPipe.transform(axis.value, 2, 'vB', 'MvB', true)}`; + } + if (axis.axisDimension === 'x') { + return axis.value; + } + }, + } + }, + formatter: (params: any) => { + const colorSpan = (index: number) => `
`; + const legendName = (index: number) => legendNames[index]; + let itemFormatted = '
' + params[0].axisValue + '
'; + params.map((item: any, index: number) => { + if (feeLevels[index - 1] < this.limitFee) { + itemFormatted += `
+ ${colorSpan(index - 1)} ${legendName(index)} +
+
${this.vbytesPipe.transform(item.value, 2, 'vB', 'MvB', true)}
+
`; + } + }); + return `
${itemFormatted}
`; + } + }, + grid: { + height: this.height, + right: this.right, + top: this.top, + left: this.left, + }, + xAxis: [ + { + type: 'category', + boundaryGap: false, + data: labels.map((value: any) => formatDate(value, 'HH:mm', this.locale)), + } + ], + yAxis: { + type: 'value', + axisLabel: { + formatter: (value: number) => (`${this.vbytesPipe.transform(value, 2, 'vB', 'MvB', true)}`), + }, + splitLine: { + lineStyle: { + type: 'dotted', + color: '#ffffff66', + opacity: 0.25, + } + } + }, + series: yAxisSeries + }; + } } diff --git a/frontend/src/app/components/statistics/chartist.component.scss b/frontend/src/app/components/statistics/chartist.component.scss deleted file mode 100644 index 1f72f6bb4..000000000 --- a/frontend/src/app/components/statistics/chartist.component.scss +++ /dev/null @@ -1,11 +0,0 @@ -.ct-legend { - top: 130px; - display: flex; - flex-direction: column-reverse; - @media (min-width: 653px) { - top: 90px; - } - } - .ct-legend.inverted { - flex-direction: column !important; - } \ No newline at end of file diff --git a/frontend/src/app/components/statistics/chartist.component.ts b/frontend/src/app/components/statistics/chartist.component.ts deleted file mode 100644 index d87c464f7..000000000 --- a/frontend/src/app/components/statistics/chartist.component.ts +++ /dev/null @@ -1,740 +0,0 @@ -import { - Component, - ElementRef, - Inject, - Input, - OnChanges, - OnDestroy, - OnInit, - PLATFORM_ID, - SimpleChanges, - ViewEncapsulation -} from '@angular/core'; - -import { isPlatformBrowser } from '@angular/common'; - -import * as Chartist from '@mempool/chartist'; - -/** - * Possible chart types - * @type {String} - */ -export type ChartType = 'Pie' | 'Bar' | 'Line'; - -export type ChartInterfaces = - | Chartist.IChartistPieChart - | Chartist.IChartistBarChart - | Chartist.IChartistLineChart; -export type ChartOptions = - | Chartist.IBarChartOptions - | Chartist.ILineChartOptions - | Chartist.IPieChartOptions; -export type ResponsiveOptionTuple = Chartist.IResponsiveOptionTuple< - ChartOptions ->; -export type ResponsiveOptions = ResponsiveOptionTuple[]; - -/** - * Represent a chart event. - * For possible values, check the Chartist docs. - */ -export interface ChartEvent { - [eventName: string]: (data: any) => void; -} - -@Component({ - selector: 'app-chartist', - template: '', - styleUrls: ['./chartist.component.scss'], - encapsulation: ViewEncapsulation.None, -}) -export class ChartistComponent implements OnInit, OnChanges, OnDestroy { - @Input() - // @ts-ignore - public data: Promise | Chartist.IChartistData; - - // @ts-ignore - @Input() public type: Promise | ChartType; - - @Input() - // @ts-ignore - public options: Promise | Chartist.IChartOptions; - - @Input() - // @ts-ignore - public responsiveOptions: Promise | ResponsiveOptions; - - // @ts-ignore - @Input() public events: ChartEvent; - - isBrowser: boolean = isPlatformBrowser(this.platformId); - - // @ts-ignore - public chart: ChartInterfaces; - - private element: HTMLElement; - - constructor( - element: ElementRef, - @Inject(PLATFORM_ID) private platformId: any, - ) { - this.element = element.nativeElement; - } - - public ngOnInit(): Promise { - if (!this.isBrowser) { - return; - } - - if (!this.type || !this.data) { - Promise.reject('Expected at least type and data.'); - } - - return this.renderChart().then((chart) => { - if (this.events !== undefined) { - this.bindEvents(chart); - } - - return chart; - }); - } - - public ngOnChanges(changes: SimpleChanges): void { - if (!this.isBrowser) { - return; - } - - this.update(changes); - } - - public ngOnDestroy(): void { - if (this.chart) { - this.chart.detach(); - } - } - - public renderChart(): Promise { - const promises: any[] = [ - this.type, - this.element, - this.data, - this.options, - this.responsiveOptions - ]; - - return Promise.all(promises).then((values) => { - const [type, ...args]: any = values; - - if (!(type in Chartist)) { - throw new Error(`${type} is not a valid chart type`); - } - - this.chart = (Chartist as any)[type](...args); - - return this.chart; - }); - } - - public update(changes: SimpleChanges): void { - if (!this.chart || 'type' in changes) { - this.renderChart(); - } else { - if (changes.data) { - this.data = changes.data.currentValue; - } - - if (changes.options) { - this.options = changes.options.currentValue; - } - - (this.chart as any).update(this.data, this.options); - } - } - - public bindEvents(chart: any): void { - for (const event of Object.keys(this.events)) { - chart.on(event, this.events[event]); - } - } -} - -/** - * Chartist.js plugin to display a "target" or "goal" line across the chart. - * Only tested with bar charts. Works for horizontal and vertical bars. - */ -(function(window, document, Chartist) { - 'use strict'; - - const defaultOptions = { - // The class name so you can style the text - className: 'ct-target-line', - // The axis to draw the line. y == vertical bars, x == horizontal - axis: 'y', - // What value the target line should be drawn at - value: null - }; - - Chartist.plugins = Chartist.plugins || {}; - - Chartist.plugins.ctTargetLine = function (options: any) { - options = Chartist.extend({}, defaultOptions, options); - return function ctTargetLine (chart: any) { - - chart.on('created', function(context: any) { - const projectTarget = { - y: function (chartRect: any, bounds: any, value: any) { - const targetLineY = chartRect.y1 - (chartRect.height() / bounds.max * value); - - return { - x1: chartRect.x1, - x2: chartRect.x2, - y1: targetLineY, - y2: targetLineY - }; - }, - x: function (chartRect: any, bounds: any, value: any) { - const targetLineX = chartRect.x1 + (chartRect.width() / bounds.max * value); - - return { - x1: targetLineX, - x2: targetLineX, - y1: chartRect.y1, - y2: chartRect.y2 - }; - } - }; - // @ts-ignore - const targetLine = projectTarget[options.axis](context.chartRect, context.bounds, options.value); - - context.svg.elem('line', targetLine, options.className); - }); - }; - }; - -}(null, null, Chartist)); - - -/** - * Chartist.js plugin to display a data label on top of the points in a line chart. - * - */ -/* global Chartist */ -(function(window, document, Chartist) { - 'use strict'; - - const defaultOptions = { - labelClass: 'ct-label', - labelOffset: { - x: 0, - y: -10 - }, - textAnchor: 'middle', - align: 'center', - labelInterpolationFnc: Chartist.noop - }; - - const labelPositionCalculation = { - point: function(data: any) { - return { - x: data.x, - y: data.y - }; - }, - bar: { - left: function(data: any) { - return { - x: data.x1, - y: data.y1 - }; - }, - center: function(data: any) { - return { - x: data.x1 + (data.x2 - data.x1) / 2, - y: data.y1 - }; - }, - right: function(data: any) { - return { - x: data.x2, - y: data.y1 - }; - } - } - }; - - Chartist.plugins = Chartist.plugins || {}; - Chartist.plugins.ctPointLabels = function(options: any) { - - options = Chartist.extend({}, defaultOptions, options); - - function addLabel(position: any, data: any) { - // if x and y exist concat them otherwise output only the existing value - const value = data.value.x !== undefined && data.value.y ? - (data.value.x + ', ' + data.value.y) : - data.value.y || data.value.x; - - data.group.elem('text', { - x: position.x + options.labelOffset.x, - y: position.y + options.labelOffset.y, - style: 'text-anchor: ' + options.textAnchor - }, options.labelClass).text(options.labelInterpolationFnc(value)); - } - - return function ctPointLabels(chart: any) { - if (chart instanceof Chartist.Line || chart instanceof Chartist.Bar) { - chart.on('draw', function(data: any) { - // @ts-ignore - const positonCalculator = labelPositionCalculation[data.type] - // @ts-ignore - && labelPositionCalculation[data.type][options.align] || labelPositionCalculation[data.type]; - if (positonCalculator) { - addLabel(positonCalculator(data), data); - } - }); - } - }; - }; - -}(null, null, Chartist)); - -const defaultOptions = { - className: '', - classNames: false, - removeAll: false, - legendNames: false, - clickable: true, - onClick: null, - position: 'top' -}; - -Chartist.plugins.legend = function (options: any) { - let cachedDOMPosition; - let cacheInactiveLegends: { [key:number]: boolean } = {}; - // Catch invalid options - if (options && options.position) { - if (!(options.position === 'top' || options.position === 'bottom' || options.position instanceof HTMLElement)) { - throw Error('The position you entered is not a valid position'); - } - if (options.position instanceof HTMLElement) { - // Detatch DOM element from options object, because Chartist.extend - // currently chokes on circular references present in HTMLElements - cachedDOMPosition = options.position; - delete options.position; - } - } - - options = Chartist.extend({}, defaultOptions, options); - - if (cachedDOMPosition) { - // Reattatch the DOM Element position if it was removed before - options.position = cachedDOMPosition; - } - - return function legend(chart: any) { - - var isSelfUpdate = false; - - chart.on('created', function (data: any) { - - const useLabels = chart instanceof Chartist.Pie && chart.data.labels && chart.data.labels.length; - const legendNames = getLegendNames(useLabels); - var dirtyChartData = (chart.data.series.length < legendNames.length); - - if (isSelfUpdate || dirtyChartData) - return; - - function removeLegendElement() { - const legendElement = chart.container.querySelector('.ct-legend'); - if (legendElement) { - legendElement.parentNode.removeChild(legendElement); - } - } - - // Set a unique className for each series so that when a series is removed, - // the other series still have the same color. - function setSeriesClassNames() { - chart.data.series = chart.data.series.map(function (series: any, seriesIndex: any) { - if (typeof series !== 'object') { - series = { - value: series - }; - } - series.className = series.className || chart.options.classNames.series + '-' + Chartist.alphaNumerate(seriesIndex); - return series; - }); - } - - function createLegendElement() { - const legendElement = document.createElement('ul'); - legendElement.className = 'ct-legend'; - const inverted = localStorage.getItem('inverted-graph') === 'true'; - if (inverted){ - legendElement.classList.add('inverted'); - } - if (chart instanceof Chartist.Pie) { - legendElement.classList.add('ct-legend-inside'); - } - if (typeof options.className === 'string' && options.className.length > 0) { - legendElement.classList.add(options.className); - } - if (chart.options.width) { - legendElement.style.cssText = 'width: ' + chart.options.width + 'px;margin: 0 auto;'; - } - return legendElement; - } - - // Get the right array to use for generating the legend. - function getLegendNames(useLabels: any) { - return options.legendNames || (useLabels ? chart.data.labels : chart.data.series); - } - - // Initialize the array that associates series with legends. - // -1 indicates that there is no legend associated with it. - function initSeriesMetadata(useLabels: any) { - const seriesMetadata = new Array(chart.data.series.length); - for (let i = 0; i < chart.data.series.length; i++) { - seriesMetadata[i] = { - data: chart.data.series[i], - label: useLabels ? chart.data.labels[i] : null, - legend: -1 - }; - } - return seriesMetadata; - } - - function createNameElement(i: any, legendText: any, classNamesViable: any) { - const li = document.createElement('li'); - li.classList.add('ct-series-' + i); - // Append specific class to a legend element, if viable classes are given - if (classNamesViable) { - li.classList.add(options.classNames[i]); - } - li.setAttribute('data-legend', i); - li.textContent = legendText; - return li; - } - - // Append the legend element to the DOM - function appendLegendToDOM(legendElement: any) { - if (!(options.position instanceof HTMLElement)) { - switch (options.position) { - case 'top': - chart.container.insertBefore(legendElement, chart.container.childNodes[0]); - break; - - case 'bottom': - chart.container.insertBefore(legendElement, null); - break; - } - } else { - // Appends the legend element as the last child of a given HTMLElement - options.position.insertBefore(legendElement, null); - } - } - - function updateChart(newSeries: any, newLabels:any, useLabels: any) { - chart.data.series = newSeries; - if (useLabels) { - chart.data.labels = newLabels; - } - - isSelfUpdate = true; - chart.update(); - isSelfUpdate = false; - } - - function addClickHandler(legendElement: any, legends: any, seriesMetadata: any, useLabels: any) { - legendElement.addEventListener('click', function(e: any) { - const li = e.target; - if (li.parentNode !== legendElement || !li.hasAttribute('data-legend')) - return; - e.preventDefault(); - - const legendIndex = parseInt(li.getAttribute('data-legend')); - const legend = legends[legendIndex]; - - const activateLegend = (_legendIndex: number): void => { - legends[_legendIndex].active = true; - legendElement.childNodes[_legendIndex].classList.remove('inactive'); - - cacheInactiveLegends[_legendIndex] = false; - } - - const deactivateLegend = (_legendIndex: number): void => { - legends[_legendIndex].active = false; - legendElement.childNodes[_legendIndex].classList.add('inactive'); - cacheInactiveLegends[_legendIndex] = true; - } - - for (let i = legends.length - 1; i >= 0; i--) { - if (i >= legendIndex) { - if (!legend.active) activateLegend(i); - } else { - if (legend.active) deactivateLegend(i); - } - } - // Make sure all values are undefined (falsy) when clicking the first legend - // After clicking the first legend all indices should be falsy - if (legendIndex === 0) cacheInactiveLegends = {}; - - const newSeries = []; - const newLabels = []; - - for (let i = 0; i < seriesMetadata.length; i++) { - if (seriesMetadata[i].legend !== -1 && legends[seriesMetadata[i].legend].active) { - newSeries.push(seriesMetadata[i].data); - newLabels.push(seriesMetadata[i].label); - } - } - - updateChart(newSeries, newLabels, useLabels); - - if (options.onClick) { - options.onClick(chart, e); - } - }); - } - - removeLegendElement(); - - const legendElement = createLegendElement(); - const seriesMetadata = initSeriesMetadata(useLabels); - const legends: any = []; - - // Check if given class names are viable to append to legends - const classNamesViable = Array.isArray(options.classNames) && options.classNames.length === legendNames.length; - - var activeSeries = []; - var activeLabels = []; - - // Loop through all legends to set each name in a list item. - legendNames.forEach(function (legend: any, i: any) { - const legendText = legend.name || legend; - const legendSeries = legend.series || [i]; - - const li = createNameElement(i, legendText, classNamesViable); - // If the value is undefined or false, isActive is true - const isActive: boolean = !cacheInactiveLegends[i]; - if (isActive) { - activeSeries.push(seriesMetadata[i].data); - activeLabels.push(seriesMetadata[i].label); - } else { - li.classList.add('inactive'); - } - legendElement.appendChild(li); - - legendSeries.forEach(function(seriesIndex: any) { - seriesMetadata[seriesIndex].legend = i; - }); - - legends.push({ - text: legendText, - series: legendSeries, - active: isActive - }); - - }); - - appendLegendToDOM(legendElement); - - if (options.clickable) { - setSeriesClassNames(); - addClickHandler(legendElement, legends, seriesMetadata, useLabels); - } - - updateChart(activeSeries, activeLabels, useLabels); - - }); - }; -}; - -Chartist.plugins.tooltip = function (options: any) { - options = Chartist.extend({}, defaultOptions, options); - - return function tooltip(chart: any) { - let tooltipSelector = options.pointClass; - if (chart instanceof Chartist.Bar) { - tooltipSelector = 'ct-bar'; - } else if (chart instanceof Chartist.Pie) { - // Added support for donut graph - if (chart.options.donut) { - tooltipSelector = 'ct-slice-donut'; - } else { - tooltipSelector = 'ct-slice-pie'; - } - } - - const $chart = chart.container; - let $toolTip = $chart.querySelector('.chartist-tooltip'); - if (!$toolTip) { - $toolTip = document.createElement('div'); - $toolTip.className = (!options.class) ? 'chartist-tooltip' : 'chartist-tooltip ' + options.class; - if (!options.appendToBody) { - $chart.appendChild($toolTip); - } else { - document.body.appendChild($toolTip); - } - } - let height = $toolTip.offsetHeight; - let width = $toolTip.offsetWidth; - - hide($toolTip); - - function on(event: any, selector: any, callback: any) { - $chart.addEventListener(event, function (e: any) { - if (!selector || hasClass(e.target, selector)) { - callback(e); - } - }); - } - - on('mouseover', tooltipSelector, function (event: any) { - const $point = event.target; - let tooltipText = ''; - - const isPieChart = (chart instanceof Chartist.Pie) ? $point : $point.parentNode; - const seriesName = (isPieChart) ? $point.parentNode.getAttribute('ct:meta') || $point.parentNode.getAttribute('ct:series-name') : ''; - let meta = $point.getAttribute('ct:meta') || seriesName || ''; - const hasMeta = !!meta; - let value = $point.getAttribute('ct:value'); - - if (options.transformTooltipTextFnc && typeof options.transformTooltipTextFnc === 'function') { - value = options.transformTooltipTextFnc(value, $point.parentNode.getAttribute('class')); - } - - if (options.tooltipFnc && typeof options.tooltipFnc === 'function') { - tooltipText = options.tooltipFnc(meta, value); - } else { - if (options.metaIsHTML) { - const txt = document.createElement('textarea'); - txt.innerHTML = meta; - meta = txt.value; - } - - meta = '' + meta + ''; - - if (hasMeta) { - tooltipText += meta + '
'; - } else { - // For Pie Charts also take the labels into account - // Could add support for more charts here as well! - if (chart instanceof Chartist.Pie) { - const label = next($point, 'ct-label'); - if (label) { - tooltipText += text(label) + '
'; - } - } - } - - if (value) { - if (options.currency) { - if (options.currencyFormatCallback != undefined) { - value = options.currencyFormatCallback(value, options); - } else { - value = options.currency + value.replace(/(\d)(?=(\d{3})+(?:\.\d+)?$)/g, '$1,'); - } - } - value = '' + value + ''; - tooltipText += value; - } - } - - if (tooltipText) { - $toolTip.innerHTML = tooltipText; - setPosition(event); - show($toolTip); - - // Remember height and width to avoid wrong position in IE - height = $toolTip.offsetHeight; - width = $toolTip.offsetWidth; - } - }); - - on('mouseout', tooltipSelector, function () { - hide($toolTip); - }); - - on('mousemove', null, function (event: any) { - if (false === options.anchorToPoint) { - setPosition(event); - } - }); - - function setPosition(event: any) { - height = height || $toolTip.offsetHeight; - width = width || $toolTip.offsetWidth; - const offsetX = - width / 2 + options.tooltipOffset.x - const offsetY = - height + options.tooltipOffset.y; - let anchorX, anchorY; - - if (!options.appendToBody) { - const box = $chart.getBoundingClientRect(); - const left = event.pageX - box.left - window.pageXOffset ; - const top = event.pageY - box.top - window.pageYOffset ; - - if (true === options.anchorToPoint && event.target.x2 && event.target.y2) { - anchorX = parseInt(event.target.x2.baseVal.value); - anchorY = parseInt(event.target.y2.baseVal.value); - } - - $toolTip.style.top = (anchorY || top) + offsetY + 'px'; - $toolTip.style.left = (anchorX || left) + offsetX + 'px'; - } else { - $toolTip.style.top = event.pageY + offsetY + 'px'; - $toolTip.style.left = event.pageX + offsetX + 'px'; - } - } - } -}; - -Chartist.plugins.ctPointLabels = (options) => { - return function ctPointLabels(chart) { - const defaultOptions2 = { - labelClass: 'ct-point-label', - labelOffset: { - x: 0, - y: -7 - }, - textAnchor: 'middle' - }; - options = Chartist.extend({}, defaultOptions2, options); - - if (chart instanceof Chartist.Line) { - chart.on('draw', (data) => { - if (data.type === 'point') { - data.group.elem('text', { - x: data.x + options.labelOffset.x, - y: data.y + options.labelOffset.y, - style: 'text-anchor: ' + options.textAnchor - }, options.labelClass).text(options.labelInterpolationFnc(data.value.y)); // 07.11.17 added ".y" - } - }); - } - }; -}; - -function show(element: any) { - if (!hasClass(element, 'tooltip-show')) { - element.className = element.className + ' tooltip-show'; - } -} - -function hide(element: any) { - const regex = new RegExp('tooltip-show' + '\\s*', 'gi'); - element.className = element.className.replace(regex, '').trim(); -} - -function hasClass(element: any, className: any) { - return (' ' + element.getAttribute('class') + ' ').indexOf(' ' + className + ' ') > -1; -} - -function next(element: any, className: any) { - do { - element = element.nextSibling; - } while (element && !hasClass(element, className)); - return element; -} - -function text(element: any) { - return element.innerText || element.textContent; -} diff --git a/frontend/src/app/components/statistics/statistics.component.html b/frontend/src/app/components/statistics/statistics.component.html index 8aaf5d833..b932c746c 100644 --- a/frontend/src/app/components/statistics/statistics.component.html +++ b/frontend/src/app/components/statistics/statistics.component.html @@ -36,12 +36,12 @@ 1Y
- +
-
- +
+
@@ -53,12 +53,8 @@ Transaction vBytes per second (vB/s)
-
- - +
+
diff --git a/frontend/src/app/components/statistics/statistics.component.scss b/frontend/src/app/components/statistics/statistics.component.scss index 04f8734bd..31eecb8c3 100644 --- a/frontend/src/app/components/statistics/statistics.component.scss +++ b/frontend/src/app/components/statistics/statistics.component.scss @@ -56,4 +56,8 @@ text-align: center; height: 80vh; justify-content: center; -} \ No newline at end of file +} + +.incoming-transactions-graph { + height: 600px; +} diff --git a/frontend/src/app/components/statistics/statistics.component.ts b/frontend/src/app/components/statistics/statistics.component.ts index 39350c247..733762764 100644 --- a/frontend/src/app/components/statistics/statistics.component.ts +++ b/frontend/src/app/components/statistics/statistics.component.ts @@ -9,7 +9,6 @@ import { OptimizedMempoolStats } from '../../interfaces/node-api.interface'; import { WebsocketService } from '../../services/websocket.service'; import { ApiService } from '../../services/api.service'; -import * as Chartist from '@mempool/chartist'; import { StateService } from 'src/app/services/state.service'; import { SeoService } from 'src/app/services/seo.service'; import { StorageService } from 'src/app/services/storage.service'; @@ -31,8 +30,6 @@ export class StatisticsComponent implements OnInit { mempoolUnconfirmedTransactionsData: any; mempoolTransactionsWeightPerSecondData: any; - transactionsWeightPerSecondOptions: any; - radioGroupForm: FormGroup; inverted: boolean; graphWindowPreference: String; @@ -64,43 +61,6 @@ export class StatisticsComponent implements OnInit { dateSpan: this.graphWindowPreference }); - const labelInterpolationFnc = (value: any, index: any) => { - switch (this.graphWindowPreference) { - case '2h': - case '24h': - value = formatDate(value, 'HH:mm', this.locale); - break; - case '1w': - value = formatDate(value, 'dd/MM HH:mm', this.locale); - break; - case '1m': - case '3m': - case '6m': - case '1y': - value = formatDate(value, 'dd/MM', this.locale); - } - - return index % labelHops === 0 ? value : null; - }; - - this.transactionsWeightPerSecondOptions = { - showArea: false, - showLine: true, - showPoint: false, - low: 0, - axisY: { - offset: 40 - }, - axisX: { - labelInterpolationFnc: labelInterpolationFnc - }, - plugins: [ - Chartist.plugins.ctTargetLine({ - value: 1667 - }), - ] - }; - this.route .fragment .subscribe((fragment) => { diff --git a/frontend/src/app/components/television/television.component.html b/frontend/src/app/components/television/television.component.html index e8a423b0b..d12564c30 100644 --- a/frontend/src/app/components/television/television.component.html +++ b/frontend/src/app/components/television/television.component.html @@ -4,12 +4,10 @@
-
- -
- +
+
+
-
diff --git a/frontend/src/app/components/television/television.component.scss b/frontend/src/app/components/television/television.component.scss index f45908655..8629fbcb7 100644 --- a/frontend/src/app/components/television/television.component.scss +++ b/frontend/src/app/components/television/television.component.scss @@ -16,30 +16,21 @@ } .chart-holder { - height: calc(100vh - 270px); - min-height: 525px; - padding-left: 20px; - width: 98.5%; - padding-top: 20px; - @media(min-width: 992px){ - padding-top: 10px; - } - @media(min-height: 800px){ - padding-top: 60px !important; - } + height: 650px; + width: 100%; + margin: 30px auto; } .blockchain-wrapper { - display: flex; height: 100%; min-height: 240px; position: relative; top: -20px; @media(min-height: 800px) { - top: 10px; + top: 30px; } - + .position-container { position: absolute; left: 50%; @@ -89,4 +80,4 @@ display: flex; margin-top: 0px; flex-direction: column; -} \ No newline at end of file +} diff --git a/frontend/src/app/dashboard/dashboard.component.html b/frontend/src/app/dashboard/dashboard.component.html index 47990f639..5eab01618 100644 --- a/frontend/src/app/dashboard/dashboard.component.html +++ b/frontend/src/app/dashboard/dashboard.component.html @@ -47,8 +47,8 @@
-
- +
+
@@ -59,12 +59,8 @@

-
- - +
+
@@ -197,13 +193,14 @@
-
 
+
 
{{ mempoolInfoData.value.vBytesPerSecond | ceil | number }} vB/s
+
Difficulty Adjustment
@@ -228,11 +225,11 @@ - {{ epochData.change | absolute | number: '1.2-2' }} + {{ epochData.change | absolute | number: '1.2-2' }} %
- Previous: + Previous: @@ -257,6 +254,12 @@ + +
+
+
+
+
diff --git a/frontend/src/app/dashboard/dashboard.component.scss b/frontend/src/app/dashboard/dashboard.component.scss index 37a37d529..a3877e855 100644 --- a/frontend/src/app/dashboard/dashboard.component.scss +++ b/frontend/src/app/dashboard/dashboard.component.scss @@ -58,11 +58,11 @@ display: block; @media (min-width: 485px) { display: flex; - flex-direction: row; + flex-direction: row; } h5 { margin-bottom: 10px; - } + } .item { width: 50%; margin: 0px auto 20px; @@ -131,7 +131,7 @@ .latest-transactions { width: 100%; text-align: left; - table-layout:fixed; + table-layout:fixed; tr, td, th { border: 0px; } @@ -220,6 +220,11 @@ .mempool-graph { height: 250px; } +.loadingGraphs{ + height: 250px; + display: grid; + place-items: center; +} .inc-tx-progress-bar { max-width: 250px; @@ -247,7 +252,7 @@ color: #ffffff66; font-size: 12px; } - .item { + .item { padding: 0 5px; width: 100%; &:nth-child(1) { @@ -276,25 +281,25 @@ justify-content: space-between; @media (min-width: 376px) { flex-direction: row; - } + } .item { max-width: 150px; margin: 0; width: -webkit-fill-available; @media (min-width: 376px) { margin: 0 auto 0px; - } + } &:first-child{ display: none; @media (min-width: 485px) { display: block; - } + } @media (min-width: 768px) { display: none; - } + } @media (min-width: 992px) { display: block; - } + } } &:last-child { margin-bottom: 0; @@ -355,4 +360,4 @@ .previous-retarget-sign { margin-right: -2px; font-size: 10px; -} \ No newline at end of file +} diff --git a/frontend/src/app/dashboard/dashboard.component.ts b/frontend/src/app/dashboard/dashboard.component.ts index 38a7eaa85..7911fc204 100644 --- a/frontend/src/app/dashboard/dashboard.component.ts +++ b/frontend/src/app/dashboard/dashboard.component.ts @@ -6,11 +6,11 @@ import { OptimizedMempoolStats } from '../interfaces/node-api.interface'; import { MempoolInfo, TransactionStripped } from '../interfaces/websocket.interface'; import { ApiService } from '../services/api.service'; import { StateService } from '../services/state.service'; -import * as Chartist from '@mempool/chartist'; import { formatDate } from '@angular/common'; import { WebsocketService } from '../services/websocket.service'; import { SeoService } from '../services/seo.service'; import { StorageService } from '../services/storage.service'; +import { EChartsOption } from 'echarts'; interface MempoolBlocksData { blocks: number; @@ -34,7 +34,7 @@ interface MempoolInfoData { memPoolInfo: MempoolInfo; vBytesPerSecond: number; progressWidth: string; - progressClass: string; + progressColor: string; } interface MempoolStatsData { @@ -74,15 +74,15 @@ export class DashboardComponent implements OnInit { ) { } ngOnInit(): void { - this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$; this.seoService.resetTitle(); this.websocketService.want(['blocks', 'stats', 'mempool-blocks', 'live-2h-chart']); this.network$ = merge(of(''), this.stateService.networkChanged$); this.collapseLevel = this.storageService.getValue('dashboard-collapsed') || 'one'; - this.mempoolLoadingStatus$ = this.stateService.loadingIndicators$.pipe( - map((indicators) => indicators.mempool !== undefined ? indicators.mempool : 100) - ); + this.mempoolLoadingStatus$ = this.stateService.loadingIndicators$ + .pipe( + map((indicators) => indicators.mempool !== undefined ? indicators.mempool : 100) + ); this.mempoolInfoData$ = combineLatest([ this.stateService.mempoolInfo$, @@ -92,11 +92,21 @@ export class DashboardComponent implements OnInit { map(([mempoolInfo, vbytesPerSecond]) => { const percent = Math.round((Math.min(vbytesPerSecond, this.vBytesPerSecondLimit) / this.vBytesPerSecondLimit) * 100); - let progressClass = 'bg-danger'; - if (percent <= 75) { - progressClass = 'bg-success'; - } else if (percent <= 99) { - progressClass = 'bg-warning'; + let progressColor = '#7CB342'; + if (vbytesPerSecond > 1667) { + progressColor = '#FDD835'; + } + if (vbytesPerSecond > 2000) { + progressColor = '#FFB300'; + } + if (vbytesPerSecond > 2500) { + progressColor = '#FB8C00'; + } + if (vbytesPerSecond > 3000) { + progressColor = '#F4511E'; + } + if (vbytesPerSecond > 3500) { + progressColor = '#D81B60'; } const mempoolSizePercentage = (mempoolInfo.usage / mempoolInfo.maxmempool * 100); @@ -111,7 +121,7 @@ export class DashboardComponent implements OnInit { memPoolInfo: mempoolInfo, vBytesPerSecond: vbytesPerSecond, progressWidth: percent + '%', - progressClass: progressClass, + progressColor: progressColor, mempoolSizeProgress: mempoolSizeProgress, }; }) @@ -164,7 +174,7 @@ export class DashboardComponent implements OnInit { } let colorPreviousAdjustments = '#dc3545'; - if (previousRetarget){ + if (previousRetarget) { if (previousRetarget >= 0) { colorPreviousAdjustments = '#3bcc49'; } @@ -191,7 +201,6 @@ export class DashboardComponent implements OnInit { }) ); - this.mempoolBlocksData$ = this.stateService.mempoolBlocks$ .pipe( map((mempoolBlocks) => { @@ -226,50 +235,32 @@ export class DashboardComponent implements OnInit { }, []), ); - this.mempoolStats$ = this.stateService.connectionState$.pipe( - filter((state) => state === 2), - switchMap(() => this.apiService.list2HStatistics$()), - switchMap((mempoolStats) => { - return merge( - this.stateService.live2Chart$ - .pipe( - scan((acc, stats) => { - acc.unshift(stats); - acc = acc.slice(0, 120); - return acc; - }, mempoolStats) - ), - of(mempoolStats) - ); - }), - map((mempoolStats) => { - return { - mempool: mempoolStats, - weightPerSecond: this.handleNewMempoolData(mempoolStats.concat([])), - }; - }), - share(), - ); - - this.transactionsWeightPerSecondOptions = { - showArea: false, - showLine: true, - fullWidth: true, - showPoint: false, - low: 0, - axisY: { - offset: 40 - }, - axisX: { - labelInterpolationFnc: (value: any, index: any) => index % 24 === 0 ? formatDate(value, 'HH:mm', this.locale) : null, - offset: 20 - }, - plugins: [ - Chartist.plugins.ctTargetLine({ - value: 1667 - }), - ] - }; + this.mempoolStats$ = this.stateService.connectionState$ + .pipe( + filter((state) => state === 2), + switchMap(() => this.apiService.list2HStatistics$()), + switchMap((mempoolStats) => { + return merge( + this.stateService.live2Chart$ + .pipe( + scan((acc, stats) => { + acc.unshift(stats); + acc = acc.slice(0, 120); + return acc; + }, mempoolStats) + ), + of(mempoolStats) + ); + }), + map((mempoolStats) => { + const data = this.handleNewMempoolData(mempoolStats.concat([])); + return { + mempool: mempoolStats, + weightPerSecond: this.handleNewMempoolData(mempoolStats.concat([])), + }; + }), + share(), + ); } handleNewMempoolData(mempoolStats: OptimizedMempoolStats[]) { diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index 5b6f6b9d4..09255a76f 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -235,7 +235,7 @@ body { color: #dc3545; } -.yellow-color { +.yellow-color { color: #ffd800; } @@ -255,168 +255,64 @@ html:lang(ru) .card-title { font-size: 0.9rem; } -/* Chartist */ -$ct-series-names: (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z); -$ct-series-colors: ( - #D81B60, - #8E24AA, - #5E35B1, - #3949AB, - #1E88E5, - #039BE5, - #00ACC1, - #00897B, - #43A047, - #7CB342, - #C0CA33, - #FDD835, - #FFB300, - #FB8C00, - #F4511E, - #6D4C41, - #757575, - #546E7A, - #b71c1c, - #880E4F, - #4A148C, - #311B92, - #1A237E, - #0D47A1, - #01579B, - #006064, - #004D40, - #1B5E20, - #33691E, - #827717, - #F57F17, - #FF6F00, - #E65100, - #BF360C, - #3E2723, - #212121, - #263238, - #a748ca, - #6188e2, - #a748ca, - #6188e2, -); +/* MEMPOOL CHARTS */ -@import "../node_modules/@mempool/chartist/dist/scss/chartist.scss"; - -.ct-bar-label { - font-size: 20px; - font-weight: bold; - fill: #fff; +.mempool-wrapper-tooltip-chart { + height: 250px; } -.ct-target-line { - stroke: #f5f5f5; - stroke-width: 3px; - stroke-dasharray: 7px; +.echarts { + height: 100%; + min-height: 180px; } -.ct-area { - stroke: none; - fill-opacity: 0.9; -} - -.ct-label { - fill: rgba(255, 255, 255, 0.4); - color: rgba(255, 255, 255, 0.4); -} - -.ct-point-label { - fill: rgba(255, 255, 255, 1); - color: rgba(255, 255, 255, 1); - font-size: 14px; -} - -.ct-grid { - stroke: rgba(255, 255, 255, 0.2); -} - -/* LEGEND */ - -.ct-legend { - position: absolute; - z-index: 10; - left: 0px; - list-style: none; - font-size: 13px; - padding: 0px 0px 0px 30px; - top: 90px; - - li { - position: relative; - padding-left: 23px; - margin-bottom: 0px; +.tx-wrapper-tooltip-chart, .fees-wrapper-tooltip-chart { + display: flex; + justify-content: space-between; + flex-direction: column; + background: rgba(#11131f, 0.85); + color: #fff; + padding: 10px 15px; + border-radius: 4px; + box-shadow: 1px 1px 10px rgba(0,0,0,0.2); + .item { + text-align: left; + display: flex; + .indicator { + display: block; + margin-right: 5px; + border-radius: 10px; + margin-top: 5px; + width: 9px; + height: 9px; } - - li:before { - width: 12px; - height: 12px; - position: absolute; - left: 0; - bottom: 3px; - content: ''; - border: 3px solid transparent; - border-radius: 2px; - } - - li.inactive:before { - background: transparent; - } - - &.ct-legend-inside { - position: absolute; - top: 0; - right: 0; - } - - @for $i from 0 to length($ct-series-colors) { - .ct-series-#{$i}:before { - background-color: nth($ct-series-colors, $i + 1); - border-color: nth($ct-series-colors, $i + 1); + .value { + text-align: right; + span { + color: #212121 !important; } + } } } +.grow { + flex-grow: 1; +} -.chartist-tooltip { - position: absolute; - display: inline-block; - opacity: 0; - min-width: 5em; - padding: .5em; - background: #F4C63D; - color: #453D3F; - font-family: Oxygen,Helvetica,Arial,sans-serif; - font-weight: 700; - text-align: center; - pointer-events: none; - z-index: 1; - -webkit-transition: opacity .2s linear; - -moz-transition: opacity .2s linear; - -o-transition: opacity .2s linear; - transition: opacity .2s linear; } - .chartist-tooltip:before { - content: ""; - position: absolute; - top: 100%; - left: 50%; - width: 0; - height: 0; - margin-left: -15px; - border: 15px solid transparent; - border-top-color: #F4C63D; } - .chartist-tooltip.tooltip-show { - opacity: 1; } +.fees-wrapper-tooltip-chart { + .item { + font-size: 9px; + line-height: 1; + } + .indicator { + margin-right: 5px !important; + border-radius: 10px !important; + margin-top: 0px !important; + } +} -.ct-area, .ct-line { - pointer-events: none; } - -.ct-bar { - stroke-width: 1px; +.fee-distribution-chart { + height: 250px; } hr { @@ -639,7 +535,7 @@ th { .card { background-color: transparent; padding: 0; - + button { text-align: left; display: block; @@ -653,17 +549,17 @@ th { box-shadow: none; } } - + .card-header { padding: 0; } - + .collapsed{ background-color: #2d3348; color: #1bd8f4; } } - + .subtitle { font-weight: bold; margin-bottom: 3px; @@ -675,7 +571,6 @@ th { .pagination-container { - display: inline-block; width: 100%; justify-content: space-between; @@ -698,4 +593,4 @@ th { .tooltip.show { width: 220px; } -} \ No newline at end of file +} From 3574f8639e338a2f97a19974d1ee9fc253f83e61 Mon Sep 17 00:00:00 2001 From: Miguel Medeiros Date: Wed, 25 Aug 2021 01:01:35 -0300 Subject: [PATCH 03/12] Add total sum to mempool chart. Add zoom tools. Add different theme for charts `big` and `small` (default). Fix date format on mouseover. Fix animations on graphs page. Fix overflow tv page. Remove `crosshair` on mouseover, changed to `line`. Fix custom tooltip styles. Remove inverted button (will add in a future PR). Remove fee range labels (will add in a future PR). Fix e2e testing. --- .../integration/mainnet/mainnet.spec.ts | 4 +- .../incoming-transactions-graph.component.ts | 53 ++++-- .../mempool-graph.component.html | 2 +- .../mempool-graph/mempool-graph.component.ts | 161 ++++++++++-------- .../statistics/statistics.component.html | 19 ++- .../television/television.component.html | 11 +- frontend/src/styles.scss | 94 ++++++++-- 7 files changed, 233 insertions(+), 111 deletions(-) diff --git a/frontend/cypress/integration/mainnet/mainnet.spec.ts b/frontend/cypress/integration/mainnet/mainnet.spec.ts index 7e56c7e97..5839f4f51 100644 --- a/frontend/cypress/integration/mainnet/mainnet.spec.ts +++ b/frontend/cypress/integration/mainnet/mainnet.spec.ts @@ -281,12 +281,12 @@ describe('Mainnet', () => { }); }); - it('loads the tv screen - mobile', () => { + it.only('loads the tv screen - mobile', () => { cy.viewport('iphone-6'); cy.visit('/tv'); cy.waitForSkeletonGone(); cy.get('.chart-holder'); - cy.get('.blockchain-wrapper').should('be.visible'); + cy.get('.blockchain-wrapper').should('not.visible'); }); it('loads the api screen', () => { diff --git a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts index dd4001821..e5951c261 100644 --- a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts +++ b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts @@ -2,6 +2,7 @@ import { Component, OnInit, Input, Inject, LOCALE_ID, ChangeDetectionStrategy } import { formatDate } from '@angular/common'; import { EChartsOption } from 'echarts'; import { OnChanges } from '@angular/core'; +import { StorageService } from 'src/app/services/storage.service'; @Component({ selector: 'app-incoming-transactions-graph', @@ -15,14 +16,18 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges { @Input() right: number | string = '10'; @Input() top: number | string = '20'; @Input() left: number | string = '50'; + @Input() size: ('small' | 'big') = 'small'; mempoolStatsChartOption: EChartsOption = {}; + windowPreference: string; constructor( @Inject(LOCALE_ID) private locale: string, + private storageService: StorageService, ) { } ngOnChanges(): void { + this.windowPreference = this.storageService.getValue('graphWindowPreference'); this.mountChart(); } @@ -38,6 +43,24 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges { top: this.top, left: this.left, }, + dataZoom: [{ + type: 'inside', + realtime: true, + }, { + show: (this.size === 'big') ? true : false, + type: 'slider', + brushSelect: false, + realtime: true, + selectedDataBackground: { + lineStyle: { + color: '#fff', + opacity: 0.45, + }, + areaStyle: { + opacity: 0, + } + } + }], tooltip: { trigger: 'axis', position: (pos, params, el, elRect, size) => { @@ -45,25 +68,16 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges { obj[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 80; return obj; }, - extraCssText: `background: transparent; + extraCssText: `width: ${(['2h', '24h'].includes(this.windowPreference) || this.size === 'small') ? '105px' : '135px'}; + background: transparent; border: none; box-shadow: none;`, axisPointer: { - type: 'cross', - label: { - formatter: (axis: any) => { - if (axis.axisDimension === 'y') { - return `${Math.floor(axis.value)}`; - } - if (axis.axisDimension === 'x') { - return axis.value; - } - }, - } + type: 'line', }, formatter: (params: any) => { const colorSpan = (color: string) => `
`; - let itemFormatted = '
' + params[0].axisValue + '
'; + let itemFormatted = '
' + params[0].axisValue + '
'; params.map((item: any, index: number) => { if (index < 26) { itemFormatted += `
@@ -73,15 +87,18 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges {
`; } }); - if (this.theme !== '') { - return `
${itemFormatted}
`; - } - return `
${itemFormatted}
`; + return `
${itemFormatted}
`; } }, xAxis: { type: 'category', - data: this.data.labels.map((value: any) => formatDate(value, 'HH:mm', this.locale)), + data: this.data.labels.map((value: any) => { + if (['2h', '24h'].includes(this.windowPreference) || this.size === 'small') { + return formatDate(value, 'HH:mm', this.locale); + } else { + return formatDate(value, 'MM/dd - HH:mm', this.locale); + } + }), }, yAxis: { type: 'value', diff --git a/frontend/src/app/components/mempool-graph/mempool-graph.component.html b/frontend/src/app/components/mempool-graph/mempool-graph.component.html index 0ea3867a8..a4cac25b7 100644 --- a/frontend/src/app/components/mempool-graph/mempool-graph.component.html +++ b/frontend/src/app/components/mempool-graph/mempool-graph.component.html @@ -1 +1 @@ -
+
diff --git a/frontend/src/app/components/mempool-graph/mempool-graph.component.ts b/frontend/src/app/components/mempool-graph/mempool-graph.component.ts index 2b384d7e8..f6e7761fe 100644 --- a/frontend/src/app/components/mempool-graph/mempool-graph.component.ts +++ b/frontend/src/app/components/mempool-graph/mempool-graph.component.ts @@ -7,13 +7,6 @@ import { StorageService } from 'src/app/services/storage.service'; import { EChartsOption } from 'echarts'; import { feeLevels, chartColors } from 'src/app/app.constants'; -interface AxisObject { - axisDimension: string; - axisIndex: number; - seriesData: any; - value: string; -} - @Component({ selector: 'app-mempool-graph', templateUrl: './mempool-graph.component.html', @@ -26,18 +19,18 @@ export class MempoolGraphComponent implements OnInit, OnChanges { @Input() top: number | string = 20; @Input() right: number | string = 10; @Input() left: number | string = 75; - @Input() dateSpan = '2h'; - @Input() showLegend = true; @Input() small = false; + @Input() size: ('small' | 'big') = 'small'; mempoolVsizeFeesData: any; mempoolVsizeFeesOptions: EChartsOption; - - inverted: boolean; + windowPreference: string; + hoverIndexSerie: -1; constructor( private vbytesPipe: VbytesPipe, private stateService: StateService, + private storageService: StorageService, @Inject(LOCALE_ID) private locale: string, ) { } @@ -46,11 +39,17 @@ export class MempoolGraphComponent implements OnInit, OnChanges { } ngOnChanges() { - // this.inverted = this.storageService.getValue('inverted-graph') === 'true'; + this.windowPreference = this.storageService.getValue('graphWindowPreference'); this.mempoolVsizeFeesData = this.handleNewMempoolData(this.data.concat([])); this.mountFeeChart(); } + onChartReady(myChart: any) { + myChart.on('mouseover', 'series', (serie: any) => { + this.hoverIndexSerie = serie.seriesIndex; + }); + } + handleNewMempoolData(mempoolStats: OptimizedMempoolStats[]) { mempoolStats.reverse(); const labels = mempoolStats.map(stats => stats.added); @@ -81,9 +80,9 @@ export class MempoolGraphComponent implements OnInit, OnChanges { feesArray.push(0); } }); - // if (this.inverted && finalArray.length) { - // feesArray = feesArray.map((value, i) => value + finalArray[finalArray.length - 1][i]); - // } + if (finalArray.length) { + feesArray = feesArray.map((value, i) => value + finalArray[finalArray.length - 1][i]); + } finalArray.push(feesArray); } finalArray.reverse(); @@ -93,45 +92,45 @@ export class MempoolGraphComponent implements OnInit, OnChanges { mountFeeChart(){ const { labels, series } = this.mempoolVsizeFeesData; - const legendNames: string[] = feeLevels.map((sat, i, arr) => { - if (sat > this.limitFee) { return `${this.limitFee}+`; } - if (i === 0) { return '0 - 1'; } - return arr[i - 1] + ' - ' + sat; + const feeLevelsOrdered = feeLevels.map((sat, i, arr) => { + if (i <= 26) { + if (i === 0) { return '0 - 1'; } + if (i === 26) { return '350+'; } + return arr[i - 1] + ' - ' + sat; + } }); const yAxisSeries = series.map((value: Array, index: number) => { - return { - name: labels[index].name, - type: 'line', - stack: 'total', - smooth: false, - lineStyle: { - width: 0, - opacity: 0, - }, - showSymbol: false, - areaStyle: { - opacity: 1, - color: chartColors[index], - }, - emphasis: { - focus: 'series' - }, - markLine: { - symbol: 'none', - itemStyle: { - borderWidth: 0, - borderColor: 'none', - color: '#fff', + if (index <= 26){ + return { + name: feeLevelsOrdered[index], + type: 'line', + stack: 'total', + smooth: false, + markPoint: { + symbol: 'rect', }, lineStyle: { - color: '#fff', - opacity: 0.75, - width: 2, + width: 0, + opacity: 0, }, - }, - data: this.vbytesPipe.transform(value, 2, 'vB', 'MvB', true) - }; + symbolSize: (this.size === 'big') ? 15 : 10, + showSymbol: false, + areaStyle: { + opacity: 1, + color: chartColors[index], + }, + emphasis: { + focus: 'series', + }, + itemStyle: { + borderWidth: 30, + color: chartColors[index], + borderColor: chartColors[index], + }, + data: this.vbytesPipe.transform(value, 2, 'vB', 'MvB', true) + }; + } }); this.mempoolVsizeFeesOptions = { @@ -143,39 +142,57 @@ export class MempoolGraphComponent implements OnInit, OnChanges { positions[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 80; return positions; }, - extraCssText: `width: 150px; + extraCssText: `width: ${(this.size === 'big') ? '200px' : '170px'}; background: transparent; border: none; box-shadow: none;`, axisPointer: { - type: 'cross', - label: { - formatter: (axis: AxisObject) => { - if (axis.axisDimension === 'y') { - return `${this.vbytesPipe.transform(axis.value, 2, 'vB', 'MvB', true)}`; - } - if (axis.axisDimension === 'x') { - return axis.value; - } - }, - } + type: 'line', }, formatter: (params: any) => { const colorSpan = (index: number) => `
`; - const legendName = (index: number) => legendNames[index]; - let itemFormatted = '
' + params[0].axisValue + '
'; + const legendName = (index: number) => feeLevelsOrdered[index]; + let itemFormatted = `
${params[0].axisValue}
`; + let total = 0; params.map((item: any, index: number) => { - if (feeLevels[index - 1] < this.limitFee) { - itemFormatted += `
- ${colorSpan(index - 1)} ${legendName(index)} + total += item.value; + if (index <= 26) { + let activeItemClass = ''; + if (this.hoverIndexSerie === index){ + activeItemClass = 'active'; + } + itemFormatted += `
+ ${colorSpan(index)} ${legendName(index)}
-
${this.vbytesPipe.transform(item.value, 2, 'vB', 'MvB', true)}
+
${this.vbytesPipe.transform(item.value, 2, 'vB', 'MvB', false)}
`; } }); - return `
${itemFormatted}
`; + const totalDiv = `
Total + ${this.vbytesPipe.transform(total, 2, 'vB', 'MvB', true)} +
`; + const bigClass = (this.size === 'big') ? 'fees-wrapper-tooltip-chart-big' : ''; + return `
${itemFormatted} ${totalDiv}
`; } }, + dataZoom: [{ + type: 'inside', + realtime: true, + }, { + show: (this.size === 'big') ? true : false, + type: 'slider', + brushSelect: false, + realtime: true, + selectedDataBackground: { + lineStyle: { + color: '#fff', + opacity: 0.45, + }, + areaStyle: { + opacity: 0, + } + } + }], grid: { height: this.height, right: this.right, @@ -186,11 +203,19 @@ export class MempoolGraphComponent implements OnInit, OnChanges { { type: 'category', boundaryGap: false, - data: labels.map((value: any) => formatDate(value, 'HH:mm', this.locale)), + axisLine: { onZero: false }, + data: labels.map((value: any) => { + if (['2h', '24h'].includes(this.windowPreference) || this.size === 'small') { + return formatDate(value, 'HH:mm', this.locale); + } else { + return formatDate(value, 'MM/dd - HH:mm', this.locale); + } + }), } ], yAxis: { type: 'value', + axisLine: { onZero: false }, axisLabel: { formatter: (value: number) => (`${this.vbytesPipe.transform(value, 2, 'vB', 'MvB', true)}`), }, diff --git a/frontend/src/app/components/statistics/statistics.component.html b/frontend/src/app/components/statistics/statistics.component.html index b932c746c..8e8677267 100644 --- a/frontend/src/app/components/statistics/statistics.component.html +++ b/frontend/src/app/components/statistics/statistics.component.html @@ -36,25 +36,36 @@ 1Y
-
- +
-
+
Transaction vBytes per second (vB/s)
- +
diff --git a/frontend/src/app/components/television/television.component.html b/frontend/src/app/components/television/television.component.html index d12564c30..5b8d52bb3 100644 --- a/frontend/src/app/components/television/television.component.html +++ b/frontend/src/app/components/television/television.component.html @@ -5,8 +5,15 @@
-
- +
+
diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index 09255a76f..39c7292f0 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -255,7 +255,7 @@ html:lang(ru) .card-title { font-size: 0.9rem; } -/* MEMPOOL CHARTS */ +/* MEMPOOL CHARTS - start */ .mempool-wrapper-tooltip-chart { height: 250px; @@ -267,54 +267,116 @@ html:lang(ru) .card-title { } .tx-wrapper-tooltip-chart, .fees-wrapper-tooltip-chart { - display: flex; - justify-content: space-between; - flex-direction: column; background: rgba(#11131f, 0.85); - color: #fff; - padding: 10px 15px; border-radius: 4px; box-shadow: 1px 1px 10px rgba(0,0,0,0.2); + color: #b1b1b1; + display: flex; + flex-direction: column; + justify-content: space-between; + padding: 10px 15px; + text-align: left; + .title { + font-size: 12px; + font-weight: 700; + margin-bottom: 2px; + color: #fff; + } + .active { + color: yellow !important; + font-weight: 900; + .value { + .symbol { + color: yellow !important; + } + } + } .item { - text-align: left; display: flex; .indicator { - display: block; margin-right: 5px; - border-radius: 10px; + border-radius: 0px; margin-top: 5px; width: 9px; height: 9px; } .value { text-align: right; - span { - color: #212121 !important; + .symbol { + color: #7e7e7e; + font-size: 9px !important; } } } -} - -.grow { - flex-grow: 1; + .total-label { + width: 100%; + margin-top: 0px; + font-size: 10px; + text-align: left; + color: #fff; + span { + font-weight: 700; + float: right !important; + } + } } .fees-wrapper-tooltip-chart { .item { font-size: 9px; line-height: 1; + margin: 0px; } .indicator { margin-right: 5px !important; - border-radius: 10px !important; + border-radius: 1px !important; margin-top: 0px !important; } } +.fees-wrapper-tooltip-chart-big, .tx-wrapper-tooltip-chart-big { + background: rgba(#1d1f31, 0.85); + .title { + font-size: 15px; + margin-bottom: 5px; + } + .item { + font-size: 12px; + line-height: 1; + margin: 2px 0px; + .value { + .symbol { + font-size: 12px !important; + } + } + } + .total-label { + width: 100%; + margin-top: 5px; + font-size: 14px; + span { + font-weight: 700; + float: right !important; + } + } +} + +.tx-wrapper-tooltip-chart-big { + .indicator { + margin: 0px !important;; + } +} + .fee-distribution-chart { height: 250px; } +/* MEMPOOL CHARTS - end */ + +.grow { + flex-grow: 1; +} + hr { border-top: 1px solid rgba(255, 255, 255, 0.1); } From 1a7decb91daa87a857dacd22dc93d6d8ae59f489 Mon Sep 17 00:00:00 2001 From: Miguel Medeiros Date: Thu, 26 Aug 2021 23:29:39 -0300 Subject: [PATCH 04/12] Add default data to title tooltip `MM/dd HH:mm`. Add symbol to tx chart tooltip . --- .../incoming-transactions-graph.component.ts | 12 +++--------- .../mempool-graph/mempool-graph.component.ts | 8 +------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts index e5951c261..0e43bfbb6 100644 --- a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts +++ b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts @@ -68,7 +68,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges { obj[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 80; return obj; }, - extraCssText: `width: ${(['2h', '24h'].includes(this.windowPreference) || this.size === 'small') ? '105px' : '135px'}; + extraCssText: `width: ${(['2h', '24h'].includes(this.windowPreference) || this.size === 'small') ? '125px' : '135px'}; background: transparent; border: none; box-shadow: none;`, @@ -83,7 +83,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges { itemFormatted += `
${colorSpan(item.color)}
-
${item.value}
+
${item.value} vB/s
`; } }); @@ -92,13 +92,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges { }, xAxis: { type: 'category', - data: this.data.labels.map((value: any) => { - if (['2h', '24h'].includes(this.windowPreference) || this.size === 'small') { - return formatDate(value, 'HH:mm', this.locale); - } else { - return formatDate(value, 'MM/dd - HH:mm', this.locale); - } - }), + data: this.data.labels.map((value: any) => formatDate(value, 'MM/dd - HH:mm', this.locale)), }, yAxis: { type: 'value', diff --git a/frontend/src/app/components/mempool-graph/mempool-graph.component.ts b/frontend/src/app/components/mempool-graph/mempool-graph.component.ts index f6e7761fe..f7df31fb2 100644 --- a/frontend/src/app/components/mempool-graph/mempool-graph.component.ts +++ b/frontend/src/app/components/mempool-graph/mempool-graph.component.ts @@ -204,13 +204,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges { type: 'category', boundaryGap: false, axisLine: { onZero: false }, - data: labels.map((value: any) => { - if (['2h', '24h'].includes(this.windowPreference) || this.size === 'small') { - return formatDate(value, 'HH:mm', this.locale); - } else { - return formatDate(value, 'MM/dd - HH:mm', this.locale); - } - }), + data: labels.map((value: any) => formatDate(value, 'MM/dd - HH:mm', this.locale)), } ], yAxis: { From f434e50a2c39d0c55060c187f770c5fb62874f6f Mon Sep 17 00:00:00 2001 From: Miguel Medeiros Date: Thu, 26 Aug 2021 23:30:57 -0300 Subject: [PATCH 05/12] Invert the tooltip legends order. Fix default data to title tooltip MM/dd HH:mm. Add symbol to tx chart tooltip . Add accumulative total for tooltip information. Add 3th column to tooltip with a progress bar. Add and max span zoom span. Add feeRate limit input to mempool graph component. Add showZoom option to mempool graph component. Remove start animation to match the layout for future SSR. Remove mouse wheel zoom from small template. Fix small template style. --- .../incoming-transactions-graph.component.ts | 18 ++- .../mempool-graph/mempool-graph.component.ts | 94 ++++++++---- .../statistics/statistics.component.html | 10 +- .../television/television.component.html | 8 +- .../app/dashboard/dashboard.component.html | 6 +- frontend/src/styles.scss | 145 ++++++++++++++---- 6 files changed, 209 insertions(+), 72 deletions(-) diff --git a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts index 0e43bfbb6..35aee245c 100644 --- a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts +++ b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts @@ -15,8 +15,8 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges { @Input() height: number | string = '200'; @Input() right: number | string = '10'; @Input() top: number | string = '20'; - @Input() left: number | string = '50'; - @Input() size: ('small' | 'big') = 'small'; + @Input() left: number | string = '0'; + @Input() template: ('widget' | 'advanced') = 'widget'; mempoolStatsChartOption: EChartsOption = {}; windowPreference: string; @@ -43,11 +43,15 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges { top: this.top, left: this.left, }, + animation: false, dataZoom: [{ type: 'inside', realtime: true, + zoomOnMouseWheel: (this.template === 'advanced') ? true : false, + maxSpan: 100, + minSpan: 10, }, { - show: (this.size === 'big') ? true : false, + show: (this.template === 'advanced') ? true : false, type: 'slider', brushSelect: false, realtime: true, @@ -68,7 +72,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges { obj[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 80; return obj; }, - extraCssText: `width: ${(['2h', '24h'].includes(this.windowPreference) || this.size === 'small') ? '125px' : '135px'}; + extraCssText: `width: ${(['2h', '24h'].includes(this.windowPreference) || this.template === 'widget') ? '125px' : '135px'}; background: transparent; border: none; box-shadow: none;`, @@ -76,18 +80,18 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges { type: 'line', }, formatter: (params: any) => { - const colorSpan = (color: string) => `
`; + const colorSpan = (color: string) => ``; let itemFormatted = '
' + params[0].axisValue + '
'; params.map((item: any, index: number) => { if (index < 26) { itemFormatted += `
- ${colorSpan(item.color)} +
${colorSpan(item.color)}
${item.value} vB/s
`; } }); - return `
${itemFormatted}
`; + return `
${itemFormatted}
`; } }, xAxis: { diff --git a/frontend/src/app/components/mempool-graph/mempool-graph.component.ts b/frontend/src/app/components/mempool-graph/mempool-graph.component.ts index f7df31fb2..25d0371bc 100644 --- a/frontend/src/app/components/mempool-graph/mempool-graph.component.ts +++ b/frontend/src/app/components/mempool-graph/mempool-graph.component.ts @@ -14,18 +14,19 @@ import { feeLevels, chartColors } from 'src/app/app.constants'; }) export class MempoolGraphComponent implements OnInit, OnChanges { @Input() data: any[]; - @Input() limitFee = 300; + @Input() limitFee = 350; @Input() height: number | string = 200; @Input() top: number | string = 20; @Input() right: number | string = 10; @Input() left: number | string = 75; - @Input() small = false; - @Input() size: ('small' | 'big') = 'small'; + @Input() template: ('widget' | 'advanced') = 'widget'; + @Input() showZoom = true; mempoolVsizeFeesData: any; mempoolVsizeFeesOptions: EChartsOption; windowPreference: string; hoverIndexSerie: -1; + feeLimitIndex: number; constructor( private vbytesPipe: VbytesPipe, @@ -93,15 +94,17 @@ export class MempoolGraphComponent implements OnInit, OnChanges { const { labels, series } = this.mempoolVsizeFeesData; const feeLevelsOrdered = feeLevels.map((sat, i, arr) => { - if (i <= 26) { + if (arr[i] === this.limitFee) { this.feeLimitIndex = i; } + if (arr[i] < this.limitFee) { if (i === 0) { return '0 - 1'; } - if (i === 26) { return '350+'; } - return arr[i - 1] + ' - ' + sat; + return `${arr[i - 1]} - ${arr[i]}`; + } else { + return `${this.limitFee}+`; } }); const yAxisSeries = series.map((value: Array, index: number) => { - if (index <= 26){ + if (index <= this.feeLimitIndex){ return { name: feeLevelsOrdered[index], type: 'line', @@ -114,7 +117,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges { width: 0, opacity: 0, }, - symbolSize: (this.size === 'big') ? 15 : 10, + symbolSize: (this.template === 'advanced') ? 20 : 10, showSymbol: false, areaStyle: { opacity: 1, @@ -122,11 +125,14 @@ export class MempoolGraphComponent implements OnInit, OnChanges { }, emphasis: { focus: 'series', + select: { + areaStyle: { + opacity: 1, + } + }, }, itemStyle: { borderWidth: 30, - color: chartColors[index], - borderColor: chartColors[index], }, data: this.vbytesPipe.transform(value, 2, 'vB', 'MvB', true) }; @@ -138,11 +144,11 @@ export class MempoolGraphComponent implements OnInit, OnChanges { tooltip: { trigger: 'axis', position: (pos, params, el, elRect, size) => { - const positions = { top: -20 }; - positions[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 80; + const positions = { top: (this.template === 'advanced') ? 30 : -30 }; + positions[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 60; return positions; }, - extraCssText: `width: ${(this.size === 'big') ? '200px' : '170px'}; + extraCssText: `width: ${(this.template === 'advanced') ? '210px' : '200px'}; background: transparent; border: none; box-shadow: none;`, @@ -150,36 +156,69 @@ export class MempoolGraphComponent implements OnInit, OnChanges { type: 'line', }, formatter: (params: any) => { - const colorSpan = (index: number) => `
`; const legendName = (index: number) => feeLevelsOrdered[index]; - let itemFormatted = `
${params[0].axisValue}
`; + const colorSpan = (index: number) => `${legendName(index)}`; + const title = `
${params[0].axisValue}
`; + const rangeLines = params; let total = 0; - params.map((item: any, index: number) => { + rangeLines.map((item: any) => { total += item.value; - if (index <= 26) { + }); + const itemFormatted = []; + let totalParcial = 0; + let progressPercentageText = ''; + rangeLines.map((item: any, index: number) => { + totalParcial += item.value; + let progressPercentage = 0; + let progressPercentageTotalParcial = 0; + if (index <= this.feeLimitIndex) { + progressPercentage = (item.value / total) * 100; + progressPercentageTotalParcial = (totalParcial / total) * 100; let activeItemClass = ''; - if (this.hoverIndexSerie === index){ + if (this.hoverIndexSerie === index) { + progressPercentageText = `
+ ${this.vbytesPipe.transform(totalParcial, 2, 'vB', 'MvB', false)} +
+
`; activeItemClass = 'active'; } - itemFormatted += `
- ${colorSpan(index)} ${legendName(index)} -
-
${this.vbytesPipe.transform(item.value, 2, 'vB', 'MvB', false)}
-
`; + itemFormatted.push(` + ${colorSpan(index)} + ${this.vbytesPipe.transform(item.value, 2, 'vB', 'MvB', false)} + + `); } }); + const progressActiveDiv = `${progressPercentageText}`; const totalDiv = `
Total - ${this.vbytesPipe.transform(total, 2, 'vB', 'MvB', true)} + ${this.vbytesPipe.transform(total, 2, 'vB', 'MvB', false)} +
`; + const advancedClass = (this.template === 'advanced') ? 'fees-wrapper-tooltip-chart-advanced' : ''; + return `
+ ${title} + + + + + + + + + ${itemFormatted.reverse().join('')} +
RangeSize%
+ ${progressActiveDiv} + ${totalDiv}
`; - const bigClass = (this.size === 'big') ? 'fees-wrapper-tooltip-chart-big' : ''; - return `
${itemFormatted} ${totalDiv}
`; } }, dataZoom: [{ type: 'inside', realtime: true, + zoomOnMouseWheel: (this.template === 'advanced') ? true : false, + maxSpan: 100, + minSpan: 10, }, { - show: (this.size === 'big') ? true : false, + show: (this.template === 'advanced' && this.showZoom) ? true : false, type: 'slider', brushSelect: false, realtime: true, @@ -193,6 +232,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges { } } }], + animation: false, grid: { height: this.height, right: this.right, diff --git a/frontend/src/app/components/statistics/statistics.component.html b/frontend/src/app/components/statistics/statistics.component.html index 8e8677267..04cfb5548 100644 --- a/frontend/src/app/components/statistics/statistics.component.html +++ b/frontend/src/app/components/statistics/statistics.component.html @@ -42,10 +42,10 @@
@@ -62,8 +62,8 @@
diff --git a/frontend/src/app/components/television/television.component.html b/frontend/src/app/components/television/television.component.html index 5b8d52bb3..e5134c9bd 100644 --- a/frontend/src/app/components/television/television.component.html +++ b/frontend/src/app/components/television/television.component.html @@ -7,12 +7,12 @@
diff --git a/frontend/src/app/dashboard/dashboard.component.html b/frontend/src/app/dashboard/dashboard.component.html index 5eab01618..6a521e4d2 100644 --- a/frontend/src/app/dashboard/dashboard.component.html +++ b/frontend/src/app/dashboard/dashboard.component.html @@ -48,7 +48,11 @@
- +
diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index 39c7292f0..bbbfc1e03 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -266,16 +266,29 @@ html:lang(ru) .card-title { min-height: 180px; } -.tx-wrapper-tooltip-chart, .fees-wrapper-tooltip-chart { - background: rgba(#11131f, 0.85); +.tx-wrapper-tooltip-chart, +.fees-wrapper-tooltip-chart { + background: rgba(#11131f, 0.95); border-radius: 4px; - box-shadow: 1px 1px 10px rgba(0,0,0,0.2); + box-shadow: 1px 1px 10px rgba(0,0,0,0.5); color: #b1b1b1; display: flex; flex-direction: column; justify-content: space-between; padding: 10px 15px; text-align: left; + thead { + th { + font-size: 9px; + color: #b1b1b1; + text-align: right; + &:first-child { + text-align: left; + left: -1px; + position: relative; + } + } + } .title { font-size: 12px; font-weight: 700; @@ -284,21 +297,25 @@ html:lang(ru) .card-title { } .active { color: yellow !important; - font-weight: 900; - .value { + .value, + .total-partial { + color: yellow !important; .symbol { color: yellow !important; } } } .item { - display: flex; - .indicator { - margin-right: 5px; - border-radius: 0px; - margin-top: 5px; - width: 9px; - height: 9px; + line-height: 0.8; + .indicator-container { + .indicator { + display: inline-block; + margin-right: 5px; + border-radius: 0px; + margin-top: 5px; + width: 9px; + height: 9px; + } } .value { text-align: right; @@ -307,24 +324,81 @@ html:lang(ru) .card-title { font-size: 9px !important; } } + .total-partial { + text-align: right; + font-size: 10px; + width: 70px; + } + .total-percentage-bar { + padding-left: 8px; + } } .total-label { width: 100%; - margin-top: 0px; - font-size: 10px; text-align: left; color: #fff; + margin-top: 5px; + font-size: 14px; span { - font-weight: 700; - float: right !important; + float: right; + } + .symbol { + margin-left: 3px; + font-size: 9px; + position: relative; + top: 2px; } } + .total-percentage-bar { + margin: auto; + width: 35px; + position: relative; + span { + display: block; + background: #282d47; + height: 5px; + border-radius: 2px; + } + } + .total-parcial-active { + text-align: right; + margin: 5px auto 5px; + padding-left: 0px; + span { + font-size: 10px; + } + .symbol { + font-size: 9px; + } + .total-percentage-bar { + width: 100%; + span { + transition: 1000 all ease-in-out; + } + } + } +} + +.tx-wrapper-tooltip-chart { + .item { + display: flex; + } + .value { + margin-top: 5px; + } + .indicator-container { + border-radius: 2px; + } +} + +.fee-distribution-chart { + height: 250px; } .fees-wrapper-tooltip-chart { .item { font-size: 9px; - line-height: 1; + line-height: 0.8; margin: 0px; } .indicator { @@ -334,8 +408,9 @@ html:lang(ru) .card-title { } } -.fees-wrapper-tooltip-chart-big, .tx-wrapper-tooltip-chart-big { - background: rgba(#1d1f31, 0.85); +.fees-wrapper-tooltip-chart-big, +.tx-wrapper-tooltip-chart-big { + background: rgba(#1d1f31, 0.98); .title { font-size: 15px; margin-bottom: 5px; @@ -343,7 +418,6 @@ html:lang(ru) .card-title { .item { font-size: 12px; line-height: 1; - margin: 2px 0px; .value { .symbol { font-size: 12px !important; @@ -351,25 +425,40 @@ html:lang(ru) .card-title { } } .total-label { - width: 100%; margin-top: 5px; font-size: 14px; span { - font-weight: 700; - float: right !important; + float: right; + } + } + .total-parcial-active { + text-align: right; + margin: 5px auto 5px; + span { + font-size: 10px; + } + .total-percentage-bar { + width: 100%; + left: 0; + span { + transition: 1000 all ease-in-out; + } } } } .tx-wrapper-tooltip-chart-big { - .indicator { - margin: 0px !important;; + .indicator-container { + .indicator { + margin-right: 5px; + border-radius: 0px; + margin-top: 5px; + width: 9px; + height: 9px; + } } } -.fee-distribution-chart { - height: 250px; -} /* MEMPOOL CHARTS - end */ From 584d091d4efb016d9cf7cf17ab7ac22cafc42b65 Mon Sep 17 00:00:00 2001 From: Miguel Medeiros Date: Thu, 26 Aug 2021 23:50:32 -0300 Subject: [PATCH 06/12] Fix indicator border-radius. --- frontend/src/styles.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index bbbfc1e03..8f94c8742 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -311,7 +311,7 @@ html:lang(ru) .card-title { .indicator { display: inline-block; margin-right: 5px; - border-radius: 0px; + border-radius: 2px; margin-top: 5px; width: 9px; height: 9px; From 9020c618f034e0ca9384377e29a2bf23beb22ed9 Mon Sep 17 00:00:00 2001 From: Miguel Medeiros Date: Tue, 7 Sep 2021 17:48:43 -0300 Subject: [PATCH 07/12] Change renderer to svg. Fix data structure of mempool graph. Change tooltip total position (top). Change tooltip visual of partial porcentage. --- .../mempool-graph.component.html | 2 +- .../mempool-graph/mempool-graph.component.ts | 41 +++++++------------ .../src/app/interfaces/node-api.interface.ts | 2 +- frontend/src/styles.scss | 9 ++++ 4 files changed, 25 insertions(+), 29 deletions(-) diff --git a/frontend/src/app/components/mempool-graph/mempool-graph.component.html b/frontend/src/app/components/mempool-graph/mempool-graph.component.html index a4cac25b7..ac67c6e9e 100644 --- a/frontend/src/app/components/mempool-graph/mempool-graph.component.html +++ b/frontend/src/app/components/mempool-graph/mempool-graph.component.html @@ -1 +1 @@ -
+
diff --git a/frontend/src/app/components/mempool-graph/mempool-graph.component.ts b/frontend/src/app/components/mempool-graph/mempool-graph.component.ts index 25d0371bc..42321c5e9 100644 --- a/frontend/src/app/components/mempool-graph/mempool-graph.component.ts +++ b/frontend/src/app/components/mempool-graph/mempool-graph.component.ts @@ -24,6 +24,9 @@ export class MempoolGraphComponent implements OnInit, OnChanges { mempoolVsizeFeesData: any; mempoolVsizeFeesOptions: EChartsOption; + mempoolVsizeFeesInitOptions = { + renderer: 'svg', + }; windowPreference: string; hoverIndexSerie: -1; feeLimitIndex: number; @@ -70,20 +73,11 @@ export class MempoolGraphComponent implements OnInit, OnChanges { generateArray(mempoolStats: OptimizedMempoolStats[]) { const finalArray: number[][] = []; let feesArray: number[] = []; - for (let index = 37; index > -1; index--) { feesArray = []; mempoolStats.forEach((stats) => { - const theFee = stats.vsizes[index].toString(); - if (theFee) { - feesArray.push(parseInt(theFee, 10)); - } else { - feesArray.push(0); - } + feesArray.push(stats.vsizes[index] ? stats.vsizes[index] : 0); }); - if (finalArray.length) { - feesArray = feesArray.map((value, i) => value + finalArray[finalArray.length - 1][i]); - } finalArray.push(feesArray); } finalArray.reverse(); @@ -103,10 +97,9 @@ export class MempoolGraphComponent implements OnInit, OnChanges { } }); - const yAxisSeries = series.map((value: Array, index: number) => { + const seriesGraph = series.map((value: Array, index: number) => { if (index <= this.feeLimitIndex){ return { - name: feeLevelsOrdered[index], type: 'line', stack: 'total', smooth: false, @@ -157,42 +150,37 @@ export class MempoolGraphComponent implements OnInit, OnChanges { }, formatter: (params: any) => { const legendName = (index: number) => feeLevelsOrdered[index]; - const colorSpan = (index: number) => `${legendName(index)}`; - const title = `
${params[0].axisValue}
`; - const rangeLines = params; + const colorSpan = (index: number) => `${legendName(index)}`; let total = 0; - rangeLines.map((item: any) => { + params.map((item: any) => { total += item.value; }); + const title = `
${params[0].axisValue} + ${this.vbytesPipe.transform(total, 2, 'vB', 'MvB', false)}
`; const itemFormatted = []; let totalParcial = 0; let progressPercentageText = ''; - rangeLines.map((item: any, index: number) => { + params.map((item: any, index: number) => { totalParcial += item.value; let progressPercentage = 0; - let progressPercentageTotalParcial = 0; if (index <= this.feeLimitIndex) { progressPercentage = (item.value / total) * 100; - progressPercentageTotalParcial = (totalParcial / total) * 100; let activeItemClass = ''; if (this.hoverIndexSerie === index) { progressPercentageText = `
- ${this.vbytesPipe.transform(totalParcial, 2, 'vB', 'MvB', false)} -
+ ${progressPercentage.toFixed(2)} %${this.vbytesPipe.transform(totalParcial, 2, 'vB', 'MvB', false)} +
`; activeItemClass = 'active'; } itemFormatted.push(` ${colorSpan(index)} ${this.vbytesPipe.transform(item.value, 2, 'vB', 'MvB', false)} - + ${progressPercentage.toFixed(1)} % `); } }); const progressActiveDiv = `${progressPercentageText}`; - const totalDiv = `
Total - ${this.vbytesPipe.transform(total, 2, 'vB', 'MvB', false)} -
`; const advancedClass = (this.template === 'advanced') ? 'fees-wrapper-tooltip-chart-advanced' : ''; return `
${title} @@ -207,7 +195,6 @@ export class MempoolGraphComponent implements OnInit, OnChanges { ${itemFormatted.reverse().join('')} ${progressActiveDiv} - ${totalDiv}
`; } }, @@ -261,7 +248,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges { } } }, - series: yAxisSeries + series: seriesGraph }; } } diff --git a/frontend/src/app/interfaces/node-api.interface.ts b/frontend/src/app/interfaces/node-api.interface.ts index 2a4bce0e8..d73c12d4e 100644 --- a/frontend/src/app/interfaces/node-api.interface.ts +++ b/frontend/src/app/interfaces/node-api.interface.ts @@ -6,7 +6,7 @@ export interface OptimizedMempoolStats { vbytes_per_second: number; total_fee: number; mempool_byte_weight: number; - vsizes: number[] | string[]; + vsizes: number[]; } interface Ancestor { diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index 8f94c8742..9fb38711d 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -294,6 +294,9 @@ html:lang(ru) .card-title { font-weight: 700; margin-bottom: 2px; color: #fff; + .total-value { + float: right; + } } .active { color: yellow !important; @@ -332,6 +335,9 @@ html:lang(ru) .card-title { .total-percentage-bar { padding-left: 8px; } + .total-progress-percentage { + text-align: right; + } } .total-label { width: 100%; @@ -376,6 +382,9 @@ html:lang(ru) .card-title { transition: 1000 all ease-in-out; } } + .progress-percentage { + float: left; + } } } From 34695146ee29acd371a52a330fc595a9af8e7d71 Mon Sep 17 00:00:00 2001 From: Miguel Medeiros Date: Tue, 7 Sep 2021 17:54:27 -0300 Subject: [PATCH 08/12] Change renderer to svg. --- .../fee-distribution-graph.component.html | 2 +- .../fee-distribution-graph/fee-distribution-graph.component.ts | 3 +++ .../incoming-transactions-graph.component.html | 2 +- .../incoming-transactions-graph.component.ts | 3 +++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.html b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.html index 84cf34cfe..3465bde35 100644 --- a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.html +++ b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.html @@ -1,5 +1,5 @@
-
+
diff --git a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts index 873d42fb8..8c90036fd 100644 --- a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts +++ b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts @@ -14,6 +14,9 @@ export class FeeDistributionGraphComponent implements OnInit, OnChanges { @Input() left: number | string = 30; mempoolVsizeFeesOptions: any; + mempoolVsizeFeesInitOptions = { + renderer: 'svg' + }; constructor() { } diff --git a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.html b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.html index 7c6de093e..537aaceab 100644 --- a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.html +++ b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.html @@ -1 +1 @@ -
+
diff --git a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts index 35aee245c..53c9a0251 100644 --- a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts +++ b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts @@ -19,6 +19,9 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges { @Input() template: ('widget' | 'advanced') = 'widget'; mempoolStatsChartOption: EChartsOption = {}; + mempoolStatsChartInitOption = { + renderer: 'svg' + }; windowPreference: string; constructor( From 388aa7fbe34b4218d343f09a7d36f60df27c9473 Mon Sep 17 00:00:00 2001 From: Miguel Medeiros Date: Tue, 7 Sep 2021 23:08:24 -0300 Subject: [PATCH 09/12] Fix y axis margin left. --- .../app/components/statistics/statistics.component.html | 4 ++-- .../src/app/components/statistics/statistics.component.ts | 8 -------- frontend/src/app/dashboard/dashboard.component.html | 5 ++++- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/frontend/src/app/components/statistics/statistics.component.html b/frontend/src/app/components/statistics/statistics.component.html index 04cfb5548..c06ef4b37 100644 --- a/frontend/src/app/components/statistics/statistics.component.html +++ b/frontend/src/app/components/statistics/statistics.component.html @@ -45,7 +45,7 @@ [template]="'advanced'" [limitFee]="500" [height]="500" - [left]="50" + [left]="65" [data]="mempoolStats" >
@@ -62,7 +62,7 @@
diff --git a/frontend/src/app/components/statistics/statistics.component.ts b/frontend/src/app/components/statistics/statistics.component.ts index 733762764..c7f0042d9 100644 --- a/frontend/src/app/components/statistics/statistics.component.ts +++ b/frontend/src/app/components/statistics/statistics.component.ts @@ -1,6 +1,5 @@ import { Component, OnInit, LOCALE_ID, Inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { formatDate } from '@angular/common'; import { FormGroup, FormBuilder } from '@angular/forms'; import { of, merge} from 'rxjs'; import { switchMap } from 'rxjs/operators'; @@ -31,7 +30,6 @@ export class StatisticsComponent implements OnInit { mempoolTransactionsWeightPerSecondData: any; radioGroupForm: FormGroup; - inverted: boolean; graphWindowPreference: String; constructor( @@ -48,7 +46,6 @@ export class StatisticsComponent implements OnInit { ngOnInit() { this.seoService.setTitle($localize`:@@5d4f792f048fcaa6df5948575d7cb325c9393383:Graphs`); this.stateService.networkChanged$.subscribe((network) => this.network = network); - this.inverted = this.storageService.getValue('inverted-graph') === 'true'; this.graphWindowPreference = this.storageService.getValue('graphWindowPreference') ? this.storageService.getValue('graphWindowPreference').trim() : '2h'; const isMobile = window.innerWidth <= 767.98; let labelHops = isMobile ? 48 : 24; @@ -124,11 +121,6 @@ export class StatisticsComponent implements OnInit { }; } - invertGraph() { - this.storageService.setValue('inverted-graph', !this.inverted); - document.location.reload(); - } - saveGraphPreference() { this.storageService.setValue('graphWindowPreference', this.radioGroupForm.controls.dateSpan.value); } diff --git a/frontend/src/app/dashboard/dashboard.component.html b/frontend/src/app/dashboard/dashboard.component.html index 6a521e4d2..cbda3076a 100644 --- a/frontend/src/app/dashboard/dashboard.component.html +++ b/frontend/src/app/dashboard/dashboard.component.html @@ -64,7 +64,10 @@

- +
From d376ba1c61927597dbcb1d9a608c96b0c705ae0d Mon Sep 17 00:00:00 2001 From: Miguel Medeiros Date: Mon, 13 Sep 2021 11:07:17 -0300 Subject: [PATCH 10/12] Move total MvB to the top. Fix yAxis value. Fix yAxis value. Make disks smaller and transparent. Change opacity on mouseover stack bars. Add 4th column "sum of vsize" to tooltips table. Add toggle show/hide tier fees. Make progress active bar inline with text value. Add a break line to the timestamp text. Add colored progress bar with number. --- .../incoming-transactions-graph.component.ts | 4 +- .../mempool-graph/mempool-graph.component.ts | 111 ++++++++++++------ .../statistics/statistics.component.html | 1 + frontend/src/styles.scss | 94 +++++++++++++-- 4 files changed, 167 insertions(+), 43 deletions(-) diff --git a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts index 53c9a0251..68d1744e3 100644 --- a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts +++ b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts @@ -99,7 +99,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges { }, xAxis: { type: 'category', - data: this.data.labels.map((value: any) => formatDate(value, 'MM/dd - HH:mm', this.locale)), + data: this.data.labels.map((value: any) => `${formatDate(value, 'M/d', this.locale)}\n${formatDate(value, 'H:mm', this.locale)}`), }, yAxis: { type: 'value', @@ -115,7 +115,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges { { data: this.data.series[0], type: 'line', - smooth: true, + smooth: (this.template === 'advanced') ? false : true, showSymbol: false, lineStyle: { width: 3, diff --git a/frontend/src/app/components/mempool-graph/mempool-graph.component.ts b/frontend/src/app/components/mempool-graph/mempool-graph.component.ts index 42321c5e9..eeaa0b00a 100644 --- a/frontend/src/app/components/mempool-graph/mempool-graph.component.ts +++ b/frontend/src/app/components/mempool-graph/mempool-graph.component.ts @@ -28,8 +28,11 @@ export class MempoolGraphComponent implements OnInit, OnChanges { renderer: 'svg', }; windowPreference: string; - hoverIndexSerie: -1; + hoverIndexSerie = 0; feeLimitIndex: number; + feeLevelsOrdered = []; + toggledLegends = []; + // feeLevelsFiltered = []; constructor( private vbytesPipe: VbytesPipe, @@ -39,6 +42,15 @@ export class MempoolGraphComponent implements OnInit, OnChanges { ) { } ngOnInit(): void { + this.feeLevelsOrdered = feeLevels.map((sat, i, arr) => { + if (arr[i] === this.limitFee) { this.feeLimitIndex = i; } + if (arr[i] < this.limitFee) { + if (i === 0) { return '0 - 1'; } + return `${arr[i - 1]} - ${arr[i]}`; + } else { + return `${this.limitFee}+`; + } + }); this.mountFeeChart(); } @@ -52,6 +64,10 @@ export class MempoolGraphComponent implements OnInit, OnChanges { myChart.on('mouseover', 'series', (serie: any) => { this.hoverIndexSerie = serie.seriesIndex; }); + // myChart.on('legendselectchanged', (params: any) => { + // this.feeLevelsFiltered = params.selected; + // console.log(params.selected, this.feeLevelsOrdered); + // }); } handleNewMempoolData(mempoolStats: OptimizedMempoolStats[]) { @@ -84,22 +100,13 @@ export class MempoolGraphComponent implements OnInit, OnChanges { return finalArray; } - mountFeeChart(){ + mountFeeChart() { const { labels, series } = this.mempoolVsizeFeesData; - const feeLevelsOrdered = feeLevels.map((sat, i, arr) => { - if (arr[i] === this.limitFee) { this.feeLimitIndex = i; } - if (arr[i] < this.limitFee) { - if (i === 0) { return '0 - 1'; } - return `${arr[i - 1]} - ${arr[i]}`; - } else { - return `${this.limitFee}+`; - } - }); - const seriesGraph = series.map((value: Array, index: number) => { if (index <= this.feeLimitIndex){ return { + name: this.feeLevelsOrdered[index], type: 'line', stack: 'total', smooth: false, @@ -110,7 +117,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges { width: 0, opacity: 0, }, - symbolSize: (this.template === 'advanced') ? 20 : 10, + symbolSize: (this.template === 'advanced') ? 10 : 10, showSymbol: false, areaStyle: { opacity: 1, @@ -118,14 +125,15 @@ export class MempoolGraphComponent implements OnInit, OnChanges { }, emphasis: { focus: 'series', - select: { - areaStyle: { - opacity: 1, - } + areaStyle: { + opacity: 1, + }, + itemStyle: { + opacity: 0.2, }, }, itemStyle: { - borderWidth: 30, + opacity: 0, }, data: this.vbytesPipe.transform(value, 2, 'vB', 'MvB', true) }; @@ -133,15 +141,33 @@ export class MempoolGraphComponent implements OnInit, OnChanges { }); this.mempoolVsizeFeesOptions = { - color: chartColors, + emphasis: { + areaStyle: { + opacity: 1, + } + }, + legend: { + icon: 'none', + align: 'right', + inactiveColor: '#212121', + orient: 'vertical', + height: '650px', + selectorPosition: 'auto', + right: 0, + selectedMode: 'multiple', + data: this.feeLevelsOrdered, + show: (this.template === 'advanced') ? true : false, + }, tooltip: { + show: true, trigger: 'axis', + alwaysShowContent: false, position: (pos, params, el, elRect, size) => { const positions = { top: (this.template === 'advanced') ? 30 : -30 }; positions[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 60; return positions; }, - extraCssText: `width: ${(this.template === 'advanced') ? '210px' : '200px'}; + extraCssText: `width: ${(this.template === 'advanced') ? '275px' : '200px'}; background: transparent; border: none; box-shadow: none;`, @@ -149,22 +175,31 @@ export class MempoolGraphComponent implements OnInit, OnChanges { type: 'line', }, formatter: (params: any) => { - const legendName = (index: number) => feeLevelsOrdered[index]; + const legendName = (index: number) => this.feeLevelsOrdered[index]; const colorSpan = (index: number) => `${legendName(index)}`; - let total = 0; - params.map((item: any) => { - total += item.value; - }); + const totals = (values: any) => { + let totalValue = 0; + const totalValueArray = []; + const valuesInverted = values.slice(0).reverse(); + for (const item of valuesInverted) { + totalValue += item.value; + totalValueArray.push(totalValue); + } + return { totalValue, totalValueArray: totalValueArray.reverse() }; + }; + const { totalValue, totalValueArray } = totals(params); const title = `
${params[0].axisValue} - ${this.vbytesPipe.transform(total, 2, 'vB', 'MvB', false)}
`; + ${this.vbytesPipe.transform(totalValue, 2, 'vB', 'MvB', false)}
`; const itemFormatted = []; let totalParcial = 0; let progressPercentageText = ''; params.map((item: any, index: number) => { totalParcial += item.value; let progressPercentage = 0; + let progressPercentageSum = 0; if (index <= this.feeLimitIndex) { - progressPercentage = (item.value / total) * 100; + progressPercentage = (item.value / totalValue) * 100; + progressPercentageSum = (totalValueArray[index] / totalValue) * 100; let activeItemClass = ''; if (this.hoverIndexSerie === index) { progressPercentageText = `
@@ -176,25 +211,31 @@ export class MempoolGraphComponent implements OnInit, OnChanges { itemFormatted.push(` ${colorSpan(index)} ${this.vbytesPipe.transform(item.value, 2, 'vB', 'MvB', false)} - ${progressPercentage.toFixed(1)} % + + ${this.vbytesPipe.transform(totalValueArray[index], 2, 'vB', 'MvB', false)} + + +
+ +
+ `); } }); - const progressActiveDiv = `${progressPercentageText}`; - const advancedClass = (this.template === 'advanced') ? 'fees-wrapper-tooltip-chart-advanced' : ''; - return `
+ return `
${title} - + + ${itemFormatted.reverse().join('')}
Range Size%Sum
- ${progressActiveDiv} + ${progressPercentageText}
`; } }, @@ -209,6 +250,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges { type: 'slider', brushSelect: false, realtime: true, + bottom: 0, selectedDataBackground: { lineStyle: { color: '#fff', @@ -231,7 +273,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges { type: 'category', boundaryGap: false, axisLine: { onZero: false }, - data: labels.map((value: any) => formatDate(value, 'MM/dd - HH:mm', this.locale)), + data: labels.map((value: any) => `${formatDate(value, 'M/d', this.locale)}\n${formatDate(value, 'H:mm', this.locale)}`), } ], yAxis: { @@ -252,3 +294,4 @@ export class MempoolGraphComponent implements OnInit, OnChanges { }; } } + diff --git a/frontend/src/app/components/statistics/statistics.component.html b/frontend/src/app/components/statistics/statistics.component.html index c06ef4b37..29a10b0b3 100644 --- a/frontend/src/app/components/statistics/statistics.component.html +++ b/frontend/src/app/components/statistics/statistics.component.html @@ -46,6 +46,7 @@ [limitFee]="500" [height]="500" [left]="65" + [right]="85" [data]="mempoolStats" >
diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index 9fb38711d..4722feabb 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -277,6 +277,7 @@ html:lang(ru) .card-title { justify-content: space-between; padding: 10px 15px; text-align: left; + width: 200px; thead { th { font-size: 9px; @@ -287,6 +288,9 @@ html:lang(ru) .card-title { left: -1px; position: relative; } + &:nth-child(4){ + display: none; + } } } .title { @@ -327,15 +331,25 @@ html:lang(ru) .card-title { font-size: 9px !important; } } + .symbol { + font-size: 9px; + } .total-partial { - text-align: right; font-size: 10px; - width: 70px; + width: 58px; + text-align: right; } .total-percentage-bar { padding-left: 8px; } .total-progress-percentage { + width: 45px; + height: 5px; + text-align: right; + display: none; + } + .total-progress-sum { + width: 58px; text-align: right; } } @@ -355,6 +369,24 @@ html:lang(ru) .card-title { top: 2px; } } + thead { + th { + font-size: 9px; + color: #b1b1b1; + text-align: right; + &:first-child { + text-align: left; + left: -1px; + position: relative; + } + &:nth-child(4){ + display: none; + } + &:nth-child(5){ + display: none; + } + } + } .total-percentage-bar { margin: auto; width: 35px; @@ -389,6 +421,7 @@ html:lang(ru) .card-title { } .tx-wrapper-tooltip-chart { + width: 115px; .item { display: flex; } @@ -417,19 +450,65 @@ html:lang(ru) .card-title { } } -.fees-wrapper-tooltip-chart-big, -.tx-wrapper-tooltip-chart-big { +.fees-wrapper-tooltip-chart-advanced, +.tx-wrapper-tooltip-chart-advanced { background: rgba(#1d1f31, 0.98); + width: 250px; + + thead { + th { + &:nth-child(4){ + display: table-cell; + } + &:nth-child(5){ + display: table-cell; + } + } + } .title { font-size: 15px; margin-bottom: 5px; } .item { - font-size: 12px; line-height: 1; .value { + width: 60px; .symbol { - font-size: 12px !important; + font-size: 10px !important; + } + } + .total-partial { + font-size: 10px; + width: 58px; + text-align: right; + } + .total-progress-percentage { + width: 65px; + height: 4px; + padding: 0px 5px; + display: table-cell !important; + border-radius: 4px; + } + .total-progress-sum { + width: 65px; + height: 4px; + padding: 0px 5px; + border-radius: 4px; + } + .total-progress-percentage-bar, + .total-progress-sum-bar { + width: 35px; + height: 4px; + div { + width: 100%; + border-radius: 4px; + display: block; + background: #29324c94; + } + span { + height: 4px; + border-radius: 4px; + display: block; } } } @@ -456,7 +535,8 @@ html:lang(ru) .card-title { } } -.tx-wrapper-tooltip-chart-big { +.tx-wrapper-tooltip-chart-advanced { + width: 115px; .indicator-container { .indicator { margin-right: 5px; From fa7a45421eaf2ebf699e932fc8550a1a370fedc6 Mon Sep 17 00:00:00 2001 From: Miguel Medeiros Date: Mon, 13 Sep 2021 11:19:29 -0300 Subject: [PATCH 11/12] Fix linting and unclosed html tag. Fix no shadowed variable tslint warning. --- .../mempool-graph/mempool-graph.component.ts | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/frontend/src/app/components/mempool-graph/mempool-graph.component.ts b/frontend/src/app/components/mempool-graph/mempool-graph.component.ts index eeaa0b00a..63e051a1c 100644 --- a/frontend/src/app/components/mempool-graph/mempool-graph.component.ts +++ b/frontend/src/app/components/mempool-graph/mempool-graph.component.ts @@ -176,16 +176,19 @@ export class MempoolGraphComponent implements OnInit, OnChanges { }, formatter: (params: any) => { const legendName = (index: number) => this.feeLevelsOrdered[index]; - const colorSpan = (index: number) => `${legendName(index)}`; + const colorSpan = (index: number) => `${legendName(index)}`; const totals = (values: any) => { - let totalValue = 0; - const totalValueArray = []; + let totalValueTemp = 0; + const totalValueArrayTemp = []; const valuesInverted = values.slice(0).reverse(); for (const item of valuesInverted) { - totalValue += item.value; - totalValueArray.push(totalValue); + totalValueTemp += item.value; + totalValueArrayTemp.push(totalValueTemp); } - return { totalValue, totalValueArray: totalValueArray.reverse() }; + return { + totalValue: totalValueTemp, + totalValueArray: totalValueArrayTemp.reverse() + }; }; const { totalValue, totalValueArray } = totals(params); const title = `
${params[0].axisValue} @@ -203,8 +206,16 @@ export class MempoolGraphComponent implements OnInit, OnChanges { let activeItemClass = ''; if (this.hoverIndexSerie === index) { progressPercentageText = `
- ${progressPercentage.toFixed(2)} %${this.vbytesPipe.transform(totalParcial, 2, 'vB', 'MvB', false)} -
+ + ${progressPercentage.toFixed(2)} + % + + ${this.vbytesPipe.transform(totalParcial, 2, 'vB', 'MvB', false)} +
+ + + +
`; activeItemClass = 'active'; } From 8b8b06e6ab0580caaba9ef71cd949e956adb92eb Mon Sep 17 00:00:00 2001 From: Miguel Medeiros Date: Tue, 14 Sep 2021 22:35:34 -0300 Subject: [PATCH 12/12] Remove fee tier legends. Fix tv page css. --- .../mempool-graph/mempool-graph.component.ts | 59 ++++++++++--------- .../statistics/statistics.component.html | 1 - .../television/television.component.scss | 33 +---------- 3 files changed, 34 insertions(+), 59 deletions(-) diff --git a/frontend/src/app/components/mempool-graph/mempool-graph.component.ts b/frontend/src/app/components/mempool-graph/mempool-graph.component.ts index 63e051a1c..3cb6fff4e 100644 --- a/frontend/src/app/components/mempool-graph/mempool-graph.component.ts +++ b/frontend/src/app/components/mempool-graph/mempool-graph.component.ts @@ -31,8 +31,6 @@ export class MempoolGraphComponent implements OnInit, OnChanges { hoverIndexSerie = 0; feeLimitIndex: number; feeLevelsOrdered = []; - toggledLegends = []; - // feeLevelsFiltered = []; constructor( private vbytesPipe: VbytesPipe, @@ -64,10 +62,6 @@ export class MempoolGraphComponent implements OnInit, OnChanges { myChart.on('mouseover', 'series', (serie: any) => { this.hoverIndexSerie = serie.seriesIndex; }); - // myChart.on('legendselectchanged', (params: any) => { - // this.feeLevelsFiltered = params.selected; - // console.log(params.selected, this.feeLevelsOrdered); - // }); } handleNewMempoolData(mempoolStats: OptimizedMempoolStats[]) { @@ -146,18 +140,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges { opacity: 1, } }, - legend: { - icon: 'none', - align: 'right', - inactiveColor: '#212121', - orient: 'vertical', - height: '650px', - selectorPosition: 'auto', - right: 0, - selectedMode: 'multiple', - data: this.feeLevelsOrdered, - show: (this.template === 'advanced') ? true : false, - }, + color: chartColors, tooltip: { show: true, trigger: 'axis', @@ -176,7 +159,10 @@ export class MempoolGraphComponent implements OnInit, OnChanges { }, formatter: (params: any) => { const legendName = (index: number) => this.feeLevelsOrdered[index]; - const colorSpan = (index: number) => `${legendName(index)}`; + const colorSpan = (index: number) => ` + + ${legendName(index)} + `; const totals = (values: any) => { let totalValueTemp = 0; const totalValueArrayTemp = []; @@ -191,8 +177,12 @@ export class MempoolGraphComponent implements OnInit, OnChanges { }; }; const { totalValue, totalValueArray } = totals(params); - const title = `
${params[0].axisValue} - ${this.vbytesPipe.transform(totalValue, 2, 'vB', 'MvB', false)}
`; + const title = `
+ ${params[0].axisValue} + + ${this.vbytesPipe.transform(totalValue, 2, 'vB', 'MvB', false)} + +
`; const itemFormatted = []; let totalParcial = 0; let progressPercentageText = ''; @@ -210,7 +200,9 @@ export class MempoolGraphComponent implements OnInit, OnChanges { ${progressPercentage.toFixed(2)} % - ${this.vbytesPipe.transform(totalParcial, 2, 'vB', 'MvB', false)} + + ${this.vbytesPipe.transform(totalParcial, 2, 'vB', 'MvB', false)} +
@@ -220,10 +212,16 @@ export class MempoolGraphComponent implements OnInit, OnChanges { activeItemClass = 'active'; } itemFormatted.push(` - ${colorSpan(index)} - ${this.vbytesPipe.transform(item.value, 2, 'vB', 'MvB', false)} + + ${colorSpan(index)} + + + ${this.vbytesPipe.transform(item.value, 2, 'vB', 'MvB', false)} + - ${this.vbytesPipe.transform(totalValueArray[index], 2, 'vB', 'MvB', false)} + + ${this.vbytesPipe.transform(totalValueArray[index], 2, 'vB', 'MvB', false)} +
@@ -233,7 +231,8 @@ export class MempoolGraphComponent implements OnInit, OnChanges { `); } }); - return `
+ const classActive = (this.template === 'advanced') ? 'fees-wrapper-tooltip-chart-advanced' : ''; + return `
${title} @@ -244,9 +243,13 @@ export class MempoolGraphComponent implements OnInit, OnChanges { - ${itemFormatted.reverse().join('')} + + ${itemFormatted.reverse().join('')} +
- ${progressPercentageText} + + ${progressPercentageText} +
`; } }, diff --git a/frontend/src/app/components/statistics/statistics.component.html b/frontend/src/app/components/statistics/statistics.component.html index 29a10b0b3..c06ef4b37 100644 --- a/frontend/src/app/components/statistics/statistics.component.html +++ b/frontend/src/app/components/statistics/statistics.component.html @@ -46,7 +46,6 @@ [limitFee]="500" [height]="500" [left]="65" - [right]="85" [data]="mempoolStats" >
diff --git a/frontend/src/app/components/television/television.component.scss b/frontend/src/app/components/television/television.component.scss index 8629fbcb7..78e27750c 100644 --- a/frontend/src/app/components/television/television.component.scss +++ b/frontend/src/app/components/television/television.component.scss @@ -18,18 +18,15 @@ .chart-holder { height: 650px; width: 100%; - margin: 30px auto; + margin: 30px auto 0; } .blockchain-wrapper { - display: flex; + display: block; height: 100%; min-height: 240px; position: relative; - top: -20px; - @media(min-height: 800px) { - top: 30px; - } + top: 30px; .position-container { position: absolute; @@ -37,10 +34,6 @@ bottom: 170px; } - .chart-holder { - height: calc(100% - 220px); - } - #divider { width: 3px; height: 175px; @@ -55,27 +48,7 @@ top: -28px; } } - - @media (min-width: 1920px) { - .position-container { - transform: scale(1.3); - bottom: 210px; - } - .chart-holder { - height: calc(100% - 280px); - } - } - } -:host ::ng-deep .ct-legend { - top: 20px !important; - display: flex; - flex-direction: column-reverse; - @media(min-height: 800px){ - padding-top: 40px !important; - } -} - .tv-container { display: flex; margin-top: 0px;