Channel component
This commit is contained in:
144
frontend/src/app/lightning/channel/channel.component.html
Normal file
144
frontend/src/app/lightning/channel/channel.component.html
Normal file
@@ -0,0 +1,144 @@
|
||||
<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.id }}</a> <app-clipboard [text]="channel.id"></app-clipboard></h1>
|
||||
<div class="badges">
|
||||
<span class="badge rounded-pill badge-success">Open</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="clearfix"></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">Last update</td>
|
||||
<td>{{ channel.updated_at | date:'yyyy-MM-dd HH:mm' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n="address.total-sent">Transaction ID</td>
|
||||
<td>
|
||||
<a [routerLink]="['/tx' | relativeUrl, channel.transaction_id + ':' + channel.transaction_vout]" >
|
||||
<span>{{ channel.transaction_id | shortenString : 10 }}</span>
|
||||
</a>
|
||||
<app-clipboard [text]="channel.transaction_id"></app-clipboard>
|
||||
</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>
|
||||
<tr>
|
||||
<td i18n="address.total-received">Capacity</td>
|
||||
<td><app-sats [satoshis]="channel.capacity"></app-sats></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<br>
|
||||
<h2>Peers</h2>
|
||||
|
||||
<div class="box">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td i18n="address.total-sent">Node</td>
|
||||
<td>
|
||||
{{ channel.alias_left }}
|
||||
<br>
|
||||
<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>
|
||||
</td>
|
||||
</tr>
|
||||
<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 class="w-100 d-block d-md-none"></div>
|
||||
<div class="col-md">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td i18n="address.total-sent">Node</td>
|
||||
<td>
|
||||
{{ channel.alias_right }}
|
||||
<br>
|
||||
<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>
|
||||
</td>
|
||||
</tr>
|
||||
<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>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<br>
|
||||
@@ -0,0 +1,3 @@
|
||||
.badges {
|
||||
font-size: 18px;
|
||||
}
|
||||
30
frontend/src/app/lightning/channel/channel.component.ts
Normal file
30
frontend/src/app/lightning/channel/channel.component.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, ParamMap } from '@angular/router';
|
||||
import { Observable } from 'rxjs';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
import { LightningApiService } from '../lightning-api.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-channel',
|
||||
templateUrl: './channel.component.html',
|
||||
styleUrls: ['./channel.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ChannelComponent implements OnInit {
|
||||
channel$: Observable<any>;
|
||||
|
||||
constructor(
|
||||
private lightningApiService: LightningApiService,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.channel$ = this.activatedRoute.paramMap
|
||||
.pipe(
|
||||
switchMap((params: ParamMap) => {
|
||||
return this.lightningApiService.getChannel$(params.get('short_id'));
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<div>
|
||||
|
||||
<table class="table table-borderless">
|
||||
<thead>
|
||||
<th class="alias text-left" i18n="nodes.alias">Node Alias</th>
|
||||
<th class="channels text-right" i18n="channels.rate">Fee Rate</th>
|
||||
<th class="capacity text-right" i18n="channels.id">Channel ID</th>
|
||||
<th class="capacity text-right" i18n="nodes.capacity">Capacity</th>
|
||||
<th class="alias text-right" i18n="channels.transaction">Transaction 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">
|
||||
<a [routerLink]="['/lightning/node' | relativeUrl, channel.node1_public_key]">{{ channel.alias_left }}</a>
|
||||
</td>
|
||||
<td class="capacity text-right">
|
||||
{{ channel.node1_fee_rate / 10000 | number }}%
|
||||
</td>
|
||||
</ng-template>
|
||||
<ng-template #right>
|
||||
<td class="alias text-left" *ngIf="channel.node1_public_key === publicKey">
|
||||
<a [routerLink]="['/lightning/node' | relativeUrl, channel.node2_public_key]">{{ channel.alias_right }}</a>
|
||||
</td>
|
||||
<td class="capacity text-right">
|
||||
{{ channel.node2_fee_rate / 10000 | number }}%
|
||||
</td>
|
||||
</ng-template>
|
||||
<td class="capacity text-right">
|
||||
<a [routerLink]="['/lightning/channel' | relativeUrl, channel.id]">{{ channel.id }}</a>
|
||||
</td>
|
||||
<td class="capacity text-right">
|
||||
<app-amount [satoshis]="channel.capacity" digitsInfo="1.2-2"></app-amount>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<a [routerLink]="['/tx' | relativeUrl, channel.transaction_id + ':' + channel.transaction_vout]" >
|
||||
<span>{{ channel.transaction_id | shortenString : 10 }}</span>
|
||||
</a>
|
||||
<app-clipboard [text]="channel.transaction_id"></app-clipboard>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<ng-template #skeleton>
|
||||
<tbody>
|
||||
<tr *ngFor="let item of [1,2,3,4,5,6,7,8,9,10]">
|
||||
<td class="alias text-left">
|
||||
<span class="skeleton-loader"></span>
|
||||
</td>
|
||||
<td class="capacity text-left">
|
||||
<span class="skeleton-loader"></span>
|
||||
</td>
|
||||
<td class="channels text-left">
|
||||
<span class="skeleton-loader"></span>
|
||||
</td>
|
||||
<td class="channels text-right">
|
||||
<span class="skeleton-loader"></span>
|
||||
</td>
|
||||
<td class="channels text-right">
|
||||
<span class="skeleton-loader"></span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</ng-template>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnChanges, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { LightningApiService } from '../lightning-api.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-channels-list',
|
||||
templateUrl: './channels-list.component.html',
|
||||
styleUrls: ['./channels-list.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ChannelsListComponent implements OnChanges {
|
||||
@Input() publicKey: string;
|
||||
channels$: Observable<any[]>;
|
||||
|
||||
constructor(
|
||||
private lightningApiService: LightningApiService,
|
||||
) { }
|
||||
|
||||
ngOnChanges(): void {
|
||||
this.channels$ = this.lightningApiService.getChannelsByNodeId$(this.publicKey);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
const API_BASE_URL = '/lightning/api/v1';
|
||||
@@ -16,8 +16,16 @@ export class LightningApiService {
|
||||
return this.httpClient.get<any>(API_BASE_URL + '/nodes/' + publicKey);
|
||||
}
|
||||
|
||||
getChannel$(shortId: string): Observable<any> {
|
||||
return this.httpClient.get<any>(API_BASE_URL + '/channels/' + shortId);
|
||||
}
|
||||
|
||||
getChannelsByNodeId$(publicKey: string): Observable<any> {
|
||||
return this.httpClient.get<any>(API_BASE_URL + '/channels/' + publicKey);
|
||||
let params = new HttpParams()
|
||||
.set('public_key', publicKey)
|
||||
;
|
||||
|
||||
return this.httpClient.get<any>(API_BASE_URL + '/channels', { params });
|
||||
}
|
||||
|
||||
getLatestStatistics$(): Observable<any> {
|
||||
|
||||
@@ -8,12 +8,16 @@ import { RouterModule } from '@angular/router';
|
||||
import { NodeStatisticsComponent } from './node-statistics/node-statistics.component';
|
||||
import { NodeComponent } from './node/node.component';
|
||||
import { LightningRoutingModule } from './lightning.routing.module';
|
||||
import { ChannelsListComponent } from './channels-list/channels-list.component';
|
||||
import { ChannelComponent } from './channel/channel.component';
|
||||
@NgModule({
|
||||
declarations: [
|
||||
LightningDashboardComponent,
|
||||
NodesListComponent,
|
||||
NodeStatisticsComponent,
|
||||
NodeComponent,
|
||||
ChannelsListComponent,
|
||||
ChannelComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
||||
@@ -2,6 +2,7 @@ import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { LightningDashboardComponent } from './lightning-dashboard/lightning-dashboard.component';
|
||||
import { NodeComponent } from './node/node.component';
|
||||
import { ChannelComponent } from './channel/channel.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@@ -12,6 +13,10 @@ const routes: Routes = [
|
||||
path: 'node/:public_key',
|
||||
component: NodeComponent,
|
||||
},
|
||||
{
|
||||
path: 'channel/:short_id',
|
||||
component: ChannelComponent,
|
||||
},
|
||||
{
|
||||
path: '**',
|
||||
redirectTo: ''
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<h5 class="card-title" i18n="mining.average-fee">Capacity</h5>
|
||||
<div class="card-text" i18n-ngbTooltip="mining.average-fee"
|
||||
ngbTooltip="Fee paid on average for each transaction in the past 144 blocks" placement="bottom">
|
||||
<app-amount [satoshis]="statistics.latest.total_capacity" digitsInfo="1.2-3"></app-amount>
|
||||
<app-amount [satoshis]="statistics.latest.total_capacity" digitsInfo="1.2-2"></app-amount>
|
||||
<span class="fiat">
|
||||
<app-change [current]="statistics.latest.total_capacity" [previous]="statistics.previous.total_capacity"></app-change>
|
||||
</span>
|
||||
|
||||
@@ -1 +1,66 @@
|
||||
<p>node works!</p>
|
||||
<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>
|
||||
<span class="tx-link">
|
||||
<a [routerLink]="['/lightning/node' | relativeUrl, node.public_key]" >
|
||||
<span class="d-inline">{{ node.public_key | shortenString : 18 }}</span>
|
||||
</a>
|
||||
<app-clipboard [text]="node.public_key"></app-clipboard>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<div class="box">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td i18n="address.total-received">First Seen</td>
|
||||
<td>{{ node.first_seen | date:'yyyy-MM-dd HH:mm' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n="address.total-sent">Updated At</td>
|
||||
<td>{{ node.updated_at | date:'yyyy-MM-dd HH:mm' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n="address.balance">Color</td>
|
||||
<td><div [ngStyle]="{'color': node.color}">{{ node.color }}</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="w-100 d-block d-md-none"></div>
|
||||
<div class="col-md qrcode-col">
|
||||
<div class="qr-wrapper">
|
||||
<app-qrcode [data]="node.public_key"></app-qrcode>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<br>
|
||||
<h2>Channels</h2>
|
||||
|
||||
<app-channels-list [publicKey]="node.public_key"></app-channels-list>
|
||||
|
||||
<!--
|
||||
|
||||
<br>
|
||||
<div class="title-tx">
|
||||
<h2 class="text-left">
|
||||
<ng-template [ngIf]="!transactions?.length"> </ng-template>
|
||||
<ng-template i18n="X of X Address Transaction" [ngIf]="transactions?.length === 1">{{ (transactions?.length | number) || '?' }} of {{ txCount | number }} transaction</ng-template>
|
||||
<ng-template i18n="X of X Address Transactions (Plural)" [ngIf]="transactions?.length > 1">{{ (transactions?.length | number) || '?' }} of {{ txCount | number }} transactions</ng-template>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<app-transactions-list [transactions]="transactions" [showConfirmations]="true" [address]="address.address" (loadMore)="loadMore()"></app-transactions-list>
|
||||
-->
|
||||
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
.qr-wrapper {
|
||||
background-color: #FFF;
|
||||
padding: 10px;
|
||||
padding-bottom: 5px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.qrcode-col {
|
||||
margin: 20px auto 10px;
|
||||
text-align: center;
|
||||
@media (min-width: 992px){
|
||||
margin: 0px auto 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, ParamMap } from '@angular/router';
|
||||
import { Observable } from 'rxjs';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
@@ -7,10 +7,12 @@ import { LightningApiService } from '../lightning-api.service';
|
||||
@Component({
|
||||
selector: 'app-node',
|
||||
templateUrl: './node.component.html',
|
||||
styleUrls: ['./node.component.scss']
|
||||
styleUrls: ['./node.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class NodeComponent implements OnInit {
|
||||
node$: Observable<any>;
|
||||
publicKey$: Observable<string>;
|
||||
|
||||
constructor(
|
||||
private lightningApiService: LightningApiService,
|
||||
@@ -21,7 +23,7 @@ export class NodeComponent implements OnInit {
|
||||
this.node$ = this.activatedRoute.paramMap
|
||||
.pipe(
|
||||
switchMap((params: ParamMap) => {
|
||||
return this.lightningApiService.getNode$(params.get('id'));
|
||||
return this.lightningApiService.getNode$(params.get('public_key'));
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
‎{{ addPlus && satoshis >= 0 ? '+' : '' }}{{ satoshis | number }}
|
||||
<span class="symbol"><ng-template [ngIf]="network === 'liquid'">L-</ng-template>
|
||||
<ng-template [ngIf]="network === 'liquidtestnet'">tL-</ng-template>
|
||||
<ng-template [ngIf]="network === 'testnet'">t-</ng-template>
|
||||
<ng-template [ngIf]="network === 'signet'">s-</ng-template>sats</span>
|
||||
32
frontend/src/app/shared/components/sats/sats.component.ts
Normal file
32
frontend/src/app/shared/components/sats/sats.component.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { StateService } from '../../../services/state.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-sats',
|
||||
templateUrl: './sats.component.html',
|
||||
styleUrls: ['./sats.component.scss']
|
||||
})
|
||||
export class SatsComponent implements OnInit {
|
||||
@Input() satoshis: number;
|
||||
@Input() digitsInfo = 0;
|
||||
@Input() addPlus = false;
|
||||
|
||||
network = '';
|
||||
stateSubscription: Subscription;
|
||||
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.stateSubscription = this.stateService.networkChanged$.subscribe((network) => this.network = network);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.stateSubscription) {
|
||||
this.stateSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -74,6 +74,7 @@ import { LoadingIndicatorComponent } from '../components/loading-indicator/loadi
|
||||
import { IndexingProgressComponent } from '../components/indexing-progress/indexing-progress.component';
|
||||
import { SvgImagesComponent } from '../components/svg-images/svg-images.component';
|
||||
import { ChangeComponent } from '../components/change/change.component';
|
||||
import { SatsComponent } from './components/sats/sats.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -142,6 +143,7 @@ import { ChangeComponent } from '../components/change/change.component';
|
||||
IndexingProgressComponent,
|
||||
SvgImagesComponent,
|
||||
ChangeComponent,
|
||||
SatsComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
@@ -238,6 +240,7 @@ import { ChangeComponent } from '../components/change/change.component';
|
||||
IndexingProgressComponent,
|
||||
SvgImagesComponent,
|
||||
ChangeComponent,
|
||||
SatsComponent
|
||||
]
|
||||
})
|
||||
export class SharedModule {
|
||||
|
||||
Reference in New Issue
Block a user