Node and Channel pages improvements
This commit is contained in:
		
							parent
							
								
									179f959f0f
								
							
						
					
					
						commit
						e12f32ef80
					
				@ -66,9 +66,9 @@ export class SearchFormComponent implements OnInit {
 | 
			
		||||
          if (this.network === 'bisq' && text.match(/^(b)[^c]/i)) {
 | 
			
		||||
            return text.substr(1);
 | 
			
		||||
          }
 | 
			
		||||
          return text;
 | 
			
		||||
          return text.trim();
 | 
			
		||||
        }),
 | 
			
		||||
        debounceTime(300),
 | 
			
		||||
        debounceTime(250),
 | 
			
		||||
        distinctUntilChanged(),
 | 
			
		||||
        switchMap((text) => {
 | 
			
		||||
          if (!text.length) {
 | 
			
		||||
@ -82,7 +82,10 @@ export class SearchFormComponent implements OnInit {
 | 
			
		||||
          }
 | 
			
		||||
          return zip(
 | 
			
		||||
            this.electrsApiService.getAddressesByPrefix$(text).pipe(catchError(() => of([]))),
 | 
			
		||||
            this.apiService.lightningSearch$(text),
 | 
			
		||||
            this.apiService.lightningSearch$(text).pipe(catchError(() => of({
 | 
			
		||||
              nodes: [],
 | 
			
		||||
              channels: [],
 | 
			
		||||
            }))),
 | 
			
		||||
          );
 | 
			
		||||
        }),
 | 
			
		||||
        map((result: any[]) => {
 | 
			
		||||
 | 
			
		||||
@ -17,8 +17,8 @@
 | 
			
		||||
  </ng-template>
 | 
			
		||||
  <ng-template [ngIf]="results.channels.length">
 | 
			
		||||
    <div class="card-title">Lightning Channels</div>
 | 
			
		||||
    <ng-template [class.active]="results.addresses.length + results.nodes.length + i === activeIdx" ngFor [ngForOf]="results.channels" let-channel let-i="index">
 | 
			
		||||
      <button (click)="clickItem(results.addresses.length + results.nodes.length + i)" type="button" role="option" class="dropdown-item">
 | 
			
		||||
    <ng-template ngFor [ngForOf]="results.channels" let-channel let-i="index">
 | 
			
		||||
      <button (click)="clickItem(results.addresses.length + results.nodes.length + i)" [class.active]="results.addresses.length + results.nodes.length + i === activeIdx" type="button" role="option" class="dropdown-item">
 | 
			
		||||
        <ngb-highlight [result]="channel.short_id" [term]="searchTerm"></ngb-highlight>  <span class="symbol">{{ channel.id }}</span>
 | 
			
		||||
      </button>
 | 
			
		||||
    </ng-template>
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,40 @@
 | 
			
		||||
<div class="mb-2">
 | 
			
		||||
  <h2 class="mb-0">{{ channel.alias || '?' }}</h2>
 | 
			
		||||
  <a [routerLink]="['/lightning/node' | relativeUrl, channel.public_key]" >
 | 
			
		||||
    {{ channel.public_key | shortenString : 12 }}
 | 
			
		||||
  </a>
 | 
			
		||||
  <app-clipboard [text]="channel.node1_public_key"></app-clipboard>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="box">
 | 
			
		||||
 | 
			
		||||
  <div class="col-md">
 | 
			
		||||
    <table class="table table-borderless table-striped">
 | 
			
		||||
      <tbody>
 | 
			
		||||
        <tr>
 | 
			
		||||
          <td i18n="address.total-sent">Fee rate</td>
 | 
			
		||||
          <td>
 | 
			
		||||
            {{ channel.fee_rate }} <span class="symbol">ppm ({{ channel.fee_rate / 10000 | number }}%)</span>
 | 
			
		||||
          </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
          <td i18n="address.total-sent">Base fee</td>
 | 
			
		||||
          <td>
 | 
			
		||||
            <app-sats [satoshis]="channel.base_fee_mtokens / 1000"></app-sats>
 | 
			
		||||
          </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
          <td i18n="address.total-sent">Min HTLC</td>
 | 
			
		||||
          <td>
 | 
			
		||||
            <app-sats [satoshis]="channel.min_htlc_mtokens / 1000"></app-sats>
 | 
			
		||||
          </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
          <td i18n="address.total-sent">Max HTLC</td>
 | 
			
		||||
          <td>
 | 
			
		||||
            <app-sats [satoshis]="channel.max_htlc_mtokens / 1000"></app-sats>
 | 
			
		||||
          </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
      </tbody>
 | 
			
		||||
    </table>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
@ -0,0 +1,14 @@
 | 
			
		||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-channel-box',
 | 
			
		||||
  templateUrl: './channel-box.component.html',
 | 
			
		||||
  styleUrls: ['./channel-box.component.scss'],
 | 
			
		||||
  changeDetection: ChangeDetectionStrategy.OnPush,
 | 
			
		||||
})
 | 
			
		||||
export class ChannelBoxComponent {
 | 
			
		||||
  @Input() channel: any;
 | 
			
		||||
 | 
			
		||||
  constructor() { }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -1,11 +1,15 @@
 | 
			
		||||
<div class="container-xl" *ngIf="(channel$ | async) as channel">
 | 
			
		||||
  <div class="mb-2">
 | 
			
		||||
    <h1 i18n="shared.address" class="mb-0">Channel <a [routerLink]="['/lightning/channel' | relativeUrl, channel.id]">{{ channel.short_id }}</a> <app-clipboard [text]="channel.id"></app-clipboard></h1>
 | 
			
		||||
    <div class="badges">
 | 
			
		||||
      <span class="badge rounded-pill badge-secondary" *ngIf="channel.status === 0">Inactive</span>
 | 
			
		||||
      <span class="badge rounded-pill badge-success" *ngIf="channel.status === 1">Active</span>
 | 
			
		||||
      <span class="badge rounded-pill badge-danger" *ngIf="channel.status === 2">Closed</span>
 | 
			
		||||
    </div>
 | 
			
		||||
  <div class="title-container">
 | 
			
		||||
    <h1 class="mb-0">{{ channel.short_id }}</h1>
 | 
			
		||||
    <span class="tx-link">
 | 
			
		||||
      <a [routerLink]="['/lightning/channel' | relativeUrl, channel.id]">{{ channel.id }}</a>
 | 
			
		||||
      <app-clipboard [text]="channel.id"></app-clipboard>
 | 
			
		||||
    </span>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="badges mb-2">
 | 
			
		||||
    <span class="badge rounded-pill badge-secondary" *ngIf="channel.status === 0">Inactive</span>
 | 
			
		||||
    <span class="badge rounded-pill badge-success" *ngIf="channel.status === 1">Active</span>
 | 
			
		||||
    <span class="badge rounded-pill badge-danger" *ngIf="channel.status === 2">Closed</span>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <div class="clearfix"></div>
 | 
			
		||||
@ -42,7 +46,7 @@
 | 
			
		||||
            <tbody>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td i18n="address.total-received">Capacity</td>
 | 
			
		||||
                <td><app-sats [satoshis]="channel.capacity"></app-sats>  <app-fiat [value]="channel.capacity" digitsInfo="1.2-2"></app-fiat></td>
 | 
			
		||||
                <td><app-sats [satoshis]="channel.capacity"></app-sats><app-fiat [value]="channel.capacity" digitsInfo="1.0-0"></app-fiat></td>
 | 
			
		||||
              </tr>
 | 
			
		||||
            </tbody>
 | 
			
		||||
          </table>
 | 
			
		||||
@ -55,90 +59,10 @@
 | 
			
		||||
 | 
			
		||||
    <div class="row row-cols-1 row-cols-md-2">
 | 
			
		||||
      <div class="col">
 | 
			
		||||
        <div class="mb-2">
 | 
			
		||||
          <h2 class="mb-0">{{ channel.alias_left || '?' }}</h2>
 | 
			
		||||
          <a [routerLink]="['/lightning/node' | relativeUrl, channel.node1_public_key]" >
 | 
			
		||||
            {{ channel.node1_public_key | shortenString : 18 }}
 | 
			
		||||
          </a>
 | 
			
		||||
          <app-clipboard [text]="channel.node1_public_key"></app-clipboard>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="box">
 | 
			
		||||
 | 
			
		||||
          <div class="row">
 | 
			
		||||
            <div class="col-md">
 | 
			
		||||
              <table class="table table-borderless table-striped">
 | 
			
		||||
                <tbody>
 | 
			
		||||
                  <tr>
 | 
			
		||||
                    <td i18n="address.total-sent">Fee rate</td>
 | 
			
		||||
                    <td>
 | 
			
		||||
                      {{ channel.node1_fee_rate / 10000 | number }}%
 | 
			
		||||
                    </td>
 | 
			
		||||
                  </tr>
 | 
			
		||||
                  <tr>
 | 
			
		||||
                    <td i18n="address.total-sent">Base fee</td>
 | 
			
		||||
                    <td>
 | 
			
		||||
                      <app-sats [satoshis]="channel.node1_base_fee_mtokens / 1000"></app-sats>
 | 
			
		||||
                    </td>
 | 
			
		||||
                  </tr>
 | 
			
		||||
                  <tr>
 | 
			
		||||
                    <td i18n="address.total-sent">Min HTLC</td>
 | 
			
		||||
                    <td>
 | 
			
		||||
                      <app-sats [satoshis]="channel.node1_min_htlc_mtokens / 1000"></app-sats>
 | 
			
		||||
                    </td>
 | 
			
		||||
                  </tr>
 | 
			
		||||
                  <tr>
 | 
			
		||||
                    <td i18n="address.total-sent">Max HTLC</td>
 | 
			
		||||
                    <td>
 | 
			
		||||
                      <app-sats [satoshis]="channel.node1_max_htlc_mtokens / 1000"></app-sats>
 | 
			
		||||
                    </td>
 | 
			
		||||
                  </tr>
 | 
			
		||||
                </tbody>
 | 
			
		||||
              </table>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <app-channel-box [channel]="channel.node_left"></app-channel-box>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="col">
 | 
			
		||||
        <div class="mb-2">
 | 
			
		||||
          <h2 class="mb-0">{{ channel.alias_right || '?' }}</h2>
 | 
			
		||||
          <a [routerLink]="['/lightning/node' | relativeUrl, channel.node2_public_key]" >
 | 
			
		||||
            {{ channel.node2_public_key | shortenString : 18 }}
 | 
			
		||||
          </a>
 | 
			
		||||
          <app-clipboard [text]="channel.node1_public_key"></app-clipboard>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="box">
 | 
			
		||||
 | 
			
		||||
          <div class="col-md">
 | 
			
		||||
            <table class="table table-borderless table-striped">
 | 
			
		||||
              <tbody>
 | 
			
		||||
                <tr>
 | 
			
		||||
                  <td i18n="address.total-sent">Fee rate</td>
 | 
			
		||||
                  <td>
 | 
			
		||||
                    {{ channel.node2_fee_rate / 10000 | number }}%
 | 
			
		||||
                  </td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                <tr>
 | 
			
		||||
                  <td i18n="address.total-sent">Base fee</td>
 | 
			
		||||
                  <td>
 | 
			
		||||
                    <app-sats [satoshis]="channel.node2_base_fee_mtokens / 1000"></app-sats>
 | 
			
		||||
                  </td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                <tr>
 | 
			
		||||
                  <td i18n="address.total-sent">Min HTLC</td>
 | 
			
		||||
                  <td>
 | 
			
		||||
                    <app-sats [satoshis]="channel.node2_min_htlc_mtokens / 1000"></app-sats>
 | 
			
		||||
                  </td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                <tr>
 | 
			
		||||
                  <td i18n="address.total-sent">Max HTLC</td>
 | 
			
		||||
                  <td>
 | 
			
		||||
                    <app-sats [satoshis]="channel.node2_max_htlc_mtokens / 1000"></app-sats>
 | 
			
		||||
                  </td>
 | 
			
		||||
                </tr>
 | 
			
		||||
              </tbody>
 | 
			
		||||
            </table>
 | 
			
		||||
          </div>
 | 
			
		||||
      </div>
 | 
			
		||||
        <app-channel-box [channel]="channel.node_right"></app-channel-box>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,41 @@
 | 
			
		||||
.title-container {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: row;
 | 
			
		||||
 | 
			
		||||
  @media (max-width: 768px) {
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tx-link {
 | 
			
		||||
  display: flex;
 | 
			
		||||
	flex-grow: 1;
 | 
			
		||||
	@media (min-width: 650px) {
 | 
			
		||||
    align-self: end;
 | 
			
		||||
    margin-left: 15px;
 | 
			
		||||
    margin-top: 0px;
 | 
			
		||||
    margin-bottom: -3px;
 | 
			
		||||
	}
 | 
			
		||||
	@media (min-width: 768px) {
 | 
			
		||||
    margin-bottom: 4px;
 | 
			
		||||
    top: 1px;
 | 
			
		||||
    position: relative;
 | 
			
		||||
	}
 | 
			
		||||
	@media (max-width: 768px) {
 | 
			
		||||
	  order: 2;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.badges {
 | 
			
		||||
  font-size: 20px;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
app-fiat {
 | 
			
		||||
  display: block;
 | 
			
		||||
  font-size: 13px;
 | 
			
		||||
  @media (min-width: 768px) {
 | 
			
		||||
    font-size: 14px;
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    margin-left: 10px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
 | 
			
		||||
import { ActivatedRoute, ParamMap } from '@angular/router';
 | 
			
		||||
import { Observable } from 'rxjs';
 | 
			
		||||
import { switchMap } from 'rxjs/operators';
 | 
			
		||||
import { SeoService } from 'src/app/services/seo.service';
 | 
			
		||||
import { LightningApiService } from '../lightning-api.service';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
@ -16,12 +17,14 @@ export class ChannelComponent implements OnInit {
 | 
			
		||||
  constructor(
 | 
			
		||||
    private lightningApiService: LightningApiService,
 | 
			
		||||
    private activatedRoute: ActivatedRoute,
 | 
			
		||||
    private seoService: SeoService,
 | 
			
		||||
  ) { }
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    this.channel$ = this.activatedRoute.paramMap
 | 
			
		||||
      .pipe(
 | 
			
		||||
        switchMap((params: ParamMap) => {
 | 
			
		||||
          this.seoService.setTitle(`Channel: ${params.get('short_id')}`);
 | 
			
		||||
          return this.lightningApiService.getChannel$(params.get('short_id'));
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
@ -1,60 +1,16 @@
 | 
			
		||||
<div>
 | 
			
		||||
 | 
			
		||||
  <table class="table table-borderless">
 | 
			
		||||
    <thead>
 | 
			
		||||
      <th class="alias text-left" i18n="nodes.alias">Node Alias</th>
 | 
			
		||||
      <th class="alias text-left d-none d-md-table-cell" i18n="channels.transaction">Node ID</th>
 | 
			
		||||
      <th class="alias text-left d-none d-md-table-cell" i18n="nodes.alias">Status</th>
 | 
			
		||||
      <th class="channels text-right d-none d-md-table-cell" i18n="channels.rate">Fee Rate</th>
 | 
			
		||||
      <th class="channels text-left d-none d-md-table-cell" i18n="channels.rate">Fee Rate</th>
 | 
			
		||||
      <th class="capacity text-right d-none d-md-table-cell" i18n="nodes.capacity">Capacity</th>
 | 
			
		||||
      <th class="capacity text-right" i18n="channels.id">Channel ID</th>
 | 
			
		||||
      <th class="capacity text-left" i18n="channels.id">Channel ID</th>
 | 
			
		||||
    </thead>
 | 
			
		||||
    <tbody *ngIf="channels$ | async as channels; else skeleton">
 | 
			
		||||
      <tr *ngFor="let channel of channels; let i = index;">
 | 
			
		||||
        <ng-template [ngIf]="channel.node2_public_key === publicKey" [ngIfElse]="right">
 | 
			
		||||
          <td class="alias text-left">
 | 
			
		||||
            {{ channel.alias_left || '?' }}
 | 
			
		||||
          </td>
 | 
			
		||||
          <td class="text-left d-none d-md-table-cell">
 | 
			
		||||
            <a [routerLink]="['/lightning/node' | relativeUrl, channel.node1_public_key]">
 | 
			
		||||
              <span>{{ channel.node1_public_key | shortenString : 10 }}</span>
 | 
			
		||||
            </a>
 | 
			
		||||
            <app-clipboard [text]="channel.node1_public_key"></app-clipboard>
 | 
			
		||||
          </td>
 | 
			
		||||
          <td class="d-none d-md-table-cell">
 | 
			
		||||
            <span class="badge rounded-pill badge-secondary" *ngIf="channel.status === 0">Inactive</span>
 | 
			
		||||
            <span class="badge rounded-pill badge-success" *ngIf="channel.status === 1">Active</span>
 | 
			
		||||
            <span class="badge rounded-pill badge-danger" *ngIf="channel.status === 2">Closed</span>
 | 
			
		||||
          </td>
 | 
			
		||||
          <td class="capacity text-right d-none d-md-table-cell">
 | 
			
		||||
           {{ channel.node1_fee_rate / 10000 | number }}%
 | 
			
		||||
          </td>
 | 
			
		||||
        </ng-template>
 | 
			
		||||
        <ng-template #right>
 | 
			
		||||
          <td class="alias text-left">
 | 
			
		||||
            {{ channel.alias_right || '?' }}
 | 
			
		||||
          </td>
 | 
			
		||||
          <td class="text-left d-none d-md-table-cell">
 | 
			
		||||
            <a [routerLink]="['/lightning/node' | relativeUrl, channel.node2_public_key]">
 | 
			
		||||
              <span>{{ channel.node2_public_key | shortenString : 10 }}</span>
 | 
			
		||||
            </a>
 | 
			
		||||
            <app-clipboard [text]="channel.node2_public_key"></app-clipboard>
 | 
			
		||||
          </td>
 | 
			
		||||
          <td class="d-none d-md-table-cell">
 | 
			
		||||
            <span class="badge rounded-pill badge-secondary" *ngIf="channel.status === 0">Inactive</span>
 | 
			
		||||
            <span class="badge rounded-pill badge-success" *ngIf="channel.status === 1">Active</span>
 | 
			
		||||
            <span class="badge rounded-pill badge-danger" *ngIf="channel.status === 2">Closed</span>
 | 
			
		||||
          </td>
 | 
			
		||||
          <td class="capacity text-right d-none d-md-table-cell">
 | 
			
		||||
            {{ channel.node2_fee_rate / 10000 | number }}%
 | 
			
		||||
           </td>
 | 
			
		||||
        </ng-template>
 | 
			
		||||
        <td class="capacity text-right d-none d-md-table-cell">
 | 
			
		||||
          <app-amount [satoshis]="channel.capacity" digitsInfo="1.2-2"></app-amount>
 | 
			
		||||
        </td>
 | 
			
		||||
        <td class="capacity text-right">
 | 
			
		||||
          <a [routerLink]="['/lightning/channel' | relativeUrl, channel.id]">{{ channel.short_id }}</a>
 | 
			
		||||
         </td>
 | 
			
		||||
        <ng-container *ngTemplateOutlet="tableTemplate; context: { $implicit: channel, node: channel.node_left.public_key === publicKey ? channel.node_right : channel.node_left }"></ng-container>
 | 
			
		||||
      </tr>
 | 
			
		||||
    </tbody>
 | 
			
		||||
    <ng-template #skeleton>
 | 
			
		||||
@ -75,12 +31,37 @@
 | 
			
		||||
          <td class="channels text-right d-none d-md-table-cell">
 | 
			
		||||
            <span class="skeleton-loader"></span>
 | 
			
		||||
          </td>
 | 
			
		||||
          <td class="channels text-right">
 | 
			
		||||
          <td class="channels text-left">
 | 
			
		||||
            <span class="skeleton-loader"></span>
 | 
			
		||||
          </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
      </tbody>
 | 
			
		||||
    </ng-template>
 | 
			
		||||
  </table>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
<ng-template #tableTemplate let-channel let-node="node">
 | 
			
		||||
  <td class="alias text-left">
 | 
			
		||||
    {{ node.alias || '?' }}
 | 
			
		||||
  </td>
 | 
			
		||||
  <td class="text-left d-none d-md-table-cell">
 | 
			
		||||
    <a [routerLink]="['/lightning/node' | relativeUrl, node.public_key]">
 | 
			
		||||
      <span>{{ node.public_key | shortenString : 10 }}</span>
 | 
			
		||||
    </a>
 | 
			
		||||
    <app-clipboard [text]="node.public_key"></app-clipboard>
 | 
			
		||||
  </td>
 | 
			
		||||
  <td class="d-none d-md-table-cell">
 | 
			
		||||
    <span class="badge rounded-pill badge-secondary" *ngIf="channel.status === 0">Inactive</span>
 | 
			
		||||
    <span class="badge rounded-pill badge-success" *ngIf="channel.status === 1">Active</span>
 | 
			
		||||
    <span class="badge rounded-pill badge-danger" *ngIf="channel.status === 2">Closed</span>
 | 
			
		||||
  </td>
 | 
			
		||||
  <td class="capacity text-left d-none d-md-table-cell">
 | 
			
		||||
    {{ node.fee_rate }} <span class="symbol">ppm ({{ node.fee_rate / 10000 | number }}%)</span>
 | 
			
		||||
  </td>
 | 
			
		||||
  <td class="capacity text-right d-none d-md-table-cell">
 | 
			
		||||
    <app-amount [satoshis]="channel.capacity" digitsInfo="1.2-2"></app-amount>
 | 
			
		||||
  </td>
 | 
			
		||||
  <td class="capacity text-left">
 | 
			
		||||
    <a [routerLink]="['/lightning/channel' | relativeUrl, channel.id]">{{ channel.short_id }}</a>
 | 
			
		||||
   </td>
 | 
			
		||||
</ng-template>
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@
 | 
			
		||||
 | 
			
		||||
    <div class="col">
 | 
			
		||||
      <div class="main-title">
 | 
			
		||||
        <span i18n="lightning.statistics-title">Nodes Statistics</span> 
 | 
			
		||||
        <span i18n="lightning.statistics-title">Network Statistics</span> 
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="card-wrapper">
 | 
			
		||||
        <div class="card" style="height: 123px">
 | 
			
		||||
 | 
			
		||||
@ -11,6 +11,7 @@ import { LightningRoutingModule } from './lightning.routing.module';
 | 
			
		||||
import { ChannelsListComponent } from './channels-list/channels-list.component';
 | 
			
		||||
import { ChannelComponent } from './channel/channel.component';
 | 
			
		||||
import { LightningWrapperComponent } from './lightning-wrapper/lightning-wrapper.component';
 | 
			
		||||
import { ChannelBoxComponent } from './channel/channel-box/channel-box.component';
 | 
			
		||||
@NgModule({
 | 
			
		||||
  declarations: [
 | 
			
		||||
    LightningDashboardComponent,
 | 
			
		||||
@ -20,6 +21,7 @@ import { LightningWrapperComponent } from './lightning-wrapper/lightning-wrapper
 | 
			
		||||
    ChannelsListComponent,
 | 
			
		||||
    ChannelComponent,
 | 
			
		||||
    LightningWrapperComponent,
 | 
			
		||||
    ChannelBoxComponent,
 | 
			
		||||
  ],
 | 
			
		||||
  imports: [
 | 
			
		||||
    CommonModule,
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,8 @@
 | 
			
		||||
<div class="container-xl" *ngIf="(node$ | async) as node">
 | 
			
		||||
  <div class="title-container mb-2">
 | 
			
		||||
    <h1 i18n="shared.address" class="mb-0">{{ node.alias }}</h1>
 | 
			
		||||
    <h1 class="mb-0">{{ node.alias }}</h1>
 | 
			
		||||
    <span class="tx-link">
 | 
			
		||||
      <a [routerLink]="['/lightning/node' | relativeUrl, node.public_key]" >
 | 
			
		||||
        <span class="d-inline">{{ node.public_key | shortenString : 18 }}</span>
 | 
			
		||||
      </a>
 | 
			
		||||
      <a [routerLink]="['/lightning/node' | relativeUrl, node.public_key]">{{ node.public_key | shortenString : 12 }}</a>
 | 
			
		||||
      <app-clipboard [text]="node.public_key"></app-clipboard>
 | 
			
		||||
    </span>
 | 
			
		||||
  </div>
 | 
			
		||||
@ -20,7 +18,7 @@
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td i18n="address.total-received">Total capacity</td>
 | 
			
		||||
                <td>
 | 
			
		||||
                  <app-sats [satoshis]="node.capacity"></app-sats>  <app-fiat [value]="node.capacity" digitsInfo="1.0-0"></app-fiat>
 | 
			
		||||
                  <app-sats [satoshis]="node.capacity"></app-sats><app-fiat [value]="node.capacity" digitsInfo="1.0-0"></app-fiat>
 | 
			
		||||
                </td>
 | 
			
		||||
              </tr>
 | 
			
		||||
              <tr>
 | 
			
		||||
@ -32,12 +30,13 @@
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td i18n="address.total-received">Average channel size</td>
 | 
			
		||||
                <td>
 | 
			
		||||
                  <app-sats [satoshis]="node.channels_capacity_avg"></app-sats>  <app-fiat [value]="node.channels_capacity_avg" digitsInfo="1.0-0"></app-fiat>
 | 
			
		||||
                  <app-sats [satoshis]="node.channels_capacity_avg"></app-sats><app-fiat [value]="node.channels_capacity_avg" digitsInfo="1.0-0"></app-fiat>
 | 
			
		||||
                </td>
 | 
			
		||||
              </tr>
 | 
			
		||||
            </tbody>
 | 
			
		||||
          </table>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="w-100 d-block d-md-none"></div>
 | 
			
		||||
        <div class="col-md">
 | 
			
		||||
          <table class="table table-borderless table-striped">
 | 
			
		||||
            <tbody>
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,31 @@
 | 
			
		||||
.title-container {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: row;
 | 
			
		||||
 | 
			
		||||
  @media (max-width: 768px) {
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tx-link {
 | 
			
		||||
  display: flex;
 | 
			
		||||
	flex-grow: 1;
 | 
			
		||||
	@media (min-width: 650px) {
 | 
			
		||||
    align-self: end;
 | 
			
		||||
    margin-left: 15px;
 | 
			
		||||
    margin-top: 0px;
 | 
			
		||||
    margin-bottom: -3px;
 | 
			
		||||
	}
 | 
			
		||||
	@media (min-width: 768px) {
 | 
			
		||||
    margin-bottom: 4px;
 | 
			
		||||
    top: 1px;
 | 
			
		||||
    position: relative;
 | 
			
		||||
	}
 | 
			
		||||
	@media (max-width: 768px) {
 | 
			
		||||
	  order: 2;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.qr-wrapper {
 | 
			
		||||
  background-color: #FFF;
 | 
			
		||||
  padding: 10px;
 | 
			
		||||
@ -20,34 +48,13 @@
 | 
			
		||||
  position: relative;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.qrcode-col {
 | 
			
		||||
  margin: 20px auto 10px;
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  @media (min-width: 992px){
 | 
			
		||||
    margin: 0px auto 0px;
 | 
			
		||||
app-fiat {
 | 
			
		||||
  display: block;
 | 
			
		||||
  font-size: 13px;
 | 
			
		||||
  @media (min-width: 768px) {
 | 
			
		||||
    font-size: 14px;
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    margin-left: 10px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tx-link {
 | 
			
		||||
  display: flex;
 | 
			
		||||
	flex-grow: 1;
 | 
			
		||||
	@media (min-width: 650px) {
 | 
			
		||||
    align-self: end;
 | 
			
		||||
    margin-left: 15px;
 | 
			
		||||
    margin-top: 0px;
 | 
			
		||||
    margin-bottom: -3px;
 | 
			
		||||
	}
 | 
			
		||||
	@media (min-width: 768px) {
 | 
			
		||||
    margin-bottom: 4px;
 | 
			
		||||
    top: 1px;
 | 
			
		||||
    position: relative;
 | 
			
		||||
	}
 | 
			
		||||
	@media (max-width: 768px) {
 | 
			
		||||
	  order: 3;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.title-container {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: row;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
 | 
			
		||||
import { ActivatedRoute, ParamMap } from '@angular/router';
 | 
			
		||||
import { Observable } from 'rxjs';
 | 
			
		||||
import { map, switchMap } from 'rxjs/operators';
 | 
			
		||||
import { SeoService } from 'src/app/services/seo.service';
 | 
			
		||||
import { LightningApiService } from '../lightning-api.service';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
@ -20,6 +21,7 @@ export class NodeComponent implements OnInit {
 | 
			
		||||
  constructor(
 | 
			
		||||
    private lightningApiService: LightningApiService,
 | 
			
		||||
    private activatedRoute: ActivatedRoute,
 | 
			
		||||
    private seoService: SeoService,
 | 
			
		||||
  ) { }
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
@ -29,6 +31,8 @@ export class NodeComponent implements OnInit {
 | 
			
		||||
          return this.lightningApiService.getNode$(params.get('public_key'));
 | 
			
		||||
        }),
 | 
			
		||||
        map((node) => {
 | 
			
		||||
          this.seoService.setTitle(`Node: ${node.alias}`);
 | 
			
		||||
 | 
			
		||||
          const socketsObject = [];
 | 
			
		||||
          for (const socket of node.sockets.split(',')) {
 | 
			
		||||
            if (socket === '') {
 | 
			
		||||
 | 
			
		||||
@ -51,7 +51,9 @@ class ChannelsApi {
 | 
			
		||||
    try {
 | 
			
		||||
      const query = `SELECT n1.alias AS alias_left, n2.alias AS alias_right, channels.* FROM channels LEFT JOIN nodes AS n1 ON n1.public_key = channels.node1_public_key LEFT JOIN nodes AS n2 ON n2.public_key = channels.node2_public_key WHERE channels.id = ?`;
 | 
			
		||||
      const [rows]: any = await DB.query(query, [shortId]);
 | 
			
		||||
      return rows[0];
 | 
			
		||||
      if (rows[0]) {
 | 
			
		||||
        return this.convertChannel(rows[0]);
 | 
			
		||||
      }
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      logger.err('$getChannel error: ' + (e instanceof Error ? e.message : e));
 | 
			
		||||
      throw e;
 | 
			
		||||
@ -63,7 +65,8 @@ class ChannelsApi {
 | 
			
		||||
      transactionIds = transactionIds.map((id) => '\'' + id + '\'');
 | 
			
		||||
      const query = `SELECT n1.alias AS alias_left, n2.alias AS alias_right, channels.* FROM channels LEFT JOIN nodes AS n1 ON n1.public_key = channels.node1_public_key LEFT JOIN nodes AS n2 ON n2.public_key = channels.node2_public_key WHERE channels.transaction_id IN (${transactionIds.join(', ')})`;
 | 
			
		||||
      const [rows]: any = await DB.query(query);
 | 
			
		||||
      return rows;
 | 
			
		||||
      const channels = rows.map((row) => this.convertChannel(row));
 | 
			
		||||
      return channels;
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      logger.err('$getChannelByTransactionId error: ' + (e instanceof Error ? e.message : e));
 | 
			
		||||
      throw e;
 | 
			
		||||
@ -72,14 +75,50 @@ class ChannelsApi {
 | 
			
		||||
 | 
			
		||||
  public async $getChannelsForNode(public_key: string): Promise<any> {
 | 
			
		||||
    try {
 | 
			
		||||
      const query = `SELECT n1.alias AS alias_left, n2.alias AS alias_right, channels.* FROM channels LEFT JOIN nodes AS n1 ON n1.public_key = channels.node1_public_key LEFT JOIN nodes AS n2 ON n2.public_key = channels.node2_public_key WHERE node1_public_key = ? OR node2_public_key = ?`;
 | 
			
		||||
      const query = `SELECT n1.alias AS alias_left, n2.alias AS alias_right, channels.* FROM channels LEFT JOIN nodes AS n1 ON n1.public_key = channels.node1_public_key LEFT JOIN nodes AS n2 ON n2.public_key = channels.node2_public_key WHERE node1_public_key = ? OR node2_public_key = ? ORDER BY channels.capacity DESC`;
 | 
			
		||||
      const [rows]: any = await DB.query(query, [public_key, public_key]);
 | 
			
		||||
      return rows;
 | 
			
		||||
      const channels = rows.map((row) => this.convertChannel(row));
 | 
			
		||||
      return channels;
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      logger.err('$getChannelsForNode error: ' + (e instanceof Error ? e.message : e));
 | 
			
		||||
      throw e;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private convertChannel(channel: any): any {
 | 
			
		||||
    return {
 | 
			
		||||
      'id': channel.id,
 | 
			
		||||
      'short_id': channel.short_id,
 | 
			
		||||
      'capacity': channel.capacity,
 | 
			
		||||
      'transaction_id': channel.transaction_id,
 | 
			
		||||
      'transaction_vout': channel.void,
 | 
			
		||||
      'updated_at': channel.updated_at,
 | 
			
		||||
      'created': channel.created,
 | 
			
		||||
      'status': channel.status,
 | 
			
		||||
      'node_left': {
 | 
			
		||||
        'alias': channel.alias_left,
 | 
			
		||||
        'public_key': channel.node1_public_key,
 | 
			
		||||
        'base_fee_mtokens': channel.node1_base_fee_mtokens,
 | 
			
		||||
        'cltv_delta': channel.node1_cltv_delta,
 | 
			
		||||
        'fee_rate': channel.node1_fee_rate,
 | 
			
		||||
        'is_disabled': channel.node1_is_disabled,
 | 
			
		||||
        'max_htlc_mtokens': channel.node1_max_htlc_mtokens,
 | 
			
		||||
        'min_htlc_mtokens': channel.node1_min_htlc_mtokens,
 | 
			
		||||
        'updated_at': channel.node1_updated_at,
 | 
			
		||||
      },
 | 
			
		||||
      'node_right': {
 | 
			
		||||
        'alias': channel.alias_right,
 | 
			
		||||
        'public_key': channel.node2_public_key,
 | 
			
		||||
        'base_fee_mtokens': channel.node2_base_fee_mtokens,
 | 
			
		||||
        'cltv_delta': channel.node2_cltv_delta,
 | 
			
		||||
        'fee_rate': channel.node2_fee_rate,
 | 
			
		||||
        'is_disabled': channel.node2_is_disabled,
 | 
			
		||||
        'max_htlc_mtokens': channel.node2_max_htlc_mtokens,
 | 
			
		||||
        'min_htlc_mtokens': channel.node2_min_htlc_mtokens,
 | 
			
		||||
        'updated_at': channel.node2_updated_at,
 | 
			
		||||
      },
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default new ChannelsApi();
 | 
			
		||||
 | 
			
		||||
@ -63,7 +63,7 @@ class NodesApi {
 | 
			
		||||
  public async $searchNodeByPublicKeyOrAlias(search: string) {
 | 
			
		||||
    try {
 | 
			
		||||
      const searchStripped = search.replace('%', '') + '%';
 | 
			
		||||
      const query = `SELECT public_key, alias, color FROM nodes WHERE public_key LIKE ? OR alias LIKE ? LIMIT 10`;
 | 
			
		||||
      const query = `SELECT nodes.public_key, nodes.alias, node_stats.capacity FROM nodes LEFT JOIN node_stats ON node_stats.public_key = nodes.public_key WHERE nodes.public_key LIKE ? OR nodes.alias LIKE ? GROUP BY nodes.public_key ORDER BY node_stats.capacity DESC LIMIT 10`;
 | 
			
		||||
      const [rows]: any = await DB.query(query, [searchStripped, searchStripped]);
 | 
			
		||||
      return rows;
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user