Merge pull request #2273 from mempool/nymkappa/feature/channel-page-map

Show channel on the map in channel page
This commit is contained in:
wiz 2022-08-10 23:24:31 +09:00 committed by GitHub
commit 7fecea9cca
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 91 additions and 8 deletions

View file

@ -96,7 +96,31 @@ class ChannelsApi {
public async $getChannel(id: string): Promise<any> { public async $getChannel(id: string): Promise<any> {
try { try {
const query = `SELECT n1.alias AS alias_left, n2.alias AS alias_right, channels.*, ns1.channels AS channels_left, ns1.capacity AS capacity_left, ns2.channels AS channels_right, ns2.capacity AS capacity_right 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 LEFT JOIN node_stats AS ns1 ON ns1.public_key = channels.node1_public_key LEFT JOIN node_stats AS ns2 ON ns2.public_key = channels.node2_public_key WHERE (ns1.id = (SELECT MAX(id) FROM node_stats WHERE public_key = channels.node1_public_key) AND ns2.id = (SELECT MAX(id) FROM node_stats WHERE public_key = channels.node2_public_key)) AND channels.id = ?`; const query = `
SELECT n1.alias AS alias_left, n1.longitude as node1_longitude, n1.latitude as node1_latitude,
n2.alias AS alias_right, n2.longitude as node2_longitude, n2.latitude as node2_latitude,
channels.*,
ns1.channels AS channels_left, ns1.capacity AS capacity_left, ns2.channels AS channels_right, ns2.capacity AS capacity_right
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
LEFT JOIN node_stats AS ns1 ON ns1.public_key = channels.node1_public_key
LEFT JOIN node_stats AS ns2 ON ns2.public_key = channels.node2_public_key
WHERE (
ns1.id = (
SELECT MAX(id)
FROM node_stats
WHERE public_key = channels.node1_public_key
)
AND ns2.id = (
SELECT MAX(id)
FROM node_stats
WHERE public_key = channels.node2_public_key
)
)
AND channels.id = ?
`;
const [rows]: any = await DB.query(query, [id]); const [rows]: any = await DB.query(query, [id]);
if (rows[0]) { if (rows[0]) {
return this.convertChannel(rows[0]); return this.convertChannel(rows[0]);
@ -289,6 +313,8 @@ class ChannelsApi {
'max_htlc_mtokens': channel.node1_max_htlc_mtokens, 'max_htlc_mtokens': channel.node1_max_htlc_mtokens,
'min_htlc_mtokens': channel.node1_min_htlc_mtokens, 'min_htlc_mtokens': channel.node1_min_htlc_mtokens,
'updated_at': channel.node1_updated_at, 'updated_at': channel.node1_updated_at,
'longitude': channel.node1_longitude,
'latitude': channel.node1_latitude,
}, },
'node_right': { 'node_right': {
'alias': channel.alias_right, 'alias': channel.alias_right,
@ -302,6 +328,8 @@ class ChannelsApi {
'max_htlc_mtokens': channel.node2_max_htlc_mtokens, 'max_htlc_mtokens': channel.node2_max_htlc_mtokens,
'min_htlc_mtokens': channel.node2_min_htlc_mtokens, 'min_htlc_mtokens': channel.node2_min_htlc_mtokens,
'updated_at': channel.node2_updated_at, 'updated_at': channel.node2_updated_at,
'longitude': channel.node2_longitude,
'latitude': channel.node2_latitude,
}, },
}; };
} }

View file

@ -32,6 +32,9 @@ class ChannelsRoutes {
res.status(404).send('Channel not found'); res.status(404).send('Channel not found');
return; return;
} }
res.header('Pragma', 'public');
res.header('Cache-control', 'public');
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
res.json(channel); res.json(channel);
} catch (e) { } catch (e) {
res.status(500).send(e instanceof Error ? e.message : e); res.status(500).send(e instanceof Error ? e.message : e);

View file

@ -14,6 +14,8 @@
<div class="clearfix"></div> <div class="clearfix"></div>
<app-nodes-channels-map *ngIf="!error" [style]="'channelpage'" [channel]="channelGeo"></app-nodes-channels-map>
<div class="box"> <div class="box">
<div class="row"> <div class="row">

View file

@ -1,7 +1,7 @@
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { ActivatedRoute, ParamMap } from '@angular/router'; import { ActivatedRoute, ParamMap } from '@angular/router';
import { Observable, of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { catchError, switchMap } from 'rxjs/operators'; import { catchError, switchMap, tap } from 'rxjs/operators';
import { SeoService } from 'src/app/services/seo.service'; import { SeoService } from 'src/app/services/seo.service';
import { LightningApiService } from '../lightning-api.service'; import { LightningApiService } from '../lightning-api.service';
@ -14,6 +14,7 @@ import { LightningApiService } from '../lightning-api.service';
export class ChannelComponent implements OnInit { export class ChannelComponent implements OnInit {
channel$: Observable<any>; channel$: Observable<any>;
error: any = null; error: any = null;
channelGeo: number[] = [];
constructor( constructor(
private lightningApiService: LightningApiService, private lightningApiService: LightningApiService,
@ -29,9 +30,23 @@ export class ChannelComponent implements OnInit {
this.seoService.setTitle(`Channel: ${params.get('short_id')}`); this.seoService.setTitle(`Channel: ${params.get('short_id')}`);
return this.lightningApiService.getChannel$(params.get('short_id')) return this.lightningApiService.getChannel$(params.get('short_id'))
.pipe( .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,
];
}
}),
catchError((err) => { catchError((err) => {
this.error = err; this.error = err;
console.log(this.error);
return of(null); return of(null);
}) })
); );

View file

@ -16,8 +16,9 @@ import 'echarts-gl';
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class NodesChannelsMap implements OnInit, OnDestroy { export class NodesChannelsMap implements OnInit, OnDestroy {
@Input() style: 'graph' | 'nodepage' | 'widget' = 'graph'; @Input() style: 'graph' | 'nodepage' | 'widget' | 'channelpage' = 'graph';
@Input() publicKey: string | undefined; @Input() publicKey: string | undefined;
@Input() channel: any[] = [];
observable$: Observable<any>; observable$: Observable<any>;
@ -25,6 +26,8 @@ export class NodesChannelsMap implements OnInit, OnDestroy {
zoom: number | undefined; zoom: number | undefined;
channelWidth = 0.6; channelWidth = 0.6;
channelOpacity = 0.1; channelOpacity = 0.1;
channelColor = '#466d9d';
channelCurve = 0;
chartInstance = undefined; chartInstance = undefined;
chartOptions: EChartsOption = {}; chartOptions: EChartsOption = {};
@ -67,13 +70,29 @@ export class NodesChannelsMap implements OnInit, OnDestroy {
const nodes = []; const nodes = [];
const nodesPubkeys = {}; const nodesPubkeys = {};
let thisNodeGPS: number[] | undefined = undefined; let thisNodeGPS: number[] | undefined = undefined;
for (const channel of data[1]) {
let geoloc = data[1];
if (this.style === 'channelpage') {
if (this.channel.length === 0) {
geoloc = [];
} else {
geoloc = [this.channel];
}
}
for (const channel of geoloc) {
if (!thisNodeGPS && data[2] === channel[0]) { if (!thisNodeGPS && data[2] === channel[0]) {
thisNodeGPS = [channel[2], channel[3]]; thisNodeGPS = [channel[2], channel[3]];
} else if (!thisNodeGPS && data[2] === channel[4]) { } else if (!thisNodeGPS && data[2] === channel[4]) {
thisNodeGPS = [channel[6], channel[7]]; thisNodeGPS = [channel[6], channel[7]];
} }
// 0 - node1 pubkey
// 1 - node1 alias
// 2,3 - node1 GPS
// 4 - node2 pubkey
// 5 - node2 alias
// 6,7 - node2 GPS
// We add a bit of noise so nodes at the same location are not all // We add a bit of noise so nodes at the same location are not all
// on top of each other // on top of each other
let random = Math.random() * 2 * Math.PI; let random = Math.random() * 2 * Math.PI;
@ -115,6 +134,22 @@ export class NodesChannelsMap implements OnInit, OnDestroy {
this.channelWidth = 1; this.channelWidth = 1;
this.channelOpacity = 1; this.channelOpacity = 1;
} }
if (this.style === 'channelpage' && this.channel.length > 0) {
this.channelWidth = 2;
this.channelOpacity = 1;
this.channelColor = '#bafcff';
this.channelCurve = 0.1;
this.center = [
(this.channel[2] + this.channel[6]) / 2,
(this.channel[3] + this.channel[7]) / 2
];
const distance = Math.sqrt(
Math.pow(this.channel[7] - this.channel[3], 2) +
Math.pow(this.channel[6] - this.channel[2], 2)
);
this.zoom = -0.05 * distance + 8;
}
this.prepareChartOptions(nodes, channelsLoc); this.prepareChartOptions(nodes, channelsLoc);
})); }));
@ -202,8 +237,8 @@ export class NodesChannelsMap implements OnInit, OnDestroy {
lineStyle: { lineStyle: {
opacity: this.channelOpacity, opacity: this.channelOpacity,
width: this.channelWidth, width: this.channelWidth,
curveness: 0, curveness: this.channelCurve,
color: '#466d9d', color: this.channelColor,
}, },
blendMode: 'lighter', blendMode: 'lighter',
tooltip: { tooltip: {