Create top 100 node per capacity component
This commit is contained in:
		
							parent
							
								
									7520e3beba
								
							
						
					
					
						commit
						2359e44b16
					
				@ -10,10 +10,10 @@ class NodesApi {
 | 
				
			|||||||
      // General info
 | 
					      // General info
 | 
				
			||||||
      let query = `
 | 
					      let query = `
 | 
				
			||||||
        SELECT public_key, alias, UNIX_TIMESTAMP(first_seen) AS first_seen,
 | 
					        SELECT public_key, alias, UNIX_TIMESTAMP(first_seen) AS first_seen,
 | 
				
			||||||
        UNIX_TIMESTAMP(updated_at) AS updated_at, color, sockets as sockets,
 | 
					          UNIX_TIMESTAMP(updated_at) AS updated_at, color, sockets as sockets,
 | 
				
			||||||
        as_number, city_id, country_id, subdivision_id, longitude, latitude,
 | 
					          as_number, city_id, country_id, subdivision_id, longitude, latitude,
 | 
				
			||||||
        geo_names_iso.names as iso_code, geo_names_as.names as as_organization, geo_names_city.names as city,
 | 
					          geo_names_iso.names as iso_code, geo_names_as.names as as_organization, geo_names_city.names as city,
 | 
				
			||||||
        geo_names_country.names as country, geo_names_subdivision.names as subdivision
 | 
					          geo_names_country.names as country, geo_names_subdivision.names as subdivision
 | 
				
			||||||
        FROM nodes
 | 
					        FROM nodes
 | 
				
			||||||
        LEFT JOIN geo_names geo_names_as on geo_names_as.id = as_number
 | 
					        LEFT JOIN geo_names geo_names_as on geo_names_as.id = as_number
 | 
				
			||||||
        LEFT JOIN geo_names geo_names_city on geo_names_city.id = city_id
 | 
					        LEFT JOIN geo_names geo_names_city on geo_names_city.id = city_id
 | 
				
			||||||
@ -113,21 +113,46 @@ class NodesApi {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public async $getTopCapacityNodes(): Promise<TopNodesPerCapacity[]> {
 | 
					  public async $getTopCapacityNodes(full: boolean): Promise<TopNodesPerCapacity[]> {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      let [rows]: any[] = await DB.query('SELECT UNIX_TIMESTAMP(MAX(added)) as maxAdded FROM node_stats');
 | 
					      let [rows]: any[] = await DB.query('SELECT UNIX_TIMESTAMP(MAX(added)) as maxAdded FROM node_stats');
 | 
				
			||||||
      const latestDate = rows[0].maxAdded;
 | 
					      const latestDate = rows[0].maxAdded;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const query = `
 | 
					      let query: string;
 | 
				
			||||||
        SELECT nodes.public_key, IF(nodes.alias = '', SUBSTRING(nodes.public_key, 1, 20), alias) as alias,
 | 
					      if (full === false) {
 | 
				
			||||||
          node_stats.capacity
 | 
					        query = `
 | 
				
			||||||
        FROM node_stats
 | 
					          SELECT nodes.public_key AS publicKey, IF(nodes.alias = '', SUBSTRING(nodes.public_key, 1, 20), alias) as alias,
 | 
				
			||||||
        JOIN nodes ON nodes.public_key = node_stats.public_key
 | 
					            node_stats.capacity
 | 
				
			||||||
        WHERE added = FROM_UNIXTIME(${latestDate})
 | 
					          FROM node_stats
 | 
				
			||||||
        ORDER BY capacity DESC
 | 
					          JOIN nodes ON nodes.public_key = node_stats.public_key
 | 
				
			||||||
        LIMIT 100;
 | 
					          WHERE added = FROM_UNIXTIME(${latestDate})
 | 
				
			||||||
      `;
 | 
					          ORDER BY capacity DESC
 | 
				
			||||||
      [rows] = await DB.query(query);
 | 
					          LIMIT 100
 | 
				
			||||||
 | 
					        `;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [rows] = await DB.query(query);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        query = `
 | 
				
			||||||
 | 
					          SELECT node_stats.public_key AS publicKey, IF(nodes.alias = '', SUBSTRING(node_stats.public_key, 1, 20), alias) as alias,
 | 
				
			||||||
 | 
					            CAST(COALESCE(node_stats.capacity, 0) as INT) as capacity,
 | 
				
			||||||
 | 
					            CAST(COALESCE(node_stats.channels, 0) as INT) as channels,
 | 
				
			||||||
 | 
					            UNIX_TIMESTAMP(nodes.first_seen) as firstSeen, UNIX_TIMESTAMP(nodes.updated_at) as updatedAt,
 | 
				
			||||||
 | 
					            geo_names_city.names as city, geo_names_country.names as country
 | 
				
			||||||
 | 
					          FROM node_stats
 | 
				
			||||||
 | 
					          RIGHT 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 AND geo_names_country.type = 'country'
 | 
				
			||||||
 | 
					          LEFT JOIN geo_names geo_names_city ON geo_names_city.id = nodes.city_id AND geo_names_city.type = 'city'
 | 
				
			||||||
 | 
					          WHERE added = FROM_UNIXTIME(${latestDate})
 | 
				
			||||||
 | 
					          ORDER BY capacity DESC
 | 
				
			||||||
 | 
					          LIMIT 100
 | 
				
			||||||
 | 
					        `;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [rows] = await DB.query(query);
 | 
				
			||||||
 | 
					        for (let i = 0; i < rows.length; ++i) {
 | 
				
			||||||
 | 
					          rows[i].country = JSON.parse(rows[i].country);
 | 
				
			||||||
 | 
					          rows[i].city = JSON.parse(rows[i].city);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return rows;
 | 
					      return rows;
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
				
			|||||||
@ -11,10 +11,11 @@ class NodesRoutes {
 | 
				
			|||||||
    app
 | 
					    app
 | 
				
			||||||
      .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/country/:country', this.$getNodesPerCountry)
 | 
					      .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/country/:country', this.$getNodesPerCountry)
 | 
				
			||||||
      .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/search/:search', this.$searchNode)
 | 
					      .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/search/:search', this.$searchNode)
 | 
				
			||||||
      .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/rankings', this.$getNodesRanking)
 | 
					 | 
				
			||||||
      .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/isp-ranking', this.$getISPRanking)
 | 
					      .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/isp-ranking', this.$getISPRanking)
 | 
				
			||||||
      .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/isp/:isp', this.$getNodesPerISP)
 | 
					      .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/isp/:isp', this.$getNodesPerISP)
 | 
				
			||||||
      .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/countries', this.$getNodesCountries)
 | 
					      .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/countries', this.$getNodesCountries)
 | 
				
			||||||
 | 
					      .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/rankings', this.$getNodesRanking)
 | 
				
			||||||
 | 
					      .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/rankings/capacity', this.$getTopNodesByCapacity)
 | 
				
			||||||
      .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/:public_key/statistics', this.$getHistoricalNodeStats)
 | 
					      .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/:public_key/statistics', this.$getHistoricalNodeStats)
 | 
				
			||||||
      .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/:public_key', this.$getNode)
 | 
					      .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/:public_key', this.$getNode)
 | 
				
			||||||
    ;
 | 
					    ;
 | 
				
			||||||
@ -59,8 +60,11 @@ class NodesRoutes {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  private async $getNodesRanking(req: Request, res: Response): Promise<void> {
 | 
					  private async $getNodesRanking(req: Request, res: Response): Promise<void> {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const topCapacityNodes = await nodesApi.$getTopCapacityNodes();
 | 
					      const topCapacityNodes = await nodesApi.$getTopCapacityNodes(false);
 | 
				
			||||||
      const topChannelsNodes = await nodesApi.$getTopChannelsNodes();
 | 
					      const topChannelsNodes = await nodesApi.$getTopChannelsNodes();
 | 
				
			||||||
 | 
					      res.header('Pragma', 'public');
 | 
				
			||||||
 | 
					      res.header('Cache-control', 'public');
 | 
				
			||||||
 | 
					      res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
 | 
				
			||||||
      res.json(<INodesRanking>{
 | 
					      res.json(<INodesRanking>{
 | 
				
			||||||
        topByCapacity: topCapacityNodes,
 | 
					        topByCapacity: topCapacityNodes,
 | 
				
			||||||
        topByChannels: topChannelsNodes,
 | 
					        topByChannels: topChannelsNodes,
 | 
				
			||||||
@ -70,6 +74,18 @@ class NodesRoutes {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private async $getTopNodesByCapacity(req: Request, res: Response): Promise<void> {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const topCapacityNodes = await nodesApi.$getTopCapacityNodes(true);
 | 
				
			||||||
 | 
					      res.header('Pragma', 'public');
 | 
				
			||||||
 | 
					      res.header('Cache-control', 'public');
 | 
				
			||||||
 | 
					      res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
 | 
				
			||||||
 | 
					      res.json(topCapacityNodes);
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      res.status(500).send(e instanceof Error ? e.message : e);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private async $getISPRanking(req: Request, res: Response): Promise<void> {
 | 
					  private async $getISPRanking(req: Request, res: Response): Promise<void> {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const groupBy = req.query.groupBy as string;
 | 
					      const groupBy = req.query.groupBy as string;
 | 
				
			||||||
 | 
				
			|||||||
@ -259,9 +259,14 @@ export interface TopNodesPerChannels {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface TopNodesPerCapacity {
 | 
					export interface TopNodesPerCapacity {
 | 
				
			||||||
  public_key: string,
 | 
					  publicKey: string,
 | 
				
			||||||
  alias: string,
 | 
					  alias: string,
 | 
				
			||||||
  capacity: number,
 | 
					  capacity: number,
 | 
				
			||||||
 | 
					  channels?: number,
 | 
				
			||||||
 | 
					  firstSeen?: number,
 | 
				
			||||||
 | 
					  updatedAt?: number,
 | 
				
			||||||
 | 
					  city?: any,
 | 
				
			||||||
 | 
					  country?: any,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface INodesRanking {
 | 
					export interface INodesRanking {
 | 
				
			||||||
 | 
				
			|||||||
@ -159,9 +159,14 @@ export interface ITopNodesPerChannels {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ITopNodesPerCapacity {
 | 
					export interface ITopNodesPerCapacity {
 | 
				
			||||||
  public_key: string,
 | 
					  publicKey: string,
 | 
				
			||||||
  alias: string,
 | 
					  alias: string,
 | 
				
			||||||
  capacity: number,
 | 
					  capacity: number,
 | 
				
			||||||
 | 
					  channels?: number,
 | 
				
			||||||
 | 
					  firstSeen?: number,
 | 
				
			||||||
 | 
					  updatedAt?: number,
 | 
				
			||||||
 | 
					  city?: any,
 | 
				
			||||||
 | 
					  country?: any,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface INodesRanking {
 | 
					export interface INodesRanking {
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
 | 
				
			|||||||
import { HttpClient, HttpParams } from '@angular/common/http';
 | 
					import { HttpClient, HttpParams } from '@angular/common/http';
 | 
				
			||||||
import { Observable } from 'rxjs';
 | 
					import { Observable } from 'rxjs';
 | 
				
			||||||
import { StateService } from '../services/state.service';
 | 
					import { StateService } from '../services/state.service';
 | 
				
			||||||
import { INodesRanking } from '../interfaces/node-api.interface';
 | 
					import { INodesRanking, ITopNodesPerCapacity } from '../interfaces/node-api.interface';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable({
 | 
					@Injectable({
 | 
				
			||||||
  providedIn: 'root'
 | 
					  providedIn: 'root'
 | 
				
			||||||
@ -63,4 +63,10 @@ export class LightningApiService {
 | 
				
			|||||||
      (interval !== undefined ? `/${interval}` : ''), { observe: 'response' }
 | 
					      (interval !== undefined ? `/${interval}` : ''), { observe: 'response' }
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getTopNodesByCapacity$(): Observable<ITopNodesPerCapacity[]> {
 | 
				
			||||||
 | 
					    return this.httpClient.get<ITopNodesPerCapacity[]>(
 | 
				
			||||||
 | 
					      this.apiBasePath + '/api/v1/lightning/nodes/rankings/capacity'
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -56,8 +56,8 @@
 | 
				
			|||||||
    <div class="col">
 | 
					    <div class="col">
 | 
				
			||||||
      <div class="card">
 | 
					      <div class="card">
 | 
				
			||||||
        <div class="card-body">
 | 
					        <div class="card-body">
 | 
				
			||||||
          <a class="title-link" href="" [routerLink]="['/lightning/nodes/ranking' | relativeUrl]">
 | 
					          <a class="title-link" href="" [routerLink]="['/lightning/nodes/top-capacity' | relativeUrl]">
 | 
				
			||||||
            <h5 class="card-title d-inline" i18n="lightning.top-capacity-nodes">Top Capacity Nodes</h5>
 | 
					            <h5 class="card-title d-inline" i18n="lightning.top-capacity-nodes">Top capacity nodes</h5>
 | 
				
			||||||
            <span> </span>
 | 
					            <span> </span>
 | 
				
			||||||
            <fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="vertical-align: 'text-top'; font-size: 13px; color: '#4a68b9'"></fa-icon>
 | 
					            <fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="vertical-align: 'text-top'; font-size: 13px; color: '#4a68b9'"></fa-icon>
 | 
				
			||||||
          </a>
 | 
					          </a>
 | 
				
			||||||
@ -70,7 +70,7 @@
 | 
				
			|||||||
    <div class="col">
 | 
					    <div class="col">
 | 
				
			||||||
      <div class="card">
 | 
					      <div class="card">
 | 
				
			||||||
        <div class="card-body">
 | 
					        <div class="card-body">
 | 
				
			||||||
          <a class="title-link" href="" [routerLink]="['/lightning/nodes/ranking' | relativeUrl]">
 | 
					          <a class="title-link" href="" [routerLink]="['/lightning/nodes/top-channels' | relativeUrl]">
 | 
				
			||||||
            <h5 class="card-title d-inline" i18n="lightning.top-channels-nodes">Most connected nodes</h5>
 | 
					            <h5 class="card-title d-inline" i18n="lightning.top-channels-nodes">Most connected nodes</h5>
 | 
				
			||||||
            <span> </span>
 | 
					            <span> </span>
 | 
				
			||||||
            <fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="vertical-align: 'text-top'; font-size: 13px; color: '#4a68b9'"></fa-icon>
 | 
					            <fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="vertical-align: 'text-top'; font-size: 13px; color: '#4a68b9'"></fa-icon>
 | 
				
			||||||
 | 
				
			|||||||
@ -24,6 +24,7 @@ import { NodesPerISP } from './nodes-per-isp/nodes-per-isp.component';
 | 
				
			|||||||
import { NodesPerCountryChartComponent } from '../lightning/nodes-per-country-chart/nodes-per-country-chart.component';
 | 
					import { NodesPerCountryChartComponent } from '../lightning/nodes-per-country-chart/nodes-per-country-chart.component';
 | 
				
			||||||
import { NodesMap } from '../lightning/nodes-map/nodes-map.component';
 | 
					import { NodesMap } from '../lightning/nodes-map/nodes-map.component';
 | 
				
			||||||
import { NodesChannelsMap } from '../lightning/nodes-channels-map/nodes-channels-map.component';
 | 
					import { NodesChannelsMap } from '../lightning/nodes-channels-map/nodes-channels-map.component';
 | 
				
			||||||
 | 
					import { NodesRanking } from '../lightning/nodes-ranking/nodes-ranking.component';
 | 
				
			||||||
import { TopNodesPerChannels } from '../lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component';
 | 
					import { TopNodesPerChannels } from '../lightning/nodes-ranking/top-nodes-per-channels/top-nodes-per-channels.component';
 | 
				
			||||||
import { TopNodesPerCapacity } from '../lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component';
 | 
					import { TopNodesPerCapacity } from '../lightning/nodes-ranking/top-nodes-per-capacity/top-nodes-per-capacity.component';
 | 
				
			||||||
@NgModule({
 | 
					@NgModule({
 | 
				
			||||||
@ -47,6 +48,7 @@ import { TopNodesPerCapacity } from '../lightning/nodes-ranking/top-nodes-per-ca
 | 
				
			|||||||
    NodesPerCountryChartComponent,
 | 
					    NodesPerCountryChartComponent,
 | 
				
			||||||
    NodesMap,
 | 
					    NodesMap,
 | 
				
			||||||
    NodesChannelsMap,
 | 
					    NodesChannelsMap,
 | 
				
			||||||
 | 
					    NodesRanking,
 | 
				
			||||||
    TopNodesPerChannels,
 | 
					    TopNodesPerChannels,
 | 
				
			||||||
    TopNodesPerCapacity,
 | 
					    TopNodesPerCapacity,
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
@ -77,6 +79,7 @@ import { TopNodesPerCapacity } from '../lightning/nodes-ranking/top-nodes-per-ca
 | 
				
			|||||||
    NodesPerCountryChartComponent,
 | 
					    NodesPerCountryChartComponent,
 | 
				
			||||||
    NodesMap,
 | 
					    NodesMap,
 | 
				
			||||||
    NodesChannelsMap,
 | 
					    NodesChannelsMap,
 | 
				
			||||||
 | 
					    NodesRanking,
 | 
				
			||||||
    TopNodesPerChannels,
 | 
					    TopNodesPerChannels,
 | 
				
			||||||
    TopNodesPerCapacity,
 | 
					    TopNodesPerCapacity,
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
 | 
				
			|||||||
@ -34,8 +34,11 @@ const routes: Routes = [
 | 
				
			|||||||
          component: NodesPerISP,
 | 
					          component: NodesPerISP,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          path: 'nodes/ranking',
 | 
					          path: 'nodes/top-capacity',
 | 
				
			||||||
          component: NodesRanking,
 | 
					          component: NodesRanking,
 | 
				
			||||||
 | 
					          data: {
 | 
				
			||||||
 | 
					            type: 'capacity'
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          path: '**',
 | 
					          path: '**',
 | 
				
			||||||
 | 
				
			|||||||
@ -1,29 +1,2 @@
 | 
				
			|||||||
<div style="width: 100%">
 | 
					<app-top-nodes-per-capacity [nodes$]="null" [widget]="false" *ngIf="type === 'capacity'">
 | 
				
			||||||
 | 
					</app-top-nodes-per-capacity>
 | 
				
			||||||
  <table class="table table-borderless" style="width: 100%">
 | 
					 | 
				
			||||||
    <thead>
 | 
					 | 
				
			||||||
      <th class="" i18n="" style="width: 33%">Rank</th>
 | 
					 | 
				
			||||||
      <th class="" i18n="" style="width: 33%">Alias</th>
 | 
					 | 
				
			||||||
      <th class="" i18n="" style="width: 33%">Placeholder</th>
 | 
					 | 
				
			||||||
    </thead>
 | 
					 | 
				
			||||||
    <tbody>
 | 
					 | 
				
			||||||
    </tbody>
 | 
					 | 
				
			||||||
  </table>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  <ng-template #skeleton>
 | 
					 | 
				
			||||||
    <tbody>
 | 
					 | 
				
			||||||
      <tr *ngFor="let item of [1,2,3,4,5,6,7,8,9,10]">
 | 
					 | 
				
			||||||
        <td class="">
 | 
					 | 
				
			||||||
          <span class="skeleton-loader"></span>
 | 
					 | 
				
			||||||
        </td>
 | 
					 | 
				
			||||||
        <td class="">
 | 
					 | 
				
			||||||
          <span class="skeleton-loader"></span>
 | 
					 | 
				
			||||||
        </td>
 | 
					 | 
				
			||||||
        <td class="">
 | 
					 | 
				
			||||||
          <span class="skeleton-loader"></span>
 | 
					 | 
				
			||||||
        </td>
 | 
					 | 
				
			||||||
      </tr>
 | 
					 | 
				
			||||||
    </tbody>
 | 
					 | 
				
			||||||
  </ng-template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
@ -1,4 +1,5 @@
 | 
				
			|||||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
 | 
					import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
 | 
				
			||||||
 | 
					import { ActivatedRoute } from '@angular/router';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-nodes-ranking',
 | 
					  selector: 'app-nodes-ranking',
 | 
				
			||||||
@ -7,9 +8,13 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
 | 
				
			|||||||
  changeDetection: ChangeDetectionStrategy.OnPush,
 | 
					  changeDetection: ChangeDetectionStrategy.OnPush,
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class NodesRanking implements OnInit {
 | 
					export class NodesRanking implements OnInit {
 | 
				
			||||||
 | 
					  type: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(private route: ActivatedRoute) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {
 | 
				
			||||||
    console.log('hi');
 | 
					    this.route.data.subscribe(data => {
 | 
				
			||||||
 | 
					      this.type = data.type;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,37 +1,59 @@
 | 
				
			|||||||
<div>
 | 
					<div [class]="!widget ? 'container-xl full-height' : ''">
 | 
				
			||||||
  <table class="table table-borderless">
 | 
					  <h1 *ngIf="!widget" class="float-left" i18n="lightning.top-100-capacity">
 | 
				
			||||||
    <thead>
 | 
					    <span>Top 100 nodes by capacity</span>
 | 
				
			||||||
      <th class="rank"></th>
 | 
					  </h1>
 | 
				
			||||||
      <th class="alias text-left" i18n="nodes.alias">Alias</th>
 | 
					
 | 
				
			||||||
      <th class="capacity text-right" i18n="node.capacity">Capacity</th>
 | 
					  <div [class]="widget ? 'widget' : 'full'">
 | 
				
			||||||
    </thead>
 | 
					    <table class="table table-borderless">
 | 
				
			||||||
    <tbody *ngIf="topNodesPerCapacity$ | async as nodes; else skeleton">
 | 
					      <thead>
 | 
				
			||||||
      <tr *ngFor="let node of nodes; let i = index;">
 | 
					        <th class="rank"></th>
 | 
				
			||||||
        <td class="rank text-left">
 | 
					        <th class="alias text-left" i18n="nodes.alias">Alias</th>
 | 
				
			||||||
          {{ i + 1 }}
 | 
					        <th class="capacity text-right" i18n="node.capacity">Capacity</th>
 | 
				
			||||||
        </td>
 | 
					        <th *ngIf="!widget" class="channels text-right" i18n="lightning.channels">Channels</th>
 | 
				
			||||||
        <td class="alias text-left">
 | 
					        <th *ngIf="!widget" class="timestamp-first text-left" i18n="lightning.first_seen">First seen</th>
 | 
				
			||||||
          <a [routerLink]="['/lightning/node' | relativeUrl, node.public_key]">{{ node.alias }}</a>
 | 
					        <th *ngIf="!widget" class="timestamp-update text-left" i18n="lightning.last_update">Last update</th>
 | 
				
			||||||
        </td>
 | 
					        <th *ngIf="!widget" class="location text-right" i18n="lightning.location">Location</th>
 | 
				
			||||||
        <td class="capacity text-right">
 | 
					      </thead>
 | 
				
			||||||
          <app-amount [satoshis]="node.capacity" [digitsInfo]="'1.2-2'" [noFiat]="true"></app-amount>
 | 
					      <tbody *ngIf="topNodesPerCapacity$ | async as nodes; else skeleton">
 | 
				
			||||||
        </td>
 | 
					        <tr *ngFor="let node of nodes; let i = index;">
 | 
				
			||||||
      </tr>
 | 
					 | 
				
			||||||
    </tbody>
 | 
					 | 
				
			||||||
    <ng-template #skeleton>
 | 
					 | 
				
			||||||
      <tbody>
 | 
					 | 
				
			||||||
        <tr *ngFor="let item of skeletonRows">
 | 
					 | 
				
			||||||
          <td class="rank text-left">
 | 
					          <td class="rank text-left">
 | 
				
			||||||
            <span class="skeleton-loader"></span>
 | 
					            {{ i + 1 }}
 | 
				
			||||||
          </td>
 | 
					          </td>
 | 
				
			||||||
          <td class="alias text-left">
 | 
					          <td class="alias text-left">
 | 
				
			||||||
            <span class="skeleton-loader"></span>
 | 
					            <a [routerLink]="['/lightning/node' | relativeUrl, node.publicKey]">{{ node.alias }}</a>
 | 
				
			||||||
          </td>
 | 
					          </td>
 | 
				
			||||||
          <td class="capacity text-right">
 | 
					          <td class="capacity text-right">
 | 
				
			||||||
            <span class="skeleton-loader"></span>
 | 
					            <app-amount [satoshis]="node.capacity" [digitsInfo]="'1.2-2'" [noFiat]="true"></app-amount>
 | 
				
			||||||
 | 
					          </td>
 | 
				
			||||||
 | 
					          <td *ngIf="!widget" class="channels text-right">
 | 
				
			||||||
 | 
					            {{ node.channels | number }}
 | 
				
			||||||
 | 
					          </td>
 | 
				
			||||||
 | 
					          <td *ngIf="!widget" class="timestamp-first text-left">
 | 
				
			||||||
 | 
					            <app-timestamp [customFormat]="'yyyy-MM-dd'" [unixTime]="node.firstSeen"></app-timestamp>
 | 
				
			||||||
 | 
					          </td>
 | 
				
			||||||
 | 
					          <td *ngIf="!widget" class="timestamp-update text-left">
 | 
				
			||||||
 | 
					            <app-timestamp [customFormat]="'yyyy-MM-dd'" [unixTime]="node.updatedAt"></app-timestamp>
 | 
				
			||||||
 | 
					          </td>
 | 
				
			||||||
 | 
					          <td *ngIf="!widget" class="location text-right text-truncate">
 | 
				
			||||||
 | 
					            {{ node?.city?.en ?? '-' }}
 | 
				
			||||||
          </td>
 | 
					          </td>
 | 
				
			||||||
        </tr>
 | 
					        </tr>
 | 
				
			||||||
      </tbody>
 | 
					      </tbody>
 | 
				
			||||||
    </ng-template>
 | 
					      <ng-template #skeleton>
 | 
				
			||||||
  </table>
 | 
					        <tbody>
 | 
				
			||||||
 | 
					          <tr *ngFor="let item of skeletonRows">
 | 
				
			||||||
 | 
					            <td class="rank text-left">
 | 
				
			||||||
 | 
					              <span class="skeleton-loader"></span>
 | 
				
			||||||
 | 
					            </td>
 | 
				
			||||||
 | 
					            <td class="alias text-left">
 | 
				
			||||||
 | 
					              <span class="skeleton-loader"></span>
 | 
				
			||||||
 | 
					            </td>
 | 
				
			||||||
 | 
					            <td class="capacity text-right">
 | 
				
			||||||
 | 
					              <span class="skeleton-loader"></span>
 | 
				
			||||||
 | 
					            </td>
 | 
				
			||||||
 | 
					          </tr>
 | 
				
			||||||
 | 
					        </tbody>
 | 
				
			||||||
 | 
					      </ng-template>
 | 
				
			||||||
 | 
					    </table>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
@ -1,8 +1,20 @@
 | 
				
			|||||||
 | 
					.container-xl {
 | 
				
			||||||
 | 
					  max-width: 1400px;
 | 
				
			||||||
 | 
					  padding-bottom: 100px;
 | 
				
			||||||
 | 
					  @media (min-width: 767.98px) {
 | 
				
			||||||
 | 
					    padding-left: 50px;
 | 
				
			||||||
 | 
					    padding-right: 50px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.table td, .table th {
 | 
					.table td, .table th {
 | 
				
			||||||
  padding: 0.5rem;
 | 
					  padding: 0.5rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.rank {
 | 
					.full .rank {
 | 
				
			||||||
 | 
					  width: 5%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.widget .rank {
 | 
				
			||||||
  @media (min-width: 767.98px) {
 | 
					  @media (min-width: 767.98px) {
 | 
				
			||||||
    width: 13%;
 | 
					    width: 13%;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -12,7 +24,16 @@
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.alias {
 | 
					.full .alias {
 | 
				
			||||||
 | 
					  width: 10%;
 | 
				
			||||||
 | 
					  overflow: hidden;
 | 
				
			||||||
 | 
					  text-overflow: ellipsis;
 | 
				
			||||||
 | 
					  max-width: 350px;
 | 
				
			||||||
 | 
					  @media (max-width: 767.98px) {
 | 
				
			||||||
 | 
					    max-width: 175px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.widget .alias {
 | 
				
			||||||
  width: 55%;
 | 
					  width: 55%;
 | 
				
			||||||
  overflow: hidden;
 | 
					  overflow: hidden;
 | 
				
			||||||
  text-overflow: ellipsis;
 | 
					  text-overflow: ellipsis;
 | 
				
			||||||
@ -21,10 +42,43 @@
 | 
				
			|||||||
    max-width: 175px;
 | 
					    max-width: 175px;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
.capacity {
 | 
					
 | 
				
			||||||
 | 
					.full .capacity {
 | 
				
			||||||
 | 
					  width: 10%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.widget .capacity {
 | 
				
			||||||
  width: 32%;
 | 
					  width: 32%;
 | 
				
			||||||
  @media (max-width: 767.98px) {
 | 
					  @media (max-width: 767.98px) {
 | 
				
			||||||
    padding-left: 0px;
 | 
					    padding-left: 0px;
 | 
				
			||||||
    padding-right: 0px;
 | 
					    padding-right: 0px;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.full .channels {
 | 
				
			||||||
 | 
					  width: 15%;
 | 
				
			||||||
 | 
					  padding-right: 50px;
 | 
				
			||||||
 | 
					  @media (max-width: 767.98px) {
 | 
				
			||||||
 | 
					    display: none;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.full .timestamp-first {
 | 
				
			||||||
 | 
					  width: 15%;
 | 
				
			||||||
 | 
					  @media (max-width: 767.98px) {
 | 
				
			||||||
 | 
					    display: none;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.full .timestamp-update {
 | 
				
			||||||
 | 
					  width: 15%;
 | 
				
			||||||
 | 
					  @media (max-width: 767.98px) {
 | 
				
			||||||
 | 
					    display: none;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.full .location {
 | 
				
			||||||
 | 
					  width: 10%;
 | 
				
			||||||
 | 
					  @media (max-width: 767.98px) {
 | 
				
			||||||
 | 
					    display: none;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
 | 
					import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
 | 
				
			||||||
import { map, Observable } from 'rxjs';
 | 
					import { map, Observable } from 'rxjs';
 | 
				
			||||||
import { INodesRanking, ITopNodesPerCapacity } from 'src/app/interfaces/node-api.interface';
 | 
					import { INodesRanking, ITopNodesPerCapacity } from 'src/app/interfaces/node-api.interface';
 | 
				
			||||||
 | 
					import { LightningApiService } from '../../lightning-api.service';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-top-nodes-per-capacity',
 | 
					  selector: 'app-top-nodes-per-capacity',
 | 
				
			||||||
@ -15,20 +16,24 @@ export class TopNodesPerCapacity implements OnInit {
 | 
				
			|||||||
  topNodesPerCapacity$: Observable<ITopNodesPerCapacity[]>;
 | 
					  topNodesPerCapacity$: Observable<ITopNodesPerCapacity[]>;
 | 
				
			||||||
  skeletonRows: number[] = [];
 | 
					  skeletonRows: number[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(private apiService: LightningApiService) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {
 | 
				
			||||||
    for (let i = 1; i <= (this.widget ? 10 : 100); ++i) {
 | 
					    for (let i = 1; i <= (this.widget ? 10 : 100); ++i) {
 | 
				
			||||||
      this.skeletonRows.push(i);
 | 
					      this.skeletonRows.push(i);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.topNodesPerCapacity$ = this.nodes$.pipe(
 | 
					    console.log(this.widget);
 | 
				
			||||||
      map((ranking) => {
 | 
					
 | 
				
			||||||
        if (this.widget === true) {
 | 
					    if (this.widget === false) {
 | 
				
			||||||
 | 
					      this.topNodesPerCapacity$ = this.apiService.getTopNodesByCapacity$();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      this.topNodesPerCapacity$ = this.nodes$.pipe(
 | 
				
			||||||
 | 
					        map((ranking) => {
 | 
				
			||||||
          return ranking.topByCapacity.slice(0, 10);
 | 
					          return ranking.topByCapacity.slice(0, 10);
 | 
				
			||||||
        } else {
 | 
					        })
 | 
				
			||||||
          return ranking.topByCapacity;
 | 
					      );
 | 
				
			||||||
        }
 | 
					    }
 | 
				
			||||||
      })
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user