Show summary stats and world map in isp and country node list page
This commit is contained in:
@@ -1,9 +1,58 @@
|
||||
<div class="container-xl full-height" style="min-height: 335px">
|
||||
<h1 class="float-left" i18n="lightning.nodes-in-country">
|
||||
<h1 i18n="lightning.nodes-in-country">
|
||||
<span>Lightning nodes in {{ country?.name }}</span>
|
||||
<span style="font-size: 50px; vertical-align:sub;"> {{ country?.flag }}</span>
|
||||
</h1>
|
||||
|
||||
<div class="box">
|
||||
<div class="row" *ngIf="nodes$ | async as countryNodes">
|
||||
<div class="col-12 col-md-6">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td i18n="lightning.node-count">Nodes</td>
|
||||
<td>{{ countryNodes.nodes.length }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n="lightning.liquidity">Liquidity</td>
|
||||
<td>
|
||||
<app-amount *ngIf="countryNodes.sumLiquidity > 100000000; else smallnode" [satoshis]="countryNodes.sumLiquidity" [digitsInfo]="'1.2-2'" [noFiat]="false"></app-amount>
|
||||
<ng-template #smallnode>
|
||||
{{ countryNodes.sumLiquidity | amountShortener: 1 }}
|
||||
<span class="sats" i18n="shared.sats">sats</span>
|
||||
</ng-template>
|
||||
<span class="d-none d-md-inline-block"> </span>
|
||||
<span class="d-block d-md-none"></span>
|
||||
<app-fiat [value]="countryNodes.sumLiquidity" digitsInfo="1.0-0"></app-fiat>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n="lightning.channels">Channels</td>
|
||||
<td>{{ countryNodes.sumChannels }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n="lightning.isp-count">ISP Count</td>
|
||||
<td>{{ countryNodes.ispCount }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n="lightning.top-isp">Top ISP</td>
|
||||
<td class="text-truncate">
|
||||
<a class="d-block text-wrap" [routerLink]="['/lightning/nodes/isp' | relativeUrl, countryNodes.topIsp.id]">
|
||||
{{ countryNodes.topIsp.name }} [ASN {{ countryNodes.topIsp.id }}]
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 p-3 p-md-0 pr-md-3">
|
||||
<div style="background-color: #181b2d">
|
||||
<app-nodes-map [widget]="true" [nodes]="countryNodes.nodes" type="country"></app-nodes-map>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="min-height: 295px">
|
||||
<table class="table table-borderless">
|
||||
|
||||
@@ -15,9 +64,8 @@
|
||||
<th class="channels text-right" i18n="lightning.channels">Channels</th>
|
||||
<th class="city text-right" i18n="lightning.location">Location</th>
|
||||
</thead>
|
||||
|
||||
<tbody *ngIf="nodes$ | async as nodes; else skeleton">
|
||||
<tr *ngFor="let node of nodes; let i= index; trackBy: trackByPublicKey">
|
||||
<tbody *ngIf="nodes$ | async as countryNodes; else skeleton">
|
||||
<tr *ngFor="let node of countryNodes.nodes; let i= index; trackBy: trackByPublicKey">
|
||||
<td class="alias text-left text-truncate">
|
||||
<a [routerLink]="['/lightning/node/' | relativeUrl, node.public_key]">{{ node.alias }}</a>
|
||||
</td>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { map, Observable } from 'rxjs';
|
||||
import { map, Observable, share } from 'rxjs';
|
||||
import { ApiService } from 'src/app/services/api.service';
|
||||
import { SeoService } from 'src/app/services/seo.service';
|
||||
import { getFlagEmoji } from 'src/app/shared/common.utils';
|
||||
@@ -32,6 +32,8 @@ export class NodesPerCountry implements OnInit {
|
||||
this.nodes$ = this.apiService.getNodeForCountry$(this.route.snapshot.params.country)
|
||||
.pipe(
|
||||
map(response => {
|
||||
this.seoService.setTitle($localize`Lightning nodes in ${response.country.en}`);
|
||||
|
||||
this.country = {
|
||||
name: response.country.en,
|
||||
flag: getFlagEmoji(this.route.snapshot.params.country)
|
||||
@@ -45,14 +47,50 @@ export class NodesPerCountry implements OnInit {
|
||||
iso: response.nodes[i].iso_code,
|
||||
};
|
||||
}
|
||||
|
||||
this.seoService.setTitle($localize`Lightning nodes in ${this.country.name}`);
|
||||
return response.nodes;
|
||||
})
|
||||
|
||||
const sumLiquidity = response.nodes.reduce((partialSum, a) => partialSum + a.capacity, 0);
|
||||
const sumChannels = response.nodes.reduce((partialSum, a) => partialSum + a.channels, 0);
|
||||
const isps = {};
|
||||
const topIsp = {
|
||||
count: 0,
|
||||
id: '',
|
||||
name: '',
|
||||
};
|
||||
for (const node of response.nodes) {
|
||||
if (!node.isp) {
|
||||
continue;
|
||||
}
|
||||
if (!isps[node.isp]) {
|
||||
isps[node.isp] = {
|
||||
count: 0,
|
||||
asns: [],
|
||||
};
|
||||
}
|
||||
if (isps[node.isp].asns.indexOf(node.as_number) === -1) {
|
||||
isps[node.isp].asns.push(node.as_number);
|
||||
}
|
||||
isps[node.isp].count++;
|
||||
|
||||
if (isps[node.isp].count > topIsp.count) {
|
||||
topIsp.count = isps[node.isp].count;
|
||||
topIsp.id = isps[node.isp].asns.join(',');
|
||||
topIsp.name = node.isp;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
nodes: response.nodes,
|
||||
sumLiquidity: sumLiquidity,
|
||||
sumChannels: sumChannels,
|
||||
topIsp: topIsp,
|
||||
ispCount: Object.keys(isps).length
|
||||
};
|
||||
}),
|
||||
share()
|
||||
);
|
||||
}
|
||||
|
||||
trackByPublicKey(index: number, node: any) {
|
||||
trackByPublicKey(index: number, node: any): string {
|
||||
return node.public_key;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user