Add lightning channel link previews
This commit is contained in:
parent
18d18fa234
commit
9216936a71
@ -0,0 +1,72 @@
|
|||||||
|
<div class="box preview-box" *ngIf="(channel$ | async) as channel">
|
||||||
|
<div class="row d-flex justify-content-between full-width-row">
|
||||||
|
<h1 class="title">
|
||||||
|
<span i18n="lightning.channel">Channel</span>
|
||||||
|
<a [routerLink]="['/lightning/channel' | relativeUrl, channel.id]"> {{ channel.short_id }}</a>
|
||||||
|
</h1>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<app-closing-type [type]="channel.closing_reason" *ngIf="channel.status === 2"></app-closing-type>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row d-flex justify-content-between full-width-row nodes">
|
||||||
|
<span class="node left">
|
||||||
|
{{ channel.node_left.alias || '?' }}
|
||||||
|
</span>
|
||||||
|
<fa-icon class="between-arrow" [icon]="['fas', 'arrow-right-arrow-left']" [fixedWidth]="true" title="channel between"></fa-icon>
|
||||||
|
<span class="node right">
|
||||||
|
{{ channel.node_right.alias || '?' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md">
|
||||||
|
<table class="table table-borderless table-striped">
|
||||||
|
<tbody>
|
||||||
|
<tr></tr>
|
||||||
|
<tr>
|
||||||
|
<td i18n="channel.created">Created</td>
|
||||||
|
<td>{{ channel.created | date:'yyyy-MM-dd HH:mm' }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td i18n="channel.capacity">Capacity</td>
|
||||||
|
<td><app-amount [satoshis]="channel.capacity" [noFiat]="true"></app-amount></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td i18n="channel.fee-rate">Fee rate</td>
|
||||||
|
<td>
|
||||||
|
<div class="dual-cell">
|
||||||
|
<span>{{ channel.node_left.fee_rate }} <span class="symbol">ppm</span></span>
|
||||||
|
<fa-icon class="between-arrow" [icon]="['fas', 'arrow-right-arrow-left']" [fixedWidth]="true"></fa-icon>
|
||||||
|
<span>{{ channel.node_right.fee_rate }} <span class="symbol">ppm</span></span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td i18n="channel.base-fee">Base fee</td>
|
||||||
|
<td>
|
||||||
|
<div class="dual-cell">
|
||||||
|
<app-sats [satoshis]="channel.node_left.base_fee_mtokens / 1000" digitsInfo="1.0-2"></app-sats>
|
||||||
|
<fa-icon class="between-arrow" [icon]="['fas', 'arrow-right-arrow-left']" [fixedWidth]="true"></fa-icon>
|
||||||
|
<app-sats [satoshis]="channel.node_right.base_fee_mtokens / 1000" digitsInfo="1.0-2"></app-sats>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="col-md map-col">
|
||||||
|
<app-nodes-channels-map *ngIf="!error" [style]="'channelpage'" [channel]="channelGeo" [fitContainer]="true" (readyEvent)="onMapReady()"></app-nodes-channels-map>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-template [ngIf]="error">
|
||||||
|
<div class="text-center">
|
||||||
|
<span i18n="error.general-loading-data">Error loading data.</span>
|
||||||
|
<br><br>
|
||||||
|
<i>{{ error.status }}: {{ error.error }}</i>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
@ -0,0 +1,76 @@
|
|||||||
|
.title {
|
||||||
|
font-size: 52px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
font-size: 32px;
|
||||||
|
margin-top: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badges {
|
||||||
|
font-size: 28px;
|
||||||
|
|
||||||
|
::ng-deep .badge {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-width-row {
|
||||||
|
padding-left: 15px;
|
||||||
|
padding-right: 15px;
|
||||||
|
|
||||||
|
&:nth-child(even) {
|
||||||
|
background: #181b2d;
|
||||||
|
margin: 15px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodes {
|
||||||
|
font-size: 36px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.between-arrow {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-col {
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 470px;
|
||||||
|
min-width: 470px;
|
||||||
|
padding: 0;
|
||||||
|
background: #181b2d;
|
||||||
|
max-height: 470px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .symbol {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dual-cell {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: baseline;
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
width: 0;
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
&:nth-child(2) {
|
||||||
|
text-align: center;
|
||||||
|
max-width: 1.5em;
|
||||||
|
}
|
||||||
|
&:nth-child(3) {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||||
|
import { ActivatedRoute, ParamMap } from '@angular/router';
|
||||||
|
import { Observable, of } from 'rxjs';
|
||||||
|
import { catchError, switchMap, tap } from 'rxjs/operators';
|
||||||
|
import { SeoService } from 'src/app/services/seo.service';
|
||||||
|
import { OpenGraphService } from 'src/app/services/opengraph.service';
|
||||||
|
import { LightningApiService } from '../lightning-api.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-channel-preview',
|
||||||
|
templateUrl: './channel-preview.component.html',
|
||||||
|
styleUrls: ['./channel-preview.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class ChannelPreviewComponent implements OnInit {
|
||||||
|
channel$: Observable<any>;
|
||||||
|
error: any = null;
|
||||||
|
channelGeo: number[] = [];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private lightningApiService: LightningApiService,
|
||||||
|
private activatedRoute: ActivatedRoute,
|
||||||
|
private seoService: SeoService,
|
||||||
|
private openGraphService: OpenGraphService,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.channel$ = this.activatedRoute.paramMap
|
||||||
|
.pipe(
|
||||||
|
switchMap((params: ParamMap) => {
|
||||||
|
this.openGraphService.waitFor('channel-map');
|
||||||
|
this.openGraphService.waitFor('channel-data');
|
||||||
|
this.error = null;
|
||||||
|
this.seoService.setTitle(`Channel: ${params.get('short_id')}`);
|
||||||
|
return this.lightningApiService.getChannel$(params.get('short_id'))
|
||||||
|
.pipe(
|
||||||
|
tap((data) => {
|
||||||
|
if (!data.node_left.longitude || !data.node_left.latitude ||
|
||||||
|
!data.node_right.longitude || !data.node_right.latitude) {
|
||||||
|
this.channelGeo = [];
|
||||||
|
} else {
|
||||||
|
this.channelGeo = [
|
||||||
|
data.node_left.public_key,
|
||||||
|
data.node_left.alias,
|
||||||
|
data.node_left.longitude, data.node_left.latitude,
|
||||||
|
data.node_right.public_key,
|
||||||
|
data.node_right.alias,
|
||||||
|
data.node_right.longitude, data.node_right.latitude,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
this.openGraphService.waitOver('channel-data');
|
||||||
|
}),
|
||||||
|
catchError((err) => {
|
||||||
|
this.error = err;
|
||||||
|
this.openGraphService.waitOver('channel-map');
|
||||||
|
this.openGraphService.waitOver('channel-data');
|
||||||
|
return of(null);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMapReady() {
|
||||||
|
this.openGraphService.waitOver('channel-map');
|
||||||
|
}
|
||||||
|
}
|
@ -7,9 +7,11 @@ import { LightningModule } from './lightning.module';
|
|||||||
import { LightningApiService } from './lightning-api.service';
|
import { LightningApiService } from './lightning-api.service';
|
||||||
import { NodePreviewComponent } from './node/node-preview.component';
|
import { NodePreviewComponent } from './node/node-preview.component';
|
||||||
import { LightningPreviewsRoutingModule } from './lightning-previews.routing.module';
|
import { LightningPreviewsRoutingModule } from './lightning-previews.routing.module';
|
||||||
|
import { ChannelPreviewComponent } from './channel/channel-preview.component';
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
NodePreviewComponent,
|
NodePreviewComponent,
|
||||||
|
ChannelPreviewComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
import { NodePreviewComponent } from './node/node-preview.component';
|
import { NodePreviewComponent } from './node/node-preview.component';
|
||||||
|
import { ChannelPreviewComponent } from './channel/channel-preview.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: 'node/:public_key',
|
path: 'node/:public_key',
|
||||||
component: NodePreviewComponent,
|
component: NodePreviewComponent,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'channel/:short_id',
|
||||||
|
component: ChannelPreviewComponent,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '**',
|
path: '**',
|
||||||
redirectTo: ''
|
redirectTo: ''
|
||||||
|
Loading…
x
Reference in New Issue
Block a user