From 376484a9377e8c0df431d5c5963f23fcdce1c6d9 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Sat, 16 Jul 2022 15:56:36 +0200 Subject: [PATCH] Nodes per country list component --- backend/src/api/explorer/nodes.api.ts | 21 ++++++- .../src/app/lightning/lightning.module.ts | 2 + .../app/lightning/lightning.routing.module.ts | 5 ++ .../nodes-per-country.component.html | 42 +++++++++++++ .../nodes-per-country.component.scss | 62 +++++++++++++++++++ .../nodes-per-country.component.ts | 37 +++++++++++ frontend/src/app/services/api.service.ts | 4 ++ .../timestamp/timestamp.component.html | 2 +- .../timestamp/timestamp.component.ts | 1 + 9 files changed, 172 insertions(+), 4 deletions(-) create mode 100644 frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.html create mode 100644 frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.scss create mode 100644 frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts diff --git a/backend/src/api/explorer/nodes.api.ts b/backend/src/api/explorer/nodes.api.ts index 4abad3e4e..3814da73d 100644 --- a/backend/src/api/explorer/nodes.api.ts +++ b/backend/src/api/explorer/nodes.api.ts @@ -127,12 +127,27 @@ class NodesApi { public async $getNodesPerCountry(country: string) { try { - const query = `SELECT nodes.* FROM nodes - JOIN geo_names ON geo_names.id = nodes.country_id - WHERE LOWER(json_extract(names, '$.en')) = ? + const query = ` + SELECT node_stats.public_key, node_stats.capacity, node_stats.channels, nodes.alias, + UNIX_TIMESTAMP(nodes.first_seen) as first_seen, UNIX_TIMESTAMP(nodes.updated_at) as updated_at, + geo_names_city.names as city + FROM node_stats + JOIN ( + SELECT public_key, MAX(added) as last_added + FROM node_stats + GROUP BY public_key + ) as b ON b.public_key = node_stats.public_key AND b.last_added = node_stats.added + LEFT JOIN nodes ON nodes.public_key = node_stats.public_key + LEFT JOIN geo_names geo_names_country ON geo_names_country.id = nodes.country_id + LEFT JOIN geo_names geo_names_city ON geo_names_city.id = nodes.city_id + WHERE LOWER(JSON_EXTRACT(geo_names_country.names, '$.en')) = ? + ORDER BY capacity DESC `; const [rows]: any = await DB.query(query, [`"${country}"`]); + for (let i = 0; i < rows.length; ++i) { + rows[i].city = JSON.parse(rows[i].city); + } return rows; } catch (e) { logger.err(`Cannot get nodes for country ${country}. Reason: ${e instanceof Error ? e.message : e}`); diff --git a/frontend/src/app/lightning/lightning.module.ts b/frontend/src/app/lightning/lightning.module.ts index 1cf9992f6..c4fa1bfb0 100644 --- a/frontend/src/app/lightning/lightning.module.ts +++ b/frontend/src/app/lightning/lightning.module.ts @@ -19,6 +19,7 @@ import { GraphsModule } from '../graphs/graphs.module'; import { NodesNetworksChartComponent } from './nodes-networks-chart/nodes-networks-chart.component'; import { ChannelsStatisticsComponent } from './channels-statistics/channels-statistics.component'; import { NodesPerAsChartComponent } from '../lightning/nodes-per-as-chart/nodes-per-as-chart.component'; +import { NodesPerCountry } from './nodes-per-country/nodes-per-country.component'; @NgModule({ declarations: [ LightningDashboardComponent, @@ -35,6 +36,7 @@ import { NodesPerAsChartComponent } from '../lightning/nodes-per-as-chart/nodes- NodesNetworksChartComponent, ChannelsStatisticsComponent, NodesPerAsChartComponent, + NodesPerCountry, ], imports: [ CommonModule, diff --git a/frontend/src/app/lightning/lightning.routing.module.ts b/frontend/src/app/lightning/lightning.routing.module.ts index e56a527f9..be6de3afd 100644 --- a/frontend/src/app/lightning/lightning.routing.module.ts +++ b/frontend/src/app/lightning/lightning.routing.module.ts @@ -4,6 +4,7 @@ import { LightningDashboardComponent } from './lightning-dashboard/lightning-das import { LightningWrapperComponent } from './lightning-wrapper/lightning-wrapper.component'; import { NodeComponent } from './node/node.component'; import { ChannelComponent } from './channel/channel.component'; +import { NodesPerCountry } from './nodes-per-country/nodes-per-country.component'; const routes: Routes = [ { @@ -22,6 +23,10 @@ const routes: Routes = [ path: 'channel/:short_id', component: ChannelComponent, }, + { + path: 'nodes/country/:country', + component: NodesPerCountry, + }, { path: '**', redirectTo: '' diff --git a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.html b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.html new file mode 100644 index 000000000..90e6ed1bd --- /dev/null +++ b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.html @@ -0,0 +1,42 @@ +
+

Nodes in {{ country }}

+ +
+ + + + + + + + + + + + + + + + + + +
AliasFirst seenLast updateCapacityChannelsCity
+ {{ node.alias }} + + + + + + + + {{ node.capacity | amountShortener: 1 }} + sats + + + {{ node.channels }} + + {{ node?.city?.en ?? '-' }} +
+
+ +
diff --git a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.scss b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.scss new file mode 100644 index 000000000..02b47e8be --- /dev/null +++ b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.scss @@ -0,0 +1,62 @@ +.container-xl { + max-width: 1400px; + padding-bottom: 100px; +} + +.sats { + color: #ffffff66; + font-size: 12px; + top: 0px; +} + +.alias { + width: 30%; + max-width: 400px; + padding-right: 70px; + + @media (max-width: 576px) { + width: 50%; + max-width: 150px; + padding-right: 0px; + } +} + +.timestamp-first { + width: 20%; + + @media (max-width: 576px) { + display: none + } +} + +.timestamp-update { + width: 16%; + + @media (max-width: 576px) { + display: none + } +} + +.capacity { + width: 10%; + + @media (max-width: 576px) { + width: 25%; + } +} + +.channels { + width: 10%; + + @media (max-width: 576px) { + width: 25%; + } +} + +.city { + max-width: 150px; + + @media (max-width: 576px) { + display: none + } +} \ No newline at end of file diff --git a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts new file mode 100644 index 000000000..db9bc5809 --- /dev/null +++ b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts @@ -0,0 +1,37 @@ +import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { map, Observable } from 'rxjs'; +import { ApiService } from 'src/app/services/api.service'; + +@Component({ + selector: 'app-nodes-per-country', + templateUrl: './nodes-per-country.component.html', + styleUrls: ['./nodes-per-country.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class NodesPerCountry implements OnInit { + nodes$: Observable; + country: string; + + constructor( + private apiService: ApiService, + private route: ActivatedRoute, + ) { } + + ngOnInit(): void { + this.country = this.route.snapshot.params.country; + this.country = this.country.charAt(0).toUpperCase() + this.country.slice(1); + + this.nodes$ = this.apiService.getNodeForCountry$(this.route.snapshot.params.country) + .pipe( + map(nodes => { + console.log(nodes); + return nodes; + }) + ); + } + + trackByPublicKey(index: number, node: any) { + return node.public_key; + } +} diff --git a/frontend/src/app/services/api.service.ts b/frontend/src/app/services/api.service.ts index 48f23a94f..6f83ce7e8 100644 --- a/frontend/src/app/services/api.service.ts +++ b/frontend/src/app/services/api.service.ts @@ -254,4 +254,8 @@ export class ApiService { getNodesPerAs(): Observable { return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/nodes/asShare'); } + + getNodeForCountry$(country: string): Observable { + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/nodes/country/' + country); + } } diff --git a/frontend/src/app/shared/components/timestamp/timestamp.component.html b/frontend/src/app/shared/components/timestamp/timestamp.component.html index b37ff065a..769b292d4 100644 --- a/frontend/src/app/shared/components/timestamp/timestamp.component.html +++ b/frontend/src/app/shared/components/timestamp/timestamp.component.html @@ -1,4 +1,4 @@ -‎{{ seconds * 1000 | date:'yyyy-MM-dd HH:mm' }} +‎{{ seconds * 1000 | date: customFormat ?? 'yyyy-MM-dd HH:mm' }}
()
diff --git a/frontend/src/app/shared/components/timestamp/timestamp.component.ts b/frontend/src/app/shared/components/timestamp/timestamp.component.ts index a0c9861f0..dc577a185 100644 --- a/frontend/src/app/shared/components/timestamp/timestamp.component.ts +++ b/frontend/src/app/shared/components/timestamp/timestamp.component.ts @@ -9,6 +9,7 @@ import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/c export class TimestampComponent implements OnChanges { @Input() unixTime: number; @Input() dateString: string; + @Input() customFormat: string; seconds: number;