Create top 100 node per channel count component
This commit is contained in:
		
							parent
							
								
									2359e44b16
								
							
						
					
					
						commit
						6421bc82f2
					
				@ -2,7 +2,7 @@ import logger from '../../logger';
 | 
			
		||||
import DB from '../../database';
 | 
			
		||||
import { ResultSetHeader } from 'mysql2';
 | 
			
		||||
import { ILightningApi } from '../lightning/lightning-api.interface';
 | 
			
		||||
import { TopNodesPerCapacity, TopNodesPerChannels } from '../../mempool.interfaces';
 | 
			
		||||
import { ITopNodesPerCapacity, ITopNodesPerChannels } from '../../mempool.interfaces';
 | 
			
		||||
 | 
			
		||||
class NodesApi {
 | 
			
		||||
  public async $getNode(public_key: string): Promise<any> {
 | 
			
		||||
@ -113,7 +113,7 @@ class NodesApi {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public async $getTopCapacityNodes(full: boolean): Promise<TopNodesPerCapacity[]> {
 | 
			
		||||
  public async $getTopCapacityNodes(full: boolean): Promise<ITopNodesPerCapacity[]> {
 | 
			
		||||
    try {
 | 
			
		||||
      let [rows]: any[] = await DB.query('SELECT UNIX_TIMESTAMP(MAX(added)) as maxAdded FROM node_stats');
 | 
			
		||||
      const latestDate = rows[0].maxAdded;
 | 
			
		||||
@ -161,21 +161,46 @@ class NodesApi {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public async $getTopChannelsNodes(): Promise<TopNodesPerChannels[]> {
 | 
			
		||||
  public async $getTopChannelsNodes(full: boolean): Promise<ITopNodesPerChannels[]> {
 | 
			
		||||
    try {
 | 
			
		||||
      let [rows]: any[] = await DB.query('SELECT UNIX_TIMESTAMP(MAX(added)) as maxAdded FROM node_stats');
 | 
			
		||||
      const latestDate = rows[0].maxAdded;
 | 
			
		||||
 | 
			
		||||
      const query = `
 | 
			
		||||
        SELECT nodes.public_key, IF(nodes.alias = '', SUBSTRING(nodes.public_key, 1, 20), alias) as alias,
 | 
			
		||||
          node_stats.channels
 | 
			
		||||
        FROM node_stats
 | 
			
		||||
        JOIN nodes ON nodes.public_key = node_stats.public_key
 | 
			
		||||
        WHERE added = FROM_UNIXTIME(${latestDate})
 | 
			
		||||
        ORDER BY channels DESC
 | 
			
		||||
        LIMIT 100;
 | 
			
		||||
      `;
 | 
			
		||||
      [rows] = await DB.query(query);
 | 
			
		||||
      let query: string;
 | 
			
		||||
      if (full === false) {
 | 
			
		||||
        query = `
 | 
			
		||||
          SELECT nodes.public_key, IF(nodes.alias = '', SUBSTRING(nodes.public_key, 1, 20), alias) as alias,
 | 
			
		||||
            node_stats.channels
 | 
			
		||||
          FROM node_stats
 | 
			
		||||
          JOIN nodes ON nodes.public_key = node_stats.public_key
 | 
			
		||||
          WHERE added = FROM_UNIXTIME(${latestDate})
 | 
			
		||||
          ORDER BY channels DESC
 | 
			
		||||
          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.channels, 0) as INT) as channels,
 | 
			
		||||
            CAST(COALESCE(node_stats.capacity, 0) as INT) as capacity,
 | 
			
		||||
            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 channels 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;
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,7 @@ class NodesRoutes {
 | 
			
		||||
      .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/rankings/channels', this.$getTopNodesByChannels)
 | 
			
		||||
      .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)
 | 
			
		||||
    ;
 | 
			
		||||
@ -61,7 +62,7 @@ class NodesRoutes {
 | 
			
		||||
  private async $getNodesRanking(req: Request, res: Response): Promise<void> {
 | 
			
		||||
    try {
 | 
			
		||||
      const topCapacityNodes = await nodesApi.$getTopCapacityNodes(false);
 | 
			
		||||
      const topChannelsNodes = await nodesApi.$getTopChannelsNodes();
 | 
			
		||||
      const topChannelsNodes = await nodesApi.$getTopChannelsNodes(false);
 | 
			
		||||
      res.header('Pragma', 'public');
 | 
			
		||||
      res.header('Cache-control', 'public');
 | 
			
		||||
      res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
 | 
			
		||||
@ -86,6 +87,18 @@ class NodesRoutes {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async $getTopNodesByChannels(req: Request, res: Response): Promise<void> {
 | 
			
		||||
    try {
 | 
			
		||||
      const topCapacityNodes = await nodesApi.$getTopChannelsNodes(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> {
 | 
			
		||||
    try {
 | 
			
		||||
      const groupBy = req.query.groupBy as string;
 | 
			
		||||
 | 
			
		||||
@ -252,13 +252,18 @@ export interface RewardStats {
 | 
			
		||||
  totalTx: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface TopNodesPerChannels {
 | 
			
		||||
  public_key: string,
 | 
			
		||||
export interface ITopNodesPerChannels {
 | 
			
		||||
  publicKey: string,
 | 
			
		||||
  alias: string,
 | 
			
		||||
  channels: number,
 | 
			
		||||
  channels?: number,
 | 
			
		||||
  capacity: number,
 | 
			
		||||
  firstSeen?: number,
 | 
			
		||||
  updatedAt?: number,
 | 
			
		||||
  city?: any,
 | 
			
		||||
  country?: any,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface TopNodesPerCapacity {
 | 
			
		||||
export interface ITopNodesPerCapacity {
 | 
			
		||||
  publicKey: string,
 | 
			
		||||
  alias: string,
 | 
			
		||||
  capacity: number,
 | 
			
		||||
@ -270,6 +275,6 @@ export interface TopNodesPerCapacity {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface INodesRanking {
 | 
			
		||||
  topByCapacity: TopNodesPerCapacity[];
 | 
			
		||||
  topByChannels: TopNodesPerChannels[];
 | 
			
		||||
  topByCapacity: ITopNodesPerCapacity[];
 | 
			
		||||
  topByChannels: ITopNodesPerChannels[];
 | 
			
		||||
}
 | 
			
		||||
@ -153,9 +153,14 @@ export interface RewardStats {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ITopNodesPerChannels {
 | 
			
		||||
  public_key: string,
 | 
			
		||||
  publicKey: string,
 | 
			
		||||
  alias: string,
 | 
			
		||||
  channels: number,
 | 
			
		||||
  channels?: number,
 | 
			
		||||
  capacity: number,
 | 
			
		||||
  firstSeen?: number,
 | 
			
		||||
  updatedAt?: number,
 | 
			
		||||
  city?: any,
 | 
			
		||||
  country?: any,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ITopNodesPerCapacity {
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
 | 
			
		||||
import { HttpClient, HttpParams } from '@angular/common/http';
 | 
			
		||||
import { Observable } from 'rxjs';
 | 
			
		||||
import { StateService } from '../services/state.service';
 | 
			
		||||
import { INodesRanking, ITopNodesPerCapacity } from '../interfaces/node-api.interface';
 | 
			
		||||
import { INodesRanking, ITopNodesPerCapacity, ITopNodesPerChannels } from '../interfaces/node-api.interface';
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
  providedIn: 'root'
 | 
			
		||||
@ -69,4 +69,10 @@ export class LightningApiService {
 | 
			
		||||
      this.apiBasePath + '/api/v1/lightning/nodes/rankings/capacity'
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getTopNodesByChannels$(): Observable<ITopNodesPerChannels[]> {
 | 
			
		||||
    return this.httpClient.get<ITopNodesPerChannels[]>(
 | 
			
		||||
      this.apiBasePath + '/api/v1/lightning/nodes/rankings/channels'
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -40,6 +40,13 @@ const routes: Routes = [
 | 
			
		||||
            type: 'capacity'
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          path: 'nodes/top-channels',
 | 
			
		||||
          component: NodesRanking,
 | 
			
		||||
          data: {
 | 
			
		||||
            type: 'channels'
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          path: '**',
 | 
			
		||||
          redirectTo: ''
 | 
			
		||||
 | 
			
		||||
@ -1,2 +1,5 @@
 | 
			
		||||
<app-top-nodes-per-capacity [nodes$]="null" [widget]="false" *ngIf="type === 'capacity'">
 | 
			
		||||
</app-top-nodes-per-capacity>
 | 
			
		||||
</app-top-nodes-per-capacity>
 | 
			
		||||
 | 
			
		||||
<app-top-nodes-per-channels [nodes$]="null" [widget]="false" *ngIf="type === 'channels'">
 | 
			
		||||
</app-top-nodes-per-channels>
 | 
			
		||||
@ -51,6 +51,18 @@
 | 
			
		||||
            <td class="capacity text-right">
 | 
			
		||||
              <span class="skeleton-loader"></span>
 | 
			
		||||
            </td>
 | 
			
		||||
            <td *ngIf="!widget" class="channels text-right">
 | 
			
		||||
              <span class="skeleton-loader"></span>
 | 
			
		||||
            </td>
 | 
			
		||||
            <td *ngIf="!widget" class="timestamp-first text-left">
 | 
			
		||||
              <span class="skeleton-loader"></span>
 | 
			
		||||
            </td>
 | 
			
		||||
            <td *ngIf="!widget" class="timestamp-update text-left">
 | 
			
		||||
              <span class="skeleton-loader"></span>
 | 
			
		||||
            </td>
 | 
			
		||||
            <td *ngIf="!widget" class="location text-right text-truncate">
 | 
			
		||||
              <span class="skeleton-loader"></span>
 | 
			
		||||
            </td>
 | 
			
		||||
          </tr>
 | 
			
		||||
        </tbody>
 | 
			
		||||
      </ng-template>
 | 
			
		||||
 | 
			
		||||
@ -23,8 +23,6 @@ export class TopNodesPerCapacity implements OnInit {
 | 
			
		||||
      this.skeletonRows.push(i);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    console.log(this.widget);
 | 
			
		||||
 | 
			
		||||
    if (this.widget === false) {
 | 
			
		||||
      this.topNodesPerCapacity$ = this.apiService.getTopNodesByCapacity$();
 | 
			
		||||
    } else {
 | 
			
		||||
 | 
			
		||||
@ -1,37 +1,71 @@
 | 
			
		||||
<div>
 | 
			
		||||
  <table class="table table-borderless">
 | 
			
		||||
    <thead>
 | 
			
		||||
      <th class="rank"></th>
 | 
			
		||||
      <th class="alias text-left" i18n="nodes.alias">Alias</th>
 | 
			
		||||
      <th class="channels text-right" i18n="node.channels">Channels</th>
 | 
			
		||||
    </thead>
 | 
			
		||||
    <tbody *ngIf="topNodesPerChannels$ | async as nodes; else skeleton">
 | 
			
		||||
      <tr *ngFor="let node of nodes; let i = index;">
 | 
			
		||||
        <td class="rank text-left">
 | 
			
		||||
          {{ i + 1 }}
 | 
			
		||||
        </td>
 | 
			
		||||
        <td class="alias text-left">
 | 
			
		||||
          <a [routerLink]="['/lightning/node' | relativeUrl, node.public_key]">{{ node.alias }}</a>
 | 
			
		||||
        </td>
 | 
			
		||||
          <td class="channels text-right">
 | 
			
		||||
          {{ node.channels | number }}
 | 
			
		||||
        </td>
 | 
			
		||||
      </tr>
 | 
			
		||||
    </tbody>
 | 
			
		||||
    <ng-template #skeleton>
 | 
			
		||||
      <tbody>
 | 
			
		||||
        <tr *ngFor="let item of skeletonRows">
 | 
			
		||||
<div [class]="!widget ? 'container-xl full-height' : ''">
 | 
			
		||||
  <h1 *ngIf="!widget" class="float-left" i18n="lightning.top-100-channel">
 | 
			
		||||
    <span>Top 100 nodes by channel count</span>
 | 
			
		||||
  </h1>
 | 
			
		||||
 | 
			
		||||
  <div [class]="widget ? 'widget' : 'full'">
 | 
			
		||||
    <table class="table table-borderless">
 | 
			
		||||
      <thead>
 | 
			
		||||
        <th class="rank"></th>
 | 
			
		||||
        <th class="alias text-left" i18n="nodes.alias">Alias</th>
 | 
			
		||||
        <th class="channels text-right" i18n="node.channels">Channels</th>
 | 
			
		||||
        <th *ngIf="!widget" class="capacity text-right" i18n="lightning.capacity">Capacity</th>
 | 
			
		||||
        <th *ngIf="!widget" class="timestamp-first text-left" i18n="lightning.first_seen">First seen</th>
 | 
			
		||||
        <th *ngIf="!widget" class="timestamp-update text-left" i18n="lightning.last_update">Last update</th>
 | 
			
		||||
        <th *ngIf="!widget" class="location text-right" i18n="lightning.location">Location</th>
 | 
			
		||||
      </thead>
 | 
			
		||||
      <tbody *ngIf="topNodesPerChannels$ | async as nodes; else skeleton">
 | 
			
		||||
        <tr *ngFor="let node of nodes; let i = index;">
 | 
			
		||||
          <td class="rank text-left">
 | 
			
		||||
            <span class="skeleton-loader"></span>
 | 
			
		||||
            {{ i + 1 }}
 | 
			
		||||
          </td>
 | 
			
		||||
          <td class="alias text-left">
 | 
			
		||||
            <span class="skeleton-loader"></span>
 | 
			
		||||
            <a [routerLink]="['/lightning/node' | relativeUrl, node.publicKey]">{{ node.alias }}</a>
 | 
			
		||||
          </td>
 | 
			
		||||
          <td class="channels text-right">
 | 
			
		||||
            <span class="skeleton-loader"></span>
 | 
			
		||||
            {{ node.channels | number }}
 | 
			
		||||
          </td>
 | 
			
		||||
          <td *ngIf="!widget" class="capacity text-right">
 | 
			
		||||
            <app-amount [satoshis]="node.capacity" [digitsInfo]="'1.2-2'" [noFiat]="true"></app-amount>
 | 
			
		||||
          </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>
 | 
			
		||||
        </tr>
 | 
			
		||||
      </tbody>
 | 
			
		||||
    </ng-template>
 | 
			
		||||
  </table>
 | 
			
		||||
      <ng-template #skeleton>
 | 
			
		||||
        <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="channels text-right">
 | 
			
		||||
              <span class="skeleton-loader"></span>
 | 
			
		||||
            </td>
 | 
			
		||||
            <td *ngIf="!widget" class="capacity text-right">
 | 
			
		||||
              <span class="skeleton-loader"></span>
 | 
			
		||||
            </td>
 | 
			
		||||
            <td *ngIf="!widget" class="timestamp-first text-left">
 | 
			
		||||
              <span class="skeleton-loader"></span>
 | 
			
		||||
            </td>
 | 
			
		||||
            <td *ngIf="!widget" class="timestamp-update text-left">
 | 
			
		||||
              <span class="skeleton-loader"></span>
 | 
			
		||||
            </td>
 | 
			
		||||
            <td *ngIf="!widget" class="location text-right text-truncate">
 | 
			
		||||
              <span class="skeleton-loader"></span>
 | 
			
		||||
            </td>
 | 
			
		||||
          </tr>
 | 
			
		||||
        </tbody>
 | 
			
		||||
      </ng-template>
 | 
			
		||||
    </table>
 | 
			
		||||
  </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 {
 | 
			
		||||
  padding: 0.5rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rank {
 | 
			
		||||
.full .rank {
 | 
			
		||||
  width: 5%;
 | 
			
		||||
}
 | 
			
		||||
.widget .rank {
 | 
			
		||||
  @media (min-width: 767.98px) {
 | 
			
		||||
    width: 13%;
 | 
			
		||||
  }
 | 
			
		||||
@ -12,8 +24,8 @@
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.alias {
 | 
			
		||||
  width: 60%;
 | 
			
		||||
.full .alias {
 | 
			
		||||
  width: 10%;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  text-overflow: ellipsis;
 | 
			
		||||
  max-width: 350px;
 | 
			
		||||
@ -21,10 +33,52 @@
 | 
			
		||||
    max-width: 175px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
.channels {
 | 
			
		||||
  width: 27%;
 | 
			
		||||
.widget .alias {
 | 
			
		||||
  width: 55%;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  text-overflow: ellipsis;
 | 
			
		||||
  max-width: 350px;
 | 
			
		||||
  @media (max-width: 767.98px) {
 | 
			
		||||
    max-width: 175px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.full .channels {
 | 
			
		||||
  width: 10%;
 | 
			
		||||
}
 | 
			
		||||
.widget .channels {
 | 
			
		||||
  width: 32%;
 | 
			
		||||
  @media (max-width: 767.98px) {
 | 
			
		||||
    padding-left: 0px;
 | 
			
		||||
    padding-right: 0px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.full .capacity {
 | 
			
		||||
  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 { map, Observable } from 'rxjs';
 | 
			
		||||
import { INodesRanking, ITopNodesPerChannels } from 'src/app/interfaces/node-api.interface';
 | 
			
		||||
import { LightningApiService } from '../../lightning-api.service';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-top-nodes-per-channels',
 | 
			
		||||
@ -15,20 +16,22 @@ export class TopNodesPerChannels implements OnInit {
 | 
			
		||||
  topNodesPerChannels$: Observable<ITopNodesPerChannels[]>;
 | 
			
		||||
  skeletonRows: number[] = [];
 | 
			
		||||
 | 
			
		||||
  constructor(private apiService: LightningApiService) {}
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    for (let i = 1; i <= (this.widget ? 10 : 100); ++i) {
 | 
			
		||||
      this.skeletonRows.push(i);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.topNodesPerChannels$ = this.nodes$.pipe(
 | 
			
		||||
      map((ranking) => {
 | 
			
		||||
        if (this.widget === true) {
 | 
			
		||||
    if (this.widget === false) {
 | 
			
		||||
      this.topNodesPerChannels$ = this.apiService.getTopNodesByChannels$();
 | 
			
		||||
    } else {
 | 
			
		||||
      this.topNodesPerChannels$ = this.nodes$.pipe(
 | 
			
		||||
        map((ranking) => {
 | 
			
		||||
          return ranking.topByChannels.slice(0, 10);
 | 
			
		||||
        } else {
 | 
			
		||||
          return ranking.topByChannels;
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    )
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user