From 6945d084b45052376630dddd5df2f13ef26ceac7 Mon Sep 17 00:00:00 2001 From: ShahanaFarooqui Date: Wed, 12 Oct 2022 14:38:21 -0700 Subject: [PATCH] Base Config Page Base Config Page --- backend/controllers/cln/getInfo.js | 2 +- backend/controllers/cln/offers.js | 2 +- backend/controllers/eclair/getInfo.js | 2 +- backend/controllers/lnd/getInfo.js | 2 +- backend/controllers/shared/RTLConf.js | 2 +- backend/controllers/shared/authenticate.js | 2 +- backend/utils/database.js | 37 +++-- server/controllers/cln/getInfo.ts | 2 +- server/controllers/cln/offers.ts | 2 +- server/controllers/eclair/getInfo.ts | 2 +- server/controllers/lnd/getInfo.ts | 2 +- server/controllers/shared/RTLConf.ts | 2 +- server/controllers/shared/authenticate.ts | 2 +- server/utils/database.ts | 37 +++-- src/app/app.routing.ts | 6 +- .../payments/lightning-payments.component.ts | 2 +- .../node-config/node-config.component.html | 9 +- .../node-config/node-config.component.ts | 6 +- .../page-settings.component.html | 75 ++++++++++ .../page-settings.component.scss | 0 .../page-settings.component.spec.ts | 51 +++++++ .../page-settings/page-settings.component.ts | 129 ++++++++++++++++++ src/app/shared/shared.module.ts | 3 + 23 files changed, 339 insertions(+), 40 deletions(-) create mode 100644 src/app/shared/components/node-config/page-settings/page-settings.component.html create mode 100644 src/app/shared/components/node-config/page-settings/page-settings.component.scss create mode 100644 src/app/shared/components/node-config/page-settings/page-settings.component.spec.ts create mode 100644 src/app/shared/components/node-config/page-settings/page-settings.component.ts diff --git a/backend/controllers/cln/getInfo.js b/backend/controllers/cln/getInfo.js index 75891f4d..6263c542 100644 --- a/backend/controllers/cln/getInfo.js +++ b/backend/controllers/cln/getInfo.js @@ -62,7 +62,7 @@ export const getInfo = (req, res, next) => { req.session.selectedNode.ln_version = body.version || ''; logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Connecting to the Core Lightning\'s Websocket Server.' }); clWsClient.updateSelectedNode(req.session.selectedNode); - databaseService.loadDatabase(req.session.selectedNode); + databaseService.loadDatabase(req.session); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body }); return res.status(200).json(body); } diff --git a/backend/controllers/cln/offers.js b/backend/controllers/cln/offers.js index 9a735b56..d12a5a47 100644 --- a/backend/controllers/cln/offers.js +++ b/backend/controllers/cln/offers.js @@ -22,7 +22,7 @@ export const listOfferBookmarks = (req, res, next) => { }; export const deleteOfferBookmark = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Deleting Offer Bookmark..' }); - databaseService.destroy(req.session.selectedNode, CollectionsEnum.OFFERS, CollectionFieldsEnum.BOLT12, req.params.offerStr).then((deleteRes) => { + databaseService.remove(req.session.selectedNode, CollectionsEnum.OFFERS, CollectionFieldsEnum.BOLT12, req.params.offerStr).then((deleteRes) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Bookmark Deleted', data: deleteRes }); res.status(204).json(req.params.offerStr); }).catch((errRes) => { diff --git a/backend/controllers/eclair/getInfo.js b/backend/controllers/eclair/getInfo.js index 0f90db68..0578a1ef 100644 --- a/backend/controllers/eclair/getInfo.js +++ b/backend/controllers/eclair/getInfo.js @@ -38,7 +38,7 @@ export const getInfo = (req, res, next) => { body.lnImplementation = 'Eclair'; req.session.selectedNode.ln_version = body.version.split('-')[0] || ''; eclWsClient.updateSelectedNode(req.session.selectedNode); - databaseService.loadDatabase(req.session.selectedNode); + databaseService.loadDatabase(req.session); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body }); return res.status(200).json(body); }).catch((errRes) => { diff --git a/backend/controllers/lnd/getInfo.js b/backend/controllers/lnd/getInfo.js index ef899516..efa745c8 100644 --- a/backend/controllers/lnd/getInfo.js +++ b/backend/controllers/lnd/getInfo.js @@ -45,7 +45,7 @@ export const getInfo = (req, res, next) => { else { req.session.selectedNode.ln_version = body.version.split('-')[0] || ''; lndWsClient.updateSelectedNode(req.session.selectedNode); - databaseService.loadDatabase(req.session.selectedNode); + databaseService.loadDatabase(req.session); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body }); return res.status(200).json(body); } diff --git a/backend/controllers/shared/RTLConf.js b/backend/controllers/shared/RTLConf.js index f0ff3779..61deb8eb 100644 --- a/backend/controllers/shared/RTLConf.js +++ b/backend/controllers/shared/RTLConf.js @@ -19,7 +19,7 @@ export const updateSelectedNode = (req, res, next) => { if (req.headers && req.headers.authorization && req.headers.authorization !== '') { wsServer.updateLNWSClientDetails(req.session.id, +req.session.selectedNode.index, +req.params.prevNodeIndex); if (req.params.prevNodeIndex !== -1) { - databaseService.unloadDatabase(req.params.prevNodeIndex); + databaseService.unloadDatabase(req.params.prevNodeIndex, req.session.id); } } const responseVal = !req.session.selectedNode.ln_node ? '' : req.session.selectedNode.ln_node; diff --git a/backend/controllers/shared/authenticate.js b/backend/controllers/shared/authenticate.js index 6be207a9..ff5e71ad 100644 --- a/backend/controllers/shared/authenticate.js +++ b/backend/controllers/shared/authenticate.js @@ -124,7 +124,7 @@ export const resetPassword = (req, res, next) => { export const logoutUser = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Authenticate', msg: 'Logged out' }); if (req.session.selectedNode && req.session.selectedNode.index) { - databaseService.unloadDatabase(+req.session.selectedNode.index); + databaseService.unloadDatabase(+req.session.selectedNode.index, req.session.id); } req.session.destroy((err) => { res.clearCookie('connect.sid'); diff --git a/backend/utils/database.js b/backend/utils/database.js index f0ac3777..cc819054 100644 --- a/backend/utils/database.js +++ b/backend/utils/database.js @@ -11,19 +11,23 @@ export class DatabaseService { this.dbDirectory = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'database'); this.nodeDatabase = {}; } - loadDatabase(selectedNode) { + loadDatabase(session) { + const { id, selectedNode } = session; try { if (!this.nodeDatabase[selectedNode.index]) { this.nodeDatabase[selectedNode.index] = { adapter: null, data: null }; + this.nodeDatabase[selectedNode.index].adapter = new DatabaseAdapter(this.dbDirectory, 'rtldb', selectedNode, id); + this.nodeDatabase[selectedNode.index].data = this.nodeDatabase[selectedNode.index].adapter.fetchData(); + } + else { + this.nodeDatabase[selectedNode.index].adapter.insertSession(id); } - this.nodeDatabase[selectedNode.index].adapter = new DatabaseAdapter(this.dbDirectory, 'rtldb', selectedNode); - this.nodeDatabase[selectedNode.index].data = this.nodeDatabase[selectedNode.index].adapter.fetchData(); } catch (err) { this.logger.log({ selectedNode: selectedNode, level: 'ERROR', fileName: 'Database', msg: 'Database Load Error', error: err }); } } - create(selectedNode, collectionName, newDocument) { + insert(selectedNode, collectionName, newDocument) { return new Promise((resolve, reject) => { try { if (!selectedNode || !selectedNode.index) { @@ -105,7 +109,7 @@ export class DatabaseService { } }); } - destroy(selectedNode, collectionName, documentFieldName, documentFieldValue) { + remove(selectedNode, collectionName, documentFieldName, documentFieldValue) { return new Promise((resolve, reject) => { try { if (!selectedNode || !selectedNode.index) { @@ -154,18 +158,27 @@ export class DatabaseService { return new Error(err); } } - unloadDatabase(nodeIndex) { - this.saveDatabase(nodeIndex); - this.nodeDatabase[nodeIndex] = null; + unloadDatabase(nodeIndex, sessionID) { + if (nodeIndex > 0) { + if (this.nodeDatabase[nodeIndex] && this.nodeDatabase[nodeIndex].adapter) { + this.nodeDatabase[nodeIndex].adapter.removeSession(sessionID); + if (this.nodeDatabase[nodeIndex].adapter.userSessions && this.nodeDatabase[nodeIndex].adapter.userSessions.length <= 0) { + delete this.nodeDatabase[nodeIndex]; + } + } + } } } export class DatabaseAdapter { - constructor(dbDirectoryPath, fileName, selNode = null) { + constructor(dbDirectoryPath, fileName, selNode = null, id = '') { this.dbDirectoryPath = dbDirectoryPath; this.fileName = fileName; this.selNode = selNode; + this.id = id; this.dbFile = ''; + this.userSessions = []; this.dbFile = dbDirectoryPath + sep + fileName + '-node-' + selNode.index + '.json'; + this.insertSession(id); } fetchData() { try { @@ -208,5 +221,11 @@ export class DatabaseAdapter { return new Error('Database Write Error ' + JSON.stringify(err)); } } + insertSession(id = '') { + this.userSessions.push(id); + } + removeSession(sessionID = '') { + this.userSessions.splice(this.userSessions.findIndex((sId) => sId === sessionID), 1); + } } export const Database = new DatabaseService(); diff --git a/server/controllers/cln/getInfo.ts b/server/controllers/cln/getInfo.ts index 64f3c4a8..f5317931 100644 --- a/server/controllers/cln/getInfo.ts +++ b/server/controllers/cln/getInfo.ts @@ -56,7 +56,7 @@ export const getInfo = (req, res, next) => { req.session.selectedNode.ln_version = body.version || ''; logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Connecting to the Core Lightning\'s Websocket Server.' }); clWsClient.updateSelectedNode(req.session.selectedNode); - databaseService.loadDatabase(req.session.selectedNode); + databaseService.loadDatabase(req.session); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body }); return res.status(200).json(body); } diff --git a/server/controllers/cln/offers.ts b/server/controllers/cln/offers.ts index 3d71dcfd..43e8b20c 100644 --- a/server/controllers/cln/offers.ts +++ b/server/controllers/cln/offers.ts @@ -25,7 +25,7 @@ export const listOfferBookmarks = (req, res, next) => { export const deleteOfferBookmark = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Deleting Offer Bookmark..' }); - databaseService.destroy(req.session.selectedNode, CollectionsEnum.OFFERS, CollectionFieldsEnum.BOLT12, req.params.offerStr).then((deleteRes) => { + databaseService.remove(req.session.selectedNode, CollectionsEnum.OFFERS, CollectionFieldsEnum.BOLT12, req.params.offerStr).then((deleteRes) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offer Bookmark Deleted', data: deleteRes }); res.status(204).json(req.params.offerStr); }).catch((errRes) => { diff --git a/server/controllers/eclair/getInfo.ts b/server/controllers/eclair/getInfo.ts index 1707d178..df3c4d9f 100644 --- a/server/controllers/eclair/getInfo.ts +++ b/server/controllers/eclair/getInfo.ts @@ -36,7 +36,7 @@ export const getInfo = (req, res, next) => { body.lnImplementation = 'Eclair'; req.session.selectedNode.ln_version = body.version.split('-')[0] || ''; eclWsClient.updateSelectedNode(req.session.selectedNode); - databaseService.loadDatabase(req.session.selectedNode); + databaseService.loadDatabase(req.session); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body }); return res.status(200).json(body); }).catch((errRes) => { diff --git a/server/controllers/lnd/getInfo.ts b/server/controllers/lnd/getInfo.ts index fdaaedab..386b503b 100644 --- a/server/controllers/lnd/getInfo.ts +++ b/server/controllers/lnd/getInfo.ts @@ -40,7 +40,7 @@ export const getInfo = (req, res, next) => { } else { req.session.selectedNode.ln_version = body.version.split('-')[0] || ''; lndWsClient.updateSelectedNode(req.session.selectedNode); - databaseService.loadDatabase(req.session.selectedNode); + databaseService.loadDatabase(req.session); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body }); return res.status(200).json(body); } diff --git a/server/controllers/shared/RTLConf.ts b/server/controllers/shared/RTLConf.ts index 51e6500f..7a21260e 100644 --- a/server/controllers/shared/RTLConf.ts +++ b/server/controllers/shared/RTLConf.ts @@ -22,7 +22,7 @@ export const updateSelectedNode = (req, res, next) => { if (req.headers && req.headers.authorization && req.headers.authorization !== '') { wsServer.updateLNWSClientDetails(req.session.id, +req.session.selectedNode.index, +req.params.prevNodeIndex); if (req.params.prevNodeIndex !== -1) { - databaseService.unloadDatabase(req.params.prevNodeIndex); + databaseService.unloadDatabase(req.params.prevNodeIndex, req.session.id); } } const responseVal = !req.session.selectedNode.ln_node ? '' : req.session.selectedNode.ln_node; diff --git a/server/controllers/shared/authenticate.ts b/server/controllers/shared/authenticate.ts index b161b3f5..1275ae48 100644 --- a/server/controllers/shared/authenticate.ts +++ b/server/controllers/shared/authenticate.ts @@ -120,7 +120,7 @@ export const resetPassword = (req, res, next) => { export const logoutUser = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Authenticate', msg: 'Logged out' }); if (req.session.selectedNode && req.session.selectedNode.index) { - databaseService.unloadDatabase(+req.session.selectedNode.index); + databaseService.unloadDatabase(+req.session.selectedNode.index, req.session.id); } req.session.destroy((err) => { res.clearCookie('connect.sid'); diff --git a/server/utils/database.ts b/server/utils/database.ts index 3ebfe6ce..85daf2c8 100644 --- a/server/utils/database.ts +++ b/server/utils/database.ts @@ -15,19 +15,22 @@ export class DatabaseService { constructor() { } - loadDatabase(selectedNode: CommonSelectedNode) { + loadDatabase(session: any) { + const { id, selectedNode } = session; try { if (!this.nodeDatabase[selectedNode.index]) { this.nodeDatabase[selectedNode.index] = { adapter: null, data: null }; + this.nodeDatabase[selectedNode.index].adapter = new DatabaseAdapter(this.dbDirectory, 'rtldb', selectedNode, id); + this.nodeDatabase[selectedNode.index].data = this.nodeDatabase[selectedNode.index].adapter.fetchData(); + } else { + this.nodeDatabase[selectedNode.index].adapter.insertSession(id); } - this.nodeDatabase[selectedNode.index].adapter = new DatabaseAdapter(this.dbDirectory, 'rtldb', selectedNode); - this.nodeDatabase[selectedNode.index].data = this.nodeDatabase[selectedNode.index].adapter.fetchData(); } catch (err) { this.logger.log({ selectedNode: selectedNode, level: 'ERROR', fileName: 'Database', msg: 'Database Load Error', error: err }); } } - create(selectedNode: CommonSelectedNode, collectionName: CollectionsEnum, newDocument: any) { + insert(selectedNode: CommonSelectedNode, collectionName: CollectionsEnum, newDocument: any) { return new Promise((resolve, reject) => { try { if (!selectedNode || !selectedNode.index) { @@ -105,7 +108,7 @@ export class DatabaseService { }); } - destroy(selectedNode: CommonSelectedNode, collectionName: CollectionsEnum, documentFieldName: string, documentFieldValue: string) { + remove(selectedNode: CommonSelectedNode, collectionName: CollectionsEnum, documentFieldName: string, documentFieldValue: string) { return new Promise((resolve, reject) => { try { if (!selectedNode || !selectedNode.index) { @@ -155,9 +158,15 @@ export class DatabaseService { } } - unloadDatabase(nodeIndex: number) { - this.saveDatabase(nodeIndex); - this.nodeDatabase[nodeIndex] = null; + unloadDatabase(nodeIndex: number, sessionID: string) { + if (nodeIndex > 0) { + if (this.nodeDatabase[nodeIndex] && this.nodeDatabase[nodeIndex].adapter) { + this.nodeDatabase[nodeIndex].adapter.removeSession(sessionID); + if (this.nodeDatabase[nodeIndex].adapter.userSessions && this.nodeDatabase[nodeIndex].adapter.userSessions.length <= 0) { + delete this.nodeDatabase[nodeIndex]; + } + } + } } } @@ -165,9 +174,11 @@ export class DatabaseService { export class DatabaseAdapter { private dbFile = ''; + private userSessions = []; - constructor(public dbDirectoryPath: string, public fileName: string, private selNode: CommonSelectedNode = null) { + constructor(public dbDirectoryPath: string, public fileName: string, private selNode: CommonSelectedNode = null, private id: string = '') { this.dbFile = dbDirectoryPath + sep + fileName + '-node-' + selNode.index + '.json'; + this.insertSession(id); } fetchData() { @@ -210,6 +221,14 @@ export class DatabaseAdapter { } } + insertSession(id: string = '') { + this.userSessions.push(id); + } + + removeSession(sessionID: string = '') { + this.userSessions.splice(this.userSessions.findIndex((sId) => sId === sessionID), 1); + } + } export const Database = new DatabaseService(); diff --git a/src/app/app.routing.ts b/src/app/app.routing.ts index 39ec1fec..b14b534e 100644 --- a/src/app/app.routing.ts +++ b/src/app/app.routing.ts @@ -8,6 +8,7 @@ import { BitcoinConfigComponent } from './shared/components/settings/bitcoin-con import { NodeConfigComponent } from './shared/components/node-config/node-config.component'; import { LNPConfigComponent } from './shared/components/node-config/lnp-config/lnp-config.component'; import { NodeSettingsComponent } from './shared/components/node-config/node-settings/node-settings.component'; +import { PageSettingsComponent } from './shared/components/node-config/page-settings/page-settings.component'; import { ServicesSettingsComponent } from './shared/components/node-config/services-settings/services-settings.component'; import { LoopServiceSettingsComponent } from './shared/components/node-config/services-settings/loop-service-settings/loop-service-settings.component'; import { BoltzServiceSettingsComponent } from './shared/components/node-config/services-settings/boltz-service-settings/boltz-service-settings.component'; @@ -42,8 +43,9 @@ export const routes: Routes = [ }, { path: 'config', component: NodeConfigComponent, canActivate: [AuthGuard], children: [ - { path: '', pathMatch: 'full', redirectTo: 'layout' }, - { path: 'layout', component: NodeSettingsComponent, canActivate: [AuthGuard] }, + { path: '', pathMatch: 'full', redirectTo: 'applayout' }, + { path: 'applayout', component: NodeSettingsComponent, canActivate: [AuthGuard] }, + { path: 'pglayout', component: PageSettingsComponent, canActivate: [AuthGuard] }, { path: 'services', component: ServicesSettingsComponent, canActivate: [AuthGuard], children: [ { path: '', pathMatch: 'full', redirectTo: 'loop' }, diff --git a/src/app/lnd/transactions/payments/lightning-payments.component.ts b/src/app/lnd/transactions/payments/lightning-payments.component.ts index 176c1c74..d2772955 100644 --- a/src/app/lnd/transactions/payments/lightning-payments.component.ts +++ b/src/app/lnd/transactions/payments/lightning-payments.component.ts @@ -22,7 +22,7 @@ import { LNDEffects } from '../../store/lnd.effects'; import { RTLEffects } from '../../../store/rtl.effects'; import { RTLState } from '../../../store/rtl.state'; import { openAlert, openConfirmation } from '../../../store/rtl.actions'; -import { fetchPayments, sendPayment } from '../../store/lnd.actions'; +import { sendPayment } from '../../store/lnd.actions'; import { lndNodeInformation, lndNodeSettings, payments, peers } from '../../store/lnd.selector'; @Component({ diff --git a/src/app/shared/components/node-config/node-config.component.html b/src/app/shared/components/node-config/node-config.component.html index 7e0e4801..19f36d3a 100644 --- a/src/app/shared/components/node-config/node-config.component.html +++ b/src/app/shared/components/node-config/node-config.component.html @@ -7,10 +7,11 @@
diff --git a/src/app/shared/components/node-config/node-config.component.ts b/src/app/shared/components/node-config/node-config.component.ts index 32c784c1..73383c8c 100644 --- a/src/app/shared/components/node-config/node-config.component.ts +++ b/src/app/shared/components/node-config/node-config.component.ts @@ -23,7 +23,7 @@ export class NodeConfigComponent implements OnInit, OnDestroy { public showLnConfig = false; public selNode: ConfigSettingsNode | any; public lnImplementationStr = ''; - public links = [{ link: 'layout', name: 'Layout' }, { link: 'services', name: 'Services' }, { link: 'experimental', name: 'Experimental' }, { link: 'lnconfig', name: this.lnImplementationStr }]; + public links = [{ link: 'applayout', name: 'App Layout' }, { link: 'pglayout', name: 'Page Layout' }, { link: 'services', name: 'Services' }, { link: 'experimental', name: 'Experimental' }, { link: 'lnconfig', name: this.lnImplementationStr }]; public activeLink = ''; private unSubs: Array> = [new Subject(), new Subject(), new Subject()]; @@ -56,7 +56,7 @@ export class NodeConfigComponent implements OnInit, OnDestroy { break; } if (this.selNode.authentication && this.selNode.authentication.configPath && this.selNode.authentication.configPath.trim() !== '') { - this.links[3].name = this.lnImplementationStr; + this.links[4].name = this.lnImplementationStr; this.showLnConfig = true; } }); @@ -73,7 +73,7 @@ export class NodeConfigComponent implements OnInit, OnDestroy { })); this.rtlEffects.closeAlert.pipe(takeUntil(this.unSubs[1])).subscribe((alertRes) => { if (alertRes) { - this.activeLink = this.links[3].link; + this.activeLink = this.links[4].link; this.router.navigate(['./' + this.activeLink], { relativeTo: this.activatedRoute }); } }); diff --git a/src/app/shared/components/node-config/page-settings/page-settings.component.html b/src/app/shared/components/node-config/page-settings/page-settings.component.html new file mode 100644 index 00000000..2a5155c0 --- /dev/null +++ b/src/app/shared/components/node-config/page-settings/page-settings.component.html @@ -0,0 +1,75 @@ +
+
+
+ + Page Settings +
+ +
+
+ + +
+
diff --git a/src/app/shared/components/node-config/page-settings/page-settings.component.scss b/src/app/shared/components/node-config/page-settings/page-settings.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/shared/components/node-config/page-settings/page-settings.component.spec.ts b/src/app/shared/components/node-config/page-settings/page-settings.component.spec.ts new file mode 100644 index 00000000..e194ae71 --- /dev/null +++ b/src/app/shared/components/node-config/page-settings/page-settings.component.spec.ts @@ -0,0 +1,51 @@ +import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; +import { StoreModule } from '@ngrx/store'; + +import { RootReducer } from '../../../../store/rtl.reducers'; +import { LNDReducer } from '../../../../lnd/store/lnd.reducers'; +import { CLNReducer } from '../../../../cln/store/cln.reducers'; +import { ECLReducer } from '../../../../eclair/store/ecl.reducers'; +import { CommonService } from '../../../services/common.service'; +import { LoggerService } from '../../../services/logger.service'; + +import { PageSettingsComponent } from './page-settings.component'; +import { mockDataService, mockLoggerService } from '../../../test-helpers/mock-services'; +import { SharedModule } from '../../../shared.module'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { DataService } from '../../../services/data.service'; + +describe('PageSettingsComponent', () => { + let component: PageSettingsComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [PageSettingsComponent], + imports: [ + BrowserAnimationsModule, + SharedModule, + StoreModule.forRoot({ root: RootReducer, lnd: LNDReducer, cln: CLNReducer, ecl: ECLReducer }) + ], + providers: [ + CommonService, + { provide: LoggerService, useClass: mockLoggerService }, + { provide: DataService, useClass: mockDataService } + ] + }). + compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PageSettingsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + afterEach(() => { + TestBed.resetTestingModule(); + }); +}); diff --git a/src/app/shared/components/node-config/page-settings/page-settings.component.ts b/src/app/shared/components/node-config/page-settings/page-settings.component.ts new file mode 100644 index 00000000..2a6b66e5 --- /dev/null +++ b/src/app/shared/components/node-config/page-settings/page-settings.component.ts @@ -0,0 +1,129 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; +import { Store } from '@ngrx/store'; +import { faPenRuler } from '@fortawesome/free-solid-svg-icons'; + +import { CURRENCY_UNITS, UserPersonaEnum, ScreenSizeEnum, FIAT_CURRENCY_UNITS, NODE_SETTINGS, UI_MESSAGES } from '../../../services/consts-enums-functions'; +import { ConfigSettingsNode, Settings } from '../../../models/RTLconfig'; +import { LoggerService } from '../../../services/logger.service'; +import { CommonService } from '../../../services/common.service'; +import { RTLState } from '../../../../store/rtl.state'; +import { saveSettings, setSelectedNode } from '../../../../store/rtl.actions'; +import { setChildNodeSettingsECL } from '../../../../eclair/store/ecl.actions'; +import { setChildNodeSettingsCL } from '../../../../cln/store/cln.actions'; +import { setChildNodeSettingsLND } from '../../../../lnd/store/lnd.actions'; +import { rootSelectedNode } from '../../../../store/rtl.selector'; + +@Component({ + selector: 'rtl-page-settings', + templateUrl: './page-settings.component.html', + styleUrls: ['./page-settings.component.scss'] +}) +export class PageSettingsComponent implements OnInit, OnDestroy { + + public faPenRuler = faPenRuler; + public selNode: ConfigSettingsNode | any; + 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 previousSettings: Settings; + public screenSize = ''; + public screenSizeEnum = ScreenSizeEnum; + unSubs: Array> = [new Subject(), new Subject()]; + + constructor(private logger: LoggerService, private commonService: CommonService, private store: Store) { + this.screenSize = this.commonService.getScreenSize(); + } + + ngOnInit() { + this.store.select(rootSelectedNode).pipe(takeUntil(this.unSubs[0])).subscribe((selNode) => { + this.selNode = selNode; + this.selectedThemeMode = this.themeModes.find((themeMode) => this.selNode.settings.themeMode === themeMode.id) || this.themeModes[0]; + this.selectedThemeColor = this.selNode.settings.themeColor; + if (!this.selNode.settings.fiatConversion) { + this.selNode.settings.currencyUnit = ''; + } + this.previousSettings = JSON.parse(JSON.stringify(this.selNode.settings)); + this.logger.info(selNode); + }); + } + + onCurrencyChange(event: any) { + this.selNode.settings.currencyUnits = [...CURRENCY_UNITS, event.value]; + this.store.dispatch(setChildNodeSettingsLND({ + payload: { + 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(setChildNodeSettingsCL({ + payload: { + 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(setChildNodeSettingsECL({ + payload: { + 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(saveSettings({ payload: { uiMessage: UI_MESSAGES.UPDATE_NODE_SETTINGS, settings: this.selNode.settings } })); + this.store.dispatch(setChildNodeSettingsLND({ + payload: { + 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(setChildNodeSettingsCL({ + payload: { + 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(setChildNodeSettingsECL({ + payload: { + 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() { + const prevIndex = this.selNode.index || -1; + this.selNode.settings = this.previousSettings; + this.selectedThemeMode = this.themeModes.find((themeMode) => themeMode.id === this.previousSettings.themeMode) || this.themeModes[0]; + this.selectedThemeColor = this.previousSettings.themeColor; + this.store.dispatch(setSelectedNode({ payload: { uiMessage: UI_MESSAGES.NO_SPINNER, prevLnNodeIndex: +prevIndex, currentLnNode: this.selNode, isInitialSetup: true } })); + } + + ngOnDestroy() { + this.unSubs.forEach((unsub) => { + unsub.next(); + unsub.complete(); + }); + } + +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 9ccfebd5..5c0d6af2 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -62,6 +62,7 @@ import { AppSettingsComponent } from './components/settings/app-settings/app-set import { NodeConfigComponent } from './components/node-config/node-config.component'; import { LNPConfigComponent } from './components/node-config/lnp-config/lnp-config.component'; import { NodeSettingsComponent } from './components/node-config/node-settings/node-settings.component'; +import { PageSettingsComponent } from './components/node-config/page-settings/page-settings.component'; import { ServicesSettingsComponent } from './components/node-config/services-settings/services-settings.component'; import { LoopServiceSettingsComponent } from './components/node-config/services-settings/loop-service-settings/loop-service-settings.component'; import { BoltzServiceSettingsComponent } from './components/node-config/services-settings/boltz-service-settings/boltz-service-settings.component'; @@ -248,6 +249,7 @@ export const DEFAULT_DATE_FORMAT: MatDateFormats = { NodeConfigComponent, LNPConfigComponent, NodeSettingsComponent, + PageSettingsComponent, ServicesSettingsComponent, LoopServiceSettingsComponent, BoltzServiceSettingsComponent, @@ -292,6 +294,7 @@ export const DEFAULT_DATE_FORMAT: MatDateFormats = { NodeConfigComponent, LNPConfigComponent, NodeSettingsComponent, + PageSettingsComponent, ServicesSettingsComponent, LoopServiceSettingsComponent, BoltzServiceSettingsComponent,