App settings (#604)
Settings updates UTXO lease confirmation box Config settings without add new node Login and Settings pagepull/605/head
parent
a2ffd6ea05
commit
835c834b82
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
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
File diff suppressed because one or more lines are too long
@ -1 +0,0 @@
|
||||
!function(e){function r(r){for(var n,i,a=r[0],c=r[1],f=r[2],p=0,s=[];p<a.length;p++)i=a[p],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&s.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(l&&l(r);s.length;)s.shift()();return u.push.apply(u,f||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++)0!==o[t[a]]&&(n=!1);n&&(u.splice(r--,1),e=i(i.s=t[0]))}return e}var n={},o={0:0},u=[];function i(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,i),t.l=!0,t.exports}i.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise(function(r,n){t=o[e]=[r,n]});r.push(t[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+""+({}[e]||e)+"."+{1:"7b681204396e758e5e1b",5:"0b6506ded802705e1cf8",6:"2ec1dce4f108299e1066",7:"b47272f856369667bec5"}[e]+".js"}(e);var c=new Error;u=function(r){a.onerror=a.onload=null,clearTimeout(f);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var f=setTimeout(function(){u({type:"timeout",target:a})},12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(r)},i.m=e,i.c=n,i.d=function(e,r,t){i.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,r){if(1&r&&(e=i(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)i.d(t,n,(function(r){return e[r]}).bind(null,n));return t},i.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(r,"a",r),r},i.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},i.p="",i.oe=function(e){throw console.error(e),e};var a=window.webpackJsonp=window.webpackJsonp||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var f=0;f<a.length;f++)r(a[f]);var l=c;t()}([]);
|
@ -0,0 +1 @@
|
||||
!function(e){function r(r){for(var n,c,a=r[0],i=r[1],f=r[2],p=0,s=[];p<a.length;p++)c=a[p],Object.prototype.hasOwnProperty.call(o,c)&&o[c]&&s.push(o[c][0]),o[c]=0;for(n in i)Object.prototype.hasOwnProperty.call(i,n)&&(e[n]=i[n]);for(l&&l(r);s.length;)s.shift()();return u.push.apply(u,f||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++)0!==o[t[a]]&&(n=!1);n&&(u.splice(r--,1),e=c(c.s=t[0]))}return e}var n={},o={0:0},u=[];function c(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,c),t.l=!0,t.exports}c.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise(function(r,n){t=o[e]=[r,n]});r.push(t[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,c.nc&&a.setAttribute("nonce",c.nc),a.src=function(e){return c.p+""+({}[e]||e)+"."+{1:"731a8de203edc88f38d8",5:"3cc9f14aefc4012c6f28",6:"9b6fc317b012c5f7f9cf",7:"3f5e1768ebd105934cfb"}[e]+".js"}(e);var i=new Error;u=function(r){a.onerror=a.onload=null,clearTimeout(f);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;i.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",i.name="ChunkLoadError",i.type=n,i.request=u,t[1](i)}o[e]=void 0}};var f=setTimeout(function(){u({type:"timeout",target:a})},12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(r)},c.m=e,c.c=n,c.d=function(e,r,t){c.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},c.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},c.t=function(e,r){if(1&r&&(e=c(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(c.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)c.d(t,n,(function(r){return e[r]}).bind(null,n));return t},c.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return c.d(r,"a",r),r},c.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},c.p="",c.oe=function(e){throw console.error(e),e};var a=window.webpackJsonp=window.webpackJsonp||[],i=a.push.bind(a);a.push=r,a=a.slice();for(var f=0;f<a.length;f++)r(a[f]);var l=i;t()}([]);
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,2 +1,4 @@
|
||||
<mat-progress-bar *ngIf="loading" color="primary" mode="indeterminate"></mat-progress-bar>
|
||||
<router-outlet *ngIf="!loading"></router-outlet>
|
||||
<div [@routeAnimation]="outlet && outlet.activatedRouteData" class="inner-sidenav-content" fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch">
|
||||
<mat-progress-bar *ngIf="loading" color="primary" mode="indeterminate"></mat-progress-bar>
|
||||
<router-outlet #outlet="outlet"></router-outlet>
|
||||
</div>
|
||||
|
@ -1,2 +1,4 @@
|
||||
<mat-progress-bar *ngIf="loading" color="primary" mode="indeterminate"></mat-progress-bar>
|
||||
<router-outlet *ngIf="!loading"></router-outlet>
|
||||
<div [@routeAnimation]="outlet && outlet.activatedRouteData" class="inner-sidenav-content" fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch">
|
||||
<mat-progress-bar *ngIf="loading" color="primary" mode="indeterminate"></mat-progress-bar>
|
||||
<router-outlet #outlet="outlet"></router-outlet>
|
||||
</div>
|
||||
|
@ -1,2 +1,4 @@
|
||||
<mat-progress-bar *ngIf="loading" color="primary" mode="indeterminate"></mat-progress-bar>
|
||||
<router-outlet *ngIf="!loading"></router-outlet>
|
||||
<div [@routeAnimation]="outlet && outlet.activatedRouteData" class="inner-sidenav-content" fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch">
|
||||
<mat-progress-bar *ngIf="loading" color="primary" mode="indeterminate"></mat-progress-bar>
|
||||
<router-outlet #outlet="outlet"></router-outlet>
|
||||
</div>
|
||||
|
@ -0,0 +1,17 @@
|
||||
import { transition, trigger, query, style, animate, group } from '@angular/animations';
|
||||
|
||||
export const routeAnimation = trigger('routeAnimation', [
|
||||
transition('* => *', [
|
||||
query(':enter, :leave', style({ position: 'fixed', width: '100%' }), { optional: true }),
|
||||
group([
|
||||
query(':enter', [
|
||||
style({ transform: 'translateX(100%)' }),
|
||||
animate('1000ms ease-in-out', style({ transform: 'translateX(0%)' }))
|
||||
], { optional: true }),
|
||||
query(':leave', [
|
||||
style({ transform: 'translateX(0%)' }),
|
||||
animate('1000ms ease-in-out', style({ transform: 'translateX(-100%)' }))
|
||||
], { optional: true }),
|
||||
])
|
||||
])
|
||||
]);
|
@ -1,23 +1,35 @@
|
||||
<div fxLayout="row" fxLayoutAlign="start center" class="page-title-container">
|
||||
<fa-icon [icon]="faUnlockAlt" class="page-title-img mr-1"></fa-icon>
|
||||
<span class="page-title">Login to RTL</span>
|
||||
</div>
|
||||
<div fxLayout="column">
|
||||
<div class="padding-gap">
|
||||
<mat-card>
|
||||
<mat-card-content class="card-content-gap">
|
||||
<form (ngSubmit)="onLogin()" #loginForm="ngForm" fxLayout="column" fxLayout.gt-sm="row wrap" fxLayoutAlign="start" fxLayoutAlign.gt-sm="space-between">
|
||||
<mat-form-field fxFlex="100" fxLayoutAlign="start">
|
||||
<input autoFocus matInput placeholder="Password" type="password" id="password" name="password" [(ngModel)]="password" tabindex="1" required>
|
||||
<mat-error *ngIf="!password">Password is required.</mat-error>
|
||||
</mat-form-field>
|
||||
<p *ngIf="loginErrorMessage !== ''" fxFlex="100" class="color-warn" fxLayoutAlign="start center"><mat-icon class="mr-1 icon-small">close</mat-icon>{{loginErrorMessage}}</p>
|
||||
<div fxLayout="row" class="mt-2">
|
||||
<button class="mr-1" mat-stroked-button color="primary" tabindex="2" type="reset" (click)="resetData()">Clear</button>
|
||||
<button mat-flat-button color="primary" tabindex="3" type="submit">Login</button>
|
||||
</div>
|
||||
</form>
|
||||
</mat-card-content>
|
||||
<div fxLayout="column" fxFlex="100" fxLayoutAlign="center stretch" class="login-container">
|
||||
<div fxLayout="row" fxFlex="50" fxLayoutAlign="center stretch">
|
||||
<mat-card fxLayout="row" fxFlex="50" fxLayoutAlign="center stretch">
|
||||
<div fxLayout="row" fxFlex="100" fxLayoutAlign="stretch stretch">
|
||||
<div fxFlex="35" fxLayoutAlign="center center" class="bg-primary">
|
||||
<img src="assets/images/RTL-Horse-BY.svg" alt="RTL Logo" class="rtl-logo-svg">
|
||||
</div>
|
||||
<div fxFlex="65" fxLayout="column" fxLayoutAlign="center stretch" class="padding-gap-large pl-3">
|
||||
<mat-card-header fxLayout="row" fxLayoutAlign="center center" class="page-title-container p-0">
|
||||
<mat-card-title class="font-size-300 font-bold-500">
|
||||
<span class="page-title">Welcome</span>
|
||||
</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="mt-5px mb-0 pr-2">
|
||||
<form #loginForm="ngForm" fxLayout="column" fxLayout.gt-sm="row wrap" fxLayoutAlign="start" fxLayoutAlign.gt-sm="space-between">
|
||||
<mat-form-field fxFlex="100" fxLayoutAlign="start">
|
||||
<input autoFocus matInput placeholder="Password" [type]="flgShow ? 'text' : 'password'" id="password" name="password" [(ngModel)]="password" tabindex="1" required>
|
||||
<mat-icon tabindex="2" matSuffix (click)="flgShow = !flgShow">{{flgShow ? 'visibility_off' : 'visibility'}}</mat-icon>
|
||||
<mat-error *ngIf="!password">Password is required.</mat-error>
|
||||
</mat-form-field>
|
||||
<p *ngIf="loginErrorMessage !== ''" fxFlex="100" class="color-warn pre-wrap" fxLayoutAlign="start start">
|
||||
<mat-icon class="mr-1 icon-small">close</mat-icon>
|
||||
{{loginErrorMessage}}
|
||||
</p>
|
||||
<div fxLayout="row" fxFlex="100" fxLayoutAlign="end center" class="mt-2">
|
||||
<button class="mr-1" mat-stroked-button color="primary" tabindex="2" type="reset" (click)="resetData()">Clear</button>
|
||||
<button mat-flat-button color="primary" tabindex="3" type="submit" (click)="onLogin()">Login</button>
|
||||
</div>
|
||||
</form>
|
||||
</mat-card-content>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -0,0 +1,17 @@
|
||||
.login-container {
|
||||
height: 90vh;
|
||||
|
||||
& .mat-card {
|
||||
height: 30rem;
|
||||
}
|
||||
|
||||
& .rtl-logo-svg {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
& .material-icons.mat-icon {
|
||||
font-size: 120%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
}
|
@ -1,26 +1,12 @@
|
||||
.mat-menu-panel.top-menu{
|
||||
.mat-toolbar, .mat-toolbar-row{
|
||||
height: 100px !important;
|
||||
padding: 0 16px !important;
|
||||
}
|
||||
.info-block{
|
||||
width: 230px;
|
||||
p{
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
.mat-menu-item{
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
}
|
||||
.mat-menu-content {
|
||||
p{
|
||||
.mat-menu-content {
|
||||
cursor: default;
|
||||
p.mat-menu-item {
|
||||
cursor: default;
|
||||
fa-icon, span, div {
|
||||
cursor: default;
|
||||
mat-icon, span, div {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
p.mat-menu-item:hover {
|
||||
cursor: default !important;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,19 @@
|
||||
<div fxLayout="row" fxLayoutAlign="start center" class="page-title-container">
|
||||
<fa-icon [icon]="faTools" class="page-title-img mr-1"></fa-icon>
|
||||
<span class="page-title">Node Config</span>
|
||||
</div>
|
||||
<div fxLayout="column" class="padding-gap-x">
|
||||
<mat-card>
|
||||
<mat-card-content fxLayout="column">
|
||||
<nav mat-tab-nav-bar>
|
||||
<div role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === links[0].link" (click)="activeLink = links[0].link" routerLink="{{links[0].link}}">{{links[0].name}}</div>
|
||||
<div role="tab" mat-tab-link *ngIf="selNode.lnImplementation.toUpperCase() === 'LND'" class="mat-tab-label" [active]="activeLink === links[1].link" (click)="activeLink = links[1].link" routerLink="{{links[1].link}}" [state]="{ initial: false }">{{links[1].name}}</div>
|
||||
<div role="tab" mat-tab-link *ngIf="showLnConfig" class="mat-tab-label" [active]="activeLink === links[2].link" (click)="activeLink = links[2].link" routerLink="{{links[2].link}}">{{links[2].name}}</div>
|
||||
</nav>
|
||||
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="mat-tab-body-wrapper">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
|
@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { NodeConfigComponent } from './node-config.component';
|
||||
|
||||
describe('NodeConfigComponent', () => {
|
||||
let component: NodeConfigComponent;
|
||||
let fixture: ComponentFixture<NodeConfigComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ NodeConfigComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(NodeConfigComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,67 @@
|
||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { Router, ResolveEnd } from '@angular/router';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil, filter } from 'rxjs/operators';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { faTools } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import { ConfigSettingsNode, RTLConfiguration } from '../../models/RTLconfig';
|
||||
import * as fromRTLReducer from '../../../store/rtl.reducers';
|
||||
|
||||
@Component({
|
||||
selector: 'rtl-node-config',
|
||||
templateUrl: './node-config.component.html',
|
||||
styleUrls: ['./node-config.component.scss']
|
||||
})
|
||||
export class NodeConfigComponent implements OnInit, OnDestroy{
|
||||
public faTools = faTools;
|
||||
public showLnConfig = false;
|
||||
public selNode: ConfigSettingsNode;
|
||||
public appConfig: RTLConfiguration;
|
||||
public lnImplementationStr = '';
|
||||
public links = [{link: 'node', name: 'Node'}, {link: 'services', name: 'Services'}, {link: 'lnconfig', name: this.lnImplementationStr}];
|
||||
public activeLink = '';
|
||||
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()];
|
||||
|
||||
constructor(private store: Store<fromRTLReducer.RTLState>, private router: Router) {}
|
||||
|
||||
ngOnInit() {
|
||||
let linkFound = this.links.find(link => this.router.url.includes(link.link));
|
||||
this.activeLink = linkFound ? linkFound.link : this.links[0].link;
|
||||
this.router.events.pipe(takeUntil(this.unSubs[0]), filter(e => e instanceof ResolveEnd))
|
||||
.subscribe((value: ResolveEnd) => {
|
||||
let linkFound = this.links.find(link => value.urlAfterRedirects.includes(link.link));
|
||||
this.activeLink = linkFound ? linkFound.link : this.links[0].link;
|
||||
});
|
||||
this.store.select('root').pipe(takeUntil(this.unSubs[1]))
|
||||
.subscribe((rtlStore) => {
|
||||
this.showLnConfig = false;
|
||||
this.appConfig = rtlStore.appConfig;
|
||||
this.selNode = rtlStore.selNode;
|
||||
switch (this.selNode.lnImplementation.toUpperCase()) {
|
||||
case 'CLT':
|
||||
this.lnImplementationStr = 'C-Lightning Config';
|
||||
break;
|
||||
|
||||
case 'ECL':
|
||||
this.lnImplementationStr = 'Eclair Config';
|
||||
break;
|
||||
|
||||
default:
|
||||
this.lnImplementationStr = 'LND Config';
|
||||
break;
|
||||
}
|
||||
if (this.selNode.authentication && this.selNode.authentication.configPath && this.selNode.authentication.configPath.trim() !== '') {
|
||||
this.links[2].name = this.lnImplementationStr;
|
||||
this.showLnConfig = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.unSubs.forEach(completeSub => {
|
||||
completeSub.next();
|
||||
completeSub.complete();
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
<div [perfectScrollbar] fxLayout="column" fxFlex="100">
|
||||
<form fxLayout="column" fxLayoutAlign="start stretch" class="settings-container page-sub-title-container mt-1" #form="ngForm">
|
||||
<div fxLayout="row">
|
||||
<fa-icon [icon]="faMoneyBillAlt" class="page-title-img mr-1"></fa-icon>
|
||||
<span class="page-title">Balance Display</span>
|
||||
</div>
|
||||
<div fxLayout="column" fxLayoutAlign="start stretch" class="mt-1 bordered-box padding-gap-large">
|
||||
<div fxFlex="100" class="alert alert-warn">
|
||||
<fa-icon [icon]="faExclamationTriangle" class="mr-1 alert-icon"></fa-icon>
|
||||
<span>Fiat conversion calls <strong><a href="https://www.blockchain.com/api/exchange_rates_api" target="blank">Blockchain.com</a></strong> API to get conversion rates.</span>
|
||||
</div>
|
||||
<div fxLayout="row wrap" fxLayoutAlign="start center">
|
||||
<mat-slide-toggle tabindex="2" color="primary" [(ngModel)]="selNode.settings.fiatConversion" (change)="selNode.settings.currencyUnit = !$event.checked ? null : selNode.settings.currencyUnit" name="fiatConversion">Enable Fiat Conversion</mat-slide-toggle>
|
||||
<mat-form-field>
|
||||
<mat-select autoFocus [(ngModel)]="selNode.settings.currencyUnit" (selectionChange)="onCurrencyChange($event)" placeholder="Fiat Currency" [disabled]="!selNode.settings.fiatConversion" tabindex="3" [required]="selNode.settings.fiatConversion" name="currencyUnit" #currencyUnit="ngModel">
|
||||
<mat-option *ngFor="let currencyUnit of currencyUnits" [value]="currencyUnit.id">
|
||||
{{currencyUnit.id}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="selNode.settings.fiatConversion && !selNode.settings.currencyUnit">Currency unit is required.</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch">
|
||||
<div fxLayout="row wrap" fxLayoutAlign="start start" fxLayout.gt-sm="column" fxFlex="100" fxLayoutAlign.gt-sm="space-between stretch" class="settings-container page-sub-title-container mt-1">
|
||||
<div class="mt-1">
|
||||
<fa-icon [icon]="faPaintBrush" class="page-title-img mr-1"></fa-icon>
|
||||
<span class="page-title">Customization</span>
|
||||
</div>
|
||||
<div fxLayout="column" fxLayoutAlign="start stretch" class="mt-1 bordered-box padding-gap-large">
|
||||
<div fxLayout="column" fxLayoutAlign="start stretch" fxFlex="100">
|
||||
<div fxLayout="row" fxFlex="100" class="alert alert-info mb-0">
|
||||
<fa-icon [icon]="faInfoCircle" class="mr-1 alert-icon"></fa-icon>
|
||||
<span>Dashboard layout will be tailored based on the role selected to better serve its needs.</span>
|
||||
</div>
|
||||
<div fxLayout="column" fxLayoutAlign="start start" fxFlex="100">
|
||||
<h4>Dashboard Layout</h4>
|
||||
<mat-radio-group color="primary" [(ngModel)]="selNode.settings.userPersona" tabindex="1" name="userPersona">
|
||||
<mat-radio-button *ngFor="let userPersona of userPersonas" [value]="userPersona" [checked]="selNode.settings.userPersona === userPersona" class="mr-4">
|
||||
{{userPersona | titlecase}}
|
||||
</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider [inset]="true" class="mt-1"></mat-divider>
|
||||
<div fxLayout="column" fxLayout.gt-xs="row" fxFlex="100" fxLayoutAlign="space-between stretch" fxLayoutAlign.gt-xs="start stretch">
|
||||
<div fxFlex.gt-xs="20" fxFlex.gt-md="15" fxLayout="column" fxLayoutAlign="space-between stretch">
|
||||
<h4>Mode</h4>
|
||||
<mat-radio-group color="primary" [(ngModel)]="selectedThemeMode" (change)="chooseThemeMode()" name="themeMode">
|
||||
<mat-radio-button tabindex="5" *ngFor="let themeMode of themeModes" [value]="themeMode" [ngClass]="{'mr-4': screenSize === screenSizeEnum.XS || screenSize === screenSizeEnum.SM}">{{themeMode.name}}
|
||||
</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider [inset]="true" class="mt-1"></mat-divider>
|
||||
<div fxLayout="column" fxLayout.gt-xs="row" fxFlex="100" fxLayoutAlign="space-between stretch" fxLayoutAlign.gt-xs="start stretch">
|
||||
<div fxLayout="column" fxFlex.gt-xs="50" fxFlex.gt-md="40" fxLayoutAlign="space-between stretch">
|
||||
<h4>Themes</h4>
|
||||
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between start">
|
||||
<span *ngFor="let themeColor of themeColors" fxLayout="row">
|
||||
<div tabindex="9" [class]="themeColor.id | lowercase" [ngClass]="{'skin': true, 'selected-color': selectedThemeColor === themeColor.id}" (click)="changeThemeColor(themeColor.id)"></div>
|
||||
{{themeColor.name}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div fxLayout="row" class="mt-1">
|
||||
<button class="mr-1" mat-stroked-button color="primary" (click)="onResetSettings()" tabindex="10">Reset</button>
|
||||
<button mat-flat-button color="primary" (click)="onUpdateSettings()" tabindex="11">Update</button>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { NodeSettingsComponent } from './node-settings.component';
|
||||
|
||||
describe('NodeSettingsComponent', () => {
|
||||
let component: NodeSettingsComponent;
|
||||
let fixture: ComponentFixture<NodeSettingsComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ NodeSettingsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(NodeSettingsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,112 @@
|
||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { faMoneyBillAlt, faPaintBrush, faInfoCircle, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import { CURRENCY_UNITS, UserPersonaEnum, ScreenSizeEnum, FIAT_CURRENCY_UNITS, NODE_SETTINGS } from '../../../services/consts-enums-functions';
|
||||
import { ConfigSettingsNode, Settings, RTLConfiguration, GetInfoRoot } from '../../../models/RTLconfig';
|
||||
import { LoggerService } from '../../../services/logger.service';
|
||||
import { CommonService } from '../../../services/common.service';
|
||||
|
||||
import * as ECLActions from '../../../../eclair/store/ecl.actions';
|
||||
import * as CLActions from '../../../../clightning/store/cl.actions';
|
||||
import * as LNDActions from '../../../../lnd/store/lnd.actions';
|
||||
import * as RTLActions from '../../../../store/rtl.actions';
|
||||
import * as fromRTLReducer from '../../../../store/rtl.reducers';
|
||||
|
||||
@Component({
|
||||
selector: 'rtl-node-settings',
|
||||
templateUrl: './node-settings.component.html',
|
||||
styleUrls: ['./node-settings.component.scss']
|
||||
})
|
||||
export class NodeSettingsComponent implements OnInit, OnDestroy {
|
||||
public faExclamationTriangle = faExclamationTriangle;
|
||||
public faMoneyBillAlt = faMoneyBillAlt;
|
||||
public faPaintBrush = faPaintBrush;
|
||||
public faInfoCircle = faInfoCircle;
|
||||
public selNode: ConfigSettingsNode;
|
||||
public information: GetInfoRoot = {};
|
||||
public userPersonas = [UserPersonaEnum.OPERATOR, UserPersonaEnum.MERCHANT];
|
||||
public currencyUnits = FIAT_CURRENCY_UNITS;
|
||||
public themeModes = NODE_SETTINGS.modes;
|
||||
public themeColors = NODE_SETTINGS.themes;
|
||||
public selectedThemeMode = NODE_SETTINGS.modes[0];
|
||||
public selectedThemeColor = NODE_SETTINGS.themes[0].id;
|
||||
public currencyUnit = 'BTC';
|
||||
public smallerCurrencyUnit = 'Sats';
|
||||
public showSettingOption = true;
|
||||
public appConfig: RTLConfiguration;
|
||||
public previousSettings: Settings;
|
||||
public screenSize = '';
|
||||
public screenSizeEnum = ScreenSizeEnum;
|
||||
unSubs: Array<Subject<void>> = [new Subject(), new Subject()];
|
||||
|
||||
constructor(private logger: LoggerService, private commonService: CommonService, private store: Store<fromRTLReducer.RTLState>) {
|
||||
this.screenSize = this.commonService.getScreenSize();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.store.select('root')
|
||||
.pipe(takeUntil(this.unSubs[0]))
|
||||
.subscribe((rtlStore) => {
|
||||
this.appConfig = rtlStore.appConfig;
|
||||
this.selNode = rtlStore.selNode;
|
||||
this.selectedThemeMode = this.themeModes.find(themeMode => this.selNode.settings.themeMode === themeMode.id);
|
||||
this.selectedThemeColor = this.selNode.settings.themeColor;
|
||||
this.information = rtlStore.nodeData;
|
||||
this.smallerCurrencyUnit = ( this.information && this.information.smaller_currency_unit) ? this.information.smaller_currency_unit : 'Sats';
|
||||
this.currencyUnit = ( this.information && this.information.currency_unit) ? this.information.currency_unit : 'BTC';
|
||||
if(!this.selNode.settings.fiatConversion) {
|
||||
this.selNode.settings.currencyUnit = null;
|
||||
}
|
||||
this.previousSettings = JSON.parse(JSON.stringify(this.selNode.settings));
|
||||
this.logger.info(rtlStore);
|
||||
});
|
||||
}
|
||||
|
||||
onCurrencyChange(event: any) {
|
||||
this.selNode.settings.currencyUnits = [...CURRENCY_UNITS, event.value];
|
||||
this.store.dispatch(new LNDActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: event.value, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
|
||||
this.store.dispatch(new CLActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: event.value, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
|
||||
this.store.dispatch(new ECLActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: event.value, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
|
||||
}
|
||||
|
||||
toggleSettings(toggleField: string, event?: any) {
|
||||
this.selNode.settings[toggleField] = !this.selNode.settings[toggleField];
|
||||
}
|
||||
|
||||
changeThemeColor(newThemeColor: string) {
|
||||
this.selectedThemeColor = newThemeColor;
|
||||
this.selNode.settings.themeColor = newThemeColor;
|
||||
}
|
||||
|
||||
chooseThemeMode() {
|
||||
this.selNode.settings.themeMode = this.selectedThemeMode.id;
|
||||
}
|
||||
|
||||
onUpdateSettings():boolean|void {
|
||||
if(this.selNode.settings.fiatConversion && !this.selNode.settings.currencyUnit) { return true; }
|
||||
this.logger.info(this.selNode.settings);
|
||||
this.store.dispatch(new RTLActions.OpenSpinner('Updating Node Settings...'));
|
||||
this.store.dispatch(new RTLActions.SaveSettings({settings: this.selNode.settings}));
|
||||
this.store.dispatch(new LNDActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: this.selNode.settings.currencyUnit, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
|
||||
this.store.dispatch(new CLActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: this.selNode.settings.currencyUnit, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
|
||||
this.store.dispatch(new ECLActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: this.selNode.settings.currencyUnit, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
|
||||
}
|
||||
|
||||
onResetSettings() {
|
||||
this.selNode.settings = this.previousSettings;
|
||||
this.selectedThemeMode = this.themeModes.find(themeMode => themeMode.id === this.previousSettings.themeMode);
|
||||
this.selectedThemeColor = this.previousSettings.themeColor;
|
||||
this.store.dispatch(new RTLActions.SetSelelectedNode({ lnNode: this.selNode, isInitialSetup: true }));
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.unSubs.forEach(unsub => {
|
||||
unsub.next();
|
||||
unsub.complete();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
<div [perfectScrollbar] fxLayout="column" fxFlex="100">
|
||||
<form fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch" class="settings-container page-sub-title-container mt-1" #form="ngForm">
|
||||
<div fxLayout="column" fxFlex="50" fxLayoutAlign="start stretch">
|
||||
<mat-slide-toggle autoFocus class="mb-1" tabindex="1" color="primary" [(ngModel)]="enableBoltz" (change)="onEnableServiceChanged($event)" name="boltz">Enable Boltz Service</mat-slide-toggle>
|
||||
<mat-form-field class="mb-1">
|
||||
<input matInput placeholder="Boltz Server URL" type="text" id="boltzServerUrl" name="boltzServerUrl" [(ngModel)]="selNode.settings.boltzServerUrl" tabindex="2" [required]="enableBoltz" [disabled]="!enableBoltz">
|
||||
<mat-hint>Service url for boltz server REST APIs, eg. https://localhost:9003</mat-hint>
|
||||
<mat-error *ngIf="!selNode.settings.boltzServerUrl && enableBoltz">Boltz server URL is required.</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<input matInput placeholder="Boltz Macaroon Path" type="text" id="boltzMacaroonPath" name="boltzMacaroonPath" [(ngModel)]="selNode.authentication.boltzMacaroonPath" tabindex="3" [required]="enableBoltz" [disabled]="!enableBoltz">
|
||||
<mat-hint>Path for the folder containing boltz 'admin.macaroon', eg. D:\\xyz\\AppData\\Boltz\\testnet</mat-hint>
|
||||
<mat-error *ngIf="!selNode.authentication.boltzMacaroonPath && enableBoltz">Boltz macaroon path is required.</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</form>
|
||||
<div fxLayout="row" class="mt-2">
|
||||
<button class="mr-1" mat-stroked-button color="primary" type="reset" (click)="onReset()" tabindex="4">Reset</button>
|
||||
<button mat-flat-button color="primary" type="submit" (click)="onUpdateService()" tabindex="5">Update</button>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,3 @@
|
||||
h4 {
|
||||
word-break: break-word;
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { ServicesEnum } from '../../../../services/consts-enums-functions';
|
||||
import { ConfigSettingsNode, RTLConfiguration } from '../../../../models/RTLconfig';
|
||||
import { LoggerService } from '../../../../services/logger.service';
|
||||
|
||||
import * as ECLActions from '../../../../../eclair/store/ecl.actions';
|
||||
import * as CLActions from '../../../../../clightning/store/cl.actions';
|
||||
import * as LNDActions from '../../../../../lnd/store/lnd.actions';
|
||||
import * as RTLActions from '../../../../../store/rtl.actions';
|
||||
import * as fromRTLReducer from '../../../../../store/rtl.reducers';
|
||||
|
||||
@Component({
|
||||
selector: 'rtl-boltz-service-settings',
|
||||
templateUrl: './boltz-service-settings.component.html',
|
||||
styleUrls: ['./boltz-service-settings.component.scss']
|
||||
})
|
||||
export class BoltzServiceSettingsComponent implements OnInit, OnDestroy {
|
||||
public appConfig: RTLConfiguration;
|
||||
public selNode: ConfigSettingsNode;
|
||||
public previousSelNode: ConfigSettingsNode;
|
||||
public enableBoltz = false;
|
||||
unSubs: Array<Subject<void>> = [new Subject(), new Subject()];
|
||||
|
||||
constructor(private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.store.select('root')
|
||||
.pipe(takeUntil(this.unSubs[0]))
|
||||
.subscribe((rtlStore) => {
|
||||
this.appConfig = rtlStore.appConfig;
|
||||
this.selNode = rtlStore.selNode;
|
||||
this.enableBoltz = rtlStore.selNode.settings.boltzServerUrl && rtlStore.selNode.settings.boltzServerUrl.trim() !== '';
|
||||
this.previousSelNode = JSON.parse(JSON.stringify(this.selNode));
|
||||
this.logger.info(rtlStore);
|
||||
});
|
||||
}
|
||||
|
||||
onEnableServiceChanged(event) {
|
||||
this.enableBoltz = event.checked;
|
||||
if (!this.enableBoltz) {
|
||||
this.selNode.authentication.boltzMacaroonPath = '';
|
||||
this.selNode.settings.boltzServerUrl = '';
|
||||
}
|
||||
}
|
||||
|
||||
onUpdateService():boolean|void {
|
||||
if(this.enableBoltz && (!this.selNode.settings.boltzServerUrl || this.selNode.settings.boltzServerUrl.trim() === '' || !this.selNode.authentication.boltzMacaroonPath || this.selNode.authentication.boltzMacaroonPath.trim() === '')) { return true; }
|
||||
this.logger.info(this.selNode);
|
||||
this.store.dispatch(new RTLActions.OpenSpinner('Updating Boltz Service Settings...'));
|
||||
this.store.dispatch(new RTLActions.UpdateServiceSettings({service: ServicesEnum.BOLTZ, settings: { enable: this.enableBoltz, serverUrl: this.selNode.settings.boltzServerUrl, macaroonPath: this.selNode.authentication.boltzMacaroonPath }}));
|
||||
this.store.dispatch(new LNDActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: this.selNode.settings.currencyUnit, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
|
||||
this.store.dispatch(new CLActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: this.selNode.settings.currencyUnit, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
|
||||
this.store.dispatch(new ECLActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: this.selNode.settings.currencyUnit, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
|
||||
}
|
||||
|
||||
onReset() {
|
||||
this.selNode = JSON.parse(JSON.stringify(this.previousSelNode));
|
||||
this.enableBoltz = this.selNode.settings.boltzServerUrl && this.selNode.settings.boltzServerUrl.trim() !== '';
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.unSubs.forEach(unsub => {
|
||||
unsub.next();
|
||||
unsub.complete();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
<div [perfectScrollbar] fxLayout="column" fxFlex="100">
|
||||
<form fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch" class="settings-container page-sub-title-container mt-1" #form="ngForm">
|
||||
<div fxLayout="column" fxFlex="50" fxLayoutAlign="start stretch">
|
||||
<mat-slide-toggle autoFocus class="mb-1" tabindex="1" color="primary" [(ngModel)]="enableLoop" (change)="onEnableServiceChanged($event)" name="loop">Enable Loop Service</mat-slide-toggle>
|
||||
<mat-form-field class="mb-1">
|
||||
<input matInput placeholder="Loop Server URL" type="text" id="swapServerUrl" name="swapServerUrl" [(ngModel)]="selNode.settings.swapServerUrl" tabindex="2" [required]="enableLoop" [disabled]="!enableLoop">
|
||||
<mat-hint>Service url for loop server REST APIs, eg. https://localhost:8081</mat-hint>
|
||||
<mat-error *ngIf="!selNode.settings.swapServerUrl && enableLoop">Loop server URL is required.</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<input matInput placeholder="Loop Macaroon Path" type="text" id="swapMacaroonPath" name="swapMacaroonPath" [(ngModel)]="selNode.authentication.swapMacaroonPath" tabindex="3" [required]="enableLoop" [disabled]="!enableLoop">
|
||||
<mat-hint>Path for the folder containing service 'loop.macaroon', eg. D:\\xyz\\AppData\\Local\\Loop\\testnet</mat-hint>
|
||||
<mat-error *ngIf="!selNode.authentication.swapMacaroonPath && enableLoop">Loop macaroon path is required.</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</form>
|
||||
<div fxLayout="row" class="mt-2">
|
||||
<button class="mr-1" mat-stroked-button color="primary" type="reset" (click)="onReset()" tabindex="4">Reset</button>
|
||||
<button mat-flat-button color="primary" type="submit" (click)="onUpdateService()" tabindex="5">Update</button>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,3 @@
|
||||
h4 {
|
||||
word-break: break-word;
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { ServicesEnum } from '../../../../services/consts-enums-functions';
|
||||
import { ConfigSettingsNode, RTLConfiguration } from '../../../../models/RTLconfig';
|
||||
import { LoggerService } from '../../../../services/logger.service';
|
||||
|
||||
import * as ECLActions from '../../../../../eclair/store/ecl.actions';
|
||||
import * as CLActions from '../../../../../clightning/store/cl.actions';
|
||||
import * as LNDActions from '../../../../../lnd/store/lnd.actions';
|
||||
import * as RTLActions from '../../../../../store/rtl.actions';
|
||||
import * as fromRTLReducer from '../../../../../store/rtl.reducers';
|
||||
|
||||
@Component({
|
||||
selector: 'rtl-loop-service-settings',
|
||||
templateUrl: './loop-service-settings.component.html',
|
||||
styleUrls: ['./loop-service-settings.component.scss']
|
||||
})
|
||||
export class LoopServiceSettingsComponent implements OnInit, OnDestroy {
|
||||
public appConfig: RTLConfiguration;
|
||||
public selNode: ConfigSettingsNode;
|
||||
public previousSelNode: ConfigSettingsNode;
|
||||
public enableLoop = false;
|
||||
unSubs: Array<Subject<void>> = [new Subject(), new Subject()];
|
||||
|
||||
constructor(private logger: LoggerService, private store: Store<fromRTLReducer.RTLState>) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.store.select('root')
|
||||
.pipe(takeUntil(this.unSubs[0]))
|
||||
.subscribe((rtlStore) => {
|
||||
this.appConfig = rtlStore.appConfig;
|
||||
this.selNode = rtlStore.selNode;
|
||||
this.enableLoop = rtlStore.selNode.settings.swapServerUrl && rtlStore.selNode.settings.swapServerUrl.trim() !== '';
|
||||
this.previousSelNode = JSON.parse(JSON.stringify(this.selNode));
|
||||
this.logger.info(rtlStore);
|
||||
});
|
||||
}
|
||||
|
||||
onEnableServiceChanged(event) {
|
||||
this.enableLoop = event.checked;
|
||||
if (!this.enableLoop) {
|
||||
this.selNode.authentication.swapMacaroonPath = '';
|
||||
this.selNode.settings.swapServerUrl = '';
|
||||
}
|
||||
}
|
||||
|
||||
onUpdateService():boolean|void {
|
||||
if(this.enableLoop && (!this.selNode.settings.swapServerUrl || this.selNode.settings.swapServerUrl.trim() === '' || !this.selNode.authentication.swapMacaroonPath || this.selNode.authentication.swapMacaroonPath.trim() === '')) { return true; }
|
||||
this.logger.info(this.selNode);
|
||||
this.store.dispatch(new RTLActions.OpenSpinner('Updating Loop Service Settings...'));
|
||||
this.store.dispatch(new RTLActions.UpdateServiceSettings({service: ServicesEnum.LOOP, settings: { enable: this.enableLoop, serverUrl: this.selNode.settings.swapServerUrl, macaroonPath: this.selNode.authentication.swapMacaroonPath }}));
|
||||
this.store.dispatch(new LNDActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: this.selNode.settings.currencyUnit, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
|
||||
this.store.dispatch(new CLActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: this.selNode.settings.currencyUnit, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
|
||||
this.store.dispatch(new ECLActions.SetChildNodeSettings({userPersona: this.selNode.settings.userPersona, channelBackupPath: this.selNode.settings.channelBackupPath, selCurrencyUnit: this.selNode.settings.currencyUnit, currencyUnits: this.selNode.settings.currencyUnits, fiatConversion: this.selNode.settings.fiatConversion, lnImplementation: this.selNode.lnImplementation, swapServerUrl: this.selNode.settings.swapServerUrl, boltzServerUrl: this.selNode.settings.boltzServerUrl}));
|
||||
}
|
||||
|
||||
onReset() {
|
||||
this.selNode = JSON.parse(JSON.stringify(this.previousSelNode));
|
||||
this.enableLoop = this.selNode.settings.swapServerUrl && this.selNode.settings.swapServerUrl.trim() !== '';
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.unSubs.forEach(unsub => {
|
||||
unsub.next();
|
||||
unsub.complete();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
<div fxLayout="row" fxLayoutAlign="start center" class="page-title-container mt-1">
|
||||
<fa-icon [icon]="faLayerGroup" class="page-title-img mr-1"></fa-icon>
|
||||
<span class="page-title">Services</span>
|
||||
</div>
|
||||
<div fxLayout="column" class="padding-gap-x">
|
||||
<mat-card>
|
||||
<mat-card-content fxLayout="column">
|
||||
<nav mat-tab-nav-bar>
|
||||
<div role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === links[0].link" (click)="activeLink = links[0].link" routerLink="{{links[0].link}}">{{links[0].name}}</div>
|
||||
<div role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === links[1].link" (click)="activeLink = links[1].link" routerLink="{{links[1].link}}" [state]="{ initial: false }">{{links[1].name}}</div>
|
||||
</nav>
|
||||
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="mat-tab-body-wrapper">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ServicesSettingsComponent } from './services-settings.component';
|
||||
|
||||
describe('ServicesSettingsComponent', () => {
|
||||
let component: ServicesSettingsComponent;
|
||||
let fixture: ComponentFixture<ServicesSettingsComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ ServicesSettingsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ServicesSettingsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,49 @@
|
||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { Router, ResolveEnd } from '@angular/router';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil, filter } from 'rxjs/operators';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { faLayerGroup } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import { ConfigSettingsNode, RTLConfiguration } from '../../../models/RTLconfig';
|
||||
|
||||
import * as fromRTLReducer from '../../../../store/rtl.reducers';
|
||||
|
||||
@Component({
|
||||
selector: 'rtl-services-settings',
|
||||
templateUrl: './services-settings.component.html',
|
||||
styleUrls: ['./services-settings.component.scss']
|
||||
})
|
||||
export class ServicesSettingsComponent implements OnInit, OnDestroy {
|
||||
public faLayerGroup = faLayerGroup;
|
||||
public selNode: ConfigSettingsNode;
|
||||
public appConfig: RTLConfiguration;
|
||||
public links = [{link: 'loop', name: 'Loop'}, {link: 'boltz', name: 'Boltz'}];
|
||||
public activeLink = '';
|
||||
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()];
|
||||
|
||||
constructor(private store: Store<fromRTLReducer.RTLState>, private router: Router) {}
|
||||
|
||||
ngOnInit() {
|
||||
let linkFound = this.links.find(link => this.router.url.includes(link.link));
|
||||
this.activeLink = linkFound ? linkFound.link : this.links[0].link;
|
||||
this.router.events.pipe(takeUntil(this.unSubs[0]), filter(e => e instanceof ResolveEnd))
|
||||
.subscribe((value: ResolveEnd) => {
|
||||
let linkFound = this.links.find(link => value.urlAfterRedirects.includes(link.link));
|
||||
this.activeLink = linkFound ? linkFound.link : this.links[0].link;
|
||||
});
|
||||
this.store.select('root').pipe(takeUntil(this.unSubs[1]))
|
||||
.subscribe((rtlStore) => {
|
||||
this.appConfig = rtlStore.appConfig;
|
||||
this.selNode = rtlStore.selNode;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.unSubs.forEach(completeSub => {
|
||||
completeSub.next();
|
||||
completeSub.complete();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -1,14 +1 @@
|
||||
<!-- <div fxLayout="row" fxLayoutAlign="start center" class="page-title-container">
|
||||
<fa-icon [icon]="faLayerGroup" class="page-title-img mr-1"></fa-icon>
|
||||
<span class="page-title">Services</span>
|
||||
</div>
|
||||
<div fxLayout="column" class="padding-gap-x">
|
||||
<mat-card>
|
||||
<mat-card-content fxLayout="column">
|
||||
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="mat-tab-body-wrapper">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div> -->
|
||||
<router-outlet></router-outlet>
|
||||
|
@ -1,84 +1,37 @@
|
||||
<div [perfectScrollbar] fxLayout="column" fxFlex="100">
|
||||
<form fxLayout="column" fxLayoutAlign="start stretch" class="settings-container page-sub-title-container mt-1" #form="ngForm">
|
||||
<div [perfectScrollbar] fxLayout="column" fxFlex="100" class="padding-gap-x-large">
|
||||
<form *ngIf="appConfig.nodes && appConfig.nodes.length && appConfig.nodes.length > 1" fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch" class="settings-container page-sub-title-container mt-1" #form="ngForm">
|
||||
<div fxLayout="row">
|
||||
<fa-icon [icon]="faTools" class="page-title-img mr-1"></fa-icon>
|
||||
<span class="page-title">Balance Display</span>
|
||||
<fa-icon [icon]="faWindowRestore" class="page-title-img mr-1"></fa-icon>
|
||||
<span class="page-title">Default Node</span>
|
||||
</div>
|
||||
<div fxLayout="column" fxLayoutAlign="start stretch" class="mt-1 bordered-box padding-gap-large">
|
||||
<div fxFlex="100" class="alert alert-warn">
|
||||
<fa-icon [icon]="faExclamationTriangle" class="mr-1 alert-icon"></fa-icon>
|
||||
<span>Fiat conversion calls <strong><a href="https://www.blockchain.com/api/exchange_rates_api" target="blank">Blockchain.com</a></strong> API to get conversion rates.</span>
|
||||
<div fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch" class="my-2">
|
||||
<div fxLayout="row" fxFlex="100" class="alert alert-info">
|
||||
<fa-icon [icon]="faInfoCircle" class="mr-1 alert-icon"></fa-icon>
|
||||
<span>The setting will apply after RTL server restart.</span>
|
||||
</div>
|
||||
<div fxLayout="row wrap" fxLayoutAlign="start center">
|
||||
<mat-slide-toggle tabindex="2" color="primary" [(ngModel)]="selNode.settings.fiatConversion" (change)="selNode.settings.currencyUnit = !$event.checked ? null : selNode.settings.currencyUnit" name="fiatConversion">Include Fiat Conversion</mat-slide-toggle>
|
||||
<mat-form-field>
|
||||
<mat-select autoFocus [(ngModel)]="selNode.settings.currencyUnit" (selectionChange)="onCurrencyChange($event)" placeholder="Fiat Currency" [disabled]="!selNode.settings.fiatConversion" tabindex="3" [required]="selNode.settings.fiatConversion" name="currencyUnit" #currencyUnit="ngModel">
|
||||
<mat-option *ngFor="let currencyUnit of currencyUnits" [value]="currencyUnit.id">
|
||||
{{currencyUnit.id}}
|
||||
<div fxLayout="row" fxLayoutAlign="start start">
|
||||
<mat-form-field fxLayout="row" fxLayoutAlign="start start">
|
||||
<mat-select autoFocus [(ngModel)]="appConfig.defaultNodeIndex" tabindex="1" name="defaultNode">
|
||||
<mat-option *ngFor="let node of appConfig.nodes" [value]="node.index">
|
||||
{{node.lnNode}} ({{node.lnImplementation}})
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="selNode.settings.fiatConversion && !selNode.settings.currencyUnit">Currency unit is required.</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch">
|
||||
<div fxLayout="row wrap" fxLayoutAlign="start start" fxLayout.gt-sm="column" fxFlex="100" fxLayoutAlign.gt-sm="space-between stretch" class="settings-container page-sub-title-container mt-1">
|
||||
<div class="mt-1">
|
||||
<fa-icon [icon]="faPaintBrush" class="page-title-img mr-1"></fa-icon>
|
||||
<span class="page-title">Customization</span>
|
||||
</div>
|
||||
<div fxLayout="column" fxLayoutAlign="start stretch" class="mt-1 bordered-box padding-gap-large">
|
||||
<div fxLayout="column" fxFlex="100" fxLayoutAlign="start start" *ngIf="appConfig.nodes.length && appConfig.nodes.length > 1">
|
||||
<h4 class="my-0">Default Node</h4>
|
||||
<mat-form-field>
|
||||
<mat-select [(ngModel)]="appConfig.defaultNodeIndex" tabindex="4" name="defaultNode">
|
||||
<mat-option *ngFor="let node of appConfig.nodes" [value]="node.index">
|
||||
{{node.lnNode}} ({{node.lnImplementation}})
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<mat-divider [inset]="true" class="mb-1" *ngIf="appConfig.nodes.length && appConfig.nodes.length > 1"></mat-divider>
|
||||
<div fxLayout="column" fxLayoutAlign="start stretch" fxFlex="100">
|
||||
<div fxLayout="row" fxFlex="100" class="alert alert-info mb-0">
|
||||
<fa-icon [icon]="faInfoCircle" class="mr-1 alert-icon"></fa-icon>
|
||||
<span>Application layout will be tailored based on the role selected to better serve its needs.</span>
|
||||
</div>
|
||||
<div fxLayout="column" fxLayoutAlign="start start" fxFlex="100">
|
||||
<h4>Application Layout</h4>
|
||||
<mat-radio-group color="primary" [(ngModel)]="selNode.settings.userPersona" tabindex="1" name="userPersona">
|
||||
<mat-radio-button *ngFor="let userPersona of userPersonas" [value]="userPersona" [checked]="selNode.settings.userPersona === userPersona" class="mr-4">
|
||||
{{userPersona | titlecase}}
|
||||
</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider [inset]="true" class="mt-1"></mat-divider>
|
||||
<div fxLayout="column" fxLayout.gt-xs="row" fxFlex="100" fxLayoutAlign="space-between stretch" fxLayoutAlign.gt-xs="start stretch">
|
||||
<div fxFlex.gt-xs="20" fxFlex.gt-md="15" fxLayout="column" fxLayoutAlign="space-between stretch">
|
||||
<h4>Mode</h4>
|
||||
<mat-radio-group color="primary" [(ngModel)]="selectedThemeMode" (change)="chooseThemeMode()" name="themeMode">
|
||||
<mat-radio-button tabindex="5" *ngFor="let themeMode of themeModes" [value]="themeMode" [ngClass]="{'mr-4': screenSize === screenSizeEnum.XS || screenSize === screenSizeEnum.SM}">{{themeMode.name}}
|
||||
</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
<div fxLayout="column" fxFlex.gt-xs="20" fxFlex.gt-md="15" fxLayoutAlign="space-between stretch"></div>
|
||||
<div fxLayout="column" fxFlex.gt-xs="50" fxFlex.gt-md="40" fxLayoutAlign="space-between stretch">
|
||||
<h4>Themes</h4>
|
||||
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between start">
|
||||
<span *ngFor="let themeColor of themeColors" fxLayout="row">
|
||||
<div tabindex="9" [class]="themeColor.id | lowercase" [ngClass]="{'skin': true, 'selected-color': selectedThemeColor === themeColor.id}" (click)="changeThemeColor(themeColor.id)"></div>
|
||||
{{themeColor.name}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div fxLayout="row" fxLayoutAlign="start start" class="mt-1">
|
||||
<div fxLayout="row" fxLayoutAlign="start start">
|
||||
<button class="mr-1" mat-stroked-button color="primary" (click)="onResetSettings()" tabindex="2">Reset</button>
|
||||
<button mat-flat-button color="primary" (click)="onUpdateSettings()" tabindex="3">Update</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <mat-divider [inset]="true" class="my-2"></mat-divider> -->
|
||||
</form>
|
||||
<div fxLayout="row" class="mt-1">
|
||||
<button class="mr-1" mat-stroked-button color="primary" (click)="onResetSettings()" tabindex="10">Reset</button>
|
||||
<button mat-flat-button color="primary" (click)="onUpdateSettings()" tabindex="11">Update</button>
|
||||
<!-- <div fxLayout="row">
|
||||
<fa-icon [icon]="faPlus" class="page-title-img mr-1"></fa-icon>
|
||||
<span class="page-title">Add New Node</span>
|
||||
</div>
|
||||
<div fxLayout="row" fxFlex="100" fxLayoutAlign="start start">
|
||||
<button mat-flat-button color="primary" (click)="onAddNewNode()" tabindex="4" class="my-2">Add Node</button>
|
||||
</div> -->
|
||||
</div>
|
||||
|
@ -0,0 +1,21 @@
|
||||
<div fxLayout="column" fxFlex="100">
|
||||
<div fxLayout="row" fxFlex="100" fxLayoutAlign="start start">
|
||||
<div *ngIf="configData !== '' && fileFormat === 'JSON'" fxFlex="100" class="mb-6">
|
||||
<pre class="pre-wrap">{{configData | json}}</pre>
|
||||
<mat-divider *ngIf="configData !== ''" class="my-1"></mat-divider>
|
||||
</div>
|
||||
<div *ngIf="configData !== '' && (fileFormat === 'INI' || fileFormat === 'HOCON')" fxFlex="100">
|
||||
<mat-list>
|
||||
<mat-list-item *ngFor="let conf of configData; index as i;">
|
||||
<mat-card-subtitle class="my-1">
|
||||
<h2 *ngIf="conf.indexOf('[') >= 0">{{conf}}</h2>
|
||||
</mat-card-subtitle>
|
||||
<mat-card-subtitle class="m-0">
|
||||
<h4 *ngIf="conf.indexOf('[') < 0" class="ml-4">{{conf}}</h4>
|
||||
</mat-card-subtitle>
|
||||
<mat-divider [inset]="true" *ngIf="conf.indexOf('[') < 0"></mat-divider>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,3 @@
|
||||
h4 {
|
||||
word-break: break-word;
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { Router, ResolveEnd } from '@angular/router';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil, filter } from 'rxjs/operators';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { faCog } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import { RTLEffects } from '../../../../store/rtl.effects';
|
||||
import * as RTLActions from '../../../../store/rtl.actions';
|
||||
import * as fromRTLReducer from '../../../../store/rtl.reducers';
|
||||
|
||||
@Component({
|
||||
selector: 'rtl-bitcoin-config',
|
||||
templateUrl: './bitcoin-config.component.html',
|
||||
styleUrls: ['./bitcoin-config.component.scss']
|
||||
})
|
||||
export class BitcoinConfigComponent implements OnInit, OnDestroy {
|
||||
public selectedNodeType = '';
|
||||
public configData = '';
|
||||
public fileFormat = 'INI';
|
||||
public faCog = faCog;
|
||||
private unSubs: Array<Subject<void>> = [new Subject(), new Subject()];
|
||||
|
||||
constructor(private store: Store<fromRTLReducer.RTLState>, private rtlEffects: RTLEffects, private router: Router) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.selectedNodeType = (this.router.url.includes('bconfig')) ? 'bitcoind' : 'ln';
|
||||
this.router.events.pipe(takeUntil(this.unSubs[0]), filter(e => e instanceof ResolveEnd))
|
||||
.subscribe((value: ResolveEnd) => {
|
||||
this.selectedNodeType = (value.urlAfterRedirects.includes('bconfig')) ? 'bitcoind' : 'ln';
|
||||
});
|
||||
this.store.dispatch(new RTLActions.OpenSpinner('Opening Config File...'));
|
||||
this.store.dispatch(new RTLActions.FetchConfig(this.selectedNodeType));
|
||||
this.rtlEffects.showLnConfig
|
||||
.pipe(takeUntil(this.unSubs[1]))
|
||||
.subscribe((config: any) => {
|
||||
const configFile = config.data;
|
||||
this.fileFormat = config.format;
|
||||
if (configFile !== '' && configFile && (this.fileFormat === 'INI' || this.fileFormat === 'HOCON')) {
|
||||
this.configData = configFile.split('\n');
|
||||
} else if (configFile !== '' && configFile && this.fileFormat === 'JSON') {
|
||||
this.configData = configFile;
|
||||
} else {
|
||||
this.configData = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.unSubs.forEach(completeSub => {
|
||||
completeSub.next();
|
||||
completeSub.complete();
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue