User Persona and Merchant Dashboard

User Persona and Merchant Dashboard
pull/260/head
Shahana Farooqui 4 years ago
parent 2f5817cb36
commit 79d04a5d7a

@ -93,6 +93,7 @@ rtlCookiePath=C:\RTL\cookies\auth.cookie
logoutRedirectLink=/login
[Settings]
userPersona=Operator
flgSidenavOpened=true
flgSidenavPinned=true
menu=vertical

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.f18574731fbd952bd733.css"></head>
<link rel="stylesheet" href="styles.3b647f02f87dbde63ee9.css"></head>
<body>
<rtl-app></rtl-app>
<script src="runtime.c34eacb3a1d0815d5d31.js"></script><script src="polyfills-es5.2e75d230d7f9c760eb2f.js" nomodule></script><script src="polyfills.1ebb102854b0ec478c1b.js"></script><script src="main.9b61a11b9d60ea3819ec.js"></script></body>
<script src="runtime.af78130362f8d52a18b0.js"></script><script src="polyfills-es5.2e75d230d7f9c760eb2f.js" nomodule></script><script src="polyfills.1ebb102854b0ec478c1b.js"></script><script src="main.1a920fe27b5e532dac46.js"></script></body>
</html>

File diff suppressed because one or more lines are too long

@ -1 +1 @@
!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++)o[a=i[p]]&&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:"9e71a210a8bdc5ba9df9",6:"a0c4b63d37d2817e57a8",7:"f304a8c9801c62bf8967"}[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()}([]);
!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++)o[a=i[p]]&&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:"9e71a210a8bdc5ba9df9",6:"af311c76cbdf1165a591",7:"f304a8c9801c62bf8967"}[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()}([]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -59,6 +59,7 @@ connect.setDefaultConfig = () => {
configPath: configPath,
},
Settings: {
userPersona: 'Operator',
flgSidenavOpened: true,
flgSidenavPinned: true,
menu: "vertical",

@ -95,6 +95,7 @@ exports.updateUISettings = (req, res, next) => {
var config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8'));
config.nodes.find(node => {
if(node.index == common.selectedNode.index) {
node.Settings.userPersona = req.body.updatedSettings.userPersona;
node.Settings.flgSidenavOpened = req.body.updatedSettings.flgSidenavOpened;
node.Settings.flgSidenavPinned = req.body.updatedSettings.flgSidenavPinned;
node.Settings.menu = req.body.updatedSettings.menu;
@ -122,6 +123,7 @@ exports.updateUISettings = (req, res, next) => {
RTLConfFile = common.rtl_conf_file_path + '/RTL.conf';
var config = ini.parse(fs.readFileSync(RTLConfFile, 'utf-8'));
const settingsTemp = config.Settings;
settingsTemp.userPersona = req.body.updatedSettings.userPersona;
settingsTemp.flgSidenavOpened = req.body.updatedSettings.flgSidenavOpened;
settingsTemp.flgSidenavPinned = req.body.updatedSettings.flgSidenavPinned;
settingsTemp.menu = req.body.updatedSettings.menu;

@ -14,6 +14,8 @@ rtlPass=<>
[Settings]
;Set by RTL
userPersona=Operator
;Set by RTL
flgSidenavOpened=true
;Set by RTL
flgSidenavPinned=true

@ -75,6 +75,7 @@ Ensure that the follow values are correct per your config:
"configPath": "<Optional - Config file path for c-lightning>"
},
"Settings": {
"userPersona": "Operator",
"flgSidenavOpened": false,
"flgSidenavPinned": true,
"menu": "vertical",

@ -65,10 +65,10 @@ const loopMonitor = io.of('/loopMonitor').on('connection', (socket) => {
interval = setInterval(() => {
socket.emit('message', { message: 'Message ' + i });
i++;
}, 2000);
}, 15000);
});
socket.on('end', function() {
// call = null;
call = null;
clearInterval(interval);
console.log('Socket stopped subscription');
});

@ -17,6 +17,7 @@
"lndConfigPath": "<Optional:Path of the lnd.conf if present locally or empty>"
},
"Settings": {
"userPersona": "Operator",
"flgSidenavOpened": "true",
"flgSidenavPinned": "true",
"menu": "vertical",
@ -39,6 +40,7 @@
"macaroonPath": "<Complete path of the folder containing admin.macaroon for the node # 2>"
},
"Settings": {
"userPersona": "Merchant",
"flgSidenavOpened": "true",
"flgSidenavPinned": "true",
"menu": "vertical",

@ -5,6 +5,7 @@ lndConfigPath=
rtlPass=
[Settings]
userPersona=Operator
flgSidenavOpened=true
flgSidenavPinned=true
menu=vertical

@ -2,6 +2,7 @@ import { SelNodeChild } from '../../shared/models/RTLconfig';
import { GetInfoCL, FeesCL, BalanceCL, LocalRemoteBalanceCL, AddressTypeCL, PeerCL, PaymentCL, ChannelCL, FeeRatesCL, ForwardingHistoryResCL, ListInvoicesCL, FeeRateTypeCL } from '../../shared/models/clModels';
import { ErrorPayload } from '../../shared/models/errorPayload';
import * as RTLActions from '../../store/rtl.actions';
import { UserPersonaEnum } from '../../shared/services/consts-enums-functions';
export interface CLState {
effectErrorsCl: ErrorPayload[];
@ -24,7 +25,7 @@ export interface CLState {
export const initCLState: CLState = {
effectErrorsCl: [],
nodeSettings: { currencyUnit: 'USD', channelBackupPath: '', satsToBTC: false, currencyUnits: [] },
nodeSettings: { userPersona: UserPersonaEnum.OPERATOR, selCurrencyUnit: 'USD', channelBackupPath: '', satsToBTC: false, currencyUnits: [] },
information: {},
fees: {},
feeRatesPerKB: {},

@ -53,12 +53,11 @@
<div fxFlex="34" fxLayoutAlign="start start"></div>
</div>
<div fxLayout="column" fxFlex="60" fxLayoutAlign="start start" *ngIf="flgInfoUpdate">
<ngx-charts-pie-chart class="balances-info-pie-chart" style="margin-top: -2.5rem;"
<ngx-charts-pie-chart class="balances-info-pie-chart" style="margin-top: -4rem;"
[explodeSlices]="true"
[legend]="true"
[legendTitle]="''"
[legendPosition]="'below'"
[view]="[190, 190]"
[results]="totalBalances">
</ngx-charts-pie-chart>
</div>

@ -15,7 +15,7 @@
<mat-hint fxFlex="50" fxLayoutAlign="start center" class="font-size-80"><strong class="font-weight-900">Local:</strong>{{channel.local_balance || 0 | number}} Sats</mat-hint>
<mat-hint fxFlex="50" fxLayoutAlign="end center" class="font-size-80"><strong class="font-weight-900">Remote:</strong>{{channel.remote_balance || 0 | number}} Sats</mat-hint>
</div>
<mat-progress-bar class="dashboard-progress-bar" mode="determinate" value="{{(channel.balancedness)}}"></mat-progress-bar>
<mat-progress-bar class="dashboard-progress-bar" mode="determinate" value="{{channel.balancedness}}"></mat-progress-bar>
</div>
</div>
</div>

@ -9,17 +9,9 @@ import { Channel } from '../../../shared/models/lndModels';
export class ChannelCapacityInfoComponent implements OnChanges {
@Input() channelBalances: {localBalance: number, remoteBalance: number};
@Input() allChannels: Channel[];
allChannelBalances = [];
constructor() {}
ngOnChanges() {
// this.allChannelBalances = [];
// this.allChannels.forEach((channel, idx) => {
// if(idx < 20) {
// this.allChannelBalances.push({"name": ((idx + 1) + ' ' + channel.remote_alias), "series": [{ "name": "Local Balance", "value": -channel.local_balance}, {"name": "Remote Balance","value": +channel.remote_balance}]});
// }
// });
}
ngOnChanges() {}
}

@ -1,10 +1,10 @@
<div fxLayout="column" fxLayoutAlign="space-between start" fxFlex="100">
<div fxLayout="column" fxFlex="8" fxLayoutAlign="end start" class="w-100">
<h2>{{totalLiquidity | number}} Sats</h2>
<h4>Max Transaction Amount: {{maxTransactionAmount | number}} Sats</h4>
<h4>Max Transaction Amount: {{(maxAmount <= 4200000 ? maxAmount : 4200000) | number}} Sats</h4>
<mat-divider class="w-100 dashboard-divider mt-2"></mat-divider>
</div>
<div fxLayout="column" fxFlex="90" fxFlex.lt-md="85" fxLayoutAlign="start start" class="channels-capacity-scroll w-100" perfectScrollbar>
<div fxLayout="column" fxFlex="90" fxFlex.lt-md="85" fxLayoutAlign="start start" class="channels-capacity-scroll w-100 mt-2" perfectScrollbar>
<div fxLayout="column" fxFlex="100" class="w-100" *ngIf="allChannels && allChannels.length > 0; else noChannelBlock">
<div *ngFor="let channel of allChannels" class="mt-2">
<h4>{{(channel.remote_alias || channel.remote_pubkey) | slice:0:24}}{{(channel.remote_alias || channel.remote_pubkey).length > 25 ? '...' : ''}}</h4>
@ -12,8 +12,8 @@
<mat-hint *ngIf="direction === 'In'" fxFlex="100" fxLayoutAlign="start center" class="font-size-80"><strong class="font-weight-900">{{channel.remote_balance || 0 | number}} Sats</strong></mat-hint>
<mat-hint *ngIf="direction === 'Out'" fxFlex="100" fxLayoutAlign="start center" class="font-size-80"><strong class="font-weight-900">{{channel.local_balance || 0 | number}} Sats</strong></mat-hint>
</div>
<mat-progress-bar *ngIf="direction === 'In'" class="dashboard-progress-bar" mode="determinate" value="{{(+channel.remote_balance || 0)/(+channel.remote_balance + +channel.local_balance) * 100}}"></mat-progress-bar>
<mat-progress-bar *ngIf="direction === 'Out'" class="dashboard-progress-bar" mode="determinate" value="{{(+channel.local_balance || 0)/(+channel.remote_balance + +channel.local_balance) * 100}}"></mat-progress-bar>
<mat-progress-bar *ngIf="direction === 'In'" class="dashboard-progress-bar" mode="determinate" value="{{(totalLiquidity > 0) ? ((+channel.remote_balance || 0)/(totalLiquidity) * 100) : 0}}"></mat-progress-bar>
<mat-progress-bar *ngIf="direction === 'Out'" class="dashboard-progress-bar" mode="determinate" value="{{(totalLiquidity > 0) ? ((+channel.local_balance || 0)/(totalLiquidity) * 100) : 0}}"></mat-progress-bar>
</div>
</div>
</div>

@ -1,4 +1,5 @@
import { Component, OnChanges, Input } from '@angular/core';
import { Channel } from '../../../shared/models/lndModels';
@Component({
@ -10,12 +11,18 @@ export class ChannelLiquidityInfoComponent implements OnChanges {
@Input() direction: string;
@Input() totalLiquidity: number;
@Input() allChannels: Channel[];
public maxTransactionAmount = 0;
public maxAmount = 0;
constructor() {}
ngOnChanges() {
this.maxTransactionAmount = this.allChannels && this.allChannels.length > 0 ? (this.direction === 'In' ? this.allChannels[0].remote_balance : this.allChannels[0].local_balance) : 0;
if (this.allChannels && this.allChannels.length > 0) {
if(this.direction === 'In') {
this.maxAmount = this.allChannels[0].remote_balance;
} else {
this.maxAmount = this.allChannels[0].local_balance;
}
}
}
}

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

@ -1,15 +0,0 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'rtl-dashboard-transactions',
templateUrl: './dashboard-transactions.component.html',
styleUrls: ['./dashboard-transactions.component.scss']
})
export class DashboardTransactionsComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}

@ -1,4 +1,4 @@
<div fxLayout="column" *ngIf="userPersona === userPersonaEnum.OPERATOR; else merchantDashboard">
<div fxLayout="column" *ngIf="selNode.userPersona === userPersonaEnum.OPERATOR; else merchantDashboard">
<div fxLayout="row" fxLayoutAlign="start end" class="padding-gap-x page-title-container mb-0">
<fa-icon [icon]="faSmile" class="page-title-img mr-1"></fa-icon>
<span class="page-title">Welcome! Your node is up and running.</span>
@ -15,7 +15,7 @@
<div [ngSwitch]="card.id" fxLayout="column" fxFlex="100">
<rtl-node-info fxFlex="100" *ngSwitchCase="'node'" [information]="information" [ngClass]="{'error-border': flgLoading[0]==='error'}"></rtl-node-info>
<rtl-balances-info fxFlex="100" *ngSwitchCase="'balance'" [flgInfoUpdate]="flgChildInfoUpdated" [balances]="balances" [ngClass]="{'error-border': flgLoading[2]==='error' || flgLoading[5]==='error'}"></rtl-balances-info>
<rtl-channel-capacity-info fxFlex="100" *ngSwitchCase="'capacity'" [channelBalances]="channelBalances" [allChannels]="allChannels" [ngClass]="{'error-border': flgLoading[5]==='error'}"></rtl-channel-capacity-info>
<rtl-channel-capacity-info fxFlex="100" *ngSwitchCase="'capacity'" [channelBalances]="channelBalances" [allChannels]="allChannelsCapacity" [ngClass]="{'error-border': flgLoading[5]==='error'}"></rtl-channel-capacity-info>
<rtl-fee-info fxFlex="100" *ngSwitchCase="'fee'" [flgInfoUpdate]="flgChildInfoUpdated" [fees]="fees" [ngClass]="{'error-border': flgLoading[1]==='error'}"></rtl-fee-info>
<rtl-channel-status-info fxFlex="100" *ngSwitchCase="'status'" [channelsStatus]="channelsStatus" [ngClass]="{'error-border': flgLoading[5]==='error' || flgLoading[6]==='error'}"></rtl-channel-status-info>
<h3 *ngSwitchDefault>Error! Unable to find information!</h3>
@ -32,14 +32,19 @@
</div>
<mat-grid-list cols="3" rowHeight="220px">
<mat-grid-tile *ngFor="let card of merchantCards | async" [colspan]="card.cols" [rowspan]="card.rows">
<mat-card fxLayout="column" fxLayoutAlign="start start" class="dashboard-card p-16">
<mat-card-header><mat-card-title>{{card.title}}</mat-card-title></mat-card-header>
<mat-card-content class="dashboard-card-content w-100" fxFlex="95">
<mat-card fxLayout="column" fxLayoutAlign="start start" class="dashboard-card" [ngClass]="{'p-16': card.id !== 'transactions'}">
<mat-card-header *ngIf="card.id !== 'transactions'"><mat-card-title>{{card.title}}</mat-card-title></mat-card-header>
<mat-card-content class="dashboard-card-content w-100" fxFlex="{{card.id !== 'transactions' ? 95 : 100}}">
<div [ngSwitch]="card.id" fxLayout="column" fxFlex="100">
<rtl-balances-info fxFlex="100" *ngSwitchCase="'balance'" [flgInfoUpdate]="flgChildInfoUpdated" [balances]="balances" [ngClass]="{'error-border': flgLoading[2]==='error' || flgLoading[5]==='error'}"></rtl-balances-info>
<rtl-channel-liquidity-info fxFlex="100" *ngSwitchCase="'inboundLiq'" [direction]="'In'" [totalLiquidity]="totalInboundLiquidity" [allChannels]="allChannels" [ngClass]="{'error-border': flgLoading[5]==='error'}"></rtl-channel-liquidity-info>
<rtl-channel-liquidity-info fxFlex="100" *ngSwitchCase="'outboundLiq'" [direction]="'Out'" [totalLiquidity]="totalOutboundLiquidity" [allChannels]="allChannels" [ngClass]="{'error-border': flgLoading[5]==='error'}"></rtl-channel-liquidity-info>
<rtl-dashboard-transactions fxFlex="100" *ngSwitchCase="'transactions'"></rtl-dashboard-transactions>
<rtl-channel-liquidity-info fxFlex="100" *ngSwitchCase="'inboundLiq'" [direction]="'In'" [totalLiquidity]="totalInboundLiquidity" [allChannels]="allInboundChannels" [ngClass]="{'error-border': flgLoading[5]==='error'}"></rtl-channel-liquidity-info>
<rtl-channel-liquidity-info fxFlex="100" *ngSwitchCase="'outboundLiq'" [direction]="'Out'" [totalLiquidity]="totalOutboundLiquidity" [allChannels]="allOutboundChannels" [ngClass]="{'error-border': flgLoading[5]==='error'}"></rtl-channel-liquidity-info>
<span fxLayout="column" fxFlex="100" *ngSwitchCase="'transactions'">
<mat-tab-group fxLayout="column" fxFlex="100">
<mat-tab label="Receive"><rtl-lightning-invoices class="h-100" [showDetails]="false"></rtl-lightning-invoices></mat-tab>
<mat-tab label="Pay"><rtl-lightning-payments [showDetails]="false"></rtl-lightning-payments></mat-tab>
</mat-tab-group>
</span>
<h3 *ngSwitchDefault>Error! Unable to find information!</h3>
</div>
</mat-card-content>

@ -1,5 +1,4 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router } from '@angular/router';
import { map } from 'rxjs/operators';
import { Breakpoints, BreakpointObserver } from '@angular/cdk/layout';
import { Subject } from 'rxjs';
@ -9,6 +8,7 @@ import { Actions } from '@ngrx/effects';
import { faSmile } from '@fortawesome/free-regular-svg-icons';
import { LoggerService } from '../../shared/services/logger.service';
import { CommonService } from '../../shared/services/common.service';
import { UserPersonaEnum } from '../../shared/services/consts-enums-functions';
import { ChannelsStatus, GetInfo, Fees, Channel } from '../../shared/models/lndModels';
import { SelNodeChild } from '../../shared/models/RTLconfig';
@ -24,7 +24,6 @@ export class HomeComponent implements OnInit, OnDestroy {
public faSmile = faSmile;
public flgChildInfoUpdated = false;
public userPersonaEnum = UserPersonaEnum;
public userPersona = UserPersonaEnum.OPERATOR;
public activeChannels = 0;
public inactiveChannels = 0;
public pendingChannels = 0;
@ -35,6 +34,7 @@ export class HomeComponent implements OnInit, OnDestroy {
public balances = { onchain: -1, lightning: -1 };
public allChannels: Channel[] = [];
public channelsStatus: ChannelsStatus = {};
public allChannelsCapacity: Channel[] = [];
public allInboundChannels: Channel[] = [];
public allOutboundChannels: Channel[] = [];
public totalInboundLiquidity = 0;
@ -83,7 +83,7 @@ export class HomeComponent implements OnInit, OnDestroy {
})
);
constructor(private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>, private actions$: Actions, private breakpointObserver: BreakpointObserver, private router: Router) {}
constructor(private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>, private actions$: Actions, private breakpointObserver: BreakpointObserver, private commonService: CommonService) {}
ngOnInit() {
this.store.select('lnd')
@ -149,6 +149,9 @@ export class HomeComponent implements OnInit, OnDestroy {
this.totalInboundLiquidity = 0;
this.totalOutboundLiquidity = 0;
this.allChannels = rtlStore.allChannels.filter(channel => channel.active === true);
this.allChannelsCapacity = JSON.parse(JSON.stringify(this.commonService.sortDescByKey(this.allChannels, 'balancedness')));
this.allInboundChannels = JSON.parse(JSON.stringify(this.commonService.sortDescByKey(this.allChannels, 'remote_balance')));
this.allOutboundChannels = JSON.parse(JSON.stringify(this.commonService.sortDescByKey(this.allChannels, 'local_balance')));
this.allChannels.forEach(channel => {
this.totalInboundLiquidity = this.totalInboundLiquidity + +channel.remote_balance;
this.totalOutboundLiquidity = this.totalOutboundLiquidity + +channel.local_balance;

@ -47,7 +47,6 @@ import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { LayoutModule } from '@angular/cdk/layout';
import { ChannelLiquidityInfoComponent } from './home/channel-liquidity-info/channel-liquidity-info.component';
import { DashboardTransactionsComponent } from './home/dashboard-transactions/dashboard-transactions.component';
import { LoopComponent } from './loop/loop.component';
@NgModule({
@ -96,9 +95,8 @@ import { LoopComponent } from './loop/loop.component';
FeeInfoComponent,
ChannelStatusInfoComponent,
ChannelCapacityInfoComponent,
ChannelLiquidityInfoComponent,
DashboardTransactionsComponent,
LoopComponent
LoopComponent,
ChannelLiquidityInfoComponent
],
providers: [
{ provide: LoggerService, useClass: ConsoleLoggerService },

@ -41,7 +41,7 @@
<mat-hint fxFlex="50" fxLayoutAlign="start center" class="font-size-80"><strong class="font-weight-900">Local: </strong>{{channel.local_balance || 0 | number}} {{information?.smaller_currency_unit}}</mat-hint>
<mat-hint fxFlex="50" fxLayoutAlign="end center" class="font-size-80"><strong class="font-weight-900">Remote: </strong>{{channel.remote_balance || 0 | number}} {{information?.smaller_currency_unit}}</mat-hint>
</div>
<mat-progress-bar mode="determinate" value="{{channel.local_balance && channel.local_balance > 0 ? ((+channel.local_balance/((+channel.local_balance)+(+channel.remote_balance)))*100) : 0}}"></mat-progress-bar>
<mat-progress-bar mode="determinate" value="{{channel.balancedness}}"></mat-progress-bar>
</td>
</ng-container>
<ng-container matColumnDef="actions">

@ -4,6 +4,7 @@ import {
GetInfo, Peer, Fees, NetworkInfo, Balance, Channel, Payment, ListInvoices, PendingChannels, ClosedChannel, Transaction, SwitchRes, QueryRoutes
} from '../../shared/models/lndModels';
import * as RTLActions from '../../store/rtl.actions';
import { UserPersonaEnum } from '../../shared/services/consts-enums-functions';
export interface LNDState {
effectErrorsLnd: ErrorPayload[];
@ -33,7 +34,7 @@ export interface LNDState {
export const initLNDState: LNDState = {
effectErrorsLnd: [],
nodeSettings: { currencyUnit: 'USD', channelBackupPath: '', satsToBTC: false, currencyUnits: [] },
nodeSettings: { userPersona: UserPersonaEnum.OPERATOR, selCurrencyUnit: 'USD', channelBackupPath: '', satsToBTC: false, currencyUnits: [] },
information: {},
peers: [],
fees: {},

@ -1,83 +1,90 @@
<div fxLayout="column">
<form #addInvoiceForm="ngForm">
<div fxLayout="column" fxLayout.gt-sm="row wrap" fxLayoutAlign.gt-sm="start center">
<mat-form-field fxFlex="100" fxLayoutAlign="start end">
<input matInput [(ngModel)]="memo" placeholder="Memo" tabindex="1" name="memo">
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch">
<form [fxLayout]="showDetails ? 'column' : 'row wrap'" [fxLayoutAlign]="showDetails ? 'space-between stretch' : 'start start'" fxLayout.gt-md="row wrap" fxFlex="100" #addInvoiceForm="ngForm">
<mat-form-field fxFlex="100" fxLayoutAlign="start end">
<input matInput [(ngModel)]="memo" placeholder="Memo" tabindex="1" name="memo">
</mat-form-field>
<div fxLayout="column" [fxLayout.gt-sm]="showDetails ? 'row wrap' : 'column'" [fxFlex]="showDetails ? 70 : 100" [fxLayoutAlign.gt-sm]="showDetails ? 'space-between center' : 'start stretch'" fxLayoutAlign="space-between center">
<mat-form-field fxFlex="40" fxLayoutAlign="start end">
<input matInput [(ngModel)]="invoiceValue" (keyup)="onInvoiceValueChange()" placeholder="Amount" type="number" step="100" min="1" tabindex="2" name="invoiceValue">
<span matSuffix> {{information?.smaller_currency_unit}} </span>
<mat-hint>{{invoiceValueHint}}</mat-hint>
</mat-form-field>
<mat-form-field fxFlex="15" fxLayoutAlign="start end">
<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="3" name="expiry">
<span matSuffix> {{selTimeUnit | titlecase}} </span>
</mat-form-field>
<mat-form-field fxFlex="15" fxLayoutAlign="start end">
<mat-select [value]="selTimeUnit" tabindex="4" required name="timeUnit" (selectionChange)="onTimeUnitChange($event)">
<mat-option *ngFor="let timeUnit of timeUnits" [value]="timeUnit">{{timeUnit | titlecase}}</mat-option>
</mat-select>
</mat-form-field>
<div fxLayout="column" fxLayout.gt-sm="row wrap" fxFlex="70" fxLayoutAlign.gt-sm="space-between center">
<mat-form-field fxFlex="40" fxLayoutAlign="start end">
<input matInput [(ngModel)]="invoiceValue" (keyup)="onInvoiceValueChange()" placeholder="Amount" type="number" step="100" min="1" tabindex="2" name="invoiceValue">
<span matSuffix> {{information?.smaller_currency_unit}} </span>
<mat-hint>{{invoiceValueHint}}</mat-hint>
</mat-form-field>
<mat-form-field fxFlex="15" fxLayoutAlign="start end">
<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="3" name="expiry">
<span matSuffix> {{selTimeUnit | titlecase}} </span>
</mat-form-field>
<mat-form-field fxFlex="15" fxLayoutAlign="start end">
<mat-select [value]="selTimeUnit" tabindex="4" required name="timeUnit" (selectionChange)="onTimeUnitChange($event)">
<mat-option *ngFor="let timeUnit of timeUnits" [value]="timeUnit">{{timeUnit | titlecase}}</mat-option>
</mat-select>
</mat-form-field>
<div fxFlex="25" tabindex="4" fxLayoutAlign="start center">
<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 fxFlex="25" tabindex="4" fxLayoutAlign="start center">
<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>
<div class="mt-2">
<button fxFlex="10" fxLayoutAlign="center center" mat-stroked-button color="primary" class="mr-2" tabindex="5" type="reset" (click)="resetData()">Clear Field</button>
<div class="mt-2" fxLayout="row" fxFlex="100" fxLayoutAlign="space-between start" fxLayoutAlign.gt-md="start start" *ngIf="showDetails">
<button fxFlex="48" fxFlex.gt-md="10" fxLayoutAlign="center center" mat-stroked-button color="primary" class="mr-2" tabindex="5" type="reset" (click)="resetData()">Clear Field</button>
<button fxFlex="48" fxFlex.gt-md="10" fxLayoutAlign="center center" mat-raised-button color="primary" [disabled]="addInvoiceForm.form.invalid" (click)="onAddInvoice(addInvoiceForm)" tabindex="6">
<p *ngIf="addInvoiceForm.form.invalid; else createText">Invalid values</p>
<ng-template #createText><p>Create Invoice</p></ng-template>
</button>
</div>
<p *ngIf="addInvoiceForm.form.invalid; else createText">Invalid values</p>
<ng-template #createText><p>Create Invoice</p></ng-template>
</button>
</div>
<div class="mt-2" fxLayout="row" fxFlex="100" fxLayoutAlign="space-between stretch" *ngIf="!showDetails">
<button fxFlex="48" fxLayoutAlign="center center" mat-stroked-button color="primary" class="mr-2" tabindex="5" type="reset" (click)="resetData()">Clear Field</button>
<button fxFlex="48" fxLayoutAlign="center center" mat-raised-button color="primary" [disabled]="addInvoiceForm.form.invalid" (click)="onAddInvoice(addInvoiceForm)" tabindex="6">
<p *ngIf="addInvoiceForm.form.invalid; else createText">Invalid values</p>
<ng-template #createText><p>Create Invoice</p></ng-template>
</button>
</div>
</form>
<div fxLayout="row" fxLayoutAlign="start center" class="padding-gap-x page-sub-title-container mt-2">
<div fxFlex="70">
<fa-icon [icon]="faHistory" class="page-title-img mr-1"></fa-icon>
<span class="page-title">Invoices History</span>
<div fxLayout="column" fxLayoutAlign="start stretch" *ngIf="showDetails">
<div fxLayout="row" fxLayoutAlign="start center" class="padding-gap-x page-sub-title-container mt-2">
<div fxFlex="70">
<fa-icon [icon]="faHistory" class="page-title-img mr-1"></fa-icon>
<span class="page-title">Invoices History</span>
</div>
<mat-form-field fxFlex="30">
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
</mat-form-field>
</div>
<div perfectScrollbar class="table-container">
<mat-progress-bar *ngIf="flgLoading[0]===true" mode="indeterminate"></mat-progress-bar>
<table mat-table #table [dataSource]="invoices" matSort [ngClass]="{'overflow-auto error-border': flgLoading[0]==='error','overflow-auto': true}">
<ng-container matColumnDef="creation_date">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Date Created </th>
<td mat-cell *matCellDef="let invoice">
<span *ngIf="invoice.settled" class="dot green"></span>
<span *ngIf="!invoice.settled" class="dot yellow"></span>
{{invoice.creation_date_str}}</td>
</ng-container>
<ng-container matColumnDef="memo">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Memo </th>
<td mat-cell *matCellDef="let invoice">{{invoice.memo}}</td>
</ng-container>
<ng-container matColumnDef="value">
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before"> Value (Sats) </th>
<td mat-cell *matCellDef="let invoice"><span fxLayoutAlign="end center"> {{invoice?.value | number}} </span></td>
</ng-container>
<ng-container matColumnDef="settle_date">
<th mat-header-cell *matHeaderCellDef mat-sort-header class="pl-4"> Date Settled </th>
<td mat-cell *matCellDef="let invoice" class="pl-4">{{invoice.settle_date_str || '-'}}</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 invoice">
<button mat-stroked-button color="primary" type="button" tabindex="4" (click)="onInvoiceClick(invoice, $event)">View Info</button>
</td>
</ng-container>
<ng-container matColumnDef="no_invoice">
<td mat-footer-cell *matFooterCellDef colspan="4">
<p *ngIf="!invoices.data || invoices.data.length<1">No invoices available.</p>
</td>
</ng-container>
<tr mat-footer-row *matFooterRowDef="['no_invoice']" [ngClass]="{'display-none': invoices.data && invoices.data.length>0}"></tr>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: flgSticky;"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [@newlyAddedRowAnimation]="(row.memo == newlyAddedInvoiceMemo && row.value == newlyAddedInvoiceValue && flgAnimate) ? 'added' : 'notAdded'" class="row-invoices"></tr>
</table>
<mat-paginator [length]="totalInvoices" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" (page)="onPageChange($event)" showFirstLastButtons class="mb-4"></mat-paginator>
</div>
<mat-form-field fxFlex="30">
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
</mat-form-field>
</div>
<div perfectScrollbar class="table-container">
<mat-progress-bar *ngIf="flgLoading[0]===true" mode="indeterminate"></mat-progress-bar>
<table mat-table #table [dataSource]="invoices" matSort [ngClass]="{'overflow-auto error-border': flgLoading[0]==='error','overflow-auto': true}">
<ng-container matColumnDef="creation_date">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Date Created </th>
<td mat-cell *matCellDef="let invoice">
<span *ngIf="invoice.settled" class="dot green"></span>
<span *ngIf="!invoice.settled" class="dot yellow"></span>
{{invoice.creation_date_str}}</td>
</ng-container>
<ng-container matColumnDef="memo">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Memo </th>
<td mat-cell *matCellDef="let invoice">{{invoice.memo}}</td>
</ng-container>
<ng-container matColumnDef="value">
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before"> Value (Sats) </th>
<td mat-cell *matCellDef="let invoice"><span fxLayoutAlign="end center"> {{invoice?.value | number}} </span></td>
</ng-container>
<ng-container matColumnDef="settle_date">
<th mat-header-cell *matHeaderCellDef mat-sort-header class="pl-4"> Date Settled </th>
<td mat-cell *matCellDef="let invoice" class="pl-4">{{invoice.settle_date_str || '-'}}</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 invoice">
<button mat-stroked-button color="primary" type="button" tabindex="4" (click)="onInvoiceClick(invoice, $event)">View Info</button>
</td>
</ng-container>
<ng-container matColumnDef="no_invoice">
<td mat-footer-cell *matFooterCellDef colspan="4">
<p *ngIf="!invoices.data || invoices.data.length<1">No invoices available.</p>
</td>
</ng-container>
<tr mat-footer-row *matFooterRowDef="['no_invoice']" [ngClass]="{'display-none': invoices.data && invoices.data.length>0}"></tr>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: flgSticky;"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [@newlyAddedRowAnimation]="(row.memo == newlyAddedInvoiceMemo && row.value == newlyAddedInvoiceValue && flgAnimate) ? 'added' : 'notAdded'" class="row-invoices"></tr>
</table>
<mat-paginator [length]="totalInvoices" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" (page)="onPageChange($event)" showFirstLastButtons class="mb-4"></mat-paginator>
</div>
</div>
</div>

@ -1,4 +1,4 @@
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { Component, OnInit, OnDestroy, ViewChild, Input } from '@angular/core';
import { DecimalPipe } from '@angular/common';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@ -27,6 +27,7 @@ import * as fromRTLReducer from '../../../store/rtl.reducers';
]
})
export class LightningInvoicesComponent implements OnInit, OnDestroy {
@Input() showDetails = true;
@ViewChild(MatSort, { static: true }) sort: MatSort;
faHistory = faHistory;
public selNode: SelNodeChild = {};

@ -1,63 +1,72 @@
<div fxLayout="column">
<div fxLayout="column" fxLayoutAlign="space-between stretch">
<form fxLayout="column" fxLayoutAlign="space-between stretch" fxLayout.gt-md="row wrap" #sendPaymentForm="ngForm">
<mat-form-field class="w-100">
<input matInput placeholder="Payment Request" name="paymentRequest" [(ngModel)]="paymentRequest" tabindex="1" required #paymentReq="ngModel">
</mat-form-field>
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between start" fxLayoutAlign.gt-md="start start">
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between start" fxLayoutAlign.gt-md="start start" *ngIf="showDetails">
<button fxFlex="48" fxFlex.gt-md="10" mat-stroked-button color="primary" class="mr-2" tabindex="2" type="reset" (click)="resetData()">Clear Field</button>
<button fxFlex="48" fxFlex.gt-md="10" fxLayoutAlign="center center" mat-raised-button color="primary" [disabled]="paymentReq.invalid" (click)="onSendPayment();" tabindex="3">
<p *ngIf="paymentReq.invalid && (paymentReq.dirty || paymentReq.touched); else sendText">Invalid Req</p>
<ng-template #sendText><p>Send Payment</p></ng-template>
</button>
</div>
<p *ngIf="paymentReq.invalid && (paymentReq.dirty || paymentReq.touched); else sendText">Invalid Req</p>
<ng-template #sendText><p>Send Payment</p></ng-template>
</button>
</div>
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between stretch" *ngIf="!showDetails">
<button fxFlex="48" mat-stroked-button color="primary" tabindex="2" type="reset" (click)="resetData()">Clear Field</button>
<button fxFlex="48" fxLayoutAlign="center center" mat-raised-button color="primary" [disabled]="paymentReq.invalid" (click)="onSendPayment();" tabindex="3">
<p *ngIf="paymentReq.invalid && (paymentReq.dirty || paymentReq.touched); else sendText">Invalid Req</p>
<ng-template #sendText><p>Send Payment</p></ng-template>
</button>
</div>
</form>
<div fxLayout="row" fxLayoutAlign="start center" class="padding-gap-x page-sub-title-container mt-2">
<div fxFlex="70">
<fa-icon [icon]="faHistory" class="page-title-img mr-1"></fa-icon>
<span class="page-title">Payments History</span>
<div fxLayout="column" fxLayoutAlign="start stretch" *ngIf="showDetails">
<div fxLayout="row" fxLayoutAlign="start center" class="padding-gap-x page-sub-title-container mt-2">
<div fxFlex="70">
<fa-icon [icon]="faHistory" class="page-title-img mr-1"></fa-icon>
<span class="page-title">Payments History</span>
</div>
<mat-form-field fxFlex="30">
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
</mat-form-field>
</div>
<mat-form-field fxFlex="30">
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
</mat-form-field>
</div>
<div perfectScrollbar fxLayout="row" fxLayoutAlign="start center" fxFlex="100" class="table-container">
<mat-progress-bar *ngIf="flgLoading[0]===true" mode="indeterminate"></mat-progress-bar>
<table mat-table #table [dataSource]="payments" matSort [ngClass]="{'overflow-auto error-border': flgLoading[0]==='error','overflow-auto': true}">
<ng-container matColumnDef="creation_date">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Creation Date</th>
<td mat-cell *matCellDef="let payment">{{payment?.creation_date_str}}</td>
</ng-container>
<ng-container matColumnDef="payment_hash">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Payment Hash</th>
<td mat-cell *matCellDef="let payment">{{payment?.payment_hash}}</td>
</ng-container>
<ng-container matColumnDef="fee">
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before">Fee</th>
<td mat-cell *matCellDef="let payment"><span fxLayoutAlign="end center">{{payment?.fee | number}}</span></td>
</ng-container>
<ng-container matColumnDef="value">
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before">Value (Sats)</th>
<td mat-cell *matCellDef="let payment"><span fxLayoutAlign="end center">{{payment?.value | number}}</span></td>
</ng-container>
<ng-container matColumnDef="path">
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before">Path (Hops)</th>
<td mat-cell *matCellDef="let payment"><span fxLayoutAlign="end center">{{payment?.path?.length || 0}}</span></td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef class="pl-4 pr-3"><span fxLayoutAlign="end center">Actions</span></th>
<td mat-cell *matCellDef="let payment" class="pl-4">
<button mat-stroked-button color="primary" type="button" tabindex="4" (click)="onPaymentClick(payment,$event)">View Info</button>
</td>
</ng-container>
<ng-container matColumnDef="no_payment">
<td mat-footer-cell *matFooterCellDef colspan="4">
<p *ngIf="!payments.data || payments.data.length<1">No payments available.</p>
</td>
</ng-container>
<tr mat-footer-row *matFooterRowDef="['no_payment']" [ngClass]="{'display-none': payments.data && payments.data.length>0}"></tr>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: flgSticky;"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [@newlyAddedRowAnimation]="(row.payment_hash === newlyAddedPayment && flgAnimate) ? 'added' : 'notAdded'"></tr>
</table>
<div perfectScrollbar fxLayout="row" fxLayoutAlign="start center" fxFlex="100" class="table-container">
<mat-progress-bar *ngIf="flgLoading[0]===true" mode="indeterminate"></mat-progress-bar>
<table mat-table #table [dataSource]="payments" matSort [ngClass]="{'overflow-auto error-border': flgLoading[0]==='error','overflow-auto': true}">
<ng-container matColumnDef="creation_date">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Creation Date</th>
<td mat-cell *matCellDef="let payment">{{payment?.creation_date_str}}</td>
</ng-container>
<ng-container matColumnDef="payment_hash">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Payment Hash</th>
<td mat-cell *matCellDef="let payment">{{payment?.payment_hash}}</td>
</ng-container>
<ng-container matColumnDef="fee">
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before">Fee</th>
<td mat-cell *matCellDef="let payment"><span fxLayoutAlign="end center">{{payment?.fee | number}}</span></td>
</ng-container>
<ng-container matColumnDef="value">
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before">Value (Sats)</th>
<td mat-cell *matCellDef="let payment"><span fxLayoutAlign="end center">{{payment?.value | number}}</span></td>
</ng-container>
<ng-container matColumnDef="path">
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before">Path (Hops)</th>
<td mat-cell *matCellDef="let payment"><span fxLayoutAlign="end center">{{payment?.path?.length || 0}}</span></td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef class="pl-4 pr-3"><span fxLayoutAlign="end center">Actions</span></th>
<td mat-cell *matCellDef="let payment" class="pl-4">
<button mat-stroked-button color="primary" type="button" tabindex="4" (click)="onPaymentClick(payment,$event)">View Info</button>
</td>
</ng-container>
<ng-container matColumnDef="no_payment">
<td mat-footer-cell *matFooterCellDef colspan="4">
<p *ngIf="!payments.data || payments.data.length<1">No payments available.</p>
</td>
</ng-container>
<tr mat-footer-row *matFooterRowDef="['no_payment']" [ngClass]="{'display-none': payments.data && payments.data.length>0}"></tr>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: flgSticky;"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [@newlyAddedRowAnimation]="(row.payment_hash === newlyAddedPayment && flgAnimate) ? 'added' : 'notAdded'"></tr>
</table>
</div>
<mat-paginator [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" showFirstLastButtons class="mb-4"></mat-paginator>
</div>
<mat-paginator [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" showFirstLastButtons class="mb-4"></mat-paginator>
</div>

@ -1,4 +1,4 @@
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { Component, OnInit, OnDestroy, ViewChild, Input } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil, take } from 'rxjs/operators';
import { Store } from '@ngrx/store';
@ -25,6 +25,7 @@ import * as fromRTLReducer from '../../../store/rtl.reducers';
]
})
export class LightningPaymentsComponent implements OnInit, OnDestroy {
@Input() showDetails = true;
@ViewChild('sendPaymentForm', { static: true }) form;
@ViewChild(MatSort, { static: true }) sort: MatSort;
@ViewChild(MatPaginator, {static: true}) paginator: MatPaginator;

@ -17,8 +17,8 @@
<mat-card>
<mat-card-content fxLayout="column">
<mat-tab-group>
<mat-tab label="Payments"><rtl-lightning-payments></rtl-lightning-payments></mat-tab>
<mat-tab label="Invoices"><rtl-lightning-invoices></rtl-lightning-invoices></mat-tab>
<mat-tab label="Payments"><rtl-lightning-payments [showDetails]="true"></rtl-lightning-payments></mat-tab>
<mat-tab label="Invoices"><rtl-lightning-invoices [showDetails]="true"></rtl-lightning-invoices></mat-tab>
<mat-tab label="Query Routes"><rtl-query-routes></rtl-query-routes></mat-tab>
</mat-tab-group>
</mat-card-content>

@ -7,7 +7,17 @@
<mat-card-content fxLayout="column" class="card-content-gap">
<div fxLayout="column" fxLayout.gt-sm="row wrap" fxLayoutAlign.gt-sm="space-between center" fxLayoutAlign="center space-between">
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between center">
<div fxFlex="33" *ngIf="appConfig.nodes.length && appConfig.nodes.length > 1">
<div fxFlex="25">
<div fxFlex.gt-md="50" fxFlex="80">
<h4>User Persona</h4>
<mat-select [(ngModel)]="selNode.settings.userPersona" placeholder="Select User Persona" tabindex="1" required name="userPersona">
<mat-option *ngFor="let userPersona of userPersonas" [value]="userPersona">
{{userPersona}}
</mat-option>
</mat-select>
</div>
</div>
<div fxFlex="25" *ngIf="appConfig.nodes.length && appConfig.nodes.length > 1">
<div fxFlex.gt-md="50" fxFlex="80">
<h4>Default Selected Node</h4>
<mat-select [(ngModel)]="appConfig.defaultNodeIndex" placeholder="Default Node" tabindex="1" required name="defaultNode">
@ -17,7 +27,7 @@
</mat-select>
</div>
</div>
<div fxFlex="33">
<div fxFlex="25">
<div fxFlex.gt-md="30" fxFlex="70">
<h4>Currency Unit</h4>
<mat-select [(ngModel)]="selNode.settings.currencyUnit" placeholder="Currency Unit" (selectionChange)="onCurrencyChange($event)" tabindex="1" required name="currencyUnit">
@ -27,7 +37,7 @@
</mat-select>
</div>
</div>
<div fxFlex="33" *ngIf="selNode.lnImplementation !== 'CLT'">
<div fxFlex="25" *ngIf="selNode.lnImplementation !== 'CLT'">
<div fxFlex="60">
<h4>BTC</h4>
<div fxLayoutAlign="start center">
@ -120,10 +130,8 @@
<div fxLayout="row" fxLayoutAlign="start start" class="mt-4">
<mat-divider class="mb-2"></mat-divider>
<div class="mt-2">
<button class="mr-2" fxFlex="10" fxLayoutAlign="center center" mat-raised-button color="primary"
(click)="onUpdateSettings()" tabindex="12">Update</button>
<button fxFlex="10" fxLayoutAlign="center center" mat-stroked-button color="primary" (click)="onResetSettings()"
tabindex="13">Reset</button>
<button fxFlex="10" fxLayoutAlign="center center" mat-stroked-button color="primary" (click)="onResetSettings()" tabindex="12" class="mr-2">Reset</button>
<button fxFlex="10" fxLayoutAlign="center center" mat-raised-button color="primary" (click)="onUpdateSettings()" tabindex="13">Update</button>
</div>
</div>
</mat-card-content>

@ -5,7 +5,7 @@ import { Store } from '@ngrx/store';
import { faTools } from '@fortawesome/free-solid-svg-icons';
import { CURRENCY_UNITS, AlertTypeEnum } from '../../services/consts-enums-functions';
import { CURRENCY_UNITS, AlertTypeEnum, UserPersonaEnum } from '../../services/consts-enums-functions';
import { LightningNode, Settings, RTLConfiguration, GetInfoRoot } from '../../models/RTLconfig';
import { LoggerService } from '../../services/logger.service';
import { CommonService } from '../../services/common.service';
@ -22,13 +22,14 @@ export class AppSettingsComponent implements OnInit, OnDestroy {
public faTools = faTools;
public selNode: LightningNode;
public information: GetInfoRoot = {};
public userPersonas = [UserPersonaEnum.OPERATOR, UserPersonaEnum.MERCHANT];
public currencyUnits = [{id: 'USD', name: 'United States Dollar'}, {id: 'GBP', name: 'Pound'}, {id: 'INR', name: 'Indian Rupee'}];
public menus = [{id: 'vertical', name: 'Vertical'}, {id: 'horizontal', name: 'Horizontal'}];
public selectedMenu = {id: 'vertical', name: 'Vertical'};
public menuTypes = [{id: 'regular', name: 'Regular'}, {id: 'compact', name: 'Compact'}, {id: 'mini', name: 'Mini'}];
public themeModes = [{id: 'day', name: 'Day'}, {id: 'night', name: 'Night'}];
public themeColors = ['purple', 'teal', 'indigo', 'pink'];
public fontSizes = [{id: 1, name: 'Small', class: 'small-font'}, {id: 2, name: 'Regular', class: 'regular-font'}, {id: 3, name: 'Large', class: 'large-font'}];
public selectedMenu = {id: 'vertical', name: 'Vertical'};
public selectedMenuType = {id: 'regular', name: 'Regular'};
public selectedFontSize = {id: 2, name: 'Regular', class: 'regular-font'};
public selectedThemeMode = {id: 'day', name: 'Day'};
@ -73,8 +74,8 @@ export class AppSettingsComponent implements OnInit, OnDestroy {
onCurrencyChange(event: any) {
this.selNode.settings.currencyUnits = [...CURRENCY_UNITS, event.value];
this.store.dispatch(new RTLActions.SetChildNodeSettings({channelBackupPath: this.selNode.settings.channelBackupPath, satsToBTC: this.selNode.settings.satsToBTC, currencyUnit: event.value, currencyUnits: this.selNode.settings.currencyUnits}));
this.store.dispatch(new RTLActions.SetChildNodeSettingsCL({channelBackupPath: this.selNode.settings.channelBackupPath, satsToBTC: this.selNode.settings.satsToBTC, currencyUnit: event.value, currencyUnits: this.selNode.settings.currencyUnits}));
this.store.dispatch(new RTLActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, satsToBTC: this.selNode.settings.satsToBTC, selCurrencyUnit: event.value, currencyUnits: this.selNode.settings.currencyUnits}));
this.store.dispatch(new RTLActions.SetChildNodeSettingsCL({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, satsToBTC: this.selNode.settings.satsToBTC, selCurrencyUnit: event.value, currencyUnits: this.selNode.settings.currencyUnits}));
}
chooseMenuType() {
@ -88,8 +89,8 @@ export class AppSettingsComponent implements OnInit, OnDestroy {
toggleSettings(toggleField: string, event?: any) {
if (toggleField === 'satsToBTC') {
this.store.dispatch(new RTLActions.SetChildNodeSettings({channelBackupPath: this.selNode.settings.channelBackupPath, satsToBTC: this.selNode.settings.satsToBTC, currencyUnit: this.selNode.settings.currencyUnit, currencyUnits: this.selNode.settings.currencyUnits}));
this.store.dispatch(new RTLActions.SetChildNodeSettingsCL({channelBackupPath: this.selNode.settings.channelBackupPath, satsToBTC: this.selNode.settings.satsToBTC, currencyUnit: this.selNode.settings.currencyUnit, currencyUnits: this.selNode.settings.currencyUnits}));
this.store.dispatch(new RTLActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, satsToBTC: this.selNode.settings.satsToBTC, selCurrencyUnit: this.selNode.settings.currencyUnit, currencyUnits: this.selNode.settings.currencyUnits}));
this.store.dispatch(new RTLActions.SetChildNodeSettingsCL({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, satsToBTC: this.selNode.settings.satsToBTC, selCurrencyUnit: this.selNode.settings.currencyUnit, currencyUnits: this.selNode.settings.currencyUnits}));
}
if(toggleField === 'menu') {
this.selNode.settings.flgSidenavOpened = (!event.checked) ? false : true;
@ -114,20 +115,11 @@ export class AppSettingsComponent implements OnInit, OnDestroy {
}
onUpdateSettings() {
let updatedSettings = (JSON.stringify(this.previousSettings) !== JSON.stringify(this.selNode.settings)) ? this.selNode.settings : null;
let defaultNodeIndex = (this.previousDefaultNode !== this.appConfig.defaultNodeIndex) ? this.appConfig.defaultNodeIndex : null;
this.logger.info(this.selNode.settings);
if (!updatedSettings && !defaultNodeIndex) {
this.store.dispatch(new RTLActions.OpenAlert({ width: '55%', data: {
type: AlertTypeEnum.WARNING,
alertTitle: 'Configuration Not Updated',
titleMessage: 'Nothing has been updated to save!'
}}));
} else {
this.store.dispatch(new RTLActions.OpenSpinner('Updating Settings...'));
this.store.dispatch(new RTLActions.SaveSettings({settings: updatedSettings, defaultNodeIndex: defaultNodeIndex}));
this.done.emit();
}
this.store.dispatch(new RTLActions.OpenSpinner('Updating Settings...'));
this.store.dispatch(new RTLActions.SaveSettings({settings: this.selNode.settings, defaultNodeIndex: defaultNodeIndex}));
this.done.emit();
}
onResetSettings() {

@ -1,18 +1,18 @@
<mat-menu #topMenu="matMenu" [overlapTrigger]="false" class="top-menu">
<p mat-menu-item>
<fa-icon [icon]="faCodeBranch" class="fa-icon-small mr-4"></fa-icon>
<fa-icon [icon]="faCodeBranch" class="fa-icon-small mr-1"></fa-icon>
<span>Version: {{version}}</span>
</p>
<a mat-menu-item routerLink="/sconfig">
<fa-icon [icon]="faCog" class="fa-icon-small mr-4"></fa-icon>
<span routerLink="/sconfig">Node Config</span>
<a mat-menu-item routerLink="/advanced">
<fa-icon [icon]="faCog" class="fa-icon-small mr-1"></fa-icon>
<span routerLink="/advanced">Node Config</span>
</a>
<a mat-menu-item routerLink="/help">
<fa-icon [icon]="faLifeRing" class="fa-icon-small mr-4"></fa-icon>
<fa-icon [icon]="faLifeRing" class="fa-icon-small mr-1"></fa-icon>
<span routerLink="/help">Help</span>
</a>
<a *ngIf="showLogout" mat-menu-item (click)="onClick()">
<fa-icon [icon]="faEject" class="fa-icon-small mr-4"></fa-icon>
<fa-icon [icon]="faEject" class="fa-icon-small mr-1"></fa-icon>
<span>Logout</span>
</a>
</mat-menu>

@ -9,6 +9,7 @@ export class SSO {
export class Settings {
constructor(
public userPersona: string,
public flgSidenavOpened: boolean,
public flgSidenavPinned: boolean,
public menu: string,
@ -66,8 +67,9 @@ export interface GetInfoRoot {
}
export interface SelNodeChild {
userPersona?: string;
channelBackupPath?: string;
satsToBTC?: boolean;
currencyUnit?: string;
selCurrencyUnit?: string;
currencyUnits?: string[];
}

@ -26,8 +26,8 @@ export const TRANS_TYPES = [
];
export enum UserPersonaEnum {
OPERATOR = 'OPERATOR',
MERCHANT = 'MERCHANT'
OPERATOR = 'Operator',
MERCHANT = 'Merchant'
}
export enum AlertTypeEnum {

@ -15,8 +15,8 @@ export class SocketService {
observer: Observer<any>;
startLoopMonitor(): Observable<any> {
// this.loopMonitor = socketIo('http://localhost:3000/loopMonitor');
this.loopMonitor = socketIo('/loopMonitor');
this.loopMonitor = socketIo('http://localhost:3000/loopMonitor');
// this.loopMonitor = socketIo('/loopMonitor');
this.loopMonitor.emit('start');
this.loopMonitor.on('message', (res) => { this.observer.next(res.message); });
return this.createObservable();

@ -191,6 +191,7 @@ body {
.mat-tab-body-wrapper, .card-content-gap {
padding: 1.2rem 2.4rem !important;
height: 100%;
}
.routing-tabs-block .mat-tab-body-wrapper {
@ -245,6 +246,10 @@ body {
padding: 0 !important;
}
.h-100 {
height: 100% !important;
}
.w-100 {
width: 100% !important;
}

@ -311,7 +311,7 @@ export class RTLEffects implements OnDestroy {
initializeNode(node: any, isInitialSetup: boolean) {
const landingPage = isInitialSetup ? '' : 'HOME';
let selNode = { channelBackupPath: node.settings.channelBackupPath, satsToBTC: node.settings.satsToBTC, selCurrencyUnit: node.settings.currencyUnit, currencyUnits: [...CURRENCY_UNITS, node.settings.currencyUnit] };
let selNode = { userPersona: node.settings.userPersona, channelBackupPath: node.settings.channelBackupPath, satsToBTC: node.settings.satsToBTC, selCurrencyUnit: node.settings.currencyUnit, currencyUnits: [...CURRENCY_UNITS, node.settings.currencyUnit] };
this.store.dispatch(new RTLActions.ResetRootStore(node));
this.store.dispatch(new RTLActions.ResetLNDStore(selNode));
this.store.dispatch(new RTLActions.ResetCLStore(selNode));

@ -13,7 +13,7 @@ export interface RootState {
nodeData: GetInfoRoot;
}
const initNodeSettings = { flgSidenavOpened: true, flgSidenavPinned: true, menu: 'vertical', menuType: 'regular', fontSize: 'regular-font', themeMode: 'night', themeColor: 'blue', satsToBTC: false, channelBackupPath: '', selCurrencyUnit: 'USD', currencyUnits: ['Sats', 'BTC', 'USD'] };
const initNodeSettings = { userPersona: 'Operator', flgSidenavOpened: true, flgSidenavPinned: true, menu: 'vertical', menuType: 'regular', fontSize: 'regular-font', themeMode: 'night', themeColor: 'blue', satsToBTC: false, channelBackupPath: '', selCurrencyUnit: 'USD', currencyUnits: ['Sats', 'BTC', 'USD'] };
const initNodeAuthentication = { nodeAuthType: 'CUSTOM', configPath: '', bitcoindConfigPath: '' };
const initRootState: RootState = {

Loading…
Cancel
Save