diff --git a/frontend/src/app/components/search-form/search-form.component.ts b/frontend/src/app/components/search-form/search-form.component.ts index 3914918ad..b9f22b0cf 100644 --- a/frontend/src/app/components/search-form/search-form.component.ts +++ b/frontend/src/app/components/search-form/search-form.component.ts @@ -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[]) => { diff --git a/frontend/src/app/components/search-form/search-results/search-results.component.html b/frontend/src/app/components/search-form/search-results/search-results.component.html index e3e3e5212..8c18b1565 100644 --- a/frontend/src/app/components/search-form/search-results/search-results.component.html +++ b/frontend/src/app/components/search-form/search-results/search-results.component.html @@ -17,8 +17,8 @@
Lightning Channels
- - diff --git a/frontend/src/app/lightning/channel/channel-box/channel-box.component.html b/frontend/src/app/lightning/channel/channel-box/channel-box.component.html new file mode 100644 index 000000000..978d49c8c --- /dev/null +++ b/frontend/src/app/lightning/channel/channel-box/channel-box.component.html @@ -0,0 +1,40 @@ +
+

{{ channel.alias || '?' }}

+ + {{ channel.public_key | shortenString : 12 }} + + +
+
+ +
+ + + + + + + + + + + + + + + + + + + +
Fee rate + {{ channel.fee_rate }} ppm ({{ channel.fee_rate / 10000 | number }}%) +
Base fee + +
Min HTLC + +
Max HTLC + +
+
+
\ No newline at end of file diff --git a/frontend/src/app/lightning/channel/channel-box/channel-box.component.scss b/frontend/src/app/lightning/channel/channel-box/channel-box.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/lightning/channel/channel-box/channel-box.component.ts b/frontend/src/app/lightning/channel/channel-box/channel-box.component.ts new file mode 100644 index 000000000..f6f735f56 --- /dev/null +++ b/frontend/src/app/lightning/channel/channel-box/channel-box.component.ts @@ -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() { } + +} diff --git a/frontend/src/app/lightning/channel/channel.component.html b/frontend/src/app/lightning/channel/channel.component.html index 649773b41..47b5b334c 100644 --- a/frontend/src/app/lightning/channel/channel.component.html +++ b/frontend/src/app/lightning/channel/channel.component.html @@ -1,11 +1,15 @@
-
-

Channel {{ channel.short_id }}

-
- Inactive - Active - Closed -
+
+

{{ channel.short_id }}

+ + {{ channel.id }} + + +
+
+ Inactive + Active + Closed
@@ -42,7 +46,7 @@ Capacity -   + @@ -55,90 +59,10 @@
-
-

{{ channel.alias_left || '?' }}

- - {{ channel.node1_public_key | shortenString : 18 }} - - -
-
- -
-
- - - - - - - - - - - - - - - - - - - -
Fee rate - {{ channel.node1_fee_rate / 10000 | number }}% -
Base fee - -
Min HTLC - -
Max HTLC - -
-
-
-
+
-
-

{{ channel.alias_right || '?' }}

- - {{ channel.node2_public_key | shortenString : 18 }} - - -
-
- -
- - - - - - - - - - - - - - - - - - - -
Fee rate - {{ channel.node2_fee_rate / 10000 | number }}% -
Base fee - -
Min HTLC - -
Max HTLC - -
-
-
+
diff --git a/frontend/src/app/lightning/channel/channel.component.scss b/frontend/src/app/lightning/channel/channel.component.scss index a6878a23c..a5aff4428 100644 --- a/frontend/src/app/lightning/channel/channel.component.scss +++ b/frontend/src/app/lightning/channel/channel.component.scss @@ -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; -} \ No newline at end of file +} + +app-fiat { + display: block; + font-size: 13px; + @media (min-width: 768px) { + font-size: 14px; + display: inline-block; + margin-left: 10px; + } +} diff --git a/frontend/src/app/lightning/channel/channel.component.ts b/frontend/src/app/lightning/channel/channel.component.ts index b64e08353..029b83e08 100644 --- a/frontend/src/app/lightning/channel/channel.component.ts +++ b/frontend/src/app/lightning/channel/channel.component.ts @@ -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')); }) ); diff --git a/frontend/src/app/lightning/channels-list/channels-list.component.html b/frontend/src/app/lightning/channels-list/channels-list.component.html index fe6d44e42..dfc01a6e5 100644 --- a/frontend/src/app/lightning/channels-list/channels-list.component.html +++ b/frontend/src/app/lightning/channels-list/channels-list.component.html @@ -1,60 +1,16 @@
- - + - + - - - - - - - - - - - - - - + @@ -75,12 +31,37 @@ -
Node Alias Node ID StatusFee RateFee Rate CapacityChannel IDChannel ID
- {{ channel.alias_left || '?' }} - - - {{ channel.node1_public_key | shortenString : 10 }} - - - - Inactive - Active - Closed - - {{ channel.node1_fee_rate / 10000 | number }}% - - {{ channel.alias_right || '?' }} - - - {{ channel.node2_public_key | shortenString : 10 }} - - - - Inactive - Active - Closed - - {{ channel.node2_fee_rate / 10000 | number }}% - - - - {{ channel.short_id }} -
+
+
-
\ No newline at end of file + + + {{ node.alias || '?' }} + + + + {{ node.public_key | shortenString : 10 }} + + + + + Inactive + Active + Closed + + + {{ node.fee_rate }} ppm ({{ node.fee_rate / 10000 | number }}%) + + + + + + {{ channel.short_id }} + + diff --git a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.html b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.html index 3c14763a5..31b0784d6 100644 --- a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.html +++ b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.html @@ -4,7 +4,7 @@
- Nodes Statistics  + Network Statistics 
diff --git a/frontend/src/app/lightning/lightning.module.ts b/frontend/src/app/lightning/lightning.module.ts index ffb1fd356..1ab421108 100644 --- a/frontend/src/app/lightning/lightning.module.ts +++ b/frontend/src/app/lightning/lightning.module.ts @@ -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, diff --git a/frontend/src/app/lightning/node/node.component.html b/frontend/src/app/lightning/node/node.component.html index 5309e10f2..80e357c22 100644 --- a/frontend/src/app/lightning/node/node.component.html +++ b/frontend/src/app/lightning/node/node.component.html @@ -1,10 +1,8 @@
@@ -20,7 +18,7 @@ Total capacity -   + @@ -32,12 +30,13 @@ Average channel size -   +
+
diff --git a/frontend/src/app/lightning/node/node.component.scss b/frontend/src/app/lightning/node/node.component.scss index c3831ded9..db4adc7b6 100644 --- a/frontend/src/app/lightning/node/node.component.scss +++ b/frontend/src/app/lightning/node/node.component.scss @@ -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; -} diff --git a/frontend/src/app/lightning/node/node.component.ts b/frontend/src/app/lightning/node/node.component.ts index 5842a1b9d..3f2af52b9 100644 --- a/frontend/src/app/lightning/node/node.component.ts +++ b/frontend/src/app/lightning/node/node.component.ts @@ -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 === '') { diff --git a/lightning-backend/src/api/explorer/channels.api.ts b/lightning-backend/src/api/explorer/channels.api.ts index 8551a23bd..283b9388a 100644 --- a/lightning-backend/src/api/explorer/channels.api.ts +++ b/lightning-backend/src/api/explorer/channels.api.ts @@ -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 { 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(); diff --git a/lightning-backend/src/api/explorer/nodes.api.ts b/lightning-backend/src/api/explorer/nodes.api.ts index 28aa6d925..391056d0b 100644 --- a/lightning-backend/src/api/explorer/nodes.api.ts +++ b/lightning-backend/src/api/explorer/nodes.api.ts @@ -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) {