dashboard incomplete

dashboard incomplete
pull/260/head
Shahana Farooqui 5 years ago
parent b7bbf94abf
commit 835dca0557

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -128,6 +128,8 @@ THE SOFTWARE.
@angular/material/form-field
@angular/material/grid-list
@angular/material/icon
@angular/material/list
@ -211,6 +213,44 @@ Awesome, nor vice versa. **Please do not use brand logos for any purpose except
to represent the company, product, or service to which they refer.**
@fortawesome/free-regular-svg-icons
(CC-BY-4.0 AND MIT)
Font Awesome Free License
-------------------------
Font Awesome Free is free, open source, and GPL friendly. You can use it for
commercial projects, open source projects, or really almost whatever you want.
Full Font Awesome Free license: https://fontawesome.com/license/free.
# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/)
In the Font Awesome Free download, the CC BY 4.0 license applies to all icons
packaged as SVG and JS file types.
# Fonts: SIL OFL 1.1 License (https://scripts.sil.org/OFL)
In the Font Awesome Free download, the SIL OFL license applies to all icons
packaged as web and desktop font files.
# Code: MIT License (https://opensource.org/licenses/MIT)
In the Font Awesome Free download, the MIT license applies to all non-font and
non-icon files.
# Attribution
Attribution is required by MIT, SIL OFL, and CC BY licenses. Downloaded Font
Awesome Free files already contain embedded comments with sufficient
attribution, so you shouldn't need to do anything additional when using these
files normally.
We've kept attribution comments terse, so we ask that you do not actively work
to remove them from files, especially code. They're a great way for folks to
learn about Font Awesome.
# Brand Icons
All brand icons are trademarks of their respective owners. The use of these
trademarks does not indicate endorsement of the trademark holder by Font
Awesome, nor vice versa. **Please do not use brand logos for any purpose except
to represent the company, product, or service to which they refer.**
@fortawesome/free-solid-svg-icons
(CC-BY-4.0 AND MIT)
Font Awesome Free License

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

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.c08e367434232493455f.css"></head>
<link rel="stylesheet" href="styles.905fb133af8ca5e83162.css"></head>
<body>
<rtl-app></rtl-app>
<script src="runtime.d75168f7793ce675c2a6.js"></script><script src="polyfills-es5.92f4069201c83f4833ef.js" nomodule></script><script src="polyfills.5ddcccdb990eb395f306.js"></script><script src="main.a5b80ff2c34bfc900dc4.js"></script></body>
<script src="runtime.f4f82038f2bdab157558.js"></script><script src="polyfills-es5.92f4069201c83f4833ef.js" nomodule></script><script src="polyfills.5ddcccdb990eb395f306.js"></script><script src="main.d9079634ba953d139364.js"></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 +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:"50859d501eab13ab41b8",6:"ad453b6e8bc53c913101",7:"fdc70b63fa397b86320a"}[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:"69ceab2c54f4d2bf4227",6:"c9f217da6011a463bec0",7:"55a06eb6028cdaf8fef4"}[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

8
package-lock.json generated

@ -1466,6 +1466,14 @@
"@fortawesome/fontawesome-common-types": "^0.2.25"
}
},
"@fortawesome/free-regular-svg-icons": {
"version": "5.11.2",
"resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.11.2.tgz",
"integrity": "sha512-k0vbThRv9AvnXYBWi1gn1rFW4X7co/aFkbm0ZNmAR5PoWb9vY9EDDDobg8Ay4ISaXtCPypvJ0W1FWkSpLQwZ6w==",
"requires": {
"@fortawesome/fontawesome-common-types": "^0.2.25"
}
},
"@fortawesome/free-solid-svg-icons": {
"version": "5.11.2",
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.11.2.tgz",

@ -29,6 +29,7 @@
"@angular/router": "~8.1.2",
"@fortawesome/angular-fontawesome": "^0.5.0",
"@fortawesome/fontawesome-svg-core": "^1.2.25",
"@fortawesome/free-regular-svg-icons": "^5.11.2",
"@fortawesome/free-solid-svg-icons": "^5.11.2",
"@ngrx/effects": "^8.4.0",
"@ngrx/router-store": "^8.4.0",

@ -41,6 +41,7 @@ export class CLEffects implements OnDestroy {
.pipe(
map((info) => {
this.logger.info(info);
info.lnImplementation = 'C-Lightning';
this.initializeRemainingData(info, action.payload.loadPage);
return {
type: RTLActions.SET_INFO_CL,

@ -0,0 +1,13 @@
<div fxLayout="column" fxFlex="50" fxLayoutAlign="center start">
<div fxFlex="33">
<h4 fxLayoutAlign="start" class="font-bold-500">On-chain</h4>
<div class="foreground-secondary-text">{{balances.onchain | number}} (Sats)</div>
</div>
<div fxFlex="33">
<h4 fxLayoutAlign="start" class="font-bold-500">Lightning</h4>
<div class="foreground-secondary-text">{{balances.lightning | number}} (Sats)</div>
</div>
<div fxFlex="34">
</div>
</div>
<div fxLayout="column" fxFlex="50" fxLayoutAlign="center start" style="background-color: bisque;height:67%;border-radius: 500px;"></div>

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

@ -0,0 +1,15 @@
import { Component, OnChanges, Input } from '@angular/core';
@Component({
selector: 'rtl-balances-info',
templateUrl: './balances-info.component.html',
styleUrls: ['./balances-info.component.scss']
})
export class BalancesInfoComponent implements OnChanges {
@Input() balances = { onchain: 0, lightning: 0 };
constructor() {}
ngOnChanges() {}
}

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

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

@ -0,0 +1,36 @@
<div fxLayout="column" fxFlex="30" fxLayoutAlign="center start">
<div fxFlex="25">
<h4 fxLayoutAlign="start" class="font-bold-500">Active</h4>
<div class="foreground-secondary-text"><span class="dot tiny-dot green"></span>{{(channelsStatus.active.channels || 0) | number}}</div>
</div>
<div fxFlex="25">
<h4 fxLayoutAlign="start" class="font-bold-500">Pending</h4>
<div class="foreground-secondary-text"><span class="dot tiny-dot yellow"></span>{{(channelsStatus.pending.channels || 0) | number}}</div>
</div>
<div fxFlex="25">
<h4 fxLayoutAlign="start" class="font-bold-500">Inactive</h4>
<div class="foreground-secondary-text"><span class="dot tiny-dot red"></span>{{(channelsStatus.inactive.channels || 0) | number}}</div>
</div>
<div fxFlex="25">
<h4 fxLayoutAlign="start" class="font-bold-500">Closed</h4>
<div class="foreground-secondary-text"><span class="dot tiny-dot grey"></span>{{(channelsStatus.closed.channels || 0) | number}}</div>
</div>
</div>
<div fxLayout="column" fxFlex="70" fxLayoutAlign="center start">
<div fxFlex="25">
<h4 fxLayoutAlign="start" class="font-bold-500">Capacity</h4>
<div class="foreground-secondary-text">{{(channelsStatus.active.capacity || 0) | number}} (Sats)</div>
</div>
<div fxFlex="25">
<h4 fxLayoutAlign="start" class="font-bold-500">Capacity</h4>
<div class="foreground-secondary-text">{{(channelsStatus.pending.capacity || 0) | number}} (Sats)</div>
</div>
<div fxFlex="25">
<h4 fxLayoutAlign="start" class="font-bold-500">Capacity</h4>
<div class="foreground-secondary-text">{{(channelsStatus.inactive.capacity || 0) | number}} (Sats)</div>
</div>
<div fxFlex="25">
<h4 fxLayoutAlign="start" class="font-bold-500">Capacity</h4>
<div class="foreground-secondary-text">{{(channelsStatus.closed.capacity || 0) | number}} (Sats)</div>
</div>
</div>

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

@ -0,0 +1,16 @@
import { Component, OnChanges, Input } from '@angular/core';
import { ChannelsStatus } from '../../../shared/models/lndModels';
@Component({
selector: 'rtl-channel-status-info',
templateUrl: './channel-status-info.component.html',
styleUrls: ['./channel-status-info.component.scss']
})
export class ChannelStatusInfoComponent implements OnChanges {
@Input() channelsStatus: ChannelsStatus = {};
constructor() {}
ngOnChanges() {}
}

@ -0,0 +1,15 @@
<div fxLayout="column" fxFlex="50" fxLayoutAlign="center start">
<div fxFlex="33">
<h4 fxLayoutAlign="start" class="font-bold-500">Daily</h4>
<div class="foreground-secondary-text">{{fees?.day_fee_sum | number}} (Sats)</div>
</div>
<div fxFlex="33">
<h4 fxLayoutAlign="start" class="font-bold-500">Weekly</h4>
<div class="foreground-secondary-text">{{fees?.week_fee_sum | number}} (Sats)</div>
</div>
<div fxFlex="33">
<h4 fxLayoutAlign="start" class="font-bold-500">Monthly</h4>
<div class="foreground-secondary-text">{{fees?.month_fee_sum | number}} (Sats)</div>
</div>
</div>
<div fxLayout="column" fxFlex="50" fxLayoutAlign="center start" style="background-color:cadetblue;height:67%;border-radius: 500px;"></div>

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

@ -0,0 +1,16 @@
import { Component, OnChanges, Input } from '@angular/core';
import { Fees } from '../../../shared/models/lndModels';
@Component({
selector: 'rtl-fee-info',
templateUrl: './fee-info.component.html',
styleUrls: ['./fee-info.component.scss']
})
export class FeeInfoComponent implements OnChanges {
@Input() fees: Fees;
constructor() {}
ngOnChanges() {}
}

@ -1,4 +1,39 @@
<div fxLayout="column" fxLayout.gt-sm="row wrap">
<div fxLayout="column">
<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>
</div>
<mat-grid-list cols="10" rowHeight="330px">
<mat-grid-tile *ngFor="let card of cards | 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}}
<button mat-icon-button class="more-button" [matMenuTriggerFor]="menu" aria-label="Toggle menu">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #menu="matMenu" xPosition="before">
<button mat-menu-item>Expand</button>
<button mat-menu-item>Remove</button>
</mat-menu>
</mat-card-title>
</mat-card-header>
<mat-card-content class="dashboard-card-content w-100" fxFlex="100">
<div [ngSwitch]="card.id" fxLayout="column" fxFlex="100">
<rtl-node-info fxFlex="100" *ngSwitchCase="'node'" [information]="information"></rtl-node-info>
<rtl-balances-info fxFlex="100" *ngSwitchCase="'balance'" [balances]="balances"></rtl-balances-info>
<rtl-channel-capacity-info fxFlex="100" *ngSwitchCase="'capacity'"></rtl-channel-capacity-info>
<rtl-fee-info fxFlex="100" *ngSwitchCase="'fee'" [fees]="fees"></rtl-fee-info>
<rtl-channel-status-info fxFlex="100" *ngSwitchCase="'status'" [channelsStatus]="channelsStatus"></rtl-channel-status-info>
<h3 *ngSwitchDefault>Error! Unable to find information!</h3>
</div>
</mat-card-content>
</mat-card>
</mat-grid-tile>
</mat-grid-list>
</div>
<!-- <div fxLayout="column" fxLayout.gt-sm="row wrap">
<div fxFlex="25" class="padding-gap">
<mat-card [ngClass]="{'custom-card error-border': flgLoading[2]==='error','custom-card': true}">
<mat-card-header class="bg-primary" fxLayoutAlign="center end">
@ -234,4 +269,4 @@
</mat-card>
</div>
</div>
<ng-template #withoutData><h3>Sats</h3></ng-template>
<ng-template #withoutData><h3>Sats</h3></ng-template> -->

@ -1,12 +1,30 @@
.network-info-list .mat-list-item {
height: 44px;
.dashboard-card {
position: absolute;
top: 1rem;
left: 1rem;
right: 1rem;
bottom: 1rem;
}
.mat-column-bytes_sent, .mat-column-bytes_recv, .mat-column-sat_sent, .mat-column-sat_recv, .mat-column-inbound, .mat-column-ping_time {
flex: 0 0 8%;
min-width: 80px;
.more-button {
position: absolute;
top: 7px;
right: 7px;
}
.card-chnl-balances {
min-height: 354px;
.dashboard-card-content {
text-align: left;
}
// .network-info-list .mat-list-item {
// height: 44px;
// }
// .mat-column-bytes_sent, .mat-column-bytes_recv, .mat-column-sat_sent, .mat-column-sat_recv, .mat-column-inbound, .mat-column-ping_time {
// flex: 0 0 8%;
// min-width: 80px;
// }
// .card-chnl-balances {
// min-height: 354px;
// }

@ -1,11 +1,14 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs';
import { map } from 'rxjs/operators';
import { Breakpoints, BreakpointObserver } from '@angular/cdk/layout';
import { Subject, of } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { faSmile } from '@fortawesome/free-regular-svg-icons';
import { LoggerService } from '../../shared/services/logger.service';
import { GetInfo, NetworkInfo, Fees, Peer } from '../../shared/models/lndModels';
import { ChannelsStatus, GetInfo, NetworkInfo, Fees, Peer } from '../../shared/models/lndModels';
import { SelNodeChild } from '../../shared/models/RTLconfig';
import * as RTLActions from '../../store/rtl.actions';
@ -17,9 +20,11 @@ import * as fromRTLReducer from '../../store/rtl.reducers';
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit, OnDestroy {
public faSmile = faSmile;
public selNode: SelNodeChild = {};
public fees: Fees;
public information: GetInfo = {};
public balances = { onchain: -1, lightning: -1 };
public remainder = 0;
public totalPeers = -1;
public totalBalance = 0;
@ -34,6 +39,7 @@ export class HomeComponent implements OnInit, OnDestroy {
public activeChannels = 0;
public inactiveChannels = 0;
public pendingChannels = 0;
public channelsStatus: ChannelsStatus = {};
public peers: Peer[] = [];
barPadding = 0;
maxBalanceValue = 0;
@ -42,8 +48,30 @@ export class HomeComponent implements OnInit, OnDestroy {
view = [];
yAxisLabel = 'Balance';
colorScheme = {domain: ['#FFFFFF']};
cards = this.breakpointObserver.observe(Breakpoints.Handset).pipe(
map(({ matches }) => {
if (matches) {
return [
{ id: 'node', title: 'Node Details', cols: 10, rows: 1 },
{ id: 'balance', title: 'Balances', cols: 10, rows: 1 },
{ id: 'fee', title: 'Routing Fee Earned', cols: 10, rows: 1 },
{ id: 'status', title: 'Channel Status', cols: 10, rows: 1 },
{ id: 'capacity', title: 'Channel Capacity', cols: 10, rows: 1 }
];
}
return [
{ id: 'node', title: 'Node Details', cols: 3, rows: 1 },
{ id: 'balance', title: 'Balances', cols: 3, rows: 1 },
{ id: 'capacity', title: 'Channel Capacity', cols: 4, rows: 2 },
{ id: 'fee', title: 'Routing Fee Earned', cols: 3, rows: 1 },
{ id: 'status', title: 'Channel Status', cols: 3, rows: 1 }
];
})
);
constructor(private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>, private actions$: Actions) {
constructor(private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>, private actions$: Actions, private breakpointObserver: BreakpointObserver) {
switch (true) {
case (window.innerWidth <= 730):
this.view = [250, 352];
@ -104,7 +132,7 @@ export class HomeComponent implements OnInit, OnDestroy {
if (this.flgLoading[1] !== 'error') {
this.flgLoading[1] = (undefined !== this.fees.day_fee_sum) ? false : true;
}
this.balances.onchain = rtlStore.blockchainBalance.total_balance;
this.totalBalance = rtlStore.blockchainBalance.total_balance;
this.BTCtotalBalance = rtlStore.blockchainBalance.btc_total_balance;
if (this.flgLoading[2] !== 'error') {
@ -123,6 +151,7 @@ export class HomeComponent implements OnInit, OnDestroy {
}
if (rtlStore.totalLocalBalance >= 0 && rtlStore.totalRemoteBalance >= 0) {
this.balances.lightning = rtlStore.totalLocalBalance;
this.totalBalances = [{'name': 'Local Balance', 'value': rtlStore.totalLocalBalance}, {'name': 'Remote Balance', 'value': rtlStore.totalRemoteBalance}];
this.maxBalanceValue = (rtlStore.totalLocalBalance > rtlStore.totalRemoteBalance) ? rtlStore.totalLocalBalance : rtlStore.totalRemoteBalance;
this.flgTotalCalculated = true;
@ -130,10 +159,19 @@ export class HomeComponent implements OnInit, OnDestroy {
this.flgLoading[5] = false;
}
}
this.activeChannels = rtlStore.numberOfActiveChannels;
this.inactiveChannels = rtlStore.numberOfInactiveChannels;
this.pendingChannels = (undefined !== rtlStore.pendingChannels.pending_open_channels) ? rtlStore.pendingChannels.pending_open_channels.length : 0;
this.pendingChannels = this.pendingChannels + ((undefined !== rtlStore.pendingChannels.waiting_close_channels) ? rtlStore.pendingChannels.waiting_close_channels.length : 0);
this.pendingChannels = this.pendingChannels + ((undefined !== rtlStore.pendingChannels.pending_closing_channels) ? rtlStore.pendingChannels.pending_closing_channels.length : 0);
this.pendingChannels = this.pendingChannels + ((undefined !== rtlStore.pendingChannels.pending_force_closing_channels) ? rtlStore.pendingChannels.pending_force_closing_channels.length : 0);
console.warn(rtlStore.pendingChannels.total_limbo_balance);
this.channelsStatus = {
active: { channels: rtlStore.numberOfActiveChannels, capacity: rtlStore.totalCapacityActive },
inactive: { channels: rtlStore.numberOfInactiveChannels, capacity: rtlStore.totalCapacityInactive },
pending: { channels: this.pendingChannels, capacity: rtlStore.pendingChannels.total_limbo_balance },
closed: { channels: (rtlStore.closedChannels && rtlStore.closedChannels.length) ? rtlStore.closedChannels.length : 0, capacity: 0 }
};
if (rtlStore.totalLocalBalance >= 0 && rtlStore.totalRemoteBalance >= 0 && this.flgLoading[6] !== 'error') {
this.flgLoading[6] = false;
}
@ -144,6 +182,30 @@ export class HomeComponent implements OnInit, OnDestroy {
});
}
initializeCards() {
this.breakpointObserver.observe(Breakpoints.Handset).pipe(
map(({ matches }) => {
if (matches) {
return [
{ title: 'Card 1', cols: 1, rows: 1 },
{ title: 'Card 2', cols: 1, rows: 1 },
{ title: 'Card 3', cols: 1, rows: 1 },
{ title: 'Card 4', cols: 1, rows: 1 },
{ title: 'Card 4', cols: 1, rows: 1 }
];
}
return [
{ title: 'Card 1', cols: 3, rows: 1 },
{ title: 'Card 2', cols: 3, rows: 1 },
{ title: 'Card 3', cols: 4, rows: 2 },
{ title: 'Card 4', cols: 3, rows: 1 },
{ title: 'Card 4', cols: 3, rows: 1 }
];
})
);
}
ngOnDestroy() {
this.unsub.forEach(completeSub => {
completeSub.next();

@ -0,0 +1,14 @@
<div fxLayout="column" fxFlex="100" fxLayoutAlign="center start">
<div fxFlex="33">
<h4 fxLayoutAlign="start" class="font-bold-500">Alias</h4>
<div class="foreground-secondary-text" [ngStyle]="{'backgroundColor': information.color}">{{information.alias}}</div>
</div>
<div fxFlex="33">
<h4 fxLayoutAlign="start" class="font-bold-500">Implementation</h4>
<div class="foreground-secondary-text">{{information.lnImplementation + ' v' + information.version}}</div>
</div>
<div fxFlex="34">
<h4 fxLayoutAlign="start" class="font-bold-500">Chain</h4>
<span class="overflow-wrap foreground-secondary-text" *ngFor="let chain of chains">{{chain}}</span>
</div>
</div>

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

@ -0,0 +1,25 @@
import { Component, OnChanges, Input } from '@angular/core';
import { GetInfo } from '../../../shared/models/lndModels';
import { CommonService } from '../../../shared/services/common.service';
@Component({
selector: 'rtl-node-info',
templateUrl: './node-info.component.html',
styleUrls: ['./node-info.component.scss']
})
export class NodeInfoComponent implements OnChanges {
@Input() information: GetInfo;
public chains: Array<string> = [''];
constructor(private commonService: CommonService) { }
ngOnChanges() {
if(this.information && this.information.chains && this.information.chains.length > 0) {
this.chains = [''];
this.information.chains.forEach(chain => {
this.chains.push(this.commonService.titleCase(chain.chain) + ' ' + this.commonService.titleCase(chain.network));
});
}
}
}

@ -35,12 +35,29 @@ import { LNDUnlockedGuard } from '../shared/services/auth.guard';
import { ChannelOpenTableComponent } from './peers-channels/channels/channels-tables/channel-open-table/channel-open-table.component';
import { UnlockWalletComponent } from './wallet/unlock/unlock.component';
import { InitializeWalletComponent } from './wallet/initialize/initialize.component';
import { NodeInfoComponent } from './home/node-info/node-info.component';
import { BalancesInfoComponent } from './home/balances-info/balances-info.component';
import { FeeInfoComponent } from './home/fee-info/fee-info.component';
import { ChannelStatusInfoComponent } from './home/channel-status-info/channel-status-info.component';
import { ChannelCapacityInfoComponent } from './home/channel-capacity-info/channel-capacity-info.component';
import { MatGridListModule } from '@angular/material/grid-list';
import { MatCardModule } from '@angular/material/card';
import { MatMenuModule } from '@angular/material/menu';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { LayoutModule } from '@angular/cdk/layout';
@NgModule({
imports: [
CommonModule,
SharedModule,
LNDRouting
LNDRouting,
MatGridListModule,
MatCardModule,
MatMenuModule,
MatIconModule,
MatButtonModule,
LayoutModule
],
declarations: [
LNDRootComponent,
@ -70,7 +87,12 @@ import { InitializeWalletComponent } from './wallet/initialize/initialize.compon
ChannelsTablesComponent,
ChannelOpenTableComponent,
UnlockWalletComponent,
InitializeWalletComponent
InitializeWalletComponent,
NodeInfoComponent,
BalancesInfoComponent,
FeeInfoComponent,
ChannelStatusInfoComponent,
ChannelCapacityInfoComponent
],
providers: [
{ provide: LoggerService, useClass: ConsoleLoggerService },

@ -58,7 +58,6 @@ export class ChannelClosedTableComponent implements OnInit, OnDestroy {
}
ngOnInit() {
this.store.dispatch(new RTLActions.FetchChannels({routeParam: 'closed'}));
this.actions$.pipe(takeUntil(this.unsub[2]), filter((action) => action.type === RTLActions.RESET_LND_STORE)).subscribe((resetLndStore: RTLActions.ResetLNDStore) => {
this.store.dispatch(new RTLActions.FetchChannels({routeParam: 'closed'}));
});

@ -12,8 +12,8 @@
<th mat-header-cell *matHeaderCellDef mat-sort-header> Peer </th>
<td mat-cell *matCellDef="let channel">
<div class="ellipsis-parent">
<span *ngIf="channel.active" class="green-dot"></span>
<span *ngIf="!channel.active" class="yellow-dot"></span>
<span *ngIf="channel.active" class="dot green"></span>
<span *ngIf="!channel.active" class="dot yellow"></span>
<span class="ellipsis-child">{{channel.remote_alias || channel.remote_pubkey}}</span>
</div>
</td>

@ -58,6 +58,7 @@ export class LNDEffects implements OnDestroy {
payload: {}
};
} else {
info.lnImplementation = 'LND';
this.initializeRemainingData(info, action.payload.loadPage);
return {
type: RTLActions.SET_INFO,
@ -1048,6 +1049,7 @@ export class LNDEffects implements OnDestroy {
this.store.dispatch(new RTLActions.FetchNetwork());
this.store.dispatch(new RTLActions.FetchChannels({routeParam: 'all'}));
this.store.dispatch(new RTLActions.FetchChannels({routeParam: 'pending'}));
this.store.dispatch(new RTLActions.FetchChannels({routeParam: 'closed'}));
this.store.dispatch(new RTLActions.FetchInvoices({num_max_invoices: 10, reversed: true}));
this.store.dispatch(new RTLActions.FetchPayments());
let newRoute = this.location.path();

@ -1,7 +1,7 @@
import { SelNodeChild } from '../../shared/models/RTLconfig';
import { ErrorPayload } from '../../shared/models/errorPayload';
import {
GetInfo, Peer, AddressType, Fees, NetworkInfo, Balance, Channel, Payment, ListInvoices, PendingChannels, ClosedChannel, Transaction, SwitchRes, QueryRoutes
GetInfo, Peer, Fees, NetworkInfo, Balance, Channel, Payment, ListInvoices, PendingChannels, ClosedChannel, Transaction, SwitchRes, QueryRoutes
} from '../../shared/models/lndModels';
import * as RTLActions from '../../store/rtl.actions';
@ -20,6 +20,8 @@ export interface LNDState {
numberOfActiveChannels: number;
numberOfInactiveChannels: number;
numberOfPendingChannels: number;
totalCapacityActive: number;
totalCapacityInactive: number;
totalLocalBalance: number;
totalRemoteBalance: number;
totalInvoices: number;
@ -44,6 +46,8 @@ export const initLNDState: LNDState = {
numberOfActiveChannels: 0,
numberOfInactiveChannels: 0,
numberOfPendingChannels: -1,
totalCapacityActive: -1,
totalCapacityInactive: -1,
totalLocalBalance: -1,
totalRemoteBalance: -1,
totalInvoices: -1,
@ -133,7 +137,7 @@ export function LNDReducer(state = initLNDState, action: RTLActions.RTLActions)
numberOfPendingChannels: action.payload.pendingChannels,
};
case RTLActions.SET_CHANNELS:
let localBal = 0, remoteBal = 0, activeChannels = 0, inactiveChannels = 0;
let localBal = 0, remoteBal = 0, activeChannels = 0, inactiveChannels = 0, totalCapacityActive = 0, totalCapacityInactive = 0;
if (action.payload) {
action.payload.filter(channel => {
if (undefined !== channel.local_balance) {
@ -143,8 +147,10 @@ export function LNDReducer(state = initLNDState, action: RTLActions.RTLActions)
remoteBal = +remoteBal + +channel.remote_balance;
}
if (channel.active === true) {
totalCapacityActive = totalCapacityActive + +channel.capacity;
activeChannels = activeChannels + 1;
} else {
totalCapacityInactive = totalCapacityInactive + +channel.capacity;
inactiveChannels = inactiveChannels + 1;
}
});
@ -154,6 +160,8 @@ export function LNDReducer(state = initLNDState, action: RTLActions.RTLActions)
allChannels: action.payload,
numberOfActiveChannels: activeChannels,
numberOfInactiveChannels: inactiveChannels,
totalCapacityActive: totalCapacityActive,
totalCapacityInactive: totalCapacityInactive,
totalLocalBalance: localBal,
totalRemoteBalance: remoteBal
};

@ -47,8 +47,8 @@
<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="green-dot"></span>
<span *ngIf="!invoice.settled" class="yellow-dot"></span>
<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">

@ -26,6 +26,7 @@ export interface GetInfoCL {
fees_collected_msat?: string;
currency_unit?: string;
smaller_currency_unit?: string;
lnImplementation?: string;
}
export interface FeesCL {

@ -1,3 +1,15 @@
export interface ChannelStatus {
channels?: number;
capacity?:number;
}
export interface ChannelsStatus {
active?: ChannelStatus;
inactive?: ChannelStatus;
pending?: ChannelStatus;
closed?: ChannelStatus;
}
export interface AddressType {
addressId?: string;
addressTp?: string;
@ -142,6 +154,7 @@ export interface GetInfoChain {
export interface GetInfo {
identity_pubkey?: string;
alias?: string;
color?: string;
num_pending_channels?: number;
num_active_channels?: number;
num_peers?: number;
@ -155,6 +168,7 @@ export interface GetInfo {
version?: string;
currency_unit?: string;
smaller_currency_unit?: string;
lnImplementation?: string;
}
export interface GraphNode {

@ -23,4 +23,7 @@ $fa-icon-regular-size: 4rem;
$yellow-color: #ffbd2e;
$green-color: #28ca43;
$red-color: #c62828;
$grey-color: #AAAAAA;
$tiny-dot-size: 0.8rem;
$dot-size: 1.2rem;

@ -229,12 +229,16 @@ body {
color: #388e3c !important;
}
.yellow {
color: #ffd740 !important;
}
.red {
color: #c62828 !important;
}
.yellow {
color: #ffd740 !important;
.grey {
color: #CCCCCC !important;
}
.mat-dialog-container {
@ -417,6 +421,10 @@ body {
padding: 1rem !important;
}
.p-16 {
padding: 1.6rem !important;
}
.pt-2 {
padding-top: 2rem !important;
}
@ -793,22 +801,31 @@ table {
width:100%;
}
.green-dot {
display: inline-flex;
width: $dot-size;
height: $dot-size;
border-radius: $dot-size;
margin-right: 1rem;
background-color: $green-color;
}
.yellow-dot {
.dot {
display: inline-flex;
width: $dot-size;
height: $dot-size;
border-radius: $dot-size;
margin-right: 1rem;
background-color: $yellow-color;
&.tiny-dot {
width: $tiny-dot-size;
height: $tiny-dot-size;
border-radius: $tiny-dot-size;
margin-right: 0.6rem;
margin-bottom: 0.2rem;
}
&.green {
background-color: $green-color;
}
&.yellow {
background-color: $yellow-color;
}
&.red {
background-color: $red-color;
}
&.grey {
background-color: $grey-color;
}
}
.font-size-80 {

Loading…
Cancel
Save