CL UX updates

CL UX updates
pull/260/head
Shahana Farooqui 4 years ago
parent b3d85d30fc
commit 1b4f55761e

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -9,8 +9,8 @@
<link rel="icon" type="image/png" sizes="32x32" href="assets/images/favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="assets/images/favicon/favicon-16x16.png">
<link rel="manifest" href="assets/images/favicon/site.webmanifest">
<link rel="stylesheet" href="styles.50c2f6a606d60f850d7c.css"></head>
<link rel="stylesheet" href="styles.09fbe043baaff9d23c8b.css"></head>
<body>
<rtl-app></rtl-app>
<script src="runtime.05dca71f751b1d7befeb.js" defer></script><script src="polyfills-es5.b8e32dec482ae69710a2.js" nomodule defer></script><script src="polyfills.ebf9033c33aa4a5af12a.js" defer></script><script src="main.c57e3ff765ca9ee015b5.js" defer></script></body>
<script src="runtime.8e5ce25f927cbaca2d37.js" defer></script><script src="polyfills-es5.b8e32dec482ae69710a2.js" nomodule defer></script><script src="polyfills.ebf9033c33aa4a5af12a.js" defer></script><script src="main.e5b7d4371169014e6bcc.js" defer></script></body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +0,0 @@
!function(e){function r(r){for(var n,a,i=r[0],c=r[1],f=r[2],p=0,s=[];p<i.length;p++)a=i[p],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&s.push(o[a][0]),o[a]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(l&&l(r);s.length;)s.shift()();return u.push.apply(u,f||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++)0!==o[t[i]]&&(n=!1);n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={0:0},u=[];function a(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,i=document.createElement("script");i.charset="utf-8",i.timeout=120,a.nc&&i.setAttribute("nonce",a.nc),i.src=function(e){return a.p+""+({}[e]||e)+"."+{1:"77d491bf73b15683870f",6:"b4a9bd707a549a4eb404",7:"84e864ea895f0d630a75"}[e]+".js"}(e);var c=new Error;u=function(r){i.onerror=i.onload=null,clearTimeout(f);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var f=setTimeout((function(){u({type:"timeout",target:i})}),12e4);i.onerror=i.onload=u,document.head.appendChild(i)}return Promise.all(r)},a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,(function(r){return e[r]}).bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="",a.oe=function(e){throw console.error(e),e};var i=window.webpackJsonp=window.webpackJsonp||[],c=i.push.bind(i);i.push=r,i=i.slice();for(var f=0;f<i.length;f++)r(i[f]);var l=c;t()}([]);

@ -0,0 +1 @@
!function(e){function r(r){for(var n,i,a=r[0],c=r[1],f=r[2],p=0,s=[];p<a.length;p++)i=a[p],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&s.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(l&&l(r);s.length;)s.shift()();return u.push.apply(u,f||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++)0!==o[t[a]]&&(n=!1);n&&(u.splice(r--,1),e=i(i.s=t[0]))}return e}var n={},o={0:0},u=[];function i(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,i),t.l=!0,t.exports}i.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+""+({}[e]||e)+"."+{1:"77d491bf73b15683870f",6:"b479355282ea9b377ebc",7:"22cd396798191c8cb98f"}[e]+".js"}(e);var c=new Error;u=function(r){a.onerror=a.onload=null,clearTimeout(f);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var f=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(r)},i.m=e,i.c=n,i.d=function(e,r,t){i.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,r){if(1&r&&(e=i(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)i.d(t,n,(function(r){return e[r]}).bind(null,n));return t},i.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(r,"a",r),r},i.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},i.p="",i.oe=function(e){throw console.error(e),e};var a=window.webpackJsonp=window.webpackJsonp||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var f=0;f<a.length;f++)r(a[f]);var l=c;t()}([]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -9,9 +9,9 @@ exports.listChannels = (req, res, next) => {
request(options).then(function (body) {
logger.info({fileName: 'Channels', msg: 'List Channels: ' + JSON.stringify(body)});
body.map(channel => {
local = (channel.msatoshi_to_us) ? +channel.msatoshi_to_us : 0;
remote = (channel.msatoshi_to_them) ? +msatoshi_to_them : 0;
total = channel.msatoshi_total ? +channel.msatoshi_total : 0;
local = (channel.msatoshi_to_us) ? channel.msatoshi_to_us : 0;
remote = (channel.msatoshi_to_them) ? channel.msatoshi_to_them : 0;
total = channel.msatoshi_total ? channel.msatoshi_total : 0;
channel.balancedness = (1 - Math.abs((local-remote)/total)).toFixed(3);
})
res.status(200).json(body);

@ -23,13 +23,15 @@ import { CLChannelLookupComponent } from './lookups/channel-lookup/channel-looku
import { CLNodeLookupComponent } from './lookups/node-lookup/node-lookup.component';
import { CLQueryRoutesComponent } from './transactions/query-routes/query-routes.component';
import { CLChannelOpenTableComponent } from './peers-channels/channels/channels-tables/channel-open-table/channel-open-table.component';
import { CLChannelPendingTableComponent } from './peers-channels/channels/channels-tables/channel-pending-table/channel-pending-table.component';
import { CLNodeInfoComponent } from './home/node-info/node-info.component';
import { CLBalancesInfoComponent } from './home/balances-info/balances-info.component';
import { CLFeeInfoComponent } from './home/fee-info/fee-info.component';
import { CLChannelStatusInfoComponent } from './home/channel-status-info/channel-status-info.component';
import { CLChannelCapacityInfoComponent } from './home/channel-capacity-info/channel-capacity-info.component';
import { CLChannelLiquidityInfoComponent } from './home/channel-liquidity-info/channel-liquidity-info.component';
import { CLFeeRatesComponent } from './home/fee-rates/fee-rates.component';
import { CLNetworkInfoComponent } from './network-info/network-info.component';
import { CLFeeRatesComponent } from './network-info/fee-rates/fee-rates.component';
import { CLUnlockedGuard } from '../shared/services/auth.guard';
@ -59,12 +61,14 @@ import { CLUnlockedGuard } from '../shared/services/auth.guard';
CLOnChainComponent,
CLChannelsTablesComponent,
CLChannelOpenTableComponent,
CLChannelPendingTableComponent,
CLNodeInfoComponent,
CLBalancesInfoComponent,
CLFeeInfoComponent,
CLChannelStatusInfoComponent,
CLChannelCapacityInfoComponent,
CLChannelLiquidityInfoComponent,
CLNetworkInfoComponent,
CLFeeRatesComponent
],
providers: [

@ -8,6 +8,7 @@ import { CLPeersChannelsComponent } from '../clightning/peers-channels/peers-cha
import { CLTransactionsComponent } from '../clightning/transactions/transactions.component';
import { CLRoutingComponent } from '../clightning/routing/routing.component';
import { CLLookupsComponent } from './lookups/lookups.component';
import { CLNetworkInfoComponent } from './network-info/network-info.component';
import { CLUnlockedGuard } from '../shared/services/auth.guard';
import { NotFoundComponent } from '../shared/components/not-found/not-found.component';
@ -20,7 +21,9 @@ export const ClRoutes: Routes = [
{ path: 'transactions', component: CLTransactionsComponent, canActivate: [CLUnlockedGuard] },
{ path: 'routing', component: CLRoutingComponent, canActivate: [CLUnlockedGuard] },
{ path: 'lookups', component: CLLookupsComponent, canActivate: [CLUnlockedGuard] },
{ path: 'rates', component: CLNetworkInfoComponent, canActivate: [CLUnlockedGuard] },
{ path: '**', component: NotFoundComponent },
{ path: 'network', redirectTo: 'rates' },
{ path: 'wallet', redirectTo: 'home' },
{ path: 'backup', redirectTo: 'home' }
]}

@ -1,69 +0,0 @@
<mat-card [ngClass]="{'custom-card error-border': flgLoading==='error','custom-card': true}">
<mat-card-header class="bg-primary" fxLayoutAlign="center center">
<mat-card-title class="m-0 pt-2">
<h5>Fee Rates - per{{feeRateStyle}}</h5>
</mat-card-title>
</mat-card-header>
<mat-card-content>
<div fxLayout="column" class="px-4">
<mat-list class="fee-rate-list" fxFlex="100" fxLayoutAlign="start start">
<mat-list-item fxFlex="50" fxLayoutAlign="start start">Urgent</mat-list-item>
<mat-list-item fxFlex="45" fxLayoutAlign="end start">
<p class="mat-button-text">{{perkbw?.urgent | number}}</p>
</mat-list-item>
<mat-divider></mat-divider>
</mat-list>
<mat-list class="fee-rate-list" fxFlex="100" fxLayoutAlign="start start">
<mat-list-item fxFlex="50" fxLayoutAlign="start start">Normal</mat-list-item>
<mat-list-item fxFlex="45" fxLayoutAlign="end start">
<p class="mat-button-text">{{perkbw?.normal | number}}</p>
</mat-list-item>
<mat-divider></mat-divider>
</mat-list>
<mat-list class="fee-rate-list" fxFlex="100" fxLayoutAlign="start start">
<mat-list-item fxFlex="50" fxLayoutAlign="start start">Slow</mat-list-item>
<mat-list-item fxFlex="45" fxLayoutAlign="end start">
<p class="mat-button-text">{{perkbw?.slow | number}}</p>
</mat-list-item>
<mat-divider></mat-divider>
</mat-list>
<mat-list class="fee-rate-list" fxFlex="100" fxLayoutAlign="start start">
<mat-list-item fxFlex="50" fxLayoutAlign="start start">Min Acceptable</mat-list-item>
<mat-list-item fxFlex="45" fxLayoutAlign="end start">
<p class="mat-button-text">{{perkbw?.min_acceptable | number}}</p>
</mat-list-item>
<mat-divider></mat-divider>
</mat-list>
<mat-list class="fee-rate-list" fxFlex="100" fxLayoutAlign="start start">
<mat-list-item fxFlex="50" fxLayoutAlign="start start">Max Acceptable</mat-list-item>
<mat-list-item fxFlex="45" fxLayoutAlign="end start">
<p class="mat-button-text">{{perkbw?.max_acceptable | number}}</p>
</mat-list-item>
<mat-divider></mat-divider>
</mat-list>
<mat-list class="fee-rate-list" fxFlex="100" fxLayoutAlign="start start">
<mat-list-item fxFlex="50" fxLayoutAlign="start start">Opening Channel</mat-list-item>
<mat-list-item fxFlex="45" fxLayoutAlign="end start">
<p class="mat-button-text">{{feeRates?.onchain_fee_estimates?.opening_channel_satoshis | number}}</p>
</mat-list-item>
<mat-divider></mat-divider>
</mat-list>
<mat-list class="fee-rate-list" fxFlex="100" fxLayoutAlign="start start">
<mat-list-item fxFlex="50" fxLayoutAlign="start start">Mutual Close</mat-list-item>
<mat-list-item fxFlex="45" fxLayoutAlign="end start">
<p class="mat-button-text">{{feeRates?.onchain_fee_estimates?.mutual_close_satoshis | number}}</p>
</mat-list-item>
<mat-divider></mat-divider>
</mat-list>
<mat-list class="fee-rate-list" fxFlex="100" fxLayoutAlign="start start">
<mat-list-item fxFlex="50" fxLayoutAlign="start start">Unilateral Close</mat-list-item>
<mat-list-item fxFlex="45" fxLayoutAlign="end start">
<p class="mat-button-text">{{feeRates?.onchain_fee_estimates?.unilateral_close_satoshis | number}}</p>
</mat-list-item>
<mat-divider></mat-divider>
</mat-list>
</div>
<mat-progress-bar *ngIf="flgLoading===true" mode="indeterminate" class="mt-minus-5"></mat-progress-bar>
<mat-divider></mat-divider>
</mat-card-content>
</mat-card>

@ -25,7 +25,7 @@
<rtl-cl-balances-info fxFlex="100" *ngSwitchCase="'balance'" [balances]="balances" [ngClass]="{'error-border': flgLoading[2]==='error'}"></rtl-cl-balances-info>
<rtl-cl-channel-capacity-info fxFlex="100" *ngSwitchCase="'capacity'" [sortBy]="sortField" [channelBalances]="channelBalances" [allChannels]="allChannelsCapacity" [ngClass]="{'error-border': flgLoading[5]==='error'}"></rtl-cl-channel-capacity-info>
<rtl-cl-fee-info fxFlex="100" *ngSwitchCase="'fee'" [fees]="fees" [ngClass]="{'error-border': flgLoading[1]==='error'}"></rtl-cl-fee-info>
<rtl-cl-channel-status-info fxFlex="100" *ngSwitchCase="'status'" [channelsStatus]="channelsStatus" [ngClass]="{'error-border': flgLoading[2]==='error'}"></rtl-cl-channel-status-info>
<rtl-cl-channel-status-info fxFlex="100" *ngSwitchCase="'status'" [channelsStatus]="channelsStatus" [ngClass]="{'error-border': flgLoading[0]==='error'}"></rtl-cl-channel-status-info>
<h3 *ngSwitchDefault>Error! Unable to find information!</h3>
</div>
</mat-card-content>

@ -31,8 +31,6 @@ export class CLHomeComponent implements OnInit, OnDestroy {
public faNetworkWired = faNetworkWired;
public flgChildInfoUpdated = false;
public userPersonaEnum = UserPersonaEnum;
public activeChannels = 0;
public inactiveChannels = 0;
public channelBalances = {localBalance: 0, remoteBalance: 0, balancedness: '0'};
public selNode: SelNodeChild = {};
public fees: FeesCL;
@ -169,8 +167,6 @@ export class CLHomeComponent implements OnInit, OnDestroy {
this.flgLoading[4] = (undefined !== this.feeRatesPerKB && undefined !== this.feeRatesPerKW) ? false : true;
}
this.activeChannels = rtlStore.information.num_active_channels;
this.inactiveChannels = rtlStore.information.num_inactive_channels;
this.channelsStatus = {
active: { channels: rtlStore.information.num_active_channels, capacity: 0 },
inactive: { channels: rtlStore.information.num_inactive_channels, capacity: 0 },
@ -186,6 +182,12 @@ export class CLHomeComponent implements OnInit, OnDestroy {
this.totalInboundLiquidity = this.totalInboundLiquidity + channel.msatoshi_to_them;
this.totalOutboundLiquidity = this.totalOutboundLiquidity + channel.msatoshi_to_us;
});
if (this.totalInboundLiquidity>0) {
this.totalInboundLiquidity = this.totalInboundLiquidity / 1000;
}
if (this.totalOutboundLiquidity>0) {
this.totalOutboundLiquidity = this.totalOutboundLiquidity / 1000;
}
if (this.flgLoading[5] !== 'error') {
this.flgLoading[5] = (this.allChannels && this.allChannels.length) ? false : true;
}

@ -3,13 +3,13 @@
<h4 class="dashboard-info-title">Alias</h4>
<div class="overflow-wrap dashboard-info-value">
{{information.alias}}
<span *ngIf="!showColorFieldSeparately" class="dashboard-node-dot dot" [ngStyle]="{'backgroundColor': information.color}"></span>
<span *ngIf="!showColorFieldSeparately" class="dashboard-node-dot dot" [ngStyle]="{'backgroundColor': '#' + information.color}"></span>
</div>
</div>
<div *ngIf="showColorFieldSeparately">
<h4 class="dashboard-info-title">Color</h4>
<div class="overflow-wrap dashboard-info-value">
<span class="dashboard-node-square" [ngStyle]="{'backgroundColor': information.color}"></span>
<span class="dashboard-node-square" [ngStyle]="{'backgroundColor': '#' + information.color}"></span>
{{information.color | uppercase}}
</div>
</div>

@ -28,7 +28,7 @@
</div>
<mat-divider [inset]="true"></mat-divider>
<div fxLayout="column" fxFlex="10" class="my-1">
<h4 class="font-bold-500">Base Fee mSats</h4>
<h4 class="font-bold-500">Base Fee (mSats)</h4>
<span class="foreground-secondary-text">{{lookupResult[0].base_fee_millisatoshi | number}}</span>
</div>
<mat-divider [inset]="true"></mat-divider>
@ -109,7 +109,7 @@
</div>
<mat-divider [inset]="true"></mat-divider>
<div fxLayout="column" fxFlex="10" class="my-1">
<h4 class="font-bold-500">Base Fee mSats</h4>
<h4 class="font-bold-500">Base Fee (mSats)</h4>
<span class="foreground-secondary-text">{{lookupResult[1].base_fee_millisatoshi | number}}</span>
</div>
<mat-divider [inset]="true"></mat-divider>

@ -84,7 +84,7 @@ export class CLLookupsComponent implements OnInit, OnDestroy {
}
onSelectChange(event: any) {
this.form.resetForm();
this.resetData();
this.selectedFieldId = event.value;
}

@ -0,0 +1,38 @@
<div fxLayout="row" fxFlex="100" fxLayoutAlign="stretch" class="h-100">
<div fxLayout="column" fxFlex="50" fxLayoutAlign="space-between stretch">
<div>
<h4 fxLayoutAlign="start" class="dashboard-info-title">Urgent</h4>
<div class="overflow-wrap dashboard-info-value">{{perkbw?.urgent | number}}</div>
</div>
<div>
<h4 fxLayoutAlign="start" class="dashboard-info-title">Normal</h4>
<div class="overflow-wrap dashboard-info-value">{{perkbw?.normal | number}}</div>
</div>
<div>
<h4 fxLayoutAlign="start" class="dashboard-info-title">Slow</h4>
<div class="overflow-wrap dashboard-info-value">{{perkbw?.slow | number}}</div>
</div>
<div>
<h4 fxLayoutAlign="start" class="dashboard-info-title">Opening Channel</h4>
<div class="overflow-wrap dashboard-info-value">{{feeRates?.onchain_fee_estimates?.opening_channel_satoshis | number}}</div>
</div>
</div>
<div fxLayout="column" fxFlex="50" fxLayoutAlign="space-between stretch">
<div>
<h4 fxLayoutAlign="start" class="dashboard-info-title">Min Acceptable</h4>
<div class="overflow-wrap dashboard-info-value">{{perkbw?.min_acceptable | number}}</div>
</div>
<div>
<h4 fxLayoutAlign="start" class="dashboard-info-title">Max Acceptable</h4>
<div class="overflow-wrap dashboard-info-value">{{perkbw?.max_acceptable | number}}</div>
</div>
<div>
<h4 fxLayoutAlign="start" class="dashboard-info-title">Mutual Close</h4>
<div class="overflow-wrap dashboard-info-value">{{feeRates?.onchain_fee_estimates?.mutual_close_satoshis | number}}</div>
</div>
<div>
<h4 fxLayoutAlign="start" class="dashboard-info-title">Unilateral Close</h4>
<div class="overflow-wrap dashboard-info-value">{{feeRates?.onchain_fee_estimates?.unilateral_close_satoshis | number}}</div>
</div>
</div>
</div>

@ -0,0 +1,52 @@
<div fxLayout="column" fxLayoutAlign="space-between stretch" class="mb-4">
<mat-grid-list *ngIf="selNode.userPersona === userPersonaEnum.OPERATOR" cols="6" rowHeight="110px">
<mat-grid-tile class="node-grid-tile" *ngFor="let card of nodeCardsOperator" [colspan]="card.cols" [rowspan]="card.rows">
<div fxLayout="column" fxLayoutAlign="stretch start" fxFlex="100" class="h-100">
<div fxLayout="row" fxLayoutAlign="start start" class="w-100">
<div fxLayout="row" fxLayoutAlign="start start" class="page-title-container pl-2">
<fa-icon [icon]="card.icon" class="mr-1"></fa-icon>
<span>{{card.title}}</span>
</div>
</div>
<div fxLayout="column" fxLayoutAlign="stretch center" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="center stretch" class="w-100 h-93">
<mat-card fxLayout="row" fxFlex="95" fxLayoutAlign="start stretch" class="dashboard-card p-24 w-96 h-93">
<mat-card-content fxFlex="100" class="dashboard-card-content">
<div [ngSwitch]="card.id" fxLayout="column" fxFlex="100">
<rtl-cl-node-info fxFlex="100" *ngSwitchCase="'node'" [information]="information" [showColorFieldSeparately]="false" [ngClass]="{'error-border': flgLoading[0]==='error'}"></rtl-cl-node-info>
<rtl-cl-channel-status-info fxFlex="100" *ngSwitchCase="'status'" [channelsStatus]="channelsStatus" [ngClass]="{'error-border': flgLoading[0]==='error'}"></rtl-cl-channel-status-info>
<rtl-cl-fee-info fxFlex="100" *ngSwitchCase="'fee'" [fees]="fees" [ngClass]="{'error-border': flgLoading[1]==='error'}"></rtl-cl-fee-info>
<rtl-cl-fee-rates *ngSwitchCase="'feeRatesKB'" [flgLoading]="flgLoading[2]" [feeRates]="feeRatesPerKB" [feeRateStyle]="'KB'" class="h-100"></rtl-cl-fee-rates>
<rtl-cl-fee-rates *ngSwitchCase="'feeRatesKW'" [flgLoading]="flgLoading[2]" [feeRates]="feeRatesPerKW" [feeRateStyle]="'KW'" class="h-100"></rtl-cl-fee-rates>
</div>
</mat-card-content>
</mat-card>
</div>
</div>
</mat-grid-tile>
</mat-grid-list>
<mat-grid-list *ngIf="selNode.userPersona === userPersonaEnum.MERCHANT" cols="6" rowHeight="110px">
<mat-grid-tile class="node-grid-tile" *ngFor="let card of nodeCardsMerchant" [colspan]="card.cols" [rowspan]="card.rows">
<div fxLayout="column" fxLayoutAlign="stretch start" fxFlex="100" class="h-100">
<div fxLayout="row" fxLayoutAlign="start start" class="w-100">
<div fxLayout="row" fxLayoutAlign="start start" class="page-title-container pl-15px">
<fa-icon [icon]="card.icon" class="mr-1"></fa-icon>
<span>{{card.title}}</span>
</div>
</div>
<div fxLayout="column" fxLayoutAlign="stretch center" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="center stretch" class="w-100 h-93">
<mat-card fxLayout="row" fxFlex="95" fxLayoutAlign="start stretch" class="dashboard-card p-24 w-96 h-93">
<mat-card-content fxFlex="100" class="dashboard-card-content">
<div [ngSwitch]="card.id" fxLayout="column" fxFlex="100">
<rtl-cl-node-info fxFlex="100" *ngSwitchCase="'node'" [information]="information" [showColorFieldSeparately]="false" [ngClass]="{'error-border': flgLoading[0]==='error'}"></rtl-cl-node-info>
<rtl-cl-channel-status-info fxFlex="100" *ngSwitchCase="'status'" [channelsStatus]="channelsStatus" [ngClass]="{'error-border': flgLoading[0]==='error'}"></rtl-cl-channel-status-info>
<rtl-cl-fee-info fxFlex="100" *ngSwitchCase="'fee'" [fees]="fees" [ngClass]="{'error-border': flgLoading[1]==='error'}"></rtl-cl-fee-info>
<rtl-cl-fee-rates *ngSwitchCase="'feeRatesKB'" [flgLoading]="flgLoading[2]" [feeRates]="feeRatesPerKB" [feeRateStyle]="'KB'" class="h-100"></rtl-cl-fee-rates>
<rtl-cl-fee-rates *ngSwitchCase="'feeRatesKW'" [flgLoading]="flgLoading[2]" [feeRates]="feeRatesPerKW" [feeRateStyle]="'KW'" class="h-100"></rtl-cl-fee-rates>
</div>
</mat-card-content>
</mat-card>
</div>
</div>
</mat-grid-tile>
</mat-grid-list>
</div>

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NetworkInfoComponent } from './network-info.component';
describe('NetworkInfoComponent', () => {
let component: NetworkInfoComponent;
let fixture: ComponentFixture<NetworkInfoComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ NetworkInfoComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(NetworkInfoComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,119 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { faBolt, faServer, faNetworkWired } from '@fortawesome/free-solid-svg-icons';
import { LoggerService } from '../../shared/services/logger.service';
import { GetInfoCL, FeesCL, ChannelsStatusCL, FeeRatesCL } from '../../shared/models/clModels';
import { SelNodeChild } from '../../shared/models/RTLconfig';
import * as fromRTLReducer from '../../store/rtl.reducers';
import { CommonService } from '../../shared/services/common.service';
import { ScreenSizeEnum, UserPersonaEnum } from '../../shared/services/consts-enums-functions';
@Component({
selector: 'rtl-cl-network-info',
templateUrl: './network-info.component.html',
styleUrls: ['./network-info.component.scss']
})
export class CLNetworkInfoComponent implements OnInit, OnDestroy {
public faBolt = faBolt;
public faServer = faServer;
public faNetworkWired = faNetworkWired;
public selNode: SelNodeChild = {};
public information: GetInfoCL = {};
public fees: FeesCL;
public channelsStatus: ChannelsStatusCL = {};
feeRatesPerKB: FeeRatesCL = {};
feeRatesPerKW: FeeRatesCL = {};
public nodeCardsOperator = [];
public nodeCardsMerchant = [];
public screenSize = '';
public screenSizeEnum = ScreenSizeEnum;
public userPersonaEnum = UserPersonaEnum;
public flgLoading: Array<Boolean | 'error'> = [true, true, true];
private unSubs: Array<Subject<void>> = [new Subject()];
constructor(private logger: LoggerService, private commonService: CommonService, private store: Store<fromRTLReducer.RTLState>) {
this.screenSize = this.commonService.getScreenSize();
if(this.screenSize === ScreenSizeEnum.XS) {
this.nodeCardsMerchant = [
{ id: 'node', icon: this.faServer, title: 'Node Information', cols: 6, rows: 3 },
{ id: 'status', icon: this.faNetworkWired, title: 'Channels', cols: 6, rows: 3 },
{ id: 'fee', icon: this.faBolt, title: 'Routing Fee', cols: 6, rows: 1 },
{ id: 'feeRatesKB', icon: this.faServer, title: 'Fee Rate Per KB', cols: 6, rows: 4 },
{ id: 'feeRatesKW', icon: this.faNetworkWired, title: 'Fee Rate Per KW', cols: 6, rows: 4 }
];
this.nodeCardsOperator = [
{ id: 'feeRatesKB', icon: this.faServer, title: 'Fee Rate Per KB', cols: 6, rows: 4 },
{ id: 'feeRatesKW', icon: this.faNetworkWired, title: 'Fee Rate Per KW', cols: 6, rows: 4 }
];
} else {
this.nodeCardsMerchant = [
{ id: 'node', icon: this.faServer, title: 'Node Information', cols: 2, rows: 3 },
{ id: 'status', icon: this.faNetworkWired, title: 'Channels', cols: 2, rows: 3 },
{ id: 'fee', icon: this.faBolt, title: 'Routing Fee', cols: 2, rows: 3 },
{ id: 'feeRatesKB', icon: this.faServer, title: 'Fee Rate Per KB', cols: 3, rows: 4 },
{ id: 'feeRatesKW', icon: this.faNetworkWired, title: 'Fee Rate Per KW', cols: 3, rows: 4 }
];
this.nodeCardsOperator = [
{ id: 'feeRatesKB', icon: this.faServer, title: 'Fee Rate Per KB', cols: 3, rows: 4 },
{ id: 'feeRatesKW', icon: this.faNetworkWired, title: 'Fee Rate Per KW', cols: 3, rows: 4 }
];
}
}
ngOnInit() {
this.store.select('cl')
.pipe(takeUntil(this.unSubs[0]))
.subscribe((rtlStore) => {
rtlStore.effectErrorsCl.forEach(effectsErr => {
if (effectsErr.action === 'FetchInfoCL') {
this.flgLoading[0] = 'error';
}
if (effectsErr.action === 'FetchFeesCL') {
this.flgLoading[1] = 'error';
}
if (effectsErr.action === 'FetchFeeRatesCL') {
this.flgLoading[2] = 'error';
}
});
this.selNode = rtlStore.nodeSettings;
this.information = rtlStore.information;
if (this.flgLoading[0] !== 'error') {
this.flgLoading[0] = (undefined !== this.information.id) ? false : true;
}
this.fees = rtlStore.fees;
this.fees.totalTxCount = 0;
if (rtlStore.forwardingHistory && rtlStore.forwardingHistory.forwarding_events && rtlStore.forwardingHistory.forwarding_events.length) {
this.fees.totalTxCount = rtlStore.forwardingHistory.forwarding_events.filter(event => event.status === 'settled').length
}
if (this.flgLoading[1] !== 'error') {
this.flgLoading[1] = (undefined !== this.fees.feeCollected) ? false : true;
}
this.channelsStatus = {
active: { channels: rtlStore.information.num_active_channels, capacity: 0 },
inactive: { channels: rtlStore.information.num_inactive_channels, capacity: 0 },
pending: { channels: rtlStore.information.num_pending_channels, capacity: 0 }
};
this.feeRatesPerKB = rtlStore.feeRatesPerKB;
this.feeRatesPerKW = rtlStore.feeRatesPerKW;
if (this.flgLoading[2] !== 'error') {
this.flgLoading[2] = (undefined !== this.feeRatesPerKB && undefined !== this.feeRatesPerKW) ? false : true;
}
this.logger.info(rtlStore);
});
}
ngOnDestroy() {
this.unSubs.forEach(completeSub => {
completeSub.next();
completeSub.complete();
});
}
}

@ -2,7 +2,6 @@ import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { faExchangeAlt, faChartPie } from '@fortawesome/free-solid-svg-icons';
import { SelNodeChild } from '../../shared/models/RTLconfig';
@ -20,7 +19,7 @@ export class CLOnChainComponent implements OnInit, OnDestroy {
public balances = [{title: 'Total Balance', dataValue: 0}, {title: 'Confirmed', dataValue: 0}, {title: 'Unconfirmed', dataValue: 0}];
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject()];
constructor(private store: Store<fromRTLReducer.RTLState>, private actions$: Actions) {}
constructor(private store: Store<fromRTLReducer.RTLState>) {}
ngOnInit() {
this.store.select('cl')

@ -12,7 +12,7 @@
width: 22%;
}
.mat-column-short_channel_id, .mat-column-state, .mat-column-msatoshi_total {
.mat-column-short_channel_id, .mat-column-state, .mat-column-msatoshi_to_us, .mat-column-msatoshi_total {
flex: 0 0 15%;
width: 15%;
white-space: nowrap;

@ -4,7 +4,7 @@ import { take, takeUntil } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { MatTableDataSource, MatSort, MatPaginator, MatPaginatorIntl } from '@angular/material';
import { ChannelCL, GetInfoCL } from '../../../../../shared/models/clModels';
import { ChannelCL, GetInfoCL, ChannelEdgeCL } from '../../../../../shared/models/clModels';
import { PAGE_SIZE, PAGE_SIZE_OPTIONS, getPaginatorLabel, AlertTypeEnum, DataTypeEnum, ScreenSizeEnum, FEE_RATE_TYPES } from '../../../../../shared/services/consts-enums-functions';
import { LoggerService } from '../../../../../shared/services/logger.service';
import { CommonService } from '../../../../../shared/services/common.service';
@ -46,16 +46,16 @@ export class CLChannelOpenTableComponent implements OnInit, OnDestroy {
this.screenSize = this.commonService.getScreenSize();
if(this.screenSize === ScreenSizeEnum.XS) {
this.flgSticky = false;
this.displayedColumns = ['short_channel_id', 'state', 'msatoshi_total', 'actions'];
this.displayedColumns = ['short_channel_id', 'alias', 'msatoshi_total', 'actions'];
} else if(this.screenSize === ScreenSizeEnum.SM) {
this.flgSticky = false;
this.displayedColumns = ['short_channel_id', 'alias', 'state', 'msatoshi_total', 'actions'];
this.displayedColumns = ['short_channel_id', 'alias', 'msatoshi_total', 'actions'];
} else if(this.screenSize === ScreenSizeEnum.MD) {
this.flgSticky = false;
this.displayedColumns = ['short_channel_id', 'alias', 'state', 'msatoshi_total', 'actions'];
this.displayedColumns = ['short_channel_id', 'alias', 'msatoshi_to_us', 'msatoshi_total', 'actions'];
} else {
this.flgSticky = true;
this.displayedColumns = ['short_channel_id', 'alias', 'state', 'msatoshi_total', 'balancedness', 'actions'];
this.displayedColumns = ['short_channel_id', 'alias', 'msatoshi_to_us', 'msatoshi_total', 'balancedness', 'actions'];
}
}
@ -71,26 +71,31 @@ export class CLChannelOpenTableComponent implements OnInit, OnDestroy {
this.information = rtlStore.information;
this.numPeers = (rtlStore.peers && rtlStore.peers.length) ? rtlStore.peers.length : 0;
this.totalBalance = rtlStore.balance.totalBalance;
if (undefined !== rtlStore.allChannels) {
this.loadChannelsTable(rtlStore.allChannels);
if (rtlStore.allChannels) {
this.loadChannelsTable(rtlStore.allChannels.filter(channel => channel.state === 'CHANNELD_NORMAL'));
}
if (this.flgLoading[0] !== 'error') {
this.flgLoading[0] = (undefined !== rtlStore.allChannels) ? false : true;
this.flgLoading[0] = (rtlStore.allChannels) ? false : true;
}
this.logger.info(rtlStore);
});
}
onViewRemotePolicy(selChannel: ChannelCL) {
console.warn(selChannel);
this.store.dispatch(new RTLActions.ChannelLookupCL(selChannel.short_channel_id));
this.clEffects.setLookupCL
.pipe(take(1))
.subscribe(resLookup => {
.pipe(take(1))
.subscribe((resLookup: ChannelEdgeCL[]) => {
let remoteNode: ChannelEdgeCL = {};
if(resLookup[0].source !== this.information.id) {
remoteNode = resLookup[0];
} else {
remoteNode = resLookup[1];
}
const reorderedChannelPolicy = [
[{key: 'fee_base_msat', value: resLookup.fee_base_msat, title: 'Base Fees (mSats)', width: 32, type: DataTypeEnum.NUMBER},
{key: 'fee_rate_milli_msat', value: resLookup.fee_rate_milli_msat, title: 'Fee Rate (milli mSats)', width: 32, type: DataTypeEnum.NUMBER},
{key: 'time_lock_delta', value: resLookup.time_lock_delta, title: 'Time Lock Delta', width: 32, type: DataTypeEnum.NUMBER}]
[{key: 'base_fee_millisatoshi', value: remoteNode.base_fee_millisatoshi, title: 'Base Fees (mSats)', width: 32, type: DataTypeEnum.NUMBER},
{key: 'fee_per_millionth', value: remoteNode.fee_per_millionth, title: 'Fee/Millionth', width: 32, type: DataTypeEnum.NUMBER},
{key: 'delay', value: remoteNode.delay, title: 'Delay', width: 32, type: DataTypeEnum.NUMBER}]
];
this.store.dispatch(new RTLActions.OpenAlert({ data: {
type: AlertTypeEnum.INFORMATION,
@ -218,7 +223,7 @@ export class CLChannelOpenTableComponent implements OnInit, OnDestroy {
this.store.dispatch(new RTLActions.OpenAlert({ data: {
type: AlertTypeEnum.INFORMATION,
alertTitle: 'Channel Information',
showCopyName: 'Channel ID',
showCopyName: 'Short Channel ID',
showCopyField: selChannel.short_channel_id,
message: reorderedChannel
}}));

@ -0,0 +1,64 @@
<div fxLayout="column" class="padding-gap">
<div fxLayout="column" fxLayout.gt-xs="row" fxLayoutAlign.gt-xs="start center" fxLayoutAlign="start stretch" class="padding-gap-x page-sub-title-container">
<div fxFlex="70"></div>
<mat-form-field fxFlex="30">
<input matInput (keyup)="applyFilter()" [(ngModel)]="selFilter" name="filter" placeholder="Filter">
</mat-form-field>
</div>
<div perfectScrollbar fxLayout="row" fxLayoutAlign="start center" fxFlex="100" class="table-container w-100">
<mat-progress-bar *ngIf="flgLoading[0]===true" mode="indeterminate"></mat-progress-bar>
<table mat-table #table [dataSource]="channels" matSort [ngClass]="{'overflow-auto error-border': flgLoading[0]==='error','overflow-auto': true}">
<ng-container matColumnDef="short_channel_id">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Short Channel ID </th>
<td mat-cell *matCellDef="let channel"> {{channel?.short_channel_id}}</td>
</ng-container>
<ng-container matColumnDef="alias">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Alias </th>
<td mat-cell *matCellDef="let channel">{{channel?.alias}}</td>
</ng-container>
<ng-container matColumnDef="connected">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Connected </th>
<td mat-cell *matCellDef="let channel"> {{(channel?.connected) ? 'Connected' : 'Disconnected'}} </td>
</ng-container>
<ng-container matColumnDef="private">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Private </th>
<td mat-cell *matCellDef="let channel"> {{(channel?.private ? 'Private' : 'Public')}} </td>
</ng-container>
<ng-container matColumnDef="state">
<th mat-header-cell *matHeaderCellDef mat-sort-header> State </th>
<td mat-cell *matCellDef="let channel"> {{channel?.state}}</td>
</ng-container>
<ng-container matColumnDef="msatoshi_to_us">
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before"> mSatoshi To Us </th>
<td mat-cell *matCellDef="let channel"><span fxLayoutAlign="end center">
{{channel?.msatoshi_to_us | number}} </span></td>
</ng-container>
<ng-container matColumnDef="msatoshi_total">
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before"> Total mSatoshis </th>
<td mat-cell *matCellDef="let channel"><span fxLayoutAlign="end center">
{{channel?.msatoshi_total | number}} </span></td>
</ng-container>
<ng-container matColumnDef="spendable_msatoshi">
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before"> Spendable Satoshi </th>
<td mat-cell *matCellDef="let channel"><span fxLayoutAlign="end center">
{{channel?.spendable_msatoshi | number}} </span></td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef class="pr-3"><span fxLayoutAlign="end center">Actions</span></th>
<td mat-cell *matCellDef="let channel"><span fxLayoutAlign="end center">
<button mat-stroked-button color="primary" type="button" tabindex="4" (click)="onChannelClick(channel, $event)">View Info</button>
</span></td>
</ng-container>
<ng-container matColumnDef="no_peer">
<td mat-footer-cell *matFooterCellDef colspan="4">
<p *ngIf="numPeers<1">No peers connected. Add a peer in order to open a channel.</p>
<p *ngIf="numPeers>0 && (!channels.data || channels.data.length<1)">No channels available.</p>
</td>
</ng-container>
<tr mat-footer-row *matFooterRowDef="['no_peer']" [ngClass]="{'display-none': numPeers>0 && channels.data && channels.data.length>0}"></tr>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: flgSticky;"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</div>
<mat-paginator [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true" class="mb-4"></mat-paginator>
</div>

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CLChannelPendingTableComponent } from './channel-pending-table.component';
describe('CLChannelPendingTableComponent', () => {
let component: CLChannelPendingTableComponent;
let fixture: ComponentFixture<CLChannelPendingTableComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CLChannelPendingTableComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CLChannelPendingTableComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,140 @@
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { Subject } from 'rxjs';
import { take, takeUntil } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { MatTableDataSource, MatSort, MatPaginator, MatPaginatorIntl } from '@angular/material';
import { ChannelCL, GetInfoCL } from '../../../../../shared/models/clModels';
import { PAGE_SIZE, PAGE_SIZE_OPTIONS, getPaginatorLabel, AlertTypeEnum, DataTypeEnum, ScreenSizeEnum, FEE_RATE_TYPES } from '../../../../../shared/services/consts-enums-functions';
import { LoggerService } from '../../../../../shared/services/logger.service';
import { CommonService } from '../../../../../shared/services/common.service';
import { CLEffects } from '../../../../store/cl.effects';
import { RTLEffects } from '../../../../../store/rtl.effects';
import * as RTLActions from '../../../../../store/rtl.actions';
import * as fromRTLReducer from '../../../../../store/rtl.reducers';
@Component({
selector: 'rtl-cl-channel-pending-table',
templateUrl: './channel-pending-table.component.html',
styleUrls: ['./channel-pending-table.component.scss'],
providers: [
{ provide: MatPaginatorIntl, useValue: getPaginatorLabel('Channels') }
]
})
export class CLChannelPendingTableComponent implements OnInit, OnDestroy {
@ViewChild(MatSort, { static: true }) sort: MatSort;
@ViewChild(MatPaginator, {static: true}) paginator: MatPaginator;
public totalBalance = 0;
public displayedColumns = [];
public channels: any;
public myChanPolicy: any = {};
public information: GetInfoCL = {};
public numPeers = -1;
public feeRateTypes = FEE_RATE_TYPES;
public flgLoading: Array<Boolean | 'error'> = [true];
public selectedFilter = '';
public selFilter = '';
public flgSticky = false;
public pageSize = PAGE_SIZE;
public pageSizeOptions = PAGE_SIZE_OPTIONS;
public screenSize = '';
public screenSizeEnum = ScreenSizeEnum;
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject(), new Subject(), new Subject()];
constructor(private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>, private rtlEffects: RTLEffects, private clEffects: CLEffects, private commonService: CommonService) {
this.screenSize = this.commonService.getScreenSize();
if(this.screenSize === ScreenSizeEnum.XS) {
this.flgSticky = false;
this.displayedColumns = ['short_channel_id', 'state', 'msatoshi_total', 'actions'];
} else if(this.screenSize === ScreenSizeEnum.SM) {
this.flgSticky = false;
this.displayedColumns = ['short_channel_id', 'alias', 'state', 'msatoshi_total', 'actions'];
} else if(this.screenSize === ScreenSizeEnum.MD) {
this.flgSticky = false;
this.displayedColumns = ['short_channel_id', 'alias', 'state', 'msatoshi_total', 'actions'];
} else {
this.flgSticky = true;
this.displayedColumns = ['short_channel_id', 'alias', 'state', 'msatoshi_total', 'actions'];
}
}
ngOnInit() {
this.store.select('cl')
.pipe(takeUntil(this.unSubs[0]))
.subscribe((rtlStore) => {
rtlStore.effectErrorsCl.forEach(effectsErr => {
if (effectsErr.action === 'FetchChannelsCL') {
this.flgLoading[0] = 'error';
}
});
this.information = rtlStore.information;
this.numPeers = (rtlStore.peers && rtlStore.peers.length) ? rtlStore.peers.length : 0;
this.totalBalance = rtlStore.balance.totalBalance;
if (rtlStore.allChannels) {
this.loadChannelsTable(rtlStore.allChannels.filter(channel => channel.state !== 'CHANNELD_NORMAL'));
}
if (this.flgLoading[0] !== 'error') {
this.flgLoading[0] = (rtlStore.allChannels) ? false : true;
}
this.logger.info(rtlStore);
});
}
applyFilter() {
this.selectedFilter = this.selFilter;
this.channels.filter = this.selFilter;
}
onChannelClick(selChannel: ChannelCL, event: any) {
const reorderedChannel = [
[{key: 'alias', value: selChannel.alias, title: 'Peer Alias', width: 40},
{key: 'connected', value: selChannel.connected, title: 'Connected', width: 30, type: DataTypeEnum.BOOLEAN},
{key: 'private', value: selChannel.private, title: 'Private', width: 30, type: DataTypeEnum.BOOLEAN}],
[{key: 'id', value: selChannel.id, title: 'Peer Public Key', width: 100}],
[{key: 'short_channel_id', value: selChannel.short_channel_id, title: 'Short Channel ID', width: 100}],
[{key: 'channel_id', value: selChannel.channel_id, title: 'Channel ID', width: 50},
{key: 'state', value: selChannel.state, title: 'State', width: 50, type: DataTypeEnum.NUMBER}],
[{key: 'our_channel_reserve_satoshis', value: selChannel.our_channel_reserve_satoshis, title: 'Our Channel Reserve Satoshis', width: 50, type: DataTypeEnum.NUMBER},
{key: 'their_channel_reserve_satoshis', value: selChannel.their_channel_reserve_satoshis, title: 'Their Channel Reserve Satoshis', width: 50, type: DataTypeEnum.NUMBER}],
[{key: 'msatoshi_to_us', value: selChannel.msatoshi_to_us, title: 'mSatoshi to Us', width: 50, type: DataTypeEnum.NUMBER},
{key: 'spendable_msatoshi', value: selChannel.spendable_msatoshi, title: 'Spendable mSatoshi', width: 50, type: DataTypeEnum.NUMBER}],
[{key: 'msatoshi_total', value: selChannel.msatoshi_total, title: 'Total mSatoshi', width: 50, type: DataTypeEnum.NUMBER},
{key: 'funding_txid', value: selChannel.funding_txid, title: 'Funding Transaction Id', width: 50, type: DataTypeEnum.NUMBER}]
];
this.store.dispatch(new RTLActions.OpenAlert({ data: {
type: AlertTypeEnum.INFORMATION,
alertTitle: 'Channel Information',
showCopyName: 'Short Channel ID',
showCopyField: selChannel.short_channel_id,
message: reorderedChannel
}}));
}
loadChannelsTable(mychannels) {
mychannels.sort(function(a, b) {
return (a.active === b.active) ? 0 : ((b.active) ? 1 : -1);
});
this.channels = new MatTableDataSource<ChannelCL>([...mychannels]);
this.channels.filterPredicate = (channel: ChannelCL, fltr: string) => {
const newChannel = ((channel.connected) ? 'connected' : 'disconnected') + (channel.channel_id ? channel.channel_id : '') +
(channel.short_channel_id ? channel.short_channel_id : '') + (channel.id ? channel.id : '') + (channel.alias ? channel.alias : '') +
(channel.private ? 'private' : 'public') + (channel.state ? channel.state.toLowerCase() : '') +
(channel.funding_txid ? channel.funding_txid : '') + (channel.msatoshi_to_us ? channel.msatoshi_to_us : '') +
(channel.msatoshi_total ? channel.msatoshi_total : '') + (channel.their_channel_reserve_satoshis ? channel.their_channel_reserve_satoshis : '') +
(channel.our_channel_reserve_satoshis ? channel.our_channel_reserve_satoshis : '') + (channel.spendable_msatoshi ? channel.spendable_msatoshi : '');
return newChannel.includes(fltr.toLowerCase());
};
this.channels.sort = this.sort;
this.channels.paginator = this.paginator;
this.logger.info(this.channels);
}
ngOnDestroy() {
this.unSubs.forEach(completeSub => {
completeSub.next();
completeSub.complete();
});
}
}

@ -6,5 +6,11 @@
</ng-template>
<rtl-cl-channel-open-table></rtl-cl-channel-open-table>
</mat-tab>
<mat-tab>
<ng-template mat-tab-label>
<span matBadge="{{pendingChannels}}" matBadgeOverlap="false" class="tab-badge">Pending</span>
</ng-template>
<rtl-cl-channel-pending-table></rtl-cl-channel-pending-table>
</mat-tab>
</mat-tab-group>
</div>

@ -13,6 +13,7 @@ import * as fromRTLReducer from '../../../../store/rtl.reducers';
})
export class CLChannelsTablesComponent implements OnInit, OnDestroy {
public openChannels = 0;
public pendingChannels = 0;
private unSubs: Array<Subject<void>> = [new Subject()];
constructor(private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>) {}
@ -21,7 +22,20 @@ export class CLChannelsTablesComponent implements OnInit, OnDestroy {
this.store.select('cl')
.pipe(takeUntil(this.unSubs[0]))
.subscribe((rtlStore) => {
this.openChannels = (rtlStore.allChannels && rtlStore.allChannels.length) ? rtlStore.allChannels.length : 0;
if (rtlStore.allChannels && rtlStore.allChannels.length) {
this.openChannels = 0;
this.pendingChannels = 0;
rtlStore.allChannels.forEach(channel => {
if(channel.state === 'CHANNELD_NORMAL') {
this.openChannels++;
} else {
this.pendingChannels++;
}
});
} else {
this.openChannels = 0;
this.pendingChannels = 0;
}
this.logger.info(rtlStore);
});
}

@ -1,15 +1,17 @@
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="padding-gap">
<form [fxLayout]="showDetails ? 'column' : 'row wrap'" fxLayoutAlign.gt-sm="space-between center" fxLayoutAlign="space-between stretch" fxLayout.gt-sm="row wrap" fxFlex="100" #addInvoiceForm="ngForm">
<mat-form-field fxFlex="49" fxLayoutAlign="space-between stretch">
<input matInput [(ngModel)]="label" placeholder="Label" tabindex="1" name="label">
<mat-form-field [fxFlex]="showDetails ? '49' : '100'" fxLayoutAlign="space-between stretch">
<input matInput [(ngModel)]="label" placeholder="Label" tabindex="1" name="label" required>
<mat-error *ngIf="!label">Label is required.</mat-error>
</mat-form-field>
<mat-form-field fxFlex="49" fxLayoutAlign="space-between stretch">
<mat-form-field *ngIf="showDetails" fxFlex="49" fxLayoutAlign="space-between stretch">
<input matInput [(ngModel)]="description" placeholder="Description" tabindex="2" name="description">
</mat-form-field>
<mat-form-field [fxFlex]="showDetails ? '40' : '100'" fxLayoutAlign="start end">
<input matInput [(ngModel)]="invoiceValue" (keyup)="onInvoiceValueChange()" placeholder="Amount" type="number" step="100" min="1" tabindex="3" name="invoiceValue">
<input matInput [(ngModel)]="invoiceValue" (keyup)="onInvoiceValueChange()" placeholder="Amount" type="number" step="100" min="1" tabindex="3" name="invoiceValue" required>
<span matSuffix> {{information?.smaller_currency_unit}} </span>
<mat-hint>{{invoiceValueHint}}</mat-hint>
<mat-hint>{{invoiceValueHint}}</mat-hint>
<mat-error *ngIf="!invoiceValue">Amount is required.</mat-error>
</mat-form-field>
<mat-form-field fxFlex="15" fxLayoutAlign="start end" *ngIf="showDetails" [ngClass]="{'mr-2': screenSize === screenSizeEnum.LG}">
<input matInput [(ngModel)]="expiry" placeholder="Expiry" type="number" step="{{selTimeUnit === timeUnitEnum.SECS ? 300 : selTimeUnit === timeUnitEnum.MINS ? 10 : selTimeUnit === timeUnitEnum.HOURS ? 2 : 1}}" min="1" tabindex="4" name="expiry">
@ -24,8 +26,9 @@
<mat-slide-toggle color="primary" [(ngModel)]="private" matTooltip="Include routing hints for private channels" [matTooltipPosition]="'above'" name="private">Private Routing Hints</mat-slide-toggle>
</div>
<div fxLayout="row" fxFlex="100" fxFlex.gt-sm="30" fxLayoutAlign="space-between start" *ngIf="showDetails" class="mt-2">
<button fxFlex="48" fxLayoutAlign="center center" mat-stroked-button color="primary" tabindex="7" type="reset" (click)="resetData()">Clear Field</button>
<button fxFlex="48" fxLayoutAlign="center center" mat-raised-button color="primary" (click)="onAddInvoice(addInvoiceForm)" tabindex="8">Create Invoice</button>
<button fxFlex="32" fxLayoutAlign="center center" mat-stroked-button color="warn" tabindex="11" type="button" (click)="onDeleteExpiredInvoices()">Delete Expired</button>
<button fxFlex="32" fxLayoutAlign="center center" mat-stroked-button color="primary" tabindex="7" type="reset" (click)="resetData()">Clear Field</button>
<button fxFlex="32" fxLayoutAlign="center center" mat-raised-button color="primary" (click)="onAddInvoice(addInvoiceForm)" tabindex="8">Create Invoice</button>
</div>
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between stretch" *ngIf="!showDetails" class="mt-1">
<button fxFlex="48" fxLayoutAlign="center center" mat-stroked-button color="primary" tabindex="9" type="reset" (click)="resetData()">Clear Field</button>

@ -14,6 +14,7 @@ import { CommonService } from '../../../shared/services/common.service';
import { InvoiceInformationComponent } from '../../../shared/components/data-modal/invoice-information/invoice-information.component';
import { newlyAddedRowAnimation } from '../../../shared/animation/row-animation';
import { RTLEffects } from '../../../store/rtl.effects';
import * as RTLActions from '../../../store/rtl.actions';
import * as fromRTLReducer from '../../../store/rtl.reducers';
@ -59,7 +60,7 @@ export class CLLightningInvoicesComponent implements OnInit, OnDestroy {
public screenSizeEnum = ScreenSizeEnum;
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject(), new Subject()];
constructor(private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>, private decimalPipe: DecimalPipe, private commonService: CommonService) {
constructor(private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>, private decimalPipe: DecimalPipe, private commonService: CommonService, private rtlEffects: RTLEffects) {
this.screenSize = this.commonService.getScreenSize();
if(this.screenSize === ScreenSizeEnum.XS) {
this.flgSticky = false;
@ -77,11 +78,12 @@ export class CLLightningInvoicesComponent implements OnInit, OnDestroy {
}
ngOnInit() {
this.store.dispatch(new RTLActions.FetchInvoicesCL({num_max_invoices: 100, index_offset: 0, reversed: false}));
this.store.select('cl')
.pipe(takeUntil(this.unSubs[0]))
.subscribe((rtlStore) => {
rtlStore.effectErrorsCl.forEach(effectsErr => {
if (effectsErr.action === 'FetchInvoices') {
if (effectsErr.action === 'FetchInvoicesCL') {
this.flgLoading[0] = 'error';
}
});
@ -100,6 +102,7 @@ export class CLLightningInvoicesComponent implements OnInit, OnDestroy {
}
onAddInvoice(form: any) {
if(!this.label || !this.invoiceValue) { return true; }
let expiryInSecs = (this.expiry ? this.expiry : 3600);
if (this.selTimeUnit !== TimeUnitEnum.SECS) {
expiryInSecs = this.commonService.convertTime(this.expiry, this.selTimeUnit, TimeUnitEnum.SECS);
@ -109,11 +112,25 @@ export class CLLightningInvoicesComponent implements OnInit, OnDestroy {
this.newlyAddedInvoiceValue = this.invoiceValue;
this.store.dispatch(new RTLActions.OpenSpinner('Adding Invoice...'));
this.store.dispatch(new RTLActions.SaveNewInvoiceCL({
label: this.label, amount: this.invoiceValue, description: this.description, expiry: (this.expiry ? this.expiry : 3600), private: this.private
label: this.label, amount: this.invoiceValue, description: this.description, expiry: expiryInSecs, private: this.private
}));
this.resetData();
}
onDeleteExpiredInvoices() {
this.store.dispatch(new RTLActions.OpenConfirmation({
width: '70%', data: { type: 'CONFIRM', titleMessage: 'Delete Expired Invoices', noBtnText: 'Cancel', yesBtnText: 'Delete Invoices'
}}));
this.rtlEffects.closeConfirm
.pipe(takeUntil(this.unSubs[1]))
.subscribe(confirmRes => {
if (confirmRes) {
this.store.dispatch(new RTLActions.OpenSpinner('Deleting Invoices...'));
this.store.dispatch(new RTLActions.DeleteExpiredInvoiceCL());
}
});
}
onInvoiceClick(selInvoice: InvoiceCL, event: any) {
let reCreatedInvoice = {
payment_request: selInvoice.bolt11,
@ -122,12 +139,17 @@ export class CLLightningInvoicesComponent implements OnInit, OnDestroy {
settle_date_str: selInvoice.paid_at_str,
expiry: selInvoice.expires_at_str
};
// creation_date_str?: string;
// r_hash?: string;
// r_preimage?: string;
// cltv_expiry?: string;
// private?: boolean;
// msatoshi
// label
// expires_at
// paid_at
// bolt11
// Advanced:
// payment_hash
// description
// status
// msatoshi_received
this.store.dispatch(new RTLActions.OpenAlert({ data: {
invoice: reCreatedInvoice,
newlyAdded: false,

@ -29,7 +29,8 @@ export const LndRoutes: Routes = [
{ path: 'lookups', component: LookupsComponent, canActivate: [LNDUnlockedGuard] },
{ path: 'network', component: NetworkInfoComponent, canActivate: [LNDUnlockedGuard] },
{ path: 'loop', component: LoopComponent, canActivate: [LNDUnlockedGuard] },
{ path: '**', component: NotFoundComponent }
{ path: '**', component: NotFoundComponent },
{ path: 'rates', redirectTo: 'network' }
]}
];

@ -75,7 +75,7 @@ export class LookupsComponent implements OnInit, OnDestroy {
}
onSelectChange(event: any) {
this.form.resetForm();
this.resetData();
this.selectedFieldId = event.value;
}

@ -6,7 +6,7 @@
</mat-option>
</mat-select>
<mat-divider class="w-100"></mat-divider>
<mat-tree [dataSource]="navMenus" [treeControl]="treeControlNested" *ngIf="settings.menuType === 'regular'">
<mat-tree #tree [dataSource]="navMenus" [treeControl]="treeControlNested" *ngIf="settings.menuType === 'regular'">
<mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle routerLinkActive="active-link" routerLink="{{node.link}}">
<div (click)="onChildNavClicked(node)">
<div fxLayout="row" fxFlex="100" fxLayoutAlign="start center">

@ -1,4 +1,4 @@
import { Component, OnInit, OnDestroy, Output, EventEmitter } from '@angular/core';
import { Component, OnInit, OnDestroy, Output, EventEmitter, ViewChild } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { Store } from '@ngrx/store';
@ -6,7 +6,7 @@ import { Actions } from '@ngrx/effects';
import { environment } from '../../../../../environments/environment';
import { NestedTreeControl } from '@angular/cdk/tree';
import { MatTreeNestedDataSource } from '@angular/material/tree';
import { MatTreeNestedDataSource, MatTree } from '@angular/material/tree';
import { faEject, faEye } from '@fortawesome/free-solid-svg-icons';
import { RTLConfiguration, LightningNode, Settings, GetInfoRoot } from '../../../models/RTLconfig';
@ -26,6 +26,7 @@ import { AlertTypeEnum, UserPersonaEnum } from '../../../services/consts-enums-f
styleUrls: ['./side-navigation.component.scss']
})
export class SideNavigationComponent implements OnInit, OnDestroy {
@ViewChild(MatTree, { static: true }) tree: any;
@Output() ChildNavClicked = new EventEmitter<any>();
faEject = faEject;
faEye = faEye;
@ -90,11 +91,7 @@ export class SideNavigationComponent implements OnInit, OnDestroy {
if (window.innerWidth <= 414) {
this.smallScreen = true;
}
if(this.selNode && this.selNode.lnImplementation && this.selNode.lnImplementation.toUpperCase() === 'CLT') {
this.navMenus.data = MENU_DATA.CLChildren.filter(navMenuData => navMenuData.userPersona === UserPersonaEnum.ALL || navMenuData.userPersona === this.settings.userPersona );
} else {
this.navMenus.data = MENU_DATA.LNDChildren.filter(navMenuData => navMenuData.userPersona === UserPersonaEnum.ALL || navMenuData.userPersona === this.settings.userPersona );
}
this.filterSideMenuNodes();
this.logger.info(rtlStore);
});
this.sessionService.watchSession()
@ -133,6 +130,24 @@ export class SideNavigationComponent implements OnInit, OnDestroy {
this.ChildNavClicked.emit(node);
}
filterSideMenuNodes() {
if(this.selNode && this.selNode.lnImplementation && this.selNode.lnImplementation.toUpperCase() === 'CLT') {
this.navMenus.data = MENU_DATA.CLChildren.filter(navMenuData => {
if(navMenuData.children && navMenuData.children.length) {
navMenuData.children = navMenuData.children.filter(navMenuChild => navMenuChild.userPersona === UserPersonaEnum.ALL || navMenuChild.userPersona === this.settings.userPersona);
}
return navMenuData.userPersona === UserPersonaEnum.ALL || navMenuData.userPersona === this.settings.userPersona;
});
} else {
this.navMenus.data = MENU_DATA.LNDChildren.filter(navMenuData => {
if(navMenuData.children && navMenuData.children.length) {
navMenuData.children = navMenuData.children.filter(navMenuChild => navMenuChild.userPersona === UserPersonaEnum.ALL || navMenuChild.userPersona === this.settings.userPersona);
}
return navMenuData.userPersona === UserPersonaEnum.ALL || navMenuData.userPersona === this.settings.userPersona;
});
}
}
onShowData(node: MenuChildNode) {
this.store.dispatch(new RTLActions.ShowPubkey());
this.ChildNavClicked.emit('showData');
@ -143,6 +158,9 @@ export class SideNavigationComponent implements OnInit, OnDestroy {
this.store.dispatch(new RTLActions.OpenSpinner('Updating Selected Node...'));
this.store.dispatch(new RTLActions.SetSelelectedNode({ lnNode: selNodeValue, isInitialSetup: false }));
this.ChildNavClicked.emit('selectNode');
if(this.tree) {
// this.tree.renderNodeChanges(this.navMenus.data);
}
}
ngOnDestroy() {

@ -1,4 +1,4 @@
import { faTachometerAlt, faLink, faBolt, faExchangeAlt, faUsers, faMapSigns, faQuestion, faSearch, faTools, faProjectDiagram, faDownload, faServer } from '@fortawesome/free-solid-svg-icons';
import { faTachometerAlt, faLink, faBolt, faExchangeAlt, faUsers, faMapSigns, faQuestion, faSearch, faTools, faProjectDiagram, faDownload, faServer, faPercentage } from '@fortawesome/free-solid-svg-icons';
import { UserPersonaEnum } from '../services/consts-enums-functions';
export const MENU_DATA: MenuRootNode = {
@ -24,8 +24,10 @@ export const MENU_DATA: MenuRootNode = {
{id: 31, parentId: 3, name: 'Peers/Channels', iconType: 'FA', icon: faUsers, link: '/cl/peerschannels', userPersona: UserPersonaEnum.ALL},
{id: 32, parentId: 3, name: 'Transactions', iconType: 'FA', icon: faExchangeAlt, link: '/cl/transactions', userPersona: UserPersonaEnum.ALL},
{id: 34, parentId: 3, name: 'Routing', iconType: 'FA', icon: faMapSigns, link: '/cl/routing', userPersona: UserPersonaEnum.ALL},
{id: 35, parentId: 3, name: 'Graph Lookup', iconType: 'FA', icon: faSearch, link: '/cl/lookups', userPersona: UserPersonaEnum.ALL}
]},
{id: 35, parentId: 3, name: 'Graph Lookup', iconType: 'FA', icon: faSearch, link: '/cl/lookups', userPersona: UserPersonaEnum.ALL},
{id: 36, parentId: 3, name: 'Rates', iconType: 'FA', icon: faPercentage, link: '/cl/rates', userPersona: UserPersonaEnum.OPERATOR},
{id: 37, parentId: 3, name: 'Node/Rates', iconType: 'FA', icon: faServer, link: '/cl/rates', userPersona: UserPersonaEnum.MERCHANT}
]},
{id: 7, parentId: 0, name: 'Settings', iconType: 'FA', icon: faTools, link: '/settings', userPersona: UserPersonaEnum.ALL},
{id: 8, parentId: 0, name: 'Help', iconType: 'FA', icon: faQuestion, link: '/help', userPersona: UserPersonaEnum.ALL}
]

@ -128,6 +128,9 @@
.color-primary {
color: $primary-darker !important;
}
.mat-expansion-panel {
}
}
&.day {
@ -205,13 +208,6 @@
.modal-qr-code-container {
background: $foreground-divider;
}
.mat-expansion-panel.flat-expansion-panel {
box-shadow: none;
padding: 0;
border: 1px solid $foreground-divider;
border-radius: 4px;
background: none;
}
.mat-tab-label, .mat-tab-link {
color: $foreground-secondary-text;
}
@ -234,8 +230,18 @@
.color-primary {
color: $primary-color !important;
}
.mat-expansion-panel.flat-expansion-panel {
border: 1px solid $foreground-divider;
}
}
.mat-expansion-panel.flat-expansion-panel {
box-shadow: none;
padding: 0;
border-radius: 4px;
background: none;
}
.mat-progress-bar-buffer {
background-color: mat-color($primary, 100);
}

@ -402,6 +402,10 @@ body {
padding-left: 1rem !important;
}
.pl-15px {
padding-left: 1.5rem !important;
}
.pr-0 {
padding-right: 0 !important;
}

Loading…
Cancel
Save