Update shared components to use async pipes

Convert shared folder components to use async pipe pattern:
- help, settings, auth-settings components
- top-menu, side-navigation components
- node-config, services-settings components

Pattern: Create observable properties with shareReplay(1) and use
async pipe in templates with object destructuring syntax.

Committer: saubyk <39208279+saubyk@users.noreply.github.com>
This commit is contained in:
saubyk 2026-01-19 20:04:14 -08:00 committed by ShahanaFarooqui
parent 925d165b1e
commit 9a13b80b3e
No known key found for this signature in database
GPG key ID: 951B8D133DD4AF6E
17 changed files with 203 additions and 185 deletions

View file

@ -1,4 +1,4 @@
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { Component, OnInit, OnDestroy, ViewChild, ChangeDetectorRef } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { Store } from '@ngrx/store';
@ -37,7 +37,13 @@ export class CLNLookupsComponent implements OnInit, OnDestroy {
public screenSizeEnum = ScreenSizeEnum;
private unSubs: Array<Subject<void>> = [new Subject(), new Subject()];
constructor(private logger: LoggerService, private commonService: CommonService, private store: Store<RTLState>, private actions: Actions) {
constructor(
private logger: LoggerService,
private commonService: CommonService,
private store: Store<RTLState>,
private actions: Actions,
private cdr: ChangeDetectorRef
) {
this.screenSize = this.commonService.getScreenSize();
}
@ -63,9 +69,11 @@ export class CLNLookupsComponent implements OnInit, OnDestroy {
this.flgSetLookupValue = true;
this.logger.info(this.nodeLookupValue);
this.logger.info(this.channelLookupValue);
this.cdr.detectChanges();
}
if (resLookup.type === CLNActions.UPDATE_API_CALL_STATUS_CLN && resLookup.payload.status === APICallStatusEnum.ERROR && resLookup.payload.action === 'Lookup') {
this.flgLoading[0] = 'error';
this.cdr.detectChanges();
}
});
}

View file

@ -1,4 +1,4 @@
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { Component, OnInit, OnDestroy, ViewChild, ChangeDetectorRef } from '@angular/core';
import { UntypedFormControl } from '@angular/forms';
import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
@ -39,7 +39,13 @@ export class ECLLookupsComponent implements OnInit, OnDestroy {
public screenSizeEnum = ScreenSizeEnum;
private unSubs: Array<Subject<void>> = [new Subject(), new Subject()];
constructor(private logger: LoggerService, private commonService: CommonService, private store: Store<RTLState>, private actions: Actions) {
constructor(
private logger: LoggerService,
private commonService: CommonService,
private store: Store<RTLState>,
private actions: Actions,
private cdr: ChangeDetectorRef
) {
this.screenSize = this.commonService.getScreenSize();
}
@ -67,9 +73,11 @@ export class ECLLookupsComponent implements OnInit, OnDestroy {
this.flgSetLookupValue = true;
this.logger.info(this.nodeLookupValue);
this.logger.info(this.channelLookupValue);
this.cdr.detectChanges();
}
if (resLookup.type === ECLActions.UPDATE_API_CALL_STATUS_ECL && resLookup.payload.status === APICallStatusEnum.ERROR && resLookup.payload.action === 'Lookup') {
this.flgLoading[0] = 'error';
this.cdr.detectChanges();
}
});
this.lookupKeyCtrl.valueChanges.pipe(takeUntil(this.unSubs[1])).subscribe((value) => {

View file

@ -1,4 +1,4 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Component, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { Store } from '@ngrx/store';
@ -36,7 +36,13 @@ export class LookupsComponent implements OnInit, OnDestroy {
public apiCallStatusEnum = APICallStatusEnum;
private unSubs: Array<Subject<void>> = [new Subject()];
constructor(private logger: LoggerService, private commonService: CommonService, private store: Store<RTLState>, private actions: Actions) {
constructor(
private logger: LoggerService,
private commonService: CommonService,
private store: Store<RTLState>,
private actions: Actions,
private cdr: ChangeDetectorRef
) {
this.screenSize = this.commonService.getScreenSize();
}
@ -52,6 +58,7 @@ export class LookupsComponent implements OnInit, OnDestroy {
this.lookupValue = JSON.parse(JSON.stringify(resLookup.payload));
this.flgSetLookupValue = (this.selectedFieldId === 0 && resLookup.payload.hasOwnProperty('node')) ? true : !!((this.selectedFieldId === 1 && resLookup.payload.hasOwnProperty('channel_id')));
this.logger.info(this.lookupValue);
this.cdr.detectChanges();
}
if (resLookup.type === LNDActions.UPDATE_API_CALL_STATUS_LND && resLookup.payload.action === 'Lookup') {
this.errorMessage = '';
@ -61,6 +68,7 @@ export class LookupsComponent implements OnInit, OnDestroy {
if (resLookup.payload.status === APICallStatusEnum.INITIATED) {
this.errorMessage = UI_MESSAGES.GET_LOOKUP_DETAILS;
}
this.cdr.detectChanges();
}
});
}

View file

@ -1,4 +1,4 @@
<div fxLayout="column" fxFlex="100">
<div fxLayout="column" fxFlex="100" *ngIf="{ selNode: selNode$ | async, flgLoggedIn: flgLoggedIn$ | async } as obs">
<div fxLayout="row" fxLayoutAlign="start center" class="page-title-container">
<fa-icon class="page-title-img mr-1" [icon]="faQuestion" />
<span class="page-title">Help</span>
@ -6,13 +6,13 @@
<div fxLayout="row" fxFlex="100" class="padding-gap-x" fxLayoutAlign="start start">
<div fxLayout="column" fxFlex="100">
<div *ngFor="let helpTopic of helpTopics" fxFlex="100">
<mat-expansion-panel *ngIf="helpTopic.help.lnImplementation === 'ALL' || helpTopic.help.lnImplementation === selNode.lnImplementation" class="flat-expansion-panel help-expansion mb-2px">
<mat-expansion-panel *ngIf="helpTopic.help.lnImplementation === 'ALL' || helpTopic.help.lnImplementation === obs.selNode?.lnImplementation" class="flat-expansion-panel help-expansion mb-2px">
<mat-expansion-panel-header>
<mat-panel-title>{{helpTopic.help.question}}</mat-panel-title>
</mat-expansion-panel-header>
<mat-panel-description fxLayout="column" fxFlex="100" fxLayoutAlign="start start">
<span class="pre-wrap" [innerHTML]="helpTopic.help.answer"></span>
<a class="mt-2" [routerLink]="flgLoggedIn ? helpTopic.help.link : '/login'">{{!flgLoggedIn ? 'Login to go to the page' : helpTopic.help.linkCaption}}</a>
<a class="mt-2" [routerLink]="obs.flgLoggedIn ? helpTopic.help.link : '/login'">{{!obs.flgLoggedIn ? 'Login to go to the page' : helpTopic.help.linkCaption}}</a>
</mat-panel-description>
</mat-expansion-panel>
</div>

View file

@ -1,6 +1,6 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Subject, Observable, merge, of } from 'rxjs';
import { takeUntil, map, shareReplay, tap, startWith } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { faQuestion } from '@fortawesome/free-solid-svg-icons';
@ -20,29 +20,31 @@ export class HelpComponent implements OnInit, OnDestroy {
public helpTopics: Array<HelpTopic> = [];
public faQuestion = faQuestion;
public selNode: Node | any;
public LNPLink = '/lnd/';
public flgLoggedIn = false;
public selNode$: Observable<Node | null>;
public flgLoggedIn$: Observable<boolean>;
private unSubs = [new Subject(), new Subject(), new Subject(), new Subject()];
constructor(private store: Store<RTLState>, private sessionService: SessionService) {}
ngOnInit() {
this.store.select(rootSelectedNode).pipe(takeUntil(this.unSubs[0])).subscribe((selNode) => {
this.selNode = selNode;
if (this.selNode.lnImplementation && this.selNode.lnImplementation.trim() !== '') {
this.LNPLink = '/' + this.selNode.lnImplementation.toLowerCase() + '/';
this.addHelpTopics();
}
});
this.sessionService.watchSession().
pipe(takeUntil(this.unSubs[1])).
subscribe((session) => {
this.flgLoggedIn = !!session.token;
});
if (this.sessionService.getItem('token')) {
this.flgLoggedIn = true;
}
this.selNode$ = this.store.select(rootSelectedNode).pipe(
takeUntil(this.unSubs[0]),
tap((selNode) => {
if (selNode && selNode.lnImplementation && selNode.lnImplementation.trim() !== '') {
this.LNPLink = '/' + selNode.lnImplementation.toLowerCase() + '/';
this.addHelpTopics();
}
}),
shareReplay(1)
);
this.flgLoggedIn$ = merge(
of(!!this.sessionService.getItem('token')),
this.sessionService.watchSession().pipe(map((session) => !!session.token))
).pipe(
takeUntil(this.unSubs[1]),
shareReplay(1)
);
}
addHelpTopics() {

View file

@ -1,8 +1,8 @@
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between start" [perfectScrollbar]>
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between start" [perfectScrollbar] *ngIf="{ appConfig: appConfig$ | async, showLogout: showLogout$ | async } as obs">
<div fxLayout="column" fxFlex="90" fxLayoutAlign="start stretch" class="w-100">
<mat-select *ngIf="appConfig?.nodes?.length > 1" class="m-2 multi-node-select" [value]="selConfigNodeIndex" (selectionChange)="onNodeSelectionChange($event.value)">
<mat-select *ngIf="obs.appConfig?.nodes?.length > 1" class="m-2 multi-node-select" [value]="selConfigNodeIndex" (selectionChange)="onNodeSelectionChange($event.value)">
<perfect-scrollbar>
<mat-option *ngFor="let node of appConfig.nodes" tabindex="1" [value]="node.index">
<mat-option *ngFor="let node of obs.appConfig?.nodes" tabindex="1" [value]="node.index">
{{node.lnNode}} ({{node.lnImplementation}})
</mat-option>
</perfect-scrollbar>
@ -47,7 +47,7 @@
</mat-tree>
</div>
<div fxLayout="column" fxLayoutAlign="end stretch" class="w-100">
<mat-tree *ngIf="showLogout" [dataSource]="navMenusLogout" [treeControl]="treeControlLogout">
<mat-tree *ngIf="obs.showLogout" [dataSource]="navMenusLogout" [treeControl]="treeControlLogout">
<mat-tree-node *matTreeNodeDef="let node" (click)="onClick(node)">
<span *ngIf="node.iconType === 'SVG'" class="mr-2"><ng-container [ngTemplateOutlet]="node.icon" /></span>
<fa-icon *ngIf="node.iconType === 'FA'" matTooltip="{{node.name}}" class="fa-icon-small mr-2" matTooltipPosition="right" [icon]="node.icon" />

View file

@ -1,6 +1,6 @@
import { Component, OnInit, OnDestroy, Output, EventEmitter, ViewChild, ChangeDetectorRef } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { Subject, Observable, merge, of } from 'rxjs';
import { takeUntil, filter, map, shareReplay, tap } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
@ -32,6 +32,7 @@ export class SideNavigationComponent implements OnInit, OnDestroy {
@Output() readonly ChildNavClicked = new EventEmitter<any>();
faEject = faEject;
faEye = faEye;
public appConfig$: Observable<RTLConfiguration>;
public appConfig: RTLConfiguration;
public selConfigNodeIndex: Number;
public selNode: Node | any = null;
@ -41,7 +42,7 @@ export class SideNavigationComponent implements OnInit, OnDestroy {
public flgLoading = true;
public logoutNode = [{ id: 200, parentId: 0, name: 'Logout', iconType: 'FA', icon: faEject }];
public showDataNodes = [{ id: 1000, parentId: 0, name: 'Public Key', iconType: 'FA', icon: faEye }];
public showLogout = false;
public showLogout$: Observable<boolean>;
public numPendingChannels = 0;
public smallScreen = false;
public childRootRoute = '';
@ -73,12 +74,15 @@ export class SideNavigationComponent implements OnInit, OnDestroy {
ngOnInit() {
const token = this.sessionService.getItem('token');
this.showLogout = !!token;
this.flgLoading = !!token;
this.store.select(rootAppConfig).pipe(takeUntil(this.unSubs[0])).subscribe((appConfig) => {
this.appConfig = appConfig;
this.cdr.detectChanges();
});
this.appConfig$ = this.store.select(rootAppConfig).pipe(
takeUntil(this.unSubs[0]),
tap((appConfig) => {
this.appConfig = appConfig;
this.cdr.detectChanges();
}),
shareReplay(1)
);
this.store.select(rootSelNodeAndNodeData).pipe(takeUntil(this.unSubs[1])).
subscribe((rootData: { nodeDate: GetInfoRoot, selNode: Node | null }) => {
this.information = rootData.nodeDate;
@ -107,20 +111,21 @@ export class SideNavigationComponent implements OnInit, OnDestroy {
this.logger.info(rootData);
this.cdr.detectChanges();
});
this.sessionService.watchSession().
pipe(takeUntil(this.unSubs[2])).
subscribe((session) => {
this.showLogout = !!session.token;
this.flgLoading = !!session.token;
this.cdr.detectChanges();
});
this.actions.pipe(
takeUntil(this.unSubs[3]),
filter((action) => action.type === RTLActions.LOGOUT)).
subscribe((action: any) => {
this.showLogout = false;
this.cdr.detectChanges();
});
this.showLogout$ = merge(
of(!!token),
this.sessionService.watchSession().pipe(
tap(() => this.cdr.detectChanges()),
map((session) => !!session.token)
),
this.actions.pipe(
filter((action) => action.type === RTLActions.LOGOUT),
tap(() => this.cdr.detectChanges()),
map(() => false)
)
).pipe(
takeUntil(this.unSubs[2]),
shareReplay(1)
);
}
hasChild = (_: number, node: MenuChildNode) => !!node.children && node.children.length > 0;
@ -138,7 +143,6 @@ export class SideNavigationComponent implements OnInit, OnDestroy {
pipe(takeUntil(this.unSubs[4])).
subscribe((confirmRes) => {
if (confirmRes) {
this.showLogout = false;
this.store.dispatch(logout({ payload: '' }));
}
});

View file

@ -1,32 +1,34 @@
<mat-menu #topMenu="matMenu" class="top-menu" [overlapTrigger]="false">
<p tabindex="1" mat-menu-item class="cursor-default">
<fa-icon class="fa-icon-small mr-1" [icon]="faCodeBranch" />
<span>Version: {{version}}</span>
</p>
<p *ngIf="information?.api_version" tabindex="2" mat-menu-item class="cursor-default">
<fa-icon class="fa-icon-small mr-1" [icon]="faCode" />
<span>API Version: {{information?.api_version}}</span>
</p>
<p *ngIf="showLogout" tabindex="3" mat-menu-item routerLink="/settings">
<fa-icon class="fa-icon-small mr-1" [icon]="faUserCog" />
<span routerLink="/settings">Settings</span>
</p>
<p *ngIf="showLogout" tabindex="4" mat-menu-item routerLink="/help">
<fa-icon class="fa-icon-small mr-1" [icon]="faQuestion" />
<span routerLink="/help">Help</span>
</p>
<p mat-menu-item tabindex="5" fxLayoutAlign="start center" (click)="onDonate()">
<svg class="svg-donation" fill="currentColor" version="1.1" viewBox="0 0 64 64" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g>
<path d="M62.519,17.698l-12-14c-0.659-0.768-1.786-0.923-2.628-0.362l-8.712,5.808l-16.688,4.172 c-2.485,0.622-4.537,2.412-5.487,4.791L12.21,30.09C10.206,32.512,9,35.618,9,39c0,2.974,0.939,5.73,2.527,8H5 c-2.206,0-4,1.794-4,4v6c0,2.206,1.794,4,4,4h36c2.206,0,4-1.794,4-4v-6c0-2.206-1.794-4-4-4h-6.522 c0.375-0.535,0.713-1.1,1.013-1.691l4.291-2.452l3.378-0.965c2.619-0.749,4.903-2.269,6.604-4.395 c1.39-1.736,2.317-3.813,2.682-6.006l0.412-2.472l9.48-8.532C63.145,19.76,63.225,18.523,62.519,17.698z M34.428,30.929 c-1.487-2.094-3.517-3.732-5.842-4.75L29.207,25h7.058l0.588,4.11L34.428,30.929z M31.225,33.331l-0.373,0.28 c-1.772,1.329-2.889,3.273-3.146,5.473c-0.257,2.2,0.382,4.348,1.8,6.048l0.667,0.8C28.315,47.845,25.742,49,23,49 c-5.514,0-10-4.486-10-10s4.486-10,10-10C26.299,29,29.376,30.663,31.225,33.331z M41,57H5v-6h10.826c2.101,1.261,4.55,2,7.174,2 c2.571,0,5.041-0.723,7.176-2H41V57z M49.662,26.513c-0.336,0.303-0.561,0.711-0.635,1.158L48.5,30.833 c-0.253,1.521-0.896,2.96-1.86,4.165c-1.18,1.475-2.763,2.529-4.579,3.048l-3.61,1.031c-0.155,0.044-0.303,0.106-0.443,0.187 l-5.541,3.166c-0.63-0.826-0.909-1.843-0.788-2.882c0.128-1.1,0.687-2.072,1.573-2.737l6.001-4.5 c1.169-0.877,1.767-2.32,1.56-3.766l-0.587-4.11C39.946,22.477,38.244,21,36.266,21h-7.059c-1.489,0-2.845,0.818-3.539,2.136 l-1.037,1.969C24.093,25.041,23.549,25,23,25c-1.685,0-3.294,0.314-4.791,0.862l2.509-6.271c0.476-1.189,1.501-2.084,2.743-2.395 l17.024-4.256c0.223-0.056,0.434-0.149,0.625-0.276l7.525-5.017l9.576,11.172L49.662,26.513z"/>
</g>
</svg>
<span>Donate</span>
</p>
<p *ngIf="showLogout" tabindex="6" mat-menu-item (click)="onClick()">
<fa-icon class="fa-icon-small mr-1" [icon]="faEject" />
<span>Logout</span>
</p>
<ng-container *ngIf="{ information: information$ | async, showLogout: showLogout$ | async } as obs">
<p tabindex="1" mat-menu-item class="cursor-default">
<fa-icon class="fa-icon-small mr-1" [icon]="faCodeBranch" />
<span>Version: {{version}}</span>
</p>
<p *ngIf="obs.information?.api_version" tabindex="2" mat-menu-item class="cursor-default">
<fa-icon class="fa-icon-small mr-1" [icon]="faCode" />
<span>API Version: {{obs.information?.api_version}}</span>
</p>
<p *ngIf="obs.showLogout" tabindex="3" mat-menu-item routerLink="/settings">
<fa-icon class="fa-icon-small mr-1" [icon]="faUserCog" />
<span routerLink="/settings">Settings</span>
</p>
<p *ngIf="obs.showLogout" tabindex="4" mat-menu-item routerLink="/help">
<fa-icon class="fa-icon-small mr-1" [icon]="faQuestion" />
<span routerLink="/help">Help</span>
</p>
<p mat-menu-item tabindex="5" fxLayoutAlign="start center" (click)="onDonate()">
<svg class="svg-donation" fill="currentColor" version="1.1" viewBox="0 0 64 64" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g>
<path d="M62.519,17.698l-12-14c-0.659-0.768-1.786-0.923-2.628-0.362l-8.712,5.808l-16.688,4.172 c-2.485,0.622-4.537,2.412-5.487,4.791L12.21,30.09C10.206,32.512,9,35.618,9,39c0,2.974,0.939,5.73,2.527,8H5 c-2.206,0-4,1.794-4,4v6c0,2.206,1.794,4,4,4h36c2.206,0,4-1.794,4-4v-6c0-2.206-1.794-4-4-4h-6.522 c0.375-0.535,0.713-1.1,1.013-1.691l4.291-2.452l3.378-0.965c2.619-0.749,4.903-2.269,6.604-4.395 c1.39-1.736,2.317-3.813,2.682-6.006l0.412-2.472l9.48-8.532C63.145,19.76,63.225,18.523,62.519,17.698z M34.428,30.929 c-1.487-2.094-3.517-3.732-5.842-4.75L29.207,25h7.058l0.588,4.11L34.428,30.929z M31.225,33.331l-0.373,0.28 c-1.772,1.329-2.889,3.273-3.146,5.473c-0.257,2.2,0.382,4.348,1.8,6.048l0.667,0.8C28.315,47.845,25.742,49,23,49 c-5.514,0-10-4.486-10-10s4.486-10,10-10C26.299,29,29.376,30.663,31.225,33.331z M41,57H5v-6h10.826c2.101,1.261,4.55,2,7.174,2 c2.571,0,5.041-0.723,7.176-2H41V57z M49.662,26.513c-0.336,0.303-0.561,0.711-0.635,1.158L48.5,30.833 c-0.253,1.521-0.896,2.96-1.86,4.165c-1.18,1.475-2.763,2.529-4.579,3.048l-3.61,1.031c-0.155,0.044-0.303,0.106-0.443,0.187 l-5.541,3.166c-0.63-0.826-0.909-1.843-0.788-2.882c0.128-1.1,0.687-2.072,1.573-2.737l6.001-4.5 c1.169-0.877,1.767-2.32,1.56-3.766l-0.587-4.11C39.946,22.477,38.244,21,36.266,21h-7.059c-1.489,0-2.845,0.818-3.539,2.136 l-1.037,1.969C24.093,25.041,23.549,25,23,25c-1.685,0-3.294,0.314-4.791,0.862l2.509-6.271c0.476-1.189,1.501-2.084,2.743-2.395 l17.024-4.256c0.223-0.056,0.434-0.149,0.625-0.276l7.525-5.017l9.576,11.172L49.662,26.513z"/>
</g>
</svg>
<span>Donate</span>
</p>
<p *ngIf="obs.showLogout" tabindex="6" mat-menu-item (click)="onClick()">
<fa-icon class="fa-icon-small mr-1" [icon]="faEject" />
<span>Logout</span>
</p>
</ng-container>
</mat-menu>
<button tabindex="7" mat-icon-button [matMenuTriggerFor]="topMenu">

View file

@ -1,6 +1,6 @@
import { Component, OnInit, OnDestroy, ViewEncapsulation, ChangeDetectorRef } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { Subject, Observable, merge, of } from 'rxjs';
import { takeUntil, filter, map, shareReplay, tap, startWith } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { faCodeBranch, faCode, faCog, faQuestion, faEject, faUserCog } from '@fortawesome/free-solid-svg-icons';
@ -32,10 +32,8 @@ export class TopMenuComponent implements OnInit, OnDestroy {
public faQuestion = faQuestion;
public faEject = faEject;
public version = '';
public information: GetInfoRoot = {};
public informationChain: GetInfoChain = {};
public flgLoading = true;
public showLogout = false;
public information$: Observable<GetInfoRoot>;
public showLogout$: Observable<boolean>;
private unSubs = [new Subject(), new Subject(), new Subject(), new Subject()];
constructor(
@ -50,39 +48,21 @@ export class TopMenuComponent implements OnInit, OnDestroy {
}
ngOnInit() {
this.store.select(rootNodeData).
pipe(takeUntil(this.unSubs[0])).
subscribe((nodeData: GetInfoRoot) => {
this.information = nodeData;
this.flgLoading = !(this.information.identity_pubkey);
if (this.information.identity_pubkey) {
if (this.information.chains && typeof this.information.chains[0] === 'string') {
this.informationChain.chain = this.information.chains[0].toString();
this.informationChain.network = (this.information.testnet) ? 'Testnet' : 'Mainnet';
} else if (this.information && this.information.chains && this.information.chains.length && this.information.chains.length > 0 && typeof this.information.chains[0] === 'object' && this.information.chains[0].hasOwnProperty('chain')) {
const getInfoChain = <GetInfoChain>this.information.chains[0];
this.informationChain.chain = getInfoChain.chain;
this.informationChain.network = getInfoChain.network;
}
} else {
this.informationChain.chain = '';
this.informationChain.network = '';
}
this.logger.info(nodeData);
});
this.sessionService.watchSession().
pipe(takeUntil(this.unSubs[1])).
subscribe((session) => {
this.showLogout = !!session.token;
this.flgLoading = !!session.token;
});
this.actions.
pipe(
takeUntil(this.unSubs[2]),
filter((action) => action.type === RTLActions.LOGOUT)
).subscribe(() => {
this.showLogout = false;
});
this.information$ = this.store.select(rootNodeData).pipe(
takeUntil(this.unSubs[0]),
tap((nodeData) => this.logger.info(nodeData)),
shareReplay(1)
);
this.showLogout$ = merge(
this.sessionService.watchSession().pipe(map((session) => !!session.token)),
this.actions.pipe(
filter((action) => action.type === RTLActions.LOGOUT),
map(() => false)
)
).pipe(
takeUntil(this.unSubs[1]),
shareReplay(1)
);
}
onClick() {
@ -101,9 +81,7 @@ export class TopMenuComponent implements OnInit, OnDestroy {
pipe(takeUntil(this.unSubs[3])).
subscribe((confirmRes) => {
if (confirmRes) {
this.showLogout = false;
this.store.dispatch(logout({ payload: '' }));
this.cdr.detectChanges();
}
});
}

View file

@ -2,7 +2,7 @@
<fa-icon class="page-title-img mr-1" [icon]="faTools" />
<span class="page-title">Node Config</span>
</div>
<div fxLayout="column" class="padding-gap-x">
<div fxLayout="column" class="padding-gap-x" *ngIf="selNode$ | async as selNode">
<mat-card>
<mat-card-content fxLayout="column">
<nav mat-tab-nav-bar mat-stretch-tabs="false" mat-align-tabs="start" [tabPanel]="tabPanel">

View file

@ -1,7 +1,7 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router, ResolveEnd, Event, ActivatedRoute } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { Subject, Observable } from 'rxjs';
import { takeUntil, filter, tap, shareReplay, map } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { faTools } from '@fortawesome/free-solid-svg-icons';
import { openAlert } from '../../../store/rtl.actions';
@ -23,7 +23,7 @@ export class NodeConfigComponent implements OnInit, OnDestroy {
public faTools = faTools;
public showLnConfig = false;
public appConfig: RTLConfiguration;
public selNode: Node | any;
public selNode$: Observable<Node | null>;
public lnImplementationStr = '';
public links = [{ link: 'nodesettings', name: 'Node Settings' }, { link: 'pglayout', name: 'Page Layout' }, { link: 'services', name: 'Services' }, { link: 'experimental', name: 'Experimental' }, { link: 'lnconfig', name: this.lnImplementationStr }];
public activeLink = '';
@ -44,27 +44,30 @@ export class NodeConfigComponent implements OnInit, OnDestroy {
this.store.select(rootAppConfig).pipe(takeUntil(this.unSubs[1])).subscribe((appConfig) => {
this.appConfig = appConfig;
});
this.store.select(rootSelectedNode).pipe(takeUntil(this.unSubs[2])).subscribe((selNode) => {
this.showLnConfig = false;
this.selNode = selNode;
switch (this.selNode.lnImplementation?.toUpperCase()) {
case 'CLN':
this.lnImplementationStr = 'Core Lightning Config';
break;
this.selNode$ = this.store.select(rootSelectedNode).pipe(
takeUntil(this.unSubs[2]),
tap((selNode) => {
this.showLnConfig = false;
switch (selNode?.lnImplementation?.toUpperCase()) {
case 'CLN':
this.lnImplementationStr = 'Core Lightning Config';
break;
case 'ECL':
this.lnImplementationStr = 'Eclair 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[4].name = this.lnImplementationStr;
this.showLnConfig = true;
}
});
default:
this.lnImplementationStr = 'LND Config';
break;
}
if (selNode?.authentication && selNode.authentication.configPath && selNode.authentication.configPath.trim() !== '') {
this.links[4].name = this.lnImplementationStr;
this.showLnConfig = true;
}
}),
shareReplay(1)
);
}
showLnConfigClicked() {

View file

@ -4,15 +4,15 @@
<span class="page-title">Services</span>
</div>
</div>
<div fxLayout="column">
<div fxLayout="column" *ngIf="selNode$ | async as selNode">
<mat-card>
<mat-card-content fxLayout="column">
<nav mat-tab-nav-bar mat-stretch-tabs="false" mat-align-tabs="start" [tabPanel]="tabPanel">
<div *ngIf="selNode.lnImplementation === 'LND'" routerLink="{{links[0].link}}" tabindex="1" role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === links[0].link" (click)="setActiveLink(links[0].link)">{{links[0].name}}</div>
<div *ngIf="selNode.lnImplementation !== 'ECL'" routerLink="{{links[1].link}}" tabindex="2" role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === links[1].link" (click)="setActiveLink(links[1].link)">{{links[1].name}}</div>
<div *ngIf="selNode?.lnImplementation === 'LND'" routerLink="{{links[0].link}}" tabindex="1" role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === links[0].link" (click)="setActiveLink(links[0].link)">{{links[0].name}}</div>
<div *ngIf="selNode?.lnImplementation !== 'ECL'" routerLink="{{links[1].link}}" tabindex="2" role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === links[1].link" (click)="setActiveLink(links[1].link)">{{links[1].name}}</div>
<!-- <div *ngIf="selNode?.settings?.enablePeerswap" tabindex="3" role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === links[2].link" routerLink="{{links[2].link}}" (click)="activeLink = links[2].link">{{links[2].name}}</div> -->
<!-- <div *ngIf="selNode?.settings?.swapServerUrl?.trim() === '' && selNode?.settings?.boltzServerUrl?.trim() === '' && !selNode?.settings?.enablePeerswap" tabindex="4" role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === links[3].link" routerLink="{{links[3].link}}" (click)="setActiveLink(links[3].link)">{{links[3].name}}</div> -->
<div *ngIf="selNode.lnImplementation === 'ECL'" routerLink="{{links[2].link}}" tabindex="3" role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === links[2].link" (click)="setActiveLink(links[2].link)">{{links[2].name}}</div>
<div *ngIf="selNode?.lnImplementation === 'ECL'" routerLink="{{links[2].link}}" tabindex="3" role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === links[2].link" (click)="setActiveLink(links[2].link)">{{links[2].name}}</div>
</nav>
<mat-tab-nav-panel #tabPanel />
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="mat-tab-body-wrapper">

View file

@ -1,7 +1,7 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router, ResolveEnd, Event, ActivatedRoute } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { Subject, Observable } from 'rxjs';
import { takeUntil, filter, tap, shareReplay } from 'rxjs/operators';
import { faLayerGroup } from '@fortawesome/free-solid-svg-icons';
import { Node } from '../../../models/RTLconfig';
import { Store } from '@ngrx/store';
@ -20,18 +20,23 @@ export class ServicesSettingsComponent implements OnInit, OnDestroy {
// public links = [{ link: 'loop', name: 'Loop' }, { link: 'boltz', name: 'Boltz' }, { link: 'peerswap', name: 'Peerswap' }, { link: 'noservice', name: 'No Service' }];
public links = [{ link: 'loop', name: 'Loop' }, { link: 'boltz', name: 'Boltz' }, { link: 'noservice', name: 'No Service' }];
public activeLink = '';
public selNode: Node | any;
public selNode$: Observable<Node | null>;
private selNode: Node | any;
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()];
constructor(private store: Store<RTLState>, private router: Router, private activatedRoute: ActivatedRoute) { }
ngOnInit() {
this.setActiveLink();
this.store.select(rootSelectedNode).pipe(takeUntil(this.unSubs[1])).subscribe((selNode) => {
this.selNode = selNode;
this.setActiveLink();
this.router.navigate(['./' + this.activeLink], { relativeTo: this.activatedRoute });
});
this.selNode$ = this.store.select(rootSelectedNode).pipe(
takeUntil(this.unSubs[1]),
tap((selNode) => {
this.selNode = selNode;
this.setActiveLink();
this.router.navigate(['./' + this.activeLink], { relativeTo: this.activatedRoute });
}),
shareReplay(1)
);
}
setActiveLink(link?: string) {

View file

@ -1,4 +1,4 @@
<div fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch" class="padding-gap-x-large">
<div fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch" class="padding-gap-x-large" *ngIf="appConfig$ | async as appConfig">
<form *ngIf="appConfig?.allowPasswordUpdate" #authForm="ngForm" fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch" class="page-sub-title-container mt-1">
<div fxLayout="row" fxLayoutAlign="start start" class="mb-2">
<fa-icon class="page-title-img mr-1" [icon]="faLock" />
@ -35,7 +35,7 @@
<span>Protect your account from unauthorized access by requiring a second authentication method in addition to your password.</span>
</div>
<div class="mt-1">
<button mat-flat-button color="primary" tabindex="6" class="mb-2" (click)="on2FAuth()">{{appConfig.enable2FA ? 'Disable 2FA' : 'Enable 2FA'}}</button>
<button mat-flat-button color="primary" tabindex="6" class="mb-2" (click)="on2FAuth()">{{appConfig?.enable2FA ? 'Disable 2FA' : 'Enable 2FA'}}</button>
</div>
</div>
</div>

View file

@ -1,7 +1,7 @@
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { Subject, Observable } from 'rxjs';
import { takeUntil, filter, tap, shareReplay } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { faUserLock, faUserClock, faInfoCircle, faLock } from '@fortawesome/free-solid-svg-icons';
@ -36,6 +36,7 @@ export class AuthSettingsComponent implements OnInit, OnDestroy {
public errorMsg = '';
public errorConfirmMsg = '';
public initializeNodeData = false;
public appConfig$: Observable<RTLConfiguration>;
public appConfig: RTLConfiguration;
public selNode: Node | any;
unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()];
@ -44,10 +45,14 @@ export class AuthSettingsComponent implements OnInit, OnDestroy {
ngOnInit() {
this.initializeNodeData = this.sessionService.getItem('defaultPassword') === 'true';
this.store.select(rootAppConfig).pipe(takeUntil(this.unSubs[0])).subscribe((appConfig) => {
this.appConfig = appConfig;
this.logger.info(this.appConfig);
});
this.appConfig$ = this.store.select(rootAppConfig).pipe(
takeUntil(this.unSubs[0]),
tap((appConfig) => {
this.appConfig = appConfig;
this.logger.info(this.appConfig);
}),
shareReplay(1)
);
this.store.select(rootSelectedNode).pipe(takeUntil(this.unSubs[1])).subscribe((selNode) => {
this.selNode = selNode;
});

View file

@ -2,13 +2,13 @@
<fa-icon class="page-title-img mr-1" [icon]="faUserCog" />
<span class="page-title">Settings</span>
</div>
<div fxLayout="column" class="padding-gap-x">
<div fxLayout="column" class="padding-gap-x" *ngIf="{ appConfig: appConfig$ | async, showBitcoind: showBitcoind$ | async } as obs">
<mat-card>
<mat-card-content fxLayout="column">
<nav mat-tab-nav-bar mat-stretch-tabs="false" mat-align-tabs="start" [tabPanel]="tabPanel">
<div routerLink="{{links[0].link}}" tabindex="1" role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === links[0].link" (click)="activeLink = links[0].link">{{links[0].name}}</div>
<div *ngIf="!+appConfig.SSO.rtlSSO" routerLink="{{links[1].link}}" tabindex="2" role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === links[1].link" [state]="{ initial: false }" (click)="activeLink = links[1].link">{{links[1].name}}</div>
<div *ngIf="showBitcoind" routerLink="{{links[2].link}}" role="tab" tabindex="3" mat-tab-link class="mat-tab-label" [active]="activeLink === links[2].link" (click)="activeLink = links[2].link">{{links[2].name}}</div>
<div *ngIf="!+obs.appConfig?.SSO?.rtlSSO" routerLink="{{links[1].link}}" tabindex="2" role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === links[1].link" [state]="{ initial: false }" (click)="activeLink = links[1].link">{{links[1].name}}</div>
<div *ngIf="obs.showBitcoind" routerLink="{{links[2].link}}" role="tab" tabindex="3" mat-tab-link class="mat-tab-label" [active]="activeLink === links[2].link" (click)="activeLink = links[2].link">{{links[2].name}}</div>
</nav>
<mat-tab-nav-panel #tabPanel />
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="mat-tab-body-wrapper">

View file

@ -1,7 +1,7 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router, ResolveEnd, Event } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { Subject, Observable } from 'rxjs';
import { takeUntil, filter, map, shareReplay } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { faUserCog } from '@fortawesome/free-solid-svg-icons';
@ -18,9 +18,8 @@ import { rootSelectedNode, rootAppConfig } from '../../../store/rtl.selector';
export class SettingsComponent implements OnInit, OnDestroy {
public faUserCog = faUserCog;
public showBitcoind = false;
public selNode: Node | any;
public appConfig: RTLConfiguration;
public appConfig$: Observable<RTLConfiguration>;
public showBitcoind$: Observable<boolean>;
public links = [{ link: 'app', name: 'Application' }, { link: 'auth', name: 'Authentication' }, { link: 'bconfig', name: 'BitcoinD Config' }];
public activeLink = '';
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()];
@ -37,16 +36,12 @@ export class SettingsComponent implements OnInit, OnDestroy {
this.activeLink = linkFound ? linkFound.link : this.links[0].link;
}
});
this.store.select(rootAppConfig).pipe(takeUntil(this.unSubs[1])).subscribe((appConfig) => {
this.appConfig = appConfig;
});
this.store.select(rootSelectedNode).pipe(takeUntil(this.unSubs[2])).subscribe((selNode) => {
this.showBitcoind = false;
this.selNode = selNode;
if (this.selNode.settings && this.selNode.settings.bitcoindConfigPath && this.selNode.settings.bitcoindConfigPath.trim() !== '') {
this.showBitcoind = true;
}
});
this.appConfig$ = this.store.select(rootAppConfig).pipe(takeUntil(this.unSubs[1]), shareReplay(1));
this.showBitcoind$ = this.store.select(rootSelectedNode).pipe(
takeUntil(this.unSubs[2]),
map((selNode) => !!(selNode?.settings?.bitcoindConfigPath?.trim())),
shareReplay(1)
);
}
ngOnDestroy() {